From d113982891b6f07dfc8db2f547b1ac2f3191cffa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 3 Feb 2023 17:10:57 -0500 Subject: [PATCH 001/758] A whole lot really, restructure libs The file structure of kolbot has been a bit of a mess so this is meant to fix that. Folder Structure Changes: - tools renamed to threads, the files in this folder are all threads it makes more sense then for that to be the name - libs\bots renamed to scripts, each of these is a script file run by the loader, scripts is then the better name - libs\common renamed to core, this directory contains core libraries without which we cannot run - core\Auto\ created to store the AutoSkill/Stat/Build files, the only time these should be included is if we are specifically running one of the systems - NTItemAlias/Parser file extensions changed from .dbl to .js, they are .js files and the .dbl was legacy and served no purpose other than to disrupts IDE's and github from recognizing them as such - GameData.js broken up into GameData/MonsterData/AreaData - MonsterData/AreaData/LocaleStringId all changed into requirable modules - core/Data created for these Data files - Common.js broken up into separate , includeable files, Common.js remains and when one of the core\Common\ files are included they add to the Common namespace to prevent namespace conflicts with any like-named structures - sdk\txt created to store the loose txt files from the sdk folder - sdk\types created to store type definition files that are a work in progress to improve intellisense of modern IDE's - libs\systems\ created to handle kolbot systems AutoMule/MuleLogger/TorchSystem/GameAction/CraftingSystem, each system then given it's own folder and the config settings moved out of the core logic file - libs\oog\ created to break up large OOG.js file into includeable chunks, we don't need to have access to Starter everywhere we need access to D2Bot functions - libs\starter\ created to handle StarterConfig file and AdvancedConfig file - libs\critcal.js created to simplify what files always need to be included, any of which if we don't have included will cause the system to fail Lib Changes: - Lots - Added sdk to Polyfill.js, its absolutly necessary it is loaded globally - Added includeIfNotIncluded to PolyFill.js - Created includeSystemLibs, takes care of including main systems files, does not include MuleLogger and GameAction by default as they aren't necessary everywhere - Created String.prototype.format, it takes any number or pairs to format a string since we don't have template literals. - Renamed includeCommonLibs to includeCoreLibs - Moved Misc.clone and Misc.copy out of Misc and into Polyfill as clone and copyObj, there is no point to include the whole Misc namespace when all we needed from it was clone and copy - Moved LocalChat out of misc and made it into its own module in the modules directory - Moved PacketBuilder out of misc and into Util.js, not making it's own module because its more of a utility function but needed everywhere while in bot mode - Created CopyData class as a requirable module to simplfy using the sendCopyData function with D2Bot functions, it still needs to be updated to handle custom data objects that don't follow the same format as the D2Bot functions - Created Experience.js and moved the namespace from misc - Created Me.js and moved any 'me' related prototypes out of Prototypes.js - Created Item.js and moved the Item namespace from misc - Created Skill.js and moved the Skill namespace from misc - Cleaned up Skill namespace and created a SkillData class, Skill.skills is still subject to change as i'm not a fan of having Skill.skills.all.StaticField, but it was the only way I could get JSDOC to pickup the structure - ManualPlay now runs it's own main thread like SoloPlay does, it was pointless to keep default.dbj just idly looping - UnitInfo made into it's own class and requirable module - Feature update for Config.QuitListDelay, once triggered we get a overhead message countdown - Pather.getAreaName removed from Pather namespace and getAreaName added to Util.js, same reason as removing clone and copy. We don't need the whole namespace everywhere, sometimes we just want that single function - BaalAssistant updated to prevent infinite waittime when not helping kill baal, we now watch for the baal death packet - Fixed some of the function files leaking functions to the global scope - Fixed Pickit recursion, from having to move to new location while picking, this also fixes the double printing of items to the item log - Fixed barb Item Find skill usage and recursion - Added new Config option Config.FastItemFind to allow using between attacks - Improved barbs usage of LeapAttack while pathing, track when and where we last used it rather than just if it's been used once - Added using Whirlwind while pathing if we have it and are stuck, whirlwind allows us to reposition and move through enemies so it's very useful for that. It follows the same required rules of checking when and where we last whirled from - Improved clearing in the instance of being stuck while pathing, track when and where we last cleared - Moved Pickit.getItemQualityToName and Pickit.getItemColor into Item namespace and renamed as Item.qualityToName and Item.color - Created Packet.js to move the Packet namespace out of misc - Improved npc scrolling text detection - Huge improvment on Misc.getShrinesInArea detection when looking for a specific shrine, no more running all the way to it just to decide it wasn't the one we wanted - Changed manualplay\ItemHooks to use map structure for easier checks - Changed manualplay\ShrineHooks to use map structure for easier checks - Added necro raise army to precast if set to Config.ActiveSummon, this is experimental - Hopefully fixed mule issues, but added more checks for being in game and removed gameevent listener whenever we go to refresh a game, also only add the listner once we are calling init to prevent premature changes - Removed AutoAssign.js as far as I can tell it was never used - Fix crash in Questing.js when we had failed to detect anya's scrolling text - Changed Summoner.js to always use the portal and take the waypoint back to town unless Duriel is our next script - Added soloplay entry check into default.dbj, its minimal so why not. Helps anyone who's running soloplay and switches to a different branch. - Added precasting every other place we visit while running GemHunter - Add quick sort method after we mule items to condense our stash - Add force parameter to Attack.getIntoPosition - Add allowPicking option to pathSettings in Pather.move - Add hasMonsterType into AreaData module - Add Type into MonsterData module I think that's it for now? --- .gitignore | 1 + d2bs/kolbot/D2BotBlank.dbj | 30 +- d2bs/kolbot/D2BotChannel.dbj | 52 +- d2bs/kolbot/D2BotCleaner.dbj | 22 +- d2bs/kolbot/D2BotFollow.dbj | 44 +- d2bs/kolbot/D2BotGameAction.dbj | 27 +- d2bs/kolbot/D2BotLead.dbj | 57 +- d2bs/kolbot/D2BotMap.dbj | 13 +- d2bs/kolbot/D2BotMule.dbj | 119 +- d2bs/kolbot/D2BotMuleLog.dbj | 28 +- d2bs/kolbot/D2BotPubJoin.dbj | 37 +- d2bs/kolbot/default.dbj | 76 +- d2bs/kolbot/libs/GameData.js | 289 - d2bs/kolbot/libs/NTItemAlias.dbl | 1477 ---- d2bs/kolbot/libs/OOG.js | 2978 +++---- d2bs/kolbot/libs/Polyfill.js | 246 +- d2bs/kolbot/libs/StarterConfig.js | 78 - d2bs/kolbot/libs/bots/BaalAssistant.js | 433 - d2bs/kolbot/libs/bots/Summoner.js | 42 - d2bs/kolbot/libs/common/AutoAssign.js | 252 - d2bs/kolbot/libs/common/Common.js | 1478 ---- d2bs/kolbot/libs/common/Item.js | 240 - d2bs/kolbot/libs/common/Misc.js | 2742 ------ d2bs/kolbot/libs/common/Storage.js | 397 - d2bs/kolbot/libs/common/Util.js | 231 - d2bs/kolbot/libs/config/Amazon.js | 26 +- d2bs/kolbot/libs/config/Assassin.js | 26 +- d2bs/kolbot/libs/config/Barbarian.js | 26 +- d2bs/kolbot/libs/config/Builds/Class.Build.js | 12 +- .../config/Builds/Sorceress.ExampleBuild.js | 12 +- d2bs/kolbot/libs/config/Druid.js | 26 +- d2bs/kolbot/libs/config/Necromancer.js | 26 +- d2bs/kolbot/libs/config/Paladin.js | 26 +- d2bs/kolbot/libs/config/Sorceress.js | 26 +- d2bs/kolbot/libs/config/_BaseConfigFile.js | 30 +- d2bs/kolbot/libs/{common => core}/Attack.js | 373 +- .../libs/{common => core}/Attacks/Amazon.js | 0 .../libs/{common => core}/Attacks/Assassin.js | 0 .../{common => core}/Attacks/Barbarian.js | 76 +- .../libs/{common => core}/Attacks/Druid.js | 0 .../{common => core}/Attacks/Necromancer.js | 47 +- .../libs/{common => core}/Attacks/Paladin.js | 20 + .../{common => core}/Attacks/Sorceress.js | 0 .../libs/{common => core}/Attacks/Wereform.js | 2 +- .../libs/{common => core/Auto}/AutoBuild.js | 10 +- .../libs/{common => core/Auto}/AutoSkill.js | 2 +- .../libs/{common => core/Auto}/AutoStat.js | 0 d2bs/kolbot/libs/{common => core}/CollMap.js | 0 d2bs/kolbot/libs/core/Common.js | 11 + d2bs/kolbot/libs/core/Common/Ancients.js | 143 + d2bs/kolbot/libs/core/Common/Baal.js | 293 + d2bs/kolbot/libs/core/Common/Cain.js | 128 + d2bs/kolbot/libs/core/Common/Cows.js | 79 + d2bs/kolbot/libs/core/Common/Diablo.js | 550 ++ d2bs/kolbot/libs/core/Common/Leecher.js | 51 + d2bs/kolbot/libs/core/Common/Smith.js | 29 + d2bs/kolbot/libs/core/Common/Tools.js | 314 + d2bs/kolbot/libs/{common => core}/Config.js | 83 +- d2bs/kolbot/libs/{common => core}/Cubing.js | 8 +- d2bs/kolbot/libs/core/Data/AreaData.js | 206 + d2bs/kolbot/libs/core/Data/GameData.js | 168 + d2bs/kolbot/libs/core/Data/LocaleStringID.js | 7806 +++++++++++++++++ d2bs/kolbot/libs/core/Data/MonsterData.js | 116 + d2bs/kolbot/libs/core/Data/NTItemAlias.js | 1485 ++++ d2bs/kolbot/libs/core/Experience.js | 138 + d2bs/kolbot/libs/core/Item.js | 585 ++ d2bs/kolbot/libs/{common => core}/Loader.js | 20 +- d2bs/kolbot/libs/core/Me.js | 753 ++ d2bs/kolbot/libs/core/Misc.js | 767 ++ .../NTItemParser.js} | 14 +- d2bs/kolbot/libs/core/Packet.js | 383 + d2bs/kolbot/libs/{common => core}/Pather.js | 501 +- d2bs/kolbot/libs/{common => core}/Pickit.js | 127 +- d2bs/kolbot/libs/{common => core}/Precast.js | 20 +- .../libs/{common => core}/Prototypes.js | 4352 ++++----- .../kolbot/libs/{common => core}/Runewords.js | 4 +- d2bs/kolbot/libs/core/Skill.js | 775 ++ d2bs/kolbot/libs/core/Storage.js | 402 + d2bs/kolbot/libs/{common => core}/Town.js | 519 +- d2bs/kolbot/libs/core/Util.js | 609 ++ d2bs/kolbot/libs/critical.js | 12 + d2bs/kolbot/libs/manualplay/MapMode.js | 4 +- d2bs/kolbot/libs/manualplay/config/Amazon.js | 4 +- .../kolbot/libs/manualplay/config/Assassin.js | 4 +- .../libs/manualplay/config/Barbarian.js | 6 +- d2bs/kolbot/libs/manualplay/config/Druid.js | 4 +- .../libs/manualplay/config/Necromancer.js | 4 +- d2bs/kolbot/libs/manualplay/config/Paladin.js | 4 +- .../libs/manualplay/config/Sorceress.js | 7 +- .../libs/manualplay/hooks/ActionHooks.js | 50 +- .../kolbot/libs/manualplay/hooks/ItemHooks.js | 229 +- .../libs/manualplay/hooks/MonsterHooks.js | 2 +- .../libs/manualplay/hooks/ShrineHooks.js | 69 +- .../kolbot/libs/manualplay/hooks/TextHooks.js | 26 +- .../libs/manualplay/hooks/VectorHooks.js | 52 +- .../libs/manualplay/libs/AttackOverrides.js | 10 +- .../libs/manualplay/libs/ConfigOverrides.js | 6 +- .../libs/manualplay/libs/MiscOverrides.js | 2 +- .../libs/manualplay/libs/PatherOverrides.js | 2 +- .../libs/manualplay/libs/PickitOverrides.js | 2 +- .../libs/manualplay/libs/TownOverrides.js | 4 +- .../libs/manualplay/threads/MapHelper.js | 30 +- .../libs/manualplay/threads/MapThread.js | 108 +- .../libs/manualplay/threads/MapToolsThread.js | 73 +- .../libs/manualplay/threads/PickThread.js | 20 +- d2bs/kolbot/libs/modules/Control.js | 42 +- d2bs/kolbot/libs/modules/CopyData.js | 102 + d2bs/kolbot/libs/modules/LocalChat.js | 80 + d2bs/kolbot/libs/{ => modules}/UnitInfo.js | 219 +- d2bs/kolbot/libs/modules/sdk.js | 47 +- d2bs/kolbot/libs/oog/D2Bot.js | 207 + d2bs/kolbot/libs/oog/DataFile.js | 131 + d2bs/kolbot/libs/oog/FileAction.js | 95 + d2bs/kolbot/libs/oog/ShitList.js | 59 + d2bs/kolbot/libs/require.js | 19 +- d2bs/kolbot/libs/{bots => scripts}/Abaddon.js | 0 .../libs/{bots => scripts}/AncientTunnels.js | 0 .../kolbot/libs/{bots => scripts}/Andariel.js | 0 .../kolbot/libs/{bots => scripts}/AutoBaal.js | 141 +- d2bs/kolbot/libs/{bots => scripts}/Baal.js | 1 + d2bs/kolbot/libs/scripts/BaalAssistant.js | 449 + .../libs/{bots => scripts}/BaalHelper.js | 1 + .../libs/{bots => scripts}/BattleOrders.js | 0 .../{bots => scripts}/BattlemaidSarina.js | 0 .../libs/{bots => scripts}/Bishibosh.js | 0 .../libs/{bots => scripts}/BoBarbHelper.js | 2 +- d2bs/kolbot/libs/{bots => scripts}/BoneAsh.js | 0 d2bs/kolbot/libs/{bots => scripts}/Bonesaw.js | 0 .../libs/{bots => scripts}/ChestMania.js | 0 .../ClassicChaosAssistant.js | 1 + .../libs/{bots => scripts}/ClearAnyArea.js | 0 .../kolbot/libs/{bots => scripts}/Coldcrow.js | 0 .../kolbot/libs/{bots => scripts}/Coldworm.js | 0 .../libs/{bots => scripts}/ControlBot.js | 2 +- .../libs/{bots => scripts}/Corpsefire.js | 0 .../kolbot/libs/{bots => scripts}/Countess.js | 0 d2bs/kolbot/libs/{bots => scripts}/Cows.js | 14 +- .../kolbot/libs/{bots => scripts}/Crafting.js | 0 .../libs/{bots => scripts}/CreepingFeature.js | 0 .../libs/{bots => scripts}/CrushTele.js | 0 .../libs/{bots => scripts}/DeveloperMode.js | 34 +- d2bs/kolbot/libs/{bots => scripts}/Diablo.js | 1 + .../libs/{bots => scripts}/DiabloHelper.js | 1 + d2bs/kolbot/libs/{bots => scripts}/Duriel.js | 0 .../kolbot/libs/{bots => scripts}/Eldritch.js | 0 d2bs/kolbot/libs/{bots => scripts}/Endugu.js | 0 d2bs/kolbot/libs/{bots => scripts}/Eyeback.js | 0 .../kolbot/libs/{bots => scripts}/Fangskin.js | 0 .../kolbot/libs/{bots => scripts}/Follower.js | 2 +- .../libs/{bots => scripts}/Frozenstein.js | 0 d2bs/kolbot/libs/{bots => scripts}/Gamble.js | 0 .../libs/{bots => scripts}/GemHunter.js | 6 +- d2bs/kolbot/libs/{bots => scripts}/GetKeys.js | 0 .../libs/{bots => scripts}/GhostBusters.js | 0 .../kolbot/libs/{bots => scripts}/Hephasto.js | 0 .../kolbot/libs/{bots => scripts}/IPHunter.js | 0 d2bs/kolbot/libs/{bots => scripts}/Icehawk.js | 0 d2bs/kolbot/libs/{bots => scripts}/Izual.js | 0 .../libs/{bots => scripts}/KillDclone.js | 0 .../libs/{bots => scripts}/KurastTemples.js | 0 .../kolbot/libs/{bots => scripts}/MFHelper.js | 1 + .../libs/{bots => scripts}/Mausoleum.js | 0 .../kolbot/libs/{bots => scripts}/Mephisto.js | 0 .../libs/{bots => scripts}/Nihlathak.js | 0 .../kolbot/libs/{bots => scripts}/OrgTorch.js | 2 +- .../libs/{bots => scripts}/OuterSteppes.js | 0 .../libs/{bots => scripts}/Pindleskin.js | 0 d2bs/kolbot/libs/{bots => scripts}/Pit.js | 0 .../kolbot/libs/{bots => scripts}/Questing.js | 164 +- .../kolbot/libs/{bots => scripts}/Radament.js | 0 .../libs/{bots => scripts}/Rakanishu.js | 0 d2bs/kolbot/libs/{bots => scripts}/Rushee.js | 2 +- d2bs/kolbot/libs/{bots => scripts}/Rusher.js | 10 +- .../libs/{bots => scripts}/SealLeecher.js | 0 .../libs/{bots => scripts}/SharpTooth.js | 0 d2bs/kolbot/libs/{bots => scripts}/ShopBot.js | 2 +- d2bs/kolbot/libs/{bots => scripts}/Smith.js | 0 .../kolbot/libs/{bots => scripts}/Snapchip.js | 0 .../libs/{bots => scripts}/Stormtree.js | 0 d2bs/kolbot/libs/scripts/Summoner.js | 57 + d2bs/kolbot/libs/{bots => scripts}/Synch.js | 0 d2bs/kolbot/libs/{bots => scripts}/Synch2.js | 0 d2bs/kolbot/libs/{bots => scripts}/Test.js | 0 .../libs/{bots => scripts}/ThreshSocket.js | 0 d2bs/kolbot/libs/{bots => scripts}/Tombs.js | 0 .../libs/{bots => scripts}/Travincal.js | 0 .../libs/{bots => scripts}/TravincalLeech.js | 1 + .../kolbot/libs/{bots => scripts}/Treehead.js | 0 .../kolbot/libs/{bots => scripts}/Tristram.js | 1 + .../libs/{bots => scripts}/TristramLeech.js | 1 + .../{bots => scripts}/UndergroundPassage.js | 0 .../libs/{bots => scripts}/UserAddon.js | 23 +- .../kolbot/libs/{bots => scripts}/WPGetter.js | 0 d2bs/kolbot/libs/{bots => scripts}/Wakka.js | 3 +- .../libs/{bots => scripts}/Worldstone.js | 0 d2bs/kolbot/libs/starter/AdvancedConfig.js | 46 + d2bs/kolbot/libs/starter/StarterConfig.js | 43 + .../libs/{ => systems/automule}/AutoMule.js | 205 +- .../libs/systems/automule/MuleConfig.js | 43 + .../libs/systems/automule/TorchAnniMules.js | 45 + .../{ => systems/crafting}/CraftingSystem.js | 39 +- .../libs/systems/crafting/TeamsConfig.js | 45 + .../libs/{ => systems/gambling}/Gambling.js | 45 +- .../libs/systems/gambling/TeamsConfig.js | 37 + .../{ => systems/gameaction}/GameAction.js | 19 +- .../libs/systems/mulelogger/LoggerConfig.js | 36 + .../{ => systems/mulelogger}/MuleLogger.js | 58 +- .../kolbot/libs/systems/torch/FarmerConfig.js | 45 + .../libs/{ => systems/torch}/TorchSystem.js | 133 +- d2bs/kolbot/sdk/LocaleStringID.js | 7794 ---------------- d2bs/kolbot/sdk/Unit.d.ts | 59 - d2bs/kolbot/sdk/globals.d.ts | 1198 ++- d2bs/kolbot/sdk/{ => txt}/Shrines.txt | 0 d2bs/kolbot/sdk/{ => txt}/SuperUniques.txt | 0 d2bs/kolbot/sdk/{ => txt}/areas.txt | 0 d2bs/kolbot/sdk/{ => txt}/basestats.txt | 0 d2bs/kolbot/sdk/{ => txt}/bodylocations.txt | 0 d2bs/kolbot/sdk/{ => txt}/chests.txt | 0 d2bs/kolbot/sdk/{ => txt}/enchants.txt | 0 d2bs/kolbot/sdk/{ => txt}/getskillinfo.txt | 0 d2bs/kolbot/sdk/{ => txt}/itemflags.txt | 0 d2bs/kolbot/sdk/{ => txt}/miscscreenmodes.txt | 0 d2bs/kolbot/sdk/{ => txt}/modes.txt | 0 .../sdk/{ => txt}/monster classID's.txt | 0 d2bs/kolbot/sdk/{ => txt}/npcmenuid.txt | 0 d2bs/kolbot/sdk/{ => txt}/quests.txt | 0 d2bs/kolbot/sdk/{ => txt}/roomstats.txt | 0 d2bs/kolbot/sdk/{ => txt}/skills.txt | 0 d2bs/kolbot/sdk/{ => txt}/states.txt | 0 d2bs/kolbot/sdk/{ => txt}/stats.txt | 0 d2bs/kolbot/sdk/{ => txt}/stats_skills.txt | 0 d2bs/kolbot/sdk/{ => txt}/stats_tabs.txt | 0 .../{ => txt}/superunique_presetunitids.txt | 0 d2bs/kolbot/sdk/{ => txt}/tile.d2l | 0 d2bs/kolbot/sdk/{ => txt}/uiflag.txt | 0 d2bs/kolbot/sdk/{ => txt}/waypoints.txt | 0 d2bs/kolbot/sdk/types/Attack.d.ts | 66 + d2bs/kolbot/sdk/types/AutoMule.d.ts | 101 + d2bs/kolbot/sdk/types/Config.d.ts | 562 ++ d2bs/kolbot/sdk/types/Cubing.d.ts | 23 + d2bs/kolbot/sdk/types/Item.d.ts | 12 + d2bs/kolbot/sdk/types/Loader.d.ts | 16 + d2bs/kolbot/sdk/types/Misc.d.ts | 38 + d2bs/kolbot/sdk/types/NTIP.d.ts | 7 + d2bs/kolbot/sdk/types/OOG.d.ts | 67 + d2bs/kolbot/sdk/types/Pather.d.ts | 37 + d2bs/kolbot/sdk/types/Pickit.d.ts | 36 + d2bs/kolbot/sdk/types/Runewords.d.ts | 105 + d2bs/kolbot/sdk/types/Skill.d.ts | 44 + d2bs/kolbot/sdk/types/Storage.d.ts | 95 + d2bs/kolbot/sdk/types/Town.d.ts | 113 + d2bs/kolbot/sdk/types/Util.d.ts | 121 + d2bs/kolbot/sdk/types/sdk.d.ts | 4693 ++++++++++ d2bs/kolbot/{tools => threads}/AntiHostile.js | 26 +- d2bs/kolbot/{tools => threads}/AntiIdle.js | 4 +- d2bs/kolbot/{tools => threads}/AreaWatcher.js | 6 +- .../{tools => threads}/AutoBuildThread.js | 30 +- d2bs/kolbot/{tools => threads}/CloneKilla.js | 31 +- d2bs/kolbot/{tools => threads}/HeartBeat.js | 13 +- d2bs/kolbot/{tools => threads}/Party.js | 116 +- d2bs/kolbot/{tools => threads}/RushThread.js | 28 +- d2bs/kolbot/{tools => threads}/ToolsThread.js | 71 +- d2bs/kolbot/{tools => threads}/TownChicken.js | 21 +- package-lock.json | 37 + package.json | 16 + tsconfig.json | 43 + 266 files changed, 30489 insertions(+), 22230 deletions(-) delete mode 100644 d2bs/kolbot/libs/GameData.js delete mode 100644 d2bs/kolbot/libs/NTItemAlias.dbl delete mode 100644 d2bs/kolbot/libs/StarterConfig.js delete mode 100644 d2bs/kolbot/libs/bots/BaalAssistant.js delete mode 100644 d2bs/kolbot/libs/bots/Summoner.js delete mode 100644 d2bs/kolbot/libs/common/AutoAssign.js delete mode 100644 d2bs/kolbot/libs/common/Common.js delete mode 100644 d2bs/kolbot/libs/common/Item.js delete mode 100644 d2bs/kolbot/libs/common/Misc.js delete mode 100644 d2bs/kolbot/libs/common/Storage.js delete mode 100644 d2bs/kolbot/libs/common/Util.js rename d2bs/kolbot/libs/{common => core}/Attack.js (82%) rename d2bs/kolbot/libs/{common => core}/Attacks/Amazon.js (100%) rename d2bs/kolbot/libs/{common => core}/Attacks/Assassin.js (100%) rename d2bs/kolbot/libs/{common => core}/Attacks/Barbarian.js (73%) rename d2bs/kolbot/libs/{common => core}/Attacks/Druid.js (100%) rename d2bs/kolbot/libs/{common => core}/Attacks/Necromancer.js (92%) rename d2bs/kolbot/libs/{common => core}/Attacks/Paladin.js (91%) rename d2bs/kolbot/libs/{common => core}/Attacks/Sorceress.js (100%) rename d2bs/kolbot/libs/{common => core}/Attacks/Wereform.js (99%) rename d2bs/kolbot/libs/{common => core/Auto}/AutoBuild.js (92%) rename d2bs/kolbot/libs/{common => core/Auto}/AutoSkill.js (98%) rename d2bs/kolbot/libs/{common => core/Auto}/AutoStat.js (100%) rename d2bs/kolbot/libs/{common => core}/CollMap.js (100%) create mode 100644 d2bs/kolbot/libs/core/Common.js create mode 100644 d2bs/kolbot/libs/core/Common/Ancients.js create mode 100644 d2bs/kolbot/libs/core/Common/Baal.js create mode 100644 d2bs/kolbot/libs/core/Common/Cain.js create mode 100644 d2bs/kolbot/libs/core/Common/Cows.js create mode 100644 d2bs/kolbot/libs/core/Common/Diablo.js create mode 100644 d2bs/kolbot/libs/core/Common/Leecher.js create mode 100644 d2bs/kolbot/libs/core/Common/Smith.js create mode 100644 d2bs/kolbot/libs/core/Common/Tools.js rename d2bs/kolbot/libs/{common => core}/Config.js (87%) rename d2bs/kolbot/libs/{common => core}/Cubing.js (99%) create mode 100644 d2bs/kolbot/libs/core/Data/AreaData.js create mode 100644 d2bs/kolbot/libs/core/Data/GameData.js create mode 100644 d2bs/kolbot/libs/core/Data/LocaleStringID.js create mode 100644 d2bs/kolbot/libs/core/Data/MonsterData.js create mode 100644 d2bs/kolbot/libs/core/Data/NTItemAlias.js create mode 100644 d2bs/kolbot/libs/core/Experience.js create mode 100644 d2bs/kolbot/libs/core/Item.js rename d2bs/kolbot/libs/{common => core}/Loader.js (90%) create mode 100644 d2bs/kolbot/libs/core/Me.js create mode 100644 d2bs/kolbot/libs/core/Misc.js rename d2bs/kolbot/libs/{NTItemParser.dbl => core/NTItemParser.js} (98%) create mode 100644 d2bs/kolbot/libs/core/Packet.js rename d2bs/kolbot/libs/{common => core}/Pather.js (85%) rename d2bs/kolbot/libs/{common => core}/Pickit.js (88%) rename d2bs/kolbot/libs/{common => core}/Precast.js (96%) rename d2bs/kolbot/libs/{common => core}/Prototypes.js (77%) rename d2bs/kolbot/libs/{common => core}/Runewords.js (99%) create mode 100644 d2bs/kolbot/libs/core/Skill.js create mode 100644 d2bs/kolbot/libs/core/Storage.js rename d2bs/kolbot/libs/{common => core}/Town.js (84%) create mode 100644 d2bs/kolbot/libs/core/Util.js create mode 100644 d2bs/kolbot/libs/critical.js create mode 100644 d2bs/kolbot/libs/modules/CopyData.js create mode 100644 d2bs/kolbot/libs/modules/LocalChat.js rename d2bs/kolbot/libs/{ => modules}/UnitInfo.js (57%) create mode 100644 d2bs/kolbot/libs/oog/D2Bot.js create mode 100644 d2bs/kolbot/libs/oog/DataFile.js create mode 100644 d2bs/kolbot/libs/oog/FileAction.js create mode 100644 d2bs/kolbot/libs/oog/ShitList.js rename d2bs/kolbot/libs/{bots => scripts}/Abaddon.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/AncientTunnels.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Andariel.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/AutoBaal.js (61%) rename d2bs/kolbot/libs/{bots => scripts}/Baal.js (98%) create mode 100644 d2bs/kolbot/libs/scripts/BaalAssistant.js rename d2bs/kolbot/libs/{bots => scripts}/BaalHelper.js (98%) rename d2bs/kolbot/libs/{bots => scripts}/BattleOrders.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/BattlemaidSarina.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Bishibosh.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/BoBarbHelper.js (96%) rename d2bs/kolbot/libs/{bots => scripts}/BoneAsh.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Bonesaw.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/ChestMania.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/ClassicChaosAssistant.js (98%) rename d2bs/kolbot/libs/{bots => scripts}/ClearAnyArea.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Coldcrow.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Coldworm.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/ControlBot.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/Corpsefire.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Countess.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Cows.js (94%) rename d2bs/kolbot/libs/{bots => scripts}/Crafting.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/CreepingFeature.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/CrushTele.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/DeveloperMode.js (88%) rename d2bs/kolbot/libs/{bots => scripts}/Diablo.js (98%) rename d2bs/kolbot/libs/{bots => scripts}/DiabloHelper.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/Duriel.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Eldritch.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Endugu.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Eyeback.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Fangskin.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Follower.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/Frozenstein.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Gamble.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/GemHunter.js (83%) rename d2bs/kolbot/libs/{bots => scripts}/GetKeys.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/GhostBusters.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Hephasto.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/IPHunter.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Icehawk.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Izual.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/KillDclone.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/KurastTemples.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/MFHelper.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/Mausoleum.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Mephisto.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Nihlathak.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/OrgTorch.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/OuterSteppes.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Pindleskin.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Pit.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Questing.js (76%) rename d2bs/kolbot/libs/{bots => scripts}/Radament.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Rakanishu.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Rushee.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/Rusher.js (95%) rename d2bs/kolbot/libs/{bots => scripts}/SealLeecher.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/SharpTooth.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/ShopBot.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/Smith.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Snapchip.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Stormtree.js (100%) create mode 100644 d2bs/kolbot/libs/scripts/Summoner.js rename d2bs/kolbot/libs/{bots => scripts}/Synch.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Synch2.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Test.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/ThreshSocket.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Tombs.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Travincal.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/TravincalLeech.js (98%) rename d2bs/kolbot/libs/{bots => scripts}/Treehead.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Tristram.js (97%) rename d2bs/kolbot/libs/{bots => scripts}/TristramLeech.js (98%) rename d2bs/kolbot/libs/{bots => scripts}/UndergroundPassage.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/UserAddon.js (83%) rename d2bs/kolbot/libs/{bots => scripts}/WPGetter.js (100%) rename d2bs/kolbot/libs/{bots => scripts}/Wakka.js (99%) rename d2bs/kolbot/libs/{bots => scripts}/Worldstone.js (100%) create mode 100644 d2bs/kolbot/libs/starter/AdvancedConfig.js create mode 100644 d2bs/kolbot/libs/starter/StarterConfig.js rename d2bs/kolbot/libs/{ => systems/automule}/AutoMule.js (78%) create mode 100644 d2bs/kolbot/libs/systems/automule/MuleConfig.js create mode 100644 d2bs/kolbot/libs/systems/automule/TorchAnniMules.js rename d2bs/kolbot/libs/{ => systems/crafting}/CraftingSystem.js (90%) create mode 100644 d2bs/kolbot/libs/systems/crafting/TeamsConfig.js rename d2bs/kolbot/libs/{ => systems/gambling}/Gambling.js (70%) create mode 100644 d2bs/kolbot/libs/systems/gambling/TeamsConfig.js rename d2bs/kolbot/libs/{ => systems/gameaction}/GameAction.js (89%) create mode 100644 d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js rename d2bs/kolbot/libs/{ => systems/mulelogger}/MuleLogger.js (79%) create mode 100644 d2bs/kolbot/libs/systems/torch/FarmerConfig.js rename d2bs/kolbot/libs/{ => systems/torch}/TorchSystem.js (69%) delete mode 100644 d2bs/kolbot/sdk/LocaleStringID.js delete mode 100644 d2bs/kolbot/sdk/Unit.d.ts rename d2bs/kolbot/sdk/{ => txt}/Shrines.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/SuperUniques.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/areas.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/basestats.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/bodylocations.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/chests.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/enchants.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/getskillinfo.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/itemflags.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/miscscreenmodes.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/modes.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/monster classID's.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/npcmenuid.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/quests.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/roomstats.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/skills.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/states.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/stats.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/stats_skills.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/stats_tabs.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/superunique_presetunitids.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/tile.d2l (100%) rename d2bs/kolbot/sdk/{ => txt}/uiflag.txt (100%) rename d2bs/kolbot/sdk/{ => txt}/waypoints.txt (100%) create mode 100644 d2bs/kolbot/sdk/types/Attack.d.ts create mode 100644 d2bs/kolbot/sdk/types/AutoMule.d.ts create mode 100644 d2bs/kolbot/sdk/types/Config.d.ts create mode 100644 d2bs/kolbot/sdk/types/Cubing.d.ts create mode 100644 d2bs/kolbot/sdk/types/Item.d.ts create mode 100644 d2bs/kolbot/sdk/types/Loader.d.ts create mode 100644 d2bs/kolbot/sdk/types/Misc.d.ts create mode 100644 d2bs/kolbot/sdk/types/NTIP.d.ts create mode 100644 d2bs/kolbot/sdk/types/OOG.d.ts create mode 100644 d2bs/kolbot/sdk/types/Pather.d.ts create mode 100644 d2bs/kolbot/sdk/types/Pickit.d.ts create mode 100644 d2bs/kolbot/sdk/types/Runewords.d.ts create mode 100644 d2bs/kolbot/sdk/types/Skill.d.ts create mode 100644 d2bs/kolbot/sdk/types/Storage.d.ts create mode 100644 d2bs/kolbot/sdk/types/Town.d.ts create mode 100644 d2bs/kolbot/sdk/types/Util.d.ts create mode 100644 d2bs/kolbot/sdk/types/sdk.d.ts rename d2bs/kolbot/{tools => threads}/AntiHostile.js (94%) rename d2bs/kolbot/{tools => threads}/AntiIdle.js (89%) rename d2bs/kolbot/{tools => threads}/AreaWatcher.js (66%) rename d2bs/kolbot/{tools => threads}/AutoBuildThread.js (93%) rename d2bs/kolbot/{tools => threads}/CloneKilla.js (58%) rename d2bs/kolbot/{tools => threads}/HeartBeat.js (73%) rename d2bs/kolbot/{tools => threads}/Party.js (65%) rename d2bs/kolbot/{tools => threads}/RushThread.js (97%) rename d2bs/kolbot/{tools => threads}/ToolsThread.js (84%) rename d2bs/kolbot/{tools => threads}/TownChicken.js (82%) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 613d92ffe..8d983277e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ d2bs/kolbot/logs/**/** d2bs/kolbot/mules/**/*.txt d2bs/logs/*.log d2bs/kolbot/libs/manualplay/config/*.*.js +d2bs/kolbot/libs/config/*.*.js diff --git a/d2bs/kolbot/D2BotBlank.dbj b/d2bs/kolbot/D2BotBlank.dbj index 5e45a44c2..826c2e2c9 100644 --- a/d2bs/kolbot/D2BotBlank.dbj +++ b/d2bs/kolbot/D2BotBlank.dbj @@ -1,15 +1,18 @@ /** * @filename D2BotBlank.dbj -* @author kolton +* @author kolton, theBGuy * @desc Entry script for testing * */ + + function main() { - include("json2.js"); - include("OOG.js"); - include("common/util.js"); - include("common/misc.js"); + include("critical.js"); // required + if (!FileTools.exists("data/" + me.profile + ".json")) { + DataFile.create(); + } + addEventListener("copydata", Starter.receiveCopyData); while (!Starter.handle) { @@ -19,14 +22,23 @@ function main() { DataFile.updateStats("handle", Starter.handle); delay(500); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); - if (!FileTools.exists("data/" + me.profile + ".json")) { - DataFile.create(); + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); } while (true) { - Starter.isUp = me.ingame ? "yes" : "no"; + delay(1000); + + if (me.gameReady) { + Starter.isUp === "no" && (Starter.isUp = "yes"); + me.ingame && D2Bot.updateStatus("(Char: " + me.charname + ") (Game: " + (me.gamename || "singleplayer") + ") (Level: " + me.charlvl + ")"); + } else { + D2Bot.updateStatus("Out of Game"); + Starter.isUp = "no"; + } delay(1000); } diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 0a1951f74..6571b27f8 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -4,9 +4,10 @@ * @desc Entry script for following bots using channels * */ -include("StarterConfig.js"); -// D2BotChannel specific settings - for global settings see libs/StarterConfig.js +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// D2BotChannel specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.Games = [""]; // List of games to look for. Example: Games: ["some baal-", "chaos run-"], Starter.Config.Passwords = [""]; // List of game passwords. Each array in Games array should have a matching element in Passwords. Use "" for blank pw. Starter.Config.JoinDelay = 10; // Seconds to wait between announcement and clicking join @@ -20,25 +21,18 @@ Starter.Config.Follow = []; // leader's in game character name, only use this if // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds // Starter.Config.JoinChannel = ""; // Default channel. -// todo: figure out a way to check if player who announced game is still in channel - -// No touchy! -include("json2.js"); -include("polyfill.js"); -include("OOG.js"); -include("automule.js"); -include("gambling.js"); -include("torchsystem.js"); -include("craftingsystem.js"); -include("common/misc.js"); -include("common/util.js"); -include("common/prototypes.js"); -let sdk = require("./modules/sdk"); + +// the only things we really need from these are their oog checks +includeSystemLibs(); + 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 channelTick = getTickCount(); @@ -132,11 +126,7 @@ function locationAction (location) { if (!ControlAction.mutedKey && (!Starter.chatActionsDone || getTickCount() - channelTick >= 120e3)) { if (Starter.Config.JoinChannel !== "") { - if (typeof AdvancedConfig[me.profile] === "object" && typeof AdvancedConfig[me.profile].JoinChannel === "string") { - joinInfo.joinChannel = AdvancedConfig[me.profile].JoinChannel; - } else { - joinInfo.joinChannel = Starter.Config.JoinChannel; - } + joinInfo.joinChannel = Starter.Config.JoinChannel; if (joinInfo.joinChannel) { if (ControlAction.joinChannel(joinInfo.joinChannel)) { @@ -366,24 +356,20 @@ function locationAction (location) { Controls.JoinGameName.setText(joinInfo.gameName); Controls.JoinGamePass.setText(joinInfo.gamePass); - if (typeof AdvancedConfig[me.profile] === "object" && typeof AdvancedConfig[me.profile].AnnounceGame === "boolean" && typeof AdvancedConfig[me.profile].AnnounceMessage === "string") { - Starter.sayMsg(AdvancedConfig[me.profile].AnnounceMessage + " " + joinInfo.gameName); + if (Starter.Config.AnnounceGames && Starter.Config.AnnounceMessage) { + Starter.sayMsg(Starter.Config.AnnounceMessage + " " + joinInfo.gameName); } // Only delay on first join - the rest is handled by GameDoesNotExistTimeout. Any other case is instant fail (ie. full game). if (retry === 0 || Starter.lastGameStatus === "pending") { - if (typeof AdvancedConfig[me.profile] === "object" && typeof AdvancedConfig[me.profile].JoinDelay === "number") { - ControlAction.timeoutDelay("Custom Join Delay", AdvancedConfig[me.profile].JoinDelay * 1e3); - } else if (Starter.Config.JoinDelay) { - ControlAction.timeoutDelay("Join Game Delay", Starter.Config.JoinDelay * 1e3); - } + ControlAction.timeoutDelay("Join Game Delay", Starter.Config.JoinDelay * 1e3); } - me.blockmouse = true; + me.blockMouse = true; Controls.JoinGame.click(); - me.blockmouse = false; + me.blockMouse = false; Starter.lastGameStatus = "pending"; Starter.locationTimeout(5000, location); @@ -491,7 +477,7 @@ function main() { DataFile.updateStats("handle", Starter.handle); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index 7b571c313..39fab0816 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -4,11 +4,14 @@ * @credits Whoever did the original D2BotAccountCleaner.dbj * @desc The purpose of this entryscript is to clean/remove characters and/or files easily * +* @typedef {import("./sdk/globals")} */ -include("StarterConfig.js"); -// D2BotCleaner settings -// New Stuff - DataCleaner to delete old files associated with running kolbot or SoloPlay +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// D2BotCleaner settings - for global settings @see libs/starter/StarterConfig.js +// New Stuff: +// DataCleaner - to delete old files associated with running kolbot or SoloPlay // SaveFiles - to save important SoloPlay files to SoloPlay/Data/ for performance review //***********************************************************************************************************************// // DataCleaner and SaveFiles can both be used for cleaning/saving files without having to delete associated characters // @@ -22,6 +25,9 @@ Starter.Config.DelayBetweenAccounts = rand(15, 30); //Seconds to wait before cle // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds +/** + * @todo this section should be in it's own config leaving this file only containing core logic + */ const AccountsToClean = { /* Format: "account1/password1/realm": ["charname1", "charname2"], @@ -93,14 +99,6 @@ const AdvancedCleanerConfig = { rangeStop: 10 }; -// No touchy! -include("json2.js"); -include("polyfill.js"); -include("OOG.js"); -include("common/misc.js"); -include("common/prototypes.js"); -include("common/util.js"); -let sdk = require("./modules/sdk"); let Controls = require("./modules/Control"); if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { @@ -469,7 +467,7 @@ function main () { DataFile.updateStats("handle", Starter.handle); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 1d41eaec7..62a9a2233 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -4,14 +4,18 @@ * @desc Entry script for following bots running on the same pc * */ -include("StarterConfig.js"); -// D2BotFollow specific settings - for global settings see libs/StarterConfig.js +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// D2BotFollow specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.JoinRetryDelay = 5; // Time in seconds to wait before next join attempt // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds +/** + * @todo this section should be in it's own config leaving this file only containing core logic + */ /* Join game settings Format: "leader's profile": ["leecher 1 profile", "leecher 2 profile", ...] If you want everyone to join the same leader, use "leader's profile": ["all"] @@ -26,29 +30,25 @@ Starter.Config.JoinRetryDelay = 5; // Time in seconds to wait before next join a const JoinSettings = { "Leader": ["Leecher"], + "map": ["all"] }; -// No touchy! -include("json2.js"); -include("polyfill.js"); -include("OOG.js"); -include("automule.js"); -include("gambling.js"); -include("craftingsystem.js"); -include("torchsystem.js"); -include("common/misc.js"); -include("common/util.js"); -let sdk = require("./modules/sdk"); +// the only things we really need from these are their oog checks +includeSystemLibs(); + 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 lastGameTick, leader = "", - announced = false, - lastGame = []; +let lastGameTick, leader = ""; +let announced = false; +let lastGame = []; if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; @@ -220,8 +220,8 @@ function locationAction (location) { print("joining game " + Starter.joinInfo.gameName); - if (typeof AdvancedConfig[me.profile] === "object" && typeof AdvancedConfig[me.profile].JoinDelay === "number") { - ControlAction.timeoutDelay("Custom Join Delay", AdvancedConfig[me.profile].JoinDelay * 1e3); + if (typeof Starter.Config.JoinDelay === "number") { + ControlAction.timeoutDelay("Custom Join Delay", Starter.Config.JoinDelay * 1e3); } me.blockMouse = true; @@ -385,7 +385,9 @@ function main () { DataFile.updateStats("handle", Starter.handle); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); + + require("./modules/OOGGuard"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index 4044ee662..fd63d633d 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -4,9 +4,10 @@ * @desc Entry script for limedrop * */ -include("StarterConfig.js"); -// D2BotGameAction specific settings - for global settings see libs/StarterConfig.js +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// D2BotGameAction specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.MinGameTime = 0; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby Starter.Config.CreateGameDelay = 5; // Seconds to wait before creating a new game Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/banned key or after realm down @@ -14,15 +15,11 @@ Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/ba // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -// No touchy! -include("json2.js"); -include("polyfill.js"); -include("OOG.js"); -include("GameAction.js"); -include("MuleLogger.js"); -include("common/misc.js"); -include("common/util.js"); -let sdk = require("./modules/sdk"); +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); + let Controls = require("./modules/Control"); let Overrides = require("./modules/Override"); @@ -30,9 +27,9 @@ if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; } -let tag, charList, - ftj = 0, - creatingActions = ["doMule"]; +let tag, charList; +let ftj = 0; +let creatingActions = ["doMule"]; new Overrides.Override(Starter, Starter.receiveCopyData, function(orignal, mode, msg) { if (mode === 3) return; @@ -340,7 +337,7 @@ function main () { DataFile.updateStats("handle", Starter.handle); delay(500); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 051da986b..2a9af63a2 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -4,28 +4,25 @@ * @author kolton, theBGuy * @desc Entry script for leader * +* @typedef {import("./sdk/globals")} */ -include("StarterConfig.js"); -// - for global settings see libs/StarterConfig.js +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// - for global settings @see libs/starter/StarterConfig.js // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -// No touchy! -include("json2.js"); -include("OOG.js"); -include("automule.js"); -include("gambling.js"); -include("craftingsystem.js"); -include("torchsystem.js"); -include("Polyfill.js"); -include("common/misc.js"); -include("common/util.js"); -let sdk = require("./modules/sdk"); +// the only things we really need from these are their oog checks +includeSystemLibs(); + let Controls = require("./modules/Control"); -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 then + delete Starter.AdvancedConfig; } if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { @@ -53,7 +50,7 @@ function locationAction (location) { Starter.pingQuit = false; } - if (Starter.Config.JoinChannel !== "" || (AdvancedConfig[me.profile] && AdvancedConfig[me.profile].JoinChannel !== "")) { + if (Starter.Config.JoinChannel !== "") { Controls.LobbyEnterChat.click(); break; @@ -121,11 +118,7 @@ function locationAction (location) { DataFile.updateStats("runs", Starter.gameCount); } - if (AdvancedConfig[me.profile] && AdvancedConfig[me.profile].hasOwnProperty("AfterGameMessage")) { - Starter.chanInfo.afterMsg = AdvancedConfig[me.profile].AfterGameMessage; - } else { - Starter.chanInfo.afterMsg = Starter.Config.AfterGameMessage; - } + Starter.chanInfo.afterMsg = Starter.Config.AfterGameMessage; // check that we are in the channel we are supposed to be in if (Starter.chanInfo.joinChannel.length) { @@ -149,18 +142,8 @@ function locationAction (location) { if (!Starter.chatActionsDone) { Starter.chatActionsDone = true; - - if (AdvancedConfig[me.profile] && AdvancedConfig[me.profile].hasOwnProperty("JoinChannel")) { - Starter.chanInfo.joinChannel = AdvancedConfig[me.profile].JoinChannel; - } else { - Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; - } - - if (AdvancedConfig[me.profile] && AdvancedConfig[me.profile].hasOwnProperty("FirstJoinMessage")) { - Starter.chanInfo.firstMsg = AdvancedConfig[me.profile].FirstJoinMessage; - } else { - Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; - } + Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; + Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; if (Starter.chanInfo.joinChannel) { typeof Starter.chanInfo.joinChannel === "string" && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); @@ -185,11 +168,7 @@ function locationAction (location) { } // Announce game - if (AdvancedConfig[me.profile] && AdvancedConfig[me.profile].hasOwnProperty("AnnounceGames")) { - Starter.chanInfo.announce = AdvancedConfig[me.profile].AnnounceGames; - } else { - Starter.chanInfo.announce = Starter.Config.AnnounceGames; - } + Starter.chanInfo.announce = Starter.Config.AnnounceGames; Starter.LocationEvents.openCreateGameWindow(); @@ -342,7 +321,7 @@ function main () { DataFile.updateStats("handle", Starter.handle); delay(500); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/D2BotMap.dbj b/d2bs/kolbot/D2BotMap.dbj index 280669538..f9be928fb 100644 --- a/d2bs/kolbot/D2BotMap.dbj +++ b/d2bs/kolbot/D2BotMap.dbj @@ -4,13 +4,14 @@ * @desc Entry script for running map mode * */ -include("json2.js"); -include("polyfill.js"); -include("OOG.js"); -include("common/misc.js"); -include("common/util.js"); function main () { + include("critical.js"); // required + + if (!FileTools.exists("data/" + me.profile + ".json")) { + DataFile.create(); + } + addEventListener("copydata", Starter.receiveCopyData); while (!Starter.handle) { @@ -20,7 +21,7 @@ function main () { DataFile.updateStats("handle", Starter.handle); delay(500); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index ba76e9b69..60df37c0f 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -3,16 +3,19 @@ * @author kolton, theBGuy * @desc Entry script for AutoMule.js * +* @typedef {import("./sdk/globals")} */ -include("StarterConfig.js"); +js_strict(true); +include("critical.js"); // required /** * @todo redo how muleing is handled, really dislike how it's handled here */ -// D2BotMule specific settings - for global settings see libs/StarterConfig.js +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// D2BotMule specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.MinGameTime = 30; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby -Starter.Config.MaxGameTime = 60; // Maximum game length in minutes, only for continuous muling +Starter.Config.MaxGameTime = 10; // Maximum game length in minutes, only for continuous muling Starter.Config.CreateGameDelay = 5; // Seconds to wait before creating a new game Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/banned key or after realm down Starter.Config.ExitToMenu = false; // Set to true to wait out restriction in main menu or false to wait in lobby. @@ -22,16 +25,13 @@ Starter.Config.MakeAccountOnFailure = true; // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -// No touchy! -include("json2.js"); -include("oog.js"); -include("automule.js"); -include("mulelogger.js"); -include("torchsystem.js"); -include("NTItemParser.dbl"); -include("NTItemAlias.dbl"); -include("common/util.js"); -includeCommonLibs(); +// globals needed for core gameplay +includeCoreLibs(); + +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); let Controls = require("./modules/Control"); let Overrides = require("./modules/Override"); @@ -202,7 +202,10 @@ const Mule = { Storage.Init(); checkOnJoin && (status = "begin"); Starter.inGame = true; - Mule.continuousMule && !muleObj.onlyLogWhenFull && MuleLogger.logChar(); + if (Mule.continuousMule) { + !muleObj.onlyLogWhenFull && MuleLogger.logChar(); + addEventListener("gameevent", gameEvent); + } // Worker.runInBackground.areaWatcher = Mule.areaWatcher; // bugs for some reason, quits game then on re-join d2bot spams In mule game for ~30seconds if (Worker.runInBackground.antiIdle === undefined) { Worker.runInBackground.antiIdle = Mule.antiIdle; @@ -269,7 +272,7 @@ const Mule = { }, quit: function () { - ["default.dbj", "tools/AntiIdle.js", "tools/AreaWatcher.js"].forEach(thread => { + ["default.dbj", "threads/AntiIdle.js", "threads/AreaWatcher.js"].forEach(thread => { let script = getScript(thread); if (script && script.running && script.stop()) { delay(100); @@ -295,6 +298,7 @@ const Mule = { gameRefresh: function () { if (!this.continuousMule) return this.quit(); console.log("MaxGameTime Reached: "); + removeEventListener("gameevent", gameEvent); Mule.quit(); Starter.firstLogin = true; @@ -898,13 +902,13 @@ function main () { DataFile.updateStats("handle", Starter.handle); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); delay(500); } - + D2Bot.updateRuns(); // we need the mule to swap keys somehow after all delay(1000); @@ -916,7 +920,6 @@ function main () { muleMode = AutoMule.getMuleMode(); muleObj = AutoMule.getMuleObject(muleMode, "", true); muleFilename = AutoMule.getMuleFilename(muleMode, "", true); - addEventListener("gameevent", gameEvent); } else { // Wait for master before login = give room to determine muling mode (normal or torch) while (!master) { @@ -951,53 +954,61 @@ function main () { MainLoop: while (true) { try { - if (me.ingame && me.gameReady) { - if (!Starter.inGame) { - if (!Mule.init()) { - console.debug("Failed to init. Trying again. Ingame and ready? " + (me.ingame && me.gameReady && !!me.area)); - delay(1000); - continue; + while (me.ingame) { + if (me.gameReady) { + if (!Starter.inGame) { + if (!Mule.init()) { + console.debug("Failed to init. Trying again. Ingame and ready? " + (me.ingame && me.gameReady && !!me.area)); + delay(1000); + continue; + } } - } - if (!Mule.continuousMule) { - Mule.waitForMaster(); - } + if (!Mule.continuousMule) { + Mule.waitForMaster(); + } - D2Bot.updateStatus(Mule.statusString + " Status: " + status + Starter.timer(me.gamestarttime)); + D2Bot.updateStatus(Mule.statusString + " Status: " + status + Starter.timer(me.gamestarttime)); - if (status === "begin") { - switch (Mule.pickItems()) { - // done picking, tell the master to leave game and kill mule profile - case "done": - Mule.done(); + if (status === "begin") { + switch (Mule.pickItems()) { + // done picking, tell the master to leave game and kill mule profile + case "done": + Mule.done(); - return; - // can't fit more items, get to next character or account - case "next": - Mule.next(); + return; + // can't fit more items, get to next character or account + case "next": + Mule.next(); - // should fix hitting gameRefresh when making a new character - continue MainLoop; - case "ready": - Mule.recheckTick = getTickCount(); + // should fix hitting gameRefresh when making a new character + continue MainLoop; + case "ready": + Mule.recheckTick = getTickCount(); - break; - case "fail": - // Try again - break; + break; + case "fail": + // Try again + break; + } } - } - if (Mule.continuousMule) { - if (Starter.Config.MaxGameTime > 0 && getTickCount() - me.gamestarttime > Time.minutes(Starter.Config.MaxGameTime) && Mule.foreverAlone()) { - Mule.gameRefresh(); - } else if (getTickCount() - Mule.recheckTick > Time.minutes(10)) { - // recheck every 10 minutes? - status = "begin"; + if (Mule.continuousMule) { + if (Starter.Config.MaxGameTime > 0 && getTickCount() - me.gamestarttime > Time.minutes(Starter.Config.MaxGameTime) && Mule.foreverAlone()) { + Mule.gameRefresh(); + + break; + } else if (getTickCount() - Mule.recheckTick > Time.minutes(10)) { + // recheck every 10 minutes? + status = "begin"; + } } } - } else if (!me.ingame) { + + delay(1000); + } + + if (!me.ingame) { delay(1000); locationAction(getLocation()); } diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index b17c3fa9e..a9cc80e5e 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -4,9 +4,10 @@ * @desc Entry script for Mulelogger.js * */ -include("StarterConfig.js"); -// D2BotMuleLog specific settings - for global settings see libs/StarterConfig.js +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// D2BotMuleLog specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.MinGameTime = rand(150, 180); // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby Starter.Config.CreateGameDelay = 5; // Seconds to wait before creating a new game Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/banned key or after realm down @@ -14,26 +15,21 @@ Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/ba // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -// No touchy! -include("json2.js"); -include("polyfill.js"); -include("OOG.js"); -include("MuleLogger.js"); +// only libs we should need here as te rest of the actions are performed from default.dbj thread include("DropperSetup.js"); -include("common/misc.js"); -include("common/util.js"); -include("common/prototypes.js"); +include("systems/mulelogger/MuleLogger.js"); + let Controls = require("./modules/Control"); if (!FileTools.exists("data/" + me.profile + ".json")) { DataFile.create(); } -let currAcc, - usingDroper = isIncluded("DropperSetup.js"), - charList = [], - accounts = [], - chars = []; +let currAcc; +let usingDroper = isIncluded("DropperSetup.js"); +let charList = []; +let accounts = []; +let chars = []; function parseInfo() { usingDroper && parseDropperAccounts(accounts, chars); @@ -308,7 +304,7 @@ function main() { DataFile.updateStats("handle", Starter.handle); delay(500); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index b5546ff98..782587b0e 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -4,9 +4,10 @@ * @desc Entry script for following public games * */ -include("StarterConfig.js"); -// D2BotPubJoin specific settings - for global settings see libs/StarterConfig.js +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// D2BotPubJoin specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.MinGameTime = 0; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby Starter.Config.ResetCount = 0; // Reset game count back to 1 every X games. Starter.Config.JoinDelay = 10; // Seconds to wait between join attempts @@ -16,6 +17,9 @@ Starter.Config.AttemptNextGameRetrys = 5; // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds +/** + * @todo this section should be in it's own config leaving this file only containing core logic + */ /** IncludeFilter config: @@ -90,22 +94,17 @@ function excludeCheck (game) { return true; } -// No touchy! -include("json2.js"); -include("polyfill.js"); -include("OOG.js"); -include("automule.js"); -include("gambling.js"); -include("craftingsystem.js"); -include("torchsystem.js"); -include("common/misc.js"); -include("common/util.js"); -let sdk = require("./modules/sdk"); +// the only things we really need from these are their oog checks +includeSystemLibs(); + 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; } new Overrides.Override(Starter, Starter.setNextGame, function (orignal, gameName) { @@ -113,7 +112,7 @@ new Overrides.Override(Starter, Starter.setNextGame, function (orignal, gameName return text.replace(/(\d*)$/, (_, t) => (+t + 1).toString().padStart(t.length, 0)); } - let nextGame = (gameName || this.randomString(null, true)); + let nextGame = (gameName || Starter.randomString(null, true)); nextGame = incrementString(nextGame); DataFile.updateStats("nextGame", nextGame); @@ -239,7 +238,7 @@ function locationAction (location) { if (gameList) { doneGames = []; gameToJoin = false; - FileTools.exists("logs/doneGames.json") && (doneGames = JSON.parse(Misc.fileAction("logs/doneGames.json", 0))); + FileTools.exists("logs/doneGames.json") && (doneGames = JSON.parse(FileAction.read("logs/doneGames.json"))); gameList.sort(function (a, b) { return b.players - a.players; @@ -257,7 +256,7 @@ function locationAction (location) { if (gameToJoin) { doneGames.length >= 20 && doneGames.shift(); doneGames.push(gameToJoin); - Misc.fileAction("logs/doneGames.json", 1, JSON.stringify(doneGames)); + FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); me.blockMouse = true; @@ -378,7 +377,7 @@ function main() { DataFile.updateStats("handle", Starter.handle); delay(500); D2Bot.init(); - load("tools/heartbeat.js"); + load("threads/heartbeat.js"); while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index bcaad5ad6..0dd85b67e 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/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 @@ -38,25 +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"); - if (getScript("d2botmap.dbj")) { - include("manualplay/MapMode.js"); - MapMode.include(); - Config.init(true); - LocalChat.init(); + // 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; + } - // load threads - me.automap = true; + // map mode runs in it's own thread + if (getScript("d2botmap.dbj")) { 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 @@ -65,7 +57,7 @@ function main () { // don't load default for dropper/mules if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { FileTools.exists("libs/ItemDB.js") && include("ItemDB.js"); - load("tools/AreaWatcher.js"); + load("threads/AreaWatcher.js"); while (true) { delay(1000); @@ -143,14 +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")) { + 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."); @@ -173,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)); } } @@ -189,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); } diff --git a/d2bs/kolbot/libs/GameData.js b/d2bs/kolbot/libs/GameData.js deleted file mode 100644 index a518f47a9..000000000 --- a/d2bs/kolbot/libs/GameData.js +++ /dev/null @@ -1,289 +0,0 @@ -/** -* @filename GameData.js -* @author Nishimura-Katsuo -* @desc game data library -* -*/ - -const MONSTER_INDEX_COUNT = 734; -const AREA_INDEX_COUNT = 137; -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 - -/** - * MonsterData[classID] - * .Index = Index of this monster - * .Level = Level of this monster in normal (use GameData.monsterLevel to find monster levels) - * .Ranged = if monster is ranged - * .Rarity = weight of this monster in level generation - * .Threat = threat level used by mercs - * .Align = alignment of unit (determines what it will attack) - * .Melee = if monster is melee - * .NPC = if unit is NPC - * .Demon = if monster is demon - * .Flying = if monster is flying - * .Boss = if monster is a boss - * .ActBoss = if monster is act boss - * .Killable = if monster can be killed - * .Convertable = if monster is affected by convert or mind blast - * .NeverCount = if not counted as a minion - * .DeathDamage = explodes on death - * .Regeneration = hp regeneration - * .LocaleString = locale string index for getLocaleString - * .ExperienceModifier = percent of base monster exp this unit rewards when killed - * .Undead = 2 if greater undead, 1 if lesser undead, 0 if neither - * .Drain = drain effectiveness percent - * .Block = block percent - * .Physical = physical resist - * .Magic = magic resist - * .Fire = fire resist - * .Lightning = lightning resist - * .Poison = poison resist - * .Minions = array of minions that can spawn with this unit - */ - -const MonsterData = Array(MONSTER_INDEX_COUNT); - -for (let i = 0; i < MonsterData.length; i++) { - let index = i; - MonsterData[i] = Object.freeze(Object.defineProperties({}, { - Index: {get: () => index, enumerable: true}, - Level: {get: () => getBaseStat("monstats", index, "Level"), enumerable: true}, // normal only, nm/hell are determined by area's LevelEx - Ranged: {get: () => getBaseStat("monstats", index, "RangedType"), enumerable: true}, - Rarity: {get: () => getBaseStat("monstats", index, "Rarity"), enumerable: true}, - Threat: {get: () => getBaseStat("monstats", index, "threat"), enumerable: true}, - Align: {get: () => getBaseStat("monstats", index, "Align"), enumerable: true}, - Melee: {get: () => getBaseStat("monstats", index, "isMelee"), enumerable: true}, - NPC: {get: () => getBaseStat("monstats", index, "npc"), enumerable: true}, - Demon: {get: () => getBaseStat("monstats", index, "demon"), enumerable: true}, - Flying: {get: () => getBaseStat("monstats", index, "flying"), enumerable: true}, - Boss: {get: () => getBaseStat("monstats", index, "boss"), enumerable: true}, - ActBoss: {get: () => getBaseStat("monstats", index, "primeevil"), enumerable: true}, - Killable: {get: () => getBaseStat("monstats", index, "killable"), enumerable: true}, - Convertable: {get: () => getBaseStat("monstats", index, "switchai"), enumerable: true}, - NeverCount: {get: () => getBaseStat("monstats", index, "neverCount"), enumerable: true}, - DeathDamage: {get: () => getBaseStat("monstats", index, "deathDmg"), enumerable: true}, - Regeneration: {get: () => getBaseStat("monstats", index, "DamageRegen"), enumerable: true}, - LocaleString: {get: () => getBaseStat("monstats", index, "NameStr"), enumerable: true}, - ExperienceModifier: {get: () => getBaseStat("monstats", index, ["Exp", "Exp(N)", "Exp(H)"][me.diff]), enumerable: true}, - Undead: {get: () => (getBaseStat("monstats", index, "hUndead") && 2) | (getBaseStat("monstats", index, "lUndead") && 1), enumerable: true}, - Drain: {get: () => getBaseStat("monstats", index, ["Drain", "Drain(N)", "Drain(H)"][me.diff]), enumerable: true}, - Block: {get: () => getBaseStat("monstats", index, ["ToBlock", "ToBlock(N)", "ToBlock(H)"][me.diff]), enumerable: true}, - Physical: {get: () => getBaseStat("monstats", index, ["ResDm", "ResDm(N)", "ResDm(H)"][me.diff]), enumerable: true}, - Magic: {get: () => getBaseStat("monstats", index, ["ResMa", "ResMa(N)", "ResMa(H)"][me.diff]), enumerable: true}, - Fire: {get: () => getBaseStat("monstats", index, ["ResFi", "ResFi(N)", "ResFi(H)"][me.diff]), enumerable: true}, - Lightning: {get: () => getBaseStat("monstats", index, ["ResLi", "ResLi(N)", "ResLi(H)"][me.diff]), enumerable: true}, - Cold: {get: () => getBaseStat("monstats", index, ["ResCo", "ResCo(N)", "ResCo(H)"][me.diff]), enumerable: true}, - Poison: {get: () => getBaseStat("monstats", index, ["ResPo", "ResPo(N)", "ResPo(H)"][me.diff]), enumerable: true}, - Minions: {get: () => [getBaseStat("monstats", index, "minion1"), getBaseStat("monstats", index, "minion2")].filter(mon => mon !== 65535), enumerable: true}, - })); -} - -Object.freeze(MonsterData); - -/** - * AreaData[areaID] - * .Super = number of super uniques present in this area - * .Index = areaID - * .Act = act this area is in [0-4] - * .MonsterDensity = value used to determine monster population density - * .ChampionPacks.Min = minimum number of champion or unique packs that spawn here - * .ChampionPacks.Max = maximum number of champion or unique packs that spawn here - * .Waypoint = number in waypoint menu that leads to this area - * .Level = level of area (use GameData.areaLevel) - * .Size.x = width of area - * .Size.y = depth of area - * .Monsters = array of monsters that can spawn in this area - * .LocaleString = locale string index for getLocaleString - */ - -const AreaData = new Array(AREA_INDEX_COUNT); - -for (let i = 0; i < AreaData.length; i++) { - let index = i; - AreaData[i] = Object.freeze(Object.defineProperties({}, { - Super: {get: () => SUPER[index], enumerable: true}, - Index: {get: () => index, enumerable: true}, - Act: {get: () => getBaseStat("levels", index, "Act"), enumerable: true}, - MonsterDensity: {get: () => getBaseStat("levels", index, ["MonDen", "MonDen(N)", "MonDen(H)"][me.diff]), enumerable: true}, - ChampionPacks: {get: () => ({Min: getBaseStat("levels", index, ["MonUMin", "MonUMin(N)", "MonUMin(H)"][me.diff]), Max: getBaseStat("levels", index, ["MonUMax", "MonUMax(N)", "MonUMax(H)"][me.diff])}), enumerable: true}, - Waypoint: {get: () => getBaseStat("levels", index, "Waypoint"), enumerable: true}, - Level: {get: () => getBaseStat("levels", index, ["MonLvl1Ex", "MonLvl2Ex", "MonLvl3Ex"][me.diff]), enumerable: true}, - Size: {get: () => { - 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]) - }; - }, enumerable: true}, - Monsters: {get: () => MONSTER_KEYS.map(key => getBaseStat("levels", index, key)).filter(key => key !== 65535), enumerable: true}, - LocaleString: {get: () => AREA_LOCALE_STRING[index], enumerable: true}, - })); -} - -Object.freeze(AreaData); - -const GameData = { - townAreas: [0, 1, 40, 75, 103, 109], - monsterLevel: function (monsterID, areaID) { - if (me.diff) { // levels on nm/hell are determined by area, not by monster data - return AreaData[areaID].Level; - } - - return MonsterData[monsterID].Level; - }, - monsterExp: function (monsterID, areaID) { - return Experience.monsterExp[this.monsterLevel(monsterID, areaID)][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; - }, - areaLevel: function (areaID) { - let levels = 0, total = 0; - - if (me.diff) { // levels on nm/hell are determined by area, not by monster data - return AreaData[areaID].Level; - } - - AreaData[areaID].Monsters.forEach(mon => { - levels += MonsterData[mon].Level * MonsterData[mon].Rarity; - total += MonsterData[mon].Rarity; - }); - - return Math.round(levels / total); - }, - areaImmunities: function (areaID) { - let resists = {Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0}; - - function checkmon (monID) { - for (let k in resists) { - resists[k] = Math.max(resists[k], MonsterData[monID][k]); - } - } - - AreaData[areaID].Monsters.forEach(mon => { - checkmon(mon); - MonsterData[mon].Minions.forEach(checkmon); - }); - - 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(me); - - if (!party) { - return 1; - } - - count = 1; - - while (party.getNext()) { - count++; - } - } - - return (count + 1) / 2; - }, - partyModifier: function (playerID) { - let party = getParty(me), partyid = -1, level = 0, total = 0; - - if (!party) { - return 1; - } - - 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(me), partyid = -1, level = 0, total = 0, gamesize = 0; - - if (!party) { - return 0; - } - - 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); - }, - areaPartyExp: function (areaID, exclude = null, onlytown = true, ignore = null) { // amount of total party exp gained per kill on average - let party = getParty(me), partyid = -1, partylevels = 0, gamesize = 0, exp = 0, playerexp = 0, poolsize = 0; - - if (!party) { - return 0; - } - - // very rough approximation of unique population ratio, could be approved but this works well enough - let uniqueratio = parseFloat(Config.ChampionBias) * (AreaData[areaID].ChampionPacks.Min + AreaData[areaID].ChampionPacks.Max + AreaData[areaID].Super * 2) / (AreaData[areaID].Size.x * AreaData[areaID].Size.y); - - partyid = party.partyid; - - do { - gamesize++; - - if (party.partyid === partyid && party.name !== exclude && party.gid !== exclude && (!onlytown || this.townAreas.indexOf(party.area) > -1) && (areaID < 128 || party.level >= (1 + me.diff) * 20)) { - partylevels += party.level; - - if (party.name !== ignore && party.gid !== ignore) { - poolsize = 0; - playerexp = 0; - - AreaData[areaID].Monsters.forEach(mon => { - if (MonsterData[mon].Rarity > 0) { - playerexp += ((1 - uniqueratio) + (3 * uniqueratio)) * this.monsterExp(mon, areaID) * this.levelModifier(party.level, this.monsterLevel(mon, areaID)) * MonsterData[mon].Rarity; - poolsize += MonsterData[mon].Rarity; - } - }); - - if (poolsize) { - exp += party.level * playerexp / poolsize; - } - } - } - } while (party.getNext()); - - return (partylevels ? exp * this.multiplayerModifier(gamesize) / partylevels : 0); - } -}; diff --git a/d2bs/kolbot/libs/NTItemAlias.dbl b/d2bs/kolbot/libs/NTItemAlias.dbl deleted file mode 100644 index 4a3157f7f..000000000 --- a/d2bs/kolbot/libs/NTItemAlias.dbl +++ /dev/null @@ -1,1477 +0,0 @@ -/** -* @filename NTItemAlias.dbl -* @author kolton -* @credit d2nt -* @desc Item alias's to work with NTItemParser for kolbots pickit system -* -*/ - -var NTIPAliasType = {}; -NTIPAliasType["shield"] = 2; -NTIPAliasType["armor"] = 3; -NTIPAliasType["gold"] = 4; -NTIPAliasType["bowquiver"] = 5; -NTIPAliasType["crossbowquiver"] = 6; -NTIPAliasType["playerbodypart"] = 7; -NTIPAliasType["herb"] = 8; -NTIPAliasType["potion"] = 9; -NTIPAliasType["ring"] = 10; -NTIPAliasType["elixir"] = 11; -NTIPAliasType["amulet"] = 12; -NTIPAliasType["charm"] = 13; -NTIPAliasType["notused"] = 14; -NTIPAliasType["boots"] = 15; -NTIPAliasType["gloves"] = 16; -NTIPAliasType["notused"] = 17; -NTIPAliasType["book"] = 18; -NTIPAliasType["belt"] = 19; -NTIPAliasType["gem"] = 20; -NTIPAliasType["torch"] = 21; -NTIPAliasType["scroll"] = 22; -NTIPAliasType["notused"] = 23; -NTIPAliasType["scepter"] = 24; -NTIPAliasType["wand"] = 25; -NTIPAliasType["staff"] = 26; -NTIPAliasType["bow"] = 27; -NTIPAliasType["axe"] = 28; -NTIPAliasType["club"] = 29; -NTIPAliasType["sword"] = 30; -NTIPAliasType["hammer"] = 31; -NTIPAliasType["knife"] = 32; -NTIPAliasType["spear"] = 33; -NTIPAliasType["polearm"] = 34; -NTIPAliasType["crossbow"] = 35; -NTIPAliasType["mace"] = 36; -NTIPAliasType["helm"] = 37; -NTIPAliasType["missilepotion"] = 38; -NTIPAliasType["quest"] = 39; -NTIPAliasType["bodypart"] = 40; -NTIPAliasType["key"] = 41; -NTIPAliasType["throwingknife"] = 42; -NTIPAliasType["throwingaxe"] = 43; -NTIPAliasType["javelin"] = 44; -NTIPAliasType["weapon"] = 45; -NTIPAliasType["meleeweapon"] = 46; -NTIPAliasType["missileweapon"] = 47; -NTIPAliasType["thrownweapon"] = 48; -NTIPAliasType["comboweapon"] = 49; -NTIPAliasType["anyarmor"] = 50; -NTIPAliasType["anyshield"] = 51; -NTIPAliasType["miscellaneous"] = 52; -NTIPAliasType["socketfiller"] = 53; -NTIPAliasType["secondhand"] = 54; -NTIPAliasType["stavesandrods"] = 55; -NTIPAliasType["missile"] = 56; -NTIPAliasType["blunt"] = 57; -NTIPAliasType["jewel"] = 58; -NTIPAliasType["classspecific"] = 59; -NTIPAliasType["amazonitem"] = 60; -NTIPAliasType["barbarianitem"] = 61; -NTIPAliasType["necromanceritem"] = 62; -NTIPAliasType["paladinitem"] = 63; -NTIPAliasType["sorceressitem"] = 64; -NTIPAliasType["assassinitem"] = 65; -NTIPAliasType["druiditem"] = 66; -NTIPAliasType["handtohand"] = 67; -NTIPAliasType["orb"] = 68; -NTIPAliasType["voodooheads"] = 69; -NTIPAliasType["auricshields"] = 70; -NTIPAliasType["primalhelm"] = 71; -NTIPAliasType["pelt"] = 72; -NTIPAliasType["cloak"] = 73; -NTIPAliasType["rune"] = 74; -NTIPAliasType["circlet"] = 75; -NTIPAliasType["healingpotion"] = 76; -NTIPAliasType["manapotion"] = 77; -NTIPAliasType["rejuvpotion"] = 78; -NTIPAliasType["staminapotion"] = 79; -NTIPAliasType["antidotepotion"] = 80; -NTIPAliasType["thawingpotion"] = 81; -NTIPAliasType["smallcharm"] = 82; -NTIPAliasType["mediumcharm"] = 83; -NTIPAliasType["largecharm"] = 84; -NTIPAliasType["amazonbow"] = 85; -NTIPAliasType["amazonspear"] = 86; -NTIPAliasType["amazonjavelin"] = 87; -NTIPAliasType["assassinclaw"] = 88; -NTIPAliasType["magicbowquiv"] = 89; -NTIPAliasType["magicxbowquiv"] = 90; -NTIPAliasType["chippedgem"] = 91; -NTIPAliasType["flawedgem"] = 92; -NTIPAliasType["standardgem"] = 93; -NTIPAliasType["flawlessgem"] = 94; -NTIPAliasType["perfectgem"] = 95; -NTIPAliasType["amethyst"] = 96; -NTIPAliasType["diamond"] = 97; -NTIPAliasType["emerald"] = 98; -NTIPAliasType["ruby"] = 99; -NTIPAliasType["sapphire"] = 100; -NTIPAliasType["topaz"] = 101; -NTIPAliasType["skull"] = 102; - -var NTIPAliasClassID = {}; -NTIPAliasClassID["hax"] = 0; NTIPAliasClassID["handaxe"] = 0; -NTIPAliasClassID["axe"] = 1; -NTIPAliasClassID["2ax"] = 2; NTIPAliasClassID["doubleaxe"] = 2; -NTIPAliasClassID["mpi"] = 3; NTIPAliasClassID["militarypick"] = 3; -NTIPAliasClassID["wax"] = 4; NTIPAliasClassID["waraxe"] = 4; -NTIPAliasClassID["lax"] = 5; NTIPAliasClassID["largeaxe"] = 5; -NTIPAliasClassID["bax"] = 6; NTIPAliasClassID["broadaxe"] = 6; -NTIPAliasClassID["btx"] = 7; NTIPAliasClassID["battleaxe"] = 7; -NTIPAliasClassID["gax"] = 8; NTIPAliasClassID["greataxe"] = 8; -NTIPAliasClassID["gix"] = 9; NTIPAliasClassID["giantaxe"] = 9; -NTIPAliasClassID["wnd"] = 10; NTIPAliasClassID["wand"] = 10; -NTIPAliasClassID["ywn"] = 11; NTIPAliasClassID["yewwand"] = 11; -NTIPAliasClassID["bwn"] = 12; NTIPAliasClassID["bonewand"] = 12; -NTIPAliasClassID["gwn"] = 13; NTIPAliasClassID["grimwand"] = 13; -NTIPAliasClassID["clb"] = 14; NTIPAliasClassID["club"] = 14; -NTIPAliasClassID["scp"] = 15; NTIPAliasClassID["scepter"] = 15; -NTIPAliasClassID["gsc"] = 16; NTIPAliasClassID["grandscepter"] = 16; -NTIPAliasClassID["wsp"] = 17; NTIPAliasClassID["warscepter"] = 17; -NTIPAliasClassID["spc"] = 18; NTIPAliasClassID["spikedclub"] = 18; -NTIPAliasClassID["mac"] = 19; NTIPAliasClassID["mace"] = 19; -NTIPAliasClassID["mst"] = 20; NTIPAliasClassID["morningstar"] = 20; -NTIPAliasClassID["fla"] = 21; NTIPAliasClassID["flail"] = 21; -NTIPAliasClassID["whm"] = 22; NTIPAliasClassID["warhammer"] = 22; -NTIPAliasClassID["mau"] = 23; NTIPAliasClassID["maul"] = 23; -NTIPAliasClassID["gma"] = 24; NTIPAliasClassID["greatmaul"] = 24; -NTIPAliasClassID["ssd"] = 25; NTIPAliasClassID["shortsword"] = 25; -NTIPAliasClassID["scm"] = 26; NTIPAliasClassID["scimitar"] = 26; -NTIPAliasClassID["sbr"] = 27; NTIPAliasClassID["sabre"] = 27; -NTIPAliasClassID["flc"] = 28; NTIPAliasClassID["falchion"] = 28; -NTIPAliasClassID["crs"] = 29; NTIPAliasClassID["crystalsword"] = 29; -NTIPAliasClassID["bsd"] = 30; NTIPAliasClassID["broadsword"] = 30; -NTIPAliasClassID["lsd"] = 31; NTIPAliasClassID["longsword"] = 31; -NTIPAliasClassID["wsd"] = 32; NTIPAliasClassID["warsword"] = 32; -NTIPAliasClassID["2hs"] = 33; NTIPAliasClassID["twohandedsword"] = 33; -NTIPAliasClassID["clm"] = 34; NTIPAliasClassID["claymore"] = 34; -NTIPAliasClassID["gis"] = 35; NTIPAliasClassID["giantsword"] = 35; -NTIPAliasClassID["bsw"] = 36; NTIPAliasClassID["bastardsword"] = 36; -NTIPAliasClassID["flb"] = 37; NTIPAliasClassID["flamberge"] = 37; -NTIPAliasClassID["gsd"] = 38; NTIPAliasClassID["greatsword"] = 38; -NTIPAliasClassID["dgr"] = 39; NTIPAliasClassID["dagger"] = 39; -NTIPAliasClassID["dir"] = 40; NTIPAliasClassID["dirk"] = 40; -NTIPAliasClassID["kri"] = 41; NTIPAliasClassID["kris"] = 41; -NTIPAliasClassID["bld"] = 42; NTIPAliasClassID["blade"] = 42; -NTIPAliasClassID["tkf"] = 43; NTIPAliasClassID["throwingknife"] = 43; -NTIPAliasClassID["tax"] = 44; NTIPAliasClassID["throwingaxe"] = 44; -NTIPAliasClassID["bkf"] = 45; NTIPAliasClassID["balancedknife"] = 45; -NTIPAliasClassID["bal"] = 46; NTIPAliasClassID["balancedaxe"] = 46; -NTIPAliasClassID["jav"] = 47; NTIPAliasClassID["javelin"] = 47; -NTIPAliasClassID["pil"] = 48; NTIPAliasClassID["pilum"] = 48; -NTIPAliasClassID["ssp"] = 49; NTIPAliasClassID["shortspear"] = 49; -NTIPAliasClassID["glv"] = 50; NTIPAliasClassID["glaive"] = 50; -NTIPAliasClassID["tsp"] = 51; NTIPAliasClassID["throwingspear"] = 51; -NTIPAliasClassID["spr"] = 52; NTIPAliasClassID["spear"] = 52; -NTIPAliasClassID["tri"] = 53; NTIPAliasClassID["trident"] = 53; -NTIPAliasClassID["brn"] = 54; NTIPAliasClassID["brandistock"] = 54; -NTIPAliasClassID["spt"] = 55; NTIPAliasClassID["spetum"] = 55; -NTIPAliasClassID["pik"] = 56; NTIPAliasClassID["pike"] = 56; -NTIPAliasClassID["bar"] = 57; NTIPAliasClassID["bardiche"] = 57; -NTIPAliasClassID["vou"] = 58; NTIPAliasClassID["voulge"] = 58; -NTIPAliasClassID["scy"] = 59; NTIPAliasClassID["scythe"] = 59; -NTIPAliasClassID["pax"] = 60; NTIPAliasClassID["poleaxe"] = 60; -NTIPAliasClassID["hal"] = 61; NTIPAliasClassID["halberd"] = 61; -NTIPAliasClassID["wsc"] = 62; NTIPAliasClassID["warscythe"] = 62; -NTIPAliasClassID["sst"] = 63; NTIPAliasClassID["shortstaff"] = 63; -NTIPAliasClassID["lst"] = 64; NTIPAliasClassID["longstaff"] = 64; -NTIPAliasClassID["cst"] = 65; NTIPAliasClassID["gnarledstaff"] = 65; -NTIPAliasClassID["bst"] = 66; NTIPAliasClassID["battlestaff"] = 66; -NTIPAliasClassID["wst"] = 67; NTIPAliasClassID["warstaff"] = 67; -NTIPAliasClassID["sbw"] = 68; NTIPAliasClassID["shortbow"] = 68; -NTIPAliasClassID["hbw"] = 69; NTIPAliasClassID["hunter'sbow"] = 69; -NTIPAliasClassID["lbw"] = 70; NTIPAliasClassID["longbow"] = 70; -NTIPAliasClassID["cbw"] = 71; NTIPAliasClassID["compositebow"] = 71; -NTIPAliasClassID["sbb"] = 72; NTIPAliasClassID["shortbattlebow"] = 72; -NTIPAliasClassID["lbb"] = 73; NTIPAliasClassID["longbattlebow"] = 73; -NTIPAliasClassID["swb"] = 74; NTIPAliasClassID["shortwarbow"] = 74; -NTIPAliasClassID["lwb"] = 75; NTIPAliasClassID["longwarbow"] = 75; -NTIPAliasClassID["lxb"] = 76; NTIPAliasClassID["lightcrossbow"] = 76; -NTIPAliasClassID["mxb"] = 77; NTIPAliasClassID["crossbow"] = 77; -NTIPAliasClassID["hxb"] = 78; NTIPAliasClassID["heavycrossbow"] = 78; -NTIPAliasClassID["rxb"] = 79; NTIPAliasClassID["repeatingcrossbow"] = 79; -NTIPAliasClassID["gps"] = 80; NTIPAliasClassID["rancidgaspotion"] = 80; -NTIPAliasClassID["ops"] = 81; NTIPAliasClassID["oilpotion"] = 81; -NTIPAliasClassID["gpm"] = 82; NTIPAliasClassID["chokinggaspotion"] = 82; -NTIPAliasClassID["opm"] = 83; NTIPAliasClassID["explodingpotion"] = 83; -NTIPAliasClassID["gpl"] = 84; NTIPAliasClassID["stranglinggaspotion"] = 84; -NTIPAliasClassID["opl"] = 85; NTIPAliasClassID["fulminatingpotion"] = 85; -NTIPAliasClassID["d33"] = 86; NTIPAliasClassID["decoygidbinn"] = 86; -NTIPAliasClassID["g33"] = 87; NTIPAliasClassID["thegidbinn"] = 87; -NTIPAliasClassID["leg"] = 88; NTIPAliasClassID["wirt'sleg"] = 88; -NTIPAliasClassID["hdm"] = 89; NTIPAliasClassID["horadricmalus"] = 89; -NTIPAliasClassID["hfh"] = 90; NTIPAliasClassID["hellforgehammer"] = 90; -NTIPAliasClassID["hst"] = 91; NTIPAliasClassID["horadricstaff"] = 91; -NTIPAliasClassID["msf"] = 92; NTIPAliasClassID["shaftofthehoradricstaff"] = 92; -NTIPAliasClassID["9ha"] = 93; NTIPAliasClassID["hatchet"] = 93; -NTIPAliasClassID["9ax"] = 94; NTIPAliasClassID["cleaver"] = 94; -NTIPAliasClassID["92a"] = 95; NTIPAliasClassID["twinaxe"] = 95; -NTIPAliasClassID["9mp"] = 96; NTIPAliasClassID["crowbill"] = 96; -NTIPAliasClassID["9wa"] = 97; NTIPAliasClassID["naga"] = 97; -NTIPAliasClassID["9la"] = 98; NTIPAliasClassID["militaryaxe"] = 98; -NTIPAliasClassID["9ba"] = 99; NTIPAliasClassID["beardedaxe"] = 99; -NTIPAliasClassID["9bt"] = 100; NTIPAliasClassID["tabar"] = 100; -NTIPAliasClassID["9ga"] = 101; NTIPAliasClassID["gothicaxe"] = 101; -NTIPAliasClassID["9gi"] = 102; NTIPAliasClassID["ancientaxe"] = 102; -NTIPAliasClassID["9wn"] = 103; NTIPAliasClassID["burntwand"] = 103; -NTIPAliasClassID["9yw"] = 104; NTIPAliasClassID["petrifiedwand"] = 104; -NTIPAliasClassID["9bw"] = 105; NTIPAliasClassID["tombwand"] = 105; -NTIPAliasClassID["9gw"] = 106; NTIPAliasClassID["gravewand"] = 106; -NTIPAliasClassID["9cl"] = 107; NTIPAliasClassID["cudgel"] = 107; -NTIPAliasClassID["9sc"] = 108; NTIPAliasClassID["runescepter"] = 108; -NTIPAliasClassID["9qs"] = 109; NTIPAliasClassID["holywatersprinkler"] = 109; -NTIPAliasClassID["9ws"] = 110; NTIPAliasClassID["divinescepter"] = 110; -NTIPAliasClassID["9sp"] = 111; NTIPAliasClassID["barbedclub"] = 111; -NTIPAliasClassID["9ma"] = 112; NTIPAliasClassID["flangedmace"] = 112; -NTIPAliasClassID["9mt"] = 113; NTIPAliasClassID["jaggedstar"] = 113; -NTIPAliasClassID["9fl"] = 114; NTIPAliasClassID["knout"] = 114; -NTIPAliasClassID["9wh"] = 115; NTIPAliasClassID["battlehammer"] = 115; -NTIPAliasClassID["9m9"] = 116; NTIPAliasClassID["warclub"] = 116; -NTIPAliasClassID["9gm"] = 117; NTIPAliasClassID["marteldefer"] = 117; -NTIPAliasClassID["9ss"] = 118; NTIPAliasClassID["gladius"] = 118; -NTIPAliasClassID["9sm"] = 119; NTIPAliasClassID["cutlass"] = 119; -NTIPAliasClassID["9sb"] = 120; NTIPAliasClassID["shamshir"] = 120; -NTIPAliasClassID["9fc"] = 121; NTIPAliasClassID["tulwar"] = 121; -NTIPAliasClassID["9cr"] = 122; NTIPAliasClassID["dimensionalblade"] = 122; -NTIPAliasClassID["9bs"] = 123; NTIPAliasClassID["battlesword"] = 123; -NTIPAliasClassID["9ls"] = 124; NTIPAliasClassID["runesword"] = 124; -NTIPAliasClassID["9wd"] = 125; NTIPAliasClassID["ancientsword"] = 125; -NTIPAliasClassID["92h"] = 126; NTIPAliasClassID["espandon"] = 126; -NTIPAliasClassID["9cm"] = 127; NTIPAliasClassID["dacianfalx"] = 127; -NTIPAliasClassID["9gs"] = 128; NTIPAliasClassID["tusksword"] = 128; -NTIPAliasClassID["9b9"] = 129; NTIPAliasClassID["gothicsword"] = 129; -NTIPAliasClassID["9fb"] = 130; NTIPAliasClassID["zweihander"] = 130; -NTIPAliasClassID["9gd"] = 131; NTIPAliasClassID["executionersword"] = 131; -NTIPAliasClassID["9dg"] = 132; NTIPAliasClassID["poignard"] = 132; -NTIPAliasClassID["9di"] = 133; NTIPAliasClassID["rondel"] = 133; -NTIPAliasClassID["9kr"] = 134; NTIPAliasClassID["cinquedeas"] = 134; -NTIPAliasClassID["9bl"] = 135; NTIPAliasClassID["stiletto"] = 135; -NTIPAliasClassID["9tk"] = 136; NTIPAliasClassID["battledart"] = 136; -NTIPAliasClassID["9ta"] = 137; NTIPAliasClassID["francisca"] = 137; -NTIPAliasClassID["9bk"] = 138; NTIPAliasClassID["wardart"] = 138; -NTIPAliasClassID["9b8"] = 139; NTIPAliasClassID["hurlbat"] = 139; -NTIPAliasClassID["9ja"] = 140; NTIPAliasClassID["warjavelin"] = 140; -NTIPAliasClassID["9pi"] = 141; NTIPAliasClassID["greatpilum"] = 141; -NTIPAliasClassID["9s9"] = 142; NTIPAliasClassID["simbilan"] = 142; -NTIPAliasClassID["9gl"] = 143; NTIPAliasClassID["spiculum"] = 143; -NTIPAliasClassID["9ts"] = 144; NTIPAliasClassID["harpoon"] = 144; -NTIPAliasClassID["9sr"] = 145; NTIPAliasClassID["warspear"] = 145; -NTIPAliasClassID["9tr"] = 146; NTIPAliasClassID["fuscina"] = 146; -NTIPAliasClassID["9br"] = 147; NTIPAliasClassID["warfork"] = 147; -NTIPAliasClassID["9st"] = 148; NTIPAliasClassID["yari"] = 148; -NTIPAliasClassID["9p9"] = 149; NTIPAliasClassID["lance"] = 149; -NTIPAliasClassID["9b7"] = 150; NTIPAliasClassID["lochaberaxe"] = 150; -NTIPAliasClassID["9vo"] = 151; NTIPAliasClassID["bill"] = 151; -NTIPAliasClassID["9s8"] = 152; NTIPAliasClassID["battlescythe"] = 152; -NTIPAliasClassID["9pa"] = 153; NTIPAliasClassID["partizan"] = 153; -NTIPAliasClassID["9h9"] = 154; NTIPAliasClassID["becdecorbin"] = 154; -NTIPAliasClassID["9wc"] = 155; NTIPAliasClassID["grimscythe"] = 155; -NTIPAliasClassID["8ss"] = 156; NTIPAliasClassID["jostaff"] = 156; -NTIPAliasClassID["8ls"] = 157; NTIPAliasClassID["quarterstaff"] = 157; -NTIPAliasClassID["8cs"] = 158; NTIPAliasClassID["cedarstaff"] = 158; -NTIPAliasClassID["8bs"] = 159; NTIPAliasClassID["gothicstaff"] = 159; -NTIPAliasClassID["8ws"] = 160; NTIPAliasClassID["runestaff"] = 160; -NTIPAliasClassID["8sb"] = 161; NTIPAliasClassID["edgebow"] = 161; -NTIPAliasClassID["8hb"] = 162; NTIPAliasClassID["razorbow"] = 162; -NTIPAliasClassID["8lb"] = 163; NTIPAliasClassID["cedarbow"] = 163; -NTIPAliasClassID["8cb"] = 164; NTIPAliasClassID["doublebow"] = 164; -NTIPAliasClassID["8s8"] = 165; NTIPAliasClassID["shortsiegebow"] = 165; -NTIPAliasClassID["8l8"] = 166; NTIPAliasClassID["largesiegebow"] = 166; -NTIPAliasClassID["8sw"] = 167; NTIPAliasClassID["runebow"] = 167; -NTIPAliasClassID["8lw"] = 168; NTIPAliasClassID["gothicbow"] = 168; -NTIPAliasClassID["8lx"] = 169; NTIPAliasClassID["arbalest"] = 169; -NTIPAliasClassID["8mx"] = 170; NTIPAliasClassID["siegecrossbow"] = 170; -NTIPAliasClassID["8hx"] = 171; NTIPAliasClassID["ballista"] = 171; -NTIPAliasClassID["8rx"] = 172; NTIPAliasClassID["chukonu"] = 172; -NTIPAliasClassID["qf1"] = 173; NTIPAliasClassID["khalim'sflail"] = 173; -NTIPAliasClassID["qf2"] = 174; NTIPAliasClassID["khalim'swill"] = 174; -NTIPAliasClassID["ktr"] = 175; NTIPAliasClassID["katar"] = 175; -NTIPAliasClassID["wrb"] = 176; NTIPAliasClassID["wristblade"] = 176; -NTIPAliasClassID["axf"] = 177; NTIPAliasClassID["hatchethands"] = 177; -NTIPAliasClassID["ces"] = 178; NTIPAliasClassID["cestus"] = 178; -NTIPAliasClassID["clw"] = 179; NTIPAliasClassID["claws"] = 179; -NTIPAliasClassID["btl"] = 180; NTIPAliasClassID["bladetalons"] = 180; -NTIPAliasClassID["skr"] = 181; NTIPAliasClassID["scissorskatar"] = 181; -NTIPAliasClassID["9ar"] = 182; NTIPAliasClassID["quhab"] = 182; -NTIPAliasClassID["9wb"] = 183; NTIPAliasClassID["wristspike"] = 183; -NTIPAliasClassID["9xf"] = 184; NTIPAliasClassID["fascia"] = 184; -NTIPAliasClassID["9cs"] = 185; NTIPAliasClassID["handscythe"] = 185; -NTIPAliasClassID["9lw"] = 186; NTIPAliasClassID["greaterclaws"] = 186; -NTIPAliasClassID["9tw"] = 187; NTIPAliasClassID["greatertalons"] = 187; -NTIPAliasClassID["9qr"] = 188; NTIPAliasClassID["scissorsquhab"] = 188; -NTIPAliasClassID["7ar"] = 189; NTIPAliasClassID["suwayyah"] = 189; -NTIPAliasClassID["7wb"] = 190; NTIPAliasClassID["wristsword"] = 190; -NTIPAliasClassID["7xf"] = 191; NTIPAliasClassID["warfist"] = 191; -NTIPAliasClassID["7cs"] = 192; NTIPAliasClassID["battlecestus"] = 192; -NTIPAliasClassID["7lw"] = 193; NTIPAliasClassID["feralclaws"] = 193; -NTIPAliasClassID["7tw"] = 194; NTIPAliasClassID["runictalons"] = 194; -NTIPAliasClassID["7qr"] = 195; NTIPAliasClassID["scissorssuwayyah"] = 195; -NTIPAliasClassID["7ha"] = 196; NTIPAliasClassID["tomahawk"] = 196; -NTIPAliasClassID["7ax"] = 197; NTIPAliasClassID["smallcrescent"] = 197; -NTIPAliasClassID["72a"] = 198; NTIPAliasClassID["ettinaxe"] = 198; -NTIPAliasClassID["7mp"] = 199; NTIPAliasClassID["warspike"] = 199; -NTIPAliasClassID["7wa"] = 200; NTIPAliasClassID["berserkeraxe"] = 200; -NTIPAliasClassID["7la"] = 201; NTIPAliasClassID["feralaxe"] = 201; -NTIPAliasClassID["7ba"] = 202; NTIPAliasClassID["silveredgedaxe"] = 202; -NTIPAliasClassID["7bt"] = 203; NTIPAliasClassID["decapitator"] = 203; -NTIPAliasClassID["7ga"] = 204; NTIPAliasClassID["championaxe"] = 204; -NTIPAliasClassID["7gi"] = 205; NTIPAliasClassID["gloriousaxe"] = 205; -NTIPAliasClassID["7wn"] = 206; NTIPAliasClassID["polishedwand"] = 206; -NTIPAliasClassID["7yw"] = 207; NTIPAliasClassID["ghostwand"] = 207; -NTIPAliasClassID["7bw"] = 208; NTIPAliasClassID["lichwand"] = 208; -NTIPAliasClassID["7gw"] = 209; NTIPAliasClassID["unearthedwand"] = 209; -NTIPAliasClassID["7cl"] = 210; NTIPAliasClassID["truncheon"] = 210; -NTIPAliasClassID["7sc"] = 211; NTIPAliasClassID["mightyscepter"] = 211; -NTIPAliasClassID["7qs"] = 212; NTIPAliasClassID["seraphrod"] = 212; -NTIPAliasClassID["7ws"] = 213; NTIPAliasClassID["caduceus"] = 213; -NTIPAliasClassID["7sp"] = 214; NTIPAliasClassID["tyrantclub"] = 214; -NTIPAliasClassID["7ma"] = 215; NTIPAliasClassID["reinforcedmace"] = 215; -NTIPAliasClassID["7mt"] = 216; NTIPAliasClassID["devilstar"] = 216; -NTIPAliasClassID["7fl"] = 217; NTIPAliasClassID["scourge"] = 217; -NTIPAliasClassID["7wh"] = 218; NTIPAliasClassID["legendarymallet"] = 218; -NTIPAliasClassID["7m7"] = 219; NTIPAliasClassID["ogremaul"] = 219; -NTIPAliasClassID["7gm"] = 220; NTIPAliasClassID["thundermaul"] = 220; -NTIPAliasClassID["7ss"] = 221; NTIPAliasClassID["falcata"] = 221; -NTIPAliasClassID["7sm"] = 222; NTIPAliasClassID["ataghan"] = 222; -NTIPAliasClassID["7sb"] = 223; NTIPAliasClassID["elegantblade"] = 223; -NTIPAliasClassID["7fc"] = 224; NTIPAliasClassID["hydraedge"] = 224; -NTIPAliasClassID["7cr"] = 225; NTIPAliasClassID["phaseblade"] = 225; -NTIPAliasClassID["7bs"] = 226; NTIPAliasClassID["conquestsword"] = 226; -NTIPAliasClassID["7ls"] = 227; NTIPAliasClassID["crypticsword"] = 227; -NTIPAliasClassID["7wd"] = 228; NTIPAliasClassID["mythicalsword"] = 228; -NTIPAliasClassID["72h"] = 229; NTIPAliasClassID["legendsword"] = 229; -NTIPAliasClassID["7cm"] = 230; NTIPAliasClassID["highlandblade"] = 230; -NTIPAliasClassID["7gs"] = 231; NTIPAliasClassID["balrogblade"] = 231; -NTIPAliasClassID["7b7"] = 232; NTIPAliasClassID["championsword"] = 232; -NTIPAliasClassID["7fb"] = 233; NTIPAliasClassID["colossussword"] = 233; -NTIPAliasClassID["7gd"] = 234; NTIPAliasClassID["colossusblade"] = 234; -NTIPAliasClassID["7dg"] = 235; NTIPAliasClassID["boneknife"] = 235; -NTIPAliasClassID["7di"] = 236; NTIPAliasClassID["mithrilpoint"] = 236; -NTIPAliasClassID["7kr"] = 237; NTIPAliasClassID["fangedknife"] = 237; -NTIPAliasClassID["7bl"] = 238; NTIPAliasClassID["legendspike"] = 238; -NTIPAliasClassID["7tk"] = 239; NTIPAliasClassID["flyingknife"] = 239; -NTIPAliasClassID["7ta"] = 240; NTIPAliasClassID["flyingaxe"] = 240; -NTIPAliasClassID["7bk"] = 241; NTIPAliasClassID["wingedknife"] = 241; -NTIPAliasClassID["7b8"] = 242; NTIPAliasClassID["wingedaxe"] = 242; -NTIPAliasClassID["7ja"] = 243; NTIPAliasClassID["hyperionjavelin"] = 243; -NTIPAliasClassID["7pi"] = 244; NTIPAliasClassID["stygianpilum"] = 244; -NTIPAliasClassID["7s7"] = 245; NTIPAliasClassID["balrogspear"] = 245; -NTIPAliasClassID["7gl"] = 246; NTIPAliasClassID["ghostglaive"] = 246; -NTIPAliasClassID["7ts"] = 247; NTIPAliasClassID["wingedharpoon"] = 247; -NTIPAliasClassID["7sr"] = 248; NTIPAliasClassID["hyperionspear"] = 248; -NTIPAliasClassID["7tr"] = 249; NTIPAliasClassID["stygianpike"] = 249; -NTIPAliasClassID["7br"] = 250; NTIPAliasClassID["mancatcher"] = 250; -NTIPAliasClassID["7st"] = 251; NTIPAliasClassID["ghostspear"] = 251; -NTIPAliasClassID["7p7"] = 252; NTIPAliasClassID["warpike"] = 252; -NTIPAliasClassID["7o7"] = 253; NTIPAliasClassID["ogreaxe"] = 253; -NTIPAliasClassID["7vo"] = 254; NTIPAliasClassID["colossusvoulge"] = 254; -NTIPAliasClassID["7s8"] = 255; NTIPAliasClassID["thresher"] = 255; -NTIPAliasClassID["7pa"] = 256; NTIPAliasClassID["crypticaxe"] = 256; -NTIPAliasClassID["7h7"] = 257; NTIPAliasClassID["greatpoleaxe"] = 257; -NTIPAliasClassID["7wc"] = 258; NTIPAliasClassID["giantthresher"] = 258; -NTIPAliasClassID["6ss"] = 259; NTIPAliasClassID["walkingstick"] = 259; -NTIPAliasClassID["6ls"] = 260; NTIPAliasClassID["stalagmite"] = 260; -NTIPAliasClassID["6cs"] = 261; NTIPAliasClassID["elderstaff"] = 261; -NTIPAliasClassID["6bs"] = 262; NTIPAliasClassID["shillelagh"] = 262; -NTIPAliasClassID["6ws"] = 263; NTIPAliasClassID["archonstaff"] = 263; -NTIPAliasClassID["6sb"] = 264; NTIPAliasClassID["spiderbow"] = 264; -NTIPAliasClassID["6hb"] = 265; NTIPAliasClassID["bladebow"] = 265; -NTIPAliasClassID["6lb"] = 266; NTIPAliasClassID["shadowbow"] = 266; -NTIPAliasClassID["6cb"] = 267; NTIPAliasClassID["greatbow"] = 267; -NTIPAliasClassID["6s7"] = 268; NTIPAliasClassID["diamondbow"] = 268; -NTIPAliasClassID["6l7"] = 269; NTIPAliasClassID["crusaderbow"] = 269; -NTIPAliasClassID["6sw"] = 270; NTIPAliasClassID["wardbow"] = 270; -NTIPAliasClassID["6lw"] = 271; NTIPAliasClassID["hydrabow"] = 271; -NTIPAliasClassID["6lx"] = 272; NTIPAliasClassID["pelletbow"] = 272; -NTIPAliasClassID["6mx"] = 273; NTIPAliasClassID["gorgoncrossbow"] = 273; -NTIPAliasClassID["6hx"] = 274; NTIPAliasClassID["colossuscrossbow"] = 274; -NTIPAliasClassID["6rx"] = 275; NTIPAliasClassID["demoncrossbow"] = 275; -NTIPAliasClassID["ob1"] = 276; NTIPAliasClassID["eagleorb"] = 276; -NTIPAliasClassID["ob2"] = 277; NTIPAliasClassID["sacredglobe"] = 277; -NTIPAliasClassID["ob3"] = 278; NTIPAliasClassID["smokedsphere"] = 278; -NTIPAliasClassID["ob4"] = 279; NTIPAliasClassID["claspedorb"] = 279; -NTIPAliasClassID["ob5"] = 280; NTIPAliasClassID["jared'sstone"] = 280; -NTIPAliasClassID["am1"] = 281; NTIPAliasClassID["stagbow"] = 281; -NTIPAliasClassID["am2"] = 282; NTIPAliasClassID["reflexbow"] = 282; -NTIPAliasClassID["am3"] = 283; NTIPAliasClassID["maidenspear"] = 283; -NTIPAliasClassID["am4"] = 284; NTIPAliasClassID["maidenpike"] = 284; -NTIPAliasClassID["am5"] = 285; NTIPAliasClassID["maidenjavelin"] = 285; -NTIPAliasClassID["ob6"] = 286; NTIPAliasClassID["glowingorb"] = 286; -NTIPAliasClassID["ob7"] = 287; NTIPAliasClassID["crystallineglobe"] = 287; -NTIPAliasClassID["ob8"] = 288; NTIPAliasClassID["cloudysphere"] = 288; -NTIPAliasClassID["ob9"] = 289; NTIPAliasClassID["sparklingball"] = 289; -NTIPAliasClassID["oba"] = 290; NTIPAliasClassID["swirlingcrystal"] = 290; -NTIPAliasClassID["am6"] = 291; NTIPAliasClassID["ashwoodbow"] = 291; -NTIPAliasClassID["am7"] = 292; NTIPAliasClassID["ceremonialbow"] = 292; -NTIPAliasClassID["am8"] = 293; NTIPAliasClassID["ceremonialspear"] = 293; -NTIPAliasClassID["am9"] = 294; NTIPAliasClassID["ceremonialpike"] = 294; -NTIPAliasClassID["ama"] = 295; NTIPAliasClassID["ceremonialjavelin"] = 295; -NTIPAliasClassID["obb"] = 296; NTIPAliasClassID["heavenlystone"] = 296; -NTIPAliasClassID["obc"] = 297; NTIPAliasClassID["eldritchorb"] = 297; -NTIPAliasClassID["obd"] = 298; NTIPAliasClassID["demonheart"] = 298; -NTIPAliasClassID["obe"] = 299; NTIPAliasClassID["vortexorb"] = 299; -NTIPAliasClassID["obf"] = 300; NTIPAliasClassID["dimensionalshard"] = 300; -NTIPAliasClassID["amb"] = 301; NTIPAliasClassID["matriarchalbow"] = 301; -NTIPAliasClassID["amc"] = 302; NTIPAliasClassID["grandmatronbow"] = 302; -NTIPAliasClassID["amd"] = 303; NTIPAliasClassID["matriarchalspear"] = 303; -NTIPAliasClassID["ame"] = 304; NTIPAliasClassID["matriarchalpike"] = 304; -NTIPAliasClassID["amf"] = 305; NTIPAliasClassID["matriarchaljavelin"] = 305; -NTIPAliasClassID["cap"] = 306; -NTIPAliasClassID["skp"] = 307; NTIPAliasClassID["skullcap"] = 307; -NTIPAliasClassID["hlm"] = 308; NTIPAliasClassID["helm"] = 308; -NTIPAliasClassID["fhl"] = 309; NTIPAliasClassID["fullhelm"] = 309; -NTIPAliasClassID["ghm"] = 310; NTIPAliasClassID["greathelm"] = 310; -NTIPAliasClassID["crn"] = 311; NTIPAliasClassID["crown"] = 311; -NTIPAliasClassID["msk"] = 312; NTIPAliasClassID["mask"] = 312; -NTIPAliasClassID["qui"] = 313; NTIPAliasClassID["quiltedarmor"] = 313; -NTIPAliasClassID["lea"] = 314; NTIPAliasClassID["leatherarmor"] = 314; -NTIPAliasClassID["hla"] = 315; NTIPAliasClassID["hardleatherarmor"] = 315; -NTIPAliasClassID["stu"] = 316; NTIPAliasClassID["studdedleather"] = 316; -NTIPAliasClassID["rng"] = 317; NTIPAliasClassID["ringmail"] = 317; -NTIPAliasClassID["scl"] = 318; NTIPAliasClassID["scalemail"] = 318; -NTIPAliasClassID["chn"] = 319; NTIPAliasClassID["chainmail"] = 319; -NTIPAliasClassID["brs"] = 320; NTIPAliasClassID["breastplate"] = 320; -NTIPAliasClassID["spl"] = 321; NTIPAliasClassID["splintmail"] = 321; -NTIPAliasClassID["plt"] = 322; NTIPAliasClassID["platemail"] = 322; -NTIPAliasClassID["fld"] = 323; NTIPAliasClassID["fieldplate"] = 323; -NTIPAliasClassID["gth"] = 324; NTIPAliasClassID["gothicplate"] = 324; -NTIPAliasClassID["ful"] = 325; NTIPAliasClassID["fullplatemail"] = 325; -NTIPAliasClassID["aar"] = 326; NTIPAliasClassID["ancientarmor"] = 326; -NTIPAliasClassID["ltp"] = 327; NTIPAliasClassID["lightplate"] = 327; -NTIPAliasClassID["buc"] = 328; NTIPAliasClassID["buckler"] = 328; -NTIPAliasClassID["sml"] = 329; NTIPAliasClassID["smallshield"] = 329; -NTIPAliasClassID["lrg"] = 330; NTIPAliasClassID["largeshield"] = 330; -NTIPAliasClassID["kit"] = 331; NTIPAliasClassID["kiteshield"] = 331; -NTIPAliasClassID["tow"] = 332; NTIPAliasClassID["towershield"] = 332; -NTIPAliasClassID["gts"] = 333; NTIPAliasClassID["gothicshield"] = 333; -NTIPAliasClassID["lgl"] = 334; NTIPAliasClassID["leathergloves"] = 334; -NTIPAliasClassID["vgl"] = 335; NTIPAliasClassID["heavygloves"] = 335; -NTIPAliasClassID["mgl"] = 336; NTIPAliasClassID["chaingloves"] = 336; -NTIPAliasClassID["tgl"] = 337; NTIPAliasClassID["lightgauntlets"] = 337; -NTIPAliasClassID["hgl"] = 338; NTIPAliasClassID["gauntlets"] = 338; -NTIPAliasClassID["lbt"] = 339; NTIPAliasClassID["boots"] = 339; -NTIPAliasClassID["vbt"] = 340; NTIPAliasClassID["heavyboots"] = 340; -NTIPAliasClassID["mbt"] = 341; NTIPAliasClassID["chainboots"] = 341; -NTIPAliasClassID["tbt"] = 342; NTIPAliasClassID["lightplatedboots"] = 342; -NTIPAliasClassID["hbt"] = 343; NTIPAliasClassID["greaves"] = 343; -NTIPAliasClassID["lbl"] = 344; NTIPAliasClassID["sash"] = 344; -NTIPAliasClassID["vbl"] = 345; NTIPAliasClassID["lightbelt"] = 345; -NTIPAliasClassID["mbl"] = 346; NTIPAliasClassID["belt"] = 346; -NTIPAliasClassID["tbl"] = 347; NTIPAliasClassID["heavybelt"] = 347; -NTIPAliasClassID["hbl"] = 348; NTIPAliasClassID["platedbelt"] = 348; -NTIPAliasClassID["bhm"] = 349; NTIPAliasClassID["bonehelm"] = 349; -NTIPAliasClassID["bsh"] = 350; NTIPAliasClassID["boneshield"] = 350; -NTIPAliasClassID["spk"] = 351; NTIPAliasClassID["spikedshield"] = 351; -NTIPAliasClassID["xap"] = 352; NTIPAliasClassID["warhat"] = 352; -NTIPAliasClassID["xkp"] = 353; NTIPAliasClassID["sallet"] = 353; -NTIPAliasClassID["xlm"] = 354; NTIPAliasClassID["casque"] = 354; -NTIPAliasClassID["xhl"] = 355; NTIPAliasClassID["basinet"] = 355; -NTIPAliasClassID["xhm"] = 356; NTIPAliasClassID["wingedhelm"] = 356; -NTIPAliasClassID["xrn"] = 357; NTIPAliasClassID["grandcrown"] = 357; -NTIPAliasClassID["xsk"] = 358; NTIPAliasClassID["deathmask"] = 358; -NTIPAliasClassID["xui"] = 359; NTIPAliasClassID["ghostarmor"] = 359; -NTIPAliasClassID["xea"] = 360; NTIPAliasClassID["serpentskinarmor"] = 360; -NTIPAliasClassID["xla"] = 361; NTIPAliasClassID["demonhidearmor"] = 361; -NTIPAliasClassID["xtu"] = 362; NTIPAliasClassID["trellisedarmor"] = 362; -NTIPAliasClassID["xng"] = 363; NTIPAliasClassID["linkedmail"] = 363; -NTIPAliasClassID["xcl"] = 364; NTIPAliasClassID["tigulatedmail"] = 364; -NTIPAliasClassID["xhn"] = 365; NTIPAliasClassID["mesharmor"] = 365; -NTIPAliasClassID["xrs"] = 366; NTIPAliasClassID["cuirass"] = 366; -NTIPAliasClassID["xpl"] = 367; NTIPAliasClassID["russetarmor"] = 367; -NTIPAliasClassID["xlt"] = 368; NTIPAliasClassID["templarcoat"] = 368; -NTIPAliasClassID["xld"] = 369; NTIPAliasClassID["sharktootharmor"] = 369; -NTIPAliasClassID["xth"] = 370; NTIPAliasClassID["embossedplate"] = 370; -NTIPAliasClassID["xul"] = 371; NTIPAliasClassID["chaosarmor"] = 371; -NTIPAliasClassID["xar"] = 372; NTIPAliasClassID["ornateplate"] = 372; -NTIPAliasClassID["xtp"] = 373; NTIPAliasClassID["mageplate"] = 373; -NTIPAliasClassID["xuc"] = 374; NTIPAliasClassID["defender"] = 374; -NTIPAliasClassID["xml"] = 375; NTIPAliasClassID["roundshield"] = 375; -NTIPAliasClassID["xrg"] = 376; NTIPAliasClassID["scutum"] = 376; -NTIPAliasClassID["xit"] = 377; NTIPAliasClassID["dragonshield"] = 377; -NTIPAliasClassID["xow"] = 378; NTIPAliasClassID["pavise"] = 378; -NTIPAliasClassID["xts"] = 379; NTIPAliasClassID["ancientshield"] = 379; -NTIPAliasClassID["xlg"] = 380; NTIPAliasClassID["demonhidegloves"] = 380; -NTIPAliasClassID["xvg"] = 381; NTIPAliasClassID["sharkskingloves"] = 381; -NTIPAliasClassID["xmg"] = 382; NTIPAliasClassID["heavybracers"] = 382; -NTIPAliasClassID["xtg"] = 383; NTIPAliasClassID["battlegauntlets"] = 383; -NTIPAliasClassID["xhg"] = 384; NTIPAliasClassID["wargauntlets"] = 384; -NTIPAliasClassID["xlb"] = 385; NTIPAliasClassID["demonhideboots"] = 385; -NTIPAliasClassID["xvb"] = 386; NTIPAliasClassID["sharkskinboots"] = 386; -NTIPAliasClassID["xmb"] = 387; NTIPAliasClassID["meshboots"] = 387; -NTIPAliasClassID["xtb"] = 388; NTIPAliasClassID["battleboots"] = 388; -NTIPAliasClassID["xhb"] = 389; NTIPAliasClassID["warboots"] = 389; -NTIPAliasClassID["zlb"] = 390; NTIPAliasClassID["demonhidesash"] = 390; -NTIPAliasClassID["zvb"] = 391; NTIPAliasClassID["sharkskinbelt"] = 391; -NTIPAliasClassID["zmb"] = 392; NTIPAliasClassID["meshbelt"] = 392; -NTIPAliasClassID["ztb"] = 393; NTIPAliasClassID["battlebelt"] = 393; -NTIPAliasClassID["zhb"] = 394; NTIPAliasClassID["warbelt"] = 394; -NTIPAliasClassID["xh9"] = 395; NTIPAliasClassID["grimhelm"] = 395; -NTIPAliasClassID["xsh"] = 396; NTIPAliasClassID["grimshield"] = 396; -NTIPAliasClassID["xpk"] = 397; NTIPAliasClassID["barbedshield"] = 397; -NTIPAliasClassID["dr1"] = 398; NTIPAliasClassID["wolfhead"] = 398; -NTIPAliasClassID["dr2"] = 399; NTIPAliasClassID["hawkhelm"] = 399; -NTIPAliasClassID["dr3"] = 400; NTIPAliasClassID["antlers"] = 400; -NTIPAliasClassID["dr4"] = 401; NTIPAliasClassID["falconmask"] = 401; -NTIPAliasClassID["dr5"] = 402; NTIPAliasClassID["spiritmask"] = 402; -NTIPAliasClassID["ba1"] = 403; NTIPAliasClassID["jawbonecap"] = 403; -NTIPAliasClassID["ba2"] = 404; NTIPAliasClassID["fangedhelm"] = 404; -NTIPAliasClassID["ba3"] = 405; NTIPAliasClassID["hornedhelm"] = 405; -NTIPAliasClassID["ba4"] = 406; NTIPAliasClassID["assaulthelmet"] = 406; -NTIPAliasClassID["ba5"] = 407; NTIPAliasClassID["avengerguard"] = 407; -NTIPAliasClassID["pa1"] = 408; NTIPAliasClassID["targe"] = 408; -NTIPAliasClassID["pa2"] = 409; NTIPAliasClassID["rondache"] = 409; -NTIPAliasClassID["pa3"] = 410; NTIPAliasClassID["heraldicshield"] = 410; -NTIPAliasClassID["pa4"] = 411; NTIPAliasClassID["aerinshield"] = 411; -NTIPAliasClassID["pa5"] = 412; NTIPAliasClassID["crownshield"] = 412; -NTIPAliasClassID["ne1"] = 413; NTIPAliasClassID["preservedhead"] = 413; -NTIPAliasClassID["ne2"] = 414; NTIPAliasClassID["zombiehead"] = 414; -NTIPAliasClassID["ne3"] = 415; NTIPAliasClassID["unravellerhead"] = 415; -NTIPAliasClassID["ne4"] = 416; NTIPAliasClassID["gargoylehead"] = 416; -NTIPAliasClassID["ne5"] = 417; NTIPAliasClassID["demonhead"] = 417; -NTIPAliasClassID["ci0"] = 418; NTIPAliasClassID["circlet"] = 418; -NTIPAliasClassID["ci1"] = 419; NTIPAliasClassID["coronet"] = 419; -NTIPAliasClassID["ci2"] = 420; NTIPAliasClassID["tiara"] = 420; -NTIPAliasClassID["ci3"] = 421; NTIPAliasClassID["diadem"] = 421; -NTIPAliasClassID["uap"] = 422; NTIPAliasClassID["shako"] = 422; -NTIPAliasClassID["ukp"] = 423; NTIPAliasClassID["hydraskull"] = 423; -NTIPAliasClassID["ulm"] = 424; NTIPAliasClassID["armet"] = 424; -NTIPAliasClassID["uhl"] = 425; NTIPAliasClassID["giantconch"] = 425; -NTIPAliasClassID["uhm"] = 426; NTIPAliasClassID["spiredhelm"] = 426; -NTIPAliasClassID["urn"] = 427; NTIPAliasClassID["corona"] = 427; -NTIPAliasClassID["usk"] = 428; NTIPAliasClassID["demonhead"] = 428; -NTIPAliasClassID["uui"] = 429; NTIPAliasClassID["duskshroud"] = 429; -NTIPAliasClassID["uea"] = 430; NTIPAliasClassID["wyrmhide"] = 430; -NTIPAliasClassID["ula"] = 431; NTIPAliasClassID["scarabhusk"] = 431; -NTIPAliasClassID["utu"] = 432; NTIPAliasClassID["wirefleece"] = 432; -NTIPAliasClassID["ung"] = 433; NTIPAliasClassID["diamondmail"] = 433; -NTIPAliasClassID["ucl"] = 434; NTIPAliasClassID["loricatedmail"] = 434; -NTIPAliasClassID["uhn"] = 435; NTIPAliasClassID["boneweave"] = 435; -NTIPAliasClassID["urs"] = 436; NTIPAliasClassID["greathauberk"] = 436; -NTIPAliasClassID["upl"] = 437; NTIPAliasClassID["balrogskin"] = 437; -NTIPAliasClassID["ult"] = 438; NTIPAliasClassID["hellforgeplate"] = 438; -NTIPAliasClassID["uld"] = 439; NTIPAliasClassID["krakenshell"] = 439; -NTIPAliasClassID["uth"] = 440; NTIPAliasClassID["lacqueredplate"] = 440; -NTIPAliasClassID["uul"] = 441; NTIPAliasClassID["shadowplate"] = 441; -NTIPAliasClassID["uar"] = 442; NTIPAliasClassID["sacredarmor"] = 442; -NTIPAliasClassID["utp"] = 443; NTIPAliasClassID["archonplate"] = 443; -NTIPAliasClassID["uuc"] = 444; NTIPAliasClassID["heater"] = 444; -NTIPAliasClassID["uml"] = 445; NTIPAliasClassID["luna"] = 445; -NTIPAliasClassID["urg"] = 446; NTIPAliasClassID["hyperion"] = 446; -NTIPAliasClassID["uit"] = 447; NTIPAliasClassID["monarch"] = 447; -NTIPAliasClassID["uow"] = 448; NTIPAliasClassID["aegis"] = 448; -NTIPAliasClassID["uts"] = 449; NTIPAliasClassID["ward"] = 449; -NTIPAliasClassID["ulg"] = 450; NTIPAliasClassID["bramblemitts"] = 450; -NTIPAliasClassID["uvg"] = 451; NTIPAliasClassID["vampirebonegloves"] = 451; -NTIPAliasClassID["umg"] = 452; NTIPAliasClassID["vambraces"] = 452; -NTIPAliasClassID["utg"] = 453; NTIPAliasClassID["crusadergauntlets"] = 453; -NTIPAliasClassID["uhg"] = 454; NTIPAliasClassID["ogregauntlets"] = 454; -NTIPAliasClassID["ulb"] = 455; NTIPAliasClassID["wyrmhideboots"] = 455; -NTIPAliasClassID["uvb"] = 456; NTIPAliasClassID["scarabshellboots"] = 456; -NTIPAliasClassID["umb"] = 457; NTIPAliasClassID["boneweaveboots"] = 457; -NTIPAliasClassID["utb"] = 458; NTIPAliasClassID["mirroredboots"] = 458; -NTIPAliasClassID["uhb"] = 459; NTIPAliasClassID["myrmidongreaves"] = 459; -NTIPAliasClassID["ulc"] = 460; NTIPAliasClassID["spiderwebsash"] = 460; -NTIPAliasClassID["uvc"] = 461; NTIPAliasClassID["vampirefangbelt"] = 461; -NTIPAliasClassID["umc"] = 462; NTIPAliasClassID["mithrilcoil"] = 462; -NTIPAliasClassID["utc"] = 463; NTIPAliasClassID["trollbelt"] = 463; -NTIPAliasClassID["uhc"] = 464; NTIPAliasClassID["colossusgirdle"] = 464; -NTIPAliasClassID["uh9"] = 465; NTIPAliasClassID["bonevisage"] = 465; -NTIPAliasClassID["ush"] = 466; NTIPAliasClassID["trollnest"] = 466; -NTIPAliasClassID["upk"] = 467; NTIPAliasClassID["bladebarrier"] = 467; -NTIPAliasClassID["dr6"] = 468; NTIPAliasClassID["alphahelm"] = 468; -NTIPAliasClassID["dr7"] = 469; NTIPAliasClassID["griffonheaddress"] = 469; -NTIPAliasClassID["dr8"] = 470; NTIPAliasClassID["hunter'sguise"] = 470; -NTIPAliasClassID["dr9"] = 471; NTIPAliasClassID["sacredfeathers"] = 471; -NTIPAliasClassID["dra"] = 472; NTIPAliasClassID["totemicmask"] = 472; -NTIPAliasClassID["ba6"] = 473; NTIPAliasClassID["jawbonevisor"] = 473; -NTIPAliasClassID["ba7"] = 474; NTIPAliasClassID["lionhelm"] = 474; -NTIPAliasClassID["ba8"] = 475; NTIPAliasClassID["ragemask"] = 475; -NTIPAliasClassID["ba9"] = 476; NTIPAliasClassID["savagehelmet"] = 476; -NTIPAliasClassID["baa"] = 477; NTIPAliasClassID["slayerguard"] = 477; -NTIPAliasClassID["pa6"] = 478; NTIPAliasClassID["akarantarge"] = 478; -NTIPAliasClassID["pa7"] = 479; NTIPAliasClassID["akaranrondache"] = 479; -NTIPAliasClassID["pa8"] = 480; NTIPAliasClassID["protectorshield"] = 480; -NTIPAliasClassID["pa9"] = 481; NTIPAliasClassID["gildedshield"] = 481; -NTIPAliasClassID["paa"] = 482; NTIPAliasClassID["royalshield"] = 482; -NTIPAliasClassID["ne6"] = 483; NTIPAliasClassID["mummifiedtrophy"] = 483; -NTIPAliasClassID["ne7"] = 484; NTIPAliasClassID["fetishtrophy"] = 484; -NTIPAliasClassID["ne8"] = 485; NTIPAliasClassID["sextontrophy"] = 485; -NTIPAliasClassID["ne9"] = 486; NTIPAliasClassID["cantortrophy"] = 486; -NTIPAliasClassID["nea"] = 487; NTIPAliasClassID["hierophanttrophy"] = 487; -NTIPAliasClassID["drb"] = 488; NTIPAliasClassID["bloodspirit"] = 488; -NTIPAliasClassID["drc"] = 489; NTIPAliasClassID["sunspirit"] = 489; -NTIPAliasClassID["drd"] = 490; NTIPAliasClassID["earthspirit"] = 490; -NTIPAliasClassID["dre"] = 491; NTIPAliasClassID["skyspirit"] = 491; -NTIPAliasClassID["drf"] = 492; NTIPAliasClassID["dreamspirit"] = 492; -NTIPAliasClassID["bab"] = 493; NTIPAliasClassID["carnagehelm"] = 493; -NTIPAliasClassID["bac"] = 494; NTIPAliasClassID["furyvisor"] = 494; -NTIPAliasClassID["bad"] = 495; NTIPAliasClassID["destroyerhelm"] = 495; -NTIPAliasClassID["bae"] = 496; NTIPAliasClassID["conquerorcrown"] = 496; -NTIPAliasClassID["baf"] = 497; NTIPAliasClassID["guardiancrown"] = 497; -NTIPAliasClassID["pab"] = 498; NTIPAliasClassID["sacredtarge"] = 498; -NTIPAliasClassID["pac"] = 499; NTIPAliasClassID["sacredrondache"] = 499; -NTIPAliasClassID["pad"] = 500; NTIPAliasClassID["kurastshield"] = 500; -NTIPAliasClassID["pae"] = 501; NTIPAliasClassID["zakarumshield"] = 501; -NTIPAliasClassID["paf"] = 502; NTIPAliasClassID["vortexshield"] = 502; -NTIPAliasClassID["neb"] = 503; NTIPAliasClassID["minionskull"] = 503; -NTIPAliasClassID["neg"] = 504; NTIPAliasClassID["hellspawnskull"] = 504; -NTIPAliasClassID["ned"] = 505; NTIPAliasClassID["overseerskull"] = 505; -NTIPAliasClassID["nee"] = 506; NTIPAliasClassID["succubusskull"] = 506; -NTIPAliasClassID["nef"] = 507; NTIPAliasClassID["bloodlordskull"] = 507; -NTIPAliasClassID["elx"] = 508; NTIPAliasClassID["elixir"] = 508; -NTIPAliasClassID["hpo"] = 509; -NTIPAliasClassID["mpo"] = 510; -NTIPAliasClassID["hpf"] = 511; -NTIPAliasClassID["mpf"] = 512; -NTIPAliasClassID["vps"] = 513; NTIPAliasClassID["staminapotion"] = 513; -NTIPAliasClassID["yps"] = 514; NTIPAliasClassID["antidotepotion"] = 514; -NTIPAliasClassID["rvs"] = 515; NTIPAliasClassID["rejuvenationpotion"] = 515; -NTIPAliasClassID["rvl"] = 516; NTIPAliasClassID["fullrejuvenationpotion"] = 516; -NTIPAliasClassID["wms"] = 517; NTIPAliasClassID["thawingpotion"] = 517; -NTIPAliasClassID["tbk"] = 518; NTIPAliasClassID["tomeoftownportal"] = 518; -NTIPAliasClassID["ibk"] = 519; NTIPAliasClassID["tomeofidentify"] = 519; -NTIPAliasClassID["amu"] = 520; NTIPAliasClassID["amulet"] = 520; -NTIPAliasClassID["vip"] = 521; NTIPAliasClassID["topofthehoradricstaff"] = 521; -NTIPAliasClassID["rin"] = 522; NTIPAliasClassID["ring"] = 522; -NTIPAliasClassID["gld"] = 523; NTIPAliasClassID["gold"] = 523; -NTIPAliasClassID["bks"] = 524; NTIPAliasClassID["scrollofinifuss"] = 524; -NTIPAliasClassID["bkd"] = 525; NTIPAliasClassID["keytothecairnstones"] = 525; -NTIPAliasClassID["aqv"] = 526; NTIPAliasClassID["arrows"] = 526; -NTIPAliasClassID["tch"] = 527; NTIPAliasClassID["torch"] = 527; -NTIPAliasClassID["cqv"] = 528; NTIPAliasClassID["bolts"] = 528; -NTIPAliasClassID["tsc"] = 529; NTIPAliasClassID["scrolloftownportal"] = 529; -NTIPAliasClassID["isc"] = 530; NTIPAliasClassID["scrollofidentify"] = 530; -NTIPAliasClassID["hrt"] = 531; NTIPAliasClassID["heart"] = 531; -NTIPAliasClassID["brz"] = 532; NTIPAliasClassID["brain"] = 532; -NTIPAliasClassID["jaw"] = 533; NTIPAliasClassID["jawbone"] = 533; -NTIPAliasClassID["eyz"] = 534; NTIPAliasClassID["eye"] = 534; -NTIPAliasClassID["hrn"] = 535; NTIPAliasClassID["horn"] = 535; -NTIPAliasClassID["tal"] = 536; NTIPAliasClassID["tail"] = 536; -NTIPAliasClassID["flg"] = 537; NTIPAliasClassID["flag"] = 537; -NTIPAliasClassID["fng"] = 538; NTIPAliasClassID["fang"] = 538; -NTIPAliasClassID["qll"] = 539; NTIPAliasClassID["quill"] = 539; -NTIPAliasClassID["sol"] = 540; NTIPAliasClassID["soul"] = 540; -NTIPAliasClassID["scz"] = 541; NTIPAliasClassID["scalp"] = 541; -NTIPAliasClassID["spe"] = 542; NTIPAliasClassID["spleen"] = 542; -NTIPAliasClassID["key"] = 543; -NTIPAliasClassID["luv"] = 544; NTIPAliasClassID["theblacktowerkey"] = 544; -NTIPAliasClassID["xyz"] = 545; NTIPAliasClassID["potionoflife"] = 545; -NTIPAliasClassID["j34"] = 546; NTIPAliasClassID["ajadefigurine"] = 546; -NTIPAliasClassID["g34"] = 547; NTIPAliasClassID["thegoldenbird"] = 547; -NTIPAliasClassID["bbb"] = 548; NTIPAliasClassID["lamesen'stome"] = 548; -NTIPAliasClassID["box"] = 549; NTIPAliasClassID["horadriccube"] = 549; -NTIPAliasClassID["tr1"] = 550; NTIPAliasClassID["horadricscroll"] = 550; -NTIPAliasClassID["mss"] = 551; NTIPAliasClassID["mephisto'ssoulstone"] = 551; -NTIPAliasClassID["ass"] = 552; NTIPAliasClassID["bookofskill"] = 552; -NTIPAliasClassID["qey"] = 553; NTIPAliasClassID["khalim'seye"] = 553; -NTIPAliasClassID["qhr"] = 554; NTIPAliasClassID["khalim'sheart"] = 554; -NTIPAliasClassID["qbr"] = 555; NTIPAliasClassID["khalim'sbrain"] = 555; -NTIPAliasClassID["ear"] = 556; -NTIPAliasClassID["gcv"] = 557; NTIPAliasClassID["chippedamethyst"] = 557; -NTIPAliasClassID["gfv"] = 558; NTIPAliasClassID["flawedamethyst"] = 558; -NTIPAliasClassID["gsv"] = 559; NTIPAliasClassID["amethyst"] = 559; -NTIPAliasClassID["gzv"] = 560; NTIPAliasClassID["flawlessamethyst"] = 560; -NTIPAliasClassID["gpv"] = 561; NTIPAliasClassID["perfectamethyst"] = 561; -NTIPAliasClassID["gcy"] = 562; NTIPAliasClassID["chippedtopaz"] = 562; -NTIPAliasClassID["gfy"] = 563; NTIPAliasClassID["flawedtopaz"] = 563; -NTIPAliasClassID["gsy"] = 564; NTIPAliasClassID["topaz"] = 564; -NTIPAliasClassID["gly"] = 565; NTIPAliasClassID["flawlesstopaz"] = 565; -NTIPAliasClassID["gpy"] = 566; NTIPAliasClassID["perfecttopaz"] = 566; -NTIPAliasClassID["gcb"] = 567; NTIPAliasClassID["chippedsapphire"] = 567; -NTIPAliasClassID["gfb"] = 568; NTIPAliasClassID["flawedsapphire"] = 568; -NTIPAliasClassID["gsb"] = 569; NTIPAliasClassID["sapphire"] = 569; -NTIPAliasClassID["glb"] = 570; NTIPAliasClassID["flawlesssapphire"] = 570; -NTIPAliasClassID["gpb"] = 571; NTIPAliasClassID["perfectsapphire"] = 571; -NTIPAliasClassID["gcg"] = 572; NTIPAliasClassID["chippedemerald"] = 572; -NTIPAliasClassID["gfg"] = 573; NTIPAliasClassID["flawedemerald"] = 573; -NTIPAliasClassID["gsg"] = 574; NTIPAliasClassID["emerald"] = 574; -NTIPAliasClassID["glg"] = 575; NTIPAliasClassID["flawlessemerald"] = 575; -NTIPAliasClassID["gpg"] = 576; NTIPAliasClassID["perfectemerald"] = 576; -NTIPAliasClassID["gcr"] = 577; NTIPAliasClassID["chippedruby"] = 577; -NTIPAliasClassID["gfr"] = 578; NTIPAliasClassID["flawedruby"] = 578; -NTIPAliasClassID["gsr"] = 579; NTIPAliasClassID["ruby"] = 579; -NTIPAliasClassID["glr"] = 580; NTIPAliasClassID["flawlessruby"] = 580; -NTIPAliasClassID["gpr"] = 581; NTIPAliasClassID["perfectruby"] = 581; -NTIPAliasClassID["gcw"] = 582; NTIPAliasClassID["chippeddiamond"] = 582; -NTIPAliasClassID["gfw"] = 583; NTIPAliasClassID["flaweddiamond"] = 583; -NTIPAliasClassID["gsw"] = 584; NTIPAliasClassID["diamond"] = 584; -NTIPAliasClassID["glw"] = 585; NTIPAliasClassID["flawlessdiamond"] = 585; -NTIPAliasClassID["gpw"] = 586; NTIPAliasClassID["perfectdiamond"] = 586; -NTIPAliasClassID["hp1"] = 587; NTIPAliasClassID["minorhealingpotion"] = 587; -NTIPAliasClassID["hp2"] = 588; NTIPAliasClassID["lighthealingpotion"] = 588; -NTIPAliasClassID["hp3"] = 589; NTIPAliasClassID["healingpotion"] = 589; -NTIPAliasClassID["hp4"] = 590; NTIPAliasClassID["greaterhealingpotion"] = 590; -NTIPAliasClassID["hp5"] = 591; NTIPAliasClassID["superhealingpotion"] = 591; -NTIPAliasClassID["mp1"] = 592; NTIPAliasClassID["minormanapotion"] = 592; -NTIPAliasClassID["mp2"] = 593; NTIPAliasClassID["lightmanapotion"] = 593; -NTIPAliasClassID["mp3"] = 594; NTIPAliasClassID["manapotion"] = 594; -NTIPAliasClassID["mp4"] = 595; NTIPAliasClassID["greatermanapotion"] = 595; -NTIPAliasClassID["mp5"] = 596; NTIPAliasClassID["supermanapotion"] = 596; -NTIPAliasClassID["skc"] = 597; NTIPAliasClassID["chippedskull"] = 597; -NTIPAliasClassID["skf"] = 598; NTIPAliasClassID["flawedskull"] = 598; -NTIPAliasClassID["sku"] = 599; NTIPAliasClassID["skull"] = 599; -NTIPAliasClassID["skl"] = 600; NTIPAliasClassID["flawlessskull"] = 600; -NTIPAliasClassID["skz"] = 601; NTIPAliasClassID["perfectskull"] = 601; -NTIPAliasClassID["hrb"] = 602; NTIPAliasClassID["herb"] = 602; -NTIPAliasClassID["cm1"] = 603; NTIPAliasClassID["smallcharm"] = 603; -NTIPAliasClassID["cm2"] = 604; NTIPAliasClassID["largecharm"] = 604; -NTIPAliasClassID["cm3"] = 605; NTIPAliasClassID["grandcharm"] = 605; -NTIPAliasClassID["rps"] = 606; -NTIPAliasClassID["rpl"] = 607; -NTIPAliasClassID["bps"] = 608; -NTIPAliasClassID["bpl"] = 609; -NTIPAliasClassID["r01"] = 610; NTIPAliasClassID["elrune"] = 610; -NTIPAliasClassID["r02"] = 611; NTIPAliasClassID["eldrune"] = 611; -NTIPAliasClassID["r03"] = 612; NTIPAliasClassID["tirrune"] = 612; -NTIPAliasClassID["r04"] = 613; NTIPAliasClassID["nefrune"] = 613; -NTIPAliasClassID["r05"] = 614; NTIPAliasClassID["ethrune"] = 614; -NTIPAliasClassID["r06"] = 615; NTIPAliasClassID["ithrune"] = 615; -NTIPAliasClassID["r07"] = 616; NTIPAliasClassID["talrune"] = 616; -NTIPAliasClassID["r08"] = 617; NTIPAliasClassID["ralrune"] = 617; -NTIPAliasClassID["r09"] = 618; NTIPAliasClassID["ortrune"] = 618; -NTIPAliasClassID["r10"] = 619; NTIPAliasClassID["thulrune"] = 619; -NTIPAliasClassID["r11"] = 620; NTIPAliasClassID["amnrune"] = 620; -NTIPAliasClassID["r12"] = 621; NTIPAliasClassID["solrune"] = 621; -NTIPAliasClassID["r13"] = 622; NTIPAliasClassID["shaelrune"] = 622; -NTIPAliasClassID["r14"] = 623; NTIPAliasClassID["dolrune"] = 623; -NTIPAliasClassID["r15"] = 624; NTIPAliasClassID["helrune"] = 624; -NTIPAliasClassID["r16"] = 625; NTIPAliasClassID["iorune"] = 625; -NTIPAliasClassID["r17"] = 626; NTIPAliasClassID["lumrune"] = 626; -NTIPAliasClassID["r18"] = 627; NTIPAliasClassID["korune"] = 627; -NTIPAliasClassID["r19"] = 628; NTIPAliasClassID["falrune"] = 628; -NTIPAliasClassID["r20"] = 629; NTIPAliasClassID["lemrune"] = 629; -NTIPAliasClassID["r21"] = 630; NTIPAliasClassID["pulrune"] = 630; -NTIPAliasClassID["r22"] = 631; NTIPAliasClassID["umrune"] = 631; -NTIPAliasClassID["r23"] = 632; NTIPAliasClassID["malrune"] = 632; -NTIPAliasClassID["r24"] = 633; NTIPAliasClassID["istrune"] = 633; -NTIPAliasClassID["r25"] = 634; NTIPAliasClassID["gulrune"] = 634; -NTIPAliasClassID["r26"] = 635; NTIPAliasClassID["vexrune"] = 635; -NTIPAliasClassID["r27"] = 636; NTIPAliasClassID["ohmrune"] = 636; -NTIPAliasClassID["r28"] = 637; NTIPAliasClassID["lorune"] = 637; -NTIPAliasClassID["r29"] = 638; NTIPAliasClassID["surrune"] = 638; -NTIPAliasClassID["r30"] = 639; NTIPAliasClassID["berrune"] = 639; -NTIPAliasClassID["r31"] = 640; NTIPAliasClassID["jahrune"] = 640; -NTIPAliasClassID["r32"] = 641; NTIPAliasClassID["chamrune"] = 641; -NTIPAliasClassID["r33"] = 642; NTIPAliasClassID["zodrune"] = 642; -NTIPAliasClassID["jew"] = 643; NTIPAliasClassID["jewel"] = 643; -NTIPAliasClassID["ice"] = 644; NTIPAliasClassID["malah'spotion"] = 644; -NTIPAliasClassID["0sc"] = 645; NTIPAliasClassID["scrollofknowledge"] = 645; -NTIPAliasClassID["tr2"] = 646; NTIPAliasClassID["scrollofresistance"] = 646; -NTIPAliasClassID["pk1"] = 647; NTIPAliasClassID["keyofterror"] = 647; -NTIPAliasClassID["pk2"] = 648; NTIPAliasClassID["keyofhate"] = 648; -NTIPAliasClassID["pk3"] = 649; NTIPAliasClassID["keyofdestruction"] = 649; -NTIPAliasClassID["dhn"] = 650; NTIPAliasClassID["diablo'shorn"] = 650; -NTIPAliasClassID["bey"] = 651; NTIPAliasClassID["baal'seye"] = 651; -NTIPAliasClassID["mbr"] = 652; NTIPAliasClassID["mephisto'sbrain"] = 652; -NTIPAliasClassID["toa"] = 653; NTIPAliasClassID["tokenofabsolution"] = 653; -NTIPAliasClassID["tes"] = 654; NTIPAliasClassID["twistedessenceofsuffering"] = 654; -NTIPAliasClassID["ceh"] = 655; NTIPAliasClassID["chargedessenceofhatred"] = 655; -NTIPAliasClassID["bet"] = 656; NTIPAliasClassID["burningessenceofterror"] = 656; -NTIPAliasClassID["fed"] = 657; NTIPAliasClassID["festeringessenceofdestruction"] = 657; -NTIPAliasClassID["std"] = 658; NTIPAliasClassID["standardofheroes"] = 658; - -var NTIPAliasClass = {}; -NTIPAliasClass["normal"] = 0; -NTIPAliasClass["exceptional"] = 1; -NTIPAliasClass["elite"] = 2; - -var NTIPAliasQuality = {}; -NTIPAliasQuality["lowquality"] = 1; -NTIPAliasQuality["normal"] = 2; -NTIPAliasQuality["superior"] = 3; -NTIPAliasQuality["magic"] = 4; -NTIPAliasQuality["set"] = 5; -NTIPAliasQuality["rare"] = 6; -NTIPAliasQuality["unique"] = 7; -NTIPAliasQuality["crafted"] = 8; - -var NTIPAliasFlag = {}; -NTIPAliasFlag["identified"] = 0x10; -NTIPAliasFlag["eth"] = 0x400000; NTIPAliasFlag["ethereal"] = 0x400000; -NTIPAliasFlag["runeword"] = 0x4000000; - -// rare item colors -var NTIPAliasColor = {}; -NTIPAliasColor["black"] = 3; -NTIPAliasColor["white"] = 20; -NTIPAliasColor["orange"] = 19; -NTIPAliasColor["lightyellow"] = 13; -NTIPAliasColor["lightred"] = 7; -NTIPAliasColor["lightgold"] = 15; -NTIPAliasColor["lightblue"] = 4; -NTIPAliasColor["lightpurple"] = 17; -NTIPAliasColor["crystalblue"] = 6; -NTIPAliasColor["crystalred"] = 9; -NTIPAliasColor["crystalgreen"] = 12; -NTIPAliasColor["darkyellow"] = 14; -NTIPAliasColor["darkred"] = 8; -NTIPAliasColor["darkgold"] = 16; -NTIPAliasColor["darkgreen"] = 11; -NTIPAliasColor["darkblue"] = 5; - -var NTIPAliasStat = {}; -NTIPAliasStat["strength"] = 0; -NTIPAliasStat["energy"] = 1; -NTIPAliasStat["dexterity"] = 2; -NTIPAliasStat["vitality"] = 3; -NTIPAliasStat["statpts"] = 4; -NTIPAliasStat["newskills"] = 5; -NTIPAliasStat["hitpoints"] = 6; -NTIPAliasStat["maxhp"] = 7; -NTIPAliasStat["mana"] = 8; -NTIPAliasStat["maxmana"] = 9; -NTIPAliasStat["stamina"] = 10; -NTIPAliasStat["maxstamina"] = 11; -NTIPAliasStat["level"] = 12; -NTIPAliasStat["experience"] = 13; -NTIPAliasStat["gold"] = 14; -NTIPAliasStat["goldbank"] = 15; -NTIPAliasStat["itemarmorpercent"] = [16,0]; NTIPAliasStat["enhanceddefense"] = [16,0]; -NTIPAliasStat["itemmaxdamagepercent"] = [17,0]; -NTIPAliasStat["itemmindamagepercent"] = [18,0]; NTIPAliasStat["enhanceddamage"] = [18,0]; -NTIPAliasStat["tohit"] = 19; -NTIPAliasStat["toblock"] = 20; -NTIPAliasStat["plusmindamage"] = [21, 1]; -NTIPAliasStat["mindamage"] = 21; -NTIPAliasStat["plusmaxdamage"] = [22, 1]; -NTIPAliasStat["maxdamage"] = 22; -NTIPAliasStat["secondarymindamage"] = 23; -NTIPAliasStat["secondarymaxdamage"] = 24; -NTIPAliasStat["damagepercent"] = 25; -NTIPAliasStat["manarecovery"] = 26; -NTIPAliasStat["manarecoverybonus"] = 27; -NTIPAliasStat["staminarecoverybonus"] = 28; -NTIPAliasStat["lastexp"] = 29; -NTIPAliasStat["nextexp"] = 30; - -NTIPAliasStat["armorclass"] = 31; NTIPAliasStat["defense"] = 31; -NTIPAliasStat["plusdefense"] = [31,0]; - -NTIPAliasStat["armorclassvsmissile"] = 32; -NTIPAliasStat["armorclassvshth"] = 33; -NTIPAliasStat["normaldamagereduction"] = 34; -NTIPAliasStat["magicdamagereduction"] = 35; -NTIPAliasStat["damageresist"] = 36; -NTIPAliasStat["magicresist"] = 37; -NTIPAliasStat["maxmagicresist"] = 38; -NTIPAliasStat["fireresist"] = 39; -NTIPAliasStat["maxfireresist"] = 40; -NTIPAliasStat["lightresist"] = 41; -NTIPAliasStat["maxlightresist"] = 42; -NTIPAliasStat["coldresist"] = 43; -NTIPAliasStat["maxcoldresist"] = 44; -NTIPAliasStat["poisonresist"] = 45; -NTIPAliasStat["maxpoisonresist"] = 46; -NTIPAliasStat["damageaura"] = 47; -NTIPAliasStat["firemindam"] = 48; -NTIPAliasStat["firemaxdam"] = 49; -NTIPAliasStat["lightmindam"] = 50; -NTIPAliasStat["lightmaxdam"] = 51; -NTIPAliasStat["magicmindam"] = 52; -NTIPAliasStat["magicmaxdam"] = 53; -NTIPAliasStat["coldmindam"] = 54; -NTIPAliasStat["coldmaxdam"] = 55; -NTIPAliasStat["coldlength"] = 56; -NTIPAliasStat["poisondamage"] = [57, 1]; -NTIPAliasStat["poisonmindam"] = 57; -NTIPAliasStat["poisonmaxdam"] = 58; -NTIPAliasStat["poisonlength"] = 59; -NTIPAliasStat["lifedrainmindam"] = 60; NTIPAliasStat["lifeleech"] = 60; -NTIPAliasStat["lifedrainmaxdam"] = 61; -NTIPAliasStat["manadrainmindam"] = 62; NTIPAliasStat["manaleech"] = 62; -NTIPAliasStat["manadrainmaxdam"] = 63; -NTIPAliasStat["stamdrainmindam"] = 64; -NTIPAliasStat["stamdrainmaxdam"] = 65; -NTIPAliasStat["stunlength"] = 66; -NTIPAliasStat["velocitypercent"] = 67; -NTIPAliasStat["attackrate"] = 68; -NTIPAliasStat["otheranimrate"] = 69; -NTIPAliasStat["quantity"] = 70; -NTIPAliasStat["value"] = 71; -NTIPAliasStat["durability"] = 72; -NTIPAliasStat["maxdurability"] = 73; -NTIPAliasStat["hpregen"] = 74; -NTIPAliasStat["itemmaxdurabilitypercent"] = 75; -NTIPAliasStat["itemmaxhppercent"] = 76; -NTIPAliasStat["itemmaxmanapercent"] = 77; -NTIPAliasStat["itemattackertakesdamage"] = 78; -NTIPAliasStat["itemgoldbonus"] = 79; -NTIPAliasStat["itemmagicbonus"] = 80; -NTIPAliasStat["itemknockback"] = 81; -NTIPAliasStat["itemtimeduration"] = 82; - -NTIPAliasStat["itemaddclassskills"] = 83; -NTIPAliasStat["itemaddamazonskills"] = [83,0]; NTIPAliasStat["amazonskills"] = [83,0]; -NTIPAliasStat["itemaddsorceressskills"] = [83,1]; NTIPAliasStat["sorceressskills"] = [83,1]; -NTIPAliasStat["itemaddnecromancerskills"] = [83,2]; NTIPAliasStat["necromancerskills"] = [83,2]; -NTIPAliasStat["itemaddpaladinskills"] = [83,3]; NTIPAliasStat["paladinskills"] = [83,3]; -NTIPAliasStat["itemaddbarbarianskills"] = [83,4]; NTIPAliasStat["barbarianskills"] = [83,4]; -NTIPAliasStat["itemadddruidskills"] = [83,5]; NTIPAliasStat["druidskills"] = [83,5]; -NTIPAliasStat["itemaddassassinskills"] = [83,6]; NTIPAliasStat["assassinskills"] = [83,6]; - -NTIPAliasStat["unsentparam1"] = 84; -NTIPAliasStat["itemaddexperience"] = 85; -NTIPAliasStat["itemhealafterkill"] = 86; -NTIPAliasStat["itemreducedprices"] = 87; -NTIPAliasStat["itemdoubleherbduration"] = 88; -NTIPAliasStat["itemlightradius"] = 89; -NTIPAliasStat["itemlightcolor"] = 90; -NTIPAliasStat["itemreqpercent"] = 91; -NTIPAliasStat["itemlevelreq"] = 92; -NTIPAliasStat["itemfasterattackrate"] = 93; NTIPAliasStat["ias"] = 93; -NTIPAliasStat["itemlevelreqpct"] = 94; -NTIPAliasStat["lastblockframe"] = 95; -NTIPAliasStat["itemfastermovevelocity"] = 96; NTIPAliasStat["frw"] = 96; - -// oskill -NTIPAliasStat["itemnonclassskill"] = 97; -// Amazon -NTIPAliasStat["plusskillcriticalstrike"] = [97,9]; -NTIPAliasStat["plusskillguidedarrow"] = [97,22]; -// Sorceress -NTIPAliasStat["plusskillteleport"] = [97,54]; -// Barbarian -NTIPAliasStat["plusskillbattleorders"] = [97,149]; -NTIPAliasStat["plusskillbattlecommand"] = [97,155]; -NTIPAliasStat["plusskillbattlecry"] = [97,146]; -// Druid -NTIPAliasStat["plusskillwerewolf"] = [97,223]; -NTIPAliasStat["plusskillshapeshifting"] = [97,224]; NTIPAliasStat["plusskilllycanthropy"] = [97,224]; -NTIPAliasStat["plusskillsummonspiritwolf"] = [97,227]; -NTIPAliasStat["plusskillferalrage"] = [97,232]; - -NTIPAliasStat["state"] = 98; -NTIPAliasStat["itemfastergethitrate"] = 99; NTIPAliasStat["fhr"] = 99; -NTIPAliasStat["monsterplayercount"] = 100; -NTIPAliasStat["skillpoisonoverridelength"] = 101; -NTIPAliasStat["itemfasterblockrate"] = 102; NTIPAliasStat["fbr"] = 102; -NTIPAliasStat["skillbypassundead"] = 103; -NTIPAliasStat["skillbypassdemons"] = 104; -NTIPAliasStat["itemfastercastrate"] = 105; NTIPAliasStat["fcr"] = 105; -NTIPAliasStat["skillbypassbeasts"] = 106; - -NTIPAliasStat["itemsingleskill"] = 107; -// Amazon skills -NTIPAliasStat["skillmagicarrow"] = [107,6]; -NTIPAliasStat["skillfirearrow"] = [107,7]; -NTIPAliasStat["skillinnersight"] = [107,8]; -NTIPAliasStat["skillcriticalstrike"] = [107,9]; -NTIPAliasStat["skilljab"] = [107,10]; -NTIPAliasStat["skillcoldarrow"] = [107,11]; -NTIPAliasStat["skillmultipleshot"] = [107,12]; -NTIPAliasStat["skilldodge"] = [107,13]; -NTIPAliasStat["skillpowerstrike"] = [107,14]; -NTIPAliasStat["skillpoisonjavelin"] = [107,15]; -NTIPAliasStat["skillexplodingarrow"] = [107,16]; -NTIPAliasStat["skillslowmissiles"] = [107,17]; -NTIPAliasStat["skillavoid"] = [107,18]; -NTIPAliasStat["skillimpale"] = [107,19]; -NTIPAliasStat["skilllightningbolt"] = [107,20]; -NTIPAliasStat["skillicearrow"] = [107,21]; -NTIPAliasStat["skillguidedarrow"] = [107,22]; -NTIPAliasStat["skillpenetrate"] = [107,23]; -NTIPAliasStat["skillchargedstrike"] = [107,24]; -NTIPAliasStat["skillplaguejavelin"] = [107,25]; -NTIPAliasStat["skillstrafe"] = [107,26]; -NTIPAliasStat["skillimmolationarrow"] = [107,27]; -NTIPAliasStat["skilldecoy"] = [107,28]; -NTIPAliasStat["skillevade"] = [107,29]; -NTIPAliasStat["skillfend"] = [107,30]; -NTIPAliasStat["skillfreezingarrow"] = [107,31]; -NTIPAliasStat["skillvalkyrie"] = [107,32]; -NTIPAliasStat["skillpierce"] = [107,33]; -NTIPAliasStat["skilllightningstrike"] = [107,34]; -NTIPAliasStat["skilllightningfury"] = [107,35]; -// Sorceress skills -NTIPAliasStat["skillfirebolt"] = [107,36]; -NTIPAliasStat["skillwarmth"] = [107,37]; -NTIPAliasStat["skillchargedbolt"] = [107,38]; -NTIPAliasStat["skillicebolt"] = [107,39]; -NTIPAliasStat["skillfrozenarmor"] = [107,40]; -NTIPAliasStat["skillinferno"] = [107,41]; -NTIPAliasStat["skillstaticfield"] = [107,42]; -NTIPAliasStat["skilltelekinesis"] = [107,43]; -NTIPAliasStat["skillfrostnova"] = [107,44]; -NTIPAliasStat["skilliceblast"] = [107,45]; -NTIPAliasStat["skillblaze"] = [107,46]; -NTIPAliasStat["skillfireball"] = [107,47]; -NTIPAliasStat["skillnova"] = [107,48]; -NTIPAliasStat["skilllightning"] = [107,49]; -NTIPAliasStat["skillshiverarmor"] = [107,50]; -NTIPAliasStat["skillfirewall"] = [107,51]; -NTIPAliasStat["skillenchant"] = [107,52]; -NTIPAliasStat["skillchainlightning"] = [107,53]; -NTIPAliasStat["skillteleport"] = [107,54]; -NTIPAliasStat["skillglacialspike"] = [107,55]; -NTIPAliasStat["skillmeteor"] = [107,56]; -NTIPAliasStat["skillthunderstorm"] = [107,57]; -NTIPAliasStat["skillenergyshield"] = [107,58]; -NTIPAliasStat["skillblizzard"] = [107,59]; -NTIPAliasStat["skillchillingarmor"] = [107,60]; -NTIPAliasStat["skillfiremastery"] = [107,61]; -NTIPAliasStat["skillhydra"] = [107,62]; -NTIPAliasStat["skilllightningmastery"] = [107,63]; -NTIPAliasStat["skillfrozenorb"] = [107,64]; -NTIPAliasStat["skillcoldmastery"] = [107,65]; -// Necromancer skills -NTIPAliasStat["skillamplifydamage"] = [107,66]; -NTIPAliasStat["skillteeth"] = [107,67]; -NTIPAliasStat["skillbonearmor"] = [107,68]; -NTIPAliasStat["skillskeletonmastery"] = [107,69]; -NTIPAliasStat["skillraiseskeleton"] = [107,70]; -NTIPAliasStat["skilldimvision"] = [107,71]; -NTIPAliasStat["skillweaken"] = [107,72]; -NTIPAliasStat["skillpoisondagger"] = [107,73]; -NTIPAliasStat["skillcorpseexplosion"] = [107,74]; -NTIPAliasStat["skillclaygolem"] = [107,75]; -NTIPAliasStat["skillironmaiden"] = [107,76]; -NTIPAliasStat["skillterror"] = [107,77]; -NTIPAliasStat["skillbonewall"] = [107,78]; -NTIPAliasStat["skillgolemmastery"] = [107,79]; -NTIPAliasStat["skillskeletalmage"] = [107,80]; -NTIPAliasStat["skillconfuse"] = [107,81]; -NTIPAliasStat["skilllifetap"] = [107,82]; -NTIPAliasStat["skillpoisonexplosion"] = [107,83]; -NTIPAliasStat["skillbonespear"] = [107,84]; -NTIPAliasStat["skillbloodgolem"] = [107,85]; -NTIPAliasStat["skillattract"] = [107,86]; -NTIPAliasStat["skilldecrepify"] = [107,87]; -NTIPAliasStat["skillboneprison"] = [107,88]; -NTIPAliasStat["skillsummonresist"] = [107,89]; -NTIPAliasStat["skillirongolem"] = [107,90]; -NTIPAliasStat["skilllowerresist"] = [107,91]; -NTIPAliasStat["skillpoisonnova"] = [107,92]; -NTIPAliasStat["skillbonespirit"] = [107,93]; -NTIPAliasStat["skillfiregolem"] = [107,94]; -NTIPAliasStat["skillrevive"] = [107,95]; -// Paladin skills -NTIPAliasStat["skillsacrifice"] = [107,96]; -NTIPAliasStat["skillsmite"] = [107,97]; -NTIPAliasStat["skillmight"] = [107,98]; -NTIPAliasStat["skillprayer"] = [107,99]; -NTIPAliasStat["skillresistfire"] = [107,100]; -NTIPAliasStat["skillholybolt"] = [107,101]; -NTIPAliasStat["skillholyfire"] = [107,102]; -NTIPAliasStat["skillthorns"] = [107,103]; -NTIPAliasStat["skilldefiance"] = [107,104]; -NTIPAliasStat["skillresistcold"] = [107,105]; -NTIPAliasStat["skillzeal"] = [107,106]; -NTIPAliasStat["skillcharge"] = [107,107]; -NTIPAliasStat["skillblessedaim"] = [107,108]; -NTIPAliasStat["skillcleansing"] = [107,109]; -NTIPAliasStat["skillresistlightning"] = [107,110]; -NTIPAliasStat["skillvengeance"] = [107,111]; -NTIPAliasStat["skillblessedhammer"] = [107,112]; -NTIPAliasStat["skillconcentration"] = [107,113]; -NTIPAliasStat["skillholyfreeze"] = [107,114]; -NTIPAliasStat["skillvigor"] = [107,115]; -NTIPAliasStat["skillconversion"] = [107,116]; -NTIPAliasStat["skillholyshield"] = [107,117]; -NTIPAliasStat["skillholyshock"] = [107,118]; -NTIPAliasStat["skillsanctuary"] = [107,119]; -NTIPAliasStat["skillmeditation"] = [107,120]; -NTIPAliasStat["skillfistoftheheavens"] = [107,121]; -NTIPAliasStat["skillfanaticism"] = [107,122]; -NTIPAliasStat["skillconviction"] = [107,123]; -NTIPAliasStat["skillredemption"] = [107,124]; -NTIPAliasStat["skillsalvation"] = [107,125]; -// Barbarian skills -NTIPAliasStat["skillbash"] = [107,126]; -NTIPAliasStat["skillswordmastery"] = [107,127]; -NTIPAliasStat["skillaxemastery"] = [107,128]; -NTIPAliasStat["skillmacemastery"] = [107,129]; -NTIPAliasStat["skillhowl"] = [107,130]; -NTIPAliasStat["skillfindpotion"] = [107,131]; -NTIPAliasStat["skillleap"] = [107,132]; -NTIPAliasStat["skilldoubleswing"] = [107,133]; -NTIPAliasStat["skillpolearmmastery"] = [107,134]; -NTIPAliasStat["skillthrowingmastery"] = [107,135]; -NTIPAliasStat["skillspearmastery"] = [107,136]; -NTIPAliasStat["skilltaunt"] = [107,137]; -NTIPAliasStat["skillshout"] = [107,138]; -NTIPAliasStat["skillstun"] = [107,139]; -NTIPAliasStat["skilldoublethrow"] = [107,140]; -NTIPAliasStat["skillincreasedstamina"] = [107,141]; -NTIPAliasStat["skillfinditem"] = [107,142]; -NTIPAliasStat["skillleapattack"] = [107,143]; -NTIPAliasStat["skillconcentrate"] = [107,144]; -NTIPAliasStat["skillironskin"] = [107,145]; -NTIPAliasStat["skillbattlecry"] = [107,146]; -NTIPAliasStat["skillfrenzy"] = [107,147]; -NTIPAliasStat["skillincreasedspeed"] = [107,148]; -NTIPAliasStat["skillbattleorders"] = [107,149]; -NTIPAliasStat["skillgrimward"] = [107,150]; -NTIPAliasStat["skillwhirlwind"] = [107,151]; -NTIPAliasStat["skillberserk"] = [107,152]; -NTIPAliasStat["skillnaturalresistance"] = [107,153]; -NTIPAliasStat["skillwarcry"] = [107,154]; -NTIPAliasStat["skillbattlecommand"] = [107,155]; -// Druid skills -NTIPAliasStat["skillraven"] = [107,221]; -NTIPAliasStat["skillpoisoncreeper"] = [107,222]; -NTIPAliasStat["skillwerewolf"] = [107,223]; -NTIPAliasStat["skilllycanthropy"] = [107,224]; -NTIPAliasStat["skillfirestorm"] = [107,225]; -NTIPAliasStat["skilloaksage"] = [107,226]; -NTIPAliasStat["skillsummonspiritwolf"] = [107,227]; -NTIPAliasStat["skillwerebear"] = [107,228]; -NTIPAliasStat["skillmoltenboulder"] = [107,229]; -NTIPAliasStat["skillarcticblast"] = [107,230]; -NTIPAliasStat["skillcarrionvine"] = [107,231]; -NTIPAliasStat["skillferalrage"] = [107,232]; -NTIPAliasStat["skillmaul"] = [107,233]; -NTIPAliasStat["skillfissure"] = [107,234]; -NTIPAliasStat["skillcyclonearmor"] = [107,235]; -NTIPAliasStat["skillheartofwolverine"] = [107,236]; -NTIPAliasStat["skillsummondirewolf"] = [107,237]; -NTIPAliasStat["skillrabies"] = [107,238]; -NTIPAliasStat["skillfireclaws"] = [107,239]; -NTIPAliasStat["skilltwister"] = [107,240]; -NTIPAliasStat["skillsolarcreeper"] = [107,241]; -NTIPAliasStat["skillhunger"] = [107,242]; -NTIPAliasStat["skillshockwave"] = [107,243]; -NTIPAliasStat["skillvolcano"] = [107,244]; -NTIPAliasStat["skilltornado"] = [107,245]; -NTIPAliasStat["skillspiritofbarbs"] = [107,246]; -NTIPAliasStat["skillsummongrizzly"] = [107,247]; -NTIPAliasStat["skillfury"] = [107,248]; -NTIPAliasStat["skillarmageddon"] = [107,249]; -NTIPAliasStat["skillhurricane"] = [107,250]; -// Assassin skills -NTIPAliasStat["skillfireblast"] = [107,251]; -NTIPAliasStat["skillclawmastery"] = [107,252]; -NTIPAliasStat["skillpsychichammer"] = [107,253]; -NTIPAliasStat["skilltigerstrike"] = [107,254]; -NTIPAliasStat["skilldragontalon"] = [107,255]; -NTIPAliasStat["skillshockweb"] = [107,256]; -NTIPAliasStat["skillbladesentinel"] = [107,257]; -NTIPAliasStat["skillburstofspeed"] = [107,258]; -NTIPAliasStat["skillfistsoffire"] = [107,259]; -NTIPAliasStat["skilldragonclaw"] = [107,260]; -NTIPAliasStat["skillchargedboltsentry"] = [107,261]; -NTIPAliasStat["skillwakeoffire"] = [107,262]; -NTIPAliasStat["skillweaponblock"] = [107,263]; -NTIPAliasStat["skillcloakofshadows"] = [107,264]; -NTIPAliasStat["skillcobrastrike"] = [107,265]; -NTIPAliasStat["skillbladefury"] = [107,266]; -NTIPAliasStat["skillfade"] = [107,267]; -NTIPAliasStat["skillshadowwarrior"] = [107,268]; -NTIPAliasStat["skillclawsofthunder"] = [107,269]; -NTIPAliasStat["skilldragontail"] = [107,270]; -NTIPAliasStat["skilllightningsentry"] = [107,271]; -NTIPAliasStat["skillwakeofinferno"] = [107,272]; -NTIPAliasStat["skillmindblast"] = [107,273]; -NTIPAliasStat["skillbladesofice"] = [107,274]; -NTIPAliasStat["skilldragonflight"] = [107,275]; -NTIPAliasStat["skilldeathsentry"] = [107,276]; -NTIPAliasStat["skillbladeshield"] = [107,277]; -NTIPAliasStat["skillvenom"] = [107,278]; -NTIPAliasStat["skillshadowmaster"] = [107,279]; -NTIPAliasStat["skillphoenixstrike"] = [107,280]; - -NTIPAliasStat["itemrestinpeace"] = 108; -NTIPAliasStat["curseresistance"] = 109; -NTIPAliasStat["itempoisonlengthresist"] = 110; -NTIPAliasStat["itemnormaldamage"] = 111; -NTIPAliasStat["itemhowl"] = 112; -NTIPAliasStat["itemstupidity"] = 113; -NTIPAliasStat["itemdamagetomana"] = 114; -NTIPAliasStat["itemignoretargetac"] = 115; -NTIPAliasStat["itemfractionaltargetac"] = 116; -NTIPAliasStat["itempreventheal"] = 117; -NTIPAliasStat["itemhalffreezeduration"] = 118; -NTIPAliasStat["itemtohitpercent"] = 119; -NTIPAliasStat["itemdamagetargetac"] = 120; -NTIPAliasStat["itemdemondamagepercent"] = 121; -NTIPAliasStat["itemundeaddamagepercent"] = 122; -NTIPAliasStat["itemdemontohit"] = 123; -NTIPAliasStat["itemundeadtohit"] = 124; -NTIPAliasStat["itemthrowable"] = 125; -NTIPAliasStat["itemelemskill"] = 126; -NTIPAliasStat["itemallskills"] = 127; -NTIPAliasStat["itemattackertakeslightdamage"] = 128; -NTIPAliasStat["ironmaidenlevel"] = 129; -NTIPAliasStat["lifetaplevel"] = 130; -NTIPAliasStat["thornspercent"] = 131; -NTIPAliasStat["bonearmor"] = 132; -NTIPAliasStat["bonearmormax"] = 133; -NTIPAliasStat["itemfreeze"] = 134; -NTIPAliasStat["itemopenwounds"] = 135; -NTIPAliasStat["itemcrushingblow"] = 136; -NTIPAliasStat["itemkickdamage"] = 137; -NTIPAliasStat["itemmanaafterkill"] = 138; -NTIPAliasStat["itemhealafterdemonkill"] = 139; -NTIPAliasStat["itemextrablood"] = 140; -NTIPAliasStat["itemdeadlystrike"] = 141; -NTIPAliasStat["itemabsorbfirepercent"] = 142; -NTIPAliasStat["itemabsorbfire"] = 143; -NTIPAliasStat["itemabsorblightpercent"] = 144; -NTIPAliasStat["itemabsorblight"] = 145; -NTIPAliasStat["itemabsorbmagicpercent"] = 146; -NTIPAliasStat["itemabsorbmagic"] = 147; -NTIPAliasStat["itemabsorbcoldpercent"] = 148; -NTIPAliasStat["itemabsorbcold"] = 149; -NTIPAliasStat["itemslow"] = 150; - -NTIPAliasStat["itemaura"] = 151; -NTIPAliasStat["mightaura"] = [151,98]; -NTIPAliasStat["holyfireaura"] = [151,102]; -NTIPAliasStat["thornsaura"] = [151,103]; -NTIPAliasStat["defianceaura"] = [151,104]; -NTIPAliasStat["concentrationaura"] = [151,113]; -NTIPAliasStat["holyfreezeaura"] = [151,114]; -NTIPAliasStat["vigoraura"] = [151,115]; -NTIPAliasStat["holyshockaura"] = [151,118]; -NTIPAliasStat["sanctuaryaura"] = [151,119]; -NTIPAliasStat["meditationaura"] = [151,120]; -NTIPAliasStat["fanaticismaura"] = [151,122]; -NTIPAliasStat["convictionaura"] = [151,123]; -NTIPAliasStat["redemptionaura"] = [151,124]; - -NTIPAliasStat["itemindestructible"] = 152; -NTIPAliasStat["itemcannotbefrozen"] = 153; -NTIPAliasStat["itemstaminadrainpct"] = 154; -NTIPAliasStat["itemreanimate"] = 155; -NTIPAliasStat["itempierce"] = 156; -NTIPAliasStat["itemmagicarrow"] = 157; -NTIPAliasStat["itemexplosivearrow"] = 158; -NTIPAliasStat["itemthrowmindamage"] = 159; -NTIPAliasStat["itemthrowmaxdamage"] = 160; -NTIPAliasStat["itemskillhandofathena"] = 161; -NTIPAliasStat["itemskillstaminapercent"] = 162; -NTIPAliasStat["itemskillpassivestaminapercent"] = 163; -NTIPAliasStat["itemskillconcentration"] = 164; -NTIPAliasStat["itemskillenchant"] = 165; -NTIPAliasStat["itemskillpierce"] = 166; -NTIPAliasStat["itemskillconviction"] = 167; -NTIPAliasStat["itemskillchillingarmor"] = 168; -NTIPAliasStat["itemskillfrenzy"] = 169; -NTIPAliasStat["itemskilldecrepify"] = 170; -NTIPAliasStat["itemskillarmorpercent"] = 171; -NTIPAliasStat["alignment"] = 172; -NTIPAliasStat["target0"] = 173; -NTIPAliasStat["target1"] = 174; -NTIPAliasStat["goldlost"] = 175; -NTIPAliasStat["conversionlevel"] = 176; -NTIPAliasStat["conversionmaxhp"] = 177; -NTIPAliasStat["unitdooverlay"] = 178; -NTIPAliasStat["attackvsmontype"] = 179; -NTIPAliasStat["damagevsmontype"] = 180; -NTIPAliasStat["fade"] = 181; -NTIPAliasStat["armoroverridepercent"] = 182; -NTIPAliasStat["unused183"] = 183; -NTIPAliasStat["unused184"] = 184; -NTIPAliasStat["unused185"] = 185; -NTIPAliasStat["unused186"] = 186; -NTIPAliasStat["unused187"] = 187; - -NTIPAliasStat["itemaddskilltab"] = 188; -NTIPAliasStat["itemaddbowandcrossbowskilltab"] = [188,0]; NTIPAliasStat["bowandcrossbowskilltab"] = [188,0]; -NTIPAliasStat["itemaddpassiveandmagicskilltab"] = [188,1]; NTIPAliasStat["passiveandmagicskilltab"] = [188,1]; -NTIPAliasStat["itemaddjavelinandspearskilltab"] = [188,2]; NTIPAliasStat["javelinandspearskilltab"] = [188,2]; -NTIPAliasStat["itemaddfireskilltab"] = [188,8]; NTIPAliasStat["fireskilltab"] = [188,8]; -NTIPAliasStat["itemaddlightningskilltab"] = [188,9]; NTIPAliasStat["lightningskilltab"] = [188,9]; -NTIPAliasStat["itemaddcoldskilltab"] = [188,10]; NTIPAliasStat["coldskilltab"] = [188,10]; -NTIPAliasStat["itemaddcursesskilltab"] = [188,16]; NTIPAliasStat["cursesskilltab"] = [188,16]; -NTIPAliasStat["itemaddpoisonandboneskilltab"] = [188,17]; NTIPAliasStat["poisonandboneskilltab"] = [188,17]; -NTIPAliasStat["itemaddnecromancersummoningskilltab"] = [188,18]; NTIPAliasStat["necromancersummoningskilltab"] = [188,18]; -NTIPAliasStat["itemaddpalicombatskilltab"] = [188,24]; NTIPAliasStat["palicombatskilltab"] = [188,24]; -NTIPAliasStat["itemaddoffensiveaurasskilltab"] = [188,25]; NTIPAliasStat["offensiveaurasskilltab"] = [188,25]; -NTIPAliasStat["itemadddefensiveaurasskilltab"] = [188,26]; NTIPAliasStat["defensiveaurasskilltab"] = [188,26]; -NTIPAliasStat["itemaddbarbcombatskilltab"] = [188,32]; NTIPAliasStat["barbcombatskilltab"] = [188,32]; -NTIPAliasStat["itemaddmasteriesskilltab"] = [188,33]; NTIPAliasStat["masteriesskilltab"] = [188,33]; -NTIPAliasStat["itemaddwarcriesskilltab"] = [188,34]; NTIPAliasStat["warcriesskilltab"] = [188,34]; -NTIPAliasStat["itemadddruidsummoningskilltab"] = [188,40]; NTIPAliasStat["druidsummoningskilltab"] = [188,40]; -NTIPAliasStat["itemaddshapeshiftingskilltab"] = [188,41]; NTIPAliasStat["shapeshiftingskilltab"] = [188,41]; -NTIPAliasStat["itemaddelementalskilltab"] = [188,42]; NTIPAliasStat["elementalskilltab"] = [188,42]; -NTIPAliasStat["itemaddtrapsskilltab"] = [188,48]; NTIPAliasStat["trapsskilltab"] = [188,48]; -NTIPAliasStat["itemaddshadowdisciplinesskilltab"] = [188,49]; NTIPAliasStat["shadowdisciplinesskilltab"] = [188,49]; -NTIPAliasStat["itemaddmartialartsskilltab"] = [188,50]; NTIPAliasStat["martialartsskilltab"] = [188,50]; - -NTIPAliasStat["unused189"] = 189; -NTIPAliasStat["unused190"] = 190; -NTIPAliasStat["unused191"] = 191; -NTIPAliasStat["unused192"] = 192; -NTIPAliasStat["unused193"] = 193; -NTIPAliasStat["itemnumsockets"] = 194; NTIPAliasStat["sockets"] = 194; -NTIPAliasStat["itemskillonattack"] = [195, 1]; -NTIPAliasStat["itemskillonattacklevel"] = [195, 2]; -NTIPAliasStat["itemskillonkill"] = [196, 1]; -NTIPAliasStat["itemskillonkilllevel"] = [196, 2]; -NTIPAliasStat["itemskillondeath"] = [197, 1]; -NTIPAliasStat["itemskillondeathlevel"] = [197, 2]; - -NTIPAliasStat["itemskillonhit"] = [198, 1]; -NTIPAliasStat["itemskillonhitlevel"] = [198, 2]; -NTIPAliasStat["amplifydamageonhit"] = [198,4225]; - -NTIPAliasStat["itemskillonlevelup"] = [199, 1]; -NTIPAliasStat["itemskillonleveluplevel"] = [199, 2]; -NTIPAliasStat["unused200"] = 200; -NTIPAliasStat["itemskillongethit"] = [201, 1]; -NTIPAliasStat["itemskillongethitlevel"] = [201, 2]; -NTIPAliasStat["unused202"] = 202; -NTIPAliasStat["unused203"] = 203; - -NTIPAliasStat["itemchargedskill"] = [204, 1]; -NTIPAliasStat["itemchargedskilllevel"] = [204, 2]; -NTIPAliasStat["teleportcharges"] = [204,3461]; - -NTIPAliasStat["unused204"] = 205; -NTIPAliasStat["unused205"] = 206; -NTIPAliasStat["unused206"] = 207; -NTIPAliasStat["unused207"] = 208; -NTIPAliasStat["unused208"] = 209; -NTIPAliasStat["unused209"] = 210; -NTIPAliasStat["unused210"] = 211; -NTIPAliasStat["unused211"] = 212; -NTIPAliasStat["unused212"] = 213; -NTIPAliasStat["itemarmorperlevel"] = 214; -NTIPAliasStat["itemarmorpercentperlevel"] = 215; -NTIPAliasStat["itemhpperlevel"] = 216; -NTIPAliasStat["itemmanaperlevel"] = 217; -NTIPAliasStat["itemmaxdamageperlevel"] = 218; -NTIPAliasStat["itemmaxdamagepercentperlevel"] = 219; -NTIPAliasStat["itemstrengthperlevel"] = 220; -NTIPAliasStat["itemdexterityperlevel"] = 221; -NTIPAliasStat["itemenergyperlevel"] = 222; -NTIPAliasStat["itemvitalityperlevel"] = 223; -NTIPAliasStat["itemtohitperlevel"] = 224; -NTIPAliasStat["itemtohitpercentperlevel"] = 225; -NTIPAliasStat["itemcolddamagemaxperlevel"] = 226; -NTIPAliasStat["itemfiredamagemaxperlevel"] = 227; -NTIPAliasStat["itemltngdamagemaxperlevel"] = 228; -NTIPAliasStat["itempoisdamagemaxperlevel"] = 229; -NTIPAliasStat["itemresistcoldperlevel"] = 230; -NTIPAliasStat["itemresistfireperlevel"] = 231; -NTIPAliasStat["itemresistltngperlevel"] = 232; -NTIPAliasStat["itemresistpoisperlevel"] = 233; -NTIPAliasStat["itemabsorbcoldperlevel"] = 234; -NTIPAliasStat["itemabsorbfireperlevel"] = 235; -NTIPAliasStat["itemabsorbltngperlevel"] = 236; -NTIPAliasStat["itemabsorbpoisperlevel"] = 237; -NTIPAliasStat["itemthornsperlevel"] = 238; -NTIPAliasStat["itemfindgoldperlevel"] = 239; -NTIPAliasStat["itemfindmagicperlevel"] = 240; -NTIPAliasStat["itemregenstaminaperlevel"] = 241; -NTIPAliasStat["itemstaminaperlevel"] = 242; -NTIPAliasStat["itemdamagedemonperlevel"] = 243; -NTIPAliasStat["itemdamageundeadperlevel"] = 244; -NTIPAliasStat["itemtohitdemonperlevel"] = 245; -NTIPAliasStat["itemtohitundeadperlevel"] = 246; -NTIPAliasStat["itemcrushingblowperlevel"] = 247; -NTIPAliasStat["itemopenwoundsperlevel"] = 248; -NTIPAliasStat["itemkickdamageperlevel"] = 249; -NTIPAliasStat["itemdeadlystrikeperlevel"] = 250; -NTIPAliasStat["itemfindgemsperlevel"] = 251; -NTIPAliasStat["itemreplenishdurability"] = 252; -NTIPAliasStat["itemreplenishquantity"] = 253; -NTIPAliasStat["itemextrastack"] = 254; -NTIPAliasStat["itemfinditem"] = 255; -NTIPAliasStat["itemslashdamage"] = 256; -NTIPAliasStat["itemslashdamagepercent"] = 257; -NTIPAliasStat["itemcrushdamage"] = 258; -NTIPAliasStat["itemcrushdamagepercent"] = 259; -NTIPAliasStat["itemthrustdamage"] = 260; -NTIPAliasStat["itemthrustdamagepercent"] = 261; -NTIPAliasStat["itemabsorbslash"] = 262; -NTIPAliasStat["itemabsorbcrush"] = 263; -NTIPAliasStat["itemabsorbthrust"] = 264; -NTIPAliasStat["itemabsorbslashpercent"] = 265; -NTIPAliasStat["itemabsorbcrushpercent"] = 266; -NTIPAliasStat["itemabsorbthrustpercent"] = 267; -NTIPAliasStat["itemarmorbytime"] = 268; -NTIPAliasStat["itemarmorpercentbytime"] = 269; -NTIPAliasStat["itemhpbytime"] = 270; -NTIPAliasStat["itemmanabytime"] = 271; -NTIPAliasStat["itemmaxdamagebytime"] = 272; -NTIPAliasStat["itemmaxdamagepercentbytime"] = 273; -NTIPAliasStat["itemstrengthbytime"] = 274; -NTIPAliasStat["itemdexteritybytime"] = 275; -NTIPAliasStat["itemenergybytime"] = 276; -NTIPAliasStat["itemvitalitybytime"] = 277; -NTIPAliasStat["itemtohitbytime"] = 278; -NTIPAliasStat["itemtohitpercentbytime"] = 279; -NTIPAliasStat["itemcolddamagemaxbytime"] = 280; -NTIPAliasStat["itemfiredamagemaxbytime"] = 281; -NTIPAliasStat["itemltngdamagemaxbytime"] = 282; -NTIPAliasStat["itempoisdamagemaxbytime"] = 283; -NTIPAliasStat["itemresistcoldbytime"] = 284; -NTIPAliasStat["itemresistfirebytime"] = 285; -NTIPAliasStat["itemresistltngbytime"] = 286; -NTIPAliasStat["itemresistpoisbytime"] = 287; -NTIPAliasStat["itemabsorbcoldbytime"] = 288; -NTIPAliasStat["itemabsorbfirebytime"] = 289; -NTIPAliasStat["itemabsorbltngbytime"] = 290; -NTIPAliasStat["itemabsorbpoisbytime"] = 291; -NTIPAliasStat["itemfindgoldbytime"] = 292; -NTIPAliasStat["itemfindmagicbytime"] = 293; -NTIPAliasStat["itemregenstaminabytime"] = 294; -NTIPAliasStat["itemstaminabytime"] = 295; -NTIPAliasStat["itemdamagedemonbytime"] = 296; -NTIPAliasStat["itemdamageundeadbytime"] = 297; -NTIPAliasStat["itemtohitdemonbytime"] = 298; -NTIPAliasStat["itemtohitundeadbytime"] = 299; -NTIPAliasStat["itemcrushingblowbytime"] = 300; -NTIPAliasStat["itemopenwoundsbytime"] = 301; -NTIPAliasStat["itemkickdamagebytime"] = 302; -NTIPAliasStat["itemdeadlystrikebytime"] = 303; -NTIPAliasStat["itemfindgemsbytime"] = 304; -NTIPAliasStat["itempiercecold"] = 305; -NTIPAliasStat["itempiercefire"] = 306; -NTIPAliasStat["itempierceltng"] = 307; -NTIPAliasStat["itempiercepois"] = 308; -NTIPAliasStat["itemdamagevsmonster"] = 309; -NTIPAliasStat["itemdamagepercentvsmonster"] = 310; -NTIPAliasStat["itemtohitvsmonster"] = 311; -NTIPAliasStat["itemtohitpercentvsmonster"] = 312; -NTIPAliasStat["itemacvsmonster"] = 313; -NTIPAliasStat["itemacpercentvsmonster"] = 314; -NTIPAliasStat["firelength"] = 315; -NTIPAliasStat["burningmin"] = 316; -NTIPAliasStat["burningmax"] = 317; -NTIPAliasStat["progressivedamage"] = 318; -NTIPAliasStat["progressivesteal"] = 319; -NTIPAliasStat["progressiveother"] = 320; -NTIPAliasStat["progressivefire"] = 321; -NTIPAliasStat["progressivecold"] = 322; -NTIPAliasStat["progressivelightning"] = 323; -NTIPAliasStat["itemextracharges"] = 324; -NTIPAliasStat["progressivetohit"] = 325; -NTIPAliasStat["poisoncount"] = 326; -NTIPAliasStat["damageframerate"] = 327; -NTIPAliasStat["pierceidx"] = 328; -NTIPAliasStat["passivefiremastery"] = 329; -NTIPAliasStat["passiveltngmastery"] = 330; -NTIPAliasStat["passivecoldmastery"] = 331; -NTIPAliasStat["passivepoismastery"] = 332; -NTIPAliasStat["passivefirepierce"] = 333; -NTIPAliasStat["passiveltngpierce"] = 334; -NTIPAliasStat["passivecoldpierce"] = 335; -NTIPAliasStat["passivepoispierce"] = 336; -NTIPAliasStat["passivecriticalstrike"] = 337; -NTIPAliasStat["passivedodge"] = 338; -NTIPAliasStat["passiveavoid"] = 339; -NTIPAliasStat["passiveevade"] = 340; -NTIPAliasStat["passivewarmth"] = 341; -NTIPAliasStat["passivemasterymeleeth"] = 342; -NTIPAliasStat["passivemasterymeleedmg"] = 343; -NTIPAliasStat["passivemasterymeleecrit"] = 344; -NTIPAliasStat["passivemasterythrowth"] = 345; -NTIPAliasStat["passivemasterythrowdmg"] = 346; -NTIPAliasStat["passivemasterythrowcrit"] = 347; -NTIPAliasStat["passiveweaponblock"] = 348; -NTIPAliasStat["passivesummonresist"] = 349; -NTIPAliasStat["modifierlistskill"] = 350; -NTIPAliasStat["modifierlistlevel"] = 351; -NTIPAliasStat["lastsenthppct"] = 352; -NTIPAliasStat["sourceunittype"] = 353; -NTIPAliasStat["sourceunitid"] = 354; -NTIPAliasStat["shortparam1"] = 355; -NTIPAliasStat["questitemdifficulty"] = 356; -NTIPAliasStat["passivemagmastery"] = 357; -NTIPAliasStat["passivemagpierce"] = 358; - - -// Doesnt really exists, but is calculated in getStatEx -NTIPAliasStat["allres"] = 555; diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 2f0cc62e4..f5c2ed791 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1,1084 +1,791 @@ /** * @filename OOG.js * @author kolton, D3STROY3R, theBGuy -* @desc handle out of game operations like creating characters/accounts, maintaining profile datafiles, d2bot# logging etc. +* @desc handle out of game operations, interacting with controls, location actions, and starter settings * */ -!isIncluded("Polyfill.js") && include("Polyfill.js"); -!isIncluded("common/Util.js") && include("common/Util.js"); - -let sdk = require("./modules/sdk"); -let Controls = require("./modules/Control"); - -const D2Bot = { - handle: 0, - - init: function () { - let handle = DataFile.getStats().handle; - if (handle) { - this.handle = handle; - } +!isIncluded("Polyfill.js") && include("Polyfill.js"); +includeIfNotIncluded("oog/D2Bot.js"); // required - return this.handle; - }, - - sendMessage: function (handle, mode, msg) { - sendCopyData(null, handle, mode, msg); - }, - - printToConsole: function (msg, color, tooltip, trigger) { - let printObj = { - msg: msg, - color: color || 0, - tooltip: tooltip || "", - trigger: trigger || "" - }; - - let obj = { - profile: me.profile, - func: "printToConsole", - args: [JSON.stringify(printObj)] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - printToItemLog: function (itemObj) { - let obj = { - profile: me.profile, - func: "printToItemLog", - args: [JSON.stringify(itemObj)] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - uploadItem: function (itemObj) { - let obj = { - profile: me.profile, - func: "uploadItem", - args: [JSON.stringify(itemObj)] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - writeToFile: function (filename, msg) { - let obj = { - profile: me.profile, - func: "writeToFile", - args: [filename, msg] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - postToIRC: function (ircProfile, recepient, msg) { - let obj = { - profile: me.profile, - func: "postToIRC", - args: [ircProfile, recepient, msg] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - ircEvent: function (mode) { - let obj = { - profile: me.profile, - func: "ircEvent", - args: [mode ? "true" : "false"] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - notify: function (msg) { - let obj = { - profile: me.profile, - func: "notify", - args: [msg] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - saveItem: function (itemObj) { - let obj = { - profile: me.profile, - func: "saveItem", - args: [JSON.stringify(itemObj)] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - updateStatus: function (msg) { - let obj = { - profile: me.profile, - func: "updateStatus", - args: [msg] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - updateRuns: function () { - let obj = { - profile: me.profile, - func: "updateRuns", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - updateChickens: function () { - let obj = { - profile: me.profile, - func: "updateChickens", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - updateDeaths: function () { - let obj = { - profile: me.profile, - func: "updateDeaths", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - requestGameInfo: function () { - let obj = { - profile: me.profile, - func: "requestGameInfo", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - restart: function (keySwap) { - let obj = { - profile: me.profile, - func: "restartProfile", - args: arguments.length > 0 ? [me.profile, keySwap] : [me.profile] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - CDKeyInUse: function () { - let obj = { - profile: me.profile, - func: "CDKeyInUse", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - CDKeyDisabled: function () { - let obj = { - profile: me.profile, - func: "CDKeyDisabled", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - CDKeyRD: function () { - let obj = { - profile: me.profile, - func: "CDKeyRD", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - stop: function (profile, release) { - !profile && (profile = me.profile); - - let obj = { - profile: me.profile, - func: "stop", - args: [profile, release ? "True" : "False"] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - start: function (profile) { - let obj = { - profile: me.profile, - func: "start", - args: [profile] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - startSchedule: function (profile) { - let obj = { - profile: me.profile, - func: "startSchedule", - args: [profile] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - stopSchedule: function (profile) { - let obj = { - profile: me.profile, - func: "stopSchedule", - args: [profile] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - updateCount: function () { - let obj = { - profile: me.profile, - func: "updateCount", - args: ["1"] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - shoutGlobal: function (msg, mode) { - let obj = { - profile: me.profile, - func: "shoutGlobal", - args: [msg, mode] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - heartBeat: function () { - let obj = { - profile: me.profile, - func: "heartBeat", - args: [] - }; - - //print("ÿc1Heart beat " + this.handle); - sendCopyData(null, this.handle, 0xbbbb, JSON.stringify(obj)); - }, - - sendWinMsg: function (wparam, lparam) { - let obj = { - profile: me.profile, - func: "winmsg", - args: [wparam, lparam] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - ingame: function () { - this.sendWinMsg(0x0086, 0x0000); - this.sendWinMsg(0x0006, 0x0002); - this.sendWinMsg(0x001c, 0x0000); - }, - - // Profile to profile communication - joinMe: function (profile, gameName, gameCount, gamePass, isUp) { - let obj = { - gameName: gameName + gameCount, - gamePass: gamePass, - inGame: isUp === "yes" - }; - - sendCopyData(null, profile, 1, JSON.stringify(obj)); - }, - - requestGame: function (profile) { - let obj = { - profile: me.profile - }; - - sendCopyData(null, profile, 3, JSON.stringify(obj)); - }, - - getProfile: function () { - let obj = { - profile: me.profile, - func: "getProfile", - args: [] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - setProfile: function (account, password, character, difficulty, realm, infoTag, gamePath) { - let obj = { - profile: me.profile, - func: "setProfile", - args: [account, password, character, difficulty, realm, infoTag, gamePath] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - setTag: function (tag) { - let obj = { - profile: me.profile, - func: "setTag", - args: [JSON.stringify(tag)] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - // Store info in d2bot# cache - store: function (info) { - this.remove(); - - let obj = { - profile: me.profile, - func: "store", - args: [me.profile, info] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - // Get info from d2bot# cache - retrieve: function () { - let obj = { - profile: me.profile, - func: "retrieve", - args: [me.profile] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); - }, - - // Delete info from d2bot# cache - remove: function () { - let obj = { - profile: me.profile, - func: "delete", - args: [me.profile] - }; - - sendCopyData(null, this.handle, 0, JSON.stringify(obj)); +/** + * ControlAction and Starter are very closely related, how should this be handled? + * Starter can probably be cleaned up, maybe taking out LocationEvents as that is mostly what + * interfaces with ControlAction + */ + +(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(["require", "./modules/Control"], factory); + } else { + Object.assign(root, factory()); } -}; - -const DataFile = { - create: function () { - let obj = { - runs: 0, - experience: 0, - deaths: 0, - lastArea: "", - gold: 0, - level: 0, - name: "", - gameName: "", - ingameTick: 0, - handle: 0, - nextGame: "" - }; - - let string = JSON.stringify(obj); - - Misc.fileAction("data/" + me.profile + ".json", 1, string); - - return obj; - }, - - getObj: function () { - !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); - - let obj; - let string = Misc.fileAction("data/" + me.profile + ".json", 0); - - try { - obj = JSON.parse(string); - } catch (e) { - // If we failed, file might be corrupted, so create a new one - obj = this.create(); - } - - if (obj) { - return obj; - } - - print("Error reading DataFile. Using null values."); - - return {runs: 0, experience: 0, lastArea: "", gold: 0, level: 0, name: "", gameName: "", ingameTick: 0, handle: 0, nextGame: ""}; - }, - - getStats: function () { - let obj = this.getObj(); - - return Misc.clone(obj); - }, - - updateStats: function (arg, value) { - while (me.ingame && !me.gameReady) { - delay(100); - } - - let statArr = []; - - typeof arg === "object" && (statArr = arg.slice()); - typeof arg === "string" && statArr.push(arg); - - let obj = this.getObj(); +}(this, function() { + const Controls = require("./modules/Control"); - for (let i = 0; i < statArr.length; i += 1) { - switch (statArr[i]) { - case "experience": - obj.experience = me.getStat(sdk.stats.Experience); - obj.level = me.getStat(sdk.stats.Level); - - break; - case "lastArea": - if (obj.lastArea === Pather.getAreaName(me.area)) { - return; - } + const ControlAction = { + mutedKey: false, + realms: { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }, - obj.lastArea = Pather.getAreaName(me.area); + timeoutDelay: function (text, time, stopfunc, arg) { + let currTime = 0; + let endTime = getTickCount() + time; - break; - case "gold": - if (!me.gameReady) { + while (getTickCount() < endTime) { + if (typeof stopfunc === "function" && stopfunc(arg)) { break; } - obj.gold = me.getStat(sdk.stats.Gold) + me.getStat(sdk.stats.GoldBank); + if (currTime !== Math.floor((endTime - getTickCount()) / 1000)) { + currTime = Math.floor((endTime - getTickCount()) / 1000); - break; - case "name": - obj.name = me.name; + D2Bot.updateStatus(text + " (" + Math.max(currTime, 0) + "s)"); + } - break; - case "ingameTick": - obj.ingameTick = getTickCount(); + delay(10); + } + }, - break; - case "deaths": - obj.deaths = (obj.deaths || 0) + 1; + click: function (type, x, y, xsize, ysize, targetx, targety) { + let control = getControl(type, x, y, xsize, ysize); - break; - default: - obj[statArr[i]] = value; + if (!control) { + print("control not found " + type + " " + x + " " + y + " " + xsize + " " + ysize + " location " + getLocation()); - break; + return false; } - } - let string = JSON.stringify(obj); + control.click(targetx, targety); - Misc.fileAction("data/" + me.profile + ".json", 1, string); - } -}; + return true; + }, -const ControlAction = { - mutedKey: false, + setText: function (type, x, y, xsize, ysize, text) { + if (!text) return false; - timeoutDelay: function (text, time, stopfunc, arg) { - let currTime = 0; - let endTime = getTickCount() + time; + let control = getControl(type, x, y, xsize, ysize); + if (!control) return false; - while (getTickCount() < endTime) { - if (typeof stopfunc === "function" && stopfunc(arg)) { - break; - } + let currText = control.text; + if (currText && currText === text) return true; - if (currTime !== Math.floor((endTime - getTickCount()) / 1000)) { - currTime = Math.floor((endTime - getTickCount()) / 1000); + currText = control.getText(); - D2Bot.updateStatus(text + " (" + Math.max(currTime, 0) + "s)"); + if (currText && ((typeof currText === "string" && currText === text) || (typeof currText === "object" && currText.includes(text)))) { + return true; } - delay(10); - } - }, + control.setText(text); - click: function (type, x, y, xsize, ysize, targetx, targety) { - let control = getControl(type, x, y, xsize, ysize); + return true; + }, - if (!control) { - print("control not found " + type + " " + x + " " + y + " " + xsize + " " + ysize + " location " + getLocation()); + getText: function (type, x, y, xsize, ysize) { + let control = getControl(type, x, y, xsize, ysize); - return false; - } + return (!!control ? control.getText() : false); + }, - control.click(targetx, targety); + joinChannel: function (channel) { + me.blockMouse = true; - return true; - }, + let tick; + let rval = false; + let timeout = 5000; - setText: function (type, x, y, xsize, ysize, text) { - if (!text) return false; + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.Lobby: + Controls.LobbyEnterChat.click(); - let control = getControl(type, x, y, xsize, ysize); - if (!control) return false; + break; + case sdk.game.locations.LobbyChat: + let currChan = Controls.LobbyChannelName.getText(); // returns array + + if (currChan) { + for (let i = 0; i < currChan.length; i += 1) { + if (currChan[i].split(" (") && String.isEqual(currChan[i].split(" (")[0], channel)) { + rval = true; - let currText = control.text; - if (currText && currText === text) return true; + break MainLoop; + } + } + } - currText = control.getText(); + !tick && Controls.LobbyChannel.click() && (tick = getTickCount()); - if (currText && ((typeof currText === "string" && currText === text) || (typeof currText === "object" && currText.includes(text)))) { - return true; - } + break; + case sdk.game.locations.ChannelList: // Channel + Controls.LobbyChannelText.setText(channel); + Controls.LobbyChannelOk.click(); - control.setText(text); + break; + } - return true; - }, + if (getTickCount() - tick >= timeout) { + break; + } - getText: function (type, x, y, xsize, ysize) { - let control = getControl(type, x, y, xsize, ysize); + delay(100); + } - return (!!control ? control.getText() : false); - }, + me.blockMouse = false; - joinChannel: function (channel) { - me.blockMouse = true; + return rval; + }, - let tick; - let rval = false; - let timeout = 5000; + createGame: function (name, pass, diff, delay) { + Controls.CreateGameName.setText(name); + Controls.CreateGamePass.setText(pass); - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.Lobby: - Controls.LobbyEnterChat.click(); + switch (diff) { + case "Normal": + Controls.Normal.click(); break; - case sdk.game.locations.LobbyChat: - let currChan = Controls.LobbyChannelName.getText(); // returns array + case "Nightmare": + Controls.Nightmare.click(); - if (currChan) { - for (let i = 0; i < currChan.length; i += 1) { - if (currChan[i].split(" (") && currChan[i].split(" (")[0].toLowerCase() === channel.toLowerCase()) { - rval = true; + break; + case "Highest": + if (Controls.Hell.disabled !== 4 && Controls.Hell.click()) { + break; + } - break MainLoop; - } - } + if (Controls.Nightmare.disabled !== 4 && Controls.Nightmare.click()) { + break; } - !tick && Controls.LobbyChannel.click() && (tick = getTickCount()); + Controls.Normal.click(); break; - case sdk.game.locations.ChannelList: // Channel - Controls.LobbyChannelText.setText(channel); - Controls.LobbyChannelOk.click(); + default: + Controls.Hell.click(); break; } - if (getTickCount() - tick >= timeout) { - break; + !!delay && this.timeoutDelay("Make Game Delay", delay); + + if (Starter.chanInfo.announce) { + Starter.sayMsg("Next game is " + name + (pass === "" ? "" : "//" + pass)); } - delay(100); - } + me.blockMouse = true; - me.blockMouse = false; + print("Creating Game: " + name); + Controls.CreateGame.click(); - return rval; - }, + me.blockMouse = false; + }, - createGame: function (name, pass, diff, delay) { - Controls.CreateGameName.setText(name); - Controls.CreateGamePass.setText(pass); + clickRealm: function (realm) { + if (realm === undefined || typeof realm !== "number" || realm < 0 || realm > 3) { + throw new Error("clickRealm: Invalid realm!"); + } - switch (diff) { - case "Normal": - Controls.Normal.click(); + let retry = 0; - break; - case "Nightmare": - Controls.Nightmare.click(); + me.blockMouse = true; - break; - case "Highest": - if (Controls.Hell.disabled !== 4 && Controls.Hell.click()) { - break; - } + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + let control = Controls.Gateway.control; + if (!control) { + if (retry > 3) return false; + retry++; - if (Controls.Nightmare.disabled !== 4 && Controls.Nightmare.click()) { - break; - } + break; + } - Controls.Normal.click(); + let gateText = getLocaleString(sdk.locale.text.Gateway); + let currentRealm = (() => { + switch (control.text.split(gateText.substring(0, gateText.length - 2))[1]) { + case "U.S. WEST": + return 0; + case "ASIA": + return 2; + case "EUROPE": + return 3; + case "U.S. EAST": + default: + return 1; + } + })(); - break; - default: - Controls.Hell.click(); + if (currentRealm === realm) { + break MainLoop; + } - break; - } + Controls.Gateway.click(); - !!delay && this.timeoutDelay("Make Game Delay", delay); + break; + case sdk.game.locations.GatewaySelect: + this.click(4, 257, 500, 292, 160, 403, 350 + realm * 25); + Controls.GatewayOk.click(); - if (Starter.chanInfo.announce) { - Starter.sayMsg("Next game is " + name + (pass === "" ? "" : "//" + pass)); - } + break; + } - me.blockMouse = true; + delay(500); + } - print("Creating Game: " + name); - Controls.CreateGame.click(); + me.blockMouse = false; - me.blockMouse = false; - }, + return true; + }, - clickRealm: function (realm) { - if (realm === undefined || typeof realm !== "number" || realm < 0 || realm > 3) { - throw new Error("clickRealm: Invalid realm!"); - } + loginAccount: function (info) { + me.blockMouse = true; - let currentRealm, retry = 0; + let locTick; + let tick = getTickCount(); - me.blockMouse = true; + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.PreSplash: + break; + case sdk.game.locations.MainMenu: + info.realm && ControlAction.clickRealm(this.realms[info.realm]); + Controls.BattleNet.click(); - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - let control = Controls.Gateway.control; - if (!control) { - if (retry > 3) return false; - retry++; + 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; - switch (control.text.split(getLocaleString(sdk.locale.text.Gateway).substring(0, getLocaleString(sdk.locale.text.Gateway).length - 2))[1]) { - case "U.S. EAST": - currentRealm = 1; + return false; + case sdk.game.locations.CharSelect: + break MainLoop; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); break; - case "U.S. WEST": - currentRealm = 0; - + case sdk.game.locations.CharSelectPleaseWait: + case sdk.game.locations.MainMenuConnecting: + case sdk.game.locations.CharSelectConnecting: break; - case "ASIA": - currentRealm = 2; + case sdk.game.locations.CharSelectNoChars: + // make sure we're not on connecting screen + locTick = getTickCount(); - break; - case "EUROPE": - currentRealm = 3; + while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { + delay(25); + } - break; + if (getLocation() === sdk.game.locations.CharSelectConnecting) { + break; + } + + break MainLoop; // break if we're sure we're on empty char screen + default: + print(getLocation()); + + me.blockMouse = false; + + return false; } - if (currentRealm === realm) { - break MainLoop; + if (getTickCount() - tick >= 20000) { + return false; } - Controls.Gateway.click(); + delay(100); + } - break; - case sdk.game.locations.GatewaySelect: - this.click(4, 257, 500, 292, 160, 403, 350 + realm * 25); - Controls.GatewayOk.click(); + delay(1000); - break; - } + me.blockMouse = false; - delay(500); - } + return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; + }, - me.blockMouse = false; + setEmail: function (email = "", domain = "@email.com") { + if (getLocation() !== sdk.game.locations.RegisterEmail) return false; + if (!email || !email.length) { + email = Starter.randomString(null, true); + } + + while (getLocation() !== sdk.game.locations.CharSelect) { + switch (getLocation()) { + case sdk.game.locations.RegisterEmail: + if (Controls.EmailSetEmail.setText(email + domain) && Controls.EmailVerifyEmail.setText(email + domain)) { + Controls.EmailRegister.click(); + delay(100); + } + + break; + case sdk.game.locations.LoginError: + // todo test what conditions get here other than email not matching + D2Bot.printToConsole("Failed to set email"); + Controls.LoginErrorOk.click(); + + return false; + case sdk.game.locations.CharSelectNoChars: + // fresh acc + return true; + } + } - return true; - }, + return true; + }, - loginAccount: function (info) { - me.blockMouse = true; + makeAccount: function (info) { + me.blockMouse = true; + + let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; + + // cycle until in empty char screen + MainLoop: + while (getLocation() !== sdk.game.locations.CharSelectNoChars) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + ControlAction.clickRealm(this.realms[info.realm]); + if (openBnet) { + Controls.OtherMultiplayer.click() && Controls.OpenBattleNet.click(); + } else { + Controls.BattleNet.click(); + } - let locTick; - let realms = { - "uswest": 0, - "useast": 1, - "asia": 2, - "europe": 3 - }; + break; + case sdk.game.locations.Login: + Controls.CreateNewAccount.click(); - let tick = getTickCount(); + break; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.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.CharacterCreate: + Controls.CharSelectExit.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.TermsOfUse: + Controls.TermsOfUseAgree.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.CreateNewAccount: + Controls.CreateNewAccountName.setText(info.account); + Controls.CreateNewAccountPassword.setText(info.password); + Controls.CreateNewAccountConfirmPassword.setText(info.password); + Controls.CreateNewAccountOk.click(); - return false; - case sdk.game.locations.CharSelect: - break MainLoop; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); + break; + case sdk.game.locations.PleaseRead: + Controls.PleaseReadOk.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.RegisterEmail: + Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); - while (getTickCount() - locTick < 3000 && getLocation() === sdk.game.locations.CharSelectNoChars) { - delay(25); - } + break; + case sdk.game.locations.CharSelect: + if (openBnet) { + break MainLoop; + } - if (getLocation() === sdk.game.locations.CharSelectConnecting) { + break; + default: break; } - break MainLoop; // break if we're sure we're on empty char screen - default: - print(getLocation()); + delay(100); + } - me.blockMouse = false; + me.blockMouse = false; - return false; + return true; + }, + + scrollDown: function () { + me.blockMouse = true; + for (let i = 0; i < 4; i++) { + sendKey(sdk.keys.code.DownArrow); } + me.blockMouse = false; + }, - if (getTickCount() - tick >= 20000) { - return false; + findCharacter: function (info) { + let count = 0; + let tick = getTickCount(); + + while (getLocation() !== sdk.game.locations.CharSelect) { + if (getTickCount() - tick >= 5000) { + break; + } + + delay(25); } - delay(100); - } + // start from beginning of the char list + sendKey(sdk.keys.code.Home); - delay(1000); + while (getLocation() === sdk.game.locations.CharSelect && count < 24) { + let control = Controls.CharSelectCharInfo0.control; - me.blockMouse = false; + if (control) { + do { + let text = control.getText(); - return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; - }, + if (text instanceof Array && typeof text[1] === "string") { + count++; - setEmail: function (email = "", domain = "@email.com") { - if (getLocation() !== sdk.game.locations.RegisterEmail) return false; - if (!email || !email.length) { - email = Starter.randomString(null, true); - } - - while (getLocation() !== sdk.game.locations.CharSelect) { - switch (getLocation()) { - case sdk.game.locations.RegisterEmail: - if (Controls.EmailSetEmail.setText(email + domain) && Controls.EmailVerifyEmail.setText(email + domain)) { - Controls.EmailRegister.click(); - delay(100); + if (String.isEqual(text[1], info.charName)) { + return true; + } + } + } while (count < 24 && control.getNext()); } - break; - case sdk.game.locations.LoginError: - // todo test what conditions get here other than email not matching - D2Bot.printToConsole("Failed to set email"); - Controls.LoginErrorOk.click(); - - return false; - case sdk.game.locations.CharSelectNoChars: - // fresh acc - return true; - } - } - - return true; - }, - - makeAccount: function (info) { - me.blockMouse = true; - - let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; - let realms = { - "uswest": 0, - "useast": 1, - "asia": 2, - "europe": 3 - }; - // cycle until in empty char screen - MainLoop: - while (getLocation() !== sdk.game.locations.CharSelectNoChars) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - ControlAction.clickRealm(realms[info.realm]); - if (openBnet) { - Controls.OtherMultiplayer.click() && Controls.OpenBattleNet.click(); + // check for additional characters up to 24 + if (count === 8 || count === 16) { + Controls.CharSelectChar6.click() && this.scrollDown(); } else { - Controls.BattleNet.click(); + // no further check necessary + break; } + } - break; - case sdk.game.locations.Login: - Controls.CreateNewAccount.click(); - - break; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); + return false; + }, - break; - case sdk.game.locations.CharacterCreate: - Controls.CharSelectExit.click(); + // get all characters + getCharacters: function () { + let count = 0; + let list = []; - break; - case sdk.game.locations.TermsOfUse: - Controls.TermsOfUseAgree.click(); + // start from beginning of the char list + sendKey(sdk.keys.code.Home); - break; - case sdk.game.locations.CreateNewAccount: - Controls.CreateNewAccountName.setText(info.account); - Controls.CreateNewAccountPassword.setText(info.password); - Controls.CreateNewAccountConfirmPassword.setText(info.password); - Controls.CreateNewAccountOk.click(); + while (getLocation() === sdk.game.locations.CharSelect && count < 24) { + let control = Controls.CharSelectCharInfo0.control; - break; - case sdk.game.locations.PleaseRead: - Controls.PleaseReadOk.click(); + if (control) { + do { + let text = control.getText(); - break; - case sdk.game.locations.RegisterEmail: - Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + if (text instanceof Array && typeof text[1] === "string") { + count++; - break; - case sdk.game.locations.CharSelect: - if (openBnet) { - break MainLoop; + if (list.indexOf(text[1]) === -1) { + list.push(text[1]); + } + } + } while (count < 24 && control.getNext()); } - break; - default: - break; + // check for additional characters up to 24 + if (count === 8 || count === 16) { + Controls.CharSelectChar6.click() && this.scrollDown(); + } else { + // no further check necessary + break; + } } - delay(100); - } + // back to beginning of the char list + sendKey(sdk.keys.code.Home); - me.blockMouse = false; + return list; + }, - return true; - }, + getPermStatus: function (info) { + let count = 0; + let tick = getTickCount(); + let expireStr = getLocaleString(sdk.locale.text.ExpiresIn); + expireStr = expireStr.slice(0, expireStr.indexOf("%")).trim(); - findCharacter: function (info) { - let count = 0; - let tick = getTickCount(); + while (getLocation() !== sdk.game.locations.CharSelect) { + if (getTickCount() - tick >= 5000) { + break; + } - while (getLocation() !== sdk.game.locations.CharSelect) { - if (getTickCount() - tick >= 5000) { - break; + delay(25); } - delay(25); - } - - // start from beginning of the char list - sendKey(0x24); + // start from beginning of the char list + sendKey(sdk.keys.code.Home); - while (getLocation() === sdk.game.locations.CharSelect && count < 24) { - let control = Controls.CharSelectCharInfo0.control; + while (getLocation() === sdk.game.locations.CharSelect && count < 24) { + 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") { - count++; + 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 !text.some(el => el.includes(expireStr)); + } } - } - } while (count < 24 && control.getNext()); - } - - // check for additional characters up to 24 - if (count === 8 || count === 16) { - if (Controls.CharSelectChar6.click()) { - me.blockMouse = true; - - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); + } while (count < 24 && control.getNext()); + } - me.blockMouse = false; + // check for additional characters up to 24 + if (count === 8 || count === 16) { + Controls.CharSelectChar6.click() && this.scrollDown(); + } else { + // no further check necessary + break; } - } else { - // no further check necessary - break; } - } - return false; - }, + return false; + }, - // get all characters - getCharacters: function () { - let count = 0; - let list = []; + // get character position + getPosition: function () { + let position = 0; - // start from beginning of the char list - sendKey(0x24); + if (getLocation() === sdk.game.locations.CharSelect) { + let control = Controls.CharSelectCharInfo0.control; + + if (control) { + do { + let text = control.getText(); + + if (text instanceof Array && typeof text[1] === "string") { + position += 1; + } + } while (control.getNext()); + } + } - while (getLocation() === sdk.game.locations.CharSelect && count < 24) { - let control = Controls.CharSelectCharInfo0.control; + return position; + }, - if (control) { - do { - let text = control.getText(); + loginCharacter: function (info, startFromTop = true) { + me.blockMouse = true; - if (text instanceof Array && typeof text[1] === "string") { - count++; + let count = 0; - if (list.indexOf(text[1]) === -1) { - list.push(text[1]); - } + // 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.CharSelect: + let control = Controls.CharSelectCharInfo0.control; + + if (control) { + do { + let text = control.getText(); + + if (text instanceof Array && typeof text[1] === "string") { + count++; + + if (String.isEqual(text[1], info.charName)) { + control.click(); + Controls.CreateNewAccountOk.click(); + me.blockMouse = false; + + if (getLocation() === sdk.game.locations.SelectDifficultySP) { + try { + login(info.profile); + } catch (err) { + break MainLoop; + } + + if (me.ingame) { + return true; + } + } + + return true; + } + } + } while (control.getNext()); } - } while (count < 24 && control.getNext()); - } - // check for additional characters up to 24 - if (count === 8 || count === 16) { - if (Controls.CharSelectChar6.click()) { - me.blockMouse = true; - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); + // check for additional characters up to 24 + if (count === 8 || count === 16) { + Controls.CharSelectChar6.click() && this.scrollDown(); + } else { + // no further check necessary + break MainLoop; + } - me.blockMouse = false; + break; + case sdk.game.locations.CharSelectNoChars: + Controls.CharSelectExit.click(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.OkCenteredErrorPopUp: + break MainLoop; + default: + break; } - } else { - // no further check necessary - break; + + delay(100); } - } - // back to beginning of the char list - sendKey(0x24); + me.blockMouse = false; - return list; - }, + return false; + }, - getPermStatus: function (info) { - let count = 0; - let tick = getTickCount(); - let expireStr = getLocaleString(sdk.locale.text.ExpiresIn); - expireStr = expireStr.slice(0, expireStr.indexOf("%")).trim(); + makeCharacter: function (info) { + me.blockMouse = true; + !info.charClass && (info.charClass = "barbarian"); + + if (info.charName.match(/\d+/g)) { + console.warn("Invalid character name, cannot contain numbers"); - while (getLocation() !== sdk.game.locations.CharSelect) { - if (getTickCount() - tick >= 5000) { - break; + return false; } - delay(25); - } + let clickCoords = []; + + // cycle until in lobby + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + case sdk.game.locations.CharSelectNoChars: + // Create Character greyed out + if (Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { + me.blockMouse = false; - // start from beginning of the char list - sendKey(0x24); + return false; + } + + Controls.CharSelectCreate.click(); + + 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 (getLocation() === sdk.game.locations.CharSelect && count < 24) { - let control = Controls.CharSelectCharInfo0.control; + // coords: + // zon: 100, 280 + // barb: 400, 280 + // necro: 300, 290 + // sin: 200, 280 + // paladin: 521 260 + // sorc: 620, 270 + // druid: 700, 280 - if (control) { - do { - let text = control.getText(); + getControl().click(clickCoords[0], clickCoords[1]); + delay(500); - if (text instanceof Array && typeof text[1] === "string") { - count++; + break; + case sdk.game.locations.NewCharSelected: + if (Controls.CharCreateHCWarningOk.control) { + Controls.CharCreateHCWarningOk.click(); + } else { + Controls.CharCreateCharName.setText(info.charName); + + if (!info.expansion) { + switch (info.charClass) { + case "druid": + case "assassin": + D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); + D2Bot.stop(); + + break; + default: + break; + } - if (text[1].toLowerCase() === info.charName.toLowerCase()) { - return !text.some(el => el.includes(expireStr)); + Controls.CharCreateExpansion.click(); } - } - } while (count < 24 && control.getNext()); - } - // check for additional characters up to 24 - if (count === 8 || count === 16) { - if (Controls.CharSelectChar6.click()) { - me.blockMouse = true; + !info.ladder && Controls.CharCreateLadder.click(); + info.hardcore && Controls.CharCreateHardcore.click(); + + Controls.CreateNewAccountOk.click(); + } - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); + break; + case sdk.game.locations.OkCenteredErrorPopUp: + // char name exists (text box 4, 268, 320, 264, 120) + Controls.OkCentered.click(); + Controls.CharSelectExit.click(); me.blockMouse = false; + + return false; + default: + break; + } + + // Singleplayer loop break fix. + if (me.ingame) { + break; } - } else { - // no further check necessary - break; - } - } - return false; - }, + delay(500); + } - // get character position - getPosition: function () { - let position = 0; + me.blockMouse = false; - if (getLocation() === sdk.game.locations.CharSelect) { - let control = Controls.CharSelectCharInfo0.control; + return true; + }, - if (control) { - do { - let text = control.getText(); + // Test version - modified core only + getGameList: function () { + let text = Controls.JoinGameList.getText(); - if (text instanceof Array && typeof text[1] === "string") { - position += 1; - } - } while (control.getNext()); - } - } + if (text) { + let gameList = []; - return position; - }, + for (let i = 0; i < text.length; i += 1) { + gameList.push({ + gameName: text[i][0], + players: text[i][1] + }); + } - loginCharacter: function (info, startFromTop = true) { - me.blockMouse = true; + return gameList; + } - let count = 0; + return false; + }, - // start from beginning of the char list - startFromTop && sendKey(0x24); + deleteCharacter: function (info) { + me.blockMouse = true; - MainLoop: - // cycle until in lobby or in game - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: + // start from beginning of the char list + sendKey(sdk.keys.code.Home); + + // cycle until in lobby + while (getLocation() === sdk.game.locations.CharSelect) { + let count = 0; let control = Controls.CharSelectCharInfo0.control; if (control) { @@ -1088,23 +795,16 @@ const ControlAction = { if (text instanceof Array && typeof text[1] === "string") { count++; - if (text[1].toLowerCase() === info.charName.toLowerCase()) { + if (String.isEqual(text[1], info.charName)) { + print("delete character " + info.charName); + control.click(); - Controls.CreateNewAccountOk.click(); + Controls.CharSelectDelete.click(); + delay(500); + Controls.CharDeleteYes.click(); + delay(500); me.blockMouse = false; - - if (getLocation() === sdk.game.locations.SelectDifficultySP) { - try { - login(info.profile); - } catch (err) { - break MainLoop; - } - - if (me.ingame) { - return true; - } - } - + return true; } } @@ -1113,1043 +813,779 @@ const ControlAction = { // check for additional characters up to 24 if (count === 8 || count === 16) { - if (Controls.CharSelectChar6.click()) { - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); - } + Controls.CharSelectChar6.click() && this.scrollDown(); } else { // no further check necessary - break MainLoop; + break; } - 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; - - return false; - }, - - makeCharacter: function (info) { - me.blockMouse = true; - !info.charClass && (info.charClass = "barbarian"); - - if (info.charName.match(/\d+/g)) { - console.warn("Invalid character name, cannot contain numbers"); + me.blockMouse = false; return false; - } + }, - let clickCoords = []; + getQueueTime: function() { + // You are in line to create a game.,Try joining a game to avoid waiting.,,Your position in line is: ÿc02912 + const text = Controls.CreateGameInLine.getText(); + if (text && text.indexOf(getLocaleString(sdk.locale.text.YourPositionInLineIs)) > -1) { + const result = /ÿc0(\d*)/gm.exec(text); + if (result && typeof result[1] === "string") { + return parseInt(result[1]) || 0; + } + } - // cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - case sdk.game.locations.CharSelectNoChars: - // Create Character greyed out - if (Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { - me.blockMouse = false; + return 0; // You're in line 0, aka no queue + }, - return false; - } + loginOtherMultiplayer: function () { + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + if (Controls.CharSelectCurrentRealm.control) { + console.log("Not in single player character select screen"); + Controls.CharSelectExit.click(); - Controls.CharSelectCreate.click(); + break; + } - break; - case sdk.game.locations.CharacterCreate: - switch (info.charClass) { - case "barbarian": - clickCoords = [400, 280]; + Starter.LocationEvents.login(false); break; - case "amazon": - clickCoords = [100, 280]; - + case sdk.game.locations.SelectDifficultySP: + Starter.LocationEvents.selectDifficultySP(); + break; - case "necromancer": - clickCoords = [300, 290]; + case sdk.game.locations.SplashScreen: + ControlAction.click(); break; - case "sorceress": - clickCoords = [620, 270]; + case sdk.game.locations.MainMenu: + if (Profile().type === sdk.game.profiletype.OpenBattlenet) { + // check we are on the correct gateway + let realms = {"west": 0, "east": 1, "asia": 2, "europe": 3}; + ControlAction.clickRealm(realms[Profile().gateway.toLowerCase()]); + try { + login(me.profile); + } catch (e) { + print(e); + } - break; - case "assassin": - clickCoords = [200, 280]; + break; + } + + Controls.OtherMultiplayer.click(); break; - case "druid": - clickCoords = [700, 280]; + case sdk.game.locations.OtherMultiplayer: + Starter.LocationEvents.otherMultiplayerSelect(); break; - case "paladin": - clickCoords = [521, 260]; + case sdk.game.locations.TcpIp: + // handle this in otherMultiplayerSelect + // not sure how to handle enter ip though, should that be left to the starter to decide? + Controls.TcpIpCancel.click(); break; - } - - // coords: - // zon: 100, 280 - // barb: 400, 280 - // necro: 300, 290 - // sin: 200, 280 - // paladin: 521 260 - // sorc: 620, 270 - // druid: 700, 280 + case sdk.game.locations.TcpIpEnterIp: + break MainLoop; + case sdk.game.locations.Login: + login(me.profile); - getControl().click(clickCoords[0], clickCoords[1]); - delay(500); + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); - break; - case sdk.game.locations.NewCharSelected: - if (Controls.CharCreateHCWarningOk.control) { - Controls.CharCreateHCWarningOk.click(); - } else { - Controls.CharCreateCharName.setText(info.charName); + break; + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + D2Bot.updateStatus("Lobby"); - if (!info.expansion) { - switch (info.charClass) { - case "druid": - case "assassin": - D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); - D2Bot.stop(); + if (me.charname !== Starter.profileInfo.charName) { + Controls.LobbyQuit.click(); + + break; + } - break; - default: - break; - } + me.blockKeys = false; + !Starter.firstLogin && (Starter.firstLogin = true); - Controls.CharCreateExpansion.click(); + break MainLoop; + default: + if (me.ingame) { + break MainLoop; } - !info.ladder && Controls.CharCreateLadder.click(); - info.hardcore && Controls.CharCreateHardcore.click(); - - Controls.CreateNewAccountOk.click(); + break; } - - break; - case sdk.game.locations.OkCenteredErrorPopUp: - // char name exists (text box 4, 268, 320, 264, 120) - Controls.OkCentered.click(); - Controls.CharSelectExit.click(); - - me.blockMouse = false; - - return false; - default: - break; } - - // Singleplayer loop break fix. - if (me.ingame) { - break; - } - - delay(500); + + // handling Enter Ip inside entry for now so that location === sucess + return (me.ingame || getLocation() === [sdk.game.locations.TcpIpEnterIp]); } + }; + + const Starter = { + Config: require("./starter/StarterConfig"), + AdvancedConfig: require("./starter/AdvancedConfig"), + useChat: false, + pingQuit: false, + inGame: false, + firstLogin: true, + firstRun: false, + isUp: "no", + loginRetry: 0, + deadCheck: false, + chatActionsDone: false, + gameStart: 0, + gameCount: 0, + lastGameStatus: "ready", + handle: null, + connectFail: false, + connectFailRetry: 0, + makeAccount: false, + channelNotify: false, + chanInfo: { + joinChannel: "", + firstMsg: "", + afterMsg: "", + announce: false + }, + gameInfo: {}, + joinInfo: {}, + profileInfo: {}, + + sayMsg: function (string) { + if (!this.useChat) return; + say(string); + }, - me.blockMouse = false; + timer: function (tick) { + return " (" + new Date(getTickCount() - tick).toISOString().slice(11, -5) + ")"; + }, - return true; - }, + locationTimeout: function (time, location) { + let endtime = getTickCount() + time; - // Test version - modified core only - getGameList: function () { - let text = Controls.JoinGameList.getText(); + while (!me.ingame && getLocation() === location && endtime > getTickCount()) { + delay(500); + } - if (text) { - let gameList = []; + return (getLocation() !== location); + }, - for (let i = 0; i < text.length; i += 1) { - gameList.push({ - gameName: text[i][0], - players: text[i][1] - }); + setNextGame: function (gameInfo = {}) { + let nextGame = (gameInfo.gameName || this.randomString(null, true)); + + if ((this.gameCount + 1 >= Starter.Config.ResetCount) || (nextGame.length + this.gameCount + 1 > 15)) { + nextGame += "1"; + } else { + nextGame += (this.gameCount + 1); } - return gameList; - } + DataFile.updateStats("nextGame", nextGame); + }, - return false; - }, + updateCount: function () { + D2Bot.updateCount(); + delay(1000); + Controls.BattleNet.click(); - deleteCharacter: function (info) { - me.blockMouse = true; + try { + login(me.profile); + } catch (e) { + return; + } - // start from beginning of the char list - sendKey(0x24); - - // cycle until in lobby - while (getLocation() === sdk.game.locations.CharSelect) { - let count = 0; - let control = Controls.CharSelectCharInfo0.control; + delay(1000); + Controls.CharSelectExit.click(); + }, - if (control) { - do { - let text = control.getText(); + scriptMsgEvent: function (msg) { + if (msg && typeof msg !== "string") return; + switch (msg) { + case "mule": + AutoMule.check = true; - if (text instanceof Array && typeof text[1] === "string") { - count++; + break; + case "muleTorch": + AutoMule.torchAnniCheck = 1; - if (text[1].toLowerCase() === info.charName.toLowerCase()) { - print("delete character " + info.charName); - - control.click(); - Controls.CharSelectDelete.click(); - delay(500); - Controls.CharDeleteYes.click(); - delay(500); - me.blockMouse = false; - - return true; - } - } - } while (control.getNext()); - } + break; + case "muleAnni": + AutoMule.torchAnniCheck = 2; - // check for additional characters up to 24 - if (count === 8 || count === 16) { - if (Controls.CharSelectChar6.click()) { - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); - sendKey(0x28); - } - } else { - // no further check necessary break; - } + case "torch": + TorchSystem.check = true; - delay(100); - } + break; + case "crafting": + CraftingSystem.check = true; - me.blockMouse = false; + break; + case "getMuleMode": + if (AutoMule.torchAnniCheck === 2) { + scriptBroadcast("2"); + } else if (AutoMule.torchAnniCheck === 1) { + scriptBroadcast("1"); + } else if (AutoMule.check) { + scriptBroadcast("0"); + } - return false; - }, + break; + case "pingquit": + Starter.pingQuit = true; - getQueueTime: function() { - // You are in line to create a game.,Try joining a game to avoid waiting.,,Your position in line is: ÿc02912 - const text = Controls.CreateGameInLine.getText(); - if (text && text.indexOf(getLocaleString(sdk.locale.text.YourPositionInLineIs)) > -1) { - const result = /ÿc0(\d*)/gm.exec(text); - if (result && typeof result[1] === "string") { - return parseInt(result[1]) || 0; + break; } - } - - return 0; // You're in line 0, aka no queue - }, + }, - loginOtherMultiplayer: function () { - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - if (Controls.CharSelectCurrentRealm.control) { - console.log("Not in single player character select screen"); - Controls.CharSelectExit.click(); + receiveCopyData: function (mode, msg) { + let obj; - break; - } + msg === "Handle" && typeof mode === "number" && (Starter.handle = mode); - Starter.LocationEvents.login(false); + switch (mode) { + case 1: // JoinInfo + obj = JSON.parse(msg); + Object.assign(Starter.joinInfo, obj); break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); - - break; - case sdk.game.locations.SplashScreen: - ControlAction.click(); + case 2: // Game info + print("Recieved Game Info"); + obj = JSON.parse(msg); + Object.assign(Starter.gameInfo, obj); break; - case sdk.game.locations.MainMenu: - if (Profile().type === sdk.game.profiletype.OpenBattlenet) { - // check we are on the correct gateway - let realms = {"west": 0, "east": 1, "asia": 2, "europe": 3}; - ControlAction.clickRealm(realms[Profile().gateway.toLowerCase()]); - try { - login(me.profile); - } catch (e) { - print(e); + case 3: // Game request + // in case someone is using a lightweight entry like blank/map to play manually and these aren't included + if (typeof AutoMule !== "undefined") { + // Don't let others join mule/torch/key/gold drop game + if (AutoMule.inGame || Gambling.inGame || TorchSystem.inGame || CraftingSystem.inGame) { + break; } - - break; } - - Controls.OtherMultiplayer.click(); - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); + if (Object.keys(Starter.gameInfo).length) { + obj = JSON.parse(msg); - break; - case sdk.game.locations.TcpIp: - // handle this in otherMultiplayerSelect - // not sure how to handle enter ip though, should that be left to the starter to decide? - Controls.TcpIpCancel.click(); + if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { + me.gameReady && D2Bot.joinMe(obj.profile, me.gameserverip.toString(), "", "", Starter.isUp); + } else { + if (me.gameReady) { + D2Bot.joinMe(obj.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), Starter.isUp); + } else { + D2Bot.joinMe(obj.profile, Starter.gameInfo.gameName.toLowerCase(), Starter.gameCount, Starter.gameInfo.gamePass.toLowerCase(), Starter.isUp); + } + } + } break; - case sdk.game.locations.TcpIpEnterIp: - break MainLoop; - case sdk.game.locations.Login: - login(me.profile); + case 4: // Heartbeat ping + msg === "pingreq" && sendCopyData(null, me.windowtitle, 4, "pingrep"); break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); + case 61732: // Cached info retreival + msg !== "null" && (Starter.gameInfo.crashInfo = JSON.parse(msg)); break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); - - if (me.charname !== Starter.profileInfo.charName) { - Controls.LobbyQuit.click(); - - break; - } - - me.blockKeys = false; - !Starter.firstLogin && (Starter.firstLogin = true); - - break MainLoop; - default: - if (me.ingame) { - break MainLoop; + case 1638: // getProfile + try { + obj = JSON.parse(msg); + Starter.profileInfo.profile = me.profile; + Starter.profileInfo.account = obj.account; + Starter.profileInfo.charName = obj.Character; + obj.Realm = obj.Realm.toLowerCase(); + Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; + } catch (e) { + print(e); } break; } - } - - // handling Enter Ip inside entry for now so that location === sucess - return (me.ingame || getLocation() === [sdk.game.locations.TcpIpEnterIp]); - } -}; - -const ShitList = { - create: function () { - let obj = { - shitlist: [] - }; + }, - let string = JSON.stringify(obj); + randomString: function (len, useNumbers = false) { + !len && (len = rand(5, 14)); - //FileTools.writeText("shitlist.json", string); - Misc.fileAction("shitlist.json", 1, string); + let rval = ""; + let letters = useNumbers ? "abcdefghijklmnopqrstuvwxyz0123456789" : "abcdefghijklmnopqrstuvwxyz"; - return obj; - }, + for (let i = 0; i < len; i += 1) { + rval += letters[rand(0, letters.length - 1)]; + } - getObj: function () { - let obj; - let string = Misc.fileAction("shitlist.json", 0); - //string = FileTools.readText("shitlist.json"); + return rval; + }, - try { - obj = JSON.parse(string); - } catch (e) { - obj = this.create(); - } + randomNumberString: function (len) { + !len && (len = rand(2, 5)); - if (obj) { - return obj; - } + let rval = ""; + let vals = "0123456789"; - print("Failed to read ShitList. Using null values"); + for (let i = 0; i < len; i += 1) { + rval += vals[rand(0, vals.length - 1)]; + } - return {shitlist: []}; - }, + return rval; + }, - read: function () { - !FileTools.exists("shitlist.json") && this.create(); - - let obj = this.getObj(); + LocationEvents: { + selectDifficultySP: function () { + let diff = (Starter.gameInfo.difficulty || "Highest"); + diff === "Highest" && (diff = "Hell"); // starts from top with fall-through to select highest - return obj.shitlist; - }, + switch (diff) { + case "Hell": + if (Controls.HellSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { + break; + } + // eslint-disable-next-line no-fallthrough + case "Nightmare": + if (Controls.NightmareSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { + break; + } + // eslint-disable-next-line no-fallthrough + case "Normal": + Controls.NormalSP.click(); - add: function (name) { - let obj = this.getObj(); + break; + } + return Starter.locationTimeout(5e3, sdk.game.locations.SelectDifficultySP); + }, + + loginError: function () { + let cdkeyError = false; + let defaultPrint = true; + let string = ""; + let text = (Controls.LoginErrorText.getText() || Controls.LoginInvalidCdKey.getText()); + + if (text) { + for (let i = 0; i < text.length; i += 1) { + string += text[i]; + i !== text.length - 1 && (string += " "); + } - obj.shitlist.push(name); + 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(); - let string = JSON.stringify(obj); + break; + case getLocaleString(sdk.locale.text.InvalidPassword): + D2Bot.printToConsole("Invalid Password"); + ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); + D2Bot.printToConsole("Invalid Password - Restart"); + D2Bot.restart(); - //FileTools.writeText("shitlist.json", string); - Misc.fileAction("shitlist.json", 1, string); - } -}; - -const Starter = { - Config: {}, - useChat: false, - pingQuit: false, - inGame: false, - firstLogin: true, - firstRun: false, - isUp: "no", - loginRetry: 0, - deadCheck: false, - chatActionsDone: false, - gameStart: 0, - gameCount: 0, - lastGameStatus: "ready", - handle: undefined, - connectFail: false, - connectFailRetry: 0, - makeAccount: false, - channelNotify: false, - chanInfo: { - joinChannel: "", - firstMsg: "", - afterMsg: "", - announce: false - }, - gameInfo: {}, - joinInfo: {}, - profileInfo: {}, - - sayMsg: function (string) { - if (!this.useChat) return; - say(string); - }, - - timer: function (tick) { - return " (" + new Date(getTickCount() - tick).toISOString().slice(11, -5) + ")"; - }, - - locationTimeout: function (time, location) { - let endtime = getTickCount() + time; - - while (!me.ingame && getLocation() === location && endtime > getTickCount()) { - delay(500); - } + break; + case getLocaleString(sdk.locale.text.AccountDoesNotExist): + if (!!Starter.Config.MakeAccountOnFailure) { + Starter.makeAccount = true; + Controls.LoginErrorOk.click(); - return (getLocation() !== location); - }, + return; + } else { + D2Bot.printToConsole(string); + D2Bot.updateStatus(string); + } - setNextGame: function (gameInfo = {}) { - let nextGame = (gameInfo.gameName || this.randomString(null, true)); - - if ((this.gameCount + 1 >= Starter.Config.ResetCount) || (nextGame.length + this.gameCount + 1 > 15)) { - nextGame += "1"; - } else { - nextGame += (this.gameCount + 1); - } + break; + case getLocaleString(sdk.locale.text.AccountIsCorrupted): + case getLocaleString(sdk.locale.text.UnableToCreateAccount): + D2Bot.printToConsole(string); + D2Bot.updateStatus(string); - DataFile.updateStats("nextGame", nextGame); - }, + break; + case getLocaleString(sdk.locale.text.Disconnected): + D2Bot.updateStatus("Disconnected"); + D2Bot.printToConsole("Disconnected"); + Controls.OkCentered.click(); + Controls.LoginErrorOk.click(); - updateCount: function () { - D2Bot.updateCount(); - delay(1000); - Controls.BattleNet.click(); - - try { - login(me.profile); - } catch (e) { - return; - } + return; + 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; - delay(1000); - Controls.CharSelectExit.click(); - }, - - scriptMsgEvent: function (msg) { - if (msg && typeof msg !== "string") return; - switch (msg) { - case "mule": - AutoMule.check = true; - - break; - case "muleTorch": - AutoMule.torchAnniCheck = 1; - - break; - case "muleAnni": - AutoMule.torchAnniCheck = 2; - - break; - case "torch": - TorchSystem.check = true; - - break; - case "crafting": - CraftingSystem.check = true; - - break; - case "getMuleMode": - if (AutoMule.torchAnniCheck === 2) { - scriptBroadcast("2"); - } else if (AutoMule.torchAnniCheck === 1) { - scriptBroadcast("1"); - } else if (AutoMule.check) { - scriptBroadcast("0"); - } + break; + case getLocaleString(sdk.locale.text.CdKeyInUseBy): + string += (" " + Controls.LoginCdKeyInUseBy.getText()); + D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyInUse(); - break; - case "pingquit": - this.pingQuit = true; + if (Starter.gameInfo.switchKeys) { + cdkeyError = true; + } else { + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); + + return; + } - break; - } - }, - - receiveCopyData: function (mode, msg) { - let obj; - - msg === "Handle" && typeof mode === "number" && (Starter.handle = mode); - - switch (mode) { - case 1: // JoinInfo - obj = JSON.parse(msg); - Object.assign(Starter.joinInfo, obj); - - break; - case 2: // Game info - print("Recieved Game Info"); - obj = JSON.parse(msg); - Object.assign(Starter.gameInfo, obj); - - break; - case 3: // Game request - // in case someone is using a lightweight entry like blank/map to play manually and these aren't included - if (typeof AutoMule !== "undefined") { - // Don't let others join mule/torch/key/gold drop game - if (AutoMule.inGame || Gambling.inGame || TorchSystem.inGame || CraftingSystem.inGame) { - break; - } - } + break; + case getLocaleString(sdk.locale.text.LoginError): + case getLocaleString(sdk.locale.text.OnlyOneInstanceAtATime): + Controls.LoginErrorOk.click(); + Controls.LoginExit.click(); + D2Bot.printToConsole(string); + ControlAction.timeoutDelay("Login Error Delay", 5 * 6e4); + D2Bot.printToConsole("Login Error - Restart"); + D2Bot.restart(); - if (Object.keys(Starter.gameInfo).length) { - obj = JSON.parse(msg); + break; + default: + D2Bot.updateStatus("Login Error"); + D2Bot.printToConsole("Login Error - " + string); + cdkeyError = true; + defaultPrint = false; - if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { - me.gameReady && D2Bot.joinMe(obj.profile, me.gameserverip.toString(), "", "", Starter.isUp); - } else { - if (me.gameReady) { - D2Bot.joinMe(obj.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), Starter.isUp); - } else { - D2Bot.joinMe(obj.profile, Starter.gameInfo.gameName.toLowerCase(), Starter.gameCount, Starter.gameInfo.gamePass.toLowerCase(), Starter.isUp); + break; } - } - } - - break; - case 4: // Heartbeat ping - msg === "pingreq" && sendCopyData(null, me.windowtitle, 4, "pingrep"); - - break; - case 61732: // Cached info retreival - msg !== "null" && (Starter.gameInfo.crashInfo = JSON.parse(msg)); - - break; - case 1638: // getProfile - try { - obj = JSON.parse(msg); - Starter.profileInfo.profile = me.profile; - Starter.profileInfo.account = obj.account; - Starter.profileInfo.charName = obj.Character; - obj.Realm = obj.Realm.toLowerCase(); - Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; - } catch (e) { - print(e); - } - - break; - } - }, - randomString: function (len, useNumbers = false) { - !len && (len = rand(5, 14)); - - let rval = ""; - let letters = useNumbers ? "abcdefghijklmnopqrstuvwxyz0123456789" : "abcdefghijklmnopqrstuvwxyz"; - - for (let i = 0; i < len; i += 1) { - rval += letters[rand(0, letters.length - 1)]; - } - - return rval; - }, - - randomNumberString: function (len) { - !len && (len = rand(2, 5)); + 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(); + } + } - let rval = ""; - let vals = "0123456789"; + Controls.LoginErrorOk.click(); + delay(1000); + Controls.CharSelectExit.click(); + + while (true) { + delay(1000); + } + } + }, - for (let i = 0; i < len; i += 1) { - rval += vals[rand(0, vals.length - 1)]; - } + charSelectError: function () { + let string = ""; + let text = Controls.CharSelectError.getText(); + let currentLoc = getLocation(); - return rval; - }, + if (text) { + for (let i = 0; i < text.length; i += 1) { + string += text[i]; + i !== text.length - 1 && (string += " "); + } - LocationEvents: { - selectDifficultySP: function () { - let diff = (Starter.gameInfo.difficulty || "Highest"); - diff === "Highest" && (diff = "Hell"); // starts from top with fall-through to select highest + 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(); - switch (diff) { - case "Hell": - if (Controls.HellSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { - break; - } - // eslint-disable-next-line no-fallthrough - case "Nightmare": - if (Controls.NightmareSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { - break; + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } + } } - // eslint-disable-next-line no-fallthrough - case "Normal": - Controls.NormalSP.click(); - break; - } - return Starter.locationTimeout(5e3, sdk.game.locations.SelectDifficultySP); - }, - - loginError: function () { - let cdkeyError = false; - let defaultPrint = true; - let string = ""; - let text = (Controls.LoginErrorText.getText() || Controls.LoginInvalidCdKey.getText()); + if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, currentLoc)) { + // Click create char button on infinite "connecting" screen + Controls.CharSelectCreate.click(); + delay(1000); + + Controls.CharSelectExit.click(); + delay(1000); + + if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; + + Controls.CharSelectExit.click(); + Starter.gameInfo.rdBlocker && D2Bot.restart(); - if (text) { - for (let i = 0; i < text.length; i += 1) { - string += text[i]; - i !== text.length - 1 && (string += " "); + return false; } - 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.printToConsole("Invalid Password"); - ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); - D2Bot.printToConsole("Invalid Password - Restart"); - D2Bot.restart(); - - break; - case getLocaleString(sdk.locale.text.AccountDoesNotExist): - if (!!Starter.Config.MakeAccountOnFailure) { - Starter.makeAccount = true; - Controls.LoginErrorOk.click(); - - return; - } else { - D2Bot.printToConsole(string); - D2Bot.updateStatus(string); - } - - break; - case getLocaleString(sdk.locale.text.AccountIsCorrupted): - case getLocaleString(sdk.locale.text.UnableToCreateAccount): - D2Bot.printToConsole(string); - D2Bot.updateStatus(string); + return true; + }, - break; - case getLocaleString(sdk.locale.text.Disconnected): - D2Bot.updateStatus("Disconnected"); - D2Bot.printToConsole("Disconnected"); - Controls.OkCentered.click(); - Controls.LoginErrorOk.click(); + realmDown: function () { + D2Bot.updateStatus("Realm Down"); + delay(1000); - return; - 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; + if (!Controls.CharSelectExit.click()) return; - break; - case getLocaleString(sdk.locale.text.CdKeyInUseBy): - string += (" " + Controls.LoginCdKeyInUseBy.getText()); - D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyInUse(); + Starter.updateCount(); + ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); + D2Bot.CDKeyRD(); - if (Starter.gameInfo.switchKeys) { - cdkeyError = true; - } else { - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); - - return; - } - - break; - case getLocaleString(sdk.locale.text.LoginError): - case getLocaleString(sdk.locale.text.OnlyOneInstanceAtATime): - Controls.LoginErrorOk.click(); - Controls.LoginExit.click(); - D2Bot.printToConsole(string); - ControlAction.timeoutDelay("Login Error Delay", 5 * 6e4); - D2Bot.printToConsole("Login Error - Restart"); + if (Starter.gameInfo.switchKeys && !Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole("Realm Down - Changing CD-Key"); + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.printToConsole("Realm Down - Restart"); D2Bot.restart(); - - break; - default: - D2Bot.updateStatus("Login Error"); - D2Bot.printToConsole("Login Error - " + string); - cdkeyError = true; - defaultPrint = false; - - break; } + }, + + waitingInLine: function () { + let queue = ControlAction.getQueueTime(); + let currentLoc = getLocation(); + + if (queue > 0) { + switch (true) { + case (queue < 10000): + D2Bot.updateStatus("Waiting line... Queue: " + queue); + + // If stuck here for too long, game creation likely failed. Exit to char selection and try again. + if (queue < 10) { + if (!Starter.locationTimeout(Starter.Config.WaitInLineTimeout * 1e3, currentLoc)) { + print("Failed to create game"); + Controls.CancelCreateGame.click(); + Controls.LobbyQuit.click(); + delay(1000); + } + } - 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; + case (queue > 10000): + if (Starter.Config.WaitOutQueueRestriction) { + D2Bot.updateStatus("Waiting out Queue restriction: " + queue); + } else { + print("Restricted... Queue: " + queue); + D2Bot.printToConsole("Restricted... Queue: " + queue, sdk.colors.D2Bot.Red); + Controls.CancelCreateGame.click(); - Controls.LoginErrorOk.click(); - delay(1000); - Controls.CharSelectExit.click(); - - while (true) { - delay(1000); - } - } - }, + if (Starter.Config.WaitOutQueueExitToMenu) { + Controls.LobbyQuit.click(); + delay(1000); + Controls.CharSelectExit.click(); + } - charSelectError: function () { - let string = ""; - let text = Controls.CharSelectError.getText(); - let currentLoc = getLocation(); + // Wait out each queue as 1 sec and add extra 10 min + ControlAction.timeoutDelay("Restricted", (queue + 600) * 1000); + } - if (text) { - for (let i = 0; i < text.length; i += 1) { - string += text[i]; - i !== text.length - 1 && (string += " "); + break; + } } + }, + + gameDoesNotExist: function () { + let currentLoc = getLocation(); + console.log("Game doesn't exist"); - 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.rdBlocker) { + D2Bot.printToConsole(Starter.gameInfo.mpq + " is probably flagged.", sdk.colors.D2Bot.Gold); if (Starter.gameInfo.switchKeys) { ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); D2Bot.restart(true); - } else { - D2Bot.stop(); } + } else { + Starter.locationTimeout(Starter.Config.GameDoesNotExistTimeout * 1e3, currentLoc); } - } - if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, currentLoc)) { - // Click create char button on infinite "connecting" screen - Controls.CharSelectCreate.click(); - delay(1000); - - Controls.CharSelectExit.click(); - delay(1000); - - if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; - - Controls.CharSelectExit.click(); - Starter.gameInfo.rdBlocker && D2Bot.restart(); + Starter.lastGameStatus = "ready"; + }, - return false; - } - - return true; - }, - - realmDown: function () { - D2Bot.updateStatus("Realm Down"); - delay(1000); + unableToConnect: function () { + let currentLoc = getLocation(); - if (!Controls.CharSelectExit.click()) return; + if (getLocation() === sdk.game.locations.TcpIpUnableToConnect) { + D2Bot.updateStatus("Unable To Connect TCP/IP"); + Starter.connectFail && ControlAction.timeoutDelay("Unable to Connect", Starter.Config.TCPIPNoHostDelay * 1e3); + Controls.OkCentered.click(); + Starter.connectFail = !Starter.connectFail; + } else { + D2Bot.updateStatus("Unable To Connect"); - Starter.updateCount(); - ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); - D2Bot.CDKeyRD(); + if (Starter.connectFailRetry < 2) { + Starter.connectFailRetry++; + Controls.UnableToConnectOk.click(); - if (Starter.gameInfo.switchKeys && !Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole("Realm Down - Changing CD-Key"); - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.printToConsole("Realm Down - Restart"); - D2Bot.restart(); - } - }, + return; + } - waitingInLine: function () { - let queue = ControlAction.getQueueTime(); - let currentLoc = getLocation(); + Starter.connectFailRetry >= 2 && (Starter.connectFail = true); - if (queue > 0) { - switch (true) { - case (queue < 10000): - D2Bot.updateStatus("Waiting line... Queue: " + queue); + if (Starter.connectFail && !Starter.locationTimeout(10e4, currentLoc)) { + let string = ""; + let text = Controls.LoginUnableToConnect.getText(); - // If stuck here for too long, game creation likely failed. Exit to char selection and try again. - if (queue < 10) { - if (!Starter.locationTimeout(Starter.Config.WaitInLineTimeout * 1e3, currentLoc)) { - print("Failed to create game"); - Controls.CancelCreateGame.click(); - Controls.LobbyQuit.click(); - delay(1000); + if (text) { + for (let i = 0; i < text.length; i++) { + string += text[i]; + i !== text.length - 1 && (string += " "); + } } - } - - break; - case (queue > 10000): - if (Starter.Config.WaitOutQueueRestriction) { - D2Bot.updateStatus("Waiting out Queue restriction: " + queue); - } else { - print("Restricted... Queue: " + queue); - D2Bot.printToConsole("Restricted... Queue: " + queue, sdk.colors.D2Bot.Red); - Controls.CancelCreateGame.click(); - - if (Starter.Config.WaitOutQueueExitToMenu) { - Controls.LobbyQuit.click(); - delay(1000); - Controls.CharSelectExit.click(); + + switch (string) { + case getLocaleString(sdk.locale.text.UnableToIndentifyVersion): + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("Version error", Starter.Config.VersionErrorDelay * 1000); + + break; + default: // Regular UTC and everything else + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 1000 * 60); + + break; } - // Wait out each queue as 1 sec and add extra 10 min - ControlAction.timeoutDelay("Restricted", (queue + 600) * 1000); + Starter.connectFail = false; } - break; - } - } - }, - - gameDoesNotExist: function () { - let currentLoc = getLocation(); - console.log("Game doesn't exist"); - - if (Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole(Starter.gameInfo.mpq + " is probably flagged.", sdk.colors.D2Bot.Gold); + if (!Controls.UnableToConnectOk.click()) { + return; + } - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); + Starter.connectFail = true; + Starter.connectFailRetry = 0; } - } else { - Starter.locationTimeout(Starter.Config.GameDoesNotExistTimeout * 1e3, currentLoc); - } + }, - Starter.lastGameStatus = "ready"; - }, - - unableToConnect: function () { - let currentLoc = getLocation(); - - if (getLocation() === sdk.game.locations.TcpIpUnableToConnect) { - D2Bot.updateStatus("Unable To Connect TCP/IP"); - Starter.connectFail && ControlAction.timeoutDelay("Unable to Connect", Starter.Config.TCPIPNoHostDelay * 1e3); - Controls.OkCentered.click(); - Starter.connectFail = !Starter.connectFail; - } else { - D2Bot.updateStatus("Unable To Connect"); + openCreateGameWindow: function () { + let currentLoc = getLocation(); - if (Starter.connectFailRetry < 2) { - Starter.connectFailRetry++; - Controls.UnableToConnectOk.click(); - - return; + if (!Controls.CreateGameWindow.click()) { + return true; } - Starter.connectFailRetry >= 2 && (Starter.connectFail = true); - - if (Starter.connectFail && !Starter.locationTimeout(10e4, currentLoc)) { - let string = ""; - let text = Controls.LoginUnableToConnect.getText(); - - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; - i !== text.length - 1 && (string += " "); - } - } - - switch (string) { - case getLocaleString(sdk.locale.text.UnableToIndentifyVersion): - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("Version error", Starter.Config.VersionErrorDelay * 1000); - - break; - default: // Regular UTC and everything else - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 1000 * 60); - - break; + // dead HardCore character + if (Controls.CreateGameWindow.control && Controls.CreateGameWindow.disabled === sdk.game.controls.Disabled) { + if (Starter.Config.StopOnDeadHardcore) { + D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Shutting down, better luck next time", sdk.colors.D2Bot.Gold); + D2Bot.stop(); + } else { + D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Better luck next time", sdk.colors.D2Bot.Gold); + D2Bot.updateStatus(Profile().character + " has died. They shall be remembered...maybe. Better luck next time"); + Starter.deadCheck = true; + Controls.LobbyQuit.click(); } - Starter.connectFail = false; - } - - if (!Controls.UnableToConnectOk.click()) { - return; + return false; } - Starter.connectFail = true; - Starter.connectFailRetry = 0; - } - }, - - openCreateGameWindow: function () { - let currentLoc = getLocation(); - - if (!Controls.CreateGameWindow.click()) { - return true; - } + // in case create button gets bugged + if (!Starter.locationTimeout(5000, currentLoc)) { + if (!Controls.CreateGameWindow.click()) { + return true; + } - // dead HardCore character - if (Controls.CreateGameWindow.control && Controls.CreateGameWindow.disabled === sdk.game.controls.Disabled) { - if (Starter.Config.StopOnDeadHardcore) { - D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Shutting down, better luck next time", sdk.colors.D2Bot.Gold); - D2Bot.stop(); - } else { - D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Better luck next time", sdk.colors.D2Bot.Gold); - D2Bot.updateStatus(Profile().character + " has died. They shall be remembered...maybe. Better luck next time"); - Starter.deadCheck = true; - Controls.LobbyQuit.click(); + if (!Controls.JoinGameWindow.click()) { + return true; + } } - return false; - } + return (getLocation() === sdk.game.locations.CreateGame); + }, - // in case create button gets bugged - if (!Starter.locationTimeout(5000, currentLoc)) { - if (!Controls.CreateGameWindow.click()) { - return true; - } + openJoinGameWindow: function () { + let currentLoc = getLocation(); if (!Controls.JoinGameWindow.click()) { - return true; + return; } - } - - return (getLocation() === sdk.game.locations.CreateGame); - }, - - openJoinGameWindow: function () { - let currentLoc = getLocation(); - if (!Controls.JoinGameWindow.click()) { - return; - } + // in case create button gets bugged + if (!Starter.locationTimeout(5000, currentLoc)) { + if (!Controls.CreateGameWindow.click()) { + return; + } - // in case create button gets bugged - if (!Starter.locationTimeout(5000, currentLoc)) { - if (!Controls.CreateGameWindow.click()) { - return; + if (!Controls.JoinGameWindow.click()) { + return; + } } + }, - if (!Controls.JoinGameWindow.click()) { - return; + login: function (otherMultiCheck = false) { + Starter.inGame && (Starter.inGame = false); + if (otherMultiCheck && [sdk.game.gametype.SinglePlayer, sdk.game.gametype.BattleNet].indexOf(Profile().type) === -1) { + return ControlAction.loginOtherMultiplayer(); } - } - }, - login: function (otherMultiCheck = false) { - Starter.inGame && (Starter.inGame = false); - if (otherMultiCheck && [sdk.game.gametype.SinglePlayer, sdk.game.gametype.BattleNet].indexOf(Profile().type) === -1) { - return ControlAction.loginOtherMultiplayer(); - } - - if (getLocation() === sdk.game.locations.MainMenu) { - if (Profile().type === sdk.game.profiletype.SinglePlayer - && Starter.firstRun - && Controls.SinglePlayer.click()) { - return true; + if (getLocation() === sdk.game.locations.MainMenu) { + if (Profile().type === sdk.game.profiletype.SinglePlayer + && Starter.firstRun + && Controls.SinglePlayer.click()) { + return true; + } } - } - // 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(); - - return false; + // 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(); + + return false; + } } - } - // 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 { - login(me.profile); - } catch (e) { - if (getLocation() === sdk.game.locations.CharSelect && Starter.loginRetry < 2) { - if (!ControlAction.findCharacter(Starter.profileInfo)) { - // dead hardcore character on sp - if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { - // Exit from that pop-up - Controls.OkCentered.click(); - D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); - D2Bot.stop(); + // 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 { + login(me.profile); + } catch (e) { + if (getLocation() === sdk.game.locations.CharSelect && Starter.loginRetry < 2) { + if (!ControlAction.findCharacter(Starter.profileInfo)) { + // dead hardcore character on sp + if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { + // Exit from that pop-up + Controls.OkCentered.click(); + D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } else { + Starter.loginRetry++; + } } else { - Starter.loginRetry++; + login(me.profile); } + } else if (getLocation() === sdk.game.locations.TcpIpEnterIp && Profile().type === sdk.game.profiletype.TcpIpJoin) { + return true; // handled in its own case } else { - login(me.profile); + print(e + " " + getLocation()); } - } else if (getLocation() === sdk.game.locations.TcpIpEnterIp && Profile().type === sdk.game.profiletype.TcpIpJoin) { - return true; // handled in its own case + } + + return true; + }, + + otherMultiplayerSelect: function () { + if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { + Controls.TcpIp.click() && (Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpJoin.click()); + } else if (Profile().type === sdk.game.profiletype.OpenBattlenet) { + Controls.OpenBattleNet.click(); } else { - print(e + " " + getLocation()); + Controls.OtherMultiplayerCancel.click(); } } - - return true; }, + }; - otherMultiplayerSelect: function () { - if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { - Controls.TcpIp.click() && (Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpJoin.click()); - } else if (Profile().type === sdk.game.profiletype.OpenBattlenet) { - Controls.OpenBattleNet.click(); - } else { - Controls.OtherMultiplayerCancel.click(); - } - } - }, -}; + return { + ControlAction: ControlAction, + Starter: Starter, + }; +})); diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index ad64001b3..83b250cf2 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -1,7 +1,7 @@ /** * @filename Polyfill.js * @author Jaenster (probably) -* @desc Some polyfills since we run an old spidermonkey +* @desc Some polyfills since we run old spidermonkey (61f7ebb) * */ @@ -10,14 +10,15 @@ String.prototype.lcsGraph = function (compareToThis) { return null; } - let stringA = this.toString().toLowerCase(), stringB = compareToThis.toLowerCase(), graph = Array(this.length), x, - y; + let stringA = this.toString().toLowerCase(); + let stringB = compareToThis.toLowerCase(); + let graph = Array(this.length); let check = (i, j) => (i < 0 || j < 0 || i >= stringA.length || j >= stringB.length) ? 0 : graph[i][j]; - for (x = 0; x < stringA.length; x++) { + for (let x = 0; x < stringA.length; x++) { graph[x] = new Uint16Array(stringB.length); - for (y = 0; y < stringB.length; y++) { + for (let y = 0; y < stringB.length; y++) { if (stringA[x] === stringB[y]) { graph[x][y] = check(x - 1, y - 1) + 1; } else { @@ -173,6 +174,26 @@ if (!String.isEqual) { }; } +/** + * Use since we don't have template literals + * Replaces placeholders in a string with provided values. + * + * @param {Array>} pairs - An array of arrays, + * where the first item in each inner array is a placeholder in the form of "$placeholder", + * and the second item is the value to replace it with. + * @returns {string} The formatted string. + */ +String.prototype.format = function (...pairs) { + if (!pairs.length) return this; + let newString = this; + pairs.forEach(pair => { + let [match, replace] = pair; + if (match === undefined || replace === undefined) return; + newString = newString.replace(match, replace); + }); + return newString; +}; + // Production steps of ECMA-262, Edition 6, 22.1.2.1 if (!Array.from) { Array.from = (function () { @@ -432,6 +453,7 @@ Array.prototype.fill = function (value, start = 0, end = undefined) { for (let i = start; i < stop; i++) { this[i] = value; } + return this; }; /** @@ -479,15 +501,17 @@ if (!Array.prototype.flat) { }); } // eslint-disable-next-line block-scoped-var -if (typeof global === "undefined") { - // eslint-disable-next-line no-var - var global = this; -} +// if (typeof global === "undefined") { +// var global = this; +// } + +// eslint-disable-next-line no-var +if (typeof global === "undefined") var global = [].filter.constructor("return this")(); +// eslint-disable-next-line dot-notation +global["globalThis"] = [].filter.constructor("return this")(); -// eslint-disable-next-line block-scoped-var if (!global.hasOwnProperty("require")) { let cache; - // eslint-disable-next-line block-scoped-var Object.defineProperty(global, "require", { get: function () { if (cache) return cache; @@ -664,7 +688,207 @@ if (!Object.entries) { this.log(logInfo); }; + /** + * @param {object | any[]} data + * @param {string[]} [columns] + */ + console.table = function (data, columns) { + if (data === undefined) return; + + let output = ""; + let table = []; + let row = []; + + // Create table headers + if (!columns) { + columns = Object.keys(data[0]); + } + row = columns; + table.push(row); + + // Create table rows + for (let i = 0; i < data.length; i++) { + row = []; + for (let j = 0; j < columns.length; j++) { + row.push(data[i][columns[j]]); + } + table.push(row); + } + + // todo - get longest element and adjust the output of that column to stay within the header bars + let maxLengths = new Array(table[0].length).fill(0); + + for (let i = 0; i < table.length; i++) { + for (let j = 0; j < table[i].length; j++) { + maxLengths[j] = Math.max(maxLengths[j], table[i][j].toString().length); + } + } + console.log(maxLengths); + + // Create table output + for (let i = 0; i < table.length; i++) { + for (let j = 0; j < table[i].length; j++) { + // output += "| " + table[i][j] + " "; + output += "| " + table[i][j].toString().padEnd(maxLengths[j]) + " "; + } + output += "|\n"; + } + + // // Log table to console + console.log(output); + + // for (let i = 0; i < data.length; i++) { + // let row = "|"; + // for (let j = 0; j < data[i].length; j++) { + // row += " " + data[i][j].toString().padEnd(maxLengths[j]) + " |"; + // } + // console.log(row); + // } + }; + return console; })(); })([].filter.constructor("return this")(), print); + +if (!global.hasOwnProperty("sdk") && typeof require !== "undefined") { + Object.defineProperty(global, "sdk", { + value: require("../modules/sdk"), + enumerable: true, + }); +} + +if (!global.hasOwnProperty("includeIfNotIncluded")) { + Object.defineProperty(global, "includeIfNotIncluded", { + /** + * @param {string} file + */ + value: function (file = "") { + if (!isIncluded(file)) { + if (!include(file)) { + console.error("Failed to include " + file); + console.trace(); + return false; + } + } + return true; + }, + }); +} + +if (!global.hasOwnProperty("includeCoreLibs")) { + Object.defineProperty(global, "includeCoreLibs", { + /** + * @description includes all files from libs/core/ folder + * @param {string[]} ignoreFiles + */ + value: function (obj = { exclude: [] }) { + /** @type {string[]} */ + let files = dopen("libs/core/").getFiles(); + if (!files.length) throw new Error("Failed to find my files"); + if (!files.includes("Pather.js")) { + console.warn("Incorrect Files?", files); + // something went wrong? + while (!files.includes("Pather.js")) { + files = dopen("libs/core/").getFiles(); + delay(50); + } + } + // always include util first + includeIfNotIncluded("core/Util.js"); + files.filter(file => file.endsWith(".js") && !obj.exclude.includes(file) && !file.match("util.js", "gi")) + .forEach(function (x) { + if (!includeIfNotIncluded("core/" + x)) { + throw new Error("Failed to include core/" + x); + } + }); + return true; + }, + }); +} + +if (!global.hasOwnProperty("includeSystemLibs")) { + Object.defineProperty(global, "includeSystemLibs", { + /** + * @description includes system driver files from libs/systems/ folder + */ + value: function () { + include("systems/automule/automule.js"); + include("systems/crafting/CraftingSystem.js"); + include("systems/gambling/Gambling.js"); + include("systems/torch/TorchSystem.js"); + return true; + }, + }); +} + +if (!global.hasOwnProperty("clone")) { + Object.defineProperty(global, "clone", { + /** + * @param {Date | any[] | object} obj + * @returns {ThisParameterType} deep copy of parameter + */ + value: function (obj) { + let copy; + + // Handle the 3 simple types, and null or undefined + if (null === obj || "object" !== typeof obj) { + return obj; + } + + // Handle Date + if (obj instanceof Date) { + copy = new Date(); + copy.setTime(obj.getTime()); + + return copy; + } + + // Handle Array + if (obj instanceof Array) { + copy = []; + + for (let i = 0; i < obj.length; i += 1) { + copy[i] = clone(obj[i]); + } + + return copy; + } + + // Handle Object + if (obj instanceof Object) { + copy = {}; + + for (let attr in obj) { + if (obj.hasOwnProperty(attr)) { + copy[attr] = clone(obj[attr]); + } + } + + return copy; + } + + throw new Error("Unable to copy obj! Its type isn't supported."); + }, + }); +} + +if (!global.hasOwnProperty("copyObj")) { + Object.defineProperty(global, "copyObj", { + /** + * @param {object} from + * @returns {object} deep copy + */ + value: function (from) { + let obj = {}; + + for (let i in from) { + if (from.hasOwnProperty(i)) { + obj[i] = clone(from[i]); + } + } + + return obj; + }, + }); +} diff --git a/d2bs/kolbot/libs/StarterConfig.js b/d2bs/kolbot/libs/StarterConfig.js deleted file mode 100644 index c4517f1af..000000000 --- a/d2bs/kolbot/libs/StarterConfig.js +++ /dev/null @@ -1,78 +0,0 @@ -/** -* @filename StarterConfig.js -* @author theBGuy -* @desc Global settings for entry scripts -* -*/ -!isIncluded("OOG.js") && include("OOG.js"); - -Starter.Config = { - MinGameTime: 360, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby - PingQuitDelay: 30, // Time in seconds to wait in lobby after quitting due to high ping - CreateGameDelay: rand(5, 15), // Seconds to wait before creating a new game - ResetCount: 999, // Reset game count back to 1 every X games. - CharacterDifference: 99, // Character level difference. Set to false to disable character difference. - MaxPlayerCount: 8, // Max amount of players in game between 1 and 8 - StopOnDeadHardcore: true, // Stop profile character has died on hardcore mode - - // ChannelConfig can override these options for individual profiles. - JoinChannel: "", // Default channel. - FirstJoinMessage: "", // Default join message. Can be an array of messages - ChatActionsDelay: 2, // Seconds to wait in lobby before entering a channel - AnnounceGames: false, // Default value - AfterGameMessage: "", // Default message after a finished game. Can be an array of messages - - InvalidPasswordDelay: 10, // Minutes to wait after getting Invalid Password message - VersionErrorDelay: rand(5, 30), // Seconds to wait after 'unable to identify version' message - SwitchKeyDelay: 5, // Seconds to wait before switching a used/banned key or after realm down - CrashDelay: rand(120, 150), // Seconds to wait after a d2 window crash - FTJDelay: 120, // Seconds to wait after failing to create a game - RealmDownDelay: 3, // Minutes to wait after getting Realm Down message - UnableToConnectDelay: 5, // Minutes to wait after Unable To Connect message - TCPIPNoHostDelay: 5, // Seconds to wait after Cannot Connect To Server message - CDKeyInUseDelay: 5, // Minutes to wait before connecting again if CD-Key is in use. - ConnectingTimeout: 60, // Seconds to wait before cancelling the 'Connecting...' screen - PleaseWaitTimeout: 60, // Seconds to wait before cancelling the 'Please Wait...' screen - WaitInLineTimeout: 3600, // Seconds to wait before cancelling the 'Waiting in Line...' screen - WaitOutQueueRestriction: true, // Wait out queue if we are restricted, queue time > 10000 - WaitOutQueueExitToMenu: false, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby - GameDoesNotExistTimeout: 30, // Seconds to wait before cancelling the 'Game does not exist.' screen -}; - -// Advanced config - you don't have to edit this unless you need some of the features provided -const AdvancedConfig = { - /* Features: - Override channel for each profile, Override join delay for each profile - Override default values for JoinChannel, FirstJoinMessage, AnnounceGames and AfterGameMessage per profile - - * Format *: - "Profile Name": {JoinDelay: number_of_seconds} - or - "Profile Name": {JoinChannel: "channel name"} - or - "Profile Name": {JoinChannel: "channel name", JoinDelay: number_of_seconds} - - * Example * (don't edit this - it's just an example): - - "MyProfile1": {JoinDelay: 3}, - "MyProfile2": {JoinChannel: "some channel"}, - "MyProfile3": {JoinChannel: "some other channel", JoinDelay: 11} - "MyProfile4": {AnnounceGames: true, AnnounceMessage: "Joining game"} // announce game you are joining - - "Profile Name": { - JoinChannel: "channel name", - FirstJoinMessage: "first message", -OR- ["join msg 1", "join msg 2"], - AnnounceGames: true, - AfterGameMessage: "message after a finished run" -OR- ["msg 1", msg 2"] - } - */ - - // Put your lines under this one. Multiple entries are separated by commas. No comma after the last one. - - "Test": { - JoinChannel: "op nnqry", - JoinDelay: 3, - AnnounceGames: true, - AnnounceMessage: "Joining game" // output: Joining game Baals-23 - }, -}; diff --git a/d2bs/kolbot/libs/bots/BaalAssistant.js b/d2bs/kolbot/libs/bots/BaalAssistant.js deleted file mode 100644 index f80cdbee4..000000000 --- a/d2bs/kolbot/libs/bots/BaalAssistant.js +++ /dev/null @@ -1,433 +0,0 @@ -/** -* @filename BaalAssistant.js -* @author kolton, YGM, theBGuy -* @desc Help or Leech Baal Runs. -* -*/ - -// todo - combine autobaal, baalhelper, and baalassistant into one script -// todo - track leaders area so we can do silent follow - -function BaalAssistant () { - let Leader = Config.Leader; - let Helper = Config.BaalAssistant.Helper; - let hotCheck = false; - let safeCheck = false; - let baalCheck = false; - let ngCheck = false; - let ShrineStatus = false; - let secondAttempt = false; - let throneStatus = false; - let firstAttempt = true; - let killTracker = false; - - // convert all messages to lowercase - Config.BaalAssistant.HotTPMessage.forEach((msg, i) => { - Config.BaalAssistant.HotTPMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.SafeTPMessage.forEach((msg, i) => { - Config.BaalAssistant.SafeTPMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.BaalMessage.forEach((msg, i) => { - Config.BaalAssistant.BaalMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.NextGameMessage.forEach((msg, i) => { - Config.BaalAssistant.NextGameMessage[i] = msg.toLowerCase(); - }); - - addEventListener("chatmsg", - function (nick, msg) { - if (nick === Leader) { - msg = msg.toLowerCase(); - - for (let i = 0; i < Config.BaalAssistant.HotTPMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.HotTPMessage[i])) { - hotCheck = true; - break; - } - } - - for (let i = 0; i < Config.BaalAssistant.SafeTPMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.SafeTPMessage[i])) { - safeCheck = true; - break; - } - } - - for (let i = 0; i < Config.BaalAssistant.BaalMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.BaalMessage[i])) { - baalCheck = true; - break; - } - } - - for (let i = 0; i < Config.BaalAssistant.NextGameMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.NextGameMessage[i])) { - ngCheck = true; - killTracker = true; - break; - } - } - } - }); - - this.checkParty = function () { - for (let i = 0; i < Config.BaalAssistant.Wait; i += 1) { - let partycheck = getParty(); - if (partycheck) { - do { - if (partycheck.area === sdk.areas.ThroneofDestruction) return false; - if (partycheck.area === sdk.areas.RiverofFlame || partycheck.area === sdk.areas.ChaosSanctuary) return true; - } while (partycheck.getNext()); - } - - delay(1000); - } - - return false; - }; - - // Start - const Worker = require("../modules/Worker"); - - if (Leader) { - if (!Misc.poll(() => Misc.inMyParty(Leader), Time.seconds(30), 1000)) throw new Error("BaalAssistant: Leader not partied"); - } - - let killLeaderTracker = false; - if (!Leader && (Config.BaalAssistant.KillNihlathak || Config.BaalAssistant.FastChaos)) { - // run background auto detect so we don't miss messages while running add ons - let leadTick = getTickCount(); - - Worker.runInBackground.leaderTracker = function () { - if (killLeaderTracker || killTracker) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) return false; - - let party = getParty(); - - if (party) { - do { - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { - Leader = party.name; - console.log(sdk.colors.DarkGold + "Autodected " + Leader); - return false; - } - } while (party.getNext()); - } - - return true; - }; - } - - Config.BaalAssistant.KillNihlathak && Loader.runScript("Nihlathak"); - Config.BaalAssistant.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); - - Town.goToTown(5); - Town.doChores(); - - if (Leader - || (Leader = Misc.autoLeaderDetect({destination: sdk.areas.WorldstoneLvl3, quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area)})) - || (Leader = Misc.autoLeaderDetect({destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area)}))) { - print("ÿc hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); - - if (!hotCheck) { - print("ÿc1Leader didn't tell me to start hunting for an experience shrine."); - ShrineStatus = true; - } - } - - // don't waste time looking for seal if party is already killing baal, he'll be dead by the time we find one - if (!ShrineStatus && !baalCheck) { - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - let i; - - for (i = sdk.areas.StonyField; i > sdk.areas.RogueEncampment; i -= 1) { - if (safeCheck) { - break; - } - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; - } - } - - if (!safeCheck) { - if (i === sdk.areas.RogueEncampment) { - Town.goToTown(); - Pather.useWaypoint(sdk.areas.DarkWood); - Precast.doPrecast(true); - - for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i += 1) { - if (safeCheck) { - break; - } - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; - } - } - } - } - } - - Town.goToTown(5); - ShrineStatus = true; - } - - if (firstAttempt && !secondAttempt && !safeCheck && !baalCheck && !me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { - !!Config.RandomPrecast ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); - } - - if (!me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { - if (Config.BaalAssistant.SkipTP) { - if (firstAttempt && !secondAttempt) { - !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) throw new Error("Failed to move to WSK3."); - - this.checkParty(); - let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); - entrance && Pather.moveTo(entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, entrance.y > me.y ? entrance.y - 5 : entrance.y + 5); - - if (!Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) || !Pather.moveTo(15118, 5002)) throw new Error("Failed to move to Throne of Destruction."); - - Pather.moveTo(15095, 5029); - - if ((Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { - print("Burning Souls or Undead Soul Killers found, ending script."); - return true; - } - - Pather.moveTo(15118, 5002); - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - - secondAttempt = true; - safeCheck = true; - } else { - if (me.inTown) { - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - } - } - } else { - if (firstAttempt && !secondAttempt) { - !me.inArea(sdk.areas.Harrogath) && Pather.useWaypoint(sdk.areas.Harrogath); - Town.move("portalspot"); - - if (Config.BaalAssistant.WaitForSafeTP && !Misc.poll(() => safeCheck, Time.seconds(Config.BaalAssistant.Wait), 1000)) { - throw new Error("No safe TP message."); - } - - if (!Misc.poll(() => Pather.usePortal(sdk.areas.ThroneofDestruction, null), Time.seconds(Config.BaalAssistant.Wait), 1000)) { - throw new Error("No portals to Throne."); - } - - if ((Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { - throw new Error("Burning Souls or Undead Soul Killers found, ending script."); - } - - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - secondAttempt = true; - safeCheck = true; - } else { - if (me.inTown) { - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - } - } - } - } - - if (safeCheck && !baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { - if (!baalCheck && !throneStatus) { - if (Helper) { - Attack.clear(15); - Common.Baal.clearThrone(); - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - Precast.doPrecast(true); - } - - let tick = getTickCount(); - - MainLoop: while (true) { - if (Helper) { - if (getDistance(me, 15094, me.paladin ? 5029 : 5038) > 3) { - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - } - } - - if (!Game.getMonster(sdk.monsters.ThroneBaal)) { - break; - } - - switch (Common.Baal.checkThrone(Helper)) { - case 1: - Helper && Attack.clear(40); - tick = getTickCount(); - - break; - case 2: - Helper && Attack.clear(40); - tick = getTickCount(); - - break; - case 4: - Helper && Attack.clear(40); - tick = getTickCount(); - - break; - case 3: - Helper && Attack.clear(40) && Common.Baal.checkHydra(); - tick = getTickCount(); - - break; - case 5: - if (Helper) { - Attack.clear(40); - } else { - while ([sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2] - .map((unitId) => Game.getMonster(unitId)) - .filter(Boolean).some((unit) => unit.attackable)) { - delay(1000); - } - - delay(1000); - } - - break MainLoop; - default: - if (getTickCount() - tick < 7e3) { - if (me.paladin && me.getState(sdk.states.Poison) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { - break; - } - } - - if (Helper && !Common.Baal.preattack()) { - delay(100); - } - - break; - } - delay(10); - } - throneStatus = true; - baalCheck = true; - } - } - - if ((throneStatus || baalCheck) && Config.BaalAssistant.KillBaal && me.inArea(sdk.areas.ThroneofDestruction)) { - Helper ? Pather.moveTo(15090, 5008) && delay(2000) : Pather.moveTo(15090, 5010); - Precast.doPrecast(true); - - while (Game.getMonster(sdk.monsters.ThroneBaal)) { - delay(500); - } - - let portal = Game.getObject(sdk.objects.WorldstonePortal); - - if (portal) { - delay((Helper ? 1000 : 4000)); - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } - - if (Helper) { - delay(1000); - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); - } else { - Pather.moveTo(15177, 5952); - let baal = Game.getMonster(sdk.monsters.Baal); - - while (!!baal && baal.attackable) { - delay(1000); - } - } - - } else { - while (!ngCheck) { - delay(500); - } - } - - delay(500); - } - } catch (e) { - console.error(e); - } finally { - killTracker = true; - } - } else { - throw new Error("Empty game."); - } - - return true; -} diff --git a/d2bs/kolbot/libs/bots/Summoner.js b/d2bs/kolbot/libs/bots/Summoner.js deleted file mode 100644 index 1e899b28b..000000000 --- a/d2bs/kolbot/libs/bots/Summoner.js +++ /dev/null @@ -1,42 +0,0 @@ -/** -* @filename Summoner.js -* @author kolton -* @desc kill the Summoner -* -*/ - -function Summoner () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); - - if (Config.Summoner.FireEye) { - try { - if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); - } catch (e) { - console.error(e); - } - } - - if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) throw new Error("Failed to move to Summoner"); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, -3, -3)) throw new Error("Failed to move to Summoner"); - - Attack.clear(15, 0, sdk.monsters.TheSummoner); - - if (Loader.scriptName(1) === "Duriel") { - let journal = Game.getObject(sdk.quest.chest.Journal); - if (!journal) return true; - - Pather.moveToUnit(journal); - journal.interact(); - delay(500); - me.cancel(); - - if (!Pather.usePortal(sdk.areas.CanyonofMagic)) return true; - - Loader.skipTown.push("Duriel"); - } - - return true; -} diff --git a/d2bs/kolbot/libs/common/AutoAssign.js b/d2bs/kolbot/libs/common/AutoAssign.js deleted file mode 100644 index 6382d1c8a..000000000 --- a/d2bs/kolbot/libs/common/AutoAssign.js +++ /dev/null @@ -1,252 +0,0 @@ -/** -* @filename AutoAssign.js -* @author ? -* @desc ? -* -*/ -let answer = false; -let request = false; - -const AutoAssign = { - recursion: true, - Barbs: [], - Sorcs: [], - Pallys: [], - Jobs: { - Barb: "", - Sorc: "", - Pally: "", - Mine: 0 - }, - - init: function () { - AutoAssign.updateNames(); // initiates all scripts - // Do something else? What else do we need to do... - - return true; - }, - - receiveCopyData: function (mode, msg) { - switch (mode) { - case 69: // request - msg === me.name && D2Bot.shoutGlobal("bot", 70); - - break; - case 70: // Received answer - msg === "bot" && request === true && (answer = true); - - break; - default: - break; - } - }, - - // eslint-disable-next-line no-unused-vars - gameEvent: function (mode, param1, param2, name1) { - switch (mode) { - case 0x00: // Left game due to time-out - AutoAssign.updateNames(name1); - - break; - case 0x02: // Joined game - AutoAssign.updateNames(); - - break; - case 0x03: //left game - AutoAssign.updateNames(name1); - - break; - - } - delay(250); - }, - - getJobs: function () { - let quitCheck; - let array = [this.Barbs, this.Pallys, this.Sorcs]; - - for (let i = 0; i < array.length; i++) { - let current = array[i]; - - switch (i) { - case 0: - quitCheck = getParty(this.Jobs.Barb); - !quitCheck && (this.Jobs.Barb = ""); - - if (current.length > 0) { - this.Jobs.Barb = current[0].name; - //print ("setting leader Barb to: " + AutoAssign.Jobs.Barb); - } - break; - case 1: - quitCheck = getParty(this.Jobs.Pally); - !quitCheck && (this.Jobs.Pally = ""); - - if (current.length > 0) { - this.Jobs.Pally = current[0].name; - //print ("setting leader Pally to: " + AutoAssign.Jobs.Pally); - } - break; - case 2: - quitCheck = getParty(this.Jobs.Sorc); - !quitCheck && (this.Jobs.Sorc = ""); - - if (current.length > 0) { - this.Jobs.Sorc = current[0].name; - //print ("setting leader Sorc to: " + AutoAssign.Jobs.Sorc); - } - break; - } - - for (let y = 0; y < current.length; y++) { - if (current[y].name === me.name) { - this.Jobs.Mine = y; - } - } - } - return true; - }, - - pushNames: function (name, level, classid) { - let obj = {name: name, level: level}; - - switch (classid) { - case sdk.player.class.Sorceress: - this.Sorcs.push(obj); - break; - case sdk.player.class.Paladin: - this.Pallys.push(obj); - break; - case sdk.player.class.Barbarian: - this.Barbs.push(obj); - break; - } - return true; - }, - - checkNames: function (name, type) { - let i, timeout = 1000; - - for (i = 0; i < type.length; i++) { - if (type[i].name === name) { - break; - } - } - - if (i === type.length) { - D2Bot.shoutGlobal(name, 69); - let tick = getTickCount(); - request = true; - - while (!answer) { - if (getTickCount() - tick > timeout) { - break; - } - delay (100); - } - } - - if (answer) { - answer = false; - request = false; - return true; - } - - answer = false; - request = false; - - return false; - }, - - sortNames: function () { - let arrays = [this.Barbs, this.Pallys, this.Sorcs]; - - for (let i = 0; i < arrays.length; i++) { - let type = arrays[i]; - - type.sort(function (a, b) { - if (a.name > b.name) return 1; - if (a.name < b.name) return -1; - return 0; - }); - - type.sort(function (a, b) { - return b.level - a.level; - }); - - } - return true; - }, - - removeNames: function (quitter) { - print(quitter + " has left. updating.."); - let arrays = [this.Barbs, this.Pallys, this.Sorcs]; - - for (let i = 0; i < arrays.length; i++) { - let currentClass = arrays[i]; - - for (let y = 0; y < currentClass.length; y++) { - if (currentClass[y].name === quitter) { - currentClass.splice(y, 1); - } - } - } - return true; - }, - - getNames: function () { - print("Updating names."); - - for (let i = 0; i < 3; i++) { - let party = getParty(); - - if (party) { - do { - switch (party.classid) { - case sdk.player.class.Sorceress: - if (this.checkNames(party.name, this.Sorcs)) { - this.pushNames(party.name, party.level, party.classid); - } - - break; - case sdk.player.class.Paladin: - if (this.checkNames(party.name, this.Pallys)) { - this.pushNames(party.name, party.level, party.classid); - } - - break; - case sdk.player.class.Barbarian: - if (this.checkNames(party.name, this.Barbs)) { - this.pushNames(party.name, party.level, party.classid); - } - - break; - default: - break; - } - } while (party.getNext()); - } - } - - this.sortNames(); - this.getJobs(); - - return this.Jobs; - }, - - updateNames: function (quitter) { - if (this.recursion) { - this.recursion = false; - try { - quitter && this.removeNames(quitter); - this.getNames(); - } finally { - this.recursion = true; - } - } - return true; - } -}; - //addEventListener("scriptmsg", AutoAssign.ScriptMsgEvent); -addEventListener("copydata", AutoAssign.receiveCopyData); -addEventListener("gameevent", AutoAssign.gameEvent); diff --git a/d2bs/kolbot/libs/common/Common.js b/d2bs/kolbot/libs/common/Common.js deleted file mode 100644 index 68df34b66..000000000 --- a/d2bs/kolbot/libs/common/Common.js +++ /dev/null @@ -1,1478 +0,0 @@ -/** -* @filename Common.js -* @author theBGuy -* @desc collection of functions shared between muliple scripts -* -*/ - -const Common = { - Questing: { - activateStone: function (stone) { - for (let i = 0; i < 3; i++) { - // don't use tk if we are right next to it - let useTK = (stone.distance > 5 && Skill.useTK(stone) && i === 0); - if (useTK) { - stone.distance > 13 && Attack.getIntoPosition(stone, 13, sdk.collision.Ranged); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stone)) { - console.debug("Failed to tk: attempt: " + i); - continue; - } - } else { - [(stone.x + 1), (stone.y + 2)].distance > 5 && Pather.moveTo(stone.x + 1, stone.y + 2, 3); - Misc.click(0, 0, stone); - } - - if (Misc.poll(() => stone.mode, 1000, 50)) { - return true; - } else { - 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; - }, - - cain: function () { - MainLoop: - while (true) { - switch (true) { - case !Game.getItem(sdk.quest.item.ScrollofInifuss) && !Game.getItem(sdk.quest.item.KeytotheCairnStones) && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4): - Pather.useWaypoint(sdk.areas.DarkWood, true); - Precast.doPrecast(true); - - if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - throw new Error("Failed to move to Tree of Inifuss"); - } - - let tree = Game.getObject(sdk.quest.chest.InifussTree); - !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Misc.openChest(tree); - let scroll = Misc.poll(() => Game.getItem(sdk.quest.item.ScrollofInifuss), 1000, 100); - - Pickit.pickItem(scroll); - Town.goToTown(); - Town.npcInteract("Akara"); - - break; - case Game.getItem(sdk.quest.item.ScrollofInifuss): - Town.goToTown(1); - Town.npcInteract("Akara"); - - break; - case Game.getItem(sdk.quest.item.KeytotheCairnStones) && !me.inArea(sdk.areas.StonyField): - Pather.journeyTo(sdk.areas.StonyField); - Precast.doPrecast(true); - - break; - case Game.getItem(sdk.quest.item.KeytotheCairnStones) && me.inArea(sdk.areas.StonyField): - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); - Attack.securePosition(me.x, me.y, 40, 3000, true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); - let stones = [ - Game.getObject(sdk.quest.chest.StoneAlpha), - Game.getObject(sdk.quest.chest.StoneBeta), - Game.getObject(sdk.quest.chest.StoneGamma), - Game.getObject(sdk.quest.chest.StoneDelta), - Game.getObject(sdk.quest.chest.StoneLambda) - ]; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (this.activateStone(stone)) { - stones.splice(i, 1); - i--; - } - delay(10); - } - } - - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram)) { - Pather.usePortal(sdk.areas.Tristram); - - break; - } - } - - break; - case me.inArea(sdk.areas.Tristram) && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed): - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { - Town.goToTown(1); - Town.npcInteract("Akara") && console.log("Akara done"); - } - } - - break; - default: - break MainLoop; - } - } - - return true; - }, - - smith: function () { - if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { - throw new Error("Failed to move to the Smith"); - } - - Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); - let malusChest = Game.getObject(sdk.quest.chest.MalusHolder); - !!malusChest && malusChest.distance > 5 && Pather.moveToUnit(malusChest); - Misc.openChest(malusChest); - let malus = Misc.poll(() => Game.getItem(sdk.quest.item.HoradricMalus), 1000, 100); - Pickit.pickItem(malus); - Town.goToTown(); - Town.npcInteract("Charsi"); - } - }, - - Cows: { - buildCowRooms: function () { - let finalRooms = []; - let indexes = []; - - let kingPreset = Game.getPresetMonster(sdk.areas.MooMooFarm, sdk.monsters.preset.TheCowKing); - let badRooms = getRoom(kingPreset.roomx * 5 + kingPreset.x, kingPreset.roomy * 5 + kingPreset.y).getNearby(); - - for (let i = 0; i < badRooms.length; i += 1) { - let badRooms2 = badRooms[i].getNearby(); - - for (let j = 0; j < badRooms2.length; j += 1) { - if (indexes.indexOf(badRooms2[j].x + "" + badRooms2[j].y) === -1) { - indexes.push(badRooms2[j].x + "" + badRooms2[j].y); - } - } - } - - let room = getRoom(); - - do { - if (indexes.indexOf(room.x + "" + room.y) === -1) { - finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); - } - } while (room.getNext()); - - return finalRooms; - }, - - clearCowLevel: function () { - function roomSort(a, b) { - return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); - } - - Config.MFLeader && Pather.makePortal() && say("cows"); - - let myRoom; - let rooms = this.buildCowRooms(); - - 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); - let room = rooms.shift(); - let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); - - if (result) { - Pather.moveTo(result[0], result[1], 3); - if (!Attack.clear(30)) return false; - } - } - - return true; - }, - }, - - Diablo: { - diabloSpawned: false, - diaWaitTime: Time.seconds(30), - clearRadius: 30, - done: false, - waitForGlow: false, - sealOrder: [], - vizLayout: -1, - seisLayout: -1, - infLayout: -1, - entranceCoords: {x: 7790, y: 5544}, - starCoords: {x: 7791, y: 5293}, - // path coordinates - entranceToStar: [7794, 5517, 7791, 5491, 7768, 5459, 7775, 5424, 7817, 5458, 7777, 5408, 7769, 5379, 7777, 5357, 7809, 5359, 7805, 5330, 7780, 5317, 7791, 5293], - starToVizA: [7759, 5295, 7734, 5295, 7716, 5295, 7718, 5276, 7697, 5292, 7678, 5293, 7665, 5276, 7662, 5314], - starToVizB: [7759, 5295, 7734, 5295, 7716, 5295, 7701, 5315, 7666, 5313, 7653, 5284], - starToSeisA: [7781, 5259, 7805, 5258, 7802, 5237, 7776, 5228, 7775, 5205, 7804, 5193, 7814, 5169, 7788, 5153], - starToSeisB: [7781, 5259, 7805, 5258, 7802, 5237, 7776, 5228, 7811, 5218, 7807, 5194, 7779, 5193, 7774, 5160, 7803, 5154], - starToInfA: [7809, 5268, 7834, 5306, 7852, 5280, 7852, 5310, 7869, 5294, 7895, 5295, 7919, 5290], - starToInfB: [7809, 5268, 7834, 5306, 7852, 5280, 7852, 5310, 7869, 5294, 7895, 5274, 7927, 5275, 7932, 5297, 7923, 5313], - // check for strays array - cleared: [], - - diabloLightsEvent: function (bytes = []) { - if (me.inArea(sdk.areas.ChaosSanctuary) && bytes && bytes.length === 2 && bytes[0] === 0x89 && bytes[1] === 0x0C) { - Common.Diablo.diabloSpawned = true; - } - }, - - sort: function (a, b) { - if (Config.BossPriority) { - if ((a.isSuperUnique) && (b.isSuperUnique)) return getDistance(me, a) - getDistance(me, b); - if (a.isSuperUnique) return -1; - if (b.isSuperUnique) return 1; - } - - // Entrance to Star / De Seis - if (me.y > 5325 || me.y < 5260) return (a.y > b.y ? -1 : 1); - // Vizier - if (me.x < 7765) return (a.x > b.x ? -1 : 1); - // Infector - if (me.x > 7825) return (!checkCollision(me, a, sdk.collision.BlockWall) && a.x < b.x ? -1 : 1); - - return getDistance(me, a) - getDistance(me, b); - }, - - getLayout: function (seal, value) { - let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); - if (!seal) throw new Error("Seal preset not found. Can't continue."); - - if (sealPreset.roomy * 5 + sealPreset.y === value - || sealPreset.roomx * 5 + sealPreset.x === value) { - return 1; - } - - return 2; - }, - - initLayout: function () { - // 1 = "Y", 2 = "L" - this.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); - // 1 = "2", 2 = "5" - this.seisLayout = this.getLayout(sdk.objects.DiabloSealSeis, 7773); - // 1 = "I", 2 = "J" - this.infLayout = this.getLayout(sdk.objects.DiabloSealInfector, 7893); - }, - - followPath: function (path) { - if (Config.Diablo.Fast) { - let len = path.length; - let lastNode = {x: path[len - 2], y: path[len - 1]}; - Pather.moveToUnit(lastNode); - return; - } - - for (let i = 0; i < path.length; i += 2) { - this.cleared.length > 0 && this.clearStrays(); - - // no monsters at the next node, skip it - if ([path[i], path[i + 1]].distance < 40 && [path[i], path[i + 1]].mobCount({range: 35}) === 0) { - continue; - } - - Pather.moveTo(path[i], path[i + 1], 3, getDistance(me, path[i], path[i + 1]) > 50); - Attack.clear(this.clearRadius, 0, false, Common.Diablo.sort); - - // Push cleared positions so they can be checked for strays - this.cleared.push([path[i], path[i + 1]]); - - // After 5 nodes go back 2 nodes to check for monsters - if (i === 10 && path.length > 16) { - path = path.slice(6); - i = 0; - } - } - }, - - clearStrays: function () { - let oldPos = { x: me.x, y: me.y }; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable) { - for (let i = 0; i < this.cleared.length; i += 1) { - if (getDistance(monster, this.cleared[i][0], this.cleared[i][1]) < 30 && Attack.validSpot(monster.x, monster.y)) { - Pather.moveToUnit(monster); - Attack.clear(15, 0, false, Common.Diablo.sort); - - break; - } - } - } - } while (monster.getNext()); - } - - getDistance(me, oldPos.x, oldPos.y) > 5 && Pather.moveTo(oldPos.x, oldPos.y); - - return true; - }, - - runSeals: function (sealOrder, openSeals = true) { - print("seal order: " + sealOrder); - this.sealOrder = sealOrder; - let seals = { - 1: () => this.vizierSeal(openSeals), - 2: () => this.seisSeal(openSeals), - 3: () => this.infectorSeal(openSeals), - "vizier": () => this.vizierSeal(openSeals), - "seis": () => this.seisSeal(openSeals), - "infector": () => this.infectorSeal(openSeals), - }; - sealOrder.forEach(seal => {seals[seal]();}); - }, - - tkSeal: function (seal) { - if (!Skill.useTK(seal)) return false; - - for (let i = 0; i < 5; i++) { - seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); - - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, seal) && Misc.poll(() => seal.mode, 1000, 100)) { - break; - } - } - - return !!seal.mode; - }, - - openSeal: function (classid) { - let seal; - let warn = Config.PublicMode && [sdk.objects.DiabloSealVizier, sdk.objects.DiabloSealSeis, sdk.objects.DiabloSealInfector].includes(classid) && Loader.scriptName() === "Diablo"; - let usetk = (Skill.haveTK && (classid !== sdk.objects.DiabloSealSeis || this.seisLayout !== 1)); - let seisSeal = classid === sdk.objects.DiabloSealSeis; - - for (let i = 0; i < 5; i++) { - if (!seal) { - usetk - ? Pather.moveNearPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, 15) - : Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, seisSeal ? 5 : 2, seisSeal ? 5 : 0); - seal = Misc.poll(() => Game.getObject(classid), 1000, 100); - } - - if (!seal) { - console.debug("Couldn't find seal: " + classid); - return false; - } - - if (seal.mode) { - warn && say(Config.Diablo.SealWarning); - return true; - } - - // Clear around Infector seal, Any leftover abyss knights casting decrep is bad news with Infector - if (([sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealInfector2].includes(classid) || i > 1) && me.getMobCount() > 1) { - Attack.clear(15); - // Move back to seal - usetk ? Pather.moveNearUnit(seal, 15) : Pather.moveToUnit(seal, seisSeal ? 5 : 2, seisSeal ? 5 : 0); - } - - if (usetk && this.tkSeal(seal)) { - return seal.mode; - } else { - usetk && (usetk = false); - - if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({x: 7899, y: 5293}); - check && ClassAttack.placeTraps({x: 7899, y: 5293}, check); - } - } - - seisSeal ? Misc.poll(function () { - // stupid diablo shit, walk around the de-seis seal clicking it until we find "the spot"...sigh - if (!seal.mode) { - Pather.walkTo(seal.x + (rand(-1, 1)), seal.y + (rand(-1, 1))); - clickUnitAndWait(0, 0, seal) || seal.interact(); - } - return !!seal.mode; - }, 3000, 60) : seal.interact(); - - // de seis optimization - if (seisSeal && Attack.validSpot(seal.x + 15, seal.y)) { - Pather.walkTo(seal.x + 15, seal.y); - } else { - Pather.walkTo(seal.x - 5, seal.y - 5); - } - } - - delay(seisSeal ? 1000 + me.ping : 500 + me.ping); - - if (seal.mode) { - break; - } - } - - return (!!seal && seal.mode); - }, - - vizierSeal: function (openSeal = true) { - print("Viz layout " + Common.Diablo.vizLayout); - let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); - let distCheck = { - x: path[path.length - 2], - y: (path.last()) - }; - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } - - distCheck.distance > 30 && this.followPath(Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); - - if (openSeal && (!Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2) || !Common.Diablo.openSeal(sdk.objects.DiabloSealVizier))) { - throw new Error("Failed to open Vizier seals."); - } - - delay(1 + me.ping); - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7691, 5292) : Pather.moveTo(7695, 5316); - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { - throw new Error("Failed to kill Vizier"); - } - - Config.Diablo.SealLeader && say("out"); - - return true; - }, - - seisSeal: function (openSeal = true) { - print("Seis layout " + Common.Diablo.seisLayout); - let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); - let distCheck = { - x: path[path.length - 2], - y: (path.last()) - }; - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } - - distCheck.distance > 30 && this.followPath(Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); - - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealSeis)) throw new Error("Failed to open de Seis seal."); - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7798, 5194) : Pather.moveTo(7796, 5155); - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) throw new Error("Failed to kill de Seis"); - - Config.Diablo.SealLeader && say("out"); - - return true; - }, - - infectorSeal: function (openSeal = true) { - Precast.doPrecast(true); - print("Inf layout " + Common.Diablo.infLayout); - let path = (Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - let distCheck = { - x: path[path.length - 2], - y: (path.last()) - }; - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } - - distCheck.distance > 70 && this.followPath(Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - - if (Config.Diablo.Fast) { - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); - - if (Common.Diablo.infLayout === 1) { - (me.sorceress || me.assassin) && Pather.moveTo(7876, 5296); - delay(1 + me.ping); - } else { - delay(1 + me.ping); - Pather.moveTo(7928, 5295); - } - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); - } else { - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); - - if (Common.Diablo.infLayout === 1) { - (me.sorceress || me.assassin) && Pather.moveTo(7876, 5296); - delay(1 + me.ping); - } else { - delay(1 + me.ping); - Pather.moveTo(7928, 5295); - } - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); - // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss - !openSeal && [3, "infector"].includes(Common.Diablo.sealOrder.last()) && Misc.poll(() => { - if (Common.Diablo.diabloSpawned) return true; - - let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); - if (lastSeal && lastSeal.mode) { - return true; - } - return false; - }, Time.minutes(3), 1000); - } - - Config.Diablo.SealLeader && say("out"); - - return true; - }, - - hammerdinPreAttack: function (name, amount = 5) { - if (me.paladin && Config.AttackSkill[1] === sdk.skills.BlessedHammer) { - let target = Game.getMonster(name); - - if (!target || !target.attackable) return true; - - let positions = [[6, 11], [0, 8], [8, -1], [-9, 2], [0, -11], [8, -8]]; - - for (let i = 0; i < positions.length; i += 1) { - // check if we can move there - if (Attack.validSpot(target.x + positions[i][0], target.y + positions[i][1])) { - Pather.moveTo(target.x + positions[i][0], target.y + positions[i][1]); - Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - - for (let n = 0; n < amount; n += 1) { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); - } - - return true; - } - } - } - - return false; - }, - - preattack: function (id) { - let coords = (() => { - switch (id) { - case getLocaleString(sdk.locale.monsters.GrandVizierofChaos): - return Common.Diablo.vizLayout === 1 ? [7676, 5295] : [7684, 5318]; - case getLocaleString(sdk.locale.monsters.LordDeSeis): - return Common.Diablo.seisLayout === 1 ? [7778, 5216] : [7775, 5208]; - case getLocaleString(sdk.locale.monsters.InfectorofSouls): - return Common.Diablo.infLayout === 1 ? [7913, 5292] : [7915, 5280]; - default: - return []; - } - })(); - if (!coords.length) return false; - - switch (me.classid) { - case sdk.player.class.Sorceress: - if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { - me.skillDelay && delay(500); - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, coords[0], coords[1]); - - return true; - } - - break; - case sdk.player.class.Paladin: - return this.hammerdinPreAttack(id, 8); - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let trapCheck = ClassAttack.checkTraps({x: coords[0], y: coords[1]}); - - if (trapCheck) { - ClassAttack.placeTraps({x: coords[0], y: coords[1]}, 5); - - return true; - } - } - - break; - } - - return false; - }, - - getBoss: function (name) { - let glow = Game.getObject(sdk.objects.SealGlow); - - if (this.waitForGlow) { - while (true) { - if (!this.preattack(name)) { - delay(500); - } - - glow = Game.getObject(sdk.objects.SealGlow); - - if (glow) { - break; - } - } - } - - for (let i = 0; i < 16; i += 1) { - let boss = Game.getMonster(name); - - if (boss) { - Common.Diablo.hammerdinPreAttack(name, 8); - return (Config.Diablo.Fast ? Attack.kill(name) : Attack.clear(40, 0, name, this.sort)); - } - - delay(250); - } - - return !!glow; - }, - - moveToStar: function () { - switch (me.classid) { - case sdk.player.class.Amazon: - case sdk.player.class.Sorceress: - case sdk.player.class.Necromancer: - case sdk.player.class.Assassin: - return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), {returnSpotOnError: true}); - case sdk.player.class.Paladin: - case sdk.player.class.Druid: - case sdk.player.class.Barbarian: - return Pather.moveTo(7788, 5292); - } - - return false; - }, - - diabloPrep: function () { - if (Config.Diablo.SealLeader) { - Pather.moveTo(7763, 5267); - Pather.makePortal() && say("in"); - Pather.moveTo(7788, 5292); - } - - this.moveToStar(); - - let tick = getTickCount(); - - while (getTickCount() - tick < this.diaWaitTime) { - if (getTickCount() - tick >= Time.seconds(8)) { - switch (me.classid) { - 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); - - break; - } - } else { - delay(500); - } - - if (Game.getMonster(sdk.monsters.Diablo)) { - return true; - } - } - - throw new Error("Diablo not found"); - }, - }, - - Ancients: { - altarSpot: {x: 10047, y: 12622}, - - canAttack: function () { - let ancient = Game.getMonster(); - - if (ancient) { - do { - if (!ancient.getParent() && !Attack.canAttack(ancient)) { - console.log("Can't attack ancients"); - return false; - } - } while (ancient.getNext()); - } - - return true; - }, - - touchAltar: function () { - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (Game.getObject(sdk.objects.AncientsAltar)) { - break; - } - - delay(20 + me.ping); - } - - let altar = Game.getObject(sdk.objects.AncientsAltar); - - if (altar) { - while (altar.mode !== sdk.objects.mode.Active) { - Pather.moveToUnit(altar); - altar.interact(); - delay(200 + me.ping); - me.cancel(); - } - - // wait for ancients to spawn - while (!Game.getMonster(sdk.monsters.TalictheDefender)) { - delay(250 + me.ping); - } - - return true; - } - - return false; - }, - - checkStatues: function () { - let statues = getUnits(sdk.unittype.Object) - .filter(u => [sdk.objects.KorlictheProtectorStatue, sdk.objects.TalictheDefenderStatue, sdk.objects.MadawctheGuardianStatue].includes(u.classid) - && u.mode === sdk.objects.mode.Active); - return statues.length === 3; - }, - - checkCorners: function () { - let pos = [ - { x: 10036, y: 12592 }, { x: 10066, y: 12589 }, - { x: 10065, y: 12623 }, { x: 10058, y: 12648 }, - { x: 10040, y: 12660 }, { x: 10036, y: 12630 }, - { x: 10038, y: 12611 } - ]; - Pather.moveToUnit(this.altarSpot); - if (!this.checkStatues()) { - return pos.forEach((node) => { - // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({range: 30}) === 0) { - return; - } - Pather.moveTo(node.x, node.y); - Attack.clear(30); - }); - } - - return true; - }, - - killAncients: function (checkQuest = false) { - let retry = 0; - Pather.moveToUnit(this.altarSpot); - - while (!this.checkStatues()) { - if (retry > 5) { - console.log("Failed to kill anicents."); - - break; - } - - Attack.clearClassids(sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian); - delay(1000); - - if (checkQuest) { - if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { - break; - } else { - console.log("Failed to kill anicents. Attempt: " + retry); - } - } - - this.checkCorners(); - retry++; - } - }, - - ancientsPrep: function () { - Town.goToTown(); - Town.fillTome(sdk.items.TomeofTownPortal); - [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion].forEach(p => Town.buyPots(10, p, true)); - Town.buyPotions(); - Pather.usePortal(sdk.areas.ArreatSummit, me.name); - }, - - startAncients: function (preTasks = false, checkQuest = false) { - let retry = 0; - Pather.moveToUnit(this.altarSpot); - this.touchAltar(); - - while (!this.canAttack()) { - if (retry > 10) throw new Error("I think I'm unable to complete ancients, I've rolled them 10 times"); - preTasks ? this.ancientsPrep() : Pather.makePortal(); - Pather.moveToUnit(this.altarSpot); - this.touchAltar(); - retry++; - } - - this.killAncients(checkQuest); - }, - }, - - Baal: { - checkHydra: function () { - let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - if (hydra) { - do { - if (hydra.mode !== sdk.monsters.mode.Dead && hydra.getStat(sdk.stats.Alignment) !== 2) { - Pather.moveTo(15072, 5002); - while (hydra.mode !== sdk.monsters.mode.Dead) { - delay(500); - if (!copyUnit(hydra).x) { - break; - } - } - - break; - } - } while (hydra.getNext()); - } - - return true; - }, - - checkThrone: function (clear = true) { - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable && monster.y < 5080) { - switch (monster.classid) { - case sdk.monsters.WarpedFallen: - case sdk.monsters.WarpedShaman: - return 1; - case sdk.monsters.BaalSubjectMummy: - case sdk.monsters.BaalColdMage: - return 2; - case sdk.monsters.Council4: - return 3; - case sdk.monsters.VenomLord2: - return 4; - case sdk.monsters.ListerTheTormenter: - return 5; - default: - if (clear) { - Attack.getIntoPosition(monster, 10, sdk.collision.Ranged); - Attack.clear(15); - } - - return false; - } - } - } while (monster.getNext()); - } - - return false; - }, - - clearThrone: function () { - if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - - let monList = []; - - if (Config.AvoidDolls) { - let mon = Game.getMonster(sdk.monsters.SoulKiller); - - if (mon) { - do { - // exclude dolls from the list - if (!mon.isDoll && mon.x >= 15072 && mon.x <= 15118 && mon.y >= 5002 && mon.y <= 5079 && mon.attackable && !Attack.skipCheck(mon)) { - monList.push(copyUnit(mon)); - } - } while (mon.getNext()); - } - - if (monList.length > 0) { - return Attack.clearList(monList); - } - } - - let pos = [ - { x: 15097, y: 5054 }, { x: 15079, y: 5014 }, - { x: 15085, y: 5053 }, { x: 15085, y: 5040 }, - { x: 15098, y: 5040 }, { x: 15099, y: 5022 }, - { x: 15086, y: 5024 }, { x: 15079, y: 5014 } - ]; - return pos.forEach((node) => { - // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({range: 30}) === 0) { - return; - } - Pather.moveTo(node.x, node.y); - Attack.clear(30); - }); - }, - - preattack: function () { - switch (me.classid) { - case sdk.player.class.Sorceress: - if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { - if (me.getState(sdk.states.SkillDelay)) { - delay(50); - } else { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5024); - } - } - - break; - case sdk.player.class.Paladin: - if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { - Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); - - return Skill.cast(Config.AttackSkill[3], 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[3], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5029); - - return true; - } - - break; - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({x: 15094, y: 5028}); - - if (check) { - return ClassAttack.placeTraps({x: 15094, y: 5028}, 5); - } - } - - if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { - return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); - } - - break; - } - - return false; - }, - - clearWaves: function () { - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - - let tick = getTickCount(); - let totalTick = getTickCount(); - - MainLoop: - while (true) { - if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - - switch (this.checkThrone()) { - case 1: - Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); - - break; - case 2: - Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); - - break; - case 3: - Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); - this.checkHydra() && (tick = getTickCount()); - - break; - case 4: - Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); - - break; - case 5: - Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2) && (tick = getTickCount()); - - 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)) { - this.clearThrone(); - tick = getTickCount(); - } - - if (!this.preattack()) { - delay(100); - } - - 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); - } - - this.clearThrone(); - - return true; - }, - - killBaal: function () { - if (me.inArea(sdk.areas.ThroneofDestruction)) { - Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); - me.checkForMobs({range: 30}) && this.clearWaves(); // ensure waves are actually done - Pather.moveTo(15090, 5008); - delay(5000); - Precast.doPrecast(true); - Misc.poll(() => { - if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({range: 15})) { - Common.Baal.clearThrone(); - Pather.moveTo(15090, 5008); - } - return !Game.getMonster(sdk.monsters.ThroneBaal); - }, Time.minutes(3), 1000); - - let portal = Game.getObject(sdk.objects.WorldstonePortal); - - if (portal) { - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } - } - - if (me.inArea(sdk.areas.WorldstoneChamber)) { - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); - - return true; - } - - return false; - } - }, - Toolsthread: { - pots: { - Health: 0, - Mana: 1, - Rejuv: 2, - MercHealth: 3, - MercRejuv: 4 - }, - pingTimer: [], - pauseScripts: [], - stopScripts: [], - timerLastDrink: [], - cloneWalked: false, - - checkPing: function (print = true) { - // Quit after at least 5 seconds in game - if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; - - for (let i = 0; i < Config.PingQuit.length; i += 1) { - if (Config.PingQuit[i].Ping > 0) { - if (me.ping >= Config.PingQuit[i].Ping) { - me.overhead("High Ping"); - - if (this.pingTimer[i] === undefined || this.pingTimer[i] === 0) { - this.pingTimer[i] = getTickCount(); - } - - if (getTickCount() - this.pingTimer[i] >= Config.PingQuit[i].Duration * 1000) { - print && D2Bot.printToConsole("High ping (" + me.ping + "/" + Config.PingQuit[i].Ping + ") - leaving game.", sdk.colors.D2Bot.Red); - scriptBroadcast("pingquit"); - scriptBroadcast("quit"); - - return true; - } - } else { - this.pingTimer[i] = 0; - } - } - } - - return false; - }, - - initQuitList: function () { - let temp = []; - - for (let i = 0; i < Config.QuitList.length; i += 1) { - if (FileTools.exists("data/" + Config.QuitList[i] + ".json")) { - let string = Misc.fileAction("data/" + Config.QuitList[i] + ".json", 0); - - if (string) { - let obj = JSON.parse(string); - - if (obj && obj.hasOwnProperty("name")) { - temp.push(obj.name); - } - } - } - } - - Config.QuitList = temp.slice(0); - }, - - togglePause: function () { - for (let i = 0; i < this.pauseScripts.length; i++) { - let script = getScript(this.pauseScripts[i]); - - if (script) { - if (script.running) { - this.pauseScripts[i] === "default.dbj" && console.log("ÿc1Pausing."); - - // don't pause townchicken during clone walk - if (this.pauseScripts[i] !== "tools/townchicken.js" || !this.cloneWalked) { - script.pause(); - } - } else { - this.pauseScripts[i] === "default.dbj" && console.log("ÿc2Resuming."); - script.resume(); - } - } - } - - return true; - }, - - stopDefault: function () { - for (let i = 0; i < this.stopScripts.length; i++) { - let script = getScript(this.stopScripts[i]); - !!script && script.running && script.stop(); - } - - return true; - }, - - exit: function (chickenExit = false) { - chickenExit && D2Bot.updateChickens(); - Config.LogExperience && Experience.log(); - console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); - this.stopDefault(); - quit(); - }, - - getPotion: function (pottype, type) { - if (!pottype) return false; - - let items = me.getItemsEx().filter((item) => item.itemType === pottype); - if (items.length === 0) return false; - - // Get highest id = highest potion first - items.sort(function (a, b) { - return b.classid - a.classid; - }); - - for (let i = 0; i < items.length; i += 1) { - if (type < this.pots.MercHealth && items[i].isInInventory && items[i].itemType === pottype) { - console.log("ÿc2Drinking potion from inventory."); - return copyUnit(items[i]); - } - - if (items[i].isInBelt && items[i].itemType === pottype) { - console.log("ÿc2" + (type > 2 ? "Giving Merc" : "Drinking") + " potion from belt."); - return copyUnit(items[i]); - } - } - - return false; - }, - - drinkPotion: function (type) { - if (type === undefined) return false; - let pottype, tNow = getTickCount(); - - switch (type) { - case this.pots.Health: - case this.pots.Mana: - if ((this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 1000)) || me.getState(type === this.pots.Health ? sdk.states.HealthPot : sdk.states.ManaPot)) { - return false; - } - - break; - case this.pots.Rejuv: - // small delay for juvs just to prevent using more at once - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 300)) { - return false; - } - - break; - case this.pots.MercRejuv: - // larger delay for juvs just to prevent using more at once, considering merc update rate - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 2000)) { - return false; - } - - break; - default: - if (this.timerLastDrink[type] && (tNow - this.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; - - switch (type) { - case this.pots.Health: - case this.pots.MercHealth: - pottype = sdk.items.type.HealingPotion; - - break; - case this.pots.Mana: - pottype = sdk.items.type.ManaPotion; - - break; - default: - pottype = sdk.items.type.RejuvPotion; - - break; - } - - let potion = this.getPotion(pottype, type); - - if (!!potion) { - if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; - - try { - type < this.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); - } catch (e) { - console.error(e); - } - - this.timerLastDrink[type] = getTickCount(); - - return true; - } - - return false; - }, - - checkVipers: function () { - let monster = Game.getMonster(sdk.monsters.TombViper2); - - if (monster) { - do { - if (monster.getState(sdk.states.Revive)) { - let owner = monster.getParent(); - - if (owner && owner.name !== me.name) { - D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); - - return true; - } - } - } while (monster.getNext()); - } - - return false; - }, - - getIronGolem: function () { - let golem = Game.getMonster(sdk.summons.IronGolem); - - if (golem) { - do { - let owner = golem.getParent(); - - if (owner && owner.name === me.name) { - return copyUnit(golem); - } - } while (golem.getNext()); - } - - return false; - }, - - getNearestPreset: function () { - let id; - let unit = getPresetUnits(me.area); - let dist = 99; - - for (let i = 0; i < unit.length; i += 1) { - if (getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y) < dist) { - dist = getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y); - id = unit[i].type + " " + unit[i].id; - } - } - - return id || ""; - }, - - getStatsString: function (unit) { - let realFCR = unit.getStat(sdk.stats.FCR); - let realIAS = unit.getStat(sdk.stats.IAS); - let realFBR = unit.getStat(sdk.stats.FBR); - let realFHR = unit.getStat(sdk.stats.FHR); - // me.getStat(sdk.stats.FasterCastRate) will return real FCR from gear + Config.FCR from char cfg - - if (unit === me) { - realFCR -= Config.FCR; - realIAS -= Config.IAS; - realFBR -= Config.FBR; - realFHR -= Config.FHR; - } - - let maxHellFireRes = 75 + unit.getStat(sdk.stats.MaxFireResist); - let hellFireRes = unit.getRes(sdk.stats.FireResist, sdk.difficulty.Hell); - hellFireRes > maxHellFireRes && (hellFireRes = maxHellFireRes); - - let maxHellColdRes = 75 + unit.getStat(sdk.stats.MaxColdResist); - let hellColdRes = unit.getRes(sdk.stats.ColdResist, sdk.difficulty.Hell); - hellColdRes > maxHellColdRes && (hellColdRes = maxHellColdRes); - - let maxHellLightRes = 75 + unit.getStat(sdk.stats.MaxLightResist); - let hellLightRes = unit.getRes(sdk.stats.LightResist, sdk.difficulty.Hell); - hellLightRes > maxHellLightRes && (hellLightRes = maxHellLightRes); - - let maxHellPoisonRes = 75 + unit.getStat(sdk.stats.MaxPoisonResist); - let hellPoisonRes = unit.getRes(sdk.stats.PoisonResist, sdk.difficulty.Hell); - hellPoisonRes > maxHellPoisonRes && (hellPoisonRes = maxHellPoisonRes); - - let str = - "ÿc4Character Level: ÿc0" + unit.charlvl + (unit === me ? " ÿc4Difficulty: ÿc0" + sdk.difficulty.nameOf(me.diff) + " ÿc4HighestActAvailable: ÿc0" + me.highestAct : "") + "\n" - + "ÿc1FR: ÿc0" + unit.getStat(sdk.stats.FireResist) + "ÿc1 Applied FR: ÿc0" + unit.fireRes - + "/ÿc3 CR: ÿc0" + unit.getStat(sdk.stats.ColdResist) + "ÿc3 Applied CR: ÿc0" + unit.coldRes - + "/ÿc9 LR: ÿc0" + unit.getStat(sdk.stats.LightResist) + "ÿc9 Applied LR: ÿc0" + unit.lightRes - + "/ÿc2 PR: ÿc0" + unit.getStat(sdk.stats.PoisonResist) + "ÿc2 Applied PR: ÿc0" + unit.poisonRes + "\n" - + (!me.hell ? "Hell res: ÿc1" + hellFireRes + "ÿc0/ÿc3" + hellColdRes + "ÿc0/ÿc9" + hellLightRes + "ÿc0/ÿc2" + hellPoisonRes + "ÿc0\n" : "") - + "ÿc4MF: ÿc0" + unit.getStat(sdk.stats.MagicBonus) + "ÿc4 GF: ÿc0" + unit.getStat(sdk.stats.GoldBonus) - + " ÿc4FCR: ÿc0" + realFCR + " ÿc4IAS: ÿc0" + realIAS + " ÿc4FBR: ÿc0" + realFBR - + " ÿc4FHR: ÿc0" + realFHR + " ÿc4FRW: ÿc0" + unit.getStat(sdk.stats.FRW) + "\n" - + "ÿc4CB: ÿc0" + unit.getStat(sdk.stats.CrushingBlow) + " ÿc4DS: ÿc0" + unit.getStat(sdk.stats.DeadlyStrike) - + " ÿc4OW: ÿc0" + unit.getStat(sdk.stats.OpenWounds) - + " ÿc1LL: ÿc0" + unit.getStat(sdk.stats.LifeLeech) + " ÿc3ML: ÿc0" + unit.getStat(sdk.stats.ManaLeech) - + " ÿc8DR: ÿc0" + unit.getStat(sdk.stats.DamageResist) + "% + " + unit.getStat(sdk.stats.NormalDamageReduction) - + " ÿc8MDR: ÿc0" + unit.getStat(sdk.stats.MagicResist) + "% + " + unit.getStat(sdk.stats.MagicDamageReduction) + "\n" - + (unit.getStat(sdk.stats.CannotbeFrozen) > 0 ? "ÿc3Cannot be Frozenÿc1\n" : "\n"); - - return str; - }, - }, - - Leecher: { - leadTick: 0, - leader: null, - killLeaderTracker: false, - currentScript: "", - nextScriptAreas: [sdk.areas.TowerCellarLvl5, sdk.areas.PitLvl1, sdk.areas.PitLvl2, sdk.areas.BurialGrounds, - sdk.areas.CatacombsLvl4, sdk.areas.MooMooFarm, sdk.areas.DuranceofHateLvl3, - sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber - ], - - leaderTracker: function () { - if (Common.Leecher.killLeaderTracker) return false; - // check every 3 seconds - if (getTickCount() - Common.Leecher.leadTick < 3000) return true; - Common.Leecher.leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - - let party = getParty(Common.Leecher.leader); - - if (party) { - // Player has moved on to another script - if (Common.Leecher.nextScriptAreas.includes(party.area)) { - if (Loader.scriptName() === Common.Leecher.currentScript) { - Common.Leecher.killLeaderTracker = true; - throw new Error("Party leader is running a new script"); - } else { - // kill process - return false; - } - } - } - - return true; - } - } -}; diff --git a/d2bs/kolbot/libs/common/Item.js b/d2bs/kolbot/libs/common/Item.js deleted file mode 100644 index efa80e75f..000000000 --- a/d2bs/kolbot/libs/common/Item.js +++ /dev/null @@ -1,240 +0,0 @@ -/** -* @filename Item.js -* @author kolton, theBGuy -* @desc handle item and autoequip related things -* -*/ - -// torn on if this should be broken up in two classes Item and AutoEquip, for now leaving as is -const Item = { - hasTier: function (item) { - return Config.AutoEquip && NTIP.GetTier(item) > 0; - }, - - canEquip: function (item) { - // Not an item or unid - if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; - // Higher requirements - if (item.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) || item.dexreq > me.getStat(sdk.stats.Dexterity) || item.strreq > me.getStat(sdk.stats.Strength)) return false; - - return true; - }, - - // Equips an item and throws away the old equipped 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.isInStash && !Town.openStash()) return false; - - 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) { - if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { - cursorItem.drop(); - } - } - } - - return true; - } - } - } - - return false; - }, - - getEquippedItem: function (bodyLoc) { - let item = me.getItem(); - - if (item) { - do { - if (item.bodylocation === bodyLoc) { - return { - classid: item.classid, - tier: NTIP.GetTier(item) - }; - } - } while (item.getNext()); - } - - // Don't have anything equipped in there - return { - classid: -1, - tier: -1 - }; - }, - - getBodyLoc: function (item) { - let bodyLoc; - - switch (item.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - case sdk.items.type.BowQuiver: - case sdk.items.type.CrossbowQuiver: - bodyLoc = sdk.body.LeftArm; - - break; - case sdk.items.type.Armor: - bodyLoc = sdk.body.Armor; - - break; - case sdk.items.type.Ring: - bodyLoc = [sdk.body.RingRight, sdk.body.RingLeft]; - - break; - case sdk.items.type.Amulet: - bodyLoc = sdk.body.Neck; - - break; - case sdk.items.type.Boots: - bodyLoc = sdk.body.Feet; - - break; - case sdk.items.type.Gloves: - bodyLoc = sdk.body.Gloves; - - break; - case sdk.items.type.Belt: - bodyLoc = sdk.body.Belt; - - break; - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - bodyLoc = sdk.body.Head; - - break; - 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: - bodyLoc = me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - - break; - case sdk.items.type.HandtoHand: - case sdk.items.type.AssassinClaw: - bodyLoc = me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - - break; - default: - return false; - } - - !Array.isArray(bodyLoc) && (bodyLoc = [bodyLoc]); - - return bodyLoc; - }, - - autoEquipCheck: function (item) { - if (!Config.AutoEquip) return true; - - let tier = NTIP.GetTier(item); - let bodyLoc = this.getBodyLoc(item); - - if (tier > 0 && bodyLoc) { - for (let i = 0; i < bodyLoc.length; i += 1) { - // Low tier items shouldn't be kept if they can't be equipped - if (tier > this.getEquippedItem(bodyLoc[i]).tier && (this.canEquip(item) || !item.getFlag(sdk.items.flags.Identified))) { - return true; - } - } - } - - // Sell/ignore low tier items, keep high tier - if (tier > 0 && tier < 100) return false; - - return true; - }, - - // returns true if the item should be kept+logged, false if not - autoEquip: function () { - if (!Config.AutoEquip) return true; - - let items = me.findItems(-1, sdk.items.mode.inStorage); - - if (!items) return false; - - function sortEq(a, b) { - if (Item.canEquip(a)) return -1; - if (Item.canEquip(b)) return 1; - - return 0; - } - - me.cancel(); - - // Remove items without tier - for (let i = 0; i < items.length; i += 1) { - if (NTIP.GetTier(items[i]) === 0) { - items.splice(i, 1); - - i -= 1; - } - } - - while (items.length > 0) { - items.sort(sortEq); - - let tier = NTIP.GetTier(items[0]); - let bodyLoc = this.getBodyLoc(items[0]); - - if (tier > 0 && bodyLoc) { - for (let j = 0; j < bodyLoc.length; j += 1) { - // khalim's will adjustment - const equippedItem = this.getEquippedItem(bodyLoc[j]); - if (items[0].isInStorage && tier > equippedItem.tier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { - if (!items[0].identified) { - let tome = me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (tome && tome.getStat(sdk.stats.Quantity) > 0) { - items[0].isInStash && Town.openStash(); - Town.identifyItem(items[0], tome); - } - } - - let gid = items[0].gid; - console.log(items[0].name); - - if (this.equip(items[0], bodyLoc[j])) { - Misc.logItem("Equipped", me.getItem(-1, -1, gid)); - } - - break; - } - } - } - - items.shift(); - } - - return true; - } -}; diff --git a/d2bs/kolbot/libs/common/Misc.js b/d2bs/kolbot/libs/common/Misc.js deleted file mode 100644 index 8fe699b20..000000000 --- a/d2bs/kolbot/libs/common/Misc.js +++ /dev/null @@ -1,2742 +0,0 @@ -/** -* @filename Misc.js -* @author kolton, theBGuy -* @desc misc library containing Skill, Misc and Sort classes -* -*/ - -includeIfNotIncluded("common/Item.js"); - -const Skill = { - usePvpRange: false, - skills: { - initialized: false, - all: [], - addSkills: function (skill, condition = () => true) { - this.all[skill] = { - hardpoints: false, - checked: false, - condition: condition, - have: function () { - if (!this.condition()) return false; - if (skill === undefined) return false; - if (this.hardpoints) return true; - if (!this.checked) { - this.hardpoints = !!me.getSkill(skill, sdk.skills.subindex.HardPoints); - this.checked = true; - } - return (this.hardpoints || me.getSkill(skill, sdk.skills.subindex.SoftPoints)); - } - }; - }, - init: function () { - this.addSkills(sdk.skills.MagicArrow); - this.addSkills(sdk.skills.FireArrow); - this.addSkills(sdk.skills.InnerSight, () => Config.UseInnerSight); - this.addSkills(sdk.skills.Jab); - this.addSkills(sdk.skills.ColdArrow); - this.addSkills(sdk.skills.MultipleShot); - this.addSkills(sdk.skills.PowerStrike); - this.addSkills(sdk.skills.PoisonJavelin); - this.addSkills(sdk.skills.ExplodingArrow); - this.addSkills(sdk.skills.SlowMissiles, () => Config.UseSlowMissiles); - this.addSkills(sdk.skills.LightningBolt); - this.addSkills(sdk.skills.IceArrow); - this.addSkills(sdk.skills.GuidedArrow); - this.addSkills(sdk.skills.ChargedStrike); - this.addSkills(sdk.skills.Strafe); - this.addSkills(sdk.skills.ImmolationArrow); - this.addSkills(sdk.skills.Decoy, () => Config.UseDecoy); - this.addSkills(sdk.skills.Fend); - this.addSkills(sdk.skills.FreezingArrow); - this.addSkills(sdk.skills.Valkyrie, () => Config.SummonValkyrie); - this.addSkills(sdk.skills.LightningStrike); - this.addSkills(sdk.skills.LightningFury); - // sorceress skills start - this.addSkills(sdk.skills.FireBolt); - this.addSkills(sdk.skills.ChargedBolt); - this.addSkills(sdk.skills.IceBolt); - this.addSkills(sdk.skills.FrozenArmor); - this.addSkills(sdk.skills.Inferno); - this.addSkills(sdk.skills.StaticField); - this.addSkills(sdk.skills.Telekinesis, () => Config.UseTelekinesis); - this.addSkills(sdk.skills.FrostNova); - this.addSkills(sdk.skills.IceBlast); - this.addSkills(sdk.skills.Blaze); - this.addSkills(sdk.skills.FireBall); - this.addSkills(sdk.skills.Nova); - this.addSkills(sdk.skills.Lightning); - this.addSkills(sdk.skills.ShiverArmor); - this.addSkills(sdk.skills.FireWall); - this.addSkills(sdk.skills.Enchant); - this.addSkills(sdk.skills.ChainLightning); - this.addSkills(sdk.skills.Teleport); - this.addSkills(sdk.skills.GlacialSpike); - this.addSkills(sdk.skills.Meteor); - this.addSkills(sdk.skills.ThunderStorm); - this.addSkills(sdk.skills.EnergyShield, () => Config.UseEnergyShield); - this.addSkills(sdk.skills.Blizzard); - this.addSkills(sdk.skills.ChillingArmor); - this.addSkills(sdk.skills.Hydra); - this.addSkills(sdk.skills.FrozenOrb); - // necromancer skills start - this.addSkills(sdk.skills.AmplifyDamage); - this.addSkills(sdk.skills.Teeth); - this.addSkills(sdk.skills.BoneArmor); - this.addSkills(sdk.skills.RaiseSkeleton); - this.addSkills(sdk.skills.DimVision); - this.addSkills(sdk.skills.Weaken); - this.addSkills(sdk.skills.PoisonDagger); - this.addSkills(sdk.skills.CorpseExplosion); - this.addSkills(sdk.skills.ClayGolem); - this.addSkills(sdk.skills.IronMaiden); - this.addSkills(sdk.skills.Terror); - this.addSkills(sdk.skills.BoneWall); - this.addSkills(sdk.skills.RaiseSkeletalMage); - this.addSkills(sdk.skills.Confuse); - this.addSkills(sdk.skills.LifeTap); - this.addSkills(sdk.skills.PoisonExplosion); - this.addSkills(sdk.skills.BoneSpear); - this.addSkills(sdk.skills.BloodGolem); - this.addSkills(sdk.skills.Attract); - this.addSkills(sdk.skills.Decrepify); - this.addSkills(sdk.skills.BonePrison); - this.addSkills(sdk.skills.IronGolem); - this.addSkills(sdk.skills.LowerResist); - this.addSkills(sdk.skills.PoisonNova); - this.addSkills(sdk.skills.BoneSpirit); - this.addSkills(sdk.skills.FireGolem); - this.addSkills(sdk.skills.Revive); - // paladin skills start - this.addSkills(sdk.skills.Sacrifice); - this.addSkills(sdk.skills.Smite); - this.addSkills(sdk.skills.Might); - this.addSkills(sdk.skills.Prayer); - this.addSkills(sdk.skills.ResistFire); - this.addSkills(sdk.skills.HolyBolt); - this.addSkills(sdk.skills.HolyFire); - this.addSkills(sdk.skills.Thorns); - this.addSkills(sdk.skills.Defiance); - this.addSkills(sdk.skills.ResistCold); - this.addSkills(sdk.skills.Zeal); - this.addSkills(sdk.skills.Charge, () => Config.Charge); - this.addSkills(sdk.skills.BlessedAim); - this.addSkills(sdk.skills.Cleansing); - this.addSkills(sdk.skills.ResistLightning); - this.addSkills(sdk.skills.Vengeance); - this.addSkills(sdk.skills.BlessedHammer); - this.addSkills(sdk.skills.Concentration); - this.addSkills(sdk.skills.HolyFreeze); - this.addSkills(sdk.skills.Vigor, () => Config.Vigor || me.inTown); - this.addSkills(sdk.skills.Conversion); - this.addSkills(sdk.skills.HolyShield); - this.addSkills(sdk.skills.HolyShock); - this.addSkills(sdk.skills.Sanctuary); - this.addSkills(sdk.skills.Meditation); - this.addSkills(sdk.skills.FistoftheHeavens); - this.addSkills(sdk.skills.Fanaticism); - this.addSkills(sdk.skills.Conviction); - this.addSkills(sdk.skills.Redemption); - this.addSkills(sdk.skills.Salvation); - // barbarian skills start - this.addSkills(sdk.skills.Bash); - this.addSkills(sdk.skills.Howl); - this.addSkills(sdk.skills.FindPotion); - this.addSkills(sdk.skills.Leap); - this.addSkills(sdk.skills.DoubleSwing); - this.addSkills(sdk.skills.Taunt); - this.addSkills(sdk.skills.Shout); - this.addSkills(sdk.skills.Stun); - this.addSkills(sdk.skills.DoubleThrow); - this.addSkills(sdk.skills.FindItem, () => Config.FindItem); - this.addSkills(sdk.skills.LeapAttack); - this.addSkills(sdk.skills.BattleCry); - this.addSkills(sdk.skills.Frenzy); - this.addSkills(sdk.skills.BattleOrders); - this.addSkills(sdk.skills.GrimWard); - this.addSkills(sdk.skills.Whirlwind); - this.addSkills(sdk.skills.Berserk); - this.addSkills(sdk.skills.WarCry); - this.addSkills(sdk.skills.BattleCommand); - // druid skills start - this.addSkills(sdk.skills.Raven, () => Config.SummonRaven); - this.addSkills(sdk.skills.PoisonCreeper); - this.addSkills(sdk.skills.Werewolf); - this.addSkills(sdk.skills.Firestorm); - this.addSkills(sdk.skills.OakSage); - this.addSkills(sdk.skills.SpiritWolf); - this.addSkills(sdk.skills.Werebear); - this.addSkills(sdk.skills.MoltenBoulder); - this.addSkills(sdk.skills.ArcticBlast); - this.addSkills(sdk.skills.CarrionVine); - this.addSkills(sdk.skills.FeralRage); - this.addSkills(sdk.skills.Maul); - this.addSkills(sdk.skills.Fissure); - this.addSkills(sdk.skills.CycloneArmor); - this.addSkills(sdk.skills.HeartofWolverine); - this.addSkills(sdk.skills.SummonDireWolf); - this.addSkills(sdk.skills.Rabies); - this.addSkills(sdk.skills.FireClaws); - this.addSkills(sdk.skills.Twister); - this.addSkills(sdk.skills.SolarCreeper); - this.addSkills(sdk.skills.Hunger); - this.addSkills(sdk.skills.ShockWave); - this.addSkills(sdk.skills.Volcano); - this.addSkills(sdk.skills.Tornado); - this.addSkills(sdk.skills.SpiritofBarbs); - this.addSkills(sdk.skills.Grizzly); - this.addSkills(sdk.skills.Fury); - this.addSkills(sdk.skills.Armageddon); - this.addSkills(sdk.skills.Hurricane); - // assassin skills start - this.addSkills(sdk.skills.FireBlast); - this.addSkills(sdk.skills.PsychicHammer); - this.addSkills(sdk.skills.TigerStrike); - this.addSkills(sdk.skills.DragonTalon); - this.addSkills(sdk.skills.ShockWeb); - this.addSkills(sdk.skills.BladeSentinel); - this.addSkills(sdk.skills.BurstofSpeed, () => !Config.UseBoS && !me.inTown); - this.addSkills(sdk.skills.FistsofFire); - this.addSkills(sdk.skills.DragonClaw); - this.addSkills(sdk.skills.ChargedBoltSentry); - this.addSkills(sdk.skills.WakeofFire); - this.addSkills(sdk.skills.CloakofShadows); - this.addSkills(sdk.skills.CobraStrike); - this.addSkills(sdk.skills.BladeFury); - this.addSkills(sdk.skills.Fade, () => Config.UseFade); - this.addSkills(sdk.skills.ShadowWarrior); - this.addSkills(sdk.skills.ClawsofThunder); - this.addSkills(sdk.skills.DragonTail); - this.addSkills(sdk.skills.LightningSentry); - this.addSkills(sdk.skills.WakeofInferno); - this.addSkills(sdk.skills.MindBlast); - this.addSkills(sdk.skills.BladesofIce); - this.addSkills(sdk.skills.DragonFlight); - this.addSkills(sdk.skills.DeathSentry); - this.addSkills(sdk.skills.BladeShield, () => Config.UseBladeShield); - this.addSkills(sdk.skills.Venom, () => Config.UseVenom); - this.addSkills(sdk.skills.ShadowMaster); - this.addSkills(sdk.skills.PhoenixStrike); - this.addSkills(sdk.skills.WakeofDestructionSentry); - this.initialized = true; - }, - have: function (skill = 0) { - // ensure the values have been initialized - !this.initialized && this.init(); - return typeof this.all[skill] !== "undefined" && this.all[skill].have(); - }, - reset: function () { - let [min, max] = (() => { - switch (me.classid) { - case sdk.player.class.Amazon: - return [sdk.skills.MagicArrow, sdk.skills.LightningFury]; - case sdk.player.class.Sorceress: - return [sdk.skills.FireBolt, sdk.skills.ColdMastery]; - case sdk.player.class.Necromancer: - return [sdk.skills.AmplifyDamage, sdk.skills.Revive]; - case sdk.player.class.Paladin: - return [sdk.skills.Sacrifice, sdk.skills.Salvation]; - case sdk.player.class.Barbarian: - return [sdk.skills.Bash, sdk.skills.BattleCommand]; - case sdk.player.class.Druid: - return [sdk.skills.Raven, sdk.skills.Hurricane]; - case sdk.player.class.Assassin: - return [sdk.skills.FireBlast, sdk.skills.PhoenixStrike]; - default: - return [0, 0]; - } - })(); - - for (let i = min; i <= max; i++) { - if (typeof this.all[i] !== "undefined" && !this.all[i].hardpoints) { - this.all[i].checked = false; - } - } - } - }, - - // initialize our skill data - init: function () { - // reset check values - !Skill.skills.initialized ? Skill.skills.init() : Skill.skills.reset(); - // reset mana values - Skill.manaCostList = {}; - - switch (me.classid) { - case sdk.player.class.Amazon: - break; - case sdk.player.class.Sorceress: - if (Config.UseColdArmor === true) { - Precast.skills.coldArmor.best = (function () { - let coldArmor = [ - {skillId: sdk.skills.ShiverArmor, level: me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints)}, - {skillId: sdk.skills.ChillingArmor, level: me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints)}, - {skillId: sdk.skills.FrozenArmor, level: me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints)}, - ].filter(skill => !!skill.level && skill.level > 0).sort((a, b) => b.level - a.level).first(); - return coldArmor !== undefined ? coldArmor.skillId : false; - })(); - Precast.skills.coldArmor.duration = this.getDuration(Precast.skills.coldArmor.best); - } else { - Precast.skills.coldArmor.duration = this.getDuration(Config.UseColdArmor); - } - - break; - case sdk.player.class.Necromancer: - { - let bMax = me.getStat(sdk.stats.SkillBoneArmorMax); - bMax > 0 && (Precast.skills.boneArmor.max = bMax); - } - if (!!Config.Golem && Config.Golem !== "None") { - // todo: change Config.Golem to use skillid instead of 0, 1, 2, and 3 - } - break; - case sdk.player.class.Paladin: - // how to handle if someone manually equips a shield during game play, don't want to build entire item list if we don't need to - // maybe store gid of shield, would still require doing me.getItem(-1, 1, gid) everytime we wanted to cast but that's still less involved - // than getting every item we have and finding shield, for now keeping this. Checks during init if we have a shield or not - Precast.skills.holyShield.canUse = me.usingShield(); - - break; - case sdk.player.class.Barbarian: - Skill.canUse(sdk.skills.Shout) && (Precast.skills.shout.duration = this.getDuration(sdk.skills.Shout)); - Skill.canUse(sdk.skills.BattleOrders) && (Precast.skills.battleOrders.duration = this.getDuration(sdk.skills.BattleOrders)); - Skill.canUse(sdk.skills.BattleCommand) && (Precast.skills.battleCommand.duration = this.getDuration(sdk.skills.BattleCommand)); - - break; - case sdk.player.class.Druid: - if (!!Config.SummonAnimal && Config.SummonAnimal !== "None") { - // todo: change Config.SummonAnimal to use skillid instead of 0, 1, 2, and 3 - } - if (!!Config.SummonVine && Config.SummonVine !== "None") { - // todo: change Config.SummonVine to use skillid instead of 0, 1, 2, and 3 - } - if (!!Config.SummonSpirit && Config.SummonSpirit !== "None") { - // todo: change Config.SummonSpirit to use skillid instead of 0, 1, 2, and 3 - } - break; - case sdk.player.class.Assassin: - if (!!Config.SummonShadow) { - // todo: change Config.SummonShadow to use skillid instead of 0, 1, 2, and 3 - } - break; - } - }, - - canUse: function (skillId = -1) { - try { - if (skillId === -1) return false; - if (skillId >= sdk.skills.Attack && skillId <= sdk.skills.LeftHandSwing) return true; - let valid = Skill.skills.have(skillId); - - return valid; - } catch (e) { - return false; - } - }, - - getDuration: function (skillId = -1) { - switch (skillId) { - case sdk.skills.Decoy: - return ((10 + me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints) * 5) * 1000); - case sdk.skills.FrozenArmor: - return (((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) * 1000); - case sdk.skills.ShiverArmor: - return (((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) * 1000); - case sdk.skills.ChillingArmor: - return (((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) * 1000); - case sdk.skills.EnergyShield: - return (84 + (60 * me.getSkill(sdk.skills.EnergyShield, sdk.skills.subindex.SoftPoints)) * 1000); - case sdk.skills.ThunderStorm: - return (24 + (8 * me.getSkill(sdk.skills.ThunderStorm, sdk.skills.subindex.SoftPoints))) * 1000; - case sdk.skills.Shout: - return (((10 + me.getSkill(sdk.skills.Shout, sdk.skills.subindex.SoftPoints) * 10) + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) * 1000); - case sdk.skills.BattleOrders: - return (((20 + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) * 10) + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) * 1000); - case sdk.skills.BattleCommand: - return (((10 * me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.SoftPoints) - 5) + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints)) * 5)) * 1000); - case sdk.skills.HolyShield: - return (5 + (25 * me.getSkill(sdk.skills.HolyShield, sdk.skills.subindex.SoftPoints)) * 1000); - case sdk.skills.Hurricane: - return (10 + (2 * me.getSkill(sdk.skills.CycloneArmor, sdk.skills.subindex.HardPoints)) * 1000); - case sdk.skills.Werewolf: - case sdk.skills.Werebear: - return (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20) * 1000); - case sdk.skills.BurstofSpeed: - return (108 + (12 * me.getSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.SoftPoints)) * 1000); - case sdk.skills.Fade: - return (108 + (12 * me.getSkill(sdk.skills.Fade, sdk.skills.subindex.SoftPoints)) * 1000); - case sdk.skills.Venom: - return (116 + (4 * me.getSkill(sdk.skills.Venom, sdk.skills.subindex.SoftPoints)) * 1000); - case sdk.skills.BladeShield: - return (15 + (5 * me.getSkill(sdk.skills.BladeShield, sdk.skills.subindex.SoftPoints)) * 1000); - default: - return 0; - } - }, - - getMaxSummonCount: function (skillId) { - let skillNum = 0; - - switch (skillId) { - case sdk.skills.Raven: - return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 5); - case sdk.skills.SummonSpiritWolf: - return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 5); - case sdk.skills.SummonDireWolf: - return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 3); - case sdk.skills.RaiseSkeleton: - case sdk.skills.RaiseSkeletalMage: - skillNum = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); - return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); - case sdk.skills.Revive: - return me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints); - case sdk.skills.ShadowWarrior: - case sdk.skills.ShadowMaster: - case sdk.skills.PoisonCreeper: - case sdk.skills.CarrionVine: - case sdk.skills.SolarCreeper: - case sdk.skills.OakSage: - case sdk.skills.HeartofWolverine: - case sdk.skills.SpiritofBarbs: - case sdk.skills.SummonGrizzly: - case sdk.skills.ClayGolem: - case sdk.skills.BloodGolem: - case sdk.skills.FireGolem: - case sdk.skills.Valkyrie: - return 1; - } - - return 0; - }, - - getRange: function (skillId) { - switch (skillId) { - case sdk.skills.Attack: - return Attack.usingBow() ? 20 : 3; - case sdk.skills.Kick: - case sdk.skills.LeftHandSwing: - case sdk.skills.Jab: - case sdk.skills.PowerStrike: - case sdk.skills.ChargedStrike: - case sdk.skills.LightningStrike: - case sdk.skills.Impale: - case sdk.skills.Fend: - case sdk.skills.Blaze: - case sdk.skills.PoisonDagger: - case sdk.skills.Sacrifice: - case sdk.skills.Smite: - case sdk.skills.Zeal: - case sdk.skills.Vengeance: - case sdk.skills.Conversion: - case sdk.skills.BlessedHammer: - case sdk.skills.FindPotion: - case sdk.skills.FindItem: - case sdk.skills.GrimWard: - case sdk.skills.Bash: - case sdk.skills.DoubleSwing: - case sdk.skills.Stun: - case sdk.skills.Concentrate: - case sdk.skills.Frenzy: - case sdk.skills.Berserk: - case sdk.skills.FeralRage: - case sdk.skills.Maul: - case sdk.skills.Rabies: - case sdk.skills.FireClaws: - case sdk.skills.Hunger: - case sdk.skills.Fury: - case sdk.skills.DragonTalon: - case sdk.skills.DragonClaw: - case sdk.skills.DragonTail: - return 3; - case sdk.skills.BattleCry: - case sdk.skills.WarCry: - return 4; - case sdk.skills.FrostNova: - case sdk.skills.Twister: - case sdk.skills.Tornado: - case sdk.skills.Summoner: - return 5; - case sdk.skills.ChargedBolt: - return 6; - case sdk.skills.Nova: - case sdk.skills.Whirlwind: - return 7; - case sdk.skills.PoisonNova: - return 8; - case sdk.skills.Armageddon: - return 9; - case sdk.skills.PoisonJavelin: - case sdk.skills.PlagueJavelin: - case sdk.skills.HolyBolt: - case sdk.skills.Charge: - case sdk.skills.Howl: - case sdk.skills.Firestorm: - case sdk.skills.MoltenBoulder: - case sdk.skills.ShockWave: - return 10; - case sdk.skills.InnerSight: - case sdk.skills.SlowMissiles: - return 13; - case sdk.skills.LightningFury: - case sdk.skills.FrozenOrb: - case sdk.skills.Teeth: - case sdk.skills.Fissure: - case sdk.skills.Volcano: - case sdk.skills.FireBlast: - case sdk.skills.ShockWeb: - case sdk.skills.BladeSentinel: - case sdk.skills.BladeFury: - return 15; - case sdk.skills.FireArrow: - case sdk.skills.MultipleShot: - case sdk.skills.ExplodingArrow: - case sdk.skills.GuidedArrow: - case sdk.skills.ImmolationArrow: - case sdk.skills.FreezingArrow: - case sdk.skills.IceBolt: - case sdk.skills.IceBlast: - case sdk.skills.FireBolt: - case sdk.skills.Revive: - case sdk.skills.FistoftheHeavens: - case sdk.skills.DoubleThrow: - case sdk.skills.PsychicHammer: - case sdk.skills.DragonFlight: - return 20; - case sdk.skills.LowerResist: - return 50; - // Variable range - case sdk.skills.StaticField: - return Math.floor((me.getSkill(sdk.skills.StaticField, sdk.skills.subindex.SoftPoints) + 4) * 2 / 3); - case sdk.skills.Leap: - { - let skLvl = me.getSkill(sdk.skills.Leap, sdk.skills.subindex.SoftPoints); - return Math.floor(Math.min(4 + (26 * ((110 * skLvl / (skLvl + 6)) / 100)), 30) * (2 / 3)); - } - case sdk.skills.ArcticBlast: - { - let skLvl = me.getSkill(sdk.skills.ArcticBlast, sdk.skills.subindex.SoftPoints); - let range = Math.floor(((33 + (2 * skLvl)) / 4) * (2 / 3)); - // Druid using this on physical immunes needs the monsters to be within range of hurricane - range > 6 && Config.AttackSkill[5] === sdk.skills.ArcticBlast && (range = 6); - - return range; - } - case sdk.skills.Lightning: - case sdk.skills.BoneSpear: - case sdk.skills.BoneSpirit: - return !!this.usePvpRange ? 35 : 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 ? 35 : 20; - } - - // Every other skill - return !!this.usePvpRange ? 30 : 20; - }, - - needFloor: [ - sdk.skills.Blizzard, sdk.skills.Meteor, sdk.skills.Fissure, sdk.skills.Volcano, sdk.skills.ShockWeb, sdk.skills.LeapAttack, sdk.skills.Hydra - ], - - missileSkills: [ - sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.ColdArrow, sdk.skills.MultipleShot, sdk.skills.PoisonJavelin, sdk.skills.ExplodingArrow, - sdk.skills.LightningBolt, sdk.skills.IceArrow, sdk.skills.GuidedArrow, sdk.skills.PlagueJavelin, sdk.skills.Strafe, sdk.skills.ImmolationArrow, - sdk.skills.FreezingArrow, sdk.skills.LightningFury, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.FireBolt, sdk.skills.Inferno, - sdk.skills.IceBlast, sdk.skills.FireBall, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.GlacialSpike, sdk.skills.FrozenOrb, - sdk.skills.Teeth, sdk.skills.BoneSpear, sdk.skills.BoneSpirit, sdk.skills.HolyBolt, sdk.skills.FistoftheHeavens, sdk.skills.DoubleThrow, - sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, sdk.skills.Twister, sdk.skills.Tornado, sdk.skills.FireBlast - ], - - getHand: function (skillId) { - switch (skillId) { - case sdk.skills.MagicArrow: - case sdk.skills.FireArrow: - case sdk.skills.ColdArrow: - case sdk.skills.MultipleShot: - case sdk.skills.PoisonJavelin: - case sdk.skills.ExplodingArrow: - case sdk.skills.Impale: - case sdk.skills.LightningBolt: - case sdk.skills.IceArrow: - case sdk.skills.GuidedArrow: - case sdk.skills.PlagueJavelin: - case sdk.skills.Strafe: - case sdk.skills.ImmolationArrow: - case sdk.skills.Fend: - case sdk.skills.FreezingArrow: - case sdk.skills.LightningFury: - case sdk.skills.FireBolt: - case sdk.skills.ChargedBolt: - case sdk.skills.IceBolt: - case sdk.skills.Inferno: - case sdk.skills.IceBlast: - case sdk.skills.FireBall: - case sdk.skills.Lightning: - case sdk.skills.ChainLightning: - case sdk.skills.GlacialSpike: - case sdk.skills.FrozenOrb: - case sdk.skills.Teeth: - case sdk.skills.PoisonDagger: - case sdk.skills.BoneSpear: - case sdk.skills.BoneSpirit: - case sdk.skills.HolyBolt: - case sdk.skills.Charge: - case sdk.skills.BlessedHammer: - case sdk.skills.FistoftheHeavens: - case sdk.skills.Leap: - case sdk.skills.DoubleThrow: - case sdk.skills.LeapAttack: - case sdk.skills.Whirlwind: - case sdk.skills.Firestorm: - case sdk.skills.MoltenBoulder: - case sdk.skills.ArcticBlast: - case sdk.skills.Twister: - case sdk.skills.ShockWave: - case sdk.skills.Tornado: - case sdk.skills.FireBlast: - case sdk.skills.TigerStrike: - case sdk.skills.ShockWeb: - case sdk.skills.BladeSentinel: - case sdk.skills.FistsofFire: - case sdk.skills.CobraStrike: - case sdk.skills.BladeFury: - case sdk.skills.ClawsofThunder: - case sdk.skills.BladesofIce: - case sdk.skills.DragonFlight: - return sdk.skills.hand.Left; - case sdk.skills.Attack: - case sdk.skills.Jab: - case sdk.skills.PowerStrike: - case sdk.skills.ChargedStrike: - case sdk.skills.LightningStrike: - case sdk.skills.Sacrifice: - case sdk.skills.Smite: - case sdk.skills.Zeal: - case sdk.skills.Vengeance: - case sdk.skills.Conversion: - case sdk.skills.Bash: - case sdk.skills.DoubleSwing: - case sdk.skills.Stun: - case sdk.skills.Concentrate: - case sdk.skills.Frenzy: - case sdk.skills.Berserk: - case sdk.skills.FeralRage: - case sdk.skills.Maul: - case sdk.skills.Rabies: - case sdk.skills.FireClaws: - case sdk.skills.Hunger: - case sdk.skills.Fury: - case sdk.skills.DragonTalon: - case sdk.skills.DragonClaw: - case sdk.skills.DragonTail: - return sdk.skills.hand.LeftNoShift; // Shift bypass - } - - // Every other skill - return sdk.skills.hand.Right; - }, - - charges: [], - - // Cast a skill on self, Unit or coords - cast: function (skillId, hand, x, y, item) { - switch (true) { - case me.inTown && !this.townSkill(skillId): - case !item && (this.getManaCost(skillId) > me.mp || !this.canUse(skillId)): - case !this.wereFormCheck(skillId): - return false; - case skillId === undefined: - throw new Error("Unit.cast: Must supply a skill ID"); - } - - if (skillId === sdk.skills.Telekinesis && typeof x === "object" && Packet.telekinesis(x)) { - delay(250); - return true; - } - - 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)) { - delay(300); - } - - return false; - } - - if (skillId === sdk.skills.Teleport && typeof x === "number" && Packet.teleport(x, y)) { - delay(250); - return true; - } - - if (!this.setSkill(skillId, hand, item)) return false; - - if (Config.PacketCasting > 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 += 1) { - if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { - break; - } - - delay(10); - } - } - - return true; - }, - - // Put a skill on desired slot - setSkill: function (skillId, hand, item) { - // Check if the skill is already set - if (me.getSkill(hand === sdk.skills.hand.Right ? sdk.skills.get.RightId : sdk.skills.get.LeftId) === skillId) return true; - if (!item && !Skill.canUse(skillId)) return false; - - // Charged skills must be cast from right hand - if (hand === undefined || hand === sdk.skills.hand.RightShift || item) { - item && hand !== sdk.skills.hand.Right && console.warn("[ÿc9Warningÿc0] charged skills must be cast from right hand"); - hand = sdk.skills.hand.Right; - } - - return (me.setSkill(skillId, hand, item)); - }, - - // Timed skills - isTimed: function (skillId) { - return [ - sdk.skills.PoisonJavelin, sdk.skills.PlagueJavelin, sdk.skills.ImmolationArrow, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, - sdk.skills.Hydra, sdk.skills.FrozenOrb, sdk.skills.FistoftheHeavens, sdk.skills.Firestorm, sdk.skills.Werewolf, sdk.skills.Werebear, sdk.skills.MoltenBoulder, - sdk.skills.Fissure, sdk.skills.Volcano, sdk.skills.Grizzly, sdk.skills.Armageddon, sdk.skills.Hurricane, sdk.skills.ShockWeb, sdk.skills.ShadowWarrior, - sdk.skills.DragonFlight, sdk.skills.BladeShield, sdk.skills.ShadowMaster - ].includes(skillId); - }, - - // Wereform skill check - wereFormCheck: function (skillId) { - // we don't even have the skills to transform or we aren't transformed - add handler for wereform given by an item that is on switch - if (!Skill.canUse(sdk.skills.Werewolf) && !Skill.canUse(sdk.skills.Werebear)) return true; - if (!me.getState(sdk.states.Wearwolf) && !me.getState(sdk.states.Wearbear)) return true; - - // Can be cast by both - if ([sdk.skills.Attack, sdk.skills.Kick, sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, - sdk.skills.HeartofWolverine, sdk.skills.SummonDireWolf, sdk.skills.FireClaws, sdk.skills.SolarCreeper, sdk.skills.Hunger, sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.Armageddon].includes(skillId)) { - return true; - } - - // Can be cast by werewolf only - if (me.getState(sdk.states.Wearwolf) && [sdk.skills.Werewolf, sdk.skills.FeralRage, sdk.skills.Rabies, sdk.skills.Fury].includes(skillId)) return true; - // Can be cast by werebear only - if (me.getState(sdk.states.Wearbear) && [sdk.skills.Werebear, sdk.skills.Maul, sdk.skills.ShockWave].includes(skillId)) return true; - - return false; - }, - - // Skills that cn be cast in town - townSkill: function (skillId = -1) { - return [ - sdk.skills.Valkyrie, sdk.skills.FrozenArmor, sdk.skills.Telekinesis, sdk.skills.ShiverArmor, sdk.skills.Enchant, sdk.skills.ThunderStorm, sdk.skills.EnergyShield, sdk.skills.ChillingArmor, - sdk.skills.BoneArmor, sdk.skills.ClayGolem, sdk.skills.BloodGolem, sdk.skills.FireGolem, sdk.skills.HolyShield, sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.Werewolf, sdk.skills.Werebear, - sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, sdk.skills.CycloneArmor, sdk.skills.HeartofWolverine, sdk.skills.SummonDireWolf, sdk.skills.SolarCreeper, - sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.BurstofSpeed, sdk.skills.Fade, sdk.skills.ShadowWarrior, sdk.skills.BladeShield, sdk.skills.Venom, sdk.skills.ShadowMaster - ].includes(skillId); - }, - - manaCostList: {}, - - // Get mana cost of the skill (mBot) - getManaCost: function (skillId) { - if (skillId < sdk.skills.MagicArrow) return 0; - if (this.manaCostList.hasOwnProperty(skillId)) return this.manaCostList[skillId]; - - let skillLvl = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); - let 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("skills", skillId, "manashift")] / 256), getBaseStat("skills", skillId, "minmana")); - - if (!this.manaCostList.hasOwnProperty(skillId)) { - this.manaCostList[skillId] = ret; - } - - return ret; - }, - - useTK: function (unit = undefined) { - try { - if (!unit || !Skill.canUse(sdk.skills.Telekinesis) - || typeof unit !== "object" || unit.type !== sdk.unittype.Object - || unit.name.toLowerCase() === "dummy" - || (unit.name.toLowerCase() === "portal" && !me.inTown && unit.classid !== sdk.objects.ArcaneSanctuaryPortal) - || [sdk.objects.RedPortalToAct4, sdk.objects.WorldstonePortal, sdk.objects.RedPortal, sdk.objects.RedPortalToAct5].includes(unit.classid)) { - return false; - } - - return me.inTown || (me.mpPercent > 25); - } catch (e) { - return false; - } - } -}; - -Object.defineProperties(Skill, { - haveTK: { - get: function () { - return Skill.canUse(sdk.skills.Telekinesis); - }, - }, -}); - -const Misc = { - // Click something - click: function (button, shift, x, y) { - if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); - - while (!me.gameReady) { - delay(100); - } - - switch (arguments.length) { - case 2: - me.blockMouse = true; - clickMap(button, shift, me.x, me.y); - delay(20); - clickMap(button + 2, shift, me.x, me.y); - me.blockMouse = false; - - break; - case 3: - if (typeof (x) !== "object") throw new Error("Misc.click: Third arg must be a Unit."); - - me.blockMouse = true; - clickMap(button, shift, x); - delay(20); - clickMap(button + 2, shift, x); - me.blockMouse = false; - - break; - case 4: - me.blockMouse = true; - clickMap(button, shift, x, y); - delay(20); - clickMap(button + 2, shift, x, y); - me.blockMouse = false; - - break; - } - - return true; - }, - - // Check if a player is in your party - inMyParty: function (name) { - if (me.name === name) return true; - - while (!me.gameReady) { - delay(100); - } - - let player, myPartyId; - - try { - player = getParty(); - if (!player) return false; - - myPartyId = player.partyid; - player = getParty(name); // May throw an error - - if (player && player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { - return true; - } - } catch (e) { - player = getParty(); - - if (player) { - myPartyId = player.partyid; - - while (player.getNext()) { - if (player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { - return true; - } - } - } - } - - return false; - }, - - // Find a player - findPlayer: function (name) { - let player = getParty(); - - if (player) { - do { - if (player.name !== me.name && player.name === name) { - return player; - } - } while (player.getNext()); - } - - return false; - }, - - // Get player unit - getPlayerUnit: function (name) { - let player = Game.getPlayer(name); - - if (player) { - do { - if (!player.dead) { - return player; - } - } while (player.getNext()); - } - - return false; - }, - - // Get the player act, accepts party unit or name - getPlayerAct: function (player) { - if (!player) return false; - - let unit = (typeof player === "object" ? player : this.findPlayer(player)); - - if (!unit) { - return false; - } else { - return sdk.areas.actOf(unit.area); - } - }, - - // Get number of players within getUnit distance - getNearbyPlayerCount: function () { - let count = 0; - let player = Game.getPlayer(); - - if (player) { - do { - if (player.name !== me.name && !player.dead) { - count += 1; - } - } while (player.getNext()); - } - - return count; - }, - - // Get total number of players in game - getPlayerCount: function () { - let count = 0; - let party = getParty(); - - if (party) { - do { - count += 1; - } while (party.getNext()); - } - - return count; - }, - - // Get total number of players in game and in my party - getPartyCount: function () { - let count = 0; - let party = getParty(); - - if (party) { - let myPartyId = party.partyid; - - do { - if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name) { - print(party.name); - count += 1; - } - } while (party.getNext()); - } - - return count; - }, - - // check if any member of our party meets a certain level req - checkPartyLevel: function (levelCheck = 1, exclude = []) { - !Array.isArray(exclude) && (exclude = [exclude]); - let party = getParty(); - - if (party) { - let myPartyId = party.partyid; - - do { - if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name && !exclude.includes(party.name)) { - if (party.level >= levelCheck) { - return true; - } - } - } while (party.getNext()); - } - - return false; - }, - - getPlayerArea: function (player) { - if (!player) return false; - - let unit = (typeof player === "object" ? player : this.findPlayer(player)); - - return !!unit ? unit.area : 0; - }, - - // autoleader by Ethic - refactored by theBGuy - autoLeaderDetect: function (givenSettings = {}) { - const settings = Object.assign({}, { - destination: -1, - quitIf: false, - timeout: Infinity - }, givenSettings); - - let leader; - let startTick = getTickCount(); - let check = typeof settings.quitIf === "function"; - do { - let solofail = 0; - let suspect = getParty(); // get party object (players in game) - - do { - // player isn't alone - suspect.name !== me.name && (solofail += 1); - - if (check && settings.quitIf(suspect.area)) return false; - - // first player not hostile found in destination area... - if (suspect.area === settings.destination && !getPlayerFlag(me.gid, suspect.gid, 8)) { - leader = suspect.name; // ... is our leader - console.log("ÿc4Autodetected " + leader); - - return leader; - } - } while (suspect.getNext()); - - // empty game, nothing left to do. Or we exceeded our wait time - if (solofail === 0 || (getTickCount() - startTick > settings.timeout)) { - return false; - } - - delay(500); - } while (!leader); // repeat until leader is found (or until game is empty) - - return false; - }, - - // Open a chest Unit (takes chestID or unit) - 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); - if (useTK) { - unit.distance > 13 && Attack.getIntoPosition(unit, 13, sdk.collision.WallOrRanged); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { - console.debug("Failed to tk: attempt: " + i); - continue; - } - } else { - [(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.mode, 1000, 50)) { - return true; - } else { - 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; - }, - - // Open all chests that have preset units in an area - openChestsInArea: function (area, chestIds = []) { - !area && (area = me.area); - area !== me.area && Pather.journeyTo(area); - - let presetUnits = Game.getPresetObjects(area); - if (!presetUnits) return false; - - if (!chestIds.length) { - chestIds = [ - 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 - ]; - } - - 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.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; - }, - - openChests: function (range = 15) { - if (!Config.OpenChests.Enabled) return true; - - let unitList = []; - let containers = []; - - // Testing all container code - if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { - containers = [ - "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", "casket", "armorstand", "weaponrack", "barrel", "holeanim", "tomb2", - "tomb3", "roguecorpse", "ratnest", "corpse", "goo pile", "largeurn", "urn", "chest3", "jug", "skeleton", "guardcorpse", "sarcophagus", "object2", - "cocoon", "basket", "stash", "hollow log", "hungskeleton", "pillar", "skullpile", "skull pile", "jar3", "jar2", "jar1", "bonechest", "woodchestl", - "woodchestr", "barrel wilderness", "burialchestr", "burialchestl", "explodingchest", "chestl", "chestr", "groundtomb", "icecavejar1", "icecavejar2", - "icecavejar3", "icecavejar4", "deadperson", "deadperson2", "evilurn", "tomb1l", "tomb3l", "groundtombl" - ]; - } else { - containers = Config.OpenChests.Types; - } - - let unit = Game.getObject(); - - if (unit) { - do { - if (unit.name && unit.mode === sdk.objects.mode.Inactive && getDistance(me.x, me.y, unit.x, unit.y) <= range && containers.includes(unit.name.toLowerCase())) { - unitList.push(copyUnit(unit)); - } - } while (unit.getNext()); - } - - while (unitList.length > 0) { - unitList.sort(Sort.units); - unit = unitList.shift(); - - if (unit && (Pather.useTeleport() || !checkCollision(me, unit, sdk.collision.WallOrRanged)) && this.openChest(unit)) { - Pickit.pickItems(); - } - } - - return true; - }, - - shrineStates: false, - - scanShrines: function (range, ignore = []) { - if (!Config.ScanShrines.length) return false; - - !range && (range = Pather.useTeleport() ? 25 : 15); - !Array.isArray(ignore) && (ignore = [ignore]); - - let shrineList = []; - - // 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 shrine = Game.getObject("shrine"); - - if (shrine) { - let index = -1; - // Build a list of nearby shrines - do { - if (shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) && getDistance(me.x, me.y, shrine.x, shrine.y) <= range) { - shrineList.push(copyUnit(shrine)); - } - } while (shrine.getNext()); - - // 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 j = 0; j < shrineList.length; j += 1) { - // 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]); - - // Gem shrine - pick gem - if (Config.ScanShrines[i] === sdk.shrines.Gem) { - Pickit.pickItems(); - } - } - } - } - } - } - - return true; - }, - - // Use a shrine Unit - getShrine: function (unit) { - if (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); - !Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, 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)) { - return true; - } - } - - return false; - }, - - // Check all shrines in area and get the first one of specified type - getShrinesInArea: function (area, type, use) { - let shrineLocs = []; - let shrineIds = [2, 81, 83]; - let unit = Game.getPresetObjects(area); - let result = false; - - 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]); - } - } - } - - try { - NodeAction.shrinesToIgnore.push(type); - - while (shrineLocs.length > 0) { - shrineLocs.sort(Sort.points); - let coords = shrineLocs.shift(); - - Skill.haveTK ? Pather.moveNear(coords[0], coords[1], 20) : Pather.moveTo(coords[0], coords[1], 2); - - 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; - return true; - } - } - } while (shrine.getNext()); - } - } - } finally { - NodeAction.shrinesToIgnore.remove(type); - } - - return result; - }, - - getItemDesc: function (unit, logILvl = true) { - let stringColor = ""; - let desc = unit.description; - - if (!desc) return ""; - desc = desc.split("\n"); - - // Lines are normally in reverse. Add color tags if needed and reverse order. - for (let i = 0; i < desc.length; i += 1) { - // Remove sell value - if (desc[i].includes(getLocaleString(sdk.locale.text.SellValue))) { - desc.splice(i, 1); - - i -= 1; - } else { - // Add color info - if (!desc[i].match(/^(y|ÿ)c/)) { - desc[i] = stringColor + desc[i]; - } - - // Find and store new color info - let index = desc[i].lastIndexOf("ÿc"); - - if (index > -1) { - stringColor = desc[i].substring(index, index + "ÿ".length + 2); - } - } - - desc[i] = desc[i].replace(/(y|ÿ)c([0-9!"+<:;.*])/g, "\\xffc$2"); - } - - if (logILvl && desc[desc.length - 1]) { - desc[desc.length - 1] = desc[desc.length - 1].trim() + " (" + unit.ilvl + ")"; - } - - desc = desc.reverse().join("\n"); - - return desc; - }, - - getItemCode: function (unit) { - if (unit === undefined) return ""; - - let code = (() => { - switch (unit.quality) { - case sdk.items.quality.Set: - switch (unit.classid) { - case sdk.items.Sabre: - return "inv9sbu"; - case sdk.items.ShortWarBow: - return "invswbu"; - case sdk.items.Helm: - return "invhlmu"; - case sdk.items.LargeShield: - return "invlrgu"; - case sdk.items.LongSword: - case sdk.items.CrypticSword: - return "invlsdu"; - case sdk.items.SmallShield: - return "invsmlu"; - case sdk.items.Buckler: - return "invbucu"; - case sdk.items.Cap: - return "invcapu"; - case sdk.items.BroadSword: - return "invbsdu"; - case sdk.items.FullHelm: - return "invfhlu"; - case sdk.items.GothicShield: - return "invgtsu"; - case sdk.items.AncientArmor: - case sdk.items.SacredArmor: - return "invaaru"; - case sdk.items.KiteShield: - return "invkitu"; - case sdk.items.TowerShield: - return "invtowu"; - case sdk.items.FullPlateMail: - return "invfulu"; - case sdk.items.MilitaryPick: - return "invmpiu"; - case sdk.items.JaggedStar: - return "invmstu"; - case sdk.items.ColossusBlade: - return "invgsdu"; - case sdk.items.OrnatePlate: - return "invxaru"; - case sdk.items.Cuirass: - case sdk.items.ReinforcedMace: - case sdk.items.Ward: - case sdk.items.SpiredHelm: - return "inv" + unit.code + "s"; - case sdk.items.GrandCrown: - return "invxrnu"; - case sdk.items.ScissorsSuwayyah: - return "invskru"; - case sdk.items.GrimHelm: - case sdk.items.BoneVisage: - return "invbhmu"; - case sdk.items.ElderStaff: - return "invcstu"; - case sdk.items.RoundShield: - return "invxmlu"; - case sdk.items.BoneWand: - return "invbwnu"; - default: - return ""; - } - case sdk.items.quality.Unique: - for (let i = 0; i < 401; i += 1) { - if (unit.code === getBaseStat("uniqueitems", i, 4).trim() - && unit.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat("uniqueitems", i, 2)))) { - return getBaseStat("uniqueitems", i, "invfile"); - } - } - return ""; - default: - return ""; - } - })(); - - if (!code) { - // Tiara/Diadem - code = ["ci2", "ci3"].includes(unit.code) ? unit.code : (getBaseStat("items", unit.classid, "normcode") || unit.code); - code = code.replace(" ", ""); - [sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(unit.itemType) && (code += (unit.gfx + 1)); - } - - return code; - }, - - getItemSockets: function (unit) { - let code; - let sockets = unit.sockets; - let subItems = unit.getItemsEx(); - let tempArray = []; - - if (subItems.length) { - switch (unit.sizex) { - case 2: - switch (unit.sizey) { - case 3: // 2 x 3 - switch (sockets) { - case 4: - tempArray = [subItems[0], subItems[3], subItems[2], subItems[1]]; - - break; - case 5: - tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; - - break; - case 6: - tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; - - break; - } - - break; - case 4: // 2 x 4 - switch (sockets) { - case 5: - tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; - - break; - case 6: - tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; - - break; - } - - break; - } - - break; - } - - if (tempArray.length === 0 && subItems.length > 0) { - tempArray = subItems.slice(0); - } - } - - for (let i = 0; i < sockets; i += 1) { - if (tempArray[i]) { - code = tempArray[i].code; - - if ([sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(tempArray[i].itemType)) { - code += (tempArray[i].gfx + 1); - } - } else { - code = "gemsocket"; - } - - tempArray[i] = code; - } - - return tempArray; - }, - - useItemLog: true, // Might be a bit dirty - - itemLogger: function (action, unit, text) { - if (!Config.ItemInfo || !this.useItemLog) return false; - - let desc; - let date = new Date(); - let dateString = "[" + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; - - switch (action) { - case "Sold": - if (Config.ItemInfoQuality.indexOf(unit.quality) === -1) { - return false; - } - - desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]/gi, "").trim(); - - break; - case "Kept": - case "Field Kept": - case "Runeword Kept": - case "Cubing Kept": - case "Shopped": - case "Gambled": - case "Dropped": - desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); - - break; - case "No room for": - desc = unit.name; - - break; - default: - desc = unit.fname.split("\n").reverse().join(" ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); - - break; - } - - return this.fileAction("logs/ItemLog.txt", 2, dateString + " <" + me.profile + "> <" + action + "> (" + Pickit.itemQualityToName(unit.quality) + ") " + desc + (text ? " {" + text + "}" : "") + "\n"); - }, - - // Log kept item stats in the manager. - logItem: function (action, unit, keptLine) { - if (!this.useItemLog) 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)); - } - - 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.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; - }, - - // skip low items: MuleLogger - skipItem: function (id) { - return [ - sdk.items.HandAxe, sdk.items.Wand, sdk.items.Club, sdk.items.ShortSword, sdk.items.Javelin, sdk.items.ShortStaff, sdk.items.Katar, - sdk.items.Buckler, sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, - sdk.items.ThawingPotion, sdk.items.TomeofTownPortal, sdk.items.TomeofIdentify, sdk.items.ScrollofIdentify, sdk.items.ScrollofTownPortal, - sdk.items.Key, 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 - ].includes(id); - }, - - // Change into werewolf or werebear - shapeShift: function (mode) { - let [skill, state] = (() => { - switch (mode.toString().toLowerCase()) { - case "0": - return [-1, -1]; - case "1": - case "werewolf": - return [sdk.skills.Werewolf, sdk.states.Wearwolf]; - case "2": - case "werebear": - return [sdk.skills.Werebear, sdk.states.Wearbear]; - default: - throw new Error("shapeShift: Invalid parameter"); - } - })(); - - // don't have wanted skill - if (!Skill.canUse(skill)) return false; - // already in wanted state - if (me.getState(state)) return true; - - let slot = Attack.getPrimarySlot(); - me.switchWeapons(Precast.getBetterSlot(skill)); - - for (let i = 0; i < 3; i += 1) { - Skill.cast(skill, sdk.skills.hand.Right); - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (me.getState(state)) { - delay(250); - me.weaponswitch !== slot && me.switchWeapons(slot); - - return true; - } - - delay(10); - } - } - - me.weaponswitch !== slot && me.switchWeapons(slot); - - return false; - }, - - // Change back to human shape - unShift: function () { - if (me.getState(sdk.states.Wearwolf) || me.getState(sdk.states.Wearbear)) { - for (let i = 0; i < 3; i += 1) { - Skill.cast(me.getState(sdk.states.Wearwolf) ? sdk.skills.Werewolf : sdk.skills.Werebear); - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (!me.getState(sdk.states.Wearwolf) && !me.getState(sdk.states.Wearbear)) { - delay(250); - - return true; - } - - delay(10); - } - } - } else { - return true; - } - - return false; - }, - - // Go to town when low on hp/mp or when out of potions. can be upgraded to check for curses etc. - townCheck: function () { - if (!Town.canTpToTown()) return false; - - let tTick = getTickCount(); - let check = false; - - if (Config.TownCheck && !me.inTown) { - try { - if (Town.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { - check = true; - } - } catch (e) { - return false; - } - - if (check) { - // check that townchicken is running - so we don't spam needing potions if it isn't - let townChick = getScript("tools/TownChicken.js"); - if (!townChick || townChick && !townChick.running) { - return false; - } - - townChick.send("townCheck"); - console.log("townCheck check Duration: " + (getTickCount() - tTick)); - - return true; - } - } - - return false; - }, - - // Log someone's gear - spy: function (name) { - includeIfNotIncluded("oog.js"); - includeIfNotIncluded("common/prototypes.js"); - - let unit = getUnit(-1, name); - - if (!unit) { - console.warn("player not found"); - return false; - } - - let item = unit.getItem(); - - if (item) { - do { - this.logItem(unit.name, item); - } while (item.getNext()); - } - - return true; - }, - - fileAction: function (path, mode, msg) { - let contents = ""; - - MainLoop: - for (let i = 0; i < 30; i += 1) { - try { - switch (mode) { - case 0: // read - contents = FileTools.readText(path); - - break MainLoop; - case 1: // write - FileTools.writeText(path, msg); - - break MainLoop; - case 2: // append - FileTools.appendText(path, msg); - - break MainLoop; - } - } catch (e) { - continue; - } - - delay(100); - } - - return mode === 0 ? contents : true; - }, - - errorConsolePrint: true, - screenshotErrors: true, - - // Report script errors to logs/ScriptErrorLog.txt - 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: " + 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 += ", "; - } - } - } - } - } - - stackLog && (filemsg += "Stack: " + stackLog + "\n"); - } - - this.errorConsolePrint && D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); - showConsole(); - console.log(msg); - this.fileAction("logs/ScriptErrorLog.txt", 2, filemsg); - - if (this.screenshotErrors) { - takeScreenshot(); - delay(500); - } - }, - - debugLog: function (msg) { - if (!Config.Debug) return; - debugLog(me.profile + ": " + msg); - }, - - // Use a NPC menu. Experimental function, subject to change - // id = string number (with exception of Ressurect merc). - useMenu: function (id) { - //print("useMenu " + getLocaleString(id)); - - let npc; - - switch (id) { - case sdk.menu.RessurectMerc: // (non-English dialog) - case sdk.menu.Trade: // (crash dialog) - npc = getInteractedNPC(); - - if (npc) { - npc.useMenu(id); - delay(750); - - return true; - } - - break; - } - - let lines = getDialogLines(); - if (!lines) return false; - - for (let i = 0; i < lines.length; i += 1) { - if (lines[i].selectable && lines[i].text.includes(getLocaleString(id))) { - getDialogLines()[i].handler(); - delay(750); - - return true; - } - } - - return false; - }, - - clone: function (obj) { - let copy; - - // Handle the 3 simple types, and null or undefined - if (null === obj || "object" !== typeof obj) { - return obj; - } - - // Handle Date - if (obj instanceof Date) { - copy = new Date(); - copy.setTime(obj.getTime()); - - return copy; - } - - // Handle Array - if (obj instanceof Array) { - copy = []; - - for (let i = 0; i < obj.length; i += 1) { - copy[i] = this.clone(obj[i]); - } - - return copy; - } - - // Handle Object - if (obj instanceof Object) { - copy = {}; - - for (let attr in obj) { - if (obj.hasOwnProperty(attr)) { - copy[attr] = this.clone(obj[attr]); - } - } - - return copy; - } - - throw new Error("Unable to copy obj! Its type isn't supported."); - }, - - copy: function (from) { - let obj = {}; - - for (let i in from) { - if (from.hasOwnProperty(i)) { - obj[i] = this.clone(from[i]); - } - } - - return obj; - }, - - poll: function (check, timeout = 6000, sleep = 40) { - let ret, start = getTickCount(); - - while (getTickCount() - start <= timeout) { - if ((ret = check())) { - return ret; - } - - delay(sleep); - } - - return false; - }, - - // returns array of UI flags that are set, or null if none are set - getUIFlags: function (excluded = []) { - if (!me.gameReady) return null; - - const MAX_FLAG = 37; // anything over 37 crashes - let flags = []; - - if (typeof excluded !== "object" || excluded.length === undefined) { - // not an array-like object, make it an array - excluded = [excluded]; - } - - for (let c = 1; c <= MAX_FLAG; c++) { - // 0x23 is always set in-game - if (c !== 0x23 && excluded.indexOf(c) === -1 && getUIFlag(c)) { - flags.push(c); - } - } - - return flags.length ? flags : null; - }, - - checkQuest: function (id, state) { - Packet.questRefresh(); - delay(500); - return me.getQuest(id, state); - }, - - getQuestStates: function (questID) { - if (!me.gameReady) return []; - Packet.questRefresh(); - delay(500); - const MAX_STATE = 16; - let questStates = []; - - for (let i = 0; i < MAX_STATE; i++) { - if (me.getQuest(questID, i)) { - questStates.push(i); - } - - delay(50); - } - - return questStates; - } -}; - -const Sort = { - // Sort units by comparing distance between the player - units: function (a, b) { - return Math.round(getDistance(me.x, me.y, a.x, a.y)) - Math.round(getDistance(me.x, me.y, b.x, b.y)); - }, - - // Sort preset units by comparing distance between the player (using preset x/y calculations) - presetUnits: function (a, b) { - return getDistance(me, a.roomx * 5 + a.x, a.roomy * 5 + a.y) - getDistance(me, b.roomx * 5 + b.x, b.roomy * 5 + b.y); - }, - - // Sort arrays of x,y coords by comparing distance between the player - points: function (a, b) { - return getDistance(me, a[0], a[1]) - getDistance(me, b[0], b[1]); - }, - - numbers: function (a, b) { - return a - b; - } -}; - -const Experience = { - totalExp: [0, 0, 500, 1500, 3750, 7875, 14175, 22680, 32886, 44396, 57715, 72144, 90180, 112725, 140906, 176132, 220165, 275207, 344008, 430010, 537513, 671891, 839864, 1049830, 1312287, 1640359, 2050449, 2563061, 3203826, 3902260, 4663553, 5493363, 6397855, 7383752, 8458379, 9629723, 10906488, 12298162, 13815086, 15468534, 17270791, 19235252, 21376515, 23710491, 26254525, 29027522, 32050088, 35344686, 38935798, 42850109, 47116709, 51767302, 56836449, 62361819, 68384473, 74949165, 82104680, 89904191, 98405658, 107672256, 117772849, 128782495, 140783010, 153863570, 168121381, 183662396, 200602101, 219066380, 239192444, 261129853, 285041630, 311105466, 339515048, 370481492, 404234916, 441026148, 481128591, 524840254, 572485967, 624419793, 681027665, 742730244, 809986056, 883294891, 963201521, 1050299747, 1145236814, 1248718217, 1361512946, 1484459201, 1618470619, 1764543065, 1923762030, 2097310703, 2286478756, 2492671933, 2717422497, 2962400612, 3229426756, 3520485254, 0, 0], - nextExp: [0, 500, 1000, 2250, 4125, 6300, 8505, 10206, 11510, 13319, 14429, 18036, 22545, 28181, 35226, 44033, 55042, 68801, 86002, 107503, 134378, 167973, 209966, 262457, 328072, 410090, 512612, 640765, 698434, 761293, 829810, 904492, 985897, 1074627, 1171344, 1276765, 1391674, 1516924, 1653448, 1802257, 1964461, 2141263, 2333976, 2544034, 2772997, 3022566, 3294598, 3591112, 3914311, 4266600, 4650593, 5069147, 5525370, 6022654, 6564692, 7155515, 7799511, 8501467, 9266598, 10100593, 11009646, 12000515, 13080560, 14257811, 15541015, 16939705, 18464279, 20126064, 21937409, 23911777, 26063836, 28409582, 30966444, 33753424, 36791232, 40102443, 43711663, 47645713, 51933826, 56607872, 61702579, 67255812, 73308835, 79906630, 87098226, 94937067, 103481403, 112794729, 122946255, 134011418, 146072446, 159218965, 173548673, 189168053, 206193177, 224750564, 244978115, 267026144, 291058498, 0, 0], - expCurve: [13, 16, 110, 159, 207, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 174, 92, 38, 5], - expPenalty: [1024, 976, 928, 880, 832, 784, 736, 688, 640, 592, 544, 496, 448, 400, 352, 304, 256, 192, 144, 108, 81, 61, 46, 35, 26, 20, 15, 11, 8, 6, 5], - monsterExp: [ - [1, 1, 1], [30, 78, 117], [40, 104, 156], [50, 131, 197], [60, 156, 234], [70, 182, 273], [80, 207, 311], [90, 234, 351], [100, 260, 390], [110, 285, 428], [120, 312, 468], - [130, 338, 507], [140, 363, 545], [154, 401, 602], [169, 440, 660], [186, 482, 723], [205, 533, 800], [225, 584, 876], [248, 644, 966], [273, 708, 1062], [300, 779, 1169], - [330, 857, 1286], [363, 942, 1413], [399, 1035, 1553], [439, 1139, 1709], [470, 1220, 1830], [503, 1305, 1958], [538, 1397, 2096], [576, 1494, 2241], [616, 1598, 2397], - [659, 1709, 2564], [706, 1832, 2748], [755, 1958, 2937], [808, 2097, 3146], [864, 2241, 3362], [925, 2399, 3599], [990, 2568, 3852], [1059, 2745, 4118], [1133, 2939, 4409], - [1212, 3144, 4716], [1297, 3365, 5048], [1388, 3600, 5400], [1485, 3852, 5778], [1589, 4121, 6182], [1693, 4409, 6614], [1797, 4718, 7077], [1901, 5051, 7577], - [2005, 5402, 8103], [2109, 5783, 8675], [2213, 6186, 9279], [2317, 6618, 9927], [2421, 7080, 10620], [2525, 7506, 11259], [2629, 7956, 11934], [2733, 8435, 12653], - [2837, 8942, 13413], [2941, 9477, 14216], [3045, 10044, 15066], [3149, 10647, 15971], [3253, 11286, 16929], [3357, 11964, 17946], [3461, 12680, 19020], - [3565, 13442, 20163], [3669, 14249, 21374], [3773, 15104, 22656], [3877, 16010, 24015], [3981, 16916, 25374], [4085, 17822, 26733], [4189, 18728, 28092], - [4293, 19634, 29451], [4397, 20540, 30810], [4501, 21446, 32169], [4605, 22352, 33528], [4709, 23258, 34887], [4813, 24164, 36246], [4917, 25070, 37605], - [5021, 25976, 38964], [5125, 26882, 40323], [5229, 27788, 41682], [5333, 28694, 43041], [5437, 29600, 44400], [5541, 30506, 45759], [5645, 31412, 47118], - [5749, 32318, 48477], [5853, 33224, 49836], [5957, 34130, 51195], [6061, 35036, 52554], [6165, 35942, 53913], [6269, 36848, 55272], [6373, 37754, 56631], - [6477, 38660, 57990], [6581, 39566, 59349], [6685, 40472, 60708], [6789, 41378, 62067], [6893, 42284, 63426], [6997, 43190, 64785], [7101, 44096, 66144], - [7205, 45002, 67503], [7309, 45908, 68862], [7413, 46814, 70221], [7517, 47720, 71580], [7621, 48626, 72939], [7725, 49532, 74298], [7829, 50438, 75657], - [7933, 51344, 77016], [8037, 52250, 78375], [8141, 53156, 79734], [8245, 54062, 81093], [8349, 54968, 82452], [8453, 55874, 83811], [160000, 160000, 160000] - ], - // Percent progress into the current level. Format: xx.xx% - progress: function () { - return me.getStat(sdk.stats.Level) === 99 ? 0 : (((me.getStat(sdk.stats.Experience) - this.totalExp[me.getStat(sdk.stats.Level)]) / this.nextExp[me.getStat(sdk.stats.Level)]) * 100).toFixed(2); - }, - - // Total experience gained in current run - gain: function () { - return (me.getStat(sdk.stats.Experience) - DataFile.getStats().experience); - }, - - // Percent experience gained in current run - gainPercent: function () { - return me.getStat(sdk.stats.Level) === 99 ? 0 : (this.gain() * 100 / this.nextExp[me.getStat(sdk.stats.Level)]).toFixed(6); - }, - - // Runs until next level - runsToLevel: function () { - return Math.round(((100 - this.progress()) / 100) * this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); - }, - - // Total runs needed for next level (not counting current progress) - totalRunsToLevel: function () { - return Math.round(this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); - }, - - // Total time till next level - timeToLevel: function () { - let tTLrawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000)).toString(); - let tTLrawtimeToLevel = this.runsToLevel() * tTLrawSeconds; - let tTLDays = Math.floor(tTLrawtimeToLevel / 86400); - let tTLHours = Math.floor((tTLrawtimeToLevel % 86400) / 3600); - let tTLMinutes = Math.floor(((tTLrawtimeToLevel % 86400) % 3600) / 60); - //let tTLSeconds = ((tTLrawtimeToLevel % 86400) % 3600) % 60; - - //return tDays + "d " + tTLHours + "h " + tTLMinutes + "m " + tTLSeconds + "s"; - //return tTLDays + "d " + tTLHours + "h " + tTLMinutes + "m"; - return (tTLDays ? tTLDays + " d " : "") + (tTLHours ? tTLHours + " h " : "") + (tTLMinutes ? tTLMinutes + " m" : ""); - }, - - // Get Game Time - getGameTime: function () { - let rawMinutes = Math.floor((getTickCount() - me.gamestarttime) / 60000).toString(); - let rawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000) % 60).toString(); - - rawMinutes <= 9 && (rawMinutes = "0" + rawMinutes); - rawSeconds <= 9 && (rawSeconds = "0" + rawSeconds); - - return " (" + rawMinutes + ":" + rawSeconds + ")"; - }, - - // Log to manager - log: function () { - let gain = this.gain(); - let progress = this.progress(); - let runsToLevel = this.runsToLevel(); - let getGameTime = this.getGameTime(); - let string = "[Game: " + me.gamename + (me.gamepassword ? "//" + me.gamepassword : "") + getGameTime + "] [Level: " + me.getStat(sdk.stats.Level) + " (" + progress + "%)] [XP: " + gain + "] [Games ETA: " + runsToLevel + "]"; - - if (gain) { - D2Bot.printToConsole(string, sdk.colors.D2Bot.Blue); - - if (me.getStat(sdk.stats.Level) > DataFile.getStats().level) { - D2Bot.printToConsole("Congrats! You gained a level. Current level:" + me.getStat(sdk.stats.Level), sdk.colors.D2Bot.Green); - } - } - } -}; - -const Packet = { - openMenu: function (unit) { - if (unit.type !== sdk.unittype.NPC) throw new Error("openMenu: Must be used on NPCs."); - if (getUIFlag(sdk.uiflags.NPCMenu)) return true; - let pingDelay = (me.gameReady ? me.ping : 125); - - for (let i = 0; i < 5; i += 1) { - unit.distance > 4 && Pather.moveToUnit(unit); - Packet.entityInteract(unit); - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (getUIFlag(sdk.uiflags.NPCMenu)) { - delay(Math.max(500, pingDelay * 2)); - - return true; - } - - if (getInteractedNPC() && getTickCount() - tick > 1000) { - me.cancel(); - } - - delay(100); - } - - sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, unit.gid); - delay(pingDelay + 1 * 2); - Packet.cancelNPC(unit); - delay(pingDelay + 1 * 2); - this.flash(me.gid); - } - - return false; - }, - - startTrade: function (unit, mode) { - if (unit.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); - if (getUIFlag(sdk.uiflags.Shop)) return true; - - const gamble = mode === "Gamble"; - console.info(true, mode + " at " + unit.name); - - if (this.openMenu(unit)) { - for (let i = 0; i < 10; i += 1) { - delay(200); - - i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, gamble ? 2 : 1, 4, unit.gid, 4, 0); - - if (unit.itemcount > 0) { - delay(200); - console.info(false, "Successfully started " + mode + " at " + unit.name); - return true; - } - } - } - - return false; - }, - - buyItem: function (unit, shiftBuy, gamble) { - let oldGold = me.gold; - let itemCount = me.itemcount; - let npc = getInteractedNPC(); - - try { - if (!npc) throw new Error("buyItem: No NPC menu open."); - - // Can we afford the item? - if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; - - for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : gamble ? 0x2 : 0x0, 4, 0); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { - if (shiftBuy && me.gold < oldGold) return true; - if (itemCount !== me.itemcount) return true; - - delay(10); - } - } - } catch (e) { - console.error(e); - } - - return false; - }, - - buyScroll: function (unit, tome, shiftBuy) { - let oldGold = me.gold; - let itemCount = me.itemcount; - let npc = getInteractedNPC(); - tome === undefined && (tome = me.findItem( - (unit.classid === sdk.items.ScrollofTownPortal ? sdk.items.TomeofTownPortal : sdk.items.TomeofIdentify), - sdk.items.mode.inStorage, sdk.storage.Inventory - )); - let preCount = !!tome ? tome.getStat(sdk.stats.Quantity) : 0; - - try { - if (!npc) throw new Error("buyItem: No NPC menu open."); - - // Can we afford the item? - if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; - - for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : 0x0, 4, 0); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { - if (shiftBuy && me.gold < oldGold) return true; - if (itemCount !== me.itemcount) return true; - if (tome && tome.getStat(sdk.stats.Quantity) > preCount) return true; - delay(10); - } - } - } catch (e) { - console.error(e); - } - - return false; - }, - - sellItem: function (unit) { - // Check if it's an item we want to buy - if (unit.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); - if (!unit.sellable) { - console.error((new Error("Item is unsellable"))); - return false; - } - - let itemCount = me.itemcount; - let npc = getInteractedNPC(); - - if (!npc) return false; - - for (let i = 0; i < 5; i += 1) { - sendPacket(1, sdk.packets.send.NPCSell, 4, npc.gid, 4, unit.gid, 4, 0, 4, 0); - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (me.itemcount !== itemCount) return true; - delay(10); - } - } - - return false; - }, - - identifyItem: function (unit, tome) { - if (!unit || unit.identified) return false; - - CursorLoop: - for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (getCursorType() === sdk.cursortype.Identify) { - break CursorLoop; - } - - delay(10); - } - } - - if (getCursorType() !== sdk.cursortype.Identify) { - return false; - } - - for (let i = 0; i < 3; i += 1) { - getCursorType() === sdk.cursortype.Identify && sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (unit.identified) { - delay(50); - return true; - } - - delay(10); - } - } - - return false; - }, - - itemToCursor: function (item) { - // Something already on cursor - if (me.itemoncursor) { - let cursorItem = Game.getCursorUnit(); - // Return true if the item is already on cursor - if (cursorItem.gid === item.gid) { - return true; - } - this.dropItem(cursorItem); // If another item is on cursor, drop it - } - - for (let i = 0; i < 15; i += 1) { - // equipped - item.isEquipped ? sendPacket(1, sdk.packets.send.PickupBodyItem, 2, item.bodylocation) : sendPacket(1, sdk.packets.send.PickupBufferItem, 4, item.gid); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { - if (me.itemoncursor) return true; - delay(10); - } - } - - return false; - }, - - dropItem: function (item) { - if (!this.itemToCursor(item)) return false; - - for (let i = 0; i < 15; i += 1) { - sendPacket(1, sdk.packets.send.DropItem, 4, item.gid); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { - if (!me.itemoncursor) return true; - delay(10); - } - } - - return false; - }, - - givePotToMerc: function (item) { - if (!!item - && [sdk.items.type.HealingPotion, sdk.items.type.RejuvPotion, sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion].includes(item.itemType)) { - switch (item.location) { - case sdk.storage.Belt: - return this.useBeltItemForMerc(item); - case sdk.storage.Inventory: - if (this.itemToCursor(item)) { - sendPacket(1, sdk.packets.send.MercItem, 2, 0); - - return true; - } - - break; - default: - break; - } - } - - return false; - }, - - placeInBelt: function (item, xLoc) { - item.toCursor(true) && new PacketBuilder().byte(sdk.packets.send.ItemToBelt).dword(item.gid).dword(xLoc).send(); - return Misc.poll(() => item.isInBelt, 500, 100); - }, - - click: function (who, toCursor = false) { - if (!who || !copyUnit(who).x) return false; - new PacketBuilder().byte(sdk.packets.send.PickupItem).dword(sdk.unittype.Item).dword(who.gid).dword(toCursor ? 1 : 0).send(); - return true; - }, - - entityInteract: function (who) { - if (!who || !copyUnit(who).x) return false; - sendPacket(1, sdk.packets.send.InteractWithEntity, 4, who.type, 4, who.gid); - return true; - }, - - cancelNPC: function (who) { - if (!who || !copyUnit(who).x) return false; - sendPacket(1, sdk.packets.send.NPCCancel, 4, who.type, 4, who.gid); - return true; - }, - - useBeltItemForMerc: function (who) { - if (!who) return false; - sendPacket(1, sdk.packets.send.UseBeltItem, 4, who.gid, 4, 1, 4, 0); - return true; - }, - - castSkill: function (hand, wX, wY) { - hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocation : sdk.packets.send.LeftSkillOnLocation; - sendPacket(1, hand, 2, wX, 2, wY); - }, - - unitCast: function (hand, who) { - hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnEntityEx3 : sdk.packets.send.LeftSkillOnEntityEx3; - sendPacket(1, hand, 4, who.type, 4, who.gid); - }, - - telekinesis: function (who) { - if (!who || !Skill.setSkill(sdk.skills.Telekinesis, sdk.skills.hand.Right)) return false; - sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); - return true; - }, - - enchant: function (who) { - if (!who || !Skill.setSkill(sdk.skills.Enchant, sdk.skills.hand.Right)) return false; - sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); - return true; - }, - - teleport: function (wX, wY) { - if (![wX, wY].every(n => typeof n === "number") || !Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right)) return false; - sendPacket(1, sdk.packets.send.RightSkillOnLocation, 2, wX, 2, wY); - return true; - }, - - // moveNPC: function (npc, dwX, dwY) { // commented the patched packet - // //sendPacket(1, sdk.packets.send.MakeEntityMove, 4, npc.type, 4, npc.gid, 4, dwX, 4, dwY); - // }, - - teleWalk: function (x, y, maxDist = 5) { - !this.telewalkTick && (this.telewalkTick = 0); - - if (getDistance(me, x, y) > 10 && getTickCount() - this.telewalkTick > 3000 && Attack.validSpot(x, y)) { - for (let i = 0; i < 5; i += 1) { - sendPacket(1, sdk.packets.send.UpdatePlayerPos, 2, x + rand(-1, 1), 2, y + rand(-1, 1)); - delay(me.ping + 1); - sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, me.type, 4, me.gid); - delay(me.ping + 1); - - if (getDistance(me, x, y) < maxDist) { - delay(200); - - return true; - } - } - - this.telewalkTick = getTickCount(); - } - - return false; - }, - - questRefresh: function () { - sendPacket(1, sdk.packets.send.UpdateQuests); - }, - - flash: function (gid, wait = 0) { - wait === 0 && (wait = 300 + (me.gameReady ? 2 * me.ping : 300)); - sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, 0, 4, gid); - - if (wait > 0) { - delay(wait); - } - }, - - changeStat: function (stat, value) { - if (value > 0) { - getPacket(1, 0x1d, 1, stat, 1, value); - } - }, - - // specialized wrapper for addEventListener - addListener: function (packetType, callback) { - if (typeof packetType === "number") { - packetType = [packetType]; - } - - if (typeof packetType === "object" && packetType.length) { - addEventListener("gamepacket", packet => (packetType.indexOf(packet[0]) > -1 ? callback(packet) : false)); - - return callback; - } - - return null; - }, - - removeListener: callback => removeEventListener("gamepacket", callback), // just a wrapper -}; - -/* - -new PacketBuilder() - create new packet object - -Example (Spoof 'reassign player' packet to client): - new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); - -Example (Spoof 'player move' packet to server): - new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); -*/ - -function PacketBuilder () { - // globals DataView ArrayBuffer - if (this.__proto__.constructor !== PacketBuilder) throw new Error("PacketBuilder must be called with 'new' operator!"); - - let queue = [], count = 0; - - // accepts any number of arguments - let enqueue = (type, size) => (...args) => { - args.forEach(arg => { - if (type === "String") { - arg = stringToEUC(arg); - size = arg.length + 1; - } - - queue.push({type: type, size: size, data: arg}); - count += size; - }); - - return this; - }; - - this.float = enqueue("Float32", 4); - this.dword = enqueue("Uint32", 4); - this.word = enqueue("Uint16", 2); - this.byte = enqueue("Uint8", 1); - this.string = enqueue("String"); - - this.buildDataView = () => { - let dv = new DataView(new ArrayBuffer(count)), i = 0; - queue.forEach(field => { - if (field.type === "String") { - for (let l = 0; l < field.data.length; l++) { - dv.setUint8(i++, field.data.charCodeAt(l), true); - } - - i += field.size - field.data.length; // fix index for field.size !== field.data.length - } else { - dv["set" + field.type](i, field.data, true); - i += field.size; - } - }); - - return dv; - }; - - this.send = () => (sendPacket(this.buildDataView().buffer), this); - this.spoof = () => (getPacket(this.buildDataView().buffer), this); - this.get = this.spoof; // same thing but spoof has clearer intent than get -} - -const LocalChat = new function () { - const LOCAL_CHAT_ID = 0xD2BAAAA; - let toggle, proxy = say; - - let relay = (msg) => D2Bot.shoutGlobal(JSON.stringify({ msg: msg, realm: me.realm, charname: me.charname, gamename: me.gamename }), LOCAL_CHAT_ID); - - let onChatInput = (speaker, msg) => { - relay(msg); - return true; - }; - - let onChatRecv = (mode, msg) => { - if (mode !== LOCAL_CHAT_ID) { - return; - } - - msg = JSON.parse(msg); - - if (me.gamename === msg.gamename && me.realm === msg.realm) { - new PacketBuilder().byte(38).byte(1, me.locale).word(2, 0, 0).byte(90).string(msg.charname, msg.msg).get(); - } - }; - - let onKeyEvent = (key) => { - if (toggle === key) { - this.init(true); - } - }; - - this.init = (cycle = false) => { - if (!Config.LocalChat.Enabled) return; - - Config.LocalChat.Mode = (Config.LocalChat.Mode + cycle) % 3; - print("ÿc2LocalChat enabled. Mode: " + Config.LocalChat.Mode); - - switch (Config.LocalChat.Mode) { - case 2: - removeEventListener("chatinputblocker", onChatInput); - addEventListener("chatinputblocker", onChatInput); - // eslint-disable-next-line no-fallthrough - case 1: - removeEventListener("copydata", onChatRecv); - addEventListener("copydata", onChatRecv); - say = (msg, force = false) => force ? proxy(msg) : relay(msg); - break; - case 0: - removeEventListener("chatinputblocker", onChatInput); - removeEventListener("copydata", onChatRecv); - say = proxy; - break; - } - - if (Config.LocalChat.Toggle) { - toggle = typeof Config.LocalChat.Toggle === "string" ? Config.LocalChat.Toggle.charCodeAt(0) : Config.LocalChat.Toggle; - Config.LocalChat.Toggle = false; - addEventListener("keyup", onKeyEvent); - } - }; -}; - -const Messaging = { - sendToScript: function (name, msg) { - let script = getScript(name); - - if (script && script.running) { - script.send(msg); - - return true; - } - - return false; - }, - - sendToProfile: function (profileName, mode, message, getResponse = false) { - let response; - - function copyDataEvent(mode2, msg) { - if (mode2 === mode) { - let obj; - - try { - obj = JSON.parse(msg); - } catch (e) { - return false; - } - - if (obj.hasOwnProperty("sender") && obj.sender === profileName) { - response = Misc.copy(obj); - } - - return true; - } - - return false; - } - - getResponse && addEventListener("copydata", copyDataEvent); - - if (!sendCopyData(null, profileName, mode, JSON.stringify({message: message, sender: me.profile}))) { - //print("sendToProfile: failed to get response from " + profileName); - getResponse && removeEventListener("copydata", copyDataEvent); - - return false; - } - - if (getResponse) { - delay(200); - removeEventListener("copydata", copyDataEvent); - - if (!!response) { - return response; - } - - return false; - } - - return true; - } -}; - -// Unused anywhere -// var Events = { -// // gamepacket -// gamePacket: function (bytes) { -// let temp; - -// switch (bytes[0]) { -// // Block movement after using TP/WP/Exit -// case 0x0D: // Player Stop -// // This can mess up death screen so disable for characters that are allowed to die -// if (Config.LifeChicken > 0) { -// return true; -// } - -// break; -// // Block poison skills that might crash the client -// case 0x4C: // Cast skill on target -// case 0x4D: // Cast skill on coords -// temp = Number("0x" + bytes[7].toString(16) + bytes[6].toString(16)); - -// // Match Poison Javelin, Plague Javelin or Poison Nova -// if (temp && [15, 25, 92].indexOf(temp) > -1) { -// return true; -// } - -// break; -// } - -// return false; -// } -// }; diff --git a/d2bs/kolbot/libs/common/Storage.js b/d2bs/kolbot/libs/common/Storage.js deleted file mode 100644 index 39d4fd5a6..000000000 --- a/d2bs/kolbot/libs/common/Storage.js +++ /dev/null @@ -1,397 +0,0 @@ -/** -* @filename Storage.js -* @author McGod, kolton (small kolbot related edits) -* @desc manage inventory, belt, stash, cube -* -*/ - -let Container = function (name, width, height, location) { - let h, w; - - this.name = name; - this.width = width; - this.height = height; - this.location = location; - this.buffer = []; - this.itemList = []; - this.openPositions = this.height * this.width; - - // Initalize the buffer array for use, set all as empty. - for (h = 0; h < this.height; h += 1) { - this.buffer.push([]); - - for (w = 0; w < this.width; w += 1) { - this.buffer[h][w] = 0; - } - } - - /* Container.Mark(item) - * Marks the item in the buffer, and adds it to the item list. - */ - this.Mark = function (item) { - let x, y; - - //Make sure it is in this container. - if (item.location !== this.location || (item.mode !== 0 && item.mode !== 2)) { - 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; - }; - - /* Container.isLocked(item) - * Checks if the item is in a locked spot - */ - this.IsLocked = function (item, baseRef) { - let h, w, reference; - - reference = baseRef.slice(0); - - //Make sure it is in this container. - if (item.mode !== 0 || 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; - }; - - this.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; - }; - - /* Container.CanFit(item) - * Checks to see if we can fit the item in the buffer. - */ - this.CanFit = function (item) { - return (!!this.FindSpot(item)); - }; - - /* Container.FindSpot(item) - * Finds a spot available in the buffer to place the item. - */ - this.FindSpot = function (item) { - let x, y, nx, ny; - - //Make sure it's a valid item - if (!item) { - return false; - } - - Storage.Reload(); - - //Loop buffer looking for spot to place item. - for (y = 0; y < this.width - (item.sizex - 1); y += 1) { - Loop: - for (x = 0; x < this.height - (item.sizey - 1); x += 1) { - //Check if there is something in this spot. - if (this.buffer[x][y] > 0) { - 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]) { - continue Loop; - } - } - } - - return ({x: x, y: y}); - } - } - - return false; - }; - - /* Container.MoveTo(item) - * Takes any item and moves it into given buffer. - */ - this.MoveTo = function (item) { - let nPos, n, nDelay, cItem, cube; - - try { - //Can we even fit it in here? - nPos = this.FindSpot(item); - - if (!nPos) { - return false; - } - - //Cube -> Stash, must place item in inventory first - if (item.location === 6 && this.location === 7 && !Storage.Inventory.MoveTo(item)) { - return false; - } - - //Can't deal with items on ground! - if (item.mode === 3) { - return false; - } - - //Item already on the cursor. - if (me.itemoncursor && item.mode !== 4) { - return false; - } - - //Make sure stash is open - if (this.location === 7 && !Town.openStash()) { - return false; - } - - //Pick to cursor if not already. - if (!item.toCursor()) { - return false; - } - - //Loop three times to try and place it. - for (n = 0; n < 5; n += 1) { - if (this.location === 6) { // place item into cube - cItem = Game.getCursorUnit(); - cube = me.getItem(sdk.quest.item.Cube); - - if (cItem !== null && cube !== null) { - sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); - } - } else if (this.location === 2) { - cItem = Game.getCursorUnit(); - if (cItem !== null) { - sendPacket(1, sdk.packets.send.ItemToBelt, 4, cItem.gid, 4, nPos.y); - } - } else { - clickItemAndWait(sdk.clicktypes.click.item.Left, nPos.y, nPos.x, this.location); - } - - nDelay = getTickCount(); - - while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 3 + 500)) { - if (!me.itemoncursor) { - print("Successfully placed " + item.name + " at X: " + nPos.x + " Y: " + nPos.y); - delay(200); - - return true; - } - - delay(10); - } - } - - return true; - } catch (e) { - return false; - } - }; - - /* Container.Dump() - * Prints all known information about container. - */ - this.Dump = function () { - let x, y, string; - - print(this.name + " has the width of " + this.width + " and the height of " + this.height); - print(this.name + " has " + this.itemList.length + " items inside, and has " + this.openPositions + " spots left."); - - 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"; - } - - print(string); - } - }; - - /* Container.UsedSpacePercent() - * Returns percentage of the container used. - */ - this.UsedSpacePercent = function () { - let x, y, - usedSpace = 0, - totalSpace = this.height * this.width; - - Storage.Reload(); - - for (x = 0; x < this.height; x += 1) { - for (y = 0; y < this.width; y += 1) { - if (this.buffer[x][y] > 0) { - usedSpace += 1; - } - } - } - - return usedSpace * 100 / totalSpace; - }; - - /* Container.compare(reference) - * Compare given container versus the current one, return all new items in current buffer. - */ - this.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; - } - }; - - this.toSource = function () { - return this.buffer.toSource(); - }; -}; - -let Storage = new function () { - this.Init = function () { - this.StashY = me.gametype === 0 ? 4 : 8; - this.Inventory = new Container("Inventory", 10, 4, 3); - this.TradeScreen = new Container("Inventory", 10, 4, 5); - this.Stash = new Container("Stash", 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) { // nothing equipped - return 1; - } - - do { - if (item.bodylocation === 8) { // belt slot - 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 3: - this.Inventory.Mark(item); - - break; - case 5: - this.TradeScreen.Mark(item); - - break; - case 2: - this.Belt.Mark(item); - - break; - case 6: - this.Cube.Mark(item); - - break; - case 7: - this.Stash.Mark(item); - - break; - } - } while (item.getNext()); - - return true; - }; -}; diff --git a/d2bs/kolbot/libs/common/Util.js b/d2bs/kolbot/libs/common/Util.js deleted file mode 100644 index 68556c5cd..000000000 --- a/d2bs/kolbot/libs/common/Util.js +++ /dev/null @@ -1,231 +0,0 @@ -/** -* @filename Util.js -* @author Jaenster, theBGuy -* @desc utility functions for kolbot -* -*/ - -!isIncluded("Polyfill.js") && include("Polyfill.js"); -// torn on if these include functions should be here or in polyfill - not exactly polyfill functions but sorta? -const includeIfNotIncluded = function (file = "") { - if (!isIncluded(file)) { - if (!include(file)) { - console.error("Failed to include " + file); - console.trace(); - } - } - return true; -}; - -const includeCommonLibs = function () { - let files = dopen("libs/common/").getFiles(); - if (!files.length) throw new Error("Failed to find my files"); - if (!files.includes("Pather.js")) { - console.warn("Incorrect Files?", files); - // something went wrong? - while (!files.includes("Pather.js")) { - files = dopen("libs/common/").getFiles(); - delay(50); - } - } - - files.filter(file => file.endsWith(".js") && !file.match("auto", "gi") && !file.match("util.js", "gi")) - .forEach(function (x) { - if (!includeIfNotIncluded("common/" + x)) { - throw new Error("Failed to include common/" + x); - } - }); -}; - -const includeOOGLibs = function () { - let files = dopen("libs/oog/").getFiles(); - if (!files.length) throw new Error("Failed to find my files"); - if (!files.includes("DataFile.js")) { - console.warn("Incorrect Files?", files); - // something went wrong? - while (!files.includes("DataFile.js")) { - files = dopen("libs/oog/").getFiles(); - delay(50); - } - } - - files.filter(file => file.endsWith(".js")) - .forEach(function (x) { - if (!includeIfNotIncluded("oog/" + x)) { - throw new Error("Failed to include oog/" + x); - } - }); -}; - -/** - * @param args - * @returns Unit[] - */ -const getUnits = function (...args) { - let units = [], unit = getUnit.apply(null, args); - - if (!unit) { - return []; - } - do { - units.push(copyUnit(unit)); - } while (unit.getNext()); - return units; -}; - -const clickItemAndWait = function (...args) { - let timeout = getTickCount(), timedOut; - let before = !me.itemoncursor; - - clickItem.apply(undefined, args); - delay(Math.max(me.ping * 2, 250)); - - - while (true) { // Wait until item is picked up. - delay(3); - - if (before !== !!me.itemoncursor || (timedOut = getTickCount() - timeout > Math.min(1000, 100 + (me.ping * 4)))) { - break; // quit the loop of item on cursor has changed - } - } - - delay(Math.max(me.ping, 50)); - - // return item if we didnt timeout - return !timedOut; -}; - -/** - * @description clickMap doesn't return if we sucessfully clicked a unit just that a click was sent, this checks and returns that a units mode has changed - * as a result of us clicking it. - * @returns boolean - */ -const clickUnitAndWait = function (button, shift, unit) { - if (typeof (unit) !== "object") throw new Error("clickUnitAndWait: Third arg must be a Unit."); - - let before = unit.mode; - - me.blockMouse = true; - clickMap(button, shift, unit); - delay(Math.max(me.ping * 2, 250)); - clickMap(button + 2, shift, unit); - me.blockMouse = false; - - let waitTick = getTickCount(); - let timeOut = Math.min(1000, 100 + (me.ping * 4)); - - while (getTickCount() - waitTick < timeOut) { - delay(30); - - // quit the loop if mode has changed - if (before !== unit.mode) { - break; - } - } - - delay(Math.max(me.ping + 1, 50)); - - return (before !== unit.mode); -}; - -// helper functions in case you find it annoying like me to write while (getTickCount() - tick > 3 * 60 * 1000) which is 3 minutes -// instead we can do while (getTickCount() - tick > Time.minutes(5)) -const Time = { - seconds: function (seconds = 0) { - if (typeof seconds !== "number") return 0; - return (seconds * 1000); - }, - minutes: function (minutes = 0) { - if (typeof minutes !== "number") return 0; - return (minutes * 60000); - }, - format: function (ms = 0) { - return (new Date(ms).toISOString().slice(11, -5)); - } -}; - -const Game = { - getDistance: function (...args) { - switch (args.length) { - case 0: - return Infinity; - case 1: - // getDistance(unit) - returns distance that unit is from me - if (typeof args[0] !== "object") return Infinity; - if (!args[0].hasOwnProperty("x")) return Infinity; - return Math.sqrt(Math.pow((me.x - args[0].x), 2) + Math.pow((me.y - args[0].y), 2)); - case 2: - // getDistance(x, y) - returns distance x, y is from me - // getDistance(unitA, unitB) - returns distace unitA is from unitB - if (typeof args[0] === "number" && typeof args[1] === "number") { - return Math.sqrt(Math.pow((me.x - args[0]), 2) + Math.pow((me.y - args[1]), 2)); - } else if (typeof args[0] === "object" && typeof args[1] === "object") { - if (!args[1].hasOwnProperty("x")) return Infinity; - return Math.sqrt(Math.pow((args[0].x - args[1].x), 2) + Math.pow((args[0].y - args[1].y), 2)); - } - return Infinity; - case 3: - // getDistance(unit, x, y) - returns distance x, y is from unit - if (typeof args[2] !== "number") return Infinity; - if (!args[0].hasOwnProperty("x")) return Infinity; - return Math.sqrt(Math.pow((args[0].x - args[1]), 2) + Math.pow((args[0].y - args[2]), 2)); - case 4: - // getDistance(x1, y1, x2, y2) - if (typeof args[0] !== "number" || typeof args[3] !== "number") return Infinity; - return Math.sqrt(Math.pow((args[0] - args[2]), 2) + Math.pow((args[1] - args[3]), 2)); - default: - return Infinity; - } - }, - getCursorUnit: function () { - return getUnit(100); - }, - getSelectedUnit: function () { - return getUnit(101); - }, - getPlayer: function (id, mode, gid) { - return getUnit(sdk.unittype.Player, id, mode, gid); - }, - getMonster: function (id, mode, gid) { - return getUnit(sdk.unittype.Monster, id, mode, gid); - }, - getNPC: function (id, mode, gid) { - return getUnit(sdk.unittype.NPC, id, mode, gid); - }, - getObject: function (id, mode, gid) { - return getUnit(sdk.unittype.Object, id, mode, gid); - }, - getMissile: function (id, mode, gid) { - return getUnit(sdk.unittype.Missile, id, mode, gid); - }, - getItem: function (id, mode, gid) { - return getUnit(sdk.unittype.Item, id, mode, gid); - }, - getStairs: function (id, mode, gid) { - return getUnit(sdk.unittype.Stairs, id, mode, gid); - }, - getPresetMonster: function (area, id) { - !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Monster, id); - }, - getPresetMonsters: function (area, id) { - !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Monster, id); - }, - getPresetObject: function (area, id) { - !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Object, id); - }, - getPresetObjects: function (area, id) { - !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Object, id); - }, - getPresetStair: function (area, id) { - !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Stairs, id); - }, - getPresetStairs: function (area, id) { - !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Stairs, id); - }, -}; diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index ec22a34a7..332d8397d 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -21,7 +21,7 @@ function LoadConfig() { * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) @@ -301,7 +301,7 @@ function LoadConfig() { // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -323,10 +323,10 @@ function LoadConfig() { sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -434,7 +434,7 @@ function LoadConfig() { Config.FastPick = false; // Check and pick items between attacks Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -467,7 +467,7 @@ function LoadConfig() { Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -571,7 +571,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -580,13 +580,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz @@ -672,7 +672,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -692,7 +692,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index a412c858a..4c2772e3c 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -21,7 +21,7 @@ function LoadConfig() { * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) @@ -301,7 +301,7 @@ function LoadConfig() { // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -323,10 +323,10 @@ function LoadConfig() { sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -434,7 +434,7 @@ function LoadConfig() { Config.FastPick = false; // Check and pick items between attacks Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -467,7 +467,7 @@ function LoadConfig() { Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -577,7 +577,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -586,13 +586,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz @@ -678,7 +678,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -698,7 +698,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 2c658cbe3..5c8a9d7b7 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -21,7 +21,7 @@ function LoadConfig() { * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) @@ -301,7 +301,7 @@ function LoadConfig() { // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -323,10 +323,10 @@ function LoadConfig() { sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -434,7 +434,7 @@ function LoadConfig() { Config.FastPick = false; // Check and pick items between attacks Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -467,7 +467,7 @@ function LoadConfig() { Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -566,7 +566,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -575,13 +575,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz @@ -667,7 +667,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -687,7 +687,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/config/Builds/Class.Build.js b/d2bs/kolbot/libs/config/Builds/Class.Build.js index dfc927c01..c076defcd 100644 --- a/d2bs/kolbot/libs/config/Builds/Class.Build.js +++ b/d2bs/kolbot/libs/config/Builds/Class.Build.js @@ -4,7 +4,7 @@ * * Instructions: See /d2bs/kolbot/libs/config/Builds/README.txt * -* Skill IDs: See /d2bs/kolbot/sdk/skills.txt for a list of skill IDs. +* Skill IDs: See /d2bs/kolbot/sdk/txt/skills.txt for a list of skill IDs. * * Stat IDs: * @@ -16,10 +16,10 @@ */ js_strict(true); -if (!isIncluded("common/Cubing.js")) { include("common/Cubing.js"); }; -if (!isIncluded("common/Prototypes.js")) { include("common/Prototypes.js"); }; -if (!isIncluded("common/Runewords.js")) { include("common/Runewords.js"); }; -if (!isIncluded("common/Town.js")) { include("common/Town.js"); }; +if (!isIncluded("core/Cubing.js")) { include("core/Cubing.js"); }; +if (!isIncluded("core/Prototypes.js")) { include("core/Prototypes.js"); }; +if (!isIncluded("core/Runewords.js")) { include("core/Runewords.js"); }; +if (!isIncluded("core/Town.js")) { include("core/Town.js"); }; var AutoBuildTemplate = { @@ -913,4 +913,4 @@ var AutoBuildTemplate = { Config.LowManaSkill = [-1,-1]; } } -}; \ No newline at end of file +}; diff --git a/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js b/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js index 3d39b9c1e..2f7e0e8e1 100644 --- a/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js +++ b/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js @@ -4,7 +4,7 @@ * * Instructions: See /d2bs/kolbot/libs/config/Builds/README.txt * -* Skill IDs: See /d2bs/kolbot/sdk/skills.txt for a list of skill IDs. +* Skill IDs: See /d2bs/kolbot/sdk/txt/skills.txt for a list of skill IDs. * * Stat IDs: * @@ -16,10 +16,10 @@ */ js_strict(true); -if (!isIncluded("common/Cubing.js")) { include("common/Cubing.js"); }; -if (!isIncluded("common/Prototypes.js")) { include("common/Prototypes.js"); }; -if (!isIncluded("common/Runewords.js")) { include("common/Runewords.js"); }; -if (!isIncluded("common/Town.js")) { include("common/Town.js"); }; +if (!isIncluded("core/Cubing.js")) { include("core/Cubing.js"); }; +if (!isIncluded("core/Prototypes.js")) { include("core/Prototypes.js"); }; +if (!isIncluded("core/Runewords.js")) { include("core/Runewords.js"); }; +if (!isIncluded("core/Town.js")) { include("core/Town.js"); }; var AutoBuildTemplate = { @@ -936,4 +936,4 @@ var AutoBuildTemplate = { Config.LowManaSkill = [-1,-1]; } } -}; \ No newline at end of file +}; diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 13e35d275..c768ec1b3 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -21,7 +21,7 @@ function LoadConfig() { * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) @@ -301,7 +301,7 @@ function LoadConfig() { // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -323,10 +323,10 @@ function LoadConfig() { sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -434,7 +434,7 @@ function LoadConfig() { Config.FastPick = false; // Check and pick items between attacks Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -467,7 +467,7 @@ function LoadConfig() { Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -570,7 +570,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -579,13 +579,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz @@ -671,7 +671,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -691,7 +691,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index fc30e650b..956dffde9 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -21,7 +21,7 @@ function LoadConfig() { * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) @@ -301,7 +301,7 @@ function LoadConfig() { // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -323,10 +323,10 @@ function LoadConfig() { sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -434,7 +434,7 @@ function LoadConfig() { Config.FastPick = false; // Check and pick items between attacks Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -467,7 +467,7 @@ function LoadConfig() { Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -591,7 +591,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -600,13 +600,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz @@ -692,7 +692,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -712,7 +712,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 218cd92b2..9176c1a1c 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -21,7 +21,7 @@ function LoadConfig() { * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) @@ -301,7 +301,7 @@ function LoadConfig() { // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -323,10 +323,10 @@ function LoadConfig() { sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -434,7 +434,7 @@ function LoadConfig() { Config.FastPick = false; // Check and pick items between attacks Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -467,7 +467,7 @@ function LoadConfig() { Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -570,7 +570,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -579,13 +579,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz @@ -671,7 +671,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -691,7 +691,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index f2cff764d..57fb1753c 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -21,7 +21,7 @@ function LoadConfig() { * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) @@ -301,7 +301,7 @@ function LoadConfig() { // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -323,10 +323,10 @@ function LoadConfig() { sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -434,7 +434,7 @@ function LoadConfig() { Config.FastPick = false; // Check and pick items between attacks Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -467,7 +467,7 @@ function LoadConfig() { Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -572,7 +572,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -581,13 +581,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz @@ -673,7 +673,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -693,7 +693,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 2f757fd65..05b47e1e5 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -4,7 +4,7 @@ * - copy the desired script/config section and paste it to your character configuration file, named Class.CharName.js */ - // User addon script. Read the description in libs/bots/UserAddon.js + // User addon script. Read the description in libs/scripts/UserAddon.js Scripts.UserAddon = false; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! // Battle orders script - Use this for 2+ characters @@ -282,7 +282,7 @@ // ##### EXTRA SCRIPTS ##### // Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum @@ -304,10 +304,10 @@ sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/areas.txt + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland @@ -363,7 +363,7 @@ Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See NTItemAlias.dbl for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -407,7 +407,7 @@ /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in NTItemAlias.dbl or modules/sdk.js + // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -416,7 +416,7 @@ Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/chests.txt for full list of chest names + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names // ########################### // /* ##### PUBLIC SETTINGS ##### */ @@ -449,7 +449,7 @@ Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // DClone config @@ -622,7 +622,7 @@ Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -631,12 +631,12 @@ // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check NTItemAlias.dbl + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Gem, "Chipped Amethyst"]); // make FlawedAmethyst //Config.Recipes.push([Recipe.Gem, "Chipped Topaz"]); // make Flawed Topaz @@ -672,7 +672,7 @@ //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Rune, "El Rune"]); // Upgrade El to Eld //Config.Recipes.push([Recipe.Rune, "Eld Rune"]); // Upgrade Eld to Tir @@ -702,7 +702,7 @@ //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex - // Ingredients for the following recipes will be auto-picked, for classids check libs/NTItemAlias.dbl + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Helm //Config.Recipes.push([Recipe.Blood.Boots, "Mirrored Boots"]); // Craft Blood Boots @@ -837,7 +837,7 @@ // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -857,7 +857,7 @@ * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/common/Attack.js b/d2bs/kolbot/libs/core/Attack.js similarity index 82% rename from d2bs/kolbot/libs/common/Attack.js rename to d2bs/kolbot/libs/core/Attack.js index 453e323ce..5b27b292c 100644 --- a/d2bs/kolbot/libs/common/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1,10 +1,15 @@ /** -* @filename Attack.js -* @author kolton, theBGuy -* @desc handle player attacks -* -*/ + * @filename Attack.js + * @author kolton, theBGuy + * @desc handle player attacks + * + * @typedef {import("../../sdk/globals")} + */ +/** + * Attack + * @global + */ const Attack = { infinity: false, auradin: false, @@ -25,12 +30,12 @@ const Attack = { // Initialize attacks init: function () { 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 { - include("common/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); + include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); } if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { @@ -48,7 +53,11 @@ const Attack = { } }, - // check if slot has items + /** + * @description check if slot has items + * @param {0 | 1} slot + * @returns {boolean} If weapon slot has an item equipped + */ checkSlot: function (slot = me.weaponswitch) { let item = me.getItem(-1, sdk.items.mode.Equipped); @@ -69,6 +78,10 @@ const Attack = { return false; }, + /** + * @description Automatically determine primary weapon slot, Weapon slot with items that isn't a CTA + * @returns {0 | 1 | -1} Primary weapon slot + */ getPrimarySlot: function () { // determine primary slot if not set if (Config.PrimarySlot === -1) { @@ -99,14 +112,23 @@ const Attack = { return Config.PrimarySlot; }, + /** + * + * @param {Monster} unit + * @returns {[number, number] | boolean} + * @todo add checking for other options than just name/classid + * - option for based on spectype + * - option for based on enchant/aura + */ getCustomAttack: function (unit) { // Check if unit got invalidated if (!unit || !unit.name || !copyUnit(unit).x) return false; - + for (let i in Config.CustomAttack) { if (Config.CustomAttack.hasOwnProperty(i)) { // if it contains numbers but is a string, convert to an int - if (i.match(/\d+/g)) { + if (typeof i === "string" && i.match(/\d+/g)) { + // @ts-ignore i = parseInt(i, 10); } @@ -128,7 +150,11 @@ const Attack = { return false; }, - // Get items with charges - isn't used anywhere + /** + * @depreciated + * @description Get items with charges - isn't used anywhere + * @returns {boolean} + */ getCharges: function () { !Skill.charges && (Skill.charges = []); @@ -169,7 +195,10 @@ const Attack = { return true; }, - // Check if player or his merc are using Infinity, and adjust resistance checks based on that + /** + * @description Check if player or his merc are using Infinity, and adjust resistance checks based on that + * @returns {boolean} + */ checkInfinity: function () { if (me.classic) return false; @@ -178,31 +207,42 @@ const Attack = { Config.UseMerc && !me.mercrevivecost && (merc = Misc.poll(() => me.getMerc(), 1000, 100)); // Check merc infinity - !!merc && (this.infinity = merc.checkItem({name: sdk.locale.items.Infinity}).have); + !!merc && (Attack.infinity = merc.checkItem({name: sdk.locale.items.Infinity}).have); // Check player infinity - only check if merc doesn't have - !this.infinity && (this.infinity = me.checkItem({name: sdk.locale.items.Infinity, equipped: true}).have); + !Attack.infinity && (Attack.infinity = me.checkItem({name: sdk.locale.items.Infinity, equipped: true}).have); - return this.infinity; + return Attack.infinity; }, + /** + * @description Check if player is using Dragon, Dream, HoJ, or Ice, and adjust resistance checks based on that + * @returns {boolean} + */ checkAuradin: function () { - // Check player Dragon, Dream, HoJ, or Ice - this.auradin = me.haveSome([ + Attack.auradin = me.haveSome([ {name: sdk.locale.items.Dragon, equipped: true}, {name: sdk.locale.items.Dream, equipped: true}, {name: sdk.locale.items.HandofJustice, equipped: true}, {name: sdk.locale.items.Ice, equipped: true}, ]); - return this.auradin; + return Attack.auradin; }, - // just check if we can telestomp + /** + * @description check if we can telestomp a unit + * @param {Unit} unit + * @returns {boolean} + */ canTeleStomp: function (unit) { if (!unit || !unit.attackable) return false; return Config.TeleStomp && Config.UseMerc && Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() && Attack.validSpot(unit.x, unit.y); }, - // Kill a monster based on its classId, can pass a unit as well + /** + * @description Kill a monster based on its classId, can pass a unit as well + * @param {Unit | number} classId + * @returns {boolean} If we managed to kill the unit + */ kill: function (classId) { if (!classId || Config.AttackSkill[1] < 0) return false; let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); @@ -298,9 +338,15 @@ const Attack = { return (!target || !copyUnit(target).x || target.dead || !target.attackable); }, + /** + * @description hurt a unit to a certain percentage of life left + * @param {string | number | Unit} classId + * @param {number} percent + * @returns {boolean} + */ hurt: function (classId, percent) { if (!classId || !percent) return false; - let target = (typeof classId === "object" ? classid : Misc.poll(() => Game.getMonster(classId), 2000, 100)); + let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); if (!target) { console.warn("Attack.hurt: Target not found"); @@ -343,6 +389,11 @@ const Attack = { return true; }, + /** + * @description Determine scariness of monster for monster sorting + * @param {Unit} unit + * @returns {number} scariness + */ getScarinessLevel: function (unit) { // todo - define summonertype prototype let scariness = 0; @@ -369,8 +420,16 @@ const Attack = { return scariness; }, - // 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 + /** + * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster + * @param {number} [range=25] + * @param {number} [spectype=0] + * @param {number | Unit} [bossId] + * @param {Function} [sortfunc] + * @param {boolean} [pickit] + * @returns {boolean} + * @todo change to passing an object + */ clear: function (range, spectype, bossId, sortfunc, pickit = true) { while (!me.gameReady) { delay(40); @@ -434,21 +493,22 @@ const Attack = { while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { if (me.dead) 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]); + // target = copyUnit(monsterList[0]); + target = Game.getMonster(-1, -1, monsterList[0].gid); - if (target.x !== undefined && (getDistance(target, orgx, orgy) <= range || (this.getScarinessLevel(target) > 7 && target.distance <= range)) + if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range || (this.getScarinessLevel(target) > 7 && target.distance <= range)) && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Misc.townCheck(true); + Misc.townCheck(); tick = getTickCount(); if (!logged && boss && boss.gid === target.gid) { logged = true; console.log("ÿc7Clear ÿc0:: " + (!!target.name ? target.name : bossId)); } - //me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); + // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); let result = ClassAttack.doAttack(target, attackCount % 15 === 0); @@ -510,14 +570,23 @@ const Attack = { monsterList.shift(); } - if (target.dead || Config.FastPick) { + /** + * @todo allow for more aggressive horking here + */ + if (target.dead || Config.FastPick || Config.FastFindItem) { if (boss && boss.gid === target.gid && target.dead) { killedBoss = true; console.log("ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); } + Config.FastFindItem && pickit && ClassAttack.findItem(); Pickit.fastPick(); } } else { + if (me.inArea(sdk.areas.ChaosSanctuary) && target.classid === sdk.monsters.StormCaster1) { + // probably behind the wall - skip them + monsterList.shift(); + retry = 0; + } if (retry++ > 3) { monsterList.shift(); retry = 0; @@ -550,6 +619,11 @@ const Attack = { return true; }, + /** + * @description clear all monsters based on classid arguments + * @param {...number} ids + * @returns {boolean} + */ clearClassids: function (...ids) { let monster = Game.getMonster(); @@ -568,7 +642,14 @@ const Attack = { return true; }, - // Filter monsters based on classId, spectype and range + /** + * @description Filter monsters based on classId, spectype and range + * @param {number} classid + * @param {number} spectype + * @param {number} range + * @param {Unit | {x: number, y: number}} center + * @returns {Monster[]} + */ getMob: function (classid, spectype, range, center) { let monsterList = []; let monster = Game.getMonster(); @@ -609,7 +690,13 @@ const Attack = { return monsterList; }, - // Clear an already formed array of monstas + /** + * @description Clear an already formed array of monstas + * @param {Function | Array} mainArg + * @param {Function} [sortFunc] + * @param {boolean} [refresh] + * @returns {boolean} + */ clearList: function (mainArg, sortFunc, refresh) { let i, target, monsterList; let retry = 0; @@ -618,7 +705,7 @@ const Attack = { switch (typeof mainArg) { case "function": - monsterList = mainArg.call(); + monsterList = mainArg.call(this); break; case "object": @@ -645,7 +732,7 @@ const Attack = { if (target.x !== undefined && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Misc.townCheck(true); + Misc.townCheck(); //me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); let result = ClassAttack.doAttack(target, attackCount % 15 === 0); @@ -728,6 +815,15 @@ const Attack = { return true; }, + /** + * @param {number} x + * @param {number} y + * @param {number} [range=15] + * @param {number} [timer=3000] - time in ms + * @param {boolean} [skipBlocked=true] + * @param {boolean} [special=false] + * @returns {void} + */ securePosition: function (x, y, range = 15, timer = 3000, skipBlocked = true, special = false) { let tick; @@ -774,7 +870,11 @@ const Attack = { } }, - // Draw lines around a room on minimap + /** + * @description Draw lines around a room on minimap + * @param {Room} room + * @param {number} color + */ markRoom: function (room, color) { let arr = []; @@ -784,26 +884,33 @@ const Attack = { arr.push(new Line(room.x * 5, room.y * 5 + room.ysize, room.x * 5 + room.xsize, room.y * 5 + room.ysize, color, true)); }, + /** + * @description Count uniques in current area within getUnit range + */ countUniques: function () { - !this.uniques && (this.uniques = 0); - !this.ignoredGids && (this.ignoredGids = []); + !Attack.uniques && (Attack.uniques = 0); + !Attack.ignoredGids && (Attack.ignoredGids = []); let monster = Game.getMonster(); if (monster) { do { - if ((monster.isSuperUnique) && this.ignoredGids.indexOf(monster.gid) === -1) { - this.uniques += 1; - this.ignoredGids.push(monster.gid); + if ((monster.isSuperUnique) && Attack.ignoredGids.indexOf(monster.gid) === -1) { + Attack.uniques += 1; + Attack.ignoredGids.push(monster.gid); } } while (monster.getNext()); } }, + /** + * @description Store average unique monsters counted in area during run + * @param {number} area + */ storeStatistics: function (area) { - !FileTools.exists("statistics.json") && Misc.fileAction("statistics.json", 1, "{}"); + !FileTools.exists("statistics.json") && FileAction.write("statistics.json", "{}"); - let obj = JSON.parse(Misc.fileAction("statistics.json", 0)); + let obj = JSON.parse(FileAction.read("statistics.json")); if (obj) { if (obj[area] === undefined) { @@ -813,17 +920,21 @@ const Attack = { }; } - obj[area].averageUniques = ((obj[area].averageUniques * obj[area].runs + this.uniques) / (obj[area].runs + 1)).toFixed(4); + obj[area].averageUniques = ((obj[area].averageUniques * obj[area].runs + Attack.uniques) / (obj[area].runs + 1)).toFixed(4); obj[area].runs += 1; - Misc.fileAction("statistics.json", 1, JSON.stringify(obj)); + FileAction.write("statistics.json", JSON.stringify(obj)); } - this.uniques = 0; - this.ignoredGids = []; + Attack.uniques = 0; + Attack.ignoredGids = []; }, - // Clear an entire area based on monster spectype + /** + * @description Clear an entire area based on monster spectype + * @param {number} spectype + * @returns {boolean} + */ clearLevel: function (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]); @@ -833,7 +944,7 @@ const Attack = { if (!room) return false; console.time("clearLevel"); - console.info(true, Pather.getAreaName(me.area)); + console.info(true, getAreaName(me.area)); let myRoom, previousArea; let rooms = []; @@ -850,7 +961,7 @@ const Attack = { if (me.area >= sdk.areas.TalRashasTomb1 && me.area <= sdk.areas.TalRashasTomb7) { say("clearlevel " + me.area); } else { - say("clearlevel " + Pather.getAreaName(currentArea)); + say("clearlevel " + getAreaName(currentArea)); } } @@ -896,14 +1007,20 @@ const Attack = { } } - //this.storeStatistics(Pather.getAreaName(me.area)); - console.info(false, Pather.getAreaName(currentArea), "clearLevel"); + //this.storeStatistics(getAreaName(me.area)); + console.info(false, getAreaName(currentArea), "clearLevel"); return true; }, - // Sort monsters based on distance, spectype and classId (summoners are attacked first) - // Think this needs a collison check included for non tele chars, might prevent choosing closer mob that is actually behind a wall vs the one we pass trying to get behind the wall + /** + * @description Sort monsters based on distance, spectype and classId (summoners are attacked first) + * @param {Unit} unitA + * @param {Unit} unitB + * @returns {boolean} + * @todo Think this needs a collison check included for non tele chars, might prevent choosing + * closer mob that is actually behind a wall vs the one we pass trying to get behind the wall + */ sortMonsters: function (unitA, unitB) { // No special sorting for were-form if (Config.Wereform) return getDistance(me, unitA) - getDistance(me, unitB); @@ -970,11 +1087,18 @@ const Attack = { return getDistance(me, unitA) - getDistance(me, unitB); }, - // Check if a set of coords is valid/accessable - // re-work this for more info - // casting skills can go over non-floors - excluding bliz/meteor - not sure if any others - // physical skills can't, need to exclude monster objects though - // splash skills can go through some objects, however some objects are cast blockers + /** + * @description Check if a set of coords is valid/accessable + * @param {number} x + * @param {number} y + * @param {number} [skill=-1] + * @param {number} [unitid=0] + * @returns {boolean} If the spot is a valid location for walking/casting/attack + * @todo re-work this for more info: + * - casting skills can go over non-floors - excluding bliz/meteor - not sure if any others + * - physical skills can't, need to exclude monster objects though + * - splash skills can go through some objects, however some objects are cast blockers + */ validSpot: function (x, y, skill = -1, unitid = 0) { // Just in case if (!me.area || !x || !y) return false; @@ -1019,7 +1143,13 @@ const Attack = { return true; }, - // Open chests when clearing + /** + * @description Open chests when clearing + * @param {number} range + * @param {number} [x=me.x] + * @param {number} [y=me.y] + * @returns {boolean} + */ openChests: function (range, x, y) { if (!Config.OpenChests.Enabled) return false; (typeof x !== "number" || typeof y !== "number") && ({x, y} = me); @@ -1048,6 +1178,10 @@ const Attack = { return true; }, + /** + * @description build list of attackable monsters currently around us + * @returns {Array | []} + */ buildMonsterList: function () { let monList = []; let monster = Game.getMonster(); @@ -1063,6 +1197,13 @@ const Attack = { return monList; }, + /** + * @param {Unit} unit + * @param {number} distance + * @param {number} spread + * @param {number} range + * @returns {{x: number, y: number}} x/y coords of safe spot + */ findSafeSpot: function (unit, distance, spread, range) { if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); @@ -1158,9 +1299,9 @@ const Attack = { }, /** - * @param unit - * @desc checks if we should skip a monster - * @returns Boolean + * @description checks if we should skip a monster + * @param {Unit} unit + * @returns {Boolean} If we should skip this monster */ skipCheck: function (unit) { if (me.inArea(sdk.areas.ThroneofDestruction)) return false; @@ -1266,9 +1407,13 @@ const Attack = { return false; }, - // Get element by skill number + /** + * @description Get element by skill number + * @param {number} skillId + * @returns {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none" | false} + */ getSkillElement: function (skillId) { - this.elements = ["physical", "fire", "lightning", "magic", "cold", "poison", "none"]; + let elements = ["physical", "fire", "lightning", "magic", "cold", "poison", "none"]; switch (skillId) { case sdk.skills.HolyFire: @@ -1291,10 +1436,15 @@ const Attack = { let eType = getBaseStat("skills", skillId, "etype"); - return typeof (eType) === "number" ? this.elements[eType] : false; + return typeof (eType) === "number" ? elements[eType] : false; }, - // Get a monster's resistance to specified element + /** + * @description Get a monster's resistance to specified element + * @param {Unit | Monster} unit + * @param {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"} type + * @returns {number} + */ getResist: function (unit, type) { // some scripts pass empty units in throne room if (!unit || !unit.getStat) return 100; @@ -1319,11 +1469,10 @@ const Attack = { if (getBaseStat("monstats", unit.classid, "lUndead") || getBaseStat("monstats", unit.classid, "hUndead")) { return 0; } - + // eslint-disable-next-line no-fallthrough + default: return 100; } - - return 100; }, getLowerResistPercent: function () { @@ -1411,7 +1560,11 @@ const Attack = { return this.getResist(unit, damageType) < maxres; }, - // Check if we have valid skills to attack a monster + /** + * Check if we have valid skills to attack a monster + * @param {Monster} unit + * @returns {boolean} + */ canAttack: function (unit) { if (unit.isMonster) { // Unique/Champion @@ -1433,7 +1586,10 @@ const Attack = { return false; }, - // Detect use of bows/crossbows + /** + * Detect use of bows/crossbows + * @returns {string | false} + */ usingBow: function () { let item = me.getItem(-1, sdk.items.mode.Equipped); @@ -1454,12 +1610,22 @@ const Attack = { return false; }, - // Find an optimal attack position and move or walk to it - getIntoPosition: function (unit, distance, coll, walk) { + /** + * Find an optimal attack position and move or walk to it + * @param {Unit} unit + * @param {number} distance + * @param {number} coll + * @param {boolean} walk + * @param {boolean} force + * @returns {boolean} sucessfully found and moved into position + */ + getIntoPosition: function (unit, distance, coll, walk, force) { if (!unit || !unit.x || !unit.y) return false; walk === true && (walk = 1); + force && console.debug("Forcing new position"); + if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { //me.overhead("Short range"); @@ -1479,6 +1645,7 @@ const Attack = { let name = unit.hasOwnProperty("name") ? unit.name : ""; let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 135, -135, 180]; + const { x: orgX, y: orgY } = me; //let t = getTickCount(); @@ -1489,6 +1656,8 @@ const Attack = { let cx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * distance + unit.x); let cy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * 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}); } @@ -1503,27 +1672,25 @@ const Attack = { // Valid position found if (!CollMap.checkColl({x: coords[i].x, y: coords[i].y}, unit, coll, 1)) { //print("ÿc9optimal pos build time: ÿc2" + (getTickCount() - t) + " ÿc9distance from target: ÿc2" + getDistance(cx, cy, unit.x, unit.y)); - - switch (walk) { - case 1: - Pather.walkTo(coords[i].x, coords[i].y, 2); - - break; - case 2: - if (coords[i].distance < 6 && !CollMap.checkColl(me, coords[i], sdk.collision.WallOrRanged)) { - Pather.walkTo(coords[i].x, coords[i].y, 2); - } else { - Pather.moveTo(coords[i].x, coords[i].y, 1); + if ((() => { + switch (walk) { + case 1: + return Pather.walkTo(coords[i].x, coords[i].y, 2); + case 2: + if (coords[i].distance < 6 && !CollMap.checkColl(me, coords[i], sdk.collision.WallOrRanged)) { + return Pather.walkTo(coords[i].x, coords[i].y, 2); + } else { + return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); + } + default: + return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); } - - break; - default: - Pather.moveTo(coords[i].x, coords[i].y, 1); - - break; + })()) { + if (Config.DebugMode && force) { + console.debug("Sucessfully got into position. orginal Loc: " + orgX + "/" + orgY + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance); + } + return true; } - - return true; } } } @@ -1534,6 +1701,11 @@ const Attack = { return false; }, + /** + * Find the nearest monster to us with optional exception parameters + * @param {{ skipBlocked?: boolean, skipImmune?: boolean, skipGid?: number}} givenSettings + * @returns {Monster | false} + */ getNearestMonster: function (givenSettings = {}) { const settings = Object.assign({}, { skipBlocked: true, @@ -1564,6 +1736,11 @@ const Attack = { return !!gid ? Game.getMonster(-1, -1, gid) : false; }, + /** + * Check valid corpse for Redemption/Horking/Summoning + * @param {Monster} unit + * @returns {boolean} valid corpse + */ checkCorpse: function (unit) { if (!unit || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) return false; if (unit.classid <= sdk.monsters.BurningDeadArcher2 && !getBaseStat("monstats2", unit.classid, "corpseSel")) return false; @@ -1573,6 +1750,12 @@ const Attack = { ].every(state => !unit.getState(state))); }, + /** + * Get valid corpses for Redemption/Horking/Summoning + * @param {Monster} unit + * @param {number} range + * @returns {Monster[]} + */ checkNearCorpses: function (unit, range = 15) { let corpses = getUnits(sdk.unittype.Monster).filter(function (corpse) { return getDistance(corpse, unit) <= range && Attack.checkCorpse(corpse); @@ -1580,6 +1763,10 @@ const Attack = { return corpses.length > 0 ? corpses : []; }, + /** + * @param {Monster} unit + * @returns {boolean} + */ whirlwind: function (unit) { if (!unit.attackable) return true; diff --git a/d2bs/kolbot/libs/common/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js similarity index 100% rename from d2bs/kolbot/libs/common/Attacks/Amazon.js rename to d2bs/kolbot/libs/core/Attacks/Amazon.js diff --git a/d2bs/kolbot/libs/common/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js similarity index 100% rename from d2bs/kolbot/libs/common/Attacks/Assassin.js rename to d2bs/kolbot/libs/core/Attacks/Assassin.js diff --git a/d2bs/kolbot/libs/common/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js similarity index 73% rename from d2bs/kolbot/libs/common/Attacks/Barbarian.js rename to d2bs/kolbot/libs/core/Attacks/Barbarian.js index c8305aa14..8ae279087 100644 --- a/d2bs/kolbot/libs/common/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -5,9 +5,18 @@ * */ -// todo - add howl +/** + * @todo + * - Add howl + * - Fix item find bug + */ const ClassAttack = { + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ doAttack: function (unit, preattack = false) { if (!unit) return Attack.Result.SUCCESS; let gid = unit.gid; @@ -61,6 +70,10 @@ const ClassAttack = { return this.doCast(unit, attackSkill); }, + /** + * Check if we need to precast, repair items, or perform findItem + * @param {boolean} pickit - determines if we use findItem or not + */ afterAttack: function (pickit = true) { Precast.doPrecast(false); @@ -71,6 +84,11 @@ const ClassAttack = { pickit && this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); }, + /** + * @param {Monster} unit + * @param {number} attackSkill + * @returns {AttackResult} + */ doCast: function (unit, attackSkill = -1) { if (attackSkill < 0) return Attack.Result.CANTATTACK; // check if unit became invalidated @@ -106,6 +124,11 @@ const ClassAttack = { } }, + /** + * Check whether there are any monsters in range that are attackable + * @param {number} range + * @returns {boolean} + */ checkCloseMonsters: function (range = 10) { let monster = Game.getMonster(); @@ -122,13 +145,17 @@ const ClassAttack = { return false; }, + /** + * Use findItem skill to hork bodies + * @param {number} range + * @returns {boolean} + */ findItem: function (range = 10) { if (!Skill.canUse(sdk.skills.FindItem)) return false; let retry = false; let corpseList = []; - let orgX = me.x; - let orgY = me.y; + const { x: orgX, y: orgY } = me; MainLoop: for (let i = 0; i < 3; i += 1) { @@ -146,34 +173,53 @@ const ClassAttack = { if (this.checkCloseMonsters(5)) { Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); Attack.clear(10, false, false, false, false); - retry = true; + Pather.moveToEx(orgX, orgY, { allowPicking: false }); - break MainLoop; + continue MainLoop; } corpseList.sort(Sort.units); - corpse = corpseList.shift(); + let 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)) { - (corpse.distance > 30 || checkCollision(me, corpse, sdk.collision.BlockWall)) && Pather.moveToUnit(corpse); + (corpse.distance > 30 || checkCollision(me, corpse, sdk.collision.BlockWall)) && 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 + if (!corpse || !copyUnit(corpse).x) { + console.debug("We lost reference to the 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)) { + Pickit.fastPick(); - 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)); + } } } } diff --git a/d2bs/kolbot/libs/common/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js similarity index 100% rename from d2bs/kolbot/libs/common/Attacks/Druid.js rename to d2bs/kolbot/libs/core/Attacks/Druid.js diff --git a/d2bs/kolbot/libs/common/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js similarity index 92% rename from d2bs/kolbot/libs/common/Attacks/Necromancer.js rename to d2bs/kolbot/libs/core/Attacks/Necromancer.js index 938edefb3..3be9b1f23 100644 --- a/d2bs/kolbot/libs/common/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -12,12 +12,14 @@ const ClassAttack = { maxRevives: 0, setArmySize: function () { - this.maxSkeletons = Config.Skeletons === "max" ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeleton) : Config.Skeletons; - this.maxMages = Config.SkeletonMages === "max" ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeletalMage) : Config.SkeletonMages; - this.maxRevives = Config.Revives === "max" ? Skill.getMaxSummonCount(sdk.skills.Revive) : Config.Revives; + ClassAttack.maxSkeletons = Config.Skeletons === "max" ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeleton) : Config.Skeletons; + ClassAttack.maxMages = Config.SkeletonMages === "max" ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeletalMage) : Config.SkeletonMages; + ClassAttack.maxRevives = Config.Revives === "max" ? Skill.getMaxSummonCount(sdk.skills.Revive) : Config.Revives; }, - // Returns: true - doesn't use summons or has all he can summon, false - not full of summons yet + /** + * @returns {boolean} true - doesn't use summons or has all he can summon, false - not full of summons yet + */ isArmyFull: function () { // This necro doesn't summon anything so assume he's full if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { @@ -38,6 +40,12 @@ const ClassAttack = { return true; }, + /** + * Check if we can use specific curse on monster + * @param {Monster} unit + * @param {number} curseID + * @returns {boolean} + */ canCurse: function (unit, curseID) { if (unit === undefined || unit.dead || !Skill.canUse(curseID)) return false; @@ -77,6 +85,10 @@ const ClassAttack = { return state ? !unit.getState(state) : false; }, + /** + * @param {Monster} unit + * @returns {number | boolean} + */ getCustomCurse: function (unit) { if (Config.CustomCurse.length <= 0) return false; @@ -100,9 +112,15 @@ const ClassAttack = { return false; }, + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills + */ doAttack: function (unit, preattack = false) { if (!unit || unit.dead) return Attack.Result.SUCCESS; + let mercRevive = 0; let gid = unit.gid; let classid = unit.classid; @@ -252,7 +270,12 @@ const ClassAttack = { this.novaTick = 0; }, - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills + */ doCast: function (unit, timedSkill = -1, untimedSkill = -1) { // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; @@ -336,6 +359,9 @@ const ClassAttack = { return Attack.Result.SUCCESS; }, + /** + * @param {number} range + */ raiseArmy: function (range = 25) { let tick, count; @@ -416,6 +442,9 @@ const ClassAttack = { return true; }, + /** + * @param {Monster} unit + */ explodeCorpses: function (unit) { if (Config.ExplodeCorpses === 0 || unit.dead) return false; @@ -472,6 +501,10 @@ const ClassAttack = { return true; }, + /** + * @param {Monster} monster + * @param {number} range + */ checkCorpseNearMonster: function (monster, range) { let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); @@ -489,6 +522,10 @@ const ClassAttack = { return false; }, + /** + * @param {Unit} unit + * @param {boolean} revive + */ checkCorpse: function (unit, revive = false) { if (!unit || unit.mode !== sdk.monsters.mode.Dead) return false; diff --git a/d2bs/kolbot/libs/common/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js similarity index 91% rename from d2bs/kolbot/libs/common/Attacks/Paladin.js rename to d2bs/kolbot/libs/core/Attacks/Paladin.js index 042d844fd..824b25d13 100644 --- a/d2bs/kolbot/libs/common/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -8,6 +8,11 @@ const ClassAttack = { attackAuras: [sdk.skills.HolyFire, sdk.skills.HolyFreeze, sdk.skills.HolyShock], + /** + * @param {Unit} unit + * @param {boolean} [preattack] + * @returns {AttackResult} + */ doAttack: function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; let gid = unit.gid; @@ -129,6 +134,21 @@ const ClassAttack = { delay(1500); } } + + /** + * @todo add config options for these and possibly add to Pather.walkTo + */ + // if (Skill.canUse(sdk.skills.Cleansing) + // && ([sdk.states.AmplifyDamage, sdk.states.Decrepify].some(s => me.getState(s)) || me.hpPercent < 70 && me.getState(sdk.states.Poison)) + // && !me.checkForMobs({range: 12, coll: sdk.collision.BlockWall}) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + // me.overhead("Delaying for a second to get rid of Poison"); + // Misc.poll(() => (![sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Poison].some(s => me.getState(s)) || 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); + // } }, doCast: function (unit, attackSkill = -1, aura = -1) { diff --git a/d2bs/kolbot/libs/common/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js similarity index 100% rename from d2bs/kolbot/libs/common/Attacks/Sorceress.js rename to d2bs/kolbot/libs/core/Attacks/Sorceress.js diff --git a/d2bs/kolbot/libs/common/Attacks/Wereform.js b/d2bs/kolbot/libs/core/Attacks/Wereform.js similarity index 99% rename from d2bs/kolbot/libs/common/Attacks/Wereform.js rename to d2bs/kolbot/libs/core/Attacks/Wereform.js index 93ce8dcbd..b8c2bdc35 100644 --- a/d2bs/kolbot/libs/common/Attacks/Wereform.js +++ b/d2bs/kolbot/libs/core/Attacks/Wereform.js @@ -37,7 +37,7 @@ const ClassAttack = { this.maulBoost = ((Math.floor(me.getSkill(sdk.skills.Maul, sdk.skills.subindex.SoftPoints) / 2) + 3) * 20) + this.baseED; } - Misc.shapeShift(Config.Wereform); + Skill.shapeShift(Config.Wereform); if (((Config.AttackSkill[0] === sdk.skills.FeralRage && (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < this.feralBoost)) || (Config.AttackSkill[0] === sdk.skills.Maul && (!me.getState(sdk.states.Maul) || me.getStat(sdk.stats.DamagePercent) < this.maulBoost)) diff --git a/d2bs/kolbot/libs/common/AutoBuild.js b/d2bs/kolbot/libs/core/Auto/AutoBuild.js similarity index 92% rename from d2bs/kolbot/libs/common/AutoBuild.js rename to d2bs/kolbot/libs/core/Auto/AutoBuild.js index 2b86d09fe..245507a89 100644 --- a/d2bs/kolbot/libs/common/AutoBuild.js +++ b/d2bs/kolbot/libs/core/Auto/AutoBuild.js @@ -1,7 +1,7 @@ /** * @filename AutoBuild.js * @author alogwe -* @desc This script is included when any script includes libs/common/Config.js and calls Config.init(). +* @desc This script is included when any script includes libs/core/Config.js and calls Config.init(). * If enabled, loads a threaded helper script that will monitor changes in character level and * upon level up detection, it will spend skill and stat points based on a configurable * character build template file located in libs/config/Builds/*. @@ -12,9 +12,9 @@ */ js_strict(true); -!isIncluded("common/Prototypes.js") && include("common/Prototypes.js"); -!isIncluded("common/Cubing.js") && include("common/Cubing.js"); -!isIncluded("common/Runewords.js") && include("common/Runewords.js"); +!isIncluded("core/Prototypes.js") && include("core/Prototypes.js"); +!isIncluded("core/Cubing.js") && include("core/Cubing.js"); +!isIncluded("core/Runewords.js") && include("core/Runewords.js"); const AutoBuild = new function AutoBuild () { Config.AutoBuild.DebugMode && (Config.AutoBuild.Verbose = true); @@ -68,7 +68,7 @@ const AutoBuild = new function AutoBuild () { // Only load() helper thread from default.dbj if it isn't loaded if (currentScript === "default.dbj" && !getScript("tools\\autobuildthread.js")) { - load("tools/autobuildthread.js"); + load("threads/autobuildthread.js"); } // All threads except autobuildthread.js use this event listener diff --git a/d2bs/kolbot/libs/common/AutoSkill.js b/d2bs/kolbot/libs/core/Auto/AutoSkill.js similarity index 98% rename from d2bs/kolbot/libs/common/AutoSkill.js rename to d2bs/kolbot/libs/core/Auto/AutoSkill.js index 9b580a0da..85dd27ecb 100644 --- a/d2bs/kolbot/libs/common/AutoSkill.js +++ b/d2bs/kolbot/libs/core/Auto/AutoSkill.js @@ -14,7 +14,7 @@ const AutoSkill = new function () { skillBuildOrder Settings Set skillBuildOrder in the array form: [[skill, count, satisfy], [skill, count, satisfy], ... [skill, count, satisfy]] - skill - skill id number (see /sdk/skills.txt) + skill - skill id number (see /sdk/txt/skills.txt) count - maximum number of skill points to allocate for that skill satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. diff --git a/d2bs/kolbot/libs/common/AutoStat.js b/d2bs/kolbot/libs/core/Auto/AutoStat.js similarity index 100% rename from d2bs/kolbot/libs/common/AutoStat.js rename to d2bs/kolbot/libs/core/Auto/AutoStat.js diff --git a/d2bs/kolbot/libs/common/CollMap.js b/d2bs/kolbot/libs/core/CollMap.js similarity index 100% rename from d2bs/kolbot/libs/common/CollMap.js rename to d2bs/kolbot/libs/core/CollMap.js diff --git a/d2bs/kolbot/libs/core/Common.js b/d2bs/kolbot/libs/core/Common.js new file mode 100644 index 000000000..4a29cf635 --- /dev/null +++ b/d2bs/kolbot/libs/core/Common.js @@ -0,0 +1,11 @@ +/** +* @filename Common.js +* @author theBGuy +* @desc collection of functions shared between muliple scripts +* +*/ + +const Common = { + // each common functionality is loaded into this object when it's needed + // for the actual function files @see core/Common/ +}; diff --git a/d2bs/kolbot/libs/core/Common/Ancients.js b/d2bs/kolbot/libs/core/Common/Ancients.js new file mode 100644 index 000000000..050497286 --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Ancients.js @@ -0,0 +1,143 @@ +/** +* @filename Ancients.js +* @author theBGuy +* @desc Handle Ancients quest +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Ancients", { + value: { + altarSpot: {x: 10047, y: 12622}, + + canAttack: function () { + let ancient = Game.getMonster(); + + if (ancient) { + do { + if (!ancient.getParent() && !Attack.canAttack(ancient)) { + console.log("Can't attack ancients"); + return false; + } + } while (ancient.getNext()); + } + + return true; + }, + + touchAltar: function () { + let tick = getTickCount(); + + while (getTickCount() - tick < 5000) { + if (Game.getObject(sdk.objects.AncientsAltar)) { + break; + } + + delay(20 + me.ping); + } + + let altar = Game.getObject(sdk.objects.AncientsAltar); + + if (altar) { + while (altar.mode !== sdk.objects.mode.Active) { + Pather.moveToUnit(altar); + altar.interact(); + delay(200 + me.ping); + me.cancel(); + } + + // wait for ancients to spawn + while (!Game.getMonster(sdk.monsters.TalictheDefender)) { + delay(250 + me.ping); + } + + return true; + } + + return false; + }, + + checkStatues: function () { + let statues = getUnits(sdk.unittype.Object) + .filter(u => [sdk.objects.KorlictheProtectorStatue, sdk.objects.TalictheDefenderStatue, sdk.objects.MadawctheGuardianStatue].includes(u.classid) + && u.mode === sdk.objects.mode.Active); + return statues.length === 3; + }, + + checkCorners: function () { + let pos = [ + { x: 10036, y: 12592 }, { x: 10066, y: 12589 }, + { x: 10065, y: 12623 }, { x: 10058, y: 12648 }, + { x: 10040, y: 12660 }, { x: 10036, y: 12630 }, + { x: 10038, y: 12611 } + ]; + Pather.moveToUnit(this.altarSpot); + if (!this.checkStatues()) { + return pos.forEach((node) => { + // no mobs at that next, skip it + if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({range: 30}) === 0) { + return; + } + Pather.moveTo(node.x, node.y); + Attack.clear(30); + }); + } + + return true; + }, + + killAncients: function (checkQuest = false) { + let retry = 0; + Pather.moveToUnit(this.altarSpot); + + while (!this.checkStatues()) { + if (retry > 5) { + console.log("Failed to kill anicents."); + + break; + } + + Attack.clearClassids(sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian); + delay(1000); + + if (checkQuest) { + if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { + break; + } else { + console.log("Failed to kill anicents. Attempt: " + retry); + } + } + + this.checkCorners(); + retry++; + } + }, + + ancientsPrep: function () { + Town.goToTown(); + Town.fillTome(sdk.items.TomeofTownPortal); + [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion].forEach(p => Town.buyPots(10, p, true)); + Town.buyPotions(); + Pather.usePortal(sdk.areas.ArreatSummit, me.name); + }, + + startAncients: function (preTasks = false, checkQuest = false) { + let retry = 0; + Pather.moveToUnit(this.altarSpot); + this.touchAltar(); + + while (!this.canAttack()) { + if (retry > 10) throw new Error("I think I'm unable to complete ancients, I've rolled them 10 times"); + preTasks ? this.ancientsPrep() : Pather.makePortal(); + Pather.moveToUnit(this.altarSpot); + this.touchAltar(); + retry++; + } + + this.killAncients(checkQuest); + }, + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js new file mode 100644 index 000000000..308ee00e5 --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -0,0 +1,293 @@ +/** +* @filename Cows.js +* @author theBGuy +* @desc Handle Baal functions +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Baal", { + value: { + checkHydra: function () { + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); + if (hydra) { + do { + if (hydra.mode !== sdk.monsters.mode.Dead && hydra.getStat(sdk.stats.Alignment) !== 2) { + Pather.moveTo(15072, 5002); + while (hydra.mode !== sdk.monsters.mode.Dead) { + delay(500); + if (!copyUnit(hydra).x) { + break; + } + } + + break; + } + } while (hydra.getNext()); + } + + return true; + }, + + checkThrone: function (clear = true) { + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable && monster.y < 5080) { + switch (monster.classid) { + case sdk.monsters.WarpedFallen: + case sdk.monsters.WarpedShaman: + return 1; + case sdk.monsters.BaalSubjectMummy: + case sdk.monsters.BaalColdMage: + return 2; + case sdk.monsters.Council4: + return 3; + case sdk.monsters.VenomLord2: + return 4; + case sdk.monsters.ListerTheTormenter: + return 5; + default: + if (clear) { + Attack.getIntoPosition(monster, 10, sdk.collision.Ranged); + Attack.clear(15); + } + + return false; + } + } + } while (monster.getNext()); + } + + return false; + }, + + clearThrone: function () { + if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + + let monList = []; + + if (Config.AvoidDolls) { + let mon = Game.getMonster(sdk.monsters.SoulKiller); + + if (mon) { + do { + // exclude dolls from the list + if (!mon.isDoll && mon.x >= 15072 && mon.x <= 15118 && mon.y >= 5002 && mon.y <= 5079 && mon.attackable && !Attack.skipCheck(mon)) { + monList.push(copyUnit(mon)); + } + } while (mon.getNext()); + } + + if (monList.length > 0) { + return Attack.clearList(monList); + } + } + + let pos = [ + { x: 15097, y: 5054 }, { x: 15079, y: 5014 }, + { x: 15085, y: 5053 }, { x: 15085, y: 5040 }, + { x: 15098, y: 5040 }, { x: 15099, y: 5022 }, + { x: 15086, y: 5024 }, { x: 15079, y: 5014 } + ]; + return pos.forEach((node) => { + // no mobs at that next, skip it + if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({range: 30}) === 0) { + return; + } + Pather.moveTo(node.x, node.y); + Attack.clear(30); + }); + }, + + preattack: function () { + switch (me.classid) { + case sdk.player.class.Sorceress: + if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { + if (me.getState(sdk.states.SkillDelay)) { + delay(50); + } else { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5024); + } + } + + break; + case sdk.player.class.Paladin: + if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { + Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); + + return Skill.cast(Config.AttackSkill[3], 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[3], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5029); + + return true; + } + + break; + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let check = ClassAttack.checkTraps({x: 15094, y: 5028}); + + if (check) { + return ClassAttack.placeTraps({x: 15094, y: 5028}, 5); + } + } + + if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { + return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); + } + + break; + } + + return false; + }, + + clearWaves: function () { + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + + let tick = getTickCount(); + let totalTick = getTickCount(); + + MainLoop: + while (true) { + if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + + switch (this.checkThrone()) { + case 1: + Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); + + break; + case 2: + Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); + + break; + case 3: + Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); + this.checkHydra() && (tick = getTickCount()); + + break; + case 4: + Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); + + break; + case 5: + Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2) && (tick = getTickCount()); + + 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)) { + this.clearThrone(); + tick = getTickCount(); + } + + if (!this.preattack()) { + delay(100); + } + + 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); + } + + this.clearThrone(); + + return true; + }, + + killBaal: function () { + if (me.inArea(sdk.areas.ThroneofDestruction)) { + Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); + me.checkForMobs({range: 30}) && this.clearWaves(); // ensure waves are actually done + Pather.moveTo(15090, 5008); + delay(5000); + Precast.doPrecast(true); + Misc.poll(() => { + if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({range: 15})) { + Common.Baal.clearThrone(); + Pather.moveTo(15090, 5008); + } + return !Game.getMonster(sdk.monsters.ThroneBaal); + }, Time.minutes(3), 1000); + + let portal = Game.getObject(sdk.objects.WorldstonePortal); + + if (portal) { + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); + } + } + + if (me.inArea(sdk.areas.WorldstoneChamber)) { + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); + + return true; + } + + return false; + } + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/core/Common/Cain.js b/d2bs/kolbot/libs/core/Common/Cain.js new file mode 100644 index 000000000..856683d46 --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Cain.js @@ -0,0 +1,128 @@ +/** +* @filename Cain.js +* @author theBGuy +* @desc Complete cain quest +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Cain", { + value: { + activateStone: function (stone) { + for (let i = 0; i < 3; i++) { + // don't use tk if we are right next to it + let useTK = (stone.distance > 5 && Skill.useTK(stone) && i === 0); + if (useTK) { + stone.distance > 13 && Attack.getIntoPosition(stone, 13, sdk.collision.Ranged); + if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stone)) { + console.debug("Failed to tk: attempt: " + i); + continue; + } + } else { + [(stone.x + 1), (stone.y + 2)].distance > 5 && Pather.moveTo(stone.x + 1, stone.y + 2, 3); + Misc.click(0, 0, stone); + } + + if (Misc.poll(() => stone.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; + }, + + run: function () { + MainLoop: + while (true) { + switch (true) { + case !Game.getItem(sdk.quest.item.ScrollofInifuss) && !Game.getItem(sdk.quest.item.KeytotheCairnStones) && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4): + Pather.useWaypoint(sdk.areas.DarkWood, true); + Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + throw new Error("Failed to move to Tree of Inifuss"); + } + + let tree = Game.getObject(sdk.quest.chest.InifussTree); + !!tree && tree.distance > 5 && Pather.moveToUnit(tree); + Misc.openChest(tree); + let scroll = Misc.poll(() => Game.getItem(sdk.quest.item.ScrollofInifuss), 1000, 100); + + Pickit.pickItem(scroll); + Town.goToTown(); + Town.npcInteract("Akara"); + + break; + case Game.getItem(sdk.quest.item.ScrollofInifuss): + Town.goToTown(1); + Town.npcInteract("Akara"); + + break; + case Game.getItem(sdk.quest.item.KeytotheCairnStones) && !me.inArea(sdk.areas.StonyField): + Pather.journeyTo(sdk.areas.StonyField); + Precast.doPrecast(true); + + break; + case Game.getItem(sdk.quest.item.KeytotheCairnStones) && me.inArea(sdk.areas.StonyField): + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); + Attack.securePosition(me.x, me.y, 40, 3000, true); + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); + let stones = [ + Game.getObject(sdk.quest.chest.StoneAlpha), + Game.getObject(sdk.quest.chest.StoneBeta), + Game.getObject(sdk.quest.chest.StoneGamma), + Game.getObject(sdk.quest.chest.StoneDelta), + Game.getObject(sdk.quest.chest.StoneLambda) + ]; + + while (stones.some((stone) => !stone.mode)) { + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; + + if (this.activateStone(stone)) { + stones.splice(i, 1); + i--; + } + delay(10); + } + } + + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.getPortal(sdk.areas.Tristram)) { + Pather.usePortal(sdk.areas.Tristram); + + break; + } + } + + break; + case me.inArea(sdk.areas.Tristram) && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed): + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { + Town.goToTown(1); + Town.npcInteract("Akara") && console.log("Akara done"); + } + } + + break; + default: + break MainLoop; + } + } + + return true; + } + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/core/Common/Cows.js b/d2bs/kolbot/libs/core/Common/Cows.js new file mode 100644 index 000000000..8dbbfaead --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Cows.js @@ -0,0 +1,79 @@ +/** +* @filename Cows.js +* @author theBGuy +* @desc clear Moo Moo Farm +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Cows", { + value: { + buildCowRooms: function () { + let finalRooms = []; + let indexes = []; + + let kingPreset = Game.getPresetMonster(sdk.areas.MooMooFarm, sdk.monsters.preset.TheCowKing); + let badRooms = getRoom(kingPreset.roomx * 5 + kingPreset.x, kingPreset.roomy * 5 + kingPreset.y).getNearby(); + + for (let i = 0; i < badRooms.length; i += 1) { + let badRooms2 = badRooms[i].getNearby(); + + for (let j = 0; j < badRooms2.length; j += 1) { + if (indexes.indexOf(badRooms2[j].x + "" + badRooms2[j].y) === -1) { + indexes.push(badRooms2[j].x + "" + badRooms2[j].y); + } + } + } + + let room = getRoom(); + + do { + if (indexes.indexOf(room.x + "" + room.y) === -1) { + finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + } + } while (room.getNext()); + + return finalRooms; + }, + + clearCowLevel: function () { + function roomSort(a, b) { + return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); + } + + Config.MFLeader && Pather.makePortal() && say("cows"); + + let myRoom; + let rooms = this.buildCowRooms(); + + 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); + let room = rooms.shift(); + let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); + + if (result) { + Pather.moveTo(result[0], result[1], 3); + if (!Attack.clear(30)) return false; + } + } + + return true; + }, + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js new file mode 100644 index 000000000..a3960a49b --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -0,0 +1,550 @@ +/** +* @filename Diablo.js +* @author theBGuy +* @desc Handle Diablo related functions +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Diablo", { + value: { + diabloSpawned: false, + diaWaitTime: Time.seconds(30), + clearRadius: 30, + done: false, + waitForGlow: false, + sealOrder: [], + vizLayout: -1, + seisLayout: -1, + infLayout: -1, + entranceCoords: {x: 7790, y: 5544}, + starCoords: {x: 7791, y: 5293}, + // path coordinates + entranceToStar: [ + [7794, 5517], [7791, 5491], [7768, 5459], + [7775, 5424], [7817, 5458], [7777, 5408], + [7769, 5379], [7777, 5357], [7809, 5359], + [7805, 5330], [7780, 5317], [7791, 5293]], + starToVizA: [ + [7759, 5295], [7734, 5295], [7716, 5295], [7718, 5276], + [7697, 5292], [7678, 5293], [7665, 5276], [7662, 5314] + ], + starToVizB: [ + [7759, 5295], [7734, 5295], [7716, 5295], + [7701, 5315], [7666, 5313], [7653, 5284] + ], + starToSeisA: [ + [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], + [7775, 5205], [7804, 5193], [7814, 5169], [7788, 5153] + ], + starToSeisB: [ + [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], + [7811, 5218], [7807, 5194], [7779, 5193], [7774, 5160], [7803, 5154] + ], + starToInfA: [ + [7809, 5268], [7834, 5306], [7852, 5280], + [7852, 5310], [7869, 5294], [7895, 5295], [7919, 5290] + ], + starToInfB: [ + [7809, 5268], [7834, 5306], [7852, 5280], [7852, 5310], + [7869, 5294], [7895, 5274], [7927, 5275], [7932, 5297], [7923, 5313] + ], + // check for strays array + cleared: [], + + diabloLightsEvent: function (bytes = []) { + if (me.inArea(sdk.areas.ChaosSanctuary) && bytes && bytes.length === 2 && bytes[0] === 0x89 && bytes[1] === 0x0C) { + Common.Diablo.diabloSpawned = true; + } + }, + + sort: function (a, b) { + if (Config.BossPriority) { + if ((a.isSuperUnique) && (b.isSuperUnique)) return getDistance(me, a) - getDistance(me, b); + if (a.isSuperUnique) return -1; + if (b.isSuperUnique) return 1; + } + + // Entrance to Star / De Seis + if (me.y > 5325 || me.y < 5260) return (a.y > b.y ? -1 : 1); + // Vizier + if (me.x < 7765) return (a.x > b.x ? -1 : 1); + // Infector + if (me.x > 7825) return (!checkCollision(me, a, sdk.collision.BlockWall) && a.x < b.x ? -1 : 1); + + return getDistance(me, a) - getDistance(me, b); + }, + + getLayout: function (seal, value) { + let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); + if (!seal) throw new Error("Seal preset not found. Can't continue."); + + if (sealPreset.roomy * 5 + sealPreset.y === value + || sealPreset.roomx * 5 + sealPreset.x === value) { + return 1; + } + + return 2; + }, + + initLayout: function () { + // 1 = "Y", 2 = "L" + Common.Diablo.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); + // 1 = "2", 2 = "5" + Common.Diablo.seisLayout = this.getLayout(sdk.objects.DiabloSealSeis, 7773); + // 1 = "I", 2 = "J" + Common.Diablo.infLayout = this.getLayout(sdk.objects.DiabloSealInfector, 7893); + }, + + /** + * Follow static path + * @param {number[][]} path + * @returns {void} + */ + followPath: function (path) { + if (Config.Diablo.Fast) { + let last = path.last(); + let lastNode = { x: last[0], y: last[1] }; + Pather.moveToUnit(lastNode); + return; + } + + for (let i = 0; i < path.length; i++) { + this.cleared.length > 0 && this.clearStrays(); + + // no monsters at the next node, skip it + let next = i + 1 !== path.length ? path[i + 1] : null; + if (next && next.distance < 40 && next.mobCount({ range: 35 }) === 0) { + continue; + } + + Pather.moveTo(path[i][0], path[i][1], 3, getDistance(me, path[i][0], path[i][1]) > 50); + Attack.clear(this.clearRadius, 0, false, Common.Diablo.sort); + + // Push cleared positions so they can be checked for strays + this.cleared.push(path[i]); + + // After 5 nodes go back 2 nodes to check for monsters + if (i === 5 && path.length > 8) { + path = path.slice(3); + i = 0; + } + } + }, + + clearStrays: function () { + let oldPos = { x: me.x, y: me.y }; + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable) { + for (let i = 0; i < this.cleared.length; i += 1) { + if (getDistance(monster, this.cleared[i][0], this.cleared[i][1]) < 30 && Attack.validSpot(monster.x, monster.y)) { + Pather.moveToUnit(monster); + Attack.clear(15, 0, false, Common.Diablo.sort); + + break; + } + } + } + } while (monster.getNext()); + } + + getDistance(me, oldPos.x, oldPos.y) > 5 && Pather.moveTo(oldPos.x, oldPos.y); + + return true; + }, + + runSeals: function (sealOrder, openSeals = true) { + print("seal order: " + sealOrder); + Common.Diablo.sealOrder = sealOrder; + let seals = { + 1: () => this.vizierSeal(openSeals), + 2: () => this.seisSeal(openSeals), + 3: () => this.infectorSeal(openSeals), + "vizier": () => this.vizierSeal(openSeals), + "seis": () => this.seisSeal(openSeals), + "infector": () => this.infectorSeal(openSeals), + }; + sealOrder.forEach(seal => {seals[seal]();}); + }, + + tkSeal: function (seal) { + if (!Skill.useTK(seal)) return false; + + for (let i = 0; i < 5; i++) { + seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); + + if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, seal) && Misc.poll(() => seal.mode, 1000, 100)) { + break; + } + } + + return !!seal.mode; + }, + + openSeal: function (classid) { + let seal; + let warn = Config.PublicMode && [sdk.objects.DiabloSealVizier, sdk.objects.DiabloSealSeis, sdk.objects.DiabloSealInfector].includes(classid) && Loader.scriptName() === "Diablo"; + let usetk = (Skill.haveTK && (classid !== sdk.objects.DiabloSealSeis || this.seisLayout !== 1)); + let seisSeal = classid === sdk.objects.DiabloSealSeis; + + for (let i = 0; i < 5; i++) { + if (!seal) { + usetk + ? Pather.moveNearPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, 15) + : Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, seisSeal ? 5 : 2, seisSeal ? 5 : 0); + seal = Misc.poll(() => Game.getObject(classid), 1000, 100); + } + + if (!seal) { + console.debug("Couldn't find seal: " + classid); + return false; + } + + if (seal.mode) { + warn && say(Config.Diablo.SealWarning); + return true; + } + + // Clear around Infector seal, Any leftover abyss knights casting decrep is bad news with Infector + if (([sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealInfector2].includes(classid) || i > 1) && me.getMobCount() > 1) { + Attack.clear(15); + // Move back to seal + usetk ? Pather.moveNearUnit(seal, 15) : Pather.moveToUnit(seal, seisSeal ? 5 : 2, seisSeal ? 5 : 0); + } + + if (usetk && this.tkSeal(seal)) { + return seal.mode; + } else { + usetk && (usetk = false); + + if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { + if (Config.UseTraps) { + let check = ClassAttack.checkTraps({x: 7899, y: 5293}); + check && ClassAttack.placeTraps({x: 7899, y: 5293}, check); + } + } + + seisSeal ? Misc.poll(function () { + // stupid diablo shit, walk around the de-seis seal clicking it until we find "the spot"...sigh + if (!seal.mode) { + Pather.walkTo(seal.x + (rand(-1, 1)), seal.y + (rand(-1, 1))); + clickUnitAndWait(0, 0, seal) || seal.interact(); + } + return !!seal.mode; + }, 3000, 60) : seal.interact(); + + // de seis optimization + if (seisSeal && Attack.validSpot(seal.x + 15, seal.y)) { + Pather.walkTo(seal.x + 15, seal.y); + } else { + Pather.walkTo(seal.x - 5, seal.y - 5); + } + } + + delay(seisSeal ? 1000 + me.ping : 500 + me.ping); + + if (seal.mode) { + break; + } + } + + return (!!seal && seal.mode); + }, + + vizierSeal: function (openSeal = true) { + print("Viz layout " + Common.Diablo.vizLayout); + let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + distCheck.distance > 30 && this.followPath(Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + + if (openSeal && (!Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2) || !Common.Diablo.openSeal(sdk.objects.DiabloSealVizier))) { + throw new Error("Failed to open Vizier seals."); + } + + delay(1 + me.ping); + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7691, 5292) : Pather.moveTo(7695, 5316); + + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { + throw new Error("Failed to kill Vizier"); + } + + Config.Diablo.SealLeader && say("out"); + + return true; + }, + + seisSeal: function (openSeal = true) { + print("Seis layout " + Common.Diablo.seisLayout); + let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + Common.Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + distCheck.distance > 30 && this.followPath(Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealSeis)) throw new Error("Failed to open de Seis seal."); + Common.Diablo.seisLayout === 1 ? Pather.moveTo(7798, 5194) : Pather.moveTo(7796, 5155); + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) throw new Error("Failed to kill de Seis"); + + Config.Diablo.SealLeader && say("out"); + + return true; + }, + + infectorSeal: function (openSeal = true) { + Precast.doPrecast(true); + print("Inf layout " + Common.Diablo.infLayout); + let path = (Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + Common.Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + distCheck.distance > 70 && this.followPath(Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + + if (Config.Diablo.Fast) { + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); + + if (Common.Diablo.infLayout === 1) { + (me.sorceress || me.assassin) && Pather.moveTo(7876, 5296); + delay(1 + me.ping); + } else { + delay(1 + me.ping); + Pather.moveTo(7928, 5295); + } + + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); + } else { + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); + + if (Common.Diablo.infLayout === 1) { + (me.sorceress || me.assassin) && Pather.moveTo(7876, 5296); + delay(1 + me.ping); + } else { + delay(1 + me.ping); + Pather.moveTo(7928, 5295); + } + + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); + // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss + !openSeal && [3, "infector"].includes(Common.Diablo.sealOrder.last()) && Misc.poll(() => { + if (Common.Diablo.diabloSpawned) return true; + + let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); + if (lastSeal && lastSeal.mode) { + return true; + } + return false; + }, Time.minutes(3), 1000); + } + + Config.Diablo.SealLeader && say("out"); + + return true; + }, + + hammerdinPreAttack: function (name, amount = 5) { + if (me.paladin && Config.AttackSkill[1] === sdk.skills.BlessedHammer) { + let target = Game.getMonster(name); + + if (!target || !target.attackable) return true; + + let positions = [[6, 11], [0, 8], [8, -1], [-9, 2], [0, -11], [8, -8]]; + + for (let i = 0; i < positions.length; i += 1) { + // check if we can move there + if (Attack.validSpot(target.x + positions[i][0], target.y + positions[i][1])) { + Pather.moveTo(target.x + positions[i][0], target.y + positions[i][1]); + Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + + for (let n = 0; n < amount; n += 1) { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); + } + + return true; + } + } + } + + return false; + }, + + preattack: function (id) { + let coords = (() => { + switch (id) { + case getLocaleString(sdk.locale.monsters.GrandVizierofChaos): + return Common.Diablo.vizLayout === 1 ? [7676, 5295] : [7684, 5318]; + case getLocaleString(sdk.locale.monsters.LordDeSeis): + return Common.Diablo.seisLayout === 1 ? [7778, 5216] : [7775, 5208]; + case getLocaleString(sdk.locale.monsters.InfectorofSouls): + return Common.Diablo.infLayout === 1 ? [7913, 5292] : [7915, 5280]; + default: + return []; + } + })(); + if (!coords.length) return false; + + switch (me.classid) { + case sdk.player.class.Sorceress: + if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { + me.skillDelay && delay(500); + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, coords[0], coords[1]); + + return true; + } + + break; + case sdk.player.class.Paladin: + return this.hammerdinPreAttack(id, 8); + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let trapCheck = ClassAttack.checkTraps({x: coords[0], y: coords[1]}); + + if (trapCheck) { + ClassAttack.placeTraps({x: coords[0], y: coords[1]}, 5); + + return true; + } + } + + break; + } + + return false; + }, + + getBoss: function (name) { + let glow = Game.getObject(sdk.objects.SealGlow); + + if (this.waitForGlow) { + while (true) { + if (!this.preattack(name)) { + delay(500); + } + + glow = Game.getObject(sdk.objects.SealGlow); + + if (glow) { + break; + } + } + } + + for (let i = 0; i < 16; i += 1) { + let boss = Game.getMonster(name); + + if (boss) { + Common.Diablo.hammerdinPreAttack(name, 8); + return (Config.Diablo.Fast ? Attack.kill(name) : Attack.clear(40, 0, name, this.sort)); + } + + delay(250); + } + + return !!glow; + }, + + moveToStar: function () { + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Sorceress: + case sdk.player.class.Necromancer: + case sdk.player.class.Assassin: + return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), {returnSpotOnError: true}); + case sdk.player.class.Paladin: + case sdk.player.class.Druid: + case sdk.player.class.Barbarian: + return Pather.moveTo(7788, 5292); + } + + return false; + }, + + diabloPrep: function () { + if (Config.Diablo.SealLeader) { + Pather.moveTo(7763, 5267); + Pather.makePortal() && say("in"); + Pather.moveTo(7788, 5292); + } + + this.moveToStar(); + + let tick = getTickCount(); + + while (getTickCount() - tick < this.diaWaitTime) { + if (getTickCount() - tick >= Time.seconds(8)) { + switch (me.classid) { + 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); + + break; + } + } else { + delay(500); + } + + if (Game.getMonster(sdk.monsters.Diablo)) { + return true; + } + } + + throw new Error("Diablo not found"); + }, + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/core/Common/Leecher.js b/d2bs/kolbot/libs/core/Common/Leecher.js new file mode 100644 index 000000000..ee4675a96 --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Leecher.js @@ -0,0 +1,51 @@ +/** +* @filename Leecher.js +* @author theBGuy +* @desc Leecher tools +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Leecher", { + value: { + leadTick: 0, + leader: null, + killLeaderTracker: false, + currentScript: "", + nextScriptAreas: [sdk.areas.TowerCellarLvl5, sdk.areas.PitLvl1, sdk.areas.PitLvl2, sdk.areas.BurialGrounds, + sdk.areas.CatacombsLvl4, sdk.areas.MooMooFarm, sdk.areas.DuranceofHateLvl3, + sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber + ], + + leaderTracker: function () { + if (Common.Leecher.killLeaderTracker) return false; + // check every 3 seconds + if (getTickCount() - Common.Leecher.leadTick < 3000) return true; + Common.Leecher.leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + + let party = getParty(Common.Leecher.leader); + + if (party) { + // Player has moved on to another script + if (Common.Leecher.nextScriptAreas.includes(party.area)) { + if (Loader.scriptName() === Common.Leecher.currentScript) { + Common.Leecher.killLeaderTracker = true; + throw new Error("Party leader is running a new script"); + } else { + // kill process + return false; + } + } + } + + return true; + } + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/core/Common/Smith.js b/d2bs/kolbot/libs/core/Common/Smith.js new file mode 100644 index 000000000..61d73dd31 --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Smith.js @@ -0,0 +1,29 @@ +/** +* @filename Smith.js +* @author theBGuy +* @desc Complete smith quest +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Smith", { + value: function () { + if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { + throw new Error("Failed to move to the Smith"); + } + + Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); + let malusChest = Game.getObject(sdk.quest.chest.MalusHolder); + !!malusChest && malusChest.distance > 5 && Pather.moveToUnit(malusChest); + Misc.openChest(malusChest); + let malus = Misc.poll(() => Game.getItem(sdk.quest.item.HoradricMalus), 1000, 100); + Pickit.pickItem(malus); + Town.goToTown(); + Town.npcInteract("Charsi"); + + return !!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete); + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js new file mode 100644 index 000000000..e1bdb0070 --- /dev/null +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -0,0 +1,314 @@ +/** +* @filename Tools.js +* @author theBGuy +* @desc Tools for Toolsthread and its variations (MapToolsThread, ect) +* +*/ + +(function (Common) { + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Toolsthread", { + value: { + pots: { + Health: 0, + Mana: 1, + Rejuv: 2, + MercHealth: 3, + MercRejuv: 4 + }, + pingTimer: [], + pauseScripts: [], + stopScripts: [], + timerLastDrink: [], + cloneWalked: false, + + checkPing: function (print = true) { + // Quit after at least 5 seconds in game + if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; + + for (let i = 0; i < Config.PingQuit.length; i += 1) { + if (Config.PingQuit[i].Ping > 0) { + if (me.ping >= Config.PingQuit[i].Ping) { + me.overhead("High Ping"); + + if (this.pingTimer[i] === undefined || this.pingTimer[i] === 0) { + this.pingTimer[i] = getTickCount(); + } + + if (getTickCount() - this.pingTimer[i] >= Config.PingQuit[i].Duration * 1000) { + print && D2Bot.printToConsole("High ping (" + me.ping + "/" + Config.PingQuit[i].Ping + ") - leaving game.", sdk.colors.D2Bot.Red); + scriptBroadcast("pingquit"); + scriptBroadcast("quit"); + + return true; + } + } else { + this.pingTimer[i] = 0; + } + } + } + + return false; + }, + + initQuitList: function () { + let temp = []; + + for (let i = 0; i < Config.QuitList.length; i += 1) { + if (FileTools.exists("data/" + Config.QuitList[i] + ".json")) { + let string = FileAction.read("data/" + Config.QuitList[i] + ".json"); + + if (string) { + let obj = JSON.parse(string); + + if (obj && obj.hasOwnProperty("name")) { + temp.push(obj.name); + } + } + } + } + + Config.QuitList = temp.slice(0); + }, + + togglePause: function () { + for (let i = 0; i < this.pauseScripts.length; i++) { + let script = getScript(this.pauseScripts[i]); + + if (script) { + if (script.running) { + this.pauseScripts[i] === "default.dbj" && console.log("ÿc1Pausing."); + + // don't pause townchicken during clone walk + if (this.pauseScripts[i] !== "threads/townchicken.js" || !this.cloneWalked) { + script.pause(); + } + } else { + this.pauseScripts[i] === "default.dbj" && console.log("ÿc2Resuming."); + script.resume(); + } + } + } + + return true; + }, + + stopDefault: function () { + for (let i = 0; i < this.stopScripts.length; i++) { + try { + let script = getScript(this.stopScripts[i]); + !!script && script.running && script.stop(); + } catch (e) { + console.error(e); + } + } + + return true; + }, + + exit: function (chickenExit = false) { + chickenExit && D2Bot.updateChickens(); + Config.LogExperience && Experience.log(); + console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); + this.stopDefault(); + quit(); + }, + + getPotion: function (pottype, type) { + if (!pottype) return false; + + let items = me.getItemsEx().filter((item) => item.itemType === pottype); + if (items.length === 0) return false; + + // Get highest id = highest potion first + items.sort(function (a, b) { + return b.classid - a.classid; + }); + + for (let i = 0; i < items.length; i += 1) { + if (type < this.pots.MercHealth && items[i].isInInventory && items[i].itemType === pottype) { + console.log("ÿc2Drinking potion from inventory."); + return items[i]; + } + + if (items[i].isInBelt && items[i].itemType === pottype) { + console.log("ÿc2" + (type > 2 ? "Giving Merc" : "Drinking") + " potion from belt."); + return items[i]; + } + } + + return false; + }, + + drinkPotion: function (type) { + if (type === undefined) return false; + let tNow = getTickCount(); + + switch (type) { + case this.pots.Health: + case this.pots.Mana: + if ((this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 1000)) || me.getState(type === this.pots.Health ? sdk.states.HealthPot : sdk.states.ManaPot)) { + return false; + } + + break; + case this.pots.Rejuv: + // small delay for juvs just to prevent using more at once + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 300)) { + return false; + } + + break; + case this.pots.MercRejuv: + // larger delay for juvs just to prevent using more at once, considering merc update rate + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 2000)) { + return false; + } + + break; + default: + if (this.timerLastDrink[type] && (tNow - this.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 this.pots.Health: + case this.pots.MercHealth: + return sdk.items.type.HealingPotion; + case this.pots.Mana: + return sdk.items.type.ManaPotion; + default: + return sdk.items.type.RejuvPotion; + } + })(); + + let potion = this.getPotion(pottype, type); + + if (!!potion) { + if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; + + try { + type < this.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); + } catch (e) { + console.error(e); + } + + this.timerLastDrink[type] = getTickCount(); + + return true; + } + + return false; + }, + + checkVipers: function () { + let monster = Game.getMonster(sdk.monsters.TombViper2); + + if (monster) { + do { + if (monster.getState(sdk.states.Revive)) { + let owner = monster.getParent(); + + if (owner && owner.name !== me.name) { + D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); + + return true; + } + } + } while (monster.getNext()); + } + + return false; + }, + + getIronGolem: function () { + let golem = Game.getMonster(sdk.summons.IronGolem); + + if (golem) { + do { + let owner = golem.getParent(); + + if (owner && owner.name === me.name) { + return copyUnit(golem); + } + } while (golem.getNext()); + } + + return false; + }, + + getNearestPreset: function () { + let id; + let unit = getPresetUnits(me.area); + let dist = 99; + + for (let i = 0; i < unit.length; i += 1) { + if (getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y) < dist) { + dist = getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y); + id = unit[i].type + " " + unit[i].id; + } + } + + return id || ""; + }, + + getStatsString: function (unit) { + let realFCR = unit.getStat(sdk.stats.FCR); + let realIAS = unit.getStat(sdk.stats.IAS); + let realFBR = unit.getStat(sdk.stats.FBR); + let realFHR = unit.getStat(sdk.stats.FHR); + // me.getStat(sdk.stats.FasterCastRate) will return real FCR from gear + Config.FCR from char cfg + + if (unit === me) { + realFCR -= Config.FCR; + realIAS -= Config.IAS; + realFBR -= Config.FBR; + realFHR -= Config.FHR; + } + + let maxHellFireRes = 75 + unit.getStat(sdk.stats.MaxFireResist); + let hellFireRes = unit.getRes(sdk.stats.FireResist, sdk.difficulty.Hell); + hellFireRes > maxHellFireRes && (hellFireRes = maxHellFireRes); + + let maxHellColdRes = 75 + unit.getStat(sdk.stats.MaxColdResist); + let hellColdRes = unit.getRes(sdk.stats.ColdResist, sdk.difficulty.Hell); + hellColdRes > maxHellColdRes && (hellColdRes = maxHellColdRes); + + let maxHellLightRes = 75 + unit.getStat(sdk.stats.MaxLightResist); + let hellLightRes = unit.getRes(sdk.stats.LightResist, sdk.difficulty.Hell); + hellLightRes > maxHellLightRes && (hellLightRes = maxHellLightRes); + + let maxHellPoisonRes = 75 + unit.getStat(sdk.stats.MaxPoisonResist); + let hellPoisonRes = unit.getRes(sdk.stats.PoisonResist, sdk.difficulty.Hell); + hellPoisonRes > maxHellPoisonRes && (hellPoisonRes = maxHellPoisonRes); + + let str = + "ÿc4Character Level: ÿc0" + unit.charlvl + (unit === me ? " ÿc4Difficulty: ÿc0" + sdk.difficulty.nameOf(me.diff) + " ÿc4HighestActAvailable: ÿc0" + me.highestAct : "") + "\n" + + "ÿc1FR: ÿc0" + unit.getStat(sdk.stats.FireResist) + "ÿc1 Applied FR: ÿc0" + unit.fireRes + + "/ÿc3 CR: ÿc0" + unit.getStat(sdk.stats.ColdResist) + "ÿc3 Applied CR: ÿc0" + unit.coldRes + + "/ÿc9 LR: ÿc0" + unit.getStat(sdk.stats.LightResist) + "ÿc9 Applied LR: ÿc0" + unit.lightRes + + "/ÿc2 PR: ÿc0" + unit.getStat(sdk.stats.PoisonResist) + "ÿc2 Applied PR: ÿc0" + unit.poisonRes + "\n" + + (!me.hell ? "Hell res: ÿc1" + hellFireRes + "ÿc0/ÿc3" + hellColdRes + "ÿc0/ÿc9" + hellLightRes + "ÿc0/ÿc2" + hellPoisonRes + "ÿc0\n" : "") + + "ÿc4MF: ÿc0" + unit.getStat(sdk.stats.MagicBonus) + "ÿc4 GF: ÿc0" + unit.getStat(sdk.stats.GoldBonus) + + " ÿc4FCR: ÿc0" + realFCR + " ÿc4IAS: ÿc0" + realIAS + " ÿc4FBR: ÿc0" + realFBR + + " ÿc4FHR: ÿc0" + realFHR + " ÿc4FRW: ÿc0" + unit.getStat(sdk.stats.FRW) + "\n" + + "ÿc4CB: ÿc0" + unit.getStat(sdk.stats.CrushingBlow) + " ÿc4DS: ÿc0" + unit.getStat(sdk.stats.DeadlyStrike) + + " ÿc4OW: ÿc0" + unit.getStat(sdk.stats.OpenWounds) + + " ÿc1LL: ÿc0" + unit.getStat(sdk.stats.LifeLeech) + " ÿc3ML: ÿc0" + unit.getStat(sdk.stats.ManaLeech) + + " ÿc8DR: ÿc0" + unit.getStat(sdk.stats.DamageResist) + "% + " + unit.getStat(sdk.stats.NormalDamageReduction) + + " ÿc8MDR: ÿc0" + unit.getStat(sdk.stats.MagicResist) + "% + " + unit.getStat(sdk.stats.MagicDamageReduction) + "\n" + + (unit.getStat(sdk.stats.CannotbeFrozen) > 0 ? "ÿc3Cannot be Frozenÿc1\n" : "\n"); + + return str; + }, + }, + configurable: true, + }); +})(Common); diff --git a/d2bs/kolbot/libs/common/Config.js b/d2bs/kolbot/libs/core/Config.js similarity index 87% rename from d2bs/kolbot/libs/common/Config.js rename to d2bs/kolbot/libs/core/Config.js index 9b4bf1633..72e2e15cc 100644 --- a/d2bs/kolbot/libs/common/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -1,6 +1,6 @@ /** * @filename Config.js -* @author kolton +* @author kolton, theBGuy * @desc config loading and default config values storage * */ @@ -8,46 +8,39 @@ const Scripts = {}; let Config = { - init: function (notify) { + init: function (notify = true) { + const className = sdk.player.class.nameOf(me.classid); + 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", + }))(className, me.profile, me.charname, me.realm); let configFilename = ""; - let classes = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; for (let i = 0; i < 5; i++) { switch (i) { case 0: // Custom config - if (!isIncluded("config/_customconfig.js")) { - include("config/_customconfig.js"); - } + includeIfNotIncluded("config/_customconfig.js"); for (let n in CustomConfig) { - if (CustomConfig.hasOwnProperty(n)) { - if (CustomConfig[n].indexOf(me.profile) > -1) { - if (notify) { - print("ÿc2Loading custom config: ÿc9" + n + ".js"); - } - - configFilename = n + ".js"; + if (CustomConfig.hasOwnProperty(n) && CustomConfig[n].includes(me.profile)) { + notify && console.log("ÿc2Loading custom config: ÿc9" + n + ".js"); + configFilename = n + ".js"; - break; - } + break; } } break; - case 1:// Class.Profile.js - configFilename = classes[me.classid] + "." + me.profile + ".js"; - - break; - case 2: // Realm.Class.Charname.js - configFilename = me.realm + "." + classes[me.classid] + "." + me.charname + ".js"; - - break; - case 3: // Class.Charname.js - configFilename = classes[me.classid] + "." + me.charname + ".js"; - - break; - case 4: // Profile.js - configFilename = me.profile + ".js"; + default: + configFilename = formats[i]; break; } @@ -67,18 +60,18 @@ let Config = { } } else { if (notify) { - print("ÿc1" + classes[me.classid] + "." + me.charname + ".js not found!"); // Use the primary format + print("ÿc1" + className + "." + me.charname + ".js not found!"); // Use the primary format print("ÿc1Loading default config."); } // Try to find default config - if (!FileTools.exists("libs/config/" + classes[me.classid] + ".js")) { + if (!FileTools.exists("libs/config/" + className + ".js")) { D2Bot.printToConsole("Not going well? Read the guides: https://github.com/blizzhackers/documentation"); throw new Error("ÿc1Default config not found. \nÿc9 Try reading the kolbot guides."); } try { - if (!include("config/" + classes[me.classid] + ".js")) { + if (!include("config/" + className + ".js")) { throw new Error(); } } catch (e) { @@ -104,12 +97,12 @@ let Config = { } try { - if (Config.AutoBuild.Enabled === true && !isIncluded("common/AutoBuild.js") && include("common/AutoBuild.js")) { + if (Config.AutoBuild.Enabled === true && !isIncluded("core/Auto/AutoBuild.js") && include("core/Auto/AutoBuild.js")) { AutoBuild.initialize(); } } catch (e3) { - print("ÿc8Error in libs/common/AutoBuild.js (AutoBuild system is not active!)"); - print(e3.toSource()); + print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); + console.error(e3); } }, @@ -192,8 +185,14 @@ let Config = { Silence: false, PublicMode: false, PartyAfterScript: false, + + /** @type {string[]} */ Greetings: [], + + /** @type {string[]} */ DeathMessages: [], + + /** @type {string[]} */ Congratulations: [], ShitList: false, UnpartyShitlisted: false, @@ -294,6 +293,7 @@ let Config = { ChampionBias: 60, UseCta: true, + ForcePrecast: false, // Attack specific Dodge: false, @@ -343,6 +343,7 @@ let Config = { // Barbarian specific FindItem: false, + FastFindItem: false, FindItemSwitch: false, UseWarcries: true, @@ -366,7 +367,7 @@ let Config = { SummonShadow: false, // Custom Attack - CustomClassAttack: "", // If set it loads common/Attack/[CustomClassAttack].js + CustomClassAttack: "", // If set it loads core/Attack/[CustomClassAttack].js MapMode: { UseOwnItemFilter: false, @@ -563,7 +564,7 @@ let Config = { }, OrgTorch: { WaitForKeys: false, - WaitTimeout: false, + WaitTimeout: 0, UseSalvation: false, GetFade: false, MakeTorch: true, @@ -627,6 +628,10 @@ let Config = { Questing: { StopProfile: false }, + GemHunter: { + AreaList: [], + GemList: [] + }, AutoSkill: { Enabled: false, Build: [], @@ -644,9 +649,5 @@ let Config = { Template: "", Verbose: false, DebugMode: false - }, - GemHunter: { - AreaList: [], - GemList: [] } }; diff --git a/d2bs/kolbot/libs/common/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js similarity index 99% rename from d2bs/kolbot/libs/common/Cubing.js rename to d2bs/kolbot/libs/core/Cubing.js index fc9389bb6..5e9b6a954 100644 --- a/d2bs/kolbot/libs/common/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -887,13 +887,13 @@ const Cubing = { switch (result.result) { case Pickit.Result.UNWANTED: - Misc.itemLogger("Dropped", cubeItems[j], "doCubing"); + Item.logger("Dropped", cubeItems[j], "doCubing"); cubeItems[j].drop(); break; case Pickit.Result.WANTED: - Misc.itemLogger("Cubing Kept", cubeItems[j]); - Misc.logItem("Cubing Kept", cubeItems[j], result.line); + Item.logger("Cubing Kept", cubeItems[j]); + Item.logItem("Cubing Kept", cubeItems[j], result.line); break; case Pickit.Result.CRAFTING: @@ -931,7 +931,7 @@ const Cubing = { if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return true; if (item.drop()) { - Misc.itemLogger("Dropped", item, "cursorCheck"); + Item.logger("Dropped", item, "cursorCheck"); return true; } } diff --git a/d2bs/kolbot/libs/core/Data/AreaData.js b/d2bs/kolbot/libs/core/Data/AreaData.js new file mode 100644 index 000000000..15f5db50c --- /dev/null +++ b/d2bs/kolbot/libs/core/Data/AreaData.js @@ -0,0 +1,206 @@ +/** +* @filename AreaData.js +* @author Nishimura-Katsuo, theBGuy +* @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 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, require); diff --git a/d2bs/kolbot/libs/core/Data/GameData.js b/d2bs/kolbot/libs/core/Data/GameData.js new file mode 100644 index 000000000..10f3eae75 --- /dev/null +++ b/d2bs/kolbot/libs/core/Data/GameData.js @@ -0,0 +1,168 @@ +/** +* @filename GameData.js +* @author Nishimura-Katsuo +* @desc game data library +* +*/ + + +(function (module, require) { + const MonsterData = require("./MonsterData"); + const AreaData = require("./AreaData"); + + const GameData = { + townAreas: [0, 1, 40, 75, 103, 109], + monsterLevel: function (monsterID, areaID) { + if (me.diff) { // levels on nm/hell are determined by area, not by monster data + return AreaData[areaID].Level; + } + + return MonsterData[monsterID].Level; + }, + monsterExp: function (monsterID, areaID) { + return Experience.monsterExp[this.monsterLevel(monsterID, areaID)][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; + }, + areaLevel: function (areaID) { + let levels = 0, total = 0; + + if (me.diff) { // levels on nm/hell are determined by area, not by monster data + return AreaData[areaID].Level; + } + + AreaData[areaID].Monsters.forEach(mon => { + levels += MonsterData[mon].Level * MonsterData[mon].Rarity; + total += MonsterData[mon].Rarity; + }); + + return Math.round(levels / total); + }, + areaImmunities: function (areaID) { + let resists = {Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0}; + + function checkmon (monID) { + for (let k in resists) { + resists[k] = Math.max(resists[k], MonsterData[monID][k]); + } + } + + AreaData[areaID].Monsters.forEach(mon => { + checkmon(mon); + MonsterData[mon].Minions.forEach(checkmon); + }); + + 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(me); + + if (!party) { + return 1; + } + + count = 1; + + while (party.getNext()) { + count++; + } + } + + return (count + 1) / 2; + }, + partyModifier: function (playerID) { + let party = getParty(me), partyid = -1, level = 0, total = 0; + + if (!party) { + return 1; + } + + 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(me), partyid = -1, level = 0, total = 0, gamesize = 0; + + if (!party) { + return 0; + } + + 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); + }, + areaPartyExp: function (areaID, exclude = null, onlytown = true, ignore = null) { // amount of total party exp gained per kill on average + let party = getParty(me), partyid = -1, partylevels = 0, gamesize = 0, exp = 0, playerexp = 0, poolsize = 0; + + if (!party) { + return 0; + } + + // very rough approximation of unique population ratio, could be approved but this works well enough + let uniqueratio = parseFloat(Config.ChampionBias) * (AreaData[areaID].ChampionPacks.Min + AreaData[areaID].ChampionPacks.Max + AreaData[areaID].Super * 2) / (AreaData[areaID].Size.x * AreaData[areaID].Size.y); + + partyid = party.partyid; + + do { + gamesize++; + + if (party.partyid === partyid && party.name !== exclude && party.gid !== exclude && (!onlytown || this.townAreas.indexOf(party.area) > -1) && (areaID < 128 || party.level >= (1 + me.diff) * 20)) { + partylevels += party.level; + + if (party.name !== ignore && party.gid !== ignore) { + poolsize = 0; + playerexp = 0; + + AreaData[areaID].Monsters.forEach(mon => { + if (MonsterData[mon].Rarity > 0) { + playerexp += ((1 - uniqueratio) + (3 * uniqueratio)) * this.monsterExp(mon, areaID) * this.levelModifier(party.level, this.monsterLevel(mon, areaID)) * MonsterData[mon].Rarity; + poolsize += MonsterData[mon].Rarity; + } + }); + + if (poolsize) { + exp += party.level * playerexp / poolsize; + } + } + } + } while (party.getNext()); + + return (partylevels ? exp * this.multiplayerModifier(gamesize) / partylevels : 0); + } + }; + + module.exports = GameData; +})(module, require); diff --git a/d2bs/kolbot/libs/core/Data/LocaleStringID.js b/d2bs/kolbot/libs/core/Data/LocaleStringID.js new file mode 100644 index 000000000..133617d53 --- /dev/null +++ b/d2bs/kolbot/libs/core/Data/LocaleStringID.js @@ -0,0 +1,7806 @@ +/** +* @filename LocaleStringID.js +* @author Nishimura-Katsuo +* @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 LocaleStringName = {}; + + for (let k in LocaleStringID) { + LocaleStringName[LocaleStringID[k]] = k; + } + + module.exports = { + LocaleStringName: LocaleStringName, + LocaleStringID: LocaleStringID + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Data/MonsterData.js b/d2bs/kolbot/libs/core/Data/MonsterData.js new file mode 100644 index 000000000..337941fba --- /dev/null +++ b/d2bs/kolbot/libs/core/Data/MonsterData.js @@ -0,0 +1,116 @@ +/** +* @filename MonsterData.js +* @author Nishimura-Katsuo +* @desc monster data library +* +*/ + +(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); + + 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]); + + return matches[0][1]; + }; + + module.exports = MonsterData; +})(module, require); diff --git a/d2bs/kolbot/libs/core/Data/NTItemAlias.js b/d2bs/kolbot/libs/core/Data/NTItemAlias.js new file mode 100644 index 000000000..bbfbaaac2 --- /dev/null +++ b/d2bs/kolbot/libs/core/Data/NTItemAlias.js @@ -0,0 +1,1485 @@ +/* eslint-disable dot-notation */ +/** +* @filename NTItemAlias.js +* @author kolton +* @credit d2nt +* @desc Item alias's to work with NTItemParser for kolbots pickit system +* +*/ + +/** @global */ +const NTIPAliasType = {}; +NTIPAliasType["shield"] = 2; +NTIPAliasType["armor"] = 3; +NTIPAliasType["gold"] = 4; +NTIPAliasType["bowquiver"] = 5; +NTIPAliasType["crossbowquiver"] = 6; +NTIPAliasType["playerbodypart"] = 7; +NTIPAliasType["herb"] = 8; +NTIPAliasType["potion"] = 9; +NTIPAliasType["ring"] = 10; +NTIPAliasType["elixir"] = 11; +NTIPAliasType["amulet"] = 12; +NTIPAliasType["charm"] = 13; +NTIPAliasType["notused"] = 14; +NTIPAliasType["boots"] = 15; +NTIPAliasType["gloves"] = 16; +NTIPAliasType["notused"] = 17; +NTIPAliasType["book"] = 18; +NTIPAliasType["belt"] = 19; +NTIPAliasType["gem"] = 20; +NTIPAliasType["torch"] = 21; +NTIPAliasType["scroll"] = 22; +NTIPAliasType["notused"] = 23; +NTIPAliasType["scepter"] = 24; +NTIPAliasType["wand"] = 25; +NTIPAliasType["staff"] = 26; +NTIPAliasType["bow"] = 27; +NTIPAliasType["axe"] = 28; +NTIPAliasType["club"] = 29; +NTIPAliasType["sword"] = 30; +NTIPAliasType["hammer"] = 31; +NTIPAliasType["knife"] = 32; +NTIPAliasType["spear"] = 33; +NTIPAliasType["polearm"] = 34; +NTIPAliasType["crossbow"] = 35; +NTIPAliasType["mace"] = 36; +NTIPAliasType["helm"] = 37; +NTIPAliasType["missilepotion"] = 38; +NTIPAliasType["quest"] = 39; +NTIPAliasType["bodypart"] = 40; +NTIPAliasType["key"] = 41; +NTIPAliasType["throwingknife"] = 42; +NTIPAliasType["throwingaxe"] = 43; +NTIPAliasType["javelin"] = 44; +NTIPAliasType["weapon"] = 45; +NTIPAliasType["meleeweapon"] = 46; +NTIPAliasType["missileweapon"] = 47; +NTIPAliasType["thrownweapon"] = 48; +NTIPAliasType["comboweapon"] = 49; +NTIPAliasType["anyarmor"] = 50; +NTIPAliasType["anyshield"] = 51; +NTIPAliasType["miscellaneous"] = 52; +NTIPAliasType["socketfiller"] = 53; +NTIPAliasType["secondhand"] = 54; +NTIPAliasType["stavesandrods"] = 55; +NTIPAliasType["missile"] = 56; +NTIPAliasType["blunt"] = 57; +NTIPAliasType["jewel"] = 58; +NTIPAliasType["classspecific"] = 59; +NTIPAliasType["amazonitem"] = 60; +NTIPAliasType["barbarianitem"] = 61; +NTIPAliasType["necromanceritem"] = 62; +NTIPAliasType["paladinitem"] = 63; +NTIPAliasType["sorceressitem"] = 64; +NTIPAliasType["assassinitem"] = 65; +NTIPAliasType["druiditem"] = 66; +NTIPAliasType["handtohand"] = 67; +NTIPAliasType["orb"] = 68; +NTIPAliasType["voodooheads"] = 69; +NTIPAliasType["auricshields"] = 70; +NTIPAliasType["primalhelm"] = 71; +NTIPAliasType["pelt"] = 72; +NTIPAliasType["cloak"] = 73; +NTIPAliasType["rune"] = 74; +NTIPAliasType["circlet"] = 75; +NTIPAliasType["healingpotion"] = 76; +NTIPAliasType["manapotion"] = 77; +NTIPAliasType["rejuvpotion"] = 78; +NTIPAliasType["staminapotion"] = 79; +NTIPAliasType["antidotepotion"] = 80; +NTIPAliasType["thawingpotion"] = 81; +NTIPAliasType["smallcharm"] = 82; +NTIPAliasType["mediumcharm"] = 83; +NTIPAliasType["largecharm"] = 84; +NTIPAliasType["amazonbow"] = 85; +NTIPAliasType["amazonspear"] = 86; +NTIPAliasType["amazonjavelin"] = 87; +NTIPAliasType["assassinclaw"] = 88; +NTIPAliasType["magicbowquiv"] = 89; +NTIPAliasType["magicxbowquiv"] = 90; +NTIPAliasType["chippedgem"] = 91; +NTIPAliasType["flawedgem"] = 92; +NTIPAliasType["standardgem"] = 93; +NTIPAliasType["flawlessgem"] = 94; +NTIPAliasType["perfectgem"] = 95; +NTIPAliasType["amethyst"] = 96; +NTIPAliasType["diamond"] = 97; +NTIPAliasType["emerald"] = 98; +NTIPAliasType["ruby"] = 99; +NTIPAliasType["sapphire"] = 100; +NTIPAliasType["topaz"] = 101; +NTIPAliasType["skull"] = 102; + +/** @global */ +const NTIPAliasClassID = {}; +NTIPAliasClassID["hax"] = 0; NTIPAliasClassID["handaxe"] = 0; +NTIPAliasClassID["axe"] = 1; +NTIPAliasClassID["2ax"] = 2; NTIPAliasClassID["doubleaxe"] = 2; +NTIPAliasClassID["mpi"] = 3; NTIPAliasClassID["militarypick"] = 3; +NTIPAliasClassID["wax"] = 4; NTIPAliasClassID["waraxe"] = 4; +NTIPAliasClassID["lax"] = 5; NTIPAliasClassID["largeaxe"] = 5; +NTIPAliasClassID["bax"] = 6; NTIPAliasClassID["broadaxe"] = 6; +NTIPAliasClassID["btx"] = 7; NTIPAliasClassID["battleaxe"] = 7; +NTIPAliasClassID["gax"] = 8; NTIPAliasClassID["greataxe"] = 8; +NTIPAliasClassID["gix"] = 9; NTIPAliasClassID["giantaxe"] = 9; +NTIPAliasClassID["wnd"] = 10; NTIPAliasClassID["wand"] = 10; +NTIPAliasClassID["ywn"] = 11; NTIPAliasClassID["yewwand"] = 11; +NTIPAliasClassID["bwn"] = 12; NTIPAliasClassID["bonewand"] = 12; +NTIPAliasClassID["gwn"] = 13; NTIPAliasClassID["grimwand"] = 13; +NTIPAliasClassID["clb"] = 14; NTIPAliasClassID["club"] = 14; +NTIPAliasClassID["scp"] = 15; NTIPAliasClassID["scepter"] = 15; +NTIPAliasClassID["gsc"] = 16; NTIPAliasClassID["grandscepter"] = 16; +NTIPAliasClassID["wsp"] = 17; NTIPAliasClassID["warscepter"] = 17; +NTIPAliasClassID["spc"] = 18; NTIPAliasClassID["spikedclub"] = 18; +NTIPAliasClassID["mac"] = 19; NTIPAliasClassID["mace"] = 19; +NTIPAliasClassID["mst"] = 20; NTIPAliasClassID["morningstar"] = 20; +NTIPAliasClassID["fla"] = 21; NTIPAliasClassID["flail"] = 21; +NTIPAliasClassID["whm"] = 22; NTIPAliasClassID["warhammer"] = 22; +NTIPAliasClassID["mau"] = 23; NTIPAliasClassID["maul"] = 23; +NTIPAliasClassID["gma"] = 24; NTIPAliasClassID["greatmaul"] = 24; +NTIPAliasClassID["ssd"] = 25; NTIPAliasClassID["shortsword"] = 25; +NTIPAliasClassID["scm"] = 26; NTIPAliasClassID["scimitar"] = 26; +NTIPAliasClassID["sbr"] = 27; NTIPAliasClassID["sabre"] = 27; +NTIPAliasClassID["flc"] = 28; NTIPAliasClassID["falchion"] = 28; +NTIPAliasClassID["crs"] = 29; NTIPAliasClassID["crystalsword"] = 29; +NTIPAliasClassID["bsd"] = 30; NTIPAliasClassID["broadsword"] = 30; +NTIPAliasClassID["lsd"] = 31; NTIPAliasClassID["longsword"] = 31; +NTIPAliasClassID["wsd"] = 32; NTIPAliasClassID["warsword"] = 32; +NTIPAliasClassID["2hs"] = 33; NTIPAliasClassID["twohandedsword"] = 33; +NTIPAliasClassID["clm"] = 34; NTIPAliasClassID["claymore"] = 34; +NTIPAliasClassID["gis"] = 35; NTIPAliasClassID["giantsword"] = 35; +NTIPAliasClassID["bsw"] = 36; NTIPAliasClassID["bastardsword"] = 36; +NTIPAliasClassID["flb"] = 37; NTIPAliasClassID["flamberge"] = 37; +NTIPAliasClassID["gsd"] = 38; NTIPAliasClassID["greatsword"] = 38; +NTIPAliasClassID["dgr"] = 39; NTIPAliasClassID["dagger"] = 39; +NTIPAliasClassID["dir"] = 40; NTIPAliasClassID["dirk"] = 40; +NTIPAliasClassID["kri"] = 41; NTIPAliasClassID["kris"] = 41; +NTIPAliasClassID["bld"] = 42; NTIPAliasClassID["blade"] = 42; +NTIPAliasClassID["tkf"] = 43; NTIPAliasClassID["throwingknife"] = 43; +NTIPAliasClassID["tax"] = 44; NTIPAliasClassID["throwingaxe"] = 44; +NTIPAliasClassID["bkf"] = 45; NTIPAliasClassID["balancedknife"] = 45; +NTIPAliasClassID["bal"] = 46; NTIPAliasClassID["balancedaxe"] = 46; +NTIPAliasClassID["jav"] = 47; NTIPAliasClassID["javelin"] = 47; +NTIPAliasClassID["pil"] = 48; NTIPAliasClassID["pilum"] = 48; +NTIPAliasClassID["ssp"] = 49; NTIPAliasClassID["shortspear"] = 49; +NTIPAliasClassID["glv"] = 50; NTIPAliasClassID["glaive"] = 50; +NTIPAliasClassID["tsp"] = 51; NTIPAliasClassID["throwingspear"] = 51; +NTIPAliasClassID["spr"] = 52; NTIPAliasClassID["spear"] = 52; +NTIPAliasClassID["tri"] = 53; NTIPAliasClassID["trident"] = 53; +NTIPAliasClassID["brn"] = 54; NTIPAliasClassID["brandistock"] = 54; +NTIPAliasClassID["spt"] = 55; NTIPAliasClassID["spetum"] = 55; +NTIPAliasClassID["pik"] = 56; NTIPAliasClassID["pike"] = 56; +NTIPAliasClassID["bar"] = 57; NTIPAliasClassID["bardiche"] = 57; +NTIPAliasClassID["vou"] = 58; NTIPAliasClassID["voulge"] = 58; +NTIPAliasClassID["scy"] = 59; NTIPAliasClassID["scythe"] = 59; +NTIPAliasClassID["pax"] = 60; NTIPAliasClassID["poleaxe"] = 60; +NTIPAliasClassID["hal"] = 61; NTIPAliasClassID["halberd"] = 61; +NTIPAliasClassID["wsc"] = 62; NTIPAliasClassID["warscythe"] = 62; +NTIPAliasClassID["sst"] = 63; NTIPAliasClassID["shortstaff"] = 63; +NTIPAliasClassID["lst"] = 64; NTIPAliasClassID["longstaff"] = 64; +NTIPAliasClassID["cst"] = 65; NTIPAliasClassID["gnarledstaff"] = 65; +NTIPAliasClassID["bst"] = 66; NTIPAliasClassID["battlestaff"] = 66; +NTIPAliasClassID["wst"] = 67; NTIPAliasClassID["warstaff"] = 67; +NTIPAliasClassID["sbw"] = 68; NTIPAliasClassID["shortbow"] = 68; +NTIPAliasClassID["hbw"] = 69; NTIPAliasClassID["hunter'sbow"] = 69; +NTIPAliasClassID["lbw"] = 70; NTIPAliasClassID["longbow"] = 70; +NTIPAliasClassID["cbw"] = 71; NTIPAliasClassID["compositebow"] = 71; +NTIPAliasClassID["sbb"] = 72; NTIPAliasClassID["shortbattlebow"] = 72; +NTIPAliasClassID["lbb"] = 73; NTIPAliasClassID["longbattlebow"] = 73; +NTIPAliasClassID["swb"] = 74; NTIPAliasClassID["shortwarbow"] = 74; +NTIPAliasClassID["lwb"] = 75; NTIPAliasClassID["longwarbow"] = 75; +NTIPAliasClassID["lxb"] = 76; NTIPAliasClassID["lightcrossbow"] = 76; +NTIPAliasClassID["mxb"] = 77; NTIPAliasClassID["crossbow"] = 77; +NTIPAliasClassID["hxb"] = 78; NTIPAliasClassID["heavycrossbow"] = 78; +NTIPAliasClassID["rxb"] = 79; NTIPAliasClassID["repeatingcrossbow"] = 79; +NTIPAliasClassID["gps"] = 80; NTIPAliasClassID["rancidgaspotion"] = 80; +NTIPAliasClassID["ops"] = 81; NTIPAliasClassID["oilpotion"] = 81; +NTIPAliasClassID["gpm"] = 82; NTIPAliasClassID["chokinggaspotion"] = 82; +NTIPAliasClassID["opm"] = 83; NTIPAliasClassID["explodingpotion"] = 83; +NTIPAliasClassID["gpl"] = 84; NTIPAliasClassID["stranglinggaspotion"] = 84; +NTIPAliasClassID["opl"] = 85; NTIPAliasClassID["fulminatingpotion"] = 85; +NTIPAliasClassID["d33"] = 86; NTIPAliasClassID["decoygidbinn"] = 86; +NTIPAliasClassID["g33"] = 87; NTIPAliasClassID["thegidbinn"] = 87; +NTIPAliasClassID["leg"] = 88; NTIPAliasClassID["wirt'sleg"] = 88; +NTIPAliasClassID["hdm"] = 89; NTIPAliasClassID["horadricmalus"] = 89; +NTIPAliasClassID["hfh"] = 90; NTIPAliasClassID["hellforgehammer"] = 90; +NTIPAliasClassID["hst"] = 91; NTIPAliasClassID["horadricstaff"] = 91; +NTIPAliasClassID["msf"] = 92; NTIPAliasClassID["shaftofthehoradricstaff"] = 92; +NTIPAliasClassID["9ha"] = 93; NTIPAliasClassID["hatchet"] = 93; +NTIPAliasClassID["9ax"] = 94; NTIPAliasClassID["cleaver"] = 94; +NTIPAliasClassID["92a"] = 95; NTIPAliasClassID["twinaxe"] = 95; +NTIPAliasClassID["9mp"] = 96; NTIPAliasClassID["crowbill"] = 96; +NTIPAliasClassID["9wa"] = 97; NTIPAliasClassID["naga"] = 97; +NTIPAliasClassID["9la"] = 98; NTIPAliasClassID["militaryaxe"] = 98; +NTIPAliasClassID["9ba"] = 99; NTIPAliasClassID["beardedaxe"] = 99; +NTIPAliasClassID["9bt"] = 100; NTIPAliasClassID["tabar"] = 100; +NTIPAliasClassID["9ga"] = 101; NTIPAliasClassID["gothicaxe"] = 101; +NTIPAliasClassID["9gi"] = 102; NTIPAliasClassID["ancientaxe"] = 102; +NTIPAliasClassID["9wn"] = 103; NTIPAliasClassID["burntwand"] = 103; +NTIPAliasClassID["9yw"] = 104; NTIPAliasClassID["petrifiedwand"] = 104; +NTIPAliasClassID["9bw"] = 105; NTIPAliasClassID["tombwand"] = 105; +NTIPAliasClassID["9gw"] = 106; NTIPAliasClassID["gravewand"] = 106; +NTIPAliasClassID["9cl"] = 107; NTIPAliasClassID["cudgel"] = 107; +NTIPAliasClassID["9sc"] = 108; NTIPAliasClassID["runescepter"] = 108; +NTIPAliasClassID["9qs"] = 109; NTIPAliasClassID["holywatersprinkler"] = 109; +NTIPAliasClassID["9ws"] = 110; NTIPAliasClassID["divinescepter"] = 110; +NTIPAliasClassID["9sp"] = 111; NTIPAliasClassID["barbedclub"] = 111; +NTIPAliasClassID["9ma"] = 112; NTIPAliasClassID["flangedmace"] = 112; +NTIPAliasClassID["9mt"] = 113; NTIPAliasClassID["jaggedstar"] = 113; +NTIPAliasClassID["9fl"] = 114; NTIPAliasClassID["knout"] = 114; +NTIPAliasClassID["9wh"] = 115; NTIPAliasClassID["battlehammer"] = 115; +NTIPAliasClassID["9m9"] = 116; NTIPAliasClassID["warclub"] = 116; +NTIPAliasClassID["9gm"] = 117; NTIPAliasClassID["marteldefer"] = 117; +NTIPAliasClassID["9ss"] = 118; NTIPAliasClassID["gladius"] = 118; +NTIPAliasClassID["9sm"] = 119; NTIPAliasClassID["cutlass"] = 119; +NTIPAliasClassID["9sb"] = 120; NTIPAliasClassID["shamshir"] = 120; +NTIPAliasClassID["9fc"] = 121; NTIPAliasClassID["tulwar"] = 121; +NTIPAliasClassID["9cr"] = 122; NTIPAliasClassID["dimensionalblade"] = 122; +NTIPAliasClassID["9bs"] = 123; NTIPAliasClassID["battlesword"] = 123; +NTIPAliasClassID["9ls"] = 124; NTIPAliasClassID["runesword"] = 124; +NTIPAliasClassID["9wd"] = 125; NTIPAliasClassID["ancientsword"] = 125; +NTIPAliasClassID["92h"] = 126; NTIPAliasClassID["espandon"] = 126; +NTIPAliasClassID["9cm"] = 127; NTIPAliasClassID["dacianfalx"] = 127; +NTIPAliasClassID["9gs"] = 128; NTIPAliasClassID["tusksword"] = 128; +NTIPAliasClassID["9b9"] = 129; NTIPAliasClassID["gothicsword"] = 129; +NTIPAliasClassID["9fb"] = 130; NTIPAliasClassID["zweihander"] = 130; +NTIPAliasClassID["9gd"] = 131; NTIPAliasClassID["executionersword"] = 131; +NTIPAliasClassID["9dg"] = 132; NTIPAliasClassID["poignard"] = 132; +NTIPAliasClassID["9di"] = 133; NTIPAliasClassID["rondel"] = 133; +NTIPAliasClassID["9kr"] = 134; NTIPAliasClassID["cinquedeas"] = 134; +NTIPAliasClassID["9bl"] = 135; NTIPAliasClassID["stiletto"] = 135; +NTIPAliasClassID["9tk"] = 136; NTIPAliasClassID["battledart"] = 136; +NTIPAliasClassID["9ta"] = 137; NTIPAliasClassID["francisca"] = 137; +NTIPAliasClassID["9bk"] = 138; NTIPAliasClassID["wardart"] = 138; +NTIPAliasClassID["9b8"] = 139; NTIPAliasClassID["hurlbat"] = 139; +NTIPAliasClassID["9ja"] = 140; NTIPAliasClassID["warjavelin"] = 140; +NTIPAliasClassID["9pi"] = 141; NTIPAliasClassID["greatpilum"] = 141; +NTIPAliasClassID["9s9"] = 142; NTIPAliasClassID["simbilan"] = 142; +NTIPAliasClassID["9gl"] = 143; NTIPAliasClassID["spiculum"] = 143; +NTIPAliasClassID["9ts"] = 144; NTIPAliasClassID["harpoon"] = 144; +NTIPAliasClassID["9sr"] = 145; NTIPAliasClassID["warspear"] = 145; +NTIPAliasClassID["9tr"] = 146; NTIPAliasClassID["fuscina"] = 146; +NTIPAliasClassID["9br"] = 147; NTIPAliasClassID["warfork"] = 147; +NTIPAliasClassID["9st"] = 148; NTIPAliasClassID["yari"] = 148; +NTIPAliasClassID["9p9"] = 149; NTIPAliasClassID["lance"] = 149; +NTIPAliasClassID["9b7"] = 150; NTIPAliasClassID["lochaberaxe"] = 150; +NTIPAliasClassID["9vo"] = 151; NTIPAliasClassID["bill"] = 151; +NTIPAliasClassID["9s8"] = 152; NTIPAliasClassID["battlescythe"] = 152; +NTIPAliasClassID["9pa"] = 153; NTIPAliasClassID["partizan"] = 153; +NTIPAliasClassID["9h9"] = 154; NTIPAliasClassID["becdecorbin"] = 154; +NTIPAliasClassID["9wc"] = 155; NTIPAliasClassID["grimscythe"] = 155; +NTIPAliasClassID["8ss"] = 156; NTIPAliasClassID["jostaff"] = 156; +NTIPAliasClassID["8ls"] = 157; NTIPAliasClassID["quarterstaff"] = 157; +NTIPAliasClassID["8cs"] = 158; NTIPAliasClassID["cedarstaff"] = 158; +NTIPAliasClassID["8bs"] = 159; NTIPAliasClassID["gothicstaff"] = 159; +NTIPAliasClassID["8ws"] = 160; NTIPAliasClassID["runestaff"] = 160; +NTIPAliasClassID["8sb"] = 161; NTIPAliasClassID["edgebow"] = 161; +NTIPAliasClassID["8hb"] = 162; NTIPAliasClassID["razorbow"] = 162; +NTIPAliasClassID["8lb"] = 163; NTIPAliasClassID["cedarbow"] = 163; +NTIPAliasClassID["8cb"] = 164; NTIPAliasClassID["doublebow"] = 164; +NTIPAliasClassID["8s8"] = 165; NTIPAliasClassID["shortsiegebow"] = 165; +NTIPAliasClassID["8l8"] = 166; NTIPAliasClassID["largesiegebow"] = 166; +NTIPAliasClassID["8sw"] = 167; NTIPAliasClassID["runebow"] = 167; +NTIPAliasClassID["8lw"] = 168; NTIPAliasClassID["gothicbow"] = 168; +NTIPAliasClassID["8lx"] = 169; NTIPAliasClassID["arbalest"] = 169; +NTIPAliasClassID["8mx"] = 170; NTIPAliasClassID["siegecrossbow"] = 170; +NTIPAliasClassID["8hx"] = 171; NTIPAliasClassID["ballista"] = 171; +NTIPAliasClassID["8rx"] = 172; NTIPAliasClassID["chukonu"] = 172; +NTIPAliasClassID["qf1"] = 173; NTIPAliasClassID["khalim'sflail"] = 173; +NTIPAliasClassID["qf2"] = 174; NTIPAliasClassID["khalim'swill"] = 174; +NTIPAliasClassID["ktr"] = 175; NTIPAliasClassID["katar"] = 175; +NTIPAliasClassID["wrb"] = 176; NTIPAliasClassID["wristblade"] = 176; +NTIPAliasClassID["axf"] = 177; NTIPAliasClassID["hatchethands"] = 177; +NTIPAliasClassID["ces"] = 178; NTIPAliasClassID["cestus"] = 178; +NTIPAliasClassID["clw"] = 179; NTIPAliasClassID["claws"] = 179; +NTIPAliasClassID["btl"] = 180; NTIPAliasClassID["bladetalons"] = 180; +NTIPAliasClassID["skr"] = 181; NTIPAliasClassID["scissorskatar"] = 181; +NTIPAliasClassID["9ar"] = 182; NTIPAliasClassID["quhab"] = 182; +NTIPAliasClassID["9wb"] = 183; NTIPAliasClassID["wristspike"] = 183; +NTIPAliasClassID["9xf"] = 184; NTIPAliasClassID["fascia"] = 184; +NTIPAliasClassID["9cs"] = 185; NTIPAliasClassID["handscythe"] = 185; +NTIPAliasClassID["9lw"] = 186; NTIPAliasClassID["greaterclaws"] = 186; +NTIPAliasClassID["9tw"] = 187; NTIPAliasClassID["greatertalons"] = 187; +NTIPAliasClassID["9qr"] = 188; NTIPAliasClassID["scissorsquhab"] = 188; +NTIPAliasClassID["7ar"] = 189; NTIPAliasClassID["suwayyah"] = 189; +NTIPAliasClassID["7wb"] = 190; NTIPAliasClassID["wristsword"] = 190; +NTIPAliasClassID["7xf"] = 191; NTIPAliasClassID["warfist"] = 191; +NTIPAliasClassID["7cs"] = 192; NTIPAliasClassID["battlecestus"] = 192; +NTIPAliasClassID["7lw"] = 193; NTIPAliasClassID["feralclaws"] = 193; +NTIPAliasClassID["7tw"] = 194; NTIPAliasClassID["runictalons"] = 194; +NTIPAliasClassID["7qr"] = 195; NTIPAliasClassID["scissorssuwayyah"] = 195; +NTIPAliasClassID["7ha"] = 196; NTIPAliasClassID["tomahawk"] = 196; +NTIPAliasClassID["7ax"] = 197; NTIPAliasClassID["smallcrescent"] = 197; +NTIPAliasClassID["72a"] = 198; NTIPAliasClassID["ettinaxe"] = 198; +NTIPAliasClassID["7mp"] = 199; NTIPAliasClassID["warspike"] = 199; +NTIPAliasClassID["7wa"] = 200; NTIPAliasClassID["berserkeraxe"] = 200; +NTIPAliasClassID["7la"] = 201; NTIPAliasClassID["feralaxe"] = 201; +NTIPAliasClassID["7ba"] = 202; NTIPAliasClassID["silveredgedaxe"] = 202; +NTIPAliasClassID["7bt"] = 203; NTIPAliasClassID["decapitator"] = 203; +NTIPAliasClassID["7ga"] = 204; NTIPAliasClassID["championaxe"] = 204; +NTIPAliasClassID["7gi"] = 205; NTIPAliasClassID["gloriousaxe"] = 205; +NTIPAliasClassID["7wn"] = 206; NTIPAliasClassID["polishedwand"] = 206; +NTIPAliasClassID["7yw"] = 207; NTIPAliasClassID["ghostwand"] = 207; +NTIPAliasClassID["7bw"] = 208; NTIPAliasClassID["lichwand"] = 208; +NTIPAliasClassID["7gw"] = 209; NTIPAliasClassID["unearthedwand"] = 209; +NTIPAliasClassID["7cl"] = 210; NTIPAliasClassID["truncheon"] = 210; +NTIPAliasClassID["7sc"] = 211; NTIPAliasClassID["mightyscepter"] = 211; +NTIPAliasClassID["7qs"] = 212; NTIPAliasClassID["seraphrod"] = 212; +NTIPAliasClassID["7ws"] = 213; NTIPAliasClassID["caduceus"] = 213; +NTIPAliasClassID["7sp"] = 214; NTIPAliasClassID["tyrantclub"] = 214; +NTIPAliasClassID["7ma"] = 215; NTIPAliasClassID["reinforcedmace"] = 215; +NTIPAliasClassID["7mt"] = 216; NTIPAliasClassID["devilstar"] = 216; +NTIPAliasClassID["7fl"] = 217; NTIPAliasClassID["scourge"] = 217; +NTIPAliasClassID["7wh"] = 218; NTIPAliasClassID["legendarymallet"] = 218; +NTIPAliasClassID["7m7"] = 219; NTIPAliasClassID["ogremaul"] = 219; +NTIPAliasClassID["7gm"] = 220; NTIPAliasClassID["thundermaul"] = 220; +NTIPAliasClassID["7ss"] = 221; NTIPAliasClassID["falcata"] = 221; +NTIPAliasClassID["7sm"] = 222; NTIPAliasClassID["ataghan"] = 222; +NTIPAliasClassID["7sb"] = 223; NTIPAliasClassID["elegantblade"] = 223; +NTIPAliasClassID["7fc"] = 224; NTIPAliasClassID["hydraedge"] = 224; +NTIPAliasClassID["7cr"] = 225; NTIPAliasClassID["phaseblade"] = 225; +NTIPAliasClassID["7bs"] = 226; NTIPAliasClassID["conquestsword"] = 226; +NTIPAliasClassID["7ls"] = 227; NTIPAliasClassID["crypticsword"] = 227; +NTIPAliasClassID["7wd"] = 228; NTIPAliasClassID["mythicalsword"] = 228; +NTIPAliasClassID["72h"] = 229; NTIPAliasClassID["legendsword"] = 229; +NTIPAliasClassID["7cm"] = 230; NTIPAliasClassID["highlandblade"] = 230; +NTIPAliasClassID["7gs"] = 231; NTIPAliasClassID["balrogblade"] = 231; +NTIPAliasClassID["7b7"] = 232; NTIPAliasClassID["championsword"] = 232; +NTIPAliasClassID["7fb"] = 233; NTIPAliasClassID["colossussword"] = 233; +NTIPAliasClassID["7gd"] = 234; NTIPAliasClassID["colossusblade"] = 234; +NTIPAliasClassID["7dg"] = 235; NTIPAliasClassID["boneknife"] = 235; +NTIPAliasClassID["7di"] = 236; NTIPAliasClassID["mithrilpoint"] = 236; +NTIPAliasClassID["7kr"] = 237; NTIPAliasClassID["fangedknife"] = 237; +NTIPAliasClassID["7bl"] = 238; NTIPAliasClassID["legendspike"] = 238; +NTIPAliasClassID["7tk"] = 239; NTIPAliasClassID["flyingknife"] = 239; +NTIPAliasClassID["7ta"] = 240; NTIPAliasClassID["flyingaxe"] = 240; +NTIPAliasClassID["7bk"] = 241; NTIPAliasClassID["wingedknife"] = 241; +NTIPAliasClassID["7b8"] = 242; NTIPAliasClassID["wingedaxe"] = 242; +NTIPAliasClassID["7ja"] = 243; NTIPAliasClassID["hyperionjavelin"] = 243; +NTIPAliasClassID["7pi"] = 244; NTIPAliasClassID["stygianpilum"] = 244; +NTIPAliasClassID["7s7"] = 245; NTIPAliasClassID["balrogspear"] = 245; +NTIPAliasClassID["7gl"] = 246; NTIPAliasClassID["ghostglaive"] = 246; +NTIPAliasClassID["7ts"] = 247; NTIPAliasClassID["wingedharpoon"] = 247; +NTIPAliasClassID["7sr"] = 248; NTIPAliasClassID["hyperionspear"] = 248; +NTIPAliasClassID["7tr"] = 249; NTIPAliasClassID["stygianpike"] = 249; +NTIPAliasClassID["7br"] = 250; NTIPAliasClassID["mancatcher"] = 250; +NTIPAliasClassID["7st"] = 251; NTIPAliasClassID["ghostspear"] = 251; +NTIPAliasClassID["7p7"] = 252; NTIPAliasClassID["warpike"] = 252; +NTIPAliasClassID["7o7"] = 253; NTIPAliasClassID["ogreaxe"] = 253; +NTIPAliasClassID["7vo"] = 254; NTIPAliasClassID["colossusvoulge"] = 254; +NTIPAliasClassID["7s8"] = 255; NTIPAliasClassID["thresher"] = 255; +NTIPAliasClassID["7pa"] = 256; NTIPAliasClassID["crypticaxe"] = 256; +NTIPAliasClassID["7h7"] = 257; NTIPAliasClassID["greatpoleaxe"] = 257; +NTIPAliasClassID["7wc"] = 258; NTIPAliasClassID["giantthresher"] = 258; +NTIPAliasClassID["6ss"] = 259; NTIPAliasClassID["walkingstick"] = 259; +NTIPAliasClassID["6ls"] = 260; NTIPAliasClassID["stalagmite"] = 260; +NTIPAliasClassID["6cs"] = 261; NTIPAliasClassID["elderstaff"] = 261; +NTIPAliasClassID["6bs"] = 262; NTIPAliasClassID["shillelagh"] = 262; +NTIPAliasClassID["6ws"] = 263; NTIPAliasClassID["archonstaff"] = 263; +NTIPAliasClassID["6sb"] = 264; NTIPAliasClassID["spiderbow"] = 264; +NTIPAliasClassID["6hb"] = 265; NTIPAliasClassID["bladebow"] = 265; +NTIPAliasClassID["6lb"] = 266; NTIPAliasClassID["shadowbow"] = 266; +NTIPAliasClassID["6cb"] = 267; NTIPAliasClassID["greatbow"] = 267; +NTIPAliasClassID["6s7"] = 268; NTIPAliasClassID["diamondbow"] = 268; +NTIPAliasClassID["6l7"] = 269; NTIPAliasClassID["crusaderbow"] = 269; +NTIPAliasClassID["6sw"] = 270; NTIPAliasClassID["wardbow"] = 270; +NTIPAliasClassID["6lw"] = 271; NTIPAliasClassID["hydrabow"] = 271; +NTIPAliasClassID["6lx"] = 272; NTIPAliasClassID["pelletbow"] = 272; +NTIPAliasClassID["6mx"] = 273; NTIPAliasClassID["gorgoncrossbow"] = 273; +NTIPAliasClassID["6hx"] = 274; NTIPAliasClassID["colossuscrossbow"] = 274; +NTIPAliasClassID["6rx"] = 275; NTIPAliasClassID["demoncrossbow"] = 275; +NTIPAliasClassID["ob1"] = 276; NTIPAliasClassID["eagleorb"] = 276; +NTIPAliasClassID["ob2"] = 277; NTIPAliasClassID["sacredglobe"] = 277; +NTIPAliasClassID["ob3"] = 278; NTIPAliasClassID["smokedsphere"] = 278; +NTIPAliasClassID["ob4"] = 279; NTIPAliasClassID["claspedorb"] = 279; +NTIPAliasClassID["ob5"] = 280; NTIPAliasClassID["jared'sstone"] = 280; +NTIPAliasClassID["am1"] = 281; NTIPAliasClassID["stagbow"] = 281; +NTIPAliasClassID["am2"] = 282; NTIPAliasClassID["reflexbow"] = 282; +NTIPAliasClassID["am3"] = 283; NTIPAliasClassID["maidenspear"] = 283; +NTIPAliasClassID["am4"] = 284; NTIPAliasClassID["maidenpike"] = 284; +NTIPAliasClassID["am5"] = 285; NTIPAliasClassID["maidenjavelin"] = 285; +NTIPAliasClassID["ob6"] = 286; NTIPAliasClassID["glowingorb"] = 286; +NTIPAliasClassID["ob7"] = 287; NTIPAliasClassID["crystallineglobe"] = 287; +NTIPAliasClassID["ob8"] = 288; NTIPAliasClassID["cloudysphere"] = 288; +NTIPAliasClassID["ob9"] = 289; NTIPAliasClassID["sparklingball"] = 289; +NTIPAliasClassID["oba"] = 290; NTIPAliasClassID["swirlingcrystal"] = 290; +NTIPAliasClassID["am6"] = 291; NTIPAliasClassID["ashwoodbow"] = 291; +NTIPAliasClassID["am7"] = 292; NTIPAliasClassID["ceremonialbow"] = 292; +NTIPAliasClassID["am8"] = 293; NTIPAliasClassID["ceremonialspear"] = 293; +NTIPAliasClassID["am9"] = 294; NTIPAliasClassID["ceremonialpike"] = 294; +NTIPAliasClassID["ama"] = 295; NTIPAliasClassID["ceremonialjavelin"] = 295; +NTIPAliasClassID["obb"] = 296; NTIPAliasClassID["heavenlystone"] = 296; +NTIPAliasClassID["obc"] = 297; NTIPAliasClassID["eldritchorb"] = 297; +NTIPAliasClassID["obd"] = 298; NTIPAliasClassID["demonheart"] = 298; +NTIPAliasClassID["obe"] = 299; NTIPAliasClassID["vortexorb"] = 299; +NTIPAliasClassID["obf"] = 300; NTIPAliasClassID["dimensionalshard"] = 300; +NTIPAliasClassID["amb"] = 301; NTIPAliasClassID["matriarchalbow"] = 301; +NTIPAliasClassID["amc"] = 302; NTIPAliasClassID["grandmatronbow"] = 302; +NTIPAliasClassID["amd"] = 303; NTIPAliasClassID["matriarchalspear"] = 303; +NTIPAliasClassID["ame"] = 304; NTIPAliasClassID["matriarchalpike"] = 304; +NTIPAliasClassID["amf"] = 305; NTIPAliasClassID["matriarchaljavelin"] = 305; +NTIPAliasClassID["cap"] = 306; +NTIPAliasClassID["skp"] = 307; NTIPAliasClassID["skullcap"] = 307; +NTIPAliasClassID["hlm"] = 308; NTIPAliasClassID["helm"] = 308; +NTIPAliasClassID["fhl"] = 309; NTIPAliasClassID["fullhelm"] = 309; +NTIPAliasClassID["ghm"] = 310; NTIPAliasClassID["greathelm"] = 310; +NTIPAliasClassID["crn"] = 311; NTIPAliasClassID["crown"] = 311; +NTIPAliasClassID["msk"] = 312; NTIPAliasClassID["mask"] = 312; +NTIPAliasClassID["qui"] = 313; NTIPAliasClassID["quiltedarmor"] = 313; +NTIPAliasClassID["lea"] = 314; NTIPAliasClassID["leatherarmor"] = 314; +NTIPAliasClassID["hla"] = 315; NTIPAliasClassID["hardleatherarmor"] = 315; +NTIPAliasClassID["stu"] = 316; NTIPAliasClassID["studdedleather"] = 316; +NTIPAliasClassID["rng"] = 317; NTIPAliasClassID["ringmail"] = 317; +NTIPAliasClassID["scl"] = 318; NTIPAliasClassID["scalemail"] = 318; +NTIPAliasClassID["chn"] = 319; NTIPAliasClassID["chainmail"] = 319; +NTIPAliasClassID["brs"] = 320; NTIPAliasClassID["breastplate"] = 320; +NTIPAliasClassID["spl"] = 321; NTIPAliasClassID["splintmail"] = 321; +NTIPAliasClassID["plt"] = 322; NTIPAliasClassID["platemail"] = 322; +NTIPAliasClassID["fld"] = 323; NTIPAliasClassID["fieldplate"] = 323; +NTIPAliasClassID["gth"] = 324; NTIPAliasClassID["gothicplate"] = 324; +NTIPAliasClassID["ful"] = 325; NTIPAliasClassID["fullplatemail"] = 325; +NTIPAliasClassID["aar"] = 326; NTIPAliasClassID["ancientarmor"] = 326; +NTIPAliasClassID["ltp"] = 327; NTIPAliasClassID["lightplate"] = 327; +NTIPAliasClassID["buc"] = 328; NTIPAliasClassID["buckler"] = 328; +NTIPAliasClassID["sml"] = 329; NTIPAliasClassID["smallshield"] = 329; +NTIPAliasClassID["lrg"] = 330; NTIPAliasClassID["largeshield"] = 330; +NTIPAliasClassID["kit"] = 331; NTIPAliasClassID["kiteshield"] = 331; +NTIPAliasClassID["tow"] = 332; NTIPAliasClassID["towershield"] = 332; +NTIPAliasClassID["gts"] = 333; NTIPAliasClassID["gothicshield"] = 333; +NTIPAliasClassID["lgl"] = 334; NTIPAliasClassID["leathergloves"] = 334; +NTIPAliasClassID["vgl"] = 335; NTIPAliasClassID["heavygloves"] = 335; +NTIPAliasClassID["mgl"] = 336; NTIPAliasClassID["chaingloves"] = 336; +NTIPAliasClassID["tgl"] = 337; NTIPAliasClassID["lightgauntlets"] = 337; +NTIPAliasClassID["hgl"] = 338; NTIPAliasClassID["gauntlets"] = 338; +NTIPAliasClassID["lbt"] = 339; NTIPAliasClassID["boots"] = 339; +NTIPAliasClassID["vbt"] = 340; NTIPAliasClassID["heavyboots"] = 340; +NTIPAliasClassID["mbt"] = 341; NTIPAliasClassID["chainboots"] = 341; +NTIPAliasClassID["tbt"] = 342; NTIPAliasClassID["lightplatedboots"] = 342; +NTIPAliasClassID["hbt"] = 343; NTIPAliasClassID["greaves"] = 343; +NTIPAliasClassID["lbl"] = 344; NTIPAliasClassID["sash"] = 344; +NTIPAliasClassID["vbl"] = 345; NTIPAliasClassID["lightbelt"] = 345; +NTIPAliasClassID["mbl"] = 346; NTIPAliasClassID["belt"] = 346; +NTIPAliasClassID["tbl"] = 347; NTIPAliasClassID["heavybelt"] = 347; +NTIPAliasClassID["hbl"] = 348; NTIPAliasClassID["platedbelt"] = 348; +NTIPAliasClassID["bhm"] = 349; NTIPAliasClassID["bonehelm"] = 349; +NTIPAliasClassID["bsh"] = 350; NTIPAliasClassID["boneshield"] = 350; +NTIPAliasClassID["spk"] = 351; NTIPAliasClassID["spikedshield"] = 351; +NTIPAliasClassID["xap"] = 352; NTIPAliasClassID["warhat"] = 352; +NTIPAliasClassID["xkp"] = 353; NTIPAliasClassID["sallet"] = 353; +NTIPAliasClassID["xlm"] = 354; NTIPAliasClassID["casque"] = 354; +NTIPAliasClassID["xhl"] = 355; NTIPAliasClassID["basinet"] = 355; +NTIPAliasClassID["xhm"] = 356; NTIPAliasClassID["wingedhelm"] = 356; +NTIPAliasClassID["xrn"] = 357; NTIPAliasClassID["grandcrown"] = 357; +NTIPAliasClassID["xsk"] = 358; NTIPAliasClassID["deathmask"] = 358; +NTIPAliasClassID["xui"] = 359; NTIPAliasClassID["ghostarmor"] = 359; +NTIPAliasClassID["xea"] = 360; NTIPAliasClassID["serpentskinarmor"] = 360; +NTIPAliasClassID["xla"] = 361; NTIPAliasClassID["demonhidearmor"] = 361; +NTIPAliasClassID["xtu"] = 362; NTIPAliasClassID["trellisedarmor"] = 362; +NTIPAliasClassID["xng"] = 363; NTIPAliasClassID["linkedmail"] = 363; +NTIPAliasClassID["xcl"] = 364; NTIPAliasClassID["tigulatedmail"] = 364; +NTIPAliasClassID["xhn"] = 365; NTIPAliasClassID["mesharmor"] = 365; +NTIPAliasClassID["xrs"] = 366; NTIPAliasClassID["cuirass"] = 366; +NTIPAliasClassID["xpl"] = 367; NTIPAliasClassID["russetarmor"] = 367; +NTIPAliasClassID["xlt"] = 368; NTIPAliasClassID["templarcoat"] = 368; +NTIPAliasClassID["xld"] = 369; NTIPAliasClassID["sharktootharmor"] = 369; +NTIPAliasClassID["xth"] = 370; NTIPAliasClassID["embossedplate"] = 370; +NTIPAliasClassID["xul"] = 371; NTIPAliasClassID["chaosarmor"] = 371; +NTIPAliasClassID["xar"] = 372; NTIPAliasClassID["ornateplate"] = 372; +NTIPAliasClassID["xtp"] = 373; NTIPAliasClassID["mageplate"] = 373; +NTIPAliasClassID["xuc"] = 374; NTIPAliasClassID["defender"] = 374; +NTIPAliasClassID["xml"] = 375; NTIPAliasClassID["roundshield"] = 375; +NTIPAliasClassID["xrg"] = 376; NTIPAliasClassID["scutum"] = 376; +NTIPAliasClassID["xit"] = 377; NTIPAliasClassID["dragonshield"] = 377; +NTIPAliasClassID["xow"] = 378; NTIPAliasClassID["pavise"] = 378; +NTIPAliasClassID["xts"] = 379; NTIPAliasClassID["ancientshield"] = 379; +NTIPAliasClassID["xlg"] = 380; NTIPAliasClassID["demonhidegloves"] = 380; +NTIPAliasClassID["xvg"] = 381; NTIPAliasClassID["sharkskingloves"] = 381; +NTIPAliasClassID["xmg"] = 382; NTIPAliasClassID["heavybracers"] = 382; +NTIPAliasClassID["xtg"] = 383; NTIPAliasClassID["battlegauntlets"] = 383; +NTIPAliasClassID["xhg"] = 384; NTIPAliasClassID["wargauntlets"] = 384; +NTIPAliasClassID["xlb"] = 385; NTIPAliasClassID["demonhideboots"] = 385; +NTIPAliasClassID["xvb"] = 386; NTIPAliasClassID["sharkskinboots"] = 386; +NTIPAliasClassID["xmb"] = 387; NTIPAliasClassID["meshboots"] = 387; +NTIPAliasClassID["xtb"] = 388; NTIPAliasClassID["battleboots"] = 388; +NTIPAliasClassID["xhb"] = 389; NTIPAliasClassID["warboots"] = 389; +NTIPAliasClassID["zlb"] = 390; NTIPAliasClassID["demonhidesash"] = 390; +NTIPAliasClassID["zvb"] = 391; NTIPAliasClassID["sharkskinbelt"] = 391; +NTIPAliasClassID["zmb"] = 392; NTIPAliasClassID["meshbelt"] = 392; +NTIPAliasClassID["ztb"] = 393; NTIPAliasClassID["battlebelt"] = 393; +NTIPAliasClassID["zhb"] = 394; NTIPAliasClassID["warbelt"] = 394; +NTIPAliasClassID["xh9"] = 395; NTIPAliasClassID["grimhelm"] = 395; +NTIPAliasClassID["xsh"] = 396; NTIPAliasClassID["grimshield"] = 396; +NTIPAliasClassID["xpk"] = 397; NTIPAliasClassID["barbedshield"] = 397; +NTIPAliasClassID["dr1"] = 398; NTIPAliasClassID["wolfhead"] = 398; +NTIPAliasClassID["dr2"] = 399; NTIPAliasClassID["hawkhelm"] = 399; +NTIPAliasClassID["dr3"] = 400; NTIPAliasClassID["antlers"] = 400; +NTIPAliasClassID["dr4"] = 401; NTIPAliasClassID["falconmask"] = 401; +NTIPAliasClassID["dr5"] = 402; NTIPAliasClassID["spiritmask"] = 402; +NTIPAliasClassID["ba1"] = 403; NTIPAliasClassID["jawbonecap"] = 403; +NTIPAliasClassID["ba2"] = 404; NTIPAliasClassID["fangedhelm"] = 404; +NTIPAliasClassID["ba3"] = 405; NTIPAliasClassID["hornedhelm"] = 405; +NTIPAliasClassID["ba4"] = 406; NTIPAliasClassID["assaulthelmet"] = 406; +NTIPAliasClassID["ba5"] = 407; NTIPAliasClassID["avengerguard"] = 407; +NTIPAliasClassID["pa1"] = 408; NTIPAliasClassID["targe"] = 408; +NTIPAliasClassID["pa2"] = 409; NTIPAliasClassID["rondache"] = 409; +NTIPAliasClassID["pa3"] = 410; NTIPAliasClassID["heraldicshield"] = 410; +NTIPAliasClassID["pa4"] = 411; NTIPAliasClassID["aerinshield"] = 411; +NTIPAliasClassID["pa5"] = 412; NTIPAliasClassID["crownshield"] = 412; +NTIPAliasClassID["ne1"] = 413; NTIPAliasClassID["preservedhead"] = 413; +NTIPAliasClassID["ne2"] = 414; NTIPAliasClassID["zombiehead"] = 414; +NTIPAliasClassID["ne3"] = 415; NTIPAliasClassID["unravellerhead"] = 415; +NTIPAliasClassID["ne4"] = 416; NTIPAliasClassID["gargoylehead"] = 416; +NTIPAliasClassID["ne5"] = 417; NTIPAliasClassID["demonhead"] = 417; +NTIPAliasClassID["ci0"] = 418; NTIPAliasClassID["circlet"] = 418; +NTIPAliasClassID["ci1"] = 419; NTIPAliasClassID["coronet"] = 419; +NTIPAliasClassID["ci2"] = 420; NTIPAliasClassID["tiara"] = 420; +NTIPAliasClassID["ci3"] = 421; NTIPAliasClassID["diadem"] = 421; +NTIPAliasClassID["uap"] = 422; NTIPAliasClassID["shako"] = 422; +NTIPAliasClassID["ukp"] = 423; NTIPAliasClassID["hydraskull"] = 423; +NTIPAliasClassID["ulm"] = 424; NTIPAliasClassID["armet"] = 424; +NTIPAliasClassID["uhl"] = 425; NTIPAliasClassID["giantconch"] = 425; +NTIPAliasClassID["uhm"] = 426; NTIPAliasClassID["spiredhelm"] = 426; +NTIPAliasClassID["urn"] = 427; NTIPAliasClassID["corona"] = 427; +NTIPAliasClassID["usk"] = 428; NTIPAliasClassID["demonhead"] = 428; +NTIPAliasClassID["uui"] = 429; NTIPAliasClassID["duskshroud"] = 429; +NTIPAliasClassID["uea"] = 430; NTIPAliasClassID["wyrmhide"] = 430; +NTIPAliasClassID["ula"] = 431; NTIPAliasClassID["scarabhusk"] = 431; +NTIPAliasClassID["utu"] = 432; NTIPAliasClassID["wirefleece"] = 432; +NTIPAliasClassID["ung"] = 433; NTIPAliasClassID["diamondmail"] = 433; +NTIPAliasClassID["ucl"] = 434; NTIPAliasClassID["loricatedmail"] = 434; +NTIPAliasClassID["uhn"] = 435; NTIPAliasClassID["boneweave"] = 435; +NTIPAliasClassID["urs"] = 436; NTIPAliasClassID["greathauberk"] = 436; +NTIPAliasClassID["upl"] = 437; NTIPAliasClassID["balrogskin"] = 437; +NTIPAliasClassID["ult"] = 438; NTIPAliasClassID["hellforgeplate"] = 438; +NTIPAliasClassID["uld"] = 439; NTIPAliasClassID["krakenshell"] = 439; +NTIPAliasClassID["uth"] = 440; NTIPAliasClassID["lacqueredplate"] = 440; +NTIPAliasClassID["uul"] = 441; NTIPAliasClassID["shadowplate"] = 441; +NTIPAliasClassID["uar"] = 442; NTIPAliasClassID["sacredarmor"] = 442; +NTIPAliasClassID["utp"] = 443; NTIPAliasClassID["archonplate"] = 443; +NTIPAliasClassID["uuc"] = 444; NTIPAliasClassID["heater"] = 444; +NTIPAliasClassID["uml"] = 445; NTIPAliasClassID["luna"] = 445; +NTIPAliasClassID["urg"] = 446; NTIPAliasClassID["hyperion"] = 446; +NTIPAliasClassID["uit"] = 447; NTIPAliasClassID["monarch"] = 447; +NTIPAliasClassID["uow"] = 448; NTIPAliasClassID["aegis"] = 448; +NTIPAliasClassID["uts"] = 449; NTIPAliasClassID["ward"] = 449; +NTIPAliasClassID["ulg"] = 450; NTIPAliasClassID["bramblemitts"] = 450; +NTIPAliasClassID["uvg"] = 451; NTIPAliasClassID["vampirebonegloves"] = 451; +NTIPAliasClassID["umg"] = 452; NTIPAliasClassID["vambraces"] = 452; +NTIPAliasClassID["utg"] = 453; NTIPAliasClassID["crusadergauntlets"] = 453; +NTIPAliasClassID["uhg"] = 454; NTIPAliasClassID["ogregauntlets"] = 454; +NTIPAliasClassID["ulb"] = 455; NTIPAliasClassID["wyrmhideboots"] = 455; +NTIPAliasClassID["uvb"] = 456; NTIPAliasClassID["scarabshellboots"] = 456; +NTIPAliasClassID["umb"] = 457; NTIPAliasClassID["boneweaveboots"] = 457; +NTIPAliasClassID["utb"] = 458; NTIPAliasClassID["mirroredboots"] = 458; +NTIPAliasClassID["uhb"] = 459; NTIPAliasClassID["myrmidongreaves"] = 459; +NTIPAliasClassID["ulc"] = 460; NTIPAliasClassID["spiderwebsash"] = 460; +NTIPAliasClassID["uvc"] = 461; NTIPAliasClassID["vampirefangbelt"] = 461; +NTIPAliasClassID["umc"] = 462; NTIPAliasClassID["mithrilcoil"] = 462; +NTIPAliasClassID["utc"] = 463; NTIPAliasClassID["trollbelt"] = 463; +NTIPAliasClassID["uhc"] = 464; NTIPAliasClassID["colossusgirdle"] = 464; +NTIPAliasClassID["uh9"] = 465; NTIPAliasClassID["bonevisage"] = 465; +NTIPAliasClassID["ush"] = 466; NTIPAliasClassID["trollnest"] = 466; +NTIPAliasClassID["upk"] = 467; NTIPAliasClassID["bladebarrier"] = 467; +NTIPAliasClassID["dr6"] = 468; NTIPAliasClassID["alphahelm"] = 468; +NTIPAliasClassID["dr7"] = 469; NTIPAliasClassID["griffonheaddress"] = 469; +NTIPAliasClassID["dr8"] = 470; NTIPAliasClassID["hunter'sguise"] = 470; +NTIPAliasClassID["dr9"] = 471; NTIPAliasClassID["sacredfeathers"] = 471; +NTIPAliasClassID["dra"] = 472; NTIPAliasClassID["totemicmask"] = 472; +NTIPAliasClassID["ba6"] = 473; NTIPAliasClassID["jawbonevisor"] = 473; +NTIPAliasClassID["ba7"] = 474; NTIPAliasClassID["lionhelm"] = 474; +NTIPAliasClassID["ba8"] = 475; NTIPAliasClassID["ragemask"] = 475; +NTIPAliasClassID["ba9"] = 476; NTIPAliasClassID["savagehelmet"] = 476; +NTIPAliasClassID["baa"] = 477; NTIPAliasClassID["slayerguard"] = 477; +NTIPAliasClassID["pa6"] = 478; NTIPAliasClassID["akarantarge"] = 478; +NTIPAliasClassID["pa7"] = 479; NTIPAliasClassID["akaranrondache"] = 479; +NTIPAliasClassID["pa8"] = 480; NTIPAliasClassID["protectorshield"] = 480; +NTIPAliasClassID["pa9"] = 481; NTIPAliasClassID["gildedshield"] = 481; +NTIPAliasClassID["paa"] = 482; NTIPAliasClassID["royalshield"] = 482; +NTIPAliasClassID["ne6"] = 483; NTIPAliasClassID["mummifiedtrophy"] = 483; +NTIPAliasClassID["ne7"] = 484; NTIPAliasClassID["fetishtrophy"] = 484; +NTIPAliasClassID["ne8"] = 485; NTIPAliasClassID["sextontrophy"] = 485; +NTIPAliasClassID["ne9"] = 486; NTIPAliasClassID["cantortrophy"] = 486; +NTIPAliasClassID["nea"] = 487; NTIPAliasClassID["hierophanttrophy"] = 487; +NTIPAliasClassID["drb"] = 488; NTIPAliasClassID["bloodspirit"] = 488; +NTIPAliasClassID["drc"] = 489; NTIPAliasClassID["sunspirit"] = 489; +NTIPAliasClassID["drd"] = 490; NTIPAliasClassID["earthspirit"] = 490; +NTIPAliasClassID["dre"] = 491; NTIPAliasClassID["skyspirit"] = 491; +NTIPAliasClassID["drf"] = 492; NTIPAliasClassID["dreamspirit"] = 492; +NTIPAliasClassID["bab"] = 493; NTIPAliasClassID["carnagehelm"] = 493; +NTIPAliasClassID["bac"] = 494; NTIPAliasClassID["furyvisor"] = 494; +NTIPAliasClassID["bad"] = 495; NTIPAliasClassID["destroyerhelm"] = 495; +NTIPAliasClassID["bae"] = 496; NTIPAliasClassID["conquerorcrown"] = 496; +NTIPAliasClassID["baf"] = 497; NTIPAliasClassID["guardiancrown"] = 497; +NTIPAliasClassID["pab"] = 498; NTIPAliasClassID["sacredtarge"] = 498; +NTIPAliasClassID["pac"] = 499; NTIPAliasClassID["sacredrondache"] = 499; +NTIPAliasClassID["pad"] = 500; NTIPAliasClassID["kurastshield"] = 500; +NTIPAliasClassID["pae"] = 501; NTIPAliasClassID["zakarumshield"] = 501; +NTIPAliasClassID["paf"] = 502; NTIPAliasClassID["vortexshield"] = 502; +NTIPAliasClassID["neb"] = 503; NTIPAliasClassID["minionskull"] = 503; +NTIPAliasClassID["neg"] = 504; NTIPAliasClassID["hellspawnskull"] = 504; +NTIPAliasClassID["ned"] = 505; NTIPAliasClassID["overseerskull"] = 505; +NTIPAliasClassID["nee"] = 506; NTIPAliasClassID["succubusskull"] = 506; +NTIPAliasClassID["nef"] = 507; NTIPAliasClassID["bloodlordskull"] = 507; +NTIPAliasClassID["elx"] = 508; NTIPAliasClassID["elixir"] = 508; +NTIPAliasClassID["hpo"] = 509; +NTIPAliasClassID["mpo"] = 510; +NTIPAliasClassID["hpf"] = 511; +NTIPAliasClassID["mpf"] = 512; +NTIPAliasClassID["vps"] = 513; NTIPAliasClassID["staminapotion"] = 513; +NTIPAliasClassID["yps"] = 514; NTIPAliasClassID["antidotepotion"] = 514; +NTIPAliasClassID["rvs"] = 515; NTIPAliasClassID["rejuvenationpotion"] = 515; +NTIPAliasClassID["rvl"] = 516; NTIPAliasClassID["fullrejuvenationpotion"] = 516; +NTIPAliasClassID["wms"] = 517; NTIPAliasClassID["thawingpotion"] = 517; +NTIPAliasClassID["tbk"] = 518; NTIPAliasClassID["tomeoftownportal"] = 518; +NTIPAliasClassID["ibk"] = 519; NTIPAliasClassID["tomeofidentify"] = 519; +NTIPAliasClassID["amu"] = 520; NTIPAliasClassID["amulet"] = 520; +NTIPAliasClassID["vip"] = 521; NTIPAliasClassID["topofthehoradricstaff"] = 521; +NTIPAliasClassID["rin"] = 522; NTIPAliasClassID["ring"] = 522; +NTIPAliasClassID["gld"] = 523; NTIPAliasClassID["gold"] = 523; +NTIPAliasClassID["bks"] = 524; NTIPAliasClassID["scrollofinifuss"] = 524; +NTIPAliasClassID["bkd"] = 525; NTIPAliasClassID["keytothecairnstones"] = 525; +NTIPAliasClassID["aqv"] = 526; NTIPAliasClassID["arrows"] = 526; +NTIPAliasClassID["tch"] = 527; NTIPAliasClassID["torch"] = 527; +NTIPAliasClassID["cqv"] = 528; NTIPAliasClassID["bolts"] = 528; +NTIPAliasClassID["tsc"] = 529; NTIPAliasClassID["scrolloftownportal"] = 529; +NTIPAliasClassID["isc"] = 530; NTIPAliasClassID["scrollofidentify"] = 530; +NTIPAliasClassID["hrt"] = 531; NTIPAliasClassID["heart"] = 531; +NTIPAliasClassID["brz"] = 532; NTIPAliasClassID["brain"] = 532; +NTIPAliasClassID["jaw"] = 533; NTIPAliasClassID["jawbone"] = 533; +NTIPAliasClassID["eyz"] = 534; NTIPAliasClassID["eye"] = 534; +NTIPAliasClassID["hrn"] = 535; NTIPAliasClassID["horn"] = 535; +NTIPAliasClassID["tal"] = 536; NTIPAliasClassID["tail"] = 536; +NTIPAliasClassID["flg"] = 537; NTIPAliasClassID["flag"] = 537; +NTIPAliasClassID["fng"] = 538; NTIPAliasClassID["fang"] = 538; +NTIPAliasClassID["qll"] = 539; NTIPAliasClassID["quill"] = 539; +NTIPAliasClassID["sol"] = 540; NTIPAliasClassID["soul"] = 540; +NTIPAliasClassID["scz"] = 541; NTIPAliasClassID["scalp"] = 541; +NTIPAliasClassID["spe"] = 542; NTIPAliasClassID["spleen"] = 542; +NTIPAliasClassID["key"] = 543; +NTIPAliasClassID["luv"] = 544; NTIPAliasClassID["theblacktowerkey"] = 544; +NTIPAliasClassID["xyz"] = 545; NTIPAliasClassID["potionoflife"] = 545; +NTIPAliasClassID["j34"] = 546; NTIPAliasClassID["ajadefigurine"] = 546; +NTIPAliasClassID["g34"] = 547; NTIPAliasClassID["thegoldenbird"] = 547; +NTIPAliasClassID["bbb"] = 548; NTIPAliasClassID["lamesen'stome"] = 548; +NTIPAliasClassID["box"] = 549; NTIPAliasClassID["horadriccube"] = 549; +NTIPAliasClassID["tr1"] = 550; NTIPAliasClassID["horadricscroll"] = 550; +NTIPAliasClassID["mss"] = 551; NTIPAliasClassID["mephisto'ssoulstone"] = 551; +NTIPAliasClassID["ass"] = 552; NTIPAliasClassID["bookofskill"] = 552; +NTIPAliasClassID["qey"] = 553; NTIPAliasClassID["khalim'seye"] = 553; +NTIPAliasClassID["qhr"] = 554; NTIPAliasClassID["khalim'sheart"] = 554; +NTIPAliasClassID["qbr"] = 555; NTIPAliasClassID["khalim'sbrain"] = 555; +NTIPAliasClassID["ear"] = 556; +NTIPAliasClassID["gcv"] = 557; NTIPAliasClassID["chippedamethyst"] = 557; +NTIPAliasClassID["gfv"] = 558; NTIPAliasClassID["flawedamethyst"] = 558; +NTIPAliasClassID["gsv"] = 559; NTIPAliasClassID["amethyst"] = 559; +NTIPAliasClassID["gzv"] = 560; NTIPAliasClassID["flawlessamethyst"] = 560; +NTIPAliasClassID["gpv"] = 561; NTIPAliasClassID["perfectamethyst"] = 561; +NTIPAliasClassID["gcy"] = 562; NTIPAliasClassID["chippedtopaz"] = 562; +NTIPAliasClassID["gfy"] = 563; NTIPAliasClassID["flawedtopaz"] = 563; +NTIPAliasClassID["gsy"] = 564; NTIPAliasClassID["topaz"] = 564; +NTIPAliasClassID["gly"] = 565; NTIPAliasClassID["flawlesstopaz"] = 565; +NTIPAliasClassID["gpy"] = 566; NTIPAliasClassID["perfecttopaz"] = 566; +NTIPAliasClassID["gcb"] = 567; NTIPAliasClassID["chippedsapphire"] = 567; +NTIPAliasClassID["gfb"] = 568; NTIPAliasClassID["flawedsapphire"] = 568; +NTIPAliasClassID["gsb"] = 569; NTIPAliasClassID["sapphire"] = 569; +NTIPAliasClassID["glb"] = 570; NTIPAliasClassID["flawlesssapphire"] = 570; +NTIPAliasClassID["gpb"] = 571; NTIPAliasClassID["perfectsapphire"] = 571; +NTIPAliasClassID["gcg"] = 572; NTIPAliasClassID["chippedemerald"] = 572; +NTIPAliasClassID["gfg"] = 573; NTIPAliasClassID["flawedemerald"] = 573; +NTIPAliasClassID["gsg"] = 574; NTIPAliasClassID["emerald"] = 574; +NTIPAliasClassID["glg"] = 575; NTIPAliasClassID["flawlessemerald"] = 575; +NTIPAliasClassID["gpg"] = 576; NTIPAliasClassID["perfectemerald"] = 576; +NTIPAliasClassID["gcr"] = 577; NTIPAliasClassID["chippedruby"] = 577; +NTIPAliasClassID["gfr"] = 578; NTIPAliasClassID["flawedruby"] = 578; +NTIPAliasClassID["gsr"] = 579; NTIPAliasClassID["ruby"] = 579; +NTIPAliasClassID["glr"] = 580; NTIPAliasClassID["flawlessruby"] = 580; +NTIPAliasClassID["gpr"] = 581; NTIPAliasClassID["perfectruby"] = 581; +NTIPAliasClassID["gcw"] = 582; NTIPAliasClassID["chippeddiamond"] = 582; +NTIPAliasClassID["gfw"] = 583; NTIPAliasClassID["flaweddiamond"] = 583; +NTIPAliasClassID["gsw"] = 584; NTIPAliasClassID["diamond"] = 584; +NTIPAliasClassID["glw"] = 585; NTIPAliasClassID["flawlessdiamond"] = 585; +NTIPAliasClassID["gpw"] = 586; NTIPAliasClassID["perfectdiamond"] = 586; +NTIPAliasClassID["hp1"] = 587; NTIPAliasClassID["minorhealingpotion"] = 587; +NTIPAliasClassID["hp2"] = 588; NTIPAliasClassID["lighthealingpotion"] = 588; +NTIPAliasClassID["hp3"] = 589; NTIPAliasClassID["healingpotion"] = 589; +NTIPAliasClassID["hp4"] = 590; NTIPAliasClassID["greaterhealingpotion"] = 590; +NTIPAliasClassID["hp5"] = 591; NTIPAliasClassID["superhealingpotion"] = 591; +NTIPAliasClassID["mp1"] = 592; NTIPAliasClassID["minormanapotion"] = 592; +NTIPAliasClassID["mp2"] = 593; NTIPAliasClassID["lightmanapotion"] = 593; +NTIPAliasClassID["mp3"] = 594; NTIPAliasClassID["manapotion"] = 594; +NTIPAliasClassID["mp4"] = 595; NTIPAliasClassID["greatermanapotion"] = 595; +NTIPAliasClassID["mp5"] = 596; NTIPAliasClassID["supermanapotion"] = 596; +NTIPAliasClassID["skc"] = 597; NTIPAliasClassID["chippedskull"] = 597; +NTIPAliasClassID["skf"] = 598; NTIPAliasClassID["flawedskull"] = 598; +NTIPAliasClassID["sku"] = 599; NTIPAliasClassID["skull"] = 599; +NTIPAliasClassID["skl"] = 600; NTIPAliasClassID["flawlessskull"] = 600; +NTIPAliasClassID["skz"] = 601; NTIPAliasClassID["perfectskull"] = 601; +NTIPAliasClassID["hrb"] = 602; NTIPAliasClassID["herb"] = 602; +NTIPAliasClassID["cm1"] = 603; NTIPAliasClassID["smallcharm"] = 603; +NTIPAliasClassID["cm2"] = 604; NTIPAliasClassID["largecharm"] = 604; +NTIPAliasClassID["cm3"] = 605; NTIPAliasClassID["grandcharm"] = 605; +NTIPAliasClassID["rps"] = 606; +NTIPAliasClassID["rpl"] = 607; +NTIPAliasClassID["bps"] = 608; +NTIPAliasClassID["bpl"] = 609; +NTIPAliasClassID["r01"] = 610; NTIPAliasClassID["elrune"] = 610; +NTIPAliasClassID["r02"] = 611; NTIPAliasClassID["eldrune"] = 611; +NTIPAliasClassID["r03"] = 612; NTIPAliasClassID["tirrune"] = 612; +NTIPAliasClassID["r04"] = 613; NTIPAliasClassID["nefrune"] = 613; +NTIPAliasClassID["r05"] = 614; NTIPAliasClassID["ethrune"] = 614; +NTIPAliasClassID["r06"] = 615; NTIPAliasClassID["ithrune"] = 615; +NTIPAliasClassID["r07"] = 616; NTIPAliasClassID["talrune"] = 616; +NTIPAliasClassID["r08"] = 617; NTIPAliasClassID["ralrune"] = 617; +NTIPAliasClassID["r09"] = 618; NTIPAliasClassID["ortrune"] = 618; +NTIPAliasClassID["r10"] = 619; NTIPAliasClassID["thulrune"] = 619; +NTIPAliasClassID["r11"] = 620; NTIPAliasClassID["amnrune"] = 620; +NTIPAliasClassID["r12"] = 621; NTIPAliasClassID["solrune"] = 621; +NTIPAliasClassID["r13"] = 622; NTIPAliasClassID["shaelrune"] = 622; +NTIPAliasClassID["r14"] = 623; NTIPAliasClassID["dolrune"] = 623; +NTIPAliasClassID["r15"] = 624; NTIPAliasClassID["helrune"] = 624; +NTIPAliasClassID["r16"] = 625; NTIPAliasClassID["iorune"] = 625; +NTIPAliasClassID["r17"] = 626; NTIPAliasClassID["lumrune"] = 626; +NTIPAliasClassID["r18"] = 627; NTIPAliasClassID["korune"] = 627; +NTIPAliasClassID["r19"] = 628; NTIPAliasClassID["falrune"] = 628; +NTIPAliasClassID["r20"] = 629; NTIPAliasClassID["lemrune"] = 629; +NTIPAliasClassID["r21"] = 630; NTIPAliasClassID["pulrune"] = 630; +NTIPAliasClassID["r22"] = 631; NTIPAliasClassID["umrune"] = 631; +NTIPAliasClassID["r23"] = 632; NTIPAliasClassID["malrune"] = 632; +NTIPAliasClassID["r24"] = 633; NTIPAliasClassID["istrune"] = 633; +NTIPAliasClassID["r25"] = 634; NTIPAliasClassID["gulrune"] = 634; +NTIPAliasClassID["r26"] = 635; NTIPAliasClassID["vexrune"] = 635; +NTIPAliasClassID["r27"] = 636; NTIPAliasClassID["ohmrune"] = 636; +NTIPAliasClassID["r28"] = 637; NTIPAliasClassID["lorune"] = 637; +NTIPAliasClassID["r29"] = 638; NTIPAliasClassID["surrune"] = 638; +NTIPAliasClassID["r30"] = 639; NTIPAliasClassID["berrune"] = 639; +NTIPAliasClassID["r31"] = 640; NTIPAliasClassID["jahrune"] = 640; +NTIPAliasClassID["r32"] = 641; NTIPAliasClassID["chamrune"] = 641; +NTIPAliasClassID["r33"] = 642; NTIPAliasClassID["zodrune"] = 642; +NTIPAliasClassID["jew"] = 643; NTIPAliasClassID["jewel"] = 643; +NTIPAliasClassID["ice"] = 644; NTIPAliasClassID["malah'spotion"] = 644; +NTIPAliasClassID["0sc"] = 645; NTIPAliasClassID["scrollofknowledge"] = 645; +NTIPAliasClassID["tr2"] = 646; NTIPAliasClassID["scrollofresistance"] = 646; +NTIPAliasClassID["pk1"] = 647; NTIPAliasClassID["keyofterror"] = 647; +NTIPAliasClassID["pk2"] = 648; NTIPAliasClassID["keyofhate"] = 648; +NTIPAliasClassID["pk3"] = 649; NTIPAliasClassID["keyofdestruction"] = 649; +NTIPAliasClassID["dhn"] = 650; NTIPAliasClassID["diablo'shorn"] = 650; +NTIPAliasClassID["bey"] = 651; NTIPAliasClassID["baal'seye"] = 651; +NTIPAliasClassID["mbr"] = 652; NTIPAliasClassID["mephisto'sbrain"] = 652; +NTIPAliasClassID["toa"] = 653; NTIPAliasClassID["tokenofabsolution"] = 653; +NTIPAliasClassID["tes"] = 654; NTIPAliasClassID["twistedessenceofsuffering"] = 654; +NTIPAliasClassID["ceh"] = 655; NTIPAliasClassID["chargedessenceofhatred"] = 655; +NTIPAliasClassID["bet"] = 656; NTIPAliasClassID["burningessenceofterror"] = 656; +NTIPAliasClassID["fed"] = 657; NTIPAliasClassID["festeringessenceofdestruction"] = 657; +NTIPAliasClassID["std"] = 658; NTIPAliasClassID["standardofheroes"] = 658; + +/** @global */ +const NTIPAliasClass = {}; +NTIPAliasClass["normal"] = 0; +NTIPAliasClass["exceptional"] = 1; +NTIPAliasClass["elite"] = 2; + +/** @global */ +const NTIPAliasQuality = {}; +NTIPAliasQuality["lowquality"] = 1; +NTIPAliasQuality["normal"] = 2; +NTIPAliasQuality["superior"] = 3; +NTIPAliasQuality["magic"] = 4; +NTIPAliasQuality["set"] = 5; +NTIPAliasQuality["rare"] = 6; +NTIPAliasQuality["unique"] = 7; +NTIPAliasQuality["crafted"] = 8; + +/** @global */ +const NTIPAliasFlag = {}; +NTIPAliasFlag["identified"] = 0x10; +NTIPAliasFlag["eth"] = 0x400000; NTIPAliasFlag["ethereal"] = 0x400000; +NTIPAliasFlag["runeword"] = 0x4000000; + +// rare item colors +/** @global */ +const NTIPAliasColor = {}; +NTIPAliasColor["black"] = 3; +NTIPAliasColor["white"] = 20; +NTIPAliasColor["orange"] = 19; +NTIPAliasColor["lightyellow"] = 13; +NTIPAliasColor["lightred"] = 7; +NTIPAliasColor["lightgold"] = 15; +NTIPAliasColor["lightblue"] = 4; +NTIPAliasColor["lightpurple"] = 17; +NTIPAliasColor["crystalblue"] = 6; +NTIPAliasColor["crystalred"] = 9; +NTIPAliasColor["crystalgreen"] = 12; +NTIPAliasColor["darkyellow"] = 14; +NTIPAliasColor["darkred"] = 8; +NTIPAliasColor["darkgold"] = 16; +NTIPAliasColor["darkgreen"] = 11; +NTIPAliasColor["darkblue"] = 5; + +/** @global */ +const NTIPAliasStat = {}; +NTIPAliasStat["strength"] = 0; +NTIPAliasStat["energy"] = 1; +NTIPAliasStat["dexterity"] = 2; +NTIPAliasStat["vitality"] = 3; +NTIPAliasStat["statpts"] = 4; +NTIPAliasStat["newskills"] = 5; +NTIPAliasStat["hitpoints"] = 6; +NTIPAliasStat["maxhp"] = 7; +NTIPAliasStat["mana"] = 8; +NTIPAliasStat["maxmana"] = 9; +NTIPAliasStat["stamina"] = 10; +NTIPAliasStat["maxstamina"] = 11; +NTIPAliasStat["level"] = 12; +NTIPAliasStat["experience"] = 13; +NTIPAliasStat["gold"] = 14; +NTIPAliasStat["goldbank"] = 15; +NTIPAliasStat["itemarmorpercent"] = [16, 0]; NTIPAliasStat["enhanceddefense"] = [16, 0]; +NTIPAliasStat["itemmaxdamagepercent"] = [17, 0]; +NTIPAliasStat["itemmindamagepercent"] = [18, 0]; NTIPAliasStat["enhanceddamage"] = [18, 0]; +NTIPAliasStat["tohit"] = 19; +NTIPAliasStat["toblock"] = 20; +NTIPAliasStat["plusmindamage"] = [21, 1]; +NTIPAliasStat["mindamage"] = 21; +NTIPAliasStat["plusmaxdamage"] = [22, 1]; +NTIPAliasStat["maxdamage"] = 22; +NTIPAliasStat["secondarymindamage"] = 23; +NTIPAliasStat["secondarymaxdamage"] = 24; +NTIPAliasStat["damagepercent"] = 25; +NTIPAliasStat["manarecovery"] = 26; +NTIPAliasStat["manarecoverybonus"] = 27; +NTIPAliasStat["staminarecoverybonus"] = 28; +NTIPAliasStat["lastexp"] = 29; +NTIPAliasStat["nextexp"] = 30; + +NTIPAliasStat["armorclass"] = 31; NTIPAliasStat["defense"] = 31; +NTIPAliasStat["plusdefense"] = [31, 0]; + +NTIPAliasStat["armorclassvsmissile"] = 32; +NTIPAliasStat["armorclassvshth"] = 33; +NTIPAliasStat["normaldamagereduction"] = 34; +NTIPAliasStat["magicdamagereduction"] = 35; +NTIPAliasStat["damageresist"] = 36; +NTIPAliasStat["magicresist"] = 37; +NTIPAliasStat["maxmagicresist"] = 38; +NTIPAliasStat["fireresist"] = 39; +NTIPAliasStat["maxfireresist"] = 40; +NTIPAliasStat["lightresist"] = 41; +NTIPAliasStat["maxlightresist"] = 42; +NTIPAliasStat["coldresist"] = 43; +NTIPAliasStat["maxcoldresist"] = 44; +NTIPAliasStat["poisonresist"] = 45; +NTIPAliasStat["maxpoisonresist"] = 46; +NTIPAliasStat["damageaura"] = 47; +NTIPAliasStat["firemindam"] = 48; +NTIPAliasStat["firemaxdam"] = 49; +NTIPAliasStat["lightmindam"] = 50; +NTIPAliasStat["lightmaxdam"] = 51; +NTIPAliasStat["magicmindam"] = 52; +NTIPAliasStat["magicmaxdam"] = 53; +NTIPAliasStat["coldmindam"] = 54; +NTIPAliasStat["coldmaxdam"] = 55; +NTIPAliasStat["coldlength"] = 56; +NTIPAliasStat["poisondamage"] = [57, 1]; +NTIPAliasStat["poisonmindam"] = 57; +NTIPAliasStat["poisonmaxdam"] = 58; +NTIPAliasStat["poisonlength"] = 59; +NTIPAliasStat["lifedrainmindam"] = 60; NTIPAliasStat["lifeleech"] = 60; +NTIPAliasStat["lifedrainmaxdam"] = 61; +NTIPAliasStat["manadrainmindam"] = 62; NTIPAliasStat["manaleech"] = 62; +NTIPAliasStat["manadrainmaxdam"] = 63; +NTIPAliasStat["stamdrainmindam"] = 64; +NTIPAliasStat["stamdrainmaxdam"] = 65; +NTIPAliasStat["stunlength"] = 66; +NTIPAliasStat["velocitypercent"] = 67; +NTIPAliasStat["attackrate"] = 68; +NTIPAliasStat["otheranimrate"] = 69; +NTIPAliasStat["quantity"] = 70; +NTIPAliasStat["value"] = 71; +NTIPAliasStat["durability"] = 72; +NTIPAliasStat["maxdurability"] = 73; +NTIPAliasStat["hpregen"] = 74; +NTIPAliasStat["itemmaxdurabilitypercent"] = 75; +NTIPAliasStat["itemmaxhppercent"] = 76; +NTIPAliasStat["itemmaxmanapercent"] = 77; +NTIPAliasStat["itemattackertakesdamage"] = 78; +NTIPAliasStat["itemgoldbonus"] = 79; +NTIPAliasStat["itemmagicbonus"] = 80; +NTIPAliasStat["itemknockback"] = 81; +NTIPAliasStat["itemtimeduration"] = 82; + +NTIPAliasStat["itemaddclassskills"] = 83; +NTIPAliasStat["itemaddamazonskills"] = [83, 0]; NTIPAliasStat["amazonskills"] = [83, 0]; +NTIPAliasStat["itemaddsorceressskills"] = [83, 1]; NTIPAliasStat["sorceressskills"] = [83, 1]; +NTIPAliasStat["itemaddnecromancerskills"] = [83, 2]; NTIPAliasStat["necromancerskills"] = [83, 2]; +NTIPAliasStat["itemaddpaladinskills"] = [83, 3]; NTIPAliasStat["paladinskills"] = [83, 3]; +NTIPAliasStat["itemaddbarbarianskills"] = [83, 4]; NTIPAliasStat["barbarianskills"] = [83, 4]; +NTIPAliasStat["itemadddruidskills"] = [83, 5]; NTIPAliasStat["druidskills"] = [83, 5]; +NTIPAliasStat["itemaddassassinskills"] = [83, 6]; NTIPAliasStat["assassinskills"] = [83, 6]; + +NTIPAliasStat["unsentparam1"] = 84; +NTIPAliasStat["itemaddexperience"] = 85; +NTIPAliasStat["itemhealafterkill"] = 86; +NTIPAliasStat["itemreducedprices"] = 87; +NTIPAliasStat["itemdoubleherbduration"] = 88; +NTIPAliasStat["itemlightradius"] = 89; +NTIPAliasStat["itemlightcolor"] = 90; +NTIPAliasStat["itemreqpercent"] = 91; +NTIPAliasStat["itemlevelreq"] = 92; +NTIPAliasStat["itemfasterattackrate"] = 93; NTIPAliasStat["ias"] = 93; +NTIPAliasStat["itemlevelreqpct"] = 94; +NTIPAliasStat["lastblockframe"] = 95; +NTIPAliasStat["itemfastermovevelocity"] = 96; NTIPAliasStat["frw"] = 96; + +// oskill +NTIPAliasStat["itemnonclassskill"] = 97; +// Amazon +NTIPAliasStat["plusskillcriticalstrike"] = [97, 9]; +NTIPAliasStat["plusskillguidedarrow"] = [97, 22]; +// Sorceress +NTIPAliasStat["plusskillteleport"] = [97, 54]; +// Barbarian +NTIPAliasStat["plusskillbattleorders"] = [97, 149]; +NTIPAliasStat["plusskillbattlecommand"] = [97, 155]; +NTIPAliasStat["plusskillbattlecry"] = [97, 146]; +// Druid +NTIPAliasStat["plusskillwerewolf"] = [97, 223]; +NTIPAliasStat["plusskillshapeshifting"] = [97, 224]; NTIPAliasStat["plusskilllycanthropy"] = [97, 224]; +NTIPAliasStat["plusskillsummonspiritwolf"] = [97, 227]; +NTIPAliasStat["plusskillferalrage"] = [97, 232]; + +NTIPAliasStat["state"] = 98; +NTIPAliasStat["itemfastergethitrate"] = 99; NTIPAliasStat["fhr"] = 99; +NTIPAliasStat["monsterplayercount"] = 100; +NTIPAliasStat["skillpoisonoverridelength"] = 101; +NTIPAliasStat["itemfasterblockrate"] = 102; NTIPAliasStat["fbr"] = 102; +NTIPAliasStat["skillbypassundead"] = 103; +NTIPAliasStat["skillbypassdemons"] = 104; +NTIPAliasStat["itemfastercastrate"] = 105; NTIPAliasStat["fcr"] = 105; +NTIPAliasStat["skillbypassbeasts"] = 106; + +NTIPAliasStat["itemsingleskill"] = 107; +// Amazon skills +NTIPAliasStat["skillmagicarrow"] = [107, 6]; +NTIPAliasStat["skillfirearrow"] = [107, 7]; +NTIPAliasStat["skillinnersight"] = [107, 8]; +NTIPAliasStat["skillcriticalstrike"] = [107, 9]; +NTIPAliasStat["skilljab"] = [107, 10]; +NTIPAliasStat["skillcoldarrow"] = [107, 11]; +NTIPAliasStat["skillmultipleshot"] = [107, 12]; +NTIPAliasStat["skilldodge"] = [107, 13]; +NTIPAliasStat["skillpowerstrike"] = [107, 14]; +NTIPAliasStat["skillpoisonjavelin"] = [107, 15]; +NTIPAliasStat["skillexplodingarrow"] = [107, 16]; +NTIPAliasStat["skillslowmissiles"] = [107, 17]; +NTIPAliasStat["skillavoid"] = [107, 18]; +NTIPAliasStat["skillimpale"] = [107, 19]; +NTIPAliasStat["skilllightningbolt"] = [107, 20]; +NTIPAliasStat["skillicearrow"] = [107, 21]; +NTIPAliasStat["skillguidedarrow"] = [107, 22]; +NTIPAliasStat["skillpenetrate"] = [107, 23]; +NTIPAliasStat["skillchargedstrike"] = [107, 24]; +NTIPAliasStat["skillplaguejavelin"] = [107, 25]; +NTIPAliasStat["skillstrafe"] = [107, 26]; +NTIPAliasStat["skillimmolationarrow"] = [107, 27]; +NTIPAliasStat["skilldecoy"] = [107, 28]; +NTIPAliasStat["skillevade"] = [107, 29]; +NTIPAliasStat["skillfend"] = [107, 30]; +NTIPAliasStat["skillfreezingarrow"] = [107, 31]; +NTIPAliasStat["skillvalkyrie"] = [107, 32]; +NTIPAliasStat["skillpierce"] = [107, 33]; +NTIPAliasStat["skilllightningstrike"] = [107, 34]; +NTIPAliasStat["skilllightningfury"] = [107, 35]; +// Sorceress skills +NTIPAliasStat["skillfirebolt"] = [107, 36]; +NTIPAliasStat["skillwarmth"] = [107, 37]; +NTIPAliasStat["skillchargedbolt"] = [107, 38]; +NTIPAliasStat["skillicebolt"] = [107, 39]; +NTIPAliasStat["skillfrozenarmor"] = [107, 40]; +NTIPAliasStat["skillinferno"] = [107, 41]; +NTIPAliasStat["skillstaticfield"] = [107, 42]; +NTIPAliasStat["skilltelekinesis"] = [107, 43]; +NTIPAliasStat["skillfrostnova"] = [107, 44]; +NTIPAliasStat["skilliceblast"] = [107, 45]; +NTIPAliasStat["skillblaze"] = [107, 46]; +NTIPAliasStat["skillfireball"] = [107, 47]; +NTIPAliasStat["skillnova"] = [107, 48]; +NTIPAliasStat["skilllightning"] = [107, 49]; +NTIPAliasStat["skillshiverarmor"] = [107, 50]; +NTIPAliasStat["skillfirewall"] = [107, 51]; +NTIPAliasStat["skillenchant"] = [107, 52]; +NTIPAliasStat["skillchainlightning"] = [107, 53]; +NTIPAliasStat["skillteleport"] = [107, 54]; +NTIPAliasStat["skillglacialspike"] = [107, 55]; +NTIPAliasStat["skillmeteor"] = [107, 56]; +NTIPAliasStat["skillthunderstorm"] = [107, 57]; +NTIPAliasStat["skillenergyshield"] = [107, 58]; +NTIPAliasStat["skillblizzard"] = [107, 59]; +NTIPAliasStat["skillchillingarmor"] = [107, 60]; +NTIPAliasStat["skillfiremastery"] = [107, 61]; +NTIPAliasStat["skillhydra"] = [107, 62]; +NTIPAliasStat["skilllightningmastery"] = [107, 63]; +NTIPAliasStat["skillfrozenorb"] = [107, 64]; +NTIPAliasStat["skillcoldmastery"] = [107, 65]; +// Necromancer skills +NTIPAliasStat["skillamplifydamage"] = [107, 66]; +NTIPAliasStat["skillteeth"] = [107, 67]; +NTIPAliasStat["skillbonearmor"] = [107, 68]; +NTIPAliasStat["skillskeletonmastery"] = [107, 69]; +NTIPAliasStat["skillraiseskeleton"] = [107, 70]; +NTIPAliasStat["skilldimvision"] = [107, 71]; +NTIPAliasStat["skillweaken"] = [107, 72]; +NTIPAliasStat["skillpoisondagger"] = [107, 73]; +NTIPAliasStat["skillcorpseexplosion"] = [107, 74]; +NTIPAliasStat["skillclaygolem"] = [107, 75]; +NTIPAliasStat["skillironmaiden"] = [107, 76]; +NTIPAliasStat["skillterror"] = [107, 77]; +NTIPAliasStat["skillbonewall"] = [107, 78]; +NTIPAliasStat["skillgolemmastery"] = [107, 79]; +NTIPAliasStat["skillskeletalmage"] = [107, 80]; +NTIPAliasStat["skillconfuse"] = [107, 81]; +NTIPAliasStat["skilllifetap"] = [107, 82]; +NTIPAliasStat["skillpoisonexplosion"] = [107, 83]; +NTIPAliasStat["skillbonespear"] = [107, 84]; +NTIPAliasStat["skillbloodgolem"] = [107, 85]; +NTIPAliasStat["skillattract"] = [107, 86]; +NTIPAliasStat["skilldecrepify"] = [107, 87]; +NTIPAliasStat["skillboneprison"] = [107, 88]; +NTIPAliasStat["skillsummonresist"] = [107, 89]; +NTIPAliasStat["skillirongolem"] = [107, 90]; +NTIPAliasStat["skilllowerresist"] = [107, 91]; +NTIPAliasStat["skillpoisonnova"] = [107, 92]; +NTIPAliasStat["skillbonespirit"] = [107, 93]; +NTIPAliasStat["skillfiregolem"] = [107, 94]; +NTIPAliasStat["skillrevive"] = [107, 95]; +// Paladin skills +NTIPAliasStat["skillsacrifice"] = [107, 96]; +NTIPAliasStat["skillsmite"] = [107, 97]; +NTIPAliasStat["skillmight"] = [107, 98]; +NTIPAliasStat["skillprayer"] = [107, 99]; +NTIPAliasStat["skillresistfire"] = [107, 100]; +NTIPAliasStat["skillholybolt"] = [107, 101]; +NTIPAliasStat["skillholyfire"] = [107, 102]; +NTIPAliasStat["skillthorns"] = [107, 103]; +NTIPAliasStat["skilldefiance"] = [107, 104]; +NTIPAliasStat["skillresistcold"] = [107, 105]; +NTIPAliasStat["skillzeal"] = [107, 106]; +NTIPAliasStat["skillcharge"] = [107, 107]; +NTIPAliasStat["skillblessedaim"] = [107, 108]; +NTIPAliasStat["skillcleansing"] = [107, 109]; +NTIPAliasStat["skillresistlightning"] = [107, 110]; +NTIPAliasStat["skillvengeance"] = [107, 111]; +NTIPAliasStat["skillblessedhammer"] = [107, 112]; +NTIPAliasStat["skillconcentration"] = [107, 113]; +NTIPAliasStat["skillholyfreeze"] = [107, 114]; +NTIPAliasStat["skillvigor"] = [107, 115]; +NTIPAliasStat["skillconversion"] = [107, 116]; +NTIPAliasStat["skillholyshield"] = [107, 117]; +NTIPAliasStat["skillholyshock"] = [107, 118]; +NTIPAliasStat["skillsanctuary"] = [107, 119]; +NTIPAliasStat["skillmeditation"] = [107, 120]; +NTIPAliasStat["skillfistoftheheavens"] = [107, 121]; +NTIPAliasStat["skillfanaticism"] = [107, 122]; +NTIPAliasStat["skillconviction"] = [107, 123]; +NTIPAliasStat["skillredemption"] = [107, 124]; +NTIPAliasStat["skillsalvation"] = [107, 125]; +// Barbarian skills +NTIPAliasStat["skillbash"] = [107, 126]; +NTIPAliasStat["skillswordmastery"] = [107, 127]; +NTIPAliasStat["skillaxemastery"] = [107, 128]; +NTIPAliasStat["skillmacemastery"] = [107, 129]; +NTIPAliasStat["skillhowl"] = [107, 130]; +NTIPAliasStat["skillfindpotion"] = [107, 131]; +NTIPAliasStat["skillleap"] = [107, 132]; +NTIPAliasStat["skilldoubleswing"] = [107, 133]; +NTIPAliasStat["skillpolearmmastery"] = [107, 134]; +NTIPAliasStat["skillthrowingmastery"] = [107, 135]; +NTIPAliasStat["skillspearmastery"] = [107, 136]; +NTIPAliasStat["skilltaunt"] = [107, 137]; +NTIPAliasStat["skillshout"] = [107, 138]; +NTIPAliasStat["skillstun"] = [107, 139]; +NTIPAliasStat["skilldoublethrow"] = [107, 140]; +NTIPAliasStat["skillincreasedstamina"] = [107, 141]; +NTIPAliasStat["skillfinditem"] = [107, 142]; +NTIPAliasStat["skillleapattack"] = [107, 143]; +NTIPAliasStat["skillconcentrate"] = [107, 144]; +NTIPAliasStat["skillironskin"] = [107, 145]; +NTIPAliasStat["skillbattlecry"] = [107, 146]; +NTIPAliasStat["skillfrenzy"] = [107, 147]; +NTIPAliasStat["skillincreasedspeed"] = [107, 148]; +NTIPAliasStat["skillbattleorders"] = [107, 149]; +NTIPAliasStat["skillgrimward"] = [107, 150]; +NTIPAliasStat["skillwhirlwind"] = [107, 151]; +NTIPAliasStat["skillberserk"] = [107, 152]; +NTIPAliasStat["skillnaturalresistance"] = [107, 153]; +NTIPAliasStat["skillwarcry"] = [107, 154]; +NTIPAliasStat["skillbattlecommand"] = [107, 155]; +// Druid skills +NTIPAliasStat["skillraven"] = [107, 221]; +NTIPAliasStat["skillpoisoncreeper"] = [107, 222]; +NTIPAliasStat["skillwerewolf"] = [107, 223]; +NTIPAliasStat["skilllycanthropy"] = [107, 224]; +NTIPAliasStat["skillfirestorm"] = [107, 225]; +NTIPAliasStat["skilloaksage"] = [107, 226]; +NTIPAliasStat["skillsummonspiritwolf"] = [107, 227]; +NTIPAliasStat["skillwerebear"] = [107, 228]; +NTIPAliasStat["skillmoltenboulder"] = [107, 229]; +NTIPAliasStat["skillarcticblast"] = [107, 230]; +NTIPAliasStat["skillcarrionvine"] = [107, 231]; +NTIPAliasStat["skillferalrage"] = [107, 232]; +NTIPAliasStat["skillmaul"] = [107, 233]; +NTIPAliasStat["skillfissure"] = [107, 234]; +NTIPAliasStat["skillcyclonearmor"] = [107, 235]; +NTIPAliasStat["skillheartofwolverine"] = [107, 236]; +NTIPAliasStat["skillsummondirewolf"] = [107, 237]; +NTIPAliasStat["skillrabies"] = [107, 238]; +NTIPAliasStat["skillfireclaws"] = [107, 239]; +NTIPAliasStat["skilltwister"] = [107, 240]; +NTIPAliasStat["skillsolarcreeper"] = [107, 241]; +NTIPAliasStat["skillhunger"] = [107, 242]; +NTIPAliasStat["skillshockwave"] = [107, 243]; +NTIPAliasStat["skillvolcano"] = [107, 244]; +NTIPAliasStat["skilltornado"] = [107, 245]; +NTIPAliasStat["skillspiritofbarbs"] = [107, 246]; +NTIPAliasStat["skillsummongrizzly"] = [107, 247]; +NTIPAliasStat["skillfury"] = [107, 248]; +NTIPAliasStat["skillarmageddon"] = [107, 249]; +NTIPAliasStat["skillhurricane"] = [107, 250]; +// Assassin skills +NTIPAliasStat["skillfireblast"] = [107, 251]; +NTIPAliasStat["skillclawmastery"] = [107, 252]; +NTIPAliasStat["skillpsychichammer"] = [107, 253]; +NTIPAliasStat["skilltigerstrike"] = [107, 254]; +NTIPAliasStat["skilldragontalon"] = [107, 255]; +NTIPAliasStat["skillshockweb"] = [107, 256]; +NTIPAliasStat["skillbladesentinel"] = [107, 257]; +NTIPAliasStat["skillburstofspeed"] = [107, 258]; +NTIPAliasStat["skillfistsoffire"] = [107, 259]; +NTIPAliasStat["skilldragonclaw"] = [107, 260]; +NTIPAliasStat["skillchargedboltsentry"] = [107, 261]; +NTIPAliasStat["skillwakeoffire"] = [107, 262]; +NTIPAliasStat["skillweaponblock"] = [107, 263]; +NTIPAliasStat["skillcloakofshadows"] = [107, 264]; +NTIPAliasStat["skillcobrastrike"] = [107, 265]; +NTIPAliasStat["skillbladefury"] = [107, 266]; +NTIPAliasStat["skillfade"] = [107, 267]; +NTIPAliasStat["skillshadowwarrior"] = [107, 268]; +NTIPAliasStat["skillclawsofthunder"] = [107, 269]; +NTIPAliasStat["skilldragontail"] = [107, 270]; +NTIPAliasStat["skilllightningsentry"] = [107, 271]; +NTIPAliasStat["skillwakeofinferno"] = [107, 272]; +NTIPAliasStat["skillmindblast"] = [107, 273]; +NTIPAliasStat["skillbladesofice"] = [107, 274]; +NTIPAliasStat["skilldragonflight"] = [107, 275]; +NTIPAliasStat["skilldeathsentry"] = [107, 276]; +NTIPAliasStat["skillbladeshield"] = [107, 277]; +NTIPAliasStat["skillvenom"] = [107, 278]; +NTIPAliasStat["skillshadowmaster"] = [107, 279]; +NTIPAliasStat["skillphoenixstrike"] = [107, 280]; + +NTIPAliasStat["itemrestinpeace"] = 108; +NTIPAliasStat["curseresistance"] = 109; +NTIPAliasStat["itempoisonlengthresist"] = 110; +NTIPAliasStat["itemnormaldamage"] = 111; +NTIPAliasStat["itemhowl"] = 112; +NTIPAliasStat["itemstupidity"] = 113; +NTIPAliasStat["itemdamagetomana"] = 114; +NTIPAliasStat["itemignoretargetac"] = 115; +NTIPAliasStat["itemfractionaltargetac"] = 116; +NTIPAliasStat["itempreventheal"] = 117; +NTIPAliasStat["itemhalffreezeduration"] = 118; +NTIPAliasStat["itemtohitpercent"] = 119; +NTIPAliasStat["itemdamagetargetac"] = 120; +NTIPAliasStat["itemdemondamagepercent"] = 121; +NTIPAliasStat["itemundeaddamagepercent"] = 122; +NTIPAliasStat["itemdemontohit"] = 123; +NTIPAliasStat["itemundeadtohit"] = 124; +NTIPAliasStat["itemthrowable"] = 125; +NTIPAliasStat["itemelemskill"] = 126; +NTIPAliasStat["itemallskills"] = 127; +NTIPAliasStat["itemattackertakeslightdamage"] = 128; +NTIPAliasStat["ironmaidenlevel"] = 129; +NTIPAliasStat["lifetaplevel"] = 130; +NTIPAliasStat["thornspercent"] = 131; +NTIPAliasStat["bonearmor"] = 132; +NTIPAliasStat["bonearmormax"] = 133; +NTIPAliasStat["itemfreeze"] = 134; +NTIPAliasStat["itemopenwounds"] = 135; +NTIPAliasStat["itemcrushingblow"] = 136; +NTIPAliasStat["itemkickdamage"] = 137; +NTIPAliasStat["itemmanaafterkill"] = 138; +NTIPAliasStat["itemhealafterdemonkill"] = 139; +NTIPAliasStat["itemextrablood"] = 140; +NTIPAliasStat["itemdeadlystrike"] = 141; +NTIPAliasStat["itemabsorbfirepercent"] = 142; +NTIPAliasStat["itemabsorbfire"] = 143; +NTIPAliasStat["itemabsorblightpercent"] = 144; +NTIPAliasStat["itemabsorblight"] = 145; +NTIPAliasStat["itemabsorbmagicpercent"] = 146; +NTIPAliasStat["itemabsorbmagic"] = 147; +NTIPAliasStat["itemabsorbcoldpercent"] = 148; +NTIPAliasStat["itemabsorbcold"] = 149; +NTIPAliasStat["itemslow"] = 150; + +NTIPAliasStat["itemaura"] = 151; +NTIPAliasStat["mightaura"] = [151, 98]; +NTIPAliasStat["holyfireaura"] = [151, 102]; +NTIPAliasStat["thornsaura"] = [151, 103]; +NTIPAliasStat["defianceaura"] = [151, 104]; +NTIPAliasStat["concentrationaura"] = [151, 113]; +NTIPAliasStat["holyfreezeaura"] = [151, 114]; +NTIPAliasStat["vigoraura"] = [151, 115]; +NTIPAliasStat["holyshockaura"] = [151, 118]; +NTIPAliasStat["sanctuaryaura"] = [151, 119]; +NTIPAliasStat["meditationaura"] = [151, 120]; +NTIPAliasStat["fanaticismaura"] = [151, 122]; +NTIPAliasStat["convictionaura"] = [151, 123]; +NTIPAliasStat["redemptionaura"] = [151, 124]; + +NTIPAliasStat["itemindestructible"] = 152; +NTIPAliasStat["itemcannotbefrozen"] = 153; +NTIPAliasStat["itemstaminadrainpct"] = 154; +NTIPAliasStat["itemreanimate"] = 155; +NTIPAliasStat["itempierce"] = 156; +NTIPAliasStat["itemmagicarrow"] = 157; +NTIPAliasStat["itemexplosivearrow"] = 158; +NTIPAliasStat["itemthrowmindamage"] = 159; +NTIPAliasStat["itemthrowmaxdamage"] = 160; +NTIPAliasStat["itemskillhandofathena"] = 161; +NTIPAliasStat["itemskillstaminapercent"] = 162; +NTIPAliasStat["itemskillpassivestaminapercent"] = 163; +NTIPAliasStat["itemskillconcentration"] = 164; +NTIPAliasStat["itemskillenchant"] = 165; +NTIPAliasStat["itemskillpierce"] = 166; +NTIPAliasStat["itemskillconviction"] = 167; +NTIPAliasStat["itemskillchillingarmor"] = 168; +NTIPAliasStat["itemskillfrenzy"] = 169; +NTIPAliasStat["itemskilldecrepify"] = 170; +NTIPAliasStat["itemskillarmorpercent"] = 171; +NTIPAliasStat["alignment"] = 172; +NTIPAliasStat["target0"] = 173; +NTIPAliasStat["target1"] = 174; +NTIPAliasStat["goldlost"] = 175; +NTIPAliasStat["conversionlevel"] = 176; +NTIPAliasStat["conversionmaxhp"] = 177; +NTIPAliasStat["unitdooverlay"] = 178; +NTIPAliasStat["attackvsmontype"] = 179; +NTIPAliasStat["damagevsmontype"] = 180; +NTIPAliasStat["fade"] = 181; +NTIPAliasStat["armoroverridepercent"] = 182; +NTIPAliasStat["unused183"] = 183; +NTIPAliasStat["unused184"] = 184; +NTIPAliasStat["unused185"] = 185; +NTIPAliasStat["unused186"] = 186; +NTIPAliasStat["unused187"] = 187; + +NTIPAliasStat["itemaddskilltab"] = 188; +NTIPAliasStat["itemaddbowandcrossbowskilltab"] = [188, 0]; NTIPAliasStat["bowandcrossbowskilltab"] = [188, 0]; +NTIPAliasStat["itemaddpassiveandmagicskilltab"] = [188, 1]; NTIPAliasStat["passiveandmagicskilltab"] = [188, 1]; +NTIPAliasStat["itemaddjavelinandspearskilltab"] = [188, 2]; NTIPAliasStat["javelinandspearskilltab"] = [188, 2]; +NTIPAliasStat["itemaddfireskilltab"] = [188, 8]; NTIPAliasStat["fireskilltab"] = [188, 8]; +NTIPAliasStat["itemaddlightningskilltab"] = [188, 9]; NTIPAliasStat["lightningskilltab"] = [188, 9]; +NTIPAliasStat["itemaddcoldskilltab"] = [188, 10]; NTIPAliasStat["coldskilltab"] = [188, 10]; +NTIPAliasStat["itemaddcursesskilltab"] = [188, 16]; NTIPAliasStat["cursesskilltab"] = [188, 16]; +NTIPAliasStat["itemaddpoisonandboneskilltab"] = [188, 17]; NTIPAliasStat["poisonandboneskilltab"] = [188, 17]; +NTIPAliasStat["itemaddnecromancersummoningskilltab"] = [188, 18]; NTIPAliasStat["necromancersummoningskilltab"] = [188, 18]; +NTIPAliasStat["itemaddpalicombatskilltab"] = [188, 24]; NTIPAliasStat["palicombatskilltab"] = [188, 24]; +NTIPAliasStat["itemaddoffensiveaurasskilltab"] = [188, 25]; NTIPAliasStat["offensiveaurasskilltab"] = [188, 25]; +NTIPAliasStat["itemadddefensiveaurasskilltab"] = [188, 26]; NTIPAliasStat["defensiveaurasskilltab"] = [188, 26]; +NTIPAliasStat["itemaddbarbcombatskilltab"] = [188, 32]; NTIPAliasStat["barbcombatskilltab"] = [188, 32]; +NTIPAliasStat["itemaddmasteriesskilltab"] = [188, 33]; NTIPAliasStat["masteriesskilltab"] = [188, 33]; +NTIPAliasStat["itemaddwarcriesskilltab"] = [188, 34]; NTIPAliasStat["warcriesskilltab"] = [188, 34]; +NTIPAliasStat["itemadddruidsummoningskilltab"] = [188, 40]; NTIPAliasStat["druidsummoningskilltab"] = [188, 40]; +NTIPAliasStat["itemaddshapeshiftingskilltab"] = [188, 41]; NTIPAliasStat["shapeshiftingskilltab"] = [188, 41]; +NTIPAliasStat["itemaddelementalskilltab"] = [188, 42]; NTIPAliasStat["elementalskilltab"] = [188, 42]; +NTIPAliasStat["itemaddtrapsskilltab"] = [188, 48]; NTIPAliasStat["trapsskilltab"] = [188, 48]; +NTIPAliasStat["itemaddshadowdisciplinesskilltab"] = [188, 49]; NTIPAliasStat["shadowdisciplinesskilltab"] = [188, 49]; +NTIPAliasStat["itemaddmartialartsskilltab"] = [188, 50]; NTIPAliasStat["martialartsskilltab"] = [188, 50]; + +NTIPAliasStat["unused189"] = 189; +NTIPAliasStat["unused190"] = 190; +NTIPAliasStat["unused191"] = 191; +NTIPAliasStat["unused192"] = 192; +NTIPAliasStat["unused193"] = 193; +NTIPAliasStat["itemnumsockets"] = 194; NTIPAliasStat["sockets"] = 194; +NTIPAliasStat["itemskillonattack"] = [195, 1]; +NTIPAliasStat["itemskillonattacklevel"] = [195, 2]; +NTIPAliasStat["itemskillonkill"] = [196, 1]; +NTIPAliasStat["itemskillonkilllevel"] = [196, 2]; +NTIPAliasStat["itemskillondeath"] = [197, 1]; +NTIPAliasStat["itemskillondeathlevel"] = [197, 2]; + +NTIPAliasStat["itemskillonhit"] = [198, 1]; +NTIPAliasStat["itemskillonhitlevel"] = [198, 2]; +NTIPAliasStat["amplifydamageonhit"] = [198, 4225]; + +NTIPAliasStat["itemskillonlevelup"] = [199, 1]; +NTIPAliasStat["itemskillonleveluplevel"] = [199, 2]; +NTIPAliasStat["unused200"] = 200; +NTIPAliasStat["itemskillongethit"] = [201, 1]; +NTIPAliasStat["itemskillongethitlevel"] = [201, 2]; +NTIPAliasStat["unused202"] = 202; +NTIPAliasStat["unused203"] = 203; + +NTIPAliasStat["itemchargedskill"] = [204, 1]; +NTIPAliasStat["itemchargedskilllevel"] = [204, 2]; +NTIPAliasStat["teleportcharges"] = [204, 3461]; + +NTIPAliasStat["unused204"] = 205; +NTIPAliasStat["unused205"] = 206; +NTIPAliasStat["unused206"] = 207; +NTIPAliasStat["unused207"] = 208; +NTIPAliasStat["unused208"] = 209; +NTIPAliasStat["unused209"] = 210; +NTIPAliasStat["unused210"] = 211; +NTIPAliasStat["unused211"] = 212; +NTIPAliasStat["unused212"] = 213; +NTIPAliasStat["itemarmorperlevel"] = 214; +NTIPAliasStat["itemarmorpercentperlevel"] = 215; +NTIPAliasStat["itemhpperlevel"] = 216; +NTIPAliasStat["itemmanaperlevel"] = 217; +NTIPAliasStat["itemmaxdamageperlevel"] = 218; +NTIPAliasStat["itemmaxdamagepercentperlevel"] = 219; +NTIPAliasStat["itemstrengthperlevel"] = 220; +NTIPAliasStat["itemdexterityperlevel"] = 221; +NTIPAliasStat["itemenergyperlevel"] = 222; +NTIPAliasStat["itemvitalityperlevel"] = 223; +NTIPAliasStat["itemtohitperlevel"] = 224; +NTIPAliasStat["itemtohitpercentperlevel"] = 225; +NTIPAliasStat["itemcolddamagemaxperlevel"] = 226; +NTIPAliasStat["itemfiredamagemaxperlevel"] = 227; +NTIPAliasStat["itemltngdamagemaxperlevel"] = 228; +NTIPAliasStat["itempoisdamagemaxperlevel"] = 229; +NTIPAliasStat["itemresistcoldperlevel"] = 230; +NTIPAliasStat["itemresistfireperlevel"] = 231; +NTIPAliasStat["itemresistltngperlevel"] = 232; +NTIPAliasStat["itemresistpoisperlevel"] = 233; +NTIPAliasStat["itemabsorbcoldperlevel"] = 234; +NTIPAliasStat["itemabsorbfireperlevel"] = 235; +NTIPAliasStat["itemabsorbltngperlevel"] = 236; +NTIPAliasStat["itemabsorbpoisperlevel"] = 237; +NTIPAliasStat["itemthornsperlevel"] = 238; +NTIPAliasStat["itemfindgoldperlevel"] = 239; +NTIPAliasStat["itemfindmagicperlevel"] = 240; +NTIPAliasStat["itemregenstaminaperlevel"] = 241; +NTIPAliasStat["itemstaminaperlevel"] = 242; +NTIPAliasStat["itemdamagedemonperlevel"] = 243; +NTIPAliasStat["itemdamageundeadperlevel"] = 244; +NTIPAliasStat["itemtohitdemonperlevel"] = 245; +NTIPAliasStat["itemtohitundeadperlevel"] = 246; +NTIPAliasStat["itemcrushingblowperlevel"] = 247; +NTIPAliasStat["itemopenwoundsperlevel"] = 248; +NTIPAliasStat["itemkickdamageperlevel"] = 249; +NTIPAliasStat["itemdeadlystrikeperlevel"] = 250; +NTIPAliasStat["itemfindgemsperlevel"] = 251; +NTIPAliasStat["itemreplenishdurability"] = 252; +NTIPAliasStat["itemreplenishquantity"] = 253; +NTIPAliasStat["itemextrastack"] = 254; +NTIPAliasStat["itemfinditem"] = 255; +NTIPAliasStat["itemslashdamage"] = 256; +NTIPAliasStat["itemslashdamagepercent"] = 257; +NTIPAliasStat["itemcrushdamage"] = 258; +NTIPAliasStat["itemcrushdamagepercent"] = 259; +NTIPAliasStat["itemthrustdamage"] = 260; +NTIPAliasStat["itemthrustdamagepercent"] = 261; +NTIPAliasStat["itemabsorbslash"] = 262; +NTIPAliasStat["itemabsorbcrush"] = 263; +NTIPAliasStat["itemabsorbthrust"] = 264; +NTIPAliasStat["itemabsorbslashpercent"] = 265; +NTIPAliasStat["itemabsorbcrushpercent"] = 266; +NTIPAliasStat["itemabsorbthrustpercent"] = 267; +NTIPAliasStat["itemarmorbytime"] = 268; +NTIPAliasStat["itemarmorpercentbytime"] = 269; +NTIPAliasStat["itemhpbytime"] = 270; +NTIPAliasStat["itemmanabytime"] = 271; +NTIPAliasStat["itemmaxdamagebytime"] = 272; +NTIPAliasStat["itemmaxdamagepercentbytime"] = 273; +NTIPAliasStat["itemstrengthbytime"] = 274; +NTIPAliasStat["itemdexteritybytime"] = 275; +NTIPAliasStat["itemenergybytime"] = 276; +NTIPAliasStat["itemvitalitybytime"] = 277; +NTIPAliasStat["itemtohitbytime"] = 278; +NTIPAliasStat["itemtohitpercentbytime"] = 279; +NTIPAliasStat["itemcolddamagemaxbytime"] = 280; +NTIPAliasStat["itemfiredamagemaxbytime"] = 281; +NTIPAliasStat["itemltngdamagemaxbytime"] = 282; +NTIPAliasStat["itempoisdamagemaxbytime"] = 283; +NTIPAliasStat["itemresistcoldbytime"] = 284; +NTIPAliasStat["itemresistfirebytime"] = 285; +NTIPAliasStat["itemresistltngbytime"] = 286; +NTIPAliasStat["itemresistpoisbytime"] = 287; +NTIPAliasStat["itemabsorbcoldbytime"] = 288; +NTIPAliasStat["itemabsorbfirebytime"] = 289; +NTIPAliasStat["itemabsorbltngbytime"] = 290; +NTIPAliasStat["itemabsorbpoisbytime"] = 291; +NTIPAliasStat["itemfindgoldbytime"] = 292; +NTIPAliasStat["itemfindmagicbytime"] = 293; +NTIPAliasStat["itemregenstaminabytime"] = 294; +NTIPAliasStat["itemstaminabytime"] = 295; +NTIPAliasStat["itemdamagedemonbytime"] = 296; +NTIPAliasStat["itemdamageundeadbytime"] = 297; +NTIPAliasStat["itemtohitdemonbytime"] = 298; +NTIPAliasStat["itemtohitundeadbytime"] = 299; +NTIPAliasStat["itemcrushingblowbytime"] = 300; +NTIPAliasStat["itemopenwoundsbytime"] = 301; +NTIPAliasStat["itemkickdamagebytime"] = 302; +NTIPAliasStat["itemdeadlystrikebytime"] = 303; +NTIPAliasStat["itemfindgemsbytime"] = 304; +NTIPAliasStat["itempiercecold"] = 305; +NTIPAliasStat["itempiercefire"] = 306; +NTIPAliasStat["itempierceltng"] = 307; +NTIPAliasStat["itempiercepois"] = 308; +NTIPAliasStat["itemdamagevsmonster"] = 309; +NTIPAliasStat["itemdamagepercentvsmonster"] = 310; +NTIPAliasStat["itemtohitvsmonster"] = 311; +NTIPAliasStat["itemtohitpercentvsmonster"] = 312; +NTIPAliasStat["itemacvsmonster"] = 313; +NTIPAliasStat["itemacpercentvsmonster"] = 314; +NTIPAliasStat["firelength"] = 315; +NTIPAliasStat["burningmin"] = 316; +NTIPAliasStat["burningmax"] = 317; +NTIPAliasStat["progressivedamage"] = 318; +NTIPAliasStat["progressivesteal"] = 319; +NTIPAliasStat["progressiveother"] = 320; +NTIPAliasStat["progressivefire"] = 321; +NTIPAliasStat["progressivecold"] = 322; +NTIPAliasStat["progressivelightning"] = 323; +NTIPAliasStat["itemextracharges"] = 324; +NTIPAliasStat["progressivetohit"] = 325; +NTIPAliasStat["poisoncount"] = 326; +NTIPAliasStat["damageframerate"] = 327; +NTIPAliasStat["pierceidx"] = 328; +NTIPAliasStat["passivefiremastery"] = 329; +NTIPAliasStat["passiveltngmastery"] = 330; +NTIPAliasStat["passivecoldmastery"] = 331; +NTIPAliasStat["passivepoismastery"] = 332; +NTIPAliasStat["passivefirepierce"] = 333; +NTIPAliasStat["passiveltngpierce"] = 334; +NTIPAliasStat["passivecoldpierce"] = 335; +NTIPAliasStat["passivepoispierce"] = 336; +NTIPAliasStat["passivecriticalstrike"] = 337; +NTIPAliasStat["passivedodge"] = 338; +NTIPAliasStat["passiveavoid"] = 339; +NTIPAliasStat["passiveevade"] = 340; +NTIPAliasStat["passivewarmth"] = 341; +NTIPAliasStat["passivemasterymeleeth"] = 342; +NTIPAliasStat["passivemasterymeleedmg"] = 343; +NTIPAliasStat["passivemasterymeleecrit"] = 344; +NTIPAliasStat["passivemasterythrowth"] = 345; +NTIPAliasStat["passivemasterythrowdmg"] = 346; +NTIPAliasStat["passivemasterythrowcrit"] = 347; +NTIPAliasStat["passiveweaponblock"] = 348; +NTIPAliasStat["passivesummonresist"] = 349; +NTIPAliasStat["modifierlistskill"] = 350; +NTIPAliasStat["modifierlistlevel"] = 351; +NTIPAliasStat["lastsenthppct"] = 352; +NTIPAliasStat["sourceunittype"] = 353; +NTIPAliasStat["sourceunitid"] = 354; +NTIPAliasStat["shortparam1"] = 355; +NTIPAliasStat["questitemdifficulty"] = 356; +NTIPAliasStat["passivemagmastery"] = 357; +NTIPAliasStat["passivemagpierce"] = 358; + + +// Doesnt really exists, but is calculated in getStatEx +NTIPAliasStat["allres"] = 555; diff --git a/d2bs/kolbot/libs/core/Experience.js b/d2bs/kolbot/libs/core/Experience.js new file mode 100644 index 000000000..da44dd646 --- /dev/null +++ b/d2bs/kolbot/libs/core/Experience.js @@ -0,0 +1,138 @@ +/** +* @filename Experience.js +* @author kolton +* @desc Experience library +* +*/ + +const Experience = { + /** + * @todo combine this and nextExp into key-value pairs 1-99 + * Experience[me.charlvl].total and Experience[me.charlvl].next + */ + totalExp: [ + 0, 0, 500, 1500, 3750, 7875, 14175, 22680, 32886, 44396, 57715, 72144, 90180, 112725, + 140906, 176132, 220165, 275207, 344008, 430010, 537513, 671891, 839864, 1049830, 1312287, + 1640359, 2050449, 2563061, 3203826, 3902260, 4663553, 5493363, 6397855, 7383752, 8458379, + 9629723, 10906488, 12298162, 13815086, 15468534, 17270791, 19235252, 21376515, 23710491, + 26254525, 29027522, 32050088, 35344686, 38935798, 42850109, 47116709, 51767302, 56836449, + 62361819, 68384473, 74949165, 82104680, 89904191, 98405658, 107672256, 117772849, 128782495, + 140783010, 153863570, 168121381, 183662396, 200602101, 219066380, 239192444, 261129853, + 285041630, 311105466, 339515048, 370481492, 404234916, 441026148, 481128591, 524840254, + 572485967, 624419793, 681027665, 742730244, 809986056, 883294891, 963201521, 1050299747, + 1145236814, 1248718217, 1361512946, 1484459201, 1618470619, 1764543065, 1923762030, + 2097310703, 2286478756, 2492671933, 2717422497, 2962400612, 3229426756, 3520485254, 0, 0 + ], + nextExp: [ + 0, 500, 1000, 2250, 4125, 6300, 8505, 10206, 11510, 13319, 14429, 18036, 22545, 28181, + 35226, 44033, 55042, 68801, 86002, 107503, 134378, 167973, 209966, 262457, 328072, 410090, + 512612, 640765, 698434, 761293, 829810, 904492, 985897, 1074627, 1171344, 1276765, 1391674, + 1516924, 1653448, 1802257, 1964461, 2141263, 2333976, 2544034, 2772997, 3022566, 3294598, + 3591112, 3914311, 4266600, 4650593, 5069147, 5525370, 6022654, 6564692, 7155515, 7799511, + 8501467, 9266598, 10100593, 11009646, 12000515, 13080560, 14257811, 15541015, 16939705, + 18464279, 20126064, 21937409, 23911777, 26063836, 28409582, 30966444, 33753424, 36791232, + 40102443, 43711663, 47645713, 51933826, 56607872, 61702579, 67255812, 73308835, 79906630, + 87098226, 94937067, 103481403, 112794729, 122946255, 134011418, 146072446, 159218965, 173548673, + 189168053, 206193177, 224750564, 244978115, 267026144, 291058498, 0, 0 + ], + expCurve: [13, 16, 110, 159, 207, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 174, 92, 38, 5], + expPenalty: [1024, 976, 928, 880, 832, 784, 736, 688, 640, 592, 544, 496, 448, 400, 352, 304, 256, 192, 144, 108, 81, 61, 46, 35, 26, 20, 15, 11, 8, 6, 5], + monsterExp: [ + [1, 1, 1], [30, 78, 117], [40, 104, 156], [50, 131, 197], [60, 156, 234], [70, 182, 273], [80, 207, 311], [90, 234, 351], [100, 260, 390], [110, 285, 428], [120, 312, 468], + [130, 338, 507], [140, 363, 545], [154, 401, 602], [169, 440, 660], [186, 482, 723], [205, 533, 800], [225, 584, 876], [248, 644, 966], [273, 708, 1062], [300, 779, 1169], + [330, 857, 1286], [363, 942, 1413], [399, 1035, 1553], [439, 1139, 1709], [470, 1220, 1830], [503, 1305, 1958], [538, 1397, 2096], [576, 1494, 2241], [616, 1598, 2397], + [659, 1709, 2564], [706, 1832, 2748], [755, 1958, 2937], [808, 2097, 3146], [864, 2241, 3362], [925, 2399, 3599], [990, 2568, 3852], [1059, 2745, 4118], [1133, 2939, 4409], + [1212, 3144, 4716], [1297, 3365, 5048], [1388, 3600, 5400], [1485, 3852, 5778], [1589, 4121, 6182], [1693, 4409, 6614], [1797, 4718, 7077], [1901, 5051, 7577], + [2005, 5402, 8103], [2109, 5783, 8675], [2213, 6186, 9279], [2317, 6618, 9927], [2421, 7080, 10620], [2525, 7506, 11259], [2629, 7956, 11934], [2733, 8435, 12653], + [2837, 8942, 13413], [2941, 9477, 14216], [3045, 10044, 15066], [3149, 10647, 15971], [3253, 11286, 16929], [3357, 11964, 17946], [3461, 12680, 19020], + [3565, 13442, 20163], [3669, 14249, 21374], [3773, 15104, 22656], [3877, 16010, 24015], [3981, 16916, 25374], [4085, 17822, 26733], [4189, 18728, 28092], + [4293, 19634, 29451], [4397, 20540, 30810], [4501, 21446, 32169], [4605, 22352, 33528], [4709, 23258, 34887], [4813, 24164, 36246], [4917, 25070, 37605], + [5021, 25976, 38964], [5125, 26882, 40323], [5229, 27788, 41682], [5333, 28694, 43041], [5437, 29600, 44400], [5541, 30506, 45759], [5645, 31412, 47118], + [5749, 32318, 48477], [5853, 33224, 49836], [5957, 34130, 51195], [6061, 35036, 52554], [6165, 35942, 53913], [6269, 36848, 55272], [6373, 37754, 56631], + [6477, 38660, 57990], [6581, 39566, 59349], [6685, 40472, 60708], [6789, 41378, 62067], [6893, 42284, 63426], [6997, 43190, 64785], [7101, 44096, 66144], + [7205, 45002, 67503], [7309, 45908, 68862], [7413, 46814, 70221], [7517, 47720, 71580], [7621, 48626, 72939], [7725, 49532, 74298], [7829, 50438, 75657], + [7933, 51344, 77016], [8037, 52250, 78375], [8141, 53156, 79734], [8245, 54062, 81093], [8349, 54968, 82452], [8453, 55874, 83811], [160000, 160000, 160000] + ], + /** + * Percent progress into the current level. Format: xx.xx% + */ + progress: function () { + return me.getStat(sdk.stats.Level) === 99 ? 0 : (((me.getStat(sdk.stats.Experience) - this.totalExp[me.getStat(sdk.stats.Level)]) / this.nextExp[me.getStat(sdk.stats.Level)]) * 100).toFixed(2); + }, + + /** + * Total experience gained in current run + */ + gain: function () { + return (me.getStat(sdk.stats.Experience) - DataFile.getStats().experience); + }, + + /** + * Percent experience gained in current run + */ + gainPercent: function () { + return me.getStat(sdk.stats.Level) === 99 ? 0 : (this.gain() * 100 / this.nextExp[me.getStat(sdk.stats.Level)]).toFixed(6); + }, + + /** + * Runs until next level + */ + runsToLevel: function () { + return Math.round(((100 - this.progress()) / 100) * this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); + }, + + /** + * Total runs needed for next level (not counting current progress) + */ + totalRunsToLevel: function () { + return Math.round(this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); + }, + + /** + * Total time till next level + */ + timeToLevel: function () { + let tTLrawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000)).toString(); + let tTLrawtimeToLevel = this.runsToLevel() * tTLrawSeconds; + let tTLDays = Math.floor(tTLrawtimeToLevel / 86400); + let tTLHours = Math.floor((tTLrawtimeToLevel % 86400) / 3600); + let tTLMinutes = Math.floor(((tTLrawtimeToLevel % 86400) % 3600) / 60); + //let tTLSeconds = ((tTLrawtimeToLevel % 86400) % 3600) % 60; + + //return tDays + "d " + tTLHours + "h " + tTLMinutes + "m " + tTLSeconds + "s"; + //return tTLDays + "d " + tTLHours + "h " + tTLMinutes + "m"; + return (tTLDays ? tTLDays + " d " : "") + (tTLHours ? tTLHours + " h " : "") + (tTLMinutes ? tTLMinutes + " m" : ""); + }, + + /** + * Get Game Time + */ + getGameTime: function () { + let rawMinutes = Math.floor((getTickCount() - me.gamestarttime) / 60000).toString(); + let rawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000) % 60).toString(); + + rawMinutes <= 9 && (rawMinutes = "0" + rawMinutes); + rawSeconds <= 9 && (rawSeconds = "0" + rawSeconds); + + return " (" + rawMinutes + ":" + rawSeconds + ")"; + }, + + /** + * Log to manager + */ + log: function () { + let gain = this.gain(); + let progress = this.progress(); + let runsToLevel = this.runsToLevel(); + let getGameTime = this.getGameTime(); + let string = "[Game: " + me.gamename + (me.gamepassword ? "//" + me.gamepassword : "") + getGameTime + "] [Level: " + me.getStat(sdk.stats.Level) + " (" + progress + "%)] [XP: " + gain + "] [Games ETA: " + runsToLevel + "]"; + + if (gain) { + D2Bot.printToConsole(string, sdk.colors.D2Bot.Blue); + + if (me.getStat(sdk.stats.Level) > DataFile.getStats().level) { + D2Bot.printToConsole("Congrats! You gained a level. Current level:" + me.getStat(sdk.stats.Level), sdk.colors.D2Bot.Green); + } + } + } +}; diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js new file mode 100644 index 000000000..000e2a36d --- /dev/null +++ b/d2bs/kolbot/libs/core/Item.js @@ -0,0 +1,585 @@ +/** +* @filename Item.js +* @author kolton, theBGuy +* @desc handle item and autoequip related things +* +*/ + +// torn on if this should be broken up in two classes Item and AutoEquip, for now leaving as is +const Item = { + qualityToName: function (quality) { + let qualNames = ["", "lowquality", "normal", "superior", "magic", "set", "rare", "unique", "crafted"]; + return qualNames[quality]; + }, + + color: function (unit, type) { + type === undefined && (type = true); + + if (type) { + switch (unit.itemType) { + case sdk.items.type.Gold: + return "ÿc4"; + case sdk.items.type.Rune: + return "ÿc8"; + case sdk.items.type.HealingPotion: + return "ÿc1"; + case sdk.items.type.ManaPotion: + return "ÿc3"; + case sdk.items.type.RejuvPotion: + return "ÿc;"; + } + } + + switch (unit.quality) { + case sdk.items.quality.Magic: + return "ÿc3"; + case sdk.items.quality.Set: + return "ÿc2"; + case sdk.items.quality.Rare: + return "ÿc9"; + case sdk.items.quality.Unique: + return "ÿc4"; + case sdk.items.quality.Crafted: + return "ÿc8"; + } + + return "ÿc0"; + }, + + hasTier: function (item) { + return Config.AutoEquip && NTIP.GetTier(item) > 0; + }, + + canEquip: function (item) { + // Not an item or unid + if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; + // Higher requirements + if (item.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) || item.dexreq > me.getStat(sdk.stats.Dexterity) || item.strreq > me.getStat(sdk.stats.Strength)) return false; + + return true; + }, + + // Equips an item and throws away the old equipped 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.isInStash && !Town.openStash()) return false; + + 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) { + if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { + cursorItem.drop(); + } + } + } + + return true; + } + } + } + + return false; + }, + + getEquippedItem: function (bodyLoc) { + let item = me.getItem(); + + if (item) { + do { + if (item.bodylocation === bodyLoc) { + return { + classid: item.classid, + tier: NTIP.GetTier(item) + }; + } + } while (item.getNext()); + } + + // Don't have anything equipped in there + return { + classid: -1, + tier: -1 + }; + }, + + getBodyLoc: function (item) { + let bodyLoc; + + switch (item.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.AuricShields: + case sdk.items.type.VoodooHeads: + case sdk.items.type.BowQuiver: + case sdk.items.type.CrossbowQuiver: + bodyLoc = sdk.body.LeftArm; + + break; + case sdk.items.type.Armor: + bodyLoc = sdk.body.Armor; + + break; + case sdk.items.type.Ring: + bodyLoc = [sdk.body.RingRight, sdk.body.RingLeft]; + + break; + case sdk.items.type.Amulet: + bodyLoc = sdk.body.Neck; + + break; + case sdk.items.type.Boots: + bodyLoc = sdk.body.Feet; + + break; + case sdk.items.type.Gloves: + bodyLoc = sdk.body.Gloves; + + break; + case sdk.items.type.Belt: + bodyLoc = sdk.body.Belt; + + break; + case sdk.items.type.Helm: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Circlet: + case sdk.items.type.Pelt: + bodyLoc = sdk.body.Head; + + break; + 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: + bodyLoc = me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; + + break; + case sdk.items.type.HandtoHand: + case sdk.items.type.AssassinClaw: + bodyLoc = me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; + + break; + default: + return false; + } + + !Array.isArray(bodyLoc) && (bodyLoc = [bodyLoc]); + + return bodyLoc; + }, + + autoEquipCheck: function (item) { + if (!Config.AutoEquip) return true; + + let tier = NTIP.GetTier(item); + let bodyLoc = this.getBodyLoc(item); + + if (tier > 0 && bodyLoc) { + for (let i = 0; i < bodyLoc.length; i += 1) { + // Low tier items shouldn't be kept if they can't be equipped + if (tier > this.getEquippedItem(bodyLoc[i]).tier && (this.canEquip(item) || !item.getFlag(sdk.items.flags.Identified))) { + return true; + } + } + } + + // Sell/ignore low tier items, keep high tier + if (tier > 0 && tier < 100) return false; + + return true; + }, + + // returns true if the item should be kept+logged, false if not + autoEquip: function () { + if (!Config.AutoEquip) return true; + + let items = me.findItems(-1, sdk.items.mode.inStorage); + + if (!items) return false; + + function sortEq(a, b) { + if (Item.canEquip(a)) return -1; + if (Item.canEquip(b)) return 1; + + return 0; + } + + me.cancel(); + + // Remove items without tier + for (let i = 0; i < items.length; i += 1) { + if (NTIP.GetTier(items[i]) === 0) { + items.splice(i, 1); + + i -= 1; + } + } + + while (items.length > 0) { + items.sort(sortEq); + + let tier = NTIP.GetTier(items[0]); + let bodyLoc = this.getBodyLoc(items[0]); + + if (tier > 0 && bodyLoc) { + for (let j = 0; j < bodyLoc.length; j += 1) { + // khalim's will adjustment + const equippedItem = this.getEquippedItem(bodyLoc[j]); + if (items[0].isInStorage && tier > equippedItem.tier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { + if (!items[0].identified) { + let tome = me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); + + if (tome && tome.getStat(sdk.stats.Quantity) > 0) { + items[0].isInStash && Town.openStash(); + Town.identifyItem(items[0], tome); + } + } + + let gid = items[0].gid; + console.log(items[0].name); + + if (this.equip(items[0], bodyLoc[j])) { + Item.logItem("Equipped", me.getItem(-1, -1, gid)); + } + + break; + } + } + } + + items.shift(); + } + + return true; + }, + + getItemDesc: function (unit, logILvl = true) { + let stringColor = ""; + let desc = unit.description; + + if (!desc) return ""; + desc = desc.split("\n"); + + // Lines are normally in reverse. Add color tags if needed and reverse order. + for (let i = 0; i < desc.length; i += 1) { + // Remove sell value + if (desc[i].includes(getLocaleString(sdk.locale.text.SellValue))) { + desc.splice(i, 1); + + i -= 1; + } else { + // Add color info + if (!desc[i].match(/^(y|ÿ)c/)) { + desc[i] = stringColor + desc[i]; + } + + // Find and store new color info + let index = desc[i].lastIndexOf("ÿc"); + + if (index > -1) { + stringColor = desc[i].substring(index, index + "ÿ".length + 2); + } + } + + desc[i] = desc[i].replace(/(y|ÿ)c([0-9!"+<:;.*])/g, "\\xffc$2"); + } + + if (logILvl && desc[desc.length - 1]) { + desc[desc.length - 1] = desc[desc.length - 1].trim() + " (" + unit.ilvl + ")"; + } + + desc = desc.reverse().join("\n"); + + return desc; + }, + + getItemCode: function (unit) { + if (unit === undefined) return ""; + + let code = (() => { + switch (unit.quality) { + case sdk.items.quality.Set: + switch (unit.classid) { + case sdk.items.Sabre: + return "inv9sbu"; + case sdk.items.ShortWarBow: + return "invswbu"; + case sdk.items.Helm: + return "invhlmu"; + case sdk.items.LargeShield: + return "invlrgu"; + case sdk.items.LongSword: + case sdk.items.CrypticSword: + return "invlsdu"; + case sdk.items.SmallShield: + return "invsmlu"; + case sdk.items.Buckler: + return "invbucu"; + case sdk.items.Cap: + return "invcapu"; + case sdk.items.BroadSword: + return "invbsdu"; + case sdk.items.FullHelm: + return "invfhlu"; + case sdk.items.GothicShield: + return "invgtsu"; + case sdk.items.AncientArmor: + case sdk.items.SacredArmor: + return "invaaru"; + case sdk.items.KiteShield: + return "invkitu"; + case sdk.items.TowerShield: + return "invtowu"; + case sdk.items.FullPlateMail: + return "invfulu"; + case sdk.items.MilitaryPick: + return "invmpiu"; + case sdk.items.JaggedStar: + return "invmstu"; + case sdk.items.ColossusBlade: + return "invgsdu"; + case sdk.items.OrnatePlate: + return "invxaru"; + case sdk.items.Cuirass: + case sdk.items.ReinforcedMace: + case sdk.items.Ward: + case sdk.items.SpiredHelm: + return "inv" + unit.code + "s"; + case sdk.items.GrandCrown: + return "invxrnu"; + case sdk.items.ScissorsSuwayyah: + return "invskru"; + case sdk.items.GrimHelm: + case sdk.items.BoneVisage: + return "invbhmu"; + case sdk.items.ElderStaff: + return "invcstu"; + case sdk.items.RoundShield: + return "invxmlu"; + case sdk.items.BoneWand: + return "invbwnu"; + default: + return ""; + } + case sdk.items.quality.Unique: + for (let i = 0; i < 401; i += 1) { + if (unit.code === getBaseStat("uniqueitems", i, 4).trim() + && unit.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat("uniqueitems", i, 2)))) { + return getBaseStat("uniqueitems", i, "invfile"); + } + } + return ""; + default: + return ""; + } + })(); + + if (!code) { + // Tiara/Diadem + code = ["ci2", "ci3"].includes(unit.code) ? unit.code : (getBaseStat("items", unit.classid, "normcode") || unit.code); + code = code.replace(" ", ""); + [sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(unit.itemType) && (code += (unit.gfx + 1)); + } + + return code; + }, + + getItemSockets: function (unit) { + let code; + let sockets = unit.sockets; + let subItems = unit.getItemsEx(); + let tempArray = []; + + if (subItems.length) { + switch (unit.sizex) { + case 2: + switch (unit.sizey) { + case 3: // 2 x 3 + switch (sockets) { + case 4: + tempArray = [subItems[0], subItems[3], subItems[2], subItems[1]]; + + break; + case 5: + tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; + + break; + case 6: + tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; + + break; + } + + break; + case 4: // 2 x 4 + switch (sockets) { + case 5: + tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; + + break; + case 6: + tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; + + break; + } + + break; + } + + break; + } + + if (tempArray.length === 0 && subItems.length > 0) { + tempArray = subItems.slice(0); + } + } + + for (let i = 0; i < sockets; i += 1) { + if (tempArray[i]) { + code = tempArray[i].code; + + if ([sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(tempArray[i].itemType)) { + code += (tempArray[i].gfx + 1); + } + } else { + code = "gemsocket"; + } + + tempArray[i] = code; + } + + return tempArray; + }, + + useItemLog: true, // Might be a bit dirty + + logger: function (action, unit, text) { + if (!Config.ItemInfo || !this.useItemLog) return false; + + let desc; + let date = new Date(); + let dateString = "[" + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; + + switch (action) { + case "Sold": + if (Config.ItemInfoQuality.indexOf(unit.quality) === -1) { + return false; + } + + desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]/gi, "").trim(); + + break; + case "Kept": + case "Field Kept": + case "Runeword Kept": + case "Cubing Kept": + case "Shopped": + case "Gambled": + case "Dropped": + desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); + + break; + case "No room for": + desc = unit.name; + + break; + default: + desc = unit.fname.split("\n").reverse().join(" ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); + + break; + } + + return FileAction.read("logs/ItemLog.txt", dateString + " <" + me.profile + "> <" + action + "> (" + Item.qualityToName(unit.quality) + ") " + desc + (text ? " {" + text + "}" : "") + "\n"); + }, + + // Log kept item stats in the manager. + logItem: function (action, unit, keptLine) { + if (!this.useItemLog) 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)); + } + + 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.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; + }, + + // skip low items: MuleLogger + skipItem: function (id) { + return [ + sdk.items.HandAxe, sdk.items.Wand, sdk.items.Club, sdk.items.ShortSword, sdk.items.Javelin, sdk.items.ShortStaff, sdk.items.Katar, + sdk.items.Buckler, sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, + sdk.items.ThawingPotion, sdk.items.TomeofTownPortal, sdk.items.TomeofIdentify, sdk.items.ScrollofIdentify, sdk.items.ScrollofTownPortal, + sdk.items.Key, 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 + ].includes(id); + }, +}; diff --git a/d2bs/kolbot/libs/common/Loader.js b/d2bs/kolbot/libs/core/Loader.js similarity index 90% rename from d2bs/kolbot/libs/common/Loader.js rename to d2bs/kolbot/libs/core/Loader.js index e33806972..1101723ee 100644 --- a/d2bs/kolbot/libs/common/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -19,7 +19,7 @@ const Loader = { }, getScripts: function () { - let fileList = dopen("libs/bots/").getFiles(); + let fileList = dopen("libs/scripts/").getFiles(); for (let i = 0; i < fileList.length; i += 1) { if (fileList[i].indexOf(".js") > -1) { @@ -93,15 +93,15 @@ const Loader = { for (let s in Scripts) { if (Scripts.hasOwnProperty(s) && Scripts[s]) { - this.scriptList.push(s); + Loader.scriptList.push(s); } } - for (this.scriptIndex = 0; this.scriptIndex < this.scriptList.length; this.scriptIndex++) { + for (Loader.scriptIndex = 0; Loader.scriptIndex < Loader.scriptList.length; Loader.scriptIndex++) { let script = this.scriptList[this.scriptIndex]; if (this.fileList.indexOf(script) === -1) { - if (FileTools.exists("bots/" + script + ".js")) { + if (FileTools.exists("scripts/" + 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."); @@ -110,12 +110,12 @@ const Loader = { } } - if (!include("bots/" + script + ".js")) { + if (!include("scripts/" + script + ".js")) { Misc.errorReport("Failed to include script: " + script); continue; } - if (isIncluded("bots/" + script + ".js")) { + if (isIncluded("scripts/" + script + ".js")) { try { if (typeof (global[script]) !== "function") { throw new Error("Invalid script function name"); @@ -123,7 +123,7 @@ const Loader = { if (this.skipTown.includes(script) || Town.goToTown()) { print("ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("tools/toolsthread.js", JSON.stringify({currScript: script})); + Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({currScript: script})); reconfiguration = typeof Scripts[script] === "object"; if (reconfiguration) { @@ -185,13 +185,13 @@ const Loader = { this.copy(Config, unmodifiedConfig); - if (!include("bots/" + script + ".js")) { + if (!include("scripts/" + script + ".js")) { Misc.errorReport("Failed to include script: " + script); return false; } - if (isIncluded("bots/" + script + ".js")) { + if (isIncluded("scripts/" + script + ".js")) { try { if (typeof (global[script]) !== "function") { throw new Error("Invalid script function name"); @@ -201,7 +201,7 @@ const Loader = { let mainScriptStr = (mainScript !== script ? buildScriptMsg() : ""); this.tempList.push(script); print(mainScriptStr + "ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("tools/toolsthread.js", JSON.stringify({currScript: script})); + Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({currScript: script})); reconfiguration = typeof Scripts[script] === "object"; diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js new file mode 100644 index 000000000..2fd0028f5 --- /dev/null +++ b/d2bs/kolbot/libs/core/Me.js @@ -0,0 +1,753 @@ +/** +* @filename Me.js +* @author theBGuy +* @desc 'me' prototypes +* +*/ + +// Ensure these are in polyfill.js +!isIncluded("Polyfill.js") && include("Polyfill.js"); + +/** + * @desciption Set me.runwalk to 0 (walk) + * @returns {void} + */ +me.walk = () => me.runwalk = sdk.player.move.Walk; + +/** + * @desciption Set me.runwalk to 1 (run) + * @returns {void} + */ +me.run = () => me.runwalk = sdk.player.move.Run; + +/** + * @description Calling me.ping can bug sometimes so check if game is in ready state. + * - Single-Player returns static ping of 25. + * - Game not ready returns ping of 250 + * - ping < 10 returns 50 + * @returns {number} pingDelay + */ +me.getPingDelay = function () { + // single-player + if (!me.gameserverip) return 25; + let pingDelay = me.gameReady ? me.ping : 250; + pingDelay < 10 && (pingDelay = 50); + return pingDelay; +}; + +/** + * @description Find an item by classid, mode, loc, quality + * @param {number} id + * @param {number} [mode] + * @param {number} [loc] + * @param {number} [quality] + * @returns {ItemUnit | false} + */ +me.findItem = function (id = -1, mode = -1, loc = -1, quality = -1) { + let item = me.getItem(id, mode); + + if (item) { + do { + if ((loc === -1 || item.location === loc) && (quality === -1 || item.quality === quality)) { + return item; + } + } while (item.getNext()); + } + + return false; +}; + +me.findItems = function (id = -1, mode = -1, loc = false) { + let list = []; + let item = me.getItem(id, mode); + + if (item) { + do { + if (!loc || item.location === loc) { + list.push(copyUnit(item)); + } + } while (item.getNext()); + } + + return list; +}; + +me.cancelUIFlags = function () { + while (!me.gameReady) { + delay(25); + } + + const flags = [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, + sdk.uiflags.Waypoint, sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Stash, + sdk.uiflags.Cube, sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.SubmitItem + ]; + + for (let i = 0; i < flags.length; i++) { + if (getUIFlag(flags[i]) && me.cancel()) { + delay(250); + i = 0; // Reset + } + } +}; + +me.switchWeapons = function (slot) { + if (this.gametype === sdk.game.gametype.Classic || (slot !== undefined && this.weaponswitch === slot)) { + return true; + } + + while (typeof me !== "object") { + delay(10); + } + + while (!me.gameReady) { + delay(25); + } + + let originalSlot = this.weaponswitch; + let switched = false; + let packetHandler = (bytes) => bytes.length > 0 && bytes[0] === sdk.packets.recv.WeaponSwitch && (switched = true) && false; // false to not block + addEventListener("gamepacket", packetHandler); + try { + for (let i = 0; i < 10; i += 1) { + for (let j = 10; --j && me.idle;) { + delay(3); + } + + i > 0 && delay(10); + !switched && sendPacket(1, sdk.packets.send.SwapWeapon); // Swap weapons + + let tick = getTickCount(); + while (getTickCount() - tick < 300) { + if (switched || originalSlot !== me.weaponswitch) { + delay(50); + return true; + } + + delay(3); + } + // Retry + } + } finally { + removeEventListener("gamepacket", packetHandler); + } + + return false; +}; + +// Returns the number of frames needed to cast a given skill at a given FCR for a given char. +me.castingFrames = function (skillId, fcr, charClass) { + if (skillId === undefined) return 0; + + fcr === undefined && (fcr = me.FCR); + charClass === undefined && (charClass = this.classid); + + // https://diablo.fandom.com/wiki/Faster_Cast_Rate + let effectiveFCR = Math.min(75, Math.floor(fcr * 120 / (fcr + 120)) | 0); + let isLightning = skillId === sdk.skills.Lightning || skillId === sdk.skills.ChainLightning; + let baseCastRate = [20, isLightning ? 19 : 14, 16, 16, 14, 15, 17][charClass]; + let animationSpeed = { + normal: 256, + human: 208, + wolf: 229, + bear: 228 + }[charClass === sdk.player.class.Druid ? (me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear)) : "normal"]; + return Math.ceil(256 * baseCastRate / Math.floor(animationSpeed * (100 + effectiveFCR) / 100) - (isLightning ? 0 : 1)); +}; + +// Returns the duration in seconds needed to cast a given skill at a given FCR for a given char. +me.castingDuration = function (skillId, fcr = me.FCR, charClass = me.classid) { + return (me.castingFrames(skillId, fcr, charClass) / 25); +}; + +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; +}; + +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) { + const pots = { + hp: [], + mp: [], + }; + 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(p); + } else if (p.itemType === sdk.items.type.ManaPotion) { + pots.mp.push(p); + } + }); + + // quick check + if ((Config.BeltColumn.includes("hp") && !pots.hp.length) + || (Config.BeltColumn.includes("mp") && !pots.mp.length)) { + return true; + } + + // if we have no belt what should qualify is to go to town at this point? + // we've confirmed having at least some potions in the above check + // if (!me.inTown && Storage.BeltSize() === 1) return false; + + // 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; +}; + +/** @returns {ItemUnit | null} */ +me.getTpTool = function () { + const items = me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((item) => item.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(item.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); + return scroll ? scroll : null; +}; + +/** @returns {ItemUnit | null} */ +me.getIdTool = function () { + const items = me.getItemsEx() + .filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); + if (!items.length) return null; + let tome = items.find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify && i.getStat(sdk.stats.Quantity) > 0); + if (tome) return tome; + let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); + return scroll ? scroll : null; +}; + +/** @returns {boolean} */ +me.canTpToTown = function () { + // can't tp if dead + if (me.dead) 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(); +}; + +/** + * @description Check if healing is needed, based on character config + * @returns {boolean} + */ +me.needHealing = function () { + if (me.hpPercent <= Config.HealHP || me.mpPercent <= Config.HealMP) 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((state) => me.getState(state))); +}; + +me.getTome = function (id) { + const tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); + return tome ? tome : null; +}; + +me.getUnids = function () { + let list = []; + let item = me.getItem(-1, sdk.items.mode.inStorage); + + if (!item) return false; + + do { + if (item.isInInventory && !item.identified) { + list.push(copyUnit(item)); + } + } while (item.getNext()); + + return list.length ? list : false; +}; + +// Identify items while in the field if we have a id tome +me.fieldID = function () { + let list = me.getUnids(); + if (!list) return false; + + let tome = me.getTome(sdk.items.TomeofIdentify); + if (!tome || tome.getStat(sdk.stats.Quantity) < list.length) return false; + + while (list.length > 0) { + let item = list.shift(); + let result = Pickit.checkItem(item); + + // unid item that should be identified + if (result.result === Pickit.Result.UNID) { + Town.identifyItem(item, tome, Config.FieldID.PacketID); + delay(me.ping + 1); + result = Pickit.checkItem(item); + + switch (result.result) { + case Pickit.Result.UNWANTED: + Item.logger("Dropped", item, "fieldID"); + + if (Config.DroppedItemsAnnounce.Enable && Config.DroppedItemsAnnounce.Quality.includes(item.quality)) { + say("Dropped: [" + Item.qualityToName(item.quality).capitalize() + "] " + item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim()); + + if (Config.DroppedItemsAnnounce.LogToOOG && Config.DroppedItemsAnnounce.OOGQuality.includes(item.quality)) { + Item.logItem("Field Dropped", item, result.line); + } + } + + item.drop(); + + break; + case Pickit.Result.WANTED: + Item.logger("Field Kept", item); + Item.logItem("Field Kept", item, result.line); + + break; + default: + break; + } + } + } + + delay(200); + me.cancel(); + + return true; +}; + +Object.defineProperties(me, { + maxNearMonsters: { + get: function () { + return Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); + } + }, + inShop: { + get: function () { + if (getUIFlag(sdk.uiflags.Shop)) return true; + if (!Config.PacketShopping) return false; + let npc = getInteractedNPC(); + return !!(npc && npc.itemcount > 0); + } + }, + walking: { + get: function () { + return me.runwalk === sdk.player.move.Walk; + } + }, + running: { + get: function () { + return me.runwalk === sdk.player.move.Run; + } + }, + deadOrInSequence: { + get: function () { + return me.dead || me.mode === sdk.player.mode.SkillActionSequence; + } + }, + moving: { + get: function () { + return [sdk.player.mode.Walking, sdk.player.mode.Running, sdk.player.mode.WalkingInTown].includes(me.mode); + } + }, + highestAct: { + get: function () { + let acts = [true, + me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed), + me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed), + me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed), + me.getQuest(sdk.quest.id.AbleToGotoActV, sdk.quest.states.Completed)]; + let index = acts.findIndex((i) => !i); // find first false, returns between 1 and 5 + return index === -1 ? 5 : index; + } + }, + highestQuestDone: { + get: function () { + for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { + if (me.getQuest(i, sdk.quest.states.Completed)) { + return i; + } + + // check if we've completed main part but not used our reward + if ([sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade].includes(i) && me.getQuest(i, sdk.quest.states.ReqComplete)) { + return i; + } + } + return undefined; + } + }, + staminaPercent: { + get: function () { + return Math.round((me.stamina / me.staminamax) * 100); + } + }, + staminaDrainPerSec: { + get: function () { + let bonusReduction = me.getStat(sdk.stats.StaminaRecoveryBonus); + let armorMalusReduction = 0; // TODO + return 25 * Math.max(40 * (1 + armorMalusReduction / 10) * (100 - bonusReduction) / 100, 1) / 256; + } + }, + staminaTimeLeft: { + get: function () { + return me.stamina / me.staminaDrainPerSec; + } + }, + staminaMaxDuration: { + get: function () { + return me.staminamax / me.staminaDrainPerSec; + } + }, + FCR: { + get: function () { + return me.getStat(sdk.stats.FCR) - (!!Config ? Config.FCR : 0); + } + }, + FHR: { + get: function () { + return me.getStat(sdk.stats.FHR) - (!!Config ? Config.FHR : 0); + } + }, + FBR: { + get: function () { + return me.getStat(sdk.stats.FBR) - (!!Config ? Config.FBR : 0); + } + }, + IAS: { + get: function () { + return me.getStat(sdk.stats.IAS) - (!!Config ? Config.IAS : 0); + } + }, + shapeshifted: { + get: function () { + return me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear) || me.getState(sdk.states.Delerium); + } + }, + mpPercent: { + get: function () { + return Math.round(me.mp * 100 / me.mpmax); + } + }, + skillDelay: { + get: function () { + return me.getState(sdk.states.SkillDelay); + } + }, + classic: { + get: function () { + return me.gametype === sdk.game.gametype.Classic; + } + }, + expansion: { + get: function () { + return me.gametype === sdk.game.gametype.Expansion; + } + }, + softcore: { + get: function () { + return me.playertype === false; + } + }, + hardcore: { + get: function () { + return me.playertype === true; + } + }, + normal: { + get: function () { + return me.diff === sdk.difficulty.Normal; + } + }, + nightmare: { + get: function () { + return me.diff === sdk.difficulty.Nightmare; + } + }, + hell: { + get: function () { + return me.diff === sdk.difficulty.Hell; + } + }, + amazon: { + get: function () { + return me.classid === sdk.player.class.Amazon; + } + }, + sorceress: { + get: function () { + return me.classid === sdk.player.class.Sorceress; + } + }, + necromancer: { + get: function () { + return me.classid === sdk.player.class.Necromancer; + } + }, + paladin: { + get: function () { + return me.classid === sdk.player.class.Paladin; + } + }, + barbarian: { + get: function () { + return me.classid === sdk.player.class.Barbarian; + } + }, + druid: { + get: function () { + return me.classid === sdk.player.class.Druid; + } + }, + assassin: { + get: function () { + return me.classid === sdk.player.class.Assassin; + } + }, + // quest items + wirtsleg: { + get: function () { + return me.getItem(sdk.quest.item.WirtsLeg); + } + }, + cube: { + get: function () { + return me.getItem(sdk.quest.item.Cube); + } + }, + shaft: { + get: function () { + return me.getItem(sdk.quest.item.ShaftoftheHoradricStaff); + } + }, + amulet: { + get: function () { + return me.getItem(sdk.quest.item.ViperAmulet); + } + }, + staff: { + get: function () { + return me.getItem(sdk.quest.item.HoradricStaff); + } + }, + completestaff: { + get: function () { + return me.getItem(sdk.quest.item.HoradricStaff); + } + }, + eye: { + get: function () { + return me.getItem(sdk.items.quest.KhalimsEye); + } + }, + brain: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsBrain); + } + }, + heart: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsHeart); + } + }, + khalimswill: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsWill); + } + }, + khalimsflail: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsFlail); + } + }, + malahspotion: { + get: function () { + return me.getItem(sdk.quest.item.MalahsPotion); + } + }, + scrollofresistance: { + get: function () { + return me.getItem(sdk.quest.item.ScrollofResistance); + } + }, + // quests + den: { + get: function () { + return me.getQuest(sdk.quest.id.DenofEvil, sdk.quest.states.Completed); + } + }, + bloodraven: { + get: function () { + return me.getQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed); + } + }, + smith: { + get: function () { + return me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.Completed); + } + }, + cain: { + get: function () { + return me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + } + }, + tristram: { + get: function () { + // update where this is used and change the state to be portal opened and me.cain to be quest completed + return me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); + } + }, + countess: { + get: function () { + return me.getQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); + } + }, + andariel: { + get: function () { + return me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed); + } + }, + radament: { + get: function () { + return me.getQuest(sdk.quest.id.RadamentsLair, sdk.quest.states.Completed); + } + }, + horadricstaff: { + get: function () { + return me.getQuest(sdk.quest.id.TheHoradricStaff, sdk.quest.states.Completed); + } + }, + summoner: { + get: function () { + return me.getQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed); + } + }, + duriel: { + get: function () { + return me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed); + } + }, + goldenbird: { + get: function () { + return me.getQuest(sdk.quest.id.TheGoldenBird, sdk.quest.states.Completed); + } + }, + lamessen: { + get: function () { + return me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed); + } + }, + gidbinn: { + get: function () { + return me.getQuest(sdk.quest.id.BladeoftheOldReligion, sdk.quest.states.Completed); + } + }, + travincal: { + get: function () { + return me.getQuest(sdk.quest.id.KhalimsWill, sdk.quest.states.Completed); + } + }, + mephisto: { + get: function () { + return me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed); + } + }, + izual: { + get: function () { + return me.getQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.Completed); + } + }, + hellforge: { + get: function () { + return me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.Completed); + } + }, + diablo: { + get: function () { + return me.getQuest(sdk.quest.id.TerrorsEnd, sdk.quest.states.Completed); + } + }, + shenk: { + get: function () { + return me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed); + } + }, + larzuk: { + get: function () { + return me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete); + } + }, + savebarby: { + get: function () { + return me.getQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); + } + }, + barbrescue: { + get: function () { + return me.getQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); + } + }, + anya: { + get: function () { + return me.getQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed); + } + }, + ancients: { + get: function () { + return me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed); + } + }, + baal: { + get: function () { + return me.getQuest(sdk.quest.id.EyeofDestruction, sdk.quest.states.Completed); + } + }, + // Misc + cows: { + get: function () { + return me.getQuest(sdk.quest.id.TheSearchForCain, 10); + } + }, + respec: { + get: function () { + return me.getQuest(sdk.quest.id.Respec, sdk.quest.states.Completed); + } + }, + diffCompleted: { + get: function () { + return !!((me.classic && me.diablo) || me.baal); + } + }, +}); diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js new file mode 100644 index 000000000..5e99fa6b0 --- /dev/null +++ b/d2bs/kolbot/libs/core/Misc.js @@ -0,0 +1,767 @@ +/** +* @filename Misc.js +* @author kolton, theBGuy +* @desc Misc library for functions that don't fit neatly into another namespace +* contains clickhandling, shrine/chest/player locating, ect +* +*/ + +const Misc = { + // Click something + click: function (button, shift, x, y) { + if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); + + while (!me.gameReady) { + delay(100); + } + + switch (arguments.length) { + case 2: + me.blockMouse = true; + clickMap(button, shift, me.x, me.y); + delay(20); + clickMap(button + 2, shift, me.x, me.y); + me.blockMouse = false; + + break; + case 3: + if (typeof (x) !== "object") throw new Error("Misc.click: Third arg must be a Unit."); + + me.blockMouse = true; + clickMap(button, shift, x); + delay(20); + clickMap(button + 2, shift, x); + me.blockMouse = false; + + break; + case 4: + me.blockMouse = true; + clickMap(button, shift, x, y); + delay(20); + clickMap(button + 2, shift, x, y); + me.blockMouse = false; + + break; + } + + return true; + }, + + // Check if a player is in your party + inMyParty: function (name) { + if (me.name === name) return true; + + while (!me.gameReady) { + delay(100); + } + + let player, myPartyId; + + try { + player = getParty(); + if (!player) return false; + + myPartyId = player.partyid; + player = getParty(name); // May throw an error + + if (player && player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { + return true; + } + } catch (e) { + player = getParty(); + + if (player) { + myPartyId = player.partyid; + + while (player.getNext()) { + if (player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { + return true; + } + } + } + } + + return false; + }, + + // Find a player + findPlayer: function (name) { + let player = getParty(); + + if (player) { + do { + if (player.name !== me.name && player.name === name) { + return player; + } + } while (player.getNext()); + } + + return false; + }, + + // Get player unit + getPlayerUnit: function (name) { + let player = Game.getPlayer(name); + + if (player) { + do { + if (!player.dead) { + return player; + } + } while (player.getNext()); + } + + return false; + }, + + // Get the player act, accepts party unit or name + getPlayerAct: function (player) { + if (!player) return false; + + let unit = (typeof player === "object" ? player : this.findPlayer(player)); + + return unit ? sdk.areas.actOf(unit.area) : false; + }, + + // Get number of players within getUnit distance + getNearbyPlayerCount: function () { + let count = 0; + let player = Game.getPlayer(); + + if (player) { + do { + if (player.name !== me.name && !player.dead) { + count += 1; + } + } while (player.getNext()); + } + + return count; + }, + + // Get total number of players in game + getPlayerCount: function () { + let count = 0; + let party = getParty(); + + if (party) { + do { + count += 1; + } while (party.getNext()); + } + + return count; + }, + + // Get total number of players in game and in my party + getPartyCount: function () { + let count = 0; + let party = getParty(); + + if (party) { + let myPartyId = party.partyid; + + do { + if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name) { + print(party.name); + count += 1; + } + } while (party.getNext()); + } + + return count; + }, + + // check if any member of our party meets a certain level req + checkPartyLevel: function (levelCheck = 1, exclude = []) { + !Array.isArray(exclude) && (exclude = [exclude]); + let party = getParty(); + + if (party) { + let myPartyId = party.partyid; + + do { + if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name && !exclude.includes(party.name)) { + if (party.level >= levelCheck) { + return true; + } + } + } while (party.getNext()); + } + + return false; + }, + + getPlayerArea: function (player) { + if (!player) return false; + + let unit = (typeof player === "object" ? player : this.findPlayer(player)); + + return !!unit ? unit.area : 0; + }, + + // autoleader by Ethic - refactored by theBGuy + autoLeaderDetect: function (givenSettings = {}) { + const settings = Object.assign({}, { + destination: -1, + quitIf: false, + timeout: Infinity + }, givenSettings); + + let leader; + let startTick = getTickCount(); + let check = typeof settings.quitIf === "function"; + do { + let solofail = 0; + let suspect = getParty(); // get party object (players in game) + + do { + // player isn't alone + suspect.name !== me.name && (solofail += 1); + + if (check && settings.quitIf(suspect.area)) return false; + + // first player not hostile found in destination area... + if (suspect.area === settings.destination && !getPlayerFlag(me.gid, suspect.gid, 8)) { + leader = suspect.name; // ... is our leader + console.log("ÿc4Autodetected " + leader); + + return leader; + } + } while (suspect.getNext()); + + // empty game, nothing left to do. Or we exceeded our wait time + if (solofail === 0 || (getTickCount() - startTick > settings.timeout)) { + return false; + } + + delay(500); + } while (!leader); // repeat until leader is found (or until game is empty) + + return false; + }, + + /** + * @description Open a chest Unit (takes chestID or unit) + * @param {Unit | number} unit + * @returns {boolean} If we opened the chest + */ + 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); + if (useTK) { + unit.distance > 13 && Attack.getIntoPosition(unit, 13, sdk.collision.WallOrRanged); + if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { + console.debug("Failed to tk: attempt: " + i); + continue; + } + } else { + [(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.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; + }, + + // Open all chests that have preset units in an area + openChestsInArea: function (area, chestIds = []) { + !area && (area = me.area); + area !== me.area && Pather.journeyTo(area); + + let presetUnits = Game.getPresetObjects(area); + if (!presetUnits) return false; + + if (!chestIds.length) { + chestIds = [ + 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 + ]; + } + + 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.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; + }, + + openChests: function (range = 15) { + if (!Config.OpenChests.Enabled) return true; + + let unitList = []; + let containers = []; + + // Testing all container code + if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { + containers = [ + "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", "casket", "armorstand", "weaponrack", "barrel", "holeanim", "tomb2", + "tomb3", "roguecorpse", "ratnest", "corpse", "goo pile", "largeurn", "urn", "chest3", "jug", "skeleton", "guardcorpse", "sarcophagus", "object2", + "cocoon", "basket", "stash", "hollow log", "hungskeleton", "pillar", "skullpile", "skull pile", "jar3", "jar2", "jar1", "bonechest", "woodchestl", + "woodchestr", "barrel wilderness", "burialchestr", "burialchestl", "explodingchest", "chestl", "chestr", "groundtomb", "icecavejar1", "icecavejar2", + "icecavejar3", "icecavejar4", "deadperson", "deadperson2", "evilurn", "tomb1l", "tomb3l", "groundtombl" + ]; + } else { + containers = Config.OpenChests.Types; + } + + let unit = Game.getObject(); + + if (unit) { + do { + if (unit.name && unit.mode === sdk.objects.mode.Inactive && getDistance(me.x, me.y, unit.x, unit.y) <= range && containers.includes(unit.name.toLowerCase())) { + unitList.push(copyUnit(unit)); + } + } while (unit.getNext()); + } + + while (unitList.length > 0) { + unitList.sort(Sort.units); + unit = unitList.shift(); + + if (unit) { + const chest = Game.getObject(-1, -1, unit.gid); + if (chest && (Pather.useTeleport() || !checkCollision(me, chest, sdk.collision.WallOrRanged)) && this.openChest(chest)) { + Pickit.pickItems(); + } + } + } + + return true; + }, + + shrineStates: false, + + scanShrines: function (range, ignore = []) { + if (!Config.ScanShrines.length) return false; + + !range && (range = Pather.useTeleport() ? 25 : 15); + !Array.isArray(ignore) && (ignore = [ignore]); + + let shrineList = []; + + // Initiate shrine states + if (!this.shrineStates) { + Misc.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 shrine = Game.getObject("shrine"); + + if (shrine) { + let index = -1; + // Build a list of nearby shrines + do { + if (shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) && getDistance(me.x, me.y, shrine.x, shrine.y) <= range) { + shrineList.push(copyUnit(shrine)); + } + } while (shrine.getNext()); + + // 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 j = 0; j < shrineList.length; j += 1) { + // 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]); + + // Gem shrine - pick gem + if (Config.ScanShrines[i] === sdk.shrines.Gem) { + Pickit.pickItems(); + } + } + } + } + } + } + + return true; + }, + + // Use a shrine Unit + getShrine: function (unit) { + if (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); + !Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, 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)) { + return true; + } + } + + return false; + }, + + /** + * 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. + */ + getShrinesInArea: function (area, type, use) { + let shrineLocs = []; + let shrineIds = [2, 81, 83]; + let unit = Game.getPresetObjects(area); + let result = false; + + 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]); + } + } + } + + try { + NodeAction.shrinesToIgnore.push(type); + + while (shrineLocs.length > 0) { + 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"); + return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; + }}); + + 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; + } + } + } while (shrine.getNext()); + } + } + } finally { + NodeAction.shrinesToIgnore.remove(type); + } + + return result; + }, + + // Go to town when low on hp/mp or when out of potions. can be upgraded to check for curses etc. + townCheck: function () { + if (!me.canTpToTown()) return false; + + let tTick = getTickCount(); + let check = false; + + if (Config.TownCheck && !me.inTown) { + try { + if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { + check = true; + } + } catch (e) { + return false; + } + + if (check) { + // check that townchicken is running - so we don't spam needing potions if it isn't + let townChick = getScript("threads/TownChicken.js"); + if (!townChick || townChick && !townChick.running) { + return false; + } + + townChick.send("townCheck"); + console.log("townCheck check Duration: " + (getTickCount() - tTick)); + + return true; + } + } + + return false; + }, + + // Log someone's gear + spy: function (name) { + let unit = getUnit(-1, name); + + if (!unit) { + console.warn("player not found"); + return false; + } + + let item = unit.getItem(); + + if (item) { + do { + this.logItem(unit.name, item); + } while (item.getNext()); + } + + return true; + }, + + errorConsolePrint: true, + screenshotErrors: true, + + // Report script errors to logs/ScriptErrorLog.txt + 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: " + 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 += ", "; + } + } + } + } + } + + stackLog && (filemsg += "Stack: " + stackLog + "\n"); + } + + this.errorConsolePrint && D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); + showConsole(); + console.log(msg); + FileAction.read("logs/ScriptErrorLog.txt", filemsg); + + if (this.screenshotErrors) { + takeScreenshot(); + delay(500); + } + }, + + debugLog: function (msg) { + if (!Config.Debug) return; + debugLog(me.profile + ": " + msg); + }, + + // Use a NPC menu. Experimental function, subject to change + // id = string number (with exception of Ressurect merc). + useMenu: function (id) { + //print("useMenu " + getLocaleString(id)); + + let npc; + + switch (id) { + case sdk.menu.RessurectMerc: // (non-English dialog) + case sdk.menu.Trade: // (crash dialog) + npc = getInteractedNPC(); + + if (npc) { + npc.useMenu(id); + delay(750); + + return true; + } + + break; + } + + let lines = getDialogLines(); + if (!lines) return false; + + for (let i = 0; i < lines.length; i += 1) { + if (lines[i].selectable && lines[i].text.includes(getLocaleString(id))) { + getDialogLines()[i].handler(); + delay(750); + + return true; + } + } + + return false; + }, + + poll: function (check, timeout = 6000, sleep = 40) { + let ret, start = getTickCount(); + + while (getTickCount() - start <= timeout) { + if ((ret = check())) { + return ret; + } + + delay(sleep); + } + + return false; + }, + + // returns array of UI flags that are set, or null if none are set + getUIFlags: function (excluded = []) { + if (!me.gameReady) return null; + + const MAX_FLAG = 37; // anything over 37 crashes + let flags = []; + + if (typeof excluded !== "object" || excluded.length === undefined) { + // not an array-like object, make it an array + excluded = [excluded]; + } + + for (let c = 1; c <= MAX_FLAG; c++) { + // 0x23 is always set in-game + if (c !== 0x23 && excluded.indexOf(c) === -1 && getUIFlag(c)) { + flags.push(c); + } + } + + return flags.length ? flags : null; + }, + + checkQuest: function (id, state) { + Packet.questRefresh(); + delay(500); + return me.getQuest(id, state); + }, + + getQuestStates: function (questID) { + if (!me.gameReady) return []; + Packet.questRefresh(); + delay(500); + const MAX_STATE = 16; + let questStates = []; + + for (let i = 0; i < MAX_STATE; i++) { + if (me.getQuest(questID, i)) { + questStates.push(i); + } + + delay(50); + } + + return questStates; + } +}; +// (function() { +// // todo figure out why the Misc.d.ts file only works right if Misc is wrapped in a IEFE + +// // export to global scope +// global.Misc = Misc; +// })(); diff --git a/d2bs/kolbot/libs/NTItemParser.dbl b/d2bs/kolbot/libs/core/NTItemParser.js similarity index 98% rename from d2bs/kolbot/libs/NTItemParser.dbl rename to d2bs/kolbot/libs/core/NTItemParser.js index 4dcaec420..9ef251b0a 100644 --- a/d2bs/kolbot/libs/NTItemParser.dbl +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -1,8 +1,9 @@ /** -* @filename NTItemParser.dbl -* @author kolton +* @filename NTItemParser.js +* @author kolton, jaenster * @credit d2nt * @desc nip file parser for kolbots pickit system +* * * @Item-parser Syntax Information * 1. [Keyword] separates into two groups @@ -14,11 +15,16 @@ * 5. Use '+', '-', '*', '/', '(', ')', '&&', '||', '>', '>=', '<', '<=', '==', '!=' symbols for comparison * 6. Use '//' symbol for comment * -* @Example: [name] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 // Perfect Raven Frost +* @example: [name] == ring && [quality] == unique # [dexterity] == 20 && [tohit] == 250 // Perfect Raven Frost * */ -include("NTItemAlias.dbl"); +includeIfNotIncluded("core/Prototypes.js"); +includeIfNotIncluded("core/Data/NTItemAlias.js"); + +/** + * @todo clean up this file + */ const NTIP = {}; const NTIP_CheckList = []; diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js new file mode 100644 index 000000000..1154505eb --- /dev/null +++ b/d2bs/kolbot/libs/core/Packet.js @@ -0,0 +1,383 @@ +/** +* @filename Packet.js +* @author kolton, theBGuy +* @desc handle packet based functions +* +*/ + +const Packet = { + openMenu: function (unit) { + if (unit.type !== sdk.unittype.NPC) throw new Error("openMenu: Must be used on NPCs."); + if (getUIFlag(sdk.uiflags.NPCMenu)) return true; + let pingDelay = (me.gameReady ? me.ping : 125); + + for (let i = 0; i < 5; i += 1) { + unit.distance > 4 && Pather.moveToUnit(unit); + Packet.entityInteract(unit); + let tick = getTickCount(); + + while (getTickCount() - tick < 5000) { + if (getUIFlag(sdk.uiflags.NPCMenu)) { + delay(Math.max(500, pingDelay * 2)); + + return true; + } + + if ((getTickCount() - tick > 1000 && getInteractedNPC()) || (getTickCount() - tick > 500 && getIsTalkingNPC())) { + me.cancel(); + } + + delay(100); + } + + sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, unit.gid); + delay(pingDelay + 1 * 2); + Packet.cancelNPC(unit); + delay(pingDelay + 1 * 2); + this.flash(me.gid); + } + + return false; + }, + + startTrade: function (unit, mode) { + if (unit.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); + if (getUIFlag(sdk.uiflags.Shop)) return true; + + const gamble = mode === "Gamble"; + console.info(true, mode + " at " + unit.name); + + if (this.openMenu(unit)) { + for (let i = 0; i < 10; i += 1) { + delay(200); + + i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, gamble ? 2 : 1, 4, unit.gid, 4, 0); + + if (unit.itemcount > 0) { + delay(200); + console.info(false, "Successfully started " + mode + " at " + unit.name); + return true; + } + } + } + + return false; + }, + + buyItem: function (unit, shiftBuy, gamble) { + let oldGold = me.gold; + let itemCount = me.itemcount; + let npc = getInteractedNPC(); + + try { + if (!npc) throw new Error("buyItem: No NPC menu open."); + + // Can we afford the item? + if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; + + for (let i = 0; i < 3; i += 1) { + sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : gamble ? 0x2 : 0x0, 4, 0); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { + if (shiftBuy && me.gold < oldGold) return true; + if (itemCount !== me.itemcount) return true; + + delay(10); + } + } + } catch (e) { + console.error(e); + } + + return false; + }, + + buyScroll: function (unit, tome, shiftBuy) { + let oldGold = me.gold; + let itemCount = me.itemcount; + let npc = getInteractedNPC(); + tome === undefined && (tome = me.findItem( + (unit.classid === sdk.items.ScrollofTownPortal ? sdk.items.TomeofTownPortal : sdk.items.TomeofIdentify), + sdk.items.mode.inStorage, sdk.storage.Inventory + )); + let preCount = !!tome ? tome.getStat(sdk.stats.Quantity) : 0; + + try { + if (!npc) throw new Error("buyItem: No NPC menu open."); + + // Can we afford the item? + if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; + + for (let i = 0; i < 3; i += 1) { + sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : 0x0, 4, 0); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { + if (shiftBuy && me.gold < oldGold) return true; + if (itemCount !== me.itemcount) return true; + if (tome && tome.getStat(sdk.stats.Quantity) > preCount) return true; + delay(10); + } + } + } catch (e) { + console.error(e); + } + + return false; + }, + + sellItem: function (unit) { + // Check if it's an item we want to buy + if (unit.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); + if (!unit.sellable) { + console.error((new Error("Item is unsellable"))); + return false; + } + + let itemCount = me.itemcount; + let npc = getInteractedNPC(); + + if (!npc) return false; + + for (let i = 0; i < 5; i += 1) { + sendPacket(1, sdk.packets.send.NPCSell, 4, npc.gid, 4, unit.gid, 4, 0, 4, 0); + + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (me.itemcount !== itemCount) return true; + delay(10); + } + } + + return false; + }, + + identifyItem: function (unit, tome) { + if (!unit || unit.identified) return false; + + CursorLoop: + for (let i = 0; i < 3; i += 1) { + sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); + + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (getCursorType() === sdk.cursortype.Identify) { + break CursorLoop; + } + + delay(10); + } + } + + if (getCursorType() !== sdk.cursortype.Identify) { + return false; + } + + for (let i = 0; i < 3; i += 1) { + getCursorType() === sdk.cursortype.Identify && sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); + + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (unit.identified) { + delay(50); + return true; + } + + delay(10); + } + } + + return false; + }, + + itemToCursor: function (item) { + // Something already on cursor + if (me.itemoncursor) { + let cursorItem = Game.getCursorUnit(); + // Return true if the item is already on cursor + if (cursorItem.gid === item.gid) { + return true; + } + this.dropItem(cursorItem); // If another item is on cursor, drop it + } + + for (let i = 0; i < 15; i += 1) { + // equipped + item.isEquipped ? sendPacket(1, sdk.packets.send.PickupBodyItem, 2, item.bodylocation) : sendPacket(1, sdk.packets.send.PickupBufferItem, 4, item.gid); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { + if (me.itemoncursor) return true; + delay(10); + } + } + + return false; + }, + + dropItem: function (item) { + if (!this.itemToCursor(item)) return false; + + for (let i = 0; i < 15; i += 1) { + sendPacket(1, sdk.packets.send.DropItem, 4, item.gid); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { + if (!me.itemoncursor) return true; + delay(10); + } + } + + return false; + }, + + givePotToMerc: function (item) { + if (!!item + && [sdk.items.type.HealingPotion, sdk.items.type.RejuvPotion, sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion].includes(item.itemType)) { + switch (item.location) { + case sdk.storage.Belt: + return this.useBeltItemForMerc(item); + case sdk.storage.Inventory: + if (this.itemToCursor(item)) { + sendPacket(1, sdk.packets.send.MercItem, 2, 0); + + return true; + } + + break; + default: + break; + } + } + + return false; + }, + + placeInBelt: function (item, xLoc) { + item.toCursor(true) && new PacketBuilder().byte(sdk.packets.send.ItemToBelt).dword(item.gid).dword(xLoc).send(); + return Misc.poll(() => item.isInBelt, 500, 100); + }, + + click: function (who, toCursor = false) { + if (!who || !copyUnit(who).x) return false; + new PacketBuilder().byte(sdk.packets.send.PickupItem).dword(sdk.unittype.Item).dword(who.gid).dword(toCursor ? 1 : 0).send(); + return true; + }, + + entityInteract: function (who) { + if (!who || !copyUnit(who).x) return false; + sendPacket(1, sdk.packets.send.InteractWithEntity, 4, who.type, 4, who.gid); + return true; + }, + + cancelNPC: function (who) { + if (!who || !copyUnit(who).x) return false; + sendPacket(1, sdk.packets.send.NPCCancel, 4, who.type, 4, who.gid); + return true; + }, + + useBeltItemForMerc: function (who) { + if (!who) return false; + sendPacket(1, sdk.packets.send.UseBeltItem, 4, who.gid, 4, 1, 4, 0); + return true; + }, + + castSkill: function (hand, wX, wY) { + hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocation : sdk.packets.send.LeftSkillOnLocation; + sendPacket(1, hand, 2, wX, 2, wY); + }, + + unitCast: function (hand, who) { + hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnEntityEx3 : sdk.packets.send.LeftSkillOnEntityEx3; + sendPacket(1, hand, 4, who.type, 4, who.gid); + }, + + telekinesis: function (who) { + if (!who || !Skill.setSkill(sdk.skills.Telekinesis, sdk.skills.hand.Right)) return false; + sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); + return true; + }, + + enchant: function (who) { + if (!who || !Skill.setSkill(sdk.skills.Enchant, sdk.skills.hand.Right)) return false; + sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); + return true; + }, + + teleport: function (wX, wY) { + if (![wX, wY].every(n => typeof n === "number") || !Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right)) return false; + new PacketBuilder().byte(sdk.packets.send.RightSkillOnLocation).word(wX).word(wY).send(); + return true; + }, + + // moveNPC: function (npc, dwX, dwY) { // commented the patched packet + // //sendPacket(1, sdk.packets.send.MakeEntityMove, 4, npc.type, 4, npc.gid, 4, dwX, 4, dwY); + // }, + + teleWalk: function (x, y, maxDist = 5) { + !Packet.telewalkTick && (Packet.telewalkTick = 0); + + if (getDistance(me, x, y) > 10 && getTickCount() - this.telewalkTick > 3000 && Attack.validSpot(x, y)) { + for (let i = 0; i < 5; i += 1) { + sendPacket(1, sdk.packets.send.UpdatePlayerPos, 2, x + rand(-1, 1), 2, y + rand(-1, 1)); + delay(me.ping + 1); + sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, me.type, 4, me.gid); + delay(me.ping + 1); + + if (getDistance(me, x, y) < maxDist) { + delay(200); + + return true; + } + } + + Packet.telewalkTick = getTickCount(); + } + + return false; + }, + + questRefresh: function () { + sendPacket(1, sdk.packets.send.UpdateQuests); + }, + + flash: function (gid, wait = 0) { + wait === 0 && (wait = 300 + (me.gameReady ? 2 * me.ping : 300)); + sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, 0, 4, gid); + + if (wait > 0) { + delay(wait); + } + }, + + changeStat: function (stat, value) { + if (value > 0) { + getPacket(1, 0x1d, 1, stat, 1, value); + } + }, + + // specialized wrapper for addEventListener + addListener: function (packetType, callback) { + if (typeof packetType === "number") { + packetType = [packetType]; + } + + if (typeof packetType === "object" && packetType.length) { + addEventListener("gamepacket", packet => (packetType.indexOf(packet[0]) > -1 ? callback(packet) : false)); + + return callback; + } + + return null; + }, + + removeListener: callback => removeEventListener("gamepacket", callback), // just a wrapper +}; diff --git a/d2bs/kolbot/libs/common/Pather.js b/d2bs/kolbot/libs/core/Pather.js similarity index 85% rename from d2bs/kolbot/libs/common/Pather.js rename to d2bs/kolbot/libs/core/Pather.js index fed93409e..8b7c854f2 100644 --- a/d2bs/kolbot/libs/common/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -86,10 +86,10 @@ const PathDebug = { removeHooks: function () { for (let i = 0; i < this.hooks.length; i += 1) { - this.hooks[i].remove(); + PathDebug.hooks[i].remove(); } - this.hooks = []; + PathDebug.hooks = []; }, coordsInPath: function (path, x, y) { @@ -178,21 +178,46 @@ const Pather = { || (settings.returnSpotOnError ? spot : { x: me.x, y: me.y })); }, + /** + * @typedef {object} pathSettings + * @property {boolean} [allowTeleport] + * @property {boolean} [allowClearing] + * @property {boolean} [allowTown] + * @property {boolean} [allowPicking] + * @property {number} [minDist] + * @property {number} [retry] + * @property {boolean} [pop] + * @property {boolean} [returnSpotOnError] + * @property {Function} [callback] + * @property {object} [clearSettings] + * @property {boolean} [clearSettings.clearPath] + * @property {number} [clearSettings.range] + * @property {number} [clearSettings.specType] + * @property {Function} [clearSettings.sort] + * + * @param {PathNode | Unit | PresetUnit} target + * @param {pathSettings} givenSettings + * @returns {boolean} + */ 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, allowClearing: true, allowTown: true, + allowPicking: true, minDist: 3, retry: 5, pop: false, returnSpotOnError: true, - callback: () => {}, + callback: null, }, givenSettings); // assign clear settings becasue object.assign was removing the default properties of settings.clearSettings const clearSettings = Object.assign({ @@ -205,7 +230,7 @@ const Pather = { settings.clearSettings = clearSettings; !settings.allowClearing && (settings.clearSettings.allowClearing = 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 = this.spotOnDistance(target, settings.minDist, {returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2)}); @@ -213,7 +238,22 @@ const Pather = { 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]; for (let i = 0; i < this.cancelFlags.length; i += 1) { getUIFlag(this.cancelFlags[i]) && me.cancel(); @@ -234,10 +274,10 @@ const Pather = { useTeleport && Config.TeleSwitch && path.length > 5 && me.switchWeapons(Attack.getPrimarySlot() ^ 1); while (path.length > 0) { - // Abort if dead + // Abort if dead if (me.dead) return false; // main path - this.recursion && (this.currentWalkingPath = path); + Pather.recursion && (Pather.currentWalkingPath = path); for (let i = 0; i < this.cancelFlags.length; i += 1) { if (getUIFlag(this.cancelFlags[i])) me.cancel(); @@ -245,8 +285,15 @@ const Pather = { 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 + // 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) { @@ -263,13 +310,13 @@ const Pather = { if (useTeleport && tpMana <= me.mp ? this.teleportTo(node.x, node.y) : this.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { if (!me.inTown) { - if (this.recursion) { - this.recursion = false; + if (Pather.recursion) { + Pather.recursion = false; try { NodeAction.go(settings.clearSettings); - node.distance > 5 && this.moveTo(node.x, node.y); + node.distance > 5 && this.move(node, settings); } finally { - this.recursion = true; + Pather.recursion = true; } } @@ -277,7 +324,7 @@ const Pather = { } } else { if (!me.inTown) { - if (!useTeleport && settings.allowClearing && me.checkForMobs({range: 10}) && Attack.clear(10)) { + if (!useTeleport && settings.allowClearing && me.checkForMobs({range: 10}) && Attack.clear(10, null, null, null, settings.allowPicking)) { console.debug("Cleared Node"); continue; } @@ -286,15 +333,46 @@ const Pather = { } 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]; + } + } + } + + // 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) { + // 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]; + } + } } - // 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; + /** + * 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) { + // 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]; + } + } } } } @@ -329,32 +407,48 @@ const Pather = { return getDistance(me, node.x, node.y) < 5; }, + /** + * + * @param {number} x + * @param {number} y + * @param {number} minDist + * @param {pathSettings} givenSettings + * @returns {boolean} + */ moveNear: function (x, y, minDist, givenSettings = {}) { return Pather.move({x: x, y: y}, Object.assign({minDist: minDist}, givenSettings)); }, - /* - Pather.moveTo(x, y, retry, clearPath, pop); - x - the x coord to move to - y - the y coord to move to - retry - number of attempts before aborting - clearPath - kill monsters while moving - pop - remove last node - */ + /** + * @param {number} x - the x coord to move to + * @param {number} y - the y coord to move to + * @param {number} retry - number of attempts before aborting + * @param {boolean} clearPath - kill monsters while moving + * @param {boolean} pop - remove last node + * @returns {boolean} + */ moveTo: function (x, y, retry, clearPath = false, pop = false) { return Pather.move({x: x, y: y}, {retry: retry, pop: pop, allowClearing: clearPath}); }, + /** + * + * @param {number} x + * @param {number} y + * @param {pathSettings} givenSettings + * @returns + */ moveToEx: function (x, y, givenSettings = {}) { return Pather.move({x: x, y: y}, givenSettings); }, - /* - Pather.teleportTo(x, y); - x - the x coord to teleport to - y - the y coord to teleport to - */ - // does this need a validLocation check? - maybe if we fail once check the spot + /** + * @param {number} x - the x coord to teleport to + * @param {number} y - the y coord to teleport to + * @param {number} [maxRange] - max acceptable distance from node + * @returns {boolean} + * @todo does this need a validLocation check? - maybe if we fail once check the spot + */ teleportTo: function (x, y, maxRange = 5) { for (let i = 0; i < 3; i += 1) { Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); @@ -373,12 +467,12 @@ const Pather = { return false; }, - /* - Pather.walkTo(x, y); - x - the x coord to walk to - y - the y coord to walk to - minDist - minimal distance from x/y before returning true - */ + /** + * @param {number} x - the x coord to teleport to + * @param {number} y - the y coord to teleport to + * @param {number} [minDist] - minimal distance from x/y before returning true + * @returns {boolean} - sucessfully moved within minDist + */ walkTo: function (x, y, minDist) { while (!me.gameReady) { delay(100); @@ -390,6 +484,9 @@ const Pather = { let nTimer; let [nFail, attemptCount] = [0, 0]; + /** + * @todo add cleansing/meditation here as well + */ // credit @Jaenster // Stamina handler and Charge if (!me.inTown) { @@ -608,14 +705,15 @@ const Pather = { return brokeABarrel; }, - /* - Pather.moveToUnit(unit, offX, offY, clearPath, pop); - unit - a valid Unit or PresetUnit object - offX - offset from unit's x coord - offY - offset from unit's x coord - clearPath - kill monsters while moving - pop - remove last node - */ + /** + * Move to unit + * @param {Unit} unit - unit to move to + * @param {number} [offX] - offset from unit's x coord + * @param {number} [offY] - offset from unit's x coord + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved to unit + */ moveToUnit: function (unit, offX, offY, clearPath, pop) { const useTeleport = this.useTeleport(); @@ -636,6 +734,13 @@ const Pather = { return this.moveTo(unit.x + offX, unit.y + offY, useTeleport && unit.type && unit.isMonster ? 3 : 0, clearPath, pop); }, + /** + * Move near unit + * @param {Unit} unit - unit to move near + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved near unit + */ moveNearUnit: function (unit, minDist, clearPath, pop = false) { const useTeleport = this.useTeleport(); minDist === undefined && (minDist = me.inTown ? 2 : 5); @@ -652,6 +757,16 @@ const Pather = { return this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: pop }); }, + /** + * Move near preset unit + * @param {number} area - area of the preset unit + * @param {number} unitType - type of the preset unit + * @param {number} unitId - preset unit id + * @param {number} [minDist] - minimum distance from unit + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved near unit + */ moveNearPreset: function (area, unitType, unitId, minDist, clearPath = false, pop = false) { if (area === undefined || unitType === undefined || unitId === undefined) { throw new Error("moveNearPreset: Invalid parameters."); @@ -661,7 +776,7 @@ const Pather = { let presetUnit = getPresetUnit(area, unitType, unitId); if (!presetUnit) { - throw new Error("moveNearPreset: Couldn't find preset unit - id: " + unitId + " unitType: " + unitType + " in area: " + this.getAreaName(area)); + throw new Error("moveNearPreset: Couldn't find preset unit - id: " + unitId + " unitType: " + unitType + " in area: " + getAreaName(area)); } delay(40); @@ -672,16 +787,17 @@ const Pather = { return this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: pop }); }, - /* - Pather.moveToPreset(area, unitType, unitId, offX, offY, clearPath, pop); - area - area of the preset unit - unitType - type of the preset unit - unitId - preset unit id - offX - offset from unit's x coord - offY - offset from unit's x coord - clearPath - kill monsters while moving - pop - remove last node - */ + /** + * Move to preset unit + * @param {number} area - area of the preset unit + * @param {number} unitType - type of the preset unit + * @param {number} unitId - preset unit id + * @param {number} [offX] - offset from unit's x coord + * @param {number} [offY] - offset from unit's x coord + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved to unit + */ moveToPreset: function (area, unitType, unitId, offX, offY, clearPath, pop) { if (area === undefined || unitType === undefined || unitId === undefined) { throw new Error("moveToPreset: Invalid parameters."); @@ -696,7 +812,7 @@ const Pather = { let presetUnit = getPresetUnit(area, unitType, unitId); if (!presetUnit) { - throw new Error("moveToPreset: Couldn't find preset unit - id: " + unitId + " unitType: " + unitType + " in area: " + this.getAreaName(area)); + throw new Error("moveToPreset: Couldn't find preset unit - id: " + unitId + " unitType: " + unitType + " in area: " + getAreaName(area)); } delay(40); @@ -705,27 +821,33 @@ const Pather = { return this.moveTo(presetUnit.roomx * 5 + presetUnit.x + offX, presetUnit.roomy * 5 + presetUnit.y + offY, 3, clearPath, pop); }, - /* - Pather.moveToExit(targetArea, use, clearPath); - targetArea - area id or array of area ids to move to - use - enter target area or last area in the array - clearPath - kill monsters while moving - */ + /** + * @todo + * moveTo/NearPresetMonster + * moveTo/NearPresetObject + * moveTo/NearPresetTile + */ + + /** + * @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 {boolean} clearPath - kill monsters while moving + */ moveToExit: function (targetArea, use = false, clearPath = false) { if (targetArea === undefined) return false; console.time("moveToExit"); - console.info(true, "ÿc7MyArea: ÿc0" + Pather.getAreaName(me.area) + " ÿc7TargetArea: ÿc0" + Pather.getAreaName(targetArea)); + console.info(true, "ÿc7MyArea: ÿc0" + getAreaName(me.area) + " ÿc7TargetArea: ÿc0" + getAreaName(targetArea)); let areas = Array.isArray(targetArea) ? targetArea : [targetArea]; let finalDest = areas.last(); for (let i = 0; i < areas.length; i += 1) { if (me.area === areas[i]) { - console.debug("Already in: " + Pather.getAreaName(areas[i])); + console.debug("Already in: " + getAreaName(areas[i])); continue; } - console.info(null, "ÿc0Moving from: " + Pather.getAreaName(me.area) + " to " + Pather.getAreaName(areas[i])); + console.info(null, "ÿc0Moving from: " + getAreaName(me.area) + " to " + getAreaName(areas[i])); Config.DebugMode && console.time("getArea"); let area = Misc.poll(() => getArea(me.area)); @@ -824,7 +946,7 @@ const Pather = { } } - console.info(false, "ÿc7targetArea: ÿc0" + this.getAreaName(finalDest) + " ÿc7myArea: ÿc0" + this.getAreaName(me.area), "moveToExit"); + console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(finalDest) + " ÿc7myArea: ÿc0" + getAreaName(me.area), "moveToExit"); delay(300); return (use && finalDest ? me.area === finalDest : true); @@ -834,7 +956,7 @@ const Pather = { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); let areaToCheck = Misc.poll(() => getArea(area)); - if (!areaToCheck) throw new Error("Couldn't get area info for " + Pather.getAreaName(area)); + if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); let exits = areaToCheck.exits; if (!exits.length) throw new Error("Failed to find exits"); let loc = exits.find(a => a.target === exit); @@ -846,7 +968,7 @@ const Pather = { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); let areaToCheck = Misc.poll(() => getArea(area)); - if (!areaToCheck) throw new Error("Couldn't get area info for " + Pather.getAreaName(area)); + if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); let exits = areaToCheck.exits; if (!exits.length) throw new Error("Failed to find exits"); let loc = exits.find(a => a.target === exit); @@ -957,7 +1079,7 @@ const Pather = { let preArea = me.area; if (!unit) { - throw new Error("useUnit: Unit not found. TYPE: " + type + " ID: " + id + " MyArea: " + this.getAreaName(me.area) + (!!targetArea ? " TargetArea: " + Pather.getAreaName(targetArea) : "")); + throw new Error("useUnit: Unit not found. TYPE: " + type + " ID: " + id + " MyArea: " + getAreaName(me.area) + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); } for (let i = 0; i < 5; i += 1) { @@ -972,7 +1094,7 @@ const Pather = { if (type === sdk.unittype.Object && unit.mode === sdk.objects.mode.Inactive) { if ((me.inArea(sdk.areas.Travincal) && targetArea === sdk.areas.DuranceofHateLvl1 && me.getQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) !== 1) || (me.inArea(sdk.areas.ArreatSummit) && targetArea === sdk.areas.WorldstoneLvl1 && me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed) !== 1)) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + Pather.getAreaName(targetArea) : "")); + throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); } me.inArea(sdk.areas.A3SewersLvl1) ? this.openUnit(sdk.unittype.Object, sdk.objects.SewerLever) : this.openUnit(sdk.unittype.Object, id); @@ -980,7 +1102,7 @@ const Pather = { if (type === sdk.unittype.Object && id === sdk.objects.RedPortalToAct4 && me.inArea(sdk.areas.DuranceofHateLvl3) && targetArea === sdk.areas.PandemoniumFortress && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + Pather.getAreaName(targetArea) : "")); + throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); } delay(300); @@ -1031,7 +1153,7 @@ const Pather = { throw new Error( "useUnit: Unit not found. TYPE: " + (settings.type ? settings.type : "") + " ID: " + (settings.id ? settings.id : "") - + " MyArea: " + this.getAreaName(me.area) + (!!targetArea ? " TargetArea: " + Pather.getAreaName(targetArea) : "") + + " MyArea: " + getAreaName(me.area) + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "") ); } @@ -1053,7 +1175,7 @@ const Pather = { if (type === sdk.unittype.Object && unit.mode === sdk.objects.mode.Inactive) { if ((me.inArea(sdk.areas.Travincal) && targetArea === sdk.areas.DuranceofHateLvl1 && me.getQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) !== 1) || (me.inArea(sdk.areas.ArreatSummit) && targetArea === sdk.areas.WorldstoneLvl1 && me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed) !== 1)) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + Pather.getAreaName(targetArea) : "")); + throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); } me.inArea(sdk.areas.A3SewersLvl1) ? this.openUnit(sdk.unittype.Object, sdk.objects.SewerLever) : this.openUnit(sdk.unittype.Object, id); @@ -1061,7 +1183,7 @@ const Pather = { if (type === sdk.unittype.Object && id === sdk.objects.RedPortalToAct4 && me.inArea(sdk.areas.DuranceofHateLvl3) && targetArea === sdk.areas.PandemoniumFortress && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + Pather.getAreaName(targetArea) : "")); + throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); } delay(300); @@ -1121,7 +1243,7 @@ const Pather = { } this.broadcastIntent(targetArea); - console.info(true, "ÿc7targetArea: ÿc0" + this.getAreaName(targetArea) + " ÿc7myArea: ÿc0" + this.getAreaName(me.area)); + console.info(true, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area)); let wpTick = getTickCount(); for (let i = 0; i < 12; i += 1) { @@ -1201,14 +1323,14 @@ const Pather = { if (!getWaypoint(this.wpAreas.indexOf(targetArea))) { me.cancel(); - console.log("Trying to get the waypoint: " + this.getAreaName(targetArea)); + console.log("Trying to get the waypoint: " + getAreaName(targetArea)); me.overhead("Trying to get the waypoint"); if (Pather.getWP(targetArea)) { return true; } - throw new Error("Pather.useWaypoint: Failed to go to waypoint " + this.getAreaName(targetArea)); + throw new Error("Pather.useWaypoint: Failed to go to waypoint " + getAreaName(targetArea)); } break; @@ -1237,8 +1359,17 @@ const Pather = { while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 2)) { if (me.area === targetArea) { delay((1500 + (pingDelay * i))); - console.info(false, "ÿc7targetArea: ÿc0" + this.getAreaName(targetArea) + " ÿc7myArea: ÿc0" + this.getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); + console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); + + // if (!me.inTown && Config.ForcePrecast && !(new Error().stack.match("precast.js"))) { + // let 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); + // Precast.doPrecast(false, true); + // } + // } return true; } @@ -1268,8 +1399,16 @@ const Pather = { if (me.area === targetArea) { // delay to allow act to init - helps with crashes delay(500); - console.info(false, "ÿc7targetArea: ÿc0" + this.getAreaName(targetArea) + " ÿc7myArea: ÿc0" + this.getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - + console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); + // if (!me.inTown && Config.ForcePrecast && !(new Error().stack.match("precast.js"))) { + // let 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); + // Precast.doPrecast(false, true); + // } + // } return true; } @@ -1288,8 +1427,8 @@ const Pather = { for (let i = 0; i < 5; i += 1) { if (me.dead) return false; - let tpTool = Town.getTpTool(); - let pingDelay = i === 0 ? 100 : me.gameReady ? (me.ping + 25) : 350; + const tpTool = me.getTpTool(); + const pingDelay = i === 0 ? 100 : me.gameReady ? (me.ping + 25) : 350; if (!tpTool) return false; let oldPortal = getUnits(sdk.unittype.Object, "portal") @@ -1306,14 +1445,18 @@ const Pather = { .filter((p) => p.getParent() === me.name && p.gid !== oldGid) .first(); - if (!!portal) { - if (use) { - if (this.usePortal(null, null, copyUnit(portal))) { - return true; + if (portal) { + // getUnits returns a copied version, get the actual portal so we don't copy a copy + const realPortal = Game.getObject(-1, -1, portal.gid); + if (realPortal) { + if (use) { + if (this.usePortal(null, null, copyUnit(realPortal))) { + return true; + } + break; // don't spam usePortal + } else { + return copyUnit(realPortal); } - break; // don't spam usePortal - } else { - return copyUnit(portal); } } @@ -1338,10 +1481,6 @@ const Pather = { unit - use existing portal unit */ usePortal: function (targetArea, owner, unit) { - // while (!me.gameReady) { - // delay(30); - // } - if (targetArea && me.area === targetArea) return true; me.cancelUIFlags(); @@ -1407,7 +1546,7 @@ const Pather = { while (getTickCount() - tick < 500) { if (me.area !== preArea) { - this.lastPortalTick = getTickCount(); + Pather.lastPortalTick = getTickCount(); delay(100); return true; @@ -1569,7 +1708,7 @@ const Pather = { let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); if (preset) { - Skill.haveTK ? Pather.moveNearUnit(preset, 20, clearPath) : this.moveToUnit(preset, 0, 0, clearPath); + Skill.haveTK ? Pather.moveNearUnit(preset, 20, clearPath) : Pather.moveToUnit(preset, 0, 0, clearPath); let wp = Game.getObject("waypoint"); @@ -1586,7 +1725,7 @@ const Pather = { if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { delay(500); - !this.initialized && (this.initialized = true); + !Pather.initialized && (Pather.initialized = true); me.cancelUIFlags(); return true; @@ -1656,7 +1795,7 @@ const Pather = { continue; } - console.info(null, "ÿc0Moving from: " + Pather.getAreaName(currArea) + " to " + Pather.getAreaName(targetArea)); + console.info(null, "ÿc0Moving from: " + getAreaName(currArea) + " to " + getAreaName(targetArea)); if (!me.inTown) { Precast.doPrecast(false); @@ -1699,8 +1838,8 @@ const Pather = { for (let i = 0; i < 5; i++) { unit = Game.getObject(sdk.objects.Journal); - Misc.click(0, 0, unit); - delay(1000); + Packet.entityInteract(unit); + Misc.poll(() => getIsTalkingNPC(), 1000, 50); me.cancel(); if (this.usePortal(sdk.areas.CanyonofMagic)) { @@ -1774,14 +1913,14 @@ const Pather = { retry = 0; } else { if (retry > 3) { - console.warn("Failed to journeyTo " + Pather.getAreaName(area) + " currentarea: " + Pather.getAreaName(me.area)); + console.warn("Failed to journeyTo " + getAreaName(area) + " currentarea: " + getAreaName(me.area)); return false; } retry++; } } - console.info(false, "ÿc4MyArea: ÿc0" + Pather.getAreaName(me.area), "journeyTo"); + console.info(false, "ÿc4MyArea: ÿc0" + getAreaName(me.area), "journeyTo"); return me.area === area; }, @@ -1898,156 +2037,6 @@ const Pather = { return true; }, - - areaNames: [ - "None", - "Rogue Encampment", - "Blood Moor", - "Cold Plains", - "Stony Field", - "Dark Wood", - "Black Marsh", - "Tamoe Highland", - "Den Of Evil", - "Cave Level 1", - "Underground Passage Level 1", - "Hole Level 1", - "Pit Level 1", - "Cave Level 2", - "Underground Passage Level 2", - "Hole Level 2", - "Pit Level 2", - "Burial Grounds", - "Crypt", - "Mausoleum", - "Forgotten Tower", - "Tower Cellar Level 1", - "Tower Cellar Level 2", - "Tower Cellar Level 3", - "Tower Cellar Level 4", - "Tower Cellar Level 5", - "Monastery Gate", - "Outer Cloister", - "Barracks", - "Jail Level 1", - "Jail Level 2", - "Jail Level 3", - "Inner Cloister", - "Cathedral", - "Catacombs Level 1", - "Catacombs Level 2", - "Catacombs Level 3", - "Catacombs Level 4", - "Tristram", - "Moo Moo Farm", - "Lut Gholein", - "Rocky Waste", - "Dry Hills", - "Far Oasis", - "Lost City", - "Valley Of Snakes", - "Canyon Of The Magi", - "Sewers Level 1", - "Sewers Level 2", - "Sewers Level 3", - "Harem Level 1", - "Harem Level 2", - "Palace Cellar Level 1", - "Palace Cellar Level 2", - "Palace Cellar Level 3", - "Stony Tomb Level 1", - "Halls Of The Dead Level 1", - "Halls Of The Dead Level 2", - "Claw Viper Temple Level 1", - "Stony Tomb Level 2", - "Halls Of The Dead Level 3", - "Claw Viper Temple Level 2", - "Maggot Lair Level 1", - "Maggot Lair Level 2", - "Maggot Lair Level 3", - "Ancient Tunnels", - "Tal Rashas Tomb #1", - "Tal Rashas Tomb #2", - "Tal Rashas Tomb #3", - "Tal Rashas Tomb #4", - "Tal Rashas Tomb #5", - "Tal Rashas Tomb #6", - "Tal Rashas Tomb #7", - "Duriels Lair", - "Arcane Sanctuary", - "Kurast Docktown", - "Spider Forest", - "Great Marsh", - "Flayer Jungle", - "Lower Kurast", - "Kurast Bazaar", - "Upper Kurast", - "Kurast Causeway", - "Travincal", - "Spider Cave", - "Spider Cavern", - "Swampy Pit Level 1", - "Swampy Pit Level 2", - "Flayer Dungeon Level 1", - "Flayer Dungeon Level 2", - "Swampy Pit Level 3", - "Flayer Dungeon Level 3", - "Sewers Level 1", - "Sewers Level 2", - "Ruined Temple", - "Disused Fane", - "Forgotten Reliquary", - "Forgotten Temple", - "Ruined Fane", - "Disused Reliquary", - "Durance Of Hate Level 1", - "Durance Of Hate Level 2", - "Durance Of Hate Level 3", - "The Pandemonium Fortress", - "Outer Steppes", - "Plains Of Despair", - "City Of The Damned", - "River Of Flame", - "Chaos Sanctuary", - "Harrogath", - "Bloody Foothills", - "Frigid Highlands", - "Arreat Plateau", - "Crystalline Passage", - "Frozen River", - "Glacial Trail", - "Drifter Cavern", - "Frozen Tundra", - "Ancient's Way", - "Icy Cellar", - "Arreat Summit", - "Nihlathak's Temple", - "Halls Of Anguish", - "Halls Of Pain", - "Halls Of Vaught", - "Abaddon", - "Pit Of Acheron", - "Infernal Pit", - "Worldstone Keep Level 1", - "Worldstone Keep Level 2", - "Worldstone Keep Level 3", - "Throne Of Destruction", - "The Worldstone Chamber", - "Matron's Den", - "Forgotten Sands", - "Furnace of Pain", - "Tristram" - ], - - /* - Pather.getAreaName(area); - area - id of the area to get the name for - */ - getAreaName: function (area) { - if (["number", "string"].indexOf(typeof area) === -1) return "undefined"; - if (typeof area === "string") return area; - return (this.areaNames[area] || "undefined"); - }, }; Pather.nextAreas[sdk.areas.RogueEncampment] = sdk.areas.BloodMoor; diff --git a/d2bs/kolbot/libs/common/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js similarity index 88% rename from d2bs/kolbot/libs/common/Pickit.js rename to d2bs/kolbot/libs/core/Pickit.js index f749bca39..ea41f938b 100644 --- a/d2bs/kolbot/libs/common/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -5,6 +5,9 @@ * */ +/** + * @namespace Pickit + */ const Pickit = { gidList: [], invoLocked: true, @@ -95,52 +98,6 @@ const Pickit = { return check === 4; }, - /** - * @param {number} quality - */ - itemQualityToName: function (quality) { - let qualNames = ["", "lowquality", "normal", "superior", "magic", "set", "rare", "unique", "crafted"]; - return qualNames[quality]; - }, - - /** - * @param {ItemUnit} unit - * @param {boolean} [type] - */ - itemColor: function (unit, type) { - type === undefined && (type = true); - - if (type) { - switch (unit.itemType) { - case sdk.items.type.Gold: - return "ÿc4"; - case sdk.items.type.Rune: - return "ÿc8"; - case sdk.items.type.HealingPotion: - return "ÿc1"; - case sdk.items.type.ManaPotion: - return "ÿc3"; - case sdk.items.type.RejuvPotion: - return "ÿc;"; - } - } - - switch (unit.quality) { - case sdk.items.quality.Magic: - return "ÿc3"; - case sdk.items.quality.Set: - return "ÿc2"; - case sdk.items.quality.Rare: - return "ÿc9"; - case sdk.items.quality.Unique: - return "ÿc4"; - case sdk.items.quality.Crafted: - return "ÿc8"; - } - - return "ÿc0"; - }, - /** * @param {ItemUnit} unit */ @@ -349,11 +306,16 @@ const Pickit = { return rval; }, + track: { + lastItem: null, + }, + /** * @param {ItemUnit} unit * @param {PickitResult} status * @param {string} keptLine * @param {number} retry + * @todo figure out why sometimes we double print picking up an item, gut feeling is recursion somewhere */ pickItem: function (unit, status, keptLine, retry = 3) { /** @@ -365,7 +327,7 @@ const Pickit = { this.type = unit.itemType; this.classid = unit.classid; this.name = unit.name; - this.color = Pickit.itemColor(unit); + this.color = Item.color(unit); this.gold = unit.getStat(sdk.stats.Gold); this.dist = (unit.distance || Infinity); this.useTk = (Skill.haveTK && Pickit.tkable.includes(this.type) @@ -394,11 +356,21 @@ const Pickit = { MainLoop: for (let i = 0; i < retry; i += 1) { + if (me.dead) return false; + if (this.track.lastItem === gid) { + D2Bot.printToConsole("Attempt to pick same item detected"); + return true; + } + + // can't find the item if (!Game.getItem(-1, -1, gid)) { - break; + return false; } - if (me.dead) return false; + if (me.getItem(item.classid, -1, item.gid)) { + console.debug("Already picked item"); + break; + } while (!me.idle) { delay(40); @@ -475,22 +447,21 @@ const Pickit = { switch (status) { case Pickit.Result.WANTED: console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (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); } 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; @@ -504,6 +475,8 @@ const Pickit = { break; } + + this.track.lastItem = item.gid; } return true; @@ -526,7 +499,7 @@ const Pickit = { case Pickit.Result.UNWANTED: case Pickit.Result.TRASH: // if we've got items to sell then we can make room as long as we can get to town - return Town.canTpToTown(); + return me.canTpToTown(); default: // Check if a kept item can be stashed return Town.canStash(item); } @@ -536,6 +509,12 @@ const Pickit = { return false; }, + /** + * Sometimes we get in a recursion scenario with pickItems, maybe globally keep track of items instead + * so when we pick the item during recursion we don't end up getting the double print. Also maybe use a Set data structure? + */ + pickList: new Set(), + /** * @param {number} range * @returns {boolean} If we picked items @@ -544,6 +523,9 @@ const Pickit = { if (me.dead) return false; let needMule = false; + /** + * @type {ItemUnit[]} + */ let pickList = []; Town.clearBelt(); @@ -556,8 +538,10 @@ const Pickit = { if (item) { do { - if (item.onGroundOrDropping && getDistance(me, item) <= range) { + if (item.onGroundOrDropping && getDistance(me, item) <= range && !this.pickList.has(item.gid)) { pickList.push(copyUnit(item)); + // ensure uniqueness with set + this.pickList.add(item.gid); } } while (item.getNext()); } @@ -565,12 +549,14 @@ const Pickit = { while (pickList.length > 0) { if (me.dead) return false; pickList.sort(this.sortItems); - - const itemToPick = pickList.shift(); + let check = pickList.shift(); + // get the actual item again + const itemToPick = Game.getItem(check.classid, -1, check.gid); + const itemGid = itemToPick.gid; // 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(itemToPick).x !== undefined && itemToPick.onGroundOrDropping + if (itemToPick && copyUnit(itemToPick).x !== undefined && itemToPick.onGroundOrDropping && (Pather.useTeleport() || me.inTown || !checkCollision(me, itemToPick, sdk.collision.BlockWall))) { // Check if the item should be picked let status = this.checkItem(itemToPick); @@ -581,14 +567,14 @@ const Pickit = { // 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)) { - Town.fieldID() && (canFit = (itemToPick.gid !== undefined && Storage.Inventory.CanFit(itemToPick))); + me.fieldID() && (canFit = (itemToPick.gid !== undefined && Storage.Inventory.CanFit(itemToPick))); } // 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 " + Pickit.itemColor(itemToPick) + itemToPick.name); + console.log("ÿc7Trying to make room for " + Item.color(itemToPick) + itemToPick.name); // Go to town and do town chores if (Town.visitTown()) { @@ -598,14 +584,14 @@ const Pickit = { } // Town visit failed - abort - console.log("ÿc7Not enough room for " + Pickit.itemColor(itemToPick) + itemToPick.name); + console.log("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); return false; } // Can't make room - trigger automule - Misc.itemLogger("No room for", itemToPick); - console.log("ÿc7Not enough room for " + Pickit.color(itemToPick) + itemToPick.name); + Item.logger("No room for", itemToPick); + console.log("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); if (copyUnit(itemToPick).x !== undefined) { needMule = true; @@ -615,7 +601,12 @@ const Pickit = { } // Item can fit - pick it up - canFit && this.pickItem(itemToPick, status.result, status.line); + if (canFit && this.pickItem(itemToPick, status.result, status.line)) { + // picked it up so remove gid from set + this.pickList.delete(itemGid); + } + } else { + this.pickList.delete(itemGid); } } } @@ -648,10 +639,12 @@ const Pickit = { while (itemList.length > 0) { itemList.sort(this.sortFastPickItems); - item = copyUnit(itemList.shift()); + let check = itemList.shift(); + // we were passed the copied unit, lets find the real thing + item = Game.getItem(check.classid, -1, check.gid); // Check if the item unit is still valid - if (item.x !== undefined) { + if (item && item.x !== undefined) { let status = this.checkItem(item); if (status.result && this.canPick(item) && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType))) { diff --git a/d2bs/kolbot/libs/common/Precast.js b/d2bs/kolbot/libs/core/Precast.js similarity index 96% rename from d2bs/kolbot/libs/common/Precast.js rename to d2bs/kolbot/libs/core/Precast.js index ec1f9ed1a..4d8fa31ac 100644 --- a/d2bs/kolbot/libs/common/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -367,8 +367,14 @@ const Precast = new function () { }; // should the config check still be included even though its part of Skill.init? - // todo: durations - this.doPrecast = function (force = false) { + /** + * @description Handle precast related skills + * @param {boolean} force - force re-cast of all precast skills + * @param {boolean} partial - force re-cast of all state related precast skills + * @returns {boolean} sucessfully casted + * @todo durations + */ + this.doPrecast = function (force = false, partial = false) { if (!this.enabled) return false; while (!me.gameReady) { @@ -378,14 +384,14 @@ const Precast = new function () { let [buffSummons, forceBo] = [false, false]; // Force BO 30 seconds before it expires - if (this.haveCTA > -1) { - forceBo = (force - || (getTickCount() - this.skills.battleOrders.tick >= this.skills.battleOrders.duration - 30000) + if (Precast.haveCTA > -1) { + forceBo = (force || partial + || (getTickCount() - Precast.skills.battleOrders.tick >= Precast.skills.battleOrders.duration - 30000) || !me.getState(sdk.states.BattleCommand)); forceBo && this.precastCTA(forceBo); } - const needToCast = (state) => (force || !me.getState(state)); + const needToCast = (state) => (force || partial || !me.getState(state)); switch (me.classid) { case sdk.player.class.Amazon: @@ -465,6 +471,8 @@ const Precast = new function () { } })(); + Config.ActiveSummon && ClassAttack.raiseArmy(); + break; case sdk.player.class.Paladin: if (Skill.canUse(sdk.skills.HolyShield) && Precast.skills.holyShield.canUse && needToCast(sdk.states.HolyShield)) { diff --git a/d2bs/kolbot/libs/common/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js similarity index 77% rename from d2bs/kolbot/libs/common/Prototypes.js rename to d2bs/kolbot/libs/core/Prototypes.js index 162f26c39..73cc77853 100644 --- a/d2bs/kolbot/libs/common/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -2,16 +2,13 @@ * @filename Prototypes.js * @author kolton, theBGuy * @credit Jaenster -* @desc various 'Unit' and 'me' prototypes +* @desc various 'Unit' prototypes * */ // Ensure these are in polyfill.js !isIncluded("Polyfill.js") && include("Polyfill.js"); -// Make sure we have our util functions -!isIncluded("common/Util.js") && include("common/Util.js"); - -let sdk = require("../modules/sdk"); +!isIncluded("core/Me.js") && include("core/Me.js"); (function (global, original) { let firstRun = true; @@ -39,2578 +36,2068 @@ let sdk = require("../modules/sdk"); }; })([].filter.constructor("return this")(), getUnit); -// Check if unit is idle -Unit.prototype.__defineGetter__("idle", function () { - if (this.type > sdk.unittype.Player) throw new Error("Unit.idle: Must be used with player units."); - // Dead is pretty idle too - return (this.mode === sdk.player.mode.StandingOutsideTown || this.mode === sdk.player.mode.StandingInTown || this.mode === sdk.player.mode.Dead); -}); - -Unit.prototype.__defineGetter__("gold", function () { - return this.getStat(sdk.stats.Gold) + this.getStat(sdk.stats.GoldBank); -}); - -// Death check -Unit.prototype.__defineGetter__("dead", function () { - switch (this.type) { - case sdk.unittype.Player: - return this.mode === sdk.player.mode.Death || this.mode === sdk.player.mode.Dead; - case sdk.unittype.Monster: - return this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead; - default: - return false; - } -}); - -// Check if unit is in town -Unit.prototype.__defineGetter__("inTown", function () { - if (this.type > sdk.unittype.Player) throw new Error("Unit.inTown: Must be used with player units."); - return sdk.areas.Towns.includes(this.area); -}); - // Check if party unit is in town Party.prototype.__defineGetter__("inTown", function () { return sdk.areas.Towns.includes(this.area); }); -Unit.prototype.__defineGetter__("attacking", function () { - if (this.type > sdk.unittype.Monster) throw new Error("Unit.attacking: Must be used with Monster or Player units."); - switch (this.type) { - case sdk.unittype.Player: - return [ - sdk.player.mode.Attacking1, sdk.player.mode.Attacking2, sdk.player.mode.CastingSkill, sdk.player.mode.ThrowingItem, - sdk.player.mode.Kicking, sdk.player.mode.UsingSkill1, sdk.player.mode.UsingSkill2, sdk.player.mode.UsingSkill3, - sdk.player.mode.UsingSkill4, sdk.player.mode.SkillActionSequence - ].includes(this.mode); - case sdk.unittype.Monster: - return [ - sdk.monsters.mode.Attacking1, sdk.monsters.mode.Attacking2, sdk.monsters.mode.CastingSkill, - sdk.monsters.mode.UsingSkill1, sdk.monsters.mode.UsingSkill2, sdk.monsters.mode.UsingSkill3, sdk.monsters.mode.UsingSkill4 - ].includes(this.mode); - default: - return false; - } -}); - -Unit.prototype.__defineGetter__("durabilityPercent", function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.durabilityPercent: Must be used on items."); - if (this.getStat(sdk.stats.Quantity) || !this.getStat(sdk.stats.MaxDurability)) return 100; - return Math.round(this.getStat(sdk.stats.Durability) * 100 / this.getStat(sdk.stats.MaxDurability)); -}); - -// Open NPC menu -Unit.prototype.openMenu = function (addDelay) { - if (Config.PacketShopping) return Packet.openMenu(this); - if (this.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); - if (getUIFlag(sdk.uiflags.NPCMenu)) return true; - - addDelay === undefined && (addDelay = 0); - let pingDelay = (me.gameReady ? me.ping : 125); - - for (let i = 0; i < 5; i += 1) { - getDistance(me, this) > 4 && Pather.moveToUnit(this); - - Misc.click(0, 0, this); - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (getUIFlag(sdk.uiflags.NPCMenu)) { - delay(Math.max(700 + pingDelay, 500 + pingDelay * 2 + addDelay * 500)); - - return true; - } - - if (getInteractedNPC() && getTickCount() - tick > 1000) { - me.cancel(); - } - - delay(100); +Object.defineProperties(Unit.prototype, { + isChampion: { + get: function () { + return (this.spectype & sdk.monsters.spectype.Champion) > 0; + }, + }, + isUnique: { + get: function () { + return (this.spectype & sdk.monsters.spectype.Unique) > 0; + }, + }, + isMinion: { + get: function () { + return (this.spectype & sdk.monsters.spectype.Minion) > 0; + }, + }, + isSuperUnique: { + get: function () { + return (this.spectype & (sdk.monsters.spectype.Super | sdk.monsters.spectype.Unique)) > 0; + }, + }, + isSpecial: { + get: function () { + return (this.isChampion || this.isUnique || this.isSuperUnique); + }, + }, + isPlayer: { + get: function () { + return this.type === sdk.unittype.Player; + }, + }, + isMonster: { + get: function () { + return this.type === sdk.unittype.Monster; + }, + }, + isNPC: { + get: function () { + return this.type === sdk.unittype.Monster && this.getStat(sdk.stats.Alignment) === 2; + }, + }, + // todo - monster types + isPrimeEvil: { + get: function () { + return [ + sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, + sdk.monsters.Baal, sdk.monsters.BaalClone, sdk.monsters.UberDuriel, sdk.monsters.UberIzual, + sdk.monsters.UberMephisto, sdk.monsters.UberDiablo, sdk.monsters.UberBaal, sdk.monsters.Lilith, sdk.monsters.DiabloClone + ].includes(this.classid) || getBaseStat("monstats", this.classid, "primeevil"); + }, + }, + isBoss: { + get: function () { + return this.isPrimeEvil + || + [ + sdk.monsters.TheSmith, sdk.monsters.BloodRaven, sdk.monsters.Radament, sdk.monsters.Griswold, + sdk.monsters.TheSummoner, sdk.monsters.Izual, sdk.monsters.Hephasto, sdk.monsters.KorlictheProtector, + sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian, sdk.monsters.ListerTheTormenter, + sdk.monsters.TheCowKing, sdk.monsters.ColdwormtheBurrower, sdk.monsters.Nihlathak + ].includes(this.classid); + }, + }, + isGhost: { + get: function () { + return [ + sdk.monsters.Ghost1, sdk.monsters.Wraith1, sdk.monsters.Specter1, + sdk.monsters.Apparition, sdk.monsters.DarkShape, sdk.monsters.Ghost2, sdk.monsters.Wraith2, sdk.monsters.Specter2 + ].includes(this.classid) || getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Wraith; + }, + }, + isDoll: { + get: function () { + return [ + sdk.monsters.BoneFetish1, sdk.monsters.BoneFetish2, sdk.monsters.BoneFetish3, + sdk.monsters.SoulKiller3, sdk.monsters.StygianDoll2, sdk.monsters.StygianDoll6, sdk.monsters.SoulKiller + ].includes(this.classid); + }, + }, + isMonsterObject: { + get: function () { + return [ + sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, + sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, sdk.monsters.FireTower, + sdk.monsters.BarricadeDoor1, sdk.monsters.BarricadeDoor2, sdk.monsters.BarricadeWall1, sdk.monsters.BarricadeWall2, + sdk.monsters.CatapultS, sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, sdk.monsters.CatapultW, + sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.Hut, + ].includes(this.classid); + }, + }, + isMonsterEgg: { + get: function () { + return [ + sdk.monsters.SandMaggotEgg, sdk.monsters.RockWormEgg, sdk.monsters.DevourerEgg, sdk.monsters.GiantLampreyEgg, + sdk.monsters.WorldKillerEgg1, sdk.monsters.WorldKillerEgg2 + ].includes(this.classid); + }, + }, + isMonsterNest: { + get: function () { + return [ + sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, + sdk.monsters.BloodWingNest, sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest + ].includes(this.classid); + }, + }, + isBaalTentacle: { + get: function () { + return [ + sdk.monsters.Tentacle1, sdk.monsters.Tentacle2, + sdk.monsters.Tentacle3, sdk.monsters.Tentacle4, sdk.monsters.Tentacle5 + ].includes(this.classid); + }, + }, + isShaman: { + get: function () { + return [ + sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, + sdk.monsters.WarpedShaman, sdk.monsters.CarverShaman, sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2 + ].includes(this.classid); + }, + }, + isUnraveler: { + get: function () { + return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Unraveler; + }, + }, + isFallen: { + get: function () { + return [ + sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, + sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2 + ].includes(this.classid); + }, + }, + isBeetle: { + get: function () { + return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Scarab; + }, + }, + isWalking: { + get: function () { + return (this.mode === sdk.monsters.mode.Walking && (this.targetx !== this.x || this.targety !== this.y)); } - - sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, this.gid); - delay(pingDelay * 2 + 1); - Packet.cancelNPC(this); - delay(pingDelay * 2 + 1); - Packet.flash(me.gid); - } - - return false; -}; - -// mode = "Gamble", "Repair" or "Shop" -Unit.prototype.startTrade = function (mode) { - if (Config.PacketShopping) return Packet.startTrade(this, mode); - if (this.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); - console.log("Starting " + mode + " at " + this.name); - if (getUIFlag(sdk.uiflags.Shop)) return true; - - let menuId = mode === "Gamble" ? sdk.menu.Gamble : mode === "Repair" ? sdk.menu.TradeRepair : sdk.menu.Trade; - - for (let i = 0; i < 3; i += 1) { - // Incremental delay on retries - if (this.openMenu(i)) { - Misc.useMenu(menuId); - - let tick = getTickCount(); - - while (getTickCount() - tick < 1000) { - if (getUIFlag(sdk.uiflags.Shop) && this.itemcount > 0) { - delay(200); - console.log("Successfully started " + mode + " at " + this.name); - - return true; - } - - delay(25); - } - - me.cancel(); + }, + isRunning: { + get: function () { + return (this.mode === sdk.monsters.mode.Running && (this.targetx !== this.x || this.targety !== this.y)); } - } - - return false; -}; - -Unit.prototype.buy = function (shiftBuy, gamble) { - if (Config.PacketShopping) return Packet.buyItem(this, shiftBuy, gamble); - // Check if it's an item we want to buy - if (this.type !== sdk.unittype.Item) throw new Error("Unit.buy: Must be used on items."); - - // Check if it's an item belonging to a NPC - if (!getUIFlag(sdk.uiflags.Shop) || (this.getParent() && this.getParent().gid !== getInteractedNPC().gid)) { - throw new Error("Unit.buy: Must be used in shops."); - } - - // Can we afford the item? - if (me.gold < this.getItemCost(sdk.items.cost.ToBuy)) return false; - - let oldGold = me.gold; - let itemCount = me.itemcount; - - for (let i = 0; i < 3; i += 1) { - this.shop(shiftBuy ? 6 : 2); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { - if ((shiftBuy && me.gold < oldGold) || itemCount !== me.itemcount) { - delay(500); - - return true; + }, + isMoving: { + get: function () { + return (this.isWalking || this.isRunning); + }, + }, + isFrozen: { + get: function () { + return this.getState(sdk.states.FrozenSolid); + }, + }, + isChilled: { + get: function () { + return this.getState(sdk.states.Frozen); + }, + }, + extraStrong: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ExtraStrong); + }, + }, + extraFast: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ExtraFast); + }, + }, + cursed: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.Cursed); + }, + }, + magicResistant: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.MagicResistant); + }, + }, + fireEnchanted: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.FireEnchanted); + }, + }, + lightningEnchanted: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.LightningEnchanted); + }, + }, + coldEnchanted: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ColdEnchanted); + }, + }, + manBurn: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ManaBurn); + }, + }, + teleportation: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.Teleportation); + }, + }, + spectralHit: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.SpectralHit); + }, + }, + stoneSkin: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.StoneSkin); + }, + }, + multiShot: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.MultipleShots); + }, + }, + resPenalty: { + value: me.classic ? [0, 20, 50][me.diff] : [0, 40, 100][me.diff], + writable: true + }, + fireRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResFire) && (modifier += 75); } - - delay(10); + return this.getStat(sdk.stats.FireResist) - me.resPenalty - modifier; } - } - - return false; -}; - -// Item owner name -Unit.prototype.__defineGetter__("parentName", - function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.parentName: Must be used with item units."); - - let parent = this.getParent(); - - return parent ? parent.name : false; - }); - -// You MUST use a delay after Unit.sell() if using custom scripts. delay(500) works best, dynamic delay is used when identifying/selling (500 - item id time) -Unit.prototype.sell = function () { - if (Config.PacketShopping) return Packet.sellItem(this); - - // Check if it's an item we want to buy - if (this.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); - if (!this.sellable) { - console.error((new Error("Item is unsellable"))); - return false; - } - - // Check if it's an item belonging to a NPC - if (!getUIFlag(sdk.uiflags.Shop)) throw new Error("Unit.sell: Must be used in shops."); - - let itemCount = me.itemcount; - - for (let i = 0; i < 5; i += 1) { - this.shop(1); - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (me.itemcount !== itemCount) { - //delay(500); - - return true; + }, + coldRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResCold) && (modifier += 75); + me.getState(sdk.states.Thawing) && (modifier += 50); } - - delay(10); + return this.getStat(sdk.stats.ColdResist) - me.resPenalty - modifier; } - } - - return false; -}; - -Unit.prototype.toCursor = function (usePacket = false) { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.toCursor: Must be used with items."); - if (me.itemoncursor && this.mode === sdk.items.mode.onCursor) return true; - - this.location === sdk.storage.Stash && Town.openStash(); - this.location === sdk.storage.Cube && Cubing.openCube(); - - if (usePacket) return Packet.itemToCursor(this); - - for (let i = 0; i < 3; i += 1) { - try { - if (this.mode === sdk.items.mode.Equipped) { - // fix for equipped items (cubing viper staff for example) - clickItem(sdk.clicktypes.click.item.Left, this.bodylocation); - } else { - clickItem(sdk.clicktypes.click.item.Left, this); + }, + lightRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResLighting) && (modifier += 75); } - } catch (e) { - return false; + return this.getStat(sdk.stats.LightResist) - me.resPenalty - modifier; } - - let tick = getTickCount(); - - while (getTickCount() - tick < 1000) { - if (me.itemoncursor) { - delay(200); - - return true; + }, + poisonRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResPoison) && (modifier += 75); + me.getState(sdk.states.Antidote) && (modifier += 50); } - - delay(10); + return this.getStat(sdk.stats.PoisonResist) - me.resPenalty - modifier; } - } - - return false; -}; - -Unit.prototype.drop = function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.drop: Must be used with items. Unit Name: " + this.name); - if (!this.toCursor()) return false; - - let tick = getTickCount(); - let timeout = Math.max(1000, me.ping * 6); - - while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash) || !me.gameReady) { - if (getTickCount() - tick > timeout) return false; - - if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { - me.cancel(0); + }, + hpPercent: { + get: function () { + return Math.round(this.hp * 100 / this.hpmax); } - - delay(me.ping * 2 + 100); - } - - for (let i = 0; i < 3; i += 1) { - clickMap(0, 0, me.x, me.y); - delay(40); - clickMap(2, 0, me.x, me.y); - - tick = getTickCount(); - - while (getTickCount() - tick < 500) { - if (!me.itemoncursor) { - delay(200); - - return true; + }, + attacking: { + get: function () { + if (this.type > sdk.unittype.Monster) throw new Error("Unit.attacking: Must be used with Monster or Player units."); + switch (this.type) { + case sdk.unittype.Player: + return [ + sdk.player.mode.Attacking1, sdk.player.mode.Attacking2, sdk.player.mode.CastingSkill, sdk.player.mode.ThrowingItem, + sdk.player.mode.Kicking, sdk.player.mode.UsingSkill1, sdk.player.mode.UsingSkill2, sdk.player.mode.UsingSkill3, + sdk.player.mode.UsingSkill4, sdk.player.mode.SkillActionSequence + ].includes(this.mode); + case sdk.unittype.Monster: + return [ + sdk.monsters.mode.Attacking1, sdk.monsters.mode.Attacking2, sdk.monsters.mode.CastingSkill, + sdk.monsters.mode.UsingSkill1, sdk.monsters.mode.UsingSkill2, sdk.monsters.mode.UsingSkill3, sdk.monsters.mode.UsingSkill4 + ].includes(this.mode); + default: + return false; } - - delay(10); } - } - - return false; -}; - -me.walk = () => me.runwalk = 0; -me.run = () => me.runwalk = 1; - -// calling me.ping can cause issues, use this instead to assign a value -// might need work to be more accurate but works for now -me.getPingDelay = function () { - // single-player - if (!me.gameserverip) return 25; - let pingDelay = me.gameReady ? me.ping : 250; - pingDelay < 10 && (pingDelay = 50); - return pingDelay; -}; - -/** - * @description use consumable item, fixes issue with interact() returning false even if we used an item - * @returns boolean - */ -Unit.prototype.use = function () { - if (this === undefined || !this.type) return false; - if (this.type !== sdk.unittype.Item) throw new Error("Unit.use: Must be used with items. Unit Name: " + this.name); - if (!getBaseStat("items", this.classid, "useable")) throw new Error("Unit.use: Must be used with consumable items. Unit Name: " + this.name); - - let gid = this.gid; - let pingDelay = me.getPingDelay(); - let quantity = 0; - let iType = this.itemType; - let checkQuantity = false; - - switch (this.location) { - case sdk.storage.Stash: - case sdk.storage.Inventory: - if (this.isInStash && !Town.openStash()) return false; - // doesn't work, not sure why but it's missing something - //new PacketBuilder().byte(sdk.packets.send.UseItem).dword(gid).dword(this.x).dword(this.y).send(); - checkQuantity = iType === sdk.items.type.Book; - checkQuantity && (quantity = this.getStat(sdk.stats.Quantity)); - this.interact(); // use interact instead, was hoping to skip this since its really just doing the same thing over but oh well - - break; - case sdk.storage.Belt: - new PacketBuilder().byte(sdk.packets.send.UseBeltItem).dword(gid).dword(0).dword(0).send(); - - break; - default: - return false; - } - - if (checkQuantity) { - return Misc.poll(() => this.getStat(sdk.stats.Quantity) < quantity, 200 + pingDelay, 50); - } else { - return Misc.poll(() => !Game.getItem(-1, -1, gid), 200 + pingDelay, 50); - } -}; - -me.findItem = function (id = -1, mode = -1, loc = -1, quality = -1) { - let item = me.getItem(id, mode); - - if (item) { - do { - if ((loc === -1 || item.location === loc) && (quality === -1 || item.quality === quality)) { - return item; - } - } while (item.getNext()); - } - - return false; -}; - -me.findItems = function (id = -1, mode = -1, loc = false) { - let list = []; - let item = me.getItem(id, mode); - - if (item) { - do { - if (loc) { - if (item.location === loc) { - list.push(copyUnit(item)); - } - } else { - list.push(copyUnit(item)); - } - } while (item.getNext()); - } - - return list; -}; - -me.cancelUIFlags = function () { - while (!me.gameReady) { - delay(25); - } - - const flags = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, - sdk.uiflags.Waypoint, sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Stash, - sdk.uiflags.Cube, sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.SubmitItem - ]; - - for (let i = 0; i < flags.length; i++) { - if (getUIFlag(flags[i]) && me.cancel()) { - delay(250); - i = 0; // Reset + }, + idle: { + get: function () { + if (this.type > sdk.unittype.Player) throw new Error("Unit.idle: Must be used with player units."); + // Dead is pretty idle too + return (this.mode === sdk.player.mode.StandingOutsideTown || this.mode === sdk.player.mode.StandingInTown || this.mode === sdk.player.mode.Dead); } - } -}; - -me.switchWeapons = function (slot) { - if (this.gametype === sdk.game.gametype.Classic || (slot !== undefined && this.weaponswitch === slot)) { - return true; - } - - while (typeof me !== "object") { - delay(10); - } - - while (!me.gameReady) { - delay(25); - } - - let originalSlot = this.weaponswitch; - let switched = false; - let packetHandler = (bytes) => bytes.length > 0 && bytes[0] === sdk.packets.recv.WeaponSwitch && (switched = true) && false; // false to not block - addEventListener("gamepacket", packetHandler); - try { - for (let i = 0; i < 10; i += 1) { - for (let j = 10; --j && me.idle;) { - delay(3); - } - - i > 0 && delay(10); - !switched && sendPacket(1, sdk.packets.send.SwapWeapon); // Swap weapons - - let tick = getTickCount(); - while (getTickCount() - tick < 300) { - if (switched || originalSlot !== me.weaponswitch) { - delay(50); - return true; - } - - delay(3); + }, + gold: { + get: function () { + return this.getStat(sdk.stats.Gold) + this.getStat(sdk.stats.GoldBank); + } + }, + dead: { + get: function () { + switch (this.type) { + case sdk.unittype.Player: + return this.mode === sdk.player.mode.Death || this.mode === sdk.player.mode.Dead; + case sdk.unittype.Monster: + return this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead; + default: + return false; } - // Retry } - } finally { - removeEventListener("gamepacket", packetHandler); - } - - return false; -}; - -// Returns the number of frames needed to cast a given skill at a given FCR for a given char. -me.castingFrames = function (skillId, fcr, charClass) { - if (skillId === undefined) return 0; - - fcr === undefined && (fcr = me.FCR); - charClass === undefined && (charClass = this.classid); - - // https://diablo.fandom.com/wiki/Faster_Cast_Rate - let effectiveFCR = Math.min(75, Math.floor(fcr * 120 / (fcr + 120)) | 0); - let isLightning = skillId === sdk.skills.Lightning || skillId === sdk.skills.ChainLightning; - let baseCastRate = [20, isLightning ? 19 : 14, 16, 16, 14, 15, 17][charClass]; - let animationSpeed = { - normal: 256, - human: 208, - wolf: 229, - bear: 228 - }[charClass === sdk.player.class.Druid ? (me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear)) : "normal"]; - return Math.ceil(256 * baseCastRate / Math.floor(animationSpeed * (100 + effectiveFCR) / 100) - (isLightning ? 0 : 1)); -}; - -// Returns the duration in seconds needed to cast a given skill at a given FCR for a given char. -me.castingDuration = function (skillId, fcr = me.FCR, charClass = me.classid) { - return (me.castingFrames(skillId, fcr, charClass) / 25); -}; - -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; -}; - -/** - * @description Returns item given by itemInfo - * @param itemInfo object - - * { - * classid: Number, - * itemtype: Number, - * quality: Number, - * runeword: Boolean, - * ethereal: Boolean, - * name: getLocaleString(id) || localeStringId, - * equipped: Boolean || Number (bodylocation) - * } - * @returns Unit[] - */ -Unit.prototype.checkItem = function (itemInfo) { - if (this === undefined || this.type > 1 || typeof itemInfo !== "object") return {have: false, item: null}; - - const itemObj = Object.assign({}, { - classid: -1, - itemtype: -1, - quality: -1, - runeword: null, - ethereal: null, - equipped: null, - basetype: null, - name: "" - }, itemInfo); - - // convert id into string - typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - - let items = this.getItemsEx() - .filter(function (item) { - return (!item.questItem - && (itemObj.classid === -1 || item.classid === itemObj.classid) - && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) - && (itemObj.quality === -1 || item.quality === itemObj.quality) - && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) - && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) - && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) - && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) - && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) - ); - }); - if (items.length > 0) { - return { - have: true, - item: copyUnit(items.first()) - }; - } else { - return { - have: false, - item: null - }; - } -}; - -/** - * @description Returns first item given by itemInfo - * @param itemInfo array of objects - - * { - * classid: Number, - * itemtype: Number, - * quality: Number, - * runeword: Boolean, - * ethereal: Boolean, - * name: getLocaleString(id) || localeStringId, - * equipped: Boolean || Number (bodylocation) - * } - * @returns Unit[] - */ -Unit.prototype.findFirst = function (itemInfo = []) { - if (this === undefined || this.type > 1) return {have: false, item: null}; - if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return {have: false, item: null}; - let itemList = this.getItemsEx(); - - for (let i = 0; i < itemInfo.length; i++) { - const itemObj = Object.assign({}, { - classid: -1, - itemtype: -1, - quality: -1, - runeword: null, - ethereal: null, - equipped: null, - name: "" - }, itemInfo[i]); - - // convert id into string - typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - - let items = itemList - .filter(function (item) { - return (!item.questItem - && (itemObj.classid === -1 || item.classid === itemObj.classid) - && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) - && (itemObj.quality === -1 || item.quality === itemObj.quality) - && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) - && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) - && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) - && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) - ); - }); - if (items.length > 0) { - return { - have: true, - item: copyUnit(items.first()) - }; + }, + inTown: { + get: function () { + if (this.type > sdk.unittype.Player) throw new Error("Unit.inTown: Must be used with player units."); + return sdk.areas.Towns.includes(this.area); } } - - return { - have: false, - item: null - }; -}; +}); /** - * @description Returns boolean if we have all the items given by itemInfo - * @param itemInfo array of objects - - * { - * classid: Number, - * itemtype: Number, - * quality: Number, - * runeword: Boolean, - * ethereal: Boolean, - * name: getLocaleString(id) || localeStringId, - * equipped: Boolean || Number (bodylocation) - * } - * @returns Boolean + * @extends ItemUnit */ -Unit.prototype.haveAll = function (itemInfo = [], returnIfSome = false) { - if (this === undefined || this.type > 1) return false; - // if an object but not an array convert to array - !Array.isArray(itemInfo) && typeof itemInfo === "object" && (itemInfo = [itemInfo]); - if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return false; - let itemList = this.getItemsEx(); - let haveAll = false; - let checkedGids = []; - - for (let i = 0; i < itemInfo.length; i++) { - const itemObj = Object.assign({}, { - classid: -1, - itemtype: -1, - quality: -1, - runeword: null, - ethereal: null, - equipped: null, - basetype: null, - name: "" - }, itemInfo[i]); - - // convert id into string - typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - - let items = itemList - .filter(function (item) { - return (!item.questItem - && (checkedGids.indexOf(item.gid) === -1) - && (itemObj.classid === -1 || item.classid === itemObj.classid) - && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) - && (itemObj.quality === -1 || item.quality === itemObj.quality) - && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) - && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) - && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) - && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) - && (!itemObj.name.length || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) - ); - }); - if (items.length > 0) { - if (returnIfSome) return true; - checkedGids.push(items.first().gid); - haveAll = true; - } else { - if (returnIfSome) continue; - return false; +Object.defineProperties(Unit.prototype, { + isEquipped: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Equipped; } - } - - return haveAll; -}; - -Unit.prototype.haveSome = function (itemInfo = []) { - return this.haveAll(itemInfo, true); -}; - -/** - * @description Return the items of a player, or an empty array - * @param args - * @returns Unit[] - */ -Unit.prototype.getItems = function (...args) { - let items = []; - let item = this.getItem.apply(this, args); - - if (item) { - do { - items.push(copyUnit(item)); - } while (item.getNext()); - } - - return Array.isArray(items) ? items : []; -}; - -Unit.prototype.getItemsEx = function (...args) { - let items = []; - let item = this.getItem.apply(this, args); - - if (item) { - do { - items.push(copyUnit(item)); - } while (item.getNext()); - } - - return items; -}; - -Unit.prototype.getPrefix = function (id) { - switch (typeof id) { - case "number": - if (typeof this.prefixnums !== "object") return this.prefixnum === id; - - for (let i = 0; i < this.prefixnums.length; i += 1) { - if (id === this.prefixnums[i]) { - return true; - } + }, + isEquippedCharm: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return (this.location === sdk.storage.Inventory && [sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(this.itemType)); } - - break; - case "string": - if (typeof this.prefixes !== "object") { - return this.prefix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); + }, + isInInventory: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Inventory && this.mode === sdk.items.mode.inStorage; } - - for (let i = 0; i < this.prefixes.length; i += 1) { - if (id.replace(/\s+/g, "").toLowerCase() === this.prefixes[i].replace(/\s+/g, "").toLowerCase()) { - return true; - } + }, + isInStash: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Stash && this.mode === sdk.items.mode.inStorage; } - - break; - } - - return false; -}; - -Unit.prototype.getSuffix = function (id) { - switch (typeof id) { - case "number": - if (typeof this.suffixnums !== "object") return this.suffixnum === id; - - for (let i = 0; i < this.suffixnums.length; i += 1) { - if (id === this.suffixnums[i]) { - return true; - } + }, + isInCube: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Cube && this.mode === sdk.items.mode.inStorage; } - - break; - case "string": - if (typeof this.suffixes !== "object") { - return this.suffix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); + }, + isInStorage: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.mode === sdk.items.mode.inStorage && [sdk.storage.Inventory, sdk.storage.Cube, sdk.storage.Stash].includes(this.location); } - - for (let i = 0; i < this.suffixes.length; i += 1) { - if (id.replace(/\s+/g, "").toLowerCase() === this.suffixes[i].replace(/\s+/g, "").toLowerCase()) { - return true; + }, + isInBelt: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Belt && this.mode === sdk.items.mode.inBelt; + } + }, + isOnMain: { + get: function () { + if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; + return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); + } + }, + isOnSwap: { + get: function () { + if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; + switch (me.weaponswitch) { + case sdk.player.slot.Main: + return [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(this.bodylocation); + case sdk.player.slot.Secondary: + return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); } + return false; } - - break; - } - - return false; -}; - -Unit.prototype.__defineGetter__("dexreq", - function () { - let ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - let baseReq = getBaseStat("items", this.classid, "reqdex"); - let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - - return Math.max(finalReq, 0); - }); - -Unit.prototype.__defineGetter__("strreq", - function () { - let ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - let baseReq = getBaseStat("items", this.classid, "reqstr"); - let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - - return Math.max(finalReq, 0); - }); - -Unit.prototype.__defineGetter__("itemclass", - function () { - if (getBaseStat("items", this.classid, "code") === undefined) return 0; - if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ultracode")) return 2; - if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ubercode")) return 1; - - return 0; - }); - -Unit.prototype.getStatEx = function (id, subid) { - let temp, rval, regex; - - switch (id) { - case sdk.stats.AllRes: - // calculates all res, doesn't exist though - // 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.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; + }, + identified: { + get: function () { + // Can't tell, as it isn't an item + if (this.type !== sdk.unittype.Item) return undefined; + // Is also true for white items + return this.getFlag(sdk.items.flags.Identified); } + }, + ethereal: { + get: function () { + // Can't tell, as it isn't an item + if (this.type !== sdk.unittype.Item) return undefined; + return this.getFlag(sdk.items.flags.Ethereal); + } + }, + twoHanded: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return getBaseStat("items", this.classid, "2handed") === 1; + } + }, + oneOrTwoHanded: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return getBaseStat("items", this.classid, "1or2handed") === 1; + } + }, + strictlyTwoHanded: { + get: function () { + return this.twoHanded && !this.oneOrTwoHanded; + } + }, + runeword: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return !!this.getFlag(sdk.items.flags.Runeword); + } + }, + questItem: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return (this.itemType === sdk.items.type.Quest + || [ + sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, + sdk.items.quest.ViperAmulet, sdk.items.quest.DecoyGidbinn, sdk.items.quest.TheGidbinn, sdk.items.quest.KhalimsFlail, + sdk.items.quest.KhalimsWill, sdk.items.quest.HellForgeHammer, sdk.items.quest.StandardofHeroes + ].includes(this.classid)); + } + }, + sellable: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + if (this.getItemCost(sdk.items.cost.ToSell) <= 1) return false; + return (!this.questItem + && [ + sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction, sdk.items.quest.DiablosHorn, + sdk.items.quest.BaalsEye, sdk.items.quest.MephistosBrain, sdk.items.quest.TokenofAbsolution, sdk.items.quest.TwistedEssenceofSuffering, + sdk.items.quest.ChargedEssenceofHatred, sdk.items.quest.BurningEssenceofTerror, sdk.items.quest.FesteringEssenceofDestruction + ].indexOf(this.classid) === -1); + } + }, + lowquality: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.LowQuality; + }, + }, + normal: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Normal; + }, + }, + superior: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Superior; + }, + }, + magic: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Magic; + }, + }, + set: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Set; + }, + }, + rare: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Rare; + }, + }, + unique: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Unique; + }, + }, + crafted: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Crafted; + }, + }, + sockets: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.getStat(sdk.stats.NumSockets); + }, + }, + onGroundOrDropping: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return (this.mode === sdk.items.mode.onGround || this.mode === sdk.items.mode.Dropping); + }, + }, + isShield: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads].includes(this.itemType); + }, + }, + isAnni: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.unique && this.itemType === sdk.items.type.SmallCharm; + }, + }, + isTorch: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.unique && this.itemType === sdk.items.type.LargeCharm; + }, + }, + isGheeds: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.unique && this.itemType === sdk.items.type.GrandCharm; + }, + }, + prettyPrint: { + get: function () { + if (this.type !== sdk.unittype.Item) return this.name; + return this.fname.split("\n").reverse().join(" "); + } + }, + durabilityPercent: { + get: function () { + if (this.type !== sdk.unittype.Item) throw new Error("Unit.durabilityPercent: Must be used on items."); + if (this.getStat(sdk.stats.Quantity) || !this.getStat(sdk.stats.MaxDurability)) return 100; + return Math.round(this.getStat(sdk.stats.Durability) * 100 / this.getStat(sdk.stats.MaxDurability)); + } + }, +}); - break; - case sdk.stats.MinDamage: - case sdk.stats.MaxDamage: - if (subid === 1) { - temp = this.getStat(-1); - rval = 0; - - for (let 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; - } +// Open NPC menu +Unit.prototype.openMenu = function (addDelay) { + if (Config.PacketShopping) return Packet.openMenu(this); + if (this.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); + if (getUIFlag(sdk.uiflags.NPCMenu)) return true; - if (this.getStat(temp[i][0]) > 0 && this.getStat(temp[i][0]) > temp[i][2]) { - rval = temp[i][2]; // Store the potential +dmg value - } + addDelay === undefined && (addDelay = 0); + let pingDelay = (me.gameReady ? me.ping : 125); - break; - } - } + for (let i = 0; i < 5; i += 1) { + getDistance(me, this) > 4 && Pather.moveToUnit(this); - return 0; - } + Misc.click(0, 0, this); + let tick = getTickCount(); - break; - case sdk.stats.Defense: - if (subid === 0) { - if ([0, 1].indexOf(this.mode) < 0) { - break; - } + while (getTickCount() - tick < 5000) { + if (getUIFlag(sdk.uiflags.NPCMenu)) { + delay(Math.max(700 + pingDelay, 500 + pingDelay * 2 + addDelay * 500)); - 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); + return true; } - // can fail sometimes - !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); - } - } + if (getInteractedNPC() && getTickCount() - tick > 1000) { + me.cancel(); } - return 0; + delay(100); } - break; - case sdk.stats.PoisonMinDamage: - if (subid === 1) { - return Math.round(this.getStat(sdk.stats.PoisonMinDamage) * this.getStat(sdk.stats.PoisonLength) / 256); - } + sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, this.gid); + delay(pingDelay * 2 + 1); + Packet.cancelNPC(this); + delay(pingDelay * 2 + 1); + Packet.flash(me.gid); + } - 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 false; +}; - return 0; - } +// mode = "Gamble", "Repair" or "Shop" +Unit.prototype.startTrade = function (mode) { + if (Config.PacketShopping) return Packet.startTrade(this, mode); + if (this.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); + console.log("Starting " + mode + " at " + this.name); + if (getUIFlag(sdk.uiflags.Shop)) return true; - break; - case sdk.stats.AddSkillTab: - if (subid === undefined) { - temp = Object.values(sdk.skills.tabs); + let menuId = mode === "Gamble" ? sdk.menu.Gamble : mode === "Repair" ? sdk.menu.TradeRepair : sdk.menu.Trade; - for (let i = 0; i < temp.length; i += 1) { - let sTab = this.getStat(sdk.stats.AddSkillTab, temp[i]); - if (sTab) return sTab; - } + for (let i = 0; i < 3; i += 1) { + // Incremental delay on retries + if (this.openMenu(i)) { + Misc.useMenu(menuId); - return 0; - } + let tick = getTickCount(); - 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); + while (getTickCount() - tick < 1000) { + if (getUIFlag(sdk.uiflags.Shop) && this.itemcount > 0) { + delay(200); + console.log("Successfully started " + mode + " at " + this.name); - if (temp.hasOwnProperty(id)) { - if (temp[id] instanceof Array) { - for (let i = 0; i < temp[id].length; i += 1) { - if (temp[id][i] !== undefined) { - return temp[id][i].skill; - } - } - } else { - return temp[id].skill; + return true; } + + delay(25); } - return 0; + me.cancel(); } + } - if (subid === 2) { - temp = this.getStat(-2); + return false; +}; - if (temp.hasOwnProperty(id)) { - if (temp[id] instanceof Array) { - for (let i = 0; i < temp[id].length; i += 1) { - if (temp[id][i] !== undefined) { - return temp[id][i].level; - } - } - } else { - return temp[id].level; - } +/** + * optionally repair all or a single item + * @todo improve this, at the moment it's here as more of a proof of concept + */ +Unit.prototype.repairItem = function () { + // lets check if we have and can afford to repair this item + if (me.gold < this.getItemCost(2)) return false; + let npc = getInteractedNPC(); + if (!npc || npc.name.toLowerCase() !== Town.tasks[me.act - 1].Repair) return false; + // if (!this.startTrade("Repair")) return false; + let preDurability = this.getStat(sdk.stats.Durability); + new PacketBuilder().byte(0x35).dword(npc.gid).dword(this.gid).dword(1/* 1 for single item | 0 for all*/).dword(0).send(); + return Misc.poll(() => this.getStat(sdk.stats.Durability) !== preDurability, 500, 50); +}; + +Unit.prototype.buy = function (shiftBuy, gamble) { + if (Config.PacketShopping) return Packet.buyItem(this, shiftBuy, gamble); + // Check if it's an item we want to buy + if (this.type !== sdk.unittype.Item) throw new Error("Unit.buy: Must be used on items."); + + // Check if it's an item belonging to a NPC + if (!getUIFlag(sdk.uiflags.Shop) || (this.getParent() && this.getParent().gid !== getInteractedNPC().gid)) { + throw new Error("Unit.buy: Must be used in shops."); + } + + // Can we afford the item? + if (me.gold < this.getItemCost(sdk.items.cost.ToBuy)) return false; + + let oldGold = me.gold; + let itemCount = me.itemcount; + + for (let i = 0; i < 3; i += 1) { + this.shop(shiftBuy ? 6 : 2); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { + if ((shiftBuy && me.gold < oldGold) || itemCount !== me.itemcount) { + delay(500); + + return true; } - return 0; + delay(10); } + } - 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; + return false; +}; + +// Item owner name +Unit.prototype.__defineGetter__("parentName", + function () { + if (this.type !== sdk.unittype.Item) throw new Error("Unit.parentName: Must be used with item units."); + + let parent = this.getParent(); + + return parent ? parent.name : false; + }); + +// You MUST use a delay after Unit.sell() if using custom scripts. delay(500) works best, dynamic delay is used when identifying/selling (500 - item id time) +Unit.prototype.sell = function () { + if (Config.PacketShopping) return Packet.sellItem(this); + + // Check if it's an item we want to buy + if (this.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); + if (!this.sellable) { + console.error((new Error("Item is unsellable"))); + return false; } - if (this.getFlag(sdk.items.flags.Runeword)) { - switch (id) { - case sdk.stats.ArmorPercent: - if ([0, 1].indexOf(this.mode) < 0) { - break; - } + // Check if it's an item belonging to a NPC + if (!getUIFlag(sdk.uiflags.Shop)) throw new Error("Unit.sell: Must be used in shops."); - !this.desc && (this.desc = this.description); + let itemCount = me.itemcount; - if (this.desc) { - temp = this.desc.split("\n"); + for (let i = 0; i < 5; i += 1) { + this.shop(1); - 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); - } - } + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (me.itemcount !== itemCount) { + //delay(500); + + return true; } - return 0; - case sdk.stats.EnhancedDamage: - if ([0, 1].indexOf(this.mode) < 0) { - break; + delay(10); + } + } + + return false; +}; + +Unit.prototype.toCursor = function (usePacket = false) { + if (this.type !== sdk.unittype.Item) throw new Error("Unit.toCursor: Must be used with items."); + if (me.itemoncursor && this.mode === sdk.items.mode.onCursor) return true; + + this.location === sdk.storage.Stash && Town.openStash(); + this.location === sdk.storage.Cube && Cubing.openCube(); + + if (usePacket) return Packet.itemToCursor(this); + + for (let i = 0; i < 3; i += 1) { + try { + if (this.mode === sdk.items.mode.Equipped) { + // fix for equipped items (cubing viper staff for example) + clickItem(sdk.clicktypes.click.item.Left, this.bodylocation); + } else { + clickItem(sdk.clicktypes.click.item.Left, this); } + } catch (e) { + return false; + } - !this.desc && (this.desc = this.description); + let tick = getTickCount(); - if (this.desc) { - temp = this.desc.split("\n"); + while (getTickCount() - tick < 1000) { + if (me.itemoncursor) { + delay(200); - 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 true; } - return 0; + delay(10); } } - return (subid === undefined ? this.getStat(id) : this.getStat(id, subid)); + return false; }; -/* - _NTIPAliasColor["black"] = 3; - _NTIPAliasColor["lightblue"] = 4; - _NTIPAliasColor["darkblue"] = 5; - _NTIPAliasColor["crystalblue"] = 6; - _NTIPAliasColor["lightred"] = 7; - _NTIPAliasColor["darkred"] = 8; - _NTIPAliasColor["crystalred"] = 9; - _NTIPAliasColor["darkgreen"] = 11; - _NTIPAliasColor["crystalgreen"] = 12; - _NTIPAliasColor["lightyellow"] = 13; - _NTIPAliasColor["darkyellow"] = 14; - _NTIPAliasColor["lightgold"] = 15; - _NTIPAliasColor["darkgold"] = 16; - _NTIPAliasColor["lightpurple"] = 17; - _NTIPAliasColor["orange"] = 19; - _NTIPAliasColor["white"] = 20; -*/ +Unit.prototype.drop = function () { + if (this.type !== sdk.unittype.Item) throw new Error("Unit.drop: Must be used with items. Unit Name: " + this.name); + if (!this.toCursor()) return false; -Unit.prototype.getColor = function () { - let colors; - let Color = { - black: 3, - lightblue: 4, - darkblue: 5, - crystalblue: 6, - lightred: 7, - darkred: 8, - crystalred: 9, - darkgreen: 11, - crystalgreen: 12, - lightyellow: 13, - darkyellow: 14, - lightgold: 15, - darkgold: 16, - lightpurple: 17, - orange: 19, - white: 20 - }; + let tick = getTickCount(); + let timeout = Math.max(1000, me.ping * 6); - // check type - switch (this.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.Armor: - case sdk.items.type.Boots: - case sdk.items.type.Gloves: - case sdk.items.type.Belt: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - 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: - break; - default: - return -1; - } + while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash) || !me.gameReady) { + if (getTickCount() - tick > timeout) return false; - // check quality - if ([sdk.items.quality.Magic, sdk.items.quality.Set, sdk.items.quality.Rare, sdk.items.quality.Unique].indexOf(this.quality) === -1) { - return -1; + if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { + me.cancel(0); + } + + delay(me.ping * 2 + 100); } - if (this.quality === sdk.items.quality.Magic || this.quality === sdk.items.quality.Rare) { - colors = { - "Screaming": Color.orange, - "Howling": Color.orange, - "Wailing": Color.orange, - "Sapphire": Color.lightblue, - "Snowy": Color.lightblue, - "Shivering": Color.lightblue, - "Boreal": Color.lightblue, - "Hibernal": Color.lightblue, - "Ruby": Color.lightred, - "Amber": Color.lightyellow, - "Static": Color.lightyellow, - "Glowing": Color.lightyellow, - "Buzzing": Color.lightyellow, - "Arcing": Color.lightyellow, - "Shocking": Color.lightyellow, - "Emerald": Color.crystalgreen, - "Saintly": Color.darkgold, - "Holy": Color.darkgold, - "Godly": Color.darkgold, - "Visionary": Color.white, - "Mnemonic": Color.crystalblue, - "Bowyer's": Color.lightgold, - "Gymnastic": Color.lightgold, - "Spearmaiden's": Color.lightgold, - "Archer's": Color.lightgold, - "Athlete's": Color.lightgold, - "Lancer's": Color.lightgold, - "Charged": Color.lightgold, - "Blazing": Color.lightgold, - "Freezing": Color.lightgold, - "Glacial": Color.lightgold, - "Powered": Color.lightgold, - "Volcanic": Color.lightgold, - "Blighting": Color.lightgold, - "Noxious": Color.lightgold, - "Mojo": Color.lightgold, - "Cursing": Color.lightgold, - "Venomous": Color.lightgold, - "Golemlord's": Color.lightgold, - "Warden's": Color.lightgold, - "Hawk Branded": Color.lightgold, - "Commander's": Color.lightgold, - "Marshal's": Color.lightgold, - "Rose Branded": Color.lightgold, - "Guardian's": Color.lightgold, - "Veteran's": Color.lightgold, - "Resonant": Color.lightgold, - "Raging": Color.lightgold, - "Echoing": Color.lightgold, - "Furious": Color.lightgold, - "Master's": Color.lightgold, // there's 2x masters... - "Caretaker's": Color.lightgold, - "Terrene": Color.lightgold, - "Feral": Color.lightgold, - "Gaean": Color.lightgold, - "Communal": Color.lightgold, - "Keeper's": Color.lightgold, - "Sensei's": Color.lightgold, - "Trickster's": Color.lightgold, - "Psychic": Color.lightgold, - "Kenshi's": Color.lightgold, - "Cunning": Color.lightgold, - "Shadow": Color.lightgold, - "Faithful": Color.white, - "Priest's": Color.crystalgreen, - "Dragon's": Color.crystalblue, - "Vulpine": Color.crystalblue, - "Shimmering": Color.lightpurple, - "Rainbow": Color.lightpurple, - "Scintillating": Color.lightpurple, - "Prismatic": Color.lightpurple, - "Chromatic": Color.lightpurple, - "Hierophant's": Color.crystalgreen, - "Berserker's": Color.crystalgreen, - "Necromancer's": Color.crystalgreen, - "Witch-hunter's": Color.crystalgreen, - "Arch-Angel's": Color.crystalgreen, - "Valkyrie's": Color.crystalgreen, - "Massive": Color.darkgold, - "Savage": Color.darkgold, - "Merciless": Color.darkgold, - "Ferocious": Color.black, - "Grinding": Color.white, - "Cruel": Color.black, - "Gold": Color.lightgold, - "Platinum": Color.lightgold, - "Meteoric": Color.lightgold, - "Strange": Color.lightgold, - "Weird": Color.lightgold, - "Knight's": Color.darkgold, - "Lord's": Color.darkgold, - "Fool's": Color.white, - "King's": Color.darkgold, - //"Master's": Color.darkgold, - "Elysian": Color.darkgold, - "Fiery": Color.darkred, - "Smoldering": Color.darkred, - "Smoking": Color.darkred, - "Flaming": Color.darkred, - "Condensing": Color.darkred, - "Septic": Color.darkgreen, - "Foul": Color.darkgreen, - "Corrosive": Color.darkgreen, - "Toxic": Color.darkgreen, - "Pestilent": Color.darkgreen, - "of Quickness": Color.darkyellow, - "of the Glacier": Color.darkblue, - "of Winter": Color.darkblue, - "of Burning": Color.darkred, - "of Incineration": Color.darkred, - "of Thunder": Color.darkyellow, - "of Storms": Color.darkyellow, - "of Carnage": Color.black, - "of Slaughter": Color.black, - "of Butchery": Color.black, - "of Evisceration": Color.black, - "of Performance": Color.black, - "of Transcendence": Color.black, - "of Pestilence": Color.darkgreen, - "of Anthrax": Color.darkgreen, - "of the Locust": Color.crystalred, - "of the Lamprey": Color.crystalred, - "of the Wraith": Color.crystalred, - "of the Vampire": Color.crystalred, - "of Icebolt": Color.lightblue, - "of Nova": Color.crystalblue, - "of the Mammoth": Color.crystalred, - "of Frost Shield": Color.lightblue, - "of Nova Shield": Color.crystalblue, - "of Wealth": Color.lightgold, - "of Fortune": Color.lightgold, - "of Luck": Color.lightgold, - "of Perfection": Color.darkgold, - "of Regrowth": Color.crystalred, - "of Spikes": Color.orange, - "of Razors": Color.orange, - "of Swords": Color.orange, - "of Stability": Color.darkyellow, - "of the Colosuss": Color.crystalred, - "of the Squid": Color.crystalred, - "of the Whale": Color.crystalred, - "of Defiance": Color.darkred, - "of the Titan": Color.darkgold, - "of Atlas": Color.darkgold, - "of Wizardry": Color.darkgold - }; + for (let i = 0; i < 3; i += 1) { + clickMap(0, 0, me.x, me.y); + delay(40); + clickMap(2, 0, me.x, me.y); - switch (this.itemType) { - case sdk.items.type.Boots: - colors["of Precision"] = Color.darkgold; + tick = getTickCount(); - break; - case sdk.items.type.Gloves: - colors["of Alacrity"] = Color.darkyellow; - colors["of the Leech"] = Color.crystalred; - colors["of the Bat"] = Color.crystalred; - colors["of the Giant"] = Color.darkgold; + while (getTickCount() - tick < 500) { + if (!me.itemoncursor) { + delay(200); - break; - } - } else if (this.set) { - if (this.identified) { - for (let i = 0; i < 127; i += 1) { - if (this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(16, i, 3)))) { - return getBaseStat(16, i, 12) > 20 ? -1 : getBaseStat(16, i, 12); - } - } - } else { - return Color.lightyellow; // Unidentified set item - } - } else if (this.unique) { - for (let i = 0; i < 401; i += 1) { - if (this.code === getBaseStat(17, i, 4).replace(/^\s+|\s+$/g, "") && this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(17, i, 2)))) { - return getBaseStat(17, i, 13) > 20 ? -1 : getBaseStat(17, i, 13); + return true; } - } - } - - for (let i = 0; i < this.suffixes.length; i += 1) { - if (colors.hasOwnProperty(this.suffixes[i])) { - return colors[this.suffixes[i]]; - } - } - for (let i = 0; i < this.prefixes.length; i += 1) { - if (colors.hasOwnProperty(this.prefixes[i])) { - return colors[this.prefixes[i]]; + delay(10); } } - return -1; + return false; }; /** - * @description Used upon item units like ArachnidMesh.castChargedSkill([skillId]) or directly on the "me" unit me.castChargedSkill(278); - * @param {int} skillId = undefined - * @param {int} x = undefined - * @param {int} y = undefined - * @return boolean - * @throws Error + * @description use consumable item, fixes issue with interact() returning false even if we used an item */ -Unit.prototype.castChargedSkill = function (...args) { - let skillId, x, y, unit, chargedItem, charge; - let chargedItems = []; - let validCharge = function (itemCharge) { - return itemCharge.skill === skillId && itemCharge.charges; - }; - - 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]; - } +Unit.prototype.use = function () { + if (this === undefined || !this.type) return false; + if (this.type !== sdk.unittype.Item) throw new Error("Unit.use: Must be used with items. Unit Name: " + this.name); + if (!getBaseStat("items", this.classid, "useable")) throw new Error("Unit.use: Must be used with consumable items. Unit Name: " + this.name); + + let gid = this.gid; + let pingDelay = me.getPingDelay(); + let quantity = 0; + let iType = this.itemType; + let checkQuantity = false; - 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)"); - } + switch (this.location) { + case sdk.storage.Stash: + case sdk.storage.Inventory: + if (this.isInStash && !Town.openStash()) return false; + // doesn't work, not sure why but it's missing something + //new PacketBuilder().byte(sdk.packets.send.UseItem).dword(gid).dword(this.x).dword(this.y).send(); + checkQuantity = iType === sdk.items.type.Book; + checkQuantity && (quantity = this.getStat(sdk.stats.Quantity)); + this.interact(); // use interact instead, was hoping to skip this since its really just doing the same thing over but oh well 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]; - } + case sdk.storage.Belt: + new PacketBuilder().byte(sdk.packets.send.UseBeltItem).dword(gid).dword(0).dword(0).send(); break; default: - throw new Error("invalid arguments, expected 'me' object or 'item' unit"); + return false; } - // 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) { - throw Error("invalid arguments, expected 'me' object or 'item' unit"); + if (checkQuantity) { + return Misc.poll(() => this.getStat(sdk.stats.Quantity) < quantity, 200 + pingDelay, 50); + } else { + return Misc.poll(() => !Game.getItem(-1, -1, gid), 200 + pingDelay, 50); } +}; - // Called the function the unit, me. - if (this === me) { - if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); - - chargedItems = []; - - // Item must be equipped, or a charm in inventory - this.getItemsEx(-1) - .filter(item => item && (item.isEquipped || (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 - }); - } - } - } - }); - - if (chargedItems.length === 0) throw Error("Don't have the charged skill (" + skillId + "), or not enough charges"); - - chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; - - return chargedItem.castChargedSkill.apply(chargedItem, args); - } else if (this.type === sdk.unittype.Item) { - charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates +/** + * @description Returns item given by itemInfo + * @param itemInfo object - + * { + * classid: Number, + * itemtype: Number, + * quality: Number, + * runeword: Boolean, + * ethereal: Boolean, + * name: getLocaleString(id) || localeStringId, + * equipped: Boolean || Number (bodylocation) + * } + * @returns Unit[] + */ +Unit.prototype.checkItem = function (itemInfo) { + if (this === undefined || this.type > 1 || typeof itemInfo !== "object") return {have: false, item: null}; - if (!charge) throw Error("No charged skill on this item"); + const itemObj = Object.assign({}, { + classid: -1, + itemtype: -1, + quality: -1, + runeword: null, + ethereal: null, + equipped: null, + basetype: null, + name: "" + }, itemInfo); - if (skillId) { - // Filter out all other charged skills - charge = charge.filter(item => (skillId && item.skill === skillId) && !!item.charges); - } else if (charge.length > 1) { - throw new Error("multiple charges on this item without a given skillId"); - } + // convert id into string + typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - charge = charge.first(); + let items = this.getItemsEx() + .filter(function (item) { + return (!item.questItem + && (itemObj.classid === -1 || item.classid === itemObj.classid) + && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) + && (itemObj.quality === -1 || item.quality === itemObj.quality) + && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) + && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) + && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) + && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) + && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) + ); + }); + if (items.length > 0) { + return { + have: true, + item: copyUnit(items.first()) + }; + } else { + return { + have: false, + item: null + }; + } +}; - if (charge) { - // Setting skill on hand - if (!Config.PacketCasting || Config.PacketCasting === 1 && skillId !== sdk.skills.Teleport) { - return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); // Non packet casting - } +/** + * @description Returns first item given by itemInfo + * @param itemInfo array of objects - + * { + * classid: Number, + * itemtype: Number, + * quality: Number, + * runeword: Boolean, + * ethereal: Boolean, + * name: getLocaleString(id) || localeStringId, + * equipped: Boolean || Number (bodylocation) + * } + * @returns Unit[] + */ +Unit.prototype.findFirst = function (itemInfo = []) { + if (this === undefined || this.type > 1) return {have: false, item: null}; + if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return {have: false, item: null}; + let itemList = this.getItemsEx(); - // Packet casting - sendPacket(1, sdk.packets.send.SelectSkill, 2, charge.skill, 1, 0x0, 1, 0x00, 4, this.gid); - // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet + for (let i = 0; i < itemInfo.length; i++) { + const itemObj = Object.assign({}, { + classid: -1, + itemtype: -1, + quality: -1, + runeword: null, + ethereal: null, + equipped: null, + name: "" + }, itemInfo[i]); - // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked - sendPacket(1, sdk.packets.send.RightSkillOnLocation, 2, x || me.x, 2, y || me.y); // Cast the skill + // convert id into string + typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - return true; + let items = itemList + .filter(function (item) { + return (!item.questItem + && (itemObj.classid === -1 || item.classid === itemObj.classid) + && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) + && (itemObj.quality === -1 || item.quality === itemObj.quality) + && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) + && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) + && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) + && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) + ); + }); + if (items.length > 0) { + return { + have: true, + item: copyUnit(items.first()) + }; } } - return false; + return { + have: false, + item: null + }; }; /** - * @description equip an item. + * @description Returns boolean if we have all the items given by itemInfo + * @param itemInfo array of objects - + * { + * classid: Number, + * itemtype: Number, + * quality: Number, + * runeword: Boolean, + * ethereal: Boolean, + * name: getLocaleString(id) || localeStringId, + * equipped: Boolean || Number (bodylocation) + * } + * @returns Boolean */ -Unit.prototype.equip = function (destLocation = undefined) { - if (this.isEquipped) return true; // Item already equiped - - const findspot = function (item) { - let tempspot = Storage.Stash.FindSpot(item); +Unit.prototype.haveAll = function (itemInfo = [], returnIfSome = false) { + if (this === undefined || this.type > 1) return false; + // if an object but not an array convert to array + !Array.isArray(itemInfo) && typeof itemInfo === "object" && (itemInfo = [itemInfo]); + if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return false; + let itemList = this.getItemsEx(); + let haveAll = false; + let checkedGids = []; - if (getUIFlag(sdk.uiflags.Stash) && tempspot) { - return {location: Storage.Stash.location, coord: tempspot}; - } - - tempspot = Storage.Inventory.FindSpot(item); + for (let i = 0; i < itemInfo.length; i++) { + const itemObj = Object.assign({}, { + classid: -1, + itemtype: -1, + quality: -1, + runeword: null, + ethereal: null, + equipped: null, + basetype: null, + name: "" + }, itemInfo[i]); - return tempspot ? {location: Storage.Inventory.location, coord: tempspot} : false; - }; - const doubleHanded = [ - sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Polearm, sdk.items.type.Crossbow, - sdk.items.type.HandtoHand, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear - ]; + // convert id into string + typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - // Not an item, or unidentified, or not enough stats - if (this.type !== sdk.unittype.Item || !this.getFlag(sdk.items.flags.Identified) - || this.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) - || this.dexreq > me.getStat(sdk.stats.Dexterity) - || this.strreq > me.getStat(sdk.stats.Strength)) { - return false; + let items = itemList + .filter(function (item) { + return (!item.questItem + && (checkedGids.indexOf(item.gid) === -1) + && (itemObj.classid === -1 || item.classid === itemObj.classid) + && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) + && (itemObj.quality === -1 || item.quality === itemObj.quality) + && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) + && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) + && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) + && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) + && (!itemObj.name.length || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) + ); + }); + if (items.length > 0) { + if (returnIfSome) return true; + checkedGids.push(items.first().gid); + haveAll = true; + } else { + if (returnIfSome) continue; + return false; + } } - // If not a specific location is given, figure it out (can be useful to equip a double weapon) - !destLocation && (destLocation = this.getBodyLoc()); - // If destLocation isnt an array, make it one - !Array.isArray(destLocation) && (destLocation = [destLocation]); + return haveAll; +}; - console.log("equiping " + this.name + " to bodylocation: " + destLocation.first()); +Unit.prototype.haveSome = function (itemInfo = []) { + return this.haveAll(itemInfo, true); +}; - let currentEquiped = me.getItemsEx(-1).filter(item => - destLocation.indexOf(item.bodylocation) !== -1 - || ( // Deal with double handed weapons +/** + * @description Return the items of a player, or an empty array + * @param args + * @returns Unit[] + */ +Unit.prototype.getItems = function (...args) { + let items = []; + let item = this.getItem.apply(this, args); - (item.isOnMain) - && [sdk.body.RightArm, sdk.body.LeftArm].indexOf(destLocation) // in case destination is on the weapon/shield slot - && ( - doubleHanded.indexOf(this.itemType) !== -1 // this item is a double handed item - || doubleHanded.indexOf(item.itemType) !== -1 // current item is a double handed item - ) - ) - ).sort((a, b) => b - a); // shields first + if (item) { + do { + items.push(copyUnit(item)); + } while (item.getNext()); + } - // if nothing is equipped at the moment, just equip it - if (!currentEquiped.length) { - clickItemAndWait(sdk.clicktypes.click.item.Left, this); - clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); - } else { - // unequip / swap items - currentEquiped.forEach((item, index) => { - // Last item, so swap instead of putting off first - if (index === (currentEquiped.length - 1)) { - print("swap " + this.name + " for " + item.name); - let oldLoc = {x: this.x, y: this.y, location: this.location}; - clickItemAndWait(sdk.clicktypes.click.item.Left, this); // Pick up current item - clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // the swap of items - // Find a spot for the current item - let spot = findspot(item); + return Array.isArray(items) ? items : []; +}; - if (!spot) { // If no spot is found for the item, rollback - clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // swap again - clickItemAndWait(sdk.clicktypes.click.item.Left, oldLoc.x, oldLoc.y, oldLoc.location); // put item back on old spot - throw Error("cant find spot for unequipped item"); - } +Unit.prototype.getItemsEx = function (...args) { + let items = []; + let item = this.getItem.apply(this, args); - clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.y, spot.coord.x, spot.location); // put item on the found spot + if (item) { + do { + items.push(copyUnit(item)); + } while (item.getNext()); + } - return; + return items; +}; + +Unit.prototype.getPrefix = function (id) { + switch (typeof id) { + case "number": + if (typeof this.prefixnums !== "object") return this.prefixnum === id; + + for (let i = 0; i < this.prefixnums.length; i += 1) { + if (id === this.prefixnums[i]) { + return true; } + } - print("Unequip item first " + item.name); - // Incase multiple items are equipped - let spot = findspot(item); // Find a spot for the current item + break; + case "string": + if (typeof this.prefixes !== "object") { + return this.prefix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); + } - if (!spot) throw Error("cant find spot for unequipped item"); + for (let i = 0; i < this.prefixes.length; i += 1) { + if (id.replace(/\s+/g, "").toLowerCase() === this.prefixes[i].replace(/\s+/g, "").toLowerCase()) { + return true; + } + } - clickItemAndWait(sdk.clicktypes.click.item.Left, item.bodylocation); - clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.x, spot.coord.y, spot.location); - }); + break; } - return { - success: this.bodylocation === destLocation.first(), - unequiped: currentEquiped, - rollback: () => currentEquiped.forEach(item => item.equip()) // Note; rollback only works if you had other items equipped before. - }; + return false; }; -Unit.prototype.getBodyLoc = function () { - const types = {}; - types[sdk.body.Head] = [sdk.items.type.Helm, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; // helm - types[sdk.body.Neck] = [sdk.items.type.Amulet]; // amulet - types[sdk.body.Armor] = [sdk.items.type.Armor]; // armor - types[sdk.body.RightArm] = [ - 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.HandtoHand, sdk.items.type.Orb, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.AssassinClaw - ]; // weapons - types[sdk.body.LeftArm] = [sdk.items.type.Shield, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads], // shields / Arrows / bolts - types[sdk.body.RingRight] = [sdk.items.type.Ring]; // ring slot 1 - types[sdk.body.RingLeft] = [sdk.items.type.Ring]; // ring slot 2 - types[sdk.body.Belt] = [sdk.items.type.Belt]; // belt - types[sdk.body.Feet] = [sdk.items.type.Boots]; // boots - types[sdk.body.Gloves] = [sdk.items.type.Gloves]; // gloves - //types[sdk.body.RightArmSecondary] = types[sdk.body.RightArm]; - //types[sdk.body.LeftArmSecondary] = types[sdk.body.LeftArm]; - let bodyLoc = []; +Unit.prototype.getSuffix = function (id) { + switch (typeof id) { + case "number": + if (typeof this.suffixnums !== "object") return this.suffixnum === id; - for (let i in types) { - this.itemType && types[i].indexOf(this.itemType) !== -1 && bodyLoc.push(i); + for (let i = 0; i < this.suffixnums.length; i += 1) { + if (id === this.suffixnums[i]) { + return true; + } + } + + break; + case "string": + if (typeof this.suffixes !== "object") { + return this.suffix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); + } + + for (let i = 0; i < this.suffixes.length; i += 1) { + if (id.replace(/\s+/g, "").toLowerCase() === this.suffixes[i].replace(/\s+/g, "").toLowerCase()) { + return true; + } + } + + break; } - // Strings are hard to calculate with, parse to int - return bodyLoc.map(parseInt); + return false; }; -Unit.prototype.getRes = function (type, difficulty) { - if (!type || ![sdk.stats.FireResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, sdk.stats.LightningResist].includes(type)) { - return -1; - } - - difficulty === undefined || difficulty < 0 && (difficulty = 0); - difficulty > 2 && (difficulty = 2); +Unit.prototype.__defineGetter__("dexreq", + function () { + let ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + let baseReq = getBaseStat("items", this.classid, "reqdex"); + let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - let modifier = me.classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; - if (this === me) { - switch (type) { - case sdk.stats.FireResist: - me.getState(sdk.states.ShrineResFire) && (modifier += 75); + return Math.max(finalReq, 0); + }); - break; - case sdk.stats.ColdResist: - me.getState(sdk.states.ShrineResCold) && (modifier += 75); - me.getState(sdk.states.Thawing) && (modifier += 50); +Unit.prototype.__defineGetter__("strreq", + function () { + let ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + let baseReq = getBaseStat("items", this.classid, "reqstr"); + let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - break; - case sdk.stats.LightningResist: - me.getState(sdk.states.ShrineResLighting) && (modifier += 75); + return Math.max(finalReq, 0); + }); - break; - case sdk.stats.PoisonResist: - me.getState(sdk.states.ShrineResPoison) && (modifier += 75); - me.getState(sdk.states.Antidote) && (modifier += 50); +Unit.prototype.__defineGetter__("itemclass", + function () { + if (getBaseStat("items", this.classid, "code") === undefined) return 0; + if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ultracode")) return 2; + if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ubercode")) return 1; - break; - } - } - return this.getStat(type) - modifier; -}; + return 0; + }); -{ - let coords = function () { - if (Array.isArray(this) && this.length > 1) { - return [this[0], this[1]]; - } +Unit.prototype.getStatEx = function (id, subid) { + let temp, rval, regex; - 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]; - } + switch (id) { + case sdk.stats.AllRes: + // calculates all res, doesn't exist though + // 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) + ]; - return [undefined, undefined]; - }; + // What is the minimum of the 4? + let min = Math.min.apply(null, allres); - Object.defineProperties(Object.prototype, { - distance: { - get: function () { - return !me.gameReady ? NaN : /* Math.round */(getDistance.apply(null, [me, ...coords.apply(this)])); - }, - enumerable: false, - }, - }); + // Cap all res to the minimum amount of res + allres = allres.map(res => res > min ? min : res); - Object.prototype.mobCount = function (givenSettings = {}) { - let [x, y] = coords.apply(this); - const settings = Object.assign({}, { - range: 5, - coll: (sdk.collision.BlockWall | 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; - }; - Object.defineProperty(Object.prototype, "mobCount", {enumerable: false}); -} + // Get it in local variables, its more easy to read + let [fire, cold, light, psn] = allres; -Object.defineProperties(Unit.prototype, { - isChampion: { - get: function () { - return (this.spectype & sdk.monsters.spectype.Champion) > 0; - }, - }, - isUnique: { - get: function () { - return (this.spectype & sdk.monsters.spectype.Unique) > 0; - }, - }, - isMinion: { - get: function () { - return (this.spectype & sdk.monsters.spectype.Minion) > 0; - }, - }, - isSuperUnique: { - get: function () { - return (this.spectype & (sdk.monsters.spectype.Super | sdk.monsters.spectype.Unique)) > 0; - }, - }, - isSpecial: { - get: function () { - return (this.isChampion || this.isUnique || this.isSuperUnique); - }, - }, - isPlayer: { - get: function () { - return this.type === sdk.unittype.Player; - }, - }, - isMonster: { - get: function () { - return this.type === sdk.unittype.Monster; - }, - }, - isNPC: { - get: function () { - return this.type === sdk.unittype.Monster && this.getStat(sdk.stats.Alignment) === 2; - }, - }, - // todo - monster types - isPrimeEvil: { - get: function () { - return [ - sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, - sdk.monsters.Baal, sdk.monsters.BaalClone, sdk.monsters.UberDuriel, sdk.monsters.UberIzual, - sdk.monsters.UberMephisto, sdk.monsters.UberDiablo, sdk.monsters.UberBaal, sdk.monsters.Lilith, sdk.monsters.DiabloClone - ].includes(this.classid) || getBaseStat("monstats", this.classid, "primeevil"); - }, - }, - isBoss: { - get: function () { - return this.isPrimeEvil - || - [ - sdk.monsters.TheSmith, sdk.monsters.BloodRaven, sdk.monsters.Radament, sdk.monsters.Griswold, - sdk.monsters.TheSummoner, sdk.monsters.Izual, sdk.monsters.Hephasto, sdk.monsters.KorlictheProtector, - sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian, sdk.monsters.ListerTheTormenter, - sdk.monsters.TheCowKing, sdk.monsters.ColdwormtheBurrower, sdk.monsters.Nihlathak - ].includes(this.classid); - }, - }, - isGhost: { - get: function () { - return [ - sdk.monsters.Ghost1, sdk.monsters.Wraith1, sdk.monsters.Specter1, - sdk.monsters.Apparition, sdk.monsters.DarkShape, sdk.monsters.Ghost2, sdk.monsters.Wraith2, sdk.monsters.Specter2 - ].includes(this.classid) || getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Wraith; - }, - }, - isDoll: { - get: function () { - return [ - sdk.monsters.BoneFetish1, sdk.monsters.BoneFetish2, sdk.monsters.BoneFetish3, - sdk.monsters.SoulKiller3, sdk.monsters.StygianDoll2, sdk.monsters.StygianDoll6, sdk.monsters.SoulKiller - ].includes(this.classid); - }, - }, - isMonsterObject: { - get: function () { - return [ - sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, - sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, sdk.monsters.FireTower, - sdk.monsters.BarricadeDoor1, sdk.monsters.BarricadeDoor2, sdk.monsters.BarricadeWall1, sdk.monsters.BarricadeWall2, - sdk.monsters.CatapultS, sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, sdk.monsters.CatapultW, - sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.Hut, - ].includes(this.classid); - }, - }, - isMonsterEgg: { - get: function () { - return [ - sdk.monsters.SandMaggotEgg, sdk.monsters.RockWormEgg, sdk.monsters.DevourerEgg, sdk.monsters.GiantLampreyEgg, - sdk.monsters.WorldKillerEgg1, sdk.monsters.WorldKillerEgg2 - ].includes(this.classid); - }, - }, - isMonsterNest: { - get: function () { - return [ - sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, - sdk.monsters.BloodWingNest, sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest - ].includes(this.classid); - }, - }, - isBaalTentacle: { - get: function () { - return [ - sdk.monsters.Tentacle1, sdk.monsters.Tentacle2, - sdk.monsters.Tentacle3, sdk.monsters.Tentacle4, sdk.monsters.Tentacle5 - ].includes(this.classid); - }, - }, - isShaman: { - get: function () { - return [ - sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, - sdk.monsters.WarpedShaman, sdk.monsters.CarverShaman, sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2 - ].includes(this.classid); - }, - }, - isUnraveler: { - get: function () { - return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Unraveler; - }, - }, - isFallen: { - get: function () { - return [ - sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, - sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2 - ].includes(this.classid); - }, - }, - isBeetle: { - get: function () { - return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Scarab; - }, - }, - isWalking: { - get: function () { - return (this.mode === sdk.monsters.mode.Walking && (this.targetx !== this.x || this.targety !== this.y)); - } - }, - isRunning: { - get: function () { - return (this.mode === sdk.monsters.mode.Running && (this.targetx !== this.x || this.targety !== this.y)); - } - }, - isMoving: { - get: function () { - return (this.isWalking || this.isRunning); - }, - }, - isFrozen: { - get: function () { - return this.getState(sdk.states.FrozenSolid); - }, - }, - isChilled: { - get: function () { - return this.getState(sdk.states.Frozen); - }, - }, - extraStrong: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ExtraStrong); - }, - }, - extraFast: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ExtraFast); - }, - }, - cursed: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.Cursed); - }, - }, - magicResistant: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.MagicResistant); - }, - }, - fireEnchanted: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.FireEnchanted); - }, - }, - lightningEnchanted: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.LightningEnchanted); - }, - }, - coldEnchanted: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ColdEnchanted); - }, - }, - manBurn: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ManaBurn); - }, - }, - teleportation: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.Teleportation); - }, - }, - spectralHit: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.SpectralHit); - }, - }, - stoneSkin: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.StoneSkin); - }, - }, - multiShot: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.MultipleShots); - }, - }, - resPenalty: { - value: me.classic ? [0, 20, 50][me.diff] : [0, 40, 100][me.diff], - writable: true - }, - fireRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResFire) && (modifier += 75); - } - return this.getStat(sdk.stats.FireResist) - me.resPenalty - modifier; - } - }, - coldRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResCold) && (modifier += 75); - me.getState(sdk.states.Thawing) && (modifier += 50); - } - return this.getStat(sdk.stats.ColdResist) - me.resPenalty - modifier; - } - }, - lightRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResLighting) && (modifier += 75); - } - return this.getStat(sdk.stats.LightResist) - me.resPenalty - modifier; - } - }, - poisonRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResPoison) && (modifier += 75); - me.getState(sdk.states.Antidote) && (modifier += 50); - } - return this.getStat(sdk.stats.PoisonResist) - me.resPenalty - modifier; - } - }, - hpPercent: { - get: function () { - return Math.round(this.hp * 100 / this.hpmax); - } - }, - isEquipped: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Equipped; - } - }, - isEquippedCharm: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return (this.location === sdk.storage.Inventory && [sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(this.itemType)); - } - }, - isInInventory: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Inventory && this.mode === sdk.items.mode.inStorage; - } - }, - isInStash: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Stash && this.mode === sdk.items.mode.inStorage; - } - }, - isInCube: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Cube && this.mode === sdk.items.mode.inStorage; - } - }, - isInStorage: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.mode === sdk.items.mode.inStorage && [sdk.storage.Inventory, sdk.storage.Cube, sdk.storage.Stash].includes(this.location); - } - }, - isInBelt: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Belt && this.mode === sdk.items.mode.inBelt; - } - }, - isOnMain: { - get: function () { - if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; - return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); - } - }, - isOnSwap: { - get: function () { - if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; - switch (me.weaponswitch) { - case sdk.player.slot.Main: - return [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(this.bodylocation); - case sdk.player.slot.Secondary: - return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); - } - return false; - } - }, - identified: { - get: function () { - // Can't tell, as it isn't an item - if (this.type !== sdk.unittype.Item) return undefined; - // Is also true for white items - return this.getFlag(sdk.items.flags.Identified); - } - }, - ethereal: { - get: function () { - // Can't tell, as it isn't an item - if (this.type !== sdk.unittype.Item) return undefined; - return this.getFlag(sdk.items.flags.Ethereal); - } - }, - twoHanded: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return getBaseStat("items", this.classid, "2handed") === 1; - } - }, - oneOrTwoHanded: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return getBaseStat("items", this.classid, "1or2handed") === 1; - } - }, - strictlyTwoHanded: { - get: function () { - return this.twoHanded && !this.oneOrTwoHanded; - } - }, - runeword: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return !!this.getFlag(sdk.items.flags.Runeword); - } - }, - questItem: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return (this.itemType === sdk.items.type.Quest - || [ - sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, - sdk.items.quest.ViperAmulet, sdk.items.quest.DecoyGidbinn, sdk.items.quest.TheGidbinn, sdk.items.quest.KhalimsFlail, - sdk.items.quest.KhalimsWill, sdk.items.quest.HellForgeHammer, sdk.items.quest.StandardofHeroes - ].includes(this.classid)); - } - }, - sellable: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - if (this.getItemCost(sdk.items.cost.ToSell) <= 1) return false; - return (!this.questItem - && [ - sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction, sdk.items.quest.DiablosHorn, - sdk.items.quest.BaalsEye, sdk.items.quest.MephistosBrain, sdk.items.quest.TokenofAbsolution, sdk.items.quest.TwistedEssenceofSuffering, - sdk.items.quest.ChargedEssenceofHatred, sdk.items.quest.BurningEssenceofTerror, sdk.items.quest.FesteringEssenceofDestruction - ].indexOf(this.classid) === -1); - } - }, - lowquality: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.LowQuality; - }, - }, - normal: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Normal; - }, - }, - superior: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Superior; - }, - }, - magic: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Magic; - }, - }, - set: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Set; - }, - }, - rare: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Rare; - }, - }, - unique: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Unique; - }, - }, - crafted: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Crafted; - }, - }, - sockets: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.getStat(sdk.stats.NumSockets); - }, - }, - onGroundOrDropping: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return (this.mode === sdk.items.mode.onGround || this.mode === sdk.items.mode.Dropping); - }, - }, - isShield: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads].includes(this.itemType); - }, - }, - isAnni: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.unique && this.itemType === sdk.items.type.SmallCharm; - }, - }, - isTorch: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.unique && this.itemType === sdk.items.type.LargeCharm; - }, - }, - isGheeds: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.unique && this.itemType === sdk.items.type.GrandCharm; - }, - }, - prettyPrint: { - get: function () { - if (this.type !== sdk.unittype.Item) return this.name; - return this.fname.split("\n").reverse().join(" "); + return fire === cold && cold === light && light === psn ? min : 0; + } + 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; } - }, -}); -Unit.prototype.hasEnchant = function (...enchants) { - if (!this.isMonster) return false; - for (let enchant of enchants) { - if (this.getEnchant(enchant)) return true; - } - return false; -}; + break; + case sdk.stats.MinDamage: + case sdk.stats.MaxDamage: + if (subid === 1) { + temp = this.getStat(-1); + rval = 0; -Unit.prototype.usingShield = function () { - if (this.type > sdk.unittype.Monster) return false; - // always switch to main hand if we are checking ourselves - this === me && me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - let shield = this.getItemsEx(-1, sdk.items.mode.Equipped).filter(s => s.isShield).first(); - return !!shield; -}; + for (let 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. -Object.defineProperties(me, { - inShop: { - get: function () { - if (getUIFlag(sdk.uiflags.Shop)) return true; - if (!Config.PacketShopping) return false; - let npc = getInteractedNPC(); - return !!(npc && npc.itemcount > 0); - } - }, - walking: { - get: function () { - return me.runwalk === sdk.player.move.Walk; - } - }, - running: { - get: function () { - return me.runwalk === sdk.player.move.Run; - } - }, - deadOrInSequence: { - get: function () { - return me.dead || me.mode === sdk.player.mode.SkillActionSequence; - } - }, - moving: { - get: function () { - return [sdk.player.mode.Walking, sdk.player.mode.Running, sdk.player.mode.WalkingInTown].includes(me.mode); - } - }, - highestAct: { - get: function () { - let acts = [true, - me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed), - me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed), - me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed), - me.getQuest(sdk.quest.id.AbleToGotoActV, sdk.quest.states.Completed)]; - let index = acts.findIndex((i) => !i); // find first false, returns between 1 and 5 - return index === -1 ? 5 : index; - } - }, - highestQuestDone: { - get: function () { - for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { - if (me.getQuest(i, sdk.quest.states.Completed)) { - return i; - } + if (rval) { // First occurence stored, return if the second one exists + return rval; + } - // check if we've completed main part but not used our reward - if ([sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade].includes(i) && me.getQuest(i, sdk.quest.states.ReqComplete)) { - return i; + 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 undefined; - } - }, - staminaPercent: { - get: function () { - return Math.round((me.stamina / me.staminamax) * 100); - } - }, - staminaDrainPerSec: { - get: function () { - let bonusReduction = me.getStat(sdk.stats.StaminaRecoveryBonus); - let armorMalusReduction = 0; // TODO - return 25 * Math.max(40 * (1 + armorMalusReduction / 10) * (100 - bonusReduction) / 100, 1) / 256; - } - }, - staminaTimeLeft: { - get: function () { - return me.stamina / me.staminaDrainPerSec; - } - }, - staminaMaxDuration: { - get: function () { - return me.staminamax / me.staminaDrainPerSec; - } - }, - FCR: { - get: function () { - return me.getStat(sdk.stats.FCR) - (!!Config ? Config.FCR : 0); - } - }, - FHR: { - get: function () { - return me.getStat(sdk.stats.FHR) - (!!Config ? Config.FHR : 0); - } - }, - FBR: { - get: function () { - return me.getStat(sdk.stats.FBR) - (!!Config ? Config.FBR : 0); - } - }, - IAS: { - get: function () { - return me.getStat(sdk.stats.IAS) - (!!Config ? Config.IAS : 0); - } - }, - shapeshifted: { - get: function () { - return me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear) || me.getState(sdk.states.Delerium); - } - }, - mpPercent: { - get: function () { - return Math.round(me.mp * 100 / me.mpmax); - } - }, - skillDelay: { - get: function () { - return me.getState(sdk.states.SkillDelay); - } - }, - classic: { - get: function () { - return me.gametype === sdk.game.gametype.Classic; - } - }, - expansion: { - get: function () { - return me.gametype === sdk.game.gametype.Expansion; - } - }, - softcore: { - get: function () { - return me.playertype === false; - } - }, - hardcore: { - get: function () { - return me.playertype === true; - } - }, - normal: { - get: function () { - return me.diff === sdk.difficulty.Normal; - } - }, - nightmare: { - get: function () { - return me.diff === sdk.difficulty.Nightmare; - } - }, - hell: { - get: function () { - return me.diff === sdk.difficulty.Hell; - } - }, - amazon: { - get: function () { - return me.classid === sdk.player.class.Amazon; - } - }, - sorceress: { - get: function () { - return me.classid === sdk.player.class.Sorceress; - } - }, - necromancer: { - get: function () { - return me.classid === sdk.player.class.Necromancer; - } - }, - paladin: { - get: function () { - return me.classid === sdk.player.class.Paladin; - } - }, - barbarian: { - get: function () { - return me.classid === sdk.player.class.Barbarian; - } - }, - druid: { - get: function () { - return me.classid === sdk.player.class.Druid; - } - }, - assassin: { - get: function () { - return me.classid === sdk.player.class.Assassin; - } - }, - // quest items - wirtsleg: { - get: function () { - return me.getItem(sdk.quest.item.WirtsLeg); - } - }, - cube: { - get: function () { - return me.getItem(sdk.quest.item.Cube); - } - }, - shaft: { - get: function () { - return me.getItem(sdk.quest.item.ShaftoftheHoradricStaff); - } - }, - amulet: { - get: function () { - return me.getItem(sdk.quest.item.ViperAmulet); - } - }, - staff: { - get: function () { - return me.getItem(sdk.quest.item.HoradricStaff); - } - }, - completestaff: { - get: function () { - return me.getItem(sdk.quest.item.HoradricStaff); - } - }, - eye: { - get: function () { - return me.getItem(sdk.items.quest.KhalimsEye); - } - }, - brain: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsBrain); - } - }, - heart: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsHeart); - } - }, - khalimswill: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsWill); - } - }, - khalimsflail: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsFlail); - } - }, - malahspotion: { - get: function () { - return me.getItem(sdk.quest.item.MalahsPotion); - } - }, - scrollofresistance: { - get: function () { - return me.getItem(sdk.quest.item.ScrollofResistance); - } - }, - // quests - den: { - get: function () { - return me.getQuest(sdk.quest.id.DenofEvil, sdk.quest.states.Completed); - } - }, - bloodraven: { - get: function () { - return me.getQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed); - } - }, - smith: { - get: function () { - return me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.Completed); - } - }, - cain: { - get: function () { - return me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - } - }, - tristram: { - get: function () { - // update where this is used and change the state to be portal opened and me.cain to be quest completed - return me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - } - }, - countess: { - get: function () { - return me.getQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); - } - }, - andariel: { - get: function () { - return me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed); + + return 0; } - }, - radament: { - get: function () { - return me.getQuest(sdk.quest.id.RadamentsLair, sdk.quest.states.Completed); + + 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); + } + + // can fail sometimes + !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; } - }, - horadricstaff: { - get: function () { - return me.getQuest(sdk.quest.id.TheHoradricStaff, sdk.quest.states.Completed); + + break; + case sdk.stats.PoisonMinDamage: + if (subid === 1) { + return Math.round(this.getStat(sdk.stats.PoisonMinDamage) * this.getStat(sdk.stats.PoisonLength) / 256); } - }, - summoner: { - get: function () { - return me.getQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed); + + 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; } - }, - duriel: { - get: function () { - return me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed); + + 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; } - }, - goldenbird: { - get: function () { - return me.getQuest(sdk.quest.id.TheGoldenBird, sdk.quest.states.Completed); + + 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 (let i = 0; i < temp[id].length; i += 1) { + if (temp[id][i] !== undefined) { + return temp[id][i].skill; + } + } + } else { + return temp[id].skill; + } + } + + return 0; } - }, - lamessen: { - get: function () { - return me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed); + + if (subid === 2) { + temp = this.getStat(-2); + + if (temp.hasOwnProperty(id)) { + if (temp[id] instanceof Array) { + for (let 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; } - }, - gidbinn: { - get: function () { - return me.getQuest(sdk.quest.id.BladeoftheOldReligion, sdk.quest.states.Completed); + + 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 && (this.desc = this.description); + + if (this.desc) { + temp = 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 && (this.desc = this.description); + + if (this.desc) { + temp = 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; } - }, - travincal: { - get: function () { - return me.getQuest(sdk.quest.id.KhalimsWill, sdk.quest.states.Completed); + } + + return (subid === undefined ? this.getStat(id) : this.getStat(id, subid)); +}; + +/* + _NTIPAliasColor["black"] = 3; + _NTIPAliasColor["lightblue"] = 4; + _NTIPAliasColor["darkblue"] = 5; + _NTIPAliasColor["crystalblue"] = 6; + _NTIPAliasColor["lightred"] = 7; + _NTIPAliasColor["darkred"] = 8; + _NTIPAliasColor["crystalred"] = 9; + _NTIPAliasColor["darkgreen"] = 11; + _NTIPAliasColor["crystalgreen"] = 12; + _NTIPAliasColor["lightyellow"] = 13; + _NTIPAliasColor["darkyellow"] = 14; + _NTIPAliasColor["lightgold"] = 15; + _NTIPAliasColor["darkgold"] = 16; + _NTIPAliasColor["lightpurple"] = 17; + _NTIPAliasColor["orange"] = 19; + _NTIPAliasColor["white"] = 20; +*/ + +Unit.prototype.getColor = function () { + let colors; + let Color = { + black: 3, + lightblue: 4, + darkblue: 5, + crystalblue: 6, + lightred: 7, + darkred: 8, + crystalred: 9, + darkgreen: 11, + crystalgreen: 12, + lightyellow: 13, + darkyellow: 14, + lightgold: 15, + darkgold: 16, + lightpurple: 17, + orange: 19, + white: 20 + }; + + // check type + switch (this.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.Armor: + case sdk.items.type.Boots: + case sdk.items.type.Gloves: + case sdk.items.type.Belt: + case sdk.items.type.AuricShields: + case sdk.items.type.VoodooHeads: + case sdk.items.type.Helm: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Circlet: + case sdk.items.type.Pelt: + 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: + break; + default: + return -1; + } + + // check quality + if ([sdk.items.quality.Magic, sdk.items.quality.Set, sdk.items.quality.Rare, sdk.items.quality.Unique].indexOf(this.quality) === -1) { + return -1; + } + + if (this.quality === sdk.items.quality.Magic || this.quality === sdk.items.quality.Rare) { + colors = { + "Screaming": Color.orange, + "Howling": Color.orange, + "Wailing": Color.orange, + "Sapphire": Color.lightblue, + "Snowy": Color.lightblue, + "Shivering": Color.lightblue, + "Boreal": Color.lightblue, + "Hibernal": Color.lightblue, + "Ruby": Color.lightred, + "Amber": Color.lightyellow, + "Static": Color.lightyellow, + "Glowing": Color.lightyellow, + "Buzzing": Color.lightyellow, + "Arcing": Color.lightyellow, + "Shocking": Color.lightyellow, + "Emerald": Color.crystalgreen, + "Saintly": Color.darkgold, + "Holy": Color.darkgold, + "Godly": Color.darkgold, + "Visionary": Color.white, + "Mnemonic": Color.crystalblue, + "Bowyer's": Color.lightgold, + "Gymnastic": Color.lightgold, + "Spearmaiden's": Color.lightgold, + "Archer's": Color.lightgold, + "Athlete's": Color.lightgold, + "Lancer's": Color.lightgold, + "Charged": Color.lightgold, + "Blazing": Color.lightgold, + "Freezing": Color.lightgold, + "Glacial": Color.lightgold, + "Powered": Color.lightgold, + "Volcanic": Color.lightgold, + "Blighting": Color.lightgold, + "Noxious": Color.lightgold, + "Mojo": Color.lightgold, + "Cursing": Color.lightgold, + "Venomous": Color.lightgold, + "Golemlord's": Color.lightgold, + "Warden's": Color.lightgold, + "Hawk Branded": Color.lightgold, + "Commander's": Color.lightgold, + "Marshal's": Color.lightgold, + "Rose Branded": Color.lightgold, + "Guardian's": Color.lightgold, + "Veteran's": Color.lightgold, + "Resonant": Color.lightgold, + "Raging": Color.lightgold, + "Echoing": Color.lightgold, + "Furious": Color.lightgold, + "Master's": Color.lightgold, // there's 2x masters... + "Caretaker's": Color.lightgold, + "Terrene": Color.lightgold, + "Feral": Color.lightgold, + "Gaean": Color.lightgold, + "Communal": Color.lightgold, + "Keeper's": Color.lightgold, + "Sensei's": Color.lightgold, + "Trickster's": Color.lightgold, + "Psychic": Color.lightgold, + "Kenshi's": Color.lightgold, + "Cunning": Color.lightgold, + "Shadow": Color.lightgold, + "Faithful": Color.white, + "Priest's": Color.crystalgreen, + "Dragon's": Color.crystalblue, + "Vulpine": Color.crystalblue, + "Shimmering": Color.lightpurple, + "Rainbow": Color.lightpurple, + "Scintillating": Color.lightpurple, + "Prismatic": Color.lightpurple, + "Chromatic": Color.lightpurple, + "Hierophant's": Color.crystalgreen, + "Berserker's": Color.crystalgreen, + "Necromancer's": Color.crystalgreen, + "Witch-hunter's": Color.crystalgreen, + "Arch-Angel's": Color.crystalgreen, + "Valkyrie's": Color.crystalgreen, + "Massive": Color.darkgold, + "Savage": Color.darkgold, + "Merciless": Color.darkgold, + "Ferocious": Color.black, + "Grinding": Color.white, + "Cruel": Color.black, + "Gold": Color.lightgold, + "Platinum": Color.lightgold, + "Meteoric": Color.lightgold, + "Strange": Color.lightgold, + "Weird": Color.lightgold, + "Knight's": Color.darkgold, + "Lord's": Color.darkgold, + "Fool's": Color.white, + "King's": Color.darkgold, + //"Master's": Color.darkgold, + "Elysian": Color.darkgold, + "Fiery": Color.darkred, + "Smoldering": Color.darkred, + "Smoking": Color.darkred, + "Flaming": Color.darkred, + "Condensing": Color.darkred, + "Septic": Color.darkgreen, + "Foul": Color.darkgreen, + "Corrosive": Color.darkgreen, + "Toxic": Color.darkgreen, + "Pestilent": Color.darkgreen, + "of Quickness": Color.darkyellow, + "of the Glacier": Color.darkblue, + "of Winter": Color.darkblue, + "of Burning": Color.darkred, + "of Incineration": Color.darkred, + "of Thunder": Color.darkyellow, + "of Storms": Color.darkyellow, + "of Carnage": Color.black, + "of Slaughter": Color.black, + "of Butchery": Color.black, + "of Evisceration": Color.black, + "of Performance": Color.black, + "of Transcendence": Color.black, + "of Pestilence": Color.darkgreen, + "of Anthrax": Color.darkgreen, + "of the Locust": Color.crystalred, + "of the Lamprey": Color.crystalred, + "of the Wraith": Color.crystalred, + "of the Vampire": Color.crystalred, + "of Icebolt": Color.lightblue, + "of Nova": Color.crystalblue, + "of the Mammoth": Color.crystalred, + "of Frost Shield": Color.lightblue, + "of Nova Shield": Color.crystalblue, + "of Wealth": Color.lightgold, + "of Fortune": Color.lightgold, + "of Luck": Color.lightgold, + "of Perfection": Color.darkgold, + "of Regrowth": Color.crystalred, + "of Spikes": Color.orange, + "of Razors": Color.orange, + "of Swords": Color.orange, + "of Stability": Color.darkyellow, + "of the Colosuss": Color.crystalred, + "of the Squid": Color.crystalred, + "of the Whale": Color.crystalred, + "of Defiance": Color.darkred, + "of the Titan": Color.darkgold, + "of Atlas": Color.darkgold, + "of Wizardry": Color.darkgold + }; + + switch (this.itemType) { + case sdk.items.type.Boots: + colors["of Precision"] = Color.darkgold; + + break; + case sdk.items.type.Gloves: + colors["of Alacrity"] = Color.darkyellow; + colors["of the Leech"] = Color.crystalred; + colors["of the Bat"] = Color.crystalred; + colors["of the Giant"] = Color.darkgold; + + break; } - }, - mephisto: { - get: function () { - return me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed); + } else if (this.set) { + if (this.identified) { + for (let i = 0; i < 127; i += 1) { + if (this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(16, i, 3)))) { + return getBaseStat(16, i, 12) > 20 ? -1 : getBaseStat(16, i, 12); + } + } + } else { + return Color.lightyellow; // Unidentified set item } - }, - izual: { - get: function () { - return me.getQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.Completed); + } else if (this.unique) { + for (let i = 0; i < 401; i += 1) { + if (this.code === getBaseStat(17, i, 4).replace(/^\s+|\s+$/g, "") && this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(17, i, 2)))) { + return getBaseStat(17, i, 13) > 20 ? -1 : getBaseStat(17, i, 13); + } } - }, - hellforge: { - get: function () { - return me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.Completed); + } + + for (let i = 0; i < this.suffixes.length; i += 1) { + if (colors.hasOwnProperty(this.suffixes[i])) { + return colors[this.suffixes[i]]; } - }, - diablo: { - get: function () { - return me.getQuest(sdk.quest.id.TerrorsEnd, sdk.quest.states.Completed); + } + + for (let i = 0; i < this.prefixes.length; i += 1) { + if (colors.hasOwnProperty(this.prefixes[i])) { + return colors[this.prefixes[i]]; } - }, - shenk: { - get: function () { - return me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed); + } + + return -1; +}; + +/** + * @description Used upon item units like ArachnidMesh.castChargedSkill([skillId]) or directly on the "me" unit me.castChargedSkill(278); + * @param {int} skillId = undefined + * @param {int} x = undefined + * @param {int} y = undefined + * @return boolean + * @throws Error + */ +Unit.prototype.castChargedSkill = function (...args) { + let skillId, x, y, unit, chargedItem, charge; + let chargedItems = []; + let validCharge = function (itemCharge) { + return itemCharge.skill === skillId && itemCharge.charges; + }; + + 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]; } - }, - larzuk: { - get: function () { - return me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete); + + 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)"); } - }, - savebarby: { - get: function () { - return me.getQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); + + 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]; } - }, - barbrescue: { - get: function () { - return me.getQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); + + 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) { + throw Error("invalid arguments, expected 'me' object or 'item' unit"); + } + + // Called the function the unit, me. + if (this === me) { + if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); + + chargedItems = []; + + // Item must be equipped, or a charm in inventory + this.getItemsEx(-1) + .filter(item => item && (item.isEquipped || (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 + }); + } + } + } + }); + + if (chargedItems.length === 0) throw Error("Don't have the charged skill (" + skillId + "), or not enough charges"); + + chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; + + return chargedItem.castChargedSkill.apply(chargedItem, args); + } else if (this.type === sdk.unittype.Item) { + charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates + + if (!charge) throw Error("No charged skill on this item"); + + if (skillId) { + // Filter out all other charged skills + charge = charge.filter(item => (skillId && item.skill === skillId) && !!item.charges); + } else if (charge.length > 1) { + throw new Error("multiple charges on this item without a given skillId"); } - }, - anya: { - get: function () { - return me.getQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed); + + charge = charge.first(); + + if (charge) { + // Setting skill on hand + if (!Config.PacketCasting || Config.PacketCasting === 1 && skillId !== sdk.skills.Teleport) { + return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); // Non packet casting + } + + // Packet casting + sendPacket(1, sdk.packets.send.SelectSkill, 2, charge.skill, 1, 0x0, 1, 0x00, 4, this.gid); + // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet + + // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked + sendPacket(1, sdk.packets.send.RightSkillOnLocation, 2, x || me.x, 2, y || me.y); // Cast the skill + + return true; } - }, - ancients: { - get: function () { - return me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed); + } + + return false; +}; + +/** + * @description equip an item. + */ +Unit.prototype.equip = function (destLocation = undefined) { + if (this.isEquipped) return true; // Item already equiped + + const findspot = function (item) { + let tempspot = Storage.Stash.FindSpot(item); + + if (getUIFlag(sdk.uiflags.Stash) && tempspot) { + return {location: Storage.Stash.location, coord: tempspot}; } - }, - baal: { - get: function () { - return me.getQuest(sdk.quest.id.EyeofDestruction, sdk.quest.states.Completed); + + tempspot = Storage.Inventory.FindSpot(item); + + return tempspot ? {location: Storage.Inventory.location, coord: tempspot} : false; + }; + const doubleHanded = [ + sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Polearm, sdk.items.type.Crossbow, + sdk.items.type.HandtoHand, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear + ]; + + // Not an item, or unidentified, or not enough stats + if (this.type !== sdk.unittype.Item || !this.getFlag(sdk.items.flags.Identified) + || this.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) + || this.dexreq > me.getStat(sdk.stats.Dexterity) + || this.strreq > me.getStat(sdk.stats.Strength)) { + return false; + } + + // If not a specific location is given, figure it out (can be useful to equip a double weapon) + !destLocation && (destLocation = this.getBodyLoc()); + // If destLocation isnt an array, make it one + !Array.isArray(destLocation) && (destLocation = [destLocation]); + + console.log("equiping " + this.name + " to bodylocation: " + destLocation.first()); + + let currentEquiped = me.getItemsEx(-1).filter(item => + destLocation.indexOf(item.bodylocation) !== -1 + || ( // Deal with double handed weapons + + (item.isOnMain) + && [sdk.body.RightArm, sdk.body.LeftArm].indexOf(destLocation) // in case destination is on the weapon/shield slot + && ( + doubleHanded.indexOf(this.itemType) !== -1 // this item is a double handed item + || doubleHanded.indexOf(item.itemType) !== -1 // current item is a double handed item + ) + ) + ).sort((a, b) => b - a); // shields first + + // if nothing is equipped at the moment, just equip it + if (!currentEquiped.length) { + clickItemAndWait(sdk.clicktypes.click.item.Left, this); + clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); + } else { + // unequip / swap items + currentEquiped.forEach((item, index) => { + // Last item, so swap instead of putting off first + if (index === (currentEquiped.length - 1)) { + print("swap " + this.name + " for " + item.name); + let oldLoc = {x: this.x, y: this.y, location: this.location}; + clickItemAndWait(sdk.clicktypes.click.item.Left, this); // Pick up current item + clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // the swap of items + // Find a spot for the current item + let spot = findspot(item); + + if (!spot) { // If no spot is found for the item, rollback + clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // swap again + clickItemAndWait(sdk.clicktypes.click.item.Left, oldLoc.x, oldLoc.y, oldLoc.location); // put item back on old spot + throw Error("cant find spot for unequipped item"); + } + + clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.y, spot.coord.x, spot.location); // put item on the found spot + + return; + } + + print("Unequip item first " + item.name); + // Incase multiple items are equipped + let spot = findspot(item); // Find a spot for the current item + + if (!spot) throw Error("cant find spot for unequipped item"); + + clickItemAndWait(sdk.clicktypes.click.item.Left, item.bodylocation); + clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.x, spot.coord.y, spot.location); + }); + } + + return { + success: this.bodylocation === destLocation.first(), + unequiped: currentEquiped, + rollback: () => currentEquiped.forEach(item => item.equip()) // Note; rollback only works if you had other items equipped before. + }; +}; + +Unit.prototype.getBodyLoc = function () { + const types = {}; + types[sdk.body.Head] = [sdk.items.type.Helm, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; // helm + types[sdk.body.Neck] = [sdk.items.type.Amulet]; // amulet + types[sdk.body.Armor] = [sdk.items.type.Armor]; // armor + types[sdk.body.RightArm] = [ + 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.HandtoHand, sdk.items.type.Orb, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.AssassinClaw + ]; // weapons + types[sdk.body.LeftArm] = [sdk.items.type.Shield, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads], // shields / Arrows / bolts + types[sdk.body.RingRight] = [sdk.items.type.Ring]; // ring slot 1 + types[sdk.body.RingLeft] = [sdk.items.type.Ring]; // ring slot 2 + types[sdk.body.Belt] = [sdk.items.type.Belt]; // belt + types[sdk.body.Feet] = [sdk.items.type.Boots]; // boots + types[sdk.body.Gloves] = [sdk.items.type.Gloves]; // gloves + //types[sdk.body.RightArmSecondary] = types[sdk.body.RightArm]; + //types[sdk.body.LeftArmSecondary] = types[sdk.body.LeftArm]; + let bodyLoc = []; + + for (let i in types) { + this.itemType && types[i].indexOf(this.itemType) !== -1 && bodyLoc.push(i); + } + + // Strings are hard to calculate with, parse to int + return bodyLoc.map(parseInt); +}; + +Unit.prototype.getRes = function (type, difficulty) { + if (!type || ![sdk.stats.FireResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, sdk.stats.LightningResist].includes(type)) { + return -1; + } + + difficulty === undefined || difficulty < 0 && (difficulty = 0); + difficulty > 2 && (difficulty = 2); + + let modifier = me.classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; + if (this === me) { + switch (type) { + case sdk.stats.FireResist: + me.getState(sdk.states.ShrineResFire) && (modifier += 75); + + break; + case sdk.stats.ColdResist: + me.getState(sdk.states.ShrineResCold) && (modifier += 75); + me.getState(sdk.states.Thawing) && (modifier += 50); + + break; + case sdk.stats.LightningResist: + me.getState(sdk.states.ShrineResLighting) && (modifier += 75); + + break; + case sdk.stats.PoisonResist: + me.getState(sdk.states.ShrineResPoison) && (modifier += 75); + me.getState(sdk.states.Antidote) && (modifier += 50); + + break; } - }, - // Misc - cows: { - get: function () { - return me.getQuest(sdk.quest.id.TheSearchForCain, 10); + } + return this.getStat(type) - modifier; +}; + +{ + let coords = function () { + if (Array.isArray(this) && this.length > 1) { + return [this[0], this[1]]; } - }, - respec: { - get: function () { - return me.getQuest(sdk.quest.id.Respec, sdk.quest.states.Completed); + + 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]; } - }, - diffCompleted: { - get: function () { - return !!((me.classic && me.diablo) || me.baal); + + return [undefined, undefined]; + }; + + Object.defineProperties(Object.prototype, { + distance: { + get: function () { + return !me.gameReady ? NaN : /* Math.round */(getDistance.apply(null, [me, ...coords.apply(this)])); + }, + enumerable: false, + }, + }); + + 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; } - }, -}); + }); +} + +Unit.prototype.hasEnchant = function (...enchants) { + if (!this.isMonster) return false; + for (let enchant of enchants) { + if (this.getEnchant(enchant)) return true; + } + return false; +}; + +Unit.prototype.usingShield = function () { + if (this.type > sdk.unittype.Monster) return false; + // always switch to main hand if we are checking ourselves + this === me && me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + let shield = this.getItemsEx(-1, sdk.items.mode.Equipped).filter(s => s.isShield).first(); + return !!shield; +}; // something in here is causing demon imps in barricade towers to be skipped - todo: figure out what Unit.prototype.__defineGetter__("attackable", function () { @@ -2648,38 +2135,41 @@ Unit.prototype.__defineGetter__("attackable", function () { return [sdk.monsters.ThroneBaal, sdk.monsters.Cow/*an evil force*/].indexOf(this.classid) === -1; }); -Unit.prototype.__defineGetter__("curseable", function () { - // must be player or monster - if (this === undefined || !copyUnit(this).x || this.type > 1) return false; - // Dead monster - if (this.hp === 0 || this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead) return false; - // attract can't be overridden - if (this.getState(sdk.states.Attract)) return false; - // "Possessed" - if (!!this.name && !!this.name.includes(getLocaleString(sdk.locale.text.Possessed))) return false; - if (this.type === sdk.unittype.Player && getPlayerFlag(me.gid, this.gid, 8) && !this.dead) return true; - // Friendly monster/NPC - if (this.getStat(sdk.stats.Alignment) === 2) return false; - // catapults were returning a level of 0 and hanging up clear scripts - if (this.charlvl < 1) return false; - // Monsters that are in flight - if ([ - sdk.monsters.CarrionBird1, sdk.monsters.UndeadScavenger, sdk.monsters.HellBuzzard, - sdk.monsters.WingedNightmare, sdk.monsters.SoulKiller2/*feel like this one is wrong*/, - sdk.monsters.CarrionBird2].includes(this.classid) && this.mode === sdk.monsters.mode.UsingSkill1) { - return false; - } - // Monsters that are Burrowed/Submerged - if ([ - sdk.monsters.SandMaggot, sdk.monsters.RockWorm, sdk.monsters.Devourer, sdk.monsters.GiantLamprey, sdk.monsters.WorldKiller2, - sdk.monsters.WaterWatcherLimb, sdk.monsters.RiverStalkerLimb, sdk.monsters.StygianWatcherLimb, - sdk.monsters.WaterWatcherHead, sdk.monsters.RiverStalkerHead, sdk.monsters.StygianWatcherHead].includes(this.classid) && this.mode === sdk.monsters.mode.Spawning) { - return false; - } +Object.defineProperty(Unit.prototype, "curseable", { + /** @this {Unit} */ + get: function () { + // must be player or monster + if (this === undefined || !copyUnit(this).x || this.type > 1) return false; + // Dead monster + if (this.hp === 0 || this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead) return false; + // attract can't be overridden + if (this.getState(sdk.states.Attract)) return false; + // "Possessed" + if (!!this.name && !!this.name.includes(getLocaleString(sdk.locale.text.Possessed))) return false; + if (this.type === sdk.unittype.Player && getPlayerFlag(me.gid, this.gid, 8) && !this.dead) return true; + // Friendly monster/NPC + if (this.getStat(sdk.stats.Alignment) === 2) return false; + // catapults were returning a level of 0 and hanging up clear scripts + if (this.charlvl < 1) return false; + // Monsters that are in flight + if ([ + sdk.monsters.CarrionBird1, sdk.monsters.UndeadScavenger, sdk.monsters.HellBuzzard, + sdk.monsters.WingedNightmare, sdk.monsters.SoulKiller2/*feel like this one is wrong*/, + sdk.monsters.CarrionBird2].includes(this.classid) && this.mode === sdk.monsters.mode.UsingSkill1) { + return false; + } + // Monsters that are Burrowed/Submerged + if ([ + sdk.monsters.SandMaggot, sdk.monsters.RockWorm, sdk.monsters.Devourer, sdk.monsters.GiantLamprey, sdk.monsters.WorldKiller2, + sdk.monsters.WaterWatcherLimb, sdk.monsters.RiverStalkerLimb, sdk.monsters.StygianWatcherLimb, + sdk.monsters.WaterWatcherHead, sdk.monsters.RiverStalkerHead, sdk.monsters.StygianWatcherHead].includes(this.classid) && this.mode === sdk.monsters.mode.Spawning) { + return false; + } - return (!this.isMonsterObject && !this.isMonsterEgg && !this.isMonsterNest && !this.isBaalTentacle && [ - sdk.monsters.WaterWatcherLimb, sdk.monsters.WaterWatcherHead, sdk.monsters.Flavie, sdk.monsters.ThroneBaal, sdk.monsters.Cow - ].indexOf(this.classid) === -1); + return (!this.isMonsterObject && !this.isMonsterEgg && !this.isMonsterNest && !this.isBaalTentacle && [ + sdk.monsters.WaterWatcherLimb, sdk.monsters.WaterWatcherHead, sdk.monsters.Flavie, sdk.monsters.ThroneBaal, sdk.monsters.Cow + ].indexOf(this.classid) === -1); + } }); Unit.prototype.__defineGetter__("scareable", function () { @@ -2744,17 +2234,19 @@ Unit.prototype.isUnit = function (classid = -1) { return this.classid === classid; }; -/** - * - * @param {any} key - * @returns value of key if it exists - * @description replicate .? operator of modern js since d2bs doesn't have it - */ -Object.prototype.test = function (key, last = false) { - if (this === undefined) return false; - return this[key] !== undefined ? this[key] : last ? null : {}; -}; -Object.defineProperty(Object.prototype, "test", { enumerable: false }); +Object.defineProperty(Object.prototype, "has", { + writable: true, + enumerable: false, + configurable: true, + value: function (...args) { + if (this === undefined) return undefined; + return this[args[0]] !== undefined + ? typeof this[args[0]] === "function" + ? this[args[0]].apply(this, ([...args].slice(1))) + : this[args[0]] + : {}; + } +}); PresetUnit.prototype.realCoords = function () { return { diff --git a/d2bs/kolbot/libs/common/Runewords.js b/d2bs/kolbot/libs/core/Runewords.js similarity index 99% rename from d2bs/kolbot/libs/common/Runewords.js rename to d2bs/kolbot/libs/core/Runewords.js index f51270314..5a101b634 100644 --- a/d2bs/kolbot/libs/common/Runewords.js +++ b/d2bs/kolbot/libs/core/Runewords.js @@ -349,8 +349,8 @@ const Runewords = { D2Bot.printToConsole("Made runeword: " + items[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""), sdk.colors.D2Bot.Green); if (NTIP.CheckItem(items[0], this.pickitEntries)) { - Misc.itemLogger("Runeword Kept", items[0]); - Misc.logItem("Runeword Kept", items[0]); + Item.logger("Runeword Kept", items[0]); + Item.logItem("Runeword Kept", items[0]); } } diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js new file mode 100644 index 000000000..523b2366e --- /dev/null +++ b/d2bs/kolbot/libs/core/Skill.js @@ -0,0 +1,775 @@ +/** +* @filename Skill.js +* @author kolton, theBGuy +* @desc Skill library +* +*/ + +(function() { + /** + * @constructor + * @param {number} skillId + */ + function SkillData(skillId) { + /** @type {boolean} */ + this.hardpoints = false; + /** @type {boolean} */ + this.checked = false; + /** @type {number} */ + this.manaCost = null; + // this part feels kinda ugly to me, todo is figure out a better method for conditon + /** @type {Function} */ + this.condition = typeof skillConditions[skillId] === "function" + ? skillConditions[skillId] + : () => true; + } + + /** + * @param {number} skill + */ + SkillData.prototype.have = function (skill) { + if (!this.condition()) return false; + if (this.hardpoints) return true; + if (!this.checked) { + this.hardpoints = !!me.getSkill(skill, sdk.skills.subindex.HardPoints); + this.checked = true; + } + + return (this.hardpoints || me.getSkill(skill, sdk.skills.subindex.SoftPoints)); + }; + + const skillConditions = {}; + skillConditions[sdk.skills.InnerSight] = () => Config.UseInnerSight; + skillConditions[sdk.skills.SlowMissiles] = () => Config.UseSlowMissiles; + skillConditions[sdk.skills.Decoy] = () => Config.UseDecoy; + skillConditions[sdk.skills.Valkyrie] = () => Config.SummonValkyrie; + skillConditions[sdk.skills.Telekinesis] = () => Config.UseTelekinesis; + skillConditions[sdk.skills.EnergyShield] = () => Config.UseEnergyShield; + skillConditions[sdk.skills.Charge] = () => Config.Charge; + skillConditions[sdk.skills.Vigor] = () => Config.Vigor || me.inTown; + skillConditions[sdk.skills.FindItem] = () => Config.FindItem; + skillConditions[sdk.skills.Raven] = () => Config.SummonRaven; + skillConditions[sdk.skills.BurstofSpeed] = () => !Config.UseBoS && !me.inTown; + skillConditions[sdk.skills.Fade] = () => Config.UseFade; + skillConditions[sdk.skills.BladeShield] = () => Config.UseBladeShield; + skillConditions[sdk.skills.Venom] = () => Config.UseVenom; + + const Skill = { + usePvpRange: false, + charges: [], + manaCostList: {}, + needFloor: [ + sdk.skills.Blizzard, sdk.skills.Meteor, sdk.skills.Fissure, sdk.skills.Volcano, sdk.skills.ShockWeb, sdk.skills.LeapAttack, sdk.skills.Hydra + ], + missileSkills: [ + sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.ColdArrow, sdk.skills.MultipleShot, sdk.skills.PoisonJavelin, sdk.skills.ExplodingArrow, + sdk.skills.LightningBolt, sdk.skills.IceArrow, sdk.skills.GuidedArrow, sdk.skills.PlagueJavelin, sdk.skills.Strafe, sdk.skills.ImmolationArrow, + sdk.skills.FreezingArrow, sdk.skills.LightningFury, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.FireBolt, sdk.skills.Inferno, + sdk.skills.IceBlast, sdk.skills.FireBall, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.GlacialSpike, sdk.skills.FrozenOrb, + sdk.skills.Teeth, sdk.skills.BoneSpear, sdk.skills.BoneSpirit, sdk.skills.HolyBolt, sdk.skills.FistoftheHeavens, sdk.skills.DoubleThrow, + sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, sdk.skills.Twister, sdk.skills.Tornado, sdk.skills.FireBlast + ], + + getClassSkillRange: function (classid = me.classid) { + switch (classid) { + case sdk.player.class.Amazon: + return [sdk.skills.MagicArrow, sdk.skills.LightningFury]; + case sdk.player.class.Sorceress: + return [sdk.skills.FireBolt, sdk.skills.ColdMastery]; + case sdk.player.class.Necromancer: + return [sdk.skills.AmplifyDamage, sdk.skills.Revive]; + case sdk.player.class.Paladin: + return [sdk.skills.Sacrifice, sdk.skills.Salvation]; + case sdk.player.class.Barbarian: + return [sdk.skills.Bash, sdk.skills.BattleCommand]; + case sdk.player.class.Druid: + return [sdk.skills.Raven, sdk.skills.Hurricane]; + case sdk.player.class.Assassin: + return [sdk.skills.FireBlast, sdk.skills.PhoenixStrike]; + default: + return [0, 0]; + } + }, + + skills: { + /** @type {Object.} */ + all: {}, + initialized: false, + + init: function () { + // should all skills for all classes be initialized? We really only care about ours? + for (let i = sdk.player.class.Amazon; i <= sdk.player.class.Assassin; i++) { + let [min, max] = Skill.getClassSkillRange(i); + + for (let skill = min; skill <= max; skill++) { + // should this just be on the Skill object itself? + Skill.skills.all[skill] = new SkillData(skill); + } + } + + Skill.skills.initialized = true; + }, + have: function (skill = 0) { + // ensure the values have been initialized + !this.initialized && this.init(); + return typeof this.all[skill] !== "undefined" && this.all[skill].have(skill); + }, + reset: function () { + let [min, max] = Skill.getClassSkillRange(); + + for (let i = min; i <= max; i++) { + if (typeof this.all[i] !== "undefined" && !this.all[i].hardpoints) { + this.all[i].checked = false; + } + } + } + }, + + // initialize our skill data + init: function () { + // reset check values + !Skill.skills.initialized ? Skill.skills.init() : Skill.skills.reset(); + // reset mana values + Skill.manaCostList = {}; + + switch (me.classid) { + case sdk.player.class.Amazon: + break; + case sdk.player.class.Sorceress: + if (Config.UseColdArmor === true) { + Precast.skills.coldArmor.best = (function () { + let coldArmor = [ + {skillId: sdk.skills.ShiverArmor, level: me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints)}, + {skillId: sdk.skills.ChillingArmor, level: me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints)}, + {skillId: sdk.skills.FrozenArmor, level: me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints)}, + ].filter(skill => !!skill.level && skill.level > 0).sort((a, b) => b.level - a.level).first(); + return coldArmor !== undefined ? coldArmor.skillId : false; + })(); + Precast.skills.coldArmor.duration = this.getDuration(Precast.skills.coldArmor.best); + } else { + Precast.skills.coldArmor.duration = this.getDuration(Config.UseColdArmor); + } + + break; + case sdk.player.class.Necromancer: + { + let bMax = me.getStat(sdk.stats.SkillBoneArmorMax); + bMax > 0 && (Precast.skills.boneArmor.max = bMax); + } + if (!!Config.Golem && Config.Golem !== "None") { + // todo: change Config.Golem to use skillid instead of 0, 1, 2, and 3 + } + break; + case sdk.player.class.Paladin: + // how to handle if someone manually equips a shield during game play, don't want to build entire item list if we don't need to + // maybe store gid of shield, would still require doing me.getItem(-1, 1, gid) everytime we wanted to cast but that's still less involved + // than getting every item we have and finding shield, for now keeping this. Checks during init if we have a shield or not + Precast.skills.holyShield.canUse = me.usingShield(); + + break; + case sdk.player.class.Barbarian: + Skill.canUse(sdk.skills.Shout) && (Precast.skills.shout.duration = this.getDuration(sdk.skills.Shout)); + Skill.canUse(sdk.skills.BattleOrders) && (Precast.skills.battleOrders.duration = this.getDuration(sdk.skills.BattleOrders)); + Skill.canUse(sdk.skills.BattleCommand) && (Precast.skills.battleCommand.duration = this.getDuration(sdk.skills.BattleCommand)); + + break; + case sdk.player.class.Druid: + if (!!Config.SummonAnimal && Config.SummonAnimal !== "None") { + // todo: change Config.SummonAnimal to use skillid instead of 0, 1, 2, and 3 + } + if (!!Config.SummonVine && Config.SummonVine !== "None") { + // todo: change Config.SummonVine to use skillid instead of 0, 1, 2, and 3 + } + if (!!Config.SummonSpirit && Config.SummonSpirit !== "None") { + // todo: change Config.SummonSpirit to use skillid instead of 0, 1, 2, and 3 + } + break; + case sdk.player.class.Assassin: + if (!!Config.SummonShadow) { + // todo: change Config.SummonShadow to use skillid instead of 0, 1, 2, and 3 + } + break; + } + }, + + canUse: function (skillId = -1) { + try { + if (skillId === -1) return false; + if (skillId >= sdk.skills.Attack && skillId <= sdk.skills.LeftHandSwing) return true; + let valid = Skill.skills.have(skillId); + + return valid; + } catch (e) { + return false; + } + }, + + getDuration: function (skillId = -1) { + return Time.seconds((() => { + switch (skillId) { + case sdk.skills.Decoy: + return ((10 + me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints) * 5)); + case sdk.skills.FrozenArmor: + return (((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10))); + case sdk.skills.ShiverArmor: + return (((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10))); + case sdk.skills.ChillingArmor: + return (((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10))); + case sdk.skills.EnergyShield: + return (84 + (60 * me.getSkill(sdk.skills.EnergyShield, sdk.skills.subindex.SoftPoints))); + case sdk.skills.ThunderStorm: + return (24 + (8 * me.getSkill(sdk.skills.ThunderStorm, sdk.skills.subindex.SoftPoints))); + case sdk.skills.Shout: + return (((10 + me.getSkill(sdk.skills.Shout, sdk.skills.subindex.SoftPoints) * 10) + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5))); + case sdk.skills.BattleOrders: + return (((20 + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) * 10) + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5))); + case sdk.skills.BattleCommand: + return (((10 * me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.SoftPoints) - 5) + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints)) * 5))); + case sdk.skills.HolyShield: + return (5 + (25 * me.getSkill(sdk.skills.HolyShield, sdk.skills.subindex.SoftPoints))); + case sdk.skills.Hurricane: + return (10 + (2 * me.getSkill(sdk.skills.CycloneArmor, sdk.skills.subindex.HardPoints))); + case sdk.skills.Werewolf: + case sdk.skills.Werebear: + return (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)); + case sdk.skills.BurstofSpeed: + return (108 + (12 * me.getSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.SoftPoints))); + case sdk.skills.Fade: + return (108 + (12 * me.getSkill(sdk.skills.Fade, sdk.skills.subindex.SoftPoints))); + case sdk.skills.Venom: + return (116 + (4 * me.getSkill(sdk.skills.Venom, sdk.skills.subindex.SoftPoints))); + case sdk.skills.BladeShield: + return (15 + (5 * me.getSkill(sdk.skills.BladeShield, sdk.skills.subindex.SoftPoints))); + default: + return 0; + } + })()); + }, + + getMaxSummonCount: function (skillId) { + let skillNum = 0; + + switch (skillId) { + case sdk.skills.Raven: + return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 5); + case sdk.skills.SummonSpiritWolf: + return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 5); + case sdk.skills.SummonDireWolf: + return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 3); + case sdk.skills.RaiseSkeleton: + case sdk.skills.RaiseSkeletalMage: + skillNum = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); + return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); + case sdk.skills.Revive: + return me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints); + case sdk.skills.ShadowWarrior: + case sdk.skills.ShadowMaster: + case sdk.skills.PoisonCreeper: + case sdk.skills.CarrionVine: + case sdk.skills.SolarCreeper: + case sdk.skills.OakSage: + case sdk.skills.HeartofWolverine: + case sdk.skills.SpiritofBarbs: + case sdk.skills.SummonGrizzly: + case sdk.skills.ClayGolem: + case sdk.skills.BloodGolem: + case sdk.skills.FireGolem: + case sdk.skills.Valkyrie: + return 1; + } + + return 0; + }, + + getRange: function (skillId) { + switch (skillId) { + case sdk.skills.Attack: + return Attack.usingBow() ? 20 : 3; + case sdk.skills.Kick: + case sdk.skills.LeftHandSwing: + case sdk.skills.Jab: + case sdk.skills.PowerStrike: + case sdk.skills.ChargedStrike: + case sdk.skills.LightningStrike: + case sdk.skills.Impale: + case sdk.skills.Fend: + case sdk.skills.Blaze: + case sdk.skills.PoisonDagger: + case sdk.skills.Sacrifice: + case sdk.skills.Smite: + case sdk.skills.Zeal: + case sdk.skills.Vengeance: + case sdk.skills.Conversion: + case sdk.skills.BlessedHammer: + case sdk.skills.FindPotion: + case sdk.skills.FindItem: + case sdk.skills.GrimWard: + case sdk.skills.Bash: + case sdk.skills.DoubleSwing: + case sdk.skills.Stun: + case sdk.skills.Concentrate: + case sdk.skills.Frenzy: + case sdk.skills.Berserk: + case sdk.skills.FeralRage: + case sdk.skills.Maul: + case sdk.skills.Rabies: + case sdk.skills.FireClaws: + case sdk.skills.Hunger: + case sdk.skills.Fury: + case sdk.skills.DragonTalon: + case sdk.skills.DragonClaw: + case sdk.skills.DragonTail: + return 3; + case sdk.skills.BattleCry: + case sdk.skills.WarCry: + return 4; + case sdk.skills.FrostNova: + case sdk.skills.Twister: + case sdk.skills.Tornado: + case sdk.skills.Summoner: + return 5; + case sdk.skills.ChargedBolt: + return 6; + case sdk.skills.Nova: + case sdk.skills.Whirlwind: + return 7; + case sdk.skills.PoisonNova: + return 8; + case sdk.skills.Armageddon: + return 9; + case sdk.skills.PoisonJavelin: + case sdk.skills.PlagueJavelin: + case sdk.skills.HolyBolt: + case sdk.skills.Charge: + case sdk.skills.Howl: + case sdk.skills.Firestorm: + case sdk.skills.MoltenBoulder: + case sdk.skills.ShockWave: + return 10; + case sdk.skills.InnerSight: + case sdk.skills.SlowMissiles: + return 13; + case sdk.skills.LightningFury: + case sdk.skills.FrozenOrb: + case sdk.skills.Teeth: + case sdk.skills.Fissure: + case sdk.skills.Volcano: + case sdk.skills.FireBlast: + case sdk.skills.ShockWeb: + case sdk.skills.BladeSentinel: + case sdk.skills.BladeFury: + return 15; + case sdk.skills.FireArrow: + case sdk.skills.MultipleShot: + case sdk.skills.ExplodingArrow: + case sdk.skills.GuidedArrow: + case sdk.skills.ImmolationArrow: + case sdk.skills.FreezingArrow: + case sdk.skills.IceBolt: + case sdk.skills.IceBlast: + case sdk.skills.FireBolt: + case sdk.skills.Revive: + case sdk.skills.FistoftheHeavens: + case sdk.skills.DoubleThrow: + case sdk.skills.PsychicHammer: + case sdk.skills.DragonFlight: + return 20; + case sdk.skills.LowerResist: + return 50; + // Variable range + case sdk.skills.StaticField: + return Math.floor((me.getSkill(sdk.skills.StaticField, sdk.skills.subindex.SoftPoints) + 4) * 2 / 3); + case sdk.skills.Leap: + { + let skLvl = me.getSkill(sdk.skills.Leap, sdk.skills.subindex.SoftPoints); + return Math.floor(Math.min(4 + (26 * ((110 * skLvl / (skLvl + 6)) / 100)), 30) * (2 / 3)); + } + case sdk.skills.ArcticBlast: + { + let skLvl = me.getSkill(sdk.skills.ArcticBlast, sdk.skills.subindex.SoftPoints); + let range = Math.floor(((33 + (2 * skLvl)) / 4) * (2 / 3)); + // Druid using this on physical immunes needs the monsters to be within range of hurricane + range > 6 && Config.AttackSkill[5] === sdk.skills.ArcticBlast && (range = 6); + + return range; + } + case sdk.skills.Lightning: + case sdk.skills.BoneSpear: + case sdk.skills.BoneSpirit: + return !!this.usePvpRange ? 35 : 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 ? 35 : 20; + } + + // Every other skill + return !!this.usePvpRange ? 30 : 20; + }, + + getHand: function (skillId) { + switch (skillId) { + case sdk.skills.MagicArrow: + case sdk.skills.FireArrow: + case sdk.skills.ColdArrow: + case sdk.skills.MultipleShot: + case sdk.skills.PoisonJavelin: + case sdk.skills.ExplodingArrow: + case sdk.skills.Impale: + case sdk.skills.LightningBolt: + case sdk.skills.IceArrow: + case sdk.skills.GuidedArrow: + case sdk.skills.PlagueJavelin: + case sdk.skills.Strafe: + case sdk.skills.ImmolationArrow: + case sdk.skills.Fend: + case sdk.skills.FreezingArrow: + case sdk.skills.LightningFury: + case sdk.skills.FireBolt: + case sdk.skills.ChargedBolt: + case sdk.skills.IceBolt: + case sdk.skills.Inferno: + case sdk.skills.IceBlast: + case sdk.skills.FireBall: + case sdk.skills.Lightning: + case sdk.skills.ChainLightning: + case sdk.skills.GlacialSpike: + case sdk.skills.FrozenOrb: + case sdk.skills.Teeth: + case sdk.skills.PoisonDagger: + case sdk.skills.BoneSpear: + case sdk.skills.BoneSpirit: + case sdk.skills.HolyBolt: + case sdk.skills.Charge: + case sdk.skills.BlessedHammer: + case sdk.skills.FistoftheHeavens: + case sdk.skills.Leap: + case sdk.skills.DoubleThrow: + case sdk.skills.LeapAttack: + case sdk.skills.Whirlwind: + case sdk.skills.Firestorm: + case sdk.skills.MoltenBoulder: + case sdk.skills.ArcticBlast: + case sdk.skills.Twister: + case sdk.skills.ShockWave: + case sdk.skills.Tornado: + case sdk.skills.FireBlast: + case sdk.skills.TigerStrike: + case sdk.skills.ShockWeb: + case sdk.skills.BladeSentinel: + case sdk.skills.FistsofFire: + case sdk.skills.CobraStrike: + case sdk.skills.BladeFury: + case sdk.skills.ClawsofThunder: + case sdk.skills.BladesofIce: + case sdk.skills.DragonFlight: + return sdk.skills.hand.Left; + case sdk.skills.Attack: + case sdk.skills.Jab: + case sdk.skills.PowerStrike: + case sdk.skills.ChargedStrike: + case sdk.skills.LightningStrike: + case sdk.skills.Sacrifice: + case sdk.skills.Smite: + case sdk.skills.Zeal: + case sdk.skills.Vengeance: + case sdk.skills.Conversion: + case sdk.skills.Bash: + case sdk.skills.DoubleSwing: + case sdk.skills.Stun: + case sdk.skills.Concentrate: + case sdk.skills.Frenzy: + case sdk.skills.Berserk: + case sdk.skills.FeralRage: + case sdk.skills.Maul: + case sdk.skills.Rabies: + case sdk.skills.FireClaws: + case sdk.skills.Hunger: + case sdk.skills.Fury: + case sdk.skills.DragonTalon: + case sdk.skills.DragonClaw: + case sdk.skills.DragonTail: + return sdk.skills.hand.LeftNoShift; // Shift bypass + } + + // Every other skill + return sdk.skills.hand.Right; + }, + + // Get mana cost of the skill (mBot) + getManaCost: function (skillId) { + if (skillId < sdk.skills.MagicArrow) return 0; + if (this.manaCostList.hasOwnProperty(skillId)) return this.manaCostList[skillId]; + + let skillLvl = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); + let 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("skills", skillId, "manashift")] / 256), getBaseStat("skills", skillId, "minmana")); + + if (!this.manaCostList.hasOwnProperty(skillId)) { + this.manaCostList[skillId] = ret; + } + + return ret; + }, + + // Put a skill on desired slot + setSkill: function (skillId, hand, item) { + // Check if the skill is already set + if (me.getSkill(hand === sdk.skills.hand.Right ? sdk.skills.get.RightId : sdk.skills.get.LeftId) === skillId) return true; + if (!item && !Skill.canUse(skillId)) return false; + + // Charged skills must be cast from right hand + if (hand === undefined || hand === sdk.skills.hand.RightShift || item) { + item && hand !== sdk.skills.hand.Right && console.warn("[ÿc9Warningÿc0] charged skills must be cast from right hand"); + hand = sdk.skills.hand.Right; + } + + return (me.setSkill(skillId, hand, item)); + }, + + // Timed skills + isTimed: function (skillId) { + return [ + sdk.skills.PoisonJavelin, sdk.skills.PlagueJavelin, sdk.skills.ImmolationArrow, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, + sdk.skills.Hydra, sdk.skills.FrozenOrb, sdk.skills.FistoftheHeavens, sdk.skills.Firestorm, sdk.skills.Werewolf, sdk.skills.Werebear, sdk.skills.MoltenBoulder, + sdk.skills.Fissure, sdk.skills.Volcano, sdk.skills.Grizzly, sdk.skills.Armageddon, sdk.skills.Hurricane, sdk.skills.ShockWeb, sdk.skills.ShadowWarrior, + sdk.skills.DragonFlight, sdk.skills.BladeShield, sdk.skills.ShadowMaster + ].includes(skillId); + }, + + // Skills that cn be cast in town + townSkill: function (skillId = -1) { + return [ + sdk.skills.Valkyrie, sdk.skills.FrozenArmor, sdk.skills.Telekinesis, sdk.skills.ShiverArmor, sdk.skills.Enchant, sdk.skills.ThunderStorm, sdk.skills.EnergyShield, sdk.skills.ChillingArmor, + sdk.skills.BoneArmor, sdk.skills.ClayGolem, sdk.skills.BloodGolem, sdk.skills.FireGolem, sdk.skills.HolyShield, sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.Werewolf, sdk.skills.Werebear, + sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, sdk.skills.CycloneArmor, sdk.skills.HeartofWolverine, sdk.skills.SummonDireWolf, sdk.skills.SolarCreeper, + sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.BurstofSpeed, sdk.skills.Fade, sdk.skills.ShadowWarrior, sdk.skills.BladeShield, sdk.skills.Venom, sdk.skills.ShadowMaster + ].includes(skillId); + }, + + // Wereform skill check + wereFormCheck: function (skillId) { + // we don't even have the skills to transform or we aren't transformed - add handler for wereform given by an item that is on switch + if (!Skill.canUse(sdk.skills.Werewolf) && !Skill.canUse(sdk.skills.Werebear)) return true; + if (!me.getState(sdk.states.Wearwolf) && !me.getState(sdk.states.Wearbear)) return true; + + // Can be cast by both + if ([sdk.skills.Attack, sdk.skills.Kick, sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, + sdk.skills.HeartofWolverine, sdk.skills.SummonDireWolf, sdk.skills.FireClaws, sdk.skills.SolarCreeper, sdk.skills.Hunger, sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.Armageddon].includes(skillId)) { + return true; + } + + // Can be cast by werewolf only + if (me.getState(sdk.states.Wearwolf) && [sdk.skills.Werewolf, sdk.skills.FeralRage, sdk.skills.Rabies, sdk.skills.Fury].includes(skillId)) return true; + // Can be cast by werebear only + if (me.getState(sdk.states.Wearbear) && [sdk.skills.Werebear, sdk.skills.Maul, sdk.skills.ShockWave].includes(skillId)) return true; + + return false; + }, + + // Change into werewolf or werebear + shapeShift: function (mode) { + let [skill, state] = (() => { + switch (mode.toString().toLowerCase()) { + case "0": + return [-1, -1]; + case "1": + case "werewolf": + return [sdk.skills.Werewolf, sdk.states.Wearwolf]; + case "2": + case "werebear": + return [sdk.skills.Werebear, sdk.states.Wearbear]; + default: + throw new Error("shapeShift: Invalid parameter"); + } + })(); + + // don't have wanted skill + if (!Skill.canUse(skill)) return false; + // already in wanted state + if (me.getState(state)) return true; + + let slot = Attack.getPrimarySlot(); + me.switchWeapons(Precast.getBetterSlot(skill)); + + for (let i = 0; i < 3; i += 1) { + Skill.cast(skill, sdk.skills.hand.Right); + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (me.getState(state)) { + delay(250); + me.weaponswitch !== slot && me.switchWeapons(slot); + + return true; + } + + delay(10); + } + } + + me.weaponswitch !== slot && me.switchWeapons(slot); + + return false; + }, + + // Change back to human shape + unShift: function () { + if (me.getState(sdk.states.Wearwolf) || me.getState(sdk.states.Wearbear)) { + for (let i = 0; i < 3; i += 1) { + Skill.cast(me.getState(sdk.states.Wearwolf) ? sdk.skills.Werewolf : sdk.skills.Werebear); + + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (!me.getState(sdk.states.Wearwolf) && !me.getState(sdk.states.Wearbear)) { + delay(250); + + return true; + } + + delay(10); + } + } + } else { + return true; + } + + return false; + }, + + useTK: function (unit = undefined) { + try { + if (!unit || !Skill.canUse(sdk.skills.Telekinesis) + || typeof unit !== "object" || unit.type !== sdk.unittype.Object + || unit.name.toLowerCase() === "dummy" + || (unit.name.toLowerCase() === "portal" && !me.inTown && unit.classid !== sdk.objects.ArcaneSanctuaryPortal) + || [sdk.objects.RedPortalToAct4, sdk.objects.WorldstonePortal, sdk.objects.RedPortal, sdk.objects.RedPortalToAct5].includes(unit.classid)) { + return false; + } + + return me.inTown || (me.mpPercent > 25); + } catch (e) { + return false; + } + }, + + // Cast a skill on self, Unit or coords + cast: function (skillId, hand, x, y, item) { + switch (true) { + case me.inTown && !this.townSkill(skillId): + case !item && (this.getManaCost(skillId) > me.mp || !this.canUse(skillId)): + case !this.wereFormCheck(skillId): + return false; + case skillId === undefined: + throw new Error("Unit.cast: Must supply a skill ID"); + } + + if (skillId === sdk.skills.Telekinesis && typeof x === "object" && Packet.telekinesis(x)) { + delay(250); + return true; + } + + 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)) { + delay(300); + } + + return false; + } + + if (skillId === sdk.skills.Teleport) { + if (typeof x === "number") { + const orgDist = [x, y].distance; + if (Packet.teleport(x, y)) { + return Misc.poll(() => [x, y].distance < orgDist, 300, 25); + } + } + } + + if (!this.setSkill(skillId, hand, item)) return false; + + if (Config.PacketCasting > 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 += 1) { + if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { + break; + } + + delay(10); + } + } + + return true; + }, + }; + + Object.defineProperties(Skill, { + haveTK: { + get: function () { + return Skill.canUse(sdk.skills.Telekinesis); + }, + }, + }); + + // export to the global scope + global.Skill = Skill; +})(); diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js new file mode 100644 index 000000000..3e9c082d1 --- /dev/null +++ b/d2bs/kolbot/libs/core/Storage.js @@ -0,0 +1,402 @@ +/** +* @filename Storage.js +* @author McGod, kolton (small kolbot related edits) +* @desc manage inventory, belt, stash, cube +* +*/ + +(function() { + let Container = function (name, width, height, location) { + let h, w; + + this.name = name; + this.width = width; + this.height = height; + this.location = location; + this.buffer = []; + this.itemList = []; + this.openPositions = this.height * this.width; + + // Initalize the buffer array for use, set all as empty. + for (h = 0; h < this.height; h += 1) { + this.buffer.push([]); + + for (w = 0; w < this.width; w += 1) { + this.buffer[h][w] = 0; + } + } + + /* Container.Mark(item) + * Marks the item in the buffer, and adds it to the item list. + */ + this.Mark = function (item) { + let x, y; + + //Make sure it is in this container. + if (item.location !== this.location || (item.mode !== 0 && item.mode !== 2)) { + 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; + }; + + /* Container.isLocked(item) + * Checks if the item is in a locked spot + */ + this.IsLocked = function (item, baseRef) { + let h, w, reference; + + reference = baseRef.slice(0); + + //Make sure it is in this container. + if (item.mode !== 0 || 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; + }; + + this.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; + }; + + /* Container.CanFit(item) + * Checks to see if we can fit the item in the buffer. + */ + this.CanFit = function (item) { + return (!!this.FindSpot(item)); + }; + + /* Container.FindSpot(item) + * Finds a spot available in the buffer to place the item. + */ + this.FindSpot = function (item) { + let x, y, nx, ny; + + //Make sure it's a valid item + if (!item) { + return false; + } + + Storage.Reload(); + + //Loop buffer looking for spot to place item. + for (y = 0; y < this.width - (item.sizex - 1); y += 1) { + Loop: + for (x = 0; x < this.height - (item.sizey - 1); x += 1) { + //Check if there is something in this spot. + if (this.buffer[x][y] > 0) { + 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]) { + continue Loop; + } + } + } + + return ({x: x, y: y}); + } + } + + return false; + }; + + /* Container.MoveTo(item) + * Takes any item and moves it into given buffer. + */ + this.MoveTo = function (item) { + let nPos, n, nDelay, cItem, cube; + + try { + //Can we even fit it in here? + nPos = this.FindSpot(item); + + if (!nPos) { + return false; + } + + //Cube -> Stash, must place item in inventory first + if (item.location === 6 && this.location === 7 && !Storage.Inventory.MoveTo(item)) { + return false; + } + + //Can't deal with items on ground! + if (item.mode === 3) { + return false; + } + + //Item already on the cursor. + if (me.itemoncursor && item.mode !== 4) { + return false; + } + + //Make sure stash is open + if (this.location === 7 && !Town.openStash()) { + return false; + } + + //Pick to cursor if not already. + if (!item.toCursor()) { + return false; + } + + //Loop three times to try and place it. + for (n = 0; n < 5; n += 1) { + if (this.location === 6) { // place item into cube + cItem = Game.getCursorUnit(); + cube = me.getItem(sdk.quest.item.Cube); + + if (cItem !== null && cube !== null) { + sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); + } + } else if (this.location === 2) { + cItem = Game.getCursorUnit(); + if (cItem !== null) { + sendPacket(1, sdk.packets.send.ItemToBelt, 4, cItem.gid, 4, nPos.y); + } + } else { + clickItemAndWait(sdk.clicktypes.click.item.Left, nPos.y, nPos.x, this.location); + } + + nDelay = getTickCount(); + + while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 3 + 500)) { + if (!me.itemoncursor) { + print("Successfully placed " + item.name + " at X: " + nPos.x + " Y: " + nPos.y); + delay(200); + + return true; + } + + delay(10); + } + } + + return true; + } catch (e) { + return false; + } + }; + + /* Container.Dump() + * Prints all known information about container. + */ + this.Dump = function () { + let x, y, string; + + print(this.name + " has the width of " + this.width + " and the height of " + this.height); + print(this.name + " has " + this.itemList.length + " items inside, and has " + this.openPositions + " spots left."); + + 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"; + } + + print(string); + } + }; + + /* Container.UsedSpacePercent() + * Returns percentage of the container used. + */ + this.UsedSpacePercent = function () { + let x, y, + usedSpace = 0, + totalSpace = this.height * this.width; + + Storage.Reload(); + + for (x = 0; x < this.height; x += 1) { + for (y = 0; y < this.width; y += 1) { + if (this.buffer[x][y] > 0) { + usedSpace += 1; + } + } + } + + return usedSpace * 100 / totalSpace; + }; + + /* Container.compare(reference) + * Compare given container versus the current one, return all new items in current buffer. + */ + this.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; + } + }; + + this.toSource = function () { + return this.buffer.toSource(); + }; + }; + + let Storage = new function () { + this.Init = function () { + this.StashY = me.gametype === 0 ? 4 : 8; + this.Inventory = new Container("Inventory", 10, 4, 3); + this.TradeScreen = new Container("Inventory", 10, 4, 5); + this.Stash = new Container("Stash", 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) { // nothing equipped + return 1; + } + + do { + if (item.bodylocation === 8) { // belt slot + 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 3: + this.Inventory.Mark(item); + + break; + case 5: + this.TradeScreen.Mark(item); + + break; + case 2: + this.Belt.Mark(item); + + break; + case 6: + this.Cube.Mark(item); + + break; + case 7: + this.Stash.Mark(item); + + break; + } + } while (item.getNext()); + + return true; + }; + }; + + // export to global scope + global.Storage = Storage; +})(); diff --git a/d2bs/kolbot/libs/common/Town.js b/d2bs/kolbot/libs/core/Town.js similarity index 84% rename from d2bs/kolbot/libs/common/Town.js rename to d2bs/kolbot/libs/core/Town.js index 45ca80f64..58c5023fe 100644 --- a/d2bs/kolbot/libs/common/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -5,6 +5,9 @@ * */ +/** + * @enum {string} + */ const NPC = { Akara: getLocaleString(sdk.locale.npcs.Akara).toLowerCase(), Gheed: getLocaleString(sdk.locale.npcs.Gheed).toLowerCase(), @@ -39,15 +42,27 @@ const NPC = { Cain: getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase() }; +/** + * @namespace + */ const Town = { telekinesis: true, sellTimer: getTickCount(), // shop speedup test + /** + * @namespace + */ lastInteractedNPC: { - unit: null, tick: 0, + /** + * @type {NPCUnit} + */ + unit: null, + /** + * @param {NPCUnit} npc + */ set: function (npc) { - this.unit = npc; - this.tick = getTickCount(); + Town.lastInteractedNPC.unit = npc; + Town.lastInteractedNPC.tick = getTickCount(); }, get: function () { try { @@ -68,8 +83,8 @@ const Town = { } }, reset: function () { - this.unit = null; - this.tick = 0; + Town.lastInteractedNPC.unit = null; + Town.lastInteractedNPC.tick = 0; } }, @@ -89,70 +104,18 @@ const Town = { sdk.items.type.AntidotePotion, sdk.items.type.ThawingPotion ], + /** + * @description Check if item type is included in the ignore types list + * @param {number} type + * @returns {boolean} If it is an item in the list + */ ignoreType: function (type) { return Town.ignoredItemTypes.includes(type); }, - 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: [], - }; - 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; - } - - // if we have no belt what should qualify is to go to town at this point? - // we've confirmed having at least some potions in the above check - // if (!me.inTown && Storage.BeltSize() === 1) return false; - - // 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; - }, - - // Do town chores + /** + * @param {boolean} repair + */ doChores: function (repair = false) { delay(250); @@ -197,9 +160,13 @@ const Town = { return true; }, + /** + * @param {string} name + * @param {boolean} cancel + * @returns {boolean | Unit} + */ npcInteract: function (name = "", cancel = true) { - !name.includes("_") && (name = name.capitalize(true)); - name.includes("_") && (name = "Qual_Kehk"); + name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); !me.inTown && Town.goToTown(); me.cancelUIFlags(); @@ -239,7 +206,9 @@ const Town = { return false; }, - // just consumables + /** + * @description handle quest consumables if we have them + */ checkQuestItems: function () { // Radament skill book let book = me.getItem(sdk.quest.item.BookofSkill); @@ -282,41 +251,12 @@ const Town = { } }, - getTpTool: function () { - let items = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((item) => item.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(item.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; - }, - - getIdTool: function () { - let items = me.getItemsEx().filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); - if (!items.length) return null; - let tome = items.find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify && i.getStat(sdk.stats.Quantity) > 0); - if (tome) return tome; - let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); - if (scroll) return scroll; - return null; - }, - - canTpToTown: function () { - // can't tp if dead - if (me.dead) return false; - let badAreas = [ - sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.KurastDocktown, - sdk.areas.PandemoniumFortress, sdk.areas.Harrogath, sdk.areas.ArreatSummit, sdk.areas.UberTristram - ]; - let myArea = me.area; - // 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 !!this.getTpTool(); - }, - - // Start a task and return the NPC Unit + /** + * @description Start a task and return the NPC Unit + * @param {string} task + * @param {string} reason + * @returns {boolean | Unit} + */ initNPC: function (task = "", reason = "undefined") { console.time("initNPC"); console.info(true, reason); @@ -325,7 +265,7 @@ const Town = { delay(250); let npc = this.lastInteractedNPC.get(); - let justUseClosest = (["clearInventory", "sell"].includes(reason) && !Town.getUnids()); + let justUseClosest = (["clearInventory", "sell"].includes(reason) && !me.getUnids()); try { if (!!npc) { @@ -418,28 +358,14 @@ const Town = { return npc; }, - // Go to a town healer + /** + * @description Go to a town healer if we are below certain hp/mp percent or have a status effect + */ heal: function () { - if (!this.needHealing()) return true; + if (!me.needHealing()) return true; return !!(this.initNPC("Heal", "heal")); }, - // Check if healing is needed, based on character config - needHealing: function () { - if (me.hpPercent <= Config.HealHP || me.mpPercent <= Config.HealMP) return true; - - // Status effects - return (Config.HealStatus - && [ - sdk.states.Poison, - sdk.states.AmplifyDamage, - sdk.states.Frozen, - sdk.states.Weaken, - sdk.states.Decrepify, - sdk.states.LowerResist - ].some((state) => me.getState(state))); - }, - buyPotions: function () { // Ain't got money fo' dat shyt if (me.gold < 1000) return false; @@ -552,7 +478,11 @@ const Town = { return true; }, - // Check when to shift-buy potions + /** + * @description Check when to shift-buy potions + * @param {number} col + * @param {0 | 1 | 2 | 3 | 4} beltSize + */ shiftCheck: function (col, beltSize) { let fillType; @@ -583,7 +513,11 @@ const Town = { return true; }, - // Return column status (needed potions in each column) + /** + * @description Return column status (needed potions in each column) + * @param {0 | 1 | 2 | 3 | 4} beltSize + * @returns {[number, number, number, number]} + */ checkColumns: function (beltSize) { let col = [beltSize, beltSize, beltSize, beltSize]; let pot = me.getItem(-1, sdk.items.mode.inBelt); @@ -598,7 +532,13 @@ const Town = { return col; }, - // Get the highest potion from current npc + /** + * @description Get the highest potion from current npc + * @param {Unit} npc + * @param {"hp" | "mp"} type + * @param {1 | 2 | 3 | 4 | 5} highestPot + * @returns {boolean | ItemUnit} + */ getPotion: function (npc, type, highestPot = 5) { if (!type) return false; @@ -615,6 +555,9 @@ const Town = { return false; }, + /** + * @param {number} classid + */ fillTome: function (classid) { if (me.gold < 450) return false; if (this.checkScrolls(classid) >= 13) return true; @@ -624,7 +567,7 @@ const Town = { delay(500); - if (classid === sdk.items.TomeofTownPortal && !me.findItem(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory)) { + if (classid === sdk.items.TomeofTownPortal && !me.getTome(sdk.items.TomeofTownPortal)) { let tome = npc.getItem(sdk.items.TomeofTownPortal); if (tome && Storage.Inventory.CanFit(tome)) { @@ -654,8 +597,12 @@ const Town = { return true; }, + /** + * @param {number} id + * @returns {number} quantity of scrolls in tome + */ checkScrolls: function (id) { - let tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); + let tome = me.getTome(id); if (!tome) { switch (id) { @@ -672,9 +619,8 @@ const Town = { }, identify: function () { - me.cancelUIFlags(); - - if (this.cainID()) return true; + !me.inShop && me.cancelUIFlags(); + if (Town.cainID()) return true; let list = (Storage.Inventory.Compare(Config.Inventory) || []); if (list.length === 0) return false; @@ -682,38 +628,36 @@ const Town = { // 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; - return ((!identified || Config.LowGold > 0) && ([Pickit.Result.UNID, Pickit.Result.TRASH].includes(Pickit.checkItem(item).result)/* || (!identified && AutoEquip.hasTier(item)) */)); + const unid = !item.identified; + const results = [Pickit.Result.UNID, Pickit.Result.TRASH]; + return ((unid || Config.LowGold > 0) && (results.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 && Town.fillTome(sdk.items.TomeofIdentify); MainLoop: while (list.length > 0) { - let item = list.shift(); + const item = list.shift(); - if (!item.identified && item.isInInventory && !this.ignoredItemTypes.includes(item.itemType)) { + if (!item.identified && item.isInInventory && !Town.ignoreType(item.itemType)) { let result = Pickit.checkItem(item); - // Force ID for unid items matching autoEquip criteria - //result.result === 1 && !item.identified && Item.hasTier(item) && (result.result = -1); - switch (result.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: - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); item.sell(); break; case Pickit.Result.UNID: - let idTool = tome ? tome : Town.getIdTool(); + let idTool = tome ? tome : me.getIdTool(); if (idTool) { this.identifyItem(item, idTool); @@ -722,7 +666,7 @@ const Town = { 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); if (tpTome) { tpTome.sell(); @@ -746,35 +690,27 @@ const Town = { result = Pickit.checkItem(item); - // should autoequip even be checked by default? - //!Item.autoEquipCheck(item) && (result.result = 0); - switch (result.result) { case Pickit.Result.WANTED: - // Couldn't id autoEquip item. Don't log it. - // if (result.result === 1 && Config.AutoEquip && !item.indentifed && Item.autoEquipCheck(item)) { - // break; - // } - - Misc.itemLogger("Kept", item); - Misc.logItem("Kept", item, result.line); + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); break; case Pickit.Result.UNID: case Pickit.Result.RUNEWORD: // (doesn't trigger normally) break; case Pickit.Result.CUBING: - Misc.itemLogger("Kept", item, "Cubing-Town"); + Item.logger("Kept", item, "Cubing-Town"); Cubing.update(); break; case Pickit.Result.CRAFTING: - Misc.itemLogger("Kept", item, "CraftSys-Town"); + Item.logger("Kept", item, "CraftSys-Town"); CraftingSystem.update(item); break; default: - Misc.itemLogger("Sold", item); + Item.logger("Sold", item); item.sell(); let timer = getTickCount() - this.sellTimer; // shop speedup test @@ -807,7 +743,7 @@ const Town = { me.cancel(); this.stash(false); - let unids = this.getUnids(); + let unids = me.getUnids(); if (unids) { // Check if we may use Cain - number of unid items @@ -824,17 +760,15 @@ const Town = { for (let i = 0; i < unids.length; i += 1) { let result = Pickit.checkItem(unids[i]); - //!Item.autoEquipCheck(unids[i]) && (result = 0); - switch (result.result) { case Pickit.Result.UNWANTED: - Misc.itemLogger("Dropped", unids[i], "cainID"); + Item.logger("Dropped", unids[i], "cainID"); unids[i].drop(); break; case Pickit.Result.WANTED: - Misc.itemLogger("Kept", unids[i]); - Misc.logItem("Kept", unids[i], result.line); + Item.logger("Kept", unids[i]); + Item.logItem("Kept", unids[i], result.line); break; default: @@ -846,83 +780,17 @@ const Town = { return true; }, - // Identify items while in the field if we have a id tome - fieldID: function () { - let list = this.getUnids(); - if (!list) return false; - - let tome = me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - if (!tome || tome.getStat(sdk.stats.Quantity) < list.length) return false; - - while (list.length > 0) { - let item = list.shift(); - let result = Pickit.checkItem(item); - - // Force ID for unid items matching autoEquip criteria - //result.result === 1 && !item.getFlag(sdk.items.flags.Identified) && Item.hasTier(item) && (result.result = -1); - - // unid item that should be identified - if (result.result === Pickit.Result.UNID) { - this.identifyItem(item, tome, Config.FieldID.PacketID); - delay(me.ping + 1); - result = Pickit.checkItem(item); - - //!Item.autoEquipCheck(item) && (result.result = 0); - - switch (result.result) { - case Pickit.Result.UNWANTED: - Misc.itemLogger("Dropped", item, "fieldID"); - - if (Config.DroppedItemsAnnounce.Enable && Config.DroppedItemsAnnounce.Quality.includes(item.quality)) { - say("Dropped: [" + Pickit.itemQualityToName(item.quality).charAt(0).toUpperCase() + Pickit.itemQualityToName(item.quality).slice(1) + "] " + item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim()); - - if (Config.DroppedItemsAnnounce.LogToOOG && Config.DroppedItemsAnnounce.OOGQuality.includes(item.quality)) { - Misc.logItem("Field Dropped", item, result.line); - } - } - - item.drop(); - - break; - case Pickit.Result.WANTED: - Misc.itemLogger("Field Kept", item); - Misc.logItem("Field Kept", item, result.line); - - break; - default: - break; - } - } - } - - delay(200); - me.cancel(); - - return true; - }, - - getUnids: function () { - let list = []; - let item = me.getItem(-1, sdk.items.mode.inStorage); - - if (!item) return false; - - do { - if (item.isInInventory && !item.identified) { - list.push(copyUnit(item)); - } - } while (item.getNext()); - - if (!list.length) return false; - - return list; - }, - + /** + * @param {ItemUnit} unit + * @param {ItemUnit} tome + * @param {Boolean} packetID + * @returns {boolean} + */ identifyItem: function (unit, tome, packetID = false) { if (Config.PacketShopping || packetID) return Packet.identifyItem(unit, tome); if (!unit || unit.identified) return false; - this.sellTimer = getTickCount(); // shop speedup test + Town.sellTimer = getTickCount(); // shop speedup test CursorLoop: for (let i = 0; i < 3; i += 1) { @@ -972,7 +840,7 @@ const Town = { let npc = getInteractedNPC(); if (!npc || !npc.itemcount) return false; - let items = npc.getItemsEx().filter((item) => Town.ignoredItemTypes.indexOf(item.itemType) === -1); + let items = npc.getItemsEx().filter((item) => !Town.ignoreType(item.itemType)); if (!items.length) return false; print("ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); @@ -980,11 +848,11 @@ const Town = { for (let i = 0; i < items.length; i += 1) { let result = Pickit.checkItem(items[i]); - if (result.result === Pickit.Result.WANTED/* && Item.autoEquipCheck(items[i]) */) { + if (result.result === Pickit.Result.WANTED) { try { if (Storage.Inventory.CanFit(items[i]) && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy)) { - Misc.itemLogger("Shopped", items[i]); - Misc.logItem("Shopped", items[i], result.line); + Item.logger("Shopped", items[i]); + Item.logItem("Shopped", items[i], result.line); items[i].buy(); } } catch (e) { @@ -998,6 +866,9 @@ const Town = { return true; }, + /** + * @type {number[]} + */ gambleIds: [], gamble: function () { @@ -1050,19 +921,17 @@ const Town = { return false; } - //me.overhead("Buy: " + items[i].name); + me.overhead("Buy: " + items[i].name); items[i].buy(false, true); let newItem = this.getGambledItem(list); if (newItem) { let result = Pickit.checkItem(newItem); - //!Item.autoEquipCheck(newItem) && (result = 0); - 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; @@ -1076,7 +945,7 @@ const Town = { break; default: - Misc.itemLogger("Sold", newItem, "Gambling"); + Item.logger("Sold", newItem, "Gambling"); me.overhead("Sell: " + newItem.name); newItem.sell(); @@ -1100,6 +969,9 @@ const Town = { return Config.Gamble && me.gold >= Config.GambleGoldStart; }, + /** + * @param {ItemUnit[]} list + */ getGambledItem: function (list = []) { let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); @@ -1120,6 +992,13 @@ const Town = { return false; }, + /** + * @param {number} quantity + * @param {number | string} type + * @param {boolean} [drink=false] + * @param {boolean} [force=false] + * @param {Unit} [npc=null] + */ buyPots: function (quantity = 0, type = undefined, drink = false, force = false, npc = null) { if (!quantity || !type) return false; @@ -1194,6 +1073,11 @@ const Town = { return true; }, + /** + * @param {number | string} type + * @param {boolean} [log=true] + * @returns {{ potName: string, quantity: number }} + */ drinkPots: function (type = undefined, log = true) { // convert to classid if isn't one typeof type === "string" && (type = (sdk.items[type.capitalize(true) + "Potion"] || false)); @@ -1204,8 +1088,6 @@ const Town = { if (chugs.length > 0) { name = chugs.first().name; - let pingDelay = me.getPingDelay(); - chugs.forEach(function (pot) { if (!!pot && pot.use()) { quantity++; @@ -1271,15 +1153,16 @@ const Town = { return this.checkKeys() <= 6; }, + /** + * @param {ItemUnit} item - Rune + */ repairIngredientCheck: function (item) { if (!Config.CubeRepair) return false; - let needRal = 0; - let needOrt = 0; - let have = 0; + let [have, needRal, needOrt] = [0, 0, 0]; let items = this.getItemsForRepair(Config.RepairPercent, false); - if (items && items.length) { + if (items.length) { while (items.length > 0) { switch (items.shift().itemType) { case sdk.items.type.Shield: @@ -1330,6 +1213,10 @@ const Town = { return true; }, + /** + * @param {ItemUnit} item + * @returns {boolean} + */ cubeRepairItem: function (item) { if (!item.isInStorage) return false; @@ -1394,6 +1281,9 @@ const Town = { return false; }, + /** + * @param {boolean} [force=false] + */ repair: function (force = false) { if (this.cubeRepair()) return true; @@ -1442,15 +1332,9 @@ const Town = { let bowCheck = Attack.usingBow(); if (bowCheck) { - switch (bowCheck) { - case "bow": - quiver = me.getItem("aqv", sdk.items.mode.Equipped); // Equipped arrow quiver - - break; - case "crossbow": - quiver = me.getItem("cqv", sdk.items.mode.Equipped); // Equipped bolt quiver - - break; + const quiverType = { bow: "aqv", crossbow: "cqv" }; + if (quiverType[bowCheck]) { + quiver = me.getItem(quiverType[bowCheck], sdk.items.mode.Equipped); } if (!quiver) { // Out of arrows/bolts @@ -1476,6 +1360,11 @@ const Town = { return repairAction; }, + /** + * @param {number} repairPercent + * @param {boolean} chargedItems + * @returns {ItemUnit[]} + */ getItemsForRepair: function (repairPercent, chargedItems) { let itemList = []; let item = me.getItem(-1, sdk.items.mode.Equipped); @@ -1676,13 +1565,17 @@ const Town = { if (Loader.scriptName() === "GemHunter") { if (this.getGemsInInv().length === 0 && this.getGemsInStash().length > 0) { let gem = this.getGemsInStash().first(); - Storage.Inventory.MoveTo(gem) && Misc.itemLogger("Inventoried", gem); + Storage.Inventory.MoveTo(gem) && Item.logger("Inventoried", gem); return true; } } return false; }, + /** + * @param {boolean} [stashGold=true] + * @returns {boolean} + */ stash: function (stashGold = true) { if (!this.needStash()) return true; @@ -1709,7 +1602,7 @@ const Town = { } if (result) { - Storage.Stash.MoveTo(items[i]) && Misc.itemLogger("Stashed", items[i]); + Storage.Stash.MoveTo(items[i]) && Item.logger("Stashed", items[i]); } } } @@ -1918,13 +1811,14 @@ const Town = { }, clearScrolls: function () { - let scrolls = me.getItemsEx().filter((scroll) => scroll.isInInventory && scroll.itemType === sdk.items.type.Scroll); - let tpTome = scrolls.some(function (scroll) { + const scrolls = me.getItemsEx().filter((scroll) => scroll.isInInventory && scroll.itemType === sdk.items.type.Scroll); + if (!scrolls.length) return false; + const tpTome = scrolls.some(function (scroll) { return scroll.classid === sdk.items.ScrollofTownPortal; - }) ? me.findItem(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory) : false; - let idTome = scrolls.some(function (scroll) { + }) ? me.getTome(sdk.items.TomeofTownPortal) : false; + const idTome = scrolls.some(function (scroll) { return scroll.classid === sdk.items.ScrollofIdentify; - }) ? me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory) : false; + }) ? me.getTome(sdk.items.TomeofIdentify) : false; let currQuantity; for (let i = 0; !!scrolls && i < scrolls.length; i++) { @@ -1969,10 +1863,10 @@ const Town = { // Might as well sell the item if already in shop if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { console.info(null, "Sell " + scrolls[i].name); - Misc.itemLogger("Sold", scrolls[i]); + Item.logger("Sold", scrolls[i]); scrolls[i].sell(); } else { - Misc.itemLogger("Dropped", scrolls[i], "clearScrolls"); + Item.logger("Dropped", scrolls[i], "clearScrolls"); scrolls[i].drop(); } } @@ -1999,7 +1893,7 @@ const Town = { this.clearBelt(); // Return potions from inventory to belt - let beltSize = Storage.BeltSize(); + const beltSize = Storage.BeltSize(); // belt 4x4 locations /** * 12 13 14 15 @@ -2007,8 +1901,8 @@ const Town = { * 4 5 6 7 * 0 1 2 3 */ - let beltMax = (beltSize * 4); - let beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; + const beltMax = (beltSize * 4); + const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; let 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); @@ -2053,15 +1947,15 @@ const Town = { switch (p.itemType) { case sdk.items.type.HealingPotion: - return (hp.push(copyUnit(p))); + return (hp.push(p)); case sdk.items.type.ManaPotion: - return (mp.push(copyUnit(p))); + return (mp.push(p)); case sdk.items.type.RejuvPotion: - return (rv.push(copyUnit(p))); + return (rv.push(p)); case sdk.items.type.ThawingPotion: case sdk.items.type.AntidotePotion: case sdk.items.type.StaminaPotion: - return (specials.push(copyUnit(p))); + return (specials.push(p)); } return false; @@ -2123,29 +2017,30 @@ const Town = { }); // we have items to sell, might as well sell the dropable items as well if (sell.length) { - Config.DebugMode && console.debug("PreSellLen: " + sell.length + " PreDropLen: " + drop.length); - drop.filter(function (item) { - // add to sellable array - if (item.sellable && sell.push(item)) return false; - return true; - }); - Config.DebugMode && console.debug("AfterSellLen: " + sell.length + " AfterDropLen: " + drop.length); // should there be multiple attempts to interact with npc or if we fail should we move everything from the sell list to the drop list? - Town.initNPC("Shop", "clearInventory"); + let npc; + for (let i = 0; i < 3 && !npc; i++) { + npc = Town.initNPC("Shop", "clearInventory"); + } + // now lets sell them items - sell.forEach(function (item) { - let sold = false; // so we know to delay or not - try { - console.info(null, "Sell :: " + item.name); - Misc.itemLogger("Sold", item); - item.sell() && (sold = true); - } catch (e) { - console.error(e); - } - sold && delay(250); // would a rand delay be better? - }); + if (npc) { + [].concat(sell, drop.filter((item) => item.sellable)) + .forEach(function (item) { + let sold = false; // so we know to delay or not + try { + console.info(null, "Sell :: " + item.name); + Item.logger("Sold", item); + item.sell() && (sold = true); + } catch (e) { + console.error(e); + } + sold && delay(250); // would a rand delay be better? + }); + } // now lets see if we need to drop anything, so lets exit the shop me.cancelUIFlags(); + drop = drop.filter((item) => !!item && me.getItem(-1, sdk.items.mode.inStorage, item.gid)); } if (drop.length) { @@ -2153,7 +2048,7 @@ const Town = { let drop = false; // so we know to delay or not try { console.info(null, "Drop :: " + item.name); - Misc.itemLogger("Dropped", item, "clearInventory"); + Item.logger("Dropped", item, "clearInventory"); item.drop() && (drop = true); } catch (e) { console.error(e); @@ -2168,6 +2063,9 @@ const Town = { return true; }, + /** + * @todo figure out how to typedef this. + */ act: [{}, {}, {}, {}, {}], initialize: function () { @@ -2259,6 +2157,10 @@ const Town = { return true; }, + /** + * @param {string} spot + * @returns {number} distance to town location + */ getDistance: function (spot = "") { !me.inTown && this.goToTown(); !this.act[me.act - 1].initialized && this.initialize(); @@ -2275,6 +2177,11 @@ const Town = { } }, + /** + * @param {string} spot + * @param {boolean} [allowTK] + * @returns {boolean} + */ move: function (spot = "", allowTK = true) { !me.inTown && this.goToTown(); !this.act[me.act - 1].initialized && this.initialize(); @@ -2322,6 +2229,11 @@ const Town = { return false; }, + /** + * @param {string} spot + * @param {boolean} [allowTK] + * @returns {boolean} + */ moveToSpot: function (spot = "", allowTK = true) { let townSpot; let longRange = (!Skill.haveTK && spot === "waypoint"); @@ -2398,6 +2310,11 @@ const Town = { return false; }, + /** + * @param {1 | 2 | 3 | 4 | 5} act + * @param {boolean} [wpmenu=false] + * @returns {boolean} + */ goToTown: function (act = 0, wpmenu = false) { if (!me.inTown) { try { @@ -2409,7 +2326,7 @@ const Town = { if (!me.inTown && !Pather.usePortal(sdk.areas.townOf(me.area))) throw new Error("Town.goToTown: Failed to take TP"); } } catch (e) { - let tpTool = Town.getTpTool(); + 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"); @@ -2445,6 +2362,10 @@ const Town = { return true; }, + /** + * @param {boolean} [repair] + * @returns {boolean} + */ visitTown: function (repair = false) { console.info(true); @@ -2455,7 +2376,7 @@ const Town = { return true; } - if (!this.canTpToTown()) return false; + if (!me.canTpToTown()) return false; let preArea = me.area; let preAct = me.act; @@ -2481,7 +2402,7 @@ const Town = { } Config.PublicMode && Pather.makePortal(); - console.info(false, "CurrentArea: " + Pather.getAreaName(me.area)); + console.info(false, "CurrentArea: " + getAreaName(me.area)); return true; } diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js new file mode 100644 index 000000000..844f22847 --- /dev/null +++ b/d2bs/kolbot/libs/core/Util.js @@ -0,0 +1,609 @@ +/** + * @filename Util.js + * @author Jaenster, theBGuy + * @desc utility functions for kolbot + * + */ + +!isIncluded("Polyfill.js") && include("Polyfill.js"); + +(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 { + let temp = factory(); + Object.keys(temp).forEach(key => { + if (typeof root[key] === "undefined") { + root[key] = temp[key]; + } + }); + } +}(this, function() { + /** + * get all running threads and return them as an array + * @returns {Script[]} + */ + const getThreads = function () { + let threads = []; + let script = getScript(); + + if (script) { + do { + threads.push(copyObj(script)); + } while (script.getNext()); + } + + return threads; + }; + + /** + * @param {...args} + * @returns {Unit[]} + */ + const getUnits = function (...args) { + let units = [], unit = getUnit.apply(null, args); + + if (!unit) { + return []; + } + do { + units.push(copyUnit(unit)); + } while (unit.getNext()); + return units; + }; + + /** + * @typedef {Object} Args + * @property {0 | 1 | 2} arg1 - where + * @property {number | ItemUnit} arg2 - bodyLoc to click, item to click, x coord + * @property {number} [arg3] - y coord + * @property {number} [arg4] - location + * + * @param {...Args} args + */ + const clickItemAndWait = function (...args) { + let timeout = getTickCount(), timedOut; + let before = !me.itemoncursor; + + clickItem.apply(undefined, args); + delay(Math.max(me.ping * 2, 250)); + + + while (true) { // Wait until item is picked up. + delay(3); + + if (before !== !!me.itemoncursor || (timedOut = getTickCount() - timeout > Math.min(1000, 100 + (me.ping * 4)))) { + break; // quit the loop of item on cursor has changed + } + } + + delay(Math.max(me.ping, 50)); + + // return item if we didnt timeout + return !timedOut; + }; + + /** + * @description clickMap doesn't return if we sucessfully clicked a unit just that a click was sent, this checks and returns that a units mode has changed + * as a result of us clicking it. + * @param {number} button + * @param {0 | 1} shift + * @param {Unit} unit + * @returns {boolean} If a units mode has changed as a result of clicking it + */ + const clickUnitAndWait = function (button, shift, unit) { + if (typeof (unit) !== "object") throw new Error("clickUnitAndWait: Third arg must be a Unit."); + + let before = unit.mode; + + me.blockMouse = true; + clickMap(button, shift, unit); + delay(Math.max(me.ping * 2, 250)); + clickMap(button + 2, shift, unit); + me.blockMouse = false; + + let waitTick = getTickCount(); + let timeOut = Math.min(1000, 100 + (me.ping * 4)); + + while (getTickCount() - waitTick < timeOut) { + delay(30); + + // quit the loop if mode has changed + if (before !== unit.mode) { + break; + } + } + + delay(Math.max(me.ping + 1, 50)); + + return (before !== unit.mode); + }; + + /** + * @class + * @description new PacketBuilder() - create new packet object + * @example (Spoof 'reassign player' packet to client): + * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); + * @example (Spoof 'player move' packet to server): + * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); + * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` + * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` + */ + function PacketBuilder () { + // globals DataView ArrayBuffer + if (this.__proto__.constructor !== PacketBuilder) throw new Error("PacketBuilder must be called with 'new' operator!"); + + let queue = [], count = 0; + + // accepts any number of arguments + let enqueue = (type, size) => (...args) => { + args.forEach(arg => { + if (type === "String") { + arg = stringToEUC(arg); + size = arg.length + 1; + } + + queue.push({type: type, size: size, data: arg}); + count += size; + }); + + return this; + }; + + /** @description size = 4 */ + this.float = enqueue("Float32", 4); + /** @description size = 4 */ + this.dword = enqueue("Uint32", 4); + /** @description size = 2 */ + this.word = enqueue("Uint16", 2); + /** @description size = 1 */ + this.byte = enqueue("Uint8", 1); + this.string = enqueue("String"); + + this.buildDataView = () => { + let dv = new DataView(new ArrayBuffer(count)), i = 0; + queue.forEach(field => { + if (field.type === "String") { + for (let l = 0; l < field.data.length; l++) { + dv.setUint8(i++, field.data.charCodeAt(l), true); + } + + i += field.size - field.data.length; // fix index for field.size !== field.data.length + } else { + dv["set" + field.type](i, field.data, true); + i += field.size; + } + }); + + return dv; + }; + + this.send = () => (sendPacket(this.buildDataView().buffer), this); + this.spoof = () => (getPacket(this.buildDataView().buffer), this); + this.get = this.spoof; // same thing but spoof has clearer intent than get + } + + const areaNames = [ + "None", + "Rogue Encampment", + "Blood Moor", + "Cold Plains", + "Stony Field", + "Dark Wood", + "Black Marsh", + "Tamoe Highland", + "Den Of Evil", + "Cave Level 1", + "Underground Passage Level 1", + "Hole Level 1", + "Pit Level 1", + "Cave Level 2", + "Underground Passage Level 2", + "Hole Level 2", + "Pit Level 2", + "Burial Grounds", + "Crypt", + "Mausoleum", + "Forgotten Tower", + "Tower Cellar Level 1", + "Tower Cellar Level 2", + "Tower Cellar Level 3", + "Tower Cellar Level 4", + "Tower Cellar Level 5", + "Monastery Gate", + "Outer Cloister", + "Barracks", + "Jail Level 1", + "Jail Level 2", + "Jail Level 3", + "Inner Cloister", + "Cathedral", + "Catacombs Level 1", + "Catacombs Level 2", + "Catacombs Level 3", + "Catacombs Level 4", + "Tristram", + "Moo Moo Farm", + "Lut Gholein", + "Rocky Waste", + "Dry Hills", + "Far Oasis", + "Lost City", + "Valley Of Snakes", + "Canyon Of The Magi", + "Sewers Level 1", + "Sewers Level 2", + "Sewers Level 3", + "Harem Level 1", + "Harem Level 2", + "Palace Cellar Level 1", + "Palace Cellar Level 2", + "Palace Cellar Level 3", + "Stony Tomb Level 1", + "Halls Of The Dead Level 1", + "Halls Of The Dead Level 2", + "Claw Viper Temple Level 1", + "Stony Tomb Level 2", + "Halls Of The Dead Level 3", + "Claw Viper Temple Level 2", + "Maggot Lair Level 1", + "Maggot Lair Level 2", + "Maggot Lair Level 3", + "Ancient Tunnels", + "Tal Rashas Tomb #1", + "Tal Rashas Tomb #2", + "Tal Rashas Tomb #3", + "Tal Rashas Tomb #4", + "Tal Rashas Tomb #5", + "Tal Rashas Tomb #6", + "Tal Rashas Tomb #7", + "Duriels Lair", + "Arcane Sanctuary", + "Kurast Docktown", + "Spider Forest", + "Great Marsh", + "Flayer Jungle", + "Lower Kurast", + "Kurast Bazaar", + "Upper Kurast", + "Kurast Causeway", + "Travincal", + "Spider Cave", + "Spider Cavern", + "Swampy Pit Level 1", + "Swampy Pit Level 2", + "Flayer Dungeon Level 1", + "Flayer Dungeon Level 2", + "Swampy Pit Level 3", + "Flayer Dungeon Level 3", + "Sewers Level 1", + "Sewers Level 2", + "Ruined Temple", + "Disused Fane", + "Forgotten Reliquary", + "Forgotten Temple", + "Ruined Fane", + "Disused Reliquary", + "Durance Of Hate Level 1", + "Durance Of Hate Level 2", + "Durance Of Hate Level 3", + "The Pandemonium Fortress", + "Outer Steppes", + "Plains Of Despair", + "City Of The Damned", + "River Of Flame", + "Chaos Sanctuary", + "Harrogath", + "Bloody Foothills", + "Frigid Highlands", + "Arreat Plateau", + "Crystalline Passage", + "Frozen River", + "Glacial Trail", + "Drifter Cavern", + "Frozen Tundra", + "Ancient's Way", + "Icy Cellar", + "Arreat Summit", + "Nihlathak's Temple", + "Halls Of Anguish", + "Halls Of Pain", + "Halls Of Vaught", + "Abaddon", + "Pit Of Acheron", + "Infernal Pit", + "Worldstone Keep Level 1", + "Worldstone Keep Level 2", + "Worldstone Keep Level 3", + "Throne Of Destruction", + "The Worldstone Chamber", + "Matron's Den", + "Forgotten Sands", + "Furnace of Pain", + "Tristram" + ]; + + /** @param {number} area */ + const getAreaName = function (area) { + if (["number", "string"].indexOf(typeof area) === -1) return "undefined"; + if (typeof area === "string") return area; + return (areaNames[area] || "undefined"); + }; + + // helper functions in case you find it annoying like me to write while (getTickCount() - tick > 3 * 60 * 1000) which is 3 minutes + // instead we can do while (getTickCount() - tick > Time.minutes(5)) + const Time = { + seconds: function (seconds = 0) { + if (typeof seconds !== "number") return 0; + return (seconds * 1000); + }, + minutes: function (minutes = 0) { + if (typeof minutes !== "number") return 0; + return (minutes * 60000); + }, + format: function (ms = 0) { + return (new Date(ms).toISOString().slice(11, -5)); + } + }; + + const Game = { + getDistance: function (...args) { + switch (args.length) { + case 0: + return Infinity; + case 1: + // getDistance(unit) - returns distance that unit is from me + if (typeof args[0] !== "object") return Infinity; + if (!args[0].hasOwnProperty("x")) return Infinity; + return Math.sqrt(Math.pow((me.x - args[0].x), 2) + Math.pow((me.y - args[0].y), 2)); + case 2: + // getDistance(x, y) - returns distance x, y is from me + // getDistance(unitA, unitB) - returns distace unitA is from unitB + if (typeof args[0] === "number" && typeof args[1] === "number") { + return Math.sqrt(Math.pow((me.x - args[0]), 2) + Math.pow((me.y - args[1]), 2)); + } else if (typeof args[0] === "object" && typeof args[1] === "object") { + if (!args[1].hasOwnProperty("x")) return Infinity; + return Math.sqrt(Math.pow((args[0].x - args[1].x), 2) + Math.pow((args[0].y - args[1].y), 2)); + } + return Infinity; + case 3: + // getDistance(unit, x, y) - returns distance x, y is from unit + if (typeof args[2] !== "number") return Infinity; + if (!args[0].hasOwnProperty("x")) return Infinity; + return Math.sqrt(Math.pow((args[0].x - args[1]), 2) + Math.pow((args[0].y - args[2]), 2)); + case 4: + // getDistance(x1, y1, x2, y2) + if (typeof args[0] !== "number" || typeof args[3] !== "number") return Infinity; + return Math.sqrt(Math.pow((args[0] - args[2]), 2) + Math.pow((args[1] - args[3]), 2)); + default: + return Infinity; + } + }, + /** + * @returns {ItemUnit | undefined} item on cursor + */ + getCursorUnit: function () { + return getUnit(100); + }, + /** + * @returns {ItemUnit | undefined} item cursor is hovering over + */ + getSelectedUnit: function () { + return getUnit(101); + }, + /** + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Player} + */ + getPlayer: function (id, mode, gid) { + return getUnit(sdk.unittype.Player, id, mode, gid); + }, + /** + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Monster} + */ + getMonster: function (id, mode, gid) { + return getUnit(sdk.unittype.Monster, id, mode, gid); + }, + /** + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Monster} + */ + getNPC: function (id, mode, gid) { + return getUnit(sdk.unittype.NPC, id, mode, gid); + }, + /** + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {ObjectUnit} + */ + getObject: function (id, mode, gid) { + return getUnit(sdk.unittype.Object, id, mode, gid); + }, + /** + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Missile} + */ + getMissile: function (id, mode, gid) { + return getUnit(sdk.unittype.Missile, id, mode, gid); + }, + /** + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {ItemUnit} + */ + getItem: function (id, mode, gid) { + return getUnit(sdk.unittype.Item, id, mode, gid); + }, + /** + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Tile} + */ + getStairs: function (id, mode, gid) { + return getUnit(sdk.unittype.Stairs, id, mode, gid); + }, + /** + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ + getPresetMonster: function (area, id) { + !area && (area = me.area); + return getPresetUnit(area, sdk.unittype.Monster, id); + }, + /** + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ + getPresetMonsters: function (area, id) { + !area && (area = me.area); + return getPresetUnits(area, sdk.unittype.Monster, id); + }, + /** + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ + getPresetObject: function (area, id) { + !area && (area = me.area); + return getPresetUnit(area, sdk.unittype.Object, id); + }, + /** + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ + getPresetObjects: function (area, id) { + !area && (area = me.area); + return getPresetUnits(area, sdk.unittype.Object, id); + }, + /** + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ + getPresetStair: function (area, id) { + !area && (area = me.area); + return getPresetUnit(area, sdk.unittype.Stairs, id); + }, + /** + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ + getPresetStairs: function (area, id) { + !area && (area = me.area); + return getPresetUnits(area, sdk.unittype.Stairs, id); + }, + }; + + const Sort = { + // Sort units by comparing distance between the player + units: function (a, b) { + return Math.round(getDistance(me.x, me.y, a.x, a.y)) - Math.round(getDistance(me.x, me.y, b.x, b.y)); + }, + + // Sort preset units by comparing distance between the player (using preset x/y calculations) + presetUnits: function (a, b) { + return getDistance(me, a.roomx * 5 + a.x, a.roomy * 5 + a.y) - getDistance(me, b.roomx * 5 + b.x, b.roomy * 5 + b.y); + }, + + // Sort arrays of x,y coords by comparing distance between the player + points: function (a, b) { + return getDistance(me, a[0], a[1]) - getDistance(me, b[0], b[1]); + }, + + numbers: function (a, b) { + return a - b; + } + }; + + const Messaging = { + sendToScript: function (name, msg) { + let script = getScript(name); + + if (script && script.running) { + script.send(msg); + + return true; + } + + return false; + }, + + sendToProfile: function (profileName, mode, message, getResponse = false) { + let response; + + function copyDataEvent(mode2, msg) { + if (mode2 === mode) { + let obj; + + try { + obj = JSON.parse(msg); + } catch (e) { + return false; + } + + if (obj.hasOwnProperty("sender") && obj.sender === profileName) { + response = copyObj(obj); + } + + return true; + } + + return false; + } + + getResponse && addEventListener("copydata", copyDataEvent); + + if (!sendCopyData(null, profileName, mode, JSON.stringify({message: message, sender: me.profile}))) { + //print("sendToProfile: failed to get response from " + profileName); + getResponse && removeEventListener("copydata", copyDataEvent); + + return false; + } + + if (getResponse) { + delay(200); + removeEventListener("copydata", copyDataEvent); + + if (!!response) { + return response; + } + + return false; + } + + return true; + } + }; + + return { + getThreads: getThreads, + getUnits: getUnits, + clickItemAndWait: clickItemAndWait, + clickUnitAndWait: clickUnitAndWait, + PacketBuilder: PacketBuilder, + getAreaName: getAreaName, + Time: Time, + Game: Game, + Sort: Sort, + Messaging: Messaging, + }; +})); diff --git a/d2bs/kolbot/libs/critical.js b/d2bs/kolbot/libs/critical.js new file mode 100644 index 000000000..a677bf431 --- /dev/null +++ b/d2bs/kolbot/libs/critical.js @@ -0,0 +1,12 @@ +/** +* @filename critical.js +* @author theBGuy +* @desc Simple loader file for the critical components of kolbot, without these we can't run +* +*/ + +(() => { + include("json2.js"); // I don't know if this one is actually critical but including it + include("polyfill.js"); + me.ingame ? include("oog/D2Bot.js") : include("OOG.js"); +})(); diff --git a/d2bs/kolbot/libs/manualplay/MapMode.js b/d2bs/kolbot/libs/manualplay/MapMode.js index 33f7103e4..7686ef4a6 100644 --- a/d2bs/kolbot/libs/manualplay/MapMode.js +++ b/d2bs/kolbot/libs/manualplay/MapMode.js @@ -31,7 +31,7 @@ const MapMode = { // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See NTItemAlias.dbl for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -49,7 +49,7 @@ const MapMode = { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/NTItemAlias.dbl file for other item classids. + // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); diff --git a/d2bs/kolbot/libs/manualplay/config/Amazon.js b/d2bs/kolbot/libs/manualplay/config/Amazon.js index 68ea43d45..fcad5bf33 100644 --- a/d2bs/kolbot/libs/manualplay/config/Amazon.js +++ b/d2bs/kolbot/libs/manualplay/config/Amazon.js @@ -86,7 +86,7 @@ function LoadConfig() { Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // MF Switch @@ -169,7 +169,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/manualplay/config/Assassin.js b/d2bs/kolbot/libs/manualplay/config/Assassin.js index c1370b40e..1200fc96d 100644 --- a/d2bs/kolbot/libs/manualplay/config/Assassin.js +++ b/d2bs/kolbot/libs/manualplay/config/Assassin.js @@ -86,7 +86,7 @@ function LoadConfig() { Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // MF Switch @@ -178,7 +178,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/manualplay/config/Barbarian.js b/d2bs/kolbot/libs/manualplay/config/Barbarian.js index bcd41010e..76114a390 100644 --- a/d2bs/kolbot/libs/manualplay/config/Barbarian.js +++ b/d2bs/kolbot/libs/manualplay/config/Barbarian.js @@ -78,7 +78,7 @@ function LoadConfig() { // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + Config.PublicMode = 1; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. @@ -86,7 +86,7 @@ function LoadConfig() { Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // MF Switch @@ -167,7 +167,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/manualplay/config/Druid.js b/d2bs/kolbot/libs/manualplay/config/Druid.js index b8bb80272..64ab4cdc1 100644 --- a/d2bs/kolbot/libs/manualplay/config/Druid.js +++ b/d2bs/kolbot/libs/manualplay/config/Druid.js @@ -86,7 +86,7 @@ function LoadConfig() { Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // MF Switch @@ -171,7 +171,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/manualplay/config/Necromancer.js b/d2bs/kolbot/libs/manualplay/config/Necromancer.js index 5f561b22d..6fed93d6e 100644 --- a/d2bs/kolbot/libs/manualplay/config/Necromancer.js +++ b/d2bs/kolbot/libs/manualplay/config/Necromancer.js @@ -86,7 +86,7 @@ function LoadConfig() { Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // MF Switch @@ -192,7 +192,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/manualplay/config/Paladin.js b/d2bs/kolbot/libs/manualplay/config/Paladin.js index 7b5b887e1..f55bb752a 100644 --- a/d2bs/kolbot/libs/manualplay/config/Paladin.js +++ b/d2bs/kolbot/libs/manualplay/config/Paladin.js @@ -86,7 +86,7 @@ function LoadConfig() { Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // MF Switch @@ -171,7 +171,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/manualplay/config/Sorceress.js b/d2bs/kolbot/libs/manualplay/config/Sorceress.js index b8cd8c8ee..c66f21c88 100644 --- a/d2bs/kolbot/libs/manualplay/config/Sorceress.js +++ b/d2bs/kolbot/libs/manualplay/config/Sorceress.js @@ -68,6 +68,9 @@ function LoadConfig() { Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. // Public game options + Config.QuitList = ["unfairsocks"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = [15, 30]; // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET @@ -86,7 +89,7 @@ function LoadConfig() { Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/shrines.txt + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt Config.ScanShrines = []; // MF Switch @@ -170,7 +173,7 @@ function LoadConfig() { * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. * * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/skills.txt) + * skill - skill id number (see /sdk/txt/skills.txt) * count - maximum number of skill points to allocate for that skill * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. * diff --git a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js index a5938161b..36910ce58 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js @@ -53,6 +53,12 @@ const ActionHooks = { sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen ].some((flag) => getUIFlag(flag)), + /** + * Set action based on key input + * @param {number} keycode + * @returns {void} + * @todo this would probably be better as pushing to an action stack and implementing a timeout to prevent spamming the same action + */ event: function (keycode) { if ([sdk.keys.Shift, sdk.keys.Alt].some(k => k === keycode)) { return; @@ -121,6 +127,7 @@ const ActionHooks = { } } + console.debug(TextHooks.displaySettings, ItemHooks.pickitEnabled); if (TextHooks.displaySettings) { TextHooks.getHook("pickitStatus", TextHooks.statusHooks).hook.text = "ÿc4N-Pad - ÿc0: " + (ItemHooks.pickitEnabled ? "ÿc= sdk.items.runes.Vex) { - color = 0x9B; - code = "ÿc;" + item.fname; + [color, code] = [0x9B, "ÿc;" + item.fname]; } else if (item.classid >= sdk.items.runes.Lum) { - color = 0x9A; - code = "ÿc8" + item.fname; + [color, code] = [0x9A, "ÿc8" + item.fname]; } else { - color = 0xA1; - code = item.fname; + [color, code] = [0xA1, item.fname]; } break; default: - if (item.name) { - if (item.sockets === 1) { - break; - } - + if (item.name && item.sockets !== 1) { color = 0x20; if (item.runeword) { @@ -155,20 +162,23 @@ const ItemHooks = { case sdk.items.quality.Unique: ({color, code} = this.itemColorCode[item.quality]); - if (!this.itemCodeByClassId[item.classid]) { - switch (item.classid) { - case sdk.items.Ring: - case sdk.items.Amulet: - code += item.name + "(" + item.ilvl + ")"; - - break; - default: - code += ((!!this.itemCodeByClassIdAndQuality[item.classid] && !!this.itemCodeByClassIdAndQuality[item.classid][item.quality]) ? this.itemCodeByClassIdAndQuality[item.classid][item.quality] : item.name); - - break; + if (this.codeById.has(item.classid)) { + code += this.codeById.get(item.classid); + } + + switch (item.classid) { + case sdk.items.Ring: + case sdk.items.Amulet: + code += item.name + "(" + item.ilvl + ")"; + + break; + default: + { + let check = this.codeByIdAndQuality.get(item.classid); + code += (check.get(item.quality) || item.name); } - } else { - code += this.itemCodeByClassId[item.classid]; + + break; } break; @@ -197,6 +207,10 @@ const ItemHooks = { }; }, + /** + * Add new item hook to our hook array + * @param {ItemUnit} item + */ add: function (item) { if (item === undefined || !item.classid) { return; @@ -213,6 +227,11 @@ const ItemHooks = { }); }, + /** + * Get item hook if it exists based on item parameters gid + * @param {ItemUnit} item + * @returns {{ item: ItemUnit, area: number, hook: Line, name: Text, vector: Line} | false} + */ getHook: function (item) { for (let i = 0; i < this.hooks.length; i++) { if (this.hooks[i].item.gid === item.gid) { @@ -223,6 +242,10 @@ const ItemHooks = { return false; }, + /** + * @param {ItemUnit} item + * @returns {boolean} + */ remove: function (item) { for (let i = 0; i < this.hooks.length; i++) { if (this.hooks[i].item.gid === item.gid) { @@ -255,75 +278,75 @@ const ItemHooks = { }; // have to be set after ItemHooks is created -ItemHooks.itemCodeByClassId[sdk.items.BattleAxe] = "The Chieftain"; -ItemHooks.itemCodeByClassId[sdk.items.Falchion] = "Gleamscythe"; -ItemHooks.itemCodeByClassId[sdk.items.BurntWand] = "Suicide Branch"; -ItemHooks.itemCodeByClassId[sdk.items.PetrifiedWand] = "Carin Shard"; -ItemHooks.itemCodeByClassId[sdk.items.TombWand] = "King Leoric's Arm"; -ItemHooks.itemCodeByClassId[sdk.items.Quarterstaff] = "Ribcracker"; -ItemHooks.itemCodeByClassId[sdk.items.EdgeBow] = "Skystrike"; -ItemHooks.itemCodeByClassId[sdk.items.GreaterTalons] = "Bartuc's"; -ItemHooks.itemCodeByClassId[sdk.items.WristSword] = "Jade Talon"; -ItemHooks.itemCodeByClassId[sdk.items.BattleCestus] = "Shadow Killer"; -ItemHooks.itemCodeByClassId[sdk.items.FeralClaws] = "Firelizard's"; -ItemHooks.itemCodeByClassId[sdk.items.EttinAxe] = "Rune Master"; -ItemHooks.itemCodeByClassId[sdk.items.LichWand] = "Boneshade"; -ItemHooks.itemCodeByClassId[sdk.items.UnearthedWand] = "Death's Web"; -ItemHooks.itemCodeByClassId[sdk.items.FlyingAxe] = "Gimmershred"; -ItemHooks.itemCodeByClassId[sdk.items.WingedKnife] = "Warshrike"; -ItemHooks.itemCodeByClassId[sdk.items.WingedAxe] = "Lacerator"; -ItemHooks.itemCodeByClassId[sdk.items.Thresher] = "Reaper's Toll"; -ItemHooks.itemCodeByClassId[sdk.items.CrypticAxe] = "Tomb Reaver"; -ItemHooks.itemCodeByClassId[sdk.items.GiantThresher] = "Stormspire"; -ItemHooks.itemCodeByClassId[sdk.items.ArchonStaff] = "Mang Song's"; -ItemHooks.itemCodeByClassId[sdk.items.CrusaderBow] = "Eaglehorn"; -ItemHooks.itemCodeByClassId[sdk.items.WardBow] = "Ward Bow"; -ItemHooks.itemCodeByClassId[sdk.items.HydraBow] = "Windforce"; -ItemHooks.itemCodeByClassId[sdk.items.CeremonialBow] = "Lycander's Aim"; -ItemHooks.itemCodeByClassId[sdk.items.CeremonialPike] = "Lycander's Pike"; -ItemHooks.itemCodeByClassId[sdk.items.CeremonialJavelin] = "Titan's Revenge"; -ItemHooks.itemCodeByClassId[sdk.items.EldritchOrb] = "Eschuta's"; -ItemHooks.itemCodeByClassId[sdk.items.DimensionalShard] = "Death's Fathom"; -ItemHooks.itemCodeByClassId[sdk.items.MatriarchalBow] = "Bloodraven's"; -ItemHooks.itemCodeByClassId[sdk.items.MatriarchalSpear] = "Stoneraven"; -ItemHooks.itemCodeByClassId[sdk.items.MatriarchalJavelin] = "Thunder Stroke"; -ItemHooks.itemCodeByClassId[sdk.items.LightPlatedBoots] = "Goblin Toe"; -ItemHooks.itemCodeByClassId[sdk.items.Sallet] = "Rockstopper"; -ItemHooks.itemCodeByClassId[sdk.items.GhostArmor] = "Spirit Shroud"; -ItemHooks.itemCodeByClassId[sdk.items.SerpentskinArmor] = "Vipermagi's"; -ItemHooks.itemCodeByClassId[sdk.items.MeshArmor] = "Shaftstop"; -ItemHooks.itemCodeByClassId[sdk.items.RussetArmor] = "Skullder's"; -ItemHooks.itemCodeByClassId[sdk.items.MagePlate] = "Que-Hegan's"; -ItemHooks.itemCodeByClassId[sdk.items.SharkskinBoots] = "Waterwalk"; -ItemHooks.itemCodeByClassId[sdk.items.DemonHead] = "Andariel's Vis"; -ItemHooks.itemCodeByClassId[sdk.items.Tiara] = "Kira's"; -ItemHooks.itemCodeByClassId[sdk.items.Shako] = "Harlequin Crest"; -ItemHooks.itemCodeByClassId[sdk.items.WireFleece] = "Gladiator's Bane"; -ItemHooks.itemCodeByClassId[sdk.items.ScarabshellBoots] = "Sandstorm Trek's"; -ItemHooks.itemCodeByClassId[sdk.items.BoneweaveBoots] = "Marrowwalk"; -ItemHooks.itemCodeByClassId[sdk.items.MyrmidonGreaves] = "Shadow Dancer"; -ItemHooks.itemCodeByClassId[sdk.items.TotemicMask] = "Jalal's"; -ItemHooks.itemCodeByClassId[sdk.items.SlayerGuard] = "Arreat's Face"; -ItemHooks.itemCodeByClassId[sdk.items.GildedShield] = "HoZ"; -ItemHooks.itemCodeByClassId[sdk.items.HierophantTrophy] = "Homunculus"; -ItemHooks.itemCodeByClassId[sdk.items.BloodSpirit] = "Cerebus"; -ItemHooks.itemCodeByClassId[sdk.items.EarthSpirit] = "Spirit Keeper"; -ItemHooks.itemCodeByClassId[sdk.items.FuryVisor] = "Wolfhowl"; -ItemHooks.itemCodeByClassId[sdk.items.DestroyerHelm] = "Demonhorn's"; -ItemHooks.itemCodeByClassId[sdk.items.ConquerorCrown] = "Halaberd's"; -ItemHooks.itemCodeByClassId[sdk.items.SacredRondache] = "Alma Negra"; -ItemHooks.itemCodeByClassId[sdk.items.ZakarumShield] = "Dragonscale"; -ItemHooks.itemCodeByClassId[sdk.items.BloodlordSkull] = "Darkforce"; -ItemHooks.itemCodeByClassId[sdk.items.SuccubusSkull] = "Boneflame"; -ItemHooks.itemCodeByClassId[sdk.items.SmallCharm] = "Annihilus"; -ItemHooks.itemCodeByClassId[sdk.items.LargeCharm] = "Hellfire Torch"; -ItemHooks.itemCodeByClassId[sdk.items.GrandCharm] = "Gheed's"; -ItemHooks.itemCodeByClassId[sdk.items.Jewel] = "Facet"; -ItemHooks.itemCodeByClassId[sdk.items.quest.TokenofAbsolution] = "ÿc8Token"; -ItemHooks.itemCodeByClassId[sdk.items.quest.TwistedEssenceofSuffering] = "ÿc3Ess-Of-Suffering"; -ItemHooks.itemCodeByClassId[sdk.items.quest.ChargedEssenceofHatred] = "ÿc7Ess-Of-Hatred"; -ItemHooks.itemCodeByClassId[sdk.items.quest.BurningEssenceofTerror] = "ÿc1Ess-Of-Terror"; -ItemHooks.itemCodeByClassId[sdk.items.quest.FesteringEssenceofDestruction] = "ÿc3Ess-Of-Destruction"; +ItemHooks.codeById.set(sdk.items.BattleAxe, "The Chieftain"); +ItemHooks.codeById.set(sdk.items.Falchion, "Gleamscythe"); +ItemHooks.codeById.set(sdk.items.BurntWand, "Suicide Branch"); +ItemHooks.codeById.set(sdk.items.PetrifiedWand, "Carin Shard"); +ItemHooks.codeById.set(sdk.items.TombWand, "King Leoric's Arm"); +ItemHooks.codeById.set(sdk.items.Quarterstaff, "Ribcracker"); +ItemHooks.codeById.set(sdk.items.EdgeBow, "Skystrike"); +ItemHooks.codeById.set(sdk.items.GreaterTalons, "Bartuc's"); +ItemHooks.codeById.set(sdk.items.WristSword, "Jade Talon"); +ItemHooks.codeById.set(sdk.items.BattleCestus, "Shadow Killer"); +ItemHooks.codeById.set(sdk.items.FeralClaws, "Firelizard's"); +ItemHooks.codeById.set(sdk.items.EttinAxe, "Rune Master"); +ItemHooks.codeById.set(sdk.items.LichWand, "Boneshade"); +ItemHooks.codeById.set(sdk.items.UnearthedWand, "Death's Web"); +ItemHooks.codeById.set(sdk.items.FlyingAxe, "Gimmershred"); +ItemHooks.codeById.set(sdk.items.WingedKnife, "Warshrike"); +ItemHooks.codeById.set(sdk.items.WingedAxe, "Lacerator"); +ItemHooks.codeById.set(sdk.items.Thresher, "Reaper's Toll"); +ItemHooks.codeById.set(sdk.items.CrypticAxe, "Tomb Reaver"); +ItemHooks.codeById.set(sdk.items.GiantThresher, "Stormspire"); +ItemHooks.codeById.set(sdk.items.ArchonStaff, "Mang Song's"); +ItemHooks.codeById.set(sdk.items.CrusaderBow, "Eaglehorn"); +ItemHooks.codeById.set(sdk.items.WardBow, "Ward Bow"); +ItemHooks.codeById.set(sdk.items.HydraBow, "Windforce"); +ItemHooks.codeById.set(sdk.items.CeremonialBow, "Lycander's Aim"); +ItemHooks.codeById.set(sdk.items.CeremonialPike, "Lycander's Pike"); +ItemHooks.codeById.set(sdk.items.CeremonialJavelin, "Titan's Revenge"); +ItemHooks.codeById.set(sdk.items.EldritchOrb, "Eschuta's"); +ItemHooks.codeById.set(sdk.items.DimensionalShard, "Death's Fathom"); +ItemHooks.codeById.set(sdk.items.MatriarchalBow, "Bloodraven's"); +ItemHooks.codeById.set(sdk.items.MatriarchalSpear, "Stoneraven"); +ItemHooks.codeById.set(sdk.items.MatriarchalJavelin, "Thunder Stroke"); +ItemHooks.codeById.set(sdk.items.LightPlatedBoots, "Goblin Toe"); +ItemHooks.codeById.set(sdk.items.Sallet, "Rockstopper"); +ItemHooks.codeById.set(sdk.items.GhostArmor, "Spirit Shroud"); +ItemHooks.codeById.set(sdk.items.SerpentskinArmor, "Vipermagi's"); +ItemHooks.codeById.set(sdk.items.MeshArmor, "Shaftstop"); +ItemHooks.codeById.set(sdk.items.RussetArmor, "Skullder's"); +ItemHooks.codeById.set(sdk.items.MagePlate, "Que-Hegan's"); +ItemHooks.codeById.set(sdk.items.SharkskinBoots, "Waterwalk"); +ItemHooks.codeById.set(sdk.items.DemonHead, "Andariel's Vis"); +ItemHooks.codeById.set(sdk.items.Tiara, "Kira's"); +ItemHooks.codeById.set(sdk.items.Shako, "Harlequin Crest"); +ItemHooks.codeById.set(sdk.items.WireFleece, "Gladiator's Bane"); +ItemHooks.codeById.set(sdk.items.ScarabshellBoots, "Sandstorm Trek's"); +ItemHooks.codeById.set(sdk.items.BoneweaveBoots, "Marrowwalk"); +ItemHooks.codeById.set(sdk.items.MyrmidonGreaves, "Shadow Dancer"); +ItemHooks.codeById.set(sdk.items.TotemicMask, "Jalal's"); +ItemHooks.codeById.set(sdk.items.SlayerGuard, "Arreat's Face"); +ItemHooks.codeById.set(sdk.items.GildedShield, "HoZ"); +ItemHooks.codeById.set(sdk.items.HierophantTrophy, "Homunculus"); +ItemHooks.codeById.set(sdk.items.BloodSpirit, "Cerebus"); +ItemHooks.codeById.set(sdk.items.EarthSpirit, "Spirit Keeper"); +ItemHooks.codeById.set(sdk.items.FuryVisor, "Wolfhowl"); +ItemHooks.codeById.set(sdk.items.DestroyerHelm, "Demonhorn's"); +ItemHooks.codeById.set(sdk.items.ConquerorCrown, "Halaberd's"); +ItemHooks.codeById.set(sdk.items.SacredRondache, "Alma Negra"); +ItemHooks.codeById.set(sdk.items.ZakarumShield, "Dragonscale"); +ItemHooks.codeById.set(sdk.items.BloodlordSkull, "Darkforce"); +ItemHooks.codeById.set(sdk.items.SuccubusSkull, "Boneflame"); +ItemHooks.codeById.set(sdk.items.SmallCharm, "Annihilus"); +ItemHooks.codeById.set(sdk.items.LargeCharm, "Hellfire Torch"); +ItemHooks.codeById.set(sdk.items.GrandCharm, "Gheed's"); +ItemHooks.codeById.set(sdk.items.Jewel, "Facet"); +ItemHooks.codeById.set(sdk.items.quest.TokenofAbsolution, "ÿc8Token"); +ItemHooks.codeById.set(sdk.items.quest.TwistedEssenceofSuffering, "ÿc3Ess-Of-Suffering"); +ItemHooks.codeById.set(sdk.items.quest.ChargedEssenceofHatred, "ÿc7Ess-Of-Hatred"); +ItemHooks.codeById.set(sdk.items.quest.BurningEssenceofTerror, "ÿc1Ess-Of-Terror"); +ItemHooks.codeById.set(sdk.items.quest.FesteringEssenceofDestruction, "ÿc3Ess-Of-Destruction"); ItemHooks.addToCodeByClassIdAndQuality(sdk.items.JaggedStar, "Aldur's Wep", "Moonfall"); ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HuntersGuise, "Aldur's Helm"); diff --git a/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js b/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js index 3a35e33c0..32a60fea1 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js @@ -39,7 +39,7 @@ const MonsterHooks = { } }, - // credit DetectiveSquirrel from his maphack https://github.com/DetectiveSquirrel/Kolbot-MapThread/blob/9c721a72a934518cfca1d1a05211b5e03b5b624f/kolbot/tools/MapThread.js#L2353 + // credit DetectiveSquirrel from his maphack https://github.com/DetectiveSquirrel/Kolbot-MapThread/blob/9c721a72a934518cfca1d1a05211b5e03b5b624f/kolbot/threads/MapThread.js#L2353 specTypeColor: function (unit) { switch (unit.spectype) { case sdk.monsters.spectype.Minion: diff --git a/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js index 826f0cac2..4cf04742b 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js @@ -8,7 +8,7 @@ const ShrineHooks = { enabled: true, hooks: [], - shrines: {}, + shrines: new Map(), check: function () { if (!this.enabled || me.inTown) { @@ -26,29 +26,26 @@ const ShrineHooks = { } } - let shrine = Game.getObject("shrine"); + let shrine = Game.getObject(); if (shrine) { do { - if (shrine.mode === sdk.objects.mode.Inactive) { - if (!this.getHook(shrine)) { - this.add(shrine); + if (this.shrines.has(shrine.objtype) && shrine.name.toLowerCase().includes("shrine")) { + if (shrine.mode === sdk.objects.mode.Inactive) { + if (!this.getHook(shrine)) { + this.add(shrine); + } + } else { + this.remove(shrine); } - } else { - this.remove(shrine); } } while (shrine.getNext()); } }, newHook: function (shrine) { - let arr = []; - let typeName; - - typeName = this.shrines[shrine.objtype]; - typeName && arr.push(new Text(typeName, shrine.x, shrine.y, 4, 6, 2, true)); - - return arr; + let typeName = this.shrines.get(shrine.objtype); + return typeName ? [new Text(typeName, shrine.x, shrine.y, 4, 6, 2, true)] : []; }, add: function (shrine) { @@ -91,25 +88,25 @@ const ShrineHooks = { } }; -ShrineHooks.shrines[sdk.shrines.Refilling] = "Refilling"; -ShrineHooks.shrines[sdk.shrines.Health] = "Health"; -ShrineHooks.shrines[sdk.shrines.Mana] = "Mana"; -ShrineHooks.shrines[sdk.shrines.HealthExchange] = "Health Exchange"; -ShrineHooks.shrines[sdk.shrines.ManaExchange] = "Mana Exchange"; -ShrineHooks.shrines[sdk.shrines.Armor] = "Armor"; -ShrineHooks.shrines[sdk.shrines.Combat] = "Combat"; -ShrineHooks.shrines[sdk.shrines.ResistFire] = "Resist Fire"; -ShrineHooks.shrines[sdk.shrines.ResistCold] = "Resist Cold"; -ShrineHooks.shrines[sdk.shrines.ResistLightning] = "Resist Lightning"; -ShrineHooks.shrines[sdk.shrines.ResistPoison] = "Resist Poison"; -ShrineHooks.shrines[sdk.shrines.Skill] = "Skill"; -ShrineHooks.shrines[sdk.shrines.ManaRecharge] = "Mana Recharge"; -ShrineHooks.shrines[sdk.shrines.Stamina] = "Stamina"; -ShrineHooks.shrines[sdk.shrines.Experience] = "Experience"; -ShrineHooks.shrines[sdk.shrines.Enirhs] = "Enirhs"; -ShrineHooks.shrines[sdk.shrines.Portal] = "Portal"; -ShrineHooks.shrines[sdk.shrines.Gem] = "Gem"; -ShrineHooks.shrines[sdk.shrines.Fire] = "Fire"; -ShrineHooks.shrines[sdk.shrines.Monster] = "Monster"; -ShrineHooks.shrines[sdk.shrines.Exploding] = "Exploding"; -ShrineHooks.shrines[sdk.shrines.Poison] = "Poison"; +ShrineHooks.shrines.set(sdk.shrines.Refilling, "Refilling"); +ShrineHooks.shrines.set(sdk.shrines.Health, "Health"); +ShrineHooks.shrines.set(sdk.shrines.Mana, "Mana"); +ShrineHooks.shrines.set(sdk.shrines.HealthExchange, "Health Exchange"); +ShrineHooks.shrines.set(sdk.shrines.ManaExchange, "Mana Exchange"); +ShrineHooks.shrines.set(sdk.shrines.Armor, "Armor"); +ShrineHooks.shrines.set(sdk.shrines.Combat, "Combat"); +ShrineHooks.shrines.set(sdk.shrines.ResistFire, "Resist Fire"); +ShrineHooks.shrines.set(sdk.shrines.ResistCold, "Resist Cold"); +ShrineHooks.shrines.set(sdk.shrines.ResistLightning, "Resist Lightning"); +ShrineHooks.shrines.set(sdk.shrines.ResistPoison, "Resist Poison"); +ShrineHooks.shrines.set(sdk.shrines.Skill, "Skill"); +ShrineHooks.shrines.set(sdk.shrines.ManaRecharge, "Mana Recharge"); +ShrineHooks.shrines.set(sdk.shrines.Stamina, "Stamina"); +ShrineHooks.shrines.set(sdk.shrines.Experience, "Experience"); +ShrineHooks.shrines.set(sdk.shrines.Enirhs, "Enirhs"); +ShrineHooks.shrines.set(sdk.shrines.Portal, "Portal"); +ShrineHooks.shrines.set(sdk.shrines.Gem, "Gem"); +ShrineHooks.shrines.set(sdk.shrines.Fire, "Fire"); +ShrineHooks.shrines.set(sdk.shrines.Monster, "Monster"); +ShrineHooks.shrines.set(sdk.shrines.Exploding, "Exploding"); +ShrineHooks.shrines.set(sdk.shrines.Poison, "Poison"); diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js index d52a4a213..2940f4be3 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js @@ -28,15 +28,15 @@ const TextHooks = { modifier: 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)), getScale: function (hkLen) { - if (!!this.yLocMapScale[hkLen]) { - this.frameYSizeScale = (-1 * this.yLocMapScale[hkLen]); - this.frameYLocScale = this.yLocMapScale[hkLen]; + if (!!TextHooks.yLocMapScale[hkLen]) { + TextHooks.frameYSizeScale = (-1 * this.yLocMapScale[hkLen]); + TextHooks.frameYLocScale = this.yLocMapScale[hkLen]; } else { - this.frameYSizeScale = 0; - this.frameYLocScale = 0; + TextHooks.frameYSizeScale = 0; + TextHooks.frameYLocScale = 0; } - this.settingsModifer = Math.max(0, hkLen - 3); + TextHooks.settingsModifer = Math.max(0, hkLen - 3); }, check: function () { @@ -49,8 +49,8 @@ const TextHooks = { if (!this.frameworkDisplayed) { !this.getHook("credits", this.hooks) && this.add("credits"); !!me.gameserverip && !this.getHook("ip", this.hooks) && this.add("ip"); - this.lastAct = 0; // sorta hacky solution, but works this will cause qolBoard to update after being flushed from a uiflag - this.frameworkDisplayed = true; + TextHooks.lastAct = 0; // sorta hacky solution, but works this will cause qolBoard to update after being flushed from a uiflag + TextHooks.frameworkDisplayed = true; } this.displaySettings ? !this.getHook("showSettings", this.statusHooks) && this.add("showSettings") : !this.getHook("hideSettings", this.statusHooks) && this.add("hideSettings"); @@ -176,9 +176,9 @@ const TextHooks = { break; case "qolBoard": - this.qolFrameYSize = 50; - this.lastAct = me.act; - this.wasInTown = me.inTown; + TextHooks.qolFrameYSize = 50; + TextHooks.lastAct = me.act; + TextHooks.wasInTown = me.inTown; while (this.qolHooks.length) { this.qolHooks.shift().hook.remove(); @@ -230,7 +230,7 @@ const TextHooks = { break; case "showSettings": - this.statusFrameYSize = 0; + TextHooks.statusFrameYSize = 0; while (this.statusHooks.length) { this.statusHooks.shift().hook.remove(); @@ -295,7 +295,7 @@ const TextHooks = { this.hooks.shift().hook.remove(); } - this.frameworkDisplayed = false; + TextHooks.frameworkDisplayed = false; } while (this.statusHooks.length) { diff --git a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js index 18a745450..d989d1608 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js @@ -11,6 +11,20 @@ const VectorHooks = { lastLoc: {x: 0, y: 0}, names: [], hooks: [], + nextAreas: (function() { + let nextAreas = []; + + // Specific area override + nextAreas[sdk.areas.TamoeHighland] = sdk.areas.MonasteryGate; + nextAreas[sdk.areas.SpiderForest] = sdk.areas.FlayerJungle; + nextAreas[sdk.areas.GreatMarsh] = sdk.areas.FlayerJungle; + nextAreas[sdk.areas.CrystalizedPassage] = sdk.areas.GlacialTrail; + nextAreas[sdk.areas.GlacialTrail] = sdk.areas.FrozenTundra; + nextAreas[sdk.areas.AncientsWay] = sdk.areas.ArreatSummit; + nextAreas[sdk.areas.ThroneofDestruction] = sdk.areas.WorldstoneChamber; + + return nextAreas; + })(), check: function () { if (!this.enabled) { @@ -24,28 +38,17 @@ const VectorHooks = { if (!me.area || !me.gameReady) return; - let nextAreas = []; - - // Specific area override - nextAreas[sdk.areas.TamoeHighland] = sdk.areas.MonasteryGate; - nextAreas[sdk.areas.SpiderForest] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.GreatMarsh] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.CrystalizedPassage] = sdk.areas.GlacialTrail; - nextAreas[sdk.areas.GlacialTrail] = sdk.areas.FrozenTundra; - nextAreas[sdk.areas.AncientsWay] = sdk.areas.ArreatSummit; - nextAreas[sdk.areas.ThroneofDestruction] = sdk.areas.WorldstoneChamber; - try { let exits = getArea().exits; - this.currArea = me.area; + VectorHooks.currArea = me.area; if (exits) { for (let i = 0; i < exits.length; i++) { if (me.inArea(sdk.areas.CanyonofMagic)) { this.add(exits[i].x, exits[i].y, exits[i].target === getRoom().correcttomb ? 0x69 : 0x99); - } else if (exits[i].target === nextAreas[me.area] && nextAreas[me.area]) { + } else if (exits[i].target === this.nextAreas[me.area] && this.nextAreas[me.area]) { this.add(exits[i].x, exits[i].y, 0x1F); - } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area) && nextAreas[me.area]) { + } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area) && this.nextAreas[me.area]) { this.add(exits[i].x, exits[i].y, 0x99); } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area)) { this.add(exits[i].x, exits[i].y, 0x1F); @@ -76,11 +79,11 @@ const VectorHooks = { }, addNames: function (area) { - this.names.push(new Text(Pather.getAreaName(area.target), area.x, area.y, 0, 6, 2, true)); + this.names.push(new Text(getAreaName(area.target), area.x, area.y, 0, 6, 2, true)); }, update: function () { - this.lastLoc = {x: me.x, y: me.y}; + VectorHooks.lastLoc = {x: me.x, y: me.y}; for (let i = 0; i < this.hooks.length; i++) { this.hooks[i].x = me.x; @@ -97,7 +100,7 @@ const VectorHooks = { this.names.shift().remove(); } - this.currArea = 0; + VectorHooks.currArea = 0; }, getWP: function () { @@ -107,10 +110,7 @@ const VectorHooks = { let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); if (preset) { - return { - x: preset.roomx * 5 + preset.x, - y: preset.roomy * 5 + preset.y - }; + return preset.realCoords(); } } @@ -384,14 +384,8 @@ const VectorHooks = { if (unit) { name && !poi.name && (poi.name = name); - - if (unit instanceof PresetUnit) { - poi.x = unit.roomx * 5 + unit.x; - poi.y = unit.roomy * 5 + unit.y; - } else { - poi.x = unit.x; - poi.y = unit.y; - } + (unit instanceof PresetUnit) && (unit = unit.realCoords()); + [poi.x, poi.y] = [unit.x, unit.y]; return poi; } diff --git a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js index d003b3507..564e549e2 100644 --- a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js @@ -5,16 +5,16 @@ * */ -includeIfNotIncluded("common/Attack.js"); +includeIfNotIncluded("core/Attack.js"); Attack.init = function (notify = false) { 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")) { print("Loading custom attack file"); - include("common/Attacks/" + Config.CustomClassAttack + ".js"); + include("core/Attacks/" + Config.CustomClassAttack + ".js"); } else { - include("common/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); + include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); } if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { diff --git a/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js b/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js index 9f776ec7d..d04d9635e 100644 --- a/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Config.js"); +includeIfNotIncluded("core/Config.js"); let original = Config.init; @@ -103,11 +103,11 @@ Config.init = function (notify) { } try { - if (Config.AutoBuild.Enabled === true && !isIncluded("common/AutoBuild.js") && include("common/AutoBuild.js")) { + if (Config.AutoBuild.Enabled === true && !isIncluded("core/Auto/AutoBuild.js") && include("core/Auto/AutoBuild.js")) { AutoBuild.initialize(); } } catch (e3) { - print("ÿc8Error in libs/common/AutoBuild.js (AutoBuild system is not active!)"); + print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); print(e3.toSource()); } }; diff --git a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js b/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js index 631832360..162d8664a 100644 --- a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Misc.js"); +includeIfNotIncluded("core/Misc.js"); Misc.openRedPortal = function (portalID) { if (!me.getItem(sdk.quest.item.Cube)) return; diff --git a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js index 179b72ff7..1090d7b2c 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Pather.js"); +includeIfNotIncluded("core/Pather.js"); Pather.stop = false; Pather.stopEvent = function (key) { diff --git a/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js index 051dde7ee..aa1935733 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Pickit.js"); +includeIfNotIncluded("core/Pickit.js"); Pickit.basicPickItems = function () { let itemList = []; diff --git a/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js b/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js index 306aadcf5..e4c0c732a 100644 --- a/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js @@ -5,7 +5,7 @@ * */ -includeIfNotIncluded("common/Town.js"); +includeIfNotIncluded("core/Town.js"); Town.stash = function (stashGold = true) { me.cancel(); @@ -29,7 +29,7 @@ Town.stash = function (stashGold = true) { if (items) { for (let i = 0; i < items.length; i++) { if (this.canStash(items[i])) { - Misc.itemLogger("Stashed", items[i]); + Item.logger("Stashed", items[i]); Storage.Stash.MoveTo(items[i]); } } diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 906f75c1f..d519b6a56 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -5,23 +5,27 @@ * @desc MapHelper used in conjuction with MapThread.js * */ -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("AutoMule.js"); -include("Gambling.js"); -include("TorchSystem.js"); -include("CraftingSystem.js"); -include("MuleLogger.js"); -include("common/util.js"); - -includeCommonLibs(); +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"); // MapMode include("manualplay/MapMode.js"); MapMode.include(); function main() { + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); + + console.log("ÿc9MapHelper loaded"); + let obj = {type: false, dest: false, action: false}; let action, fail = 0, x, y; let mapThread = getScript("libs/manualplay/threads/mapthread.js"); @@ -45,7 +49,6 @@ function main() { 25: [12948, 9128], }; - console.log("ÿc9MapHelper loaded"); Config.init(); Attack.init(true); Pickit.init(); @@ -57,7 +60,7 @@ function main() { this.togglePickThread = function () { if (!Config.ManualPlayPick) return; - let pickThread = getScript("tools/pickthread.js"); + const pickThread = getScript("threads/pickthread.js"); if (pickThread) { if (pickThread.running) { @@ -100,7 +103,6 @@ function main() { if (getUIFlag(sdk.uiflags.EscMenu)) { delay(100); mapThread.running && this.togglePause(); - } else { if (!mapThread.running) { if (!this.togglePause()) { diff --git a/d2bs/kolbot/libs/manualplay/threads/MapThread.js b/d2bs/kolbot/libs/manualplay/threads/MapThread.js index cbd484955..119297bfe 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapThread.js @@ -5,17 +5,19 @@ * @desc MapThread used with D2BotMap.dbj * */ -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("UnitInfo.js"); -include("common/util.js"); -includeCommonLibs(); +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("../../modules/LocalChat"); include("manualplay/MapMode.js"); MapMode.include(); @@ -50,7 +52,7 @@ const Hooks = { } if (!this.enabled) { - this.enabled = getUIFlag(sdk.uiflags.AutoMap); + Hooks.enabled = getUIFlag(sdk.uiflags.AutoMap); return; } @@ -68,7 +70,7 @@ const Hooks = { if (Hooks.flushed === flag) return true; if (flag === true) { - this.enabled = false; + Hooks.enabled = false; MonsterHooks.flush(); ShrineHooks.flush(); @@ -101,30 +103,67 @@ const Hooks = { }; function main() { - print("ÿc9Map Thread Loaded."); - Config.init(false); + 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"); + + console.log("ÿc9Map Thread Loaded."); + MapMode.include(); + Config.init(true); + LocalChat.init(); Storage.Init(); Pickit.init(true); Hooks.init(); - if (Config.MapMode.UseOwnItemFilter) { - ItemHooks.pickitEnabled = true; - } + // load threads + me.automap = true; + load("libs/manualplay/threads/maphelper.js"); + load("libs/manualplay/threads/maptoolsthread.js"); + Config.ManualPlayPick && load("libs/manualplay/threads/pickthread.js"); + Config.PublicMode && load("threads/party.js"); const Worker = require("../../modules/Worker"); + const UnitInfo = new (require("../../modules/UnitInfo")); Worker.runInBackground.unitInfo = function () { - if (!Hooks.userAddon || (!UnitInfo.cleared && !Game.getSelectedUnit())) { - UnitInfo.remove(); + // always, maybe a timeout would be good though + UnitInfo.check(); + + // not being used atm - keep looping + if (!Hooks.userAddon) { return true; } - - let unit = Game.getSelectedUnit(); - !!unit && UnitInfo.createInfo(unit); + + UnitInfo.createInfo(Game.getSelectedUnit()); return true; }; + const log = (msg = "") => { + me.overhead(msg); + console.log(msg); + }; + + if (Config.MapMode.UseOwnItemFilter) { + ItemHooks.pickitEnabled = true; + } + const hideFlags = [ sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Waypoint, sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, @@ -148,9 +187,7 @@ function main() { // Run commands from chat this.runCommand = function (msg) { - if (msg.length <= 1) { - return true; - } + if (msg.length <= 1) return true; msg = msg.toLowerCase(); let cmd = msg.split(" ")[0].split(".")[1]; @@ -160,12 +197,11 @@ function main() { switch (cmd) { case "useraddon": Hooks.userAddon = !Hooks.userAddon; - me.overhead("userAddon set to " + Hooks.userAddon); + log("userAddon set to " + Hooks.userAddon); break; case "me": - print("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); - me.overhead("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); + log("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); break; case "stash": @@ -202,7 +238,7 @@ function main() { case "help": if (HelpMenu.cleared) { HelpMenu.showMenu(); - me.overhead("Click each command for more info"); + log("Click each command for more info"); } break; @@ -217,11 +253,13 @@ function main() { break; case "make": - if (!FileTools.exists("libs/manualplay/config/" + sdk.player.class.nameOf(me.classid) + "." + me.name + ".js")) { - FileTools.copy("libs/manualplay/config/" + sdk.player.class.nameOf(me.classid) + ".js", "libs/manualplay/config/" + sdk.player.class.nameOf(me.classid) + "." + me.name + ".js"); - D2Bot.printToConsole("libs/manualplay/config/" + sdk.player.class.nameOf(me.classid) + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); - print("libs/manualplay/config/" + sdk.player.class.nameOf(me.classid) + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); - me.overhead("libs/manualplay/config/" + sdk.player.class.nameOf(me.classid) + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); + { + let className = sdk.player.class.nameOf(me.classid); + if (!FileTools.exists("libs/manualplay/config/" + className + "." + me.name + ".js")) { + FileTools.copy("libs/manualplay/config/" + className + ".js", "libs/manualplay/config/" + className + "." + me.name + ".js"); + D2Bot.printToConsole("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); + log("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); + } } break; diff --git a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js index 2d303f60b..e78de8ed7 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js @@ -6,30 +6,32 @@ * */ 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("common/util.js"); +// globals needed for core gameplay +includeCoreLibs(); +include("core/Common/Tools.js"); -includeCommonLibs(); +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); // MapMode include("manualplay/MapMode.js"); MapMode.include(); function main() { + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); + + console.log("ÿc9MapToolsThread loaded"); + let ironGolem, debugInfo = {area: 0, currScript: "no entry"}; let quitFlag = false; let quitListDelayTime; let canQuit = true; - console.log("ÿc9MapToolsThread loaded"); D2Bot.init(); Config.init(false); Pickit.init(false); @@ -49,12 +51,12 @@ function main() { // General functions Common.Toolsthread.pauseScripts = [ - "default.dbj", "tools/townchicken.js", "libs/manualplay/threads/pickthread.js", - "tools/antihostile.js", "tools/party.js", "libs/manualplay/threads/maphelper.js", + "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", + "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", ]; Common.Toolsthread.stopScripts = [ - "default.dbj", "tools/townchicken.js", "libs/manualplay/threads/pickthread.js", - "tools/antihostile.js", "tools/party.js", "libs/manualplay/threads/maphelper.js", + "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", + "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", ]; // Event functions @@ -112,13 +114,13 @@ function main() { 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") - || (Array.isArray(Config.QuitList) && Config.QuitList.includes(name1))) { - print(name1 + (mode === 0 ? " timed out" : " left")); + 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(); } @@ -154,7 +156,7 @@ function main() { if (Config.SoJWaitTime && me.expansion) { !!me.realm && 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("default.dbj", "soj"); } break; @@ -179,20 +181,15 @@ function main() { }; // Cache variables to prevent a bug where d2bs loses the reference to Config object - Config = Misc.copy(Config); + Config = copyObj(Config); let tick = getTickCount(); addEventListener("keyup", this.keyEvent); addEventListener("gameevent", this.gameEvent); addEventListener("scriptmsg", this.scriptEvent); - // Load Fastmod - // Packet.changeStat(105, Config.FCR); - // Packet.changeStat(99, Config.FHR); - // Packet.changeStat(102, Config.FBR); - // Packet.changeStat(93, Config.IAS); - Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); + !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks // Start while (true) { @@ -204,7 +201,7 @@ function main() { if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting if (!me.inTown) { - D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + Pather.getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -215,7 +212,7 @@ function main() { Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { - D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -229,7 +226,7 @@ function main() { if (ironGolem) { // ironGolem.hpmax is bugged with BO if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { - D2Bot.printToConsole("Irom Golem Chicken in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -244,7 +241,7 @@ function main() { if (mercHP > 0 && merc.mode !== sdk.monsters.mode.Dead) { if (mercHP < Config.MercChicken) { - D2Bot.printToConsole("Merc Chicken in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -271,15 +268,19 @@ function main() { quitFlag = true; } - if (quitFlag && canQuit && (typeof quitListDelayTime === "undefined" || getTickCount() >= quitListDelayTime)) { + if (quitFlag && canQuit) { + if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { + me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); + continue; + } Common.Toolsthread.checkPing(false); // In case of quitlist triggering first Common.Toolsthread.exit(); break; } - 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/d2bs/kolbot/libs/manualplay/threads/PickThread.js b/d2bs/kolbot/libs/manualplay/threads/PickThread.js index 8fdee6a94..a1e8e2472 100644 --- a/d2bs/kolbot/libs/manualplay/threads/PickThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/PickThread.js @@ -6,20 +6,26 @@ */ js_strict(true); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("CraftingSystem.js"); -include("common/util.js"); +include("json2.js"); // required? +include("polyfill.js"); // required +include("oog/D2Bot.js"); // required -includeCommonLibs(); +// globals needed for core gameplay +// todo - figure out what here is actually needed for mapmode vs what is only required for bot mode +include("core/NTItemParser.js"); +include("core/Util"); +includeCoreLibs(); + +// system libs - same for here +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); // MapMode include("manualplay/MapMode.js"); MapMode.include(); function main() { - print("ÿc9Pick Thread Loaded."); + console.log("ÿc9Pick Thread Loaded."); Config.init(false); Pickit.init(false); Attack.init(); diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index 68db3598c..cf6b780b5 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -3,19 +3,47 @@ * @author Jaenster, theBGuy(added the rest of the controls) */ (function (module) { - /** - * @constructor - Not callable as a function - * - * @method Control.click(targetx, targety) - * @method Control.setText(text) - * @method Control.getText(text) - */ + * Not callable as a function + * @constructor + * @method Control.click(targetx, targety) + * @method Control.setText(text) + * @method Control.getText(text) + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + */ function Control(type, x, y, xsize, ysize) { + /** + * @private + * @type {number} + */ this.type = type; + + /** + * @private + * @type {number} + */ this.x = x; + + /** + * @private + * @type {number} + */ this.y = y; + + /** + * @private + * @type {number} + */ this.xsize = xsize; + + /** + * @private + * @type {number} + */ this.ysize = ysize; return new Proxy(this, { diff --git a/d2bs/kolbot/libs/modules/CopyData.js b/d2bs/kolbot/libs/modules/CopyData.js new file mode 100644 index 000000000..a3955248e --- /dev/null +++ b/d2bs/kolbot/libs/modules/CopyData.js @@ -0,0 +1,102 @@ +/** + * @filename CopyData.js + * @author theBGuy + * @desc UMD module for creating and sending a copy data obj + * + */ + +(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.CopyData = factory(); + console.trace(); + } +}(globalThis, function() { + /** + * @class + * @classdesc A class for creating and sending copy data packets. + * @property {number} _mode - Defaults to 0, works for most D2Bot functions + * @property {number | string} _handle - Defaults to value of D2Bot.handle, works for any D2Bot + * functions that act on ourselves + * @example Request a game from "scl-sorc-001" profile + * new CopyData().handle("scl-sorc-001").mode(3).send(); + * @example Start mule profile "mule" + * new CopyData().data("start", ["mule"]).send(); + */ + function CopyData() { + if (this.__proto__.constructor !== CopyData) throw new Error("CopyData must be called with 'new' operator!"); + + /** + * @private + * @type {string | number} - The handle to send the copy data to. + */ + this._handle = D2Bot.handle || me.profile; + + /** + * @private + * @type {number} - The mode of the copy data packet. + */ + this._mode = 0; + + /** + * @private + * @type {string} - The data to send in the copy data + */ + this._data = null; + } + + /** + * - D2Bot.handle is for any functions that act on ourselves + * - Otherwise it is the D2Bot# profile name of the profile to act upon + * @param {string | number} handle - The handle or profile to send the copy data to. + */ + CopyData.prototype.handle = function (handle) { + this._handle = handle; + return this; + }; + + /** + * - 0 is for most functions, and the default value set + * - 1 is for joinMe + * - 3 is for requestGame + * - 0xbbbb is for heartBeat + * @param {number} mode - The mode of the copy data packet. + */ + CopyData.prototype.mode = function (mode) { + this._mode = mode; + return this; + }; + + /** + * @param {string} [func] - The function to call from D2Bot# + * @param {string[]} [args] - The additonal info needed for the function call + */ + CopyData.prototype.data = function (func = "", args = []) { + if (func.includes("Item") || func === "printToConsole" || (func === "setTag" && typeof args[0] === "object")) { + args[0] = JSON.stringify(args[0]); + } + this._data = JSON.stringify({ + profile: me.profile, + func: func, + args: args + }); + return this; + }; + + /** + * CopyData.data works for functions call d2bot# functions but what about gerneral use? + * @todo handle passing custom data obj + */ + + CopyData.prototype.send = function () { + // check that data is set + this._data === null && this.data(); + return sendCopyData(null, this._handle, this._mode, this._data); + }; + + return CopyData; +})); diff --git a/d2bs/kolbot/libs/modules/LocalChat.js b/d2bs/kolbot/libs/modules/LocalChat.js new file mode 100644 index 000000000..942f7650f --- /dev/null +++ b/d2bs/kolbot/libs/modules/LocalChat.js @@ -0,0 +1,80 @@ +/** + * @filename LocalChat.js + * @author theBGuy + * @desc UMD module for LocalChat system + * + */ + +(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(["require", "../core/Util"], factory); + } else { + root.LocalChat = factory(); + console.trace(); + } +}(this, function() { + // const { PacketBuilder } = require("../core/Util"); + + const LocalChat = new function () { + const LOCAL_CHAT_ID = 0xD2BAAAA; + let toggle, proxy = say; + + let relay = (msg) => D2Bot.shoutGlobal(JSON.stringify({msg: msg, realm: me.realm, charname: me.charname, gamename: me.gamename }), LOCAL_CHAT_ID); + + let onChatInput = (speaker, msg) => { + relay(msg); + return true; + }; + + let onChatRecv = (mode, msg) => { + if (mode !== LOCAL_CHAT_ID) return; + + msg = JSON.parse(msg); + + if (me.gamename === msg.gamename && me.realm === msg.realm) { + new PacketBuilder().byte(38).byte(1, me.locale).word(2, 0, 0).byte(90).string(msg.charname, msg.msg).get(); + } + }; + + let onKeyEvent = (key) => { + if (toggle === key) { + this.init(true); + } + }; + + this.init = (cycle = false) => { + if (!Config.LocalChat.Enabled) return; + + Config.LocalChat.Mode = (Config.LocalChat.Mode + cycle) % 3; + print("ÿc2LocalChat enabled. Mode: " + Config.LocalChat.Mode); + + switch (Config.LocalChat.Mode) { + case 2: + removeEventListener("chatinputblocker", onChatInput); + addEventListener("chatinputblocker", onChatInput); + // eslint-disable-next-line no-fallthrough + case 1: + removeEventListener("copydata", onChatRecv); + addEventListener("copydata", onChatRecv); + say = (msg, force = false) => force ? proxy(msg) : relay(msg); + break; + case 0: + removeEventListener("chatinputblocker", onChatInput); + removeEventListener("copydata", onChatRecv); + say = proxy; + break; + } + + if (Config.LocalChat.Toggle) { + toggle = typeof Config.LocalChat.Toggle === "string" ? Config.LocalChat.Toggle.charCodeAt(0) : Config.LocalChat.Toggle; + Config.LocalChat.Toggle = false; + addEventListener("keyup", onKeyEvent); + } + }; + }; + + return LocalChat; +})); diff --git a/d2bs/kolbot/libs/UnitInfo.js b/d2bs/kolbot/libs/modules/UnitInfo.js similarity index 57% rename from d2bs/kolbot/libs/UnitInfo.js rename to d2bs/kolbot/libs/modules/UnitInfo.js index a15422eca..e955eed6a 100644 --- a/d2bs/kolbot/libs/UnitInfo.js +++ b/d2bs/kolbot/libs/modules/UnitInfo.js @@ -4,59 +4,125 @@ * @desc Display unit info * */ -include("common/prototypes.js"); -const UnitInfo = new function () { - this.x = 200; - this.y = 250; - this.hooks = []; - this.cleared = true; - this.resfix = {x: (me.screensize ? 0 : -160), y: (me.screensize ? 0 : -120)}; +include("core/prototypes.js"); + +(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.UnitInfo = factory(); + } +}(this, function () { + /** + * @constructor + */ + function UnitInfo () { + /** + * screen coordinate for info box + * @private + * @type {number} + */ + this.x = 200; + + /** + * screen coordinate for info box + * @private + * @type {number} + */ + this.y = 250; + + /** + * @private + * @type {any[]} + */ + this.hooks = []; + + /** + * @private + * @type {number | null} + */ + this.currentGid = null; + + /** + * @private + * @type {boolean} + */ + this.cleared = true; - this.createInfo = function (unit) { + /** + * @private + * @type {{ x: number, y: number }} + */ + this.resfix = { + x: (me.screensize ? 0 : -160), + y: (me.screensize ? 0 : -120) + }; + } + + /** + * Create info based on unit type + * @param {Unit} unit + */ + UnitInfo.prototype.createInfo = function (unit) { if (typeof unit === "undefined") { this.remove(); + } else { + // same unit, no changes so skip + if (this.currentGid === unit.gid) return; + // some hooks left over, remove them + this.hooks.length > 0 && this.remove(); + // set new gid + this.currentGid = unit.gid; - return; - } - - switch (unit.type) { - case sdk.unittype.Player: - this.playerInfo(unit); + switch (unit.type) { + case sdk.unittype.Player: + this.playerInfo(unit); - break; - case sdk.unittype.Monster: - this.monsterInfo(unit); + break; + case sdk.unittype.Monster: + this.monsterInfo(unit); - break; - case sdk.unittype.Object: - case sdk.unittype.Stairs: - this.objectInfo(unit); + break; + case sdk.unittype.Object: + case sdk.unittype.Stairs: + this.objectInfo(unit); - break; - case sdk.unittype.Item: - this.itemInfo(unit); + break; + case sdk.unittype.Item: + this.itemInfo(unit); - break; + break; + } } - }; - - this.playerInfo = function (unit) { - !this.currentGid && (this.currentGid = unit.gid); - if (this.currentGid === unit.gid && !this.cleared) { - return; - } + }; - if (this.currentGid !== unit.gid) { + /** + * Check that selected unit is still valid + */ + UnitInfo.prototype.check = function () { + // make sure things got cleaned up properly if we are supposedly cleared + if (this.hooks.length === 0 && this.cleared) return; + // don't deal with this when an item is on our cursor + if (me.itemoncursor) return; + let unit = Game.getSelectedUnit(); + if (typeof unit === "undefined" || unit.gid !== this.currentGid) { this.remove(); - this.currentGid = unit.gid; } + }; + /** + * @private + * @param {Player} unit + */ + UnitInfo.prototype.playerInfo = function (unit) { let string; let frameXsize = 0; let frameYsize = 20; - let quality = ["ÿc0", "ÿc0", "ÿc0", "ÿc0", "ÿc3", "ÿc2", "ÿc9", "ÿc4", "ÿc8"]; let items = unit.getItemsEx(); this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y, 4, 13, 2)); @@ -65,17 +131,19 @@ const UnitInfo = new function () { this.hooks.push(new Text("Equipped items:", this.x, this.y + 15, 4, 13, 2)); frameYsize += 15; - for (let i = 0; i < items.length; i += 1) { - if (items[i].getFlag(sdk.items.flags.Runeword)) { - string = items[i].fname.split("\n")[1] + "ÿc0 " + items[i].fname.split("\n")[0]; + items.forEach(item => { + if (item.runeword) { + string = item.fname.split("\n")[1] + "ÿc0 " + item.fname.split("\n")[0]; } else { - string = quality[items[i].quality] + (items[i].quality > 4 && items[i].getFlag(sdk.items.flags.Identified) ? items[i].fname.split("\n").reverse()[0].replace("ÿc4", "") : items[i].name); + string = Item.color(item, false) + (item.quality > sdk.items.quality.Magic && item.identified + ? item.fname.split("\n").reverse()[0].replace("ÿc4", "") + : item.name); } this.hooks.push(new Text(string, this.x, this.y + (i + 2) * 15, 0, 13, 2)); string.length > frameXsize && (frameXsize = string.length); frameYsize += 15; - } + }); } this.cleared = false; @@ -85,18 +153,11 @@ const UnitInfo = new function () { this.hooks[this.hooks.length - 2].zorder = 0; }; - this.monsterInfo = function (unit) { - !this.currentGid && (this.currentGid = unit.gid); - - if (this.currentGid === unit.gid && !this.cleared) { - return; - } - - if (this.currentGid !== unit.gid) { - this.remove(); - this.currentGid = unit.gid; - } - + /** + * @private + * @param {Monster} unit + */ + UnitInfo.prototype.monsterInfo = function (unit) { let frameYsize = 125; this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y, 4, 13, 2)); @@ -115,21 +176,14 @@ const UnitInfo = new function () { this.hooks[this.hooks.length - 2].zorder = 0; }; - this.itemInfo = function (unit) { - !this.currentGid && (this.currentGid = unit.gid); - - if (this.currentGid === unit.gid && !this.cleared) { - return; - } - - if (this.currentGid !== unit.gid) { - this.remove(); - this.currentGid = unit.gid; - } - + /** + * @private + * @param {ItemUnit} unit + */ + UnitInfo.prototype.itemInfo = function (unit) { let xpos = 60; let ypos = (me.getMerc() ? 80 : 20) + (-1 * this.resfix.y); - let frameYsize = 50; + let frameYsize = 65; this.hooks.push(new Text("Code: ÿc0" + unit.code, xpos, ypos + 0, 4, 13, 2)); this.hooks.push(new Text("Classid: ÿc0" + unit.classid, xpos, ypos + 15, 4, 13, 2)); @@ -137,11 +191,11 @@ const UnitInfo = new function () { this.hooks.push(new Text("Item level: ÿc0" + unit.ilvl, xpos, ypos + 45, 4, 13, 2)); this.cleared = false; - this.socketedItems = unit.getItems(); + this.socketedItems = unit.getItemsEx(); - if (this.socketedItems) { + if (this.socketedItems.length) { this.hooks.push(new Text("Socketed with:", xpos, ypos + 60, 4, 13, 2)); - frameYsize += 30; + frameYsize += 15; for (let i = 0; i < this.socketedItems.length; i += 1) { this.hooks.push(new Text(this.socketedItems[i].fname.split("\n").reverse().join(" "), xpos, ypos + (i + 5) * 15, 0, 13, 2)); @@ -168,25 +222,18 @@ const UnitInfo = new function () { this.hooks[this.hooks.length - 2].zorder = 0; }; - this.objectInfo = function (unit) { - !this.currentGid && (this.currentGid = unit.gid); - - if (this.currentGid === unit.gid && !this.cleared) { - return; - } - - if (this.currentGid !== unit.gid) { - this.remove(); - this.currentGid = unit.gid; - } - + /** + * @private + * @param {ObjectUnit} unit + */ + UnitInfo.prototype.objectInfo = function (unit) { let frameYsize = 35; this.hooks.push(new Text("Type: ÿc0" + unit.type, this.x, this.y, 4, 13, 2)); this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y + 15, 4, 13, 2)); if (!!unit.objtype) { - this.hooks.push(new Text("Destination: ÿc0" + unit.objtype, this.x, this.y + 30, 4, 13, 2)); + this.hooks.push(new Text((unit.name.toLowerCase().includes("shrine") ? "Type" : "Destination") + ": ÿc0" + unit.objtype, this.x, this.y + 30, 4, 13, 2)); frameYsize += 15; } @@ -197,12 +244,16 @@ const UnitInfo = new function () { this.hooks.push(new Frame(this.x, this.y - 15, 120, frameYsize, 2)); this.hooks[this.hooks.length - 2].zorder = 0; }; - - this.remove = function () { + + UnitInfo.prototype.remove = function () { while (this.hooks.length > 0) { this.hooks.shift().remove(); } + this.currentGid = null; this.cleared = true; }; -}; + + return UnitInfo; +})); + diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index dbda6f715..3a7ef0f58 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -3,36 +3,24 @@ * @author jaenster, theBGuy * @desc development tool for readability * @format sdk.objprop.objprop.ObjProp (excludes functions which use sdk.objprop.camelCase) +* @type UMD module * */ -// todo: break this up to make more sense. Example -/* -item: { - mode: { - - }, - class: { - - }, - quality: { - - }, - type: { - - }, - classid: { - - }, - locale: { - - }, - body and storage? It onlys applies to items - or would it make more sense for player.body? -} -*/ - -(function (module) { +(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.sdk = factory(); + } +}(this, function () { + "use strict"; + /** + * @exports sdk + */ const sdk = { waypoints: { Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539], @@ -4771,6 +4759,7 @@ item: { ReassignPlayer: 0x15, SetSkill: 0x23, Chat: 0x26, + UniqueEvents: 0x89, WeaponSwitch: 0x97, } } @@ -4878,5 +4867,5 @@ item: { }, }; - module.exports = sdk; -})(module); + return sdk; +})); diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js new file mode 100644 index 000000000..93aac874d --- /dev/null +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -0,0 +1,207 @@ +/** +* @filename D2Bot.js +* @author kolton, D3STROY3R, theBGuy +* @desc UMD module to handle interfacing with D2Bot# +* +*/ + +!isIncluded("Polyfill.js") && include("Polyfill.js"); +includeIfNotIncluded("oog/DataFile.js"); + +(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(["require", "../modules/CopyData"], factory); + } else { + root.D2Bot = factory(); + } +}(globalThis, function() { + const CopyData = require("../modules/CopyData"); + + const D2Bot = { + handle: 0, + + init: function () { + let handle = DataFile.getStats().handle; + + if (handle) { + D2Bot.handle = handle; + } + + return D2Bot.handle; + }, + + sendMessage: function (handle, mode, msg) { + sendCopyData(null, handle, mode, msg); + }, + + printToConsole: function (msg, color, tooltip, trigger) { + let printObj = { + msg: msg, + color: color || 0, + tooltip: tooltip || "", + trigger: trigger || "" + }; + + new CopyData().data("printToConsole", [printObj]).send(); + }, + + printToItemLog: function (itemObj) { + new CopyData().data("printToItemLog", [itemObj]).send(); + }, + + uploadItem: function (itemObj) { + new CopyData().data("uploadItem", [itemObj]).send(); + }, + + writeToFile: function (filename, msg) { + new CopyData().data("writeToFile", [filename, msg]).send(); + }, + + postToIRC: function (ircProfile, recepient, msg) { + new CopyData().data("postToIRC", [ircProfile, recepient, msg]).send(); + }, + + ircEvent: function (mode) { + new CopyData().data("ircEvent", [mode ? "true" : "false"]).send(); + }, + + notify: function (msg) { + new CopyData().data("notify", [msg]).send(); + }, + + saveItem: function (itemObj) { + new CopyData().data("saveItem", [itemObj]).send(); + }, + + updateStatus: function (msg) { + new CopyData().data("updateStatus", [msg]).send(); + }, + + updateRuns: function () { + new CopyData().data("updateRuns", []).send(); + }, + + updateChickens: function () { + new CopyData().data("updateChickens", []).send(); + }, + + updateDeaths: function () { + new CopyData().data("updateDeaths", []).send(); + }, + + requestGameInfo: function () { + new CopyData().data("requestGameInfo", []).send(); + }, + + restart: function (keySwap) { + new CopyData().data( + "restartProfile", + arguments.length > 0 ? [me.profile, keySwap] : [me.profile] + ).send(); + }, + + CDKeyInUse: function () { + new CopyData().data("CDKeyInUse", []).send(); + }, + + CDKeyDisabled: function () { + new CopyData().data("CDKeyDisabled", []).send(); + }, + + CDKeyRD: function () { + new CopyData().data("CDKeyRD", []).send(); + }, + + stop: function (profile, release) { + !profile && (profile = me.profile); + + new CopyData().data("stop", [profile, release ? "True" : "False"]).send(); + }, + + start: function (profile) { + new CopyData().data("start", [profile]).send(); + }, + + startSchedule: function (profile) { + new CopyData().data("startSchedule", [profile]).send(); + }, + + stopSchedule: function (profile) { + new CopyData().data("stopSchedule", [profile]).send(); + }, + + updateCount: function () { + new CopyData().data("updateCount", ["1"]).send(); + }, + + shoutGlobal: function (msg, mode) { + new CopyData().data("shoutGlobal", [msg, mode]).send(); + }, + + heartBeat: function () { + new CopyData().mode(0xbbbb).data("heartBeat", []).send(); + }, + + sendWinMsg: function (wparam, lparam) { + new CopyData().data("winmsg", [wparam, lparam]).send(); + }, + + ingame: function () { + this.sendWinMsg(0x0086, 0x0000); + this.sendWinMsg(0x0006, 0x0002); + this.sendWinMsg(0x001c, 0x0000); + }, + + // Profile to profile communication + joinMe: function (profile, gameName, gameCount, gamePass, isUp) { + let obj = { + gameName: gameName + gameCount, + gamePass: gamePass, + inGame: isUp === "yes" + }; + + sendCopyData(null, profile, 1, JSON.stringify(obj)); + }, + + requestGame: function (profile) { + new CopyData().handle(profile).mode(3).send(); + }, + + getProfile: function () { + new CopyData().data("getProfile", []).send(); + }, + + setProfile: function (account, password, character, difficulty, realm, infoTag, gamePath) { + new CopyData().data( + "setProfile", + [account, password, character, difficulty, realm, infoTag, gamePath] + ).send(); + }, + + setTag: function (tag) { + new CopyData().data("setTag", [tag]).send(); + }, + + // Store info in d2bot# cache + store: function (info) { + this.remove(); + + new CopyData().data("store", [me.profile, info]).send(); + }, + + // Get info from d2bot# cache + retrieve: function () { + new CopyData().data("retrieve", [me.profile]).send(); + }, + + // Delete info from d2bot# cache + remove: function () { + new CopyData().data("delete", [me.profile]).send(); + } + }; + + return D2Bot; +})); diff --git a/d2bs/kolbot/libs/oog/DataFile.js b/d2bs/kolbot/libs/oog/DataFile.js new file mode 100644 index 000000000..ab134113b --- /dev/null +++ b/d2bs/kolbot/libs/oog/DataFile.js @@ -0,0 +1,131 @@ +/** +* @filename DataFile.js +* @author kolton, D3STROY3R, theBGuy +* @desc Maintain profile datafiles +* +*/ + +!isIncluded("Polyfill.js") && include("Polyfill.js"); +includeIfNotIncluded("oog/FileAction.js"); + +(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.DataFile = factory(); + } +}(this, function() { + const DataFile = { + _default: { + runs: 0, + experience: 0, + deaths: 0, + lastArea: "", + gold: 0, + level: 0, + name: "", + gameName: "", + ingameTick: 0, + handle: 0, + nextGame: "" + }, + + create: function () { + FileAction.write("data/" + me.profile + ".json", JSON.stringify(this._default)); + + return this._default; + }, + + getObj: function () { + !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); + + let obj; + let string = FileAction.read("data/" + me.profile + ".json"); + + try { + obj = JSON.parse(string); + } catch (e) { + // If we failed, file might be corrupted, so create a new one + obj = this.create(); + } + + if (obj) { + return obj; + } + + console.warn("Error reading DataFile. Using null values."); + + return this._default; + }, + + getStats: function () { + let obj = this.getObj(); + + return clone(obj); + }, + + updateStats: function (arg, value) { + while (me.ingame && !me.gameReady) { + delay(100); + } + + let statArr = []; + + typeof arg === "object" && (statArr = arg.slice()); + typeof arg === "string" && statArr.push(arg); + + let obj = this.getObj(); + + for (let i = 0; i < statArr.length; i += 1) { + switch (statArr[i]) { + case "experience": + obj.experience = me.getStat(sdk.stats.Experience); + obj.level = me.getStat(sdk.stats.Level); + + break; + case "lastArea": + if (obj.lastArea === getAreaName(me.area)) { + return; + } + + obj.lastArea = getAreaName(me.area); + + break; + case "gold": + if (!me.gameReady) { + break; + } + + obj.gold = me.getStat(sdk.stats.Gold) + me.getStat(sdk.stats.GoldBank); + + break; + case "name": + obj.name = me.name; + + break; + case "ingameTick": + obj.ingameTick = getTickCount(); + + break; + case "deaths": + obj.deaths = (obj.deaths || 0) + 1; + + break; + default: + obj[statArr[i]] = value; + + break; + } + } + + let string = JSON.stringify(obj); + + FileAction.write("data/" + me.profile + ".json", string); + } + }; + + return DataFile; +})); diff --git a/d2bs/kolbot/libs/oog/FileAction.js b/d2bs/kolbot/libs/oog/FileAction.js new file mode 100644 index 000000000..c2982c07d --- /dev/null +++ b/d2bs/kolbot/libs/oog/FileAction.js @@ -0,0 +1,95 @@ +/** +* @filename FileAction.js +* @author theBGuy +* @desc Handle CRUD operations +* +*/ + +!isIncluded("Polyfill.js") && include("Polyfill.js"); + +/** + * Should this file be in the oog folder? Its technically actions performed out of game but many in game functions use it. + * Maybe common? I want core/ to essentially be an alias for core. Everything that is required for in game functionality + * All "extras" should be somewhere else + */ + +const FileAction = { + read: function (path = "") { + if (!path) throw new Error("No path provided"); + + let contents = ""; + + for (let i = 0; i < 30; i++) { + try { + contents = FileTools.readText(path); + + if (contents) return contents; + } catch (e) { + console.error(e); + } + + delay(100); + } + + return contents; + }, + + write: function (path = "", msg = "") { + if (!path) throw new Error("No path provided"); + + // do we read the file to see if it has changed? + // for now keep the orginal behavior + for (let i = 0; i < 30; i++) { + try { + FileTools.writeText(path, msg); + + break; + } catch (e) { + console.error(e); + } + + delay(100); + } + + return true; + }, + + append: function (path = "", msg = "") { + if (!path) throw new Error("No path provided"); + + // do we read the file to see if it has changed? + // for now keep the orginal behavior + for (let i = 0; i < 30; i++) { + try { + FileTools.appendText(path, msg); + + break; + } catch (e) { + console.error(e); + } + + delay(100); + } + + return true; + }, + + parse: function (path = "") { + if (!path) throw new Error("No path provided"); + if (!FileTools.exists(path)) throw new Error("Can't parse file that doesn't exist"); + + let contents = ""; + + try { + contents = FileAction.read(path); + + if (contents) { + return JSON.parse(contents); + } + } catch (e) { + console.error(e); + } + + return contents; + }, +}; diff --git a/d2bs/kolbot/libs/oog/ShitList.js b/d2bs/kolbot/libs/oog/ShitList.js new file mode 100644 index 000000000..e5b1f2929 --- /dev/null +++ b/d2bs/kolbot/libs/oog/ShitList.js @@ -0,0 +1,59 @@ +/** +* @filename ShitList.js +* @author kolton, D3STROY3R, theBGuy +* @desc Maintain shitlist of griefers +* +*/ + +!isIncluded("Polyfill.js") && include("Polyfill.js"); +includeIfNotIncluded("oog/FileAction.js"); + +const ShitList = { + _default: { + shitlist: [] + }, + create: function () { + let string = JSON.stringify(this._default); + + FileAction.write("shitlist.json", string); + + return obj; + }, + + getObj: function () { + let obj; + let string = FileAction.read("shitlist.json"); + + try { + obj = JSON.parse(string); + } catch (e) { + obj = this.create(); + } + + if (obj) { + return obj; + } + + console.warn("Failed to read ShitList. Using null values"); + + return this._default; + }, + + read: function () { + !FileTools.exists("shitlist.json") && this.create(); + + let obj = this.getObj(); + + return obj.shitlist; + }, + + add: function (name) { + let obj = this.getObj(); + + obj.shitlist.push(name); + + let string = JSON.stringify(obj); + + FileAction.write("shitlist.json", string); + } +}; diff --git a/d2bs/kolbot/libs/require.js b/d2bs/kolbot/libs/require.js index 416a08f85..c34cc8e06 100644 --- a/d2bs/kolbot/libs/require.js +++ b/d2bs/kolbot/libs/require.js @@ -26,10 +26,9 @@ function removeRelativePath(test) { } global.require = (function (include, isIncluded, print, notify) { - let depth = 0; const modules = {}; - const obj = function require(field, path) { + const obj = function require(field, path, log = true) { const stack = new Error().stack.match(/[^\r\n]+/g); let directory = stack[1].match(/.*?@.*?d2bs\\(kolbot\\?.*)\\.*(\.js|\.dbj):/)[1].replace("\\", "/") + "/"; let filename = stack[1].match(/.*?@.*?d2bs\\kolbot\\?(.*)(\.js|\.dbj):/)[1]; @@ -51,7 +50,6 @@ global.require = (function (include, isIncluded, print, notify) { directory = "../" + directory; // Add a extra recursive path, as we start out of the lib directory } - path = path || directory; let fullpath = removeRelativePath((path + field).replace(/\\/, "/")).toLowerCase(); @@ -67,11 +65,20 @@ global.require = (function (include, isIncluded, print, notify) { return modules[packageName] = File.open("libs/" + path + field, 0).readAllLines(); } - const moduleNameShort = (fullpath + ".js").match(/.*?\/(\w*).js$/)[1]; + let nameShort; + try { + nameShort = (fullpath + ".js").match(/.*?\/(\w*).js$/)[1]; + } catch (e) { + // file in libs folder same as us + nameShort = (fullpath + ".js").match(/.*?\/?(\w*).js$/)[0]; + } + const moduleNameShort = nameShort; if (!isIncluded(fullpath + ".js") && !modules.hasOwnProperty(moduleNameShort)) { - depth && notify && print("ÿc2Kolbotÿc0 :: - loading dependency of " + filename + ": " + moduleNameShort); - !depth && notify && print("ÿc2Kolbotÿc0 :: Loading module: " + moduleNameShort); + if (log) { + depth && notify && print("ÿc2Kolbotÿc0 :: - loading dependency of " + filename + ": " + moduleNameShort); + !depth && notify && print("ÿc2Kolbotÿc0 :: Loading module: " + moduleNameShort); + } let oldModule = Object.create(global["module"]); let oldExports = Object.create(global["exports"]); diff --git a/d2bs/kolbot/libs/bots/Abaddon.js b/d2bs/kolbot/libs/scripts/Abaddon.js similarity index 100% rename from d2bs/kolbot/libs/bots/Abaddon.js rename to d2bs/kolbot/libs/scripts/Abaddon.js diff --git a/d2bs/kolbot/libs/bots/AncientTunnels.js b/d2bs/kolbot/libs/scripts/AncientTunnels.js similarity index 100% rename from d2bs/kolbot/libs/bots/AncientTunnels.js rename to d2bs/kolbot/libs/scripts/AncientTunnels.js diff --git a/d2bs/kolbot/libs/bots/Andariel.js b/d2bs/kolbot/libs/scripts/Andariel.js similarity index 100% rename from d2bs/kolbot/libs/bots/Andariel.js rename to d2bs/kolbot/libs/scripts/Andariel.js diff --git a/d2bs/kolbot/libs/bots/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js similarity index 61% rename from d2bs/kolbot/libs/bots/AutoBaal.js rename to d2bs/kolbot/libs/scripts/AutoBaal.js index 8e59b86bb..c1bfb41a6 100644 --- a/d2bs/kolbot/libs/bots/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -12,17 +12,18 @@ * @todo: * - add silent follow support * - needs to be in a way that doesn't interfere with normal following +* - should this listen for baal death packet? */ function AutoBaal() { // internal variables - let i, baalCheck, throneCheck, hotCheck, leader; // internal variables + let baalCheck, throneCheck, hotCheck, leader; // internal variables const safeMsg = ["safe", "throne clear", "leechers can come", "tp is up", "1 clear"]; // safe message - casing doesn't matter const baalMsg = ["baal"]; // baal message - casing doesn't matter const hotMsg = ["hot", "warm", "dangerous", "lethal"]; // used for shrine hunt // chat event handler function, listen to what leader says - addEventListener("chatmsg", function (nick, msg) { + const chatEvent = function (nick, msg) { // filter leader messages if (nick === leader) { // loop through all predefined messages to find a match @@ -55,10 +56,12 @@ function AutoBaal() { } } } - }); + }; - // test - maybe factor this out and make it useable for other leecher scripts? - this.longRangeSupport = function () { + /** + * @todo maybe factor this out and make it useable for other leecher scripts? + */ + const longRangeSupport = function () { switch (me.classid) { case sdk.player.class.Necromancer: ClassAttack.raiseArmy(50); @@ -148,91 +151,97 @@ function AutoBaal() { if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), Time.seconds(1))) throw new Error("AutoBaal: Leader not partied"); } - Config.AutoBaal.FindShrine === 2 && (hotCheck = true); + try { + addEventListener("chatmsg", chatEvent); + Config.AutoBaal.FindShrine === 2 && (hotCheck = true); - Town.doChores(); - Town.move("portalspot"); + Town.doChores(); + Town.move("portalspot"); - // find the first player in throne of destruction - if (leader || (leader = Misc.autoLeaderDetect({destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area)}))) { - // do our stuff while partied - while (Misc.inMyParty(leader)) { - if (hotCheck) { - if (Config.AutoBaal.FindShrine) { - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); + // find the first player in throne of destruction + if (leader || (leader = Misc.autoLeaderDetect({destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area)}))) { + // do our stuff while partied + while (Misc.inMyParty(leader)) { + if (hotCheck) { + if (Config.AutoBaal.FindShrine) { + let i; + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); - for (i = sdk.areas.StonyField; i > 1; i--) { - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; + for (i = sdk.areas.StonyField; i > 1; i--) { + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } } - } - if (i === 1) { - Town.goToTown(); - Pather.useWaypoint(sdk.areas.DarkWood); + if (i === 1) { + Town.goToTown(); + Pather.useWaypoint(sdk.areas.DarkWood); - for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i++) { - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; + for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i++) { + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } } } } + + Town.goToTown(5); + Town.move("portalspot"); + + hotCheck = false; } - Town.goToTown(5); - Town.move("portalspot"); + // wait for throne signal - leader's safe message + if ((throneCheck || baalCheck) && me.inArea(sdk.areas.Harrogath)) { + print("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + // move to a safe spot + Pather.moveTo(Config.AutoBaal.LeechSpot[0], Config.AutoBaal.LeechSpot[1]); + Precast.doPrecast(true); + Town.getCorpse(); + } - hotCheck = false; - } + !baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport && this.longRangeSupport(); - // wait for throne signal - leader's safe message - if ((throneCheck || baalCheck) && me.inArea(sdk.areas.Harrogath)) { - print("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); - // move to a safe spot - Pather.moveTo(Config.AutoBaal.LeechSpot[0], Config.AutoBaal.LeechSpot[1]); - Precast.doPrecast(true); - Town.getCorpse(); - } + // wait for baal signal - leader's baal message + if (baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { + // move closer to chamber portal + Pather.moveTo(15092, 5010); + Precast.doPrecast(false); - !baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport && this.longRangeSupport(); + // wait for baal to go through the portal + while (Game.getMonster(sdk.monsters.ThroneBaal)) { + delay(500); + } - // wait for baal signal - leader's baal message - if (baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { - // move closer to chamber portal - Pather.moveTo(15092, 5010); - Precast.doPrecast(false); + let portal = Game.getObject(sdk.objects.WorldstonePortal); - // wait for baal to go through the portal - while (Game.getMonster(sdk.monsters.ThroneBaal)) { - delay(500); + delay(2000); // wait for others to enter first - helps with curses and tentacles from spawning around you + print("ÿc4AutoBaal: ÿc0Entering chamber."); + Pather.usePortal(null, null, portal) && Pather.moveTo(15166, 5903); // go to a safe position + Town.getCorpse(); } - let portal = Game.getObject(sdk.objects.WorldstonePortal); - - delay(2000); // wait for others to enter first - helps with curses and tentacles from spawning around you - print("ÿc4AutoBaal: ÿc0Entering chamber."); - Pather.usePortal(null, null, portal) && Pather.moveTo(15166, 5903); // go to a safe position - Town.getCorpse(); - } + let baal = Game.getMonster(sdk.monsters.Baal); - let baal = Game.getMonster(sdk.monsters.Baal); + if (baal) { + if (baal.dead) { + break; + } - if (baal) { - if (baal.dead) { - break; + longRangeSupport(); } - this.longRangeSupport(); - } + me.mode === sdk.player.mode.Dead && me.revive(); - me.mode === sdk.player.mode.Dead && me.revive(); - - delay(500); + delay(500); + } + } else { + throw new Error("Empty game."); } - } else { - throw new Error("Empty game."); + } finally { + removeEventListener("chatmsg", chatEvent); } return true; diff --git a/d2bs/kolbot/libs/bots/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js similarity index 98% rename from d2bs/kolbot/libs/bots/Baal.js rename to d2bs/kolbot/libs/scripts/Baal.js index 2e5c0270d..29b5e5f0f 100644 --- a/d2bs/kolbot/libs/bots/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -6,6 +6,7 @@ */ function Baal() { + include("core/Common/Baal.js"); this.announce = function () { let count, string, souls, dolls; let monster = Game.getMonster(); diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js new file mode 100644 index 000000000..a3d119064 --- /dev/null +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -0,0 +1,449 @@ +/** +* @filename BaalAssistant.js +* @author kolton, YGM, theBGuy +* @desc Help or Leech Baal Runs. +* +*/ + +/** + * @todo + * - combine autobaal, baalhelper, and baalassistant into one script + * - track leaders area so we can do silent follow + * - override Misc.getShrinesInArea to end when we recieve safeCheck message + */ + +function BaalAssistant () { + include("core/Common/Baal.js"); + let Leader = Config.Leader; + let Helper = Config.BaalAssistant.Helper; + let firstAttempt = true; + let [hotCheck, safeCheck, baalCheck, ngCheck, baalIsDead] = [false, false, false, false, false]; + let [ShrineStatus, secondAttempt, throneStatus, killTracker] = [false, false, false, false]; + + // convert all messages to lowercase + Config.BaalAssistant.HotTPMessage.forEach((msg, i) => { + Config.BaalAssistant.HotTPMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.SafeTPMessage.forEach((msg, i) => { + Config.BaalAssistant.SafeTPMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.BaalMessage.forEach((msg, i) => { + Config.BaalAssistant.BaalMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.NextGameMessage.forEach((msg, i) => { + Config.BaalAssistant.NextGameMessage[i] = msg.toLowerCase(); + }); + + const chatEvent = function (nick, msg) { + if (nick === Leader) { + msg = msg.toLowerCase(); + + for (let i = 0; i < Config.BaalAssistant.HotTPMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.HotTPMessage[i])) { + hotCheck = true; + break; + } + } + + for (let i = 0; i < Config.BaalAssistant.SafeTPMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.SafeTPMessage[i])) { + safeCheck = true; + break; + } + } + + for (let i = 0; i < Config.BaalAssistant.BaalMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.BaalMessage[i])) { + baalCheck = true; + break; + } + } + + for (let i = 0; i < Config.BaalAssistant.NextGameMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.NextGameMessage[i])) { + ngCheck = true; + killTracker = true; + break; + } + } + } + }; + + const baalDeathEvent = function (bytes = []) { + if (!bytes.length || bytes.length !== 2) return; + + if (bytes[0] === sdk.packets.recv.UniqueEvents && bytes[1] === 0x13) { + baalIsDead = true; + } + }; + + const checkParty = function () { + for (let i = 0; i < Config.BaalAssistant.Wait; i += 1) { + let partycheck = getParty(); + if (partycheck) { + do { + if (partycheck.area === sdk.areas.ThroneofDestruction) return false; + if (partycheck.area === sdk.areas.RiverofFlame || partycheck.area === sdk.areas.ChaosSanctuary) return true; + } while (partycheck.getNext()); + } + + delay(1000); + } + + return false; + }; + + // Start + const Worker = require("../modules/Worker"); + + if (Leader) { + if (!Misc.poll(() => Misc.inMyParty(Leader), Time.seconds(30), 1000)) throw new Error("BaalAssistant: Leader not partied"); + } + + try { + addEventListener("chatmsg", chatEvent); + + let killLeaderTracker = false; + if (!Leader && (Config.BaalAssistant.KillNihlathak || Config.BaalAssistant.FastChaos)) { + // run background auto detect so we don't miss messages while running add ons + let leadTick = getTickCount(); + + Worker.runInBackground.leaderTracker = function () { + if (killLeaderTracker || killTracker) return false; + // check every 3 seconds + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) return false; + + let party = getParty(); + + if (party) { + do { + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { + Leader = party.name; + console.log(sdk.colors.DarkGold + "Autodected " + Leader); + return false; + } + } while (party.getNext()); + } + + return true; + }; + } + + Config.BaalAssistant.KillNihlathak && Loader.runScript("Nihlathak"); + Config.BaalAssistant.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); + + Town.goToTown(5); + Town.doChores(); + + if (Leader + || (Leader = Misc.autoLeaderDetect({destination: sdk.areas.WorldstoneLvl3, quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area)})) + || (Leader = Misc.autoLeaderDetect({destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area)}))) { + print("ÿc hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); + + if (!hotCheck) { + print("ÿc1Leader didn't tell me to start hunting for an experience shrine."); + ShrineStatus = true; + } + } + + // don't waste time looking for seal if party is already killing baal, he'll be dead by the time we find one + if (!ShrineStatus && !baalCheck) { + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + let i; + + for (i = sdk.areas.StonyField; i > sdk.areas.RogueEncampment; i -= 1) { + if (safeCheck) { + break; + } + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } + } + + if (!safeCheck) { + if (i === sdk.areas.RogueEncampment) { + Town.goToTown(); + Pather.useWaypoint(sdk.areas.DarkWood); + Precast.doPrecast(true); + + for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i += 1) { + if (safeCheck) { + break; + } + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } + } + } + } + } + + Town.goToTown(5); + ShrineStatus = true; + } + + if (firstAttempt && !secondAttempt && !safeCheck && !baalCheck && !me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { + !!Config.RandomPrecast ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + } + + if (!me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { + if (Config.BaalAssistant.SkipTP) { + if (firstAttempt && !secondAttempt) { + !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) throw new Error("Failed to move to WSK3."); + + checkParty(); + let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); + entrance && Pather.moveTo(entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, entrance.y > me.y ? entrance.y - 5 : entrance.y + 5); + + if (!Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) || !Pather.moveTo(15118, 5002)) throw new Error("Failed to move to Throne of Destruction."); + + Pather.moveTo(15095, 5029); + + if ((Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { + print("Burning Souls or Undead Soul Killers found, ending script."); + return true; + } + + Pather.moveTo(15118, 5002); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + + secondAttempt = true; + safeCheck = true; + } else { + if (me.inTown) { + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + } + } + } else { + if (firstAttempt && !secondAttempt) { + !me.inArea(sdk.areas.Harrogath) && Pather.useWaypoint(sdk.areas.Harrogath); + Town.move("portalspot"); + + if (Config.BaalAssistant.WaitForSafeTP && !Misc.poll(() => safeCheck, Time.seconds(Config.BaalAssistant.Wait), 1000)) { + throw new Error("No safe TP message."); + } + + if (!Misc.poll(() => Pather.usePortal(sdk.areas.ThroneofDestruction, null), Time.seconds(Config.BaalAssistant.Wait), 1000)) { + throw new Error("No portals to Throne."); + } + + if ((Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { + throw new Error("Burning Souls or Undead Soul Killers found, ending script."); + } + + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + secondAttempt = true; + safeCheck = true; + } else { + if (me.inTown) { + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + } + } + } + } + + if (safeCheck && !baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { + if (!baalCheck && !throneStatus) { + if (Helper) { + Attack.clear(15); + Common.Baal.clearThrone(); + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + Precast.doPrecast(true); + } + + let tick = getTickCount(); + + MainLoop: while (true) { + if (Helper) { + if (getDistance(me, 15094, me.paladin ? 5029 : 5038) > 3) { + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + } + } + + if (!Game.getMonster(sdk.monsters.ThroneBaal)) { + break; + } + + switch (Common.Baal.checkThrone(Helper)) { + case 1: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 2: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 4: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 3: + Helper && Attack.clear(40) && Common.Baal.checkHydra(); + tick = getTickCount(); + + break; + case 5: + if (Helper) { + Attack.clear(40); + } else { + while ([sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2] + .map((unitId) => Game.getMonster(unitId)) + .filter(Boolean).some((unit) => unit.attackable)) { + delay(1000); + } + + delay(1000); + } + + break MainLoop; + default: + if (getTickCount() - tick < 7e3) { + if (me.paladin && me.getState(sdk.states.Poison) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + break; + } + } + + if (Helper && !Common.Baal.preattack()) { + delay(100); + } + + break; + } + delay(10); + } + throneStatus = true; + baalCheck = true; + } + } + + if ((throneStatus || baalCheck) && Config.BaalAssistant.KillBaal && me.inArea(sdk.areas.ThroneofDestruction)) { + Helper ? Pather.moveTo(15090, 5008) && delay(2000) : Pather.moveTo(15090, 5010); + Precast.doPrecast(true); + !Helper && addEventListener("gamepacket", baalDeathEvent); + + while (Game.getMonster(sdk.monsters.ThroneBaal)) { + delay(500); + } + + let portal = Game.getObject(sdk.objects.WorldstonePortal); + + if (portal) { + delay((Helper ? 1000 : 4000)); + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); + } + + if (Helper) { + delay(1000); + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); + } else { + Pather.moveTo(15177, 5952); + let baal = Game.getMonster(sdk.monsters.Baal); + + while (!!baal && baal.attackable && !baalIsDead) { + delay(1000); + } + } + + } else { + // how to accurately know when to end script in the instance of no ngCheck + // listen for baal death packet maybe? + while (!ngCheck && !baalIsDead) { + delay(500); + } + } + + delay(500); + } + } catch (e) { + console.error(e); + } finally { + killTracker = true; + } + } else { + throw new Error("Empty game."); + } + } finally { + removeEventListener("chatmsg", chatEvent); + removeEventListener("gamepacket", baalDeathEvent); + } + + return true; +} diff --git a/d2bs/kolbot/libs/bots/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js similarity index 98% rename from d2bs/kolbot/libs/bots/BaalHelper.js rename to d2bs/kolbot/libs/scripts/BaalHelper.js index 61f8a8241..ecb038075 100644 --- a/d2bs/kolbot/libs/bots/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -6,6 +6,7 @@ */ function BaalHelper() { + include("core/Common/Baal.js"); Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); diff --git a/d2bs/kolbot/libs/bots/BattleOrders.js b/d2bs/kolbot/libs/scripts/BattleOrders.js similarity index 100% rename from d2bs/kolbot/libs/bots/BattleOrders.js rename to d2bs/kolbot/libs/scripts/BattleOrders.js diff --git a/d2bs/kolbot/libs/bots/BattlemaidSarina.js b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js similarity index 100% rename from d2bs/kolbot/libs/bots/BattlemaidSarina.js rename to d2bs/kolbot/libs/scripts/BattlemaidSarina.js diff --git a/d2bs/kolbot/libs/bots/Bishibosh.js b/d2bs/kolbot/libs/scripts/Bishibosh.js similarity index 100% rename from d2bs/kolbot/libs/bots/Bishibosh.js rename to d2bs/kolbot/libs/scripts/Bishibosh.js diff --git a/d2bs/kolbot/libs/bots/BoBarbHelper.js b/d2bs/kolbot/libs/scripts/BoBarbHelper.js similarity index 96% rename from d2bs/kolbot/libs/bots/BoBarbHelper.js rename to d2bs/kolbot/libs/scripts/BoBarbHelper.js index 94b3050a6..9e2fa3e9c 100644 --- a/d2bs/kolbot/libs/bots/BoBarbHelper.js +++ b/d2bs/kolbot/libs/scripts/BoBarbHelper.js @@ -77,7 +77,7 @@ function BoBarbHelper () { } catch (e) { showConsole(); print("Failed to move to BO WP"); - print("make sure I have " + Pather.getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); + print("make sure I have " + getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); delay(20000); return true; diff --git a/d2bs/kolbot/libs/bots/BoneAsh.js b/d2bs/kolbot/libs/scripts/BoneAsh.js similarity index 100% rename from d2bs/kolbot/libs/bots/BoneAsh.js rename to d2bs/kolbot/libs/scripts/BoneAsh.js diff --git a/d2bs/kolbot/libs/bots/Bonesaw.js b/d2bs/kolbot/libs/scripts/Bonesaw.js similarity index 100% rename from d2bs/kolbot/libs/bots/Bonesaw.js rename to d2bs/kolbot/libs/scripts/Bonesaw.js diff --git a/d2bs/kolbot/libs/bots/ChestMania.js b/d2bs/kolbot/libs/scripts/ChestMania.js similarity index 100% rename from d2bs/kolbot/libs/bots/ChestMania.js rename to d2bs/kolbot/libs/scripts/ChestMania.js diff --git a/d2bs/kolbot/libs/bots/ClassicChaosAssistant.js b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js similarity index 98% rename from d2bs/kolbot/libs/bots/ClassicChaosAssistant.js rename to d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js index 03759b18a..23a1043a0 100644 --- a/d2bs/kolbot/libs/bots/ClassicChaosAssistant.js +++ b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js @@ -8,6 +8,7 @@ // redo this, maybe different keys or chat commands instead? function ClassicChaosAssistant() { + include("core/Common/Diablo.js"); let stargo, infgo, seisgo, vizgo, infseal, seisseal, vizseal, diablopickup, normalpickup = false; addEventListener("keyup", diff --git a/d2bs/kolbot/libs/bots/ClearAnyArea.js b/d2bs/kolbot/libs/scripts/ClearAnyArea.js similarity index 100% rename from d2bs/kolbot/libs/bots/ClearAnyArea.js rename to d2bs/kolbot/libs/scripts/ClearAnyArea.js diff --git a/d2bs/kolbot/libs/bots/Coldcrow.js b/d2bs/kolbot/libs/scripts/Coldcrow.js similarity index 100% rename from d2bs/kolbot/libs/bots/Coldcrow.js rename to d2bs/kolbot/libs/scripts/Coldcrow.js diff --git a/d2bs/kolbot/libs/bots/Coldworm.js b/d2bs/kolbot/libs/scripts/Coldworm.js similarity index 100% rename from d2bs/kolbot/libs/bots/Coldworm.js rename to d2bs/kolbot/libs/scripts/Coldworm.js diff --git a/d2bs/kolbot/libs/bots/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js similarity index 99% rename from d2bs/kolbot/libs/bots/ControlBot.js rename to d2bs/kolbot/libs/scripts/ControlBot.js index abf1d29a6..ab15c8222 100644 --- a/d2bs/kolbot/libs/bots/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -429,7 +429,7 @@ function ControlBot() { Pather.useWaypoint(wpList[i], true); Config.ControlBot.Wps.SecurePortal && Attack.securePosition(me.x, me.y, 20, 1000); Pather.makePortal(); - say(Pather.getAreaName(me.area) + " TP up"); + say(getAreaName(me.area) + " TP up"); for (let timeout = 0; timeout < 20; timeout++) { if (Game.getPlayer(nick)) { diff --git a/d2bs/kolbot/libs/bots/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js similarity index 100% rename from d2bs/kolbot/libs/bots/Corpsefire.js rename to d2bs/kolbot/libs/scripts/Corpsefire.js diff --git a/d2bs/kolbot/libs/bots/Countess.js b/d2bs/kolbot/libs/scripts/Countess.js similarity index 100% rename from d2bs/kolbot/libs/bots/Countess.js rename to d2bs/kolbot/libs/scripts/Countess.js diff --git a/d2bs/kolbot/libs/bots/Cows.js b/d2bs/kolbot/libs/scripts/Cows.js similarity index 94% rename from d2bs/kolbot/libs/bots/Cows.js rename to d2bs/kolbot/libs/scripts/Cows.js index 0d5eb2f90..8ac0e3a11 100644 --- a/d2bs/kolbot/libs/bots/Cows.js +++ b/d2bs/kolbot/libs/scripts/Cows.js @@ -6,7 +6,9 @@ */ function Cows() { - this.getLeg = function () { + include("core/Common/Cows.js"); + + const getLeg = function () { if (me.wirtsleg) return me.wirtsleg; Pather.useWaypoint(sdk.areas.StonyField); @@ -43,7 +45,7 @@ function Cows() { throw new Error("Failed to get the leg"); }; - this.getTome = function () { + const getTome = function () { let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); if (tpTome.length < 2) { @@ -80,7 +82,7 @@ function Cows() { return tpTome.last(); }; - this.openPortal = function (leg, tome) { + const openPortal = function (leg, tome) { if (!Town.openStash()) throw new Error("Failed to open stash"); if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); if (!Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome) || !Cubing.openCube()) { @@ -117,9 +119,9 @@ function Cows() { if (!me.tristram) throw new Error("Cain quest incomplete"); if (me.cows) throw new Error("Already killed the Cow King."); - let leg = this.getLeg(); - let tome = this.getTome(); - this.openPortal(leg, tome); + let leg = getLeg(); + let tome = getTome(); + openPortal(leg, tome); } } catch (e) { typeof e === "object" && e.message && e.message !== "NOT PORTAL MAKER" && console.error(e); diff --git a/d2bs/kolbot/libs/bots/Crafting.js b/d2bs/kolbot/libs/scripts/Crafting.js similarity index 100% rename from d2bs/kolbot/libs/bots/Crafting.js rename to d2bs/kolbot/libs/scripts/Crafting.js diff --git a/d2bs/kolbot/libs/bots/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js similarity index 100% rename from d2bs/kolbot/libs/bots/CreepingFeature.js rename to d2bs/kolbot/libs/scripts/CreepingFeature.js diff --git a/d2bs/kolbot/libs/bots/CrushTele.js b/d2bs/kolbot/libs/scripts/CrushTele.js similarity index 100% rename from d2bs/kolbot/libs/bots/CrushTele.js rename to d2bs/kolbot/libs/scripts/CrushTele.js diff --git a/d2bs/kolbot/libs/bots/DeveloperMode.js b/d2bs/kolbot/libs/scripts/DeveloperMode.js similarity index 88% rename from d2bs/kolbot/libs/bots/DeveloperMode.js rename to d2bs/kolbot/libs/scripts/DeveloperMode.js index 14ef59b9a..85783ad6d 100644 --- a/d2bs/kolbot/libs/bots/DeveloperMode.js +++ b/d2bs/kolbot/libs/scripts/DeveloperMode.js @@ -4,13 +4,11 @@ * @desc developer mode made easy - run commands or scripts from chat commands. View packets. See unit info * */ -include("UnitInfo.js"); function DeveloperMode() { - let done = false, action = false, command = false, userAddon = false, test = false; - let watchSent = [], watchRecv = [], blockSent = [], blockRecv = []; - let unitInfo; - let runCommand = function (msg) { + 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]; @@ -170,7 +168,7 @@ function DeveloperMode() { }; // Received packet handler - let packetReceived = function(pBytes) { + const packetReceived = function(pBytes) { let ID = pBytes[0].toString(16); // Block received packets from list @@ -187,7 +185,7 @@ function DeveloperMode() { }; // Sent packet handler - let packetSent = function (pBytes) { + const packetSent = function (pBytes) { let ID = pBytes[0].toString(16); // Block all commands or irc chat from being sent to server @@ -219,7 +217,8 @@ function DeveloperMode() { return false; }; - const copiedConfig = Misc.copy(Config); + const copiedConfig = copyObj(Config); + const UnitInfo = new (require("../modules/UnitInfo")); try { console.log("starting developermode"); @@ -230,14 +229,11 @@ function DeveloperMode() { while (!done) { if (action) { - includeIfNotIncluded("bots/" + action + ".js"); + includeIfNotIncluded("scripts/" + action + ".js"); - if (!UnitInfo.cleared) { - UnitInfo.remove(); - userAddon = false; - } + UnitInfo.check(); - if (isIncluded("bots/" + action + ".js")) { + if (isIncluded("scripts/" + action + ".js")) { try { Loader.runScript(action); } catch (e) { @@ -252,10 +248,7 @@ function DeveloperMode() { } if (command) { - if (!UnitInfo.cleared) { - UnitInfo.remove(); - userAddon = false; - } + UnitInfo.check(); try { eval(command); @@ -268,9 +261,7 @@ function DeveloperMode() { } if (userAddon) { - !UnitInfo.cleared && !Game.getSelectedUnit() && UnitInfo.remove(); - unitInfo = Game.getSelectedUnit(); - UnitInfo.createInfo(unitInfo); + UnitInfo.createInfo(Game.getSelectedUnit()); } if (test) { @@ -284,6 +275,7 @@ function DeveloperMode() { removeEventListener("gamepacketsent", packetSent); removeEventListener("gamepacket", packetReceived); Config = copiedConfig; + UnitInfo.remove(); } return true; diff --git a/d2bs/kolbot/libs/bots/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js similarity index 98% rename from d2bs/kolbot/libs/bots/Diablo.js rename to d2bs/kolbot/libs/scripts/Diablo.js index 397115f5e..823692655 100644 --- a/d2bs/kolbot/libs/bots/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -10,6 +10,7 @@ */ function Diablo() { + include("core/Common/Diablo.js"); Pather._teleport = Pather.teleport; Common.Diablo.clearRadius = Config.Diablo.ClearRadius; diff --git a/d2bs/kolbot/libs/bots/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js similarity index 99% rename from d2bs/kolbot/libs/bots/DiabloHelper.js rename to d2bs/kolbot/libs/scripts/DiabloHelper.js index 474166dcf..c0ae47858 100644 --- a/d2bs/kolbot/libs/bots/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -6,6 +6,7 @@ */ function DiabloHelper() { + include("core/Common/Diablo.js"); this.Leader = Config.Leader; Common.Diablo.waitForGlow = true; Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; diff --git a/d2bs/kolbot/libs/bots/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js similarity index 100% rename from d2bs/kolbot/libs/bots/Duriel.js rename to d2bs/kolbot/libs/scripts/Duriel.js diff --git a/d2bs/kolbot/libs/bots/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js similarity index 100% rename from d2bs/kolbot/libs/bots/Eldritch.js rename to d2bs/kolbot/libs/scripts/Eldritch.js diff --git a/d2bs/kolbot/libs/bots/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js similarity index 100% rename from d2bs/kolbot/libs/bots/Endugu.js rename to d2bs/kolbot/libs/scripts/Endugu.js diff --git a/d2bs/kolbot/libs/bots/Eyeback.js b/d2bs/kolbot/libs/scripts/Eyeback.js similarity index 100% rename from d2bs/kolbot/libs/bots/Eyeback.js rename to d2bs/kolbot/libs/scripts/Eyeback.js diff --git a/d2bs/kolbot/libs/bots/Fangskin.js b/d2bs/kolbot/libs/scripts/Fangskin.js similarity index 100% rename from d2bs/kolbot/libs/bots/Fangskin.js rename to d2bs/kolbot/libs/scripts/Fangskin.js diff --git a/d2bs/kolbot/libs/bots/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js similarity index 99% rename from d2bs/kolbot/libs/bots/Follower.js rename to d2bs/kolbot/libs/scripts/Follower.js index dde2aee5d..ab96136c7 100644 --- a/d2bs/kolbot/libs/bots/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -624,7 +624,7 @@ function Follower() { break; case me.name + " tp": - unit = Town.getTpTool(); + unit = me.getTpTool(); if (unit) { unit.interact(); diff --git a/d2bs/kolbot/libs/bots/Frozenstein.js b/d2bs/kolbot/libs/scripts/Frozenstein.js similarity index 100% rename from d2bs/kolbot/libs/bots/Frozenstein.js rename to d2bs/kolbot/libs/scripts/Frozenstein.js diff --git a/d2bs/kolbot/libs/bots/Gamble.js b/d2bs/kolbot/libs/scripts/Gamble.js similarity index 100% rename from d2bs/kolbot/libs/bots/Gamble.js rename to d2bs/kolbot/libs/scripts/Gamble.js diff --git a/d2bs/kolbot/libs/bots/GemHunter.js b/d2bs/kolbot/libs/scripts/GemHunter.js similarity index 83% rename from d2bs/kolbot/libs/bots/GemHunter.js rename to d2bs/kolbot/libs/scripts/GemHunter.js index 6a8b391ad..dc5e4e4bb 100644 --- a/d2bs/kolbot/libs/bots/GemHunter.js +++ b/d2bs/kolbot/libs/scripts/GemHunter.js @@ -10,6 +10,7 @@ * - Call check if we have an gems to upgrade in the stash instead of always keep some in invo as that takes up space. * If we do, go get the gem from the stash before activating shrine. * - We should also then keep track of where the shrine was, (I don't remember if gem shrines regen, so check this) + * - Take into account the next area and sort the shrines to bring us to the exit if its connected */ function GemHunter () { Town.doChores(); @@ -21,15 +22,16 @@ function GemHunter () { for (let i = 0; i < Config.GemHunter.AreaList.length; i++) { if (Town.getGemsInInv().length > 0) { - print("ÿc4GemHunterÿc0: Moving to " + Pather.getAreaName(Config.GemHunter.AreaList[i])); + print("ÿc4GemHunterÿc0: Moving to " + getAreaName(Config.GemHunter.AreaList[i])); Pather.journeyTo(Config.GemHunter.AreaList[i]); - if (i === 0) Precast.doPrecast(true); + if (i % 2 === 0) Precast.doPrecast(true); if (Misc.getShrinesInArea(Config.GemHunter.AreaList[i], sdk.shrines.Gem, true)) { Pickit.pickItems(); print("ÿc4GemHunterÿc0: found a gem Shrine"); if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { print("ÿc4GemHunterÿc0: Getting a new Gem in Town."); Town.visitTown(); // Go to Town and do chores. Will throw an error if it fails to return from Town. + Town.getGem(); } } } diff --git a/d2bs/kolbot/libs/bots/GetKeys.js b/d2bs/kolbot/libs/scripts/GetKeys.js similarity index 100% rename from d2bs/kolbot/libs/bots/GetKeys.js rename to d2bs/kolbot/libs/scripts/GetKeys.js diff --git a/d2bs/kolbot/libs/bots/GhostBusters.js b/d2bs/kolbot/libs/scripts/GhostBusters.js similarity index 100% rename from d2bs/kolbot/libs/bots/GhostBusters.js rename to d2bs/kolbot/libs/scripts/GhostBusters.js diff --git a/d2bs/kolbot/libs/bots/Hephasto.js b/d2bs/kolbot/libs/scripts/Hephasto.js similarity index 100% rename from d2bs/kolbot/libs/bots/Hephasto.js rename to d2bs/kolbot/libs/scripts/Hephasto.js diff --git a/d2bs/kolbot/libs/bots/IPHunter.js b/d2bs/kolbot/libs/scripts/IPHunter.js similarity index 100% rename from d2bs/kolbot/libs/bots/IPHunter.js rename to d2bs/kolbot/libs/scripts/IPHunter.js diff --git a/d2bs/kolbot/libs/bots/Icehawk.js b/d2bs/kolbot/libs/scripts/Icehawk.js similarity index 100% rename from d2bs/kolbot/libs/bots/Icehawk.js rename to d2bs/kolbot/libs/scripts/Icehawk.js diff --git a/d2bs/kolbot/libs/bots/Izual.js b/d2bs/kolbot/libs/scripts/Izual.js similarity index 100% rename from d2bs/kolbot/libs/bots/Izual.js rename to d2bs/kolbot/libs/scripts/Izual.js diff --git a/d2bs/kolbot/libs/bots/KillDclone.js b/d2bs/kolbot/libs/scripts/KillDclone.js similarity index 100% rename from d2bs/kolbot/libs/bots/KillDclone.js rename to d2bs/kolbot/libs/scripts/KillDclone.js diff --git a/d2bs/kolbot/libs/bots/KurastTemples.js b/d2bs/kolbot/libs/scripts/KurastTemples.js similarity index 100% rename from d2bs/kolbot/libs/bots/KurastTemples.js rename to d2bs/kolbot/libs/scripts/KurastTemples.js diff --git a/d2bs/kolbot/libs/bots/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js similarity index 99% rename from d2bs/kolbot/libs/bots/MFHelper.js rename to d2bs/kolbot/libs/scripts/MFHelper.js index a8555d1d6..d8c9cb095 100644 --- a/d2bs/kolbot/libs/bots/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -114,6 +114,7 @@ function MFHelper() { Town.goToTown(1) && Pather.usePortal(sdk.areas.MooMooFarm); return me.inArea(sdk.areas.MooMooFarm); }, Time.minutes(1), 500 + me.ping)) { + include("core/Common/Cows.js"); Precast.doPrecast(false); Common.Cows.clearCowLevel(); delay(1000); diff --git a/d2bs/kolbot/libs/bots/Mausoleum.js b/d2bs/kolbot/libs/scripts/Mausoleum.js similarity index 100% rename from d2bs/kolbot/libs/bots/Mausoleum.js rename to d2bs/kolbot/libs/scripts/Mausoleum.js diff --git a/d2bs/kolbot/libs/bots/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js similarity index 100% rename from d2bs/kolbot/libs/bots/Mephisto.js rename to d2bs/kolbot/libs/scripts/Mephisto.js diff --git a/d2bs/kolbot/libs/bots/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js similarity index 100% rename from d2bs/kolbot/libs/bots/Nihlathak.js rename to d2bs/kolbot/libs/scripts/Nihlathak.js diff --git a/d2bs/kolbot/libs/bots/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js similarity index 99% rename from d2bs/kolbot/libs/bots/OrgTorch.js rename to d2bs/kolbot/libs/scripts/OrgTorch.js index 43ad3ac3c..76d025639 100644 --- a/d2bs/kolbot/libs/bots/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -62,7 +62,7 @@ function OrgTorch () { let id = item.classid; let canFit = Storage.Inventory.CanFit(item); if (!canFit && Pickit.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Pickit.itemColor(item) + item.name); + console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); Town.visitTown(); !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); } diff --git a/d2bs/kolbot/libs/bots/OuterSteppes.js b/d2bs/kolbot/libs/scripts/OuterSteppes.js similarity index 100% rename from d2bs/kolbot/libs/bots/OuterSteppes.js rename to d2bs/kolbot/libs/scripts/OuterSteppes.js diff --git a/d2bs/kolbot/libs/bots/Pindleskin.js b/d2bs/kolbot/libs/scripts/Pindleskin.js similarity index 100% rename from d2bs/kolbot/libs/bots/Pindleskin.js rename to d2bs/kolbot/libs/scripts/Pindleskin.js diff --git a/d2bs/kolbot/libs/bots/Pit.js b/d2bs/kolbot/libs/scripts/Pit.js similarity index 100% rename from d2bs/kolbot/libs/bots/Pit.js rename to d2bs/kolbot/libs/scripts/Pit.js diff --git a/d2bs/kolbot/libs/bots/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js similarity index 76% rename from d2bs/kolbot/libs/bots/Questing.js rename to d2bs/kolbot/libs/scripts/Questing.js index 86cac4dd2..79210d3a6 100644 --- a/d2bs/kolbot/libs/bots/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -18,7 +18,7 @@ function Questing () { let id = item.classid; let canFit = Storage.Inventory.CanFit(item); if (!canFit && Pickit.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Pickit.itemColor(item) + item.name); + console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); Town.visitTown(); !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); } @@ -26,23 +26,7 @@ function Questing () { return Pickit.pickItem(item); }; - let quests = [ - [sdk.quest.id.DenofEvil, "den"], - [sdk.quest.id.ToolsoftheTrade, "smith"], - [sdk.quest.id.TheSearchForCain, "cain"], - [sdk.quest.id.SistersToTheSlaughter, "andy"], - [sdk.quest.id.RadamentsLair, "radament"], - [sdk.quest.id.LamEsensTome, "lamEssen"], - [sdk.quest.id.TheFallenAngel, "izual"], - [sdk.quest.id.TerrorsEnd, "diablo"], - [sdk.quest.id.SiegeOnHarrogath, "shenk"], - [sdk.quest.id.RescueonMountArreat, "barbs"], - [sdk.quest.id.PrisonofIce, "anya"], - [sdk.quest.id.RiteofPassage, "ancients"], - [sdk.quest.id.EyeofDestruction, "baal"] - ]; - - this.den = function () { + const den = function () { log("starting den"); if (!Town.goToTown(1) || !Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true)) { @@ -57,24 +41,17 @@ function Questing () { return true; }; - this.smith = function () { + const smith = function () { if (Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) return true; log("starting smith"); - if (!Loader.runScript("Smith")) throw new Error(); + include("core/Common/Smith.js"); - let malusChest = Game.getObject(sdk.quest.chest.MalusHolder); - !!malusChest && malusChest.distance > 5 && Pather.moveToUnit(malusChest); - Misc.openChest(malusChest); - let malus = Misc.poll(() => Game.getItem(sdk.quest.item.HoradricMalus), 1000, 100); - getQuestItem(malus); - Town.goToTown(); - Town.npcInteract("Charsi"); - - return !!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete); + return Common.Smith(); }; - this.cain = function () { + const cain = function () { + include("core/Common/Cain.js"); log("starting cain"); Town.doChores(); @@ -83,7 +60,7 @@ function Questing () { return true; }; - this.andy = function () { + const andy = function () { log("starting andy"); Town.doChores(); @@ -124,7 +101,7 @@ function Questing () { return true; }; - this.radament = function () { + const radament = function () { if (!Pather.accessToAct(2)) return false; log("starting radament"); @@ -150,7 +127,7 @@ function Questing () { return true; }; - this.lamEssen = function () { + const lamEssen = function () { if (!Pather.accessToAct(3)) return false; log("starting lam essen"); @@ -174,7 +151,7 @@ function Questing () { return true; }; - this.izual = function () { + const izual = function () { if (!Pather.accessToAct(4)) return false; log("starting izual"); @@ -185,7 +162,7 @@ function Questing () { return true; }; - this.diablo = function () { + const diablo = function () { if (!Pather.accessToAct(4)) return false; if (Misc.checkQuest(sdk.quest.id.TerrorsEnd, sdk.quest.states.Completed)) return true; @@ -200,7 +177,7 @@ function Questing () { return true; }; - this.shenk = function () { + const shenk = function () { if (!Pather.accessToAct(5)) return false; if (Misc.checkQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) return true; @@ -218,7 +195,7 @@ function Questing () { return true; }; - this.barbs = function () { + const barbs = function () { if (!Pather.accessToAct(5)) return false; log("starting barb rescue"); @@ -262,7 +239,7 @@ function Questing () { return !!Misc.checkQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); }; - this.anya = function () { + const anya = function () { if (!Pather.accessToAct(5)) return false; if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) return true; @@ -282,11 +259,13 @@ function Questing () { let anya = Game.getObject(sdk.objects.FrozenAnya); - // talk to anya, then cancel her boring speech - Pather.moveToUnit(anya); - Packet.entityInteract(anya); - delay(300); - me.cancel(); + // talk to anya, then cancel her speech + if (anya) { + Pather.moveToUnit(anya); + Packet.entityInteract(anya); + Misc.poll(() => getIsTalkingNPC(), 2000, 50); + me.cancel(); + } // get pot from malah, then return to anya Town.goToTown(); @@ -297,10 +276,12 @@ function Questing () { }, Time.seconds(30), 1000)) throw new Error("Anya quest failed - Failed to return to frozen river"); // unfreeze her, cancel her speech again - anya = Game.getObject(sdk.objects.FrozenAnya); - anya.interact(); - delay(1000); - me.cancel(); + if (anya) { + Pather.moveToUnit(anya, 1, 2); + Packet.entityInteract(anya); + Misc.poll(() => getIsTalkingNPC() || anya.mode, 2000, 50); + me.cancel() && me.cancel(); + } // get reward Town.goToTown(); @@ -313,7 +294,8 @@ function Questing () { }; // @theBGuy - this.ancients = function () { + const ancients = function () { + include("core/Common/Ancients.js"); Town.doChores(); log("starting ancients"); @@ -328,8 +310,8 @@ function Questing () { Town.doChores(); [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion].forEach(p => Town.buyPots(10, p, true)); - let tempConfig = Misc.copy(Config); // save and update config settings - let townChicken = getScript("tools/townchicken.js"); + let tempConfig = copyObj(Config); // save and update config settings + let townChicken = getScript("threads/townchicken.js"); townChicken && townChicken.running && townChicken.stop(); Config.TownCheck = false; @@ -365,8 +347,8 @@ function Questing () { Precast.doPrecast(true); // reload town chicken in case we are doing others scripts after this one finishes - let townChick = getScript("tools/TownChicken.js"); - (Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick) && load("tools/TownChicken.js"); + let townChick = getScript("threads/TownChicken.js"); + (Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick) && load("threads/TownChicken.js"); try { if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { @@ -380,7 +362,7 @@ function Questing () { return true; }; - this.baal = function () { + const baal = function () { log("starting baal"); // just run baal script? I mean why re-invent the wheel here Loader.runScript("Baal"); @@ -389,18 +371,78 @@ function Questing () { return true; }; - let didTask = false; + const index = { + "den": { + id: sdk.quest.id.DenofEvil, + run: () => den(), + }, + "smith": { + id: sdk.quest.id.ToolsoftheTrade, + run: () => smith(), + }, + "cain": { + id: sdk.quest.id.TheSearchForCain, + run: () => cain(), + }, + "andy": { + id: sdk.quest.id.SistersToTheSlaughter, + run: () => andy(), + }, + "radament": { + id: sdk.quest.id.RadamentsLair, + run: () => radament(), + }, + "lamEssen": { + id: sdk.quest.id.LamEsensTome, + run: () => lamEssen(), + }, + "izual": { + id: sdk.quest.id.TheFallenAngel, + run: () => izual(), + }, + "diablo": { + id: sdk.quest.id.TerrorsEnd, + run: () => diablo(), + }, + "shenk": { + id: sdk.quest.id.SiegeOnHarrogath, + run: () => shenk(), + }, + "barbs": { + id: sdk.quest.id.RescueonMountArreat, + run: () => barbs(), + }, + "anya": { + id: sdk.quest.id.PrisonofIce, + run: () => anya(), + }, + "ancients": { + id: sdk.quest.id.RiteofPassage, + run: () => ancients(), + }, + "baal": { + id: sdk.quest.id.EyeofDestruction, + run: () => baal(), + }, + }; + + Object.defineProperty(index, "complete", { + value: function () { + return !!Misc.checkQuest(this.id, sdk.quest.states.Completed); + } + }); + let didTask = false; me.inTown && Town.doChores(); - for (let i = 0; i < quests.length; i += 1) { + Object.keys(index).forEach(quest => { didTask && me.inTown && Town.doChores(); let j; for (j = 0; j < 3; j += 1) { - if (!Misc.checkQuest(quests[i][0], sdk.quest.states.Completed)) { + if (!index[quest].complete()) { try { - if (this[quests[i][1]]()) { + if (index[quest].run()) { didTask = true; break; @@ -415,8 +457,8 @@ function Questing () { } } - j === 3 && D2Bot.printToConsole("Questing :: " + quests[i][1] + " quest failed.", sdk.colors.D2Bot.Red); - } + j === 3 && D2Bot.printToConsole("Questing :: " + quest + " quest failed.", sdk.colors.D2Bot.Red); + }); if (Config.Questing.StopProfile || Loader.scriptList.length === 1) { D2Bot.printToConsole("All quests done. Stopping profile.", sdk.colors.D2Bot.Green); @@ -424,8 +466,8 @@ function Questing () { } else { log("ÿc9(Questing) :: ÿc2Complete"); // reload town chicken in case we are doing others scripts after this one finishes - let townChick = getScript("tools/TownChicken.js"); - (Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick) && load("tools/TownChicken.js"); + let townChick = getScript("threads/TownChicken.js"); + (Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick) && load("threads/TownChicken.js"); } return true; diff --git a/d2bs/kolbot/libs/bots/Radament.js b/d2bs/kolbot/libs/scripts/Radament.js similarity index 100% rename from d2bs/kolbot/libs/bots/Radament.js rename to d2bs/kolbot/libs/scripts/Radament.js diff --git a/d2bs/kolbot/libs/bots/Rakanishu.js b/d2bs/kolbot/libs/scripts/Rakanishu.js similarity index 100% rename from d2bs/kolbot/libs/bots/Rakanishu.js rename to d2bs/kolbot/libs/scripts/Rakanishu.js diff --git a/d2bs/kolbot/libs/bots/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js similarity index 99% rename from d2bs/kolbot/libs/bots/Rushee.js rename to d2bs/kolbot/libs/scripts/Rushee.js index 154749a02..b6da89e0a 100644 --- a/d2bs/kolbot/libs/bots/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -496,7 +496,7 @@ function Rushee() { // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester if (this.nonQuesterNPCTalk) { - console.debug("Leader Area: " + Pather.getAreaName(leader.area)); + console.debug("Leader Area: " + getAreaName(leader.area)); switch (leader.area) { case sdk.areas.ClawViperTempleLvl2: diff --git a/d2bs/kolbot/libs/bots/Rusher.js b/d2bs/kolbot/libs/scripts/Rusher.js similarity index 95% rename from d2bs/kolbot/libs/bots/Rusher.js rename to d2bs/kolbot/libs/scripts/Rusher.js index a546abc16..175238450 100644 --- a/d2bs/kolbot/libs/bots/Rusher.js +++ b/d2bs/kolbot/libs/scripts/Rusher.js @@ -15,7 +15,7 @@ */ function Rusher() { - load("tools/rushthread.js"); + load("threads/rushthread.js"); delay(500); let i, command, master, commandSplit0; @@ -24,16 +24,16 @@ function Rusher() { "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" ]; - let rushThread = getScript("tools/rushthread.js"); + let rushThread = getScript("threads/rushthread.js"); this.reloadThread = function () { - rushThread = getScript("tools/rushthread.js"); + rushThread = getScript("threads/rushthread.js"); rushThread && rushThread.stop(); delay(500); - load("tools/rushthread.js"); + load("threads/rushthread.js"); - rushThread = getScript("tools/rushthread.js"); + rushThread = getScript("threads/rushthread.js"); delay(500); }; diff --git a/d2bs/kolbot/libs/bots/SealLeecher.js b/d2bs/kolbot/libs/scripts/SealLeecher.js similarity index 100% rename from d2bs/kolbot/libs/bots/SealLeecher.js rename to d2bs/kolbot/libs/scripts/SealLeecher.js diff --git a/d2bs/kolbot/libs/bots/SharpTooth.js b/d2bs/kolbot/libs/scripts/SharpTooth.js similarity index 100% rename from d2bs/kolbot/libs/bots/SharpTooth.js rename to d2bs/kolbot/libs/scripts/SharpTooth.js diff --git a/d2bs/kolbot/libs/bots/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js similarity index 99% rename from d2bs/kolbot/libs/bots/ShopBot.js rename to d2bs/kolbot/libs/scripts/ShopBot.js index 9cb1f7d2a..6deae7a0b 100644 --- a/d2bs/kolbot/libs/bots/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -135,7 +135,7 @@ function ShopBot() { delay(1000); if (npc.startTrade(menuId)) { - Misc.logItem("Shopped", items[i]); + Item.logItem("Shopped", items[i]); items[i].buy(); bought = true; } diff --git a/d2bs/kolbot/libs/bots/Smith.js b/d2bs/kolbot/libs/scripts/Smith.js similarity index 100% rename from d2bs/kolbot/libs/bots/Smith.js rename to d2bs/kolbot/libs/scripts/Smith.js diff --git a/d2bs/kolbot/libs/bots/Snapchip.js b/d2bs/kolbot/libs/scripts/Snapchip.js similarity index 100% rename from d2bs/kolbot/libs/bots/Snapchip.js rename to d2bs/kolbot/libs/scripts/Snapchip.js diff --git a/d2bs/kolbot/libs/bots/Stormtree.js b/d2bs/kolbot/libs/scripts/Stormtree.js similarity index 100% rename from d2bs/kolbot/libs/bots/Stormtree.js rename to d2bs/kolbot/libs/scripts/Stormtree.js diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js new file mode 100644 index 000000000..fa70f4528 --- /dev/null +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -0,0 +1,57 @@ +/** +* @filename Summoner.js +* @author kolton, theBGuy +* @desc kill the Summoner +* +*/ + +function Summoner () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); + + if (Config.Summoner.FireEye) { + try { + if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); + } catch (e) { + console.error(e); + } + } + + if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) throw new Error("Failed to move back to arcane"); + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, -3, -3)) throw new Error("Failed to move to Summoner"); + + Attack.clear(15, 0, sdk.monsters.TheSummoner); + + // always take portal, faster access to wp + // first check if portal is already up + let portal = Game.getObject(sdk.objects.RedPortal); + + if (!portal || !Pather.usePortal(null, null, portal)) { + for (let i = 0; i < 5; i++) { + // couldn't find portal, attempt to interact with journal + let journal = Game.getObject(sdk.objects.Journal); + + // couldnt find journal? Move to it's preset + if (!journal) { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); + continue; + } + + Packet.entityInteract(journal); + Misc.poll(() => getIsTalkingNPC(), 1000, 50); + me.cancel(); + + if (Pather.usePortal(sdk.areas.CanyonofMagic)) { + break; + } + } + } + + if (me.inArea(sdk.areas.CanyonofMagic)) { + Loader.scriptName(1) === "Duriel" ? Loader.skipTown.push("Duriel") : Pather.useWaypoint(sdk.areas.LutGholein); + } + + return true; +} diff --git a/d2bs/kolbot/libs/bots/Synch.js b/d2bs/kolbot/libs/scripts/Synch.js similarity index 100% rename from d2bs/kolbot/libs/bots/Synch.js rename to d2bs/kolbot/libs/scripts/Synch.js diff --git a/d2bs/kolbot/libs/bots/Synch2.js b/d2bs/kolbot/libs/scripts/Synch2.js similarity index 100% rename from d2bs/kolbot/libs/bots/Synch2.js rename to d2bs/kolbot/libs/scripts/Synch2.js diff --git a/d2bs/kolbot/libs/bots/Test.js b/d2bs/kolbot/libs/scripts/Test.js similarity index 100% rename from d2bs/kolbot/libs/bots/Test.js rename to d2bs/kolbot/libs/scripts/Test.js diff --git a/d2bs/kolbot/libs/bots/ThreshSocket.js b/d2bs/kolbot/libs/scripts/ThreshSocket.js similarity index 100% rename from d2bs/kolbot/libs/bots/ThreshSocket.js rename to d2bs/kolbot/libs/scripts/ThreshSocket.js diff --git a/d2bs/kolbot/libs/bots/Tombs.js b/d2bs/kolbot/libs/scripts/Tombs.js similarity index 100% rename from d2bs/kolbot/libs/bots/Tombs.js rename to d2bs/kolbot/libs/scripts/Tombs.js diff --git a/d2bs/kolbot/libs/bots/Travincal.js b/d2bs/kolbot/libs/scripts/Travincal.js similarity index 100% rename from d2bs/kolbot/libs/bots/Travincal.js rename to d2bs/kolbot/libs/scripts/Travincal.js diff --git a/d2bs/kolbot/libs/bots/TravincalLeech.js b/d2bs/kolbot/libs/scripts/TravincalLeech.js similarity index 98% rename from d2bs/kolbot/libs/bots/TravincalLeech.js rename to d2bs/kolbot/libs/scripts/TravincalLeech.js index f790801f4..72a8a32f0 100644 --- a/d2bs/kolbot/libs/bots/TravincalLeech.js +++ b/d2bs/kolbot/libs/scripts/TravincalLeech.js @@ -14,6 +14,7 @@ */ function TravincalLeech () { + include("core/Common/Leecher.js"); let leader; let done = false; diff --git a/d2bs/kolbot/libs/bots/Treehead.js b/d2bs/kolbot/libs/scripts/Treehead.js similarity index 100% rename from d2bs/kolbot/libs/bots/Treehead.js rename to d2bs/kolbot/libs/scripts/Treehead.js diff --git a/d2bs/kolbot/libs/bots/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js similarity index 97% rename from d2bs/kolbot/libs/bots/Tristram.js rename to d2bs/kolbot/libs/scripts/Tristram.js index 1bc3c9382..35abc95a8 100644 --- a/d2bs/kolbot/libs/bots/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -10,6 +10,7 @@ function Tristram () { // complete quest if its not complete if (!me.getQuest(sdk.quest.id.TheSearchForCain, 4) && !me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { + include("core/Common/Cain.js"); Common.Questing.cain(); } diff --git a/d2bs/kolbot/libs/bots/TristramLeech.js b/d2bs/kolbot/libs/scripts/TristramLeech.js similarity index 98% rename from d2bs/kolbot/libs/bots/TristramLeech.js rename to d2bs/kolbot/libs/scripts/TristramLeech.js index 27075b79a..51c054116 100644 --- a/d2bs/kolbot/libs/bots/TristramLeech.js +++ b/d2bs/kolbot/libs/scripts/TristramLeech.js @@ -6,6 +6,7 @@ */ function TristramLeech () { + include("core/Common/Leecher.js"); let done = false; let whereisleader, leader; diff --git a/d2bs/kolbot/libs/bots/UndergroundPassage.js b/d2bs/kolbot/libs/scripts/UndergroundPassage.js similarity index 100% rename from d2bs/kolbot/libs/bots/UndergroundPassage.js rename to d2bs/kolbot/libs/scripts/UndergroundPassage.js diff --git a/d2bs/kolbot/libs/bots/UserAddon.js b/d2bs/kolbot/libs/scripts/UserAddon.js similarity index 83% rename from d2bs/kolbot/libs/bots/UserAddon.js rename to d2bs/kolbot/libs/scripts/UserAddon.js index 43a835fac..e2d8a2776 100644 --- a/d2bs/kolbot/libs/bots/UserAddon.js +++ b/d2bs/kolbot/libs/scripts/UserAddon.js @@ -1,6 +1,6 @@ /** * @filename UserAddon.js -* @author kolton +* @author kolton, theBGuy * @desc Allows you to see more information about items, NPCs and players by placing the cursor over them. * Shows item level, items in sockets, classid, code and magic item prefix/suffix numbers. * Shows monster's classid, HP percent and resistances. @@ -8,10 +8,9 @@ * */ -include("UnitInfo.js"); - function UserAddon () { let i, title, dummy, command = ""; + const UnitInfo = new (require("../modules/UnitInfo")); const className = sdk.player.class.nameOf(me.classid); const flags = [ sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, @@ -46,9 +45,9 @@ function UserAddon () { addEventListener("chatinputblocker", onChatInput); if (!FileTools.exists("libs/config/" + className + "." + me.name + ".js")) { - showConsole(); - print("ÿc4UserAddonÿc0: Press HOME and then press SPACE if you want to create character config."); + console.log("ÿc4UserAddonÿc0: Press HOME and then press SPACE if you want to create character config."); addEventListener("keyup", keyEvent); + showConsole(); } while (true) { @@ -71,21 +70,19 @@ function UserAddon () { dummy = new Text("`", 1, 1); // Prevents crash } - !UnitInfo.cleared && !Game.getSelectedUnit() && UnitInfo.remove(); + UnitInfo.check(); if (command && command.toLowerCase() === "done") { - print("ÿc4UserAddon ÿc1ended"); + console.log("ÿc4UserAddon ÿc1ended"); return true; } else { - print(command); + console.log(command); command = ""; } Pickit.fastPick(); - - let unit = Game.getSelectedUnit(); - !!unit && UnitInfo.createInfo(unit); + UnitInfo.createInfo(Game.getSelectedUnit()); delay(20); } @@ -93,5 +90,9 @@ function UserAddon () { removeEventListener("keyup", keyEvent); removeEventListener("itemaction", Pickit.itemEvent); removeEventListener("chatinputblocker", onChatInput); + // ensure hooks are properly disposed of + !!title && title.remove(); + dummy && dummy.remove(); + UnitInfo.remove(); } } diff --git a/d2bs/kolbot/libs/bots/WPGetter.js b/d2bs/kolbot/libs/scripts/WPGetter.js similarity index 100% rename from d2bs/kolbot/libs/bots/WPGetter.js rename to d2bs/kolbot/libs/scripts/WPGetter.js diff --git a/d2bs/kolbot/libs/bots/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js similarity index 99% rename from d2bs/kolbot/libs/bots/Wakka.js rename to d2bs/kolbot/libs/scripts/Wakka.js index 6200e952f..5284fd6ab 100644 --- a/d2bs/kolbot/libs/bots/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -6,6 +6,7 @@ */ function Wakka () { + include("core/Common/Diablo.js"); const timeout = Config.Wakka.Wait; const minDist = 50; const maxDist = 80; @@ -382,7 +383,7 @@ function Wakka () { } this.log("Diablo is dead"); - if (!Town.canTpToTown() || !Town.goToTown()) { + if (!me.canTpToTown() || !Town.goToTown()) { Pather.usePortal(sdk.areas.PandemoniumFortress); } diff --git a/d2bs/kolbot/libs/bots/Worldstone.js b/d2bs/kolbot/libs/scripts/Worldstone.js similarity index 100% rename from d2bs/kolbot/libs/bots/Worldstone.js rename to d2bs/kolbot/libs/scripts/Worldstone.js diff --git a/d2bs/kolbot/libs/starter/AdvancedConfig.js b/d2bs/kolbot/libs/starter/AdvancedConfig.js new file mode 100644 index 000000000..318139da7 --- /dev/null +++ b/d2bs/kolbot/libs/starter/AdvancedConfig.js @@ -0,0 +1,46 @@ +/** +* @filename AdvancedConfig.js +* @author theBGuy +* @desc Profile specific settings for entry scripts. +* @note For general and global settings @see StarterConfig.js +* +*/ + +(function (module) { + module.exports = { + /* Features: + Override channel for each profile, Override join delay for each profile + Override default values for JoinChannel, FirstJoinMessage, AnnounceGames and AfterGameMessage per profile + + * Format *: + "Profile Name": {JoinDelay: number_of_seconds} + or + "Profile Name": {JoinChannel: "channel name"} + or + "Profile Name": {JoinChannel: "channel name", JoinDelay: number_of_seconds} + + * Example * (don't edit this - it's just an example): + + "MyProfile1": {JoinDelay: 3}, + "MyProfile2": {JoinChannel: "some channel"}, + "MyProfile3": {JoinChannel: "some other channel", JoinDelay: 11} + "MyProfile4": {AnnounceGames: true, AnnounceMessage: "Joining game"} // announce game you are joining + + "Profile Name": { + JoinChannel: "channel name", + FirstJoinMessage: "first message", -OR- ["join msg 1", "join msg 2"], + AnnounceGames: true, + AfterGameMessage: "message after a finished run" -OR- ["msg 1", msg 2"] + } + */ + + // Put your lines under this one. Multiple entries are separated by commas. No comma after the last one. + + "Test": { + JoinChannel: "op nnqry", + JoinDelay: 3, + AnnounceGames: true, + AnnounceMessage: "Joining game" // output: Joining game Baals-23 + }, + }; +})(module); diff --git a/d2bs/kolbot/libs/starter/StarterConfig.js b/d2bs/kolbot/libs/starter/StarterConfig.js new file mode 100644 index 000000000..6505d554e --- /dev/null +++ b/d2bs/kolbot/libs/starter/StarterConfig.js @@ -0,0 +1,43 @@ +/** +* @filename StarterConfig.js +* @author theBGuy +* @desc Global settings for entry scripts +* @note For profile specific settings and overrides @see AdvancedConfig.js +* +*/ + +(function (module) { + module.exports = { + MinGameTime: 360, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby + PingQuitDelay: 30, // Time in seconds to wait in lobby after quitting due to high ping + CreateGameDelay: rand(5, 15), // Seconds to wait before creating a new game + ResetCount: 999, // Reset game count back to 1 every X games. + CharacterDifference: 99, // Character level difference. Set to false to disable character difference. + MaxPlayerCount: 8, // Max amount of players in game between 1 and 8 + StopOnDeadHardcore: true, // Stop profile character has died on hardcore mode + + // ChannelConfig can override these options for individual profiles. + JoinChannel: "", // Default channel. + FirstJoinMessage: "", // Default join message. Can be an array of messages + ChatActionsDelay: 2, // Seconds to wait in lobby before entering a channel + AnnounceGames: false, // Default value + AnnounceMessage: "", // Message to announce before making game + AfterGameMessage: "", // Default message after a finished game. Can be an array of messages + + InvalidPasswordDelay: 10, // Minutes to wait after getting Invalid Password message + VersionErrorDelay: rand(5, 30), // Seconds to wait after 'unable to identify version' message + SwitchKeyDelay: 5, // Seconds to wait before switching a used/banned key or after realm down + CrashDelay: rand(120, 150), // Seconds to wait after a d2 window crash + FTJDelay: 120, // Seconds to wait after failing to create a game + RealmDownDelay: 3, // Minutes to wait after getting Realm Down message + UnableToConnectDelay: 5, // Minutes to wait after Unable To Connect message + TCPIPNoHostDelay: 5, // Seconds to wait after Cannot Connect To Server message + CDKeyInUseDelay: 5, // Minutes to wait before connecting again if CD-Key is in use. + ConnectingTimeout: 60, // Seconds to wait before cancelling the 'Connecting...' screen + PleaseWaitTimeout: 60, // Seconds to wait before cancelling the 'Please Wait...' screen + WaitInLineTimeout: 3600, // Seconds to wait before cancelling the 'Waiting in Line...' screen + WaitOutQueueRestriction: true, // Wait out queue if we are restricted, queue time > 10000 + WaitOutQueueExitToMenu: false, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby + GameDoesNotExistTimeout: 30, // Seconds to wait before cancelling the 'Game does not exist.' screen + }; +})(module); diff --git a/d2bs/kolbot/libs/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js similarity index 78% rename from d2bs/kolbot/libs/AutoMule.js rename to d2bs/kolbot/libs/systems/automule/AutoMule.js index 9c3cb258e..151ae1acb 100644 --- a/d2bs/kolbot/libs/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -1,97 +1,44 @@ /** * @filename AutoMule.js * @author kolton, theBGuy -* @desc Configuration file for Mule system +* @desc Main driver for Mule system +* For Mules setup @see MuleConfig.js +* For TorchAnniMules setup @see TorchAnniMules.js * */ const AutoMule = { - Mules: { - "Mule1": { - muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. - accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. - accountPassword: "", // Account password. - charPrefix: "", // Character prefix. Suffix added automatically when making characters. - realm: "", // Available options: "useast", "uswest", "europe", "asia" - expansion: true, - ladder: true, - hardcore: false, - charsPerAcc: 18, // Maximum number of mules to create per account (between 1 to 18) - - // Game name and password of the mule game. Never use the same game name as for mule logger. - muleGameName: ["", ""], // ["gamename", "password"] - - // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], - enabledProfiles: [""], - - // Stop a profile prior to muling. Useful when running 8 bots without proxies. - stopProfile: "", - stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. - - // Trigger muling at the end of a game if used space in stash and inventory is equal to or more than given percent. - usedStashTrigger: 80, - usedInventoryTrigger: 80, - - // Mule items that have been stashed at some point but are no longer in pickit. - muleOrphans: true, - // Continuous Mule settings - continuousMule: false, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. - skipMuleResponse: false, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. - onlyLogWhenFull: false // Only log character when full, solves an issue with droppers attempting to use characters who are already in game - } - }, - - /** Torch/Anni mules - - Torch is muled in OrgTorch script after finishing uber Tristram successfully or when starting OrgTorch script with a Torch already on the character. - - Anni is muled after successfully killing Diablo in Palace Cellar level 3 using Config.KillDclone option or KillDClone script. - If a profile is listed in Torch/Anni mule's enabledProfiles list, it will also do a check to mule Anni at the end of each game. - Anni that is in locked inventory slot will not be muled. - - * Each mule will hold either a Torch or an Anni, but not both. As soon as the current mule has either one, a new one will be created. - */ - TorchAnniMules: { - "Mule1": { - muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. - accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. - accountPassword: "", // Account password. - charPrefix: "", // Character prefix. Suffix added automatically when making characters. - realm: "", // Available options: "useast", "uswest", "europe", "asia" - expansion: true, - ladder: true, - hardcore: false, - charsPerAcc: 8, // Maximum number of mules to create per account (between 1 to 18) - - // Game name and password of the mule game. Never use the same game name as for mule logger. - muleGameName: ["", ""], // ["gamename", "password"] - - // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], - enabledProfiles: [""], - - // Stop a profile prior to muling. Useful when running 8 bots without proxies. - stopProfile: "", - stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. - - // Continuous Mule settings - continuousMule: true, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. - skipMuleResponse: true, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. - onlyLogWhenFull: true // Only log character when full, solves an issue with droppers attempting to use characters who are already in game - } - //########################################################################################## - }, + // load configuration files + /** + * @type {Object.} + */ + Mules: Object.assign({}, require("./MuleConfig", null, false)), + + /** + * @type {Object.} + */ + TorchAnniMules: Object.assign({}, require("./TorchAnniMules", null, false)), inGame: false, check: false, torchAnniCheck: false, gids: [], - // *** Master functions *** + // ################################## // + /* ##### Master/Muler Functions ##### */ + // ################################## // + + /** + * Get mule and torchanni mule info if it exists + * @returns {muleObj | {}} + */ getInfo: function () { let info; for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { for (let j = 0; j < this.Mules[i].enabledProfiles.length; j += 1) { - if (this.Mules[i].enabledProfiles[j].toLowerCase() === me.profile.toLowerCase()) { + if (String.isEqual(this.Mules[i].enabledProfiles[j], me.profile)) { !info && (info = {}); info.muleInfo = this.Mules[i]; @@ -104,7 +51,7 @@ const AutoMule = { for (let i in this.TorchAnniMules) { if (this.TorchAnniMules.hasOwnProperty(i)) { for (let j = 0; j < this.TorchAnniMules[i].enabledProfiles.length; j += 1) { - if (this.TorchAnniMules[i].enabledProfiles[j].toLowerCase() === me.profile.toLowerCase()) { + if (String.isEqual(this.TorchAnniMules[i].enabledProfiles[j], me.profile)) { !info && (info = {}); info.torchMuleInfo = this.TorchAnniMules[i]; @@ -142,6 +89,10 @@ const AutoMule = { return false; }, + /** + * Find a mule that matches our wanted check + * @returns {muleObj | false} + */ getMule: function () { let info = this.getInfo(); @@ -167,7 +118,7 @@ const AutoMule = { let once = false; let muleInfo = {status: ""}; let failCount = 0; - let Controls = require("./modules/Control"); + let Controls = require("../../modules/Control"); if (!muleObj.continuousMule || !muleObj.skipMuleResponse) { addEventListener("copydata", muleCheckEvent); @@ -221,7 +172,7 @@ const AutoMule = { delay(2000); - this.inGame = true; + AutoMule.inGame = true; me.blockMouse = true; try { @@ -279,9 +230,9 @@ const AutoMule = { delay(1000); } - this.inGame = false; - this.check = false; - this.torchAnniCheck = false; + AutoMule.inGame = false; + AutoMule.check = false; + AutoMule.torchAnniCheck = false; // No response - stop mule profile if (!muleObj.continuousMule) { @@ -453,7 +404,9 @@ const AutoMule = { } }, - // finished if no items are on ground + /** + * finished if no items are on ground + */ isFinished: function () { let item = Game.getItem(); @@ -472,7 +425,10 @@ const AutoMule = { return true; }, - // make sure mule character is in game + /** + * make sure mule character is in game + * @param {string} mulePrefix + */ verifyMulePrefix: function (mulePrefix) { try { let player = getParty(); @@ -494,6 +450,10 @@ const AutoMule = { return false; }, + /** + * Transfer items to waiting mule + * @returns {boolean} + */ dropStuff: function () { if (!Town.openStash()) return false; @@ -508,9 +468,35 @@ const AutoMule = { delay(1000); me.cancel(); + // handle sort - for now quick and dirty method + Town.openStash() && me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter(i => i.isInStash && i.classid !== sdk.quest.item.Cube) + .sort((a, b) => b.sizex * b.sizey - a.sizex * a.sizey || b.x - a.x || b.y - a.y) + .forEach(item => { + let spot = Storage.Stash.FindSpot(item); + if (spot) { + [spot.x, spot.y] = [spot.y, spot.x]; // remember it's backwards + if (spot.x > item.x) return; + if (spot.y > item.y && spot.x === item.x) return; + if (spot.x === item.x && spot.y === item.y) return; + + if (!Storage.Stash.MoveTo(item)) { + return; + } + + Storage.Reload(); + } + }); + me.cancelUIFlags(); + return true; }, + /** + * @param {ItemUnit} item + * @param {string[] | number[]} list + * @returns {boolean} + */ matchItem: function (item, list) { let parsedPickit = [], classIDs = []; @@ -533,7 +519,10 @@ const AutoMule = { return (classIDs.includes(item.classid) || NTIP.CheckItem(item, parsedPickit)); }, - // get a list of items to mule + /** + * get a list of items to mule + * @returns {ItemUnit[] | false} + */ getMuleItems: function () { let info = this.getInfo(); @@ -577,11 +566,20 @@ const AutoMule = { return items; }, + /** + * Wanted by CraftingSystem + * @param {ItemUnit} item + * @returns {boolean} + */ utilityIngredient: function (item) { return (!!item && CraftingSystem.validGids.includes(item.gid)); }, - // check if an item is a cubing ingredient + /** + * check if an item is a cubing ingredient + * @param {ItemUnit} item + * @returns {boolean} + */ cubingIngredient: function (item) { if (!item) return false; @@ -594,13 +592,17 @@ const AutoMule = { return false; }, - // check if an item is a runeword ingrediend - rune, empty base or bad rolled base + /** + * check if an item is a runeword ingrediend - rune, empty base or bad rolled base + * @param {ItemUnit} item + * @returns {boolean} + */ runewordIngredient: function (item) { if (!item) return false; if (Runewords.validGids.includes(item.gid)) return true; if (!this.baseGids) { - this.baseGids = []; + AutoMule.baseGids = []; for (let i = 0; i < Config.Runewords.length; i += 1) { let base = Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) || Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true); @@ -611,6 +613,11 @@ const AutoMule = { return this.baseGids.includes(item.gid); }, + /** + * Drop Anni or Gheeds + * @param {boolean} dropAnni + * @returns {boolean} + */ dropCharm: function (dropAnni) { if (!Town.openStash()) return false; @@ -641,7 +648,14 @@ const AutoMule = { return true; }, - // *** Mule functions *** + // ################################## // + /* ######### Mule Functions ######### */ + // ################################## // + + /** + * @param {{ profile: string, mode: number }} info + * @returns {{ profile: string, mode: number }} master info + */ getMaster: function (info) { let muleObj = info.mode === 1 ? this.TorchAnniMules : this.Mules; @@ -669,7 +683,6 @@ const AutoMule = { * @param {number} mode - mule mode * @param {string} master - profile that whats to mule * @param {boolean} continuous - whether we are continuous or not - * @returns {muleObj | boolean} */ getMuleObject: function (mode, master, continuous = false) { mode = mode || 0; @@ -687,6 +700,12 @@ const AutoMule = { return false; }, + /** + * @param {number} mode + * @param {string} master + * @param {boolean} continuous + * @returns {string} + */ getMuleFilename: function (mode, master, continuous = false) { mode = mode || 0; let mule = mode > 0 ? this.TorchAnniMules : this.Mules; @@ -725,6 +744,9 @@ const AutoMule = { return file; }, + /** + * Get whether this is a regular mule or a torch/anni mule + */ getMuleMode: function() { for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { @@ -745,7 +767,12 @@ const AutoMule = { return 0; }, + /** + * Get whether this is a normal mule or continous mule + */ isContinousMule: function () { + console.debug(this.Mules); + console.debug(this.TorchAnniMules); for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { diff --git a/d2bs/kolbot/libs/systems/automule/MuleConfig.js b/d2bs/kolbot/libs/systems/automule/MuleConfig.js new file mode 100644 index 000000000..b5b2fe26c --- /dev/null +++ b/d2bs/kolbot/libs/systems/automule/MuleConfig.js @@ -0,0 +1,43 @@ +/** +* @filename MuleConfig.js +* @author theBGuy +* @desc Configuration profiles for AutoMule system, for TorchAnni specific @see TorchAnniMules.js +* +*/ + +(function (module) { + module.exports = { + "Mule1": { + muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. + accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. + accountPassword: "", // Account password. + charPrefix: "", // Character prefix. Suffix added automatically when making characters. + realm: "", // Available options: "useast", "uswest", "europe", "asia" + expansion: true, + ladder: true, + hardcore: false, + charsPerAcc: 18, // Maximum number of mules to create per account (between 1 to 18) + + // Game name and password of the mule game. Never use the same game name as for mule logger. + muleGameName: ["", ""], // ["gamename", "password"] + + // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], + enabledProfiles: [""], + + // Stop a profile prior to muling. Useful when running 8 bots without proxies. + stopProfile: "", + stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. + + // Trigger muling at the end of a game if used space in stash and inventory is equal to or more than given percent. + usedStashTrigger: 80, + usedInventoryTrigger: 80, + + // Mule items that have been stashed at some point but are no longer in pickit. + muleOrphans: true, + // Continuous Mule settings + continuousMule: false, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. + skipMuleResponse: false, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. + onlyLogWhenFull: false // Only log character when full, solves an issue with droppers attempting to use characters who are already in game + }, + }; +})(module); diff --git a/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js b/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js new file mode 100644 index 000000000..a22d0530d --- /dev/null +++ b/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js @@ -0,0 +1,45 @@ +/** +* @filename TorchAnniMules.js +* @author theBGuy +* @desc Torch/Anni specific mule profiles for AutoMule system +* +*/ + +/** + * @description Torch/Anni mules: + * - Torch is muled in OrgTorch script after finishing uber Tristram successfully or when starting OrgTorch script with a Torch already on the character. + * - Anni is muled after successfully killing Diablo in Palace Cellar level 3 using Config.KillDclone option or KillDClone script. + * - If a profile is listed in Torch/Anni mule's enabledProfiles list, it will also do a check to mule Anni at the end of each game. + * @note Anni that is in locked inventory slot will not be muled. + * @note Each mule will hold either a Torch or an Anni, but not both. As soon as the current mule has either one, a new one will be created. + */ +(function (module) { + module.exports = { + "Mule1": { + muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. + accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. + accountPassword: "", // Account password. + charPrefix: "", // Character prefix. Suffix added automatically when making characters. + realm: "", // Available options: "useast", "uswest", "europe", "asia" + expansion: true, + ladder: true, + hardcore: false, + charsPerAcc: 8, // Maximum number of mules to create per account (between 1 to 18) + + // Game name and password of the mule game. Never use the same game name as for mule logger. + muleGameName: ["", ""], // ["gamename", "password"] + + // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], + enabledProfiles: [""], + + // Stop a profile prior to muling. Useful when running 8 bots without proxies. + stopProfile: "", + stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. + + // Continuous Mule settings + continuousMule: true, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. + skipMuleResponse: true, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. + onlyLogWhenFull: true // Only log character when full, solves an issue with droppers attempting to use characters who are already in game + }, + }; +})(module); diff --git a/d2bs/kolbot/libs/CraftingSystem.js b/d2bs/kolbot/libs/systems/crafting/CraftingSystem.js similarity index 90% rename from d2bs/kolbot/libs/CraftingSystem.js rename to d2bs/kolbot/libs/systems/crafting/CraftingSystem.js index cbd9fbda1..c80018ea0 100644 --- a/d2bs/kolbot/libs/CraftingSystem.js +++ b/d2bs/kolbot/libs/systems/crafting/CraftingSystem.js @@ -4,47 +4,14 @@ * @desc Multi-profile crafting system * @notes This system is experimental, there will be no support offered for it. * If you can't get it to work, leave it be. +* This is the main driver file, for the Teams config @see TeamsConfig.js * */ const CraftingSystem = {}; -CraftingSystem.Teams = { - "Team 1": { - // List of profiles that will collect ingredients - Collectors: [], - - // List of profiles that will craft/reroll items - Workers: [], - - // List of Worker game names (without the numbers) - CraftingGames: [], - - /* BaseItems - list of base item class ids - * Ingredients - list of recipe ingredients - * SetAmount - number of full sets to gather before transfering - * Type - the type of recipe. Available options: "crafting", "runewords", "cubing" - */ - Sets: [ - // LLD Crafting - - // Caster Belt set, char lvl 29 - // Light Belt classid 345, shopped at nightmare Elzix - // Sharkskin Belt classid 391, shopped at nightmare Elzix - //{BaseItems: [345, 391], Ingredients: [615, 643, 561], SetAmount: 2, Type: "crafting"}, - - // Runeword Making - - // Spirit Runeset + Hel - //{BaseItems: [29, 30, 31], Ingredients: [616, 618, 619, 620, 624], SetAmount: 2, Type: "runewords"}, - - // Misc. Cubing - - // Reroll rare Diadem - //{BaseItems: [421], Ingredients: [601, 601, 601], SetAmount: 1, Type: "cubing"} - ] - } -}; +// load configuration file +CraftingSystem.Teams = Object.assign({}, require("./TeamsConfig", null, false)); // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## diff --git a/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js b/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js new file mode 100644 index 000000000..2942c4983 --- /dev/null +++ b/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js @@ -0,0 +1,45 @@ +/** +* @filename TeamsConfig.js +* @author theBGuy +* @desc Configuration file for Crafting system +* +*/ + +(function (module) { + module.exports = { + "Team 1": { + // List of profiles that will collect ingredients + Collectors: [], + + // List of profiles that will craft/reroll items + Workers: [], + + // List of Worker game names (without the numbers) + CraftingGames: [], + + /* BaseItems - list of base item class ids + * Ingredients - list of recipe ingredients + * SetAmount - number of full sets to gather before transfering + * Type - the type of recipe. Available options: "crafting", "runewords", "cubing" + */ + Sets: [ + // LLD Crafting + + // Caster Belt set, char lvl 29 + // Light Belt classid 345, shopped at nightmare Elzix + // Sharkskin Belt classid 391, shopped at nightmare Elzix + //{BaseItems: [345, 391], Ingredients: [615, 643, 561], SetAmount: 2, Type: "crafting"}, + + // Runeword Making + + // Spirit Runeset + Hel + //{BaseItems: [29, 30, 31], Ingredients: [616, 618, 619, 620, 624], SetAmount: 2, Type: "runewords"}, + + // Misc. Cubing + + // Reroll rare Diadem + //{BaseItems: [421], Ingredients: [601, 601, 601], SetAmount: 1, Type: "cubing"} + ] + }, + }; +})(module); diff --git a/d2bs/kolbot/libs/Gambling.js b/d2bs/kolbot/libs/systems/gambling/Gambling.js similarity index 70% rename from d2bs/kolbot/libs/Gambling.js rename to d2bs/kolbot/libs/systems/gambling/Gambling.js index 8c2f10ac4..fa769230c 100644 --- a/d2bs/kolbot/libs/Gambling.js +++ b/d2bs/kolbot/libs/systems/gambling/Gambling.js @@ -1,48 +1,17 @@ /** * @filename Gambling.js * @author kolton -* @desc multi-profile gambling system +* @desc Multi-profile gambling system. +* Allows lower level characters to get a steady income of gold to gamble LLD/VLLD items +* Not recommended for rings/amulets because of their high price (unless you want 3 gold finders to supply one gambler) +* It's possible to have multiple teams of gamblers/gold finders. Individual entries are separated by commas. +* @see TeamsConfig.js for setup * */ const Gambling = { - Teams: { - //#################################################################################################### - /* Gambling system for kolbot - - Allows lower level characters to get a steady income of gold to gamble LLD/VLLD items - Not recommended for rings/amulets because of their high price (unless you want 3 gold finders to supply one gambler) - It's possible to have multiple teams of gamblers/gold finders. Individual entries are separated by commas. - - Setting up: - - "Gamble Team 1": { // Put a unique team name here. - - goldFinders: ["GF Profile 1", "GF Profile 2"], // List of gold finder PROFILE names. They will join gamble games to drop gold - - gamblers: ["Gambler 1", "Gambler 2"], // List of gambler PROFILE names. They will keep gambling and picking up gold from gold finders. - - gambleGames: ["Gambling-", "HeyIGamble-"], // Games that gold finders will join, don't use numbers. - - goldTrigger: 2500000, // Minimum amount of gold before giving it to gamblers. - - goldReserve: 200000 // Amount of gold to keep after dropping. - } - - Once set up properly, the gold finders will run their own games and join gamblers' games when they're out of gold. - */ - - "Gamble Team 1": { - goldFinders: [""], - gamblers: [""], - gambleGames: [""], - - goldTrigger: 2500000, - goldReserve: 200000 - } - - //#################################################################################################### - }, + // load configuration file + Teams: Object.assign({}, require("./TeamsConfig", null, false)), inGame: false, diff --git a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js new file mode 100644 index 000000000..02846546e --- /dev/null +++ b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js @@ -0,0 +1,37 @@ +/** +* @filename TeamsConfig.js +* @author theBGuy +* @desc Configuration file for Gambling system +* +*/ + +(function (module) { + module.exports = { + /** + Setting up: + + "Gamble Team 1": { // Put a unique team name here. + + goldFinders: ["GF Profile 1", "GF Profile 2"], // List of gold finder PROFILE names. They will join gamble games to drop gold + + gamblers: ["Gambler 1", "Gambler 2"], // List of gambler PROFILE names. They will keep gambling and picking up gold from gold finders. + + gambleGames: ["Gambling-", "HeyIGamble-"], // Games that gold finders will join, don't use numbers. + + goldTrigger: 2500000, // Minimum amount of gold before giving it to gamblers. + + goldReserve: 200000 // Amount of gold to keep after dropping. + } + + Once set up properly, the gold finders will run their own games and join gamblers' games when they're out of gold. + */ + "Gamble Team 1": { + goldFinders: [""], + gamblers: [""], + gambleGames: [""], + + goldTrigger: 2500000, + goldReserve: 200000 + }, + }; +})(module); diff --git a/d2bs/kolbot/libs/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js similarity index 89% rename from d2bs/kolbot/libs/GameAction.js rename to d2bs/kolbot/libs/systems/gameaction/GameAction.js index d514e2a55..0eac8344a 100644 --- a/d2bs/kolbot/libs/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -5,9 +5,11 @@ * @desc Perform task based actions specified by Profile Tag * */ -include("MuleLogger.js"); +include("systems/mulelogger/MuleLogger.js"); const GameAction = { + // keeping with the general structure changes this section should probably be in its own config file + // but its not a lot so does it really need to be? LogNames: true, // Put account/character name on the picture LogItemLevel: true, // Add item level to the picture LogEquipped: false, // include equipped items @@ -15,9 +17,10 @@ const GameAction = { SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) IngameTime: 60, // Time to wait before leaving game + task: null, // don't edit init: function (task) { - this.task = JSON.parse(task); + GameAction.task = JSON.parse(task); if (this.task["data"] && typeof this.task.data === "string") { this.task.data = JSON.parse(this.task.data); @@ -134,7 +137,13 @@ const GameAction = { delay(1000); } - quit(); + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } return true; } @@ -149,7 +158,7 @@ const GameAction = { this.update("done", "File " + filename + " does not exist!"); D2Bot.stop(); delay(5000); - quitGame(); + quitGame(); // pretty sure quitGame crashes? } return FileTools.readText(filename); @@ -176,7 +185,7 @@ const GameAction = { continue; } - let info = droplist[i].itemid.split(":");//":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y; + let info = droplist[i].itemid.split(":"); //":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y; let classid = info[1]; let loc = info[2]; diff --git a/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js b/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js new file mode 100644 index 000000000..b63ba1590 --- /dev/null +++ b/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js @@ -0,0 +1,36 @@ +/** +* @filename LoggerConfig.js +* @author theBGuy +* @desc Configuration file for MuleLogger system +* +*/ + +(function (module) { + module.exports = { + LogGame: ["", ""], // ["gamename", "password"] + LogNames: true, // Put account/character name on the picture + LogItemLevel: true, // Add item level to the picture + LogEquipped: true, // include equipped items + LogMerc: true, // include items merc has equipped (if alive) + SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) + AutoPerm: true, // override InGameTime to perm character + IngameTime: rand(60, 120), // (180, 210) to avoid RD, increase it to (7230, 7290) for mule perming + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + LogAccounts: { + /* Format: + "account1/password1/realm": ["charname1", "charname2 etc"], + "account2/password2/realm": ["charnameX", "charnameY etc"], + "account3/password3/realm": ["all"] + + To log a full account, put "account/password/realm": ["all"] + + realm = useast, uswest, europe or asia + + Enter Individual entries are separated with a comma below + */ + "exampleAcc/pa33word3/realm": ["all"], + }, + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + }; +})(module); diff --git a/d2bs/kolbot/libs/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js similarity index 79% rename from d2bs/kolbot/libs/MuleLogger.js rename to d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index e7b1c4989..3ed1a17d6 100644 --- a/d2bs/kolbot/libs/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -1,35 +1,12 @@ /** * @filename MuleLogger.js * @author kolton, theBGuy -* @desc Log items and perm configurable accounts/characters +* @desc Log items and perm accounts/characters, for setup @see LoggerConfig.js * */ -!isIncluded("common/prototypes.js") && include("common/prototypes.js"); const MuleLogger = { - LogAccounts: { - /* Format: - "account1/password1/realm": ["charname1", "charname2 etc"], - "account2/password2/realm": ["charnameX", "charnameY etc"], - "account3/password3/realm": ["all"] - - To log a full account, put "account/password/realm": ["all"] - - realm = useast, uswest, europe or asia - - Individual entries are separated with a comma. - */ - }, - - LogGame: ["", ""], // ["gamename", "password"] - LogNames: true, // Put account/character name on the picture - LogItemLevel: true, // Add item level to the picture - LogEquipped: true, // include equipped items - LogMerc: true, // include items merc has equipped (if alive) - SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) - AutoPerm: true, // override InGameTime to perm character - IngameTime: rand(60, 120), // (180, 210) to avoid RD, increase it to (7230, 7290) for mule perming - + // configuration file loaded at bottom inGameCheck: function () { if (getScript("D2BotMuleLog.dbj") && this.LogGame[0] && me.gamename.match(this.LogGame[0], "i")) { print("ÿc4MuleLoggerÿc0: Logging items on " + me.account + " - " + me.name + "."); @@ -59,7 +36,13 @@ const MuleLogger = { } } - quit(); + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } return true; } @@ -95,23 +78,22 @@ const MuleLogger = { // Log kept item stats in the manager. logItem: function (unit, logIlvl = this.LogItemLevel) { - if (!isIncluded("common/misc.js")) { - include("common/misc.js"); - include("common/util.js"); + if (!isIncluded("core/misc.js")) { + include("core/misc.js"); } 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) + "$" + unit.gid + ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); + let desc = Item.getItemDesc(unit, logIlvl) + "$" + unit.gid + ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); let color = unit.getColor(); - let code = Misc.getItemCode(unit); + let code = Item.getItemCode(unit); let sock = unit.getItemsEx(); if (sock.length) { 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], logIlvl); + desc += Item.getItemDesc(sock[i], logIlvl); } } } @@ -122,7 +104,7 @@ const MuleLogger = { title: name, description: desc, header: header, - sockets: Misc.getItemSockets(unit) + sockets: Item.getItemSockets(unit) }; }, @@ -131,8 +113,9 @@ const MuleLogger = { delay(100); } - // try again if db is locked!! - if (isIncluded("ItemDB.js") || include("ItemDB.js")) { + // try again if db is locked!! - ItemDB is from https://github.com/dzik87/D2Dropper + // maybe just add it to the core? It's not going to work without an update + if (FileTools.exists("libs/ItemDB.js") && (isIncluded("ItemDB.js") || include("ItemDB.js"))) { while (!ItemDB.init(false)) { delay(1000); } @@ -210,3 +193,8 @@ const MuleLogger = { print("Item logging done."); } }; + +// load configuration file and apply settings to MuleLogger, has to be after the namespace is created +(function() { + Object.assign(MuleLogger, require("./LoggerConfig", null, false)); +})(); diff --git a/d2bs/kolbot/libs/systems/torch/FarmerConfig.js b/d2bs/kolbot/libs/systems/torch/FarmerConfig.js new file mode 100644 index 000000000..6b5e33738 --- /dev/null +++ b/d2bs/kolbot/libs/systems/torch/FarmerConfig.js @@ -0,0 +1,45 @@ +/** +* @filename FarmerConfig.js +* @author theBGuy +* @desc Configuration file for TorchSystem system +* +*/ + +(function (module) { + module.exports = { + // ############################ S E T U P ########################################## + + /* Each uber killer profile can have their own army of key finders + Multiple entries are separated with a comma + Example config: + + "Farmer 1": { // Farmer profile name + // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], + KeyFinderProfiles: ["mf 1", "mf 2"], + + // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", + FarmGame: "torch1-" + }, + + "Farmer 2": { // Farmer profile name + // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], + KeyFinderProfiles: ["mf 3", "mf 4"], + + // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", + FarmGame: "torch2-" + } + */ + + // Edit here! + + "PROFILE NAME": { // Farmer profile name + // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], + KeyFinderProfiles: [""], + + // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", + FarmGame: "" + }, + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + }; +})(module); diff --git a/d2bs/kolbot/libs/TorchSystem.js b/d2bs/kolbot/libs/systems/torch/TorchSystem.js similarity index 69% rename from d2bs/kolbot/libs/TorchSystem.js rename to d2bs/kolbot/libs/systems/torch/TorchSystem.js index 8f07d5060..fea239480 100644 --- a/d2bs/kolbot/libs/TorchSystem.js +++ b/d2bs/kolbot/libs/systems/torch/TorchSystem.js @@ -2,46 +2,13 @@ * @filename TorchSystem.js * @author kolton * @desc Works in conjunction with OrgTorch script. Allows the uber killer to get keys from other profiles. +* For setup @see FarmerConfig.js * */ const TorchSystem = { - FarmerProfiles: { - // ############################ S E T U P ########################################## - - /* Each uber killer profile can have their own army of key finders - Multiple entries are separated with a comma - Example config: - - "Farmer 1": { // Farmer profile name - // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], - KeyFinderProfiles: ["mf 1", "mf 2"], - - // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", - FarmGame: "torch1-" - }, - - "Farmer 2": { // Farmer profile name - // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], - KeyFinderProfiles: ["mf 3", "mf 4"], - - // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", - FarmGame: "torch2-" - } - */ - - // Edit here! - - "PROFILE NAME": { // Farmer profile name - // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], - KeyFinderProfiles: [""], - - // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", - FarmGame: "" - } - - // ################################################################################# - }, + // load configuration file + FarmerProfiles: Object.assign({}, require("./FarmerConfig", null, false)), // Don't touch inGame: false, @@ -53,7 +20,7 @@ const TorchSystem = { for (let i in this.FarmerProfiles) { if (this.FarmerProfiles.hasOwnProperty(i)) { for (let j = 0; j < this.FarmerProfiles[i].KeyFinderProfiles.length; j += 1) { - if (this.FarmerProfiles[i].KeyFinderProfiles[j].toLowerCase() === me.profile.toLowerCase()) { + if (String.isEqual(this.FarmerProfiles[i].KeyFinderProfiles[j], me.profile)) { this.FarmerProfiles[i].profile = i; list.push(this.FarmerProfiles[i]); @@ -105,7 +72,14 @@ const TorchSystem = { } delay(5000); - quit(); + + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } return true; } @@ -197,7 +171,7 @@ const TorchSystem = { outOfGameCheck: function () { if (!this.check) return false; - this.check = false; + TorchSystem.check = false; let game; @@ -238,7 +212,7 @@ const TorchSystem = { if (game) { delay(2000); - this.inGame = true; + TorchSystem.inGame = true; me.blockMouse = true; joinGame(game[0], game[1]); @@ -251,7 +225,7 @@ const TorchSystem = { delay(1000); } - this.inGame = false; + TorchSystem.inGame = false; return true; } @@ -269,14 +243,14 @@ const TorchSystem = { let neededItems = {pk1: 0, pk2: 0, pk3: 0, rv: 0}; // Check whether the killer is alone in the game - this.aloneInGame = function () { + const aloneInGame = function () { return (Misc.getPlayerCount() <= 1); }; // Check if current character is the farmer let farmer = TorchSystem.isFarmer(); - this.torchSystemEvent = function (mode, msg) { + const torchSystemEvent = function (mode, msg) { let obj, farmer; if (mode === 6) { @@ -318,56 +292,59 @@ const TorchSystem = { }; // Register event that will communicate with key hunters, go to Act 1 town and wait by stash - addEventListener("copydata", this.torchSystemEvent); + addEventListener("copydata", torchSystemEvent); Town.goToTown(1); Town.move("stash"); - while (true) { - // Abort if the current character isn't a farmer - if (!farmer) { - break; - } + try { + while (true) { + // Abort if the current character isn't a farmer + if (!farmer) { + break; + } - // Free up inventory - Town.needStash() && Town.stash(); + // Free up inventory + Town.needStash() && Town.stash(); - // Get the number keys - tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; - hkeys = me.findItems("pk2", sdk.items.mode.inStorage).length || 0; - dkeys = me.findItems("pk3", sdk.items.mode.inStorage).length || 0; + // Get the number keys + tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; + hkeys = me.findItems("pk2", sdk.items.mode.inStorage).length || 0; + dkeys = me.findItems("pk3", sdk.items.mode.inStorage).length || 0; - // Stop the loop if we have enough keys or if wait time expired - if (((tkeys >= 3 && hkeys >= 3 && dkeys >= 3) - || (Config.OrgTorch.WaitTimeout && (getTickCount() - timer > Config.OrgTorch.WaitTimeout * 1000 * 60))) - && this.aloneInGame()) { - removeEventListener("copydata", this.torchSystemEvent); + // Stop the loop if we have enough keys or if wait time expired + if (((tkeys >= 3 && hkeys >= 3 && dkeys >= 3) + || (Config.OrgTorch.WaitTimeout && (getTickCount() - timer > Time.minutes(Config.OrgTorch.WaitTimeout)))) + && aloneInGame()) { - break; - } + break; + } - if (busy) { - while (getTickCount() - busyTick < 30000) { - if (!this.aloneInGame()) { - break; + if (busy) { + while (getTickCount() - busyTick < 30000) { + if (!aloneInGame()) { + break; + } + + delay(100); } - delay(100); + if (getTickCount() - busyTick > 30000 || this.aloneInGame()) { + busy = false; + } } - if (getTickCount() - busyTick > 30000 || this.aloneInGame()) { - busy = false; + // Wait for other characters to leave + while (!aloneInGame()) { + delay(500); } - } - // Wait for other characters to leave - while (!this.aloneInGame()) { - delay(500); - } - - delay(1000); + delay(1000); - // Pick the keys after the hunters drop them and leave the game - Pickit.pickItems(); + // Pick the keys after the hunters drop them and leave the game + Pickit.pickItems(); + } + } finally { + removeEventListener("copydata", torchSystemEvent); } }, }; diff --git a/d2bs/kolbot/sdk/LocaleStringID.js b/d2bs/kolbot/sdk/LocaleStringID.js deleted file mode 100644 index 1bf479200..000000000 --- a/d2bs/kolbot/sdk/LocaleStringID.js +++ /dev/null @@ -1,7794 +0,0 @@ -/** -* @filename LocaleStringID.js -* @author Nishimura-Katsuo -* @desc locale string indexes from NameStr ids -*/ - -var 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 -}; diff --git a/d2bs/kolbot/sdk/Unit.d.ts b/d2bs/kolbot/sdk/Unit.d.ts deleted file mode 100644 index d80988e13..000000000 --- a/d2bs/kolbot/sdk/Unit.d.ts +++ /dev/null @@ -1,59 +0,0 @@ -/************************************* - * Unit description * - * Needs expansion * - *************************************/ -type ItemType = 4; -declare class Item extends Unit { - public type: ItemType; - getFlags() :number; - getFlag(flag: number) :boolean; - shop():boolean; - getItemCost() :number; -} - -type UnitType = 0 | 1 | 2 | 3 | 4 | 5; -type MonsterType = 1; - -declare class Monster extends Unit{ - public type: MonsterType; - getEnchant(type: number):boolean; -} - -declare class Merc extends Monster{ - -} -declare class Unit { - type : UnitType; - getNext() : Unit|false; - cancel() : void; - repair() : boolean; - useMenu() : boolean; - interact() : boolean; - interact(area: number) : boolean; - getItem(classId?: number,mode?: number, unitId?: number) : Unit|false; - getItem(name?: string,mode?: number, unitId?: number) : Unit|false; - getItems() : Item[]|false; - getMerc() : Merc ; - getMercHP() : number| false; - - // me.getSkill(0-4); // - getSkill(type: 0|1|2|3|4) : number; - getSkill(skillId: number,type: 0 | 1, item?: Item) : number; - - getParent() : Unit|false ; - overhead(msg: string) : void ; - - getStat(index: number,subid?: number):number[]|number|false ; - getState(index: number,subid?: number):number[]|number|false ; - - setSkill() ; - move(x: number, y: number) ; - getQuest(quest:number, subid: number) ; - getMinionCount() : number ; -} - -declare class me { - revive() : void; - getRepairCost() :number; -} - diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 17bc20b6c..d07cc09e7 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1,262 +1,938 @@ -declare type PathNode = { x: number, y: number } - -declare function getUnit(type?: number, name?: string, mode?: number, unitId?: number) -declare function getUnit(type?: number, classId?: number, mode?: number, unitId?: number) - -declare function getPath(area: number, fromX: number, fromY: number, toX: number, toY: number, reductionType: 0 | 1, radius: number): PathNode | false - -declare function getCollision(area: number, x: number, y: number) - -declare function getMercHP(): number - -declare function getCursorType(type: 1 | 3 | 6): boolean - -declare function getSkillByName(name: string): number - -declare function getSkillById(id: number): string - -declare function getLocaleString(id: number) - -// Never seen in the wild, not sure about arguments -declare function getTextSize(name: string, size: number) - -declare function getThreadPriority(): number - -declare function getUIFlag(flag: number): boolean - -declare function getTradeInfo(mode: 0 | 1 | 2): boolean - -declare function getWaypoint(id: number): boolean - -declare class Script { - getNext(): Script -} - -declare function getScript(name?: string): Script | false - -declare function getScripts(): Script | false - -declare class Room { - getNext(): Room | false; -} - -declare function getRoom(area: number, x: number, y: number): Room | false -declare function getRoom(x: number, y: number): Room | false -declare function getRoom(area: number): Room | false -declare function getRoom(): Room | false - -declare class Party { - getNext(): Party | false; -} - -declare function getParty(): Party | false - -declare class PresetUnit { - getNext(): PresetUnit | false -} - -declare function getPresetUnit(): PresetUnit | false - -declare function getPresetUnits(): PresetUnit[] | false - -declare class Area { - getNext(): Area | false; -} - -declare function getArea(): Area | false - -declare function getBaseStat(table: string, row: number, column: string): number | string -declare function getBaseStat(row: number, column: string): number | string - -declare class Control { - +// @ts-nocheck +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +// import sdk from "./types/sdk"; +// import { NPC, Town } from "./types/Town"; +// import * as Attack from "./types/Attack"; +// import * as Loader from "./types/Loader"; +// import * as Pather from "./types/Pather"; +// // import * as Misc from "./types/Misc"; +// import * as Skill from "./types/Skill"; +// import * as Pickit from "./types/Pickit"; +// import { Container, Storage } from "./types/Storage"; +// import * as Cubing from "./types/Cubing"; +// import { Runeword, Runewords, Roll } from "./types/Runewords"; +// import * as NTIP from "./types/NTIP"; +// import * as AutoMule from "./types/AutoMule"; +// import { D2Bot, DataFile, ControlAction } from "./types/OOG"; + +declare global { + interface Array { + includes(searchElement: T): boolean; + find(predicate: (value: T, index: number, obj: Int8Array) => boolean, thisArg?: any): T | undefined; + first(): T | undefined; + last(): T | undefined; + findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number; + intersection(other: T[]): T[]; + difference(other: T[]): T[]; + symmetricDifference(other: T[]): T[]; + flat(depth?: number): T[]; + compactMap(callback: (value: T, index: number, obj: T[]) => any, thisArg?: any): any[]; + filterHighDistance(step: number): any[] + isEqual(t: T[]): boolean + remove(val: T): T[] + random(): T; + } + + interface String { + lcsGraph(compareToThis: string): { a: string, b: string, graph: Uint16Array[]} + diffCount(a:string): number; + startsWith(a: string): boolean; + capitalize(downCase: boolean): string; + format(...pairs: Array): string; + } + + interface StringConstructor { + static isEqual(str1: string, str2: string): boolean; + } + + interface ObjectConstructor { + assign(target: T, source: U): T & U; + assign(target: T, source1: U, source2: V): T & U & V; + assign(target: T, source1: U, source2: V, source3: W): T & U & V & W; + assign(target: object, ...sources: any[]): any; + values(source: object): any[]; + entries(source: object): any[][]; + is(o1: any, o2: any): boolean; + } + + interface Object { + distance: number, + path: PathNode[] | undefined; + + setPrototypeOf(obj: object, proto: object) + } + + interface ObjectUnit extends Unit { + objtype: number; + } + + type actType = { initialized: boolean, spot: { [data: string]: [number, number] } }; + type potType = 'hp' | 'mp' | 'rv'; + + class Hook { + color: number; + visible: boolean; + + /** + * The horizontal alignment + * - 0 - Left + * - 1 - Right + * - 2 - Center + */ + align: number; + + /** + * The z-order of the Hook (what it covers up and is covered by). + */ + zorder: number; + + /** + * How much of the controls underneath the Hook should show through. + */ + opacity: number; + + /** + * Whether the Hook is in automap coordinate space (true) or screen coordinate space (false). + */ + automap: boolean; + + remove(): void; + } + + class Line extends Hook { + constructor(x: number, y: number, x2: number, y2: number, color: number, visible: boolean, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); + /** + * The first x coordinate of the Line. + */ + x: number; + + /** + * The first y coordinate of the Line. + */ + y: number; + + /** + * The end x coordinate of the Line. + */ + x2: number; + + /** + * The end y coordinate of the Line. + */ + y2: number; + } + + class Box extends Hook { + constructor(x: number, y: number, xsize: number, ysize: number, color: number, opacity: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); + /** + * The x coordinate (left) of the Box. + */ + x: number; + + /** + * The y coordinate (top) of the Box. + */ + y: number; + + /** + * The xsize (width) of the Box. + */ + xsize: number; + + /** + * The ysize (height) of the Box. + */ + ysize: number; + } + + class Frame extends Box { + } + + interface ClassAttack { + doAttack(unit: Monster, preattack?: boolean): number + afterAttack(any?: any): void + doCast(unit: Monster, timedSkill: number, untimedSkill: number): number + + // Self defined + decideSkill(unit: Monster, skipSkill?: number[]): [number, number] + } + + const FileTools: { + readText(filename: string) + writeText(filename: string, data: string) + appendText(filename: string, data: string) + exists(filename: string): Boolean; + } + + function getCollision(area: number, x: number, y: number, x2: number, y2: number) + + function getDistance(unit: PathNode, other: PathNode): number; + function getDistance(unit: PathNode, x: number, y: number): number; + + /************************************* + * Unit description * + * Needs expansion * + *************************************/ + + type UnitType = 0 | 1 | 2 | 3 | 4 | 5; + interface Unit { + readonly type: UnitType; + readonly classid: number; + readonly mode: number; + readonly name: string; + readonly act: any; + readonly gid: number; + readonly x: number; + readonly y: number; + readonly area: number; + readonly hp: number; + readonly hpmax: number; + readonly mp: number; + readonly mpmax: number; + readonly stamina: number; + readonly staminamax: number; + readonly charlvl: number; + readonly owner: number; + readonly ownertype: number; + readonly uniqueid: number; + + } + + class Unit { + readonly attackable: boolean; + readonly dead: boolean; + readonly islocked: boolean; + readonly distance: number; + + readonly targetx: number; + readonly targety: number; + readonly idle: boolean; + readonly isPlayer: boolean; + readonly isNPC: boolean; + readonly isMonster: boolean; + readonly attackable: boolean; + readonly rawStrength: number; + readonly rawDexterity: number; + readonly fireRes: number; + readonly coldRes: number; + readonly lightRes: number; + readonly poisonRes: number; + readonly hpPercent: number; + readonly prettyPrint: string; + + getStatEx(one: number, sub?: number): number; + getNext(): Unit | false; + cancel(number?: number): boolean; + repair(): boolean; + useMenu(): boolean; + interact(): boolean; + interact(area: number): boolean; + getItem(classId?: number, mode?: number, unitId?: number): ItemUnit | false; + getItem(name?: string, mode?: number, unitId?: number): ItemUnit | false; + getItems(...args: any[]): ItemUnit[] | false; + getItemsEx(classId?: number, mode?: number, unitId?: number): ItemUnit[]; + getItemsEx(name?: string, mode?: number, unitId?: number): ItemUnit[]; + getMerc(): MercUnit; + getMercHP(): number | false; + + // me.getSkill(0-4); // + getSkill(type: 0 | 1 | 2 | 3 | 4): number; + getSkill(skillId: number, type: 0 | 1, item?: ItemUnit): number; + + getParent(): Unit | string; + overhead(msg: string): void; + getStat(index: number, subid?: number, extra?: number): number; + getState(index: number, subid?: number): boolean; + setSkill(): boolean; + move(x: number, y: number): boolean; + getQuest(quest: number, subid: number): number + getMinionCount(): number; + inArea(area: number): boolean; + checkForMobs(givenSettings: { + range?: number; + count?: number; + coll?: number; + spectype: number + }): boolean + } + + type PlayerType = 0; + class Player extends Unit { + public type: PlayerType; + } + + type MonsterType = 1; + interface Monster extends Unit { + + } + + class Monster extends Unit { + public type: MonsterType; + readonly isChampion: boolean; + readonly isUnique: boolean; + readonly isMinion: boolean; + readonly isSuperUnique: boolean; + readonly isSpecial: boolean; + readonly isWalking: boolean; + readonly isRunning: boolean; + readonly isMoving: boolean; + readonly isChilled: boolean; + readonly isFrozen: boolean; + readonly currentVelocity: number; + readonly isPrimeEvil: boolean; + readonly isBoss: boolean; + readonly isGhost: boolean; + readonly isDoll: boolean; + readonly isMonsterObject: boolean; + readonly isMonsterEgg: boolean; + readonly isMonsterNest: boolean; + readonly isBaalTentacle: boolean; + readonly isShaman: boolean; + readonly isUnraveler: boolean; + readonly isFallen: boolean; + readonly isBeetle: boolean; + readonly extraStrong: boolean; + readonly extraFast: boolean; + readonly cursed: boolean; + readonly magicResistant: boolean; + readonly fireEnchanted: boolean; + readonly lightningEnchanted: boolean; + readonly coldEnchanted: boolean; + readonly manaBurn: boolean; + readonly teleportation: boolean; + readonly spectralHit: boolean; + readonly stoneSkin: boolean; + readonly multiShot: boolean; + readonly charlvl: number; + readonly spectype: number; + readonly curseable: boolean; + readonly scareable: boolean; + + getEnchant(type: number): boolean; + hasEnchant(...enchants: number): boolean + } + + class NPCUnit extends Unit { + public type: MonsterType; + readonly itemcount: number; + + openMenu(): boolean; + startTrade: (mode: any) => (any | boolean); + } + + class MercUnit extends Monster { + equip(destination: number | undefined, item: ItemUnit) + } + + type MissileType = 3; + class Missile extends Unit { + public readonly type: MissileType; + hits(position: PathNode): boolean; + } + + type ItemType = 4; + interface ItemUnit extends Unit { + + } + + class ItemUnit extends Unit { + // todo define item modes + public readonly type: ItemType; + readonly code: string; + readonly prefixes: string[]; + readonly suffixes: string[]; + readonly prefixnum: number; + readonly suffixnum: number; + readonly prefixenums: number[]; + readonly suffixnums: number[]; + readonly fname: string; + readonly quality: number; + readonly node: number; + readonly location: number; + readonly sizex: number; + readonly sizey: number; + readonly itemType: number; + readonly bodylocation: number; + readonly ilvl: number; + readonly lvlreq: number; + readonly gfx: number; + readonly description: string; + + // additional, not from d2bs + readonly identified: boolean; + readonly isEquipped: boolean + readonly dexreq: number + readonly strreq: number + readonly isInInventory: boolean; + readonly isInStash: boolean; + readonly isInCube: boolean; + readonly isInStorage: boolean; + readonly isInBelt: boolean; + readonly isOnMain: boolean; + readonly isOnSwap: boolean; + readonly runeword: boolean; + readonly questItem: boolean; + readonly ethereal: boolean; + readonly twoHanded: boolean + readonly oneOrTwoHanded: boolean; + readonly strictlyTwoHanded: boolean; + readonly sellable: boolean; + readonly lowQuality: boolean; + readonly normal: boolean; + readonly superior: boolean; + readonly magic: boolean; + readonly set: boolean; + readonly rare: boolean; + readonly unique: boolean; + readonly crafted: boolean; + readonly sockets: number; + readonly onGroundOrDropping: boolean; + readonly isShield: boolean; + readonly isAnni: boolean; + readonly isTorch: boolean; + readonly isGheeds: boolean; + + getColor(): number; + getBodyLoc(): number[]; + getFlags(): number; + getFlag(flag: number): boolean; + // shop(mode: ShopModes): boolean; + getItemCost(type?: 0 | 1 | 2): number; + sell(): boolean; + drop(): boolean; + equip(slot?: number): boolean; + buy(shift?: boolean, gamble?: boolean): boolean; + sellOrDrop():void + toCursor():boolean + } + + type TileType = 5; + class Tile extends Unit { + public type: TileType; + } + + interface MeType extends Unit { + public type: PlayerType; + readonly account: string; + readonly charname: string; + readonly diff: 0 | 1 | 2; + readonly maxdiff: 0 | 1 | 2; + readonly gamestarttime: number; + readonly gametype: 0 | 1; + readonly itemoncursor: boolean; + readonly ladder: number; + readonly ping: number; + readonly fps: number; + readonly locale: number; + readonly playertype: 0|1; + readonly realm: string; + readonly realmshort: string; + readonly mercrevivecost: number; + chickenhp: number; + chickenmp: number; + quitonhostile: boolean; + readonly gameReady: boolean; + readonly profile: string; + readonly pid: number; + readonly charflags: number; + readonly screensize: number; + readonly windowtitle: string; + readonly ingame: boolean; + quitonerror: boolean; + maxgametime: number; + readonly gamepassword: string; + readonly gamestarttime: number; + readonly gamename: string; + readonly gameserverip: string; + readonly itemcount: number; + readonly classid: 0 | 1 | 2 | 3 | 4 | 5 | 6; + readonly weaponswitch: 0|1; + readonly gameReady: boolean; + blockMouse: boolean; + blockKeys: boolean; + runwalk: number; + automap: boolean; + + readonly expansion: boolean; + readonly classic: boolean; + readonly softcore: boolean; + readonly hardcore: boolean; + readonly normal: boolean; + readonly nightmare: boolean; + readonly hell: boolean; + readonly sorceress: boolean; + readonly amazon: boolean; + readonly necromancer: boolean; + readonly paladin: boolean; + readonly barbarian: boolean; + readonly assassin: boolean; + readonly druid: boolean; + readonly hpPercent: number; + readonly mpPercent: number; + readonly gold: number; + readonly inTown: boolean; + readonly highestAct: 1 | 2 | 3 | 4 | 5; + readonly staminaDrainPerSec: number; + readonly staminaTimeLeft: number; + readonly staminaMaxDuration: number; + readonly inShop: boolean; + readonly skillDelay: boolean; + + inArea(area: number): boolean; + switchWeapons(slot: 0 | 1): void; + checkItem(itemInfo: { + classid?: number; + itemtype?: number; + quality?: number; + runeword?: boolean; + ethereal?: boolean; + name?: string | number; + equipped?: boolean | number; + }): {have: boolean; item: ItemUnit | null}; + haveSome(arg0: { name: number; equipped: boolean; }[]): any; + equip(destination: number | undefined, item: ItemUnit); + findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; + findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; + revive(): void; + getRepairCost(): number; + findItems(param: number, number?: number, number2?: number): ItemUnit[]; + usingShield(): boolean; + cancelUIFlags(): boolean; + } + + const me: MeType + + type PathNode = { x: number, y: number } + + function getUnit(type: 4, name?: string, mode?: number, unitId?: number): ItemUnit + function getUnit(type: 4, classId?: number, mode?: number, unitId?: number): ItemUnit + function getUnit(type: 1, name?: string, mode?: number, unitId?: number): Monster + function getUnit(type: 1, classId?: number, mode?: number, unitId?: number): Monster + function getUnit(type?: number, name?: string, mode?: number, unitId?: number): Unit + function getUnit(type?: number, classId?: number, mode?: number, unitId?: number): Unit + + function getPath(area: number, fromX: number, fromY: number, toX: number, toY: number, reductionType: 0 | 1 | 2, radius: number): PathNode[] | false + function getCollision(area: number, x: number, y: number) + function getMercHP(): number + function getCursorType(type: 1 | 3 | 6): boolean + function getCursorType(): number + function getSkillByName(name: string): number + function getSkillById(id: number): string + function getLocaleString(id: number): string + + // Never seen in the wild, not sure about arguments + function getTextSize(name: string, size: number) + function getThreadPriority(): number + function getUIFlag(flag: number): boolean + function getTradeInfo(mode: 0 | 1 | 2): boolean + function getWaypoint(id: number): boolean + + class Script { + running: boolean; + name: string; + type: boolean; + threadid: number; + memory: number; + + getNext(): Script; + pause(): boolean; + resume(): boolean; + join(): void; + stop(): boolean; + send(): void; + } + + function getScript(name?: string): Script | false + function getScripts(): Script | false + + class Room { + area: number; + correcttomb: number; + x: number; + y: number; + xsize: number; + ysize: number; + + getNext(): Room | false; + getNearby(): Room[]; + isInRoom(unit: PathNode):boolean + isInRoom(x:number, y:number):boolean + } + + function getRoom(area: number, x: number, y: number): Room | false + function getRoom(x: number, y: number): Room | false + function getRoom(area: number): Room | false + function getRoom(): Room | false + + class Party { + name: string; + gid: number; + level: number; + partyid: number; + area: number; + inTown: any; + + getNext(): Party | false; + } + + function getParty(unit?: Unit): Party | false + + class PresetUnit { + id: number; + x: number; + y: number; + roomx: number; + roomy: number; + + getNext(): PresetUnit | false; + realCoords(): { area: number, x: number, y: number }; + } + + function getPresetUnit(area?: number, objType?: number, classid?: number): PresetUnit | false + function getPresetUnit(area?: number, objType?: 2, classid?: number): PresetUnit | false + function getPresetUnits(area?: number, objType?: number, classid?: number): PresetUnit[] | false + + interface Exit extends Object { + x: number, + y: number, + type: number, + target: number, + tileid: number, + level: number, + } + + class Area { + name: string; + x: number; + xsize: number; + y: number; + ysize: number; + id: number; + exits: Exit[]; + + getNext(): Area | false; + } + + function getArea(id?: number): Area | false + function getBaseStat(table: string, row: number, column: string | number): number | string + function getBaseStat(row: number, column: string): number | string + + /** + * @todo get a better understanding of Control + */ + class Control { + /** + * The text of the control + */ + text: string; + + /** + * The x coordinate of the control + */ + x: number; + + /** + * The y coordinate of the control + */ + y: number; + + /** + * The xsize (width) of the control + */ + xsize: number; + + /** + * The ysize (height) of the control + */ + ysize: number; + + /** + * The state of the control + * - Disabled - 4 + * @todo figure out the rest + */ + state: number; + + /** + * Return whether or not the Control holds a password (starred out text). + */ + password: boolean; + + /** + * The type of control + * - 1 - TextBox + * - 2 - Image + * - 3 - Image2 + * - 4 - LabelBox + * - 5 - ScrollBar + * - 6 - Button + * - 7 - List + * - 8 - Timer + * - 9 - Smack + * - 10 - ProgressBar + * - 11 - Popup + * - 12 - AccountList + */ + type: number; + cursorpos: any; + selectstart: any; + selectend: any; + disabled: number; + + getNext(): Control | undefined; + click(x?: number, y?: number): void; + setText(text: string): void; + getText(): string[]; + } + + type Profile = { + type: number, + ip: number, + username: string, + gateway: string, + character: string, + difficulty: number, + maxloginTime: number, + maxCharacterSelectTime: number, + } + function Profile(): Profile; + + function getControl(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control | false + function getControls(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control[] + function getPlayerFlag(meGid: number, otherGid: number, type: number): boolean + function getTickCount(): number + function getInteractedNPC(): Monster | false + function getIsTalkingNPC(): boolean + function getDialogLines(): { handler() }[] | false + function print(what: string): void + function stringToEUC(arg: any): [] + function utf8ToEuc(arg: any): [] + function delay(ms: number): void + function load(file: string): boolean + function isIncluded(file: string): boolean + function include(file: string): boolean + function stacktrace(): true + function rand(from: number, to: number): number + function copy(what: string): void + function paste(): string + + function sendCopyData(noIdea: null, handle: number | string, mode: number, data: string): void; + + function sendDDE() + function keystate() + + type eventName = 'gamepacket' | 'scriptmsg' | 'copydata' | 'keyup' | 'keydown'; + + function addEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void + function addEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void + function addEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void + function addEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void + function addEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number|string) => void)): void + function addEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void + function addEventListener(eventType: eventName, callback: ((...args: any) => void)): void + + function removeEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void + function removeEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void + function removeEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void + function removeEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void + function removeEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number) => void)): void + function removeEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void + function removeEventListener(eventType: eventName, callback: ((...args: any) => void)): void + + function clearEvent() + function clearAllEvents() + function js_strict() + function version(): number + function scriptBroadcast(what: string | object): void + function sqlite_version() + function sqlite_memusage() + + function dopen(what?: string): { + getFiles(): string[]; + getFolders(): string[]; + create(what?: string): boolean; + } | false; + function debugLog(text: string): void + function showConsole(): void + function hideConsole(): void + + // out of game functions + function login(name?: string): void + function selectCharacter() + function createGame() + function joinGame() + function addProfile() + function getLocation():number + function loadMpq() + + // game functions that don't have anything to do with gathering data + function submitItem(): void + function getMouseCoords() + function copyUnit(unit: S): S + function clickMap(type: 0 | 1 | 2 | 3, shift: 0 | 1, x: number, y: number) + function acceptTrade() + function tradeOk() + function beep(id?: number) + + function clickItem(where: 0 | 1 | 2, bodyLocation: number) + function clickItem(where: 0 | 1 | 2, item: ItemUnit) + function clickItem(where: 0 | 1 | 2, x: number, y: number) + function clickItem(where: 0 | 1 | 2, x: number, y: number, location: number) + + function getDistance(a: Unit, b: Unit): number + function getDistance(a: Unit, toX: number, toY: number): number + function getDistance(fromX: number, fromY: number, b: Unit): number + function getDistance(fromX: number, fromY: number, toX: number, toY: number): number + + function gold(amount: number, changeType?: 0 | 1 | 2 | 3 | 4): void + function checkCollision(a: Unit, b: Unit, type: number): boolean + function playSound(num: number): void + function quit(): never + function quitGame(): never + function say(what: string): void + function clickParty(player: Party, type: 0 | 1 | 2 | 3 | 4) + function weaponSwitch(): void + function transmute(): void + function useStatPoint(type: number): void + function useSkillPoint(type: number): void + function takeScreenshot(): void + function moveNPC(npc: Monster, x: number, y: number): void + + function getPacket(buffer: ArrayBuffer): void + function getPacket(...args: { size: number, data: number }[]): void + + function sendPacket(buffer: ArrayBuffer): void + function sendPacket(...number: number[]): void + + function getIP(): string + function sendKey(key: number): void + function revealLevel(unknown: true): void + + // hash functions + function md5(str: string): string + function sha1(str: string): string + function sha256(str: string): string + function sha384(str: string): string + function sha512(str: string): string + function md5_file(str: string): string + function sha1_file(str: string): string + function sha256_file(str: string): string + function sha384_file(str: string): string + function sha512_file(str: string): string + + interface Console { + static log(...whatever: any[]): void + static debug(...whatever: any[]): void + static warn(...whatever: any[]): void + static error(...whatever: any[]): void + static time(name: string): void; + static timeEnd(name: string): void; + static trace(): void; + static info(start: boolean, msg: string, timer: string): void; + } + const console: Console; + + function includeIfNotIncluded(file?: string): boolean; + function includeCoreLibs(obj: { exclude: string[] }): boolean; + function includeSystemLibs(): boolean; + function clone(obj: Date | any[] | object): ThisParameterType; + function copyObj(from: object): object; + + interface StarterConfig { + MinGameTime: number, + PingQuitDelay: number, + CreateGameDelay: number, + ResetCount: number + CharacterDifference: number, + MaxPlayerCount: number, + StopOnDeadHardcore: boolean, + + JoinChannel: string, + FirstJoinMessage: string, + ChatActionsDelay: number, + AnnounceGames: boolean, + AnnounceMessage: string, + AfterGameMessage: string, + + InvalidPasswordDelay: number, // Minutes to wait after getting Invalid Password message + VersionErrorDelay: number, // Seconds to wait after 'unable to identify version' message + SwitchKeyDelay: number, // Seconds to wait before switching a used/banned key or after realm down + CrashDelay: number, // Seconds to wait after a d2 window crash + FTJDelay: number, // Seconds to wait after failing to create a game + RealmDownDelay: number, // Minutes to wait after getting Realm Down message + UnableToConnectDelay: number, // Minutes to wait after Unable To Connect message + TCPIPNoHostDelay: number, // Seconds to wait after Cannot Connect To Server message + CDKeyInUseDelay: number, // Minutes to wait before connecting again if CD-Key is in use. + ConnectingTimeout: number, // Seconds to wait before cancelling the 'Connecting...' screen + PleaseWaitTimeout: number, // Seconds to wait before cancelling the 'Please Wait...' screen + WaitInLineTimeout: number, // Seconds to wait before cancelling the 'Waiting in Line...' screen + WaitOutQueueRestriction: boolean, // Wait out queue if we are restricted, queue time > 10000 + WaitOutQueueExitToMenu: boolean, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby + GameDoesNotExistTimeout: number, // Seconds to wait before cancelling the 'Game does not exist.' screen + } + + interface StarterInterface { + Config: StarterConfig, + useChat: boolean, + pingQuit: boolean, + inGame: boolean, + firstLogin: boolean, + firstRun: boolean, + isUp: "yes"|"no", + loginRetry: number, + deadCheck: boolean, + chatActionsDone: boolean, + gameStart: boolean, + gameCount: number, + lastGameStatus: string, + handle: number | null, + connectFail: boolean, + connectFailRetry: number, + makeAccount: false, + channelNotify: boolean, + chanInfo: { + joinChannel: string, + firstMsg: string, + afterMsg: string, + announce: boolean, + }, + gameInfo: { + error: string, + crashInfo: { + currScript: number, + area: number, + }, + switchKeys: boolean, + }, + joinInfo: {}, + profileInfo: {}, + + sayMsg(string: string): void, + timer(tick: number): string, + locationTimeout(time: number, location: number): boolean, + setNextGame(gameInfo: { gameName: string }): void, + updateCount(): void, + scriptMsgEvent(msg: string): void, + receiveCopyData(mode: number, msg: string | object): void, + randomString(len?: number, useNumbers?: boolean): string, + randomNumberString(len?: number): string, + } + + const Starter: StarterInterface; } - -declare function getControl(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control | false - -declare function getControls(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control[] - -declare function getPlayerFlag(meGid: number, otherGid: number, type: number): boolean - -declare function getTickCount(): number - -declare function getInteractedNPC(): Monster | false - -declare function getIsTalkingNPC(): boolean - -declare function getDialogLines(): { handler() }[] | false - -declare function print(what: string): void - -declare function stringToEUC(arg): [] - -declare function utf8ToEuc(arg): [] - -declare function delay(ms: number): void - -declare function load(file: string): boolean - -declare function isIncluded(file: string): boolean - -declare function include(file: string): boolean - -declare function stacktrace(): true - -declare function rand(from: number, to: number): number - -declare function copy(what: string): void - -declare function paste(): string - -declare function sendCopyData(noIdea: null, handle: number, mode: number, data: string) -declare function sendCopyData(noIdea: null, handle: string, mode: number, data: string) - -declare function sendDDE() - -declare function keystate() - -declare type eventName = 'gamepacket' | 'scriptmsg' | 'copydata' | 'keyup' | 'keydown' - -declare function addEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void -declare function addEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void -declare function addEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void -declare function addEventListener(eventType: 'itemaction', callback: ((gid:number,mode?:number,code?:string,global?:true) => void)): void -declare function addEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number) => void)): void -declare function addEventListener(eventType: 'chatmsg', callback: ((nick: string,msg:string) => void)): void -declare function addEventListener(eventType: eventName, callback: ((...args: any) => void)): void - -declare function removeEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void -declare function removeEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void -declare function removeEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void -declare function removeEventListener(eventType: 'itemaction', callback: ((gid:number,mode?:number,code?:string,global?:true) => void)): void -declare function removeEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number) => void)): void -declare function removeEventListener(eventType: 'chatmsg', callback: ((nick: string,msg:string) => void)): void -declare function removeEventListener(eventType: eventName, callback: ((...args: any) => void)): void - -declare function clearEvent() - -declare function clearAllEvents() - -declare function js_strict() - -declare function version():number - -declare function scriptBroadcast(what:string|object):void - -declare function sqlite_version() - -declare function sqlite_memusage() - -declare function dopen(path:string):false|{create(what:string)} - -declare function debugLog(text:string):void - -declare function showConsole():void - -declare function hideConsole():void - -// out of game functions - -declare function login(name?:string):void - -// -// declare function createCharacter()) -// this function is not finished - -declare function selectCharacter() - -declare function createGame() - -declare function joinGame() - -declare function addProfile() - -declare function getLocation() - -declare function loadMpq() - -// game functions that don't have anything to do with gathering data - -declare function submitItem():void - -declare function getMouseCoords() - -declare function copyUnit(unit: Unit):Unit - -declare function clickMap(type: 0|1|2|3,shift:0|1,x:number,y:number) - -declare function acceptTrade() - -declare function tradeOk() - -declare function beep(id?:number) - -declare function clickItem(where: 0|1|2,bodyLocation:number) -declare function clickItem(where: 0|1|2,item:Item) -declare function clickItem(where: 0|1|2,x:number,y:number) -declare function clickItem(where: 0|1|2,x:number,y:number,location:number) - -declare function getDistance(a: Unit,b: Unit):number -declare function getDistance(a: Unit,toX:number, toY: number):number -declare function getDistance(fromX: number, fromY: number,b: Unit):number -declare function getDistance(fromX: number, fromY: number,toX:number, toY: number):number - -declare function gold(amount: number,changeType?: 0|1|2|3|4):void - -declare function checkCollision(a: Unit,b:Unit,type:number):boolean - -declare function playSound(num:number):void - -declare function quit():never - -declare function quitGame():never - -declare function say(what:string):void - -declare function clickParty(player: Party,type: 0|1|2|3|4) - -declare function weaponSwitch():void - -declare function transmute():void - -declare function useStatPoint(type:number):void - -declare function useSkillPoint(type:number):void - -declare function takeScreenshot():void - -declare function moveNPC(npc:Monster,x:number,y:number):void - -declare function getPacket(buffer: DataView):void -declare function getPacket(...args: {size:number, data: number}[]):void - -declare function sendPacket(buffer: DataView):void -declare function sendPacket(...args: {size:number, data: number}[]):void - -declare function getIP():string - -declare function sendKey(key:number):void - -declare function revealLevel(unknown:true):void - -// hash functions - -declare function md5(str:string):string - -declare function sha1(str:string):string - -declare function sha256(str:string):string - -declare function sha384(str:string):string - -declare function sha512(str:string):string - -declare function md5_file(str:string):string - -declare function sha1_file(str:string):string - -declare function sha256_file(str:string):string - -declare function sha384_file(str:string):string - -declare function sha512_file(str:string):string \ No newline at end of file +export {}; diff --git a/d2bs/kolbot/sdk/Shrines.txt b/d2bs/kolbot/sdk/txt/Shrines.txt similarity index 100% rename from d2bs/kolbot/sdk/Shrines.txt rename to d2bs/kolbot/sdk/txt/Shrines.txt diff --git a/d2bs/kolbot/sdk/SuperUniques.txt b/d2bs/kolbot/sdk/txt/SuperUniques.txt similarity index 100% rename from d2bs/kolbot/sdk/SuperUniques.txt rename to d2bs/kolbot/sdk/txt/SuperUniques.txt diff --git a/d2bs/kolbot/sdk/areas.txt b/d2bs/kolbot/sdk/txt/areas.txt similarity index 100% rename from d2bs/kolbot/sdk/areas.txt rename to d2bs/kolbot/sdk/txt/areas.txt diff --git a/d2bs/kolbot/sdk/basestats.txt b/d2bs/kolbot/sdk/txt/basestats.txt similarity index 100% rename from d2bs/kolbot/sdk/basestats.txt rename to d2bs/kolbot/sdk/txt/basestats.txt diff --git a/d2bs/kolbot/sdk/bodylocations.txt b/d2bs/kolbot/sdk/txt/bodylocations.txt similarity index 100% rename from d2bs/kolbot/sdk/bodylocations.txt rename to d2bs/kolbot/sdk/txt/bodylocations.txt diff --git a/d2bs/kolbot/sdk/chests.txt b/d2bs/kolbot/sdk/txt/chests.txt similarity index 100% rename from d2bs/kolbot/sdk/chests.txt rename to d2bs/kolbot/sdk/txt/chests.txt diff --git a/d2bs/kolbot/sdk/enchants.txt b/d2bs/kolbot/sdk/txt/enchants.txt similarity index 100% rename from d2bs/kolbot/sdk/enchants.txt rename to d2bs/kolbot/sdk/txt/enchants.txt diff --git a/d2bs/kolbot/sdk/getskillinfo.txt b/d2bs/kolbot/sdk/txt/getskillinfo.txt similarity index 100% rename from d2bs/kolbot/sdk/getskillinfo.txt rename to d2bs/kolbot/sdk/txt/getskillinfo.txt diff --git a/d2bs/kolbot/sdk/itemflags.txt b/d2bs/kolbot/sdk/txt/itemflags.txt similarity index 100% rename from d2bs/kolbot/sdk/itemflags.txt rename to d2bs/kolbot/sdk/txt/itemflags.txt diff --git a/d2bs/kolbot/sdk/miscscreenmodes.txt b/d2bs/kolbot/sdk/txt/miscscreenmodes.txt similarity index 100% rename from d2bs/kolbot/sdk/miscscreenmodes.txt rename to d2bs/kolbot/sdk/txt/miscscreenmodes.txt diff --git a/d2bs/kolbot/sdk/modes.txt b/d2bs/kolbot/sdk/txt/modes.txt similarity index 100% rename from d2bs/kolbot/sdk/modes.txt rename to d2bs/kolbot/sdk/txt/modes.txt diff --git a/d2bs/kolbot/sdk/monster classID's.txt b/d2bs/kolbot/sdk/txt/monster classID's.txt similarity index 100% rename from d2bs/kolbot/sdk/monster classID's.txt rename to d2bs/kolbot/sdk/txt/monster classID's.txt diff --git a/d2bs/kolbot/sdk/npcmenuid.txt b/d2bs/kolbot/sdk/txt/npcmenuid.txt similarity index 100% rename from d2bs/kolbot/sdk/npcmenuid.txt rename to d2bs/kolbot/sdk/txt/npcmenuid.txt diff --git a/d2bs/kolbot/sdk/quests.txt b/d2bs/kolbot/sdk/txt/quests.txt similarity index 100% rename from d2bs/kolbot/sdk/quests.txt rename to d2bs/kolbot/sdk/txt/quests.txt diff --git a/d2bs/kolbot/sdk/roomstats.txt b/d2bs/kolbot/sdk/txt/roomstats.txt similarity index 100% rename from d2bs/kolbot/sdk/roomstats.txt rename to d2bs/kolbot/sdk/txt/roomstats.txt diff --git a/d2bs/kolbot/sdk/skills.txt b/d2bs/kolbot/sdk/txt/skills.txt similarity index 100% rename from d2bs/kolbot/sdk/skills.txt rename to d2bs/kolbot/sdk/txt/skills.txt diff --git a/d2bs/kolbot/sdk/states.txt b/d2bs/kolbot/sdk/txt/states.txt similarity index 100% rename from d2bs/kolbot/sdk/states.txt rename to d2bs/kolbot/sdk/txt/states.txt diff --git a/d2bs/kolbot/sdk/stats.txt b/d2bs/kolbot/sdk/txt/stats.txt similarity index 100% rename from d2bs/kolbot/sdk/stats.txt rename to d2bs/kolbot/sdk/txt/stats.txt diff --git a/d2bs/kolbot/sdk/stats_skills.txt b/d2bs/kolbot/sdk/txt/stats_skills.txt similarity index 100% rename from d2bs/kolbot/sdk/stats_skills.txt rename to d2bs/kolbot/sdk/txt/stats_skills.txt diff --git a/d2bs/kolbot/sdk/stats_tabs.txt b/d2bs/kolbot/sdk/txt/stats_tabs.txt similarity index 100% rename from d2bs/kolbot/sdk/stats_tabs.txt rename to d2bs/kolbot/sdk/txt/stats_tabs.txt diff --git a/d2bs/kolbot/sdk/superunique_presetunitids.txt b/d2bs/kolbot/sdk/txt/superunique_presetunitids.txt similarity index 100% rename from d2bs/kolbot/sdk/superunique_presetunitids.txt rename to d2bs/kolbot/sdk/txt/superunique_presetunitids.txt diff --git a/d2bs/kolbot/sdk/tile.d2l b/d2bs/kolbot/sdk/txt/tile.d2l similarity index 100% rename from d2bs/kolbot/sdk/tile.d2l rename to d2bs/kolbot/sdk/txt/tile.d2l diff --git a/d2bs/kolbot/sdk/uiflag.txt b/d2bs/kolbot/sdk/txt/uiflag.txt similarity index 100% rename from d2bs/kolbot/sdk/uiflag.txt rename to d2bs/kolbot/sdk/txt/uiflag.txt diff --git a/d2bs/kolbot/sdk/waypoints.txt b/d2bs/kolbot/sdk/txt/waypoints.txt similarity index 100% rename from d2bs/kolbot/sdk/waypoints.txt rename to d2bs/kolbot/sdk/txt/waypoints.txt diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts new file mode 100644 index 000000000..a41e76320 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -0,0 +1,66 @@ +export {}; +declare global { + interface AttackResult { + FAILED: 0, + SUCCESS: 1, + CANTATTACK: 2, // need to fix the ambiguity between this result and Failed + NEEDMANA: 3 + } + namespace Attack { + const infinity: boolean; + const auradin: boolean; + const monsterObjects: number[]; + const Result: AttackResult; + function init(): void; + function checkSlot(slot?: 0 | 1): boolean; + function getPrimarySlot(): 0 | 1; + function getCustomAttack(unit: Unit): boolean | [number, number]; + function getCharges(): boolean; + function checkInfinity(): boolean; + function checkAuradin(): boolean; + function canTeleStomp(unit: Unit): boolean; + function kill(classId: number | Unit): boolean; + function hurt(classId: string | number | Unit, percent: number): boolean; + function getScarinessLevel(unit: Unit): number; + function clear(range?: number, spectype?: number, bossId?: number | Unit, sortfunc?: Function, pickit?: boolean): boolean; + function clearClassids(...ids: number[]): boolean; + function getMob(classid: number, spectype: number, range: number, center: Unit | { + x: number; + y: number; + }): Monster[]; + function clearList(mainArg: Function | Unit[], sortFunc?: Function, refresh?: boolean): boolean; + function securePosition(x: number, y: number, range?: number, timer?: number, skipBlocked?: boolean, special?: boolean): void; + function markRoom(room: Room, color: number): void; + function countUniques(): void; + function storeStatistics(area: number): void; + function clearLevel(spectype?: number): boolean; + function sortMonsters(unitA: Unit, unitB: Unit): boolean; + function validSpot(x: number, y: number, skill?: number, unitid?: number): boolean; + function openChests(range: number, x?: number, y?: number): boolean; + function buildMonsterList(): [] | Monster[]; + function findSafeSpot(unit: Unit, distance: number, spread: number, range: number, ...args: any[]): { + x: number; + y: number; + }; + function deploy(unit: any, distance: any, spread: any, range: any, ...args: any[]): boolean; + function getMonsterCount(x: any, y: any, range: any, list: any): number; + function buildGrid(xmin: any, xmax: any, ymin: any, ymax: any, spread: any): { + x: any; + y: any; + coll: number; + }[]; + function skipCheck(unit: Unit): boolean; + function getSkillElement(skillId: number): false | "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"; + function getResist(unit: Unit, type: "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"): boolean; + function getLowerResistPercent(): number; + function getConvictionPercent(): number; + function checkResist(unit: any, val: any, maxres?: number): boolean; + function canAttack(unit: any): boolean; + function usingBow(): false | "bow" | "crossbow"; + function getIntoPosition(unit: any, distance: any, coll: any, walk: any): boolean; + function getNearestMonster(givenSettings?: {}): any; + function checkCorpse(unit: any): boolean; + function checkNearCorpses(unit: any, range?: number): any; + function whirlwind(unit: any): boolean; + } +} diff --git a/d2bs/kolbot/sdk/types/AutoMule.d.ts b/d2bs/kolbot/sdk/types/AutoMule.d.ts new file mode 100644 index 000000000..2035c529e --- /dev/null +++ b/d2bs/kolbot/sdk/types/AutoMule.d.ts @@ -0,0 +1,101 @@ +// @ts-nocheck +export {}; +declare global { + export type muleObj = { + /** + * - The name of mule profile in d2bot#. It will be started and stopped when needed. + */ + muleProfile: string; + /** + * - Account prefix. Numbers added automatically when making accounts. + */ + accountPrefix: string; + /** + * - Account password + */ + accountPassword: string; + /** + * - Character prefix. Suffix added automatically when making characters. + */ + charPrefix: string; + /** + * - Available options: "useast", "uswest", "europe", "asia" + */ + realm: string; + /** + * - expansion character + */ + expansion: boolean; + /** + * - ladder character + */ + ladder: boolean; + /** + * - Maximum number of mules to create per account (between 1 to 18) + */ + charsPerAcc: number; + /** + * - Game name and password of the mule game. Never use the same game name as for mule logger. + */ + muleGameName: string[]; + /** + * - List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"] + */ + enabledProfiles: string[]; + /** + * - Stop a profile prior to muling. Useful when running 8 bots without proxies. + */ + stopProfile: string; + /** + * - true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. + */ + stopProfileKeyRelease: boolean; + /** + * - Trigger muling at the end of a game if used space in stash greater than or equal to given percent. + */ + usedStashTrigger: number; + /** + * - Trigger muling at the end of a game if used space in inventory greater than or equal to given percent. + */ + usedInventoryTrigger: number; + /** + * - Mule items that have been stashed at some point but are no longer in pickit. + */ + muleOrphans: boolean; + /** + * - Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. + */ + continuousMule: boolean; + /** + * - Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. + */ + skipMuleResponse: boolean; + /** + * - Only log character when full, solves an issue with droppers attempting to use characters who are already in game + */ + onlyLogWhenFull: boolean; + }; + export const AutoMule: { + Mules: { + [x: string]: muleObj; + }; + TorchAnniMules: { + [x: string]: muleObj; + }; + getInfo(): boolean | muleObj + muleCheck(): void + getMule(): void + outOfGameCheck(): void + inGameCheck(): void + dropStuff(): void + matchItem(item: ItemUnit, list: any): void + getMuleItems(): ItemUnit[] + utilityIngredient(item: ItemUnit): void + cubingIngredient(item: ItemUnit): void + runewordIngredient(item: ItemUnit): void + dropCharm(dropAnni: any): void + getMaster(info: any): void + getMuleObject(mode: any, master: any): muleObj | undefined + getMuleFilename(mode: any, master: any): void + } +} diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts new file mode 100644 index 000000000..50fb43ae1 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -0,0 +1,562 @@ +/** +* @filename Config.js +* @author kolton +* @desc config loading and default config values storage +* +*/ + +declare global { + // interface Scripts { [data: string]: Partial | boolean } + namespace Config { + function init(notify: any): void; + const Loaded: boolean; + const DebugMode: boolean; + const StartDelay: number; + const PickDelay: number; + const AreaDelay: number; + const MinGameTime: number; + const MaxGameTime: number; + const LifeChicken: number; + const ManaChicken: number; + const UseHP: number; + const UseMP: number; + const UseRejuvHP: number; + const UseRejuvMP: number; + const UseMercHP: number; + const UseMercRejuv: number; + const MercChicken: number; + const IronGolemChicken: number; + const HealHP: number; + const HealMP: number; + const HealStatus: boolean; + const TownHP: number; + const TownMP: number; + namespace StackThawingPots { + const enabled: boolean; + const quantity: number; + } + namespace StackAntidotePots { + const enabled_1: boolean; + export { enabled_1 as enabled }; + const quantity_1: number; + export { quantity_1 as quantity }; + } + namespace StackStaminaPots { + const enabled_2: boolean; + export { enabled_2 as enabled }; + const quantity_2: number; + export { quantity_2 as quantity }; + } + const AutoMap: boolean; + const LastMessage: string; + const UseMerc: boolean; + const MercWatch: boolean; + const LowGold: number; + const StashGold: number; + namespace FieldID { + const Enabled: boolean; + const PacketID: boolean; + const UsedSpace: number; + } + namespace DroppedItemsAnnounce { + const Enable: boolean; + const Quality: any[]; + const LogToOOG: boolean; + const OOGQuality: any[]; + } + namespace CainID { + const Enable_1: boolean; + export { Enable_1 as Enable }; + export const MinGold: number; + export const MinUnids: number; + } + const Inventory: number[][]; + namespace LocalChat { + const Enabled_1: boolean; + export { Enabled_1 as Enabled }; + export const Toggle: boolean; + export const Mode: number; + } + const Silence: boolean; + const PublicMode: boolean; + const PartyAfterScript: boolean; + const Greetings: any[]; + const DeathMessages: any[]; + const Congratulations: any[]; + const ShitList: boolean; + const UnpartyShitlisted: boolean; + const Leader: string; + const QuitList: any[]; + const QuitListMode: number; + const QuitListDelay: any[]; + const HPBuffer: number; + const MPBuffer: number; + const RejuvBuffer: number; + const PickRange: number; + const MakeRoom: boolean; + const ClearInvOnStart: boolean; + const FastPick: boolean; + const ManualPlayPick: boolean; + namespace OpenChests { + const Enabled_2: boolean; + export { Enabled_2 as Enabled }; + export const Range: number; + export const Types: string[]; + } + const PickitFiles: any[]; + const BeltColumn: any[]; + const MinColumn: any[]; + const SkipId: any[]; + const SkipEnchant: any[]; + const SkipImmune: any[]; + const SkipAura: any[]; + const SkipException: any[]; + const ScanShrines: any[]; + const Debug: boolean; + namespace AutoMule { + const Trigger: any[]; + const Force: any[]; + const Exclude: any[]; + } + const ItemInfo: boolean; + const ItemInfoQuality: any[]; + const LogKeys: boolean; + const LogOrgans: boolean; + const LogLowRunes: boolean; + const LogMiddleRunes: boolean; + const LogHighRunes: boolean; + const LogLowGems: boolean; + const LogHighGems: boolean; + const SkipLogging: any[]; + const ShowCubingInfo: boolean; + const Cubing: boolean; + const CubeRepair: boolean; + const RepairPercent: number; + const Recipes: any[]; + const MakeRunewords: boolean; + const Runewords: any[]; + const KeepRunewords: any[]; + const Gamble: boolean; + const GambleItems: any[]; + const GambleGoldStart: number; + const GambleGoldStop: number; + const MiniShopBot: boolean; + const TeleSwitch: boolean; + const MFSwitchPercent: number; + const PrimarySlot: number; + const LogExperience: boolean; + const TownCheck: boolean; + const PingQuit: { + Ping: number; + Duration: number; + }[]; + const PacketShopping: boolean; + const FCR: number; + const FHR: number; + const FBR: number; + const IAS: number; + const PacketCasting: number; + const WaypointMenu: boolean; + const AntiHostile: boolean; + const RandomPrecast: boolean; + const HostileAction: number; + const TownOnHostile: boolean; + const ViperCheck: boolean; + const StopOnDClone: boolean; + const SoJWaitTime: number; + const KillDclone: boolean; + const DCloneQuit: boolean; + const DCloneWaitTime: number; + const FastParty: boolean; + const AutoEquip: boolean; + const ChampionBias: number; + const UseCta: boolean; + const Dodge: boolean; + const DodgeRange: number; + const DodgeHP: number; + const AttackSkill: any[]; + const LowManaSkill: any[]; + const CustomAttack: {}; + const TeleStomp: boolean; + const NoTele: boolean; + const ClearType: boolean; + const ClearPath: boolean; + const BossPriority: boolean; + const MaxAttackCount: number; + const LightningFuryDelay: number; + const UseInnerSight: boolean; + const UseSlowMissiles: boolean; + const UseDecoy: boolean; + const SummonValkyrie: boolean; + const UseTelekinesis: boolean; + const CastStatic: boolean; + const StaticList: any[]; + const UseEnergyShield: boolean; + const UseColdArmor: boolean; + const Golem: number; + const ActiveSummon: boolean; + const Skeletons: number; + const SkeletonMages: number; + const Revives: number; + const ReviveUnstackable: boolean; + const PoisonNovaDelay: number; + const Curse: any[]; + const CustomCurse: any[]; + const ExplodeCorpses: number; + const Redemption: number[]; + const Charge: boolean; + const Vigor: boolean; + const AvoidDolls: boolean; + const FindItem: boolean; + const FindItemSwitch: boolean; + const UseWarcries: boolean; + const Wereform: number; + const SummonRaven: number; + const SummonAnimal: number; + const SummonVine: number; + const SummonSpirit: number; + const UseTraps: boolean; + const Traps: any[]; + const BossTraps: any[]; + const UseFade: boolean; + const UseBoS: boolean; + const UseVenom: boolean; + const UseBladeShield: boolean; + const UseCloakofShadows: boolean; + const AggressiveCloak: boolean; + const SummonShadow: boolean; + const CustomClassAttack: string; + namespace MapMode { + const UseOwnItemFilter: boolean; + } + const MFLeader: boolean; + namespace Mausoleum { + const KillBishibosh: boolean; + const KillBloodRaven: boolean; + const ClearCrypt: boolean; + } + namespace Cows { + const DontMakePortal: boolean; + const JustMakePortal: boolean; + const KillKing: boolean; + } + namespace Tombs { + const KillDuriel: boolean; + } + namespace Eldritch { + const OpenChest: boolean; + const KillSharptooth: boolean; + const KillShenk: boolean; + const KillDacFarren: boolean; + } + namespace Pindleskin { + const UseWaypoint: boolean; + const KillNihlathak: boolean; + const ViperQuit: boolean; + } + namespace Nihlathak { + const ViperQuit_1: boolean; + export { ViperQuit_1 as ViperQuit }; + const UseWaypoint_1: boolean; + export { UseWaypoint_1 as UseWaypoint }; + } + namespace Pit { + const ClearPath_1: boolean; + export { ClearPath_1 as ClearPath }; + export const ClearPit1: boolean; + } + namespace Snapchip { + const ClearIcyCellar: boolean; + } + namespace Frozenstein { + const ClearFrozenRiver: boolean; + } + namespace Rakanishu { + const KillGriswold: boolean; + } + namespace AutoBaal { + const Leader_1: string; + export { Leader_1 as Leader }; + export const FindShrine: boolean; + export const LeechSpot: number[]; + export const LongRangeSupport: boolean; + } + namespace KurastChests { + const LowerKurast: boolean; + const Bazaar: boolean; + const Sewers1: boolean; + const Sewers2: boolean; + } + namespace Countess { + const KillGhosts: boolean; + } + namespace Baal { + const DollQuit: boolean; + const SoulQuit: boolean; + const KillBaal: boolean; + const HotTPMessage: string; + const SafeTPMessage: string; + const BaalMessage: string; + } + namespace BaalAssistant { + const KillNihlathak_1: boolean; + export { KillNihlathak_1 as KillNihlathak }; + export const FastChaos: boolean; + export const Wait: number; + export const Helper: boolean; + export const GetShrine: boolean; + export const GetShrineWaitForHotTP: boolean; + const DollQuit_1: boolean; + export { DollQuit_1 as DollQuit }; + const SoulQuit_1: boolean; + export { SoulQuit_1 as SoulQuit }; + export const SkipTP: boolean; + export const WaitForSafeTP: boolean; + const KillBaal_1: boolean; + export { KillBaal_1 as KillBaal }; + const HotTPMessage_1: any[]; + export { HotTPMessage_1 as HotTPMessage }; + const SafeTPMessage_1: any[]; + export { SafeTPMessage_1 as SafeTPMessage }; + const BaalMessage_1: any[]; + export { BaalMessage_1 as BaalMessage }; + export const NextGameMessage: any[]; + } + namespace BaalHelper { + const Wait_1: number; + export { Wait_1 as Wait }; + const KillNihlathak_2: boolean; + export { KillNihlathak_2 as KillNihlathak }; + const FastChaos_1: boolean; + export { FastChaos_1 as FastChaos }; + const DollQuit_2: boolean; + export { DollQuit_2 as DollQuit }; + const KillBaal_2: boolean; + export { KillBaal_2 as KillBaal }; + const SkipTP_1: boolean; + export { SkipTP_1 as SkipTP }; + } + namespace Corpsefire { + const ClearDen: boolean; + } + namespace Hephasto { + export const ClearRiver: boolean; + const ClearType_1: boolean; + export { ClearType_1 as ClearType }; + } + namespace Diablo { + const WalkClear: boolean; + const Entrance: boolean; + const JustViz: boolean; + const SealLeader: boolean; + const Fast: boolean; + const SealWarning: string; + const EntranceTP: string; + const StarTP: string; + const DiabloMsg: string; + const ClearRadius: number; + const SealOrder: string[]; + } + namespace DiabloHelper { + const Wait_2: number; + export { Wait_2 as Wait }; + const Entrance_1: boolean; + export { Entrance_1 as Entrance }; + export const SkipIfBaal: boolean; + const SkipTP_2: boolean; + export { SkipTP_2 as SkipTP }; + export const OpenSeals: boolean; + export const SafePrecast: boolean; + const ClearRadius_1: number; + export { ClearRadius_1 as ClearRadius }; + const SealOrder_1: string[]; + export { SealOrder_1 as SealOrder }; + export const RecheckSeals: boolean; + } + namespace MFHelper { + const BreakClearLevel: boolean; + } + namespace Wakka { + const Wait_3: number; + export { Wait_3 as Wait }; + export const StopAtLevel: number; + export const StopProfile: boolean; + const SkipIfBaal_1: boolean; + export { SkipIfBaal_1 as SkipIfBaal }; + } + namespace BattleOrders { + const Mode_1: number; + export { Mode_1 as Mode }; + export const Getters: any[]; + export const Idle: boolean; + export const QuitOnFailure: boolean; + export const SkipIfTardy: boolean; + const Wait_4: number; + export { Wait_4 as Wait }; + } + namespace BoBarbHelper { + const Mode_2: number; + export { Mode_2 as Mode }; + export const Wp: number; + } + namespace ControlBot { + export const Bo: boolean; + export namespace Cows_1 { + const MakeCows: boolean; + const GetLeg: boolean; + } + export { Cows_1 as Cows }; + export namespace Chant { + const Enchant: boolean; + const AutoEnchant: boolean; + } + export namespace Wps { + const GiveWps: boolean; + const SecurePortal: boolean; + } + export const EndMessage: string; + export const GameLength: number; + } + namespace IPHunter { + export const IPList: any[]; + const GameLength_1: number; + export { GameLength_1 as GameLength }; + } + namespace Follower { + const Leader_2: string; + export { Leader_2 as Leader }; + } + namespace Mephisto { + const MoatTrick: boolean; + const KillCouncil: boolean; + const TakeRedPortal: boolean; + } + namespace ShopBot { + const ScanIDs: any[]; + const ShopNPC: string; + const CycleDelay: number; + const QuitOnMatch: boolean; + } + namespace Coldworm { + const KillBeetleburst: boolean; + const ClearMaggotLair: boolean; + } + namespace Summoner { + const FireEye: boolean; + } + namespace AncientTunnels { + const OpenChest_1: boolean; + export { OpenChest_1 as OpenChest }; + export const KillDarkElder: boolean; + } + namespace OrgTorch { + const WaitForKeys: boolean; + const WaitTimeout: boolean; + const UseSalvation: boolean; + const GetFade: boolean; + const MakeTorch: boolean; + namespace PreGame { + namespace Thawing { + const Drink: number; + const At: any[]; + } + namespace Antidote { + const Drink_1: number; + export { Drink_1 as Drink }; + const At_1: any[]; + export { At_1 as At }; + } + } + } + namespace Synch { + const WaitFor: any[]; + } + namespace TristramLeech { + const Leader_3: string; + export { Leader_3 as Leader }; + const Helper_1: boolean; + export { Helper_1 as Helper }; + const Wait_5: number; + export { Wait_5 as Wait }; + } + namespace TravincalLeech { + const Leader_4: string; + export { Leader_4 as Leader }; + const Helper_2: boolean; + export { Helper_2 as Helper }; + const Wait_6: number; + export { Wait_6 as Wait }; + } + namespace Tristram { + export const PortalLeech: boolean; + const WalkClear_1: boolean; + export { WalkClear_1 as WalkClear }; + } + namespace Travincal { + const PortalLeech_1: boolean; + export { PortalLeech_1 as PortalLeech }; + } + namespace SkillStat { + const Skills: any[]; + } + namespace Bonesaw { + const ClearDrifterCavern: boolean; + } + namespace ChestMania { + const Act1: any[]; + const Act2: any[]; + const Act3: any[]; + const Act4: any[]; + const Act5: any[]; + } + namespace ClearAnyArea { + const AreaList: any[]; + } + namespace Rusher { + export const WaitPlayerCount: number; + export const Cain: boolean; + export const Radament: boolean; + export const LamEsen: boolean; + export const Izual: boolean; + export const Shenk: boolean; + export const Anya: boolean; + export const HellAncients: boolean; + const GiveWps_1: boolean; + export { GiveWps_1 as GiveWps }; + export const LastRun: string; + } + namespace Rushee { + const Quester: boolean; + const Bumper: boolean; + } + namespace Questing { + const StopProfile_1: boolean; + export { StopProfile_1 as StopProfile }; + } + namespace AutoSkill { + const Enabled_3: boolean; + export { Enabled_3 as Enabled }; + export const Build: any[]; + export const Save: number; + } + namespace AutoStat { + const Enabled_4: boolean; + export { Enabled_4 as Enabled }; + const Build_1: any[]; + export { Build_1 as Build }; + const Save_1: number; + export { Save_1 as Save }; + export const BlockChance: number; + export const UseBulk: boolean; + } + namespace AutoBuild { + const Enabled_5: boolean; + export { Enabled_5 as Enabled }; + export const Template: string; + export const Verbose: boolean; + const DebugMode_1: boolean; + export { DebugMode_1 as DebugMode }; + } + } +} +export {}; diff --git a/d2bs/kolbot/sdk/types/Cubing.d.ts b/d2bs/kolbot/sdk/types/Cubing.d.ts new file mode 100644 index 000000000..274c6cb7e --- /dev/null +++ b/d2bs/kolbot/sdk/types/Cubing.d.ts @@ -0,0 +1,23 @@ +export {}; +declare global { + const Cubing: { + init(): void + buildGemList(): void + getCube(): void + buildRecipes(): void + buildLists(): void + clearSubRecipes(): void + update(): void + checkRecipe(recipe: any): void + getRecipeNeeds(index: any): void + checkItem(unit: any): boolean + keepItem(unit: any): boolean + validItem(unit: any, recipe: any): void + doCubing(): void + cursorCheck(): void + openCube(): void + closeCube(): void + emptyCube(): void + makeRevPots(): void + } +} diff --git a/d2bs/kolbot/sdk/types/Item.d.ts b/d2bs/kolbot/sdk/types/Item.d.ts new file mode 100644 index 000000000..6da8026fd --- /dev/null +++ b/d2bs/kolbot/sdk/types/Item.d.ts @@ -0,0 +1,12 @@ +export {}; +declare global { + const Item: { + hasTier(item: any): void + canEquip(item: any): void + equip(item: any, bodyLoc: any): void + getEquippedItem(bodyLoc: any): void + getBodyLoc(item: any): void + autoEquipCheck(item: any): boolean + autoEquip(): void + } +} diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts new file mode 100644 index 000000000..c39c6aeba --- /dev/null +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -0,0 +1,16 @@ +export {}; +declare global { + const Loader: { + fileList: string[], + scriptList: string[], + scriptIndex: number, + skipTown: string[], + + init: () => void, + getScripts: () => void, + clone: (obj: any) => void, + copy: (from: any, to: any) => void, + loadScripts: () => void, + scriptName: (offset?: number) => void, + } +} diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts new file mode 100644 index 000000000..6a790215a --- /dev/null +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -0,0 +1,38 @@ + +declare namespace Misc { + const screenshotErrors: any; + const errorConsolePrint: any; + const useItemLog: boolean; + + function click(button: number, shift: number, unit: Unit): void; + function click(button: number, shift: number, x: Unit, y: undefined): void; + function inMyParty(name: any): void; + function findPlayer(name: any): void; + function getPlayerUnit(name: any): void; + function getPlayerAct(player: any): void; + function getNearbyPlayerCount(): void; + function getPlayerCount(): void; + function openChest(unit: any): boolean; + function openChestsInArea(area?: any, chestIds?: any): void; + function openChests(range: any): void; + function scanShrines(range: any): void; + function getShrine(unit: any): void; + function getShrinesInArea(area: any, type: any, use: any): void; + function getItemDesc(unit: any): void; + function getItemSockets(unit: any): void; + function itemLogger(action: string, unit: Unit, text?: string | undefined): void; + function logItem(action: string, unit: ItemUnit | undefined, keptLine?: any): void; + function skipItem(id: any): void; + function shapeShift(mode: any): void; + function unShift(): void; + function townCheck(boolean?: boolean): void; + function spy(name: any): void; + function fileAction(path: any, mode: any, msg: any): void; + function errorReport(error: Error | string, script?: string): void; + function debugLog(msg: any): void; + function useMenu(id: number): void; + function clone(obj: any): void; + function copy(from: any): void; + function poll(check: () => T, timeout?: number, sleep?: number): T; + function getUIFlags(excluded?: []): void; +} diff --git a/d2bs/kolbot/sdk/types/NTIP.d.ts b/d2bs/kolbot/sdk/types/NTIP.d.ts new file mode 100644 index 000000000..722adef2d --- /dev/null +++ b/d2bs/kolbot/sdk/types/NTIP.d.ts @@ -0,0 +1,7 @@ +export {}; +declare global { + const NTIP: { + OpenFile(string, boolean); + CheckItem(unit: Unit, entryList?: [] | false, verbose?: boolean) + } +} diff --git a/d2bs/kolbot/sdk/types/OOG.d.ts b/d2bs/kolbot/sdk/types/OOG.d.ts new file mode 100644 index 000000000..881ce0c9f --- /dev/null +++ b/d2bs/kolbot/sdk/types/OOG.d.ts @@ -0,0 +1,67 @@ +export {}; +declare global { + export const DataFile: { + create(): void + getObj(): void + getStats(): any + updateStats(arg: any, value?: any): void + } + + export const D2Bot: { + handle: number, + init(): void + sendMessage(handle: any, mode: any, msg: any): void + printToConsole(msg: string, color?: number, tooltip?: undefined, trigger?: undefined): void + printToItemLog(itemObj: any): void + uploadItem(itemObj: any): void + writeToFile(filename: any, msg: any): void + postToIRC(ircProfile: any, recepient: any, msg: any): void + ircEvent(mode: any): void + notify(msg: any): void + saveItem(itemObj: any): void + updateStatus(msg: any): void + updateRuns(): void + updateChickens(): void + updateDeaths(): void + requestGameInfo(): void + restart(keySwap?: boolean): void + CDKeyInUse(): void + CDKeyDisabled(): void + CDKeyRD(): void + stop(profile?: undefined, release?: undefined): void + start(profile: any): void + startSchedule(profile: any): void + stopSchedule(profile: any): void + updateCount(): void + shoutGlobal(msg: any, mode: any): void + heartBeat(): void + sendWinMsg(wparam: any, lparam: any): void + ingame(): void + joinMe(profile: any, gameName: any, gameCount: any, gamePass: any, isUp: any): void + requestGame(profile: any): void + getProfile(): void + setProfile(account: any, password: any, character: any, difficulty: any, realm: any, infoTag: any, gamePath: any): void + setTag(tag: any): void + store(info: any): void + retrieve(): void + remove(): void + } + + export const ControlAction: { + timeoutDelay(text: any, time: any, stopfunc?: any, arg?: any):void + click(type: any, x: any, y: any, xsize: any, ysize: any):void + setText(type: any, x: any, y: any, xsize: any, ysize: any, text: any):void + getText(type: any, x: any, y: any, xsize: any, ysize: any):string + joinChannel(channel: any):void + createGame(name: any, pass: any, diff: any, delay: any):void + clickRealm(realm: 0|1|2|3):void + loginAccount(info: any):void + makeAccount(info: any):void + findCharacter(info: any):void + getCharacters():void + getPosition():void + loginCharacter(info: any, startFromTop?: boolean):void + makeCharacter(info: any):void + getGameList():void + } +} diff --git a/d2bs/kolbot/sdk/types/Pather.d.ts b/d2bs/kolbot/sdk/types/Pather.d.ts new file mode 100644 index 000000000..e408d4a57 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Pather.d.ts @@ -0,0 +1,37 @@ +export {}; +declare global { + const Pather: { + wpAreas: number[]; + walkDistance: number; + teleDistance: number; + teleport: boolean, + cancelFlags: number[], + recursion: boolean, + lastPortalTick: 0, + getWalkDistance(x: number, y: number, area?: number, xx?: number, yy?: number, reductionType?: 0 | 1 | 2, radius?: number) + useTeleport(): boolean, + moveTo(x: number, y: number, retry?: number | undefined, clearPath?: boolean | undefined, pop?: boolean | undefined): boolean, + teleportTo(x: any, y: any, maxRange?: any): void, + walkTo(x: any, y: any, minDist?: number | undefined): boolean, + openDoors(x: any, y: any): boolean, + moveToUnit(unit: PathNode, offX?: undefined, offY?: undefined, clearPath?: undefined, pop?: undefined): boolean, + moveToPreset(area: any, unitType: any, unitId: any, offX?: any, offY?: any, clearPath?: any, pop?: any): boolean, + moveToExit(targetArea: any, use?: any, clearPath?: any): void, + getNearestRoom(area: any): void, + openExit(targetArea: any): void, + openUnit(type: any, id: any): void, + useUnit(type: any, id: any, targetArea: any): boolean, + useWaypoint(targetArea: number | null, check?: boolean): boolean + makePortal(use?: boolean | undefined): void, + usePortal(targetArea?: number | null, owner?: string | undefined, unit?: undefined): boolean, + getPortal(targetArea: any, owner?: any): ObjectUnit | false, + getNearestWalkable(x: any, y: any, range: any, step: any, coll: any, size?: any): [number, number] | false, + checkSpot(x: any, y: any, coll: any, cacheOnly: any, size: any): void, + accessToAct(act: number): boolean, + getWP(area: any, clearPath?: any): boolean, + journeyTo(area: any): boolean, + plotCourse(dest: any, src: any): false|{course:number[]}, + areasConnected(src: any, dest: any): void, + getAreaName(area: number): void, + } +} diff --git a/d2bs/kolbot/sdk/types/Pickit.d.ts b/d2bs/kolbot/sdk/types/Pickit.d.ts new file mode 100644 index 000000000..64df58769 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Pickit.d.ts @@ -0,0 +1,36 @@ +export {}; +declare global { + type PickitResult = { + UNID: -1, + UNWANTED: 0, + WANTED: 1, + CUBING: 2, + RUNEWORD: 3, + TRASH: 4, + CRAFTING: 5, + UTILITY: 6 + }; + const Pickit: { + gidList: number[], + invoLocked: boolean, + beltSize: 1 | 2 | 3 | 4, + ignoreLog: number[], // Ignored item types for item logging + Result: PickitResult, + tkable: number[], + essentials: number[], + + init: (notify: any) => void + itemEvent: (gid?: number, mode?: number, code?: number, global?: number) => void + sortItems: (unitA: Unit, unitB: Unit) => number + sortFastPickItems: (unitA: Unit, unitB: Unit) => number + checkBelt: () => boolean + canPick: (unit: ItemUnit) => boolean + checkItem: (unit: ItemUnit) => { result: PickitResult, line: null | number } + pickItem: (unit: ItemUnit, status?: PickitResult, keptLine?: any, retry?: number) => boolean + canMakeRoom: () => boolean + pickItems: (range?: number) => boolean + fastPick: () => boolean + itemQualityToName: (quality: any) => string + itemColor: (unit: Unit, type?: boolean) => string + } +} diff --git a/d2bs/kolbot/sdk/types/Runewords.d.ts b/d2bs/kolbot/sdk/types/Runewords.d.ts new file mode 100644 index 000000000..4b940454e --- /dev/null +++ b/d2bs/kolbot/sdk/types/Runewords.d.ts @@ -0,0 +1,105 @@ +export {}; +declare global { + export const Runewords: { + init(): void + validItem(item: any): void + buildLists(): void + update(classid: any, gid: any): void + checkRunewords(): void + checkItem(unit: any): boolean + keepItem(unit: any): boolean + getBase(runeword: any, base: any, ethFlag: any, reroll: any): void + socketItem(base: any, rune: any): void + getScroll(): void + makeRunewords(): void + rerollRunewords(): void + } + + export const Runeword: { + AncientsPledge: number[]; + Black: number[]; + Fury: number[]; + HolyThunder: number[]; + Honor: number[]; + KingsGrace: number[]; + Leaf: number[]; + Lionheart: number[]; + Lore: number[]; + Malice: number[]; + Melody: number[]; + Memory: number[]; + Nadir: number[]; + Radiance: number[]; + Rhyme: number[]; + Silence: number[]; + Smoke: number[]; + Stealth: number[]; + Steel: number[]; + Strength: number[]; + Venom: number[]; + Wealth: number[]; + White: number[]; + Zephyr: number[]; + Beast: number[]; + Bramble: number[]; + BreathoftheDying: number[]; + CallToArms: number[]; + ChainsofHonor: number[]; + Chaos: number[]; + CrescentMoon: number[]; + Delirium: number[]; + Doom: number[]; + Duress: number[]; + Enigma: number[]; + Eternity: number[]; + Exile: number[]; + Famine: number[]; + Gloom: number[]; + HandofJustice: number[]; + HeartoftheOak: number[]; + Kingslayer: number[]; + Passion: number[]; + Prudence: number[]; + Sanctuary: number[]; + Splendor: number[]; + Stone: number[]; + Wind: number[]; + Brand: number[]; + Death: number[]; + Destruction: number[]; + Dragon: number[]; + Dream: number[]; + Edge: number[]; + Faith: number[]; + Fortitude: number[]; + Grief: number[]; + Harmony: number[]; + Ice: number[]; + Infinity: number[]; + Insight: number[]; + LastWish: number[]; + Lawbringer: number[]; + Oath: number[]; + Obedience: number[]; + Phoenix: number[]; + Pride: number[]; + Rift: number[]; + Spirit: number[]; + VoiceofReason: number[]; + Wrath: number[]; + Bone: number[]; + Enlightenment: number[]; + Myth: number[]; + Peace: number[]; + Principle: number[]; + Rain: number[]; + Treachery: number[]; + Test: number[]; + } + + export const Roll: { + All: 0, + Eth: 1, + NonEth: 2 + } +} diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts new file mode 100644 index 000000000..893ec0c0b --- /dev/null +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -0,0 +1,44 @@ +export {}; +declare global { + function SkillData(skillId: number): void; + class SkillData { + constructor(skillId: number); + hardpoints: boolean; + checked: boolean; + manaCost: any; + condition: Function; + have(skill: number): any; + } + namespace Skill { + let usePvpRange: boolean; + const manaCostList: object; + const needFloor: number[]; + const missileSkills: number[]; + const charges: any[]; + const haveTK: boolean; + + namespace skills { + const all: { + [x: number]: SkillData; + }; + let initialized: boolean; + function init(): void; + function have(skill: number): boolean; + function reset(): void; + } + function init(): void; + function canUse(skillId: number): boolean; + function getDuration(skillId: number): number; + function getMaxSummonCount(skillId: number): number; + function getRange(skillId: any): number; + function getHand(skillId: any): number; + function cast(skillId: number, hand?: number, x?: number, y?: number, item?: ItemUnit | undefined): boolean; + function cast(skillId: number, hand?: number, unit?: Unit): boolean; + function setSkill(skillId: any, hand?: any, item?: any): boolean; + function isTimed(skillId: any): boolean; + function wereFormCheck(skillId: any): boolean; + function townSkill(skillId: any): boolean; + function getManaCost(skillId: any): number; + function useTK(unit: Unit): boolean; + } +} diff --git a/d2bs/kolbot/sdk/types/Storage.d.ts b/d2bs/kolbot/sdk/types/Storage.d.ts new file mode 100644 index 000000000..85629c831 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Storage.d.ts @@ -0,0 +1,95 @@ +// @ts-nocheck +export {}; +declare global { + function Container(name: string, width: number, height: number, location: number): void; + interface Container { + constructor(name: string, width: number, height: number, location: number): Container; + /** The name of the container */ + name: string; + /** The width of the container */ + width: number; + /** The height of the container */ + height: number; + /** The location of the container */ + location: number; + /** A 2D array to store the containers items */ + buffer: number[][]; + /** A list of the items in the container */ + itemList: ItemUnit[]; + /** The number of open positions in the container */ + openPositions: number; + + /** + * A function that marks an item in the container's buffer and adds it to the item list. + * @param item + */ + Mark(item: ItemUnit): boolean; + + /** + * A function that checks if an item is locked in the container. + * @param item + * @param baseRef + */ + IsLocked(item: ItemUnit, baseRef: number[][]): boolean + + /** + * A function that resets the container's buffer and item list. + */ + Reset(): void + + /** + * A function that checks if an item can fit in the container. + * @param item + */ + CanFit(item: ItemUnit): boolean + + /** + * A function that finds a spot for an item in the container. + * @param item + */ + FindSpot(item: ItemUnit): PathNode | false + + /** + * A function that moves an item to a location in a container + * @param item + */ + MoveTo(item: ItemUnit): boolean + + /** + * A function that dumps the information about the container to the console + */ + Dump(): void + + /** + * A function that returns the amount of space used in this container + */ + UsedSpacePercent(): void + + /** + * A function the returns an item list in comparison to a given reference array + * @param baseRef + */ + Compare(baseRef: number[][]): ItemUnit[] | false + + /** + * returns a string representation of the source object + * @deprecated + */ + toSource(): string + } + + type storage = { + StashY: 4 | 8 | 10; + Inventory: Container; + TradeScreen: Container; + Stash: Container; + Belt: Container; + Cube: Container; + InvRef: number[]; + + BeltSize(): 1 | 2 | 3 | 4; + Reload(): void; + Init(): void; + } + const Storage: storage; +} diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts new file mode 100644 index 000000000..a0434a0e7 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -0,0 +1,113 @@ +// @ts-nocheck +declare global { + type NPC = string; + export namespace NPC { + const Akara: string; + const Gheed: string; + const Charsi: string; + const Kashya: string; + const Warriv: string; + const Fara: string; + const Drognan: string; + const Elzix: string; + const Greiz: string; + const Lysander: string; + const Jerhyn: string; + const Meshif: string; + const Atma: string; + const Ormus: string; + const Alkor: string; + const Hratli: string; + const Asheara: string; + const Jamella: string; + const Halbu: string; + const Tyrael: string; + const Malah: string; + const Anya: string; + const Larzuk: string; + const Qual_Kehk: string; + const Nihlathak: string; + const Cain: string; + } + export namespace Town { + const telekinesis: boolean; + const sellTimer: number; + export namespace lastInteractedNPC { + const unit: Unit; + const tick: number; + function set(npc: Unit): void; + function get(): any; + function reset(): void; + } + const tasks: { + Heal: NPC; + Shop: NPC; + Gamble: NPC; + Repair: NPC; + Merc: NPC; + Key: NPC; + CainID: NPC; + }[]; + const ignoredItemTypes: any[]; + function needPotions(): boolean; + function doChores(repair?: boolean): boolean; + function npcInteract(name?: string, cancel?: boolean): boolean | Unit; + function checkQuestItems(): void; + function getTpTool(): ItemUnit; + function getIdTool(): ItemUnit; + function canTpToTown(): boolean; + function initNPC(task?: string, reason?: string): boolean | Unit; + function heal(): boolean; + function needHealing(): boolean; + function buyPotions(): boolean; + function shiftCheck(col: number, beltSize: 0 | 2 | 1 | 4 | 3): boolean; + function checkColumns(beltSize: 0 | 2 | 1 | 4 | 3): [number, number, number, number]; + function getPotion(npc: Unit, type: "hp" | "mp", highestPot?: 2 | 1 | 4 | 3 | 5): boolean | ItemUnit; + function fillTome(classid: number): boolean; + function checkScrolls(id: number): number; + function identify(): boolean; + function cainID(): boolean; + function fieldID(): boolean; + function getUnids(): false | ItemUnit[]; + function identifyItem(unit: ItemUnit, tome: ItemUnit, packetID?: boolean): boolean; + function shopItems(): boolean; + const gambleIds: any[]; + function gamble(): boolean; + function needGamble(): boolean; + function getGambledItem(list?: any[]): false | ItemUnit; + function buyPots(quantity?: number, type?: string | number, drink?: boolean, force?: boolean, npc?: Unit): boolean; + function drinkPots(type?: string | number, log?: boolean): { + potName: string; + quantity: number; + }; + function buyKeys(): boolean; + function checkKeys(): number; + function needKeys(): boolean; + function wantKeys(): boolean; + function repairIngredientCheck(item: ItemUnit): boolean; + function cubeRepair(): boolean; + function cubeRepairItem(item: ItemUnit): boolean; + function repair(force?: boolean): boolean; + function needRepair(): string[]; + function getItemsForRepair(repairPercent: number, chargedItems: boolean): ItemUnit[]; + function reviveMerc(): boolean; + function needMerc(): boolean; + function canStash(item: ItemUnit): boolean; + function stash(stashGold?: boolean): boolean; + function needStash(): boolean; + function openStash(): boolean; + function getCorpse(): boolean; + function checkShard(): boolean; + function clearBelt(): boolean; + function clearScrolls(): boolean; + function clearInventory(): boolean; + const act: {}[]; + function initialize(): boolean; + function getDistance(spot?: string): number; + function move(spot?: string, allowTK?: boolean): boolean; + function moveToSpot(spot?: string, allowTK?: boolean): boolean; + function goToTown(act?: 2 | 1 | 4 | 3 | 5, wpmenu?: boolean): boolean; + function visitTown(repair?: boolean): boolean; + } +} +export {}; diff --git a/d2bs/kolbot/sdk/types/Util.d.ts b/d2bs/kolbot/sdk/types/Util.d.ts new file mode 100644 index 000000000..b48dc95cc --- /dev/null +++ b/d2bs/kolbot/sdk/types/Util.d.ts @@ -0,0 +1,121 @@ +// @ts-nocheck +declare global { + /** + * @constructor + * @description new PacketBuilder() - create new packet object + * @example (Spoof 'reassign player' packet to client): + * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); + * @example (Spoof 'player move' packet to server): + * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); + * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` + * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` + */ + function PacketBuilder(): void; + class PacketBuilder { + /** @description size = 4 */ + float(a: number): this + /** @description size = 4 */ + dword(a: number): this + /** @description size = 2 */ + word(a: number): this + /** @description size = 1 */ + byte(a: number): this + string(a: any): this + send(): this + spoof(): this + } + + /** + * @class + * @classdesc A class for creating and sending copy data packets. + * @property {number} _mode - Defaults to 0, works for most D2Bot functions + * @property {number | string} _handle - Defaults to value of D2Bot.handle, works for any D2Bot + * functions that act on ourselves + * @example Request a game from "scl-sorc-001" profile + * new CopyData().handle("scl-sorc-001").mode(3).send(); + * @example Start mule profile "mule" + * new CopyData().data("start", ["mule"]).send(); + */ + function CopyData(): void; + class CopyData { + /** + * @private + * @type {string | number} - The handle to send the copy data to. + */ + private _handle: string | number; + + /** + * @private + * @type {number} - The mode of the copy data packet. + */ + private _mode: number; + + /** + * @private + * @type {string} - The data to send in the copy data + */ + private _data: string; + + /** + * - D2Bot.handle is for any functions that act on ourselves + * - Otherwise it is the D2Bot# profile name of the profile to act upon + * @param {string | number} handle - The handle or profile to send the copy data to. + */ + handle(handle: string | number): CopyData; + + /** + * - 0 is for most functions, and the default value set + * - 1 is for joinMe + * - 3 is for requestGame + * - 0xbbbb is for heartBeat + * @param {number} mode - The mode of the copy data packet. + */ + mode(mode: number): CopyData; + + /** + * @param {string} [func] - The function to call from D2Bot# + * @param {string[]} [args] - The additonal info needed for the function call + */ + data(func?: string, args?: string[]): CopyData; + send(): void; + } + + function getThreads(): Script[]; + function getUnits(...args: any[]): Unit[]; + function clickItemAndWait(...args: Args[]): boolean; + function clickUnitAndWait(button: number, shift: 0 | 1, unit: Unit): boolean; + const LocalChat: object; + const areaNames: string[]; + function getAreaName(area: number): string; + namespace Time { + function seconds(seconds: number): number; + function minutes(minutes: number): number; + function format(ms: number): string; + } + namespace Game { + function getDistance(...args: any[]): number; + + function getCursorUnit(): ItemUnit; + function getSelectedUnit(): ItemUnit; + function getPlayer(id: any, mode: any, gid: any): Player; + function getMonster(id?: string | number, mode?: number, gid?: number): Monster; + function getNPC(id?: string | number, mode?: number, gid?: number): Monster; + function getObject(id?: string | number, mode?: number, gid?: number): ObjectUnit; + function getMissile(id?: string | number, mode?: number, gid?: number): Missile; + function getItem(id?: string | number, mode?: number, gid?: number): ItemUnit; + function getStairs(id?: string | number, mode?: number, gid?: number): Tile; + function getPresetMonster(area: number, id: number): PresetUnit; + function getPresetMonsters(area: number, id: number): PresetUnit[]; + function getPresetObject(area: number, id: number): PresetUnit; + function getPresetObjects(area: number, id: number): PresetUnit[]; + function getPresetStair(area: number, id: number): PresetUnit; + function getPresetStairs(area: number, id: number): PresetUnit[]; + } + type Args = { + arg1: 0 | 1 | 2; + arg2: number | ItemUnit; + arg3?: number; + arg4?: number; + }; +} +export {}; diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts new file mode 100644 index 000000000..69e7a2320 --- /dev/null +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -0,0 +1,4693 @@ +declare global { + namespace sdk { + export namespace waypoints { + const Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; + const Act1: number[]; + const Act2: number[]; + const Act3: number[]; + const Act4: number[]; + const Act5: number[]; + } + + export namespace difficulty { + const Normal: 0; + const Nightmare: 1; + const Hell: 2; + const Difficulties: ["Normal", "Nightmare", "Hell"]; + + const nameOf: (diff: 0 | 1 | 2) => "Normal" | "Nightmare" | "Hell" | false; + } + + export namespace party { + const NoParty: 65535 + } + + export namespace clicktypes { + namespace click { + namespace item { + const Left: 0; + const Right: 1; + const ShiftLeft: 2; // For belt + const MercFromBelt: 3; // For belt + const Mercenary: 4 // Give to merc + } + namespace map { + const LeftDown: 0; + const LeftHold: 1; + const LeftUp: 2; + const RightDown: 3; + const RightHold: 4; + const RightUp: 5; + } + } + namespace shift { + const NoShift: 0; + const Shift: 1 + } + } + + export namespace cursortype { + const Empty: 1; + const ItemOnUnitHover: 3; // see notes + const ItemOnCursor: 4; // see notes + const Identify: 6; + const Repair: 7; + } + + export namespace collision { + const BlockWall: 0x01; + const LineOfSight: 0x02; + const Ranged: 0x04; + const PlayerToWalk: 0x08; + const DarkArea: 0x10; + const Casting: 0x20; + const Unknown: 0x40; + const Players: 0x80; + const Monsters: 0x100; + const Items: 0x200; + const Objects: 0x400; + const ClosedDoor: 0x800; + const IsOnFloor: 0x1000; + const MonsterIsOnFloor: 0x1100; + const MonsterIsOnFloorDarkArea: 0x1110; // in doorway + const FriendlyNPC: 0x2000; + const Unknown2: 0x4000; + const DeadBodies: 0x8000; + const MonsterObject: 0xFFFF; + const BlockMissile: 0x80E; + const WallOrRanged: 0x5; + const BlockWalk: 0x1805; + const FriendlyRanged: 0x2004; + } + + export namespace areas { + const Towns: [1, 40, 75, 103, 109]; + const None: 0; + + // Act 1 + const RogueEncampment: 1; + const BloodMoor: 2; + const ColdPlains: 3; + const StonyField: 4; + const DarkWood: 5; + const BlackMarsh: 6; + const TamoeHighland: 7; + const DenofEvil: 8; + const CaveLvl1: 9; + const UndergroundPassageLvl1: 10; + const HoleLvl1: 11; + const PitLvl1: 12; + const CaveLvl2: 13; + const UndergroundPassageLvl2: 14; + const HoleLvl2: 15; + const PitLvl2: 16; + const BurialGrounds: 17; + const Crypt: 18; + const Mausoleum: 19; + const ForgottenTower: 20; + const TowerCellarLvl1: 21; + const TowerCellarLvl2: 22; + const TowerCellarLvl3: 23; + const TowerCellarLvl4: 24; + const TowerCellarLvl5: 25; + const MonasteryGate: 26; + const OuterCloister: 27; + const Barracks: 28; + const JailLvl1: 29; + const JailLvl2: 30; + const JailLvl3: 31; + const InnerCloister: 32; + const Cathedral: 33; + const CatacombsLvl1: 34; + const CatacombsLvl2: 35; + const CatacombsLvl3: 36; + const CatacombsLvl4: 37; + const Tristram: 38; + const MooMooFarm: 39; + + // Act 2 + const LutGholein: 40; + const RockyWaste: 41; + const DryHills: 42; + const FarOasis: 43; + const LostCity: 44; + const ValleyofSnakes: 45; + const CanyonofMagic: 46; + const A2SewersLvl1: 47; + const A2SewersLvl2: 48; + const A2SewersLvl3: 49; + const HaremLvl1: 50; + const HaremLvl2: 51; + const PalaceCellarLvl1: 52; + const PalaceCellarLvl2: 53; + const PalaceCellarLvl3: 54; + const StonyTombLvl1: 55; + const HallsoftheDeadLvl1: 56; + const HallsoftheDeadLvl2: 57; + const ClawViperTempleLvl1: 58; + const StonyTombLvl2: 59; + const HallsoftheDeadLvl3: 60; + const ClawViperTempleLvl2: 61; + const MaggotLairLvl1: 62; + const MaggotLairLvl2: 63; + const MaggotLairLvl3: 64; + const AncientTunnels: 65; + const TalRashasTomb1: 66; + const TalRashasTomb2: 67; + const TalRashasTomb3: 68; + const TalRashasTomb4: 69; + const TalRashasTomb5: 70; + const TalRashasTomb6: 71; + const TalRashasTomb7: 72; + const DurielsLair: 73; + const ArcaneSanctuary: 74; + + // Act 3 + const KurastDocktown: 75; + const SpiderForest: 76; + const GreatMarsh: 77; + const FlayerJungle: 78; + const LowerKurast: 79; + const KurastBazaar: 80; + const UpperKurast: 81; + const KurastCauseway: 82; + const Travincal: 83; + const SpiderCave: 84; + const SpiderCavern: 85; + const SwampyPitLvl1: 86; + const SwampyPitLvl2: 87; + const FlayerDungeonLvl1: 88; + const FlayerDungeonLvl2: 89; + const SwampyPitLvl3: 90; + const FlayerDungeonLvl3: 91; + const A3SewersLvl1: 92; + const A3SewersLvl2: 93; + const RuinedTemple: 94; + const DisusedFane: 95; + const ForgottenReliquary: 96; + const ForgottenTemple: 97; + const RuinedFane: 98; + const DisusedReliquary: 99; + const DuranceofHateLvl1: 100; + const DuranceofHateLvl2: 101; + const DuranceofHateLvl3: 102; + + // Act 4 + const PandemoniumFortress: 103; + const OuterSteppes: 104; + const PlainsofDespair: 105; + const CityoftheDamned: 106; + const RiverofFlame: 107; + const ChaosSanctuary: 108; + + // Act 5 + const Harrogath: 109; + const BloodyFoothills: 110; + const FrigidHighlands: 111; + const ArreatPlateau: 112; + const CrystalizedPassage: 113; + const FrozenRiver: 114; + const GlacialTrail: 115; + const DrifterCavern: 116; + const FrozenTundra: 117; + const AncientsWay: 118; + const IcyCellar: 119; + const ArreatSummit: 120; + const NihlathaksTemple: 121; + const HallsofAnguish: 122; + const HallsofPain: 123; + const HallsofVaught: 124; + const Abaddon: 125; + const PitofAcheron: 126; + const InfernalPit: 127; + const WorldstoneLvl1: 128; + const WorldstoneLvl2: 129; + const WorldstoneLvl3: 130; + const ThroneofDestruction: 131; + const WorldstoneChamber: 132; + + // Ubers + const MatronsDen: 133; + const ForgottenSands: 134; + const FurnaceofPain: 135; + const UberTristram: 136; + + const actOf: (act: number) => 1 | 2 | 3 | 4 | 5; + const townOf: (townArea: number) => 1 | 40 | 75 | 103 | 109; + const townOfAct: (act: 1 | 2 | 3 | 4 | 5) => 1 | 40 | 75 | 103 | 109; + } + + export namespace skills { + namespace get { + const RightName: 0; + const LeftName: 1; + const RightId: 2; + const LeftId: 3; + const AllSkills: 4 + } + namespace hand { + const Right: 0; + const Left: 1; + const LeftNoShift: 2; + const RightShift: 3; + } + namespace subindex { + const HardPoints: 0; + const SoftPoints: 1 + } + // General + const Attack: 0; + const Kick: 1; + const Throw: 2; + const Unsummon: 3; + const LeftHandThrow: 4; + const LeftHandSwing: 5; + + // Amazon + const MagicArrow: 6; + const FireArrow: 7; + const InnerSight: 8; + const CriticalStrike: 9; + const Jab: 10; + const ColdArrow: 11; + const MultipleShot: 12; + const Dodge: 13; + const PowerStrike: 14; + const PoisonJavelin: 15; + const ExplodingArrow: 16; + const SlowMissiles: 17; + const Avoid: 18; + const Impale: 19; + const LightningBolt: 20; + const IceArrow: 21; + const GuidedArrow: 22; + const Penetrate: 23; + const ChargedStrike: 24; + const PlagueJavelin: 25; + const Strafe: 26; + const ImmolationArrow: 27; + const Dopplezon: 28; + const Decoy: 28; + const Evade: 29; + const Fend: 30; + const FreezingArrow: 31; + const Valkyrie: 32; + const Pierce: 33; + const LightningStrike: 34; + const LightningFury: 35; + + // Sorc + const FireBolt: 36; + const Warmth: 37; + const ChargedBolt: 38; + const IceBolt: 39; + const FrozenArmor: 40; + const Inferno: 41; + const StaticField: 42; + const Telekinesis: 43; + const FrostNova: 44; + const IceBlast: 45; + const Blaze: 46; + const FireBall: 47; + const Nova: 48; + const Lightning: 49; + const ShiverArmor: 50; + const FireWall: 51; + const Enchant: 52; + const ChainLightning: 53; + const Teleport: 54; + const GlacialSpike: 55; + const Meteor: 56; + const ThunderStorm: 57; + const EnergyShield: 58; + const Blizzard: 59; + const ChillingArmor: 60; + const FireMastery: 61; + const Hydra: 62; + const LightningMastery: 63; + const FrozenOrb: 64; + const ColdMastery: 65; + + // Necro + const AmplifyDamage: 66; + const Teeth: 67; + const BoneArmor: 68; + const SkeletonMastery: 69; + const RaiseSkeleton: 70; + const DimVision: 71; + const Weaken: 72; + const PoisonDagger: 73; + const CorpseExplosion: 74; + const ClayGolem: 75; + const IronMaiden: 76; + const Terror: 77; + const BoneWall: 78; + const GolemMastery: 79; + const RaiseSkeletalMage: 80; + const Confuse: 81; + const LifeTap: 82; + const PoisonExplosion: 83; + const BoneSpear: 84; + const BloodGolem: 85; + const Attract: 86; + const Decrepify: 87; + const BonePrison: 88; + const SummonResist: 89; + const IronGolem: 90; + const LowerResist: 91; + const PoisonNova: 92; + const BoneSpirit: 93; + const FireGolem: 94; + const Revive: 95; + + // Paladin + const Sacrifice: 96; + const Smite: 97; + const Might: 98; + const Prayer: 99; + const ResistFire: 100; + const HolyBolt: 101; + const HolyFire: 102; + const Thorns: 103; + const Defiance: 104; + const ResistCold: 105; + const Zeal: 106; + const Charge: 107; + const BlessedAim: 108; + const Cleansing: 109; + const ResistLightning: 110; + const Vengeance: 111; + const BlessedHammer: 112; + const Concentration: 113; + const HolyFreeze: 114; + const Vigor: 115; + const Conversion: 116; + const HolyShield: 117; + const HolyShock: 118; + const Sanctuary: 119; + const Meditation: 120; + const FistoftheHeavens: 121; + const Fanaticism: 122; + const Conviction: 123; + const Redemption: 124; + const Salvation: 125; + + // Barb + const Bash: 126; + const SwordMastery: 127; + const AxeMastery: 128; + const MaceMastery: 129; + const Howl: 130; + const FindPotion: 131; + const Leap: 132; + const DoubleSwing: 133; + const PoleArmMastery: 134; + const ThrowingMastery: 135; + const SpearMastery: 136; + const Taunt: 137; + const Shout: 138; + const Stun: 139; + const DoubleThrow: 140; + const IncreasedStamina: 141; + const FindItem: 142; + const LeapAttack: 143; + const Concentrate: 144; + const IronSkin: 145; + const BattleCry: 146; + const Frenzy: 147; + const IncreasedSpeed: 148; + const BattleOrders: 149; + const GrimWard: 150; + const Whirlwind: 151; + const Berserk: 152; + const NaturalResistance: 153; + const WarCry: 154; + const BattleCommand: 155; + + // General stuff + const IdentifyScroll: 217; + const BookofIdentify: 218; + const TownPortalScroll: 219; + const BookofTownPortal: 220; + + // Druid + const Raven: 221; + const PoisonCreeper: 222; // External + const PlaguePoppy: 222; // Internal + const Werewolf: 223; // External + const Wearwolf: 223; // Internal + const Lycanthropy: 224; // External + const ShapeShifting: 224; // Internal + const Firestorm: 225; + const OakSage: 226; + const SpiritWolf: 227; // External + const SummonSpiritWolf: 227; // Internal + const Werebear: 228; // External + const Wearbear: 228; // Internal + const MoltenBoulder: 229; + const ArcticBlast: 230; + const CarrionVine: 231; // External + const CycleofLife: 231; // Internal + const FeralRage: 232; + const Maul: 233; + const Fissure: 234; // Internal + const Eruption: 234; // Internal + const CycloneArmor: 235; + const HeartofWolverine: 236; + const SummonDireWolf: 237; // External + const SummonFenris: 237; // Internal + const Rabies: 238; + const FireClaws: 239; + const Twister: 240; + const SolarCreeper: 241; // External + const Vines: 241; // Internal + const Hunger: 242; + const ShockWave: 243; + const Volcano: 244; + const Tornado: 245; + const SpiritofBarbs: 246; + const Grizzly: 247; // External + const SummonGrizzly: 247; // Internal + const Fury: 248; + const Armageddon: 249; + const Hurricane: 250; + + // Assa + const FireBlast: 251; // External + const FireTrauma: 251; // Internal + const ClawMastery: 252; + const PsychicHammer: 253; + const TigerStrike: 254; + const DragonTalon: 255; + const ShockWeb: 256; // External + const ShockField: 256; // Internal + const BladeSentinel: 257; + const Quickness: 258; // Internal name + const BurstofSpeed: 258; // Shown name + const FistsofFire: 259; + const DragonClaw: 260; + const ChargedBoltSentry: 261; + const WakeofFire: 262; // External + const WakeofFireSentry: 262; // Internal + const WeaponBlock: 263; + const CloakofShadows: 264; + const CobraStrike: 265; + const BladeFury: 266; + const Fade: 267; + const ShadowWarrior: 268; + const ClawsofThunder: 269; + const DragonTail: 270; + const LightningSentry: 271; + const WakeofInferno: 272; // External + const InfernoSentry: 272; // Internal + const MindBlast: 273; + const BladesofIce: 274; + const DragonFlight: 275; + const DeathSentry: 276; + const BladeShield: 277; + const Venom: 278; + const ShadowMaster: 279; + const PhoenixStrike: 280; // External + const RoyalStrike: 280; // Internal + const WakeofDestructionSentry: 281; // Not used? + const Summoner: 500; // special + namespace tabs { + // Ama + const BowandCrossbow: 0; + const PassiveandMagic: 1; + const JavelinandSpear: 2; + + // Sorc + const Fire: 8; + const Lightning: 9; + const Cold: 10; + + // Necro + const Curses: 16; + const PoisonandBone: 17; + const NecroSummoning: 18; + + // Pala + const PalaCombat: 24; + const Offensive: 25; + const Defensive: 26; + + // Barb + const BarbCombat: 32; + const Masteries: 33; + const Warcries: 34; + + // Druid + const DruidSummon: 40; + const ShapeShifting: 41; + const Elemental: 42; + + // Assa + const Traps: 48; + const ShadowDisciplines: 49; + const MartialArts: 50; + } + } + export const skillTabs: undefined + + export namespace quest { + export namespace item { + // Act 1 + const WirtsLeg: 88; + const HoradricMalus: 89; + const ScrollofInifuss: 524; + const KeytotheCairnStones: 525; + // Act 2 + const FinishedStaff: 91; + const HoradricStaff: 91; + const IncompleteStaff: 92; + const ShaftoftheHoradricStaff: 92; + const ViperAmulet: 521; + const TopoftheHoradricStaff: 521; + const Cube: 549; + const BookofSkill: 552; + // Act 3 + const DecoyGidbinn: 86; + const TheGidbinn: 87; + const KhalimsFlail: 173; + const KhalimsWill: 174; + const PotofLife: 545; + const AJadeFigurine: 546; + const JadeFigurine: 546; + const TheGoldenBird: 547; + const LamEsensTome: 548; + const KhalimsEye: 553; + const KhalimsHeart: 554; + const KhalimsBrain: 555; + // Act 4 + const HellForgeHammer: 90; + const Soulstone: 551; + const MephistosSoulstone: 551; + // Act 5 + const MalahsPotion: 644; + const ScrollofKnowledge: 645; + const ScrollofResistance: 646; + // Pandemonium Event + const KeyofTerror: 647; + const KeyofHate: 648; + const KeyofDestruction: 649; + const DiablosHorn: 650; + const BaalsEye: 651; + const MephistosBrain: 652; + const StandardofHeroes: 658; + // Essences/Token + const TokenofAbsolution: 653; + const TwistedEssenceofSuffering: 654; + const ChargedEssenceofHatred: 655; + const BurningEssenceofTerror: 656; + const FesteringEssenceofDestruction: 657; + // Misc + const TheBlackTowerKey: 544; + } + const items: [ + // act 1 + 88, 89, 524, 525, + // act 2 + 91, 92, 521, 549, 552, + // act 3 + 86, 87, 173, 174, 545, 546, 547, 548, 553, 554, 555, + // act 4 + 90, 551, + // act 5 + 644, 645, 646, + ]; + export namespace chest { + // act1 + const StoneAlpha: 17; + const StoneBeta: 18; + const StoneGamma: 19; + const StoneDelta: 20; + const StoneLambda: 21; + const StoneTheta: 22; // ? + const CainsJail: 26; + const InifussTree: 30; + const MalusHolder: 108; + const Wirt: 268; + + // act 2 + const ViperAmuletChest: 149; + const HoradricStaffHolder: 152; + const HoradricCubeChest: 354; + const HoradricScrollChest: 355; + const ShaftoftheHoradricStaffChest: 356; + const Journal: 357; + + // act 3 + const ForestAltar: 81; + const LamEsensTomeHolder: 193; + const GidbinnAltar: 252; + const KhalimsHeartChest: 405; + const KhalimsBrainChest: 406; + const KhalimsEyeChest: 407; + + // act 4 + const HellForge: 376; + + // act 5 + const BarbCage: 473; + const FrozenAnya: 558; + const AncientsAltar: 546; + } + const chests: [ + // act 1 + 17, 18, 19, 20, 21, 22, 26, 30, 108, + // act 2 + 149, 152, 354, 355, 356, 357, + // act 3 + 81, 193, 405, 406, 407, + // act 4 + 376, + // act 5 + 434, 558, 546 + ]; + export namespace id { + const SpokeToWarriv: 0; + const DenofEvil: 1; + const SistersBurialGrounds: 2; + const TheSearchForCain: 4; + const ForgottenTower: 5; + const ToolsoftheTrade: 3; + const SistersToTheSlaughter: 6; + const AbleToGotoActII: 7; + const SpokeToJerhyn: 8; + const RadamentsLair: 9; + const TheHoradricStaff: 10; + const TheTaintedSun: 11; + const TheArcaneSanctuary: 12; + const TheSummoner: 13; + const TheSevenTombs: 14; + const AbleToGotoActIII: 15; + const SpokeToHratli: 16; + const TheGoldenBird: 20; + const BladeoftheOldReligion: 19; + const KhalimsWill: 18; + const LamEsensTome: 17; + const TheBlackenedTemple: 21; + const TheGuardian: 22; + const AbleToGotoActIV: 23; + const SpokeToTyrael: 24; + const TheFallenAngel: 25; + const HellsForge: 27; + const TerrorsEnd: 26; + const AbleToGotoActV: 28; + const SiegeOnHarrogath: 35; + const RescueonMountArreat: 36; + const PrisonofIce: 37; + const BetrayalofHaggorath: 38; + const RiteofPassage: 39; + const EyeofDestruction: 40; + const Respec: 41; + } + // just common states for now + namespace states { + const Completed: 0; + const ReqComplete: 1; + const GreyedOut: 12; + const PartyMemberComplete: 13; + const CannotComplete: 14; + } + } + + // in game data + export namespace uiflags { + const Inventory: 0x01; + const StatsWindow: 0x02; + const QuickSkill: 0x03; + const SkillWindow: 0x04; + const ChatBox: 0x05; + const NPCMenu: 0x08; + const EscMenu: 0x09; + const KeytotheCairnStonesScreen: 0x10; + const AutoMap: 0x0A; + const ConfigControls: 0x0B; + const Shop: 0x0C; + const ShowItem: 0x0D; + const SubmitItem: 0x0E; + const Quest: 0x0F; + const QuestLog: 0x11; + const StatusArea: 0x12; + const Waypoint: 0x14; + const MiniPanel: 0x15; + const Party: 0x16; + const TradePrompt: 0x17; + const Msgs: 0x18; + const Stash: 0x19; + const Cube: 0x1A; + const ShowBelt: 0x1F; + const Help: 0x21; + const MercScreen: 0x24; + const ScrollWindow: 0x25 + } + + export namespace menu { + const Respec: 0x2BA0; + const Ok: 0x0D49; + const Talk: 0x0D35; + const Trade: 0x0D44; + const TradeRepair: 0x0D06; + const Imbue: 0x0FB1; + const Gamble: 0x0D46; + const Hire: 0x0D45; + const GoEast: 0x0D36; + const GoWest: 0x0D37; + const IdentifyItems: 0x0FB4; + const SailEast: 0x0D38; + const SailWest: 0x0D39; + const RessurectMerc: 0x1507; + const AddSockets: 0x58DC; + const Personalize: 0x58DD; + const TravelToHarrogath: 0x58D2; + } + + // shrine types + export namespace shrines { + const Ids: [2, 81, 83, 170, 344, 197, 202]; + const None: 0; + const Refilling: 1; + const Health: 2; + const Mana: 3; + const HealthExchange: 4; + const ManaExchange: 5; + const Armor: 6; + const Combat: 7; + const ResistFire: 8; + const ResistCold: 9; + const ResistLightning: 10; + const ResistPoison: 11; + const Skill: 12; + const ManaRecharge: 13; + const Stamina: 14; + const Experience: 15; + const Enirhs: 16; + const Portal: 17; + const Gem: 18; + const Fire: 19; + const Monster: 20; + const Exploding: 21; + const Poison: 22 + } + + // unit states + export namespace states { + const None: 0; + const FrozenSolid: 1; + const Poison: 2; + const ResistFire: 3; + const ResistCold: 4; + const ResistLightning: 5; + const ResistMagic: 6; + const PlayerBody: 7; + const ResistAll: 8; + const AmplifyDamage: 9; + const FrozenArmor: 10; + const Frozen: 11; + const Inferno: 12; + const Blaze: 13; + const BoneArmor: 14; + const Concentrate: 15; + const Enchant: 16; + const InnerSight: 17; + const SkillMove: 18; + const Weaken: 19; + const ChillingArmor: 20; + const Stunned: 21; + const SpiderLay: 22; + const DimVision: 23; + const Slowed: 24; + const FetishAura: 25; + const Shout: 26; + const Taunt: 27; + const Conviction: 28; + const Convicted: 29; + const EnergyShield: 30; + const Venom: 31; + const BattleOrders: 32; + const Might: 33; + const Prayer: 34; + const HolyFire: 35; + const Thorns: 36; + const Defiance: 37; + const ThunderStorm: 38; + const LightningBolt: 39; + const BlessedAim: 40; + const Stamina: 41; + const Concentration: 42; + const Holywind: 43; + const HolyFreeze: 43; + const HolywindCold: 44; + const HolyFreezeCold: 44; + const Cleansing: 45; + const HolyShock: 46; + const Sanctuary: 47; + const Meditation: 48; + const Fanaticism: 49; + const Redemption: 50; + const BattleCommand: 51; + const PreventHeal: 52; + const Conversion: 53; + const Uninterruptable: 54; + const IronMaiden: 55; + const Terror: 56; + const Attract: 57; + const LifeTap: 58; + const Confuse: 59; + const Decrepify: 60; + const LowerResist: 61; + const OpenWounds: 62; + const Dopplezon: 63; + const Decoy: 63; + const CriticalStrike: 64; + const Dodge: 65; + const Avoid: 66; + const Penetrate: 67; + const Evade: 68; + const Pierce: 69; + const Warmth: 70; + const FireMastery: 71; + const LightningMastery: 72; + const ColdMastery: 73; + const SwordMastery: 74; + const AxeMastery: 75; + const MaceMastery: 76; + const PoleArmMastery: 77; + const ThrowingMastery: 78; + const SpearMastery: 79; + const IncreasedStamina: 80; + const IronSkin: 81; + const IncreasedSpeed: 82; + const NaturalResistance: 83; + const FingerMageCurse: 84; + const NoManaReg: 85; + const JustHit: 86; + const SlowMissiles: 87; + const ShiverArmor: 88; + const BattleCry: 89; + const Blue: 90; + const Red: 91; + const DeathDelay: 92; + const Valkyrie: 93; + const Frenzy: 94; + const Berserk: 95; + const Revive: 96; + const ItemFullSet: 97; + const SourceUnit: 98; + const Redeemed: 99; + const HealthPot: 100; + const HolyShield: 101; + const JustPortaled: 102; + const MonFrenzy: 103; + const CorpseNoDraw: 104; + const Alignment: 105; + const ManaPot: 106; + const Shatter: 107; + const SyncWarped: 108; + const ConversionSave: 109; + const Pregnat: 110; + const Rabies: 112; + const DefenceCurse: 113; + const BloodMana: 114; + const Burning: 115; + const DragonFlight: 116; + const Maul: 117; + const CorpseNoSelect: 118; + const ShadowWarrior: 119; + const FeralRage: 120; + const SkillDelay: 121; + const ProgressiveDamage: 122; + const ProgressiveSteal: 123; + const ProgressiveOther: 124; + const ProgressiveFire: 125; + const ProgressiveCold: 126; + const ProgressiveLighting: 127; + const ShrineArmor: 128; + const ShrineCombat: 129; + const ShrineResLighting: 130; + const ShrineResFire: 131; + const ShrineResCold: 132; + const ShrineResPoison: 133; + const ShrineSkill: 134; + const ShrineManaRegen: 135; + const ShrineStamina: 136; + const ShrineExperience: 137; + const FenrisRage: 138; + const Wolf: 139; + const Wearwolf: 139; + const Bear: 140; + const Wearbear: 140; + const Bloodlust: 141; + const ChangeClass: 142; + const Attached: 143; + const Hurricane: 144; + const Armageddon: 145; + const Invis: 146; + const Barbs: 147; + const HeartofWolverine: 148; + const OakSage: 149; + const VineBeast: 150; + const CycloneArmor: 151; + const ClawMastery: 152; + const CloakofShadows: 153; + const Recyled: 154; + const WeaponBlock: 155; + const Cloaked: 156; + const Quickness: 157; // Internal name + const BurstofSpeed: 157; // External name + const BladeShield: 158; + const Fade: 159; + const RestInPeace: 172; + const Glowing: 175; + const Delerium: 177; + const Antidote: 178; + const Thawing: 179; + const StaminaPot: 180; + } + + export namespace enchant { + const ExtraStrong: 5; + const ExtraFast: 6; + const Cursed: 7; + const MagicResistant: 8; + const FireEnchanted: 9; + const LightningEnchanted: 17; + const ColdEnchanted: 18; + const ManaBurn: 25; + const Teleportation: 26; + const SpectralHit: 27; + const StoneSkin: 28; + const MultipleShots: 29; + } + + // unit stats + export namespace stats { + const StunLength: 66; + const VelocityPercent: 67; + const OtherAnimrate: 69; + const HpRegen: 74; + + const LastBlockFrame: 95; + const State: 98; + const MonsterPlayerCount: 100; + + const CurseResistance: 109; + const IronMaidenLevel: 129; + const LifeTapLevel: 130; + + const Alignment: 172; + const Target0: 173; + const Target1: 174; + const GoldLost: 175; + const MinimumRequiredLevel: 176; + const ConversionLevel: 176; + const ConversionMaxHp: 177; + const UnitDooverlay: 178; + const AttackVsMontype: 179; + const DamageVsMontype: 180; + + const ArmorOverridePercent: 182; + const FireLength: 315; + const BurningMin: 316; + const BurningMax: 317; + const ProgressiveDamage: 318; + const ProgressiveSteal: 319; + const ProgressiveOther: 320; + const ProgressiveFire: 321; + const ProgressiveCold: 322; + const ProgressiveLightning: 323; + const ProgressiveTohit: 325; + const PoisonCount: 326; + const DamageFramerate: 327; + const PierceIdx: 328; + + const ModifierListSkill: 350; + const ModifierListLevel: 351; + + const LastSentHpPct: 352; + const SourceUnitType: 353; + const SourceUnitId: 354; + + const SkillThornsPercent: 131; + const SkillBoneArmor: 132; + const SkillBoneArmorMax: 133; + const SkillFade: 181; + const SkillPoisonOverrideLength: 101; + const SkillBypassUndead: 103; + const SkillBypassDemons: 104; + const SkillBypassBeasts: 106; + const SkillHandofAthena: 161; + const SkillStaminaPercent: 162; + const SkillPassiveStaminaPercent: 163; + const SkillConcentration: 164; + const SkillEnchant: 165; + const SkillPierce: 166; + const SkillConviction: 167; + const SkillChillingArmor: 168; + const SkillFrenzy: 169; + const SkillDecrepify: 170; + const SkillArmorPercent: 171; + + const Strength: 0; + const Energy: 1; + const Dexterity: 2; + const Vitality: 3; + const StatPts: 4; + const NewSkills: 5; + const HitPoints: 6; + const MaxHp: 7; + const Mana: 8; + const MaxMana: 9; + const Stamina: 10; + const MaxStamina: 11; + const Level: 12; + const Experience: 13; + const Gold: 14; + const GoldBank: 15; + const ArmorPercent: 16; + const MaxDamagePercent: 17; + const MinDamagePercent: 18; + const EnhancedDamage: 18; + const ToHit: 19; + const ToBlock: 20; + const MinDamage: 21; + const MaxDamage: 22; + const SecondaryMinDamage: 23; + const SecondaryMaxDamage: 24; + const DamagePercent: 25; + const ManaRecovery: 26; + const ManaRecoveryBonus: 27; + const StaminaRecoveryBonus: 28; + const LastExp: 29; + const NextExp: 30; + const ArmorClass: 31; + const Defense: 31; + const ArmorClassVsMissile: 32; + const ArmorClassVsHth: 33; + const NormalDamageReduction: 34; + const MagicDamageReduction: 35; + const DamageResist: 36; + const MagicResist: 37; + const MaxMagicResist: 38; + const FireResist: 39; + const MaxFireResist: 40; + const LightResist: 41; + const LightningResist: 41; + const MaxLightResist: 42; + const ColdResist: 43; + const MaxColdResist: 44; + const PoisonResist: 45; + const MaxPoisonResist: 46; + const DamageAura: 47; + const FireMinDamage: 48; + const FireMaxDamage: 49; + const LightMinDamage: 50; + const LightMaxDamage: 51; + const MagicMinDamage: 52; + const MagicMaxDamage: 53; + const ColdMinDamage: 54; + const ColdMaxDamage: 55; + const ColdLength: 56; + const PoisonMinDamage: 57; + const PoisonMaxDamage: 58; + const PoisonLength: 59; + const LifeDrainMinDamage: 60; + const LifeLeech: 60; + const LifeDrainMaxDamage: 61; + const ManaDrainMinDamage: 62; + const ManaLeech: 62; + const ManaDrainMaxDamage: 63; + const StaminaDrainMinDamage: 64; + const StaminaDrainMaxDamage: 65; + const AttackRate: 68; + const PreviousSkillRight: 181; + const PreviousSkillMiddle: 182; + const PreviousSkillLeft: 183; + const PassiveFireMastery: 329; + const PassiveLightningMastery: 330; + const PassiveColdMastery: 331; + const PassivePoisonMastery: 332; + const PassiveFirePierce: 333; + const PassiveLightningPierce: 334; + const PassiveColdPierce: 335; + const PassivePoisonPierce: 336; + const PassiveCriticalStrike: 337; + const PassiveDodge: 338; + const PassiveAvoid: 339; + const PassiveEvade: 340; + const PassiveWarmth: 341; + const PassiveMasteryMeleeTh: 342; + const PassiveMasteryMeleeDmg: 343; + const PassiveMasteryMeleeCrit: 344; + const PassiveMasteryThrowTh: 345; + const PassiveMasteryThrowDmg: 346; + const PassiveMasteryThrowCrit: 347; + const PassiveWeaponBlock: 348; + const PassiveSummonResist: 349; + const PassiveMagMastery: 357; + const PassiveMagPierce: 358; + const Quantity: 70; + const Value: 71; + const Durability: 72; + const MaxDurability: 73; + const MaxDurabilityPercent: 75; + const MaxHpPercent: 76; + const MaxManaPercent: 77; + const AttackerTakesDamage: 78; + const GoldBonus: 79; + const MagicBonus: 80; + const Knockback: 81; + const TimeDuration: 82; + const AddClassSkills: 83; + const AddExperience: 85; + const HealAfterKill: 86; + const ReducedPrices: 87; + const DoubleHerbDuration: 88; + const LightRadius: 89; + const LightColor: 90; + const ReqPercent: 91; + const LevelReq: 92; + const FasterAttackRate: 93; + const IAS: 93; + const LevelReqPct: 94; + const FasterMoveVelocity: 96; + const FRW: 96; + const NonClassSkill: 97; + const OSkill: 97; + const FasterGetHitRate: 99; + const FHR: 99; + const FasterBlockRate: 102; + const FBR: 102; + const FasterCastRate: 105; + const FCR: 105; + const SingleSkill: 107; + const RestinPeace: 108; + const PoisonLengthResist: 110; + const NormalDamage: 111; + const Howl: 112; + const Stupidity: 113; + const DamagetoMana: 114; + const IgnoreTargetAc: 115; + const IgnoreTargetDefense: 115; + const FractionalTargetAc: 116; + const PreventHeal: 117; + const HalfFreezeDuration: 118; + const ToHitPercent: 119; + const DamageTargetAc: 120; + const DemonDamagePercent: 121; + const UndeadDamagePercent: 122; + const DemontoHit: 123; + const UndeadtoHit: 124; + const Throwable: 125; + const ElemSkill: 126; + const AllSkills: 127; + const AttackerTakesLightDamage: 128; + const Freeze: 134; + const OpenWounds: 135; + const CrushingBlow: 136; + const KickDamage: 137; + const ManaAfterKill: 138; + const HealAfterDemonKill: 139; + const ExtraBlood: 140; + const DeadlyStrike: 141; + const AbsorbFirePercent: 142; + const AbsorbFire: 143; + const AbsorbLightPercent: 144; + const AbsorbLight: 145; + const AbsorbMagicPercent: 146; + const AbsorbMagic: 147; + const AbsorbColdPercent: 148; + const AbsorbCold: 149; + const AbsorbSlash: 262; + const AbsorbCrush: 263; + const AbsorbThrust: 264; + const AbsorbSlashPercent: 265; + const AbsorbCrushPercent: 266; + const AbsorbThrustPercent: 267; + const Slow: 150; + const Indestructible: 152; + const CannotbeFrozen: 153; + const StaminaDrainPct: 154; + const Reanimate: 155; + const Pierce: 156; + const MagicArrow: 157; + const ExplosiveArrow: 158; + const ThrowMinDamage: 159; + const ThrowMaxDamage: 160; + const AddSkillTab: 188; + const NumSockets: 194; + const SkillOnAura: 151; + const SkillOnAttack: 195; + const SkillOnKill: 196; + const SkillOnDeath: 197; + const SkillOnHit: 198; + const SkillOnStrike: 198; + const SkillOnLevelUp: 199; + const SkillOnGetHit: 201; + const SkillWhenStruck: 201; + const ChargedSkill: 204; + const PerLevelArmor: 214; + const PerLevelArmorPercent: 215; + const PerLevelHp: 216; + const PerLevelMana: 217; + const PerLevelMaxDamage: 218; + const PerLevelMaxDamagePercent: 219; + const PerLevelStrength: 220; + const PerLevelDexterity: 221; + const PerLevelEnergy: 222; + const PerLevelVitality: 223; + const PerLevelTohit: 224; + const PerLevelTohitPercent: 225; + const PerLevelColdDamageMax: 226; + const PerLevelFireDamageMax: 227; + const PerLevelLtngDamageMax: 228; + const PerLevelPoisDamageMax: 229; + const PerLevelResistCold: 230; + const PerLevelResistFire: 231; + const PerLevelResistLtng: 232; + const PerLevelResistPois: 233; + const PerLevelAbsorbCold: 234; + const PerLevelAbsorbFire: 235; + const PerLevelAbsorbLtng: 236; + const PerLevelAbsorbPois: 237; + const PerLevelThorns: 238; + const PerLevelFindGold: 239; + const PerLevelFindMagic: 240; + const PerLevelRegenstamina: 241; + const PerLevelStamina: 242; + const PerLevelDamageDemon: 243; + const PerLevelDamageUndead: 244; + const PerLevelTohitDemon: 245; + const PerLevelTohitUndead: 246; + const PerLevelCrushingblow: 247; + const PerLevelOpenwounds: 248; + const PerLevelKickDamage: 249; + const PerLevelDeadlystrike: 250; + const PerLevelFindGems: 251; + const ReplenishDurability: 252; + const ReplenishQuantity: 253; + const ExtraStack: 254; + const Find: 255; + const SlashDamage: 256; + const SlashDamagePercent: 257; + const CrushDamage: 258; + const CrushDamagePercent: 259; + const ThrustDamage: 260; + const ThrustDamagePercent: 261; + const ArmorByTime: 268; + const ArmorPercentByTime: 269; + const HpByTime: 270; + const ManaByTime: 271; + const MaxDamageByTime: 272; + const MaxDamagePercentByTime: 273; + const StrengthByTime: 274; + const DexterityByTime: 275; + const EnergyByTime: 276; + const VitalityByTime: 277; + const TohitByTime: 278; + const TohitPercentByTime: 279; + const ColdDamageMaxByTime: 280; + const FireDamageMaxByTime: 281; + const LtngDamageMaxByTime: 282; + const PoisDamageMaxByTime: 283; + const ResistColdByTime: 284; + const ResistFireByTime: 285; + const ResistLtngByTime: 286; + const ResistPoisByTime: 287; + const AbsorbColdByTime: 288; + const AbsorbFireByTime: 289; + const AbsorbLtngByTime: 290; + const AbsorbPoisByTime: 291; + const FindGoldByTime: 292; + const FindMagicByTime: 293; + const RegenstaminaByTime: 294; + const StaminaByTime: 295; + const DamageDemonByTime: 296; + const DamageUndeadByTime: 297; + const TohitDemonByTime: 298; + const TohitUndeadByTime: 299; + const CrushingBlowByTime: 300; + const OpenWoundsByTime: 301; + const KickDamageByTime: 302; + const DeadlyStrikeByTime: 303; + const FindGemsByTime: 304; + const PierceCold: 305; + const PierceFire: 306; + const PierceLtng: 307; + const PiercePois: 308; + const DamageVsMonster: 309; + const DamagePercentVsMonster: 310; + const TohitVsMonster: 311; + const TohitPercentVsMonster: 312; + const AcVsMonster: 313; + const AcPercentVsMonster: 314; + const ExtraCharges: 324; + const QuestDifficulty: 356; + + // doesn't exist but define for prototypes + const AllRes: 555; + } + + // unit info + export namespace unittype { + const Player: 0; + const NPC: 1; + const Monster: 1; + const Object: 2; + const Missile: 3; + const Item: 4; + const Stairs: 5; // const ToDo: might be more as stairs + } + + export namespace player { + export namespace slot { + const Main: 0; + const Secondary: 1 + } + export namespace move { + const Walk: 0; + const Run: 1 + } + export namespace mode { // sdk.player.mode. + const Death: 0; + const StandingOutsideTown: 1; + const Walking: 2; + const Running: 3; + const GettingHit: 4; + const StandingInTown: 5; + const WalkingInTown: 6; + const Attacking1: 7; + const Attacking2: 8; + const Blocking: 9; + const CastingSkill: 10; + const ThrowingItem: 11; + const Kicking: 12; + const UsingSkill1: 13; + const UsingSkill2: 14; + const UsingSkill3: 15; + const UsingSkill4: 16; + const Dead: 17; + const SkillActionSequence: 18; + const KnockedBack: 19; + } + namespace _class { + const Amazon: 0; + const Sorceress: 1; + const Necromancer: 2; + const Paladin: 3; + const Barbarian: 4; + const Druid: 5; + const Assassin: 6; + + const nameOf: (classid: 0 | 1 | 2 | 3 | 4 | 5 | 6) => "Amazon" | "Sorceress" | "Necromancer" | "Paladin" | "Barbarian" | "Druid" | "Assassin" | false; + } + export { _class as class }; + } + + export namespace npcs { + // same as monsters but more clear to use units.npcs.mode + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } + + const Akara: 148; + const Alkor: 254; + const Asheara: 252; + const WarrivAct1: 155; + const WarrivAct2: 175; + const Atma: 176; + const Tyrael: 367; + const Tyrael2: 251; + const Tyrael3: 521; + const Charsi: 154; + const DeckardCain1: 146; + const DeckardCain2: 244; + const DeckardCain3: 245; + const DeckardCain4: 246; + const DeckardCain5: 265; + const DeckardCain6: 520; + const Drognan: 177; + const Elzix: 199; + const Fara: 178; + const Gheed: 147; + const Greiz: 198; + const Halbu: 257; + const Hratli: 253; + const Jamella: 405; + const Jerhyn: 201; + const Kaelan: 331; + const Kashya: 150; + const Larzuk: 511; + const Lysander: 202; + const Malah: 513; + const Meshif: 210; + const Meshif2: 264; + const Natalya: 297; + const Ormus: 255; + const NihlathakNPC: 526; + const Qualkehk: 515; + const RogueScout: 270; + const TempleGuard1: 52; + const TempleGuard2: 665; + const TempleGuard3: 666; + const Townguard1: 535; + const Townguard2: 536; + } + + export namespace objects { + namespace mode { + const Inactive: 0; + const Interacted: 1; + const Active: 2; + } + + // act1 + const A1TownFire: 39; + const A1Waypoint: 119; + const StoneAlpha: 17; + const StoneBeta: 18; + const StoneGamma: 19; + const StoneDelta: 20; + const StoneLambda: 21; + const StoneTheta: 22; + const CainsJail: 26; + const InifussTree: 30; + const Malus: 108; + + // act 2 + const A2Waypoint: 156; + const A2UndergroundUpStairs: 22; + const TrapDoorA2: 74; // ancienttunnel/sewers act 2 + const DoorbyDockAct2: 75; // incorrect ? const TODO: figure out what 75 really corresponds to since the door is obj type 5 with classid 20 + const PortaltoDurielsLair: 100; + const HoradricStaffHolder: 152; + const ArcaneSanctuaryPortal: 298; + const HoradricCubeChest: 354; + const HoradricScrollChest: 355; + const Journal: 357; + + // act 3 + const A3Waypoint: 237; + const ForestAltar: 81; + const LamEsensTome: 193; + const SewerStairsA3: 366; + const SewerLever: 367; + const DuranceEntryStairs: 386; + const RedPortalToAct4: 342; + const CompellingOrb: 404; + + // act 4 + const A4Waypoint: 398; + const SealGlow: 131; + const DiabloStar: 255; + const DiabloSealInfector: 392; + const DiabloSealInfector2: 393; + const DiabloSealSeis: 394; + const DiabloSealVizier: 396; + const DiabloSealVizier2: 395; + const RedPortalToAct5: 566; // The one of tyreal + + // act 5 + const A5Waypoint: 429; + const SideCavesA5: 75; // FrozenRiver, DrifterCavern; IcyCellar + const Act5Gate: 449; + const KorlictheProtectorStatue: 474; + const TalictheDefenderStatue: 475; + const MadawctheGuardianStatue: 476; + const AncientsAltar: 546; + const ArreatEnterAncientsWay: 564; + const ArreatEnterWorldstone: 547; + //const AncientsDoor: 547; + const AncientsDoor: 547; // Worldstone keep lvl 1 + const FrozenAnya: 558; + const FrozenAnyasPlatform: 460; + const NihlathaksPlatform: 462; + const WorldstonePortal: 563; + + const FrigidHighlandsChest: 455; + const IcyCellarChest: 397; + + const SmallSparklyChest: 397; + const LargeSparklyChest: 455; + const SuperChest: 580; + + // misc + const BubblingPoolofBlood: 82; + const HornShrine: 83; + const Stash: 267; + const BluePortal: 59; + const RedPortal: 60; + const Smoke: 401; + } + + export namespace exits { + namespace preset { + const AreaEntrance: 0; // special + // act 1 + const CaveHoleUp: 4; + const CaveHoleLvl2: 5; + const Crypt: 6; + const Mausoleum: 7; + const CryptMausExit: 8; + const JailUpStairs: 13; + const JailDownStairs: 14; + const CathedralDownStairs: 15; + const CathedralUpStairs: 16; + const CatacombsUpStairs: 17; + const CatacombsDownStairs: 18; + + // act 2 + const A2SewersTrapDoor: 19; + const A2EnterSewersDoor: 20; + const A2ExitSewersDoor: 21; + const A2UndergroundUpStairs: 22; + const A2DownStairs: 23; + const EnterHaremStairs: 24; + const ExitHaremStairs: 25; + const PreviousLevelHaremRight: 26; + const PreviousLevelHaremLeft: 27; + const NextLevelHaremRight: 28; + const NextLevelHaremLeft: 29; + const PreviousPalaceRight: 30; + const PreviousPalaceLeft: 31; + const NextLevelPalace: 32; + const EnterStonyTomb: 33; + const EnterHalls: 36; + const EnterTalTomb1: 38; + const EnterTalTomb2: 39; + const EnterTalTomb3: 40; + const EnterTalTomb4: 41; + const EnterTalTomb5: 42; + const EnterTalTomb6: 43; + const EnterTalTomb7: 44; + const PreviousAreaTomb: 45; + const NextLevelTomb: 46; + const EnterMaggotLair: 47; + const PreviousAreaMaggotLair: 48; + const NextLevelMaggotLair: 49; + const AncientTunnelsTrapDoor: 50; + const EntrancetoDurielsLair: 100; + + // act 3 + const EnterSpiderHole: 51; + const ExitSpiderHole: 52; + const EnterPit: 53; + const EnterDungeon: 54; + const PreviousAreaDungeon: 55; + const NextLevelDungeon: 56; + const A3EnterSewers: 57; + const A3ExitSewersUpperK: 58; + const A3SewersPreviousArea: 58; + const A3ExitSewers: 59; + const A3NextLevelSewers: 60; + const EnterTemple: 61; + const ExitTemple: 63; + const EnterDurance: 64; + const PreviousLevelDurance: 65; + const NextLevelDurance: 68; + const SewerStairsA3: 366; + const DuranceEntryStairs: 386; + + // act 4 + const EnterRiverStairs: 69; + const ExitRiverStairs: 70; + // act 5 + const EnterCrystal: 71; + const A5ExitCave: 73; + const A5NextLevelCave: 74; + const EnterSubLevelCave: 75; + const EnterNithsTemple: 76; + const PreviousAreaNithsTemple: 77; + const NextAreaNithsTemple: 78; + const ArreatEnterAncientsWay: 79; + const ArreatEnterWorldstone: 80; + const PreviousAreaWorldstone: 81; + const NextAreaWorldstone: 82; + } + } + + export namespace monsters { + namespace preset { + // Confirmed + const TheSummoner: 250; + const Bishibosh: 734; + const Coldcrow: 736; + const Rakanishu: 737; + const TreeheadWoodFist: 738; + const TheCountess: 740; + const BoneAsh: 743; + const Beetleburst: 747; + const TheSmith: 754; + const Radament: 744; + const CreepingFeature: 748; + const FireEye: 750; + const DarkElder: 751; + const Corpsefire: 774; + const TheCowKing: 773; + const BloodRaven: 805; + const Izual: 406; + const DacFarren: 782; + const EyebacktheUnleashed: 784; + const SharpToothSayer: 790; + // Unconfirmed + const Bonebreak: 705; + const Griswold: 709; + const PitspawnFouldog: 711; + const FlamespiketheCrawler: 712; + const BloodwitchtheWild: 715; + const Fangskin: 716; + const Leatherarm: 718; + const ColdwormtheBurrower: 719; + const AncientKaatheSoulless: 723; + const WebMagetheBurning: 725; + const WitchDoctorEndugu: 726; + const Stormtree: 727; + const SarinatheBattlemaid: 728; + const IcehawkRiftwing: 729; + const IsmailVilehand: 730; + const GelebFlamefinger: 731; + const BremmSparkfist: 732; + const ToorcIcefist: 733; + const WyandVoidfinger: 734; + const MafferDragonhand: 735; + const WingedDeath: 736; + const ListertheTormentor: 737; + const Taintbreeder: 738; + const RiftwraiththeCannibal: 739; + const InfectorofSouls: 740; + const LordDeSeis: 741; + const GrandVizierofChaos: 742; + const Hephasto: 745; + const SiegeBoss: 746; + const AxeDweller: 750; + const BonesawBreaker: 751; + const EldritchtheRectifier: 753; + const ThreshSocket: 755; + const Pindleskin: 756; + const SnapchipShatter: 757; + const AnodizedElite: 758; + const VinvearMolech: 759; + const MagmaTorquer: 761; + const BlazeRipper: 762; + const Frozenstein: 763; + const Nihlathak: 764; + const Wave1Spawn: 765; + const Wave2Spawn: 766; + const Wave3Spawn: 767; + const Wave4Spawn: 768; + const Wave5Spawn: 769; + + // Questionable + const GriefGrumble: 741; // JailLvl2 + const UniqueJailLvl3: 273; + const UniqueArcaneSanctuary: 371; + } + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } + namespace spectype { + const All: 0; + const Super: 1; + const Champion: 2; + const Unique: 4; + const SuperUnique: 5; + const Magic: 6; + const Minion: 8; + } + // todo - determine what all these correlate to + namespace type { + const Undead: 1; + const Demon: 2; + const Insect: 3; + const Human: 4; + const Construct: 5; + const LowUndead: 6; + const HighUndead: 7; + const Skeleton: 8; + const Zombie: 9; + const BigHead: 10; + const FoulCrow: 11; + const Fallen: 12; + const Brute: 13; + const SandRaider: 14; + const Wraith: 15; + const CorruptRogue: 16; + const Baboon: 17; + const GoatMan: 18; + const QuillRat: 19; + const SandMaggot: 20; + const Viper: 21; + const SandLeaper: 22; + const PantherWoman: 23; + const Swarm: 24; + const Scarab: 25; + const Mummy: 26; + const Unraveler: 27; + const Vulture: 28; + const Mosquito: 29; + const WillowWisp: 30; + const Arach: 31; + const ThornHulk: 32; + const Vampire: 33; + const BatDemon: 34; + const Fetish: 35; + const Blunderbore: 36; + const UndeadFetish: 37; + const Zakarum: 38; + const FrogDemon: 39; + const Tentacle: 40; + const FingerMage: 41; + const Golem: 42; + const Vilekind: 43; + const Regurgitator: 44; + const DoomKnight: 45; + const CouncilMember: 46; + const MegaDemon: 47; + const Bovine: 48; + const SeigeBeast: 49; + const SnowYeti: 50; + const Minion: 51; + const Succubus: 52; + const Overseer: 53; + const Imp: 54; + const FrozenHorror: 55; + const BloodLord: 56; + const DeathMauler: 57; + const PutridDefiler: 58; + } + const DiablosBoneCage: 340; + const Dummy1: 149; + const Dummy2: 268; + const AbyssKnight: 311; + const Afflicted: 10; + const Afflicted2: 580; + const AlbinoRoach: 95; + const Ancient1: 104; + const Ancient2: 669; + const Ancient3: 670; + const Apparition: 41; + const Arach1: 122; + const Arach2: 685; + const Assailant: 33; + const Assailant2: 603; + const BaalColdMage: 381; + const Balrog1: 360; + const Balrog2: 686; + const Banished: 135; + const Barbs: 422; + const Bear1: 428; + const Bear2: 431; + const Beast: 441; + const BerserkSlayer: 462; + const BlackArcher: 163; + const BlackLancer1: 168; + const BlackLancer2: 617; + const BlackLocusts: 88; + const BlackRaptor1: 17; + const BlackRaptor2: 592; + const BlackRogue: 46; + const BlackSoul1: 121; + const BlackSoul2: 640; + const BlackVultureNest: 208; + const BloodBoss: 482; + const BloodBringer: 443; + const BloodClan1: 55; + const BloodClan2: 588; + const BloodDiver: 139; + const BloodGolem: 290; + const BloodHawk1: 16; + const BloodHawk2: 591; + const BloodHawkNest: 207; + const BloodHook: 116; + const BloodHookNest: 336; + const BloodLord1: 134; + const BloodLord2: 695; + const BloodWing: 117; + const BloodWingNest: 337; + const Blunderbore1: 186; + const Blunderbore2: 618; + const BoneArcher1: 172; + const BoneArcher2: 576; + const BoneMage1: 275; + const BoneMage2: 380; + const BoneMage3: 384; + const BoneMage4: 388; + const BoneMage5: 624; + const BoneWarrior1: 2; + const BoneWarrior2: 648; + const HellBovine: 391; + const BrambleHulk: 128; + const Brute: 24; + const Bunny: 556; + const BurningDead: 3; + const BurningDeadArcher1: 173; + const BurningDeadArcher2: 575; + const BurningDeadArcher3: 577; + const BurningDeadMage1: 276; + const BurningDeadMage2: 385; + const BurningDeadMage3: 389; + const BurningDeadMage4: 621; + const BurningSoul1: 641; + const BurningSoul2: 120; + const Cadaver1: 100; + const Cadaver2: 703; + const Cantor: 239; + const CarrionBird1: 110; + const CarrionBird2: 608; + const Carver1: 642; + const Carver2: 20; + const CarverShaman: 645; + const CarverShaman2: 59; + const CaveLeaper1: 79; + const CaveLeaper2: 629; + const ClawViper1: 74; + const ClawViper2: 594; + const CloudStalker1: 18; + const CloudStalker2: 593; + const CloudStalkerNest: 209; + const Combatant1: 522; + const Combatant2: 523; + const ConsumedFireBoar: 464; + const ConsumedIceBoar: 463; + const CorpseSpitter: 308; + const Corpulent: 307; + const Creature1: 248; + const Creature2: 427; + const Creeper: 413; + const CrushBiest: 442; + const Crusher: 26; + const Damned1: 14; + const Damned2: 584; + const DarkArcher1: 162; + const DarkArcher2: 614; + const DarkFamiliar: 140; + const DarkHunter: 43; + const DarkLancer1: 167; + const DarkLancer2: 616; + const DarkLord1: 133; + const DarkLord2: 697; + const DarkOne1: 22; + const DarkOne2: 644; + const DarkRanger: 160; + const DarkShaman1: 61; + const DarkShaman2: 647; + const DarkShape: 42; + const DarkSpearwoman: 165; + const DarkStalker: 45; + const DeamonSteed: 445; + const DeathClan1: 57; + const DeathClan2: 589; + const Decayed: 97; + const DefiledWarrior: 440; + const Defiler1: 546; + const Defiler2: 547; + const Defiler3: 548; + const Defiler4: 549; + const Defiler5: 550; + const DesertWing: 136; + const Destruction: 410; + const Devilkin: 643; + const Devilkin2: 21; + const DevilkinShaman: 646; + const DevilkinShaman2: 60; + const Devourer: 70; + const DevourerEgg: 192; + const DevourerQueen: 286; + const DevourerYoung: 182; + const Disfigured: 13; + const Disfigured2: 583; + const Dominus1: 474; + const Dominus2: 636; + const DoomApe: 51; + const DoomKnight: 310; + const DoomKnight1: 699; + const DoomKnight2: 700; + const Drehya1: 512; + const Drehya2: 527; + const DriedCorpse: 96; + const DrownedCarcass: 8; + const DuneBeast: 48; + const DungSoldier: 91; + const Dweller: 247; + const Eagle: 429; + const Embalmed: 98; + const Faithful: 236; + const Fallen: 19; + const FallenShaman: 58; + const FanaticMinion: 461; + const Feeder: 115; + const FeederNest: 335; + const Fenris: 421; + const Fetish1: 142; + const BoneFetish2: 213; + const Fetish3: 397; + const FetishShaman: 279; + const Fiend1: 137; + const Fiend2: 651; + const FireBoar: 456; + const FireTower: 372; + const FlameSpider: 125; + const Flayer1: 143; + const BoneFetish3: 214; + const Flayer3: 398; + const Flayer4: 659; + const Flayer5: 656; + const FlayerShaman1: 280; + const FlayerShaman2: 662; + const FleshArcher: 164; + const FleshBeast1: 301; + const FleshBeast2: 678; + const FleshHunter: 47; + const FleshLancer: 169; + const FleshSpawner1: 298; + const FleshSpawner2: 676; + const FlyingScimitar: 234; + const FoulCrow: 15; + const FoulCrow2: 590; + const FoulCrowNest: 206; + const FrenziedHellSpawn: 465; + const FrenziedIceSpawn: 466; + const GargantuanBeast: 28; + const Geglash: 200; + const Ghost1: 38; + const Ghost2: 631; + const Ghoul: 7; + const GhoulLord1: 131; + const GhoulLord2: 696; + const GiantLamprey: 71; + const GiantLampreyEgg: 193; + const GiantLampreyQueen: 287; + const GiantLampreyYoung: 183; + const GiantUrchin: 317; + const Gloam1: 118; + const Gloam2: 639; + const Gloombat1: 138; + const Gloombat2: 650; + const Gorbelly: 187; + const GoreBearer: 444; + const GreaterHellSpawn1: 459; + const GreaterHellSpawn2: 684; + const GreaterIceSpawn: 460; + const Groper: 304; + const Grotesque1: 300; + const Grotesque2: 675; + const GrotesqueWyrm1: 303; + const GrotesqueWyrm2: 677; + const Guardian1: 102; + const Guardian2: 667; + const Hawk: 419; + const Heirophant1: 240; + const Heirophant2: 241; + const Heirophant3: 673; + const Heirophant4: 674; + const HellBuzzard: 112; + const HellCat: 86; + const HellClan1: 56; + const HellClan2: 587; + const HellSlinger: 376; + const HellSpawn1: 457; + const HellSpawn2: 683; + const HellSwarm: 90; + const HellWhip: 483; + const HollowOne: 101; + const Horror: 4; + const Horror1: 501; + const Horror2: 502; + const Horror3: 503; + const Horror4: 504; + const Horror5: 505; + const HorrorArcher1: 174; + const HorrorArcher2: 579; + const HorrorMage1: 277; + const HorrorMage2: 382; + const HorrorMage3: 386; + const HorrorMage4: 390; + const HorrorMage5: 623; + const HorrorMage6: 625; + const HorrorMage7: 626; + const Hs1: 560; + const HungryDead: 6; + const Huntress1: 83; + const Huntress2: 627; + const Hut: 528; + const Hydra1: 351; + const Hydra2: 352; + const Hydra3: 353; + const IceBoar: 455; + const IceSpawn: 458; + const Imp1: 492; + const Imp2: 493; + const Imp3: 494; + const Imp4: 495; + const Imp5: 496; + const Imp6: 688; + const Imp7: 689; + const Infidel1: 32; + const Infidel2: 600; + const InsaneHellSpawn: 467; + const InsaneIceSpawn: 468; + const Invader1: 31; + const Invader2: 602; + const Itchies: 87; + const JungleHunter: 50; + const JungleUrchin: 67; + const Larva: 283; + const Lasher: 480; + const LightningSpire: 371; + const Lord1: 506; + const Lord2: 507; + const Lord3: 508; + const Lord4: 509; + const Lord5: 510; + const Lord6: 652; + const Lord7: 653; + const Maggot: 227; + const Malachai: 408; + const Marauder: 30; + const Marauder2: 599; + const Master: 418; + const Mauler: 188; + const Mauler1: 529; + const Mauler12: 604; + const Mauler2: 530; + const Mauler3: 531; + const Mauler4: 532; + const Mauler5: 533; + const Mauler6: 619; + const MawFiend: 694; + const MawFiend2: 309; + const Council1: 345; + const Council2: 346; + const Council3: 347; + const Council4: 557; + const Minion1: 572; + const Minion2: 573; + const Enslaved: 453; + const MinionSlayerSpawner: 485; + const MinionSpawner: 484; + const Misshapen1: 12; + const Misshapen2: 582; + const MoonClan1: 53; + const MoonClan2: 585; + const BaalSubjectMummy: 105; + const Navi: 266; + const Flavie: 266; + const NightClan1: 54; + const NightClan2: 586; + const NightLord: 132; + const NightMarauder: 295; + const NightSlinger1: 375; + const NightSlinger2: 395; + const NightTiger: 85; + const OblivionKnight1: 312; + const OblivionKnight2: 701; + const OblivionKnight3: 702; + const OverLord: 481; + const OverSeer: 479; + const PitLord1: 361; + const PitLord2: 687; + const PitViper1: 76; + const PitViper2: 595; + const PlagueBearer: 9; + const PlagueBugs: 89; + const PoisonSpinner: 124; + const PreservedDead: 99; + const ProwlingDead: 438; + const QuillBear: 313; + const QuillRat1: 63; + const QuillRat2: 605; + const RatMan1: 141; + const RatMan2: 396; + const BoneFetish1: 212; + const RatMan4: 407; + const RatManShaman: 278; + const RazorBeast: 316; + const RazorPitDemon: 82; + const RazorSpine1: 66; + const RazorSpine2: 607; + const ReanimatedHorde: 437; + const Returned1: 1; + const Returned2: 649; + const ReturnedArcher1: 171; + const ReturnedArcher2: 578; + const ReturnedMage: 274; + const ReturnedMage1: 379; + const ReturnedMage2: 383; + const ReturnedMage3: 387; + const ReturnedMage4: 620; + const ReturnedMage5: 622; + const RiverStalkerHead: 262; + const RiverStalkerLimb: 259; + const RockDweller: 49; + const RockWorm: 69; + const RockWormEgg: 191; + const RockWormQueen: 285; + const RockWormYoung: 181; + const RotWalker: 436; + const SaberCat1: 84; + const SaberCat2: 628; + const Salamander1: 75; + const Salamander2: 596; + const SandFisher: 123; + const SandLeaper: 78; + const SandMaggot: 68; + const SandMaggotEgg: 190; + const SandMaggotYoung: 180; + const SandRaider1: 29; + const SandRaider2: 601; + const SandWarrior: 92; + const Scarab1: 93; + const Scarab2: 654; + const Sentry1: 411; + const Sentry2: 412; + const Sentry3: 415; + const Sentry4: 416; + const SerpentMagus1: 77; + const SerpentMagus2: 598; + const Sexton: 238; + const Skeleton: 0; + const SkeletonArcher: 170; + const Slayerexp1: 454; + const Slayerexp2: 682; + const Slinger1: 373; + const Slinger2: 610; + const Slinger3: 611; + const Slinger4: 612; + const SnowYeti1: 446; + const SnowYeti2: 447; + const SnowYeti3: 448; + const SnowYeti4: 449; + const SoulKiller: 691; + const SoulKiller1: 399; + const SoulKiller2: 144; + const SoulKiller3: 215; + const SoulKiller4: 658; + const SoulKiller5: 661; + const SoulKillerShaman1: 664; + const SoulKillerShaman2: 281; + const SpearCat: 394; + const SpearCat1: 374; + const Specter1: 40; + const Specter2: 633; + const SpiderMagus: 126; + const SpikeFiend1: 64; + const SpikeFiend2: 606; + const Spikefist: 130; + const SpikeGiant: 314; + const SteelWeevil1: 94; + const SteelWeevil2: 655; + const StormCaster1: 306; + const StormCaster2: 693; + const Strangler1: 305; + const Strangler2: 692; + const StygianDog: 302; + const StygianDoll1: 145; + const StygianDoll2: 216; + const StygianDoll3: 400; + const StygianDoll4: 660; + const StygianDoll5: 657; + const StygianDoll6: 690; + const StygianDollShaman1: 663; + const StygianDollShaman2: 282; + const StygianFury: 476; + const StygianHag: 299; + const StygianHarlot: 471; + const StygianWatcherHead: 263; + const StygianWatcherLimb: 260; + const Succubusexp1: 469; + const Succubusexp2: 634; + const Sucker: 114; + const SuckerNest: 334; + const Summoner: 250; + const SwampGhost: 119; + const Tainted: 11; + const Tainted2: 581; + const Taunt: 545; + const Temptress1: 472; + const Temptress2: 473; + const Temptress3: 635; + const Tentacle1: 562; + const Tentacle2: 563; + const Tentacle3: 564; + const Tentacle4: 565; + const Tentacle5: 566; + const ThornBeast: 65; + const ThornBrute: 315; + const ThornedHulk1: 127; + const ThornedHulk2: 609; + const Thrasher: 129; + const TombCreeper1: 80; + const TombCreeper2: 630; + const TombViper1: 73; + const TombViper2: 597; + const TrappedSoul1: 403; + const TrappedSoul2: 404; + const TreeLurker: 81; + const UndeadScavenger: 111; + const UnholyCorpse1: 439; + const UnholyCorpse2: 698; + const Unraveler1: 103; + const Unraveler2: 668; + const Urdar: 189; + const VenomLord1: 362; + const VenomLord2: 558; + const VileArcher1: 161; + const VileArcher2: 613; + const VileHunter: 44; + const VileLancer1: 166; + const VileLancer2: 615; + const VileTemptress: 470; + const VileWitch1: 475; + const VileWitch2: 638; + const WailingBeast: 27; + const WarpedFallen: 23; + const WarpedShaman: 62; + const Warrior: 417; + const WaterWatcherHead: 261; + const WaterWatcherLimb: 258; + const WingedNightmare: 113; + const Witch1: 637; + const Witch2: 477; + const Witch3: 478; + const Wolf1: 359; + const Wolf2: 420; + const Wolf3: 430; + const WolfRider1: 450; + const WolfRider2: 451; + const WolfRider3: 452; + const WorldKiller1: 679; + const WorldKiller2: 72; + const WorldKillerEgg1: 681; + const WorldKillerEgg2: 194; + const WorldKillerQueen: 288; + const WorldKillerYoung1: 680; + const WorldKillerYoung2: 184; + const Worm1: 551; + const Worm2: 552; + const Worm3: 553; + const Worm4: 554; + const Worm5: 555; + const Wraith1: 39; + const Wraith2: 632; + const Yeti: 25; + const Zakarumite: 235; + const Zealot1: 237; + const Zealot2: 671; + const Zealot3: 672; + const Zombie: 5; + + // Bosses/Ubers + const Andariel: 156; + const Duriel: 211; + const Mephisto: 242; + const Diablo: 243; + const DiabloClone: 333; + const ThroneBaal: 543; + const Baal: 544; + const BaalClone: 570; + const UberMephisto: 704; + const UberBaal: 705; + const UberIzual: 706; + const Lilith: 707; + const UberDuriel: 708; + const UberDiablo: 709; + + // Mini-Bosses + const TheSmith: 402; + const BloodRaven: 267; + const Radament: 229; + const TheSummoner: 250; + const Griswold: 365; + const Izual: 256; + const Hephasto: 409; + const KorlictheProtector: 540; + const TalictheDefender: 541; + const MadawctheGuardian: 542; + const ListerTheTormenter: 571; + const TheCowKing: 743; + const ColdwormtheBurrower: 284; + const Nihlathak: 526; + + // Objects + const Turret1: 348; + const Turret2: 349; + const Turret3: 350; + const CatapultS: 497; + const CatapultE: 498; + const CatapultSiege: 499; + const CatapultW: 500; + const Compellingorb: 366; + const GargoyleTrap: 273; + const MummyGenerator: 228; + const Stairs: 559; + const BarricadeDoor1: 432; + const BarricadeDoor2: 433; + const PrisonDoor: 434; + const BarricadeTower: 435; + const BarricadeWall1: 524; + const BarricadeWall2: 525; + + // Misc? + const Youngdiablo: 368; + const Left: 525; + const Life: 426; + const Effect: 574; + const Pet: 414; + const Prince: 249; + const POW: 534; + const Right: 524; + const Sage: 424; + const Town: 514; + const Cow: 179; + } + + export namespace summons { + namespace type { + const Valkyrie: 2; + const Golem: 3; + const Skeleton: 4; + const SkeletonMage: 5; + const Revive: 6; + const Mercenary: 7; + const Dopplezon: 8; + const Raven: 10; + const SpiritWolf: 11; + const Fenris: 12; + const DireWolf: 12; + const Totem: 13; + const Spirit: 13; + const Vine: 14; + const Grizzly: 15; + const ShadowWarrior: 16; + const Shadow: 16; + const AssassinTrap: 17; + const Hydra: 19; + } + + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } + + const ClayGolem: 289; + const Dopplezon: 356; + const Valkyrie: 357; + const FireGolem: 292; + const IronGolem: 291; + const NecroMage: 364; + const NecroSkeleton: 363; + const Poppy: 425; + const Wolverine: 423; + } + + export namespace mercs { + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } + + const Rogue: 271; + const Guard: 338; + const IronWolf: 359; + const A5Barb: 561; + } + + export namespace storage { + const Equipped: 1; + const Belt: 2; + const Inventory: 3; + const TradeWindow: 5; + const Cube: 6; + const Stash: 7; + } + + export namespace node { + const NotOnPlayer: 0; + const Storage: 1; + const Belt: 2; + const Equipped: 3; + const Cursor: 4; + } + + // Same apply's for merc with less things available + export namespace body { + const None: 0; + const Head: 1; + const Neck: 2; + const Torso: 3; + const Armor: 3; + const RightArm: 4; + const LeftArm: 5; + const RingRight: 6; + const RingLeft: 7; + const Belt: 8; + const Feet: 9; + const Gloves: 10; + const RightArmSecondary: 11; + const LeftArmSecondary: 12 + } + + export namespace items { + export namespace cost { + const ToBuy: 0; + const ToSell: 1; + const ToRepair: 2; + } + export namespace flags { + const Equipped: 0x00000001; + const InSocket: 0x00000008; + const Identified: 0x00000010; + const OnActiveWeaponSlot: 0x00000040; + const OnSwapWeaponSlot: 0x00000080; + const Broken: 0x00000100; + const FullRejuv: 0x00000400; + const Socketed: 0x00000800; + const InTradeGamble: 0x00002000; + const NotInSocket: 0x00004000; + const Ear: 0x00010000; + const StartingItem: 0x00020000; + const RuneQuestPotion: 0x00200000; + const Ethereal: 0x00400000; + const IsAnItem: 0x00800000; + const Personalized: 0x01000000; + const Runeword: 0x04000000; + } + export namespace mode { + const inStorage: 0; //Item inven stash cube store = Item inven stash cube store + const Equipped: 1; // Item equipped self or merc + const inBelt: 2; // Item in belt + const onGround: 3; // Item on ground + const onCursor: 4; // Item on cursor + const Dropping: 5; // Item being dropped + const Socketed: 6 // Item socketed in item + } + export namespace quality { + const LowQuality: 1; + const Normal: 2; + const Superior: 3; + const Magic: 4; + const Set: 5; + const Rare: 6; + const Unique: 7; + const Crafted: 8; + } + export namespace _class1 { + const Normal: 0; + const Exceptional: 1; + const Elite: 2; + } + export { _class1 as class }; + export namespace type { + const Shield: 2; + const Armor: 3; + const Gold: 4; + const BowQuiver: 5; + const CrossbowQuiver: 6; + const PlayerBodyPart: 7; + const Herb: 8; + const Potion: 9; + const Ring: 10; + const Elixir: 11; + const Amulet: 12; + const Charm: 13; + const notused0: 14; + const Boots: 15; + const Gloves: 16; + const notused1: 17; + const Book: 18; + const Belt: 19; + const Gem: 20; + const Torch: 21; + const Scroll: 22; + const notused2: 23; + const Scepter: 24; + const Wand: 25; + const Staff: 26; + const Bow: 27; + const Axe: 28; + const Club: 29; + const Sword: 30; + const Hammer: 31; + const Knife: 32; + const Spear: 33; + const Polearm: 34; + const Crossbow: 35; + const Mace: 36; + const Helm: 37; + const MissilePotion: 38; + const Quest: 39; + const Bodypart: 40; + const Key: 41; + const ThrowingKnife: 42; + const ThrowingAxe: 43; + const Javelin: 44; + const Weapon: 45; + const MeleeWeapon: 46; + const MissileWeapon: 47; + const ThrownWeapon: 48; + const ComboWeapon: 49; + const AnyArmor: 50; + const AnyShield: 51; + const Miscellaneous: 52; + const SocketFiller: 53; + const Secondhand: 54; + const StavesandRods: 55; + const Missile: 56; + const Blunt: 57; + const Jewel: 58; + const ClassSpecific: 59; + const AmazonItem: 60; + const BarbarianItem: 61; + const NecromancerItem: 62; + const PaladinItem: 63; + const SorceressItem: 64; + const AssassinItem: 65; + const DruidItem: 66; + const HandtoHand: 67; + const Orb: 68; + const VoodooHeads: 69; + const AuricShields: 70; + const PrimalHelm: 71; + const Pelt: 72; + const Cloak: 73; + const Rune: 74; + const Circlet: 75; + const HealingPotion: 76; + const ManaPotion: 77; + const RejuvPotion: 78; + const StaminaPotion: 79; + const AntidotePotion: 80; + const ThawingPotion: 81; + const SmallCharm: 82; + const LargeCharm: 83; + const GrandCharm: 84; + const AmazonBow: 85; + const AmazonSpear: 86; + const AmazonJavelin: 87; + const AssassinClaw: 88; + const MagicBowQuiv: 89; + const MagicxBowQuiv: 90; + const ChippedGem: 91; + const FlawedGem: 92; + const StandardGem: 93; + const FlawlessGem: 94; + const PerfectgGem: 95; + const Amethyst: 96; + const Diamond: 97; + const Emerald: 98; + const Ruby: 99; + const Sapphire: 100; + const Topaz: 101; + const Skull: 102; + } + + // Weapons + export const HandAxe: 0; + export const Axe: 1; + export const DoubleAxe: 2; + export const MilitaryPick: 3; + export const WarAxe: 4; + export const LargeAxe: 5; + export const BroadAxe: 6; + export const BattleAxe: 7; + export const GreatAxe: 8; + export const GiantAxe: 9; + export const Wand: 10; + export const YewWand: 11; + export const BoneWand: 12; + export const GrimWand: 13; + export const Club: 14; + export const Scepter: 15; + export const GrandScepter: 16; + export const WarScepter: 17; + export const SpikedClub: 18; + export const Mace: 19; + export const MorningStar: 20; + export const Flail: 21; + export const WarHammer: 22; + export const Maul: 23; + export const GreatMaul: 24; + export const ShortSword: 25; + export const Scimitar: 26; + export const Sabre: 27; + export const Falchion: 28; + export const CrystalSword: 29; + export const BroadSword: 30; + export const LongSword: 31; + export const WarSword: 32; + export const Two_HandedSword: 33; + export const Claymore: 34; + export const GiantSword: 35; + export const BastardSword: 36; + export const Flamberge: 37; + export const GreatSword: 38; + export const Dagger: 39; + export const Dirk: 40; + export const Kris: 41; + export const Blade: 42; + export const ThrowingKnife: 43; + export const ThrowingAxe: 44; + export const BalancedKnife: 45; + export const BalancedAxe: 46; + export const Javelin: 47; + export const Pilum: 48; + export const ShortSpear: 49; + export const Glaive: 50; + export const ThrowingSpear: 51; + export const Spear: 52; + export const Trident: 53; + export const Brandistock: 54; + export const Spetum: 55; + export const Pike: 56; + export const Bardiche: 57; + export const Voulge: 58; + export const Scythe: 59; + export const Poleaxe: 60; + export const Halberd: 61; + export const WarScythe: 62; + export const ShortStaff: 63; + export const LongStaff: 64; + export const GnarledStaff: 65; + export const BattleStaff: 66; + export const WarStaff: 67; + export const ShortBow: 68; + export const HuntersBow: 69; + export const LongBow: 70; + export const CompositeBow: 71; + export const ShortBattleBow: 72; + export const LongBattleBow: 73; + export const ShortWarBow: 74; + export const LongWarBow: 75; + export const LightCrossbow: 76; + export const Crossbow: 77; + export const HeavyCrossbow: 78; + export const RepeatingCrossbow: 79; + export const Hatchet: 93; + export const Cleaver: 94; + export const TwinAxe: 95; + export const Crowbill: 96; + export const Naga: 97; + export const MilitaryAxe: 98; + export const BeardedAxe: 99; + export const Tabar: 100; + export const GothicAxe: 101; + export const AncientAxe: 102; + export const BurntWand: 103; + export const PetrifiedWand: 104; + export const TombWand: 105; + export const GraveWand: 106; + export const Cudgel: 107; + export const RuneScepter: 108; + export const HolyWaterSprinkler: 109; + export const DivineScepter: 110; + export const BarbedClub: 111; + export const FlangedMace: 112; + export const JaggedStar: 113; + export const Knout: 114; + export const BattleHammer: 115; + export const WarClub: 116; + export const MarteldeFer: 117; + export const Gladius: 118; + export const Cutlass: 119; + export const Shamshir: 120; + export const Tulwar: 121; + export const DimensionalBlade: 122; + export const BattleSword: 123; + export const RuneSword: 124; + export const AncientSword: 125; + export const Espandon: 126; + export const DacianFalx: 127; + export const TuskSword: 128; + export const GothicSword: 129; + export const Zweihander: 130; + export const ExecutionerSword: 131; + export const Poignard: 132; + export const Rondel: 133; + export const Cinquedeas: 134; + export const Stiletto: 135; + export const BattleDart: 136; + export const Francisca: 137; + export const WarDart: 138; + export const Hurlbat: 139; + export const WarJavelin: 140; + export const GreatPilum: 141; + export const Simbilan: 142; + export const Spiculum: 143; + export const Harpoon: 144; + export const WarSpear: 145; + export const Fuscina: 146; + export const WarFork: 147; + export const Yari: 148; + export const Lance: 149; + export const LochaberAxe: 150; + export const Bill: 151; + export const BattleScythe: 152; + export const Partizan: 153; + export const Bec_de_Corbin: 154; + export const GrimScythe: 155; + export const JoStaff: 156; + export const Quarterstaff: 157; + export const CedarStaff: 158; + export const GothicStaff: 159; + export const RuneStaff: 160; + export const EdgeBow: 161; + export const RazorBow: 162; + export const CedarBow: 163; + export const DoubleBow: 164; + export const ShortSiegeBow: 165; + export const LargeSiegeBow: 166; + export const RuneBow: 167; + export const GothicBow: 168; + export const Arbalest: 169; + export const SiegeCrossbow: 170; + export const Ballista: 171; + export const Chu_Ko_Nu: 172; + export const Katar: 175; + export const WristBlade: 176; + export const HatchetHands: 177; + export const Cestus: 178; + export const Claws: 179; + export const BladeTalons: 180; + export const ScissorsKatar: 181; + export const Quhab: 182; + export const WristSpike: 183; + export const Fascia: 184; + export const HandScythe: 185; + export const GreaterClaws: 186; + export const GreaterTalons: 187; + export const ScissorsQuhab: 188; + export const Suwayyah: 189; + export const WristSword: 190; + export const WarFist: 191; + export const BattleCestus: 192; + export const FeralClaws: 193; + export const RunicTalons: 194; + export const ScissorsSuwayyah: 195; + export const Tomahawk: 196; + export const SmallCrescent: 197; + export const EttinAxe: 198; + export const WarSpike: 199; + export const BerserkerAxe: 200; + export const FeralAxe: 201; + export const Silver_edgedAxe: 202; + export const Decapitator: 203; + export const ChampionAxe: 204; + export const GloriousAxe: 205; + export const PolishedWand: 206; + export const GhostWand: 207; + export const LichWand: 208; + export const UnearthedWand: 209; + export const Truncheon: 210; + export const MightyScepter: 211; + export const SeraphRod: 212; + export const Caduceus: 213; + export const TyrantClub: 214; + export const ReinforcedMace: 215; + export const DevilStar: 216; + export const Scourge: 217; + export const LegendaryMallet: 218; + export const OgreMaul: 219; + export const ThunderMaul: 220; + export const Falcata: 221; + export const Ataghan: 222; + export const ElegantBlade: 223; + export const HydraEdge: 224; + export const PhaseBlade: 225; + export const ConquestSword: 226; + export const CrypticSword: 227; + export const MythicalSword: 228; + export const LegendSword: 229; + export const HighlandBlade: 230; + export const BalrogBlade: 231; + export const ChampionSword: 232; + export const ColossusSword: 233; + export const ColossusBlade: 234; + export const BoneKnife: 235; + export const MithrilPoint: 236; + export const FangedKnife: 237; + export const LegendSpike: 238; + export const FlyingKnife: 239; + export const FlyingAxe: 240; + export const WingedKnife: 241; + export const WingedAxe: 242; + export const HyperionJavelin: 243; + export const StygianPilum: 244; + export const BalrogSpear: 245; + export const GhostGlaive: 246; + export const WingedHarpoon: 247; + export const HyperionSpear: 248; + export const StygianPike: 249; + export const Mancatcher: 250; + export const GhostSpear: 251; + export const WarPike: 252; + export const OgreAxe: 253; + export const ColossusVoulge: 254; + export const Thresher: 255; + export const CrypticAxe: 256; + export const GreatPoleaxe: 257; + export const GiantThresher: 258; + export const WalkingStick: 259; + export const Stalagmite: 260; + export const ElderStaff: 261; + export const Shillelagh: 262; + const ArchonStaff: 263; + const SpiderBow: 264; + const BladeBow: 265; + const ShadowBow: 266; + const GreatBow: 267; + const DiamondBow: 268; + const CrusaderBow: 269; + const WardBow: 270; + const HydraBow: 271; + const PelletBow: 272; + const GorgonCrossbow: 273; + const ColossusCrossbow: 274; + const DemonCrossbow: 275; + const EagleOrb: 276; + const SacredGlobe: 277; + const SmokedSphere: 278; + const ClaspedOrb: 279; + const JaredsStone: 280; + const StagBow: 281; + const ReflexBow: 282; + const MaidenSpear: 283; + const MaidenPike: 284; + const MaidenJavelin: 285; + const GlowingOrb: 286; + const CrystallineGlobe: 287; + const CloudySphere: 288; + const SparklingBall: 289; + const SwirlingCrystal: 290; + const AshwoodBow: 291; + const CeremonialBow: 292; + const CeremonialSpear: 293; + const CeremonialPike: 294; + const CeremonialJavelin: 295; + const HeavenlyStone: 296; + const EldritchOrb: 297; + const DemonHeart: 298; + const VortexOrb: 299; + const DimensionalShard: 300; + const MatriarchalBow: 301; + const GrandMatronBow: 302; + const MatriarchalSpear: 303; + const MatriarchalPike: 304; + const MatriarchalJavelin: 305; + const Cap: 306; + const SkullCap: 307; + const Helm: 308; + const FullHelm: 309; + const GreatHelm: 310; + const Crown: 311; + const Mask: 312; + const QuiltedArmor: 313; + const LeatherArmor: 314; + const HardLeatherArmor: 315; + const StuddedLeather: 316; + const RingMail: 317; + const ScaleMail: 318; + const ChainMail: 319; + const BreastPlate: 320; + const SplintMail: 321; + const PlateMail: 322; + const FieldPlate: 323; + const GothicPlate: 324; + const FullPlateMail: 325; + const AncientArmor: 326; + const LightPlate: 327; + const Buckler: 328; + const SmallShield: 329; + const LargeShield: 330; + const KiteShield: 331; + const TowerShield: 332; + const GothicShield: 333; + const LeatherGloves: 334; + const HeavyGloves: 335; + const ChainGloves: 336; + const LightGauntlets: 337; + const Gauntlets: 338; + const Boots: 339; + const HeavyBoots: 340; + const ChainBoots: 341; + const LightPlatedBoots: 342; + const Greaves: 343; + const Sash: 344; + const LightBelt: 345; + const Belt: 346; + const HeavyBelt: 347; + const PlatedBelt: 348; + const BoneHelm: 349; + const BoneShield: 350; + const SpikedShield: 351; + const WarHat: 352; + const Sallet: 353; + const Casque: 354; + const Basinet: 355; + const WingedHelm: 356; + const GrandCrown: 357; + const DeathMask: 358; + const GhostArmor: 359; + const SerpentskinArmor: 360; + const DemonhideArmor: 361; + const TrellisedArmor: 362; + const LinkedMail: 363; + const TigulatedMail: 364; + const MeshArmor: 365; + const Cuirass: 366; + const RussetArmor: 367; + const TemplarCoat: 368; + const SharktoothArmor: 369; + const EmbossedPlate: 370; + const ChaosArmor: 371; + const OrnatePlate: 372; + const MagePlate: 373; + const Defender: 374; + const RoundShield: 375; + const Scutum: 376; + const DragonShield: 377; + const Pavise: 378; + const AncientShield: 379; + const DemonhideGloves: 380; + const SharkskinGloves: 381; + const HeavyBracers: 382; + const BattleGauntlets: 383; + const WarGauntlets: 384; + const DemonhideBoots: 385; + const SharkskinBoots: 386; + const MeshBoots: 387; + const BattleBoots: 388; + const WarBoots: 389; + const DemonhideSash: 390; + const SharkskinBelt: 391; + const MeshBelt: 392; + const BattleBelt: 393; + const WarBelt: 394; + const GrimHelm: 395; + const GrimShield: 396; + const BarbedShield: 397; + const WolfHead: 398; + const HawkHelm: 399; + const Antlers: 400; + const FalconMask: 401; + const SpiritMask: 402; + const JawboneCap: 403; + const FangedHelm: 404; + const HornedHelm: 405; + const AssaultHelmet: 406; + const AvengerGuard: 407; + const Targe: 408; + const Rondache: 409; + const HeraldicShield: 410; + const AerinShield: 411; + const CrownShield: 412; + const PreservedHead: 413; + const ZombieHead: 414; + const UnravellerHead: 415; + const GargoyleHead: 416; + const DemonHead: 417; + const Circlet: 418; + const Coronet: 419; + const Tiara: 420; + const Diadem: 421; + const Shako: 422; + const Hydraskull: 423; + const Armet: 424; + const GiantConch: 425; + const SpiredHelm: 426; + const Corona: 427; + const Demonhead: 428; + const DuskShroud: 429; + const Wyrmhide: 430; + const ScarabHusk: 431; + const WireFleece: 432; + const DiamondMail: 433; + const LoricatedMail: 434; + const Boneweave: 435; + const GreatHauberk: 436; + const BalrogSkin: 437; + const HellforgePlate: 438; + const KrakenShell: 439; + const LacqueredPlate: 440; + const ShadowPlate: 441; + const SacredArmor: 442; + const ArchonPlate: 443; + const Heater: 444; + const Luna: 445; + const Hyperion: 446; + const Monarch: 447; + const Aegis: 448; + const Ward: 449; + const BrambleMitts: 450; + const VampireboneGloves: 451; + const Vambraces: 452; + const CrusaderGauntlets: 453; + const OgreGauntlets: 454; + const WyrmhideBoots: 455; + const ScarabshellBoots: 456; + const BoneweaveBoots: 457; + const MirroredBoots: 458; + const MyrmidonGreaves: 459; + const SpiderwebSash: 460; + const VampirefangBelt: 461; + const MithrilCoil: 462; + const TrollBelt: 463; + const ColossusGirdle: 464; + const BoneVisage: 465; + const TrollNest: 466; + const BladeBarrier: 467; + const AlphaHelm: 468; + const GriffonHeaddress: 469; + const HuntersGuise: 470; + const SacredFeathers: 471; + const TotemicMask: 472; + const JawboneVisor: 473; + const LionHelm: 474; + const RageMask: 475; + const SavageHelmet: 476; + const SlayerGuard: 477; + const AkaranTarge: 478; + const AkaranRondache: 479; + const ProtectorShield: 480; + const GildedShield: 481; + const RoyalShield: 482; + const MummifiedTrophy: 483; + const FetishTrophy: 484; + const SextonTrophy: 485; + const CantorTrophy: 486; + const HierophantTrophy: 487; + const BloodSpirit: 488; + const SunSpirit: 489; + const EarthSpirit: 490; + const SkySpirit: 491; + const DreamSpirit: 492; + const CarnageHelm: 493; + const FuryVisor: 494; + const DestroyerHelm: 495; + const ConquerorCrown: 496; + const GuardianCrown: 497; + const SacredTarge: 498; + const SacredRondache: 499; + const KurastShield: 500; + const ZakarumShield: 501; + const VortexShield: 502; + const MinionSkull: 503; + const HellspawnSkull: 504; + const OverseerSkull: 505; + const SuccubusSkull: 506; + const BloodlordSkull: 507; + const Amulet: 520; + const Ring: 522; + const Arrows: 526; + const Bolts: 528; + const Jewel: 643; + + // Misc? + const Elixir: 508; + const Torch: 527; + const Heart: 531; + const Brain: 532; + const Jawbone: 533; + const Eye: 534; + const Horn: 535; + const Tail: 536; + const Flag: 537; + const Fang: 538; + const Quill: 539; + const Soul: 540; + const Scalp: 541; + const Spleen: 542; + const Key: 543; + const Ear: 556; + const Herb: 602; + const anevilforce: 609; + // Potions, tomes/scrolls, gold + const TomeofTownPortal: 518; + const TomeofIdentify: 519; + const ScrollofTownPortal: 529; + const ScrollofIdentify: 530; + const RancidGasPotion: 80; + const OilPotion: 81; + const ChokingGasPotion: 82; + const ExplodingPotion: 83; + const StranglingGasPotion: 84; + const FulminatingPotion: 85; + const StaminaPotion: 513; + const AntidotePotion: 514; + const RejuvenationPotion: 515; + const FullRejuvenationPotion: 516; + const ThawingPotion: 517; + const MinorHealingPotion: 587; + const LightHealingPotion: 588; + const HealingPotion: 589; + const GreaterHealingPotion: 590; + const SuperHealingPotion: 591; + const MinorManaPotion: 592; + const LightManaPotion: 593; + const ManaPotion: 594; + const GreaterManaPotion: 595; + const SuperManaPotion: 596; + const Gold: 523; + // Charms + const SmallCharm: 603; + const LargeCharm: 604; + const GrandCharm: 605; + + export namespace quest { + // Act 1 + const WirtsLeg: 88; + const HoradricMalus: 89; + const ScrollofInifuss: 524; + const KeytotheCairnStones: 525; + // Act 2 + const FinishedStaff: 91; + const HoradricStaff: 91; + const IncompleteStaff: 92; + const ShaftoftheHoradricStaff: 92; + const ViperAmulet: 521; + const TopoftheHoradricStaff: 521; + const Cube: 549; + const BookofSkill: 552; + // Act 3 + const DecoyGidbinn: 86; + const TheGidbinn: 87; + const KhalimsFlail: 173; + const KhalimsWill: 174; + const PotofLife: 545; + const AJadeFigurine: 546; + const JadeFigurine: 546; + const TheGoldenBird: 547; + const LamEsensTome: 548; + const KhalimsEye: 553; + const KhalimsHeart: 554; + const KhalimsBrain: 555; + // Act 4 + const HellForgeHammer: 90; + const Soulstone: 551; + const MephistosSoulstone: 551; + // Act 5 + const MalahsPotion: 644; + const ScrollofKnowledge: 645; + const ScrollofResistance: 646; + // Pandemonium Event + const KeyofTerror: 647; + const KeyofHate: 648; + const KeyofDestruction: 649; + const DiablosHorn: 650; + const BaalsEye: 651; + const MephistosBrain: 652; + const StandardofHeroes: 658; + // Essences/Token + const TokenofAbsolution: 653; + const TwistedEssenceofSuffering: 654; + const ChargedEssenceofHatred: 655; + const BurningEssenceofTerror: 656; + const FesteringEssenceofDestruction: 657; + // Misc + const TheBlackTowerKey: 544; + } + export namespace runes { + const El: 610; + const Eld: 611; + const Tir: 612; + const Nef: 613; + const Eth: 614; + const Ith: 615; + const Tal: 616; + const Ral: 617; + const Ort: 618; + const Thul: 619; + const Amn: 620; + const Sol: 621; + const Shael: 622; + const Dol: 623; + const Hel: 624; + const Io: 625; + const Lum: 626; + const Ko: 627; + const Fal: 628; + const Lem: 629; + const Pul: 630; + const Um: 631; + const Mal: 632; + const Ist: 633; + const Gul: 634; + const Vex: 635; + const Ohm: 636; + const Lo: 637; + const Sur: 638; + const Ber: 639; + const Jah: 640; + const Cham: 641; + const Zod: 642; + } + export namespace gems { + export namespace Perfect { + const Amethyst: 561; + const Topaz: 566; + const Sapphire: 571; + const Emerald: 576; + const Ruby: 581; + const Diamond: 586; + const Skull: 601; + } + export namespace Flawless { + const Amethyst: 560; + const Topaz: 565; + const Sapphire: 570; + const Emerald: 575; + const Ruby: 580; + const Diamond: 585; + const Skull: 600; + } + export namespace Normal { + const Amethyst: 559; + const Topaz: 564; + const Sapphire: 569; + const Emerald: 574; + const Ruby: 579; + const Diamond: 584; + const Skull: 599; + } + export namespace Flawed { + const Amethyst: 558; + const Topaz: 563; + const Sapphire: 568; + const Emerald: 573; + const Ruby: 578; + const Diamond: 583; + const Skull: 598; + } + export namespace Chipped { + const Amethyst: 557; + const Topaz: 562; + const Sapphire: 567; + const Emerald: 572; + const Ruby: 577; + const Diamond: 582; + const Skull: 597; + } + } + } + + // locale strings + export namespace locale { + export namespace monsters { + // bosses + const Andariel: 3021; + const Duriel: 3054; + const Mephisto: 3062; + const Diablo: 3060; + const Baal: 3061; + // Mini bosses + const BloodRaven: 3111; + const TreeheadWoodFist: 2873; + const TheCountess: 2875; + const TheSmith: 2889; + const Radament: 2879; + const TheSummoner: 3099; + const HephastoTheArmorer: 1067; + const Izual: 1014; + const ShenktheOverseer: 22435; + // Uniques + const Corpsefire: 3319; + const TheCowKing: 2850; + const GrandVizierofChaos: 2851; + const LordDeSeis: 2852; + const InfectorofSouls: 2853; + const RiftwraiththeCannibal: 2854; + const Taintbreeder: 2855; + const TheTormentor: 2856; + const Darkwing: 2857; + const MafferDragonhand: 2858; + const WyandVoidbringer: 2859; + const ToorcIcefist: 2860; + const BremmSparkfist: 2861; + const GelebFlamefinger: 2862; + const IsmailVilehand: 2863; + const IcehawkRiftwing: 2864; + const BattlemaidSarina: 2865; + const Stormtree: 2866; + const WitchDoctorEndugu: 2867; + const SszarkTheBurning: 2868; + const Bishibosh: 2869; + const Bonebreaker: 2870; + const Coldcrow: 2871; + const Rakanishu: 2872; + const Griswold: 2874; + const PitspawnFouldog: 2876; + const FlamespiketheCrawler: 2877; + const BoneAsh: 2878; + const BloodwitchtheWild: 2880; + const Fangskin: 2881; + const Beetleburst: 2882; + const CreepingFeature: 2883; + const ColdwormtheBurrower: 2884; + const FireEye: 2885; + const DarkElder: 2886; + const AncientKaatheSoulless: 2888; + const SharpToothSayer: 22493; + const SnapchipShatter: 22496; + const Pindleskin: 22497; + const ThreshSocket: 22498; + const EyebacktheUnleashed: 22499; + const EldritchtheRectifier: 22500; + const DacFarren: 22501; + const BonesawBreaker: 22502; + const Frozenstein: 22504; + const Rogue: 2897; + const StygianDoll: 2898; + const SoulKiller: 2899; + const Flayer: 2900; + const Fetish: 2901; + const RatMan: 2902; + const UndeadStygianDoll: 2903; + const UndeadSoulKiller: 2904; + const UndeadFlayer: 2905; + const UndeadFetish: 2906; + const UndeadRatMan: 2907; + const DarkFamiliar: 2908; + const BloodDiver: 2909; + const Gloombat: 2910; + const DesertWing: 2911; + const TheBanished: 2912; + const BloodLord: 2913; + const DarkLord: 2914; + const NightLord: 2915; + const GhoulLord: 2916; + const Spikefist: 2917; + const Thrasher: 2918; + const BrambleHulk: 2919; + const ThornedHulk: 2920; + const SpiderMagus: 2921; + const FlameSpider: 2922; + const PoisonSpinner: 2923; + const SandFisher: 2924; + const Arach: 2925; + const BloodWing: 2926; + const BloodHook: 2927; + const Feeder: 2928; + const Sucker: 2929; + const WingedNightmare: 2930; + const HellBuzzard: 2931; + const UndeadScavenger: 2932; + const CarrionBird: 2933; + const Unraveler: 2934; + const Guardian: 2935; + const HollowOne: 2936; + const HoradrimAncient: 2937; + const BoneScarab: 2938; + const SteelScarab: 2939; + const Scarab: 2940; + const DeathBeetle: 2941; + const DungSoldier: 2942; + const HellSwarm: 2943; + const PlagueBugs: 2944; + const BlackLocusts: 2945; + const Itchies: 2946; + const HellCat: 2947; + const NightTiger: 2948; + const SaberCat: 2949; + const Huntress: 2950; + const CliffLurker: 2951; + const TreeLurker: 2952; + const CaveLeaper: 2953; + const TombCreeper: 2954; + const SandLeaper: 2955; + const TombViper: 2956; + const PitViper: 2957; + const Salamander: 2958; + const ClawViper: 2959; + const SerpentMagus: 2960; + const BloodMaggot: 2961; + const GiantLamprey: 2962; + const Devourer: 2963; + const RockWorm: 2964; + const SandMaggot: 2965; + const BushBarb: 2966; + const RazorSpine: 2967; + const ThornBeast: 2968; + const SpikeFiend: 2969; + const QuillRat: 2970; + const HellClan: 2971; + const MoonClan: 2972; + const NightClan: 2973; + const DeathClan: 2974; + const BloodClan: 2975; + const TempleGuard: 2976; + const DoomApe: 2977; + const JungleHunter: 2978; + const RockDweller: 2979; + const DuneBeast: 2980; + const FleshHunter: 2981; + const BlackRogue: 2982; + const DarkStalker: 2983; + const VileHunter: 2984; + const DarkHunter: 2985; + const DarkShape: 2986; + const Apparition: 2987; + const Specter: 2988; + const Wraith: 2989; + const Ghost: 2990; + const Assailant: 2991; + const Infidel: 2992; + const Invader: 2993; + const Marauder: 2994; + const SandRaider: 2995; + const GargantuanBeast: 2996; + const WailingBeast: 2997; + const Yeti: 2998; + const Crusher: 2999; + const Brute: 3000; + const CloudStalker: 3001; + const BlackVulture: 3002; + const BlackRaptor: 3003; + const BloodHawk: 3004; + const FoulCrow: 3005; + const PlagueBearer: 3006; + const Ghoul: 3007; + const DrownedCarcass: 3008; + const HungryDead: 3009; + const Zombie: 3010; + const Horror: 3012; + const Returned: 3013; + const BurningDead: 3014; + const BoneWarrior: 3015; + const Damned: 3016; + const Disfigured: 3017; + const Misshapen: 3018; + const Tainted: 3019; + const Afflicted: 3020; + const Camel: 3033; + const Cadaver: 3034; + const PreservedDead: 3035; + const Embalmed: 3036; + const DriedCorpse: 3037; + const Decayed: 3038; + const Urdar: 3039; + const Mauler: 3040; + const Gorebelly: 3041; + const Blunderbore: 3042; + const BloodMaggotYoung: 3043; + const GiantLampreyYoung: 3044; + const DevourerYoung: 3045; + const RockWormYoung: 3046; + const SandMaggotYoung: 3047; + const BloodMaggotEgg: 3048; + const GiantLampreyEgg: 3049; + const DevourerEgg: 3050; + const RockWormEgg: 3051; + const SandMaggotEgg: 3052; + const Maggot: 3053; + const BloodHawkNest: 3055; + const FlyingScimitar: 3056; + const CloudStalkerNest: 3057; + const BlackRaptorNest: 3058; + const FoulCrowNest: 3059; + const Cantor: 3063; + const Heirophant: 3064; + const Sexton: 3065; + const Zealot: 3066; + const Faithful: 3067; + const Zakarumite: 3068; + const BlackSoul: 3069; + const BurningSoul: 3070; + const SwampGhost: 3071; + const Gloam: 3072; + const WarpedShaman: 3073; + const DarkShaman: 3074; + const DevilkinShaman: 3075; + const CarverShaman: 3076; + const FallenShaman: 3077; + const WarpedOne: 3078; + const DarkOne: 3079; + const Devilkin: 3080; + const Carver: 3081; + const Fallen: 3082; + const ReturnedArcher: 3083; + const HorrorArcher: 3084; + const BurningDeadArcher: 3085; + const BoneArcher: 3086; + const CorpseArcher: 3087; + const SkeletonArcher: 3088; + const FleshLancer: 3089; + const BlackLancer: 3090; + const DarkLancer: 3091; + const VileLancer: 3092; + const DarkSpearwoman: 3093; + const FleshArcher: 3094; + const BlackArcher: 3095; + const DarkRanger: 3096; + const VileArcher: 3097; + const DarkArcher: 3098; + const StygianDollShaman: 3100; + const SoulKillerShaman: 3101; + const FlayerShaman: 3102; + const FetishShaman: 3103; + const RatManShaman: 3104; + const HorrorMage: 3105; + const BurningDeadMage: 3106; + const BoneMage: 3107; + const CorpseMage: 3108; + const ReturnedMage: 3109; + const GargoyleTrap: 3110; + const NightMarauder: 3121; + const FireGolem: 3122; + const IronGolem: 3123; + const BloodGolem: 3124; + const ClayGolem: 3125; + const BloodMaggotQueen: 3126; + const GiantLampreyQueen: 3127; + const DevourerQueen: 3128; + const RockWormQueen: 3129; + const SandMaggotQueen: 3130; + const SlimePrince: 3131; + const BogCreature: 3132; + const SwampDweller: 3133; + const BarbedGiant: 3134; + const RazorBeast: 3135; + const ThornBrute: 3136; + const SpikeGiant: 3137; + const QuillBear: 3138; + const CouncilMember: 3139; + const DarkWanderer: 3141; + const HellSlinger: 3142; + const NightSlinger: 3143; + const SpearCat: 3144; + const Slinger: 3145; + const FireTower: 3146; + const LightningSpire: 3147; + const PitLord: 3148; + const Balrog: 3149; + const VenomLord: 3150; + const IronWolf: 3151; + const InvisoSpawner: 3152; + const OblivionKnight: 3153; + const Mage: 3154; + const AbyssKnight: 3155; + const FighterMage: 3156; + const DoomKnight: 3157; + const Fighter: 3158; + const MawFiend: 3159; + const CorpseSpitter: 3160; + const Corpulent: 3161; + const StormCaster: 3162; + const Strangler: 3163; + const DoomCaster: 3164; + const GrotesqueWyrm: 3165; + const StygianDog: 3166; + const FleshBeast: 3167; + const Grotesque: 3168; + const StygianHag: 3169; + const FleshSpawner: 3170; + const RogueScout: 3171; + const BloodWingNest: 3172; + const BloodHookNest: 3173; + const FeederNest: 3174; + const SuckerNest: 3175; + const Hydra: 3325; + } + namespace npcs { + const Asheara: 1008; + const Hratli: 1009; + const Alkor: 1010; + const Ormus: 1011; + const Natalya: 1012; + const Tyrael: 1013; + const Izual1: 1014; + const Izual2: 1015; + const Jamella: 1016; + const Halbu: 1017; + const Hadriel: 1018; + const Hazade: 1019; + const Alhizeer: 1020; + const Azrael: 1021; + const Ahsab: 1022; + const Chalan: 1023; + const Haseen: 1024; + const Razan: 1025; + const Emilio: 1026; + const Pratham: 1027; + const Fazel: 1028; + const Jemali: 1029; + const Kasim: 1030; + const Gulzar: 1031; + const Mizan: 1032; + const Leharas: 1033; + const Durga: 1034; + const Neeraj: 1035; + const Ilzan: 1036; + const Zanarhi: 1037; + const Waheed: 1038; + const Vikhyat: 1039; + const Jelani: 1040; + const Barani: 1041; + const Jabari: 1042; + const Devak: 1043; + const Raldin: 1044; + const Telash: 1045; + const Ajheed: 1046; + const Narphet: 1047; + const Khaleel: 1048; + const Phaet: 1049; + const Geshef: 1050; + const Vanji: 1051; + const Haphet: 1052; + const Thadar: 1053; + const Yatiraj: 1054; + const Rhadge: 1055; + const Yashied: 1056; + const Lharhad: 1057; + const Flux: 1058; + const Scorch: 1059; + //const Natalya: 3022; both 1012 and 3022 return Natalya? + const DeckardCain: 2890; + const Gheed: 2891; + const Akara: 2892; + const Kashya: 2893; + const Charsi: 2894; + const Warriv: 2895; + const Drognan: 3023; + const Atma: 3024; + const Fara: 3025; + const Lysander: 3026; + const Jerhyn: 3028; + const Geglash: 3029; + const Elzix: 3030; + const Greiz: 3031; + const Flavie: 3112; + const Kaelan: 3113; + const Meshif: 3114; + const Larzuk: 22476; + const Anya: 22477; + const Malah: 22478; + const Nihlathak1: 22479; + const QualKehk: 22480; + const Guard: 22481; + const Combatant: 22482; + const Nihlathak2: 22483; + } + namespace items { + const KhalimsFlail: 1060; + const KhalimsWill1: 1061; + const KhalimsFlail2: 1062; + const KhalimsWill2: 1063; + const KhalimsEye: 1064; + const KhalimsBrain: 1065; + const KhalimsHeart: 1066; + const ScrollofInifuss: 2216; + const KeytotheCairnStones: 2217; + const AJadeFigurine: 2227; + const TheGoldenBird: 2228; + const LamEsensTome1: 2229; + const LamEsensTome2: 2230; + const HoradricCube: 2231; + const HoradricScroll: 2232; + const MephistosSoulstone: 2233; + const Ear: 2235; + const AmuletoftheViper: 2697; + const StaffofKings: 2698; + const HoradricStaff: 2699; + + // Sets + // Angelic Rainment + const AngelicsSword: 10172; + const AngelicsArmor: 10173; + const AngelicsRing: 10174; + const AngelicsAmulet: 10175; + // Arcannas Tricks + const ArcannasAmulet: 10180; + const ArcannasStaff: 10181; + const ArcannasHelmet: 10182; + const ArcannasArmor: 10183; + // Artic Gear + const ArticsBow: 10176; + const ArticsArmor: 10177; + const ArticsBelt: 10178; + const ArticsGloves: 10179; + // Berserkers Gear + const BerserkersHelmet: 10166; + const BerserkersAxe: 10167; + const BerserkersArmor: 10168; + // Cathans Traps + const CathansRing: 10147; + const CathansAmulet: 10148; + const CathansHelmet: 10149; + const CathansArmor: 10150; + const CathansStaff: 10151; + // Civerbs Gear + const CiverbsShield: 10122; + const CiverbsAmulet: 10123; + const CiverbsScepter: 10124; + // Clegaws Brace + const ClegawsSword: 10128; + const ClegawsShield: 10129; + const ClegawsGloves: 10130; + // Deaths Disguise + const DeathsGloves: 10169; + const DeathsBelt: 10170; + const DeathsSword: 10171; + // Hsarus Defense + const HsarusBoots: 10125; + const HsarusShield: 10126; + const HsarusBelt: 10127; + // Infernal Tools + const InfernalsHelmet: 10163; + const InfernalsWand: 10164; + const InfernalsBelt: 10165; + // Irathas Finery + const IrathasBelt: 10131; + const IrathasHelmet: 10132; + const IrathasGloves: 10133; + const IrathasAmulet: 10134; + // Isenharts Armory + const IsenhartsHelmet: 10135; + const IsenhartsArmor: 10136; + const IsenhartsShield: 10137; + const IsenhartsSword: 10138; + // Milabrega Regalia + const MilabregasArmor: 10143; + const MilabregasHelmet: 10144; + const MilabregasScepter: 10145; + const MilabregasShield: 10146; + // Sigons + const SigonsHelmet: 10157; + const SigonsArmor: 10158; + const SigonsGloves: 10159; + const SigonsBoots: 10160; + const SigonsBelt: 10161; + const SigonsShield: 10162; + // Tancreds + const TancredsPick: 10152; + const TancredsArmor: 10153; + const TancredsBoots: 10154; + const TancredsAmulet: 10155; + const TancredsHelmet: 10156; + // Vidalas + const VidalasAmulet: 10139; + const VidalasArmor: 10140; + const VidalasBoots: 10141; + const VidalasBow: 10142; + + // LoD Sets + // Aldurs's Legacy + const AldursHelmet: 21697; + const AldursArmor: 21698; + const AldursBoots: 21700; + const AldursMace: 21847; + // Bul-Kathos's Children + const BulKathosBlade: 21688; + const BulKathoSword: 21689; + // Cow Kings's Leathers + const CowKingsHelmet: 21723; + const CowKingsArmor: 21724; + const CowKingsBoots: 21725; + // Disciples + const DisciplesAmulet: 21717; + const DisciplesGloves: 21718; + const DisciplesBoots: 21719; + const DisciplesArmor: 21720; + const DisciplesBelt: 21721; + // Griswolds's Legacy + const GriswoldsScepter: 21673; + const GriswoldsShield: 21674; + const GriswoldsArmor: 21675; + const GriswoldsHelmet: 21676; + // Heaven's Brethren + const HeavensMace: 21823; + const HeavensHelmet: 21824; + const HeavensShield: 21825; + const HeavensArmor: 21826; + // Hwanin's + const HwaninsHelmet: 21712; + const HwaninsPolearm: 21713; + const HwaninsArmor: 21714; + const HwaninsBelt: 21821; + // IK + const ImmortalKingsMaul: 21840; + const ImmortalKingsBoots: 21841; + const ImmortalKingsGloves: 21842; + const ImmortalKingsBelt: 21843; + const ImmortalKingsArmor: 21844; + const ImmortalKingsHelmet: 21845; + // M'avina's + const MavinasHelmet: 21702; + const MavinasArmor: 21703; + const MavinasGloves: 21704; + const MavinasBelt: 21705; + const MavinasBow: 21706; + // Natalya's + const NatalyasHelmet: 21668; + const NatalyasClaw: 21669; + const NatalyasArmor: 21670; + const NatalyasBoots: 21671; + // Naj's + const NajsStaff: 21640; + const NajsArmor: 21831; + const NajsHelmet: 21832; + // Orphan's + const OrphansHelmet: 21731; + const OrphansBelt: 21732; + const OrphansGloves: 21733; + const OrphansShield: 21734; + // Sanders's + const SandersGloves: 21876; + const SandersBoots: 21877; + const SandersHelmet: 21878; + const SandersWand: 21879; + // Sazabi's + const SazabisSword: 21708; + const SazabisArmor: 21709; + const SazabisHelmet: 21710; + // Tal + const TalRashasBelt: 21816; + const TalRashasAmulet: 21817; + const TalRashasArmor: 21818; + const TalRashasOrb: 21819; + const TalRashasHelmet: 21820; + // Trang-Ouls + const TrangOulsHelmet: 21661; + const TrangOulsShield: 21662; + const TrangOulsArmor: 21664; + const TrangOulsGloves: 21665; + const TrangOulsBelt: 21666; + + // Uniques + // Quest/Misc + const KeyofTerror: 11146; + const KeyofHate: 11147; + const KeyofDestruction: 11148; + const DiablosHorn: 11149; + const BaalsEye: 11150; + const MephistosBrain: 11151; + const StandardofHeroes: 11152; + const HellfireTorch: 11153; + const Annihilus: 21743; + + // Unique Items + const WitchwildString: 10911; + const TitansRevenge: 21735; + const LycandersAim: 21737; + const ArreatsFace: 21744; + const Homunculus: 21755; + const JalalsMane: 21750; + const HeraldofZakarum: 21758; + const BloodRavensCharge: 21508; + const Gimmershred: 21637; + const MedusasGaze: 21516; + const Rockstopper: 21519; + const CrownofThieves: 21522; + const BlackhornsFace: 21523; + const TheSpiritShroud: 21524; + const SkinoftheFlayedOne: 21525; + const IronPelt: 21526; + const SpiritForge: 21527; + const CrowCaw: 21528; + const DurielsShell: 21529; + const SkulldersIre: 21530; + const Toothrow: 21531; + const AtmasWail: 21532; + const BlackHades: 21533; + const Corpsemourn: 21534; + const QueHegans: 21535; + const QueHegansWisdom: 21535; + const Mosers: 21536; + const MosersBlessedCircle: 21536; + const Stormchaser: 21537; + const TiamatsRubuke: 21538; + const GerkesSanctuary: 21539; + const RadamentsSphere: 21540; + const Gravepalm: 21541; + const Ghoulhide: 21542; + const Hellmouth: 21543; + const Infernostride: 21544; + const Waterwalk: 21545; + const Silkweave: 21546; + const WarTraveler: 21547; + const Razortail: 21548; + const GloomsTrap: 21549; + const Snowclash: 21550; + const ThundergodsVigor: 21551; + const LidlessWall: 21552; + const LanceGuard: 21553; + const Boneflame: 21555; + const SteelPillar: 21556; + const NightwingsVeil: 21557; + const CrownofAges: 21559; + const AndarielsVisage: 21560; + const Dragonscale: 21562; + const SteelCarapace: 21563; + const RainbowFacet: 21565; + const Ravenlore: 21566; + const Boneshade: 21567; + const Flamebellow: 21570; + const DeathsFathom: 21571; + const Wolfhowl: 21572; + const SpiritWard: 21573; + const KirasGuardian: 21574; + const OrmusRobe: 21575; + const GheedsFortune: 21576; + const HalberdsReign: 21579; + const DraculsGrasp: 21583; + const Frostwind: 21584; + const TemplarsMight: 21585; + const EschutasTemper: 21586; // also 21620? + const FirelizardsTalons: 21587; + const SandstormTrek: 21588; + const Marrowwalk: 21589; + const HeavensLight: 21590; + const ArachnidMesh: 21592; + const NosferatusCoil: 21593; + const Verdungos: 21595; + const VerdungosHeartyCord: 21595; + const CarrionWind: 21597; + const GiantSkull: 21598; + const AstreonsIronWard: 21599; + const SaracensChance: 21608; + const HighlordsWrath: 21609; + const Ravenfrost: 21610; + const Dwarfstar: 21611; + const AtmasScarab: 21612; + const Maras: 21613; + const MarasKaleidoscope: 21613; + const CrescentMoonAmulet: 21614; + const TheRisingSun: 21615; + const TheCatsEye: 21616; + const BulKathosWeddingBand: 21617; + const Metalgrid: 21619; + const Stormshield: 21621; + const BlackoakShield: 21622; + const ArkainesValor: 21624; + const TheGladiatorsBane: 21625; + const HarlequinsCrest: 21627; + const GuardianAngel: 21632; + const TheGrandfather: 21643; + const Doombringer: 21644; + const TyraelsMight: 21645; + const Lightsabre: 21646; + const TheCraniumBasher: 21647; + const DeathsWeb: 21650; + const TheAtlantean: 21654; + const CarinShard: 21658; + const Coldkill: 21286; + const ButchersCleaver: 21287; + const Islestrike: 21289; + const GuardianNaga: 21291; + const SpellSteel: 21293; + const SuicideBranch: 21297; + const ArmofKingLeoric: 21299; + const BlackhandKey: 21300; + const DarkClanCrusher: 21301; + const TheFetidSprinkler: 21304; + const HandofBlessedLight: 21305; + const Fleshrender: 21306; + const SureshrillFrost: 21307; + const Moonfall: 21308; + const BaezilsVortex: 21309; + const Earthshaker: 21310; + const TheGavelofPain: 21312; + const Bloodletter: 21313; + const ColdstealEye: 21314; + const Hexfire: 21315; + const BladeofAliBaba: 21316; + const Riftslash: 21317; + const Headstriker: 21318; + const PlagueBearer: 21319; + //const TheAtlantean: 21320; + const CrainteVomir: 21321; + const BingSzWang: 21322; + const TheVileHusk: 21323; + const Cloudcrack: 21324; + const TodesfaelleFlamme: 21325; + const Swordguard: 21326; + const Spineripper: 21327; + const HeartCarver: 21328; + const BlackbogsSharp: 21329; + const Stormspike: 21330; + const TheImpaler: 21331; + const HoneSudan: 21334; + const SpireofHonor: 21335; + const TheMeatScraper: 21336; + const BlackleachBlade: 21337; + const AthenasWrath: 21338; + const PierreTombaleCouant: 21339; + const GrimsBurningDead: 21341; + const Ribcracker: 21342; + const ChromaticIre: 21343; + const Warspear: 21344; + const SkullCollector: 21345; + const Skystrike: 21346; + //const WitchwildString: 21349; + const GoldstrikeArch: 21350; + const PusSpitter: 21352; + const VampireGaze: 21354; + const StringofEars: 21355; + const GoreRider: 21356; + const LavaGout: 21357; + const VenomGrip: 21358; + const Visceratuant: 21359; + //const GuardianAngel: 21360; + const Shaftstop: 21361; + const SkinofVipermagi: 21362; + const Blackhorn: 21363; + const ValkyrieWing: 21364; + const PeasantCrown: 21365; + const DemonMachine: 21366; + const Riphook: 21369; + const Razorswitch: 21370; + const OndalsWisdom: 21375; + const Deathbit: 21379; + const Warshrike: 21380; + const DemonLimb: 21387; + const SteelShade: 21388; + const TombReaver: 21389; + //const DeathsWeb: 21390; + const AngelsSong: 21393; + const TheRedeemer: 21394; + const Bonehew: 21398; + const Steelrend: 21399; + const AriocsNeedle: 21402; + const SoulDrainer: 21407; + const RuneMaster: 21408; + const DeathCleaver: 21409; + const ExecutionersJustice: 21410; + const Leviathan: 21412; + const WispProjector: 21417; + const Lacerator: 21419; + const MangSongsLesson: 21420; + const Viperfork: 21421; + const TheReapersToll: 21427; + const SpiritKeeper: 21428; + const Hellrack: 21429; + const AlmaNegra: 21430; + const DarkforceSpawn: 21431; + const Ghostflame: 21438; + const ShadowKiller: 21439; + const GriffonsEye: 21442; + const Thunderstroke: 21445; + const DemonsArch: 21447; + const DjinnSlayer: 21450; + + // Runewords + const AncientsPledge: 20507; + const Armageddon: 20508; + const Authority: 20509; + const Beast: 20510; + const Beauty: 20511; + const Black: 20512; + const Blood: 20513; + const Bone: 20514; + const Bramble: 20515; + const Brand: 20516; + const BreathoftheDying: 20517; + const BrokenPromise: 20518; + const CalltoArms: 20519; + const ChainsofHonor: 20520; + const Chance: 20521; + const Chaos: 20522; + const CrescentMoon: 20523; + const Darkness: 20524; + const Daylight: 20525; + const Death: 20526; + const Deception: 20527; + const Delerium: 20528; + const Desire: 20529; + const Despair: 20530; + const Destruction: 20531; + const Doom: 20532; + const Dragon: 20533; + const Dread: 20534; + const Dream: 20535; + const Duress: 20536; + const Edge: 20537; + const Elation: 20538; + const Enigma: 20539; + const Enlightenment: 20540; + const Envy: 20541; + const Eternity: 20542; + const Exile: 20543; + const Faith: 20544; + const Famine: 20545; + const Flame: 20546; + const Fortitude: 20547; + const Fortune: 20548; + const Friendship: 20549; + const Fury: 20550; + const Gloom: 20551; + const Grief: 20553; + const HandofJustice: 20554; + const Harmony: 20555; + const HeartoftheOak: 20557; + const HolyThunder: 20560; + const Honor: 20561; + const Revenge: 20562; + const Humility: 20563; + const Hunger: 20564; + const Ice: 20565; + const Infinity: 20566; + const Innocence: 20567; + const Insight: 20568; + const Jealousy: 20569; + const Judgement: 20570; + const KingsGrace: 20571; + const Kingslayer: 20572; + const KnightsVigil: 20573; + const Knowledge: 20574; + const LastWish: 20575; + const Law: 20576; + const Lawbringer: 20577; + const Leaf: 20578; + const Lightning: 20579; + const Lionheart: 20580; + const Lore: 20581; + const Love: 20582; + const Loyalty: 20583; + const Lust: 20584; + const Madness: 20585; + const Malice: 20586; + const Melody: 20587; + const Memory: 20588; + const Mist: 20589; + const Morning: 20590; + const Mystery: 20591; + const Myth: 20592; + const Nadir: 20593; + const NaturesKingdom: 20594; + const Night: 20595; + const Oath: 20596; + const Obedience: 20597; + const Oblivion: 20598; + const Obsession: 20599; + const Passion: 20600; + const Patience: 20601; + const Patter: 20602; + const Peace: 20603; + const VoiceofReason: 20604; + const Penitence: 20605; + const Peril: 20606; + const Pestilence: 20607; + const Phoenix: 20608; + const Piety: 20609; + const PillarofFaith: 20610; + const Plague: 20611; + const Praise: 20612; + const Prayer: 20613; + const Pride: 20614; + const Principle: 20615; + const ProwessinBattle: 20616; + const Prudence: 20617; + const Punishment: 20618; + const Purity: 20619; + const Question: 20620; + const Radiance: 20621; + const Rain: 20622; + const Reason: 20623; + const Red: 20624; + const Rhyme: 20625; + const Rift: 20626; + const Sanctuary: 20627; + const Serendipity: 20628; + const Shadow: 20629; + const ShadowofDoubt: 20630; + const Silence: 20631; + const SirensSong: 20632; + const Smoke: 20633; + const Sorrow: 20634; + const Spirit: 20635; + const Splendor: 20636; + const Starlight: 20637; + const Stealth: 20638; + const Steel: 20639; + const StillWater: 20640; + const Sting: 20641; + const Stone: 20642; + const Storm: 20643; + const Strength: 20644; + const Tempest: 20645; + const Temptation: 20646; + const Terror: 20647; + const Thirst: 20648; + const Thought: 20649; + const Thunder: 20650; + const Time: 20651; + const Tradition: 20652; + const Treachery: 20653; + const Trust: 20654; + const Truth: 20655; + const UnbendingWill: 20656; + const Valor: 20657; + const Vengeance: 20658; + const Venom: 20659; + const Victory: 20660; + const Voice: 20661; + const Void: 20662; + const War: 20663; + const Water: 20664; + const Wealth: 20665; + const Whisper: 20666; + const White: 20667; + const Wind: 20668; + const WingsofHope: 20669; + const Wisdom: 20670; + const Woe: 20671; + const Wonder: 20672; + const Wrath: 20673; + const Youth: 20674; + const Zephyr: 20675; + } + namespace dialog { + const youDoNotHaveEnoughGoldForThat: 3362 + } + + namespace text { + const RepairCost: 3330; + const SellValue: 3331; + const IdentifyCost: 3332; + const ItemCannotBeTradedHere: 3333; + const TradeRepair: 3334; + const Buy: 3335; + const Sell: 3336; + const Heal: 3337; + const Repair: 3338; + const NextPage: 3339; + const PreviousPage: 3340; + const Transmute: 3341; + const YourGold: 3342; + const WhichItemShouldBeImbued: 3343; + const Yes: 3344; + const No: 3345; + const Gold2: 3346; + const Sell2: 3347; + const Buy2: 3358; + const Hire: 3349; + const ToStrength: 3473; + const ToDexterity: 3474; + const Defense: 3481; + const Identify: 3350; + const Repair2: 3351; + const EnhancedDefense: 3520; + const Strength: 4060; + const Dexterity: 4062; + const Vitality: 4066; + const Energy: 4069; + const DoNotMeetLevelReqForThisGame: 5162; + const CdKeyDisabled: 5199; + const OnlyOneInstanceAtATime: 5201; + const CdKeyIntendedForAnotherProduct: 5202; + const InvalidPassword: 5207; + const AccountDoesNotExist: 5208; + const AccountIsCorrupted: 5209; + const AccountMustBeAtLeast: 5217; + const AccountCantBeMoreThan: 5218; + const PasswordMustBeAtLeast: 5219; + const PasswordCantBeMoreThan: 5220; + const LoginError: 5224; + const UsernameMustBeAtLeast: 5231; + const UsernameIncludedIllegalChars: 5232; + const UsernameIncludedDisallowedwords: 5233; + const AccountNameAlreadyExist: 5239; + const UnableToCreateAccount: 5249; + const Disconnected: 5347; + const UnableToIndentifyVersion: 5245; + const EnhancedDamage: 10038; + const LoDKeyDisabled: 10913; + const CdKeyInUseBy: 10914; + const LoDKeyIntendedForAnotherProduct: 10915; + const YourPositionInLineIs: 11026; + const Gateway: 11049; + const Ghostly: 11084; + const Fanatic: 11085; + const Possessed: 11086; + const Berserker: 11087; + const ExpiresIn: 11133; + const CdKeyDisabledFromRealm: 11161; + } + } + + export namespace game { + namespace profiletype { + const SinglePlayer: 1; + const Battlenet: 2; + const OpenBattlenet: 3; + const TcpIpHost: 4; + const TcpIpJoin: 5 + } + + namespace controls { + const Disabled: 4; + } + + namespace gametype { + const Classic: 0; + const Expansion: 1; + } + + // out of game locations + namespace locations { + const PreSplash: 0; + const Lobby: 1; + const WaitingInLine: 2; + const LobbyChat: 3; + const CreateGame: 4; + const JoinGame: 5; + const Ladder: 6; + const ChannelList: 7; + const MainMenu: 8; + const Login: 9; + const LoginError: 10; + const LoginUnableToConnect: 11; + const CharSelect: 12; + const RealmDown: 13; + const Disconnected: 14; + const NewCharSelected: 15; + const CharSelectPleaseWait: 16; + const LobbyLostConnection: 17; + const SplashScreen: 18; + const CdKeyInUse: 19; + const SelectDifficultySP: 20; + const MainMenuConnecting: 21; + const InvalidCdKey: 22; + const CharSelectConnecting: 23; + const ServerDown: 24; + const LobbyPleaseWait: 25; + const GameNameExists: 26; + const GatewaySelect: 27; + const GameDoesNotExist: 28; + const CharacterCreate: 29; + const OkCenteredErrorPopUp: 30; + const TermsOfUse: 31; + const CreateNewAccount: 32; + const PleaseRead: 33; + const RegisterEmail: 34; + const Credits: 35; + const Cinematics: 36; + const CharChangeRealm: 37; + const GameIsFull: 38; + const OtherMultiplayer: 39; + const TcpIp: 40; + const TcpIpEnterIp: 41; + const CharSelectNoChars: 42; + const CharSelectChangeRealm: 43; + const TcpIpUnableToConnect: 44; + } + } + + export namespace colors { + const White: "ÿc0"; + const Red: "ÿc1"; + const NeonGreen: "ÿc2"; + const Blue: "ÿc3"; + const DarkGold: "ÿc4"; + const Gray: "ÿc5"; + const Black: "ÿc6"; + const LightGold: "ÿc7"; + const Orange: "ÿc8"; + const Yellow: "ÿc9"; + const DarkGreen: "ÿconst c:"; + const Purple: "ÿc;"; + const Green: "ÿc<"; + namespace D2Bot { + const Black: 0; + const Blue: 4; + const Green: 5; + const Gold: 6; + const DarkGold: 7; + const Orange: 8; + const Red: 9; + const Gray: 10 + } + } + + export namespace keys { + const Backspace: 8; + const Tab: 9; + const Enter: 13; + const Shift: 16; + const Ctrl: 17; + const Alt: 18; + const PauseBreak: 19; + const CapsLock: 20; + const Escape: 27; + const Spacebar: 32; + const PageUp: 33; + const PageDown: 34; + const End: 35; + const Home: 36; + const LeftArrow: 37; + const UpArrow: 38; + const RightArrow: 39; + const DownArrow: 40; + const Insert: 45; + const Delete: 46; + const Zero: 48; + const One: 49; + const Two: 50; + const Three: 51; + const Four: 52; + const Five: 53; + const Six: 54; + const Seven: 55; + const Eight: 56; + const Nine: 57; + const LeftWindowKey: 91; + const RightWindowKey: 92; + const SelectKey: 93; + const Numpad0: 96; + const Numpad1: 97; + const Numpad2: 98; + const Numpad3: 99; + const Numpad4: 100; + const Numpad5: 101; + const Numpad6: 102; + const Numpad7: 103; + const Numpad8: 104; + const Numpad9: 105; + const NumpadStar: 106; + const NumpadPlus: 107; + const NumpadDash: 109; + const NumpadDecimal: 110; + const NumpadSlash: 111; + const F1: 112; + const F2: 113; + const F3: 114; + const F4: 115; + const F5: 116; + const F6: 117; + const F7: 118; + const F8: 119; + const F9: 120; + const F10: 121; + const F11: 122; + const F12: 123; + const NumLock: 144; + const ScrollLock: 145; + const SemiColon: 186; + const EqualSign: 187; + const Comma: 188; + const Dash: 189; + const Period: 190; + const ForwardSlash: 191; + const GraveAccent: 192; + const OpenBracket: 219; + const BackSlash: 220; + const CloseBracket: 221; + const SingleQuote: 222; + namespace code { + const Backspace: 0x08; + const Tab: 0x09; + const Clear: 0x0C; + const Enter: 0x0D; + const Shift: 0x10; + const Ctrl: 0x11; + const Alt: 0x12; + const PauseBreak: 0x13; + const CapsLock: 0x14; + const Esc: 0x1B; + const Space: 0x20; + const PageUp: 0x21; + const PageDown: 0x22; + const End: 0x23; + const Home: 0x24; + const LeftArrow: 0x25; + const UpArrow: 0x26; + const RightArrow: 0x27; + const DownArrow: 0x28; + const Select: 0x29; + const Print: 0x2A; + const PrintScreen: 0x2C; + const Insert: 0x2D; + const Delete: 0x2E; + } + } + + export namespace controls { + const TextBox: 1; + const Image1: 2; + const Image2: 3; + const LabelBox: 4; + const ScrollBar: 5; + const Button: 6; + const List: 7; + const Timer: 8; + const Smack: 9; + const ProgressBar: 10; + const Popup: 11; + const AccountList: 12 + } + + export namespace packets { + namespace send { + const WalkToLocation: 0x01; + const WalkToEntity: 0x02; + const RunToLocation: 0x03; + const RunToEntity: 0x04; + const LeftSkillOnLocation: 0x05; + const LeftSkillOnEntity: 0x06; + const LeftSkillOnEntityEx: 0x07; + const LeftSkillOnLocationEx: 0x08; + const LeftSkillOnEntityEx2: 0x09; + const LeftSkillOnEntityEx3: 0x0A; + const RightSkillOnLocation: 0x0C; + const RightSkillOnEntity: 0x0D; + const RightSkillOnEntityEx: 0x0E; + const RightSkillOnLocationEx: 0x0F; + const RightSkillOnEntityEx2: 0x10; + const RightSkillOnEntityEx3: 0x11; + const SetInfernoState: 0x12; + const InteractWithEntity: 0x13; + const OverheadMessage: 0x14; + const Chat: 0x15; + const PickupItem: 0x16; + const DropItem: 0x17; + const ItemToBuffer: 0x18; + const PickupBufferItem: 0x19; + const ItemToBody: 0x1A; + const Swap2HandedItem: 0x1B; + const PickupBodyItem: 0x1C; + const SwitchBodyItem: 0x1D; + const Switch1HandWith2Hand: 0x1E; + const SwitchInventoryItem: 0x1F; + const UseItem: 0x20; + const StackItem: 0x21; + const RemoveStackItem: 0x22; + const ItemToBelt: 0x23; + const RemoveBeltItem: 0x24; + const SwitchBeltItem: 0x25; + const UseBeltItem: 0x26; + const IndentifyItem: 0x27; + const InsertSocketItem: 0x28; + const ScrollToMe: 0x29; + const ItemToCube: 0x2A; + const NPCInit: 0x2F; + const NPCCancel: 0x30; + const QuestMessage: 0x31; + const NPCBuy: 0x32; + const NPCSell: 0x33; + const NPCIndentifyItems: 0x34; + const Repair: 0x35; + const HireMerc: 0x36; + const IndentifyGamble: 0x37; + const EntityAction: 0x38; + const AddStat: 0x3A; + const AddSkill: 0x3B; + const SelectSkill: 0x3C; + const ActivateItem: 0x3E; + const CharacterPhrase: 0x3F; + const UpdateQuests: 0x40; + const Resurrect: 0x41; + const StaffInOrifice: 0x44; + const MercInteract: 0x46; + const MercMove: 0x47; + const BusyStateOff: 0x48; + const Waypoint: 0x49; + const RequestEntityUpdate: 0x4B; + const Transmorgify: 0x4C; + const PlayNPCMessage: 0x4D; + const ClickButton: 0x4F; + const DropGold: 0x50; + const BindHotkey: 0x51; + const StaminaOn: 0x53; + const StaminaOff: 0x54; + const QuestCompleted: 0x58; + const MakeEntityMove: 0x59; + const SquelchHostile: 0x5D; + const Party: 0x5E; + const UpdatePlayerPos: 0x5F; + const SwapWeapon: 0x60; + const MercItem: 0x61; + const MercRessurect: 0x62; + const LeaveGame: 0x69; + } + namespace recv { + const GameExit: 0x06; + const MapReveal: 0x07; + const MapHide: 0x08; + const ReassignPlayer: 0x15; + const SetSkill: 0x23; + const Chat: 0x26; + const UniqueEvents: 0x89; + const WeaponSwitch: 0x97; + } + } + } +} +export {}; diff --git a/d2bs/kolbot/tools/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js similarity index 94% rename from d2bs/kolbot/tools/AntiHostile.js rename to d2bs/kolbot/threads/AntiHostile.js index b2f350bc1..d5df60913 100644 --- a/d2bs/kolbot/tools/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -6,14 +6,22 @@ */ js_strict(true); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("common/util.js"); - -includeCommonLibs(); +include("json2.js"); // required? +include("polyfill.js"); // required +include("oog/D2Bot.js"); // required + +// globals needed for core gameplay +include("core/NTItemParser.js"); +include("core/Util.js"); +includeCoreLibs(); + +// system libs +include("systems/automule/AutoMule.js"); +include("systems/gambling/Gambling.js"); +include("systems/crafting/CraftingSystem.js"); +include("systems/torch/TorchSystem.js"); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); function main() { // Variables and functions @@ -180,7 +188,7 @@ function main() { try { Town.goToTown(); } catch (e) { - print(e + " Failed to go to town. Quitting."); + console.error(e + " Failed to go to town. Quitting."); scriptBroadcast("quit"); // quit if failed to go to town } diff --git a/d2bs/kolbot/tools/AntiIdle.js b/d2bs/kolbot/threads/AntiIdle.js similarity index 89% rename from d2bs/kolbot/tools/AntiIdle.js rename to d2bs/kolbot/threads/AntiIdle.js index 9f526a1ec..b3b30635f 100644 --- a/d2bs/kolbot/tools/AntiIdle.js +++ b/d2bs/kolbot/threads/AntiIdle.js @@ -5,8 +5,8 @@ * */ -include("common/Prototypes.js"); -include("common/Misc.js"); +include("core/Prototypes.js"); +include("core/Misc.js"); function main () { console.log("ÿc3Start AntiIdle"); diff --git a/d2bs/kolbot/tools/AreaWatcher.js b/d2bs/kolbot/threads/AreaWatcher.js similarity index 66% rename from d2bs/kolbot/tools/AreaWatcher.js rename to d2bs/kolbot/threads/AreaWatcher.js index 6837da895..a53bf8ecd 100644 --- a/d2bs/kolbot/tools/AreaWatcher.js +++ b/d2bs/kolbot/threads/AreaWatcher.js @@ -5,15 +5,17 @@ * */ include("OOG.js"); -include("common/Prototypes.js"); +include("core/Prototypes.js"); function main() { let _default = getScript("default.dbj"); - print("ÿc3Start AreaWatcher"); + console.log("ÿc3Start AreaWatcher"); while (true) { try { if (me.gameReady && me.ingame && !me.inTown) { + // additonal check for wierd behavior - it shouldn't be possbile to run out of town in less than 30 seconds in game + if (getTickCount() - me.gamestarttime < Time.seconds(30)) continue; !!_default && _default.stop(); D2Bot.printToConsole("Saved from suicide walk!"); !!_default && !_default.running ? quit() : D2Bot.restart(); diff --git a/d2bs/kolbot/tools/AutoBuildThread.js b/d2bs/kolbot/threads/AutoBuildThread.js similarity index 93% rename from d2bs/kolbot/tools/AutoBuildThread.js rename to d2bs/kolbot/threads/AutoBuildThread.js index 3e58ec9ff..820796cc7 100644 --- a/d2bs/kolbot/tools/AutoBuildThread.js +++ b/d2bs/kolbot/threads/AutoBuildThread.js @@ -6,15 +6,27 @@ */ js_strict(true); -!isIncluded("common/AutoSkill.js") && include("common/AutoSkill.js"); -!isIncluded("common/AutoStat.js") && include("common/AutoStat.js"); -!isIncluded("common/Config.js") && include("common/Config.js"); -!isIncluded("common/Cubing.js") && include("common/Cubing.js"); -!isIncluded("common/Prototypes.js") && include("common/Prototypes.js"); -!isIncluded("common/Runewords.js") && include("common/Runewords.js"); -!isIncluded("common/Town.js") && include("common/Town.js"); - -Config.init(); // includes libs/common/AutoBuild.js +include("json2.js"); // required? +include("polyfill.js"); // required +include("oog/D2Bot.js"); // required + +// globals needed for core gameplay +include("core/NTItemParser.js"); +include("core/Util.js"); +include("core/Auto/AutoBuild.js"); +include("core/Auto/AutoSkill.js"); +include("core/Auto/AutoStat.js"); +includeCoreLibs(); + +// system libs +include("systems/automule/AutoMule.js"); +include("systems/gambling/Gambling.js"); +include("systems/crafting/CraftingSystem.js"); +include("systems/torch/TorchSystem.js"); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); + +Config.init(); // includes libs/core/AutoBuild.js const debug = !!Config.AutoBuild.DebugMode; const SPEND_POINTS = true; // For testing, it actually allows skill and stat point spending. diff --git a/d2bs/kolbot/tools/CloneKilla.js b/d2bs/kolbot/threads/CloneKilla.js similarity index 58% rename from d2bs/kolbot/tools/CloneKilla.js rename to d2bs/kolbot/threads/CloneKilla.js index 6f1d5bf69..60a6a6b53 100644 --- a/d2bs/kolbot/tools/CloneKilla.js +++ b/d2bs/kolbot/threads/CloneKilla.js @@ -2,19 +2,20 @@ * @filename CloneKilla.js * @author kolton * @desc Kill Diablo Clone when he walks in game. Uses Fire Eye location. +* @todo +* - handle if fire eye location isn't possible * */ -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("AutoMule.js"); -include("craftingsystem.js"); -include("Gambling.js"); -include("TorchSystem.js"); -include("MuleLogger.js"); -include("common/util.js"); +js_strict(true); +include("critical.js"); -includeCommonLibs(); +// globals needed for core gameplay +includeCoreLibs(); + +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); function main() { D2Bot.init(); @@ -25,7 +26,7 @@ function main() { CraftingSystem.buildLists(); Runewords.init(); Cubing.init(); - include("bots/KillDclone.js"); + include("scripts/KillDclone.js"); if (typeof KillDclone === "function") { try { @@ -36,7 +37,13 @@ function main() { } } - quit(); + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } return true; } diff --git a/d2bs/kolbot/tools/HeartBeat.js b/d2bs/kolbot/threads/HeartBeat.js similarity index 73% rename from d2bs/kolbot/tools/HeartBeat.js rename to d2bs/kolbot/threads/HeartBeat.js index a5f0d22f8..68466317b 100644 --- a/d2bs/kolbot/tools/HeartBeat.js +++ b/d2bs/kolbot/threads/HeartBeat.js @@ -6,12 +6,11 @@ */ function main() { - include("oog.js"); - include("json2.js"); - include("common/misc.js"); - include("common/util.js"); + include("json2.js"); // required? + include("polyfill.js"); // required + include("oog/D2Bot.js"); // required D2Bot.init(); - print("Heartbeat loaded"); + console.log("Heartbeat loaded"); function togglePause() { let script = getScript(); @@ -20,10 +19,10 @@ function main() { do { if (script.name.includes(".dbj")) { if (script.running) { - print("ÿc1Pausing ÿc0" + script.name); + console.log("ÿc1Pausing ÿc0" + script.name); script.pause(); } else { - print("ÿc2Resuming ÿc0" + script.name); + console.log("ÿc2Resuming ÿc0" + script.name); script.resume(); } } diff --git a/d2bs/kolbot/tools/Party.js b/d2bs/kolbot/threads/Party.js similarity index 65% rename from d2bs/kolbot/tools/Party.js rename to d2bs/kolbot/threads/Party.js index 602711757..ca5dd7a84 100644 --- a/d2bs/kolbot/tools/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -1,63 +1,90 @@ /** * @filename Party.js -* @author kolton +* @author kolton, theBGuy * @desc handle party procedure ingame * */ +js_strict(true); -function main() { - include("OOG.js"); - include("json2.js"); - include("common/Config.js"); - include("common/Cubing.js"); - include("common/Runewords.js"); - include("common/misc.js"); - include("common/util.js"); - include("common/Prototypes.js"); - include("common/Town.js"); +include("json2.js"); // required? +include("polyfill.js"); // required +include("oog/D2Bot.js"); // required + +// globals needed for core gameplay +include("core/NTItemParser.js"); +include("core/Util.js"); +includeCoreLibs(); + +// system libs +include("systems/automule/AutoMule.js"); +include("systems/gambling/Gambling.js"); +include("systems/crafting/CraftingSystem.js"); +include("systems/torch/TorchSystem.js"); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); +// party thread specific +include("oog/ShitList.js"); + +function main() { Config.init(); let myPartyId, player, shitList, currScript, scriptList; - let classes = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; let playerLevels = {}; let partyTick = getTickCount(); - addEventListener("gameevent", - function (mode, param1, param2, name1, name2) { - let player; + /** + * Format the event message here to prevent repetitive code + * @param {string[]} arr + * @param {Player | string} player + * @param {string} [killer] + */ + const eventMsg = (arr, player, killer) => { + try { + typeof player === "string" && (player = getParty(player)); + + if (!player || player.name === me.name) return ""; + return (arr + .random() + .format( + ["$name", player.name], + ["$level", player.level], + ["$class", sdk.player.class.nameOf(player.classid)], + ["$killer", killer] + ) + ); + } catch (e1) { + return ""; + } + }; - switch (mode) { - case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." - if (Config.Greetings.length > 0) { - try { - player = getParty(name1); - } catch (e1) { - break; - } + const gameEvent = function (mode, param1, param2, name1, name2) { + let msg = ""; - if (player && player.name !== me.name) { - say(Config.Greetings[rand(0, Config.Greetings.length - 1)].replace("$name", player.name).replace("$level", player.level).replace("$class", classes[player.classid])); - } - } + switch (mode) { + case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." + if (Config.Greetings.length > 0) { + msg = eventMsg(Config.Greetings, name1); + } - break; - case 0x06: // "%Name1 was Slain by %Name2" - if (Config.DeathMessages.length > 0) { - try { - player = getParty(name1); - } catch (e2) { - break; - } + break; + case 0x06: // "%Name1 was Slain by %Name2" + if (Config.DeathMessages.length > 0) { + msg = eventMsg(Config.DeathMessages, name1, name2); + } - if (player && player.name !== me.name) { - say(Config.DeathMessages[rand(0, Config.DeathMessages.length - 1)].replace("$name", player.name).replace("$level", player.level).replace("$class", classes[player.classid]).replace("$killer", name2)); - } - } + break; + } + + if (msg) { + say(msg); + } + }; + + if (Config.Greetings.length > 0 || Config.DeathMessages.length > 0) { + addEventListener("gameevent", gameEvent); + } - break; - } - }); addEventListener("scriptmsg", function (msg) { let obj; @@ -176,8 +203,9 @@ function main() { } if (player.level > playerLevels[player.name]) { - say(Config.Congratulations[rand(0, Config.Congratulations.length - 1)].replace("$name", player.name).replace("$level", player.level).replace("$class", classes[player.classid])); - + let msg = eventMsg(Config.Congratulations, player); + msg && say(msg); + playerLevels[player.name] = player.level; } } diff --git a/d2bs/kolbot/tools/RushThread.js b/d2bs/kolbot/threads/RushThread.js similarity index 97% rename from d2bs/kolbot/tools/RushThread.js rename to d2bs/kolbot/threads/RushThread.js index 0ac714c48..33a92517a 100644 --- a/d2bs/kolbot/tools/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -6,15 +6,22 @@ */ js_strict(true); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("Gambling.js"); -include("AutoMule.js"); -include("CraftingSystem.js"); -include("TorchSystem.js"); -include("common/util.js"); -includeCommonLibs(); +include("json2.js"); // required? +include("polyfill.js"); // required +include("oog/D2Bot.js"); // required + +// globals needed for core gameplay +include("core/NTItemParser.js"); +include("core/Util.js"); +includeCoreLibs(); + +// system libs +include("systems/automule/AutoMule.js"); +include("systems/gambling/Gambling.js"); +include("systems/crafting/CraftingSystem.js"); +include("systems/torch/TorchSystem.js"); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); let Overrides = require("../modules/Override"); @@ -488,6 +495,7 @@ function main () { }; this.diablo = function () { + include("core/Common/Diablo.js"); this.log("starting diablo"); function inviteIn () { @@ -559,6 +567,7 @@ function main () { return false; } + include("core/Common/Ancients.js"); this.log("starting ancients"); Town.doChores(); @@ -616,6 +625,7 @@ function main () { return false; } + include("core/Common/Baal.js"); this.log("starting baal"); if (me.inTown) { diff --git a/d2bs/kolbot/tools/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js similarity index 84% rename from d2bs/kolbot/tools/ToolsThread.js rename to d2bs/kolbot/threads/ToolsThread.js index 09bd37bfc..88d0d8851 100644 --- a/d2bs/kolbot/tools/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -1,22 +1,20 @@ /** * @filename ToolsThread.js -* @author kolton +* @author kolton, theBGuy * @desc several tools to help the player - potion use, chicken, Diablo clone stop, map reveal, quit with player * */ 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("common/util.js"); +// globals needed for core gameplay +includeCoreLibs(); +include("core/Common/Tools.js"); -includeCommonLibs(); +// system libs +includeSystemLibs(); +include("systems/mulelogger/MuleLogger.js"); +include("systems/gameaction/GameAction.js"); let Overrides = require("../modules/Override"); @@ -26,10 +24,12 @@ new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { }).apply(); function main() { + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); + let ironGolem, debugInfo = {area: 0, currScript: "no entry"}; - let quitFlag = false; + let [quitFlag, antiIdle] = [false, false]; let quitListDelayTime; - let antiIdle = false; let idleTick = 0; let canQuit = true; @@ -53,12 +53,12 @@ function main() { // General functions Common.Toolsthread.pauseScripts = [ - "default.dbj", "tools/townchicken.js", "tools/autobuildthread.js", "tools/antihostile.js", - "tools/party.js", "tools/rushthread.js" + "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", + "threads/party.js", "threads/rushthread.js" ]; Common.Toolsthread.stopScripts = [ - "default.dbj", "tools/townchicken.js", "tools/autobuildthread.js", "tools/antihostile.js", - "tools/party.js", "tools/rushthread.js", "libs//modules/guard.js" + "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", + "threads/party.js", "threads/rushthread.js", "libs//modules/guard.js" ]; // Event functions @@ -80,7 +80,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; @@ -174,13 +174,13 @@ function main() { case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." Config.DebugMode && mode === 0 && D2Bot.printToConsole(name1 + " timed out, check their logs"); - if ((typeof Config.QuitList === "string" && Config.QuitList.toLowerCase() === "any") - || (Config.QuitList instanceof Array && Config.QuitList.includes(name1))) { - print(name1 + (mode === 0 ? " timed out" : " left")); + 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(Time.seconds(Config.QuitListDelay[0]), Time.seconds(Config.QuitListDelay[1])); + 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(); } @@ -240,7 +240,7 @@ function main() { me.maxgametime = 0; - if (Config.KillDclone && load("tools/clonekilla.js")) { + if (Config.KillDclone && load("threads/clonekilla.js")) { break; } else { antiIdle = true; @@ -305,7 +305,7 @@ function main() { }; // Cache variables to prevent a bug where d2bs loses the reference to Config object - Config = Misc.copy(Config); + Config = copyObj(Config); let tick = getTickCount(); addEventListener("keyup", this.keyEvent); @@ -319,6 +319,7 @@ function main() { // Packet.changeStat(93, Config.IAS); Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); + !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks // Start while (true) { @@ -330,7 +331,7 @@ function main() { if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting if (!me.inTown) { - D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + Pather.getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -341,7 +342,7 @@ function main() { Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { - D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -355,7 +356,7 @@ function main() { if (ironGolem) { // ironGolem.hpmax is bugged with BO if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { - D2Bot.printToConsole("Irom Golem Chicken in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -370,7 +371,7 @@ function main() { if (mercHP > 0 && merc.mode !== sdk.monsters.mode.Dead) { if (mercHP < Config.MercChicken) { - D2Bot.printToConsole("Merc Chicken in " + Pather.getAreaName(me.area), sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); Common.Toolsthread.exit(true); break; @@ -410,15 +411,19 @@ function main() { quitFlag = true; } - if (quitFlag && canQuit && (typeof quitListDelayTime === "undefined" || getTickCount() >= quitListDelayTime)) { + if (quitFlag && canQuit) { + if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { + me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); + continue; + } Common.Toolsthread.checkPing(false); // In case of quitlist triggering first Common.Toolsthread.exit(); break; } - 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/d2bs/kolbot/tools/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js similarity index 82% rename from d2bs/kolbot/tools/TownChicken.js rename to d2bs/kolbot/threads/TownChicken.js index e7b73b41f..f7cce54c5 100644 --- a/d2bs/kolbot/tools/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -5,21 +5,19 @@ * */ js_strict(true); +include("critical.js"); -include("json2.js"); -include("NTItemParser.dbl"); -include("OOG.js"); -include("Gambling.js"); -include("CraftingSystem.js"); -include("common/util.js"); +// add core library +includeCoreLibs(); -includeCommonLibs(); +// handle systems +includeSystemLibs(); function main() { let townCheck = false; this.togglePause = function () { - let scripts = ["default.dbj", "tools/antihostile.js", "tools/rushthread.js", "tools/CloneKilla.js"]; + let scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; for (let i = 0; i < scripts.length; i += 1) { let script = getScript(scripts[i]); @@ -31,7 +29,7 @@ function main() { } else { if (scripts[i] === "default.dbj") { // resume only if clonekilla isn't running - if (!getScript("tools/clonekilla.js")) { + if (!getScript("threads/clonekilla.js")) { console.log("ÿc2Resuming."); script.resume(); } @@ -68,8 +66,7 @@ function main() { // START // test for getUnit bug - let test = Game.getMonster(); - test === null && console.warn("getUnit is bugged"); + Game.getMonster() === null && console.warn("getUnit is bugged"); while (true) { if (!me.inTown && (townCheck @@ -77,7 +74,7 @@ function main() { // We would then be able to remove all game interaction checks until we get a townCheck msg || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area - if (!Town.canTpToTown()) { + if (!me.canTpToTown()) { townCheck = false; continue; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..92390d9be --- /dev/null +++ b/package-lock.json @@ -0,0 +1,37 @@ +{ + "name": "trunk", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "trunk", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "typescript": "^4.9.3" + } + }, + "node_modules/typescript": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "typescript": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..353e38901 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "trunk", + "version": "1.0.0", + "description": "1. D2BS, D2Bot and kolbot # are educational tools with an open source developer community. These tools are meant to be used offline or on private servers that explicitly allow them. These tools are not meant to be abused on battle.net (a Blizzard Entertainment entity).", + "main": "", + "typings": "./d2bs/kolbot/**/global.d.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "typescript": "^4.9.3" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..98c065da9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,43 @@ +{ + "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, + // "declaration": true, + // "emitDeclarationOnly": true, + "baseUrl": "./d2bs/kolbot/", + "rootDir": "./d2bs/kolbot/", + // "outDir": "./d2bs/kolbot/sdk/types/", + "outFile": "./d2bs/kolbot/data/out.js", + "typeRoots": [ + "./d2bs/kolbot/sdk/global.d.ts", + "./d2bs/kolbot/sdk/types/", + "./d2bs/kolbot/libs/SoloPlay/index.d.ts", + ], + }, + "include": [ + "./d2bs/kolbot/*.dbj", + "./d2bs/kolbot/**/*.js", + "**/*.dbl", + ], + "exclude": [ + "node_modules", + "**/node_modules/*", + "**/data/", + ], +} From 75369b7ea4c9a1e2f535d4cf9d1c3ed0e18ba086 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 08:53:10 -0500 Subject: [PATCH 002/758] Update D2BotFollow.dbj - remove reference to OOGGuard --- d2bs/kolbot/D2BotFollow.dbj | 2 -- 1 file changed, 2 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 62a9a2233..70ae54505 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -387,8 +387,6 @@ function main () { D2Bot.init(); load("threads/heartbeat.js"); - require("./modules/OOGGuard"); - while (!Object.keys(Starter.gameInfo).length) { D2Bot.requestGameInfo(); delay(500); From 4207618e29819ae6acf9082ed95506f0953eb45f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 13:25:14 -0500 Subject: [PATCH 003/758] Update MuleLogger.js - Fix missed reference to moved function --- d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 3ed1a17d6..a7c880369 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -149,7 +149,7 @@ const MuleLogger = { }); for (let i = 0; i < items.length; i += 1) { - if ((this.LogEquipped || items[i].isInStorage) && (items[i].quality > sdk.items.quality.Normal || !Misc.skipItem(items[i].classid))) { + if ((this.LogEquipped || items[i].isInStorage) && (items[i].quality > sdk.items.quality.Normal || !Item.skipItem(items[i].classid))) { let parsedItem = this.logItem(items[i], logIlvl); // Log names to saved image From 8f6ce746d55018b9e0f15f3438690f3d5d82eca6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 23:57:25 -0500 Subject: [PATCH 004/758] Faster detection of Tomb Vipers - Improve safety of Nith/Pindleskin scripts when set to quit on tomb vipers by greatly improving the detection speed - Add new Error class ScriptError, used to end our script intentionally - Add Pather.moveToPresetObject, cleaner use of moveToPreset with ability to pass in additional settings - Add Pather.moveToPresetMonster, cleaner use of moveToPreset with ability to pass in additional settings --- d2bs/kolbot/libs/core/Loader.js | 8 +++- d2bs/kolbot/libs/core/Pather.js | 58 ++++++++++++++++++++++++++ d2bs/kolbot/libs/scripts/Nihlathak.js | 14 +++---- d2bs/kolbot/libs/scripts/Pindleskin.js | 14 +++---- d2bs/kolbot/sdk/globals.d.ts | 3 ++ 5 files changed, 81 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 1101723ee..bea96e6e7 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -149,7 +149,9 @@ const Loader = { } } } catch (error) { - Misc.errorReport(error, script); + if (!(error instanceof ScriptError)) { + Misc.errorReport(error, script); + } } finally { // Dont run for last script as that will clear everything anyway if (this.scriptIndex < this.scriptList.length) { @@ -222,7 +224,9 @@ const Loader = { } } } catch (error) { - Misc.errorReport(error, script); + if (!(error instanceof ScriptError)) { + Misc.errorReport(error, script); + } failed = true; } finally { // Dont run for last script as that will clear everything anyway diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 8b7c854f2..6774f52aa 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -827,6 +827,64 @@ const Pather = { * moveTo/NearPresetObject * moveTo/NearPresetTile */ + + /** + * + * @param {number} area + * @param {number} unitId + * @param {pathSettings} givenSettings + */ + moveToPresetObject: function (area, unitId, givenSettings = {}) { + if (area === undefined || unitId === undefined) { + throw new Error("moveToPreset: Invalid parameters."); + } + + let offX = givenSettings.hasOwnProperty("offX") ? givenSettings.offX : 0; + let offY = givenSettings.hasOwnProperty("offY") ? givenSettings.offY : 0; + + me.area !== area && Pather.journeyTo(area); + let presetUnit = Game.getPresetObject(area, unitId); + + if (!presetUnit) { + throw new Error("moveToPresetObject: Couldn't find preset unit - id: " + unitId + " in area: " + getAreaName(area)); + } + + let realCoords = presetUnit.realCoords(); + + delay(40); + Misc.poll(() => me.gameReady, 500, 100); + + return this.moveToEx(realCoords.x + offX, realCoords.y + offY, givenSettings); + }, + + /** + * + * @param {number} area + * @param {number} unitId + * @param {pathSettings} givenSettings + */ + moveToPresetMonster: function () { + if (area === undefined || unitId === undefined) { + throw new Error("moveToPreset: Invalid parameters."); + } + + let offX = givenSettings.hasOwnProperty("offX") ? givenSettings.offX : 0; + let offY = givenSettings.hasOwnProperty("offY") ? givenSettings.offY : 0; + + me.area !== area && Pather.journeyTo(area); + let presetUnit = Game.getPresetMonster(area, unitId); + + if (!presetUnit) { + throw new Error("moveToPresetMonster: Couldn't find preset unit - id: " + unitId + " in area: " + getAreaName(area)); + } + + let realCoords = presetUnit.realCoords(); + + delay(40); + Misc.poll(() => me.gameReady, 500, 100); + + return this.moveToEx(realCoords.x + offX, realCoords.y + offY, givenSettings); + }, /** * @param {number} targetArea - area id or array of area ids to move to diff --git a/d2bs/kolbot/libs/scripts/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js index 4fccab67a..5618b3e6c 100644 --- a/d2bs/kolbot/libs/scripts/Nihlathak.js +++ b/d2bs/kolbot/libs/scripts/Nihlathak.js @@ -19,13 +19,13 @@ function Nihlathak() { if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) throw new Error("Failed to go to Nihlathak"); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform, 0, 0, false, true); - - if (Config.Nihlathak.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { - print("Tomb Vipers found."); - - return true; - } + // faster detection of TombVipers + Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { callback: () => { + if (Config.Nihlathak.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); + throw new ScriptError("Tomb Vipers found."); + } + }}); Attack.kill(sdk.monsters.Nihlathak); Pickit.pickItems(); diff --git a/d2bs/kolbot/libs/scripts/Pindleskin.js b/d2bs/kolbot/libs/scripts/Pindleskin.js index e1f1e3c48..df6929e00 100644 --- a/d2bs/kolbot/libs/scripts/Pindleskin.js +++ b/d2bs/kolbot/libs/scripts/Pindleskin.js @@ -32,13 +32,13 @@ function Pindleskin() { if (Config.Pindleskin.KillNihlathak) { if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.HallsofVaught], true)) throw new Error("Failed to move to Halls of Vaught"); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform, 10, 10); - - if (Config.Pindleskin.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { - console.log("Tomb Vipers found."); - - return true; - } + // faster detection of TombVipers + Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { offX: 10, offY: 10, callback: () => { + if (Config.Pindleskin.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); + throw new ScriptError("Tomb Vipers found."); + } + }}); Config.Pindleskin.ClearVipers && Attack.clearList(Attack.getMob(sdk.monsters.TombViper2, 0, 20)); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index d07cc09e7..858b93c01 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -81,6 +81,9 @@ declare global { objtype: number; } + class ScriptError extends Error { + } + type actType = { initialized: boolean, spot: { [data: string]: [number, number] } }; type potType = 'hp' | 'mp' | 'rv'; From 4ce646d96ece94f359a7d2102698dc793f3b3412 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 4 Feb 2023 23:59:05 -0500 Subject: [PATCH 005/758] Update Util.js - Forgot to include new ScriptError class in last commit --- d2bs/kolbot/libs/core/Util.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 844f22847..0c9cec79d 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -22,6 +22,15 @@ }); } }(this, function() { + function ScriptError(message) { + this.name = "ScriptError"; + this.message = message || ""; + this.stack = (new Error()).stack; + } + + ScriptError.prototype = Object.create(Error.prototype); + ScriptError.prototype.constructor = ScriptError; + /** * get all running threads and return them as an array * @returns {Script[]} @@ -605,5 +614,6 @@ Game: Game, Sort: Sort, Messaging: Messaging, + ScriptError: ScriptError, }; })); From 996a2c389401f7d5e865d2c1b4186e43e02a33c7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 5 Feb 2023 09:55:17 -0500 Subject: [PATCH 006/758] Add Config.DebugMode options - Sometimes its hard to track down where a problem is so we need debugging info logged. It can be verbose though so only do that when intentionally set to debug a certain area - Add Guard module to print running stack --- d2bs/kolbot/default.dbj | 4 +- d2bs/kolbot/libs/core/Attack.js | 2 +- d2bs/kolbot/libs/core/Config.js | 8 ++- d2bs/kolbot/libs/core/Pather.js | 22 ++++---- d2bs/kolbot/libs/core/Precast.js | 2 +- d2bs/kolbot/libs/core/Town.js | 18 +++---- d2bs/kolbot/libs/modules/Guard.js | 85 +++++++++++++++++++++++++++++++ 7 files changed, 116 insertions(+), 25 deletions(-) create mode 100644 d2bs/kolbot/libs/modules/Guard.js diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 0dd85b67e..171b66f2f 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -138,7 +138,7 @@ function main () { 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.Stack && FileTools.exists("libs/modules/Guard.js")) { require("libs/modules/Guard"); } @@ -167,7 +167,7 @@ function main () { Pickit.pickItems(); me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); - if (Config.DebugMode) { + if (Config.DebugMode.Memory) { delay(2000); getThreads() .sort((a, b) => b.memory - a.memory) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 5b27b292c..656697232 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1686,7 +1686,7 @@ const Attack = { return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); } })()) { - if (Config.DebugMode && force) { + if (Config.DebugMode.Path && force) { console.debug("Sucessfully got into position. orginal Loc: " + orgX + "/" + orgY + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance); } return true; diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 72e2e15cc..d5c3b3361 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -108,7 +108,13 @@ let Config = { // dev Loaded: false, - DebugMode: false, + DebugMode: { + Path: false, + Stack: false, + Memory: false, + Skill: false, + Town: false, + }, // Time StartDelay: 0, diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 6774f52aa..04ceefd6e 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -907,9 +907,9 @@ const Pather = { console.info(null, "ÿc0Moving from: " + getAreaName(me.area) + " to " + getAreaName(areas[i])); - Config.DebugMode && console.time("getArea"); + Config.DebugMode.Path && console.time("getArea"); let area = Misc.poll(() => getArea(me.area)); - Config.DebugMode && console.timeEnd("getArea"); + Config.DebugMode.Path && console.timeEnd("getArea"); if (!area) throw new Error("moveToExit: error in getArea()"); @@ -919,12 +919,12 @@ const Pather = { let checkExits = []; if (!exits.length) return false; - Config.DebugMode && console.log("Took: " + (getTickCount() - t2) + " to assign vars"); + Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t2) + " to assign vars"); let t3 = getTickCount(); for (let j = 0; j < exits.length; j += 1) { if (!exits[j].hasOwnProperty("target") || exits[j].target !== currTarget) continue; - Config.DebugMode && console.debug(exits[j]); + Config.DebugMode.Path && console.debug(exits[j]); let currCheckExit = { x: exits[j].x, y: exits[j].y, @@ -935,10 +935,10 @@ const Pather = { currCheckExit.target === currTarget && checkExits.push(currCheckExit); } - Config.DebugMode && console.log("Took: " + (getTickCount() - t3) + " to find all exits"); + Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t3) + " to find all exits"); if (checkExits.length > 0) { - Config.DebugMode && console.debug(checkExits); + Config.DebugMode.Path && console.debug(checkExits); let t4 = getTickCount(); // if there are multiple exits to the same location find the closest one let currExit = checkExits.length > 1 @@ -957,10 +957,10 @@ const Pather = { //checkExits.sort((a, b) => getDistance(me.x, me.y, a.x, a.y) - getDistance(me.x, me.y, b.x, b.y)).first() })() : checkExits[0]; - Config.DebugMode && console.log("Took: " + (getTickCount() - t4) + " to pick exit", currExit); + Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t4) + " to pick exit", currExit); let t5 = getTickCount(); let dest = this.getNearestWalkable(currExit.x, currExit.y, 5, 1); - Config.DebugMode && console.log("Took: " + (getTickCount() - t5) + " to find nearest walkable"); + Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t5) + " to find nearest walkable"); if (!dest) return false; @@ -1056,16 +1056,16 @@ const Pather = { } while (room.getNext()); room = getRoom(area, x, y); - !!Config.DebugMode && console.log(room); + !!Config.DebugMode.Path && console.log(room); if (room) { CollMap.addRoom(room); - !!Config.DebugMode && console.log("ÿc7End ÿc8(getNearestOld) ÿc0 - ÿc7Duration: ÿc0" + (getTickCount() - startTick)); + !!Config.DebugMode.Path && console.log("ÿc7End ÿc8(getNearestOld) ÿc0 - ÿc7Duration: ÿc0" + (getTickCount() - startTick)); return this.getNearestWalkable(x, y, 20, 4); } - !!Config.DebugMode && console.log("ÿc7End ÿc8(getNearestOld) ÿc0 - ÿc7Duration: ÿc0" + (getTickCount() - startTick)); + !!Config.DebugMode.Path && console.log("ÿc7End ÿc8(getNearestOld) ÿc0 - ÿc7Duration: ÿc0" + (getTickCount() - startTick)); return [x, y]; }, diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 4d8fa31ac..ac239b973 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -216,7 +216,7 @@ const Precast = new function () { if ([sdk.skills.Shout, sdk.skills.BattleOrders, sdk.skills.BattleCommand].includes(skillId)) return this.warCries(skillId, x, y); if (Config.PacketCasting > 1 || usePacket) { - Config.DebugMode && console.debug("Packet casting: " + skillId); + Config.DebugMode.Skill && console.debug("Packet casting: " + skillId); switch (typeof x) { case "number": diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 58c5023fe..54dd455ea 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -68,17 +68,17 @@ const Town = { try { if (!!this.unit && getTickCount() - this.tick < Time.seconds(15) && this.unit.name.toLowerCase() !== "an evil force" && this.unit.area === me.area) { - Config.DebugMode && console.debug("used stored value"); + Config.DebugMode.Town && console.debug("used stored value"); return this.unit; } else { this.reset(); - Config.DebugMode && console.debug("getting new npc"); + Config.DebugMode.Town && console.debug("getting new npc"); return getInteractedNPC(); } } catch (e) { - Config.DebugMode && console.error(e); + Config.DebugMode.Town && console.error(e); this.reset(); - Config.DebugMode && console.debug("getting new npc"); + Config.DebugMode.Town && console.debug("getting new npc"); return getInteractedNPC(); } }, @@ -337,7 +337,7 @@ const Town = { 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 && console.debug("Going to Act " + goTo + " to see if it fixes getUnit bug"); + Config.DebugMode.Town && console.debug("Going to Act " + goTo + " to see if it fixes getUnit bug"); Town.goToTown(goTo); } @@ -348,7 +348,7 @@ const Town = { this.lastInteractedNPC.set(npc); if (task === "Heal") { - Config.DebugMode && console.debug("Checking if we are frozen"); + 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); @@ -1907,7 +1907,7 @@ const Town = { .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); - Config.DebugMode && potsInInventory.length > 0 && console.debug("clearInventory: start pots clean-up"); + Config.DebugMode.Town && potsInInventory.length > 0 && console.debug("clearInventory: start pots clean-up"); // Start interating over all the pots we have in our inventory potsInInventory.forEach(function (p) { let moved = false; @@ -1932,7 +1932,7 @@ const Town = { }); // Cleanup remaining potions - Config.DebugMode && console.debug("clearInventory: start clean-up remaining pots"); + Config.DebugMode.Town && console.debug("clearInventory: start clean-up remaining pots"); let sellOrDrop = []; potsInInventory = me.getItemsEx() .filter((p) => p.isInInventory && [ @@ -1984,7 +1984,7 @@ const Town = { } // Any leftover items from a failed ID (crashed game, disconnect etc.) - Config.DebugMode && console.debug("clearInventory: start invo clean-up"); + Config.DebugMode.Town && console.debug("clearInventory: start invo clean-up"); let ignoreTypes = [ sdk.items.type.Book, sdk.items.type.Key, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion ]; diff --git a/d2bs/kolbot/libs/modules/Guard.js b/d2bs/kolbot/libs/modules/Guard.js new file mode 100644 index 000000000..54bfca515 --- /dev/null +++ b/d2bs/kolbot/libs/modules/Guard.js @@ -0,0 +1,85 @@ +(function (module, require, thread) { + "use strict"; + const Messaging = require("./Messaging"); + const Worker = require("./Worker"); + const sdk = require("./sdk"); + + switch (thread) { + case "thread": { + Worker.runInBackground.stackTrace = (new function () { + let self = this; + let stack; + + let myStack = ""; + + // 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 = 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 = 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); + } + + 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({Guard: {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 8af2713a75d149a70921e05aa17de0ef13b55abd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 5 Feb 2023 12:45:14 -0500 Subject: [PATCH 007/758] Fix low default MaxGameTime in D2BotMule - I had set it low for testing, didn't mean to upload it - Define MaxGameTime in global.d.ts --- d2bs/kolbot/D2BotMule.dbj | 2 +- d2bs/kolbot/sdk/globals.d.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 60df37c0f..1d070b8bb 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -15,7 +15,7 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // D2BotMule specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.MinGameTime = 30; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby -Starter.Config.MaxGameTime = 10; // Maximum game length in minutes, only for continuous muling +Starter.Config.MaxGameTime = 60; // Maximum game length in minutes, only for continuous muling Starter.Config.CreateGameDelay = 5; // Seconds to wait before creating a new game Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/banned key or after realm down Starter.Config.ExitToMenu = false; // Set to true to wait out restriction in main menu or false to wait in lobby. diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 858b93c01..e031f61c1 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -858,6 +858,7 @@ declare global { interface StarterConfig { MinGameTime: number, + MaxGameTime?: number, PingQuitDelay: number, CreateGameDelay: number, ResetCount: number From 73cd83d2ee21b72df5665da0132217f63f24bfa2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 5 Feb 2023 15:19:48 -0500 Subject: [PATCH 008/758] Update AntiHostile.js - Add type checking for antihostile scriptEvent --- d2bs/kolbot/threads/AntiHostile.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/threads/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js index d5df60913..b7877dc6f 100644 --- a/d2bs/kolbot/threads/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -5,21 +5,13 @@ * */ js_strict(true); - -include("json2.js"); // required? -include("polyfill.js"); // required -include("oog/D2Bot.js"); // required +include("critical.js"); // required // globals needed for core gameplay -include("core/NTItemParser.js"); -include("core/Util.js"); includeCoreLibs(); // system libs -include("systems/automule/AutoMule.js"); -include("systems/gambling/Gambling.js"); -include("systems/crafting/CraftingSystem.js"); -include("systems/torch/TorchSystem.js"); +includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); @@ -31,6 +23,8 @@ function main() { // AntiHostile gets game event info from ToolsThread this.scriptEvent = function (msg) { + if (!msg || typeof msg !== "string") return; + switch (msg.split(" ")[0]) { case "remove": // Remove a hostile player that left the game if (hostiles.indexOf(msg.split(" ")[1]) > -1) { From 7ae56f25c2363c2a58266a626865ca3f852b4f08 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 5 Feb 2023 15:54:09 -0500 Subject: [PATCH 009/758] Update Pather.js - Fix missing parameters in moveToPresetMonster - more JSDOC comments --- d2bs/kolbot/libs/core/Pather.js | 178 +++++++++++++++++++------------- 1 file changed, 107 insertions(+), 71 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 04ceefd6e..9fa4747df 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -8,9 +8,15 @@ // TODO: this needs to be re-worked // Perform certain actions after moving to each node const NodeAction = { + /** + * @type {number[]} + */ shrinesToIgnore: [], - // Run all the functions within NodeAction (except for itself) + /** + * Run all the functions within NodeAction (except for itself) + * @param {clearSettings} arg + */ go: function (arg) { for (let i in this) { if (this.hasOwnProperty(i) && typeof this[i] === "function" && i !== "go") { @@ -19,7 +25,11 @@ const NodeAction = { } }, - // Kill monsters while pathing + /** + * Kill monsters while pathing + * @param {clearSettings} arg + * @returns {void} + */ killMonsters: function (arg = {}) { const settings = Object.assign({}, { clearPath: false, @@ -55,23 +65,35 @@ const NodeAction = { } }, - // Open chests while pathing + /** + * Open chests while pathing + */ popChests: function () { // fastPick check? should only open chests if surrounding monsters have been cleared or if fastPick is active // note: clear of surrounding monsters of the spectype we are set to clear Config.OpenChests.Enabled && Misc.openChests(Config.OpenChests.Range); }, - // Scan shrines while pathing + /** + * Scan shrines while pathing + */ getShrines: function () { Config.ScanShrines.length > 0 && Misc.scanShrines(null, this.shrinesToIgnore); } }; const PathDebug = { + /** + * @type {Line[]} + */ hooks: [], enableHooks: false, + /** + * Draw our path on the screen + * @param {PathNode[]} path + * @returns {void} + */ drawPath: function (path) { if (!this.enableHooks) return; @@ -92,6 +114,13 @@ const PathDebug = { PathDebug.hooks = []; }, + /** + * Check if a set of coords are a set path + * @param {PathNode[]} path + * @param {number} x + * @param {number} y + * @returns {boolean} + */ coordsInPath: function (path, x, y) { for (let i = 0; i < path.length; i += 1) { if (getDistance(x, y, path[i].x, path[i].y) < 5) { @@ -137,11 +166,11 @@ const Pather = { init: function () { if (!this.initialized) { - me.classic && (this.nonTownWpAreas = this.nonTownWpAreas.filter((wp) => wp < sdk.areas.Harrogath)); + me.classic && (Pather.nonTownWpAreas = this.nonTownWpAreas.filter((wp) => wp < sdk.areas.Harrogath)); if (!Config.WaypointMenu) { !getWaypoint(1) && this.getWP(me.area); me.cancelUIFlags(); - this.initialized = true; + Pather.initialized = true; } } }, @@ -156,6 +185,18 @@ const Pather = { return !me.inTown && !Config.NoTele && !me.shapeshifted && this.canTeleport() && numberOfTeleport > 2; }, + /** + * @typedef {object} spotOnDistanceSettings + * @property {number} [area] + * @property {number} [reductionType] + * @property {number} [coll] + * @property {boolean} [returnSpotOnError] + * + * @param {PathNode} spot + * @param {number} distance + * @param {spotOnDistanceSettings} givenSettings + * @returns {PathNode} + */ spotOnDistance: function (spot, distance, givenSettings = {}) { const settings = Object.assign({}, { area: me.area, @@ -189,7 +230,9 @@ const Pather = { * @property {boolean} [pop] * @property {boolean} [returnSpotOnError] * @property {Function} [callback] - * @property {object} [clearSettings] + * @property {clearSettings} [clearSettings] + * + * @typedef {object} clearSettings * @property {boolean} [clearSettings.clearPath] * @property {number} [clearSettings.range] * @property {number} [clearSettings.specType] @@ -587,11 +630,12 @@ const Pather = { return (!me.dead && getDistance(me.x, me.y, x, y) <= minDist); }, - /* - Pather.openDoors(x, y); - x - the x coord of the node close to the door - y - the y coord of the node close to the door - */ + /** + * If there is a door in our path, open it so we can continue moving + * @param {number} x - the x coord of the node close to the door + * @param {number} y - the y coord of the node close to the door + * @returns {boolean} true if we opened any doors that were in our way + */ openDoors: function (x, y) { if (me.inTown && me.act !== 5) return false; @@ -663,11 +707,12 @@ const Pather = { return false; }, - /* - Pather.kickBarrels(x, y); - x - the x coord of the node close to the barrel - y - the y coord of the node close to the barrel - */ + /** + * Small and annoying things like barrels can block our path, open them if they are near us + * @param {number} x - the x coord of the node close to the barrel + * @param {number} y - the y coord of the node close to the barrel + * @returns {boolean} true if we kicked any barrels that were in our way + */ kickBarrels: function (x, y) { if (me.inTown) return false; @@ -823,8 +868,6 @@ const Pather = { /** * @todo - * moveTo/NearPresetMonster - * moveTo/NearPresetObject * moveTo/NearPresetTile */ @@ -863,7 +906,7 @@ const Pather = { * @param {number} unitId * @param {pathSettings} givenSettings */ - moveToPresetMonster: function () { + moveToPresetMonster: function (area, unitId, givenSettings = {}) { if (area === undefined || unitId === undefined) { throw new Error("moveToPreset: Invalid parameters."); } @@ -1010,6 +1053,11 @@ const Pather = { return (use && finalDest ? me.area === finalDest : true); }, + /** + * @param {number} area + * @param {number} exit + * @returns {number} + */ getDistanceToExit: function (area, exit) { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); @@ -1022,6 +1070,11 @@ const Pather = { return loc ? [loc.x, loc.y].distance : Infinity; }, + /** + * @param {number} area + * @param {number} exit + * @returns {PathNode | false} + */ getExitCoords: function (area, exit) { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); @@ -1034,10 +1087,11 @@ const Pather = { return loc ? {x: loc.x, y: loc.y} : false; }, - /* - Pather.getNearestRoom(area); - area - the id of area to search for the room nearest to the player character - */ + + /** + * @param {number} area - the id of area to search for the room nearest to the player character + * @returns {[number, number] | false} + */ getNearestRoom: function (area) { let startTick = getTickCount(); let x, y, minDist = 10000; @@ -1069,10 +1123,10 @@ const Pather = { return [x, y]; }, - /* - Pather.openExit(targetArea); - targetArea - area id of where the unit leads to - */ + /** + * @param {number} targetArea - area id of where the unit leads to + * @returns {boolean} + */ openExit: function (targetArea) { switch (true) { case targetArea === sdk.areas.AncientTunnels: @@ -1096,11 +1150,11 @@ const Pather = { return false; }, - /* - Pather.openUnit(id); - type - type of the unit to open - id - id of the unit to open - */ + /** + * @param {UnitType} type - type of the unit to open + * @param {number} id - id of the unit to open + * @returns {boolean} + */ openUnit: function (type, id) { let unit = Misc.poll(() => getUnit(type, id), 1000, 200); if (!unit) throw new Error("openUnit: Unit not found. ID: " + unit); @@ -1125,13 +1179,13 @@ const Pather = { return false; }, - /* - Pather.useUnit(type, id, targetArea); - type - type of the unit to use - id - id of the unit to use - targetArea - area id of where the unit leads to - */ - // should use an object as param, or be changed to able to take an already found unit as a param + /** + * @param {UnitType} type - type of the unit to use + * @param {number} id - id of the unit to use + * @param {number} targetArea - area id of where the unit leads to + * @returns {boolean} + * @todo should use an object as param, or be changed to able to take an already found unit as a param + */ useUnit: function (type, id, targetArea) { let unit = Misc.poll(() => getUnit(type, id), 2000, 200); let preArea = me.area; @@ -1268,10 +1322,10 @@ const Pather = { return targetArea ? me.area === targetArea : me.area !== preArea; }, - /* - Pather.broadcastIntent(targetArea); - targetArea - area id - */ + /** + * Meant for use as a MfLeader to let MfHelpers know where to go next + * @param {number} targetArea - area id + */ broadcastIntent: function broadcastIntent(targetArea) { if (Config.MFLeader) { let targetAct = sdk.areas.actOf(targetArea); @@ -1279,11 +1333,11 @@ const Pather = { } }, - /* - Pather.moveTo(targetArea, check); - targetArea - id of the area to enter - check - force the waypoint menu - */ + /** + * @param {number} targetArea - id of the area to enter + * @param {boolean} check - force the waypoint menu + * @returns {boolean} + */ useWaypoint: function useWaypoint(targetArea, check = false) { switch (targetArea) { case undefined: @@ -1419,15 +1473,6 @@ const Pather = { delay((1500 + (pingDelay * i))); console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - // if (!me.inTown && Config.ForcePrecast && !(new Error().stack.match("precast.js"))) { - // let 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); - // Precast.doPrecast(false, true); - // } - // } return true; } @@ -1458,25 +1503,16 @@ const Pather = { // delay to allow act to init - helps with crashes delay(500); console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - // if (!me.inTown && Config.ForcePrecast && !(new Error().stack.match("precast.js"))) { - // let 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); - // Precast.doPrecast(false, true); - // } - // } return true; } throw new Error("useWaypoint: Failed to use waypoint"); }, - /* - Pather.makePortal(use); - use - use the portal that was made - */ + /** + * @param {boolean} use - use the portal that was made + * @returns {Unit | boolean} + */ makePortal: function (use = false) { if (me.inTown) return true; From ac41bf9b4e9183d4753ffeb828db9c0f1bd0b0fa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 02:19:19 -0500 Subject: [PATCH 010/758] Update Prototypes.js - small fix for unit prototype use, add check and delay in case we are in action sequence (leaping/whirling/ect) --- d2bs/kolbot/libs/core/Prototypes.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 73cc77853..9fb6afaf9 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -858,6 +858,12 @@ Unit.prototype.use = function () { let iType = this.itemType; let checkQuantity = false; + if (me.mode === sdk.player.mode.SkillActionSequence) { + while (me.mode === sdk.player.mode.SkillActionSequence) { + delay (25); + } + } + switch (this.location) { case sdk.storage.Stash: case sdk.storage.Inventory: From b05e368a0a6a240af523ff4e47bd2d214a843ec0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 10:43:36 -0500 Subject: [PATCH 011/758] Fix small script bugs - Increase the initial delay and time duration of checking for scrolling text after interacting with journal in arcane - If we haven't opened the wp menu yet at the start of nith, do so in order to check if we have the halls wp or not - Fix skipping unid torches in orgtorch, it was due to use of localestringid which isn't valid on the unid item (duh) --- d2bs/kolbot/libs/scripts/Nihlathak.js | 3 +++ d2bs/kolbot/libs/scripts/OrgTorch.js | 16 ++++++++++------ d2bs/kolbot/libs/scripts/Summoner.js | 4 ++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js index 5618b3e6c..7f2f1a56e 100644 --- a/d2bs/kolbot/libs/scripts/Nihlathak.js +++ b/d2bs/kolbot/libs/scripts/Nihlathak.js @@ -6,8 +6,11 @@ */ function Nihlathak() { + Town.goToTown(5); Town.doChores(); + !Pather.initialized && Pather.useWaypoint(null, true); + // UseWaypoint if set to or if we already have it if (Config.Nihlathak.UseWaypoint || getWaypoint(Pather.wpAreas.indexOf(sdk.areas.HallsofPain))) { Pather.useWaypoint(sdk.areas.HallsofPain); diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index 76d025639..65263de08 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -27,11 +27,11 @@ function OrgTorch () { const OrgTorchData = { filePath: "logs/OrgTorch-" + me.profile + ".json", - default: {gamename: me.gamename, doneAreas: []}, + _default: { gamename: me.gamename, doneAreas: [] }, create: function () { - FileTools.writeText(this.filePath, JSON.stringify(this.default)); - return this.default; + FileTools.writeText(this.filePath, JSON.stringify(this._default)); + return this._default; }, read: function () { @@ -40,7 +40,7 @@ function OrgTorch () { let string = FileTools.readText(this.filePath); obj = JSON.parse(string); } catch (e) { - return this.default; + return this._default; } return obj; @@ -57,6 +57,10 @@ function OrgTorch () { } }; + /** + * @param {ItemUnit} item + * @returns {boolean} + */ this.getQuestItem = function (item) { if (item) { let id = item.classid; @@ -81,9 +85,9 @@ function OrgTorch () { if (!Config.OrgTorch.MakeTorch) return false; - let torch = me.checkItem({name: sdk.locale.items.HellfireTorch}); + let torch = me.checkItem({ classid: sdk.items.LargeCharm, quality: sdk.items.quality.Unique }); - if (torch.have && Pickit.checkItem(torch.item).result === 1) { + if (torch.have && Pickit.checkItem(torch.item).result === Pickit.Result.WANTED) { if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { scriptBroadcast("muleTorch"); scriptBroadcast("quit"); diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index fa70f4528..e1bf6ae3a 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -40,8 +40,8 @@ function Summoner () { } Packet.entityInteract(journal); - Misc.poll(() => getIsTalkingNPC(), 1000, 50); - me.cancel(); + Misc.poll(() => getIsTalkingNPC() || Game.getObject(sdk.objects.RedPortal), 2000, 200); + me.cancel() && me.cancel(); if (Pather.usePortal(sdk.areas.CanyonofMagic)) { break; From 36c31145e23476f32e4f0487c20408ddb71f4af3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 12:33:50 -0500 Subject: [PATCH 012/758] Possible first mule fix - Still not sure of the exact cause of mules leaving on first character creation, but added extra check for foreverAlone and items on the ground --- d2bs/kolbot/D2BotMule.dbj | 18 +++++++++++++++--- d2bs/kolbot/sdk/globals.d.ts | 13 +++++++++---- d2bs/kolbot/sdk/types/Util.d.ts | 10 ++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 1d070b8bb..2307398f7 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -33,15 +33,17 @@ includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -let Controls = require("./modules/Control"); -let Overrides = require("./modules/Override"); +const Controls = require("./modules/Control"); +const Overrides = require("./modules/Override"); const Worker = require("./modules/Worker"); /** @global */ -let master, muleMode, muleFilename, muleObj, maxCharCount; +let master, muleMode, muleFilename, maxCharCount; let [checkOnJoin, makeNext] = [false, false]; let status = "loading"; let masterStatus = { status: "" }; +/** @type {muleObj} */ +let muleObj; /** * Mule Data object manipulates external mule datafile @@ -225,6 +227,7 @@ const Mule = { } me.overhead("begin"); + console.log("begin"); }, /** @@ -241,6 +244,7 @@ const Mule = { MuleData.write(obj); D2Bot.printToConsole("Done muling.", sdk.colors.D2Bot.DarkGold); + console.log("Done muling"); sendCopyData(null, master, 10, JSON.stringify({ status: "quit" })); D2Bot.stop(me.profile, true); }, @@ -403,6 +407,14 @@ const Mule = { Mule.clearedJunk = true; // only do this once } + const getItems = () => getUnits(sdk.unittype.Item).filter(i => i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType)); + console.debug(masterStatus, checkOnJoin); + if (Mule.foreverAlone()) { + if (!Misc.poll(() => !Mule.foreverAlone() && getItems.length > 0, Time.seconds(2), 500)) { + return rval; + } + } + while (me.gameReady) { if (masterStatus.status === "done" || Mule.continuousMule || checkOnJoin) { checkOnJoin && (checkOnJoin = false); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index e031f61c1..8e73106ac 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -77,10 +77,6 @@ declare global { setPrototypeOf(obj: object, proto: object) } - interface ObjectUnit extends Unit { - objtype: number; - } - class ScriptError extends Error { } @@ -342,6 +338,15 @@ declare global { equip(destination: number | undefined, item: ItemUnit) } + interface ObjectUnit extends Unit { + } + + type ObjectType = 2; + class ObjectUnit extends Unit { + public type: ObjectType; + objtype: number; + } + type MissileType = 3; class Missile extends Unit { public readonly type: MissileType; diff --git a/d2bs/kolbot/sdk/types/Util.d.ts b/d2bs/kolbot/sdk/types/Util.d.ts index b48dc95cc..0329e5cc3 100644 --- a/d2bs/kolbot/sdk/types/Util.d.ts +++ b/d2bs/kolbot/sdk/types/Util.d.ts @@ -81,6 +81,16 @@ declare global { } function getThreads(): Script[]; + function getUnits(type: MonsterType, name?: string, mode?: number, unitId?: number): Monster[]; + function getUnits(type: MonsterType, classId?: number, mode?: number, unitId?: number): Monster[]; + function getUnits(type: ObjectType, name?: string, mode?: number, unitId?: number): ObjectUnit[]; + function getUnits(type: ObjectType, classId?: number, mode?: number, unitId?: number): ObjectUnit[]; + function getUnit(type?: MissileType, name?: string, mode?: number, unitId?: number): Missile[] + function getUnit(type?: MissileType, classId?: number, mode?: number, unitId?: number): Missile[] + function getUnits(type: ItemType, name?: string, mode?: number, unitId?: number): ItemUnit[]; + function getUnits(type: ItemType, classId?: number, mode?: number, unitId?: number): ItemUnit[]; + function getUnits(type: TileType, name?: string, mode?: number, unitId?: number): Tile[]; + function getUnits(type: TileType, classId?: number, mode?: number, unitId?: number): Tile[]; function getUnits(...args: any[]): Unit[]; function clickItemAndWait(...args: Args[]): boolean; function clickUnitAndWait(button: number, shift: 0 | 1, unit: Unit): boolean; From 1644146b4b5d4bf2b17b32254e58d595d905ee14 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 13:37:21 -0500 Subject: [PATCH 013/758] Update Pather.js - couple minor changes, mostly formatting - Fix failure to move somewhere due to low retry count. May increase it more for walkers but for now if path destination is 60 units away, then give us 10 retrys if we can't teleport there - Add small delay after using an exit to prevent attempting to move before fully loaded in the level --- d2bs/kolbot/libs/core/Pather.js | 67 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 9fa4747df..8a742838c 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -5,8 +5,10 @@ * */ -// TODO: this needs to be re-worked -// Perform certain actions after moving to each node +/** + * Perform certain actions after moving to each node + * @todo this needs to be re-worked + */ const NodeAction = { /** * @type {number[]} @@ -31,7 +33,9 @@ const NodeAction = { * @returns {void} */ killMonsters: function (arg = {}) { - const settings = Object.assign({}, { + if (arg.hasOwnProperty("allowClearing") && !arg.allowClearing) return; + + const killSettings = Object.assign({}, { clearPath: false, specType: sdk.monsters.spectype.All, range: 10, @@ -43,7 +47,7 @@ const NodeAction = { monList.length > 0 && Attack.clearList(monList); } - if ((typeof Config.ClearPath === "number" || typeof Config.ClearPath === "object") && settings.clearPath === false && !settings.overrideConfig) { + if ((typeof Config.ClearPath === "number" || typeof Config.ClearPath === "object") && killSettings.clearPath === false && !killSettings.overrideConfig) { switch (typeof Config.ClearPath) { case "number": Attack.clear(30, Config.ClearPath); @@ -60,8 +64,8 @@ const NodeAction = { return; } - if (settings.clearPath !== false) { - Attack.clear(settings.range, settings.specType); + if (killSettings.clearPath !== false) { + Attack.clear(killSettings.range, killSettings.specType); } }, @@ -175,6 +179,9 @@ const Pather = { } }, + /** + * @todo Handle rare bug where teleport skill dissapears from enigma + */ canTeleport: function () { return this.teleport && (Skill.canUse(sdk.skills.Teleport) || me.getStat(sdk.stats.OSkill, sdk.skills.Teleport)); }, @@ -198,25 +205,25 @@ const Pather = { * @returns {PathNode} */ spotOnDistance: function (spot, distance, givenSettings = {}) { - const settings = Object.assign({}, { + const spotSettings = Object.assign({}, { area: me.area, reductionType: 2, coll: (sdk.collision.BlockWalk), returnSpotOnError: true }, givenSettings); - let nodes = (getPath(settings.area, me.x, me.y, spot.x, spot.y, settings.reductionType, 4) || []); + let nodes = (getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, spotSettings.reductionType, 4) || []); if (!nodes.length) { - if (settings.reductionType === 2) { + if (spotSettings.reductionType === 2) { // try again with walking reduction - nodes = getPath(settings.area, me.x, me.y, spot.x, spot.y, 0, 4); + nodes = getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, 0, 4); } - if (!nodes.length) return (settings.returnSpotOnError ? spot : { x: me.x, y: me.y }); + if (!nodes.length) return (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y }); } - return (nodes.find((node) => getDistance(spot.x, spot.y, node.x, node.y) < distance && Pather.checkSpot(node.x, node.y, settings.coll)) - || (settings.returnSpotOnError ? spot : { x: me.x, y: me.y })); + return (nodes.find((node) => getDistance(spot.x, spot.y, node.x, node.y) < distance && Pather.checkSpot(node.x, node.y, spotSettings.coll)) + || (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y })); }, /** @@ -280,7 +287,7 @@ const Pather = { } let fail = 0; - let node = {x: target.x, y: target.y}; + let node = { x: target.x, y: target.y }; const leaped = { at: 0, /** @type {PathNode} */ @@ -311,6 +318,10 @@ const Pather = { let path = getPath(me.area, target.x, target.y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? (annoyingArea ? 30 : this.teleDistance) : this.walkDistance); if (!path) throw new Error("move: Failed to generate path."); + if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { + settings.retry = 10; + } + path.reverse(); settings.pop && path.pop(); PathDebug.drawPath(path); @@ -367,6 +378,9 @@ const Pather = { } } else { if (!me.inTown) { + /** + * @todo I think some of this needs to be re-worked, I've noticed recursive Attacking/Picking + */ if (!useTeleport && settings.allowClearing && me.checkForMobs({range: 10}) && Attack.clear(10, null, null, null, settings.allowPicking)) { console.debug("Cleared Node"); continue; @@ -459,7 +473,7 @@ const Pather = { * @returns {boolean} */ 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)); }, /** @@ -471,7 +485,7 @@ const Pather = { * @returns {boolean} */ moveTo: function (x, y, retry, clearPath = false, pop = false) { - return Pather.move({x: x, y: y}, {retry: retry, pop: pop, allowClearing: clearPath}); + return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, allowClearing: clearPath }); }, /** @@ -482,7 +496,7 @@ const Pather = { * @returns */ moveToEx: function (x, y, givenSettings = {}) { - return Pather.move({x: x, y: y}, givenSettings); + return Pather.move({ x: x, y: y }, givenSettings); }, /** @@ -1017,9 +1031,10 @@ const Pather = { Misc.poll(() => me.gameReady, 1000, 200); } - /* i < areas.length - 1 is for crossing multiple areas. - In that case we must use the exit before the last area. - */ + /** + * i < areas.length - 1 is for crossing multiple areas. + * In that case we must use the exit before the last area. + */ if (use || i < areas.length - 1) { switch (currExit.type) { case 1: // walk through @@ -1191,9 +1206,12 @@ const Pather = { let preArea = me.area; if (!unit) { - throw new Error("useUnit: Unit not found. TYPE: " + type + " ID: " + id + " MyArea: " + getAreaName(me.area) + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); + throw new Error( + "useUnit: Unit not found. TYPE: " + type + " ID: " + id + " MyArea: " + getAreaName(me.area) + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "") + ); } + MainLoop: for (let i = 0; i < 5; i += 1) { let usetk = (i < 2 && Skill.useTK(unit)); @@ -1228,9 +1246,8 @@ const Pather = { while (getTickCount() - tick < 3000) { if ((!targetArea && me.area !== preArea) || me.area === targetArea) { delay(200); - //Packet.flash(me.gid); - return true; + break MainLoop; } delay(10); @@ -1241,6 +1258,10 @@ const Pather = { !!coord && this.moveTo(coord.x, coord.y); } + while (!me.idle && !me.gameReady) { + delay(40); + } + return targetArea ? me.area === targetArea : me.area !== preArea; }, From b5d86a41482127c247127f829bcde9630d67c146 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 13:39:53 -0500 Subject: [PATCH 014/758] Update Misc.js - Fix FileAction for errorReport. It got set to read rather than append --- d2bs/kolbot/libs/core/Misc.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 5e99fa6b0..2c79674af 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -603,7 +603,11 @@ const Misc = { errorConsolePrint: true, screenshotErrors: true, - // Report script errors to logs/ScriptErrorLog.txt + /** + * Report script errors to logs/ScriptErrorLog.txt + * @param {Error | string} error + * @param {string} [script] + */ errorReport: function (error, script) { let msg, oogmsg, filemsg, source, stack; let stackLog = ""; @@ -649,7 +653,7 @@ const Misc = { this.errorConsolePrint && D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); showConsole(); console.log(msg); - FileAction.read("logs/ScriptErrorLog.txt", filemsg); + FileAction.append("logs/ScriptErrorLog.txt", filemsg); if (this.screenshotErrors) { takeScreenshot(); From 9e721bc267c6f5ff1190039bc1649e1cfedec49e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 13:43:23 -0500 Subject: [PATCH 015/758] Update Barbarian.js - Relocate monster corpse if we've had to retry horking and break the loop if we can't find it or its now invalid - Added pickItem call if we aren't using fastPick, this is experimental for now. Might need range checks to prevent moving anywhere as we really just want to pick item directly around us --- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 28 +++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index 8ae279087..d5fece86c 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -96,6 +96,10 @@ const ClassAttack = { switch (attackSkill) { case sdk.skills.Whirlwind: + /** + * @todo we sometimes struggle getting into position because of monsters which is dumb since we can + * just whirl through them, so that needs to be fixed + */ 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; @@ -179,7 +183,7 @@ const ClassAttack = { } corpseList.sort(Sort.units); - let check = corpseList.shift(); + const check = corpseList.shift(); let attempted = false; let invalidated = false; // get the actual corpse rather than the copied unit @@ -192,10 +196,13 @@ const ClassAttack = { CorpseLoop: for (let j = 0; j < 3; j += 1) { // sometimes corpse can become invalidated - necro summoned from it or baal wave clearing, ect - if (!corpse || !copyUnit(corpse).x) { - console.debug("We lost reference to the corpse"); - invalidated = true; - break; + // 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); @@ -206,7 +213,7 @@ const ClassAttack = { while (getTickCount() - tick < 1000) { if (corpse.getState(sdk.states.CorpseNoSelect)) { - Pickit.fastPick(); + Config.FastPick ? Pickit.fastPick() : Pickit.pickItems(range); break CorpseLoop; } @@ -217,7 +224,6 @@ const ClassAttack = { } 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)); } } @@ -229,13 +235,17 @@ const ClassAttack = { } Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); - Pickit.pickItems(); return true; }, + /** + * Check if corpse is horkable + * @param {Monster} unit + * @returns {boolean} + */ checkCorpse: function (unit) { - if (!unit || unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead) return false; + if (!unit || !copyUnit(unit).x || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) return false; if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 && unit.spectype === sdk.monsters.spectype.All) { return false; From abf92757352549b1f00f476083ec1e74f407290f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 15:31:53 -0500 Subject: [PATCH 016/758] Update D2BotMule.dbj - Attempt I don't even know at this point, to fix muling. Removed checkOnJoin, added safety check in case there are items on the ground and we haven't received our masters message - added some debug messages to see more info --- d2bs/kolbot/D2BotMule.dbj | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 2307398f7..c2333c625 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -39,7 +39,7 @@ const Worker = require("./modules/Worker"); /** @global */ let master, muleMode, muleFilename, maxCharCount; -let [checkOnJoin, makeNext] = [false, false]; +let [makeNext] = [false]; let status = "loading"; let masterStatus = { status: "" }; /** @type {muleObj} */ @@ -90,7 +90,6 @@ const MuleData = { }, nextChar: function () { - checkOnJoin = true; let charSuffix = ""; let charNumbers = "abcdefghijklmnopqrstuvwxyz"; let obj = MuleData.read(); @@ -202,7 +201,6 @@ const Mule = { Pather.moveToUnit(coord); Storage.Init(); - checkOnJoin && (status = "begin"); Starter.inGame = true; if (Mule.continuousMule) { !muleObj.onlyLogWhenFull && MuleLogger.logChar(); @@ -227,7 +225,7 @@ const Mule = { } me.overhead("begin"); - console.log("begin"); + console.debug("begin"); }, /** @@ -253,7 +251,7 @@ const Mule = { MuleLogger.logChar(); delay(500); - [makeNext, checkOnJoin] = [true, true]; + [makeNext] = [true]; let obj = MuleData.read(); if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { @@ -388,6 +386,7 @@ const Mule = { let waitTick = getTickCount(); let rval = "fail"; let list = []; + let override = false; while (!me.name || !me.gameReady) { if (!me.ingame) return rval; @@ -408,25 +407,20 @@ const Mule = { } const getItems = () => getUnits(sdk.unittype.Item).filter(i => i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType)); - console.debug(masterStatus, checkOnJoin); + console.debug(masterStatus, Starter.firstLogin); if (Mule.foreverAlone()) { - if (!Misc.poll(() => !Mule.foreverAlone() && getItems.length > 0, Time.seconds(2), 500)) { + if (!Misc.poll(() => !Mule.foreverAlone() && getItems().length > 0, Time.seconds(2), 500)) { return rval; } } while (me.gameReady) { - if (masterStatus.status === "done" || Mule.continuousMule || checkOnJoin) { - checkOnJoin && (checkOnJoin = false); + if (masterStatus.status === "done" || Mule.continuousMule || override) { + override = false; let item = Game.getItem(); if (item) { - do { - // don't pick up trash - if (item.distance < 20 && item.onGroundOrDropping && !Town.ignoreType(item.itemType)) { - list.push(copyUnit(item)); - } - } while (item.getNext()); + list = getItems(); } // If and only if there is nothing left are we "done" @@ -483,6 +477,12 @@ const Mule = { break; } } + // safety check + if (getTickCount() - waitTick > Time.minutes(3) && getItems().length) { + // why are there items on the ground but we haven't recieved the go ahead? + console.debug("Anyone in game? " + Mule.foreverAlone(), masterStatus, Starter.firstLogin); + override = true; + } } delay(500); @@ -889,14 +889,14 @@ function gameEvent (mode, param1, param2, name1, name2) { case 0x01: // "%Name1(%Name2) dropped due to errors." case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." if (Mule.foreverAlone()) { - console.log("Waiting"); + console.debug("Waiting"); status = "ready"; } break; case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." if (name1.trim() !== me.name.trim()) { - console.log("begin"); + console.debug("begin"); status = "begin"; } From 45fe470bd8adc770437effafc5db49620c3d12dd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:07:22 -0500 Subject: [PATCH 017/758] Update D2Bot.js - Possible fix to globalThis being undefined --- d2bs/kolbot/libs/oog/D2Bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index 93aac874d..5c8e6054a 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -17,7 +17,7 @@ includeIfNotIncluded("oog/DataFile.js"); } else { root.D2Bot = factory(); } -}(globalThis, function() { +}([].filter.constructor("return this")(), function() { const CopyData = require("../modules/CopyData"); const D2Bot = { From 0f8b500d7d5004086eb04e14ad9dca66a8124896 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:09:46 -0500 Subject: [PATCH 018/758] Faster Souls/Dolls quit in baal - Pass callback check to Pather.moveToEx so we don't need to get all the way to the throne (possibly running though dolls/souls) before we actually check for them - cleanup of orgtorch, no actual changes --- d2bs/kolbot/libs/scripts/Baal.js | 26 ++-- d2bs/kolbot/libs/scripts/OrgTorch.js | 205 +++++++++++++++++---------- 2 files changed, 141 insertions(+), 90 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index 29b5e5f0f..888e0d574 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -7,7 +7,7 @@ function Baal() { include("core/Common/Baal.js"); - this.announce = function () { + const announce = function () { let count, string, souls, dolls; let monster = Game.getMonster(); @@ -54,22 +54,20 @@ function Baal() { throw new Error("Failed to move to Throne of Destruction."); } - Pather.moveTo(15095, 5029); - - if (Config.Baal.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - say("Dolls found! NG."); - - return true; - } - - if (Config.Baal.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { - say("Souls found! NG."); + Pather.moveToEx(15095, 5029, { callback: () => { + if (Config.Baal.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + say("Dolls found! NG."); + throw new ScriptError("Dolls found! NG."); + } - return true; - } + if (Config.Baal.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + say("Souls found! NG."); + throw new ScriptError("Souls found! NG."); + } + }}); if (Config.PublicMode) { - this.announce(); + announce(); Pather.moveTo(15118, 5002); Pather.makePortal(); say(Config.Baal.HotTPMessage); diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index 65263de08..c04093d11 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -18,7 +18,7 @@ */ function OrgTorch () { - this.currentGameInfo = null; + let currentGameInfo = null; const portalMode = { MiniUbers: 0, @@ -61,7 +61,7 @@ function OrgTorch () { * @param {ItemUnit} item * @returns {boolean} */ - this.getQuestItem = function (item) { + const getQuestItem = function (item) { if (item) { let id = item.classid; let canFit = Storage.Inventory.CanFit(item); @@ -75,7 +75,7 @@ function OrgTorch () { }; // Identify & mule - this.checkTorch = function () { + const checkTorch = function () { if (me.inArea(sdk.areas.UberTristram)) { Pather.moveTo(25105, 5140); Pather.usePortal(sdk.areas.Harrogath); @@ -99,11 +99,15 @@ function OrgTorch () { return false; }; - // Try to lure a monster - wait until it's close enough - // needs to be re-done - // should, lure boss AWAY from the others and to us - // create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses - this.lure = function (bossId) { + /** + * Try to lure a monster - wait until it's close enough + * @param {number} bossId + * @returns {boolean} + * @todo redo this + * - should, lure boss AWAY from the others and to us + * - create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses + */ + const lure = function (bossId) { let unit = Game.getMonster(bossId); if (unit) { @@ -121,11 +125,25 @@ function OrgTorch () { return false; }; - // Check if we have complete sets of organs - this.completeSetCheck = function () { - let horns = me.findItems("dhn"); - let brains = me.findItems("mbr"); - let eyes = me.findItems("bey"); + /** + * Check if we have complete sets of organs + * @returns {boolean} + */ + const completeSetCheck = function () { + let [horns, brains, eyes] = [0, 0, 0]; + me.getItemsEx() + .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) + .forEach(i => { + switch (i.classid) { + case sdk.items.quest.DiablosHorn: + return (horns++); + case sdk.items.quest.MephistosBrain: + return (brains++); + case sdk.items.quest.BaalsEye: + return (eyes++); + default: return; + } + }); if (!horns || !brains || !eyes) { return false; @@ -133,17 +151,24 @@ function OrgTorch () { // We just need one set to make a torch if (Config.OrgTorch.MakeTorch) { - return horns.length && brains.length && eyes.length; + return horns > 0 && brains > 0 && eyes > 0; } - return horns.length === brains.length && horns.length === eyes.length && brains.length === eyes.length; + return horns === brains && horns === eyes && brains === eyes; }; - // Get fade in River of Flames - only works if we are wearing an item with ctc Fade - // todo - equipping an item from storage if we have it - this.getFade = function () { + /** + * Get fade in River of Flames - only works if we are wearing an item with ctc Fade + * @returns {boolean} + * @todo equipping an item from storage if we have it + */ + const getFade = function () { if (Config.OrgTorch.GetFade && !me.getState(sdk.states.Fade) - && me.haveSome([{name: sdk.locale.items.Treachery, equipped: true}, {name: sdk.locale.items.LastWish, equipped: true}, {name: sdk.locale.items.SpiritWard, equipped: true}])) { + && me.haveSome([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ])) { console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); // lets figure out what fade item we have before we leave town let fadeItem = me.findFirst([ @@ -178,8 +203,12 @@ function OrgTorch () { return true; }; - // Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram - this.openPortal = function (mode) { + /** + * Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram + * @param {number} mode + * @returns {ObjectUnit | false} + */ + const openPortal = function (mode) { let item1 = mode === portalMode.MiniUbers ? me.findItem("pk1", sdk.items.mode.inStorage) : me.findItem("dhn", sdk.items.mode.inStorage); let item2 = mode === portalMode.MiniUbers ? me.findItem("pk2", sdk.items.mode.inStorage) : me.findItem("bey", sdk.items.mode.inStorage); let item3 = mode === portalMode.MiniUbers ? me.findItem("pk3", sdk.items.mode.inStorage) : me.findItem("mbr", sdk.items.mode.inStorage); @@ -205,7 +234,7 @@ function OrgTorch () { switch (mode) { case portalMode.MiniUbers: if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype) - && this.currentGameInfo.doneAreas.indexOf(portal.objtype) === -1) { + && currentGameInfo.doneAreas.indexOf(portal.objtype) === -1) { return copyUnit(portal); } @@ -224,21 +253,21 @@ function OrgTorch () { return false; }; - this.matronsDen = function () { + const matronsDen = function () { let dHorns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length; Precast.doPrecast(true); Pather.moveToPreset(sdk.areas.MatronsDen, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); Attack.kill(sdk.monsters.Lilith); Pickit.pickItems(); - this.getQuestItem(Game.getItem(sdk.items.quest.DiablosHorn)); + getQuestItem(Game.getItem(sdk.items.quest.DiablosHorn)); Town.goToTown(); // we sucessfully picked up the horn return (me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length > dHorns); }; - this.forgottenSands = function () { + const forgottenSands = function () { let bEyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length; Precast.doPrecast(true); @@ -272,7 +301,7 @@ function OrgTorch () { Attack.kill(sdk.monsters.UberDuriel); Pickit.pickItems(); - this.getQuestItem(Game.getItem(sdk.items.quest.BaalsEye)); + getQuestItem(Game.getItem(sdk.items.quest.BaalsEye)); Town.goToTown(); } catch (e) { // @@ -282,22 +311,24 @@ function OrgTorch () { return (me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length > bEyes); }; - this.furnance = function () { + const furnance = function () { let mBrain = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length; Precast.doPrecast(true); Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); Attack.kill(sdk.monsters.UberIzual); Pickit.pickItems(); - this.getQuestItem(Game.getItem(sdk.items.quest.MephistosBrain)); + getQuestItem(Game.getItem(sdk.items.quest.MephistosBrain)); Town.goToTown(); // we sucessfully picked up the brain return (me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length > mBrain); }; - // re-write this, lure doesn't always work and other classes can do ubers - this.uberTrist = function () { + /** + * @todo re-write this, lure doesn't always work and other classes can do ubers + */ + const uberTrist = function () { let skillBackup; let useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); @@ -315,10 +346,10 @@ function OrgTorch () { } useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - this.lure(sdk.monsters.UberMephisto); + lure(sdk.monsters.UberMephisto); Pather.moveTo(25129, 5198); useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - this.lure(sdk.monsters.UberMephisto); + lure(sdk.monsters.UberMephisto); if (!Game.getMonster(sdk.monsters.UberMephisto)) { Pather.moveTo(25122, 5170); @@ -354,39 +385,45 @@ function OrgTorch () { Attack.kill(sdk.monsters.UberBaal); Pickit.pickItems(); - this.currentGameInfo.doneAreas.push(sdk.areas.UberTristram) && OrgTorchData.update(this.currentGameInfo); - this.checkTorch(); + currentGameInfo.doneAreas.push(sdk.areas.UberTristram) && OrgTorchData.update(currentGameInfo); + checkTorch(); }; - // Do mini ubers or Tristram based on area we're already in - this.pandemoniumRun = function (portalId) { + /** + * Do mini ubers or Tristram based on area we're already in + * @param {number} portalId + */ + const pandemoniumRun = function (portalId) { switch (me.area) { case sdk.areas.MatronsDen: - if (this.matronsDen()) { - this.currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(this.currentGameInfo); + if (matronsDen()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); } break; case sdk.areas.ForgottenSands: - if (this.forgottenSands()) { - this.currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(this.currentGameInfo); + if (forgottenSands()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); } break; case sdk.areas.FurnaceofPain: - if (this.furnance()) { - this.currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(this.currentGameInfo); + if (furnance()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); } break; case sdk.areas.UberTristram: - this.uberTrist(); + uberTrist(); break; } }; - this.runEvent = function (portal) { + /** + * @param {ObjectUnit} portal + */ + runEvent = function (portal) { if (portal) { if (Config.OrgTorch.PreGame.Antidote.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Antidote.Drink > 0) { Town.buyPots(Config.OrgTorch.PreGame.Antidote.Drink, "Antidote", true, true); @@ -397,24 +434,24 @@ function OrgTorch () { Town.move("stash"); console.log("taking portal: " + portal.objtype); Pather.usePortal(null, null, portal); - this.pandemoniumRun(portal.objtype); + pandemoniumRun(portal.objtype); } }; - this.juvCheck = function () { - let needJuvs = 0; - let col = Town.checkColumns(Storage.BeltSize()); + // const juvCheck = function () { + // let needJuvs = 0; + // let col = Town.checkColumns(Storage.BeltSize()); - for (let i = 0; i < 4; i += 1) { - if (Config.BeltColumn[i] === "rv") { - needJuvs += col[i]; - } - } + // for (let i = 0; i < 4; i += 1) { + // if (Config.BeltColumn[i] === "rv") { + // needJuvs += col[i]; + // } + // } - print("Need " + needJuvs + " juvs."); + // print("Need " + needJuvs + " juvs."); - return needJuvs; - }; + // return needJuvs; + // }; // ################# // /* ##### START ##### */ @@ -423,22 +460,38 @@ function OrgTorch () { // make sure we are picking the organs Config.PickitFiles.length === 0 && NTIP.OpenFile("pickit/keyorg.nip", true); - FileTools.exists(OrgTorchData.filePath) && (this.currentGameInfo = OrgTorchData.read()); + FileTools.exists(OrgTorchData.filePath) && (currentGameInfo = OrgTorchData.read()); - if (!this.currentGameInfo || this.currentGameInfo.gamename !== me.gamename) { - this.currentGameInfo = OrgTorchData.create(); + if (!currentGameInfo || currentGameInfo.gamename !== me.gamename) { + currentGameInfo = OrgTorchData.create(); } let portal; - let tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; - let hkeys = me.findItems("pk2", sdk.items.mode.inStorage).length || 0; - let dkeys = me.findItems("pk3", sdk.items.mode.inStorage).length || 0; - let brains = me.findItems("mbr", sdk.items.mode.inStorage).length || 0; - let eyes = me.findItems("bey", sdk.items.mode.inStorage).length || 0; - let horns = me.findItems("dhn", sdk.items.mode.inStorage).length || 0; + let [tkeys, hkeys, dkeys] = [0, 0, 0]; + let [brains, eyes, horns] = [0, 0, 0]; + + me.getItemsEx() + .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) + .forEach(i => { + switch (i.classid) { + case sdk.items.quest.KeyofTerror: + return (tkeys++); + case sdk.items.quest.KeyofHate: + return (hkeys++); + case sdk.items.quest.KeyofDestruction: + return (dkeys++); + case sdk.items.quest.DiablosHorn: + return (horns++); + case sdk.items.quest.MephistosBrain: + return (brains++); + case sdk.items.quest.BaalsEye: + return (eyes++); + default: return; + } + }); // Do town chores and quit if MakeTorch is true and we have a torch. - this.checkTorch(); + checkTorch(); // Wait for other bots to drop off their keys. This works only if TorchSystem.js is configured properly. Config.OrgTorch.WaitForKeys && TorchSystem.waitForKeys(); @@ -463,7 +516,7 @@ function OrgTorch () { }); } else { // possible same game name but different day and data file never got deleted - this.currentGameInfo.doneAreas.length > 0 && (this.currentGameInfo = OrgTorchData.create()); + currentGameInfo.doneAreas.length > 0 && (currentGameInfo = OrgTorchData.create()); } // End the script if we don't have enough keys nor organs @@ -478,7 +531,7 @@ function OrgTorch () { // We have enough keys, do mini ubers if (tkeys >= keySetsReq && hkeys >= keySetsReq && dkeys >= keySetsReq) { - this.getFade(); + getFade(); Town.goToTown(5); console.log("Making organs."); D2Bot.printToConsole("OrgTorch: Making organs.", sdk.colors.D2Bot.DarkGold); @@ -491,7 +544,7 @@ function OrgTorch () { if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(redPortals[i].objtype) && !currentGameInfo.doneAreas.includes(redPortals[i].objtype)) { portal = redPortals[i]; - this.runEvent(portal); + runEvent(portal); } } } @@ -499,17 +552,17 @@ function OrgTorch () { for (let i = 0; i < keySetsReq; i += 1) { // Abort if we have a complete set of organs // If Config.OrgTorch.MakeTorch is false, check after at least one portal is made - if ((Config.OrgTorch.MakeTorch || i > 0) && this.completeSetCheck()) { + if ((Config.OrgTorch.MakeTorch || i > 0) && completeSetCheck()) { break; } - portal = this.openPortal(portalMode.MiniUbers); - this.runEvent(portal); + portal = openPortal(portalMode.MiniUbers); + runEvent(portal); } } // Don't make torches if not configured to OR if the char already has one - if (!Config.OrgTorch.MakeTorch || this.checkTorch()) { + if (!Config.OrgTorch.MakeTorch || checkTorch()) { OrgTorchData.remove(); return true; @@ -523,19 +576,19 @@ function OrgTorch () { // We have enough organs, do Tristram - or trist is open we may have chickened and came back so check it // if trist was already open when we joined should we run that first? if ((brains && eyes && horns) || tristOpen) { - this.getFade(); + getFade(); Town.goToTown(5); Town.move("stash"); if (!tristOpen) { console.log("Making torch"); D2Bot.printToConsole("OrgTorch: Making torch.", sdk.colors.D2Bot.DarkGold); - portal = this.openPortal(portalMode.UberTristram); + portal = openPortal(portalMode.UberTristram); } else { portal = Pather.getPortal(sdk.areas.UberTristram); } - this.runEvent(portal); + runEvent(portal); OrgTorchData.remove(); } From 789ded3af0bfc80ecd45cef26d9c2f36bc92d75d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:26:18 -0500 Subject: [PATCH 019/758] Update Tools.js - small fix to attempting to pot while in action sequence. --- d2bs/kolbot/libs/core/Common/Tools.js | 31 ++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index e1bdb0070..b376d6ed1 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -107,11 +107,19 @@ }, exit: function (chickenExit = false) { - chickenExit && D2Bot.updateChickens(); - Config.LogExperience && Experience.log(); - console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); - this.stopDefault(); - quit(); + try { + chickenExit && D2Bot.updateChickens(); + Config.LogExperience && Experience.log(); + console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); + this.stopDefault(); + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } + + return true; }, getPotion: function (pottype, type) { @@ -174,9 +182,6 @@ 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 this.pots.Health: @@ -191,8 +196,14 @@ let potion = this.getPotion(pottype, type); - if (!!potion) { - if (me.dead || me.mode === sdk.player.mode.SkillActionSequence) return false; + if (potion) { + if (me.dead) return false; + + if (me.mode === sdk.player.mode.SkillActionSequence) { + while (me.mode === sdk.player.mode.SkillActionSequence) { + delay (25); + } + } try { type < this.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); From 9bcbe458c20ae47c00354ac707ee867ccc4855c9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:36:23 -0500 Subject: [PATCH 020/758] Formatting mostly - More JSDOC comments - Fixed includes for AntiIdle, AreaWatcher, and AutoBuildThread - Add surrounding mob check to conditons for whirling/leaping during Pather.move if we are stuck --- d2bs/kolbot/libs/core/Packet.js | 81 ++++++++++++++++- d2bs/kolbot/libs/core/Pather.js | 116 +++++++++++++------------ d2bs/kolbot/threads/AntiIdle.js | 7 +- d2bs/kolbot/threads/AreaWatcher.js | 5 +- d2bs/kolbot/threads/AutoBuildThread.js | 14 +-- 5 files changed, 149 insertions(+), 74 deletions(-) diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 1154505eb..ba185a9d3 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -6,6 +6,11 @@ */ const Packet = { + /** + * Interact and open the menu of an NPC + * @param {NPCUnit} unit + * @returns {boolean} + */ openMenu: function (unit) { if (unit.type !== sdk.unittype.NPC) throw new Error("openMenu: Must be used on NPCs."); if (getUIFlag(sdk.uiflags.NPCMenu)) return true; @@ -40,6 +45,12 @@ const Packet = { return false; }, + /** + * Start a trade action with an NPC + * @param {NPCUnit} unit + * @param {number} mode + * @returns {boolean} + */ startTrade: function (unit, mode) { if (unit.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); if (getUIFlag(sdk.uiflags.Shop)) return true; @@ -64,6 +75,13 @@ const Packet = { return false; }, + /** + * Buy an item from an interacted NPC + * @param {NPCUnit} unit + * @param {boolean} shiftBuy + * @param {boolean} gamble + * @returns {boolean} + */ buyItem: function (unit, shiftBuy, gamble) { let oldGold = me.gold; let itemCount = me.itemcount; @@ -94,6 +112,14 @@ const Packet = { return false; }, + /** + * Buy scrolls from an interacted NPC, we need this as a seperate check because itemcount doesn't change + * if the scroll goes into the tome automatically. + * @param {NPCUnit} unit + * @param {ItemUnit} [tome] + * @param {boolean} [shiftBuy] + * @returns {boolean} + */ buyScroll: function (unit, tome, shiftBuy) { let oldGold = me.gold; let itemCount = me.itemcount; @@ -129,6 +155,11 @@ const Packet = { return false; }, + /** + * Sell a item to a NPC + * @param {ItemUnit} unit + * @returns {boolean} + */ sellItem: function (unit) { // Check if it's an item we want to buy if (unit.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); @@ -156,6 +187,11 @@ const Packet = { return false; }, + /** + * @param {ItemUnit} unit + * @param {ItemUnit} tome + * @returns {boolean} + */ identifyItem: function (unit, tome) { if (!unit || unit.identified) return false; @@ -284,9 +320,13 @@ const Packet = { return true; }, - useBeltItemForMerc: function (who) { - if (!who) return false; - sendPacket(1, sdk.packets.send.UseBeltItem, 4, who.gid, 4, 1, 4, 0); + /** + * @param {ItemUnit} pot + * @returns {boolean} + */ + useBeltItemForMerc: function (pot) { + if (!pot) return false; + sendPacket(1, sdk.packets.send.UseBeltItem, 4, pot.gid, 4, 1, 4, 0); return true; }, @@ -295,23 +335,41 @@ const Packet = { sendPacket(1, hand, 2, wX, 2, wY); }, + /** + * @param {number} hand + * @param {Monster | ItemUnit | ObjectUnit} who + * @returns {boolean} + */ unitCast: function (hand, who) { hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnEntityEx3 : sdk.packets.send.LeftSkillOnEntityEx3; sendPacket(1, hand, 4, who.type, 4, who.gid); }, + /** + * @param {Monster | ItemUnit | ObjectUnit} who + * @returns {boolean} + */ telekinesis: function (who) { if (!who || !Skill.setSkill(sdk.skills.Telekinesis, sdk.skills.hand.Right)) return false; sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); return true; }, + /** + * @param {Player | Monster | MercUnit} who + * @returns {boolean} + */ enchant: function (who) { if (!who || !Skill.setSkill(sdk.skills.Enchant, sdk.skills.hand.Right)) return false; sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); return true; }, + /** + * @param {number} wX + * @param {number} wY + * @returns {boolean} + */ teleport: function (wX, wY) { if (![wX, wY].every(n => typeof n === "number") || !Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right)) return false; new PacketBuilder().byte(sdk.packets.send.RightSkillOnLocation).word(wX).word(wY).send(); @@ -322,6 +380,13 @@ const Packet = { // //sendPacket(1, sdk.packets.send.MakeEntityMove, 4, npc.type, 4, npc.gid, 4, dwX, 4, dwY); // }, + /** + * @deprecated + * @param {number} x + * @param {number} y + * @param {number} maxDist + * @returns {boolean} + */ teleWalk: function (x, y, maxDist = 5) { !Packet.telewalkTick && (Packet.telewalkTick = 0); @@ -349,6 +414,11 @@ const Packet = { sendPacket(1, sdk.packets.send.UpdateQuests); }, + /** + * Request entity update + * @param {number} gid + * @param {number} wait + */ flash: function (gid, wait = 0) { wait === 0 && (wait = 300 + (me.gameReady ? 2 * me.ping : 300)); sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, 0, 4, gid); @@ -358,6 +428,11 @@ const Packet = { } }, + /** + * @deprecated + * @param {number} stat + * @param {number} value + */ changeStat: function (stat, value) { if (value > 0) { getPacket(1, 0x1d, 1, stat, 1, value); diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 8a742838c..ef5313b48 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -406,7 +406,7 @@ const Pather = { // 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) { + 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(); @@ -423,7 +423,7 @@ const Pather = { */ 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) { + 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(); @@ -945,10 +945,10 @@ const Pather = { /** * @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 {boolean} clearPath - kill monsters while moving + * @param {boolean} [use] - enter target area or last area in the array + * @param {boolean} [clearPath] - kill monsters while moving */ - moveToExit: function (targetArea, use = false, clearPath = false) { + moveToExit: function (targetArea, use, clearPath) { if (targetArea === undefined) return false; console.time("moveToExit"); @@ -1589,12 +1589,12 @@ const Pather = { return false; }, - /* - Pather.usePortal(targetArea, owner, unit); - targetArea - id of the area the portal leads to - owner - name of the portal's owner - unit - use existing portal unit - */ + /** + * @param {number} [targetArea] - id of the area the portal leads to + * @param {string} [owner] - name of the portal's owner + * @param {ObjectUnit} [unit] - use existing portal unit + * @returns {boolean} + */ usePortal: function (targetArea, owner, unit) { if (targetArea && me.area === targetArea) return true; @@ -1681,11 +1681,11 @@ const Pather = { return (targetArea ? me.area === targetArea : me.area !== preArea); }, - /* - Pather.getPortal(targetArea, owner, unit); - targetArea - id of the area the portal leads to - owner - name of the portal's owner - */ + /** + * @param {number} targetArea - id of the area the portal leads to + * @param {string} owner - name of the portal's owner + * @returns {ObjectUnit | false} + */ getPortal: function (targetArea, owner) { let portal = Game.getObject("portal"); @@ -1719,14 +1719,15 @@ const Pather = { return false; }, - /* - Pather.getNearestWalkable(x, y, range, step, coll, size); - x - the starting x coord - y - the starting y coord - range - maximum allowed range from the starting coords - step - distance between each checked dot on the grid - coll - collision flag to avoid - */ + /** + * @param {number} x - the starting x coord + * @param {number} y - the starting y coord + * @param {number} range - maximum allowed range from the starting coords + * @param {number} step - distance between each checked dot on the grid + * @param {number} coll - collision flag to avoid + * @param {number} size + * @returns {[number, number] | false} + */ getNearestWalkable: function (x, y, range, step, coll, size) { !step && (step = 1); coll === undefined && (coll = sdk.collision.BlockWall); @@ -1762,13 +1763,14 @@ const Pather = { return result; }, - /* - Pather.moveTo(x, y, coll, cacheOnly); - x - the x coord to check - y - the y coord to check - coll - collision flag to search for - cacheOnly - use only cached room data - */ + /** + * @param {number} x - the x coord to check + * @param {number} y - the y coord to check + * @param {number} coll - collision flag to search for + * @param {boolean} cacheOnly - use only cached room data + * @param {number} size + * @returns {boolean} + */ checkSpot: function (x, y, coll, cacheOnly, size) { coll === undefined && (coll = sdk.collision.BlockWall); !size && (size = 1); @@ -1788,10 +1790,10 @@ const Pather = { return true; }, - /* - Pather.accessToAct(act); - act - the act number to check for access - */ + /** + * @param {number} act - the act number to check for access + * @returns {boolean} + */ accessToAct: function (act) { switch (act) { // Act 1 is always accessible @@ -1811,11 +1813,11 @@ const Pather = { } }, - /* - Pather.getWP(area); - area - the id of area to get the waypoint in - clearPath - clear path - */ + /** + * @param {number} area - the id of area to get the waypoint in + * @param {boolean} [clearPath] + * @returns {boolean} + */ getWP: function (area, clearPath) { area !== me.area && this.journeyTo(area); @@ -1861,10 +1863,10 @@ const Pather = { return false; }, - /* - Pather.journeyTo(area); - area - the id of area to move to - */ + /** + * @param {number} area - the id of area to move to + * @returns {boolean} + */ journeyTo: function (area) { if (area === undefined) return false; console.time("journeyTo"); @@ -2041,11 +2043,13 @@ const Pather = { plotCourse_openedWpMenu: false, - /* - Pather.plotCourse(dest, src); - dest - destination area id - src - starting area id - */ + /** + * Plot a course to a specific area + * @param {number} src - starting area id + * @param {number} dest - destination area id + * @returns {{ course: number[], useWP: boolean } | false} + * @todo this needs more checks + */ plotCourse: function (dest, src) { let node, prevArea; let useWP = false; @@ -2077,7 +2081,7 @@ const Pather = { !src && (src = me.area); if (!this.plotCourse_openedWpMenu && me.inTown && this.nextAreas[me.area] !== dest && Pather.useWaypoint(null)) { - this.plotCourse_openedWpMenu = true; + Pather.plotCourse_openedWpMenu = true; } while (toVisitNodes.length > 0) { @@ -2140,11 +2144,13 @@ const Pather = { return {course: arr, useWP: useWP}; }, - /* - Pather.areasConnected(src, dest); - dest - destination area id - src - starting area id - */ + /** + * Check if two areas are connected + * @param {number} src - starting area id + * @param {number} dest - destination area id + * @returns {boolean} + * @todo this needs more checks + */ areasConnected: function (src, dest) { if (src === sdk.areas.CanyonofMagic && dest === sdk.areas.ArcaneSanctuary) { return false; diff --git a/d2bs/kolbot/threads/AntiIdle.js b/d2bs/kolbot/threads/AntiIdle.js index b3b30635f..b1d63c9a4 100644 --- a/d2bs/kolbot/threads/AntiIdle.js +++ b/d2bs/kolbot/threads/AntiIdle.js @@ -4,9 +4,10 @@ * @desc Prevent Idle diconnect * */ - -include("core/Prototypes.js"); -include("core/Misc.js"); +js_strict(true); +include("critical.js"); +include("core/Util.js"); +include("core/Packet.js"); function main () { console.log("ÿc3Start AntiIdle"); diff --git a/d2bs/kolbot/threads/AreaWatcher.js b/d2bs/kolbot/threads/AreaWatcher.js index a53bf8ecd..a3296955f 100644 --- a/d2bs/kolbot/threads/AreaWatcher.js +++ b/d2bs/kolbot/threads/AreaWatcher.js @@ -4,8 +4,9 @@ * @desc suicide walk prevention * */ -include("OOG.js"); -include("core/Prototypes.js"); +js_strict(true); +include("critical.js"); +include("core/Util.js"); function main() { let _default = getScript("default.dbj"); diff --git a/d2bs/kolbot/threads/AutoBuildThread.js b/d2bs/kolbot/threads/AutoBuildThread.js index 820796cc7..ab1b27908 100644 --- a/d2bs/kolbot/threads/AutoBuildThread.js +++ b/d2bs/kolbot/threads/AutoBuildThread.js @@ -5,24 +5,16 @@ * */ js_strict(true); - -include("json2.js"); // required? -include("polyfill.js"); // required -include("oog/D2Bot.js"); // required +include("critical.js"); // required // globals needed for core gameplay -include("core/NTItemParser.js"); -include("core/Util.js"); +includeCoreLibs(); include("core/Auto/AutoBuild.js"); include("core/Auto/AutoSkill.js"); include("core/Auto/AutoStat.js"); -includeCoreLibs(); // system libs -include("systems/automule/AutoMule.js"); -include("systems/gambling/Gambling.js"); -include("systems/crafting/CraftingSystem.js"); -include("systems/torch/TorchSystem.js"); +includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); From a8f6a121c0fac1b5ec276d2c98c273ebd00e21d0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 7 Feb 2023 09:29:49 -0500 Subject: [PATCH 021/758] Update Pickit.js - Minor cleanup, working to prevent pickit recursion - removed set data structure, use a global array instead --- d2bs/kolbot/libs/core/Pickit.js | 53 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index ea41f938b..efcf44ce0 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -358,10 +358,8 @@ const Pickit = { for (let i = 0; i < retry; i += 1) { if (me.dead) return false; if (this.track.lastItem === gid) { - D2Bot.printToConsole("Attempt to pick same item detected"); return true; } - // can't find the item if (!Game.getItem(-1, -1, gid)) { return false; @@ -369,7 +367,7 @@ const Pickit = { if (me.getItem(item.classid, -1, item.gid)) { console.debug("Already picked item"); - break; + return true; } while (!me.idle) { @@ -389,13 +387,18 @@ const Pickit = { } } else { if (item.distance > (Config.FastPick || i < 1 ? 6 : 4) || checkCollision(me, item, sdk.collision.BlockWall)) { - if (!Pather.moveNearUnit(item, 4)) { + if (!Pather.moveToEx(item.x, item.y, { retry: 3, allowPicking: false, minDist: 4 })) { continue; } // we had to move, lets check to see if it's still there - if (me.getItem(-1, -1, gid) || !Game.getItem(-1, -1, gid)) { - // we picked the item during another process or it's gone so don't continue - break; + if (me.getItem(-1, -1, gid)) { + // we picked the item during another process - recursion happened + // this has pontential to skip logging an item + return true; + } + if (!Game.getItem(-1, -1, gid)) { + // it's gone so don't continue, + return false; } } @@ -406,6 +409,7 @@ const Pickit = { let tick = getTickCount(); while (getTickCount() - tick < 1000) { + // why the use of copyUnit here? item = copyUnit(item); if (stats.classid === sdk.items.Gold) { @@ -510,10 +514,9 @@ const Pickit = { }, /** - * Sometimes we get in a recursion scenario with pickItems, maybe globally keep track of items instead - * so when we pick the item during recursion we don't end up getting the double print. Also maybe use a Set data structure? + * @type {ItemUnit[]} */ - pickList: new Set(), + pickList: [], /** * @param {number} range @@ -523,13 +526,8 @@ const Pickit = { if (me.dead) return false; let needMule = false; - /** - * @type {ItemUnit[]} - */ - let pickList = []; - - Town.clearBelt(); + // why wait for idle? while (!me.idle) { delay(40); } @@ -538,21 +536,23 @@ const Pickit = { if (item) { do { - if (item.onGroundOrDropping && getDistance(me, item) <= range && !this.pickList.has(item.gid)) { - pickList.push(copyUnit(item)); - // ensure uniqueness with set - this.pickList.add(item.gid); + 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()); } - while (pickList.length > 0) { + if (Pickit.pickList.some(i => [sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion].includes(i.itemType))) { + Town.clearBelt(); + } + + while (Pickit.pickList.length > 0) { if (me.dead) return false; - pickList.sort(this.sortItems); - let check = pickList.shift(); + Pickit.pickList.sort(this.sortItems); + const check = Pickit.pickList.shift(); // get the actual item again const itemToPick = Game.getItem(check.classid, -1, check.gid); - const itemGid = itemToPick.gid; // 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 @@ -602,11 +602,8 @@ const Pickit = { // Item can fit - pick it up if (canFit && this.pickItem(itemToPick, status.result, status.line)) { - // picked it up so remove gid from set - this.pickList.delete(itemGid); + // what should we do if we failed to pick it? } - } else { - this.pickList.delete(itemGid); } } } From 4d84b085c0ba8dee7f448fb2f604e83c028f66d0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 7 Feb 2023 09:31:36 -0500 Subject: [PATCH 022/758] Update TownChicken.js - broke togglePause up into pause and resume, add handle in case default.dbj has crashed (it's rare but I've seen it happen) --- d2bs/kolbot/threads/TownChicken.js | 51 ++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js index f7cce54c5..c1ddc67ef 100644 --- a/d2bs/kolbot/threads/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -1,6 +1,6 @@ /** * @filename TownChicken.js -* @author kolton +* @author kolton, theBGuy * @desc handle town chicken * */ @@ -15,26 +15,43 @@ includeSystemLibs(); function main() { let townCheck = false; + let scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; - this.togglePause = function () { - let scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; + this.pause = function () { + for (let i = 0; i < scripts.length; i += 1) { + let script = getScript(scripts[i]); + + if (scripts[i] === "default.dbj" && !script) { + !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + } + + if (script && script.running) { + script.pause(); + scripts[i] === "default.dbj" && print("ÿc1Pausing."); + } + } + return true; + }; + + this.resume = function () { for (let i = 0; i < scripts.length; i += 1) { let script = getScript(scripts[i]); - if (script) { - if (script.running) { - scripts[i] === "default.dbj" && print("ÿc1Pausing."); - script.pause(); - } else { - if (scripts[i] === "default.dbj") { - // resume only if clonekilla isn't running - if (!getScript("threads/clonekilla.js")) { - console.log("ÿc2Resuming."); - script.resume(); - } - } else { + if (script && !script.running && scripts[i] !== "default.dbj") { + script.resume(); + } else if (scripts[i] === "default.dbj") { + // resume only if clonekilla isn't running + if (!getScript("threads/clonekilla.js")) { + if (script && !script.running) { + console.log("ÿc2Resuming."); script.resume(); + } else { + if (!script) { + // default has crashed? We shouldn't be running then. Is toolsthread still up? + // if yes try to still quit normally, otherwise quit from here + !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + } } } } @@ -79,7 +96,7 @@ function main() { continue; } - this.togglePause(); + this.pause(); while (!me.gameReady) { if (me.dead) return; @@ -97,7 +114,7 @@ function main() { return; } finally { - this.togglePause(); + this.resume(); townCheck = false; } From e4e91173f865c09a573ddc46dc54ffd9f23aa354 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 7 Feb 2023 13:26:52 -0500 Subject: [PATCH 023/758] Loop Attack.clearClassids to ensure we succeeded - Keep track of original location when we use Attack.clearClassids, return there on each iteration of the loop since our goal is to clear those specific monsters at the location we called it from - Minor cleanup in Common\Diablo.js, some JSDOC comments and handle failure to kill seis due to being to far away --- d2bs/kolbot/libs/core/Attack.js | 60 +++++++++++++++++--------- d2bs/kolbot/libs/core/Common/Diablo.js | 51 +++++++++++++++++++++- 2 files changed, 90 insertions(+), 21 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 656697232..adc40dd26 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -468,10 +468,10 @@ const Attack = { return Attack.clear(10); } - ({orgx, orgy} = {orgx: boss.x, orgy: boss.y}); + ({orgx, orgy} = { orgx: boss.x, orgy: boss.y }); Config.MFLeader && !!bossId && Pather.makePortal() && say("clear " + bossId); } else { - ({orgx, orgy} = {orgx: me.x, orgy: me.y}); + ({orgx, orgy} = { orgx: me.x, orgy: me.y }); } let monsterList = []; @@ -623,23 +623,42 @@ const Attack = { * @description clear all monsters based on classid arguments * @param {...number} ids * @returns {boolean} + * @todo + * - Should there be a range parameter for this? + * - Should we keep track of where we started from? */ clearClassids: function (...ids) { - let monster = Game.getMonster(); + // lets keep track of where we started from and move back when done + const { x, y } = me; + let cleared = false; - if (monster) { - let list = []; + for (let i = 0; i < 3; i++) { + let monster = Game.getMonster(); - do { - if (ids.includes(monster.classid) && monster.attackable) { - list.push(copyUnit(monster)); - } - } while (monster.getNext()); + if (monster) { + let list = []; - Attack.clearList(list); + do { + if (ids.includes(monster.classid) && monster.attackable) { + list.push(copyUnit(monster)); + } + } while (monster.getNext()); + + if (!list.length) { + break; + } + // if we cleared, return to our starting position + if (Attack.clearList(list)) { + Pather.moveTo(x, y); + cleared = true; + } + } else { + // if no monsters were found should that be a pass or fail? + return false; // fail for now + } } - return true; + return cleared; }, /** @@ -698,10 +717,11 @@ const Attack = { * @returns {boolean} */ clearList: function (mainArg, sortFunc, refresh) { - let i, target, monsterList; - let retry = 0; + /** @type {Monster[]} */ + let monsterList; + /** @type {{ gid: number, attacks: number }[]} */ let gidAttack = []; - let attackCount = 0; + let [retry, attackCount] = [0, 0]; switch (typeof mainArg) { case "function": @@ -721,20 +741,20 @@ const Attack = { !sortFunc && (sortFunc = this.sortMonsters); while (monsterList.length > 0 && attackCount < Config.MaxAttackCount) { + if (me.dead) return false; + if (refresh && attackCount > 0 && attackCount % refresh === 0) { monsterList = mainArg.call(); } - if (me.dead) return false; - monsterList.sort(sortFunc); - target = copyUnit(monsterList[0]); + let target = copyUnit(monsterList[0]); if (target.x !== undefined && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); Misc.townCheck(); - //me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); - + // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); + let i; let result = ClassAttack.doAttack(target, attackCount % 15 === 0); if (result) { diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index a3960a49b..4700205b5 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -7,6 +7,12 @@ (function (Common) { typeof Common !== "object" && (Common = {}); + /** + * @todo + * - keep track of seals opened and bosses killed to + * - improve targetting when using getBoss, sometimes we run to the location we want to attack from while running past the boss + * I see this mostly with viz, also have seen seis fail due to not being close enough, even though he spawned + */ Object.defineProperty(Common, "Diablo", { value: { diabloSpawned: false, @@ -15,6 +21,7 @@ done: false, waitForGlow: false, sealOrder: [], + openedSeals: [], vizLayout: -1, seisLayout: -1, infLayout: -1, @@ -88,6 +95,11 @@ return 2; }, + /** + * - VizLayout - 1 = "Y", 2 = "L" + * - SeisLayout - 1 = "2", 2 = "5" + * - InfLayout - 1 = "I", 2 = "J" + */ initLayout: function () { // 1 = "Y", 2 = "L" Common.Diablo.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); @@ -157,6 +169,10 @@ return true; }, + /** + * @param {number[] | string[]} sealOrder + * @param {boolean} openSeals + */ runSeals: function (sealOrder, openSeals = true) { print("seal order: " + sealOrder); Common.Diablo.sealOrder = sealOrder; @@ -171,6 +187,11 @@ sealOrder.forEach(seal => {seals[seal]();}); }, + /** + * Attempt casting telekinesis on seal to activate it + * @param {Unit} seal + * @returns {boolean} + */ tkSeal: function (seal) { if (!Skill.useTK(seal)) return false; @@ -185,6 +206,11 @@ return !!seal.mode; }, + /** + * Open one of diablos seals + * @param {number} classid + * @returns {boolean} + */ openSeal: function (classid) { let seal; let warn = Config.PublicMode && [sdk.objects.DiabloSealVizier, sdk.objects.DiabloSealSeis, sdk.objects.DiabloSealInfector].includes(classid) && Loader.scriptName() === "Diablo"; @@ -255,6 +281,10 @@ return (!!seal && seal.mode); }, + /** + * @param {boolean} openSeal + * @returns {boolean} + */ vizierSeal: function (openSeal = true) { print("Viz layout " + Common.Diablo.vizLayout); let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); @@ -284,6 +314,10 @@ return true; }, + /** + * @param {boolean} openSeal + * @returns {boolean} + */ seisSeal: function (openSeal = true) { print("Seis layout " + Common.Diablo.seisLayout); let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); @@ -299,13 +333,28 @@ if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealSeis)) throw new Error("Failed to open de Seis seal."); Common.Diablo.seisLayout === 1 ? Pather.moveTo(7798, 5194) : Pather.moveTo(7796, 5155); - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) throw new Error("Failed to kill de Seis"); + try { + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) throw new Error("Failed to kill de Seis"); + } catch (e) { + /** + * sometimes we fail just because we aren't in range, + * @todo better fix for this + */ + Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { + let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); + return seis && (seis.distance < 30 || seis.dead); + }}); + } Config.Diablo.SealLeader && say("out"); return true; }, + /** + * @param {boolean} openSeal + * @returns {boolean} + */ infectorSeal: function (openSeal = true) { Precast.doPrecast(true); print("Inf layout " + Common.Diablo.infLayout); From eb9c9e0af7887d6c5126ba00d4ccbd4e716ea3b1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 7 Feb 2023 16:51:18 -0500 Subject: [PATCH 024/758] Fix old references to Common.Questing - `Common.Questing.cain()` to `Common.Cain.run()` --- d2bs/kolbot/libs/scripts/Questing.js | 2 +- d2bs/kolbot/libs/scripts/Tristram.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js index 79210d3a6..664ad8160 100644 --- a/d2bs/kolbot/libs/scripts/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -55,7 +55,7 @@ function Questing () { log("starting cain"); Town.doChores(); - Common.Questing.cain(); + Common.Cain.run(); return true; }; diff --git a/d2bs/kolbot/libs/scripts/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js index 35abc95a8..9f70229c9 100644 --- a/d2bs/kolbot/libs/scripts/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -11,7 +11,7 @@ function Tristram () { // complete quest if its not complete if (!me.getQuest(sdk.quest.id.TheSearchForCain, 4) && !me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { include("core/Common/Cain.js"); - Common.Questing.cain(); + Common.Cain.run(); } MainLoop: From f75d5511652609cfa2a1deaae4697b5efc2627f6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 00:47:41 -0500 Subject: [PATCH 025/758] Fix broadcastIntent spam during chores - broadcastIntent is good, but don't use it during town chores as that intent does not need to be broadcasted to followers --- d2bs/kolbot/libs/core/Pather.js | 3 ++- d2bs/kolbot/libs/core/Town.js | 2 ++ d2bs/kolbot/threads/TownChicken.js | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index ef5313b48..e5ef98fd1 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1343,6 +1343,7 @@ const Pather = { return targetArea ? me.area === targetArea : me.area !== preArea; }, + allowBroadcast: true, /** * Meant for use as a MfLeader to let MfHelpers know where to go next * @param {number} targetArea - area id @@ -1375,7 +1376,7 @@ const Pather = { break; } - this.broadcastIntent(targetArea); + Pather.allowBroadcast && Pather.broadcastIntent(targetArea); console.info(true, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area)); let wpTick = getTickCount(); diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 54dd455ea..06acf0fe8 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -126,6 +126,7 @@ const Town = { if (!Misc.poll(() => me.gameReady && me.inTown, 2000, 250)) throw new Error("Failed to go to town for chores"); let preAct = me.act; + Pather.allowBroadcast = false; // Burst of speed while in town if (Skill.canUse(sdk.skills.BurstofSpeed) && !me.getState(sdk.states.BurstofSpeed)) { @@ -156,6 +157,7 @@ const Town = { delay(250); console.info(false, null, "doChores"); + Pather.allowBroadcast = true; return true; }, diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js index c1ddc67ef..ea9196b4c 100644 --- a/d2bs/kolbot/threads/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -15,7 +15,10 @@ includeSystemLibs(); function main() { let townCheck = false; - let scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; + const scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; + + // override broadCastIntent - shouldn't be called at all in this thread + Pather.broadcastIntent = () => null; this.pause = function () { for (let i = 0; i < scripts.length; i += 1) { From f339bf09569715b4cb4d6d2f618a7f9a5014d369 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 10:13:28 -0500 Subject: [PATCH 026/758] Update Me.js - wait until actionsequence has finished to switch weapons --- d2bs/kolbot/libs/core/Me.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 2fd0028f5..737923f3c 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -91,15 +91,15 @@ me.cancelUIFlags = function () { } }; +/** + * @param {number} slot - 0 (Primary) or 1 (Secondary) + * @returns {boolean} + */ me.switchWeapons = function (slot) { if (this.gametype === sdk.game.gametype.Classic || (slot !== undefined && this.weaponswitch === slot)) { return true; } - while (typeof me !== "object") { - delay(10); - } - while (!me.gameReady) { delay(25); } @@ -113,6 +113,11 @@ me.switchWeapons = function (slot) { for (let j = 10; --j && me.idle;) { delay(3); } + if (me.mode === sdk.player.mode.SkillActionSequence) { + while (me.mode === sdk.player.mode.SkillActionSequence) { + delay(3); + } + } i > 0 && delay(10); !switched && sendPacket(1, sdk.packets.send.SwapWeapon); // Swap weapons From 55355d6840ce67faec35ccfb8adf90d7dd04912e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 10:21:59 -0500 Subject: [PATCH 027/758] Fix follower game is finished spam - If leader was in crash delay the followers would repeatedly spam game is finished to the console --- d2bs/kolbot/D2BotFollow.dbj | 6 ++++-- d2bs/kolbot/D2BotLead.dbj | 14 +++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 70ae54505..c32911370 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -36,8 +36,8 @@ const JoinSettings = { // the only things we really need from these are their oog checks includeSystemLibs(); -let Controls = require("./modules/Control"); -let Overrides = require("./modules/Override"); +const Controls = require("./libs/modules/Control"); +const Overrides = require("./libs/modules/Override"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); @@ -73,6 +73,8 @@ function joinCheck (leader) { if (!Starter.joinInfo.inGame || (lastGame.length && lastGame.indexOf(Starter.joinInfo.gameName) === -1)) { D2Bot.printToConsole("Game is finished. Stopping join delay."); + Starter.gameInfo.gameName = ""; + Starter.gameInfo.gamePass = ""; return true; } diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 2a9af63a2..922d9033e 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -16,7 +16,7 @@ include("critical.js"); // required // the only things we really need from these are their oog checks includeSystemLibs(); -let Controls = require("./modules/Control"); +const Controls = require("./libs/modules/Control"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); @@ -328,6 +328,12 @@ function main () { delay(500); } + while (!Object.keys(Starter.profileInfo).length) { + D2Bot.getProfile(); + console.log("Getting Profile"); + delay(500); + } + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); if (Starter.gameInfo.error) { @@ -344,12 +350,6 @@ function main () { DataFile.updateStats("debugInfo", JSON.stringify({currScript: "none", area: "out of game"})); - while (!Object.keys(Starter.profileInfo).length) { - D2Bot.getProfile(); - print("Getting Profile"); - delay(500); - } - while (true) { // returns true before actually in game so we can't only use this check while (me.ingame) { From aa208415d4ec31eaf1cdd7b6c3c9aad6d356f7bc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 11:19:26 -0500 Subject: [PATCH 028/758] Update ItemHooks.js - Fix check is undefined, add Tancred's set to list --- .../kolbot/libs/manualplay/hooks/ItemHooks.js | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js index 53b247d4a..ce5b3b46a 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js @@ -175,7 +175,7 @@ const ItemHooks = { default: { let check = this.codeByIdAndQuality.get(item.classid); - code += (check.get(item.quality) || item.name); + code += ((check && check.get(item.quality)) || item.name); } break; @@ -277,7 +277,9 @@ const ItemHooks = { } }; -// have to be set after ItemHooks is created +/** + * Unique Items + */ ItemHooks.codeById.set(sdk.items.BattleAxe, "The Chieftain"); ItemHooks.codeById.set(sdk.items.Falchion, "Gleamscythe"); ItemHooks.codeById.set(sdk.items.BurntWand, "Suicide Branch"); @@ -342,12 +344,19 @@ ItemHooks.codeById.set(sdk.items.SmallCharm, "Annihilus"); ItemHooks.codeById.set(sdk.items.LargeCharm, "Hellfire Torch"); ItemHooks.codeById.set(sdk.items.GrandCharm, "Gheed's"); ItemHooks.codeById.set(sdk.items.Jewel, "Facet"); + +/** + * Misc Items + */ ItemHooks.codeById.set(sdk.items.quest.TokenofAbsolution, "ÿc8Token"); ItemHooks.codeById.set(sdk.items.quest.TwistedEssenceofSuffering, "ÿc3Ess-Of-Suffering"); ItemHooks.codeById.set(sdk.items.quest.ChargedEssenceofHatred, "ÿc7Ess-Of-Hatred"); ItemHooks.codeById.set(sdk.items.quest.BurningEssenceofTerror, "ÿc1Ess-Of-Terror"); ItemHooks.codeById.set(sdk.items.quest.FesteringEssenceofDestruction, "ÿc3Ess-Of-Destruction"); +/** + * Set/Unique Items + */ ItemHooks.addToCodeByClassIdAndQuality(sdk.items.JaggedStar, "Aldur's Wep", "Moonfall"); ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HuntersGuise, "Aldur's Helm"); ItemHooks.addToCodeByClassIdAndQuality(sdk.items.ShadowPlate, "Aldur's Armor", "Steel Carapace"); @@ -420,6 +429,18 @@ ItemHooks.addToCodeByClassIdAndQuality(sdk.items.DoubleAxe, "Beserker's Axe", "B ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SplintMail, "Beserker's Armor", "Iceblink"); ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Helm, "Beserker's Helm", "Coif of Glory"); +/** + * Tancred's + */ +ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BoneHelm, "Tancred's Skull", "Wormskull"); +ItemHooks.addToCodeByClassIdAndQuality(sdk.items.FullPlateMail, "Tancred's Spine", "Goldskin"); +ItemHooks.addToCodeByClassIdAndQuality(sdk.items.MilitaryPick, "Tancred's Crowbill", "Skull Splitter"); +ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Boots, "Tancred's Hobnails", "Hotspur"); + +/** + * @todo the rest of the sets/uniques + */ + ItemHooks.itemColorCode[sdk.items.quality.Magic] = { color: 0x97, code: "ÿc3" }; ItemHooks.itemColorCode[sdk.items.quality.Set] = { color: 0x84, code: "ÿc2" }; ItemHooks.itemColorCode[sdk.items.quality.Rare] = { color: 0x6F, code: "ÿc9" }; From 927758be2a6120466d849ac0721f25a63c07a511 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 11:20:31 -0500 Subject: [PATCH 029/758] Update Pather.js - Add check for meph portal, If we haven't completed travincal we can't take it --- d2bs/kolbot/libs/core/Pather.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index e5ef98fd1..f779f0fde 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1601,6 +1601,7 @@ const Pather = { me.cancelUIFlags(); + let preArea = me.area; for (let i = 0; i < 10; i += 1) { @@ -1610,6 +1611,10 @@ const Pather = { let portal = unit ? copyUnit(unit) : this.getPortal(targetArea, owner); if (portal) { + if (portal.objtype === sdk.areas.DuranceofHateLvl3 && portal.getParent() !== me.name + && !Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { + throw new Error("Cannot access meph through someone elses portal without first completing Travincal"); + } let redPortal = portal.classid === sdk.objects.RedPortal; if (portal.area === me.area) { From 390c8004a9a447acadce9f437ab71ea3732804a6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 12:03:24 -0500 Subject: [PATCH 030/758] Temp solution to complaints of teleswitch not working - Check during doCast and doAttack whether we are on our correct slot, there is still a possibility to fail if after the check we have to move again --- d2bs/kolbot/libs/core/Attacks/Amazon.js | 4 +++- d2bs/kolbot/libs/core/Attacks/Assassin.js | 2 ++ d2bs/kolbot/libs/core/Attacks/Barbarian.js | 3 +++ d2bs/kolbot/libs/core/Attacks/Druid.js | 2 ++ d2bs/kolbot/libs/core/Attacks/Necromancer.js | 3 ++- d2bs/kolbot/libs/core/Attacks/Paladin.js | 2 ++ d2bs/kolbot/libs/core/Attacks/Sorceress.js | 2 ++ d2bs/kolbot/libs/core/Me.js | 5 +++++ d2bs/kolbot/sdk/globals.d.ts | 3 ++- 9 files changed, 23 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 325567d12..ab4673df8 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -10,7 +10,7 @@ const ClassAttack = { lightFuryTick: 0, 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; @@ -49,6 +49,7 @@ const ClassAttack = { doAttack: function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; let needRepair = Town.needRepair(); @@ -159,6 +160,7 @@ const ClassAttack = { doCast: function (unit, timedSkill = -1, untimedSkill = -1) { // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + Config.TeleSwitch && me.switchToPrimary(); // Arrow/bolt check if (this.bowCheck) { diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 2d216a339..c0f32d6b6 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -11,6 +11,7 @@ const ClassAttack = { doAttack: function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; if (Config.MercWatch && Town.needMerc()) { @@ -147,6 +148,7 @@ const ClassAttack = { if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let walk; let classid = unit.classid; diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index d5fece86c..6ade02a83 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -19,6 +19,7 @@ const ClassAttack = { */ doAttack: function (unit, preattack = false) { if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; let needRepair = Town.needRepair(); @@ -93,6 +94,7 @@ const ClassAttack = { if (attackSkill < 0) return Attack.Result.CANTATTACK; // check if unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); switch (attackSkill) { case sdk.skills.Whirlwind: @@ -224,6 +226,7 @@ const ClassAttack = { } 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)); } } diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index 2373c058f..c0d5ba469 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -8,6 +8,7 @@ const ClassAttack = { doAttack: function (unit, preattack = false) { if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; if (Config.MercWatch && Town.needMerc()) { @@ -122,6 +123,7 @@ const ClassAttack = { if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let walk; let classid = unit.classid; diff --git a/d2bs/kolbot/libs/core/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js index 3be9b1f23..f1dafedab 100644 --- a/d2bs/kolbot/libs/core/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -119,7 +119,7 @@ const ClassAttack = { */ doAttack: function (unit, preattack = false) { if (!unit || unit.dead) return Attack.Result.SUCCESS; - + Config.TeleSwitch && me.switchToPrimary(); let mercRevive = 0; let gid = unit.gid; @@ -281,6 +281,7 @@ const ClassAttack = { if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let walk; let classid = unit.classid; diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 824b25d13..56b7e8bbd 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -15,6 +15,7 @@ const ClassAttack = { */ doAttack: function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; if (Config.MercWatch && Town.needMerc()) { @@ -155,6 +156,7 @@ const ClassAttack = { if (attackSkill < 0) return Attack.Result.CANTATTACK; // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); switch (attackSkill) { case sdk.skills.BlessedHammer: diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 319b03e9b..5f4164531 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -46,6 +46,7 @@ const ClassAttack = { doAttack: function (unit, preattack = false) { if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; if (Config.MercWatch && Town.needMerc()) { @@ -177,6 +178,7 @@ const ClassAttack = { if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); let walk, noMana = false; let classid = unit.classid; diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 737923f3c..6d0eb9bbd 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -354,6 +354,11 @@ me.fieldID = function () { return true; }; +me.switchToPrimary = function () { + if (me.classic) return true; + return me.switchWeapons(Attack.getPrimarySlot()); +}; + Object.defineProperties(me, { maxNearMonsters: { get: function () { diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 8e73106ac..d1a5dc2b7 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -503,6 +503,7 @@ declare global { inArea(area: number): boolean; switchWeapons(slot: 0 | 1): void; + switchToPrimary(): boolean; checkItem(itemInfo: { classid?: number; itemtype?: number; @@ -565,7 +566,7 @@ declare global { send(): void; } - function getScript(name?: string): Script | false + function getScript(name?: string | boolean): Script | false function getScripts(): Script | false class Room { From 04e61f6080279e8af0921bd7563d7e27797851e3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:51:15 -0500 Subject: [PATCH 031/758] Update Town.js - Fix failure to precast or use some skills because we initialized our skill data before grabbing our corpse (in the cases where we died the previous game) --- d2bs/kolbot/libs/core/Town.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 06acf0fe8..08549f223 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1722,6 +1722,8 @@ const Town = { } me.classic && this.checkShard(); + // re-init skills since we started off without our body + Skill.init(); return true; }, From 9a26be05eeee77081936e0f277e4e17f19cb3863 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 01:46:20 -0500 Subject: [PATCH 032/758] Fix follower request game spam - There is no need to spam request game when the leader is delaying for some reason e.g create game delay or ftj, instead now leader passes on how long they are delaying for and follower will wait until that time before checking if game is ready --- d2bs/kolbot/D2BotFollow.dbj | 3 +++ d2bs/kolbot/libs/OOG.js | 24 +++++++++++++++++++----- d2bs/kolbot/libs/oog/D2Bot.js | 19 ++++++++++++++----- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index c32911370..49a3c2b32 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -212,6 +212,9 @@ function locationAction (location) { } if (!Starter.joinInfo.inGame) { + if (Starter.joinInfo.delay) { + ControlAction.timeoutDelay("Leader Delay", Starter.joinInfo.delay); + } continue; } diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index f5c2ed791..5ce3678b8 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -33,12 +33,14 @@ includeIfNotIncluded("oog/D2Bot.js"); // required timeoutDelay: function (text, time, stopfunc, arg) { let currTime = 0; let endTime = getTickCount() + time; + Starter.delay = time; while (getTickCount() < endTime) { if (typeof stopfunc === "function" && stopfunc(arg)) { break; } + Starter.delay = Math.max(0, (endTime - getTickCount())); if (currTime !== Math.floor((endTime - getTickCount()) / 1000)) { currTime = Math.floor((endTime - getTickCount()) / 1000); @@ -47,6 +49,8 @@ includeIfNotIncluded("oog/D2Bot.js"); // required delay(10); } + + Starter.delay = 0; }, click: function (type, x, y, xsize, ysize, targetx, targety) { @@ -938,6 +942,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required firstLogin: true, firstRun: false, isUp: "no", + delay: 0, loginRetry: 0, deadCheck: false, chatActionsDone: false, @@ -1045,6 +1050,11 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } }, + /** + * Handle copy data event + * @param {number} mode + * @param {object | string} msg + */ receiveCopyData: function (mode, msg) { let obj; @@ -1053,34 +1063,38 @@ includeIfNotIncluded("oog/D2Bot.js"); // required switch (mode) { case 1: // JoinInfo obj = JSON.parse(msg); + console.debug("Recieved Join Info :: ", obj); Object.assign(Starter.joinInfo, obj); break; case 2: // Game info - print("Recieved Game Info"); obj = JSON.parse(msg); + console.debug("Recieved Game Info :: "); Object.assign(Starter.gameInfo, obj); break; case 3: // Game request // in case someone is using a lightweight entry like blank/map to play manually and these aren't included if (typeof AutoMule !== "undefined") { - // Don't let others join mule/torch/key/gold drop game + // Don't let others join mule/torch/key/gold drop game if (AutoMule.inGame || Gambling.inGame || TorchSystem.inGame || CraftingSystem.inGame) { break; } } - if (Object.keys(Starter.gameInfo).length) { + if (Starter.gameInfo.hasOwnProperty("gameName")) { obj = JSON.parse(msg); + console.debug("Recieved Game Request :: ", obj.profile); if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { me.gameReady && D2Bot.joinMe(obj.profile, me.gameserverip.toString(), "", "", Starter.isUp); } else { if (me.gameReady) { - D2Bot.joinMe(obj.profile, me.gamename.toLowerCase(), "", me.gamepassword.toLowerCase(), Starter.isUp); + D2Bot.joinMe(obj.profile, me.gamename, "", me.gamepassword, Starter.isUp); } else { - D2Bot.joinMe(obj.profile, Starter.gameInfo.gameName.toLowerCase(), Starter.gameCount, Starter.gameInfo.gamePass.toLowerCase(), Starter.isUp); + // If we haven't made it to the lobby yet but are already getting game requests, stop the spam by telling followers to delay + let delay = (Starter.delay === 0 && !me.ingame && getLocation() !== sdk.game.locations.CreateGame) ? 3000 : Starter.delay; + D2Bot.joinMe(obj.profile, Starter.gameInfo.gameName, Starter.gameCount, Starter.gameInfo.gamePass, Starter.isUp, delay); } } } diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index 5c8e6054a..a367def27 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -155,12 +155,21 @@ includeIfNotIncluded("oog/DataFile.js"); this.sendWinMsg(0x001c, 0x0000); }, - // Profile to profile communication - joinMe: function (profile, gameName, gameCount, gamePass, isUp) { + /** + * Profile to profile communication + * @param {string} profile + * @param {string} gameName + * @param {number} gameCount + * @param {string} gamePass + * @param {string} isUp + * @param {number} delay + */ + joinMe: function (profile, gameName, gameCount, gamePass, isUp, delay) { let obj = { - gameName: gameName + gameCount, - gamePass: gamePass, - inGame: isUp === "yes" + gameName: (gameName + gameCount).toLowerCase(), + gamePass: (gamePass).toLowerCase(), + inGame: isUp === "yes", + delay: (delay || 0), }; sendCopyData(null, profile, 1, JSON.stringify(obj)); From 9628ffbc876d9611577359a80309e956131152c1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:27:19 -0500 Subject: [PATCH 033/758] Don't spam mule master report request - add delay after requesting report --- d2bs/kolbot/D2BotFollow.dbj | 17 ++++++++++++++++- d2bs/kolbot/D2BotMule.dbj | 8 ++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 49a3c2b32..51b6fb416 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -193,7 +193,17 @@ function locationAction (location) { D2Bot.requestGame(leader[j]); delay(100); - if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "" && (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending")) { + if (!Starter.joinInfo.hasOwnProperty("gameName") || Starter.joinInfo.gameName === "") { + delay(500); + continue; + } + + /** + * @todo handle rejoin, need to keep track of game averages and when requesting game from a leader who's game we left get the current game time + * and see if there is x amount of time left that makes it worth it vs waiting for next. + */ + + if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { Controls.JoinGameName.setText(Starter.joinInfo.gameName); Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); @@ -245,6 +255,11 @@ function locationAction (location) { Starter.locationTimeout(15000, location); break JoinLoop2; + } else { + // for now, if leader is in game and it's the last game we were in. delay to prevent copyData spam + if (lastGame.includes(Starter.joinInfo.gameName)) { + delay((Starter.joinInfo.inGame ? 5000 : 2000)); + } } } } diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index c2333c625..3aec2a509 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -407,12 +407,6 @@ const Mule = { } const getItems = () => getUnits(sdk.unittype.Item).filter(i => i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType)); - console.debug(masterStatus, Starter.firstLogin); - if (Mule.foreverAlone()) { - if (!Misc.poll(() => !Mule.foreverAlone() && getItems().length > 0, Time.seconds(2), 500)) { - return rval; - } - } while (me.gameReady) { if (masterStatus.status === "done" || Mule.continuousMule || override) { @@ -472,6 +466,7 @@ const Mule = { } else { if (!Mule.continuousMule) { sendCopyData(null, master, 10, JSON.stringify({ status: "report" })); + Misc.poll(() => masterStatus.status === "done", Time.seconds(5), 50); } else { if (getTickCount() - waitTick > Time.minutes(10)) { break; @@ -525,6 +520,7 @@ new Overrides.Override (Starter, Starter.receiveCopyData, function (orignal, mod break; case 12: // get master's status masterStatus = JSON.parse(msg); + console.debug(msg); break; default: From ee2108a3b19549e1a1d4c31aebffe19c5a37deb5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 10:50:15 -0500 Subject: [PATCH 034/758] Update OrgTorch.js - couple typos --- d2bs/kolbot/libs/scripts/OrgTorch.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index c04093d11..c7892f129 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -141,7 +141,7 @@ function OrgTorch () { return (brains++); case sdk.items.quest.BaalsEye: return (eyes++); - default: return; + default: return 0; } }); @@ -423,7 +423,7 @@ function OrgTorch () { /** * @param {ObjectUnit} portal */ - runEvent = function (portal) { + const runEvent = function (portal) { if (portal) { if (Config.OrgTorch.PreGame.Antidote.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Antidote.Drink > 0) { Town.buyPots(Config.OrgTorch.PreGame.Antidote.Drink, "Antidote", true, true); @@ -486,7 +486,7 @@ function OrgTorch () { return (brains++); case sdk.items.quest.BaalsEye: return (eyes++); - default: return; + default: return 0; } }); From 1be50d70e87c8932ccb467549cd1c11513e0ea2b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 10:57:41 -0500 Subject: [PATCH 035/758] Update Skill.js - Add recheck cta into Skill.init call --- d2bs/kolbot/libs/core/Skill.js | 58 ++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 523b2366e..6557f0d52 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -92,7 +92,9 @@ }, skills: { - /** @type {Object.} */ + /** + * @type {Object.} + */ all: {}, initialized: false, @@ -131,6 +133,8 @@ !Skill.skills.initialized ? Skill.skills.init() : Skill.skills.reset(); // reset mana values Skill.manaCostList = {}; + // redo cta check + Precast.checkCTA(); switch (me.classid) { case sdk.player.class.Amazon: @@ -192,6 +196,10 @@ } }, + /** + * @param {number} skillId + * @returns {boolean} + */ canUse: function (skillId = -1) { try { if (skillId === -1) return false; @@ -204,6 +212,10 @@ } }, + /** + * @param {number} skillId + * @returns {number} + */ getDuration: function (skillId = -1) { return Time.seconds((() => { switch (skillId) { @@ -246,9 +258,11 @@ })()); }, + /** + * @param {number} skillId + * @returns {number} + */ getMaxSummonCount: function (skillId) { - let skillNum = 0; - switch (skillId) { case sdk.skills.Raven: return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 5); @@ -258,7 +272,7 @@ return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 3); case sdk.skills.RaiseSkeleton: case sdk.skills.RaiseSkeletalMage: - skillNum = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); + let skillNum = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); case sdk.skills.Revive: return me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints); @@ -276,11 +290,15 @@ case sdk.skills.FireGolem: case sdk.skills.Valkyrie: return 1; + default: + return 0; } - - return 0; }, + /** + * @param {number} skillId + * @returns {number} + */ getRange: function (skillId) { switch (skillId) { case sdk.skills.Attack: @@ -410,6 +428,10 @@ return !!this.usePvpRange ? 30 : 20; }, + /** + * @param {number} skillId + * @returns {number} + */ getHand: function (skillId) { switch (skillId) { case sdk.skills.MagicArrow: @@ -499,7 +521,11 @@ return sdk.skills.hand.Right; }, - // Get mana cost of the skill (mBot) + /** + * Get mana cost of the skill (mBot) + * @param {number} skillId + * @returns {number} + */ getManaCost: function (skillId) { if (skillId < sdk.skills.MagicArrow) return 0; if (this.manaCostList.hasOwnProperty(skillId)) return this.manaCostList[skillId]; @@ -531,7 +557,11 @@ return (me.setSkill(skillId, hand, item)); }, - // Timed skills + /** + * Timed skills + * @param {number} skillId + * @returns {boolean} + */ isTimed: function (skillId) { return [ sdk.skills.PoisonJavelin, sdk.skills.PlagueJavelin, sdk.skills.ImmolationArrow, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, @@ -541,7 +571,11 @@ ].includes(skillId); }, - // Skills that cn be cast in town + /** + * Skills that cn be cast in town + * @param {number} skillId + * @returns {boolean} + */ townSkill: function (skillId = -1) { return [ sdk.skills.Valkyrie, sdk.skills.FrozenArmor, sdk.skills.Telekinesis, sdk.skills.ShiverArmor, sdk.skills.Enchant, sdk.skills.ThunderStorm, sdk.skills.EnergyShield, sdk.skills.ChillingArmor, @@ -551,7 +585,11 @@ ].includes(skillId); }, - // Wereform skill check + /** + * Wereform skill check + * @param {number} skillId + * @returns {number} + */ wereFormCheck: function (skillId) { // we don't even have the skills to transform or we aren't transformed - add handler for wereform given by an item that is on switch if (!Skill.canUse(sdk.skills.Werewolf) && !Skill.canUse(sdk.skills.Werebear)) return true; From 8c6937dd41210d7565fc994438b51c267b3ed6f8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 14:06:55 -0500 Subject: [PATCH 036/758] Update ControlBot.js - Mostly clean up, included ShitList.js since it's used here - Added re-enchant ability to autochant by tracking the last time a player was chanted vs the duration of our chant --- d2bs/kolbot/libs/scripts/ControlBot.js | 148 ++++++++++++++++--------- 1 file changed, 98 insertions(+), 50 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index ab15c8222..e62fb2ffc 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -12,18 +12,18 @@ function ControlBot() { /** * @type {Object.} */ - this.cmdNicks = {}; + const cmdNicks = {}; /** * @type {Object.} */ - this.wpNicks = {}; + const wpNicks = {}; let command, nick; let shitList = []; - let greet = []; + const greet = []; let controlCommands = ["help", "timeleft", "cows", "wps", "chant", "bo"]; - let commandDesc = { + const commandDesc = { "help": "Display commands", "timeleft": "Remaining time left for this game", "cows": "Open cow level", @@ -69,7 +69,11 @@ function ControlBot() { } } - this.enchant = function (nick) { + /** + * @param {string} nick + * @returns {boolean} + */ + const enchant = function (nick) { if (!Config.ControlBot.Chant.Enchant) return false; if (!Misc.inMyParty(nick)) { @@ -135,7 +139,11 @@ function ControlBot() { return true; }; - this.bo = function (nick) { + /** + * @param {string} nick + * @returns {boolean} + */ + const bo = function (nick) { if (!Config.ControlBot.Bo) return false; if (!Misc.inMyParty(nick)) { @@ -187,7 +195,13 @@ function ControlBot() { return true; }; - this.autoChant = function () { + /** + * @type {Map= chantDuration - Time.minutes(1))) { + Packet.enchant(unit); + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + chantList.set(unit.name, { lastChant: getTickCount() }); + } + } } } while (unit.getNext()); } @@ -209,7 +228,10 @@ function ControlBot() { do { if (unit.getParent() && chanted.includes(unit.getParent().name) && !unit.getState(sdk.states.Enchant) && unit.distance <= 40) { Packet.enchant(unit); - delay(500); + // not going to re-enchant the minions for now though, will think on how best to handle that later + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + } } } while (unit.getNext()); } @@ -217,7 +239,7 @@ function ControlBot() { return true; }; - this.getLeg = function () { + const getLeg = function () { if (me.getItem(sdk.quest.item.WirtsLeg)) { return me.getItem(sdk.quest.item.WirtsLeg); } @@ -278,7 +300,7 @@ function ControlBot() { return false; }; - this.getTome = function () { + const getTome = function () { let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); if (tpTome.length < 2) { @@ -313,7 +335,11 @@ function ControlBot() { return tpTome.last(); }; - this.openPortal = function (nick) { + /** + * @param {string} nick + * @returns {boolean} + */ + const openPortal = function (nick) { if (!Config.ControlBot.Cows.MakeCows) return false; try { if (!Misc.inMyParty(nick)) throw new Error("Accept party invite, noob."); @@ -329,10 +355,10 @@ function ControlBot() { return false; } - let leg = this.getLeg(); + let leg = getLeg(); if (!leg) return false; - let tome = this.getTome(); + let tome = getTome(); if (!tome) return false; if (!Town.openStash() || !Cubing.emptyCube() || !Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome) || !Cubing.openCube()) { @@ -355,13 +381,17 @@ function ControlBot() { return false; }; - this.getWpNick = function (nick) { - if (this.wpNicks.hasOwnProperty(nick)) { - if (this.wpNicks[nick].requests > 4) { + /** + * @param {string} nick + * @returns {string | boolean} + */ + const getWpNick = function (nick) { + if (wpNicks.hasOwnProperty(nick)) { + if (wpNicks[nick].requests > 4) { return "maxrequests"; } - if (getTickCount() - this.wpNicks[nick].timer < 60000) { + if (getTickCount() - wpNicks[nick].timer < 60000) { return "mintime"; } @@ -371,11 +401,19 @@ function ControlBot() { return false; }; - this.addWpNick = function (nick) { - this.wpNicks[nick] = {timer: getTickCount(), requests: 0}; + /** + * @param {string} nick + * @returns {void} + */ + const addWpNick = function (nick) { + wpNicks[nick] = { timer: getTickCount(), requests: 0 }; }; - this.giveWps = function (nick) { + /** + * @param {string} nick + * @returns {boolean} + */ + const giveWps = function (nick) { if (!Config.ControlBot.Wps.GiveWps) return false; if (!Misc.inMyParty(nick)) { say("Accept party invite, noob."); @@ -383,7 +421,7 @@ function ControlBot() { return false; } - switch (this.getWpNick(nick)) { + switch (getWpNick(nick)) { case "maxrequests": say(nick + ", you have spent all your waypoint requests for this game."); @@ -393,7 +431,7 @@ function ControlBot() { return false; case false: - this.addWpNick(nick); + addWpNick(nick); break; } @@ -421,7 +459,7 @@ function ControlBot() { let wpList = wps[act]; for (let i = 0; i < wpList.length; i++) { - if (this.checkHostiles()) { + if (checkHostiles()) { break; } @@ -455,13 +493,13 @@ function ControlBot() { Town.goToTown(1); Town.move("portalspot"); - this.wpNicks[nick].requests += 1; - this.wpNicks[nick].timer = getTickCount(); + wpNicks[nick].requests += 1; + wpNicks[nick].timer = getTickCount(); return true; }; - this.checkHostiles = function () { + const checkHostiles = function () { let rval = false; let party = getParty(); @@ -480,7 +518,11 @@ function ControlBot() { return rval; }; - this.floodCheck = function (command) { + /** + * @param {string} command + * @returns {boolean} + */ + const floodCheck = function (command) { if (!command || command.length < 2) return false; let [cmd, nick] = command; @@ -489,40 +531,45 @@ function ControlBot() { // ignore messages not related to our commands if (controlCommands.indexOf(cmd.toLowerCase()) === -1) return false; - if (!this.cmdNicks.hasOwnProperty(nick)) { - this.cmdNicks[nick] = { + if (!cmdNicks.hasOwnProperty(nick)) { + cmdNicks[nick] = { firstCmd: getTickCount(), commands: 0, ignored: false }; } - if (this.cmdNicks[nick].ignored) { - if (getTickCount() - this.cmdNicks[nick].ignored < 60000) { + if (cmdNicks[nick].ignored) { + if (getTickCount() - cmdNicks[nick].ignored < 60000) { return true; // ignore flooder } // unignore flooder - this.cmdNicks[nick].ignored = false; - this.cmdNicks[nick].commands = 0; + cmdNicks[nick].ignored = false; + cmdNicks[nick].commands = 0; } - this.cmdNicks[nick].commands += 1; + cmdNicks[nick].commands += 1; - if (getTickCount() - this.cmdNicks[nick].firstCmd < 10000) { - if (this.cmdNicks[nick].commands > 5) { - this.cmdNicks[nick].ignored = getTickCount(); + if (getTickCount() - cmdNicks[nick].firstCmd < 10000) { + if (cmdNicks[nick].commands > 5) { + cmdNicks[nick].ignored = getTickCount(); say(nick + ", you are being ignored for 60 seconds because of flooding."); } } else { - this.cmdNicks[nick].firstCmd = getTickCount(); - this.cmdNicks[nick].commands = 0; + cmdNicks[nick].firstCmd = getTickCount(); + cmdNicks[nick].commands = 0; } return false; }; + /** + * @param {string} nick + * @param {string} msg + * @returns {boolean} + */ function chatEvent(nick, msg) { if (shitList.includes(nick)) { say("No commands for the shitlisted."); @@ -545,6 +592,7 @@ function ControlBot() { } // START + include("oog/ShitList.js"); Config.ShitList && (shitList = ShitList.read()); try { @@ -567,8 +615,8 @@ function ControlBot() { spot.distance > 10 && Pather.moveTo(spot.x, spot.y); - if (command && !this.floodCheck(command)) { - let hostile = this.checkHostiles(); + if (command && !floodCheck(command)) { + let hostile = checkHostiles(); switch (command[0].toLowerCase()) { case "help": @@ -590,7 +638,7 @@ function ControlBot() { break; case "chant": - this.enchant(command[1]); + enchant(command[1]); break; case "cows": @@ -600,7 +648,7 @@ function ControlBot() { break; } - this.openPortal(command[1]); + openPortal(command[1]); me.cancel(); break; @@ -611,7 +659,7 @@ function ControlBot() { break; } - this.giveWps(command[1]); + giveWps(command[1]); break; case "bo": @@ -621,7 +669,7 @@ function ControlBot() { break; } - this.bo(command[1]); + bo(command[1]); break; } @@ -630,7 +678,7 @@ function ControlBot() { command = ""; me.act > 1 && Town.goToTown(1); - Config.ControlBot.Chant.AutoEnchant && this.autoChant(); + Config.ControlBot.Chant.AutoEnchant && autoChant(); if (getTickCount() - startTime >= Time.minutes(Config.ControlBot.GameLength)) { say((Config.ControlBot.EndMessage ? Config.ControlBot.EndMessage : "Bye")); From 58b3387cd6852da183206c432e7219fede89d01e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 18:09:07 -0500 Subject: [PATCH 037/758] Some typo fixes - includeCoreLibs into areawatcher - `i.isInInventory` was supposed to be `i.isInStash` - increased delay and end default if no longer if while loop ends --- d2bs/kolbot/default.dbj | 5 +++-- d2bs/kolbot/libs/scripts/OrgTorch.js | 2 +- d2bs/kolbot/threads/AreaWatcher.js | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 171b66f2f..f28e9daf9 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -59,9 +59,10 @@ function main () { FileTools.exists("libs/ItemDB.js") && include("ItemDB.js"); load("threads/AreaWatcher.js"); - while (true) { - delay(1000); + while (me.ingame) { + delay(10000); } + return true; } let sojPause; diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index c7892f129..8229e206e 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -471,7 +471,7 @@ function OrgTorch () { let [brains, eyes, horns] = [0, 0, 0]; me.getItemsEx() - .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) + .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) .forEach(i => { switch (i.classid) { case sdk.items.quest.KeyofTerror: diff --git a/d2bs/kolbot/threads/AreaWatcher.js b/d2bs/kolbot/threads/AreaWatcher.js index a3296955f..e66d9be86 100644 --- a/d2bs/kolbot/threads/AreaWatcher.js +++ b/d2bs/kolbot/threads/AreaWatcher.js @@ -6,7 +6,11 @@ */ js_strict(true); include("critical.js"); -include("core/Util.js"); +includeCoreLibs(); + +/** + * @todo redo this, feels messy + */ function main() { let _default = getScript("default.dbj"); From cc9a99858894e6422237d9fbcedb4ee22dc67b92 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 9 Feb 2023 19:28:57 -0500 Subject: [PATCH 038/758] Update Cubing.js - small fix to low level bots calling getCube due to reuse of config file --- d2bs/kolbot/libs/core/Cubing.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 5e9b6a954..8cceb7a3a 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -151,9 +151,9 @@ const Cubing = { getCube: function () { // Don't activate from townchicken - if (getScript(true).name === "tools\\townchicken.js") { - return false; - } + if (getScript(true).name === "tools\\townchicken.js") return false; + // Can't get the cube if we can't access the act + if (!Pather.accessToAct(2)) return false; console.log("Getting cube"); me.overhead("Getting cube"); From 53680b06e7d3cc1fb33de440f0f009b4bf610e68 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 10 Feb 2023 12:21:42 -0500 Subject: [PATCH 039/758] Update ShopBot.js - Fix npc being undefined --- d2bs/kolbot/libs/scripts/ShopBot.js | 57 ++++++++++++++++++----------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js index 6deae7a0b..1b1282cc4 100644 --- a/d2bs/kolbot/libs/scripts/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -6,7 +6,7 @@ */ function ShopBot() { - let overlayText = { + const overlayText = { title: new Text("kolbot shopbot", 50, 245, 2, 1), cycles: new Text("Cycles in last minute:", 50, 260, 2, 1), frequency: new Text("Valid item frequency:", 50, 275, 2, 1), @@ -19,12 +19,12 @@ function ShopBot() { let totalCycles = 0; Pather.teleport = false; - this.pickEntries = []; - this.npcs = {}; + const pickEntries = []; + const npcs = {}; - this.buildPickList = function () { - let nipfile, filepath = "pickit/shopbot.nip", - filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); + const buildPickList = function () { + let nipfile, filepath = "pickit/shopbot.nip"; + let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); if (!FileTools.exists(filepath)) { Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); @@ -50,13 +50,18 @@ function ShopBot() { }; let line = NTIP.ParseLineInt(lines[i], info); - line && this.pickEntries.push(line); + line && pickEntries.push(line); } return true; }; - this.openMenu = function (npc) { + /** + * Interact and open the menu of an NPC unit + * @param {NPCUnit} npc + * @returns {boolean} + */ + const openMenu = function (npc) { if (!npc || npc.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); let interactedNPC = getInteractedNPC(); @@ -92,10 +97,15 @@ function ShopBot() { return false; }; - this.shopItems = function (npc, menuId) { + /** + * @param {NPCUnit} npc + * @param {number} menuId + * @returns {boolean} + */ + const shopItems = function (npc, menuId) { let bought; - if (!Storage.Inventory.CanFit({sizex: 2, sizey: 4}) && AutoMule.getMuleItems().length > 0) { + if (!Storage.Inventory.CanFit({ sizex: 2, sizey: 4 }) && AutoMule.getMuleItems().length > 0) { D2Bot.printToConsole("Mule triggered"); scriptBroadcast("mule"); scriptBroadcast("quit"); @@ -128,7 +138,7 @@ function ShopBot() { for (let i = 0; i < items.length; i += 1) { if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && - NTIP.CheckItem(items[i], this.pickEntries) + NTIP.CheckItem(items[i], pickEntries) ) { beep(); D2Bot.printToConsole("Match found!", sdk.colors.D2Bot.DarkGold); @@ -152,7 +162,11 @@ function ShopBot() { return true; }; - this.shopAtNPC = function (name) { + /** + * @param {string} name + * @returns {boolean} + */ + const shopAtNPC = function (name) { let wp, menuId = "Shop"; switch (name) { @@ -199,20 +213,19 @@ function ShopBot() { throw new Error("Invalid NPC"); } - if (!Pather.useWaypoint(wp)) return false; + if (!me.inArea(wp) && !Pather.useWaypoint(wp)) return false; - let npc = this.npcs[name] || Game.getNPC(name); + let npc = npcs[name] || Game.getNPC(name); - if (!npc || npc.distance > 5) { - Town.move(name); - npc = Game.getNPC(name); + if (!npc || npc.type !== sdk.unittype.NPC || npc.distance > 5) { + npc = Town.npcInteract(name); } if (!npc) return false; - !this.npcs[name] && (this.npcs[name] = copyUnit(npc)); + !npcs[name] && (npcs[name] = copyUnit(npc)); Config.ShopBot.CycleDelay && delay(Config.ShopBot.CycleDelay); - this.openMenu(npc) && this.shopItems(npc, menuId); + openMenu(npc) && shopItems(npc, menuId); return true; }; @@ -238,8 +251,8 @@ function ShopBot() { if (Config.ShopBot.MinGold && me.gold < Config.ShopBot.MinGold) return true; - this.buildPickList(); - print("Shopbot: Pickit entries: " + this.pickEntries.length); + buildPickList(); + print("Shopbot: Pickit entries: " + pickEntries.length); Town.doChores(); tickCount = getTickCount(); @@ -253,7 +266,7 @@ function ShopBot() { } for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { - this.shopAtNPC(Config.ShopBot.ShopNPC[i]); + shopAtNPC(Config.ShopBot.ShopNPC[i]); } if (me.inTown) { From 47a29a53f1d1ed523bf0b4f9457dab5ec7007e7e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 11 Feb 2023 11:10:42 -0500 Subject: [PATCH 040/758] Update CopyData.js - removed use of globalThis --- d2bs/kolbot/libs/modules/CopyData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/CopyData.js b/d2bs/kolbot/libs/modules/CopyData.js index a3955248e..c62be3178 100644 --- a/d2bs/kolbot/libs/modules/CopyData.js +++ b/d2bs/kolbot/libs/modules/CopyData.js @@ -15,7 +15,7 @@ root.CopyData = factory(); console.trace(); } -}(globalThis, function() { +}([].filter.constructor("return this")(), function() { /** * @class * @classdesc A class for creating and sending copy data packets. From b24c0a26c1a851be4d96ad1eac23e917f0650668 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:27:53 -0500 Subject: [PATCH 041/758] Update FileAction.js - incremental delay in case of failure to read/write/append file --- d2bs/kolbot/libs/oog/FileAction.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/oog/FileAction.js b/d2bs/kolbot/libs/oog/FileAction.js index c2982c07d..5fb2f749f 100644 --- a/d2bs/kolbot/libs/oog/FileAction.js +++ b/d2bs/kolbot/libs/oog/FileAction.js @@ -25,10 +25,11 @@ const FileAction = { if (contents) return contents; } catch (e) { - console.error(e); + // console.error(e, path); } - delay(100); + // incremental delay + delay(100 + ((i % 5) * 100)); } return contents; @@ -45,10 +46,10 @@ const FileAction = { break; } catch (e) { - console.error(e); + // console.error(e, path); } - delay(100); + delay(100 + ((i % 5) * 100)); } return true; @@ -65,10 +66,10 @@ const FileAction = { break; } catch (e) { - console.error(e); + // console.error(e, path); } - delay(100); + delay(100 + ((i % 5) * 100)); } return true; @@ -87,7 +88,7 @@ const FileAction = { return JSON.parse(contents); } } catch (e) { - console.error(e); + console.error(e, path); } return contents; From 9d92659c61b62b20e3612809044a1a163f5c9a9f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:41:42 -0500 Subject: [PATCH 042/758] Update Precast.js - add hardcore specific check for casting oak in town, hardcore characters generally have a higher chicken% and casting oak before Bo can cause a false chicken --- d2bs/kolbot/libs/core/Precast.js | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index ac239b973..85fbfa500 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -46,6 +46,13 @@ const Precast = new function () { }, }; + /** + * Easier Shout/Bo/Bc casting with state checks to ensure it was casted + * @param {number} skillId + * @param {number | Unit} x + * @param {number} [y] + * @returns {boolean} + */ this.warCries = function (skillId, x, y) { if (!skillId || x === undefined) return false; const states = {}; @@ -56,7 +63,9 @@ const Precast = new function () { for (let i = 0; i < 3; i++) { try { - if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); + if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) { + throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); + } // Right hand + No Shift let clickType = 3, shift = sdk.clicktypes.shift.NoShift; @@ -91,7 +100,7 @@ const Precast = new function () { this.checkCTA = function () { if (this.haveCTA > -1) return true; - let check = me.checkItem({name: sdk.locale.items.CalltoArms, equipped: true}); + let check = me.checkItem({ name: sdk.locale.items.CalltoArms, equipped: true }); if (check.have) { this.haveCTA = check.item.isOnSwap ? 1 : 0; @@ -100,13 +109,17 @@ const Precast = new function () { return this.haveCTA > -1; }; + /** + * @param {boolean} force + * @returns {boolean} + */ this.precastCTA = function (force = false) { if (!Config.UseCta || this.haveCTA === -1 || me.classic || me.barbarian || me.inTown || me.shapeshifted) return false; if (!force && me.getState(sdk.states.BattleOrders)) return true; if (this.haveCTA > -1) { let slot = me.weaponswitch; - let {x, y} = me; + let { x, y } = me; me.switchWeapons(this.haveCTA); this.cast(sdk.skills.BattleCommand, x, y, true); @@ -125,8 +138,12 @@ const Precast = new function () { return false; }; - // should be done in init function? - // should this be part of the skill class instead? + /** + * Check which slot (primary or secondary) gives us the most skillpoints in a skill + * @param {number} skillId + * @returns {0 | 1} best slot to give us the most skillpoints in a skill + * @todo Move this to be part of the SkillData class + */ this.getBetterSlot = function (skillId) { if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; @@ -544,6 +561,8 @@ const Precast = new function () { switch (Config.SummonSpirit) { case 1: case "Oak Sage": + // to prevent false chickens when we cast oak before getting bo-ed + if (me.hardcore && !me.getState(sdk.states.BattleOrders) && me.inTown) return buffSummons; return (this.summon(sdk.skills.OakSage, sdk.summons.type.Spirit) || buffSummons); case 2: case "Heart of Wolverine": From e6a1f6313419a5a303adc464db1eb5e6feaa72ce Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Feb 2023 17:33:52 -0500 Subject: [PATCH 043/758] rework npcInteract and lastInteractedNPC - Should be better matching ability to find wanted npc with npcInteract --- d2bs/kolbot/libs/core/Town.js | 71 ++++++++------ d2bs/kolbot/libs/scripts/Follower.js | 132 +++++++++++++++------------ 2 files changed, 117 insertions(+), 86 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 08549f223..e57d13e46 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -54,27 +54,27 @@ const Town = { lastInteractedNPC: { tick: 0, /** - * @type {NPCUnit} + * @type {{ name: string, gid: number, area: number}} */ - unit: null, + unit: {}, /** * @param {NPCUnit} npc */ set: function (npc) { - Town.lastInteractedNPC.unit = npc; - Town.lastInteractedNPC.tick = getTickCount(); + if (npc.hasOwnProperty("name") && Object.values(NPC).includes(npc.name)) { + // valid npc + Town.lastInteractedNPC.unit.name = npc.name.toLowerCase(); + Town.lastInteractedNPC.unit.gid = npc.gid; + Town.lastInteractedNPC.unit.area = me.area; + Town.lastInteractedNPC.tick = getTickCount(); + } }, get: function () { try { - if (!!this.unit && getTickCount() - this.tick < Time.seconds(15) - && this.unit.name.toLowerCase() !== "an evil force" && this.unit.area === me.area) { - Config.DebugMode.Town && console.debug("used stored value"); - return this.unit; - } else { - this.reset(); - Config.DebugMode.Town && console.debug("getting new npc"); - return getInteractedNPC(); - } + if (!this.unit.hasOwnProperty("name")) return getInteractedNPC(); + if (getTickCount() - this.tick > Time.seconds(15)) return getInteractedNPC(); + if (this.unit.area !== me.area) return getInteractedNPC(); + return this.unit; } catch (e) { Config.DebugMode.Town && console.error(e); this.reset(); @@ -83,7 +83,7 @@ const Town = { } }, reset: function () { - Town.lastInteractedNPC.unit = null; + Town.lastInteractedNPC.unit = {}; Town.lastInteractedNPC.tick = 0; } }, @@ -122,6 +122,10 @@ const Town = { console.time("doChores"); console.info(true); + /** + * @todo Pre-build task list so we can more efficiently peform our chores + */ + !me.inTown && this.goToTown(); if (!Misc.poll(() => me.gameReady && me.inTown, 2000, 250)) throw new Error("Failed to go to town for chores"); @@ -168,12 +172,19 @@ const Town = { * @returns {boolean | Unit} */ npcInteract: function (name = "", cancel = true) { - name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); + // name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); + // what about finding the closest name in case someone mispells it? + let npcKey = Object.keys(NPC).find(key => String.isEqual(key, name)); + if (!npcKey) { + console.warn("Couldn't find " + name + " in NPC object"); + return false; + } + const npcName = NPC[npcKey]; !me.inTown && Town.goToTown(); me.cancelUIFlags(); - switch (NPC[name]) { + switch (npcName) { case NPC.Jerhyn: !Game.getNPC(NPC.Jerhyn) && Town.move("palace"); break; @@ -184,16 +195,16 @@ const Town = { } // eslint-disable-next-line no-fallthrough default: - Town.move(NPC[name]); + Town.move(npcName); } - let npc = Game.getNPC(NPC[name]); + let npc = Game.getNPC(npcName); // In case Jerhyn is by Warriv - if (name === "Jerhyn" && !npc) { + if (npcName === NPC.Jerhyn && !npc) { me.cancel(); Pather.moveTo(5166, 5206); - npc = Game.getNPC(NPC[name]); + npc = Game.getNPC(npcName); } Packet.flash(me.gid); @@ -201,7 +212,7 @@ const Town = { if (npc && npc.openMenu()) { cancel && me.cancel(); - this.lastInteractedNPC.set(npc); + // this.lastInteractedNPC.set(npc); return npc; } @@ -266,17 +277,20 @@ const Town = { delay(250); - let npc = this.lastInteractedNPC.get(); + let npc = null; let justUseClosest = (["clearInventory", "sell"].includes(reason) && !me.getUnids()); + if (getUIFlag(sdk.uiflags.NPCMenu)) { + npc = getInteractedNPC(); + } try { - if (!!npc) { + if (npc) { if (!justUseClosest && ((npc.name.toLowerCase() !== this.tasks[me.act - 1][task]) // Jamella gamble fix || (task === "Gamble" && npc.name.toLowerCase() === NPC.Jamella))) { me.cancelUIFlags(); npc = null; - this.lastInteractedNPC.reset(); + // this.lastInteractedNPC.reset(); } } @@ -347,7 +361,6 @@ const Town = { } Misc.poll(() => me.gameReady, 2000, 250); - this.lastInteractedNPC.set(npc); if (task === "Heal") { Config.DebugMode.Town && console.debug("Checking if we are frozen"); @@ -1032,21 +1045,21 @@ const Town = { break; } - npc = !!npc ? npc : Town.lastInteractedNPC.get(); + // npc = !!npc ? npc : Town.lastInteractedNPC.get(); try { if (!!npc && npc.name.toLowerCase() === NPC[potDealer] && !getUIFlag(sdk.uiflags.Shop)) { if (!npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); } else { me.cancelUIFlags(); - npc = null; - Town.lastInteractedNPC.reset(); + // npc = null; + // Town.lastInteractedNPC.reset(); Town.move(NPC[potDealer]); npc = Game.getNPC(NPC[potDealer]); if (!npc || !npc.openMenu() || !npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); - Town.lastInteractedNPC.set(npc); + // Town.lastInteractedNPC.set(npc); } } catch (e) { console.error(e); diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index ab96136c7..ff08a5d63 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -56,13 +56,18 @@ function Follower() { let openContainers = true; let action = ""; - this.announce = function (msg = "") { + const announce = function (msg = "") { if (!allowSay) return; say(msg); }; - // Change areas to where leader is - this.checkExit = function (unit, area) { + /** + * Change areas to where leader is + * @param {Player} unit + * @param {number} area + * @returns {boolean} + */ + const checkExit = function (unit, area) { if (unit.inTown) return false; let target; @@ -123,16 +128,20 @@ function Follower() { return false; }; - // Talk to a NPC - this.talk = function (name) { + /** + * Talk to a NPC + * @param {string} name + * @returns {boolean} + */ + const talk = function (name) { if (!me.inTown) { - this.announce("I'm not in town!"); + announce("I'm not in town!"); return false; } if (typeof name !== "string") { - this.announce("No NPC name given."); + announce("No NPC name given."); return false; } @@ -140,7 +149,7 @@ function Follower() { try { Town.npcInteract(name); } catch (e) { - this.announce((typeof e === "object" && e.message ? e.message : typeof e === "string" ? e : "Failed to talk to " + name)); + announce((typeof e === "object" && e.message ? e.message : typeof e === "string" ? e : "Failed to talk to " + name)); } Town.move("portalspot"); @@ -148,12 +157,16 @@ function Follower() { return false; }; - // Change act after completing last act quest - this.changeAct = function (act) { + /** + * Change act after completing last act quest + * @param {number} act + * @returns {boolean} + */ + const changeAct = function (act) { let preArea = me.area; if (me.area >= sdk.areas.townOfAct(act)) { - this.announce("My current act is higher than " + act); + announce("My current act is higher than " + act); return false; } @@ -203,19 +216,19 @@ function Follower() { if (me.area === preArea) { me.cancel(); Town.move("portalspot"); - this.announce("Act change failed."); + announce("Act change failed."); return false; } Town.move("portalspot"); - this.announce("Act change successful."); - act === 2 && this.announce("Don't forget to talk to Drognan after getting the Viper Amulet!"); + announce("Act change successful."); + act === 2 && announce("Don't forget to talk to Drognan after getting the Viper Amulet!"); return true; }; - this.pickPotions = function (range = 5) { + const pickPotions = function (range = 5) { if (me.dead) return false; Town.clearBelt(); @@ -252,7 +265,12 @@ function Follower() { return true; }; - this.chatEvent = function (nick, msg) { + /** + * + * @param {string} nick + * @param {string} msg + */ + const chatEvent = function (nick, msg) { if (msg && nick === Config.Leader) { switch (msg) { case "tele": @@ -260,11 +278,11 @@ function Follower() { if (Pather.teleport) { Pather.teleport = false; - this.announce("Teleport off."); + announce("Teleport off."); } else { Pather.teleport = true; - this.announce("Teleport on."); + announce("Teleport on."); } break; @@ -272,14 +290,14 @@ function Follower() { case me.name + " tele off": Pather.teleport = false; - this.announce("Teleport off."); + announce("Teleport off."); break; case "tele on": case me.name + " tele on": Pather.teleport = true; - this.announce("Teleport on."); + announce("Teleport on."); break; case "a": @@ -287,11 +305,11 @@ function Follower() { if (attack) { attack = false; - this.announce("Attack off."); + announce("Attack off."); } else { attack = true; - this.announce("Attack on."); + announce("Attack on."); } break; @@ -307,14 +325,14 @@ function Follower() { case me.name + " aoff": attack = false; - this.announce("Attack off."); + announce("Attack off."); break; case "aon": case me.name + " aon": attack = true; - this.announce("Attack on."); + announce("Attack on."); break; case "quit": @@ -327,11 +345,11 @@ function Follower() { if (stop) { stop = false; - this.announce("Resuming."); + announce("Resuming."); } else { stop = true; - this.announce("Stopping."); + announce("Stopping."); } break; @@ -347,14 +365,14 @@ function Follower() { skill = parseInt(msg.split(" ")[2], 10); if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { - this.announce("Active aura is: " + skill); + announce("Active aura is: " + skill); Config.AttackSkill[2] = skill; Config.AttackSkill[4] = skill; Skill.setSkill(skill, sdk.skills.hand.Right); } else { - this.announce("I don't have that aura."); + announce("I don't have that aura."); } } @@ -368,12 +386,12 @@ function Follower() { skill = parseInt(msg.split(" ")[2], 10); if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { - this.announce("Attack skill is: " + skill); + announce("Attack skill is: " + skill); Config.AttackSkill[1] = skill; Config.AttackSkill[3] = skill; } else { - this.announce("I don't have that skill."); + announce("I don't have that skill."); } } @@ -394,7 +412,7 @@ function Follower() { commanders.push(piece); } - this.announce("Switching leader to " + piece); + announce("Switching leader to " + piece); Config.Leader = piece; leader = Misc.findPlayer(Config.Leader); @@ -405,7 +423,7 @@ function Follower() { // START - addEventListener("chatmsg", this.chatEvent); + addEventListener("chatmsg", chatEvent); openContainers && Config.OpenChests.enabled && Config.OpenChests.Types.push("all"); // Override config values that use TP @@ -416,18 +434,18 @@ function Follower() { leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.seconds(20), Time.seconds(1)); if (!leader) { - this.announce("Leader not found."); + announce("Leader not found."); delay(1000); quit(); } else { - this.announce("Leader found."); + announce("Leader found."); } while (!Misc.inMyParty(Config.Leader)) { delay(500); } - this.announce("Partied."); + announce("Partied."); me.inTown && Town.move("portalspot"); @@ -440,7 +458,7 @@ function Follower() { } Town.move("portalspot"); - this.announce("I'm alive!"); + announce("I'm alive!"); } while (stop) { @@ -452,7 +470,7 @@ function Follower() { leaderUnit = Misc.getPlayerUnit(Config.Leader); if (leaderUnit) { - this.announce("Leader unit found."); + announce("Leader unit found."); } } @@ -478,7 +496,7 @@ function Follower() { if (attack) { Attack.clear(20, false, false, false, true); - this.pickPotions(20); + pickPotions(20); } me.paladin && Config.AttackSkill[2] > 0 && Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); @@ -488,27 +506,27 @@ function Follower() { delay(100); } - result = this.checkExit(leader, leader.area); + result = checkExit(leader, leader.area); switch (result) { case 1: - this.announce("Taking exit."); + announce("Taking exit."); delay(500); Pather.moveToExit(leader.area, true); break; case 2: - this.announce("Taking portal."); + announce("Taking portal."); break; case 3: - this.announce("Taking waypoint."); + announce("Taking waypoint."); delay(500); Pather.useWaypoint(leader.area, true); break; case 4: - this.announce("Special transit."); + announce("Special transit."); break; } @@ -525,7 +543,7 @@ function Follower() { case "cow": if (me.inArea(sdk.areas.RogueEncampment)) { Town.move("portalspot"); - !Pather.usePortal(sdk.areas.MooMooFarm) && this.announce("Failed to use cow portal."); + !Pather.usePortal(sdk.areas.MooMooFarm) && announce("Failed to use cow portal."); } break; @@ -547,12 +565,12 @@ function Follower() { if (unit) { for (i = 0; i < 3; i += 1) { if (Pather.getWP(me.area)) { - this.announce("Got wp."); + announce("Got wp."); break; } } - i === 3 && this.announce("Failed to get wp."); + i === 3 && announce("Failed to get wp."); } me.cancel(); @@ -563,19 +581,19 @@ function Follower() { break; case "p": - this.announce("!Picking items."); + announce("!Picking items."); Pickit.pickItems(); openContainers && Misc.openChests(20); - this.announce("!Done picking."); + announce("!Done picking."); break; case "1": if (me.inTown && leader.inTown && Misc.getPlayerAct(Config.Leader) !== me.act) { - this.announce("Going to leader's town."); + announce("Going to leader's town."); Town.goToTown(Misc.getPlayerAct(Config.Leader)); Town.move("portalspot"); } else if (me.inTown) { - this.announce("Going outside."); + announce("Going outside."); Town.goToTown(Misc.getPlayerAct(Config.Leader)); Town.move("portalspot"); @@ -593,17 +611,17 @@ function Follower() { case "2": if (!me.inTown) { delay(150); - this.announce("Going to town."); + announce("Going to town."); Pather.usePortal(null, leader.name); } break; case "3": if (me.inTown) { - this.announce("Running town chores"); + announce("Running town chores"); Town.doChores(); Town.move("portalspot"); - this.announce("Ready"); + announce("Ready"); } break; @@ -620,7 +638,7 @@ function Follower() { case "a3": case "a4": case "a5": - this.changeAct(parseInt(action[1], 10)); + changeAct(parseInt(action[1], 10)); break; case me.name + " tp": @@ -632,13 +650,13 @@ function Follower() { break; } - this.announce("No TP scrolls or tomes."); + announce("No TP scrolls or tomes."); break; } - if (action.indexOf("talk") > -1) { - this.talk(action.split(" ")[1]); + if (action.includes("talk")) { + talk(action.split(" ")[1]); } action = ""; From 55fdb322e4e6540204b69811e7829b835b119254 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 Feb 2023 13:27:40 -0500 Subject: [PATCH 044/758] Help toolsthread keep track of townchicken - Add communication from townchicken to toolsthread to fix problems caused by users attempting to pause during townchicken and causing default.dbj to be resumed instead. --- d2bs/kolbot/libs/core/Common/Tools.js | 25 ++++++++++++++-- d2bs/kolbot/threads/ToolsThread.js | 42 +++++++++++++++------------ d2bs/kolbot/threads/TownChicken.js | 17 ++++++----- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index b376d6ed1..7b78336e3 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -22,6 +22,10 @@ timerLastDrink: [], cloneWalked: false, + /** + * @param {boolean} print + * @returns {boolean} + */ checkPing: function (print = true) { // Quit after at least 5 seconds in game if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; @@ -71,7 +75,7 @@ Config.QuitList = temp.slice(0); }, - togglePause: function () { + togglePause: function (townChicken = false) { for (let i = 0; i < this.pauseScripts.length; i++) { let script = getScript(this.pauseScripts[i]); @@ -84,7 +88,10 @@ script.pause(); } } else { - this.pauseScripts[i] === "default.dbj" && console.log("ÿc2Resuming."); + if (this.pauseScripts[i] === "default.dbj") { + if (townChicken) continue; // don't resume default if we are in the middle of townChicken + console.log("ÿc2Resuming."); + } script.resume(); } } @@ -122,6 +129,11 @@ return true; }, + /** + * @param {number} pottype + * @param {number} type + * @returns {ItemUnit | false} + */ getPotion: function (pottype, type) { if (!pottype) return false; @@ -148,6 +160,11 @@ return false; }, + /** + * @param {number} type + * @returns {boolean} + * @todo add stamina/thawing/antidote pot drinking here + */ drinkPotion: function (type) { if (type === undefined) return false; let tNow = getTickCount(); @@ -270,6 +287,10 @@ return id || ""; }, + /** + * @param {MeType | MercUnit} unit + * @returns {string} + */ getStatsString: function (unit) { let realFCR = unit.getStat(sdk.stats.FCR); let realIAS = unit.getStat(sdk.stats.IAS); diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 88d0d8851..2b0c8ee03 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -16,7 +16,7 @@ includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -let Overrides = require("../modules/Override"); +let Overrides = require("../libs/modules/Override"); new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { let monster = orignal({skipBlocked: false, skipImmune: false}); @@ -27,8 +27,8 @@ function main() { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); - let ironGolem, debugInfo = {area: 0, currScript: "no entry"}; - let [quitFlag, antiIdle] = [false, false]; + let ironGolem, debugInfo = { area: 0, currScript: "no entry" }; + let [quitFlag, antiIdle, townChicken] = [false, false, false]; let quitListDelayTime; let idleTick = 0; let canQuit = true; @@ -64,8 +64,8 @@ function main() { // Event functions this.keyEvent = function (key) { switch (key) { - case sdk.keys.PauseBreak: // pause default.dbj - Common.Toolsthread.togglePause(); + case sdk.keys.PauseBreak: // pause running threads + Common.Toolsthread.togglePause(townChicken); break; case sdk.keys.Delete: // quit current game @@ -172,7 +172,7 @@ function main() { 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 && mode === 0 && D2Bot.printToConsole(name1 + " timed out, check their logs"); + 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")); @@ -254,11 +254,20 @@ function main() { this.scriptEvent = function (msg) { if (!!msg && typeof msg === "string") { switch (msg) { + case "townChickenOn": + townChicken = true; + + break; + case "townChickenOff": + townChicken = false; + + break; case "toggleQuitlist": canQuit = !canQuit; break; case "quit": + console.debug("Quiting"); quitFlag = true; break; @@ -312,12 +321,6 @@ function main() { addEventListener("gameevent", this.gameEvent); addEventListener("scriptmsg", this.scriptEvent); - // 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 && Common.Toolsthread.initQuitList(); !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks @@ -328,6 +331,9 @@ function main() { Config.UseHP > 0 && me.hpPercent < Config.UseHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Health); Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + /** + * Feel like potting and lifechicken should actually be seperate threads + */ if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting if (!me.inTown) { @@ -411,6 +417,11 @@ function main() { quitFlag = true; } + if (debugInfo.area !== getAreaName(me.area)) { + debugInfo.area = getAreaName(me.area); + DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); + } + if (quitFlag && canQuit) { if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); @@ -419,12 +430,7 @@ function main() { Common.Toolsthread.checkPing(false); // In case of quitlist triggering first Common.Toolsthread.exit(); - break; - } - - if (debugInfo.area !== getAreaName(me.area)) { - debugInfo.area = getAreaName(me.area); - DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); + return true; } delay(20); diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js index ea9196b4c..2d4545c0d 100644 --- a/d2bs/kolbot/threads/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -20,7 +20,7 @@ function main() { // override broadCastIntent - shouldn't be called at all in this thread Pather.broadcastIntent = () => null; - this.pause = function () { + const pause = function () { for (let i = 0; i < scripts.length; i += 1) { let script = getScript(scripts[i]); @@ -37,7 +37,7 @@ function main() { return true; }; - this.resume = function () { + const resume = function () { for (let i = 0; i < scripts.length; i += 1) { let script = getScript(scripts[i]); @@ -67,11 +67,12 @@ function main() { function (msg) { if (typeof msg === "string" && msg === "townCheck") { townCheck = true; + // maybe check for quit broadcast as well? If we are preparing to quit we shouldn't really be townchickening } }); // Init config and attacks - print("ÿc3Start TownChicken thread"); + console.log("ÿc3Start TownChicken thread"); D2Bot.init(); Config.init(); Pickit.init(); @@ -99,7 +100,7 @@ function main() { continue; } - this.pause(); + pause(); while (!me.gameReady) { if (me.dead) return; @@ -108,8 +109,9 @@ function main() { } try { - console.log("(TownChicken) :: Going to town"); - me.overhead("Going to town"); + Messaging.sendToScript("threads/Toolsthread.js", "townChickenOn"); + console.log("ÿc8(TownChicken) :: ÿc0Going to town"); + me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); Town.visitTown(); } catch (e) { Misc.errorReport(e, "TownChicken.js"); @@ -117,9 +119,10 @@ function main() { return; } finally { - this.resume(); + resume(); townCheck = false; + Messaging.sendToScript("threads/Toolsthread.js", "townChickenOff"); } } From bd8b3a9aea32b943946019a523a05782aeaed1f8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:03:13 -0500 Subject: [PATCH 045/758] Soul/doll quit checks for helper scripts - Watch for leader's message so we don't go in the portal right after they exit due to dolls/souls --- d2bs/kolbot/libs/scripts/AutoBaal.js | 2 +- d2bs/kolbot/libs/scripts/Baal.js | 4 +- d2bs/kolbot/libs/scripts/BaalAssistant.js | 29 +++++++++++--- d2bs/kolbot/libs/scripts/BaalHelper.js | 48 ++++++++++++++++++++--- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index c1bfb41a6..38601ee58 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -202,7 +202,7 @@ function AutoBaal() { Town.getCorpse(); } - !baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport && this.longRangeSupport(); + !baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport && longRangeSupport(); // wait for baal signal - leader's baal message if (baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index 888e0d574..a462f4d11 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -47,7 +47,9 @@ function Baal() { }; Town.doChores(); - !!Config.RandomPrecast ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + Config.RandomPrecast + ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) + : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index a3d119064..d7e06273e 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -17,6 +17,7 @@ function BaalAssistant () { let Leader = Config.Leader; let Helper = Config.BaalAssistant.Helper; let firstAttempt = true; + let quitFlag = false; let [hotCheck, safeCheck, baalCheck, ngCheck, baalIsDead] = [false, false, false, false, false]; let [ShrineStatus, secondAttempt, throneStatus, killTracker] = [false, false, false, false]; @@ -39,6 +40,13 @@ function BaalAssistant () { const chatEvent = function (nick, msg) { if (nick === Leader) { + if ((Config.BaalAssistant.DollQuit && msg === "Dolls found! NG.") + || (Config.BaalAssistant.SoulQuit && msg === "Souls found! NG.")) { + quitFlag = true; + + return; + } + msg = msg.toLowerCase(); for (let i = 0; i < Config.BaalAssistant.HotTPMessage.length; i += 1) { @@ -264,12 +272,17 @@ function BaalAssistant () { if (!Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) || !Pather.moveTo(15118, 5002)) throw new Error("Failed to move to Throne of Destruction."); - Pather.moveTo(15095, 5029); + Pather.moveToEx(15095, 5029, { callback: () => { + if (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + console.log("Undead Soul Killers found, ending script."); + throw new ScriptError("Undead Soul Killers found, ending script."); + } - if ((Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { - print("Burning Souls or Undead Soul Killers found, ending script."); - return true; - } + if (Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + console.log("Burning Souls found, ending script."); + throw new ScriptError("Burning Souls found, ending script."); + } + }}); Pather.moveTo(15118, 5002); Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); @@ -292,12 +305,16 @@ function BaalAssistant () { throw new Error("No safe TP message."); } + if ((Config.BaalAssistant.SoulQuit || Config.BaalAssistant.DollQuit) && quitFlag) { + throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); + } + if (!Misc.poll(() => Pather.usePortal(sdk.areas.ThroneofDestruction, null), Time.seconds(Config.BaalAssistant.Wait), 1000)) { throw new Error("No portals to Throne."); } if ((Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { - throw new Error("Burning Souls or Undead Soul Killers found, ending script."); + throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); } Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index ecb038075..3aec326bf 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -37,18 +37,54 @@ function BaalHelper() { if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) throw new Error("Failed to move to WSK3."); if (!Pather.moveToExit(sdk.areas.ThroneofDestruction, true)) throw new Error("Failed to move to Throne of Destruction."); - if (!Pather.moveTo(15113, 5040)) D2Bot.printToConsole("path fail"); + Pather.moveToEx(15113, 5040, { callback: () => { + if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + console.log("Undead Soul Killers found, ending script."); + throw new ScriptError("Undead Soul Killers found, ending script."); + } + + if (Config.BaalHelper.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + console.log("Burning Souls found, ending script."); + throw new ScriptError("Burning Souls found, ending script."); + } + }}); } else { Town.goToTown(5); Town.move("portalspot"); - if (!Misc.poll(() => { - if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null) && Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { - return true; + let quitFlag = false; + + const chatEvent = function (nick, msg) { + if (nick === Config.Leader) { + if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") + || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { + quitFlag = true; + } } + }; - return false; - }, Time.minutes(Config.BaalHelper.Wait), 1000)) throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + if (Config.BaalHelper.DollQuit || Config.BaalHelper.SoulQuit) { + addEventListener("chatmsg", chatEvent); + } + + try { + if (!Misc.poll(() => { + if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + if (quitFlag) throw new ScriptError("Burning Souls or Dolls found, ending script."); + if (Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + return true; + } + } + + return false; + }, Time.minutes(Config.BaalHelper.Wait), 1000)) throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + } catch (e) { + console.log(e.message); + + return true; + } finally { + removeEventListener("chatmsg", chatEvent); + } } if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { From b669d3a8b0f6d4a2bdd0279efd4e9ee990f2cec8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Feb 2023 15:53:53 -0500 Subject: [PATCH 046/758] Update Attack.js - Add NOOP to Attack.Result to handle case where we want to clear a radius but don't find any monsters. It's not exactly a success or a failure so needed another option for it - Fix Attack.clearLevel params passed into Pather.moveTo, it was passing spectype to the clearPath parameter and if we were using spectype 0 that was being translated as false instead --- d2bs/kolbot/libs/core/Attack.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index adc40dd26..8dfd6c287 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -24,7 +24,8 @@ const Attack = { FAILED: 0, SUCCESS: 1, CANTATTACK: 2, // need to fix the ambiguity between this result and Failed - NEEDMANA: 3 + NEEDMANA: 3, + NOOP: 4, // used for clearing, if we didn't find any monsters to clear it's not exactly a success or fail }, // Initialize attacks @@ -1015,7 +1016,7 @@ const Attack = { let result = Pather.getNearestWalkable(room[0], room[1], 18, 3); if (result) { - Pather.moveTo(result[0], result[1], 3, spectype); + Pather.moveToEx(result[0], result[1], { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } }); previousArea = result; if (!this.clear(40, spectype)) { @@ -1023,7 +1024,7 @@ const Attack = { } } else if (currentArea !== getArea().id) { // Make sure bot does not get stuck in different area. - Pather.moveTo(previousArea[0], previousArea[1], 3, spectype); + Pather.moveToEx(previousArea[0], previousArea[1], { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } }); } } @@ -1646,6 +1647,10 @@ const Attack = { force && console.debug("Forcing new position"); + /** + * @todo If we've disabled tele for walking clear, allow use of tele specifically for repositioning + */ + if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { //me.overhead("Short range"); @@ -1683,7 +1688,7 @@ const Attack = { } } - //print("ÿc9potential spots: ÿc2" + coords.length); + // print("ÿc9potential spots: ÿc2" + coords.length); if (coords.length > 0) { coords.sort(Sort.units); @@ -1691,7 +1696,7 @@ const Attack = { for (let i = 0; i < coords.length; i += 1) { // Valid position found if (!CollMap.checkColl({x: coords[i].x, y: coords[i].y}, unit, coll, 1)) { - //print("ÿc9optimal pos build time: ÿc2" + (getTickCount() - t) + " ÿc9distance from target: ÿc2" + getDistance(cx, cy, unit.x, unit.y)); + // print("ÿc9optimal pos build time: ÿc2" + (getTickCount() - t) + " ÿc9distance from target: ÿc2" + getDistance(cx, cy, unit.x, unit.y)); if ((() => { switch (walk) { case 1: From c03637e78e67f4161300d362431eaa5c12da20e2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Feb 2023 15:54:14 -0500 Subject: [PATCH 047/758] Update Pather.js - Fix for allowClearing being unintentionally set when clearpath was undefined rather than false --- d2bs/kolbot/libs/core/Pather.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index f779f0fde..01a2e280b 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -279,7 +279,7 @@ const Pather = { // set settings.clearSettings equal to the now properly asssigned clearSettings settings.clearSettings = clearSettings; - !settings.allowClearing && (settings.clearSettings.allowClearing = false); + !settings.allowClearing && settings.allowClearing !== undefined && (settings.clearSettings.allowClearing = false); (target instanceof PresetUnit) && (target = target.realCoords()); if (settings.minDist > 3) { @@ -381,9 +381,16 @@ const Pather = { /** * @todo I think some of this needs to be re-worked, I've noticed recursive Attacking/Picking */ - if (!useTeleport && settings.allowClearing && me.checkForMobs({range: 10}) && Attack.clear(10, null, null, null, settings.allowPicking)) { - 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 && (this.kickBarrels(node.x, node.y) || this.openDoors(node.x, node.y))) { continue; @@ -484,7 +491,7 @@ const Pather = { * @param {boolean} pop - remove last node * @returns {boolean} */ - moveTo: function (x, y, retry, clearPath = false, pop = false) { + moveTo: function (x, y, retry, clearPath, pop) { return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, allowClearing: clearPath }); }, From 6480a32c2ae7e3404c1e4c1187a6b49a92a028d8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Feb 2023 15:57:38 -0500 Subject: [PATCH 048/758] Diablo improvements - Add diablo light event watcher when we are rechecking seals to prevent false error being thrown and faster turnaround once he has actually spawned - Add callback functions to each of the diablo seals moveTo calls so we don't run past the boss. Still needs some work on positioning based on classes/builds --- d2bs/kolbot/libs/core/Common/Diablo.js | 68 ++++++++++++++++++-------- d2bs/kolbot/libs/scripts/Diablo.js | 2 +- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 4700205b5..05c212928 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -173,7 +173,7 @@ * @param {number[] | string[]} sealOrder * @param {boolean} openSeals */ - runSeals: function (sealOrder, openSeals = true) { + runSeals: function (sealOrder, openSeals = true, recheck = false) { print("seal order: " + sealOrder); Common.Diablo.sealOrder = sealOrder; let seals = { @@ -184,7 +184,19 @@ "seis": () => this.seisSeal(openSeals), "infector": () => this.infectorSeal(openSeals), }; - sealOrder.forEach(seal => {seals[seal]();}); + try { + recheck && addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + sealOrder.forEach(seal => { + if (recheck && Common.Diablo.diabloSpawned) throw new ScriptError("Diablo spawned"); + seals[seal](); + }); + } catch (e) { + if (!(e instanceof ScriptError)) { + throw e; // it wasn't the custom error so throw it to the next handler + } + } finally { + recheck && removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + } }, /** @@ -303,7 +315,17 @@ } delay(1 + me.ping); - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7691, 5292) : Pather.moveTo(7695, 5316); + let cb = () => { + let viz = Game.getMonster(getLocaleString(sdk.locale.monsters.GrandVizierofChaos)); + return viz && (viz.distance < Skill.getRange(Config.AttackSkill[1]) || viz.dead); + }; + /** + * @todo better coords or maybe a delay, viz appears in different locations and sometimes its right where we are moving to + * which is okay for hammerdins or melee chars but not for soft chars like sorcs + */ + Common.Diablo.vizLayout === 1 + ? Pather.moveToEx(7691, 5292, { callback: cb }) + : Pather.moveToEx(7695, 5316, { callback: cb }); if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { throw new Error("Failed to kill Vizier"); @@ -332,14 +354,17 @@ distCheck.distance > 30 && this.followPath(Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealSeis)) throw new Error("Failed to open de Seis seal."); - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7798, 5194) : Pather.moveTo(7796, 5155); + let cb = () => { + let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); + return seis && (seis.distance < Skill.getRange(Config.AttackSkill[1]) || seis.dead); + }; + Common.Diablo.seisLayout === 1 + ? Pather.moveToEx(7798, 5194, { callback: cb }) + : Pather.moveToEx(7796, 5155, { callback: cb }); try { if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) throw new Error("Failed to kill de Seis"); } catch (e) { - /** - * sometimes we fail just because we aren't in range, - * @todo better fix for this - */ + // sometimes we fail just because we aren't in range, Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); return seis && (seis.distance < 30 || seis.dead); @@ -369,29 +394,30 @@ distCheck.distance > 70 && this.followPath(Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - if (Config.Diablo.Fast) { - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); + let cb = () => { + let inf = Game.getMonster(getLocaleString(sdk.locale.monsters.InfectorofSouls)); + return inf && (inf.distance < Skill.getRange(Config.AttackSkill[1]) || inf.dead); + }; + let moveToLoc = () => { if (Common.Diablo.infLayout === 1) { - (me.sorceress || me.assassin) && Pather.moveTo(7876, 5296); + (me.sorceress || me.assassin) && Pather.moveToEx(7876, 5296, { callback: cb }); delay(1 + me.ping); } else { delay(1 + me.ping); - Pather.moveTo(7928, 5295); + Pather.moveToEx(7928, 5295, { callback: cb }); } + }; + + if (Config.Diablo.Fast) { + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); + moveToLoc(); if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); } else { if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); - - if (Common.Diablo.infLayout === 1) { - (me.sorceress || me.assassin) && Pather.moveTo(7876, 5296); - delay(1 + me.ping); - } else { - delay(1 + me.ping); - Pather.moveTo(7928, 5295); - } + moveToLoc(); if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); diff --git a/d2bs/kolbot/libs/scripts/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js index 823692655..8c9bd27b7 100644 --- a/d2bs/kolbot/libs/scripts/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -69,7 +69,7 @@ function Diablo() { Common.Diablo.diabloPrep(); } catch (error) { console.warn("Diablo wasn't found. Checking seals."); - Common.Diablo.runSeals(Config.Diablo.SealOrder); + Common.Diablo.runSeals(Config.Diablo.SealOrder, true, true); Common.Diablo.diabloPrep(); } From a259cd93896d06e075e442c76c2a212ca149f77b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 17 Feb 2023 13:17:05 -0500 Subject: [PATCH 049/758] Update OOG.js - Should fix cd key in use text being ignored = --- d2bs/kolbot/libs/OOG.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 5ce3678b8..8f813cdb0 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1178,7 +1178,9 @@ includeIfNotIncluded("oog/D2Bot.js"); // required let cdkeyError = false; let defaultPrint = true; let string = ""; - let text = (Controls.LoginErrorText.getText() || Controls.LoginInvalidCdKey.getText()); + let text = getLocation() === sdk.game.locations.LoginError + ? Controls.LoginErrorText.getText() + : Controls.LoginInvalidCdKey.getText(); if (text) { for (let i = 0; i < text.length; i += 1) { @@ -1228,6 +1230,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required D2Bot.printToConsole("Disconnected"); Controls.OkCentered.click(); Controls.LoginErrorOk.click(); + ControlAction.timeoutDelay("Disconnected", Time.minutes(rand(3, 5))); return; case getLocaleString(sdk.locale.text.CdKeyIntendedForAnotherProduct): From 9381420388824350a99b5056eceff681ac43baca Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 18 Feb 2023 14:37:38 -0500 Subject: [PATCH 050/758] Rebuild mfhelper - Changed mfhelper to use a taskList in order to better keep track of what currently needs to be done and improve speed of recovery when a task was missed due to chores - Changed MFLeader announcments to make parsing it for the helper easier - Added handler for Arcane -> Canyon when journal is close enough to see but out of reach for packet interaction - Fixed countdown message for quitlistdelay, I think there is a bug when displaying overhead message then quitting right after --- d2bs/kolbot/libs/core/Attack.js | 10 +- d2bs/kolbot/libs/core/Packet.js | 15 +- d2bs/kolbot/libs/core/Pather.js | 22 ++- d2bs/kolbot/libs/scripts/MFHelper.js | 254 ++++++++++++++++++--------- d2bs/kolbot/libs/scripts/Summoner.js | 2 + d2bs/kolbot/sdk/globals.d.ts | 35 ++++ d2bs/kolbot/threads/ToolsThread.js | 16 +- 7 files changed, 255 insertions(+), 99 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 8dfd6c287..7b50c32f0 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -470,7 +470,7 @@ const Attack = { } ({orgx, orgy} = { orgx: boss.x, orgy: boss.y }); - Config.MFLeader && !!bossId && Pather.makePortal() && say("clear " + bossId); + Config.MFLeader && !!bossId && Pather.makePortal() && say("clear " + (["number", "string"].includes(typeof bossId) ? bossId : bossId.name)); } else { ({orgx, orgy} = { orgx: me.x, orgy: me.y }); } @@ -978,12 +978,8 @@ const Attack = { if (Config.MFLeader && rooms.length > 0) { Pather.makePortal(); - // tombs exception - if (me.area >= sdk.areas.TalRashasTomb1 && me.area <= sdk.areas.TalRashasTomb7) { - say("clearlevel " + me.area); - } else { - say("clearlevel " + getAreaName(currentArea)); - } + console.log("clearlevel " + getAreaName(currentArea)); + say("clearlevel " + me.area); } while (rooms.length > 0) { diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index ba185a9d3..8f10cd05c 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -35,7 +35,8 @@ const Packet = { delay(100); } - sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, unit.gid); + // sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, unit.gid); + new PacketBuilder().byte(sdk.packets.send.NPCInit).dword(1).dword(unit.gid).send(); delay(pingDelay + 1 * 2); Packet.cancelNPC(unit); delay(pingDelay + 1 * 2); @@ -308,15 +309,23 @@ const Packet = { return true; }, + /** + * @param {Unit} who + * @returns {boolean} + */ entityInteract: function (who) { if (!who || !copyUnit(who).x) return false; - sendPacket(1, sdk.packets.send.InteractWithEntity, 4, who.type, 4, who.gid); + new PacketBuilder().byte(sdk.packets.send.InteractWithEntity).dword(who.type).dword(who.gid).send(); return true; }, + /** + * @param {NPCUnit} who + * @returns {boolean} + */ cancelNPC: function (who) { if (!who || !copyUnit(who).x) return false; - sendPacket(1, sdk.packets.send.NPCCancel, 4, who.type, 4, who.gid); + new PacketBuilder().byte(sdk.packets.send.NPCCancel).dword(who.type).dword(who.gid).send(); return true; }, diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 01a2e280b..56343ebfd 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1407,6 +1407,15 @@ const Pather = { } } + /** + * @todo If we start in a3 and want to go to a2, use Meshif + * somehow need to take into account reason for wanting to change act though, e.g. If we were + * going to a2 to revive a merc then running to the a3 waypoint takes us close to our goal. + * On the other hand though if we are going to a2 to take a portal then using meshif makes sense + * extending so far as not just starting next to him but finishing chores anywhere around ormus/wp + * if we are all the way at Alkor then it wouldn't make sense + */ + !getUIFlag(sdk.uiflags.Waypoint) && Town.getDistance("waypoint") > (Skill.haveTK ? 20 : 5) && Town.move("waypoint"); } @@ -1440,7 +1449,7 @@ const Pather = { // Waypoint screen is open if (getUIFlag(sdk.uiflags.Waypoint)) { delay(500); - !this.initialized && (this.initialized = true); + !Pather.initialized && (Pather.initialized = true); switch (targetArea) { case "random": @@ -1879,6 +1888,7 @@ const Pather = { /** * @param {number} area - the id of area to move to * @returns {boolean} + * @todo refactor this, it's rather messy */ journeyTo: function (area) { if (area === undefined) return false; @@ -1889,7 +1899,7 @@ const Pather = { if (area !== sdk.areas.DurielsLair) { target = this.plotCourse(area, me.area); } else { - target = {course: [sdk.areas.CanyonofMagic, sdk.areas.DurielsLair], useWP: false}; + target = { course: [sdk.areas.CanyonofMagic, sdk.areas.DurielsLair], useWP: false }; this.wpAreas.indexOf(me.area) === -1 && (target.useWP = true); } @@ -1968,6 +1978,14 @@ const Pather = { for (let i = 0; i < 5; i++) { unit = Game.getObject(sdk.objects.Journal); + // couldnt find journal? Move to it's preset + if (!unit) { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); + continue; + } else if (unit && unit.distance > 20) { + Pather.moveNearUnit(unit, 13); + } + Packet.entityInteract(unit); Misc.poll(() => getIsTalkingNPC(), 1000, 50); me.cancel(); diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index d8c9cb095..6b0071baa 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -1,33 +1,40 @@ /** * @filename MFHelper.js -* @author kolton +* @author kolton, theBGuy * @desc help another player kill bosses or clear areas * */ function MFHelper() { + /** + * @todo We should be able to handle Diablo scripts then resume MFHelper, not sure how yet but doesn't make sense to have + * helper just idle if leader does any of the a5 scripts before baal. I guess could re-order them in the configs but having + * it broken up by act flows better + */ let player, playerAct, split; - let oldCommand = ""; - let command = ""; + let lastPrecast; - function chatEvent (name, msg) { - if (!player) { - let match = [ - "kill", "clearlevel", "clear", "quit", "cows", "council", "goto", "nextup" - ]; + /** @type {{ task: string, msg: string, at: number, area: number }[]} */ + const taskList = []; + const tasks = ["kill", "clearlevel", "clear", "quit", "cows", "council", "goto", "nextup"]; - if (msg) { - for (let i = 0; i < match.length; i += 1) { - if (msg.match(match[i])) { - player = Misc.findPlayer(name); + /** + * @param {string} name + * @param {string} msg + */ + function chatEvent (name, msg) { + if (!msg) return; + let msgShort = msg && msg.length ? msg.split(" ")[0] : ""; + if (!tasks.includes(msgShort)) return; - break; - } - } - } + if (!player) { + // anything else we need to consider here? + player = Misc.findPlayer(name); } - player && name === player.name && (command = msg); + if (player && name === player.name) { + taskList.push({ task: msgShort, msg: msg, at: getTickCount(), area: player.area }); + } } addEventListener("chatmsg", chatEvent); @@ -57,7 +64,16 @@ function MFHelper() { // START while (true) { - if (me.softcore && me.mode === sdk.player.mode.Dead) { + if (me.dead) { + while (me.mode === sdk.player.mode.Death) { + delay(3); + } + + if (me.hardcore) { + D2Bot.printToConsole("(MFHelper) :: " + me.charname + " has died at level " + me.charlvl + ". Shutting down profile...", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } + while (!me.inTown) { me.revive(); delay(1000); @@ -68,8 +84,7 @@ function MFHelper() { } if (player) { - if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { - Town.heal(); + if (me.needHealing() && Town.heal()) { Town.move("portalspot"); } @@ -78,16 +93,48 @@ function MFHelper() { break; } - if (command !== oldCommand) { - oldCommand = command; + if (taskList.length) { + console.debug("Leader area :: " + player.area); + + if (taskList[0].task === "quit") return true; + // check if any message is telling us to quit + if (taskList.find(el => el.task === "quit")) return true; - if (command.includes("quit")) { - break; - } else if (command.includes("goto")) { - console.log("ÿc4MFHelperÿc0: Goto"); - split = command.substr(6); + // check if any message is telling us that nextup is diablo/baal + if (taskList.some(el => { + if (el.task === "nextup") { + let script = el.msg.split("nextup ")[1]; + + if (script && ["Diablo", "Baal"].includes(script)) { + console.log("ÿc4MFHelperÿc0: Ending script"); + return true; + } + } + + return false; + })) return true; + // handled pre-reqs, now perform normal checks + let { task, msg, at, area } = taskList.shift(); + + switch (task) { + case "goto": try { + // lets see if the task list contains any other goto messages, in case we were late + { + let gt = taskList.findIndex(el => el.task === "goto"); + if (gt > -1) { + // alright there is another so lets see where we should actually be going + for (let i = 0; i < gt - 1; i++) { + // feels hacky but this should remove all elements up to the next goto message, while preserving the order of list + ({ task, msg, at, area } = taskList.shift()); + } + } + } + + split = msg.substr(6); + console.log("ÿc4MFHelperÿc0: Goto " + split); + if (!!parseInt(split, 10)) { split = parseInt(split, 10); } @@ -98,16 +145,13 @@ function MFHelper() { console.log(townerror); } - delay(500 + me.ping); - } else if (command.includes("nextup")) { - split = command.split("nextup ")[1]; - - if (split && ["Diablo", "Baal"].includes(split)) { - break; - } + break; + case "nextup": + split = msg.split("nextup ")[1]; + console.log("ÿc4MFHelperÿc0: NextUp " + split); - delay(500 + me.ping); - } else if (command.includes("cows")) { + break; + case "cows": console.log("ÿc4MFHelperÿc0: Clear Cows"); if (Misc.poll(() => { @@ -118,66 +162,114 @@ function MFHelper() { Precast.doPrecast(false); Common.Cows.clearCowLevel(); delay(1000); - - if (!Pather.getPortal(null, player.name) || !Pather.usePortal(null, player.name)) { - Town.goToTown(); - } } else { console.warn("Failed to use portal. Currently in area: " + me.area); } - } else { - // check that we are in the town of the players act so we can take their portal - !me.inArea(sdk.areas.townOf(player.area)) && Town.goToTown(sdk.areas.actOf(player.area)); - Misc.poll(() => Pather.usePortal(player.area, player.name), Time.seconds(15), 500 + me.ping); - delay(1000); // delay to make sure leader's area is accurate + break; + case "council": + if (!me.inArea(sdk.areas.Travincal) && Town.goToTown(3)) { + Town.move("portalspot"); + Misc.poll(() => Pather.usePortal(sdk.areas.Travincal, player.name), Time.seconds(15), 500 + me.ping); + } + console.log("ÿc4MFHelperÿc0: Kill Council"); + Attack.clearClassids(sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3); - if (!me.inTown && me.area === player.area) { - Precast.doPrecast(true); + break; + default: + // alright first lets check how long its been since the command was given + // this probably needs to be adjusted but for now 3 minutes on any of theses tasks is probably too long + if (getTickCount() - at > Time.minutes(3)) continue; + + /** + * @todo still think this section needs to be done better, we are using a snapshot of the player's area at the time + * of the message but sometimes the area hasn't been updated yet, causing us to do dumb things like attempt to kill + * while still in town. We can't just use the players area though because of towncheck/chicken. Feel like best solution + * would be adding area into leaders message and just always parsing it from there + */ + try { + split = msg.split(task + " ")[1]; + if (parseInt(split, 10)) { + split = parseInt(split, 10); + } + } catch (e) { + console.warn(e.message || "Failed to get id from message split"); + break; + } - if (command.includes("kill")) { - console.log("ÿc4MFHelperÿc0: Kill"); - split = command.split("kill ")[1]; + if (me.area !== area) { + !me.inTown && Town.goToTown(); - try { - if (!!parseInt(split, 10)) { - split = parseInt(split, 10); - } + if (me.act !== sdk.areas.actOf(area)) { + Town.goToTown(sdk.areas.actOf(area)); + Town.move("portalspot"); + } - Attack.kill(split); - Pickit.pickItems(); - } catch (killerror) { - console.error(killerror); - } - } else if (command.includes("clearlevel")) { - console.log("ÿc4MFHelperÿc0: Clear Level " + getArea().name); - Precast.doPrecast(true); - Attack.clearLevel(Config.ClearType); - } else if (command.indexOf("clear") > -1) { - console.log("ÿc4MFHelperÿc0: Clear"); - split = command.split("clear ")[1]; + String.isEqual(task, "clearlevel") + ? (area = split) + : (player.area !== area && !player.inTown) && (area = player.area); - try { - if (!!parseInt(split, 10)) { - split = parseInt(split, 10); - } + try { + Misc.poll(() => Pather.usePortal(null, player.name), Time.seconds(15), 500 + me.ping); + } catch (e) { + console.warn(e.message || "Failed to take leader portal"); + continue; + } + } - Attack.clear(15, 0, split); - } catch (killerror2) { - console.error(killerror2); + if (!me.inTown && me.area === area) { + let forceCast = false; + (!lastPrecast || getTickCount() - lastPrecast > Time.minutes(2)) && (forceCast = true) && (lastPrecast = getTickCount()); + Precast.doPrecast(forceCast); + } else if (!me.inTown && !me.inArea(player.area)) { + Town.goToTown(sdk.areas.actOf(player.area)); + continue; + } + + switch (task) { + case "kill": + console.log("ÿc4MFHelperÿc0: Kill " + split); + + try { + Attack.kill(split); + Pickit.pickItems(); + } catch (killerror) { + console.error(killerror); + } + + break; + case "clearlevel": + try { + console.log("ÿc4MFHelperÿc0: Clear Level " + getAreaName(split)); + + if (me.area !== split) { + console.debug("I am in the wrong area? My Area: " + getAreaName(me.area) + " Wanted Area :: " + getAreaName(split)); + Town.goToTown(sdk.areas.actOf(split)); + Town.move("portalspot"); + if (!Misc.poll(() => Pather.usePortal(split, player.name), Time.seconds(15), 500 + me.ping)) { + throw new Error("Failed to move to clearlevel area"); + } } - } else if (command.includes("council")) { - console.log("ÿc4MFHelperÿc0: Kill Council"); - Attack.clearList(Attack.getMob([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3], 0, 40)); + Attack.clearLevel(Config.ClearType); + } catch (killerror2) { + console.error(killerror2); } - delay(100); + break; + case "clear": + console.log("ÿc4MFHelperÿc0: Clear " + split); - if (!Pather.getPortal(null, player.name) || !Pather.usePortal(null, player.name)) { - Town.goToTown(); + try { + Attack.clear(15, 0, split); + } catch (killerror2) { + console.error(killerror2); } - } else if (!me.inTown && !me.inArea(player.area)) { - Town.goToTown(sdk.areas.actOf(player.area)); + + break; + } + + if (!Pather.getPortal(sdk.areas.townOf(me.act)) || !Pather.usePortal(sdk.areas.townOf(me.act))) { + Town.goToTown(); } } } diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index e1bf6ae3a..f6c5a0758 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -37,6 +37,8 @@ function Summoner () { if (!journal) { Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); continue; + } else if (journal && journal.distance > (18 - i)) { + Pather.moveNearUnit(journal, 13); } Packet.entityInteract(journal); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index d1a5dc2b7..f4bb066a9 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -171,6 +171,40 @@ declare global { decideSkill(unit: Monster, skipSkill?: number[]): [number, number] } + /** + * @todo Figure out what each of these actually returns to properly document them + */ + class File { + readable: boolean; + writable: boolean; + seekable: boolean; + mode: number; + binaryMode: boolean; + length: number; + path: string; + position: number; + eof: boolean; + accessed: number; + created: number; + modified: number; + autoflush: boolean; + + static open(path: string, mode: number): File; + close(): File; + reopen(): File; + read(count: number): string[]; + read(count: number): ArrayBuffer[]; + readLine(): string; + readAllLines(): string[]; + readAll(): string; + write(): void; + seek(n: number): any; + seek(n: number, isLines: boolean, fromStart: boolean): any; + flush(): void; + reset(): void; + end(): void; + } + const FileTools: { readText(filename: string) writeText(filename: string, data: string) @@ -522,6 +556,7 @@ declare global { findItems(param: number, number?: number, number2?: number): ItemUnit[]; usingShield(): boolean; cancelUIFlags(): boolean; + getTome(id: number): ItemUnit | null; } const me: MeType diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 2b0c8ee03..2dbae6ceb 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -424,13 +424,17 @@ function main() { if (quitFlag && canQuit) { if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { - me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); - continue; - } - Common.Toolsthread.checkPing(false); // In case of quitlist triggering first - Common.Toolsthread.exit(); + // should there be a check if we are in the middle of interacting with an npc? Seems quitting game in the middle causes crashes + // only ancedotal evidence currently + if (getTickCount() < quitListDelayTime - 4000) { + me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); + } + } else { + Common.Toolsthread.checkPing(false); // In case of quitlist triggering first + Common.Toolsthread.exit(); - return true; + return true; + } } delay(20); From 4912f77884cbd913624ce617c6c30926da076125 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Feb 2023 02:41:24 -0500 Subject: [PATCH 051/758] Update OOG.js - Utils isn't included here so can't use Time --- d2bs/kolbot/libs/OOG.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 8f813cdb0..40c2d6053 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1230,7 +1230,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required D2Bot.printToConsole("Disconnected"); Controls.OkCentered.click(); Controls.LoginErrorOk.click(); - ControlAction.timeoutDelay("Disconnected", Time.minutes(rand(3, 5))); + ControlAction.timeoutDelay("Disconnected", (rand(3, 5)) * 60000); return; case getLocaleString(sdk.locale.text.CdKeyIntendedForAnotherProduct): From f4837c5083f87da3cc64b1241e306afbca6b2f2b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Feb 2023 00:38:20 -0500 Subject: [PATCH 052/758] Update ToolsThread.js - Log a little more info when checking an item, including pickit result + line and NTIP result + line for easier debugging --- d2bs/kolbot/threads/ToolsThread.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 2dbae6ceb..5aaa6631a 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -134,10 +134,15 @@ function main() { let itemToCheck = Game.getSelectedUnit(); if (!!itemToCheck) { + let pResult = Pickit.checkItem(itemToCheck); + let pString = "ÿc4Pickit: ÿc0" + pResult.result + " ÿc7Line: ÿc0" + pResult.line + "\n"; + let nResult = NTIP.CheckItem(itemToCheck, false, true); + let nString = "ÿc4NTIP.CheckItem: ÿc0" + nResult.result + " ÿc7Line: ÿc0" + nResult.line + "\n"; + itemString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality + "| ÿc4Gid: ÿc0" + itemToCheck.gid + "\nÿc4ItemMode: ÿc0" + itemToCheck.mode + "| ÿc4Location: ÿc0" + itemToCheck.location + "| ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation; - generalString = "ÿc4Pickit: ÿc0" + Pickit.checkItem(itemToCheck).result + " | ÿc4NTIP.CheckItem: ÿc0" + NTIP.CheckItem(itemToCheck, false, true).result + generalString = pString + nString + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck); } @@ -199,11 +204,15 @@ function main() { } break; - case 0x07: - if (Config.AntiHostile && param2 === 0x03) { // "%Player has declared hostility towards you." + case 0x07: // "%Player has declared hostility towards you." + if (Config.AntiHostile && param2 === 0x03) { scriptBroadcast("findHostiles"); } + if (Config.PublicMode) { + scriptBroadcast("hostileCheck"); + } + break; case 0x11: // "%Param1 Stones of Jordan Sold to Merchants" if (Config.DCloneQuit === 2) { From 69ae2b1fdf1bf91701fee9f01c39362fd92a8c1f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Feb 2023 13:32:59 -0500 Subject: [PATCH 053/758] Update ToolsThread.js - rebuild system lists before logging item info so it's accurate to the main thread --- d2bs/kolbot/threads/ToolsThread.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 5aaa6631a..98231e83d 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -134,6 +134,10 @@ function main() { let itemToCheck = Game.getSelectedUnit(); if (!!itemToCheck) { + Cubing.update(); + Runewords.buildLists(); + CraftingSystem.buildLists(); + let pResult = Pickit.checkItem(itemToCheck); let pString = "ÿc4Pickit: ÿc0" + pResult.result + " ÿc7Line: ÿc0" + pResult.line + "\n"; let nResult = NTIP.CheckItem(itemToCheck, false, true); From fb886afee883bce528e9eca8c866bfe1d7daf11c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:26:24 -0500 Subject: [PATCH 054/758] Update Cubing.js - Using switch case for recipe.Ethereal being undefined was failing, directly check it unstead --- d2bs/kolbot/libs/core/Cubing.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 8cceb7a3a..b39ee616a 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -181,7 +181,7 @@ const Cubing = { }, buildRecipes: function () { - this.recipes = []; + Cubing.recipes = []; for (let i = 0; i < Config.Recipes.length; i += 1) { if (typeof Config.Recipes[i] !== "object" || (Config.Recipes[i].length > 2 && typeof Config.Recipes[i][2] !== "number") || Config.Recipes[i].length < 1) { @@ -571,8 +571,8 @@ const Cubing = { buildLists: function () { CraftingSystem.checkSubrecipes(); - this.validIngredients = []; - this.neededIngredients = []; + Cubing.validIngredients = []; + Cubing.neededIngredients = []; let items = me.findItems(-1, sdk.items.mode.inStorage); for (let i = 0; i < this.recipes.length; i += 1) { @@ -661,7 +661,7 @@ const Cubing = { // Remove unneeded flawless gem recipes clearSubRecipes: function () { - this.subRecipes = []; + Cubing.subRecipes = []; for (let i = 0; i < this.recipes.length; i += 1) { if (this.recipes[i].hasOwnProperty("MainRecipe")) { @@ -799,9 +799,9 @@ const Cubing = { case upgradeUnique && unit.unique && ntipResult === Pickit.Result.WANTED: // Unique item matching pickit entry case upgradeRare && unit.rare && ntipResult === Pickit.Result.WANTED: // Rare item matching pickit entry case socketNormal && unit.normal && unit.sockets === 0: // Normal item matching pickit entry, no sockets + if (recipe.Ethereal === undefined) return 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; From 27ff1b6b4be1b7f9c130569784956dbeaabd7fe8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Feb 2023 16:00:41 -0500 Subject: [PATCH 055/758] Move juvCheck to TorchSystem --- d2bs/kolbot/libs/scripts/OrgTorch.js | 15 --------------- d2bs/kolbot/libs/systems/torch/TorchSystem.js | 19 +++++++++++++++++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index 8229e206e..8c0895d3b 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -438,21 +438,6 @@ function OrgTorch () { } }; - // const juvCheck = function () { - // let needJuvs = 0; - // let col = Town.checkColumns(Storage.BeltSize()); - - // for (let i = 0; i < 4; i += 1) { - // if (Config.BeltColumn[i] === "rv") { - // needJuvs += col[i]; - // } - // } - - // print("Need " + needJuvs + " juvs."); - - // return needJuvs; - // }; - // ################# // /* ##### START ##### */ // ################# // diff --git a/d2bs/kolbot/libs/systems/torch/TorchSystem.js b/d2bs/kolbot/libs/systems/torch/TorchSystem.js index fea239480..1659c879c 100644 --- a/d2bs/kolbot/libs/systems/torch/TorchSystem.js +++ b/d2bs/kolbot/libs/systems/torch/TorchSystem.js @@ -247,6 +247,21 @@ const TorchSystem = { return (Misc.getPlayerCount() <= 1); }; + const juvCheck = function () { + let needJuvs = 0; + let col = Town.checkColumns(Storage.BeltSize()); + + for (let i = 0; i < 4; i += 1) { + if (Config.BeltColumn[i] === "rv") { + needJuvs += col[i]; + } + } + + console.log("Need " + needJuvs + " juvs."); + + return needJuvs; + }; + // Check if current character is the farmer let farmer = TorchSystem.isFarmer(); @@ -280,7 +295,7 @@ const TorchSystem = { print("Got key count request from: " + obj.profile); // Get the number of needed keys - neededItems = {pk1: 3 - tkeys, pk2: 3 - hkeys, pk3: 3 - dkeys, rv: this.juvCheck()}; + neededItems = {pk1: 3 - tkeys, pk2: 3 - hkeys, pk3: 3 - dkeys, rv: juvCheck()}; sendCopyData(null, obj.profile, 6, JSON.stringify({name: "neededItems", value: neededItems})); } @@ -328,7 +343,7 @@ const TorchSystem = { delay(100); } - if (getTickCount() - busyTick > 30000 || this.aloneInGame()) { + if (getTickCount() - busyTick > 30000 || aloneInGame()) { busy = false; } } From 2b608aa7ca41434aa4c64b99e18f1a32bf67f1a0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 Feb 2023 13:44:01 -0500 Subject: [PATCH 056/758] Cubing refactoring + cleanup - Changed naming conventions for gems/runes, instead of using the item cubed as the value passed in its now the item we want. e.g. changed ```javascript Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex ``` to ```javascript Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex ``` - second big change is automatically cubing gems other than just pgems if we need them for a recipe. This is experimental, will need testing to ensure we don't keep too many gems - removed Cubing.getCube and made it it's own script, that gets checked from Loader rather than risk the recursion that can happen having the function be called from inside doCubing --- d2bs/kolbot/libs/config/Amazon.js | 25 +- d2bs/kolbot/libs/config/Assassin.js | 25 +- d2bs/kolbot/libs/config/Barbarian.js | 25 +- .../config/Builds/Sorceress.ExampleBuild.js | 1433 ++++++++--------- d2bs/kolbot/libs/config/Druid.js | 25 +- d2bs/kolbot/libs/config/Necromancer.js | 25 +- d2bs/kolbot/libs/config/Paladin.js | 25 +- d2bs/kolbot/libs/config/Sorceress.js | 25 +- d2bs/kolbot/libs/config/_BaseConfigFile.js | 321 ++-- d2bs/kolbot/libs/core/Cubing.js | 645 ++++---- d2bs/kolbot/libs/core/Loader.js | 8 +- d2bs/kolbot/libs/scripts/GetCube.js | 34 + 12 files changed, 1360 insertions(+), 1256 deletions(-) create mode 100644 d2bs/kolbot/libs/scripts/GetCube.js diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 332d8397d..f0366da04 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -588,21 +588,22 @@ function LoadConfig() { // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz - //Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // Make Perfect Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // Make Perfect Emerald - //Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // Make Perfect Ruby - //Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // Make Perfect Diamond - //Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - //Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - //Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - //Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 4c2772e3c..2d891511c 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -594,21 +594,22 @@ function LoadConfig() { // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz - //Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // Make Perfect Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // Make Perfect Emerald - //Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // Make Perfect Ruby - //Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // Make Perfect Diamond - //Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - //Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - //Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - //Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 5c8a9d7b7..fe212c753 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -583,21 +583,22 @@ function LoadConfig() { // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz - //Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // Make Perfect Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // Make Perfect Emerald - //Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // Make Perfect Ruby - //Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // Make Perfect Diamond - //Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - //Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - //Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - //Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring diff --git a/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js b/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js index 2f7e0e8e1..e5a8dc50a 100644 --- a/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js +++ b/d2bs/kolbot/libs/config/Builds/Sorceress.ExampleBuild.js @@ -16,924 +16,923 @@ */ js_strict(true); -if (!isIncluded("core/Cubing.js")) { include("core/Cubing.js"); }; -if (!isIncluded("core/Prototypes.js")) { include("core/Prototypes.js"); }; -if (!isIncluded("core/Runewords.js")) { include("core/Runewords.js"); }; -if (!isIncluded("core/Town.js")) { include("core/Town.js"); }; - -var AutoBuildTemplate = { +include("core/Cubing.js"); +include("core/Prototypes.js"); +include("core/Runewords.js"); +include("core/Town.js"); +const AutoBuildTemplate = { 1: { - //SkillPoints: [-1], // This doesn't matter. We don't have skill points to spend at lvl 1 - //StatPoints: [-1,-1,-1,-1,-1], // This doesn't matter. We don't have stat points to spend at lvl 1 - Update: function () { - - Scripts.ClearAnyArea = true; // We are only level 1 so we will start by clearing Blood Moor - Config.ClearAnyArea.AreaList = [2]; - Config.ClearType = 0; // Monster spectype to kill in level clear scripts (0 = all) - - // Config.PickitFiles.push("level/1.nip"); // File "level/1.nip" is not included, it's just an example. - - Config.OpenChests = true; // Open chests. Controls key buying. - Config.LogExperience = false; // Print experience statistics in the manager. - Config.StashGold = 200; // Minimum amount of gold to stash. - Config.AttackSkill = [0, 36, -1, 36, 36, 0, 0]; // At level 1 we start with a +1 Fire Bolt staff - Config.LowManaSkill = [0, 0]; - Config.PublicMode = 1; - Config.ScanShrines = [15, 13, 12, 14, 7, 6, 2]; - Config.BeltColumn = ["hp", "hp", "hp", "mp"]; // Keep tons of health potions! - } - }, + //SkillPoints: [-1], // This doesn't matter. We don't have skill points to spend at lvl 1 + //StatPoints: [-1,-1,-1,-1,-1], // This doesn't matter. We don't have stat points to spend at lvl 1 + Update: function () { + + Scripts.ClearAnyArea = true; // We are only level 1 so we will start by clearing Blood Moor + Config.ClearAnyArea.AreaList = [2]; + Config.ClearType = 0; // Monster spectype to kill in level clear scripts (0 = all) + + // Config.PickitFiles.push("level/1.nip"); // File "level/1.nip" is not included, it's just an example. + + Config.OpenChests = true; // Open chests. Controls key buying. + Config.LogExperience = false; // Print experience statistics in the manager. + Config.StashGold = 200; // Minimum amount of gold to stash. + Config.AttackSkill = [0, 36, -1, 36, 36, 0, 0]; // At level 1 we start with a +1 Fire Bolt staff + Config.LowManaSkill = [0, 0]; + Config.PublicMode = 1; + Config.ScanShrines = [15, 13, 12, 14, 7, 6, 2]; + Config.BeltColumn = ["hp", "hp", "hp", "mp"]; // Keep tons of health potions! + } + }, 2: { - SkillPoints: [36], // Fire Bolt + 1 - StatPoints: [0, 3, 3, 3, 3], // Strength + 1 , Vitality + 4 - Update: function () { - // Config.PickitFiles.splice(Config.PickitFiles.indexOf("level/1.nip"), 1); // Will remove index "level/1.nip" from Config.PickitFiles - // Config.PickitFiles.push("level/2.nip"); - Config.BeltColumn = ["hp", "hp", "mp", "mp"]; - Config.MinColumn = [1, 1, 1, 1]; - } - }, + SkillPoints: [36], // Fire Bolt + 1 + StatPoints: [0, 3, 3, 3, 3], // Strength + 1 , Vitality + 4 + Update: function () { + // Config.PickitFiles.splice(Config.PickitFiles.indexOf("level/1.nip"), 1); // Will remove index "level/1.nip" from Config.PickitFiles + // Config.PickitFiles.push("level/2.nip"); + Config.BeltColumn = ["hp", "hp", "mp", "mp"]; + Config.MinColumn = [1, 1, 1, 1]; + } + }, 3: { - SkillPoints: [39], // Ice Bolt + 1 - StatPoints: [0, 0, 3, 3, 3], // Strength + 2 , Vitality + 3 - Update: function () { - Config.AttackSkill = [39, 36, -1, 36, 0, 0, 0]; // Ice Bolt and Fire Bolt - } - }, + SkillPoints: [39], // Ice Bolt + 1 + StatPoints: [0, 0, 3, 3, 3], // Strength + 2 , Vitality + 3 + Update: function () { + Config.AttackSkill = [39, 36, -1, 36, 0, 0, 0]; // Ice Bolt and Fire Bolt + } + }, 4: { - SkillPoints: [37], // Warmth + 1 - StatPoints: [0, 0, 0, 3, 3], // Strength + 3 , Vitality + 2 - Update: function () { - Scripts.Corpsefire = true; // Lets try Corpsefire now that we're level 4 - Config.Corpsefire.ClearDen = true; + SkillPoints: [37], // Warmth + 1 + StatPoints: [0, 0, 0, 3, 3], // Strength + 3 , Vitality + 2 + Update: function () { + Scripts.Corpsefire = true; // Lets try Corpsefire now that we're level 4 + Config.Corpsefire.ClearDen = true; - Scripts.ClearAnyArea = false; // Don't want to clear Blood Moor anymore (See lvl 1 above) - Config.ClearAnyArea.AreaList = []; + Scripts.ClearAnyArea = false; // Don't want to clear Blood Moor anymore (See lvl 1 above) + Config.ClearAnyArea.AreaList = []; - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; // Start keeping rejuvs since we have +1 Warmth - Config.MinColumn = [1, 1, 1, 0]; - } - }, + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; // Start keeping rejuvs since we have +1 Warmth + Config.MinColumn = [1, 1, 1, 0]; + } + }, 5: { - SkillPoints: [38], // Charged Bolt + 1 - StatPoints: [0, 0, 0, 0, 3], // Strength + 4 , Vitality + 1 - Update: function () { + SkillPoints: [38], // Charged Bolt + 1 + StatPoints: [0, 0, 0, 0, 3], // Strength + 4 , Vitality + 1 + Update: function () { - Scripts.ClearAnyArea = true; // Now we'll try enabling it again Cold Plains and Stony Field - Config.ClearAnyArea.AreaList = [3, 4]; + Scripts.ClearAnyArea = true; // Now we'll try enabling it again Cold Plains and Stony Field + Config.ClearAnyArea.AreaList = [3, 4]; - Config.ScanShrines = [15, 13, 12]; - Config.AttackSkill = [39, 36, -1, 38, 0, 39, 0]; // All the bolts! - } - }, + Config.ScanShrines = [15, 13, 12]; + Config.AttackSkill = [39, 36, -1, 38, 0, 39, 0]; // All the bolts! + } + }, 6: { - SkillPoints: [36], // Fire Bolt + 1 - StatPoints: [0, 0, 3, 3, 2], // Strength + 2 , Vitality + 2, Dexterity + 1 - Update: function () { - Config.AttackSkill = [39, 36, -1, 36, 0, 38, 0]; // All the bolts! - } - }, + SkillPoints: [36], // Fire Bolt + 1 + StatPoints: [0, 0, 3, 3, 2], // Strength + 2 , Vitality + 2, Dexterity + 1 + Update: function () { + Config.AttackSkill = [39, 36, -1, 36, 0, 38, 0]; // All the bolts! + } + }, 7: { - SkillPoints: [-1], // TODO - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], // TODO + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 8: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 9: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 10: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 11: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 12: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 13: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 14: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 15: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 16: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 17: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 18: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 19: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 20: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 21: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 22: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 23: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 24: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 25: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 26: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 27: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 28: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 29: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 30: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 31: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 32: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 33: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 34: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 35: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 36: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 37: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 38: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 39: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 40: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 41: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 42: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 43: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 44: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 45: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 46: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 47: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 48: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 49: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 50: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 51: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 52: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 53: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 54: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 55: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 56: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 57: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 58: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 59: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 60: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 61: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 62: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 63: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 64: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 65: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 66: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 67: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 68: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 69: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 70: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 71: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 72: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 73: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 74: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 75: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 76: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 77: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 78: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 79: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 80: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 81: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 82: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 83: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 84: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 85: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 86: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 87: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 88: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 89: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 90: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 91: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 92: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 93: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 94: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 95: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 96: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 97: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 98: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, 99: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; } + } }; diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index c768ec1b3..27c63f38b 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -587,21 +587,22 @@ function LoadConfig() { // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz - //Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // Make Perfect Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // Make Perfect Emerald - //Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // Make Perfect Ruby - //Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // Make Perfect Diamond - //Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - //Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - //Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - //Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 956dffde9..e75723820 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -608,21 +608,22 @@ function LoadConfig() { // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz - //Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // Make Perfect Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // Make Perfect Emerald - //Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // Make Perfect Ruby - //Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // Make Perfect Diamond - //Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - //Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - //Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - //Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 9176c1a1c..f00da4c87 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -587,21 +587,22 @@ function LoadConfig() { // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz - //Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // Make Perfect Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // Make Perfect Emerald - //Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // Make Perfect Ruby - //Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // Make Perfect Diamond - //Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - //Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - //Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - //Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 57fb1753c..0369f9aec 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -589,21 +589,22 @@ function LoadConfig() { // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // Make Perfect Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // Make Perfect Topaz - //Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // Make Perfect Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // Make Perfect Emerald - //Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // Make Perfect Ruby - //Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // Make Perfect Diamond - //Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // Make Perfect Skull + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - //Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - //Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - //Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 05b47e1e5..e386e36a8 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -376,13 +376,15 @@ Config.MPBuffer = 0; // Number of mana potions to keep in inventory. Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - /* Potion types for belt columns from left to right. + /** + * Potion types for belt columns from left to right. * Rejuvenation potions must always be rightmost. * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") */ Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - /* Minimum amount of potions from left to right. + /** + * Minimum amount of potions from left to right. * If we have less, go to vendor to purchase more. * Set rejuvenation columns to 0, because they can't be bought. */ @@ -409,8 +411,8 @@ // Default folder is kolbot/pickit. // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); Config.PickRange = 40; // Pick radius Config.FastPick = false; // Check and pick items between attacks Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. @@ -476,7 +478,8 @@ /* ##### ATTACK SETTINGS ##### */ // ########################### // - /* Attack config + /** + * Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. @@ -578,7 +581,8 @@ Config.Curse[0] = 0; // Boss curse. Use skill number or set to 0 to disable. Config.Curse[1] = 0; // Other monsters curse. Use skill number or set to 0 to disable. - /* Custom curses for monster + /** + * Custom curses for monster * Can use monster name or classid * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; * Optional 3rd parameter for spectype, leave blank to use on all @@ -631,185 +635,189 @@ // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + /* + * All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). + * Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Gem, "Chipped Amethyst"]); // make FlawedAmethyst - //Config.Recipes.push([Recipe.Gem, "Chipped Topaz"]); // make Flawed Topaz - //Config.Recipes.push([Recipe.Gem, "Chipped Sapphire"]); // make Flawed Sapphire - //Config.Recipes.push([Recipe.Gem, "Chipped Emerald"]); // make Flawed Emerald - //Config.Recipes.push([Recipe.Gem, "Chipped Ruby"]); // make Flawed Ruby - //Config.Recipes.push([Recipe.Gem, "Chipped Diamond"]); // make Flawed Diamond - //Config.Recipes.push([Recipe.Gem, "Chipped Skull"]); // make Flawed Skull - - //Config.Recipes.push([Recipe.Gem, "Flawed Amethyst"]); // make Amethyst - //Config.Recipes.push([Recipe.Gem, "Flawed Topaz"]); // make Topaz - //Config.Recipes.push([Recipe.Gem, "Flawed Sapphire"]); // make Sapphire - //Config.Recipes.push([Recipe.Gem, "Flawed Emerald"]); // make Emerald - //Config.Recipes.push([Recipe.Gem, "Flawed Ruby"]); // make Ruby - //Config.Recipes.push([Recipe.Gem, "Flawed Diamond"]); // make Diamond - //Config.Recipes.push([Recipe.Gem, "Flawed Skull"]); // make Skull - - Config.Recipes.push([Recipe.Gem, "Amethyst"]); // make Flawless Amethyst - Config.Recipes.push([Recipe.Gem, "Topaz"]); // make Flawless Topaz - Config.Recipes.push([Recipe.Gem, "Sapphire"]); // make Flawless Sapphire - Config.Recipes.push([Recipe.Gem, "Emerald"]); // make Flawless Emerald - Config.Recipes.push([Recipe.Gem, "Ruby"]); // make Flawless Ruby - Config.Recipes.push([Recipe.Gem, "Diamond"]); // make Flawless Diamond - Config.Recipes.push([Recipe.Gem, "Skull"]); // make Flawless Skull - - Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // make Perfect Amethyst - Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // make Perfect Topaz - Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // make Perfect Sapphire - Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // make Perfect Emerald - Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // make Perfect Ruby - Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // make Perfect Diamond - Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + // Config.Recipes.push([Recipe.Gem, "Flawed Amethyst"]); // make Flawed Amethyst + // Config.Recipes.push([Recipe.Gem, "Flawed Topaz"]); // make Flawed Topaz + // Config.Recipes.push([Recipe.Gem, "Flawed Sapphire"]); // make Flawed Sapphire + // Config.Recipes.push([Recipe.Gem, "Flawed Emerald"]); // make Flawed Emerald + // Config.Recipes.push([Recipe.Gem, "Flawed Ruby"]); // make Flawed Ruby + // Config.Recipes.push([Recipe.Gem, "Flawed Diamond"]); // make Flawed Diamond + // Config.Recipes.push([Recipe.Gem, "Flawed Skull"]); // make Flawed Skull + + // Config.Recipes.push([Recipe.Gem, "Amethyst"]); // make Amethyst + // Config.Recipes.push([Recipe.Gem, "Topaz"]); // make Topaz + // Config.Recipes.push([Recipe.Gem, "Sapphire"]); // make Sapphire + // Config.Recipes.push([Recipe.Gem, "Emerald"]); // make Emerald + // Config.Recipes.push([Recipe.Gem, "Ruby"]); // make Ruby + // Config.Recipes.push([Recipe.Gem, "Diamond"]); // make Diamond + // Config.Recipes.push([Recipe.Gem, "Skull"]); // make Skull + + // Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // make Flawless Amethyst + // Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // make Flawless Topaz + // Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // make Flawless Sapphire + // Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // make Flawless Emerald + // Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // make Flawless Ruby + // Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // make Flawless Diamond + // Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // make Flawless Skull + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + // Config.Recipes.push([Recipe.Token]); // Make Token of Absolution // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Rune, "El Rune"]); // Upgrade El to Eld - //Config.Recipes.push([Recipe.Rune, "Eld Rune"]); // Upgrade Eld to Tir - //Config.Recipes.push([Recipe.Rune, "Tir Rune"]); // Upgrade Tir to Nef - //Config.Recipes.push([Recipe.Rune, "Nef Rune"]); // Upgrade Nef to Eth - //Config.Recipes.push([Recipe.Rune, "Eth Rune"]); // Upgrade Eth to Ith - //Config.Recipes.push([Recipe.Rune, "Ith Rune"]); // Upgrade Ith to Tal - //Config.Recipes.push([Recipe.Rune, "Tal Rune"]); // Upgrade Tal to Ral - //Config.Recipes.push([Recipe.Rune, "Ral Rune"]); // Upgrade Ral to Ort - //Config.Recipes.push([Recipe.Rune, "Ort Rune"]); // Upgrade Ort to Thul - - //Config.Recipes.push([Recipe.Rune, "Thul Rune"]); // Upgrade Thul to Amn - //Config.Recipes.push([Recipe.Rune, "Amn Rune"]); // Upgrade Amn to Sol - //Config.Recipes.push([Recipe.Rune, "Sol Rune"]); // Upgrade Sol to Shael - //Config.Recipes.push([Recipe.Rune, "Shael Rune"]); // Upgrade Shael to Dol - //Config.Recipes.push([Recipe.Rune, "Dol Rune"]); // Upgrade Dol to Hel - //Config.Recipes.push([Recipe.Rune, "Hel Rune"]); // Upgrade Hel to Io - //Config.Recipes.push([Recipe.Rune, "Io Rune"]); // Upgrade Io to Lum - //Config.Recipes.push([Recipe.Rune, "Lum Rune"]); // Upgrade Lum to Ko - //Config.Recipes.push([Recipe.Rune, "Ko Rune"]); // Upgrade Ko to Fal - //Config.Recipes.push([Recipe.Rune, "Fal Rune"]); // Upgrade Fal to Lem - //Config.Recipes.push([Recipe.Rune, "Lem Rune"]); // Upgrade Lem to Pul - - Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Pul to Um - //Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Um to Mal - Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Mal to Ist - //Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Ist to Gul - Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Gul to Vex + // Config.Recipes.push([Recipe.Rune, "Eld Rune"]); // Upgrade El to Eld + // Config.Recipes.push([Recipe.Rune, "Tir Rune"]); // Upgrade Eld to Tir + // Config.Recipes.push([Recipe.Rune, "Nef Rune"]); // Upgrade Tir to Nef + // Config.Recipes.push([Recipe.Rune, "Eth Rune"]); // Upgrade Nef to Eth + // Config.Recipes.push([Recipe.Rune, "Ith Rune"]); // Upgrade Eth to Ith + // Config.Recipes.push([Recipe.Rune, "Tal Rune"]); // Upgrade Ith to Tal + // Config.Recipes.push([Recipe.Rune, "Ral Rune"]); // Upgrade Tal to Ral + // Config.Recipes.push([Recipe.Rune, "Ort Rune"]); // Upgrade Ral to Ort + + // Config.Recipes.push([Recipe.Rune, "Thul Rune"]); // Upgrade Ort to Thul + // Config.Recipes.push([Recipe.Rune, "Amn Rune"]); // Upgrade Thul to Amn + // Config.Recipes.push([Recipe.Rune, "Sol Rune"]); // Upgrade Amn to Sol + // Config.Recipes.push([Recipe.Rune, "Shael Rune"]); // Upgrade Sol to Shael + // Config.Recipes.push([Recipe.Rune, "Dol Rune"]); // Upgrade Shael to Dol + // Config.Recipes.push([Recipe.Rune, "Hel Rune"]); // Upgrade Dol to Hel + // Config.Recipes.push([Recipe.Rune, "Io Rune"]); // Upgrade Hel to Io + // Config.Recipes.push([Recipe.Rune, "Lum Rune"]); // Upgrade Io to Lum + // Config.Recipes.push([Recipe.Rune, "Ko Rune"]); // Upgrade Lum to Ko + // Config.Recipes.push([Recipe.Rune, "Fal Rune"]); // Upgrade Ko to Fal + // Config.Recipes.push([Recipe.Rune, "Lem Rune"]); // Upgrade Fal to Lem + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Helm - //Config.Recipes.push([Recipe.Blood.Boots, "Mirrored Boots"]); // Craft Blood Boots - //Config.Recipes.push([Recipe.Blood.Gloves, "Vampirebone Gloves"]); // Craft Blood Gloves - //Config.Recipes.push([Recipe.Blood.Belt, "Mithril Coil"]); // Craft Blood Belt - //Config.Recipes.push([Recipe.Blood.Shield, "Blade Barrier"]); // Craft Blood Shield - //Config.Recipes.push([Recipe.Blood.Body, "Hellforge Plate"]); // Craft Blood Armor - //Config.Recipes.push([Recipe.Blood.Amulet]); // Craft Blood Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Weapon, "Berserker Axe"]); // Craft Blood Weapon - - //Config.Recipes.push([Recipe.Caster.Helm, "Demonhead Mask"]); // Craft Caster Helm - //Config.Recipes.push([Recipe.Caster.Boots, "Wyrmhide Boots"]); // Craft Caster Boots - //Config.Recipes.push([Recipe.Caster.Gloves, "Bramble Mitts"]); // Craft Caster Gloves - //Config.Recipes.push([Recipe.Caster.Belt, "Vampirefang Belt"]); // Craft Caster Belt - //Config.Recipes.push([Recipe.Caster.Shield, "Luna"]); // Craft Caster Shield - //Config.Recipes.push([Recipe.Caster.Body, "Archon Plate"]); // Craft Caster Armor - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Caster.Ring]); // Craft Caster Ring - //Config.Recipes.push([Recipe.Caster.Weapon, "Seraph Rod"]); // Craft Caster Weapon - - //Config.Recipes.push([Recipe.HitPower.Helm, "Giant Conch"]); // Craft Hit Power Helm - //Config.Recipes.push([Recipe.HitPower.Boots, "Boneweave Boots"]); // Craft Hit Power Boots - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Gloves - //Config.Recipes.push([Recipe.HitPower.Belt, "Troll Belt"]); // Craft Hit Power Belt - //Config.Recipes.push([Recipe.HitPower.Shield, "Ward"]); // Craft Hit Power Shield - //Config.Recipes.push([Recipe.HitPower.Body, "Kraken Shell"]); // Craft Hit Power Armor - //Config.Recipes.push([Recipe.HitPower.Amulet]); // Craft Hit Power Amulet - //Config.Recipes.push([Recipe.HitPower.Ring]); // Craft Hit Power Ring - //Config.Recipes.push([Recipe.HitPower.Weapon, "Scourge"]); // Craft Hit Power Weapon | "Blunt" = All maces, rods (+50% Undead), excepting orbs - - //Config.Recipes.push([Recipe.Safety.Helm, "Corona"]); // Craft Safety Helm - //Config.Recipes.push([Recipe.Safety.Boots, "Myrmidon Boots"]); // Craft Safety Boots - //Config.Recipes.push([Recipe.Safety.Gloves, "Ogre Gauntlets"]); // Craft Safety Gloves - //Config.Recipes.push([Recipe.Safety.Belt, "Spiderweb Sash"]); // Craft Safety Belt - //Config.Recipes.push([Recipe.Safety.Shield, "Monarch"]); // Craft Safety Shield - //Config.Recipes.push([Recipe.Safety.Body, "Great Hauberk"]); // Craft Safety Armor - //Config.Recipes.push([Recipe.Safety.Amulet]); // Craft Safety Amulet - //Config.Recipes.push([Recipe.Safety.Ring]); // Craft Safety Ring - //Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Javelin"]); // Craft Safety Weapon - //Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Spear"]); // Craft Safety Weapon + // Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Helm + // Config.Recipes.push([Recipe.Blood.Boots, "Mirrored Boots"]); // Craft Blood Boots + // Config.Recipes.push([Recipe.Blood.Gloves, "Vampirebone Gloves"]); // Craft Blood Gloves + // Config.Recipes.push([Recipe.Blood.Belt, "Mithril Coil"]); // Craft Blood Belt + // Config.Recipes.push([Recipe.Blood.Shield, "Blade Barrier"]); // Craft Blood Shield + // Config.Recipes.push([Recipe.Blood.Body, "Hellforge Plate"]); // Craft Blood Armor + // Config.Recipes.push([Recipe.Blood.Amulet]); // Craft Blood Amulet + // Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + // Config.Recipes.push([Recipe.Blood.Weapon, "Berserker Axe"]); // Craft Blood Weapon + + // Config.Recipes.push([Recipe.Caster.Helm, "Demonhead Mask"]); // Craft Caster Helm + // Config.Recipes.push([Recipe.Caster.Boots, "Wyrmhide Boots"]); // Craft Caster Boots + // Config.Recipes.push([Recipe.Caster.Gloves, "Bramble Mitts"]); // Craft Caster Gloves + // Config.Recipes.push([Recipe.Caster.Belt, "Vampirefang Belt"]); // Craft Caster Belt + // Config.Recipes.push([Recipe.Caster.Shield, "Luna"]); // Craft Caster Shield + // Config.Recipes.push([Recipe.Caster.Body, "Archon Plate"]); // Craft Caster Armor + // Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + // Config.Recipes.push([Recipe.Caster.Ring]); // Craft Caster Ring + // Config.Recipes.push([Recipe.Caster.Weapon, "Seraph Rod"]); // Craft Caster Weapon + + // Config.Recipes.push([Recipe.HitPower.Helm, "Giant Conch"]); // Craft Hit Power Helm + // Config.Recipes.push([Recipe.HitPower.Boots, "Boneweave Boots"]); // Craft Hit Power Boots + // Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Gloves + // Config.Recipes.push([Recipe.HitPower.Belt, "Troll Belt"]); // Craft Hit Power Belt + // Config.Recipes.push([Recipe.HitPower.Shield, "Ward"]); // Craft Hit Power Shield + // Config.Recipes.push([Recipe.HitPower.Body, "Kraken Shell"]); // Craft Hit Power Armor + // Config.Recipes.push([Recipe.HitPower.Amulet]); // Craft Hit Power Amulet + // Config.Recipes.push([Recipe.HitPower.Ring]); // Craft Hit Power Ring + // Config.Recipes.push([Recipe.HitPower.Weapon, "Scourge"]); // Craft Hit Power Weapon | "Blunt" = All maces, rods (+50% Undead), excepting orbs + + // Config.Recipes.push([Recipe.Safety.Helm, "Corona"]); // Craft Safety Helm + // Config.Recipes.push([Recipe.Safety.Boots, "Myrmidon Boots"]); // Craft Safety Boots + // Config.Recipes.push([Recipe.Safety.Gloves, "Ogre Gauntlets"]); // Craft Safety Gloves + // Config.Recipes.push([Recipe.Safety.Belt, "Spiderweb Sash"]); // Craft Safety Belt + // Config.Recipes.push([Recipe.Safety.Shield, "Monarch"]); // Craft Safety Shield + // Config.Recipes.push([Recipe.Safety.Body, "Great Hauberk"]); // Craft Safety Armor + // Config.Recipes.push([Recipe.Safety.Amulet]); // Craft Safety Amulet + // Config.Recipes.push([Recipe.Safety.Ring]); // Craft Safety Ring + // Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Javelin"]); // Craft Safety Weapon + // Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Spear"]); // Craft Safety Weapon // The gems not used by other recipes will be used for magic item rerolling. - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + // Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + // Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) // the cubing formula: 6 Perfect Skulls + 1 Rare Item = 1 random low quality rare item of the same type - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + // Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem // the cubing formula: 1 Perfect Skull + 1 Rare Item + Stone of Jordan = 1 high quality new rare item of the same type - //Config.Recipes.push([Recipe.Reroll.HighRare, "Diadem"]); // Reroll high rare Diadem + // Config.Recipes.push([Recipe.Reroll.HighRare, "Diadem"]); // Reroll high rare Diadem - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + /* + * Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + // Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + // Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + // Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + // Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite // ########################### // /* #### RUNEWORD SETTINGS #### */ // ########################### // - /* All recipes are available in Templates/Runewords.txt + /* + * All recipes are available in Templates/Runewords.txt * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them */ Config.MakeRunewords = true; // Set to true to enable runeword making/rerolling - Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.Runewords.push([Runeword.Insight, "Great Poleaxe"]); // Make Insight Great Poleaxe - //Config.Runewords.push([Runeword.Insight, "Giant Thresher"]); // Make Insight Giant Thresher - Config.Runewords.push([Runeword.Insight, "Colossus Voulge"]); // Make Insight Colossus Voulge - Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); // medium Insight - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17 && [enhanceddamage] >= 260 && [attackrate] >= 250"); // perfect Insight - - Config.Runewords.push([Runeword.Grief, "Phase Blade"]); // Make Grief Phase Blade - //Config.Runewords.push([Runeword.Grief, "Berserker Axe"]); // Make Grief Berserker Axe - Config.KeepRunewords.push("([type] == sword || [type] == axe) # [plusmaxdamage] >= 390"); // medium Grief - //Config.KeepRunewords.push("([type] == sword || [type] == axe) # [itemfasterattackrate] >= 40 && [plusmaxdamage] >= 400"); // perfect Grief and *optional [itempiercepois] >= 25 - - Config.Runewords.push([Runeword.CallToArms, "Crystal Sword"]); // Make CTA Crystal Sword - Config.Runewords.push([Runeword.CallToArms, "Phase Blade"]); // Make CTA Phase Blade - //Config.Runewords.push([Runeword.CallToArms, "Flail"]); // Make CTA Flail - //Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 3 && [plusskillbattleorders] >=3"); - Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 6 && [plusskillbattleorders] >=6 && [plusskillbattlecry] >= 4"); // perfect CTA and *optional [enhanceddamage] = 290% - - Config.Runewords.push([Runeword.Spirit, "Crystal Sword"]); // Make Spirit Crystal Sword - Config.Runewords.push([Runeword.Spirit, "Broad Sword"]); // Make Spirit Broad Sword - //Config.Runewords.push([Runeword.Spirit, "Battle Sword"]); // Make Spirit Battle Sword - //Config.Runewords.push([Runeword.Spirit, "Phase Blade"]); // Make Spirit Phase Blade - Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - Config.Runewords.push([Runeword.Spirit, "Kurast Shield"]); // Make Spirit Kurast Shield - //Config.Runewords.push([Runeword.Spirit, "Vortex Shield"]); // Make Spirit Vortex Shield - Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35"); // middle spirit - //Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35 && [maxmana] >= 112 && [itemabsorbmagic] >=8"); // perfect spirit - - //Config.Runewords.push([Runeword.Prudence, "Sacred Armor", Roll.Eth]); // Make ethereal Prudence Sacred Armor - //Config.KeepRunewords.push("[type] == Armor # [enhanceddefense] == 170 && [fireresist] == 35"); + // Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + // Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + // Config.Runewords.push([Runeword.Insight, "Great Poleaxe"]); // Make Insight Great Poleaxe + // Config.Runewords.push([Runeword.Insight, "Giant Thresher"]); // Make Insight Giant Thresher + // Config.Runewords.push([Runeword.Insight, "Colossus Voulge"]); // Make Insight Colossus Voulge + // Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); // medium Insight + // Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17 && [enhanceddamage] >= 260 && [attackrate] >= 250"); // perfect Insight + + // Config.Runewords.push([Runeword.Grief, "Phase Blade"]); // Make Grief Phase Blade + // Config.Runewords.push([Runeword.Grief, "Berserker Axe"]); // Make Grief Berserker Axe + // Config.KeepRunewords.push("([type] == sword || [type] == axe) # [plusmaxdamage] >= 390"); // medium Grief + // Config.KeepRunewords.push("([type] == sword || [type] == axe) # [itemfasterattackrate] >= 40 && [plusmaxdamage] >= 400"); // perfect Grief and *optional [itempiercepois] >= 25 + + // Config.Runewords.push([Runeword.CallToArms, "Crystal Sword"]); // Make CTA Crystal Sword + // Config.Runewords.push([Runeword.CallToArms, "Phase Blade"]); // Make CTA Phase Blade + // Config.Runewords.push([Runeword.CallToArms, "Flail"]); // Make CTA Flail + // Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 3 && [plusskillbattleorders] >=3"); + // Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 6 && [plusskillbattleorders] >=6 && [plusskillbattlecry] >= 4"); // perfect CTA and *optional [enhanceddamage] = 290% + + // Config.Runewords.push([Runeword.Spirit, "Crystal Sword"]); // Make Spirit Crystal Sword + // Config.Runewords.push([Runeword.Spirit, "Broad Sword"]); // Make Spirit Broad Sword + // Config.Runewords.push([Runeword.Spirit, "Battle Sword"]); // Make Spirit Battle Sword + // Config.Runewords.push([Runeword.Spirit, "Phase Blade"]); // Make Spirit Phase Blade + // Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + // Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + // Config.Runewords.push([Runeword.Spirit, "Kurast Shield"]); // Make Spirit Kurast Shield + // Config.Runewords.push([Runeword.Spirit, "Vortex Shield"]); // Make Spirit Vortex Shield + // Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35"); // middle spirit + // Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35 && [maxmana] >= 112 && [itemabsorbmagic] >=8"); // perfect spirit + + // Config.Runewords.push([Runeword.Prudence, "Sacred Armor", Roll.Eth]); // Make ethereal Prudence Sacred Armor + // Config.KeepRunewords.push("[type] == Armor # [enhanceddefense] == 170 && [fireresist] == 35"); // #################################### // /* #### ADVANCED AUTOMULE SETTINGS #### */ @@ -867,7 +875,8 @@ Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved Config.AutoSkill.Build = []; - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + /** + * AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. * * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index b39ee616a..df4d77272 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -1,6 +1,6 @@ /** * @filename Cubing.js -* @author kolton +* @author kolton, theBGuy * @desc transmute Horadric Cube recipes * */ @@ -96,18 +96,211 @@ const Recipe = { } }; +/** + * @memberof Recipe + * @function ingredients + * @returns {number[]} + */ +Object.defineProperty(Recipe, "ingredients", { + /** + * Get list of ingredients needed for certain recipe + * @param {number} index - Index of recipe to check + * @param {number} [keyItem] - Key item in cubing recipe + * @returns {number[]} + */ + value: function (index, keyItem) { + switch (index) { + case Recipe.Gem: + return [keyItem - 1, keyItem - 1, keyItem - 1]; + case Recipe.HitPower.Helm: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Boots: + return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Gloves: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Belt: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Shield: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Body: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Ring: + return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Weapon: + return [keyItem, sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Blood.Helm: + return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Boots: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Gloves: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Belt: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Shield: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Body: + return [keyItem, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Ring: + return [sdk.items.Ring, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Weapon: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Caster.Helm: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Boots: + return [keyItem, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Gloves: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Belt: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Shield: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Body: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Ring: + return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Weapon: + return [keyItem, sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Safety.Helm: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Boots: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Gloves: + return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Belt: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Shield: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Body: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Ring: + return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Weapon: + return [keyItem, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Unique.Weapon.ToExceptional: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald]; + case Recipe.Unique.Weapon.ToElite: // Ladder only + return [keyItem, sdk.items.runes.Lum, sdk.items.runes.Pul, sdk.items.gems.Perfect.Emerald]; + case Recipe.Unique.Armor.ToExceptional: + return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Shael, sdk.items.gems.Perfect.Diamond]; + case Recipe.Unique.Armor.ToElite: // Ladder only + return [keyItem, sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.gems.Perfect.Diamond]; + case Recipe.Rare.Weapon.ToExceptional: + return [keyItem, sdk.items.runes.Ort, sdk.items.runes.Amn, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Rare.Weapon.ToElite: + return [keyItem, sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Rare.Armor.ToExceptional: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Rare.Armor.ToElite: + return [keyItem, sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Socket.Shield: + return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby]; + case Recipe.Socket.Weapon: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Amn, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Socket.Armor: + return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz]; + case Recipe.Socket.Helm: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Reroll.Magic: // Hacky solution ftw + return [keyItem, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Rare: + return [keyItem, 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]; + case Recipe.Reroll.HighRare: + return [keyItem, sdk.items.gems.Perfect.Skull, sdk.items.Ring]; + case Recipe.LowToNorm.Weapon: + return [keyItem, sdk.items.runes.Eld, "cgem"]; + case Recipe.LowToNorm.Armor: + return [keyItem, sdk.items.runes.El, "cgem"]; + case Recipe.Rune: + switch (keyItem) { + 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: + return [keyItem - 1, keyItem - 1, keyItem - 1]; + case sdk.items.runes.Amn: // thul->amn + return [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz]; + case sdk.items.runes.Sol: // amn->sol + return [sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.gems.Chipped.Amethyst]; + case sdk.items.runes.Shael: // sol->shael + return [sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.gems.Chipped.Sapphire]; + case sdk.items.runes.Dol: // shael->dol + return [sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.gems.Chipped.Ruby]; + case sdk.items.runes.Hel: // dol->hel + return [sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.gems.Chipped.Emerald]; + case sdk.items.runes.Io: // hel->io + return [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.gems.Chipped.Diamond]; + case sdk.items.runes.Lum: // io->lum + return [sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.gems.Flawed.Topaz]; + case sdk.items.runes.Ko: // lum->ko + return [sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.gems.Flawed.Amethyst]; + case sdk.items.runes.Fal: // ko->fal + return [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.gems.Flawed.Sapphire]; + case sdk.items.runes.Lem: // fal->lem + return [sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.gems.Flawed.Ruby]; + case sdk.items.runes.Pul: // lem->pul + return [sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.gems.Flawed.Emerald]; + case sdk.items.runes.Um: // pul->um + return [sdk.items.runes.Pul, sdk.items.runes.Pul, sdk.items.gems.Flawed.Diamond]; + case sdk.items.runes.Mal: // um->mal + return [sdk.items.runes.Um, sdk.items.runes.Um, sdk.items.gems.Normal.Topaz]; + case sdk.items.runes.Ist: // mal->ist + return [sdk.items.runes.Mal, sdk.items.runes.Mal, sdk.items.gems.Normal.Amethyst]; + case sdk.items.runes.Gul: // ist->gul + return [sdk.items.runes.Ist, sdk.items.runes.Ist, sdk.items.gems.Normal.Sapphire]; + case sdk.items.runes.Vex: // gul->vex + return [sdk.items.runes.Gul, sdk.items.runes.Gul, sdk.items.gems.Normal.Ruby]; + case sdk.items.runes.Ohm: // vex->ohm + return [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.gems.Normal.Emerald]; + case sdk.items.runes.Lo: // ohm->lo + return [sdk.items.runes.Ohm, sdk.items.runes.Ohm, sdk.items.gems.Normal.Diamond]; + case sdk.items.runes.Sur: // lo->sur + return [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz]; + case sdk.items.runes.Ber: // sur->ber + return [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst]; + case sdk.items.runes.Jah: // ber->jah + return [sdk.items.runes.Ber, sdk.items.runes.Ber, sdk.items.gems.Flawless.Sapphire]; + case sdk.items.runes.Cham: // jah->cham + return [sdk.items.runes.Jah, sdk.items.runes.Jah, sdk.items.gems.Flawless.Ruby]; + case sdk.items.runes.Zod: // cham->zod + return [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald]; + } + + break; + case Recipe.Token: + return [sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction]; + } + return []; + }, + enumerable: false, +}); + const Cubing = { recipes: [], gemList: [], - chippedGems: [ - sdk.items.gems.Chipped.Amethyst, sdk.items.gems.Chipped.Topaz, sdk.items.gems.Chipped.Sapphire, sdk.items.gems.Chipped.Emerald, - sdk.items.gems.Chipped.Ruby, sdk.items.gems.Chipped.Diamond, sdk.items.gems.Chipped.Skull - ], + gems: (() => ({ + chipped: Object.values(sdk.items.gems.Chipped), + flawed: Object.values(sdk.items.gems.Flawed), + normal: Object.values(sdk.items.gems.Normal), + flawless: Object.values(sdk.items.gems.Flawless), + perfect: Object.values(sdk.items.gems.Perfect), + }))(), init: function () { if (!Config.Cubing) return; - //print("We have " + Config.Recipes.length + " cubing recipe(s)."); + // print("We have " + Config.Recipes.length + " cubing recipe(s)."); for (let i = 0; i < Config.Recipes.length; i += 1) { if (Config.Recipes[i].length > 1 && isNaN(Config.Recipes[i][1])) { @@ -128,10 +321,7 @@ const Cubing = { }, buildGemList: function () { - let gemList = [ - sdk.items.gems.Perfect.Amethyst, sdk.items.gems.Perfect.Topaz, sdk.items.gems.Perfect.Sapphire, - sdk.items.gems.Perfect.Emerald, sdk.items.gems.Perfect.Ruby, sdk.items.gems.Perfect.Diamond, sdk.items.gems.Perfect.Skull - ]; + let gemList = Cubing.gems.perfect.slice(); for (let i = 0; i < this.recipes.length; i += 1) { // Skip gems and other magic rerolling recipes @@ -144,42 +334,15 @@ const Cubing = { } } - this.gemList = gemList.slice(0); + Cubing.gemList = gemList.slice(0); return true; }, - getCube: function () { - // Don't activate from townchicken - if (getScript(true).name === "tools\\townchicken.js") return false; - // Can't get the cube if we can't access the act - if (!Pather.accessToAct(2)) return false; - - console.log("Getting cube"); - me.overhead("Getting cube"); - let cube; - - Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); - Precast.doPrecast(true); - - if (Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) && Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { - let chest = Game.getObject(sdk.quest.chest.HoradricCubeChest); - - if (chest) { - Misc.openChest(chest); - Misc.poll(function () { - cube = Game.getItem(sdk.quest.item.Cube); - return !!cube && Pickit.pickItem(cube); - }, 1000, 2000); - } - } - - Town.goToTown(); - cube = me.getItem(sdk.quest.item.Cube); - - return (!!cube && Storage.Stash.MoveTo(cube)); - }, - + /** + * @todo + * - Allow passing in ilvl + */ buildRecipes: function () { Cubing.recipes = []; @@ -188,230 +351,209 @@ const Cubing = { throw new Error("Cubing.buildRecipes: Invalid recipe format."); } - switch (Config.Recipes[i][0]) { + /** @type {number[]} */ + let [index, keyItem] = Config.Recipes[i]; + const ingredients = Recipe.ingredients(index, keyItem); + + switch (index) { 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: ingredients, Index: index, AlwaysEnabled: true}); break; 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: ingredients, Level: 84, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 79, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 82, Index: index}); 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: ingredients, Level: 85, Index: index}); 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: ingredients, Level: 90, Index: index}); 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: ingredients, Level: 77, Index: index}); 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: ingredients, Level: 85, Index: index}); 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: ingredients, Level: 84, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 79, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 82, Index: index}); 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: ingredients, Level: 85, Index: index}); 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: ingredients, Level: 90, Index: index}); 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: ingredients, Level: 77, Index: index}); 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: ingredients, Level: 85, Index: index}); 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: ingredients, Level: 84, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 79, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 82, Index: index}); 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: ingredients, Level: 85, Index: index}); 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: ingredients, Level: 90, Index: index}); 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: ingredients, Level: 77, Index: index}); 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: ingredients, Level: 85, Index: index}); 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: ingredients, Level: 84, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 79, Index: index}); 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: ingredients, Level: 71, Index: index}); 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: ingredients, Level: 82, Index: index}); 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: ingredients, Level: 85, Index: index}); 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: ingredients, Level: 90, Index: index}); 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: ingredients, Level: 77, Index: index}); 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: ingredients, Level: 85, Index: index}); break; 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: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); break; case Recipe.Unique.Weapon.ToElite: // Ladder only if (me.ladder) { - 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: ingredients, Index: index, 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: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); break; case Recipe.Unique.Armor.ToElite: // Ladder only if (me.ladder) { - 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: ingredients, Index: index, 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; 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]}); + this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); break; 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: ingredients, Level: 91, Index: index}); 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: ingredients, Index: index}); 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: ingredients, Index: index, 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}); + this.recipes.push({Ingredients: ingredients, Index: index}); break; 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: @@ -420,136 +562,38 @@ const Cubing = { 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - this.recipes.push({Ingredients: [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz], Index: Recipe.Rune}); - } + this.recipes.push({Ingredients: ingredients, Index: index, AlwaysEnabled: true}); break; - case sdk.items.runes.Sur: // sur->ber - if (me.ladder) { - this.recipes.push({Ingredients: [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst], Index: Recipe.Rune}); - } + case sdk.items.runes.Thul: + case sdk.items.runes.Amn: + case sdk.items.runes.Sol: + case sdk.items.runes.Shael: + case sdk.items.runes.Dol: + this.recipes.push({Ingredients: ingredients, Index: index}); break; - case sdk.items.runes.Ber: // ber->jah + case sdk.items.runes.Hel: + case sdk.items.runes.Io: + case sdk.items.runes.Lum: + case sdk.items.runes.Ko: + case sdk.items.runes.Fal: + case sdk.items.runes.Lem: + case sdk.items.runes.Pul: + case sdk.items.runes.Um: + case sdk.items.runes.Mal: + case sdk.items.runes.Ist: + case sdk.items.runes.Gul: + case sdk.items.runes.Vex: + case sdk.items.runes.Ohm: + case sdk.items.runes.Lo: + case sdk.items.runes.Sur: + case sdk.items.runes.Ber: + case sdk.items.runes.Jah: + case sdk.items.runes.Cham: + case sdk.items.runes.Zod: if (me.ladder) { - 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) { - 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) { - this.recipes.push({Ingredients: [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald], Index: Recipe.Rune}); + this.recipes.push({Ingredients: ingredients, Index: index}); } break; @@ -557,7 +601,7 @@ const Cubing = { 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: ingredients, Index: index, AlwaysEnabled: true}); break; } @@ -583,15 +627,14 @@ const Cubing = { 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] === "cgem" && this.chippedGems.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "cgem" && this.gems.chipped.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({classid: items[k].classid, gid: items[k].gid}); + this.validIngredients.push({ classid: items[k].classid, gid: items[k].gid }); // 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 @@ -605,55 +648,23 @@ const Cubing = { } // 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) { break; } - // if the recipe is enabled (we have the main item), add flawless gem recipes (if needed) - - // Make perf amethyst - 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 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 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 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 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 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 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); + // if the recipe is enabled (we have the main item), add gem recipes (if needed) + for (let gType of Object.values(Cubing.gems)) { + // skip over cgems - can't cube them + if (gType.includes(sdk.items.gems.Chipped.Amethyst)) continue; + for (let gem of gType) { + if (this.subRecipes.indexOf(gem) === -1 && (this.recipes[i].Ingredients[j] === gem || (this.recipes[i].Ingredients[j] === "pgem" && Cubing.gemList.includes(gem)))) { + this.recipes.push({ Ingredients: [gem - 1, gem - 1, gem - 1], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(gem); + } + } } } } @@ -686,7 +697,7 @@ const Cubing = { if (usedGids.indexOf(this.validIngredients[j].gid) === -1 && ( this.validIngredients[j].classid === recipe.Ingredients[i] || (recipe.Ingredients[i] === "pgem" && this.gemList.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "cgem" && this.chippedGems.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "cgem" && this.gems.chipped.includes(this.validIngredients[j].classid)) )) { let item = me.getItem(this.validIngredients[j].classid, -1, this.validIngredients[j].gid); @@ -710,7 +721,11 @@ const Cubing = { return matchList; }, - // debug function - get what each recipe needs + /** + * debug function - get what each recipe needs + * @param {number} index + * @returns {string} + */ getRecipeNeeds: function (index) { let rval = " ["; @@ -725,7 +740,11 @@ const Cubing = { return rval; }, - // Check an item on ground for pickup + /** + * Check an item on ground for pickup + * @param {ItemUnit} unit + * @returns {boolean} + */ checkItem: function (unit) { if (!Config.Cubing) return false; if (this.keepItem(unit)) return true; @@ -740,7 +759,11 @@ const Cubing = { return false; }, - // Don't drop an item from inventory if it's a part of cubing recipe + /** + * Don't drop an item from inventory if it's a part of cubing recipe + * @param {ItemUnit} unit + * @returns {boolean} + */ keepItem: function (unit) { if (!Config.Cubing) return false; @@ -753,6 +776,12 @@ const Cubing = { return false; }, + /** + * Check if this item is valid for a given recipe + * @param {ItemUnit} unit + * @param {*} recipe + * @returns {boolean} + */ validItem: function (unit, recipe) { // Excluded items // Don't use items in locked inventory space - or wanted by other systems @@ -847,7 +876,7 @@ const Cubing = { doCubing: function () { if (!Config.Cubing) return false; - if (!me.getItem(sdk.quest.item.Cube) && !this.getCube()) return false; + if (!me.getItem(sdk.quest.item.Cube)) return false; this.update(); // Randomize the recipe array to prevent recipe blocking (multiple caster items etc.) @@ -883,21 +912,32 @@ const Cubing = { if (items) { for (let j = 0; j < cubeItems.length; j += 1) { - let result = Pickit.checkItem(cubeItems[j]); + let cubeItem = cubeItems[j]; + let result = Pickit.checkItem(cubeItem); + + /** + * @todo + * - build better method of updating cubelist so if a item we cube is wanted by cubing we + * can update our list without clearing and rebuilding the whole thing + */ switch (result.result) { case Pickit.Result.UNWANTED: - Item.logger("Dropped", cubeItems[j], "doCubing"); - cubeItems[j].drop(); + Item.logger("Dropped", cubeItem, "doCubing"); + cubeItem.drop(); break; case Pickit.Result.WANTED: - Item.logger("Cubing Kept", cubeItems[j]); - Item.logItem("Cubing Kept", cubeItems[j], result.line); + Item.logger("Cubing Kept", cubeItem); + Item.logItem("Cubing Kept", cubeItem, result.line); + + break; + case Pickit.Result.RUNEWORD: + Runewords.update(cubeItem.classid, cubeItem.gid); break; case Pickit.Result.CRAFTING: - CraftingSystem.update(cubeItems[j]); + CraftingSystem.update(cubeItem); break; } @@ -910,6 +950,17 @@ const Cubing = { } } + /** + * For now, until I write a better update method, give a recursive call to doCubing if after building list + * we find we can still cube + */ + Cubing.update(); + let checkList = this.recipes.slice().shuffle(); + if (checkList.some(r => Cubing.checkRecipe(r))) { + // we can still cube so recursive call to doCubing + return Cubing.doCubing(); + } + if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { delay(1000); diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index bea96e6e7..7d728b382 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -5,8 +5,6 @@ * */ -let global = this; - const Loader = { fileList: [], scriptList: [], @@ -97,6 +95,12 @@ const Loader = { } } + // handle getting cube here instead of from Cubing.doCubing + if (Config.Cubing && !me.getItem(sdk.quest.item.Cube) && Pather.accessToAct(2)) { + // we can actually get the cube - fixes bug causing level 1's to crash + Loader.runScript("GetCube"); + } + for (Loader.scriptIndex = 0; Loader.scriptIndex < Loader.scriptList.length; Loader.scriptIndex++) { let script = this.scriptList[this.scriptIndex]; diff --git a/d2bs/kolbot/libs/scripts/GetCube.js b/d2bs/kolbot/libs/scripts/GetCube.js new file mode 100644 index 000000000..7ff6efa6e --- /dev/null +++ b/d2bs/kolbot/libs/scripts/GetCube.js @@ -0,0 +1,34 @@ +/** +* @filename GetCube.js +* @author theBGuy +* @desc Go get the cube from Halls if we don't have it are enabled to cube +* +*/ + +function GetCube() { + // Can't get the cube if we can't access the act + if (!Pather.accessToAct(2)) return false; + + console.log("Getting cube"); + me.overhead("Getting cube"); + + Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); + Precast.doPrecast(true); + + if (Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) && Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricCubeChest)) { + let chest = Game.getObject(sdk.quest.chest.HoradricCubeChest); + + if (chest) { + Misc.openChest(chest); + Misc.poll(function () { + let cube = Game.getItem(sdk.quest.item.Cube); + return !!cube && Pickit.pickItem(cube); + }, 1000, 2000); + } + } + + Town.goToTown(); + let cube = me.getItem(sdk.quest.item.Cube); + + return (!!cube && Storage.Stash.MoveTo(cube)); +} From 2f2573b01a93d01389071416d25a378da0d6ff40 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 24 Feb 2023 00:18:10 -0500 Subject: [PATCH 057/758] Update Packet.js - Fix itemToCursor for belt items - Add `Packet.castAndHoldSkill` meant to be used with Inferno/Artic blast to perform shift hold --- d2bs/kolbot/libs/core/Packet.js | 41 ++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 8f10cd05c..f4222188e 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -233,6 +233,10 @@ const Packet = { return false; }, + /** + * @param {ItemUnit} item + * @returns {boolean} + */ itemToCursor: function (item) { // Something already on cursor if (me.itemoncursor) { @@ -246,7 +250,11 @@ const Packet = { for (let i = 0; i < 15; i += 1) { // equipped - item.isEquipped ? sendPacket(1, sdk.packets.send.PickupBodyItem, 2, item.bodylocation) : sendPacket(1, sdk.packets.send.PickupBufferItem, 4, item.gid); + item.isEquipped + ? sendPacket(1, sdk.packets.send.PickupBodyItem, 2, item.bodylocation) + : item.isInBelt + ? new PacketBuilder().byte(sdk.packets.send.RemoveBeltItem).dword(item.gid).send() + : sendPacket(1, sdk.packets.send.PickupBufferItem, 4, item.gid); let tick = getTickCount(); @@ -259,6 +267,10 @@ const Packet = { return false; }, + /** + * @param {ItemUnit} item + * @returns {boolean} + */ dropItem: function (item) { if (!this.itemToCursor(item)) return false; @@ -276,6 +288,10 @@ const Packet = { return false; }, + /** + * @param {ItemUnit} item + * @returns {boolean} + */ givePotToMerc: function (item) { if (!!item && [sdk.items.type.HealingPotion, sdk.items.type.RejuvPotion, sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion].includes(item.itemType)) { @@ -298,11 +314,21 @@ const Packet = { return false; }, + /** + * @param {ItemUnit} item + * @param {number} xLoc + * @returns {boolean} + */ placeInBelt: function (item, xLoc) { item.toCursor(true) && new PacketBuilder().byte(sdk.packets.send.ItemToBelt).dword(item.gid).dword(xLoc).send(); return Misc.poll(() => item.isInBelt, 500, 100); }, + /** + * @param {ItemUnit} who + * @param {boolean} toCursor + * @returns {boolean} + */ click: function (who, toCursor = false) { if (!who || !copyUnit(who).x) return false; new PacketBuilder().byte(sdk.packets.send.PickupItem).dword(sdk.unittype.Item).dword(who.gid).dword(toCursor ? 1 : 0).send(); @@ -344,6 +370,19 @@ const Packet = { sendPacket(1, hand, 2, wX, 2, wY); }, + castAndHoldSkill: function (hand, wX, wY, duration = 1000) { + let nHand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocation : sdk.packets.send.LeftSkillOnLocation; + hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocationEx : sdk.packets.send.LeftSkillOnLocationEx; + + let endT = getTickCount() + duration; + // has to be cast normally first with a click before held packet is sent + sendPacket(1, nHand, 2, wX, 2, wY); + while (getTickCount() < endT) { + sendPacket(1, hand, 2, wX, 2, wY); + delay(25); + } + }, + /** * @param {number} hand * @param {Monster | ItemUnit | ObjectUnit} who From 44cec5a59cea287a0edaffbd8bb1c945bc6a7729 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 24 Feb 2023 15:07:09 -0500 Subject: [PATCH 058/758] New cubing recipes - Add specific recipes for each charm type, so the ilvl is easier to control - Add socket low/high magic weapons - Add socket rare items - Add Rejuv recipes --- d2bs/kolbot/libs/config/_BaseConfigFile.js | 14 ++- d2bs/kolbot/libs/config/_CustomConfig.js | 4 +- d2bs/kolbot/libs/core/Cubing.js | 140 +++++++++++++++++++-- 3 files changed, 146 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index e386e36a8..0fdbbfa91 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -642,6 +642,7 @@ */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Flawed Amethyst"]); // make Flawed Amethyst @@ -678,6 +679,9 @@ // Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + // Config.Recipes.push([Recipe.Rejuv]); // Make Rejuv + // Config.Recipes.push([Recipe.FullRejuv]); // Make Full Rejuv + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js // Config.Recipes.push([Recipe.Rune, "Eld Rune"]); // Upgrade El to Eld @@ -753,8 +757,11 @@ // The gems not used by other recipes will be used for magic item rerolling. - // Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + // Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem (ilvl 91+) // Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + // Config.Recipes.push([Recipe.Reroll.Charm.Small]); // Reroll magic Small Charm (ilvl 94+) + // Config.Recipes.push([Recipe.Reroll.Charm.Large]); // Reroll magic Large Charm (ilvl 76+) + // Config.Recipes.push([Recipe.Reroll.Charm.Grand]); // Reroll magic Grand Charm (ilvl 77+) // the cubing formula: 6 Perfect Skulls + 1 Rare Item = 1 random low quality rare item of the same type // Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem @@ -771,6 +778,11 @@ // Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor // Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + // Config.Recipes.push([Recipe.Socket.Magic.LowWeapon, "Bone Wand"]); // Socket magic Bone Wand (ilvl < 30) + // Config.Recipes.push([Recipe.Socket.Magic.HighWeapon, "Swirling Crystal"]); // Socket magic Swirling Crystal (ilvl >= 30) + + // Config.Recipes.push([Recipe.Socket.Rare, "Diadem"]); // Socket rare Diadem + // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite diff --git a/d2bs/kolbot/libs/config/_CustomConfig.js b/d2bs/kolbot/libs/config/_CustomConfig.js index 86de0811e..78c85f3fc 100644 --- a/d2bs/kolbot/libs/config/_CustomConfig.js +++ b/d2bs/kolbot/libs/config/_CustomConfig.js @@ -1,4 +1,4 @@ -var CustomConfig = { +const CustomConfig = { /* Format: "Config_Filename_Without_Extension": ["array", "of", "profiles"] @@ -6,4 +6,4 @@ var CustomConfig = { */ -}; \ No newline at end of file +}; diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index df4d77272..ce4b4469c 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -11,6 +11,9 @@ const Roll = { NonEth: 2 }; +/** + * @todo Fix/refactor this, these numbers are all arbitrary anyway + */ const Recipe = { Gem: 0, HitPower: { @@ -81,19 +84,31 @@ const Recipe = { Shield: 45, Weapon: 46, Armor: 47, - Helm: 48 + Helm: 48, + Magic: { + LowWeapon: 59, + HighWeapon: 60, + }, + Rare: 61, }, Reroll: { Magic: 49, Rare: 50, - HighRare: 51 + HighRare: 51, + Charm: { + Small: 56, + Large: 57, + Grand: 58, + }, }, Rune: 52, Token: 53, LowToNorm: { Armor: 54, Weapon: 55 - } + }, + Rejuv: 62, + FullRejuv: 63, }; /** @@ -112,6 +127,7 @@ Object.defineProperty(Recipe, "ingredients", { switch (index) { case Recipe.Gem: return [keyItem - 1, keyItem - 1, keyItem - 1]; + // Crafting Recipes---------------------------------------------------------------------// case Recipe.HitPower.Helm: return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; case Recipe.HitPower.Boots: @@ -184,6 +200,7 @@ Object.defineProperty(Recipe, "ingredients", { return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; case Recipe.Safety.Weapon: return [keyItem, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + // Upgrading Recipes-----------------------------------------------------------------------------// case Recipe.Unique.Weapon.ToExceptional: return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald]; case Recipe.Unique.Weapon.ToElite: // Ladder only @@ -200,6 +217,7 @@ Object.defineProperty(Recipe, "ingredients", { return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst]; case Recipe.Rare.Armor.ToElite: return [keyItem, sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst]; + // Socketing Recipes-------------------------------------------------------------------------------// case Recipe.Socket.Shield: return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby]; case Recipe.Socket.Weapon: @@ -208,6 +226,19 @@ Object.defineProperty(Recipe, "ingredients", { return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz]; case Recipe.Socket.Helm: return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Socket.Magic.LowWeapon: + return [keyItem, "cgem", "cgem", "cgem"]; + case Recipe.Socket.Magic.HighWeapon: + return [keyItem, "fgem", "fgem", "fgem"]; + case Recipe.Socket.Rare: + return [keyItem, sdk.items.Ring, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull]; + // Re-rolling Recipes-------------------------------------------------------------------------------// + case Recipe.Reroll.Charm.Small: + return [sdk.items.SmallCharm, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Charm.Large: + return [sdk.items.LargeCharm, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Charm.Grand: + return [sdk.items.GrandCharm, "pgem", "pgem", "pgem"]; case Recipe.Reroll.Magic: // Hacky solution ftw return [keyItem, "pgem", "pgem", "pgem"]; case Recipe.Reroll.Rare: @@ -218,6 +249,7 @@ Object.defineProperty(Recipe, "ingredients", { return [keyItem, sdk.items.runes.Eld, "cgem"]; case Recipe.LowToNorm.Armor: return [keyItem, sdk.items.runes.El, "cgem"]; + // Rune Recipes--------------------------------------------------------------------------------------// case Recipe.Rune: switch (keyItem) { case sdk.items.runes.Eld: @@ -280,6 +312,10 @@ Object.defineProperty(Recipe, "ingredients", { break; case Recipe.Token: return [sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction]; + case Recipe.Rejuv: + return ["cgem", "hpot", "hpot", "hpot", "mpot", "mpot", "mpot"]; + case Recipe.FullRejuv: + return ["gem", "hpot", "hpot", "hpot", "mpot", "mpot", "mpot"]; } return []; }, @@ -287,6 +323,7 @@ Object.defineProperty(Recipe, "ingredients", { }); const Cubing = { + /** @type {recipeObj[]} */ recipes: [], gemList: [], gems: (() => ({ @@ -296,6 +333,10 @@ const Cubing = { flawless: Object.values(sdk.items.gems.Flawless), perfect: Object.values(sdk.items.gems.Perfect), }))(), + pots: { + healing: [sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, sdk.items.HealingPotion, sdk.items.GreaterHealingPotion], + mana: [sdk.items.MinorManaPotion, sdk.items.LightManaPotion, sdk.items.ManaPotion, sdk.items.GreaterManaPotion], + }, init: function () { if (!Config.Cubing) return; @@ -340,6 +381,15 @@ const Cubing = { }, /** + * @typedef recipeObj + * @property {number[] | string[]} Ingredients + * @property {number} Index + * @property {number} [Level] + * @property {number} [Ethereal] + * @property {boolean} [Enabled] + * @property {boolean} [AlwaysEnabled] + * + * * @todo * - Allow passing in ilvl */ @@ -360,6 +410,7 @@ const Cubing = { this.recipes.push({Ingredients: ingredients, Index: index, AlwaysEnabled: true}); break; + // Crafting Recipes--------------------------------------------------------------// case Recipe.HitPower.Helm: this.recipes.push({Ingredients: ingredients, Level: 84, Index: index}); @@ -504,6 +555,7 @@ const Cubing = { this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); break; + // Upgrading Recipes------------------------------------------------------------------------// case Recipe.Unique.Weapon.ToExceptional: this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); @@ -532,11 +584,36 @@ const Cubing = { case Recipe.Socket.Weapon: case Recipe.Socket.Armor: case Recipe.Socket.Helm: + case Recipe.Socket.Rare: this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); break; + case Recipe.Socket.Magic.LowWeapon: + // ilvl < 30 + this.recipes.push({Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2]}); + + break; + case Recipe.Socket.Magic.HighWeapon: + // ilvl >= 30 + this.recipes.push({Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2]}); + + break; + case Recipe.Reroll.Charm.Small: + case Recipe.Reroll.Charm.Large: + case Recipe.Reroll.Charm.Grand: case Recipe.Reroll.Magic: // Hacky solution ftw - this.recipes.push({Ingredients: ingredients, Level: 91, Index: index}); + /** + * Charm ilvls based on https://diablo2.diablowiki.net/Guide:Charms_v1.10,_by_Kronos + */ + if (index === Recipe.Reroll.Charm.Small) { + this.recipes.push({ Ingredients: ingredients, Level: 94, Index: index }); + } else if (index === Recipe.Reroll.Charm.Large) { + this.recipes.push({ Ingredients: ingredients, Level: 76, Index: index }); + } else if (index === Recipe.Reroll.Charm.Grand) { + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); + } else { + this.recipes.push({ Ingredients: ingredients, Level: 91, Index: index }); + } break; case Recipe.Reroll.Rare: @@ -603,6 +680,11 @@ const Cubing = { case Recipe.Token: this.recipes.push({Ingredients: ingredients, Index: index, AlwaysEnabled: true}); + break; + case Recipe.Rejuv: + case Recipe.FullRejuv: + this.recipes.push({ Ingredients: ingredients, Index: index }); + break; } } @@ -627,7 +709,11 @@ const Cubing = { 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" && this.gems.flawless.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "gem" && this.gems.normal.includes(items[k].classid)) || (this.recipes[i].Ingredients[j] === "cgem" && this.gems.chipped.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "hpot" && this.pots.healing.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "mpot" && this.pots.mana.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 @@ -638,9 +724,10 @@ const Cubing = { k -= 1; // Enable recipes for gem/jewel pickup - if (this.recipes[i].Index !== Recipe.Rune || (this.recipes[i].Index === Recipe.Rune && j >= 1)) { + if (this.recipes[i].Index !== Recipe.Rune || ([Recipe.Rune, Recipe.Rejuv, Recipe.FullRejuv].includes(this.recipes[i].Index) && j >= 1)) { // Enable rune recipe after 2 bases are found this.recipes[i].Enabled = true; + console.debug("Here?", this.recipes[i].Index); } continue IngredientLoop; @@ -688,6 +775,10 @@ const Cubing = { this.buildLists(); }, + /** + * @param {recipeObj} recipe + * @returns {boolean} + */ checkRecipe: function (recipe) { let usedGids = []; let matchList = []; @@ -697,7 +788,11 @@ const Cubing = { if (usedGids.indexOf(this.validIngredients[j].gid) === -1 && ( this.validIngredients[j].classid === recipe.Ingredients[i] || (recipe.Ingredients[i] === "pgem" && this.gemList.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "fgem" && this.gems.flawless.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "gem" && this.gems.normal.includes(this.validIngredients[j].classid)) || (recipe.Ingredients[i] === "cgem" && this.gems.chipped.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "hpot" && this.pots.healing.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "mpot" && this.pots.mana.includes(this.validIngredients[j].classid)) )) { let item = me.getItem(this.validIngredients[j].classid, -1, this.validIngredients[j].gid); @@ -741,7 +836,7 @@ const Cubing = { }, /** - * Check an item on ground for pickup + * Check an item on ground for pickup * @param {ItemUnit} unit * @returns {boolean} */ @@ -779,7 +874,7 @@ const Cubing = { /** * Check if this item is valid for a given recipe * @param {ItemUnit} unit - * @param {*} recipe + * @param {recipeObj} recipe * @returns {boolean} */ validItem: function (unit, recipe) { @@ -790,6 +885,20 @@ const Cubing = { return false; } + // Pots and Gems - for Rejuv recipes + if ([Recipe.Rejuv, Recipe.FullRejuv].includes(recipe.Index)) { + if (!recipe.Enabled) { + if (recipe.Index === Recipe.Rejuv && this.gems.chipped.includes(unit.classid)) return true; + if (recipe.Index === Recipe.FullRejuv && this.gems.normal.includes(unit.classid)) return true; + return false; + } + if ([].concat(Cubing.pots.healing, Cubing.pots.mana).includes(unit.classid)) { + return true; + } + + 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) { @@ -822,12 +931,25 @@ const Cubing = { let upgradeUnique = recipe.Index >= Recipe.Unique.Weapon.ToExceptional && recipe.Index <= Recipe.Unique.Armor.ToElite; let upgradeRare = recipe.Index >= Recipe.Rare.Weapon.ToExceptional && recipe.Index <= Recipe.Rare.Armor.ToElite; let socketNormal = recipe.Index >= Recipe.Socket.Shield && recipe.Index <= Recipe.Socket.Helm; + let socketMagic = [Recipe.Socket.Magic.LowWeapon, Recipe.Socket.Magic.HighWeapon].includes(recipe.Index); + let socketRare = recipe.Index === Recipe.Socket.Rare; - if (upgradeUnique || upgradeRare || socketNormal) { + if (socketRare && 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; + } + + if (upgradeUnique || upgradeRare || socketNormal || socketRare) { switch (true) { case upgradeUnique && unit.unique && ntipResult === Pickit.Result.WANTED: // Unique item matching pickit entry case upgradeRare && unit.rare && ntipResult === Pickit.Result.WANTED: // Rare item matching pickit entry case socketNormal && unit.normal && unit.sockets === 0: // Normal item matching pickit entry, no sockets + case socketMagic && unit.magic && unit.sockets === 0: // Magic item matching pickit entry, no sockets + case socketRare && unit.rare && unit.sockets === 0: // Rare item matching pickit entry, no sockets + if (socketMagic) { + if (recipe.Index === Recipe.Socket.Magic.LowWeapon && unit.ilvl > recipe.Level) return false; + if (recipe.Index === Recipe.Socket.Magic.HighWeapon && unit.ilvl < recipe.Level) return false; + } if (recipe.Ethereal === undefined) return ntipResult === Pickit.Result.WANTED; switch (recipe.Ethereal) { case Roll.All: @@ -844,7 +966,7 @@ const Cubing = { return false; } - if (recipe.Index === Recipe.Reroll.Magic) { + if (recipe.Index === Recipe.Reroll.Magic || (recipe.Index >= Recipe.Reroll.Charm.Small && recipe.Index <= Recipe.Reroll.Charm.Grand)) { return (unit.magic && unit.ilvl >= recipe.Level && ntipResult === Pickit.Result.UNWANTED); } From 68e92b4e27d6955c335d1b4e9fceb8551ab35e16 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Feb 2023 01:10:40 -0500 Subject: [PATCH 059/758] Update Cubing.js - remove debug line - fix adding recursive subrecipes - fix failing rejuv recipe --- d2bs/kolbot/libs/core/Cubing.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index ce4b4469c..ce9ad374b 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -727,7 +727,6 @@ const Cubing = { if (this.recipes[i].Index !== Recipe.Rune || ([Recipe.Rune, Recipe.Rejuv, Recipe.FullRejuv].includes(this.recipes[i].Index) && j >= 1)) { // Enable rune recipe after 2 bases are found this.recipes[i].Enabled = true; - console.debug("Here?", this.recipes[i].Index); } continue IngredientLoop; @@ -743,13 +742,16 @@ const Cubing = { } // if the recipe is enabled (we have the main item), add gem recipes (if needed) - for (let gType of Object.values(Cubing.gems)) { - // skip over cgems - can't cube them - if (gType.includes(sdk.items.gems.Chipped.Amethyst)) continue; - for (let gem of gType) { - if (this.subRecipes.indexOf(gem) === -1 && (this.recipes[i].Ingredients[j] === gem || (this.recipes[i].Ingredients[j] === "pgem" && Cubing.gemList.includes(gem)))) { - this.recipes.push({ Ingredients: [gem - 1, gem - 1, gem - 1], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(gem); + if (!this.recipes[i].hasOwnProperty("MainRecipe")) { + // make sure we don't add a subrecipe to a subrecipe + for (let gType of Object.values(Cubing.gems)) { + // skip over cgems - can't cube them + if (gType.includes(sdk.items.gems.Chipped.Amethyst)) continue; + for (let gem of gType) { + if (this.subRecipes.indexOf(gem) === -1 && (this.recipes[i].Ingredients[j] === gem || (this.recipes[i].Ingredients[j] === "pgem" && Cubing.gemList.includes(gem)))) { + this.recipes.push({ Ingredients: [gem - 1, gem - 1, gem - 1], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); + this.subRecipes.push(gem); + } } } } @@ -887,11 +889,17 @@ const Cubing = { // Pots and Gems - for Rejuv recipes if ([Recipe.Rejuv, Recipe.FullRejuv].includes(recipe.Index)) { + /** + * @todo do this better, hacky fix for now + */ if (!recipe.Enabled) { if (recipe.Index === Recipe.Rejuv && this.gems.chipped.includes(unit.classid)) return true; if (recipe.Index === Recipe.FullRejuv && this.gems.normal.includes(unit.classid)) return true; return false; } + + if (recipe.Index === Recipe.Rejuv && this.gems.chipped.includes(unit.classid)) return true; + if (recipe.Index === Recipe.FullRejuv && this.gems.normal.includes(unit.classid)) return true; if ([].concat(Cubing.pots.healing, Cubing.pots.mana).includes(unit.classid)) { return true; } From 3043e25036815155aa097239706744637f785993 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Feb 2023 01:16:55 -0500 Subject: [PATCH 060/758] Update .eslintrc.js - add object-curly-spacing rule, makes things easier to read --- .eslintrc.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 40feecf6f..f81bdf457 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -75,7 +75,6 @@ module.exports = { "getPlayerFlag": true, "clickParty": true, "dopen": true, - "NTIPAliasClassID": true, "Items": true, "Text": true, "File": true, @@ -95,17 +94,18 @@ module.exports = { "indent": ["warn", "tab"], "linebreak-style": ["off", "windows"], "semi": ["error", "always"], - "comma-spacing": ["error", {"before": false, "after": true}], - "keyword-spacing": ["error", {"before": true, "after": true}], - "brace-style": ["error", "1tbs", {"allowSingleLine": true}], + "comma-spacing": ["error", { "before": false, "after": true }], + "keyword-spacing": ["error", { "before": true, "after": true }], + "object-curly-spacing": ["error", "always"], + "brace-style": ["error", "1tbs", { "allowSingleLine": true }], "space-infix-ops": "error", - "space-unary-ops": ["error", {"words": true, "nonwords": false}], + "space-unary-ops": ["error", { "words": true, "nonwords": false }], "arrow-spacing": "error", "arrow-body-style": ["error", "as-needed"], "space-before-blocks": "error", - "key-spacing": ["error", {"beforeColon": false, "afterColon": true}], + "key-spacing": ["error", { "mode": "strict", "beforeColon": false, "afterColon": true }], "no-mixed-spaces-and-tabs": "error", - "no-trailing-spaces": ["warn", {"ignoreComments": true, "skipBlankLines": true}], + "no-trailing-spaces": ["warn", { "ignoreComments": true, "skipBlankLines": true }], "no-whitespace-before-property": "error", "comma-style": ["error", "last"], "eol-last": ["error", "always"], @@ -116,7 +116,7 @@ module.exports = { "eqeqeq": ["error", "smart"], "no-caller": "error", "no-floating-decimal": "error", - "no-multi-spaces": ["error", {"ignoreEOLComments": true }], + "no-multi-spaces": ["error", { "ignoreEOLComments": true }], "no-self-compare": "error", "no-case-declarations": "off", "no-with": "error", @@ -124,11 +124,11 @@ module.exports = { "no-use-before-define": "off", "no-prototype-builtins": "off", "quotes": ["warn", "double", { "avoidEscape": true }], - "no-constant-condition": ["error", {"checkLoops": false}], + "no-constant-condition": ["error", { "checkLoops": false }], "no-extra-label": "error", //"no-labels": ["error", {"allowLoop": true}], // in the future no loops ;) - "no-unused-vars": ["warn", {"vars": "local"}], - "no-fallthrough": ["error", {"commentPattern": "break[\\s\\w]*omitted"}], + "no-unused-vars": ["warn", { "vars": "local" }], + "no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted" }], "no-undef": ["off", "always"], "no-extra-boolean-cast": ["off", "always"], } From ea86a823b32b232584df0e880f42ae03f137315b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Feb 2023 01:27:51 -0500 Subject: [PATCH 061/758] Update Town.js - Add getAct to the NPC enum, use it in Town.npcInteract to ensure we don't attempt interacting with an npc from the wrong act - Add Cubing/Crafting/Runeword to possible items results we can shop for --- d2bs/kolbot/libs/core/Town.js | 96 +++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index e57d13e46..b49afa918 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -42,6 +42,36 @@ const NPC = { Cain: getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase() }; +Object.defineProperty(NPC, "getAct", { + /** + * Returns the act(s) where the given NPC can be found. + * + * @memberof NPC + * @method getAct + * @param {string} name - The name of the NPC. + * @returns {Array} An array of act numbers where the NPC can be found. + */ + value: function (name) { + if (name === NPC.Cain) return [me.act]; + if (name === NPC.Warriv) return [1, 2]; + if (name === NPC.Meshif) return [2, 3]; + switch (true) { + case [NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Warriv].includes(name): + return [1]; + case [NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Greiz, NPC.Lysander, NPC.Jerhyn, NPC.Atma].includes(name): + return [2]; + case [NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara].includes(name): + return [3]; + case [NPC.Jamella, NPC.Halbu, NPC.Tyrael].includes(name): + return [4]; + case [NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Nihlathak].includes(name): + return [2]; + } + return []; + }, + enumerable: false, +}); + /** * @namespace */ @@ -89,11 +119,11 @@ const Town = { }, tasks: [ - {Heal: NPC.Akara, Shop: NPC.Akara, Gamble: NPC.Gheed, Repair: NPC.Charsi, Merc: NPC.Kashya, Key: NPC.Akara, CainID: NPC.Cain}, - {Heal: NPC.Fara, Shop: NPC.Drognan, Gamble: NPC.Elzix, Repair: NPC.Fara, Merc: NPC.Greiz, Key: NPC.Lysander, CainID: NPC.Cain}, - {Heal: NPC.Ormus, Shop: NPC.Ormus, Gamble: NPC.Alkor, Repair: NPC.Hratli, Merc: NPC.Asheara, Key: NPC.Hratli, CainID: NPC.Cain}, - {Heal: NPC.Jamella, Shop: NPC.Jamella, Gamble: NPC.Jamella, Repair: NPC.Halbu, Merc: NPC.Tyrael, Key: NPC.Jamella, CainID: NPC.Cain}, - {Heal: NPC.Malah, Shop: NPC.Malah, Gamble: NPC.Anya, Repair: NPC.Larzuk, Merc: NPC.Qual_Kehk, Key: NPC.Malah, CainID: NPC.Cain} + { Heal: NPC.Akara, Shop: NPC.Akara, Gamble: NPC.Gheed, Repair: NPC.Charsi, Merc: NPC.Kashya, Key: NPC.Akara, CainID: NPC.Cain }, + { Heal: NPC.Fara, Shop: NPC.Drognan, Gamble: NPC.Elzix, Repair: NPC.Fara, Merc: NPC.Greiz, Key: NPC.Lysander, CainID: NPC.Cain }, + { Heal: NPC.Ormus, Shop: NPC.Ormus, Gamble: NPC.Alkor, Repair: NPC.Hratli, Merc: NPC.Asheara, Key: NPC.Hratli, CainID: NPC.Cain }, + { Heal: NPC.Jamella, Shop: NPC.Jamella, Gamble: NPC.Jamella, Repair: NPC.Halbu, Merc: NPC.Tyrael, Key: NPC.Jamella, CainID: NPC.Cain }, + { Heal: NPC.Malah, Shop: NPC.Malah, Gamble: NPC.Anya, Repair: NPC.Larzuk, Merc: NPC.Qual_Kehk, Key: NPC.Malah, CainID: NPC.Cain } ], ignoredItemTypes: [ @@ -153,6 +183,7 @@ const Town = { Cubing.doCubing(); Runewords.makeRunewords(); this.stash(true); + this.checkQuestItems(); !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); me.act !== preAct && this.goToTown(preAct); @@ -182,6 +213,11 @@ const Town = { const npcName = NPC[npcKey]; !me.inTown && Town.goToTown(); + + if (!NPC.getAct(npcName).includes(me.act)) { + Town.goToTown(NPC.getAct(npcName)[0]); + } + me.cancelUIFlags(); switch (npcName) { @@ -212,7 +248,6 @@ const Town = { if (npc && npc.openMenu()) { cancel && me.cancel(); - // this.lastInteractedNPC.set(npc); return npc; } @@ -278,7 +313,7 @@ const Town = { delay(250); let npc = null; - let justUseClosest = (["clearInventory", "sell"].includes(reason) && !me.getUnids()); + let justUseClosest = (["clearInventory", "sell"].includes(reason) && !me.getUnids().length); if (getUIFlag(sdk.uiflags.NPCMenu)) { npc = getInteractedNPC(); } @@ -290,7 +325,6 @@ const Town = { || (task === "Gamble" && npc.name.toLowerCase() === NPC.Jamella))) { me.cancelUIFlags(); npc = null; - // this.lastInteractedNPC.reset(); } } @@ -421,6 +455,10 @@ const Town = { } } + /** + * @todo If we are set to cube rejuvs, allow buying potions once we have our gem + */ + // We have enough potions in inventory (buffer.mp >= Config.MPBuffer && buffer.hp >= Config.HPBuffer) && (needBuffer = false); @@ -458,7 +496,7 @@ const Town = { let pot = this.getPotion(npc, Config.BeltColumn[i]); 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); @@ -589,7 +627,7 @@ const Town = { try { tome.buy(); } catch (e1) { - print(e1); + console.log(e1); // Couldn't buy the tome, don't spam the scrolls return false; } @@ -604,7 +642,7 @@ const Town = { try { scroll.buy(true); } catch (e2) { - print(e2.message); + console.log(e2.message); return false; } @@ -858,20 +896,25 @@ const Town = { let items = npc.getItemsEx().filter((item) => !Town.ignoreType(item.itemType)); if (!items.length) return false; - print("ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); + console.log("ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); for (let i = 0; i < items.length; i += 1) { - let result = Pickit.checkItem(items[i]); - - if (result.result === Pickit.Result.WANTED) { + let item = items[i]; + let result = Pickit.checkItem(item); + + switch (result.result) { + case Pickit.Result.WANTED: + case Pickit.Result.CUBING: + case Pickit.Result.CRAFTING: + case Pickit.Result.RUNEWORD: try { - if (Storage.Inventory.CanFit(items[i]) && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy)) { - Item.logger("Shopped", items[i]); - Item.logItem("Shopped", items[i], result.line); - items[i].buy(); + if (Storage.Inventory.CanFit(item) && me.gold >= item.getItemCost(sdk.items.cost.ToBuy)) { + Item.logger("Shopped", item); + Item.logItem("Shopped", item, result.line); + item.buy(); } } catch (e) { - print(e); + console.error(e); } } @@ -1144,7 +1187,7 @@ const Town = { }, checkKeys: function () { - if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 || (!me.getItem("key") && !Storage.Inventory.CanFit({sizex: 1, sizey: 1}))) { + if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { return 12; } @@ -1278,8 +1321,8 @@ const Town = { delay(me.ping * 2 + 500); if (cubeItems[0].bodylocation === bodyLoc) { - print(cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim() + " successfully repaired and equipped."); - D2Bot.printToConsole(cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim() + " successfully repaired and equipped.", sdk.colors.D2Bot.Green); + console.log(cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim() + " successfully repaired and equipped."); + D2Bot.console.logToConsole(cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim() + " successfully repaired and equipped.", sdk.colors.D2Bot.Green); return true; } @@ -1727,7 +1770,7 @@ const Town = { } if (getTickCount() - timer > 30000) { - D2Bot.printToConsole("Failed to get corpse, stopping.", sdk.colors.D2Bot.Red); + D2Bot.console.logToConsole("Failed to get corpse, stopping.", sdk.colors.D2Bot.Red); D2Bot.stop(); } @@ -1743,7 +1786,7 @@ const Town = { checkShard: function () { let shard; - let check = {left: false, right: false}; + let check = { left: false, right: false }; let item = me.getItem("bld", sdk.items.mode.inStorage); if (item) { @@ -1894,7 +1937,6 @@ const Town = { clearInventory: function () { console.info(true); console.time("clearInventory"); - this.checkQuestItems(); // only golden bird quest for now // If we are at an npc already, open the window otherwise moving potions around fails if (getUIFlag(sdk.uiflags.NPCMenu) && !getUIFlag(sdk.uiflags.Shop)) { @@ -2086,7 +2128,7 @@ const Town = { act: [{}, {}, {}, {}, {}], initialize: function () { - //print("Initialize town " + me.act); + //console.log("Initialize town " + me.act); switch (me.act) { case 1: From a4e61f2ab51a313cf05baef875bac5482d973061 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Feb 2023 01:29:12 -0500 Subject: [PATCH 062/758] Update Me.js - change getUnids to always return an empty array rather than false, makes checks on it easier --- d2bs/kolbot/libs/core/Me.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 6d0eb9bbd..c53709d79 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -293,7 +293,7 @@ me.getUnids = function () { let list = []; let item = me.getItem(-1, sdk.items.mode.inStorage); - if (!item) return false; + if (!item) return []; do { if (item.isInInventory && !item.identified) { @@ -301,7 +301,7 @@ me.getUnids = function () { } } while (item.getNext()); - return list.length ? list : false; + return list; }; // Identify items while in the field if we have a id tome From c917d7e06f56fb79785dc3aad645bc18308e91cb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Feb 2023 12:48:39 -0500 Subject: [PATCH 063/758] Update Pather.js - Allow `Pather.getPortal` to use a portal we own if its to the correct target area without having to specify who the owner is or pass in null as the second parameter - Use blaze if we have it, no reason to not do some extra damage if we are having to walk somewhere --- d2bs/kolbot/libs/core/Pather.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 56343ebfd..a61311c66 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -283,7 +283,7 @@ const Pather = { (target instanceof PresetUnit) && (target = target.realCoords()); if (settings.minDist > 3) { - target = this.spotOnDistance(target, settings.minDist, {returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2)}); + target = this.spotOnDistance(target, settings.minDist, { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) }); } let fail = 0; @@ -400,7 +400,7 @@ const Pather = { // 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"); @@ -563,7 +563,7 @@ const Pather = { // the less stamina you have, the more you wait to recover let recover = me.staminaMaxDuration < 30 ? 80 : 50; (me.walking && me.staminaPercent >= recover) && me.run(); - if (Skill.canUse(sdk.skills.Charge) && me.paladin && me.mp >= 9 && getDistance(me.x, me.y, x, y) > 8 && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { + if (Skill.canUse(sdk.skills.Charge) && me.paladin && me.mp >= 9 && [x, y].distance > 8 && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { if (Skill.canUse(sdk.skills.Vigor)) { Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); } else if (!Config.Vigor && !Attack.auradin && Skill.canUse(sdk.skills.HolyFreeze)) { @@ -575,6 +575,10 @@ const Pather = { delay(40); } } + + if (Skill.canUse(sdk.skills.Blaze) && me.mp > (Skill.getManaCost(sdk.skills.Blaze) * 2) && !me.getState(sdk.states.Blaze)) { + Skill.cast(sdk.skills.Blaze); + } } else { me.walking && me.run(); Skill.canUse(sdk.skills.Vigor) && Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); @@ -590,7 +594,7 @@ const Pather = { return true; } - if (attemptCount > 1 && CollMap.checkColl(me, {x: x, y: y}, sdk.collision.BlockWall | sdk.collision.ClosedDoor)) { + if (attemptCount > 1 && CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWall | sdk.collision.ClosedDoor)) { this.openDoors(me.x, me.y); } @@ -660,7 +664,7 @@ const Pather = { openDoors: function (x, y) { if (me.inTown && me.act !== 5) return false; - (typeof x !== "number" || typeof y !== "number") && ({x, y} = me); + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); // Regular doors let door = Game.getObject("door", sdk.objects.mode.Inactive); @@ -737,7 +741,7 @@ const Pather = { kickBarrels: function (x, y) { if (me.inTown) return false; - (typeof x !== "number" || typeof y !== "number") && ({x, y} = me); + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); // anything small and annoying really let barrels = getUnits(sdk.unittype.Object) @@ -1106,7 +1110,7 @@ const Pather = { if (!exits.length) throw new Error("Failed to find exits"); let loc = exits.find(a => a.target === exit); console.debug(area, exit, loc); - return loc ? {x: loc.x, y: loc.y} : false; + return loc ? { x: loc.x, y: loc.y } : false; }, @@ -1617,12 +1621,11 @@ const Pather = { me.cancelUIFlags(); - let preArea = me.area; for (let i = 0; i < 10; i += 1) { if (me.dead) return false; - i > 0 && owner && me.inTown && Town.move("portalspot"); + i > 0 && me.inTown && Town.move("portalspot"); let portal = unit ? copyUnit(unit) : this.getPortal(targetArea, owner); @@ -1716,7 +1719,7 @@ const Pather = { if (typeof targetArea !== "number" || portal.objtype === targetArea) { switch (owner) { case undefined: // Pather.usePortal(area) - red portal - if (!portal.getParent()) { + if (!portal.getParent() || portal.getParent() === me.name) { return copyUnit(portal); } @@ -2107,7 +2110,7 @@ const Pather = { sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath ]; let visitedNodes = []; - let toVisitNodes = [{from: dest, to: null}]; + let toVisitNodes = [{ from: dest, to: null }]; !src && (src = me.area); @@ -2142,13 +2145,13 @@ const Pather = { } if ((prevArea = previousAreas[node.from]) !== 0 && visitedNodes.indexOf(prevArea) === -1) { - toVisitNodes.push({from: prevArea, to: node.from}); + toVisitNodes.push({ from: prevArea, to: node.from }); } for (prevArea = 1; prevArea < previousAreas.length; prevArea += 1) { // Only interested in those connected to node if (previousAreas[prevArea] === node.from && visitedNodes.indexOf(prevArea) === -1) { - toVisitNodes.push({from: prevArea, to: node.from}); + toVisitNodes.push({ from: prevArea, to: node.from }); } } } @@ -2172,7 +2175,7 @@ const Pather = { return false; } - return {course: arr, useWP: useWP}; + return { course: arr, useWP: useWP }; }, /** From 29ef3c028a02acd48c95789520c2e7e0085e8aed Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 Feb 2023 13:48:31 -0500 Subject: [PATCH 064/758] Debug/Warn llines --- d2bs/kolbot/libs/core/Pickit.js | 14 ++++++++------ d2bs/kolbot/libs/core/Town.js | 5 ++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index efcf44ce0..35a6be00e 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -248,7 +248,7 @@ const Pickit = { /** * @param {ItemUnit} unit - * @returns {PickitResult} + * @returns { { result: PickitResult, line: string } } * -1 : Needs iding, * 0 : Unwanted, * 1 : NTIP wants, @@ -526,6 +526,7 @@ const Pickit = { if (me.dead) return false; let needMule = false; + let canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); // why wait for idle? while (!me.idle) { @@ -584,16 +585,17 @@ const Pickit = { } // Town visit failed - abort - console.log("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); + console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); return false; } // Can't make room - trigger automule Item.logger("No room for", itemToPick); - console.log("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); + console.warn("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); if (copyUnit(itemToPick).x !== undefined) { + canUseMule && console.debug("Attempt to trigger automule"); needMule = true; break; @@ -601,15 +603,15 @@ const Pickit = { } // Item can fit - pick it up - if (canFit && this.pickItem(itemToPick, status.result, status.line)) { - // what should we do if we failed to pick it? + if (canFit && !this.pickItem(itemToPick, status.result, status.line)) { + console.warn("Failed to pick item " + item.prettyPrint); } } } } // 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) { scriptBroadcast("mule"); scriptBroadcast("quit"); } diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index b49afa918..50e0e86f1 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -2435,7 +2435,10 @@ const Town = { return true; } - if (!me.canTpToTown()) return false; + if (!me.canTpToTown()) { + console.warn("Unable to visit town"); + return false; + } let preArea = me.area; let preAct = me.act; From 2fc5a513978b23d21a471916e6b8fd29293b45b7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 Feb 2023 23:36:03 -0500 Subject: [PATCH 065/758] Redo Storage.js - Add sorting - This is implementing #211 by esd1, with a lot of re-write Co-Authored-By: esd1 <60323803+esd1@users.noreply.github.com> --- d2bs/kolbot/libs/core/Config.js | 35 +- d2bs/kolbot/libs/core/Storage.js | 732 ++++++++++++------ d2bs/kolbot/libs/core/Town.js | 2 + d2bs/kolbot/libs/systems/automule/AutoMule.js | 30 +- 4 files changed, 544 insertions(+), 255 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index d5c3b3361..e8d7c9bff 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -183,6 +183,29 @@ let Config = { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ], + SortSettings: { + SortInventory: true, + SortStash: true, + PlugYStash: false, + 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 + ], + }, LocalChat: { Enabled: false, Toggle: false, @@ -266,7 +289,7 @@ let Config = { PrimarySlot: -1, LogExperience: false, TownCheck: false, - PingQuit: [{Ping: 0, Duration: 0}], + PingQuit: [{ Ping: 0, Duration: 0 }], PacketShopping: false, // Fastmod @@ -393,6 +416,7 @@ let Config = { }, Tombs: { KillDuriel: false, + WalkClear: false, }, Eldritch: { OpenChest: false, @@ -575,8 +599,8 @@ let Config = { GetFade: false, MakeTorch: true, PreGame: { - Thawing: {Drink: 0, At: []}, - Antidote: {Drink: 0, At: []}, + Thawing: { Drink: 0, At: [] }, + Antidote: { Drink: 0, At: [] }, } }, Synch: { @@ -587,6 +611,11 @@ let Config = { Helper: false, Wait: 5 }, + TombLeech: { + Leader: "", + Helper: false, + Wait: 5 + }, TravincalLeech: { Leader: "", Helper: false, diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index 3e9c082d1..ac5031e40 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -1,240 +1,500 @@ /** * @filename Storage.js -* @author McGod, kolton (small kolbot related edits) -* @desc manage inventory, belt, stash, cube +* @author McGod, kolton, esd1, theBGuy +* @desc Manage our storage space, belt, stash, cube, inventory * */ (function() { - let Container = function (name, width, height, location) { - let h, w; - + /** + * @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; - // Initalize the buffer array for use, set all as empty. - for (h = 0; h < this.height; h += 1) { + for (let h = 0; h < this.height; h += 1) { this.buffer.push([]); - for (w = 0; w < this.width; w += 1) { + 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; - /* Container.Mark(item) - * Marks the item in the buffer, and adds it to the item list. - */ - this.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; + } - //Make sure it is in this container. - if (item.location !== this.location || (item.mode !== 0 && item.mode !== 2)) { - 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"); + } - //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; + 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); + } - //Add item to list. - this.itemList.push(copyUnit(item)); + 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; - }; + } - /* Container.isLocked(item) - * Checks if the item is in a locked spot - */ - this.IsLocked = function (item, baseRef) { - let h, w, reference; + let makeCubeSpot = this.MakeSpot(cube, { x: 0, y: 0 }, true); // NOTE: passing these in buffer order [h/x][w/y] - reference = baseRef.slice(0); + 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; + } - //Make sure it is in this container. - if (item.mode !== 0 || item.location !== this.location) { - return false; - } + return true; + }; - // Make sure the item is ours - if (!item.getParent() || item.getParent().type !== me.type || item.getParent().gid !== me.gid) { - return false; - } + /** + * @param {ItemUnit} item + */ + Container.prototype.CanFit = function (item) { + return (!!this.FindSpot(item)); + }; - //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"); - } + /** + * @param {number[]} itemIdsLeft + * @param {number[]} itemIdsRight + */ + Container.prototype.SortItems = function (itemIdsLeft, itemIdsRight) { + Storage.Reload(); - 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; - } - } + this.cubeSpot(this.name); + + let x, y, item, 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 } - } 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; - }; + item = this.itemList[this.buffer[x][y] - 1]; - this.Reset = function () { - let h, w; + if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { + continue; // dont touch the cube + } + + let [ix, iy] = [item.y, item.x]; // x and y are backwards! - for (h = 0; h < this.height; h += 1) { - for (w = 0; w < this.width; w += 1) { - this.buffer[h][w] = 0; + 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 } - } - this.itemList = []; - this.openPositions = this.height * this.width; - return true; - }; + if (this.location === sdk.storage.Inventory && this.IsLocked(item, Config.Inventory)) { + continue; // locked spot / item + } - /* Container.CanFit(item) - * Checks to see if we can fit the item in the buffer. - */ - this.CanFit = function (item) { - return (!!this.FindSpot(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, Config.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, Config.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 + } - /* Container.FindSpot(item) - * Finds a spot available in the buffer to place the item. - */ - this.FindSpot = function (item) { - let x, y, nx, ny; + // We moved an item so reload & restart + Storage.Reload(); + y = this.width - 0; - //Make sure it's a valid item - if (!item) { - return false; + break; // Loop again from begin } + } - Storage.Reload(); + // this.Dump(); - //Loop buffer looking for spot to place item. - for (y = 0; y < this.width - (item.sizex - 1); y += 1) { - Loop: - for (x = 0; x < this.height - (item.sizey - 1); x += 1) { - //Check if there is something in this spot. - if (this.buffer[x][y] > 0) { + 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 startX, startY, endX, endY, xDir = 1, yDir = 1; + + startX = 0; + startY = 0; + endX = this.width - (item.sizex - 1); + endY = this.height - (item.sizey - 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 (Config.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]) { + //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 ({ x: x, y: y }); } + } - return false; - }; + return false; + }; - /* Container.MoveTo(item) - * Takes any item and moves it into given buffer. - */ - this.MoveTo = function (item) { - let nPos, n, nDelay, cItem, cube; + /** + * @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 + || (Config.SortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 + && Config.SortSettings.ItemsSortedFromLeftPriority.indexOf(item.classid) === -1 + && !force)) { + return false; // only continue if the item is in the priority sort list + } - try { - //Can we even fit it in here? - nPos = this.FindSpot(item); + // 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 + } - if (!nPos) { - return false; - } + Storage.Reload(); - //Cube -> Stash, must place item in inventory first - if (item.location === 6 && this.location === 7 && !Storage.Inventory.MoveTo(item)) { - return false; - } + // 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 + } - //Can't deal with items on ground! - if (item.mode === 3) { - return false; + 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 } + } + } - //Item already on the cursor. - if (me.itemoncursor && item.mode !== 4) { - return false; - } + // Move any item(s) out of the way + if (itemsToMove.length) { + for (let i = 0; i < itemsToMove.length; i++) { + let reverseX = !(Config.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); - //Make sure stash is open - if (this.location === 7 && !Town.openStash()) { return false; } + } + } - //Pick to cursor if not already. - if (!item.toCursor()) { - 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 }); + }; - //Loop three times to try and place it. - for (n = 0; n < 5; n += 1) { - if (this.location === 6) { // place item into cube - cItem = Game.getCursorUnit(); - cube = me.getItem(sdk.quest.item.Cube); + /** + * @param {ItemUnit} item + * @param {number} mX + * @param {number} mY + */ + Container.prototype.MoveToSpot = function (item, mX, mY) { + let cItem, cube; - if (cItem !== null && cube !== null) { - sendPacket(1, sdk.packets.send.ItemToCube, 4, cItem.gid, 4, cube.gid); - } - } else if (this.location === 2) { - cItem = Game.getCursorUnit(); - if (cItem !== null) { - sendPacket(1, sdk.packets.send.ItemToBelt, 4, cItem.gid, 4, nPos.y); - } - } else { - clickItemAndWait(sdk.clicktypes.click.item.Left, nPos.y, nPos.x, this.location); - } + // 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; + } - nDelay = getTickCount(); + // 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; - while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 3 + 500)) { - if (!me.itemoncursor) { - print("Successfully placed " + item.name + " at X: " + nPos.x + " Y: " + nPos.y); - delay(200); + // Make sure stash is open + if (this.location === sdk.storage.Stash && !Town.openStash()) return false; - return true; - } + 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); - delay(10); - } + 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; } - return true; - } catch (e) { - return false; + let nDelay = getTickCount(); + + while ((getTickCount() - nDelay) < Math.max(1000, me.ping * 2 + 200)) { + if (!me.itemoncursor) return true; + + delay(10 + me.ping); + } } + + return false; }; - /* Container.Dump() - * Prints all known information about container. - */ - this.Dump = function () { - let x, y, string; + 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; - print(this.name + " has the width of " + this.width + " and the height of " + this.height); - print(this.name + " has " + this.itemList.length + " items inside, and has " + this.openPositions + " spots left."); + 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 = ""; @@ -242,88 +502,110 @@ string += (this.buffer[x][y] > 0) ? "ÿc1x" : "ÿc0o"; } - print(string); + console.log(string); } - }; + } - /* Container.UsedSpacePercent() - * Returns percentage of the container used. - */ - this.UsedSpacePercent = function () { - let x, y, - usedSpace = 0, - totalSpace = this.height * this.width; + console.log("ÿc9Storageÿc0: " + this.name + " has used " + this.UsedSpacePercent().toFixed(2) + "% of its total space"); + }; - Storage.Reload(); + Container.prototype.UsedSpacePercent = function () { + let usedSpace = 0; + let totalSpace = this.height * this.width; - for (x = 0; x < this.height; x += 1) { - for (y = 0; y < this.width; y += 1) { - if (this.buffer[x][y] > 0) { - usedSpace += 1; - } + 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; - }; + return usedSpace * 100 / totalSpace; + }; - /* Container.compare(reference) - * Compare given container versus the current one, return all new items in current buffer. - */ - this.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; } - }; - this.toSource = function () { - return this.buffer.toSource(); - }; + return itemList; + } catch (e) { + return false; + } }; - let Storage = new function () { - this.Init = function () { - this.StashY = me.gametype === 0 ? 4 : 8; + Container.prototype.toSource = function () { + return this.buffer.toSource(); + }; + + /** + * @type {storage} Storage + */ + const Storage = new function () { + this.Init = () => { + this.StashY = me.classic ? 4 : Config.SortSettings.PlugYStash ? 10 : 8; this.Inventory = new Container("Inventory", 10, 4, 3); this.TradeScreen = new Container("Inventory", 10, 4, 5); - this.Stash = new Container("Stash", 6, this.StashY, 7); + this.Stash = new Container("Stash", (Config.SortSettings.PlugYStash ? 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 = []; @@ -332,13 +614,10 @@ this.BeltSize = function () { let item = me.getItem(-1, sdk.items.mode.Equipped); // get equipped item - - if (!item) { // nothing equipped - return 1; - } + if (!item) return 1; // nothing equipped do { - if (item.bodylocation === 8) { // belt slot + if (item.bodylocation === sdk.body.Belt) { switch (item.code) { case "lbl": // sash case "vbl": // light belt @@ -363,30 +642,27 @@ this.TradeScreen.Reset(); let item = me.getItem(); - - if (!item) { - return false; - } + if (!item) return false; do { switch (item.location) { - case 3: + case sdk.storage.Inventory: this.Inventory.Mark(item); break; - case 5: + case sdk.storage.TradeWindow: this.TradeScreen.Mark(item); break; - case 2: + case sdk.storage.Belt: this.Belt.Mark(item); break; - case 6: + case sdk.storage.Cube: this.Cube.Mark(item); break; - case 7: + case sdk.storage.Stash: this.Stash.Mark(item); break; diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 50e0e86f1..aa940cafe 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1642,6 +1642,8 @@ const Town = { let items = Storage.Inventory.Compare(Config.Inventory); if (items) { + Config.SortSettings.SortStash && Storage.Stash.SortItems(); + for (let i = 0; i < items.length; i += 1) { if (this.canStash(items[i])) { let result = false; diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index 151ae1acb..6bb657104 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -116,7 +116,7 @@ const AutoMule = { let stopCheck = false; let once = false; - let muleInfo = {status: ""}; + let muleInfo = { status: "" }; let failCount = 0; let Controls = require("../../modules/Control"); @@ -138,7 +138,7 @@ const AutoMule = { muleInfo.status = "ready"; // If nothing received our copy data start the mule profile - } else if (!sendCopyData(null, muleObj.muleProfile, 10, JSON.stringify({profile: me.profile, mode: this.torchAnniCheck || 0})) && !muleObj.continuousMule) { + } else if (!sendCopyData(null, muleObj.muleProfile, 10, JSON.stringify({ profile: me.profile, mode: this.torchAnniCheck || 0 })) && !muleObj.continuousMule) { // if the mule profile isn't already running and there is a profile to be stopped, stop it before starting the mule profile if (!stopCheck && muleObj.stopProfile && !String.isEqual(me.profile, muleObj.stopProfile)) { D2Bot.stop(muleObj.stopProfile, muleObj.stopProfileKeyRelease); @@ -273,7 +273,7 @@ const AutoMule = { if (mode === 10) { switch (JSON.parse(msg).status) { case "report": // reply to status request - sendCopyData(null, muleObj.muleProfile, 12, JSON.stringify({status: status})); + sendCopyData(null, muleObj.muleProfile, 12, JSON.stringify({ status: status })); break; case "quit": // quit command @@ -335,6 +335,7 @@ const AutoMule = { throw new Error("Error - Unable to find mule character"); } } else { + console.debug("MuleProfile :: " + muleObj.muleProfile); sendCopyData(null, muleObj.muleProfile, 11, "begin"); } @@ -468,25 +469,8 @@ const AutoMule = { delay(1000); me.cancel(); - // handle sort - for now quick and dirty method - Town.openStash() && me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter(i => i.isInStash && i.classid !== sdk.quest.item.Cube) - .sort((a, b) => b.sizex * b.sizey - a.sizex * a.sizey || b.x - a.x || b.y - a.y) - .forEach(item => { - let spot = Storage.Stash.FindSpot(item); - if (spot) { - [spot.x, spot.y] = [spot.y, spot.x]; // remember it's backwards - if (spot.x > item.x) return; - if (spot.y > item.y && spot.x === item.x) return; - if (spot.x === item.x && spot.y === item.y) return; - - if (!Storage.Stash.MoveTo(item)) { - return; - } - - Storage.Reload(); - } - }); + // clean up stash + Storage.Stash.SortItems(); me.cancelUIFlags(); return true; @@ -771,8 +755,6 @@ const AutoMule = { * Get whether this is a normal mule or continous mule */ isContinousMule: function () { - console.debug(this.Mules); - console.debug(this.TorchAnniMules); for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { From 620d4e022e6f05802fb9e5fb6743b287846aa8d8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:16:21 -0500 Subject: [PATCH 066/758] Update Town.js - Fix typo --- d2bs/kolbot/libs/core/Town.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index aa940cafe..e07eb254e 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -65,7 +65,7 @@ Object.defineProperty(NPC, "getAct", { case [NPC.Jamella, NPC.Halbu, NPC.Tyrael].includes(name): return [4]; case [NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Nihlathak].includes(name): - return [2]; + return [5]; } return []; }, From e0a4065ead7f9b11654334ba484a2d18f154be21 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:14:37 -0500 Subject: [PATCH 067/758] Update Baal.js - Add throneCoords and fix rare bug where checkThrone detected a monster in the side areas of throne and ran over there to clear it because thier y coordinate value was correctly < 5080 --- d2bs/kolbot/libs/core/Common/Baal.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 308ee00e5..410ee2b64 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -9,6 +9,16 @@ typeof Common !== "object" && (Common = {}); Object.defineProperty(Common, "Baal", { value: { + throneCoords: { + bottomLeft: { x: 15072, y: 5073 }, + bottomRight: { x: 15118, y: 5073 }, + bottomCenter: { x: 15093, y: 5073 }, + entraceArchway: { x: 15097, y: 5099 }, + topLeft: { x: 15072, y: 5002 }, + topRight: { x: 15118, y: 5002 }, + baal: { x: 15090, y: 5014 }, + }, + checkHydra: function () { let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); if (hydra) { @@ -35,7 +45,9 @@ if (monster) { do { - if (monster.attackable && monster.y < 5080) { + if (monster.attackable + && monster.y < 5080 + && (monster.x > 15072 && monster.x < 15118)) { switch (monster.classid) { case sdk.monsters.WarpedFallen: case sdk.monsters.WarpedShaman: @@ -94,7 +106,7 @@ ]; return pos.forEach((node) => { // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({range: 30}) === 0) { + if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { return; } Pather.moveTo(node.x, node.y); @@ -132,10 +144,10 @@ break; case sdk.player.class.Assassin: if (Config.UseTraps) { - let check = ClassAttack.checkTraps({x: 15094, y: 5028}); + let check = ClassAttack.checkTraps({ x: 15094, y: 5028 }); if (check) { - return ClassAttack.placeTraps({x: 15094, y: 5028}, 5); + return ClassAttack.placeTraps({ x: 15094, y: 5028 }, 5); } } @@ -256,12 +268,12 @@ killBaal: function () { if (me.inArea(sdk.areas.ThroneofDestruction)) { Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); - me.checkForMobs({range: 30}) && this.clearWaves(); // ensure waves are actually done + me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done Pather.moveTo(15090, 5008); delay(5000); Precast.doPrecast(true); Misc.poll(() => { - if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({range: 15})) { + if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({ range: 15 })) { Common.Baal.clearThrone(); Pather.moveTo(15090, 5008); } From 4f3e9cd5616b4d7998962f5f9ecabedc0c2518a9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Feb 2023 14:20:42 -0500 Subject: [PATCH 068/758] Linting mostly --- d2bs/kolbot/libs/core/Attack.js | 36 ++--- d2bs/kolbot/libs/core/Attacks/Amazon.js | 2 +- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 5 - d2bs/kolbot/libs/core/Attacks/Paladin.js | 2 +- d2bs/kolbot/libs/core/CollMap.js | 8 +- d2bs/kolbot/libs/core/Common/Diablo.js | 29 ++-- d2bs/kolbot/libs/core/Cubing.js | 104 ++++++------- d2bs/kolbot/libs/core/Misc.js | 63 ++++++-- d2bs/kolbot/libs/core/Prototypes.js | 14 +- d2bs/kolbot/libs/core/Skill.js | 10 +- d2bs/kolbot/libs/critical.js | 8 +- d2bs/kolbot/libs/modules/Team.js | 26 ++-- d2bs/kolbot/libs/modules/sdk.js | 6 + d2bs/kolbot/libs/oog/D2Bot.js | 2 +- d2bs/kolbot/libs/scripts/Duriel.js | 6 +- d2bs/kolbot/libs/scripts/Eldritch.js | 6 +- d2bs/kolbot/libs/scripts/GetKeys.js | 18 +-- d2bs/kolbot/sdk/globals.d.ts | 42 +++--- d2bs/kolbot/sdk/types/Town.d.ts | 1 + d2bs/kolbot/sdk/types/sdk.d.ts | 164 +++++++++++---------- d2bs/kolbot/threads/HeartBeat.js | 4 +- d2bs/kolbot/threads/RushThread.js | 14 +- d2bs/kolbot/threads/ToolsThread.js | 2 +- d2bs/kolbot/threads/TownChicken.js | 2 +- 24 files changed, 314 insertions(+), 260 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 7b50c32f0..642bc050c 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -208,10 +208,10 @@ const Attack = { Config.UseMerc && !me.mercrevivecost && (merc = Misc.poll(() => me.getMerc(), 1000, 100)); // Check merc infinity - !!merc && (Attack.infinity = merc.checkItem({name: sdk.locale.items.Infinity}).have); + !!merc && (Attack.infinity = merc.checkItem({ name: sdk.locale.items.Infinity }).have); // Check player infinity - only check if merc doesn't have - !Attack.infinity && (Attack.infinity = me.checkItem({name: sdk.locale.items.Infinity, equipped: true}).have); + !Attack.infinity && (Attack.infinity = me.checkItem({ name: sdk.locale.items.Infinity, equipped: true }).have); return Attack.infinity; }, @@ -222,8 +222,8 @@ const Attack = { */ checkAuradin: function () { Attack.auradin = me.haveSome([ - {name: sdk.locale.items.Dragon, equipped: true}, {name: sdk.locale.items.Dream, equipped: true}, - {name: sdk.locale.items.HandofJustice, equipped: true}, {name: sdk.locale.items.Ice, equipped: true}, + { name: sdk.locale.items.Dragon, equipped: true }, { name: sdk.locale.items.Dream, equipped: true }, + { name: sdk.locale.items.HandofJustice, equipped: true }, { name: sdk.locale.items.Ice, equipped: true }, ]); return Attack.auradin; @@ -274,7 +274,7 @@ const Attack = { let errorInfo = ""; let attackCount = 0; - let lastLoc = {x: me.x, y: me.y}; + let lastLoc = { x: me.x, y: me.y }; let tick = getTickCount(); console.log("ÿc7Kill ÿc0:: " + who); Config.MFLeader && Pather.makePortal() && say("kill " + classId); @@ -321,7 +321,7 @@ const Attack = { retry = 0; } - lastLoc = {x: me.x, y: me.y}; + lastLoc = { x: me.x, y: me.y }; attackCount++; } @@ -469,10 +469,10 @@ const Attack = { return Attack.clear(10); } - ({orgx, orgy} = { orgx: boss.x, orgy: boss.y }); + ({ orgx, orgy } = { orgx: boss.x, orgy: boss.y }); Config.MFLeader && !!bossId && Pather.makePortal() && say("clear " + (["number", "string"].includes(typeof bossId) ? bossId : bossId.name)); } else { - ({orgx, orgy} = { orgx: me.x, orgy: me.y }); + ({ orgx, orgy } = { orgx: me.x, orgy: me.y }); } let monsterList = []; @@ -494,7 +494,7 @@ const Attack = { while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { if (me.dead) 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]); target = Game.getMonster(-1, -1, monsterList[0].gid); @@ -531,7 +531,7 @@ const Attack = { } if (i === gidAttack.length) { - gidAttack.push({gid: target.gid, attacks: 0, name: target.name}); + gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); } gidAttack[i].attacks += 1; @@ -776,7 +776,7 @@ const Attack = { } if (i === gidAttack.length) { - gidAttack.push({gid: target.gid, attacks: 0}); + gidAttack.push({ gid: target.gid, attacks: 0 }); } gidAttack[i].attacks += 1; @@ -848,7 +848,7 @@ const Attack = { securePosition: function (x, y, range = 15, timer = 3000, skipBlocked = true, special = false) { let tick; - (typeof x !== "number" || typeof y !== "number") && ({x, y} = me); + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); skipBlocked === true && (skipBlocked = sdk.collision.Ranged); while (true) { @@ -1169,7 +1169,7 @@ const Attack = { */ openChests: function (range, x, y) { if (!Config.OpenChests.Enabled) return false; - (typeof x !== "number" || typeof y !== "number") && ({x, y} = me); + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); range === undefined && (range = 10); let list = []; @@ -1239,7 +1239,7 @@ const Attack = { grid.sort((a, b) => 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)) { + 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)) { let currCount = this.getMonsterCount(grid[i].x, grid[i].y, range, monList); if (currCount < count) { @@ -1307,7 +1307,7 @@ const Attack = { let coll = CollMap.getColl(i, j, true); if (typeof coll === "number") { - grid.push({x: i, y: j, coll: coll}); + grid.push({ x: i, y: j, coll: coll }); } } } @@ -1680,7 +1680,7 @@ const Attack = { // 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}); + coords.push({ x: cx, y: cy }); } } @@ -1691,7 +1691,7 @@ const Attack = { for (let i = 0; i < coords.length; i += 1) { // Valid position found - if (!CollMap.checkColl({x: coords[i].x, y: coords[i].y}, unit, coll, 1)) { + if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { // print("ÿc9optimal pos build time: ÿc2" + (getTickCount() - t) + " ÿc9distance from target: ÿc2" + getDistance(cx, cy, unit.x, unit.y)); if ((() => { switch (walk) { @@ -1802,7 +1802,7 @@ const Attack = { for (let i = 0; i < angles.length; i += 1) { let coords = [Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * 4 + unit.x), Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * 4 + unit.y)]; - if (!CollMap.checkColl(me, {x: coords[0], y: coords[1]}, sdk.collision.BlockWall, 1)) { + if (!CollMap.checkColl(me, { x: coords[0], y: coords[1] }, sdk.collision.BlockWall, 1)) { return Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, coords[0], coords[1]); } } diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index ab4673df8..7d5511095 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -187,7 +187,7 @@ const ClassAttack = { } if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { - this.lightFuryTick = getTickCount(); + ClassAttack.lightFuryTick = getTickCount(); } return Attack.Result.SUCCESS; diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index 6ade02a83..c12f95314 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -159,7 +159,6 @@ const ClassAttack = { findItem: function (range = 10) { if (!Skill.canUse(sdk.skills.FindItem)) return false; - let retry = false; let corpseList = []; const { x: orgX, y: orgY } = me; @@ -233,10 +232,6 @@ const ClassAttack = { } } - if (retry) { - return this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); - } - Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); return true; diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 56b7e8bbd..959e5985b 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -284,7 +284,7 @@ const ClassAttack = { 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 Pather.moveToEx(unit.x + cx, unit.y + cy, { clearSettings: { allowClearing: false } }); } } diff --git a/d2bs/kolbot/libs/core/CollMap.js b/d2bs/kolbot/libs/core/CollMap.js index c0dd0f802..d9d65239c 100644 --- a/d2bs/kolbot/libs/core/CollMap.js +++ b/d2bs/kolbot/libs/core/CollMap.js @@ -36,7 +36,7 @@ const CollMap = new function () { let coll = !!room ? room.getCollision() : null; if (coll) { - this.rooms.push({x: room.x, y: room.y, xsize: room.xsize, ysize: room.ysize}); + this.rooms.push({ x: room.x, y: room.y, xsize: room.xsize, ysize: room.ysize }); this.maps.push(coll); return true; @@ -134,7 +134,7 @@ const CollMap = new function () { for (let a = 0; a < collision.length; a++) { for (let b = 0; b < collision[a].length; b++) { if (!(collision[a][b] & 1)) { - validTiles.push({x: roomx + b - bMid, y: roomy + a - aMid, distance: getDistance(0, 0, a - aMid, b - bMid)}); + validTiles.push({ x: roomx + b - bMid, y: roomy + a - aMid, distance: getDistance(0, 0, a - aMid, b - bMid) }); } } } @@ -148,7 +148,7 @@ const CollMap = new function () { return null; } - return {x: roomx, y: roomy, distance: 0}; + return { x: roomx, y: roomy, distance: 0 }; }; this.getRandCoordinate = function (cX, xmin, xmax, cY, ymin, ymax, factor = 1) { @@ -177,6 +177,6 @@ const CollMap = new function () { } while (getCollision(me.area, coordX, coordY) & 1); // print("Move " + retry + " from (" + cX + ", " + cY + ") to (" + coordX + ", " + coordY + ")"); - return {x: coordX, y: coordY}; + return { x: coordX, y: coordY }; }; }; diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 05c212928..c88d34844 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -14,6 +14,13 @@ * I see this mostly with viz, also have seen seis fail due to not being close enough, even though he spawned */ Object.defineProperty(Common, "Diablo", { + /** + * @namespace Common + * + * @typedef Diablo + * @property {boolean} diabloSpawned + * + */ value: { diabloSpawned: false, diaWaitTime: Time.seconds(30), @@ -25,8 +32,8 @@ vizLayout: -1, seisLayout: -1, infLayout: -1, - entranceCoords: {x: 7790, y: 5544}, - starCoords: {x: 7791, y: 5293}, + entranceCoords: { x: 7790, y: 5544 }, + starCoords: { x: 7791, y: 5293 }, // path coordinates entranceToStar: [ [7794, 5517], [7791, 5491], [7768, 5459], @@ -261,8 +268,8 @@ if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { if (Config.UseTraps) { - let check = ClassAttack.checkTraps({x: 7899, y: 5293}); - check && ClassAttack.placeTraps({x: 7899, y: 5293}, check); + let check = ClassAttack.checkTraps({ x: 7899, y: 5293 }); + check && ClassAttack.placeTraps({ x: 7899, y: 5293 }, check); } } @@ -368,7 +375,7 @@ Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); return seis && (seis.distance < 30 || seis.dead); - }}); + } }); } Config.Diablo.SealLeader && say("out"); @@ -493,10 +500,10 @@ return this.hammerdinPreAttack(id, 8); case sdk.player.class.Assassin: if (Config.UseTraps) { - let trapCheck = ClassAttack.checkTraps({x: coords[0], y: coords[1]}); + let trapCheck = ClassAttack.checkTraps({ x: coords[0], y: coords[1] }); if (trapCheck) { - ClassAttack.placeTraps({x: coords[0], y: coords[1]}, 5); + ClassAttack.placeTraps({ x: coords[0], y: coords[1] }, 5); return true; } @@ -530,7 +537,7 @@ if (boss) { Common.Diablo.hammerdinPreAttack(name, 8); - return (Config.Diablo.Fast ? Attack.kill(name) : Attack.clear(40, 0, name, this.sort)); + return (Config.Diablo.Fast ? Attack.kill(boss) : Attack.clear(40, 0, boss, this.sort)); } delay(250); @@ -545,7 +552,7 @@ case sdk.player.class.Sorceress: case sdk.player.class.Necromancer: case sdk.player.class.Assassin: - return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), {returnSpotOnError: true}); + return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), { returnSpotOnError: true }); case sdk.player.class.Paladin: case sdk.player.class.Druid: case sdk.player.class.Barbarian: @@ -594,8 +601,8 @@ 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); diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index ce9ad374b..90f67dc98 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -407,172 +407,172 @@ const Cubing = { switch (index) { case Recipe.Gem: - this.recipes.push({Ingredients: ingredients, Index: index, AlwaysEnabled: true}); + this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); break; // Crafting Recipes--------------------------------------------------------------// case Recipe.HitPower.Helm: - this.recipes.push({Ingredients: ingredients, Level: 84, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); break; case Recipe.HitPower.Boots: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.HitPower.Gloves: - this.recipes.push({Ingredients: ingredients, Level: 79, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); break; case Recipe.HitPower.Belt: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.HitPower.Shield: - this.recipes.push({Ingredients: ingredients, Level: 82, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); break; case Recipe.HitPower.Body: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; case Recipe.HitPower.Amulet: - this.recipes.push({Ingredients: ingredients, Level: 90, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); break; case Recipe.HitPower.Ring: - this.recipes.push({Ingredients: ingredients, Level: 77, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); break; case Recipe.HitPower.Weapon: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; case Recipe.Blood.Helm: - this.recipes.push({Ingredients: ingredients, Level: 84, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); break; case Recipe.Blood.Boots: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.Blood.Gloves: - this.recipes.push({Ingredients: ingredients, Level: 79, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); break; case Recipe.Blood.Belt: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.Blood.Shield: - this.recipes.push({Ingredients: ingredients, Level: 82, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); break; case Recipe.Blood.Body: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; case Recipe.Blood.Amulet: - this.recipes.push({Ingredients: ingredients, Level: 90, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); break; case Recipe.Blood.Ring: - this.recipes.push({Ingredients: ingredients, Level: 77, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); break; case Recipe.Blood.Weapon: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; case Recipe.Caster.Helm: - this.recipes.push({Ingredients: ingredients, Level: 84, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); break; case Recipe.Caster.Boots: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.Caster.Gloves: - this.recipes.push({Ingredients: ingredients, Level: 79, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); break; case Recipe.Caster.Belt: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.Caster.Shield: - this.recipes.push({Ingredients: ingredients, Level: 82, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); break; case Recipe.Caster.Body: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; case Recipe.Caster.Amulet: - this.recipes.push({Ingredients: ingredients, Level: 90, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); break; case Recipe.Caster.Ring: - this.recipes.push({Ingredients: ingredients, Level: 77, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); break; case Recipe.Caster.Weapon: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; case Recipe.Safety.Helm: - this.recipes.push({Ingredients: ingredients, Level: 84, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); break; case Recipe.Safety.Boots: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.Safety.Gloves: - this.recipes.push({Ingredients: ingredients, Level: 79, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); break; case Recipe.Safety.Belt: - this.recipes.push({Ingredients: ingredients, Level: 71, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); break; case Recipe.Safety.Shield: - this.recipes.push({Ingredients: ingredients, Level: 82, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); break; case Recipe.Safety.Body: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; case Recipe.Safety.Amulet: - this.recipes.push({Ingredients: ingredients, Level: 90, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); break; case Recipe.Safety.Ring: - this.recipes.push({Ingredients: ingredients, Level: 77, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); break; case Recipe.Safety.Weapon: - this.recipes.push({Ingredients: ingredients, Level: 85, Index: index}); + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); break; // Upgrading Recipes------------------------------------------------------------------------// case Recipe.Unique.Weapon.ToExceptional: - this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Unique.Weapon.ToElite: // Ladder only if (me.ladder) { - this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); } break; case Recipe.Unique.Armor.ToExceptional: - this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Unique.Armor.ToElite: // Ladder only if (me.ladder) { - this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); } break; @@ -585,17 +585,17 @@ const Cubing = { case Recipe.Socket.Armor: case Recipe.Socket.Helm: case Recipe.Socket.Rare: - this.recipes.push({Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Socket.Magic.LowWeapon: // ilvl < 30 - this.recipes.push({Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Socket.Magic.HighWeapon: // ilvl >= 30 - this.recipes.push({Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2]}); + this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); break; case Recipe.Reroll.Charm.Small: @@ -617,16 +617,16 @@ const Cubing = { break; case Recipe.Reroll.Rare: - this.recipes.push({Ingredients: ingredients, Index: index}); + this.recipes.push({ Ingredients: ingredients, Index: index }); break; case Recipe.Reroll.HighRare: - this.recipes.push({Ingredients: ingredients, Index: index, Enabled: false}); + this.recipes.push({ Ingredients: ingredients, Index: index, Enabled: false }); break; case Recipe.LowToNorm.Weapon: case Recipe.LowToNorm.Armor: - this.recipes.push({Ingredients: ingredients, Index: index}); + this.recipes.push({ Ingredients: ingredients, Index: index }); break; case Recipe.Rune: @@ -639,7 +639,7 @@ const Cubing = { case sdk.items.runes.Tal: case sdk.items.runes.Ral: case sdk.items.runes.Ort: - this.recipes.push({Ingredients: ingredients, Index: index, AlwaysEnabled: true}); + this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); break; case sdk.items.runes.Thul: @@ -647,7 +647,7 @@ const Cubing = { case sdk.items.runes.Sol: case sdk.items.runes.Shael: case sdk.items.runes.Dol: - this.recipes.push({Ingredients: ingredients, Index: index}); + this.recipes.push({ Ingredients: ingredients, Index: index }); break; case sdk.items.runes.Hel: @@ -670,7 +670,7 @@ const Cubing = { case sdk.items.runes.Cham: case sdk.items.runes.Zod: if (me.ladder) { - this.recipes.push({Ingredients: ingredients, Index: index}); + this.recipes.push({ Ingredients: ingredients, Index: index }); } break; @@ -678,7 +678,7 @@ const Cubing = { break; case Recipe.Token: - this.recipes.push({Ingredients: ingredients, Index: index, AlwaysEnabled: true}); + this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); break; case Recipe.Rejuv: @@ -1222,7 +1222,7 @@ const Cubing = { revpots.forEach(function (pot, index) { // Add this to the original location array - origin.push({location: pot.location, x: pot.x, y: pot.y}); + origin.push({ location: pot.location, x: pot.x, y: pot.y }); Town.openStash(); diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 2c79674af..a092fe528 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -7,7 +7,14 @@ */ const Misc = { - // Click something + /** + * Click something + * @param {number} button + * @param {number} shift + * @param {number | Unit} [x] + * @param {number} [y] + * @returns {boolean} + */ click: function (button, shift, x, y) { if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); @@ -47,7 +54,11 @@ const Misc = { return true; }, - // Check if a player is in your party + /** + * Check if a player is in your party + * @param {string} name + * @returns {boolean} + */ inMyParty: function (name) { if (me.name === name) return true; @@ -200,7 +211,12 @@ const Misc = { return !!unit ? unit.area : 0; }, - // autoleader by Ethic - refactored by theBGuy + /** + * autoleader by Ethic - refactored by theBGuy + * Autodetect leader for leech scripts by looking to see who first enters a certain area + * @param {{ destination: number | number[], quitIf?: Function, timeout?: number }} givenSettings + * @returns + */ autoLeaderDetect: function (givenSettings = {}) { const settings = Object.assign({}, { destination: -1, @@ -208,6 +224,9 @@ const Misc = { timeout: Infinity }, givenSettings); + // make destination an array so it's easier to handle both cases + !Array.isArray(settings.destination) && (settings.destination = [settings.destination]); + let leader; let startTick = getTickCount(); let check = typeof settings.quitIf === "function"; @@ -222,7 +241,7 @@ const Misc = { if (check && settings.quitIf(suspect.area)) return false; // first player not hostile found in destination area... - if (suspect.area === settings.destination && !getPlayerFlag(me.gid, suspect.gid, 8)) { + if (settings.destination.includes(suspect.area) && !getPlayerFlag(me.gid, suspect.gid, 8)) { leader = suspect.name; // ... is our leader console.log("ÿc4Autodetected " + leader); @@ -491,8 +510,9 @@ const Misc = { * @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. + * @todo + * - Sometimes it seems like calling getPresetObjects to quickly after taking an exit causes a crash, only anecdotal evidence though. Test delays + * - Add the rest of the preset shrine id's to look for */ getShrinesInArea: function (area, type, use) { let shrineLocs = []; @@ -661,13 +681,20 @@ const Misc = { } }, + /** + * @param {string} msg + * @returns {void} + */ debugLog: function (msg) { if (!Config.Debug) return; debugLog(me.profile + ": " + msg); }, - // Use a NPC menu. Experimental function, subject to change - // id = string number (with exception of Ressurect merc). + /** + * Use a NPC menu. Experimental function, subject to change + * @param {number} id - string number (with exception of Ressurect merc). + * @returns {boolean} + */ useMenu: function (id) { //print("useMenu " + getLocaleString(id)); @@ -703,6 +730,12 @@ const Misc = { return false; }, + /** + * @param {Function} check + * @param {number} timeout + * @param {number} sleep + * @returns {boolean} + */ poll: function (check, timeout = 6000, sleep = 40) { let ret, start = getTickCount(); @@ -717,7 +750,10 @@ const Misc = { return false; }, - // returns array of UI flags that are set, or null if none are set + /** + * @param {number[]} excluded + * @returns {number[] | null} array of UI flags that are set, or null if none are set + */ getUIFlags: function (excluded = []) { if (!me.gameReady) return null; @@ -739,12 +775,21 @@ const Misc = { return flags.length ? flags : null; }, + /** + * @param {number} id + * @param {number} state + * @returns {0 | 1} + */ checkQuest: function (id, state) { Packet.questRefresh(); delay(500); return me.getQuest(id, state); }, + /** + * @param {number} questID + * @returns {number[]} List of set quest states + */ getQuestStates: function (questID) { if (!me.gameReady) return []; Packet.questRefresh(); diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 9fb6afaf9..bc458d6ee 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -905,7 +905,7 @@ Unit.prototype.use = function () { * @returns Unit[] */ Unit.prototype.checkItem = function (itemInfo) { - if (this === undefined || this.type > 1 || typeof itemInfo !== "object") return {have: false, item: null}; + if (this === undefined || this.type > 1 || typeof itemInfo !== "object") return { have: false, item: null }; const itemObj = Object.assign({}, { classid: -1, @@ -962,8 +962,8 @@ Unit.prototype.checkItem = function (itemInfo) { * @returns Unit[] */ Unit.prototype.findFirst = function (itemInfo = []) { - if (this === undefined || this.type > 1) return {have: false, item: null}; - if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return {have: false, item: null}; + if (this === undefined || this.type > 1) return { have: false, item: null }; + if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return { have: false, item: null }; let itemList = this.getItemsEx(); for (let i = 0; i < itemInfo.length; i++) { @@ -1897,12 +1897,12 @@ Unit.prototype.equip = function (destLocation = undefined) { let tempspot = Storage.Stash.FindSpot(item); if (getUIFlag(sdk.uiflags.Stash) && tempspot) { - return {location: Storage.Stash.location, coord: tempspot}; + return { location: Storage.Stash.location, coord: tempspot }; } tempspot = Storage.Inventory.FindSpot(item); - return tempspot ? {location: Storage.Inventory.location, coord: tempspot} : false; + return tempspot ? { location: Storage.Inventory.location, coord: tempspot } : false; }; const doubleHanded = [ sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Polearm, sdk.items.type.Crossbow, @@ -1947,7 +1947,7 @@ Unit.prototype.equip = function (destLocation = undefined) { // Last item, so swap instead of putting off first if (index === (currentEquiped.length - 1)) { print("swap " + this.name + " for " + item.name); - let oldLoc = {x: this.x, y: this.y, location: this.location}; + let oldLoc = { x: this.x, y: this.y, location: this.location }; clickItemAndWait(sdk.clicktypes.click.item.Left, this); // Pick up current item clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // the swap of items // Find a spot for the current item @@ -2083,7 +2083,7 @@ Unit.prototype.getRes = function (type, difficulty) { 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; } }); diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 6557f0d52..bc8d4dd59 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -10,13 +10,15 @@ * @constructor * @param {number} skillId */ - function SkillData(skillId) { + function SkillData (skillId) { /** @type {boolean} */ this.hardpoints = false; /** @type {boolean} */ this.checked = false; /** @type {number} */ this.manaCost = null; + /** @type {number} */ + this.lvlReq = null; // this part feels kinda ugly to me, todo is figure out a better method for conditon /** @type {Function} */ this.condition = typeof skillConditions[skillId] === "function" @@ -143,9 +145,9 @@ if (Config.UseColdArmor === true) { Precast.skills.coldArmor.best = (function () { let coldArmor = [ - {skillId: sdk.skills.ShiverArmor, level: me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints)}, - {skillId: sdk.skills.ChillingArmor, level: me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints)}, - {skillId: sdk.skills.FrozenArmor, level: me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints)}, + { skillId: sdk.skills.ShiverArmor, level: me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) }, + { skillId: sdk.skills.ChillingArmor, level: me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) }, + { skillId: sdk.skills.FrozenArmor, level: me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) }, ].filter(skill => !!skill.level && skill.level > 0).sort((a, b) => b.level - a.level).first(); return coldArmor !== undefined ? coldArmor.skillId : false; })(); diff --git a/d2bs/kolbot/libs/critical.js b/d2bs/kolbot/libs/critical.js index a677bf431..ba2a999f3 100644 --- a/d2bs/kolbot/libs/critical.js +++ b/d2bs/kolbot/libs/critical.js @@ -5,8 +5,6 @@ * */ -(() => { - include("json2.js"); // I don't know if this one is actually critical but including it - include("polyfill.js"); - me.ingame ? include("oog/D2Bot.js") : include("OOG.js"); -})(); +include("json2.js"); // I don't know if this one is actually critical but including it +include("polyfill.js"); +me.ingame ? include("oog/D2Bot.js") : include("OOG.js"); diff --git a/d2bs/kolbot/libs/modules/Team.js b/d2bs/kolbot/libs/modules/Team.js index 4fa1488d6..c2228bce5 100644 --- a/d2bs/kolbot/libs/modules/Team.js +++ b/d2bs/kolbot/libs/modules/Team.js @@ -39,9 +39,9 @@ if (threadType === "thread") { print("ÿc2Kolbotÿc0 :: Team thread started"); - Messaging.on("Team", data => { - return typeof data === "object" && data && data.hasOwnProperty("call") && Team[data.call].apply(Team, data.hasOwnProperty("args") && data.args || []); - }); + Messaging.on("Team", data => ( + typeof data === "object" && data && data.hasOwnProperty("call") && Team[data.call].apply(Team, data.hasOwnProperty("args") && data.args || []) + )); Worker.runInBackground.copydata = (new function () { const workBench = []; @@ -123,22 +123,18 @@ } } else { - (function (module, require) { + (function (module) { const localTeam = module.exports = Team; // <-- some get overridden, but this still works for auto completion in your IDE // Filter out all Team functions that are linked to myEvent Object.keys(Team) .filter(key => !myEvents.hasOwnProperty(key) && typeof Team[key] === "function") - .forEach(key => { - return module.exports[key] = (...args) => { - return Messaging.send({ - Team: { - call: key, - args: args - } - }); - }; - }); + .forEach(key => module.exports[key] = (...args) => Messaging.send({ + Team: { + call: key, + args: args + } + })); Messaging.on("Team", msg => typeof msg === "object" @@ -161,7 +157,7 @@ }); }) ); - })(module, require); + })(module); } diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 3a7ef0f58..8e27a14b4 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -2548,6 +2548,12 @@ A5Barb: 561, }, + missiles: { + DiabloLightning: 172, + FissureCrack1: 462, + FissureCrack2: 463, + }, + storage: { Equipped: 1, Belt: 2, diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index a367def27..7670e6835 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -10,7 +10,7 @@ includeIfNotIncluded("oog/DataFile.js"); (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(["require", "../modules/CopyData"], factory); diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index fade25911..ee84ab3ef 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -6,7 +6,7 @@ */ function Duriel () { - this.killDuriel = function () { + const killDuriel = function () { let target = Misc.poll(() => Game.getMonster(sdk.monsters.Duriel), 1000, 200); if (!target) throw new Error("Duriel not found."); @@ -41,14 +41,14 @@ function Duriel () { }, 1000, 200); } - if (!me.inArea(sdk.areas.DurielsLair) && (!unit || !Pather.useUnitEx({unit: unit}, sdk.areas.DurielsLair))) { + if (!me.inArea(sdk.areas.DurielsLair) && (!unit || !Pather.useUnitEx({ unit: unit }, sdk.areas.DurielsLair))) { Attack.clear(10); Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); } if (!me.inArea(sdk.areas.DurielsLair)) throw new Error("Failed to move to Duriel"); - me.sorceress && me.classic ? this.killDuriel() : Attack.kill(sdk.monsters.Duriel); + me.sorceress && me.classic ? killDuriel() : Attack.kill(sdk.monsters.Duriel); Pickit.pickItems(); return true; diff --git a/d2bs/kolbot/libs/scripts/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js index c386b3f16..d087bb134 100644 --- a/d2bs/kolbot/libs/scripts/Eldritch.js +++ b/d2bs/kolbot/libs/scripts/Eldritch.js @@ -1,6 +1,6 @@ /** * @filename Eldritch.js -* @author kolton +* @author kolton, theBGuy * @desc kill Eldritch the Rectifier, optionally kill Shenk the Overseer, Dac Farren and open chest * */ @@ -9,7 +9,7 @@ function Eldritch() { Town.doChores(); Pather.useWaypoint(sdk.areas.FrigidHighlands); Precast.doPrecast(true); - let {x, y} = me; + let { x, y } = me; Pather.moveTo(3745, 5084); Attack.kill(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); @@ -19,7 +19,7 @@ function Eldritch() { if (Config.Eldritch.OpenChest && Pather.moveNearPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 10)) { Misc.openChest(sdk.objects.FrigidHighlandsChest) && Pickit.pickItems(); // check distance from current location to shenk and if far tp to town and use wp instead - [x, y].distance > 120 && Town.goToTown() && Pather.useWaypoint(sdk.areas.FrigidHighlands); + ([x, y].distance > 120 || !Pather.canTeleport()) && Town.goToTown() && Pather.useWaypoint(sdk.areas.FrigidHighlands); } } catch (e) { console.warn("(Eldritch) :: Failed to open chest. " + e); diff --git a/d2bs/kolbot/libs/scripts/GetKeys.js b/d2bs/kolbot/libs/scripts/GetKeys.js index e93532613..75fc5eb02 100644 --- a/d2bs/kolbot/libs/scripts/GetKeys.js +++ b/d2bs/kolbot/libs/scripts/GetKeys.js @@ -8,27 +8,27 @@ function GetKeys() { Town.doChores(); - if (!me.findItems("pk1") || me.findItems("pk1").length < 3) { + if (me.getItemsEx(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length < 3) { try { Loader.runScript("Countess"); - } catch (countessError) { - print("ÿc1Countess failed"); + } catch (e) { + console.error("ÿc1Countess failed :: ", e); } } - if (!me.findItems("pk2") || me.findItems("pk2").length < 3) { + if (me.getItemsEx(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length < 3) { try { Loader.runScript("Summoner", () => Config.Summoner.FireEye = false); - } catch (summonerError) { - print("ÿc1Summoner failed"); + } catch (e) { + console.error("ÿc1Summoner failed :: ", e); } } - if (!me.findItems("pk3") || me.findItems("pk3").length < 3) { + if (me.getItemsEx(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length < 3) { try { Loader.runScript("Nihlathak"); - } catch (nihlathakError) { - print("ÿc1Nihlathak failed"); + } catch (e) { + console.error("ÿc1Nihlathak failed :: ", e); } } diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index f4bb066a9..dd6563588 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -267,34 +267,36 @@ declare global { readonly poisonRes: number; readonly hpPercent: number; readonly prettyPrint: string; - - getStatEx(one: number, sub?: number): number; + + // D2BS built in getNext(): Unit | false; - cancel(number?: number): boolean; - repair(): boolean; - useMenu(): boolean; interact(): boolean; interact(area: number): boolean; getItem(classId?: number, mode?: number, unitId?: number): ItemUnit | false; getItem(name?: string, mode?: number, unitId?: number): ItemUnit | false; getItems(...args: any[]): ItemUnit[] | false; - getItemsEx(classId?: number, mode?: number, unitId?: number): ItemUnit[]; - getItemsEx(name?: string, mode?: number, unitId?: number): ItemUnit[]; getMerc(): MercUnit; getMercHP(): number | false; - - // me.getSkill(0-4); // - getSkill(type: 0 | 1 | 2 | 3 | 4): number; + /** + * @param type - + * - `me.getSkill(0)` : Name of skill on right hand + * - `me.getSkill(1)` : Name of skill on left hand + * - `me.getSkill(2)` : ID of skill on right hand + * - `me.getSkill(3)` : ID of skill on left hand + * - `me.getSkill(4)` : Array of all skills in format [skillId, hardPoints, softPoints, ...repeat] + */ + getSkill(type: 0 | 1 | 2 | 3 | 4): number | number[]; getSkill(skillId: number, type: 0 | 1, item?: ItemUnit): number; - - getParent(): Unit | string; - overhead(msg: string): void; getStat(index: number, subid?: number, extra?: number): number; getState(index: number, subid?: number): boolean; - setSkill(): boolean; - move(x: number, y: number): boolean; getQuest(quest: number, subid: number): number + getParent(): Unit | string; getMinionCount(): number; + + // additions from kolbot + getStatEx(one: number, sub?: number): number; + getItemsEx(classId?: number, mode?: number, unitId?: number): ItemUnit[]; + getItemsEx(name?: string, mode?: number, unitId?: number): ItemUnit[]; inArea(area: number): boolean; checkForMobs(givenSettings: { range?: number; @@ -311,7 +313,6 @@ declare global { type MonsterType = 1; interface Monster extends Unit { - } class Monster extends Unit { @@ -365,6 +366,7 @@ declare global { readonly itemcount: number; openMenu(): boolean; + useMenu(): boolean; startTrade: (mode: any) => (any | boolean); } @@ -461,6 +463,7 @@ declare global { buy(shift?: boolean, gamble?: boolean): boolean; sellOrDrop():void toCursor():boolean + use(): boolean; } type TileType = 5; @@ -535,6 +538,12 @@ declare global { readonly inShop: boolean; readonly skillDelay: boolean; + overhead(msg: string): void; + repair(): boolean; + revive(): void; + move(x: number, y: number): boolean; + setSkill(): boolean; + cancel(number?: number): boolean; inArea(area: number): boolean; switchWeapons(slot: 0 | 1): void; switchToPrimary(): boolean; @@ -551,7 +560,6 @@ declare global { equip(destination: number | undefined, item: ItemUnit); findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; - revive(): void; getRepairCost(): number; findItems(param: number, number?: number, number2?: number): ItemUnit[]; usingShield(): boolean; diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts index a0434a0e7..05fc86e0c 100644 --- a/d2bs/kolbot/sdk/types/Town.d.ts +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -2,6 +2,7 @@ declare global { type NPC = string; export namespace NPC { + function getAct(name: string): number[]; const Akara: string; const Gheed: string; const Charsi: string; diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 69e7a2320..d1e6428af 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -2472,6 +2472,12 @@ declare global { const A5Barb: 561; } + export namespace missiles { + const DiabloLightning = 172; + const FissureCrack1 = 462; + const FissureCrack2 = 463; + } + export namespace storage { const Equipped: 1; const Belt: 2; @@ -2910,53 +2916,53 @@ declare global { export const Stalagmite: 260; export const ElderStaff: 261; export const Shillelagh: 262; - const ArchonStaff: 263; - const SpiderBow: 264; - const BladeBow: 265; - const ShadowBow: 266; - const GreatBow: 267; - const DiamondBow: 268; - const CrusaderBow: 269; - const WardBow: 270; - const HydraBow: 271; - const PelletBow: 272; - const GorgonCrossbow: 273; - const ColossusCrossbow: 274; - const DemonCrossbow: 275; - const EagleOrb: 276; - const SacredGlobe: 277; - const SmokedSphere: 278; - const ClaspedOrb: 279; - const JaredsStone: 280; - const StagBow: 281; - const ReflexBow: 282; - const MaidenSpear: 283; - const MaidenPike: 284; - const MaidenJavelin: 285; - const GlowingOrb: 286; - const CrystallineGlobe: 287; - const CloudySphere: 288; - const SparklingBall: 289; - const SwirlingCrystal: 290; - const AshwoodBow: 291; - const CeremonialBow: 292; - const CeremonialSpear: 293; - const CeremonialPike: 294; - const CeremonialJavelin: 295; - const HeavenlyStone: 296; - const EldritchOrb: 297; - const DemonHeart: 298; - const VortexOrb: 299; - const DimensionalShard: 300; - const MatriarchalBow: 301; - const GrandMatronBow: 302; - const MatriarchalSpear: 303; - const MatriarchalPike: 304; - const MatriarchalJavelin: 305; - const Cap: 306; - const SkullCap: 307; - const Helm: 308; - const FullHelm: 309; + export const ArchonStaff: 263; + export const SpiderBow: 264; + export const BladeBow: 265; + export const ShadowBow: 266; + export const GreatBow: 267; + export const DiamondBow: 268; + export const CrusaderBow: 269; + export const WardBow: 270; + export const HydraBow: 271; + export const PelletBow: 272; + export const GorgonCrossbow: 273; + export const ColossusCrossbow: 274; + export const DemonCrossbow: 275; + export const EagleOrb: 276; + export const SacredGlobe: 277; + export const SmokedSphere: 278; + export const ClaspedOrb: 279; + export const JaredsStone: 280; + export const StagBow: 281; + export const ReflexBow: 282; + export const MaidenSpear: 283; + export const MaidenPike: 284; + export const MaidenJavelin: 285; + export const GlowingOrb: 286; + export const CrystallineGlobe: 287; + export const CloudySphere: 288; + export const SparklingBall: 289; + export const SwirlingCrystal: 290; + export const AshwoodBow: 291; + export const CeremonialBow: 292; + export const CeremonialSpear: 293; + export const CeremonialPike: 294; + export const CeremonialJavelin: 295; + export const HeavenlyStone: 296; + export const EldritchOrb: 297; + export const DemonHeart: 298; + export const VortexOrb: 299; + export const DimensionalShard: 300; + export const MatriarchalBow: 301; + export const GrandMatronBow: 302; + export const MatriarchalSpear: 303; + export const MatriarchalPike: 304; + export const MatriarchalJavelin: 305; + export const Cap: 306; + export const SkullCap: 307; + export const Helm: 308; + export const FullHelm: 309; const GreatHelm: 310; const Crown: 311; const Mask: 312; @@ -2972,7 +2978,7 @@ declare global { const PlateMail: 322; const FieldPlate: 323; const GothicPlate: 324; - const FullPlateMail: 325; + export const FullPlateMail: 325; const AncientArmor: 326; const LightPlate: 327; const Buckler: 328; @@ -2986,7 +2992,7 @@ declare global { const ChainGloves: 336; const LightGauntlets: 337; const Gauntlets: 338; - const Boots: 339; + export const Boots: 339; const HeavyBoots: 340; const ChainBoots: 341; const LightPlatedBoots: 342; @@ -2996,7 +3002,7 @@ declare global { const Belt: 346; const HeavyBelt: 347; const PlatedBelt: 348; - const BoneHelm: 349; + export const BoneHelm: 349; const BoneShield: 350; const SpikedShield: 351; const WarHat: 352; @@ -3181,36 +3187,36 @@ declare global { const Herb: 602; const anevilforce: 609; // Potions, tomes/scrolls, gold - const TomeofTownPortal: 518; - const TomeofIdentify: 519; - const ScrollofTownPortal: 529; - const ScrollofIdentify: 530; - const RancidGasPotion: 80; - const OilPotion: 81; - const ChokingGasPotion: 82; - const ExplodingPotion: 83; - const StranglingGasPotion: 84; - const FulminatingPotion: 85; - const StaminaPotion: 513; - const AntidotePotion: 514; - const RejuvenationPotion: 515; - const FullRejuvenationPotion: 516; - const ThawingPotion: 517; - const MinorHealingPotion: 587; - const LightHealingPotion: 588; - const HealingPotion: 589; - const GreaterHealingPotion: 590; - const SuperHealingPotion: 591; - const MinorManaPotion: 592; - const LightManaPotion: 593; - const ManaPotion: 594; - const GreaterManaPotion: 595; - const SuperManaPotion: 596; - const Gold: 523; + export const TomeofTownPortal: 518; + export const TomeofIdentify: 519; + export const ScrollofTownPortal: 529; + export const ScrollofIdentify: 530; + export const RancidGasPotion: 80; + export const OilPotion: 81; + export const ChokingGasPotion: 82; + export const ExplodingPotion: 83; + export const StranglingGasPotion: 84; + export const FulminatingPotion: 85; + export const StaminaPotion: 513; + export const AntidotePotion: 514; + export const RejuvenationPotion: 515; + export const FullRejuvenationPotion: 516; + export const ThawingPotion: 517; + export const MinorHealingPotion: 587; + export const LightHealingPotion: 588; + export const HealingPotion: 589; + export const GreaterHealingPotion: 590; + export const SuperHealingPotion: 591; + export const MinorManaPotion: 592; + export const LightManaPotion: 593; + export const ManaPotion: 594; + export const GreaterManaPotion: 595; + export const SuperManaPotion: 596; + export const Gold: 523; // Charms - const SmallCharm: 603; - const LargeCharm: 604; - const GrandCharm: 605; + export const SmallCharm: 603; + export const LargeCharm: 604; + export const GrandCharm: 605; export namespace quest { // Act 1 diff --git a/d2bs/kolbot/threads/HeartBeat.js b/d2bs/kolbot/threads/HeartBeat.js index 68466317b..cd7969b00 100644 --- a/d2bs/kolbot/threads/HeartBeat.js +++ b/d2bs/kolbot/threads/HeartBeat.js @@ -6,9 +6,7 @@ */ function main() { - include("json2.js"); // required? - include("polyfill.js"); // required - include("oog/D2Bot.js"); // required + include("critical.js"); // required D2Bot.init(); console.log("Heartbeat loaded"); diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index 33a92517a..f96cf06ba 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -5,25 +5,17 @@ * */ js_strict(true); - -include("json2.js"); // required? -include("polyfill.js"); // required -include("oog/D2Bot.js"); // required +include("critical.js"); // globals needed for core gameplay -include("core/NTItemParser.js"); -include("core/Util.js"); includeCoreLibs(); // system libs -include("systems/automule/AutoMule.js"); -include("systems/gambling/Gambling.js"); -include("systems/crafting/CraftingSystem.js"); -include("systems/torch/TorchSystem.js"); +includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -let Overrides = require("../modules/Override"); +const Overrides = require("../libs/modules/Override"); let count = 0; let silentNameTracker = []; diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 98231e83d..8bdea750c 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -19,7 +19,7 @@ include("systems/gameaction/GameAction.js"); let Overrides = require("../libs/modules/Override"); 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(); diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js index 2d4545c0d..390ffec84 100644 --- a/d2bs/kolbot/threads/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -18,7 +18,7 @@ function main() { const scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; // override broadCastIntent - shouldn't be called at all in this thread - Pather.broadcastIntent = () => null; + Pather.broadcastIntent = () => {}; const pause = function () { for (let i = 0; i < scripts.length; i += 1) { From ba1d82a795e3c505c29207fb9ac6664b75d2ce0f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:13:08 -0500 Subject: [PATCH 069/758] type declaration updates - Fix some type declarations for pickit and add Item namespace declaration --- d2bs/kolbot/sdk/globals.d.ts | 15 +-------------- d2bs/kolbot/sdk/types/Item.d.ts | 26 ++++++++++++++++++-------- d2bs/kolbot/sdk/types/Pickit.d.ts | 4 +--- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index dd6563588..2afb4ce70 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -9,26 +9,13 @@ /// /// /// +/// /// /// /// /// /// /// -// import sdk from "./types/sdk"; -// import { NPC, Town } from "./types/Town"; -// import * as Attack from "./types/Attack"; -// import * as Loader from "./types/Loader"; -// import * as Pather from "./types/Pather"; -// // import * as Misc from "./types/Misc"; -// import * as Skill from "./types/Skill"; -// import * as Pickit from "./types/Pickit"; -// import { Container, Storage } from "./types/Storage"; -// import * as Cubing from "./types/Cubing"; -// import { Runeword, Runewords, Roll } from "./types/Runewords"; -// import * as NTIP from "./types/NTIP"; -// import * as AutoMule from "./types/AutoMule"; -// import { D2Bot, DataFile, ControlAction } from "./types/OOG"; declare global { interface Array { diff --git a/d2bs/kolbot/sdk/types/Item.d.ts b/d2bs/kolbot/sdk/types/Item.d.ts index 6da8026fd..cbb96366b 100644 --- a/d2bs/kolbot/sdk/types/Item.d.ts +++ b/d2bs/kolbot/sdk/types/Item.d.ts @@ -1,12 +1,22 @@ export {}; declare global { - const Item: { - hasTier(item: any): void - canEquip(item: any): void - equip(item: any, bodyLoc: any): void - getEquippedItem(bodyLoc: any): void - getBodyLoc(item: any): void - autoEquipCheck(item: any): boolean - autoEquip(): void + namespace Item { + let useItemLog: boolean; + + function qualityToName(quality : number): string; + function color(unit: ItemUnit, type: boolean): string; + function hasTier(item: ItemUnit): boolean; + function canEquip(item: ItemUnit): boolean; + function equip(item: ItemUnit, bodyLoc: number): boolean; + function getEquippedItem(bodyLoc: number): { classid: number, tier: number }; + function getBodyLoc(item: ItemUnit): number[]; + function autoEquipCheck(item: ItemUnit): boolean; + function autoEquip(): boolean; + function getItemDesc(unit: ItemUnit, logILvl: boolean): string; + function getItemCode(unit: ItemUnit): string; + function getItemSockets(unit: ItemUnit): ItemUnit[]; + function logger(action: string, unit: ItemUnit, text?: string): string; + function logItem(action: string, unit: ItemUnit, keptLine?: string): boolean; + function skipItem(id: number): boolean; } } diff --git a/d2bs/kolbot/sdk/types/Pickit.d.ts b/d2bs/kolbot/sdk/types/Pickit.d.ts index 64df58769..9f35db095 100644 --- a/d2bs/kolbot/sdk/types/Pickit.d.ts +++ b/d2bs/kolbot/sdk/types/Pickit.d.ts @@ -26,11 +26,9 @@ declare global { checkBelt: () => boolean canPick: (unit: ItemUnit) => boolean checkItem: (unit: ItemUnit) => { result: PickitResult, line: null | number } - pickItem: (unit: ItemUnit, status?: PickitResult, keptLine?: any, retry?: number) => boolean + pickItem: (unit: ItemUnit, status?: PickitResult, keptLine?: any, retry?: number) => { result: PickitResult, line: string | null }; canMakeRoom: () => boolean pickItems: (range?: number) => boolean fastPick: () => boolean - itemQualityToName: (quality: any) => string - itemColor: (unit: Unit, type?: boolean) => string } } From e1079b79da5c47c7a56d1f5cab68bc636dd0fcb1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:14:20 -0500 Subject: [PATCH 070/758] Update .gitignore - Add soloplay to gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8d983277e..8cff0af1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ - +data/ +images/ +logs/ d2bs/kolbot/data/secure/*.txt d2bs/kolbot/data/*.json d2bs/kolbot/logs/*.json @@ -7,4 +9,6 @@ d2bs/kolbot/logs/**/** d2bs/kolbot/mules/**/*.txt d2bs/logs/*.log d2bs/kolbot/libs/manualplay/config/*.*.js +d2bs/kolbot/libs/soloplay/** d2bs/kolbot/libs/config/*.*.js +d2bs/kolbot/D2BotSoloPlay.dbj From 603011df7cd2fde3f058eee00941c15b9d29f42b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:23:28 -0500 Subject: [PATCH 071/758] Update Party.js - clean up party.js --- d2bs/kolbot/threads/Party.js | 87 +++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/d2bs/kolbot/threads/Party.js b/d2bs/kolbot/threads/Party.js index ca5dd7a84..676f34f9b 100644 --- a/d2bs/kolbot/threads/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -5,21 +5,13 @@ * */ js_strict(true); - -include("json2.js"); // required? -include("polyfill.js"); // required -include("oog/D2Bot.js"); // required +include("critical.js"); // globals needed for core gameplay -include("core/NTItemParser.js"); -include("core/Util.js"); includeCoreLibs(); // system libs -include("systems/automule/AutoMule.js"); -include("systems/gambling/Gambling.js"); -include("systems/crafting/CraftingSystem.js"); -include("systems/torch/TorchSystem.js"); +includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); @@ -29,10 +21,17 @@ include("oog/ShitList.js"); function main() { Config.init(); - let myPartyId, player, shitList, currScript, scriptList; + /** @type {string[][]} */ + let [shitList, scriptList] = [[], []]; + let myPartyId, player, currScript; let playerLevels = {}; let partyTick = getTickCount(); + if (!me.gameserverip) { + console.log("Shutting down party thread, it's not needed on single player"); + return true; + } + /** * Format the event message here to prevent repetitive code * @param {string[]} arr @@ -85,27 +84,47 @@ function main() { addEventListener("gameevent", gameEvent); } - addEventListener("scriptmsg", - function (msg) { - let obj; + let quitting = false; + let partyCheck = false; + + const scriptEvent = function (msg) { + if (!!msg && typeof msg === "string") { + switch (msg) { + case "hostileCheck": + partyCheck = true; + + break; + case "quit": + console.debug("Quiting"); + quitting = true; - try { - obj = JSON.parse(msg); + break; + default: + let obj; + + try { + obj = JSON.parse(msg); + } catch (e) { + return; + } if (obj && obj.hasOwnProperty("currScript")) { currScript = obj.currScript; } - } catch (e3) { - return; + + break; } - }); + } + }; + + addEventListener("scriptmsg", scriptEvent); - print("ÿc2Party thread loaded. Mode: " + (Config.PublicMode === 2 ? "Accept" : "Invite")); + console.log("ÿc2Party thread loaded. Mode: " + (Config.PublicMode === 2 ? "Accept" : "Invite")); if (Config.ShitList || Config.UnpartyShitlisted) { shitList = ShitList.read(); - print(shitList.length + " entries in shit list."); + console.log(shitList.length + " entries in shit list."); } if (Config.PartyAfterScript) { @@ -120,6 +139,16 @@ function main() { // Main loop while (true) { + if (quitting) { + // we intercepted quit message to toolsthread, go ahead an shut down + return true; + } + + /** + * @todo if we are already partied with everyone in game, then this doesn't need to keep checking unless an event happens + * e.g. someone joins/leaves game or someone declares hostility + * the exception to that is if we are running with Config.Congratulations, in which case we do need to constantly monitor changes + */ if (me.gameReady && (!Config.PartyAfterScript || scriptList.indexOf(currScript) > scriptList.indexOf(Config.PartyAfterScript))) { player = getParty(); @@ -145,7 +174,7 @@ function main() { break; } - if (Config.ShitList && shitList.indexOf(player.name) > -1) { + if (Config.ShitList && shitList.includes(player.name)) { break; } @@ -179,9 +208,9 @@ function main() { shitList.push(player.name); } - if (shitList.indexOf(player.name) > -1 && myPartyId !== sdk.party.NoParty && player.partyid === myPartyId) { + if (shitList.includes(player.name) && myPartyId !== sdk.party.NoParty && player.partyid === myPartyId) { // Only the one sending invites should say this. - if ([1, 3].indexOf(Config.PublicMode) > -1) { + if ([1, 3].includes(Config.PublicMode)) { say(player.name + " is shitlisted. Do not invite them."); } @@ -189,14 +218,8 @@ function main() { delay(100); } } - } - } - if (Config.Congratulations.length > 0) { - player = getParty(); - - if (player) { - do { + if (Config.Congratulations.length > 0) { if (player.name !== me.name) { if (!playerLevels[player.name]) { playerLevels[player.name] = player.level; @@ -209,7 +232,7 @@ function main() { playerLevels[player.name] = player.level; } } - } while (player.getNext()); + } } } } From 5e65c9077a5e3b47d7fd7e25ddc68089c02866f2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:31:28 -0500 Subject: [PATCH 072/758] Update Prototypes.js - Fix prettyPrint prototype --- d2bs/kolbot/libs/core/Prototypes.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index bc458d6ee..352b2caa4 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -587,8 +587,10 @@ Object.defineProperties(Unit.prototype, { }, }, prettyPrint: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return this.name; + if (this.fname === undefined) return typeof this.name === "string" ? this.name : "undefined"; return this.fname.split("\n").reverse().join(" "); } }, From 5bb37bb4f684a905ef5819eaff08ac0311664a6d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 2 Mar 2023 15:16:14 -0500 Subject: [PATCH 073/758] type declaration updates - found some magic numbers that had been missed for party related actions - Added some missile id's, need to add more --- d2bs/kolbot/libs/modules/sdk.js | 21 +- d2bs/kolbot/sdk/globals.d.ts | 33 ++- d2bs/kolbot/sdk/types/Util.d.ts | 2 +- d2bs/kolbot/sdk/types/sdk.d.ts | 423 +++++++++++++++++--------------- 4 files changed, 266 insertions(+), 213 deletions(-) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 8e27a14b4..7737b3f39 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -9,7 +9,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); @@ -45,7 +45,20 @@ }, party: { - NoParty: 65535 + NoParty: 65535, + flag: { + Invite: 0, + InParty: 1, + Accept: 2, + Cancel: 4 + }, + controls: { + Hostile: 1, + InviteOrCancel: 2, + Leave: 3, + Ignore: 4, + Squelch: 5, + }, }, clicktypes: { @@ -1435,6 +1448,10 @@ }, player: { + flag: { + Ignore: 2, + Hostile: 8, + }, slot: { Main: 0, Secondary: 1 diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 2afb4ce70..d2dae9b15 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -532,7 +532,6 @@ declare global { setSkill(): boolean; cancel(number?: number): boolean; inArea(area: number): boolean; - switchWeapons(slot: 0 | 1): void; switchToPrimary(): boolean; checkItem(itemInfo: { classid?: number; @@ -545,13 +544,28 @@ declare global { }): {have: boolean; item: ItemUnit | null}; haveSome(arg0: { name: number; equipped: boolean; }[]): any; equip(destination: number | undefined, item: ItemUnit); - findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; - findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; getRepairCost(): number; findItems(param: number, number?: number, number2?: number): ItemUnit[]; usingShield(): boolean; + walk(): void; + run(): void; + getPingDelay(): number; + findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; + findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; cancelUIFlags(): boolean; + switchWeapons(slot: 0 | 1): boolean; + castingFrames(skillId: number, fcr?: number, charClass?: number): number; + castingDuration(skillId: number, fcr?: number, charClass?: number): number; + getWeaponQuantity(weaponLoc: number): number; + needPotions(): boolean; + getTpTool(): ItemUnit | null; + getIdTool(): ItemUnit | null; + canTpToTown(): boolean; + needHealing(): boolean; getTome(id: number): ItemUnit | null; + getUnids(): ItemUnit[]; + fieldID(): boolean; + switchToPrimary(): boolean; } const me: MeType @@ -619,11 +633,16 @@ declare global { function getRoom(): Room | false class Party { - name: string; + x: number; + y: number; + area: number; gid: number; - level: number; + life: number; + partyflag: number; partyid: number; - area: number; + name: string; + classid: number; + level: number; inTown: any; getNext(): Party | false; @@ -755,7 +774,7 @@ declare global { function getControls(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control[] function getPlayerFlag(meGid: number, otherGid: number, type: number): boolean function getTickCount(): number - function getInteractedNPC(): Monster | false + function getInteractedNPC(): NPCUnit | false function getIsTalkingNPC(): boolean function getDialogLines(): { handler() }[] | false function print(what: string): void diff --git a/d2bs/kolbot/sdk/types/Util.d.ts b/d2bs/kolbot/sdk/types/Util.d.ts index 0329e5cc3..ef375f1fa 100644 --- a/d2bs/kolbot/sdk/types/Util.d.ts +++ b/d2bs/kolbot/sdk/types/Util.d.ts @@ -109,7 +109,7 @@ declare global { function getSelectedUnit(): ItemUnit; function getPlayer(id: any, mode: any, gid: any): Player; function getMonster(id?: string | number, mode?: number, gid?: number): Monster; - function getNPC(id?: string | number, mode?: number, gid?: number): Monster; + function getNPC(id?: string | number, mode?: number, gid?: number): NPCUnit; function getObject(id?: string | number, mode?: number, gid?: number): ObjectUnit; function getMissile(id?: string | number, mode?: number, gid?: number): Missile; function getItem(id?: string | number, mode?: number, gid?: number): ItemUnit; diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index d1e6428af..d0dc6e800 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -20,6 +20,19 @@ declare global { export namespace party { const NoParty: 65535 + namespace flag { + const Invite: 0; + const InParty: 1; + const Accept: 2; + const Cancel: 4; + } + namespace controls { + const Hostile: 1; + const InviteOrCancel: 2; + const Leave: 3; + const Ignore: 4; + const Squelch: 5; + } } export namespace clicktypes { @@ -1362,6 +1375,10 @@ declare global { } export namespace player { + export namespace flag { + const Ignore: 2; + const Hostile: 8; + } export namespace slot { const Main: 0; const Secondary: 1 @@ -2473,9 +2490,9 @@ declare global { } export namespace missiles { - const DiabloLightning = 172; - const FissureCrack1 = 462; - const FissureCrack2 = 463; + const DiabloLightning: 172; + const FissureCrack1: 462; + const FissureCrack2: 463; } export namespace storage { @@ -2963,209 +2980,209 @@ declare global { export const SkullCap: 307; export const Helm: 308; export const FullHelm: 309; - const GreatHelm: 310; - const Crown: 311; - const Mask: 312; - const QuiltedArmor: 313; - const LeatherArmor: 314; - const HardLeatherArmor: 315; - const StuddedLeather: 316; - const RingMail: 317; - const ScaleMail: 318; - const ChainMail: 319; - const BreastPlate: 320; - const SplintMail: 321; - const PlateMail: 322; - const FieldPlate: 323; - const GothicPlate: 324; + export const GreatHelm: 310; + export const Crown: 311; + export const Mask: 312; + export const QuiltedArmor: 313; + export const LeatherArmor: 314; + export const HardLeatherArmor: 315; + export const StuddedLeather: 316; + export const RingMail: 317; + export const ScaleMail: 318; + export const ChainMail: 319; + export const BreastPlate: 320; + export const SplintMail: 321; + export const PlateMail: 322; + export const FieldPlate: 323; + export const GothicPlate: 324; export const FullPlateMail: 325; - const AncientArmor: 326; - const LightPlate: 327; - const Buckler: 328; - const SmallShield: 329; - const LargeShield: 330; - const KiteShield: 331; - const TowerShield: 332; - const GothicShield: 333; - const LeatherGloves: 334; - const HeavyGloves: 335; - const ChainGloves: 336; - const LightGauntlets: 337; - const Gauntlets: 338; + export const AncientArmor: 326; + export const LightPlate: 327; + export const Buckler: 328; + export const SmallShield: 329; + export const LargeShield: 330; + export const KiteShield: 331; + export const TowerShield: 332; + export const GothicShield: 333; + export const LeatherGloves: 334; + export const HeavyGloves: 335; + export const ChainGloves: 336; + export const LightGauntlets: 337; + export const Gauntlets: 338; export const Boots: 339; - const HeavyBoots: 340; - const ChainBoots: 341; - const LightPlatedBoots: 342; - const Greaves: 343; - const Sash: 344; - const LightBelt: 345; - const Belt: 346; - const HeavyBelt: 347; - const PlatedBelt: 348; + export const HeavyBoots: 340; + export const ChainBoots: 341; + export const LightPlatedBoots: 342; + export const Greaves: 343; + export const Sash: 344; + export const LightBelt: 345; + export const Belt: 346; + export const HeavyBelt: 347; + export const PlatedBelt: 348; export const BoneHelm: 349; - const BoneShield: 350; - const SpikedShield: 351; - const WarHat: 352; - const Sallet: 353; - const Casque: 354; - const Basinet: 355; - const WingedHelm: 356; - const GrandCrown: 357; - const DeathMask: 358; - const GhostArmor: 359; - const SerpentskinArmor: 360; - const DemonhideArmor: 361; - const TrellisedArmor: 362; - const LinkedMail: 363; - const TigulatedMail: 364; - const MeshArmor: 365; - const Cuirass: 366; - const RussetArmor: 367; - const TemplarCoat: 368; - const SharktoothArmor: 369; - const EmbossedPlate: 370; - const ChaosArmor: 371; - const OrnatePlate: 372; - const MagePlate: 373; - const Defender: 374; - const RoundShield: 375; - const Scutum: 376; - const DragonShield: 377; - const Pavise: 378; - const AncientShield: 379; - const DemonhideGloves: 380; - const SharkskinGloves: 381; - const HeavyBracers: 382; - const BattleGauntlets: 383; - const WarGauntlets: 384; - const DemonhideBoots: 385; - const SharkskinBoots: 386; - const MeshBoots: 387; - const BattleBoots: 388; - const WarBoots: 389; - const DemonhideSash: 390; - const SharkskinBelt: 391; - const MeshBelt: 392; - const BattleBelt: 393; - const WarBelt: 394; - const GrimHelm: 395; - const GrimShield: 396; - const BarbedShield: 397; - const WolfHead: 398; - const HawkHelm: 399; - const Antlers: 400; - const FalconMask: 401; - const SpiritMask: 402; - const JawboneCap: 403; - const FangedHelm: 404; - const HornedHelm: 405; - const AssaultHelmet: 406; - const AvengerGuard: 407; - const Targe: 408; - const Rondache: 409; - const HeraldicShield: 410; - const AerinShield: 411; - const CrownShield: 412; - const PreservedHead: 413; - const ZombieHead: 414; - const UnravellerHead: 415; - const GargoyleHead: 416; - const DemonHead: 417; - const Circlet: 418; - const Coronet: 419; - const Tiara: 420; - const Diadem: 421; - const Shako: 422; - const Hydraskull: 423; - const Armet: 424; - const GiantConch: 425; - const SpiredHelm: 426; - const Corona: 427; - const Demonhead: 428; - const DuskShroud: 429; - const Wyrmhide: 430; - const ScarabHusk: 431; - const WireFleece: 432; - const DiamondMail: 433; - const LoricatedMail: 434; - const Boneweave: 435; - const GreatHauberk: 436; - const BalrogSkin: 437; - const HellforgePlate: 438; - const KrakenShell: 439; - const LacqueredPlate: 440; - const ShadowPlate: 441; - const SacredArmor: 442; - const ArchonPlate: 443; - const Heater: 444; - const Luna: 445; - const Hyperion: 446; - const Monarch: 447; - const Aegis: 448; - const Ward: 449; - const BrambleMitts: 450; - const VampireboneGloves: 451; - const Vambraces: 452; - const CrusaderGauntlets: 453; - const OgreGauntlets: 454; - const WyrmhideBoots: 455; - const ScarabshellBoots: 456; - const BoneweaveBoots: 457; - const MirroredBoots: 458; - const MyrmidonGreaves: 459; - const SpiderwebSash: 460; - const VampirefangBelt: 461; - const MithrilCoil: 462; - const TrollBelt: 463; - const ColossusGirdle: 464; - const BoneVisage: 465; - const TrollNest: 466; - const BladeBarrier: 467; - const AlphaHelm: 468; - const GriffonHeaddress: 469; - const HuntersGuise: 470; - const SacredFeathers: 471; - const TotemicMask: 472; - const JawboneVisor: 473; - const LionHelm: 474; - const RageMask: 475; - const SavageHelmet: 476; - const SlayerGuard: 477; - const AkaranTarge: 478; - const AkaranRondache: 479; - const ProtectorShield: 480; - const GildedShield: 481; - const RoyalShield: 482; - const MummifiedTrophy: 483; - const FetishTrophy: 484; - const SextonTrophy: 485; - const CantorTrophy: 486; - const HierophantTrophy: 487; - const BloodSpirit: 488; - const SunSpirit: 489; - const EarthSpirit: 490; - const SkySpirit: 491; - const DreamSpirit: 492; - const CarnageHelm: 493; - const FuryVisor: 494; - const DestroyerHelm: 495; - const ConquerorCrown: 496; - const GuardianCrown: 497; - const SacredTarge: 498; - const SacredRondache: 499; - const KurastShield: 500; - const ZakarumShield: 501; - const VortexShield: 502; - const MinionSkull: 503; - const HellspawnSkull: 504; - const OverseerSkull: 505; - const SuccubusSkull: 506; - const BloodlordSkull: 507; - const Amulet: 520; - const Ring: 522; - const Arrows: 526; - const Bolts: 528; - const Jewel: 643; + export const BoneShield: 350; + export const SpikedShield: 351; + export const WarHat: 352; + export const Sallet: 353; + export const Casque: 354; + export const Basinet: 355; + export const WingedHelm: 356; + export const GrandCrown: 357; + export const DeathMask: 358; + export const GhostArmor: 359; + export const SerpentskinArmor: 360; + export const DemonhideArmor: 361; + export const TrellisedArmor: 362; + export const LinkedMail: 363; + export const TigulatedMail: 364; + export const MeshArmor: 365; + export const Cuirass: 366; + export const RussetArmor: 367; + export const TemplarCoat: 368; + export const SharktoothArmor: 369; + export const EmbossedPlate: 370; + export const ChaosArmor: 371; + export const OrnatePlate: 372; + export const MagePlate: 373; + export const Defender: 374; + export const RoundShield: 375; + export const Scutum: 376; + export const DragonShield: 377; + export const Pavise: 378; + export const AncientShield: 379; + export const DemonhideGloves: 380; + export const SharkskinGloves: 381; + export const HeavyBracers: 382; + export const BattleGauntlets: 383; + export const WarGauntlets: 384; + export const DemonhideBoots: 385; + export const SharkskinBoots: 386; + export const MeshBoots: 387; + export const BattleBoots: 388; + export const WarBoots: 389; + export const DemonhideSash: 390; + export const SharkskinBelt: 391; + export const MeshBelt: 392; + export const BattleBelt: 393; + export const WarBelt: 394; + export const GrimHelm: 395; + export const GrimShield: 396; + export const BarbedShield: 397; + export const WolfHead: 398; + export const HawkHelm: 399; + export const Antlers: 400; + export const FalconMask: 401; + export const SpiritMask: 402; + export const JawboneCap: 403; + export const FangedHelm: 404; + export const HornedHelm: 405; + export const AssaultHelmet: 406; + export const AvengerGuard: 407; + export const Targe: 408; + export const Rondache: 409; + export const HeraldicShield: 410; + export const AerinShield: 411; + export const CrownShield: 412; + export const PreservedHead: 413; + export const ZombieHead: 414; + export const UnravellerHead: 415; + export const GargoyleHead: 416; + export const DemonHead: 417; + export const Circlet: 418; + export const Coronet: 419; + export const Tiara: 420; + export const Diadem: 421; + export const Shako: 422; + export const Hydraskull: 423; + export const Armet: 424; + export const GiantConch: 425; + export const SpiredHelm: 426; + export const Corona: 427; + export const Demonhead: 428; + export const DuskShroud: 429; + export const Wyrmhide: 430; + export const ScarabHusk: 431; + export const WireFleece: 432; + export const DiamondMail: 433; + export const LoricatedMail: 434; + export const Boneweave: 435; + export const GreatHauberk: 436; + export const BalrogSkin: 437; + export const HellforgePlate: 438; + export const KrakenShell: 439; + export const LacqueredPlate: 440; + export const ShadowPlate: 441; + export const SacredArmor: 442; + export const ArchonPlate: 443; + export const Heater: 444; + export const Luna: 445; + export const Hyperion: 446; + export const Monarch: 447; + export const Aegis: 448; + export const Ward: 449; + export const BrambleMitts: 450; + export const VampireboneGloves: 451; + export const Vambraces: 452; + export const CrusaderGauntlets: 453; + export const OgreGauntlets: 454; + export const WyrmhideBoots: 455; + export const ScarabshellBoots: 456; + export const BoneweaveBoots: 457; + export const MirroredBoots: 458; + export const MyrmidonGreaves: 459; + export const SpiderwebSash: 460; + export const VampirefangBelt: 461; + export const MithrilCoil: 462; + export const TrollBelt: 463; + export const ColossusGirdle: 464; + export const BoneVisage: 465; + export const TrollNest: 466; + export const BladeBarrier: 467; + export const AlphaHelm: 468; + export const GriffonHeaddress: 469; + export const HuntersGuise: 470; + export const SacredFeathers: 471; + export const TotemicMask: 472; + export const JawboneVisor: 473; + export const LionHelm: 474; + export const RageMask: 475; + export const SavageHelmet: 476; + export const SlayerGuard: 477; + export const AkaranTarge: 478; + export const AkaranRondache: 479; + export const ProtectorShield: 480; + export const GildedShield: 481; + export const RoyalShield: 482; + export const MummifiedTrophy: 483; + export const FetishTrophy: 484; + export const SextonTrophy: 485; + export const CantorTrophy: 486; + export const HierophantTrophy: 487; + export const BloodSpirit: 488; + export const SunSpirit: 489; + export const EarthSpirit: 490; + export const SkySpirit: 491; + export const DreamSpirit: 492; + export const CarnageHelm: 493; + export const FuryVisor: 494; + export const DestroyerHelm: 495; + export const ConquerorCrown: 496; + export const GuardianCrown: 497; + export const SacredTarge: 498; + export const SacredRondache: 499; + export const KurastShield: 500; + export const ZakarumShield: 501; + export const VortexShield: 502; + export const MinionSkull: 503; + export const HellspawnSkull: 504; + export const OverseerSkull: 505; + export const SuccubusSkull: 506; + export const BloodlordSkull: 507; + export const Amulet: 520; + export const Ring: 522; + export const Arrows: 526; + export const Bolts: 528; + export const Jewel: 643; // Misc? const Elixir: 508; From 2222888bc9f4a1c0e4b1bf07a8066f04d09937ba Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 2 Mar 2023 15:18:27 -0500 Subject: [PATCH 074/758] Update party.js with the sdk values --- d2bs/kolbot/libs/OOG.js | 8 ++++---- d2bs/kolbot/libs/core/Pickit.js | 9 +++------ d2bs/kolbot/threads/Party.js | 18 +++++++++--------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 40c2d6053..434adc820 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -870,7 +870,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required case sdk.game.locations.MainMenu: if (Profile().type === sdk.game.profiletype.OpenBattlenet) { // check we are on the correct gateway - let realms = {"west": 0, "east": 1, "asia": 2, "europe": 3}; + let realms = { "west": 0, "east": 1, "asia": 2, "europe": 3 }; ControlAction.clickRealm(realms[Profile().gateway.toLowerCase()]); try { login(me.profile); @@ -1063,13 +1063,13 @@ includeIfNotIncluded("oog/D2Bot.js"); // required switch (mode) { case 1: // JoinInfo obj = JSON.parse(msg); - console.debug("Recieved Join Info :: ", obj); + // console.debug("Recieved Join Info :: ", obj); Object.assign(Starter.joinInfo, obj); break; case 2: // Game info obj = JSON.parse(msg); - console.debug("Recieved Game Info :: "); + // console.debug("Recieved Game Info :: ", obj); Object.assign(Starter.gameInfo, obj); break; @@ -1084,7 +1084,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required if (Starter.gameInfo.hasOwnProperty("gameName")) { obj = JSON.parse(msg); - console.debug("Recieved Game Request :: ", obj.profile); + // console.debug("Recieved Game Request :: ", obj); if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { me.gameReady && D2Bot.joinMe(obj.profile, me.gameserverip.toString(), "", "", Starter.isUp); diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 35a6be00e..d3513a8a0 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -357,13 +357,10 @@ const Pickit = { MainLoop: for (let i = 0; i < retry; i += 1) { if (me.dead) return false; - if (this.track.lastItem === gid) { - return true; - } + // recursion appeared + if (this.track.lastItem === gid) return true; // can't find the item - if (!Game.getItem(-1, -1, gid)) { - return false; - } + if (!Game.getItem(-1, -1, gid)) return false; if (me.getItem(item.classid, -1, item.gid)) { console.debug("Already picked item"); diff --git a/d2bs/kolbot/threads/Party.js b/d2bs/kolbot/threads/Party.js index 676f34f9b..268291c2c 100644 --- a/d2bs/kolbot/threads/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -159,15 +159,15 @@ function main() { switch (Config.PublicMode) { case 1: // Invite others case 3: // Invite others but never accept - if (getPlayerFlag(me.gid, player.gid, 8)) { + if (getPlayerFlag(me.gid, player.gid, sdk.player.flag.Hostile)) { if (Config.ShitList && shitList.indexOf(player.name) === -1) { say(player.name + " has been shitlisted."); shitList.push(player.name); ShitList.add(player.name); } - if (player.partyflag === 4) { - clickParty(player, 2); // cancel invitation + if (player.partyflag === sdk.party.flag.Cancel) { + clickParty(player, sdk.party.controls.InviteOrCancel); // cancel invitation delay(100); } @@ -178,8 +178,8 @@ function main() { break; } - if (player.partyflag !== 4 && player.partyflag !== 2 && player.partyid === sdk.party.NoParty) { - clickParty(player, 2); + if (player.partyflag !== sdk.party.flag.Cancel && player.partyflag !== sdk.party.flag.Accept && player.partyid === sdk.party.NoParty) { + clickParty(player, sdk.party.controls.InviteOrCancel); delay(100); } @@ -193,8 +193,8 @@ function main() { break; } - if (player.partyflag === 2 && (getTickCount() - partyTick >= 2000 || Config.FastParty)) { - clickParty(player, 2); + if (player.partyflag === sdk.party.flag.Accept && (getTickCount() - partyTick >= 2000 || Config.FastParty)) { + clickParty(player, sdk.party.controls.InviteOrCancel); delay(100); } } @@ -204,7 +204,7 @@ function main() { if (Config.UnpartyShitlisted) { // Add new hostile players to temp shitlist, leader should have Config.ShitList set to true to update the permanent list. - if (getPlayerFlag(me.gid, player.gid, 8) && shitList.indexOf(player.name) === -1) { + if (getPlayerFlag(me.gid, player.gid, sdk.player.flag.Hostile) && shitList.indexOf(player.name) === -1) { shitList.push(player.name); } @@ -214,7 +214,7 @@ function main() { say(player.name + " is shitlisted. Do not invite them."); } - clickParty(player, 3); + clickParty(player, sdk.party.controls.Leave); delay(100); } } From 8ac4c0335cb862e4fd3bbb1242af2839a6586948 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 7 Mar 2023 13:24:11 -0500 Subject: [PATCH 075/758] Update Cubing.js - debugging info and extra checks to ensure cube is emptied properly --- d2bs/kolbot/libs/core/Cubing.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 90f67dc98..6c4760384 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -1016,7 +1016,7 @@ const Cubing = { let string = "Transmuting: "; let items = this.checkRecipe(tempArray[i]); - if (items) { + if (Array.isArray(items) && items.length) { // 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; @@ -1024,12 +1024,24 @@ const Cubing = { i = -1; + let itemsToCubeCount = items.length; + while (items.length) { string += (items[0].name.trim() + (items.length > 1 ? " + " : "")); Storage.Cube.MoveTo(items[0]); items.shift(); } + let itemsInCube = me.getItemsEx().filter(el => el.isInCube); + if (itemsInCube.length !== itemsToCubeCount) { + console.warn("Failed to move all necesary items to cube"); + itemsInCube.forEach(item => { + if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) return; + if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return; + }); + return false; + } + if (!this.openCube()) return false; transmute(); @@ -1170,13 +1182,14 @@ const Cubing = { emptyCube: function () { let cube = me.getItem(sdk.quest.item.Cube); - let items = me.findItems(-1, -1, sdk.storage.Cube); - if (!cube) return false; + + let items = me.findItems(-1, -1, sdk.storage.Cube); if (!items) return true; while (items.length) { if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { + console.warn("Failed to empty cube. Items still in cube :: ", items.map(i => i && i.prettyPrint)); return false; } From cd36b5fff1e4217e1dbc6b38278f420624bbe649 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:28:14 -0500 Subject: [PATCH 076/758] Cubing + storage fixes - Handling passing in an object to Storage.Container.CanFit - Faster open/close Cube actions - Extra check that the cube is open when attempting to empty the cube, and close the cube when we are done --- d2bs/kolbot/libs/core/Cubing.js | 46 +++++++++++++++++--------------- d2bs/kolbot/libs/core/Storage.js | 33 ++++++++++++++++------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 6c4760384..5cb98121b 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -1136,48 +1136,40 @@ const Cubing = { }, openCube: function () { - let cube = me.getItem(sdk.quest.item.Cube); + if (getUIFlag(sdk.uiflags.Cube)) return true; + let cube = me.getItem(sdk.quest.item.Cube); if (!cube) return false; - if (getUIFlag(sdk.uiflags.Cube)) return true; + if (cube.isInStash && !Town.openStash()) return false; - for (let i = 0; i < 3; i += 1) { + for (let i = 0; i < 5 && !getUIFlag(sdk.uiflags.Cube); i++) { cube.interact(); - let tick = getTickCount(); - while (getTickCount() - tick < 5000) { - if (getUIFlag(sdk.uiflags.Cube)) { - delay(100 + me.ping * 2); // allow UI to initialize + if (Misc.poll(() => getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { + delay(100 + me.ping * 2); // allow UI to initialize - return true; - } - - delay(100); + return true; } } - return false; + return getUIFlag(sdk.uiflags.Cube); }, closeCube: function () { if (!getUIFlag(sdk.uiflags.Cube)) return true; - for (let i = 0; i < 5; i++) { + for (let i = 0; i < 5 && getUIFlag(sdk.uiflags.Cube); i++) { me.cancel(); - let tick = getTickCount(); - while (getTickCount() - tick < 3000) { - if (!getUIFlag(sdk.uiflags.Cube)) { - delay(250 + me.ping * 2); // allow UI to initialize - return true; - } + if (Misc.poll(() => !getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { + delay(250 + me.ping * 2); // allow UI to initialize - delay(100); + return true; } } - return false; + return !getUIFlag(sdk.uiflags.Cube); }, emptyCube: function () { @@ -1187,8 +1179,18 @@ const Cubing = { let items = me.findItems(-1, -1, sdk.storage.Cube); if (!items) return true; + let sorted = false; + while (items.length) { + !getUIFlag(sdk.uiflags.Cube) && Cubing.openCube(); + if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { + // attempt to sort inventory first then try again + if (!sorted && Storage.Inventory.SortItems()) { + sorted = true; + continue; + } + console.warn("Failed to empty cube. Items still in cube :: ", items.map(i => i && i.prettyPrint)); return false; } @@ -1196,6 +1198,8 @@ const Cubing = { items.shift(); } + this.closeCube(); + return true; }, diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index ac5031e40..dc4534e83 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -154,6 +154,9 @@ this.cubeSpot(this.name); + itemIdsLeft === undefined && (itemIdsLeft = Config.SortSettings.ItemsSortedFromLeft); + itemIdsRight === undefined && (itemIdsRight = Config.SortSettings.ItemsSortedFromRight); + let x, y, item, nPos; for (y = this.width - 1; y >= 0; y--) { @@ -244,15 +247,23 @@ */ 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; + } + if (reverseX) { // right-to-left startX = endX - 1; endX = -1; // stops at 0 @@ -308,12 +319,12 @@ } } - //Loop the item size to make sure we can fit it. + // 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 ) { + if (item.gid !== this.itemList[this.buffer[x + nx][y + ny] - 1].gid) { continue Loop; } } @@ -410,9 +421,11 @@ 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; + // handle opening cube + if (item.location === sdk.storage.Cube/* && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item) */) { + if (!getUIFlag(sdk.uiflags.Cube) && !Cubing.openCube()) return false; + // Cube -> Stash, must place item in inventory first + if (this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item)) return false; } // Can't deal with items on ground! From 4ae12b5e6672644639fd81a7f21d4bc8f179818b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 00:43:35 -0500 Subject: [PATCH 077/758] core/Data/ to core/GameData/ - gitignore and intellisense were ignoring the folder because it was named Data --- d2bs/kolbot/libs/core/{Data => GameData}/AreaData.js | 0 d2bs/kolbot/libs/core/{Data => GameData}/GameData.js | 0 d2bs/kolbot/libs/core/{Data => GameData}/LocaleStringID.js | 0 d2bs/kolbot/libs/core/{Data => GameData}/MonsterData.js | 0 d2bs/kolbot/libs/core/{Data => GameData}/NTItemAlias.js | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename d2bs/kolbot/libs/core/{Data => GameData}/AreaData.js (100%) rename d2bs/kolbot/libs/core/{Data => GameData}/GameData.js (100%) rename d2bs/kolbot/libs/core/{Data => GameData}/LocaleStringID.js (100%) rename d2bs/kolbot/libs/core/{Data => GameData}/MonsterData.js (100%) rename d2bs/kolbot/libs/core/{Data => GameData}/NTItemAlias.js (100%) diff --git a/d2bs/kolbot/libs/core/Data/AreaData.js b/d2bs/kolbot/libs/core/GameData/AreaData.js similarity index 100% rename from d2bs/kolbot/libs/core/Data/AreaData.js rename to d2bs/kolbot/libs/core/GameData/AreaData.js diff --git a/d2bs/kolbot/libs/core/Data/GameData.js b/d2bs/kolbot/libs/core/GameData/GameData.js similarity index 100% rename from d2bs/kolbot/libs/core/Data/GameData.js rename to d2bs/kolbot/libs/core/GameData/GameData.js diff --git a/d2bs/kolbot/libs/core/Data/LocaleStringID.js b/d2bs/kolbot/libs/core/GameData/LocaleStringID.js similarity index 100% rename from d2bs/kolbot/libs/core/Data/LocaleStringID.js rename to d2bs/kolbot/libs/core/GameData/LocaleStringID.js diff --git a/d2bs/kolbot/libs/core/Data/MonsterData.js b/d2bs/kolbot/libs/core/GameData/MonsterData.js similarity index 100% rename from d2bs/kolbot/libs/core/Data/MonsterData.js rename to d2bs/kolbot/libs/core/GameData/MonsterData.js diff --git a/d2bs/kolbot/libs/core/Data/NTItemAlias.js b/d2bs/kolbot/libs/core/GameData/NTItemAlias.js similarity index 100% rename from d2bs/kolbot/libs/core/Data/NTItemAlias.js rename to d2bs/kolbot/libs/core/GameData/NTItemAlias.js From c111fa5f7b2bfbc7a1bab831821a0f547efa491e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 00:45:07 -0500 Subject: [PATCH 078/758] Create RuneData.js - Cleaner way to handle Runeword data structure --- d2bs/kolbot/libs/core/GameData/RuneData.js | 275 +++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 d2bs/kolbot/libs/core/GameData/RuneData.js diff --git a/d2bs/kolbot/libs/core/GameData/RuneData.js b/d2bs/kolbot/libs/core/GameData/RuneData.js new file mode 100644 index 000000000..3cbd59c9e --- /dev/null +++ b/d2bs/kolbot/libs/core/GameData/RuneData.js @@ -0,0 +1,275 @@ +(function (module) { + const RunesData = (function () { + /** @type {Array} runewords - Array of runeword objects. */ + const runewords = []; + const ladder = me.ladder > 0; + const RUNES_COUNT = 169; + + const validRunes = Object.values(sdk.items.runes).filter(v => !isNaN(v)); + const validInsertable = (id) => { + if (validRunes.includes(id)) return true; + if (id === sdk.items.Jewel) return true; + return id >= sdk.items.gems.Chipped.Amethyst && id <= sdk.items.gems.Perfect.Skull; + }; + const anyShield = [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads]; + const missileWeapon = [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow]; + const meleeWeapons = [ + sdk.items.type.Scepter, sdk.items.type.Wand, sdk.items.type.AmazonSpear, + sdk.items.type.Axe, sdk.items.type.Hammer, sdk.items.type.Mace, + sdk.items.type.Sword, sdk.items.type.Knife, sdk.items.type.AssassinClaw, + sdk.items.type.Polearm, sdk.items.type.Scepter, sdk.items.type.HandtoHand + ]; + const ladderRws = [ + "Brand", "Death", "Destruction", "Dragon", "Edge", "Fortitude", "Grief", + "Ice", "Infinity", "Insight", "LastWish", "Lawbringer", "Oath", "Obedience", + "Phoenix", "Pride", "Rift", "Spirit", "VoiceofReason", "White", + ]; + + function getItemType (iType) { + switch (iType) { + case sdk.items.type.AnyShield: + return anyShield; + case sdk.items.type.Weapon: + return [].concat(missileWeapon, meleeWeapons); + case sdk.items.type.MissileWeapon: + return missileWeapon; + case sdk.items.type.MeleeWeapon: + return meleeWeapons; + case sdk.items.type.Helm: + return [sdk.items.type.Helm, sdk.items.type.Circlet, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; + default: + return [iType]; + } + } + + /** + * @constructor + * @param {string} name - The name of the recipe. + * @param {number} sockets - The number of sockets required for the recipe. + * @param {number[]} runes - Array of insertable IDs required for the recipe. + * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. + */ + function RunewordObj (name, sockets, runes, itemTypes) { + this.name = name; + this.sockets = sockets; + this.runes = runes; + this.itemTypes = itemTypes; + this._ladder = ladderRws.includes(name); + } + + RunewordObj.prototype.ladderRestricted = function () { + // not ladder restricted or we are on ladder + if (!this._ladder || ladder) return false; + // ladder restricted and we have enabled ladder override + if (Config.LadderOveride) return false; + // ladder restricted + return true; + }; + + /** + * Finds a runeword by name. + * @param {string} name - The name of the runeword. + * @returns {Runeword} - The runeword object. + */ + const findByName = function (name) { + return runewords.find(r => String.isEqual(r.name, name)); + }; + + /** + * Find all runewords that have the given rune. + * @param {number} rune - classid of rune + * @returns {Array} + */ + const findByRune = function (rune) { + return runewords.filter(r => r.runes.includes(rune)); + }; + + /** + * Find all runewords that can be applied to the given item type. + * @param {number} type - item type + * @returns {Array} + */ + const findByType = function (type) { + return runewords.filter(r => r.itemTypes.includes(type)); + }; + + /** + * Create a new non standard runeword. + * @param {string} name - The name of the recipe. + * @param {number} sockets - The number of sockets required for the recipe. + * @param {number[]} runes - Array of insertable IDs required for the recipe. + * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. + * @returns {runeword} - The new runeword object. + */ + const addRuneword = function (name, sockets, runes, itemTypes) { + if (!name || !sockets || !runes || !itemTypes) return false; + !Array.isArray(runes) && (runes = [runes]); + if (!runes.every(validInsertable)) return false; + !Array.isArray(itemTypes) && (itemTypes = [itemTypes]); + + let rw = new RunewordObj(name, runes.length, runes, itemTypes.map(getItemType).flat()); + runewords.push(rw); + + return rw; + }; + + for (let i = 0; i < RUNES_COUNT; i++) { + const index = i; + if (!getBaseStat("runes", index, "complete")) continue; + + const runes = []; + + for (let r = 1; r < 7; r++) { + const rune = getBaseStat("runes", index, "rune" + r); + if (rune > -1 && validRunes.includes(rune)) { + runes.push(rune); + } else { + break; + } + } + + const itemTypes = [ + getBaseStat("runes", index, "itype1"), + getBaseStat("runes", index, "itype2"), + getBaseStat("runes", index, "itype3"), + getBaseStat("runes", index, "itype4"), + getBaseStat("runes", index, "itype5"), + getBaseStat("runes", index, "itype6"), + ].filter(el => el && el !== 65535).map(getItemType).flat(); + + const name = (() => { + let temp = getBaseStat("runes", index, "rune name"); + + switch (temp) { + case "The Beast": + return "Beast"; + case "Bound by Duty": + return "ChainsofHonor"; + case "Doomsayer": + return "Doom"; + case "Exile's Path": + return "Exile"; + case "Widowmaker": + return "Grief"; + case "Winter": + return "VoiceofReason"; + default: + return temp.replace(/[^a-zA-Z0-9]/g, ""); + } + })(); + + runewords.push(new RunewordObj(name, runes.length, runes, itemTypes)); + } + + return { + // runewords: runewords, // other files don't actually need this + // 1.09 + AncientsPledge: findByName("AncientsPledge"), + Black: findByName("Black"), + Fury: findByName("Fury"), + HolyThunder: findByName("HolyThunder"), + Honor: findByName("Honor"), + KingsGrace: findByName("KingsGrace"), + Leaf: findByName("Leaf"), + Lionheart: findByName("Lionheart"), + Lore: findByName("Lore"), + Malice: findByName("Malice"), + Melody: findByName("Melody"), + Memory: findByName("Memory"), + Nadir: findByName("Nadir"), + Radiance: findByName("Radiance"), + Rhyme: findByName("Rhyme"), + Silence: findByName("Silence"), + Smoke: findByName("Smoke"), + Stealth: findByName("Stealth"), + Steel: findByName("Steel"), + Strength: findByName("Strength"), + Venom: findByName("Venom"), + Wealth: findByName("Wealth"), + White: findByName("White"), + Zephyr: findByName("Zephyr"), + + // 1.10 + Beast: findByName("Beast"), + Bramble: findByName("Bramble"), + BreathoftheDying: findByName("BreathoftheDying"), + CallToArms: findByName("CallToArms"), + ChainsofHonor: findByName("ChainsofHonor"), + Chaos: findByName("Chaos"), + CrescentMoon: findByName("CrescentMoon"), + Delirium: findByName("Delirium"), + Doom: findByName("Doom"), + Duress: findByName("Duress"), + Enigma: findByName("Enigma"), + Eternity: findByName("Eternity"), + Exile: findByName("Exile"), + Famine: findByName("Famine"), + Gloom: findByName("Gloom"), + HandofJustice: findByName("HandofJustice"), + HeartoftheOak: findByName("HeartoftheOak"), + Kingslayer: findByName("Kingslayer"), + Passion: findByName("Passion"), + Prudence: findByName("Prudence"), + Sanctuary: findByName("Sanctuary"), + Splendor: findByName("Splendor"), + Stone: findByName("Stone"), + Wind: findByName("Wind"), + + // ladder only + Brand: findByName("Brand"), + Death: findByName("Death"), + Destruction: findByName("Destruction"), + Dragon: findByName("Dragon"), + Dream: findByName("Dream"), + Edge: findByName("Edge"), + Faith: findByName("Faith"), + Fortitude: findByName("Fortitude"), + Grief: findByName("Grief"), + Harmony: findByName("Harmony"), + Ice: findByName("Ice"), + Infinity: findByName("Infinity"), + Insight: findByName("Insight"), + LastWish: findByName("LastWish"), + Lawbringer: findByName("Lawbringer"), + Oath: findByName("Oath"), + Obedience: findByName("Obedience"), + Phoenix: findByName("Phoenix"), + Pride: findByName("Pride"), + Rift: findByName("Rift"), + Spirit: findByName("Spirit"), + VoiceofReason: findByName("VoiceofReason"), + Wrath: findByName("Wrath"), + + // 1.11 + Bone: findByName("Bone"), + Enlightenment: findByName("Enlightenment"), + Myth: findByName("Myth"), + Peace: findByName("Peace"), + Principle: findByName("Principle"), + Rain: findByName("Rain"), + Treachery: findByName("Treachery"), + + Test: (() => { + addRuneword("Test", 3, + [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel], + [sdk.items.type.Armor, sdk.items.type.AnyShield, sdk.items.type.Weapon, sdk.items.type.Helm] + ); + return findByName("Test"); + })(), + + addRuneword: addRuneword, + findByName: findByName, + findByRune: findByRune, + findByType: findByType, + }; + })(); + + Object.defineProperties(RunesData, { + "addRuneword": { enumerable: false }, + "findByName": { enumerable: false }, + "findByRune": { enumerable: false }, + "findByType": { enumerable: false }, + }); + + module.exports = RunesData; +})(module); From 6d6bd390839e0e1fbe07cc592fa6f72912658291 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 00:46:41 -0500 Subject: [PATCH 079/758] Update Polyfill.js - Type handling for console --- d2bs/kolbot/libs/Polyfill.js | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 83b250cf2..25430439c 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -27,7 +27,7 @@ String.prototype.lcsGraph = function (compareToThis) { } } - return {a: this.toString(), b: compareToThis, graph: graph}; + return { a: this.toString(), b: compareToThis, graph: graph }; }; String.prototype.diffCount = function (stringB) { @@ -602,7 +602,39 @@ if (!Object.entries) { (function (global, print) { global.console = global.console || (function () { const console = {}; - const argMap = el => typeof el === "object" && el /*not null */ && JSON.stringify(el) || el; + + const argMap = (el) => { + switch (typeof el) { + case "undefined": + return "undefined"; + case "boolean": + return el ? "true" : false; + case "function": + return "function"; + case "object": + if (el === null) return "null"; + if (el instanceof Error) { + return JSON.stringify({ + name: (el.name || "Error"), + fileName: (el.fileName || "unknown"), + lineNumber: (el.lineNumber || ":?"), + message: (el.message || ""), + stack: (el.stack || ""), + }); + } + if (el instanceof Map) { + return el.toString(); + } + if (Array.isArray(el)) { + // handle multidimensional arrays + return JSON.stringify( + el.map(inner => Array.isArray(inner) ? inner.map(argMap) : inner) + ); + } + return JSON.stringify(el); + } + return el; + }; console.log = function (...args) { // use call to avoid type errors From 0c7a3bfed951813531730d31c314cc921547b313 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 00:59:21 -0500 Subject: [PATCH 080/758] Rebuild Runeword data structure - Use new RuneData module - Update Runewords methods - Fix Bug in Runeworc.checkRunewords that was removing available ingredients from item list for failed runewords causing use to fail to make runewords even when we had all the ingredients - Add itemType checking to fix user errors of attempting to use a recipe that wasn't compatible - Add easy ability to toggle ladder runewords without editing the files if they've been enabled offline. Use `Config.LadderOverride = true` - todo: still need to figure out how to add stats --- d2bs/kolbot/libs/core/Config.js | 4 + d2bs/kolbot/libs/core/Runewords.js | 209 +++++++++++--------------- d2bs/kolbot/sdk/types/Runewords.d.ts | 210 ++++++++++++++------------- 3 files changed, 199 insertions(+), 224 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index e8d7c9bff..74c92ce32 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -277,8 +277,12 @@ let Config = { RepairPercent: 40, Recipes: [], MakeRunewords: false, + /** + * @type {[runeword, string | number, ?boolean][]} + */ Runewords: [], KeepRunewords: [], + LadderOveride: false, Gamble: false, GambleItems: [], GambleGoldStart: 0, diff --git a/d2bs/kolbot/libs/core/Runewords.js b/d2bs/kolbot/libs/core/Runewords.js index 5a101b634..fec44e132 100644 --- a/d2bs/kolbot/libs/core/Runewords.js +++ b/d2bs/kolbot/libs/core/Runewords.js @@ -1,101 +1,11 @@ /** * @filename Runewords.js -* @author kolton +* @author kolton, theBGuy * @desc make and reroll runewords * */ -// TODO: Config.Runewords[i][0] can be false, but array methods can be used on it - -const Runeword = { - // 1.09 - AncientsPledge: [sdk.items.runes.Ral, sdk.items.runes.Ort, sdk.items.runes.Tal], // Ral + Ort + Tal - Black: [sdk.items.runes.Thul, sdk.items.runes.Io, sdk.items.runes.Nef], // Thul + Io + Nef - Fury: [sdk.items.runes.Jah, sdk.items.runes.Gul, sdk.items.runes.Eth], // Jah + Gul + Eth - HolyThunder: [sdk.items.runes.Eth, sdk.items.runes.Ral, sdk.items.runes.Ort, sdk.items.runes.Tal], // Eth + Ral + Ort + Tal - Honor: [sdk.items.runes.Amn, sdk.items.runes.El, sdk.items.runes.Ith, sdk.items.runes.Tir, sdk.items.runes.Sol], // Amn + El + Ith + Tir + Sol - KingsGrace: [sdk.items.runes.Amn, sdk.items.runes.Ral, sdk.items.runes.Thul], // Amn + Ral + Thul - Leaf: [sdk.items.runes.Tir, sdk.items.runes.Ral], // Tir + Ral - Lionheart: [sdk.items.runes.Hel, sdk.items.runes.Lum, sdk.items.runes.Fal], // Hel + Lum + Fal - Lore: [sdk.items.runes.Ort, sdk.items.runes.Sol], // Ort + Sol - Malice: [sdk.items.runes.Ith, sdk.items.runes.El, sdk.items.runes.Eth], // Ith + El + Eth - Melody: [sdk.items.runes.Shael, sdk.items.runes.Ko, sdk.items.runes.Nef], // Shael + Ko + Nef - Memory: [sdk.items.runes.Lum, sdk.items.runes.Io, sdk.items.runes.Sol, sdk.items.runes.Eth], // Lum + Io + Sol + Eth - Nadir: [sdk.items.runes.Nef, sdk.items.runes.Tir], // Nef + Tir - Radiance: [sdk.items.runes.Nef, sdk.items.runes.Sol, sdk.items.runes.Ith], // Nef + Sol + Ith - Rhyme: [sdk.items.runes.Shael, sdk.items.runes.Eth], // Shael + Eth - Silence: [sdk.items.runes.Dol, sdk.items.runes.Eld, sdk.items.runes.Hel, sdk.items.runes.Ist, sdk.items.runes.Tir, sdk.items.runes.Vex], // Dol + Eld + Hel + Ist + Tir + Vex - Smoke: [sdk.items.runes.Nef, sdk.items.runes.Lum], // Nef + Lum - Stealth: [sdk.items.runes.Tal, sdk.items.runes.Eth], // Tal + Eth - Steel: [sdk.items.runes.Tir, sdk.items.runes.El], // Tir + El - Strength: [sdk.items.runes.Amn, sdk.items.runes.Tir], // Amn + Tir - Venom: [sdk.items.runes.Tal, sdk.items.runes.Dol, sdk.items.runes.Mal], // Tal + Dol + Mal - Wealth: [sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.runes.Tir], // Lem + Ko + Tir - White: [sdk.items.runes.Dol, sdk.items.runes.Io], // Dol + Io - Zephyr: [sdk.items.runes.Ort, sdk.items.runes.Eth], // Ort + Eth - - // 1.10 - Beast: [sdk.items.runes.Ber, sdk.items.runes.Tir, sdk.items.runes.Um, sdk.items.runes.Mal, sdk.items.runes.Lum], // Ber + Tir + Um + Mal + Lum - Bramble: [sdk.items.runes.Ral, sdk.items.runes.Ohm, sdk.items.runes.Sur, sdk.items.runes.Eth], // Ral + Ohm + Sur + Eth - BreathoftheDying: [sdk.items.runes.Vex, sdk.items.runes.Hel, sdk.items.runes.El, sdk.items.runes.Eld, sdk.items.runes.Zod, sdk.items.runes.Eth], // Vex + Hel + El + Eld + Zod + Eth - CallToArms: [sdk.items.runes.Amn, sdk.items.runes.Ral, sdk.items.runes.Mal, sdk.items.runes.Ist, sdk.items.runes.Ohm], // Amn + Ral + Mal + Ist + Ohm - ChainsofHonor: [sdk.items.runes.Dol, sdk.items.runes.Um, sdk.items.runes.Ber, sdk.items.runes.Ist], // Dol + Um + Ber + Ist - Chaos: [sdk.items.runes.Fal, sdk.items.runes.Ohm, sdk.items.runes.Um], // Fal + Ohm + Um - CrescentMoon: [sdk.items.runes.Shael, sdk.items.runes.Um, sdk.items.runes.Tir], // Shael + Um + Tir - Delirium: [sdk.items.runes.Lem, sdk.items.runes.Ist, sdk.items.runes.Io], // Lem + Ist + Io - Doom: [sdk.items.runes.Hel, sdk.items.runes.Ohm, sdk.items.runes.Um, sdk.items.runes.Lo, sdk.items.runes.Cham], // Hel + Ohm + Um + Lo + Cham - Duress: [sdk.items.runes.Shael, sdk.items.runes.Um, sdk.items.runes.Thul], // Shael + Um + Thul - Enigma: [sdk.items.runes.Jah, sdk.items.runes.Ith, sdk.items.runes.Ber], // Jah + Ith + Ber - Eternity: [sdk.items.runes.Amn, sdk.items.runes.Ber, sdk.items.runes.Ist, sdk.items.runes.Sol, sdk.items.runes.Sur], // Amn + Ber + Ist + Sol + Sur - Exile: [sdk.items.runes.Vex, sdk.items.runes.Ohm, sdk.items.runes.Ist, sdk.items.runes.Dol], // Vex + Ohm + Ist + Dol - Famine: [sdk.items.runes.Fal, sdk.items.runes.Ohm, sdk.items.runes.Ort, sdk.items.runes.Jah], // Fal + Ohm + Ort + Jah - Gloom: [sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.runes.Pul], // Fal + Um + Pul - HandofJustice: [sdk.items.runes.Sur, sdk.items.runes.Cham, sdk.items.runes.Amn, sdk.items.runes.Lo], // Sur + Cham + Amn + Lo - HeartoftheOak: [sdk.items.runes.Ko, sdk.items.runes.Vex, sdk.items.runes.Pul, sdk.items.runes.Thul], // Ko + Vex + Pul + Thul - Kingslayer: [sdk.items.runes.Mal, sdk.items.runes.Um, sdk.items.runes.Gul, sdk.items.runes.Fal], // Mal + Um + Gul + Fal - Passion: [sdk.items.runes.Dol, sdk.items.runes.Ort, sdk.items.runes.Eld, sdk.items.runes.Lem], // Dol + Ort + Eld + Lem - Prudence: [sdk.items.runes.Mal, sdk.items.runes.Tir], // Mal + Tir - Sanctuary: [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Mal], // Ko + Ko + Mal - Splendor: [sdk.items.runes.Eth, sdk.items.runes.Lum], // Eth + Lum - Stone: [sdk.items.runes.Shael, sdk.items.runes.Um, sdk.items.runes.Pul, sdk.items.runes.Lum], // Shael + Um + Pul + Lum - Wind: [sdk.items.runes.Sur, sdk.items.runes.El], // Sur + El - - // Don't use ladder-only on NL - Brand: me.ladder ? [sdk.items.runes.Jah, sdk.items.runes.Lo, sdk.items.runes.Mal, sdk.items.runes.Gul] : false, // Jah + Lo + Mal + Gul - Death: me.ladder ? [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 - Destruction: me.ladder ? [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 - Dragon: me.ladder ? [sdk.items.runes.Sur, sdk.items.runes.Lo, sdk.items.runes.Sol] : false, // Sur + Lo + Sol - Dream: me.ladder ? [sdk.items.runes.Io, sdk.items.runes.Jah, sdk.items.runes.Pul] : false, // Io + Jah + Pul - Edge: me.ladder ? [sdk.items.runes.Tir, sdk.items.runes.Tal, sdk.items.runes.Amn] : false, // Tir + Tal + Amn - Faith: me.ladder ? [sdk.items.runes.Ohm, sdk.items.runes.Jah, sdk.items.runes.Lem, sdk.items.runes.Eld] : false, // Ohm + Jah + Lem + Eld - Fortitude: me.ladder ? [sdk.items.runes.El, sdk.items.runes.Sol, sdk.items.runes.Dol, sdk.items.runes.Lo] : false, // El + Sol + Dol + Lo - Grief: me.ladder ? [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 - Harmony: me.ladder ? [sdk.items.runes.Tir, sdk.items.runes.Ith, sdk.items.runes.Sol, sdk.items.runes.Ko] : false, // Tir + Ith + Sol + Ko - Ice: me.ladder ? [sdk.items.runes.Amn, sdk.items.runes.Shael, sdk.items.runes.Jah, sdk.items.runes.Lo] : false, // Amn + Shael + Jah + Lo - "Infinity": me.ladder ? [sdk.items.runes.Ber, sdk.items.runes.Mal, sdk.items.runes.Ber, sdk.items.runes.Ist] : false, // Ber + Mal + Ber + Ist - Insight: me.ladder ? [sdk.items.runes.Ral, sdk.items.runes.Tir, sdk.items.runes.Tal, sdk.items.runes.Sol] : false, // Ral + Tir + Tal + Sol - LastWish: me.ladder ? [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 - Lawbringer: me.ladder ? [sdk.items.runes.Amn, sdk.items.runes.Lem, sdk.items.runes.Ko] : false, // Amn + Lem + Ko - Oath: me.ladder ? [sdk.items.runes.Shael, sdk.items.runes.Pul, sdk.items.runes.Mal, sdk.items.runes.Lum] : false, // Shael + Pul + Mal + Lum - Obedience: me.ladder ? [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 - Phoenix: me.ladder ? [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.runes.Lo, sdk.items.runes.Jah] : false, // Vex + Vex + Lo + Jah - Pride: me.ladder ? [sdk.items.runes.Cham, sdk.items.runes.Sur, sdk.items.runes.Io, sdk.items.runes.Lo] : false, // Cham + Sur + Io + Lo - Rift: me.ladder ? [sdk.items.runes.Hel, sdk.items.runes.Ko, sdk.items.runes.Lem, sdk.items.runes.Gul] : false, // Hel + Ko + Lem + Gul - Spirit: me.ladder ? [sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.runes.Ort, sdk.items.runes.Amn] : false, // Tal + Thul + Ort + Amn - VoiceofReason: me.ladder ? [sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.runes.El, sdk.items.runes.Eld] : false, // Lem + Ko + El + Eld - Wrath: me.ladder ? [sdk.items.runes.Pul, sdk.items.runes.Lum, sdk.items.runes.Ber, sdk.items.runes.Mal] : false, // Pul + Lum + Ber + Mal - - // 1.11 - Bone: [sdk.items.runes.Sol, sdk.items.runes.Um, sdk.items.runes.Um], // Sol + Um + Um - Enlightenment: [sdk.items.runes.Pul, sdk.items.runes.Ral, sdk.items.runes.Sol], // Pul + Ral + Sol - Myth: [sdk.items.runes.Hel, sdk.items.runes.Amn, sdk.items.runes.Nef], // Hel + Amn + Nef - Peace: [sdk.items.runes.Shael, sdk.items.runes.Thul, sdk.items.runes.Amn], // Shael + Thul + Amn - Principle: [sdk.items.runes.Ral, sdk.items.runes.Gul, sdk.items.runes.Eld], // Ral + Gul + Eld - Rain: [sdk.items.runes.Ort, sdk.items.runes.Mal, sdk.items.runes.Ith], // Ort + Mal + Ith - Treachery: [sdk.items.runes.Shael, sdk.items.runes.Thul, sdk.items.runes.Lem], // Shael + Thul + Lem - - Test: [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel] -}; +const Runeword = require("./GameData/RuneData"); const Runewords = { needList: [], @@ -105,7 +15,7 @@ const Runewords = { init: function () { if (!Config.MakeRunewords) return; - this.pickitEntries = []; + Runewords.pickitEntries = []; // initiate pickit entries for (let i = 0; i < Config.KeepRunewords.length; i += 1) { @@ -120,12 +30,14 @@ const Runewords = { // change text to classid for (let i = 0; i < Config.Runewords.length; i += 1) { - if (Config.Runewords[i][0] !== false) { - if (isNaN(Config.Runewords[i][1])) { - if (NTIPAliasClassID.hasOwnProperty(Config.Runewords[i][1].replace(/\s+/g, "").toLowerCase())) { - Config.Runewords[i][1] = NTIPAliasClassID[Config.Runewords[i][1].replace(/\s+/g, "").toLowerCase()]; + 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 " + Config.Runewords[i][1]); + Misc.errorReport("ÿc1Invalid runewords entry:ÿc0 " + base); Config.Runewords.splice(i, 1); i -= 1; @@ -137,27 +49,38 @@ const Runewords = { this.buildLists(); }, + /** + * Ensures this item isn't wanted by the CraftingSystem + * @param {ItemUnit} item + * @returns {boolean} + * @todo Why only the crafting system? + */ validItem: function (item) { return CraftingSystem.validGids.indexOf(item.gid) === -1; }, - // build a list of needed runes. won't count runes until the base item is found for a given runeword + /** + * build a list of needed runes. won't count runes until the base item is found for a given runeword + * @returns {void} + */ buildLists: function () { - this.validGids = []; - this.needList = []; + Runewords.validGids = []; + Runewords.needList = []; let baseCheck; let items = me.findItems(-1, sdk.items.mode.inStorage); for (let i = 0; i < Config.Runewords.length; i += 1) { + const [runeword, base, ethFlag] = Config.Runewords[i]; + if (!baseCheck) { - baseCheck = this.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) || this.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true); + baseCheck = this.getBase(runeword, base, (ethFlag || 0)) || this.getBase(runeword, base, (ethFlag || 0), true); } - if (this.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0))) { + if (this.getBase(runeword, base, (ethFlag || 0))) { RuneLoop: - for (let j = 0; j < Config.Runewords[i][0].length; j += 1) { + for (let j = 0; j < runeword.runes.length; j += 1) { for (let k = 0; k < items.length; k += 1) { - if (items[k].classid === Config.Runewords[i][0][j] && this.validItem(items[k])) { + if (items[k].classid === runeword.runes[j] && this.validItem(items[k])) { this.validGids.push(items[k].gid); items.splice(k, 1); @@ -167,7 +90,7 @@ const Runewords = { } } - this.needList.push(Config.Runewords[i][0][j]); + this.needList.push(runeword.runes[j]); } } } @@ -190,6 +113,10 @@ const Runewords = { } }, + /** + * @param {number} classid + * @param {number} gid + */ update: function (classid, gid) { for (let i = 0; i < this.needList.length; i += 1) { if (this.needList[i] === classid) { @@ -204,20 +131,27 @@ const Runewords = { this.validGids.push(gid); }, - // returns an array of items that make a runeword if found, false if we don't have enough items for any + /** + * returns an array of items that make a runeword if found, false if we don't have enough items for any + * @returns {ItemUnit[] | boolean} + */ checkRunewords: function () { - let items = 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 base = this.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)); // check base + let items = itemsRef.slice(); // copy itemsRef + + const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; + let base = this.getBase(runeword, wantedBase, (ethFlag || 0)); // check base if (base) { itemList.push(base); // push the base - for (let j = 0; j < Config.Runewords[i][0].length; j += 1) { + for (let j = 0; j < runeword.runes.length; j += 1) { for (let k = 0; k < items.length; k += 1) { - if (items[k].classid === Config.Runewords[i][0][j]) { // rune matched + 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 @@ -232,7 +166,7 @@ const Runewords = { break; } - if (itemList.length === Config.Runewords[i][0].length + 1) { // runes + base + if (itemList.length === runeword.runes.length + 1) { // runes + base return itemList; // these items are our runeword } } @@ -242,30 +176,46 @@ const Runewords = { return false; }, - // for pickit + /** + * for pickit + * @param {ItemUnit} unit + * @returns {boolean} + */ checkItem: function (unit) { if (!Config.MakeRunewords) return false; return (unit.itemType === sdk.items.type.Rune && this.needList.includes(unit.classid)); }, - // for clearInventory - don't drop runes that are a part of runeword recipe + /** + * for clearInventory - don't drop runes that are a part of runeword recipe + * @param {ItemUnit} unit + * @returns {boolean} + */ keepItem: function (unit) { return this.validGids.includes(unit.gid); }, - /* get the base item based on classid and runeword recipe - optional reroll argument = gets a runeword that needs rerolling - rigged to accept item or classid as 2nd arg - */ + /** + * 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} + */ 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 (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()) || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries))) { if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { @@ -279,7 +229,11 @@ const Runewords = { return false; }, - // args named this way to prevent confusion + /** + * @param {ItemUnit} base + * @param {ItemUnit} rune + * @returns {boolean} + */ socketItem: function (base, rune) { if (!rune.toCursor()) return false; @@ -345,7 +299,7 @@ const Runewords = { this.socketItem(items[0], items[i]); } - print("ÿc4Runewords: ÿc0Made runeword: " + items[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); + console.log("ÿc4Runewords: ÿc0Made runeword: " + items[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); D2Bot.printToConsole("Made runeword: " + items[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""), sdk.colors.D2Bot.Green); if (NTIP.CheckItem(items[0], this.pickitEntries)) { @@ -366,7 +320,8 @@ const Runewords = { let hel = me.getItem(sdk.items.runes.Hel, sdk.items.mode.inStorage); if (!hel) return false; - let base = this.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true); // get a bad runeword + const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; + let base = this.getBase(runeword, wantedBase, (ethFlag || 0), true); // get a bad runeword if (base) { let scroll = this.getScroll(); @@ -382,7 +337,7 @@ const Runewords = { // probably only happens on server crash if (!Cubing.openCube()) return false; - print("ÿc4Runewords: ÿc0Rerolling runeword: " + base.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); + console.log("ÿc4Runewords: ÿc0Rerolling runeword: " + base.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); D2Bot.printToConsole("Rerolling runeword: " + base.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""), sdk.colors.D2Bot.Green); transmute(); delay(500); diff --git a/d2bs/kolbot/sdk/types/Runewords.d.ts b/d2bs/kolbot/sdk/types/Runewords.d.ts index 4b940454e..19543d5cb 100644 --- a/d2bs/kolbot/sdk/types/Runewords.d.ts +++ b/d2bs/kolbot/sdk/types/Runewords.d.ts @@ -1,105 +1,121 @@ export {}; declare global { - export const Runewords: { - init(): void - validItem(item: any): void - buildLists(): void - update(classid: any, gid: any): void - checkRunewords(): void - checkItem(unit: any): boolean - keepItem(unit: any): boolean - getBase(runeword: any, base: any, ethFlag: any, reroll: any): void - socketItem(base: any, rune: any): void - getScroll(): void - makeRunewords(): void - rerollRunewords(): void + /** + * @property {string} name - The name of the runeword. + * @property {number} sockets - The number of sockets required for the item. + * @property {Array} runes - Array of rune IDs required for the runeword. + * @property {Array} itemTypes - Array of item type IDs the runeword can be applied to. + * @method ladderRestricted - Returns true if we are unable to make the runeword because we are not on ladder. + */ + interface runeword { + name: string; + sockets: number; + runes: number[]; + itemTypes: number[]; + _ladder: boolean; + ladderRestricted: () => boolean; } - export const Runeword: { - AncientsPledge: number[]; - Black: number[]; - Fury: number[]; - HolyThunder: number[]; - Honor: number[]; - KingsGrace: number[]; - Leaf: number[]; - Lionheart: number[]; - Lore: number[]; - Malice: number[]; - Melody: number[]; - Memory: number[]; - Nadir: number[]; - Radiance: number[]; - Rhyme: number[]; - Silence: number[]; - Smoke: number[]; - Stealth: number[]; - Steel: number[]; - Strength: number[]; - Venom: number[]; - Wealth: number[]; - White: number[]; - Zephyr: number[]; - Beast: number[]; - Bramble: number[]; - BreathoftheDying: number[]; - CallToArms: number[]; - ChainsofHonor: number[]; - Chaos: number[]; - CrescentMoon: number[]; - Delirium: number[]; - Doom: number[]; - Duress: number[]; - Enigma: number[]; - Eternity: number[]; - Exile: number[]; - Famine: number[]; - Gloom: number[]; - HandofJustice: number[]; - HeartoftheOak: number[]; - Kingslayer: number[]; - Passion: number[]; - Prudence: number[]; - Sanctuary: number[]; - Splendor: number[]; - Stone: number[]; - Wind: number[]; - Brand: number[]; - Death: number[]; - Destruction: number[]; - Dragon: number[]; - Dream: number[]; - Edge: number[]; - Faith: number[]; - Fortitude: number[]; - Grief: number[]; - Harmony: number[]; - Ice: number[]; - Infinity: number[]; - Insight: number[]; - LastWish: number[]; - Lawbringer: number[]; - Oath: number[]; - Obedience: number[]; - Phoenix: number[]; - Pride: number[]; - Rift: number[]; - Spirit: number[]; - VoiceofReason: number[]; - Wrath: number[]; - Bone: number[]; - Enlightenment: number[]; - Myth: number[]; - Peace: number[]; - Principle: number[]; - Rain: number[]; - Treachery: number[]; - Test: number[]; + namespace Runeword { + const AncientsPledge: runeword; + const Black: runeword; + const Fury: runeword; + const HolyThunder: runeword; + const Honor: runeword; + const KingsGrace: runeword; + const Leaf: runeword; + const Lionheart: runeword; + const Lore: runeword; + const Malice: runeword; + const Melody: runeword; + const Memory: runeword; + const Nadir: runeword; + const Radiance: runeword; + const Rhyme: runeword; + const Silence: runeword; + const Smoke: runeword; + const Stealth: runeword; + const Steel: runeword; + const Strength: runeword; + const Venom: runeword; + const Wealth: runeword; + const White: runeword; + const Zephyr: runeword; + const Beast: runeword; + const Bramble: runeword; + const BreathoftheDying: runeword; + const CallToArms: runeword; + const ChainsofHonor: runeword; + const Chaos: runeword; + const CrescentMoon: runeword; + const Delirium: runeword; + const Doom: runeword; + const Duress: runeword; + const Enigma: runeword; + const Eternity: runeword; + const Exile: runeword; + const Famine: runeword; + const Gloom: runeword; + const HandofJustice: runeword; + const HeartoftheOak: runeword; + const Kingslayer: runeword; + const Passion: runeword; + const Prudence: runeword; + const Sanctuary: runeword; + const Splendor: runeword; + const Stone: runeword; + const Wind: runeword; + const Brand: runeword; + const Death: runeword; + const Destruction: runeword; + const Dragon: runeword; + const Dream: runeword; + const Edge: runeword; + const Faith: runeword; + const Fortitude: runeword; + const Grief: runeword; + const Harmony: runeword; + const Ice: runeword; + const Infinity: runeword; + const Insight: runeword; + const LastWish: runeword; + const Lawbringer: runeword; + const Oath: runeword; + const Obedience: runeword; + const Phoenix: runeword; + const Pride: runeword; + const Rift: runeword; + const Spirit: runeword; + const VoiceofReason: runeword; + const Wrath: runeword; + const Bone: runeword; + const Enlightenment: runeword; + const Myth: runeword; + const Peace: runeword; + const Principle: runeword; + const Rain: runeword; + const Treachery: runeword; + const Test: runeword; + + function findByName(name: string): runeword | undefined; + function findByRune(rune: number): runeword[]; + function findByType(type: number): runeword[]; + + function addRuneword(name: string, sockets: number, runes: number | number[], itemTypes: number | number[]): runeword | boolean; } - export const Roll: { - All: 0, - Eth: 1, - NonEth: 2 + namespace Runewords { + function init(): void + function validItem(item: any): void + function buildLists(): void + function update(classid: any, gid: any): void + function checkRunewords(): void + function checkItem(unit: any): boolean + function keepItem(unit: any): boolean + function getBase(runeword: any, base: any, ethFlag: any, reroll: any): void + function socketItem(base: any, rune: any): void + function getScroll(): void + function makeRunewords(): void + function rerollRunewords(): void } } From e8f2883408ae3e6b502b43495b19566afa85c7bf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 00:59:52 -0500 Subject: [PATCH 081/758] Missed some references to old core/Data/ --- d2bs/kolbot/libs/config/Amazon.js | 12 ++++++------ d2bs/kolbot/libs/config/Assassin.js | 12 ++++++------ d2bs/kolbot/libs/config/Barbarian.js | 12 ++++++------ d2bs/kolbot/libs/config/Druid.js | 12 ++++++------ d2bs/kolbot/libs/config/Necromancer.js | 12 ++++++------ d2bs/kolbot/libs/config/Paladin.js | 12 ++++++------ d2bs/kolbot/libs/config/Sorceress.js | 12 ++++++------ d2bs/kolbot/libs/config/_BaseConfigFile.js | 16 ++++++++-------- d2bs/kolbot/libs/core/NTItemParser.js | 2 +- 9 files changed, 51 insertions(+), 51 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index f0366da04..f57937374 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -571,7 +571,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -580,13 +580,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz @@ -673,7 +673,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 2d891511c..bf008739b 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -577,7 +577,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -586,13 +586,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz @@ -679,7 +679,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index fe212c753..573dddc11 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -566,7 +566,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -575,13 +575,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz @@ -668,7 +668,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 27c63f38b..da851e523 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -570,7 +570,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -579,13 +579,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz @@ -672,7 +672,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index e75723820..161fec284 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -591,7 +591,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -600,13 +600,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz @@ -693,7 +693,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index f00da4c87..b6c9c2598 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -570,7 +570,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -579,13 +579,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz @@ -672,7 +672,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 0369f9aec..fb076dcbc 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -382,7 +382,7 @@ function LoadConfig() { Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -426,7 +426,7 @@ function LoadConfig() { /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js //Config.PickitFiles.push("kolton.nip"); //Config.PickitFiles.push("LLD.nip"); @@ -572,7 +572,7 @@ function LoadConfig() { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -581,13 +581,13 @@ function LoadConfig() { // ########################### // /* ##### CUBING SETTINGS ##### */ // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz @@ -674,7 +674,7 @@ function LoadConfig() { // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 0fdbbfa91..1dbb53390 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -363,7 +363,7 @@ Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/Data/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; // Potion settings Config.UseHP = 75; // Drink a healing potion if life is under designated percent. @@ -409,7 +409,7 @@ /* ##### PICKIT SETTINGS ##### */ // ########################### // // Default folder is kolbot/pickit. - // Item name and classids located in core/Data/NTItemAlias.js or modules/sdk.js + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js // Config.PickitFiles.push("kolton.nip"); // Config.PickitFiles.push("LLD.nip"); @@ -626,7 +626,7 @@ Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); @@ -636,14 +636,14 @@ /* ##### CUBING SETTINGS ##### */ // ########################### // /* - * All recipe names are available in Templates/Cubing.txt. For item names/classids check core/Data/NTItemAlias.js + * All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). * Etherealness is optional and only applies to some recipes. */ Config.Cubing = false; // Set to true to enable cubing. Config.ShowCubingInfo = true; // Show cubing messages on console - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Gem, "Flawed Amethyst"]); // make Flawed Amethyst // Config.Recipes.push([Recipe.Gem, "Flawed Topaz"]); // make Flawed Topaz @@ -682,7 +682,7 @@ // Config.Recipes.push([Recipe.Rejuv]); // Make Rejuv // Config.Recipes.push([Recipe.FullRejuv]); // Make Full Rejuv - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Rune, "Eld Rune"]); // Upgrade El to Eld // Config.Recipes.push([Recipe.Rune, "Tir Rune"]); // Upgrade Eld to Tir @@ -712,7 +712,7 @@ // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/Data/NTItemAlias.js + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js // Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Helm // Config.Recipes.push([Recipe.Blood.Boots, "Mirrored Boots"]); // Craft Blood Boots @@ -857,7 +857,7 @@ // ############################### // // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 9ef251b0a..94c80d4e4 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -20,7 +20,7 @@ */ includeIfNotIncluded("core/Prototypes.js"); -includeIfNotIncluded("core/Data/NTItemAlias.js"); +includeIfNotIncluded("core/GameData/NTItemAlias.js"); /** * @todo clean up this file From 1e625e3a823b3d303de87546dddcd3a3edfb4592 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 01:01:32 -0500 Subject: [PATCH 082/758] Update Worker.js - Ensure no duplicate background worker processes - Add ability to stop a process by name --- d2bs/kolbot/libs/modules/Worker.js | 33 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index e35be283f..aa392acbd 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -55,24 +55,39 @@ }; /** - * - * @param {function({Worker}):boolean} callback - */ - this.runInBackground = new Proxy({processes: {}}, { + * + * @param {function({Worker}):boolean} callback + */ + this.runInBackground = new Proxy({ processes: {} }, { set: function (target, name, callback) { - target.processes[name] = {callback: callback, running: true}; - + if (target.processes.hasOwnProperty(name)) { + throw new Error("Process " + name + " already exists."); + } + target.processes[name] = { callback: callback, running: true, name: name }; let proxyCallback = function () { - target.processes.running = (callback() && self.pushLowPrio(proxyCallback) > -1); - if (!target.processes.running) { + if (target.processes[name].running) { + target.processes[name].running = (callback() && self.pushLowPrio(proxyCallback) > -1); + } + if (!target.processes[name].running) { delete target.processes[name]; } }; - self.pushLowPrio(proxyCallback); }, + deleteProperty: function (target, name) { + if (!target.processes.hasOwnProperty(name)) { + throw new Error("Process " + name + " does not exists."); + } + target.processes[name].running = false; + delete target.processes[name]; + return true; + } }); + this.stopProcess = function (name) { + delete self.runInBackground.processes[name]; + }; + global.await = function (promise) { while (delay() && !promise.stopped) { // From 0f83afa0442ee66e2291b1ff11b4be1bfc5be9a8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 01:02:07 -0500 Subject: [PATCH 083/758] Update MapMode.js - Another missed reference --- d2bs/kolbot/libs/manualplay/MapMode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/MapMode.js b/d2bs/kolbot/libs/manualplay/MapMode.js index 7686ef4a6..fcc883268 100644 --- a/d2bs/kolbot/libs/manualplay/MapMode.js +++ b/d2bs/kolbot/libs/manualplay/MapMode.js @@ -31,7 +31,7 @@ const MapMode = { // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/Data/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; // Manager Item Log Screen Config.LogKeys = false; // Log keys on item viewer @@ -49,7 +49,7 @@ const MapMode = { Config.GambleGoldStart = 1000000; Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/Data/NTItemAlias.js file for other item classids. + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. Config.GambleItems.push("Amulet"); Config.GambleItems.push("Ring"); Config.GambleItems.push("Circlet"); From 335fec02d9dd85f7961f4ccd9eb820b0dcb8cf87 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:07:42 -0400 Subject: [PATCH 084/758] Update Polyfill.js - Polyfilled Map - Polyfilled Set - Add Math.trunc - Add Math.percentDifference - Organized the file per polyfill type --- d2bs/kolbot/libs/Polyfill.js | 570 +++++++++++++++++++++++++---------- 1 file changed, 418 insertions(+), 152 deletions(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 25430439c..c84dd3fdb 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -5,6 +5,26 @@ * */ +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ String Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - String.prototype.lcsGraph + * - String.prototype.diffCount + * - String.prototype.includes + * - String.prototype.capitalize + * - String.prototype.padEnd + * - String.prototype.padStart + * - String.prototype.repeat + * - String.prototype.trim + * - String.prototype.startsWith + * - String.prototype.endsWith + * - String.prototype.isEqual + * - String.prototype.format + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + + String.prototype.lcsGraph = function (compareToThis) { if (!this.length || !compareToThis || !compareToThis.length) { return null; @@ -69,6 +89,152 @@ String.prototype.capitalize = function (downcase = false) { return this.charAt(0).toUpperCase() + (downcase ? this.slice(1).toLowerCase() : this.slice(1)); }; +String.prototype.padEnd = function padEnd(targetLength, padString) { + targetLength = targetLength >> 0; //floor if number or convert non-number to 0; + padString = String(typeof padString !== "undefined" ? padString : " "); + if (this.length > targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed + } + return String(this) + padString.slice(0, targetLength); + } +}; + +String.prototype.padStart = function padStart(targetLength, padString) { + targetLength = targetLength >> 0; //floor if number or convert non-number to 0; + padString = String(typeof padString !== "undefined" ? padString : " "); + if (this.length > targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed + } + return padString.slice(0, targetLength) + String(this); + } +}; + +String.prototype.repeat = function(count) { + "use strict"; + if (this == null) throw new TypeError("can't convert " + this + " to object"); + let str = "" + this; + count = +count; + // eslint-disable-next-line no-self-compare + if (count !== count) { + count = 0; + } + if (count < 0) throw new RangeError("repeat count must be non-negative"); + if (count === Infinity) throw new RangeError("repeat count must be less than infinity"); + + count = Math.floor(count); + if (str.length === 0 || count === 0) { + return ""; + } + if (str.length * count >= 1 << 28) { + throw new RangeError( + "repeat count must not overflow maximum string size" + ); + } + let rpt = ""; + for (;;) { + if ((count & 1) === 1) { + rpt += str; + } + count >>>= 1; + if (count === 0) { + break; + } + str += str; + } + return rpt; +}; + +// Trim String +if (!String.prototype.trim) { + String.prototype.trim = function () { + return this.replace(/^\s+|\s+$/g, ""); + }; +} + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (prefix) { + return !prefix || this.substring(0, prefix.length) === prefix; + }; +} + +if (!String.prototype.endsWith) { + String.prototype.endsWith = function (search, this_len) { + if (this_len === undefined || this_len > this.length) { + this_len = this.length; + } + return this.substring(this_len - search.length, this_len) === search; + }; +} + +if (!String.isEqual) { + /** + * Check if two strings are equal + * @static + * @param {string} str1 + * @param {string} str2 + * @returns {boolean} + */ + String.isEqual = function (str1, str2) { + return str1.toLowerCase() === str2.toLowerCase(); + }; +} + +/** + * Use since we don't have template literals + * Replaces placeholders in a string with provided values. + * + * @param {Array>} pairs - An array of arrays, + * where the first item in each inner array is a placeholder in the form of "$placeholder", + * and the second item is the value to replace it with. + * @returns {string} The formatted string. + */ +String.prototype.format = function (...pairs) { + if (!pairs.length) return this; + let newString = this; + pairs.forEach(pair => { + let [match, replace] = pair; + if (match === undefined || replace === undefined) return; + newString = newString.replace(match, replace); + }); + return newString; +}; + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Array Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Array.prototype.isEqual + * - Array.prototype.filterHighDistance + * - Array.prototype.findIndex + * - Array.prototype.remove + * - Array.prototype.from + * - Array.prototype.filterNull + * - Array.prototype.compactMap + * - Array.prototype.random + * - Array.prototype.shuffle + * - Array.prototype.includes + * - Array.prototype.at + * - Array.prototype.contains + * - Array.prototype.intersection + * - Array.prototype.difference + * - Array.prototype.symmetricDifference + * - Array.prototype.find + * - Array.prototype.fill + * - Array.prototype.first + * - Array.prototype.last + * - Array.prototype.flat + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + + Array.prototype.isEqual = function (t) { return this.map((x, i) => t.hasOwnProperty(i) && x === t[i]).reduce((a, c) => c & a, true); }; @@ -146,54 +312,6 @@ if (!Array.prototype.remove) { }; } -if (!String.prototype.startsWith) { - String.prototype.startsWith = function (prefix) { - return !prefix || this.substring(0, prefix.length) === prefix; - }; -} - -if (!String.prototype.endsWith) { - String.prototype.endsWith = function (search, this_len) { - if (this_len === undefined || this_len > this.length) { - this_len = this.length; - } - return this.substring(this_len - search.length, this_len) === search; - }; -} - -if (!String.isEqual) { - /** - * Check if two strings are equal - * @static - * @param {string} str1 - * @param {string} str2 - * @returns {boolean} - */ - String.isEqual = function (str1, str2) { - return str1.toLowerCase() === str2.toLowerCase(); - }; -} - -/** - * Use since we don't have template literals - * Replaces placeholders in a string with provided values. - * - * @param {Array>} pairs - An array of arrays, - * where the first item in each inner array is a placeholder in the form of "$placeholder", - * and the second item is the value to replace it with. - * @returns {string} The formatted string. - */ -String.prototype.format = function (...pairs) { - if (!pairs.length) return this; - let newString = this; - pairs.forEach(pair => { - let [match, replace] = pair; - if (match === undefined || replace === undefined) return; - newString = newString.replace(match, replace); - }); - return newString; -}; - // Production steps of ECMA-262, Edition 6, 22.1.2.1 if (!Array.from) { Array.from = (function () { @@ -342,13 +460,6 @@ if (!Array.prototype.symmetricDifference) { }; } -// Returns a random integer between start and end included. -Math.randomIntBetween = function (start, end) { - let min = Math.ceil(start); - let max = Math.floor(end); - return Math.floor(Math.random() * (max - min + 1)) + min; -}; - // Shuffle Array // http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript if (!Array.prototype.shuffle) { @@ -374,42 +485,6 @@ if (!Array.prototype.shuffle) { }; } -// Trim String -if (!String.prototype.trim) { - String.prototype.trim = function () { - return this.replace(/^\s+|\s+$/g, ""); - }; -} - -// Object.assign polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign -if (typeof Object.assign !== "function") { - Object.defineProperty(Object, "assign", { - value: function assign(target) { - if (target === null) { - throw new TypeError("Cannot convert undefined or null to object"); - } - - let to = Object(target); - - for (let index = 1; index < arguments.length; index++) { - let nextSource = arguments[index]; - - if (nextSource !== null) { - for (let nextKey in nextSource) { - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - - return to; - }, - writable: true, - configurable: true - }); -} - // Array.find polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find if (!Array.prototype.find) { Object.defineProperty(Array.prototype, "find", { @@ -500,10 +575,60 @@ if (!Array.prototype.flat) { writable: true }); } -// eslint-disable-next-line block-scoped-var -// if (typeof global === "undefined") { -// var global = this; -// } + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Object Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Object.assign + * - Object.entries + * - Object.values + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + +/** + * @description Copy the values of all enumerable own properties from one or more source objects to a target object. Returns the target object. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + */ +if (typeof Object.assign !== "function") { + Object.defineProperty(Object, "assign", { + value: function assign(target) { + if (target === null) { + throw new TypeError("Cannot convert undefined or null to object"); + } + + let to = Object(target); + + for (let index = 1; index < arguments.length; index++) { + let nextSource = arguments[index]; + + if (nextSource !== null) { + for (let nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + + return to; + }, + writable: true, + configurable: true + }); +} + +if (!Object.values) { + 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]]; }); + }; +} // eslint-disable-next-line no-var if (typeof global === "undefined") var global = [].filter.constructor("return this")(); @@ -524,81 +649,207 @@ if (!global.hasOwnProperty("require")) { }); } -String.prototype.padEnd = function padEnd(targetLength, padString) { - targetLength = targetLength >> 0; //floor if number or convert non-number to 0; - padString = String(typeof padString !== "undefined" ? padString : " "); - if (this.length > targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed - } - return String(this) + padString.slice(0, targetLength); - } +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Math Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Math.randomIntBetween + * - Math.trunc + * - Math.percentDifference + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + +// Returns a random integer between start and end included. +Math.randomIntBetween = function (start, end) { + let min = Math.ceil(start); + let max = Math.floor(end); + return Math.floor(Math.random() * (max - min + 1)) + min; }; -String.prototype.padStart = function padStart(targetLength, padString) { - targetLength = targetLength >> 0; //floor if number or convert non-number to 0; - padString = String(typeof padString !== "undefined" ? padString : " "); - if (this.length > targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed +if (!Math.trunc) { + /** + * Polyfill for Math.trunc + * Static method returns the integer part of a number by removing any fractional digits. + * @static + * @param {number} number + * @returns {number} + */ + Math.trunc = function (number) { + return number < 0 ? Math.ceil(number) : Math.floor(number); + }; +} + +Math.percentDifference = function (value1, value2) { + const diff = Math.abs(value1 - value2); + const average = (value1 + value2) / 2; + const percentDiff = (diff / average) * 100; + return Math.trunc(percentDiff); +}; + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Map Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Map.prototype.forEach + * - Map.prototype.toString + * - Map.prototype.keys + * - Map.prototype.values + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + +if (typeof Map.prototype.forEach !== "function") { + Map.prototype.forEach = function(callbackFn, thisArg) { + thisArg = thisArg || this; + for (let [key, value] of this.entries()) { + callbackFn.call(thisArg, value, key, this); } - return padString.slice(0, targetLength) + String(this); + }; +} + +Map.prototype.toString = function() { + let obj = {}; + for (let [key, value] of this.entries()) { + obj[key] = value; } + return JSON.stringify(obj); }; -String.prototype.repeat = function(count) { - "use strict"; - if (this == null) throw new TypeError("can't convert " + this + " to object"); - let str = "" + this; - count = +count; - // eslint-disable-next-line no-self-compare - if (count !== count) { - count = 0; +Map.prototype.keys = function() { + let keys = []; + // eslint-disable-next-line no-unused-vars + for (let [key, _value] of this.entries()) { + keys.push(key); } - if (count < 0) throw new RangeError("repeat count must be non-negative"); - if (count === Infinity) throw new RangeError("repeat count must be less than infinity"); + return keys; +}; - count = Math.floor(count); - if (str.length === 0 || count === 0) { - return ""; - } - if (str.length * count >= 1 << 28) { - throw new RangeError( - "repeat count must not overflow maximum string size" - ); +Map.prototype.values = function() { + let values = []; + // eslint-disable-next-line no-unused-vars + for (let [_key, value] of this.entries()) { + values.push(value); } - let rpt = ""; - for (;;) { - if ((count & 1) === 1) { - rpt += str; + return values; +}; + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Set.prototype.forEach + * - Set.prototype.keys + * - Set.prototype.values + * - Set.prototype.entries + * - Set.prototype.isSuperset + * - Set.prototype.union + * - Set.prototype.intersection + * - Set.prototype.difference + * - Set.prototype.symmetricDifference + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + +if (typeof Set.prototype.forEach !== "function") { + Set.prototype.forEach = function(callbackFn, thisArg) { + thisArg = thisArg || this; + for (let item of this) { + callbackFn.call(thisArg, item, item, this); } - count >>>= 1; - if (count === 0) { - break; + }; +} + +if (typeof Set.prototype.keys !== "function") { + Set.prototype.keys = function() { + let keys = []; + for (let item of this) { + keys.push(item); } - str += str; - } - return rpt; -}; + return keys; + }; +} -if (!Object.values) { - Object.values = function (source) { - return Object.keys(source).map(function (k) { return source[k]; }); +if (typeof Set.prototype.values !== "function") { + Set.prototype.values = function() { + let values = []; + for (let item of this) { + values.push(item); + } + return values; }; } -if (!Object.entries) { - Object.entries = function (source) { - return Object.keys(source).map(function (k) { return [k, source[k]]; }); +if (typeof Set.prototype.entries !== "function") { + Set.prototype.entries = function() { + let entries = []; + for (let item of this) { + entries.push([item, item]); + } + return entries; }; } +Set.prototype.isSuperset = function(subset) { + for (let item of subset) { + if (!this.has(item)) { + return false; + } + } + return true; +}; + +Set.prototype.union = function(setB) { + let union = new Set(this); + for (let item of setB) { + union.add(item); + } + return union; +}; + +Set.prototype.intersection = function(setB) { + let intersection = new Set(); + for (let item of setB) { + if (this.has(item)) { + intersection.add(item); + } + } + return intersection; +}; + +Set.prototype.symmetricDifference = function(setB) { + let difference = new Set(this); + for (let item of setB) { + if (difference.has(item)) { + difference.delete(item); + } else { + difference.add(item); + } + } + return difference; +}; + +Set.prototype.difference = function(setB) { + let difference = new Set(this); + for (let item of setB) { + difference.delete(item); + } + return difference; +}; + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~ console Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - console.log + * - console.debug + * - console.warn + * - console.error + * - console.info + * - console.trace + * - console.time + * - console.timeEnd + * - console.table (partial) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + (function (global, print) { global.console = global.console || (function () { const console = {}; @@ -783,6 +1034,21 @@ if (!Object.entries) { })(); })([].filter.constructor("return this")(), print); + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~ global d2bs Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - sdk - sdk object @see libs/modules/sdk.js + * - includeIfNotIncluded - include file if not already included + * - includeCoreLibs - include all core libs + * - includeSystemLibs - include all system driver files + * - clone - clone object + * - copyObj + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + + if (!global.hasOwnProperty("sdk") && typeof require !== "undefined") { Object.defineProperty(global, "sdk", { value: require("../modules/sdk"), From 015bb1e21bb63a702ae093055e764ecddde1e332 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 Mar 2023 19:22:10 -0400 Subject: [PATCH 085/758] Update Prototypes.js - Fix `Unit.prototype.use` for case when we have an item on our cursor, --- d2bs/kolbot/libs/core/Prototypes.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 352b2caa4..7bf46e22b 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -866,6 +866,11 @@ Unit.prototype.use = function () { } } + // make sure we don't have anything on cursor + if (me.itemoncursor) { + if (!Game.getCursorUnit().drop()) return false; + } + switch (this.location) { case sdk.storage.Stash: case sdk.storage.Inventory: From 3f0bac5399106637845e287bec208e95dc1d6e09 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 Mar 2023 19:59:00 -0400 Subject: [PATCH 086/758] Update sdk for presetIds - Fixed incorrect monster presets - Added sdk.object.chestIds - Added sdk.shrines.Ids, and Presets --- d2bs/kolbot/libs/modules/sdk.js | 127 ++++++++++++++++++-------------- d2bs/kolbot/sdk/globals.d.ts | 6 ++ d2bs/kolbot/sdk/types/sdk.d.ts | 107 ++++++++++++++------------- 3 files changed, 132 insertions(+), 108 deletions(-) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 7737b3f39..673c6e8fb 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -852,7 +852,17 @@ // shrine types shrines: { - Ids: [2, 81, 83, 170, 344, 197, 202], + Presets: [2, 81, 83, 170, 344, 197, 202], + Ids: [ + 2, 77, 81, 83, 84, 85, 93, 96, 97, 109, 116, 123, 124, + 133, 134, 135, 136, 150, 151, 164, 165, 166, 167, 168, + 170, 172, 173, 184, 190, 191, 197, 199, 200, 201, 202, + 206, 226, 231, 232, 236, 249, 260, 262, 263, 264, 265, + 275, 276, 277, 278, 279, 280, 281, 282, 299, 300, 302, + 303, 320, 325, 343, 344, 361, 414, 415, 421, 422, 423, + 427, 428, 464, 465, 472, 479, 483, 484, 488, 491, 492, + 495, 497, 499, 503, 509, 512, 520, 521, 522 + ], None: 0, Refilling: 1, Health: 2, @@ -1571,6 +1581,12 @@ Active: 2, }, + chestIds: [ + 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 + ], + // act1 A1TownFire: 39, A1Waypoint: 119, @@ -1739,74 +1755,75 @@ monsters: { preset: { // Confirmed - TheSummoner: 250, + Izual: 256, Bishibosh: 734, + Bonebreak: 735, Coldcrow: 736, Rakanishu: 737, TreeheadWoodFist: 738, + Griswold: 739, TheCountess: 740, + PitspawnFouldog: 741, + FlamespiketheCrawler: 742, BoneAsh: 743, - Beetleburst: 747, - TheSmith: 754, Radament: 744, + BloodwitchtheWild: 745, + Fangskin: 746, + Beetleburst: 747, CreepingFeature: 748, + ColdwormtheBurrower: 749, FireEye: 750, DarkElder: 751, - Corpsefire: 774, + TheSummoner: 752, + AncientKaatheSoulless: 753, + TheSmith: 754, + SszarktheBurning: 755, + WitchDoctorEndugu: 756, + Stormtree: 757, + BattlemaidSarina: 758, + IcehawkRiftwing: 759, + IsmailVilehand: 760, + GelebFlamefinger: 761, + BremmSparkfist: 762, + ToorcIcefist: 763, + WyandVoidfinger: 764, + MafferDragonhand: 765, + WingedDeath: 766, + Taintbreeder: 768, + RiftwraiththeCannibal: 769, + InfectorofSouls: 770, + LordDeSeis: 771, + GrandVizierofChaos: 772, TheCowKing: 773, - BloodRaven: 805, - Izual: 406, + Corpsefire: 774, + Hephasto: 775, + ShenktheOverseer: 776, + TalictheDefender: 777, + MadawctheGuardian: 778, + KorlictheProtector: 779, + AxeDweller: 780, + BonesawBreaker: 781, DacFarren: 782, + EldritchtheRectifier: 783, EyebacktheUnleashed: 784, + ThreshSocket: 785, + Pindleskin: 786, + SnapchipShatter: 787, + AnodizedElite: 788, + VinvearMolech: 789, SharpToothSayer: 790, - // Unconfirmed - Bonebreak: 705, - Griswold: 709, - PitspawnFouldog: 711, - FlamespiketheCrawler: 712, - BloodwitchtheWild: 715, - Fangskin: 716, - Leatherarm: 718, - ColdwormtheBurrower: 719, - AncientKaatheSoulless: 723, - WebMagetheBurning: 725, - WitchDoctorEndugu: 726, - Stormtree: 727, - SarinatheBattlemaid: 728, - IcehawkRiftwing: 729, - IsmailVilehand: 730, - GelebFlamefinger: 731, - BremmSparkfist: 732, - ToorcIcefist: 733, - WyandVoidfinger: 734, - MafferDragonhand: 735, - WingedDeath: 736, - ListertheTormentor: 737, - Taintbreeder: 738, - RiftwraiththeCannibal: 739, - InfectorofSouls: 740, - LordDeSeis: 741, - GrandVizierofChaos: 742, - Hephasto: 745, - SiegeBoss: 746, - AxeDweller: 750, - BonesawBreaker: 751, - EldritchtheRectifier: 753, - ThreshSocket: 755, - Pindleskin: 756, - SnapchipShatter: 757, - AnodizedElite: 758, - VinvearMolech: 759, - MagmaTorquer: 761, - BlazeRipper: 762, - Frozenstein: 763, - Nihlathak: 764, - Wave1Spawn: 765, - Wave2Spawn: 766, - Wave3Spawn: 767, - Wave4Spawn: 768, - Wave5Spawn: 769, + MagmaTorquer: 791, + BlazeRipper: 792, + Frozenstein: 793, + Nihlathak: 794, + ColenzotheAnnihilator: 795, + AchmeltheCursed: 796, + BartuctheBloody: 797, + VentartheUnholy: 798, + ListertheTormentor: 799, + BloodRaven: 805, + // Unconfirmed // Questionable GriefGrumble: 741, // JailLvl2 UniqueJailLvl3: 273, @@ -2449,7 +2466,7 @@ TalictheDefender: 541, MadawctheGuardian: 542, ListerTheTormenter: 571, - TheCowKing: 743, + TheCowKing: 743, // 773? ColdwormtheBurrower: 284, Nihlathak: 526, diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index d2dae9b15..e7107f76b 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -18,6 +18,11 @@ /// declare global { + interface Error { + fileName: string; + lineNumber: number; + } + interface Array { includes(searchElement: T): boolean; find(predicate: (value: T, index: number, obj: Int8Array) => boolean, thisArg?: any): T | undefined; @@ -656,6 +661,7 @@ declare global { y: number; roomx: number; roomy: number; + level: number; getNext(): PresetUnit | false; realCoords(): { area: number, x: number, y: number }; diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index d0dc6e800..336d75ef0 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -1663,74 +1663,75 @@ declare global { export namespace monsters { namespace preset { // Confirmed - const TheSummoner: 250; + const Izual: 256; const Bishibosh: 734; + const Bonebreak: 735; const Coldcrow: 736; const Rakanishu: 737; const TreeheadWoodFist: 738; + const Griswold: 739; const TheCountess: 740; + const PitspawnFouldog: 741; + const FlamespiketheCrawler: 742; const BoneAsh: 743; - const Beetleburst: 747; - const TheSmith: 754; const Radament: 744; + const BloodwitchtheWild: 745; + const Fangskin: 746; + const Beetleburst: 747; const CreepingFeature: 748; + const ColdwormtheBurrower: 749; const FireEye: 750; const DarkElder: 751; - const Corpsefire: 774; + const TheSummoner: 752; + const AncientKaatheSoulless: 753; + const TheSmith: 754; + const SszarktheBurning: 755; + const WitchDoctorEndugu: 756; + const Stormtree: 757; + const BattlemaidSarina: 758; + const IcehawkRiftwing: 759; + const IsmailVilehand: 760; + const GelebFlamefinger: 761; + const BremmSparkfist: 762; + const ToorcIcefist: 763; + const WyandVoidfinger: 764; + const MafferDragonhand: 765; + const WingedDeath: 766; + const Taintbreeder: 768; + const RiftwraiththeCannibal: 769; + const InfectorofSouls: 770; + const LordDeSeis: 771; + const GrandVizierofChaos: 772; const TheCowKing: 773; - const BloodRaven: 805; - const Izual: 406; + const Corpsefire: 774; + const Hephasto: 775; + const ShenktheOverseer: 776; + const TalictheDefender: 777; + const MadawctheGuardian: 778; + const KorlictheProtector: 779; + const AxeDweller: 780; + const BonesawBreaker: 781; const DacFarren: 782; + const EldritchtheRectifier: 783; const EyebacktheUnleashed: 784; + const ThreshSocket: 785; + const Pindleskin: 786; + const SnapchipShatter: 787; + const AnodizedElite: 788; + const VinvearMolech: 789; const SharpToothSayer: 790; - // Unconfirmed - const Bonebreak: 705; - const Griswold: 709; - const PitspawnFouldog: 711; - const FlamespiketheCrawler: 712; - const BloodwitchtheWild: 715; - const Fangskin: 716; - const Leatherarm: 718; - const ColdwormtheBurrower: 719; - const AncientKaatheSoulless: 723; - const WebMagetheBurning: 725; - const WitchDoctorEndugu: 726; - const Stormtree: 727; - const SarinatheBattlemaid: 728; - const IcehawkRiftwing: 729; - const IsmailVilehand: 730; - const GelebFlamefinger: 731; - const BremmSparkfist: 732; - const ToorcIcefist: 733; - const WyandVoidfinger: 734; - const MafferDragonhand: 735; - const WingedDeath: 736; - const ListertheTormentor: 737; - const Taintbreeder: 738; - const RiftwraiththeCannibal: 739; - const InfectorofSouls: 740; - const LordDeSeis: 741; - const GrandVizierofChaos: 742; - const Hephasto: 745; - const SiegeBoss: 746; - const AxeDweller: 750; - const BonesawBreaker: 751; - const EldritchtheRectifier: 753; - const ThreshSocket: 755; - const Pindleskin: 756; - const SnapchipShatter: 757; - const AnodizedElite: 758; - const VinvearMolech: 759; - const MagmaTorquer: 761; - const BlazeRipper: 762; - const Frozenstein: 763; - const Nihlathak: 764; - const Wave1Spawn: 765; - const Wave2Spawn: 766; - const Wave3Spawn: 767; - const Wave4Spawn: 768; - const Wave5Spawn: 769; + const MagmaTorquer: 791; + const BlazeRipper: 792; + const Frozenstein: 793; + const Nihlathak: 794; + const ColenzotheAnnihilator: 795; + const AchmeltheCursed: 796; + const BartuctheBloody: 797; + const VentartheUnholy: 798; + const ListertheTormentor: 799; + const BloodRaven: 805; + // Unconfirmed // Questionable const GriefGrumble: 741; // JailLvl2 const UniqueJailLvl3: 273; From 7f6901322cdf68bcf728ae75d613feddde0856ac Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Mar 2023 02:40:24 -0400 Subject: [PATCH 087/758] Create ShrineData.js - ShrineData module to handle shrine types, states, durations, and regen times --- d2bs/kolbot/libs/core/GameData/ShrineData.js | 63 ++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 d2bs/kolbot/libs/core/GameData/ShrineData.js diff --git a/d2bs/kolbot/libs/core/GameData/ShrineData.js b/d2bs/kolbot/libs/core/GameData/ShrineData.js new file mode 100644 index 000000000..39e38ddd2 --- /dev/null +++ b/d2bs/kolbot/libs/core/GameData/ShrineData.js @@ -0,0 +1,63 @@ +/** +* @filename ShrineData.js +* @author theBGuy +* @desc shrine data library, handles shrine types, states, durations, and regen times +* +*/ + +(function (module) { + const ShrineData = (function () { + function Shrine (state, duration, regen) { + this.state = state || 0; + this.duration = duration || 0; + this.regenTime = Time.minutes(regen) || Infinity; + } + const shrineMap = new Map(); + shrineMap.set(sdk.shrines.Refilling, new Shrine(0, 0, 2)); + shrineMap.set(sdk.shrines.Health, new Shrine(0, 0, 5)); + shrineMap.set(sdk.shrines.Mana, new Shrine(0, 0, 5)); + shrineMap.set(sdk.shrines.HealthExchange, new Shrine()); + shrineMap.set(sdk.shrines.ManaExchange, new Shrine()); + shrineMap.set(sdk.shrines.Armor, new Shrine(sdk.states.ShrineArmor, 2400, 5)); + shrineMap.set(sdk.shrines.Combat, new Shrine(sdk.states.ShrineCombat, 2400, 5)); + shrineMap.set(sdk.shrines.ResistFire, new Shrine(sdk.states.ShrineResFire, 3600, 5)); + shrineMap.set(sdk.shrines.ResistCold, new Shrine(sdk.states.ShrineResCold, 3600, 5)); + shrineMap.set(sdk.shrines.ResistLightning, new Shrine(sdk.states.ShrineResLighting, 3600, 5)); + shrineMap.set(sdk.shrines.ResistPoison, new Shrine(sdk.states.ShrineResPoison, 3600, 5)); + shrineMap.set(sdk.shrines.Skill, new Shrine(sdk.states.ShrineSkill, 2400, 5)); + shrineMap.set(sdk.shrines.ManaRecharge, new Shrine(sdk.states.ShrineManaRegen, 2400, 5)); + shrineMap.set(sdk.shrines.Stamina, new Shrine(sdk.states.ShrineStamina, 4800, 5)); + shrineMap.set(sdk.shrines.Experience, new Shrine(sdk.states.ShrineResCold, 3600)); + shrineMap.set(sdk.shrines.Enirhs, new Shrine()); + shrineMap.set(sdk.shrines.Portal, new Shrine()); + shrineMap.set(sdk.shrines.Gem, new Shrine()); + shrineMap.set(sdk.shrines.Fire, new Shrine()); + shrineMap.set(sdk.shrines.Monster, new Shrine()); + shrineMap.set(sdk.shrines.Exploding, new Shrine()); + shrineMap.set(sdk.shrines.Poison, new Shrine()); + + return { + get: function (shrineType) { + return shrineMap.get(shrineType); + }, + + has: function (shrineType) { + return shrineMap.has(shrineType); + }, + + getState: function (shrineType) { + return shrineMap.get(shrineType).state || 0; + }, + + getDuration: function (shrineType) { + return shrineMap.get(shrineType).duration || 0; + }, + + getRegenTime: function (shrineType) { + return shrineMap.get(shrineType).regenTime || Infinity; + }, + }; + })(); + + module.exports = ShrineData; +})(module); From 0d11b5b3710b89e8c12c536d9e41e0f3031eead6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Mar 2023 17:25:55 -0400 Subject: [PATCH 088/758] Create QuestData.js - Make handling quest data easier --- d2bs/kolbot/libs/core/GameData/QuestData.js | 156 ++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 d2bs/kolbot/libs/core/GameData/QuestData.js diff --git a/d2bs/kolbot/libs/core/GameData/QuestData.js b/d2bs/kolbot/libs/core/GameData/QuestData.js new file mode 100644 index 000000000..3c02bcf2d --- /dev/null +++ b/d2bs/kolbot/libs/core/GameData/QuestData.js @@ -0,0 +1,156 @@ +/** +* @filename QuestData.js +* @author theBGuy +* @desc quest data library, make checking quests easier +* +*/ + +(function (module) { + /** + * @todo Fill out more, items for quests, npcs, etc + */ + const QuestData = (function () { + let _lastRefresh = 0; + + /** @type {Set} */ + const _specials = new Set(); + [ + sdk.quest.id.SpokeToWarriv, sdk.quest.id.AbleToGotoActII, sdk.quest.id.SpokeToJerhyn, sdk.quest.id.AbleToGotoActIII, + sdk.quest.id.SpokeToHratli, sdk.quest.id.AbleToGotoActIV, sdk.quest.id.SpokeToTyrael, sdk.quest.id.AbleToGotoActV, + ].forEach(questId => _specials.add(questId)); + + const refresh = function () { + if (getTickCount() - _lastRefresh > 500) { + Packet.questRefresh(); + _lastRefresh = getTickCount(); + } + }; + + /** + * @constructor + * @param {number} questId + * @param {number} act + */ + function Quest (questId, act) { + this.id = questId; + this.act = act; + this.states = new Array(16).fill(0); // todo figure a method to ensure the length is immutable + this.completed = false; + this.reqComplete = false; + this.cannotComplete = false; + } + + Quest.prototype.complete = function (reqCheck = false) { + if (this.completed) return true; + if (this.cannotComplete) return false; + if (reqCheck && this.reqComplete) return true; + refresh(); + + let completedStatus = me.getQuest(this.id, sdk.quest.states.Completed); + if (completedStatus) { + this.completed = true; + return true; + } + + let cannotCompleteStatus = me.getQuest(this.id, sdk.quest.states.CannotComplete); + if (cannotCompleteStatus) { + this.cannotComplete = true; + return false; + } + + if (reqCheck) { + let reqCompleteStatus = me.getQuest(this.id, sdk.quest.states.ReqComplete); + if (reqCompleteStatus) { + this.reqComplete = true; + return true; + } + } + + return false; + }; + + /** + * @param {number} state - quest state (0 - 15) + * @param {boolean} complete - if true, will check if state bit is 1 (active) otherwise 0 (inactive) + * @returns {boolean} + */ + Quest.prototype.checkState = function (state, complete = true) { + // handle the ones we already know + if (state === sdk.quest.states.Completed && this.completed) return sdk.quest.states.Completed; + if (state === sdk.quest.states.CannotComplete && this.cannotComplete) return sdk.quest.states.CannotComplete; + if (state === sdk.quest.states.ReqComplete && this.reqComplete) return sdk.quest.states.ReqComplete; + + refresh(); + let val = me.getQuest(this.id, state); + this.states[state] = val; + return complete ? val === 1 : val === 0; + }; + + Quest.prototype.getStates = function () { + refresh(); + + // the non-visible quests can stop after 1 + let max = _specials.has(this.id) ? 1 : 16; + + for (let state = 0; state < max; state++) { + this.states[state] = me.getQuest(this.id, state); + delay(10); + } + + this.completed = this.states[sdk.quest.states.Completed] === 1; + this.cannotComplete = this.states[sdk.quest.states.CannotComplete] === 1; + this.reqComplete = this.states[sdk.quest.states.ReqComplete] === 1; + + return this.states; + }; + + const questMap = new Map(); + [ + [ + sdk.quest.id.SpokeToWarriv, sdk.quest.id.DenofEvil, sdk.quest.id.SistersBurialGrounds, sdk.quest.id.ToolsoftheTrade, + sdk.quest.id.TheSearchForCain, sdk.quest.id.ForgottenTower, sdk.quest.id.SistersToTheSlaughter, sdk.quest.id.Respec + ], + [ + sdk.quest.id.AbleToGotoActII, sdk.quest.id.SpokeToJerhyn, sdk.quest.id.RadamentsLair, sdk.quest.id.TheHoradricStaff, + sdk.quest.id.TheTaintedSun, sdk.quest.id.TheSummoner, sdk.quest.id.TheArcaneSanctuary, sdk.quest.id.TheSevenTombs + ], + [ + sdk.quest.id.AbleToGotoActIII, sdk.quest.id.SpokeToHratli, sdk.quest.id.TheGoldenBird, sdk.quest.id.BladeoftheOldReligion, + sdk.quest.id.KhalimsWill, sdk.quest.id.TheBlackenedTemple, sdk.quest.id.TheGuardian, + ], + [ + sdk.quest.id.AbleToGotoActIV, sdk.quest.id.TheFallenAngel, sdk.quest.id.SpokeToTyrael, sdk.quest.id.HellsForge, sdk.quest.id.TerrorsEnd, + ], + [ + sdk.quest.id.AbleToGotoActV, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.RescueonMountArreat, sdk.quest.id.PrisonofIce, + sdk.quest.id.BetrayalofHarrogath, sdk.quest.id.RiteofPassage, sdk.quest.id.EyeofDestruction, + ] + ].forEach((questIds, act) => { + for (let questId of questIds) { + questMap.set(questId, new Quest(questId, act + 1)); + } + }); + + return { + get: function (questId) { + return questMap.get(questId); + }, + + has: function (questId) { + return questMap.has(questId); + }, + + init: function () { + console.time("QuestData.init"); + questMap.forEach(quest => quest.getStates()); + console.timeEnd("QuestData.init"); + }, + + getActForQuest: function (questId) { + return questMap.get(questId).act; + }, + }; + })(); + + module.exports = QuestData; +})(module); From 5c7f2d77e2e4bef0f7952c6302c2cf1e93266eca Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Mar 2023 17:27:19 -0400 Subject: [PATCH 089/758] Fix sdk typo - `BetrayalofHaggorath` -> `BetrayalofHarrogath` --- d2bs/kolbot/libs/modules/sdk.js | 2 +- d2bs/kolbot/sdk/types/sdk.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 673c6e8fb..59dd4cf50 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -784,7 +784,7 @@ SiegeOnHarrogath: 35, RescueonMountArreat: 36, PrisonofIce: 37, - BetrayalofHaggorath: 38, + BetrayalofHarrogath: 38, RiteofPassage: 39, EyeofDestruction: 40, Respec: 41, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 336d75ef0..370ffdc06 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -711,7 +711,7 @@ declare global { const SiegeOnHarrogath: 35; const RescueonMountArreat: 36; const PrisonofIce: 37; - const BetrayalofHaggorath: 38; + const BetrayalofHarrogath: 38; const RiteofPassage: 39; const EyeofDestruction: 40; const Respec: 41; From 35c5e7684053b076edc2455081741943abd11825 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Mar 2023 00:09:19 -0400 Subject: [PATCH 090/758] Update AutoMule.js - Clean up getMuleItems, to be sure what items are selected/excluded --- d2bs/kolbot/libs/systems/automule/AutoMule.js | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index 6bb657104..ca384607e 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -8,15 +8,10 @@ */ const AutoMule = { - // load configuration files - /** - * @type {Object.} - */ + /** @type {Object.} */ Mules: Object.assign({}, require("./MuleConfig", null, false)), - /** - * @type {Object.} - */ + /** @type {Object.} */ TorchAnniMules: Object.assign({}, require("./TorchAnniMules", null, false)), inGame: false, @@ -509,11 +504,8 @@ const AutoMule = { */ getMuleItems: function () { let info = this.getInfo(); - if (!info || !info.hasOwnProperty("muleInfo")) return false; - let items = []; - let item = me.getItem(-1, sdk.items.mode.inStorage); const muleOrphans = !!(info.muleInfo.hasOwnProperty("muleOrphans") && info.muleInfo.muleOrphans); /** @@ -528,24 +520,29 @@ const AutoMule = { */ const isWanted = (item) => (AutoMule.cubingIngredient(item) || AutoMule.runewordIngredient(item) || AutoMule.utilityIngredient(item)); - if (item) { - do { - if (Town.ignoredItemTypes.indexOf(item.itemType) === -1 - && (![Pickit.Result.UNID, Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(Pickit.checkItem(item).result) || (item.isInStash && muleOrphans)) - && item.classid !== sdk.quest.item.Cube // Don't drop Horadric Cube - && (!item.isAnni) // Don't drop Annihilus - && (!item.isTorch) // Don't drop Hellfire Torch - && (item.isInStash || (item.isInInventory && !Storage.Inventory.IsLocked(item, Config.Inventory))) // Don't drop items in locked slots - && ((!TorchSystem.getFarmers() && !TorchSystem.isFarmer()) || isAKey(item))) { // Don't drop Keys if part of TorchSystem - // Always drop items on Force or Trigger list - if (this.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger)) - // Don't drop Excluded items or Runeword/Cubing/CraftingSystem ingredients - || (!this.matchItem(item, Config.AutoMule.Exclude) && !isWanted(item))) { - items.push(copyUnit(item)); - } - } - } while (item.getNext()); - } + 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 cube/torch/annihilus + if (item.isAnni || item.isTorch || item.classid === sdk.quest.item.Cube) return false; + // don't mule items in locked spots + 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 + if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return false; + // don't mule keys if part of torchsystem + 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 + let pResult = Pickit.checkItem(item).result; + // if it's a junk item, we don't want it + if ([Pickit.Result.UNID, Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pResult)) return (item.isInStash && muleOrphans); + // we've made it this far, we want it + return true; + }); return items; }, From a834ac61c75c85defc4beb2b59473c4d9ababd2d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Mar 2023 00:50:14 -0400 Subject: [PATCH 091/758] type declaration updates - added more to MeType - general stuff for the others still lots to do --- d2bs/kolbot/sdk/globals.d.ts | 72 +++++++++++++++++++++++++++-- d2bs/kolbot/sdk/types/Config.d.ts | 2 +- d2bs/kolbot/sdk/types/Misc.d.ts | 75 ++++++++++++++++--------------- d2bs/kolbot/sdk/types/Pickit.d.ts | 38 ++++++++-------- d2bs/kolbot/sdk/types/Util.d.ts | 7 +++ d2bs/kolbot/sdk/types/sdk.d.ts | 17 ++++++- 6 files changed, 151 insertions(+), 60 deletions(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index e7107f76b..0505d04dc 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -524,12 +524,68 @@ declare global { readonly gold: number; readonly inTown: boolean; readonly highestAct: 1 | 2 | 3 | 4 | 5; + readonly staminaPercent: number; readonly staminaDrainPerSec: number; readonly staminaTimeLeft: number; readonly staminaMaxDuration: number; readonly inShop: boolean; readonly skillDelay: boolean; - + readonly highestAct: 1 | 2 | 3 | 4 | 5; + readonly highestQuestDone: number; + readonly den: boolean; + readonly bloodraven: boolean; + readonly smith: boolean; + readonly imbue: boolean; + readonly cain: boolean; + readonly tristram: boolean; + readonly countess: boolean; + readonly andariel: boolean; + readonly radament: boolean; + readonly horadricstaff: boolean; + readonly summoner: boolean; + readonly duriel: boolean; + readonly goldenbird: boolean; + readonly lamessen: boolean; + readonly gidbinn: boolean; + readonly travincal: boolean; + readonly mephisto: boolean; + readonly izual: boolean; + readonly hellforge: boolean; + readonly diablo: boolean; + readonly shenk: boolean; + readonly larzuk: boolean; + readonly savebarby: boolean; + readonly barbrescue: boolean; + readonly anya: boolean; + readonly ancients: boolean; + readonly baal: boolean; + readonly cows: boolean; + readonly respec: boolean; + readonly diffCompleted: boolean; + wirtsleg: ItemUnit; + cube: ItemUnit; + shaft: ItemUnit; + amulet: ItemUnit; + staff: ItemUnit; + completestaff: ItemUnit; + eye: ItemUnit; + brain: ItemUnit; + heart: ItemUnit; + khalimswill: ItemUnit; + khalimsflail: ItemUnit; + malahspotion: ItemUnit; + scrollofresistance: ItemUnit; + readonly walking: boolean; + readonly running: boolean; + readonly deadOrInSequence: boolean; + readonly moving: boolean; + readonly FCR: number; + readonly FHR: number; + readonly FBR: number; + readonly IAS: number; + readonly shapeshifted: boolean; + + haveWaypoint(area: number): boolean; overhead(msg: string): void; repair(): boolean; revive(): void; @@ -571,6 +627,7 @@ declare global { getUnids(): ItemUnit[]; fieldID(): boolean; switchToPrimary(): boolean; + haveWaypoint(area: number): boolean; } const me: MeType @@ -667,6 +724,14 @@ declare global { realCoords(): { area: number, x: number, y: number }; } + type PresetObject = { + area: number, + id: number, + type: number, + x: number, + y: number, + } + function getPresetUnit(area?: number, objType?: number, classid?: number): PresetUnit | false function getPresetUnit(area?: number, objType?: 2, classid?: number): PresetUnit | false function getPresetUnits(area?: number, objType?: number, classid?: number): PresetUnit[] | false @@ -826,11 +891,12 @@ declare global { function sqlite_version() function sqlite_memusage() - function dopen(what?: string): { + type directory = { getFiles(): string[]; getFolders(): string[]; create(what?: string): boolean; - } | false; + }; + function dopen(what?: string): directory | false; function debugLog(text: string): void function showConsole(): void function hideConsole(): void diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 50fb43ae1..2bb85b1c5 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -134,7 +134,7 @@ declare global { const RepairPercent: number; const Recipes: any[]; const MakeRunewords: boolean; - const Runewords: any[]; + const Runewords: any[][]; const KeepRunewords: any[]; const Gamble: boolean; const GambleItems: any[]; diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index 6a790215a..f58d21f27 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -1,38 +1,41 @@ -declare namespace Misc { - const screenshotErrors: any; - const errorConsolePrint: any; - const useItemLog: boolean; - - function click(button: number, shift: number, unit: Unit): void; - function click(button: number, shift: number, x: Unit, y: undefined): void; - function inMyParty(name: any): void; - function findPlayer(name: any): void; - function getPlayerUnit(name: any): void; - function getPlayerAct(player: any): void; - function getNearbyPlayerCount(): void; - function getPlayerCount(): void; - function openChest(unit: any): boolean; - function openChestsInArea(area?: any, chestIds?: any): void; - function openChests(range: any): void; - function scanShrines(range: any): void; - function getShrine(unit: any): void; - function getShrinesInArea(area: any, type: any, use: any): void; - function getItemDesc(unit: any): void; - function getItemSockets(unit: any): void; - function itemLogger(action: string, unit: Unit, text?: string | undefined): void; - function logItem(action: string, unit: ItemUnit | undefined, keptLine?: any): void; - function skipItem(id: any): void; - function shapeShift(mode: any): void; - function unShift(): void; - function townCheck(boolean?: boolean): void; - function spy(name: any): void; - function fileAction(path: any, mode: any, msg: any): void; - function errorReport(error: Error | string, script?: string): void; - function debugLog(msg: any): void; - function useMenu(id: number): void; - function clone(obj: any): void; - function copy(from: any): void; - function poll(check: () => T, timeout?: number, sleep?: number): T; - function getUIFlags(excluded?: []): void; +export{}; +declare global { + namespace Misc { + const screenshotErrors: any; + const errorConsolePrint: any; + const useItemLog: boolean; + + function click(button: number, shift: number, unit: Unit): void; + function click(button: number, shift: number, x: Unit, y: undefined): void; + function inMyParty(name: any): void; + function findPlayer(name: any): void; + function getPlayerUnit(name: any): void; + function getPlayerAct(player: any): void; + function getNearbyPlayerCount(): void; + function getPlayerCount(): void; + function openChest(unit: any): boolean; + function openChestsInArea(area?: any, chestIds?: any): void; + function openChests(range: any): void; + function scanShrines(range: any): void; + function getShrine(unit: any): void; + function getShrinesInArea(area: any, type: any, use: any): void; + function getItemDesc(unit: any): void; + function getItemSockets(unit: any): void; + function itemLogger(action: string, unit: Unit, text?: string | undefined): void; + function logItem(action: string, unit: ItemUnit | undefined, keptLine?: any): void; + function skipItem(id: any): void; + function shapeShift(mode: any): void; + function unShift(): void; + function townCheck(boolean?: boolean): void; + function spy(name: any): void; + function fileAction(path: any, mode: any, msg: any): void; + function errorReport(error: Error | string, script?: string): void; + function debugLog(msg: any): void; + function useMenu(id: number): void; + function clone(obj: any): void; + function copy(from: any): void; + function poll(check: () => T, timeout?: number, sleep?: number): T; + function getUIFlags(excluded?: []): void; + } } diff --git a/d2bs/kolbot/sdk/types/Pickit.d.ts b/d2bs/kolbot/sdk/types/Pickit.d.ts index 9f35db095..8a600b2f0 100644 --- a/d2bs/kolbot/sdk/types/Pickit.d.ts +++ b/d2bs/kolbot/sdk/types/Pickit.d.ts @@ -10,25 +10,25 @@ declare global { CRAFTING: 5, UTILITY: 6 }; - const Pickit: { - gidList: number[], - invoLocked: boolean, - beltSize: 1 | 2 | 3 | 4, - ignoreLog: number[], // Ignored item types for item logging - Result: PickitResult, - tkable: number[], - essentials: number[], + namespace Pickit { + const gidList: number[]; + let invoLocked: boolean; + let beltSize: 1 | 2 | 3 | 4; + const ignoreLog: number[]; // Ignored item types for item logging + const Result: PickitResult; + const tkable: number[]; + const essentials: number[]; - init: (notify: any) => void - itemEvent: (gid?: number, mode?: number, code?: number, global?: number) => void - sortItems: (unitA: Unit, unitB: Unit) => number - sortFastPickItems: (unitA: Unit, unitB: Unit) => number - checkBelt: () => boolean - canPick: (unit: ItemUnit) => boolean - checkItem: (unit: ItemUnit) => { result: PickitResult, line: null | number } - pickItem: (unit: ItemUnit, status?: PickitResult, keptLine?: any, retry?: number) => { result: PickitResult, line: string | null }; - canMakeRoom: () => boolean - pickItems: (range?: number) => boolean - fastPick: () => boolean + function init(notify: any): void; + function itemEvent(gid?: number, mode?: number, code?: number, global?: number): void; + function sortItems(unitA: Unit, unitB: Unit): number; + function sortFastPickItems(unitA: Unit, unitB: Unit): number; + function checkBelt(): boolean; + function canPick(unit: ItemUnit): boolean; + function checkItem(unit: ItemUnit): { result: PickitResult, line: null | number }; + function pickItem(unit: ItemUnit, status?: PickitResult, keptLine?: any, retry?: number): { result: PickitResult, line: string | null }; + function canMakeRoom(): boolean; + function pickItems(range?: number): boolean; + function fastPick(): boolean; } } diff --git a/d2bs/kolbot/sdk/types/Util.d.ts b/d2bs/kolbot/sdk/types/Util.d.ts index ef375f1fa..0327d483d 100644 --- a/d2bs/kolbot/sdk/types/Util.d.ts +++ b/d2bs/kolbot/sdk/types/Util.d.ts @@ -127,5 +127,12 @@ declare global { arg3?: number; arg4?: number; }; + + namespace Sort { + function units(a: Unit, b: Unit): number; + function presetUnits(a: PresetUnit, b: PresetUnit): number; + function points(a: [number, number], b: [number, number]): number; + function numbers(a: number, b: number): number; + } } export {}; diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 370ffdc06..6a59edc0e 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -779,7 +779,17 @@ declare global { // shrine types export namespace shrines { - const Ids: [2, 81, 83, 170, 344, 197, 202]; + const Presets: [2, 81, 83, 170, 344, 197, 202]; + const Ids: [ + 2, 77, 81, 83, 84, 85, 93, 96, 97, 109, 116, 123, 124, + 133, 134, 135, 136, 150, 151, 164, 165, 166, 167, 168, + 170, 172, 173, 184, 190, 191, 197, 199, 200, 201, 202, + 206, 226, 231, 232, 236, 249, 260, 262, 263, 264, 265, + 275, 276, 277, 278, 279, 280, 281, 282, 299, 300, 302, + 303, 320, 325, 343, 344, 361, 414, 415, 421, 422, 423, + 427, 428, 464, 465, 472, 479, 483, 484, 488, 491, 492, + 495, 497, 499, 503, 509, 512, 520, 521, 522 + ]; const None: 0; const Refilling: 1; const Health: 2; @@ -1575,6 +1585,11 @@ declare global { } export namespace exits { + namespace type { + const WalkThrough: 1; + const Stairs: 2; + const RedPortal: 60; + } namespace preset { const AreaEntrance: 0; // special // act 1 From ad254044bf58c41a3bfe38637bc5e4c6eb397778 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:23:39 -0400 Subject: [PATCH 092/758] Add missing LoginError handler --- d2bs/kolbot/libs/OOG.js | 2 ++ d2bs/kolbot/libs/modules/sdk.js | 7 +++++++ d2bs/kolbot/sdk/types/sdk.d.ts | 2 ++ 3 files changed, 11 insertions(+) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 434adc820..02de839c8 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1256,6 +1256,8 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; 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.LoginExit.click(); diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 59dd4cf50..2b04455ee 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -1667,6 +1667,11 @@ }, exits: { + type: { + WalkThrough: 1, + Stairs: 2, + RedPortal: 60, + }, preset: { AreaEntrance: 0, // special // act 1 @@ -4483,6 +4488,8 @@ UnableToCreateAccount: 5249, Disconnected: 5347, UnableToIndentifyVersion: 5245, + BattlenetNotResponding: 5353, + BattlenetNotResponding2: 5354, EnhancedDamage: 10038, LoDKeyDisabled: 10913, CdKeyInUseBy: 10914, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 6a59edc0e..8ef762176 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4407,6 +4407,8 @@ declare global { const UnableToCreateAccount: 5249; const Disconnected: 5347; const UnableToIndentifyVersion: 5245; + const BattlenetNotResponding: 5353; + const BattlenetNotResponding2: 5354; const EnhancedDamage: 10038; const LoDKeyDisabled: 10913; const CdKeyInUseBy: 10914; From ef3bd720ab3a9da6ea6d7af62d99b66a93f8b578 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Mar 2023 14:39:55 -0400 Subject: [PATCH 093/758] Add Date prototype dateStamp returns string "[mm/dd/yyyy}" - Use new dateStamp to prepend printToConsole messages with date as they currently only show time --- d2bs/kolbot/libs/Polyfill.js | 21 +++++++++++++++++++++ d2bs/kolbot/libs/oog/D2Bot.js | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index c84dd3fdb..34451cb09 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -714,6 +714,9 @@ Map.prototype.toString = function() { return JSON.stringify(obj); }; +/** + * @returns {Array} + */ Map.prototype.keys = function() { let keys = []; // eslint-disable-next-line no-unused-vars @@ -1034,6 +1037,24 @@ Set.prototype.difference = function(setB) { })(); })([].filter.constructor("return this")(), print); +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Date Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Date.prototype.dateStamp + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + +if (!Date.prototype.hasOwnProperty("dateStamp")) { + Object.defineProperty(Date.prototype, "dateStamp", { + value: function () { + let month = this.getMonth() + 1; + let day = this.getDate(); + let year = this.getFullYear(); + return "[" + (month < 10 ? "0" + month : month) + "/" + (day < 10 ? "0" + day : day) + "/" + year + "]"; + } + }); +} /** * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index 7670e6835..607c5b25d 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -39,7 +39,7 @@ includeIfNotIncluded("oog/DataFile.js"); printToConsole: function (msg, color, tooltip, trigger) { let printObj = { - msg: msg, + msg: ((new Date().dateStamp() + " ") + msg), color: color || 0, tooltip: tooltip || "", trigger: trigger || "" From 30dc6039bd29cec016de47b04b16c622fc99e7e1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Mar 2023 15:23:45 -0400 Subject: [PATCH 094/758] Update require.js - Block notify unless debug is set, cleans up logs --- d2bs/kolbot/libs/require.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/require.js b/d2bs/kolbot/libs/require.js index c34cc8e06..33afc05b3 100644 --- a/d2bs/kolbot/libs/require.js +++ b/d2bs/kolbot/libs/require.js @@ -10,7 +10,7 @@ // noinspection ThisExpressionReferencesGlobalObjectJS <-- definition of global here typeof global === "undefined" && (this["global"] = this); -global["module"] = {exports: undefined}; +global["module"] = { exports: undefined }; global["exports"] = {}; function removeRelativePath(test) { return test.replace(/\\/g, "/").split("/").reduce(function (acc, cur) { @@ -25,10 +25,11 @@ function removeRelativePath(test) { }, []).join("/"); } global.require = (function (include, isIncluded, print, notify) { + const debug = false; let depth = 0; const modules = {}; - const obj = function require(field, path, log = true) { + const obj = function require(field, path) { const stack = new Error().stack.match(/[^\r\n]+/g); let directory = stack[1].match(/.*?@.*?d2bs\\(kolbot\\?.*)\\.*(\.js|\.dbj):/)[1].replace("\\", "/") + "/"; let filename = stack[1].match(/.*?@.*?d2bs\\kolbot\\?(.*)(\.js|\.dbj):/)[1]; @@ -75,7 +76,7 @@ global.require = (function (include, isIncluded, print, notify) { const moduleNameShort = nameShort; if (!isIncluded(fullpath + ".js") && !modules.hasOwnProperty(moduleNameShort)) { - if (log) { + if (debug) { depth && notify && print("ÿc2Kolbotÿc0 :: - loading dependency of " + filename + ": " + moduleNameShort); !depth && notify && print("ÿc2Kolbotÿc0 :: Loading module: " + moduleNameShort); } @@ -84,7 +85,7 @@ global.require = (function (include, isIncluded, print, notify) { let oldExports = Object.create(global["exports"]); delete global["module"]; delete global["exports"]; - global["module"] = {exports: null}; + global["module"] = { exports: null }; global["exports"] = {}; // Include the file; From a75689a497853e101df71288d5b0c3a14ecb77f4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 Mar 2023 19:19:48 -0400 Subject: [PATCH 095/758] Add reqLvl to RuneData --- d2bs/kolbot/libs/core/GameData/RuneData.js | 3 +++ d2bs/kolbot/sdk/types/Runewords.d.ts | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/RuneData.js b/d2bs/kolbot/libs/core/GameData/RuneData.js index 3cbd59c9e..afd09c640 100644 --- a/d2bs/kolbot/libs/core/GameData/RuneData.js +++ b/d2bs/kolbot/libs/core/GameData/RuneData.js @@ -55,6 +55,9 @@ this.runes = runes; this.itemTypes = itemTypes; this._ladder = ladderRws.includes(name); + let highestItem = runes.sort((a, b) => b - a).first(); + let reqLvl = getBaseStat("items", highestItem, "levelreq"); + this.reqLvl = reqLvl > 0 ? reqLvl : 1; } RunewordObj.prototype.ladderRestricted = function () { diff --git a/d2bs/kolbot/sdk/types/Runewords.d.ts b/d2bs/kolbot/sdk/types/Runewords.d.ts index 19543d5cb..ab9cd56c3 100644 --- a/d2bs/kolbot/sdk/types/Runewords.d.ts +++ b/d2bs/kolbot/sdk/types/Runewords.d.ts @@ -8,12 +8,13 @@ declare global { * @method ladderRestricted - Returns true if we are unable to make the runeword because we are not on ladder. */ interface runeword { - name: string; - sockets: number; - runes: number[]; - itemTypes: number[]; - _ladder: boolean; - ladderRestricted: () => boolean; + name: string; + sockets: number; + runes: number[]; + itemTypes: number[]; + _ladder: boolean; + reqLvl: number; + ladderRestricted: () => boolean; } namespace Runeword { From ae70c3a6004dd28ac9edfc9a233cdc3d8010653d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 Mar 2023 00:06:07 -0400 Subject: [PATCH 096/758] Update RuneData.js - Fix mutation of original runes array when finding the highest classid --- d2bs/kolbot/libs/core/GameData/RuneData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/GameData/RuneData.js b/d2bs/kolbot/libs/core/GameData/RuneData.js index afd09c640..fefd11105 100644 --- a/d2bs/kolbot/libs/core/GameData/RuneData.js +++ b/d2bs/kolbot/libs/core/GameData/RuneData.js @@ -55,7 +55,7 @@ this.runes = runes; this.itemTypes = itemTypes; this._ladder = ladderRws.includes(name); - let highestItem = runes.sort((a, b) => b - a).first(); + let highestItem = runes.slice().sort((a, b) => b - a).first(); let reqLvl = getBaseStat("items", highestItem, "levelreq"); this.reqLvl = reqLvl > 0 ? reqLvl : 1; } From a98afb63bd2001a1421e5c135c2a00dcdaa5c23d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:21:23 -0400 Subject: [PATCH 097/758] Update Pickit.js - Add ignoreList to pickit, don't try to continually pick the same item if we can't fit it. --- d2bs/kolbot/libs/core/Pickit.js | 39 +++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index d3513a8a0..519a4da3b 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -510,10 +510,10 @@ const Pickit = { return false; }, - /** - * @type {ItemUnit[]} - */ + /** @type {ItemUnit[]} */ pickList: [], + /** @type {Set} */ + ignoreList: new Set(), /** * @param {number} range @@ -523,7 +523,7 @@ const Pickit = { if (me.dead) return false; let needMule = false; - let canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); + const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); // why wait for idle? while (!me.idle) { @@ -534,6 +534,7 @@ const Pickit = { 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)); @@ -552,6 +553,12 @@ const Pickit = { // get the actual item again const itemToPick = Game.getItem(check.classid, -1, check.gid); + if (Pickit.ignoreList.has(itemToPick.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 (itemToPick && copyUnit(itemToPick).x !== undefined && itemToPick.onGroundOrDropping @@ -578,6 +585,7 @@ const Pickit = { 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(); return this.pickItems(); } @@ -588,20 +596,27 @@ const Pickit = { } // Can't make room - trigger automule - Item.logger("No room for", itemToPick); - console.warn("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); - if (copyUnit(itemToPick).x !== undefined) { - canUseMule && console.debug("Attempt to trigger automule"); - needMule = true; + Item.logger("No room for", itemToPick); + console.warn("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); + Pickit.ignoreList.add(itemToPick.gid); + if (canUseMule) { + console.debug("Attempt to trigger automule"); + needMule = true; + } break; } } // Item can fit - pick it up - if (canFit && !this.pickItem(itemToPick, status.result, status.line)) { - console.warn("Failed to pick item " + item.prettyPrint); + if (canFit) { + let picked = this.pickItem(itemToPick, status.result, status.line); + if (!picked) { + console.warn("Failed to pick item " + itemToPick.prettyPrint); + + break; + } } } } @@ -611,6 +626,8 @@ const Pickit = { if (needMule && canUseMule && AutoMule.getMuleItems().length > 0) { scriptBroadcast("mule"); scriptBroadcast("quit"); + + return false; } return true; From 2c5e938fe9e08d56bec8100958f3f9b2b6d03c70 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Mar 2023 12:05:19 -0400 Subject: [PATCH 098/758] Update Pickit.js - Handle if itemToPick is undefined --- d2bs/kolbot/libs/core/Pickit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 519a4da3b..808613b61 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -553,7 +553,7 @@ const Pickit = { // get the actual item again const itemToPick = Game.getItem(check.classid, -1, check.gid); - if (Pickit.ignoreList.has(itemToPick.gid)) { + if (!itemToPick || Pickit.ignoreList.has(itemToPick.gid)) { Pickit.pickList.shift(); continue; From 8453d5274ebeb3f6136d0fdea8ac2812103dfb10 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 2 Apr 2023 19:43:19 -0400 Subject: [PATCH 099/758] Update Controls + ControlAction + sdk - Fix CdKeyInUse locale string value, the one I had previously was for Lod key in use - Add `CannotCreateGamesDeadHCChar`, it's the popup single player chars get when selecing a dead hc character to use - When we delete a datafile and use a default one if the old was corrupted, transfer the handle value, otherwise we can have weird bugs using a handle value of 0 - Add some more typings, I had missed Text hooks and padStart/End string prototypes - Organized ControlAction a little better, still want to refactor more. - added typedef for the character info passed in to multiple functions. - reduced repetitive finding character code. Instead define that code once in findCharacter and have calls to that for the other functions - added convertCharacter, unused but should have the functionality to do so - Cleaned up the Control module a bit to use Generalized names for the controls that are repeat, i.e CharSelectExit, LoginExit, are now just BottomLeftExit --- d2bs/kolbot/D2BotChannel.dbj | 2 +- d2bs/kolbot/D2BotCleaner.dbj | 8 +- d2bs/kolbot/D2BotFollow.dbj | 2 +- d2bs/kolbot/D2BotGameAction.dbj | 2 +- d2bs/kolbot/D2BotLead.dbj | 4 +- d2bs/kolbot/D2BotMule.dbj | 12 +- d2bs/kolbot/D2BotMuleLog.dbj | 6 +- d2bs/kolbot/D2BotPubJoin.dbj | 2 +- d2bs/kolbot/libs/OOG.js | 1000 +++++++++++++-------------- d2bs/kolbot/libs/modules/Control.js | 313 +++++---- d2bs/kolbot/libs/modules/sdk.js | 4 +- d2bs/kolbot/libs/oog/DataFile.js | 4 +- d2bs/kolbot/sdk/globals.d.ts | 26 + d2bs/kolbot/sdk/types/sdk.d.ts | 10 +- 14 files changed, 724 insertions(+), 671 deletions(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 6571b27f8..d6e28a470 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -496,7 +496,7 @@ function main() { 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/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index 39fab0816..bdc951fa7 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -359,14 +359,14 @@ function locationAction (location) { // Single Player screen fix if (currAcc.toLowerCase() !== "singleplayer") { if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; } } if (!charList.length) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; } @@ -401,7 +401,7 @@ function locationAction (location) { if (!charList.length) { accounts.shift(); chars.shift(); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); } FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); @@ -413,7 +413,7 @@ function locationAction (location) { break; case sdk.game.locations.CharacterCreate: case sdk.game.locations.NewCharSelected: - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; case sdk.game.locations.CharSelectPleaseWait: diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 51b6fb416..7ff9a6bef 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -427,7 +427,7 @@ function main () { 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/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index fd63d633d..2a63550bb 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -180,7 +180,7 @@ function locationAction (location) { // Single Player screen fix if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; } diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 922d9033e..137d9b951 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -283,7 +283,7 @@ function locationAction (location) { break; case sdk.game.locations.CharacterCreate: - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; case sdk.game.locations.OtherMultiplayer: @@ -348,7 +348,7 @@ function main () { 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 (true) { // returns true before actually in game so we can't only use this check diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 3aec2a509..f5a3097c8 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -543,7 +543,7 @@ new Overrides.Override (Starter, Starter.updateCount, function () { MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); ControlAction.loginAccount(info); delay(1000); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); }).apply(); function locationAction (location) { @@ -730,7 +730,7 @@ function locationAction (location) { // Single Player screen fix // TODO: see if this is still needed. d2bs doesn't load scripts twice anymore if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; } @@ -740,7 +740,7 @@ function locationAction (location) { D2Bot.updateStatus("Realm Down"); delay(1000); - if (!Controls.CharSelectExit.click()) { + if (!Controls.BottomLeftExit.click()) { break; } @@ -762,7 +762,7 @@ function locationAction (location) { if (makeNext) { if (obj.fullChars.length >= maxCharCount || (muleMode > 0 && obj.torchChars.length >= maxCharCount)) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); MuleData.nextAccount(); break; @@ -797,7 +797,7 @@ function locationAction (location) { } else { // premade account that's already full if (ControlAction.getCharacters().length >= maxCharCount) { - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); MuleData.nextAccount(); break; @@ -846,7 +846,7 @@ function locationAction (location) { break; case sdk.game.locations.OkCenteredErrorPopUp: Controls.OkCentered.click(); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; case sdk.game.locations.ServerDown: diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index a9cc80e5e..3566281a5 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -165,11 +165,11 @@ function locationAction (location) { break; case sdk.game.locations.CharSelect: // Single Player screen fix - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control && Controls.CharSelectExit.click()) { + if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control && Controls.BottomLeftExit.click()) { break; } - if (!charList.length && Controls.CharSelectExit.click()) { + if (!charList.length && Controls.BottomLeftExit.click()) { break; } @@ -228,7 +228,7 @@ function locationAction (location) { break; case sdk.game.locations.NewCharSelected: - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; case sdk.game.locations.CharSelectPleaseWait: diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index 782587b0e..cfab177cf 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -339,7 +339,7 @@ function locationAction (location) { break; case sdk.game.locations.CharacterCreate: - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; case sdk.game.locations.OtherMultiplayer: diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 02de839c8..05ed2f143 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -23,13 +23,19 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } else { Object.assign(root, factory()); } -}(this, function() { +}([].filter.constructor("return this")(), function() { const Controls = require("./modules/Control"); const ControlAction = { mutedKey: false, realms: { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }, + /** + * @param {string} text + * @param {number} time - in milliseconds + * @param {Function} [stopfunc] + * @param {*} [arg] + */ timeoutDelay: function (text, time, stopfunc, arg) { let currTime = 0; let endTime = getTickCount() + time; @@ -53,6 +59,16 @@ includeIfNotIncluded("oog/D2Bot.js"); // required Starter.delay = 0; }, + /** + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + * @param {number} targetx + * @param {number} targety + * @returns {boolean} + */ click: function (type, x, y, xsize, ysize, targetx, targety) { let control = getControl(type, x, y, xsize, ysize); @@ -67,6 +83,15 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return true; }, + /** + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + * @param {string} text + * @returns {boolean} + */ setText: function (type, x, y, xsize, ysize, text) { if (!text) return false; @@ -87,103 +112,26 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return true; }, + /** + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + * @returns {string[] | false} + */ getText: function (type, x, y, xsize, ysize) { let control = getControl(type, x, y, xsize, ysize); return (!!control ? control.getText() : false); }, - joinChannel: function (channel) { + // ~~~ Start of general functions ~~~ // + scrollDown: function () { me.blockMouse = true; - - let tick; - let rval = false; - let timeout = 5000; - - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.Lobby: - Controls.LobbyEnterChat.click(); - - break; - case sdk.game.locations.LobbyChat: - let currChan = Controls.LobbyChannelName.getText(); // returns array - - if (currChan) { - for (let i = 0; i < currChan.length; i += 1) { - if (currChan[i].split(" (") && String.isEqual(currChan[i].split(" (")[0], channel)) { - rval = true; - - break MainLoop; - } - } - } - - !tick && Controls.LobbyChannel.click() && (tick = getTickCount()); - - break; - case sdk.game.locations.ChannelList: // Channel - Controls.LobbyChannelText.setText(channel); - Controls.LobbyChannelOk.click(); - - break; - } - - if (getTickCount() - tick >= timeout) { - break; - } - - delay(100); - } - - me.blockMouse = false; - - return rval; - }, - - createGame: function (name, pass, diff, delay) { - Controls.CreateGameName.setText(name); - Controls.CreateGamePass.setText(pass); - - switch (diff) { - case "Normal": - Controls.Normal.click(); - - break; - case "Nightmare": - Controls.Nightmare.click(); - - break; - case "Highest": - if (Controls.Hell.disabled !== 4 && Controls.Hell.click()) { - break; - } - - if (Controls.Nightmare.disabled !== 4 && Controls.Nightmare.click()) { - break; - } - - Controls.Normal.click(); - - break; - default: - Controls.Hell.click(); - - break; - } - - !!delay && this.timeoutDelay("Make Game Delay", delay); - - if (Starter.chanInfo.announce) { - Starter.sayMsg("Next game is " + name + (pass === "" ? "" : "//" + pass)); + for (let i = 0; i < 4; i++) { + sendKey(sdk.keys.code.DownArrow); } - - me.blockMouse = true; - - print("Creating Game: " + name); - Controls.CreateGame.click(); - me.blockMouse = false; }, @@ -245,185 +193,22 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return true; }, - loginAccount: function (info) { - me.blockMouse = true; - - let locTick; - let tick = getTickCount(); - - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.PreSplash: - break; - case sdk.game.locations.MainMenu: - info.realm && ControlAction.clickRealm(this.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.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 - default: - print(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; - }, - - setEmail: function (email = "", domain = "@email.com") { - if (getLocation() !== sdk.game.locations.RegisterEmail) return false; - if (!email || !email.length) { - email = Starter.randomString(null, true); - } - - while (getLocation() !== sdk.game.locations.CharSelect) { - switch (getLocation()) { - case sdk.game.locations.RegisterEmail: - if (Controls.EmailSetEmail.setText(email + domain) && Controls.EmailVerifyEmail.setText(email + domain)) { - Controls.EmailRegister.click(); - delay(100); - } - - break; - case sdk.game.locations.LoginError: - // todo test what conditions get here other than email not matching - D2Bot.printToConsole("Failed to set email"); - Controls.LoginErrorOk.click(); - - return false; - case sdk.game.locations.CharSelectNoChars: - // fresh acc - return true; - } - } - - return true; - }, - - makeAccount: function (info) { - me.blockMouse = true; - - let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; - - // cycle until in empty char screen - MainLoop: - while (getLocation() !== sdk.game.locations.CharSelectNoChars) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - ControlAction.clickRealm(this.realms[info.realm]); - if (openBnet) { - Controls.OtherMultiplayer.click() && Controls.OpenBattleNet.click(); - } else { - Controls.BattleNet.click(); - } - - break; - case sdk.game.locations.Login: - Controls.CreateNewAccount.click(); - - break; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); - - break; - case sdk.game.locations.CharacterCreate: - Controls.CharSelectExit.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.PleaseRead: - Controls.PleaseReadOk.click(); - - break; - case sdk.game.locations.RegisterEmail: - Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); - - break; - case sdk.game.locations.CharSelect: - if (openBnet) { - break MainLoop; - } - - break; - default: - break; - } - - delay(100); - } - - me.blockMouse = false; - - return true; - }, - - scrollDown: function () { - me.blockMouse = true; - for (let i = 0; i < 4; i++) { - sendKey(sdk.keys.code.DownArrow); - } - me.blockMouse = false; - }, + /** + * @typedef {Object} CharacterInfo + * @property {string} charName + * @property {string} charClass + * @property {number} charLevel + * @property {boolean} expansion + * @property {boolean} hardcore + * @property {boolean} ladder + */ - findCharacter: function (info) { + /** + * @param {CharacterInfo} info + * @param {boolean} [startFromTop] + * @returns {Control | false} + */ + findCharacter: function (info, startFromTop = true) { let count = 0; let tick = getTickCount(); @@ -436,7 +221,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } // start from beginning of the char list - sendKey(sdk.keys.code.Home); + startFromTop && sendKey(sdk.keys.code.Home); while (getLocation() === sdk.game.locations.CharSelect && count < 24) { let control = Controls.CharSelectCharInfo0.control; @@ -449,7 +234,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required count++; if (String.isEqual(text[1], info.charName)) { - return true; + return control; } } } while (count < 24 && control.getNext()); @@ -467,7 +252,6 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return false; }, - // get all characters getCharacters: function () { let count = 0; let list = []; @@ -507,24 +291,31 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return list; }, + /** + * @param {CharacterInfo} info + * @returns {boolean} + */ getPermStatus: function (info) { - let count = 0; - let tick = getTickCount(); let expireStr = getLocaleString(sdk.locale.text.ExpiresIn); expireStr = expireStr.slice(0, expireStr.indexOf("%")).trim(); - while (getLocation() !== sdk.game.locations.CharSelect) { - if (getTickCount() - tick >= 5000) { - break; - } + let control = this.findCharacter(info); + if (!control) return false; - delay(25); - } + let text = control.getText(); + if (!Array.isArray(text) || typeof text[1] !== "string") return false; - // start from beginning of the char list - sendKey(sdk.keys.code.Home); + return !text.some(el => el.includes(expireStr)); + }, - while (getLocation() === sdk.game.locations.CharSelect && count < 24) { + /** + * get character position - useless? this doesn't take any arguments to even check the character + * @returns {number} + */ + getPosition: function () { + let position = 0; + + if (getLocation() === sdk.game.locations.CharSelect) { let control = Controls.CharSelectCharInfo0.control; if (control) { @@ -532,236 +323,470 @@ includeIfNotIncluded("oog/D2Bot.js"); // required let text = control.getText(); if (text instanceof Array && typeof text[1] === "string") { - count++; - - if (String.isEqual(text[1], info.charName)) { - return !text.some(el => el.includes(expireStr)); - } + position += 1; } - } while (count < 24 && control.getNext()); + } while (control.getNext()); } + } + + return position; + }, + + /** + * @param {CharacterInfo} info + * @returns {boolean} + */ + makeCharacter: function (info) { + me.blockMouse = true; + !info.charClass && (info.charClass = "barbarian"); + (!info.charName || info.charName.length < 2 || info.charName.length > 15) && (info.charName = Starter.randomString(8, false)); + info.charName.match(/\d+/g) && (info.charName.replace(/\d+/g, "")); + !info.expansion && ["druid", "assassin"].includes(info.charClass) && (info.expansion = true); + + let clickCoords = []; + /** @type {Map el.toLowerCase().includes("expansion"))) { + console.warn(info.charName + " already expansion"); + console.debug(control, "\n", control.getText()); + + return false; + } + + try { + me.blockMouse = true; + console.log("converting character to expansion " + info.charName); + control.click(); + Controls.CharSelectConvert.click(); + delay(500); + Controls.PopupYes.click(); + delay(500); + + return true; + } catch (e) { + console.error(e); + + return false; + } finally { + me.blockMouse = false; + } + }, + + /** + * @param {CharacterInfo} info + * @param {boolean} startFromTop + * @returns {boolean} + */ + loginCharacter: function (info, startFromTop = true) { + me.blockMouse = true; + + try { + MainLoop: + // cycle until in lobby or in game + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + let control = this.findCharacter(info, startFromTop); + if (!control) return false; + + control.click(); + Controls.BottomRightOk.click(); + Starter.locationTimeout(sdk.game.locations.CharSelect, 5000); + + return getLocation() === sdk.game.locations.SelectDifficultySP + ? login(info.profile) + : true; + case sdk.game.locations.CharSelectNoChars: + Controls.BottomLeftExit.click(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.OkCenteredErrorPopUp: + break MainLoop; + default: + break; + } + + delay(100); + } + + return true; + } catch (e) { + console.error(e); + + return false; + } finally { + me.blockMouse = false; + } + }, + + setEmail: function (email = "", domain = "@email.com") { + if (getLocation() !== sdk.game.locations.RegisterEmail) return false; + if (!email || !email.length) { + email = Starter.randomString(null, true); + } + + while (getLocation() !== sdk.game.locations.CharSelect) { + switch (getLocation()) { + case sdk.game.locations.RegisterEmail: + if (Controls.EmailSetEmail.setText(email + domain) && Controls.EmailVerifyEmail.setText(email + domain)) { + Controls.EmailRegister.click(); + delay(100); + } + + break; + case sdk.game.locations.LoginError: + // todo test what conditions get here other than email not matching + D2Bot.printToConsole("Failed to set email"); + Controls.LoginErrorOk.click(); + + return false; + case sdk.game.locations.CharSelectNoChars: + // fresh acc + return true; + } + } + + return true; + }, + + makeAccount: function (info) { + me.blockMouse = true; + + let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; + + // cycle until in empty char screen + MainLoop: + while (getLocation() !== sdk.game.locations.CharSelectNoChars) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + ControlAction.clickRealm(this.realms[info.realm]); + if (openBnet) { + Controls.OtherMultiplayer.click() && Controls.OpenBattleNet.click(); + } else { + Controls.BattleNet.click(); + } + + break; + case sdk.game.locations.Login: + Controls.CreateNewAccount.click(); + + break; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); + + break; + case sdk.game.locations.CharacterCreate: + Controls.BottomLeftExit.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(); - // get character position - getPosition: function () { - let position = 0; + break; + case sdk.game.locations.PleaseRead: + Controls.PleaseReadOk.click(); - if (getLocation() === sdk.game.locations.CharSelect) { - let control = Controls.CharSelectCharInfo0.control; + break; + case sdk.game.locations.RegisterEmail: + Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); - if (control) { - do { - let text = control.getText(); + break; + case sdk.game.locations.CharSelect: + if (openBnet) { + break MainLoop; + } - if (text instanceof Array && typeof text[1] === "string") { - position += 1; - } - } while (control.getNext()); + break; + default: + break; } + + delay(100); } - return position; + me.blockMouse = false; + + return true; }, - loginCharacter: function (info, startFromTop = true) { + loginAccount: function (info) { me.blockMouse = true; - let count = 0; - - // start from beginning of the char list - startFromTop && sendKey(sdk.keys.code.Home); + let locTick; + let tick = getTickCount(); MainLoop: - // cycle until in lobby or in game - while (getLocation() !== sdk.game.locations.Lobby) { + while (true) { 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") { - count++; - - if (String.isEqual(text[1], info.charName)) { - control.click(); - Controls.CreateNewAccountOk.click(); - me.blockMouse = false; - - if (getLocation() === sdk.game.locations.SelectDifficultySP) { - try { - login(info.profile); - } 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; - } + case sdk.game.locations.PreSplash: + break; + case sdk.game.locations.MainMenu: + info.realm && ControlAction.clickRealm(this.realms[info.realm]); + Controls.BattleNet.click(); break; - case sdk.game.locations.CharSelectNoChars: - Controls.CharSelectExit.click(); + case sdk.game.locations.Login: + Controls.EnterAccountName.setText(info.account); + Controls.EnterAccountPassword.setText(info.password); + Controls.Login.click(); break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.OkCenteredErrorPopUp: + 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; - default: + 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 + default: + print(getLocation()); + + me.blockMouse = false; + + return false; + } + + if (getTickCount() - tick >= 20000) { + return false; } delay(100); } + delay(1000); + me.blockMouse = false; - return false; + return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; }, - makeCharacter: function (info) { + joinChannel: function (channel) { me.blockMouse = true; - !info.charClass && (info.charClass = "barbarian"); - - if (info.charName.match(/\d+/g)) { - console.warn("Invalid character name, cannot contain numbers"); - - return false; - } - let clickCoords = []; + let tick; + let rval = false; + let timeout = 5000; - // cycle until in lobby - while (getLocation() !== sdk.game.locations.Lobby) { + MainLoop: + while (true) { switch (getLocation()) { - case sdk.game.locations.CharSelect: - case sdk.game.locations.CharSelectNoChars: - // Create Character greyed out - if (Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { - me.blockMouse = false; + case sdk.game.locations.Lobby: + Controls.LobbyEnterChat.click(); - return false; - } + break; + case sdk.game.locations.LobbyChat: + let currChan = Controls.LobbyChannelName.getText(); // returns array - Controls.CharSelectCreate.click(); + if (currChan) { + for (let i = 0; i < currChan.length; i += 1) { + if (currChan[i].split(" (") && String.isEqual(currChan[i].split(" (")[0], channel)) { + rval = 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]; + break MainLoop; + } } - })(); + } - // coords: - // zon: 100, 280 - // barb: 400, 280 - // necro: 300, 290 - // sin: 200, 280 - // paladin: 521 260 - // sorc: 620, 270 - // druid: 700, 280 + !tick && Controls.LobbyChannel.click() && (tick = getTickCount()); - getControl().click(clickCoords[0], clickCoords[1]); - delay(500); + break; + case sdk.game.locations.ChannelList: // Channel + Controls.LobbyChannelText.setText(channel); + Controls.LobbyChannelOk.click(); break; - case sdk.game.locations.NewCharSelected: - if (Controls.CharCreateHCWarningOk.control) { - Controls.CharCreateHCWarningOk.click(); - } else { - Controls.CharCreateCharName.setText(info.charName); + } - if (!info.expansion) { - switch (info.charClass) { - case "druid": - case "assassin": - D2Bot.printToConsole("Error in profile name. Expansion characters cannot be made in classic", sdk.colors.D2Bot.Red); - D2Bot.stop(); + if (getTickCount() - tick >= timeout) { + break; + } - break; - default: - break; - } + delay(100); + } - Controls.CharCreateExpansion.click(); - } + me.blockMouse = false; - !info.ladder && Controls.CharCreateLadder.click(); - info.hardcore && Controls.CharCreateHardcore.click(); + return rval; + }, - Controls.CreateNewAccountOk.click(); - } + createGame: function (name, pass, diff, delay) { + Controls.CreateGameName.setText(name); + Controls.CreateGamePass.setText(pass); - break; - case sdk.game.locations.OkCenteredErrorPopUp: - // char name exists (text box 4, 268, 320, 264, 120) - Controls.OkCentered.click(); - Controls.CharSelectExit.click(); + switch (diff) { + case "Normal": + Controls.Normal.click(); - me.blockMouse = false; + break; + case "Nightmare": + Controls.Nightmare.click(); - return false; - default: + break; + case "Highest": + if (Controls.Hell.disabled !== 4 && Controls.Hell.click()) { break; } - // Singleplayer loop break fix. - if (me.ingame) { + if (Controls.Nightmare.disabled !== 4 && Controls.Nightmare.click()) { break; } - delay(500); + Controls.Normal.click(); + + break; + default: + Controls.Hell.click(); + + break; } - me.blockMouse = false; + !!delay && this.timeoutDelay("Make Game Delay", delay); - return true; - }, + if (Starter.chanInfo.announce) { + Starter.sayMsg("Next game is " + name + (pass === "" ? "" : "//" + pass)); + } + + me.blockMouse = true; + + print("Creating Game: " + name); + Controls.CreateGame.click(); - // Test version - modified core only + me.blockMouse = false; + }, + getGameList: function () { let text = Controls.JoinGameList.getText(); @@ -781,56 +806,6 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return false; }, - deleteCharacter: function (info) { - me.blockMouse = true; - - // start from beginning of the char list - sendKey(sdk.keys.code.Home); - - // cycle until in lobby - while (getLocation() === sdk.game.locations.CharSelect) { - let count = 0; - let control = Controls.CharSelectCharInfo0.control; - - if (control) { - do { - let text = control.getText(); - - if (text instanceof Array && typeof text[1] === "string") { - count++; - - if (String.isEqual(text[1], info.charName)) { - print("delete character " + info.charName); - - control.click(); - Controls.CharSelectDelete.click(); - delay(500); - Controls.CharDeleteYes.click(); - delay(500); - me.blockMouse = false; - - 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; - } - - delay(100); - } - - me.blockMouse = false; - - return false; - }, - getQueueTime: function() { // You are in line to create a game.,Try joining a game to avoid waiting.,,Your position in line is: ÿc02912 const text = Controls.CreateGameInLine.getText(); @@ -851,7 +826,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required case sdk.game.locations.CharSelect: if (Controls.CharSelectCurrentRealm.control) { console.log("Not in single player character select screen"); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); break; } @@ -1007,7 +982,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } delay(1000); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); }, scriptMsgEvent: function (msg) { @@ -1058,18 +1033,21 @@ includeIfNotIncluded("oog/D2Bot.js"); // required receiveCopyData: function (mode, msg) { let obj; + if (msg === "Handle") { + console.debug("Recieved Handle :: ", mode); + } msg === "Handle" && typeof mode === "number" && (Starter.handle = mode); switch (mode) { case 1: // JoinInfo obj = JSON.parse(msg); - // console.debug("Recieved Join Info :: ", obj); + console.debug("Recieved Join Info :: ", obj); Object.assign(Starter.joinInfo, obj); break; case 2: // Game info obj = JSON.parse(msg); - // console.debug("Recieved Game Info :: ", obj); + console.debug("Recieved Game Info :: "); Object.assign(Starter.gameInfo, obj); break; @@ -1084,7 +1062,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required if (Starter.gameInfo.hasOwnProperty("gameName")) { obj = JSON.parse(msg); - // console.debug("Recieved Game Request :: ", obj); + console.debug("Recieved Game Request :: ", obj.profile); if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { me.gameReady && D2Bot.joinMe(obj.profile, me.gameserverip.toString(), "", "", Starter.isUp); @@ -1180,7 +1158,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required let string = ""; let text = getLocation() === sdk.game.locations.LoginError ? Controls.LoginErrorText.getText() - : Controls.LoginInvalidCdKey.getText(); + : Controls.LoginCdKeyInUseBy.getText(); if (text) { for (let i = 0; i < text.length; i += 1) { @@ -1241,7 +1219,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; case getLocaleString(sdk.locale.text.CdKeyInUseBy): - string += (" " + Controls.LoginCdKeyInUseBy.getText()); + string += (" " + Controls.LoginLodKeyInUseBy.getText()); D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string, sdk.colors.D2Bot.Gold); D2Bot.CDKeyInUse(); @@ -1260,7 +1238,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required case getLocaleString(sdk.locale.text.BattlenetNotResponding2): case getLocaleString(sdk.locale.text.OnlyOneInstanceAtATime): Controls.LoginErrorOk.click(); - Controls.LoginExit.click(); + Controls.BottomLeftExit.click(); D2Bot.printToConsole(string); ControlAction.timeoutDelay("Login Error Delay", 5 * 6e4); D2Bot.printToConsole("Login Error - Restart"); @@ -1290,7 +1268,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required Controls.LoginErrorOk.click(); delay(1000); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); while (true) { delay(1000); @@ -1328,12 +1306,12 @@ includeIfNotIncluded("oog/D2Bot.js"); // required Controls.CharSelectCreate.click(); delay(1000); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); delay(1000); if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); Starter.gameInfo.rdBlocker && D2Bot.restart(); return false; @@ -1346,7 +1324,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required D2Bot.updateStatus("Realm Down"); delay(1000); - if (!Controls.CharSelectExit.click()) return; + if (!Controls.BottomLeftExit.click()) return; Starter.updateCount(); ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); @@ -1393,7 +1371,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required if (Starter.Config.WaitOutQueueExitToMenu) { Controls.LobbyQuit.click(); delay(1000); - Controls.CharSelectExit.click(); + Controls.BottomLeftExit.click(); } // Wait out each queue as 1 sec and add extra 10 min @@ -1553,14 +1531,14 @@ includeIfNotIncluded("oog/D2Bot.js"); // required 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(); + Controls.BottomLeftExit.click(); return false; } } // 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"); diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index cf6b780b5..d915bb32e 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -77,147 +77,186 @@ return null; } }); + } + // General Controls - Still non-exhaustive + Control.BottomLeftExit = new Control(sdk.controls.Button, 33, 572, 128, 35); + Control.BottomRightOk = new Control(sdk.controls.Button, 627, 572, 128, 35); + Control.OkCentered = new Control(sdk.controls.Button, 351, 337, 96, 32); + Control.OkCenteredText = new Control(sdk.controls.LabelBox, 268, 300, 264, 100); + Control.EnterAccountName = new Control(sdk.controls.TextBox, 322, 342, 162, 19); + Control.EnterAccountPassword = new Control(sdk.controls.TextBox, 322, 396, 162, 19); + Control.PopupYes = new Control(sdk.controls.Button, 421, 337, 96, 32); + Control.PopupNo = new Control(sdk.controls.Button, 281, 337, 96, 32); + + // Main Menu Controls + { + Control.SplashScreen = new Control(sdk.controls.TextBox, 0, 599, 800, 600); + Control.D2SplashCopyright = new Control(sdk.controls.LabelBox, 100, 580, 600, 80); + Control.MainMenuD2Version = new Control(sdk.controls.LabelBox, 0, 599, 200, 40); + Control.MainMenuCredits = new Control(sdk.controls.Button, 264, 528, 135, 25); + Control.MainMenuCinematics = new Control(sdk.controls.Button, 402, 528, 135, 25); + Control.MainMenuExit = new Control(sdk.controls.Button, 264, 568, 272, 35); + Control.SinglePlayer = new Control(-1, 264, 324, 272, 35); + Control.BattleNet = new Control(-1, 264, 366, 272, 35); + Control.Gateway = new Control(sdk.controls.Button, 264, 391, 272, 25); + Control.OtherMultiplayer = new Control(-1, 264, 433, 272, 35); } - Control.SplashScreen = new Control(sdk.controls.TextBox, 0, 599, 800, 600); - Control.D2SplashCopyright = new Control(sdk.controls.LabelBox, 100, 580, 600, 80); - Control.MainMenuD2Version = new Control(sdk.controls.LabelBox, 0, 599, 200, 40); - Control.MainMenuCredits = new Control(sdk.controls.Button, 264, 528, 135, 25); - Control.MainMenuCinematics = new Control(sdk.controls.Button, 402, 528, 135, 25); - Control.MainMenuExit = new Control(sdk.controls.Button, 264, 568, 272, 35); - - Control.SinglePlayer = new Control(-1, 264, 324, 272, 35); - Control.BattleNet = new Control(-1, 264, 366, 272, 35); - Control.Gateway = new Control(sdk.controls.Button, 264, 391, 272, 25); - Control.OtherMultiplayer = new Control(-1, 264, 433, 272, 35); - - Control.Login = new Control(-1, 264, 484, 272, 35); - Control.LoginHeading = new Control(sdk.controls.LabelBox, 200, 350, 400, 100); - Control.LoginUsername = new Control(-1, 322, 342, 162, 19); - Control.LoginPassword = new Control(-1, 322, 396, 162, 19); - Control.LoginErrorOk = new Control(sdk.controls.Button, 335, 412, 128, 35); - Control.LoginCancelWait = new Control(sdk.controls.Button, 330, 416, 128, 35); - Control.LoginInvalidCdKey = new Control(sdk.controls.LabelBox, 162, 270, 477, 50); - Control.LoginCdKeyInUseBy = new Control(sdk.controls.LabelBox, 158, 310, 485, 40); - Control.LoginUnableToConnect = new Control(sdk.controls.LabelBox, 158, 220, 485, 40); - Control.LoginErrorText = new Control(sdk.controls.LabelBox, 199, 377, 402, 140); - Control.LoginAccountSettings = new Control(sdk.controls.Button, 264, 528, 272, 35); - Control.LoginExit = new Control(sdk.controls.Button, 33, 572, 128, 35); - - Control.UnableToConnectOk = new Control(sdk.controls.Button, 335, 450, 128, 35); - - Control.OpenBattleNet = new Control(-1, 264, 310, 272, 35); - Control.TcpIp = new Control(-1, 264, 350, 272, 35); - Control.OtherMultiplayerCancel = new Control(sdk.controls.Button, 264, 568, 272, 35); - - Control.GatewayOk = new Control(sdk.controls.Button, 281, 538, 96, 32); - Control.GatewayCancel = new Control(sdk.controls.Button, 436, 538, 96, 32); - - Control.TcpIpHost = new Control(-1, 265, 206, 272, 35); - Control.TcpIpJoin = new Control(-1, 265, 264, 272, 35); - Control.TcpIpCancel = new Control(sdk.controls.Button, 39, 571, 128, 35); - - Control.IPAdress = new Control(-1, 300, 268, -1, -1); - Control.IPAdressOk = new Control(-1, 421, 337, 96, 32); - - Control.CreateNewAccount = new Control(sdk.controls.Button, 264, 572, 272, 35); - Control.CreateNewAccountName = new Control(sdk.controls.TextBox, 322, 342, 162, 19); - Control.CreateNewAccountPassword = new Control(sdk.controls.TextBox, 322, 396, 162, 19); - Control.CreateNewAccountConfirmPassword = new Control(sdk.controls.TextBox, 322, 450, 162, 19); - Control.CreateNewAccountOk = new Control(sdk.controls.Button, 627, 572, 128, 35); - Control.CreateNewAccountExit = new Control(sdk.controls.Button, 33, 572, 128, 35); - - Control.TermsOfUseAgree = new Control(sdk.controls.Button, 525, 513, 128, 35); - Control.TermsOfUseDisagree = new Control(sdk.controls.Button, 133, 513, 128, 35); - - Control.PleaseReadOk = new Control(sdk.controls.Button, 525, 513, 128, 35); - Control.PleaseReadCancel = new Control(sdk.controls.Button, 133, 513, 128, 35); - - Control.EmailSetEmail = new Control(sdk.controls.TextBox, 253, 342, 293, 19); - Control.EmailVerifyEmail = new Control(sdk.controls.TextBox, 253, 396, 293, 19); - Control.EmailRegister = new Control(sdk.controls.Button, 265, 527, 272, 35); - Control.EmailDontRegister = new Control(sdk.controls.Button, 265, 572, 272, 35); - Control.EmailDontRegisterContinue = new Control(sdk.controls.Button, 415, 412, 128, 35); - - Control.CharSelectCreate = new Control(sdk.controls.Button, 33, 528, 168, 60); - Control.CharSelectExit = new Control(sdk.controls.Button, 33, 572, 128, 35); - Control.CharSelectDelete = new Control(sdk.controls.Button, 433, 528, 168, 60); - Control.CharSelectConvert = new Control(sdk.controls.Button, 233, 528, 168, 60); - Control.CharDeleteYes = new Control(sdk.controls.Button, 421, 337, 96, 32); - Control.CharSelectError = new Control(sdk.controls.LabelBox, 45, 318, 531, 140); - Control.CharSelectCharInfo0 = new Control(sdk.controls.LabelBox, 37, 178, 200, 92); - Control.CharSelectChar4 = new Control(sdk.controls.LabelBox, 237, 364, 72, 93); - Control.CharSelectChar6 = new Control(sdk.controls.LabelBox, 237, 457, 72, 93); - Control.CharSelectCurrentRealm = new Control(sdk.controls.LabelBox, 626, 100, 151, 44); - - Control.CharCreateCharName = new Control(sdk.controls.TextBox, 318, 510, 157, 16); - Control.CharCreateExpansion = new Control(sdk.controls.Button, 319, 540, 15, 16); - Control.CharCreateLadder = new Control(sdk.controls.Button, 319, 580, 15, 16); - Control.CharCreateHardcore = new Control(sdk.controls.Button, 319, 560, 15, 16); - Control.CharCreateHCWarningOk = new Control(sdk.controls.Button, 421, 337, 96, 32); - Control.CharCreateHCWarningCancel = new Control(sdk.controls.Button, 281, 337, 96, 32); - - Control.SinglePlayerNormal = new Control(-1, 264, 297, 272, 35); - Control.SinglePlayerNightmare = new Control(-1, 264, 340, 272, 35); - Control.SinglePlayerHell = new Control(-1, 264, 383, 272, 35); - - Control.LobbyCharacterInfo = new Control(sdk.controls.LabelBox, 143, 588, 230, 87); - Control.LobbyEnterChat = new Control(sdk.controls.Button, 27, 480, 120, 20); - Control.LobbyLadder = new Control(sdk.controls.Button, 614, 490, 80, 20); - Control.LobbyHelp = new Control(sdk.controls.Button, 146, 480, 120, 20); - Control.LobbyQuit = new Control(sdk.controls.Button, 693, 490, 80, 20); - - Control.JoinGameWindow = new Control(sdk.controls.Button, 652, 469, 120, 20); - Control.JoinGame = new Control(sdk.controls.Button, 594, 433, 172, 32); - Control.JoinGameName = new Control(sdk.controls.TextBox, 432, 148, 155, 20); - Control.JoinGamePass = new Control(sdk.controls.TextBox, 606, 148, 155, 20); - Control.JoinGameList = new Control(sdk.controls.LabelBox, 432, 393, 160, 173); - Control.JoinGameDetails = new Control(sdk.controls.LabelBox, 609, 393, 143, 194); - Control.CancelJoinGame = new Control(sdk.controls.Button, 433, 433, 96, 32); - - Control.CreateGameWindow = new Control(sdk.controls.Button, 533, 469, 120, 20); - Control.CreateGame = new Control(sdk.controls.Button, 594, 433, 172, 32); - Control.CreateGameName = new Control(sdk.controls.TextBox, 432, 162, 158, 20); - Control.CreateGamePass = new Control(sdk.controls.TextBox, 432, 217, 158, 20); - Control.CharacterDifferenceButton = new Control(sdk.controls.Button, 431, 341, 15, 16); - Control.CharacterDifference = new Control(sdk.controls.TextBox, 657, 342, 27, 20); - Control.MaxPlayerCount = new Control(sdk.controls.TextBox, 657, 308, 27, 20); - Control.Normal = new Control(sdk.controls.Button, 430, 381, 16, 16); - Control.Nightmare = new Control(sdk.controls.Button, 555, 381, 16, 16); - Control.Hell = new Control(sdk.controls.Button, 698, 381, 16, 16); - Control.CreateGameInLine = new Control(sdk.controls.LabelBox, 427, 234, 300, 100); - Control.CancelCreateGame = new Control(sdk.controls.Button, 433, 433, 96, 32); - - Control.LobbyChannel = new Control(sdk.controls.Button, 535, 490, 80, 20); - Control.LobbyChannelName = new Control(sdk.controls.LabelBox, 28, 138, 354, 60); - Control.LobbyChannelText = new Control(sdk.controls.TextBox, 432, 162, 155, 20); - Control.LobbyChannelOk = new Control(sdk.controls.Button, 671, 433, 96, 32); - Control.LobbyChannelCancel = new Control(sdk.controls.Button, 433, 433, 96, 32); - Control.LobbyChannelSend = new Control(sdk.controls.Button, 27, 470, 80, 20); - Control.LobbyChannelWhisper = new Control(sdk.controls.Button, 107, 470, 80, 20); - Control.LobbyChannelSquelch = new Control(sdk.controls.Button, 27, 490, 72, 20); - Control.LobbyChannelUnsquelch = new Control(sdk.controls.Button, 99, 490, 96, 20); - Control.LobbyChannelEmote = new Control(sdk.controls.Button, 195, 490, 72, 20); - Control.LobbyChannelChar0 = new Control(sdk.controls.Button, 40, 591, 60, 100); - Control.LobbyChannelChar1 = new Control(sdk.controls.Button, 100, 591, 60, 100); - Control.LobbyChannelChar2 = new Control(sdk.controls.Button, 160, 591, 60, 100); - Control.LobbyChannelChar3 = new Control(sdk.controls.Button, 220, 591, 60, 100); - Control.LobbyChannelChar4 = new Control(sdk.controls.Button, 280, 591, 60, 100); - Control.LobbyChannelChar5 = new Control(sdk.controls.Button, 340, 591, 60, 100); - Control.LobbyChannelChar6 = new Control(sdk.controls.Button, 400, 591, 60, 100); - Control.LobbyChannelChar7 = new Control(sdk.controls.Button, 460, 591, 60, 100); - Control.LobbyChannelChar8 = new Control(sdk.controls.Button, 520, 591, 60, 100); - Control.LobbyChannelChar9 = new Control(sdk.controls.Button, 580, 591, 60, 100); - Control.LobbyChannelChar10 = new Control(sdk.controls.Button, 640, 591, 60, 100); - - Control.LobbyChat = new Control(sdk.controls.LabelBox, 28, 410, 354, 298); - Control.LobbyServerDown = new Control(sdk.controls.LabelBox, 438, 300, 326, 150); + // Login Menu Controls + { + Control.Login = new Control(-1, 264, 484, 272, 35); + Control.LoginHeading = new Control(sdk.controls.LabelBox, 200, 350, 400, 100); + Control.LoginErrorOk = new Control(sdk.controls.Button, 335, 412, 128, 35); + Control.LoginCancelWait = new Control(sdk.controls.Button, 330, 416, 128, 35); + Control.LoginCdKeyInUseBy = new Control(sdk.controls.LabelBox, 162, 270, 477, 50); + Control.LoginLodKeyInUseBy = new Control(sdk.controls.LabelBox, 158, 310, 485, 40); + Control.LoginInvalidCdKey = new Control(sdk.controls.LabelBox, 4, 162, 320, 477, 100); + Control.LoginUnableToConnect = new Control(sdk.controls.LabelBox, 158, 220, 485, 40); + Control.LoginErrorText = new Control(sdk.controls.LabelBox, 199, 377, 402, 140); + Control.LoginAccountSettings = new Control(sdk.controls.Button, 264, 528, 272, 35); + Control.UnableToConnectOk = new Control(sdk.controls.Button, 335, 450, 128, 35); + } - Control.OkCentered = new Control(sdk.controls.Button, 351, 337, 96, 32); - Control.HellSP = new Control(-1, 264, 383, 272, 35); - Control.NightmareSP = new Control(-1, 264, 340, 272, 35); - Control.NormalSP = new Control(-1, 264, 297, 272, 35); + // Other Multiplayer Menu Controls + { + Control.OpenBattleNet = new Control(-1, 264, 310, 272, 35); + Control.TcpIp = new Control(-1, 264, 350, 272, 35); + Control.OtherMultiplayerCancel = new Control(sdk.controls.Button, 264, 568, 272, 35); + } + + // Gateway Menu Controls + { + Control.GatewayOk = new Control(sdk.controls.Button, 281, 538, 96, 32); + Control.GatewayCancel = new Control(sdk.controls.Button, 436, 538, 96, 32); + } + + // TCP/IP Menu Controls + { + Control.TcpIpHost = new Control(-1, 265, 206, 272, 35); + Control.TcpIpJoin = new Control(-1, 265, 264, 272, 35); + Control.TcpIpCancel = new Control(sdk.controls.Button, 39, 571, 128, 35); + + Control.IPAdress = new Control(-1, 300, 268, -1, -1); + Control.IPAdressOk = new Control(-1, 421, 337, 96, 32); + } + + // Create Account Menu Controls + { + Control.CreateNewAccount = new Control(sdk.controls.Button, 264, 572, 272, 35); + Control.ConfirmPassword = new Control(sdk.controls.TextBox, 322, 450, 162, 19); + } + + // Terms of Use Menu Controls + { + Control.TermsOfUseAgree = new Control(sdk.controls.Button, 525, 513, 128, 35); + Control.TermsOfUseDisagree = new Control(sdk.controls.Button, 133, 513, 128, 35); + + Control.PleaseReadOk = new Control(sdk.controls.Button, 525, 513, 128, 35); + Control.PleaseReadCancel = new Control(sdk.controls.Button, 133, 513, 128, 35); + } + + // Email Menu Controls + { + Control.EmailSetEmail = new Control(sdk.controls.TextBox, 253, 342, 293, 19); + Control.EmailVerifyEmail = new Control(sdk.controls.TextBox, 253, 396, 293, 19); + Control.EmailRegister = new Control(sdk.controls.Button, 265, 527, 272, 35); + Control.EmailDontRegister = new Control(sdk.controls.Button, 265, 572, 272, 35); + Control.EmailDontRegisterContinue = new Control(sdk.controls.Button, 415, 412, 128, 35); + } + + // Character Select Menu Controls + { + Control.CharSelectCreate = new Control(sdk.controls.Button, 33, 528, 168, 60); + Control.CharSelectDelete = new Control(sdk.controls.Button, 433, 528, 168, 60); + Control.CharSelectConvert = new Control(sdk.controls.Button, 233, 528, 168, 60); + Control.CharSelectError = new Control(sdk.controls.LabelBox, 45, 318, 531, 140); + Control.CharSelectCharInfo0 = new Control(sdk.controls.LabelBox, 37, 178, 200, 92); + Control.CharSelectChar4 = new Control(sdk.controls.LabelBox, 237, 364, 72, 93); + Control.CharSelectChar6 = new Control(sdk.controls.LabelBox, 237, 457, 72, 93); + Control.CharSelectCurrentRealm = new Control(sdk.controls.LabelBox, 626, 100, 151, 44); + } + + // Character Create Menu Controls + { + Control.CharCreateCharName = new Control(sdk.controls.TextBox, 318, 510, 157, 16); + Control.CharCreateExpansion = new Control(sdk.controls.Button, 319, 540, 15, 16); + Control.CharCreateLadder = new Control(sdk.controls.Button, 319, 580, 15, 16); + Control.CharCreateHardcore = new Control(sdk.controls.Button, 319, 560, 15, 16); + // these two are the same as popup yes/no, should they be kept seperate or merged? + Control.CharCreateHCWarningOk = new Control(sdk.controls.Button, 421, 337, 96, 32); + Control.CharCreateHCWarningCancel = new Control(sdk.controls.Button, 281, 337, 96, 32); + } + + // Lobby Menu Controls + { + Control.LobbyCharacterInfo = new Control(sdk.controls.LabelBox, 143, 588, 230, 87); + Control.LobbyEnterChat = new Control(sdk.controls.Button, 27, 480, 120, 20); + Control.LobbyChat = new Control(sdk.controls.LabelBox, 28, 410, 354, 298); + Control.LobbyServerDown = new Control(sdk.controls.LabelBox, 438, 300, 326, 150); + Control.LobbyLadder = new Control(sdk.controls.Button, 614, 490, 80, 20); + Control.LobbyHelp = new Control(sdk.controls.Button, 146, 480, 120, 20); + Control.LobbyQuit = new Control(sdk.controls.Button, 693, 490, 80, 20); + } + + // Join Game Menu Controls + { + Control.JoinGameWindow = new Control(sdk.controls.Button, 652, 469, 120, 20); + Control.JoinGame = new Control(sdk.controls.Button, 594, 433, 172, 32); + Control.JoinGameName = new Control(sdk.controls.TextBox, 432, 148, 155, 20); + Control.JoinGamePass = new Control(sdk.controls.TextBox, 606, 148, 155, 20); + Control.JoinGameList = new Control(sdk.controls.LabelBox, 432, 393, 160, 173); + Control.JoinGameDetails = new Control(sdk.controls.LabelBox, 609, 393, 143, 194); + Control.CancelJoinGame = new Control(sdk.controls.Button, 433, 433, 96, 32); + } + + // Create Game Menu Controls + { + Control.CreateGameWindow = new Control(sdk.controls.Button, 533, 469, 120, 20); + Control.CreateGame = new Control(sdk.controls.Button, 594, 433, 172, 32); + Control.CreateGameName = new Control(sdk.controls.TextBox, 432, 162, 158, 20); + Control.CreateGamePass = new Control(sdk.controls.TextBox, 432, 217, 158, 20); + Control.CharacterDifferenceButton = new Control(sdk.controls.Button, 431, 341, 15, 16); + Control.CharacterDifference = new Control(sdk.controls.TextBox, 657, 342, 27, 20); + Control.MaxPlayerCount = new Control(sdk.controls.TextBox, 657, 308, 27, 20); + Control.Normal = new Control(sdk.controls.Button, 430, 381, 16, 16); + Control.Nightmare = new Control(sdk.controls.Button, 555, 381, 16, 16); + Control.Hell = new Control(sdk.controls.Button, 698, 381, 16, 16); + Control.CreateGameInLine = new Control(sdk.controls.LabelBox, 427, 234, 300, 100); + Control.CancelCreateGame = new Control(sdk.controls.Button, 433, 433, 96, 32); + } + + // Channel Menu Controls + { + Control.LobbyChannel = new Control(sdk.controls.Button, 535, 490, 80, 20); + Control.LobbyChannelName = new Control(sdk.controls.LabelBox, 28, 138, 354, 60); + Control.LobbyChannelText = new Control(sdk.controls.TextBox, 432, 162, 155, 20); + Control.LobbyChannelOk = new Control(sdk.controls.Button, 671, 433, 96, 32); + Control.LobbyChannelCancel = new Control(sdk.controls.Button, 433, 433, 96, 32); + Control.LobbyChannelSend = new Control(sdk.controls.Button, 27, 470, 80, 20); + Control.LobbyChannelWhisper = new Control(sdk.controls.Button, 107, 470, 80, 20); + Control.LobbyChannelSquelch = new Control(sdk.controls.Button, 27, 490, 72, 20); + Control.LobbyChannelUnsquelch = new Control(sdk.controls.Button, 99, 490, 96, 20); + Control.LobbyChannelEmote = new Control(sdk.controls.Button, 195, 490, 72, 20); + Control.LobbyChannelChar0 = new Control(sdk.controls.Button, 40, 591, 60, 100); + Control.LobbyChannelChar1 = new Control(sdk.controls.Button, 100, 591, 60, 100); + Control.LobbyChannelChar2 = new Control(sdk.controls.Button, 160, 591, 60, 100); + Control.LobbyChannelChar3 = new Control(sdk.controls.Button, 220, 591, 60, 100); + Control.LobbyChannelChar4 = new Control(sdk.controls.Button, 280, 591, 60, 100); + Control.LobbyChannelChar5 = new Control(sdk.controls.Button, 340, 591, 60, 100); + Control.LobbyChannelChar6 = new Control(sdk.controls.Button, 400, 591, 60, 100); + Control.LobbyChannelChar7 = new Control(sdk.controls.Button, 460, 591, 60, 100); + Control.LobbyChannelChar8 = new Control(sdk.controls.Button, 520, 591, 60, 100); + Control.LobbyChannelChar9 = new Control(sdk.controls.Button, 580, 591, 60, 100); + Control.LobbyChannelChar10 = new Control(sdk.controls.Button, 640, 591, 60, 100); + } + + // Single Player Difficulty Controls + { + Control.HellSP = new Control(-1, 264, 383, 272, 35); + Control.NightmareSP = new Control(-1, 264, 340, 272, 35); + Control.NormalSP = new Control(-1, 264, 297, 272, 35); + } module.exports = Control; })(module); diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 2b04455ee..6e1386686 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -4471,6 +4471,7 @@ Energy: 4069, DoNotMeetLevelReqForThisGame: 5162, CdKeyDisabled: 5199, + CdKeyInUseBy: 5200, OnlyOneInstanceAtATime: 5201, CdKeyIntendedForAnotherProduct: 5202, InvalidPassword: 5207, @@ -4486,13 +4487,14 @@ UsernameIncludedDisallowedwords: 5233, AccountNameAlreadyExist: 5239, UnableToCreateAccount: 5249, + CannotCreateGamesDeadHCChar: 5304, Disconnected: 5347, UnableToIndentifyVersion: 5245, BattlenetNotResponding: 5353, BattlenetNotResponding2: 5354, EnhancedDamage: 10038, LoDKeyDisabled: 10913, - CdKeyInUseBy: 10914, + LodKeyInUseBy: 10914, LoDKeyIntendedForAnotherProduct: 10915, YourPositionInLineIs: 11026, Gateway: 11049, diff --git a/d2bs/kolbot/libs/oog/DataFile.js b/d2bs/kolbot/libs/oog/DataFile.js index ab134113b..8b68a2f2c 100644 --- a/d2bs/kolbot/libs/oog/DataFile.js +++ b/d2bs/kolbot/libs/oog/DataFile.js @@ -10,7 +10,7 @@ includeIfNotIncluded("oog/FileAction.js"); (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); @@ -50,6 +50,7 @@ includeIfNotIncluded("oog/FileAction.js"); } catch (e) { // If we failed, file might be corrupted, so create a new one obj = this.create(); + obj.handle = D2Bot.handle; } if (obj) { @@ -63,7 +64,6 @@ includeIfNotIncluded("oog/FileAction.js"); getStats: function () { let obj = this.getObj(); - return clone(obj); }, diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 0505d04dc..dce3ce7d2 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -46,6 +46,8 @@ declare global { startsWith(a: string): boolean; capitalize(downCase: boolean): string; format(...pairs: Array): string; + padStart(targetLength: number, padString: string): string; + padEnd(targetLength: number, padString: string): string; } interface StringConstructor { @@ -128,6 +130,30 @@ declare global { y2: number; } + class Text extends Hook { + constructor( + text: string, + x: number, + y: number, + color: number, + font: number, + align: number, + automap: boolean, + ClickHandler?: Function, + HoverHandler?: Function + ); + text: string; + /** + * The x coordinate (left) of the Text. + */ + x: number; + + /** + * The y coordinate (top) of the Text. + */ + y: number; + } + class Box extends Hook { constructor(x: number, y: number, xsize: number, ysize: number, color: number, opacity: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); /** diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 8ef762176..b79261695 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -1505,6 +1505,12 @@ declare global { const Active: 2; } + const chestIds: [ + 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 + ]; + // act1 const A1TownFire: 39; const A1Waypoint: 119; @@ -4390,6 +4396,7 @@ declare global { const Energy: 4069; const DoNotMeetLevelReqForThisGame: 5162; const CdKeyDisabled: 5199; + const CdKeyInUseBy: 5200; const OnlyOneInstanceAtATime: 5201; const CdKeyIntendedForAnotherProduct: 5202; const InvalidPassword: 5207; @@ -4405,13 +4412,14 @@ declare global { const UsernameIncludedDisallowedwords: 5233; const AccountNameAlreadyExist: 5239; const UnableToCreateAccount: 5249; + const CannotCreateGamesDeadHCChar: 5304; const Disconnected: 5347; const UnableToIndentifyVersion: 5245; const BattlenetNotResponding: 5353; const BattlenetNotResponding2: 5354; const EnhancedDamage: 10038; const LoDKeyDisabled: 10913; - const CdKeyInUseBy: 10914; + const LodKeyInUseBy: 10914; const LoDKeyIntendedForAnotherProduct: 10915; const YourPositionInLineIs: 11026; const Gateway: 11049; From b2ab409c0327509c36f26e4c348b11f69e0ca7fd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 2 Apr 2023 19:44:26 -0400 Subject: [PATCH 100/758] Update UnitInfo.js - Fix missing index variable --- d2bs/kolbot/libs/modules/UnitInfo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/UnitInfo.js b/d2bs/kolbot/libs/modules/UnitInfo.js index e955eed6a..522083683 100644 --- a/d2bs/kolbot/libs/modules/UnitInfo.js +++ b/d2bs/kolbot/libs/modules/UnitInfo.js @@ -131,7 +131,7 @@ include("core/prototypes.js"); this.hooks.push(new Text("Equipped items:", this.x, this.y + 15, 4, 13, 2)); frameYsize += 15; - items.forEach(item => { + items.forEach((item, i) => { if (item.runeword) { string = item.fname.split("\n")[1] + "ÿc0 " + item.fname.split("\n")[0]; } else { From 1fe5a2cf43a8d1492ceca3469979787bca63b674 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 3 Apr 2023 13:03:23 -0400 Subject: [PATCH 101/758] Update QuestData.js - Add LamEssen quest - Fix checkState to always return boolean value - jsdoc comments --- d2bs/kolbot/libs/core/GameData/QuestData.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/QuestData.js b/d2bs/kolbot/libs/core/GameData/QuestData.js index 3c02bcf2d..a0a0586b1 100644 --- a/d2bs/kolbot/libs/core/GameData/QuestData.js +++ b/d2bs/kolbot/libs/core/GameData/QuestData.js @@ -76,9 +76,9 @@ */ Quest.prototype.checkState = function (state, complete = true) { // handle the ones we already know - if (state === sdk.quest.states.Completed && this.completed) return sdk.quest.states.Completed; - if (state === sdk.quest.states.CannotComplete && this.cannotComplete) return sdk.quest.states.CannotComplete; - if (state === sdk.quest.states.ReqComplete && this.reqComplete) return sdk.quest.states.ReqComplete; + if (state === sdk.quest.states.Completed && this.completed) return complete; + if (state === sdk.quest.states.CannotComplete && this.cannotComplete) return complete; + if (state === sdk.quest.states.ReqComplete && this.reqComplete) return complete; refresh(); let val = me.getQuest(this.id, state); @@ -104,6 +104,7 @@ return this.states; }; + /** @type {Map} */ const questMap = new Map(); [ [ @@ -116,7 +117,7 @@ ], [ sdk.quest.id.AbleToGotoActIII, sdk.quest.id.SpokeToHratli, sdk.quest.id.TheGoldenBird, sdk.quest.id.BladeoftheOldReligion, - sdk.quest.id.KhalimsWill, sdk.quest.id.TheBlackenedTemple, sdk.quest.id.TheGuardian, + sdk.quest.id.LamEsensTome, sdk.quest.id.KhalimsWill, sdk.quest.id.TheBlackenedTemple, sdk.quest.id.TheGuardian, ], [ sdk.quest.id.AbleToGotoActIV, sdk.quest.id.TheFallenAngel, sdk.quest.id.SpokeToTyrael, sdk.quest.id.HellsForge, sdk.quest.id.TerrorsEnd, @@ -132,10 +133,18 @@ }); return { + /** + * @param {number} questId + * @returns {Quest | undefined} + */ get: function (questId) { return questMap.get(questId); }, + /** + * @param {number} questId + * @returns {boolean} + */ has: function (questId) { return questMap.has(questId); }, @@ -146,6 +155,10 @@ console.timeEnd("QuestData.init"); }, + /** + * @param {number} questId + * @returns {number} + */ getActForQuest: function (questId) { return questMap.get(questId).act; }, From fbad3660c3cb1714de63f90e213cb3e7ea91581e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 3 Apr 2023 13:07:29 -0400 Subject: [PATCH 102/758] Clean up me props + Fix `Town.checkQuestItems` - Broke up the define prop list into distinct sections. - Use QuestData module for quest checking - `checkQuestItems` had no checks regarding actual state of quest, this caused it to bug when we had a quest item from a different difficulty. --- d2bs/kolbot/libs/core/Me.js | 385 ++++++++++++++++++---------------- d2bs/kolbot/libs/core/Town.js | 92 ++++---- 2 files changed, 261 insertions(+), 216 deletions(-) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index c53709d79..c178954e6 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -255,13 +255,12 @@ me.getIdTool = function () { me.canTpToTown = function () { // can't tp if dead if (me.dead) 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 (badAreas.includes(me.area)) return false; // If we made it this far, we can only tp if we even have a tp return !!me.getTpTool(); }; @@ -359,11 +358,15 @@ me.switchToPrimary = function () { return me.switchWeapons(Attack.getPrimarySlot()); }; +/** + * Misc functions, stats/modes/states/ etc + */ Object.defineProperties(me, { maxNearMonsters: { get: function () { return Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); - } + }, + configurable: true }, inShop: { get: function () { @@ -393,32 +396,6 @@ Object.defineProperties(me, { return [sdk.player.mode.Walking, sdk.player.mode.Running, sdk.player.mode.WalkingInTown].includes(me.mode); } }, - highestAct: { - get: function () { - let acts = [true, - me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed), - me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed), - me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed), - me.getQuest(sdk.quest.id.AbleToGotoActV, sdk.quest.states.Completed)]; - let index = acts.findIndex((i) => !i); // find first false, returns between 1 and 5 - return index === -1 ? 5 : index; - } - }, - highestQuestDone: { - get: function () { - for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { - if (me.getQuest(i, sdk.quest.states.Completed)) { - return i; - } - - // check if we've completed main part but not used our reward - if ([sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade].includes(i) && me.getQuest(i, sdk.quest.states.ReqComplete)) { - return i; - } - } - return undefined; - } - }, staminaPercent: { get: function () { return Math.round((me.stamina / me.staminamax) * 100); @@ -476,6 +453,12 @@ Object.defineProperties(me, { return me.getState(sdk.states.SkillDelay); } }, +}); + +/** + * Game type, difficulty, classtype, etc + */ +Object.defineProperties(me, { classic: { get: function () { return me.gametype === sdk.game.gametype.Classic; @@ -546,7 +529,12 @@ Object.defineProperties(me, { return me.classid === sdk.player.class.Assassin; } }, - // quest items +}); + +/** + * Quest items + */ +Object.defineProperties(me, { wirtsleg: { get: function () { return me.getItem(sdk.quest.item.WirtsLeg); @@ -612,152 +600,193 @@ Object.defineProperties(me, { return me.getItem(sdk.quest.item.ScrollofResistance); } }, - // quests - den: { - get: function () { - return me.getQuest(sdk.quest.id.DenofEvil, sdk.quest.states.Completed); - } - }, - bloodraven: { - get: function () { - return me.getQuest(sdk.quest.id.SistersBurialGrounds, sdk.quest.states.Completed); - } - }, - smith: { - get: function () { - return me.getQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.Completed); - } - }, - cain: { - get: function () { - return me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - } - }, - tristram: { - get: function () { - // update where this is used and change the state to be portal opened and me.cain to be quest completed - return me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed); - } - }, - countess: { - get: function () { - return me.getQuest(sdk.quest.id.ForgottenTower, sdk.quest.states.Completed); - } - }, - andariel: { - get: function () { - return me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed); - } - }, - radament: { - get: function () { - return me.getQuest(sdk.quest.id.RadamentsLair, sdk.quest.states.Completed); - } - }, - horadricstaff: { - get: function () { - return me.getQuest(sdk.quest.id.TheHoradricStaff, sdk.quest.states.Completed); - } - }, - summoner: { - get: function () { - return me.getQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed); - } - }, - duriel: { - get: function () { - return me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed); - } - }, - goldenbird: { - get: function () { - return me.getQuest(sdk.quest.id.TheGoldenBird, sdk.quest.states.Completed); - } - }, - lamessen: { - get: function () { - return me.getQuest(sdk.quest.id.LamEsensTome, sdk.quest.states.Completed); - } - }, - gidbinn: { - get: function () { - return me.getQuest(sdk.quest.id.BladeoftheOldReligion, sdk.quest.states.Completed); - } - }, - travincal: { - get: function () { - return me.getQuest(sdk.quest.id.KhalimsWill, sdk.quest.states.Completed); - } - }, - mephisto: { - get: function () { - return me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed); - } - }, - izual: { - get: function () { - return me.getQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.Completed); - } - }, - hellforge: { - get: function () { - return me.getQuest(sdk.quest.id.HellsForge, sdk.quest.states.Completed); - } - }, - diablo: { - get: function () { - return me.getQuest(sdk.quest.id.TerrorsEnd, sdk.quest.states.Completed); - } - }, - shenk: { - get: function () { - return me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.Completed); - } - }, - larzuk: { - get: function () { - return me.getQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete); - } - }, - savebarby: { - get: function () { - return me.getQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); - } - }, - barbrescue: { - get: function () { - return me.getQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); - } - }, - anya: { - get: function () { - return me.getQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.Completed); - } - }, - ancients: { - get: function () { - return me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed); - } - }, - baal: { - get: function () { - return me.getQuest(sdk.quest.id.EyeofDestruction, sdk.quest.states.Completed); - } - }, - // Misc - cows: { - get: function () { - return me.getQuest(sdk.quest.id.TheSearchForCain, 10); - } - }, - respec: { - get: function () { - return me.getQuest(sdk.quest.id.Respec, sdk.quest.states.Completed); - } - }, - diffCompleted: { - get: function () { - return !!((me.classic && me.diablo) || me.baal); - } - }, }); + +/** + * Quests + */ +(function () { + const QuestData = require("./GameData/QuestData"); + + Object.defineProperties(me, { + highestAct: { + get: function () { + let acts = [true, + QuestData.get(sdk.quest.id.AbleToGotoActII).complete(), + QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(), + QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(), + QuestData.get(sdk.quest.id.AbleToGotoActV).complete()]; + let index = acts.findIndex((i) => !i); // find first false, returns between 1 and 5 + return index === -1 ? 5 : index; + } + }, + highestQuestDone: { + get: function () { + for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { + if (QuestData.get(i).complete()) { + return i; + } + + // check if we've completed main part but not used our reward + if ([sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade].includes(i) + && QuestData.get(i).complete(true)) { + return i; + } + } + return undefined; + } + }, + den: { + get: function () { + return QuestData.get(sdk.quest.id.DenofEvil).complete(); + } + }, + bloodraven: { + get: function () { + return QuestData.get(sdk.quest.id.SistersBurialGrounds).complete(); + } + }, + smith: { + get: function () { + return QuestData.get(sdk.quest.id.ToolsoftheTrade).complete(); + } + }, + imbue: { + get: function () { + return QuestData.get(sdk.quest.id.ToolsoftheTrade).checkState(sdk.quest.states.ReqComplete, true); + } + }, + cain: { + get: function () { + return QuestData.get(sdk.quest.id.TheSearchForCain).complete(); + } + }, + tristram: { + get: function () { + // update where this is used and change the state to be portal opened and me.cain to be quest completed + return QuestData.get(sdk.quest.id.TheSearchForCain).complete(); + } + }, + countess: { + get: function () { + return QuestData.get(sdk.quest.id.ForgottenTower).complete(); + } + }, + andariel: { + get: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActII).complete(); + } + }, + radament: { + get: function () { + return QuestData.get(sdk.quest.id.RadamentsLair).complete(); + } + }, + horadricstaff: { + get: function () { + return QuestData.get(sdk.quest.id.TheHoradricStaff).complete(); + } + }, + summoner: { + get: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + } + }, + duriel: { + get: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(); + } + }, + goldenbird: { + get: function () { + return QuestData.get(sdk.quest.id.TheGoldenBird).complete(); + } + }, + lamessen: { + get: function () { + return QuestData.get(sdk.quest.id.LamEsensTome).complete(); + } + }, + gidbinn: { + get: function () { + return QuestData.get(sdk.quest.id.BladeoftheOldReligion).complete(); + } + }, + travincal: { + get: function () { + return QuestData.get(sdk.quest.id.KhalimsWill).complete(); + } + }, + mephisto: { + get: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(); + } + }, + izual: { + get: function () { + return QuestData.get(sdk.quest.id.TheFallenAngel).complete(); + } + }, + hellforge: { + get: function () { + return QuestData.get(sdk.quest.id.HellsForge).complete(); + } + }, + diablo: { + get: function () { + return QuestData.get(sdk.quest.id.TerrorsEnd).complete(); + } + }, + shenk: { + get: function () { + return QuestData.get(sdk.quest.id.SiegeOnHarrogath).complete(true); + } + }, + larzuk: { + get: function () { + return QuestData.get(sdk.quest.id.SiegeOnHarrogath).checkState(sdk.quest.states.ReqComplete, true); + } + }, + savebarby: { + get: function () { + return QuestData.get(sdk.quest.id.RescueonMountArreat).complete(); + } + }, + barbrescue: { + get: function () { + return QuestData.get(sdk.quest.id.RescueonMountArreat).complete(); + } + }, + anya: { + get: function () { + return QuestData.get(sdk.quest.id.PrisonofIce).complete(); + } + }, + ancients: { + get: function () { + return QuestData.get(sdk.quest.id.RiteofPassage).complete(); + } + }, + baal: { + get: function () { + return QuestData.get(sdk.quest.id.EyeofDestruction).complete(); + } + }, + // Misc + cows: { + get: function () { + return me.getQuest(sdk.quest.id.TheSearchForCain, 10); + } + }, + respec: { + get: function () { + return QuestData.get(sdk.quest.id.Respec).complete(); + } + }, + diffCompleted: { + get: function () { + return !!((me.classic && me.diablo) || me.baal); + } + }, + }); +})(); diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index e07eb254e..491684403 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -207,6 +207,7 @@ const Town = { // what about finding the closest name in case someone mispells it? let npcKey = Object.keys(NPC).find(key => String.isEqual(key, name)); if (!npcKey) { + // @todo handle if NPC object key is used instead of common name console.warn("Couldn't find " + name + " in NPC object"); return false; } @@ -258,44 +259,60 @@ const Town = { * @description handle quest consumables if we have them */ checkQuestItems: function () { + // Act 1 + // Tools of the trade + if (!me.smith) { + let malus = me.getItem(sdk.items.quest.HoradricMalus); + !!malus && Town.goToTown(1) && Town.npcInteract("charsi"); + } + + // Act 2 // Radament skill book - let book = me.getItem(sdk.quest.item.BookofSkill); - if (book) { - book.isInStash && this.openStash() && delay(300 + me.ping); - book.use(); + if (!me.radament) { + let book = me.getItem(sdk.quest.item.BookofSkill); + if (book) { + book.isInStash && this.openStash() && delay(300 + me.ping); + book.use(); + } } // Act 3 // Figurine -> Golden Bird - if (me.getItem(sdk.quest.item.AJadeFigurine)) { - Town.goToTown(3) && Town.npcInteract("meshif"); - } + if (!me.goldenbird) { + if (me.getItem(sdk.quest.item.AJadeFigurine)) { + Town.goToTown(3) && Town.npcInteract("meshif"); + } - // Golden Bird -> Ashes - if (me.getItem(sdk.items.quest.TheGoldenBird)) { - Town.goToTown(3) && Town.npcInteract("alkor"); - } + // Golden Bird -> Ashes + if (me.getItem(sdk.items.quest.TheGoldenBird)) { + Town.goToTown(3) && Town.npcInteract("alkor"); + } - // Potion of life - let pol = me.getItem(sdk.quest.item.PotofLife); - if (pol) { - pol.isInStash && this.openStash() && delay(300 + me.ping); - pol.use(); + // Potion of life + let pol = me.getItem(sdk.quest.item.PotofLife); + if (pol) { + pol.isInStash && this.openStash() && delay(300 + me.ping); + pol.use(); + } } // LamEssen's Tome - let tome = me.getItem(sdk.quest.item.LamEsensTome); - if (tome) { - !me.inTown && Town.goToTown(3); - tome.isInStash && Town.openStash() && Storage.Inventory.MoveTo(tome); - Town.npcInteract("alkor"); + if (!me.lamessen) { + let tome = me.getItem(sdk.quest.item.LamEsensTome); + if (tome) { + !me.inTown && Town.goToTown(3); + tome.isInStash && Town.openStash() && Storage.Inventory.MoveTo(tome); + Town.npcInteract("alkor"); + } } // Scroll of resistance - let sor = me.getItem(sdk.items.quest.ScrollofResistance); - if (sor) { - sor.isInStash && this.openStash() && delay(300 + me.ping); - sor.use(); + if (!me.anya) { + let sor = me.getItem(sdk.items.quest.ScrollofResistance); + if (sor) { + sor.isInStash && this.openStash() && delay(300 + me.ping); + sor.use(); + } } }, @@ -312,17 +329,22 @@ const Town = { delay(250); + /** @type {NPCUnit} */ let npc = null; + let wantedNpc = this.tasks[me.act - 1][task] !== undefined ? this.tasks[me.act - 1][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) { - if (!justUseClosest && ((npc.name.toLowerCase() !== this.tasks[me.act - 1][task]) + let npcName = npc.name.toLowerCase(); + if (!justUseClosest && ((npcName !== wantedNpc) // Jamella gamble fix - || (task === "Gamble" && npc.name.toLowerCase() === NPC.Jamella))) { + || (task === "Gamble" && npcName === NPC.Jamella))) { me.cancelUIFlags(); npc = null; } @@ -339,12 +361,11 @@ const Town = { .find(unit => [npcs.Shop, npcs.Repair].includes(unit.name.toLowerCase())); } - if (!npc) { - npc = Game.getNPC(this.tasks[me.act - 1][task]); + if (!npc && wantedNpc !== "undefined") { + npc = Game.getNPC(wantedNpc); - if (!npc) { - this.move(this.tasks[me.act - 1][task]); - npc = Game.getNPC(this.tasks[me.act - 1][task]); + if (!npc && this.move(wantedNpc)) { + npc = Game.getNPC(wantedNpc); } } @@ -572,6 +593,7 @@ const Town = { * @returns {[number, number, number, number]} */ checkColumns: function (beltSize) { + (typeof beltSize !== "number" || beltSize < 0 || beltSize > 4) && (beltSize = Storage.BeltSize()); let col = [beltSize, beltSize, beltSize, beltSize]; let pot = me.getItem(-1, sdk.items.mode.inBelt); @@ -1088,21 +1110,15 @@ const Town = { break; } - // npc = !!npc ? npc : Town.lastInteractedNPC.get(); - try { if (!!npc && npc.name.toLowerCase() === NPC[potDealer] && !getUIFlag(sdk.uiflags.Shop)) { if (!npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); } else { me.cancelUIFlags(); - // npc = null; - // Town.lastInteractedNPC.reset(); - Town.move(NPC[potDealer]); npc = Game.getNPC(NPC[potDealer]); if (!npc || !npc.openMenu() || !npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); - // Town.lastInteractedNPC.set(npc); } } catch (e) { console.error(e); From d759bdfd56997fc787bef6119a8e9f95821c3f2d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 3 Apr 2023 16:24:30 -0400 Subject: [PATCH 103/758] Fill out Time namespace more - Moved Time namespace to polyfill, I want it to be available globally. - Add more time related functions to it - `toSeconds` - `toMinutes` - `toHours` - `toDays` - `elapsed` - Fix some of the jsdoc comments in Util - Move type def from Util.d.ts to global.d.ts for the Time namespace --- d2bs/kolbot/libs/Polyfill.js | 31 ++++- d2bs/kolbot/libs/core/Util.js | 207 +++++++++++++++----------------- d2bs/kolbot/sdk/globals.d.ts | 11 ++ d2bs/kolbot/sdk/types/Util.d.ts | 5 - 4 files changed, 136 insertions(+), 118 deletions(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 34451cb09..61eeeb840 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -1,6 +1,6 @@ /** * @filename Polyfill.js -* @author Jaenster (probably) +* @author Jaenster, theBGuy * @desc Some polyfills since we run old spidermonkey (61f7ebb) * */ @@ -1211,3 +1211,32 @@ if (!global.hasOwnProperty("copyObj")) { }, }); } + +const Time = { + seconds: function (seconds = 0) { + if (typeof seconds !== "number") return 0; + return (seconds * 1000); + }, + minutes: function (minutes = 0) { + if (typeof minutes !== "number") return 0; + return (minutes * 60000); + }, + format: function (ms = 0) { + return (new Date(ms).toISOString().slice(11, -5)); + }, + toSeconds: function (ms = 0) { + return (ms / 1000); + }, + toMinutes: function (ms = 0) { + return (ms / 60000); + }, + toHours: function (ms = 0) { + return (ms / 3600000); + }, + toDays: function (ms = 0) { + return (ms / 86400000); + }, + elapsed: function (ms = 0) { + return (getTickCount() - ms); + } +}; diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 0c9cec79d..ad22a7f27 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -32,9 +32,9 @@ ScriptError.prototype.constructor = ScriptError; /** - * get all running threads and return them as an array - * @returns {Script[]} - */ + * get all running threads and return them as an array + * @returns {Script[]} + */ const getThreads = function () { let threads = []; let script = getScript(); @@ -49,9 +49,9 @@ }; /** - * @param {...args} - * @returns {Unit[]} - */ + * @param {...args} + * @returns {Unit[]} + */ const getUnits = function (...args) { let units = [], unit = getUnit.apply(null, args); @@ -65,14 +65,14 @@ }; /** - * @typedef {Object} Args - * @property {0 | 1 | 2} arg1 - where - * @property {number | ItemUnit} arg2 - bodyLoc to click, item to click, x coord - * @property {number} [arg3] - y coord - * @property {number} [arg4] - location - * - * @param {...Args} args - */ + * @typedef {Object} Args + * @property {0 | 1 | 2} arg1 - where + * @property {number | ItemUnit} arg2 - bodyLoc to click, item to click, x coord + * @property {number} [arg3] - y coord + * @property {number} [arg4] - location + * + * @param {...Args} args + */ const clickItemAndWait = function (...args) { let timeout = getTickCount(), timedOut; let before = !me.itemoncursor; @@ -96,13 +96,13 @@ }; /** - * @description clickMap doesn't return if we sucessfully clicked a unit just that a click was sent, this checks and returns that a units mode has changed - * as a result of us clicking it. - * @param {number} button - * @param {0 | 1} shift - * @param {Unit} unit - * @returns {boolean} If a units mode has changed as a result of clicking it - */ + * @description clickMap doesn't return if we sucessfully clicked a unit just that a click was sent, this checks and returns that a units mode has changed + * as a result of us clicking it. + * @param {number} button + * @param {0 | 1} shift + * @param {Unit} unit + * @returns {boolean} If a units mode has changed as a result of clicking it + */ const clickUnitAndWait = function (button, shift, unit) { if (typeof (unit) !== "object") throw new Error("clickUnitAndWait: Third arg must be a Unit."); @@ -132,15 +132,15 @@ }; /** - * @class - * @description new PacketBuilder() - create new packet object - * @example (Spoof 'reassign player' packet to client): - * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); - * @example (Spoof 'player move' packet to server): - * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); - * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` - * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` - */ + * @class + * @description new PacketBuilder() - create new packet object + * @example (Spoof 'reassign player' packet to client): + * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); + * @example (Spoof 'player move' packet to server): + * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); + * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` + * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` + */ function PacketBuilder () { // globals DataView ArrayBuffer if (this.__proto__.constructor !== PacketBuilder) throw new Error("PacketBuilder must be called with 'new' operator!"); @@ -155,7 +155,7 @@ size = arg.length + 1; } - queue.push({type: type, size: size, data: arg}); + queue.push({ type: type, size: size, data: arg }); count += size; }); @@ -342,22 +342,6 @@ return (areaNames[area] || "undefined"); }; - // helper functions in case you find it annoying like me to write while (getTickCount() - tick > 3 * 60 * 1000) which is 3 minutes - // instead we can do while (getTickCount() - tick > Time.minutes(5)) - const Time = { - seconds: function (seconds = 0) { - if (typeof seconds !== "number") return 0; - return (seconds * 1000); - }, - minutes: function (minutes = 0) { - if (typeof minutes !== "number") return 0; - return (minutes * 60000); - }, - format: function (ms = 0) { - return (new Date(ms).toISOString().slice(11, -5)); - } - }; - const Game = { getDistance: function (...args) { switch (args.length) { @@ -392,130 +376,130 @@ } }, /** - * @returns {ItemUnit | undefined} item on cursor - */ + * @returns {ItemUnit | undefined} item on cursor + */ getCursorUnit: function () { return getUnit(100); }, /** - * @returns {ItemUnit | undefined} item cursor is hovering over - */ + * @returns {ItemUnit | undefined} item cursor is hovering over + */ getSelectedUnit: function () { return getUnit(101); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Player} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Player} + */ getPlayer: function (id, mode, gid) { return getUnit(sdk.unittype.Player, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Monster} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Monster} + */ getMonster: function (id, mode, gid) { return getUnit(sdk.unittype.Monster, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Monster} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Monster} + */ getNPC: function (id, mode, gid) { return getUnit(sdk.unittype.NPC, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {ObjectUnit} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {ObjectUnit} + */ getObject: function (id, mode, gid) { return getUnit(sdk.unittype.Object, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Missile} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Missile} + */ getMissile: function (id, mode, gid) { return getUnit(sdk.unittype.Missile, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {ItemUnit} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {ItemUnit} + */ getItem: function (id, mode, gid) { return getUnit(sdk.unittype.Item, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Tile} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Tile} + */ getStairs: function (id, mode, gid) { return getUnit(sdk.unittype.Stairs, id, mode, gid); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ getPresetMonster: function (area, id) { !area && (area = me.area); return getPresetUnit(area, sdk.unittype.Monster, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit[]} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ getPresetMonsters: function (area, id) { !area && (area = me.area); return getPresetUnits(area, sdk.unittype.Monster, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ getPresetObject: function (area, id) { !area && (area = me.area); return getPresetUnit(area, sdk.unittype.Object, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit[]} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ getPresetObjects: function (area, id) { !area && (area = me.area); return getPresetUnits(area, sdk.unittype.Object, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ getPresetStair: function (area, id) { !area && (area = me.area); return getPresetUnit(area, sdk.unittype.Stairs, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit[]} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ getPresetStairs: function (area, id) { !area && (area = me.area); return getPresetUnits(area, sdk.unittype.Stairs, id); @@ -581,7 +565,7 @@ getResponse && addEventListener("copydata", copyDataEvent); - if (!sendCopyData(null, profileName, mode, JSON.stringify({message: message, sender: me.profile}))) { + if (!sendCopyData(null, profileName, mode, JSON.stringify({ message: message, sender: me.profile }))) { //print("sendToProfile: failed to get response from " + profileName); getResponse && removeEventListener("copydata", copyDataEvent); @@ -610,7 +594,6 @@ clickUnitAndWait: clickUnitAndWait, PacketBuilder: PacketBuilder, getAreaName: getAreaName, - Time: Time, Game: Game, Sort: Sort, Messaging: Messaging, diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index dce3ce7d2..f29e07071 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1091,5 +1091,16 @@ declare global { } const Starter: StarterInterface; + + namespace Time { + function seconds(seconds: number): number; + function minutes(minutes: number): number; + function format(ms: number): string; + function toSeconds(ms: number): number; + function toMinutes(ms: number): number; + function toHours(ms: number): number; + function toDays(ms: number): number; + function elapsed(start: number): number; + } } export {}; diff --git a/d2bs/kolbot/sdk/types/Util.d.ts b/d2bs/kolbot/sdk/types/Util.d.ts index 0327d483d..ebeede7b6 100644 --- a/d2bs/kolbot/sdk/types/Util.d.ts +++ b/d2bs/kolbot/sdk/types/Util.d.ts @@ -97,11 +97,6 @@ declare global { const LocalChat: object; const areaNames: string[]; function getAreaName(area: number): string; - namespace Time { - function seconds(seconds: number): number; - function minutes(minutes: number): number; - function format(ms: number): string; - } namespace Game { function getDistance(...args: any[]): number; From 6a774285a66225ba452ea8b11891cbb689bada7d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:27:56 -0400 Subject: [PATCH 104/758] Update Loader.js - Give more info about the script completed - Tweaked the format as well --- d2bs/kolbot/libs/core/Loader.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 7d728b382..b33f3e9f6 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -127,7 +127,7 @@ const Loader = { if (this.skipTown.includes(script) || Town.goToTown()) { print("ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({currScript: script})); + Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); reconfiguration = typeof Scripts[script] === "object"; if (reconfiguration) { @@ -136,6 +136,7 @@ const Loader = { } let tick = getTickCount(); + let exp = me.getStat(sdk.stats.Experience); if (me.inTown) { Config.StackThawingPots.enabled && Town.buyPots(Config.StackThawingPots.quantity, sdk.items.ThawingPotion, true); @@ -149,7 +150,15 @@ const Loader = { } if (global[script]()) { - console.log("ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick))); + let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); + let duration = Time.elapsed(tick); + console.log( + "ÿc7" + script + " :: ÿc0Complete\n" + + "ÿc2 Statistics:\n" + + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" + + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) + ); } } } catch (error) { @@ -207,7 +216,7 @@ const Loader = { let mainScriptStr = (mainScript !== script ? buildScriptMsg() : ""); this.tempList.push(script); print(mainScriptStr + "ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({currScript: script})); + Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); reconfiguration = typeof Scripts[script] === "object"; @@ -222,9 +231,19 @@ const Loader = { } let tick = getTickCount(); + let exp = me.getStat(sdk.stats.Experience); if (global[script]()) { console.log(mainScriptStr + "ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick))); + let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); + let duration = Time.elapsed(tick); + console.log( + mainScriptStr + "ÿc7" + script + " :: ÿc0Complete\n" + + "ÿc2 Statistics:\n" + + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" + + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) + ); } } } catch (error) { From 441335be22bc9fd59d7a2683080cf4d5b6aa6bbc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:55:11 -0400 Subject: [PATCH 105/758] Create SkillData.js - Skill data library, still more to fill out but this organizes the data we have available and makes it easy to check for related info. --- d2bs/kolbot/libs/core/GameData/SkillData.js | 1360 +++++++++++++++++++ 1 file changed, 1360 insertions(+) create mode 100644 d2bs/kolbot/libs/core/GameData/SkillData.js diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js new file mode 100644 index 000000000..6c3a6666f --- /dev/null +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -0,0 +1,1360 @@ +/** +* @filename SkillData.js +* @author theBGuy +* @desc skill data library +* +*/ + + +(function (module) { + /** + * @typedef {Object} SkillInterface + * @property {number} hand + * @property {boolean} [missile] + * @property {number | () => number} range + * @property {number} [state] + * @property {() => boolean} [condition] + * @property {() => number} [summonCount] + */ + + /** @type {Map} */ + const skillMap = new Map(); + // basics + { + skillMap.set(sdk.skills.Attack, { + hand: sdk.skills.hand.LeftNoShift, + range: () => Attack.usingBow() ? 20 : 3, + }); + skillMap.set(sdk.skills.Kick, { + hand: sdk.skills.hand.Right, + range: 3, + }); + skillMap.set(sdk.skills.Throw, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Unsummon, { + hand: sdk.skills.hand.Right, + range: 20, + }); + skillMap.set(sdk.skills.LeftHandThrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.LeftHandSwing, { + hand: sdk.skills.hand.Right, + range: 3, + }); + } + // ~~~ start of amazon skills ~~~ // + { + skillMap.set(sdk.skills.MagicArrow, { + hand: sdk.skills.hand.Left, + missile: true, + }); + skillMap.set(sdk.skills.FireArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.InnerSight, { + hand: sdk.skills.hand.Right, + range: 13, + state: sdk.states.InnerSight, + condition: () => Config.UseInnerSight, + }); + skillMap.set(sdk.skills.CriticalStrike, { + hand: -1, + range: -1, + state: sdk.states.CriticalStrike, + }); + skillMap.set(sdk.skills.Jab, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ColdArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.MultipleShot, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Dodge, { + hand: -1, + range: -1, + state: sdk.states.Dodge, + }); + skillMap.set(sdk.skills.PowerStrike, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.PoisonJavelin, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.ExplodingArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.SlowMissiles, { + hand: sdk.skills.hand.Right, + range: 13, + state: sdk.states.SlowMissiles, + condition: () => Config.UseSlowMissiles, + }); + skillMap.set(sdk.skills.Avoid, { + hand: -1, + range: -1, + state: sdk.states.Avoid, + }); + skillMap.set(sdk.skills.Impale, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.LightningBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.IceArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.GuidedArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Penetrate, { + hand: -1, + range: -1, + state: sdk.states.Penetrate, + }); + skillMap.set(sdk.skills.ChargedStrike, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.PlagueJavelin, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Strafe, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.ImmolationArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Decoy, { + hand: sdk.skills.hand.Right, + range: 30, + duration: () => ((10 + me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints) * 5)), + condition: () => Config.UseDecoy, + }); + skillMap.set(sdk.skills.Evade, { + hand: -1, + range: -1, + state: sdk.states.Evade, + }); + skillMap.set(sdk.skills.Fend, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.FreezingArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Valkyrie, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 1, + condition: () => Config.UseValkyrie, + }); + skillMap.set(sdk.skills.Pierce, { + hand: -1, + range: -1, + state: sdk.states.Pierce, + }); + skillMap.set(sdk.skills.LightningStrike, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.LightningFury, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + } + // ~~~ start of sorc skills ~~~ // + { + skillMap.set(sdk.skills.FireBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Warmth, { + hand: -1, + range: -1, + state: sdk.states.Warmth, + }); + skillMap.set(sdk.skills.ChargedBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 10, + }); + skillMap.set(sdk.skills.IceBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.FrozenArmor, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => ( + ((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) + + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) + ), + }); + skillMap.set(sdk.skills.Inferno, { + hand: sdk.skills.hand.Left, + missile: true, + range: ((17 + (me.getSkill(sdk.skills.Inferno, sdk.skills.subindex.SoftPoints) * 3) / 4) * 2 / 3), + }); + skillMap.set(sdk.skills.StaticField, { + hand: sdk.skills.hand.Right, + range: () => Math.floor((me.getSkill(sdk.skills.StaticField, sdk.skills.subindex.SoftPoints) + 4) * 2 / 3), + }); + skillMap.set(sdk.skills.Telekinesis, { + hand: sdk.skills.hand.Right, + range: 40, + condition: () => Config.UseTelekinesis, + }); + skillMap.set(sdk.skills.FrostNova, { + hand: sdk.skills.hand.Right, + range: 5, + }); + skillMap.set(sdk.skills.IceBlast, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Blaze, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.FireBall, { + hand: sdk.skills.hand.Left, + missile: true, + range: (pvpRange) => pvpRange ? 40 : 20, + }); + skillMap.set(sdk.skills.Nova, { + hand: sdk.skills.hand.Right, + range: 7, + }); + skillMap.set(sdk.skills.Lightning, { + hand: sdk.skills.hand.Left, + missile: true, + range: 25, + }); + skillMap.set(sdk.skills.ShiverArmor, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => ( + ((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) + + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) + ), + }); + skillMap.set(sdk.skills.FireWall, { + hand: sdk.skills.hand.Right, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.Enchant, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Enchant, + }); + skillMap.set(sdk.skills.ChainLightning, { + hand: sdk.skills.hand.Left, + missile: true, + range: 25, + }); + skillMap.set(sdk.skills.Teleport, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.GlacialSpike, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Meteor, { + hand: sdk.skills.hand.Right, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.ThunderStorm, { + hand: sdk.skills.hand.Right, + range: 1, + townSkill: true, + duration: () => (24 + (8 * me.getSkill(sdk.skills.ThunderStorm, sdk.skills.subindex.SoftPoints))), + }); + skillMap.set(sdk.skills.EnergyShield, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.EnergyShield, + duration: () => (84 + (60 * me.getSkill(sdk.skills.EnergyShield, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseEnergyShield, + }); + skillMap.set(sdk.skills.Blizzard, { + hand: sdk.skills.hand.Right, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.ChillingArmor, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => ( + ((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) + + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) + ), + }); + skillMap.set(sdk.skills.FireMastery, { + hand: -1, + range: -1, + state: sdk.states.FireMastery, + }); + skillMap.set(sdk.skills.Hydra, { + hand: sdk.skills.hand.Right, + range: 30, + }); + skillMap.set(sdk.skills.LightningMastery, { + hand: -1, + range: -1, + state: sdk.states.LightningMastery, + }); + skillMap.set(sdk.skills.FrozenOrb, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.ColdMastery, { + hand: -1, + range: -1, + state: sdk.states.ColdMastery, + }); + } + // ~~~ start of necro skills ~~~ // + { + skillMap.set(sdk.skills.AmplifyDamage, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.AmplifyDamage, + }); + skillMap.set(sdk.skills.Teeth, { + hand: sdk.skills.hand.Left, + missile: true, + range: 15, + }); + skillMap.set(sdk.skills.BoneArmor, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.SkeletonMastery, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.RaiseSkeleton, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => { + let skillNum = me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.SoftPoints); + return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); + }, + }); + skillMap.set(sdk.skills.DimVision, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.DimVision, + }); + skillMap.set(sdk.skills.Weaken, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Weaken, + }); + skillMap.set(sdk.skills.PoisonDagger, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.CorpseExplosion, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.ClayGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.IronMaiden, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.IronMaiden, + }); + skillMap.set(sdk.skills.Terror, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Terror, + }); + skillMap.set(sdk.skills.BoneWall, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.GolemMastery, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.RaiseSkeletalMage, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => { + let skillNum = me.getSkill(sdk.skills.RaiseSkeletalMage, sdk.skills.subindex.SoftPoints); + return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); + }, + }); + skillMap.set(sdk.skills.Confuse, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Confuse, + }); + skillMap.set(sdk.skills.LifeTap, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.LifeTap, + }); + skillMap.set(sdk.skills.PoisonExplosion, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.BoneSpear, { + hand: sdk.skills.hand.Left, + missile: true, + range: (pvpRange) => pvpRange ? 35 : 25, + }); + skillMap.set(sdk.skills.BloodGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Attract, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Attract, + }); + skillMap.set(sdk.skills.Decrepify, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Decrepify, + }); + skillMap.set(sdk.skills.BonePrison, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.SummonResist, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.IronGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.LowerResist, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.LowerResist, + }); + skillMap.set(sdk.skills.PoisonNova, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.Poison, + }); + skillMap.set(sdk.skills.BoneSpirit, { + hand: sdk.skills.hand.Left, + missile: true, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.FireGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Revive, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints), + }); + } + // ~~~ start of paladin skills ~~~ // + { + skillMap.set(sdk.skills.Sacrifice, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Smite, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Might, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Might, + }); + skillMap.set(sdk.skills.Prayer, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Prayer, + }); + skillMap.set(sdk.skills.ResistFire, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.ResistFire, + }); + skillMap.set(sdk.skills.HolyBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.HolyFire, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.HolyFire, + }); + skillMap.set(sdk.skills.Thorns, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Thorns, + }); + skillMap.set(sdk.skills.Defiance, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Defiance, + }); + skillMap.set(sdk.skills.ResistCold, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.ResistCold, + }); + skillMap.set(sdk.skills.Zeal, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Charge, { + hand: sdk.skills.hand.Left, + range: 10, + condition: () => Config.Charge, + }); + skillMap.set(sdk.skills.BlessedAim, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.BlessedAim, + }); + skillMap.set(sdk.skills.Cleansing, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Cleansing, + }); + skillMap.set(sdk.skills.ResistLightning, { + range: 1, + state: sdk.states.ResistLightning, + }); + skillMap.set(sdk.skills.Vengeance, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.BlessedHammer, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.Concentration, { + range: 1, + state: sdk.states.Concentration, + }); + skillMap.set(sdk.skills.HolyFreeze, { + range: 1, + state: sdk.states.HolyFreeze, + }); + skillMap.set(sdk.skills.Vigor, { + range: 1, + state: sdk.states.Stamina, + condition: () => Config.Vigor || me.inTown, + }); + skillMap.set(sdk.skills.Conversion, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.HolyShield, { + range: 1, + state: sdk.states.HolyShield, + duration: () => (5 + (25 * me.getSkill(sdk.skills.HolyShield, sdk.skills.subindex.SoftPoints))), + }); + skillMap.set(sdk.skills.HolyShock, { + range: 1, + state: sdk.states.HolyShock, + }); + skillMap.set(sdk.skills.Sanctuary, { + range: 1, + state: sdk.states.Sanctuary, + }); + skillMap.set(sdk.skills.Meditation, { + range: 1, + state: sdk.states.Meditation, + }); + skillMap.set(sdk.skills.FistoftheHeavens, { + hand: sdk.skills.hand.Left, + range: 30, + }); + skillMap.set(sdk.skills.Fanaticism, { + range: 1, + state: sdk.states.Fanaticism, + }); + skillMap.set(sdk.skills.Conviction, { + range: 1, + state: sdk.states.Conviction, + }); + skillMap.set(sdk.skills.Redemption, { + range: 1, + state: sdk.states.Redemption, + }); + skillMap.set(sdk.skills.Salvation, { + range: 1, + state: sdk.states.ResistAll, + }); + } + // ~~~ start of barbarian skills ~~~ // + { + skillMap.set(sdk.skills.Bash, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.SwordMastery, { + hand: -1, + range: -1, + state: sdk.states.SwordMastery, + }); + skillMap.set(sdk.skills.AxeMastery, { + hand: -1, + range: -1, + state: sdk.states.AxeMastery, + }); + skillMap.set(sdk.skills.MaceMastery, { + hand: -1, + range: -1, + state: sdk.states.MaceMastery, + }); + skillMap.set(sdk.skills.Howl, { + range: 5, + state: sdk.states.Terror, + }); + skillMap.set(sdk.skills.FindPotion, { + hand: sdk.skills.hand.RightShift, + range: 30, + }); + skillMap.set(sdk.skills.Leap, { + hand: sdk.skills.hand.Left, + range: () => { + let skLvl = me.getSkill(sdk.skills.Leap, sdk.skills.subindex.SoftPoints); + return Math.floor(Math.min(4 + (26 * ((110 * skLvl / (skLvl + 6)) / 100)), 30) * (2 / 3)); + }, + }); + skillMap.set(sdk.skills.DoubleSwing, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.PoleArmMastery, { + hand: -1, + range: -1, + state: sdk.states.PoleArmMastery, + }); + skillMap.set(sdk.skills.ThrowingMastery, { + hand: -1, + range: -1, + state: sdk.states.ThrowingMastery, + }); + skillMap.set(sdk.skills.SpearMastery, { + hand: -1, + range: -1, + state: sdk.states.SpearMastery, + }); + skillMap.set(sdk.skills.Taunt, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Taunt, + }); + skillMap.set(sdk.skills.Shout, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.Shout, + duration: () => ( + ((10 + me.getSkill(sdk.skills.Shout, sdk.skills.subindex.SoftPoints) * 10) + + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) + ), + }); + skillMap.set(sdk.skills.Stun, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Stunned, + }); + skillMap.set(sdk.skills.DoubleThrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.IncreasedStamina, { + hand: -1, + range: -1, + state: sdk.states.IncreasedStamina, + }); + skillMap.set(sdk.skills.FindItem, { + hand: sdk.skills.hand.RightShift, + range: 30, + condition: () => Config.FindItem, + }); + skillMap.set(sdk.skills.LeapAttack, { + hand: sdk.skills.hand.Left, + range: 10, + }); + skillMap.set(sdk.skills.Concentrate, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Concentrate, + }); + skillMap.set(sdk.skills.IronSkin, { + hand: -1, + range: -1, + state: sdk.states.IronSkin, + }); + skillMap.set(sdk.skills.BattleCry, { + hand: sdk.skills.hand.Right, + range: 5, + state: sdk.states.BattleCry, + duration: () => ( + (9.6 + me.getSkill(sdk.skills.BattleCry, sdk.skills.subindex.SoftPoints) * 2.4) + ), + }); + skillMap.set(sdk.skills.Frenzy, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Frenzy, + }); + skillMap.set(sdk.skills.IncreasedSpeed, { + hand: -1, + range: -1, + state: sdk.states.IncreasedSpeed, + }); + skillMap.set(sdk.skills.BattleOrders, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.BattleOrders, + duration: () => ( + ((20 + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) * 10) + + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) + ), + }); + skillMap.set(sdk.skills.GrimWard, { + hand: sdk.skills.hand.RightShift, + range: 40, + state: sdk.states.Terror, + duration: () => 40, + }); + skillMap.set(sdk.skills.Whirlwind, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.Berserk, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Berserk, + }); + skillMap.set(sdk.skills.NaturalResistance, { + hand: -1, + range: -1, + state: sdk.states.NaturalResistance, + }); + skillMap.set(sdk.skills.WarCry, { + hand: sdk.skills.hand.Right, + range: 5, + state: sdk.states.Stunned, + duration: () => ( + Math.min(0.8 + me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.SoftPoints) * 0.2, 10) + ), + }); + skillMap.set(sdk.skills.BattleCommand, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.BattleCommand, + duration: () => ( + ((10 * me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.SoftPoints) - 5) + + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints)) * 5)) + ), + }); + } + // misc skills - scrolls and books + { + skillMap.set(sdk.skills.IdentifyScroll, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.BookofIdentify, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.TownPortalScroll, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.BookofTownPortal, { + hand: sdk.skills.hand.Right, + range: 1, + }); + } + // ~~~ start of druid skills ~~~ // + { + skillMap.set(sdk.skills.Raven, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => Math.min(me.getSkill(sdk.skills.Raven, sdk.skills.subindex.SoftPoints), 5), + condition: () => Config.SummonRaven, + }); + skillMap.set(sdk.skills.PoisonCreeper, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonVine === "string" + // ? Config.SummonVine.toLowerCase() === "poison" + // : Config.SummonVine === sdk.skills.PoisonCreeper), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Werewolf, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)), + }); + skillMap.set(sdk.skills.Lycanthropy, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.Firestorm, { + hand: sdk.skills.hand.Left, + missile: true, + range: 10, + }); + skillMap.set(sdk.skills.OakSage, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.OakSage, + // condition: () => (typeof Config.SummonSpirit === "string" + // ? Config.SummonSpirit.toLowerCase() === "oak" + // : Config.SummonSpirit === sdk.skills.OakSage), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.SpiritWolf, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonAnimal === "string" + // ? Config.SummonAnimal.toLowerCase() === "spirit wolf" + // : Config.SummonAnimal === sdk.skills.SpiritWolf), + summonCount: () => Math.min(me.getSkill(sdk.skills.SpiritWolf, sdk.skills.subindex.SoftPoints), 5), + }); + skillMap.set(sdk.skills.Werebear, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)), + }); + skillMap.set(sdk.skills.MoltenBoulder, { + hand: sdk.skills.hand.Left, + missile: true, + range: 10, + }); + skillMap.set(sdk.skills.ArcticBlast, { + hand: sdk.skills.hand.Left, + missile: true, + range: () => { + let skLvl = me.getSkill(sdk.skills.ArcticBlast, sdk.skills.subindex.SoftPoints); + let range = Math.floor(((33 + (2 * skLvl)) / 4) * (2 / 3)); + // Druid using this on physical immunes needs the monsters to be within range of hurricane + range > 6 && Config.AttackSkill[5] === sdk.skills.ArcticBlast && (range = 6); + + return range; + }, + }); + skillMap.set(sdk.skills.CarrionVine, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonVine === "string" + // ? Config.SummonVine.toLowerCase() === "carion" + // : Config.SummonVine === sdk.skills.CarrionVine), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.FeralRage, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.FeralRage, + }); + skillMap.set(sdk.skills.Maul, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Maul, + }); + skillMap.set(sdk.skills.Fissure, { + hand: sdk.skills.hand.Right, + range: 20, + }); + skillMap.set(sdk.skills.CycloneArmor, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.CycloneArmor, + // todo - armor percent like I did for bonearmor + }); + skillMap.set(sdk.skills.HeartofWolverine, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.HeartofWolverine, + // condition: () => (typeof Config.SummonSpirit === "string" + // ? Config.SummonSpirit.toLowerCase() === "wolverine" + // : Config.SummonSpirit === sdk.skills.HeartofWolverine), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.SummonDireWolf, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonAnimal === "string" + // ? Config.SummonAnimal.toLowerCase() === "dire wolf" + // : Config.SummonAnimal === sdk.skills.SummonDireWolf), + summonCount: () => Math.min(me.getSkill(sdk.skills.SummonDireWolf, sdk.skills.subindex.SoftPoints), 3), + }); + skillMap.set(sdk.skills.Rabies, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Rabies, + }); + skillMap.set(sdk.skills.FireClaws, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Twister, { + hand: sdk.skills.hand.Left, + missile: true, + range: 5, + }); + skillMap.set(sdk.skills.SolarCreeper, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonVine === "string" + // ? Config.SummonVine.toLowerCase() === "solar" + // : Config.SummonVine === sdk.skills.SolarCreeper), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Hunger, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ShockWave, { + hand: sdk.skills.hand.Left, + range: 8, + }); + skillMap.set(sdk.skills.Volcano, { + hand: sdk.skills.hand.Right, + range: 30, + }); + skillMap.set(sdk.skills.Tornado, { + hand: sdk.skills.hand.Left, + missile: true, + range: 5, + }); + skillMap.set(sdk.skills.SpiritofBarbs, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Barbs, + // condition: () => (typeof Config.SummonSpirit === "string" + // ? Config.SummonSpirit.toLowerCase() === "barbs" + // : Config.SummonSpirit === sdk.skills.SpiritofBarbs), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.SummonGrizzly, { + hand: sdk.skills.hand.Right, + range: 40, + timed: true, + // condition: () => (typeof Config.SummonAnimal === "string" + // ? Config.SummonAnimal.toLowerCase() === "grizzly" + // : Config.SummonAnimal === sdk.skills.SummonGrizzly), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Fury, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Armageddon, { + hand: sdk.skills.hand.Right, + range: 10, + state: sdk.states.Armageddon, + duration: () => (10 + me.getSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints) * 2), + }); + skillMap.set(sdk.skills.Hurricane, { + hand: sdk.skills.hand.Right, + range: 10, + state: sdk.states.Hurricane, + duration: () => (10 + me.getSkill(sdk.skills.CycloneArmor, sdk.skills.subindex.HardPoints) * 2), + }); + } + // ~~~ start of assassin skills ~~~ // + { + skillMap.set(sdk.skills.FireBlast, { + hand: sdk.skills.hand.Left, + missile: true, + range: 15, + }); + skillMap.set(sdk.skills.ClawMastery, { + hand: -1, + range: -1, + state: sdk.states.ClawMastery, + }); + skillMap.set(sdk.skills.PsychicHammer, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.TigerStrike, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonTalon, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ShockWeb, { + hand: sdk.skills.hand.Left, + range: 15, + }); + skillMap.set(sdk.skills.BladeSentinel, { + hand: sdk.skills.hand.Left, + range: 15, + }); + skillMap.set(sdk.skills.BurstofSpeed, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.BurstofSpeed, + duration: () => (108 + (12 * me.getSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.SoftPoints))), + condition: () => !Config.UseBoS && !me.inTown, + }); + skillMap.set(sdk.skills.FistsofFire, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonClaw, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ChargedBoltSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.WakeofFireSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.WeaponBlock, { + hand: -1, + range: -1, + state: sdk.states.WeaponBlock, + }); + skillMap.set(sdk.skills.CloakofShadows, { + hand: sdk.skills.hand.Right, + range: 30, + state: sdk.states.CloakofShadows, + duration: () => (7 + me.getSkill(sdk.skills.CloakofShadows, sdk.skills.subindex.SoftPoints)), + }); + skillMap.set(sdk.skills.CobraStrike, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.BladeFury, { + hand: sdk.skills.hand.Left, + range: 15, + }); + skillMap.set(sdk.skills.Fade, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Fade, + duration: () => (108 + (12 * me.getSkill(sdk.skills.Fade, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseFade, + }); + skillMap.set(sdk.skills.ShadowWarrior, { + hand: sdk.skills.hand.Right, + range: 30, + // condition: () => Config.UseValkyrie, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.ClawsofThunder, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonTail, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.LightningSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.InfernoSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.MindBlast, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.BladesofIce, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonFlight, { + hand: sdk.skills.hand.Left, + range: 10, + }); + skillMap.set(sdk.skills.DeathSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.BladeShield, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.BladeShield, + timed: true, + duration: () => (15 + (5 * me.getSkill(sdk.skills.BladeShield, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseBladeShield, + }); + skillMap.set(sdk.skills.Venom, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Venom, + duration: () => (116 + (4 * me.getSkill(sdk.skills.Venom, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseVenom, + }); + skillMap.set(sdk.skills.ShadowMaster, { + hand: sdk.skills.hand.Right, + range: 30, + // condition: () => Config.UseValkyrie, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.RoyalStrike, { + hand: sdk.skills.hand.Right, + range: 3, + }); + } + + const damageTypes = [ + "Physical", "Fire", "Lightning", + "Magic", "Cold", "Poison", "None", + "None", "None", "Physical" + ]; + + /** + * @constructor + * @param {number} skillId + */ + function Skill (skillId) { + let _skillData = skillMap.get(skillId); + /** @type {number} */ + this.skillId = skillId; + /** @type {number} */ + this.hand = (_skillData.hand || sdk.skills.hand.Right); + /** @type {number} */ + this.state = (_skillData.state || sdk.states.None); + /** @type {() => number} */ + this.summonCount = (_skillData.summonCount || (() => 0)); + /** @type {() => boolean} */ + this.condition = (_skillData.condition || (() => true)); + /** @type {boolean} */ + this.townSkill = (_skillData.townSkill || getBaseStat("skills", skillId, "InTown") === 1); + /** @type {boolean} */ + this.timed = (_skillData.timed || getBaseStat("skills", skillId, "delay") > 0); + /** @type {boolean} */ + this.missleSkill = (_skillData.missile || false); + /** @type {number} */ + this.charClass = getBaseStat("skills", skillId, "charClass"); + /** @type {number} */ + this.reqLevel = getBaseStat("skills", skillId, "reqlevel"); + /** @type {number[]} */ + this.preReqs = (() => { + let preReqs = []; + + for (let t = sdk.stats.PreviousSkillLeft; t >= sdk.stats.PreviousSkillRight; t--) { + let preReq = (getBaseStat("skills", skillId, t)); + + if (preReq > sdk.skills.Attack && preReq < sdk.skills.RoyalStrike) { + return preReqs.push(preReq); + } + } + + return preReqs; + })(); + this.damageType = damageTypes[getBaseStat("skills", skillId, "EType")]; + + /** + * @private + * @type {number | () => number} + */ + this._range = (_skillData.range || 1); + /** + * @private + * @type {() => number} + */ + this._duration = (_skillData.duration || (() => 0)); + /** + * @private + * @type {number} + */ + this._manaCost = Infinity; + /** + * @private + * @type {number} + */ + this._mana = getBaseStat("skills", this.skillId, "mana"); + /** + * @private + * @type {number} + */ + this._minMana = getBaseStat("skills", this.skillId, "minmana"); + /** + * @private + * @type {number} + */ + this._lvlMana = getBaseStat("skills", this.skillId, "lvlmana"); + let effectiveShift = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]; + /** + * @private + * @type {number} + */ + this._manaShift = (effectiveShift[getBaseStat("skills", this.skillId, "manashift")] / 256); + /** + * @private + * @type {number} + */ + this._bestSlot = 0; + /** + * @private + * @type {number} + */ + this._dmg = 0; + /** + * @private + * @type {number} + */ + this._hardPoints = 0; + /** + * @private + * @type {number} + */ + this._softPoints = 0; + /** + * @private + * @type {boolean} + */ + this._checked = false; + } + + /** + * @this Skill + * @returns {number} + */ + Skill.prototype.duration = function () { + return Time.seconds(this._duration()); + }; + + /** + * @this Skill + * @returns {number} + */ + Skill.prototype.manaCost = function () { + if (this._manaCost !== Infinity) return this._manaCost; + if (this.skillId < sdk.skills.MagicArrow) { + return (this._manaCost = 0); + } + let skillLvl = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints); + if (skillLvl !== this._softPoints) { + this._softPoints = skillLvl; + this._hardPoints = me.getSkill(this.skillId, sdk.skills.subindex.HardPoints); + } + // Decoy wasn't reading from skill bin + if (this.skillId === sdk.skills.Decoy) { + return (this._manaCost = Math.max(19.75 - (0.75 * skillLvl), 1)); + } + let lvlmana = this._lvlMana === 65535 + ? -1 + : this._lvlMana; // Correction for skills that need less mana with levels (kolton) + let ret = Math.max((this._mana + lvlmana * (skillLvl - 1)) * this._manaShift, this._minMana); + return (this._manaCost = ret); + }; + + /** + * @this Skill + * @property {boolean} pvpRange + * @returns {number} + */ + Skill.prototype.range = function (pvpRange = false) { + return typeof this._range === "function" ? this._range(pvpRange) : this._range; + }; + + /** + * @this Skill + * @returns {boolean} + */ + Skill.prototype.have = function () { + if (!this.condition()) return false; + if (this._hardPoints > 0) return true; + if (!this._checked) { + this._checked = true; + this._hardPoints = me.getSkill(this.skillId, sdk.skills.subindex.HardPoints); + // this._softPoints = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints); + } + return this._hardPoints > 0 || (this._softPoints = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints)) > 0; + }; + + Skill.prototype.reset = function () { + this._manaCost = Infinity; + this._dmg = 0; + this._hardPoints = 0; + this._softPoints = 0; + this._checked = false; + }; + + /** + * @todo Damage calculations, best slot, etc. + */ + + /** @type {Map Date: Thu, 6 Apr 2023 16:56:32 -0400 Subject: [PATCH 106/758] Update Skill.js - complete refactor to use the SkillData module --- d2bs/kolbot/libs/core/Skill.js | 576 ++++++--------------------------- 1 file changed, 101 insertions(+), 475 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index bc8d4dd59..31b14df10 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -1,65 +1,20 @@ /** * @filename Skill.js -* @author kolton, theBGuy +* @author theBGuy +* @credit kolton * @desc Skill library * */ -(function() { - /** - * @constructor - * @param {number} skillId - */ - function SkillData (skillId) { - /** @type {boolean} */ - this.hardpoints = false; - /** @type {boolean} */ - this.checked = false; - /** @type {number} */ - this.manaCost = null; - /** @type {number} */ - this.lvlReq = null; - // this part feels kinda ugly to me, todo is figure out a better method for conditon - /** @type {Function} */ - this.condition = typeof skillConditions[skillId] === "function" - ? skillConditions[skillId] - : () => true; - } +(function () { + const _SkillData = require("./GameData/SkillData"); /** - * @param {number} skill + * @todo Move some of the precast functions here */ - SkillData.prototype.have = function (skill) { - if (!this.condition()) return false; - if (this.hardpoints) return true; - if (!this.checked) { - this.hardpoints = !!me.getSkill(skill, sdk.skills.subindex.HardPoints); - this.checked = true; - } - - return (this.hardpoints || me.getSkill(skill, sdk.skills.subindex.SoftPoints)); - }; - - const skillConditions = {}; - skillConditions[sdk.skills.InnerSight] = () => Config.UseInnerSight; - skillConditions[sdk.skills.SlowMissiles] = () => Config.UseSlowMissiles; - skillConditions[sdk.skills.Decoy] = () => Config.UseDecoy; - skillConditions[sdk.skills.Valkyrie] = () => Config.SummonValkyrie; - skillConditions[sdk.skills.Telekinesis] = () => Config.UseTelekinesis; - skillConditions[sdk.skills.EnergyShield] = () => Config.UseEnergyShield; - skillConditions[sdk.skills.Charge] = () => Config.Charge; - skillConditions[sdk.skills.Vigor] = () => Config.Vigor || me.inTown; - skillConditions[sdk.skills.FindItem] = () => Config.FindItem; - skillConditions[sdk.skills.Raven] = () => Config.SummonRaven; - skillConditions[sdk.skills.BurstofSpeed] = () => !Config.UseBoS && !me.inTown; - skillConditions[sdk.skills.Fade] = () => Config.UseFade; - skillConditions[sdk.skills.BladeShield] = () => Config.UseBladeShield; - skillConditions[sdk.skills.Venom] = () => Config.UseVenom; - const Skill = { usePvpRange: false, charges: [], - manaCostList: {}, needFloor: [ sdk.skills.Blizzard, sdk.skills.Meteor, sdk.skills.Fissure, sdk.skills.Volcano, sdk.skills.ShockWeb, sdk.skills.LeapAttack, sdk.skills.Hydra ], @@ -93,48 +48,16 @@ } }, - skills: { - /** - * @type {Object.} - */ - all: {}, - initialized: false, - - init: function () { - // should all skills for all classes be initialized? We really only care about ours? - for (let i = sdk.player.class.Amazon; i <= sdk.player.class.Assassin; i++) { - let [min, max] = Skill.getClassSkillRange(i); - - for (let skill = min; skill <= max; skill++) { - // should this just be on the Skill object itself? - Skill.skills.all[skill] = new SkillData(skill); - } - } - - Skill.skills.initialized = true; - }, - have: function (skill = 0) { - // ensure the values have been initialized - !this.initialized && this.init(); - return typeof this.all[skill] !== "undefined" && this.all[skill].have(skill); - }, - reset: function () { + // initialize our skill data + init: function () { + // reset check values + { let [min, max] = Skill.getClassSkillRange(); for (let i = min; i <= max; i++) { - if (typeof this.all[i] !== "undefined" && !this.all[i].hardpoints) { - this.all[i].checked = false; - } + _SkillData.get(i).reset(); } } - }, - - // initialize our skill data - init: function () { - // reset check values - !Skill.skills.initialized ? Skill.skills.init() : Skill.skills.reset(); - // reset mana values - Skill.manaCostList = {}; // redo cta check Precast.checkCTA(); @@ -203,15 +126,9 @@ * @returns {boolean} */ canUse: function (skillId = -1) { - try { - if (skillId === -1) return false; - if (skillId >= sdk.skills.Attack && skillId <= sdk.skills.LeftHandSwing) return true; - let valid = Skill.skills.have(skillId); - - return valid; - } catch (e) { - return false; - } + if (skillId === -1) return false; + if (skillId <= sdk.skills.LeftHandSwing) return true; + return _SkillData.get(skillId).have(); }, /** @@ -219,45 +136,7 @@ * @returns {number} */ getDuration: function (skillId = -1) { - return Time.seconds((() => { - switch (skillId) { - case sdk.skills.Decoy: - return ((10 + me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints) * 5)); - case sdk.skills.FrozenArmor: - return (((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10))); - case sdk.skills.ShiverArmor: - return (((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10))); - case sdk.skills.ChillingArmor: - return (((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10))); - case sdk.skills.EnergyShield: - return (84 + (60 * me.getSkill(sdk.skills.EnergyShield, sdk.skills.subindex.SoftPoints))); - case sdk.skills.ThunderStorm: - return (24 + (8 * me.getSkill(sdk.skills.ThunderStorm, sdk.skills.subindex.SoftPoints))); - case sdk.skills.Shout: - return (((10 + me.getSkill(sdk.skills.Shout, sdk.skills.subindex.SoftPoints) * 10) + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5))); - case sdk.skills.BattleOrders: - return (((20 + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) * 10) + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5))); - case sdk.skills.BattleCommand: - return (((10 * me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.SoftPoints) - 5) + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints)) * 5))); - case sdk.skills.HolyShield: - return (5 + (25 * me.getSkill(sdk.skills.HolyShield, sdk.skills.subindex.SoftPoints))); - case sdk.skills.Hurricane: - return (10 + (2 * me.getSkill(sdk.skills.CycloneArmor, sdk.skills.subindex.HardPoints))); - case sdk.skills.Werewolf: - case sdk.skills.Werebear: - return (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)); - case sdk.skills.BurstofSpeed: - return (108 + (12 * me.getSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.SoftPoints))); - case sdk.skills.Fade: - return (108 + (12 * me.getSkill(sdk.skills.Fade, sdk.skills.subindex.SoftPoints))); - case sdk.skills.Venom: - return (116 + (4 * me.getSkill(sdk.skills.Venom, sdk.skills.subindex.SoftPoints))); - case sdk.skills.BladeShield: - return (15 + (5 * me.getSkill(sdk.skills.BladeShield, sdk.skills.subindex.SoftPoints))); - default: - return 0; - } - })()); + return _SkillData.get(skillId).duration(); }, /** @@ -265,36 +144,7 @@ * @returns {number} */ getMaxSummonCount: function (skillId) { - switch (skillId) { - case sdk.skills.Raven: - return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 5); - case sdk.skills.SummonSpiritWolf: - return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 5); - case sdk.skills.SummonDireWolf: - return Math.min(me.getSkill(skillId, sdk.skills.subindex.SoftPoints), 3); - case sdk.skills.RaiseSkeleton: - case sdk.skills.RaiseSkeletalMage: - let skillNum = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); - return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); - case sdk.skills.Revive: - return me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints); - case sdk.skills.ShadowWarrior: - case sdk.skills.ShadowMaster: - case sdk.skills.PoisonCreeper: - case sdk.skills.CarrionVine: - case sdk.skills.SolarCreeper: - case sdk.skills.OakSage: - case sdk.skills.HeartofWolverine: - case sdk.skills.SpiritofBarbs: - case sdk.skills.SummonGrizzly: - case sdk.skills.ClayGolem: - case sdk.skills.BloodGolem: - case sdk.skills.FireGolem: - case sdk.skills.Valkyrie: - return 1; - default: - return 0; - } + return _SkillData.get(skillId).summonCount(); }, /** @@ -302,132 +152,7 @@ * @returns {number} */ getRange: function (skillId) { - switch (skillId) { - case sdk.skills.Attack: - return Attack.usingBow() ? 20 : 3; - case sdk.skills.Kick: - case sdk.skills.LeftHandSwing: - case sdk.skills.Jab: - case sdk.skills.PowerStrike: - case sdk.skills.ChargedStrike: - case sdk.skills.LightningStrike: - case sdk.skills.Impale: - case sdk.skills.Fend: - case sdk.skills.Blaze: - case sdk.skills.PoisonDagger: - case sdk.skills.Sacrifice: - case sdk.skills.Smite: - case sdk.skills.Zeal: - case sdk.skills.Vengeance: - case sdk.skills.Conversion: - case sdk.skills.BlessedHammer: - case sdk.skills.FindPotion: - case sdk.skills.FindItem: - case sdk.skills.GrimWard: - case sdk.skills.Bash: - case sdk.skills.DoubleSwing: - case sdk.skills.Stun: - case sdk.skills.Concentrate: - case sdk.skills.Frenzy: - case sdk.skills.Berserk: - case sdk.skills.FeralRage: - case sdk.skills.Maul: - case sdk.skills.Rabies: - case sdk.skills.FireClaws: - case sdk.skills.Hunger: - case sdk.skills.Fury: - case sdk.skills.DragonTalon: - case sdk.skills.DragonClaw: - case sdk.skills.DragonTail: - return 3; - case sdk.skills.BattleCry: - case sdk.skills.WarCry: - return 4; - case sdk.skills.FrostNova: - case sdk.skills.Twister: - case sdk.skills.Tornado: - case sdk.skills.Summoner: - return 5; - case sdk.skills.ChargedBolt: - return 6; - case sdk.skills.Nova: - case sdk.skills.Whirlwind: - return 7; - case sdk.skills.PoisonNova: - return 8; - case sdk.skills.Armageddon: - return 9; - case sdk.skills.PoisonJavelin: - case sdk.skills.PlagueJavelin: - case sdk.skills.HolyBolt: - case sdk.skills.Charge: - case sdk.skills.Howl: - case sdk.skills.Firestorm: - case sdk.skills.MoltenBoulder: - case sdk.skills.ShockWave: - return 10; - case sdk.skills.InnerSight: - case sdk.skills.SlowMissiles: - return 13; - case sdk.skills.LightningFury: - case sdk.skills.FrozenOrb: - case sdk.skills.Teeth: - case sdk.skills.Fissure: - case sdk.skills.Volcano: - case sdk.skills.FireBlast: - case sdk.skills.ShockWeb: - case sdk.skills.BladeSentinel: - case sdk.skills.BladeFury: - return 15; - case sdk.skills.FireArrow: - case sdk.skills.MultipleShot: - case sdk.skills.ExplodingArrow: - case sdk.skills.GuidedArrow: - case sdk.skills.ImmolationArrow: - case sdk.skills.FreezingArrow: - case sdk.skills.IceBolt: - case sdk.skills.IceBlast: - case sdk.skills.FireBolt: - case sdk.skills.Revive: - case sdk.skills.FistoftheHeavens: - case sdk.skills.DoubleThrow: - case sdk.skills.PsychicHammer: - case sdk.skills.DragonFlight: - return 20; - case sdk.skills.LowerResist: - return 50; - // Variable range - case sdk.skills.StaticField: - return Math.floor((me.getSkill(sdk.skills.StaticField, sdk.skills.subindex.SoftPoints) + 4) * 2 / 3); - case sdk.skills.Leap: - { - let skLvl = me.getSkill(sdk.skills.Leap, sdk.skills.subindex.SoftPoints); - return Math.floor(Math.min(4 + (26 * ((110 * skLvl / (skLvl + 6)) / 100)), 30) * (2 / 3)); - } - case sdk.skills.ArcticBlast: - { - let skLvl = me.getSkill(sdk.skills.ArcticBlast, sdk.skills.subindex.SoftPoints); - let range = Math.floor(((33 + (2 * skLvl)) / 4) * (2 / 3)); - // Druid using this on physical immunes needs the monsters to be within range of hurricane - range > 6 && Config.AttackSkill[5] === sdk.skills.ArcticBlast && (range = 6); - - return range; - } - case sdk.skills.Lightning: - case sdk.skills.BoneSpear: - case sdk.skills.BoneSpirit: - return !!this.usePvpRange ? 35 : 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 ? 35 : 20; - } - - // Every other skill - return !!this.usePvpRange ? 30 : 20; + return _SkillData.get(skillId).range(this.usePvpRange); }, /** @@ -435,92 +160,23 @@ * @returns {number} */ getHand: function (skillId) { - switch (skillId) { - case sdk.skills.MagicArrow: - case sdk.skills.FireArrow: - case sdk.skills.ColdArrow: - case sdk.skills.MultipleShot: - case sdk.skills.PoisonJavelin: - case sdk.skills.ExplodingArrow: - case sdk.skills.Impale: - case sdk.skills.LightningBolt: - case sdk.skills.IceArrow: - case sdk.skills.GuidedArrow: - case sdk.skills.PlagueJavelin: - case sdk.skills.Strafe: - case sdk.skills.ImmolationArrow: - case sdk.skills.Fend: - case sdk.skills.FreezingArrow: - case sdk.skills.LightningFury: - case sdk.skills.FireBolt: - case sdk.skills.ChargedBolt: - case sdk.skills.IceBolt: - case sdk.skills.Inferno: - case sdk.skills.IceBlast: - case sdk.skills.FireBall: - case sdk.skills.Lightning: - case sdk.skills.ChainLightning: - case sdk.skills.GlacialSpike: - case sdk.skills.FrozenOrb: - case sdk.skills.Teeth: - case sdk.skills.PoisonDagger: - case sdk.skills.BoneSpear: - case sdk.skills.BoneSpirit: - case sdk.skills.HolyBolt: - case sdk.skills.Charge: - case sdk.skills.BlessedHammer: - case sdk.skills.FistoftheHeavens: - case sdk.skills.Leap: - case sdk.skills.DoubleThrow: - case sdk.skills.LeapAttack: - case sdk.skills.Whirlwind: - case sdk.skills.Firestorm: - case sdk.skills.MoltenBoulder: - case sdk.skills.ArcticBlast: - case sdk.skills.Twister: - case sdk.skills.ShockWave: - case sdk.skills.Tornado: - case sdk.skills.FireBlast: - case sdk.skills.TigerStrike: - case sdk.skills.ShockWeb: - case sdk.skills.BladeSentinel: - case sdk.skills.FistsofFire: - case sdk.skills.CobraStrike: - case sdk.skills.BladeFury: - case sdk.skills.ClawsofThunder: - case sdk.skills.BladesofIce: - case sdk.skills.DragonFlight: - return sdk.skills.hand.Left; - case sdk.skills.Attack: - case sdk.skills.Jab: - case sdk.skills.PowerStrike: - case sdk.skills.ChargedStrike: - case sdk.skills.LightningStrike: - case sdk.skills.Sacrifice: - case sdk.skills.Smite: - case sdk.skills.Zeal: - case sdk.skills.Vengeance: - case sdk.skills.Conversion: - case sdk.skills.Bash: - case sdk.skills.DoubleSwing: - case sdk.skills.Stun: - case sdk.skills.Concentrate: - case sdk.skills.Frenzy: - case sdk.skills.Berserk: - case sdk.skills.FeralRage: - case sdk.skills.Maul: - case sdk.skills.Rabies: - case sdk.skills.FireClaws: - case sdk.skills.Hunger: - case sdk.skills.Fury: - case sdk.skills.DragonTalon: - case sdk.skills.DragonClaw: - case sdk.skills.DragonTail: - return sdk.skills.hand.LeftNoShift; // Shift bypass - } + return _SkillData.get(skillId).hand; + }, - // Every other skill - return sdk.skills.hand.Right; + /** + * @param {number} skillId + * @returns {number} + */ + getState: function (skillId) { + return _SkillData.get(skillId).state; + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getCharClass: function (skillId) { + return _SkillData.get(skillId).charClass; }, /** @@ -530,33 +186,7 @@ */ getManaCost: function (skillId) { if (skillId < sdk.skills.MagicArrow) return 0; - if (this.manaCostList.hasOwnProperty(skillId)) return this.manaCostList[skillId]; - - let skillLvl = me.getSkill(skillId, sdk.skills.subindex.SoftPoints); - let 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("skills", skillId, "manashift")] / 256), getBaseStat("skills", skillId, "minmana")); - - if (!this.manaCostList.hasOwnProperty(skillId)) { - this.manaCostList[skillId] = ret; - } - - return ret; - }, - - // Put a skill on desired slot - setSkill: function (skillId, hand, item) { - // Check if the skill is already set - if (me.getSkill(hand === sdk.skills.hand.Right ? sdk.skills.get.RightId : sdk.skills.get.LeftId) === skillId) return true; - if (!item && !Skill.canUse(skillId)) return false; - - // Charged skills must be cast from right hand - if (hand === undefined || hand === sdk.skills.hand.RightShift || item) { - item && hand !== sdk.skills.hand.Right && console.warn("[ÿc9Warningÿc0] charged skills must be cast from right hand"); - hand = sdk.skills.hand.Right; - } - - return (me.setSkill(skillId, hand, item)); + return _SkillData.get(skillId).manaCost(); }, /** @@ -565,12 +195,7 @@ * @returns {boolean} */ isTimed: function (skillId) { - return [ - sdk.skills.PoisonJavelin, sdk.skills.PlagueJavelin, sdk.skills.ImmolationArrow, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, - sdk.skills.Hydra, sdk.skills.FrozenOrb, sdk.skills.FistoftheHeavens, sdk.skills.Firestorm, sdk.skills.Werewolf, sdk.skills.Werebear, sdk.skills.MoltenBoulder, - sdk.skills.Fissure, sdk.skills.Volcano, sdk.skills.Grizzly, sdk.skills.Armageddon, sdk.skills.Hurricane, sdk.skills.ShockWeb, sdk.skills.ShadowWarrior, - sdk.skills.DragonFlight, sdk.skills.BladeShield, sdk.skills.ShadowMaster - ].includes(skillId); + return _SkillData.get(skillId).timed; }, /** @@ -579,12 +204,15 @@ * @returns {boolean} */ townSkill: function (skillId = -1) { - return [ - sdk.skills.Valkyrie, sdk.skills.FrozenArmor, sdk.skills.Telekinesis, sdk.skills.ShiverArmor, sdk.skills.Enchant, sdk.skills.ThunderStorm, sdk.skills.EnergyShield, sdk.skills.ChillingArmor, - sdk.skills.BoneArmor, sdk.skills.ClayGolem, sdk.skills.BloodGolem, sdk.skills.FireGolem, sdk.skills.HolyShield, sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.Werewolf, sdk.skills.Werebear, - sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, sdk.skills.CycloneArmor, sdk.skills.HeartofWolverine, sdk.skills.SummonDireWolf, sdk.skills.SolarCreeper, - sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.BurstofSpeed, sdk.skills.Fade, sdk.skills.ShadowWarrior, sdk.skills.BladeShield, sdk.skills.Venom, sdk.skills.ShadowMaster - ].includes(skillId); + return _SkillData.get(skillId).townSkill; + }, + + /** + * @param {number} skillId + * @returns {boolean} + */ + missileSkill: function (skillId = -1) { + return _SkillData.get(skillId).missleSkill; }, /** @@ -595,20 +223,38 @@ wereFormCheck: function (skillId) { // we don't even have the skills to transform or we aren't transformed - add handler for wereform given by an item that is on switch if (!Skill.canUse(sdk.skills.Werewolf) && !Skill.canUse(sdk.skills.Werebear)) return true; - if (!me.getState(sdk.states.Wearwolf) && !me.getState(sdk.states.Wearbear)) return true; + const shared = new Set([ + sdk.skills.Attack, sdk.skills.Kick, sdk.skills.Raven, sdk.skills.PoisonCreeper, + sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, sdk.skills.HeartofWolverine, + sdk.skills.SummonDireWolf, sdk.skills.FireClaws, sdk.skills.SolarCreeper, sdk.skills.Hunger, + sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.Armageddon + ]); + const wolfOnly = new Set([sdk.skills.FeralRage, sdk.skills.Rabies, sdk.skills.Fury]); + const bearOnly = new Set([sdk.skills.Maul, sdk.skills.ShockWave]); + + let wolfForm = me.getState(sdk.states.Wearwolf); + if (wolfForm) return shared.has(skillId) || wolfOnly.has(skillId); - // Can be cast by both - if ([sdk.skills.Attack, sdk.skills.Kick, sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, - sdk.skills.HeartofWolverine, sdk.skills.SummonDireWolf, sdk.skills.FireClaws, sdk.skills.SolarCreeper, sdk.skills.Hunger, sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.Armageddon].includes(skillId)) { - return true; - } + let bearForm = me.getState(sdk.states.Wearbear); + if (bearForm) return shared.has(skillId) || bearOnly.has(skillId); - // Can be cast by werewolf only - if (me.getState(sdk.states.Wearwolf) && [sdk.skills.Werewolf, sdk.skills.FeralRage, sdk.skills.Rabies, sdk.skills.Fury].includes(skillId)) return true; - // Can be cast by werebear only - if (me.getState(sdk.states.Wearbear) && [sdk.skills.Werebear, sdk.skills.Maul, sdk.skills.ShockWave].includes(skillId)) return true; + // if we are not in either form, we can use any skill + return true; + }, - return false; + // Put a skill on desired slot + setSkill: function (skillId, hand, item) { + // Check if the skill is already set + if (me.getSkill(hand === sdk.skills.hand.Right ? sdk.skills.get.RightId : sdk.skills.get.LeftId) === skillId) return true; + if (!item && !Skill.canUse(skillId)) return false; + + // Charged skills must be cast from right hand + if (hand === undefined || hand === sdk.skills.hand.RightShift || item) { + item && hand !== sdk.skills.hand.Right && console.warn("[ÿc9Warningÿc0] charged skills must be cast from right hand"); + hand = sdk.skills.hand.Right; + } + + return (me.setSkill(skillId, hand, item)); }, // Change into werewolf or werebear @@ -636,53 +282,45 @@ let slot = Attack.getPrimarySlot(); me.switchWeapons(Precast.getBetterSlot(skill)); - for (let i = 0; i < 3; i += 1) { - Skill.cast(skill, sdk.skills.hand.Right); - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (me.getState(state)) { - delay(250); - me.weaponswitch !== slot && me.switchWeapons(slot); - + try { + for (let i = 0; i < 3; i += 1) { + Skill.cast(skill, sdk.skills.hand.Right); + + if (Misc.poll(() => me.getState(state), 2000, 50)) { return true; } - - delay(10); } - } - me.weaponswitch !== slot && me.switchWeapons(slot); - - return false; + return false; + } finally { + me.weaponswitch !== slot && me.switchWeapons(slot); + } }, // Change back to human shape unShift: function () { - if (me.getState(sdk.states.Wearwolf) || me.getState(sdk.states.Wearbear)) { - for (let i = 0; i < 3; i += 1) { - Skill.cast(me.getState(sdk.states.Wearwolf) ? sdk.skills.Werewolf : sdk.skills.Werebear); - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (!me.getState(sdk.states.Wearwolf) && !me.getState(sdk.states.Wearbear)) { - delay(250); - - return true; - } - - delay(10); - } + let [state, skill] = me.getState(sdk.states.Wearwolf) + ? [sdk.states.Wearwolf, sdk.skills.Werewolf] + : me.getState(sdk.states.Wearbear) + ? [sdk.states.Wearbear, sdk.skills.Werebear] + : [0, 0]; + if (!state) return true; + for (let i = 0; i < 3; i++) { + Skill.cast(skill); + + if (Misc.poll(() => !me.getState(state), 2000, 50)) { + return true; } - } else { - return true; } return false; }, - useTK: function (unit = undefined) { + /** + * @param {Unit} unit + * @returns {boolean} + */ + useTK: function (unit) { try { if (!unit || !Skill.canUse(sdk.skills.Telekinesis) || typeof unit !== "object" || unit.type !== sdk.unittype.Object @@ -700,13 +338,12 @@ // Cast a skill on self, Unit or coords cast: function (skillId, hand, x, y, item) { + if (skillId === undefined) throw new Error("Unit.cast: Must supply a skill ID"); switch (true) { case me.inTown && !this.townSkill(skillId): case !item && (this.getManaCost(skillId) > me.mp || !this.canUse(skillId)): case !this.wereFormCheck(skillId): return false; - case skillId === undefined: - throw new Error("Unit.cast: Must supply a skill ID"); } if (skillId === sdk.skills.Telekinesis && typeof x === "object" && Packet.telekinesis(x)) { @@ -767,18 +404,13 @@ } })(); - 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); + if (Misc.poll(() => me.attacking, 200, 20)) { + break; } } @@ -789,13 +421,7 @@ // account for lag, state 121 doesn't kick in immediately if (this.isTimed(skillId)) { - for (let i = 0; i < 10; i += 1) { - if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { - break; - } - - delay(10); - } + Misc.poll(() => me.skillDelay || [sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode), 100, 10); } return true; From 4c496522a70cac8e50a28881cfb9ac132343add8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 6 Apr 2023 17:34:46 -0400 Subject: [PATCH 107/758] Update Skill.js - Invalid skill handling + fix cold armor check --- d2bs/kolbot/libs/core/Skill.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 31b14df10..425ca0389 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -72,7 +72,7 @@ { skillId: sdk.skills.ChillingArmor, level: me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) }, { skillId: sdk.skills.FrozenArmor, level: me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) }, ].filter(skill => !!skill.level && skill.level > 0).sort((a, b) => b.level - a.level).first(); - return coldArmor !== undefined ? coldArmor.skillId : false; + return coldArmor !== undefined ? coldArmor.skillId : -1; })(); Precast.skills.coldArmor.duration = this.getDuration(Precast.skills.coldArmor.best); } else { @@ -126,7 +126,7 @@ * @returns {boolean} */ canUse: function (skillId = -1) { - if (skillId === -1) return false; + if (skillId < 0) return false; if (skillId <= sdk.skills.LeftHandSwing) return true; return _SkillData.get(skillId).have(); }, @@ -136,6 +136,7 @@ * @returns {number} */ getDuration: function (skillId = -1) { + if (skillId < 0) return 0; return _SkillData.get(skillId).duration(); }, @@ -144,6 +145,7 @@ * @returns {number} */ getMaxSummonCount: function (skillId) { + if (skillId < 0) return 0; return _SkillData.get(skillId).summonCount(); }, @@ -152,6 +154,7 @@ * @returns {number} */ getRange: function (skillId) { + if (skillId < 0) return 0; return _SkillData.get(skillId).range(this.usePvpRange); }, @@ -160,6 +163,7 @@ * @returns {number} */ getHand: function (skillId) { + if (skillId < 0) return -1; return _SkillData.get(skillId).hand; }, @@ -168,6 +172,7 @@ * @returns {number} */ getState: function (skillId) { + if (skillId < 0) return 0; return _SkillData.get(skillId).state; }, @@ -176,6 +181,7 @@ * @returns {number} */ getCharClass: function (skillId) { + if (skillId < 0) return 0; return _SkillData.get(skillId).charClass; }, @@ -195,6 +201,7 @@ * @returns {boolean} */ isTimed: function (skillId) { + if (skillId < 0) return false; return _SkillData.get(skillId).timed; }, @@ -204,6 +211,7 @@ * @returns {boolean} */ townSkill: function (skillId = -1) { + if (skillId < 0) return false; return _SkillData.get(skillId).townSkill; }, @@ -212,6 +220,7 @@ * @returns {boolean} */ missileSkill: function (skillId = -1) { + if (skillId < 0) return false; return _SkillData.get(skillId).missleSkill; }, From 78e6557ba19a123de7c3d4e2f1e1ddde0fe3695f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:02:59 -0400 Subject: [PATCH 108/758] Update SkillData.js - Added AoE data - Added more duration data --- d2bs/kolbot/libs/core/GameData/SkillData.js | 119 +++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 6c3a6666f..3b8ec3cf0 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -175,7 +175,8 @@ skillMap.set(sdk.skills.FreezingArrow, { hand: sdk.skills.hand.Left, missile: true, - range: 20, + range: 30, + AoE: () => 3, }); skillMap.set(sdk.skills.Valkyrie, { hand: sdk.skills.hand.Right, @@ -337,6 +338,8 @@ skillMap.set(sdk.skills.Hydra, { hand: sdk.skills.hand.Right, range: 30, + duration: () => 10, + AoE: () => 14, }); skillMap.set(sdk.skills.LightningMastery, { hand: -1, @@ -360,6 +363,8 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.AmplifyDamage, + duration: () => (5 + (3 * me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints))), + AoE: () => ((4 + (2 * me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Teeth, { hand: sdk.skills.hand.Left, @@ -386,11 +391,24 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.DimVision, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (5 + (2 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))); + case sdk.difficulty.Nightmare: + return ((125 + (50 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((125 + (50 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => ((6 + (2 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Weaken, { hand: sdk.skills.hand.Right, range: 40, state: sdk.states.Weaken, + duration: () => (11.6 + (2.4 * me.getSkill(sdk.skills.Weaken, sdk.skills.subindex.SoftPoints))), + AoE: () => ((16 + (2 * me.getSkill(sdk.skills.Weaken, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.PoisonDagger, { hand: sdk.skills.hand.Left, @@ -399,6 +417,7 @@ skillMap.set(sdk.skills.CorpseExplosion, { hand: sdk.skills.hand.Right, range: 40, + AoE: () => (((7 + me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints)) / 2) * (2 / 3)), }); skillMap.set(sdk.skills.ClayGolem, { hand: sdk.skills.hand.Right, @@ -409,11 +428,24 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.IronMaiden, + duration: () => (9.6 + (2.4 * me.getSkill(sdk.skills.IronMaiden, sdk.skills.subindex.SoftPoints))), + AoE: () => 4, }); skillMap.set(sdk.skills.Terror, { hand: sdk.skills.hand.Right, range: 40, state: sdk.states.Terror, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (7 + me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints)); + case sdk.difficulty.Nightmare: + return ((175 + (25 * me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((175 + (25 * me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => 2.66, }); skillMap.set(sdk.skills.BoneWall, { hand: sdk.skills.hand.Right, @@ -435,15 +467,29 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.Confuse, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (8 + (2 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))); + case sdk.difficulty.Nightmare: + return ((200 + (50 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((200 + (50 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.LifeTap, { hand: sdk.skills.hand.Right, range: 40, state: sdk.states.LifeTap, + duration: () => (13.6 + (2.4 * me.getSkill(sdk.skills.LifeTap, sdk.skills.subindex.SoftPoints))), + AoE: () => ((6 + (2 * me.getSkill(sdk.skills.LifeTap, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.PoisonExplosion, { hand: sdk.skills.hand.Right, range: 40, + AoE: () => 2, }); skillMap.set(sdk.skills.BoneSpear, { hand: sdk.skills.hand.Left, @@ -459,11 +505,24 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.Attract, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (8.4 + (3.6 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))); + case sdk.difficulty.Nightmare: + return ((210 + (90 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((210 + (90 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => 6, }); skillMap.set(sdk.skills.Decrepify, { hand: sdk.skills.hand.Right, range: 40, state: sdk.states.Decrepify, + duration: () => (3.4 + (0.6 * me.getSkill(sdk.skills.Decrepify, sdk.skills.subindex.SoftPoints))), + AoE: () => 4, }); skillMap.set(sdk.skills.BonePrison, { hand: sdk.skills.hand.Right, @@ -482,11 +541,14 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.LowerResist, + duration: () => (18 + (2 * me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints))), + AoE: () => ((12 + (2 * me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.PoisonNova, { hand: sdk.skills.hand.Right, range: 20, state: sdk.states.Poison, + AoE: () => 11, }); skillMap.set(sdk.skills.BoneSpirit, { hand: sdk.skills.hand.Left, @@ -518,16 +580,19 @@ hand: sdk.skills.hand.Right, range: 1, state: sdk.states.Might, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Might, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Prayer, { hand: sdk.skills.hand.Right, range: 1, state: sdk.states.Prayer, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Prayer, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.ResistFire, { hand: sdk.skills.hand.Right, range: 1, state: sdk.states.ResistFire, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistFire, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.HolyBolt, { hand: sdk.skills.hand.Left, @@ -538,21 +603,25 @@ hand: sdk.skills.hand.Right, range: 1, state: sdk.states.HolyFire, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyFire, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Thorns, { hand: sdk.skills.hand.Right, range: 1, state: sdk.states.Thorns, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Thorns, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Defiance, { hand: sdk.skills.hand.Right, range: 1, state: sdk.states.Defiance, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Defiance, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.ResistCold, { hand: sdk.skills.hand.Right, range: 1, state: sdk.states.ResistCold, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistCold, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Zeal, { hand: sdk.skills.hand.LeftNoShift, @@ -567,15 +636,18 @@ hand: sdk.skills.hand.Right, range: 1, state: sdk.states.BlessedAim, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Cleansing, { hand: sdk.skills.hand.Right, range: 1, state: sdk.states.Cleansing, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Cleansing, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.ResistLightning, { range: 1, state: sdk.states.ResistLightning, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistLightning, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Vengeance, { hand: sdk.skills.hand.LeftNoShift, @@ -584,23 +656,28 @@ skillMap.set(sdk.skills.BlessedHammer, { hand: sdk.skills.hand.Left, range: 3, + AoE: () => 10, }); skillMap.set(sdk.skills.Concentration, { range: 1, state: sdk.states.Concentration, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Concentration, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.HolyFreeze, { range: 1, state: sdk.states.HolyFreeze, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyFreeze, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Vigor, { range: 1, state: sdk.states.Stamina, condition: () => Config.Vigor || me.inTown, + AoE: () => ((26 + (6 * me.getSkill(sdk.skills.Vigor, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Conversion, { hand: sdk.skills.hand.LeftNoShift, range: 3, + duration: () => 16, }); skillMap.set(sdk.skills.HolyShield, { range: 1, @@ -610,14 +687,17 @@ skillMap.set(sdk.skills.HolyShock, { range: 1, state: sdk.states.HolyShock, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Sanctuary, { range: 1, state: sdk.states.Sanctuary, + AoE: () => ((8 + (2 * me.getSkill(sdk.skills.Sanctuary, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Meditation, { range: 1, state: sdk.states.Meditation, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Meditation, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.FistoftheHeavens, { hand: sdk.skills.hand.Left, @@ -626,18 +706,22 @@ skillMap.set(sdk.skills.Fanaticism, { range: 1, state: sdk.states.Fanaticism, + AoE: () => ((20 + (2 * me.getSkill(sdk.skills.Fanaticism, sdk.skills.subindex.SoftPoints)) / 3)), }); skillMap.set(sdk.skills.Conviction, { range: 1, state: sdk.states.Conviction, + AoE: () => 13, }); skillMap.set(sdk.skills.Redemption, { range: 1, state: sdk.states.Redemption, + AoE: () => 10, }); skillMap.set(sdk.skills.Salvation, { range: 1, state: sdk.states.ResistAll, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints)) / 3)), }); } // ~~~ start of barbarian skills ~~~ // @@ -664,6 +748,7 @@ skillMap.set(sdk.skills.Howl, { range: 5, state: sdk.states.Terror, + duration: () => (2 + me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints)), }); skillMap.set(sdk.skills.FindPotion, { hand: sdk.skills.hand.RightShift, @@ -713,6 +798,13 @@ hand: sdk.skills.hand.LeftNoShift, range: 3, state: sdk.states.Stunned, + duration: () => { + let skLvl = me.getSkill(sdk.skills.Stun, sdk.skills.subindex.SoftPoints); + let wcSkAddition = (me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) * 5); + return skLvl < 16 + ? 1 + (skLvl * 0.2) + wcSkAddition + : Math.min(2.92 + (skLvl * 0.08) + wcSkAddition, 10); + }, }); skillMap.set(sdk.skills.DoubleThrow, { hand: sdk.skills.hand.Left, @@ -775,6 +867,7 @@ range: 40, state: sdk.states.Terror, duration: () => 40, + AoE: () => (2 + me.getSkill(sdk.skills.GrimWard, sdk.skills.subindex.SoftPoints) * (2 / 3)), }); skillMap.set(sdk.skills.Whirlwind, { hand: sdk.skills.hand.Left, @@ -908,6 +1001,7 @@ hand: sdk.skills.hand.LeftNoShift, range: 3, state: sdk.states.FeralRage, + duration: () => 20, }); skillMap.set(sdk.skills.Maul, { hand: sdk.skills.hand.LeftNoShift, @@ -945,6 +1039,7 @@ hand: sdk.skills.hand.LeftNoShift, range: 3, state: sdk.states.Rabies, + duration: () => (3.6 + me.getSkill(sdk.skills.Rabies, sdk.skills.subindex.SoftPoints) * 0.4), }); skillMap.set(sdk.skills.FireClaws, { hand: sdk.skills.hand.LeftNoShift, @@ -970,6 +1065,7 @@ skillMap.set(sdk.skills.ShockWave, { hand: sdk.skills.hand.Left, range: 8, + duration: () => Math.min(1 + me.getSkill(sdk.skills.ShockWave, sdk.skills.subindex.SoftPoints) * 0.6, 10), }); skillMap.set(sdk.skills.Volcano, { hand: sdk.skills.hand.Right, @@ -1007,12 +1103,14 @@ range: 10, state: sdk.states.Armageddon, duration: () => (10 + me.getSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints) * 2), + AoE: () => 11, }); skillMap.set(sdk.skills.Hurricane, { hand: sdk.skills.hand.Right, range: 10, state: sdk.states.Hurricane, duration: () => (10 + me.getSkill(sdk.skills.CycloneArmor, sdk.skills.subindex.HardPoints) * 2), + AoE: () => 6, }); } // ~~~ start of assassin skills ~~~ // @@ -1042,6 +1140,11 @@ skillMap.set(sdk.skills.ShockWeb, { hand: sdk.skills.hand.Left, range: 15, + AoE: () => { + let baseMissiles = Math.floor((6 + me.getSkill(sdk.skills.ShockWeb, sdk.skills.subindex.SoftPoints)) / 4); + let extraMissiles = Math.floor(me.getSkill(sdk.skills.FireBlast, sdk.skills.subindex.HardPoints) / 3); + return ((((baseMissiles + extraMissiles) / 4) + 1) * (2 / 3)); + }, }); skillMap.set(sdk.skills.BladeSentinel, { hand: sdk.skills.hand.Left, @@ -1125,6 +1228,7 @@ skillMap.set(sdk.skills.MindBlast, { hand: sdk.skills.hand.Right, range: 40, + AoE: () => 2.66, }); skillMap.set(sdk.skills.BladesofIce, { hand: sdk.skills.hand.Left, @@ -1219,6 +1323,11 @@ * @type {number | () => number} */ this._range = (_skillData.range || 1); + /** + * @private + * @type {() => number} + */ + this._AoE = (_skillData.AoE || (() => 0)); /** * @private * @type {() => number} @@ -1319,6 +1428,14 @@ return typeof this._range === "function" ? this._range(pvpRange) : this._range; }; + /** + * @this Skill + * @returns {number} + */ + Skill.prototype.AoE = function () { + return this._AoE(); + }; + /** * @this Skill * @returns {boolean} From 0142f0759086a4b8480672dbe3cd0f7e75fb9794 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:08:47 -0400 Subject: [PATCH 109/758] Update Skill.js - add `Skill.getAoE` - better error checking using Map method `has`, rather than relying on numerical check --- d2bs/kolbot/libs/core/Skill.js | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 425ca0389..4efd3723e 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -126,7 +126,7 @@ * @returns {boolean} */ canUse: function (skillId = -1) { - if (skillId < 0) return false; + if (!_SkillData.has(skillId)) return false; if (skillId <= sdk.skills.LeftHandSwing) return true; return _SkillData.get(skillId).have(); }, @@ -136,7 +136,7 @@ * @returns {number} */ getDuration: function (skillId = -1) { - if (skillId < 0) return 0; + if (!_SkillData.has(skillId)) return 0; return _SkillData.get(skillId).duration(); }, @@ -145,7 +145,7 @@ * @returns {number} */ getMaxSummonCount: function (skillId) { - if (skillId < 0) return 0; + if (!_SkillData.has(skillId)) return 0; return _SkillData.get(skillId).summonCount(); }, @@ -154,16 +154,25 @@ * @returns {number} */ getRange: function (skillId) { - if (skillId < 0) return 0; + if (!_SkillData.has(skillId)) return 0; return _SkillData.get(skillId).range(this.usePvpRange); }, + /** + * @param {number} skillId + * @returns {number} + */ + getAoE: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).AoE(); + }, + /** * @param {number} skillId * @returns {number} */ getHand: function (skillId) { - if (skillId < 0) return -1; + if (!_SkillData.has(skillId)) return -1; return _SkillData.get(skillId).hand; }, @@ -172,7 +181,7 @@ * @returns {number} */ getState: function (skillId) { - if (skillId < 0) return 0; + if (!_SkillData.has(skillId)) return 0; return _SkillData.get(skillId).state; }, @@ -181,7 +190,7 @@ * @returns {number} */ getCharClass: function (skillId) { - if (skillId < 0) return 0; + if (!_SkillData.has(skillId)) return 0; return _SkillData.get(skillId).charClass; }, @@ -191,6 +200,7 @@ * @returns {number} */ getManaCost: function (skillId) { + if (!_SkillData.has(skillId)) return 0; if (skillId < sdk.skills.MagicArrow) return 0; return _SkillData.get(skillId).manaCost(); }, @@ -201,7 +211,7 @@ * @returns {boolean} */ isTimed: function (skillId) { - if (skillId < 0) return false; + if (!_SkillData.has(skillId)) return false; return _SkillData.get(skillId).timed; }, @@ -211,7 +221,7 @@ * @returns {boolean} */ townSkill: function (skillId = -1) { - if (skillId < 0) return false; + if (!_SkillData.has(skillId)) return false; return _SkillData.get(skillId).townSkill; }, @@ -220,7 +230,7 @@ * @returns {boolean} */ missileSkill: function (skillId = -1) { - if (skillId < 0) return false; + if (!_SkillData.has(skillId)) return false; return _SkillData.get(skillId).missleSkill; }, From 79ca9d513dca3fa8b859da1e7e8a216f0b96a921 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Apr 2023 17:38:39 -0400 Subject: [PATCH 110/758] Update Me.js - add `me.accessToAct` - depreciates use of `Pather.accessToAct` --- d2bs/kolbot/libs/core/Me.js | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index c178954e6..e5bf70def 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -107,8 +107,9 @@ me.switchWeapons = function (slot) { let originalSlot = this.weaponswitch; let switched = false; let packetHandler = (bytes) => bytes.length > 0 && bytes[0] === sdk.packets.recv.WeaponSwitch && (switched = true) && false; // false to not block - addEventListener("gamepacket", packetHandler); try { + addEventListener("gamepacket", packetHandler); + for (let i = 0; i < 10; i += 1) { for (let j = 10; --j && me.idle;) { delay(3); @@ -131,7 +132,6 @@ me.switchWeapons = function (slot) { delay(3); } - // Retry } } finally { removeEventListener("gamepacket", packetHandler); @@ -283,30 +283,25 @@ me.needHealing = function () { ].some((state) => me.getState(state))); }; +/** + * @param {number} id + * @returns {ItemUnit | null} + */ me.getTome = function (id) { - const tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); + if (!id) return null; + let tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); return tome ? tome : null; }; me.getUnids = function () { - let list = []; - let item = me.getItem(-1, sdk.items.mode.inStorage); - - if (!item) return []; - - do { - if (item.isInInventory && !item.identified) { - list.push(copyUnit(item)); - } - } while (item.getNext()); - - return list; + return me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((item) => item.isInInventory && !item.identified); }; // Identify items while in the field if we have a id tome me.fieldID = function () { let list = me.getUnids(); - if (!list) return false; + if (!list.length) return false; let tome = me.getTome(sdk.items.TomeofIdentify); if (!tome || tome.getStat(sdk.stats.Quantity) < list.length) return false; @@ -608,6 +603,15 @@ Object.defineProperties(me, { (function () { const QuestData = require("./GameData/QuestData"); + /** + * @param {number} act + * @returns {boolean} + */ + me.accessToAct = function (act) { + if (act === 1) return true; + return me.highestAct >= act; + }; + Object.defineProperties(me, { highestAct: { get: function () { @@ -623,9 +627,7 @@ Object.defineProperties(me, { highestQuestDone: { get: function () { for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { - if (QuestData.get(i).complete()) { - return i; - } + if (QuestData.get(i).complete()) return i; // check if we've completed main part but not used our reward if ([sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade].includes(i) From b3a5f6680352afe057f551627891078f9d9278c4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Apr 2023 17:43:17 -0400 Subject: [PATCH 111/758] Update Questing.js - Cleaner questing tasks list - Fix anya again - remove redundant returns --- d2bs/kolbot/libs/scripts/Questing.js | 343 +++++++++++---------------- 1 file changed, 144 insertions(+), 199 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js index 664ad8160..552817087 100644 --- a/d2bs/kolbot/libs/scripts/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -13,6 +13,10 @@ function Questing () { console.log("ÿc9(Questing) :: " + (errorMsg ? "ÿc1" : "ÿc0") + msg); }; + /** + * @param {ItemUnit} item + * @returns {boolean} + */ const getQuestItem = (item) => { if (item) { let id = item.classid; @@ -29,25 +33,17 @@ function Questing () { const den = function () { log("starting den"); - if (!Town.goToTown(1) || !Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true)) { - throw new Error(); - } - + Town.doChores(); + if (!Pather.journeyTo(sdk.areas.DenofEvil)) throw new Error("den failed"); Precast.doPrecast(true); Attack.clearLevel(); - Town.goToTown(); - Town.npcInteract("Akara"); - - return true; + Town.goToTown() && Town.npcInteract("Akara"); }; const smith = function () { - if (Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete)) return true; - log("starting smith"); include("core/Common/Smith.js"); - - return Common.Smith(); + Common.Smith(); }; const cain = function () { @@ -56,25 +52,19 @@ function Questing () { Town.doChores(); Common.Cain.run(); - - return true; }; const andy = function () { log("starting andy"); Town.doChores(); - Pather.useWaypoint(sdk.areas.CatacombsLvl2, true); - Precast.doPrecast(true); - - if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true) || !Pather.moveTo(22582, 9612)) { - throw new Error("andy failed"); - } + if (!Pather.journeyTo(sdk.areas.CatacombsLvl4)) throw new Error("andy failed"); + Pather.moveTo(22582, 9612); 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()) { @@ -97,17 +87,13 @@ function Questing () { Town.goToTown(); Town.npcInteract("Warriv", false); Misc.useMenu(sdk.menu.GoEast); - - return true; }; const radament = function () { - if (!Pather.accessToAct(2)) return false; - log("starting radament"); if (!Pather.journeyTo(sdk.areas.A2SewersLvl3)) { - throw new Error(); + throw new Error("radament failed"); } Precast.doPrecast(true); @@ -123,13 +109,9 @@ function Questing () { Town.goToTown(); Town.npcInteract("Atma"); - - return true; }; const lamEssen = function () { - if (!Pather.accessToAct(3)) return false; - log("starting lam essen"); if (!Pather.journeyTo(sdk.areas.RuinedTemple)) { @@ -147,25 +129,16 @@ function Questing () { getQuestItem(book); Town.goToTown(); Town.npcInteract("Alkor"); - - return true; }; const izual = function () { - if (!Pather.accessToAct(4)) return false; - log("starting izual"); - if (!Loader.runScript("Izual")) throw new Error(); + if (!Loader.runScript("Izual")) throw new Error("izual failed"); Town.goToTown(); Town.npcInteract("Tyrael"); - - return true; }; const diablo = function () { - if (!Pather.accessToAct(4)) return false; - if (Misc.checkQuest(sdk.quest.id.TerrorsEnd, sdk.quest.states.Completed)) return true; - log("starting diablo"); if (!Loader.runScript("Diablo")) throw new Error(); Town.goToTown(4); @@ -173,43 +146,32 @@ function Questing () { Game.getObject(sdk.objects.RedPortalToAct5) ? Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath) : Town.npcInteract("Tyrael", false) && Misc.useMenu(sdk.menu.TravelToHarrogath); - - return true; }; const shenk = function () { - if (!Pather.accessToAct(5)) return false; - if (Misc.checkQuest(sdk.quest.id.SiegeOnHarrogath, sdk.quest.states.ReqComplete)) return true; - log("starting shenk"); - if (!Town.goToTown() || !Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { - throw new Error(); + if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { + throw new Error("shenk failed"); } Precast.doPrecast(true); Pather.moveTo(3883, 5113); Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); Town.goToTown(); - - return true; + Town.npcInteract("Larzuk"); }; const barbs = function () { - if (!Pather.accessToAct(5)) return false; - log("starting barb rescue"); - Pather.journeyTo(sdk.areas.FrigidHighlands); + if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { + throw new Error("barbs failed"); + } Precast.doPrecast(true); let barbs = (Game.getPresetObjects(me.area, sdk.quest.chest.BarbCage) || []); - - if (!barbs.length) { - log("Couldn't find the barbs"); - - return false; - } + if (!barbs.length) throw new Error("Couldn't find the barbs"); let coords = []; @@ -235,80 +197,115 @@ function Questing () { } Town.npcInteract("qual_kehk"); - - return !!Misc.checkQuest(sdk.quest.id.RescueonMountArreat, sdk.quest.states.Completed); }; const anya = function () { - if (!Pather.accessToAct(5)) return false; - if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) return true; - log("starting anya"); - if (!Pather.journeyTo(sdk.areas.CrystalizedPassage)) { - throw new Error(); - } - - Precast.doPrecast(true); - - if (!Pather.moveToPreset(sdk.areas.FrozenRiver, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { - throw new Error("Anya quest failed"); - } + if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { + if (!Pather.journeyTo(sdk.areas.FrozenRiver)) { + throw new Error("anya failed"); + } - delay(1000); + Precast.doPrecast(true); - let anya = Game.getObject(sdk.objects.FrozenAnya); + if (!Pather.moveToPreset(sdk.areas.FrozenRiver, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { + throw new Error("Anya quest failed"); + } - // talk to anya, then cancel her speech - if (anya) { - Pather.moveToUnit(anya); - Packet.entityInteract(anya); - Misc.poll(() => getIsTalkingNPC(), 2000, 50); - me.cancel(); - } + delay(1000); + + let 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(); + } - // get pot from malah, then return to anya - Town.goToTown(); - Town.npcInteract("Malah"); - 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"); - - // unfreeze her, cancel her speech again - if (anya) { - Pather.moveToUnit(anya, 1, 2); - Packet.entityInteract(anya); - Misc.poll(() => getIsTalkingNPC() || anya.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(); + } + } + } + } } - // get reward - Town.goToTown(); - Town.npcInteract("Malah"); + /** + * 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"); + } - let scroll = me.scrollofresistance; - !!scroll && scroll.use(); + /** + * 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"); + } - return true; + /** Handles using the scroll, no need to repeat the same code here */ + let scroll = me.scrollofresistance; + !!scroll && scroll.use(); + } }; // @theBGuy const ancients = function () { include("core/Common/Ancients.js"); - Town.doChores(); log("starting ancients"); + Town.doChores(); - Pather.useWaypoint(sdk.areas.AncientsWay); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.ArreatSummit, true); - - // failed to move to Arreat Summit - if (!me.inArea(sdk.areas.ArreatSummit)) return false; + if (!Pather.journeyTo(sdk.areas.ArreatSummit)) throw new Error("ancients failed"); // ancients prep Town.doChores(); - [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion].forEach(p => Town.buyPots(10, p, true)); + [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion] + .forEach(p => Town.buyPots(10, p, true)); let tempConfig = copyObj(Config); // save and update config settings let townChicken = getScript("threads/townchicken.js"); @@ -358,8 +355,6 @@ function Questing () { } catch (err) { log("Cleared Ancients. Failed to get WSK Waypoint", true); } - - return true; }; const baal = function () { @@ -367,98 +362,48 @@ function Questing () { // just run baal script? I mean why re-invent the wheel here Loader.runScript("Baal"); Town.goToTown(5); - - return true; }; - const index = { - "den": { - id: sdk.quest.id.DenofEvil, - run: () => den(), - }, - "smith": { - id: sdk.quest.id.ToolsoftheTrade, - run: () => smith(), - }, - "cain": { - id: sdk.quest.id.TheSearchForCain, - run: () => cain(), - }, - "andy": { - id: sdk.quest.id.SistersToTheSlaughter, - run: () => andy(), - }, - "radament": { - id: sdk.quest.id.RadamentsLair, - run: () => radament(), - }, - "lamEssen": { - id: sdk.quest.id.LamEsensTome, - run: () => lamEssen(), - }, - "izual": { - id: sdk.quest.id.TheFallenAngel, - run: () => izual(), - }, - "diablo": { - id: sdk.quest.id.TerrorsEnd, - run: () => diablo(), - }, - "shenk": { - id: sdk.quest.id.SiegeOnHarrogath, - run: () => shenk(), - }, - "barbs": { - id: sdk.quest.id.RescueonMountArreat, - run: () => barbs(), - }, - "anya": { - id: sdk.quest.id.PrisonofIce, - run: () => anya(), - }, - "ancients": { - id: sdk.quest.id.RiteofPassage, - run: () => ancients(), - }, - "baal": { - id: sdk.quest.id.EyeofDestruction, - run: () => baal(), - }, - }; - - Object.defineProperty(index, "complete", { - value: function () { - return !!Misc.checkQuest(this.id, sdk.quest.states.Completed); + const tasks = (function () { + /** + * @constructor + * @param {function(): void} task + * @param {() => boolean} preReq + * @param {() => boolean} complete + */ + function Task (task, preReq, complete) { + this.run = task; + this.preReq = (preReq || (() => true)); + this.complete = (complete || (() => false)); } - }); - - let didTask = false; - me.inTown && Town.doChores(); - - Object.keys(index).forEach(quest => { - didTask && me.inTown && Town.doChores(); - let j; - - for (j = 0; j < 3; j += 1) { - if (!index[quest].complete()) { - try { - if (index[quest].run()) { - didTask = true; - - break; - } - } catch (e) { - continue; - } - } else { - didTask = false; + return [ + new Task(den, () => true, () => me.den), + new Task(smith, () => me.charlvl > 9, () => me.smith || me.imbue), + new Task(cain, () => true, () => me.cain), + new Task(andy, () => true, () => me.andariel), + new Task(radament, () => me.accessToAct(2), () => me.radament), + new Task(lamEssen, () => me.accessToAct(3), () => me.lamessen), + new Task(izual, () => me.accessToAct(4), () => me.izual), + new Task(diablo, () => me.accessToAct(4), () => me.diablo), + new Task(shenk, () => me.accessToAct(5), () => me.shenk || me.larzuk), + new Task(barbs, () => me.accessToAct(5), () => me.barbrescue), + new Task(anya, () => me.accessToAct(5), () => me.anya), + new Task(ancients, () => me.accessToAct(5) && me.charlvl > [20, 40, 60][me.diff], () => me.ancients), + new Task(baal, () => me.accessToAct(5) && me.ancients, () => me.baal), + ]; + })(); - break; + !me.inTown && Town.doChores(); + + for (let task of tasks) { + if (task.preReq() && !task.complete()) { + try { + task.run(); + } catch (e) { + console.error(e); } } - - j === 3 && D2Bot.printToConsole("Questing :: " + quest + " quest failed.", sdk.colors.D2Bot.Red); - }); + } if (Config.Questing.StopProfile || Loader.scriptList.length === 1) { D2Bot.printToConsole("All quests done. Stopping profile.", sdk.colors.D2Bot.Green); From 61fa6c845c682009f66cce007e243e3fdbc4f89f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:12:37 -0400 Subject: [PATCH 112/758] Add `sdk.locale.areas` - locale string id's for each area --- d2bs/kolbot/libs/modules/sdk.js | 150 ++++++++++++++++++++++++++++++++ d2bs/kolbot/sdk/types/sdk.d.ts | 150 ++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 6e1386686..28968a00c 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -4505,6 +4505,156 @@ ExpiresIn: 11133, CdKeyDisabledFromRealm: 11161, }, + + areas: { + // Act 1 + RogueEncampment: 5055, + BloodMoor: 5054, + ColdPlains: 5053, + StonyField: 5052, + DarkWood: 5051, + BlackMarsh: 5050, + TamoeHighland: 5049, + DenofEvil: 5048, + CaveLvl1: 5047, + UndergroundPassageLvl1: 5046, + HoleLvl1: 5045, + PitLvl1: 5044, + CaveLvl2: 5043, + UndergroundPassageLvl2: 5042, + HoleLvl2: 5041, + PitLvl2: 5040, + BurialGrounds: 5039, + Crypt: 5038, + Mausoleum: 5037, + ForgottenTower: 5036, + TowerCellarLvl1: 5035, + TowerCellarLvl2: 5034, + TowerCellarLvl3: 5033, + TowerCellarLvl4: 5032, + TowerCellarLvl5: 5031, + MonasteryGate: 5030, + OuterCloister: 5029, + Barracks: 5038, + JailLvl1: 5027, + JailLvl2: 5026, + JailLvl3: 5025, + InnerCloister: 5024, + Cathedral: 5023, + CatacombsLvl1: 5022, + CatacombsLvl2: 5021, + CatacombsLvl3: 5020, + CatacombsLvl4: 5019, + Tristram: 5018, + MooMooFarm: 788, + + // Act 2 + LutGholein: 852, + RockyWaste: 851, + DryHills: 850, + FarOasis: 849, + LostCity: 848, + ValleyofSnakes: 847, + CanyonofMagic: 846, + A2SewersLvl1: 845, + A2SewersLvl2: 844, + A2SewersLvl3: 843, + HaremLvl1: 842, + HaremLvl2: 841, + PalaceCellarLvl1: 840, + PalaceCellarLvl2: 839, + PalaceCellarLvl3: 838, + StonyTombLvl1: 837, + HallsoftheDeadLvl1: 836, + HallsoftheDeadLvl2: 835, + ClawViperTempleLvl1: 834, + StonyTombLvl2: 833, + HallsoftheDeadLvl3: 832, + ClawViperTempleLvl2: 831, + MaggotLairLvl1: 830, + MaggotLairLvl2: 829, + MaggotLairLvl3: 828, + AncientTunnels: 827, + TalRashasTomb1: 826, + TalRashasTomb2: 826, + TalRashasTomb3: 826, + TalRashasTomb4: 826, + TalRashasTomb5: 826, + TalRashasTomb6: 826, + TalRashasTomb7: 826, + DurielsLair: 825, + ArcaneSanctuary: 824, + + // Act 3 + KurastDocktown: 820, + SpiderForest: 819, + GreatMarsh: 818, + FlayerJungle: 817, + LowerKurast: 816, + KurastBazaar: 815, + UpperKurast: 814, + KurastCauseway: 813, + Travincal: 812, + SpiderCave: 810, + SpiderCavern: 811, + SwampyPitLvl1: 809, + SwampyPitLvl2: 808, + FlayerDungeonLvl1: 806, + FlayerDungeonLvl2: 805, + SwampyPitLvl3: 807, + FlayerDungeonLvl3: 804, + A3SewersLvl1: 845, + A3SewersLvl2: 844, + RuinedTemple: 803, + DisusedFane: 802, + ForgottenReliquary: 801, + ForgottenTemple: 800, + RuinedFane: 799, + DisusedReliquary: 798, + DuranceofHateLvl1: 797, + DuranceofHateLvl2: 796, + DuranceofHateLvl3: 795, + + // Act 4 + PandemoniumFortress: 790, + OuterSteppes: 792, + PlainsofDespair: 793, + CityoftheDamned: 794, + RiverofFlame: 791, + ChaosSanctuary: 789, + + // Act 5 + Harrogath: 22646, + BloodyFoothills: 22647, + FrigidHighlands: 22648, + ArreatPlateau: 22649, + CrystalizedPassage: 22650, + FrozenRiver: 22651, + GlacialTrail: 22652, + DrifterCavern: 22653, + FrozenTundra: 22654, + AncientsWay: 22655, + IcyCellar: 22656, + ArreatSummit: 22657, + NihlathaksTemple: 22658, + HallsofAnguish: 22659, + HallsofPain: 22660, + HallsofVaught: 22662, + Abaddon: 21865, + PitofAcheron: 21866, + InfernalPit: 21867, + WorldstoneLvl1: 22663, + WorldstoneLvl2: 22664, + WorldstoneLvl3: 22665, + ThroneofDestruction: 22667, + WorldstoneChamber: 22666, + + // Ubers + MatronsDen: 5389, + ForgottenSands: 5389, + FurnaceofPain: 5389, + UberTristram: 5018, + }, }, game: { diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index b79261695..3e04c8629 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4430,6 +4430,156 @@ declare global { const ExpiresIn: 11133; const CdKeyDisabledFromRealm: 11161; } + + namespace areas { + // Act 1 + const RogueEncampment: 5055; + const BloodMoor: 5054; + const ColdPlains: 5053; + const StonyField: 5052; + const DarkWood: 5051; + const BlackMarsh: 5050; + const TamoeHighland: 5049; + const DenofEvil: 5048; + const CaveLvl1: 5047; + const UndergroundPassageLvl1: 5046; + const HoleLvl1: 5045; + const PitLvl1: 5044; + const CaveLvl2: 5043; + const UndergroundPassageLvl2: 5042; + const HoleLvl2: 5041; + const PitLvl2: 5040; + const BurialGrounds: 5039; + const Crypt: 5038; + const Mausoleum: 5037; + const ForgottenTower: 5036; + const TowerCellarLvl1: 5035; + const TowerCellarLvl2: 5034; + const TowerCellarLvl3: 5033; + const TowerCellarLvl4: 5032; + const TowerCellarLvl5: 5031; + const MonasteryGate: 5030; + const OuterCloister: 5029; + const Barracks: 5038; + const JailLvl1: 5027; + const JailLvl2: 5026; + const JailLvl3: 5025; + const InnerCloister: 5024; + const Cathedral: 5023; + const CatacombsLvl1: 5022; + const CatacombsLvl2: 5021; + const CatacombsLvl3: 5020; + const CatacombsLvl4: 5019; + const Tristram: 5018; + const MooMooFarm: 788; + + // Act 2 + const LutGholein: 852; + const RockyWaste: 851; + const DryHills: 850; + const FarOasis: 849; + const LostCity: 848; + const ValleyofSnakes: 847; + const CanyonofMagic: 846; + const A2SewersLvl1: 845; + const A2SewersLvl2: 844; + const A2SewersLvl3: 843; + const HaremLvl1: 842; + const HaremLvl2: 841; + const PalaceCellarLvl1: 840; + const PalaceCellarLvl2: 839; + const PalaceCellarLvl3: 838; + const StonyTombLvl1: 837; + const HallsoftheDeadLvl1: 836; + const HallsoftheDeadLvl2: 835; + const ClawViperTempleLvl1: 834; + const StonyTombLvl2: 833; + const HallsoftheDeadLvl3: 832; + const ClawViperTempleLvl2: 831; + const MaggotLairLvl1: 830; + const MaggotLairLvl2: 829; + const MaggotLairLvl3: 828; + const AncientTunnels: 827; + const TalRashasTomb1: 826; + const TalRashasTomb2: 826; + const TalRashasTomb3: 826; + const TalRashasTomb4: 826; + const TalRashasTomb5: 826; + const TalRashasTomb6: 826; + const TalRashasTomb7: 826; + const DurielsLair: 825; + const ArcaneSanctuary: 824; + + // Act 3 + const KurastDocktown: 820; + const SpiderForest: 819; + const GreatMarsh: 818; + const FlayerJungle: 817; + const LowerKurast: 816; + const KurastBazaar: 815; + const UpperKurast: 814; + const KurastCauseway: 813; + const Travincal: 812; + const SpiderCave: 810; + const SpiderCavern: 811; + const SwampyPitLvl1: 809; + const SwampyPitLvl2: 808; + const FlayerDungeonLvl1: 806; + const FlayerDungeonLvl2: 805; + const SwampyPitLvl3: 807; + const FlayerDungeonLvl3: 804; + const A3SewersLvl1: 845; + const A3SewersLvl2: 844; + const RuinedTemple: 803; + const DisusedFane: 802; + const ForgottenReliquary: 801; + const ForgottenTemple: 800; + const RuinedFane: 799; + const DisusedReliquary: 798; + const DuranceofHateLvl1: 797; + const DuranceofHateLvl2: 796; + const DuranceofHateLvl3: 795; + + // Act 4 + const PandemoniumFortress: 790; + const OuterSteppes: 792; + const PlainsofDespair: 793; + const CityoftheDamned: 794; + const RiverofFlame: 791; + const ChaosSanctuary: 789; + + // Act 5 + const Harrogath: 22646; + const BloodyFoothills: 22647; + const FrigidHighlands: 22648; + const ArreatPlateau: 22649; + const CrystalizedPassage: 22650; + const FrozenRiver: 22651; + const GlacialTrail: 22652; + const DrifterCavern: 22653; + const FrozenTundra: 22654; + const AncientsWay: 22655; + const IcyCellar: 22656; + const ArreatSummit: 22657; + const NihlathaksTemple: 22658; + const HallsofAnguish: 22659; + const HallsofPain: 22660; + const HallsofVaught: 22662; + const Abaddon: 21865; + const PitofAcheron: 21866; + const InfernalPit: 21867; + const WorldstoneLvl1: 22663; + const WorldstoneLvl2: 22664; + const WorldstoneLvl3: 22665; + const ThroneofDestruction: 22667; + const WorldstoneChamber: 22666; + + // Ubers + const MatronsDen: 5389; + const ForgottenSands: 5389; + const FurnaceofPain: 5389; + const UberTristram: 5018; + } } export namespace game { From 5828394ebc4608e1961241f4a87636d12bf1a0bd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 17 Apr 2023 14:04:28 -0400 Subject: [PATCH 113/758] Update KurastTemples.js - little bit cleaner intent --- d2bs/kolbot/libs/scripts/KurastTemples.js | 39 ++++++++++++----------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/KurastTemples.js b/d2bs/kolbot/libs/scripts/KurastTemples.js index a847f53cc..d1832c563 100644 --- a/d2bs/kolbot/libs/scripts/KurastTemples.js +++ b/d2bs/kolbot/libs/scripts/KurastTemples.js @@ -1,6 +1,6 @@ /** * @filename KurastTemples.js -* @author kolton +* @author kolton, theBGuy * @desc clear Kurast Temples * */ @@ -8,25 +8,28 @@ function KurastTemples() { Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar); - Precast.doPrecast(true); - let areas = [ - sdk.areas.RuinedTemple, sdk.areas.DisusedFane, sdk.areas.ForgottenReliquary, - sdk.areas.ForgottenTemple, sdk.areas.RuinedFane, sdk.areas.DisusedReliquary - ]; + [ + { 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(area => { + try { + if (!me.inArea(area.base)) { + // maybe journeyTo instead? + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to change area"); + } + let precastTimeout = getTickCount() + Time.minutes(2); + // @todo sort by distance + area.temples.forEach(temple => { + if (!Pather.moveToExit(temple, true)) throw new Error("Failed to move to the temple"); + Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to move out of the temple"); + Precast.doPrecast((getTickCount() > precastTimeout)); + }); - areas.forEach((area, i) => { - if (!me.inArea(sdk.areas.KurastBazaar) + Math.floor(i / 2)) { - if (!Pather.moveToExit(sdk.areas.KurastBazaar + Math.floor(i / 2), true)) throw new Error("Failed to change area"); - } - - if (!Pather.moveToExit(area, true)) throw new Error("Failed to move to the temple"); - - i === 3 && Precast.doPrecast(true); - Attack.clearLevel(Config.ClearType); - - if (i < 5 && !Pather.moveToExit(sdk.areas.KurastBazaar + Math.floor(i / 2), true)) { - throw new Error("Failed to move out of the temple"); + } catch (e) { + console.error(e); } }); From 6acba0591ffd6bbfca8e318465803d3d6f4e2a9f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:57:29 -0400 Subject: [PATCH 114/758] Update Ancients.js - Add using tk on altar - Don't move to altar spot if we have a ranged main attack --- d2bs/kolbot/libs/core/Common/Ancients.js | 39 +++++++++++------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Ancients.js b/d2bs/kolbot/libs/core/Common/Ancients.js index 050497286..6f00b1101 100644 --- a/d2bs/kolbot/libs/core/Common/Ancients.js +++ b/d2bs/kolbot/libs/core/Common/Ancients.js @@ -9,7 +9,7 @@ typeof Common !== "object" && (Common = {}); Object.defineProperty(Common, "Ancients", { value: { - altarSpot: {x: 10047, y: 12622}, + altarSpot: { x: 10047, y: 12622 }, canAttack: function () { let ancient = Game.getMonster(); @@ -27,22 +27,17 @@ }, touchAltar: function () { - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (Game.getObject(sdk.objects.AncientsAltar)) { - break; - } - - delay(20 + me.ping); - } - - let altar = Game.getObject(sdk.objects.AncientsAltar); + let altar = Misc.poll(() => Game.getObject(sdk.objects.AncientsAltar), 5000, 100); if (altar) { while (altar.mode !== sdk.objects.mode.Active) { - Pather.moveToUnit(altar); - altar.interact(); + if (Skill.haveTK) { + Attack.getIntoPosition(altar, 19, sdk.collision.LineOfSight, !Pather.useTeleport(), true); + Packet.telekinesis(altar); + } else { + Pather.moveToUnit(altar); + altar.interact(); + } delay(200 + me.ping); me.cancel(); } @@ -53,6 +48,8 @@ } return true; + } else { + Pather.moveNearUnit(this.altarSpot, (Skill.haveTK ? 19 : 5)); } return false; @@ -76,7 +73,7 @@ if (!this.checkStatues()) { return pos.forEach((node) => { // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({range: 30}) === 0) { + if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { return; } Pather.moveTo(node.x, node.y); @@ -89,7 +86,8 @@ killAncients: function (checkQuest = false) { let retry = 0; - Pather.moveToUnit(this.altarSpot); + let attackRange = Skill.getRange(Config.AttackSkill[1]); + Pather.moveNearUnit(this.altarSpot, attackRange); while (!this.checkStatues()) { if (retry > 5) { @@ -97,16 +95,17 @@ break; } - + /** + * @todo - far cast pwning the ancients + */ Attack.clearClassids(sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian); delay(1000); if (checkQuest) { if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { break; - } else { - console.log("Failed to kill anicents. Attempt: " + retry); } + console.log("Failed to kill anicents. Attempt: " + retry); } this.checkCorners(); @@ -124,13 +123,11 @@ startAncients: function (preTasks = false, checkQuest = false) { let retry = 0; - Pather.moveToUnit(this.altarSpot); this.touchAltar(); while (!this.canAttack()) { if (retry > 10) throw new Error("I think I'm unable to complete ancients, I've rolled them 10 times"); preTasks ? this.ancientsPrep() : Pather.makePortal(); - Pather.moveToUnit(this.altarSpot); this.touchAltar(); retry++; } From 717b85e701f8794190da638c0e70ae49bfcaab0d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 21 Apr 2023 11:28:47 -0400 Subject: [PATCH 115/758] Add CreateGameDescription control - Allow option to set a game description when creating game if desired --- d2bs/kolbot/libs/OOG.js | 1 + d2bs/kolbot/libs/modules/Control.js | 1 + d2bs/kolbot/libs/starter/StarterConfig.js | 1 + 3 files changed, 3 insertions(+) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 05ed2f143..633317dae 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -745,6 +745,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required createGame: function (name, pass, diff, delay) { Controls.CreateGameName.setText(name); Controls.CreateGamePass.setText(pass); + Controls.CreateGameDescription.setText(Starter.Config.GameDescription); switch (diff) { case "Normal": diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index d915bb32e..6452c28e4 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -216,6 +216,7 @@ Control.CreateGame = new Control(sdk.controls.Button, 594, 433, 172, 32); Control.CreateGameName = new Control(sdk.controls.TextBox, 432, 162, 158, 20); Control.CreateGamePass = new Control(sdk.controls.TextBox, 432, 217, 158, 20); + Control.CreateGameDescription = new Control(sdk.controls.TextBox, 432, 268, 333, 20); Control.CharacterDifferenceButton = new Control(sdk.controls.Button, 431, 341, 15, 16); Control.CharacterDifference = new Control(sdk.controls.TextBox, 657, 342, 27, 20); Control.MaxPlayerCount = new Control(sdk.controls.TextBox, 657, 308, 27, 20); diff --git a/d2bs/kolbot/libs/starter/StarterConfig.js b/d2bs/kolbot/libs/starter/StarterConfig.js index 6505d554e..871b85d26 100644 --- a/d2bs/kolbot/libs/starter/StarterConfig.js +++ b/d2bs/kolbot/libs/starter/StarterConfig.js @@ -14,6 +14,7 @@ ResetCount: 999, // Reset game count back to 1 every X games. CharacterDifference: 99, // Character level difference. Set to false to disable character difference. MaxPlayerCount: 8, // Max amount of players in game between 1 and 8 + GameDescription: "", // Game description when creating a game StopOnDeadHardcore: true, // Stop profile character has died on hardcore mode // ChannelConfig can override these options for individual profiles. From 27b967565e9707d4a1ab7e67fb15fbe7046cae62 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:47:15 -0400 Subject: [PATCH 116/758] Update NTItemParser.js - add distance flag for pickit lines --- d2bs/kolbot/libs/core/NTItemParser.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 94c80d4e4..20ebef3f4 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -133,7 +133,10 @@ NTIP.Clear = function () { stringArray = []; }; -/** @return {function({Unit} item)} */ +/** + * @param {string} tierType + * @returns {(item: ItemUnit) => number} + */ NTIP.generateTierFunc = function (tierType) { return function (item) { let tier = -1; @@ -183,12 +186,18 @@ NTIP.generateTierFunc = function (tierType) { }; }; -/**@function - * @param item */ +/** + * @function + * @param {ItemUnit} item + * @returns {number} + */ NTIP.GetTier = NTIP.generateTierFunc("Tier"); -/**@function - * @param item */ +/** + * @function + * @param {ItemUnit} item + * @returns {number} + */ NTIP.GetMercTier = NTIP.generateTierFunc("Merctier"); NTIP.CheckItem = function (item, entryList, verbose) { @@ -422,6 +431,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); From 7fea124ab68fd90b448e38a6a8d81fb465275471 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:39:24 -0400 Subject: [PATCH 117/758] Add more enchant mods --- d2bs/kolbot/libs/modules/sdk.js | 27 +++++++++++++++++++++++++++ d2bs/kolbot/sdk/types/sdk.d.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 28968a00c..a6b7f80d5 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -1064,18 +1064,44 @@ }, enchant: { + RandName: 1, + HpMultiply: 2, + AddLightRadius: 3, + AddMLvl: 4, ExtraStrong: 5, ExtraFast: 6, Cursed: 7, MagicResistant: 8, FireEnchanted: 9, + PoisonDeath: 10, + InsectDeath: 11, + ChainLightingDeath: 12, + IgnoreTargetDefense: 13, + UnknownMod: 14, + KillMinionsDeath: 15, + ChampMods: 16, LightningEnchanted: 17, ColdEnchanted: 18, + UnusedMercMod: 19, + ChargedBoltWhenStruck: 20, + TempSummoned: 21, + QuestMod: 22, + PoisonField: 23, + Thief: 24, ManaBurn: 25, Teleportation: 26, SpectralHit: 27, StoneSkin: 28, MultipleShots: 29, + Aura: 30, + CorpseExplosion: 31, + FireExplosionOnDeath: 32, // not sure what the difference is between this and 9 + FreezeOnDeath: 33, + SelfResurrect: 34, + IceShatter: 35, + ChampStoned: 36, + ChampStats: 37, + ChampCurseImmune: 38, }, // unit stats @@ -1588,6 +1614,7 @@ ], // act1 + MoldyTome: 8, A1TownFire: 39, A1Waypoint: 119, StoneAlpha: 17, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 3e04c8629..702e51876 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -991,18 +991,44 @@ declare global { } export namespace enchant { + const RandName: 1; + const HpMultiply: 2; + const AddLightRadius: 3; + const AddMLvl: 4; const ExtraStrong: 5; const ExtraFast: 6; const Cursed: 7; const MagicResistant: 8; const FireEnchanted: 9; + const PoisonDeath: 10; + const InsectDeath: 11; + const ChainLightingDeath: 12; + const IgnoreTargetDefense: 13; + const UnknownMod: 14; + const KillMinionsDeath: 15; + const ChampMods: 16; const LightningEnchanted: 17; const ColdEnchanted: 18; + const UnusedMercMod: 19; + const ChargedBoltWhenStruck: 20; + const TempSummoned: 21; + const QuestMod: 22; + const PoisonField: 23; + const Thief: 24; const ManaBurn: 25; const Teleportation: 26; const SpectralHit: 27; const StoneSkin: 28; const MultipleShots: 29; + const Aura: 30; + const CorpseExplosion: 31; + const FireExplosionOnDeath: 32; // not sure what the difference is between this and 9 + const FreezeOnDeath: 33; + const SelfResurrect: 34; + const IceShatter: 35; + const ChampStoned: 36; + const ChampStats: 37; + const ChampCurseImmune: 38; } // unit stats @@ -1512,6 +1538,7 @@ declare global { ]; // act1 + const MoldyTome: 8; const A1TownFire: 39; const A1Waypoint: 119; const StoneAlpha: 17; From 6832e5fb3e709b1db299144aaf0ab1d95d94ac54 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 22 May 2023 18:28:45 -0400 Subject: [PATCH 118/758] Add some new array polyfills - `Array.of` - `Array.prototype.toReveresed` - `Array.prototype.toSorted` - `Array.prototype.toSpliced` --- d2bs/kolbot/libs/Polyfill.js | 66 ++++++++++++++++++++++++++++++++++++ d2bs/kolbot/sdk/globals.d.ts | 41 ++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 61eeeb840..0e4bab5cf 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -231,6 +231,10 @@ String.prototype.format = function (...pairs) { * - Array.prototype.first * - Array.prototype.last * - Array.prototype.flat + * - Array.of + * - Array.prototype.toSorted + * - Array.prototype.toReversed + * - Array.prototype.toSpliced * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // */ @@ -576,6 +580,68 @@ if (!Array.prototype.flat) { }); } +/** + * @description The Array.of() static method creates a new Array instance from a + * variable number of arguments, regardless of number or type of the arguments. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of + * @return {Array<...args>} + */ +if (!Array.of) { + Object.defineProperty(Array, "of", { + value: function of() { + return Array.prototype.slice.call(arguments); + }, + configurable: true, + writable: true + }); +} + +/** + * @description The toReversed() method of Array instances is the copying counterpart of the reverse() + * method. It returns a new array with the elements in reversed order. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toReversed + * @return {Array} + */ +if (!Array.prototype.toReversed) { + Array.prototype.toReversed = function() { + return this.slice().reverse(); + }; +} + +/** + * Creates a new array with the elements of the original array sorted in ascending order. + * + * @param {Function} [compareFunction] A function that defines the sort order. + * If omitted, the elements are sorted in ascending order based on their string conversion. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted + * @returns {Array} A new array with the elements sorted in ascending order. + */ +if (!Array.prototype.toSorted) { + Array.prototype.toSorted = function(compareFunction) { + return this.slice().sort(compareFunction); + }; +} + +/** + * Creates a new array by removing or replacing elements from the original array. + * + * @param {number} start The index at which to start changing the array. + * If negative, it is treated as `array.length + start`. + * @param {number} [deleteCount] The number of elements to remove from the array. + * If omitted or greater than `array.length - start`, all elements from `start` to the end of the array are deleted. + * @param {...*} [items] The elements to add to the array starting from the `start` index. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced + * @returns {Array} A new array with the modified elements. + */ +if (!Array.prototype.toSpliced) { + Array.prototype.toSpliced = function(start, deleteCount) { + const newArr = this.slice(); + const items = Array.prototype.slice.call(arguments, 2); + Array.prototype.splice.apply(newArr, [start, deleteCount].concat(items)); + return newArr; + }; +} + /** * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Object Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index f29e07071..bfc92137e 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -23,6 +23,19 @@ declare global { lineNumber: number; } + interface ArrayConstructor { + /** + * Creates a new Array instance with a variable number of elements passed as arguments. + * + * @param {...T[]} items The elements to include in the array. + * ```ts + * const arr = Array.of(1, 2, 3, 4, 5); + * ``` + * @returns {Array} A new array with the provided elements. + */ + of(...items: T[]): T[]; + } + interface Array { includes(searchElement: T): boolean; find(predicate: (value: T, index: number, obj: Int8Array) => boolean, thisArg?: any): T | undefined; @@ -38,6 +51,34 @@ declare global { isEqual(t: T[]): boolean remove(val: T): T[] random(): T; + /** + * Creates a new array by sorting the elements of the original array. + * + * @param {(function(a: any, b: any): number) | undefined} compareFn Function used to determine the order of the elements. + * It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive + * value otherwise. If omitted, the elements are sorted in ascending, ASCII character order. + * ```ts + * [11,2,22,1].toSorted((a, b) => a - b) + * ``` + * @returns {Array} A new array with the sorted elements, leaving the orignal intact. + */ + toSorted(compareFn?: ((a: T, b: T) => number) | undefined): T[]; + /** + * Creates a new array with the elements of the original array in reversed order. + * Without mutating the original array. + * + * @returns {Array} A new array with the reversed elements. + */ + toReversed(): T[]; + /** + * Creates a new array by removing and/or adding elements from/to the original array. + * + * @param {number} start The index at which to start changing the array. + * @param {number} deleteCount The number of elements to remove starting from the `start` index. + * @param {...T[]} items The elements to add to the array. + * @returns {Array} A new array with the removed elements and optionally added elements. + */ + toSpliced(start: number, deleteCount?: number, ...items: T[]): T[]; } interface String { From 08034d7c91f5cca105ba195c2851a79a84eb75e8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 23 May 2023 15:47:08 -0400 Subject: [PATCH 119/758] Update Polyfill.js - Change timers to be a map - update `console.info` so that it can initialize a timer as well as use one to display the duration --- d2bs/kolbot/libs/Polyfill.js | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 0e4bab5cf..77a8ddec2 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -991,16 +991,24 @@ Set.prototype.difference = function(setB) { this.log("[ÿc1Errorÿc0] " + msg); }; - const timers = {}; + /** @type {Map} */ + const timers = new Map(); + + /** + * @param {string} name + */ console.time = function (name) { - name && (timers[name] = getTickCount()); + name && timers.set(name, getTickCount()); }; + /** + * @param {string} name + */ console.timeEnd = function (name) { - let currTimer = timers[name]; + let currTimer = timers.get(name); if (currTimer) { this.log("[ÿc7Timerÿc0] :: ÿc8" + name + " - ÿc4Durationÿc0: " + (getTickCount() - currTimer) + "ms"); - delete timers[name]; + timers.delete(name); } }; @@ -1023,18 +1031,24 @@ Set.prototype.difference = function(setB) { }; console.info = function (start = false, msg = "", timer = "") { - let stack = new Error().stack.match(/[^\r\n]+/g); + const stack = new Error().stack.match(/[^\r\n]+/g); let funcName = stack[1].substr(0, stack[1].indexOf("@")); - let logInfo = start === true ? "[ÿc2Start " : start === false ? "[ÿc1End " : "[ÿc8"; + let logInfo = start === true + ? "[ÿc2Start " + : start === false + ? "[ÿc1End " + : "[ÿc8"; logInfo += (funcName + "ÿc0] :: " + (msg ? msg : "")); if (timer) { - let currTimer = timers[timer]; + let currTimer = timers.get(timer); if (currTimer) { let tFormat = (getTickCount() - currTimer); // if less than 1 second, display in ms tFormat > 1000 ? (tFormat = Time.format(tFormat)) : (tFormat += " ms"); logInfo += (" - ÿc4Durationÿc0: " + tFormat); - delete timers[timer]; + timers.delete(timer); + } else { + this.time(timer); } } this.log(logInfo); From 365f2a3c9887c4bf102ff7c3045e453c0d64ea01 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 23 May 2023 23:46:21 -0400 Subject: [PATCH 120/758] little bit of core\ cleanup - added optional callback parameter to buildMonsterList - fixed jsdoc comment for `Misc.poll` - added enabled property to NodeAction so we can disable if if desired - changed Precast declaration, using the singleton function declaration was pointless. Use object literal instead like the rest of the namespaces --- d2bs/kolbot/libs/core/Attack.js | 5 +-- d2bs/kolbot/libs/core/Misc.js | 11 ++++--- d2bs/kolbot/libs/core/Pather.js | 29 ++++++++--------- d2bs/kolbot/libs/core/Precast.js | 56 ++++++++++++++++---------------- 4 files changed, 50 insertions(+), 51 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 642bc050c..4580749ea 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1197,15 +1197,16 @@ const Attack = { /** * @description build list of attackable monsters currently around us + * @param {function(): boolean} check - callback function to build list * @returns {Array | []} */ - buildMonsterList: function () { + buildMonsterList: function (check = () => true) { let monList = []; let monster = Game.getMonster(); if (monster) { do { - if (monster.attackable) { + if (monster.attackable && check(monster)) { monList.push(copyUnit(monster)); } } while (monster.getNext()); diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index a092fe528..58b47d814 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -539,7 +539,7 @@ const Misc = { Pather.moveToEx(coords[0], coords[1], { minDist: Skill.haveTK ? 20 : 5, callback: () => { let shrine = Game.getObject("shrine"); return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; - }}); + } }); let shrine = Game.getObject("shrine"); @@ -731,10 +731,11 @@ const Misc = { }, /** - * @param {Function} check - * @param {number} timeout - * @param {number} sleep - * @returns {boolean} + * @template T + * @param {function(): T} check + * @param {number} [timeout=6000] + * @param {number} [sleep=40] + * @returns {T | false} */ poll: function (check, timeout = 6000, sleep = 40) { let ret, start = getTickCount(); diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index a61311c66..707c0f92b 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -14,12 +14,14 @@ const NodeAction = { * @type {number[]} */ shrinesToIgnore: [], + enabled: true, /** * Run all the functions within NodeAction (except for itself) * @param {clearSettings} arg */ go: function (arg) { + if (!this.enabled) return; for (let i in this) { if (this.hasOwnProperty(i) && typeof this[i] === "function" && i !== "go") { this[i](arg); @@ -286,24 +288,19 @@ const Pather = { target = this.spotOnDistance(target, settings.minDist, { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) }); } + /** @constructor */ + function PathAction () { + this.at = 0; + /** @type {PathNode} */ + this.from = { x: null, y: null }; + } + let fail = 0; + let invalidCheck = 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(); for (let i = 0; i < this.cancelFlags.length; i += 1) { getUIFlag(this.cancelFlags[i]) && me.cancel(); diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 85fbfa500..af6455960 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -5,16 +5,16 @@ * */ -const Precast = new function () { - this.enabled = true; - this.haveCTA = -1; - this.bestSlot = {}; +const Precast = { + enabled: true, + haveCTA: -1, + bestSlot: {}, // TODO: build better method of keeping track of duration based skills so we can reduce resource usage // build obj -> figure out which skills we have -> calc duration -> assign tick of last casted -> track tick (background worker maybe?) // would reduce checking have skill and state calls, just let tick = getTickCount(); -> obj.some((el) => tick - el.lastTick > el.duration) -> true then cast // would probably make sense to just re-cast everything (except summons) if one of our skills is about to run out rather than do this process again 3 seconds later - this.skills = { + skills: { // Not sure how I want to handle cold armors coldArmor: { best: false, @@ -44,7 +44,7 @@ const Precast = new function () { duration: 0, tick: 0 }, - }; + }, /** * Easier Shout/Bo/Bc casting with state checks to ensure it was casted @@ -53,7 +53,7 @@ const Precast = new function () { * @param {number} [y] * @returns {boolean} */ - this.warCries = function (skillId, x, y) { + warCries: function (skillId, x, y) { if (!skillId || x === undefined) return false; const states = {}; states[sdk.skills.Shout] = sdk.states.Shout; @@ -95,25 +95,25 @@ const Precast = new function () { } } return false; - }; + }, - this.checkCTA = function () { + checkCTA: function () { if (this.haveCTA > -1) return true; let check = me.checkItem({ name: sdk.locale.items.CalltoArms, equipped: true }); if (check.have) { - this.haveCTA = check.item.isOnSwap ? 1 : 0; + Precast.haveCTA = check.item.isOnSwap ? 1 : 0; } return this.haveCTA > -1; - }; + }, /** * @param {boolean} force * @returns {boolean} */ - this.precastCTA = function (force = false) { + precastCTA: function (force = false) { if (!Config.UseCta || this.haveCTA === -1 || me.classic || me.barbarian || me.inTown || me.shapeshifted) return false; if (!force && me.getState(sdk.states.BattleOrders)) return true; @@ -136,7 +136,7 @@ const Precast = new function () { } return false; - }; + }, /** * Check which slot (primary or secondary) gives us the most skillpoints in a skill @@ -144,7 +144,7 @@ const Precast = new function () { * @returns {0 | 1} best slot to give us the most skillpoints in a skill * @todo Move this to be part of the SkillData class */ - this.getBetterSlot = function (skillId) { + getBetterSlot: function (skillId) { if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; let [classid, skillTab] = (() => { @@ -211,9 +211,9 @@ const Precast = new function () { }); this.bestSlot[skillId] = (sumSwap > sumCurr) ? me.weaponswitch ^ 1 : me.weaponswitch; return this.bestSlot[skillId]; - }; + }, - this.cast = function (skillId, x = me.x, y = me.y, dontSwitch = false) { + cast: function (skillId, x = me.x, y = me.y, dontSwitch = false) { if (!skillId || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) return false; if (Skill.getManaCost(skillId) > me.mp) return false; @@ -225,7 +225,7 @@ const Precast = new function () { 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); - (typeof x !== "number" || typeof y !== "number") && ({x, y} = me); + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); try { !dontSwitch && me.switchWeapons(this.getBetterSlot(skillId)); @@ -288,9 +288,9 @@ const Precast = new function () { !dontSwitch && me.switchWeapons(swap); return success; - }; + }, - this.summon = function (skillId, minionType) { + summon: function (skillId, minionType) { if (!Skill.canUse(skillId)) return false; let rv, retry = 0; @@ -348,9 +348,9 @@ const Precast = new function () { } return !!rv; - }; + }, - this.enchant = function () { + enchant: function () { let unit, slot = me.weaponswitch, chanted = []; me.switchWeapons(this.getBetterSlot(sdk.skills.Enchant)); @@ -381,7 +381,7 @@ const Precast = new function () { me.switchWeapons(slot); return true; - }; + }, // should the config check still be included even though its part of Skill.init? /** @@ -391,7 +391,7 @@ const Precast = new function () { * @returns {boolean} sucessfully casted * @todo durations */ - this.doPrecast = function (force = false, partial = false) { + doPrecast: function (force = false, partial = false) { if (!this.enabled) return false; while (!me.gameReady) { @@ -617,13 +617,13 @@ const Precast = new function () { me.switchWeapons(Attack.getPrimarySlot()); return true; - }; + }, - this.needOutOfTownCast = function () { + needOutOfTownCast: function () { return Skill.canUse(sdk.skills.Shout) || Skill.canUse(sdk.skills.BattleOrders) || Precast.checkCTA(); - }; + }, - this.doRandomPrecast = function (force = false, goToWhenDone = undefined) { + doRandomPrecast: function (force = false, goToWhenDone = undefined) { let returnTo = (goToWhenDone && typeof goToWhenDone === "number" ? goToWhenDone : me.area); try { @@ -643,5 +643,5 @@ const Precast = new function () { } return (me.area === returnTo); - }; + }, }; From a5836b4e1343b024273a41cc87709fca4d6adab4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 23 May 2023 23:48:34 -0400 Subject: [PATCH 121/758] little bit of script cleanup - refactored GhostBusters - minor minification to Follower --- d2bs/kolbot/libs/scripts/Follower.js | 112 ++++++--------- d2bs/kolbot/libs/scripts/GhostBusters.js | 169 +++++++++-------------- d2bs/kolbot/libs/scripts/Tombs.js | 5 +- d2bs/kolbot/libs/scripts/Wakka.js | 64 +++++---- d2bs/kolbot/libs/scripts/Worldstone.js | 2 +- 5 files changed, 143 insertions(+), 209 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index ff08a5d63..d29788ad7 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -28,7 +28,8 @@ * @Skills *** refer to skills.txt *** * all skill - change skill for all. refer to skills.txt * skill - change skill for -* skill - change skill for all characters of certain class *** any part of class name will do *** for example: "sorc skill 36", "zon skill 0", "din skill 106" +* skill - change skill for all characters of certain class +* *** any part of class name will do *** for example: "sorc skill 36", "zon skill 0", "din skill 106" * Auras: *** refer to skills.txt *** * all aura - change aura for all paladins * aura - change aura for @@ -45,15 +46,19 @@ * move - move in a random direction (use if you're stuck by followers) * reload - reload script. Use only in case of emergency, or after editing character config. * quit - exit game +* @todo +* run - run a script +* run - run a script on +* skills - list current attack skills +* skills - list current attack skills for * */ function Follower() { - let i, stop, leader, leaderUnit, charClass, piece, skill, result, unit, player, coord; - let commanders = [Config.Leader]; - let allowSay = true; - let attack = true; - let openContainers = true; + const commanders = [Config.Leader]; + let piece, skill; + let [allowSay, attack, openContainers, stop] = [true, true, true, false]; + let [leader, leaderUnit] = [null, null]; let action = ""; const announce = function (msg = "") { @@ -134,27 +139,20 @@ function Follower() { * @returns {boolean} */ const talk = function (name) { - if (!me.inTown) { - announce("I'm not in town!"); - - return false; - } - - if (typeof name !== "string") { - announce("No NPC name given."); - - return false; - } - try { + if (!me.inTown) throw new Error("I'm not in town!"); + if (typeof name !== "string") throw new Error("No NPC name given."); Town.npcInteract(name); + + return true; } catch (e) { + console.error(e); announce((typeof e === "object" && e.message ? e.message : typeof e === "string" ? e : "Failed to talk to " + name)); - } - Town.move("portalspot"); - - return false; + return false; + } finally { + Town.move("portalspot"); + } }; /** @@ -242,7 +240,8 @@ function Follower() { if (item) { do { - if (item.onGroundOrDropping && item.itemType >= sdk.items.type.HealingPotion && item.itemType <= sdk.items.type.RejuvPotion && item.distance <= range) { + if (item.onGroundOrDropping && item.itemType >= sdk.items.type.HealingPotion + && item.itemType <= sdk.items.type.RejuvPotion && item.distance <= range) { pickList.push(copyUnit(item)); } } while (item.getNext()); @@ -266,7 +265,6 @@ function Follower() { }; /** - * * @param {string} nick * @param {string} msg */ @@ -275,42 +273,26 @@ function Follower() { switch (msg) { case "tele": case me.name + " tele": - if (Pather.teleport) { - Pather.teleport = false; - - announce("Teleport off."); - } else { - Pather.teleport = true; - - announce("Teleport on."); - } + Pather.teleport = !Pather.teleport; + announce("Teleport " + (Pather.teleport ? "on" : "off")); break; case "tele off": case me.name + " tele off": Pather.teleport = false; - announce("Teleport off."); break; case "tele on": case me.name + " tele on": Pather.teleport = true; - announce("Teleport on."); break; case "a": case me.name + " a": - if (attack) { - attack = false; - - announce("Attack off."); - } else { - attack = true; - - announce("Attack on."); - } + attack = !attack; + announce("Attack " + (attack ? "on" : "off")); break; case "flash": @@ -324,14 +306,12 @@ function Follower() { case "aoff": case me.name + " aoff": attack = false; - announce("Attack off."); break; case "aon": case me.name + " aon": attack = true; - announce("Attack on."); break; @@ -342,15 +322,8 @@ function Follower() { break; case "s": case me.name + " s": - if (stop) { - stop = false; - - announce("Resuming."); - } else { - stop = true; - - announce("Stopping."); - } + stop = !stop; + announce((stop ? "Stopping." : "Resuming.")); break; case "r": @@ -430,7 +403,7 @@ function Follower() { Config.TownCheck = false; Config.TownHP = 0; Config.TownMP = 0; - charClass = sdk.player.class.nameOf(me.classid).toLowerCase(); + const charClass = sdk.player.class.nameOf(me.classid).toLowerCase(); leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.seconds(20), Time.seconds(1)); if (!leader) { @@ -475,7 +448,7 @@ function Follower() { } if (!leaderUnit) { - player = Game.getPlayer(); + let player = Game.getPlayer(); if (player) { do { @@ -506,7 +479,7 @@ function Follower() { delay(100); } - result = checkExit(leader, leader.area); + let result = checkExit(leader, leader.area); switch (result) { case 1: @@ -539,6 +512,7 @@ function Follower() { } } + MainSwitch: switch (action) { case "cow": if (me.inArea(sdk.areas.RogueEncampment)) { @@ -548,7 +522,7 @@ function Follower() { break; case "move": - coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); + let coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); Pather.moveTo(coord.x, coord.y); break; @@ -560,17 +534,15 @@ function Follower() { delay(rand(1, 3) * 500); - unit = Game.getObject("waypoint"); - - if (unit) { - for (i = 0; i < 3; i += 1) { + if (Game.getObject("waypoint")) { + for (let retry = 0; retry < 3; retry++) { if (Pather.getWP(me.area)) { announce("Got wp."); - break; + break MainSwitch; } } - i === 3 && announce("Failed to get wp."); + announce("Failed to get wp."); } me.cancel(); @@ -642,16 +614,10 @@ function Follower() { break; case me.name + " tp": - unit = me.getTpTool(); - - if (unit) { - unit.interact(); - - break; + if (!Pather.makePortal()) { + announce("No TP scrolls or tomes."); } - announce("No TP scrolls or tomes."); - break; } diff --git a/d2bs/kolbot/libs/scripts/GhostBusters.js b/d2bs/kolbot/libs/scripts/GhostBusters.js index 01139c4a9..d62738eed 100644 --- a/d2bs/kolbot/libs/scripts/GhostBusters.js +++ b/d2bs/kolbot/libs/scripts/GhostBusters.js @@ -1,16 +1,18 @@ /** * @filename GhostBusters.js -* @author kolton +* @author kolton, theBGuy * @desc who you gonna call? * */ function GhostBusters() { - this.clearGhosts = function () { + const clearGhosts = function () { let room = getRoom(); if (!room) return false; - let rooms = []; + const rooms = []; + /** @param {Monster} monster */ + const check = (monster) => monster.isGhost && monster.distance <= 30; do { rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); @@ -25,16 +27,8 @@ function GhostBusters() { if (result) { Pather.moveTo(result[0], result[1], 3); - let monList = []; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.isGhost && monster.distance <= 30 && monster.attackable) { - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } + let monList = Attack.buildMonsterList(check); + if (!monList.length) continue; if (!Attack.clearList(monList)) { return false; @@ -45,100 +39,75 @@ function GhostBusters() { return true; }; - this.cellar = function () { - Pather.useWaypoint(sdk.areas.BlackMarsh); - Precast.doPrecast(true); - - for (let i = sdk.areas.ForgottenTower; i <= sdk.areas.TowerCellarLvl5; i += 1) { - Pather.moveToExit(i, true) && this.clearGhosts(); - } - - return true; - }; - - this.jail = function () { - // gonna use inner cloister wp and travel backwards - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); - - for (let i = sdk.areas.JailLvl3; i >= sdk.areas.JailLvl1; i -= 1) { - Pather.moveToExit(i, true) && this.clearGhosts(); - } - - return true; - }; - - this.cathedral = function () { - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.Cathedral, true); - this.clearGhosts(); - - return true; - }; - - this.tombs = function () { - Pather.useWaypoint(sdk.areas.CanyonofMagic); - Precast.doPrecast(true); - - for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i += 1) { - Pather.moveToExit(i, true) && this.clearGhosts(); - Pather.moveToExit(sdk.areas.CanyonofMagic, true); - } - - return true; - }; - - this.flayerDungeon = function () { - let areas = [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3]; - - Pather.useWaypoint(sdk.areas.FlayerJungle); - Precast.doPrecast(true); - - while (areas.length) { - Pather.moveToExit(areas.shift(), true) && this.clearGhosts(); - } - - return true; - }; - - this.crystalinePassage = function () { - Pather.useWaypoint(sdk.areas.CrystalizedPassage); - Precast.doPrecast(true); - this.clearGhosts(); - Pather.moveToExit(sdk.areas.FrozenRiver, true) && this.clearGhosts(); - - return true; - }; - - this.glacialTrail = function () { - Pather.useWaypoint(sdk.areas.GlacialTrail); - Precast.doPrecast(true); - this.clearGhosts(); - Pather.moveToExit(sdk.areas.DrifterCavern, true) && this.clearGhosts(); + const tasks = new Map([ + ["cellar", () => { + Pather.useWaypoint(sdk.areas.BlackMarsh); + Precast.doPrecast(true); - return true; - }; - - this.icyCellar = function () { - Pather.useWaypoint(sdk.areas.AncientsWay); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.IcyCellar, true) && this.clearGhosts(); - - return true; - }; - - let sequence = ["cellar", "jail", "cathedral", "tombs", "flayerDungeon", "crystalinePassage", "glacialTrail", "icyCellar"]; - - for (let i = 0; i < sequence.length; i += 1) { + for (let i = sdk.areas.ForgottenTower; i <= sdk.areas.TowerCellarLvl5; i += 1) { + Pather.moveToExit(i, true) && clearGhosts(); + } + }], + ["jail", () => { + // gonna use inner cloister wp and travel backwards + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); + + for (let i = sdk.areas.JailLvl3; i >= sdk.areas.JailLvl1; i -= 1) { + Pather.moveToExit(i, true) && clearGhosts(); + } + }], + ["cathedral", () => { + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.Cathedral, true); + clearGhosts(); + }], + ["tombs", () => { + Pather.useWaypoint(sdk.areas.CanyonofMagic); + Precast.doPrecast(true); + + for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i += 1) { + Pather.moveToExit(i, true) && clearGhosts(); + Pather.moveToExit(sdk.areas.CanyonofMagic, true); + } + }], + ["flayerDungeon", () => { + Pather.useWaypoint(sdk.areas.FlayerJungle); + Precast.doPrecast(true); + + [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3].forEach(area => { + Pather.moveToExit(area, true) && clearGhosts(); + }); + }], + ["crystalinePassage", () => { + Pather.useWaypoint(sdk.areas.CrystalizedPassage); + Precast.doPrecast(true); + clearGhosts(); + Pather.moveToExit(sdk.areas.FrozenRiver, true) && clearGhosts(); + }], + ["glacialTrail", () => { + Pather.useWaypoint(sdk.areas.GlacialTrail); + Precast.doPrecast(true); + clearGhosts(); + Pather.moveToExit(sdk.areas.DrifterCavern, true) && clearGhosts(); + }], + ["icyCellar", () => { + Pather.useWaypoint(sdk.areas.AncientsWay); + Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.IcyCellar, true) && clearGhosts(); + }] + ]); + + tasks.forEach(task => { Town.doChores(); try { - this[sequence[i]](); + task(); } finally { Town.goToTown(); } - } + }); return true; } diff --git a/d2bs/kolbot/libs/scripts/Tombs.js b/d2bs/kolbot/libs/scripts/Tombs.js index e0b2e017c..d7990df1c 100644 --- a/d2bs/kolbot/libs/scripts/Tombs.js +++ b/d2bs/kolbot/libs/scripts/Tombs.js @@ -9,14 +9,15 @@ function Tombs() { Town.doChores(); Pather.useWaypoint(sdk.areas.CanyonofMagic); Precast.doPrecast(true); + const correctTomb = getRoom().correcttomb; - for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i += 1) { + for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i++) { try { if (!Pather.journeyTo(i, true)) throw new Error("Failed to move to tomb"); Attack.clearLevel(Config.ClearType); - if (Config.Tombs.KillDuriel && me.area === getRoom().correcttomb) { + if (Config.Tombs.KillDuriel && me.inArea(correctTomb)) { Pather.journeyTo(sdk.areas.DurielsLair) && Attack.kill(sdk.monsters.Duriel); Pather.journeyTo(sdk.areas.CanyonofMagic); } diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 5284fd6ab..d34b2e0cc 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -8,8 +8,7 @@ function Wakka () { include("core/Common/Diablo.js"); const timeout = Config.Wakka.Wait; - const minDist = 50; - const maxDist = 80; + const [minDist, maxDist] = [50, 80]; const internals = { safeTP: false, coordsInit: false, @@ -22,11 +21,10 @@ function Wakka () { }; let portal, tick; - let leaderUnit = null; - let leaderPartyUnit = null; let leader = ""; + let [leaderUnit, leaderPartyUnit] = [null, null]; - this.checkMonsters = function (range = 15, dodge = false) { + const checkMonsters = function (range = 15, dodge = false) { let monList = []; let monster = Game.getMonster(); @@ -50,7 +48,7 @@ function Wakka () { return true; }; - this.getCoords = function () { + const getCoords = function () { if (!internals.coordsInit) { Common.Diablo.initLayout(); internals.vizCoords = Common.Diablo.vizLayout === 1 ? [7707, 5274] : [7708, 5298]; @@ -60,7 +58,7 @@ function Wakka () { } }; - this.checkBoss = function (name) { + const checkBoss = function (name) { let glow = Game.getObject(sdk.objects.SealGlow); if (glow) { @@ -84,7 +82,7 @@ function Wakka () { return false; }; - this.getCorpse = function () { + const getCorpse = function () { me.mode === sdk.player.mode.Dead && me.revive(); let rval = false; @@ -105,7 +103,7 @@ function Wakka () { return rval; }; - this.followPath = function (dest) { + const followPath = function (dest) { let path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 10); if (!path) throw new Error("Failed go get path"); @@ -115,7 +113,7 @@ function Wakka () { if (leaderUnit) { // monsters nearby - don't move - if (this.checkMonsters(45, true) && leaderUnit.distance <= maxDist) { + if (checkMonsters(45, true) && leaderUnit.distance <= maxDist) { path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); delay(200); @@ -130,7 +128,7 @@ function Wakka () { } // make sure distance to next node isn't too hot - if ([path[0].x, path[0].y].mobCount({range: 15}) !== 0) { + if ([path[0].x, path[0].y].mobCount({ range: 15 }) !== 0) { console.log("Mobs at next node"); // mobs, stay where we are delay(200); @@ -150,7 +148,7 @@ function Wakka () { } // if there's monsters between the leecher and leader, wait until monsters are dead or leader is out of maxDist range - if (this.checkMonsters(45, true) && getDistance(me, leaderPartyUnit.x, leaderPartyUnit.y) <= maxDist) { + if (checkMonsters(45, true) && getDistance(me, leaderPartyUnit.x, leaderPartyUnit.y) <= maxDist) { path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); delay(200); @@ -162,19 +160,19 @@ function Wakka () { Pather.moveTo(path[0].x, path[0].y) && path.shift(); // no mobs around us, so it's safe to pick - !me.checkForMobs({range: 10, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor)}) && Pickit.pickItems(5); - this.getCorpse(); + !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor) }) && Pickit.pickItems(5); + getCorpse(); } return true; }; - this.getLeaderUnitArea = function () { + const getLeaderUnitArea = function () { (!leaderUnit || !copyUnit(leaderUnit).x) && (leaderUnit = Game.getPlayer(leader)); return !!leaderUnit ? leaderUnit.area : getParty(leader).area; }; - this.log = function (msg = "") { + const log = function (msg = "") { me.overhead(msg); console.log(msg); }; @@ -215,7 +213,7 @@ function Wakka () { if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(this.getLeaderUnitArea())) { + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { if (Loader.scriptName() === "Wakka") { killLeaderTracker = true; throw new Error("Party leader is running baal"); @@ -290,26 +288,26 @@ function Wakka () { }; if (!internals.safeTP) { - if (this.checkMonsters(25, false)) { - this.log("hot tp"); + if (checkMonsters(25, false)) { + log("hot tp"); Pather.usePortal(sdk.areas.PandemoniumFortress, null); - this.getCorpse(); + getCorpse(); break; } else { - this.getCoords(); + getCoords(); internals.safeTP = true; } } if (!internals.vizClear) { - if (!this.followPath(internals.vizCoords)) { + if (!followPath(internals.vizCoords)) { console.debug("Failed to move to viz"); break; } - if (this.checkBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { - this.log("vizier dead"); + if (checkBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { + log("vizier dead"); internals.vizClear = true; Precast.doPrecast(true); tick = getTickCount(); @@ -323,13 +321,13 @@ function Wakka () { } if (internals.vizClear && !internals.seisClear) { - if (!this.followPath(internals.seisCoords)) { + if (!followPath(internals.seisCoords)) { console.debug("Failed to move to seis"); break; } - if (this.checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { - this.log("seis dead"); + if (checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { + log("seis dead"); internals.seisClear = true; Precast.doPrecast(true); tick = getTickCount(); @@ -343,13 +341,13 @@ function Wakka () { } if (internals.vizClear && internals.seisClear && !internals.infClear) { - if (!this.followPath(internals.infCoords)) { + if (!followPath(internals.infCoords)) { console.debug("Failed to move to infector"); break; } - if (this.checkBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { - this.log("infector dead"); + if (checkBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { + log("infector dead"); internals.infClear = true; Precast.doPrecast(true); tick = getTickCount(); @@ -381,7 +379,7 @@ function Wakka () { while (!diablo.dead) { delay(100); } - this.log("Diablo is dead"); + log("Diablo is dead"); if (!me.canTpToTown() || !Town.goToTown()) { Pather.usePortal(sdk.areas.PandemoniumFortress); @@ -389,7 +387,7 @@ function Wakka () { return true; } else { - this.log("Couldn't find diablo"); + log("Couldn't find diablo"); } } @@ -415,7 +413,7 @@ function Wakka () { throw new Error("No leader found"); } - this.log("Wakka complete"); + log("Wakka complete"); return true; } diff --git a/d2bs/kolbot/libs/scripts/Worldstone.js b/d2bs/kolbot/libs/scripts/Worldstone.js index 1d9fe0921..df8c5b807 100644 --- a/d2bs/kolbot/libs/scripts/Worldstone.js +++ b/d2bs/kolbot/libs/scripts/Worldstone.js @@ -1,6 +1,6 @@ /** * @filename Worldstone.js -* @author kolton +* @author kolton, theBGuy * @desc Clear Worldstone levels * */ From 3ff74bf60f4887a6c90b7d4d140cf1a8e2fc9f8c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 25 May 2023 14:47:17 -0400 Subject: [PATCH 122/758] Update Follower.js - redo act change method, map the pre req tasks, allow use of portal for faster act 2 -> 3 change when we have to talk to jerhyn --- d2bs/kolbot/libs/scripts/Follower.js | 137 ++++++++++++++++++++------- 1 file changed, 102 insertions(+), 35 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index d29788ad7..5fb3603eb 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -55,7 +55,9 @@ */ function Follower() { - const commanders = [Config.Leader]; + const QuestData = require("../core/GameData/QuestData"); + const commanders = []; + Config.Leader && commanders.push(Config.Leader); let piece, skill; let [allowSay, attack, openContainers, stop] = [true, true, true, false]; let [leader, leaderUnit] = [null, null]; @@ -147,7 +149,13 @@ function Follower() { return true; } catch (e) { console.error(e); - announce((typeof e === "object" && e.message ? e.message : typeof e === "string" ? e : "Failed to talk to " + name)); + announce( + (typeof e === "object" && e.message + ? e.message + : typeof e === "string" + ? e + : "Failed to talk to " + name) + ); return false; } finally { @@ -168,47 +176,106 @@ function Follower() { return false; } - switch (act) { - case 2: - Town.npcInteract("Warriv", false) && Misc.useMenu(sdk.menu.GoEast); + const npcTravel = new Map([ + [1, ["Warriv", sdk.areas.RogueEncampment]], + [2, [(me.act === 1 ? "Warriv" : "Meshif"), sdk.areas.LutGholein]], + [3, ["Meshif", sdk.areas.KurastDocktown]], + [4, ["", sdk.areas.PandemoniumFortress]], + [5, ["Tyrael", sdk.areas.Harrogath]], + ]); + + const preCheck = new Map([ + [ + 2, + () => QuestData.get(sdk.quest.id.SistersToTheSlaughter).complete(true) + ], + [ + 3, + () => { + if (QuestData.get(sdk.quest.id.TheSevenTombs).complete()) return true; + if (!QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/)) { + Town.npcInteract("Jerhyn"); + if (me.getTpTool()) { + Pather.moveToExit(sdk.areas.HaremLvl1, true); + Pather.usePortal(null) || Pather.makePortal(true); + } + } + return QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/); + } + ], + [ + 4, + () => { + if (me.inTown) { + if (!QuestData.get(sdk.quest.id.TheBlackenedTemple).complete()) { + Town.npcInteract("Cain"); + } + Town.move("portalspot"); + Pather.usePortal(sdk.areas.DuranceofHateLvl3, null); + } + return me.inArea(sdk.areas.DuranceofHateLvl3); + } + ], + [ + 5, + () => { + if (!QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/)) { + Town.npcInteract("Tyrael"); + } + return QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/); + } + ] + ]); - break; - case 3: - Town.npcInteract("Jerhyn"); - Town.move("Meshif") && Misc.useMenu(sdk.menu.SailEast); + if (!preCheck.get(act)()) { + announce("Failed act " + act + " precheck"); + return false; + } - break; - case 4: - if (me.inTown) { - Town.npcInteract("Cain"); - Town.move("portalspot"); - Pather.usePortal(sdk.areas.DuranceofHateLvl3, null); + if (act !== 4) { + let [npc, loc] = npcTravel.get(act); + 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); + } } - delay(1500); - - let target = Game.getObject(sdk.objects.RedPortalToAct4); - target && Pather.moveTo(target.x - 3, target.y - 1); - - Pather.usePortal(null); - - break; - case 5: - if (Town.npcInteract("Tyrael")) { - try { - Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath); - } catch (a5e) { - break; + if (npcUnit) { + for (let i = 0; i < 5; i++) { + new PacketBuilder() + .byte(sdk.packets.send.EntityAction) + .dword(0) + .dword(npcUnit.gid) + .dword(loc) + .send(); + delay(1000); + + if (me.act === act) { + break; + } } } + } else { + if (me.inArea(sdk.areas.DuranceofHateLvl3)) { + let target = Game.getObject(sdk.objects.RedPortalToAct4); + target && Pather.moveTo(target.x - 3, target.y - 1); - break; + Pather.usePortal(null); + } } - delay(2000); - - while (!me.area) { - delay(500); + while (!me.gameReady) { + delay(100); } if (me.area === preArea) { @@ -377,7 +444,7 @@ function Follower() { } } - if (msg && msg.split(" ")[0] === "leader" && commanders.indexOf(nick) > -1) { + if (msg && msg.split(" ")[0] === "leader" && (commanders.includes(nick) || !commanders.length)) { piece = msg.split(" ")[1]; if (typeof piece === "string") { From c14ca4c849e9252ad6f02db048dac6acf80f0f24 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 25 May 2023 14:55:28 -0400 Subject: [PATCH 123/758] Update Town.js - Fix quitting game when bot is allowed to die --- d2bs/kolbot/libs/core/Town.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 491684403..a45fe0ddf 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -2400,7 +2400,9 @@ const Town = { } if (!me.inTown && !Pather.usePortal(null, 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"); + 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(); @@ -2409,16 +2411,17 @@ const Town = { scriptBroadcast("quit"); } else { if (!Misc.poll(() => { - if (me.inTown) return true; + if (me.inTown || me.dead) 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"); + // don't quit if this is a character that is allowed to die + if (Config.LifeChicken > 0) { + Misc.errorReport(new Error("Town.goToTown: Failed to go to town. Quiting.")); + scriptBroadcast("quit"); + } } } } From de2d3771c18d5cfee74cb6188dd1f676af4a09d3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 25 May 2023 17:30:36 -0400 Subject: [PATCH 124/758] Update TownChicken.js - Handle when someone uses townchicken but has lifechicken disabled. We don't want to kill the thread if character is dead then so delay until they are revived --- d2bs/kolbot/threads/TownChicken.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js index 390ffec84..6216ac18e 100644 --- a/d2bs/kolbot/threads/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -95,6 +95,12 @@ function main() { // We would then be able to remove all game interaction checks until we get a townCheck msg || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area + if (me.dead && Config.LifeChicken <= 0) { + console.log("ÿc1TownChicken :: ÿc0We are dead and LifeChicken is set to 0"); + while (me.dead) delay(100); + + continue; + } if (!me.canTpToTown()) { townCheck = false; From 55d527bb974b06802919067c7d83d22744d481fc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 25 May 2023 17:31:29 -0400 Subject: [PATCH 125/758] Update Follower.js - allow main actions to be applied to only a specific follower, rather than always being applied to all --- d2bs/kolbot/libs/scripts/Follower.js | 29 +++++++++++++--------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 5fb3603eb..db0f15953 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -10,7 +10,7 @@ * c - get corpse * p - pick items * s - toggle stop -* s - toggle stop +* - tell specific character to perform action * @Attack * a - attack toggle for all * a - attack toggle for @@ -398,10 +398,11 @@ function Follower() { break; default: - if (me.paladin && msg.includes("aura ")) { - piece = msg.split(" ")[0]; + let piecewise = msg.split(" "); + let who = piecewise.length > 1 && piecewise.first() || ""; - if (piece === me.name || piece === "all") { + if (me.paladin && msg.includes("aura ")) { + if (who === me.name || piece === "all") { skill = parseInt(msg.split(" ")[2], 10); if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { @@ -415,14 +416,8 @@ function Follower() { announce("I don't have that aura."); } } - - break; - } - - if (msg.includes("skill ")) { - piece = msg.split(" ")[0]; - - if (charClass.includes(piece) || piece === me.name || piece === "all") { + } else if (msg.includes("skill ")) { + if (charClass.includes(who) || who === me.name || who === "all") { skill = parseInt(msg.split(" ")[2], 10); if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { @@ -434,12 +429,14 @@ function Follower() { announce("I don't have that skill."); } } - - break; + } else { + if (who && who !== me.name && who !== "all") { + return; + } + who && (msg = msg.replace(who, "").trim()); + action = msg; } - action = msg; - break; } } From a96eda89981e7f1bb62678c209c61e183f87c479 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 26 May 2023 18:56:56 -0400 Subject: [PATCH 126/758] formatting - Tabs -> 2 spaces - Set a max-len of 120 characters for code readability on smaller screens --- .eslintrc.js | 259 +- .vscode/settings.json | 7 + d2bs/kolbot/D2BotBlank.dbj | 61 +- d2bs/kolbot/D2BotChannel.dbj | 907 +- d2bs/kolbot/D2BotCleaner.dbj | 711 +- d2bs/kolbot/D2BotFollow.dbj | 753 +- d2bs/kolbot/D2BotGameAction.dbj | 663 +- d2bs/kolbot/D2BotLead.dbj | 665 +- d2bs/kolbot/D2BotMap.dbj | 63 +- d2bs/kolbot/D2BotMule.dbj | 1881 +- d2bs/kolbot/D2BotMuleLog.dbj | 633 +- d2bs/kolbot/D2BotPubJoin.dbj | 679 +- d2bs/kolbot/default.dbj | 473 +- d2bs/kolbot/libs/OOG.js | 2821 +-- d2bs/kolbot/libs/Polyfill.js | 1797 +- d2bs/kolbot/libs/config/Amazon.js | 19 +- d2bs/kolbot/libs/config/Assassin.js | 17 +- d2bs/kolbot/libs/config/Barbarian.js | 17 +- d2bs/kolbot/libs/config/Druid.js | 17 +- d2bs/kolbot/libs/config/Necromancer.js | 17 +- d2bs/kolbot/libs/config/Paladin.js | 17 +- d2bs/kolbot/libs/config/Sorceress.js | 17 +- d2bs/kolbot/libs/config/_BaseConfigFile.js | 19 +- d2bs/kolbot/libs/config/_CustomConfig.js | 2 +- d2bs/kolbot/libs/core/Attack.js | 3207 ++-- d2bs/kolbot/libs/core/Attacks/Amazon.js | 476 +- d2bs/kolbot/libs/core/Attacks/Assassin.js | 506 +- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 449 +- d2bs/kolbot/libs/core/Attacks/Druid.js | 374 +- d2bs/kolbot/libs/core/Attacks/Necromancer.js | 1113 +- d2bs/kolbot/libs/core/Attacks/Paladin.js | 711 +- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 470 +- d2bs/kolbot/libs/core/Attacks/Wereform.js | 316 +- d2bs/kolbot/libs/core/Auto/AutoBuild.js | 172 +- d2bs/kolbot/libs/core/Auto/AutoSkill.js | 260 +- d2bs/kolbot/libs/core/Auto/AutoStat.js | 1423 +- d2bs/kolbot/libs/core/CollMap.js | 293 +- d2bs/kolbot/libs/core/Common.js | 4 +- d2bs/kolbot/libs/core/Common/Ancients.js | 277 +- d2bs/kolbot/libs/core/Common/Baal.js | 590 +- d2bs/kolbot/libs/core/Common/Cain.js | 245 +- d2bs/kolbot/libs/core/Common/Cows.js | 112 +- d2bs/kolbot/libs/core/Common/Diablo.js | 1172 +- d2bs/kolbot/libs/core/Common/Leecher.js | 74 +- d2bs/kolbot/libs/core/Common/Smith.js | 36 +- d2bs/kolbot/libs/core/Common/Tools.js | 687 +- d2bs/kolbot/libs/core/Config.js | 1361 +- d2bs/kolbot/libs/core/Cubing.js | 2530 +-- d2bs/kolbot/libs/core/Experience.js | 197 +- d2bs/kolbot/libs/core/GameData/AreaData.js | 436 +- d2bs/kolbot/libs/core/GameData/GameData.js | 313 +- .../libs/core/GameData/LocaleStringID.js | 15592 ++++++++-------- d2bs/kolbot/libs/core/GameData/MonsterData.js | 136 +- d2bs/kolbot/libs/core/GameData/NTItemAlias.js | 63 +- d2bs/kolbot/libs/core/GameData/QuestData.js | 280 +- d2bs/kolbot/libs/core/GameData/RuneData.js | 452 +- d2bs/kolbot/libs/core/GameData/ShrineData.js | 96 +- d2bs/kolbot/libs/core/GameData/SkillData.js | 2768 +-- d2bs/kolbot/libs/core/Item.js | 1128 +- d2bs/kolbot/libs/core/Loader.js | 528 +- d2bs/kolbot/libs/core/Me.js | 1322 +- d2bs/kolbot/libs/core/Misc.js | 1462 +- d2bs/kolbot/libs/core/NTItemParser.js | 1039 +- d2bs/kolbot/libs/core/Packet.js | 747 +- d2bs/kolbot/libs/core/Pather.js | 3648 ++-- d2bs/kolbot/libs/core/Pickit.js | 1343 +- d2bs/kolbot/libs/core/Precast.js | 1208 +- d2bs/kolbot/libs/core/Prototypes.js | 4029 ++-- d2bs/kolbot/libs/core/Runewords.js | 516 +- d2bs/kolbot/libs/core/Skill.js | 926 +- d2bs/kolbot/libs/core/Storage.js | 1191 +- d2bs/kolbot/libs/core/Town.js | 4499 ++--- d2bs/kolbot/libs/core/Util.js | 957 +- d2bs/kolbot/libs/json2.js | 521 +- d2bs/kolbot/libs/manualplay/MapMode.js | 100 +- d2bs/kolbot/libs/manualplay/config/Amazon.js | 2 +- .../kolbot/libs/manualplay/config/Assassin.js | 2 +- .../libs/manualplay/config/Barbarian.js | 2 +- d2bs/kolbot/libs/manualplay/config/Druid.js | 2 +- .../libs/manualplay/config/Necromancer.js | 2 +- d2bs/kolbot/libs/manualplay/config/Paladin.js | 2 +- .../libs/manualplay/config/Sorceress.js | 2 +- .../libs/manualplay/hooks/ActionHooks.js | 1511 +- d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js | 297 +- .../kolbot/libs/manualplay/hooks/ItemHooks.js | 473 +- .../libs/manualplay/hooks/MonsterHooks.js | 194 +- .../libs/manualplay/hooks/ShrineHooks.js | 160 +- .../kolbot/libs/manualplay/hooks/TextHooks.js | 597 +- .../libs/manualplay/hooks/VectorHooks.js | 774 +- .../libs/manualplay/libs/AttackOverrides.js | 36 +- .../libs/manualplay/libs/ConfigOverrides.js | 201 +- .../libs/manualplay/libs/MiscOverrides.js | 282 +- .../libs/manualplay/libs/PatherOverrides.js | 699 +- .../libs/manualplay/libs/PickitOverrides.js | 40 +- .../libs/manualplay/libs/TownOverrides.js | 77 +- .../libs/manualplay/threads/MapHelper.js | 783 +- .../libs/manualplay/threads/MapThread.js | 551 +- .../libs/manualplay/threads/MapToolsThread.js | 529 +- .../libs/manualplay/threads/PickThread.js | 66 +- d2bs/kolbot/libs/modules/Control.js | 403 +- d2bs/kolbot/libs/modules/CopyData.js | 96 +- d2bs/kolbot/libs/modules/Deltas.js | 54 +- d2bs/kolbot/libs/modules/Events.js | 86 +- d2bs/kolbot/libs/modules/Guard.js | 140 +- d2bs/kolbot/libs/modules/HTTP.js | 86 +- d2bs/kolbot/libs/modules/LocalChat.js | 121 +- d2bs/kolbot/libs/modules/Messaging.js | 58 +- d2bs/kolbot/libs/modules/Override.js | 104 +- d2bs/kolbot/libs/modules/Promise.js | 162 +- d2bs/kolbot/libs/modules/SimpleParty.js | 311 +- d2bs/kolbot/libs/modules/Socket.js | 98 +- d2bs/kolbot/libs/modules/Team.js | 307 +- d2bs/kolbot/libs/modules/UnitInfo.js | 365 +- d2bs/kolbot/libs/modules/Worker.js | 227 +- d2bs/kolbot/libs/modules/sdk.js | 10035 +++++----- d2bs/kolbot/libs/oog/D2Bot.js | 336 +- d2bs/kolbot/libs/oog/DataFile.js | 190 +- d2bs/kolbot/libs/oog/FileAction.js | 118 +- d2bs/kolbot/libs/oog/ShitList.js | 66 +- d2bs/kolbot/libs/require.js | 241 +- d2bs/kolbot/libs/scripts/Abaddon.js | 17 +- d2bs/kolbot/libs/scripts/AncientTunnels.js | 37 +- d2bs/kolbot/libs/scripts/Andariel.js | 40 +- d2bs/kolbot/libs/scripts/AutoBaal.js | 465 +- d2bs/kolbot/libs/scripts/Baal.js | 172 +- d2bs/kolbot/libs/scripts/BaalAssistant.js | 936 +- d2bs/kolbot/libs/scripts/BaalHelper.js | 224 +- d2bs/kolbot/libs/scripts/BattleOrders.js | 504 +- d2bs/kolbot/libs/scripts/BattlemaidSarina.js | 18 +- d2bs/kolbot/libs/scripts/Bishibosh.js | 14 +- d2bs/kolbot/libs/scripts/BoBarbHelper.js | 158 +- d2bs/kolbot/libs/scripts/BoneAsh.js | 14 +- d2bs/kolbot/libs/scripts/Bonesaw.js | 19 +- d2bs/kolbot/libs/scripts/ChestMania.js | 37 +- .../libs/scripts/ClassicChaosAssistant.js | 196 +- d2bs/kolbot/libs/scripts/ClearAnyArea.js | 18 +- d2bs/kolbot/libs/scripts/Coldcrow.js | 16 +- d2bs/kolbot/libs/scripts/Coldworm.js | 46 +- d2bs/kolbot/libs/scripts/ControlBot.js | 1220 +- d2bs/kolbot/libs/scripts/Corpsefire.js | 18 +- d2bs/kolbot/libs/scripts/Countess.js | 40 +- d2bs/kolbot/libs/scripts/Cows.js | 282 +- d2bs/kolbot/libs/scripts/Crafting.js | 742 +- d2bs/kolbot/libs/scripts/CreepingFeature.js | 14 +- d2bs/kolbot/libs/scripts/CrushTele.js | 84 +- d2bs/kolbot/libs/scripts/DeveloperMode.js | 544 +- d2bs/kolbot/libs/scripts/Diablo.js | 122 +- d2bs/kolbot/libs/scripts/DiabloHelper.js | 320 +- d2bs/kolbot/libs/scripts/Duriel.js | 69 +- d2bs/kolbot/libs/scripts/Eldritch.js | 61 +- d2bs/kolbot/libs/scripts/Endugu.js | 16 +- d2bs/kolbot/libs/scripts/Eyeback.js | 16 +- d2bs/kolbot/libs/scripts/Fangskin.js | 24 +- d2bs/kolbot/libs/scripts/Follower.js | 1204 +- d2bs/kolbot/libs/scripts/Frozenstein.js | 19 +- d2bs/kolbot/libs/scripts/Gamble.js | 78 +- d2bs/kolbot/libs/scripts/GemHunter.js | 46 +- d2bs/kolbot/libs/scripts/GetCube.js | 39 +- d2bs/kolbot/libs/scripts/GetKeys.js | 46 +- d2bs/kolbot/libs/scripts/GhostBusters.js | 208 +- d2bs/kolbot/libs/scripts/Hephasto.js | 26 +- d2bs/kolbot/libs/scripts/IPHunter.js | 83 +- d2bs/kolbot/libs/scripts/Icehawk.js | 16 +- d2bs/kolbot/libs/scripts/Izual.js | 18 +- d2bs/kolbot/libs/scripts/KillDclone.js | 22 +- d2bs/kolbot/libs/scripts/KurastTemples.js | 50 +- d2bs/kolbot/libs/scripts/MFHelper.js | 521 +- d2bs/kolbot/libs/scripts/Mausoleum.js | 64 +- d2bs/kolbot/libs/scripts/Mephisto.js | 283 +- d2bs/kolbot/libs/scripts/Nihlathak.js | 44 +- d2bs/kolbot/libs/scripts/OrgTorch.js | 1130 +- d2bs/kolbot/libs/scripts/OuterSteppes.js | 14 +- d2bs/kolbot/libs/scripts/Pindleskin.js | 84 +- d2bs/kolbot/libs/scripts/Pit.js | 20 +- d2bs/kolbot/libs/scripts/Questing.js | 724 +- d2bs/kolbot/libs/scripts/Radament.js | 21 +- d2bs/kolbot/libs/scripts/Rakanishu.js | 24 +- d2bs/kolbot/libs/scripts/Rushee.js | 2001 +- d2bs/kolbot/libs/scripts/Rusher.js | 424 +- d2bs/kolbot/libs/scripts/SealLeecher.js | 180 +- d2bs/kolbot/libs/scripts/SharpTooth.js | 20 +- d2bs/kolbot/libs/scripts/ShopBot.js | 540 +- d2bs/kolbot/libs/scripts/Smith.js | 18 +- d2bs/kolbot/libs/scripts/Snapchip.js | 19 +- d2bs/kolbot/libs/scripts/Stormtree.js | 16 +- d2bs/kolbot/libs/scripts/Summoner.js | 104 +- d2bs/kolbot/libs/scripts/Synch.js | 66 +- d2bs/kolbot/libs/scripts/Synch2.js | 70 +- d2bs/kolbot/libs/scripts/Test.js | 40 +- d2bs/kolbot/libs/scripts/ThreshSocket.js | 18 +- d2bs/kolbot/libs/scripts/Tombs.js | 36 +- d2bs/kolbot/libs/scripts/Travincal.js | 104 +- d2bs/kolbot/libs/scripts/TravincalLeech.js | 108 +- d2bs/kolbot/libs/scripts/Treehead.js | 16 +- d2bs/kolbot/libs/scripts/Tristram.js | 110 +- d2bs/kolbot/libs/scripts/TristramLeech.js | 217 +- .../kolbot/libs/scripts/UndergroundPassage.js | 16 +- d2bs/kolbot/libs/scripts/UserAddon.js | 174 +- d2bs/kolbot/libs/scripts/WPGetter.js | 29 +- d2bs/kolbot/libs/scripts/Wakka.js | 821 +- d2bs/kolbot/libs/scripts/Worldstone.js | 42 +- d2bs/kolbot/libs/starter/AdvancedConfig.js | 20 +- d2bs/kolbot/libs/starter/StarterConfig.js | 64 +- d2bs/kolbot/libs/systems/automule/AutoMule.js | 1291 +- .../libs/systems/automule/MuleConfig.js | 58 +- .../libs/systems/automule/TorchAnniMules.js | 48 +- .../libs/systems/crafting/CraftingSystem.js | 608 +- .../libs/systems/crafting/TeamsConfig.js | 48 +- d2bs/kolbot/libs/systems/gambling/Gambling.js | 218 +- .../libs/systems/gambling/TeamsConfig.js | 22 +- .../libs/systems/gameaction/GameAction.js | 383 +- .../libs/systems/mulelogger/LoggerConfig.js | 32 +- .../libs/systems/mulelogger/MuleLogger.js | 375 +- .../kolbot/libs/systems/torch/FarmerConfig.js | 24 +- d2bs/kolbot/libs/systems/torch/TorchSystem.js | 624 +- d2bs/kolbot/threads/AntiHostile.js | 733 +- d2bs/kolbot/threads/AntiIdle.js | 22 +- d2bs/kolbot/threads/AreaWatcher.js | 32 +- d2bs/kolbot/threads/AutoBuildThread.js | 399 +- d2bs/kolbot/threads/CloneKilla.js | 50 +- d2bs/kolbot/threads/HeartBeat.js | 88 +- d2bs/kolbot/threads/Party.js | 418 +- d2bs/kolbot/threads/RushThread.js | 2001 +- d2bs/kolbot/threads/ToolsThread.js | 851 +- d2bs/kolbot/threads/TownChicken.js | 234 +- 225 files changed, 61056 insertions(+), 59821 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.eslintrc.js b/.eslintrc.js index f81bdf457..bdae662b9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,133 +3,134 @@ // Compatible with ESLint plugin module.exports = { - "root": true, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaFeatures": { - "impliedStrict": true, - } - }, - "env": { - "es6": true, - }, - "globals": { - // The following globals are defined within D2BS, or are actually defined in the source code - "include": true, - "print": true, - "me": true, - "td": true, - "getTickCount": true, - "delay": true, - "getParty": true, - "takeScreenshot": true, - "getUnit": true, - "quit": true, - "clickMap": true, - "getBaseStat": true, - "clickItem": true, - "getCursorType": true, - "getPresetUnits": true, - "getDistance": true, - "copyUnit": true, - "getRoom": true, - "getLocaleString": true, - "scriptBroadcast": true, - "isIncluded": true, - "showConsole": true, - "getInteractedNPC": true, - "getDialogLines": true, - "getUIFlag": true, - "sendPacket": true, - "getPacket": true, - "getPath": true, - "rand": true, - "PresetUnit": true, - "getPresetUnit": true, - "getArea": true, - "getWaypoint": true, - "getScript": true, - "Room": true, - "say": true, - "load": true, - "addEventListener": true, - "getMercHP": true, - "checkCollision": true, - "gold": true, - "getLocation": true, - "login": true, - "sendCopyData": true, - "getControl": true, - "debugLog": true, - "getCollision": true, - "transmute": true, - "submitItem": true, - "createGame": true, - "joinGame": true, - "Line": true, - "removeEventListener": true, - "Unit": true, - "Party": true, - "UtilitySystem": true, - "moveNPC": true, - "getPlayerFlag": true, - "clickParty": true, - "dopen": true, - "Items": true, - "Text": true, - "File": true, - "js_strict": true, - "handler": true, - "sendKey": true, - "md5": true, - "module": true, - "require": true, - "Box": true, - "Frame": true, - "revealLevel": true, - "hideConsole": true, - }, - "rules": { - // enable additional rules - "indent": ["warn", "tab"], - "linebreak-style": ["off", "windows"], - "semi": ["error", "always"], - "comma-spacing": ["error", { "before": false, "after": true }], - "keyword-spacing": ["error", { "before": true, "after": true }], - "object-curly-spacing": ["error", "always"], - "brace-style": ["error", "1tbs", { "allowSingleLine": true }], - "space-infix-ops": "error", - "space-unary-ops": ["error", { "words": true, "nonwords": false }], - "arrow-spacing": "error", - "arrow-body-style": ["error", "as-needed"], - "space-before-blocks": "error", - "key-spacing": ["error", { "mode": "strict", "beforeColon": false, "afterColon": true }], - "no-mixed-spaces-and-tabs": "error", - "no-trailing-spaces": ["warn", { "ignoreComments": true, "skipBlankLines": true }], - "no-whitespace-before-property": "error", - "comma-style": ["error", "last"], - "eol-last": ["error", "always"], - "block-scoped-var": "error", - "no-var": "warn", - "curly": ["error", "multi-line"], - "dot-notation": "warn", - "eqeqeq": ["error", "smart"], - "no-caller": "error", - "no-floating-decimal": "error", - "no-multi-spaces": ["error", { "ignoreEOLComments": true }], - "no-self-compare": "error", - "no-case-declarations": "off", - "no-with": "error", - "no-shadow": "off", - "no-use-before-define": "off", - "no-prototype-builtins": "off", - "quotes": ["warn", "double", { "avoidEscape": true }], - "no-constant-condition": ["error", { "checkLoops": false }], - "no-extra-label": "error", - //"no-labels": ["error", {"allowLoop": true}], // in the future no loops ;) - "no-unused-vars": ["warn", { "vars": "local" }], - "no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted" }], - "no-undef": ["off", "always"], - "no-extra-boolean-cast": ["off", "always"], - } + "root": true, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaFeatures": { + "impliedStrict": true, + } + }, + "env": { + "es6": true, + }, + "globals": { + // The following globals are defined within D2BS, or are actually defined in the source code + "include": true, + "print": true, + "me": true, + "td": true, + "getTickCount": true, + "delay": true, + "getParty": true, + "takeScreenshot": true, + "getUnit": true, + "quit": true, + "clickMap": true, + "getBaseStat": true, + "clickItem": true, + "getCursorType": true, + "getPresetUnits": true, + "getDistance": true, + "copyUnit": true, + "getRoom": true, + "getLocaleString": true, + "scriptBroadcast": true, + "isIncluded": true, + "showConsole": true, + "getInteractedNPC": true, + "getDialogLines": true, + "getUIFlag": true, + "sendPacket": true, + "getPacket": true, + "getPath": true, + "rand": true, + "PresetUnit": true, + "getPresetUnit": true, + "getArea": true, + "getWaypoint": true, + "getScript": true, + "Room": true, + "say": true, + "load": true, + "addEventListener": true, + "getMercHP": true, + "checkCollision": true, + "gold": true, + "getLocation": true, + "login": true, + "sendCopyData": true, + "getControl": true, + "debugLog": true, + "getCollision": true, + "transmute": true, + "submitItem": true, + "createGame": true, + "joinGame": true, + "Line": true, + "removeEventListener": true, + "Unit": true, + "Party": true, + "UtilitySystem": true, + "moveNPC": true, + "getPlayerFlag": true, + "clickParty": true, + "dopen": true, + "Items": true, + "Text": true, + "File": true, + "js_strict": true, + "handler": true, + "sendKey": true, + "md5": true, + "module": true, + "require": true, + "Box": true, + "Frame": true, + "revealLevel": true, + "hideConsole": true, + }, + "rules": { + // enable additional rules + "indent": ["warn", 2], + "linebreak-style": ["off", "windows"], + "semi": ["error", "always"], + "comma-spacing": ["error", { "before": false, "after": true }], + "keyword-spacing": ["error", { "before": true, "after": true }], + "object-curly-spacing": ["error", "always"], + "brace-style": ["error", "1tbs", { "allowSingleLine": true }], + "space-infix-ops": "error", + "space-unary-ops": ["error", { "words": true, "nonwords": false }], + "arrow-spacing": "error", + "arrow-body-style": ["error", "as-needed"], + "space-before-blocks": "error", + "key-spacing": ["error", { "mode": "strict", "beforeColon": false, "afterColon": true }], + "no-mixed-spaces-and-tabs": "error", + "no-trailing-spaces": ["warn", { "ignoreComments": true, "skipBlankLines": true }], + "no-whitespace-before-property": "error", + "comma-style": ["error", "last"], + "eol-last": ["error", "always"], + "block-scoped-var": "error", + "no-var": "warn", + "curly": ["error", "multi-line"], + "dot-notation": "warn", + "eqeqeq": ["error", "smart"], + "no-caller": "error", + "no-floating-decimal": "error", + "no-multi-spaces": ["error", { "ignoreEOLComments": true }], + "no-self-compare": "error", + "no-case-declarations": "off", + "no-with": "error", + "no-shadow": "off", + "no-use-before-define": "off", + "no-prototype-builtins": "off", + "quotes": ["warn", "double", { "avoidEscape": true }], + "no-constant-condition": ["error", { "checkLoops": false }], + "no-extra-label": "error", + //"no-labels": ["error", {"allowLoop": true}], // in the future no loops ;) + "no-unused-vars": ["warn", { "vars": "local" }], + "no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted" }], + "no-undef": ["off", "always"], + "no-extra-boolean-cast": ["off", "always"], + "max-len": ["warn", { "code": 120, "ignoreComments": true, "ignoreUrls": true }], + } }; diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..68e0b66f9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.rulers": [ + 120 + ], + "editor.tabSize": 2, + "editor.detectIndentation": false +} \ No newline at end of file diff --git a/d2bs/kolbot/D2BotBlank.dbj b/d2bs/kolbot/D2BotBlank.dbj index 826c2e2c9..dcea8cda8 100644 --- a/d2bs/kolbot/D2BotBlank.dbj +++ b/d2bs/kolbot/D2BotBlank.dbj @@ -7,39 +7,44 @@ function main() { - include("critical.js"); // required + include("critical.js"); // required - if (!FileTools.exists("data/" + me.profile + ".json")) { - DataFile.create(); - } + if (!FileTools.exists("data/" + me.profile + ".json")) { + DataFile.create(); + } - addEventListener("copydata", Starter.receiveCopyData); + addEventListener("copydata", Starter.receiveCopyData); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - delay(500); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - while (true) { - delay(1000); + while (true) { + delay(1000); - if (me.gameReady) { - Starter.isUp === "no" && (Starter.isUp = "yes"); - me.ingame && D2Bot.updateStatus("(Char: " + me.charname + ") (Game: " + (me.gamename || "singleplayer") + ") (Level: " + me.charlvl + ")"); - } else { - D2Bot.updateStatus("Out of Game"); - Starter.isUp = "no"; - } - - delay(1000); - } + if (me.gameReady) { + Starter.isUp === "no" && (Starter.isUp = "yes"); + if (me.ingame) { + D2Bot.updateStatus( + "(Char: " + me.charname + ") (Game: " + + (me.gamename || "singleplayer") + ") (Level: " + me.charlvl + ")" + ); + } + } else { + D2Bot.updateStatus("Out of Game"); + Starter.isUp = "no"; + } + + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index d6e28a470..835763efb 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename D2BotChannel.dbj * @author kolton, theBGuy @@ -29,10 +30,10 @@ let Controls = require("./modules/Control"); let Overrides = require("./modules/Override"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { - Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); + Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); } else { - // no need to carry around the reference - delete Starter.AdvancedConfig; + // no need to carry around the reference + delete Starter.AdvancedConfig; } let channelTick = getTickCount(); @@ -41,497 +42,497 @@ let retry = 0; let badGames = []; let lastText; let joinInfo = { - gameName: "", - gamePass: "", - oldGame: "", - inGame: false + gameName: "", + gamePass: "", + oldGame: "", + inGame: false }; if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { - Starter.firstRun = true; + Starter.firstRun = true; } new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { - if (mode === 3) { - Starter.isUp = (me.gameReady ? "yes" : "no"); - if (!me.gameReady) { - return; - } - Starter.gameInfo.gameName = (me.gamename || ""); - Starter.gameInfo.gamePass = (me.gamepassword || ""); - } else { - orignal(mode, msg); - } + if (mode === 3) { + Starter.isUp = (me.gameReady ? "yes" : "no"); + if (!me.gameReady) { + return; + } + Starter.gameInfo.gameName = (me.gamename || ""); + Starter.gameInfo.gamePass = (me.gamepassword || ""); + } else { + orignal(mode, msg); + } }).apply(); function locationAction (location) { - let i, n, string, text, regex, fullText, lines; - - MainSwitch: - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); - - me.blockKeys = false; - Starter.loginRetry = 0; - !Starter.firstLogin && (Starter.firstLogin = true); - Controls.LobbyEnterChat.click(); - - break; - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby Chat"); - - if (Starter.inGame) { - if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } - - print("updating runs"); - D2Bot.updateRuns(); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - retry = 0; - } - - // Muted key handler - fullText = ""; - lines = Controls.LobbyChat.getText(); - - if (!lines) { - break; - } - - fullText = lines.join(" ").replace(/\s+/g, " "); - - if (fullText.match(Starter.Config.MutedKeyTrigger.replace(/\s+/g, " "), "gi")) { - D2Bot.printToConsole(Starter.gameInfo.mpq + " is muted.", sdk.colors.D2Bot.Gold); + let i, n, string, text, regex, fullText, lines; + + MainSwitch: + switch (location) { + case sdk.game.locations.PreSplash: + ControlAction.click(); + + break; + case sdk.game.locations.Lobby: + D2Bot.updateStatus("Lobby"); + + me.blockKeys = false; + Starter.loginRetry = 0; + !Starter.firstLogin && (Starter.firstLogin = true); + Controls.LobbyEnterChat.click(); + + break; + case sdk.game.locations.LobbyChat: + D2Bot.updateStatus("Lobby Chat"); + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { + break; + } + + print("updating runs"); + D2Bot.updateRuns(); + + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + retry = 0; + } + + // Muted key handler + fullText = ""; + lines = Controls.LobbyChat.getText(); + + if (!lines) { + break; + } + + fullText = lines.join(" ").replace(/\s+/g, " "); + + if (fullText.match(Starter.Config.MutedKeyTrigger.replace(/\s+/g, " "), "gi")) { + D2Bot.printToConsole(Starter.gameInfo.mpq + " is muted.", sdk.colors.D2Bot.Gold); - ControlAction.mutedKey = true; - - if (Starter.Config.SkipMutedKey) { - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); - } - } - } + ControlAction.mutedKey = true; + + if (Starter.Config.SkipMutedKey) { + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } + } + } - if (!ControlAction.mutedKey && (!Starter.chatActionsDone || getTickCount() - channelTick >= 120e3)) { - if (Starter.Config.JoinChannel !== "") { - joinInfo.joinChannel = Starter.Config.JoinChannel; + if (!ControlAction.mutedKey && (!Starter.chatActionsDone || getTickCount() - channelTick >= 120e3)) { + if (Starter.Config.JoinChannel !== "") { + joinInfo.joinChannel = Starter.Config.JoinChannel; - if (joinInfo.joinChannel) { - if (ControlAction.joinChannel(joinInfo.joinChannel)) { - Starter.useChat = true; - } else { - print("Unable to join channel, chat messages disabled."); - - Starter.useChat = false; - } - } - } + if (joinInfo.joinChannel) { + if (ControlAction.joinChannel(joinInfo.joinChannel)) { + Starter.useChat = true; + } else { + print("Unable to join channel, chat messages disabled."); + + Starter.useChat = false; + } + } + } - if (Starter.Config.Follow.length > 0 && !Starter.channelNotify) { - Starter.sayMsg("/d2notify"); - lines = Controls.LobbyChat.getText(); - - if (!lines) { - break; - } - - if (lines.some(line => line.match("notifications are disabled"))) { - Starter.sayMsg("/d2notify"); - lines = Controls.LobbyChat.getText(); - } - - if (lines.some(line => line.match("notifications are enabled"))) { - Starter.channelNotify = true; - } - } - - // Added !chatActionsDone condition to prevent spam - if (Starter.Config.FirstJoinMessage !== "" && !Starter.chatActionsDone) { - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); - Starter.sayMsg(Starter.Config.FirstJoinMessage); - delay(500); - } - - Starter.chatActionsDone = true; - channelTick = getTickCount(); - } - - if (Starter.Config.FriendListQuery > 0 && getTickCount() - fListTick >= Starter.Config.FriendListQuery * 1000) { - say("/f l"); - - fListTick = getTickCount(); - } - - switch (Starter.lastGameStatus) { - case "pending": // Most likely FTJ (can't detect it directly) - string = ""; - text = Controls.LobbyServerDown.getText(); - - if (text) { - for (i = 0; i < text.length; i += 1) { - string += text[i]; - - if (i !== text.length - 1) { - string += " "; - } - } - - // Didn't meet level restriction - if (string === getLocaleString(sdk.locale.text.DoNotMeetLevelReqForThisGame)) { - print(string); - - retry = Starter.Config.JoinRetry; - - break; - } - } - - retry += 1; - - D2Bot.updateRuns(); - - if (retry < Starter.Config.JoinRetry) { - Controls.JoinGameWindow.click(); - - break MainSwitch; - } - - break; - case "DNE": // Game didn't exist - retry += 1; - - break; - case "FULL": // Game is full - retry = Starter.Config.JoinRetry; - - break; - } - - if (retry >= Starter.Config.JoinRetry) { - D2Bot.printToConsole("Failed to join " + joinInfo.gameName + ". Aborting."); - badGames.push(joinInfo.gameName); + if (Starter.Config.Follow.length > 0 && !Starter.channelNotify) { + Starter.sayMsg("/d2notify"); + lines = Controls.LobbyChat.getText(); + + if (!lines) { + break; + } + + if (lines.some(line => line.match("notifications are disabled"))) { + Starter.sayMsg("/d2notify"); + lines = Controls.LobbyChat.getText(); + } + + if (lines.some(line => line.match("notifications are enabled"))) { + Starter.channelNotify = true; + } + } + + // Added !chatActionsDone condition to prevent spam + if (Starter.Config.FirstJoinMessage !== "" && !Starter.chatActionsDone) { + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + Starter.sayMsg(Starter.Config.FirstJoinMessage); + delay(500); + } + + Starter.chatActionsDone = true; + channelTick = getTickCount(); + } + + if (Starter.Config.FriendListQuery > 0 && getTickCount() - fListTick >= Starter.Config.FriendListQuery * 1000) { + say("/f l"); + + fListTick = getTickCount(); + } + + switch (Starter.lastGameStatus) { + case "pending": // Most likely FTJ (can't detect it directly) + string = ""; + text = Controls.LobbyServerDown.getText(); + + if (text) { + for (i = 0; i < text.length; i += 1) { + string += text[i]; + + if (i !== text.length - 1) { + string += " "; + } + } + + // Didn't meet level restriction + if (string === getLocaleString(sdk.locale.text.DoNotMeetLevelReqForThisGame)) { + print(string); + + retry = Starter.Config.JoinRetry; + + break; + } + } + + retry += 1; + + D2Bot.updateRuns(); + + if (retry < Starter.Config.JoinRetry) { + Controls.JoinGameWindow.click(); + + break MainSwitch; + } + + break; + case "DNE": // Game didn't exist + retry += 1; + + break; + case "FULL": // Game is full + retry = Starter.Config.JoinRetry; + + break; + } + + if (retry >= Starter.Config.JoinRetry) { + D2Bot.printToConsole("Failed to join " + joinInfo.gameName + ". Aborting."); + badGames.push(joinInfo.gameName); - Starter.lastGameStatus = "ready"; - joinInfo.oldGame = joinInfo.gameName; - retry = 0; - } + Starter.lastGameStatus = "ready"; + joinInfo.oldGame = joinInfo.gameName; + retry = 0; + } - fullText = ""; - lines = Controls.LobbyChat.getText(); + fullText = ""; + lines = Controls.LobbyChat.getText(); - if (!lines) { - break; - } + if (!lines) { + break; + } - fullText = lines.join(" ").replace(/\s+/g, " "); + fullText = lines.join(" ").replace(/\s+/g, " "); - if (lastText === fullText) { - if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { - // we have a game and nothing else has changed since last announcement so go ahead and try joining again - Controls.JoinGameWindow.click(); - } - // nothing has changed since our last check so break - break; - } + if (lastText === fullText) { + if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { + // we have a game and nothing else has changed since last announcement so go ahead and try joining again + Controls.JoinGameWindow.click(); + } + // nothing has changed since our last check so break + break; + } - lastText = fullText; + lastText = fullText; - // we are set to follow a specific leader, lets look for their messages - if (Starter.Config.Follow.length > 0) { - let newLines = lines - .map((line, index) => { - if (index > 0 + // we are set to follow a specific leader, lets look for their messages + if (Starter.Config.Follow.length > 0) { + let newLines = lines + .map((line, index) => { + if (index > 0 // eslint-disable-next-line no-useless-escape && !line.match(/\<.*\>/, "gi") && !line.match(" has left", "gi") && !line.match(" has joined", "gi")) { - line = lines[index - 1] + line; - } - return line; - }) - .filter(line => line.match("Next game is", "gi")); - - if (newLines.length === 0) { - break; - } - - for (n = 0; n < Starter.Config.Follow.length; n++) { - let test = []; - - newLines.forEach(element => { - if (element.includes(Starter.Config.Follow[n])) { - test.push(element); - } - }); - - if (test.length === 0) continue; - test.reverse(); - - for (let msg = 0; msg < test.length; msg++) { - let checkName = test[msg].toString(); - let hasPass = checkName.indexOf("/") > -1; - - let gName = (checkName.slice(checkName.indexOf("is ") + 3, (hasPass ? checkName.indexOf("/") : undefined)) || "").trim(); - if (gName.length > 15) continue; // invalid game name - let gPass = (hasPass ? checkName.slice(checkName.lastIndexOf("/") + 1) : ""); - if (gPass.length > 15) continue; // invalid game pass - joinInfo.gameName = gName; - joinInfo.gamePass = gPass; - console.debug(joinInfo.gameName + " " + joinInfo.gamePass); - - if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { - // wait until leader has left the channel - if (Starter.Config.JoinDelay && Starter.channelNotify) { - let wTick = getTickCount(); - - while (true) { - lines = Controls.LobbyChat.getText(); - - if (!lines || (getTickCount() - wTick > Time.minutes(1))) { - break; - } - - if (lines.some(line => line.match(Starter.Config.Follow[n] + " has left"))) { - break; - } - - delay(2000); - } - } - - Controls.JoinGameWindow.click(); - - break; - } - } - } - } - - // we are just trying to follow game names - for (let n = 0; n < Starter.Config.Games.length; n += 1) { - if (Starter.Config.Games[n] === "") continue; - regex = new RegExp("\\W+" + Starter.Config.Games[n].toLowerCase() + "\\d+", "gi"); - joinInfo.gameName = fullText.match(regex); - - if (joinInfo.gameName) { - // use last match and trim it - joinInfo.gameName = joinInfo.gameName[joinInfo.gameName.length - 1].toString().replace(/^\W*/, ""); - joinInfo.gamePass = Starter.Config.Passwords[n] || ""; - - if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { - Controls.JoinGameWindow.click(); - - break; - } - } - } - - break; - case sdk.game.locations.WaitingInLine: - case sdk.game.locations.CreateGame: - Controls.CancelCreateGame.click(); - - break; - case sdk.game.locations.JoinGame: - if (joinInfo.oldGame === joinInfo.gameName || badGames.includes(joinInfo.gameName)) { - Controls.CancelJoinGame.click(); - } - - D2Bot.updateStatus("Join Game"); - - if (joinInfo.gameName !== "") { - print("ÿc2Joining ÿc0" + joinInfo.gameName); - Controls.JoinGameName.setText(joinInfo.gameName); - Controls.JoinGamePass.setText(joinInfo.gamePass); - - if (Starter.Config.AnnounceGames && Starter.Config.AnnounceMessage) { - Starter.sayMsg(Starter.Config.AnnounceMessage + " " + joinInfo.gameName); - } - - // Only delay on first join - the rest is handled by GameDoesNotExistTimeout. Any other case is instant fail (ie. full game). - if (retry === 0 || Starter.lastGameStatus === "pending") { - ControlAction.timeoutDelay("Join Game Delay", Starter.Config.JoinDelay * 1e3); - } - - me.blockMouse = true; - - Controls.JoinGame.click(); - - me.blockMouse = false; - Starter.lastGameStatus = "pending"; - - Starter.locationTimeout(5000, location); - } - - break; - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - case sdk.game.locations.SplashScreen: - Starter.LocationEvents.login(); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - Starter.lastGameStatus = "DNE"; - - break; - case sdk.game.locations.GameIsFull: - badGames.push(joinInfo.gameName); - Controls.JoinGameWindow.click(); - Controls.CancelCreateGame.click(); - Starter.lastGameStatus = "FULL"; - - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + line = lines[index - 1] + line; + } + return line; + }) + .filter(line => line.match("Next game is", "gi")); + + if (newLines.length === 0) { + break; + } + + for (n = 0; n < Starter.Config.Follow.length; n++) { + let test = []; + + newLines.forEach(element => { + if (element.includes(Starter.Config.Follow[n])) { + test.push(element); + } + }); + + if (test.length === 0) continue; + test.reverse(); + + for (let msg = 0; msg < test.length; msg++) { + let checkName = test[msg].toString(); + let hasPass = checkName.indexOf("/") > -1; + + let gName = (checkName.slice(checkName.indexOf("is ") + 3, (hasPass ? checkName.indexOf("/") : undefined)) || "").trim(); + if (gName.length > 15) continue; // invalid game name + let gPass = (hasPass ? checkName.slice(checkName.lastIndexOf("/") + 1) : ""); + if (gPass.length > 15) continue; // invalid game pass + joinInfo.gameName = gName; + joinInfo.gamePass = gPass; + console.debug(joinInfo.gameName + " " + joinInfo.gamePass); + + if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { + // wait until leader has left the channel + if (Starter.Config.JoinDelay && Starter.channelNotify) { + let wTick = getTickCount(); + + while (true) { + lines = Controls.LobbyChat.getText(); + + if (!lines || (getTickCount() - wTick > Time.minutes(1))) { + break; + } + + if (lines.some(line => line.match(Starter.Config.Follow[n] + " has left"))) { + break; + } + + delay(2000); + } + } + + Controls.JoinGameWindow.click(); + + break; + } + } + } + } + + // we are just trying to follow game names + for (let n = 0; n < Starter.Config.Games.length; n += 1) { + if (Starter.Config.Games[n] === "") continue; + regex = new RegExp("\\W+" + Starter.Config.Games[n].toLowerCase() + "\\d+", "gi"); + joinInfo.gameName = fullText.match(regex); + + if (joinInfo.gameName) { + // use last match and trim it + joinInfo.gameName = joinInfo.gameName[joinInfo.gameName.length - 1].toString().replace(/^\W*/, ""); + joinInfo.gamePass = Starter.Config.Passwords[n] || ""; + + if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { + Controls.JoinGameWindow.click(); + + break; + } + } + } + + break; + case sdk.game.locations.WaitingInLine: + case sdk.game.locations.CreateGame: + Controls.CancelCreateGame.click(); + + break; + case sdk.game.locations.JoinGame: + if (joinInfo.oldGame === joinInfo.gameName || badGames.includes(joinInfo.gameName)) { + Controls.CancelJoinGame.click(); + } + + D2Bot.updateStatus("Join Game"); + + if (joinInfo.gameName !== "") { + print("ÿc2Joining ÿc0" + joinInfo.gameName); + Controls.JoinGameName.setText(joinInfo.gameName); + Controls.JoinGamePass.setText(joinInfo.gamePass); + + if (Starter.Config.AnnounceGames && Starter.Config.AnnounceMessage) { + Starter.sayMsg(Starter.Config.AnnounceMessage + " " + joinInfo.gameName); + } + + // Only delay on first join - the rest is handled by GameDoesNotExistTimeout. Any other case is instant fail (ie. full game). + if (retry === 0 || Starter.lastGameStatus === "pending") { + ControlAction.timeoutDelay("Join Game Delay", Starter.Config.JoinDelay * 1e3); + } + + me.blockMouse = true; + + Controls.JoinGame.click(); + + me.blockMouse = false; + Starter.lastGameStatus = "pending"; + + Starter.locationTimeout(5000, location); + } + + break; + case sdk.game.locations.Ladder: + case sdk.game.locations.ChannelList: + break; + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + case sdk.game.locations.CharSelect: + case sdk.game.locations.SplashScreen: + Starter.LocationEvents.login(); + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + break; + case sdk.game.locations.MainMenuConnecting: + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + Starter.LocationEvents.charSelectError(); + + break; + case sdk.game.locations.ServerDown: + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GameNameExists: + break; + case sdk.game.locations.GatewaySelect: + Controls.GatewayCancel.click(); + + break; + case sdk.game.locations.GameDoesNotExist: + Starter.LocationEvents.gameDoesNotExist(); + Starter.lastGameStatus = "DNE"; + + break; + case sdk.game.locations.GameIsFull: + badGames.push(joinInfo.gameName); + Controls.JoinGameWindow.click(); + Controls.CancelCreateGame.click(); + Starter.lastGameStatus = "FULL"; + + break; + case sdk.game.locations.OtherMultiplayer: + Starter.LocationEvents.otherMultiplayerSelect(); + + break; + case sdk.game.locations.TcpIp: + case sdk.game.locations.TcpIpEnterIp: + Controls.TcpIpCancel.click(); + + break; + default: + if (location !== undefined) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } function main() { - addEventListener("copydata", Starter.receiveCopyData); - addEventListener("scriptmsg", Starter.scriptMsgEvent); + addEventListener("copydata", Starter.receiveCopyData); + addEventListener("scriptmsg", Starter.scriptMsgEvent); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); - if (Starter.gameInfo.error) { - if (!!DataFile.getStats().debugInfo) { - Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); - } + if (Starter.gameInfo.error) { + if (!!DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + } - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - D2Bot.updateRuns(); - } + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + 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(); - print("Getting Profile"); - delay(500); - } + while (!Object.keys(Starter.profileInfo).length) { + D2Bot.getProfile(); + print("Getting Profile"); + 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) { - joinInfo.inGame = true; + 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) { + joinInfo.inGame = true; - if (!Starter.inGame) { - print("Updating Status"); + if (!Starter.inGame) { + print("Updating Status"); - badGames.push(joinInfo.gameName); - joinInfo.oldGame = me.gamename; - Starter.lastGameStatus = "ingame"; - Starter.inGame = true; - Starter.gameStart = getTickCount(); + badGames.push(joinInfo.gameName); + joinInfo.oldGame = me.gamename; + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + Starter.gameStart = getTickCount(); - DataFile.updateStats("runs", Starter.gameCount); - } + DataFile.updateStats("runs", Starter.gameCount); + } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); - } + D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + } - delay(1000); - } + delay(1000); + } - joinInfo.inGame = false; + joinInfo.inGame = false; - locationAction(getLocation()); - delay(1000); - } + locationAction(getLocation()); + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index bdc951fa7..e715c5c43 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename D2BotCleaner.dbj * @author theBGuy @@ -29,7 +30,7 @@ Starter.Config.DelayBetweenAccounts = rand(15, 30); //Seconds to wait before cle * @todo this section should be in it's own config leaving this file only containing core logic */ const AccountsToClean = { - /* Format: + /* Format: "account1/password1/realm": ["charname1", "charname2"], "account2/password2/realm": ["charnameX", "charnameY"], "account3/password3/realm": ["all"] @@ -43,12 +44,12 @@ const AccountsToClean = { Individual entries are separated with a comma. */ - /* Example: + /* Example: "MyAcc1/tempPass/useast": ["soloSorc"], "singleplayer": ["solobarb"], */ - // Enter your lines under here + // Enter your lines under here }; @@ -56,11 +57,11 @@ const CharactersToExclude = [""]; // NEW STUFF - Please enter your profile name exactly as is const profiles = [ - /* Format. Enter in profile exactly the way it appears in D2Bot# + /* Format. Enter in profile exactly the way it appears in D2Bot# "SCL-ZON123", "hcnl-pal123", */ - // Enter your lines under here + // Enter your lines under here ]; @@ -73,12 +74,12 @@ const profiles = [ } */ const AdvancedProfileCleanerConfig = [ - // { - // profilePrefix: "scl-sorc-", - // profileSuffixStart: "002", - // end: "009" - // }, - // Your lines under here + // { + // profilePrefix: "scl-sorc-", + // profileSuffixStart: "002", + // end: "009" + // }, + // Your lines under here ]; /* Generate accounts to entirely clean ("all") @@ -91,18 +92,18 @@ const AdvancedProfileCleanerConfig = [ */ const AdvancedCleanerConfig = { - generateAccounts: false, - accountPrefix: "account", - accountPassword: "password", - accountRealm: "realm", - rangeStart: 1, - rangeStop: 10 + generateAccounts: false, + accountPrefix: "account", + accountPassword: "password", + accountRealm: "realm", + rangeStart: 1, + rangeStop: 10 }; let Controls = require("./modules/Control"); if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { - Starter.firstRun = true; + Starter.firstRun = true; } let currAcc, charList, realm; @@ -111,386 +112,386 @@ let accounts = []; let chars = []; function dataCleaner () { - if (AdvancedProfileCleanerConfig.length) { - let incrementString = function (text) { - return text.replace(/(\d*)$/, (_, t) => (+t + 1).toString().padStart(t.length, 0)); - }; - - AdvancedProfileCleanerConfig.forEach(p => { - let curr = p.profilePrefix + p.profileSuffixStart; - let end = p.profilePrefix + p.end; - profiles.push(curr); - while (curr !== end) { - curr = incrementString(curr); - profiles.push(curr); - } - }); - } - if (!profiles.length) { - D2Bot.printToConsole("D2BotCleaner: No profiles entered to clean. If this was a mistake, fill out profile information under NEW STUFF. Exiting dataCleaner and moving on to clean characters...", sdk.colors.D2Bot.Gold); - return; - } - - let charClass; - let folder, j; - let charClassMap = {"ZON": "amazon", "SOR": "sorceress", "NEC": "necromancer", "PAL": "paladin", "BAR": "barbarian", "DRU": "druid", "SIN": "assassin"}; - - for (let i = 0; i < profiles.length; i++) { - let buildCheck = profiles[i].toUpperCase().split("-"); - buildCheck[1] = buildCheck[1].toString().substring(0, 3).toUpperCase(); - let charType = buildCheck[0].includes("CC") ? "Classic" : "Expansion"; - let profileExists = false; - let soloplayProfile = false; - - // Filepaths - let dataFP = "data/" + profiles[i] + ".json"; - let gameTimeFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-GameTime" + ".json"; - let charDataFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-CharData" + ".json"; - let lvlPerfFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-LevelingPerformance" + ".csv"; - let scrPerfFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-ScriptPerformance" + ".csv"; - let savePath = "logs/"; // default value in case something goes wrong with assigning actual savePath - - if (charClassMap[buildCheck[1]]) { - charClass = charClassMap[buildCheck[1]]; - soloplayProfile = true; - } else { - //D2Bot.printToConsole("D2BotCleaner: Failed to get charClass. Please check that your profile was entered correctly under NEW STUFF.", sdk.colors.D2Bot.Gold); - //print("Invalid profile name, couldn't set character class"); - charClass = "undefined"; - } - - if (Starter.Config.SaveFiles && soloplayProfile) { - if (FileTools.exists(dataFP) || FileTools.exists(gameTimeFP) || FileTools.exists(charDataFP) || FileTools.exists(lvlPerfFP) || FileTools.exists(scrPerfFP)) { - // Create folder to copy files to - if (!FileTools.exists("libs/SoloPlay/Data/" + charType)) { - folder = dopen("libs/SoloPlay/Data"); - folder.create(charType); - } - - if (!FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass)) { - folder = dopen("libs/SoloPlay/Data/" + charType); - folder.create(charClass); - } - - let files = dopen("libs/SoloPlay/Data/" + charType + "/" + charClass + "/").getFolders(); - j = files.length + 1; - - // make sure folder doesn't already exist. - while (FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString())) { - j++; - delay(100); - } - - if (!FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString())) { - folder = dopen("libs/SoloPlay/Data/" + charType + "/" + charClass); - folder.create(j.toString()); - } - - savePath = "libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString() + "/" + profiles[i]; - profileExists = true; - } - } + if (AdvancedProfileCleanerConfig.length) { + let incrementString = function (text) { + return text.replace(/(\d*)$/, (_, t) => (+t + 1).toString().padStart(t.length, 0)); + }; + + AdvancedProfileCleanerConfig.forEach(p => { + let curr = p.profilePrefix + p.profileSuffixStart; + let end = p.profilePrefix + p.end; + profiles.push(curr); + while (curr !== end) { + curr = incrementString(curr); + profiles.push(curr); + } + }); + } + if (!profiles.length) { + D2Bot.printToConsole("D2BotCleaner: No profiles entered to clean. If this was a mistake, fill out profile information under NEW STUFF. Exiting dataCleaner and moving on to clean characters...", sdk.colors.D2Bot.Gold); + return; + } + + let charClass; + let folder, j; + let charClassMap = { "ZON": "amazon", "SOR": "sorceress", "NEC": "necromancer", "PAL": "paladin", "BAR": "barbarian", "DRU": "druid", "SIN": "assassin" }; + + for (let i = 0; i < profiles.length; i++) { + let buildCheck = profiles[i].toUpperCase().split("-"); + buildCheck[1] = buildCheck[1].toString().substring(0, 3).toUpperCase(); + let charType = buildCheck[0].includes("CC") ? "Classic" : "Expansion"; + let profileExists = false; + let soloplayProfile = false; + + // Filepaths + let dataFP = "data/" + profiles[i] + ".json"; + let gameTimeFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-GameTime" + ".json"; + let charDataFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-CharData" + ".json"; + let lvlPerfFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-LevelingPerformance" + ".csv"; + let scrPerfFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-ScriptPerformance" + ".csv"; + let savePath = "logs/"; // default value in case something goes wrong with assigning actual savePath + + if (charClassMap[buildCheck[1]]) { + charClass = charClassMap[buildCheck[1]]; + soloplayProfile = true; + } else { + //D2Bot.printToConsole("D2BotCleaner: Failed to get charClass. Please check that your profile was entered correctly under NEW STUFF.", sdk.colors.D2Bot.Gold); + //print("Invalid profile name, couldn't set character class"); + charClass = "undefined"; + } + + if (Starter.Config.SaveFiles && soloplayProfile) { + if (FileTools.exists(dataFP) || FileTools.exists(gameTimeFP) || FileTools.exists(charDataFP) || FileTools.exists(lvlPerfFP) || FileTools.exists(scrPerfFP)) { + // Create folder to copy files to + if (!FileTools.exists("libs/SoloPlay/Data/" + charType)) { + folder = dopen("libs/SoloPlay/Data"); + folder.create(charType); + } + + if (!FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass)) { + folder = dopen("libs/SoloPlay/Data/" + charType); + folder.create(charClass); + } + + let files = dopen("libs/SoloPlay/Data/" + charType + "/" + charClass + "/").getFolders(); + j = files.length + 1; + + // make sure folder doesn't already exist. + while (FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString())) { + j++; + delay(100); + } + + if (!FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString())) { + folder = dopen("libs/SoloPlay/Data/" + charType + "/" + charClass); + folder.create(j.toString()); + } + + savePath = "libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString() + "/" + profiles[i]; + profileExists = true; + } + } - if (FileTools.exists(dataFP)) { - Starter.Config.SaveFiles && FileTools.copy(dataFP, savePath + "Old.json"); - FileTools.remove(dataFP); - profileExists = true; - } + if (FileTools.exists(dataFP)) { + Starter.Config.SaveFiles && FileTools.copy(dataFP, savePath + "Old.json"); + FileTools.remove(dataFP); + profileExists = true; + } - if (FileTools.exists(gameTimeFP)) { - Starter.Config.SaveFiles && FileTools.copy(gameTimeFP, savePath + "-GameTimeOld.json"); - FileTools.remove(gameTimeFP); - } - - if (FileTools.exists(charDataFP)) { - Starter.Config.SaveFiles && FileTools.copy(charDataFP, savePath + "-CharDataOld.json"); - FileTools.remove(charDataFP); - } + if (FileTools.exists(gameTimeFP)) { + Starter.Config.SaveFiles && FileTools.copy(gameTimeFP, savePath + "-GameTimeOld.json"); + FileTools.remove(gameTimeFP); + } + + if (FileTools.exists(charDataFP)) { + Starter.Config.SaveFiles && FileTools.copy(charDataFP, savePath + "-CharDataOld.json"); + FileTools.remove(charDataFP); + } - if (FileTools.exists(lvlPerfFP)) { - Starter.Config.SaveFiles && FileTools.copy(lvlPerfFP, savePath + "-LevelingPerformanceOld.csv"); - FileTools.remove(lvlPerfFP); - } - - if (FileTools.exists(scrPerfFP)) { - Starter.Config.SaveFiles && FileTools.copy(scrPerfFP, savePath + "-ScriptPerformanceOld.csv"); - FileTools.remove(scrPerfFP); - } + if (FileTools.exists(lvlPerfFP)) { + Starter.Config.SaveFiles && FileTools.copy(lvlPerfFP, savePath + "-LevelingPerformanceOld.csv"); + FileTools.remove(lvlPerfFP); + } + + if (FileTools.exists(scrPerfFP)) { + Starter.Config.SaveFiles && FileTools.copy(scrPerfFP, savePath + "-ScriptPerformanceOld.csv"); + FileTools.remove(scrPerfFP); + } - if (Starter.Config.SaveFiles && profileExists && soloplayProfile) { - D2Bot.printToConsole("D2BotCleaner: Files saved to -> libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j, sdk.colors.D2Bot.Gold); - } + if (Starter.Config.SaveFiles && profileExists && soloplayProfile) { + D2Bot.printToConsole("D2BotCleaner: Files saved to -> libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j, sdk.colors.D2Bot.Gold); + } - if (profileExists) { - D2Bot.printToConsole("D2BotCleaner: Cleaned files for -> " + profiles[i], sdk.colors.D2Bot.Gold); - } + if (profileExists) { + D2Bot.printToConsole("D2BotCleaner: Cleaned files for -> " + profiles[i], sdk.colors.D2Bot.Gold); + } - delay(500); - } + delay(500); + } - D2Bot.printToConsole("D2BotCleaner: Done cleaning files", sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("D2BotCleaner: Done cleaning files", sdk.colors.D2Bot.Gold); } function parseInfo () { - for (let i in AccountsToClean) { - if (AccountsToClean.hasOwnProperty(i) && typeof i === "string") { - accounts.push(i); - chars.push(AccountsToClean[i]); - } - } + for (let i in AccountsToClean) { + if (AccountsToClean.hasOwnProperty(i) && typeof i === "string") { + accounts.push(i); + chars.push(AccountsToClean[i]); + } + } - if (AdvancedCleanerConfig.generateAccounts) { - for (let index = rangeStart; index <= rangeStop ; index += 1) { - accounts.push(AdvancedCleanerConfig.accountPrefix + index + "/" + AdvancedCleanerConfig.accountPassword + "/" + AdvancedCleanerConfig.accountRealm); - chars.push(["all"]); - } - } - - if (!accounts.length) { - FileTools.remove("logs/D2BotCleaner.json"); - D2Bot.printToConsole("D2BotCleaner: No accounts entered. Exiting...", sdk.colors.D2Bot.Gold); - ControlAction.timeoutDelay("Exiting in: ", 3 * 1e3); - D2Bot.stop(me.profile, true); - } + if (AdvancedCleanerConfig.generateAccounts) { + for (let index = rangeStart; index <= rangeStop ; index += 1) { + accounts.push(AdvancedCleanerConfig.accountPrefix + index + "/" + AdvancedCleanerConfig.accountPassword + "/" + AdvancedCleanerConfig.accountRealm); + chars.push(["all"]); + } + } + + if (!accounts.length) { + FileTools.remove("logs/D2BotCleaner.json"); + D2Bot.printToConsole("D2BotCleaner: No accounts entered. Exiting...", sdk.colors.D2Bot.Gold); + ControlAction.timeoutDelay("Exiting in: ", 3 * 1e3); + D2Bot.stop(me.profile, true); + } } function deleteAllCharacters () { - let characters = ControlAction.getCharacters(); - for (let character of characters) { - let info = {charName: character}; - if (CharactersToExclude.includes(character)) continue; - if (!ControlAction.deleteCharacter(info)) { - print("failed to delete character " + character); - return false; - } - delay(500); - } - return true; + let characters = ControlAction.getCharacters(); + for (let character of characters) { + let info = { charName: character }; + if (CharactersToExclude.includes(character)) continue; + if (!ControlAction.deleteCharacter(info)) { + print("failed to delete character " + character); + return false; + } + delay(500); + } + return true; } function locationAction (location) { - let i, currChar, - obj = {}; - - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - - break; - case sdk.game.locations.WaitingInLine: - Controls.CancelCreateGame.click(); - - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - case sdk.game.locations.CreateGame: - case sdk.game.locations.JoinGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameDoesNotExist: - case sdk.game.locations.GameIsFull: - Controls.LobbyQuit.click(); - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.SplashScreen: - if (!accounts.length) { - FileTools.remove("logs/D2BotCleaner.json"); - D2Bot.printToConsole("D2BotCleaner: Done cleaning accounts!", sdk.colors.D2Bot.Gold); - D2Bot.stop(me.profile, true); - } + let i, currChar, + obj = {}; + + switch (location) { + case sdk.game.locations.PreSplash: + ControlAction.click(); + + break; + case sdk.game.locations.WaitingInLine: + Controls.CancelCreateGame.click(); + + break; + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + case sdk.game.locations.CreateGame: + case sdk.game.locations.JoinGame: + case sdk.game.locations.Ladder: + case sdk.game.locations.ChannelList: + case sdk.game.locations.GameNameExists: + case sdk.game.locations.GameDoesNotExist: + case sdk.game.locations.GameIsFull: + Controls.LobbyQuit.click(); + + break; + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + case sdk.game.locations.SplashScreen: + if (!accounts.length) { + FileTools.remove("logs/D2BotCleaner.json"); + D2Bot.printToConsole("D2BotCleaner: Done cleaning accounts!", sdk.colors.D2Bot.Gold); + D2Bot.stop(me.profile, true); + } - if (!firstAccount) { - for (i = 0 ; i < Starter.Config.DelayBetweenAccounts; i += 1) { - D2Bot.updateStatus("Waiting " + (Starter.Config.DelayBetweenAccounts - i) + "s for next account"); - delay(1e3); - } - } + if (!firstAccount) { + for (i = 0 ; i < Starter.Config.DelayBetweenAccounts; i += 1) { + D2Bot.updateStatus("Waiting " + (Starter.Config.DelayBetweenAccounts - i) + "s for next account"); + delay(1e3); + } + } - firstAccount = false; + firstAccount = false; - if (FileTools.exists("logs/D2BotCleaner.json")) { - obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); + if (FileTools.exists("logs/D2BotCleaner.json")) { + obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); - if (obj.currAcc) { - for (i = 0; i < accounts.length; i += 1) { - if (accounts[i].split("/")[0] === obj.currAcc) { - accounts.splice(0, i); - chars.splice(0, i); + if (obj.currAcc) { + for (i = 0; i < accounts.length; i += 1) { + if (accounts[i].split("/")[0] === obj.currAcc) { + accounts.splice(0, i); + chars.splice(0, i); - i -= 1; + i -= 1; - break; - } - } - } - } + break; + } + } + } + } - let currAccInfo = accounts[0].split("/"); - currAcc = currAccInfo[0]; - obj.currAcc = currAccInfo[0]; - charList = chars[0]; + let currAccInfo = accounts[0].split("/"); + currAcc = currAccInfo[0]; + obj.currAcc = currAccInfo[0]; + charList = chars[0]; - D2Bot.printToConsole("D2BotCleaner: Cleaning account:" + currAcc + " , Character list: " + charList, sdk.colors.D2Bot.Gold); - FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); + D2Bot.printToConsole("D2BotCleaner: Cleaning account:" + currAcc + " , Character list: " + charList, sdk.colors.D2Bot.Gold); + FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); - if (currAcc.toLowerCase() === "singleplayer") { - Controls.SinglePlayer.click(); - } else if (currAccInfo.length === 3) { - realm = currAccInfo[2].toLowerCase(); - ControlAction.loginAccount({account: currAcc, password: currAccInfo[1], realm: realm}); - } - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.CharSelect: - case sdk.game.locations.CharSelectNoChars: - // Single Player screen fix - if (currAcc.toLowerCase() !== "singleplayer") { - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.BottomLeftExit.click(); - - break; - } - } + if (currAcc.toLowerCase() === "singleplayer") { + Controls.SinglePlayer.click(); + } else if (currAccInfo.length === 3) { + realm = currAccInfo[2].toLowerCase(); + ControlAction.loginAccount({ account: currAcc, password: currAccInfo[1], realm: realm }); + } + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.CharSelect: + case sdk.game.locations.CharSelectNoChars: + // Single Player screen fix + if (currAcc.toLowerCase() !== "singleplayer") { + if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { + Controls.BottomLeftExit.click(); + + break; + } + } - if (!charList.length) { - Controls.BottomLeftExit.click(); - - break; - } - - if (charList[0] === "all") { - deleteAllCharacters(); - } else { - if (FileTools.exists("logs/D2BotCleaner.json")) { - obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); - - if (obj.currChar) { - for (i = 0; i < charList.length; i += 1) { - if (charList[i] === obj.currChar) { - // Remove the previous currChar as well - charList.splice(0, i + 1); - - break; - } - } - } - } + if (!charList.length) { + Controls.BottomLeftExit.click(); + + break; + } + + if (charList[0] === "all") { + deleteAllCharacters(); + } else { + if (FileTools.exists("logs/D2BotCleaner.json")) { + obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); + + if (obj.currChar) { + for (i = 0; i < charList.length; i += 1) { + if (charList[i] === obj.currChar) { + // Remove the previous currChar as well + charList.splice(0, i + 1); + + break; + } + } + } + } - let charInfo = {charName: charList[0]}; - CharactersToExclude.indexOf(charInfo) === -1 && ControlAction.deleteCharacter(charInfo); - delay(500); - } + let charInfo = { charName: charList[0] }; + CharactersToExclude.indexOf(charInfo) === -1 && ControlAction.deleteCharacter(charInfo); + delay(500); + } - currChar = charList.shift(); - obj.currChar = currChar; + currChar = charList.shift(); + obj.currChar = currChar; - // last char in acc = trigger next acc - if (!charList.length) { - accounts.shift(); - chars.shift(); - Controls.BottomLeftExit.click(); - } + // last char in acc = trigger next acc + if (!charList.length) { + accounts.shift(); + chars.shift(); + Controls.BottomLeftExit.click(); + } - FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); + FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); - break; - case sdk.game.locations.CharacterCreate: - case sdk.game.locations.NewCharSelected: - Controls.BottomLeftExit.click(); + break; + case sdk.game.locations.CharacterCreate: + case sdk.game.locations.NewCharSelected: + Controls.BottomLeftExit.click(); - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: // Main Menu - Connecting - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: // Server Down - not much to do but wait.. - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + break; + case sdk.game.locations.MainMenuConnecting: // Main Menu - Connecting + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + Starter.LocationEvents.charSelectError(); + + break; + case sdk.game.locations.ServerDown: // Server Down - not much to do but wait.. + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GatewaySelect: + Controls.GatewayCancel.click(); + + break; + default: + if (location !== undefined) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } function main () { - addEventListener("copydata", Starter.receiveCopyData); + addEventListener("copydata", Starter.receiveCopyData); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - if (Starter.gameInfo.error) { - if (!!DataFile.getStats().debugInfo) { - Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); - } + if (Starter.gameInfo.error) { + if (!!DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + } - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - D2Bot.updateRuns(); - } + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + D2Bot.updateRuns(); + } - DataFile.updateStats("debugInfo", JSON.stringify({currScript: "none", area: "out of game"})); + DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); - Starter.Config.DataCleaner && dataCleaner(); - !accounts.length && parseInfo(); + Starter.Config.DataCleaner && dataCleaner(); + !accounts.length && parseInfo(); - while (true) { - locationAction(getLocation()); - delay(1000); - } + while (true) { + locationAction(getLocation()); + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 7ff9a6bef..063931d8e 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename D2BotFollow.dbj * @author kolton, theBGuy @@ -29,8 +30,8 @@ Starter.Config.JoinRetryDelay = 5; // Time in seconds to wait before next join a */ const JoinSettings = { - "Leader": ["Leecher"], - "map": ["all"] + "Leader": ["Leecher"], + "map": ["all"] }; // the only things we really need from these are their oog checks @@ -40,10 +41,10 @@ const Controls = require("./libs/modules/Control"); const Overrides = require("./libs/modules/Override"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { - Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); + Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); } else { - // no need to carry around the reference - delete Starter.AdvancedConfig; + // no need to carry around the reference + delete Starter.AdvancedConfig; } let lastGameTick, leader = ""; @@ -51,411 +52,411 @@ let announced = false; let lastGame = []; if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { - Starter.firstRun = true; + Starter.firstRun = true; } new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { - if (mode === 3) { - Starter.isUp = (me.gameReady ? "yes" : "no"); - if (!me.gameReady) { - return; - } - Starter.gameInfo.gameName = (me.gamename || ""); - Starter.gameInfo.gamePass = (me.gamepassword || ""); - } else { - orignal(mode, msg); - } + if (mode === 3) { + Starter.isUp = (me.gameReady ? "yes" : "no"); + if (!me.gameReady) { + return; + } + Starter.gameInfo.gameName = (me.gamename || ""); + Starter.gameInfo.gamePass = (me.gamepassword || ""); + } else { + orignal(mode, msg); + } }).apply(); function joinCheck (leader) { - D2Bot.requestGame(leader); - delay(500); + D2Bot.requestGame(leader); + delay(500); - if (!Starter.joinInfo.inGame || (lastGame.length && lastGame.indexOf(Starter.joinInfo.gameName) === -1)) { - D2Bot.printToConsole("Game is finished. Stopping join delay."); - Starter.gameInfo.gameName = ""; - Starter.gameInfo.gamePass = ""; + if (!Starter.joinInfo.inGame || (lastGame.length && lastGame.indexOf(Starter.joinInfo.gameName) === -1)) { + D2Bot.printToConsole("Game is finished. Stopping join delay."); + Starter.gameInfo.gameName = ""; + Starter.gameInfo.gamePass = ""; - return true; - } + return true; + } - return false; + return false; } function locationAction (location) { - if (me.ingame || location === undefined) { - return; - } - - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); - - me.blockKeys = false; - Starter.loginRetry = 0; - !Starter.firstLogin && (Starter.firstLogin = true); - - if (Starter.Config.JoinChannel !== "") { - Controls.LobbyEnterChat.click(); - - break; - } - - if (Starter.inGame) { - if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } - - print("updating runs"); - D2Bot.updateRuns(); - - lastGameTick = getTickCount(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - } - - Starter.LocationEvents.openJoinGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - case sdk.game.locations.CreateGame: - Controls.CancelCreateGame.click(); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby Chat"); - - if (Starter.inGame) { - if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } - - print("updating runs"); - D2Bot.updateRuns(); - - lastGameTick = getTickCount(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - } - - if (!Starter.chatActionsDone) { - Starter.chatActionsDone = true; - - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); - say("/j " + Starter.Config.JoinChannel); - delay(1000); - - if (Starter.Config.FirstJoinMessage !== "") { - say(Starter.Config.FirstJoinMessage); - delay(500); - } - } - - Starter.LocationEvents.openJoinGameWindow(); - - break; - case sdk.game.locations.JoinGame: - D2Bot.updateStatus("Join Game"); - - if (!leader) { - leader = []; - - for (let i in JoinSettings) { - if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { - for (let j = 0; j < JoinSettings[i].length; j += 1) { - if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { - leader.push(i); - } - } - } - } - } - - if (!leader || !leader.length && !announced) { - print("No leader"); - D2Bot.printToConsole("No leader"); - announced = true; - - break; - } - - JoinLoop2: - for (let i = 0; i < 5; i += 1) { - for (let j = 0; j < leader.length; j += 1) { - Starter.joinInfo = {}; - D2Bot.requestGame(leader[j]); - delay(100); - - if (!Starter.joinInfo.hasOwnProperty("gameName") || Starter.joinInfo.gameName === "") { - delay(500); - continue; - } - - /** + if (me.ingame || location === undefined) { + return; + } + + switch (location) { + case sdk.game.locations.PreSplash: + ControlAction.click(); + + break; + case sdk.game.locations.Lobby: + D2Bot.updateStatus("Lobby"); + + me.blockKeys = false; + Starter.loginRetry = 0; + !Starter.firstLogin && (Starter.firstLogin = true); + + if (Starter.Config.JoinChannel !== "") { + Controls.LobbyEnterChat.click(); + + break; + } + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { + break; + } + + print("updating runs"); + D2Bot.updateRuns(); + + lastGameTick = getTickCount(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + } + + Starter.LocationEvents.openJoinGameWindow(); + + break; + case sdk.game.locations.WaitingInLine: + case sdk.game.locations.CreateGame: + Controls.CancelCreateGame.click(); + Controls.JoinGameWindow.click(); + + break; + case sdk.game.locations.LobbyChat: + D2Bot.updateStatus("Lobby Chat"); + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { + break; + } + + print("updating runs"); + D2Bot.updateRuns(); + + lastGameTick = getTickCount(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + } + + if (!Starter.chatActionsDone) { + Starter.chatActionsDone = true; + + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + say("/j " + Starter.Config.JoinChannel); + delay(1000); + + if (Starter.Config.FirstJoinMessage !== "") { + say(Starter.Config.FirstJoinMessage); + delay(500); + } + } + + Starter.LocationEvents.openJoinGameWindow(); + + break; + case sdk.game.locations.JoinGame: + D2Bot.updateStatus("Join Game"); + + if (!leader) { + leader = []; + + for (let i in JoinSettings) { + if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { + for (let j = 0; j < JoinSettings[i].length; j += 1) { + if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { + leader.push(i); + } + } + } + } + } + + if (!leader || !leader.length && !announced) { + print("No leader"); + D2Bot.printToConsole("No leader"); + announced = true; + + break; + } + + JoinLoop2: + for (let i = 0; i < 5; i += 1) { + for (let j = 0; j < leader.length; j += 1) { + Starter.joinInfo = {}; + D2Bot.requestGame(leader[j]); + delay(100); + + if (!Starter.joinInfo.hasOwnProperty("gameName") || Starter.joinInfo.gameName === "") { + delay(500); + continue; + } + + /** * @todo handle rejoin, need to keep track of game averages and when requesting game from a leader who's game we left get the current game time * and see if there is x amount of time left that makes it worth it vs waiting for next. */ - if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { - Controls.JoinGameName.setText(Starter.joinInfo.gameName); - Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); - - if (Starter.lastGameStatus === "pending" || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { - D2Bot.printToConsole("Failed to join game"); - ControlAction.timeoutDelay("Join Delay", Starter.Config.JoinRetryDelay * 1000, joinCheck(leader[j])); - D2Bot.updateRuns(); - D2Bot.requestGame(leader[j]); - delay(200); - - if (!Starter.joinInfo.inGame) { - Starter.lastGameStatus = "ready"; - - break; - } - } - - if (!Starter.joinInfo.inGame) { - if (Starter.joinInfo.delay) { - ControlAction.timeoutDelay("Leader Delay", Starter.joinInfo.delay); - } - continue; - } - - // Don't join immediately after previous game to avoid FTJ - if (getTickCount() - lastGameTick < 5000) { - ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); - } - - print("joining game " + Starter.joinInfo.gameName); - - if (typeof Starter.Config.JoinDelay === "number") { - ControlAction.timeoutDelay("Custom Join Delay", Starter.Config.JoinDelay * 1e3); - } - - me.blockMouse = true; - - DataFile.updateStats("gameName", Starter.joinInfo.gameName); - Controls.JoinGame.click(); - - me.blockMouse = false; - - lastGame.push(Starter.joinInfo.gameName); - - // Might need a fixed number. Right now it stores 1 game per leader. - lastGame.length > leader.length && lastGame.shift(); - - Starter.lastGameStatus = "pending"; - Starter.locationTimeout(15000, location); - - break JoinLoop2; - } else { - // for now, if leader is in game and it's the last game we were in. delay to prevent copyData spam - if (lastGame.includes(Starter.joinInfo.gameName)) { - delay((Starter.joinInfo.inGame ? 5000 : 2000)); - } - } - } - } - - break; - case sdk.game.locations.Ladder: - break; - case sdk.game.locations.ChannelList: - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - case sdk.game.locations.SplashScreen: - Starter.LocationEvents.login([sdk.game.gametype.TcpIpJoin, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); - - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - - break; - case sdk.game.locations.GameIsFull: - D2Bot.printToConsole("Game is full"); - Controls.JoinGameWindow.click(); - lastGame.push(Starter.joinInfo.gameName); - Starter.lastGameStatus = "ready"; - - break; - case sdk.game.locations.OtherMultiplayer: - Profile().type === sdk.game.profiletype.TcpIpJoin ? Controls.TcpIp.click() : Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - Profile().type === sdk.game.profiletype.TcpIpJoin ? Controls.TcpIpJoin.click() : Controls.TcpIpCancel.click(); - - break; - case sdk.game.locations.TcpIpEnterIp: - try { - if (!leader) { - leader = []; - - for (let i in JoinSettings) { - if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { - for (let j = 0; j < JoinSettings[i].length; j += 1) { - if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { - leader.push(i); - } - } - } - } - } - - mainLoop: - for (let i = 0; i < 3; i++) { - for (let j = 0; j < leader.length; j++) { - D2Bot.requestGame(leader[j]); - - if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "") { - break mainLoop; - } - } - } - - if (Controls.IPAdress.setText(Object.keys(Starter.joinInfo).length ? Starter.joinInfo.gameName : "localhost") + if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { + Controls.JoinGameName.setText(Starter.joinInfo.gameName); + Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); + + if (Starter.lastGameStatus === "pending" || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { + D2Bot.printToConsole("Failed to join game"); + ControlAction.timeoutDelay("Join Delay", Starter.Config.JoinRetryDelay * 1000, joinCheck(leader[j])); + D2Bot.updateRuns(); + D2Bot.requestGame(leader[j]); + delay(200); + + if (!Starter.joinInfo.inGame) { + Starter.lastGameStatus = "ready"; + + break; + } + } + + if (!Starter.joinInfo.inGame) { + if (Starter.joinInfo.delay) { + ControlAction.timeoutDelay("Leader Delay", Starter.joinInfo.delay); + } + continue; + } + + // Don't join immediately after previous game to avoid FTJ + if (getTickCount() - lastGameTick < 5000) { + ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); + } + + print("joining game " + Starter.joinInfo.gameName); + + if (typeof Starter.Config.JoinDelay === "number") { + ControlAction.timeoutDelay("Custom Join Delay", Starter.Config.JoinDelay * 1e3); + } + + me.blockMouse = true; + + DataFile.updateStats("gameName", Starter.joinInfo.gameName); + Controls.JoinGame.click(); + + me.blockMouse = false; + + lastGame.push(Starter.joinInfo.gameName); + + // Might need a fixed number. Right now it stores 1 game per leader. + lastGame.length > leader.length && lastGame.shift(); + + Starter.lastGameStatus = "pending"; + Starter.locationTimeout(15000, location); + + break JoinLoop2; + } else { + // for now, if leader is in game and it's the last game we were in. delay to prevent copyData spam + if (lastGame.includes(Starter.joinInfo.gameName)) { + delay((Starter.joinInfo.inGame ? 5000 : 2000)); + } + } + } + } + + break; + case sdk.game.locations.Ladder: + break; + case sdk.game.locations.ChannelList: + break; + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + case sdk.game.locations.CharSelect: + case sdk.game.locations.SplashScreen: + Starter.LocationEvents.login([sdk.game.gametype.TcpIpJoin, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + Starter.LocationEvents.selectDifficultySP(); + + break; + case sdk.game.locations.MainMenuConnecting: + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + Starter.LocationEvents.charSelectError(); + + break; + case sdk.game.locations.ServerDown: + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GameNameExists: + break; + case sdk.game.locations.GatewaySelect: + Controls.GatewayCancel.click(); + + break; + case sdk.game.locations.GameDoesNotExist: + Starter.LocationEvents.gameDoesNotExist(); + + break; + case sdk.game.locations.GameIsFull: + D2Bot.printToConsole("Game is full"); + Controls.JoinGameWindow.click(); + lastGame.push(Starter.joinInfo.gameName); + Starter.lastGameStatus = "ready"; + + break; + case sdk.game.locations.OtherMultiplayer: + Profile().type === sdk.game.profiletype.TcpIpJoin ? Controls.TcpIp.click() : Controls.OtherMultiplayerCancel.click(); + + break; + case sdk.game.locations.TcpIp: + Profile().type === sdk.game.profiletype.TcpIpJoin ? Controls.TcpIpJoin.click() : Controls.TcpIpCancel.click(); + + break; + case sdk.game.locations.TcpIpEnterIp: + try { + if (!leader) { + leader = []; + + for (let i in JoinSettings) { + if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { + for (let j = 0; j < JoinSettings[i].length; j += 1) { + if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { + leader.push(i); + } + } + } + } + } + + mainLoop: + for (let i = 0; i < 3; i++) { + for (let j = 0; j < leader.length; j++) { + D2Bot.requestGame(leader[j]); + + if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "") { + break mainLoop; + } + } + } + + if (Controls.IPAdress.setText(Object.keys(Starter.joinInfo).length ? Starter.joinInfo.gameName : "localhost") && Controls.IPAdressOk.click() && Starter.locationTimeout(2e3, sdk.game.locations.TcpIpEnterIp)) { - getLocation() === sdk.game.locations.CharSelect && login(me.profile); - } - } catch (e) { - print(e); - } - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + getLocation() === sdk.game.locations.CharSelect && login(me.profile); + } + } catch (e) { + print(e); + } + + break; + default: + if (location !== undefined) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } function main () { - debugLog(me.profile); - addEventListener("copydata", Starter.receiveCopyData); - addEventListener("scriptmsg", Starter.scriptMsgEvent); + debugLog(me.profile); + addEventListener("copydata", Starter.receiveCopyData); + addEventListener("scriptmsg", Starter.scriptMsgEvent); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); - if (Starter.gameInfo.error) { - delay(200); + if (Starter.gameInfo.error) { + delay(200); - if (!!DataFile.getStats().debugInfo) { - Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + if (!!DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); - } + D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + } - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - D2Bot.updateRuns(); - } + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + 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(); - print("Getting Profile"); - delay(500); - } + while (!Object.keys(Starter.profileInfo).length) { + D2Bot.getProfile(); + print("Getting Profile"); + 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) { - if (!Starter.inGame) { - print("ÿc4Updating Status"); - Starter.lastGameStatus = "ingame"; - Starter.inGame = true; - Starter.gameStart = getTickCount(); + 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) { + if (!Starter.inGame) { + print("ÿc4Updating Status"); + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + Starter.gameStart = getTickCount(); - DataFile.updateStats("runs", Starter.gameCount); - } + DataFile.updateStats("runs", Starter.gameCount); + } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); - } + D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + } - delay(1000); - } + delay(1000); + } - locationAction(getLocation()); - delay(1000); - } + locationAction(getLocation()); + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index 2a63550bb..eb95c0262 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename D2BotGameAction.dbj * @author noah, theBGuy @@ -24,7 +25,7 @@ let Controls = require("./modules/Control"); let Overrides = require("./modules/Override"); if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { - Starter.firstRun = true; + Starter.firstRun = true; } let tag, charList; @@ -32,369 +33,369 @@ let ftj = 0; let creatingActions = ["doMule"]; new Overrides.Override(Starter, Starter.receiveCopyData, function(orignal, mode, msg) { - if (mode === 3) return; - if (mode === 1638) { - print("Recieved Profile Info"); - tag = JSON.parse(msg).Tag; - } - orignal(mode, msg); + if (mode === 3) return; + if (mode === 1638) { + print("Recieved Profile Info"); + tag = JSON.parse(msg).Tag; + } + orignal(mode, msg); }).apply(); function locationAction (location) { - let i, string, text, currChar; - - switch (location) { - case sdk.game.locations.PreSplash: - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); - - if (Starter.inGame) { - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); - } - - print("updating runs"); - D2Bot.updateRuns(); - delay(1000); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - - Controls.LobbyQuit.click(); - - break; - } - - // a game name was specified - if (GameAction.gameInfo() !== null) { - if (++ftj > 5) { - GameAction.update("done", "GameAction failed to join game!"); - D2Bot.stop(me.profile, true); - break; - } + let i, string, text, currChar; + + switch (location) { + case sdk.game.locations.PreSplash: + break; + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + D2Bot.updateStatus("Lobby"); + + if (Starter.inGame) { + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + } + + print("updating runs"); + D2Bot.updateRuns(); + delay(1000); + + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + + Controls.LobbyQuit.click(); + + break; + } + + // a game name was specified + if (GameAction.gameInfo() !== null) { + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to join game!"); + D2Bot.stop(me.profile, true); + break; + } - if (!Starter.LocationEvents.openCreateGameWindow()) { - break; - } - - Starter.LocationEvents.openJoinGameWindow(); - } else { - if (++ftj > 5) { - GameAction.update("done", "GameAction failed to create game!"); - D2Bot.stop(me.profile, true); - break; - } - - Starter.LocationEvents.openCreateGameWindow(); - } - - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); - - break; - case sdk.game.locations.CreateGame: - if (creatingActions.indexOf(JSON.parse(tag).action) < 0) { - GameAction.update("done", "GameAction failed to create game!"); - D2Bot.stop(me.profile, true); - break; - } + if (!Starter.LocationEvents.openCreateGameWindow()) { + break; + } + + Starter.LocationEvents.openJoinGameWindow(); + } else { + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to create game!"); + D2Bot.stop(me.profile, true); + break; + } + + Starter.LocationEvents.openCreateGameWindow(); + } + + break; + case sdk.game.locations.WaitingInLine: + Starter.LocationEvents.waitingInLine(); + + break; + case sdk.game.locations.CreateGame: + if (creatingActions.indexOf(JSON.parse(tag).action) < 0) { + GameAction.update("done", "GameAction failed to create game!"); + D2Bot.stop(me.profile, true); + break; + } - D2Bot.updateStatus("Creating Game"); + D2Bot.updateStatus("Creating Game"); - // remove level restriction - Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); + // remove level restriction + Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); - // Max number of players - Controls.MaxPlayerCount.setText("8"); + // Max number of players + Controls.MaxPlayerCount.setText("8"); - if (Starter.gameCount >= 99) { - Starter.gameCount = 1; + if (Starter.gameCount >= 99) { + Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); - } + DataFile.updateStats("runs", Starter.gameCount); + } - if (Starter.lastGameStatus === "pending") { - D2Bot.printToConsole("Failed to create game"); + if (Starter.lastGameStatus === "pending") { + D2Bot.printToConsole("Failed to create game"); - Starter.gameCount += 1; - } + Starter.gameCount += 1; + } - ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); - ControlAction.createGame(Starter.gameInfo.gameName + Starter.gameCount, Starter.gameInfo.gamePass, 0); - Starter.locationTimeout(5000, location); + ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); + ControlAction.createGame(Starter.gameInfo.gameName + Starter.gameCount, Starter.gameInfo.gamePass, 0); + Starter.locationTimeout(5000, location); - Starter.lastGameStatus = "pending"; + Starter.lastGameStatus = "pending"; - break; - case sdk.game.locations.JoinGame: // Join Game - D2Bot.updateStatus("Join Game"); - let joinInfo = GameAction.gameInfo(); - - joinGame(joinInfo.gameName, joinInfo.gamePass); - Starter.locationTimeout(5000, location); - - break; - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Controls.LobbyChannelCancel.click(); - - break; - case sdk.game.locations.MainMenu: // Main Menu - case sdk.game.locations.Login: // Login - case sdk.game.locations.SplashScreen: // D2 Splash - !charList && (charList = GameAction.getCharacters()); - - // last char in list - if (!charList || !charList.length) { - GameAction.update("done", "GameAction has completed task"); - D2Bot.stop(me.profile, true); - delay(5000); - break; - } - - ControlAction.loginAccount(GameAction.getLogin()); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.CharSelect: // Character Select - // Reset ftj counter - ftj = 0; + break; + case sdk.game.locations.JoinGame: // Join Game + D2Bot.updateStatus("Join Game"); + let joinInfo = GameAction.gameInfo(); + + joinGame(joinInfo.gameName, joinInfo.gamePass); + Starter.locationTimeout(5000, location); + + break; + case sdk.game.locations.Ladder: + case sdk.game.locations.ChannelList: + Controls.LobbyChannelCancel.click(); + + break; + case sdk.game.locations.MainMenu: // Main Menu + case sdk.game.locations.Login: // Login + case sdk.game.locations.SplashScreen: // D2 Splash + !charList && (charList = GameAction.getCharacters()); + + // last char in list + if (!charList || !charList.length) { + GameAction.update("done", "GameAction has completed task"); + D2Bot.stop(me.profile, true); + delay(5000); + break; + } + + ControlAction.loginAccount(GameAction.getLogin()); + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); + + break; + case sdk.game.locations.CharSelect: // Character Select + // Reset ftj counter + ftj = 0; - // Single Player screen fix - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.BottomLeftExit.click(); - - break; - } - - // last char in list - if (!charList || !charList.length) { - GameAction.update("done", "GameAction has completed task"); - D2Bot.stop(me.profile, true); - delay(5000); - break; - } - - // "" empty string means all characters - if (charList[0].length === 0) { - charList = ControlAction.getCharacters(); - - // empty account - if (!charList || !charList.length) { - GameAction.update("done", "Account has no chars!"); - D2Bot.stop(me.profile, true); - delay(5000); - break; - } - } - - currChar = charList.shift(); - - print("ÿc4Game Actionÿc2: Login character: " + currChar); - ControlAction.loginCharacter({charName: currChar}); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.NewCharSelected: // New Character - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: // Server Down - not much to do but wait.. - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: // Lobby - Game Name Exists - if (++ftj > 5) { - GameAction.update("done", "GameAction failed to create game!"); - D2Bot.stop(me.profile, true); - break; - } - ControlAction.timeoutDelay("Game Already Exists", 5e3); - Controls.CreateGameWindow.click(); - - break; - case sdk.game.locations.GatewaySelect: // Gateway Select - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: // Lobby - Game Does Not Exist - if (++ftj > 5) { - GameAction.update("done", "GameAction failed to join game!"); - D2Bot.stop(me.profile, true); - break; - } - ControlAction.timeoutDelay("Game Doesn't Exist", 5e3); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.GameIsFull: // Game is full - D2Bot.printToConsole("Game is full"); - Starter.lastGameStatus = "ready"; - delay(500); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.CharSelectNoChars: // Empty character screen - // TODO: see if this is needed in case 12 too - string = ""; - text = Controls.CharSelectError.getText(); - - if (text) { - for (i = 0; i < text.length; i += 1) { - string += text[i]; - - if (i !== text.length - 1) { - string += " "; - } - } - - if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { // CDKey disabled from realm play - 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 { - GameAction.update("done", "GameAction has failed in location 42"); - D2Bot.stop(me.profile, true); - } - } - } - - if (!Starter.locationTimeout(5000, location)) { - GameAction.update("done", "Account has no chars! location 42"); - D2Bot.stop(me.profile, true); - } - - break; - case sdk.game.locations.OtherMultiplayer: - Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + // Single Player screen fix + if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { + Controls.BottomLeftExit.click(); + + break; + } + + // last char in list + if (!charList || !charList.length) { + GameAction.update("done", "GameAction has completed task"); + D2Bot.stop(me.profile, true); + delay(5000); + break; + } + + // "" empty string means all characters + if (charList[0].length === 0) { + charList = ControlAction.getCharacters(); + + // empty account + if (!charList || !charList.length) { + GameAction.update("done", "Account has no chars!"); + D2Bot.stop(me.profile, true); + delay(5000); + break; + } + } + + currChar = charList.shift(); + + print("ÿc4Game Actionÿc2: Login character: " + currChar); + ControlAction.loginCharacter({ charName: currChar }); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.NewCharSelected: // New Character + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + break; + case sdk.game.locations.MainMenuConnecting: + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + Starter.LocationEvents.charSelectError(); + + break; + case sdk.game.locations.ServerDown: // Server Down - not much to do but wait.. + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GameNameExists: // Lobby - Game Name Exists + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to create game!"); + D2Bot.stop(me.profile, true); + break; + } + ControlAction.timeoutDelay("Game Already Exists", 5e3); + Controls.CreateGameWindow.click(); + + break; + case sdk.game.locations.GatewaySelect: // Gateway Select + Controls.GatewayCancel.click(); + + break; + case sdk.game.locations.GameDoesNotExist: // Lobby - Game Does Not Exist + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to join game!"); + D2Bot.stop(me.profile, true); + break; + } + ControlAction.timeoutDelay("Game Doesn't Exist", 5e3); + Controls.JoinGameWindow.click(); + + break; + case sdk.game.locations.GameIsFull: // Game is full + D2Bot.printToConsole("Game is full"); + Starter.lastGameStatus = "ready"; + delay(500); + Controls.JoinGameWindow.click(); + + break; + case sdk.game.locations.CharSelectNoChars: // Empty character screen + // TODO: see if this is needed in case 12 too + string = ""; + text = Controls.CharSelectError.getText(); + + if (text) { + for (i = 0; i < text.length; i += 1) { + string += text[i]; + + if (i !== text.length - 1) { + string += " "; + } + } + + if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { // CDKey disabled from realm play + 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 { + GameAction.update("done", "GameAction has failed in location 42"); + D2Bot.stop(me.profile, true); + } + } + } + + if (!Starter.locationTimeout(5000, location)) { + GameAction.update("done", "Account has no chars! location 42"); + D2Bot.stop(me.profile, true); + } + + break; + case sdk.game.locations.OtherMultiplayer: + Controls.OtherMultiplayerCancel.click(); + + break; + case sdk.game.locations.TcpIp: + case sdk.game.locations.TcpIpEnterIp: + Controls.TcpIpCancel.click(); + + break; + default: + if (location !== undefined) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } function main () { - addEventListener("copydata", Starter.receiveCopyData); + addEventListener("copydata", Starter.receiveCopyData); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - delay(500); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); - while (!tag) { - D2Bot.getProfile(); - delay(500); - } + while (!tag) { + D2Bot.getProfile(); + delay(500); + } - if (Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole("You must disable RD Blocker for Mule Logger to work properly. Stopping."); - GameAction.update("done", "GameAction has failed, please disable RD Blocker"); - D2Bot.stop(me.profile, true); + if (Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole("You must disable RD Blocker for Mule Logger to work properly. Stopping."); + GameAction.update("done", "GameAction has failed, please disable RD Blocker"); + D2Bot.stop(me.profile, true); - return; - } + return; + } - GameAction.init(tag); + GameAction.init(tag); - if (Starter.gameInfo.error) { - if (!!DataFile.getStats().debugInfo) { - Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + if (Starter.gameInfo.error) { + if (!!DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); - } + D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + } - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - D2Bot.updateRuns(); - } + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + 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 (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) { - if (!Starter.inGame) { - print("Updating Status"); - D2Bot.updateStatus("Game: " + me.gamename); + 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) { + if (!Starter.inGame) { + print("Updating Status"); + D2Bot.updateStatus("Game: " + me.gamename); - Starter.lastGameStatus = "ingame"; - Starter.inGame = true; - Starter.gameStart = getTickCount(); + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + Starter.gameStart = getTickCount(); - DataFile.updateStats("runs", Starter.gameCount); - } - } + DataFile.updateStats("runs", Starter.gameCount); + } + } - delay(1000); - } + delay(1000); + } - locationAction(getLocation()); - delay(1000); - } + locationAction(getLocation()); + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 137d9b951..72c7d4ce2 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /* eslint-disable no-fallthrough */ /** * @filename D2BotLead.dbj @@ -19,362 +20,362 @@ includeSystemLibs(); const Controls = require("./libs/modules/Control"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { - Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); + Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); } else { - // no need to carry around the reference then - delete Starter.AdvancedConfig; + // no need to carry around the reference then + delete Starter.AdvancedConfig; } if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { - Starter.firstRun = true; + Starter.firstRun = true; } function locationAction (location) { - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - Starter.locationTimeout(5000, location); - getLocation() === sdk.game.locations.PreSplash && sendKey(0x0D); - - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); - - me.blockKeys = false; - Starter.loginRetry = 0; - !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(); - - break; - } - - 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } - - print("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.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); - - break; - case sdk.game.locations.LobbyChat: - 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } - - print("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; - - // check that we are in the channel we are supposed to be in - if (Starter.chanInfo.joinChannel.length) { - let chanName = Controls.LobbyChannelName.getText(); - chanName && (chanName = chanName.toString()); - chanName && (chanName = chanName.slice(0, chanName.indexOf("(") - 1)); - Starter.chanInfo.joinChannel.indexOf(chanName) === -1 && (Starter.chatActionsDone = false); - } - - if (Starter.chanInfo.afterMsg) { - if (typeof Starter.chanInfo.afterMsg === "string") { - Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]; - } - - for (let i = 0; i < Starter.chanInfo.afterMsg.length; i += 1) { - 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) { - typeof Starter.chanInfo.joinChannel === "string" && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); - typeof Starter.chanInfo.firstMsg === "string" && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); - - for (let i = 0; i < Starter.chanInfo.joinChannel.length; i += 1) { - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); - - if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { - Starter.useChat = true; - } else { - print("ÿ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; - - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.CreateGame: - 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()); - - // Get game name if there is none - while (!Starter.gameInfo.gameName) { - D2Bot.requestGameInfo(); - delay(500); - } - - // 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(); - } - - let gameName = (Starter.gameInfo.gameName === "?" ? Starter.randomString(null, true) : Starter.gameInfo.gameName + Starter.gameCount); - let gamePass = (Starter.gameInfo.gamePass === "?" ? Starter.randomString(null, true) : Starter.gameInfo.gamePass); - - ControlAction.createGame(gameName, gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); - - Starter.lastGameStatus = "pending"; - Starter.setNextGame(Starter.gameInfo); - Starter.locationTimeout(10000, location); - - break; - case sdk.game.locations.JoinGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.SplashScreen: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - Starter.LocationEvents.login([sdk.game.gametype.TcpIpHost, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); - - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameIsFull: - Controls.CreateGameWindow.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - - break; - case sdk.game.locations.CharacterCreate: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); - - break; - case sdk.game.locations.TcpIp: - Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); - - break; - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + switch (location) { + case sdk.game.locations.PreSplash: + ControlAction.click(); + Starter.locationTimeout(5000, location); + getLocation() === sdk.game.locations.PreSplash && sendKey(0x0D); + + break; + case sdk.game.locations.Lobby: + D2Bot.updateStatus("Lobby"); + + me.blockKeys = false; + Starter.loginRetry = 0; + !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(); + + break; + } + + 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { + break; + } + + print("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.LocationEvents.openCreateGameWindow(); + + break; + case sdk.game.locations.WaitingInLine: + Starter.LocationEvents.waitingInLine(); + + break; + case sdk.game.locations.LobbyChat: + 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { + break; + } + + print("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; + + // check that we are in the channel we are supposed to be in + if (Starter.chanInfo.joinChannel.length) { + let chanName = Controls.LobbyChannelName.getText(); + chanName && (chanName = chanName.toString()); + chanName && (chanName = chanName.slice(0, chanName.indexOf("(") - 1)); + Starter.chanInfo.joinChannel.indexOf(chanName) === -1 && (Starter.chatActionsDone = false); + } + + if (Starter.chanInfo.afterMsg) { + if (typeof Starter.chanInfo.afterMsg === "string") { + Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]; + } + + for (let i = 0; i < Starter.chanInfo.afterMsg.length; i += 1) { + 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) { + typeof Starter.chanInfo.joinChannel === "string" && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); + typeof Starter.chanInfo.firstMsg === "string" && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); + + for (let i = 0; i < Starter.chanInfo.joinChannel.length; i += 1) { + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + + if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { + Starter.useChat = true; + } else { + print("ÿ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; + + Starter.LocationEvents.openCreateGameWindow(); + + break; + case sdk.game.locations.CreateGame: + 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()); + + // Get game name if there is none + while (!Starter.gameInfo.gameName) { + D2Bot.requestGameInfo(); + delay(500); + } + + // 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(); + } + + let gameName = (Starter.gameInfo.gameName === "?" ? Starter.randomString(null, true) : Starter.gameInfo.gameName + Starter.gameCount); + let gamePass = (Starter.gameInfo.gamePass === "?" ? Starter.randomString(null, true) : Starter.gameInfo.gamePass); + + ControlAction.createGame(gameName, gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); + + Starter.lastGameStatus = "pending"; + Starter.setNextGame(Starter.gameInfo); + Starter.locationTimeout(10000, location); + + break; + case sdk.game.locations.JoinGame: + case sdk.game.locations.Ladder: + case sdk.game.locations.ChannelList: + Starter.LocationEvents.openCreateGameWindow(); + + break; + case sdk.game.locations.MainMenu: + case sdk.game.locations.SplashScreen: + case sdk.game.locations.Login: + case sdk.game.locations.CharSelect: + Starter.LocationEvents.login([sdk.game.gametype.TcpIpHost, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + Starter.LocationEvents.selectDifficultySP(); + + break; + case sdk.game.locations.MainMenuConnecting: + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + Starter.LocationEvents.charSelectError(); + + break; + case sdk.game.locations.ServerDown: + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GameNameExists: + case sdk.game.locations.GameIsFull: + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + + break; + case sdk.game.locations.GatewaySelect: + Controls.GatewayCancel.click(); + + break; + case sdk.game.locations.GameDoesNotExist: + Starter.LocationEvents.gameDoesNotExist(); + + break; + case sdk.game.locations.CharacterCreate: + Controls.BottomLeftExit.click(); + + break; + case sdk.game.locations.OtherMultiplayer: + Starter.LocationEvents.otherMultiplayerSelect(); + + break; + case sdk.game.locations.TcpIp: + Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); + + break; + case sdk.game.locations.TcpIpEnterIp: + Controls.TcpIpCancel.click(); + + break; + default: + if (location !== undefined) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } function main () { - debugLog(me.profile); - addEventListener("copydata", Starter.receiveCopyData); - addEventListener("scriptmsg", Starter.scriptMsgEvent); + debugLog(me.profile); + addEventListener("copydata", Starter.receiveCopyData); + addEventListener("scriptmsg", Starter.scriptMsgEvent); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - delay(500); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - while (!Object.keys(Starter.profileInfo).length) { - D2Bot.getProfile(); - console.log("Getting Profile"); - delay(500); - } + while (!Object.keys(Starter.profileInfo).length) { + D2Bot.getProfile(); + console.log("Getting Profile"); + delay(500); + } - Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); - if (Starter.gameInfo.error) { - delay(200); + if (Starter.gameInfo.error) { + delay(200); - if (!!DataFile.getStats().debugInfo) { - Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); - } + if (!!DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + } - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - D2Bot.updateRuns(); - } + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + 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 (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"; + 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; + if (!Starter.inGame) { + Starter.gameStart = getTickCount(); + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; - DataFile.updateStats("runs", Starter.gameCount); - DataFile.updateStats("ingameTick"); - } + DataFile.updateStats("runs", Starter.gameCount); + DataFile.updateStats("ingameTick"); + } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); - } + D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + } - delay(1000); - } + delay(1000); + } - Starter.isUp = "no"; + Starter.isUp = "no"; - locationAction(getLocation()); - delay(1000); - } + locationAction(getLocation()); + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotMap.dbj b/d2bs/kolbot/D2BotMap.dbj index f9be928fb..b9b6e6053 100644 --- a/d2bs/kolbot/D2BotMap.dbj +++ b/d2bs/kolbot/D2BotMap.dbj @@ -6,39 +6,44 @@ */ function main () { - include("critical.js"); // required - - if (!FileTools.exists("data/" + me.profile + ".json")) { - DataFile.create(); - } + include("critical.js"); // required + + if (!FileTools.exists("data/" + me.profile + ".json")) { + DataFile.create(); + } - addEventListener("copydata", Starter.receiveCopyData); + addEventListener("copydata", Starter.receiveCopyData); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - delay(500); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - while (true) { - delay(1000); - - if (me.gameReady) { - Starter.isUp === "no" && (Starter.isUp = "yes"); - me.ingame && D2Bot.updateStatus("(Char: " + me.charname + ") (Game: " + (me.gamename || "singleplayer") + ") (Level: " + me.charlvl + ")"); - } else { - D2Bot.updateStatus("Out of Game"); - Starter.isUp = "no"; - } + while (true) { + delay(1000); + + if (me.gameReady) { + Starter.isUp === "no" && (Starter.isUp = "yes"); + if (me.ingame) { + D2Bot.updateStatus( + "(Char: " + me.charname + ") (Game: " + + (me.gamename || "singleplayer") + ") (Level: " + me.charlvl + ")" + ); + } + } else { + D2Bot.updateStatus("Out of Game"); + Starter.isUp = "no"; + } - delay(1000); - } + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index f5a3097c8..6b0ac4ec8 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename D2BotMule.dbj * @author kolton, theBGuy @@ -49,983 +50,983 @@ let muleObj; * Mule Data object manipulates external mule datafile */ const MuleData = { - _default: { - account: "", - accNum: 0, - character: "", - charNum: 0, - fullChars: [], - torchChars: [] - }, - // create a new mule datafile - create: function () { - let string = JSON.stringify(this._default); - FileTools.writeText(muleFilename, string); - }, - - // read data from the mule datafile and return the data object - read: function () { - let string = FileTools.readText(muleFilename); - let obj = JSON.parse(string); - - return obj; - }, - - // write a data object to the mule datafile - write: function (obj) { - let string = JSON.stringify(obj); - FileTools.writeText(muleFilename, string); - }, - - // set next account - increase account number in mule datafile - nextAccount: function () { - let obj = MuleData.read(); - - obj.accNum += 1; - obj.account = muleObj.accountPrefix + obj.accNum; - - MuleData.write(Object.assign(this._default, { accNum: obj.accNum, account: obj.account })); - - return obj.account; - }, - - nextChar: function () { - let charSuffix = ""; - let charNumbers = "abcdefghijklmnopqrstuvwxyz"; - let obj = MuleData.read(); - - // dirty - obj.charNum > 25 && (obj.charNum = 0); - let num = obj.accNum.toString(); - - for (let i = 0; i < num.length; i++) { - charSuffix += charNumbers[parseInt(num[i], 10)]; - } - - charSuffix += charNumbers[obj.charNum]; - obj.charNum = obj.charNum + 1; - obj.character = muleObj.charPrefix + charSuffix; - - MuleData.write(obj); - - return obj.character; - }, + _default: { + account: "", + accNum: 0, + character: "", + charNum: 0, + fullChars: [], + torchChars: [] + }, + // create a new mule datafile + create: function () { + let string = JSON.stringify(this._default); + FileTools.writeText(muleFilename, string); + }, + + // read data from the mule datafile and return the data object + read: function () { + let string = FileTools.readText(muleFilename); + let obj = JSON.parse(string); + + return obj; + }, + + // write a data object to the mule datafile + write: function (obj) { + let string = JSON.stringify(obj); + FileTools.writeText(muleFilename, string); + }, + + // set next account - increase account number in mule datafile + nextAccount: function () { + let obj = MuleData.read(); + + obj.accNum += 1; + obj.account = muleObj.accountPrefix + obj.accNum; + + MuleData.write(Object.assign(this._default, { accNum: obj.accNum, account: obj.account })); + + return obj.account; + }, + + nextChar: function () { + let charSuffix = ""; + let charNumbers = "abcdefghijklmnopqrstuvwxyz"; + let obj = MuleData.read(); + + // dirty + obj.charNum > 25 && (obj.charNum = 0); + let num = obj.accNum.toString(); + + for (let i = 0; i < num.length; i++) { + charSuffix += charNumbers[parseInt(num[i], 10)]; + } + + charSuffix += charNumbers[obj.charNum]; + obj.charNum = obj.charNum + 1; + obj.character = muleObj.charPrefix + charSuffix; + + MuleData.write(obj); + + return obj.character; + }, }; const Mule = { - continuousMule: false, - clearedJunk: false, - startTick: 0, - idleTick: 0, - areaTick: 0, - recheckTick: 0, - statusString: "", - - /** + continuousMule: false, + clearedJunk: false, + startTick: 0, + idleTick: 0, + areaTick: 0, + recheckTick: 0, + statusString: "", + + /** * @description background worker to prevent idle disconnect */ - antiIdle: function () { - if (!Starter.inGame) return true; - if (!me.ingame || getTickCount() - me.gamestarttime < Time.minutes(1) || !me.gameReady) return true; - if (Mule.idleTick === 0) { - Mule.idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); - console.log("Game start, anti-idle refresh in: (" + Time.format(Mule.idleTick - getTickCount()) + ")"); - } - if (me.gameReady) { - if (getTickCount() - Mule.idleTick > 0) { - Packet.questRefresh(); - Mule.idleTick += Time.seconds(rand(1200, 1500)); - console.log("Sent anti-idle packet, next refresh in: (" + Time.format(Mule.idleTick - getTickCount()) + ")"); - } - } else if (getLocation() !== null) { - Mule.idleTick = 0; - } - - return true; - }, - - /** + antiIdle: function () { + if (!Starter.inGame) return true; + if (!me.ingame || getTickCount() - me.gamestarttime < Time.minutes(1) || !me.gameReady) return true; + if (Mule.idleTick === 0) { + Mule.idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); + console.log("Game start, anti-idle refresh in: (" + Time.format(Mule.idleTick - getTickCount()) + ")"); + } + if (me.gameReady) { + if (getTickCount() - Mule.idleTick > 0) { + Packet.questRefresh(); + Mule.idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(Mule.idleTick - getTickCount()) + ")"); + } + } else if (getLocation() !== null) { + Mule.idleTick = 0; + } + + return true; + }, + + /** * @description background worker to prevent suicide walks * @todo figure out why this bugs out when used */ - areaWatcher: function () { - if (!Starter.inGame) return true; - // run area check every half second - if (getTickCount() - Mule.areaTick < 500) return true; - Mule.areaTick = getTickCount(); - // check that we are actually in game and that we've been there longer than a minute - if (getLocation() !== null || getTickCount() - me.gamestarttime < Time.minutes(1)) return true; - console.debug("Check Area: " + me.area); - - if (me.ingame && me.gameReady && me.area > 0) { - if (me.area !== sdk.areas.RogueEncampment) { - console.warn("Preventing Suicide Walk! Current Area: " + me.area); - console.trace(); - - Mule.quit(); - } - } - - return true; - }, - - init: function () { - Mule.startTick = getTickCount(); - - while ((getLocation() !== null) && !!me.area && getTickCount() - Mule.startTick < Time.seconds(10)) { - delay(200); - } - - if (getLocation() !== null || !me.ingame || !me.gameReady || !me.inTown) { - return false; - } - - Starter.firstLogin ? (Starter.firstLogin = false) : (status = "begin"); - status !== "begin" && (status = "ready"); - Mule.statusString = "In " + (muleMode === 2 ? "anni " : muleMode === 1 ? "torch " : "") + "mule game."; - - D2Bot.updateStatus(Mule.statusString + " Status: " + status); - D2Bot.printToConsole(Mule.statusString, sdk.colors.D2Bot.DarkGold); - Mule.recheckTick = getTickCount(); - - Town.goToTown(1); - Town.move("stash"); - - // Move away from stash so we don't block muler - let coord = {}; - do { - delay(1000); - coord = CollMap.getRandCoordinate(me.x, -6, 6, me.y, -6, 6); - } while (!Attack.validSpot(coord.x, coord.y)); - - Pather.moveToUnit(coord); + areaWatcher: function () { + if (!Starter.inGame) return true; + // run area check every half second + if (getTickCount() - Mule.areaTick < 500) return true; + Mule.areaTick = getTickCount(); + // check that we are actually in game and that we've been there longer than a minute + if (getLocation() !== null || getTickCount() - me.gamestarttime < Time.minutes(1)) return true; + console.debug("Check Area: " + me.area); + + if (me.ingame && me.gameReady && me.area > 0) { + if (me.area !== sdk.areas.RogueEncampment) { + console.warn("Preventing Suicide Walk! Current Area: " + me.area); + console.trace(); + + Mule.quit(); + } + } + + return true; + }, + + init: function () { + Mule.startTick = getTickCount(); + + while ((getLocation() !== null) && !!me.area && getTickCount() - Mule.startTick < Time.seconds(10)) { + delay(200); + } + + if (getLocation() !== null || !me.ingame || !me.gameReady || !me.inTown) { + return false; + } + + Starter.firstLogin ? (Starter.firstLogin = false) : (status = "begin"); + status !== "begin" && (status = "ready"); + Mule.statusString = "In " + (muleMode === 2 ? "anni " : muleMode === 1 ? "torch " : "") + "mule game."; + + D2Bot.updateStatus(Mule.statusString + " Status: " + status); + D2Bot.printToConsole(Mule.statusString, sdk.colors.D2Bot.DarkGold); + Mule.recheckTick = getTickCount(); + + Town.goToTown(1); + Town.move("stash"); + + // Move away from stash so we don't block muler + let coord = {}; + do { + delay(1000); + coord = CollMap.getRandCoordinate(me.x, -6, 6, me.y, -6, 6); + } while (!Attack.validSpot(coord.x, coord.y)); + + Pather.moveToUnit(coord); - Storage.Init(); - Starter.inGame = true; - if (Mule.continuousMule) { - !muleObj.onlyLogWhenFull && MuleLogger.logChar(); - addEventListener("gameevent", gameEvent); - } - // Worker.runInBackground.areaWatcher = Mule.areaWatcher; // bugs for some reason, quits game then on re-join d2bot spams In mule game for ~30seconds - if (Worker.runInBackground.antiIdle === undefined) { - Worker.runInBackground.antiIdle = Mule.antiIdle; - } - - return true; - }, - - waitForMaster: function () { - console.log("Waiting for muler"); - // forever alone check? - Misc.poll(() => status === "begin", Time.minutes(3), 100); - - if (status !== "begin") { - D2Bot.printToConsole("Nobody joined - stopping.", sdk.colors.D2Bot.Red); - D2Bot.stop(me.profile, true); - } - - me.overhead("begin"); - console.debug("begin"); - }, - - /** + Storage.Init(); + Starter.inGame = true; + if (Mule.continuousMule) { + !muleObj.onlyLogWhenFull && MuleLogger.logChar(); + addEventListener("gameevent", gameEvent); + } + // Worker.runInBackground.areaWatcher = Mule.areaWatcher; // bugs for some reason, quits game then on re-join d2bot spams In mule game for ~30seconds + if (Worker.runInBackground.antiIdle === undefined) { + Worker.runInBackground.antiIdle = Mule.antiIdle; + } + + return true; + }, + + waitForMaster: function () { + console.log("Waiting for muler"); + // forever alone check? + Misc.poll(() => status === "begin", Time.minutes(3), 100); + + if (status !== "begin") { + D2Bot.printToConsole("Nobody joined - stopping.", sdk.colors.D2Bot.Red); + D2Bot.stop(me.profile, true); + } + + me.overhead("begin"); + console.debug("begin"); + }, + + /** * @todo check if there are any other profiles that need to mule while we are already in game? */ - done: function () { - !muleObj.onlyLogWhenFull && MuleLogger.logChar(); - - let obj = MuleData.read(); - - if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { - obj.torchChars.push(me.name); - } - - MuleData.write(obj); - D2Bot.printToConsole("Done muling.", sdk.colors.D2Bot.DarkGold); - console.log("Done muling"); - sendCopyData(null, master, 10, JSON.stringify({ status: "quit" })); - D2Bot.stop(me.profile, true); - }, - - next: function () { - MuleLogger.logChar(); - delay(500); - - [makeNext] = [true]; - let obj = MuleData.read(); - - if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { - obj.torchChars.push(me.name); - } - - obj.fullChars.push(me.name); - MuleData.write(obj); - MuleData.nextChar(); - D2Bot.printToConsole("Mule full, getting next character.", sdk.colors.D2Bot.DarkGold); - - if (Starter.Config.MinGameTime && getTickCount() - Mule.startTick < Starter.Config.MinGameTime * 1000) { - while (getTickCount() - Mule.startTick < Starter.Config.MinGameTime * 1000) { - me.overhead("Stalling for " + Math.round(((Mule.startTick + (Starter.Config.MinGameTime * 1000)) - getTickCount()) / 1000) + " Seconds"); - delay(1000); - } - } - - Mule.quit(); - }, + done: function () { + !muleObj.onlyLogWhenFull && MuleLogger.logChar(); + + let obj = MuleData.read(); + + if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { + obj.torchChars.push(me.name); + } + + MuleData.write(obj); + D2Bot.printToConsole("Done muling.", sdk.colors.D2Bot.DarkGold); + console.log("Done muling"); + sendCopyData(null, master, 10, JSON.stringify({ status: "quit" })); + D2Bot.stop(me.profile, true); + }, + + next: function () { + MuleLogger.logChar(); + delay(500); + + [makeNext] = [true]; + let obj = MuleData.read(); + + if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { + obj.torchChars.push(me.name); + } + + obj.fullChars.push(me.name); + MuleData.write(obj); + MuleData.nextChar(); + D2Bot.printToConsole("Mule full, getting next character.", sdk.colors.D2Bot.DarkGold); + + if (Starter.Config.MinGameTime && getTickCount() - Mule.startTick < Starter.Config.MinGameTime * 1000) { + while (getTickCount() - Mule.startTick < Starter.Config.MinGameTime * 1000) { + me.overhead("Stalling for " + Math.round(((Mule.startTick + (Starter.Config.MinGameTime * 1000)) - getTickCount()) / 1000) + " Seconds"); + delay(1000); + } + } + + Mule.quit(); + }, - quit: function () { - ["default.dbj", "threads/AntiIdle.js", "threads/AreaWatcher.js"].forEach(thread => { - let script = getScript(thread); - if (script && script.running && script.stop()) { - delay(100); - } - }); - Mule.cursorCheck(); - console.log("ÿc8Mule game duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); - - try { - quit(); - } finally { - while (me.ingame) { - delay(100); - } - } - - return true; - }, - - /** + quit: function () { + ["default.dbj", "threads/AntiIdle.js", "threads/AreaWatcher.js"].forEach(thread => { + let script = getScript(thread); + if (script && script.running && script.stop()) { + delay(100); + } + }); + Mule.cursorCheck(); + console.log("ÿc8Mule game duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); + + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } + + return true; + }, + + /** * @todo handle when a bot wants to mule while we are refreshing the game */ - gameRefresh: function () { - if (!this.continuousMule) return this.quit(); - console.log("MaxGameTime Reached: "); - removeEventListener("gameevent", gameEvent); - Mule.quit(); + gameRefresh: function () { + if (!this.continuousMule) return this.quit(); + console.log("MaxGameTime Reached: "); + removeEventListener("gameevent", gameEvent); + Mule.quit(); - Starter.firstLogin = true; - console.log("updating runs"); - D2Bot.updateRuns(); - status = "ready"; - Starter.inGame = false; + Starter.firstLogin = true; + console.log("updating runs"); + D2Bot.updateRuns(); + status = "ready"; + Starter.inGame = false; - delay(1000); - Controls.LobbyQuit.click(); // Quit from Lobby - ControlAction.timeoutDelay("Refresh game", 330 * 1000); // 5.5 minutes + delay(1000); + Controls.LobbyQuit.click(); // Quit from Lobby + ControlAction.timeoutDelay("Refresh game", 330 * 1000); // 5.5 minutes - return true; - }, + return true; + }, - /** + /** * @param {number} time */ - ingameTimeout: function (time) { - let tick = getTickCount(); - - while (getTickCount() - tick < time) { - if (me.ingame && me.gameReady && !!me.area) break; - - // game doesn't exist, might need more locs - if (getLocation() === sdk.game.locations.GameDoesNotExist) { - break; - } - - delay(100); - } - - return (me.ingame && me.gameReady && !!me.area); - }, - - foreverAlone: function () { - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name) return false; - } while (party.getNext()); - } - - return true; - }, - - checkAnniTorch: function () { - while (!me.gameReady) { - delay(500); - } - - return me.getItemsEx() - .filter(i => i.isInStorage && i.unique && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(i.classid)) - .some(i => [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(i.classid)); - }, - - stashItems: function () { - me.getItemsEx() - .filter(item => item.isInInventory) - .sort((a, b) => (b.sizex * b.sizey - a.sizex * a.sizey)) - .forEach(item => { - Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item); - }); - - return true; - }, - - cursorCheck: function () { - let cursorItem = Game.getCursorUnit(); - - if (cursorItem) { - if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { - console.warn("Can't place " + cursorItem.prettyPrint + " in inventory"); - cursorItem.drop(); - } - } + ingameTimeout: function (time) { + let tick = getTickCount(); + + while (getTickCount() - tick < time) { + if (me.ingame && me.gameReady && !!me.area) break; + + // game doesn't exist, might need more locs + if (getLocation() === sdk.game.locations.GameDoesNotExist) { + break; + } + + delay(100); + } + + return (me.ingame && me.gameReady && !!me.area); + }, + + foreverAlone: function () { + let party = getParty(); + + if (party) { + do { + if (party.name !== me.name) return false; + } while (party.getNext()); + } + + return true; + }, + + checkAnniTorch: function () { + while (!me.gameReady) { + delay(500); + } + + return me.getItemsEx() + .filter(i => i.isInStorage && i.unique && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(i.classid)) + .some(i => [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(i.classid)); + }, + + stashItems: function () { + me.getItemsEx() + .filter(item => item.isInInventory) + .sort((a, b) => (b.sizex * b.sizey - a.sizex * a.sizey)) + .forEach(item => { + Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item); + }); + + return true; + }, + + cursorCheck: function () { + let cursorItem = Game.getCursorUnit(); + + if (cursorItem) { + if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { + console.warn("Can't place " + cursorItem.prettyPrint + " in inventory"); + cursorItem.drop(); + } + } - return true; - }, - - pickItems: function () { - let waitTick = getTickCount(); - let rval = "fail"; - let list = []; - let override = false; - - while (!me.name || !me.gameReady) { - if (!me.ingame) return rval; - delay(100); - } - - if (!Mule.clearedJunk) { - me.getItemsEx() - .filter(item => item.isInInventory && Town.ignoreType(item.itemType) && (muleMode === 0 || item.classid !== sdk.items.ScrollofIdentify)) - .forEach(item => { - try { - item.drop(); - } catch (e) { - console.warn("Failed to drop an item."); - } - }); - Mule.clearedJunk = true; // only do this once - } - - const getItems = () => getUnits(sdk.unittype.Item).filter(i => i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType)); - - while (me.gameReady) { - if (masterStatus.status === "done" || Mule.continuousMule || override) { - override = false; - let item = Game.getItem(); - - if (item) { - list = getItems(); - } - - // If and only if there is nothing left are we "done" - if (list.length === 0) { - rval = Mule.continuousMule ? "ready" : "done"; - - break; - } - - // pick large items first by sorting items by size in descending order and move gheed's charm to the end of the list - list.sort(function(a, b) { - if (a.isGheeds && !Pickit.canPick(a)) return 1; - if (b.isGheeds && !Pickit.canPick(b)) return -1; - - return (b.sizex * b.sizey - a.sizex * a.sizey); - }); - - while (list.length > 0) { - item = list.shift(); - let canFit = Storage.Inventory.CanFit(item); - - // Torch and Anni handling - if (muleMode > 0 && item.unique && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(item.classid) && !Pickit.canPick(item)) { - let msg = item.classid === sdk.items.LargeCharm ? "Mule already has a Torch." : "Mule already has a Anni."; - D2Bot.printToConsole(msg, sdk.colors.D2Bot.DarkGold); - rval = "next"; - } - - // Gheed's Fortune handling - if (item.isGheeds && !Pickit.canPick(item)) { - D2Bot.printToConsole("Mule already has Gheed's.", sdk.colors.D2Bot.DarkGold); - rval = "next"; - } - - if (!canFit && Mule.stashItems()) { - canFit = Storage.Inventory.CanFit(item); - } - - if (canFit) { - Pickit.pickItem(item); - } else { - rval = "next"; - } - } - - if (rval === "next") { - break; - } - } else { - if (!Mule.continuousMule) { - sendCopyData(null, master, 10, JSON.stringify({ status: "report" })); - Misc.poll(() => masterStatus.status === "done", Time.seconds(5), 50); - } else { - if (getTickCount() - waitTick > Time.minutes(10)) { - break; - } - } - // safety check - if (getTickCount() - waitTick > Time.minutes(3) && getItems().length) { - // why are there items on the ground but we haven't recieved the go ahead? - console.debug("Anyone in game? " + Mule.foreverAlone(), masterStatus, Starter.firstLogin); - override = true; - } - } - - delay(500); - } - - return rval; - }, + return true; + }, + + pickItems: function () { + let waitTick = getTickCount(); + let rval = "fail"; + let list = []; + let override = false; + + while (!me.name || !me.gameReady) { + if (!me.ingame) return rval; + delay(100); + } + + if (!Mule.clearedJunk) { + me.getItemsEx() + .filter(item => item.isInInventory && Town.ignoreType(item.itemType) && (muleMode === 0 || item.classid !== sdk.items.ScrollofIdentify)) + .forEach(item => { + try { + item.drop(); + } catch (e) { + console.warn("Failed to drop an item."); + } + }); + Mule.clearedJunk = true; // only do this once + } + + const getItems = () => getUnits(sdk.unittype.Item).filter(i => i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType)); + + while (me.gameReady) { + if (masterStatus.status === "done" || Mule.continuousMule || override) { + override = false; + let item = Game.getItem(); + + if (item) { + list = getItems(); + } + + // If and only if there is nothing left are we "done" + if (list.length === 0) { + rval = Mule.continuousMule ? "ready" : "done"; + + break; + } + + // pick large items first by sorting items by size in descending order and move gheed's charm to the end of the list + list.sort(function(a, b) { + if (a.isGheeds && !Pickit.canPick(a)) return 1; + if (b.isGheeds && !Pickit.canPick(b)) return -1; + + return (b.sizex * b.sizey - a.sizex * a.sizey); + }); + + while (list.length > 0) { + item = list.shift(); + let canFit = Storage.Inventory.CanFit(item); + + // Torch and Anni handling + if (muleMode > 0 && item.unique && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(item.classid) && !Pickit.canPick(item)) { + let msg = item.classid === sdk.items.LargeCharm ? "Mule already has a Torch." : "Mule already has a Anni."; + D2Bot.printToConsole(msg, sdk.colors.D2Bot.DarkGold); + rval = "next"; + } + + // Gheed's Fortune handling + if (item.isGheeds && !Pickit.canPick(item)) { + D2Bot.printToConsole("Mule already has Gheed's.", sdk.colors.D2Bot.DarkGold); + rval = "next"; + } + + if (!canFit && Mule.stashItems()) { + canFit = Storage.Inventory.CanFit(item); + } + + if (canFit) { + Pickit.pickItem(item); + } else { + rval = "next"; + } + } + + if (rval === "next") { + break; + } + } else { + if (!Mule.continuousMule) { + sendCopyData(null, master, 10, JSON.stringify({ status: "report" })); + Misc.poll(() => masterStatus.status === "done", Time.seconds(5), 50); + } else { + if (getTickCount() - waitTick > Time.minutes(10)) { + break; + } + } + // safety check + if (getTickCount() - waitTick > Time.minutes(3) && getItems().length) { + // why are there items on the ground but we haven't recieved the go ahead? + console.debug("Anyone in game? " + Mule.foreverAlone(), masterStatus, Starter.firstLogin); + override = true; + } + } + + delay(500); + } + + return rval; + }, }; new Overrides.Override (Starter, Starter.receiveCopyData, function (orignal, mode, msg) { - if (mode === 3) return; - // master/mule communication function - switch (mode) { - case 10: // mule request - let obj = JSON.parse(msg); - - if (Mule.continuousMule && me.ingame) { - sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "ready" })); - } else { - if (!master) { - let masterInfo = AutoMule.getMaster(obj); - - if (masterInfo) { - master = masterInfo.profile; - muleMode = masterInfo.mode; - } - } else { - if (obj.profile === master) { - sendCopyData(null, master, 10, JSON.stringify({ status: status })); - } else { - sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "busy" })); - } - } - } - - break; - case 11: // begin item pickup - status = "begin"; - - break; - case 12: // get master's status - masterStatus = JSON.parse(msg); - console.debug(msg); - - break; - default: - orignal(mode, msg); - } + if (mode === 3) return; + // master/mule communication function + switch (mode) { + case 10: // mule request + let obj = JSON.parse(msg); + + if (Mule.continuousMule && me.ingame) { + sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "ready" })); + } else { + if (!master) { + let masterInfo = AutoMule.getMaster(obj); + + if (masterInfo) { + master = masterInfo.profile; + muleMode = masterInfo.mode; + } + } else { + if (obj.profile === master) { + sendCopyData(null, master, 10, JSON.stringify({ status: status })); + } else { + sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "busy" })); + } + } + } + + break; + case 11: // begin item pickup + status = "begin"; + + break; + case 12: // get master's status + masterStatus = JSON.parse(msg); + console.debug(msg); + + break; + default: + orignal(mode, msg); + } }).apply(); new Overrides.Override (Starter, Starter.updateCount, function () { - D2Bot.updateCount(); - delay(1000); - Controls.BattleNet.click(); - - let obj = MuleData.read(); - let info = { - realm: muleObj.realm, - account: obj.account, - password: muleObj.accountPassword - }; - - MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); - ControlAction.loginAccount(info); - delay(1000); - Controls.BottomLeftExit.click(); + D2Bot.updateCount(); + delay(1000); + Controls.BattleNet.click(); + + let obj = MuleData.read(); + let info = { + realm: muleObj.realm, + account: obj.account, + password: muleObj.accountPassword + }; + + MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); + ControlAction.loginAccount(info); + delay(1000); + Controls.BottomLeftExit.click(); }).apply(); function locationAction (location) { - let obj, info, string, text; - - switch (location) { - case sdk.game.locations.PreSplash: - case sdk.game.locations.SplashScreen: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); - - if (Starter.inGame) { - print("updating runs"); - D2Bot.updateRuns(); - status = "ready"; - Starter.inGame = false; - } - - if (makeNext) { - Controls.LobbyQuit.click(); - } else { - Starter.LocationEvents.openJoinGameWindow(); - } - - break; - case sdk.game.locations.CreateGame: - D2Bot.updateStatus("Creating Game"); - - // remove level restriction - Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); - - // Max number of players - Controls.MaxPlayerCount.setText("8"); - - delay(2000); - - // FTJ handler - if (status === "pending") { - D2Bot.printToConsole("Failed to create game"); - ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); - D2Bot.updateRuns(); - } - - createGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); - if (!Mule.ingameTimeout(Time.minutes(1))) { - console.debug("Failed to get in game, current location: " + getLocation() + " inGame? " + me.ingame + " area? " + me.area); - break; - } + let obj, info, string, text; + + switch (location) { + case sdk.game.locations.PreSplash: + case sdk.game.locations.SplashScreen: + ControlAction.click(); + + break; + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + D2Bot.updateStatus("Lobby"); + + if (Starter.inGame) { + print("updating runs"); + D2Bot.updateRuns(); + status = "ready"; + Starter.inGame = false; + } + + if (makeNext) { + Controls.LobbyQuit.click(); + } else { + Starter.LocationEvents.openJoinGameWindow(); + } + + break; + case sdk.game.locations.CreateGame: + D2Bot.updateStatus("Creating Game"); + + // remove level restriction + Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); + + // Max number of players + Controls.MaxPlayerCount.setText("8"); + + delay(2000); + + // FTJ handler + if (status === "pending") { + D2Bot.printToConsole("Failed to create game"); + ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); + D2Bot.updateRuns(); + } + + createGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); + if (!Mule.ingameTimeout(Time.minutes(1))) { + console.debug("Failed to get in game, current location: " + getLocation() + " inGame? " + me.ingame + " area? " + me.area); + break; + } - status = "pending"; - - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); - - break; - case sdk.game.locations.JoinGame: - D2Bot.updateStatus("Join Game"); - - if (status === "pending") { - D2Bot.printToConsole("Failed to join game"); - ControlAction.timeoutDelay("Join Delay", Starter.Config.FTJDelay * 1000); - D2Bot.updateRuns(); - } - - if (!Mule.continuousMule) { - D2Bot.requestGame(master); - delay(100); - } - - if (Starter.inGame) { - print("updating runs"); - D2Bot.updateRuns(); - status = "ready"; - Starter.inGame = false; - } - - delay(2000); - - if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "" && Starter.joinInfo.inGame) { - joinGame(Starter.joinInfo.gameName, Starter.joinInfo.gamePass); - } else { - joinGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); - } - - !Starter.firstLogin && (status = "pending"); - - if (Mule.ingameTimeout(Time.minutes(1))) { - console.debug("Ingame timeout done."); - } - - // could not join game - getLocation() === sdk.game.locations.Lobby && !me.ingame && Controls.CreateGameWindow.click(); - - break; - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - makeNext && (makeNext = false); - - obj = MuleData.read(); - - if (!obj.account || obj.account.indexOf(muleObj.accountPrefix) < 0) { - MuleData.nextAccount(); - obj = MuleData.read(); - } - - info = { - realm: muleObj.realm, - account: obj.account, - password: muleObj.accountPassword - }; - - if (Starter.makeAccount) { - ControlAction.makeAccount(info); - D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); - Starter.makeAccount = false; - - break; - } - - MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); - !ControlAction.loginAccount(info) && (Starter.makeAccount = true); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelect: - case sdk.game.locations.NewCharSelected: - case sdk.game.locations.CharacterCreate: - case sdk.game.locations.CharSelectNoChars: - string = ""; - text = Controls.CharSelectError.getText(); - - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; - - if (i !== text.length - 1) { - string += " "; - } - } - - if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { // CDKey disabled from realm play - 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(me.profile, true); - } - } - } - - // Single Player screen fix - // TODO: see if this is still needed. d2bs doesn't load scripts twice anymore - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.BottomLeftExit.click(); - - break; - } - - // Can't create character, button greyed out = high likelyhood of realm down - if (getLocation() === sdk.game.locations.CharSelectNoChars && Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { - D2Bot.updateStatus("Realm Down"); - delay(1000); - - if (!Controls.BottomLeftExit.click()) { - break; - } - - Starter.updateCount(); - ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); - D2Bot.CDKeyRD(); - - if (Starter.gameInfo.switchKeys) { - D2Bot.printToConsole("Realm Down - Changing CD-Key"); - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.restart(); - } - } - - obj = MuleData.read(); - maxCharCount = (muleObj.charsPerAcc > 0 ? Math.min(muleObj.charsPerAcc, 18) : 8); - - if (makeNext) { - if (obj.fullChars.length >= maxCharCount || (muleMode > 0 && obj.torchChars.length >= maxCharCount)) { - Controls.BottomLeftExit.click(); - MuleData.nextAccount(); - - break; - } - - makeNext = false; - } - - if (!obj.character || obj.character.indexOf(muleObj.charPrefix) < 0) { - MuleData.nextChar(); - - obj = MuleData.read(); - } - - info = { - account: obj.account, - charName: obj.character, - ladder: muleObj.ladder, - hardcore: muleObj.hardcore, - expansion: muleObj.expansion, - charClass: "amazon" - }; - - if (muleMode > 0 && obj.torchChars.includes(info.charName)) { - MuleData.nextChar(); - - break; - } - - if (ControlAction.findCharacter(info)) { - ControlAction.loginCharacter(info, false); - } else { - // premade account that's already full - if (ControlAction.getCharacters().length >= maxCharCount) { - Controls.BottomLeftExit.click(); - MuleData.nextAccount(); - - break; - } - - if (!ControlAction.makeCharacter(info)) { - // TODO: check if acc is full and cancel location 15 and 29 if true - MuleData.nextChar(); - - break; - } - - D2Bot.printToConsole("Made character: " + info.charName, sdk.colors.D2Bot.DarkGold); - } - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Controls.CreateGameWindow.click(); - - break; - case sdk.game.locations.OkCenteredErrorPopUp: - Controls.OkCentered.click(); - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.ServerDown: - case sdk.game.locations.GameIsFull: - break; - case sdk.game.locations.OtherMultiplayer: - // probably should implement way to use open-bnet - Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined && location !== null) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + status = "pending"; + + break; + case sdk.game.locations.WaitingInLine: + Starter.LocationEvents.waitingInLine(); + + break; + case sdk.game.locations.JoinGame: + D2Bot.updateStatus("Join Game"); + + if (status === "pending") { + D2Bot.printToConsole("Failed to join game"); + ControlAction.timeoutDelay("Join Delay", Starter.Config.FTJDelay * 1000); + D2Bot.updateRuns(); + } + + if (!Mule.continuousMule) { + D2Bot.requestGame(master); + delay(100); + } + + if (Starter.inGame) { + print("updating runs"); + D2Bot.updateRuns(); + status = "ready"; + Starter.inGame = false; + } + + delay(2000); + + if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "" && Starter.joinInfo.inGame) { + joinGame(Starter.joinInfo.gameName, Starter.joinInfo.gamePass); + } else { + joinGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); + } + + !Starter.firstLogin && (status = "pending"); + + if (Mule.ingameTimeout(Time.minutes(1))) { + console.debug("Ingame timeout done."); + } + + // could not join game + getLocation() === sdk.game.locations.Lobby && !me.ingame && Controls.CreateGameWindow.click(); + + break; + case sdk.game.locations.Ladder: + case sdk.game.locations.ChannelList: + break; + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + makeNext && (makeNext = false); + + obj = MuleData.read(); + + if (!obj.account || obj.account.indexOf(muleObj.accountPrefix) < 0) { + MuleData.nextAccount(); + obj = MuleData.read(); + } + + info = { + realm: muleObj.realm, + account: obj.account, + password: muleObj.accountPassword + }; + + if (Starter.makeAccount) { + ControlAction.makeAccount(info); + D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); + Starter.makeAccount = false; + + break; + } + + MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); + !ControlAction.loginAccount(info) && (Starter.makeAccount = true); + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.CharSelect: + case sdk.game.locations.NewCharSelected: + case sdk.game.locations.CharacterCreate: + case sdk.game.locations.CharSelectNoChars: + string = ""; + text = Controls.CharSelectError.getText(); + + if (text) { + for (let i = 0; i < text.length; i++) { + string += text[i]; + + if (i !== text.length - 1) { + string += " "; + } + } + + if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { // CDKey disabled from realm play + 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(me.profile, true); + } + } + } + + // Single Player screen fix + // TODO: see if this is still needed. d2bs doesn't load scripts twice anymore + if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { + Controls.BottomLeftExit.click(); + + break; + } + + // Can't create character, button greyed out = high likelyhood of realm down + if (getLocation() === sdk.game.locations.CharSelectNoChars && Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { + D2Bot.updateStatus("Realm Down"); + delay(1000); + + if (!Controls.BottomLeftExit.click()) { + break; + } + + Starter.updateCount(); + ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); + D2Bot.CDKeyRD(); + + if (Starter.gameInfo.switchKeys) { + D2Bot.printToConsole("Realm Down - Changing CD-Key"); + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.restart(); + } + } + + obj = MuleData.read(); + maxCharCount = (muleObj.charsPerAcc > 0 ? Math.min(muleObj.charsPerAcc, 18) : 8); + + if (makeNext) { + if (obj.fullChars.length >= maxCharCount || (muleMode > 0 && obj.torchChars.length >= maxCharCount)) { + Controls.BottomLeftExit.click(); + MuleData.nextAccount(); + + break; + } + + makeNext = false; + } + + if (!obj.character || obj.character.indexOf(muleObj.charPrefix) < 0) { + MuleData.nextChar(); + + obj = MuleData.read(); + } + + info = { + account: obj.account, + charName: obj.character, + ladder: muleObj.ladder, + hardcore: muleObj.hardcore, + expansion: muleObj.expansion, + charClass: "amazon" + }; + + if (muleMode > 0 && obj.torchChars.includes(info.charName)) { + MuleData.nextChar(); + + break; + } + + if (ControlAction.findCharacter(info)) { + ControlAction.loginCharacter(info, false); + } else { + // premade account that's already full + if (ControlAction.getCharacters().length >= maxCharCount) { + Controls.BottomLeftExit.click(); + MuleData.nextAccount(); + + break; + } + + if (!ControlAction.makeCharacter(info)) { + // TODO: check if acc is full and cancel location 15 and 29 if true + MuleData.nextChar(); + + break; + } + + D2Bot.printToConsole("Made character: " + info.charName, sdk.colors.D2Bot.DarkGold); + } + + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + break; + case sdk.game.locations.MainMenuConnecting: + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + Starter.LocationEvents.charSelectError(); + + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GameNameExists: + Controls.JoinGameWindow.click(); + + break; + case sdk.game.locations.GatewaySelect: + Controls.GatewayCancel.click(); + + break; + case sdk.game.locations.GameDoesNotExist: + Controls.CreateGameWindow.click(); + + break; + case sdk.game.locations.OkCenteredErrorPopUp: + Controls.OkCentered.click(); + Controls.BottomLeftExit.click(); + + break; + case sdk.game.locations.ServerDown: + case sdk.game.locations.GameIsFull: + break; + case sdk.game.locations.OtherMultiplayer: + // probably should implement way to use open-bnet + Controls.OtherMultiplayerCancel.click(); + + break; + case sdk.game.locations.TcpIp: + case sdk.game.locations.TcpIpEnterIp: + Controls.TcpIpCancel.click(); + + break; + default: + if (location !== undefined && location !== null) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } // item event check instead? // eslint-disable-next-line no-unused-vars function gameEvent (mode, param1, param2, name1, name2) { - if (!me.ingame || !me.gameReady || !me.name) { - return; - } - - 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 (Mule.foreverAlone()) { - console.debug("Waiting"); - status = "ready"; - } - - break; - case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." - if (name1.trim() !== me.name.trim()) { - console.debug("begin"); - status = "begin"; - } - - break; - } + if (!me.ingame || !me.gameReady || !me.name) { + return; + } + + 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 (Mule.foreverAlone()) { + console.debug("Waiting"); + status = "ready"; + } + + break; + case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." + if (name1.trim() !== me.name.trim()) { + console.debug("begin"); + status = "begin"; + } + + break; + } } function main () { - // basics -- don't touch - addEventListener("copydata", Starter.receiveCopyData); - - while (!Starter.handle) { - delay(100); - } - - DataFile.updateStats("handle", Starter.handle); - D2Bot.init(); - load("threads/heartbeat.js"); - - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } - - D2Bot.updateRuns(); // we need the mule to swap keys somehow after all - delay(1000); - - // mule/master data initialization block - Mule.continuousMule = AutoMule.isContinousMule(); - - if (Mule.continuousMule) { - console.log("Continuous Mule Mode Started"); - muleMode = AutoMule.getMuleMode(); - muleObj = AutoMule.getMuleObject(muleMode, "", true); - muleFilename = AutoMule.getMuleFilename(muleMode, "", true); - } else { - // Wait for master before login = give room to determine muling mode (normal or torch) - while (!master) { - delay(100); - } - - console.log("Master found: " + master); - - muleObj = AutoMule.getMuleObject(muleMode, master); - muleFilename = AutoMule.getMuleFilename(muleMode, master); - } - - console.log("Mule filename: " + muleFilename); - - try { - // ugly solution to uglier problem - pickItem area update - !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); - - // create mule datafile if it doesn't exist - !FileTools.exists(muleFilename) && MuleData.create(); - - let obj = MuleData.read(); - obj.account && obj.account.indexOf(muleObj.accountPrefix) < 0 && MuleData.create(); - } catch (e) { - // probably should try again if fails to make file or shut down instead of continuing loop - console.warn("Caught exception creating data files."); - console.error(e); - D2Bot.printToConsole("DataFileException: " + e.message + " (" + e.fileName.substring(e.fileName.lastIndexOf("\\") + 1, e.fileName.length) + " #" + e.lineNumber + ")"); - } - - // begin - MainLoop: - while (true) { - try { - while (me.ingame) { - if (me.gameReady) { - if (!Starter.inGame) { - if (!Mule.init()) { - console.debug("Failed to init. Trying again. Ingame and ready? " + (me.ingame && me.gameReady && !!me.area)); - delay(1000); - continue; - } - } - - if (!Mule.continuousMule) { - Mule.waitForMaster(); - } - - D2Bot.updateStatus(Mule.statusString + " Status: " + status + Starter.timer(me.gamestarttime)); - - if (status === "begin") { - switch (Mule.pickItems()) { - // done picking, tell the master to leave game and kill mule profile - case "done": - Mule.done(); - - return; - // can't fit more items, get to next character or account - case "next": - Mule.next(); - - // should fix hitting gameRefresh when making a new character - continue MainLoop; - case "ready": - Mule.recheckTick = getTickCount(); - - break; - case "fail": - // Try again - break; - } - } - - if (Mule.continuousMule) { - if (Starter.Config.MaxGameTime > 0 && getTickCount() - me.gamestarttime > Time.minutes(Starter.Config.MaxGameTime) && Mule.foreverAlone()) { - Mule.gameRefresh(); - - break; - } else if (getTickCount() - Mule.recheckTick > Time.minutes(10)) { - // recheck every 10 minutes? - status = "begin"; - } - } - } - - delay(1000); - } - - if (!me.ingame) { - delay(1000); - locationAction(getLocation()); - } - } catch (e2) { - console.warn("Caught an exception in the main loop."); - console.error(e2); - D2Bot.printToConsole("MainLoopException: " + e2.message + " (" + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + " #" + e2.lineNumber + ")"); - } - - delay(100); - } + // basics -- don't touch + addEventListener("copydata", Starter.receiveCopyData); + + while (!Starter.handle) { + delay(100); + } + + DataFile.updateStats("handle", Starter.handle); + D2Bot.init(); + load("threads/heartbeat.js"); + + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } + + D2Bot.updateRuns(); // we need the mule to swap keys somehow after all + delay(1000); + + // mule/master data initialization block + Mule.continuousMule = AutoMule.isContinousMule(); + + if (Mule.continuousMule) { + console.log("Continuous Mule Mode Started"); + muleMode = AutoMule.getMuleMode(); + muleObj = AutoMule.getMuleObject(muleMode, "", true); + muleFilename = AutoMule.getMuleFilename(muleMode, "", true); + } else { + // Wait for master before login = give room to determine muling mode (normal or torch) + while (!master) { + delay(100); + } + + console.log("Master found: " + master); + + muleObj = AutoMule.getMuleObject(muleMode, master); + muleFilename = AutoMule.getMuleFilename(muleMode, master); + } + + console.log("Mule filename: " + muleFilename); + + try { + // ugly solution to uglier problem - pickItem area update + !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); + + // create mule datafile if it doesn't exist + !FileTools.exists(muleFilename) && MuleData.create(); + + let obj = MuleData.read(); + obj.account && obj.account.indexOf(muleObj.accountPrefix) < 0 && MuleData.create(); + } catch (e) { + // probably should try again if fails to make file or shut down instead of continuing loop + console.warn("Caught exception creating data files."); + console.error(e); + D2Bot.printToConsole("DataFileException: " + e.message + " (" + e.fileName.substring(e.fileName.lastIndexOf("\\") + 1, e.fileName.length) + " #" + e.lineNumber + ")"); + } + + // begin + MainLoop: + while (true) { + try { + while (me.ingame) { + if (me.gameReady) { + if (!Starter.inGame) { + if (!Mule.init()) { + console.debug("Failed to init. Trying again. Ingame and ready? " + (me.ingame && me.gameReady && !!me.area)); + delay(1000); + continue; + } + } + + if (!Mule.continuousMule) { + Mule.waitForMaster(); + } + + D2Bot.updateStatus(Mule.statusString + " Status: " + status + Starter.timer(me.gamestarttime)); + + if (status === "begin") { + switch (Mule.pickItems()) { + // done picking, tell the master to leave game and kill mule profile + case "done": + Mule.done(); + + return; + // can't fit more items, get to next character or account + case "next": + Mule.next(); + + // should fix hitting gameRefresh when making a new character + continue MainLoop; + case "ready": + Mule.recheckTick = getTickCount(); + + break; + case "fail": + // Try again + break; + } + } + + if (Mule.continuousMule) { + if (Starter.Config.MaxGameTime > 0 && getTickCount() - me.gamestarttime > Time.minutes(Starter.Config.MaxGameTime) && Mule.foreverAlone()) { + Mule.gameRefresh(); + + break; + } else if (getTickCount() - Mule.recheckTick > Time.minutes(10)) { + // recheck every 10 minutes? + status = "begin"; + } + } + } + + delay(1000); + } + + if (!me.ingame) { + delay(1000); + locationAction(getLocation()); + } + } catch (e2) { + console.warn("Caught an exception in the main loop."); + console.error(e2); + D2Bot.printToConsole("MainLoopException: " + e2.message + " (" + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + " #" + e2.lineNumber + ")"); + } + + delay(100); + } } diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 3566281a5..6a4297248 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename D2BotMuleLogger.dbj * @author kolton, theBGuy @@ -22,7 +23,7 @@ include("systems/mulelogger/MuleLogger.js"); let Controls = require("./modules/Control"); if (!FileTools.exists("data/" + me.profile + ".json")) { - DataFile.create(); + DataFile.create(); } let currAcc; @@ -32,327 +33,327 @@ let accounts = []; let chars = []; function parseInfo() { - usingDroper && parseDropperAccounts(accounts, chars); + usingDroper && parseDropperAccounts(accounts, chars); - for (let i in MuleLogger.LogAccounts) { - if (MuleLogger.LogAccounts.hasOwnProperty(i) && typeof i === "string") { - accounts.push(i); - chars.push(MuleLogger.LogAccounts[i]); - } - } + for (let i in MuleLogger.LogAccounts) { + if (MuleLogger.LogAccounts.hasOwnProperty(i) && typeof i === "string") { + accounts.push(i); + chars.push(MuleLogger.LogAccounts[i]); + } + } } function locationAction (location) { - let i, currChar, - obj = {}; - - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); - - if (Starter.inGame) { - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); - } - - print("updating runs"); - D2Bot.updateRuns(); - delay(1000); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - Controls.LobbyQuit.click(); - - break; - } - - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); - - break; - case sdk.game.locations.CreateGame: - D2Bot.updateStatus("Creating Game"); - - // remove level restriction - Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); - - // Max number of players - Controls.MaxPlayerCount.setText("8"); - - if (Starter.gameCount >= 99) { - Starter.gameCount = 1; - - DataFile.updateStats("runs", Starter.gameCount); - } - - if (Starter.lastGameStatus === "pending") { - D2Bot.printToConsole("Failed to create game"); - - Starter.gameCount += 1; - } - - ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); - createGame(MuleLogger.LogGame[0] + Starter.gameCount, MuleLogger.LogGame[1], 0); - Starter.locationTimeout(5000, location); - Starter.lastGameStatus = "pending"; - - break; - case sdk.game.locations.JoinGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.SplashScreen: - if (!accounts.length) { - MuleLogger.remove(); - D2Bot.printToConsole("Done logging mules!"); - D2Bot.stop(); - - break; - } - - if (FileTools.exists("logs/MuleLog.json")) { - obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); - - if (obj.currAcc) { - for (i = 0; i < accounts.length; i += 1) { - if (accounts[i].split("/")[0] === obj.currAcc) { - accounts.splice(0, i); - chars.splice(0, i); - i -= 1; - - break; - } - } - } - } - - currAcc = accounts[0]; - currAcc = currAcc.split("/"); - charList = chars[0]; - obj.currAcc = currAcc[0]; - - print("ÿc4Mule Loggerÿc2: Login account: " + currAcc[0]); - MuleLogger.save(md5(currAcc[2].toLowerCase() + currAcc[0].toLowerCase()), currAcc[1]); - - if (ControlAction.loginAccount({account: currAcc[0], password: currAcc[1], realm: currAcc[2]})) { - accounts.shift(); // remove current account from the list - } - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.CharSelect: - // Single Player screen fix - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control && Controls.BottomLeftExit.click()) { - break; - } - - if (!charList.length && Controls.BottomLeftExit.click()) { - break; - } - - charList[0] === "all" && (charList = ControlAction.getCharacters()); - - if (FileTools.exists("logs/MuleLog.json")) { - obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); + let i, currChar, + obj = {}; + + switch (location) { + case sdk.game.locations.PreSplash: + ControlAction.click(); + + break; + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + D2Bot.updateStatus("Lobby"); + + if (Starter.inGame) { + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + } + + print("updating runs"); + D2Bot.updateRuns(); + delay(1000); + + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + Controls.LobbyQuit.click(); + + break; + } + + Starter.LocationEvents.openCreateGameWindow(); + + break; + case sdk.game.locations.WaitingInLine: + Starter.LocationEvents.waitingInLine(); + + break; + case sdk.game.locations.CreateGame: + D2Bot.updateStatus("Creating Game"); + + // remove level restriction + Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); + + // Max number of players + Controls.MaxPlayerCount.setText("8"); + + if (Starter.gameCount >= 99) { + Starter.gameCount = 1; + + DataFile.updateStats("runs", Starter.gameCount); + } + + if (Starter.lastGameStatus === "pending") { + D2Bot.printToConsole("Failed to create game"); + + Starter.gameCount += 1; + } + + ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); + createGame(MuleLogger.LogGame[0] + Starter.gameCount, MuleLogger.LogGame[1], 0); + Starter.locationTimeout(5000, location); + Starter.lastGameStatus = "pending"; + + break; + case sdk.game.locations.JoinGame: + case sdk.game.locations.Ladder: + case sdk.game.locations.ChannelList: + Starter.LocationEvents.openCreateGameWindow(); + + break; + case sdk.game.locations.MainMenu: + case sdk.game.locations.Login: + case sdk.game.locations.SplashScreen: + if (!accounts.length) { + MuleLogger.remove(); + D2Bot.printToConsole("Done logging mules!"); + D2Bot.stop(); + + break; + } + + if (FileTools.exists("logs/MuleLog.json")) { + obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); + + if (obj.currAcc) { + for (i = 0; i < accounts.length; i += 1) { + if (accounts[i].split("/")[0] === obj.currAcc) { + accounts.splice(0, i); + chars.splice(0, i); + i -= 1; + + break; + } + } + } + } + + currAcc = accounts[0]; + currAcc = currAcc.split("/"); + charList = chars[0]; + obj.currAcc = currAcc[0]; + + print("ÿc4Mule Loggerÿc2: Login account: " + currAcc[0]); + MuleLogger.save(md5(currAcc[2].toLowerCase() + currAcc[0].toLowerCase()), currAcc[1]); + + if (ControlAction.loginAccount({ account: currAcc[0], password: currAcc[1], realm: currAcc[2] })) { + accounts.shift(); // remove current account from the list + } + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.CharSelect: + // Single Player screen fix + if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control && Controls.BottomLeftExit.click()) { + break; + } + + if (!charList.length && Controls.BottomLeftExit.click()) { + break; + } + + charList[0] === "all" && (charList = ControlAction.getCharacters()); + + if (FileTools.exists("logs/MuleLog.json")) { + obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); - if (obj.currChar) { - for (i = 0; i < charList.length; i += 1) { - if (charList[i] === obj.currChar) { - // Remove the previous currChar as well - charList.splice(0, i + 1); - - break; - } - } - } - } - - // last char in acc = trigger next acc - if (!charList.length) { - print("No more characters"); - accounts.shift(); // remove current account from the list - chars.shift(); - - break; - } - - currChar = charList.shift(); - obj.currChar = currChar; - - print("ÿc4Mule Loggerÿc2: Login character: " + currChar); - FileTools.writeText("logs/MuleLog.json", JSON.stringify(obj)); - - if (MuleLogger.AutoPerm) { - let characterStatus = { - charname: currChar, - perm: ControlAction.getPermStatus({charName: currChar}) - }; - MuleLogger.savePermedStatus(characterStatus); - } - - ControlAction.loginCharacter({charName: currChar}); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.NewCharSelected: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); - - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - if (!Starter.LocationEvents.charSelectError()) { - accounts.shift(); // remove current account from the list - chars.shift(); - } - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameDoesNotExist: - Controls.CreateGameWindow.click(); - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameIsFull: - D2Bot.printToConsole("Game is full"); - Starter.lastGameStatus = "ready"; - delay(500); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.OtherMultiplayer: - // probably should implement way to use open bnet - Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + if (obj.currChar) { + for (i = 0; i < charList.length; i += 1) { + if (charList[i] === obj.currChar) { + // Remove the previous currChar as well + charList.splice(0, i + 1); + + break; + } + } + } + } + + // last char in acc = trigger next acc + if (!charList.length) { + print("No more characters"); + accounts.shift(); // remove current account from the list + chars.shift(); + + break; + } + + currChar = charList.shift(); + obj.currChar = currChar; + + print("ÿc4Mule Loggerÿc2: Login character: " + currChar); + FileTools.writeText("logs/MuleLog.json", JSON.stringify(obj)); + + if (MuleLogger.AutoPerm) { + let characterStatus = { + charname: currChar, + perm: ControlAction.getPermStatus({ charName: currChar }) + }; + MuleLogger.savePermedStatus(characterStatus); + } + + ControlAction.loginCharacter({ charName: currChar }); + + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.NewCharSelected: + Controls.BottomLeftExit.click(); + + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + Starter.LocationEvents.selectDifficultySP(); + + break; + case sdk.game.locations.MainMenuConnecting: + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + if (!Starter.LocationEvents.charSelectError()) { + accounts.shift(); // remove current account from the list + chars.shift(); + } + + break; + case sdk.game.locations.ServerDown: + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GameNameExists: + case sdk.game.locations.GameDoesNotExist: + Controls.CreateGameWindow.click(); + + break; + case sdk.game.locations.GatewaySelect: + Controls.GatewayCancel.click(); + + break; + case sdk.game.locations.GameIsFull: + D2Bot.printToConsole("Game is full"); + Starter.lastGameStatus = "ready"; + delay(500); + Controls.JoinGameWindow.click(); + + break; + case sdk.game.locations.OtherMultiplayer: + // probably should implement way to use open bnet + Controls.OtherMultiplayerCancel.click(); + + break; + case sdk.game.locations.TcpIp: + case sdk.game.locations.TcpIpEnterIp: + Controls.TcpIpCancel.click(); + + break; + default: + if (location !== undefined) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } function main() { - addEventListener("copydata", Starter.receiveCopyData); - - while (!Starter.handle) { - delay(100); - } - - DataFile.updateStats("handle", Starter.handle); - delay(500); - D2Bot.init(); - load("threads/heartbeat.js"); - - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } - - if (Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole("You must disable RD Blocker for Mule Logger to work properly. Stopping."); - D2Bot.stop(); - - return; - } - - parseInfo(); - - if (Starter.gameInfo.error) { - if (!!DataFile.getStats().debugInfo) { - Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); - } - - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - D2Bot.updateRuns(); - } - - DataFile.updateStats("debugInfo", JSON.stringify({currScript: "none", area: "out of game"})); - - 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) { - if (!Starter.inGame) { - print("Updating Status"); - Starter.lastGameStatus = "ingame"; - Starter.inGame = true; - Starter.gameStart = getTickCount(); - DataFile.updateStats("runs", Starter.gameCount); - } - - D2Bot.updateStatus("Game: " + me.gamename + Starter.timer(Starter.gameStart)); - } - - delay(1000); - } - - locationAction(getLocation()); - delay(1000); - } + addEventListener("copydata", Starter.receiveCopyData); + + while (!Starter.handle) { + delay(100); + } + + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); + + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } + + if (Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole("You must disable RD Blocker for Mule Logger to work properly. Stopping."); + D2Bot.stop(); + + return; + } + + parseInfo(); + + if (Starter.gameInfo.error) { + if (!!DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + + D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + } + + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + D2Bot.updateRuns(); + } + + DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); + + 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) { + if (!Starter.inGame) { + print("Updating Status"); + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + Starter.gameStart = getTickCount(); + DataFile.updateStats("runs", Starter.gameCount); + } + + D2Bot.updateStatus("Game: " + me.gamename + Starter.timer(Starter.gameStart)); + } + + delay(1000); + } + + locationAction(getLocation()); + delay(1000); + } } diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index cfab177cf..f6769c62e 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename D2BotPubJoin.dbj * @author kolton, theBGuy @@ -41,57 +42,57 @@ Starter.Config.AttemptNextGameRetrys = 5; */ const IncludeFilter = [ - [""] + [""] ]; const ExcludeFilter = [ - [""] + [""] ]; // ############################################################################### function includeCheck (game) { - // No filters - if (!IncludeFilter.length) return true; - - for (let i = 0; i < IncludeFilter.length; i += 1) { - let j; - for (j = 0; j < IncludeFilter[i].length; j += 1) { - // Break the inner loop if an element didn't match or if an element is invalid - if (!IncludeFilter[i][j] || !game.match(IncludeFilter[i][j], "gi")) { - break; - } - } - - // All elements matched - if (j === IncludeFilter[i].length) { - return true; - } - } - - return false; + // No filters + if (!IncludeFilter.length) return true; + + for (let i = 0; i < IncludeFilter.length; i += 1) { + let j; + for (j = 0; j < IncludeFilter[i].length; j += 1) { + // Break the inner loop if an element didn't match or if an element is invalid + if (!IncludeFilter[i][j] || !game.match(IncludeFilter[i][j], "gi")) { + break; + } + } + + // All elements matched + if (j === IncludeFilter[i].length) { + return true; + } + } + + return false; } function excludeCheck (game) { - // No filters - if (!ExcludeFilter.length) return true; - - for (let i = 0; i < ExcludeFilter.length; i += 1) { - let j; - for (j = 0; j < ExcludeFilter[i].length; j += 1) { - // Break the inner loop if an element didn't match or if an element is invalid - if (!ExcludeFilter[i][j] || !game.match(ExcludeFilter[i][j], "gi")) { - break; - } - } - - // All elements matched - if (j === ExcludeFilter[i].length) { - return false; - } - } - - return true; + // No filters + if (!ExcludeFilter.length) return true; + + for (let i = 0; i < ExcludeFilter.length; i += 1) { + let j; + for (j = 0; j < ExcludeFilter[i].length; j += 1) { + // Break the inner loop if an element didn't match or if an element is invalid + if (!ExcludeFilter[i][j] || !game.match(ExcludeFilter[i][j], "gi")) { + break; + } + } + + // All elements matched + if (j === ExcludeFilter[i].length) { + return false; + } + } + + return true; } // the only things we really need from these are their oog checks @@ -101,343 +102,343 @@ let Controls = require("./modules/Control"); let Overrides = require("./modules/Override"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { - Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); + Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); } else { - // no need to carry around the reference - delete Starter.AdvancedConfig; + // no need to carry around the reference + delete Starter.AdvancedConfig; } new Overrides.Override(Starter, Starter.setNextGame, function (orignal, gameName) { - function incrementString (text) { - return text.replace(/(\d*)$/, (_, t) => (+t + 1).toString().padStart(t.length, 0)); - } + function incrementString (text) { + return text.replace(/(\d*)$/, (_, t) => (+t + 1).toString().padStart(t.length, 0)); + } - let nextGame = (gameName || Starter.randomString(null, true)); - nextGame = incrementString(nextGame); + let nextGame = (gameName || Starter.randomString(null, true)); + nextGame = incrementString(nextGame); - DataFile.updateStats("nextGame", nextGame); + DataFile.updateStats("nextGame", nextGame); }).apply(); if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { - Starter.firstRun = true; + Starter.firstRun = true; } let lastGameTick, retry = 0; let retryTick = 0; function locationAction (location) { - let gameToJoin, doneGames, gameList; - - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); + let gameToJoin, doneGames, gameList; + + switch (location) { + case sdk.game.locations.PreSplash: + ControlAction.click(); + + break; + case sdk.game.locations.Lobby: + D2Bot.updateStatus("Lobby"); - me.blockKeys = false; - Starter.loginRetry = 0; - !Starter.firstLogin && (Starter.firstLogin = true); - Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - Starter.loginFail = 0; - - if (Starter.Config.PingQuitDelay && Starter.pingQuit) { - ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); + me.blockKeys = false; + Starter.loginRetry = 0; + !Starter.firstLogin && (Starter.firstLogin = true); + Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); + Starter.loginFail = 0; + + if (Starter.Config.PingQuitDelay && Starter.pingQuit) { + ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); - Starter.pingQuit = false; - } - - 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } + Starter.pingQuit = false; + } + + 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { + break; + } - print("updating runs"); - D2Bot.updateRuns(); - - lastGameTick = getTickCount(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; + print("updating runs"); + D2Bot.updateRuns(); + + lastGameTick = getTickCount(); + 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.openJoinGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - Controls.CancelCreateGame.click(); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.LobbyChat: - case sdk.game.locations.CreateGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Starter.LocationEvents.openJoinGameWindow(); - - break; - case sdk.game.locations.JoinGame: - // Don't join immediately after previous game to avoid FTJ - if (getTickCount() - lastGameTick < 5000) { - ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); - } - - if (Starter.Config.AttemptNextGame && retry < Starter.Config.AttemptNextGameRetrys) { - let ng = DataFile.getStats().nextGame; - - if (ng && (retry === 0 || (getTickCount() - retryTick > Starter.Config.JoinDelay * 1e3))) { - gameToJoin = ng; - console.debug(gameToJoin); - - me.blockMouse = true; - - try { - joinGame(gameToJoin, ""); - } catch (joinErr) { - print(joinErr); - } - - retry++; - retryTick = getTickCount(); - me.blockMouse = false; - - Starter.locationTimeout(5000, location); - - if (getLocation() === sdk.game.locations.GameDoesNotExist) { - Starter.LocationEvents.openJoinGameWindow(); - } - } - } - - for (let i = 0; i < 5; i += 1) { - gameList = ControlAction.getGameList(); - - if (gameList && gameList.length > 0) { - break; - } - - delay(1000); - } - - console.debug(gameList); - - if (gameList) { - doneGames = []; - gameToJoin = false; - FileTools.exists("logs/doneGames.json") && (doneGames = JSON.parse(FileAction.read("logs/doneGames.json"))); - - gameList.sort(function (a, b) { - return b.players - a.players; - }); - - for (let i = 0; i < gameList.length; i += 1) { - if (doneGames.indexOf(gameList[i].gameName) === -1 && includeCheck(gameList[i].gameName) && excludeCheck(gameList[i].gameName)) { - console.log("ÿc7Game: " + gameList[i].gameName + ", Players: " + gameList[i].players); - gameToJoin = gameList[i].gameName; - - break; - } - } - - if (gameToJoin) { - doneGames.length >= 20 && doneGames.shift(); - doneGames.push(gameToJoin); - FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); - - me.blockMouse = true; - - try { - joinGame(gameToJoin, ""); - } catch (joinErr) { - print(joinErr); - } - - me.blockMouse = false; - - Starter.locationTimeout(5000, location); - } - } - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.SplashScreen: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - Starter.LocationEvents.login(); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameIsFull: - Controls.CreateGameWindow.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - - break; - case sdk.game.locations.CharacterCreate: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); - - break; - case sdk.game.locations.TcpIp: - Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); - - break; - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } + if (Starter.Config.ResetCount && Starter.gameCount >= Starter.Config.ResetCount) { + Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); + } + } + + Starter.LocationEvents.openJoinGameWindow(); + + break; + case sdk.game.locations.WaitingInLine: + Controls.CancelCreateGame.click(); + Controls.JoinGameWindow.click(); + + break; + case sdk.game.locations.LobbyChat: + case sdk.game.locations.CreateGame: + case sdk.game.locations.Ladder: + case sdk.game.locations.ChannelList: + Starter.LocationEvents.openJoinGameWindow(); + + break; + case sdk.game.locations.JoinGame: + // Don't join immediately after previous game to avoid FTJ + if (getTickCount() - lastGameTick < 5000) { + ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); + } + + if (Starter.Config.AttemptNextGame && retry < Starter.Config.AttemptNextGameRetrys) { + let ng = DataFile.getStats().nextGame; + + if (ng && (retry === 0 || (getTickCount() - retryTick > Starter.Config.JoinDelay * 1e3))) { + gameToJoin = ng; + console.debug(gameToJoin); + + me.blockMouse = true; + + try { + joinGame(gameToJoin, ""); + } catch (joinErr) { + print(joinErr); + } + + retry++; + retryTick = getTickCount(); + me.blockMouse = false; + + Starter.locationTimeout(5000, location); + + if (getLocation() === sdk.game.locations.GameDoesNotExist) { + Starter.LocationEvents.openJoinGameWindow(); + } + } + } + + for (let i = 0; i < 5; i += 1) { + gameList = ControlAction.getGameList(); + + if (gameList && gameList.length > 0) { + break; + } + + delay(1000); + } + + console.debug(gameList); + + if (gameList) { + doneGames = []; + gameToJoin = false; + FileTools.exists("logs/doneGames.json") && (doneGames = JSON.parse(FileAction.read("logs/doneGames.json"))); + + gameList.sort(function (a, b) { + return b.players - a.players; + }); + + for (let i = 0; i < gameList.length; i += 1) { + if (doneGames.indexOf(gameList[i].gameName) === -1 && includeCheck(gameList[i].gameName) && excludeCheck(gameList[i].gameName)) { + console.log("ÿc7Game: " + gameList[i].gameName + ", Players: " + gameList[i].players); + gameToJoin = gameList[i].gameName; + + break; + } + } + + if (gameToJoin) { + doneGames.length >= 20 && doneGames.shift(); + doneGames.push(gameToJoin); + FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); + + me.blockMouse = true; + + try { + joinGame(gameToJoin, ""); + } catch (joinErr) { + print(joinErr); + } + + me.blockMouse = false; + + Starter.locationTimeout(5000, location); + } + } + + break; + case sdk.game.locations.MainMenu: + case sdk.game.locations.SplashScreen: + case sdk.game.locations.Login: + case sdk.game.locations.CharSelect: + Starter.LocationEvents.login(); + + break; + case sdk.game.locations.LoginError: + case sdk.game.locations.InvalidCdKey: + case sdk.game.locations.CdKeyInUse: + Starter.LocationEvents.loginError(); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.RealmDown: + Starter.LocationEvents.realmDown(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.LobbyLostConnection: + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + + break; + case sdk.game.locations.CharSelectPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.SelectDifficultySP: + break; + case sdk.game.locations.MainMenuConnecting: + !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); + + break; + case sdk.game.locations.CharSelectConnecting: + case sdk.game.locations.CharSelectNoChars: + Starter.LocationEvents.charSelectError(); + + break; + case sdk.game.locations.ServerDown: + break; + case sdk.game.locations.LobbyPleaseWait: + !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); + + break; + case sdk.game.locations.GameNameExists: + case sdk.game.locations.GameIsFull: + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + + break; + case sdk.game.locations.GatewaySelect: + Controls.GatewayCancel.click(); + + break; + case sdk.game.locations.GameDoesNotExist: + Starter.LocationEvents.gameDoesNotExist(); + + break; + case sdk.game.locations.CharacterCreate: + Controls.BottomLeftExit.click(); + + break; + case sdk.game.locations.OtherMultiplayer: + Starter.LocationEvents.otherMultiplayerSelect(); + + break; + case sdk.game.locations.TcpIp: + Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); + + break; + case sdk.game.locations.TcpIpEnterIp: + Controls.TcpIpCancel.click(); + + break; + default: + if (location !== undefined) { + D2Bot.printToConsole("Unhandled location " + location); + delay(500); + D2Bot.restart(); + } + + break; + } } function main() { - debugLog(me.profile); - addEventListener("copydata", Starter.receiveCopyData); - addEventListener("scriptmsg", Starter.scriptMsgEvent); + debugLog(me.profile); + addEventListener("copydata", Starter.receiveCopyData); + addEventListener("scriptmsg", Starter.scriptMsgEvent); - while (!Starter.handle) { - delay(100); - } + while (!Starter.handle) { + delay(100); + } - DataFile.updateStats("handle", Starter.handle); - delay(500); - D2Bot.init(); - load("threads/heartbeat.js"); + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); - while (!Object.keys(Starter.gameInfo).length) { - D2Bot.requestGameInfo(); - delay(500); - } + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } - Starter.gameCount = (DataFile.getStats().runs + 1 || 1); - DataFile.updateStats("nextGame", ""); + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + DataFile.updateStats("nextGame", ""); - if (Starter.gameInfo.error) { - D2Bot.retrieve(); - delay(200); + if (Starter.gameInfo.error) { + D2Bot.retrieve(); + delay(200); - if (Starter.gameInfo.crashInfo) { - D2Bot.printToConsole("Crash Info: Script: " + Starter.gameInfo.crashInfo.currScript + " Area: " + Starter.gameInfo.crashInfo.area, sdk.colors.D2Bot.Gray); - } + if (Starter.gameInfo.crashInfo) { + D2Bot.printToConsole("Crash Info: Script: " + Starter.gameInfo.crashInfo.currScript + " Area: " + Starter.gameInfo.crashInfo.area, sdk.colors.D2Bot.Gray); + } - ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); - D2Bot.updateRuns(); - } + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + D2Bot.updateRuns(); + } - D2Bot.store(JSON.stringify({currScript: "none", area: "out of game"})); + D2Bot.store(JSON.stringify({ currScript: "none", area: "out of game" })); - while (!Object.keys(Starter.profileInfo).length) { - D2Bot.getProfile(); - print("Getting Profile"); - delay(500); - } + while (!Object.keys(Starter.profileInfo).length) { + D2Bot.getProfile(); + print("Getting Profile"); + 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"; + 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(); + if (!Starter.inGame) { + Starter.gameStart = getTickCount(); - print("Updating Status"); + print("Updating Status"); - Starter.lastGameStatus = "ingame"; - Starter.inGame = true; - retry = 0; - retryTick = 0; + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + retry = 0; + retryTick = 0; - DataFile.updateStats("runs", Starter.gameCount); - DataFile.updateStats("ingameTick"); - Starter.setNextGame(me.gamename); - } + DataFile.updateStats("runs", Starter.gameCount); + DataFile.updateStats("ingameTick"); + Starter.setNextGame(me.gamename); + } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); - } + D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + } - delay(1000); - } + delay(1000); + } - Starter.isUp = "no"; + Starter.isUp = "no"; - locationAction(getLocation()); - delay(1000); - } + locationAction(getLocation()); + delay(1000); + } } diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index f28e9daf9..128b08a01 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename default.dbj * @author kolton, theBGuy @@ -19,245 +20,245 @@ 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; - } + 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; + // 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"); + // 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 + " | 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.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"); - } + 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 + " | 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.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 + 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 + (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; + 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 + (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; } diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 633317dae..a73188063 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename OOG.js * @author kolton, D3STROY3R, theBGuy @@ -15,51 +16,51 @@ includeIfNotIncluded("oog/D2Bot.js"); // required */ (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(["require", "./modules/Control"], factory); - } else { - Object.assign(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(["require", "./modules/Control"], factory); + } else { + Object.assign(root, factory()); + } }([].filter.constructor("return this")(), function() { - const Controls = require("./modules/Control"); + const Controls = require("./modules/Control"); - const ControlAction = { - mutedKey: false, - realms: { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }, + const ControlAction = { + mutedKey: false, + realms: { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }, - /** + /** * @param {string} text * @param {number} time - in milliseconds * @param {Function} [stopfunc] * @param {*} [arg] */ - timeoutDelay: function (text, time, stopfunc, arg) { - let currTime = 0; - let endTime = getTickCount() + time; - Starter.delay = time; + timeoutDelay: function (text, time, stopfunc, arg) { + let currTime = 0; + let endTime = getTickCount() + time; + Starter.delay = time; - while (getTickCount() < endTime) { - if (typeof stopfunc === "function" && stopfunc(arg)) { - break; - } + while (getTickCount() < endTime) { + if (typeof stopfunc === "function" && stopfunc(arg)) { + break; + } - Starter.delay = Math.max(0, (endTime - getTickCount())); - if (currTime !== Math.floor((endTime - getTickCount()) / 1000)) { - currTime = Math.floor((endTime - getTickCount()) / 1000); + Starter.delay = Math.max(0, (endTime - getTickCount())); + if (currTime !== Math.floor((endTime - getTickCount()) / 1000)) { + currTime = Math.floor((endTime - getTickCount()) / 1000); - D2Bot.updateStatus(text + " (" + Math.max(currTime, 0) + "s)"); - } + D2Bot.updateStatus(text + " (" + Math.max(currTime, 0) + "s)"); + } - delay(10); - } + delay(10); + } - Starter.delay = 0; - }, + Starter.delay = 0; + }, - /** + /** * @param {number} type * @param {number} x * @param {number} y @@ -69,21 +70,21 @@ includeIfNotIncluded("oog/D2Bot.js"); // required * @param {number} targety * @returns {boolean} */ - click: function (type, x, y, xsize, ysize, targetx, targety) { - let control = getControl(type, x, y, xsize, ysize); + click: function (type, x, y, xsize, ysize, targetx, targety) { + let control = getControl(type, x, y, xsize, ysize); - if (!control) { - print("control not found " + type + " " + x + " " + y + " " + xsize + " " + ysize + " location " + getLocation()); + if (!control) { + print("control not found " + type + " " + x + " " + y + " " + xsize + " " + ysize + " location " + getLocation()); - return false; - } + return false; + } - control.click(targetx, targety); + control.click(targetx, targety); - return true; - }, + return true; + }, - /** + /** * @param {number} type * @param {number} x * @param {number} y @@ -92,27 +93,27 @@ includeIfNotIncluded("oog/D2Bot.js"); // required * @param {string} text * @returns {boolean} */ - setText: function (type, x, y, xsize, ysize, text) { - if (!text) return false; + setText: function (type, x, y, xsize, ysize, text) { + if (!text) return false; - let control = getControl(type, x, y, xsize, ysize); - if (!control) return false; + let control = getControl(type, x, y, xsize, ysize); + if (!control) return false; - let currText = control.text; - if (currText && currText === text) return true; + let currText = control.text; + if (currText && currText === text) return true; - currText = control.getText(); + currText = control.getText(); - if (currText && ((typeof currText === "string" && currText === text) || (typeof currText === "object" && currText.includes(text)))) { - return true; - } + if (currText && ((typeof currText === "string" && currText === text) || (typeof currText === "object" && currText.includes(text)))) { + return true; + } - control.setText(text); + control.setText(text); - return true; - }, + return true; + }, - /** + /** * @param {number} type * @param {number} x * @param {number} y @@ -120,80 +121,80 @@ includeIfNotIncluded("oog/D2Bot.js"); // required * @param {number} ysize * @returns {string[] | false} */ - getText: function (type, x, y, xsize, ysize) { - let control = getControl(type, x, y, xsize, ysize); - - return (!!control ? control.getText() : false); - }, - - // ~~~ Start of general functions ~~~ // - scrollDown: function () { - me.blockMouse = true; - for (let i = 0; i < 4; i++) { - sendKey(sdk.keys.code.DownArrow); - } - me.blockMouse = false; - }, - - clickRealm: function (realm) { - if (realm === undefined || typeof realm !== "number" || realm < 0 || realm > 3) { - throw new Error("clickRealm: Invalid realm!"); - } - - let retry = 0; - - me.blockMouse = true; - - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - let control = Controls.Gateway.control; - if (!control) { - if (retry > 3) return false; - retry++; - - break; - } - - let gateText = getLocaleString(sdk.locale.text.Gateway); - let currentRealm = (() => { - switch (control.text.split(gateText.substring(0, gateText.length - 2))[1]) { - case "U.S. WEST": - return 0; - case "ASIA": - return 2; - case "EUROPE": - return 3; - case "U.S. EAST": - default: - return 1; - } - })(); - - if (currentRealm === realm) { - break MainLoop; - } - - Controls.Gateway.click(); - - break; - case sdk.game.locations.GatewaySelect: - this.click(4, 257, 500, 292, 160, 403, 350 + realm * 25); - Controls.GatewayOk.click(); - - break; - } - - delay(500); - } - - me.blockMouse = false; - - return true; - }, - - /** + getText: function (type, x, y, xsize, ysize) { + let control = getControl(type, x, y, xsize, ysize); + + return (!!control ? control.getText() : false); + }, + + // ~~~ Start of general functions ~~~ // + scrollDown: function () { + me.blockMouse = true; + for (let i = 0; i < 4; i++) { + sendKey(sdk.keys.code.DownArrow); + } + me.blockMouse = false; + }, + + clickRealm: function (realm) { + if (realm === undefined || typeof realm !== "number" || realm < 0 || realm > 3) { + throw new Error("clickRealm: Invalid realm!"); + } + + let retry = 0; + + me.blockMouse = true; + + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + let control = Controls.Gateway.control; + if (!control) { + if (retry > 3) return false; + retry++; + + break; + } + + let gateText = getLocaleString(sdk.locale.text.Gateway); + let currentRealm = (() => { + switch (control.text.split(gateText.substring(0, gateText.length - 2))[1]) { + case "U.S. WEST": + return 0; + case "ASIA": + return 2; + case "EUROPE": + return 3; + case "U.S. EAST": + default: + return 1; + } + })(); + + if (currentRealm === realm) { + break MainLoop; + } + + Controls.Gateway.click(); + + break; + case sdk.game.locations.GatewaySelect: + this.click(4, 257, 500, 292, 160, 403, 350 + realm * 25); + Controls.GatewayOk.click(); + + break; + } + + delay(500); + } + + me.blockMouse = false; + + return true; + }, + + /** * @typedef {Object} CharacterInfo * @property {string} charName * @property {string} charClass @@ -203,1387 +204,1387 @@ includeIfNotIncluded("oog/D2Bot.js"); // required * @property {boolean} ladder */ - /** + /** * @param {CharacterInfo} info * @param {boolean} [startFromTop] * @returns {Control | false} */ - findCharacter: function (info, startFromTop = true) { - let count = 0; - let tick = getTickCount(); - - while (getLocation() !== sdk.game.locations.CharSelect) { - if (getTickCount() - tick >= 5000) { - break; - } - - delay(25); - } - - // start from beginning of the char list - startFromTop && sendKey(sdk.keys.code.Home); - - while (getLocation() === sdk.game.locations.CharSelect && count < 24) { - let control = Controls.CharSelectCharInfo0.control; - - if (control) { - 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 < 24 && 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; - } - } - - return false; - }, - - getCharacters: function () { - let count = 0; - let list = []; - - // start from beginning of the char list - sendKey(sdk.keys.code.Home); - - while (getLocation() === sdk.game.locations.CharSelect && count < 24) { - let control = Controls.CharSelectCharInfo0.control; - - if (control) { - do { - let text = control.getText(); - - if (text instanceof Array && typeof text[1] === "string") { - count++; - - if (list.indexOf(text[1]) === -1) { - list.push(text[1]); - } - } - } while (count < 24 && 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; - } - } - - // back to beginning of the char list - sendKey(sdk.keys.code.Home); - - return list; - }, - - /** + findCharacter: function (info, startFromTop = true) { + let count = 0; + let tick = getTickCount(); + + while (getLocation() !== sdk.game.locations.CharSelect) { + if (getTickCount() - tick >= 5000) { + break; + } + + delay(25); + } + + // start from beginning of the char list + startFromTop && sendKey(sdk.keys.code.Home); + + while (getLocation() === sdk.game.locations.CharSelect && count < 24) { + let control = Controls.CharSelectCharInfo0.control; + + if (control) { + 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 < 24 && 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; + } + } + + return false; + }, + + getCharacters: function () { + let count = 0; + let list = []; + + // start from beginning of the char list + sendKey(sdk.keys.code.Home); + + while (getLocation() === sdk.game.locations.CharSelect && count < 24) { + let control = Controls.CharSelectCharInfo0.control; + + if (control) { + do { + let text = control.getText(); + + if (text instanceof Array && typeof text[1] === "string") { + count++; + + if (list.indexOf(text[1]) === -1) { + list.push(text[1]); + } + } + } while (count < 24 && 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; + } + } + + // back to beginning of the char list + sendKey(sdk.keys.code.Home); + + return list; + }, + + /** * @param {CharacterInfo} info * @returns {boolean} */ - getPermStatus: function (info) { - let expireStr = getLocaleString(sdk.locale.text.ExpiresIn); - expireStr = expireStr.slice(0, expireStr.indexOf("%")).trim(); + getPermStatus: function (info) { + let expireStr = getLocaleString(sdk.locale.text.ExpiresIn); + expireStr = expireStr.slice(0, expireStr.indexOf("%")).trim(); - let control = this.findCharacter(info); - if (!control) return false; + let control = this.findCharacter(info); + if (!control) return false; - let text = control.getText(); - if (!Array.isArray(text) || typeof text[1] !== "string") return false; + let text = control.getText(); + if (!Array.isArray(text) || typeof text[1] !== "string") return false; - return !text.some(el => el.includes(expireStr)); - }, + return !text.some(el => el.includes(expireStr)); + }, - /** + /** * get character position - useless? this doesn't take any arguments to even check the character * @returns {number} */ - getPosition: function () { - let position = 0; + getPosition: function () { + let position = 0; - if (getLocation() === sdk.game.locations.CharSelect) { - let control = Controls.CharSelectCharInfo0.control; + if (getLocation() === 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") { - position += 1; - } - } while (control.getNext()); - } - } + if (text instanceof Array && typeof text[1] === "string") { + position += 1; + } + } while (control.getNext()); + } + } - return position; - }, + return position; + }, - /** + /** * @param {CharacterInfo} info * @returns {boolean} */ - makeCharacter: function (info) { - me.blockMouse = true; - !info.charClass && (info.charClass = "barbarian"); - (!info.charName || info.charName.length < 2 || info.charName.length > 15) && (info.charName = Starter.randomString(8, false)); - info.charName.match(/\d+/g) && (info.charName.replace(/\d+/g, "")); - !info.expansion && ["druid", "assassin"].includes(info.charClass) && (info.expansion = true); - - let clickCoords = []; - /** @type {Map 15) && (info.charName = Starter.randomString(8, false)); + info.charName.match(/\d+/g) && (info.charName.replace(/\d+/g, "")); + !info.expansion && ["druid", "assassin"].includes(info.charClass) && (info.expansion = true); + + let clickCoords = []; + /** @type {Map el.toLowerCase().includes("expansion"))) { - console.warn(info.charName + " already expansion"); - console.debug(control, "\n", control.getText()); + if (control.getText().find(el => el.toLowerCase().includes("expansion"))) { + console.warn(info.charName + " already expansion"); + console.debug(control, "\n", control.getText()); - return false; - } - - try { - me.blockMouse = true; - console.log("converting character to expansion " + info.charName); - control.click(); - Controls.CharSelectConvert.click(); - delay(500); - Controls.PopupYes.click(); - delay(500); - - return true; - } catch (e) { - console.error(e); - - return false; - } finally { - me.blockMouse = false; - } - }, - - /** + return false; + } + + try { + me.blockMouse = true; + console.log("converting character to expansion " + info.charName); + control.click(); + Controls.CharSelectConvert.click(); + delay(500); + Controls.PopupYes.click(); + delay(500); + + return true; + } catch (e) { + console.error(e); + + return false; + } finally { + me.blockMouse = false; + } + }, + + /** * @param {CharacterInfo} info * @param {boolean} startFromTop * @returns {boolean} */ - loginCharacter: function (info, startFromTop = true) { - me.blockMouse = true; + loginCharacter: function (info, startFromTop = true) { + me.blockMouse = true; - try { - MainLoop: - // cycle until in lobby or in game - while (getLocation() !== sdk.game.locations.Lobby) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - let control = this.findCharacter(info, startFromTop); - if (!control) return false; + try { + MainLoop: + // cycle until in lobby or in game + while (getLocation() !== sdk.game.locations.Lobby) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + let control = this.findCharacter(info, startFromTop); + if (!control) return false; - control.click(); - Controls.BottomRightOk.click(); - Starter.locationTimeout(sdk.game.locations.CharSelect, 5000); - - return getLocation() === sdk.game.locations.SelectDifficultySP - ? login(info.profile) - : true; - case sdk.game.locations.CharSelectNoChars: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.OkCenteredErrorPopUp: - break MainLoop; - default: - break; - } - - delay(100); - } - - return true; - } catch (e) { - console.error(e); - - return false; - } finally { - me.blockMouse = false; - } - }, - - setEmail: function (email = "", domain = "@email.com") { - if (getLocation() !== sdk.game.locations.RegisterEmail) return false; - if (!email || !email.length) { - email = Starter.randomString(null, true); - } + control.click(); + Controls.BottomRightOk.click(); + Starter.locationTimeout(sdk.game.locations.CharSelect, 5000); + + return getLocation() === sdk.game.locations.SelectDifficultySP + ? login(info.profile) + : true; + case sdk.game.locations.CharSelectNoChars: + Controls.BottomLeftExit.click(); + + break; + case sdk.game.locations.Disconnected: + case sdk.game.locations.OkCenteredErrorPopUp: + break MainLoop; + default: + break; + } + + delay(100); + } + + return true; + } catch (e) { + console.error(e); + + return false; + } finally { + me.blockMouse = false; + } + }, + + setEmail: function (email = "", domain = "@email.com") { + if (getLocation() !== sdk.game.locations.RegisterEmail) return false; + if (!email || !email.length) { + email = Starter.randomString(null, true); + } - while (getLocation() !== sdk.game.locations.CharSelect) { - switch (getLocation()) { - case sdk.game.locations.RegisterEmail: - if (Controls.EmailSetEmail.setText(email + domain) && Controls.EmailVerifyEmail.setText(email + domain)) { - Controls.EmailRegister.click(); - delay(100); - } - - break; - case sdk.game.locations.LoginError: - // todo test what conditions get here other than email not matching - D2Bot.printToConsole("Failed to set email"); - Controls.LoginErrorOk.click(); + while (getLocation() !== sdk.game.locations.CharSelect) { + switch (getLocation()) { + case sdk.game.locations.RegisterEmail: + if (Controls.EmailSetEmail.setText(email + domain) && Controls.EmailVerifyEmail.setText(email + domain)) { + Controls.EmailRegister.click(); + delay(100); + } + + break; + case sdk.game.locations.LoginError: + // todo test what conditions get here other than email not matching + D2Bot.printToConsole("Failed to set email"); + Controls.LoginErrorOk.click(); - return false; - case sdk.game.locations.CharSelectNoChars: - // fresh acc - return true; - } - } + return false; + case sdk.game.locations.CharSelectNoChars: + // fresh acc + return true; + } + } - return true; - }, + return true; + }, - makeAccount: function (info) { - me.blockMouse = true; + makeAccount: function (info) { + me.blockMouse = true; - let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; + let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; - // cycle until in empty char screen - MainLoop: - while (getLocation() !== sdk.game.locations.CharSelectNoChars) { - switch (getLocation()) { - case sdk.game.locations.MainMenu: - ControlAction.clickRealm(this.realms[info.realm]); - if (openBnet) { - Controls.OtherMultiplayer.click() && Controls.OpenBattleNet.click(); - } else { - Controls.BattleNet.click(); - } - - break; - case sdk.game.locations.Login: - Controls.CreateNewAccount.click(); - - break; - case sdk.game.locations.SplashScreen: - Controls.SplashScreen.click(); - - break; - case sdk.game.locations.CharacterCreate: - Controls.BottomLeftExit.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.PleaseRead: - Controls.PleaseReadOk.click(); - - break; - case sdk.game.locations.RegisterEmail: - Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); - - break; - case sdk.game.locations.CharSelect: - if (openBnet) { - break MainLoop; - } - - break; - default: - break; - } - - delay(100); - } - - me.blockMouse = false; - - return true; - }, - - loginAccount: function (info) { - me.blockMouse = true; - - let locTick; - let tick = getTickCount(); - - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.PreSplash: - break; - case sdk.game.locations.MainMenu: - info.realm && ControlAction.clickRealm(this.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 - default: - print(getLocation()); + // cycle until in empty char screen + MainLoop: + while (getLocation() !== sdk.game.locations.CharSelectNoChars) { + switch (getLocation()) { + case sdk.game.locations.MainMenu: + ControlAction.clickRealm(this.realms[info.realm]); + if (openBnet) { + Controls.OtherMultiplayer.click() && Controls.OpenBattleNet.click(); + } else { + Controls.BattleNet.click(); + } + + break; + case sdk.game.locations.Login: + Controls.CreateNewAccount.click(); + + break; + case sdk.game.locations.SplashScreen: + Controls.SplashScreen.click(); + + break; + case sdk.game.locations.CharacterCreate: + Controls.BottomLeftExit.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.PleaseRead: + Controls.PleaseReadOk.click(); + + break; + case sdk.game.locations.RegisterEmail: + Controls.EmailDontRegisterContinue.control ? Controls.EmailDontRegisterContinue.click() : Controls.EmailDontRegister.click(); + + break; + case sdk.game.locations.CharSelect: + if (openBnet) { + break MainLoop; + } + + break; + default: + break; + } + + delay(100); + } + + me.blockMouse = false; + + return true; + }, + + loginAccount: function (info) { + me.blockMouse = true; + + let locTick; + let tick = getTickCount(); + + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.PreSplash: + break; + case sdk.game.locations.MainMenu: + info.realm && ControlAction.clickRealm(this.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 + default: + print(getLocation()); - me.blockMouse = false; + me.blockMouse = false; - return false; - } - - if (getTickCount() - tick >= 20000) { - return false; - } + 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; - }, + return getLocation() === sdk.game.locations.CharSelect || getLocation() === sdk.game.locations.CharSelectNoChars; + }, - joinChannel: function (channel) { - me.blockMouse = true; + joinChannel: function (channel) { + me.blockMouse = true; - let tick; - let rval = false; - let timeout = 5000; + let tick; + let rval = false; + let timeout = 5000; - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.Lobby: - Controls.LobbyEnterChat.click(); + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.Lobby: + Controls.LobbyEnterChat.click(); - break; - case sdk.game.locations.LobbyChat: - let currChan = Controls.LobbyChannelName.getText(); // returns array + break; + case sdk.game.locations.LobbyChat: + let currChan = Controls.LobbyChannelName.getText(); // returns array - if (currChan) { - for (let i = 0; i < currChan.length; i += 1) { - if (currChan[i].split(" (") && String.isEqual(currChan[i].split(" (")[0], channel)) { - rval = true; + if (currChan) { + for (let i = 0; i < currChan.length; i += 1) { + if (currChan[i].split(" (") && String.isEqual(currChan[i].split(" (")[0], channel)) { + rval = true; - break MainLoop; - } - } - } + break MainLoop; + } + } + } - !tick && Controls.LobbyChannel.click() && (tick = getTickCount()); + !tick && Controls.LobbyChannel.click() && (tick = getTickCount()); - break; - case sdk.game.locations.ChannelList: // Channel - Controls.LobbyChannelText.setText(channel); - Controls.LobbyChannelOk.click(); + break; + case sdk.game.locations.ChannelList: // Channel + Controls.LobbyChannelText.setText(channel); + Controls.LobbyChannelOk.click(); - break; - } + break; + } - if (getTickCount() - tick >= timeout) { - break; - } + if (getTickCount() - tick >= timeout) { + break; + } - delay(100); - } + delay(100); + } - me.blockMouse = false; + me.blockMouse = false; - return rval; - }, + return rval; + }, - createGame: function (name, pass, diff, delay) { - Controls.CreateGameName.setText(name); - Controls.CreateGamePass.setText(pass); - Controls.CreateGameDescription.setText(Starter.Config.GameDescription); + createGame: function (name, pass, diff, delay) { + Controls.CreateGameName.setText(name); + Controls.CreateGamePass.setText(pass); + Controls.CreateGameDescription.setText(Starter.Config.GameDescription); - switch (diff) { - case "Normal": - Controls.Normal.click(); + switch (diff) { + case "Normal": + Controls.Normal.click(); - break; - case "Nightmare": - Controls.Nightmare.click(); + break; + case "Nightmare": + Controls.Nightmare.click(); - break; - case "Highest": - if (Controls.Hell.disabled !== 4 && Controls.Hell.click()) { - break; - } + break; + case "Highest": + if (Controls.Hell.disabled !== 4 && Controls.Hell.click()) { + break; + } - if (Controls.Nightmare.disabled !== 4 && Controls.Nightmare.click()) { - break; - } + if (Controls.Nightmare.disabled !== 4 && Controls.Nightmare.click()) { + break; + } - Controls.Normal.click(); + Controls.Normal.click(); - break; - default: - Controls.Hell.click(); + break; + default: + Controls.Hell.click(); - break; - } + break; + } - !!delay && this.timeoutDelay("Make Game Delay", delay); + !!delay && this.timeoutDelay("Make Game Delay", delay); - if (Starter.chanInfo.announce) { - Starter.sayMsg("Next game is " + name + (pass === "" ? "" : "//" + pass)); - } + if (Starter.chanInfo.announce) { + Starter.sayMsg("Next game is " + name + (pass === "" ? "" : "//" + pass)); + } - me.blockMouse = true; + me.blockMouse = true; - print("Creating Game: " + name); - Controls.CreateGame.click(); + print("Creating Game: " + name); + Controls.CreateGame.click(); - me.blockMouse = false; - }, + me.blockMouse = false; + }, - getGameList: function () { - let text = Controls.JoinGameList.getText(); - - if (text) { - let gameList = []; - - for (let i = 0; i < text.length; i += 1) { - gameList.push({ - gameName: text[i][0], - players: text[i][1] - }); - } - - return gameList; - } - - return false; - }, - - getQueueTime: function() { - // You are in line to create a game.,Try joining a game to avoid waiting.,,Your position in line is: ÿc02912 - const text = Controls.CreateGameInLine.getText(); - if (text && text.indexOf(getLocaleString(sdk.locale.text.YourPositionInLineIs)) > -1) { - const result = /ÿc0(\d*)/gm.exec(text); - if (result && typeof result[1] === "string") { - return parseInt(result[1]) || 0; - } - } - - return 0; // You're in line 0, aka no queue - }, - - loginOtherMultiplayer: function () { - MainLoop: - while (true) { - switch (getLocation()) { - case sdk.game.locations.CharSelect: - if (Controls.CharSelectCurrentRealm.control) { - console.log("Not in single player character select screen"); - Controls.BottomLeftExit.click(); - - break; - } - - Starter.LocationEvents.login(false); - - break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); + getGameList: function () { + let text = Controls.JoinGameList.getText(); + + if (text) { + let gameList = []; + + for (let i = 0; i < text.length; i += 1) { + gameList.push({ + gameName: text[i][0], + players: text[i][1] + }); + } + + return gameList; + } + + return false; + }, + + getQueueTime: function() { + // You are in line to create a game.,Try joining a game to avoid waiting.,,Your position in line is: ÿc02912 + const text = Controls.CreateGameInLine.getText(); + if (text && text.indexOf(getLocaleString(sdk.locale.text.YourPositionInLineIs)) > -1) { + const result = /ÿc0(\d*)/gm.exec(text); + if (result && typeof result[1] === "string") { + return parseInt(result[1]) || 0; + } + } + + return 0; // You're in line 0, aka no queue + }, + + loginOtherMultiplayer: function () { + MainLoop: + while (true) { + switch (getLocation()) { + case sdk.game.locations.CharSelect: + if (Controls.CharSelectCurrentRealm.control) { + console.log("Not in single player character select screen"); + Controls.BottomLeftExit.click(); + + break; + } + + Starter.LocationEvents.login(false); + + break; + case sdk.game.locations.SelectDifficultySP: + Starter.LocationEvents.selectDifficultySP(); - break; - case sdk.game.locations.SplashScreen: - ControlAction.click(); - - break; - case sdk.game.locations.MainMenu: - if (Profile().type === sdk.game.profiletype.OpenBattlenet) { - // check we are on the correct gateway - let realms = { "west": 0, "east": 1, "asia": 2, "europe": 3 }; - ControlAction.clickRealm(realms[Profile().gateway.toLowerCase()]); - try { - login(me.profile); - } catch (e) { - print(e); - } - - break; - } + break; + case sdk.game.locations.SplashScreen: + ControlAction.click(); + + break; + case sdk.game.locations.MainMenu: + if (Profile().type === sdk.game.profiletype.OpenBattlenet) { + // check we are on the correct gateway + let realms = { "west": 0, "east": 1, "asia": 2, "europe": 3 }; + ControlAction.clickRealm(realms[Profile().gateway.toLowerCase()]); + try { + login(me.profile); + } catch (e) { + print(e); + } + + break; + } - Controls.OtherMultiplayer.click(); - - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); - - break; - case sdk.game.locations.TcpIp: - // handle this in otherMultiplayerSelect - // not sure how to handle enter ip though, should that be left to the starter to decide? - Controls.TcpIpCancel.click(); - - break; - case sdk.game.locations.TcpIpEnterIp: - break MainLoop; - case sdk.game.locations.Login: - login(me.profile); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); - - if (me.charname !== Starter.profileInfo.charName) { - Controls.LobbyQuit.click(); + Controls.OtherMultiplayer.click(); + + break; + case sdk.game.locations.OtherMultiplayer: + Starter.LocationEvents.otherMultiplayerSelect(); + + break; + case sdk.game.locations.TcpIp: + // handle this in otherMultiplayerSelect + // not sure how to handle enter ip though, should that be left to the starter to decide? + Controls.TcpIpCancel.click(); + + break; + case sdk.game.locations.TcpIpEnterIp: + break MainLoop; + case sdk.game.locations.Login: + login(me.profile); + + break; + case sdk.game.locations.LoginUnableToConnect: + case sdk.game.locations.TcpIpUnableToConnect: + Starter.LocationEvents.unableToConnect(); + + break; + case sdk.game.locations.Lobby: + case sdk.game.locations.LobbyChat: + D2Bot.updateStatus("Lobby"); + + if (me.charname !== Starter.profileInfo.charName) { + Controls.LobbyQuit.click(); - break; - } + break; + } - me.blockKeys = false; - !Starter.firstLogin && (Starter.firstLogin = true); + me.blockKeys = false; + !Starter.firstLogin && (Starter.firstLogin = true); - break MainLoop; - default: - if (me.ingame) { - break MainLoop; - } + break MainLoop; + default: + if (me.ingame) { + break MainLoop; + } - break; - } - } + break; + } + } - // handling Enter Ip inside entry for now so that location === sucess - return (me.ingame || getLocation() === [sdk.game.locations.TcpIpEnterIp]); - } - }; - - const Starter = { - Config: require("./starter/StarterConfig"), - AdvancedConfig: require("./starter/AdvancedConfig"), - useChat: false, - pingQuit: false, - inGame: false, - firstLogin: true, - firstRun: false, - isUp: "no", - delay: 0, - loginRetry: 0, - deadCheck: false, - chatActionsDone: false, - gameStart: 0, - gameCount: 0, - lastGameStatus: "ready", - handle: null, - connectFail: false, - connectFailRetry: 0, - makeAccount: false, - channelNotify: false, - chanInfo: { - joinChannel: "", - firstMsg: "", - afterMsg: "", - announce: false - }, - gameInfo: {}, - joinInfo: {}, - profileInfo: {}, - - sayMsg: function (string) { - if (!this.useChat) return; - say(string); - }, - - timer: function (tick) { - return " (" + new Date(getTickCount() - tick).toISOString().slice(11, -5) + ")"; - }, - - locationTimeout: function (time, location) { - let endtime = getTickCount() + time; - - while (!me.ingame && getLocation() === location && endtime > getTickCount()) { - delay(500); - } - - return (getLocation() !== location); - }, - - setNextGame: function (gameInfo = {}) { - let nextGame = (gameInfo.gameName || this.randomString(null, true)); + // handling Enter Ip inside entry for now so that location === sucess + return (me.ingame || getLocation() === [sdk.game.locations.TcpIpEnterIp]); + } + }; + + const Starter = { + Config: require("./starter/StarterConfig"), + AdvancedConfig: require("./starter/AdvancedConfig"), + useChat: false, + pingQuit: false, + inGame: false, + firstLogin: true, + firstRun: false, + isUp: "no", + delay: 0, + loginRetry: 0, + deadCheck: false, + chatActionsDone: false, + gameStart: 0, + gameCount: 0, + lastGameStatus: "ready", + handle: null, + connectFail: false, + connectFailRetry: 0, + makeAccount: false, + channelNotify: false, + chanInfo: { + joinChannel: "", + firstMsg: "", + afterMsg: "", + announce: false + }, + gameInfo: {}, + joinInfo: {}, + profileInfo: {}, + + sayMsg: function (string) { + if (!this.useChat) return; + say(string); + }, + + timer: function (tick) { + return " (" + new Date(getTickCount() - tick).toISOString().slice(11, -5) + ")"; + }, + + locationTimeout: function (time, location) { + let endtime = getTickCount() + time; + + while (!me.ingame && getLocation() === location && endtime > getTickCount()) { + delay(500); + } + + return (getLocation() !== location); + }, + + setNextGame: function (gameInfo = {}) { + let nextGame = (gameInfo.gameName || this.randomString(null, true)); - if ((this.gameCount + 1 >= Starter.Config.ResetCount) || (nextGame.length + this.gameCount + 1 > 15)) { - nextGame += "1"; - } else { - nextGame += (this.gameCount + 1); - } - - DataFile.updateStats("nextGame", nextGame); - }, - - updateCount: function () { - D2Bot.updateCount(); - delay(1000); - Controls.BattleNet.click(); - - try { - login(me.profile); - } catch (e) { - return; - } - - delay(1000); - Controls.BottomLeftExit.click(); - }, - - scriptMsgEvent: function (msg) { - if (msg && typeof msg !== "string") return; - switch (msg) { - case "mule": - AutoMule.check = true; - - break; - case "muleTorch": - AutoMule.torchAnniCheck = 1; - - break; - case "muleAnni": - AutoMule.torchAnniCheck = 2; - - break; - case "torch": - TorchSystem.check = true; - - break; - case "crafting": - CraftingSystem.check = true; - - break; - case "getMuleMode": - if (AutoMule.torchAnniCheck === 2) { - scriptBroadcast("2"); - } else if (AutoMule.torchAnniCheck === 1) { - scriptBroadcast("1"); - } else if (AutoMule.check) { - scriptBroadcast("0"); - } - - break; - case "pingquit": - Starter.pingQuit = true; - - break; - } - }, - - /** + if ((this.gameCount + 1 >= Starter.Config.ResetCount) || (nextGame.length + this.gameCount + 1 > 15)) { + nextGame += "1"; + } else { + nextGame += (this.gameCount + 1); + } + + DataFile.updateStats("nextGame", nextGame); + }, + + updateCount: function () { + D2Bot.updateCount(); + delay(1000); + Controls.BattleNet.click(); + + try { + login(me.profile); + } catch (e) { + return; + } + + delay(1000); + Controls.BottomLeftExit.click(); + }, + + scriptMsgEvent: function (msg) { + if (msg && typeof msg !== "string") return; + switch (msg) { + case "mule": + AutoMule.check = true; + + break; + case "muleTorch": + AutoMule.torchAnniCheck = 1; + + break; + case "muleAnni": + AutoMule.torchAnniCheck = 2; + + break; + case "torch": + TorchSystem.check = true; + + break; + case "crafting": + CraftingSystem.check = true; + + break; + case "getMuleMode": + if (AutoMule.torchAnniCheck === 2) { + scriptBroadcast("2"); + } else if (AutoMule.torchAnniCheck === 1) { + scriptBroadcast("1"); + } else if (AutoMule.check) { + scriptBroadcast("0"); + } + + break; + case "pingquit": + Starter.pingQuit = true; + + break; + } + }, + + /** * Handle copy data event * @param {number} mode * @param {object | string} msg */ - receiveCopyData: function (mode, msg) { - let obj; - - if (msg === "Handle") { - console.debug("Recieved Handle :: ", mode); - } - msg === "Handle" && typeof mode === "number" && (Starter.handle = mode); - - switch (mode) { - case 1: // JoinInfo - obj = JSON.parse(msg); - console.debug("Recieved Join Info :: ", obj); - Object.assign(Starter.joinInfo, obj); - - break; - case 2: // Game info - obj = JSON.parse(msg); - console.debug("Recieved Game Info :: "); - Object.assign(Starter.gameInfo, obj); - - break; - case 3: // Game request - // in case someone is using a lightweight entry like blank/map to play manually and these aren't included - if (typeof AutoMule !== "undefined") { - // Don't let others join mule/torch/key/gold drop game - if (AutoMule.inGame || Gambling.inGame || TorchSystem.inGame || CraftingSystem.inGame) { - break; - } - } - - if (Starter.gameInfo.hasOwnProperty("gameName")) { - obj = JSON.parse(msg); - console.debug("Recieved Game Request :: ", obj.profile); - - if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { - me.gameReady && D2Bot.joinMe(obj.profile, me.gameserverip.toString(), "", "", Starter.isUp); - } else { - if (me.gameReady) { - D2Bot.joinMe(obj.profile, me.gamename, "", me.gamepassword, Starter.isUp); - } else { - // If we haven't made it to the lobby yet but are already getting game requests, stop the spam by telling followers to delay - let delay = (Starter.delay === 0 && !me.ingame && getLocation() !== sdk.game.locations.CreateGame) ? 3000 : Starter.delay; - D2Bot.joinMe(obj.profile, Starter.gameInfo.gameName, Starter.gameCount, Starter.gameInfo.gamePass, Starter.isUp, delay); - } - } - } - - break; - case 4: // Heartbeat ping - msg === "pingreq" && sendCopyData(null, me.windowtitle, 4, "pingrep"); - - break; - case 61732: // Cached info retreival - msg !== "null" && (Starter.gameInfo.crashInfo = JSON.parse(msg)); - - break; - case 1638: // getProfile - try { - obj = JSON.parse(msg); - Starter.profileInfo.profile = me.profile; - Starter.profileInfo.account = obj.account; - Starter.profileInfo.charName = obj.Character; - obj.Realm = obj.Realm.toLowerCase(); - Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; - } catch (e) { - print(e); - } - - break; - } - }, - - randomString: function (len, useNumbers = false) { - !len && (len = rand(5, 14)); - - let rval = ""; - let letters = useNumbers ? "abcdefghijklmnopqrstuvwxyz0123456789" : "abcdefghijklmnopqrstuvwxyz"; - - for (let i = 0; i < len; i += 1) { - rval += letters[rand(0, letters.length - 1)]; - } - - return rval; - }, - - randomNumberString: function (len) { - !len && (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; - }, - - LocationEvents: { - selectDifficultySP: function () { - let diff = (Starter.gameInfo.difficulty || "Highest"); - diff === "Highest" && (diff = "Hell"); // starts from top with fall-through to select highest - - switch (diff) { - case "Hell": - if (Controls.HellSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { - break; - } - // eslint-disable-next-line no-fallthrough - case "Nightmare": - if (Controls.NightmareSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { - break; - } - // eslint-disable-next-line no-fallthrough - case "Normal": - Controls.NormalSP.click(); - - break; - } - return Starter.locationTimeout(5e3, sdk.game.locations.SelectDifficultySP); - }, - - loginError: function () { - let cdkeyError = false; - let defaultPrint = true; - let string = ""; - let text = getLocation() === sdk.game.locations.LoginError - ? Controls.LoginErrorText.getText() - : Controls.LoginCdKeyInUseBy.getText(); - - if (text) { - for (let i = 0; i < text.length; i += 1) { - string += text[i]; - 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): - D2Bot.printToConsole(string); - D2Bot.stop(); - - break; - case getLocaleString(sdk.locale.text.InvalidPassword): - D2Bot.printToConsole("Invalid Password"); - ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); - D2Bot.printToConsole("Invalid Password - Restart"); - D2Bot.restart(); - - break; - case getLocaleString(sdk.locale.text.AccountDoesNotExist): - if (!!Starter.Config.MakeAccountOnFailure) { - Starter.makeAccount = true; - Controls.LoginErrorOk.click(); - - return; - } else { - D2Bot.printToConsole(string); - D2Bot.updateStatus(string); - } - - break; - case getLocaleString(sdk.locale.text.AccountIsCorrupted): - case getLocaleString(sdk.locale.text.UnableToCreateAccount): - D2Bot.printToConsole(string); - D2Bot.updateStatus(string); - - break; - 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.CdKeyIntendedForAnotherProduct): - case getLocaleString(sdk.locale.text.LoDKeyIntendedForAnotherProduct): - case getLocaleString(sdk.locale.text.CdKeyDisabled): - case getLocaleString(sdk.locale.text.LoDKeyDisabled): - cdkeyError = true; - - break; - case getLocaleString(sdk.locale.text.CdKeyInUseBy): - string += (" " + Controls.LoginLodKeyInUseBy.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); + receiveCopyData: function (mode, msg) { + let obj; + + if (msg === "Handle") { + console.debug("Recieved Handle :: ", mode); + } + msg === "Handle" && typeof mode === "number" && (Starter.handle = mode); + + switch (mode) { + case 1: // JoinInfo + obj = JSON.parse(msg); + console.debug("Recieved Join Info :: ", obj); + Object.assign(Starter.joinInfo, obj); + + break; + case 2: // Game info + obj = JSON.parse(msg); + console.debug("Recieved Game Info :: "); + Object.assign(Starter.gameInfo, obj); + + break; + case 3: // Game request + // in case someone is using a lightweight entry like blank/map to play manually and these aren't included + if (typeof AutoMule !== "undefined") { + // Don't let others join mule/torch/key/gold drop game + if (AutoMule.inGame || Gambling.inGame || TorchSystem.inGame || CraftingSystem.inGame) { + break; + } + } + + if (Starter.gameInfo.hasOwnProperty("gameName")) { + obj = JSON.parse(msg); + console.debug("Recieved Game Request :: ", obj.profile); + + if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { + me.gameReady && D2Bot.joinMe(obj.profile, me.gameserverip.toString(), "", "", Starter.isUp); + } else { + if (me.gameReady) { + D2Bot.joinMe(obj.profile, me.gamename, "", me.gamepassword, Starter.isUp); + } else { + // If we haven't made it to the lobby yet but are already getting game requests, stop the spam by telling followers to delay + let delay = (Starter.delay === 0 && !me.ingame && getLocation() !== sdk.game.locations.CreateGame) ? 3000 : Starter.delay; + D2Bot.joinMe(obj.profile, Starter.gameInfo.gameName, Starter.gameCount, Starter.gameInfo.gamePass, Starter.isUp, delay); + } + } + } + + break; + case 4: // Heartbeat ping + msg === "pingreq" && sendCopyData(null, me.windowtitle, 4, "pingrep"); + + break; + case 61732: // Cached info retreival + msg !== "null" && (Starter.gameInfo.crashInfo = JSON.parse(msg)); + + break; + case 1638: // getProfile + try { + obj = JSON.parse(msg); + Starter.profileInfo.profile = me.profile; + Starter.profileInfo.account = obj.account; + Starter.profileInfo.charName = obj.Character; + obj.Realm = obj.Realm.toLowerCase(); + Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; + } catch (e) { + print(e); + } + + break; + } + }, + + randomString: function (len, useNumbers = false) { + !len && (len = rand(5, 14)); + + let rval = ""; + let letters = useNumbers ? "abcdefghijklmnopqrstuvwxyz0123456789" : "abcdefghijklmnopqrstuvwxyz"; + + for (let i = 0; i < len; i += 1) { + rval += letters[rand(0, letters.length - 1)]; + } + + return rval; + }, + + randomNumberString: function (len) { + !len && (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; + }, + + LocationEvents: { + selectDifficultySP: function () { + let diff = (Starter.gameInfo.difficulty || "Highest"); + diff === "Highest" && (diff = "Hell"); // starts from top with fall-through to select highest + + switch (diff) { + case "Hell": + if (Controls.HellSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { + break; + } + // eslint-disable-next-line no-fallthrough + case "Nightmare": + if (Controls.NightmareSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { + break; + } + // eslint-disable-next-line no-fallthrough + case "Normal": + Controls.NormalSP.click(); + + break; + } + return Starter.locationTimeout(5e3, sdk.game.locations.SelectDifficultySP); + }, + + loginError: function () { + let cdkeyError = false; + let defaultPrint = true; + let string = ""; + let text = getLocation() === sdk.game.locations.LoginError + ? Controls.LoginErrorText.getText() + : Controls.LoginCdKeyInUseBy.getText(); + + if (text) { + for (let i = 0; i < text.length; i += 1) { + string += text[i]; + 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): + D2Bot.printToConsole(string); + D2Bot.stop(); + + break; + case getLocaleString(sdk.locale.text.InvalidPassword): + D2Bot.printToConsole("Invalid Password"); + ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); + D2Bot.printToConsole("Invalid Password - Restart"); + D2Bot.restart(); + + break; + case getLocaleString(sdk.locale.text.AccountDoesNotExist): + if (!!Starter.Config.MakeAccountOnFailure) { + Starter.makeAccount = true; + Controls.LoginErrorOk.click(); + + return; + } else { + D2Bot.printToConsole(string); + D2Bot.updateStatus(string); + } + + break; + case getLocaleString(sdk.locale.text.AccountIsCorrupted): + case getLocaleString(sdk.locale.text.UnableToCreateAccount): + D2Bot.printToConsole(string); + D2Bot.updateStatus(string); + + break; + 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.CdKeyIntendedForAnotherProduct): + case getLocaleString(sdk.locale.text.LoDKeyIntendedForAnotherProduct): + case getLocaleString(sdk.locale.text.CdKeyDisabled): + case getLocaleString(sdk.locale.text.LoDKeyDisabled): + cdkeyError = true; + + break; + case getLocaleString(sdk.locale.text.CdKeyInUseBy): + string += (" " + Controls.LoginLodKeyInUseBy.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(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(); - } - } - - Controls.LoginErrorOk.click(); - delay(1000); - Controls.BottomLeftExit.click(); + return; + } + + break; + 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(); + } + } + + Controls.LoginErrorOk.click(); + delay(1000); + Controls.BottomLeftExit.click(); - while (true) { - delay(1000); - } - } - }, - - charSelectError: function () { - let string = ""; - let text = Controls.CharSelectError.getText(); - let currentLoc = getLocation(); - - if (text) { - for (let i = 0; i < text.length; i += 1) { - string += text[i]; - i !== text.length - 1 && (string += " "); - } - - 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.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, currentLoc)) { - // Click create char button on infinite "connecting" screen - Controls.CharSelectCreate.click(); - delay(1000); + while (true) { + delay(1000); + } + } + }, + + charSelectError: function () { + let string = ""; + let text = Controls.CharSelectError.getText(); + let currentLoc = getLocation(); + + if (text) { + for (let i = 0; i < text.length; i += 1) { + string += text[i]; + i !== text.length - 1 && (string += " "); + } + + 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.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, currentLoc)) { + // Click create char button on infinite "connecting" screen + Controls.CharSelectCreate.click(); + delay(1000); - Controls.BottomLeftExit.click(); - delay(1000); + Controls.BottomLeftExit.click(); + delay(1000); - if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; + if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; - Controls.BottomLeftExit.click(); - Starter.gameInfo.rdBlocker && D2Bot.restart(); - - return false; - } - - return true; - }, - - realmDown: function () { - D2Bot.updateStatus("Realm Down"); - delay(1000); - - if (!Controls.BottomLeftExit.click()) return; - - Starter.updateCount(); - ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); - D2Bot.CDKeyRD(); - - if (Starter.gameInfo.switchKeys && !Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole("Realm Down - Changing CD-Key"); - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.printToConsole("Realm Down - Restart"); - D2Bot.restart(); - } - }, - - waitingInLine: function () { - let queue = ControlAction.getQueueTime(); - let currentLoc = getLocation(); - - if (queue > 0) { - switch (true) { - case (queue < 10000): - D2Bot.updateStatus("Waiting line... Queue: " + queue); - - // If stuck here for too long, game creation likely failed. Exit to char selection and try again. - if (queue < 10) { - if (!Starter.locationTimeout(Starter.Config.WaitInLineTimeout * 1e3, currentLoc)) { - print("Failed to create game"); - Controls.CancelCreateGame.click(); - Controls.LobbyQuit.click(); - delay(1000); - } - } - - break; - case (queue > 10000): - if (Starter.Config.WaitOutQueueRestriction) { - D2Bot.updateStatus("Waiting out Queue restriction: " + queue); - } else { - print("Restricted... Queue: " + queue); - D2Bot.printToConsole("Restricted... Queue: " + queue, sdk.colors.D2Bot.Red); - Controls.CancelCreateGame.click(); - - if (Starter.Config.WaitOutQueueExitToMenu) { - Controls.LobbyQuit.click(); - delay(1000); - Controls.BottomLeftExit.click(); - } - - // Wait out each queue as 1 sec and add extra 10 min - ControlAction.timeoutDelay("Restricted", (queue + 600) * 1000); - } - - break; - } - } - }, - - gameDoesNotExist: function () { - let currentLoc = getLocation(); - console.log("Game doesn't exist"); - - if (Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole(Starter.gameInfo.mpq + " is probably flagged.", sdk.colors.D2Bot.Gold); - - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } - } else { - Starter.locationTimeout(Starter.Config.GameDoesNotExistTimeout * 1e3, currentLoc); - } - - Starter.lastGameStatus = "ready"; - }, - - unableToConnect: function () { - let currentLoc = getLocation(); - - if (getLocation() === sdk.game.locations.TcpIpUnableToConnect) { - D2Bot.updateStatus("Unable To Connect TCP/IP"); - Starter.connectFail && ControlAction.timeoutDelay("Unable to Connect", Starter.Config.TCPIPNoHostDelay * 1e3); - Controls.OkCentered.click(); - Starter.connectFail = !Starter.connectFail; - } else { - D2Bot.updateStatus("Unable To Connect"); - - if (Starter.connectFailRetry < 2) { - Starter.connectFailRetry++; - Controls.UnableToConnectOk.click(); - - return; - } - - Starter.connectFailRetry >= 2 && (Starter.connectFail = true); - - if (Starter.connectFail && !Starter.locationTimeout(10e4, currentLoc)) { - let string = ""; - let text = Controls.LoginUnableToConnect.getText(); - - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; - i !== text.length - 1 && (string += " "); - } - } + Controls.BottomLeftExit.click(); + Starter.gameInfo.rdBlocker && D2Bot.restart(); + + return false; + } + + return true; + }, + + realmDown: function () { + D2Bot.updateStatus("Realm Down"); + delay(1000); + + if (!Controls.BottomLeftExit.click()) return; + + Starter.updateCount(); + ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); + D2Bot.CDKeyRD(); + + if (Starter.gameInfo.switchKeys && !Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole("Realm Down - Changing CD-Key"); + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.printToConsole("Realm Down - Restart"); + D2Bot.restart(); + } + }, + + waitingInLine: function () { + let queue = ControlAction.getQueueTime(); + let currentLoc = getLocation(); + + if (queue > 0) { + switch (true) { + case (queue < 10000): + D2Bot.updateStatus("Waiting line... Queue: " + queue); + + // If stuck here for too long, game creation likely failed. Exit to char selection and try again. + if (queue < 10) { + if (!Starter.locationTimeout(Starter.Config.WaitInLineTimeout * 1e3, currentLoc)) { + print("Failed to create game"); + Controls.CancelCreateGame.click(); + Controls.LobbyQuit.click(); + delay(1000); + } + } + + break; + case (queue > 10000): + if (Starter.Config.WaitOutQueueRestriction) { + D2Bot.updateStatus("Waiting out Queue restriction: " + queue); + } else { + print("Restricted... Queue: " + queue); + D2Bot.printToConsole("Restricted... Queue: " + queue, sdk.colors.D2Bot.Red); + Controls.CancelCreateGame.click(); + + if (Starter.Config.WaitOutQueueExitToMenu) { + Controls.LobbyQuit.click(); + delay(1000); + Controls.BottomLeftExit.click(); + } + + // Wait out each queue as 1 sec and add extra 10 min + ControlAction.timeoutDelay("Restricted", (queue + 600) * 1000); + } + + break; + } + } + }, + + gameDoesNotExist: function () { + let currentLoc = getLocation(); + console.log("Game doesn't exist"); + + if (Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole(Starter.gameInfo.mpq + " is probably flagged.", sdk.colors.D2Bot.Gold); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } + } else { + Starter.locationTimeout(Starter.Config.GameDoesNotExistTimeout * 1e3, currentLoc); + } + + Starter.lastGameStatus = "ready"; + }, + + unableToConnect: function () { + let currentLoc = getLocation(); + + if (getLocation() === sdk.game.locations.TcpIpUnableToConnect) { + D2Bot.updateStatus("Unable To Connect TCP/IP"); + Starter.connectFail && ControlAction.timeoutDelay("Unable to Connect", Starter.Config.TCPIPNoHostDelay * 1e3); + Controls.OkCentered.click(); + Starter.connectFail = !Starter.connectFail; + } else { + D2Bot.updateStatus("Unable To Connect"); + + if (Starter.connectFailRetry < 2) { + Starter.connectFailRetry++; + Controls.UnableToConnectOk.click(); + + return; + } + + Starter.connectFailRetry >= 2 && (Starter.connectFail = true); + + if (Starter.connectFail && !Starter.locationTimeout(10e4, currentLoc)) { + let string = ""; + let text = Controls.LoginUnableToConnect.getText(); + + if (text) { + for (let i = 0; i < text.length; i++) { + string += text[i]; + i !== text.length - 1 && (string += " "); + } + } - switch (string) { - case getLocaleString(sdk.locale.text.UnableToIndentifyVersion): - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("Version error", Starter.Config.VersionErrorDelay * 1000); + switch (string) { + case getLocaleString(sdk.locale.text.UnableToIndentifyVersion): + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("Version error", Starter.Config.VersionErrorDelay * 1000); - break; - default: // Regular UTC and everything else - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 1000 * 60); + break; + default: // Regular UTC and everything else + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 1000 * 60); - break; - } - - Starter.connectFail = false; - } - - if (!Controls.UnableToConnectOk.click()) { - return; - } - - Starter.connectFail = true; - Starter.connectFailRetry = 0; - } - }, - - openCreateGameWindow: function () { - let currentLoc = getLocation(); - - if (!Controls.CreateGameWindow.click()) { - return true; - } - - // dead HardCore character - if (Controls.CreateGameWindow.control && Controls.CreateGameWindow.disabled === sdk.game.controls.Disabled) { - if (Starter.Config.StopOnDeadHardcore) { - D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Shutting down, better luck next time", sdk.colors.D2Bot.Gold); - D2Bot.stop(); - } else { - D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Better luck next time", sdk.colors.D2Bot.Gold); - D2Bot.updateStatus(Profile().character + " has died. They shall be remembered...maybe. Better luck next time"); - Starter.deadCheck = true; - Controls.LobbyQuit.click(); - } - - return false; - } - - // in case create button gets bugged - if (!Starter.locationTimeout(5000, currentLoc)) { - if (!Controls.CreateGameWindow.click()) { - return true; - } - - if (!Controls.JoinGameWindow.click()) { - return true; - } - } - - return (getLocation() === sdk.game.locations.CreateGame); - }, - - openJoinGameWindow: function () { - let currentLoc = getLocation(); - - if (!Controls.JoinGameWindow.click()) { - return; - } - - // in case create button gets bugged - if (!Starter.locationTimeout(5000, currentLoc)) { - if (!Controls.CreateGameWindow.click()) { - return; - } - - if (!Controls.JoinGameWindow.click()) { - return; - } - } - }, - - login: function (otherMultiCheck = false) { - Starter.inGame && (Starter.inGame = false); - if (otherMultiCheck && [sdk.game.gametype.SinglePlayer, sdk.game.gametype.BattleNet].indexOf(Profile().type) === -1) { - return ControlAction.loginOtherMultiplayer(); - } - - if (getLocation() === sdk.game.locations.MainMenu) { - if (Profile().type === sdk.game.profiletype.SinglePlayer + break; + } + + Starter.connectFail = false; + } + + if (!Controls.UnableToConnectOk.click()) { + return; + } + + Starter.connectFail = true; + Starter.connectFailRetry = 0; + } + }, + + openCreateGameWindow: function () { + let currentLoc = getLocation(); + + if (!Controls.CreateGameWindow.click()) { + return true; + } + + // dead HardCore character + if (Controls.CreateGameWindow.control && Controls.CreateGameWindow.disabled === sdk.game.controls.Disabled) { + if (Starter.Config.StopOnDeadHardcore) { + D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Shutting down, better luck next time", sdk.colors.D2Bot.Gold); + D2Bot.stop(); + } else { + D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Better luck next time", sdk.colors.D2Bot.Gold); + D2Bot.updateStatus(Profile().character + " has died. They shall be remembered...maybe. Better luck next time"); + Starter.deadCheck = true; + Controls.LobbyQuit.click(); + } + + return false; + } + + // in case create button gets bugged + if (!Starter.locationTimeout(5000, currentLoc)) { + if (!Controls.CreateGameWindow.click()) { + return true; + } + + if (!Controls.JoinGameWindow.click()) { + return true; + } + } + + return (getLocation() === sdk.game.locations.CreateGame); + }, + + openJoinGameWindow: function () { + let currentLoc = getLocation(); + + if (!Controls.JoinGameWindow.click()) { + return; + } + + // in case create button gets bugged + if (!Starter.locationTimeout(5000, currentLoc)) { + if (!Controls.CreateGameWindow.click()) { + return; + } + + if (!Controls.JoinGameWindow.click()) { + return; + } + } + }, + + login: function (otherMultiCheck = false) { + Starter.inGame && (Starter.inGame = false); + if (otherMultiCheck && [sdk.game.gametype.SinglePlayer, sdk.game.gametype.BattleNet].indexOf(Profile().type) === -1) { + return ControlAction.loginOtherMultiplayer(); + } + + if (getLocation() === sdk.game.locations.MainMenu) { + if (Profile().type === sdk.game.profiletype.SinglePlayer && Starter.firstRun && Controls.SinglePlayer.click()) { - return true; - } - } - - // 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) + return true; + } + } + + // 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.BottomLeftExit.click(); + Controls.BottomLeftExit.click(); - return false; - } - } + return false; + } + } - // Multiple realm botting fix in case of R/D or disconnect - Starter.firstLogin && getLocation() === sdk.game.locations.Login && Controls.BottomLeftExit.click(); + // 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"); + D2Bot.updateStatus("Logging In"); - try { - login(me.profile); - } catch (e) { - if (getLocation() === sdk.game.locations.CharSelect && Starter.loginRetry < 2) { - if (!ControlAction.findCharacter(Starter.profileInfo)) { - // dead hardcore character on sp - if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { - // Exit from that pop-up - Controls.OkCentered.click(); - D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); - D2Bot.stop(); - } else { - Starter.loginRetry++; - } - } else { - login(me.profile); - } - } else if (getLocation() === sdk.game.locations.TcpIpEnterIp && Profile().type === sdk.game.profiletype.TcpIpJoin) { - return true; // handled in its own case - } else { - print(e + " " + getLocation()); - } - } - - return true; - }, - - otherMultiplayerSelect: function () { - if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { - Controls.TcpIp.click() && (Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpJoin.click()); - } else if (Profile().type === sdk.game.profiletype.OpenBattlenet) { - Controls.OpenBattleNet.click(); - } else { - Controls.OtherMultiplayerCancel.click(); - } - } - }, - }; - - return { - ControlAction: ControlAction, - Starter: Starter, - }; + try { + login(me.profile); + } catch (e) { + if (getLocation() === sdk.game.locations.CharSelect && Starter.loginRetry < 2) { + if (!ControlAction.findCharacter(Starter.profileInfo)) { + // dead hardcore character on sp + if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { + // Exit from that pop-up + Controls.OkCentered.click(); + D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } else { + Starter.loginRetry++; + } + } else { + login(me.profile); + } + } else if (getLocation() === sdk.game.locations.TcpIpEnterIp && Profile().type === sdk.game.profiletype.TcpIpJoin) { + return true; // handled in its own case + } else { + print(e + " " + getLocation()); + } + } + + return true; + }, + + otherMultiplayerSelect: function () { + if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { + Controls.TcpIp.click() && (Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpJoin.click()); + } else if (Profile().type === sdk.game.profiletype.OpenBattlenet) { + Controls.OpenBattleNet.click(); + } else { + Controls.OtherMultiplayerCancel.click(); + } + } + }, + }; + + return { + ControlAction: ControlAction, + Starter: Starter, + }; })); diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 77a8ddec2..80ff8a287 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -26,165 +26,165 @@ String.prototype.lcsGraph = function (compareToThis) { - if (!this.length || !compareToThis || !compareToThis.length) { - return null; - } - - let stringA = this.toString().toLowerCase(); - let stringB = compareToThis.toLowerCase(); - let graph = Array(this.length); - let check = (i, j) => (i < 0 || j < 0 || i >= stringA.length || j >= stringB.length) ? 0 : graph[i][j]; - - for (let x = 0; x < stringA.length; x++) { - graph[x] = new Uint16Array(stringB.length); - - for (let y = 0; y < stringB.length; y++) { - if (stringA[x] === stringB[y]) { - graph[x][y] = check(x - 1, y - 1) + 1; - } else { - graph[x][y] = Math.max(check(x - 1, y), check(x, y - 1)); - } - } - } - - return { a: this.toString(), b: compareToThis, graph: graph }; + if (!this.length || !compareToThis || !compareToThis.length) { + return null; + } + + let stringA = this.toString().toLowerCase(); + let stringB = compareToThis.toLowerCase(); + let graph = Array(this.length); + let check = (i, j) => (i < 0 || j < 0 || i >= stringA.length || j >= stringB.length) ? 0 : graph[i][j]; + + for (let x = 0; x < stringA.length; x++) { + graph[x] = new Uint16Array(stringB.length); + + for (let y = 0; y < stringB.length; y++) { + if (stringA[x] === stringB[y]) { + graph[x][y] = check(x - 1, y - 1) + 1; + } else { + graph[x][y] = Math.max(check(x - 1, y), check(x, y - 1)); + } + } + } + + return { a: this.toString(), b: compareToThis, graph: graph }; }; String.prototype.diffCount = function (stringB) { - try { - if (typeof stringB !== "string" || !stringB) { - return this.length; - } + try { + if (typeof stringB !== "string" || !stringB) { + return this.length; + } - if (!this.length) { - return stringB.length; - } + if (!this.length) { + return stringB.length; + } - let graph = this.lcsGraph(stringB); + let graph = this.lcsGraph(stringB); - return (Math.max(graph.a.length, graph.b.length) - graph.graph[graph.a.length - 1][graph.b.length - 1]); - } catch (err) { - print(err.stack); - } + return (Math.max(graph.a.length, graph.b.length) - graph.graph[graph.a.length - 1][graph.b.length - 1]); + } catch (err) { + print(err.stack); + } - return Infinity; + return Infinity; }; if (!String.prototype.includes) { - String.prototype.includes = function (search, start) { - "use strict"; - if (typeof start !== "number") { - start = 0; - } - - if (start + search.length > this.length) { - return false; - } else { - return this.indexOf(search, start) !== -1; - } - }; + String.prototype.includes = function (search, start) { + "use strict"; + if (typeof start !== "number") { + start = 0; + } + + if (start + search.length > this.length) { + return false; + } else { + return this.indexOf(search, start) !== -1; + } + }; } String.prototype.capitalize = function (downcase = false) { - return this.charAt(0).toUpperCase() + (downcase ? this.slice(1).toLowerCase() : this.slice(1)); + return this.charAt(0).toUpperCase() + (downcase ? this.slice(1).toLowerCase() : this.slice(1)); }; String.prototype.padEnd = function padEnd(targetLength, padString) { - targetLength = targetLength >> 0; //floor if number or convert non-number to 0; - padString = String(typeof padString !== "undefined" ? padString : " "); - if (this.length > targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed - } - return String(this) + padString.slice(0, targetLength); - } + targetLength = targetLength >> 0; //floor if number or convert non-number to 0; + padString = String(typeof padString !== "undefined" ? padString : " "); + if (this.length > targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed + } + return String(this) + padString.slice(0, targetLength); + } }; String.prototype.padStart = function padStart(targetLength, padString) { - targetLength = targetLength >> 0; //floor if number or convert non-number to 0; - padString = String(typeof padString !== "undefined" ? padString : " "); - if (this.length > targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed - } - return padString.slice(0, targetLength) + String(this); - } + targetLength = targetLength >> 0; //floor if number or convert non-number to 0; + padString = String(typeof padString !== "undefined" ? padString : " "); + if (this.length > targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed + } + return padString.slice(0, targetLength) + String(this); + } }; String.prototype.repeat = function(count) { - "use strict"; - if (this == null) throw new TypeError("can't convert " + this + " to object"); - let str = "" + this; - count = +count; - // eslint-disable-next-line no-self-compare - if (count !== count) { - count = 0; - } - if (count < 0) throw new RangeError("repeat count must be non-negative"); - if (count === Infinity) throw new RangeError("repeat count must be less than infinity"); - - count = Math.floor(count); - if (str.length === 0 || count === 0) { - return ""; - } - if (str.length * count >= 1 << 28) { - throw new RangeError( - "repeat count must not overflow maximum string size" - ); - } - let rpt = ""; - for (;;) { - if ((count & 1) === 1) { - rpt += str; - } - count >>>= 1; - if (count === 0) { - break; - } - str += str; - } - return rpt; + "use strict"; + if (this == null) throw new TypeError("can't convert " + this + " to object"); + let str = "" + this; + count = +count; + // eslint-disable-next-line no-self-compare + if (count !== count) { + count = 0; + } + if (count < 0) throw new RangeError("repeat count must be non-negative"); + if (count === Infinity) throw new RangeError("repeat count must be less than infinity"); + + count = Math.floor(count); + if (str.length === 0 || count === 0) { + return ""; + } + if (str.length * count >= 1 << 28) { + throw new RangeError( + "repeat count must not overflow maximum string size" + ); + } + let rpt = ""; + for (;;) { + if ((count & 1) === 1) { + rpt += str; + } + count >>>= 1; + if (count === 0) { + break; + } + str += str; + } + return rpt; }; // Trim String if (!String.prototype.trim) { - String.prototype.trim = function () { - return this.replace(/^\s+|\s+$/g, ""); - }; + String.prototype.trim = function () { + return this.replace(/^\s+|\s+$/g, ""); + }; } if (!String.prototype.startsWith) { - String.prototype.startsWith = function (prefix) { - return !prefix || this.substring(0, prefix.length) === prefix; - }; + String.prototype.startsWith = function (prefix) { + return !prefix || this.substring(0, prefix.length) === prefix; + }; } if (!String.prototype.endsWith) { - String.prototype.endsWith = function (search, this_len) { - if (this_len === undefined || this_len > this.length) { - this_len = this.length; - } - return this.substring(this_len - search.length, this_len) === search; - }; + String.prototype.endsWith = function (search, this_len) { + if (this_len === undefined || this_len > this.length) { + this_len = this.length; + } + return this.substring(this_len - search.length, this_len) === search; + }; } if (!String.isEqual) { - /** - * Check if two strings are equal - * @static - * @param {string} str1 - * @param {string} str2 - * @returns {boolean} - */ - String.isEqual = function (str1, str2) { - return str1.toLowerCase() === str2.toLowerCase(); - }; + /** + * Check if two strings are equal + * @static + * @param {string} str1 + * @param {string} str2 + * @returns {boolean} + */ + String.isEqual = function (str1, str2) { + return str1.toLowerCase() === str2.toLowerCase(); + }; } /** @@ -197,14 +197,14 @@ if (!String.isEqual) { * @returns {string} The formatted string. */ String.prototype.format = function (...pairs) { - if (!pairs.length) return this; - let newString = this; - pairs.forEach(pair => { - let [match, replace] = pair; - if (match === undefined || replace === undefined) return; - newString = newString.replace(match, replace); - }); - return newString; + if (!pairs.length) return this; + let newString = this; + pairs.forEach(pair => { + let [match, replace] = pair; + if (match === undefined || replace === undefined) return; + newString = newString.replace(match, replace); + }); + return newString; }; /** @@ -240,299 +240,303 @@ String.prototype.format = function (...pairs) { Array.prototype.isEqual = function (t) { - return this.map((x, i) => t.hasOwnProperty(i) && x === t[i]).reduce((a, c) => c & a, true); + return this.map((x, i) => t.hasOwnProperty(i) && x === t[i]).reduce((a, c) => c & a, true); }; Array.prototype.filterHighDistance = function (step = 0) { - if (step > 10) return this; // If we took 10 steps, give up - const distances = this.map( - (x, i) => this - .filter((_, index) => index !== i) // Not this element - .map(y => Math.abs(y - this[i])).reduce((a, c) => c + a || 0, 0) / (this.length - 1) // Avg of distance to others - ); - const distancesAvg = distances.reduce((a, c) => c + a || 0, 0) / this.length; - - // Recursion until only viable areas are in the list - if (distancesAvg > 30) return this.filter((x, i) => distances[i] < distancesAvg * 0.75 || this[i] < distancesAvg).filterHighDistance(step++); - - return this; // Everything is relatively the same + if (step > 10) return this; // If we took 10 steps, give up + const distances = this.map( + (x, i) => this + .filter((_, index) => index !== i) // Not this element + .map(y => Math.abs(y - this[i])).reduce((a, c) => c + a || 0, 0) / (this.length - 1) // Avg of distance to others + ); + const distancesAvg = distances.reduce((a, c) => c + a || 0, 0) / this.length; + + // Recursion until only viable areas are in the list + if (distancesAvg > 30) { + return this + .filter((x, i) => distances[i] < distancesAvg * 0.75 || this[i] < distancesAvg) + .filterHighDistance(step++); + } + + return this; // Everything is relatively the same }; // https://tc39.github.io/ecma262/#sec-array.prototype.findindex if (!Array.prototype.findIndex) { - Object.defineProperty(Array.prototype, "findIndex", { - value: function (predicate) { - // 1. Let O be ? ToObject(this value). - if (this == null) { - throw new TypeError('"this" is null or not defined'); - } - - let o = Object(this); - - // 2. Let len be ? ToLength(? Get(O, "length")). - let len = o.length >>> 0; - - // 3. If IsCallable(predicate) is false, throw a TypeError exception. - if (typeof predicate !== "function") { - throw new TypeError("predicate must be a function"); - } - - // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. - let thisArg = arguments[1]; - - // 5. Let k be 0. - let k = 0; - - // 6. Repeat, while k < len - while (k < len) { - // a. Let Pk be ! ToString(k). - // b. Let kValue be ? Get(O, Pk). - // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). - // d. If testResult is true, return k. - let kValue = o[k]; - if (predicate.call(thisArg, kValue, k, o)) { - return k; - } - // e. Increase k by 1. - k++; - } - - // 7. Return -1. - return -1; - }, - configurable: true, - writable: true - }); + Object.defineProperty(Array.prototype, "findIndex", { + value: function (predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + let o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + let len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== "function") { + throw new TypeError("predicate must be a function"); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + let thisArg = arguments[1]; + + // 5. Let k be 0. + let k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). + // d. If testResult is true, return k. + let kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return k; + } + // e. Increase k by 1. + k++; + } + + // 7. Return -1. + return -1; + }, + configurable: true, + writable: true + }); } // basic remove prototype if (!Array.prototype.remove) { - Array.prototype.remove = function (val) { - if (this === undefined || !this.length) throw new Error("No Array defined"); - if (val === undefined || !val) throw new Error("Cannot remove and element if there is no element defined"); - let index = this.indexOf(val); - index >= 0 && this.splice(index, 1); - return this; - }; + Array.prototype.remove = function (val) { + if (this === undefined || !this.length) throw new Error("No Array defined"); + if (val === undefined || !val) throw new Error("Cannot remove and element if there is no element defined"); + let index = this.indexOf(val); + index >= 0 && this.splice(index, 1); + return this; + }; } // Production steps of ECMA-262, Edition 6, 22.1.2.1 if (!Array.from) { - Array.from = (function () { - let toStr = Object.prototype.toString; - let isCallable = function (fn) { - return typeof fn === "function" || toStr.call(fn) === "[object Function]"; - }; - let toInteger = function (value) { - let number = Number(value); - if (isNaN(number)) { - return 0; - } - if (number === 0 || !isFinite(number)) { - return number; - } - return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); - }; - let maxSafeInteger = Math.pow(2, 53) - 1; - let toLength = function (value) { - let len = toInteger(value); - return Math.min(Math.max(len, 0), maxSafeInteger); - }; - - // The length property of the from method is 1. - return function from(arrayLike/*, mapFn, thisArg */) { - // 1. Let C be the this value. - let C = this; - - // 2. Let items be ToObject(arrayLike). - let items = Object(arrayLike); - - // 3. ReturnIfAbrupt(items). - if (arrayLike == null) { - throw new TypeError("Array.from requires an array-like object - not null or undefined"); - } - - // 4. If mapfn is undefined, then let mapping be false. - let mapFn = arguments.length > 1 ? arguments[1] : void undefined; - let T; - if (typeof mapFn !== "undefined") { - // 5. else - // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. - if (!isCallable(mapFn)) { - throw new TypeError("Array.from: when provided, the second argument must be a function"); - } - - // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. - if (arguments.length > 2) { - T = arguments[2]; - } - } - - // 10. Let lenValue be Get(items, "length"). - // 11. Let len be ToLength(lenValue). - let len = toLength(items.length); - - // 13. If IsConstructor(C) is true, then - // 13. a. Let A be the result of calling the [[Construct]] internal method - // of C with an argument list containing the single item len. - // 14. a. Else, Let A be ArrayCreate(len). - let A = isCallable(C) ? Object(new C(len)) : new Array(len); - - // 16. Let k be 0. - let k = 0; - // 17. Repeat, while k < len… (also steps a - h) - let kValue; - while (k < len) { - kValue = items[k]; - if (mapFn) { - A[k] = typeof T === "undefined" ? mapFn(kValue, k) : mapFn.call(T, kValue, k); - } else { - A[k] = kValue; - } - k += 1; - } - // 18. Let putStatus be Put(A, "length", len, true). - A.length = len; - // 20. Return A. - return A; - }; - }()); + Array.from = (function () { + let toStr = Object.prototype.toString; + let isCallable = function (fn) { + return typeof fn === "function" || toStr.call(fn) === "[object Function]"; + }; + let toInteger = function (value) { + let number = Number(value); + if (isNaN(number)) { + return 0; + } + if (number === 0 || !isFinite(number)) { + return number; + } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + let maxSafeInteger = Math.pow(2, 53) - 1; + let toLength = function (value) { + let len = toInteger(value); + return Math.min(Math.max(len, 0), maxSafeInteger); + }; + + // The length property of the from method is 1. + return function from(arrayLike/*, mapFn, thisArg */) { + // 1. Let C be the this value. + let C = this; + + // 2. Let items be ToObject(arrayLike). + let items = Object(arrayLike); + + // 3. ReturnIfAbrupt(items). + if (arrayLike == null) { + throw new TypeError("Array.from requires an array-like object - not null or undefined"); + } + + // 4. If mapfn is undefined, then let mapping be false. + let mapFn = arguments.length > 1 ? arguments[1] : void undefined; + let T; + if (typeof mapFn !== "undefined") { + // 5. else + // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. + if (!isCallable(mapFn)) { + throw new TypeError("Array.from: when provided, the second argument must be a function"); + } + + // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 2) { + T = arguments[2]; + } + } + + // 10. Let lenValue be Get(items, "length"). + // 11. Let len be ToLength(lenValue). + let len = toLength(items.length); + + // 13. If IsConstructor(C) is true, then + // 13. a. Let A be the result of calling the [[Construct]] internal method + // of C with an argument list containing the single item len. + // 14. a. Else, Let A be ArrayCreate(len). + let A = isCallable(C) ? Object(new C(len)) : new Array(len); + + // 16. Let k be 0. + let k = 0; + // 17. Repeat, while k < len… (also steps a - h) + let kValue; + while (k < len) { + kValue = items[k]; + if (mapFn) { + A[k] = typeof T === "undefined" ? mapFn(kValue, k) : mapFn.call(T, kValue, k); + } else { + A[k] = kValue; + } + k += 1; + } + // 18. Let putStatus be Put(A, "length", len, true). + A.length = len; + // 20. Return A. + return A; + }; + }()); } // Filter null or undefined objects in array if (!Array.prototype.filterNull) { - Array.prototype.filterNull = function () { - return this.filter(x => x); - }; + Array.prototype.filterNull = function () { + return this.filter(x => x); + }; } // Map the objects with the callback function and filter null values after mapping. if (!Array.prototype.compactMap) { - Array.prototype.compactMap = function (callback) { - return this.map((x, i, array) => { - if (x == null) { - return null; - } - return callback(x, i, array); - }) - .filterNull(); - }; + Array.prototype.compactMap = function (callback) { + return this.map((x, i, array) => { + if (x == null) { + return null; + } + return callback(x, i, array); + }) + .filterNull(); + }; } // Returns a random object in array if (!Array.prototype.random) { - Array.prototype.random = function () { - return this[Math.floor((Math.random() * this.length))]; - }; + Array.prototype.random = function () { + return this[Math.floor((Math.random() * this.length))]; + }; } if (!Array.prototype.includes) { - Array.prototype.includes = function (e) { - return this.indexOf(e) > -1; - }; + Array.prototype.includes = function (e) { + return this.indexOf(e) > -1; + }; } 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]; + }; } Array.prototype.contains = Array.prototype.includes; if (!Array.prototype.intersection) { - Array.prototype.intersection = function (other) { - return this.filter(e => other.includes(e)); - }; + Array.prototype.intersection = function (other) { + return this.filter(e => other.includes(e)); + }; } if (!Array.prototype.difference) { - Array.prototype.difference = function (other) { - return this.filter(e => !other.includes(e)); - }; + Array.prototype.difference = function (other) { + return this.filter(e => !other.includes(e)); + }; } if (!Array.prototype.symmetricDifference) { - Array.prototype.symmetricDifference = function (other) { - return this - .filter(e => !other.includes(e)) - .concat(other.filter(e => !this.includes(e))); - }; + Array.prototype.symmetricDifference = function (other) { + return this + .filter(e => !other.includes(e)) + .concat(other.filter(e => !this.includes(e))); + }; } // Shuffle Array // http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript if (!Array.prototype.shuffle) { - Array.prototype.shuffle = function () { - let temp, index; - let counter = this.length; - - // While there are elements in the array - while (counter > 0) { - // Pick a random index - index = Math.floor(Math.random() * counter); - - // Decrease counter by 1 - counter -= 1; - - // And swap the last element with it - temp = this[counter]; - this[counter] = this[index]; - this[index] = temp; - } - - return this; - }; + Array.prototype.shuffle = function () { + let temp, index; + let counter = this.length; + + // While there are elements in the array + while (counter > 0) { + // Pick a random index + index = Math.floor(Math.random() * counter); + + // Decrease counter by 1 + counter -= 1; + + // And swap the last element with it + temp = this[counter]; + this[counter] = this[index]; + this[index] = temp; + } + + return this; + }; } // Array.find polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find if (!Array.prototype.find) { - Object.defineProperty(Array.prototype, "find", { - value: function (predicate) { - if (this === null) { - throw new TypeError('"this" is null or not defined'); - } + Object.defineProperty(Array.prototype, "find", { + value: function (predicate) { + if (this === null) { + throw new TypeError('"this" is null or not defined'); + } - let o = Object(this); + let o = Object(this); - let len = o.length >>> 0; + let len = o.length >>> 0; - if (typeof predicate !== "function") { - throw new TypeError("predicate must be a function"); - } + if (typeof predicate !== "function") { + throw new TypeError("predicate must be a function"); + } - let thisArg = arguments[1]; + let thisArg = arguments[1]; - let k = 0; + let k = 0; - while (k < len) { - let kValue = o[k]; + while (k < len) { + let kValue = o[k]; - if (predicate.call(thisArg, kValue, k, o)) { - return kValue; - } + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } - k++; - } + k++; + } - return undefined; - }, - configurable: true, - writable: true - }); + return undefined; + }, + configurable: true, + writable: true + }); } // Fill an array with the same value from start to end indexes. Array.prototype.fill = function (value, start = 0, end = undefined) { - let stop = end || this.length; - for (let i = start; i < stop; i++) { - this[i] = value; - } - return this; + let stop = end || this.length; + for (let i = start; i < stop; i++) { + this[i] = value; + } + return this; }; /** @@ -540,9 +544,9 @@ Array.prototype.fill = function (value, start = 0, end = undefined) { * @return undefined|* */ if (!Array.prototype.first) { - Array.prototype.first = function () { - return this.length > 0 ? this[0] : undefined; - }; + Array.prototype.first = function () { + return this.length > 0 ? this[0] : undefined; + }; } /** @@ -550,9 +554,9 @@ if (!Array.prototype.first) { * @return undefined|* */ if (!Array.prototype.last) { - Array.prototype.last = function () { - return this.length > 0 ? this[this.length - 1] : undefined; - }; + Array.prototype.last = function () { + return this.length > 0 ? this[this.length - 1] : undefined; + }; } /** @@ -561,23 +565,23 @@ if (!Array.prototype.last) { * @return array */ if (!Array.prototype.flat) { - Object.defineProperty(Array.prototype, "flat", { - value: function flat() { - let depth = arguments.length > 0 ? isNaN(arguments[0]) ? 1 : Number(arguments[0]) : 1; - - return depth ? Array.prototype.reduce.call(this, function (acc, cur) { - if (Array.isArray(cur)) { - acc.push.apply(acc, flat.call(cur, depth - 1)); - } else { - acc.push(cur); - } - - return acc; - }, []) : Array.prototype.slice.call(this); - }, - configurable: true, - writable: true - }); + Object.defineProperty(Array.prototype, "flat", { + value: function flat() { + let depth = arguments.length > 0 ? isNaN(arguments[0]) ? 1 : Number(arguments[0]) : 1; + + return depth ? Array.prototype.reduce.call(this, function (acc, cur) { + if (Array.isArray(cur)) { + acc.push.apply(acc, flat.call(cur, depth - 1)); + } else { + acc.push(cur); + } + + return acc; + }, []) : Array.prototype.slice.call(this); + }, + configurable: true, + writable: true + }); } /** @@ -587,13 +591,13 @@ if (!Array.prototype.flat) { * @return {Array<...args>} */ if (!Array.of) { - Object.defineProperty(Array, "of", { - value: function of() { - return Array.prototype.slice.call(arguments); - }, - configurable: true, - writable: true - }); + Object.defineProperty(Array, "of", { + value: function of() { + return Array.prototype.slice.call(arguments); + }, + configurable: true, + writable: true + }); } /** @@ -603,9 +607,9 @@ if (!Array.of) { * @return {Array} */ if (!Array.prototype.toReversed) { - Array.prototype.toReversed = function() { - return this.slice().reverse(); - }; + Array.prototype.toReversed = function() { + return this.slice().reverse(); + }; } /** @@ -617,9 +621,9 @@ if (!Array.prototype.toReversed) { * @returns {Array} A new array with the elements sorted in ascending order. */ if (!Array.prototype.toSorted) { - Array.prototype.toSorted = function(compareFunction) { - return this.slice().sort(compareFunction); - }; + Array.prototype.toSorted = function(compareFunction) { + return this.slice().sort(compareFunction); + }; } /** @@ -634,12 +638,12 @@ if (!Array.prototype.toSorted) { * @returns {Array} A new array with the modified elements. */ if (!Array.prototype.toSpliced) { - Array.prototype.toSpliced = function(start, deleteCount) { - const newArr = this.slice(); - const items = Array.prototype.slice.call(arguments, 2); - Array.prototype.splice.apply(newArr, [start, deleteCount].concat(items)); - return newArr; - }; + Array.prototype.toSpliced = function(start, deleteCount) { + const newArr = this.slice(); + const items = Array.prototype.slice.call(arguments, 2); + Array.prototype.splice.apply(newArr, [start, deleteCount].concat(items)); + return newArr; + }; } /** @@ -657,43 +661,43 @@ if (!Array.prototype.toSpliced) { * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign */ if (typeof Object.assign !== "function") { - Object.defineProperty(Object, "assign", { - value: function assign(target) { - if (target === null) { - throw new TypeError("Cannot convert undefined or null to object"); - } - - let to = Object(target); - - for (let index = 1; index < arguments.length; index++) { - let nextSource = arguments[index]; - - if (nextSource !== null) { - for (let nextKey in nextSource) { - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - - return to; - }, - writable: true, - configurable: true - }); + Object.defineProperty(Object, "assign", { + value: function assign(target) { + if (target === null) { + throw new TypeError("Cannot convert undefined or null to object"); + } + + let to = Object(target); + + for (let index = 1; index < arguments.length; index++) { + let nextSource = arguments[index]; + + if (nextSource !== null) { + for (let nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + + return to; + }, + writable: true, + configurable: true + }); } 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]]; }); + }; } // eslint-disable-next-line no-var @@ -702,17 +706,17 @@ if (typeof global === "undefined") var global = [].filter.constructor("return th global["globalThis"] = [].filter.constructor("return this")(); if (!global.hasOwnProperty("require")) { - let cache; - Object.defineProperty(global, "require", { - get: function () { - if (cache) return cache; - !isIncluded("require.js") && include("require.js"); - return cache; // cache is loaded by require.js - }, - set: function(v) { - cache = v; - } - }); + let cache; + Object.defineProperty(global, "require", { + get: function () { + if (cache) return cache; + !isIncluded("require.js") && include("require.js"); + return cache; // cache is loaded by require.js + }, + set: function(v) { + cache = v; + } + }); } /** @@ -727,29 +731,29 @@ if (!global.hasOwnProperty("require")) { // Returns a random integer between start and end included. Math.randomIntBetween = function (start, end) { - let min = Math.ceil(start); - let max = Math.floor(end); - return Math.floor(Math.random() * (max - min + 1)) + min; + let min = Math.ceil(start); + let max = Math.floor(end); + return Math.floor(Math.random() * (max - min + 1)) + min; }; if (!Math.trunc) { - /** - * Polyfill for Math.trunc - * Static method returns the integer part of a number by removing any fractional digits. - * @static - * @param {number} number - * @returns {number} - */ - Math.trunc = function (number) { - return number < 0 ? Math.ceil(number) : Math.floor(number); - }; + /** + * Polyfill for Math.trunc + * Static method returns the integer part of a number by removing any fractional digits. + * @static + * @param {number} number + * @returns {number} + */ + Math.trunc = function (number) { + return number < 0 ? Math.ceil(number) : Math.floor(number); + }; } Math.percentDifference = function (value1, value2) { - const diff = Math.abs(value1 - value2); - const average = (value1 + value2) / 2; - const percentDiff = (diff / average) * 100; - return Math.trunc(percentDiff); + const diff = Math.abs(value1 - value2); + const average = (value1 + value2) / 2; + const percentDiff = (diff / average) * 100; + return Math.trunc(percentDiff); }; /** @@ -764,41 +768,41 @@ Math.percentDifference = function (value1, value2) { */ if (typeof Map.prototype.forEach !== "function") { - Map.prototype.forEach = function(callbackFn, thisArg) { - thisArg = thisArg || this; - for (let [key, value] of this.entries()) { - callbackFn.call(thisArg, value, key, this); - } - }; + Map.prototype.forEach = function(callbackFn, thisArg) { + thisArg = thisArg || this; + for (let [key, value] of this.entries()) { + callbackFn.call(thisArg, value, key, this); + } + }; } Map.prototype.toString = function() { - let obj = {}; - for (let [key, value] of this.entries()) { - obj[key] = value; - } - return JSON.stringify(obj); + let obj = {}; + for (let [key, value] of this.entries()) { + obj[key] = value; + } + return JSON.stringify(obj); }; /** * @returns {Array} */ Map.prototype.keys = function() { - let keys = []; - // eslint-disable-next-line no-unused-vars - for (let [key, _value] of this.entries()) { - keys.push(key); - } - return keys; + let keys = []; + // eslint-disable-next-line no-unused-vars + for (let [key, _value] of this.entries()) { + keys.push(key); + } + return keys; }; Map.prototype.values = function() { - let values = []; - // eslint-disable-next-line no-unused-vars - for (let [_key, value] of this.entries()) { - values.push(value); - } - return values; + let values = []; + // eslint-disable-next-line no-unused-vars + for (let [_key, value] of this.entries()) { + values.push(value); + } + return values; }; /** @@ -818,89 +822,89 @@ Map.prototype.values = function() { */ if (typeof Set.prototype.forEach !== "function") { - Set.prototype.forEach = function(callbackFn, thisArg) { - thisArg = thisArg || this; - for (let item of this) { - callbackFn.call(thisArg, item, item, this); - } - }; + Set.prototype.forEach = function(callbackFn, thisArg) { + thisArg = thisArg || this; + for (let item of this) { + callbackFn.call(thisArg, item, item, this); + } + }; } if (typeof Set.prototype.keys !== "function") { - Set.prototype.keys = function() { - let keys = []; - for (let item of this) { - keys.push(item); - } - return keys; - }; + Set.prototype.keys = function() { + let keys = []; + for (let item of this) { + keys.push(item); + } + return keys; + }; } if (typeof Set.prototype.values !== "function") { - Set.prototype.values = function() { - let values = []; - for (let item of this) { - values.push(item); - } - return values; - }; + Set.prototype.values = function() { + let values = []; + for (let item of this) { + values.push(item); + } + return values; + }; } if (typeof Set.prototype.entries !== "function") { - Set.prototype.entries = function() { - let entries = []; - for (let item of this) { - entries.push([item, item]); - } - return entries; - }; + Set.prototype.entries = function() { + let entries = []; + for (let item of this) { + entries.push([item, item]); + } + return entries; + }; } Set.prototype.isSuperset = function(subset) { - for (let item of subset) { - if (!this.has(item)) { - return false; - } - } - return true; + for (let item of subset) { + if (!this.has(item)) { + return false; + } + } + return true; }; Set.prototype.union = function(setB) { - let union = new Set(this); - for (let item of setB) { - union.add(item); - } - return union; + let union = new Set(this); + for (let item of setB) { + union.add(item); + } + return union; }; Set.prototype.intersection = function(setB) { - let intersection = new Set(); - for (let item of setB) { - if (this.has(item)) { - intersection.add(item); - } - } - return intersection; + let intersection = new Set(); + for (let item of setB) { + if (this.has(item)) { + intersection.add(item); + } + } + return intersection; }; Set.prototype.symmetricDifference = function(setB) { - let difference = new Set(this); - for (let item of setB) { - if (difference.has(item)) { - difference.delete(item); - } else { - difference.add(item); - } - } - return difference; + let difference = new Set(this); + for (let item of setB) { + if (difference.has(item)) { + difference.delete(item); + } else { + difference.add(item); + } + } + return difference; }; Set.prototype.difference = function(setB) { - let difference = new Set(this); - for (let item of setB) { - difference.delete(item); - } - return difference; + let difference = new Set(this); + for (let item of setB) { + difference.delete(item); + } + return difference; }; /** @@ -920,201 +924,204 @@ Set.prototype.difference = function(setB) { */ (function (global, print) { - global.console = global.console || (function () { - const console = {}; - - const argMap = (el) => { - switch (typeof el) { - case "undefined": - return "undefined"; - case "boolean": - return el ? "true" : false; - case "function": - return "function"; - case "object": - if (el === null) return "null"; - if (el instanceof Error) { - return JSON.stringify({ - name: (el.name || "Error"), - fileName: (el.fileName || "unknown"), - lineNumber: (el.lineNumber || ":?"), - message: (el.message || ""), - stack: (el.stack || ""), - }); - } - if (el instanceof Map) { - return el.toString(); - } - if (Array.isArray(el)) { - // handle multidimensional arrays - return JSON.stringify( - el.map(inner => Array.isArray(inner) ? inner.map(argMap) : inner) - ); - } - return JSON.stringify(el); - } - return el; - }; - - console.log = function (...args) { - // use call to avoid type errors - print.call(null, args.map(argMap).join(",")); - }; - - console.printDebug = true; - console.debug = function (...args) { - if (console.printDebug) { - const stack = new Error().stack.match(/[^\r\n]+/g); - let filenameAndLine = stack && stack.length && stack[1].substr(stack[1].lastIndexOf("\\") + 1) || "unknown:0"; - filenameAndLine = filenameAndLine.replace(":", " :: "); - this.log("[ÿc:Debugÿc0] ÿc:[" + filenameAndLine + "]ÿc0 " + args.map(argMap).join(",")); - } - }; - - console.warn = function (...args) { - const stack = new Error().stack.match(/[^\r\n]+/g); - let filenameAndLine = stack && stack.length && stack[1].substr(stack[1].lastIndexOf("\\") + 1) || "unknown:0"; - filenameAndLine = filenameAndLine.replace(":", " :: "); - this.log("[ÿc9Warningÿc0] ÿc9[" + filenameAndLine + "]ÿc0 " + args.map(argMap).join(",")); - }; - - console.error = function (error = "") { - let msg, source; - - if (typeof error === "string") { - msg = error; - } else { - source = error.fileName.substring(error.fileName.lastIndexOf("\\") + 1, error.fileName.length); - msg = "ÿc1[" + source + " :: " + error.lineNumber + "] ÿc0" + error.message; - } - - this.log("[ÿc1Errorÿc0] " + msg); - }; - - /** @type {Map} */ - const timers = new Map(); - - /** - * @param {string} name - */ - console.time = function (name) { - name && timers.set(name, getTickCount()); - }; - - /** - * @param {string} name - */ - console.timeEnd = function (name) { - let currTimer = timers.get(name); - if (currTimer) { - this.log("[ÿc7Timerÿc0] :: ÿc8" + name + " - ÿc4Durationÿc0: " + (getTickCount() - currTimer) + "ms"); - timers.delete(name); - } - }; - - console.trace = function () { - let stackLog = ""; - let stack = new Error().stack; - if (stack) { - stack = stack.split("\n"); - stack && typeof stack === "object" && stack.reverse(); - - for (let i = 0; i < stack.length - 1; 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); - i < stack.length - 1 && (stackLog += ", "); - } - } - - this.log("[ÿc;StackTraceÿc0] :: " + stackLog); - } - }; - - console.info = function (start = false, msg = "", timer = "") { - const stack = new Error().stack.match(/[^\r\n]+/g); - let funcName = stack[1].substr(0, stack[1].indexOf("@")); - let logInfo = start === true - ? "[ÿc2Start " - : start === false - ? "[ÿc1End " - : "[ÿc8"; - logInfo += (funcName + "ÿc0] :: " + (msg ? msg : "")); - if (timer) { - let currTimer = timers.get(timer); - if (currTimer) { - let tFormat = (getTickCount() - currTimer); - // if less than 1 second, display in ms - tFormat > 1000 ? (tFormat = Time.format(tFormat)) : (tFormat += " ms"); - logInfo += (" - ÿc4Durationÿc0: " + tFormat); - timers.delete(timer); - } else { - this.time(timer); - } - } - this.log(logInfo); - }; - - /** - * @param {object | any[]} data - * @param {string[]} [columns] - */ - console.table = function (data, columns) { - if (data === undefined) return; - - let output = ""; - let table = []; - let row = []; - - // Create table headers - if (!columns) { - columns = Object.keys(data[0]); - } - row = columns; - table.push(row); - - // Create table rows - for (let i = 0; i < data.length; i++) { - row = []; - for (let j = 0; j < columns.length; j++) { - row.push(data[i][columns[j]]); - } - table.push(row); - } - - // todo - get longest element and adjust the output of that column to stay within the header bars - let maxLengths = new Array(table[0].length).fill(0); - - for (let i = 0; i < table.length; i++) { - for (let j = 0; j < table[i].length; j++) { - maxLengths[j] = Math.max(maxLengths[j], table[i][j].toString().length); - } - } - console.log(maxLengths); - - // Create table output - for (let i = 0; i < table.length; i++) { - for (let j = 0; j < table[i].length; j++) { - // output += "| " + table[i][j] + " "; - output += "| " + table[i][j].toString().padEnd(maxLengths[j]) + " "; - } - output += "|\n"; - } - - // // Log table to console - console.log(output); - - // for (let i = 0; i < data.length; i++) { - // let row = "|"; - // for (let j = 0; j < data[i].length; j++) { - // row += " " + data[i][j].toString().padEnd(maxLengths[j]) + " |"; - // } - // console.log(row); - // } - }; - - return console; - - })(); + global.console = global.console || (function () { + const console = {}; + + const argMap = (el) => { + switch (typeof el) { + case "undefined": + return "undefined"; + case "boolean": + return el ? "true" : false; + case "function": + return "function"; + case "object": + if (el === null) return "null"; + if (el instanceof Error) { + return JSON.stringify({ + name: (el.name || "Error"), + fileName: (el.fileName || "unknown"), + lineNumber: (el.lineNumber || ":?"), + message: (el.message || ""), + stack: (el.stack || ""), + }); + } + if (el instanceof Map) { + return el.toString(); + } + if (Array.isArray(el)) { + // handle multidimensional arrays + return JSON.stringify( + el.map(inner => Array.isArray(inner) ? inner.map(argMap) : inner) + ); + } + return JSON.stringify(el); + } + return el; + }; + + console.log = function (...args) { + // use call to avoid type errors + print.call(null, args.map(argMap).join(",")); + }; + + console.printDebug = true; + console.debug = function (...args) { + if (console.printDebug) { + const stack = new Error().stack.match(/[^\r\n]+/g); + let filenameAndLine = stack && stack.length && stack[1].substr(stack[1].lastIndexOf("\\") + 1) || "unknown:0"; + filenameAndLine = filenameAndLine.replace(":", " :: "); + this.log("[ÿc:Debugÿc0] ÿc:[" + filenameAndLine + "]ÿc0 " + args.map(argMap).join(",")); + } + }; + + console.warn = function (...args) { + const stack = new Error().stack.match(/[^\r\n]+/g); + let filenameAndLine = stack && stack.length && stack[1].substr(stack[1].lastIndexOf("\\") + 1) || "unknown:0"; + filenameAndLine = filenameAndLine.replace(":", " :: "); + this.log("[ÿc9Warningÿc0] ÿc9[" + filenameAndLine + "]ÿc0 " + args.map(argMap).join(",")); + }; + + console.error = function (error = "") { + let msg, source; + + if (typeof error === "string") { + msg = error; + } else { + source = error.fileName.substring(error.fileName.lastIndexOf("\\") + 1, error.fileName.length); + msg = "ÿc1[" + source + " :: " + error.lineNumber + "] ÿc0" + error.message; + } + + this.log("[ÿc1Errorÿc0] " + msg); + }; + + /** @type {Map} */ + const timers = new Map(); + + /** + * @param {string} name + */ + console.time = function (name) { + name && timers.set(name, getTickCount()); + }; + + /** + * @param {string} name + */ + console.timeEnd = function (name) { + let currTimer = timers.get(name); + if (currTimer) { + this.log("[ÿc7Timerÿc0] :: ÿc8" + name + " - ÿc4Durationÿc0: " + (getTickCount() - currTimer) + "ms"); + timers.delete(name); + } + }; + + console.trace = function () { + let stackLog = ""; + let stack = new Error().stack; + if (stack) { + stack = stack.split("\n"); + stack && typeof stack === "object" && stack.reverse(); + + for (let i = 0; i < stack.length - 1; 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 + ); + i < stack.length - 1 && (stackLog += ", "); + } + } + + this.log("[ÿc;StackTraceÿc0] :: " + stackLog); + } + }; + + console.info = function (start = false, msg = "", timer = "") { + const stack = new Error().stack.match(/[^\r\n]+/g); + let funcName = stack[1].substr(0, stack[1].indexOf("@")); + let logInfo = start === true + ? "[ÿc2Start " + : start === false + ? "[ÿc1End " + : "[ÿc8"; + logInfo += (funcName + "ÿc0] :: " + (msg ? msg : "")); + if (timer) { + let currTimer = timers.get(timer); + if (currTimer) { + let tFormat = (getTickCount() - currTimer); + // if less than 1 second, display in ms + tFormat > 1000 ? (tFormat = Time.format(tFormat)) : (tFormat += " ms"); + logInfo += (" - ÿc4Durationÿc0: " + tFormat); + timers.delete(timer); + } else { + this.time(timer); + } + } + this.log(logInfo); + }; + + /** + * @param {object | any[]} data + * @param {string[]} [columns] + */ + console.table = function (data, columns) { + if (data === undefined) return; + + let output = ""; + let table = []; + let row = []; + + // Create table headers + if (!columns) { + columns = Object.keys(data[0]); + } + row = columns; + table.push(row); + + // Create table rows + for (let i = 0; i < data.length; i++) { + row = []; + for (let j = 0; j < columns.length; j++) { + row.push(data[i][columns[j]]); + } + table.push(row); + } + + // todo - get longest element and adjust the output of that column to stay within the header bars + let maxLengths = new Array(table[0].length).fill(0); + + for (let i = 0; i < table.length; i++) { + for (let j = 0; j < table[i].length; j++) { + maxLengths[j] = Math.max(maxLengths[j], table[i][j].toString().length); + } + } + console.log(maxLengths); + + // Create table output + for (let i = 0; i < table.length; i++) { + for (let j = 0; j < table[i].length; j++) { + // output += "| " + table[i][j] + " "; + output += "| " + table[i][j].toString().padEnd(maxLengths[j]) + " "; + } + output += "|\n"; + } + + // // Log table to console + console.log(output); + + // for (let i = 0; i < data.length; i++) { + // let row = "|"; + // for (let j = 0; j < data[i].length; j++) { + // row += " " + data[i][j].toString().padEnd(maxLengths[j]) + " |"; + // } + // console.log(row); + // } + }; + + return console; + + })(); })([].filter.constructor("return this")(), print); /** @@ -1126,14 +1133,14 @@ Set.prototype.difference = function(setB) { */ if (!Date.prototype.hasOwnProperty("dateStamp")) { - Object.defineProperty(Date.prototype, "dateStamp", { - value: function () { - let month = this.getMonth() + 1; - let day = this.getDate(); - let year = this.getFullYear(); - return "[" + (month < 10 ? "0" + month : month) + "/" + (day < 10 ? "0" + day : day) + "/" + year + "]"; - } - }); + Object.defineProperty(Date.prototype, "dateStamp", { + value: function () { + let month = this.getMonth() + 1; + let day = this.getDate(); + let year = this.getFullYear(); + return "[" + (month < 10 ? "0" + month : month) + "/" + (day < 10 ? "0" + day : day) + "/" + year + "]"; + } + }); } /** @@ -1151,172 +1158,172 @@ if (!Date.prototype.hasOwnProperty("dateStamp")) { if (!global.hasOwnProperty("sdk") && typeof require !== "undefined") { - Object.defineProperty(global, "sdk", { - value: require("../modules/sdk"), - enumerable: true, - }); + Object.defineProperty(global, "sdk", { + value: require("../modules/sdk"), + enumerable: true, + }); } if (!global.hasOwnProperty("includeIfNotIncluded")) { - Object.defineProperty(global, "includeIfNotIncluded", { - /** - * @param {string} file - */ - value: function (file = "") { - if (!isIncluded(file)) { - if (!include(file)) { - console.error("Failed to include " + file); - console.trace(); - return false; - } - } - return true; - }, - }); + Object.defineProperty(global, "includeIfNotIncluded", { + /** + * @param {string} file + */ + value: function (file = "") { + if (!isIncluded(file)) { + if (!include(file)) { + console.error("Failed to include " + file); + console.trace(); + return false; + } + } + return true; + }, + }); } if (!global.hasOwnProperty("includeCoreLibs")) { - Object.defineProperty(global, "includeCoreLibs", { - /** - * @description includes all files from libs/core/ folder - * @param {string[]} ignoreFiles - */ - value: function (obj = { exclude: [] }) { - /** @type {string[]} */ - let files = dopen("libs/core/").getFiles(); - if (!files.length) throw new Error("Failed to find my files"); - if (!files.includes("Pather.js")) { - console.warn("Incorrect Files?", files); - // something went wrong? - while (!files.includes("Pather.js")) { - files = dopen("libs/core/").getFiles(); - delay(50); - } - } - // always include util first - includeIfNotIncluded("core/Util.js"); - files.filter(file => file.endsWith(".js") && !obj.exclude.includes(file) && !file.match("util.js", "gi")) - .forEach(function (x) { - if (!includeIfNotIncluded("core/" + x)) { - throw new Error("Failed to include core/" + x); - } - }); - return true; - }, - }); + Object.defineProperty(global, "includeCoreLibs", { + /** + * @description includes all files from libs/core/ folder + * @param {string[]} ignoreFiles + */ + value: function (obj = { exclude: [] }) { + /** @type {string[]} */ + let files = dopen("libs/core/").getFiles(); + if (!files.length) throw new Error("Failed to find my files"); + if (!files.includes("Pather.js")) { + console.warn("Incorrect Files?", files); + // something went wrong? + while (!files.includes("Pather.js")) { + files = dopen("libs/core/").getFiles(); + delay(50); + } + } + // always include util first + includeIfNotIncluded("core/Util.js"); + files.filter(file => file.endsWith(".js") && !obj.exclude.includes(file) && !file.match("util.js", "gi")) + .forEach(function (x) { + if (!includeIfNotIncluded("core/" + x)) { + throw new Error("Failed to include core/" + x); + } + }); + return true; + }, + }); } if (!global.hasOwnProperty("includeSystemLibs")) { - Object.defineProperty(global, "includeSystemLibs", { - /** - * @description includes system driver files from libs/systems/ folder - */ - value: function () { - include("systems/automule/automule.js"); - include("systems/crafting/CraftingSystem.js"); - include("systems/gambling/Gambling.js"); - include("systems/torch/TorchSystem.js"); - return true; - }, - }); + Object.defineProperty(global, "includeSystemLibs", { + /** + * @description includes system driver files from libs/systems/ folder + */ + value: function () { + include("systems/automule/automule.js"); + include("systems/crafting/CraftingSystem.js"); + include("systems/gambling/Gambling.js"); + include("systems/torch/TorchSystem.js"); + return true; + }, + }); } if (!global.hasOwnProperty("clone")) { - Object.defineProperty(global, "clone", { - /** - * @param {Date | any[] | object} obj - * @returns {ThisParameterType} deep copy of parameter - */ - value: function (obj) { - let copy; - - // Handle the 3 simple types, and null or undefined - if (null === obj || "object" !== typeof obj) { - return obj; - } - - // Handle Date - if (obj instanceof Date) { - copy = new Date(); - copy.setTime(obj.getTime()); - - return copy; - } - - // Handle Array - if (obj instanceof Array) { - copy = []; - - for (let i = 0; i < obj.length; i += 1) { - copy[i] = clone(obj[i]); - } - - return copy; - } - - // Handle Object - if (obj instanceof Object) { - copy = {}; - - for (let attr in obj) { - if (obj.hasOwnProperty(attr)) { - copy[attr] = clone(obj[attr]); - } - } - - return copy; - } - - throw new Error("Unable to copy obj! Its type isn't supported."); - }, - }); + Object.defineProperty(global, "clone", { + /** + * @param {Date | any[] | object} obj + * @returns {ThisParameterType} deep copy of parameter + */ + value: function (obj) { + let copy; + + // Handle the 3 simple types, and null or undefined + if (null === obj || "object" !== typeof obj) { + return obj; + } + + // Handle Date + if (obj instanceof Date) { + copy = new Date(); + copy.setTime(obj.getTime()); + + return copy; + } + + // Handle Array + if (obj instanceof Array) { + copy = []; + + for (let i = 0; i < obj.length; i += 1) { + copy[i] = clone(obj[i]); + } + + return copy; + } + + // Handle Object + if (obj instanceof Object) { + copy = {}; + + for (let attr in obj) { + if (obj.hasOwnProperty(attr)) { + copy[attr] = clone(obj[attr]); + } + } + + return copy; + } + + throw new Error("Unable to copy obj! Its type isn't supported."); + }, + }); } if (!global.hasOwnProperty("copyObj")) { - Object.defineProperty(global, "copyObj", { - /** - * @param {object} from - * @returns {object} deep copy - */ - value: function (from) { - let obj = {}; - - for (let i in from) { - if (from.hasOwnProperty(i)) { - obj[i] = clone(from[i]); - } - } - - return obj; - }, - }); + Object.defineProperty(global, "copyObj", { + /** + * @param {object} from + * @returns {object} deep copy + */ + value: function (from) { + let obj = {}; + + for (let i in from) { + if (from.hasOwnProperty(i)) { + obj[i] = clone(from[i]); + } + } + + return obj; + }, + }); } const Time = { - seconds: function (seconds = 0) { - if (typeof seconds !== "number") return 0; - return (seconds * 1000); - }, - minutes: function (minutes = 0) { - if (typeof minutes !== "number") return 0; - return (minutes * 60000); - }, - format: function (ms = 0) { - return (new Date(ms).toISOString().slice(11, -5)); - }, - toSeconds: function (ms = 0) { - return (ms / 1000); - }, - toMinutes: function (ms = 0) { - return (ms / 60000); - }, - toHours: function (ms = 0) { - return (ms / 3600000); - }, - toDays: function (ms = 0) { - return (ms / 86400000); - }, - elapsed: function (ms = 0) { - return (getTickCount() - ms); - } + seconds: function (seconds = 0) { + if (typeof seconds !== "number") return 0; + return (seconds * 1000); + }, + minutes: function (minutes = 0) { + if (typeof minutes !== "number") return 0; + return (minutes * 60000); + }, + format: function (ms = 0) { + return (new Date(ms).toISOString().slice(11, -5)); + }, + toSeconds: function (ms = 0) { + return (ms / 1000); + }, + toMinutes: function (ms = 0) { + return (ms / 60000); + }, + toHours: function (ms = 0) { + return (ms / 3600000); + }, + toDays: function (ms = 0) { + return (ms / 86400000); + }, + elapsed: function (ms = 0) { + return (getTickCount() - ms); + } }; diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index f57937374..eb1f44d91 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -304,23 +304,27 @@ function LoadConfig() { Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -333,7 +337,8 @@ function LoadConfig() { ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; @@ -359,7 +364,7 @@ function LoadConfig() { Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. Config.TownHP = 0; // Go to town if life is under designated percent. Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Town settings Config.HealHP = 50; // Go to a healer if under designated percent of life. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index bf008739b..d7448e455 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -304,23 +304,27 @@ function LoadConfig() { Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -333,7 +337,8 @@ function LoadConfig() { ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 573dddc11..16ed4c110 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -304,23 +304,27 @@ function LoadConfig() { Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -333,7 +337,8 @@ function LoadConfig() { ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index da851e523..7317ac3b1 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -304,23 +304,27 @@ function LoadConfig() { Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -333,7 +337,8 @@ function LoadConfig() { ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 161fec284..296d04c99 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -304,23 +304,27 @@ function LoadConfig() { Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -333,7 +337,8 @@ function LoadConfig() { ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index b6c9c2598..bf80342d3 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -304,23 +304,27 @@ function LoadConfig() { Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -333,7 +337,8 @@ function LoadConfig() { ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index fb076dcbc..d45461a85 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -304,23 +304,27 @@ function LoadConfig() { Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -333,7 +337,8 @@ function LoadConfig() { ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 1dbb53390..0fe43554e 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -285,23 +285,27 @@ Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js // List of act 1 areas to open chests in Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum ]; // List of act 2 areas to open chests in Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, - sdk.areas.TalRashasTomb3, sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 ]; // List of act 3 areas to open chests in Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 ]; // List of act 4 areas to open chests in Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; // List of act 5 areas to open chests in Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt @@ -314,7 +318,8 @@ ]; // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull ]; @@ -340,7 +345,7 @@ Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. Config.TownHP = 0; // Go to town if life is under designated percent. Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Town settings Config.HealHP = 50; // Go to a healer if under designated percent of life. diff --git a/d2bs/kolbot/libs/config/_CustomConfig.js b/d2bs/kolbot/libs/config/_CustomConfig.js index 78c85f3fc..b18c66200 100644 --- a/d2bs/kolbot/libs/config/_CustomConfig.js +++ b/d2bs/kolbot/libs/config/_CustomConfig.js @@ -1,5 +1,5 @@ const CustomConfig = { - /* Format: + /* Format: "Config_Filename_Without_Extension": ["array", "of", "profiles"] Multiple entries are separated by commas diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 4580749ea..c83ea8e45 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -11,109 +11,109 @@ * @global */ const Attack = { - infinity: false, - auradin: false, - monsterObjects: [ - sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, - sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, sdk.monsters.FireTower, - sdk.monsters.BarricadeDoor1, sdk.monsters.BarricadeDoor2, sdk.monsters.BarricadeWall1, sdk.monsters.BarricadeWall2, - sdk.monsters.CatapultS, sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, sdk.monsters.CatapultW, - sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.Hut, - ], - Result: { - FAILED: 0, - SUCCESS: 1, - CANTATTACK: 2, // need to fix the ambiguity between this result and Failed - NEEDMANA: 3, - NOOP: 4, // used for clearing, if we didn't find any monsters to clear it's not exactly a success or fail - }, - - // Initialize attacks - init: function () { - 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 { - include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".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(); - } - }, - - /** + infinity: false, + auradin: false, + monsterObjects: [ + sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, + sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, sdk.monsters.FireTower, + sdk.monsters.BarricadeDoor1, sdk.monsters.BarricadeDoor2, sdk.monsters.BarricadeWall1, sdk.monsters.BarricadeWall2, + sdk.monsters.CatapultS, sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, sdk.monsters.CatapultW, + sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.Hut, + ], + Result: { + FAILED: 0, + SUCCESS: 1, + CANTATTACK: 2, // need to fix the ambiguity between this result and Failed + NEEDMANA: 3, + NOOP: 4, // used for clearing, if we didn't find any monsters to clear it's not exactly a success or fail + }, + + // Initialize attacks + init: function () { + 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 { + include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".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(); + } + }, + + /** * @description check if slot has items * @param {0 | 1} slot * @returns {boolean} If weapon slot has an item equipped */ - checkSlot: function (slot = me.weaponswitch) { - let item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - if (me.weaponswitch !== slot) { - if (item.bodylocation === sdk.body.RightArmSecondary || item.bodylocation === sdk.body.LeftArmSecondary) { - return true; - } - } else { - if (item.isOnMain) { - return true; - } - } - } while (item.getNext()); - } - - return false; - }, - - /** + checkSlot: function (slot = me.weaponswitch) { + let item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + if (me.weaponswitch !== slot) { + if (item.bodylocation === sdk.body.RightArmSecondary || item.bodylocation === sdk.body.LeftArmSecondary) { + return true; + } + } else { + if (item.isOnMain) { + return true; + } + } + } while (item.getNext()); + } + + return false; + }, + + /** * @description Automatically determine primary weapon slot, Weapon slot with items that isn't a CTA * @returns {0 | 1 | -1} Primary weapon slot */ - getPrimarySlot: function () { - // determine primary slot if not set - if (Config.PrimarySlot === -1) { - if (me.classic) { - Config.PrimarySlot = sdk.player.slot.Main; - } else { - // Always start on main-hand - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - // have cta - if ((Precast.haveCTA > -1) || Precast.checkCTA()) { - // have item on non-cta slot - set non-cta slot as primary - if (this.checkSlot(Precast.haveCTA ^ 1)) { - Config.PrimarySlot = Precast.haveCTA ^ 1; - } else { - // other slot is empty - set cta as primary slot - Config.PrimarySlot = Precast.haveCTA; - } - } else if (!this.checkSlot(sdk.player.slot.Main) && this.checkSlot(sdk.player.slot.Secondary)) { - // only slot II has items - Config.PrimarySlot = sdk.player.slot.Secondary; - } else { - // both slots have items, both are empty, or only slot I has items - Config.PrimarySlot = sdk.player.slot.Main; - } - } - } - - return Config.PrimarySlot; - }, - - /** + getPrimarySlot: function () { + // determine primary slot if not set + if (Config.PrimarySlot === -1) { + if (me.classic) { + Config.PrimarySlot = sdk.player.slot.Main; + } else { + // Always start on main-hand + me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + // have cta + if ((Precast.haveCTA > -1) || Precast.checkCTA()) { + // have item on non-cta slot - set non-cta slot as primary + if (this.checkSlot(Precast.haveCTA ^ 1)) { + Config.PrimarySlot = Precast.haveCTA ^ 1; + } else { + // other slot is empty - set cta as primary slot + Config.PrimarySlot = Precast.haveCTA; + } + } else if (!this.checkSlot(sdk.player.slot.Main) && this.checkSlot(sdk.player.slot.Secondary)) { + // only slot II has items + Config.PrimarySlot = sdk.player.slot.Secondary; + } else { + // both slots have items, both are empty, or only slot I has items + Config.PrimarySlot = sdk.player.slot.Main; + } + } + } + + return Config.PrimarySlot; + }, + + /** * * @param {Monster} unit * @returns {[number, number] | boolean} @@ -121,307 +121,315 @@ const Attack = { * - option for based on spectype * - option for based on enchant/aura */ - getCustomAttack: function (unit) { - // Check if unit got invalidated - if (!unit || !unit.name || !copyUnit(unit).x) return false; + getCustomAttack: function (unit) { + // Check if unit got invalidated + if (!unit || !unit.name || !copyUnit(unit).x) return false; - for (let i in Config.CustomAttack) { - if (Config.CustomAttack.hasOwnProperty(i)) { - // if it contains numbers but is a string, convert to an int - if (typeof i === "string" && i.match(/\d+/g)) { - // @ts-ignore - i = parseInt(i, 10); - } - - switch (typeof i) { - case "string": - if (unit.name.toLowerCase() === i.toLowerCase()) { - return Config.CustomAttack[i]; - } - - break; - case "number": - if (unit.classid === i) { - return Config.CustomAttack[i]; - } - } - } - } - - return false; - }, - - /** + for (let i in Config.CustomAttack) { + if (Config.CustomAttack.hasOwnProperty(i)) { + // if it contains numbers but is a string, convert to an int + if (typeof i === "string" && i.match(/\d+/g)) { + // @ts-ignore + i = parseInt(i, 10); + } + + switch (typeof i) { + case "string": + if (unit.name.toLowerCase() === i.toLowerCase()) { + return Config.CustomAttack[i]; + } + + break; + case "number": + if (unit.classid === i) { + return Config.CustomAttack[i]; + } + } + } + } + + return false; + }, + + /** * @depreciated * @description Get items with charges - isn't used anywhere * @returns {boolean} */ - getCharges: function () { - !Skill.charges && (Skill.charges = []); - - let item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - 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) { - Skill.charges.push({ - unit: copyUnit(item), - gid: item.gid, - 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 { - Skill.charges.push({ - unit: copyUnit(item), - gid: item.gid, - skill: stats[sdk.stats.ChargedSkill].skill, - level: stats[sdk.stats.ChargedSkill].level, - charges: stats[sdk.stats.ChargedSkill].charges, - maxcharges: stats[sdk.stats.ChargedSkill].maxcharges - }); - } - } - } while (item.getNext()); - } - - return true; - }, - - /** + getCharges: function () { + !Skill.charges && (Skill.charges = []); + + let item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + 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) { + Skill.charges.push({ + unit: copyUnit(item), + gid: item.gid, + 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 { + Skill.charges.push({ + unit: copyUnit(item), + gid: item.gid, + skill: stats[sdk.stats.ChargedSkill].skill, + level: stats[sdk.stats.ChargedSkill].level, + charges: stats[sdk.stats.ChargedSkill].charges, + maxcharges: stats[sdk.stats.ChargedSkill].maxcharges + }); + } + } + } while (item.getNext()); + } + + return true; + }, + + /** * @description Check if player or his merc are using Infinity, and adjust resistance checks based on that * @returns {boolean} */ - checkInfinity: function () { - if (me.classic) return false; + checkInfinity: function () { + if (me.classic) return false; - let merc; - // check if we have a merc and they aren't dead - Config.UseMerc && !me.mercrevivecost && (merc = Misc.poll(() => me.getMerc(), 1000, 100)); + let merc; + // check if we have a merc and they aren't dead + Config.UseMerc && !me.mercrevivecost && (merc = Misc.poll(() => me.getMerc(), 1000, 100)); - // Check merc infinity - !!merc && (Attack.infinity = merc.checkItem({ name: sdk.locale.items.Infinity }).have); + // Check merc infinity + !!merc && (Attack.infinity = merc.checkItem({ name: sdk.locale.items.Infinity }).have); - // Check player infinity - only check if merc doesn't have - !Attack.infinity && (Attack.infinity = me.checkItem({ name: sdk.locale.items.Infinity, equipped: true }).have); + // Check player infinity - only check if merc doesn't have + !Attack.infinity && (Attack.infinity = me.checkItem({ name: sdk.locale.items.Infinity, equipped: true }).have); - return Attack.infinity; - }, + return Attack.infinity; + }, - /** + /** * @description Check if player is using Dragon, Dream, HoJ, or Ice, and adjust resistance checks based on that * @returns {boolean} */ - checkAuradin: function () { - Attack.auradin = me.haveSome([ - { name: sdk.locale.items.Dragon, equipped: true }, { name: sdk.locale.items.Dream, equipped: true }, - { name: sdk.locale.items.HandofJustice, equipped: true }, { name: sdk.locale.items.Ice, equipped: true }, - ]); + checkAuradin: function () { + Attack.auradin = me.haveSome([ + { name: sdk.locale.items.Dragon, equipped: true }, { name: sdk.locale.items.Dream, equipped: true }, + { name: sdk.locale.items.HandofJustice, equipped: true }, { name: sdk.locale.items.Ice, equipped: true }, + ]); - return Attack.auradin; - }, + return Attack.auradin; + }, - /** + /** * @description check if we can telestomp a unit * @param {Unit} unit * @returns {boolean} */ - canTeleStomp: function (unit) { - if (!unit || !unit.attackable) return false; - return Config.TeleStomp && Config.UseMerc && Pather.canTeleport() && Attack.checkResist(unit, "physical") && !!me.getMerc() && Attack.validSpot(unit.x, unit.y); - }, + canTeleStomp: function (unit) { + if (!unit || !unit.attackable) return false; + return Config.TeleStomp && Config.UseMerc && Pather.canTeleport() + && Attack.checkResist(unit, "physical") && !!me.getMerc() && Attack.validSpot(unit.x, unit.y); + }, - /** + /** * @description Kill a monster based on its classId, can pass a unit as well * @param {Unit | number} classId * @returns {boolean} If we managed to kill the unit */ - kill: function (classId) { - if (!classId || Config.AttackSkill[1] < 0) return false; - let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); - - if (!target) { - console.warn("Attack.kill: Target not found"); - return Attack.clear(10); - } - - 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; - } - }; - - const who = (!!target.name ? target.name : classId); - const gid = target.gid; - - let retry = 0; - let errorInfo = ""; - let attackCount = 0; - - let lastLoc = { x: me.x, y: me.y }; - let tick = getTickCount(); - console.log("ÿc7Kill ÿc0:: " + who); - Config.MFLeader && Pather.makePortal() && say("kill " + classId); - - while (attackCount < Config.MaxAttackCount && target.attackable && !this.skipCheck(target)) { - Misc.townCheck(); + kill: function (classId) { + if (!classId || Config.AttackSkill[1] < 0) return false; + let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); + + if (!target) { + console.warn("Attack.kill: Target not found"); + return Attack.clear(10); + } + + 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; + } + }; + + const who = (!!target.name ? target.name : classId); + const gid = target.gid; + + let retry = 0; + let errorInfo = ""; + let attackCount = 0; + + let lastLoc = { x: me.x, y: me.y }; + let tick = getTickCount(); + console.log("ÿc7Kill ÿc0:: " + who); + Config.MFLeader && Pather.makePortal() && say("kill " + classId); + + 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); - !target && (target = findTarget(gid, lastLoc)); - - if (!target) { - console.warn("ÿc1Failed to kill " + who + " (couldn't relocate unit)"); - break; - } - } - - // todo - dodge boss missiles - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Config.MFSwitchPercent && target.hpPercent < Config.MFSwitchPercent && me.switchWeapons(this.getPrimarySlot() ^ 1); - - if (attackCount > 0 && attackCount % 15 === 0 && Skill.getRange(Config.AttackSkill[1]) < 4) { - Packet.flash(me.gid); - } - - 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++; - } - - attackCount === Config.MaxAttackCount && (errorInfo = " (attackCount exceeded: " + attackCount + ")"); - Config.MFSwitchPercent && me.switchWeapons(this.getPrimarySlot()); - 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)); - } - - return (!target || !copyUnit(target).x || target.dead || !target.attackable); - }, - - /** + // 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; + } + } + + // todo - dodge boss missiles + Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + Config.MFSwitchPercent && target.hpPercent < Config.MFSwitchPercent && me.switchToPrimary(); + + if (attackCount > 0 && attackCount % 15 === 0 && Skill.getRange(Config.AttackSkill[1]) < 4) { + Packet.flash(me.gid); + } + + 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++; + } + + attackCount === Config.MaxAttackCount && (errorInfo = " (attackCount exceeded: " + attackCount + ")"); + Config.MFSwitchPercent && me.switchWeapons(this.getPrimarySlot()); + 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)); + } + + return (!target || !copyUnit(target).x || target.dead || !target.attackable); + }, + + /** * @description hurt a unit to a certain percentage of life left * @param {string | number | Unit} classId * @param {number} percent * @returns {boolean} */ - hurt: function (classId, percent) { - if (!classId || !percent) return false; - let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); - - if (!target) { - console.warn("Attack.hurt: Target not found"); - return false; - } - - let retry = 0, attackCount = 0; - let tick = getTickCount(); - const who = (!!target.name ? target.name : classId); - - while (attackCount < Config.MaxAttackCount && target.attackable && !Attack.skipCheck(target)) { - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); - - if (result === this.Result.FAILED) { - if (retry++ > 3) { - break; - } - - Packet.flash(me.gid); - } else if (result === this.Result.CANTATTACK) { - break; - } else if (result === this.Result.NEEDMANA) { - continue; - } else { - retry = 0; - } - - if (!copyUnit(target).x) { - return true; - } - - attackCount += 1; - - if (target.hpPercent <= percent) { - console.log("ÿc7Hurt ÿc0:: " + who + "ÿc7HpPercent: ÿc0" + target.hpPercent + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); - break; - } - } - - return true; - }, - - /** + hurt: function (classId, percent) { + if (!classId || !percent) return false; + let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); + + if (!target) { + console.warn("Attack.hurt: Target not found"); + return false; + } + + let retry = 0, attackCount = 0; + let tick = getTickCount(); + const who = (!!target.name ? target.name : classId); + + while (attackCount < Config.MaxAttackCount && target.attackable && !Attack.skipCheck(target)) { + let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + + if (result === this.Result.FAILED) { + if (retry++ > 3) { + break; + } + + Packet.flash(me.gid); + } else if (result === this.Result.CANTATTACK) { + break; + } else if (result === this.Result.NEEDMANA) { + continue; + } else { + retry = 0; + } + + if (!copyUnit(target).x) { + return true; + } + + attackCount += 1; + + if (target.hpPercent <= percent) { + console.log( + "ÿc7Hurt ÿc0:: " + who + "ÿc7HpPercent: ÿc0" + target.hpPercent + + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) + ); + break; + } + } + + return true; + }, + + /** * @description Determine scariness of monster for monster sorting * @param {Unit} unit * @returns {number} scariness */ - getScarinessLevel: function (unit) { - // todo - define summonertype prototype - let scariness = 0; - const ids = [ - 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.Ancient2, sdk.monsters.Ancient3, - sdk.monsters.BaalSubjectMummy, 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.Grotesque2 - ]; - - // Only handling monsters for now - if (!unit || unit.type !== sdk.unittype.Monster) return undefined; - // Minion - (unit.isMinion) && (scariness += 1); - // Champion - (unit.isChampion) && (scariness += 2); - // Boss - (unit.isUnique) && (scariness += 4); - // Summoner or the like - ids.includes(unit.classid) && (scariness += 8); - - return scariness; - }, - - /** + getScarinessLevel: function (unit) { + // todo - define summonertype prototype + let scariness = 0; + const ids = [ + 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.Ancient2, sdk.monsters.Ancient3, + sdk.monsters.BaalSubjectMummy, 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.Grotesque2 + ]; + + // Only handling monsters for now + if (!unit || unit.type !== sdk.unittype.Monster) return undefined; + // Minion + (unit.isMinion) && (scariness += 1); + // Champion + (unit.isChampion) && (scariness += 2); + // Boss + (unit.isUnique) && (scariness += 4); + // Summoner or the like + ids.includes(unit.classid) && (scariness += 8); + + return scariness; + }, + + /** * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster * @param {number} [range=25] * @param {number} [spectype=0] @@ -431,196 +439,209 @@ const Attack = { * @returns {boolean} * @todo change to passing an object */ - clear: function (range, spectype, bossId, sortfunc, pickit = true) { - while (!me.gameReady) { - delay(40); - } - - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) return false; - - range === undefined && (range = 25); - spectype === undefined && (spectype = 0); - bossId === undefined && (bossId = false); - sortfunc === undefined && (sortfunc = false); - !sortfunc && (sortfunc = this.sortMonsters); - - if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); - - let i, boss, orgx, orgy, start, skillCheck; - let gidAttack = []; - let tick = getTickCount(); - let [killedBoss, logged] = [false, false]; - let [retry, attackCount] = [0, 0]; - - 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 }); - Config.MFLeader && !!bossId && Pather.makePortal() && say("clear " + (["number", "string"].includes(typeof bossId) ? bossId : bossId.name)); - } 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 && (Pather.canTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { - start = true; - } - - monsterList.push(copyUnit(target)); - } - } while (target.getNext()); - } - - while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { - if (me.dead) return false; + clear: function (range, spectype, bossId, sortfunc, pickit = true) { + while (!me.gameReady) { + delay(40); + } + + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) return false; + + range === undefined && (range = 25); + spectype === undefined && (spectype = 0); + bossId === undefined && (bossId = false); + sortfunc === undefined && (sortfunc = false); + !sortfunc && (sortfunc = this.sortMonsters); + + if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); + + let i, boss, orgx, orgy, start, skillCheck; + let gidAttack = []; + let tick = getTickCount(); + let [killedBoss, logged] = [false, false]; + let [retry, attackCount] = [0, 0]; + + 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 }); + if (Config.MFLeader && !!bossId && Pather.makePortal()) { + say("clear " + (["number", "string"].includes(typeof bossId) ? bossId : bossId.name)); + } + } 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 + && (Pather.canTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { + start = true; + } + + monsterList.push(copyUnit(target)); + } + } while (target.getNext()); + } + + while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { + if (me.dead) return false; - boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); - monsterList.sort(sortfunc); - // target = copyUnit(monsterList[0]); - target = Game.getMonster(-1, -1, monsterList[0].gid); + boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); + monsterList.sort(sortfunc); + // target = copyUnit(monsterList[0]); + target = Game.getMonster(-1, -1, monsterList[0].gid); - if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range || (this.getScarinessLevel(target) > 7 && target.distance <= range)) + if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range + || (this.getScarinessLevel(target) > 7 && target.distance <= range)) && target.attackable) { - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Misc.townCheck(); - tick = getTickCount(); - - if (!logged && boss && boss.gid === target.gid) { - logged = true; - console.log("ÿc7Clear ÿc0:: " + (!!target.name ? target.name : bossId)); - } - // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); - - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); - - 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; - } - } - - if (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) { - print("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); - monsterList.shift(); - } - - /** + Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + Misc.townCheck(); + tick = getTickCount(); + + if (!logged && boss && boss.gid === target.gid) { + logged = true; + console.log("ÿc7Clear ÿc0:: " + (!!target.name ? target.name : bossId)); + } + // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); + + let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + + 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; + } + } + + if (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; + 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 (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) { + print("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + monsterList.shift(); + } + + /** * @todo allow for more aggressive horking here */ - if (target.dead || Config.FastPick || Config.FastFindItem) { - if (boss && boss.gid === target.gid && target.dead) { - killedBoss = true; - console.log("ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); - } - Config.FastFindItem && pickit && ClassAttack.findItem(); - Pickit.fastPick(); - } - } else { - if (me.inArea(sdk.areas.ChaosSanctuary) && target.classid === sdk.monsters.StormCaster1) { - // probably behind the wall - skip them - monsterList.shift(); - retry = 0; - } - if (retry++ > 3) { - monsterList.shift(); - retry = 0; - } - - Packet.flash(me.gid); - } - } else { - monsterList.shift(); - } - } - - if (attackCount > 0) { - ClassAttack.afterAttack(pickit); - this.openChests(range, orgx, orgy); - pickit && 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 - } - - if (boss && !killedBoss) { - // check if boss corpse is around - 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 true; - }, - - /** + if (target.dead || Config.FastPick || Config.FastFindItem) { + if (boss && boss.gid === target.gid && target.dead) { + killedBoss = true; + console.log( + "ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) + ); + } + Config.FastFindItem && pickit && ClassAttack.findItem(); + Pickit.fastPick(); + } + } else { + if (me.inArea(sdk.areas.ChaosSanctuary) && target.classid === sdk.monsters.StormCaster1) { + // probably behind the wall - skip them + monsterList.shift(); + retry = 0; + } + if (retry++ > 3) { + monsterList.shift(); + retry = 0; + } + + Packet.flash(me.gid); + } + } else { + monsterList.shift(); + } + } + + if (attackCount > 0) { + ClassAttack.afterAttack(pickit); + this.openChests(range, orgx, orgy); + pickit && 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 + } + + if (boss && !killedBoss) { + // check if boss corpse is around + 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 true; + }, + + /** * @description clear all monsters based on classid arguments * @param {...number} ids * @returns {boolean} @@ -628,41 +649,41 @@ const Attack = { * - Should there be a range parameter for this? * - Should we keep track of where we started from? */ - clearClassids: function (...ids) { - // lets keep track of where we started from and move back when done - const { x, y } = me; - let cleared = false; - - for (let i = 0; i < 3; i++) { - let monster = Game.getMonster(); - - if (monster) { - let list = []; - - do { - if (ids.includes(monster.classid) && monster.attackable) { - list.push(copyUnit(monster)); - } - } while (monster.getNext()); - - if (!list.length) { - break; - } - // if we cleared, return to our starting position - if (Attack.clearList(list)) { - Pather.moveTo(x, y); - cleared = true; - } - } else { - // if no monsters were found should that be a pass or fail? - return false; // fail for now - } - } - - return cleared; - }, - - /** + clearClassids: function (...ids) { + // lets keep track of where we started from and move back when done + const { x, y } = me; + let cleared = false; + + for (let i = 0; i < 3; i++) { + let monster = Game.getMonster(); + + if (monster) { + let list = []; + + do { + if (ids.includes(monster.classid) && monster.attackable) { + list.push(copyUnit(monster)); + } + } while (monster.getNext()); + + if (!list.length) { + break; + } + // if we cleared, return to our starting position + if (Attack.clearList(list)) { + Pather.moveTo(x, y); + cleared = true; + } + } else { + // if no monsters were found should that be a pass or fail? + return false; // fail for now + } + } + + return cleared; + }, + + /** * @description Filter monsters based on classId, spectype and range * @param {number} classid * @param {number} spectype @@ -670,173 +691,174 @@ const Attack = { * @param {Unit | {x: number, y: number}} center * @returns {Monster[]} */ - getMob: function (classid, spectype, range, center) { - let monsterList = []; - let monster = Game.getMonster(); + getMob: function (classid, spectype, range, center) { + let monsterList = []; + let monster = Game.getMonster(); - range === undefined && (range = 25); - !center && (center = me); + range === undefined && (range = 25); + !center && (center = me); - switch (typeof classid) { - case "number": - case "string": - monster = Game.getMonster(classid); + switch (typeof classid) { + case "number": + case "string": + monster = Game.getMonster(classid); - if (monster) { - do { - if (getDistance(center.x, center.y, monster.x, monster.y) <= range + if (monster) { + do { + if (getDistance(center.x, center.y, monster.x, monster.y) <= range && (!spectype || (monster.spectype & spectype)) && monster.attackable) { - monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - - break; - case "object": - monster = Game.getMonster(); - - if (monster) { - do { - if (classid.includes(monster.classid) && getDistance(center.x, center.y, monster.x, monster.y) <= range + monsterList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + + break; + case "object": + monster = Game.getMonster(); + + if (monster) { + do { + if (classid.includes(monster.classid) && getDistance(center.x, center.y, monster.x, monster.y) <= range && (!spectype || (monster.spectype & spectype)) && monster.attackable) { - monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } + monsterList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } - break; - } + break; + } - return monsterList; - }, + return monsterList; + }, - /** + /** * @description Clear an already formed array of monstas * @param {Function | Array} mainArg * @param {Function} [sortFunc] * @param {boolean} [refresh] * @returns {boolean} */ - clearList: function (mainArg, sortFunc, refresh) { - /** @type {Monster[]} */ - let monsterList; - /** @type {{ gid: number, attacks: number }[]} */ - let gidAttack = []; - let [retry, attackCount] = [0, 0]; - - switch (typeof mainArg) { - case "function": - monsterList = mainArg.call(this); - - break; - case "object": - monsterList = mainArg.slice(0); - - break; - case "boolean": // false from Attack.getMob() - return false; - default: - throw new Error("clearList: Invalid argument"); - } - - !sortFunc && (sortFunc = this.sortMonsters); - - while (monsterList.length > 0 && attackCount < Config.MaxAttackCount) { - if (me.dead) return false; - - if (refresh && attackCount > 0 && attackCount % refresh === 0) { - monsterList = mainArg.call(); - } - - monsterList.sort(sortFunc); - let target = copyUnit(monsterList[0]); - - if (target.x !== undefined && target.attackable) { - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Misc.townCheck(); - // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); - let i; - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); - - 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; - } - } - - if (i === gidAttack.length) { - gidAttack.push({ gid: target.gid, attacks: 0 }); - } - - gidAttack[i].attacks += 1; - let isSpecial = target.isSpecial; - - // Desync/bad position handler - switch (Config.AttackSkill[isSpecial ? 1 : 3]) { - case sdk.skills.BlessedHammer: - // Tele in random direction with Blessed Hammer - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 5 : 15) === 0) { - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 4); - Pather.moveTo(coord.x, coord.y); - } - - break; - default: - // Flash with melee skills - if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 5 : 15) === 0 && Skill.getRange(Config.AttackSkill[isSpecial ? 1 : 3]) < 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.log("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); - monsterList.shift(); - } - - attackCount += 1; - - if (target.dead || Config.FastPick) { - Pickit.fastPick(); - } - } else { - if (retry++ > 3) { - monsterList.shift(); - retry = 0; - } - - Packet.flash(me.gid); - } - } else { - monsterList.shift(); - } - } - - 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; - }, - - /** + clearList: function (mainArg, sortFunc, refresh) { + /** @type {Monster[]} */ + let monsterList; + /** @type {{ gid: number, attacks: number }[]} */ + let gidAttack = []; + let [retry, attackCount] = [0, 0]; + + switch (typeof mainArg) { + case "function": + monsterList = mainArg.call(this); + + break; + case "object": + monsterList = mainArg.slice(0); + + break; + case "boolean": // false from Attack.getMob() + return false; + default: + throw new Error("clearList: Invalid argument"); + } + + !sortFunc && (sortFunc = this.sortMonsters); + + while (monsterList.length > 0 && attackCount < Config.MaxAttackCount) { + if (me.dead) return false; + + if (refresh && attackCount > 0 && attackCount % refresh === 0) { + monsterList = mainArg.call(); + } + + monsterList.sort(sortFunc); + let target = copyUnit(monsterList[0]); + + if (target.x !== undefined && target.attackable) { + Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + Misc.townCheck(); + // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); + let i; + let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + + 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; + } + } + + if (i === gidAttack.length) { + gidAttack.push({ gid: target.gid, attacks: 0 }); + } + + gidAttack[i].attacks += 1; + let isSpecial = target.isSpecial; + + // Desync/bad position handler + switch (Config.AttackSkill[isSpecial ? 1 : 3]) { + case sdk.skills.BlessedHammer: + // Tele in random direction with Blessed Hammer + if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 5 : 15) === 0) { + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 4); + Pather.moveTo(coord.x, coord.y); + } + + break; + default: + // Flash with melee skills + if (gidAttack[i].attacks > 0 && gidAttack[i].attacks % (isSpecial ? 5 : 15) === 0 + && Skill.getRange(Config.AttackSkill[isSpecial ? 1 : 3]) < 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.log("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + monsterList.shift(); + } + + attackCount += 1; + + if (target.dead || Config.FastPick) { + Pickit.fastPick(); + } + } else { + if (retry++ > 3) { + monsterList.shift(); + retry = 0; + } + + Packet.flash(me.gid); + } + } else { + monsterList.shift(); + } + } + + 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; + }, + + /** * @param {number} x * @param {number} y * @param {number} [range=15] @@ -845,192 +867,203 @@ const Attack = { * @param {boolean} [special=false] * @returns {void} */ - securePosition: function (x, y, range = 15, timer = 3000, skipBlocked = true, special = false) { - let tick; + securePosition: function (x, y, range = 15, timer = 3000, skipBlocked = true, special = false) { + let tick; - (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); - skipBlocked === true && (skipBlocked = sdk.collision.Ranged); + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); + skipBlocked === true && (skipBlocked = sdk.collision.Ranged); - while (true) { - [x, y].distance > 5 && Pather.moveTo(x, y); + while (true) { + [x, y].distance > 5 && Pather.moveTo(x, y); - let monster = Game.getMonster(); - let monList = []; + let monster = Game.getMonster(); + let monList = []; - if (monster) { - do { - if (getDistance(monster, x, y) <= range && monster.attackable && this.canAttack(monster) + if (monster) { + do { + if (getDistance(monster, x, y) <= range && monster.attackable && this.canAttack(monster) && (!skipBlocked || !checkCollision(me, monster, skipBlocked)) && (Pather.canTeleport() || !checkCollision(me, monster, sdk.collision.BlockWall))) { - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - - if (!monList.length) { - !tick && (tick = getTickCount()); - - // only return if it's been safe long enough - if (getTickCount() - tick >= timer) { - return; - } - } else { - this.clearList(monList); - - // reset the timer when there's monsters in range - tick && (tick = false); - } - - if (special) { - if (me.paladin && Skill.canUse(sdk.skills.Redemption) && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { - delay(1000); - } - } - - delay(100); - } - }, - - /** + monList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + + if (!monList.length) { + !tick && (tick = getTickCount()); + + // only return if it's been safe long enough + if (getTickCount() - tick >= timer) { + return; + } + } else { + this.clearList(monList); + + // reset the timer when there's monsters in range + tick && (tick = false); + } + + if (special) { + if (me.paladin && Skill.canUse(sdk.skills.Redemption) + && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { + delay(1000); + } + } + + delay(100); + } + }, + + /** * @description Draw lines around a room on minimap * @param {Room} room * @param {number} color */ - markRoom: function (room, color) { - let arr = []; + markRoom: function (room, color) { + let arr = []; + const [rX, rY] = [room.x * 5, room.y * 5]; - arr.push(new Line(room.x * 5, room.y * 5, room.x * 5, room.y * 5 + room.ysize, color, true)); - arr.push(new Line(room.x * 5, room.y * 5, room.x * 5 + room.xsize, room.y * 5, color, true)); - arr.push(new Line(room.x * 5 + room.xsize, room.y * 5, room.x * 5 + room.xsize, room.y * 5 + room.ysize, color, true)); - arr.push(new Line(room.x * 5, room.y * 5 + room.ysize, room.x * 5 + room.xsize, room.y * 5 + room.ysize, color, true)); - }, + arr.push(new Line(rX, rY, rX, rY + room.ysize, color, true)); + arr.push(new Line(rX, rY, rX + room.xsize, rY, color, true)); + arr.push(new Line(rX + room.xsize, rY, rX + room.xsize, rY + room.ysize, color, true)); + arr.push(new Line(rX, rY + room.ysize, rX + room.xsize, rY + room.ysize, color, true)); + }, - /** + /** * @description Count uniques in current area within getUnit range */ - countUniques: function () { - !Attack.uniques && (Attack.uniques = 0); - !Attack.ignoredGids && (Attack.ignoredGids = []); - - let monster = Game.getMonster(); - - if (monster) { - do { - if ((monster.isSuperUnique) && Attack.ignoredGids.indexOf(monster.gid) === -1) { - Attack.uniques += 1; - Attack.ignoredGids.push(monster.gid); - } - } while (monster.getNext()); - } - }, - - /** + countUniques: function () { + !Attack.uniques && (Attack.uniques = 0); + !Attack.ignoredGids && (Attack.ignoredGids = []); + + let monster = Game.getMonster(); + + if (monster) { + do { + if ((monster.isSuperUnique) && Attack.ignoredGids.indexOf(monster.gid) === -1) { + Attack.uniques += 1; + Attack.ignoredGids.push(monster.gid); + } + } while (monster.getNext()); + } + }, + + /** * @description Store average unique monsters counted in area during run * @param {number} area */ - storeStatistics: function (area) { - !FileTools.exists("statistics.json") && FileAction.write("statistics.json", "{}"); + storeStatistics: function (area) { + !FileTools.exists("statistics.json") && FileAction.write("statistics.json", "{}"); - let obj = JSON.parse(FileAction.read("statistics.json")); + let obj = JSON.parse(FileAction.read("statistics.json")); - if (obj) { - if (obj[area] === undefined) { - obj[area] = { - runs: 0, - averageUniques: 0 - }; - } + if (obj) { + if (obj[area] === undefined) { + obj[area] = { + runs: 0, + averageUniques: 0 + }; + } - obj[area].averageUniques = ((obj[area].averageUniques * obj[area].runs + Attack.uniques) / (obj[area].runs + 1)).toFixed(4); - obj[area].runs += 1; + obj[area].averageUniques = ( + (obj[area].averageUniques * obj[area].runs + Attack.uniques) / (obj[area].runs + 1) + ).toFixed(4); + obj[area].runs += 1; - FileAction.write("statistics.json", JSON.stringify(obj)); - } + FileAction.write("statistics.json", JSON.stringify(obj)); + } - Attack.uniques = 0; - Attack.ignoredGids = []; - }, + Attack.uniques = 0; + Attack.ignoredGids = []; + }, - /** + /** * @description Clear an entire area based on monster spectype * @param {number} spectype * @returns {boolean} */ - clearLevel: function (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; - - console.time("clearLevel"); - console.info(true, getAreaName(me.area)); - - let myRoom, previousArea; - let rooms = []; - const currentArea = getArea().id; - const breakClearLevelCheck = !!(Loader.scriptName() === "MFHelper" && Config.MFHelper.BreakClearLevel && Config.Leader !== ""); - - do { - rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); - } while (room.getNext()); + clearLevel: function (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; + + console.time("clearLevel"); + console.info(true, getAreaName(me.area)); + + let myRoom, previousArea; + let rooms = []; + const currentArea = getArea().id; + const breakClearLevelCheck = !!(Loader.scriptName() === "MFHelper" + && Config.MFHelper.BreakClearLevel && Config.Leader !== ""); + + do { + rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + } while (room.getNext()); - if (Config.MFLeader && rooms.length > 0) { - Pather.makePortal(); - console.log("clearlevel " + getAreaName(currentArea)); - say("clearlevel " + me.area); - } - - while (rooms.length > 0) { - // get the first room + initialize myRoom var - !myRoom && (room = getRoom(me.x, me.y)); - - if (breakClearLevelCheck) { - let leader = Misc.findPlayer(Config.Leader); - - if (leader && leader.area !== me.area && !leader.inTown) { - me.overhead("break the clearing in " + getArea().name); - - return true; - } - } - - 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(); - - let result = Pather.getNearestWalkable(room[0], room[1], 18, 3); - - if (result) { - Pather.moveToEx(result[0], result[1], { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } }); - previousArea = result; - - if (!this.clear(40, spectype)) { - break; - } - } else if (currentArea !== getArea().id) { - // Make sure bot does not get stuck in different area. - Pather.moveToEx(previousArea[0], previousArea[1], { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } }); - } - } - - //this.storeStatistics(getAreaName(me.area)); - console.info(false, getAreaName(currentArea), "clearLevel"); - - return true; - }, - - /** + if (Config.MFLeader && rooms.length > 0) { + Pather.makePortal(); + console.log("clearlevel " + getAreaName(currentArea)); + say("clearlevel " + me.area); + } + + while (rooms.length > 0) { + // get the first room + initialize myRoom var + !myRoom && (room = getRoom(me.x, me.y)); + + if (breakClearLevelCheck) { + let leader = Misc.findPlayer(Config.Leader); + + if (leader && leader.area !== me.area && !leader.inTown) { + me.overhead("break the clearing in " + getArea().name); + + return true; + } + } + + 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(); + + let result = Pather.getNearestWalkable(room[0], room[1], 18, 3); + + if (result) { + Pather.moveToEx( + result[0], result[1], + { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } } + ); + previousArea = result; + + if (!this.clear(40, spectype)) { + break; + } + } else if (currentArea !== getArea().id) { + // Make sure bot does not get stuck in different area. + Pather.moveToEx( + previousArea[0], previousArea[1], + { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } } + ); + } + } + + //this.storeStatistics(getAreaName(me.area)); + console.info(false, getAreaName(currentArea), "clearLevel"); + + return true; + }, + + /** * @description Sort monsters based on distance, spectype and classId (summoners are attacked first) * @param {Unit} unitA * @param {Unit} unitB @@ -1038,73 +1071,84 @@ const Attack = { * @todo Think this needs a collison check included for non tele chars, might prevent choosing * closer mob that is actually behind a wall vs the one we pass trying to get behind the wall */ - sortMonsters: function (unitA, unitB) { - // No special sorting for were-form - if (Config.Wereform) return getDistance(me, unitA) - getDistance(me, unitB); - - // sort main bosses first - // Andy - if (me.inArea(sdk.areas.CatacombsLvl4)) { - if (unitA.distance < 5 && unitA.classid === sdk.monsters.Andariel && !checkCollision(me, unitA, sdk.collision.Ranged)) return -1; - } - - // Meph - if (me.inArea(sdk.areas.DuranceofHateLvl3)) { - if (unitA.distance < 5 && unitA.classid === sdk.monsters.Mephisto && !checkCollision(me, unitA, sdk.collision.Ranged)) return -1; - } - - // Baal - if (me.inArea(sdk.areas.WorldstoneChamber)) { - if (unitA.classid === sdk.monsters.Baal) 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 - ]; - - 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 (Config.BossPriority) { - if ((unitA.isSuperUnique) && (unitB.isSuperUnique)) return getDistance(me, unitA) - getDistance(me, unitB); - - if (unitA.isSuperUnique) return -1; - if (unitB.isSuperUnique) return 1; - } - - return getDistance(me, unitA) - getDistance(me, unitB); - }, - - /** + sortMonsters: function (unitA, unitB) { + // No special sorting for were-form + if (Config.Wereform) return getDistance(me, unitA) - getDistance(me, unitB); + + // sort main bosses first + // Andy + if (me.inArea(sdk.areas.CatacombsLvl4)) { + if (unitA.distance < 5 && unitA.classid === sdk.monsters.Andariel + && !checkCollision(me, unitA, sdk.collision.Ranged)) { + return -1; + } + } + + // Meph + if (me.inArea(sdk.areas.DuranceofHateLvl3)) { + if (unitA.distance < 5 && unitA.classid === sdk.monsters.Mephisto + && !checkCollision(me, unitA, sdk.collision.Ranged)) { + return -1; + } + } + + // Baal + if (me.inArea(sdk.areas.WorldstoneChamber)) { + if (unitA.classid === sdk.monsters.Baal) 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 + ]; + + 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 (Config.BossPriority) { + if ((unitA.isSuperUnique) && (unitB.isSuperUnique)) return getDistance(me, unitA) - getDistance(me, unitB); + + if (unitA.isSuperUnique) return -1; + if (unitB.isSuperUnique) return 1; + } + + return getDistance(me, unitA) - getDistance(me, unitB); + }, + + /** * @description Check if a set of coords is valid/accessable * @param {number} x * @param {number} y @@ -1116,519 +1160,530 @@ const Attack = { * - physical skills can't, need to exclude monster objects though * - splash skills can go through some objects, however some objects are cast blockers */ - validSpot: function (x, y, skill = -1, unitid = 0) { - // Just in case - if (!me.area || !x || !y) return false; - // for now this just returns true and we leave getting into position to the actual class attack files - if (Skill.missileSkills.includes(skill) - || ([sdk.skills.Blizzard, sdk.skills.Meteor].includes(skill) && unitid > 0 && !getBaseStat("monstats", unitid, "flying"))) { - return true; - } - - let result; - let nonFloorAreas = [sdk.areas.ArcaneSanctuary, sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit]; - - // Treat thrown errors as invalid spot - try { - result = getCollision(me.area, x, y); - } catch (e) { - return false; - } - - if (result === undefined) return false; - - switch (true) { - case Skill.needFloor.includes(skill) && nonFloorAreas.includes(me.area): - let isFloor = !!(result & (0 | sdk.collision.IsOnFloor)); - // this spot is not on the floor (lava (river/chaos, space (arcane), ect)) - if (!isFloor) { - return false; - } - - return !(result & sdk.collision.BlockWall); // outside lava area in abaddon returns coll 1 - case Attack.monsterObjects.includes(unitid) && (!!(result & sdk.collision.MonsterIsOnFloor) || !!(result & sdk.collision.MonsterObject)): - // kinda dumb - monster objects have a collision that causes them to not be attacked - // this should fix that - return true; - default: - // Avoid non-walkable spots, objects - this preserves the orignal function and also physical attack skills will get here - if ((result & sdk.collision.BlockWall) || (result & sdk.collision.Objects)) return false; - - break; - } - - return true; - }, - - /** + validSpot: function (x, y, skill = -1, unitid = 0) { + // Just in case + if (!me.area || !x || !y) return false; + // for now this just returns true and we leave getting into position to the actual class attack files + if (Skill.missileSkills.includes(skill) + || ([sdk.skills.Blizzard, sdk.skills.Meteor].includes(skill) + && unitid > 0 && !getBaseStat("monstats", unitid, "flying"))) { + return true; + } + + let result; + let mObject = Attack.monsterObjects.includes(unitid); + let nonFloorAreas = [ + sdk.areas.ArcaneSanctuary, sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary, + sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit + ]; + + // Treat thrown errors as invalid spot + try { + result = getCollision(me.area, x, y); + } catch (e) { + return false; + } + + if (result === undefined) return false; + + switch (true) { + case Skill.needFloor.includes(skill) && nonFloorAreas.includes(me.area): + let isFloor = !!(result & (0 | sdk.collision.IsOnFloor)); + // this spot is not on the floor (lava (river/chaos, space (arcane), ect)) + if (!isFloor) { + return false; + } + + return !(result & sdk.collision.BlockWall); // outside lava area in abaddon returns coll 1 + case (mObject && (!!(result & sdk.collision.MonsterIsOnFloor) || !!(result & sdk.collision.MonsterObject))): + // kinda dumb - monster objects have a collision that causes them to not be attacked + // this should fix that + return true; + default: + // Avoid non-walkable spots, objects - this preserves the orignal function and also physical attack skills will get here + if ((result & sdk.collision.BlockWall) || (result & sdk.collision.Objects)) return false; + + break; + } + + return true; + }, + + /** * @description Open chests when clearing * @param {number} range * @param {number} [x=me.x] * @param {number} [y=me.y] * @returns {boolean} */ - openChests: function (range, x, y) { - if (!Config.OpenChests.Enabled) return false; - (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); - range === undefined && (range = 10); - - let list = []; - let ids = ["chest", "chest3", "weaponrack", "armorstand"]; - let unit = Game.getObject(); - - if (unit) { - do { - if (unit.name && getDistance(unit, x, y) <= range && ids.includes(unit.name.toLowerCase())) { - list.push(copyUnit(unit)); - } - } while (unit.getNext()); - } - - while (list.length) { - list.sort(Sort.units); - - if (Misc.openChest(list.shift())) { - Pickit.pickItems(); - } - } - - return true; - }, - - /** + openChests: function (range, x, y) { + if (!Config.OpenChests.Enabled) return false; + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); + range === undefined && (range = 10); + + let list = []; + let ids = ["chest", "chest3", "weaponrack", "armorstand"]; + let unit = Game.getObject(); + + if (unit) { + do { + if (unit.name && getDistance(unit, x, y) <= range && ids.includes(unit.name.toLowerCase())) { + list.push(copyUnit(unit)); + } + } while (unit.getNext()); + } + + while (list.length) { + list.sort(Sort.units); + + if (Misc.openChest(list.shift())) { + Pickit.pickItems(); + } + } + + return true; + }, + + /** * @description build list of attackable monsters currently around us * @param {function(): boolean} check - callback function to build list * @returns {Array | []} */ - buildMonsterList: function (check = () => true) { - let monList = []; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable && check(monster)) { - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - - return monList; - }, - - /** + buildMonsterList: function (check = () => true) { + let monList = []; + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable && check(monster)) { + monList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + + return monList; + }, + + /** * @param {Unit} unit * @param {number} distance * @param {number} spread * @param {number} range * @returns {{x: number, y: number}} x/y coords of safe spot */ - findSafeSpot: function (unit, distance, spread, range) { - if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); - - let index; - let monList = []; - let count = 999; - - monList = this.buildMonsterList(); - monList.sort(Sort.units); - 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); - - if (!grid.length) return false; - grid.sort((a, b) => 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)) { - let currCount = this.getMonsterCount(grid[i].x, grid[i].y, range, monList); - - if (currCount < count) { - index = i; - count = currCount; - } - - if (currCount === 0) { - break; - } - } - } - - if (typeof index === "number") { - return { - x: grid[index].x, - y: grid[index].y, - }; - } - - return false; - }, - - deploy: function (unit, distance, spread, range) { - if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); - - let safeLoc = this.findSafeSpot(unit, distance, spread, range); - - return (typeof safeLoc === "object" ? Pather.moveToUnit(safeLoc, 0) : false); - }, - - getMonsterCount: function (x, y, range, list) { - let count = 0; - let ignored = [sdk.monsters.Diablo]; // why is diablo ignored? - - for (let i = 0; i < list.length; i += 1) { - if (ignored.indexOf(list[i].classid) === -1 && list[i].attackable && getDistance(x, y, list[i].x, list[i].y) <= range) { - count += 1; - } - } - - // missile check? - let fire = Game.getObject("fire"); - - if (fire) { - do { - if (getDistance(x, y, fire.x, fire.y) <= 4) { - count += 100; - } - } while (fire.getNext()); - } - - return count; - }, - - buildGrid: function (xmin, xmax, ymin, ymax, spread) { - if (xmin >= xmax || ymin >= ymax || spread < 1) { - throw new Error("buildGrid: Bad parameters"); - } - - let grid = []; - - for (let i = xmin; i <= xmax; i += spread) { - for (let j = ymin; j <= ymax; j += spread) { - let coll = CollMap.getColl(i, j, true); - - if (typeof coll === "number") { - grid.push({ x: i, y: j, coll: coll }); - } - } - } - - return grid; - }, - - /** + findSafeSpot: function (unit, distance, spread, range) { + if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); + + let index; + let monList = []; + let count = 999; + + monList = this.buildMonsterList(); + monList.sort(Sort.units); + 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); + + if (!grid.length) return false; + grid.sort((a, b) => 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)) { + let currCount = this.getMonsterCount(grid[i].x, grid[i].y, range, monList); + + if (currCount < count) { + index = i; + count = currCount; + } + + if (currCount === 0) { + break; + } + } + } + + if (typeof index === "number") { + return { + x: grid[index].x, + y: grid[index].y, + }; + } + + return false; + }, + + deploy: function (unit, distance, spread, range) { + if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); + + let safeLoc = this.findSafeSpot(unit, distance, spread, range); + + return (typeof safeLoc === "object" ? Pather.moveToUnit(safeLoc, 0) : false); + }, + + getMonsterCount: function (x, y, range, list) { + let count = 0; + let ignored = [sdk.monsters.Diablo]; // why is diablo ignored? + + for (let i = 0; i < list.length; i += 1) { + if (ignored.indexOf(list[i].classid) === -1 && list[i].attackable + && getDistance(x, y, list[i].x, list[i].y) <= range) { + count += 1; + } + } + + // missile check? + let fire = Game.getObject("fire"); + + if (fire) { + do { + if (getDistance(x, y, fire.x, fire.y) <= 4) { + count += 100; + } + } while (fire.getNext()); + } + + return count; + }, + + buildGrid: function (xmin, xmax, ymin, ymax, spread) { + if (xmin >= xmax || ymin >= ymax || spread < 1) { + throw new Error("buildGrid: Bad parameters"); + } + + let grid = []; + + for (let i = xmin; i <= xmax; i += spread) { + for (let j = ymin; j <= ymax; j += spread) { + let coll = CollMap.getColl(i, j, true); + + if (typeof coll === "number") { + grid.push({ x: i, y: j, coll: coll }); + } + } + } + + return grid; + }, + + /** * @description checks if we should skip a monster * @param {Unit} unit * @returns {Boolean} If we should skip this monster */ - skipCheck: function (unit) { - if (me.inArea(sdk.areas.ThroneofDestruction)) return false; - if (unit.isSpecial && Config.SkipException && Config.SkipException.includes(unit.name)) { - console.log("ÿc1Skip Exception: " + unit.name); - return false; - } - - if (Config.SkipId.includes(unit.classid)) return true; - - let tempArray = []; - - // EnchantLoop: // Skip enchanted monsters - for (let i = 0; i < Config.SkipEnchant.length; i += 1) { - tempArray = Config.SkipEnchant[i].toLowerCase().split(" and "); - - for (let j = 0; j < tempArray.length; j += 1) { - switch (tempArray[j]) { - case "extra strong": - tempArray[j] = sdk.enchant.ExtraStrong; - - break; - case "extra fast": - tempArray[j] = sdk.enchant.ExtraFast; - - break; - case "cursed": - tempArray[j] = sdk.enchant.Cursed; - - break; - case "magic resistant": - tempArray[j] = sdk.enchant.MagicResistant; - - break; - case "fire enchanted": - tempArray[j] = sdk.enchant.FireEnchanted; - - break; - case "lightning enchanted": - tempArray[j] = sdk.enchant.LightningEnchanted; - - break; - case "cold enchanted": - tempArray[j] = sdk.enchant.ColdEnchanted; - - break; - case "mana burn": - tempArray[j] = sdk.enchant.ManaBurn; - - break; - case "teleportation": - tempArray[j] = sdk.enchant.Teleportation; - - break; - case "spectral hit": - tempArray[j] = sdk.enchant.SpectralHit; - - break; - case "stone skin": - tempArray[j] = sdk.enchant.StoneSkin; - - break; - case "multiple shots": - tempArray[j] = sdk.enchant.MultipleShots; - - break; - } - } - - if (tempArray.every(enchant => unit.getEnchant(enchant))) { - return true; - } - } - - // ImmuneLoop: // Skip immune monsters - for (let i = 0; i < Config.SkipImmune.length; i += 1) { - tempArray = Config.SkipImmune[i].toLowerCase().split(" and "); - - // Infinity calculations are built-in - if (tempArray.every(immnue => !Attack.checkResist(unit, immnue))) { - return true; - } - } - - // AuraLoop: // Skip monsters with auras - for (let i = 0; i < Config.SkipAura.length; i += 1) { - let aura = Config.SkipAura[i].toLowerCase(); - - switch (true) { - case aura === "might" && unit.getState(sdk.states.Might): - case aura === "blessed aim" && unit.getState(sdk.states.BlessedAim): - case aura === "fanaticism" && unit.getState(sdk.states.Fanaticism): - case aura === "conviction" && unit.getState(sdk.states.Conviction): - case aura === "holy fire" && unit.getState(sdk.states.HolyFire): - case aura === "holy freeze" && unit.getState(sdk.states.HolyFreeze): - case aura === "holy shock" && unit.getState(sdk.states.HolyShock): - return true; - default: - break; - } - } - - return false; - }, - - /** + skipCheck: function (unit) { + if (me.inArea(sdk.areas.ThroneofDestruction)) return false; + if (unit.isSpecial && Config.SkipException && Config.SkipException.includes(unit.name)) { + console.log("ÿc1Skip Exception: " + unit.name); + return false; + } + + if (Config.SkipId.includes(unit.classid)) return true; + + let tempArray = []; + + // EnchantLoop: // Skip enchanted monsters + for (let i = 0; i < Config.SkipEnchant.length; i += 1) { + tempArray = Config.SkipEnchant[i].toLowerCase().split(" and "); + + for (let j = 0; j < tempArray.length; j += 1) { + switch (tempArray[j]) { + case "extra strong": + tempArray[j] = sdk.enchant.ExtraStrong; + + break; + case "extra fast": + tempArray[j] = sdk.enchant.ExtraFast; + + break; + case "cursed": + tempArray[j] = sdk.enchant.Cursed; + + break; + case "magic resistant": + tempArray[j] = sdk.enchant.MagicResistant; + + break; + case "fire enchanted": + tempArray[j] = sdk.enchant.FireEnchanted; + + break; + case "lightning enchanted": + tempArray[j] = sdk.enchant.LightningEnchanted; + + break; + case "cold enchanted": + tempArray[j] = sdk.enchant.ColdEnchanted; + + break; + case "mana burn": + tempArray[j] = sdk.enchant.ManaBurn; + + break; + case "teleportation": + tempArray[j] = sdk.enchant.Teleportation; + + break; + case "spectral hit": + tempArray[j] = sdk.enchant.SpectralHit; + + break; + case "stone skin": + tempArray[j] = sdk.enchant.StoneSkin; + + break; + case "multiple shots": + tempArray[j] = sdk.enchant.MultipleShots; + + break; + } + } + + if (tempArray.every(enchant => unit.getEnchant(enchant))) { + return true; + } + } + + // ImmuneLoop: // Skip immune monsters + for (let i = 0; i < Config.SkipImmune.length; i += 1) { + tempArray = Config.SkipImmune[i].toLowerCase().split(" and "); + + // Infinity calculations are built-in + if (tempArray.every(immnue => !Attack.checkResist(unit, immnue))) { + return true; + } + } + + // AuraLoop: // Skip monsters with auras + for (let i = 0; i < Config.SkipAura.length; i += 1) { + let aura = Config.SkipAura[i].toLowerCase(); + + switch (true) { + case aura === "might" && unit.getState(sdk.states.Might): + case aura === "blessed aim" && unit.getState(sdk.states.BlessedAim): + case aura === "fanaticism" && unit.getState(sdk.states.Fanaticism): + case aura === "conviction" && unit.getState(sdk.states.Conviction): + case aura === "holy fire" && unit.getState(sdk.states.HolyFire): + case aura === "holy freeze" && unit.getState(sdk.states.HolyFreeze): + case aura === "holy shock" && unit.getState(sdk.states.HolyShock): + return true; + default: + break; + } + } + + return false; + }, + + /** * @description Get element by skill number * @param {number} skillId * @returns {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none" | false} */ - getSkillElement: function (skillId) { - let elements = ["physical", "fire", "lightning", "magic", "cold", "poison", "none"]; - - switch (skillId) { - case sdk.skills.HolyFire: - return "fire"; - case sdk.skills.HolyFreeze: - return "cold"; - case sdk.skills.HolyShock: - return "lightning"; - case sdk.skills.CorpseExplosion: - case sdk.skills.Stun: - case sdk.skills.Concentrate: - case sdk.skills.Frenzy: - case sdk.skills.MindBlast: - case sdk.skills.Summoner: - return "physical"; - case sdk.skills.HolyBolt: - // no need to use this.elements array because it returns before going over the array - return "holybolt"; - } - - let eType = getBaseStat("skills", skillId, "etype"); - - return typeof (eType) === "number" ? elements[eType] : false; - }, - - /** + getSkillElement: function (skillId) { + let elements = ["physical", "fire", "lightning", "magic", "cold", "poison", "none"]; + + switch (skillId) { + case sdk.skills.HolyFire: + return "fire"; + case sdk.skills.HolyFreeze: + return "cold"; + case sdk.skills.HolyShock: + return "lightning"; + case sdk.skills.CorpseExplosion: + case sdk.skills.Stun: + case sdk.skills.Concentrate: + case sdk.skills.Frenzy: + case sdk.skills.MindBlast: + case sdk.skills.Summoner: + return "physical"; + case sdk.skills.HolyBolt: + // no need to use this.elements array because it returns before going over the array + return "holybolt"; + } + + let eType = getBaseStat("skills", skillId, "etype"); + + return typeof (eType) === "number" ? elements[eType] : false; + }, + + /** * @description Get a monster's resistance to specified element * @param {Unit | Monster} unit * @param {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"} type * @returns {number} */ - getResist: function (unit, type) { - // some scripts pass empty units in throne room - if (!unit || !unit.getStat) return 100; - if (unit.isPlayer) return 0; - - switch (type) { - case "physical": - return unit.getStat(sdk.stats.DamageResist); - case "fire": - return unit.getStat(sdk.stats.FireResist); - case "lightning": - return unit.getStat(sdk.stats.LightningResist); - case "magic": - return unit.getStat(sdk.stats.MagicResist); - case "cold": - return unit.getStat(sdk.stats.ColdResist); - case "poison": - return unit.getStat(sdk.stats.PoisonResist); - case "none": - return 0; - case "holybolt": // check if a monster is undead - if (getBaseStat("monstats", unit.classid, "lUndead") || getBaseStat("monstats", unit.classid, "hUndead")) { - return 0; - } - // eslint-disable-next-line no-fallthrough - default: - return 100; - } - }, - - getLowerResistPercent: function () { - const calc = (level) => Math.floor(Math.min(25 + (45 * ((110 * level) / (level + 6)) / 100), 70)); - if (Skill.canUse(sdk.skills.LowerResist)) { - return calc(me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)); - } - return 0; - }, - - getConvictionPercent: function () { - const calc = (level) => Math.floor(Math.min(25 + (5 * level), 150)); - if (me.expansion && this.checkInfinity()) { - return calc(12); - } - if (Skill.canUse(sdk.skills.Conviction)) { - return calc(me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.SoftPoints)); - } - return 0; - }, - - // Check if a monster is immune to specified attack type - checkResist: function (unit, val, maxres = 100) { - if (!unit || !unit.type || unit.isPlayer) return true; - - const damageType = typeof val === "number" ? this.getSkillElement(val) : val; - const addLowerRes = !!(Skill.canUse(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)) { - 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) { - let valid = false; - - // 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); - } - - // check unit's fire resistance - if (me.getState(sdk.states.HolyFire)) { - valid = this.getResist(unit, "fire") < maxres; - } - - // check unit's light resistance but only if the above check failed - if (me.getState(sdk.states.HolyShock) && !valid) { - valid = this.getResist(unit, "lightning") < maxres; - } - - // check unit's cold resistance but only if the above checks failed - we might be using an Ice Bow - if (me.getState(sdk.states.HolyFreeze) && !valid) { - valid = this.getResist(unit, "cold") < 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)) { - return (this.getResist(unit, damageType) - (Math.floor(lowerResPercent / 5)) < 100); - } - } - - return this.getResist(unit, damageType) < maxres; - }, - - /** + getResist: function (unit, type) { + // some scripts pass empty units in throne room + if (!unit || !unit.getStat) return 100; + if (unit.isPlayer) return 0; + + switch (type) { + case "physical": + return unit.getStat(sdk.stats.DamageResist); + case "fire": + return unit.getStat(sdk.stats.FireResist); + case "lightning": + return unit.getStat(sdk.stats.LightningResist); + case "magic": + return unit.getStat(sdk.stats.MagicResist); + case "cold": + return unit.getStat(sdk.stats.ColdResist); + case "poison": + return unit.getStat(sdk.stats.PoisonResist); + case "none": + return 0; + case "holybolt": // check if a monster is undead + if (getBaseStat("monstats", unit.classid, "lUndead") || getBaseStat("monstats", unit.classid, "hUndead")) { + return 0; + } + // eslint-disable-next-line no-fallthrough + default: + return 100; + } + }, + + getLowerResistPercent: function () { + const calc = (level) => Math.floor(Math.min(25 + (45 * ((110 * level) / (level + 6)) / 100), 70)); + if (Skill.canUse(sdk.skills.LowerResist)) { + return calc(me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)); + } + return 0; + }, + + getConvictionPercent: function () { + const calc = (level) => Math.floor(Math.min(25 + (5 * level), 150)); + if (me.expansion && this.checkInfinity()) { + return calc(12); + } + if (Skill.canUse(sdk.skills.Conviction)) { + return calc(me.getSkill(sdk.skills.Conviction, sdk.skills.subindex.SoftPoints)); + } + return 0; + }, + + // Check if a monster is immune to specified attack type + checkResist: function (unit, val, maxres = 100) { + if (!unit || !unit.type || unit.isPlayer) return true; + + const damageType = typeof val === "number" ? this.getSkillElement(val) : val; + const addLowerRes = !!(Skill.canUse(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)) { + 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) { + let valid = false; + + // 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); + } + + // check unit's fire resistance + if (me.getState(sdk.states.HolyFire)) { + valid = this.getResist(unit, "fire") < maxres; + } + + // check unit's light resistance but only if the above check failed + if (me.getState(sdk.states.HolyShock) && !valid) { + valid = this.getResist(unit, "lightning") < maxres; + } + + // check unit's cold resistance but only if the above checks failed - we might be using an Ice Bow + if (me.getState(sdk.states.HolyFreeze) && !valid) { + valid = this.getResist(unit, "cold") < 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)) { + return (this.getResist(unit, damageType) - (Math.floor(lowerResPercent / 5)) < 100); + } + } + + return this.getResist(unit, damageType) < maxres; + }, + + /** * Check if we have valid skills to attack a monster * @param {Monster} unit * @returns {boolean} */ - 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]))) { - 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; - }, - - /** + 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]))) { + 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; + }, + + /** * Detect use of bows/crossbows * @returns {string | false} */ - usingBow: function () { - let item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - if (item.isOnMain) { - switch (item.itemType) { - case sdk.items.type.Bow: - case sdk.items.type.AmazonBow: - return "bow"; - case sdk.items.type.Crossbow: - return "crossbow"; - } - } - } while (item.getNext()); - } - - return false; - }, - - /** + usingBow: function () { + let item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + if (item.isOnMain) { + switch (item.itemType) { + case sdk.items.type.Bow: + case sdk.items.type.AmazonBow: + return "bow"; + case sdk.items.type.Crossbow: + return "crossbow"; + } + } + } while (item.getNext()); + } + + return false; + }, + + /** * Find an optimal attack position and move or walk to it * @param {Unit} unit * @param {number} distance @@ -1637,177 +1692,179 @@ const Attack = { * @param {boolean} force * @returns {boolean} sucessfully found and moved into position */ - getIntoPosition: function (unit, distance, coll, walk, force) { - if (!unit || !unit.x || !unit.y) return false; + getIntoPosition: function (unit, distance, coll, walk, force) { + if (!unit || !unit.x || !unit.y) return false; - walk === true && (walk = 1); + walk === true && (walk = 1); - force && console.debug("Forcing new position"); + force && console.debug("Forcing new position"); - /** + /** * @todo If we've disabled tele for walking clear, allow use of tele specifically for repositioning */ - if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { - //me.overhead("Short range"); - - if (walk) { - if (unit.distance > 8 || checkCollision(me, unit, coll)) { - Pather.walkTo(unit.x, unit.y, 3); - } - } else { - Pather.moveTo(unit.x, unit.y, 0); - } - - return !CollMap.checkColl(me, unit, coll); - } - - let coords = []; - let fullDistance = distance; - let name = unit.hasOwnProperty("name") ? unit.name : ""; - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 135, -135, 180]; - const { x: orgX, y: orgY } = me; - - //let t = getTickCount(); - - for (let n = 0; n < 3; n += 1) { - n > 0 && (distance -= Math.floor(fullDistance / 3 - 1)); - - for (let i = 0; i < angles.length; i += 1) { - let cx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * distance + unit.x); - let cy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * 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 }); - } - } - - // print("ÿc9potential spots: ÿc2" + coords.length); - - if (coords.length > 0) { - coords.sort(Sort.units); - - for (let i = 0; i < coords.length; i += 1) { - // Valid position found - if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { - // print("ÿc9optimal pos build time: ÿc2" + (getTickCount() - t) + " ÿc9distance from target: ÿc2" + getDistance(cx, cy, unit.x, unit.y)); - if ((() => { - switch (walk) { - case 1: - return Pather.walkTo(coords[i].x, coords[i].y, 2); - case 2: - if (coords[i].distance < 6 && !CollMap.checkColl(me, coords[i], sdk.collision.WallOrRanged)) { - return Pather.walkTo(coords[i].x, coords[i].y, 2); - } else { - return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); - } - default: - return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); - } - })()) { - if (Config.DebugMode.Path && force) { - console.debug("Sucessfully got into position. orginal Loc: " + orgX + "/" + orgY + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance); - } - return true; - } - } - } - } - } - - !!name && print("ÿc4Attackÿc0: No valid positions for: " + name); - - return false; - }, - - /** + if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { + if (walk) { + if (unit.distance > 8 || checkCollision(me, unit, coll)) { + Pather.walkTo(unit.x, unit.y, 3); + } + } else { + Pather.moveTo(unit.x, unit.y, 0); + } + + return !CollMap.checkColl(me, unit, coll); + } + + 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]; + const { x: orgX, y: orgY } = me; + + for (let n = 0; n < 3; n++) { + const coords = []; + n > 0 && (distance -= Math.floor(fullDistance / 3 - 1)); + + 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; + + coords.sort(Sort.units); + + for (let i = 0; i < coords.length; i += 1) { + // Valid position found + if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { + if ((() => { + switch (walk) { + case 1: + return Pather.walkTo(coords[i].x, coords[i].y, 2); + case 2: + if (coords[i].distance < 6 && !CollMap.checkColl(me, coords[i], sdk.collision.WallOrRanged)) { + return Pather.walkTo(coords[i].x, coords[i].y, 2); + } else { + return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); + } + default: + return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); + } + })()) { + if (Config.DebugMode.Path && force) { + console.debug( + "Sucessfully got into position. orginal Loc: " + orgX + "/" + orgY + + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance + ); + } + return true; + } + } + } + } + + !!name && print("ÿc4Attackÿc0: No valid positions for: " + name); + + return false; + }, + + /** * Find the nearest monster to us with optional exception parameters * @param {{ skipBlocked?: boolean, skipImmune?: boolean, skipGid?: number}} givenSettings * @returns {Monster | false} */ - getNearestMonster: function (givenSettings = {}) { - const settings = Object.assign({}, { - skipBlocked: true, - skipImmune: true, - skipGid: -1, - }, givenSettings); - - let gid; - let monster = Game.getMonster(); - let range = 30; - - if (monster) { - do { - if (monster.attackable && !monster.getParent()) { - let distance = getDistance(me, monster); - - if (distance < range + getNearestMonster: function (givenSettings = {}) { + const settings = Object.assign({}, { + skipBlocked: true, + skipImmune: true, + skipGid: -1, + }, givenSettings); + + let gid; + let monster = Game.getMonster(); + let range = 30; + + if (monster) { + do { + if (monster.attackable && !monster.getParent()) { + let distance = getDistance(me, monster); + + if (distance < range && (settings.skipGid === -1 || monster.gid !== settings.skipGid) && (!settings.skipBlocked || !checkCollision(me, monster, sdk.collision.WallOrRanged)) && (!settings.skipImmune || Attack.canAttack(monster))) { - range = distance; - gid = monster.gid; - } - } - } while (monster.getNext()); - } + range = distance; + gid = monster.gid; + } + } + } while (monster.getNext()); + } - return !!gid ? Game.getMonster(-1, -1, gid) : false; - }, + return !!gid ? Game.getMonster(-1, -1, gid) : false; + }, - /** + /** * Check valid corpse for Redemption/Horking/Summoning * @param {Monster} unit * @returns {boolean} valid corpse */ - checkCorpse: function (unit) { - if (!unit || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) return false; - if (unit.classid <= sdk.monsters.BurningDeadArcher2 && !getBaseStat("monstats2", unit.classid, "corpseSel")) return false; - return ([ - sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, - sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect - ].every(state => !unit.getState(state))); - }, - - /** + checkCorpse: function (unit) { + if (!unit || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) return false; + if (unit.classid <= sdk.monsters.BurningDeadArcher2 && !getBaseStat("monstats2", unit.classid, "corpseSel")) { + return false; + } + return ([ + sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, + sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect + ].every(state => !unit.getState(state))); + }, + + /** * Get valid corpses for Redemption/Horking/Summoning * @param {Monster} unit * @param {number} range * @returns {Monster[]} */ - checkNearCorpses: function (unit, range = 15) { - let corpses = getUnits(sdk.unittype.Monster).filter(function (corpse) { - return getDistance(corpse, unit) <= range && Attack.checkCorpse(corpse); - }); - return corpses.length > 0 ? corpses : []; - }, - - /** + checkNearCorpses: function (unit, range = 15) { + let corpses = getUnits(sdk.unittype.Monster).filter(function (corpse) { + return getDistance(corpse, unit) <= range && Attack.checkCorpse(corpse); + }); + return corpses.length > 0 ? corpses : []; + }, + + /** * @param {Monster} unit * @returns {boolean} */ - whirlwind: function (unit) { - if (!unit.attackable) return true; + whirlwind: function (unit) { + if (!unit.attackable) return true; - let angles = [180, 175, -175, 170, -170, 165, -165, 150, -150, 135, -135, 45, -45, 90, -90]; + let angles = [180, 175, -175, 170, -170, 165, -165, 150, -150, 135, -135, 45, -45, 90, -90]; - unit.isSpecial && angles.unshift(120); + unit.isSpecial && angles.unshift(120); - me.runwalk = me.gametype; - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); + me.runwalk = me.gametype; + let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - // get a better spot - for (let i = 0; i < angles.length; i += 1) { - let coords = [Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * 4 + unit.x), Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * 4 + unit.y)]; + // get a better spot + for (let i = 0; i < angles.length; i += 1) { + let coords = [ + Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * 4 + unit.x), + Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * 4 + unit.y) + ]; - if (!CollMap.checkColl(me, { x: coords[0], y: coords[1] }, sdk.collision.BlockWall, 1)) { - return Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, coords[0], coords[1]); - } - } + if (!CollMap.checkColl(me, { x: coords[0], y: coords[1] }, sdk.collision.BlockWall, 1)) { + return Skill.cast(sdk.skills.Whirlwind, sdk.skills.hand.Right, coords[0], coords[1]); + } + } - return (Attack.validSpot(unit.x, unit.y) && Skill.cast(sdk.skills.Whirlwind, Skill.getHand(sdk.skills.Whirlwind), me.x, me.y)); - } + return (Attack.validSpot(unit.x, unit.y) + && Skill.cast(sdk.skills.Whirlwind, Skill.getHand(sdk.skills.Whirlwind), me.x, me.y)); + } }; diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 7d5511095..6768d9631 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -6,240 +6,262 @@ */ const ClassAttack = { - bowCheck: false, - lightFuryTick: 0, - - decideSkill: function (unit) { - let skills = { timed: -1, untimed: -1 }; - if (!unit) return skills; - - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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]; - } - - // 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]; - } - - // 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]; - } - - return skills; - }, - - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - let needRepair = Town.needRepair(); - - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { - print("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 (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; - } - - if (Skill.canUse(sdk.skills.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); - } - } - - if (Skill.canUse(sdk.skills.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 (!unit.getState(sdk.states.InnerSight) && Config.UseInnerSight && Skill.canUse(sdk.skills.InnerSight)) { - Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); - } - } else { - Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); - } - } - } - } - - let mercRevive = 0; - 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 (Misc.townCheck()) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - } - - if (!unit) return Attack.Result.SUCCESS; - - if (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({skipGid: gid}); + bowCheck: false, + lightFuryTick: 0, + + decideSkill: function (unit) { + let skills = { timed: -1, untimed: -1 }; + if (!unit) return skills; + + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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]; + } + + // 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]; + } + + // 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]; + } + + return skills; + }, + + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + let needRepair = Town.needRepair(); + + if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + print("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 (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; + } + + if (Skill.canUse(sdk.skills.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); + } + } + + if (Skill.canUse(sdk.skills.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 (!unit.getState(sdk.states.InnerSight) && Config.UseInnerSight && Skill.canUse(sdk.skills.InnerSight)) { + Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); + } + } else { + Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); + } + } + } + } + + let mercRevive = 0; + 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 (Misc.townCheck()) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + } + } + + if (!unit) return Attack.Result.SUCCESS; + + if (Town.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 && !!spot.x && 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) === Attack.Result.SUCCESS) || (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); - } - } + if (!!closeMob) { + let findSkill = this.decideSkill(closeMob); + if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { + (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); + } + } + } - return Attack.Result.SUCCESS; - } + return Attack.Result.SUCCESS; + } - return result; - }, + return result; + }, - afterAttack: function () { - Precast.doPrecast(false); + afterAttack: function () { + Precast.doPrecast(false); - let needRepair = (Town.needRepair() || []); + let needRepair = (Town.needRepair() || []); - // Repair check, mainly to restock arrows - needRepair.length > 0 && Town.visitTown(true); + // Repair check, mainly to restock arrows + needRepair.length > 0 && Town.visitTown(true); - this.lightFuryTick = 0; - }, + this.lightFuryTick = 0; + }, - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - Config.TeleSwitch && me.switchToPrimary(); + // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + Config.TeleSwitch && me.switchToPrimary(); - // 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; - } - } - - let walk; - - if (timedSkill > -1 && (!me.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)) { - ClassAttack.lightFuryTick = getTickCount(); - } - - return Attack.Result.SUCCESS; - } - - break; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, unit.classid)) { - 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, untimedSkill, unit.classid)) { - 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 (timedSkill === sdk.skills.LightningFury && this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { - delay(40); - } - - return Attack.Result.SUCCESS; - } + // 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; + } + } + + let walk; + + if (timedSkill > -1 && (!me.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)) { + ClassAttack.lightFuryTick = getTickCount(); + } + + return Attack.Result.SUCCESS; + } + + break; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, unit.classid)) { + 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, untimedSkill, unit.classid)) { + 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 (timedSkill === sdk.skills.LightningFury + && this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { + delay(40); + } + + return Attack.Result.SUCCESS; + } }; diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index c0f32d6b6..130850626 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -6,253 +6,275 @@ */ const ClassAttack = { - lastTrapPos: {}, - trapRange: 20, - - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - - if (Config.MercWatch && Town.needMerc()) { - print("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking - } - } - } - - 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; - } - - let mercRevive = 0; - let timedSkill = -1; - let untimedSkill = -1; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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 && Skill.canUse(sdk.skills.CloakofShadows) && !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 && Skill.canUse(sdk.skills.CloakofShadows) && unit.distance < 20 && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { - Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); - } - - // 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)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { - timedSkill = Config.AttackSkill[5]; - } - - // 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, classid)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { - untimedSkill = Config.AttackSkill[6]; - } - - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(timedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } - - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(untimedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } - - let result = this.doCast(unit, timedSkill, untimedSkill); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({skipGid: gid}); - !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); - } - - return Attack.Result.SUCCESS; - } - - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - }, - - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); + lastTrapPos: {}, + trapRange: 20, + + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + + if (Config.MercWatch && Town.needMerc()) { + print("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } + } + } + + 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; + } + + let mercRevive = 0; + let timedSkill = -1; + let untimedSkill = -1; + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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 && Skill.canUse(sdk.skills.CloakofShadows) + && !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 && Skill.canUse(sdk.skills.CloakofShadows) + && unit.distance < 20 && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { + Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); + } + + // 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)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + timedSkill = Config.AttackSkill[5]; + } + + // 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, classid)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + untimedSkill = Config.AttackSkill[6]; + } + + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } + + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } + + let result = this.doCast(unit, timedSkill, untimedSkill); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); + } + + return Attack.Result.SUCCESS; + } + + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + }, + + // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); - let walk; - let classid = unit.classid; - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.Whirlwind: - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.BlockWall)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Attack.whirlwind(unit); - - return Attack.Result.SUCCESS; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { - 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 && getDistance(me, unit) < 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, untimedSkill, classid)) { - 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); - - return Attack.Result.SUCCESS; - }, - - checkTraps: function (unit) { - if (!Config.UseTraps || !unit) return false; - - // getDistance crashes when using an object with x, y props, that's why it's unit.x, unit.y and not unit - // is this still a thing ^^? todo: test it - if (me.getMinionCount(sdk.summons.type.AssassinTrap) === 0 || !this.lastTrapPos.hasOwnProperty("x") + let walk; + let classid = unit.classid; + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.Whirlwind: + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.BlockWall)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Attack.whirlwind(unit); + + return Attack.Result.SUCCESS; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + 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, untimedSkill, classid)) { + 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); + + return Attack.Result.SUCCESS; + }, + + checkTraps: function (unit) { + if (!Config.UseTraps || !unit) return false; + + // getDistance crashes when using an object with x, y props, that's why it's unit.x, unit.y and not unit + // is this still a thing ^^? todo: test it + if (me.getMinionCount(sdk.summons.type.AssassinTrap) === 0 || !this.lastTrapPos.hasOwnProperty("x") || getDistance(unit.x, unit.y, this.lastTrapPos.x, this.lastTrapPos.y) > 15) { - return 5; - } - - return 5 - me.getMinionCount(sdk.summons.type.AssassinTrap); - }, - - // todo - either import soloplays immune to trap check or add config option for immune to traps - // since this is the base file probably better to leave the option available rather than hard code it - // check if unit is still attackable after each cast? - placeTraps: function (unit, amount = 5) { - 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 - why not andy? - if ((unit.hasOwnProperty("classid") && [sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal].includes(unit.classid)) + return 5; + } + + return 5 - me.getMinionCount(sdk.summons.type.AssassinTrap); + }, + + // todo - either import soloplays immune to trap check or add config option for immune to traps + // since this is the base file probably better to leave the option available rather than hard code it + // check if unit is still attackable after each cast? + placeTraps: function (unit, amount = 5) { + 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 - why not andy? + 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; + 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; + Skill.cast(Config.BossTraps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + } else { + if (traps >= Config.Traps.length) return true; - Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); - } + Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + } - traps += 1; - } - } - } + traps += 1; + } + } + } - return true; - }, + return true; + }, }; diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index c12f95314..3ef67d8c8 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -12,253 +12,274 @@ */ const ClassAttack = { - /** + /** * @param {Monster} unit * @param {boolean} preattack * @returns {AttackResult} */ - doAttack: function (unit, preattack = false) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - let needRepair = Town.needRepair(); - - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { - print("towncheck"); - - if (Town.visitTown(!!needRepair.length)) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking - } - } - } - - if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Attack.getSkillElement(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; - } - - let 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 && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { - attackSkill = Config.AttackSkill[index + 1]; - } - } - - // Low mana skill - if (Skill.getManaCost(attackSkill) > me.mp && Config.LowManaSkill[0] > -1 && Attack.checkResist(unit, Config.LowManaSkill[0])) { - attackSkill = Config.LowManaSkill[0]; - } + doAttack: function (unit, preattack = false) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + let needRepair = Town.needRepair(); + + if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + print("towncheck"); + + if (Town.visitTown(!!needRepair.length)) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } + } + } + + if (preattack && Config.AttackSkill[0] > 0 + && Attack.checkResist(unit, Attack.getSkillElement(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; + } + + let 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 && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { + attackSkill = Config.AttackSkill[index + 1]; + } + } + + // Low mana skill + if (Skill.getManaCost(attackSkill) > me.mp + && Config.LowManaSkill[0] > -1 + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + attackSkill = Config.LowManaSkill[0]; + } - // low weapon-quantity -> use secondary skill if we can - if (attackSkill === sdk.skills.DoubleThrow && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) + // low weapon-quantity -> use secondary skill if we can + if (attackSkill === sdk.skills.DoubleThrow + && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { - attackSkill = Config.AttackSkill[index + 1]; - } + attackSkill = Config.AttackSkill[index + 1]; + } - // Telestomp with barb is pointless - return this.doCast(unit, attackSkill); - }, + // Telestomp with barb is pointless + return this.doCast(unit, attackSkill); + }, - /** + /** * Check if we need to precast, repair items, or perform findItem * @param {boolean} pickit - determines if we use findItem or not */ - afterAttack: function (pickit = true) { - Precast.doPrecast(false); + afterAttack: function (pickit = true) { + Precast.doPrecast(false); - let needRepair = (Town.needRepair() || []); + let needRepair = (Town.needRepair() || []); - // Repair check - needRepair.length > 0 && Town.visitTown(true); - pickit && this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); - }, + // Repair check + needRepair.length > 0 && Town.visitTown(true); + pickit && this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); + }, - /** + /** * @param {Monster} unit * @param {number} attackSkill * @returns {AttackResult} */ - doCast: function (unit, attackSkill = -1) { - if (attackSkill < 0) return Attack.Result.CANTATTACK; - // check if unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); + doCast: function (unit, attackSkill = -1) { + if (attackSkill < 0) return Attack.Result.CANTATTACK; + // check if unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); - switch (attackSkill) { - case sdk.skills.Whirlwind: - /** + switch (attackSkill) { + case sdk.skills.Whirlwind: + /** * @todo we sometimes struggle getting into position because of monsters which is dumb since we can * just whirl through them, so that needs to be fixed */ - 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)) { - let walk = Skill.getRange(attackSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall); - - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } - } - - !unit.dead && Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); - - return Attack.Result.SUCCESS; - } - }, - - /** + 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) + ); + + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + + return Attack.Result.SUCCESS; + } + }, + + /** * Check whether there are any monsters in range that are attackable * @param {number} range * @returns {boolean} */ - checkCloseMonsters: function (range = 10) { - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.distance <= range && monster.attackable && !checkCollision(me, monster, sdk.collision.Ranged) - && (Attack.checkResist(monster, Attack.getSkillElement(Config.AttackSkill[monster.isSpecial ? 1 : 3])) - || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, Attack.getSkillElement(Config.AttackSkill[3]))))) { - return true; - } - } while (monster.getNext()); - } - - return false; - }, - - /** + checkCloseMonsters: function (range = 10) { + const [mainAttElm, secAttElm] = [ + Attack.getSkillElement(Config.AttackSkill[1]), + Attack.getSkillElement(Config.AttackSkill[3]), + ]; + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.distance <= range && monster.attackable && !checkCollision(me, monster, sdk.collision.Ranged) + && (Attack.checkResist(monster, monster.isSpecial ? mainAttElm : secAttElm)) + || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, secAttElm))) { + return true; + } + } while (monster.getNext()); + } + + return false; + }, + + /** * Use findItem skill to hork bodies * @param {number} range * @returns {boolean} */ - findItem: function (range = 10) { - if (!Skill.canUse(sdk.skills.FindItem)) return false; - - let corpseList = []; - const { x: orgX, y: orgY } = me; - - MainLoop: - 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()); - } - - while (corpseList.length > 0) { - if (this.checkCloseMonsters(5)) { - Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); - Attack.clear(10, false, false, false, false); - Pather.moveToEx(orgX, orgY, { allowPicking: 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)) { - (corpse.distance > 30 || checkCollision(me, corpse, sdk.collision.BlockWall)) && 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.switchWeapons(Attack.getPrimarySlot()); - - return true; - }, - - /** + findItem: function (range = 10) { + if (!Skill.canUse(sdk.skills.FindItem)) return false; + + let corpseList = []; + const { x: orgX, y: orgY } = me; + + MainLoop: + 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()); + } + + while (corpseList.length > 0) { + if (this.checkCloseMonsters(5)) { + Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); + Attack.clear(10, false, false, false, false); + Pather.moveToEx(orgX, orgY, { allowPicking: 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 || checkCollision(me, corpse, sdk.collision.BlockWall)) { + 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)) { + if (!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.switchWeapons(Attack.getPrimarySlot()); + + return true; + }, + + /** * Check if corpse is horkable * @param {Monster} unit * @returns {boolean} */ - checkCorpse: function (unit) { - if (!unit || !copyUnit(unit).x || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) return false; - if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 + checkCorpse: function (unit) { + if (!unit || !copyUnit(unit).x + || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) { + return false; + } + if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 && unit.spectype === sdk.monsters.spectype.All) { - return false; - } - - // monstats2 doesn't contain guest monsters info. sigh.. - if (unit.classid <= sdk.monsters.BurningDeadArcher2 && !getBaseStat("monstats2", unit.classid, "corpseSel")) { - return false; - } - - let states = [ - sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, - sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect - ]; - - return !!(unit.distance <= 25 && !checkCollision(me, unit, sdk.collision.Ranged) && states.every(state => !unit.getState(state))); - } + return false; + } + + // monstats2 doesn't contain guest monsters info. sigh.. + if (unit.classid <= sdk.monsters.BurningDeadArcher2 && !getBaseStat("monstats2", unit.classid, "corpseSel")) { + return false; + } + + let states = [ + sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, + sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect + ]; + + return !!(unit.distance <= 25 + && !checkCollision(me, unit, sdk.collision.Ranged) + && states.every(state => !unit.getState(state))); + } }; diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index c0d5ba469..63f9bcbe5 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -6,182 +6,202 @@ */ const ClassAttack = { - doAttack: function (unit, preattack = false) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - - if (Config.MercWatch && Town.needMerc()) { - print("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking - } - } - } - - // 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 (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; - } - - let mercRevive = 0; - let timedSkill = -1; - let untimedSkill = -1; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { - timedSkill = Config.AttackSkill[5]; - } - - // 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, classid)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { - untimedSkill = Config.AttackSkill[6]; - } - - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(timedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } - - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(untimedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } - - let result = this.doCast(unit, timedSkill, untimedSkill); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({skipGid: gid}); - !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); - } - - return Attack.Result.SUCCESS; - } - - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - }, - - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); + doAttack: function (unit, preattack = false) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + + if (Config.MercWatch && Town.needMerc()) { + print("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } + } + } + + // Rebuff Hurricane + if (Skill.canUse(sdk.skills.Hurricane) && !me.getState(sdk.states.Hurricane)) { + Skill.cast(sdk.skills.Hurricane, sdk.skills.hand.Right); + } + // Rebuff Cyclone Armor + if (Skill.canUse(sdk.skills.CycloneArmor) && !me.getState(sdk.states.CycloneArmor)) { + Skill.cast(sdk.skills.CycloneArmor, sdk.skills.hand.Right); + } + + 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; + } + + let mercRevive = 0; + let timedSkill = -1; + let untimedSkill = -1; + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + timedSkill = Config.AttackSkill[5]; + } + + // 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, classid)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + untimedSkill = Config.AttackSkill[6]; + } + + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } + + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } + + let result = this.doCast(unit, timedSkill, untimedSkill); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); + } + + return Attack.Result.SUCCESS; + } + + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + }, + + // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); - let walk; - let classid = unit.classid; - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.Tornado: - 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; - } - } - - // Randomized x coord changes tornado path and prevents constant missing - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit.x + rand(-2, 2), unit.y); - - return Attack.Result.SUCCESS; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { - 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, untimedSkill, classid)) { - 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); - - return Attack.Result.SUCCESS; - } + let walk; + let classid = unit.classid; + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.Tornado: + 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; + } + } + + // Randomized x coord changes tornado path and prevents constant missing + !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit.x + rand(-2, 2), unit.y); + + return Attack.Result.SUCCESS; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + 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, untimedSkill, classid)) { + 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); + + return Attack.Result.SUCCESS; + } }; diff --git a/d2bs/kolbot/libs/core/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js index f1dafedab..dffa037da 100644 --- a/d2bs/kolbot/libs/core/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -6,542 +6,579 @@ */ const ClassAttack = { - novaTick: 0, - maxSkeletons: 0, - maxMages: 0, - maxRevives: 0, - - setArmySize: function () { - ClassAttack.maxSkeletons = Config.Skeletons === "max" ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeleton) : Config.Skeletons; - ClassAttack.maxMages = Config.SkeletonMages === "max" ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeletalMage) : Config.SkeletonMages; - ClassAttack.maxRevives = Config.Revives === "max" ? Skill.getMaxSummonCount(sdk.skills.Revive) : Config.Revives; - }, - - /** - * @returns {boolean} true - doesn't use summons or has all he can summon, false - not full of summons yet - */ - isArmyFull: function () { - // This necro doesn't summon anything so assume he's full - if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { - return true; - } - - // Make sure we have a current count of summons needed - this.setArmySize(); - - // See if we're at full army count - if ((me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) - && (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) - && (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives)) { - return false; - } - - // If we got this far this necro has all the summons he needs - return true; - }, - - /** - * Check if we can use specific curse on monster - * @param {Monster} unit - * @param {number} curseID - * @returns {boolean} - */ - canCurse: function (unit, curseID) { - if (unit === undefined || unit.dead || !Skill.canUse(curseID)) return false; - - let state = (() => { - switch (curseID) { - case sdk.skills.AmplifyDamage: - return sdk.states.AmplifyDamage; - case sdk.skills.DimVision: - // dim doesn't work on oblivion knights - if ([sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3].includes(unit.classid)) return false; - return sdk.states.DimVision; - case sdk.skills.Weaken: - return sdk.states.Weaken; - case sdk.skills.IronMaiden: - return sdk.states.IronMaiden; - case sdk.skills.Terror: - return unit.scareable ? sdk.states.Terror : false; - case sdk.skills.Confuse: - // doens't work on specials - return unit.scareable ? sdk.states.Confuse : false; - case sdk.skills.LifeTap: - return sdk.states.LifeTap; - case sdk.skills.Attract: - // doens't work on specials - return unit.scareable ? sdk.states.Attract : false; - case sdk.skills.Decrepify: - return sdk.states.Decrepify; - case sdk.skills.LowerResist: - return sdk.states.LowerResist; - default: - console.warn("(ÿc9canCurse) :: ÿc1Invalid Curse ID: " + curseID); - - return false; - } - })(); - - return state ? !unit.getState(state) : false; - }, - - /** - * @param {Monster} unit - * @returns {number | boolean} - */ - getCustomCurse: function (unit) { - if (Config.CustomCurse.length <= 0) return false; - - let curse = Config.CustomCurse - .findIndex(function (unitID) { - if ((typeof unitID[0] === "number" && unit.classid && unit.classid === unitID[0]) - || (typeof unitID[0] === "string" && unit.name && unit.name.toLowerCase() === unitID[0].toLowerCase())) { - return true; - } - return false; - }); - if (curse > -1) { - // format [id, curse, spectype] - if (Config.CustomCurse[curse].length === 3) { - return ((unit.spectype & Config.CustomCurse[curse][2]) ? Config.CustomCurse[curse][1] : false); - } else { - return Config.CustomCurse[curse][1]; - } - } - - return false; - }, - - /** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills - */ - doAttack: function (unit, preattack = false) { - if (!unit || unit.dead) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - - let mercRevive = 0; - let gid = unit.gid; - let classid = unit.classid; - let [timedSkill, untimedSkill, customCurse] = [-1, -1, -1]; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - if (Config.MercWatch && Town.needMerc()) { - print("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking - } - } - } - - 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; - } - - // only continue if we can actually curse the unit otherwise its a waste of time - if (unit.curseable) { - customCurse = this.getCustomCurse(unit); - - if (customCurse && this.canCurse(unit, customCurse)) { - if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - Skill.cast(customCurse, sdk.skills.hand.Right, unit); - - return Attack.Result.SUCCESS; - } else if (!customCurse) { - if (Config.Curse[0] > 0 && unit.isSpecial && this.canCurse(unit, Config.Curse[0])) { - if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - Skill.cast(Config.Curse[0], sdk.skills.hand.Right, unit); - - return Attack.Result.SUCCESS; - } - - if (Config.Curse[1] > 0 && !unit.isSpecial && this.canCurse(unit, Config.Curse[1])) { - if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - Skill.cast(Config.Curse[1], sdk.skills.hand.Right, unit); - - return Attack.Result.SUCCESS; - } - } - } - - // 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)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { - timedSkill = Config.AttackSkill[5]; - } - - // 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, classid)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { - untimedSkill = Config.AttackSkill[6]; - } - - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(timedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } - - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(untimedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } - - let result = this.doCast(unit, timedSkill, untimedSkill); - - if (result === 1) { - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - } else if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); - } - - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - let closeMob = Attack.getNearestMonster({skipGid: gid}); - !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); - } - - return Attack.Result.SUCCESS; - } - - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - this.raiseArmy(); - this.novaTick = 0; - }, - - /** - * @param {Monster} unit - * @param {number} timedSkill - * @param {number} untimedSkill - * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills - */ - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); - - let walk; - let classid = unit.classid; - - // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros - this.isArmyFull() && this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.PoisonNova: - if (!this.novaTick || getTickCount() - this.novaTick > Config.PoisonNovaDelay * 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.novaTick = getTickCount(); - } - } - - break; - case sdk.skills.Summoner: // Pure Summoner - 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; - } - } - - delay(300); - - break; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) return Attack.Result.FAILED; - - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - let 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); - - break; - } - } - - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) 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); - - // Delay for Poison Nova - while (timedSkill === sdk.skills.PoisonNova && this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { - delay(40); - } - - return Attack.Result.SUCCESS; - }, - - /** - * @param {number} range - */ - raiseArmy: function (range = 25) { - let tick, count; - - this.setArmySize(); - - for (let i = 0; i < 3; i += 1) { - let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); - let corpseList = []; - - if (corpse) { - do { - // within casting distance - if (corpse.distance <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - } - - while (corpseList.length > 0) { - corpse = corpseList.shift(); - - // should probably have a way to priortize which ones we summon first - if (me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) { - if (!Skill.cast(sdk.skills.RaiseSkeleton, sdk.skills.hand.Right, corpse)) { - return false; - } - - count = me.getMinionCount(sdk.summons.type.Skeleton); - tick = getTickCount(); - - while (getTickCount() - tick < 200) { - if (me.getMinionCount(sdk.summons.type.Skeleton) > count) { - break; - } - - delay(10); - } - } else if (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) { - if (!Skill.cast(sdk.skills.RaiseSkeletalMage, sdk.skills.hand.Right, corpse)) { - return false; - } - - count = me.getMinionCount(sdk.summons.type.SkeletonMage); - tick = getTickCount(); - - while (getTickCount() - tick < 200) { - if (me.getMinionCount(sdk.summons.type.SkeletonMage) > count) { - break; - } - - delay(10); - } - } else if (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives) { - if (this.checkCorpse(corpse, true)) { - print("Reviving " + corpse.name); - - if (!Skill.cast(sdk.skills.Revive, sdk.skills.hand.Right, corpse)) { - return false; - } - - count = me.getMinionCount(sdk.summons.type.Revive); - tick = getTickCount(); - - while (getTickCount() - tick < 200) { - if (me.getMinionCount(sdk.summons.type.Revive) > count) { - break; - } - - delay(10); - } - } - } else { - return true; - } - } - } - - return true; - }, - - /** - * @param {Monster} unit - */ - explodeCorpses: function (unit) { - if (Config.ExplodeCorpses === 0 || unit.dead) return false; - - let corpseList = []; - 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 (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 (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { - delay(200); - } - } - } else { - break; - } - } - } - } - - return true; - }, - - /** - * @param {Monster} monster - * @param {number} range - */ - checkCorpseNearMonster: function (monster, range) { - let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); - - // Assume CorpseExplosion if no range specified - range === undefined && (range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3)); - - if (corpse) { - do { - if (getDistance(corpse, monster) <= range) { - return true; - } - } while (corpse.getNext()); - } - - return false; - }, - - /** - * @param {Unit} unit - * @param {boolean} revive - */ - checkCorpse: function (unit, revive = false) { - if (!unit || unit.mode !== sdk.monsters.mode.Dead) return false; - - let baseId = getBaseStat("monstats", unit.classid, "baseid"), badList = [312, 571]; - let states = [ - sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, - sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect - ]; - - if (revive && (unit.isSpecial || badList.includes(baseId) || (Config.ReviveUnstackable && getBaseStat("monstats2", baseId, "sizex") === 3))) { - return false; - } - - if (!getBaseStat("monstats2", baseId, revive ? "revive" : "corpseSel")) return false; - - return !!(unit.distance <= 25 && !checkCollision(me, unit, sdk.collision.Ranged) && states.every(state => !unit.getState(state))); - } + novaTick: 0, + maxSkeletons: 0, + maxMages: 0, + maxRevives: 0, + + setArmySize: function () { + ClassAttack.maxSkeletons = Config.Skeletons === "max" + ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeleton) + : Config.Skeletons; + ClassAttack.maxMages = Config.SkeletonMages === "max" + ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeletalMage) + : Config.SkeletonMages; + ClassAttack.maxRevives = Config.Revives === "max" + ? Skill.getMaxSummonCount(sdk.skills.Revive) + : Config.Revives; + }, + + /** + * @returns {boolean} true - doesn't use summons or has all he can summon, false - not full of summons yet + */ + isArmyFull: function () { + // This necro doesn't summon anything so assume he's full + if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { + return true; + } + + // Make sure we have a current count of summons needed + this.setArmySize(); + + // See if we're at full army count + if ((me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) + && (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) + && (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives)) { + return false; + } + + // If we got this far this necro has all the summons he needs + return true; + }, + + /** + * Check if we can use specific curse on monster + * @param {Monster} unit + * @param {number} curseID + * @returns {boolean} + */ + canCurse: function (unit, curseID) { + if (unit === undefined || unit.dead || !Skill.canUse(curseID)) return false; + + let state = (() => { + switch (curseID) { + case sdk.skills.AmplifyDamage: + return sdk.states.AmplifyDamage; + case sdk.skills.DimVision: + // dim doesn't work on oblivion knights + if ([ + sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3 + ].includes(unit.classid)) { + return false; + } + return sdk.states.DimVision; + case sdk.skills.Weaken: + return sdk.states.Weaken; + case sdk.skills.IronMaiden: + return sdk.states.IronMaiden; + case sdk.skills.Terror: + return unit.scareable ? sdk.states.Terror : false; + case sdk.skills.Confuse: + // doens't work on specials + return unit.scareable ? sdk.states.Confuse : false; + case sdk.skills.LifeTap: + return sdk.states.LifeTap; + case sdk.skills.Attract: + // doens't work on specials + return unit.scareable ? sdk.states.Attract : false; + case sdk.skills.Decrepify: + return sdk.states.Decrepify; + case sdk.skills.LowerResist: + return sdk.states.LowerResist; + default: + console.warn("(ÿc9canCurse) :: ÿc1Invalid Curse ID: " + curseID); + + return false; + } + })(); + + return state ? !unit.getState(state) : false; + }, + + /** + * @param {Monster} unit + * @returns {number | boolean} + */ + getCustomCurse: function (unit) { + if (Config.CustomCurse.length <= 0) return false; + + let curse = Config.CustomCurse + .findIndex(function (unitID) { + if ((typeof unitID[0] === "number" && unit.classid && unit.classid === unitID[0]) + || (typeof unitID[0] === "string" && unit.name && unit.name.toLowerCase() === unitID[0].toLowerCase())) { + return true; + } + return false; + }); + if (curse > -1) { + // format [id, curse, spectype] + if (Config.CustomCurse[curse].length === 3) { + return ((unit.spectype & Config.CustomCurse[curse][2]) ? Config.CustomCurse[curse][1] : false); + } else { + return Config.CustomCurse[curse][1]; + } + } + + return false; + }, + + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills + */ + doAttack: function (unit, preattack = false) { + if (!unit || unit.dead) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + + let mercRevive = 0; + let gid = unit.gid; + let classid = unit.classid; + let [timedSkill, untimedSkill, customCurse] = [-1, -1, -1]; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + + if (Config.MercWatch && Town.needMerc()) { + print("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } + } + } + + 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; + } + + // only continue if we can actually curse the unit otherwise its a waste of time + if (unit.curseable) { + customCurse = this.getCustomCurse(unit); + + if (customCurse && this.canCurse(unit, customCurse)) { + if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(customCurse, sdk.skills.hand.Right, unit); + + return Attack.Result.SUCCESS; + } else if (!customCurse) { + if (Config.Curse[0] > 0 && unit.isSpecial && this.canCurse(unit, Config.Curse[0])) { + if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(Config.Curse[0], sdk.skills.hand.Right, unit); + + return Attack.Result.SUCCESS; + } + + if (Config.Curse[1] > 0 && !unit.isSpecial && this.canCurse(unit, Config.Curse[1])) { + if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(Config.Curse[1], sdk.skills.hand.Right, unit); + + return Attack.Result.SUCCESS; + } + } + } + + // 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)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + timedSkill = Config.AttackSkill[5]; + } + + // 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, classid)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + untimedSkill = Config.AttackSkill[6]; + } + + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } + + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } + + let result = this.doCast(unit, timedSkill, untimedSkill); + + if (result === 1) { + Config.ActiveSummon && this.raiseArmy(); + this.explodeCorpses(unit); + } else if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } + + Config.ActiveSummon && this.raiseArmy(); + this.explodeCorpses(unit); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); + } + + return Attack.Result.SUCCESS; + } + + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + this.raiseArmy(); + this.novaTick = 0; + }, + + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills + */ + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); + + let walk; + let classid = unit.classid; + + // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros + this.isArmyFull() && this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.PoisonNova: + if (!this.novaTick || getTickCount() - this.novaTick > Config.PoisonNovaDelay * 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.novaTick = getTickCount(); + } + } + + break; + case sdk.skills.Summoner: // Pure Summoner + 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; + } + } + + delay(300); + + break; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + return Attack.Result.FAILED; + } + + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + let 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); + + break; + } + } + + if (untimedSkill > -1) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { + 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); + + // Delay for Poison Nova + while (timedSkill === sdk.skills.PoisonNova + && this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { + delay(40); + } + + return Attack.Result.SUCCESS; + }, + + /** + * @param {number} range + */ + raiseArmy: function (range = 25) { + let tick, count; + + this.setArmySize(); + + for (let i = 0; i < 3; i += 1) { + let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); + let corpseList = []; + + if (corpse) { + do { + // within casting distance + if (corpse.distance <= range && this.checkCorpse(corpse)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); + } + + while (corpseList.length > 0) { + corpse = corpseList.shift(); + + // should probably have a way to priortize which ones we summon first + if (me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) { + if (!Skill.cast(sdk.skills.RaiseSkeleton, sdk.skills.hand.Right, corpse)) { + return false; + } + + count = me.getMinionCount(sdk.summons.type.Skeleton); + tick = getTickCount(); + + while (getTickCount() - tick < 200) { + if (me.getMinionCount(sdk.summons.type.Skeleton) > count) { + break; + } + + delay(10); + } + } else if (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) { + if (!Skill.cast(sdk.skills.RaiseSkeletalMage, sdk.skills.hand.Right, corpse)) { + return false; + } + + count = me.getMinionCount(sdk.summons.type.SkeletonMage); + tick = getTickCount(); + + while (getTickCount() - tick < 200) { + if (me.getMinionCount(sdk.summons.type.SkeletonMage) > count) { + break; + } + + delay(10); + } + } else if (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives) { + if (this.checkCorpse(corpse, true)) { + print("Reviving " + corpse.name); + + if (!Skill.cast(sdk.skills.Revive, sdk.skills.hand.Right, corpse)) { + return false; + } + + count = me.getMinionCount(sdk.summons.type.Revive); + tick = getTickCount(); + + while (getTickCount() - tick < 200) { + if (me.getMinionCount(sdk.summons.type.Revive) > count) { + break; + } + + delay(10); + } + } + } else { + return true; + } + } + } + + return true; + }, + + /** + * @param {Monster} unit + */ + explodeCorpses: function (unit) { + if (Config.ExplodeCorpses === 0 || unit.dead) return false; + + let corpseList = []; + 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 (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 (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { + delay(200); + } + } + } else { + break; + } + } + } + } + + return true; + }, + + /** + * @param {Monster} monster + * @param {number} range + */ + checkCorpseNearMonster: function (monster, range) { + let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); + + // Assume CorpseExplosion if no range specified + if (range === undefined) { + range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); + } + + if (corpse) { + do { + if (getDistance(corpse, monster) <= range) { + return true; + } + } while (corpse.getNext()); + } + + return false; + }, + + /** + * @param {Unit} unit + * @param {boolean} revive + */ + checkCorpse: function (unit, revive = false) { + if (!unit || unit.mode !== sdk.monsters.mode.Dead) return false; + + let baseId = getBaseStat("monstats", unit.classid, "baseid"), badList = [312, 571]; + let states = [ + sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, + sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect + ]; + + if (revive + && (unit.isSpecial || badList.includes(baseId) + || (Config.ReviveUnstackable && getBaseStat("monstats2", baseId, "sizex") === 3))) { + return false; + } + + if (!getBaseStat("monstats2", baseId, revive ? "revive" : "corpseSel")) return false; + + return !!(unit.distance <= 25 + && !checkCollision(me, unit, sdk.collision.Ranged) + && states.every(state => !unit.getState(state))); + } }; diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 959e5985b..e5fc23a71 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -6,360 +6,383 @@ */ const ClassAttack = { - attackAuras: [sdk.skills.HolyFire, sdk.skills.HolyFreeze, sdk.skills.HolyShock], + attackAuras: [sdk.skills.HolyFire, sdk.skills.HolyFreeze, sdk.skills.HolyShock], - /** + /** * @param {Unit} unit * @param {boolean} [preattack] * @returns {AttackResult} */ - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - - if (Config.MercWatch && Town.needMerc()) { - print("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 (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; - } - - let mercRevive = 0; - let [attackSkill, aura] = [-1, -1]; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - 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]; - } - } - } - - // Low mana skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(attackSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - [attackSkill, aura] = Config.LowManaSkill; - } - - let result = this.doCast(unit, attackSkill, aura); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({skipGid: gid}); - !!closeMob && this.doCast(closeMob, attackSkill, aura); - } - - return Attack.Result.SUCCESS; - } - - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - - // only proceed with other checks if we can use redemption and the config values aren't 0 - if (Skill.canUse(sdk.skills.Redemption) && Config.Redemption.some(v => v > 0)) { - if ((me.hpPercent < Config.Redemption[0] || me.mpPercent < Config.Redemption[1]) + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + + if (Config.MercWatch && Town.needMerc()) { + print("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 (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; + } + + let mercRevive = 0; + let [attackSkill, aura] = [-1, -1]; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + + 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) { + let _check = (this.attackAuras.includes(Config.AttackSkill[6]) + ? Config.AttackSkill[6] + : Config.AttackSkill[5]); + if (Attack.checkResist(unit, _check)) { + 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]; + } + } + } + + // Low mana skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(attackSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + [attackSkill, aura] = Config.LowManaSkill; + } + + let result = this.doCast(unit, attackSkill, aura); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(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 (Town.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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, attackSkill, aura); + } + + return Attack.Result.SUCCESS; + } + + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + + // only proceed with other checks if we can use redemption and the config values aren't 0 + if (Skill.canUse(sdk.skills.Redemption) && Config.Redemption.some(v => v > 0)) { + if ((me.hpPercent < Config.Redemption[0] || me.mpPercent < Config.Redemption[1]) && Attack.checkNearCorpses(me) > 2 && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { - delay(1500); - } - } + delay(1500); + } + } - /** + /** * @todo add config options for these and possibly add to Pather.walkTo */ - // if (Skill.canUse(sdk.skills.Cleansing) - // && ([sdk.states.AmplifyDamage, sdk.states.Decrepify].some(s => me.getState(s)) || me.hpPercent < 70 && me.getState(sdk.states.Poison)) - // && !me.checkForMobs({range: 12, coll: sdk.collision.BlockWall}) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { - // me.overhead("Delaying for a second to get rid of Poison"); - // Misc.poll(() => (![sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Poison].some(s => me.getState(s)) || 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); - // } - }, - - 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; - Config.TeleSwitch && me.switchToPrimary(); + // if (Skill.canUse(sdk.skills.Cleansing) + // && ([sdk.states.AmplifyDamage, sdk.states.Decrepify].some(s => me.getState(s)) || me.hpPercent < 70 && me.getState(sdk.states.Poison)) + // && !me.checkForMobs({range: 12, coll: sdk.collision.BlockWall}) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + // me.overhead("Delaying for a second to get rid of Poison"); + // Misc.poll(() => (![sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Poison].some(s => me.getState(s)) || 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); + // } + }, + + 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; + Config.TeleSwitch && me.switchToPrimary(); - 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, Skill.getHand(attackSkill), 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, Skill.getHand(attackSkill), unit); - - if (!unit.attackable || unit.distance > 9 || unit.isPlayer) { - break; - } - } - - return Attack.Result.SUCCESS; - case sdk.skills.HolyBolt: - if (unit.distance > Skill.getRange(attackSkill) + 3 || CollMap.checkColl(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - CollMap.reset(); - - if (unit.distance > Skill.getRange(attackSkill) || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.FriendlyRanged, true)) { - return Attack.Result.FAILED; - } - } - - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); - } - - return Attack.Result.SUCCESS; - case sdk.skills.FistoftheHeavens: - if (!me.skillDelay) { - if (unit.distance > Skill.getRange(attackSkill) || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.FriendlyRanged, true)) { - return Attack.Result.FAILED; - } - } - - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, Skill.getHand(attackSkill), 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; - } + 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, Skill.getHand(attackSkill), 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, Skill.getHand(attackSkill), unit); + + if (!unit.attackable || unit.distance > 9 || unit.isPlayer) { + break; + } + } + + return Attack.Result.SUCCESS; + case sdk.skills.HolyBolt: + if (unit.distance > Skill.getRange(attackSkill) + 3 || CollMap.checkColl(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + CollMap.reset(); + + if (unit.distance > Skill.getRange(attackSkill) || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.FriendlyRanged, true)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + } + + return Attack.Result.SUCCESS; + case sdk.skills.FistoftheHeavens: + if (!me.skillDelay) { + if (unit.distance > Skill.getRange(attackSkill) + || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.FriendlyRanged, true)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, Skill.getHand(attackSkill), 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 (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)) { - let walk = (attackSkill !== sdk.skills.Smite && Skill.getRange(attackSkill) < 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, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) return Attack.Result.FAILED; - } - - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); - } - - return Attack.Result.SUCCESS; - } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; - }, - - dollAvoid: function (unit) { - 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; - }, - - getHammerPosition: function (unit) { - let x, y, positions, baseId = getBaseStat("monstats", unit.classid, "baseid"); - let size = getBaseStat("monstats2", baseId, "sizex"); - let 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 = unit.x; - y = unit.y; - 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.x, check.y].distance < 1) { - return true; - } else if (!canTele && ([check.x, check.y].distance < 1 && !CollMap.checkColl(unit, check, sdk.collision.BlockWalk, 0)) + // 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 (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)) { + let walk = (attackSkill !== sdk.skills.Smite + && Skill.getRange(attackSkill) < 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, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + } + + return Attack.Result.SUCCESS; + } + + Misc.poll(() => !me.skillDelay, 1000, 40); + + return Attack.Result.SUCCESS; + }, + + dollAvoid: function (unit) { + 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; + }, + + getHammerPosition: function (unit) { + let x, y, positions, baseId = getBaseStat("monstats", unit.classid, "baseid"); + let size = getBaseStat("monstats2", baseId, "sizex"); + let 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 = unit.x; + y = unit.y; + 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.x, check.y].distance < 1) { + return true; + } else if (!canTele && ([check.x, check.y].distance < 1 + && !CollMap.checkColl(unit, check, sdk.collision.BlockWalk, 0)) || ([check.x, check.y].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, sdk.collision.BlockWalk, 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; - }, - - reposition: function (x, y) { - if (typeof x !== "number" || typeof y !== "number") return false; - if ([x, y].distance > 0) { - if (Pather.useTeleport()) { - [x, y].distance > 30 ? Pather.moveTo(x, y) : Pather.teleportTo(x, y, 3); - } else { - if ([x, y].distance <= 4) { - 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; - } + 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, sdk.collision.BlockWalk, 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; + }, + + reposition: function (x, y) { + if (typeof x !== "number" || typeof y !== "number") return false; + if ([x, y].distance > 0) { + if (Pather.useTeleport()) { + [x, y].distance > 30 ? Pather.moveTo(x, y) : Pather.teleportTo(x, y, 3); + } else { + if ([x, y].distance <= 4) { + 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; + } }; diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 5f4164531..fbcbf37e1 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -6,230 +6,256 @@ */ const ClassAttack = { - decideSkill: function (unit) { - let skills = {timed: -1, untimed: -1}; - if (!unit || !unit.attackable) return skills; - - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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]; - } - - // 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, classid)) { - 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 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; - }, - - doAttack: function (unit, preattack = false) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - - if (Config.MercWatch && Town.needMerc()) { - if (Town.visitTown()) { - print("mercwatch"); + decideSkill: function (unit) { + let skills = { timed: -1, untimed: -1 }; + if (!unit || !unit.attackable) return skills; + + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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]; + } + + // 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, classid)) { + 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 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; + }, + + doAttack: function (unit, preattack = false) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + + if (Config.MercWatch && Town.needMerc()) { + if (Town.visitTown()) { + print("mercwatch"); - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - console.debug("Lost reference to unit"); - return Attack.Result.SUCCESS; - } - } - } - - // 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); - - 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; - } - - let useStatic = (Config.StaticList.length > 0 && Config.CastStatic < 100 && Skill.canUse(sdk.skills.StaticField) && Attack.checkResist(unit, "lightning")); - let idCheck = function (id) { - if (unit) { - switch (true) { - case typeof id === "number" && unit.classid && unit.classid === id: - case typeof id === "string" && unit.name && unit.name.toLowerCase() === id.toLowerCase(): - case typeof id === "function" && id(unit): - return true; - default: - return false; - } - } - - return false; - }; + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + console.debug("Lost reference to unit"); + return Attack.Result.SUCCESS; + } + } + } + + // Keep Energy Shield active + if (Skill.canUse(sdk.skills.EnergyShield) && !me.getState(sdk.states.EnergyShield)) { + Skill.cast(sdk.skills.EnergyShield, sdk.skills.hand.Right); + } + + // Keep Thunder-Storm active + if (Skill.canUse(sdk.skills.ThunderStorm) && !me.getState(sdk.states.ThunderStorm)) { + Skill.cast(sdk.skills.ThunderStorm, sdk.skills.hand.Right); + } + + 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; + } + + let useStatic = (Config.StaticList.length > 0 + && Config.CastStatic < 100 + && Skill.canUse(sdk.skills.StaticField) + && Attack.checkResist(unit, "lightning")); + let idCheck = function (id) { + if (unit) { + switch (true) { + case typeof id === "number" && unit.classid && unit.classid === id: + case typeof id === "string" && unit.name && unit.name.toLowerCase() === id.toLowerCase(): + case typeof id === "function" && id(unit): + return true; + default: + return false; + } + } + + return false; + }; - // Static - needs to be re-done - if (useStatic && Config.StaticList.some(id => idCheck(id)) && unit.hpPercent > Config.CastStatic) { - let staticRange = Skill.getRange(sdk.skills.StaticField); - let casts = 0; - - while (!me.dead && unit.hpPercent > Config.CastStatic && unit.attackable) { - if (unit.distance > staticRange || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, staticRange, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } - } - - // if we fail to cast or we've casted 3 or more times - do something else - if (!Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right) || casts >= 3) { - break; - } else { - casts++; - } - } - - // re-check mob after static - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - console.debug("Lost reference to unit"); - 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(); - 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 (Town.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; - } - - (merc === undefined || !merc) && (merc = me.getMerc()); - } - - if (!!merc && getDistance(merc, unit) > 7) { - Pather.moveToUnit(unit); - - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); - } - - let closeMob = Attack.getNearestMonster({skipGid: gid}); + // Static - needs to be re-done + if (useStatic && Config.StaticList.some(id => idCheck(id)) && unit.hpPercent > Config.CastStatic) { + let staticRange = Skill.getRange(sdk.skills.StaticField); + let casts = 0; + + while (!me.dead && unit.hpPercent > Config.CastStatic && unit.attackable) { + if (unit.distance > staticRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, staticRange, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + // if we fail to cast or we've casted 3 or more times - do something else + if (!Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right) || casts >= 3) { + break; + } else { + casts++; + } + } + + // re-check mob after static + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + console.debug("Lost reference to unit"); + 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(); + 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 (Town.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } + + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 7) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && !!spot.x && 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.haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)); - } - } - - return Attack.Result.SUCCESS; - } - - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - }, - - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); + if (!!closeMob) { + let findSkill = this.decideSkill(closeMob); + if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { + (Skill.haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)); + } + } + } + + return Attack.Result.SUCCESS; + } + + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + }, + + // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); - let walk, noMana = false; - let classid = unit.classid; - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill)) && Skill.getManaCost(timedSkill) < me.mp) { - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { - 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 && !checkCollision(me, unit, sdk.collision.Ranged) && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - - return Attack.Result.SUCCESS; - } else { - noMana = !me.skillDelay; - } - - if (untimedSkill > -1 && Skill.getManaCost(untimedSkill) < me.mp) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { - 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; - } else { - noMana = true; - } - - // don't count as failed - if (noMana) return Attack.Result.NEEDMANA; - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; - } + let walk, noMana = false; + let classid = unit.classid; + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill)) && Skill.getManaCost(timedSkill) < me.mp) { + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + 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; + } + } + + if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } + return Attack.Result.SUCCESS; + } else { + noMana = !me.skillDelay; + } + + if (untimedSkill > -1 && Skill.getManaCost(untimedSkill) < me.mp) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { + 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; + } else { + noMana = true; + } + + // don't count as failed + if (noMana) return Attack.Result.NEEDMANA; + + Misc.poll(() => !me.skillDelay, 1000, 40); + + return Attack.Result.SUCCESS; + } }; diff --git a/d2bs/kolbot/libs/core/Attacks/Wereform.js b/d2bs/kolbot/libs/core/Attacks/Wereform.js index b8c2bdc35..54d137d67 100644 --- a/d2bs/kolbot/libs/core/Attacks/Wereform.js +++ b/d2bs/kolbot/libs/core/Attacks/Wereform.js @@ -7,147 +7,175 @@ // todo - handle a Bear necro summonmancer -const ClassAttack = { - feralBoost: 0, - baseLL: me.getStat(sdk.stats.LifeLeech), - baseED: me.getStat(sdk.stats.DamagePercent), - maulBoost: 0, - - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - - if (Config.MercWatch && Town.needMerc()) { - console.debug("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking - } - } - } - - if (!this.feralBoost && Config.AttackSkill.includes(sdk.skills.FeralRage)) { - // amount of life leech with max rage - this.feralBoost = ((Math.floor(me.getSkill(sdk.skills.FeralRage, sdk.skills.subindex.SoftPoints) / 2) + 3) * 4) + this.baseLL; - } - - if (!this.maulBoost && Config.AttackSkill.includes(sdk.skills.Maul)) { - // amount of enhanced damage with max maul - this.maulBoost = ((Math.floor(me.getSkill(sdk.skills.Maul, sdk.skills.subindex.SoftPoints) / 2) + 3) * 20) + this.baseED; - } - - Skill.shapeShift(Config.Wereform); - - if (((Config.AttackSkill[0] === sdk.skills.FeralRage && (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < this.feralBoost)) - || (Config.AttackSkill[0] === sdk.skills.Maul && (!me.getState(sdk.states.Maul) || me.getStat(sdk.stats.DamagePercent) < this.maulBoost)) - || (Config.AttackSkill[0] === sdk.skills.ShockWave && !unit.isSpecial && !unit.getState(sdk.states.Stunned)) - || (preattack && Config.AttackSkill[0] > 0)) - && Attack.checkResist(unit, Config.AttackSkill[0]) - && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0])) - && (Skill.wereFormCheck(Config.AttackSkill[0]) || !me.shapeshifted)) { - if (unit.distance > Skill.getRange(Config.AttackSkill[0]) || checkCollision(me, unit, sdk.collision.WallOrRanged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.WallOrRanged, true)) { - return Attack.Result.FAILED; - } - } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; - } - - // Rebuff Armageddon - Skill.canUse(sdk.skills.Armageddon) && !me.getState(sdk.states.Armageddon) && Skill.cast(sdk.skills.Armageddon, sdk.skills.hand.Right); - - let timedSkill = -1; - let untimedSkill = -1; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - // Get timed skill - let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - - if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5]) && Attack.validSpot(unit.x, unit.y)) { - timedSkill = Config.AttackSkill[5]; - } - - // Get untimed skill - checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; - - if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 && Attack.checkResist(unit, Config.AttackSkill[6]) && Attack.validSpot(unit.x, unit.y)) { - untimedSkill = Config.AttackSkill[6]; - } - - // eval skills - switch (true) { - case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.FeralRage: - if (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < this.feralBoost) { - timedSkill = sdk.skills.FeralRage; - } - - break; - case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.Rabies: - case timedSkill === sdk.skills.FireClaws && untimedSkill === sdk.skills.Rabies: - if (!unit.getState(sdk.states.Rabies)) { - timedSkill = sdk.skills.Rabies; - } - - break; - case timedSkill === sdk.skills.ShockWave && untimedSkill === sdk.skills.Maul: - case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.ShockWave: - case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.FireClaws: - if (!me.getState(sdk.states.Maul)) { - timedSkill = sdk.skills.Maul; - } - - break; - } - - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 && Skill.getManaCost(timedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } - - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 && Skill.getManaCost(untimedSkill) > me.mp && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } - - // use our secondary skill if we can't use our primary - let choosenSkill = (Skill.isTimed(timedSkill) && me.skillDelay && untimedSkill > -1 ? untimedSkill : timedSkill); - - return this.doCast(unit, choosenSkill); - }, - - afterAttack: function () { - Precast.doPrecast(false); - }, - - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills - doCast: function (unit, skill) { - // unit reference no longer valid or it died - if (!unit || unit.dead) return Attack.Result.SUCCESS; - // No valid skills can be found - if (skill < 0) return Attack.Result.CANTATTACK; - - if (Skill.getRange(skill) < 4 && !Attack.validSpot(unit.x, unit.y)) { - return Attack.Result.FAILED; - } - - if (unit.distance > Skill.getRange(skill) || checkCollision(me, unit, sdk.collision.WallOrRanged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(skill), sdk.collision.WallOrRanged, true)) { - return Attack.Result.FAILED; - } - } - - unit.attackable && Skill.cast(skill, Skill.getHand(skill), unit); - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; - } -}; +const ClassAttack = (function () { + const baseLL = me.getStat(sdk.stats.LifeLeech); + const baseED = me.getStat(sdk.stats.DamagePercent); + const feralBoost = () => ( + ((Math.floor(me.getSkill(sdk.skills.FeralRage, sdk.skills.subindex.SoftPoints) / 2) + 3) * 4) + baseLL + ); + const maulBoost = () => ( + ((Math.floor(me.getSkill(sdk.skills.Maul, sdk.skills.subindex.SoftPoints) / 2) + 3) * 20) + baseED + ); + + const wereform = { + rage: 0, + maul: 0, + duration: 0, // todo - handle duration, if we are about to lose our form, recast it before attacking + }; + + return { + feralBoost: 0, + maulBoost: 0, + + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; + + if (Config.MercWatch && Town.needMerc()) { + console.debug("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } + } + } + + if (!wereform.rage && Config.AttackSkill.includes(sdk.skills.FeralRage)) { + // amount of life leech with max rage + wereform.rage = feralBoost(); + } + + if (!wereform.maul && Config.AttackSkill.includes(sdk.skills.Maul)) { + // amount of enhanced damage with max maul + wereform.maul = maulBoost(); + } + + Skill.shapeShift(Config.Wereform); + + if (((Config.AttackSkill[0] === sdk.skills.FeralRage + && (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < wereform.rage)) + || (Config.AttackSkill[0] === sdk.skills.Maul + && (!me.getState(sdk.states.Maul) || me.getStat(sdk.stats.DamagePercent) < wereform.maul)) + || (Config.AttackSkill[0] === sdk.skills.ShockWave && !unit.isSpecial && !unit.getState(sdk.states.Stunned)) + || (preattack && Config.AttackSkill[0] > 0)) + && Attack.checkResist(unit, Config.AttackSkill[0]) + && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0])) + && (Skill.wereFormCheck(Config.AttackSkill[0]) || !me.shapeshifted)) { + if (unit.distance > Skill.getRange(Config.AttackSkill[0]) + || checkCollision(me, unit, sdk.collision.WallOrRanged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.WallOrRanged, true)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); + + return Attack.Result.SUCCESS; + } + + // Rebuff Armageddon + if (Skill.canUse(sdk.skills.Armageddon) && !me.getState(sdk.states.Armageddon)) { + Skill.cast(sdk.skills.Armageddon, sdk.skills.hand.Right); + } + + let timedSkill = -1; + let untimedSkill = -1; + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + + // Get timed skill + let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + + if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y)) { + timedSkill = Config.AttackSkill[5]; + } + + // Get untimed skill + checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; + + if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y)) { + untimedSkill = Config.AttackSkill[6]; + } + + // eval skills + switch (true) { + case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.FeralRage: + if (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < wereform.rage) { + timedSkill = sdk.skills.FeralRage; + } + + break; + case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.Rabies: + case timedSkill === sdk.skills.FireClaws && untimedSkill === sdk.skills.Rabies: + if (!unit.getState(sdk.states.Rabies)) { + timedSkill = sdk.skills.Rabies; + } + + break; + case timedSkill === sdk.skills.ShockWave && untimedSkill === sdk.skills.Maul: + case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.ShockWave: + case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.FireClaws: + if (!me.getState(sdk.states.Maul)) { + timedSkill = sdk.skills.Maul; + } + + break; + } + + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } + + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } + + // use our secondary skill if we can't use our primary + let choosenSkill = (Skill.isTimed(timedSkill) && me.skillDelay && untimedSkill > -1 ? untimedSkill : timedSkill); + + return this.doCast(unit, choosenSkill); + }, + + afterAttack: function () { + Precast.doPrecast(false); + }, + + // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + doCast: function (unit, skill) { + // unit reference no longer valid or it died + if (!unit || unit.dead) return Attack.Result.SUCCESS; + // No valid skills can be found + if (skill < 0) return Attack.Result.CANTATTACK; + + if (Skill.getRange(skill) < 4 && !Attack.validSpot(unit.x, unit.y)) { + return Attack.Result.FAILED; + } + + if (unit.distance > Skill.getRange(skill) || checkCollision(me, unit, sdk.collision.WallOrRanged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(skill), sdk.collision.WallOrRanged, true)) { + return Attack.Result.FAILED; + } + } + + unit.attackable && Skill.cast(skill, Skill.getHand(skill), unit); + + Misc.poll(() => !me.skillDelay, 1000, 40); + + return Attack.Result.SUCCESS; + } + }; +})(); diff --git a/d2bs/kolbot/libs/core/Auto/AutoBuild.js b/d2bs/kolbot/libs/core/Auto/AutoBuild.js index 245507a89..381fd3f36 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoBuild.js +++ b/d2bs/kolbot/libs/core/Auto/AutoBuild.js @@ -17,90 +17,90 @@ js_strict(true); !isIncluded("core/Runewords.js") && include("core/Runewords.js"); const AutoBuild = new function AutoBuild () { - Config.AutoBuild.DebugMode && (Config.AutoBuild.Verbose = true); - - let debug = !!Config.AutoBuild.DebugMode; - let verbose = !!Config.AutoBuild.Verbose; - let configUpdateLevel = 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 () { - debug && this.print("Updating Config from level " + configUpdateLevel + " to " + me.charlvl); - while (configUpdateLevel < me.charlvl) { - configUpdateLevel += 1; - Skill.init(); - AutoBuildTemplate[configUpdateLevel].Update.apply(Config); - } - } - - function getBuildType () { - let build = Config.AutoBuild.Template; - 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 getCurrentScript () { - return getScript(true).name.toLowerCase(); - } - - 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 build = getBuildType(); - let template = "config/Builds/" + sdk.player.class.nameOf(me.classid) + "." + 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); - - // Only load() helper thread from default.dbj if it isn't loaded - if (currentScript === "default.dbj" && !getScript("tools\\autobuildthread.js")) { - load("threads/autobuildthread.js"); - } - - // All threads except autobuildthread.js use this event listener - // to update their thread-local Config object - if (currentScript !== "tools\\autobuildthread.js") { - addEventListener("scriptmsg", levelUpHandler); - } - - // 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 log (message) { FileTools.appendText(getLogFilename(), message + "\n"); } - - // Only print to console from autobuildthread.js, - // but log from all scripts - function myPrint () { - 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.print = myPrint; - this.initialize = initialize; - this.applyConfigUpdates = applyConfigUpdates; + Config.AutoBuild.DebugMode && (Config.AutoBuild.Verbose = true); + + let debug = !!Config.AutoBuild.DebugMode; + let verbose = !!Config.AutoBuild.Verbose; + let configUpdateLevel = 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 () { + debug && this.print("Updating Config from level " + configUpdateLevel + " to " + me.charlvl); + while (configUpdateLevel < me.charlvl) { + configUpdateLevel += 1; + Skill.init(); + AutoBuildTemplate[configUpdateLevel].Update.apply(Config); + } + } + + function getBuildType () { + let build = Config.AutoBuild.Template; + 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 getCurrentScript () { + return getScript(true).name.toLowerCase(); + } + + 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 build = getBuildType(); + let template = "config/Builds/" + sdk.player.class.nameOf(me.classid) + "." + 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); + + // Only load() helper thread from default.dbj if it isn't loaded + if (currentScript === "default.dbj" && !getScript("tools\\autobuildthread.js")) { + load("threads/autobuildthread.js"); + } + + // All threads except autobuildthread.js use this event listener + // to update their thread-local Config object + if (currentScript !== "tools\\autobuildthread.js") { + addEventListener("scriptmsg", levelUpHandler); + } + + // 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 log (message) { FileTools.appendText(getLogFilename(), message + "\n"); } + + // Only print to console from autobuildthread.js, + // but log from all scripts + function myPrint () { + 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.print = myPrint; + this.initialize = initialize; + this.applyConfigUpdates = applyConfigUpdates; }; diff --git a/d2bs/kolbot/libs/core/Auto/AutoSkill.js b/d2bs/kolbot/libs/core/Auto/AutoSkill.js index 85dd27ecb..cec5b6e93 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoSkill.js +++ b/d2bs/kolbot/libs/core/Auto/AutoSkill.js @@ -6,10 +6,10 @@ */ const AutoSkill = new function () { - this.skillBuildOrder = []; - this.save = 0; + this.skillBuildOrder = []; + this.save = 0; - /* skillBuildOrder - array of skill points to spend in order + /* skillBuildOrder - array of skill points to spend in order save - number of skill points that will not be spent and saved skillBuildOrder Settings @@ -27,128 +27,136 @@ const AutoSkill = new function () { ]; */ - //a function to return false if have all prereqs or a skill if not - this.needPreReq = function (skillid) { - //a loop to go through each reqskill - for (let t = sdk.stats.PreviousSkillLeft; t >= sdk.stats.PreviousSkillRight; t--) { - // Check ReqSkills - let preReq = (getBaseStat("skills", skillid, t)); - - if (preReq > sdk.skills.Attack && preReq < 356 && !me.getSkill(preReq, sdk.skills.subindex.HardPoints)) { - return preReq; - } - } - - return false; - }; - - this.skillCheck = function (skillid, count) { - if (me.getSkill(skillid, sdk.skills.subindex.HardPoints) <= me.charlvl - getBaseStat("skills", skillid, sdk.stats.MinimumRequiredLevel) && me.getSkill(skillid, sdk.skills.subindex.HardPoints) < count) { - return true; - } + //a function to return false if have all prereqs or a skill if not + this.needPreReq = function (skillid) { + //a loop to go through each reqskill + for (let t = sdk.stats.PreviousSkillLeft; t >= sdk.stats.PreviousSkillRight; t--) { + // Check ReqSkills + let preReq = (getBaseStat("skills", skillid, t)); + + if (preReq > sdk.skills.Attack && preReq < 356 && !me.getSkill(preReq, sdk.skills.subindex.HardPoints)) { + return preReq; + } + } + + return false; + }; + + this.skillCheck = function (skillid, count) { + let _hardPoints = me.getSkill(skillid, sdk.skills.subindex.HardPoints); + if (_hardPoints <= me.charlvl - getBaseStat("skills", skillid, sdk.stats.MinimumRequiredLevel) + && _hardPoints < count) { + return true; + } + + return false; + }; + + this.skillToAdd = function (inputArray) { + for (let i = 0; i < inputArray.length; i += 1) { + // limit maximum allocation count to 20 + if (inputArray[i][1] > 20) { + print( + "AutoSkill: Skill build index " + i + " has allocation count of " + + inputArray[i][1] + " and it will be limited to 20" + ); + inputArray[i][1] = 20; + } + + // set satify condition as default if not specified + if (inputArray[i][2] === undefined) { + inputArray[i][2] = true; + } + + // check to see if skill count in previous array is satisfied + const _prevHardPoints = me.getSkill(inputArray[i - 1][0], sdk.skills.subindex.HardPoints); + if (i > 0 && inputArray[i - 1][2] + && (!_prevHardPoints ? 0 : _prevHardPoints) < inputArray[i - 1][1]) { + return false; + } + + if (me.getSkill(inputArray[i][0], sdk.skills.subindex.HardPoints) + && this.skillCheck(inputArray[i][0], inputArray[i][1])) { + return inputArray[i][0]; + } + + let reqIn; + let reqOut = this.needPreReq(inputArray[i][0]); + + if (!reqOut && this.skillCheck(inputArray[i][0], inputArray[i][1])) { + return inputArray[i][0]; + } + + while (reqOut) { + reqIn = reqOut; + reqOut = this.needPreReq(reqIn); + } + + if (this.skillCheck(reqIn, 1)) { + return reqIn; + } + } + + return false; + }; + + this.allocate = function () { + let tick = getTickCount(); + + this.remaining = me.getStat(sdk.stats.NewSkills); + + if (!getUIFlag(sdk.uiflags.TradePrompt)) { + let addTo = this.skillToAdd(this.skillBuildOrder); + + if (addTo) { + print("AutoSkill: Using skill point in Skill: " + getSkillById(addTo) + " ID: " + addTo); + delay(100); + useSkillPoint(addTo, 1); + } + } + + while (getTickCount() - tick < 1500 + 2 * me.ping) { + if (this.remaining > me.getStat(sdk.stats.NewSkills)) { + return true; + } + + delay(100); + } + + return false; + }; + + this.remaining = 0; + this.count = 0; + + this.init = function (skillBuildOrder, save = 0) { + this.skillBuildOrder = skillBuildOrder; + this.save = save; + + if (!this.skillBuildOrder || !this.skillBuildOrder.length) { + print("AutoSkill: No build array specified"); + + return false; + } + + while (me.getStat(sdk.stats.NewSkills) > this.save) { + this.allocate(); + delay(200 + me.ping); // may need longer delay under high ping + + // break out of loop if we have skill points available but cannot allocate further due to unsatisfied skill + if (me.getStat(sdk.stats.NewSkills) === this.remaining) { + this.count += 1; + } + + if (this.count > 2) { + break; + } + } + + print("AutoSkill: Finished allocating skill points"); + + return true; + }; - return false; - }; - - this.skillToAdd = function (inputArray) { - for (let i = 0; i < inputArray.length; i += 1) { - // limit maximum allocation count to 20 - if (inputArray[i][1] > 20) { - print("AutoSkill: Skill build index " + i + " has allocation count of " + inputArray[i][1] + " and it will be limited to 20"); - inputArray[i][1] = 20; - } - - // set satify condition as default if not specified - if (inputArray[i][2] === undefined) { - inputArray[i][2] = true; - } - - // check to see if skill count in previous array is satisfied - if (i > 0 && inputArray[i - 1][2] && (!me.getSkill(inputArray[i - 1][0], sdk.skills.subindex.HardPoints) ? 0 : me.getSkill(inputArray[i - 1][0], sdk.skills.subindex.HardPoints)) < inputArray[i - 1][1]) { - return false; - } - - if (me.getSkill(inputArray[i][0], sdk.skills.subindex.HardPoints) && this.skillCheck(inputArray[i][0], inputArray[i][1])) { - return inputArray[i][0]; - } - - let reqIn; - let reqOut = this.needPreReq(inputArray[i][0]); - - if (!reqOut && this.skillCheck(inputArray[i][0], inputArray[i][1])) { - return inputArray[i][0]; - } - - while (reqOut) { - reqIn = reqOut; - reqOut = this.needPreReq(reqIn); - } - - if (this.skillCheck(reqIn, 1)) { - return reqIn; - } - } - - return false; - }; - - this.allocate = function () { - let tick = getTickCount(); - - this.remaining = me.getStat(sdk.stats.NewSkills); - - if (!getUIFlag(sdk.uiflags.TradePrompt)) { - let addTo = this.skillToAdd(this.skillBuildOrder); - - if (addTo) { - print("AutoSkill: Using skill point in Skill: " + getSkillById(addTo) + " ID: " + addTo); - delay(100); - useSkillPoint(addTo, 1); - } - } - - while (getTickCount() - tick < 1500 + 2 * me.ping) { - if (this.remaining > me.getStat(sdk.stats.NewSkills)) { - return true; - } - - delay(100); - } - - return false; - }; - - this.remaining = 0; - this.count = 0; - - this.init = function (skillBuildOrder, save = 0) { - this.skillBuildOrder = skillBuildOrder; - this.save = save; - - if (!this.skillBuildOrder || !this.skillBuildOrder.length) { - print("AutoSkill: No build array specified"); - - return false; - } - - while (me.getStat(sdk.stats.NewSkills) > this.save) { - this.allocate(); - delay(200 + me.ping); // may need longer delay under high ping - - // break out of loop if we have skill points available but cannot allocate further due to unsatisfied skill - if (me.getStat(sdk.stats.NewSkills) === this.remaining) { - this.count += 1; - } - - if (this.count > 2) { - break; - } - } - - print("AutoSkill: Finished allocating skill points"); - - return true; - }; - - return true; + return true; }; diff --git a/d2bs/kolbot/libs/core/Auto/AutoStat.js b/d2bs/kolbot/libs/core/Auto/AutoStat.js index b58fd31c1..a76ebbe43 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoStat.js +++ b/d2bs/kolbot/libs/core/Auto/AutoStat.js @@ -7,12 +7,12 @@ */ const AutoStat = new function () { - this.statBuildOrder = []; - this.save = 0; - this.block = 0; - this.bulkStat = true; + this.statBuildOrder = []; + this.save = 0; + this.block = 0; + this.bulkStat = true; - /* statBuildOrder - array of stat points to spend in order + /* statBuildOrder - array of stat points to spend in order save - remaining stat points that will not be spent and saved. block - an integer value set to desired block chance. This is ignored in classic. bulkStat - set true to spend multiple stat points at once (up to 100), or false to spend 1 point at a time. @@ -30,701 +30,722 @@ const AutoStat = new function () { ]; */ - this.getBlock = function () { - if (!me.usingShield()) return this.block; - - // cast holy shield if available - if (Skill.canUse(sdk.skills.HolyShield) && !me.getState(sdk.states.HolyShield)) { - if (Precast.cast(sdk.skills.HolyShield)) { - delay(1000); - } else { - return this.block; - } - } - - if (me.classic) { - return Math.floor(me.getStat(sdk.stats.ToBlock) + getBaseStat(15, me.classid, 23)); - } - - return Math.min(75, Math.floor((me.getStat(sdk.stats.ToBlock) + getBaseStat(15, me.classid, 23)) * (me.getStat(sdk.stats.Dexterity) - 15) / (me.charlvl * 2))); - }; - - // this check may not be necessary with this.validItem(), but consider it double check - // verify that the set bonuses are there - this.verifySetStats = function (unit, type, stats) { - let string = type === sdk.stats.Strength ? sdk.locale.text.ToStrength : sdk.locale.text.ToDexterity; - - if (unit) { - let temp = unit.description.split("\n"); - - for (let i = 0; i < temp.length; i += 1) { - if (temp[i].match(getLocaleString(string), "i")) { - if (parseInt(temp[i].replace(/(y|ÿ)c[0-9!"+<;.*]/, ""), 10) === stats) { - return true; - } - } - } - } - - return false; - }; - - this.validItem = function (item) { - // ignore item bonuses from secondary weapon slot - if (me.expansion && item.isOnSwap) return false; - // check if character meets str, dex, and level requirement since stat bonuses only apply when they are active - return me.getStat(sdk.stats.Strength) >= item.strreq && me.getStat(sdk.stats.Dexterity) >= item.dexreq && me.charlvl >= item.lvlreq; - }; - - // get stats from set bonuses - this.setBonus = function (type) { - // set bonuses do not have energy or vitality (we can ignore this) - if (type === sdk.stats.Energy || type === sdk.stats.Vitality) return 0; - - // these are the only sets with possible stat bonuses - let sets = { - "angelic": [], "artic": [], "civerb": [], "iratha": [], - "isenhart": [], "vidala": [], "cowking": [], "disciple": [], - "griswold": [], "mavina": [], "naj": [], "orphan": [] - }; - - let i, j, setStat = 0; - let items = me.getItems(); - - if (items) { - for (i = 0; i < items.length; i += 1) { - if (items[i].isEquipped && items[i].set && this.validItem(items[i])) { - idSwitch: - switch (items[i].classid) { - case sdk.items.Crown: - if (items[i].getStat(sdk.stats.LightResist) === 30) { - sets.iratha.push(items[i]); - } - - break; - case sdk.items.LightGauntlets: - if (items[i].getStat(sdk.stats.MaxHp) === 20) { - sets.artic.push(items[i]); - } else if (items[i].getStat(sdk.stats.ColdResist) === 30) { - sets.iratha.push(items[i]); - } - - break; - case sdk.items.HeavyBoots: - if (items[i].getStat(sdk.stats.Dexterity) === 20) { - sets.cowking.push(items[i]); - } - - break; - case sdk.items.HeavyBelt: - if (items[i].getStat(sdk.stats.MinDamage) === 5) { - sets.iratha.push(items[i]); - } - - break; - case sdk.items.Amulet: - if (items[i].getStat(sdk.stats.DamagetoMana) === 20) { - sets.angelic.push(items[i]); - } else if (items[i].getStat(sdk.stats.HpRegen) === 4) { - sets.civerb.push(items[i]); - } else if (items[i].getStat(sdk.stats.PoisonLengthResist) === 75) { - sets.iratha.push(items[i]); - } else if (items[i].getStat(sdk.stats.ColdResist) === 20) { - sets.vidala.push(items[i]); - } else if (items[i].getStat(sdk.stats.ColdResist) === 18) { - sets.disciple.push(items[i]); - } - - break; - case sdk.items.Ring: - if (items[i].getStat(sdk.stats.HpRegen) === 6) { - // do not count ring twice - for (j = 0; j < sets.angelic.length; j += 1) { - if (sets.angelic[j].classid === items[i].classid) { - break idSwitch; - } - } - - sets.angelic.push(items[i]); - } - - break; - case sdk.items.Sabre: - // do not count twice in case of dual wield - for (j = 0; j < sets.angelic.length; j += 1) { - if (sets.angelic[j].classid === items[i].classid) { - break idSwitch; - } - } - - sets.angelic.push(items[i]); - - break; - case sdk.items.RingMail: - sets.angelic.push(items[i]); - - break; - case sdk.items.ShortWarBow: - case sdk.items.QuiltedArmor: - case sdk.items.LightBelt: - sets.artic.push(items[i]); - - break; - case sdk.items.GrandScepter: - // do not count twice in case of dual wield - for (j = 0; j < sets.civerb.length; j += 1) { - if (sets.civerb[j].classid === items[i].classid) { - break idSwitch; - } - } - - sets.civerb.push(items[i]); - - break; - case sdk.items.LargeShield: - sets.civerb.push(items[i]); - - break; - case sdk.items.BroadSword: - // do not count twice in case of dual wield - for (j = 0; j < sets.isenhart.length; j += 1) { - if (sets.isenhart[j].classid === items[i].classid) { - break idSwitch; - } - } - - sets.isenhart.push(items[i]); - - break; - case sdk.items.FullHelm: - case sdk.items.BreastPlate: - case sdk.items.GothicShield: - sets.isenhart.push(items[i]); - - break; - case sdk.items.LongBattleBow: - case sdk.items.LeatherArmor: - case sdk.items.LightPlatedBoots: - sets.vidala.push(items[i]); - - break; - case sdk.items.StuddedLeather: - case sdk.items.WarHat: - sets.cowking.push(items[i]); - - break; - case sdk.items.DemonhideBoots: - case sdk.items.DuskShroud: - case sdk.items.BrambleMitts: - case sdk.items.MithrilCoil: - sets.disciple.push(items[i]); - - break; - case sdk.items.Caduceus: - // do not count twice in case of dual wield - for (j = 0; j < sets.griswold.length; j += 1) { - if (sets.griswold[j].classid === items[i].classid) { - break idSwitch; - } - } - - sets.griswold.push(items[i]); - - break; - case sdk.items.OrnatePlate: - case sdk.items.Corona: - case sdk.items.VortexShield: - sets.griswold.push(items[i]); - - break; - case sdk.items.GrandMatronBow: - case sdk.items.BattleGauntlets: - case sdk.items.SharkskinBelt: - case sdk.items.Diadem: - case sdk.items.KrakenShell: - sets.mavina.push(items[i]); - - break; - case sdk.items.ElderStaff: - case sdk.items.Circlet: - case sdk.items.HellforgePlate: - sets.naj.push(items[i]); - - break; - case sdk.items.WingedHelm: - case sdk.items.RoundShield: - case sdk.items.SharkskinGloves: - case sdk.items.BattleBelt: - sets.orphan.push(items[i]); - - break; - } - } - } - } - - for (i in sets) { - if (sets.hasOwnProperty(i)) { - MainSwitch: - switch (i) { - case "angelic": - if (sets[i].length >= 2 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 10)) { - break MainSwitch; - } - } - - setStat += 10; - } - - break; - case "artic": - if (sets[i].length >= 2 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 5)) { - break MainSwitch; - } - } - - setStat += 5; - } - - break; - case "civerb": - if (sets[i].length === 3 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 15)) { - break MainSwitch; - } - } - - setStat += 15; - } - - break; - case "iratha": - if (sets[i].length === 4 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 15)) { - break MainSwitch; - } - } - - setStat += 15; - } - - break; - case "isenhart": - if (sets[i].length >= 2 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 10)) { - break MainSwitch; - } - } - - setStat += 10; - } - - if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 10)) { - break MainSwitch; - } - } - - setStat += 10; - } - - break; - case "vidala": - if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 15)) { - break MainSwitch; - } - } - - setStat += 15; - } - - if (sets[i].length === 4 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 10)) { - break MainSwitch; - } - } - - setStat += 10; - } - - break; - case "cowking": - if (sets[i].length === 3 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 20)) { - break MainSwitch; - } - } - - setStat += 20; - } - - break; - case "disciple": - if (sets[i].length >= 4 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 10)) { - break MainSwitch; - } - } - - setStat += 10; - } - - break; - case "griswold": - if (sets[i].length >= 2 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 20)) { - break MainSwitch; - } - } - - setStat += 20; - } - - if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 30)) { - break MainSwitch; - } - } - - setStat += 30; - } - - break; - case "mavina": - if (sets[i].length >= 2 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 20)) { - break MainSwitch; - } - } - - setStat += 20; - } - - if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 30)) { - break MainSwitch; - } - } - - setStat += 30; - } - - break; - case "naj": - if (sets[i].length === 3 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 15)) { - break MainSwitch; - } - } - - setStat += 15; - } - - if (sets[i].length === 3 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 20)) { - break MainSwitch; - } - } - - setStat += 20; - } - - break; - case "orphan": - if (sets[i].length === 4 && type === sdk.stats.Dexterity) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 10)) { - break MainSwitch; - } - } - - setStat += 10; - } - - if (sets[i].length === 4 && type === sdk.stats.Strength) { - for (j = 0; j < sets[i].length; j += 1) { - if (!this.verifySetStats(sets[i][j], type, 20)) { - break MainSwitch; - } - } - - setStat += 20; - } - - break; - } - } - } - - return setStat; - }; - - // return stat values excluding stat bonuses from sets and/or items - this.getHardStats = function (type) { - let i, statID; - let addedStat = 0; - let items = me.getItems(); - - switch (type) { - case sdk.stats.Strength: - type = sdk.stats.Strength; - statID = sdk.stats.PerLevelStrength; - - break; - case sdk.stats.Energy: - type = sdk.stats.Energy; - statID = sdk.stats.PerLevelEnergy; - - break; - case sdk.stats.Dexterity: - type = sdk.stats.Dexterity; - statID = sdk.stats.PerLevelDexterity; - - break; - case sdk.stats.Vitality: - type = sdk.stats.Vitality; - statID = sdk.stats.PerLevelVitality; - - break; - } - - if (items) { - for (i = 0; i < items.length; i += 1) { - // items equipped or charms in inventory - if ((items[i].isEquipped || items[i].isEquippedCharm) && this.validItem(items[i])) { - // stats - items[i].getStat(type) && (addedStat += items[i].getStat(type)); - - // stats per level - if (items[i].getStat(statID)) { - addedStat += Math.floor(items[i].getStat(statID) / 8 * me.charlvl); - } - } - } - } - - return (me.getStat(type) - addedStat - this.setBonus(type)); - }; - - this.requiredDex = function () { - let set = false; - let inactiveDex = 0; - let items = me.getItems(); - - if (items) { - for (let i = 0; i < items.length; i += 1) { - // items equipped but inactive (these are possible dex sources unseen by me.getStat(sdk.stats.Dexterity)) - if (items[i].isEquipped && !items[i].isOnSwap && !this.validItem(items[i])) { - if (items[i].quality === sdk.items.quality.Set) { - set = true; - - break; - } - - // stats - items[i].getStat(sdk.stats.Dexterity) && (inactiveDex += items[i].getStat(sdk.stats.Dexterity)); - - // stats per level - if (items[i].getStat(sdk.stats.PerLevelDexterity)) { - inactiveDex += Math.floor(items[i].getStat(sdk.stats.PerLevelDexterity) / 8 * me.charlvl); - } - } - } - } - - // just stat 1 at a time if there's set item (there could be dex bonus for currently inactive set) - if (set) { - return 1; - } - - // returns amount of dexterity required to get the desired block chance - return Math.ceil((2 * me.charlvl * this.block) / (me.getStat(sdk.stats.ToBlock) + getBaseStat(15, me.classid, 23)) + 15) - me.getStat(sdk.stats.Dexterity) - inactiveDex; - }; - - this.useStats = function (type, goal = false) { - let currStat = me.getStat(sdk.stats.StatPts); - let tick = getTickCount(); - let statIDToString = [ - getLocaleString(sdk.locale.text.Strength), getLocaleString(sdk.locale.text.Energy), - getLocaleString(sdk.locale.text.Dexterity), getLocaleString(sdk.locale.text.Vitality) - ]; - - // use 0x3a packet to spend multiple stat points at once (up to 100) - if (this.bulkStat) { - if (goal) { - sendPacket(1, sdk.packets.send.AddStat, 1, type, 1, Math.min(me.getStat(sdk.stats.StatPts) - this.save - 1, goal - 1, 99)); - } else { - sendPacket(1, sdk.packets.send.AddStat, 1, type, 1, Math.min(me.getStat(sdk.stats.StatPts) - this.save - 1, 99)); - } - } else { - useStatPoint(type); - } - - while (getTickCount() - tick < 3000) { - if (currStat > me.getStat(sdk.stats.StatPts)) { - print("AutoStat: Using " + (currStat - me.getStat(sdk.stats.StatPts)) + " stat points in " + statIDToString[type]); - return true; - } - - delay(100); - } - - return false; - }; - - this.addStatPoint = function () { - this.remaining = me.getStat(sdk.stats.StatPts); + this.getBlock = function () { + if (!me.usingShield()) return this.block; + + // cast holy shield if available + if (Skill.canUse(sdk.skills.HolyShield) && !me.getState(sdk.states.HolyShield)) { + if (Precast.cast(sdk.skills.HolyShield)) { + delay(1000); + } else { + return this.block; + } + } + + if (me.classic) { + return Math.floor(me.getStat(sdk.stats.ToBlock) + getBaseStat(15, me.classid, 23)); + } + + return Math.min( + 75, + Math.floor( + (me.getStat(sdk.stats.ToBlock) + getBaseStat(15, me.classid, 23)) + * (me.getStat(sdk.stats.Dexterity) - 15) / (me.charlvl * 2) + ) + ); + }; + + // this check may not be necessary with this.validItem(), but consider it double check + // verify that the set bonuses are there + this.verifySetStats = function (unit, type, stats) { + let string = type === sdk.stats.Strength ? sdk.locale.text.ToStrength : sdk.locale.text.ToDexterity; + + if (unit) { + let temp = unit.description.split("\n"); + + for (let i = 0; i < temp.length; i += 1) { + if (temp[i].match(getLocaleString(string), "i")) { + if (parseInt(temp[i].replace(/(y|ÿ)c[0-9!"+<;.*]/, ""), 10) === stats) { + return true; + } + } + } + } + + return false; + }; + + this.validItem = function (item) { + // ignore item bonuses from secondary weapon slot + if (me.expansion && item.isOnSwap) return false; + // check if character meets str, dex, and level requirement since stat bonuses only apply when they are active + return me.getStat(sdk.stats.Strength) >= item.strreq + && me.getStat(sdk.stats.Dexterity) >= item.dexreq + && me.charlvl >= item.lvlreq; + }; + + // get stats from set bonuses + this.setBonus = function (type) { + // set bonuses do not have energy or vitality (we can ignore this) + if (type === sdk.stats.Energy || type === sdk.stats.Vitality) return 0; + + // these are the only sets with possible stat bonuses + let sets = { + "angelic": [], "artic": [], "civerb": [], "iratha": [], + "isenhart": [], "vidala": [], "cowking": [], "disciple": [], + "griswold": [], "mavina": [], "naj": [], "orphan": [] + }; + + let i, j, setStat = 0; + let items = me.getItems(); + + if (items) { + for (i = 0; i < items.length; i += 1) { + if (items[i].isEquipped && items[i].set && this.validItem(items[i])) { + idSwitch: + switch (items[i].classid) { + case sdk.items.Crown: + if (items[i].getStat(sdk.stats.LightResist) === 30) { + sets.iratha.push(items[i]); + } + + break; + case sdk.items.LightGauntlets: + if (items[i].getStat(sdk.stats.MaxHp) === 20) { + sets.artic.push(items[i]); + } else if (items[i].getStat(sdk.stats.ColdResist) === 30) { + sets.iratha.push(items[i]); + } + + break; + case sdk.items.HeavyBoots: + if (items[i].getStat(sdk.stats.Dexterity) === 20) { + sets.cowking.push(items[i]); + } + + break; + case sdk.items.HeavyBelt: + if (items[i].getStat(sdk.stats.MinDamage) === 5) { + sets.iratha.push(items[i]); + } + + break; + case sdk.items.Amulet: + if (items[i].getStat(sdk.stats.DamagetoMana) === 20) { + sets.angelic.push(items[i]); + } else if (items[i].getStat(sdk.stats.HpRegen) === 4) { + sets.civerb.push(items[i]); + } else if (items[i].getStat(sdk.stats.PoisonLengthResist) === 75) { + sets.iratha.push(items[i]); + } else if (items[i].getStat(sdk.stats.ColdResist) === 20) { + sets.vidala.push(items[i]); + } else if (items[i].getStat(sdk.stats.ColdResist) === 18) { + sets.disciple.push(items[i]); + } + + break; + case sdk.items.Ring: + if (items[i].getStat(sdk.stats.HpRegen) === 6) { + // do not count ring twice + for (j = 0; j < sets.angelic.length; j += 1) { + if (sets.angelic[j].classid === items[i].classid) { + break idSwitch; + } + } + + sets.angelic.push(items[i]); + } + + break; + case sdk.items.Sabre: + // do not count twice in case of dual wield + for (j = 0; j < sets.angelic.length; j += 1) { + if (sets.angelic[j].classid === items[i].classid) { + break idSwitch; + } + } + + sets.angelic.push(items[i]); + + break; + case sdk.items.RingMail: + sets.angelic.push(items[i]); + + break; + case sdk.items.ShortWarBow: + case sdk.items.QuiltedArmor: + case sdk.items.LightBelt: + sets.artic.push(items[i]); + + break; + case sdk.items.GrandScepter: + // do not count twice in case of dual wield + for (j = 0; j < sets.civerb.length; j += 1) { + if (sets.civerb[j].classid === items[i].classid) { + break idSwitch; + } + } + + sets.civerb.push(items[i]); + + break; + case sdk.items.LargeShield: + sets.civerb.push(items[i]); + + break; + case sdk.items.BroadSword: + // do not count twice in case of dual wield + for (j = 0; j < sets.isenhart.length; j += 1) { + if (sets.isenhart[j].classid === items[i].classid) { + break idSwitch; + } + } + + sets.isenhart.push(items[i]); + + break; + case sdk.items.FullHelm: + case sdk.items.BreastPlate: + case sdk.items.GothicShield: + sets.isenhart.push(items[i]); + + break; + case sdk.items.LongBattleBow: + case sdk.items.LeatherArmor: + case sdk.items.LightPlatedBoots: + sets.vidala.push(items[i]); + + break; + case sdk.items.StuddedLeather: + case sdk.items.WarHat: + sets.cowking.push(items[i]); + + break; + case sdk.items.DemonhideBoots: + case sdk.items.DuskShroud: + case sdk.items.BrambleMitts: + case sdk.items.MithrilCoil: + sets.disciple.push(items[i]); + + break; + case sdk.items.Caduceus: + // do not count twice in case of dual wield + for (j = 0; j < sets.griswold.length; j += 1) { + if (sets.griswold[j].classid === items[i].classid) { + break idSwitch; + } + } + + sets.griswold.push(items[i]); + + break; + case sdk.items.OrnatePlate: + case sdk.items.Corona: + case sdk.items.VortexShield: + sets.griswold.push(items[i]); + + break; + case sdk.items.GrandMatronBow: + case sdk.items.BattleGauntlets: + case sdk.items.SharkskinBelt: + case sdk.items.Diadem: + case sdk.items.KrakenShell: + sets.mavina.push(items[i]); + + break; + case sdk.items.ElderStaff: + case sdk.items.Circlet: + case sdk.items.HellforgePlate: + sets.naj.push(items[i]); + + break; + case sdk.items.WingedHelm: + case sdk.items.RoundShield: + case sdk.items.SharkskinGloves: + case sdk.items.BattleBelt: + sets.orphan.push(items[i]); + + break; + } + } + } + } + + for (i in sets) { + if (sets.hasOwnProperty(i)) { + MainSwitch: + switch (i) { + case "angelic": + if (sets[i].length >= 2 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 10)) { + break MainSwitch; + } + } + + setStat += 10; + } + + break; + case "artic": + if (sets[i].length >= 2 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 5)) { + break MainSwitch; + } + } + + setStat += 5; + } + + break; + case "civerb": + if (sets[i].length === 3 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 15)) { + break MainSwitch; + } + } + + setStat += 15; + } + + break; + case "iratha": + if (sets[i].length === 4 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 15)) { + break MainSwitch; + } + } + + setStat += 15; + } + + break; + case "isenhart": + if (sets[i].length >= 2 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 10)) { + break MainSwitch; + } + } + + setStat += 10; + } + + if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 10)) { + break MainSwitch; + } + } + + setStat += 10; + } + + break; + case "vidala": + if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 15)) { + break MainSwitch; + } + } + + setStat += 15; + } + + if (sets[i].length === 4 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 10)) { + break MainSwitch; + } + } + + setStat += 10; + } + + break; + case "cowking": + if (sets[i].length === 3 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 20)) { + break MainSwitch; + } + } + + setStat += 20; + } + + break; + case "disciple": + if (sets[i].length >= 4 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 10)) { + break MainSwitch; + } + } + + setStat += 10; + } + + break; + case "griswold": + if (sets[i].length >= 2 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 20)) { + break MainSwitch; + } + } + + setStat += 20; + } + + if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 30)) { + break MainSwitch; + } + } + + setStat += 30; + } + + break; + case "mavina": + if (sets[i].length >= 2 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 20)) { + break MainSwitch; + } + } + + setStat += 20; + } + + if (sets[i].length >= 3 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 30)) { + break MainSwitch; + } + } + + setStat += 30; + } + + break; + case "naj": + if (sets[i].length === 3 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 15)) { + break MainSwitch; + } + } + + setStat += 15; + } + + if (sets[i].length === 3 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 20)) { + break MainSwitch; + } + } + + setStat += 20; + } + + break; + case "orphan": + if (sets[i].length === 4 && type === sdk.stats.Dexterity) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 10)) { + break MainSwitch; + } + } + + setStat += 10; + } + + if (sets[i].length === 4 && type === sdk.stats.Strength) { + for (j = 0; j < sets[i].length; j += 1) { + if (!this.verifySetStats(sets[i][j], type, 20)) { + break MainSwitch; + } + } + + setStat += 20; + } + + break; + } + } + } + + return setStat; + }; + + // return stat values excluding stat bonuses from sets and/or items + this.getHardStats = function (type) { + let i, statID; + let addedStat = 0; + let items = me.getItems(); + + switch (type) { + case sdk.stats.Strength: + type = sdk.stats.Strength; + statID = sdk.stats.PerLevelStrength; + + break; + case sdk.stats.Energy: + type = sdk.stats.Energy; + statID = sdk.stats.PerLevelEnergy; + + break; + case sdk.stats.Dexterity: + type = sdk.stats.Dexterity; + statID = sdk.stats.PerLevelDexterity; + + break; + case sdk.stats.Vitality: + type = sdk.stats.Vitality; + statID = sdk.stats.PerLevelVitality; + + break; + } + + if (items) { + for (i = 0; i < items.length; i += 1) { + // items equipped or charms in inventory + if ((items[i].isEquipped || items[i].isEquippedCharm) && this.validItem(items[i])) { + // stats + items[i].getStat(type) && (addedStat += items[i].getStat(type)); + + // stats per level + if (items[i].getStat(statID)) { + addedStat += Math.floor(items[i].getStat(statID) / 8 * me.charlvl); + } + } + } + } + + return (me.getStat(type) - addedStat - this.setBonus(type)); + }; + + this.requiredDex = function () { + let set = false; + let inactiveDex = 0; + let items = me.getItems(); + + if (items) { + for (let i = 0; i < items.length; i += 1) { + // items equipped but inactive (these are possible dex sources unseen by me.getStat(sdk.stats.Dexterity)) + if (items[i].isEquipped && !items[i].isOnSwap && !this.validItem(items[i])) { + if (items[i].quality === sdk.items.quality.Set) { + set = true; + + break; + } + + // stats + items[i].getStat(sdk.stats.Dexterity) && (inactiveDex += items[i].getStat(sdk.stats.Dexterity)); + + // stats per level + if (items[i].getStat(sdk.stats.PerLevelDexterity)) { + inactiveDex += Math.floor(items[i].getStat(sdk.stats.PerLevelDexterity) / 8 * me.charlvl); + } + } + } + } + + // just stat 1 at a time if there's set item (there could be dex bonus for currently inactive set) + if (set) { + return 1; + } + + // returns amount of dexterity required to get the desired block chance + return Math.ceil( + (2 * me.charlvl * this.block) / (me.getStat(sdk.stats.ToBlock) + getBaseStat(15, me.classid, 23)) + 15 + ) - me.getStat(sdk.stats.Dexterity) - inactiveDex; + }; + + this.useStats = function (type, goal = false) { + let currStat = me.getStat(sdk.stats.StatPts); + let tick = getTickCount(); + let statIDToString = [ + getLocaleString(sdk.locale.text.Strength), getLocaleString(sdk.locale.text.Energy), + getLocaleString(sdk.locale.text.Dexterity), getLocaleString(sdk.locale.text.Vitality) + ]; + + // use 0x3a packet to spend multiple stat points at once (up to 100) + if (this.bulkStat) { + if (goal) { + new PacketBuilder() + .byte(sdk.packets.send.AddStat) + .byte(type) + .byte(Math.min(me.getStat(sdk.stats.StatPts) - this.save - 1, goal - 1, 99)) + .send(); + } else { + new PacketBuilder() + .byte(sdk.packets.send.AddStat) + .byte(type) + .byte(Math.min(me.getStat(sdk.stats.StatPts) - this.save - 1, 99)) + .send(); + } + } else { + useStatPoint(type); + } + + while (getTickCount() - tick < 3000) { + if (currStat > me.getStat(sdk.stats.StatPts)) { + print( + "AutoStat: Using " + (currStat - me.getStat(sdk.stats.StatPts)) + + " stat points in " + statIDToString[type] + ); + return true; + } + + delay(100); + } + + return false; + }; + + this.addStatPoint = function () { + this.remaining = me.getStat(sdk.stats.StatPts); - let hardStats; - - for (let i = 0; i < this.statBuildOrder.length; i += 1) { - switch (this.statBuildOrder[i][0]) { - case sdk.stats.Strength: - case "s": - case "str": - case "strength": - if (typeof this.statBuildOrder[i][1] === "string") { - switch (this.statBuildOrder[i][1]) { - case "all": - return this.useStats(sdk.stats.Strength); - default: - break; - } - } else { - hardStats = this.getHardStats(sdk.stats.Strength); - - if (hardStats < this.statBuildOrder[i][1]) { - return this.useStats(sdk.stats.Strength, this.statBuildOrder[i][1] - hardStats); - } - } - - break; - case sdk.stats.Energy: - case "e": - case "enr": - case "energy": - if (typeof this.statBuildOrder[i][1] === "string") { - switch (this.statBuildOrder[i][1]) { - case "all": - return this.useStats(sdk.stats.Energy); - default: - break; - } - } else { - hardStats = this.getHardStats(sdk.stats.Energy); - - if (hardStats < this.statBuildOrder[i][1]) { - return this.useStats(sdk.stats.Energy, this.statBuildOrder[i][1] - hardStats); - } - } - - break; - case sdk.stats.Dexterity: - case "d": - case "dex": - case "dexterity": - if (typeof this.statBuildOrder[i][1] === "string") { - switch (this.statBuildOrder[i][1]) { - case "block": - if (me.expansion) { - if (this.getBlock() < this.block) { - return this.useStats(sdk.stats.Dexterity, this.requiredDex()); - } - } - - break; - case "all": - return this.useStats(sdk.stats.Dexterity); - default: - break; - } - } else { - hardStats = this.getHardStats(sdk.stats.Dexterity); - - if (hardStats < this.statBuildOrder[i][1]) { - return this.useStats(sdk.stats.Dexterity, this.statBuildOrder[i][1] - hardStats); - } - } - - break; - case sdk.stats.Vitality: - case "v": - case "vit": - case "vitality": - if (typeof this.statBuildOrder[i][1] === "string") { - switch (this.statBuildOrder[i][1]) { - case "all": - return this.useStats(sdk.stats.Vitality); - default: - break; - } - } else { - hardStats = this.getHardStats(sdk.stats.Vitality); - - if (hardStats < this.statBuildOrder[i][1]) { - return this.useStats(sdk.stats.Vitality, this.statBuildOrder[i][1] - hardStats); - } - } - - break; - } - } - - return false; - }; - - this.remaining = 0; - this.count = 0; - - this.init = function (statBuildOrder, save = 0, block = 0, bulkStat = true) { - this.statBuildOrder = statBuildOrder; - this.save = save; - this.block = block; - this.bulkStat = bulkStat; - - if (!this.statBuildOrder || !this.statBuildOrder.length) { - print("AutoStat: No build array specified"); - - return false; - } - - while (me.getStat(sdk.stats.StatPts) > this.save) { - this.addStatPoint(); - 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) === this.remaining) { - this.count += 1; - } - - if (this.count > 2) { - break; - } - } - - print("AutoStat: Finished allocating stat points"); - - return true; - }; - - return true; + let hardStats; + + for (let i = 0; i < this.statBuildOrder.length; i += 1) { + switch (this.statBuildOrder[i][0]) { + case sdk.stats.Strength: + case "s": + case "str": + case "strength": + if (typeof this.statBuildOrder[i][1] === "string") { + switch (this.statBuildOrder[i][1]) { + case "all": + return this.useStats(sdk.stats.Strength); + default: + break; + } + } else { + hardStats = this.getHardStats(sdk.stats.Strength); + + if (hardStats < this.statBuildOrder[i][1]) { + return this.useStats(sdk.stats.Strength, this.statBuildOrder[i][1] - hardStats); + } + } + + break; + case sdk.stats.Energy: + case "e": + case "enr": + case "energy": + if (typeof this.statBuildOrder[i][1] === "string") { + switch (this.statBuildOrder[i][1]) { + case "all": + return this.useStats(sdk.stats.Energy); + default: + break; + } + } else { + hardStats = this.getHardStats(sdk.stats.Energy); + + if (hardStats < this.statBuildOrder[i][1]) { + return this.useStats(sdk.stats.Energy, this.statBuildOrder[i][1] - hardStats); + } + } + + break; + case sdk.stats.Dexterity: + case "d": + case "dex": + case "dexterity": + if (typeof this.statBuildOrder[i][1] === "string") { + switch (this.statBuildOrder[i][1]) { + case "block": + if (me.expansion) { + if (this.getBlock() < this.block) { + return this.useStats(sdk.stats.Dexterity, this.requiredDex()); + } + } + + break; + case "all": + return this.useStats(sdk.stats.Dexterity); + default: + break; + } + } else { + hardStats = this.getHardStats(sdk.stats.Dexterity); + + if (hardStats < this.statBuildOrder[i][1]) { + return this.useStats(sdk.stats.Dexterity, this.statBuildOrder[i][1] - hardStats); + } + } + + break; + case sdk.stats.Vitality: + case "v": + case "vit": + case "vitality": + if (typeof this.statBuildOrder[i][1] === "string") { + switch (this.statBuildOrder[i][1]) { + case "all": + return this.useStats(sdk.stats.Vitality); + default: + break; + } + } else { + hardStats = this.getHardStats(sdk.stats.Vitality); + + if (hardStats < this.statBuildOrder[i][1]) { + return this.useStats(sdk.stats.Vitality, this.statBuildOrder[i][1] - hardStats); + } + } + + break; + } + } + + return false; + }; + + this.remaining = 0; + this.count = 0; + + this.init = function (statBuildOrder, save = 0, block = 0, bulkStat = true) { + this.statBuildOrder = statBuildOrder; + this.save = save; + this.block = block; + this.bulkStat = bulkStat; + + if (!this.statBuildOrder || !this.statBuildOrder.length) { + print("AutoStat: No build array specified"); + + return false; + } + + while (me.getStat(sdk.stats.StatPts) > this.save) { + this.addStatPoint(); + 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) === this.remaining) { + this.count += 1; + } + + if (this.count > 2) { + break; + } + } + + print("AutoStat: Finished allocating stat points"); + + return true; + }; + + return true; }; diff --git a/d2bs/kolbot/libs/core/CollMap.js b/d2bs/kolbot/libs/core/CollMap.js index d9d65239c..deafbdc90 100644 --- a/d2bs/kolbot/libs/core/CollMap.js +++ b/d2bs/kolbot/libs/core/CollMap.js @@ -6,177 +6,182 @@ */ const CollMap = new function () { - this.rooms = []; - this.maps = []; + this.rooms = []; + this.maps = []; - this.getNearbyRooms = function (x, y) { - let room = getRoom(x, y); - if (!room) return false; + this.getNearbyRooms = function (x, y) { + let room = getRoom(x, y); + if (!room) return false; - let rooms = room.getNearby(); - if (!rooms) return false; + let rooms = room.getNearby(); + if (!rooms) return false; - for (let i = 0; i < rooms.length; i += 1) { - if (this.getRoomIndex(rooms[i].x * 5 + rooms[i].xsize / 2, rooms[i].y * 5 + rooms[i].ysize / 2, true) === undefined) { - this.addRoom(rooms[i]); - } - } + for (let i = 0; i < rooms.length; i += 1) { + let [rX, rY] = [rooms[i].x * 5 + rooms[i].xsize / 2, rooms[i].y * 5 + rooms[i].ysize / 2]; + if (this.getRoomIndex(rX, rY, true) === undefined) { + this.addRoom(rooms[i]); + } + } - return true; - }; + return true; + }; - this.addRoom = function (x, y) { - let room = x instanceof Room ? x : getRoom(x, y); + this.addRoom = function (x, y) { + let room = x instanceof Room ? x : getRoom(x, y); - // Coords are not in the returned room. - if (arguments.length === 2 && !this.coordsInRoom(x, y, room)) { - return false; - } + // Coords are not in the returned room. + if (arguments.length === 2 && !this.coordsInRoom(x, y, room)) { + return false; + } - let coll = !!room ? room.getCollision() : null; + let coll = !!room ? room.getCollision() : null; - if (coll) { - this.rooms.push({ x: room.x, y: room.y, xsize: room.xsize, ysize: room.ysize }); - this.maps.push(coll); + if (coll) { + this.rooms.push({ x: room.x, y: room.y, xsize: room.xsize, ysize: room.ysize }); + this.maps.push(coll); - return true; - } + return true; + } + + return false; + }; + + this.getColl = function (x, y, cacheOnly) { + let index = this.getRoomIndex(x, y, cacheOnly); - return false; - }; + if (index === undefined) { + return 5; + } - this.getColl = function (x, y, cacheOnly) { - let index = this.getRoomIndex(x, y, cacheOnly); + let j = x - this.rooms[index].x * 5; + let i = y - this.rooms[index].y * 5; - if (index === undefined) { - return 5; - } + if (this.maps[index] !== undefined && this.maps[index][i] !== undefined && this.maps[index][i][j] !== undefined) { + return this.maps[index][i][j]; + } - let j = x - this.rooms[index].x * 5; - let i = y - this.rooms[index].y * 5; + return 5; + }; - if (this.maps[index] !== undefined && this.maps[index][i] !== undefined && this.maps[index][i][j] !== undefined) { - return this.maps[index][i][j]; - } + this.getRoomIndex = function (x, y, cacheOnly) { + this.rooms.length > 25 && this.reset(); + + let i; - return 5; - }; + for (i = 0; i < this.rooms.length; i += 1) { + if (this.coordsInRoom(x, y, this.rooms[i])) { + return i; + } + } - this.getRoomIndex = function (x, y, cacheOnly) { - this.rooms.length > 25 && this.reset(); + if (!cacheOnly && this.addRoom(x, y)) { + return i; + } - let i; + return undefined; + }; - for (i = 0; i < this.rooms.length; i += 1) { - if (this.coordsInRoom(x, y, this.rooms[i])) { - return i; - } - } + this.coordsInRoom = function (x, y, room) { + if (room && x >= room.x * 5 && x < room.x * 5 + room.xsize && y >= room.y * 5 && y < room.y * 5 + room.ysize) { + return true; + } - if (!cacheOnly && this.addRoom(x, y)) { - return i; - } + return false; + }; - return undefined; - }; + this.reset = function () { + this.rooms = []; + this.maps = []; + }; - this.coordsInRoom = function (x, y, room) { - if (room && x >= room.x * 5 && x < room.x * 5 + room.xsize && y >= room.y * 5 && y < room.y * 5 + room.ysize) { - return true; - } + // Check collision between unitA and unitB. true = collision present, false = collision not present + // If checking for blocking collisions (0x1, 0x4), true means blocked, false means not blocked + this.checkColl = function (unitA, unitB, coll, thickness) { + thickness === undefined && (thickness = 1); - return false; - }; + let i, k, l, cx, cy; + let angle = Math.atan2(unitA.y - unitB.y, unitA.x - unitB.x); + let distance = Math.round(getDistance(unitA, unitB)); - this.reset = function () { - this.rooms = []; - this.maps = []; - }; + for (i = 1; i < distance; i += 1) { + cx = Math.round((Math.cos(angle)) * i + unitB.x); + cy = Math.round((Math.sin(angle)) * i + unitB.y); - // Check collision between unitA and unitB. true = collision present, false = collision not present - // If checking for blocking collisions (0x1, 0x4), true means blocked, false means not blocked - this.checkColl = function (unitA, unitB, coll, thickness) { - thickness === undefined && (thickness = 1); + // check thicker line + for (k = cx - thickness; k <= cx + thickness; k += 1) { + for (l = cy - thickness; l <= cy + thickness; l += 1) { + if (this.getColl(k, l, false) & coll) { + return true; + } + } + } + } + + return false; + }; - let i, k, l, cx, cy; - let angle = Math.atan2(unitA.y - unitB.y, unitA.x - unitB.x); - let distance = Math.round(getDistance(unitA, unitB)); + this.getTelePoint = function (room) { + // returns {x, y, distance} of a valid point with lowest distance from room center + // distance is from room center, handy for keeping bot from trying to teleport on walls - for (i = 1; i < distance; i += 1) { - cx = Math.round((Math.cos(angle)) * i + unitB.x); - cy = Math.round((Math.sin(angle)) * i + unitB.y); + if (!room) throw new Error("Invalid room passed to getTelePoint"); - // check thicker line - for (k = cx - thickness; k <= cx + thickness; k += 1) { - for (l = cy - thickness; l <= cy + thickness; l += 1) { - if (this.getColl(k, l, false) & coll) { - return true; - } - } - } - } + let roomx = room.x * 5, roomy = room.y * 5; - return false; - }; + if (getCollision(room.area, roomx, roomy) & 1) { + let collision = room.getCollision(), validTiles = []; + let aMid = Math.round(collision.length / 2), bMid = Math.round(collision[0].length / 2); - this.getTelePoint = function (room) { - // returns {x, y, distance} of a valid point with lowest distance from room center - // distance is from room center, handy for keeping bot from trying to teleport on walls - - if (!room) throw new Error("Invalid room passed to getTelePoint"); - - let roomx = room.x * 5, roomy = room.y * 5; - - if (getCollision(room.area, roomx, roomy) & 1) { - let collision = room.getCollision(), validTiles = []; - let aMid = Math.round(collision.length / 2), bMid = Math.round(collision[0].length / 2); - - for (let a = 0; a < collision.length; a++) { - for (let b = 0; b < collision[a].length; b++) { - if (!(collision[a][b] & 1)) { - validTiles.push({ x: roomx + b - bMid, y: roomy + a - aMid, distance: getDistance(0, 0, a - aMid, b - bMid) }); - } - } - } - - if (validTiles.length) { - validTiles.sort((a, b) => a.distance - b.distance); - - return validTiles[0]; - } - - return null; - } - - return { x: roomx, y: roomy, distance: 0 }; - }; - - this.getRandCoordinate = function (cX, xmin, xmax, cY, ymin, ymax, factor = 1) { - // returns randomized {x, y} object with valid coordinates - let coordX, coordY; - let retry = 0; - - do { - if (retry > 30) { - print("failed to get valid coordinate"); - coordX = cX; - coordY = cY; - - break; - } - - coordX = cX + factor * rand(xmin, xmax); - coordY = cY + factor * rand(ymin, ymax); - - if (cX === coordX && cY === coordY) { // recalculate if same coordiante - coordX = 0; - continue; - } - - retry++; - } while (getCollision(me.area, coordX, coordY) & 1); - - // print("Move " + retry + " from (" + cX + ", " + cY + ") to (" + coordX + ", " + coordY + ")"); - return { x: coordX, y: coordY }; - }; + for (let a = 0; a < collision.length; a++) { + for (let b = 0; b < collision[a].length; b++) { + if (!(collision[a][b] & 1)) { + validTiles.push({ + x: roomx + b - bMid, + y: roomy + a - aMid, + distance: getDistance(0, 0, a - aMid, b - bMid) + }); + } + } + } + + if (validTiles.length) { + validTiles.sort((a, b) => a.distance - b.distance); + + return validTiles[0]; + } + + return null; + } + + return { x: roomx, y: roomy, distance: 0 }; + }; + + this.getRandCoordinate = function (cX, xmin, xmax, cY, ymin, ymax, factor = 1) { + // returns randomized {x, y} object with valid coordinates + let coordX, coordY; + let retry = 0; + + do { + if (retry > 30) { + print("failed to get valid coordinate"); + coordX = cX; + coordY = cY; + + break; + } + + coordX = cX + factor * rand(xmin, xmax); + coordY = cY + factor * rand(ymin, ymax); + + if (cX === coordX && cY === coordY) { // recalculate if same coordiante + coordX = 0; + continue; + } + + retry++; + } while (getCollision(me.area, coordX, coordY) & 1); + + // print("Move " + retry + " from (" + cX + ", " + cY + ") to (" + coordX + ", " + coordY + ")"); + return { x: coordX, y: coordY }; + }; }; diff --git a/d2bs/kolbot/libs/core/Common.js b/d2bs/kolbot/libs/core/Common.js index 4a29cf635..92c61ccec 100644 --- a/d2bs/kolbot/libs/core/Common.js +++ b/d2bs/kolbot/libs/core/Common.js @@ -6,6 +6,6 @@ */ const Common = { - // each common functionality is loaded into this object when it's needed - // for the actual function files @see core/Common/ + // each common functionality is loaded into this object when it's needed + // for the actual function files @see core/Common/ }; diff --git a/d2bs/kolbot/libs/core/Common/Ancients.js b/d2bs/kolbot/libs/core/Common/Ancients.js index 6f00b1101..15ddd8b0e 100644 --- a/d2bs/kolbot/libs/core/Common/Ancients.js +++ b/d2bs/kolbot/libs/core/Common/Ancients.js @@ -6,135 +6,150 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Ancients", { - value: { - altarSpot: { x: 10047, y: 12622 }, - - canAttack: function () { - let ancient = Game.getMonster(); - - if (ancient) { - do { - if (!ancient.getParent() && !Attack.canAttack(ancient)) { - console.log("Can't attack ancients"); - return false; - } - } while (ancient.getNext()); - } - - return true; - }, - - touchAltar: function () { - let altar = Misc.poll(() => Game.getObject(sdk.objects.AncientsAltar), 5000, 100); - - if (altar) { - while (altar.mode !== sdk.objects.mode.Active) { - if (Skill.haveTK) { - Attack.getIntoPosition(altar, 19, sdk.collision.LineOfSight, !Pather.useTeleport(), true); - Packet.telekinesis(altar); - } else { - Pather.moveToUnit(altar); - altar.interact(); - } - delay(200 + me.ping); - me.cancel(); - } - - // wait for ancients to spawn - while (!Game.getMonster(sdk.monsters.TalictheDefender)) { - delay(250 + me.ping); - } - - return true; - } else { - Pather.moveNearUnit(this.altarSpot, (Skill.haveTK ? 19 : 5)); - } - - return false; - }, - - checkStatues: function () { - let statues = getUnits(sdk.unittype.Object) - .filter(u => [sdk.objects.KorlictheProtectorStatue, sdk.objects.TalictheDefenderStatue, sdk.objects.MadawctheGuardianStatue].includes(u.classid) - && u.mode === sdk.objects.mode.Active); - return statues.length === 3; - }, - - checkCorners: function () { - let pos = [ - { x: 10036, y: 12592 }, { x: 10066, y: 12589 }, - { x: 10065, y: 12623 }, { x: 10058, y: 12648 }, - { x: 10040, y: 12660 }, { x: 10036, y: 12630 }, - { x: 10038, y: 12611 } - ]; - Pather.moveToUnit(this.altarSpot); - if (!this.checkStatues()) { - return pos.forEach((node) => { - // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { - return; - } - Pather.moveTo(node.x, node.y); - Attack.clear(30); - }); - } - - return true; - }, - - killAncients: function (checkQuest = false) { - let retry = 0; - let attackRange = Skill.getRange(Config.AttackSkill[1]); - Pather.moveNearUnit(this.altarSpot, attackRange); - - while (!this.checkStatues()) { - if (retry > 5) { - console.log("Failed to kill anicents."); - - break; - } - /** - * @todo - far cast pwning the ancients - */ - Attack.clearClassids(sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian); - delay(1000); - - if (checkQuest) { - if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { - break; - } - console.log("Failed to kill anicents. Attempt: " + retry); - } - - this.checkCorners(); - retry++; - } - }, - - ancientsPrep: function () { - Town.goToTown(); - Town.fillTome(sdk.items.TomeofTownPortal); - [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion].forEach(p => Town.buyPots(10, p, true)); - Town.buyPotions(); - Pather.usePortal(sdk.areas.ArreatSummit, me.name); - }, - - startAncients: function (preTasks = false, checkQuest = false) { - let retry = 0; - this.touchAltar(); - - while (!this.canAttack()) { - if (retry > 10) throw new Error("I think I'm unable to complete ancients, I've rolled them 10 times"); - preTasks ? this.ancientsPrep() : Pather.makePortal(); - this.touchAltar(); - retry++; - } - - this.killAncients(checkQuest); - }, - }, - configurable: true, - }); + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Ancients", { + value: new function () { + this.altarSpot = { x: 10047, y: 12622 }; + this.archway = { x: 10050, y: 12637 }; + this.talicStatue = { x: 10037, y: 12617 }; + this.madawcStatue = { x: 10048, y: 12607 }; + this.korlicStatue = { x: 10058, y: 12617 }; + this.lastPrep = 0; + + this.canAttack = function () { + let ancient = Game.getMonster(); + + if (ancient) { + do { + if (!ancient.getParent() && !Attack.canAttack(ancient)) { + console.log("Can't attack ancients"); + return false; + } + } while (ancient.getNext()); + } + + return true; + }; + + this.touchAltar = function () { + let altar = Misc.poll(() => Game.getObject(sdk.objects.AncientsAltar), 5000, 100); + + if (altar) { + while (altar.mode !== sdk.objects.mode.Active) { + if (Skill.haveTK) { + (this.archway.distance > 1 || altar.distance > 20) && Pather.moveToUnit(this.archway); + Packet.telekinesis(altar); + } else { + Pather.moveToUnit(altar); + altar.interact(); + } + delay(200 + me.ping); + me.cancel(); + } + + // wait for ancients to spawn + while (!Game.getMonster(sdk.monsters.TalictheDefender)) { + delay(250 + me.ping); + } + + return true; + } else { + Pather.moveNearUnit(this.altarSpot, (Skill.haveTK ? 19 : 5)); + } + + return false; + }; + + this.checkStatues = function () { + let statues = getUnits(sdk.unittype.Object) + .filter(u => [ + sdk.objects.KorlictheProtectorStatue, + sdk.objects.TalictheDefenderStatue, + sdk.objects.MadawctheGuardianStatue].includes(u.classid) + && u.mode === sdk.objects.mode.Active); + return statues.length === 3; + }; + + this.checkCorners = function () { + let pos = [ + { x: 10036, y: 12592 }, { x: 10066, y: 12589 }, + { x: 10065, y: 12623 }, { x: 10058, y: 12648 }, + { x: 10040, y: 12660 }, { x: 10036, y: 12630 }, + { x: 10038, y: 12611 } + ]; + Pather.moveToUnit(this.altarSpot); + if (!this.checkStatues()) { + return pos.forEach((node) => { + // no mobs at that next, skip it + if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { + return; + } + Pather.moveTo(node.x, node.y); + Attack.clear(30); + }); + } + + return true; + }; + + this.killAncients = function (checkQuest = false) { + let retry = 0; + let attackRange = Skill.getRange(Config.AttackSkill[1]); + Pather.moveNearUnit(this.altarSpot, attackRange); + + while (!this.checkStatues()) { + if (retry > 5) { + console.log("Failed to kill anicents."); + + break; + } + /** + * @todo - far cast pwning the ancients + */ + Attack.clearClassids( + sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian + ); + delay(1000); + + if (checkQuest) { + if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { + break; + } + console.log("Failed to kill anicents. Attempt: " + retry); + } + + this.checkCorners(); + retry++; + } + }; + + this.ancientsPrep = function () { + Town.goToTown(); + Town.fillTome(sdk.items.TomeofTownPortal); + [ + sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion + ].forEach(p => Town.buyPots(10, p, true)); + Town.buyPotions(); + Pather.usePortal(sdk.areas.ArreatSummit, me.name); + Common.Ancients.lastPrep = getTickCount(); + }; + + this.startAncients = function (preTasks = false, checkQuest = false) { + let retry = 0; + this.touchAltar(); + + while (!this.canAttack()) { + if (retry > 10) throw new Error("I think I'm unable to complete ancients, I've rolled them 10 times"); + preTasks && getTickCount() - this.lastPrep > Time.minutes(1) + ? this.ancientsPrep() + : Pather.makePortal(); + this.touchAltar(); + retry++; + } + + this.killAncients(checkQuest); + }; + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 410ee2b64..e7e73bef4 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -6,300 +6,306 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Baal", { - value: { - throneCoords: { - bottomLeft: { x: 15072, y: 5073 }, - bottomRight: { x: 15118, y: 5073 }, - bottomCenter: { x: 15093, y: 5073 }, - entraceArchway: { x: 15097, y: 5099 }, - topLeft: { x: 15072, y: 5002 }, - topRight: { x: 15118, y: 5002 }, - baal: { x: 15090, y: 5014 }, - }, - - checkHydra: function () { - let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - if (hydra) { - do { - if (hydra.mode !== sdk.monsters.mode.Dead && hydra.getStat(sdk.stats.Alignment) !== 2) { - Pather.moveTo(15072, 5002); - while (hydra.mode !== sdk.monsters.mode.Dead) { - delay(500); - if (!copyUnit(hydra).x) { - break; - } - } - - break; - } - } while (hydra.getNext()); - } - - return true; - }, - - checkThrone: function (clear = true) { - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Baal", { + value: { + throneCoords: { + bottomLeft: { x: 15072, y: 5073 }, + bottomRight: { x: 15118, y: 5073 }, + bottomCenter: { x: 15093, y: 5073 }, + entraceArchway: { x: 15097, y: 5099 }, + topLeft: { x: 15072, y: 5002 }, + topRight: { x: 15118, y: 5002 }, + baal: { x: 15090, y: 5014 }, + }, + + checkHydra: function () { + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); + if (hydra) { + do { + if (hydra.mode !== sdk.monsters.mode.Dead && hydra.getStat(sdk.stats.Alignment) !== 2) { + Pather.moveTo(15072, 5002); + while (hydra.mode !== sdk.monsters.mode.Dead) { + delay(500); + if (!copyUnit(hydra).x) { + break; + } + } + + break; + } + } while (hydra.getNext()); + } + + return true; + }, + + checkThrone: function (clear = true) { + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable && monster.y < 5080 && (monster.x > 15072 && monster.x < 15118)) { - switch (monster.classid) { - case sdk.monsters.WarpedFallen: - case sdk.monsters.WarpedShaman: - return 1; - case sdk.monsters.BaalSubjectMummy: - case sdk.monsters.BaalColdMage: - return 2; - case sdk.monsters.Council4: - return 3; - case sdk.monsters.VenomLord2: - return 4; - case sdk.monsters.ListerTheTormenter: - return 5; - default: - if (clear) { - Attack.getIntoPosition(monster, 10, sdk.collision.Ranged); - Attack.clear(15); - } - - return false; - } - } - } while (monster.getNext()); - } - - return false; - }, - - clearThrone: function () { - if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - - let monList = []; - - if (Config.AvoidDolls) { - let mon = Game.getMonster(sdk.monsters.SoulKiller); - - if (mon) { - do { - // exclude dolls from the list - if (!mon.isDoll && mon.x >= 15072 && mon.x <= 15118 && mon.y >= 5002 && mon.y <= 5079 && mon.attackable && !Attack.skipCheck(mon)) { - monList.push(copyUnit(mon)); - } - } while (mon.getNext()); - } - - if (monList.length > 0) { - return Attack.clearList(monList); - } - } - - let pos = [ - { x: 15097, y: 5054 }, { x: 15079, y: 5014 }, - { x: 15085, y: 5053 }, { x: 15085, y: 5040 }, - { x: 15098, y: 5040 }, { x: 15099, y: 5022 }, - { x: 15086, y: 5024 }, { x: 15079, y: 5014 } - ]; - return pos.forEach((node) => { - // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { - return; - } - Pather.moveTo(node.x, node.y); - Attack.clear(30); - }); - }, - - preattack: function () { - switch (me.classid) { - case sdk.player.class.Sorceress: - if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { - if (me.getState(sdk.states.SkillDelay)) { - delay(50); - } else { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5024); - } - } - - break; - case sdk.player.class.Paladin: - if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { - Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); - - return Skill.cast(Config.AttackSkill[3], 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[3], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5029); - - return true; - } - - break; - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({ x: 15094, y: 5028 }); - - if (check) { - return ClassAttack.placeTraps({ x: 15094, y: 5028 }, 5); - } - } - - if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { - return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); - } - - break; - } - - return false; - }, - - clearWaves: function () { - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - - let tick = getTickCount(); - let totalTick = getTickCount(); - - MainLoop: - while (true) { - if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - - switch (this.checkThrone()) { - case 1: - Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); - - break; - case 2: - Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); - - break; - case 3: - Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); - this.checkHydra() && (tick = getTickCount()); - - break; - case 4: - Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); - - break; - case 5: - Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2) && (tick = getTickCount()); - - 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)) { - this.clearThrone(); - tick = getTickCount(); - } - - if (!this.preattack()) { - delay(100); - } - - 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); + switch (monster.classid) { + case sdk.monsters.WarpedFallen: + case sdk.monsters.WarpedShaman: + return 1; + case sdk.monsters.BaalSubjectMummy: + case sdk.monsters.BaalColdMage: + return 2; + case sdk.monsters.Council4: + return 3; + case sdk.monsters.VenomLord2: + return 4; + case sdk.monsters.ListerTheTormenter: + return 5; + default: + if (clear) { + Attack.getIntoPosition(monster, 10, sdk.collision.Ranged); + Attack.clear(15); + } + + return false; + } + } + } while (monster.getNext()); + } + + return false; + }, + + clearThrone: function () { + if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + + let monList = []; + + if (Config.AvoidDolls) { + let mon = Game.getMonster(sdk.monsters.SoulKiller); + + if (mon) { + do { + // exclude dolls from the list + if (!mon.isDoll && mon.x >= 15072 && mon.x <= 15118 + && mon.y >= 5002 && mon.y <= 5079 + && mon.attackable && !Attack.skipCheck(mon)) { + monList.push(copyUnit(mon)); + } + } while (mon.getNext()); + } + + if (monList.length > 0) { + return Attack.clearList(monList); + } + } + + let pos = [ + { x: 15097, y: 5054 }, { x: 15079, y: 5014 }, + { x: 15085, y: 5053 }, { x: 15085, y: 5040 }, + { x: 15098, y: 5040 }, { x: 15099, y: 5022 }, + { x: 15086, y: 5024 }, { x: 15079, y: 5014 } + ]; + return pos.forEach((node) => { + // no mobs at that next, skip it + if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { + return; + } + Pather.moveTo(node.x, node.y); + Attack.clear(30); + }); + }, + + preattack: function () { + switch (me.classid) { + case sdk.player.class.Sorceress: + if ([ + sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall + ].includes(Config.AttackSkill[1])) { + if (me.getState(sdk.states.SkillDelay)) { + delay(50); + } else { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5024); + } + } + + break; + case sdk.player.class.Paladin: + if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { + Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); + + return Skill.cast(Config.AttackSkill[3], 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[3], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5029); + + return true; + } + + break; + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let check = ClassAttack.checkTraps({ x: 15094, y: 5028 }); + + if (check) { + return ClassAttack.placeTraps({ x: 15094, y: 5028 }, 5); + } + } + + if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { + return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); + } + + break; + } + + return false; + }, + + clearWaves: function () { + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + + let tick = getTickCount(); + let totalTick = getTickCount(); + + MainLoop: + while (true) { + if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + + switch (this.checkThrone()) { + case 1: + Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); + + break; + case 2: + Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); + + break; + case 3: + Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); + this.checkHydra() && (tick = getTickCount()); + + break; + case 4: + Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); + + break; + case 5: + if (Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2)) { + tick = getTickCount(); + } + + 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)) { + this.clearThrone(); + tick = getTickCount(); + } + + if (!this.preattack()) { + delay(100); + } + + 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.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); - } - - this.clearThrone(); - - return true; - }, - - killBaal: function () { - if (me.inArea(sdk.areas.ThroneofDestruction)) { - Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); - me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done - Pather.moveTo(15090, 5008); - delay(5000); - Precast.doPrecast(true); - Misc.poll(() => { - if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({ range: 15 })) { - Common.Baal.clearThrone(); - Pather.moveTo(15090, 5008); - } - return !Game.getMonster(sdk.monsters.ThroneBaal); - }, Time.minutes(3), 1000); - - let portal = Game.getObject(sdk.objects.WorldstonePortal); - - if (portal) { - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } - } - - if (me.inArea(sdk.areas.WorldstoneChamber)) { - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); - - return true; - } - - return false; - } - }, - configurable: true, - }); + 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); + } + + this.clearThrone(); + + return true; + }, + + killBaal: function () { + if (me.inArea(sdk.areas.ThroneofDestruction)) { + Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); + me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done + Pather.moveTo(15090, 5008); + delay(5000); + Precast.doPrecast(true); + Misc.poll(() => { + if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({ range: 15 })) { + Common.Baal.clearThrone(); + Pather.moveTo(15090, 5008); + } + return !Game.getMonster(sdk.monsters.ThroneBaal); + }, Time.minutes(3), 1000); + + let portal = Game.getObject(sdk.objects.WorldstonePortal); + + if (portal) { + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); + } + } + + if (me.inArea(sdk.areas.WorldstoneChamber)) { + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); + + return true; + } + + return false; + } + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Common/Cain.js b/d2bs/kolbot/libs/core/Common/Cain.js index 856683d46..7f4dfe0d1 100644 --- a/d2bs/kolbot/libs/core/Common/Cain.js +++ b/d2bs/kolbot/libs/core/Common/Cain.js @@ -6,123 +6,134 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Cain", { - value: { - activateStone: function (stone) { - for (let i = 0; i < 3; i++) { - // don't use tk if we are right next to it - let useTK = (stone.distance > 5 && Skill.useTK(stone) && i === 0); - if (useTK) { - stone.distance > 13 && Attack.getIntoPosition(stone, 13, sdk.collision.Ranged); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stone)) { - console.debug("Failed to tk: attempt: " + i); - continue; - } - } else { - [(stone.x + 1), (stone.y + 2)].distance > 5 && Pather.moveTo(stone.x + 1, stone.y + 2, 3); - Misc.click(0, 0, stone); - } - - if (Misc.poll(() => stone.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; - }, - - run: function () { - MainLoop: - while (true) { - switch (true) { - case !Game.getItem(sdk.quest.item.ScrollofInifuss) && !Game.getItem(sdk.quest.item.KeytotheCairnStones) && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4): - Pather.useWaypoint(sdk.areas.DarkWood, true); - Precast.doPrecast(true); - - if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - throw new Error("Failed to move to Tree of Inifuss"); - } - - let tree = Game.getObject(sdk.quest.chest.InifussTree); - !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Misc.openChest(tree); - let scroll = Misc.poll(() => Game.getItem(sdk.quest.item.ScrollofInifuss), 1000, 100); - - Pickit.pickItem(scroll); - Town.goToTown(); - Town.npcInteract("Akara"); + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Cain", { + value: { + activateStone: function (stone) { + for (let i = 0; i < 3; i++) { + // don't use tk if we are right next to it + let useTK = (stone.distance > 5 && Skill.useTK(stone) && i === 0); + if (useTK) { + stone.distance > 13 && Attack.getIntoPosition(stone, 13, sdk.collision.Ranged); + if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stone)) { + console.debug("Failed to tk: attempt: " + i); + continue; + } + } else { + [(stone.x + 1), (stone.y + 2)].distance > 5 && Pather.moveTo(stone.x + 1, stone.y + 2, 3); + Misc.click(0, 0, stone); + } + + if (Misc.poll(() => stone.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; + }, + + run: function () { + MainLoop: + while (true) { + switch (true) { + case !Game.getItem(sdk.quest.item.ScrollofInifuss) + && !Game.getItem(sdk.quest.item.KeytotheCairnStones) + && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4): + Pather.useWaypoint(sdk.areas.DarkWood, true); + Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + throw new Error("Failed to move to Tree of Inifuss"); + } + + let tree = Game.getObject(sdk.quest.chest.InifussTree); + !!tree && tree.distance > 5 && Pather.moveToUnit(tree); + Misc.openChest(tree); + let scroll = Misc.poll(() => Game.getItem(sdk.quest.item.ScrollofInifuss), 1000, 100); + + Pickit.pickItem(scroll); + Town.goToTown(); + Town.npcInteract("Akara"); - break; - case Game.getItem(sdk.quest.item.ScrollofInifuss): - Town.goToTown(1); - Town.npcInteract("Akara"); - - break; - case Game.getItem(sdk.quest.item.KeytotheCairnStones) && !me.inArea(sdk.areas.StonyField): - Pather.journeyTo(sdk.areas.StonyField); - Precast.doPrecast(true); - - break; - case Game.getItem(sdk.quest.item.KeytotheCairnStones) && me.inArea(sdk.areas.StonyField): - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); - Attack.securePosition(me.x, me.y, 40, 3000, true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); - let stones = [ - Game.getObject(sdk.quest.chest.StoneAlpha), - Game.getObject(sdk.quest.chest.StoneBeta), - Game.getObject(sdk.quest.chest.StoneGamma), - Game.getObject(sdk.quest.chest.StoneDelta), - Game.getObject(sdk.quest.chest.StoneLambda) - ]; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (this.activateStone(stone)) { - stones.splice(i, 1); - i--; - } - delay(10); - } - } - - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram)) { - Pather.usePortal(sdk.areas.Tristram); + break; + case Game.getItem(sdk.quest.item.ScrollofInifuss): + Town.goToTown(1); + Town.npcInteract("Akara"); + + break; + case Game.getItem(sdk.quest.item.KeytotheCairnStones) && !me.inArea(sdk.areas.StonyField): + Pather.journeyTo(sdk.areas.StonyField); + Precast.doPrecast(true); + + break; + case Game.getItem(sdk.quest.item.KeytotheCairnStones) && me.inArea(sdk.areas.StonyField): + Pather.moveToPresetMonster( + sdk.areas.StonyField, + sdk.monsters.preset.Rakanishu, + { offX: 10, offY: 10, pop: true } + ); + Attack.securePosition(me.x, me.y, 40, 3000, true); + Pather.moveToPresetObject( + sdk.areas.StonyField, + sdk.quest.chest.StoneAlpha, + { clearSettings: { clearPath: true } } + ); + let stones = [ + Game.getObject(sdk.quest.chest.StoneAlpha), + Game.getObject(sdk.quest.chest.StoneBeta), + Game.getObject(sdk.quest.chest.StoneGamma), + Game.getObject(sdk.quest.chest.StoneDelta), + Game.getObject(sdk.quest.chest.StoneLambda) + ]; + + while (stones.some((stone) => !stone.mode)) { + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; + + if (this.activateStone(stone)) { + stones.splice(i, 1); + i--; + } + delay(10); + } + } + + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.getPortal(sdk.areas.Tristram)) { + Pather.usePortal(sdk.areas.Tristram); - break; - } - } - - break; - case me.inArea(sdk.areas.Tristram) && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed): - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { - Town.goToTown(1); - Town.npcInteract("Akara") && console.log("Akara done"); - } - } - - break; - default: - break MainLoop; - } - } - - return true; - } - }, - configurable: true, - }); + break; + } + } + + break; + case me.inArea(sdk.areas.Tristram) + && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed): + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { + Town.goToTown(1); + Town.npcInteract("Akara") && console.log("Akara done"); + } + } + + break; + default: + break MainLoop; + } + } + + return true; + } + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Common/Cows.js b/d2bs/kolbot/libs/core/Common/Cows.js index 8dbbfaead..7a73cadcb 100644 --- a/d2bs/kolbot/libs/core/Common/Cows.js +++ b/d2bs/kolbot/libs/core/Common/Cows.js @@ -6,74 +6,74 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Cows", { - value: { - buildCowRooms: function () { - let finalRooms = []; - let indexes = []; + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Cows", { + value: { + buildCowRooms: function () { + let finalRooms = []; + let indexes = []; - let kingPreset = Game.getPresetMonster(sdk.areas.MooMooFarm, sdk.monsters.preset.TheCowKing); - let badRooms = getRoom(kingPreset.roomx * 5 + kingPreset.x, kingPreset.roomy * 5 + kingPreset.y).getNearby(); + let kingPreset = Game.getPresetMonster(sdk.areas.MooMooFarm, sdk.monsters.preset.TheCowKing); + let badRooms = getRoom(kingPreset.roomx * 5 + kingPreset.x, kingPreset.roomy * 5 + kingPreset.y).getNearby(); - for (let i = 0; i < badRooms.length; i += 1) { - let badRooms2 = badRooms[i].getNearby(); + for (let i = 0; i < badRooms.length; i += 1) { + let badRooms2 = badRooms[i].getNearby(); - for (let j = 0; j < badRooms2.length; j += 1) { - if (indexes.indexOf(badRooms2[j].x + "" + badRooms2[j].y) === -1) { - indexes.push(badRooms2[j].x + "" + badRooms2[j].y); - } - } - } + for (let j = 0; j < badRooms2.length; j += 1) { + if (indexes.indexOf(badRooms2[j].x + "" + badRooms2[j].y) === -1) { + indexes.push(badRooms2[j].x + "" + badRooms2[j].y); + } + } + } - let room = getRoom(); + let room = getRoom(); - do { - if (indexes.indexOf(room.x + "" + room.y) === -1) { - finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); - } - } while (room.getNext()); + do { + if (indexes.indexOf(room.x + "" + room.y) === -1) { + finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + } + } while (room.getNext()); - return finalRooms; - }, + return finalRooms; + }, - clearCowLevel: function () { - function roomSort(a, b) { - return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); - } + clearCowLevel: function () { + function roomSort(a, b) { + return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); + } - Config.MFLeader && Pather.makePortal() && say("cows"); + Config.MFLeader && Pather.makePortal() && say("cows"); - let myRoom; - let rooms = this.buildCowRooms(); + let myRoom; + let rooms = this.buildCowRooms(); - while (rooms.length > 0) { - // get the first room + initialize myRoom var - !myRoom && (room = getRoom(me.x, me.y)); + 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]; - } - } + 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); - let room = rooms.shift(); - let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); + rooms.sort(roomSort); + let room = rooms.shift(); + let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); - if (result) { - Pather.moveTo(result[0], result[1], 3); - if (!Attack.clear(30)) return false; - } - } + if (result) { + Pather.moveTo(result[0], result[1], 3); + if (!Attack.clear(30)) return false; + } + } - return true; - }, - }, - configurable: true, - }); + return true; + }, + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index c88d34844..1316da7d8 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -6,627 +6,665 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - /** + typeof Common !== "object" && (Common = {}); + /** * @todo * - keep track of seals opened and bosses killed to * - improve targetting when using getBoss, sometimes we run to the location we want to attack from while running past the boss * I see this mostly with viz, also have seen seis fail due to not being close enough, even though he spawned */ - Object.defineProperty(Common, "Diablo", { - /** + Object.defineProperty(Common, "Diablo", { + /** * @namespace Common * * @typedef Diablo * @property {boolean} diabloSpawned * */ - value: { - diabloSpawned: false, - diaWaitTime: Time.seconds(30), - clearRadius: 30, - done: false, - waitForGlow: false, - sealOrder: [], - openedSeals: [], - vizLayout: -1, - seisLayout: -1, - infLayout: -1, - entranceCoords: { x: 7790, y: 5544 }, - starCoords: { x: 7791, y: 5293 }, - // path coordinates - entranceToStar: [ - [7794, 5517], [7791, 5491], [7768, 5459], - [7775, 5424], [7817, 5458], [7777, 5408], - [7769, 5379], [7777, 5357], [7809, 5359], - [7805, 5330], [7780, 5317], [7791, 5293]], - starToVizA: [ - [7759, 5295], [7734, 5295], [7716, 5295], [7718, 5276], - [7697, 5292], [7678, 5293], [7665, 5276], [7662, 5314] - ], - starToVizB: [ - [7759, 5295], [7734, 5295], [7716, 5295], - [7701, 5315], [7666, 5313], [7653, 5284] - ], - starToSeisA: [ - [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], - [7775, 5205], [7804, 5193], [7814, 5169], [7788, 5153] - ], - starToSeisB: [ - [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], - [7811, 5218], [7807, 5194], [7779, 5193], [7774, 5160], [7803, 5154] - ], - starToInfA: [ - [7809, 5268], [7834, 5306], [7852, 5280], - [7852, 5310], [7869, 5294], [7895, 5295], [7919, 5290] - ], - starToInfB: [ - [7809, 5268], [7834, 5306], [7852, 5280], [7852, 5310], - [7869, 5294], [7895, 5274], [7927, 5275], [7932, 5297], [7923, 5313] - ], - // check for strays array - cleared: [], - - diabloLightsEvent: function (bytes = []) { - if (me.inArea(sdk.areas.ChaosSanctuary) && bytes && bytes.length === 2 && bytes[0] === 0x89 && bytes[1] === 0x0C) { - Common.Diablo.diabloSpawned = true; - } - }, - - sort: function (a, b) { - if (Config.BossPriority) { - if ((a.isSuperUnique) && (b.isSuperUnique)) return getDistance(me, a) - getDistance(me, b); - if (a.isSuperUnique) return -1; - if (b.isSuperUnique) return 1; - } - - // Entrance to Star / De Seis - if (me.y > 5325 || me.y < 5260) return (a.y > b.y ? -1 : 1); - // Vizier - if (me.x < 7765) return (a.x > b.x ? -1 : 1); - // Infector - if (me.x > 7825) return (!checkCollision(me, a, sdk.collision.BlockWall) && a.x < b.x ? -1 : 1); - - return getDistance(me, a) - getDistance(me, b); - }, - - getLayout: function (seal, value) { - let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); - if (!seal) throw new Error("Seal preset not found. Can't continue."); - - if (sealPreset.roomy * 5 + sealPreset.y === value + value: { + diabloSpawned: false, + diaWaitTime: Time.seconds(30), + clearRadius: 30, + done: false, + waitForGlow: false, + sealOrder: [], + openedSeals: [], + vizLayout: -1, + seisLayout: -1, + infLayout: -1, + entranceCoords: { x: 7790, y: 5544 }, + starCoords: { x: 7791, y: 5293 }, + // path coordinates + entranceToStar: [ + [7794, 5517], [7791, 5491], [7768, 5459], + [7775, 5424], [7817, 5458], [7777, 5408], + [7769, 5379], [7777, 5357], [7809, 5359], + [7805, 5330], [7780, 5317], [7791, 5293]], + starToVizA: [ + [7759, 5295], [7734, 5295], [7716, 5295], [7718, 5276], + [7697, 5292], [7678, 5293], [7665, 5276], [7662, 5314] + ], + starToVizB: [ + [7759, 5295], [7734, 5295], [7716, 5295], + [7701, 5315], [7666, 5313], [7653, 5284] + ], + starToSeisA: [ + [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], + [7775, 5205], [7804, 5193], [7814, 5169], [7788, 5153] + ], + starToSeisB: [ + [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], + [7811, 5218], [7807, 5194], [7779, 5193], [7774, 5160], [7803, 5154] + ], + starToInfA: [ + [7809, 5268], [7834, 5306], [7852, 5280], + [7852, 5310], [7869, 5294], [7895, 5295], [7919, 5290] + ], + starToInfB: [ + [7809, 5268], [7834, 5306], [7852, 5280], [7852, 5310], + [7869, 5294], [7895, 5274], [7927, 5275], [7932, 5297], [7923, 5313] + ], + // check for strays array + cleared: [], + + diabloLightsEvent: function (bytes = []) { + if (me.inArea(sdk.areas.ChaosSanctuary) + && bytes && bytes.length === 2 && bytes[0] === 0x89 && bytes[1] === 0x0C) { + Common.Diablo.diabloSpawned = true; + } + }, + + sort: function (a, b) { + if (Config.BossPriority) { + if ((a.isSuperUnique) && (b.isSuperUnique)) return getDistance(me, a) - getDistance(me, b); + if (a.isSuperUnique) return -1; + if (b.isSuperUnique) return 1; + } + + // Entrance to Star / De Seis + if (me.y > 5325 || me.y < 5260) return (a.y > b.y ? -1 : 1); + // Vizier + if (me.x < 7765) return (a.x > b.x ? -1 : 1); + // Infector + if (me.x > 7825) return (!checkCollision(me, a, sdk.collision.BlockWall) && a.x < b.x ? -1 : 1); + + return getDistance(me, a) - getDistance(me, b); + }, + + getLayout: function (seal, value) { + let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); + if (!seal) throw new Error("Seal preset not found. Can't continue."); + + if (sealPreset.roomy * 5 + sealPreset.y === value || sealPreset.roomx * 5 + sealPreset.x === value) { - return 1; - } + return 1; + } - return 2; - }, + return 2; + }, - /** + /** * - VizLayout - 1 = "Y", 2 = "L" * - SeisLayout - 1 = "2", 2 = "5" * - InfLayout - 1 = "I", 2 = "J" */ - initLayout: function () { - // 1 = "Y", 2 = "L" - Common.Diablo.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); - // 1 = "2", 2 = "5" - Common.Diablo.seisLayout = this.getLayout(sdk.objects.DiabloSealSeis, 7773); - // 1 = "I", 2 = "J" - Common.Diablo.infLayout = this.getLayout(sdk.objects.DiabloSealInfector, 7893); - }, - - /** + initLayout: function () { + // 1 = "Y", 2 = "L" + Common.Diablo.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); + // 1 = "2", 2 = "5" + Common.Diablo.seisLayout = this.getLayout(sdk.objects.DiabloSealSeis, 7773); + // 1 = "I", 2 = "J" + Common.Diablo.infLayout = this.getLayout(sdk.objects.DiabloSealInfector, 7893); + }, + + /** * Follow static path * @param {number[][]} path * @returns {void} */ - followPath: function (path) { - if (Config.Diablo.Fast) { - let last = path.last(); - let lastNode = { x: last[0], y: last[1] }; - Pather.moveToUnit(lastNode); - return; - } + followPath: function (path) { + if (Config.Diablo.Fast) { + let last = path.last(); + let lastNode = { x: last[0], y: last[1] }; + Pather.moveToUnit(lastNode); + return; + } - for (let i = 0; i < path.length; i++) { - this.cleared.length > 0 && this.clearStrays(); - - // no monsters at the next node, skip it - let next = i + 1 !== path.length ? path[i + 1] : null; - if (next && next.distance < 40 && next.mobCount({ range: 35 }) === 0) { - continue; - } - - Pather.moveTo(path[i][0], path[i][1], 3, getDistance(me, path[i][0], path[i][1]) > 50); - Attack.clear(this.clearRadius, 0, false, Common.Diablo.sort); - - // Push cleared positions so they can be checked for strays - this.cleared.push(path[i]); - - // After 5 nodes go back 2 nodes to check for monsters - if (i === 5 && path.length > 8) { - path = path.slice(3); - i = 0; - } - } - }, - - clearStrays: function () { - let oldPos = { x: me.x, y: me.y }; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable) { - for (let i = 0; i < this.cleared.length; i += 1) { - if (getDistance(monster, this.cleared[i][0], this.cleared[i][1]) < 30 && Attack.validSpot(monster.x, monster.y)) { - Pather.moveToUnit(monster); - Attack.clear(15, 0, false, Common.Diablo.sort); - - break; - } - } - } - } while (monster.getNext()); - } - - getDistance(me, oldPos.x, oldPos.y) > 5 && Pather.moveTo(oldPos.x, oldPos.y); - - return true; - }, - - /** + for (let i = 0; i < path.length; i++) { + this.cleared.length > 0 && this.clearStrays(); + + // no monsters at the next node, skip it + let next = i + 1 !== path.length ? path[i + 1] : null; + if (next && next.distance < 40 && next.mobCount({ range: 35 }) === 0) { + continue; + } + + Pather.moveTo(path[i][0], path[i][1], 3, getDistance(me, path[i][0], path[i][1]) > 50); + Attack.clear(this.clearRadius, 0, false, Common.Diablo.sort); + + // Push cleared positions so they can be checked for strays + this.cleared.push(path[i]); + + // After 5 nodes go back 2 nodes to check for monsters + if (i === 5 && path.length > 8) { + path = path.slice(3); + i = 0; + } + } + }, + + clearStrays: function () { + let oldPos = { x: me.x, y: me.y }; + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable) { + for (let i = 0; i < this.cleared.length; i += 1) { + if (getDistance(monster, this.cleared[i][0], this.cleared[i][1]) < 30 + && Attack.validSpot(monster.x, monster.y)) { + Pather.moveToUnit(monster); + Attack.clear(15, 0, false, Common.Diablo.sort); + + break; + } + } + } + } while (monster.getNext()); + } + + getDistance(me, oldPos.x, oldPos.y) > 5 && Pather.moveTo(oldPos.x, oldPos.y); + + return true; + }, + + /** * @param {number[] | string[]} sealOrder * @param {boolean} openSeals */ - runSeals: function (sealOrder, openSeals = true, recheck = false) { - print("seal order: " + sealOrder); - Common.Diablo.sealOrder = sealOrder; - let seals = { - 1: () => this.vizierSeal(openSeals), - 2: () => this.seisSeal(openSeals), - 3: () => this.infectorSeal(openSeals), - "vizier": () => this.vizierSeal(openSeals), - "seis": () => this.seisSeal(openSeals), - "infector": () => this.infectorSeal(openSeals), - }; - try { - recheck && addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - sealOrder.forEach(seal => { - if (recheck && Common.Diablo.diabloSpawned) throw new ScriptError("Diablo spawned"); - seals[seal](); - }); - } catch (e) { - if (!(e instanceof ScriptError)) { - throw e; // it wasn't the custom error so throw it to the next handler - } - } finally { - recheck && removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - } - }, - - /** + runSeals: function (sealOrder, openSeals = true, recheck = false) { + print("seal order: " + sealOrder); + Common.Diablo.sealOrder = sealOrder; + let seals = { + 1: () => this.vizierSeal(openSeals), + 2: () => this.seisSeal(openSeals), + 3: () => this.infectorSeal(openSeals), + "vizier": () => this.vizierSeal(openSeals), + "seis": () => this.seisSeal(openSeals), + "infector": () => this.infectorSeal(openSeals), + }; + try { + recheck && addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + sealOrder.forEach(seal => { + if (recheck && Common.Diablo.diabloSpawned) throw new ScriptError("Diablo spawned"); + seals[seal](); + }); + } catch (e) { + if (!(e instanceof ScriptError)) { + throw e; // it wasn't the custom error so throw it to the next handler + } + } finally { + recheck && removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + } + }, + + /** * Attempt casting telekinesis on seal to activate it * @param {Unit} seal * @returns {boolean} */ - tkSeal: function (seal) { - if (!Skill.useTK(seal)) return false; + tkSeal: function (seal) { + if (!Skill.useTK(seal)) return false; - for (let i = 0; i < 5; i++) { - seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); + for (let i = 0; i < 5; i++) { + seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, seal) && Misc.poll(() => seal.mode, 1000, 100)) { - break; - } - } + if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, seal) + && Misc.poll(() => seal.mode, 1000, 100)) { + break; + } + } - return !!seal.mode; - }, + return !!seal.mode; + }, - /** + /** * Open one of diablos seals * @param {number} classid * @returns {boolean} */ - openSeal: function (classid) { - let seal; - let warn = Config.PublicMode && [sdk.objects.DiabloSealVizier, sdk.objects.DiabloSealSeis, sdk.objects.DiabloSealInfector].includes(classid) && Loader.scriptName() === "Diablo"; - let usetk = (Skill.haveTK && (classid !== sdk.objects.DiabloSealSeis || this.seisLayout !== 1)); - let seisSeal = classid === sdk.objects.DiabloSealSeis; - - for (let i = 0; i < 5; i++) { - if (!seal) { - usetk - ? Pather.moveNearPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, 15) - : Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, seisSeal ? 5 : 2, seisSeal ? 5 : 0); - seal = Misc.poll(() => Game.getObject(classid), 1000, 100); - } - - if (!seal) { - console.debug("Couldn't find seal: " + classid); - return false; - } - - if (seal.mode) { - warn && say(Config.Diablo.SealWarning); - return true; - } - - // Clear around Infector seal, Any leftover abyss knights casting decrep is bad news with Infector - if (([sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealInfector2].includes(classid) || i > 1) && me.getMobCount() > 1) { - Attack.clear(15); - // Move back to seal - usetk ? Pather.moveNearUnit(seal, 15) : Pather.moveToUnit(seal, seisSeal ? 5 : 2, seisSeal ? 5 : 0); - } - - if (usetk && this.tkSeal(seal)) { - return seal.mode; - } else { - usetk && (usetk = false); - - if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({ x: 7899, y: 5293 }); - check && ClassAttack.placeTraps({ x: 7899, y: 5293 }, check); - } - } - - seisSeal ? Misc.poll(function () { - // stupid diablo shit, walk around the de-seis seal clicking it until we find "the spot"...sigh - if (!seal.mode) { - Pather.walkTo(seal.x + (rand(-1, 1)), seal.y + (rand(-1, 1))); - clickUnitAndWait(0, 0, seal) || seal.interact(); - } - return !!seal.mode; - }, 3000, 60) : seal.interact(); - - // de seis optimization - if (seisSeal && Attack.validSpot(seal.x + 15, seal.y)) { - Pather.walkTo(seal.x + 15, seal.y); - } else { - Pather.walkTo(seal.x - 5, seal.y - 5); - } - } - - delay(seisSeal ? 1000 + me.ping : 500 + me.ping); - - if (seal.mode) { - break; - } - } - - return (!!seal && seal.mode); - }, - - /** + openSeal: function (classid) { + let seal; + const mainSeal = [ + sdk.objects.DiabloSealVizier, sdk.objects.DiabloSealSeis, sdk.objects.DiabloSealInfector + ].includes(classid); + const warn = Config.PublicMode && mainSeal && Loader.scriptName() === "Diablo"; + const seisSeal = classid === sdk.objects.DiabloSealSeis; + const infSeal = [sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealInfector2].includes(classid); + let usetk = (Skill.haveTK && (!seisSeal || this.seisLayout !== 1)); + + for (let i = 0; i < 5; i++) { + if (!seal) { + usetk + ? Pather.moveNearPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, 15) + : Pather.moveToPresetObject( + sdk.areas.ChaosSanctuary, + classid, + { offX: seisSeal ? 5 : 2, offY: seisSeal ? 5 : 0 } + ); + seal = Misc.poll(() => Game.getObject(classid), 1000, 100); + } + + if (!seal) { + console.debug("Couldn't find seal: " + classid); + return false; + } + + if (seal.mode) { + warn && say(Config.Diablo.SealWarning); + return true; + } + + // Clear around Infector seal, Any leftover abyss knights casting decrep is bad news with Infector + if ((infSeal || i > 1) && me.getMobCount() > 1) { + Attack.clear(15); + // Move back to seal + usetk ? Pather.moveNearUnit(seal, 15) : Pather.moveToUnit(seal, seisSeal ? 5 : 2, seisSeal ? 5 : 0); + } + + if (usetk && this.tkSeal(seal)) { + return seal.mode; + } else { + usetk && (usetk = false); + + if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { + if (Config.UseTraps) { + let check = ClassAttack.checkTraps({ x: 7899, y: 5293 }); + check && ClassAttack.placeTraps({ x: 7899, y: 5293 }, check); + } + } + + seisSeal ? Misc.poll(function () { + // stupid diablo shit, walk around the de-seis seal clicking it until we find "the spot"...sigh + if (!seal.mode) { + Pather.walkTo(seal.x + (rand(-1, 1)), seal.y + (rand(-1, 1))); + clickUnitAndWait(0, 0, seal) || seal.interact(); + } + return !!seal.mode; + }, 3000, 60) : seal.interact(); + + // de seis optimization + if (seisSeal && Attack.validSpot(seal.x + 15, seal.y)) { + Pather.walkTo(seal.x + 15, seal.y); + } else { + Pather.walkTo(seal.x - 5, seal.y - 5); + } + } + + delay(seisSeal ? 1000 + me.ping : 500 + me.ping); + + if (seal.mode) { + break; + } + } + + return (!!seal && seal.mode); + }, + + /** * @param {boolean} openSeal * @returns {boolean} */ - vizierSeal: function (openSeal = true) { - print("Viz layout " + Common.Diablo.vizLayout); - let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); - let distCheck = path.last(); - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } - - distCheck.distance > 30 && this.followPath(Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); - - if (openSeal && (!Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2) || !Common.Diablo.openSeal(sdk.objects.DiabloSealVizier))) { - throw new Error("Failed to open Vizier seals."); - } - - delay(1 + me.ping); - let cb = () => { - let viz = Game.getMonster(getLocaleString(sdk.locale.monsters.GrandVizierofChaos)); - return viz && (viz.distance < Skill.getRange(Config.AttackSkill[1]) || viz.dead); - }; - /** + vizierSeal: function (openSeal = true) { + print("Viz layout " + Common.Diablo.vizLayout); + let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + distCheck.distance > 30 && this.followPath(Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + + if (openSeal + && ![sdk.objects.DiabloSealVizier2, sdk.objects.DiabloSealVizier].every(s => Common.Diablo.openSeal(s))) { + throw new Error("Failed to open Vizier seals."); + } + + delay(1 + me.ping); + let cb = () => { + let viz = Game.getMonster(getLocaleString(sdk.locale.monsters.GrandVizierofChaos)); + return viz && (viz.distance < Skill.getRange(Config.AttackSkill[1]) || viz.dead); + }; + /** * @todo better coords or maybe a delay, viz appears in different locations and sometimes its right where we are moving to * which is okay for hammerdins or melee chars but not for soft chars like sorcs */ - Common.Diablo.vizLayout === 1 - ? Pather.moveToEx(7691, 5292, { callback: cb }) - : Pather.moveToEx(7695, 5316, { callback: cb }); + Common.Diablo.vizLayout === 1 + ? Pather.moveToEx(7691, 5292, { callback: cb }) + : Pather.moveToEx(7695, 5316, { callback: cb }); - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { - throw new Error("Failed to kill Vizier"); - } + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { + throw new Error("Failed to kill Vizier"); + } - Config.Diablo.SealLeader && say("out"); + Config.Diablo.SealLeader && say("out"); - return true; - }, + return true; + }, - /** + /** * @param {boolean} openSeal * @returns {boolean} */ - seisSeal: function (openSeal = true) { - print("Seis layout " + Common.Diablo.seisLayout); - let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); - let distCheck = path.last(); - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } - - distCheck.distance > 30 && this.followPath(Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); - - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealSeis)) throw new Error("Failed to open de Seis seal."); - let cb = () => { - let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); - return seis && (seis.distance < Skill.getRange(Config.AttackSkill[1]) || seis.dead); - }; - Common.Diablo.seisLayout === 1 - ? Pather.moveToEx(7798, 5194, { callback: cb }) - : Pather.moveToEx(7796, 5155, { callback: cb }); - try { - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) throw new Error("Failed to kill de Seis"); - } catch (e) { - // sometimes we fail just because we aren't in range, - Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { - let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); - return seis && (seis.distance < 30 || seis.dead); - } }); - } - - Config.Diablo.SealLeader && say("out"); - - return true; - }, - - /** + seisSeal: function (openSeal = true) { + print("Seis layout " + Common.Diablo.seisLayout); + let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + Common.Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + if (distCheck.distance > 30) { + this.followPath(Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + } + + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealSeis)) { + throw new Error("Failed to open de Seis seal."); + } + let cb = () => { + let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); + return seis && (seis.distance < Skill.getRange(Config.AttackSkill[1]) || seis.dead); + }; + Common.Diablo.seisLayout === 1 + ? Pather.moveToEx(7798, 5194, { callback: cb }) + : Pather.moveToEx(7796, 5155, { callback: cb }); + try { + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { + throw new Error("Failed to kill de Seis"); + } + } catch (e) { + // sometimes we fail just because we aren't in range, + Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { + let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); + return seis && (seis.distance < 30 || seis.dead); + } }); + } + + Config.Diablo.SealLeader && say("out"); + + return true; + }, + + /** * @param {boolean} openSeal * @returns {boolean} */ - infectorSeal: function (openSeal = true) { - Precast.doPrecast(true); - print("Inf layout " + Common.Diablo.infLayout); - let path = (Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - let distCheck = path.last(); - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } - - distCheck.distance > 70 && this.followPath(Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + infectorSeal: function (openSeal = true) { + Precast.doPrecast(true); + print("Inf layout " + Common.Diablo.infLayout); + let path = (Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + Common.Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + distCheck.distance > 70 && this.followPath(Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - let cb = () => { - let inf = Game.getMonster(getLocaleString(sdk.locale.monsters.InfectorofSouls)); - return inf && (inf.distance < Skill.getRange(Config.AttackSkill[1]) || inf.dead); - }; - - let moveToLoc = () => { - if (Common.Diablo.infLayout === 1) { - (me.sorceress || me.assassin) && Pather.moveToEx(7876, 5296, { callback: cb }); - delay(1 + me.ping); - } else { - delay(1 + me.ping); - Pather.moveToEx(7928, 5295, { callback: cb }); - } - }; - - if (Config.Diablo.Fast) { - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); - moveToLoc(); - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); - } else { - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) throw new Error("Failed to open Infector seals."); - moveToLoc(); - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) throw new Error("Failed to kill Infector"); - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) throw new Error("Failed to open Infector seals."); - // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss - !openSeal && [3, "infector"].includes(Common.Diablo.sealOrder.last()) && Misc.poll(() => { - if (Common.Diablo.diabloSpawned) return true; - - let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); - if (lastSeal && lastSeal.mode) { - return true; - } - return false; - }, Time.minutes(3), 1000); - } - - Config.Diablo.SealLeader && say("out"); - - return true; - }, - - hammerdinPreAttack: function (name, amount = 5) { - if (me.paladin && Config.AttackSkill[1] === sdk.skills.BlessedHammer) { - let target = Game.getMonster(name); - - if (!target || !target.attackable) return true; - - let positions = [[6, 11], [0, 8], [8, -1], [-9, 2], [0, -11], [8, -8]]; - - for (let i = 0; i < positions.length; i += 1) { - // check if we can move there - if (Attack.validSpot(target.x + positions[i][0], target.y + positions[i][1])) { - Pather.moveTo(target.x + positions[i][0], target.y + positions[i][1]); - Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - - for (let n = 0; n < amount; n += 1) { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); - } - - return true; - } - } - } + let cb = () => { + let inf = Game.getMonster(getLocaleString(sdk.locale.monsters.InfectorofSouls)); + return inf && (inf.distance < Skill.getRange(Config.AttackSkill[1]) || inf.dead); + }; + + let moveToLoc = () => { + if (Common.Diablo.infLayout === 1) { + (me.sorceress || me.assassin) && Pather.moveToEx(7876, 5296, { callback: cb }); + delay(1 + me.ping); + } else { + delay(1 + me.ping); + Pather.moveToEx(7928, 5295, { callback: cb }); + } + }; + + if (Config.Diablo.Fast) { + if (openSeal + && ![ + sdk.objects.DiabloSealInfector2, sdk.objects.DiabloSealInfector + ].every(s => Common.Diablo.openSeal(s))) { + throw new Error("Failed to open Infector seals."); + } + moveToLoc(); + + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { + throw new Error("Failed to kill Infector"); + } + } else { + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) { + throw new Error("Failed to open Infector seals."); + } + moveToLoc(); + + if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { + throw new Error("Failed to kill Infector"); + } + if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) { + throw new Error("Failed to open Infector seals."); + } + // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss + !openSeal && [3, "infector"].includes(Common.Diablo.sealOrder.last()) && Misc.poll(() => { + if (Common.Diablo.diabloSpawned) return true; + + let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); + if (lastSeal && lastSeal.mode) { + return true; + } + return false; + }, Time.minutes(3), 1000); + } + + Config.Diablo.SealLeader && say("out"); + + return true; + }, + + hammerdinPreAttack: function (name, amount = 5) { + if (me.paladin && Config.AttackSkill[1] === sdk.skills.BlessedHammer) { + let target = Game.getMonster(name); + + if (!target || !target.attackable) return true; + + let positions = [[6, 11], [0, 8], [8, -1], [-9, 2], [0, -11], [8, -8]]; + + for (let i = 0; i < positions.length; i += 1) { + // check if we can move there + if (Attack.validSpot(target.x + positions[i][0], target.y + positions[i][1])) { + Pather.moveTo(target.x + positions[i][0], target.y + positions[i][1]); + Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + + for (let n = 0; n < amount; n += 1) { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); + } + + return true; + } + } + } - return false; - }, - - preattack: function (id) { - let coords = (() => { - switch (id) { - case getLocaleString(sdk.locale.monsters.GrandVizierofChaos): - return Common.Diablo.vizLayout === 1 ? [7676, 5295] : [7684, 5318]; - case getLocaleString(sdk.locale.monsters.LordDeSeis): - return Common.Diablo.seisLayout === 1 ? [7778, 5216] : [7775, 5208]; - case getLocaleString(sdk.locale.monsters.InfectorofSouls): - return Common.Diablo.infLayout === 1 ? [7913, 5292] : [7915, 5280]; - default: - return []; - } - })(); - if (!coords.length) return false; - - switch (me.classid) { - case sdk.player.class.Sorceress: - if ([sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall].includes(Config.AttackSkill[1])) { - me.skillDelay && delay(500); - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, coords[0], coords[1]); - - return true; - } - - break; - case sdk.player.class.Paladin: - return this.hammerdinPreAttack(id, 8); - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let trapCheck = ClassAttack.checkTraps({ x: coords[0], y: coords[1] }); - - if (trapCheck) { - ClassAttack.placeTraps({ x: coords[0], y: coords[1] }, 5); - - return true; - } - } - - break; - } - - return false; - }, - - getBoss: function (name) { - let glow = Game.getObject(sdk.objects.SealGlow); - - if (this.waitForGlow) { - while (true) { - if (!this.preattack(name)) { - delay(500); - } - - glow = Game.getObject(sdk.objects.SealGlow); - - if (glow) { - break; - } - } - } - - for (let i = 0; i < 16; i += 1) { - let boss = Game.getMonster(name); - - if (boss) { - Common.Diablo.hammerdinPreAttack(name, 8); - return (Config.Diablo.Fast ? Attack.kill(boss) : Attack.clear(40, 0, boss, this.sort)); - } - - delay(250); - } - - return !!glow; - }, - - moveToStar: function () { - switch (me.classid) { - case sdk.player.class.Amazon: - case sdk.player.class.Sorceress: - case sdk.player.class.Necromancer: - case sdk.player.class.Assassin: - return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), { returnSpotOnError: true }); - case sdk.player.class.Paladin: - case sdk.player.class.Druid: - case sdk.player.class.Barbarian: - return Pather.moveTo(7788, 5292); - } - - return false; - }, - - diabloPrep: function () { - if (Config.Diablo.SealLeader) { - Pather.moveTo(7763, 5267); - Pather.makePortal() && say("in"); - Pather.moveTo(7788, 5292); - } - - this.moveToStar(); - - let tick = getTickCount(); - - while (getTickCount() - tick < this.diaWaitTime) { - if (getTickCount() - tick >= Time.seconds(8)) { - switch (me.classid) { - 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); - - break; - } - } else { - delay(500); - } - - if (Game.getMonster(sdk.monsters.Diablo)) { - return true; - } - } - - throw new Error("Diablo not found"); - }, - }, - configurable: true, - }); + return false; + }, + + preattack: function (id) { + let coords = (() => { + switch (id) { + case getLocaleString(sdk.locale.monsters.GrandVizierofChaos): + return Common.Diablo.vizLayout === 1 ? [7676, 5295] : [7684, 5318]; + case getLocaleString(sdk.locale.monsters.LordDeSeis): + return Common.Diablo.seisLayout === 1 ? [7778, 5216] : [7775, 5208]; + case getLocaleString(sdk.locale.monsters.InfectorofSouls): + return Common.Diablo.infLayout === 1 ? [7913, 5292] : [7915, 5280]; + default: + return []; + } + })(); + if (!coords.length) return false; + + switch (me.classid) { + case sdk.player.class.Sorceress: + if ([ + sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall + ].includes(Config.AttackSkill[1])) { + me.skillDelay && delay(500); + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, coords[0], coords[1]); + + return true; + } + + break; + case sdk.player.class.Paladin: + return this.hammerdinPreAttack(id, 8); + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let trapCheck = ClassAttack.checkTraps({ x: coords[0], y: coords[1] }); + + if (trapCheck) { + ClassAttack.placeTraps({ x: coords[0], y: coords[1] }, 5); + + return true; + } + } + + break; + } + + return false; + }, + + getBoss: function (name) { + let glow = Game.getObject(sdk.objects.SealGlow); + + if (this.waitForGlow) { + while (true) { + if (!this.preattack(name)) { + delay(500); + } + + glow = Game.getObject(sdk.objects.SealGlow); + + if (glow) { + break; + } + } + } + + for (let i = 0; i < 16; i += 1) { + let boss = Game.getMonster(name); + + if (boss) { + Common.Diablo.hammerdinPreAttack(name, 8); + return (Config.Diablo.Fast ? Attack.kill(boss) : Attack.clear(40, 0, boss, this.sort)); + } + + delay(250); + } + + return !!glow; + }, + + moveToStar: function () { + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Sorceress: + case sdk.player.class.Necromancer: + case sdk.player.class.Assassin: + return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), { returnSpotOnError: true }); + case sdk.player.class.Paladin: + case sdk.player.class.Druid: + case sdk.player.class.Barbarian: + return Pather.moveTo(7788, 5292); + } + + return false; + }, + + diabloPrep: function () { + if (Config.Diablo.SealLeader) { + Pather.moveTo(7763, 5267); + Pather.makePortal() && say("in"); + Pather.moveTo(7788, 5292); + } + + this.moveToStar(); + + let tick = getTickCount(); + + while (getTickCount() - tick < this.diaWaitTime) { + if (getTickCount() - tick >= Time.seconds(8)) { + switch (me.classid) { + 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]); + if (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); + } + + if (Config.AttackSkill[1] === sdk.skills.ShockWeb) { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793, 5293); + } + + delay(500); + + break; + default: + delay(500); + + break; + } + } else { + delay(500); + } + + if (Game.getMonster(sdk.monsters.Diablo)) { + return true; + } + } + + throw new Error("Diablo not found"); + }, + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Common/Leecher.js b/d2bs/kolbot/libs/core/Common/Leecher.js index ee4675a96..f7a65262e 100644 --- a/d2bs/kolbot/libs/core/Common/Leecher.js +++ b/d2bs/kolbot/libs/core/Common/Leecher.js @@ -6,46 +6,46 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Leecher", { - value: { - leadTick: 0, - leader: null, - killLeaderTracker: false, - currentScript: "", - nextScriptAreas: [sdk.areas.TowerCellarLvl5, sdk.areas.PitLvl1, sdk.areas.PitLvl2, sdk.areas.BurialGrounds, - sdk.areas.CatacombsLvl4, sdk.areas.MooMooFarm, sdk.areas.DuranceofHateLvl3, - sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber - ], + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Leecher", { + value: { + leadTick: 0, + leader: null, + killLeaderTracker: false, + currentScript: "", + nextScriptAreas: [sdk.areas.TowerCellarLvl5, sdk.areas.PitLvl1, sdk.areas.PitLvl2, sdk.areas.BurialGrounds, + sdk.areas.CatacombsLvl4, sdk.areas.MooMooFarm, sdk.areas.DuranceofHateLvl3, + sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber + ], - leaderTracker: function () { - if (Common.Leecher.killLeaderTracker) return false; - // check every 3 seconds - if (getTickCount() - Common.Leecher.leadTick < 3000) return true; - Common.Leecher.leadTick = getTickCount(); + leaderTracker: function () { + if (Common.Leecher.killLeaderTracker) return false; + // check every 3 seconds + if (getTickCount() - Common.Leecher.leadTick < 3000) return true; + Common.Leecher.leadTick = getTickCount(); - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - let party = getParty(Common.Leecher.leader); + let party = getParty(Common.Leecher.leader); - if (party) { - // Player has moved on to another script - if (Common.Leecher.nextScriptAreas.includes(party.area)) { - if (Loader.scriptName() === Common.Leecher.currentScript) { - Common.Leecher.killLeaderTracker = true; - throw new Error("Party leader is running a new script"); - } else { - // kill process - return false; - } - } - } + if (party) { + // Player has moved on to another script + if (Common.Leecher.nextScriptAreas.includes(party.area)) { + if (Loader.scriptName() === Common.Leecher.currentScript) { + Common.Leecher.killLeaderTracker = true; + throw new Error("Party leader is running a new script"); + } else { + // kill process + return false; + } + } + } - return true; - } - }, - configurable: true, - }); + return true; + } + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Common/Smith.js b/d2bs/kolbot/libs/core/Common/Smith.js index 61d73dd31..b0640c6fb 100644 --- a/d2bs/kolbot/libs/core/Common/Smith.js +++ b/d2bs/kolbot/libs/core/Common/Smith.js @@ -6,24 +6,24 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Smith", { - value: function () { - if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { - throw new Error("Failed to move to the Smith"); - } + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Smith", { + value: function () { + if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { + throw new Error("Failed to move to the Smith"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); - let malusChest = Game.getObject(sdk.quest.chest.MalusHolder); - !!malusChest && malusChest.distance > 5 && Pather.moveToUnit(malusChest); - Misc.openChest(malusChest); - let malus = Misc.poll(() => Game.getItem(sdk.quest.item.HoradricMalus), 1000, 100); - Pickit.pickItem(malus); - Town.goToTown(); - Town.npcInteract("Charsi"); + Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); + let malusChest = Game.getObject(sdk.quest.chest.MalusHolder); + !!malusChest && malusChest.distance > 5 && Pather.moveToUnit(malusChest); + Misc.openChest(malusChest); + let malus = Misc.poll(() => Game.getItem(sdk.quest.item.HoradricMalus), 1000, 100); + Pickit.pickItem(malus); + Town.goToTown(); + Town.npcInteract("Charsi"); - return !!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete); - }, - configurable: true, - }); + return !!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete); + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index 7b78336e3..1d8a582ae 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -6,341 +6,354 @@ */ (function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Toolsthread", { - value: { - pots: { - Health: 0, - Mana: 1, - Rejuv: 2, - MercHealth: 3, - MercRejuv: 4 - }, - pingTimer: [], - pauseScripts: [], - stopScripts: [], - timerLastDrink: [], - cloneWalked: false, - - /** - * @param {boolean} print - * @returns {boolean} - */ - checkPing: function (print = true) { - // Quit after at least 5 seconds in game - if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; - - for (let i = 0; i < Config.PingQuit.length; i += 1) { - if (Config.PingQuit[i].Ping > 0) { - if (me.ping >= Config.PingQuit[i].Ping) { - me.overhead("High Ping"); - - if (this.pingTimer[i] === undefined || this.pingTimer[i] === 0) { - this.pingTimer[i] = getTickCount(); - } - - if (getTickCount() - this.pingTimer[i] >= Config.PingQuit[i].Duration * 1000) { - print && D2Bot.printToConsole("High ping (" + me.ping + "/" + Config.PingQuit[i].Ping + ") - leaving game.", sdk.colors.D2Bot.Red); - scriptBroadcast("pingquit"); - scriptBroadcast("quit"); - - return true; - } - } else { - this.pingTimer[i] = 0; - } - } - } - - return false; - }, - - initQuitList: function () { - let temp = []; - - for (let i = 0; i < Config.QuitList.length; i += 1) { - if (FileTools.exists("data/" + Config.QuitList[i] + ".json")) { - let string = FileAction.read("data/" + Config.QuitList[i] + ".json"); - - if (string) { - let obj = JSON.parse(string); - - if (obj && obj.hasOwnProperty("name")) { - temp.push(obj.name); - } - } - } - } - - Config.QuitList = temp.slice(0); - }, - - togglePause: function (townChicken = false) { - for (let i = 0; i < this.pauseScripts.length; i++) { - let script = getScript(this.pauseScripts[i]); - - if (script) { - if (script.running) { - this.pauseScripts[i] === "default.dbj" && console.log("ÿc1Pausing."); - - // don't pause townchicken during clone walk - if (this.pauseScripts[i] !== "threads/townchicken.js" || !this.cloneWalked) { - script.pause(); - } - } else { - if (this.pauseScripts[i] === "default.dbj") { - if (townChicken) continue; // don't resume default if we are in the middle of townChicken - console.log("ÿc2Resuming."); - } - script.resume(); - } - } - } - - return true; - }, - - stopDefault: function () { - for (let i = 0; i < this.stopScripts.length; i++) { - try { - let script = getScript(this.stopScripts[i]); - !!script && script.running && script.stop(); - } catch (e) { - console.error(e); - } - } - - return true; - }, - - exit: function (chickenExit = false) { - try { - chickenExit && D2Bot.updateChickens(); - Config.LogExperience && Experience.log(); - console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); - this.stopDefault(); - quit(); - } finally { - while (me.ingame) { - delay(100); - } - } - - return true; - }, - - /** - * @param {number} pottype - * @param {number} type - * @returns {ItemUnit | false} - */ - getPotion: function (pottype, type) { - if (!pottype) return false; - - let items = me.getItemsEx().filter((item) => item.itemType === pottype); - if (items.length === 0) return false; - - // Get highest id = highest potion first - items.sort(function (a, b) { - return b.classid - a.classid; - }); - - for (let i = 0; i < items.length; i += 1) { - if (type < this.pots.MercHealth && items[i].isInInventory && items[i].itemType === pottype) { - console.log("ÿc2Drinking potion from inventory."); - return items[i]; - } - - if (items[i].isInBelt && items[i].itemType === pottype) { - console.log("ÿc2" + (type > 2 ? "Giving Merc" : "Drinking") + " potion from belt."); - return items[i]; - } - } - - return false; - }, - - /** - * @param {number} type - * @returns {boolean} - * @todo add stamina/thawing/antidote pot drinking here - */ - drinkPotion: function (type) { - if (type === undefined) return false; - let tNow = getTickCount(); - - switch (type) { - case this.pots.Health: - case this.pots.Mana: - if ((this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 1000)) || me.getState(type === this.pots.Health ? sdk.states.HealthPot : sdk.states.ManaPot)) { - return false; - } - - break; - case this.pots.Rejuv: - // small delay for juvs just to prevent using more at once - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 300)) { - return false; - } - - break; - case this.pots.MercRejuv: - // larger delay for juvs just to prevent using more at once, considering merc update rate - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 2000)) { - return false; - } - - break; - default: - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 8000)) { - return false; - } - - break; - } - - let pottype = (() => { - switch (type) { - case this.pots.Health: - case this.pots.MercHealth: - return sdk.items.type.HealingPotion; - case this.pots.Mana: - return sdk.items.type.ManaPotion; - default: - return sdk.items.type.RejuvPotion; - } - })(); - - let potion = this.getPotion(pottype, type); - - if (potion) { - if (me.dead) return false; - - if (me.mode === sdk.player.mode.SkillActionSequence) { - while (me.mode === sdk.player.mode.SkillActionSequence) { - delay (25); - } - } - - try { - type < this.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); - } catch (e) { - console.error(e); - } - - this.timerLastDrink[type] = getTickCount(); - - return true; - } - - return false; - }, - - checkVipers: function () { - let monster = Game.getMonster(sdk.monsters.TombViper2); - - if (monster) { - do { - if (monster.getState(sdk.states.Revive)) { - let owner = monster.getParent(); - - if (owner && owner.name !== me.name) { - D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); - - return true; - } - } - } while (monster.getNext()); - } - - return false; - }, - - getIronGolem: function () { - let golem = Game.getMonster(sdk.summons.IronGolem); - - if (golem) { - do { - let owner = golem.getParent(); - - if (owner && owner.name === me.name) { - return copyUnit(golem); - } - } while (golem.getNext()); - } - - return false; - }, - - getNearestPreset: function () { - let id; - let unit = getPresetUnits(me.area); - let dist = 99; - - for (let i = 0; i < unit.length; i += 1) { - if (getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y) < dist) { - dist = getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y); - id = unit[i].type + " " + unit[i].id; - } - } - - return id || ""; - }, - - /** - * @param {MeType | MercUnit} unit - * @returns {string} - */ - getStatsString: function (unit) { - let realFCR = unit.getStat(sdk.stats.FCR); - let realIAS = unit.getStat(sdk.stats.IAS); - let realFBR = unit.getStat(sdk.stats.FBR); - let realFHR = unit.getStat(sdk.stats.FHR); - // me.getStat(sdk.stats.FasterCastRate) will return real FCR from gear + Config.FCR from char cfg - - if (unit === me) { - realFCR -= Config.FCR; - realIAS -= Config.IAS; - realFBR -= Config.FBR; - realFHR -= Config.FHR; - } - - let maxHellFireRes = 75 + unit.getStat(sdk.stats.MaxFireResist); - let hellFireRes = unit.getRes(sdk.stats.FireResist, sdk.difficulty.Hell); - hellFireRes > maxHellFireRes && (hellFireRes = maxHellFireRes); - - let maxHellColdRes = 75 + unit.getStat(sdk.stats.MaxColdResist); - let hellColdRes = unit.getRes(sdk.stats.ColdResist, sdk.difficulty.Hell); - hellColdRes > maxHellColdRes && (hellColdRes = maxHellColdRes); - - let maxHellLightRes = 75 + unit.getStat(sdk.stats.MaxLightResist); - let hellLightRes = unit.getRes(sdk.stats.LightResist, sdk.difficulty.Hell); - hellLightRes > maxHellLightRes && (hellLightRes = maxHellLightRes); - - let maxHellPoisonRes = 75 + unit.getStat(sdk.stats.MaxPoisonResist); - let hellPoisonRes = unit.getRes(sdk.stats.PoisonResist, sdk.difficulty.Hell); - hellPoisonRes > maxHellPoisonRes && (hellPoisonRes = maxHellPoisonRes); - - let str = - "ÿc4Character Level: ÿc0" + unit.charlvl + (unit === me ? " ÿc4Difficulty: ÿc0" + sdk.difficulty.nameOf(me.diff) + " ÿc4HighestActAvailable: ÿc0" + me.highestAct : "") + "\n" - + "ÿc1FR: ÿc0" + unit.getStat(sdk.stats.FireResist) + "ÿc1 Applied FR: ÿc0" + unit.fireRes - + "/ÿc3 CR: ÿc0" + unit.getStat(sdk.stats.ColdResist) + "ÿc3 Applied CR: ÿc0" + unit.coldRes - + "/ÿc9 LR: ÿc0" + unit.getStat(sdk.stats.LightResist) + "ÿc9 Applied LR: ÿc0" + unit.lightRes - + "/ÿc2 PR: ÿc0" + unit.getStat(sdk.stats.PoisonResist) + "ÿc2 Applied PR: ÿc0" + unit.poisonRes + "\n" - + (!me.hell ? "Hell res: ÿc1" + hellFireRes + "ÿc0/ÿc3" + hellColdRes + "ÿc0/ÿc9" + hellLightRes + "ÿc0/ÿc2" + hellPoisonRes + "ÿc0\n" : "") - + "ÿc4MF: ÿc0" + unit.getStat(sdk.stats.MagicBonus) + "ÿc4 GF: ÿc0" + unit.getStat(sdk.stats.GoldBonus) - + " ÿc4FCR: ÿc0" + realFCR + " ÿc4IAS: ÿc0" + realIAS + " ÿc4FBR: ÿc0" + realFBR - + " ÿc4FHR: ÿc0" + realFHR + " ÿc4FRW: ÿc0" + unit.getStat(sdk.stats.FRW) + "\n" - + "ÿc4CB: ÿc0" + unit.getStat(sdk.stats.CrushingBlow) + " ÿc4DS: ÿc0" + unit.getStat(sdk.stats.DeadlyStrike) - + " ÿc4OW: ÿc0" + unit.getStat(sdk.stats.OpenWounds) - + " ÿc1LL: ÿc0" + unit.getStat(sdk.stats.LifeLeech) + " ÿc3ML: ÿc0" + unit.getStat(sdk.stats.ManaLeech) - + " ÿc8DR: ÿc0" + unit.getStat(sdk.stats.DamageResist) + "% + " + unit.getStat(sdk.stats.NormalDamageReduction) - + " ÿc8MDR: ÿc0" + unit.getStat(sdk.stats.MagicResist) + "% + " + unit.getStat(sdk.stats.MagicDamageReduction) + "\n" - + (unit.getStat(sdk.stats.CannotbeFrozen) > 0 ? "ÿc3Cannot be Frozenÿc1\n" : "\n"); - - return str; - }, - }, - configurable: true, - }); + typeof Common !== "object" && (Common = {}); + Object.defineProperty(Common, "Toolsthread", { + value: { + pots: { + Health: 0, + Mana: 1, + Rejuv: 2, + MercHealth: 3, + MercRejuv: 4 + }, + pingTimer: [], + pauseScripts: [], + stopScripts: [], + timerLastDrink: [], + cloneWalked: false, + + /** + * @param {boolean} print + * @returns {boolean} + */ + checkPing: function (print = true) { + // Quit after at least 5 seconds in game + if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; + + for (let i = 0; i < Config.PingQuit.length; i += 1) { + if (Config.PingQuit[i].Ping > 0) { + if (me.ping >= Config.PingQuit[i].Ping) { + me.overhead("High Ping"); + + if (this.pingTimer[i] === undefined || this.pingTimer[i] === 0) { + this.pingTimer[i] = getTickCount(); + } + + if (getTickCount() - this.pingTimer[i] >= Config.PingQuit[i].Duration * 1000) { + if (print) { + D2Bot.printToConsole( + "High ping (" + me.ping + "/" + Config.PingQuit[i].Ping + ") - leaving game.", + sdk.colors.D2Bot.Red + ); + } + scriptBroadcast("pingquit"); + scriptBroadcast("quit"); + + return true; + } + } else { + this.pingTimer[i] = 0; + } + } + } + + return false; + }, + + initQuitList: function () { + let temp = []; + + for (let i = 0; i < Config.QuitList.length; i += 1) { + if (FileTools.exists("data/" + Config.QuitList[i] + ".json")) { + let string = FileAction.read("data/" + Config.QuitList[i] + ".json"); + + if (string) { + let obj = JSON.parse(string); + + if (obj && obj.hasOwnProperty("name")) { + temp.push(obj.name); + } + } + } + } + + Config.QuitList = temp.slice(0); + }, + + togglePause: function (townChicken = false) { + for (let i = 0; i < this.pauseScripts.length; i++) { + let script = getScript(this.pauseScripts[i]); + + if (script) { + if (script.running) { + this.pauseScripts[i] === "default.dbj" && console.log("ÿc1Pausing."); + + // don't pause townchicken during clone walk + if (this.pauseScripts[i] !== "threads/townchicken.js" || !this.cloneWalked) { + script.pause(); + } + } else { + if (this.pauseScripts[i] === "default.dbj") { + if (townChicken) continue; // don't resume default if we are in the middle of townChicken + console.log("ÿc2Resuming."); + } + script.resume(); + } + } + } + + return true; + }, + + stopDefault: function () { + for (let i = 0; i < this.stopScripts.length; i++) { + try { + let script = getScript(this.stopScripts[i]); + !!script && script.running && script.stop(); + } catch (e) { + console.error(e); + } + } + + return true; + }, + + exit: function (chickenExit = false) { + try { + chickenExit && D2Bot.updateChickens(); + Config.LogExperience && Experience.log(); + console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); + this.stopDefault(); + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } + + return true; + }, + + /** + * @param {number} pottype + * @param {number} type + * @returns {ItemUnit | false} + */ + getPotion: function (pottype, type) { + if (!pottype) return false; + + let items = me.getItemsEx().filter((item) => item.itemType === pottype); + if (items.length === 0) return false; + + // Get highest id = highest potion first + items.sort(function (a, b) { + return b.classid - a.classid; + }); + + for (let i = 0; i < items.length; i += 1) { + if (type < this.pots.MercHealth && items[i].isInInventory && items[i].itemType === pottype) { + console.log("ÿc2Drinking potion from inventory."); + return items[i]; + } + + if (items[i].isInBelt && items[i].itemType === pottype) { + console.log("ÿc2" + (type > 2 ? "Giving Merc" : "Drinking") + " potion from belt."); + return items[i]; + } + } + + return false; + }, + + /** + * @param {number} type + * @returns {boolean} + * @todo add stamina/thawing/antidote pot drinking here + */ + drinkPotion: function (type) { + if (type === undefined) return false; + let tNow = getTickCount(); + + switch (type) { + case this.pots.Health: + case this.pots.Mana: + if ((this.timerLastDrink[type] + && (tNow - this.timerLastDrink[type] < 1000)) + || me.getState(type === this.pots.Health ? sdk.states.HealthPot : sdk.states.ManaPot)) { + return false; + } + + break; + case this.pots.Rejuv: + // small delay for juvs just to prevent using more at once + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 300)) { + return false; + } + + break; + case this.pots.MercRejuv: + // larger delay for juvs just to prevent using more at once, considering merc update rate + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 2000)) { + return false; + } + + break; + default: + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 8000)) { + return false; + } + + break; + } + + let pottype = (() => { + switch (type) { + case this.pots.Health: + case this.pots.MercHealth: + return sdk.items.type.HealingPotion; + case this.pots.Mana: + return sdk.items.type.ManaPotion; + default: + return sdk.items.type.RejuvPotion; + } + })(); + + let potion = this.getPotion(pottype, type); + + if (potion) { + if (me.dead) return false; + + if (me.mode === sdk.player.mode.SkillActionSequence) { + while (me.mode === sdk.player.mode.SkillActionSequence) { + delay (25); + } + } + + try { + type < this.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); + } catch (e) { + console.error(e); + } + + this.timerLastDrink[type] = getTickCount(); + + return true; + } + + return false; + }, + + checkVipers: function () { + let monster = Game.getMonster(sdk.monsters.TombViper2); + + if (monster) { + do { + if (monster.getState(sdk.states.Revive)) { + let owner = monster.getParent(); + + if (owner && owner.name !== me.name) { + D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); + + return true; + } + } + } while (monster.getNext()); + } + + return false; + }, + + getIronGolem: function () { + let golem = Game.getMonster(sdk.summons.IronGolem); + + if (golem) { + do { + let owner = golem.getParent(); + + if (owner && owner.name === me.name) { + return copyUnit(golem); + } + } while (golem.getNext()); + } + + return false; + }, + + getNearestPreset: function () { + let id; + let unit = getPresetUnits(me.area); + let dist = 99; + + for (let i = 0; i < unit.length; i += 1) { + if (getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y) < dist) { + dist = getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y); + id = unit[i].type + " " + unit[i].id; + } + } + + return id || ""; + }, + + /** + * @param {MeType | MercUnit} unit + * @returns {string} + */ + getStatsString: function (unit) { + let realFCR = unit.getStat(sdk.stats.FCR); + let realIAS = unit.getStat(sdk.stats.IAS); + let realFBR = unit.getStat(sdk.stats.FBR); + let realFHR = unit.getStat(sdk.stats.FHR); + // me.getStat(sdk.stats.FasterCastRate) will return real FCR from gear + Config.FCR from char cfg + + if (unit === me) { + realFCR -= Config.FCR; + realIAS -= Config.IAS; + realFBR -= Config.FBR; + realFHR -= Config.FHR; + } + + let maxHellFireRes = 75 + unit.getStat(sdk.stats.MaxFireResist); + let hellFireRes = unit.getRes(sdk.stats.FireResist, sdk.difficulty.Hell); + hellFireRes > maxHellFireRes && (hellFireRes = maxHellFireRes); + + let maxHellColdRes = 75 + unit.getStat(sdk.stats.MaxColdResist); + let hellColdRes = unit.getRes(sdk.stats.ColdResist, sdk.difficulty.Hell); + hellColdRes > maxHellColdRes && (hellColdRes = maxHellColdRes); + + let maxHellLightRes = 75 + unit.getStat(sdk.stats.MaxLightResist); + let hellLightRes = unit.getRes(sdk.stats.LightResist, sdk.difficulty.Hell); + hellLightRes > maxHellLightRes && (hellLightRes = maxHellLightRes); + + let maxHellPoisonRes = 75 + unit.getStat(sdk.stats.MaxPoisonResist); + let hellPoisonRes = unit.getRes(sdk.stats.PoisonResist, sdk.difficulty.Hell); + hellPoisonRes > maxHellPoisonRes && (hellPoisonRes = maxHellPoisonRes); + + let str = + "ÿc4Character Level: ÿc0" + unit.charlvl + + (unit === me ? " ÿc4Difficulty: ÿc0" + sdk.difficulty.nameOf(me.diff) + + " ÿc4HighestActAvailable: ÿc0" + me.highestAct : "") + "\n" + + "ÿc1FR: ÿc0" + unit.getStat(sdk.stats.FireResist) + "ÿc1 Applied FR: ÿc0" + unit.fireRes + + "/ÿc3 CR: ÿc0" + unit.getStat(sdk.stats.ColdResist) + "ÿc3 Applied CR: ÿc0" + unit.coldRes + + "/ÿc9 LR: ÿc0" + unit.getStat(sdk.stats.LightResist) + "ÿc9 Applied LR: ÿc0" + unit.lightRes + + "/ÿc2 PR: ÿc0" + unit.getStat(sdk.stats.PoisonResist) + "ÿc2 Applied PR: ÿc0" + unit.poisonRes + "\n" + + (!me.hell + ? "Hell res: ÿc1" + hellFireRes + + "ÿc0/ÿc3" + hellColdRes + "ÿc0/ÿc9" + hellLightRes + "ÿc0/ÿc2" + hellPoisonRes + "ÿc0\n" : "") + + "ÿc4MF: ÿc0" + unit.getStat(sdk.stats.MagicBonus) + "ÿc4 GF: ÿc0" + unit.getStat(sdk.stats.GoldBonus) + + " ÿc4FCR: ÿc0" + realFCR + " ÿc4IAS: ÿc0" + realIAS + " ÿc4FBR: ÿc0" + realFBR + + " ÿc4FHR: ÿc0" + realFHR + " ÿc4FRW: ÿc0" + unit.getStat(sdk.stats.FRW) + "\n" + + "ÿc4CB: ÿc0" + unit.getStat(sdk.stats.CrushingBlow) + " ÿc4DS: ÿc0" + unit.getStat(sdk.stats.DeadlyStrike) + + " ÿc4OW: ÿc0" + unit.getStat(sdk.stats.OpenWounds) + + " ÿc1LL: ÿc0" + unit.getStat(sdk.stats.LifeLeech) + " ÿc3ML: ÿc0" + unit.getStat(sdk.stats.ManaLeech) + + " ÿc8DR: ÿc0" + unit.getStat(sdk.stats.DamageResist) + + "% + " + unit.getStat(sdk.stats.NormalDamageReduction) + + " ÿc8MDR: ÿc0" + unit.getStat(sdk.stats.MagicResist) + + "% + " + unit.getStat(sdk.stats.MagicDamageReduction) + "\n" + + (unit.getStat(sdk.stats.CannotbeFrozen) > 0 ? "ÿc3Cannot be Frozenÿc1\n" : "\n"); + + return str; + }, + }, + configurable: true, + }); })(Common); diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 74c92ce32..0d87f2874 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -8,685 +8,688 @@ const Scripts = {}; let Config = { - init: function (notify = true) { - const className = sdk.player.class.nameOf(me.classid); - 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", - }))(className, me.profile, me.charname, me.realm); - let configFilename = ""; - - for (let i = 0; i < 5; i++) { - switch (i) { - case 0: // Custom config - includeIfNotIncluded("config/_customconfig.js"); - - for (let n in CustomConfig) { - if (CustomConfig.hasOwnProperty(n) && CustomConfig[n].includes(me.profile)) { - notify && console.log("ÿc2Loading custom config: ÿc9" + n + ".js"); - configFilename = n + ".js"; - - break; - } - } - - break; - default: - configFilename = formats[i]; - - break; - } - - if (configFilename && FileTools.exists("libs/config/" + configFilename)) { - break; - } - } - - if (FileTools.exists("libs/config/" + configFilename)) { - try { - if (!include("config/" + configFilename)) { - throw new Error(); - } - } catch (e1) { - throw new Error("Failed to load character config."); - } - } else { - if (notify) { - print("ÿc1" + className + "." + me.charname + ".js not found!"); // Use the primary format - print("ÿc1Loading default config."); - } - - // Try to find default config - if (!FileTools.exists("libs/config/" + className + ".js")) { - D2Bot.printToConsole("Not going well? Read the guides: https://github.com/blizzhackers/documentation"); - throw new Error("ÿc1Default config not found. \nÿc9 Try reading the kolbot guides."); - } - - try { - if (!include("config/" + className + ".js")) { - throw new Error(); - } - } catch (e) { - throw new Error("ÿc1Failed to load default config."); - } - } - - try { - LoadConfig.call(); - Config.Loaded = true; - } catch (e2) { - if (notify) { - print("ÿ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; - global.say = (what) => print("Tryed to say: " + what); - } - - try { - if (Config.AutoBuild.Enabled === true && !isIncluded("core/Auto/AutoBuild.js") && include("core/Auto/AutoBuild.js")) { - AutoBuild.initialize(); - } - } catch (e3) { - print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); - console.error(e3); - } - }, - - // dev - Loaded: false, - DebugMode: { - Path: false, - Stack: false, - Memory: false, - Skill: false, - Town: false, - }, - - // Time - StartDelay: 0, - PickDelay: 0, - AreaDelay: 0, - MinGameTime: 0, - MaxGameTime: 0, - - // Healing and chicken - LifeChicken: 0, - ManaChicken: 0, - UseHP: 0, - UseMP: 0, - UseRejuvHP: 0, - UseRejuvMP: 0, - UseMercHP: 0, - UseMercRejuv: 0, - MercChicken: 0, - IronGolemChicken: 0, - HealHP: 0, - HealMP: 0, - HealStatus: false, - TownHP: 0, - TownMP: 0, - - // special pots - StackThawingPots: { - enabled: false, - quantity: 12, - }, - StackAntidotePots: { - enabled: false, - quantity: 12, - }, - StackStaminaPots: { - enabled: false, - quantity: 12, - }, - - // General - AutoMap: false, - LastMessage: "", - UseMerc: false, - MercWatch: false, - LowGold: 0, - StashGold: 0, - FieldID: { - Enabled: false, - PacketID: true, - UsedSpace: 90, - }, - DroppedItemsAnnounce: { - Enable: false, - Quality: [], - LogToOOG: false, - OOGQuality: [] - }, - CainID: { - Enable: false, - MinGold: 0, - MinUnids: 0 - }, - Inventory: [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - ], - SortSettings: { - SortInventory: true, - SortStash: true, - PlugYStash: false, - 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 - ], - }, - LocalChat: { - Enabled: false, - Toggle: false, - Mode: 0 - }, - Silence: false, - PublicMode: false, - PartyAfterScript: false, - - /** @type {string[]} */ - Greetings: [], - - /** @type {string[]} */ - DeathMessages: [], - - /** @type {string[]} */ - Congratulations: [], - ShitList: false, - UnpartyShitlisted: false, - Leader: "", - QuitList: [], - QuitListMode: 0, - QuitListDelay: [], - HPBuffer: 0, - MPBuffer: 0, - RejuvBuffer: 0, - PickRange: 40, - MakeRoom: true, - ClearInvOnStart: false, - FastPick: false, - ManualPlayPick: false, - OpenChests: { - Enabled: false, - Range: 15, - Types: ["chest", "chest3", "armorstand", "weaponrack"] - }, - PickitFiles: [], - BeltColumn: [], - MinColumn: [], - SkipId: [], - SkipEnchant: [], - SkipImmune: [], - SkipAura: [], - SkipException: [], - ScanShrines: [], - Debug: false, - - AutoMule: { - Trigger: [], - Force: [], - Exclude: [] - }, - - ItemInfo: false, - ItemInfoQuality: [], - - LogKeys: false, - LogOrgans: true, - LogLowRunes: false, - LogMiddleRunes: false, - LogHighRunes: true, - LogLowGems: false, - LogHighGems: false, - SkipLogging: [], - ShowCubingInfo: true, - - Cubing: false, - CubeRepair: false, - RepairPercent: 40, - Recipes: [], - MakeRunewords: false, - /** + init: function (notify = true) { + const className = sdk.player.class.nameOf(me.classid); + 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", + }))(className, me.profile, me.charname, me.realm); + let configFilename = ""; + + for (let i = 0; i < 5; i++) { + switch (i) { + case 0: // Custom config + includeIfNotIncluded("config/_customconfig.js"); + + for (let n in CustomConfig) { + if (CustomConfig.hasOwnProperty(n) && CustomConfig[n].includes(me.profile)) { + notify && console.log("ÿc2Loading custom config: ÿc9" + n + ".js"); + configFilename = n + ".js"; + + break; + } + } + + break; + default: + configFilename = formats[i]; + + break; + } + + if (configFilename && FileTools.exists("libs/config/" + configFilename)) { + break; + } + } + + if (FileTools.exists("libs/config/" + configFilename)) { + try { + if (!include("config/" + configFilename)) { + throw new Error(); + } + } catch (e1) { + throw new Error("Failed to load character config."); + } + } else { + if (notify) { + print("ÿc1" + className + "." + me.charname + ".js not found!"); // Use the primary format + print("ÿc1Loading default config."); + } + + // Try to find default config + if (!FileTools.exists("libs/config/" + className + ".js")) { + D2Bot.printToConsole("Not going well? Read the guides: https://github.com/blizzhackers/documentation"); + throw new Error("ÿc1Default config not found. \nÿc9 Try reading the kolbot guides."); + } + + try { + if (!include("config/" + className + ".js")) { + throw new Error(); + } + } catch (e) { + throw new Error("ÿc1Failed to load default config."); + } + } + + try { + LoadConfig.call(); + Config.Loaded = true; + } catch (e2) { + if (notify) { + // print("ÿc8Error in " + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + "(line " + e2.lineNumber + "): " + e2.message); + console.error(e2); + + 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; + global.say = (what) => print("Tryed to say: " + what); + } + + try { + if (Config.AutoBuild.Enabled === true && includeIfNotIncluded("core/Auto/AutoBuild.js")) { + AutoBuild.initialize(); + } + } catch (e3) { + print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); + console.error(e3); + } + }, + + // dev + Loaded: false, + DebugMode: { + Path: false, + Stack: false, + Memory: false, + Skill: false, + Town: false, + }, + + // Time + StartDelay: 0, + PickDelay: 0, + AreaDelay: 0, + MinGameTime: 0, + MaxGameTime: 0, + + // Healing and chicken + LifeChicken: 0, + ManaChicken: 0, + UseHP: 0, + UseMP: 0, + UseRejuvHP: 0, + UseRejuvMP: 0, + UseMercHP: 0, + UseMercRejuv: 0, + MercChicken: 0, + IronGolemChicken: 0, + HealHP: 0, + HealMP: 0, + HealStatus: false, + TownHP: 0, + TownMP: 0, + + // special pots + StackThawingPots: { + enabled: false, + quantity: 12, + }, + StackAntidotePots: { + enabled: false, + quantity: 12, + }, + StackStaminaPots: { + enabled: false, + quantity: 12, + }, + + // General + AutoMap: false, + LastMessage: "", + UseMerc: false, + MercWatch: false, + LowGold: 0, + StashGold: 0, + FieldID: { + Enabled: false, + PacketID: true, + UsedSpace: 90, + }, + DroppedItemsAnnounce: { + Enable: false, + Quality: [], + LogToOOG: false, + OOGQuality: [] + }, + CainID: { + Enable: false, + MinGold: 0, + MinUnids: 0 + }, + Inventory: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ], + SortSettings: { + SortInventory: true, + SortStash: true, + PlugYStash: false, + 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 + ], + }, + LocalChat: { + Enabled: false, + Toggle: false, + Mode: 0 + }, + Silence: false, + PublicMode: false, + PartyAfterScript: false, + + /** @type {string[]} */ + Greetings: [], + + /** @type {string[]} */ + DeathMessages: [], + + /** @type {string[]} */ + Congratulations: [], + ShitList: false, + UnpartyShitlisted: false, + Leader: "", + QuitList: [], + QuitListMode: 0, + QuitListDelay: [], + HPBuffer: 0, + MPBuffer: 0, + RejuvBuffer: 0, + PickRange: 40, + MakeRoom: true, + ClearInvOnStart: false, + FastPick: false, + ManualPlayPick: false, + OpenChests: { + Enabled: false, + Range: 15, + Types: ["chest", "chest3", "armorstand", "weaponrack"] + }, + PickitFiles: [], + BeltColumn: [], + MinColumn: [], + SkipId: [], + SkipEnchant: [], + SkipImmune: [], + SkipAura: [], + SkipException: [], + ScanShrines: [], + Debug: false, + + AutoMule: { + Trigger: [], + Force: [], + Exclude: [] + }, + + ItemInfo: false, + ItemInfoQuality: [], + + LogKeys: false, + LogOrgans: true, + LogLowRunes: false, + LogMiddleRunes: false, + LogHighRunes: true, + LogLowGems: false, + LogHighGems: false, + SkipLogging: [], + ShowCubingInfo: true, + + Cubing: false, + CubeRepair: false, + RepairPercent: 40, + Recipes: [], + MakeRunewords: false, + /** * @type {[runeword, string | number, ?boolean][]} */ - Runewords: [], - KeepRunewords: [], - LadderOveride: false, - Gamble: false, - GambleItems: [], - GambleGoldStart: 0, - GambleGoldStop: 0, - MiniShopBot: false, - TeleSwitch: false, - MFSwitchPercent: 0, - PrimarySlot: -1, - LogExperience: false, - TownCheck: false, - PingQuit: [{ Ping: 0, Duration: 0 }], - PacketShopping: false, - - // Fastmod - FCR: 0, - FHR: 0, - FBR: 0, - IAS: 0, - PacketCasting: 0, - WaypointMenu: true, - - // Anti-hostile - AntiHostile: false, - RandomPrecast: false, - HostileAction: 0, - TownOnHostile: false, - ViperCheck: false, - - // DClone - StopOnDClone: false, - SoJWaitTime: 0, - KillDclone: false, - DCloneQuit: false, - DCloneWaitTime: 30, - - // Experimental - FastParty: false, - AutoEquip: false, - - // GameData - ChampionBias: 60, - - UseCta: true, - ForcePrecast: false, - - // Attack specific - Dodge: false, - DodgeRange: 15, - DodgeHP: 100, - AttackSkill: [], - LowManaSkill: [], - CustomAttack: {}, - TeleStomp: false, - NoTele: false, - ClearType: false, - ClearPath: false, - BossPriority: false, - MaxAttackCount: 300, - - // Amazon specific - LightningFuryDelay: 0, - UseInnerSight: false, - UseSlowMissiles: false, - UseDecoy: false, - SummonValkyrie: false, - - // Sorceress specific - UseTelekinesis: false, - CastStatic: false, - StaticList: [], - UseEnergyShield: false, - UseColdArmor: true, - - // Necromancer specific - Golem: 0, - ActiveSummon: false, - Skeletons: 0, - SkeletonMages: 0, - Revives: 0, - ReviveUnstackable: false, - PoisonNovaDelay: 2000, - Curse: [], - CustomCurse: [], - ExplodeCorpses: 0, - - // Paladin speficic - Redemption: [0, 0], - Charge: false, - Vigor: false, - AvoidDolls: false, - - // Barbarian specific - FindItem: false, - FastFindItem: false, - FindItemSwitch: false, - UseWarcries: true, - - // Druid specific - Wereform: 0, - SummonRaven: 0, - SummonAnimal: 0, - SummonVine: 0, - SummonSpirit: 0, - - // Assassin specific - UseTraps: false, - Traps: [], - BossTraps: [], - UseFade: false, - UseBoS: false, - UseVenom: false, - UseBladeShield: false, - UseCloakofShadows: false, - AggressiveCloak: false, - SummonShadow: false, - - // Custom Attack - CustomClassAttack: "", // If set it loads core/Attack/[CustomClassAttack].js - - MapMode: { - UseOwnItemFilter: false, - }, - - // Script specific - MFLeader: false, - Mausoleum: { - KillBishibosh: false, - KillBloodRaven: false, - ClearCrypt: false - }, - Cows: { - DontMakePortal: false, - JustMakePortal: false, - KillKing: false - }, - Tombs: { - KillDuriel: false, - WalkClear: false, - }, - Eldritch: { - OpenChest: false, - KillSharptooth: false, - KillShenk: false, - KillDacFarren: false - }, - Pindleskin: { - UseWaypoint: false, - KillNihlathak: false, - ViperQuit: false - }, - Nihlathak: { - ViperQuit: false, - UseWaypoint: false, - }, - Pit: { - ClearPath: false, - ClearPit1: false - }, - Snapchip: { - ClearIcyCellar: false - }, - Frozenstein: { - ClearFrozenRiver: false - }, - Rakanishu: { - KillGriswold: false - }, - AutoBaal: { - Leader: "", - FindShrine: false, - LeechSpot: [15115, 5050], - LongRangeSupport: false - }, - KurastChests: { - LowerKurast: false, - Bazaar: false, - Sewers1: false, - Sewers2: false - }, - Countess: { - KillGhosts: false - }, - Baal: { - DollQuit: false, - SoulQuit: false, - KillBaal: false, - HotTPMessage: "Hot TP!", - SafeTPMessage: "Safe TP!", - BaalMessage: "Baal!" - }, - BaalAssistant: { - KillNihlathak: false, - FastChaos: false, - Wait: 120, - Helper: false, - GetShrine: false, - GetShrineWaitForHotTP: false, - DollQuit: false, - SoulQuit: false, - SkipTP: false, - WaitForSafeTP: false, - KillBaal: false, - HotTPMessage: [], - SafeTPMessage: [], - BaalMessage: [], - NextGameMessage: [] - }, - BaalHelper: { - Wait: 120, - KillNihlathak: false, - FastChaos: false, - DollQuit: false, - KillBaal: false, - SkipTP: false - }, - Corpsefire: { - ClearDen: false - }, - Hephasto: { - ClearRiver: false, - ClearType: false - }, - Diablo: { - WalkClear: false, - Entrance: false, - JustViz: false, - SealLeader: false, - Fast: false, - SealWarning: "Leave the seals alone!", - EntranceTP: "Entrance TP up", - StarTP: "Star TP up", - DiabloMsg: "Diablo", - ClearRadius: 30, - SealOrder: ["vizier", "seis", "infector"] - }, - DiabloHelper: { - Wait: 120, - Entrance: false, - SkipIfBaal: false, - SkipTP: false, - OpenSeals: false, - SafePrecast: true, - ClearRadius: 30, - SealOrder: ["vizier", "seis", "infector"], - RecheckSeals: false - }, - MFHelper: { - BreakClearLevel: false - }, - Wakka: { - Wait: 1, - StopAtLevel: 99, - StopProfile: false, - SkipIfBaal: true, - }, - BattleOrders: { - Mode: 0, - Getters: [], - Idle: false, - QuitOnFailure: false, - SkipIfTardy: true, - Wait: 10 - }, - BoBarbHelper: { - Mode: -1, - Wp: 35 - }, - ControlBot: { - Bo: false, - Cows: { - MakeCows: false, - GetLeg: false, - }, - Chant: { - Enchant: false, - AutoEnchant: false, - }, - Wps: { - GiveWps: false, - SecurePortal: false, - }, - EndMessage: "", - GameLength: 20 - }, - IPHunter: { - IPList: [], - GameLength: 3 - }, - Follower: { - Leader: "" - }, - Mephisto: { - MoatTrick: false, - KillCouncil: false, - TakeRedPortal: false - }, - ShopBot: { - ScanIDs: [], - ShopNPC: "anya", - CycleDelay: 0, - QuitOnMatch: false - }, - Coldworm: { - KillBeetleburst: false, - ClearMaggotLair: false - }, - Summoner: { - FireEye: false - }, - AncientTunnels: { - OpenChest: false, - KillDarkElder: false - }, - OrgTorch: { - WaitForKeys: false, - WaitTimeout: 0, - UseSalvation: false, - GetFade: false, - MakeTorch: true, - PreGame: { - Thawing: { Drink: 0, At: [] }, - Antidote: { Drink: 0, At: [] }, - } - }, - Synch: { - WaitFor: [] - }, - TristramLeech: { - Leader: "", - Helper: false, - Wait: 5 - }, - TombLeech: { - Leader: "", - Helper: false, - Wait: 5 - }, - TravincalLeech: { - Leader: "", - Helper: false, - Wait: 5 - }, - Tristram: { - PortalLeech: false, - WalkClear: false - }, - Travincal: { - PortalLeech: false - }, - SkillStat: { - Skills: [] - }, - Bonesaw: { - ClearDrifterCavern: false - }, - ChestMania: { - Act1: [], - Act2: [], - Act3: [], - Act4: [], - Act5: [] - }, - ClearAnyArea: { - AreaList: [] - }, - Rusher: { - WaitPlayerCount: 0, - Cain: false, - Radament: false, - LamEsen: false, - Izual: false, - Shenk: false, - Anya: false, - HellAncients: false, - GiveWps: false, - LastRun: "" - }, - Rushee: { - Quester: false, - Bumper: false - }, - Questing: { - StopProfile: false - }, - GemHunter: { - AreaList: [], - GemList: [] - }, - AutoSkill: { - Enabled: false, - Build: [], - Save: 0 - }, - AutoStat: { - Enabled: false, - Build: [], - Save: 0, - BlockChance: 0, - UseBulk: true - }, - AutoBuild: { - Enabled: false, - Template: "", - Verbose: false, - DebugMode: false - } + Runewords: [], + KeepRunewords: [], + LadderOveride: false, + Gamble: false, + GambleItems: [], + GambleGoldStart: 0, + GambleGoldStop: 0, + MiniShopBot: false, + TeleSwitch: false, + MFSwitchPercent: 0, + PrimarySlot: -1, + LogExperience: false, + TownCheck: false, + PingQuit: [{ Ping: 0, Duration: 0 }], + PacketShopping: false, + + // Fastmod + FCR: 0, + FHR: 0, + FBR: 0, + IAS: 0, + PacketCasting: 0, + WaypointMenu: true, + + // Anti-hostile + AntiHostile: false, + RandomPrecast: false, + HostileAction: 0, + TownOnHostile: false, + ViperCheck: false, + + // DClone + StopOnDClone: false, + SoJWaitTime: 0, + KillDclone: false, + DCloneQuit: false, + DCloneWaitTime: 30, + + // Experimental + FastParty: false, + AutoEquip: false, + + // GameData + ChampionBias: 60, + + UseCta: true, + ForcePrecast: false, + + // Attack specific + Dodge: false, + DodgeRange: 15, + DodgeHP: 100, + AttackSkill: [], + LowManaSkill: [], + CustomAttack: {}, + TeleStomp: false, + NoTele: false, + ClearType: false, + ClearPath: false, + BossPriority: false, + MaxAttackCount: 300, + + // Amazon specific + LightningFuryDelay: 0, + UseInnerSight: false, + UseSlowMissiles: false, + UseDecoy: false, + SummonValkyrie: false, + + // Sorceress specific + UseTelekinesis: false, + CastStatic: false, + StaticList: [], + UseEnergyShield: false, + UseColdArmor: true, + + // Necromancer specific + Golem: 0, + ActiveSummon: false, + Skeletons: 0, + SkeletonMages: 0, + Revives: 0, + ReviveUnstackable: false, + PoisonNovaDelay: 2000, + Curse: [], + CustomCurse: [], + ExplodeCorpses: 0, + + // Paladin speficic + Redemption: [0, 0], + Charge: false, + Vigor: false, + AvoidDolls: false, + + // Barbarian specific + FindItem: false, + FastFindItem: false, + FindItemSwitch: false, + UseWarcries: true, + + // Druid specific + Wereform: 0, + SummonRaven: 0, + SummonAnimal: 0, + SummonVine: 0, + SummonSpirit: 0, + + // Assassin specific + UseTraps: false, + Traps: [], + BossTraps: [], + UseFade: false, + UseBoS: false, + UseVenom: false, + UseBladeShield: false, + UseCloakofShadows: false, + AggressiveCloak: false, + SummonShadow: false, + + // Custom Attack + CustomClassAttack: "", // If set it loads core/Attack/[CustomClassAttack].js + + MapMode: { + UseOwnItemFilter: false, + }, + + // Script specific + MFLeader: false, + Mausoleum: { + KillBishibosh: false, + KillBloodRaven: false, + ClearCrypt: false + }, + Cows: { + DontMakePortal: false, + JustMakePortal: false, + KillKing: false + }, + Tombs: { + KillDuriel: false, + WalkClear: false, + }, + Eldritch: { + OpenChest: false, + KillSharptooth: false, + KillShenk: false, + KillDacFarren: false + }, + Pindleskin: { + UseWaypoint: false, + KillNihlathak: false, + ViperQuit: false + }, + Nihlathak: { + ViperQuit: false, + UseWaypoint: false, + }, + Pit: { + ClearPath: false, + ClearPit1: false + }, + Snapchip: { + ClearIcyCellar: false + }, + Frozenstein: { + ClearFrozenRiver: false + }, + Rakanishu: { + KillGriswold: false + }, + AutoBaal: { + Leader: "", + FindShrine: false, + LeechSpot: [15115, 5050], + LongRangeSupport: false + }, + KurastChests: { + LowerKurast: false, + Bazaar: false, + Sewers1: false, + Sewers2: false + }, + Countess: { + KillGhosts: false + }, + Baal: { + DollQuit: false, + SoulQuit: false, + KillBaal: false, + HotTPMessage: "Hot TP!", + SafeTPMessage: "Safe TP!", + BaalMessage: "Baal!" + }, + BaalAssistant: { + KillNihlathak: false, + FastChaos: false, + Wait: 120, + Helper: false, + GetShrine: false, + GetShrineWaitForHotTP: false, + DollQuit: false, + SoulQuit: false, + SkipTP: false, + WaitForSafeTP: false, + KillBaal: false, + HotTPMessage: [], + SafeTPMessage: [], + BaalMessage: [], + NextGameMessage: [] + }, + BaalHelper: { + Wait: 120, + KillNihlathak: false, + FastChaos: false, + DollQuit: false, + KillBaal: false, + SkipTP: false + }, + Corpsefire: { + ClearDen: false + }, + Hephasto: { + ClearRiver: false, + ClearType: false + }, + Diablo: { + WalkClear: false, + Entrance: false, + JustViz: false, + SealLeader: false, + Fast: false, + SealWarning: "Leave the seals alone!", + EntranceTP: "Entrance TP up", + StarTP: "Star TP up", + DiabloMsg: "Diablo", + ClearRadius: 30, + SealOrder: ["vizier", "seis", "infector"] + }, + DiabloHelper: { + Wait: 120, + Entrance: false, + SkipIfBaal: false, + SkipTP: false, + OpenSeals: false, + SafePrecast: true, + ClearRadius: 30, + SealOrder: ["vizier", "seis", "infector"], + RecheckSeals: false + }, + MFHelper: { + BreakClearLevel: false + }, + Wakka: { + Wait: 1, + StopAtLevel: 99, + StopProfile: false, + SkipIfBaal: true, + }, + BattleOrders: { + Mode: 0, + Getters: [], + Idle: false, + QuitOnFailure: false, + SkipIfTardy: true, + Wait: 10 + }, + BoBarbHelper: { + Mode: -1, + Wp: 35 + }, + ControlBot: { + Bo: false, + Cows: { + MakeCows: false, + GetLeg: false, + }, + Chant: { + Enchant: false, + AutoEnchant: false, + }, + Wps: { + GiveWps: false, + SecurePortal: false, + }, + EndMessage: "", + GameLength: 20 + }, + IPHunter: { + IPList: [], + GameLength: 3 + }, + Follower: { + Leader: "" + }, + Mephisto: { + MoatTrick: false, + KillCouncil: false, + TakeRedPortal: false + }, + ShopBot: { + ScanIDs: [], + ShopNPC: "anya", + CycleDelay: 0, + QuitOnMatch: false + }, + Coldworm: { + KillBeetleburst: false, + ClearMaggotLair: false + }, + Summoner: { + FireEye: false + }, + AncientTunnels: { + OpenChest: false, + KillDarkElder: false + }, + OrgTorch: { + WaitForKeys: false, + WaitTimeout: 0, + UseSalvation: false, + GetFade: false, + MakeTorch: true, + PreGame: { + Thawing: { Drink: 0, At: [] }, + Antidote: { Drink: 0, At: [] }, + } + }, + Synch: { + WaitFor: [] + }, + TristramLeech: { + Leader: "", + Helper: false, + Wait: 5 + }, + TombLeech: { + Leader: "", + Helper: false, + Wait: 5 + }, + TravincalLeech: { + Leader: "", + Helper: false, + Wait: 5 + }, + Tristram: { + PortalLeech: false, + WalkClear: false + }, + Travincal: { + PortalLeech: false + }, + SkillStat: { + Skills: [] + }, + Bonesaw: { + ClearDrifterCavern: false + }, + ChestMania: { + Act1: [], + Act2: [], + Act3: [], + Act4: [], + Act5: [] + }, + ClearAnyArea: { + AreaList: [] + }, + Rusher: { + WaitPlayerCount: 0, + Cain: false, + Radament: false, + LamEsen: false, + Izual: false, + Shenk: false, + Anya: false, + HellAncients: false, + GiveWps: false, + LastRun: "" + }, + Rushee: { + Quester: false, + Bumper: false + }, + Questing: { + StopProfile: false + }, + GemHunter: { + AreaList: [], + GemList: [] + }, + AutoSkill: { + Enabled: false, + Build: [], + Save: 0 + }, + AutoStat: { + Enabled: false, + Build: [], + Save: 0, + BlockChance: 0, + UseBulk: true + }, + AutoBuild: { + Enabled: false, + Template: "", + Verbose: false, + DebugMode: false + } }; diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 5cb98121b..6e2886468 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -6,109 +6,109 @@ */ const Roll = { - All: 0, - Eth: 1, - NonEth: 2 + All: 0, + Eth: 1, + NonEth: 2 }; /** * @todo Fix/refactor this, these numbers are all arbitrary anyway */ const Recipe = { - Gem: 0, - HitPower: { - Helm: 1, - Boots: 2, - Gloves: 3, - Belt: 4, - Shield: 5, - Body: 6, - Amulet: 7, - Ring: 8, - Weapon: 9 - }, - Blood: { - Helm: 10, - Boots: 11, - Gloves: 12, - Belt: 13, - Shield: 14, - Body: 15, - Amulet: 16, - Ring: 17, - Weapon: 18 - }, - Caster: { - Helm: 19, - Boots: 20, - Gloves: 21, - Belt: 22, - Shield: 23, - Body: 24, - Amulet: 25, - Ring: 26, - Weapon: 27 - }, - Safety: { - Helm: 28, - Boots: 29, - Gloves: 30, - Belt: 31, - Shield: 32, - Body: 33, - Amulet: 34, - Ring: 35, - Weapon: 36 - }, - Unique: { - Weapon: { - ToExceptional: 37, - ToElite: 38 - }, - Armor: { - ToExceptional: 39, - ToElite: 40 - } - }, - Rare: { - Weapon: { - ToExceptional: 41, - ToElite: 42 - }, - Armor: { - ToExceptional: 43, - ToElite: 44 - } - }, - Socket: { - Shield: 45, - Weapon: 46, - Armor: 47, - Helm: 48, - Magic: { - LowWeapon: 59, - HighWeapon: 60, - }, - Rare: 61, - }, - Reroll: { - Magic: 49, - Rare: 50, - HighRare: 51, - Charm: { - Small: 56, - Large: 57, - Grand: 58, - }, - }, - Rune: 52, - Token: 53, - LowToNorm: { - Armor: 54, - Weapon: 55 - }, - Rejuv: 62, - FullRejuv: 63, + Gem: 0, + HitPower: { + Helm: 1, + Boots: 2, + Gloves: 3, + Belt: 4, + Shield: 5, + Body: 6, + Amulet: 7, + Ring: 8, + Weapon: 9 + }, + Blood: { + Helm: 10, + Boots: 11, + Gloves: 12, + Belt: 13, + Shield: 14, + Body: 15, + Amulet: 16, + Ring: 17, + Weapon: 18 + }, + Caster: { + Helm: 19, + Boots: 20, + Gloves: 21, + Belt: 22, + Shield: 23, + Body: 24, + Amulet: 25, + Ring: 26, + Weapon: 27 + }, + Safety: { + Helm: 28, + Boots: 29, + Gloves: 30, + Belt: 31, + Shield: 32, + Body: 33, + Amulet: 34, + Ring: 35, + Weapon: 36 + }, + Unique: { + Weapon: { + ToExceptional: 37, + ToElite: 38 + }, + Armor: { + ToExceptional: 39, + ToElite: 40 + } + }, + Rare: { + Weapon: { + ToExceptional: 41, + ToElite: 42 + }, + Armor: { + ToExceptional: 43, + ToElite: 44 + } + }, + Socket: { + Shield: 45, + Weapon: 46, + Armor: 47, + Helm: 48, + Magic: { + LowWeapon: 59, + HighWeapon: 60, + }, + Rare: 61, + }, + Reroll: { + Magic: 49, + Rare: 50, + HighRare: 51, + Charm: { + Small: 56, + Large: 57, + Grand: 58, + }, + }, + Rune: 52, + Token: 53, + LowToNorm: { + Armor: 54, + Weapon: 55 + }, + Rejuv: 62, + FullRejuv: 63, }; /** @@ -117,1184 +117,1216 @@ const Recipe = { * @returns {number[]} */ Object.defineProperty(Recipe, "ingredients", { - /** - * Get list of ingredients needed for certain recipe - * @param {number} index - Index of recipe to check - * @param {number} [keyItem] - Key item in cubing recipe - * @returns {number[]} - */ - value: function (index, keyItem) { - switch (index) { - case Recipe.Gem: - return [keyItem - 1, keyItem - 1, keyItem - 1]; - // Crafting Recipes---------------------------------------------------------------------// - case Recipe.HitPower.Helm: - return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Boots: - return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Gloves: - return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Belt: - return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Shield: - return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Body: - return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Amulet: - return [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Ring: - return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.HitPower.Weapon: - return [keyItem, sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; - case Recipe.Blood.Helm: - return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Boots: - return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Gloves: - return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Belt: - return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Shield: - return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Body: - return [keyItem, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Amulet: - return [sdk.items.Amulet, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Ring: - return [sdk.items.Ring, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Blood.Weapon: - return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; - case Recipe.Caster.Helm: - return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Boots: - return [keyItem, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Gloves: - return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Belt: - return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Shield: - return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Body: - return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Amulet: - return [sdk.items.Amulet, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Ring: - return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Caster.Weapon: - return [keyItem, sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Safety.Helm: - return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Boots: - return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Gloves: - return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Belt: - return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Shield: - return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Body: - return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Amulet: - return [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Ring: - return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - case Recipe.Safety.Weapon: - return [keyItem, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; - // Upgrading Recipes-----------------------------------------------------------------------------// - case Recipe.Unique.Weapon.ToExceptional: - return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald]; - case Recipe.Unique.Weapon.ToElite: // Ladder only - return [keyItem, sdk.items.runes.Lum, sdk.items.runes.Pul, sdk.items.gems.Perfect.Emerald]; - case Recipe.Unique.Armor.ToExceptional: - return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Shael, sdk.items.gems.Perfect.Diamond]; - case Recipe.Unique.Armor.ToElite: // Ladder only - return [keyItem, sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.gems.Perfect.Diamond]; - case Recipe.Rare.Weapon.ToExceptional: - return [keyItem, sdk.items.runes.Ort, sdk.items.runes.Amn, sdk.items.gems.Perfect.Sapphire]; - case Recipe.Rare.Weapon.ToElite: - return [keyItem, sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.gems.Perfect.Sapphire]; - case Recipe.Rare.Armor.ToExceptional: - return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Rare.Armor.ToElite: - return [keyItem, sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst]; - // Socketing Recipes-------------------------------------------------------------------------------// - case Recipe.Socket.Shield: - return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby]; - case Recipe.Socket.Weapon: - return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Amn, sdk.items.gems.Perfect.Amethyst]; - case Recipe.Socket.Armor: - return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz]; - case Recipe.Socket.Helm: - return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire]; - case Recipe.Socket.Magic.LowWeapon: - return [keyItem, "cgem", "cgem", "cgem"]; - case Recipe.Socket.Magic.HighWeapon: - return [keyItem, "fgem", "fgem", "fgem"]; - case Recipe.Socket.Rare: - return [keyItem, sdk.items.Ring, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull]; - // Re-rolling Recipes-------------------------------------------------------------------------------// - case Recipe.Reroll.Charm.Small: - return [sdk.items.SmallCharm, "pgem", "pgem", "pgem"]; - case Recipe.Reroll.Charm.Large: - return [sdk.items.LargeCharm, "pgem", "pgem", "pgem"]; - case Recipe.Reroll.Charm.Grand: - return [sdk.items.GrandCharm, "pgem", "pgem", "pgem"]; - case Recipe.Reroll.Magic: // Hacky solution ftw - return [keyItem, "pgem", "pgem", "pgem"]; - case Recipe.Reroll.Rare: - return [keyItem, 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]; - case Recipe.Reroll.HighRare: - return [keyItem, sdk.items.gems.Perfect.Skull, sdk.items.Ring]; - case Recipe.LowToNorm.Weapon: - return [keyItem, sdk.items.runes.Eld, "cgem"]; - case Recipe.LowToNorm.Armor: - return [keyItem, sdk.items.runes.El, "cgem"]; - // Rune Recipes--------------------------------------------------------------------------------------// - case Recipe.Rune: - switch (keyItem) { - 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: - return [keyItem - 1, keyItem - 1, keyItem - 1]; - case sdk.items.runes.Amn: // thul->amn - return [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz]; - case sdk.items.runes.Sol: // amn->sol - return [sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.gems.Chipped.Amethyst]; - case sdk.items.runes.Shael: // sol->shael - return [sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.gems.Chipped.Sapphire]; - case sdk.items.runes.Dol: // shael->dol - return [sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.gems.Chipped.Ruby]; - case sdk.items.runes.Hel: // dol->hel - return [sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.gems.Chipped.Emerald]; - case sdk.items.runes.Io: // hel->io - return [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.gems.Chipped.Diamond]; - case sdk.items.runes.Lum: // io->lum - return [sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.gems.Flawed.Topaz]; - case sdk.items.runes.Ko: // lum->ko - return [sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.gems.Flawed.Amethyst]; - case sdk.items.runes.Fal: // ko->fal - return [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.gems.Flawed.Sapphire]; - case sdk.items.runes.Lem: // fal->lem - return [sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.gems.Flawed.Ruby]; - case sdk.items.runes.Pul: // lem->pul - return [sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.gems.Flawed.Emerald]; - case sdk.items.runes.Um: // pul->um - return [sdk.items.runes.Pul, sdk.items.runes.Pul, sdk.items.gems.Flawed.Diamond]; - case sdk.items.runes.Mal: // um->mal - return [sdk.items.runes.Um, sdk.items.runes.Um, sdk.items.gems.Normal.Topaz]; - case sdk.items.runes.Ist: // mal->ist - return [sdk.items.runes.Mal, sdk.items.runes.Mal, sdk.items.gems.Normal.Amethyst]; - case sdk.items.runes.Gul: // ist->gul - return [sdk.items.runes.Ist, sdk.items.runes.Ist, sdk.items.gems.Normal.Sapphire]; - case sdk.items.runes.Vex: // gul->vex - return [sdk.items.runes.Gul, sdk.items.runes.Gul, sdk.items.gems.Normal.Ruby]; - case sdk.items.runes.Ohm: // vex->ohm - return [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.gems.Normal.Emerald]; - case sdk.items.runes.Lo: // ohm->lo - return [sdk.items.runes.Ohm, sdk.items.runes.Ohm, sdk.items.gems.Normal.Diamond]; - case sdk.items.runes.Sur: // lo->sur - return [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz]; - case sdk.items.runes.Ber: // sur->ber - return [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst]; - case sdk.items.runes.Jah: // ber->jah - return [sdk.items.runes.Ber, sdk.items.runes.Ber, sdk.items.gems.Flawless.Sapphire]; - case sdk.items.runes.Cham: // jah->cham - return [sdk.items.runes.Jah, sdk.items.runes.Jah, sdk.items.gems.Flawless.Ruby]; - case sdk.items.runes.Zod: // cham->zod - return [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald]; - } - - break; - case Recipe.Token: - return [sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction]; - case Recipe.Rejuv: - return ["cgem", "hpot", "hpot", "hpot", "mpot", "mpot", "mpot"]; - case Recipe.FullRejuv: - return ["gem", "hpot", "hpot", "hpot", "mpot", "mpot", "mpot"]; - } - return []; - }, - enumerable: false, + /** + * Get list of ingredients needed for certain recipe + * @param {number} index - Index of recipe to check + * @param {number} [keyItem] - Key item in cubing recipe + * @returns {number[]} + */ + value: function (index, keyItem) { + switch (index) { + case Recipe.Gem: + return [keyItem - 1, keyItem - 1, keyItem - 1]; + // Crafting Recipes---------------------------------------------------------------------// + case Recipe.HitPower.Helm: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Boots: + return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Gloves: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Belt: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Shield: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Body: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Ring: + return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.HitPower.Weapon: + return [keyItem, sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Blood.Helm: + return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Boots: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Gloves: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Belt: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Shield: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Body: + return [keyItem, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Ring: + return [sdk.items.Ring, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Blood.Weapon: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Ruby]; + case Recipe.Caster.Helm: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Boots: + return [keyItem, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Gloves: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Belt: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Shield: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Body: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Ring: + return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Caster.Weapon: + return [keyItem, sdk.items.runes.Tir, sdk.items.Jewel, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Safety.Helm: + return [keyItem, sdk.items.runes.Ith, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Boots: + return [keyItem, sdk.items.runes.Ort, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Gloves: + return [keyItem, sdk.items.runes.Ral, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Belt: + return [keyItem, sdk.items.runes.Tal, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Shield: + return [keyItem, sdk.items.runes.Nef, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Body: + return [keyItem, sdk.items.runes.Eth, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Amulet: + return [sdk.items.Amulet, sdk.items.runes.Thul, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Ring: + return [sdk.items.Ring, sdk.items.runes.Amn, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + case Recipe.Safety.Weapon: + return [keyItem, sdk.items.runes.Sol, sdk.items.Jewel, sdk.items.gems.Perfect.Emerald]; + // Upgrading Recipes-----------------------------------------------------------------------------// + case Recipe.Unique.Weapon.ToExceptional: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Sol, sdk.items.gems.Perfect.Emerald]; + case Recipe.Unique.Weapon.ToElite: // Ladder only + return [keyItem, sdk.items.runes.Lum, sdk.items.runes.Pul, sdk.items.gems.Perfect.Emerald]; + case Recipe.Unique.Armor.ToExceptional: + return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Shael, sdk.items.gems.Perfect.Diamond]; + case Recipe.Unique.Armor.ToElite: // Ladder only + return [keyItem, sdk.items.runes.Lem, sdk.items.runes.Ko, sdk.items.gems.Perfect.Diamond]; + case Recipe.Rare.Weapon.ToExceptional: + return [keyItem, sdk.items.runes.Ort, sdk.items.runes.Amn, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Rare.Weapon.ToElite: + return [keyItem, sdk.items.runes.Fal, sdk.items.runes.Um, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Rare.Armor.ToExceptional: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Rare.Armor.ToElite: + return [keyItem, sdk.items.runes.Ko, sdk.items.runes.Pul, sdk.items.gems.Perfect.Amethyst]; + // Socketing Recipes-------------------------------------------------------------------------------// + case Recipe.Socket.Shield: + return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Amn, sdk.items.gems.Perfect.Ruby]; + case Recipe.Socket.Weapon: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Amn, sdk.items.gems.Perfect.Amethyst]; + case Recipe.Socket.Armor: + return [keyItem, sdk.items.runes.Tal, sdk.items.runes.Thul, sdk.items.gems.Perfect.Topaz]; + case Recipe.Socket.Helm: + return [keyItem, sdk.items.runes.Ral, sdk.items.runes.Thul, sdk.items.gems.Perfect.Sapphire]; + case Recipe.Socket.Magic.LowWeapon: + return [keyItem, "cgem", "cgem", "cgem"]; + case Recipe.Socket.Magic.HighWeapon: + return [keyItem, "fgem", "fgem", "fgem"]; + case Recipe.Socket.Rare: + return [ + keyItem, sdk.items.Ring, sdk.items.gems.Perfect.Skull, + sdk.items.gems.Perfect.Skull, sdk.items.gems.Perfect.Skull + ]; + // Re-rolling Recipes-------------------------------------------------------------------------------// + case Recipe.Reroll.Charm.Small: + return [sdk.items.SmallCharm, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Charm.Large: + return [sdk.items.LargeCharm, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Charm.Grand: + return [sdk.items.GrandCharm, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Magic: // Hacky solution ftw + return [keyItem, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Rare: + return [ + keyItem, 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 + ]; + case Recipe.Reroll.HighRare: + return [keyItem, sdk.items.gems.Perfect.Skull, sdk.items.Ring]; + case Recipe.LowToNorm.Weapon: + return [keyItem, sdk.items.runes.Eld, "cgem"]; + case Recipe.LowToNorm.Armor: + return [keyItem, sdk.items.runes.El, "cgem"]; + // Rune Recipes--------------------------------------------------------------------------------------// + case Recipe.Rune: + switch (keyItem) { + 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: + return [keyItem - 1, keyItem - 1, keyItem - 1]; + case sdk.items.runes.Amn: // thul->amn + return [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz]; + case sdk.items.runes.Sol: // amn->sol + return [sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.runes.Amn, sdk.items.gems.Chipped.Amethyst]; + case sdk.items.runes.Shael: // sol->shael + return [sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.runes.Sol, sdk.items.gems.Chipped.Sapphire]; + case sdk.items.runes.Dol: // shael->dol + return [sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.runes.Shael, sdk.items.gems.Chipped.Ruby]; + case sdk.items.runes.Hel: // dol->hel + return [sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.runes.Dol, sdk.items.gems.Chipped.Emerald]; + case sdk.items.runes.Io: // hel->io + return [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.gems.Chipped.Diamond]; + case sdk.items.runes.Lum: // io->lum + return [sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.runes.Io, sdk.items.gems.Flawed.Topaz]; + case sdk.items.runes.Ko: // lum->ko + return [sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.runes.Lum, sdk.items.gems.Flawed.Amethyst]; + case sdk.items.runes.Fal: // ko->fal + return [sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.runes.Ko, sdk.items.gems.Flawed.Sapphire]; + case sdk.items.runes.Lem: // fal->lem + return [sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.runes.Fal, sdk.items.gems.Flawed.Ruby]; + case sdk.items.runes.Pul: // lem->pul + return [sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.runes.Lem, sdk.items.gems.Flawed.Emerald]; + case sdk.items.runes.Um: // pul->um + return [sdk.items.runes.Pul, sdk.items.runes.Pul, sdk.items.gems.Flawed.Diamond]; + case sdk.items.runes.Mal: // um->mal + return [sdk.items.runes.Um, sdk.items.runes.Um, sdk.items.gems.Normal.Topaz]; + case sdk.items.runes.Ist: // mal->ist + return [sdk.items.runes.Mal, sdk.items.runes.Mal, sdk.items.gems.Normal.Amethyst]; + case sdk.items.runes.Gul: // ist->gul + return [sdk.items.runes.Ist, sdk.items.runes.Ist, sdk.items.gems.Normal.Sapphire]; + case sdk.items.runes.Vex: // gul->vex + return [sdk.items.runes.Gul, sdk.items.runes.Gul, sdk.items.gems.Normal.Ruby]; + case sdk.items.runes.Ohm: // vex->ohm + return [sdk.items.runes.Vex, sdk.items.runes.Vex, sdk.items.gems.Normal.Emerald]; + case sdk.items.runes.Lo: // ohm->lo + return [sdk.items.runes.Ohm, sdk.items.runes.Ohm, sdk.items.gems.Normal.Diamond]; + case sdk.items.runes.Sur: // lo->sur + return [sdk.items.runes.Lo, sdk.items.runes.Lo, sdk.items.gems.Flawless.Topaz]; + case sdk.items.runes.Ber: // sur->ber + return [sdk.items.runes.Sur, sdk.items.runes.Sur, sdk.items.gems.Flawless.Amethyst]; + case sdk.items.runes.Jah: // ber->jah + return [sdk.items.runes.Ber, sdk.items.runes.Ber, sdk.items.gems.Flawless.Sapphire]; + case sdk.items.runes.Cham: // jah->cham + return [sdk.items.runes.Jah, sdk.items.runes.Jah, sdk.items.gems.Flawless.Ruby]; + case sdk.items.runes.Zod: // cham->zod + return [sdk.items.runes.Cham, sdk.items.runes.Cham, sdk.items.gems.Flawless.Emerald]; + } + + break; + case Recipe.Token: + return [ + sdk.quest.item.TwistedEssenceofSuffering, sdk.quest.item.ChargedEssenceofHatred, + sdk.quest.item.BurningEssenceofTerror, sdk.quest.item.FesteringEssenceofDestruction + ]; + case Recipe.Rejuv: + return ["cgem", "hpot", "hpot", "hpot", "mpot", "mpot", "mpot"]; + case Recipe.FullRejuv: + return ["gem", "hpot", "hpot", "hpot", "mpot", "mpot", "mpot"]; + } + return []; + }, + enumerable: false, }); const Cubing = { - /** @type {recipeObj[]} */ - recipes: [], - gemList: [], - gems: (() => ({ - chipped: Object.values(sdk.items.gems.Chipped), - flawed: Object.values(sdk.items.gems.Flawed), - normal: Object.values(sdk.items.gems.Normal), - flawless: Object.values(sdk.items.gems.Flawless), - perfect: Object.values(sdk.items.gems.Perfect), - }))(), - pots: { - healing: [sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, sdk.items.HealingPotion, sdk.items.GreaterHealingPotion], - mana: [sdk.items.MinorManaPotion, sdk.items.LightManaPotion, sdk.items.ManaPotion, sdk.items.GreaterManaPotion], - }, - - init: function () { - if (!Config.Cubing) return; - - // print("We have " + Config.Recipes.length + " cubing recipe(s)."); - - for (let i = 0; i < Config.Recipes.length; i += 1) { - if (Config.Recipes[i].length > 1 && isNaN(Config.Recipes[i][1])) { - if (NTIPAliasClassID.hasOwnProperty(Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase())) { - Config.Recipes[i][1] = NTIPAliasClassID[Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase()]; - } else { - Misc.errorReport("ÿc1Invalid cubing entry:ÿc0 " + Config.Recipes[i][1]); - Config.Recipes.splice(i, 1); - - i -= 1; - } - } - } - - this.buildRecipes(); - this.buildGemList(); - this.buildLists(); - }, - - buildGemList: function () { - let gemList = Cubing.gems.perfect.slice(); - - for (let i = 0; i < this.recipes.length; i += 1) { - // Skip gems and other magic rerolling recipes - if ([Recipe.Gem, Recipe.Reroll.Magic].indexOf(this.recipes[i].Index) === -1) { - for (let j = 0; j < this.recipes[i].Ingredients.length; j += 1) { - if (gemList.includes(this.recipes[i].Ingredients[j])) { - gemList.splice(gemList.indexOf(this.recipes[i].Ingredients[j]), 1); - } - } - } - } - - Cubing.gemList = gemList.slice(0); - - return true; - }, - - /** - * @typedef recipeObj - * @property {number[] | string[]} Ingredients - * @property {number} Index - * @property {number} [Level] - * @property {number} [Ethereal] - * @property {boolean} [Enabled] - * @property {boolean} [AlwaysEnabled] - * - * - * @todo - * - Allow passing in ilvl - */ - buildRecipes: function () { - Cubing.recipes = []; - - for (let i = 0; i < Config.Recipes.length; i += 1) { - if (typeof Config.Recipes[i] !== "object" || (Config.Recipes[i].length > 2 && typeof Config.Recipes[i][2] !== "number") || Config.Recipes[i].length < 1) { - throw new Error("Cubing.buildRecipes: Invalid recipe format."); - } - - /** @type {number[]} */ - let [index, keyItem] = Config.Recipes[i]; - const ingredients = Recipe.ingredients(index, keyItem); - - switch (index) { - case Recipe.Gem: - this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); - - break; - // Crafting Recipes--------------------------------------------------------------// - case Recipe.HitPower.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.HitPower.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.HitPower.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.HitPower.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.HitPower.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.HitPower.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.HitPower.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.HitPower.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - - break; - case Recipe.HitPower.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Blood.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.Blood.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Blood.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.Blood.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Blood.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.Blood.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Blood.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.Blood.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - - break; - case Recipe.Blood.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Caster.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.Caster.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Caster.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.Caster.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Caster.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.Caster.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Caster.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.Caster.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - - break; - case Recipe.Caster.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Safety.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.Safety.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Safety.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.Safety.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Safety.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.Safety.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Safety.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.Safety.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - - break; - case Recipe.Safety.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - // Upgrading Recipes------------------------------------------------------------------------// - case Recipe.Unique.Weapon.ToExceptional: - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Unique.Weapon.ToElite: // Ladder only - if (me.ladder) { - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - } - - break; - case Recipe.Unique.Armor.ToExceptional: - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Unique.Armor.ToElite: // Ladder only - if (me.ladder) { - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - } - - break; - case Recipe.Rare.Weapon.ToExceptional: - case Recipe.Rare.Weapon.ToElite: - case Recipe.Rare.Armor.ToExceptional: - case Recipe.Rare.Armor.ToElite: - case Recipe.Socket.Shield: - case Recipe.Socket.Weapon: - case Recipe.Socket.Armor: - case Recipe.Socket.Helm: - case Recipe.Socket.Rare: - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.Magic.LowWeapon: - // ilvl < 30 - this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.Magic.HighWeapon: - // ilvl >= 30 - this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Reroll.Charm.Small: - case Recipe.Reroll.Charm.Large: - case Recipe.Reroll.Charm.Grand: - case Recipe.Reroll.Magic: // Hacky solution ftw - /** - * Charm ilvls based on https://diablo2.diablowiki.net/Guide:Charms_v1.10,_by_Kronos - */ - if (index === Recipe.Reroll.Charm.Small) { - this.recipes.push({ Ingredients: ingredients, Level: 94, Index: index }); - } else if (index === Recipe.Reroll.Charm.Large) { - this.recipes.push({ Ingredients: ingredients, Level: 76, Index: index }); - } else if (index === Recipe.Reroll.Charm.Grand) { - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - } else { - this.recipes.push({ Ingredients: ingredients, Level: 91, Index: index }); - } - - break; - case Recipe.Reroll.Rare: - this.recipes.push({ Ingredients: ingredients, Index: index }); - - break; - case Recipe.Reroll.HighRare: - this.recipes.push({ Ingredients: ingredients, Index: index, Enabled: false }); - - break; - case Recipe.LowToNorm.Weapon: - case Recipe.LowToNorm.Armor: - this.recipes.push({ Ingredients: ingredients, Index: index }); - - break; - case Recipe.Rune: - switch (Config.Recipes[i][1]) { - 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: ingredients, Index: index, AlwaysEnabled: true }); - - break; - case sdk.items.runes.Thul: - case sdk.items.runes.Amn: - case sdk.items.runes.Sol: - case sdk.items.runes.Shael: - case sdk.items.runes.Dol: - this.recipes.push({ Ingredients: ingredients, Index: index }); - - break; - case sdk.items.runes.Hel: - case sdk.items.runes.Io: - case sdk.items.runes.Lum: - case sdk.items.runes.Ko: - case sdk.items.runes.Fal: - case sdk.items.runes.Lem: - case sdk.items.runes.Pul: - case sdk.items.runes.Um: - case sdk.items.runes.Mal: - case sdk.items.runes.Ist: - case sdk.items.runes.Gul: - case sdk.items.runes.Vex: - case sdk.items.runes.Ohm: - case sdk.items.runes.Lo: - case sdk.items.runes.Sur: - case sdk.items.runes.Ber: - case sdk.items.runes.Jah: - case sdk.items.runes.Cham: - case sdk.items.runes.Zod: - if (me.ladder) { - this.recipes.push({ Ingredients: ingredients, Index: index }); - } - - break; - } - - break; - case Recipe.Token: - this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); - - break; - case Recipe.Rejuv: - case Recipe.FullRejuv: - this.recipes.push({ Ingredients: ingredients, Index: index }); - - break; - } - } - }, - - validIngredients: [], // What we have - neededIngredients: [], // What we need - subRecipes: [], - - buildLists: function () { - CraftingSystem.checkSubrecipes(); - - Cubing.validIngredients = []; - Cubing.neededIngredients = []; - let items = me.findItems(-1, sdk.items.mode.inStorage); - - 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" && this.gems.flawless.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "gem" && this.gems.normal.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "cgem" && this.gems.chipped.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "hpot" && this.pots.healing.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "mpot" && this.pots.mana.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({ classid: items[k].classid, gid: items[k].gid }); - - // 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 - if (this.recipes[i].Index !== Recipe.Rune || ([Recipe.Rune, Recipe.Rejuv, Recipe.FullRejuv].includes(this.recipes[i].Index) && j >= 1)) { - // Enable rune recipe after 2 bases are found - 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 gem recipes (if needed) - if (!this.recipes[i].hasOwnProperty("MainRecipe")) { - // make sure we don't add a subrecipe to a subrecipe - for (let gType of Object.values(Cubing.gems)) { - // skip over cgems - can't cube them - if (gType.includes(sdk.items.gems.Chipped.Amethyst)) continue; - for (let gem of gType) { - if (this.subRecipes.indexOf(gem) === -1 && (this.recipes[i].Ingredients[j] === gem || (this.recipes[i].Ingredients[j] === "pgem" && Cubing.gemList.includes(gem)))) { - this.recipes.push({ Ingredients: [gem - 1, gem - 1, gem - 1], Index: Recipe.Gem, AlwaysEnabled: true, MainRecipe: this.recipes[i].Index }); - this.subRecipes.push(gem); - } - } - } - } - } - } - }, - - // Remove unneeded flawless gem recipes - clearSubRecipes: function () { - Cubing.subRecipes = []; - - for (let i = 0; i < this.recipes.length; i += 1) { - if (this.recipes[i].hasOwnProperty("MainRecipe")) { - this.recipes.splice(i, 1); - - i -= 1; - } - } - }, - - update: function () { - this.clearSubRecipes(); - this.buildLists(); - }, - - /** - * @param {recipeObj} recipe - * @returns {boolean} - */ - checkRecipe: function (recipe) { - let usedGids = []; - let matchList = []; - - for (let i = 0; i < recipe.Ingredients.length; i += 1) { - for (let j = 0; j < this.validIngredients.length; j += 1) { - if (usedGids.indexOf(this.validIngredients[j].gid) === -1 && ( - this.validIngredients[j].classid === recipe.Ingredients[i] - || (recipe.Ingredients[i] === "pgem" && this.gemList.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "fgem" && this.gems.flawless.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "gem" && this.gems.normal.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "cgem" && this.gems.chipped.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "hpot" && this.pots.healing.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "mpot" && this.pots.mana.includes(this.validIngredients[j].classid)) - )) { - let item = me.getItem(this.validIngredients[j].classid, -1, this.validIngredients[j].gid); - - // 26.11.2012. check if the item actually belongs to the given recipe - if (item && this.validItem(item, recipe)) { - // don't repeat the same item - usedGids.push(this.validIngredients[j].gid); - // push the item into the match list - matchList.push(copyUnit(item)); - - break; - } - } - } - - // no new items in the match list = not enough ingredients - if (matchList.length !== i + 1) return false; - } - - // return the match list. these items go to cube - return matchList; - }, - - /** - * debug function - get what each recipe needs - * @param {number} index - * @returns {string} - */ - getRecipeNeeds: function (index) { - let rval = " ["; - - for (let i = 0; i < this.neededIngredients.length; i += 1) { - if (this.neededIngredients[i].recipe.Index === index) { - rval += this.neededIngredients[i].classid + (i === this.neededIngredients.length - 1 ? "" : " "); - } - } - - rval += "]"; - - return rval; - }, - - /** - * Check an item on ground for pickup - * @param {ItemUnit} unit - * @returns {boolean} - */ - checkItem: function (unit) { - if (!Config.Cubing) return false; - if (this.keepItem(unit)) return true; - - for (let i = 0; i < this.neededIngredients.length; i += 1) { - if (unit.classid === this.neededIngredients[i].classid && this.validItem(unit, this.neededIngredients[i].recipe)) { - //debugLog("Cubing: " + unit.name + " " + this.neededIngredients[i].recipe.Index + " " + (this.neededIngredients[i].recipe.hasOwnProperty("MainRecipe") ? this.neededIngredients[i].recipe.MainRecipe : "") + this.getRecipeNeeds(this.neededIngredients[i].recipe.Index)); - return true; - } - } - - return false; - }, - - /** - * Don't drop an item from inventory if it's a part of cubing recipe - * @param {ItemUnit} unit - * @returns {boolean} - */ - keepItem: function (unit) { - if (!Config.Cubing) return false; - - for (let i = 0; i < this.validIngredients.length; i += 1) { - if (unit.mode === sdk.items.mode.inStorage && unit.gid === this.validIngredients[i].gid) { - return true; - } - } - - return false; - }, - - /** - * Check if this item is valid for a given recipe - * @param {ItemUnit} unit - * @param {recipeObj} recipe - * @returns {boolean} - */ - validItem: function (unit, recipe) { - // Excluded items - // Don't use items in locked inventory space - or wanted by other systems - if ((unit.isInInventory && Storage.Inventory.IsLocked(unit, Config.Inventory) - || Runewords.validGids.includes(unit.gid) || CraftingSystem.validGids.includes(unit.gid))) { - return false; - } - - // Pots and Gems - for Rejuv recipes - if ([Recipe.Rejuv, Recipe.FullRejuv].includes(recipe.Index)) { - /** - * @todo do this better, hacky fix for now - */ - if (!recipe.Enabled) { - if (recipe.Index === Recipe.Rejuv && this.gems.chipped.includes(unit.classid)) return true; - if (recipe.Index === Recipe.FullRejuv && this.gems.normal.includes(unit.classid)) return true; - return false; - } - - if (recipe.Index === Recipe.Rejuv && this.gems.chipped.includes(unit.classid)) return true; - if (recipe.Index === Recipe.FullRejuv && this.gems.normal.includes(unit.classid)) return true; - if ([].concat(Cubing.pots.healing, Cubing.pots.mana).includes(unit.classid)) { - return true; - } - - 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 - const ntipResult = NTIP.CheckItem(unit); - - if (recipe.Index >= Recipe.HitPower.Helm && recipe.Index <= Recipe.Safety.Weapon) { - // 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 && ntipResult === Pickit.Result.UNWANTED) { - return true; - } - - return false; - } - - let upgradeUnique = recipe.Index >= Recipe.Unique.Weapon.ToExceptional && recipe.Index <= Recipe.Unique.Armor.ToElite; - let upgradeRare = recipe.Index >= Recipe.Rare.Weapon.ToExceptional && recipe.Index <= Recipe.Rare.Armor.ToElite; - let socketNormal = recipe.Index >= Recipe.Socket.Shield && recipe.Index <= Recipe.Socket.Helm; - let socketMagic = [Recipe.Socket.Magic.LowWeapon, Recipe.Socket.Magic.HighWeapon].includes(recipe.Index); - let socketRare = recipe.Index === Recipe.Socket.Rare; - - if (socketRare && 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; - } - - if (upgradeUnique || upgradeRare || socketNormal || socketRare) { - switch (true) { - case upgradeUnique && unit.unique && ntipResult === Pickit.Result.WANTED: // Unique item matching pickit entry - case upgradeRare && unit.rare && ntipResult === Pickit.Result.WANTED: // Rare item matching pickit entry - case socketNormal && unit.normal && unit.sockets === 0: // Normal item matching pickit entry, no sockets - case socketMagic && unit.magic && unit.sockets === 0: // Magic item matching pickit entry, no sockets - case socketRare && unit.rare && unit.sockets === 0: // Rare item matching pickit entry, no sockets - if (socketMagic) { - if (recipe.Index === Recipe.Socket.Magic.LowWeapon && unit.ilvl > recipe.Level) return false; - if (recipe.Index === Recipe.Socket.Magic.HighWeapon && unit.ilvl < recipe.Level) return false; - } - if (recipe.Ethereal === undefined) return ntipResult === Pickit.Result.WANTED; - switch (recipe.Ethereal) { - case Roll.All: - 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; - } - - return false; - } - - if (recipe.Index === Recipe.Reroll.Magic || (recipe.Index >= Recipe.Reroll.Charm.Small && recipe.Index <= Recipe.Reroll.Charm.Grand)) { - return (unit.magic && unit.ilvl >= recipe.Level && ntipResult === Pickit.Result.UNWANTED); - } - - if (recipe.Index === Recipe.Reroll.Rare) { - return (unit.rare && ntipResult === Pickit.Result.UNWANTED); - } - - 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; - } - - if (recipe.Index === Recipe.LowToNorm.Armor || recipe.Index === Recipe.LowToNorm.Weapon) { - return (unit.lowquality && ntipResult === Pickit.Result.UNWANTED); - } - - return false; - }, - - doCubing: function () { - if (!Config.Cubing) return false; - if (!me.getItem(sdk.quest.item.Cube)) return false; - - 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 += 1) { - let string = "Transmuting: "; - let items = this.checkRecipe(tempArray[i]); - - if (Array.isArray(items) && items.length) { - // 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(); - - i = -1; - - let itemsToCubeCount = items.length; - - while (items.length) { - string += (items[0].name.trim() + (items.length > 1 ? " + " : "")); - Storage.Cube.MoveTo(items[0]); - items.shift(); - } - - let itemsInCube = me.getItemsEx().filter(el => el.isInCube); - if (itemsInCube.length !== itemsToCubeCount) { - console.warn("Failed to move all necesary items to cube"); - itemsInCube.forEach(item => { - if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) return; - if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return; - }); - return false; - } - - if (!this.openCube()) return false; - - transmute(); - delay(700 + me.ping); - print("ÿc4Cubing: " + string); - Config.ShowCubingInfo && D2Bot.printToConsole(string, sdk.colors.D2Bot.Green); - this.update(); - - let cubeItems = me.findItems(-1, -1, sdk.storage.Cube); - - if (items) { - for (let j = 0; j < cubeItems.length; j += 1) { - let cubeItem = cubeItems[j]; - let result = Pickit.checkItem(cubeItem); - - /** - * @todo - * - build better method of updating cubelist so if a item we cube is wanted by cubing we - * can update our list without clearing and rebuilding the whole thing - */ - - switch (result.result) { - case Pickit.Result.UNWANTED: - Item.logger("Dropped", cubeItem, "doCubing"); - cubeItem.drop(); + /** @type {recipeObj[]} */ + recipes: [], + gemList: [], + gems: (() => ({ + chipped: Object.values(sdk.items.gems.Chipped), + flawed: Object.values(sdk.items.gems.Flawed), + normal: Object.values(sdk.items.gems.Normal), + flawless: Object.values(sdk.items.gems.Flawless), + perfect: Object.values(sdk.items.gems.Perfect), + }))(), + pots: { + healing: [ + sdk.items.MinorHealingPotion, sdk.items.LightHealingPotion, + sdk.items.HealingPotion, sdk.items.GreaterHealingPotion + ], + mana: [ + sdk.items.MinorManaPotion, sdk.items.LightManaPotion, + sdk.items.ManaPotion, sdk.items.GreaterManaPotion + ], + }, + + init: function () { + if (!Config.Cubing) return; + + // print("We have " + Config.Recipes.length + " cubing recipe(s)."); + + for (let i = 0; i < Config.Recipes.length; i += 1) { + if (Config.Recipes[i].length > 1 && isNaN(Config.Recipes[i][1])) { + if (NTIPAliasClassID.hasOwnProperty(Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase())) { + Config.Recipes[i][1] = NTIPAliasClassID[Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase()]; + } else { + Misc.errorReport("ÿc1Invalid cubing entry:ÿc0 " + Config.Recipes[i][1]); + Config.Recipes.splice(i, 1); + + i -= 1; + } + } + } + + this.buildRecipes(); + this.buildGemList(); + this.buildLists(); + }, + + buildGemList: function () { + let gemList = Cubing.gems.perfect.slice(); + + for (let i = 0; i < this.recipes.length; i += 1) { + // Skip gems and other magic rerolling recipes + if ([Recipe.Gem, Recipe.Reroll.Magic].indexOf(this.recipes[i].Index) === -1) { + for (let j = 0; j < this.recipes[i].Ingredients.length; j += 1) { + if (gemList.includes(this.recipes[i].Ingredients[j])) { + gemList.splice(gemList.indexOf(this.recipes[i].Ingredients[j]), 1); + } + } + } + } + + Cubing.gemList = gemList.slice(0); + + return true; + }, + + /** + * @typedef recipeObj + * @property {number[] | string[]} Ingredients + * @property {number} Index + * @property {number} [Level] + * @property {number} [Ethereal] + * @property {boolean} [Enabled] + * @property {boolean} [AlwaysEnabled] + * + * + * @todo + * - Allow passing in ilvl + */ + buildRecipes: function () { + Cubing.recipes = []; + + for (let i = 0; i < Config.Recipes.length; i += 1) { + if (typeof Config.Recipes[i] !== "object" + || (Config.Recipes[i].length > 2 && typeof Config.Recipes[i][2] !== "number") + || Config.Recipes[i].length < 1) { + throw new Error("Cubing.buildRecipes: Invalid recipe format."); + } + + /** @type {number[]} */ + let [index, keyItem] = Config.Recipes[i]; + const ingredients = Recipe.ingredients(index, keyItem); + + switch (index) { + case Recipe.Gem: + this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); + + break; + // Crafting Recipes--------------------------------------------------------------// + case Recipe.HitPower.Helm: + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); + + break; + case Recipe.HitPower.Boots: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.HitPower.Gloves: + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); + + break; + case Recipe.HitPower.Belt: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.HitPower.Shield: + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); + + break; + case Recipe.HitPower.Body: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + case Recipe.HitPower.Amulet: + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); + + break; + case Recipe.HitPower.Ring: + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); + + break; + case Recipe.HitPower.Weapon: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + case Recipe.Blood.Helm: + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); + + break; + case Recipe.Blood.Boots: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.Blood.Gloves: + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); + + break; + case Recipe.Blood.Belt: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.Blood.Shield: + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); + + break; + case Recipe.Blood.Body: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + case Recipe.Blood.Amulet: + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); + + break; + case Recipe.Blood.Ring: + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); + + break; + case Recipe.Blood.Weapon: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + case Recipe.Caster.Helm: + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); + + break; + case Recipe.Caster.Boots: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.Caster.Gloves: + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); + + break; + case Recipe.Caster.Belt: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.Caster.Shield: + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); + + break; + case Recipe.Caster.Body: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + case Recipe.Caster.Amulet: + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); + + break; + case Recipe.Caster.Ring: + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); + + break; + case Recipe.Caster.Weapon: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + case Recipe.Safety.Helm: + this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); + + break; + case Recipe.Safety.Boots: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.Safety.Gloves: + this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); + + break; + case Recipe.Safety.Belt: + this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); + + break; + case Recipe.Safety.Shield: + this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); + + break; + case Recipe.Safety.Body: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + case Recipe.Safety.Amulet: + this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); + + break; + case Recipe.Safety.Ring: + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); + + break; + case Recipe.Safety.Weapon: + this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + + break; + // Upgrading Recipes------------------------------------------------------------------------// + case Recipe.Unique.Weapon.ToExceptional: + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Unique.Weapon.ToElite: // Ladder only + if (me.ladder) { + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); + } + + break; + case Recipe.Unique.Armor.ToExceptional: + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Unique.Armor.ToElite: // Ladder only + if (me.ladder) { + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); + } + + break; + case Recipe.Rare.Weapon.ToExceptional: + case Recipe.Rare.Weapon.ToElite: + case Recipe.Rare.Armor.ToExceptional: + case Recipe.Rare.Armor.ToElite: + case Recipe.Socket.Shield: + case Recipe.Socket.Weapon: + case Recipe.Socket.Armor: + case Recipe.Socket.Helm: + case Recipe.Socket.Rare: + this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Socket.Magic.LowWeapon: + // ilvl < 30 + this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Socket.Magic.HighWeapon: + // ilvl >= 30 + this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); + + break; + case Recipe.Reroll.Charm.Small: + case Recipe.Reroll.Charm.Large: + case Recipe.Reroll.Charm.Grand: + case Recipe.Reroll.Magic: // Hacky solution ftw + /** + * Charm ilvls based on https://diablo2.diablowiki.net/Guide:Charms_v1.10,_by_Kronos + */ + if (index === Recipe.Reroll.Charm.Small) { + this.recipes.push({ Ingredients: ingredients, Level: 94, Index: index }); + } else if (index === Recipe.Reroll.Charm.Large) { + this.recipes.push({ Ingredients: ingredients, Level: 76, Index: index }); + } else if (index === Recipe.Reroll.Charm.Grand) { + this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); + } else { + this.recipes.push({ Ingredients: ingredients, Level: 91, Index: index }); + } + + break; + case Recipe.Reroll.Rare: + this.recipes.push({ Ingredients: ingredients, Index: index }); + + break; + case Recipe.Reroll.HighRare: + this.recipes.push({ Ingredients: ingredients, Index: index, Enabled: false }); + + break; + case Recipe.LowToNorm.Weapon: + case Recipe.LowToNorm.Armor: + this.recipes.push({ Ingredients: ingredients, Index: index }); + + break; + case Recipe.Rune: + switch (Config.Recipes[i][1]) { + 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: ingredients, Index: index, AlwaysEnabled: true }); + + break; + case sdk.items.runes.Thul: + case sdk.items.runes.Amn: + case sdk.items.runes.Sol: + case sdk.items.runes.Shael: + case sdk.items.runes.Dol: + this.recipes.push({ Ingredients: ingredients, Index: index }); + + break; + case sdk.items.runes.Hel: + case sdk.items.runes.Io: + case sdk.items.runes.Lum: + case sdk.items.runes.Ko: + case sdk.items.runes.Fal: + case sdk.items.runes.Lem: + case sdk.items.runes.Pul: + case sdk.items.runes.Um: + case sdk.items.runes.Mal: + case sdk.items.runes.Ist: + case sdk.items.runes.Gul: + case sdk.items.runes.Vex: + case sdk.items.runes.Ohm: + case sdk.items.runes.Lo: + case sdk.items.runes.Sur: + case sdk.items.runes.Ber: + case sdk.items.runes.Jah: + case sdk.items.runes.Cham: + case sdk.items.runes.Zod: + if (me.ladder) { + this.recipes.push({ Ingredients: ingredients, Index: index }); + } + + break; + } + + break; + case Recipe.Token: + this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); + + break; + case Recipe.Rejuv: + case Recipe.FullRejuv: + this.recipes.push({ Ingredients: ingredients, Index: index }); + + break; + } + } + }, + + validIngredients: [], // What we have + neededIngredients: [], // What we need + subRecipes: [], + + buildLists: function () { + CraftingSystem.checkSubrecipes(); + + Cubing.validIngredients = []; + Cubing.neededIngredients = []; + let items = me.findItems(-1, sdk.items.mode.inStorage); + + 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" && this.gems.flawless.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "gem" && this.gems.normal.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "cgem" && this.gems.chipped.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "hpot" && this.pots.healing.includes(items[k].classid)) + || (this.recipes[i].Ingredients[j] === "mpot" && this.pots.mana.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({ classid: items[k].classid, gid: items[k].gid }); + + // 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 + if (this.recipes[i].Index !== Recipe.Rune + || ([Recipe.Rune, Recipe.Rejuv, Recipe.FullRejuv].includes(this.recipes[i].Index) && j >= 1)) { + // Enable rune recipe after 2 bases are found + 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 gem recipes (if needed) + if (!this.recipes[i].hasOwnProperty("MainRecipe")) { + // make sure we don't add a subrecipe to a subrecipe + for (let gType of Object.values(Cubing.gems)) { + // skip over cgems - can't cube them + if (gType.includes(sdk.items.gems.Chipped.Amethyst)) continue; + for (let gem of gType) { + if (this.subRecipes.indexOf(gem) === -1 + && (this.recipes[i].Ingredients[j] === gem + || (this.recipes[i].Ingredients[j] === "pgem" && Cubing.gemList.includes(gem)))) { + this.recipes.push({ + Ingredients: [gem - 1, gem - 1, gem - 1], + Index: Recipe.Gem, + AlwaysEnabled: true, + MainRecipe: this.recipes[i].Index + }); + this.subRecipes.push(gem); + } + } + } + } + } + } + }, + + // Remove unneeded flawless gem recipes + clearSubRecipes: function () { + Cubing.subRecipes = []; + + for (let i = 0; i < this.recipes.length; i += 1) { + if (this.recipes[i].hasOwnProperty("MainRecipe")) { + this.recipes.splice(i, 1); + + i -= 1; + } + } + }, + + update: function () { + this.clearSubRecipes(); + this.buildLists(); + }, + + /** + * @param {recipeObj} recipe + * @returns {boolean} + */ + checkRecipe: function (recipe) { + let usedGids = []; + let matchList = []; + + for (let i = 0; i < recipe.Ingredients.length; i += 1) { + for (let j = 0; j < this.validIngredients.length; j += 1) { + if (usedGids.indexOf(this.validIngredients[j].gid) === -1 && ( + this.validIngredients[j].classid === recipe.Ingredients[i] + || (recipe.Ingredients[i] === "pgem" && this.gemList.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "fgem" && this.gems.flawless.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "gem" && this.gems.normal.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "cgem" && this.gems.chipped.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "hpot" && this.pots.healing.includes(this.validIngredients[j].classid)) + || (recipe.Ingredients[i] === "mpot" && this.pots.mana.includes(this.validIngredients[j].classid)) + )) { + let item = me.getItem(this.validIngredients[j].classid, -1, this.validIngredients[j].gid); + + // 26.11.2012. check if the item actually belongs to the given recipe + if (item && this.validItem(item, recipe)) { + // don't repeat the same item + usedGids.push(this.validIngredients[j].gid); + // push the item into the match list + matchList.push(copyUnit(item)); + + break; + } + } + } + + // no new items in the match list = not enough ingredients + if (matchList.length !== i + 1) return false; + } + + // return the match list. these items go to cube + return matchList; + }, + + /** + * debug function - get what each recipe needs + * @param {number} index + * @returns {string} + */ + getRecipeNeeds: function (index) { + let rval = " ["; + + for (let i = 0; i < this.neededIngredients.length; i += 1) { + if (this.neededIngredients[i].recipe.Index === index) { + rval += this.neededIngredients[i].classid + (i === this.neededIngredients.length - 1 ? "" : " "); + } + } + + rval += "]"; + + return rval; + }, + + /** + * Check an item on ground for pickup + * @param {ItemUnit} unit + * @returns {boolean} + */ + checkItem: function (unit) { + if (!Config.Cubing) return false; + if (this.keepItem(unit)) return true; + + for (let i = 0; i < this.neededIngredients.length; i += 1) { + if (unit.classid === this.neededIngredients[i].classid + && this.validItem(unit, this.neededIngredients[i].recipe)) { + //debugLog("Cubing: " + unit.name + " " + this.neededIngredients[i].recipe.Index + " " + (this.neededIngredients[i].recipe.hasOwnProperty("MainRecipe") ? this.neededIngredients[i].recipe.MainRecipe : "") + this.getRecipeNeeds(this.neededIngredients[i].recipe.Index)); + return true; + } + } + + return false; + }, + + /** + * Don't drop an item from inventory if it's a part of cubing recipe + * @param {ItemUnit} unit + * @returns {boolean} + */ + keepItem: function (unit) { + if (!Config.Cubing) return false; + + for (let i = 0; i < this.validIngredients.length; i += 1) { + if (unit.mode === sdk.items.mode.inStorage && unit.gid === this.validIngredients[i].gid) { + return true; + } + } + + return false; + }, + + /** + * Check if this item is valid for a given recipe + * @param {ItemUnit} unit + * @param {recipeObj} recipe + * @returns {boolean} + */ + validItem: function (unit, recipe) { + // Excluded items + // Don't use items in locked inventory space - or wanted by other systems + if ((unit.isInInventory && Storage.Inventory.IsLocked(unit, Config.Inventory) + || Runewords.validGids.includes(unit.gid) || CraftingSystem.validGids.includes(unit.gid))) { + return false; + } + + const rIndex = recipe.Index; + + // Pots and Gems - for Rejuv recipes + if ([Recipe.Rejuv, Recipe.FullRejuv].includes(rIndex)) { + /** + * @todo do this better, hacky fix for now + */ + if (!recipe.Enabled) { + if (rIndex === Recipe.Rejuv && this.gems.chipped.includes(unit.classid)) return true; + if (rIndex === Recipe.FullRejuv && this.gems.normal.includes(unit.classid)) return true; + return false; + } + + if (rIndex === Recipe.Rejuv && this.gems.chipped.includes(unit.classid)) return true; + if (rIndex === Recipe.FullRejuv && this.gems.normal.includes(unit.classid)) return true; + if ([].concat(Cubing.pots.healing, Cubing.pots.mana).includes(unit.classid)) { + return true; + } + + 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 (rIndex === Recipe.Token) return true; + + // START + const ntipResult = NTIP.CheckItem(unit); + + if (rIndex >= Recipe.HitPower.Helm && rIndex <= Recipe.Safety.Weapon) { + // 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 + && ntipResult === Pickit.Result.UNWANTED) { + return true; + } + + return false; + } + + let upgradeUnique = rIndex >= Recipe.Unique.Weapon.ToExceptional && rIndex <= Recipe.Unique.Armor.ToElite; + let upgradeRare = rIndex >= Recipe.Rare.Weapon.ToExceptional && rIndex <= Recipe.Rare.Armor.ToElite; + let socketNormal = rIndex >= Recipe.Socket.Shield && rIndex <= Recipe.Socket.Helm; + let socketMagic = [Recipe.Socket.Magic.LowWeapon, Recipe.Socket.Magic.HighWeapon].includes(rIndex); + let socketRare = rIndex === Recipe.Socket.Rare; + + if (socketRare && 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; + } + + if (upgradeUnique || upgradeRare || socketNormal || socketRare) { + switch (true) { + case upgradeUnique && unit.unique && ntipResult === Pickit.Result.WANTED: // Unique item matching pickit entry + case upgradeRare && unit.rare && ntipResult === Pickit.Result.WANTED: // Rare item matching pickit entry + case socketNormal && unit.normal && unit.sockets === 0: // Normal item matching pickit entry, no sockets + case socketMagic && unit.magic && unit.sockets === 0: // Magic item matching pickit entry, no sockets + case socketRare && unit.rare && unit.sockets === 0: // Rare item matching pickit entry, no sockets + if (socketMagic) { + if (rIndex === Recipe.Socket.Magic.LowWeapon && unit.ilvl > recipe.Level) return false; + if (rIndex === Recipe.Socket.Magic.HighWeapon && unit.ilvl < recipe.Level) return false; + } + if (recipe.Ethereal === undefined) return ntipResult === Pickit.Result.WANTED; + switch (recipe.Ethereal) { + case Roll.All: + 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; + } + + return false; + } + + if (rIndex === Recipe.Reroll.Magic + || (rIndex >= Recipe.Reroll.Charm.Small && rIndex <= Recipe.Reroll.Charm.Grand)) { + return (unit.magic && unit.ilvl >= recipe.Level && ntipResult === Pickit.Result.UNWANTED); + } + + if (rIndex === Recipe.Reroll.Rare) { + return (unit.rare && ntipResult === Pickit.Result.UNWANTED); + } + + if (rIndex === 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; + } + + if (rIndex === Recipe.LowToNorm.Armor || rIndex === Recipe.LowToNorm.Weapon) { + return (unit.lowquality && ntipResult === Pickit.Result.UNWANTED); + } + + return false; + }, + + doCubing: function () { + if (!Config.Cubing) return false; + if (!me.getItem(sdk.quest.item.Cube)) return false; + + 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 += 1) { + let string = "Transmuting: "; + let items = this.checkRecipe(tempArray[i]); + + if (Array.isArray(items) && items.length) { + // 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(); + + i = -1; + + let itemsToCubeCount = items.length; + + while (items.length) { + string += (items[0].name.trim() + (items.length > 1 ? " + " : "")); + Storage.Cube.MoveTo(items[0]); + items.shift(); + } + + let itemsInCube = me.getItemsEx().filter(el => el.isInCube); + if (itemsInCube.length !== itemsToCubeCount) { + console.warn("Failed to move all necesary items to cube"); + itemsInCube.forEach(item => { + if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) return; + if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return; + }); + return false; + } + + if (!this.openCube()) return false; + + transmute(); + delay(700 + me.ping); + print("ÿc4Cubing: " + string); + Config.ShowCubingInfo && D2Bot.printToConsole(string, sdk.colors.D2Bot.Green); + this.update(); + + let cubeItems = me.findItems(-1, -1, sdk.storage.Cube); + + if (items) { + for (let j = 0; j < cubeItems.length; j += 1) { + let cubeItem = cubeItems[j]; + let result = Pickit.checkItem(cubeItem); + + /** + * @todo + * - build better method of updating cubelist so if a item we cube is wanted by cubing we + * can update our list without clearing and rebuilding the whole thing + */ + + switch (result.result) { + case Pickit.Result.UNWANTED: + Item.logger("Dropped", cubeItem, "doCubing"); + cubeItem.drop(); + + break; + case Pickit.Result.WANTED: + Item.logger("Cubing Kept", cubeItem); + Item.logItem("Cubing Kept", cubeItem, result.line); - break; - case Pickit.Result.WANTED: - Item.logger("Cubing Kept", cubeItem); - Item.logItem("Cubing Kept", cubeItem, result.line); + break; + case Pickit.Result.RUNEWORD: + Runewords.update(cubeItem.classid, cubeItem.gid); - break; - case Pickit.Result.RUNEWORD: - Runewords.update(cubeItem.classid, cubeItem.gid); + break; + case Pickit.Result.CRAFTING: + CraftingSystem.update(cubeItem); - break; - case Pickit.Result.CRAFTING: - CraftingSystem.update(cubeItem); + break; + } + } + } - break; - } - } - } + if (!this.emptyCube()) { + break; + } + } + } + + /** + * For now, until I write a better update method, give a recursive call to doCubing if after building list + * we find we can still cube + */ + Cubing.update(); + let checkList = this.recipes.slice().shuffle(); + if (checkList.some(r => Cubing.checkRecipe(r))) { + // we can still cube so recursive call to doCubing + return Cubing.doCubing(); + } - if (!this.emptyCube()) { - break; - } - } - } - - /** - * For now, until I write a better update method, give a recursive call to doCubing if after building list - * we find we can still cube - */ - Cubing.update(); - let checkList = this.recipes.slice().shuffle(); - if (checkList.some(r => Cubing.checkRecipe(r))) { - // we can still cube so recursive call to doCubing - return Cubing.doCubing(); - } + if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { + delay(1000); + + while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { + me.cancel(); + delay(300); + } + } + + return true; + }, + + cursorCheck: function () { + if (me.itemoncursor) { + let item = Game.getCursorUnit(); + + if (item) { + if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) return true; + if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return true; + + if (item.drop()) { + Item.logger("Dropped", item, "cursorCheck"); + return true; + } + } + + return false; + } + + return true; + }, + + openCube: function () { + if (getUIFlag(sdk.uiflags.Cube)) return true; + + let cube = me.getItem(sdk.quest.item.Cube); + if (!cube) return false; - if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { - delay(1000); - - while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { - me.cancel(); - delay(300); - } - } - - return true; - }, - - cursorCheck: function () { - if (me.itemoncursor) { - let item = Game.getCursorUnit(); - - if (item) { - if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) return true; - if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return true; - - if (item.drop()) { - Item.logger("Dropped", item, "cursorCheck"); - return true; - } - } - - return false; - } - - return true; - }, - - openCube: function () { - if (getUIFlag(sdk.uiflags.Cube)) return true; - - let cube = me.getItem(sdk.quest.item.Cube); - if (!cube) return false; + if (cube.isInStash && !Town.openStash()) return false; - if (cube.isInStash && !Town.openStash()) return false; + for (let i = 0; i < 5 && !getUIFlag(sdk.uiflags.Cube); i++) { + cube.interact(); + + if (Misc.poll(() => getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { + delay(100 + me.ping * 2); // allow UI to initialize - for (let i = 0; i < 5 && !getUIFlag(sdk.uiflags.Cube); i++) { - cube.interact(); - - if (Misc.poll(() => getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { - delay(100 + me.ping * 2); // allow UI to initialize + return true; + } + } + + return getUIFlag(sdk.uiflags.Cube); + }, + + closeCube: function () { + if (!getUIFlag(sdk.uiflags.Cube)) return true; + + for (let i = 0; i < 5 && getUIFlag(sdk.uiflags.Cube); i++) { + me.cancel(); + + if (Misc.poll(() => !getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { + delay(250 + me.ping * 2); // allow UI to initialize + + return true; + } + } + + return !getUIFlag(sdk.uiflags.Cube); + }, + + emptyCube: function () { + let cube = me.getItem(sdk.quest.item.Cube); + if (!cube) return false; + + let items = me.findItems(-1, -1, sdk.storage.Cube); + if (!items) return true; + + let sorted = false; - return true; - } - } - - return getUIFlag(sdk.uiflags.Cube); - }, - - closeCube: function () { - if (!getUIFlag(sdk.uiflags.Cube)) return true; - - for (let i = 0; i < 5 && getUIFlag(sdk.uiflags.Cube); i++) { - me.cancel(); - - if (Misc.poll(() => !getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { - delay(250 + me.ping * 2); // allow UI to initialize - - return true; - } - } - - return !getUIFlag(sdk.uiflags.Cube); - }, - - emptyCube: function () { - let cube = me.getItem(sdk.quest.item.Cube); - if (!cube) return false; - - let items = me.findItems(-1, -1, sdk.storage.Cube); - if (!items) return true; - - let sorted = 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])) { + // attempt to sort inventory first then try again + if (!sorted && Storage.Inventory.SortItems()) { + sorted = true; + continue; + } + + console.warn("Failed to empty cube. Items still in cube :: ", items.map(i => i && i.prettyPrint)); + return false; + } - if (!Storage.Stash.MoveTo(items[0]) && !Storage.Inventory.MoveTo(items[0])) { - // attempt to sort inventory first then try again - if (!sorted && Storage.Inventory.SortItems()) { - sorted = true; - continue; - } - - console.warn("Failed to empty cube. Items still in cube :: ", items.map(i => i && i.prettyPrint)); - return false; - } + items.shift(); + } - items.shift(); - } + this.closeCube(); - this.closeCube(); + return true; + }, - return true; - }, + makeRevPots: function () { + let locations = { + Belt: 2, + Inventory: 3, + Cube: 6, + Stash: 7, + }; + let origin = [], cube = me.getItem(sdk.quest.item.Cube), cubeInStash; - makeRevPots: function () { - let locations = { - Belt: 2, - Inventory: 3, - Cube: 6, - Stash: 7, - }; - let origin = [], cube = me.getItem(sdk.quest.item.Cube), cubeInStash; + // Get a list of all items - Filter out all those rev pots + let revpots = me.getItemsEx().filter(item => item.classid === sdk.items.RejuvenationPotion); - // Get a list of all items - Filter out all those rev pots - let revpots = me.getItemsEx().filter(item => item.classid === sdk.items.RejuvenationPotion); + // Stop if less as 3 pots + if (revpots.length < 3) { + return; + } - // Stop if less as 3 pots - if (revpots.length < 3) { - return; - } + // Go to town and open stash + Town.goToTown() && Town.moveToSpot("stash"); + Town.openStash(); - // Go to town and open stash - Town.goToTown() && Town.moveToSpot("stash"); - Town.openStash(); + // For reasons unclear, cubing goes wrong in stash in my test, so for ease, i put cube in inventory + (cubeInStash = cube.location !== locations.Inventory) && Storage.Inventory.MoveTo(cube); + me.cancel(); + me.cancel(); - // For reasons unclear, cubing goes wrong in stash in my test, so for ease, i put cube in inventory - (cubeInStash = cube.location !== locations.Inventory) && Storage.Inventory.MoveTo(cube); - me.cancel(); - me.cancel(); + // clear the cube, otherwise we cant transmute + Cubing.emptyCube(); - // clear the cube, otherwise we cant transmute - Cubing.emptyCube(); + // Remove excessive pots from the list. (only groups of 3) + revpots.length -= revpots.length % 3; - // Remove excessive pots from the list. (only groups of 3) - revpots.length -= revpots.length % 3; + // Call this function for each pot + revpots.forEach(function (pot, index) { - // Call this function for each pot - revpots.forEach(function (pot, index) { + // Add this to the original location array + origin.push({ location: pot.location, x: pot.x, y: pot.y }); - // Add this to the original location array - origin.push({ location: pot.location, x: pot.x, y: pot.y }); + Town.openStash(); - Town.openStash(); + // Move to inventory first (to avoid bugs) + Storage.Inventory.MoveTo(pot); + me.cancel(); // remove inventory/cube window + me.cancel(); // remove inventory window (if it was cube) - // Move to inventory first (to avoid bugs) - Storage.Inventory.MoveTo(pot); - me.cancel(); // remove inventory/cube window - me.cancel(); // remove inventory window (if it was cube) - - // Move the current pot to the cube - Storage.Cube.MoveTo(pot); - // For every third pot, excluding the first - if (!index || (1 + index) % 3 !== 0) { - me.cancel(); // remove cube window - me.cancel(); // remove stash window - } else { - // press the transmute button - Cubing.openCube() && transmute(); - - // high delay here to avoid issues with ping spikes - delay(me.ping * 5 + 1000); // <-- probably can be less - - // Find all items in the cube. (the full rev pot) - let fullrev = me.findItem(-1, -1, sdk.storage.Cube); - - // Sort the original locations of the pots. Put a low location first (belt = 2, rest is higher). - origin.sort((a, b) => a.location - b.location).some(function (orgin) { // Loop over all the original spots. - - // Loop trough all possible locations - for (let i in locations) { - // If location is matched with its orgin, we know the name of the spot - locations[i] === orgin.location && (orgin.location = i); // Store the name of the location - } - - Storage.Inventory.MoveTo(fullrev); // First put to inventory; - me.cancel(); // cube - me.cancel(); // inventory - - // If the storage location is known, put the pot to this location - Storage[orgin.location] && Storage[orgin.location].MoveTo(fullrev); - - // If returned true, the prototype some stops looping. - return fullrev.location !== locations.Cube; - }); - - // empty the array - origin.length = 0; - - // Cube should be empty, but lets be sure - Cubing.emptyCube(); - } - }); - // Put cube back in stash, if it was when we started - cubeInStash && Storage.Stash.MoveTo(cube); - - me.cancel(); - me.cancel(); - }, + // Move the current pot to the cube + Storage.Cube.MoveTo(pot); + // For every third pot, excluding the first + if (!index || (1 + index) % 3 !== 0) { + me.cancel(); // remove cube window + me.cancel(); // remove stash window + } else { + // press the transmute button + Cubing.openCube() && transmute(); + + // high delay here to avoid issues with ping spikes + delay(me.ping * 5 + 1000); // <-- probably can be less + + // Find all items in the cube. (the full rev pot) + let fullrev = me.findItem(-1, -1, sdk.storage.Cube); + + // Sort the original locations of the pots. Put a low location first (belt = 2, rest is higher). + origin.sort((a, b) => a.location - b.location).some(function (orgin) { // Loop over all the original spots. + + // Loop trough all possible locations + for (let i in locations) { + // If location is matched with its orgin, we know the name of the spot + locations[i] === orgin.location && (orgin.location = i); // Store the name of the location + } + + Storage.Inventory.MoveTo(fullrev); // First put to inventory; + me.cancel(); // cube + me.cancel(); // inventory + + // If the storage location is known, put the pot to this location + Storage[orgin.location] && Storage[orgin.location].MoveTo(fullrev); + + // If returned true, the prototype some stops looping. + return fullrev.location !== locations.Cube; + }); + + // empty the array + origin.length = 0; + + // Cube should be empty, but lets be sure + Cubing.emptyCube(); + } + }); + // Put cube back in stash, if it was when we started + cubeInStash && Storage.Stash.MoveTo(cube); + + me.cancel(); + me.cancel(); + }, }; diff --git a/d2bs/kolbot/libs/core/Experience.js b/d2bs/kolbot/libs/core/Experience.js index da44dd646..e4bfe9f32 100644 --- a/d2bs/kolbot/libs/core/Experience.js +++ b/d2bs/kolbot/libs/core/Experience.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename Experience.js * @author kolton @@ -6,133 +7,133 @@ */ const Experience = { - /** + /** * @todo combine this and nextExp into key-value pairs 1-99 * Experience[me.charlvl].total and Experience[me.charlvl].next */ - totalExp: [ - 0, 0, 500, 1500, 3750, 7875, 14175, 22680, 32886, 44396, 57715, 72144, 90180, 112725, - 140906, 176132, 220165, 275207, 344008, 430010, 537513, 671891, 839864, 1049830, 1312287, - 1640359, 2050449, 2563061, 3203826, 3902260, 4663553, 5493363, 6397855, 7383752, 8458379, - 9629723, 10906488, 12298162, 13815086, 15468534, 17270791, 19235252, 21376515, 23710491, - 26254525, 29027522, 32050088, 35344686, 38935798, 42850109, 47116709, 51767302, 56836449, - 62361819, 68384473, 74949165, 82104680, 89904191, 98405658, 107672256, 117772849, 128782495, - 140783010, 153863570, 168121381, 183662396, 200602101, 219066380, 239192444, 261129853, - 285041630, 311105466, 339515048, 370481492, 404234916, 441026148, 481128591, 524840254, - 572485967, 624419793, 681027665, 742730244, 809986056, 883294891, 963201521, 1050299747, - 1145236814, 1248718217, 1361512946, 1484459201, 1618470619, 1764543065, 1923762030, - 2097310703, 2286478756, 2492671933, 2717422497, 2962400612, 3229426756, 3520485254, 0, 0 - ], - nextExp: [ - 0, 500, 1000, 2250, 4125, 6300, 8505, 10206, 11510, 13319, 14429, 18036, 22545, 28181, - 35226, 44033, 55042, 68801, 86002, 107503, 134378, 167973, 209966, 262457, 328072, 410090, - 512612, 640765, 698434, 761293, 829810, 904492, 985897, 1074627, 1171344, 1276765, 1391674, - 1516924, 1653448, 1802257, 1964461, 2141263, 2333976, 2544034, 2772997, 3022566, 3294598, - 3591112, 3914311, 4266600, 4650593, 5069147, 5525370, 6022654, 6564692, 7155515, 7799511, - 8501467, 9266598, 10100593, 11009646, 12000515, 13080560, 14257811, 15541015, 16939705, - 18464279, 20126064, 21937409, 23911777, 26063836, 28409582, 30966444, 33753424, 36791232, - 40102443, 43711663, 47645713, 51933826, 56607872, 61702579, 67255812, 73308835, 79906630, - 87098226, 94937067, 103481403, 112794729, 122946255, 134011418, 146072446, 159218965, 173548673, - 189168053, 206193177, 224750564, 244978115, 267026144, 291058498, 0, 0 - ], - expCurve: [13, 16, 110, 159, 207, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 174, 92, 38, 5], - expPenalty: [1024, 976, 928, 880, 832, 784, 736, 688, 640, 592, 544, 496, 448, 400, 352, 304, 256, 192, 144, 108, 81, 61, 46, 35, 26, 20, 15, 11, 8, 6, 5], - monsterExp: [ - [1, 1, 1], [30, 78, 117], [40, 104, 156], [50, 131, 197], [60, 156, 234], [70, 182, 273], [80, 207, 311], [90, 234, 351], [100, 260, 390], [110, 285, 428], [120, 312, 468], - [130, 338, 507], [140, 363, 545], [154, 401, 602], [169, 440, 660], [186, 482, 723], [205, 533, 800], [225, 584, 876], [248, 644, 966], [273, 708, 1062], [300, 779, 1169], - [330, 857, 1286], [363, 942, 1413], [399, 1035, 1553], [439, 1139, 1709], [470, 1220, 1830], [503, 1305, 1958], [538, 1397, 2096], [576, 1494, 2241], [616, 1598, 2397], - [659, 1709, 2564], [706, 1832, 2748], [755, 1958, 2937], [808, 2097, 3146], [864, 2241, 3362], [925, 2399, 3599], [990, 2568, 3852], [1059, 2745, 4118], [1133, 2939, 4409], - [1212, 3144, 4716], [1297, 3365, 5048], [1388, 3600, 5400], [1485, 3852, 5778], [1589, 4121, 6182], [1693, 4409, 6614], [1797, 4718, 7077], [1901, 5051, 7577], - [2005, 5402, 8103], [2109, 5783, 8675], [2213, 6186, 9279], [2317, 6618, 9927], [2421, 7080, 10620], [2525, 7506, 11259], [2629, 7956, 11934], [2733, 8435, 12653], - [2837, 8942, 13413], [2941, 9477, 14216], [3045, 10044, 15066], [3149, 10647, 15971], [3253, 11286, 16929], [3357, 11964, 17946], [3461, 12680, 19020], - [3565, 13442, 20163], [3669, 14249, 21374], [3773, 15104, 22656], [3877, 16010, 24015], [3981, 16916, 25374], [4085, 17822, 26733], [4189, 18728, 28092], - [4293, 19634, 29451], [4397, 20540, 30810], [4501, 21446, 32169], [4605, 22352, 33528], [4709, 23258, 34887], [4813, 24164, 36246], [4917, 25070, 37605], - [5021, 25976, 38964], [5125, 26882, 40323], [5229, 27788, 41682], [5333, 28694, 43041], [5437, 29600, 44400], [5541, 30506, 45759], [5645, 31412, 47118], - [5749, 32318, 48477], [5853, 33224, 49836], [5957, 34130, 51195], [6061, 35036, 52554], [6165, 35942, 53913], [6269, 36848, 55272], [6373, 37754, 56631], - [6477, 38660, 57990], [6581, 39566, 59349], [6685, 40472, 60708], [6789, 41378, 62067], [6893, 42284, 63426], [6997, 43190, 64785], [7101, 44096, 66144], - [7205, 45002, 67503], [7309, 45908, 68862], [7413, 46814, 70221], [7517, 47720, 71580], [7621, 48626, 72939], [7725, 49532, 74298], [7829, 50438, 75657], - [7933, 51344, 77016], [8037, 52250, 78375], [8141, 53156, 79734], [8245, 54062, 81093], [8349, 54968, 82452], [8453, 55874, 83811], [160000, 160000, 160000] - ], - /** + totalExp: [ + 0, 0, 500, 1500, 3750, 7875, 14175, 22680, 32886, 44396, 57715, 72144, 90180, 112725, + 140906, 176132, 220165, 275207, 344008, 430010, 537513, 671891, 839864, 1049830, 1312287, + 1640359, 2050449, 2563061, 3203826, 3902260, 4663553, 5493363, 6397855, 7383752, 8458379, + 9629723, 10906488, 12298162, 13815086, 15468534, 17270791, 19235252, 21376515, 23710491, + 26254525, 29027522, 32050088, 35344686, 38935798, 42850109, 47116709, 51767302, 56836449, + 62361819, 68384473, 74949165, 82104680, 89904191, 98405658, 107672256, 117772849, 128782495, + 140783010, 153863570, 168121381, 183662396, 200602101, 219066380, 239192444, 261129853, + 285041630, 311105466, 339515048, 370481492, 404234916, 441026148, 481128591, 524840254, + 572485967, 624419793, 681027665, 742730244, 809986056, 883294891, 963201521, 1050299747, + 1145236814, 1248718217, 1361512946, 1484459201, 1618470619, 1764543065, 1923762030, + 2097310703, 2286478756, 2492671933, 2717422497, 2962400612, 3229426756, 3520485254, 0, 0 + ], + nextExp: [ + 0, 500, 1000, 2250, 4125, 6300, 8505, 10206, 11510, 13319, 14429, 18036, 22545, 28181, + 35226, 44033, 55042, 68801, 86002, 107503, 134378, 167973, 209966, 262457, 328072, 410090, + 512612, 640765, 698434, 761293, 829810, 904492, 985897, 1074627, 1171344, 1276765, 1391674, + 1516924, 1653448, 1802257, 1964461, 2141263, 2333976, 2544034, 2772997, 3022566, 3294598, + 3591112, 3914311, 4266600, 4650593, 5069147, 5525370, 6022654, 6564692, 7155515, 7799511, + 8501467, 9266598, 10100593, 11009646, 12000515, 13080560, 14257811, 15541015, 16939705, + 18464279, 20126064, 21937409, 23911777, 26063836, 28409582, 30966444, 33753424, 36791232, + 40102443, 43711663, 47645713, 51933826, 56607872, 61702579, 67255812, 73308835, 79906630, + 87098226, 94937067, 103481403, 112794729, 122946255, 134011418, 146072446, 159218965, 173548673, + 189168053, 206193177, 224750564, 244978115, 267026144, 291058498, 0, 0 + ], + expCurve: [13, 16, 110, 159, 207, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 174, 92, 38, 5], + expPenalty: [1024, 976, 928, 880, 832, 784, 736, 688, 640, 592, 544, 496, 448, 400, 352, 304, 256, 192, 144, 108, 81, 61, 46, 35, 26, 20, 15, 11, 8, 6, 5], + monsterExp: [ + [1, 1, 1], [30, 78, 117], [40, 104, 156], [50, 131, 197], [60, 156, 234], [70, 182, 273], [80, 207, 311], [90, 234, 351], [100, 260, 390], [110, 285, 428], [120, 312, 468], + [130, 338, 507], [140, 363, 545], [154, 401, 602], [169, 440, 660], [186, 482, 723], [205, 533, 800], [225, 584, 876], [248, 644, 966], [273, 708, 1062], [300, 779, 1169], + [330, 857, 1286], [363, 942, 1413], [399, 1035, 1553], [439, 1139, 1709], [470, 1220, 1830], [503, 1305, 1958], [538, 1397, 2096], [576, 1494, 2241], [616, 1598, 2397], + [659, 1709, 2564], [706, 1832, 2748], [755, 1958, 2937], [808, 2097, 3146], [864, 2241, 3362], [925, 2399, 3599], [990, 2568, 3852], [1059, 2745, 4118], [1133, 2939, 4409], + [1212, 3144, 4716], [1297, 3365, 5048], [1388, 3600, 5400], [1485, 3852, 5778], [1589, 4121, 6182], [1693, 4409, 6614], [1797, 4718, 7077], [1901, 5051, 7577], + [2005, 5402, 8103], [2109, 5783, 8675], [2213, 6186, 9279], [2317, 6618, 9927], [2421, 7080, 10620], [2525, 7506, 11259], [2629, 7956, 11934], [2733, 8435, 12653], + [2837, 8942, 13413], [2941, 9477, 14216], [3045, 10044, 15066], [3149, 10647, 15971], [3253, 11286, 16929], [3357, 11964, 17946], [3461, 12680, 19020], + [3565, 13442, 20163], [3669, 14249, 21374], [3773, 15104, 22656], [3877, 16010, 24015], [3981, 16916, 25374], [4085, 17822, 26733], [4189, 18728, 28092], + [4293, 19634, 29451], [4397, 20540, 30810], [4501, 21446, 32169], [4605, 22352, 33528], [4709, 23258, 34887], [4813, 24164, 36246], [4917, 25070, 37605], + [5021, 25976, 38964], [5125, 26882, 40323], [5229, 27788, 41682], [5333, 28694, 43041], [5437, 29600, 44400], [5541, 30506, 45759], [5645, 31412, 47118], + [5749, 32318, 48477], [5853, 33224, 49836], [5957, 34130, 51195], [6061, 35036, 52554], [6165, 35942, 53913], [6269, 36848, 55272], [6373, 37754, 56631], + [6477, 38660, 57990], [6581, 39566, 59349], [6685, 40472, 60708], [6789, 41378, 62067], [6893, 42284, 63426], [6997, 43190, 64785], [7101, 44096, 66144], + [7205, 45002, 67503], [7309, 45908, 68862], [7413, 46814, 70221], [7517, 47720, 71580], [7621, 48626, 72939], [7725, 49532, 74298], [7829, 50438, 75657], + [7933, 51344, 77016], [8037, 52250, 78375], [8141, 53156, 79734], [8245, 54062, 81093], [8349, 54968, 82452], [8453, 55874, 83811], [160000, 160000, 160000] + ], + /** * Percent progress into the current level. Format: xx.xx% */ - progress: function () { - return me.getStat(sdk.stats.Level) === 99 ? 0 : (((me.getStat(sdk.stats.Experience) - this.totalExp[me.getStat(sdk.stats.Level)]) / this.nextExp[me.getStat(sdk.stats.Level)]) * 100).toFixed(2); - }, + progress: function () { + return me.getStat(sdk.stats.Level) === 99 ? 0 : (((me.getStat(sdk.stats.Experience) - this.totalExp[me.getStat(sdk.stats.Level)]) / this.nextExp[me.getStat(sdk.stats.Level)]) * 100).toFixed(2); + }, - /** + /** * Total experience gained in current run */ - gain: function () { - return (me.getStat(sdk.stats.Experience) - DataFile.getStats().experience); - }, + gain: function () { + return (me.getStat(sdk.stats.Experience) - DataFile.getStats().experience); + }, - /** + /** * Percent experience gained in current run */ - gainPercent: function () { - return me.getStat(sdk.stats.Level) === 99 ? 0 : (this.gain() * 100 / this.nextExp[me.getStat(sdk.stats.Level)]).toFixed(6); - }, + gainPercent: function () { + return me.getStat(sdk.stats.Level) === 99 ? 0 : (this.gain() * 100 / this.nextExp[me.getStat(sdk.stats.Level)]).toFixed(6); + }, - /** + /** * Runs until next level */ - runsToLevel: function () { - return Math.round(((100 - this.progress()) / 100) * this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); - }, + runsToLevel: function () { + return Math.round(((100 - this.progress()) / 100) * this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); + }, - /** + /** * Total runs needed for next level (not counting current progress) */ - totalRunsToLevel: function () { - return Math.round(this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); - }, + totalRunsToLevel: function () { + return Math.round(this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); + }, - /** + /** * Total time till next level */ - timeToLevel: function () { - let tTLrawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000)).toString(); - let tTLrawtimeToLevel = this.runsToLevel() * tTLrawSeconds; - let tTLDays = Math.floor(tTLrawtimeToLevel / 86400); - let tTLHours = Math.floor((tTLrawtimeToLevel % 86400) / 3600); - let tTLMinutes = Math.floor(((tTLrawtimeToLevel % 86400) % 3600) / 60); - //let tTLSeconds = ((tTLrawtimeToLevel % 86400) % 3600) % 60; + timeToLevel: function () { + let tTLrawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000)).toString(); + let tTLrawtimeToLevel = this.runsToLevel() * tTLrawSeconds; + let tTLDays = Math.floor(tTLrawtimeToLevel / 86400); + let tTLHours = Math.floor((tTLrawtimeToLevel % 86400) / 3600); + let tTLMinutes = Math.floor(((tTLrawtimeToLevel % 86400) % 3600) / 60); + //let tTLSeconds = ((tTLrawtimeToLevel % 86400) % 3600) % 60; - //return tDays + "d " + tTLHours + "h " + tTLMinutes + "m " + tTLSeconds + "s"; - //return tTLDays + "d " + tTLHours + "h " + tTLMinutes + "m"; - return (tTLDays ? tTLDays + " d " : "") + (tTLHours ? tTLHours + " h " : "") + (tTLMinutes ? tTLMinutes + " m" : ""); - }, + //return tDays + "d " + tTLHours + "h " + tTLMinutes + "m " + tTLSeconds + "s"; + //return tTLDays + "d " + tTLHours + "h " + tTLMinutes + "m"; + return (tTLDays ? tTLDays + " d " : "") + (tTLHours ? tTLHours + " h " : "") + (tTLMinutes ? tTLMinutes + " m" : ""); + }, - /** + /** * Get Game Time */ - getGameTime: function () { - let rawMinutes = Math.floor((getTickCount() - me.gamestarttime) / 60000).toString(); - let rawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000) % 60).toString(); + getGameTime: function () { + let rawMinutes = Math.floor((getTickCount() - me.gamestarttime) / 60000).toString(); + let rawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000) % 60).toString(); - rawMinutes <= 9 && (rawMinutes = "0" + rawMinutes); - rawSeconds <= 9 && (rawSeconds = "0" + rawSeconds); + rawMinutes <= 9 && (rawMinutes = "0" + rawMinutes); + rawSeconds <= 9 && (rawSeconds = "0" + rawSeconds); - return " (" + rawMinutes + ":" + rawSeconds + ")"; - }, + return " (" + rawMinutes + ":" + rawSeconds + ")"; + }, - /** + /** * Log to manager */ - log: function () { - let gain = this.gain(); - let progress = this.progress(); - let runsToLevel = this.runsToLevel(); - let getGameTime = this.getGameTime(); - let string = "[Game: " + me.gamename + (me.gamepassword ? "//" + me.gamepassword : "") + getGameTime + "] [Level: " + me.getStat(sdk.stats.Level) + " (" + progress + "%)] [XP: " + gain + "] [Games ETA: " + runsToLevel + "]"; + log: function () { + let gain = this.gain(); + let progress = this.progress(); + let runsToLevel = this.runsToLevel(); + let getGameTime = this.getGameTime(); + let string = "[Game: " + me.gamename + (me.gamepassword ? "//" + me.gamepassword : "") + getGameTime + "] [Level: " + me.getStat(sdk.stats.Level) + " (" + progress + "%)] [XP: " + gain + "] [Games ETA: " + runsToLevel + "]"; - if (gain) { - D2Bot.printToConsole(string, sdk.colors.D2Bot.Blue); + if (gain) { + D2Bot.printToConsole(string, sdk.colors.D2Bot.Blue); - if (me.getStat(sdk.stats.Level) > DataFile.getStats().level) { - D2Bot.printToConsole("Congrats! You gained a level. Current level:" + me.getStat(sdk.stats.Level), sdk.colors.D2Bot.Green); - } - } - } + if (me.getStat(sdk.stats.Level) > DataFile.getStats().level) { + D2Bot.printToConsole("Congrats! You gained a level. Current level:" + me.getStat(sdk.stats.Level), sdk.colors.D2Bot.Green); + } + } + } }; diff --git a/d2bs/kolbot/libs/core/GameData/AreaData.js b/d2bs/kolbot/libs/core/GameData/AreaData.js index 15f5db50c..230b78d98 100644 --- a/d2bs/kolbot/libs/core/GameData/AreaData.js +++ b/d2bs/kolbot/libs/core/GameData/AreaData.js @@ -5,202 +5,244 @@ * */ (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/d2bs/kolbot/libs/core/GameData/GameData.js b/d2bs/kolbot/libs/core/GameData/GameData.js index 10f3eae75..034b04361 100644 --- a/d2bs/kolbot/libs/core/GameData/GameData.js +++ b/d2bs/kolbot/libs/core/GameData/GameData.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename GameData.js * @author Nishimura-Katsuo @@ -7,162 +8,162 @@ (function (module, require) { - const MonsterData = require("./MonsterData"); - const AreaData = require("./AreaData"); - - const GameData = { - townAreas: [0, 1, 40, 75, 103, 109], - monsterLevel: function (monsterID, areaID) { - if (me.diff) { // levels on nm/hell are determined by area, not by monster data - return AreaData[areaID].Level; - } - - return MonsterData[monsterID].Level; - }, - monsterExp: function (monsterID, areaID) { - return Experience.monsterExp[this.monsterLevel(monsterID, areaID)][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; - }, - areaLevel: function (areaID) { - let levels = 0, total = 0; - - if (me.diff) { // levels on nm/hell are determined by area, not by monster data - return AreaData[areaID].Level; - } - - AreaData[areaID].Monsters.forEach(mon => { - levels += MonsterData[mon].Level * MonsterData[mon].Rarity; - total += MonsterData[mon].Rarity; - }); - - return Math.round(levels / total); - }, - areaImmunities: function (areaID) { - let resists = {Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0}; - - function checkmon (monID) { - for (let k in resists) { - resists[k] = Math.max(resists[k], MonsterData[monID][k]); - } - } - - AreaData[areaID].Monsters.forEach(mon => { - checkmon(mon); - MonsterData[mon].Minions.forEach(checkmon); - }); - - 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(me); - - if (!party) { - return 1; - } - - count = 1; - - while (party.getNext()) { - count++; - } - } - - return (count + 1) / 2; - }, - partyModifier: function (playerID) { - let party = getParty(me), partyid = -1, level = 0, total = 0; - - if (!party) { - return 1; - } - - 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(me), partyid = -1, level = 0, total = 0, gamesize = 0; - - if (!party) { - return 0; - } - - 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); - }, - areaPartyExp: function (areaID, exclude = null, onlytown = true, ignore = null) { // amount of total party exp gained per kill on average - let party = getParty(me), partyid = -1, partylevels = 0, gamesize = 0, exp = 0, playerexp = 0, poolsize = 0; - - if (!party) { - return 0; - } - - // very rough approximation of unique population ratio, could be approved but this works well enough - let uniqueratio = parseFloat(Config.ChampionBias) * (AreaData[areaID].ChampionPacks.Min + AreaData[areaID].ChampionPacks.Max + AreaData[areaID].Super * 2) / (AreaData[areaID].Size.x * AreaData[areaID].Size.y); - - partyid = party.partyid; - - do { - gamesize++; - - if (party.partyid === partyid && party.name !== exclude && party.gid !== exclude && (!onlytown || this.townAreas.indexOf(party.area) > -1) && (areaID < 128 || party.level >= (1 + me.diff) * 20)) { - partylevels += party.level; - - if (party.name !== ignore && party.gid !== ignore) { - poolsize = 0; - playerexp = 0; - - AreaData[areaID].Monsters.forEach(mon => { - if (MonsterData[mon].Rarity > 0) { - playerexp += ((1 - uniqueratio) + (3 * uniqueratio)) * this.monsterExp(mon, areaID) * this.levelModifier(party.level, this.monsterLevel(mon, areaID)) * MonsterData[mon].Rarity; - poolsize += MonsterData[mon].Rarity; - } - }); - - if (poolsize) { - exp += party.level * playerexp / poolsize; - } - } - } - } while (party.getNext()); + const MonsterData = require("./MonsterData"); + const AreaData = require("./AreaData"); + + const GameData = { + townAreas: [0, 1, 40, 75, 103, 109], + monsterLevel: function (monsterID, areaID) { + if (me.diff) { // levels on nm/hell are determined by area, not by monster data + return AreaData[areaID].Level; + } + + return MonsterData[monsterID].Level; + }, + monsterExp: function (monsterID, areaID) { + return Experience.monsterExp[this.monsterLevel(monsterID, areaID)][me.diff] * MonsterData[monsterID].ExperienceModifier / 100; + }, + areaLevel: function (areaID) { + let levels = 0, total = 0; + + if (me.diff) { // levels on nm/hell are determined by area, not by monster data + return AreaData[areaID].Level; + } + + AreaData[areaID].Monsters.forEach(mon => { + levels += MonsterData[mon].Level * MonsterData[mon].Rarity; + total += MonsterData[mon].Rarity; + }); + + return Math.round(levels / total); + }, + areaImmunities: function (areaID) { + let resists = { Physical: 0, Magic: 0, Fire: 0, Lightning: 0, Cold: 0, Poison: 0 }; + + function checkmon (monID) { + for (let k in resists) { + resists[k] = Math.max(resists[k], MonsterData[monID][k]); + } + } + + AreaData[areaID].Monsters.forEach(mon => { + checkmon(mon); + MonsterData[mon].Minions.forEach(checkmon); + }); + + 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(me); + + if (!party) { + return 1; + } + + count = 1; + + while (party.getNext()) { + count++; + } + } + + return (count + 1) / 2; + }, + partyModifier: function (playerID) { + let party = getParty(me), partyid = -1, level = 0, total = 0; + + if (!party) { + return 1; + } + + 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(me), partyid = -1, level = 0, total = 0, gamesize = 0; + + if (!party) { + return 0; + } + + 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); + }, + areaPartyExp: function (areaID, exclude = null, onlytown = true, ignore = null) { // amount of total party exp gained per kill on average + let party = getParty(me), partyid = -1, partylevels = 0, gamesize = 0, exp = 0, playerexp = 0, poolsize = 0; + + if (!party) { + return 0; + } + + // very rough approximation of unique population ratio, could be approved but this works well enough + let uniqueratio = parseFloat(Config.ChampionBias) * (AreaData[areaID].ChampionPacks.Min + AreaData[areaID].ChampionPacks.Max + AreaData[areaID].Super * 2) / (AreaData[areaID].Size.x * AreaData[areaID].Size.y); + + partyid = party.partyid; + + do { + gamesize++; + + if (party.partyid === partyid && party.name !== exclude && party.gid !== exclude && (!onlytown || this.townAreas.indexOf(party.area) > -1) && (areaID < 128 || party.level >= (1 + me.diff) * 20)) { + partylevels += party.level; + + if (party.name !== ignore && party.gid !== ignore) { + poolsize = 0; + playerexp = 0; + + AreaData[areaID].Monsters.forEach(mon => { + if (MonsterData[mon].Rarity > 0) { + playerexp += ((1 - uniqueratio) + (3 * uniqueratio)) * this.monsterExp(mon, areaID) * this.levelModifier(party.level, this.monsterLevel(mon, areaID)) * MonsterData[mon].Rarity; + poolsize += MonsterData[mon].Rarity; + } + }); + + if (poolsize) { + exp += party.level * playerexp / poolsize; + } + } + } + } while (party.getNext()); - return (partylevels ? exp * this.multiplayerModifier(gamesize) / partylevels : 0); - } - }; + return (partylevels ? exp * this.multiplayerModifier(gamesize) / partylevels : 0); + } + }; - module.exports = GameData; + module.exports = GameData; })(module, require); diff --git a/d2bs/kolbot/libs/core/GameData/LocaleStringID.js b/d2bs/kolbot/libs/core/GameData/LocaleStringID.js index 133617d53..96332bc3e 100644 --- a/d2bs/kolbot/libs/core/GameData/LocaleStringID.js +++ b/d2bs/kolbot/libs/core/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/d2bs/kolbot/libs/core/GameData/MonsterData.js b/d2bs/kolbot/libs/core/GameData/MonsterData.js index 337941fba..73ef80dd8 100644 --- a/d2bs/kolbot/libs/core/GameData/MonsterData.js +++ b/d2bs/kolbot/libs/core/GameData/MonsterData.js @@ -6,9 +6,9 @@ */ (function (module, require) { - const LocaleStringName = require("./LocaleStringID").LocaleStringName; - const MONSTER_INDEX_COUNT = 770; - /** + const LocaleStringName = require("./LocaleStringID").LocaleStringName; + const MONSTER_INDEX_COUNT = 770; + /** * @typedef MonsterDataObj * @type {object} * @property {number} Index = Index of this monster @@ -45,72 +45,76 @@ * @property {number} MinionCount.Max = maximum number of minions that can spawn with this unit */ - /** @type {MonsterDataObj[]} */ - const MonsterData = Array(MONSTER_INDEX_COUNT); + /** @type {MonsterDataObj[]} */ + const MonsterData = Array(MONSTER_INDEX_COUNT); - for (let i = 0; i < MonsterData.length; i++) { - let index = i; + 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[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/d2bs/kolbot/libs/core/GameData/NTItemAlias.js b/d2bs/kolbot/libs/core/GameData/NTItemAlias.js index bbfbaaac2..1b47e4f05 100644 --- a/d2bs/kolbot/libs/core/GameData/NTItemAlias.js +++ b/d2bs/kolbot/libs/core/GameData/NTItemAlias.js @@ -1274,27 +1274,48 @@ NTIPAliasStat["unused186"] = 186; NTIPAliasStat["unused187"] = 187; NTIPAliasStat["itemaddskilltab"] = 188; -NTIPAliasStat["itemaddbowandcrossbowskilltab"] = [188, 0]; NTIPAliasStat["bowandcrossbowskilltab"] = [188, 0]; -NTIPAliasStat["itemaddpassiveandmagicskilltab"] = [188, 1]; NTIPAliasStat["passiveandmagicskilltab"] = [188, 1]; -NTIPAliasStat["itemaddjavelinandspearskilltab"] = [188, 2]; NTIPAliasStat["javelinandspearskilltab"] = [188, 2]; -NTIPAliasStat["itemaddfireskilltab"] = [188, 8]; NTIPAliasStat["fireskilltab"] = [188, 8]; -NTIPAliasStat["itemaddlightningskilltab"] = [188, 9]; NTIPAliasStat["lightningskilltab"] = [188, 9]; -NTIPAliasStat["itemaddcoldskilltab"] = [188, 10]; NTIPAliasStat["coldskilltab"] = [188, 10]; -NTIPAliasStat["itemaddcursesskilltab"] = [188, 16]; NTIPAliasStat["cursesskilltab"] = [188, 16]; -NTIPAliasStat["itemaddpoisonandboneskilltab"] = [188, 17]; NTIPAliasStat["poisonandboneskilltab"] = [188, 17]; -NTIPAliasStat["itemaddnecromancersummoningskilltab"] = [188, 18]; NTIPAliasStat["necromancersummoningskilltab"] = [188, 18]; -NTIPAliasStat["itemaddpalicombatskilltab"] = [188, 24]; NTIPAliasStat["palicombatskilltab"] = [188, 24]; -NTIPAliasStat["itemaddoffensiveaurasskilltab"] = [188, 25]; NTIPAliasStat["offensiveaurasskilltab"] = [188, 25]; -NTIPAliasStat["itemadddefensiveaurasskilltab"] = [188, 26]; NTIPAliasStat["defensiveaurasskilltab"] = [188, 26]; -NTIPAliasStat["itemaddbarbcombatskilltab"] = [188, 32]; NTIPAliasStat["barbcombatskilltab"] = [188, 32]; -NTIPAliasStat["itemaddmasteriesskilltab"] = [188, 33]; NTIPAliasStat["masteriesskilltab"] = [188, 33]; -NTIPAliasStat["itemaddwarcriesskilltab"] = [188, 34]; NTIPAliasStat["warcriesskilltab"] = [188, 34]; -NTIPAliasStat["itemadddruidsummoningskilltab"] = [188, 40]; NTIPAliasStat["druidsummoningskilltab"] = [188, 40]; -NTIPAliasStat["itemaddshapeshiftingskilltab"] = [188, 41]; NTIPAliasStat["shapeshiftingskilltab"] = [188, 41]; -NTIPAliasStat["itemaddelementalskilltab"] = [188, 42]; NTIPAliasStat["elementalskilltab"] = [188, 42]; -NTIPAliasStat["itemaddtrapsskilltab"] = [188, 48]; NTIPAliasStat["trapsskilltab"] = [188, 48]; -NTIPAliasStat["itemaddshadowdisciplinesskilltab"] = [188, 49]; NTIPAliasStat["shadowdisciplinesskilltab"] = [188, 49]; -NTIPAliasStat["itemaddmartialartsskilltab"] = [188, 50]; NTIPAliasStat["martialartsskilltab"] = [188, 50]; +NTIPAliasStat["itemaddbowandcrossbowskilltab"] = [188, 0]; +NTIPAliasStat["bowandcrossbowskilltab"] = [188, 0]; +NTIPAliasStat["itemaddpassiveandmagicskilltab"] = [188, 1]; +NTIPAliasStat["passiveandmagicskilltab"] = [188, 1]; +NTIPAliasStat["itemaddjavelinandspearskilltab"] = [188, 2]; +NTIPAliasStat["javelinandspearskilltab"] = [188, 2]; +NTIPAliasStat["itemaddfireskilltab"] = [188, 8]; +NTIPAliasStat["fireskilltab"] = [188, 8]; +NTIPAliasStat["itemaddlightningskilltab"] = [188, 9]; +NTIPAliasStat["lightningskilltab"] = [188, 9]; +NTIPAliasStat["itemaddcoldskilltab"] = [188, 10]; +NTIPAliasStat["coldskilltab"] = [188, 10]; +NTIPAliasStat["itemaddcursesskilltab"] = [188, 16]; +NTIPAliasStat["cursesskilltab"] = [188, 16]; +NTIPAliasStat["itemaddpoisonandboneskilltab"] = [188, 17]; +NTIPAliasStat["poisonandboneskilltab"] = [188, 17]; +NTIPAliasStat["itemaddnecromancersummoningskilltab"] = [188, 18]; +NTIPAliasStat["necromancersummoningskilltab"] = [188, 18]; +NTIPAliasStat["itemaddpalicombatskilltab"] = [188, 24]; +NTIPAliasStat["palicombatskilltab"] = [188, 24]; +NTIPAliasStat["itemaddoffensiveaurasskilltab"] = [188, 25]; +NTIPAliasStat["offensiveaurasskilltab"] = [188, 25]; +NTIPAliasStat["itemadddefensiveaurasskilltab"] = [188, 26]; +NTIPAliasStat["defensiveaurasskilltab"] = [188, 26]; +NTIPAliasStat["itemaddbarbcombatskilltab"] = [188, 32]; +NTIPAliasStat["barbcombatskilltab"] = [188, 32]; +NTIPAliasStat["itemaddmasteriesskilltab"] = [188, 33]; +NTIPAliasStat["masteriesskilltab"] = [188, 33]; +NTIPAliasStat["itemaddwarcriesskilltab"] = [188, 34]; +NTIPAliasStat["warcriesskilltab"] = [188, 34]; +NTIPAliasStat["itemadddruidsummoningskilltab"] = [188, 40]; +NTIPAliasStat["druidsummoningskilltab"] = [188, 40]; +NTIPAliasStat["itemaddshapeshiftingskilltab"] = [188, 41]; +NTIPAliasStat["shapeshiftingskilltab"] = [188, 41]; +NTIPAliasStat["itemaddelementalskilltab"] = [188, 42]; +NTIPAliasStat["elementalskilltab"] = [188, 42]; +NTIPAliasStat["itemaddtrapsskilltab"] = [188, 48]; +NTIPAliasStat["trapsskilltab"] = [188, 48]; +NTIPAliasStat["itemaddshadowdisciplinesskilltab"] = [188, 49]; +NTIPAliasStat["shadowdisciplinesskilltab"] = [188, 49]; +NTIPAliasStat["itemaddmartialartsskilltab"] = [188, 50]; +NTIPAliasStat["martialartsskilltab"] = [188, 50]; NTIPAliasStat["unused189"] = 189; NTIPAliasStat["unused190"] = 190; diff --git a/d2bs/kolbot/libs/core/GameData/QuestData.js b/d2bs/kolbot/libs/core/GameData/QuestData.js index a0a0586b1..6bfbdbaf4 100644 --- a/d2bs/kolbot/libs/core/GameData/QuestData.js +++ b/d2bs/kolbot/libs/core/GameData/QuestData.js @@ -6,164 +6,174 @@ */ (function (module) { - /** + /** * @todo Fill out more, items for quests, npcs, etc */ - const QuestData = (function () { - let _lastRefresh = 0; - - /** @type {Set} */ - const _specials = new Set(); - [ - sdk.quest.id.SpokeToWarriv, sdk.quest.id.AbleToGotoActII, sdk.quest.id.SpokeToJerhyn, sdk.quest.id.AbleToGotoActIII, - sdk.quest.id.SpokeToHratli, sdk.quest.id.AbleToGotoActIV, sdk.quest.id.SpokeToTyrael, sdk.quest.id.AbleToGotoActV, - ].forEach(questId => _specials.add(questId)); - - const refresh = function () { - if (getTickCount() - _lastRefresh > 500) { - Packet.questRefresh(); - _lastRefresh = getTickCount(); - } - }; + const QuestData = (function () { + let _lastRefresh = 0; + + /** @type {Set} */ + const _specials = new Set(); + [ + sdk.quest.id.SpokeToWarriv, sdk.quest.id.AbleToGotoActII, + sdk.quest.id.SpokeToJerhyn, sdk.quest.id.AbleToGotoActIII, + sdk.quest.id.SpokeToHratli, sdk.quest.id.AbleToGotoActIV, + sdk.quest.id.SpokeToTyrael, sdk.quest.id.AbleToGotoActV, + ].forEach(questId => _specials.add(questId)); + + const refresh = function () { + if (getTickCount() - _lastRefresh > 500) { + Packet.questRefresh(); + _lastRefresh = getTickCount(); + } + }; - /** + /** * @constructor * @param {number} questId * @param {number} act */ - function Quest (questId, act) { - this.id = questId; - this.act = act; - this.states = new Array(16).fill(0); // todo figure a method to ensure the length is immutable - this.completed = false; - this.reqComplete = false; - this.cannotComplete = false; - } - - Quest.prototype.complete = function (reqCheck = false) { - if (this.completed) return true; - if (this.cannotComplete) return false; - if (reqCheck && this.reqComplete) return true; - refresh(); - - let completedStatus = me.getQuest(this.id, sdk.quest.states.Completed); - if (completedStatus) { - this.completed = true; - return true; - } - - let cannotCompleteStatus = me.getQuest(this.id, sdk.quest.states.CannotComplete); - if (cannotCompleteStatus) { - this.cannotComplete = true; - return false; - } - - if (reqCheck) { - let reqCompleteStatus = me.getQuest(this.id, sdk.quest.states.ReqComplete); - if (reqCompleteStatus) { - this.reqComplete = true; - return true; - } - } - - return false; - }; - - /** + function Quest (questId, act) { + this.id = questId; + this.act = act; + this.states = new Array(16).fill(0); // todo figure a method to ensure the length is immutable + this.completed = false; + this.reqComplete = false; + this.cannotComplete = false; + } + + Quest.prototype.complete = function (reqCheck = false) { + if (this.completed) return true; + if (this.cannotComplete) return false; + if (reqCheck && this.reqComplete) return true; + refresh(); + + let completedStatus = me.getQuest(this.id, sdk.quest.states.Completed); + if (completedStatus) { + this.completed = true; + return true; + } + + let cannotCompleteStatus = me.getQuest(this.id, sdk.quest.states.CannotComplete); + if (cannotCompleteStatus) { + this.cannotComplete = true; + return false; + } + + if (reqCheck) { + let reqCompleteStatus = me.getQuest(this.id, sdk.quest.states.ReqComplete); + if (reqCompleteStatus) { + this.reqComplete = true; + return true; + } + } + + return false; + }; + + /** * @param {number} state - quest state (0 - 15) * @param {boolean} complete - if true, will check if state bit is 1 (active) otherwise 0 (inactive) * @returns {boolean} */ - Quest.prototype.checkState = function (state, complete = true) { - // handle the ones we already know - if (state === sdk.quest.states.Completed && this.completed) return complete; - if (state === sdk.quest.states.CannotComplete && this.cannotComplete) return complete; - if (state === sdk.quest.states.ReqComplete && this.reqComplete) return complete; - - refresh(); - let val = me.getQuest(this.id, state); - this.states[state] = val; - return complete ? val === 1 : val === 0; - }; - - Quest.prototype.getStates = function () { - refresh(); - - // the non-visible quests can stop after 1 - let max = _specials.has(this.id) ? 1 : 16; - - for (let state = 0; state < max; state++) { - this.states[state] = me.getQuest(this.id, state); - delay(10); - } - - this.completed = this.states[sdk.quest.states.Completed] === 1; - this.cannotComplete = this.states[sdk.quest.states.CannotComplete] === 1; - this.reqComplete = this.states[sdk.quest.states.ReqComplete] === 1; - - return this.states; - }; - - /** @type {Map} */ - const questMap = new Map(); - [ - [ - sdk.quest.id.SpokeToWarriv, sdk.quest.id.DenofEvil, sdk.quest.id.SistersBurialGrounds, sdk.quest.id.ToolsoftheTrade, - sdk.quest.id.TheSearchForCain, sdk.quest.id.ForgottenTower, sdk.quest.id.SistersToTheSlaughter, sdk.quest.id.Respec - ], - [ - sdk.quest.id.AbleToGotoActII, sdk.quest.id.SpokeToJerhyn, sdk.quest.id.RadamentsLair, sdk.quest.id.TheHoradricStaff, - sdk.quest.id.TheTaintedSun, sdk.quest.id.TheSummoner, sdk.quest.id.TheArcaneSanctuary, sdk.quest.id.TheSevenTombs - ], - [ - sdk.quest.id.AbleToGotoActIII, sdk.quest.id.SpokeToHratli, sdk.quest.id.TheGoldenBird, sdk.quest.id.BladeoftheOldReligion, - sdk.quest.id.LamEsensTome, sdk.quest.id.KhalimsWill, sdk.quest.id.TheBlackenedTemple, sdk.quest.id.TheGuardian, - ], - [ - sdk.quest.id.AbleToGotoActIV, sdk.quest.id.TheFallenAngel, sdk.quest.id.SpokeToTyrael, sdk.quest.id.HellsForge, sdk.quest.id.TerrorsEnd, - ], - [ - sdk.quest.id.AbleToGotoActV, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.RescueonMountArreat, sdk.quest.id.PrisonofIce, - sdk.quest.id.BetrayalofHarrogath, sdk.quest.id.RiteofPassage, sdk.quest.id.EyeofDestruction, - ] - ].forEach((questIds, act) => { - for (let questId of questIds) { - questMap.set(questId, new Quest(questId, act + 1)); - } - }); + Quest.prototype.checkState = function (state, complete = true) { + // handle the ones we already know + if (state === sdk.quest.states.Completed && this.completed) return complete; + if (state === sdk.quest.states.CannotComplete && this.cannotComplete) return complete; + if (state === sdk.quest.states.ReqComplete && this.reqComplete) return complete; + + refresh(); + let val = me.getQuest(this.id, state); + this.states[state] = val; + return complete ? val === 1 : val === 0; + }; + + Quest.prototype.getStates = function () { + refresh(); + + // the non-visible quests can stop after 1 + let max = _specials.has(this.id) ? 1 : 16; + + for (let state = 0; state < max; state++) { + this.states[state] = me.getQuest(this.id, state); + delay(10); + } + + this.completed = this.states[sdk.quest.states.Completed] === 1; + this.cannotComplete = this.states[sdk.quest.states.CannotComplete] === 1; + this.reqComplete = this.states[sdk.quest.states.ReqComplete] === 1; + + return this.states; + }; + + /** @type {Map} */ + const questMap = new Map(); + [ + [ + sdk.quest.id.SpokeToWarriv, sdk.quest.id.DenofEvil, + sdk.quest.id.SistersBurialGrounds, sdk.quest.id.ToolsoftheTrade, + sdk.quest.id.TheSearchForCain, sdk.quest.id.ForgottenTower, + sdk.quest.id.SistersToTheSlaughter, sdk.quest.id.Respec + ], + [ + sdk.quest.id.AbleToGotoActII, sdk.quest.id.SpokeToJerhyn, + sdk.quest.id.RadamentsLair, sdk.quest.id.TheHoradricStaff, + sdk.quest.id.TheTaintedSun, sdk.quest.id.TheSummoner, + sdk.quest.id.TheArcaneSanctuary, sdk.quest.id.TheSevenTombs + ], + [ + sdk.quest.id.AbleToGotoActIII, sdk.quest.id.SpokeToHratli, + sdk.quest.id.TheGoldenBird, sdk.quest.id.BladeoftheOldReligion, + sdk.quest.id.LamEsensTome, sdk.quest.id.KhalimsWill, + sdk.quest.id.TheBlackenedTemple, sdk.quest.id.TheGuardian, + ], + [ + sdk.quest.id.AbleToGotoActIV, sdk.quest.id.TheFallenAngel, + sdk.quest.id.SpokeToTyrael, sdk.quest.id.HellsForge, sdk.quest.id.TerrorsEnd, + ], + [ + sdk.quest.id.AbleToGotoActV, sdk.quest.id.SiegeOnHarrogath, + sdk.quest.id.RescueonMountArreat, sdk.quest.id.PrisonofIce, + sdk.quest.id.BetrayalofHarrogath, sdk.quest.id.RiteofPassage, sdk.quest.id.EyeofDestruction, + ] + ].forEach((questIds, act) => { + for (let questId of questIds) { + questMap.set(questId, new Quest(questId, act + 1)); + } + }); - return { - /** + return { + /** * @param {number} questId * @returns {Quest | undefined} */ - get: function (questId) { - return questMap.get(questId); - }, + get: function (questId) { + return questMap.get(questId); + }, - /** + /** * @param {number} questId * @returns {boolean} */ - has: function (questId) { - return questMap.has(questId); - }, + has: function (questId) { + return questMap.has(questId); + }, - init: function () { - console.time("QuestData.init"); - questMap.forEach(quest => quest.getStates()); - console.timeEnd("QuestData.init"); - }, + init: function () { + console.time("QuestData.init"); + questMap.forEach(quest => quest.getStates()); + console.timeEnd("QuestData.init"); + }, - /** + /** * @param {number} questId * @returns {number} */ - getActForQuest: function (questId) { - return questMap.get(questId).act; - }, - }; - })(); + getActForQuest: function (questId) { + return questMap.get(questId).act; + }, + }; + })(); - module.exports = QuestData; + module.exports = QuestData; })(module); diff --git a/d2bs/kolbot/libs/core/GameData/RuneData.js b/d2bs/kolbot/libs/core/GameData/RuneData.js index fefd11105..824bc56a1 100644 --- a/d2bs/kolbot/libs/core/GameData/RuneData.js +++ b/d2bs/kolbot/libs/core/GameData/RuneData.js @@ -1,102 +1,102 @@ (function (module) { - const RunesData = (function () { - /** @type {Array} runewords - Array of runeword objects. */ - const runewords = []; - const ladder = me.ladder > 0; - const RUNES_COUNT = 169; + const RunesData = (function () { + /** @type {Array} runewords - Array of runeword objects. */ + const runewords = []; + const ladder = me.ladder > 0; + const RUNES_COUNT = 169; - const validRunes = Object.values(sdk.items.runes).filter(v => !isNaN(v)); - const validInsertable = (id) => { - if (validRunes.includes(id)) return true; - if (id === sdk.items.Jewel) return true; - return id >= sdk.items.gems.Chipped.Amethyst && id <= sdk.items.gems.Perfect.Skull; - }; - const anyShield = [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads]; - const missileWeapon = [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow]; - const meleeWeapons = [ - sdk.items.type.Scepter, sdk.items.type.Wand, sdk.items.type.AmazonSpear, - sdk.items.type.Axe, sdk.items.type.Hammer, sdk.items.type.Mace, - sdk.items.type.Sword, sdk.items.type.Knife, sdk.items.type.AssassinClaw, - sdk.items.type.Polearm, sdk.items.type.Scepter, sdk.items.type.HandtoHand - ]; - const ladderRws = [ - "Brand", "Death", "Destruction", "Dragon", "Edge", "Fortitude", "Grief", - "Ice", "Infinity", "Insight", "LastWish", "Lawbringer", "Oath", "Obedience", - "Phoenix", "Pride", "Rift", "Spirit", "VoiceofReason", "White", - ]; + const validRunes = Object.values(sdk.items.runes).filter(v => !isNaN(v)); + const validInsertable = (id) => { + if (validRunes.includes(id)) return true; + if (id === sdk.items.Jewel) return true; + return id >= sdk.items.gems.Chipped.Amethyst && id <= sdk.items.gems.Perfect.Skull; + }; + const anyShield = [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads]; + const missileWeapon = [sdk.items.type.Bow, sdk.items.type.Crossbow, sdk.items.type.AmazonBow]; + const meleeWeapons = [ + sdk.items.type.Scepter, sdk.items.type.Wand, sdk.items.type.AmazonSpear, + sdk.items.type.Axe, sdk.items.type.Hammer, sdk.items.type.Mace, + sdk.items.type.Sword, sdk.items.type.Knife, sdk.items.type.AssassinClaw, + sdk.items.type.Polearm, sdk.items.type.Scepter, sdk.items.type.HandtoHand + ]; + const ladderRws = [ + "Brand", "Death", "Destruction", "Dragon", "Edge", "Fortitude", "Grief", + "Ice", "Infinity", "Insight", "LastWish", "Lawbringer", "Oath", "Obedience", + "Phoenix", "Pride", "Rift", "Spirit", "VoiceofReason", "White", + ]; - function getItemType (iType) { - switch (iType) { - case sdk.items.type.AnyShield: - return anyShield; - case sdk.items.type.Weapon: - return [].concat(missileWeapon, meleeWeapons); - case sdk.items.type.MissileWeapon: - return missileWeapon; - case sdk.items.type.MeleeWeapon: - return meleeWeapons; - case sdk.items.type.Helm: - return [sdk.items.type.Helm, sdk.items.type.Circlet, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; - default: - return [iType]; - } - } + function getItemType (iType) { + switch (iType) { + case sdk.items.type.AnyShield: + return anyShield; + case sdk.items.type.Weapon: + return [].concat(missileWeapon, meleeWeapons); + case sdk.items.type.MissileWeapon: + return missileWeapon; + case sdk.items.type.MeleeWeapon: + return meleeWeapons; + case sdk.items.type.Helm: + return [sdk.items.type.Helm, sdk.items.type.Circlet, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; + default: + return [iType]; + } + } - /** + /** * @constructor * @param {string} name - The name of the recipe. * @param {number} sockets - The number of sockets required for the recipe. * @param {number[]} runes - Array of insertable IDs required for the recipe. * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. */ - function RunewordObj (name, sockets, runes, itemTypes) { - this.name = name; - this.sockets = sockets; - this.runes = runes; - this.itemTypes = itemTypes; - this._ladder = ladderRws.includes(name); - let highestItem = runes.slice().sort((a, b) => b - a).first(); - let reqLvl = getBaseStat("items", highestItem, "levelreq"); - this.reqLvl = reqLvl > 0 ? reqLvl : 1; - } + function RunewordObj (name, sockets, runes, itemTypes) { + this.name = name; + this.sockets = sockets; + this.runes = runes; + this.itemTypes = itemTypes; + this._ladder = ladderRws.includes(name); + let highestItem = runes.toSorted((a, b) => b - a).first(); + let reqLvl = getBaseStat("items", highestItem, "levelreq"); + this.reqLvl = reqLvl > 0 ? reqLvl : 1; + } - RunewordObj.prototype.ladderRestricted = function () { - // not ladder restricted or we are on ladder - if (!this._ladder || ladder) return false; - // ladder restricted and we have enabled ladder override - if (Config.LadderOveride) return false; - // ladder restricted - return true; - }; + RunewordObj.prototype.ladderRestricted = function () { + // not ladder restricted or we are on ladder + if (!this._ladder || ladder) return false; + // ladder restricted and we have enabled ladder override + if (Config.LadderOveride) return false; + // ladder restricted + return true; + }; - /** + /** * Finds a runeword by name. * @param {string} name - The name of the runeword. * @returns {Runeword} - The runeword object. */ - const findByName = function (name) { - return runewords.find(r => String.isEqual(r.name, name)); - }; + const findByName = function (name) { + return runewords.find(r => String.isEqual(r.name, name)); + }; - /** + /** * Find all runewords that have the given rune. * @param {number} rune - classid of rune * @returns {Array} */ - const findByRune = function (rune) { - return runewords.filter(r => r.runes.includes(rune)); - }; + const findByRune = function (rune) { + return runewords.filter(r => r.runes.includes(rune)); + }; - /** + /** * Find all runewords that can be applied to the given item type. * @param {number} type - item type * @returns {Array} */ - const findByType = function (type) { - return runewords.filter(r => r.itemTypes.includes(type)); - }; + const findByType = function (type) { + return runewords.filter(r => r.itemTypes.includes(type)); + }; - /** + /** * Create a new non standard runeword. * @param {string} name - The name of the recipe. * @param {number} sockets - The number of sockets required for the recipe. @@ -104,175 +104,175 @@ * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. * @returns {runeword} - The new runeword object. */ - const addRuneword = function (name, sockets, runes, itemTypes) { - if (!name || !sockets || !runes || !itemTypes) return false; - !Array.isArray(runes) && (runes = [runes]); - if (!runes.every(validInsertable)) return false; - !Array.isArray(itemTypes) && (itemTypes = [itemTypes]); + const addRuneword = function (name, sockets, runes, itemTypes) { + if (!name || !sockets || !runes || !itemTypes) return false; + !Array.isArray(runes) && (runes = [runes]); + if (!runes.every(validInsertable)) return false; + !Array.isArray(itemTypes) && (itemTypes = [itemTypes]); - let rw = new RunewordObj(name, runes.length, runes, itemTypes.map(getItemType).flat()); - runewords.push(rw); + let rw = new RunewordObj(name, runes.length, runes, itemTypes.map(getItemType).flat()); + runewords.push(rw); - return rw; - }; + return rw; + }; - for (let i = 0; i < RUNES_COUNT; i++) { - const index = i; - if (!getBaseStat("runes", index, "complete")) continue; + for (let i = 0; i < RUNES_COUNT; i++) { + const index = i; + if (!getBaseStat("runes", index, "complete")) continue; - const runes = []; + const runes = []; - for (let r = 1; r < 7; r++) { - const rune = getBaseStat("runes", index, "rune" + r); - if (rune > -1 && validRunes.includes(rune)) { - runes.push(rune); - } else { - break; - } - } + for (let r = 1; r < 7; r++) { + const rune = getBaseStat("runes", index, "rune" + r); + if (rune > -1 && validRunes.includes(rune)) { + runes.push(rune); + } else { + break; + } + } - const itemTypes = [ - getBaseStat("runes", index, "itype1"), - getBaseStat("runes", index, "itype2"), - getBaseStat("runes", index, "itype3"), - getBaseStat("runes", index, "itype4"), - getBaseStat("runes", index, "itype5"), - getBaseStat("runes", index, "itype6"), - ].filter(el => el && el !== 65535).map(getItemType).flat(); + const itemTypes = [ + getBaseStat("runes", index, "itype1"), + getBaseStat("runes", index, "itype2"), + getBaseStat("runes", index, "itype3"), + getBaseStat("runes", index, "itype4"), + getBaseStat("runes", index, "itype5"), + getBaseStat("runes", index, "itype6"), + ].filter(el => el && el !== 65535).map(getItemType).flat(); - const name = (() => { - let temp = getBaseStat("runes", index, "rune name"); + const name = (() => { + let temp = getBaseStat("runes", index, "rune name"); - switch (temp) { - case "The Beast": - return "Beast"; - case "Bound by Duty": - return "ChainsofHonor"; - case "Doomsayer": - return "Doom"; - case "Exile's Path": - return "Exile"; - case "Widowmaker": - return "Grief"; - case "Winter": - return "VoiceofReason"; - default: - return temp.replace(/[^a-zA-Z0-9]/g, ""); - } - })(); + switch (temp) { + case "The Beast": + return "Beast"; + case "Bound by Duty": + return "ChainsofHonor"; + case "Doomsayer": + return "Doom"; + case "Exile's Path": + return "Exile"; + case "Widowmaker": + return "Grief"; + case "Winter": + return "VoiceofReason"; + default: + return temp.replace(/[^a-zA-Z0-9]/g, ""); + } + })(); - runewords.push(new RunewordObj(name, runes.length, runes, itemTypes)); - } + runewords.push(new RunewordObj(name, runes.length, runes, itemTypes)); + } - return { - // runewords: runewords, // other files don't actually need this - // 1.09 - AncientsPledge: findByName("AncientsPledge"), - Black: findByName("Black"), - Fury: findByName("Fury"), - HolyThunder: findByName("HolyThunder"), - Honor: findByName("Honor"), - KingsGrace: findByName("KingsGrace"), - Leaf: findByName("Leaf"), - Lionheart: findByName("Lionheart"), - Lore: findByName("Lore"), - Malice: findByName("Malice"), - Melody: findByName("Melody"), - Memory: findByName("Memory"), - Nadir: findByName("Nadir"), - Radiance: findByName("Radiance"), - Rhyme: findByName("Rhyme"), - Silence: findByName("Silence"), - Smoke: findByName("Smoke"), - Stealth: findByName("Stealth"), - Steel: findByName("Steel"), - Strength: findByName("Strength"), - Venom: findByName("Venom"), - Wealth: findByName("Wealth"), - White: findByName("White"), - Zephyr: findByName("Zephyr"), + return { + // runewords: runewords, // other files don't actually need this + // 1.09 + AncientsPledge: findByName("AncientsPledge"), + Black: findByName("Black"), + Fury: findByName("Fury"), + HolyThunder: findByName("HolyThunder"), + Honor: findByName("Honor"), + KingsGrace: findByName("KingsGrace"), + Leaf: findByName("Leaf"), + Lionheart: findByName("Lionheart"), + Lore: findByName("Lore"), + Malice: findByName("Malice"), + Melody: findByName("Melody"), + Memory: findByName("Memory"), + Nadir: findByName("Nadir"), + Radiance: findByName("Radiance"), + Rhyme: findByName("Rhyme"), + Silence: findByName("Silence"), + Smoke: findByName("Smoke"), + Stealth: findByName("Stealth"), + Steel: findByName("Steel"), + Strength: findByName("Strength"), + Venom: findByName("Venom"), + Wealth: findByName("Wealth"), + White: findByName("White"), + Zephyr: findByName("Zephyr"), - // 1.10 - Beast: findByName("Beast"), - Bramble: findByName("Bramble"), - BreathoftheDying: findByName("BreathoftheDying"), - CallToArms: findByName("CallToArms"), - ChainsofHonor: findByName("ChainsofHonor"), - Chaos: findByName("Chaos"), - CrescentMoon: findByName("CrescentMoon"), - Delirium: findByName("Delirium"), - Doom: findByName("Doom"), - Duress: findByName("Duress"), - Enigma: findByName("Enigma"), - Eternity: findByName("Eternity"), - Exile: findByName("Exile"), - Famine: findByName("Famine"), - Gloom: findByName("Gloom"), - HandofJustice: findByName("HandofJustice"), - HeartoftheOak: findByName("HeartoftheOak"), - Kingslayer: findByName("Kingslayer"), - Passion: findByName("Passion"), - Prudence: findByName("Prudence"), - Sanctuary: findByName("Sanctuary"), - Splendor: findByName("Splendor"), - Stone: findByName("Stone"), - Wind: findByName("Wind"), + // 1.10 + Beast: findByName("Beast"), + Bramble: findByName("Bramble"), + BreathoftheDying: findByName("BreathoftheDying"), + CallToArms: findByName("CallToArms"), + ChainsofHonor: findByName("ChainsofHonor"), + Chaos: findByName("Chaos"), + CrescentMoon: findByName("CrescentMoon"), + Delirium: findByName("Delirium"), + Doom: findByName("Doom"), + Duress: findByName("Duress"), + Enigma: findByName("Enigma"), + Eternity: findByName("Eternity"), + Exile: findByName("Exile"), + Famine: findByName("Famine"), + Gloom: findByName("Gloom"), + HandofJustice: findByName("HandofJustice"), + HeartoftheOak: findByName("HeartoftheOak"), + Kingslayer: findByName("Kingslayer"), + Passion: findByName("Passion"), + Prudence: findByName("Prudence"), + Sanctuary: findByName("Sanctuary"), + Splendor: findByName("Splendor"), + Stone: findByName("Stone"), + Wind: findByName("Wind"), - // ladder only - Brand: findByName("Brand"), - Death: findByName("Death"), - Destruction: findByName("Destruction"), - Dragon: findByName("Dragon"), - Dream: findByName("Dream"), - Edge: findByName("Edge"), - Faith: findByName("Faith"), - Fortitude: findByName("Fortitude"), - Grief: findByName("Grief"), - Harmony: findByName("Harmony"), - Ice: findByName("Ice"), - Infinity: findByName("Infinity"), - Insight: findByName("Insight"), - LastWish: findByName("LastWish"), - Lawbringer: findByName("Lawbringer"), - Oath: findByName("Oath"), - Obedience: findByName("Obedience"), - Phoenix: findByName("Phoenix"), - Pride: findByName("Pride"), - Rift: findByName("Rift"), - Spirit: findByName("Spirit"), - VoiceofReason: findByName("VoiceofReason"), - Wrath: findByName("Wrath"), + // ladder only + Brand: findByName("Brand"), + Death: findByName("Death"), + Destruction: findByName("Destruction"), + Dragon: findByName("Dragon"), + Dream: findByName("Dream"), + Edge: findByName("Edge"), + Faith: findByName("Faith"), + Fortitude: findByName("Fortitude"), + Grief: findByName("Grief"), + Harmony: findByName("Harmony"), + Ice: findByName("Ice"), + Infinity: findByName("Infinity"), + Insight: findByName("Insight"), + LastWish: findByName("LastWish"), + Lawbringer: findByName("Lawbringer"), + Oath: findByName("Oath"), + Obedience: findByName("Obedience"), + Phoenix: findByName("Phoenix"), + Pride: findByName("Pride"), + Rift: findByName("Rift"), + Spirit: findByName("Spirit"), + VoiceofReason: findByName("VoiceofReason"), + Wrath: findByName("Wrath"), - // 1.11 - Bone: findByName("Bone"), - Enlightenment: findByName("Enlightenment"), - Myth: findByName("Myth"), - Peace: findByName("Peace"), - Principle: findByName("Principle"), - Rain: findByName("Rain"), - Treachery: findByName("Treachery"), + // 1.11 + Bone: findByName("Bone"), + Enlightenment: findByName("Enlightenment"), + Myth: findByName("Myth"), + Peace: findByName("Peace"), + Principle: findByName("Principle"), + Rain: findByName("Rain"), + Treachery: findByName("Treachery"), - Test: (() => { - addRuneword("Test", 3, - [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel], - [sdk.items.type.Armor, sdk.items.type.AnyShield, sdk.items.type.Weapon, sdk.items.type.Helm] - ); - return findByName("Test"); - })(), + Test: (() => { + addRuneword("Test", 3, + [sdk.items.runes.Hel, sdk.items.runes.Hel, sdk.items.runes.Hel], + [sdk.items.type.Armor, sdk.items.type.AnyShield, sdk.items.type.Weapon, sdk.items.type.Helm] + ); + return findByName("Test"); + })(), - addRuneword: addRuneword, - findByName: findByName, - findByRune: findByRune, - findByType: findByType, - }; - })(); + addRuneword: addRuneword, + findByName: findByName, + findByRune: findByRune, + findByType: findByType, + }; + })(); - Object.defineProperties(RunesData, { - "addRuneword": { enumerable: false }, - "findByName": { enumerable: false }, - "findByRune": { enumerable: false }, - "findByType": { enumerable: false }, - }); + Object.defineProperties(RunesData, { + "addRuneword": { enumerable: false }, + "findByName": { enumerable: false }, + "findByRune": { enumerable: false }, + "findByType": { enumerable: false }, + }); - module.exports = RunesData; + module.exports = RunesData; })(module); diff --git a/d2bs/kolbot/libs/core/GameData/ShrineData.js b/d2bs/kolbot/libs/core/GameData/ShrineData.js index 39e38ddd2..85397152a 100644 --- a/d2bs/kolbot/libs/core/GameData/ShrineData.js +++ b/d2bs/kolbot/libs/core/GameData/ShrineData.js @@ -6,58 +6,58 @@ */ (function (module) { - const ShrineData = (function () { - function Shrine (state, duration, regen) { - this.state = state || 0; - this.duration = duration || 0; - this.regenTime = Time.minutes(regen) || Infinity; - } - const shrineMap = new Map(); - shrineMap.set(sdk.shrines.Refilling, new Shrine(0, 0, 2)); - shrineMap.set(sdk.shrines.Health, new Shrine(0, 0, 5)); - shrineMap.set(sdk.shrines.Mana, new Shrine(0, 0, 5)); - shrineMap.set(sdk.shrines.HealthExchange, new Shrine()); - shrineMap.set(sdk.shrines.ManaExchange, new Shrine()); - shrineMap.set(sdk.shrines.Armor, new Shrine(sdk.states.ShrineArmor, 2400, 5)); - shrineMap.set(sdk.shrines.Combat, new Shrine(sdk.states.ShrineCombat, 2400, 5)); - shrineMap.set(sdk.shrines.ResistFire, new Shrine(sdk.states.ShrineResFire, 3600, 5)); - shrineMap.set(sdk.shrines.ResistCold, new Shrine(sdk.states.ShrineResCold, 3600, 5)); - shrineMap.set(sdk.shrines.ResistLightning, new Shrine(sdk.states.ShrineResLighting, 3600, 5)); - shrineMap.set(sdk.shrines.ResistPoison, new Shrine(sdk.states.ShrineResPoison, 3600, 5)); - shrineMap.set(sdk.shrines.Skill, new Shrine(sdk.states.ShrineSkill, 2400, 5)); - shrineMap.set(sdk.shrines.ManaRecharge, new Shrine(sdk.states.ShrineManaRegen, 2400, 5)); - shrineMap.set(sdk.shrines.Stamina, new Shrine(sdk.states.ShrineStamina, 4800, 5)); - shrineMap.set(sdk.shrines.Experience, new Shrine(sdk.states.ShrineResCold, 3600)); - shrineMap.set(sdk.shrines.Enirhs, new Shrine()); - shrineMap.set(sdk.shrines.Portal, new Shrine()); - shrineMap.set(sdk.shrines.Gem, new Shrine()); - shrineMap.set(sdk.shrines.Fire, new Shrine()); - shrineMap.set(sdk.shrines.Monster, new Shrine()); - shrineMap.set(sdk.shrines.Exploding, new Shrine()); - shrineMap.set(sdk.shrines.Poison, new Shrine()); + const ShrineData = (function () { + function Shrine (state, duration, regen) { + this.state = state || 0; + this.duration = duration || 0; + this.regenTime = Time.minutes(regen) || Infinity; + } + const shrineMap = new Map(); + shrineMap.set(sdk.shrines.Refilling, new Shrine(0, 0, 2)); + shrineMap.set(sdk.shrines.Health, new Shrine(0, 0, 5)); + shrineMap.set(sdk.shrines.Mana, new Shrine(0, 0, 5)); + shrineMap.set(sdk.shrines.HealthExchange, new Shrine()); + shrineMap.set(sdk.shrines.ManaExchange, new Shrine()); + shrineMap.set(sdk.shrines.Armor, new Shrine(sdk.states.ShrineArmor, 2400, 5)); + shrineMap.set(sdk.shrines.Combat, new Shrine(sdk.states.ShrineCombat, 2400, 5)); + shrineMap.set(sdk.shrines.ResistFire, new Shrine(sdk.states.ShrineResFire, 3600, 5)); + shrineMap.set(sdk.shrines.ResistCold, new Shrine(sdk.states.ShrineResCold, 3600, 5)); + shrineMap.set(sdk.shrines.ResistLightning, new Shrine(sdk.states.ShrineResLighting, 3600, 5)); + shrineMap.set(sdk.shrines.ResistPoison, new Shrine(sdk.states.ShrineResPoison, 3600, 5)); + shrineMap.set(sdk.shrines.Skill, new Shrine(sdk.states.ShrineSkill, 2400, 5)); + shrineMap.set(sdk.shrines.ManaRecharge, new Shrine(sdk.states.ShrineManaRegen, 2400, 5)); + shrineMap.set(sdk.shrines.Stamina, new Shrine(sdk.states.ShrineStamina, 4800, 5)); + shrineMap.set(sdk.shrines.Experience, new Shrine(sdk.states.ShrineResCold, 3600)); + shrineMap.set(sdk.shrines.Enirhs, new Shrine()); + shrineMap.set(sdk.shrines.Portal, new Shrine()); + shrineMap.set(sdk.shrines.Gem, new Shrine()); + shrineMap.set(sdk.shrines.Fire, new Shrine()); + shrineMap.set(sdk.shrines.Monster, new Shrine()); + shrineMap.set(sdk.shrines.Exploding, new Shrine()); + shrineMap.set(sdk.shrines.Poison, new Shrine()); - return { - get: function (shrineType) { - return shrineMap.get(shrineType); - }, + return { + get: function (shrineType) { + return shrineMap.get(shrineType); + }, - has: function (shrineType) { - return shrineMap.has(shrineType); - }, + has: function (shrineType) { + return shrineMap.has(shrineType); + }, - getState: function (shrineType) { - return shrineMap.get(shrineType).state || 0; - }, + getState: function (shrineType) { + return shrineMap.get(shrineType).state || 0; + }, - getDuration: function (shrineType) { - return shrineMap.get(shrineType).duration || 0; - }, + getDuration: function (shrineType) { + return shrineMap.get(shrineType).duration || 0; + }, - getRegenTime: function (shrineType) { - return shrineMap.get(shrineType).regenTime || Infinity; - }, - }; - })(); + getRegenTime: function (shrineType) { + return shrineMap.get(shrineType).regenTime || Infinity; + }, + }; + })(); - module.exports = ShrineData; + module.exports = ShrineData; })(module); diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 3b8ec3cf0..fb60cd1f2 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -7,7 +7,7 @@ (function (module) { - /** + /** * @typedef {Object} SkillInterface * @property {number} hand * @property {boolean} [missile] @@ -17,1461 +17,1467 @@ * @property {() => number} [summonCount] */ - /** @type {Map} */ - const skillMap = new Map(); - // basics - { - skillMap.set(sdk.skills.Attack, { - hand: sdk.skills.hand.LeftNoShift, - range: () => Attack.usingBow() ? 20 : 3, - }); - skillMap.set(sdk.skills.Kick, { - hand: sdk.skills.hand.Right, - range: 3, - }); - skillMap.set(sdk.skills.Throw, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Unsummon, { - hand: sdk.skills.hand.Right, - range: 20, - }); - skillMap.set(sdk.skills.LeftHandThrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.LeftHandSwing, { - hand: sdk.skills.hand.Right, - range: 3, - }); - } - // ~~~ start of amazon skills ~~~ // - { - skillMap.set(sdk.skills.MagicArrow, { - hand: sdk.skills.hand.Left, - missile: true, - }); - skillMap.set(sdk.skills.FireArrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.InnerSight, { - hand: sdk.skills.hand.Right, - range: 13, - state: sdk.states.InnerSight, - condition: () => Config.UseInnerSight, - }); - skillMap.set(sdk.skills.CriticalStrike, { - hand: -1, - range: -1, - state: sdk.states.CriticalStrike, - }); - skillMap.set(sdk.skills.Jab, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.ColdArrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.MultipleShot, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Dodge, { - hand: -1, - range: -1, - state: sdk.states.Dodge, - }); - skillMap.set(sdk.skills.PowerStrike, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.PoisonJavelin, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.ExplodingArrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.SlowMissiles, { - hand: sdk.skills.hand.Right, - range: 13, - state: sdk.states.SlowMissiles, - condition: () => Config.UseSlowMissiles, - }); - skillMap.set(sdk.skills.Avoid, { - hand: -1, - range: -1, - state: sdk.states.Avoid, - }); - skillMap.set(sdk.skills.Impale, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.LightningBolt, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.IceArrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.GuidedArrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Penetrate, { - hand: -1, - range: -1, - state: sdk.states.Penetrate, - }); - skillMap.set(sdk.skills.ChargedStrike, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.PlagueJavelin, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Strafe, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.ImmolationArrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Decoy, { - hand: sdk.skills.hand.Right, - range: 30, - duration: () => ((10 + me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints) * 5)), - condition: () => Config.UseDecoy, - }); - skillMap.set(sdk.skills.Evade, { - hand: -1, - range: -1, - state: sdk.states.Evade, - }); - skillMap.set(sdk.skills.Fend, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.FreezingArrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 30, - AoE: () => 3, - }); - skillMap.set(sdk.skills.Valkyrie, { - hand: sdk.skills.hand.Right, - range: 30, - summonCount: () => 1, - condition: () => Config.UseValkyrie, - }); - skillMap.set(sdk.skills.Pierce, { - hand: -1, - range: -1, - state: sdk.states.Pierce, - }); - skillMap.set(sdk.skills.LightningStrike, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.LightningFury, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - } - // ~~~ start of sorc skills ~~~ // - { - skillMap.set(sdk.skills.FireBolt, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Warmth, { - hand: -1, - range: -1, - state: sdk.states.Warmth, - }); - skillMap.set(sdk.skills.ChargedBolt, { - hand: sdk.skills.hand.Left, - missile: true, - range: 10, - }); - skillMap.set(sdk.skills.IceBolt, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.FrozenArmor, { - hand: sdk.skills.hand.Right, - range: 1, - duration: () => ( - ((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) - + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) - ), - }); - skillMap.set(sdk.skills.Inferno, { - hand: sdk.skills.hand.Left, - missile: true, - range: ((17 + (me.getSkill(sdk.skills.Inferno, sdk.skills.subindex.SoftPoints) * 3) / 4) * 2 / 3), - }); - skillMap.set(sdk.skills.StaticField, { - hand: sdk.skills.hand.Right, - range: () => Math.floor((me.getSkill(sdk.skills.StaticField, sdk.skills.subindex.SoftPoints) + 4) * 2 / 3), - }); - skillMap.set(sdk.skills.Telekinesis, { - hand: sdk.skills.hand.Right, - range: 40, - condition: () => Config.UseTelekinesis, - }); - skillMap.set(sdk.skills.FrostNova, { - hand: sdk.skills.hand.Right, - range: 5, - }); - skillMap.set(sdk.skills.IceBlast, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Blaze, { - hand: sdk.skills.hand.Right, - range: 1, - }); - skillMap.set(sdk.skills.FireBall, { - hand: sdk.skills.hand.Left, - missile: true, - range: (pvpRange) => pvpRange ? 40 : 20, - }); - skillMap.set(sdk.skills.Nova, { - hand: sdk.skills.hand.Right, - range: 7, - }); - skillMap.set(sdk.skills.Lightning, { - hand: sdk.skills.hand.Left, - missile: true, - range: 25, - }); - skillMap.set(sdk.skills.ShiverArmor, { - hand: sdk.skills.hand.Right, - range: 1, - duration: () => ( - ((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) - + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) - ), - }); - skillMap.set(sdk.skills.FireWall, { - hand: sdk.skills.hand.Right, - range: (pvpRange) => pvpRange ? 40 : 30, - }); - skillMap.set(sdk.skills.Enchant, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Enchant, - }); - skillMap.set(sdk.skills.ChainLightning, { - hand: sdk.skills.hand.Left, - missile: true, - range: 25, - }); - skillMap.set(sdk.skills.Teleport, { - hand: sdk.skills.hand.Right, - range: 40, - }); - skillMap.set(sdk.skills.GlacialSpike, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.Meteor, { - hand: sdk.skills.hand.Right, - range: (pvpRange) => pvpRange ? 40 : 30, - }); - skillMap.set(sdk.skills.ThunderStorm, { - hand: sdk.skills.hand.Right, - range: 1, - townSkill: true, - duration: () => (24 + (8 * me.getSkill(sdk.skills.ThunderStorm, sdk.skills.subindex.SoftPoints))), - }); - skillMap.set(sdk.skills.EnergyShield, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.EnergyShield, - duration: () => (84 + (60 * me.getSkill(sdk.skills.EnergyShield, sdk.skills.subindex.SoftPoints))), - condition: () => Config.UseEnergyShield, - }); - skillMap.set(sdk.skills.Blizzard, { - hand: sdk.skills.hand.Right, - range: (pvpRange) => pvpRange ? 40 : 30, - }); - skillMap.set(sdk.skills.ChillingArmor, { - hand: sdk.skills.hand.Right, - range: 1, - duration: () => ( - ((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) - + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) - ), - }); - skillMap.set(sdk.skills.FireMastery, { - hand: -1, - range: -1, - state: sdk.states.FireMastery, - }); - skillMap.set(sdk.skills.Hydra, { - hand: sdk.skills.hand.Right, - range: 30, - duration: () => 10, - AoE: () => 14, - }); - skillMap.set(sdk.skills.LightningMastery, { - hand: -1, - range: -1, - state: sdk.states.LightningMastery, - }); - skillMap.set(sdk.skills.FrozenOrb, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.ColdMastery, { - hand: -1, - range: -1, - state: sdk.states.ColdMastery, - }); - } - // ~~~ start of necro skills ~~~ // - { - skillMap.set(sdk.skills.AmplifyDamage, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.AmplifyDamage, - duration: () => (5 + (3 * me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints))), - AoE: () => ((4 + (2 * me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Teeth, { - hand: sdk.skills.hand.Left, - missile: true, - range: 15, - }); - skillMap.set(sdk.skills.BoneArmor, { - hand: sdk.skills.hand.Right, - range: 1, - }); - skillMap.set(sdk.skills.SkeletonMastery, { - hand: -1, - range: -1, - }); - skillMap.set(sdk.skills.RaiseSkeleton, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => { - let skillNum = me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.SoftPoints); - return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); - }, - }); - skillMap.set(sdk.skills.DimVision, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.DimVision, - duration: () => { - switch (me.diff) { - case sdk.difficulty.Normal: - return (5 + (2 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))); - case sdk.difficulty.Nightmare: - return ((125 + (50 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; - default: - return ((125 + (50 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; - } - }, - AoE: () => ((6 + (2 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Weaken, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Weaken, - duration: () => (11.6 + (2.4 * me.getSkill(sdk.skills.Weaken, sdk.skills.subindex.SoftPoints))), - AoE: () => ((16 + (2 * me.getSkill(sdk.skills.Weaken, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.PoisonDagger, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.CorpseExplosion, { - hand: sdk.skills.hand.Right, - range: 40, - AoE: () => (((7 + me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints)) / 2) * (2 / 3)), - }); - skillMap.set(sdk.skills.ClayGolem, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => 1, - }); - skillMap.set(sdk.skills.IronMaiden, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.IronMaiden, - duration: () => (9.6 + (2.4 * me.getSkill(sdk.skills.IronMaiden, sdk.skills.subindex.SoftPoints))), - AoE: () => 4, - }); - skillMap.set(sdk.skills.Terror, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Terror, - duration: () => { - switch (me.diff) { - case sdk.difficulty.Normal: - return (7 + me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints)); - case sdk.difficulty.Nightmare: - return ((175 + (25 * me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; - default: - return ((175 + (25 * me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; - } - }, - AoE: () => 2.66, - }); - skillMap.set(sdk.skills.BoneWall, { - hand: sdk.skills.hand.Right, - range: 40, - }); - skillMap.set(sdk.skills.GolemMastery, { - hand: -1, - range: -1, - }); - skillMap.set(sdk.skills.RaiseSkeletalMage, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => { - let skillNum = me.getSkill(sdk.skills.RaiseSkeletalMage, sdk.skills.subindex.SoftPoints); - return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); - }, - }); - skillMap.set(sdk.skills.Confuse, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Confuse, - duration: () => { - switch (me.diff) { - case sdk.difficulty.Normal: - return (8 + (2 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))); - case sdk.difficulty.Nightmare: - return ((200 + (50 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; - default: - return ((200 + (50 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; - } - }, - AoE: () => ((10 + (2 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.LifeTap, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.LifeTap, - duration: () => (13.6 + (2.4 * me.getSkill(sdk.skills.LifeTap, sdk.skills.subindex.SoftPoints))), - AoE: () => ((6 + (2 * me.getSkill(sdk.skills.LifeTap, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.PoisonExplosion, { - hand: sdk.skills.hand.Right, - range: 40, - AoE: () => 2, - }); - skillMap.set(sdk.skills.BoneSpear, { - hand: sdk.skills.hand.Left, - missile: true, - range: (pvpRange) => pvpRange ? 35 : 25, - }); - skillMap.set(sdk.skills.BloodGolem, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => 1, - }); - skillMap.set(sdk.skills.Attract, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Attract, - duration: () => { - switch (me.diff) { - case sdk.difficulty.Normal: - return (8.4 + (3.6 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))); - case sdk.difficulty.Nightmare: - return ((210 + (90 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; - default: - return ((210 + (90 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; - } - }, - AoE: () => 6, - }); - skillMap.set(sdk.skills.Decrepify, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Decrepify, - duration: () => (3.4 + (0.6 * me.getSkill(sdk.skills.Decrepify, sdk.skills.subindex.SoftPoints))), - AoE: () => 4, - }); - skillMap.set(sdk.skills.BonePrison, { - hand: sdk.skills.hand.Right, - range: 40, - }); - skillMap.set(sdk.skills.SummonResist, { - hand: -1, - range: -1, - }); - skillMap.set(sdk.skills.IronGolem, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => 1, - }); - skillMap.set(sdk.skills.LowerResist, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.LowerResist, - duration: () => (18 + (2 * me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints))), - AoE: () => ((12 + (2 * me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.PoisonNova, { - hand: sdk.skills.hand.Right, - range: 20, - state: sdk.states.Poison, - AoE: () => 11, - }); - skillMap.set(sdk.skills.BoneSpirit, { - hand: sdk.skills.hand.Left, - missile: true, - range: (pvpRange) => pvpRange ? 40 : 30, - }); - skillMap.set(sdk.skills.FireGolem, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => 1, - }); - skillMap.set(sdk.skills.Revive, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints), - }); - } - // ~~~ start of paladin skills ~~~ // - { - skillMap.set(sdk.skills.Sacrifice, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.Smite, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.Might, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.Might, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Might, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Prayer, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.Prayer, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Prayer, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.ResistFire, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.ResistFire, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistFire, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.HolyBolt, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.HolyFire, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.HolyFire, - AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyFire, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Thorns, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.Thorns, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Thorns, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Defiance, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.Defiance, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Defiance, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.ResistCold, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.ResistCold, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistCold, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Zeal, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.Charge, { - hand: sdk.skills.hand.Left, - range: 10, - condition: () => Config.Charge, - }); - skillMap.set(sdk.skills.BlessedAim, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.BlessedAim, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Cleansing, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.Cleansing, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Cleansing, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.ResistLightning, { - range: 1, - state: sdk.states.ResistLightning, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistLightning, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Vengeance, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.BlessedHammer, { - hand: sdk.skills.hand.Left, - range: 3, - AoE: () => 10, - }); - skillMap.set(sdk.skills.Concentration, { - range: 1, - state: sdk.states.Concentration, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Concentration, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.HolyFreeze, { - range: 1, - state: sdk.states.HolyFreeze, - AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyFreeze, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Vigor, { - range: 1, - state: sdk.states.Stamina, - condition: () => Config.Vigor || me.inTown, - AoE: () => ((26 + (6 * me.getSkill(sdk.skills.Vigor, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Conversion, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - duration: () => 16, - }); - skillMap.set(sdk.skills.HolyShield, { - range: 1, - state: sdk.states.HolyShield, - duration: () => (5 + (25 * me.getSkill(sdk.skills.HolyShield, sdk.skills.subindex.SoftPoints))), - }); - skillMap.set(sdk.skills.HolyShock, { - range: 1, - state: sdk.states.HolyShock, - AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Sanctuary, { - range: 1, - state: sdk.states.Sanctuary, - AoE: () => ((8 + (2 * me.getSkill(sdk.skills.Sanctuary, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Meditation, { - range: 1, - state: sdk.states.Meditation, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Meditation, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.FistoftheHeavens, { - hand: sdk.skills.hand.Left, - range: 30, - }); - skillMap.set(sdk.skills.Fanaticism, { - range: 1, - state: sdk.states.Fanaticism, - AoE: () => ((20 + (2 * me.getSkill(sdk.skills.Fanaticism, sdk.skills.subindex.SoftPoints)) / 3)), - }); - skillMap.set(sdk.skills.Conviction, { - range: 1, - state: sdk.states.Conviction, - AoE: () => 13, - }); - skillMap.set(sdk.skills.Redemption, { - range: 1, - state: sdk.states.Redemption, - AoE: () => 10, - }); - skillMap.set(sdk.skills.Salvation, { - range: 1, - state: sdk.states.ResistAll, - AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints)) / 3)), - }); - } - // ~~~ start of barbarian skills ~~~ // - { - skillMap.set(sdk.skills.Bash, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.SwordMastery, { - hand: -1, - range: -1, - state: sdk.states.SwordMastery, - }); - skillMap.set(sdk.skills.AxeMastery, { - hand: -1, - range: -1, - state: sdk.states.AxeMastery, - }); - skillMap.set(sdk.skills.MaceMastery, { - hand: -1, - range: -1, - state: sdk.states.MaceMastery, - }); - skillMap.set(sdk.skills.Howl, { - range: 5, - state: sdk.states.Terror, - duration: () => (2 + me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints)), - }); - skillMap.set(sdk.skills.FindPotion, { - hand: sdk.skills.hand.RightShift, - range: 30, - }); - skillMap.set(sdk.skills.Leap, { - hand: sdk.skills.hand.Left, - range: () => { - let skLvl = me.getSkill(sdk.skills.Leap, sdk.skills.subindex.SoftPoints); - return Math.floor(Math.min(4 + (26 * ((110 * skLvl / (skLvl + 6)) / 100)), 30) * (2 / 3)); - }, - }); - skillMap.set(sdk.skills.DoubleSwing, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.PoleArmMastery, { - hand: -1, - range: -1, - state: sdk.states.PoleArmMastery, - }); - skillMap.set(sdk.skills.ThrowingMastery, { - hand: -1, - range: -1, - state: sdk.states.ThrowingMastery, - }); - skillMap.set(sdk.skills.SpearMastery, { - hand: -1, - range: -1, - state: sdk.states.SpearMastery, - }); - skillMap.set(sdk.skills.Taunt, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Taunt, - }); - skillMap.set(sdk.skills.Shout, { - hand: sdk.skills.hand.Right, - range: 20, - state: sdk.states.Shout, - duration: () => ( - ((10 + me.getSkill(sdk.skills.Shout, sdk.skills.subindex.SoftPoints) * 10) - + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) - ), - }); - skillMap.set(sdk.skills.Stun, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - state: sdk.states.Stunned, - duration: () => { - let skLvl = me.getSkill(sdk.skills.Stun, sdk.skills.subindex.SoftPoints); - let wcSkAddition = (me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) * 5); - return skLvl < 16 - ? 1 + (skLvl * 0.2) + wcSkAddition - : Math.min(2.92 + (skLvl * 0.08) + wcSkAddition, 10); - }, - }); - skillMap.set(sdk.skills.DoubleThrow, { - hand: sdk.skills.hand.Left, - missile: true, - range: 20, - }); - skillMap.set(sdk.skills.IncreasedStamina, { - hand: -1, - range: -1, - state: sdk.states.IncreasedStamina, - }); - skillMap.set(sdk.skills.FindItem, { - hand: sdk.skills.hand.RightShift, - range: 30, - condition: () => Config.FindItem, - }); - skillMap.set(sdk.skills.LeapAttack, { - hand: sdk.skills.hand.Left, - range: 10, - }); - skillMap.set(sdk.skills.Concentrate, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - state: sdk.states.Concentrate, - }); - skillMap.set(sdk.skills.IronSkin, { - hand: -1, - range: -1, - state: sdk.states.IronSkin, - }); - skillMap.set(sdk.skills.BattleCry, { - hand: sdk.skills.hand.Right, - range: 5, - state: sdk.states.BattleCry, - duration: () => ( - (9.6 + me.getSkill(sdk.skills.BattleCry, sdk.skills.subindex.SoftPoints) * 2.4) - ), - }); - skillMap.set(sdk.skills.Frenzy, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - state: sdk.states.Frenzy, - }); - skillMap.set(sdk.skills.IncreasedSpeed, { - hand: -1, - range: -1, - state: sdk.states.IncreasedSpeed, - }); - skillMap.set(sdk.skills.BattleOrders, { - hand: sdk.skills.hand.Right, - range: 20, - state: sdk.states.BattleOrders, - duration: () => ( - ((20 + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) * 10) - + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) - ), - }); - skillMap.set(sdk.skills.GrimWard, { - hand: sdk.skills.hand.RightShift, - range: 40, - state: sdk.states.Terror, - duration: () => 40, - AoE: () => (2 + me.getSkill(sdk.skills.GrimWard, sdk.skills.subindex.SoftPoints) * (2 / 3)), - }); - skillMap.set(sdk.skills.Whirlwind, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.Berserk, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - state: sdk.states.Berserk, - }); - skillMap.set(sdk.skills.NaturalResistance, { - hand: -1, - range: -1, - state: sdk.states.NaturalResistance, - }); - skillMap.set(sdk.skills.WarCry, { - hand: sdk.skills.hand.Right, - range: 5, - state: sdk.states.Stunned, - duration: () => ( - Math.min(0.8 + me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.SoftPoints) * 0.2, 10) - ), - }); - skillMap.set(sdk.skills.BattleCommand, { - hand: sdk.skills.hand.Right, - range: 20, - state: sdk.states.BattleCommand, - duration: () => ( - ((10 * me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.SoftPoints) - 5) - + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints)) * 5)) - ), - }); - } - // misc skills - scrolls and books - { - skillMap.set(sdk.skills.IdentifyScroll, { - hand: sdk.skills.hand.Right, - range: 1, - }); - skillMap.set(sdk.skills.BookofIdentify, { - hand: sdk.skills.hand.Right, - range: 1, - }); - skillMap.set(sdk.skills.TownPortalScroll, { - hand: sdk.skills.hand.Right, - range: 1, - }); - skillMap.set(sdk.skills.BookofTownPortal, { - hand: sdk.skills.hand.Right, - range: 1, - }); - } - // ~~~ start of druid skills ~~~ // - { - skillMap.set(sdk.skills.Raven, { - hand: sdk.skills.hand.Right, - range: 40, - summonCount: () => Math.min(me.getSkill(sdk.skills.Raven, sdk.skills.subindex.SoftPoints), 5), - condition: () => Config.SummonRaven, - }); - skillMap.set(sdk.skills.PoisonCreeper, { - hand: sdk.skills.hand.Right, - range: 40, - // condition: () => (typeof Config.SummonVine === "string" - // ? Config.SummonVine.toLowerCase() === "poison" - // : Config.SummonVine === sdk.skills.PoisonCreeper), - summonCount: () => 1, - }); - skillMap.set(sdk.skills.Werewolf, { - hand: sdk.skills.hand.Right, - range: 1, - duration: () => (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)), - }); - skillMap.set(sdk.skills.Lycanthropy, { - hand: -1, - range: -1, - }); - skillMap.set(sdk.skills.Firestorm, { - hand: sdk.skills.hand.Left, - missile: true, - range: 10, - }); - skillMap.set(sdk.skills.OakSage, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.OakSage, - // condition: () => (typeof Config.SummonSpirit === "string" - // ? Config.SummonSpirit.toLowerCase() === "oak" - // : Config.SummonSpirit === sdk.skills.OakSage), - summonCount: () => 1, - }); - skillMap.set(sdk.skills.SpiritWolf, { - hand: sdk.skills.hand.Right, - range: 40, - // condition: () => (typeof Config.SummonAnimal === "string" - // ? Config.SummonAnimal.toLowerCase() === "spirit wolf" - // : Config.SummonAnimal === sdk.skills.SpiritWolf), - summonCount: () => Math.min(me.getSkill(sdk.skills.SpiritWolf, sdk.skills.subindex.SoftPoints), 5), - }); - skillMap.set(sdk.skills.Werebear, { - hand: sdk.skills.hand.Right, - range: 1, - duration: () => (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)), - }); - skillMap.set(sdk.skills.MoltenBoulder, { - hand: sdk.skills.hand.Left, - missile: true, - range: 10, - }); - skillMap.set(sdk.skills.ArcticBlast, { - hand: sdk.skills.hand.Left, - missile: true, - range: () => { - let skLvl = me.getSkill(sdk.skills.ArcticBlast, sdk.skills.subindex.SoftPoints); - let range = Math.floor(((33 + (2 * skLvl)) / 4) * (2 / 3)); - // Druid using this on physical immunes needs the monsters to be within range of hurricane - range > 6 && Config.AttackSkill[5] === sdk.skills.ArcticBlast && (range = 6); + /** @type {Map} */ + const skillMap = new Map(); + // basics + { + skillMap.set(sdk.skills.Attack, { + hand: sdk.skills.hand.LeftNoShift, + range: () => Attack.usingBow() ? 20 : 3, + }); + skillMap.set(sdk.skills.Kick, { + hand: sdk.skills.hand.Right, + range: 3, + }); + skillMap.set(sdk.skills.Throw, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Unsummon, { + hand: sdk.skills.hand.Right, + range: 20, + }); + skillMap.set(sdk.skills.LeftHandThrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.LeftHandSwing, { + hand: sdk.skills.hand.Right, + range: 3, + }); + } + // ~~~ start of amazon skills ~~~ // + { + skillMap.set(sdk.skills.MagicArrow, { + hand: sdk.skills.hand.Left, + missile: true, + }); + skillMap.set(sdk.skills.FireArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.InnerSight, { + hand: sdk.skills.hand.Right, + range: 13, + state: sdk.states.InnerSight, + condition: () => Config.UseInnerSight, + }); + skillMap.set(sdk.skills.CriticalStrike, { + hand: -1, + range: -1, + state: sdk.states.CriticalStrike, + }); + skillMap.set(sdk.skills.Jab, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ColdArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.MultipleShot, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Dodge, { + hand: -1, + range: -1, + state: sdk.states.Dodge, + }); + skillMap.set(sdk.skills.PowerStrike, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.PoisonJavelin, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.ExplodingArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.SlowMissiles, { + hand: sdk.skills.hand.Right, + range: 13, + state: sdk.states.SlowMissiles, + condition: () => Config.UseSlowMissiles, + }); + skillMap.set(sdk.skills.Avoid, { + hand: -1, + range: -1, + state: sdk.states.Avoid, + }); + skillMap.set(sdk.skills.Impale, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.LightningBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.IceArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.GuidedArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Penetrate, { + hand: -1, + range: -1, + state: sdk.states.Penetrate, + }); + skillMap.set(sdk.skills.ChargedStrike, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.PlagueJavelin, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Strafe, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.ImmolationArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Decoy, { + hand: sdk.skills.hand.Right, + range: 30, + duration: () => ((10 + me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints) * 5)), + condition: () => Config.UseDecoy, + }); + skillMap.set(sdk.skills.Evade, { + hand: -1, + range: -1, + state: sdk.states.Evade, + }); + skillMap.set(sdk.skills.Fend, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.FreezingArrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 30, + AoE: () => 3, + }); + skillMap.set(sdk.skills.Valkyrie, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 1, + condition: () => Config.UseValkyrie, + }); + skillMap.set(sdk.skills.Pierce, { + hand: -1, + range: -1, + state: sdk.states.Pierce, + }); + skillMap.set(sdk.skills.LightningStrike, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.LightningFury, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + } + // ~~~ start of sorc skills ~~~ // + { + skillMap.set(sdk.skills.FireBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Warmth, { + hand: -1, + range: -1, + state: sdk.states.Warmth, + }); + skillMap.set(sdk.skills.ChargedBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 10, + }); + skillMap.set(sdk.skills.IceBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.FrozenArmor, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => ( + ((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) + + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) + ), + }); + skillMap.set(sdk.skills.Inferno, { + hand: sdk.skills.hand.Left, + missile: true, + range: ((17 + (me.getSkill(sdk.skills.Inferno, sdk.skills.subindex.SoftPoints) * 3) / 4) * 2 / 3), + }); + skillMap.set(sdk.skills.StaticField, { + hand: sdk.skills.hand.Right, + range: () => Math.floor((me.getSkill(sdk.skills.StaticField, sdk.skills.subindex.SoftPoints) + 4) * 2 / 3), + }); + skillMap.set(sdk.skills.Telekinesis, { + hand: sdk.skills.hand.Right, + range: 40, + condition: () => Config.UseTelekinesis, + }); + skillMap.set(sdk.skills.FrostNova, { + hand: sdk.skills.hand.Right, + range: 5, + }); + skillMap.set(sdk.skills.IceBlast, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Blaze, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.FireBall, { + hand: sdk.skills.hand.Left, + missile: true, + range: (pvpRange) => pvpRange ? 40 : 20, + }); + skillMap.set(sdk.skills.Nova, { + hand: sdk.skills.hand.Right, + range: 7, + }); + skillMap.set(sdk.skills.Lightning, { + hand: sdk.skills.hand.Left, + missile: true, + range: 25, + }); + skillMap.set(sdk.skills.ShiverArmor, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => ( + ((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) + + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) + ), + }); + skillMap.set(sdk.skills.FireWall, { + hand: sdk.skills.hand.Right, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.Enchant, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Enchant, + }); + skillMap.set(sdk.skills.ChainLightning, { + hand: sdk.skills.hand.Left, + missile: true, + range: 25, + }); + skillMap.set(sdk.skills.Teleport, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.GlacialSpike, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.Meteor, { + hand: sdk.skills.hand.Right, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.ThunderStorm, { + hand: sdk.skills.hand.Right, + range: 1, + townSkill: true, + duration: () => (24 + (8 * me.getSkill(sdk.skills.ThunderStorm, sdk.skills.subindex.SoftPoints))), + }); + skillMap.set(sdk.skills.EnergyShield, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.EnergyShield, + duration: () => (84 + (60 * me.getSkill(sdk.skills.EnergyShield, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseEnergyShield, + }); + skillMap.set(sdk.skills.Blizzard, { + hand: sdk.skills.hand.Right, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.ChillingArmor, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => ( + ((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) + + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) + ), + }); + skillMap.set(sdk.skills.FireMastery, { + hand: -1, + range: -1, + state: sdk.states.FireMastery, + }); + skillMap.set(sdk.skills.Hydra, { + hand: sdk.skills.hand.Right, + range: 30, + duration: () => 10, + AoE: () => 14, + }); + skillMap.set(sdk.skills.LightningMastery, { + hand: -1, + range: -1, + state: sdk.states.LightningMastery, + }); + skillMap.set(sdk.skills.FrozenOrb, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.ColdMastery, { + hand: -1, + range: -1, + state: sdk.states.ColdMastery, + }); + } + // ~~~ start of necro skills ~~~ // + { + skillMap.set(sdk.skills.AmplifyDamage, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.AmplifyDamage, + duration: () => (5 + (3 * me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints))), + AoE: () => ((4 + (2 * me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Teeth, { + hand: sdk.skills.hand.Left, + missile: true, + range: 15, + }); + skillMap.set(sdk.skills.BoneArmor, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.SkeletonMastery, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.RaiseSkeleton, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => { + let skillNum = me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.SoftPoints); + return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); + }, + }); + skillMap.set(sdk.skills.DimVision, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.DimVision, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (5 + (2 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))); + case sdk.difficulty.Nightmare: + return ((125 + (50 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((125 + (50 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => ((6 + (2 * me.getSkill(sdk.skills.DimVision, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Weaken, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Weaken, + duration: () => (11.6 + (2.4 * me.getSkill(sdk.skills.Weaken, sdk.skills.subindex.SoftPoints))), + AoE: () => ((16 + (2 * me.getSkill(sdk.skills.Weaken, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.PoisonDagger, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.CorpseExplosion, { + hand: sdk.skills.hand.Right, + range: 40, + AoE: () => (((7 + me.getSkill(sdk.skills.AmplifyDamage, sdk.skills.subindex.SoftPoints)) / 2) * (2 / 3)), + }); + skillMap.set(sdk.skills.ClayGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.IronMaiden, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.IronMaiden, + duration: () => (9.6 + (2.4 * me.getSkill(sdk.skills.IronMaiden, sdk.skills.subindex.SoftPoints))), + AoE: () => 4, + }); + skillMap.set(sdk.skills.Terror, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Terror, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (7 + me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints)); + case sdk.difficulty.Nightmare: + return ((175 + (25 * me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((175 + (25 * me.getSkill(sdk.skills.Terror, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => 2.66, + }); + skillMap.set(sdk.skills.BoneWall, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.GolemMastery, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.RaiseSkeletalMage, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => { + let skillNum = me.getSkill(sdk.skills.RaiseSkeletalMage, sdk.skills.subindex.SoftPoints); + return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); + }, + }); + skillMap.set(sdk.skills.Confuse, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Confuse, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (8 + (2 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))); + case sdk.difficulty.Nightmare: + return ((200 + (50 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((200 + (50 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.Confuse, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.LifeTap, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.LifeTap, + duration: () => (13.6 + (2.4 * me.getSkill(sdk.skills.LifeTap, sdk.skills.subindex.SoftPoints))), + AoE: () => ((6 + (2 * me.getSkill(sdk.skills.LifeTap, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.PoisonExplosion, { + hand: sdk.skills.hand.Right, + range: 40, + AoE: () => 2, + }); + skillMap.set(sdk.skills.BoneSpear, { + hand: sdk.skills.hand.Left, + missile: true, + range: (pvpRange) => pvpRange ? 35 : 25, + }); + skillMap.set(sdk.skills.BloodGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Attract, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Attract, + duration: () => { + switch (me.diff) { + case sdk.difficulty.Normal: + return (8.4 + (3.6 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))); + case sdk.difficulty.Nightmare: + return ((210 + (90 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))) * 0.5) / 25; + default: + return ((210 + (90 * me.getSkill(sdk.skills.Attract, sdk.skills.subindex.SoftPoints))) * 0.25) / 25; + } + }, + AoE: () => 6, + }); + skillMap.set(sdk.skills.Decrepify, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Decrepify, + duration: () => (3.4 + (0.6 * me.getSkill(sdk.skills.Decrepify, sdk.skills.subindex.SoftPoints))), + AoE: () => 4, + }); + skillMap.set(sdk.skills.BonePrison, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.SummonResist, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.IronGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.LowerResist, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.LowerResist, + duration: () => (18 + (2 * me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints))), + AoE: () => ((12 + (2 * me.getSkill(sdk.skills.LowerResist, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.PoisonNova, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.Poison, + AoE: () => 11, + }); + skillMap.set(sdk.skills.BoneSpirit, { + hand: sdk.skills.hand.Left, + missile: true, + range: (pvpRange) => pvpRange ? 40 : 30, + }); + skillMap.set(sdk.skills.FireGolem, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Revive, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints), + }); + } + // ~~~ start of paladin skills ~~~ // + { + skillMap.set(sdk.skills.Sacrifice, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Smite, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Might, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Might, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Might, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Prayer, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Prayer, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Prayer, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.ResistFire, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.ResistFire, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistFire, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.HolyBolt, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.HolyFire, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.HolyFire, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyFire, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Thorns, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Thorns, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Thorns, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Defiance, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Defiance, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Defiance, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.ResistCold, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.ResistCold, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistCold, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Zeal, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Charge, { + hand: sdk.skills.hand.Left, + range: 10, + condition: () => Config.Charge, + }); + skillMap.set(sdk.skills.BlessedAim, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.BlessedAim, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.BlessedHammer, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Cleansing, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Cleansing, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Cleansing, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.ResistLightning, { + range: 1, + state: sdk.states.ResistLightning, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.ResistLightning, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Vengeance, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.BlessedHammer, { + hand: sdk.skills.hand.Left, + range: 3, + AoE: () => 10, + }); + skillMap.set(sdk.skills.Concentration, { + range: 1, + state: sdk.states.Concentration, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Concentration, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.HolyFreeze, { + range: 1, + state: sdk.states.HolyFreeze, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyFreeze, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Vigor, { + range: 1, + state: sdk.states.Stamina, + condition: () => Config.Vigor || me.inTown, + AoE: () => ((26 + (6 * me.getSkill(sdk.skills.Vigor, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Conversion, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + duration: () => 16, + }); + skillMap.set(sdk.skills.HolyShield, { + range: 1, + state: sdk.states.HolyShield, + duration: () => (5 + (25 * me.getSkill(sdk.skills.HolyShield, sdk.skills.subindex.SoftPoints))), + }); + skillMap.set(sdk.skills.HolyShock, { + range: 1, + state: sdk.states.HolyShock, + AoE: () => ((10 + (2 * me.getSkill(sdk.skills.HolyShock, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Sanctuary, { + range: 1, + state: sdk.states.Sanctuary, + AoE: () => ((8 + (2 * me.getSkill(sdk.skills.Sanctuary, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Meditation, { + range: 1, + state: sdk.states.Meditation, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Meditation, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.FistoftheHeavens, { + hand: sdk.skills.hand.Left, + range: 30, + }); + skillMap.set(sdk.skills.Fanaticism, { + range: 1, + state: sdk.states.Fanaticism, + AoE: () => ((20 + (2 * me.getSkill(sdk.skills.Fanaticism, sdk.skills.subindex.SoftPoints)) / 3)), + }); + skillMap.set(sdk.skills.Conviction, { + range: 1, + state: sdk.states.Conviction, + AoE: () => 13, + }); + skillMap.set(sdk.skills.Redemption, { + range: 1, + state: sdk.states.Redemption, + AoE: () => 10, + }); + skillMap.set(sdk.skills.Salvation, { + range: 1, + state: sdk.states.ResistAll, + AoE: () => ((28 + (4 * me.getSkill(sdk.skills.Salvation, sdk.skills.subindex.SoftPoints)) / 3)), + }); + } + // ~~~ start of barbarian skills ~~~ // + { + skillMap.set(sdk.skills.Bash, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.SwordMastery, { + hand: -1, + range: -1, + state: sdk.states.SwordMastery, + }); + skillMap.set(sdk.skills.AxeMastery, { + hand: -1, + range: -1, + state: sdk.states.AxeMastery, + }); + skillMap.set(sdk.skills.MaceMastery, { + hand: -1, + range: -1, + state: sdk.states.MaceMastery, + }); + skillMap.set(sdk.skills.Howl, { + range: 5, + state: sdk.states.Terror, + duration: () => (2 + me.getSkill(sdk.skills.Howl, sdk.skills.subindex.SoftPoints)), + }); + skillMap.set(sdk.skills.FindPotion, { + hand: sdk.skills.hand.RightShift, + range: 30, + }); + skillMap.set(sdk.skills.Leap, { + hand: sdk.skills.hand.Left, + range: () => { + let skLvl = me.getSkill(sdk.skills.Leap, sdk.skills.subindex.SoftPoints); + return Math.floor(Math.min(4 + (26 * ((110 * skLvl / (skLvl + 6)) / 100)), 30) * (2 / 3)); + }, + }); + skillMap.set(sdk.skills.DoubleSwing, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.PoleArmMastery, { + hand: -1, + range: -1, + state: sdk.states.PoleArmMastery, + }); + skillMap.set(sdk.skills.ThrowingMastery, { + hand: -1, + range: -1, + state: sdk.states.ThrowingMastery, + }); + skillMap.set(sdk.skills.SpearMastery, { + hand: -1, + range: -1, + state: sdk.states.SpearMastery, + }); + skillMap.set(sdk.skills.Taunt, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Taunt, + }); + skillMap.set(sdk.skills.Shout, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.Shout, + duration: () => ( + ((10 + me.getSkill(sdk.skills.Shout, sdk.skills.subindex.SoftPoints) * 10) + + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) + ), + }); + skillMap.set(sdk.skills.Stun, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Stunned, + duration: () => { + let skLvl = me.getSkill(sdk.skills.Stun, sdk.skills.subindex.SoftPoints); + let wcSkAddition = (me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.HardPoints) * 5); + return skLvl < 16 + ? 1 + (skLvl * 0.2) + wcSkAddition + : Math.min(2.92 + (skLvl * 0.08) + wcSkAddition, 10); + }, + }); + skillMap.set(sdk.skills.DoubleThrow, { + hand: sdk.skills.hand.Left, + missile: true, + range: 20, + }); + skillMap.set(sdk.skills.IncreasedStamina, { + hand: -1, + range: -1, + state: sdk.states.IncreasedStamina, + }); + skillMap.set(sdk.skills.FindItem, { + hand: sdk.skills.hand.RightShift, + range: 30, + condition: () => Config.FindItem, + }); + skillMap.set(sdk.skills.LeapAttack, { + hand: sdk.skills.hand.Left, + range: 10, + }); + skillMap.set(sdk.skills.Concentrate, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Concentrate, + }); + skillMap.set(sdk.skills.IronSkin, { + hand: -1, + range: -1, + state: sdk.states.IronSkin, + }); + skillMap.set(sdk.skills.BattleCry, { + hand: sdk.skills.hand.Right, + range: 5, + state: sdk.states.BattleCry, + duration: () => ( + (9.6 + me.getSkill(sdk.skills.BattleCry, sdk.skills.subindex.SoftPoints) * 2.4) + ), + }); + skillMap.set(sdk.skills.Frenzy, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Frenzy, + }); + skillMap.set(sdk.skills.IncreasedSpeed, { + hand: -1, + range: -1, + state: sdk.states.IncreasedSpeed, + }); + skillMap.set(sdk.skills.BattleOrders, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.BattleOrders, + duration: () => ( + ((20 + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) * 10) + + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) + ), + }); + skillMap.set(sdk.skills.GrimWard, { + hand: sdk.skills.hand.RightShift, + range: 40, + state: sdk.states.Terror, + duration: () => 40, + AoE: () => (2 + me.getSkill(sdk.skills.GrimWard, sdk.skills.subindex.SoftPoints) * (2 / 3)), + }); + skillMap.set(sdk.skills.Whirlwind, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.Berserk, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Berserk, + }); + skillMap.set(sdk.skills.NaturalResistance, { + hand: -1, + range: -1, + state: sdk.states.NaturalResistance, + }); + skillMap.set(sdk.skills.WarCry, { + hand: sdk.skills.hand.Right, + range: 5, + state: sdk.states.Stunned, + duration: () => ( + Math.min(0.8 + me.getSkill(sdk.skills.WarCry, sdk.skills.subindex.SoftPoints) * 0.2, 10) + ), + }); + skillMap.set(sdk.skills.BattleCommand, { + hand: sdk.skills.hand.Right, + range: 20, + state: sdk.states.BattleCommand, + duration: () => ( + ((10 * me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.SoftPoints) - 5) + + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints)) * 5)) + ), + }); + } + // misc skills - scrolls and books + { + skillMap.set(sdk.skills.IdentifyScroll, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.BookofIdentify, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.TownPortalScroll, { + hand: sdk.skills.hand.Right, + range: 1, + }); + skillMap.set(sdk.skills.BookofTownPortal, { + hand: sdk.skills.hand.Right, + range: 1, + }); + } + // ~~~ start of druid skills ~~~ // + { + skillMap.set(sdk.skills.Raven, { + hand: sdk.skills.hand.Right, + range: 40, + summonCount: () => Math.min(me.getSkill(sdk.skills.Raven, sdk.skills.subindex.SoftPoints), 5), + condition: () => Config.SummonRaven, + }); + skillMap.set(sdk.skills.PoisonCreeper, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonVine === "string" + // ? Config.SummonVine.toLowerCase() === "poison" + // : Config.SummonVine === sdk.skills.PoisonCreeper), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Werewolf, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)), + }); + skillMap.set(sdk.skills.Lycanthropy, { + hand: -1, + range: -1, + }); + skillMap.set(sdk.skills.Firestorm, { + hand: sdk.skills.hand.Left, + missile: true, + range: 10, + }); + skillMap.set(sdk.skills.OakSage, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.OakSage, + // condition: () => (typeof Config.SummonSpirit === "string" + // ? Config.SummonSpirit.toLowerCase() === "oak" + // : Config.SummonSpirit === sdk.skills.OakSage), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.SpiritWolf, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonAnimal === "string" + // ? Config.SummonAnimal.toLowerCase() === "spirit wolf" + // : Config.SummonAnimal === sdk.skills.SpiritWolf), + summonCount: () => Math.min(me.getSkill(sdk.skills.SpiritWolf, sdk.skills.subindex.SoftPoints), 5), + }); + skillMap.set(sdk.skills.Werebear, { + hand: sdk.skills.hand.Right, + range: 1, + duration: () => (40 + (20 * me.getSkill(sdk.skills.Lycanthropy, sdk.skills.subindex.SoftPoints) + 20)), + }); + skillMap.set(sdk.skills.MoltenBoulder, { + hand: sdk.skills.hand.Left, + missile: true, + range: 10, + }); + skillMap.set(sdk.skills.ArcticBlast, { + hand: sdk.skills.hand.Left, + missile: true, + range: () => { + let skLvl = me.getSkill(sdk.skills.ArcticBlast, sdk.skills.subindex.SoftPoints); + let range = Math.floor(((33 + (2 * skLvl)) / 4) * (2 / 3)); + // Druid using this on physical immunes needs the monsters to be within range of hurricane + range > 6 && Config.AttackSkill[5] === sdk.skills.ArcticBlast && (range = 6); - return range; - }, - }); - skillMap.set(sdk.skills.CarrionVine, { - hand: sdk.skills.hand.Right, - range: 40, - // condition: () => (typeof Config.SummonVine === "string" - // ? Config.SummonVine.toLowerCase() === "carion" - // : Config.SummonVine === sdk.skills.CarrionVine), - summonCount: () => 1, - }); - skillMap.set(sdk.skills.FeralRage, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - state: sdk.states.FeralRage, - duration: () => 20, - }); - skillMap.set(sdk.skills.Maul, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - state: sdk.states.Maul, - }); - skillMap.set(sdk.skills.Fissure, { - hand: sdk.skills.hand.Right, - range: 20, - }); - skillMap.set(sdk.skills.CycloneArmor, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.CycloneArmor, - // todo - armor percent like I did for bonearmor - }); - skillMap.set(sdk.skills.HeartofWolverine, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.HeartofWolverine, - // condition: () => (typeof Config.SummonSpirit === "string" - // ? Config.SummonSpirit.toLowerCase() === "wolverine" - // : Config.SummonSpirit === sdk.skills.HeartofWolverine), - summonCount: () => 1, - }); - skillMap.set(sdk.skills.SummonDireWolf, { - hand: sdk.skills.hand.Right, - range: 40, - // condition: () => (typeof Config.SummonAnimal === "string" - // ? Config.SummonAnimal.toLowerCase() === "dire wolf" - // : Config.SummonAnimal === sdk.skills.SummonDireWolf), - summonCount: () => Math.min(me.getSkill(sdk.skills.SummonDireWolf, sdk.skills.subindex.SoftPoints), 3), - }); - skillMap.set(sdk.skills.Rabies, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - state: sdk.states.Rabies, - duration: () => (3.6 + me.getSkill(sdk.skills.Rabies, sdk.skills.subindex.SoftPoints) * 0.4), - }); - skillMap.set(sdk.skills.FireClaws, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.Twister, { - hand: sdk.skills.hand.Left, - missile: true, - range: 5, - }); - skillMap.set(sdk.skills.SolarCreeper, { - hand: sdk.skills.hand.Right, - range: 40, - // condition: () => (typeof Config.SummonVine === "string" - // ? Config.SummonVine.toLowerCase() === "solar" - // : Config.SummonVine === sdk.skills.SolarCreeper), - summonCount: () => 1, - }); - skillMap.set(sdk.skills.Hunger, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.ShockWave, { - hand: sdk.skills.hand.Left, - range: 8, - duration: () => Math.min(1 + me.getSkill(sdk.skills.ShockWave, sdk.skills.subindex.SoftPoints) * 0.6, 10), - }); - skillMap.set(sdk.skills.Volcano, { - hand: sdk.skills.hand.Right, - range: 30, - }); - skillMap.set(sdk.skills.Tornado, { - hand: sdk.skills.hand.Left, - missile: true, - range: 5, - }); - skillMap.set(sdk.skills.SpiritofBarbs, { - hand: sdk.skills.hand.Right, - range: 40, - state: sdk.states.Barbs, - // condition: () => (typeof Config.SummonSpirit === "string" - // ? Config.SummonSpirit.toLowerCase() === "barbs" - // : Config.SummonSpirit === sdk.skills.SpiritofBarbs), - summonCount: () => 1, - }); - skillMap.set(sdk.skills.SummonGrizzly, { - hand: sdk.skills.hand.Right, - range: 40, - timed: true, - // condition: () => (typeof Config.SummonAnimal === "string" - // ? Config.SummonAnimal.toLowerCase() === "grizzly" - // : Config.SummonAnimal === sdk.skills.SummonGrizzly), - summonCount: () => 1, - }); - skillMap.set(sdk.skills.Fury, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.Armageddon, { - hand: sdk.skills.hand.Right, - range: 10, - state: sdk.states.Armageddon, - duration: () => (10 + me.getSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints) * 2), - AoE: () => 11, - }); - skillMap.set(sdk.skills.Hurricane, { - hand: sdk.skills.hand.Right, - range: 10, - state: sdk.states.Hurricane, - duration: () => (10 + me.getSkill(sdk.skills.CycloneArmor, sdk.skills.subindex.HardPoints) * 2), - AoE: () => 6, - }); - } - // ~~~ start of assassin skills ~~~ // - { - skillMap.set(sdk.skills.FireBlast, { - hand: sdk.skills.hand.Left, - missile: true, - range: 15, - }); - skillMap.set(sdk.skills.ClawMastery, { - hand: -1, - range: -1, - state: sdk.states.ClawMastery, - }); - skillMap.set(sdk.skills.PsychicHammer, { - hand: sdk.skills.hand.Right, - range: 40, - }); - skillMap.set(sdk.skills.TigerStrike, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.DragonTalon, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.ShockWeb, { - hand: sdk.skills.hand.Left, - range: 15, - AoE: () => { - let baseMissiles = Math.floor((6 + me.getSkill(sdk.skills.ShockWeb, sdk.skills.subindex.SoftPoints)) / 4); - let extraMissiles = Math.floor(me.getSkill(sdk.skills.FireBlast, sdk.skills.subindex.HardPoints) / 3); - return ((((baseMissiles + extraMissiles) / 4) + 1) * (2 / 3)); - }, - }); - skillMap.set(sdk.skills.BladeSentinel, { - hand: sdk.skills.hand.Left, - range: 15, - }); - skillMap.set(sdk.skills.BurstofSpeed, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.BurstofSpeed, - duration: () => (108 + (12 * me.getSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.SoftPoints))), - condition: () => !Config.UseBoS && !me.inTown, - }); - skillMap.set(sdk.skills.FistsofFire, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.DragonClaw, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.ChargedBoltSentry, { - hand: sdk.skills.hand.Right, - range: 30, - summonCount: () => 5, - }); - skillMap.set(sdk.skills.WakeofFireSentry, { - hand: sdk.skills.hand.Right, - range: 30, - summonCount: () => 5, - }); - skillMap.set(sdk.skills.WeaponBlock, { - hand: -1, - range: -1, - state: sdk.states.WeaponBlock, - }); - skillMap.set(sdk.skills.CloakofShadows, { - hand: sdk.skills.hand.Right, - range: 30, - state: sdk.states.CloakofShadows, - duration: () => (7 + me.getSkill(sdk.skills.CloakofShadows, sdk.skills.subindex.SoftPoints)), - }); - skillMap.set(sdk.skills.CobraStrike, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.BladeFury, { - hand: sdk.skills.hand.Left, - range: 15, - }); - skillMap.set(sdk.skills.Fade, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.Fade, - duration: () => (108 + (12 * me.getSkill(sdk.skills.Fade, sdk.skills.subindex.SoftPoints))), - condition: () => Config.UseFade, - }); - skillMap.set(sdk.skills.ShadowWarrior, { - hand: sdk.skills.hand.Right, - range: 30, - // condition: () => Config.UseValkyrie, - summonCount: () => 1, - }); - skillMap.set(sdk.skills.ClawsofThunder, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.DragonTail, { - hand: sdk.skills.hand.LeftNoShift, - range: 3, - }); - skillMap.set(sdk.skills.LightningSentry, { - hand: sdk.skills.hand.Right, - range: 30, - summonCount: () => 5, - }); - skillMap.set(sdk.skills.InfernoSentry, { - hand: sdk.skills.hand.Right, - range: 30, - summonCount: () => 5, - }); - skillMap.set(sdk.skills.MindBlast, { - hand: sdk.skills.hand.Right, - range: 40, - AoE: () => 2.66, - }); - skillMap.set(sdk.skills.BladesofIce, { - hand: sdk.skills.hand.Left, - range: 3, - }); - skillMap.set(sdk.skills.DragonFlight, { - hand: sdk.skills.hand.Left, - range: 10, - }); - skillMap.set(sdk.skills.DeathSentry, { - hand: sdk.skills.hand.Right, - range: 30, - summonCount: () => 5, - }); - skillMap.set(sdk.skills.BladeShield, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.BladeShield, - timed: true, - duration: () => (15 + (5 * me.getSkill(sdk.skills.BladeShield, sdk.skills.subindex.SoftPoints))), - condition: () => Config.UseBladeShield, - }); - skillMap.set(sdk.skills.Venom, { - hand: sdk.skills.hand.Right, - range: 1, - state: sdk.states.Venom, - duration: () => (116 + (4 * me.getSkill(sdk.skills.Venom, sdk.skills.subindex.SoftPoints))), - condition: () => Config.UseVenom, - }); - skillMap.set(sdk.skills.ShadowMaster, { - hand: sdk.skills.hand.Right, - range: 30, - // condition: () => Config.UseValkyrie, - summonCount: () => 1, - }); - skillMap.set(sdk.skills.RoyalStrike, { - hand: sdk.skills.hand.Right, - range: 3, - }); - } + return range; + }, + }); + skillMap.set(sdk.skills.CarrionVine, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonVine === "string" + // ? Config.SummonVine.toLowerCase() === "carion" + // : Config.SummonVine === sdk.skills.CarrionVine), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.FeralRage, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.FeralRage, + duration: () => 20, + }); + skillMap.set(sdk.skills.Maul, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Maul, + }); + skillMap.set(sdk.skills.Fissure, { + hand: sdk.skills.hand.Right, + range: 20, + }); + skillMap.set(sdk.skills.CycloneArmor, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.CycloneArmor, + // todo - armor percent like I did for bonearmor + }); + skillMap.set(sdk.skills.HeartofWolverine, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.HeartofWolverine, + // condition: () => (typeof Config.SummonSpirit === "string" + // ? Config.SummonSpirit.toLowerCase() === "wolverine" + // : Config.SummonSpirit === sdk.skills.HeartofWolverine), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.SummonDireWolf, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonAnimal === "string" + // ? Config.SummonAnimal.toLowerCase() === "dire wolf" + // : Config.SummonAnimal === sdk.skills.SummonDireWolf), + summonCount: () => Math.min(me.getSkill(sdk.skills.SummonDireWolf, sdk.skills.subindex.SoftPoints), 3), + }); + skillMap.set(sdk.skills.Rabies, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + state: sdk.states.Rabies, + duration: () => (3.6 + me.getSkill(sdk.skills.Rabies, sdk.skills.subindex.SoftPoints) * 0.4), + }); + skillMap.set(sdk.skills.FireClaws, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Twister, { + hand: sdk.skills.hand.Left, + missile: true, + range: 5, + }); + skillMap.set(sdk.skills.SolarCreeper, { + hand: sdk.skills.hand.Right, + range: 40, + // condition: () => (typeof Config.SummonVine === "string" + // ? Config.SummonVine.toLowerCase() === "solar" + // : Config.SummonVine === sdk.skills.SolarCreeper), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Hunger, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ShockWave, { + hand: sdk.skills.hand.Left, + range: 8, + duration: () => Math.min(1 + me.getSkill(sdk.skills.ShockWave, sdk.skills.subindex.SoftPoints) * 0.6, 10), + }); + skillMap.set(sdk.skills.Volcano, { + hand: sdk.skills.hand.Right, + range: 30, + }); + skillMap.set(sdk.skills.Tornado, { + hand: sdk.skills.hand.Left, + missile: true, + range: 5, + }); + skillMap.set(sdk.skills.SpiritofBarbs, { + hand: sdk.skills.hand.Right, + range: 40, + state: sdk.states.Barbs, + // condition: () => (typeof Config.SummonSpirit === "string" + // ? Config.SummonSpirit.toLowerCase() === "barbs" + // : Config.SummonSpirit === sdk.skills.SpiritofBarbs), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.SummonGrizzly, { + hand: sdk.skills.hand.Right, + range: 40, + timed: true, + // condition: () => (typeof Config.SummonAnimal === "string" + // ? Config.SummonAnimal.toLowerCase() === "grizzly" + // : Config.SummonAnimal === sdk.skills.SummonGrizzly), + summonCount: () => 1, + }); + skillMap.set(sdk.skills.Fury, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.Armageddon, { + hand: sdk.skills.hand.Right, + range: 10, + state: sdk.states.Armageddon, + duration: () => (10 + me.getSkill(sdk.skills.Fissure, sdk.skills.subindex.HardPoints) * 2), + AoE: () => 11, + }); + skillMap.set(sdk.skills.Hurricane, { + hand: sdk.skills.hand.Right, + range: 10, + state: sdk.states.Hurricane, + duration: () => (10 + me.getSkill(sdk.skills.CycloneArmor, sdk.skills.subindex.HardPoints) * 2), + AoE: () => 6, + }); + } + // ~~~ start of assassin skills ~~~ // + { + skillMap.set(sdk.skills.FireBlast, { + hand: sdk.skills.hand.Left, + missile: true, + range: 15, + }); + skillMap.set(sdk.skills.ClawMastery, { + hand: -1, + range: -1, + state: sdk.states.ClawMastery, + }); + skillMap.set(sdk.skills.PsychicHammer, { + hand: sdk.skills.hand.Right, + range: 40, + }); + skillMap.set(sdk.skills.TigerStrike, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonTalon, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ShockWeb, { + hand: sdk.skills.hand.Left, + range: 15, + AoE: () => { + let baseMissiles = Math.floor((6 + me.getSkill(sdk.skills.ShockWeb, sdk.skills.subindex.SoftPoints)) / 4); + let extraMissiles = Math.floor(me.getSkill(sdk.skills.FireBlast, sdk.skills.subindex.HardPoints) / 3); + return ((((baseMissiles + extraMissiles) / 4) + 1) * (2 / 3)); + }, + }); + skillMap.set(sdk.skills.BladeSentinel, { + hand: sdk.skills.hand.Left, + range: 15, + }); + skillMap.set(sdk.skills.BurstofSpeed, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.BurstofSpeed, + duration: () => (108 + (12 * me.getSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.SoftPoints))), + condition: () => !Config.UseBoS && !me.inTown, + }); + skillMap.set(sdk.skills.FistsofFire, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonClaw, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.ChargedBoltSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.WakeofFireSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.WeaponBlock, { + hand: -1, + range: -1, + state: sdk.states.WeaponBlock, + }); + skillMap.set(sdk.skills.CloakofShadows, { + hand: sdk.skills.hand.Right, + range: 30, + state: sdk.states.CloakofShadows, + duration: () => (7 + me.getSkill(sdk.skills.CloakofShadows, sdk.skills.subindex.SoftPoints)), + }); + skillMap.set(sdk.skills.CobraStrike, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.BladeFury, { + hand: sdk.skills.hand.Left, + range: 15, + }); + skillMap.set(sdk.skills.Fade, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Fade, + duration: () => (108 + (12 * me.getSkill(sdk.skills.Fade, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseFade, + }); + skillMap.set(sdk.skills.ShadowWarrior, { + hand: sdk.skills.hand.Right, + range: 30, + // condition: () => Config.UseValkyrie, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.ClawsofThunder, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonTail, { + hand: sdk.skills.hand.LeftNoShift, + range: 3, + }); + skillMap.set(sdk.skills.LightningSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.InfernoSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.MindBlast, { + hand: sdk.skills.hand.Right, + range: 40, + AoE: () => 2.66, + }); + skillMap.set(sdk.skills.BladesofIce, { + hand: sdk.skills.hand.Left, + range: 3, + }); + skillMap.set(sdk.skills.DragonFlight, { + hand: sdk.skills.hand.Left, + range: 10, + }); + skillMap.set(sdk.skills.DeathSentry, { + hand: sdk.skills.hand.Right, + range: 30, + summonCount: () => 5, + }); + skillMap.set(sdk.skills.BladeShield, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.BladeShield, + timed: true, + duration: () => (15 + (5 * me.getSkill(sdk.skills.BladeShield, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseBladeShield, + }); + skillMap.set(sdk.skills.Venom, { + hand: sdk.skills.hand.Right, + range: 1, + state: sdk.states.Venom, + duration: () => (116 + (4 * me.getSkill(sdk.skills.Venom, sdk.skills.subindex.SoftPoints))), + condition: () => Config.UseVenom, + }); + skillMap.set(sdk.skills.ShadowMaster, { + hand: sdk.skills.hand.Right, + range: 30, + // condition: () => Config.UseValkyrie, + summonCount: () => 1, + }); + skillMap.set(sdk.skills.RoyalStrike, { + hand: sdk.skills.hand.Right, + range: 3, + }); + } - const damageTypes = [ - "Physical", "Fire", "Lightning", - "Magic", "Cold", "Poison", "None", - "None", "None", "Physical" - ]; + const damageTypes = [ + "Physical", "Fire", "Lightning", + "Magic", "Cold", "Poison", "None", + "None", "None", "Physical" + ]; - /** + /** * @constructor * @param {number} skillId */ - function Skill (skillId) { - let _skillData = skillMap.get(skillId); - /** @type {number} */ - this.skillId = skillId; - /** @type {number} */ - this.hand = (_skillData.hand || sdk.skills.hand.Right); - /** @type {number} */ - this.state = (_skillData.state || sdk.states.None); - /** @type {() => number} */ - this.summonCount = (_skillData.summonCount || (() => 0)); - /** @type {() => boolean} */ - this.condition = (_skillData.condition || (() => true)); - /** @type {boolean} */ - this.townSkill = (_skillData.townSkill || getBaseStat("skills", skillId, "InTown") === 1); - /** @type {boolean} */ - this.timed = (_skillData.timed || getBaseStat("skills", skillId, "delay") > 0); - /** @type {boolean} */ - this.missleSkill = (_skillData.missile || false); - /** @type {number} */ - this.charClass = getBaseStat("skills", skillId, "charClass"); - /** @type {number} */ - this.reqLevel = getBaseStat("skills", skillId, "reqlevel"); - /** @type {number[]} */ - this.preReqs = (() => { - let preReqs = []; + function Skill (skillId) { + let _skillData = skillMap.get(skillId); + /** @type {number} */ + this.skillId = skillId; + /** @type {number} */ + this.hand = (_skillData.hand || sdk.skills.hand.Right); + /** @type {number} */ + this.state = (_skillData.state || sdk.states.None); + /** @type {() => number} */ + this.summonCount = (_skillData.summonCount || (() => 0)); + /** @type {() => boolean} */ + this.condition = (_skillData.condition || (() => true)); + /** @type {boolean} */ + this.townSkill = (_skillData.townSkill || getBaseStat("skills", skillId, "InTown") === 1); + /** @type {boolean} */ + this.timed = (_skillData.timed || getBaseStat("skills", skillId, "delay") > 0); + /** @type {boolean} */ + this.missleSkill = (_skillData.missile || false); + /** @type {number} */ + this.charClass = getBaseStat("skills", skillId, "charClass"); + /** @type {number} */ + this.reqLevel = getBaseStat("skills", skillId, "reqlevel"); + /** @type {number[]} */ + this.preReqs = (() => { + let preReqs = []; - for (let t = sdk.stats.PreviousSkillLeft; t >= sdk.stats.PreviousSkillRight; t--) { - let preReq = (getBaseStat("skills", skillId, t)); + for (let t = sdk.stats.PreviousSkillLeft; t >= sdk.stats.PreviousSkillRight; t--) { + let preReq = (getBaseStat("skills", skillId, t)); - if (preReq > sdk.skills.Attack && preReq < sdk.skills.RoyalStrike) { - return preReqs.push(preReq); - } - } + if (preReq > sdk.skills.Attack && preReq < sdk.skills.RoyalStrike) { + return preReqs.push(preReq); + } + } - return preReqs; - })(); - this.damageType = damageTypes[getBaseStat("skills", skillId, "EType")]; + return preReqs; + })(); + this.damageType = damageTypes[getBaseStat("skills", skillId, "EType")]; - /** + /** * @private * @type {number | () => number} */ - this._range = (_skillData.range || 1); - /** + this._range = (_skillData.range || 1); + /** * @private * @type {() => number} */ - this._AoE = (_skillData.AoE || (() => 0)); - /** + this._AoE = (_skillData.AoE || (() => 0)); + /** * @private * @type {() => number} */ - this._duration = (_skillData.duration || (() => 0)); - /** + this._duration = (_skillData.duration || (() => 0)); + /** * @private * @type {number} */ - this._manaCost = Infinity; - /** + this._manaCost = Infinity; + /** * @private * @type {number} */ - this._mana = getBaseStat("skills", this.skillId, "mana"); - /** + this._mana = getBaseStat("skills", this.skillId, "mana"); + /** * @private * @type {number} */ - this._minMana = getBaseStat("skills", this.skillId, "minmana"); - /** + this._minMana = getBaseStat("skills", this.skillId, "minmana"); + /** * @private * @type {number} */ - this._lvlMana = getBaseStat("skills", this.skillId, "lvlmana"); - let effectiveShift = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]; - /** + this._lvlMana = getBaseStat("skills", this.skillId, "lvlmana"); + let effectiveShift = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]; + /** * @private * @type {number} */ - this._manaShift = (effectiveShift[getBaseStat("skills", this.skillId, "manashift")] / 256); - /** + this._manaShift = (effectiveShift[getBaseStat("skills", this.skillId, "manashift")] / 256); + /** * @private * @type {number} */ - this._bestSlot = 0; - /** + this._bestSlot = 0; + /** * @private * @type {number} */ - this._dmg = 0; - /** + this._dmg = 0; + /** * @private * @type {number} */ - this._hardPoints = 0; - /** + this._hardPoints = 0; + /** * @private * @type {number} */ - this._softPoints = 0; - /** + this._softPoints = 0; + /** * @private * @type {boolean} */ - this._checked = false; - } + this._checked = false; + } - /** + /** * @this Skill * @returns {number} */ - Skill.prototype.duration = function () { - return Time.seconds(this._duration()); - }; + Skill.prototype.duration = function () { + return Time.seconds(this._duration()); + }; - /** + /** * @this Skill * @returns {number} */ - Skill.prototype.manaCost = function () { - if (this._manaCost !== Infinity) return this._manaCost; - if (this.skillId < sdk.skills.MagicArrow) { - return (this._manaCost = 0); - } - let skillLvl = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints); - if (skillLvl !== this._softPoints) { - this._softPoints = skillLvl; - this._hardPoints = me.getSkill(this.skillId, sdk.skills.subindex.HardPoints); - } - // Decoy wasn't reading from skill bin - if (this.skillId === sdk.skills.Decoy) { - return (this._manaCost = Math.max(19.75 - (0.75 * skillLvl), 1)); - } - let lvlmana = this._lvlMana === 65535 - ? -1 - : this._lvlMana; // Correction for skills that need less mana with levels (kolton) - let ret = Math.max((this._mana + lvlmana * (skillLvl - 1)) * this._manaShift, this._minMana); - return (this._manaCost = ret); - }; + Skill.prototype.manaCost = function () { + if (this._manaCost !== Infinity) return this._manaCost; + if (this.skillId < sdk.skills.MagicArrow) { + return (this._manaCost = 0); + } + let skillLvl = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints); + if (skillLvl !== this._softPoints) { + this._softPoints = skillLvl; + this._hardPoints = me.getSkill(this.skillId, sdk.skills.subindex.HardPoints); + } + // Decoy wasn't reading from skill bin + if (this.skillId === sdk.skills.Decoy) { + return (this._manaCost = Math.max(19.75 - (0.75 * skillLvl), 1)); + } + let lvlmana = this._lvlMana === 65535 + ? -1 + : this._lvlMana; // Correction for skills that need less mana with levels (kolton) + let ret = Math.max((this._mana + lvlmana * (skillLvl - 1)) * this._manaShift, this._minMana); + return (this._manaCost = ret); + }; - /** + /** * @this Skill * @property {boolean} pvpRange * @returns {number} */ - Skill.prototype.range = function (pvpRange = false) { - return typeof this._range === "function" ? this._range(pvpRange) : this._range; - }; + Skill.prototype.range = function (pvpRange = false) { + return typeof this._range === "function" ? this._range(pvpRange) : this._range; + }; - /** + /** * @this Skill * @returns {number} */ - Skill.prototype.AoE = function () { - return this._AoE(); - }; + Skill.prototype.AoE = function () { + return this._AoE(); + }; - /** + /** * @this Skill * @returns {boolean} */ - Skill.prototype.have = function () { - if (!this.condition()) return false; - if (this._hardPoints > 0) return true; - if (!this._checked) { - this._checked = true; - this._hardPoints = me.getSkill(this.skillId, sdk.skills.subindex.HardPoints); - // this._softPoints = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints); - } - return this._hardPoints > 0 || (this._softPoints = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints)) > 0; - }; + Skill.prototype.have = function () { + if (!this.condition()) return false; + if (this._hardPoints > 0) return true; + if (!this._checked) { + this._checked = true; + this._hardPoints = me.getSkill(this.skillId, sdk.skills.subindex.HardPoints); + // this._softPoints = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints); + } + return this._hardPoints > 0 || (this._softPoints = me.getSkill(this.skillId, sdk.skills.subindex.SoftPoints)) > 0; + }; - Skill.prototype.reset = function () { - this._manaCost = Infinity; - this._dmg = 0; - this._hardPoints = 0; - this._softPoints = 0; - this._checked = false; - }; + Skill.prototype.reset = function () { + this._manaCost = Infinity; + this._dmg = 0; + this._hardPoints = 0; + this._softPoints = 0; + this._checked = false; + }; - /** + /** * @todo Damage calculations, best slot, etc. */ - /** @type {Map 0; - }, - - canEquip: function (item) { - // Not an item or unid - if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; - // Higher requirements - if (item.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) || item.dexreq > me.getStat(sdk.stats.Dexterity) || item.strreq > me.getStat(sdk.stats.Strength)) return false; - - return true; - }, - - // Equips an item and throws away the old equipped 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.isInStash && !Town.openStash()) return false; - - 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) { - if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { - cursorItem.drop(); - } - } - } - - return true; - } - } - } - - return false; - }, - - getEquippedItem: function (bodyLoc) { - let item = me.getItem(); - - if (item) { - do { - if (item.bodylocation === bodyLoc) { - return { - classid: item.classid, - tier: NTIP.GetTier(item) - }; - } - } while (item.getNext()); - } - - // Don't have anything equipped in there - return { - classid: -1, - tier: -1 - }; - }, - - getBodyLoc: function (item) { - let bodyLoc; - - switch (item.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - case sdk.items.type.BowQuiver: - case sdk.items.type.CrossbowQuiver: - bodyLoc = sdk.body.LeftArm; - - break; - case sdk.items.type.Armor: - bodyLoc = sdk.body.Armor; - - break; - case sdk.items.type.Ring: - bodyLoc = [sdk.body.RingRight, sdk.body.RingLeft]; - - break; - case sdk.items.type.Amulet: - bodyLoc = sdk.body.Neck; - - break; - case sdk.items.type.Boots: - bodyLoc = sdk.body.Feet; - - break; - case sdk.items.type.Gloves: - bodyLoc = sdk.body.Gloves; - - break; - case sdk.items.type.Belt: - bodyLoc = sdk.body.Belt; - - break; - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - bodyLoc = sdk.body.Head; - - break; - 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: - bodyLoc = me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - - break; - case sdk.items.type.HandtoHand: - case sdk.items.type.AssassinClaw: - bodyLoc = me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - - break; - default: - return false; - } - - !Array.isArray(bodyLoc) && (bodyLoc = [bodyLoc]); - - return bodyLoc; - }, - - autoEquipCheck: function (item) { - if (!Config.AutoEquip) return true; - - let tier = NTIP.GetTier(item); - let bodyLoc = this.getBodyLoc(item); - - if (tier > 0 && bodyLoc) { - for (let i = 0; i < bodyLoc.length; i += 1) { - // Low tier items shouldn't be kept if they can't be equipped - if (tier > this.getEquippedItem(bodyLoc[i]).tier && (this.canEquip(item) || !item.getFlag(sdk.items.flags.Identified))) { - return true; - } - } - } - - // Sell/ignore low tier items, keep high tier - if (tier > 0 && tier < 100) return false; - - return true; - }, - - // returns true if the item should be kept+logged, false if not - autoEquip: function () { - if (!Config.AutoEquip) return true; - - let items = me.findItems(-1, sdk.items.mode.inStorage); - - if (!items) return false; - - function sortEq(a, b) { - if (Item.canEquip(a)) return -1; - if (Item.canEquip(b)) return 1; - - return 0; - } - - me.cancel(); - - // Remove items without tier - for (let i = 0; i < items.length; i += 1) { - if (NTIP.GetTier(items[i]) === 0) { - items.splice(i, 1); - - i -= 1; - } - } - - while (items.length > 0) { - items.sort(sortEq); - - let tier = NTIP.GetTier(items[0]); - let bodyLoc = this.getBodyLoc(items[0]); - - if (tier > 0 && bodyLoc) { - for (let j = 0; j < bodyLoc.length; j += 1) { - // khalim's will adjustment - const equippedItem = this.getEquippedItem(bodyLoc[j]); - if (items[0].isInStorage && tier > equippedItem.tier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { - if (!items[0].identified) { - let tome = me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (tome && tome.getStat(sdk.stats.Quantity) > 0) { - items[0].isInStash && Town.openStash(); - Town.identifyItem(items[0], tome); - } - } - - let gid = items[0].gid; - console.log(items[0].name); - - if (this.equip(items[0], bodyLoc[j])) { - Item.logItem("Equipped", me.getItem(-1, -1, gid)); - } - - break; - } - } - } + qualityToName: function (quality) { + let qualNames = ["", "lowquality", "normal", "superior", "magic", "set", "rare", "unique", "crafted"]; + return qualNames[quality]; + }, + + color: function (unit, type) { + type === undefined && (type = true); + + if (type) { + switch (unit.itemType) { + case sdk.items.type.Gold: + return "ÿc4"; + case sdk.items.type.Rune: + return "ÿc8"; + case sdk.items.type.HealingPotion: + return "ÿc1"; + case sdk.items.type.ManaPotion: + return "ÿc3"; + case sdk.items.type.RejuvPotion: + return "ÿc;"; + } + } + + switch (unit.quality) { + case sdk.items.quality.Magic: + return "ÿc3"; + case sdk.items.quality.Set: + return "ÿc2"; + case sdk.items.quality.Rare: + return "ÿc9"; + case sdk.items.quality.Unique: + return "ÿc4"; + case sdk.items.quality.Crafted: + return "ÿc8"; + } + + return "ÿc0"; + }, + + hasTier: function (item) { + return Config.AutoEquip && NTIP.GetTier(item) > 0; + }, + + canEquip: function (item) { + // Not an item or unid + if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; + // Higher requirements + if (item.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) + || item.dexreq > me.getStat(sdk.stats.Dexterity) || item.strreq > me.getStat(sdk.stats.Strength)) { + return false; + } + return true; + }, + + // Equips an item and throws away the old equipped 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.isInStash && !Town.openStash()) return false; + + 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) { + if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { + cursorItem.drop(); + } + } + } + + return true; + } + } + } + + return false; + }, + + getEquippedItem: function (bodyLoc) { + let item = me.getItem(); + + if (item) { + do { + if (item.bodylocation === bodyLoc) { + return { + classid: item.classid, + tier: NTIP.GetTier(item) + }; + } + } while (item.getNext()); + } + + // Don't have anything equipped in there + return { + classid: -1, + tier: -1 + }; + }, + + getBodyLoc: function (item) { + let bodyLoc; + + switch (item.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.AuricShields: + case sdk.items.type.VoodooHeads: + case sdk.items.type.BowQuiver: + case sdk.items.type.CrossbowQuiver: + bodyLoc = sdk.body.LeftArm; + + break; + case sdk.items.type.Armor: + bodyLoc = sdk.body.Armor; + + break; + case sdk.items.type.Ring: + bodyLoc = [sdk.body.RingRight, sdk.body.RingLeft]; + + break; + case sdk.items.type.Amulet: + bodyLoc = sdk.body.Neck; + + break; + case sdk.items.type.Boots: + bodyLoc = sdk.body.Feet; + + break; + case sdk.items.type.Gloves: + bodyLoc = sdk.body.Gloves; + + break; + case sdk.items.type.Belt: + bodyLoc = sdk.body.Belt; + + break; + case sdk.items.type.Helm: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Circlet: + case sdk.items.type.Pelt: + bodyLoc = sdk.body.Head; + + break; + 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: + bodyLoc = me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; + + break; + case sdk.items.type.HandtoHand: + case sdk.items.type.AssassinClaw: + bodyLoc = me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; + + break; + default: + return false; + } + + !Array.isArray(bodyLoc) && (bodyLoc = [bodyLoc]); + + return bodyLoc; + }, + + autoEquipCheck: function (item) { + if (!Config.AutoEquip) return true; + + let tier = NTIP.GetTier(item); + let bodyLoc = this.getBodyLoc(item); + + if (tier > 0 && bodyLoc) { + for (let i = 0; i < bodyLoc.length; i += 1) { + // Low tier items shouldn't be kept if they can't be equipped + if (tier > this.getEquippedItem(bodyLoc[i]).tier + && (this.canEquip(item) || !item.getFlag(sdk.items.flags.Identified))) { + return true; + } + } + } + + // Sell/ignore low tier items, keep high tier + if (tier > 0 && tier < 100) return false; + + return true; + }, + + // returns true if the item should be kept+logged, false if not + autoEquip: function () { + if (!Config.AutoEquip) return true; + + let items = me.findItems(-1, sdk.items.mode.inStorage); + + if (!items) return false; + + function sortEq(a, b) { + if (Item.canEquip(a)) return -1; + if (Item.canEquip(b)) return 1; + + return 0; + } + + me.cancel(); + + // Remove items without tier + for (let i = 0; i < items.length; i += 1) { + if (NTIP.GetTier(items[i]) === 0) { + items.splice(i, 1); + + i -= 1; + } + } + + while (items.length > 0) { + items.sort(sortEq); + + let tier = NTIP.GetTier(items[0]); + let bodyLoc = this.getBodyLoc(items[0]); + + if (tier > 0 && bodyLoc) { + for (let j = 0; j < bodyLoc.length; j += 1) { + // khalim's will adjustment + const equippedItem = this.getEquippedItem(bodyLoc[j]); + if (items[0].isInStorage + && tier > equippedItem.tier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { + if (!items[0].identified) { + let tome = me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); + + if (tome && tome.getStat(sdk.stats.Quantity) > 0) { + items[0].isInStash && Town.openStash(); + Town.identifyItem(items[0], tome); + } + } + + let gid = items[0].gid; + console.log(items[0].name); + + if (this.equip(items[0], bodyLoc[j])) { + Item.logItem("Equipped", me.getItem(-1, -1, gid)); + } + + break; + } + } + } - items.shift(); - } + items.shift(); + } - return true; - }, + return true; + }, - getItemDesc: function (unit, logILvl = true) { - let stringColor = ""; - let desc = unit.description; + getItemDesc: function (unit, logILvl = true) { + let stringColor = ""; + let desc = unit.description; - if (!desc) return ""; - desc = desc.split("\n"); + if (!desc) return ""; + desc = desc.split("\n"); - // Lines are normally in reverse. Add color tags if needed and reverse order. - for (let i = 0; i < desc.length; i += 1) { - // Remove sell value - if (desc[i].includes(getLocaleString(sdk.locale.text.SellValue))) { - desc.splice(i, 1); + // Lines are normally in reverse. Add color tags if needed and reverse order. + for (let i = 0; i < desc.length; i += 1) { + // Remove sell value + if (desc[i].includes(getLocaleString(sdk.locale.text.SellValue))) { + desc.splice(i, 1); - i -= 1; - } else { - // Add color info - if (!desc[i].match(/^(y|ÿ)c/)) { - desc[i] = stringColor + desc[i]; - } + i -= 1; + } else { + // Add color info + if (!desc[i].match(/^(y|ÿ)c/)) { + desc[i] = stringColor + desc[i]; + } - // Find and store new color info - let index = desc[i].lastIndexOf("ÿc"); + // Find and store new color info + let index = desc[i].lastIndexOf("ÿc"); - if (index > -1) { - stringColor = desc[i].substring(index, index + "ÿ".length + 2); - } - } + if (index > -1) { + stringColor = desc[i].substring(index, index + "ÿ".length + 2); + } + } - desc[i] = desc[i].replace(/(y|ÿ)c([0-9!"+<:;.*])/g, "\\xffc$2"); - } + desc[i] = desc[i].replace(/(y|ÿ)c([0-9!"+<:;.*])/g, "\\xffc$2"); + } - if (logILvl && desc[desc.length - 1]) { - desc[desc.length - 1] = desc[desc.length - 1].trim() + " (" + unit.ilvl + ")"; - } + if (logILvl && desc[desc.length - 1]) { + desc[desc.length - 1] = desc[desc.length - 1].trim() + " (" + unit.ilvl + ")"; + } - desc = desc.reverse().join("\n"); + desc = desc.reverse().join("\n"); - return desc; - }, + return desc; + }, - getItemCode: function (unit) { - if (unit === undefined) return ""; + getItemCode: function (unit) { + if (unit === undefined) return ""; - let code = (() => { - switch (unit.quality) { - case sdk.items.quality.Set: - switch (unit.classid) { - case sdk.items.Sabre: - return "inv9sbu"; - case sdk.items.ShortWarBow: - return "invswbu"; - case sdk.items.Helm: - return "invhlmu"; - case sdk.items.LargeShield: - return "invlrgu"; - case sdk.items.LongSword: - case sdk.items.CrypticSword: - return "invlsdu"; - case sdk.items.SmallShield: - return "invsmlu"; - case sdk.items.Buckler: - return "invbucu"; - case sdk.items.Cap: - return "invcapu"; - case sdk.items.BroadSword: - return "invbsdu"; - case sdk.items.FullHelm: - return "invfhlu"; - case sdk.items.GothicShield: - return "invgtsu"; - case sdk.items.AncientArmor: - case sdk.items.SacredArmor: - return "invaaru"; - case sdk.items.KiteShield: - return "invkitu"; - case sdk.items.TowerShield: - return "invtowu"; - case sdk.items.FullPlateMail: - return "invfulu"; - case sdk.items.MilitaryPick: - return "invmpiu"; - case sdk.items.JaggedStar: - return "invmstu"; - case sdk.items.ColossusBlade: - return "invgsdu"; - case sdk.items.OrnatePlate: - return "invxaru"; - case sdk.items.Cuirass: - case sdk.items.ReinforcedMace: - case sdk.items.Ward: - case sdk.items.SpiredHelm: - return "inv" + unit.code + "s"; - case sdk.items.GrandCrown: - return "invxrnu"; - case sdk.items.ScissorsSuwayyah: - return "invskru"; - case sdk.items.GrimHelm: - case sdk.items.BoneVisage: - return "invbhmu"; - case sdk.items.ElderStaff: - return "invcstu"; - case sdk.items.RoundShield: - return "invxmlu"; - case sdk.items.BoneWand: - return "invbwnu"; - default: - return ""; - } - case sdk.items.quality.Unique: - for (let i = 0; i < 401; i += 1) { - if (unit.code === getBaseStat("uniqueitems", i, 4).trim() + let code = (() => { + switch (unit.quality) { + case sdk.items.quality.Set: + switch (unit.classid) { + case sdk.items.Sabre: + return "inv9sbu"; + case sdk.items.ShortWarBow: + return "invswbu"; + case sdk.items.Helm: + return "invhlmu"; + case sdk.items.LargeShield: + return "invlrgu"; + case sdk.items.LongSword: + case sdk.items.CrypticSword: + return "invlsdu"; + case sdk.items.SmallShield: + return "invsmlu"; + case sdk.items.Buckler: + return "invbucu"; + case sdk.items.Cap: + return "invcapu"; + case sdk.items.BroadSword: + return "invbsdu"; + case sdk.items.FullHelm: + return "invfhlu"; + case sdk.items.GothicShield: + return "invgtsu"; + case sdk.items.AncientArmor: + case sdk.items.SacredArmor: + return "invaaru"; + case sdk.items.KiteShield: + return "invkitu"; + case sdk.items.TowerShield: + return "invtowu"; + case sdk.items.FullPlateMail: + return "invfulu"; + case sdk.items.MilitaryPick: + return "invmpiu"; + case sdk.items.JaggedStar: + return "invmstu"; + case sdk.items.ColossusBlade: + return "invgsdu"; + case sdk.items.OrnatePlate: + return "invxaru"; + case sdk.items.Cuirass: + case sdk.items.ReinforcedMace: + case sdk.items.Ward: + case sdk.items.SpiredHelm: + return "inv" + unit.code + "s"; + case sdk.items.GrandCrown: + return "invxrnu"; + case sdk.items.ScissorsSuwayyah: + return "invskru"; + case sdk.items.GrimHelm: + case sdk.items.BoneVisage: + return "invbhmu"; + case sdk.items.ElderStaff: + return "invcstu"; + case sdk.items.RoundShield: + return "invxmlu"; + case sdk.items.BoneWand: + return "invbwnu"; + default: + return ""; + } + case sdk.items.quality.Unique: + for (let i = 0; i < 401; i += 1) { + if (unit.code === getBaseStat("uniqueitems", i, 4).trim() && unit.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat("uniqueitems", i, 2)))) { - return getBaseStat("uniqueitems", i, "invfile"); - } - } - return ""; - default: - return ""; - } - })(); - - if (!code) { - // Tiara/Diadem - code = ["ci2", "ci3"].includes(unit.code) ? unit.code : (getBaseStat("items", unit.classid, "normcode") || unit.code); - code = code.replace(" ", ""); - [sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(unit.itemType) && (code += (unit.gfx + 1)); - } - - return code; - }, - - getItemSockets: function (unit) { - let code; - let sockets = unit.sockets; - let subItems = unit.getItemsEx(); - let tempArray = []; - - if (subItems.length) { - switch (unit.sizex) { - case 2: - switch (unit.sizey) { - case 3: // 2 x 3 - switch (sockets) { - case 4: - tempArray = [subItems[0], subItems[3], subItems[2], subItems[1]]; - - break; - case 5: - tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; - - break; - case 6: - tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; - - break; - } - - break; - case 4: // 2 x 4 - switch (sockets) { - case 5: - tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; - - break; - case 6: - tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; - - break; - } - - break; - } - - break; - } - - if (tempArray.length === 0 && subItems.length > 0) { - tempArray = subItems.slice(0); - } - } - - for (let i = 0; i < sockets; i += 1) { - if (tempArray[i]) { - code = tempArray[i].code; - - if ([sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(tempArray[i].itemType)) { - code += (tempArray[i].gfx + 1); - } - } else { - code = "gemsocket"; - } - - tempArray[i] = code; - } - - return tempArray; - }, - - useItemLog: true, // Might be a bit dirty - - logger: function (action, unit, text) { - if (!Config.ItemInfo || !this.useItemLog) return false; - - let desc; - let date = new Date(); - let dateString = "[" + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; - - switch (action) { - case "Sold": - if (Config.ItemInfoQuality.indexOf(unit.quality) === -1) { - return false; - } - - desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]/gi, "").trim(); - - break; - case "Kept": - case "Field Kept": - case "Runeword Kept": - case "Cubing Kept": - case "Shopped": - case "Gambled": - case "Dropped": - desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); - - break; - case "No room for": - desc = unit.name; - - break; - default: - desc = unit.fname.split("\n").reverse().join(" ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); - - break; - } - - return FileAction.read("logs/ItemLog.txt", dateString + " <" + me.profile + "> <" + action + "> (" + Item.qualityToName(unit.quality) + ") " + desc + (text ? " {" + text + "}" : "") + "\n"); - }, - - // Log kept item stats in the manager. - logItem: function (action, unit, keptLine) { - if (!this.useItemLog) 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)); - } - - 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.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; - }, - - // skip low items: MuleLogger - skipItem: function (id) { - return [ - sdk.items.HandAxe, sdk.items.Wand, sdk.items.Club, sdk.items.ShortSword, sdk.items.Javelin, sdk.items.ShortStaff, sdk.items.Katar, - sdk.items.Buckler, sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, - sdk.items.ThawingPotion, sdk.items.TomeofTownPortal, sdk.items.TomeofIdentify, sdk.items.ScrollofIdentify, sdk.items.ScrollofTownPortal, - sdk.items.Key, 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 - ].includes(id); - }, + return getBaseStat("uniqueitems", i, "invfile"); + } + } + return ""; + default: + return ""; + } + })(); + + if (!code) { + // Tiara/Diadem + code = ["ci2", "ci3"].includes(unit.code) ? unit.code : (getBaseStat("items", unit.classid, "normcode") || unit.code); + code = code.replace(" ", ""); + [sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(unit.itemType) && (code += (unit.gfx + 1)); + } + + return code; + }, + + getItemSockets: function (unit) { + let code; + let sockets = unit.sockets; + let subItems = unit.getItemsEx(); + let tempArray = []; + + if (subItems.length) { + switch (unit.sizex) { + case 2: + switch (unit.sizey) { + case 3: // 2 x 3 + switch (sockets) { + case 4: + tempArray = [subItems[0], subItems[3], subItems[2], subItems[1]]; + + break; + case 5: + tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; + + break; + case 6: + tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; + + break; + } + + break; + case 4: // 2 x 4 + switch (sockets) { + case 5: + tempArray = [subItems[1], subItems[4], subItems[0], subItems[3], subItems[2]]; + + break; + case 6: + tempArray = [subItems[0], subItems[3], subItems[1], subItems[4], subItems[2], subItems[5]]; + + break; + } + + break; + } + + break; + } + + if (tempArray.length === 0 && subItems.length > 0) { + tempArray = subItems.slice(0); + } + } + + for (let i = 0; i < sockets; i += 1) { + if (tempArray[i]) { + code = tempArray[i].code; + + if ([sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(tempArray[i].itemType)) { + code += (tempArray[i].gfx + 1); + } + } else { + code = "gemsocket"; + } + + tempArray[i] = code; + } + + return tempArray; + }, + + useItemLog: true, // Might be a bit dirty + + logger: function (action, unit, text) { + if (!Config.ItemInfo || !this.useItemLog) return false; + + let desc; + let date = new Date(); + let dateString = "[" + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; + + switch (action) { + case "Sold": + if (Config.ItemInfoQuality.indexOf(unit.quality) === -1) { + return false; + } + + desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]/gi, "").trim(); + + break; + case "Kept": + case "Field Kept": + case "Runeword Kept": + case "Cubing Kept": + case "Shopped": + case "Gambled": + case "Dropped": + desc = this.getItemDesc(unit).split("\n").join(" | ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); + + break; + case "No room for": + desc = unit.name; + + break; + default: + desc = unit.fname.split("\n").reverse().join(" ").replace(/(\\xff|ÿ)c[0-9!"+<:;.*]|\/|\\/gi, "").trim(); + + break; + } + + return FileAction.read("logs/ItemLog.txt", dateString + " <" + me.profile + "> <" + action + "> (" + Item.qualityToName(unit.quality) + ") " + desc + (text ? " {" + text + "}" : "") + "\n"); + }, + + // Log kept item stats in the manager. + logItem: function (action, unit, keptLine) { + if (!this.useItemLog) 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)); + } + + 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.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; + }, + + // skip low items: MuleLogger + skipItem: function (id) { + return [ + sdk.items.HandAxe, sdk.items.Wand, sdk.items.Club, sdk.items.ShortSword, sdk.items.Javelin, sdk.items.ShortStaff, sdk.items.Katar, + sdk.items.Buckler, sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, + sdk.items.ThawingPotion, sdk.items.TomeofTownPortal, sdk.items.TomeofIdentify, sdk.items.ScrollofIdentify, sdk.items.ScrollofTownPortal, + sdk.items.Key, 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 + ].includes(id); + }, }; diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index b33f3e9f6..316cfee67 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -6,279 +6,291 @@ */ const Loader = { - fileList: [], - scriptList: [], - scriptIndex: -1, - skipTown: ["Test", "Follower"], - - init: function () { - this.getScripts(); - this.loadScripts(); - }, - - getScripts: function () { - let fileList = dopen("libs/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"))); - } - } - }, - - // see http://stackoverflow.com/questions/728360/copying-an-object-in-javascript#answer-728694 - clone: function (obj) { - let copy; - - // Handle the 3 simple types, and null or undefined - if (null === obj || "object" !== typeof obj) { - return obj; - } - - // Handle Date - if (obj instanceof Date) { - copy = new Date(); - copy.setTime(obj.getTime()); - - return copy; - } - - // Handle Array - if (obj instanceof Array) { - copy = []; - - for (let i = 0; i < obj.length; i += 1) { - copy[i] = this.clone(obj[i]); - } - - return copy; - } - - // Handle Object - if (obj instanceof Object) { - copy = {}; - - for (let attr in obj) { - if (obj.hasOwnProperty(attr)) { - copy[attr] = this.clone(obj[attr]); - } - } - - return copy; - } - - throw new Error("Unable to copy obj! Its type isn't supported."); - }, - - copy: function (from, to) { - for (let i in from) { - if (from.hasOwnProperty(i)) { - to[i] = this.clone(from[i]); - } - } - }, - - 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]) { - Loader.scriptList.push(s); - } - } - - // handle getting cube here instead of from Cubing.doCubing - if (Config.Cubing && !me.getItem(sdk.quest.item.Cube) && Pather.accessToAct(2)) { - // we can actually get the cube - fixes bug causing level 1's to crash - Loader.runScript("GetCube"); - } - - for (Loader.scriptIndex = 0; Loader.scriptIndex < Loader.scriptList.length; Loader.scriptIndex++) { - let script = this.scriptList[this.scriptIndex]; - - if (this.fileList.indexOf(script) === -1) { - if (FileTools.exists("scripts/" + 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("scripts/" + script + ".js")) { - Misc.errorReport("Failed to include script: " + script); - continue; - } - - if (isIncluded("scripts/" + script + ".js")) { - try { - if (typeof (global[script]) !== "function") { - throw new Error("Invalid script function name"); - } - - if (this.skipTown.includes(script) || Town.goToTown()) { - print("ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); - reconfiguration = typeof Scripts[script] === "object"; - - if (reconfiguration) { - print("ÿc2Copying Config properties from " + script + " object."); - this.copy(Scripts[script], Config); - } - - let tick = getTickCount(); - let exp = me.getStat(sdk.stats.Experience); - - if (me.inTown) { - Config.StackThawingPots.enabled && Town.buyPots(Config.StackThawingPots.quantity, sdk.items.ThawingPotion, true); - Config.StackAntidotePots.enabled && Town.buyPots(Config.StackAntidotePots.quantity, sdk.items.AntidotePotion, true); - Config.StackStaminaPots.enabled && Town.buyPots(Config.StackStaminaPots.quantity, sdk.items.StaminaPotion, true); - } - - // kinda hacky, but faster for mfhelpers to stop - if (Config.MFLeader && Config.PublicMode && ["Diablo", "Baal"].includes(script)) { - say("nextup " + script); - } - - if (global[script]()) { - let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); - let duration = Time.elapsed(tick); - console.log( - "ÿc7" + script + " :: ÿc0Complete\n" + fileList: [], + scriptList: [], + scriptIndex: -1, + skipTown: ["Test", "Follower"], + + init: function () { + this.getScripts(); + this.loadScripts(); + }, + + getScripts: function () { + let fileList = dopen("libs/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"))); + } + } + }, + + // see http://stackoverflow.com/questions/728360/copying-an-object-in-javascript#answer-728694 + clone: function (obj) { + let copy; + + // Handle the 3 simple types, and null or undefined + if (null === obj || "object" !== typeof obj) { + return obj; + } + + // Handle Date + if (obj instanceof Date) { + copy = new Date(); + copy.setTime(obj.getTime()); + + return copy; + } + + // Handle Array + if (obj instanceof Array) { + copy = []; + + for (let i = 0; i < obj.length; i += 1) { + copy[i] = this.clone(obj[i]); + } + + return copy; + } + + // Handle Object + if (obj instanceof Object) { + copy = {}; + + for (let attr in obj) { + if (obj.hasOwnProperty(attr)) { + copy[attr] = this.clone(obj[attr]); + } + } + + return copy; + } + + throw new Error("Unable to copy obj! Its type isn't supported."); + }, + + copy: function (from, to) { + for (let i in from) { + if (from.hasOwnProperty(i)) { + to[i] = this.clone(from[i]); + } + } + }, + + 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]) { + Loader.scriptList.push(s); + } + } + + // handle getting cube here instead of from Cubing.doCubing + if (Config.Cubing && !me.getItem(sdk.quest.item.Cube) && Pather.accessToAct(2)) { + // we can actually get the cube - fixes bug causing level 1's to crash + Loader.runScript("GetCube"); + } + + for (Loader.scriptIndex = 0; Loader.scriptIndex < Loader.scriptList.length; Loader.scriptIndex++) { + let script = this.scriptList[this.scriptIndex]; + + if (this.fileList.indexOf(script) === -1) { + if (FileTools.exists("scripts/" + 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("scripts/" + script + ".js")) { + Misc.errorReport("Failed to include script: " + script); + continue; + } + + if (isIncluded("scripts/" + script + ".js")) { + try { + if (typeof (global[script]) !== "function") { + throw new Error("Invalid script function name"); + } + + if (this.skipTown.includes(script) || Town.goToTown()) { + print("ÿc2Starting script: ÿc9" + script); + Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); + reconfiguration = typeof Scripts[script] === "object"; + + if (reconfiguration) { + print("ÿc2Copying Config properties from " + script + " object."); + this.copy(Scripts[script], Config); + } + + let tick = getTickCount(); + let exp = me.getStat(sdk.stats.Experience); + + if (me.inTown) { + if (Config.StackThawingPots.enabled) { + Town.buyPots(Config.StackThawingPots.quantity, sdk.items.ThawingPotion, true); + } + if (Config.StackAntidotePots.enabled) { + Town.buyPots(Config.StackAntidotePots.quantity, sdk.items.AntidotePotion, true); + } + if (Config.StackStaminaPots.enabled) { + Town.buyPots(Config.StackStaminaPots.quantity, sdk.items.StaminaPotion, true); + } + } + + // kinda hacky, but faster for mfhelpers to stop + if (Config.MFLeader && Config.PublicMode && ["Diablo", "Baal"].includes(script)) { + say("nextup " + script); + } + + if (global[script]()) { + let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); + let duration = Time.elapsed(tick); + console.log( + "ÿc7" + script + " :: ÿc0Complete\n" + "ÿc2 Statistics:\n" + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) - ); - } - } - } catch (error) { - if (!(error instanceof ScriptError)) { - 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]; - } + ); + } + } + } catch (error) { + if (!(error instanceof ScriptError)) { + 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) { - print("ÿc2Reverting back unmodified config properties."); - this.copy(unmodifiedConfig, Config); - } - } - } - } - }, - - tempList: [], - - runScript: function (script, configOverride) { - let reconfiguration, unmodifiedConfig = {}; - let failed = false; - let mainScript = this.scriptName(); + if (reconfiguration) { + print("ÿc2Reverting back unmodified config properties."); + this.copy(unmodifiedConfig, Config); + } + } + } + } + }, + + tempList: [], + + runScript: function (script, configOverride) { + let reconfiguration, unmodifiedConfig = {}; + let failed = false; + let mainScript = this.scriptName(); - function buildScriptMsg () { - let str = "ÿc9" + mainScript + " ÿc0:: "; + function buildScriptMsg () { + let str = "ÿc9" + mainScript + " ÿc0:: "; - if (Loader.tempList.length && Loader.tempList[0] !== mainScript) { - Loader.tempList.forEach(s => str += "ÿc9" + s + " ÿc0:: "); - } + if (Loader.tempList.length && Loader.tempList[0] !== mainScript) { + Loader.tempList.forEach(s => str += "ÿc9" + s + " ÿc0:: "); + } - return str; - } - - this.copy(Config, unmodifiedConfig); - - if (!include("scripts/" + script + ".js")) { - Misc.errorReport("Failed to include script: " + script); - - return false; - } - - if (isIncluded("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); - print(mainScriptStr + "ÿc2Starting script: ÿc9" + script); - Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); - - reconfiguration = typeof Scripts[script] === "object"; - - if (reconfiguration) { - print("ÿc2Copying Config properties from " + script + " object."); - this.copy(Scripts[script], Config); - } - - if (typeof configOverride === "function") { - reconfiguration = true; - configOverride(); - } - - let tick = getTickCount(); - let exp = me.getStat(sdk.stats.Experience); - - if (global[script]()) { - console.log(mainScriptStr + "ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick))); - let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); - let duration = Time.elapsed(tick); - console.log( - mainScriptStr + "ÿc7" + script + " :: ÿc0Complete\n" + return str; + } + + this.copy(Config, unmodifiedConfig); + + if (!include("scripts/" + script + ".js")) { + Misc.errorReport("Failed to include script: " + script); + + return false; + } + + if (isIncluded("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); + print(mainScriptStr + "ÿc2Starting script: ÿc9" + script); + Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); + + reconfiguration = typeof Scripts[script] === "object"; + + if (reconfiguration) { + print("ÿc2Copying Config properties from " + script + " object."); + this.copy(Scripts[script], Config); + } + + if (typeof configOverride === "function") { + reconfiguration = true; + configOverride(); + } + + let tick = getTickCount(); + let exp = me.getStat(sdk.stats.Experience); + + if (global[script]()) { + console.log( + mainScriptStr + "ÿc7" + script + + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick)) + ); + let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); + let duration = Time.elapsed(tick); + console.log( + mainScriptStr + "ÿc7" + script + " :: ÿc0Complete\n" + "ÿc2 Statistics:\n" + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) - ); - } - } - } catch (error) { - if (!(error instanceof ScriptError)) { - Misc.errorReport(error, script); - } - failed = true; - } 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]; - } else if (this.tempList.length) { - delete global[script]; - } - - this.tempList.pop(); + ); + } + } + } catch (error) { + if (!(error instanceof ScriptError)) { + Misc.errorReport(error, script); + } + failed = true; + } 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]; + } else if (this.tempList.length) { + delete global[script]; + } + + this.tempList.pop(); - if (reconfiguration) { - print("ÿc2Reverting back unmodified config properties."); - this.copy(unmodifiedConfig, Config); - } - } - } + if (reconfiguration) { + print("ÿc2Reverting back unmodified config properties."); + this.copy(unmodifiedConfig, Config); + } + } + } - return !failed; - }, + return !failed; + }, - scriptName: function (offset = 0) { - let index = this.scriptIndex + offset; + scriptName: function (offset = 0) { + let index = this.scriptIndex + offset; - if (index >= 0 && index < this.scriptList.length) { - return this.scriptList[index]; - } + if (index >= 0 && index < this.scriptList.length) { + return this.scriptList[index]; + } - return null; - } + return null; + } }; diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index e5bf70def..c4f103db8 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -28,11 +28,11 @@ me.run = () => me.runwalk = sdk.player.move.Run; * @returns {number} pingDelay */ me.getPingDelay = function () { - // single-player - if (!me.gameserverip) return 25; - let pingDelay = me.gameReady ? me.ping : 250; - pingDelay < 10 && (pingDelay = 50); - return pingDelay; + // single-player + if (!me.gameserverip) return 25; + let pingDelay = me.gameReady ? me.ping : 250; + pingDelay < 10 && (pingDelay = 50); + return pingDelay; }; /** @@ -44,51 +44,51 @@ me.getPingDelay = function () { * @returns {ItemUnit | false} */ me.findItem = function (id = -1, mode = -1, loc = -1, quality = -1) { - let item = me.getItem(id, mode); + let item = me.getItem(id, mode); - if (item) { - do { - if ((loc === -1 || item.location === loc) && (quality === -1 || item.quality === quality)) { - return item; - } - } while (item.getNext()); - } + if (item) { + do { + if ((loc === -1 || item.location === loc) && (quality === -1 || item.quality === quality)) { + return item; + } + } while (item.getNext()); + } - return false; + return false; }; me.findItems = function (id = -1, mode = -1, loc = false) { - let list = []; - let item = me.getItem(id, mode); - - if (item) { - do { - if (!loc || item.location === loc) { - list.push(copyUnit(item)); - } - } while (item.getNext()); - } - - return list; + let list = []; + let item = me.getItem(id, mode); + + if (item) { + do { + if (!loc || item.location === loc) { + list.push(copyUnit(item)); + } + } while (item.getNext()); + } + + return list; }; me.cancelUIFlags = function () { - while (!me.gameReady) { - delay(25); - } - - const flags = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, - sdk.uiflags.Waypoint, sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Stash, - sdk.uiflags.Cube, sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.SubmitItem - ]; - - for (let i = 0; i < flags.length; i++) { - if (getUIFlag(flags[i]) && me.cancel()) { - delay(250); - i = 0; // Reset - } - } + while (!me.gameReady) { + delay(25); + } + + const flags = [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, + sdk.uiflags.Waypoint, sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Stash, + sdk.uiflags.Cube, sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.SubmitItem + ]; + + for (let i = 0; i < flags.length; i++) { + if (getUIFlag(flags[i]) && me.cancel()) { + delay(250); + i = 0; // Reset + } + } }; /** @@ -96,173 +96,178 @@ me.cancelUIFlags = function () { * @returns {boolean} */ me.switchWeapons = function (slot) { - if (this.gametype === sdk.game.gametype.Classic || (slot !== undefined && this.weaponswitch === slot)) { - return true; - } - - while (!me.gameReady) { - delay(25); - } - - let originalSlot = this.weaponswitch; - let switched = false; - let packetHandler = (bytes) => bytes.length > 0 && bytes[0] === sdk.packets.recv.WeaponSwitch && (switched = true) && false; // false to not block - try { - addEventListener("gamepacket", packetHandler); - - for (let i = 0; i < 10; i += 1) { - for (let j = 10; --j && me.idle;) { - delay(3); - } - if (me.mode === sdk.player.mode.SkillActionSequence) { - while (me.mode === sdk.player.mode.SkillActionSequence) { - delay(3); - } - } - - i > 0 && delay(10); - !switched && sendPacket(1, sdk.packets.send.SwapWeapon); // Swap weapons - - let tick = getTickCount(); - while (getTickCount() - tick < 300) { - if (switched || originalSlot !== me.weaponswitch) { - delay(50); - return true; - } - - delay(3); - } - } - } finally { - removeEventListener("gamepacket", packetHandler); - } - - return false; + if (this.gametype === sdk.game.gametype.Classic || (slot !== undefined && this.weaponswitch === slot)) { + return true; + } + + while (!me.gameReady) { + delay(25); + } + + let originalSlot = this.weaponswitch; + let switched = false; + let packetHandler = (bytes) => bytes.length > 0 + && bytes[0] === sdk.packets.recv.WeaponSwitch && (switched = true) && false; // false to not block + try { + addEventListener("gamepacket", packetHandler); + + for (let i = 0; i < 10; i += 1) { + for (let j = 10; --j && me.idle;) { + delay(3); + } + if (me.mode === sdk.player.mode.SkillActionSequence) { + while (me.mode === sdk.player.mode.SkillActionSequence) { + delay(3); + } + } + + i > 0 && delay(10); + !switched && sendPacket(1, sdk.packets.send.SwapWeapon); // Swap weapons + + let tick = getTickCount(); + while (getTickCount() - tick < 300) { + if (switched || originalSlot !== me.weaponswitch) { + delay(50); + return true; + } + + delay(3); + } + } + } finally { + removeEventListener("gamepacket", packetHandler); + } + + return false; }; // Returns the number of frames needed to cast a given skill at a given FCR for a given char. me.castingFrames = function (skillId, fcr, charClass) { - if (skillId === undefined) return 0; - - fcr === undefined && (fcr = me.FCR); - charClass === undefined && (charClass = this.classid); - - // https://diablo.fandom.com/wiki/Faster_Cast_Rate - let effectiveFCR = Math.min(75, Math.floor(fcr * 120 / (fcr + 120)) | 0); - let isLightning = skillId === sdk.skills.Lightning || skillId === sdk.skills.ChainLightning; - let baseCastRate = [20, isLightning ? 19 : 14, 16, 16, 14, 15, 17][charClass]; - let animationSpeed = { - normal: 256, - human: 208, - wolf: 229, - bear: 228 - }[charClass === sdk.player.class.Druid ? (me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear)) : "normal"]; - return Math.ceil(256 * baseCastRate / Math.floor(animationSpeed * (100 + effectiveFCR) / 100) - (isLightning ? 0 : 1)); + if (skillId === undefined) return 0; + + fcr === undefined && (fcr = me.FCR); + charClass === undefined && (charClass = this.classid); + + // https://diablo.fandom.com/wiki/Faster_Cast_Rate + let effectiveFCR = Math.min(75, Math.floor(fcr * 120 / (fcr + 120)) | 0); + let isLightning = skillId === sdk.skills.Lightning || skillId === sdk.skills.ChainLightning; + let baseCastRate = [20, isLightning ? 19 : 14, 16, 16, 14, 15, 17][charClass]; + let animationSpeed = { + normal: 256, + human: 208, + wolf: 229, + bear: 228 + }[charClass === sdk.player.class.Druid ? (me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear)) : "normal"]; + return Math.ceil( + 256 * baseCastRate / Math.floor(animationSpeed * (100 + effectiveFCR) / 100) - (isLightning ? 0 : 1) + ); }; // Returns the duration in seconds needed to cast a given skill at a given FCR for a given char. me.castingDuration = function (skillId, fcr = me.FCR, charClass = me.classid) { - return (me.castingFrames(skillId, fcr, charClass) / 25); + return (me.castingFrames(skillId, fcr, charClass) / 25); }; 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.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; + // 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) { - const pots = { - hp: [], - mp: [], - }; - 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(p); - } else if (p.itemType === sdk.items.type.ManaPotion) { - pots.mp.push(p); - } - }); - - // quick check - if ((Config.BeltColumn.includes("hp") && !pots.hp.length) + // Start + if (me.charlvl > 2 && me.gold > 1000) { + const pots = { + hp: [], + mp: [], + }; + 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(p); + } else if (p.itemType === sdk.items.type.ManaPotion) { + pots.mp.push(p); + } + }); + + // quick check + if ((Config.BeltColumn.includes("hp") && !pots.hp.length) || (Config.BeltColumn.includes("mp") && !pots.mp.length)) { - return true; - } - - // if we have no belt what should qualify is to go to town at this point? - // we've confirmed having at least some potions in the above check - // if (!me.inTown && Storage.BeltSize() === 1) return false; - - // 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; + return true; + } + + // if we have no belt what should qualify is to go to town at this point? + // we've confirmed having at least some potions in the above check + // if (!me.inTown && Storage.BeltSize() === 1) return false; + + // 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; }; /** @returns {ItemUnit | null} */ me.getTpTool = function () { - const items = me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((item) => item.isInInventory && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(item.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); - return scroll ? scroll : null; + const items = me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((item) => item.isInInventory + && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(item.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); + return scroll ? scroll : null; }; /** @returns {ItemUnit | null} */ me.getIdTool = function () { - const items = me.getItemsEx() - .filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); - if (!items.length) return null; - let tome = items.find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify && i.getStat(sdk.stats.Quantity) > 0); - if (tome) return tome; - let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); - return scroll ? scroll : null; + const items = me.getItemsEx() + .filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); + if (!items.length) return null; + let tome = items + .find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify && i.getStat(sdk.stats.Quantity) > 0); + if (tome) return tome; + let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); + return scroll ? scroll : null; }; /** @returns {boolean} */ me.canTpToTown = function () { - // can't tp if dead - if (me.dead) return false; - 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(me.area)) 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 + if (me.dead) return false; + 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(me.area)) return false; + // If we made it this far, we can only tp if we even have a tp + return !!me.getTpTool(); }; /** @@ -270,17 +275,17 @@ me.canTpToTown = function () { * @returns {boolean} */ me.needHealing = function () { - if (me.hpPercent <= Config.HealHP || me.mpPercent <= Config.HealMP) 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((state) => me.getState(state))); + if (me.hpPercent <= Config.HealHP || me.mpPercent <= Config.HealMP) 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((state) => me.getState(state))); }; /** @@ -288,507 +293,510 @@ me.needHealing = function () { * @returns {ItemUnit | null} */ me.getTome = function (id) { - if (!id) return null; - let tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); - return tome ? tome : null; + if (!id) return null; + let tome = me.findItem(id, sdk.items.mode.inStorage, sdk.storage.Inventory); + return tome ? tome : null; }; me.getUnids = function () { - return me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((item) => item.isInInventory && !item.identified); + return me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((item) => item.isInInventory && !item.identified); }; // Identify items while in the field if we have a id tome me.fieldID = function () { - let list = me.getUnids(); - if (!list.length) return false; - - let tome = me.getTome(sdk.items.TomeofIdentify); - if (!tome || tome.getStat(sdk.stats.Quantity) < list.length) return false; - - while (list.length > 0) { - let item = list.shift(); - let result = Pickit.checkItem(item); - - // unid item that should be identified - if (result.result === Pickit.Result.UNID) { - Town.identifyItem(item, tome, Config.FieldID.PacketID); - delay(me.ping + 1); - result = Pickit.checkItem(item); - - switch (result.result) { - case Pickit.Result.UNWANTED: - Item.logger("Dropped", item, "fieldID"); - - if (Config.DroppedItemsAnnounce.Enable && Config.DroppedItemsAnnounce.Quality.includes(item.quality)) { - say("Dropped: [" + Item.qualityToName(item.quality).capitalize() + "] " + item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim()); - - if (Config.DroppedItemsAnnounce.LogToOOG && Config.DroppedItemsAnnounce.OOGQuality.includes(item.quality)) { - Item.logItem("Field Dropped", item, result.line); - } - } - - item.drop(); - - break; - case Pickit.Result.WANTED: - Item.logger("Field Kept", item); - Item.logItem("Field Kept", item, result.line); - - break; - default: - break; - } - } - } - - delay(200); - me.cancel(); - - return true; + let list = me.getUnids(); + if (!list.length) return false; + + let tome = me.getTome(sdk.items.TomeofIdentify); + if (!tome || tome.getStat(sdk.stats.Quantity) < list.length) return false; + + while (list.length > 0) { + let item = list.shift(); + let result = Pickit.checkItem(item); + + // unid item that should be identified + if (result.result === Pickit.Result.UNID) { + Town.identifyItem(item, tome, Config.FieldID.PacketID); + delay(me.ping + 1); + result = Pickit.checkItem(item); + + switch (result.result) { + case Pickit.Result.UNWANTED: + Item.logger("Dropped", item, "fieldID"); + + if (Config.DroppedItemsAnnounce.Enable && Config.DroppedItemsAnnounce.Quality.includes(item.quality)) { + say( + "Dropped: [" + Item.qualityToName(item.quality).capitalize() + "] " + + item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim() + ); + if (Config.DroppedItemsAnnounce.LogToOOG && Config.DroppedItemsAnnounce.OOGQuality.includes(item.quality)) { + Item.logItem("Field Dropped", item, result.line); + } + } + + item.drop(); + + break; + case Pickit.Result.WANTED: + Item.logger("Field Kept", item); + Item.logItem("Field Kept", item, result.line); + + break; + default: + break; + } + } + } + + delay(200); + me.cancel(); + + return true; }; me.switchToPrimary = function () { - if (me.classic) return true; - return me.switchWeapons(Attack.getPrimarySlot()); + if (me.classic) return true; + return me.switchWeapons(Attack.getPrimarySlot()); }; /** * Misc functions, stats/modes/states/ etc */ Object.defineProperties(me, { - maxNearMonsters: { - get: function () { - return Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); - }, - configurable: true - }, - inShop: { - get: function () { - if (getUIFlag(sdk.uiflags.Shop)) return true; - if (!Config.PacketShopping) return false; - let npc = getInteractedNPC(); - return !!(npc && npc.itemcount > 0); - } - }, - walking: { - get: function () { - return me.runwalk === sdk.player.move.Walk; - } - }, - running: { - get: function () { - return me.runwalk === sdk.player.move.Run; - } - }, - deadOrInSequence: { - get: function () { - return me.dead || me.mode === sdk.player.mode.SkillActionSequence; - } - }, - moving: { - get: function () { - return [sdk.player.mode.Walking, sdk.player.mode.Running, sdk.player.mode.WalkingInTown].includes(me.mode); - } - }, - staminaPercent: { - get: function () { - return Math.round((me.stamina / me.staminamax) * 100); - } - }, - staminaDrainPerSec: { - get: function () { - let bonusReduction = me.getStat(sdk.stats.StaminaRecoveryBonus); - let armorMalusReduction = 0; // TODO - return 25 * Math.max(40 * (1 + armorMalusReduction / 10) * (100 - bonusReduction) / 100, 1) / 256; - } - }, - staminaTimeLeft: { - get: function () { - return me.stamina / me.staminaDrainPerSec; - } - }, - staminaMaxDuration: { - get: function () { - return me.staminamax / me.staminaDrainPerSec; - } - }, - FCR: { - get: function () { - return me.getStat(sdk.stats.FCR) - (!!Config ? Config.FCR : 0); - } - }, - FHR: { - get: function () { - return me.getStat(sdk.stats.FHR) - (!!Config ? Config.FHR : 0); - } - }, - FBR: { - get: function () { - return me.getStat(sdk.stats.FBR) - (!!Config ? Config.FBR : 0); - } - }, - IAS: { - get: function () { - return me.getStat(sdk.stats.IAS) - (!!Config ? Config.IAS : 0); - } - }, - shapeshifted: { - get: function () { - return me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear) || me.getState(sdk.states.Delerium); - } - }, - mpPercent: { - get: function () { - return Math.round(me.mp * 100 / me.mpmax); - } - }, - skillDelay: { - get: function () { - return me.getState(sdk.states.SkillDelay); - } - }, + maxNearMonsters: { + get: function () { + return Math.floor((4 * (1 / me.hpmax * me.hp)) + 1); + }, + configurable: true + }, + inShop: { + get: function () { + if (getUIFlag(sdk.uiflags.Shop)) return true; + if (!Config.PacketShopping) return false; + let npc = getInteractedNPC(); + return !!(npc && npc.itemcount > 0); + } + }, + walking: { + get: function () { + return me.runwalk === sdk.player.move.Walk; + } + }, + running: { + get: function () { + return me.runwalk === sdk.player.move.Run; + } + }, + deadOrInSequence: { + get: function () { + return me.dead || me.mode === sdk.player.mode.SkillActionSequence; + } + }, + moving: { + get: function () { + return [sdk.player.mode.Walking, sdk.player.mode.Running, sdk.player.mode.WalkingInTown].includes(me.mode); + } + }, + staminaPercent: { + get: function () { + return Math.round((me.stamina / me.staminamax) * 100); + } + }, + staminaDrainPerSec: { + get: function () { + let bonusReduction = me.getStat(sdk.stats.StaminaRecoveryBonus); + let armorMalusReduction = 0; // TODO + return 25 * Math.max(40 * (1 + armorMalusReduction / 10) * (100 - bonusReduction) / 100, 1) / 256; + } + }, + staminaTimeLeft: { + get: function () { + return me.stamina / me.staminaDrainPerSec; + } + }, + staminaMaxDuration: { + get: function () { + return me.staminamax / me.staminaDrainPerSec; + } + }, + FCR: { + get: function () { + return me.getStat(sdk.stats.FCR) - (!!Config ? Config.FCR : 0); + } + }, + FHR: { + get: function () { + return me.getStat(sdk.stats.FHR) - (!!Config ? Config.FHR : 0); + } + }, + FBR: { + get: function () { + return me.getStat(sdk.stats.FBR) - (!!Config ? Config.FBR : 0); + } + }, + IAS: { + get: function () { + return me.getStat(sdk.stats.IAS) - (!!Config ? Config.IAS : 0); + } + }, + shapeshifted: { + get: function () { + return me.getState(sdk.states.Wolf) || me.getState(sdk.states.Bear) || me.getState(sdk.states.Delerium); + } + }, + mpPercent: { + get: function () { + return Math.round(me.mp * 100 / me.mpmax); + } + }, + skillDelay: { + get: function () { + return me.getState(sdk.states.SkillDelay); + } + }, }); /** * Game type, difficulty, classtype, etc */ Object.defineProperties(me, { - classic: { - get: function () { - return me.gametype === sdk.game.gametype.Classic; - } - }, - expansion: { - get: function () { - return me.gametype === sdk.game.gametype.Expansion; - } - }, - softcore: { - get: function () { - return me.playertype === false; - } - }, - hardcore: { - get: function () { - return me.playertype === true; - } - }, - normal: { - get: function () { - return me.diff === sdk.difficulty.Normal; - } - }, - nightmare: { - get: function () { - return me.diff === sdk.difficulty.Nightmare; - } - }, - hell: { - get: function () { - return me.diff === sdk.difficulty.Hell; - } - }, - amazon: { - get: function () { - return me.classid === sdk.player.class.Amazon; - } - }, - sorceress: { - get: function () { - return me.classid === sdk.player.class.Sorceress; - } - }, - necromancer: { - get: function () { - return me.classid === sdk.player.class.Necromancer; - } - }, - paladin: { - get: function () { - return me.classid === sdk.player.class.Paladin; - } - }, - barbarian: { - get: function () { - return me.classid === sdk.player.class.Barbarian; - } - }, - druid: { - get: function () { - return me.classid === sdk.player.class.Druid; - } - }, - assassin: { - get: function () { - return me.classid === sdk.player.class.Assassin; - } - }, + classic: { + get: function () { + return me.gametype === sdk.game.gametype.Classic; + } + }, + expansion: { + get: function () { + return me.gametype === sdk.game.gametype.Expansion; + } + }, + softcore: { + get: function () { + return me.playertype === false; + } + }, + hardcore: { + get: function () { + return me.playertype === true; + } + }, + normal: { + get: function () { + return me.diff === sdk.difficulty.Normal; + } + }, + nightmare: { + get: function () { + return me.diff === sdk.difficulty.Nightmare; + } + }, + hell: { + get: function () { + return me.diff === sdk.difficulty.Hell; + } + }, + amazon: { + get: function () { + return me.classid === sdk.player.class.Amazon; + } + }, + sorceress: { + get: function () { + return me.classid === sdk.player.class.Sorceress; + } + }, + necromancer: { + get: function () { + return me.classid === sdk.player.class.Necromancer; + } + }, + paladin: { + get: function () { + return me.classid === sdk.player.class.Paladin; + } + }, + barbarian: { + get: function () { + return me.classid === sdk.player.class.Barbarian; + } + }, + druid: { + get: function () { + return me.classid === sdk.player.class.Druid; + } + }, + assassin: { + get: function () { + return me.classid === sdk.player.class.Assassin; + } + }, }); /** * Quest items */ Object.defineProperties(me, { - wirtsleg: { - get: function () { - return me.getItem(sdk.quest.item.WirtsLeg); - } - }, - cube: { - get: function () { - return me.getItem(sdk.quest.item.Cube); - } - }, - shaft: { - get: function () { - return me.getItem(sdk.quest.item.ShaftoftheHoradricStaff); - } - }, - amulet: { - get: function () { - return me.getItem(sdk.quest.item.ViperAmulet); - } - }, - staff: { - get: function () { - return me.getItem(sdk.quest.item.HoradricStaff); - } - }, - completestaff: { - get: function () { - return me.getItem(sdk.quest.item.HoradricStaff); - } - }, - eye: { - get: function () { - return me.getItem(sdk.items.quest.KhalimsEye); - } - }, - brain: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsBrain); - } - }, - heart: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsHeart); - } - }, - khalimswill: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsWill); - } - }, - khalimsflail: { - get: function () { - return me.getItem(sdk.quest.item.KhalimsFlail); - } - }, - malahspotion: { - get: function () { - return me.getItem(sdk.quest.item.MalahsPotion); - } - }, - scrollofresistance: { - get: function () { - return me.getItem(sdk.quest.item.ScrollofResistance); - } - }, + wirtsleg: { + get: function () { + return me.getItem(sdk.quest.item.WirtsLeg); + } + }, + cube: { + get: function () { + return me.getItem(sdk.quest.item.Cube); + } + }, + shaft: { + get: function () { + return me.getItem(sdk.quest.item.ShaftoftheHoradricStaff); + } + }, + amulet: { + get: function () { + return me.getItem(sdk.quest.item.ViperAmulet); + } + }, + staff: { + get: function () { + return me.getItem(sdk.quest.item.HoradricStaff); + } + }, + completestaff: { + get: function () { + return me.getItem(sdk.quest.item.HoradricStaff); + } + }, + eye: { + get: function () { + return me.getItem(sdk.items.quest.KhalimsEye); + } + }, + brain: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsBrain); + } + }, + heart: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsHeart); + } + }, + khalimswill: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsWill); + } + }, + khalimsflail: { + get: function () { + return me.getItem(sdk.quest.item.KhalimsFlail); + } + }, + malahspotion: { + get: function () { + return me.getItem(sdk.quest.item.MalahsPotion); + } + }, + scrollofresistance: { + get: function () { + return me.getItem(sdk.quest.item.ScrollofResistance); + } + }, }); /** * Quests */ (function () { - const QuestData = require("./GameData/QuestData"); + const QuestData = require("./GameData/QuestData"); - /** + /** * @param {number} act * @returns {boolean} */ - me.accessToAct = function (act) { - if (act === 1) return true; - return me.highestAct >= act; - }; - - Object.defineProperties(me, { - highestAct: { - get: function () { - let acts = [true, - QuestData.get(sdk.quest.id.AbleToGotoActII).complete(), - QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(), - QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(), - QuestData.get(sdk.quest.id.AbleToGotoActV).complete()]; - let index = acts.findIndex((i) => !i); // find first false, returns between 1 and 5 - return index === -1 ? 5 : index; - } - }, - highestQuestDone: { - get: function () { - for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { - if (QuestData.get(i).complete()) return i; - - // check if we've completed main part but not used our reward - if ([sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade].includes(i) - && QuestData.get(i).complete(true)) { - return i; - } - } - return undefined; - } - }, - den: { - get: function () { - return QuestData.get(sdk.quest.id.DenofEvil).complete(); - } - }, - bloodraven: { - get: function () { - return QuestData.get(sdk.quest.id.SistersBurialGrounds).complete(); - } - }, - smith: { - get: function () { - return QuestData.get(sdk.quest.id.ToolsoftheTrade).complete(); - } - }, - imbue: { - get: function () { - return QuestData.get(sdk.quest.id.ToolsoftheTrade).checkState(sdk.quest.states.ReqComplete, true); - } - }, - cain: { - get: function () { - return QuestData.get(sdk.quest.id.TheSearchForCain).complete(); - } - }, - tristram: { - get: function () { - // update where this is used and change the state to be portal opened and me.cain to be quest completed - return QuestData.get(sdk.quest.id.TheSearchForCain).complete(); - } - }, - countess: { - get: function () { - return QuestData.get(sdk.quest.id.ForgottenTower).complete(); - } - }, - andariel: { - get: function () { - return QuestData.get(sdk.quest.id.AbleToGotoActII).complete(); - } - }, - radament: { - get: function () { - return QuestData.get(sdk.quest.id.RadamentsLair).complete(); - } - }, - horadricstaff: { - get: function () { - return QuestData.get(sdk.quest.id.TheHoradricStaff).complete(); - } - }, - summoner: { - get: function () { - return QuestData.get(sdk.quest.id.TheSummoner).complete(); - } - }, - duriel: { - get: function () { - return QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(); - } - }, - goldenbird: { - get: function () { - return QuestData.get(sdk.quest.id.TheGoldenBird).complete(); - } - }, - lamessen: { - get: function () { - return QuestData.get(sdk.quest.id.LamEsensTome).complete(); - } - }, - gidbinn: { - get: function () { - return QuestData.get(sdk.quest.id.BladeoftheOldReligion).complete(); - } - }, - travincal: { - get: function () { - return QuestData.get(sdk.quest.id.KhalimsWill).complete(); - } - }, - mephisto: { - get: function () { - return QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(); - } - }, - izual: { - get: function () { - return QuestData.get(sdk.quest.id.TheFallenAngel).complete(); - } - }, - hellforge: { - get: function () { - return QuestData.get(sdk.quest.id.HellsForge).complete(); - } - }, - diablo: { - get: function () { - return QuestData.get(sdk.quest.id.TerrorsEnd).complete(); - } - }, - shenk: { - get: function () { - return QuestData.get(sdk.quest.id.SiegeOnHarrogath).complete(true); - } - }, - larzuk: { - get: function () { - return QuestData.get(sdk.quest.id.SiegeOnHarrogath).checkState(sdk.quest.states.ReqComplete, true); - } - }, - savebarby: { - get: function () { - return QuestData.get(sdk.quest.id.RescueonMountArreat).complete(); - } - }, - barbrescue: { - get: function () { - return QuestData.get(sdk.quest.id.RescueonMountArreat).complete(); - } - }, - anya: { - get: function () { - return QuestData.get(sdk.quest.id.PrisonofIce).complete(); - } - }, - ancients: { - get: function () { - return QuestData.get(sdk.quest.id.RiteofPassage).complete(); - } - }, - baal: { - get: function () { - return QuestData.get(sdk.quest.id.EyeofDestruction).complete(); - } - }, - // Misc - cows: { - get: function () { - return me.getQuest(sdk.quest.id.TheSearchForCain, 10); - } - }, - respec: { - get: function () { - return QuestData.get(sdk.quest.id.Respec).complete(); - } - }, - diffCompleted: { - get: function () { - return !!((me.classic && me.diablo) || me.baal); - } - }, - }); + me.accessToAct = function (act) { + if (act === 1) return true; + return me.highestAct >= act; + }; + + Object.defineProperties(me, { + highestAct: { + get: function () { + let acts = [true, + QuestData.get(sdk.quest.id.AbleToGotoActII).complete(), + QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(), + QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(), + QuestData.get(sdk.quest.id.AbleToGotoActV).complete()]; + let index = acts.findIndex((i) => !i); // find first false, returns between 1 and 5 + return index === -1 ? 5 : index; + } + }, + highestQuestDone: { + get: function () { + for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { + if (QuestData.get(i).complete()) return i; + + // check if we've completed main part but not used our reward + if ([ + sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade + ].includes(i) && QuestData.get(i).complete(true)) { + return i; + } + } + return undefined; + } + }, + den: { + get: function () { + return QuestData.get(sdk.quest.id.DenofEvil).complete(); + } + }, + bloodraven: { + get: function () { + return QuestData.get(sdk.quest.id.SistersBurialGrounds).complete(); + } + }, + smith: { + get: function () { + return QuestData.get(sdk.quest.id.ToolsoftheTrade).complete(); + } + }, + imbue: { + get: function () { + return QuestData.get(sdk.quest.id.ToolsoftheTrade).checkState(sdk.quest.states.ReqComplete, true); + } + }, + cain: { + get: function () { + return QuestData.get(sdk.quest.id.TheSearchForCain).complete(); + } + }, + tristram: { + get: function () { + // update where this is used and change the state to be portal opened and me.cain to be quest completed + return QuestData.get(sdk.quest.id.TheSearchForCain).complete(); + } + }, + countess: { + get: function () { + return QuestData.get(sdk.quest.id.ForgottenTower).complete(); + } + }, + andariel: { + get: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActII).complete(); + } + }, + radament: { + get: function () { + return QuestData.get(sdk.quest.id.RadamentsLair).complete(); + } + }, + horadricstaff: { + get: function () { + return QuestData.get(sdk.quest.id.TheHoradricStaff).complete(); + } + }, + summoner: { + get: function () { + return QuestData.get(sdk.quest.id.TheSummoner).complete(); + } + }, + duriel: { + get: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(); + } + }, + goldenbird: { + get: function () { + return QuestData.get(sdk.quest.id.TheGoldenBird).complete(); + } + }, + lamessen: { + get: function () { + return QuestData.get(sdk.quest.id.LamEsensTome).complete(); + } + }, + gidbinn: { + get: function () { + return QuestData.get(sdk.quest.id.BladeoftheOldReligion).complete(); + } + }, + travincal: { + get: function () { + return QuestData.get(sdk.quest.id.KhalimsWill).complete(); + } + }, + mephisto: { + get: function () { + return QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(); + } + }, + izual: { + get: function () { + return QuestData.get(sdk.quest.id.TheFallenAngel).complete(); + } + }, + hellforge: { + get: function () { + return QuestData.get(sdk.quest.id.HellsForge).complete(); + } + }, + diablo: { + get: function () { + return QuestData.get(sdk.quest.id.TerrorsEnd).complete(); + } + }, + shenk: { + get: function () { + return QuestData.get(sdk.quest.id.SiegeOnHarrogath).complete(true); + } + }, + larzuk: { + get: function () { + return QuestData.get(sdk.quest.id.SiegeOnHarrogath).checkState(sdk.quest.states.ReqComplete, true); + } + }, + savebarby: { + get: function () { + return QuestData.get(sdk.quest.id.RescueonMountArreat).complete(); + } + }, + barbrescue: { + get: function () { + return QuestData.get(sdk.quest.id.RescueonMountArreat).complete(); + } + }, + anya: { + get: function () { + return QuestData.get(sdk.quest.id.PrisonofIce).complete(); + } + }, + ancients: { + get: function () { + return QuestData.get(sdk.quest.id.RiteofPassage).complete(); + } + }, + baal: { + get: function () { + return QuestData.get(sdk.quest.id.EyeofDestruction).complete(); + } + }, + // Misc + cows: { + get: function () { + return me.getQuest(sdk.quest.id.TheSearchForCain, 10); + } + }, + respec: { + get: function () { + return QuestData.get(sdk.quest.id.Respec).complete(); + } + }, + diffCompleted: { + get: function () { + return !!((me.classic && me.diablo) || me.baal); + } + }, + }); })(); diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 58b47d814..ed600a559 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -7,7 +7,7 @@ */ const Misc = { - /** + /** * Click something * @param {number} button * @param {number} shift @@ -15,496 +15,511 @@ const Misc = { * @param {number} [y] * @returns {boolean} */ - click: function (button, shift, x, y) { - if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); - - while (!me.gameReady) { - delay(100); - } - - switch (arguments.length) { - case 2: - me.blockMouse = true; - clickMap(button, shift, me.x, me.y); - delay(20); - clickMap(button + 2, shift, me.x, me.y); - me.blockMouse = false; - - break; - case 3: - if (typeof (x) !== "object") throw new Error("Misc.click: Third arg must be a Unit."); - - me.blockMouse = true; - clickMap(button, shift, x); - delay(20); - clickMap(button + 2, shift, x); - me.blockMouse = false; - - break; - case 4: - me.blockMouse = true; - clickMap(button, shift, x, y); - delay(20); - clickMap(button + 2, shift, x, y); - me.blockMouse = false; - - break; - } - - return true; - }, - - /** + click: function (button, shift, x, y) { + if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); + + while (!me.gameReady) { + delay(100); + } + + switch (arguments.length) { + case 2: + me.blockMouse = true; + clickMap(button, shift, me.x, me.y); + delay(20); + clickMap(button + 2, shift, me.x, me.y); + me.blockMouse = false; + + break; + case 3: + if (typeof (x) !== "object") throw new Error("Misc.click: Third arg must be a Unit."); + + me.blockMouse = true; + clickMap(button, shift, x); + delay(20); + clickMap(button + 2, shift, x); + me.blockMouse = false; + + break; + case 4: + me.blockMouse = true; + clickMap(button, shift, x, y); + delay(20); + clickMap(button + 2, shift, x, y); + me.blockMouse = false; + + break; + } + + return true; + }, + + /** * Check if a player is in your party * @param {string} name * @returns {boolean} */ - inMyParty: function (name) { - if (me.name === name) return true; - - while (!me.gameReady) { - delay(100); - } - - let player, myPartyId; - - try { - player = getParty(); - if (!player) return false; - - myPartyId = player.partyid; - player = getParty(name); // May throw an error - - if (player && player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { - return true; - } - } catch (e) { - player = getParty(); - - if (player) { - myPartyId = player.partyid; - - while (player.getNext()) { - if (player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { - return true; - } - } - } - } - - return false; - }, - - // Find a player - findPlayer: function (name) { - let player = getParty(); - - if (player) { - do { - if (player.name !== me.name && player.name === name) { - return player; - } - } while (player.getNext()); - } - - return false; - }, - - // Get player unit - getPlayerUnit: function (name) { - let player = Game.getPlayer(name); - - if (player) { - do { - if (!player.dead) { - return player; - } - } while (player.getNext()); - } - - return false; - }, - - // Get the player act, accepts party unit or name - getPlayerAct: function (player) { - if (!player) return false; - - let unit = (typeof player === "object" ? player : this.findPlayer(player)); - - return unit ? sdk.areas.actOf(unit.area) : false; - }, - - // Get number of players within getUnit distance - getNearbyPlayerCount: function () { - let count = 0; - let player = Game.getPlayer(); - - if (player) { - do { - if (player.name !== me.name && !player.dead) { - count += 1; - } - } while (player.getNext()); - } - - return count; - }, - - // Get total number of players in game - getPlayerCount: function () { - let count = 0; - let party = getParty(); - - if (party) { - do { - count += 1; - } while (party.getNext()); - } - - return count; - }, - - // Get total number of players in game and in my party - getPartyCount: function () { - let count = 0; - let party = getParty(); - - if (party) { - let myPartyId = party.partyid; + inMyParty: function (name) { + if (me.name === name) return true; + + while (!me.gameReady) { + delay(100); + } + + let player, myPartyId; + + try { + player = getParty(); + if (!player) return false; + + myPartyId = player.partyid; + player = getParty(name); // May throw an error + + if (player && player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { + return true; + } + } catch (e) { + player = getParty(); + + if (player) { + myPartyId = player.partyid; + + while (player.getNext()) { + if (player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { + return true; + } + } + } + } + + return false; + }, + + // Find a player + findPlayer: function (name) { + let player = getParty(); + + if (player) { + do { + if (player.name !== me.name && player.name === name) { + return player; + } + } while (player.getNext()); + } + + return false; + }, + + // Get player unit + getPlayerUnit: function (name) { + let player = Game.getPlayer(name); + + if (player) { + do { + if (!player.dead) { + return player; + } + } while (player.getNext()); + } + + return false; + }, + + // Get the player act, accepts party unit or name + getPlayerAct: function (player) { + if (!player) return false; + + let unit = (typeof player === "object" ? player : this.findPlayer(player)); + + return unit ? sdk.areas.actOf(unit.area) : false; + }, + + // Get number of players within getUnit distance + getNearbyPlayerCount: function () { + let count = 0; + let player = Game.getPlayer(); + + if (player) { + do { + if (player.name !== me.name && !player.dead) { + count += 1; + } + } while (player.getNext()); + } + + return count; + }, + + // Get total number of players in game + getPlayerCount: function () { + let count = 0; + let party = getParty(); + + if (party) { + do { + count += 1; + } while (party.getNext()); + } + + return count; + }, + + // Get total number of players in game and in my party + getPartyCount: function () { + let count = 0; + let party = getParty(); + + if (party) { + let myPartyId = party.partyid; - do { - if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name) { - print(party.name); - count += 1; - } - } while (party.getNext()); - } - - return count; - }, - - // check if any member of our party meets a certain level req - checkPartyLevel: function (levelCheck = 1, exclude = []) { - !Array.isArray(exclude) && (exclude = [exclude]); - let party = getParty(); - - if (party) { - let myPartyId = party.partyid; - - do { - if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name && !exclude.includes(party.name)) { - if (party.level >= levelCheck) { - return true; - } - } - } while (party.getNext()); - } - - return false; - }, - - getPlayerArea: function (player) { - if (!player) return false; - - let unit = (typeof player === "object" ? player : this.findPlayer(player)); - - return !!unit ? unit.area : 0; - }, - - /** + do { + if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name) { + print(party.name); + count += 1; + } + } while (party.getNext()); + } + + return count; + }, + + // check if any member of our party meets a certain level req + checkPartyLevel: function (levelCheck = 1, exclude = []) { + !Array.isArray(exclude) && (exclude = [exclude]); + let party = getParty(); + + if (party) { + let myPartyId = party.partyid; + + do { + if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId + && party.name !== me.name && !exclude.includes(party.name)) { + if (party.level >= levelCheck) { + return true; + } + } + } while (party.getNext()); + } + + return false; + }, + + getPlayerArea: function (player) { + if (!player) return false; + + let unit = (typeof player === "object" ? player : this.findPlayer(player)); + + return !!unit ? unit.area : 0; + }, + + /** * autoleader by Ethic - refactored by theBGuy * Autodetect leader for leech scripts by looking to see who first enters a certain area * @param {{ destination: number | number[], quitIf?: Function, timeout?: number }} givenSettings * @returns */ - autoLeaderDetect: function (givenSettings = {}) { - const settings = Object.assign({}, { - destination: -1, - quitIf: false, - timeout: Infinity - }, givenSettings); - - // make destination an array so it's easier to handle both cases - !Array.isArray(settings.destination) && (settings.destination = [settings.destination]); - - let leader; - let startTick = getTickCount(); - let check = typeof settings.quitIf === "function"; - do { - let solofail = 0; - let suspect = getParty(); // get party object (players in game) - - do { - // player isn't alone - suspect.name !== me.name && (solofail += 1); - - if (check && settings.quitIf(suspect.area)) return false; - - // first player not hostile found in destination area... - if (settings.destination.includes(suspect.area) && !getPlayerFlag(me.gid, suspect.gid, 8)) { - leader = suspect.name; // ... is our leader - console.log("ÿc4Autodetected " + leader); - - return leader; - } - } while (suspect.getNext()); - - // empty game, nothing left to do. Or we exceeded our wait time - if (solofail === 0 || (getTickCount() - startTick > settings.timeout)) { - return false; - } - - delay(500); - } while (!leader); // repeat until leader is found (or until game is empty) - - return false; - }, - - /** + autoLeaderDetect: function (givenSettings = {}) { + const settings = Object.assign({}, { + destination: -1, + quitIf: false, + timeout: Infinity + }, givenSettings); + + // make destination an array so it's easier to handle both cases + !Array.isArray(settings.destination) && (settings.destination = [settings.destination]); + + let leader; + let startTick = getTickCount(); + let check = typeof settings.quitIf === "function"; + do { + let solofail = 0; + let suspect = getParty(); // get party object (players in game) + + do { + // player isn't alone + suspect.name !== me.name && (solofail += 1); + + if (check && settings.quitIf(suspect.area)) return false; + + // first player not hostile found in destination area... + if (settings.destination.includes(suspect.area) && !getPlayerFlag(me.gid, suspect.gid, 8)) { + leader = suspect.name; // ... is our leader + console.log("ÿc4Autodetected " + leader); + + return leader; + } + } while (suspect.getNext()); + + // empty game, nothing left to do. Or we exceeded our wait time + if (solofail === 0 || (getTickCount() - startTick > settings.timeout)) { + return false; + } + + delay(500); + } while (!leader); // repeat until leader is found (or until game is empty) + + return false; + }, + + /** * @description Open a chest Unit (takes chestID or unit) * @param {Unit | number} unit * @returns {boolean} If we opened the chest */ - openChest: function (unit) { - typeof unit === "number" && (unit = Game.getObject(unit)); + 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); - if (useTK) { - unit.distance > 13 && Attack.getIntoPosition(unit, 13, sdk.collision.WallOrRanged); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { - console.debug("Failed to tk: attempt: " + i); - continue; - } - } else { - [(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.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; - }, - - // Open all chests that have preset units in an area - openChestsInArea: function (area, chestIds = []) { - !area && (area = me.area); - area !== me.area && Pather.journeyTo(area); + // 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); + if (useTK) { + unit.distance > 13 && Attack.getIntoPosition(unit, 13, sdk.collision.WallOrRanged); + if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { + console.debug("Failed to tk: attempt: " + i); + continue; + } + } else { + [(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.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; + }, + + // Open all chests that have preset units in an area + openChestsInArea: function (area, chestIds = []) { + !area && (area = me.area); + area !== me.area && Pather.journeyTo(area); - let presetUnits = Game.getPresetObjects(area); - if (!presetUnits) return false; - - if (!chestIds.length) { - chestIds = [ - 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 - ]; - } - - 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.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; - }, - - openChests: function (range = 15) { - if (!Config.OpenChests.Enabled) return true; - - let unitList = []; - let containers = []; - - // Testing all container code - if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { - containers = [ - "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", "casket", "armorstand", "weaponrack", "barrel", "holeanim", "tomb2", - "tomb3", "roguecorpse", "ratnest", "corpse", "goo pile", "largeurn", "urn", "chest3", "jug", "skeleton", "guardcorpse", "sarcophagus", "object2", - "cocoon", "basket", "stash", "hollow log", "hungskeleton", "pillar", "skullpile", "skull pile", "jar3", "jar2", "jar1", "bonechest", "woodchestl", - "woodchestr", "barrel wilderness", "burialchestr", "burialchestl", "explodingchest", "chestl", "chestr", "groundtomb", "icecavejar1", "icecavejar2", - "icecavejar3", "icecavejar4", "deadperson", "deadperson2", "evilurn", "tomb1l", "tomb3l", "groundtombl" - ]; - } else { - containers = Config.OpenChests.Types; - } - - let unit = Game.getObject(); - - if (unit) { - do { - if (unit.name && unit.mode === sdk.objects.mode.Inactive && getDistance(me.x, me.y, unit.x, unit.y) <= range && containers.includes(unit.name.toLowerCase())) { - unitList.push(copyUnit(unit)); - } - } while (unit.getNext()); - } - - while (unitList.length > 0) { - unitList.sort(Sort.units); - unit = unitList.shift(); - - if (unit) { - const chest = Game.getObject(-1, -1, unit.gid); - if (chest && (Pather.useTeleport() || !checkCollision(me, chest, sdk.collision.WallOrRanged)) && this.openChest(chest)) { - Pickit.pickItems(); - } - } - } - - return true; - }, - - shrineStates: false, - - scanShrines: function (range, ignore = []) { - if (!Config.ScanShrines.length) return false; - - !range && (range = Pather.useTeleport() ? 25 : 15); - !Array.isArray(ignore) && (ignore = [ignore]); - - let shrineList = []; - - // Initiate shrine states - if (!this.shrineStates) { - Misc.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 shrine = Game.getObject("shrine"); - - if (shrine) { - let index = -1; - // Build a list of nearby shrines - do { - if (shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) && getDistance(me.x, me.y, shrine.x, shrine.y) <= range) { - shrineList.push(copyUnit(shrine)); - } - } while (shrine.getNext()); - - // 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 j = 0; j < shrineList.length; j += 1) { - // 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]); - - // Gem shrine - pick gem - if (Config.ScanShrines[i] === sdk.shrines.Gem) { - Pickit.pickItems(); - } - } - } - } - } - } - - return true; - }, - - // Use a shrine Unit - getShrine: function (unit) { - if (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); - !Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, 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)) { - return true; - } - } - - return false; - }, - - /** + let presetUnits = Game.getPresetObjects(area); + if (!presetUnits) return false; + + if (!chestIds.length) { + chestIds = [ + 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 + ]; + } + + 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.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; + }, + + openChests: function (range = 15) { + if (!Config.OpenChests.Enabled) return true; + + let unitList = []; + let containers = []; + + // Testing all container code + if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { + containers = [ + "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", + "casket", "armorstand", "weaponrack", "barrel", "holeanim", "tomb2", + "tomb3", "roguecorpse", "ratnest", "corpse", "goo pile", "largeurn", + "urn", "chest3", "jug", "skeleton", "guardcorpse", "sarcophagus", "object2", + "cocoon", "basket", "stash", "hollow log", "hungskeleton", "pillar", + "skullpile", "skull pile", "jar3", "jar2", "jar1", "bonechest", "woodchestl", + "woodchestr", "barrel wilderness", "burialchestr", "burialchestl", "explodingchest", + "chestl", "chestr", "groundtomb", "icecavejar1", "icecavejar2", + "icecavejar3", "icecavejar4", "deadperson", "deadperson2", "evilurn", "tomb1l", "tomb3l", "groundtombl" + ]; + } else { + containers = Config.OpenChests.Types; + } + + let unit = Game.getObject(); + + if (unit) { + do { + if (unit.name && unit.mode === sdk.objects.mode.Inactive + && getDistance(me.x, me.y, unit.x, unit.y) <= range + && containers.includes(unit.name.toLowerCase())) { + unitList.push(copyUnit(unit)); + } + } while (unit.getNext()); + } + + while (unitList.length > 0) { + unitList.sort(Sort.units); + unit = unitList.shift(); + + if (unit) { + const chest = Game.getObject(-1, -1, unit.gid); + if (chest && (Pather.useTeleport() || !checkCollision(me, chest, sdk.collision.WallOrRanged)) + && this.openChest(chest)) { + Pickit.pickItems(); + } + } + } + + return true; + }, + + shrineStates: false, + + scanShrines: function (range, ignore = []) { + if (!Config.ScanShrines.length) return false; + + !range && (range = Pather.useTeleport() ? 25 : 15); + !Array.isArray(ignore) && (ignore = [ignore]); + + let shrineList = []; + + // Initiate shrine states + if (!this.shrineStates) { + Misc.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 shrine = Game.getObject("shrine"); + + if (shrine) { + let index = -1; + // Build a list of nearby shrines + do { + if (shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) + && getDistance(me.x, me.y, shrine.x, shrine.y) <= range) { + shrineList.push(copyUnit(shrine)); + } + } while (shrine.getNext()); + + // 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 j = 0; j < shrineList.length; j += 1) { + // 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]); + + // Gem shrine - pick gem + if (Config.ScanShrines[i] === sdk.shrines.Gem) { + Pickit.pickItems(); + } + } + } + } + } + } + + return true; + }, + + // Use a shrine Unit + getShrine: function (unit) { + if (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); + if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, 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)) { + return true; + } + } + + return false; + }, + + /** * Check all shrines in area and get the first one of specified type * @param {number} area * @param {number} type @@ -514,300 +529,305 @@ const Misc = { * - Sometimes it seems like calling getPresetObjects to quickly after taking an exit causes a crash, only anecdotal evidence though. Test delays * - Add the rest of the preset shrine id's to look for */ - getShrinesInArea: function (area, type, use) { - let shrineLocs = []; - let shrineIds = [2, 81, 83]; - let unit = Game.getPresetObjects(area); - let result = false; - - 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]); - } - } - } - - try { - NodeAction.shrinesToIgnore.push(type); - - while (shrineLocs.length > 0) { - 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"); - return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; - } }); - - 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; - } - } - } while (shrine.getNext()); - } - } - } finally { - NodeAction.shrinesToIgnore.remove(type); - } - - return result; - }, - - // Go to town when low on hp/mp or when out of potions. can be upgraded to check for curses etc. - townCheck: function () { - if (!me.canTpToTown()) return false; - - let tTick = getTickCount(); - let check = false; - - if (Config.TownCheck && !me.inTown) { - try { - if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { - check = true; - } - } catch (e) { - return false; - } - - if (check) { - // check that townchicken is running - so we don't spam needing potions if it isn't - let townChick = getScript("threads/TownChicken.js"); - if (!townChick || townChick && !townChick.running) { - return false; - } - - townChick.send("townCheck"); - console.log("townCheck check Duration: " + (getTickCount() - tTick)); - - return true; - } - } - - return false; - }, - - // Log someone's gear - spy: function (name) { - let unit = getUnit(-1, name); - - if (!unit) { - console.warn("player not found"); - return false; - } - - let item = unit.getItem(); - - if (item) { - do { - this.logItem(unit.name, item); - } while (item.getNext()); - } - - return true; - }, - - errorConsolePrint: true, - screenshotErrors: true, - - /** + getShrinesInArea: function (area, type, use) { + let shrineLocs = []; + let shrineIds = [2, 81, 83]; + let unit = Game.getPresetObjects(area); + let result = false; + + 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]); + } + } + } + + try { + NodeAction.shrinesToIgnore.push(type); + + while (shrineLocs.length > 0) { + 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"); + return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; + } }); + + 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; + } + } + } while (shrine.getNext()); + } + } + } finally { + NodeAction.shrinesToIgnore.remove(type); + } + + return result; + }, + + // Go to town when low on hp/mp or when out of potions. can be upgraded to check for curses etc. + townCheck: function () { + if (!me.canTpToTown()) return false; + + let tTick = getTickCount(); + let check = false; + + if (Config.TownCheck && !me.inTown) { + try { + if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { + check = true; + } + } catch (e) { + return false; + } + + if (check) { + // check that townchicken is running - so we don't spam needing potions if it isn't + let townChick = getScript("threads/TownChicken.js"); + if (!townChick || townChick && !townChick.running) { + return false; + } + + townChick.send("townCheck"); + console.log("townCheck check Duration: " + (getTickCount() - tTick)); + + return true; + } + } + + return false; + }, + + // Log someone's gear + spy: function (name) { + let unit = getUnit(-1, name); + + if (!unit) { + console.warn("player not found"); + return false; + } + + let item = unit.getItem(); + + if (item) { + do { + this.logItem(unit.name, item); + } while (item.getNext()); + } + + return true; + }, + + errorConsolePrint: true, + screenshotErrors: true, + + /** * Report script errors to logs/ScriptErrorLog.txt * @param {Error | string} error * @param {string} [script] */ - 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: " + 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 += ", "; - } - } - } - } - } - - stackLog && (filemsg += "Stack: " + stackLog + "\n"); - } - - this.errorConsolePrint && D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); - showConsole(); - console.log(msg); - FileAction.append("logs/ScriptErrorLog.txt", filemsg); - - if (this.screenshotErrors) { - takeScreenshot(); - delay(500); - } - }, - - /** + 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: " + 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 += ", "; + } + } + } + } + } + + stackLog && (filemsg += "Stack: " + stackLog + "\n"); + } + + this.errorConsolePrint && D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); + showConsole(); + console.log(msg); + FileAction.append("logs/ScriptErrorLog.txt", filemsg); + + if (this.screenshotErrors) { + takeScreenshot(); + delay(500); + } + }, + + /** * @param {string} msg * @returns {void} */ - debugLog: function (msg) { - if (!Config.Debug) return; - debugLog(me.profile + ": " + msg); - }, + debugLog: function (msg) { + if (!Config.Debug) return; + debugLog(me.profile + ": " + msg); + }, - /** + /** * Use a NPC menu. Experimental function, subject to change * @param {number} id - string number (with exception of Ressurect merc). * @returns {boolean} */ - useMenu: function (id) { - //print("useMenu " + getLocaleString(id)); + useMenu: function (id) { + //print("useMenu " + getLocaleString(id)); - let npc; + let npc; - switch (id) { - case sdk.menu.RessurectMerc: // (non-English dialog) - case sdk.menu.Trade: // (crash dialog) - npc = getInteractedNPC(); + switch (id) { + case sdk.menu.RessurectMerc: // (non-English dialog) + case sdk.menu.Trade: // (crash dialog) + npc = getInteractedNPC(); - if (npc) { - npc.useMenu(id); - delay(750); + if (npc) { + npc.useMenu(id); + delay(750); - return true; - } + return true; + } - break; - } + break; + } - let lines = getDialogLines(); - if (!lines) return false; + let lines = getDialogLines(); + if (!lines) return false; - for (let i = 0; i < lines.length; i += 1) { - if (lines[i].selectable && lines[i].text.includes(getLocaleString(id))) { - getDialogLines()[i].handler(); - delay(750); + for (let i = 0; i < lines.length; i += 1) { + if (lines[i].selectable && lines[i].text.includes(getLocaleString(id))) { + getDialogLines()[i].handler(); + delay(750); - return true; - } - } + return true; + } + } - return false; - }, + return false; + }, - /** + /** * @template T * @param {function(): T} check * @param {number} [timeout=6000] * @param {number} [sleep=40] * @returns {T | false} */ - poll: function (check, timeout = 6000, sleep = 40) { - let ret, start = getTickCount(); + poll: function (check, timeout = 6000, sleep = 40) { + let ret, start = getTickCount(); - while (getTickCount() - start <= timeout) { - if ((ret = check())) { - return ret; - } + while (getTickCount() - start <= timeout) { + if ((ret = check())) { + return ret; + } - delay(sleep); - } + delay(sleep); + } - return false; - }, + return false; + }, - /** + /** * @param {number[]} excluded * @returns {number[] | null} array of UI flags that are set, or null if none are set */ - getUIFlags: function (excluded = []) { - if (!me.gameReady) return null; + getUIFlags: function (excluded = []) { + if (!me.gameReady) return null; - const MAX_FLAG = 37; // anything over 37 crashes - let flags = []; + const MAX_FLAG = 37; // anything over 37 crashes + let flags = []; - if (typeof excluded !== "object" || excluded.length === undefined) { - // not an array-like object, make it an array - excluded = [excluded]; - } + if (typeof excluded !== "object" || excluded.length === undefined) { + // not an array-like object, make it an array + excluded = [excluded]; + } - for (let c = 1; c <= MAX_FLAG; c++) { - // 0x23 is always set in-game - if (c !== 0x23 && excluded.indexOf(c) === -1 && getUIFlag(c)) { - flags.push(c); - } - } + for (let c = 1; c <= MAX_FLAG; c++) { + // 0x23 is always set in-game + if (c !== 0x23 && excluded.indexOf(c) === -1 && getUIFlag(c)) { + flags.push(c); + } + } - return flags.length ? flags : null; - }, + return flags.length ? flags : null; + }, - /** + /** * @param {number} id * @param {number} state * @returns {0 | 1} */ - checkQuest: function (id, state) { - Packet.questRefresh(); - delay(500); - return me.getQuest(id, state); - }, + checkQuest: function (id, state) { + Packet.questRefresh(); + delay(500); + return me.getQuest(id, state); + }, - /** + /** * @param {number} questID * @returns {number[]} List of set quest states */ - getQuestStates: function (questID) { - if (!me.gameReady) return []; - Packet.questRefresh(); - delay(500); - const MAX_STATE = 16; - let questStates = []; - - for (let i = 0; i < MAX_STATE; i++) { - if (me.getQuest(questID, i)) { - questStates.push(i); - } - - delay(50); - } - - return questStates; - } + getQuestStates: function (questID) { + if (!me.gameReady) return []; + Packet.questRefresh(); + delay(500); + const MAX_STATE = 16; + let questStates = []; + + for (let i = 0; i < MAX_STATE; i++) { + if (me.getQuest(questID, i)) { + questStates.push(i); + } + + delay(50); + } + + return questStates; + } }; // (function() { // // todo figure out why the Misc.d.ts file only works right if Misc is wrapped in a IEFE diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 20ebef3f4..b4c4a95d1 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename NTItemParser.js * @author kolton, jaenster @@ -32,105 +33,105 @@ const NTIP_CheckListNoTier = []; let stringArray = []; NTIP.OpenFile = function (filepath, notify) { - if (!FileTools.exists(filepath)) { - if (notify) { - Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); - } - - return false; - } - - let nipfile, lines; - let tick = getTickCount(); - let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); - let entries = 0; - - try { - nipfile = File.open(filepath, 0); - } catch (fileError) { - if (notify) { - Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); - } - } - - if (!nipfile) { - return false; - } - - lines = nipfile.readAllLines(); - nipfile.close(); - - for (let i = 0; i < lines.length; i += 1) { - let info = { - line: i + 1, - file: filename, - string: lines[i] - }; - - let line = NTIP.ParseLineInt(lines[i], info); - - if (line) { - entries += 1; - - NTIP_CheckList.push(line); - - if (!lines[i].toLowerCase().match("tier")) { - NTIP_CheckListNoTier.push(line); - } + if (!FileTools.exists(filepath)) { + if (notify) { + Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); + } + + return false; + } + + let nipfile, lines; + let tick = getTickCount(); + let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); + let entries = 0; + + try { + nipfile = File.open(filepath, 0); + } catch (fileError) { + if (notify) { + Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); + } + } + + if (!nipfile) { + return false; + } + + lines = nipfile.readAllLines(); + nipfile.close(); + + for (let i = 0; i < lines.length; i += 1) { + let info = { + line: i + 1, + file: filename, + string: lines[i] + }; + + let line = NTIP.ParseLineInt(lines[i], info); + + if (line) { + entries += 1; + + NTIP_CheckList.push(line); + + if (!lines[i].toLowerCase().match("tier")) { + NTIP_CheckListNoTier.push(line); + } - stringArray.push(info); - } - } + stringArray.push(info); + } + } - if (notify) { - print("ÿc4Loaded NIP: ÿc2" + filename + "ÿc4. Lines: ÿc2" + lines.length + "ÿc4. Valid entries: ÿc2" + entries + ". ÿc4Time: ÿc2" + (getTickCount() - tick) + " ms"); - } + if (notify) { + print("ÿc4Loaded NIP: ÿc2" + filename + "ÿc4. Lines: ÿc2" + lines.length + "ÿc4. Valid entries: ÿc2" + entries + ". ÿc4Time: ÿc2" + (getTickCount() - tick) + " ms"); + } - return true; + return true; }; NTIP.CheckQuantityOwned = function (item_type, item_stats) { - let item; - let num = 0; - let items = me.getItemsEx(); - - if (!items.length) { - print("I can't find my items!"); - - return 0; - } - - for (let i = 0; i < items.length; i += 1) { - if (items[i].mode === sdk.items.mode.inStorage && items[i].location === sdk.storage.Stash) { - item = items[i]; - - if ((item_type !== null && typeof item_type === "function" && item_type(item)) || item_type === null) { - if ((item_stats !== null && typeof item_stats === "function" && item_stats(item)) || item_stats === null) { - num += 1; - } - } - } else if (items[i].mode === sdk.items.mode.inStorage && items[i].location === sdk.storage.Inventory) { // inv check - item = items[i]; - - if ((item_type !== null && typeof item_type === "function" && item_type(item)) || item_type === null) { - if ((item_stats !== null && typeof item_stats === "function" && item_stats(item)) || item_stats === null) { - //if (Config.Inventory[items[i].y][items[i].x] > 0) { // we check only space that is supposed to be free - num += 1; - //} - } - } - } - } - - //print("I have "+num+" of these."); - - return num; + let item; + let num = 0; + let items = me.getItemsEx(); + + if (!items.length) { + print("I can't find my items!"); + + return 0; + } + + for (let i = 0; i < items.length; i += 1) { + if (items[i].mode === sdk.items.mode.inStorage && items[i].location === sdk.storage.Stash) { + item = items[i]; + + if ((item_type !== null && typeof item_type === "function" && item_type(item)) || item_type === null) { + if ((item_stats !== null && typeof item_stats === "function" && item_stats(item)) || item_stats === null) { + num += 1; + } + } + } else if (items[i].mode === sdk.items.mode.inStorage && items[i].location === sdk.storage.Inventory) { // inv check + item = items[i]; + + if ((item_type !== null && typeof item_type === "function" && item_type(item)) || item_type === null) { + if ((item_stats !== null && typeof item_stats === "function" && item_stats(item)) || item_stats === null) { + //if (Config.Inventory[items[i].y][items[i].x] > 0) { // we check only space that is supposed to be free + num += 1; + //} + } + } + } + } + + //print("I have "+num+" of these."); + + return num; }; NTIP.Clear = function () { - NTIP_CheckList.length = 0; - NTIP_CheckListNoTier.length = 0; - stringArray = []; + NTIP_CheckList.length = 0; + NTIP_CheckListNoTier.length = 0; + stringArray = []; }; /** @@ -138,52 +139,52 @@ NTIP.Clear = function () { * @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_CheckList.length; i += 1) { - if (NTIP_CheckList[i].length !== 3) { - continue; - } - - let [type, stat, wanted] = NTIP_CheckList[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 = stringArray[i]; - Misc.errorReport("ÿc1Pickit Tier (" + tierType + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); - } - } - } - - 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_CheckList.length; i += 1) { + if (NTIP_CheckList[i].length !== 3) { + continue; + } + + let [type, stat, wanted] = NTIP_CheckList[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 = stringArray[i]; + Misc.errorReport("ÿc1Pickit Tier (" + tierType + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); + } + } + } + + return tier; + }; }; /** @@ -201,425 +202,425 @@ NTIP.GetTier = NTIP.generateTierFunc("Tier"); NTIP.GetMercTier = NTIP.generateTierFunc("Merctier"); NTIP.CheckItem = function (item, entryList, verbose) { - let i, num; - let rval = {}; - let result = 0; - - let list = entryList ? entryList : NTIP_CheckList; - let identified = item.getFlag(sdk.items.flags.Identified); - - 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.isInStorage && num === wanted.MaxQuantity) { - result = 1; - - break; - } - } - } else { - result = 1; - - break; - } - } else if (!identified && result === 0) { - result = -1; - verbose && (rval.line = stringArray[i].file + " #" + stringArray[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.isInStorage && 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.isInStorage && num === wanted.MaxQuantity) { - result = 1; - - break; - } - } - } else { - result = 1; - - break; - } - } else if (!identified && result === 0) { - result = -1; - verbose && (rval.line = stringArray[i].file + " #" + stringArray[i].line); - } - } - } catch (pickError) { - showConsole(); - - if (!entryList) { - Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + stringArray[i].line + " ÿc1Entry: ÿc0" + stringArray[i].string + " (" + stringArray[i].file + ") Error message: " + pickError.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); - - NTIP_CheckList.splice(i, 1); // Remove the element from the list - } else { - Misc.errorReport("ÿc1Pickit error in runeword config!"); - } - - result = 0; - } - } - - if (verbose) { - switch (result) { - case -1: - break; - case 1: - rval.line = stringArray[i].file + " #" + stringArray[i].line; - - break; - default: - rval.line = null; - - break; - } - - rval.result = result; - - return rval; - } - - return result; + let i, num; + let rval = {}; + let result = 0; + + let list = entryList ? entryList : NTIP_CheckList; + let identified = item.getFlag(sdk.items.flags.Identified); + + 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.isInStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; + } + } else if (!identified && result === 0) { + result = -1; + verbose && (rval.line = stringArray[i].file + " #" + stringArray[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.isInStorage && 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.isInStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; + } + } else if (!identified && result === 0) { + result = -1; + verbose && (rval.line = stringArray[i].file + " #" + stringArray[i].line); + } + } + } catch (pickError) { + showConsole(); + + if (!entryList) { + Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + stringArray[i].line + " ÿc1Entry: ÿc0" + stringArray[i].string + " (" + stringArray[i].file + ") Error message: " + pickError.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); + + NTIP_CheckList.splice(i, 1); // Remove the element from the list + } else { + Misc.errorReport("ÿc1Pickit error in runeword config!"); + } + + result = 0; + } + } + + if (verbose) { + switch (result) { + case -1: + break; + case 1: + rval.line = stringArray[i].file + " #" + stringArray[i].line; + + break; + default: + rval.line = null; + + break; + } + + rval.result = result; + + return rval; + } + + return result; }; NTIP.IsSyntaxInt = function (ch) { - return (ch === "!" || ch === "%" || ch === "&" || (ch >= "(" && ch <= "+") || ch === "-" || ch === "/" || (ch >= ":" && ch <= "?") || ch === "|"); + return (ch === "!" || ch === "%" || ch === "&" || (ch >= "(" && ch <= "+") || ch === "-" || ch === "/" || (ch >= ":" && ch <= "?") || ch === "|"); }; 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 "color": - p_result[0] += "item.getColor()"; + switch (property) { + 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); + break; + default: + Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); - return false; - } + 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; - } - } + 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); + 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_result[0] += NTIPAliasType[p_keyword]; + 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]; - 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); - - return false; - } - - 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); - - 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) { - case "maxquantity": - value = Number(p_section[i].split("==")[1].match(/\d+/g)); - - if (!isNaN(value)) { - p_result[2].MaxQuantity = value; - } - - break; - case "merctier": - case "tier": - try { - // p_result[2].Tier = function(item) { return value }; - 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; + 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); + + return false; + } + + 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); + + 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) { + case "maxquantity": + value = Number(p_section[i].split("==")[1].match(/\d+/g)); + + if (!isNaN(value)) { + p_result[2].MaxQuantity = value; + } + + break; + case "merctier": + case "tier": + try { + // p_result[2].Tier = function(item) { return value }; + 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/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index f4222188e..49cc00dc9 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -6,114 +6,122 @@ */ const Packet = { - /** + /** * Interact and open the menu of an NPC * @param {NPCUnit} unit * @returns {boolean} */ - openMenu: function (unit) { - if (unit.type !== sdk.unittype.NPC) throw new Error("openMenu: Must be used on NPCs."); - if (getUIFlag(sdk.uiflags.NPCMenu)) return true; - let pingDelay = (me.gameReady ? me.ping : 125); - - for (let i = 0; i < 5; i += 1) { - unit.distance > 4 && Pather.moveToUnit(unit); - Packet.entityInteract(unit); - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (getUIFlag(sdk.uiflags.NPCMenu)) { - delay(Math.max(500, pingDelay * 2)); - - return true; - } - - if ((getTickCount() - tick > 1000 && getInteractedNPC()) || (getTickCount() - tick > 500 && getIsTalkingNPC())) { - me.cancel(); - } - - delay(100); - } - - // sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, unit.gid); - new PacketBuilder().byte(sdk.packets.send.NPCInit).dword(1).dword(unit.gid).send(); - delay(pingDelay + 1 * 2); - Packet.cancelNPC(unit); - delay(pingDelay + 1 * 2); - this.flash(me.gid); - } - - return false; - }, - - /** + openMenu: function (unit) { + if (unit.type !== sdk.unittype.NPC) throw new Error("openMenu: Must be used on NPCs."); + if (getUIFlag(sdk.uiflags.NPCMenu)) return true; + let pingDelay = (me.gameReady ? me.ping : 125); + + for (let i = 0; i < 5; i += 1) { + unit.distance > 4 && Pather.moveToUnit(unit); + Packet.entityInteract(unit); + let tick = getTickCount(); + + while (getTickCount() - tick < 5000) { + if (getUIFlag(sdk.uiflags.NPCMenu)) { + delay(Math.max(500, pingDelay * 2)); + + return true; + } + + if ((getTickCount() - tick > 1000 + && getInteractedNPC()) || (getTickCount() - tick > 500 && getIsTalkingNPC())) { + me.cancel(); + } + + delay(100); + } + + // sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, unit.gid); + new PacketBuilder().byte(sdk.packets.send.NPCInit).dword(1).dword(unit.gid).send(); + delay(pingDelay + 1 * 2); + Packet.cancelNPC(unit); + delay(pingDelay + 1 * 2); + this.flash(me.gid); + } + + return false; + }, + + /** * Start a trade action with an NPC * @param {NPCUnit} unit * @param {number} mode * @returns {boolean} */ - startTrade: function (unit, mode) { - if (unit.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); - if (getUIFlag(sdk.uiflags.Shop)) return true; + startTrade: function (unit, mode) { + if (unit.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); + if (getUIFlag(sdk.uiflags.Shop)) return true; - const gamble = mode === "Gamble"; - console.info(true, mode + " at " + unit.name); + const gamble = mode === "Gamble"; + console.info(true, mode + " at " + unit.name); - if (this.openMenu(unit)) { - for (let i = 0; i < 10; i += 1) { - delay(200); + if (this.openMenu(unit)) { + for (let i = 0; i < 10; i += 1) { + delay(200); - i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, gamble ? 2 : 1, 4, unit.gid, 4, 0); + i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, gamble ? 2 : 1, 4, unit.gid, 4, 0); - if (unit.itemcount > 0) { - delay(200); - console.info(false, "Successfully started " + mode + " at " + unit.name); - return true; - } - } - } + if (unit.itemcount > 0) { + delay(200); + console.info(false, "Successfully started " + mode + " at " + unit.name); + return true; + } + } + } - return false; - }, + return false; + }, - /** + /** * Buy an item from an interacted NPC * @param {NPCUnit} unit * @param {boolean} shiftBuy * @param {boolean} gamble * @returns {boolean} */ - buyItem: function (unit, shiftBuy, gamble) { - let oldGold = me.gold; - let itemCount = me.itemcount; - let npc = getInteractedNPC(); - - try { - if (!npc) throw new Error("buyItem: No NPC menu open."); - - // Can we afford the item? - if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; - - for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : gamble ? 0x2 : 0x0, 4, 0); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { - if (shiftBuy && me.gold < oldGold) return true; - if (itemCount !== me.itemcount) return true; - - delay(10); - } - } - } catch (e) { - console.error(e); - } - - return false; - }, - - /** + buyItem: function (unit, shiftBuy, gamble) { + let oldGold = me.gold; + let itemCount = me.itemcount; + let npc = getInteractedNPC(); + + try { + if (!npc) throw new Error("buyItem: No NPC menu open."); + + // Can we afford the item? + if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; + + for (let i = 0; i < 3; i += 1) { + // sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : gamble ? 0x2 : 0x0, 4, 0); + new PacketBuilder() + .byte(sdk.packets.send.NPCBuy) + .dword(npc.gid) + .dword(unit.gid) + .dword(shiftBuy ? 0x80000000 : gamble ? 0x2 : 0x0) + .dword(0) + .send(); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { + if (shiftBuy && me.gold < oldGold) return true; + if (itemCount !== me.itemcount) return true; + + delay(10); + } + } + } catch (e) { + console.error(e); + } + + return false; + }, + + /** * Buy scrolls from an interacted NPC, we need this as a seperate check because itemcount doesn't change * if the scroll goes into the tome automatically. * @param {NPCUnit} unit @@ -121,386 +129,397 @@ const Packet = { * @param {boolean} [shiftBuy] * @returns {boolean} */ - buyScroll: function (unit, tome, shiftBuy) { - let oldGold = me.gold; - let itemCount = me.itemcount; - let npc = getInteractedNPC(); - tome === undefined && (tome = me.findItem( - (unit.classid === sdk.items.ScrollofTownPortal ? sdk.items.TomeofTownPortal : sdk.items.TomeofIdentify), - sdk.items.mode.inStorage, sdk.storage.Inventory - )); - let preCount = !!tome ? tome.getStat(sdk.stats.Quantity) : 0; - - try { - if (!npc) throw new Error("buyItem: No NPC menu open."); - - // Can we afford the item? - if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; - - for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : 0x0, 4, 0); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { - if (shiftBuy && me.gold < oldGold) return true; - if (itemCount !== me.itemcount) return true; - if (tome && tome.getStat(sdk.stats.Quantity) > preCount) return true; - delay(10); - } - } - } catch (e) { - console.error(e); - } - - return false; - }, - - /** + buyScroll: function (unit, tome, shiftBuy) { + let oldGold = me.gold; + let itemCount = me.itemcount; + let npc = getInteractedNPC(); + tome === undefined && (tome = me.findItem( + (unit.classid === sdk.items.ScrollofTownPortal ? sdk.items.TomeofTownPortal : sdk.items.TomeofIdentify), + sdk.items.mode.inStorage, sdk.storage.Inventory + )); + let preCount = !!tome ? tome.getStat(sdk.stats.Quantity) : 0; + + try { + if (!npc) throw new Error("buyItem: No NPC menu open."); + + // Can we afford the item? + if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; + + for (let i = 0; i < 3; i += 1) { + sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : 0x0, 4, 0); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { + if (shiftBuy && me.gold < oldGold) return true; + if (itemCount !== me.itemcount) return true; + if (tome && tome.getStat(sdk.stats.Quantity) > preCount) return true; + delay(10); + } + } + } catch (e) { + console.error(e); + } + + return false; + }, + + /** * Sell a item to a NPC * @param {ItemUnit} unit * @returns {boolean} */ - sellItem: function (unit) { - // Check if it's an item we want to buy - if (unit.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); - if (!unit.sellable) { - console.error((new Error("Item is unsellable"))); - return false; - } + sellItem: function (unit) { + // Check if it's an item we want to buy + if (unit.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); + if (!unit.sellable) { + console.error((new Error("Item is unsellable"))); + return false; + } - let itemCount = me.itemcount; - let npc = getInteractedNPC(); + let itemCount = me.itemcount; + let npc = getInteractedNPC(); - if (!npc) return false; + if (!npc) return false; - for (let i = 0; i < 5; i += 1) { - sendPacket(1, sdk.packets.send.NPCSell, 4, npc.gid, 4, unit.gid, 4, 0, 4, 0); + for (let i = 0; i < 5; i += 1) { + sendPacket(1, sdk.packets.send.NPCSell, 4, npc.gid, 4, unit.gid, 4, 0, 4, 0); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 2000) { - if (me.itemcount !== itemCount) return true; - delay(10); - } - } + while (getTickCount() - tick < 2000) { + if (me.itemcount !== itemCount) return true; + delay(10); + } + } - return false; - }, + return false; + }, - /** + /** * @param {ItemUnit} unit * @param {ItemUnit} tome * @returns {boolean} */ - identifyItem: function (unit, tome) { - if (!unit || unit.identified) return false; + identifyItem: function (unit, tome) { + if (!unit || unit.identified) return false; - CursorLoop: - for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); + CursorLoop: + for (let i = 0; i < 3; i += 1) { + sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 2000) { - if (getCursorType() === sdk.cursortype.Identify) { - break CursorLoop; - } + while (getTickCount() - tick < 2000) { + if (getCursorType() === sdk.cursortype.Identify) { + break CursorLoop; + } - delay(10); - } - } + delay(10); + } + } - if (getCursorType() !== sdk.cursortype.Identify) { - return false; - } + if (getCursorType() !== sdk.cursortype.Identify) { + return false; + } - for (let i = 0; i < 3; i += 1) { - getCursorType() === sdk.cursortype.Identify && sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); + for (let i = 0; i < 3; i += 1) { + if (getCursorType() === sdk.cursortype.Identify) { + sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); + } - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 2000) { - if (unit.identified) { - delay(50); - return true; - } + while (getTickCount() - tick < 2000) { + if (unit.identified) { + delay(50); + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; - }, + return false; + }, - /** + /** * @param {ItemUnit} item * @returns {boolean} */ - itemToCursor: function (item) { - // Something already on cursor - if (me.itemoncursor) { - let cursorItem = Game.getCursorUnit(); - // Return true if the item is already on cursor - if (cursorItem.gid === item.gid) { - return true; - } - this.dropItem(cursorItem); // If another item is on cursor, drop it - } - - for (let i = 0; i < 15; i += 1) { - // equipped - item.isEquipped - ? sendPacket(1, sdk.packets.send.PickupBodyItem, 2, item.bodylocation) - : item.isInBelt - ? new PacketBuilder().byte(sdk.packets.send.RemoveBeltItem).dword(item.gid).send() - : sendPacket(1, sdk.packets.send.PickupBufferItem, 4, item.gid); - - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { - if (me.itemoncursor) return true; - delay(10); - } - } - - return false; - }, - - /** + itemToCursor: function (item) { + // Something already on cursor + if (me.itemoncursor) { + let cursorItem = Game.getCursorUnit(); + // Return true if the item is already on cursor + if (cursorItem.gid === item.gid) { + return true; + } + this.dropItem(cursorItem); // If another item is on cursor, drop it + } + + for (let i = 0; i < 15; i += 1) { + // equipped + item.isEquipped + ? sendPacket(1, sdk.packets.send.PickupBodyItem, 2, item.bodylocation) + : item.isInBelt + ? new PacketBuilder().byte(sdk.packets.send.RemoveBeltItem).dword(item.gid).send() + : sendPacket(1, sdk.packets.send.PickupBufferItem, 4, item.gid); + + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { + if (me.itemoncursor) return true; + delay(10); + } + } + + return false; + }, + + /** * @param {ItemUnit} item * @returns {boolean} */ - dropItem: function (item) { - if (!this.itemToCursor(item)) return false; + dropItem: function (item) { + if (!this.itemToCursor(item)) return false; - for (let i = 0; i < 15; i += 1) { - sendPacket(1, sdk.packets.send.DropItem, 4, item.gid); + for (let i = 0; i < 15; i += 1) { + sendPacket(1, sdk.packets.send.DropItem, 4, item.gid); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { - if (!me.itemoncursor) return true; - delay(10); - } - } + while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { + if (!me.itemoncursor) return true; + delay(10); + } + } - return false; - }, + return false; + }, - /** + /** * @param {ItemUnit} item * @returns {boolean} */ - givePotToMerc: function (item) { - if (!!item - && [sdk.items.type.HealingPotion, sdk.items.type.RejuvPotion, sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion].includes(item.itemType)) { - switch (item.location) { - case sdk.storage.Belt: - return this.useBeltItemForMerc(item); - case sdk.storage.Inventory: - if (this.itemToCursor(item)) { - sendPacket(1, sdk.packets.send.MercItem, 2, 0); - - return true; - } - - break; - default: - break; - } - } - - return false; - }, - - /** + givePotToMerc: function (item) { + if (!item) return false; + if (![ + sdk.items.type.HealingPotion, sdk.items.type.RejuvPotion, + sdk.items.type.ThawingPotion, sdk.items.type.AntidotePotion + ].includes(item.itemType)) { + return false; + } + if (item.isInBelt) return this.useBeltItemForMerc(item); + if (item.isInInventory && this.itemToCursor(item)) { + sendPacket(1, sdk.packets.send.MercItem, 2, 0); + + return true; + } + return false; + }, + + /** * @param {ItemUnit} item * @param {number} xLoc * @returns {boolean} */ - placeInBelt: function (item, xLoc) { - item.toCursor(true) && new PacketBuilder().byte(sdk.packets.send.ItemToBelt).dword(item.gid).dword(xLoc).send(); - return Misc.poll(() => item.isInBelt, 500, 100); - }, + placeInBelt: function (item, xLoc) { + item.toCursor(true) && new PacketBuilder().byte(sdk.packets.send.ItemToBelt).dword(item.gid).dword(xLoc).send(); + return Misc.poll(() => item.isInBelt, 500, 100); + }, - /** + /** * @param {ItemUnit} who * @param {boolean} toCursor * @returns {boolean} */ - click: function (who, toCursor = false) { - if (!who || !copyUnit(who).x) return false; - new PacketBuilder().byte(sdk.packets.send.PickupItem).dword(sdk.unittype.Item).dword(who.gid).dword(toCursor ? 1 : 0).send(); - return true; - }, - - /** + click: function (who, toCursor = false) { + if (!who || !copyUnit(who).x) return false; + new PacketBuilder() + .byte(sdk.packets.send.PickupItem) + .dword(sdk.unittype.Item) + .dword(who.gid) + .dword(toCursor ? 1 : 0) + .send(); + return true; + }, + + /** * @param {Unit} who * @returns {boolean} */ - entityInteract: function (who) { - if (!who || !copyUnit(who).x) return false; - new PacketBuilder().byte(sdk.packets.send.InteractWithEntity).dword(who.type).dword(who.gid).send(); - return true; - }, + entityInteract: function (who) { + if (!who || !copyUnit(who).x) return false; + new PacketBuilder().byte(sdk.packets.send.InteractWithEntity).dword(who.type).dword(who.gid).send(); + return true; + }, - /** + /** * @param {NPCUnit} who * @returns {boolean} */ - cancelNPC: function (who) { - if (!who || !copyUnit(who).x) return false; - new PacketBuilder().byte(sdk.packets.send.NPCCancel).dword(who.type).dword(who.gid).send(); - return true; - }, + cancelNPC: function (who) { + if (!who || !copyUnit(who).x) return false; + new PacketBuilder().byte(sdk.packets.send.NPCCancel).dword(who.type).dword(who.gid).send(); + return true; + }, - /** + /** * @param {ItemUnit} pot * @returns {boolean} */ - useBeltItemForMerc: function (pot) { - if (!pot) return false; - sendPacket(1, sdk.packets.send.UseBeltItem, 4, pot.gid, 4, 1, 4, 0); - return true; - }, - - castSkill: function (hand, wX, wY) { - hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocation : sdk.packets.send.LeftSkillOnLocation; - sendPacket(1, hand, 2, wX, 2, wY); - }, - - castAndHoldSkill: function (hand, wX, wY, duration = 1000) { - let nHand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocation : sdk.packets.send.LeftSkillOnLocation; - hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocationEx : sdk.packets.send.LeftSkillOnLocationEx; + useBeltItemForMerc: function (pot) { + if (!pot) return false; + sendPacket(1, sdk.packets.send.UseBeltItem, 4, pot.gid, 4, 1, 4, 0); + return true; + }, + + castSkill: function (hand, wX, wY) { + hand = (hand === sdk.skills.hand.Right) + ? sdk.packets.send.RightSkillOnLocation + : sdk.packets.send.LeftSkillOnLocation; + sendPacket(1, hand, 2, wX, 2, wY); + }, + + castAndHoldSkill: function (hand, wX, wY, duration = 1000) { + let nHand = (hand === sdk.skills.hand.Right) + ? sdk.packets.send.RightSkillOnLocation + : sdk.packets.send.LeftSkillOnLocation; + hand = (hand === sdk.skills.hand.Right) + ? sdk.packets.send.RightSkillOnLocationEx + : sdk.packets.send.LeftSkillOnLocationEx; - let endT = getTickCount() + duration; - // has to be cast normally first with a click before held packet is sent - sendPacket(1, nHand, 2, wX, 2, wY); - while (getTickCount() < endT) { - sendPacket(1, hand, 2, wX, 2, wY); - delay(25); - } - }, - - /** + let endT = getTickCount() + duration; + // has to be cast normally first with a click before held packet is sent + sendPacket(1, nHand, 2, wX, 2, wY); + while (getTickCount() < endT) { + sendPacket(1, hand, 2, wX, 2, wY); + delay(25); + } + }, + + /** * @param {number} hand * @param {Monster | ItemUnit | ObjectUnit} who * @returns {boolean} */ - unitCast: function (hand, who) { - hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnEntityEx3 : sdk.packets.send.LeftSkillOnEntityEx3; - sendPacket(1, hand, 4, who.type, 4, who.gid); - }, - - /** + unitCast: function (hand, who) { + hand = (hand === sdk.skills.hand.Right) + ? sdk.packets.send.RightSkillOnEntityEx3 + : sdk.packets.send.LeftSkillOnEntityEx3; + sendPacket(1, hand, 4, who.type, 4, who.gid); + }, + + /** * @param {Monster | ItemUnit | ObjectUnit} who * @returns {boolean} */ - telekinesis: function (who) { - if (!who || !Skill.setSkill(sdk.skills.Telekinesis, sdk.skills.hand.Right)) return false; - sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); - return true; - }, + telekinesis: function (who) { + if (!who || !Skill.setSkill(sdk.skills.Telekinesis, sdk.skills.hand.Right)) return false; + sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); + return true; + }, - /** + /** * @param {Player | Monster | MercUnit} who * @returns {boolean} */ - enchant: function (who) { - if (!who || !Skill.setSkill(sdk.skills.Enchant, sdk.skills.hand.Right)) return false; - sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); - return true; - }, + enchant: function (who) { + if (!who || !Skill.setSkill(sdk.skills.Enchant, sdk.skills.hand.Right)) return false; + sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); + return true; + }, - /** + /** * @param {number} wX * @param {number} wY * @returns {boolean} */ - teleport: function (wX, wY) { - if (![wX, wY].every(n => typeof n === "number") || !Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right)) return false; - new PacketBuilder().byte(sdk.packets.send.RightSkillOnLocation).word(wX).word(wY).send(); - return true; - }, - - // moveNPC: function (npc, dwX, dwY) { // commented the patched packet - // //sendPacket(1, sdk.packets.send.MakeEntityMove, 4, npc.type, 4, npc.gid, 4, dwX, 4, dwY); - // }, - - /** + teleport: function (wX, wY) { + if (![wX, wY].every(n => typeof n === "number")) return false; + if (!Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right)) return false; + new PacketBuilder().byte(sdk.packets.send.RightSkillOnLocation).word(wX).word(wY).send(); + return true; + }, + + // moveNPC: function (npc, dwX, dwY) { // commented the patched packet + // //sendPacket(1, sdk.packets.send.MakeEntityMove, 4, npc.type, 4, npc.gid, 4, dwX, 4, dwY); + // }, + + /** * @deprecated * @param {number} x * @param {number} y * @param {number} maxDist * @returns {boolean} */ - teleWalk: function (x, y, maxDist = 5) { - !Packet.telewalkTick && (Packet.telewalkTick = 0); + teleWalk: function (x, y, maxDist = 5) { + !Packet.telewalkTick && (Packet.telewalkTick = 0); - if (getDistance(me, x, y) > 10 && getTickCount() - this.telewalkTick > 3000 && Attack.validSpot(x, y)) { - for (let i = 0; i < 5; i += 1) { - sendPacket(1, sdk.packets.send.UpdatePlayerPos, 2, x + rand(-1, 1), 2, y + rand(-1, 1)); - delay(me.ping + 1); - sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, me.type, 4, me.gid); - delay(me.ping + 1); + if (getDistance(me, x, y) > 10 && getTickCount() - this.telewalkTick > 3000 && Attack.validSpot(x, y)) { + for (let i = 0; i < 5; i += 1) { + sendPacket(1, sdk.packets.send.UpdatePlayerPos, 2, x + rand(-1, 1), 2, y + rand(-1, 1)); + delay(me.ping + 1); + sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, me.type, 4, me.gid); + delay(me.ping + 1); - if (getDistance(me, x, y) < maxDist) { - delay(200); + if (getDistance(me, x, y) < maxDist) { + delay(200); - return true; - } - } + return true; + } + } - Packet.telewalkTick = getTickCount(); - } + Packet.telewalkTick = getTickCount(); + } - return false; - }, + return false; + }, - questRefresh: function () { - sendPacket(1, sdk.packets.send.UpdateQuests); - }, + questRefresh: function () { + sendPacket(1, sdk.packets.send.UpdateQuests); + }, - /** + /** * Request entity update * @param {number} gid * @param {number} wait */ - flash: function (gid, wait = 0) { - wait === 0 && (wait = 300 + (me.gameReady ? 2 * me.ping : 300)); - sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, 0, 4, gid); + flash: function (gid, wait = 0) { + wait === 0 && (wait = 300 + (me.gameReady ? 2 * me.ping : 300)); + sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, 0, 4, gid); - if (wait > 0) { - delay(wait); - } - }, + if (wait > 0) { + delay(wait); + } + }, - /** + /** * @deprecated * @param {number} stat * @param {number} value */ - changeStat: function (stat, value) { - if (value > 0) { - getPacket(1, 0x1d, 1, stat, 1, value); - } - }, + changeStat: function (stat, value) { + if (value > 0) { + getPacket(1, 0x1d, 1, stat, 1, value); + } + }, - // specialized wrapper for addEventListener - addListener: function (packetType, callback) { - if (typeof packetType === "number") { - packetType = [packetType]; - } + // specialized wrapper for addEventListener + addListener: function (packetType, callback) { + if (typeof packetType === "number") { + packetType = [packetType]; + } - if (typeof packetType === "object" && packetType.length) { - addEventListener("gamepacket", packet => (packetType.indexOf(packet[0]) > -1 ? callback(packet) : false)); + if (typeof packetType === "object" && packetType.length) { + addEventListener("gamepacket", packet => (packetType.indexOf(packet[0]) > -1 ? callback(packet) : false)); - return callback; - } + return callback; + } - return null; - }, + return null; + }, - removeListener: callback => removeEventListener("gamepacket", callback), // just a wrapper + removeListener: callback => removeEventListener("gamepacket", callback), // just a wrapper }; diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 707c0f92b..0285ada77 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -10,191 +10,208 @@ * @todo this needs to be re-worked */ const NodeAction = { - /** + /** * @type {number[]} */ - shrinesToIgnore: [], - enabled: true, + shrinesToIgnore: [], + enabled: true, - /** + /** * Run all the functions within NodeAction (except for itself) * @param {clearSettings} arg */ - go: function (arg) { - if (!this.enabled) return; - for (let i in this) { - if (this.hasOwnProperty(i) && typeof this[i] === "function" && i !== "go") { - this[i](arg); - } - } - }, - - /** + go: function (arg) { + if (!this.enabled) return; + for (let i in this) { + if (this.hasOwnProperty(i) && typeof this[i] === "function" && i !== "go") { + this[i](arg); + } + } + }, + + /** * Kill monsters while pathing * @param {clearSettings} arg * @returns {void} */ - killMonsters: function (arg = {}) { - if (arg.hasOwnProperty("allowClearing") && !arg.allowClearing) return; - - const killSettings = Object.assign({}, { - clearPath: false, - specType: sdk.monsters.spectype.All, - range: 10, - overrideConfig: false, - }, arg); - - if (Config.Countess.KillGhosts && [sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5].includes(me.area)) { - let monList = (Attack.getMob(sdk.monsters.Ghost1, sdk.monsters.spectype.All, 30) || []); - monList.length > 0 && Attack.clearList(monList); - } - - if ((typeof Config.ClearPath === "number" || typeof Config.ClearPath === "object") && killSettings.clearPath === false && !killSettings.overrideConfig) { - switch (typeof Config.ClearPath) { - case "number": - Attack.clear(30, Config.ClearPath); - - break; - case "object": - if (!Config.ClearPath.hasOwnProperty("Areas") || !Config.ClearPath.Areas.length || Config.ClearPath.Areas.includes(me.area)) { - Attack.clear(Config.ClearPath.Range, Config.ClearPath.Spectype); - } - - break; - } - - return; - } - - if (killSettings.clearPath !== false) { - Attack.clear(killSettings.range, killSettings.specType); - } - }, - - /** + killMonsters: function (arg = {}) { + if (arg.hasOwnProperty("allowClearing") && !arg.allowClearing) return; + + const killSettings = Object.assign({}, { + clearPath: false, + specType: sdk.monsters.spectype.All, + range: 10, + overrideConfig: false, + }, arg); + + if (Config.Countess.KillGhosts && me.act === 1) { + if (me.area >= sdk.areas.TowerCellarLvl1 && me.area <= sdk.areas.TowerCellarLvl5) { + let monList = (Attack.getMob(sdk.monsters.Ghost1, sdk.monsters.spectype.All, 30) || []); + monList.length > 0 && Attack.clearList(monList); + } + } + + if ((typeof Config.ClearPath === "number" || typeof Config.ClearPath === "object") + && killSettings.clearPath === false && !killSettings.overrideConfig) { + switch (typeof Config.ClearPath) { + case "number": + Attack.clear(30, Config.ClearPath); + + break; + case "object": + if (!Config.ClearPath.hasOwnProperty("Areas") + || !Config.ClearPath.Areas.length + || Config.ClearPath.Areas.includes(me.area)) { + Attack.clear(Config.ClearPath.Range, Config.ClearPath.Spectype); + } + + break; + } + + return; + } + + if (killSettings.clearPath !== false) { + Attack.clear(killSettings.range, killSettings.specType); + } + }, + + /** * Open chests while pathing */ - popChests: function () { - // fastPick check? should only open chests if surrounding monsters have been cleared or if fastPick is active - // note: clear of surrounding monsters of the spectype we are set to clear - Config.OpenChests.Enabled && Misc.openChests(Config.OpenChests.Range); - }, + popChests: function () { + // fastPick check? should only open chests if surrounding monsters have been cleared or if fastPick is active + // note: clear of surrounding monsters of the spectype we are set to clear + Config.OpenChests.Enabled && Misc.openChests(Config.OpenChests.Range); + }, - /** + /** * Scan shrines while pathing */ - getShrines: function () { - Config.ScanShrines.length > 0 && Misc.scanShrines(null, this.shrinesToIgnore); - } + getShrines: function () { + Config.ScanShrines.length > 0 && Misc.scanShrines(null, this.shrinesToIgnore); + } }; const PathDebug = { - /** + /** * @type {Line[]} */ - hooks: [], - enableHooks: false, + hooks: [], + enableHooks: false, - /** + /** * Draw our path on the screen * @param {PathNode[]} path * @returns {void} */ - drawPath: function (path) { - if (!this.enableHooks) return; + drawPath: function (path) { + if (!this.enableHooks) return; - this.removeHooks(); + this.removeHooks(); - if (path.length < 2) return; + if (path.length < 2) return; - for (let i = 0; i < path.length - 1; i += 1) { - this.hooks.push(new Line(path[i].x, path[i].y, path[i + 1].x, path[i + 1].y, 0x84, true)); - } - }, + for (let i = 0; i < path.length - 1; i += 1) { + this.hooks.push(new Line(path[i].x, path[i].y, path[i + 1].x, path[i + 1].y, 0x84, true)); + } + }, - removeHooks: function () { - for (let i = 0; i < this.hooks.length; i += 1) { - PathDebug.hooks[i].remove(); - } + removeHooks: function () { + for (let i = 0; i < this.hooks.length; i += 1) { + PathDebug.hooks[i].remove(); + } - PathDebug.hooks = []; - }, + PathDebug.hooks = []; + }, - /** + /** * Check if a set of coords are a set path * @param {PathNode[]} path * @param {number} x * @param {number} y * @returns {boolean} */ - coordsInPath: function (path, x, y) { - for (let i = 0; i < path.length; i += 1) { - if (getDistance(x, y, path[i].x, path[i].y) < 5) { - return true; - } - } - - return false; - } + coordsInPath: function (path, x, y) { + for (let i = 0; i < path.length; i += 1) { + if (getDistance(x, y, path[i].x, path[i].y) < 5) { + return true; + } + } + + return false; + } }; // todo - test path generating in a dedicated thread to prevent lagging main thread const Pather = { - initialized: false, - teleport: true, - recursion: true, - walkDistance: 5, - teleDistance: 40, - lastPortalTick: 0, - cancelFlags: [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, - sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.TradePrompt, sdk.uiflags.Stash, sdk.uiflags.Cube - ], - wpAreas: [ - sdk.areas.RogueEncampment, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.OuterCloister, - sdk.areas.JailLvl1, sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2, sdk.areas.LutGholein, sdk.areas.A2SewersLvl2, sdk.areas.DryHills, - sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic, - sdk.areas.KurastDocktown, sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, - sdk.areas.UpperKurast, sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2, sdk.areas.PandemoniumFortress, sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, - sdk.areas.Harrogath, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, sdk.areas.HallsofPain, - sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 - ], - nonTownWpAreas: [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.OuterCloister, - sdk.areas.JailLvl1, sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2, sdk.areas.A2SewersLvl2, sdk.areas.DryHills, - sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic, - sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, - sdk.areas.UpperKurast, sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2, sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, - sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, sdk.areas.HallsofPain, - sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 - ], - nextAreas: {}, - - init: function () { - if (!this.initialized) { - me.classic && (Pather.nonTownWpAreas = this.nonTownWpAreas.filter((wp) => wp < sdk.areas.Harrogath)); - if (!Config.WaypointMenu) { - !getWaypoint(1) && this.getWP(me.area); - me.cancelUIFlags(); - Pather.initialized = true; - } - } - }, - - /** + initialized: false, + teleport: true, + recursion: true, + walkDistance: 5, + teleDistance: 40, + lastPortalTick: 0, + cancelFlags: [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, + sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.TradePrompt, sdk.uiflags.Stash, sdk.uiflags.Cube + ], + wpAreas: [ + sdk.areas.RogueEncampment, sdk.areas.ColdPlains, sdk.areas.StonyField, + sdk.areas.DarkWood, sdk.areas.BlackMarsh, sdk.areas.OuterCloister, + sdk.areas.JailLvl1, sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2, + sdk.areas.LutGholein, sdk.areas.A2SewersLvl2, sdk.areas.DryHills, + sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, sdk.areas.LostCity, + sdk.areas.PalaceCellarLvl1, sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic, + sdk.areas.KurastDocktown, sdk.areas.SpiderForest, sdk.areas.GreatMarsh, + sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, + sdk.areas.UpperKurast, sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2, + sdk.areas.PandemoniumFortress, sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, + sdk.areas.Harrogath, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, + sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, sdk.areas.HallsofPain, + sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 + ], + nonTownWpAreas: [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.OuterCloister, + sdk.areas.JailLvl1, sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2, + sdk.areas.A2SewersLvl2, sdk.areas.DryHills, + sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, sdk.areas.LostCity, + sdk.areas.PalaceCellarLvl1, sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic, + sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, + sdk.areas.UpperKurast, sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2, + sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, + sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.CrystalizedPassage, + sdk.areas.GlacialTrail, sdk.areas.HallsofPain, + sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 + ], + nextAreas: {}, + + init: function () { + if (!this.initialized) { + me.classic && (Pather.nonTownWpAreas = this.nonTownWpAreas.filter((wp) => wp < sdk.areas.Harrogath)); + if (!Config.WaypointMenu) { + !getWaypoint(1) && this.getWP(me.area); + me.cancelUIFlags(); + Pather.initialized = true; + } + } + }, + + /** * @todo Handle rare bug where teleport skill dissapears from enigma */ - canTeleport: function () { - return this.teleport && (Skill.canUse(sdk.skills.Teleport) || me.getStat(sdk.stats.OSkill, sdk.skills.Teleport)); - }, + canTeleport: function () { + return this.teleport && (Skill.canUse(sdk.skills.Teleport) || me.getStat(sdk.stats.OSkill, sdk.skills.Teleport)); + }, - useTeleport: function () { - let manaTP = Skill.getManaCost(sdk.skills.Teleport); - let numberOfTeleport = ~~(me.mpmax / manaTP); - return !me.inTown && !Config.NoTele && !me.shapeshifted && this.canTeleport() && numberOfTeleport > 2; - }, + useTeleport: function () { + let manaTP = Skill.getManaCost(sdk.skills.Teleport); + let numberOfTeleport = ~~(me.mpmax / manaTP); + return !me.inTown && !Config.NoTele && !me.shapeshifted && this.canTeleport() && numberOfTeleport > 2; + }, - /** + /** * @typedef {object} spotOnDistanceSettings * @property {number} [area] * @property {number} [reductionType] @@ -206,29 +223,30 @@ const Pather = { * @param {spotOnDistanceSettings} givenSettings * @returns {PathNode} */ - spotOnDistance: function (spot, distance, givenSettings = {}) { - const spotSettings = Object.assign({}, { - area: me.area, - reductionType: 2, - coll: (sdk.collision.BlockWalk), - returnSpotOnError: true - }, givenSettings); - - let nodes = (getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, spotSettings.reductionType, 4) || []); + spotOnDistance: function (spot, distance, givenSettings = {}) { + const spotSettings = Object.assign({}, { + area: me.area, + reductionType: 2, + coll: (sdk.collision.BlockWalk), + returnSpotOnError: true + }, givenSettings); + + let nodes = (getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, spotSettings.reductionType, 4) || []); - if (!nodes.length) { - if (spotSettings.reductionType === 2) { - // try again with walking reduction - nodes = getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, 0, 4); - } - if (!nodes.length) return (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y }); - } - - return (nodes.find((node) => getDistance(spot.x, spot.y, node.x, node.y) < distance && Pather.checkSpot(node.x, node.y, spotSettings.coll)) + if (!nodes.length) { + if (spotSettings.reductionType === 2) { + // try again with walking reduction + nodes = getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, 0, 4); + } + if (!nodes.length) return (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y }); + } + + return (nodes.find((node) => getDistance(spot.x, spot.y, node.x, node.y) < distance + && Pather.checkSpot(node.x, node.y, spotSettings.coll)) || (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y })); - }, + }, - /** + /** * @typedef {object} pathSettings * @property {boolean} [allowTeleport] * @property {boolean} [allowClearing] @@ -251,224 +269,247 @@ const Pather = { * @param {pathSettings} givenSettings * @returns {boolean} */ - move: function (target, givenSettings = {}) { - // Abort if dead - if (me.dead) return false; - /** + move: function (target, givenSettings = {}) { + // Abort if dead + if (me.dead) return false; + /** * assign settings * @type {pathSettings} */ - const settings = Object.assign({}, { - clearSettings: { - }, - 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({ - clearPath: false, - range: 10, - specType: 0, - sort: Attack.sortMonsters, - }, settings.clearSettings); - // set settings.clearSettings equal to the now properly asssigned clearSettings - settings.clearSettings = clearSettings; - - !settings.allowClearing && settings.allowClearing !== undefined && (settings.clearSettings.allowClearing = false); - (target instanceof PresetUnit) && (target = target.realCoords()); - - if (settings.minDist > 3) { - target = this.spotOnDistance(target, settings.minDist, { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) }); - } - - /** @constructor */ - function PathAction () { - this.at = 0; - /** @type {PathNode} */ - this.from = { x: null, y: null }; - } - - let fail = 0; - let invalidCheck = false; - let node = { x: target.x, y: target.y }; - const leaped = new PathAction(); - const whirled = new PathAction(); - const cleared = new PathAction(); - - for (let i = 0; i < this.cancelFlags.length; i += 1) { - getUIFlag(this.cancelFlags[i]) && me.cancel(); - } - - if (typeof target.x !== "number" || typeof target.y !== "number") throw new Error("move: Coords must be numbers"); - if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, sdk.collision.BlockMissile, 5)) return true; - - let useTeleport = settings.allowTeleport && this.useTeleport(); - const tpMana = useTeleport ? Skill.getManaCost(sdk.skills.Teleport) : Infinity; - const annoyingArea = [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3].includes(me.area); - let path = getPath(me.area, target.x, target.y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? (annoyingArea ? 30 : this.teleDistance) : this.walkDistance); - if (!path) throw new Error("move: Failed to generate path."); - - if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { - settings.retry = 10; - } - - 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); - - for (let i = 0; i < this.cancelFlags.length; i += 1) { - if (getUIFlag(this.cancelFlags[i])) me.cancel(); - } - - 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 = this.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.overrideConfig, settings.clearSettings.range] = [true, 5]); - settings.retry <= 3 && !useTeleport && (settings.retry = 15); - } - - if (useTeleport && tpMana <= me.mp ? this.teleportTo(node.x, node.y) : this.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { - if (!me.inTown) { - if (Pather.recursion) { - Pather.recursion = false; - try { - NodeAction.go(settings.clearSettings); - node.distance > 5 && this.move(node, settings); - } finally { - Pather.recursion = true; - } - } - - settings.allowTown && Misc.townCheck(); - } - } else { - if (!me.inTown) { - /** + const settings = Object.assign({}, { + clearSettings: { + }, + 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({ + clearPath: false, + range: 10, + specType: 0, + sort: Attack.sortMonsters, + }, settings.clearSettings); + // set settings.clearSettings equal to the now properly asssigned clearSettings + settings.clearSettings = clearSettings; + + !settings.allowClearing && settings.allowClearing !== undefined && (settings.clearSettings.allowClearing = false); + (target instanceof PresetUnit) && (target = target.realCoords()); + + if (settings.minDist > 3) { + target = this.spotOnDistance( + target, + settings.minDist, + { returnSpotOnError: settings.returnSpotOnError, reductionType: (me.inTown ? 0 : 2) } + ); + } + + /** @constructor */ + function PathAction () { + this.at = 0; + /** @type {PathNode} */ + this.from = { x: null, y: null }; + } + + let fail = 0; + let invalidCheck = false; + let node = { x: target.x, y: target.y }; + const leaped = new PathAction(); + const whirled = new PathAction(); + const cleared = new PathAction(); + + for (let i = 0; i < this.cancelFlags.length; i += 1) { + getUIFlag(this.cancelFlags[i]) && me.cancel(); + } + + if (typeof target.x !== "number" || typeof target.y !== "number") throw new Error("move: Coords must be numbers"); + if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, sdk.collision.BlockMissile, 5)) return true; + + let useTeleport = settings.allowTeleport && this.useTeleport(); + const tpMana = useTeleport ? Skill.getManaCost(sdk.skills.Teleport) : Infinity; + const annoyingArea = [ + sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3 + ].includes(me.area); + let path = getPath( + me.area, + target.x, target.y, + me.x, me.y, + useTeleport ? 1 : 0, + useTeleport ? (annoyingArea ? 30 : this.teleDistance) : this.walkDistance + ); + if (!path) throw new Error("move: Failed to generate path."); + + if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { + settings.retry = 10; + } + + 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); + + for (let i = 0; i < this.cancelFlags.length; i += 1) { + if (getUIFlag(this.cancelFlags[i])) me.cancel(); + } + + 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 = this.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.overrideConfig, settings.clearSettings.range] = [true, 5]); + settings.retry <= 3 && !useTeleport && (settings.retry = 15); + } + + if (useTeleport && tpMana <= me.mp + ? this.teleportTo(node.x, node.y) + : this.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { + if (!me.inTown) { + if (Pather.recursion) { + Pather.recursion = false; + try { + NodeAction.go(settings.clearSettings); + node.distance > 5 && this.move(node, settings); + } finally { + Pather.recursion = true; + } + } + + settings.allowTown && Misc.townCheck(); + } + } else { + if (!me.inTown) { + /** * @todo I think some of this needs to be re-worked, I've noticed recursive Attacking/Picking */ - 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 && (this.kickBarrels(node.x, node.y) || this.openDoors(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]; - } - } - } - - /** + 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 && (this.kickBarrels(node.x, node.y) || this.openDoors(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("move: 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); - } - - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); - PathDebug.removeHooks(); - - return getDistance(me, node.x, node.y) < 5; - }, - - /** + 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("move: 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); + } + + useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + PathDebug.removeHooks(); + + return getDistance(me, node.x, node.y) < 5; + }, + + /** * * @param {number} x * @param {number} y @@ -476,11 +517,11 @@ const Pather = { * @param {pathSettings} givenSettings * @returns {boolean} */ - moveNear: function (x, y, minDist, givenSettings = {}) { - return Pather.move({ x: x, y: y }, Object.assign({ minDist: minDist }, givenSettings)); - }, + moveNear: function (x, y, minDist, givenSettings = {}) { + return Pather.move({ x: x, y: y }, Object.assign({ minDist: minDist }, givenSettings)); + }, - /** + /** * @param {number} x - the x coord to move to * @param {number} y - the y coord to move to * @param {number} retry - number of attempts before aborting @@ -488,291 +529,307 @@ const Pather = { * @param {boolean} pop - remove last node * @returns {boolean} */ - moveTo: function (x, y, retry, clearPath, pop) { - return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, allowClearing: clearPath }); - }, + moveTo: function (x, y, retry, clearPath, pop) { + return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, allowClearing: clearPath }); + }, - /** + /** * * @param {number} x * @param {number} y * @param {pathSettings} givenSettings * @returns */ - moveToEx: function (x, y, givenSettings = {}) { - return Pather.move({ x: x, y: y }, givenSettings); - }, + moveToEx: function (x, y, givenSettings = {}) { + return Pather.move({ x: x, y: y }, givenSettings); + }, - /** + /** * @param {number} x - the x coord to teleport to * @param {number} y - the y coord to teleport to * @param {number} [maxRange] - max acceptable distance from node * @returns {boolean} * @todo does this need a validLocation check? - maybe if we fail once check the spot */ - teleportTo: function (x, y, maxRange = 5) { - for (let i = 0; i < 3; i += 1) { - Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); - let tick = getTickCount(); - let pingDelay = i === 0 ? 150 : me.getPingDelay(); + teleportTo: function (x, y, maxRange = 5) { + for (let i = 0; i < 3; i += 1) { + Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); + let tick = getTickCount(); + let pingDelay = i === 0 ? 150 : me.getPingDelay(); - while (getTickCount() - tick < Math.max(500, pingDelay * 2 + 200)) { - if ([x, y].distance < maxRange) { - return true; - } + while (getTickCount() - tick < Math.max(500, pingDelay * 2 + 200)) { + if ([x, y].distance < maxRange) { + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; - }, + return false; + }, - /** + /** * @param {number} x - the x coord to teleport to * @param {number} y - the y coord to teleport to * @param {number} [minDist] - minimal distance from x/y before returning true * @returns {boolean} - sucessfully moved within minDist */ - walkTo: function (x, y, minDist) { - while (!me.gameReady) { - delay(100); - } + walkTo: function (x, y, minDist) { + while (!me.gameReady) { + delay(100); + } - if (x === undefined || y === undefined || me.dead) return false; - minDist === undefined && (minDist = me.inTown ? 2 : 4); + if (x === undefined || y === undefined || me.dead) return false; + minDist === undefined && (minDist = me.inTown ? 2 : 4); - let nTimer; - let [nFail, attemptCount] = [0, 0]; + let nTimer; + let [nFail, attemptCount] = [0, 0]; - /** + /** * @todo add cleansing/meditation here as well */ - // credit @Jaenster - // Stamina handler and Charge - if (!me.inTown) { - // Check if I have a stamina potion and use it if I do - if (me.staminaPercent <= 20) { - let stam = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((i) => i.classid === sdk.items.StaminaPotion && i.isInInventory).first(); - !!stam && !me.deadOrInSequence && stam.use(); - } - (me.running && me.staminaPercent <= 15) && me.walk(); - // the less stamina you have, the more you wait to recover - let recover = me.staminaMaxDuration < 30 ? 80 : 50; - (me.walking && me.staminaPercent >= recover) && me.run(); - if (Skill.canUse(sdk.skills.Charge) && me.paladin && me.mp >= 9 && [x, y].distance > 8 && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { - if (Skill.canUse(sdk.skills.Vigor)) { - Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); - } else if (!Config.Vigor && !Attack.auradin && Skill.canUse(sdk.skills.HolyFreeze)) { - // Useful in classic to keep mobs cold while you rush them - Skill.setSkill(sdk.skills.HolyFreeze, sdk.skills.hand.Right); - } - Misc.click(0, 1, x, y); - while (!me.idle) { - delay(40); - } - } - - if (Skill.canUse(sdk.skills.Blaze) && me.mp > (Skill.getManaCost(sdk.skills.Blaze) * 2) && !me.getState(sdk.states.Blaze)) { - Skill.cast(sdk.skills.Blaze); - } - } else { - me.walking && me.run(); - Skill.canUse(sdk.skills.Vigor) && Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); - } - - MainLoop: - while (getDistance(me.x, me.y, x, y) > minDist && !me.dead) { - if (me.paladin && !me.inTown) { - Skill.canUse(sdk.skills.Vigor) ? Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right) : Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - } - - if (this.openDoors(x, y) && getDistance(me.x, me.y, x, y) <= minDist) { - return true; - } - - if (attemptCount > 1 && CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWall | sdk.collision.ClosedDoor)) { - this.openDoors(me.x, me.y); - } - - Misc.click(0, 0, x, y); - - attemptCount += 1; - nTimer = getTickCount(); - - while (!me.moving) { - if (me.dead) return false; - - if ((getTickCount() - nTimer) > 500) { - if (nFail >= 3) { - break MainLoop; - } - - nFail += 1; - let angle = Math.atan2(me.y - y, me.x - x); - let angles = [Math.PI / 2, -Math.PI / 2]; - - for (let i = 0; i < angles.length; i += 1) { - // TODO: might need rework into getnearestwalkable - let whereToClick = { - x: Math.round(Math.cos(angle + angles[i]) * 5 + me.x), - y: Math.round(Math.sin(angle + angles[i]) * 5 + me.y) - }; - - if (Attack.validSpot(whereToClick.x, whereToClick.y)) { - Misc.click(0, 0, whereToClick.x, whereToClick.y); - - let tick = getTickCount(); - - while (getDistance(me, whereToClick) > 2 && getTickCount() - tick < 1000) { - delay(40); - } - - break; - } - } - - break; - } - - delay(10); - } - - attemptCount > 1 && this.kickBarrels(x, y); - - // Wait until we're done walking - idle or dead - while (getDistance(me.x, me.y, x, y) > minDist && !me.idle) { - delay(10); - } - - if (attemptCount >= 3) { - break; - } - } - return (!me.dead && getDistance(me.x, me.y, x, y) <= minDist); - }, - - /** + // credit @Jaenster + // Stamina handler and Charge + if (!me.inTown) { + // Check if I have a stamina potion and use it if I do + if (me.staminaPercent <= 20) { + let stam = me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter((i) => i.classid === sdk.items.StaminaPotion && i.isInInventory) + .first(); + !!stam && !me.deadOrInSequence && stam.use(); + } + (me.running && me.staminaPercent <= 15) && me.walk(); + // the less stamina you have, the more you wait to recover + let recover = me.staminaMaxDuration < 30 ? 80 : 50; + (me.walking && me.staminaPercent >= recover) && me.run(); + if (Skill.canUse(sdk.skills.Charge) && me.paladin + && me.mp >= 9 && [x, y].distance > 8 + && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { + if (Skill.canUse(sdk.skills.Vigor)) { + Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); + } else if (!Config.Vigor && !Attack.auradin && Skill.canUse(sdk.skills.HolyFreeze)) { + // Useful in classic to keep mobs cold while you rush them + Skill.setSkill(sdk.skills.HolyFreeze, sdk.skills.hand.Right); + } + Misc.click(0, 1, x, y); + while (!me.idle) { + delay(40); + } + } + + if (Skill.canUse(sdk.skills.Blaze) + && me.mp > (Skill.getManaCost(sdk.skills.Blaze) * 2) + && !me.getState(sdk.states.Blaze)) { + Skill.cast(sdk.skills.Blaze); + } + } else { + me.walking && me.run(); + Skill.canUse(sdk.skills.Vigor) && Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); + } + + MainLoop: + while (getDistance(me.x, me.y, x, y) > minDist && !me.dead) { + if (me.paladin && !me.inTown) { + Skill.canUse(sdk.skills.Vigor) + ? Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right) + : Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + } + + if (this.openDoors(x, y) && getDistance(me.x, me.y, x, y) <= minDist) { + return true; + } + + if (attemptCount > 1 + && CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWall | sdk.collision.ClosedDoor)) { + this.openDoors(me.x, me.y); + } + + Misc.click(0, 0, x, y); + + attemptCount += 1; + nTimer = getTickCount(); + + while (!me.moving) { + if (me.dead) return false; + + if ((getTickCount() - nTimer) > 500) { + if (nFail >= 3) { + break MainLoop; + } + + nFail += 1; + let angle = Math.atan2(me.y - y, me.x - x); + let angles = [Math.PI / 2, -Math.PI / 2]; + + for (let i = 0; i < angles.length; i += 1) { + // TODO: might need rework into getnearestwalkable + let whereToClick = { + x: Math.round(Math.cos(angle + angles[i]) * 5 + me.x), + y: Math.round(Math.sin(angle + angles[i]) * 5 + me.y) + }; + + if (Attack.validSpot(whereToClick.x, whereToClick.y)) { + Misc.click(0, 0, whereToClick.x, whereToClick.y); + + let tick = getTickCount(); + + while (getDistance(me, whereToClick) > 2 && getTickCount() - tick < 1000) { + delay(40); + } + + break; + } + } + + break; + } + + delay(10); + } + + attemptCount > 1 && this.kickBarrels(x, y); + + // Wait until we're done walking - idle or dead + while (getDistance(me.x, me.y, x, y) > minDist && !me.idle) { + delay(10); + } + + if (attemptCount >= 3) { + break; + } + } + return (!me.dead && getDistance(me.x, me.y, x, y) <= minDist); + }, + + /** * If there is a door in our path, open it so we can continue moving * @param {number} x - the x coord of the node close to the door * @param {number} y - the y coord of the node close to the door * @returns {boolean} true if we opened any doors that were in our way */ - openDoors: function (x, y) { - if (me.inTown && me.act !== 5) return false; - - (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); - - // Regular doors - let door = Game.getObject("door", sdk.objects.mode.Inactive); - - if (door) { - do { - if ((getDistance(door, x, y) < 4 && door.distance < 9) || door.distance < 4) { - for (let i = 0; i < 3; i++) { - Misc.click(0, 0, door); - - if (Misc.poll(() => door.mode === sdk.objects.mode.Active, 1000, 30)) { - return true; - } - - i === 2 && Packet.flash(me.gid); - } - } - } while (door.getNext()); - } - - // handle act 5 gate - if ([sdk.areas.Harrogath, sdk.areas.BloodyFoothills].includes(me.area)) { - let gate = Game.getObject("gate", sdk.objects.mode.Inactive); - - if (gate) { - if ((getDistance(gate, x, y) < 4 && gate.distance < 9) || gate.distance < 4) { - for (let i = 0; i < 3; i++) { - Misc.click(0, 0, gate); - - if (Misc.poll(() => gate.mode, 1000, 50)) { - return true; - } - - i === 2 && Packet.flash(me.gid); - } - } - } - } - - // Monsta doors (Barricaded) - not sure if this is really needed anymore - let monstadoor = Game.getMonster("barricaded door"); - - if (monstadoor) { - do { - if (monstadoor.hp > 0 && (getDistance(monstadoor, x, y) < 4 && monstadoor.distance < 9) || monstadoor.distance < 4) { - for (let p = 0; p < 20 && monstadoor.hp; p++) { - Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), monstadoor); - } - } - } while (monstadoor.getNext()); - } - - let monstawall = Game.getMonster("barricade"); + openDoors: function (x, y) { + if (me.inTown && me.act !== 5) return false; + + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); + + // Regular doors + let door = Game.getObject("door", sdk.objects.mode.Inactive); + + if (door) { + do { + if ((getDistance(door, x, y) < 4 && door.distance < 9) || door.distance < 4) { + for (let i = 0; i < 3; i++) { + Misc.click(0, 0, door); + + if (Misc.poll(() => door.mode === sdk.objects.mode.Active, 1000, 30)) { + return true; + } + + i === 2 && Packet.flash(me.gid); + } + } + } while (door.getNext()); + } + + // handle act 5 gate + if ([sdk.areas.Harrogath, sdk.areas.BloodyFoothills].includes(me.area)) { + let gate = Game.getObject("gate", sdk.objects.mode.Inactive); + + if (gate) { + if ((getDistance(gate, x, y) < 4 && gate.distance < 9) || gate.distance < 4) { + for (let i = 0; i < 3; i++) { + Misc.click(0, 0, gate); + + if (Misc.poll(() => gate.mode, 1000, 50)) { + return true; + } + + i === 2 && Packet.flash(me.gid); + } + } + } + } + + // Monsta doors (Barricaded) - not sure if this is really needed anymore + let monstadoor = Game.getMonster("barricaded door"); + + if (monstadoor) { + do { + if (monstadoor.hp > 0 && (getDistance(monstadoor, x, y) < 4 + && monstadoor.distance < 9) || monstadoor.distance < 4) { + for (let p = 0; p < 20 && monstadoor.hp; p++) { + Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), monstadoor); + } + } + } while (monstadoor.getNext()); + } + + let monstawall = Game.getMonster("barricade"); - if (monstawall) { - do { - if (monstawall.hp > 0 && (getDistance(monstawall, x, y) < 4 && monstawall.distance < 9) || monstawall.distance < 4) { - for (let p = 0; p < 20 && monstawall.hp; p++) { - Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), monstawall); - } - } - } while (monstawall.getNext()); - } - - return false; - }, - - /** + if (monstawall) { + do { + if (monstawall.hp > 0 && (getDistance(monstawall, x, y) < 4 + && monstawall.distance < 9) || monstawall.distance < 4) { + for (let p = 0; p < 20 && monstawall.hp; p++) { + Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), monstawall); + } + } + } while (monstawall.getNext()); + } + + return false; + }, + + /** * Small and annoying things like barrels can block our path, open them if they are near us * @param {number} x - the x coord of the node close to the barrel * @param {number} y - the y coord of the node close to the barrel * @returns {boolean} true if we kicked any barrels that were in our way */ - kickBarrels: function (x, y) { - if (me.inTown) return false; - - (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); - - // anything small and annoying really - let barrels = getUnits(sdk.unittype.Object) - .filter(function (el) { - return (el.name && el.mode === sdk.objects.mode.Inactive - && ["ratnest", "goo pile", "barrel", "basket", "largeurn", "jar3", "jar2", "jar1", "urn", "jug", "barrel wilderness", "cocoon"].includes(el.name.toLowerCase()) + kickBarrels: function (x, y) { + if (me.inTown) return false; + + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); + + // anything small and annoying really + let _things = [ + "ratnest", "goo pile", "barrel", "basket", + "largeurn", "jar3", "jar2", "jar1", + "urn", "jug", "barrel wilderness", "cocoon" + ]; + let barrels = getUnits(sdk.unittype.Object) + .filter(function (el) { + return (el.name && el.mode === sdk.objects.mode.Inactive + && _things.includes(el.name.toLowerCase()) && ((getDistance(el, x, y) < 4 && el.distance < 9) || el.distance < 4)); - }); - let brokeABarrel = false; - - while (barrels.length > 0) { - barrels.sort(Sort.units); - let unit = barrels.shift(); - - if (unit && !checkCollision(me, unit, sdk.collision.WallOrRanged)) { - try { - for (let i = 0; i < 5; i++) { - i < 3 ? Packet.entityInteract(unit) : Misc.click(0, 0, unit); - - if (unit.mode) { - brokeABarrel = true; - break; - } - } - } catch (e) { - continue; - } - } - } - - return brokeABarrel; - }, - - /** + }); + let brokeABarrel = false; + + while (barrels.length > 0) { + barrels.sort(Sort.units); + let unit = barrels.shift(); + + if (unit && !checkCollision(me, unit, sdk.collision.WallOrRanged)) { + try { + for (let i = 0; i < 5; i++) { + i < 3 ? Packet.entityInteract(unit) : Misc.click(0, 0, unit); + + if (unit.mode) { + brokeABarrel = true; + break; + } + } + } catch (e) { + continue; + } + } + } + + return brokeABarrel; + }, + + /** * Move to unit * @param {Unit} unit - unit to move to * @param {number} [offX] - offset from unit's x coord @@ -781,50 +838,51 @@ const Pather = { * @param {boolean} [pop] - remove last node * @returns {boolean} Sucessfully moved to unit */ - moveToUnit: function (unit, offX, offY, clearPath, pop) { - const useTeleport = this.useTeleport(); + moveToUnit: function (unit, offX, offY, clearPath, pop) { + const useTeleport = this.useTeleport(); - offX === undefined && (offX = 0); - offY === undefined && (offY = 0); - clearPath === undefined && (clearPath = false); - pop === undefined && (pop = false); + offX === undefined && (offX = 0); + offY === undefined && (offY = 0); + clearPath === undefined && (clearPath = false); + pop === undefined && (pop = false); - if (!unit || !unit.hasOwnProperty("x") || !unit.hasOwnProperty("y")) throw new Error("moveToUnit: Invalid unit."); + if (!unit || !unit.hasOwnProperty("x") || !unit.hasOwnProperty("y")) throw new Error("moveToUnit: Invalid unit."); + (unit instanceof PresetUnit) && (unit = { x: unit.roomx * 5 + unit.x, y: unit.roomy * 5 + unit.y }); - (unit instanceof PresetUnit) && (unit = { x: unit.roomx * 5 + unit.x, y: unit.roomy * 5 + unit.y }); + let [x, y] = [unit.x + offX, unit.y + offY]; - if (!useTeleport) { - // The unit will most likely be moving so call the first walk with 'pop' parameter - this.moveTo(unit.x + offX, unit.y + offY, 0, clearPath, true); - } + if (!useTeleport) { + // The unit will most likely be moving so call the first walk with 'pop' parameter + this.moveTo(x, y, 0, clearPath, true); + } - return this.moveTo(unit.x + offX, unit.y + offY, useTeleport && unit.type && unit.isMonster ? 3 : 0, clearPath, pop); - }, + return this.moveTo(x, y, useTeleport && unit.type && unit.isMonster ? 3 : 0, clearPath, pop); + }, - /** + /** * Move near unit * @param {Unit} unit - unit to move near * @param {boolean} [clearPath] - kill monsters while moving * @param {boolean} [pop] - remove last node * @returns {boolean} Sucessfully moved near unit */ - moveNearUnit: function (unit, minDist, clearPath, pop = false) { - const useTeleport = this.useTeleport(); - minDist === undefined && (minDist = me.inTown ? 2 : 5); + moveNearUnit: function (unit, minDist, clearPath, pop = false) { + const useTeleport = this.useTeleport(); + minDist === undefined && (minDist = me.inTown ? 2 : 5); - if (!unit || !unit.hasOwnProperty("x") || !unit.hasOwnProperty("y")) throw new Error("moveNearUnit: Invalid unit."); + if (!unit || !unit.hasOwnProperty("x") || !unit.hasOwnProperty("y")) throw new Error("moveNearUnit: Invalid unit."); - (unit instanceof PresetUnit) && (unit = { x: unit.roomx * 5 + unit.x, y: unit.roomy * 5 + unit.y }); + (unit instanceof PresetUnit) && (unit = { x: unit.roomx * 5 + unit.x, y: unit.roomy * 5 + unit.y }); - if (!useTeleport) { - // The unit will most likely be moving so call the first walk with 'pop' parameter - this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: true }); - } + if (!useTeleport) { + // The unit will most likely be moving so call the first walk with 'pop' parameter + this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: true }); + } - return this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: pop }); - }, + return this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: pop }); + }, - /** + /** * Move near preset unit * @param {number} area - area of the preset unit * @param {number} unitType - type of the preset unit @@ -834,27 +892,30 @@ const Pather = { * @param {boolean} [pop] - remove last node * @returns {boolean} Sucessfully moved near unit */ - moveNearPreset: function (area, unitType, unitId, minDist, clearPath = false, pop = false) { - if (area === undefined || unitType === undefined || unitId === undefined) { - throw new Error("moveNearPreset: Invalid parameters."); - } + moveNearPreset: function (area, unitType, unitId, minDist, clearPath = false, pop = false) { + if (area === undefined || unitType === undefined || unitId === undefined) { + throw new Error("moveNearPreset: Invalid parameters."); + } - me.area !== area && Pather.journeyTo(area); - let presetUnit = getPresetUnit(area, unitType, unitId); + me.area !== area && Pather.journeyTo(area); + let presetUnit = getPresetUnit(area, unitType, unitId); - if (!presetUnit) { - throw new Error("moveNearPreset: Couldn't find preset unit - id: " + unitId + " unitType: " + unitType + " in area: " + getAreaName(area)); - } + if (!presetUnit) { + throw new Error( + "moveNearPreset: Couldn't find preset unit - id: " + unitId + + " unitType: " + unitType + " in area: " + getAreaName(area) + ); + } - delay(40); - Misc.poll(() => me.gameReady, 500, 100); + delay(40); + Misc.poll(() => me.gameReady, 500, 100); - let unit = presetUnit.realCoords(); + let unit = presetUnit.realCoords(); - return this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: pop }); - }, + return this.moveNear(unit.x, unit.y, minDist, { clearSettings: { clearPath: clearPath }, pop: pop }); + }, - /** + /** * Move to preset unit * @param {number} area - area of the preset unit * @param {number} unitType - type of the preset unit @@ -865,550 +926,457 @@ const Pather = { * @param {boolean} [pop] - remove last node * @returns {boolean} Sucessfully moved to unit */ - moveToPreset: function (area, unitType, unitId, offX, offY, clearPath, pop) { - if (area === undefined || unitType === undefined || unitId === undefined) { - throw new Error("moveToPreset: Invalid parameters."); - } - - offX === undefined && (offX = 0); - offY === undefined && (offY = 0); - clearPath === undefined && (clearPath = false); - pop === undefined && (pop = false); - - me.area !== area && Pather.journeyTo(area); - let presetUnit = getPresetUnit(area, unitType, unitId); - - if (!presetUnit) { - throw new Error("moveToPreset: Couldn't find preset unit - id: " + unitId + " unitType: " + unitType + " in area: " + getAreaName(area)); - } - - delay(40); - Misc.poll(() => me.gameReady, 500, 100); - - return this.moveTo(presetUnit.roomx * 5 + presetUnit.x + offX, presetUnit.roomy * 5 + presetUnit.y + offY, 3, clearPath, pop); - }, - - /** + moveToPreset: function (area, unitType, unitId, offX, offY, clearPath, pop) { + if (area === undefined || unitType === undefined || unitId === undefined) { + throw new Error("moveToPreset: Invalid parameters."); + } + + offX === undefined && (offX = 0); + offY === undefined && (offY = 0); + clearPath === undefined && (clearPath = false); + pop === undefined && (pop = false); + + me.area !== area && Pather.journeyTo(area); + let presetUnit = getPresetUnit(area, unitType, unitId); + + if (!presetUnit) { + throw new Error( + "moveToPreset: Couldn't find preset unit - id: " + unitId + + " unitType: " + unitType + " in area: " + getAreaName(area) + ); + } + + delay(40); + Misc.poll(() => me.gameReady, 500, 100); + let { x, y } = presetUnit.realCoords(); + + return this.moveTo(x + offX, y + offY, 3, clearPath, pop); + }, + + /** * @todo * moveTo/NearPresetTile */ - /** + /** * * @param {number} area * @param {number} unitId * @param {pathSettings} givenSettings */ - moveToPresetObject: function (area, unitId, givenSettings = {}) { - if (area === undefined || unitId === undefined) { - throw new Error("moveToPreset: Invalid parameters."); - } + moveToPresetObject: function (area, unitId, givenSettings = {}) { + if (area === undefined || unitId === undefined) { + throw new Error("moveToPreset: Invalid parameters."); + } - let offX = givenSettings.hasOwnProperty("offX") ? givenSettings.offX : 0; - let offY = givenSettings.hasOwnProperty("offY") ? givenSettings.offY : 0; + let offX = givenSettings.hasOwnProperty("offX") ? givenSettings.offX : 0; + let offY = givenSettings.hasOwnProperty("offY") ? givenSettings.offY : 0; - me.area !== area && Pather.journeyTo(area); - let presetUnit = Game.getPresetObject(area, unitId); + me.area !== area && Pather.journeyTo(area); + let presetUnit = Game.getPresetObject(area, unitId); - if (!presetUnit) { - throw new Error("moveToPresetObject: Couldn't find preset unit - id: " + unitId + " in area: " + getAreaName(area)); - } + if (!presetUnit) { + throw new Error( + "moveToPresetObject: Couldn't find preset unit - id: " + unitId + + " in area: " + getAreaName(area) + ); + } - let realCoords = presetUnit.realCoords(); + delay(40); + Misc.poll(() => me.gameReady, 500, 100); + let { x, y } = presetUnit.realCoords(); - delay(40); - Misc.poll(() => me.gameReady, 500, 100); + return this.moveToEx(x + offX, y + offY, givenSettings); + }, - return this.moveToEx(realCoords.x + offX, realCoords.y + offY, givenSettings); - }, - - /** + /** * * @param {number} area * @param {number} unitId * @param {pathSettings} givenSettings */ - moveToPresetMonster: function (area, unitId, givenSettings = {}) { - if (area === undefined || unitId === undefined) { - throw new Error("moveToPreset: Invalid parameters."); - } - - let offX = givenSettings.hasOwnProperty("offX") ? givenSettings.offX : 0; - let offY = givenSettings.hasOwnProperty("offY") ? givenSettings.offY : 0; + moveToPresetMonster: function (area, unitId, givenSettings = {}) { + if (area === undefined || unitId === undefined) { + throw new Error("moveToPreset: Invalid parameters."); + } - me.area !== area && Pather.journeyTo(area); - let presetUnit = Game.getPresetMonster(area, unitId); + let offX = givenSettings.hasOwnProperty("offX") ? givenSettings.offX : 0; + let offY = givenSettings.hasOwnProperty("offY") ? givenSettings.offY : 0; - if (!presetUnit) { - throw new Error("moveToPresetMonster: Couldn't find preset unit - id: " + unitId + " in area: " + getAreaName(area)); - } + me.area !== area && Pather.journeyTo(area); + let presetUnit = Game.getPresetMonster(area, unitId); - let realCoords = presetUnit.realCoords(); + if (!presetUnit) { + throw new Error( + "moveToPresetMonster: Couldn't find preset unit - id: " + unitId + + " in area: " + getAreaName(area) + ); + } - delay(40); - Misc.poll(() => me.gameReady, 500, 100); + delay(40); + Misc.poll(() => me.gameReady, 500, 100); + let { x, y } = presetUnit.realCoords(); - return this.moveToEx(realCoords.x + offX, realCoords.y + offY, givenSettings); - }, + return this.moveToEx(x + offX, y + offY, 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 {boolean} [clearPath] - kill monsters while moving + * @param {pathSettings} givenSettings */ - moveToExit: function (targetArea, use, clearPath) { - if (targetArea === undefined) return false; - - console.time("moveToExit"); - console.info(true, "ÿc7MyArea: ÿc0" + getAreaName(me.area) + " ÿc7TargetArea: ÿc0" + getAreaName(targetArea)); - let areas = Array.isArray(targetArea) ? targetArea : [targetArea]; - let finalDest = areas.last(); - - for (let i = 0; i < areas.length; i += 1) { - if (me.area === areas[i]) { - console.debug("Already in: " + getAreaName(areas[i])); - continue; - } - - console.info(null, "ÿc0Moving from: " + getAreaName(me.area) + " to " + getAreaName(areas[i])); - - Config.DebugMode.Path && console.time("getArea"); - let area = Misc.poll(() => getArea(me.area)); - Config.DebugMode.Path && console.timeEnd("getArea"); - - if (!area) throw new Error("moveToExit: error in getArea()"); - - let t2 = getTickCount(); - let currTarget = areas[i]; - let exits = (area.exits || []); - let checkExits = []; - - if (!exits.length) return false; - Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t2) + " to assign vars"); - - let t3 = getTickCount(); - for (let j = 0; j < exits.length; j += 1) { - if (!exits[j].hasOwnProperty("target") || exits[j].target !== currTarget) continue; - Config.DebugMode.Path && console.debug(exits[j]); - let currCheckExit = { - x: exits[j].x, - y: exits[j].y, - type: exits[j].type, - target: exits[j].target, - tileid: exits[j].tileid - }; - - currCheckExit.target === currTarget && checkExits.push(currCheckExit); - } - Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t3) + " to find all exits"); - - if (checkExits.length > 0) { - Config.DebugMode.Path && console.debug(checkExits); - let t4 = getTickCount(); - // 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.sort((a, b) => getDistance(me.x, me.y, a.x, a.y) - getDistance(me.x, me.y, b.x, b.y)).first() - })() - : checkExits[0]; - Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t4) + " to pick exit", currExit); - let t5 = getTickCount(); - let dest = this.getNearestWalkable(currExit.x, currExit.y, 5, 1); - Config.DebugMode.Path && console.log("Took: " + (getTickCount() - t5) + " to find nearest walkable"); - - if (!dest) return false; - - for (let retry = 0; retry < 3; retry++) { - if (this.moveTo(dest[0], dest[1], 3, clearPath)) { - break; - } - - delay(200); - console.log("ÿc7(moveToExit) :: ÿc0Retry: " + (retry + 1)); - Misc.poll(() => me.gameReady, 1000, 200); - } - - /** - * i < areas.length - 1 is for crossing multiple areas. - * In that case we must use the exit before the last area. - */ - if (use || i < areas.length - 1) { - switch (currExit.type) { - case 1: // walk through - let targetRoom = this.getNearestRoom(areas[i]); - - if (targetRoom) { - this.moveTo(targetRoom[0], targetRoom[1]); - } else { - // might need adjustments - return false; - } - - break; - case 2: // stairs - if (!this.openExit(areas[i]) && !this.useUnit(sdk.unittype.Stairs, currExit.tileid, areas[i])) { - return false; - } - - break; - } - } - } else { - // journey there? - console.warn("Something broke"); - } - } - - console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(finalDest) + " ÿc7myArea: ÿc0" + getAreaName(me.area), "moveToExit"); - delay(300); - - return (use && finalDest ? me.area === finalDest : true); - }, - - /** + 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); + }, + + /** * @param {number} area * @param {number} exit * @returns {number} */ - getDistanceToExit: function (area, exit) { - area === undefined && (area = me.area); - exit === undefined && (exit = me.area + 1); - let areaToCheck = Misc.poll(() => getArea(area)); - if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); - let exits = areaToCheck.exits; - if (!exits.length) throw new Error("Failed to find exits"); - let loc = exits.find(a => a.target === exit); - console.debug(area, exit, loc); - return loc ? [loc.x, loc.y].distance : Infinity; - }, - - /** + getDistanceToExit: function (area, exit) { + area === undefined && (area = me.area); + exit === undefined && (exit = me.area + 1); + let areaToCheck = Misc.poll(() => getArea(area)); + if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); + let exits = areaToCheck.exits; + if (!exits.length) throw new Error("Failed to find exits"); + let loc = exits.find(a => a.target === exit); + console.debug(area, exit, loc); + return loc ? [loc.x, loc.y].distance : Infinity; + }, + + /** * @param {number} area * @param {number} exit * @returns {PathNode | false} */ - getExitCoords: function (area, exit) { - area === undefined && (area = me.area); - exit === undefined && (exit = me.area + 1); - let areaToCheck = Misc.poll(() => getArea(area)); - if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); - let exits = areaToCheck.exits; - if (!exits.length) throw new Error("Failed to find exits"); - let loc = exits.find(a => a.target === exit); - console.debug(area, exit, loc); - return loc ? { x: loc.x, y: loc.y } : false; - }, - - - /** + getExitCoords: function (area, exit) { + area === undefined && (area = me.area); + exit === undefined && (exit = me.area + 1); + let areaToCheck = Misc.poll(() => getArea(area)); + if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); + let exits = areaToCheck.exits; + if (!exits.length) throw new Error("Failed to find exits"); + let loc = exits.find(a => a.target === exit); + console.debug(area, exit, loc); + return loc ? { x: loc.x, y: loc.y } : false; + }, + + + /** * @param {number} area - the id of area to search for the room nearest to the player character * @returns {[number, number] | false} */ - getNearestRoom: function (area) { - let startTick = getTickCount(); - let x, y, minDist = 10000; + getNearestRoom: function (area) { + let x, y, minDist = 10000; - let room = Misc.poll(() => getRoom(area), 1000, 200); - if (!room) return false; + let room = Misc.poll(() => getRoom(area), 1000, 200); + if (!room) return false; - do { - let dist = getDistance(me, room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2); + do { + let dist = getDistance(me, room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2); - if (dist < minDist) { - x = room.x * 5 + room.xsize / 2; - y = room.y * 5 + room.ysize / 2; - minDist = dist; - } - } while (room.getNext()); + if (dist < minDist) { + x = room.x * 5 + room.xsize / 2; + y = room.y * 5 + room.ysize / 2; + minDist = dist; + } + } while (room.getNext()); - room = getRoom(area, x, y); - !!Config.DebugMode.Path && console.log(room); + room = getRoom(area, x, y); + !!Config.DebugMode.Path && console.log(room); - if (room) { - CollMap.addRoom(room); + if (room) { + CollMap.addRoom(room); - !!Config.DebugMode.Path && console.log("ÿc7End ÿc8(getNearestOld) ÿc0 - ÿc7Duration: ÿc0" + (getTickCount() - startTick)); - return this.getNearestWalkable(x, y, 20, 4); - } + return this.getNearestWalkable(x, y, 20, 4); + } - !!Config.DebugMode.Path && console.log("ÿc7End ÿc8(getNearestOld) ÿc0 - ÿc7Duration: ÿc0" + (getTickCount() - startTick)); - return [x, y]; - }, + return [x, y]; + }, - /** + /** * @param {number} targetArea - area id of where the unit leads to * @returns {boolean} */ - openExit: function (targetArea) { - switch (true) { - case targetArea === sdk.areas.AncientTunnels: - case targetArea === sdk.areas.A2SewersLvl1 && !(me.inArea(sdk.areas.LutGholein) && [5218, 5180].distance < 20): - return this.useUnit(sdk.unittype.Object, sdk.objects.TrapDoorA2, targetArea); - case targetArea === sdk.areas.A3SewersLvl2: - return this.useUnit(sdk.unittype.Object, sdk.objects.SewerStairsA3, targetArea); - case targetArea === sdk.areas.RuinedTemple: - case targetArea === sdk.areas.DisusedFane: - case targetArea === sdk.areas.ForgottenReliquary: - case targetArea === sdk.areas.ForgottenTemple: - case targetArea === sdk.areas.RuinedFane: - case targetArea === sdk.areas.DisusedReliquary: - return this.useUnit(sdk.unittype.Object, "stair", targetArea); - case targetArea === sdk.areas.DuranceOfHateLvl1 && me.inArea(sdk.areas.Travincal): - return this.useUnit(sdk.unittype.Object, sdk.objects.DuranceEntryStairs, targetArea); - case targetArea === sdk.areas.WorldstoneLvl1 && me.inArea(sdk.areas.ArreatSummit): - return this.useUnit(sdk.unittype.Object, sdk.objects.AncientsDoor, targetArea); - } - - return false; - }, - - /** + openExit: function (targetArea) { + switch (true) { + case targetArea === sdk.areas.AncientTunnels: + case targetArea === sdk.areas.A2SewersLvl1 && !(me.inArea(sdk.areas.LutGholein) && [5218, 5180].distance < 20): + return this.useUnit(sdk.unittype.Object, sdk.objects.TrapDoorA2, targetArea); + case targetArea === sdk.areas.A3SewersLvl2: + return this.useUnit(sdk.unittype.Object, sdk.objects.SewerStairsA3, targetArea); + case targetArea === sdk.areas.RuinedTemple: + case targetArea === sdk.areas.DisusedFane: + case targetArea === sdk.areas.ForgottenReliquary: + case targetArea === sdk.areas.ForgottenTemple: + case targetArea === sdk.areas.RuinedFane: + case targetArea === sdk.areas.DisusedReliquary: + return this.useUnit(sdk.unittype.Object, "stair", targetArea); + case targetArea === sdk.areas.DuranceOfHateLvl1 && me.inArea(sdk.areas.Travincal): + return this.useUnit(sdk.unittype.Object, sdk.objects.DuranceEntryStairs, targetArea); + case targetArea === sdk.areas.WorldstoneLvl1 && me.inArea(sdk.areas.ArreatSummit): + return this.useUnit(sdk.unittype.Object, sdk.objects.AncientsDoor, targetArea); + } + + return false; + }, + + /** * @param {UnitType} type - type of the unit to open * @param {number} id - id of the unit to open * @returns {boolean} */ - openUnit: function (type, id) { - let unit = Misc.poll(() => getUnit(type, id), 1000, 200); - if (!unit) throw new Error("openUnit: Unit not found. ID: " + unit); - if (unit.mode !== sdk.objects.mode.Inactive) return true; + openUnit: function (type, id) { + let unit = Misc.poll(() => getUnit(type, id), 1000, 200); + if (!unit) throw new Error("openUnit: Unit not found. ID: " + unit); + if (unit.mode !== sdk.objects.mode.Inactive) return true; - for (let i = 0; i < 3; i += 1) { - unit.distance > 5 && this.moveToUnit(unit); + for (let i = 0; i < 3; i += 1) { + unit.distance > 5 && this.moveToUnit(unit); - delay(300); - Packet.entityInteract(unit); + delay(300); + Packet.entityInteract(unit); - if (Misc.poll(() => unit.mode !== sdk.objects.mode.Inactive, 2000, 60)) { - delay(100); + if (Misc.poll(() => unit.mode !== sdk.objects.mode.Inactive, 2000, 60)) { + delay(100); - return true; - } + return true; + } - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); - !!coord && this.moveTo(coord.x, coord.y); - } + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); + !!coord && this.moveTo(coord.x, coord.y); + } - return false; - }, + return false; + }, - /** + /** * @param {UnitType} type - type of the unit to use * @param {number} id - id of the unit to use * @param {number} targetArea - area id of where the unit leads to * @returns {boolean} * @todo should use an object as param, or be changed to able to take an already found unit as a param */ - useUnit: function (type, id, targetArea) { - let unit = Misc.poll(() => getUnit(type, id), 2000, 200); - let preArea = me.area; - - if (!unit) { - throw new Error( - "useUnit: Unit not found. TYPE: " + type + " ID: " + id + " MyArea: " + getAreaName(me.area) + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "") - ); - } - - MainLoop: - for (let i = 0; i < 5; i += 1) { - let usetk = (i < 2 && Skill.useTK(unit)); - - if (unit.distance > 5) { - usetk ? this.moveNearUnit(unit, 20) : this.moveToUnit(unit); - // try to activate it once - usetk && i === 0 && unit.mode === sdk.objects.mode.Inactive && unit.distance < 21 && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); - } - - if (type === sdk.unittype.Object && unit.mode === sdk.objects.mode.Inactive) { - if ((me.inArea(sdk.areas.Travincal) && targetArea === sdk.areas.DuranceofHateLvl1 && me.getQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) !== 1) - || (me.inArea(sdk.areas.ArreatSummit) && targetArea === sdk.areas.WorldstoneLvl1 && me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed) !== 1)) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); - } - - me.inArea(sdk.areas.A3SewersLvl1) ? this.openUnit(sdk.unittype.Object, sdk.objects.SewerLever) : this.openUnit(sdk.unittype.Object, id); - } - - if (type === sdk.unittype.Object && id === sdk.objects.RedPortalToAct4 && me.inArea(sdk.areas.DuranceofHateLvl3) - && targetArea === sdk.areas.PandemoniumFortress && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); - } - - delay(300); - type === sdk.unittype.Stairs - ? Misc.click(0, 0, unit) - : usetk && unit.distance > 5 ? Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit) : Packet.entityInteract(unit); - delay(300); - - let tick = getTickCount(); - - while (getTickCount() - tick < 3000) { - if ((!targetArea && me.area !== preArea) || me.area === targetArea) { - delay(200); - - break MainLoop; - } - - delay(10); - } - - i > 2 && Packet.flash(me.gid); - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); - !!coord && this.moveTo(coord.x, coord.y); - } - - while (!me.idle && !me.gameReady) { - delay(40); - } - - return targetArea ? me.area === targetArea : me.area !== preArea; - }, - - /* - Pather.useUnitEx(givenSettings = {}); - optional - use either or - givenSettings.unit - defined unit thats been found - or - givenSettings.type - type of the unit to use - givenSettings.id - id of the unit to use - targetArea - area id of where the unit leads to - */ - useUnitEx: function (givenSettings = {}, targetArea = undefined) { - let settings = Object.assign({}, { - unit: undefined, - type: undefined, - id: undefined, - }, givenSettings); - let unit = settings.unit ? settings.unit : (settings.type || settings.id) ? Misc.poll(() => getUnit(settings.type, settings.id), 2000, 200) : null; - - if (!unit) { - throw new Error( - "useUnit: Unit not found. TYPE: " + (settings.type ? settings.type : "") - + " ID: " + (settings.id ? settings.id : "") - + " MyArea: " + getAreaName(me.area) + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "") - ); - } - - let preArea = me.area; - let targetAreaCheck = (unit.objtype || 0); - targetArea === undefined && targetAreaCheck > 0 && (targetArea = targetAreaCheck); - let type = unit.type; - let id = unit.classid; - - for (let i = 0; i < 5; i += 1) { - let usetk = (i < 2 && Skill.useTK(unit)); + useUnit: function (type, id, targetArea) { + let unit = Misc.poll(() => getUnit(type, id), 2000, 200); + let preArea = me.area; + + if (!unit) { + throw new Error( + "useUnit: Unit not found. TYPE: " + type + " ID: " + id + + " MyArea: " + getAreaName(me.area) + + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "") + ); + } + + MainLoop: + for (let i = 0; i < 5; i += 1) { + let usetk = (i < 2 && Skill.useTK(unit)); - if (unit.distance > 5) { - usetk ? this.moveNearUnit(unit, 20) : this.moveToUnit(unit); - // try to activate it once - usetk && i === 0 && unit.mode === sdk.objects.mode.Inactive && unit.distance < 21 && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); - } - - if (type === sdk.unittype.Object && unit.mode === sdk.objects.mode.Inactive) { - if ((me.inArea(sdk.areas.Travincal) && targetArea === sdk.areas.DuranceofHateLvl1 && me.getQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) !== 1) - || (me.inArea(sdk.areas.ArreatSummit) && targetArea === sdk.areas.WorldstoneLvl1 && me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed) !== 1)) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); - } - - me.inArea(sdk.areas.A3SewersLvl1) ? this.openUnit(sdk.unittype.Object, sdk.objects.SewerLever) : this.openUnit(sdk.unittype.Object, id); - } - - if (type === sdk.unittype.Object && id === sdk.objects.RedPortalToAct4 && me.inArea(sdk.areas.DuranceofHateLvl3) - && targetArea === sdk.areas.PandemoniumFortress && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { - throw new Error("useUnit: Incomplete quest." + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "")); - } - - delay(300); - type === 5 ? Misc.click(0, 0, unit) : usetk && unit.distance > 5 ? Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit) : Packet.entityInteract(unit); - delay(300); - - let tick = getTickCount(); - - while (getTickCount() - tick < 3000) { - if ((!targetArea && me.area !== preArea) || me.area === targetArea) { - delay(200); - - return true; - } - - delay(10); - } - - i > 2 && Packet.flash(me.gid); - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); - !!coord && this.moveTo(coord.x, coord.y); - } - - return targetArea ? me.area === targetArea : me.area !== preArea; - }, - - allowBroadcast: true, - /** + if (unit.distance > 5) { + usetk ? this.moveNearUnit(unit, 20) : this.moveToUnit(unit); + // try to activate it once + if (usetk && i === 0 && unit.mode === sdk.objects.mode.Inactive && unit.distance < 21) { + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + } + } + + if (type === sdk.unittype.Object && unit.mode === sdk.objects.mode.Inactive) { + if (me.inArea(sdk.areas.Travincal) && targetArea === sdk.areas.DuranceofHateLvl1) { + if (me.getQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) !== 1) { + throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); + } + } else if (me.inArea(sdk.areas.ArreatSummit) && targetArea === sdk.areas.WorldstoneLvl1) { + if (me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed) !== 1) { + throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); + } + } + + me.inArea(sdk.areas.A3SewersLvl1) + ? this.openUnit(sdk.unittype.Object, sdk.objects.SewerLever) + : this.openUnit(sdk.unittype.Object, id); + } + + if (type === sdk.unittype.Object && id === sdk.objects.RedPortalToAct4 && me.inArea(sdk.areas.DuranceofHateLvl3) + && targetArea === sdk.areas.PandemoniumFortress + && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { + throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); + } + + delay(300); + type === sdk.unittype.Stairs + ? Misc.click(0, 0, unit) + : usetk && unit.distance > 5 + ? Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit) + : Packet.entityInteract(unit); + delay(300); + + let tick = getTickCount(); + + while (getTickCount() - tick < 3000) { + if ((!targetArea && me.area !== preArea) || me.area === targetArea) { + delay(200); + + break MainLoop; + } + + delay(10); + } + + i > 2 && Packet.flash(me.gid); + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); + !!coord && this.moveTo(coord.x, coord.y); + } + + while (!me.idle && !me.gameReady) { + delay(40); + } + + return targetArea ? me.area === targetArea : me.area !== preArea; + }, + + allowBroadcast: true, + /** * Meant for use as a MfLeader to let MfHelpers know where to go next * @param {number} targetArea - area id */ - broadcastIntent: function broadcastIntent(targetArea) { - if (Config.MFLeader) { - let targetAct = sdk.areas.actOf(targetArea); - me.act !== targetAct && say("goto A" + targetAct); - } - }, - - /** + broadcastIntent: function broadcastIntent(targetArea) { + if (Config.MFLeader && Pather.allowBroadcast) { + let targetAct = sdk.areas.actOf(targetArea); + me.act !== targetAct && say("goto A" + targetAct); + } + }, + + /** * @param {number} targetArea - id of the area to enter * @param {boolean} check - force the waypoint menu * @returns {boolean} */ - useWaypoint: function useWaypoint(targetArea, check = 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.indexOf(targetArea) < 0) throw new Error("useWaypoint: Invalid area"); - - break; - } - - Pather.allowBroadcast && Pather.broadcastIntent(targetArea); - console.info(true, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area)); - 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"); - } - } - } - } - - /** + useWaypoint: function useWaypoint(targetArea, check = 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.indexOf(targetArea) < 0) throw new Error("useWaypoint: Invalid area"); + + break; + } + + console.time("useWaypoint"); + const destName = targetArea ? getAreaName(targetArea) : targetArea; + Pather.broadcastIntent(targetArea); + console.info(true, "ÿc7targetArea: ÿc0" + destName + " ÿc7myArea: ÿc0" + getAreaName(me.area)); + + MainLoop: + 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"); + } + } + } + } + + /** * @todo If we start in a3 and want to go to a2, use Meshif * somehow need to take into account reason for wanting to change act though, e.g. If we were * going to a2 to revive a merc then running to the a3 waypoint takes us close to our goal. @@ -1417,331 +1385,336 @@ const Pather = { * if we are all the way at Alkor then it wouldn't make sense */ - !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); - 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)) { - // Waypoint screen is open - if (getUIFlag(sdk.uiflags.Waypoint)) { - delay(500); - !Pather.initialized && (Pather.initialized = true); - - switch (targetArea) { - case "random": - while (true) { - targetArea = this.nonTownWpAreas[rand(0, this.nonTownWpAreas.length - 1)]; - - // get a valid wp, avoid towns - if (getWaypoint(this.wpAreas.indexOf(targetArea))) { - break; - } - - delay(5); - } - - break; - case null: - me.cancel(); - - return true; - } - - if (!getWaypoint(this.wpAreas.indexOf(targetArea))) { - me.cancel(); - console.log("Trying to get the waypoint: " + getAreaName(targetArea)); - me.overhead("Trying to get the waypoint"); - - if (Pather.getWP(targetArea)) { - return true; - } - - throw new Error("Pather.useWaypoint: Failed to go to waypoint " + getAreaName(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); - Packet.flash(me.gid, pingDelay); - - continue; - } - } - - if (!check || getUIFlag(sdk.uiflags.Waypoint)) { - delay(200); - wp.interact(targetArea); - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 2)) { - if (me.area === targetArea) { - delay((1500 + (pingDelay * i))); - console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - - return true; - } - - delay(20); - } - - while (!me.gameReady) { - delay(1000); - } - - // In case lag causes the wp menu to stay open - Misc.poll(() => me.gameReady, 2000, 100) && getUIFlag(sdk.uiflags.Waypoint) && me.cancelUIFlags(); - } - - 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(); - delay(200); - } - - if (me.area === targetArea) { - // delay to allow act to init - helps with crashes - delay(500); - console.info(false, "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area) + "ÿc0 - ÿc7Duration: ÿc0" + (Time.format(getTickCount() - wpTick))); - return true; - } - - throw new Error("useWaypoint: Failed to use waypoint"); - }, - - /** + if (!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); + 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)) { + // Waypoint screen is open + if (getUIFlag(sdk.uiflags.Waypoint)) { + delay(500); + !Pather.initialized && (Pather.initialized = true); + + switch (targetArea) { + case "random": + let validWps = this.nonTownWpAreas + .filter(area => getWaypoint(this.wpAreas.indexOf(area))); + if (!validWps.length) { + if (me.inTown && Pather.moveToExit(me.area + 1, true)) { + break; + } + throw new Error("Pather.useWaypoint: Failed to go to waypoint " + targetArea); + } + targetArea = validWps.random(); + + break; + case null: + me.cancel(); + + return true; + } + + if (!getWaypoint(this.wpAreas.indexOf(targetArea))) { + me.cancel(); + console.log("Trying to get the waypoint: " + getAreaName(targetArea)); + me.overhead("Trying to get the waypoint"); + if (this.getWP(targetArea)) return true; + + throw new Error("Pather.useWaypoint: Failed to go to waypoint " + getAreaName(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); + Packet.flash(me.gid, pingDelay); + + continue; + } + } + + if (!check || getUIFlag(sdk.uiflags.Waypoint)) { + delay(200); + wp.interact(targetArea); + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 2)) { + if (me.area === targetArea) { + delay(1500); + + break MainLoop; + } + + delay(20); + } + + while (!me.gameReady) { + delay(1000); + } + + // In case lag causes the wp menu to stay open + Misc.poll(() => me.gameReady, 2000, 100) && getUIFlag(sdk.uiflags.Waypoint) && me.cancelUIFlags(); + } + + 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(); + delay(200); + } + + if (me.area === targetArea) { + // delay to allow act to init - helps with crashes + delay(500); + console.info( + false, + "ÿc7targetArea: ÿc0" + getAreaName(targetArea) + " ÿc7myArea: ÿc0" + getAreaName(me.area), + "useWaypoint" + ); + return true; + } + + throw new Error("useWaypoint: Failed to use waypoint"); + }, + + /** * @param {boolean} use - use the portal that was made * @returns {Unit | boolean} */ - makePortal: function (use = false) { - if (me.inTown) return true; + makePortal: function (use = false) { + if (me.inTown) return true; - let oldGid; + let oldGid; - for (let i = 0; i < 5; i += 1) { - if (me.dead) return false; + for (let i = 0; i < 5; i += 1) { + if (me.dead) return false; - const tpTool = me.getTpTool(); - const pingDelay = i === 0 ? 100 : me.gameReady ? (me.ping + 25) : 350; - if (!tpTool) return false; + const tpTool = me.getTpTool(); + const pingDelay = i === 0 ? 100 : me.gameReady ? (me.ping + 25) : 350; + if (!tpTool) return false; - let oldPortal = getUnits(sdk.unittype.Object, "portal") - .filter((p) => p.getParent() === me.name) - .first(); + let oldPortal = getUnits(sdk.unittype.Object, "portal") + .filter((p) => p.getParent() === me.name) + .first(); - !!oldPortal && (oldGid = oldPortal.gid); + !!oldPortal && (oldGid = oldPortal.gid); - if (tpTool.use() || Game.getObject("portal")) { - let tick = getTickCount(); - - while (getTickCount() - tick < Math.max(500 + i * 100, pingDelay * 2 + 100)) { - let portal = getUnits(sdk.unittype.Object, "portal") - .filter((p) => p.getParent() === me.name && p.gid !== oldGid) - .first(); - - if (portal) { - // getUnits returns a copied version, get the actual portal so we don't copy a copy - const realPortal = Game.getObject(-1, -1, portal.gid); - if (realPortal) { - if (use) { - if (this.usePortal(null, null, copyUnit(realPortal))) { - return true; - } - break; // don't spam usePortal - } else { - return copyUnit(realPortal); - } - } - } - - delay(10); - } - } else { - console.log("Failed to use tp tool"); - Packet.flash(me.gid, pingDelay); - delay(200); - } - - delay(40); - } - - return false; - }, - - /** + if (tpTool.use() || Game.getObject("portal")) { + let tick = getTickCount(); + + while (getTickCount() - tick < Math.max(500 + i * 100, pingDelay * 2 + 100)) { + let portal = getUnits(sdk.unittype.Object, "portal") + .filter((p) => p.getParent() === me.name && p.gid !== oldGid) + .first(); + + if (portal) { + // getUnits returns a copied version, get the actual portal so we don't copy a copy + const realPortal = Game.getObject(-1, -1, portal.gid); + if (realPortal) { + if (use) { + if (this.usePortal(null, null, copyUnit(realPortal))) { + return true; + } + break; // don't spam usePortal + } else { + return copyUnit(realPortal); + } + } + } + + delay(10); + } + } else { + console.log("Failed to use tp tool"); + Packet.flash(me.gid, pingDelay); + delay(200); + } + + delay(40); + } + + return false; + }, + + /** * @param {number} [targetArea] - id of the area the portal leads to * @param {string} [owner] - name of the portal's owner * @param {ObjectUnit} [unit] - use existing portal unit * @returns {boolean} */ - usePortal: function (targetArea, owner, unit) { - if (targetArea && me.area === targetArea) return true; + usePortal: function (targetArea, owner, unit) { + if (targetArea && me.area === targetArea) return true; - me.cancelUIFlags(); + me.cancelUIFlags(); - let preArea = me.area; + let preArea = me.area; - for (let i = 0; i < 10; i += 1) { - if (me.dead) return false; - i > 0 && me.inTown && Town.move("portalspot"); + for (let i = 0; i < 10; i += 1) { + if (me.dead) return false; + i > 0 && me.inTown && Town.move("portalspot"); - let portal = unit ? copyUnit(unit) : this.getPortal(targetArea, owner); + let portal = unit ? copyUnit(unit) : this.getPortal(targetArea, owner); - if (portal) { - if (portal.objtype === sdk.areas.DuranceofHateLvl3 && portal.getParent() !== me.name + if (portal) { + if (portal.objtype === sdk.areas.DuranceofHateLvl3 && portal.getParent() !== me.name && !Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { - throw new Error("Cannot access meph through someone elses portal without first completing Travincal"); - } - let redPortal = portal.classid === sdk.objects.RedPortal; - - if (portal.area === me.area) { - if (Skill.useTK(portal) && i < 3) { - portal.distance > 21 && (me.inArea(sdk.areas.Harrogath) ? Town.move("portalspot") : Pather.moveNearUnit(portal, 20)); - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal)) { - if (Misc.poll(() => { - if (me.area !== preArea) { - Pather.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - - return false; - }, 500, 50)) { - return true; - } - } - } else { - portal.distance > 5 && this.moveToUnit(portal); - - if (getTickCount() - this.lastPortalTick > 2500) { - i < 2 ? Packet.entityInteract(portal) : Misc.click(0, 0, portal); - !!redPortal && delay(150); - } else { - let timeTillNextPortal = Math.max(3, Math.round(2500 - (getTickCount() - this.lastPortalTick))); - delay(timeTillNextPortal); + throw new Error("Cannot access meph through someone elses portal without first completing Travincal"); + } + let redPortal = portal.classid === sdk.objects.RedPortal; + + if (portal.area === me.area) { + if (Skill.useTK(portal) && i < 3) { + if (portal.distance > 21) { + me.inArea(sdk.areas.Harrogath) + ? Town.move("portalspot") + : Pather.moveNearUnit(portal, 20); + } + if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal)) { + if (Misc.poll(() => { + if (me.area !== preArea) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + + return false; + }, 500, 50)) { + return true; + } + } + } else { + portal.distance > 5 && this.moveToUnit(portal); + + if (getTickCount() - this.lastPortalTick > 2500) { + i < 2 ? Packet.entityInteract(portal) : Misc.click(0, 0, portal); + !!redPortal && delay(150); + } else { + let timeTillNextPortal = Math.max(3, Math.round(2500 - (getTickCount() - this.lastPortalTick))); + delay(timeTillNextPortal); - continue; - } - } - } + continue; + } + } + } - // Portal to/from Arcane - if (portal.classid === sdk.objects.ArcaneSanctuaryPortal && portal.mode !== sdk.objects.mode.Active) { - Misc.click(0, 0, portal); - let tick = getTickCount(); + // Portal to/from Arcane + if (portal.classid === sdk.objects.ArcaneSanctuaryPortal && portal.mode !== sdk.objects.mode.Active) { + Misc.click(0, 0, portal); + let tick = getTickCount(); - while (getTickCount() - tick < 2000) { - if (portal.mode === sdk.objects.mode.Active || me.inArea(sdk.areas.ArcaneSanctuary)) { - break; - } + while (getTickCount() - tick < 2000) { + if (portal.mode === sdk.objects.mode.Active || me.inArea(sdk.areas.ArcaneSanctuary)) { + break; + } - delay(10); - } - } + delay(10); + } + } - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 500) { - if (me.area !== preArea) { - Pather.lastPortalTick = getTickCount(); - delay(100); + while (getTickCount() - tick < 500) { + if (me.area !== preArea) { + Pather.lastPortalTick = getTickCount(); + delay(100); - return true; - } + return true; + } - delay(10); - } + delay(10); + } - i > 1 && Packet.flash(me.gid); - } else { - Packet.flash(me.gid); - } + i > 1 && Packet.flash(me.gid); + } else { + Packet.flash(me.gid); + } - delay(250); - } + delay(250); + } - return (targetArea ? me.area === targetArea : me.area !== preArea); - }, + return (targetArea ? me.area === targetArea : me.area !== preArea); + }, - /** + /** * @param {number} targetArea - id of the area the portal leads to * @param {string} owner - name of the portal's owner * @returns {ObjectUnit | false} */ - getPortal: function (targetArea, owner) { - let portal = Game.getObject("portal"); - - if (portal) { - do { - if (typeof targetArea !== "number" || portal.objtype === targetArea) { - switch (owner) { - case undefined: // Pather.usePortal(area) - red portal - if (!portal.getParent() || portal.getParent() === me.name) { - return copyUnit(portal); - } - - break; - case null: // Pather.usePortal(area, null) - any blue portal leading to area - if (portal.getParent() === me.name || Misc.inMyParty(portal.getParent())) { - return copyUnit(portal); - } - - break; - default: // Pather.usePortal(null, owner) - any blue portal belonging to owner OR Pather.usePortal(area, owner) - blue portal matching area and owner - if (portal.getParent() === owner && (owner === me.name || Misc.inMyParty(owner))) { - return copyUnit(portal); - } - - break; - } - } - } while (portal.getNext()); - } - - return false; - }, - - /** + getPortal: function (targetArea, owner) { + let portal = Game.getObject("portal"); + + if (portal) { + do { + if (typeof targetArea !== "number" || portal.objtype === targetArea) { + switch (owner) { + case undefined: // Pather.usePortal(area) - red portal + if (!portal.getParent() || portal.getParent() === me.name) { + return copyUnit(portal); + } + + break; + case null: // Pather.usePortal(area, null) - any blue portal leading to area + if (portal.getParent() === me.name || Misc.inMyParty(portal.getParent())) { + return copyUnit(portal); + } + + break; + default: // Pather.usePortal(null, owner) - any blue portal belonging to owner OR Pather.usePortal(area, owner) - blue portal matching area and owner + if (portal.getParent() === owner && (owner === me.name || Misc.inMyParty(owner))) { + return copyUnit(portal); + } + + break; + } + } + } while (portal.getNext()); + } + + return false; + }, + + /** * @param {number} x - the starting x coord * @param {number} y - the starting y coord * @param {number} range - maximum allowed range from the starting coords @@ -1750,42 +1723,42 @@ const Pather = { * @param {number} size * @returns {[number, number] | false} */ - getNearestWalkable: function (x, y, range, step, coll, size) { - !step && (step = 1); - coll === undefined && (coll = sdk.collision.BlockWall); - - let distance = 1; - let result = false; - - // Check if the original spot is valid - if (this.checkSpot(x, y, coll, false, size)) { - result = [x, y]; - } - - MainLoop: - while (!result && distance < range) { - for (let i = -distance; i <= distance; i += 1) { - for (let j = -distance; j <= distance; j += 1) { - // Check outer layer only (skip previously checked) - if (Math.abs(i) >= Math.abs(distance) || Math.abs(j) >= Math.abs(distance)) { - if (this.checkSpot(x + i, y + j, coll, false, size)) { - result = [x + i, y + j]; - - break MainLoop; - } - } - } - } - - distance += step; - } - - CollMap.reset(); - - return result; - }, - - /** + getNearestWalkable: function (x, y, range, step, coll, size) { + !step && (step = 1); + coll === undefined && (coll = sdk.collision.BlockWall); + + let distance = 1; + let result = false; + + // Check if the original spot is valid + if (this.checkSpot(x, y, coll, false, size)) { + result = [x, y]; + } + + MainLoop: + while (!result && distance < range) { + for (let i = -distance; i <= distance; i += 1) { + for (let j = -distance; j <= distance; j += 1) { + // Check outer layer only (skip previously checked) + if (Math.abs(i) >= Math.abs(distance) || Math.abs(j) >= Math.abs(distance)) { + if (this.checkSpot(x + i, y + j, coll, false, size)) { + result = [x + i, y + j]; + + break MainLoop; + } + } + } + } + + distance += step; + } + + CollMap.reset(); + + return result; + }, + + /** * @param {number} x - the x coord to check * @param {number} y - the y coord to check * @param {number} coll - collision flag to search for @@ -1793,402 +1766,437 @@ const Pather = { * @param {number} size * @returns {boolean} */ - checkSpot: function (x, y, coll, cacheOnly, size) { - coll === undefined && (coll = sdk.collision.BlockWall); - !size && (size = 1); - - for (let dx = -size; dx <= size; dx += 1) { - for (let dy = -size; dy <= size; dy += 1) { - if (Math.abs(dx) !== Math.abs(dy)) { - let value = CollMap.getColl(x + dx, y + dy, cacheOnly); - - if (value & coll) { - return false; - } - } - } - } - - return true; - }, - - /** + checkSpot: function (x, y, coll, cacheOnly, size) { + coll === undefined && (coll = sdk.collision.BlockWall); + !size && (size = 1); + + for (let dx = -size; dx <= size; dx += 1) { + for (let dy = -size; dy <= size; dy += 1) { + if (Math.abs(dx) !== Math.abs(dy)) { + let value = CollMap.getColl(x + dx, y + dy, cacheOnly); + + if (value & coll) { + return false; + } + } + } + } + + return true; + }, + + /** * @param {number} act - the act number to check for access * @returns {boolean} */ - accessToAct: function (act) { - switch (act) { - // Act 1 is always accessible - case 1: - return true; - // For the other acts, check the "Able to go to Act *" quests - case 2: - return me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed) === 1; - case 3: - return me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed) === 1; - case 4: - return me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed) === 1; - case 5: - return me.expansion && me.getQuest(sdk.quest.id.AbleToGotoActV, sdk.quest.states.Completed) === 1; - default: - return false; - } - }, - - /** + accessToAct: function (act) { + switch (act) { + // Act 1 is always accessible + case 1: + return true; + // For the other acts, check the "Able to go to Act *" quests + case 2: + return me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed) === 1; + case 3: + return me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed) === 1; + case 4: + return me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed) === 1; + case 5: + return me.expansion && me.getQuest(sdk.quest.id.AbleToGotoActV, sdk.quest.states.Completed) === 1; + default: + return false; + } + }, + + /** * @param {number} area - the id of area to get the waypoint in * @param {boolean} [clearPath] * @returns {boolean} */ - getWP: function (area, clearPath) { - area !== me.area && this.journeyTo(area); - - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - - if (preset) { - Skill.haveTK ? Pather.moveNearUnit(preset, 20, clearPath) : Pather.moveToUnit(preset, 0, 0, clearPath); - - let wp = Game.getObject("waypoint"); - - if (wp) { - for (let j = 0; j < 10; j++) { - if (!getUIFlag(sdk.uiflags.Waypoint)) { - if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { - wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); - } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { - this.moveToUnit(wp) && Misc.click(0, 0, wp); - } - } - - if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { - delay(500); - !Pather.initialized && (Pather.initialized = true); - me.cancelUIFlags(); - - return true; - } - - // handle getUnit bug - if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { - Town.getDistance("waypoint") > 5 && Town.move("waypoint"); - Misc.click(0, 0, wp); - } - - delay(500); - } - } - } - } - - return false; - }, - - /** + getWP: function (area, clearPath) { + area !== me.area && this.journeyTo(area); + + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + + if (preset) { + Skill.haveTK ? Pather.moveNearUnit(preset, 20, clearPath) : Pather.moveToUnit(preset, 0, 0, clearPath); + + let wp = Game.getObject("waypoint"); + + if (wp) { + for (let j = 0; j < 10; j++) { + if (!getUIFlag(sdk.uiflags.Waypoint)) { + if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { + wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { + this.moveToUnit(wp) && Misc.click(0, 0, wp); + } + } + + if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { + delay(500); + !Pather.initialized && (Pather.initialized = true); + me.cancelUIFlags(); + + return true; + } + + // handle getUnit bug + if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { + Town.getDistance("waypoint") > 5 && Town.move("waypoint"); + Misc.click(0, 0, wp); + } + + delay(500); + } + } + } + } + + return false; + }, + + /** * @param {number} area - the id of area to move to * @returns {boolean} * @todo refactor this, it's rather messy */ - journeyTo: function (area) { - if (area === undefined) return false; - console.time("journeyTo"); - - let target, retry = 0; - - if (area !== sdk.areas.DurielsLair) { - target = this.plotCourse(area, me.area); - } else { - target = { course: [sdk.areas.CanyonofMagic, sdk.areas.DurielsLair], useWP: false }; - this.wpAreas.indexOf(me.area) === -1 && (target.useWP = true); - } - - console.info(true, "Course :: " + target.course); - area === sdk.areas.PandemoniumFortress && me.inArea(sdk.areas.DuranceofHateLvl3) && (target.useWP = false); - target.useWP && Town.goToTown(); - - // handle variable flayer jungle entrances - if (target.course.includes(sdk.areas.FlayerJungle)) { - Town.goToTown(3); // without initiated act, getArea().exits will crash - let special = getArea(sdk.areas.FlayerJungle); - - if (special) { - special = special.exits; - - for (let i = 0; i < special.length; i += 1) { - if (special[i].target === sdk.areas.GreatMarsh) { - // add great marsh if needed - target.course.splice(target.course.indexOf(sdk.areas.FlayerJungle), 0, sdk.areas.GreatMarsh); - - break; - } - } - } - } - - while (target.course.length) { - const currArea = me.area; - const targetArea = target.course[0]; - let unit; - - if (currArea === targetArea && target.course.shift()) { - continue; - } - - console.info(null, "ÿc0Moving from: " + getAreaName(currArea) + " to " + getAreaName(targetArea)); - - if (!me.inTown) { - Precast.doPrecast(false); + journeyTo: function (area) { + if (area === undefined) return false; + console.time("journeyTo"); + + let target, retry = 0; + + if (area !== sdk.areas.DurielsLair) { + target = this.plotCourse(area, me.area); + } else { + target = { course: [sdk.areas.CanyonofMagic, sdk.areas.DurielsLair], useWP: false }; + this.wpAreas.indexOf(me.area) === -1 && (target.useWP = true); + } + + console.info(true, "Course :: " + target.course); + area === sdk.areas.PandemoniumFortress && me.inArea(sdk.areas.DuranceofHateLvl3) && (target.useWP = false); + target.useWP && Town.goToTown(); + + // handle variable flayer jungle entrances + if (target.course.includes(sdk.areas.FlayerJungle)) { + Town.goToTown(3); // without initiated act, getArea().exits will crash + let special = getArea(sdk.areas.FlayerJungle); + + if (special) { + special = special.exits; + + for (let i = 0; i < special.length; i += 1) { + if (special[i].target === sdk.areas.GreatMarsh) { + // add great marsh if needed + target.course.splice(target.course.indexOf(sdk.areas.FlayerJungle), 0, sdk.areas.GreatMarsh); + + break; + } + } + } + } + + while (target.course.length) { + const currArea = me.area; + const targetArea = target.course[0]; + let unit; + + if (currArea === targetArea && target.course.shift()) { + continue; + } + + console.info(null, "ÿc0Moving from: " + getAreaName(currArea) + " to " + getAreaName(targetArea)); + + if (!me.inTown) { + Precast.doPrecast(false); - if (this.wpAreas.includes(currArea) && !getWaypoint(this.wpAreas.indexOf(currArea))) { - this.getWP(currArea); - } - } - - if (me.inTown && this.nextAreas[currArea] !== targetArea && this.wpAreas.includes(targetArea) && getWaypoint(this.wpAreas.indexOf(targetArea))) { - this.useWaypoint(targetArea, !this.plotCourse_openedWpMenu); - Precast.doPrecast(false); - } else if (currArea === sdk.areas.StonyField && targetArea === sdk.areas.Tristram) { - // Stony Field -> Tristram - this.moveToPreset(currArea, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true); - Misc.poll(() => this.usePortal(sdk.areas.Tristram), 5000, 1000); - } else if (currArea === sdk.areas.LutGholein && targetArea === sdk.areas.A2SewersLvl1) { - // Lut Gholein -> Sewers Level 1 (use Trapdoor) - this.moveToPreset(currArea, sdk.unittype.Stairs, sdk.exits.preset.A2SewersTrapDoor); - this.useUnit(sdk.unittype.Object, sdk.objects.TrapDoorA2, sdk.areas.A2SewersLvl1); - } else if (currArea === sdk.areas.A2SewersLvl2 && targetArea === sdk.areas.A2SewersLvl1) { - // Sewers Level 2 -> Sewers Level 1 - Pather.moveToExit(targetArea, false); - this.useUnit(sdk.unittype.Stairs, sdk.objects.A2UndergroundUpStairs, sdk.areas.A2SewersLvl1); - } else if (currArea === sdk.areas.PalaceCellarLvl3 && targetArea === sdk.areas.ArcaneSanctuary) { - // Palace -> Arcane - this.moveTo(10073, 8670); - this.usePortal(null); - } else if (currArea === sdk.areas.ArcaneSanctuary && targetArea === sdk.areas.PalaceCellarLvl3) { - // Arcane Sanctuary -> Palace Cellar 3 - Skill.haveTK ? this.moveNearPreset(currArea, sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal, 20) : this.moveToPreset(currArea, sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal); - unit = Misc.poll(() => Game.getObject(sdk.objects.ArcaneSanctuaryPortal)); - unit && Pather.useUnit(sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal, sdk.areas.PalaceCellarLvl3); - } else if (currArea === sdk.areas.ArcaneSanctuary && targetArea === sdk.areas.CanyonofMagic) { - // Arcane Sanctuary -> Canyon of the Magic - this.moveToPreset(currArea, sdk.unittype.Object, sdk.objects.Journal); - unit = Game.getObject(sdk.objects.RedPortal); - - if (!unit || !this.usePortal(null, null, unit)) { - for (let i = 0; i < 5; i++) { - unit = Game.getObject(sdk.objects.Journal); - - // couldnt find journal? Move to it's preset - if (!unit) { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); - continue; - } else if (unit && unit.distance > 20) { - Pather.moveNearUnit(unit, 13); - } - - Packet.entityInteract(unit); - Misc.poll(() => getIsTalkingNPC(), 1000, 50); - me.cancel(); - - if (this.usePortal(sdk.areas.CanyonofMagic)) { - break; - } - } - } - } else if (currArea === sdk.areas.CanyonofMagic && targetArea === sdk.areas.DurielsLair) { - // Canyon -> Duriels Lair - this.moveToExit(getRoom().correcttomb, true); - this.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricStaffHolder); - unit = Misc.poll(() => Game.getObject(sdk.objects.PortaltoDurielsLair)); - unit && Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); - } else if (currArea === sdk.areas.Travincal && targetArea === sdk.areas.DuranceofHateLvl1) { - // Trav -> Durance Lvl 1 - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DuranceEntryStairs); - this.useUnit(sdk.unittype.Object, sdk.objects.DuranceEntryStairs, sdk.areas.DuranceofHateLvl1); - } else if (currArea === sdk.areas.DuranceofHateLvl3 && targetArea === sdk.areas.PandemoniumFortress) { - // Durance Lvl 3 -> Pandemonium Fortress - if (me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { - console.log(sdk.colors.Red + "(journeyTo) :: Incomplete Quest"); - return false; - } - - Pather.moveTo(17581, 8070); - delay(250 + me.ping * 2); - this.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); - } else if (currArea === sdk.areas.Harrogath && targetArea === sdk.areas.BloodyFoothills) { - // Harrogath -> Bloody Foothills - this.moveTo(5026, 5095); - this.openUnit(sdk.unittype.Object, sdk.objects.Act5Gate); - this.moveToExit(targetArea, true); - } else if (currArea === sdk.areas.Harrogath && targetArea === sdk.areas.NihlathaksTemple) { - // Harrogath -> Nihlathak's Temple - Town.move(NPC.Anya); - if (!Pather.getPortal(sdk.areas.NihlathaksTemple) && Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) { - Town.npcInteract("Anya"); - } - this.usePortal(sdk.areas.NihlathaksTemple); - } else if (currArea === sdk.areas.FrigidHighlands && targetArea === sdk.areas.Abaddon) { - // Abaddon - this.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.RedPortal); - this.usePortal(sdk.areas.Abaddon); - } else if (currArea === sdk.areas.ArreatPlateau && targetArea === sdk.areas.PitofAcheron) { - // Pits of Archeon - this.moveToPreset(sdk.areas.ArreatPlateau, sdk.unittype.Object, sdk.objects.RedPortal); - this.usePortal(sdk.areas.PitofAcheron); - } else if (currArea === sdk.areas.FrozenTundra && targetArea === sdk.areas.InfernalPit) { - // Infernal Pit - this.moveToPreset(sdk.areas.FrozenTundra, sdk.unittype.Object, sdk.objects.RedPortal); - this.usePortal(sdk.areas.InfernalPit); - } else if (targetArea === sdk.areas.MooMooFarm) { - // Moo Moo farm - currArea !== sdk.areas.RogueEncampment && Town.goToTown(1); - Town.move("stash") && (unit = this.getPortal(targetArea)); - unit && this.usePortal(null, null, unit); - } else if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain, sdk.areas.UberTristram].includes(targetArea)) { - // Uber Portals - currArea !== sdk.areas.Harrogath && Town.goToTown(5); - Town.move("stash") && (unit = this.getPortal(targetArea)); - unit && this.usePortal(null, null, unit); - } else { - this.moveToExit(targetArea, true); - } - - // give time for act to load, increases stabilty of changing acts - delay(500); - - if (me.area === targetArea) { - target.course.shift(); - retry = 0; - } else { - if (retry > 3) { - console.warn("Failed to journeyTo " + getAreaName(area) + " currentarea: " + getAreaName(me.area)); - return false; - } - retry++; - } - } - - console.info(false, "ÿc4MyArea: ÿc0" + getAreaName(me.area), "journeyTo"); - return me.area === area; - }, - - plotCourse_openedWpMenu: false, - - /** + if (this.wpAreas.includes(currArea) && !getWaypoint(this.wpAreas.indexOf(currArea))) { + this.getWP(currArea); + } + } + + if (me.inTown && this.nextAreas[currArea] !== targetArea + && this.wpAreas.includes(targetArea) && getWaypoint(this.wpAreas.indexOf(targetArea))) { + this.useWaypoint(targetArea, !this.plotCourse_openedWpMenu); + Precast.doPrecast(false); + } else if (currArea === sdk.areas.StonyField && targetArea === sdk.areas.Tristram) { + // Stony Field -> Tristram + this.moveToPreset(currArea, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true); + Misc.poll(() => this.usePortal(sdk.areas.Tristram), 5000, 1000); + } else if (currArea === sdk.areas.LutGholein && targetArea === sdk.areas.A2SewersLvl1) { + // Lut Gholein -> Sewers Level 1 (use Trapdoor) + this.moveToPreset(currArea, sdk.unittype.Stairs, sdk.exits.preset.A2SewersTrapDoor); + this.useUnit(sdk.unittype.Object, sdk.objects.TrapDoorA2, sdk.areas.A2SewersLvl1); + } else if (currArea === sdk.areas.A2SewersLvl2 && targetArea === sdk.areas.A2SewersLvl1) { + // Sewers Level 2 -> Sewers Level 1 + Pather.moveToExit(targetArea, false); + this.useUnit(sdk.unittype.Stairs, sdk.objects.A2UndergroundUpStairs, sdk.areas.A2SewersLvl1); + } else if (currArea === sdk.areas.PalaceCellarLvl3 && targetArea === sdk.areas.ArcaneSanctuary) { + // Palace -> Arcane + this.moveTo(10073, 8670); + this.usePortal(null); + } else if (currArea === sdk.areas.ArcaneSanctuary && targetArea === sdk.areas.PalaceCellarLvl3) { + // Arcane Sanctuary -> Palace Cellar 3 + Skill.haveTK + ? this.moveNearPreset(currArea, sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal, 20) + : this.moveToPreset(currArea, sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal); + unit = Misc.poll(() => Game.getObject(sdk.objects.ArcaneSanctuaryPortal)); + unit && Pather.useUnit(sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal, sdk.areas.PalaceCellarLvl3); + } else if (currArea === sdk.areas.ArcaneSanctuary && targetArea === sdk.areas.CanyonofMagic) { + // Arcane Sanctuary -> Canyon of the Magic + this.moveToPreset(currArea, sdk.unittype.Object, sdk.objects.Journal); + unit = Game.getObject(sdk.objects.RedPortal); + + if (!unit || !this.usePortal(null, null, unit)) { + for (let i = 0; i < 5; i++) { + unit = Game.getObject(sdk.objects.Journal); + + // couldnt find journal? Move to it's preset + if (!unit) { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); + continue; + } else if (unit && unit.distance > 20) { + Pather.moveNearUnit(unit, 13); + } + + Packet.entityInteract(unit); + Misc.poll(() => getIsTalkingNPC(), 1000, 50); + me.cancel(); + + if (this.usePortal(sdk.areas.CanyonofMagic)) { + break; + } + } + } + } else if (currArea === sdk.areas.CanyonofMagic && targetArea === sdk.areas.DurielsLair) { + // Canyon -> Duriels Lair + this.moveToExit(getRoom().correcttomb, true); + this.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricStaffHolder); + unit = Misc.poll(() => Game.getObject(sdk.objects.PortaltoDurielsLair)); + unit && Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); + } else if (currArea === sdk.areas.Travincal && targetArea === sdk.areas.DuranceofHateLvl1) { + // Trav -> Durance Lvl 1 + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DuranceEntryStairs); + this.useUnit(sdk.unittype.Object, sdk.objects.DuranceEntryStairs, sdk.areas.DuranceofHateLvl1); + } else if (currArea === sdk.areas.DuranceofHateLvl3 && targetArea === sdk.areas.PandemoniumFortress) { + // Durance Lvl 3 -> Pandemonium Fortress + if (me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { + console.log(sdk.colors.Red + "(journeyTo) :: Incomplete Quest"); + return false; + } + + Pather.moveTo(17581, 8070); + delay(250 + me.ping * 2); + this.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); + } else if (currArea === sdk.areas.Harrogath && targetArea === sdk.areas.BloodyFoothills) { + // Harrogath -> Bloody Foothills + this.moveTo(5026, 5095); + this.openUnit(sdk.unittype.Object, sdk.objects.Act5Gate); + this.moveToExit(targetArea, true); + } else if (currArea === sdk.areas.Harrogath && targetArea === sdk.areas.NihlathaksTemple) { + // Harrogath -> Nihlathak's Temple + Town.move(NPC.Anya); + if (!Pather.getPortal(sdk.areas.NihlathaksTemple) + && Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) { + Town.npcInteract("Anya"); + } + this.usePortal(sdk.areas.NihlathaksTemple); + } else if (currArea === sdk.areas.FrigidHighlands && targetArea === sdk.areas.Abaddon) { + // Abaddon + this.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.RedPortal); + this.usePortal(sdk.areas.Abaddon); + } else if (currArea === sdk.areas.ArreatPlateau && targetArea === sdk.areas.PitofAcheron) { + // Pits of Archeon + this.moveToPreset(sdk.areas.ArreatPlateau, sdk.unittype.Object, sdk.objects.RedPortal); + this.usePortal(sdk.areas.PitofAcheron); + } else if (currArea === sdk.areas.FrozenTundra && targetArea === sdk.areas.InfernalPit) { + // Infernal Pit + this.moveToPreset(sdk.areas.FrozenTundra, sdk.unittype.Object, sdk.objects.RedPortal); + this.usePortal(sdk.areas.InfernalPit); + } else if (targetArea === sdk.areas.MooMooFarm) { + // Moo Moo farm + currArea !== sdk.areas.RogueEncampment && Town.goToTown(1); + Town.move("stash") && (unit = this.getPortal(targetArea)); + unit && this.usePortal(null, null, unit); + } else if ([ + sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain, sdk.areas.UberTristram + ].includes(targetArea)) { + // Uber Portals + currArea !== sdk.areas.Harrogath && Town.goToTown(5); + Town.move("stash") && (unit = this.getPortal(targetArea)); + unit && this.usePortal(null, null, unit); + } else { + this.moveToExit(targetArea, true); + } + + // give time for act to load, increases stabilty of changing acts + delay(500); + + if (me.area === targetArea) { + target.course.shift(); + retry = 0; + } else { + if (retry > 3) { + console.warn("Failed to journeyTo " + getAreaName(area) + " currentarea: " + getAreaName(me.area)); + return false; + } + retry++; + } + } + + console.info(false, "ÿc4MyArea: ÿc0" + getAreaName(me.area), "journeyTo"); + return me.area === area; + }, + + plotCourse_openedWpMenu: false, + + /** * Plot a course to a specific area * @param {number} src - starting area id * @param {number} dest - destination area id * @returns {{ course: number[], useWP: boolean } | false} * @todo this needs more checks */ - plotCourse: function (dest, src) { - let node, prevArea; - let useWP = false; - let arr = []; - // need to redo this...that's gonna be a pain - const previousAreas = [ - sdk.areas.None, sdk.areas.None, sdk.areas.RogueEncampment, sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland, sdk.areas.CaveLvl1, sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, - sdk.areas.PitLvl1, sdk.areas.ColdPlains, sdk.areas.BurialGrounds, sdk.areas.BurialGrounds, sdk.areas.BlackMarsh, sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, - sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TamoeHighland, sdk.areas.MonasteryGate, sdk.areas.OuterCloister, sdk.areas.Barracks, sdk.areas.JailLvl1, sdk.areas.JailLvl2, - sdk.areas.JailLvl3, sdk.areas.InnerCloister, sdk.areas.Cathedral, sdk.areas.CatacombsLvl1, sdk.areas.CatacombsLvl2, sdk.areas.CatacombsLvl3, sdk.areas.StonyField, sdk.areas.RogueEncampment, - sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.ArcaneSanctuary, sdk.areas.LutGholein, - sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.LutGholein, sdk.areas.HaremLvl1, sdk.areas.HaremLvl2, sdk.areas.PalaceCellarLvl1, sdk.areas.PalaceCellarLvl2, sdk.areas.RockyWaste, - sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl1, sdk.areas.ValleyofSnakes, sdk.areas.StonyTombLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.ClawViperTempleLvl1, sdk.areas.FarOasis, - sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.LostCity, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, - sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.RogueEncampment, sdk.areas.PalaceCellarLvl3, sdk.areas.RogueEncampment, sdk.areas.KurastDocktown, sdk.areas.SpiderForest, - sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, - sdk.areas.SpiderForest, sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.SwampyPitLvl1, sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, sdk.areas.SwampyPitLvl2, sdk.areas.FlayerDungeonLvl2, - sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.KurastBazaar, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.KurastCauseway, - sdk.areas.Travincal, sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2, sdk.areas.DuranceofHateLvl3, sdk.areas.PandemoniumFortress, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, - sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, sdk.areas.PandemoniumFortress, sdk.areas.Harrogath, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, - sdk.areas.CrystalizedPassage, sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.AncientsWay, sdk.areas.Harrogath, - sdk.areas.NihlathaksTemple, sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.FrozenTundra, sdk.areas.ArreatSummit, sdk.areas.WorldstoneLvl1, - sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath - ]; - let visitedNodes = []; - let toVisitNodes = [{ from: dest, to: null }]; - - !src && (src = me.area); - - if (!this.plotCourse_openedWpMenu && me.inTown && this.nextAreas[me.area] !== dest && Pather.useWaypoint(null)) { - Pather.plotCourse_openedWpMenu = true; - } - - while (toVisitNodes.length > 0) { - node = toVisitNodes[0]; - - // If we've already visited it, just move on - if (visitedNodes[node.from] === undefined) { - visitedNodes[node.from] = node.to; - - if (this.areasConnected(node.from, node.to)) { - // If we have this wp we can start from there - if ((me.inTown // check wp in town + plotCourse: function (dest, src) { + let node, prevArea; + let useWP = false; + let arr = []; + // need to redo this...that's gonna be a pain + const previousAreas = [ + sdk.areas.None, sdk.areas.None, sdk.areas.RogueEncampment, sdk.areas.BloodMoor, sdk.areas.ColdPlains, + sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, + sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.StonyField, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland, sdk.areas.CaveLvl1, + sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, + sdk.areas.PitLvl1, sdk.areas.ColdPlains, sdk.areas.BurialGrounds, + sdk.areas.BurialGrounds, sdk.areas.BlackMarsh, sdk.areas.ForgottenTower, + sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, + sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TamoeHighland, + sdk.areas.MonasteryGate, sdk.areas.OuterCloister, sdk.areas.Barracks, + sdk.areas.JailLvl1, sdk.areas.JailLvl2, + sdk.areas.JailLvl3, sdk.areas.InnerCloister, sdk.areas.Cathedral, + sdk.areas.CatacombsLvl1, sdk.areas.CatacombsLvl2, sdk.areas.CatacombsLvl3, + sdk.areas.StonyField, sdk.areas.RogueEncampment, + sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.RockyWaste, + sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, + sdk.areas.ArcaneSanctuary, sdk.areas.LutGholein, + sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.LutGholein, + sdk.areas.HaremLvl1, sdk.areas.HaremLvl2, sdk.areas.PalaceCellarLvl1, + sdk.areas.PalaceCellarLvl2, sdk.areas.RockyWaste, + sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl1, sdk.areas.ValleyofSnakes, + sdk.areas.StonyTombLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.ClawViperTempleLvl1, sdk.areas.FarOasis, + sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.LostCity, + sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, + sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, + sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.RogueEncampment, + sdk.areas.PalaceCellarLvl3, sdk.areas.RogueEncampment, sdk.areas.KurastDocktown, sdk.areas.SpiderForest, + sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, + sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, + sdk.areas.SpiderForest, sdk.areas.SpiderForest, sdk.areas.FlayerJungle, + sdk.areas.SwampyPitLvl1, sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, + sdk.areas.SwampyPitLvl2, sdk.areas.FlayerDungeonLvl2, + sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.KurastBazaar, + sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.UpperKurast, + sdk.areas.KurastCauseway, sdk.areas.KurastCauseway, + sdk.areas.Travincal, sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2, + sdk.areas.DuranceofHateLvl3, sdk.areas.PandemoniumFortress, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, + sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, sdk.areas.PandemoniumFortress, + sdk.areas.Harrogath, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, + sdk.areas.CrystalizedPassage, sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, + sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, + sdk.areas.AncientsWay, sdk.areas.Harrogath, + sdk.areas.NihlathaksTemple, sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, + sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.FrozenTundra, + sdk.areas.ArreatSummit, sdk.areas.WorldstoneLvl1, + sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, + sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath + ]; + let visitedNodes = []; + let toVisitNodes = [{ from: dest, to: null }]; + + !src && (src = me.area); + + if (!this.plotCourse_openedWpMenu && me.inTown && this.nextAreas[me.area] !== dest && Pather.useWaypoint(null)) { + Pather.plotCourse_openedWpMenu = true; + } + + while (toVisitNodes.length > 0) { + node = toVisitNodes[0]; + + // If we've already visited it, just move on + if (visitedNodes[node.from] === undefined) { + visitedNodes[node.from] = node.to; + + if (this.areasConnected(node.from, node.to)) { + // If we have this wp we can start from there + if ((me.inTown // check wp in town || ((src !== previousAreas[dest] && dest !== previousAreas[src]) // check wp if areas aren't linked && previousAreas[src] !== previousAreas[dest])) // check wp if areas aren't linked with a common area && Pather.wpAreas.indexOf(node.from) > 0 && getWaypoint(Pather.wpAreas.indexOf(node.from)) - ) { - if (node.from !== src) { - useWP = true; - } - - src = node.from; - } - - // We found it, time to go - if (node.from === src) { - break; - } - - if ((prevArea = previousAreas[node.from]) !== 0 && visitedNodes.indexOf(prevArea) === -1) { - toVisitNodes.push({ from: prevArea, to: node.from }); - } - - for (prevArea = 1; prevArea < previousAreas.length; prevArea += 1) { - // Only interested in those connected to node - if (previousAreas[prevArea] === node.from && visitedNodes.indexOf(prevArea) === -1) { - toVisitNodes.push({ from: prevArea, to: node.from }); - } - } - } - - toVisitNodes.shift(); - } else { - useWP = true; - } - } - - arr.push(src); - - node = src; - - while (node !== dest && node !== undefined) { - arr.push(node = visitedNodes[node]); - } - - // Something failed - if (node === undefined) { - return false; - } - - return { course: arr, useWP: useWP }; - }, - - /** + ) { + if (node.from !== src) { + useWP = true; + } + + src = node.from; + } + + // We found it, time to go + if (node.from === src) { + break; + } + + if ((prevArea = previousAreas[node.from]) !== 0 && visitedNodes.indexOf(prevArea) === -1) { + toVisitNodes.push({ from: prevArea, to: node.from }); + } + + for (prevArea = 1; prevArea < previousAreas.length; prevArea += 1) { + // Only interested in those connected to node + if (previousAreas[prevArea] === node.from && visitedNodes.indexOf(prevArea) === -1) { + toVisitNodes.push({ from: prevArea, to: node.from }); + } + } + } + + toVisitNodes.shift(); + } else { + useWP = true; + } + } + + arr.push(src); + + node = src; + + while (node !== dest && node !== undefined) { + arr.push(node = visitedNodes[node]); + } + + // Something failed + if (node === undefined) { + return false; + } + + return { course: arr, useWP: useWP }; + }, + + /** * Check if two areas are connected * @param {number} src - starting area id * @param {number} dest - destination area id * @returns {boolean} * @todo this needs more checks */ - areasConnected: function (src, dest) { - if (src === sdk.areas.CanyonofMagic && dest === sdk.areas.ArcaneSanctuary) { - return false; - } + areasConnected: function (src, dest) { + if (src === sdk.areas.CanyonofMagic && dest === sdk.areas.ArcaneSanctuary) { + return false; + } - return true; - }, + return true; + }, }; Pather.nextAreas[sdk.areas.RogueEncampment] = sdk.areas.BloodMoor; diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 808613b61..6d593a9aa 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -9,663 +9,688 @@ * @namespace Pickit */ const Pickit = { - gidList: [], - invoLocked: true, - beltSize: 1, - /** @enum */ - Result: { - UNID: -1, - UNWANTED: 0, - WANTED: 1, - CUBING: 2, - RUNEWORD: 3, - TRASH: 4, - CRAFTING: 5, - UTILITY: 6 - }, - /** - * Ignored item types for item logging - */ - ignoreLog: [ - sdk.items.type.Gold, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, - 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 - ], - tkable: [ - sdk.items.type.Gold, sdk.items.type.Scroll, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, - sdk.items.type.RejuvPotion, sdk.items.type.StaminaPotion, sdk.items.type.AntidotePotion, sdk.items.type.ThawingPotion - ], - essentials: [sdk.items.type.Gold, sdk.items.type.Scroll, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion], - - /** - * @param {boolean} notify - */ - init: function (notify) { - Config.PickitFiles.forEach((file) => NTIP.OpenFile("pickit/" + file, notify)); - // check if we can pick up items, only do this is our inventory slots aren't completly locked - Pickit.invoLocked = !Config.Inventory.some(row => row.some(el => el > 0)); - - // sometime Storage isn't loaded? - if (typeof Storage !== "undefined") { - Pickit.beltSize = Storage.BeltSize(); - // If MinColumn is set to be more than our current belt size, set it to be 1 less than the belt size 4x3 belt will give us Config.MinColumn = [2, 2, 2, 2] - Config.MinColumn.forEach((el, index) => { - el >= Pickit.beltSize && (Config.MinColumn[index] = Math.max(1, Pickit.beltSize - 1)); - }); - } - }, - - // eslint-disable-next-line no-unused-vars - itemEvent: function (gid, mode, code, global) { - if (gid > 0 && mode === 0) { - Pickit.gidList.push(gid); - } - }, - - /** - * Just sort by distance for general item pickup - * @param {ItemUnit} unitA - * @param {ItemUnit} unitB - */ - sortItems: function (unitA, unitB) { - return getDistance(me, unitA) - getDistance(me, unitB); - }, - - /** - * Prioritize runes and unique items for fast pick - * @param {ItemUnit} unitA - * @param {ItemUnit} unitB - */ - sortFastPickItems: function (unitA, unitB) { - if (unitA.itemType === sdk.items.type.Rune || unitA.unique) return -1; - if (unitB.itemType === sdk.items.type.Rune || unitB.unique) return 1; - - return getDistance(me, unitA) - getDistance(me, unitB); - }, - - checkBelt: function () { - let check = 0; - let item = me.getItem(-1, sdk.items.mode.inBelt); - - if (item) { - do { - if (item.x < 4) { - check += 1; - } - } while (item.getNext()); - } - - return check === 4; - }, - - /** - * @param {ItemUnit} unit - */ - canPick: function (unit) { - if (!unit) return false; - if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) return false; - - let tome, potion, needPots, buffers, pottype, myKey, key; - - switch (unit.itemType) { - case sdk.items.type.Gold: - // Check current gold vs max capacity (cLvl*10000) - if (me.getStat(sdk.stats.Gold) === me.getStat(sdk.stats.Level) * 10000) { - return false; // Skip gold if full - } - - break; - case sdk.items.type.Scroll: - // 518 - Tome of Town Portal or 519 - Tome of Identify - tome = me.getItem(unit.classid - 11, sdk.items.mode.inStorage); - - if (tome) { - do { - // In inventory, contains 20 scrolls - if (tome.isInInventory && tome.getStat(sdk.stats.Quantity) === 20) { - return false; // Skip a scroll if its tome is full - } - } while (tome.getNext()); - } else { - return false; // Don't pick scrolls if there's no tome - } - - 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) { - let 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 (let 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()); - } - - if (needPots < 1 && this.checkBelt()) { - buffers = ["HPBuffer", "MPBuffer", "RejuvBuffer"]; - - for (let 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()); - } - } - } - } - } - - 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); - case undefined: // Yes, it does happen - console.warn("undefined item (!?)"); - - return false; - } - - return true; - }, - - /** - * @param {ItemUnit} unit - * @returns { { result: PickitResult, line: string } } - * -1 : Needs iding, - * 0 : Unwanted, - * 1 : NTIP wants, - * 2 : Cubing wants, - * 3 : Runeword wants, - * 4 : Pickup to sell (triggered when low on gold) - */ - checkItem: function (unit) { - let rval = NTIP.CheckItem(unit, false, true); - const resultObj = (result, line = null) => ({ - result: result, - line: line - }); - - // make sure we have essentials - no pickit files loaded - if (rval.result === Pickit.Result.UNWANTED && Config.PickitFiles.length === 0 && Pickit.essentials.includes(unit.itemType) && this.canPick(unit)) { - return resultObj(Pickit.Result.WANTED); - } - - if ((unit.classid === sdk.items.runes.Ort || unit.classid === sdk.items.runes.Ral) && Town.repairIngredientCheck(unit)) { - return resultObj(Pickit.Result.UTILITY); - } - - 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 Gemhunting, pick Item for Cubing, if no other system needs it - if ((Scripts.GemHunter) // gemhunter active - && rval.result === Pickit.Result.UNWANTED // no other subsystem needs it - && Config.GemHunter.GemList.some((p) => [unit.classid - 1, unit.classid].includes(p)) // base and upgraded gem will be kept - && me.getItemsEx(unit.classid, sdk.items.mode.inStorage) - .filter(i => i.gid !== unit.gid && (!CraftingSystem.checkItem(i) && !Cubing.checkItem(i) && !Runewords.checkItem(i))).length === 0 // bit annoying for now but force only keeping max 1 gem for gemhunter - ) return resultObj(Pickit.Result.WANTED, "GemHunter"); - - if (rval.result === Pickit.Result.UNWANTED && !Town.ignoreType(unit.itemType) && !unit.questItem - && ((unit.isInInventory && (me.inTown || !Config.FieldID.Enabled)) || (me.gold < Config.LowGold || (me.gold < 500000 && Config.PickitFiles.length === 0)))) { - // Gold doesn't take up room, just pick it up - if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.TRASH); - - if (!this.invoLocked) { - 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 LowGold Item: " + itemValue); - } else if (itemValuePerSquare >= 10) { - // 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; - }, - - track: { - lastItem: null, - }, - - /** - * @param {ItemUnit} unit - * @param {PickitResult} status - * @param {string} keptLine - * @param {number} retry - * @todo figure out why sometimes we double print picking up an item, gut feeling is recursion somewhere - */ - pickItem: function (unit, status, keptLine, retry = 3) { - /** - * @constructor - * @param {ItemUnit} unit - */ - function ItemStats (unit) { - this.ilvl = unit.ilvl; - this.type = unit.itemType; - this.classid = unit.classid; - this.name = unit.name; - this.color = Item.color(unit); - this.gold = unit.getStat(sdk.stats.Gold); - this.dist = (unit.distance || Infinity); - this.useTk = (Skill.haveTK && Pickit.tkable.includes(this.type) - && this.dist > 5 && this.dist < 20 && !checkCollision(me, unit, sdk.collision.WallOrRanged)); - this.picked = false; - } - - let gid = (unit.gid || -1); - let cancelFlags = [sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube]; - let itemCount = me.itemcount; - let item = gid > -1 ? Game.getItem(-1, -1, gid) : false; - - if (!item) return false; - - for (let i = 0; i < cancelFlags.length; i += 1) { - if (getUIFlag(cancelFlags[i])) { - delay(500); - me.cancel(0); - - break; - } - } - - let stats = new ItemStats(item); - let tkMana = stats.useTk ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 : Infinity; - - MainLoop: - for (let i = 0; i < retry; i += 1) { - if (me.dead) return false; - // recursion appeared - if (this.track.lastItem === gid) return true; - // can't find the item - if (!Game.getItem(-1, -1, gid)) return false; - - if (me.getItem(item.classid, -1, item.gid)) { - console.debug("Already picked item"); - return true; - } - - while (!me.idle) { - delay(40); - } - - if (!item.onGroundOrDropping) { - break; - } - - // fastPick check? should only pick items if surrounding monsters have been cleared or if fastPick is active - // note: clear of surrounding monsters of the spectype we are set to clear - if (stats.useTk && me.mp > tkMana) { - if (!Packet.telekinesis(item)) { - i > 1 && (stats.useTk = false); - continue; - } - } else { - if (item.distance > (Config.FastPick || i < 1 ? 6 : 4) || checkCollision(me, item, sdk.collision.BlockWall)) { - if (!Pather.moveToEx(item.x, item.y, { retry: 3, allowPicking: false, minDist: 4 })) { - continue; - } - // we had to move, lets check to see if it's still there - if (me.getItem(-1, -1, gid)) { - // we picked the item during another process - recursion happened - // this has pontential to skip logging an item - return true; - } - if (!Game.getItem(-1, -1, gid)) { - // it's gone so don't continue, - return false; - } - } - - // use packet first, if we fail and not using fast pick use click - (Config.FastPick || i < 1) ? Packet.click(item) : Misc.click(0, 0, item); - } - - let tick = getTickCount(); - - while (getTickCount() - tick < 1000) { - // why the use of copyUnit here? - item = copyUnit(item); - - 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; - } - - break MainLoop; - } - - delay(20); - } - - // TK failed, disable it - stats.useTk = false; - } - - stats.picked = me.itemcount > itemCount || !!me.getItem(-1, -1, gid); - - if (stats.picked) { - DataFile.updateStats("lastArea"); - - switch (status) { - case Pickit.Result.WANTED: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (keptLine ? ") (" + keptLine + ")" : ")")); - if (this.ignoreLog.indexOf(stats.type) === -1) { - Item.logger("Kept", item); - Item.logItem("Kept", item, keptLine); - } - - 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; - default: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + (keptLine ? ") (" + keptLine + ")" : ")")); - - break; - } - - this.track.lastItem = item.gid; - } - - return true; - }, - - /** - * Check if we can even free up the inventory - */ - canMakeRoom: function () { - if (!Config.MakeRoom) return false; - - let items = Storage.Inventory.Compare(Config.Inventory) || []; - - if (items.length) { - return items.some(item => { - switch (Pickit.checkItem(item).result) { - case Pickit.Result.UNID: - // For low level chars that can't actually get id scrolls -> prevent an infinite loop - return (me.gold > 100); - case Pickit.Result.UNWANTED: - case Pickit.Result.TRASH: - // if we've got items to sell then we can make room as long as we can get to town - return me.canTpToTown(); - default: // Check if a kept item can be stashed - return Town.canStash(item); - } - }); - } - - return false; - }, - - /** @type {ItemUnit[]} */ - pickList: [], - /** @type {Set} */ - ignoreList: new Set(), - - /** - * @param {number} range - * @returns {boolean} If we picked items - */ - pickItems: function (range = Config.PickRange) { - if (me.dead) return false; - - let needMule = false; - const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); - - // why wait for idle? - 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))) { - Town.clearBelt(); - } - - while (Pickit.pickList.length > 0) { - if (me.dead) return false; - Pickit.pickList.sort(this.sortItems); - const check = Pickit.pickList.shift(); - // get the actual item again - const itemToPick = Game.getItem(check.classid, -1, check.gid); - - if (!itemToPick || Pickit.ignoreList.has(itemToPick.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 (itemToPick && copyUnit(itemToPick).x !== undefined && itemToPick.onGroundOrDropping - && (Pather.useTeleport() || me.inTown || !checkCollision(me, itemToPick, sdk.collision.BlockWall))) { - // Check if the item should be picked - let status = this.checkItem(itemToPick); - - if (status.result && this.canPick(itemToPick)) { - // Override canFit for scrolls, potions and gold - let canFit = (Storage.Inventory.CanFit(itemToPick) || Pickit.essentials.includes(itemToPick.itemType)); - - // 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 = (itemToPick.gid !== undefined && Storage.Inventory.CanFit(itemToPick))); - } - - // 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(itemToPick) + itemToPick.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. - Pickit.ignoreList.clear(); - return this.pickItems(); - } - - // Town visit failed - abort - console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); - - return false; - } - - // Can't make room - trigger automule - if (copyUnit(itemToPick).x !== undefined) { - Item.logger("No room for", itemToPick); - console.warn("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); - Pickit.ignoreList.add(itemToPick.gid); - if (canUseMule) { - console.debug("Attempt to trigger automule"); - needMule = true; - } - - break; - } - } - - // Item can fit - pick it up - if (canFit) { - let picked = this.pickItem(itemToPick, status.result, status.line); - if (!picked) { - console.warn("Failed to pick item " + itemToPick.prettyPrint); - - break; - } - } - } - } - } - - // Quit current game and transfer the items to mule - if (needMule && canUseMule && AutoMule.getMuleItems().length > 0) { - scriptBroadcast("mule"); - scriptBroadcast("quit"); - - return false; - } - - return true; - }, - - /** - * @param {number} retry - */ - fastPick: function (retry = 3) { - let item, itemList = []; - - while (this.gidList.length > 0) { - let gid = this.gidList.shift(); - item = Game.getItem(-1, -1, gid); - - if (item && item.onGroundOrDropping - && (!Town.ignoreType(item.itemType) || (item.itemType >= sdk.items.type.HealingPotion && item.itemType <= sdk.items.type.RejuvPotion)) - && item.itemType !== sdk.items.type.Gold && getDistance(me, item) <= Config.PickRange) { - itemList.push(copyUnit(item)); - } - } - - while (itemList.length > 0) { - itemList.sort(this.sortFastPickItems); - let check = itemList.shift(); - // we were passed the copied unit, lets find the real thing - item = Game.getItem(check.classid, -1, check.gid); - - // Check if the item unit is still valid - if (item && item.x !== undefined) { - let status = this.checkItem(item); - - if (status.result && this.canPick(item) && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType))) { - this.pickItem(item, status.result, status.line, retry); - } - } - } - - return true; - }, + gidList: [], + invoLocked: true, + beltSize: 1, + /** @enum */ + Result: { + UNID: -1, + UNWANTED: 0, + WANTED: 1, + CUBING: 2, + RUNEWORD: 3, + TRASH: 4, + CRAFTING: 5, + UTILITY: 6 + }, + /** + * Ignored item types for item logging + */ + ignoreLog: [ + sdk.items.type.Gold, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, + 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 + ], + tkable: [ + sdk.items.type.Gold, sdk.items.type.Scroll, sdk.items.type.HealingPotion, + sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion, + sdk.items.type.StaminaPotion, sdk.items.type.AntidotePotion, sdk.items.type.ThawingPotion + ], + essentials: [ + sdk.items.type.Gold, sdk.items.type.Scroll, + sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion + ], + + /** + * @param {boolean} notify + */ + init: function (notify) { + Config.PickitFiles.forEach((file) => NTIP.OpenFile("pickit/" + file, notify)); + // check if we can pick up items, only do this is our inventory slots aren't completly locked + Pickit.invoLocked = !Config.Inventory.some(row => row.some(el => el > 0)); + + // sometime Storage isn't loaded? + if (typeof Storage !== "undefined") { + Pickit.beltSize = Storage.BeltSize(); + // If MinColumn is set to be more than our current belt size, set it to be 1 less than the belt size 4x3 belt will give us Config.MinColumn = [2, 2, 2, 2] + Config.MinColumn.forEach((el, index) => { + el >= Pickit.beltSize && (Config.MinColumn[index] = Math.max(1, Pickit.beltSize - 1)); + }); + } + }, + + // eslint-disable-next-line no-unused-vars + itemEvent: function (gid, mode, code, global) { + if (gid > 0 && mode === 0) { + Pickit.gidList.push(gid); + } + }, + + /** + * Just sort by distance for general item pickup + * @param {ItemUnit} unitA + * @param {ItemUnit} unitB + */ + sortItems: function (unitA, unitB) { + return getDistance(me, unitA) - getDistance(me, unitB); + }, + + /** + * Prioritize runes and unique items for fast pick + * @param {ItemUnit} unitA + * @param {ItemUnit} unitB + */ + sortFastPickItems: function (unitA, unitB) { + if (unitA.itemType === sdk.items.type.Rune || unitA.unique) return -1; + if (unitB.itemType === sdk.items.type.Rune || unitB.unique) return 1; + + return getDistance(me, unitA) - getDistance(me, unitB); + }, + + checkBelt: function () { + let check = 0; + let item = me.getItem(-1, sdk.items.mode.inBelt); + + if (item) { + do { + if (item.x < 4) { + check += 1; + } + } while (item.getNext()); + } + + return check === 4; + }, + + /** + * @param {ItemUnit} unit + */ + canPick: function (unit) { + if (!unit) return false; + if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) return false; + + let tome, potion, needPots, buffers, pottype, myKey, key; + + switch (unit.itemType) { + case sdk.items.type.Gold: + // Check current gold vs max capacity (cLvl*10000) + if (me.getStat(sdk.stats.Gold) === me.getStat(sdk.stats.Level) * 10000) { + return false; // Skip gold if full + } + + break; + case sdk.items.type.Scroll: + // 518 - Tome of Town Portal or 519 - Tome of Identify + tome = me.getItem(unit.classid - 11, sdk.items.mode.inStorage); + + if (tome) { + do { + // In inventory, contains 20 scrolls + if (tome.isInInventory && tome.getStat(sdk.stats.Quantity) === 20) { + return false; // Skip a scroll if its tome is full + } + } while (tome.getNext()); + } else { + return false; // Don't pick scrolls if there's no tome + } + + 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) { + let 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 (let 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()); + } + + if (needPots < 1 && this.checkBelt()) { + buffers = ["HPBuffer", "MPBuffer", "RejuvBuffer"]; + + for (let 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()); + } + } + } + } + } + + 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); + case undefined: // Yes, it does happen + console.warn("undefined item (!?)"); + + return false; + } + + return true; + }, + + /** + * @param {ItemUnit} unit + * @returns { { result: PickitResult, line: string } } + * -1 : Needs iding, + * 0 : Unwanted, + * 1 : NTIP wants, + * 2 : Cubing wants, + * 3 : Runeword wants, + * 4 : Pickup to sell (triggered when low on gold) + */ + checkItem: function (unit) { + let rval = NTIP.CheckItem(unit, false, true); + const resultObj = (result, line = null) => ({ + result: result, + line: line + }); + + // make sure we have essentials - no pickit files loaded + if (rval.result === Pickit.Result.UNWANTED && Config.PickitFiles.length === 0 + && Pickit.essentials.includes(unit.itemType) && this.canPick(unit)) { + return resultObj(Pickit.Result.WANTED); + } + + if ((unit.classid === sdk.items.runes.Ort || unit.classid === sdk.items.runes.Ral) + && Town.repairIngredientCheck(unit)) { + return resultObj(Pickit.Result.UTILITY); + } + + 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 Gemhunting, pick Item for Cubing, if no other system needs it + if (Scripts.GemHunter && rval.result === Pickit.Result.UNWANTED) { + // gemhunter active + if (Config.GemHunter.GemList.some((p) => [unit.classid - 1, unit.classid].includes(p))) { + // base and upgraded gem will be kept + let _items = me.getItemsEx(unit.classid, sdk.items.mode.inStorage) + .filter(i => i.gid !== unit.gid + && !CraftingSystem.checkItem(i) && !Cubing.checkItem(i) && !Runewords.checkItem(i)); + if (_items.length === 0) return resultObj(Pickit.Result.WANTED, "GemHunter"); + } + } + + if (rval.result === Pickit.Result.UNWANTED && !Town.ignoreType(unit.itemType) && !unit.questItem + && ((unit.isInInventory && (me.inTown || !Config.FieldID.Enabled)) + || (me.gold < Config.LowGold || (me.gold < 500000 && Config.PickitFiles.length === 0)))) { + // Gold doesn't take up room, just pick it up + if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.TRASH); + + if (!this.invoLocked) { + 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 LowGold Item: " + itemValue); + } else if (itemValuePerSquare >= 10) { + // 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; + }, + + track: { + lastItem: null, + }, + + /** + * @param {ItemUnit} unit + * @param {PickitResult} status + * @param {string} keptLine + * @param {number} retry + * @todo figure out why sometimes we double print picking up an item, gut feeling is recursion somewhere + */ + pickItem: function (unit, status, keptLine, retry = 3) { + /** + * @constructor + * @param {ItemUnit} unit + */ + function ItemStats (unit) { + this.ilvl = unit.ilvl; + this.type = unit.itemType; + this.classid = unit.classid; + this.name = unit.name; + this.color = Item.color(unit); + this.gold = unit.getStat(sdk.stats.Gold); + this.dist = (unit.distance || Infinity); + this.useTk = (Skill.haveTK && Pickit.tkable.includes(this.type) + && this.dist > 5 && this.dist < 20 && !checkCollision(me, unit, sdk.collision.WallOrRanged)); + this.picked = false; + } + + let gid = (unit.gid || -1); + const cancelFlags = [ + sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, + sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube + ]; + let itemCount = me.itemcount; + let item = gid > -1 ? Game.getItem(-1, -1, gid) : false; + + if (!item) return false; + + for (let i = 0; i < cancelFlags.length; i += 1) { + if (getUIFlag(cancelFlags[i])) { + delay(500); + me.cancel(0); + + break; + } + } + + let stats = new ItemStats(item); + let tkMana = stats.useTk ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 : Infinity; + + MainLoop: + for (let i = 0; i < retry; i += 1) { + if (me.dead) return false; + // recursion appeared + if (this.track.lastItem === gid) return true; + // can't find the item + if (!Game.getItem(-1, -1, gid)) return false; + + if (me.getItem(item.classid, -1, item.gid)) { + console.debug("Already picked item"); + return true; + } + + while (!me.idle) { + delay(40); + } + + if (!item.onGroundOrDropping) { + break; + } + + // fastPick check? should only pick items if surrounding monsters have been cleared or if fastPick is active + // note: clear of surrounding monsters of the spectype we are set to clear + if (stats.useTk && me.mp > tkMana) { + if (!Packet.telekinesis(item)) { + i > 1 && (stats.useTk = false); + continue; + } + } else { + if (item.distance > (Config.FastPick || i < 1 ? 6 : 4) || checkCollision(me, item, sdk.collision.BlockWall)) { + if (!Pather.moveToEx(item.x, item.y, { retry: 3, allowPicking: false, minDist: 4 })) { + continue; + } + // we had to move, lets check to see if it's still there + if (me.getItem(-1, -1, gid)) { + // we picked the item during another process - recursion happened + // this has pontential to skip logging an item + return true; + } + if (!Game.getItem(-1, -1, gid)) { + // it's gone so don't continue, + return false; + } + } + + // use packet first, if we fail and not using fast pick use click + (Config.FastPick || i < 1) ? Packet.click(item) : Misc.click(0, 0, item); + } + + let tick = getTickCount(); + + while (getTickCount() - tick < 1000) { + // why the use of copyUnit here? + item = copyUnit(item); + + 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; + } + + break MainLoop; + } + + delay(20); + } + + // TK failed, disable it + stats.useTk = false; + } + + stats.picked = me.itemcount > itemCount || !!me.getItem(-1, -1, gid); + + if (stats.picked) { + DataFile.updateStats("lastArea"); + let _common = "ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")"; + + switch (status) { + case Pickit.Result.WANTED: + console.log(_common + (keptLine ? " (" + keptLine + ")" : "")); + if (this.ignoreLog.indexOf(stats.type) === -1) { + Item.logger("Kept", item); + Item.logItem("Kept", item, keptLine); + } + + break; + case Pickit.Result.CUBING: + console.log(_common + " (Cubing)"); + Item.logger("Kept", item, "Cubing " + me.findItems(item.classid).length); + Cubing.update(); + + break; + case Pickit.Result.RUNEWORD: + console.log(_common + " (Runewords)"); + Item.logger("Kept", item, "Runewords"); + Runewords.update(stats.classid, gid); + + break; + case Pickit.Result.CRAFTING: + console.log(_common + " (Crafting System)"); + CraftingSystem.update(item); + + break; + default: + console.log(_common + (keptLine ? " (" + keptLine + ")" : "")); + + break; + } + + this.track.lastItem = item.gid; + } + + return true; + }, + + /** + * Check if we can even free up the inventory + */ + canMakeRoom: function () { + if (!Config.MakeRoom) return false; + + let items = Storage.Inventory.Compare(Config.Inventory) || []; + + if (items.length) { + return items.some(item => { + switch (Pickit.checkItem(item).result) { + case Pickit.Result.UNID: + // For low level chars that can't actually get id scrolls -> prevent an infinite loop + return (me.gold > 100); + case Pickit.Result.UNWANTED: + case Pickit.Result.TRASH: + // if we've got items to sell then we can make room as long as we can get to town + return me.canTpToTown(); + default: // Check if a kept item can be stashed + return Town.canStash(item); + } + }); + } + + return false; + }, + + /** @type {ItemUnit[]} */ + pickList: [], + /** @type {Set} */ + ignoreList: new Set(), + + /** + * @param {number} range + * @returns {boolean} If we picked items + */ + pickItems: function (range = Config.PickRange) { + if (me.dead) return false; + + let needMule = false; + const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); + + // why wait for idle? + 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))) { + Town.clearBelt(); + } + + while (Pickit.pickList.length > 0) { + if (me.dead) return false; + Pickit.pickList.sort(this.sortItems); + const check = Pickit.pickList.shift(); + // get the actual item again + const itemToPick = Game.getItem(check.classid, -1, check.gid); + + if (!itemToPick || Pickit.ignoreList.has(itemToPick.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 (itemToPick && copyUnit(itemToPick).x !== undefined && itemToPick.onGroundOrDropping + && (Pather.useTeleport() || me.inTown || !checkCollision(me, itemToPick, sdk.collision.BlockWall))) { + // Check if the item should be picked + let status = this.checkItem(itemToPick); + + if (status.result && this.canPick(itemToPick)) { + // Override canFit for scrolls, potions and gold + let canFit = (Storage.Inventory.CanFit(itemToPick) || Pickit.essentials.includes(itemToPick.itemType)); + + // 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 = (itemToPick.gid !== undefined && Storage.Inventory.CanFit(itemToPick))); + } + + // 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(itemToPick) + itemToPick.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. + Pickit.ignoreList.clear(); + return this.pickItems(); + } + + // Town visit failed - abort + console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); + + return false; + } + + // Can't make room - trigger automule + if (copyUnit(itemToPick).x !== undefined) { + Item.logger("No room for", itemToPick); + console.warn("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); + Pickit.ignoreList.add(itemToPick.gid); + if (canUseMule) { + console.debug("Attempt to trigger automule"); + needMule = true; + } + + break; + } + } + + // Item can fit - pick it up + if (canFit) { + let picked = this.pickItem(itemToPick, status.result, status.line); + if (!picked) { + console.warn("Failed to pick item " + itemToPick.prettyPrint); + + break; + } + } + } + } + } + + // Quit current game and transfer the items to mule + if (needMule && canUseMule && AutoMule.getMuleItems().length > 0) { + scriptBroadcast("mule"); + scriptBroadcast("quit"); + + return false; + } + + return true; + }, + + /** + * @param {number} retry + */ + fastPick: function (retry = 3) { + let item, itemList = []; + + while (this.gidList.length > 0) { + let gid = this.gidList.shift(); + item = Game.getItem(-1, -1, gid); + + if (item && item.onGroundOrDropping + && (!Town.ignoreType(item.itemType) || (item.itemType >= sdk.items.type.HealingPotion + && item.itemType <= sdk.items.type.RejuvPotion)) + && item.itemType !== sdk.items.type.Gold && getDistance(me, item) <= Config.PickRange) { + itemList.push(copyUnit(item)); + } + } + + while (itemList.length > 0) { + itemList.sort(this.sortFastPickItems); + let check = itemList.shift(); + // we were passed the copied unit, lets find the real thing + item = Game.getItem(check.classid, -1, check.gid); + + // Check if the item unit is still valid + if (item && item.x !== undefined) { + let status = this.checkItem(item); + + if (status.result && this.canPick(item) + && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType))) { + this.pickItem(item, status.result, status.line, retry); + } + } + } + + return true; + }, }; diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index af6455960..e16c83d02 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -6,642 +6,658 @@ */ const Precast = { - enabled: true, - haveCTA: -1, - bestSlot: {}, - - // TODO: build better method of keeping track of duration based skills so we can reduce resource usage - // build obj -> figure out which skills we have -> calc duration -> assign tick of last casted -> track tick (background worker maybe?) - // would reduce checking have skill and state calls, just let tick = getTickCount(); -> obj.some((el) => tick - el.lastTick > el.duration) -> true then cast - // would probably make sense to just re-cast everything (except summons) if one of our skills is about to run out rather than do this process again 3 seconds later - skills: { - // Not sure how I want to handle cold armors - coldArmor: { - best: false, - duration: 0, - tick: 0 - }, - boneArmor: { - max: 0, - armorPercent: function () { - return this.max > 0 ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) : 100; - }, - }, - holyShield: { - canUse: false, - duration: 0, - tick: 0 - }, - shout: { - duration: 0, - tick: 0 - }, - battleOrders: { - duration: 0, - tick: 0 - }, - battleCommand: { - duration: 0, - tick: 0 - }, - }, - - /** + enabled: true, + haveCTA: -1, + bestSlot: {}, + + // TODO: build better method of keeping track of duration based skills so we can reduce resource usage + // build obj -> figure out which skills we have -> calc duration -> assign tick of last casted -> track tick (background worker maybe?) + // would reduce checking have skill and state calls, just let tick = getTickCount(); -> obj.some((el) => tick - el.lastTick > el.duration) -> true then cast + // would probably make sense to just re-cast everything (except summons) if one of our skills is about to run out rather than do this process again 3 seconds later + skills: { + // Not sure how I want to handle cold armors + coldArmor: { + best: false, + duration: 0, + tick: 0 + }, + boneArmor: { + max: 0, + armorPercent: function () { + return this.max > 0 ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) : 100; + }, + }, + holyShield: { + canUse: false, + duration: 0, + tick: 0 + }, + shout: { + duration: 0, + tick: 0 + }, + battleOrders: { + duration: 0, + tick: 0 + }, + battleCommand: { + duration: 0, + tick: 0 + }, + }, + + /** * Easier Shout/Bo/Bc casting with state checks to ensure it was casted * @param {number} skillId * @param {number | Unit} x * @param {number} [y] * @returns {boolean} */ - warCries: function (skillId, x, y) { - if (!skillId || x === undefined) return false; - const states = {}; - states[sdk.skills.Shout] = sdk.states.Shout; - states[sdk.skills.BattleOrders] = sdk.states.BattleOrders; - states[sdk.skills.BattleCommand] = sdk.states.BattleCommand; - if (states[skillId] === undefined) return false; - - for (let i = 0; i < 3; i++) { - try { - if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) { - throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); - } - // Right hand + No Shift - let clickType = 3, shift = 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); - } - - if (Misc.poll(() => me.getState(states[skillId]), 300, 50)) return true; - } catch (e) { - console.error(e); - return false; - } - } - return false; - }, - - checkCTA: function () { - if (this.haveCTA > -1) return true; - - let check = me.checkItem({ name: sdk.locale.items.CalltoArms, equipped: true }); - - if (check.have) { - Precast.haveCTA = check.item.isOnSwap ? 1 : 0; - } - - return this.haveCTA > -1; - }, - - /** + warCries: function (skillId, x, y) { + if (!skillId || x === undefined) return false; + const states = {}; + states[sdk.skills.Shout] = sdk.states.Shout; + states[sdk.skills.BattleOrders] = sdk.states.BattleOrders; + states[sdk.skills.BattleCommand] = sdk.states.BattleCommand; + if (states[skillId] === undefined) return false; + + for (let i = 0; i < 3; i++) { + try { + if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) { + throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); + } + // Right hand + No Shift + let clickType = 3, shift = 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); + } + + if (Misc.poll(() => me.getState(states[skillId]), 300, 50)) return true; + } catch (e) { + console.error(e); + return false; + } + } + return false; + }, + + checkCTA: function () { + if (this.haveCTA > -1) return true; + + let check = me.checkItem({ name: sdk.locale.items.CalltoArms, equipped: true }); + + if (check.have) { + Precast.haveCTA = check.item.isOnSwap ? 1 : 0; + } + + return this.haveCTA > -1; + }, + + /** * @param {boolean} force * @returns {boolean} */ - precastCTA: function (force = false) { - if (!Config.UseCta || this.haveCTA === -1 || me.classic || me.barbarian || me.inTown || me.shapeshifted) return false; - if (!force && me.getState(sdk.states.BattleOrders)) return true; + precastCTA: function (force = false) { + if (!Config.UseCta || this.haveCTA === -1 || me.classic || me.barbarian || me.inTown || me.shapeshifted) { + return false; + } + if (!force && me.getState(sdk.states.BattleOrders)) return true; - if (this.haveCTA > -1) { - let slot = me.weaponswitch; - let { x, y } = me; + if (this.haveCTA > -1) { + let slot = me.weaponswitch; + let { x, y } = me; - me.switchWeapons(this.haveCTA); - this.cast(sdk.skills.BattleCommand, x, y, true); - this.cast(sdk.skills.BattleCommand, x, y, true); - this.cast(sdk.skills.BattleOrders, x, y, true); + me.switchWeapons(this.haveCTA); + this.cast(sdk.skills.BattleCommand, x, y, true); + this.cast(sdk.skills.BattleCommand, x, y, true); + this.cast(sdk.skills.BattleOrders, x, y, true); - this.skills.battleOrders.tick = getTickCount(); - // does this need to be re-calculated everytime? if no autobuild should really just be done when we initialize - !this.skills.battleOrders.duration && (this.skills.battleOrders.duration = Skill.getDuration(sdk.skills.BattleOrders)); + this.skills.battleOrders.tick = getTickCount(); + // does this need to be re-calculated everytime? if no autobuild should really just be done when we initialize + if (!this.skills.battleOrders.duration) { + this.skills.battleOrders.duration = Skill.getDuration(sdk.skills.BattleOrders); + } - me.switchWeapons(slot); + me.switchWeapons(slot); - return true; - } + return true; + } - return false; - }, + return false; + }, - /** + /** * Check which slot (primary or secondary) gives us the most skillpoints in a skill * @param {number} skillId * @returns {0 | 1} best slot to give us the most skillpoints in a skill * @todo Move this to be part of the SkillData class */ - getBetterSlot: function (skillId) { - if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; - - let [classid, skillTab] = (() => { - switch (skillId) { - case sdk.skills.FrozenArmor: - case sdk.skills.ShiverArmor: - case sdk.skills.ChillingArmor: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Cold]; - case sdk.skills.Enchant: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Fire]; - case sdk.skills.ThunderStorm: - case sdk.skills.EnergyShield: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Lightning]; - case sdk.skills.BoneArmor: - return [sdk.player.class.Necromancer, sdk.skills.tabs.PoisonandBone]; - case sdk.skills.HolyShield: - return [sdk.player.class.Paladin, sdk.skills.tabs.PalaCombat]; - case sdk.skills.Taunt: - case sdk.skills.FindItem: - case sdk.skills.BattleCry: - case sdk.skills.WarCry: - case sdk.skills.Shout: - case sdk.skills.BattleOrders: - case sdk.skills.BattleCommand: - return [sdk.player.class.Barbarian, sdk.skills.tabs.Warcries]; - case sdk.skills.CycloneArmor: - return [sdk.player.class.Druid, sdk.skills.tabs.Elemental]; - case sdk.skills.Werewolf: - case sdk.skills.Werebear: - return [sdk.player.class.Druid, sdk.skills.tabs.ShapeShifting]; - case sdk.skills.BurstofSpeed: - case sdk.skills.Fade: - return [sdk.player.class.Assassin, sdk.skills.tabs.ShadowDisciplines]; - case sdk.skills.BladeShield: - return [sdk.player.class.Assassin, sdk.skills.tabs.MartialArts]; - default: - return [-1, -1]; - } - })(); - - if (classid < 0) return me.weaponswitch; - - me.weaponswitch !== 0 && me.switchWeapons(0); - - let [sumCurr, sumSwap] = [0, 0]; - const sumStats = function (item) { - return (item.getStat(sdk.stats.AllSkills) + getBetterSlot: function (skillId) { + if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; + + let [classid, skillTab] = (() => { + switch (skillId) { + case sdk.skills.FrozenArmor: + case sdk.skills.ShiverArmor: + case sdk.skills.ChillingArmor: + return [sdk.player.class.Sorceress, sdk.skills.tabs.Cold]; + case sdk.skills.Enchant: + return [sdk.player.class.Sorceress, sdk.skills.tabs.Fire]; + case sdk.skills.ThunderStorm: + case sdk.skills.EnergyShield: + return [sdk.player.class.Sorceress, sdk.skills.tabs.Lightning]; + case sdk.skills.BoneArmor: + return [sdk.player.class.Necromancer, sdk.skills.tabs.PoisonandBone]; + case sdk.skills.HolyShield: + return [sdk.player.class.Paladin, sdk.skills.tabs.PalaCombat]; + case sdk.skills.Taunt: + case sdk.skills.FindItem: + case sdk.skills.BattleCry: + case sdk.skills.WarCry: + case sdk.skills.Shout: + case sdk.skills.BattleOrders: + case sdk.skills.BattleCommand: + return [sdk.player.class.Barbarian, sdk.skills.tabs.Warcries]; + case sdk.skills.CycloneArmor: + return [sdk.player.class.Druid, sdk.skills.tabs.Elemental]; + case sdk.skills.Werewolf: + case sdk.skills.Werebear: + return [sdk.player.class.Druid, sdk.skills.tabs.ShapeShifting]; + case sdk.skills.BurstofSpeed: + case sdk.skills.Fade: + return [sdk.player.class.Assassin, sdk.skills.tabs.ShadowDisciplines]; + case sdk.skills.BladeShield: + return [sdk.player.class.Assassin, sdk.skills.tabs.MartialArts]; + default: + return [-1, -1]; + } + })(); + + if (classid < 0) return me.weaponswitch; + + me.weaponswitch !== 0 && me.switchWeapons(0); + + let [sumCurr, sumSwap] = [0, 0]; + const sumStats = function (item) { + return (item.getStat(sdk.stats.AllSkills) + item.getStat(sdk.stats.AddClassSkills, classid) + item.getStat(sdk.stats.AddSkillTab, skillTab) + item.getStat(sdk.stats.SingleSkill, skillId) + item.getStat(sdk.stats.NonClassSkill, skillId)); - }; - - me.getItemsEx() - .filter(item => item.isEquipped && [sdk.body.RightArm, sdk.body.LeftArm, sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(item.bodylocation)) - .forEach(function (item) { - if (item.isOnMain) { - sumCurr += sumStats(item); - return; - } - - if (item.isOnSwap) { - sumSwap += sumStats(item); - return; - } - }); - this.bestSlot[skillId] = (sumSwap > sumCurr) ? me.weaponswitch ^ 1 : me.weaponswitch; - return this.bestSlot[skillId]; - }, - - cast: function (skillId, x = me.x, y = me.y, dontSwitch = false) { - if (!skillId || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) return false; - if (Skill.getManaCost(skillId) > me.mp) return false; - - let swap = me.weaponswitch; - let success = true; - // don't use packet casting with summons - or boing - 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); - (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); - - try { - !dontSwitch && me.switchWeapons(this.getBetterSlot(skillId)); - if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); - if ([sdk.skills.Shout, sdk.skills.BattleOrders, sdk.skills.BattleCommand].includes(skillId)) return this.warCries(skillId, x, y); - - if (Config.PacketCasting > 1 || usePacket) { - Config.DebugMode.Skill && console.debug("Packet casting: " + skillId); + }; + + me.getItemsEx() + .filter(item => item.isEquipped && [ + sdk.body.RightArm, sdk.body.LeftArm, sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary + ].includes(item.bodylocation)) + .forEach(function (item) { + if (item.isOnMain) { + sumCurr += sumStats(item); + return; + } + + if (item.isOnSwap) { + sumSwap += sumStats(item); + return; + } + }); + this.bestSlot[skillId] = (sumSwap > sumCurr) ? me.weaponswitch ^ 1 : me.weaponswitch; + return this.bestSlot[skillId]; + }, + + cast: function (skillId, x = me.x, y = me.y, dontSwitch = false) { + if (!skillId || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) return false; + if (Skill.getManaCost(skillId) > me.mp) return false; + + let swap = me.weaponswitch; + let success = true; + // don't use packet casting with summons - or boing + 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); + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); + + try { + !dontSwitch && me.switchWeapons(this.getBetterSlot(skillId)); + if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) { + throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); + } + if ([sdk.skills.Shout, sdk.skills.BattleOrders, sdk.skills.BattleCommand].includes(skillId)) { + return this.warCries(skillId, x, y); + } + + if (Config.PacketCasting > 1 || usePacket) { + Config.DebugMode.Skill && console.debug("Packet casting: " + skillId); - switch (typeof x) { - case "number": - Packet.castSkill(sdk.skills.hand.Right, x, y); - - break; - case "object": - Packet.unitCast(sdk.skills.hand.Right, x); - - break; - } - delay(250); - } else { - // Right hand + No Shift - let clickType = 3, shift = 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 (Skill.isTimed(skillId)) { - for (let i = 0; i < 10; i += 1) { - if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { - break; - } - - delay(10); - } - } - } catch (e) { - console.error(e); - success = false; - } - - !dontSwitch && me.switchWeapons(swap); - - return success; - }, - - 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; - - if (retry > count * 2) { - if (me.inTown) { - Town.heal() && me.cancelUIFlags(); - Town.move("portalspot"); - Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); - } else { - let 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); - } - } - - if (me.getMinionCount(minionType) === count) { - return true; - } else { - console.warn("Failed to summon minion " + skillId); - - return false; - } - } - - // todo - only delay if we are close to the mana amount we need based on our mana regen rate or potion state - // also take into account surrounding mobs so we don't delay for mana in the middle of a mob pack - if (Skill.getManaCost(skillId) > me.mp) { - if (!Misc.poll(() => me.mp >= Skill.getManaCost(skillId), 500, 100)) { - retry++; - continue; - } - } - - let coord = CollMap.getRandCoordinate(me.x, -4, 4, me.y, -4, 4); - - if (!!coord && Attack.validSpot(coord.x, coord.y)) { - Skill.cast(skillId, sdk.skills.hand.Right, coord.x, coord.y); - - if (me.getMinionCount(minionType) === count) { - break; - } else { - retry++; - } - } - - delay(200); - } - - return !!rv; - }, - - enchant: function () { - let unit, slot = me.weaponswitch, chanted = []; - - me.switchWeapons(this.getBetterSlot(sdk.skills.Enchant)); - - // Player - unit = Game.getPlayer(); - - if (unit) { - do { - if (!unit.dead && Misc.inMyParty(unit.name) && unit.distance <= 40) { - Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); - chanted.push(unit.name); - } - } while (unit.getNext()); - } - - // Minion - unit = Game.getMonster(); - - if (unit) { - do { - if (unit.getParent() && chanted.includes(unit.getParent().name) && unit.distance <= 40) { - Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); - } - } while (unit.getNext()); - } - - me.switchWeapons(slot); - - return true; - }, - - // should the config check still be included even though its part of Skill.init? - /** + switch (typeof x) { + case "number": + Packet.castSkill(sdk.skills.hand.Right, x, y); + + break; + case "object": + Packet.unitCast(sdk.skills.hand.Right, x); + + break; + } + delay(250); + } else { + // Right hand + No Shift + let clickType = 3, shift = 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 (Skill.isTimed(skillId)) { + for (let i = 0; i < 10; i += 1) { + if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { + break; + } + + delay(10); + } + } + } catch (e) { + console.error(e); + success = false; + } + + !dontSwitch && me.switchWeapons(swap); + + return success; + }, + + 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; + + if (retry > count * 2) { + if (me.inTown) { + Town.heal() && me.cancelUIFlags(); + Town.move("portalspot"); + Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); + } else { + let 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); + } + } + + if (me.getMinionCount(minionType) === count) { + return true; + } else { + console.warn("Failed to summon minion " + skillId); + + return false; + } + } + + // todo - only delay if we are close to the mana amount we need based on our mana regen rate or potion state + // also take into account surrounding mobs so we don't delay for mana in the middle of a mob pack + if (Skill.getManaCost(skillId) > me.mp) { + if (!Misc.poll(() => me.mp >= Skill.getManaCost(skillId), 500, 100)) { + retry++; + continue; + } + } + + let coord = CollMap.getRandCoordinate(me.x, -4, 4, me.y, -4, 4); + + if (!!coord && Attack.validSpot(coord.x, coord.y)) { + Skill.cast(skillId, sdk.skills.hand.Right, coord.x, coord.y); + + if (me.getMinionCount(minionType) === count) { + break; + } else { + retry++; + } + } + + delay(200); + } + + return !!rv; + }, + + enchant: function () { + let unit, slot = me.weaponswitch, chanted = []; + + me.switchWeapons(this.getBetterSlot(sdk.skills.Enchant)); + + // Player + unit = Game.getPlayer(); + + if (unit) { + do { + if (!unit.dead && Misc.inMyParty(unit.name) && unit.distance <= 40) { + Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); + chanted.push(unit.name); + } + } while (unit.getNext()); + } + + // Minion + unit = Game.getMonster(); + + if (unit) { + do { + if (unit.getParent() && chanted.includes(unit.getParent().name) && unit.distance <= 40) { + Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); + } + } while (unit.getNext()); + } + + me.switchWeapons(slot); + + return true; + }, + + // should the config check still be included even though its part of Skill.init? + /** * @description Handle precast related skills * @param {boolean} force - force re-cast of all precast skills * @param {boolean} partial - force re-cast of all state related precast skills * @returns {boolean} sucessfully casted * @todo durations */ - doPrecast: function (force = false, partial = false) { - if (!this.enabled) return false; + doPrecast: function (force = false, partial = false) { + if (!this.enabled) return false; - while (!me.gameReady) { - delay(40); - } + while (!me.gameReady) { + delay(40); + } - let [buffSummons, forceBo] = [false, false]; + let [buffSummons, forceBo] = [false, false]; - // Force BO 30 seconds before it expires - if (Precast.haveCTA > -1) { - forceBo = (force || partial + // Force BO 30 seconds before it expires + if (Precast.haveCTA > -1) { + forceBo = (force || partial || (getTickCount() - Precast.skills.battleOrders.tick >= Precast.skills.battleOrders.duration - 30000) || !me.getState(sdk.states.BattleCommand)); - forceBo && this.precastCTA(forceBo); - } + forceBo && this.precastCTA(forceBo); + } - const needToCast = (state) => (force || partial || !me.getState(state)); + const needToCast = (state) => (force || partial || !me.getState(state)); - switch (me.classid) { - case sdk.player.class.Amazon: - Skill.canUse(sdk.skills.Valkyrie) && (buffSummons = this.summon(sdk.skills.Valkyrie, sdk.summons.type.Valkyrie)); + switch (me.classid) { + case sdk.player.class.Amazon: + Skill.canUse(sdk.skills.Valkyrie) && (buffSummons = this.summon(sdk.skills.Valkyrie, sdk.summons.type.Valkyrie)); - break; - case sdk.player.class.Sorceress: - if (Skill.canUse(sdk.skills.ThunderStorm) && needToCast(sdk.states.ThunderStorm)) { - this.cast(sdk.skills.ThunderStorm); - } + break; + case sdk.player.class.Sorceress: + if (Skill.canUse(sdk.skills.ThunderStorm) && needToCast(sdk.states.ThunderStorm)) { + this.cast(sdk.skills.ThunderStorm); + } - if (Skill.canUse(sdk.skills.EnergyShield) && needToCast(sdk.states.EnergyShield)) { - this.cast(sdk.skills.EnergyShield); - } + if (Skill.canUse(sdk.skills.EnergyShield) && needToCast(sdk.states.EnergyShield)) { + this.cast(sdk.skills.EnergyShield); + } - if (Config.UseColdArmor) { - let choosenSkill = (typeof Config.UseColdArmor === "number" && Skill.canUse(Config.UseColdArmor) - ? Config.UseColdArmor - : (Precast.skills.coldArmor.best || -1)); + if (Config.UseColdArmor) { + let choosenSkill = (typeof Config.UseColdArmor === "number" && Skill.canUse(Config.UseColdArmor) + ? Config.UseColdArmor + : (Precast.skills.coldArmor.best || -1)); - if (Precast.skills.coldArmor.tick > 0 && Precast.skills.coldArmor.duration > Time.seconds(45)) { - if (getTickCount() - Precast.skills.coldArmor.tick >= Precast.skills.coldArmor.duration - Time.seconds(30)) { - force = true; - } - } - switch (choosenSkill) { - case sdk.skills.FrozenArmor: - if (needToCast(sdk.states.FrozenArmor)) { - Precast.cast(sdk.skills.FrozenArmor) && (Precast.skills.coldArmor.tick = getTickCount()); - } - - break; - case sdk.skills.ChillingArmor: - if (needToCast(sdk.states.ChillingArmor)) { - Precast.cast(sdk.skills.ChillingArmor) && (Precast.skills.coldArmor.tick = getTickCount()); - } - - break; - case sdk.skills.ShiverArmor: - if (needToCast(sdk.states.ShiverArmor)) { - Precast.cast(sdk.skills.ShiverArmor) && (Precast.skills.coldArmor.tick = getTickCount()); - } - - break; - default: - break; - } - } - - if (Skill.canUse(sdk.skills.Enchant) && needToCast(sdk.states.Enchant)) { - this.enchant(); - } - - break; - case sdk.player.class.Necromancer: - if (Skill.canUse(sdk.skills.BoneArmor) + if (Precast.skills.coldArmor.tick > 0 && Precast.skills.coldArmor.duration > Time.seconds(45)) { + if (getTickCount() - Precast.skills.coldArmor.tick >= Precast.skills.coldArmor.duration - Time.seconds(30)) { + force = true; + } + } + switch (choosenSkill) { + case sdk.skills.FrozenArmor: + if (needToCast(sdk.states.FrozenArmor)) { + Precast.cast(sdk.skills.FrozenArmor) && (Precast.skills.coldArmor.tick = getTickCount()); + } + + break; + case sdk.skills.ChillingArmor: + if (needToCast(sdk.states.ChillingArmor)) { + Precast.cast(sdk.skills.ChillingArmor) && (Precast.skills.coldArmor.tick = getTickCount()); + } + + break; + case sdk.skills.ShiverArmor: + if (needToCast(sdk.states.ShiverArmor)) { + Precast.cast(sdk.skills.ShiverArmor) && (Precast.skills.coldArmor.tick = getTickCount()); + } + + break; + default: + break; + } + } + + if (Skill.canUse(sdk.skills.Enchant) && needToCast(sdk.states.Enchant)) { + this.enchant(); + } + + break; + case sdk.player.class.Necromancer: + if (Skill.canUse(sdk.skills.BoneArmor) && (force || this.skills.boneArmor.armorPercent() < 75 || !me.getState(sdk.states.BoneArmor))) { - this.cast(sdk.skills.BoneArmor); - this.skills.boneArmor.max === 0 && (this.skills.boneArmor.max = me.getStat(sdk.stats.SkillBoneArmorMax)); - } - - (() => { - switch (Config.Golem) { - case 1: - case "Clay": - return this.summon(sdk.skills.ClayGolem, sdk.summons.type.Golem); - case 2: - case "Blood": - return this.summon(sdk.skills.BloodGolem, sdk.summons.type.Golem); - case 3: - case "Fire": - return this.summon(sdk.skills.FireGolem, sdk.summons.type.Golem); - case 0: - case "None": - default: - return false; - } - })(); - - Config.ActiveSummon && ClassAttack.raiseArmy(); - - break; - case sdk.player.class.Paladin: - if (Skill.canUse(sdk.skills.HolyShield) && Precast.skills.holyShield.canUse && needToCast(sdk.states.HolyShield)) { - this.cast(sdk.skills.HolyShield); - } - - break; - case sdk.player.class.Barbarian: // - TODO: durations - if (!Config.UseWarcries) { - break; - } - let needShout = (Skill.canUse(sdk.skills.Shout) && needToCast(sdk.states.Shout)); - let needBo = (Skill.canUse(sdk.skills.BattleOrders) && needToCast(sdk.states.BattleOrders)); - let needBc = (Skill.canUse(sdk.skills.BattleCommand) && needToCast(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 && this.cast(sdk.skills.BattleCommand, x, y, true); - needBo && this.cast(sdk.skills.BattleOrders, x, y, true); - needShout && this.cast(sdk.skills.Shout, x, y, true); - - me.weaponswitch !== primary && me.switchWeapons(primary); - } - - break; - case sdk.player.class.Druid: - if (Skill.canUse(sdk.skills.CycloneArmor) && needToCast(sdk.states.CycloneArmor)) { - this.cast(sdk.skills.CycloneArmor); - } - - Skill.canUse(sdk.skills.Raven) && this.summon(sdk.skills.Raven, sdk.summons.type.Raven); - - buffSummons = (() => { - switch (Config.SummonAnimal) { - case 1: - case "Spirit Wolf": - return (this.summon(sdk.skills.SummonSpiritWolf, sdk.summons.type.SpiritWolf) || buffSummons); - case 2: - case "Dire Wolf": - return (this.summon(sdk.skills.SummonDireWolf, sdk.summons.type.DireWolf) || buffSummons); - case 3: - case "Grizzly": - return (this.summon(sdk.skills.SummonGrizzly, sdk.summons.type.Grizzly) || buffSummons); - default: - return buffSummons; - } - })(); - - buffSummons = (() => { - switch (Config.SummonVine) { - case 1: - case "Poison Creeper": - return (this.summon(sdk.skills.PoisonCreeper, sdk.summons.type.Vine) || buffSummons); - case 2: - case "Carrion Vine": - return (this.summon(sdk.skills.CarrionVine, sdk.summons.type.Vine) || buffSummons); - case 3: - case "Solar Creeper": - return (this.summon(sdk.skills.SolarCreeper, sdk.summons.type.Vine) || buffSummons); - default: - return buffSummons; - } - })(); - - buffSummons = (() => { - switch (Config.SummonSpirit) { - case 1: - case "Oak Sage": - // to prevent false chickens when we cast oak before getting bo-ed - if (me.hardcore && !me.getState(sdk.states.BattleOrders) && me.inTown) return buffSummons; - return (this.summon(sdk.skills.OakSage, sdk.summons.type.Spirit) || buffSummons); - case 2: - case "Heart of Wolverine": - return (this.summon(sdk.skills.HeartofWolverine, sdk.summons.type.Spirit) || buffSummons); - case 3: - case "Spirit of Barbs": - return this.summon(sdk.skills.SpiritofBarbs, sdk.summons.type.Spirit) || buffSummons; - default: - return buffSummons; - } - })(); - - if (Skill.canUse(sdk.skills.Hurricane) && needToCast(sdk.states.Hurricane)) { - this.cast(sdk.skills.Hurricane); - } - - break; - case sdk.player.class.Assassin: - if (Skill.canUse(sdk.skills.Fade) && needToCast(sdk.states.Fade)) { - this.cast(sdk.skills.Fade); - } - - if (Skill.canUse(sdk.skills.Venom) && needToCast(sdk.states.Venom)) { - this.cast(sdk.skills.Venom); - } - - if (Skill.canUse(sdk.skills.BladeShield) && needToCast(sdk.states.BladeShield)) { - this.cast(sdk.skills.BladeShield); - } - - if (!Config.UseFade && Skill.canUse(sdk.skills.BurstofSpeed) && needToCast(sdk.states.BurstofSpeed)) { - this.cast(sdk.skills.BurstofSpeed); - } - - buffSummons = (() => { - switch (Config.SummonShadow) { - case 1: - case "Warrior": - return this.summon(sdk.skills.ShadowWarrior, sdk.summons.type.Shadow); - case 2: - case "Master": - return this.summon(sdk.skills.ShadowMaster, sdk.summons.type.Shadow); - default: - return false; - } - })(); - - break; - } - - buffSummons && this.haveCTA > -1 && this.precastCTA(force); - me.switchWeapons(Attack.getPrimarySlot()); - - return true; - }, - - needOutOfTownCast: function () { - return Skill.canUse(sdk.skills.Shout) || Skill.canUse(sdk.skills.BattleOrders) || Precast.checkCTA(); - }, - - doRandomPrecast: function (force = false, goToWhenDone = undefined) { - let returnTo = (goToWhenDone && typeof goToWhenDone === "number" ? goToWhenDone : me.area); - - try { - // Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time and you can precast in town - if (Precast.needOutOfTownCast()) { - Pather.useWaypoint("random") && Precast.doPrecast(force); - } else { - Precast.doPrecast(force); - } - Pather.useWaypoint(returnTo); - } catch (e) { - console.error(e); - } finally { - if (me.area !== returnTo && (!Pather.useWaypoint(returnTo) || !Pather.useWaypoint(sdk.areas.townOf(me.area)))) { - Pather.journeyTo(returnTo); - } - } - - return (me.area === returnTo); - }, + this.cast(sdk.skills.BoneArmor); + this.skills.boneArmor.max === 0 && (this.skills.boneArmor.max = me.getStat(sdk.stats.SkillBoneArmorMax)); + } + + (() => { + switch (Config.Golem) { + case 1: + case "Clay": + return this.summon(sdk.skills.ClayGolem, sdk.summons.type.Golem); + case 2: + case "Blood": + return this.summon(sdk.skills.BloodGolem, sdk.summons.type.Golem); + case 3: + case "Fire": + return this.summon(sdk.skills.FireGolem, sdk.summons.type.Golem); + case 0: + case "None": + default: + return false; + } + })(); + + Config.ActiveSummon && ClassAttack.raiseArmy(); + + break; + case sdk.player.class.Paladin: + if (Skill.canUse(sdk.skills.HolyShield) + && Precast.skills.holyShield.canUse && needToCast(sdk.states.HolyShield)) { + this.cast(sdk.skills.HolyShield); + } + + break; + case sdk.player.class.Barbarian: // - TODO: durations + if (!Config.UseWarcries) { + break; + } + let needShout = (Skill.canUse(sdk.skills.Shout) && needToCast(sdk.states.Shout)); + let needBo = (Skill.canUse(sdk.skills.BattleOrders) && needToCast(sdk.states.BattleOrders)); + let needBc = (Skill.canUse(sdk.skills.BattleCommand) && needToCast(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 && this.cast(sdk.skills.BattleCommand, x, y, true); + needBo && this.cast(sdk.skills.BattleOrders, x, y, true); + needShout && this.cast(sdk.skills.Shout, x, y, true); + + me.weaponswitch !== primary && me.switchWeapons(primary); + } + + break; + case sdk.player.class.Druid: + if (Skill.canUse(sdk.skills.CycloneArmor) && needToCast(sdk.states.CycloneArmor)) { + this.cast(sdk.skills.CycloneArmor); + } + + Skill.canUse(sdk.skills.Raven) && this.summon(sdk.skills.Raven, sdk.summons.type.Raven); + + buffSummons = (() => { + switch (Config.SummonAnimal) { + case 1: + case "Spirit Wolf": + return (this.summon(sdk.skills.SummonSpiritWolf, sdk.summons.type.SpiritWolf) || buffSummons); + case 2: + case "Dire Wolf": + return (this.summon(sdk.skills.SummonDireWolf, sdk.summons.type.DireWolf) || buffSummons); + case 3: + case "Grizzly": + return (this.summon(sdk.skills.SummonGrizzly, sdk.summons.type.Grizzly) || buffSummons); + default: + return buffSummons; + } + })(); + + buffSummons = (() => { + switch (Config.SummonVine) { + case 1: + case "Poison Creeper": + return (this.summon(sdk.skills.PoisonCreeper, sdk.summons.type.Vine) || buffSummons); + case 2: + case "Carrion Vine": + return (this.summon(sdk.skills.CarrionVine, sdk.summons.type.Vine) || buffSummons); + case 3: + case "Solar Creeper": + return (this.summon(sdk.skills.SolarCreeper, sdk.summons.type.Vine) || buffSummons); + default: + return buffSummons; + } + })(); + + buffSummons = (() => { + switch (Config.SummonSpirit) { + case 1: + case "Oak Sage": + // to prevent false chickens when we cast oak before getting bo-ed + if (me.hardcore && !me.getState(sdk.states.BattleOrders) && me.inTown) return buffSummons; + return (this.summon(sdk.skills.OakSage, sdk.summons.type.Spirit) || buffSummons); + case 2: + case "Heart of Wolverine": + return (this.summon(sdk.skills.HeartofWolverine, sdk.summons.type.Spirit) || buffSummons); + case 3: + case "Spirit of Barbs": + return this.summon(sdk.skills.SpiritofBarbs, sdk.summons.type.Spirit) || buffSummons; + default: + return buffSummons; + } + })(); + + if (Skill.canUse(sdk.skills.Hurricane) && needToCast(sdk.states.Hurricane)) { + this.cast(sdk.skills.Hurricane); + } + + break; + case sdk.player.class.Assassin: + if (Skill.canUse(sdk.skills.Fade) && needToCast(sdk.states.Fade)) { + this.cast(sdk.skills.Fade); + } + + if (Skill.canUse(sdk.skills.Venom) && needToCast(sdk.states.Venom)) { + this.cast(sdk.skills.Venom); + } + + if (Skill.canUse(sdk.skills.BladeShield) && needToCast(sdk.states.BladeShield)) { + this.cast(sdk.skills.BladeShield); + } + + if (!Config.UseFade && Skill.canUse(sdk.skills.BurstofSpeed) && needToCast(sdk.states.BurstofSpeed)) { + this.cast(sdk.skills.BurstofSpeed); + } + + buffSummons = (() => { + switch (Config.SummonShadow) { + case 1: + case "Warrior": + return this.summon(sdk.skills.ShadowWarrior, sdk.summons.type.Shadow); + case 2: + case "Master": + return this.summon(sdk.skills.ShadowMaster, sdk.summons.type.Shadow); + default: + return false; + } + })(); + + break; + } + + buffSummons && this.haveCTA > -1 && this.precastCTA(force); + me.switchWeapons(Attack.getPrimarySlot()); + + return true; + }, + + needOutOfTownCast: function () { + return Skill.canUse(sdk.skills.Shout) || Skill.canUse(sdk.skills.BattleOrders) || Precast.checkCTA(); + }, + + doRandomPrecast: function (force = false, goToWhenDone = undefined) { + let returnTo = (goToWhenDone && typeof goToWhenDone === "number" ? goToWhenDone : me.area); + + try { + // Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time and you can precast in town + if (Precast.needOutOfTownCast()) { + Pather.useWaypoint("random") && Precast.doPrecast(force); + } else { + Precast.doPrecast(force); + } + Pather.useWaypoint(returnTo); + } catch (e) { + console.error(e); + } finally { + if (me.area !== returnTo && (!Pather.useWaypoint(returnTo) || !Pather.useWaypoint(sdk.areas.townOf(me.area)))) { + Pather.journeyTo(returnTo); + } + } + + return (me.area === returnTo); + }, }; diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 7bf46e22b..4bd61389b 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -11,669 +11,688 @@ !isIncluded("core/Me.js") && include("core/Me.js"); (function (global, original) { - let firstRun = true; - global.getUnit = function (...args) { - if (firstRun) { - delay(1500); - firstRun = false; - } - - // Stupid reference thing - // eslint-disable-next-line no-unused-vars - const test = original(-1); - - let [first] = args, second = args.length >= 2 ? args[1] : undefined; - - const ret = original.apply(this, args); - - // deal with bug - if (first === 1 && typeof second === "string" && ret - && ((me.act === 1 && ret.classid === sdk.monsters.Dummy1) || me.act === 2 && ret.classid === sdk.monsters.Dummy2)) { - return null; - } - - return original.apply(this, args); - }; + let firstRun = true; + global.getUnit = function (...args) { + if (firstRun) { + delay(1500); + firstRun = false; + } + + // Stupid reference thing + // eslint-disable-next-line no-unused-vars + const test = original(-1); + + let [first] = args, second = args.length >= 2 ? args[1] : undefined; + + const ret = original.apply(this, args); + + // deal with bug + if (first === 1 && typeof second === "string" && ret + && ((me.act === 1 && ret.classid === sdk.monsters.Dummy1) + || me.act === 2 && ret.classid === sdk.monsters.Dummy2)) { + return null; + } + + return original.apply(this, args); + }; })([].filter.constructor("return this")(), getUnit); // Check if party unit is in town Party.prototype.__defineGetter__("inTown", function () { - return sdk.areas.Towns.includes(this.area); + return sdk.areas.Towns.includes(this.area); }); Object.defineProperties(Unit.prototype, { - isChampion: { - get: function () { - return (this.spectype & sdk.monsters.spectype.Champion) > 0; - }, - }, - isUnique: { - get: function () { - return (this.spectype & sdk.monsters.spectype.Unique) > 0; - }, - }, - isMinion: { - get: function () { - return (this.spectype & sdk.monsters.spectype.Minion) > 0; - }, - }, - isSuperUnique: { - get: function () { - return (this.spectype & (sdk.monsters.spectype.Super | sdk.monsters.spectype.Unique)) > 0; - }, - }, - isSpecial: { - get: function () { - return (this.isChampion || this.isUnique || this.isSuperUnique); - }, - }, - isPlayer: { - get: function () { - return this.type === sdk.unittype.Player; - }, - }, - isMonster: { - get: function () { - return this.type === sdk.unittype.Monster; - }, - }, - isNPC: { - get: function () { - return this.type === sdk.unittype.Monster && this.getStat(sdk.stats.Alignment) === 2; - }, - }, - // todo - monster types - isPrimeEvil: { - get: function () { - return [ - sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, - sdk.monsters.Baal, sdk.monsters.BaalClone, sdk.monsters.UberDuriel, sdk.monsters.UberIzual, - sdk.monsters.UberMephisto, sdk.monsters.UberDiablo, sdk.monsters.UberBaal, sdk.monsters.Lilith, sdk.monsters.DiabloClone - ].includes(this.classid) || getBaseStat("monstats", this.classid, "primeevil"); - }, - }, - isBoss: { - get: function () { - return this.isPrimeEvil - || - [ - sdk.monsters.TheSmith, sdk.monsters.BloodRaven, sdk.monsters.Radament, sdk.monsters.Griswold, - sdk.monsters.TheSummoner, sdk.monsters.Izual, sdk.monsters.Hephasto, sdk.monsters.KorlictheProtector, - sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian, sdk.monsters.ListerTheTormenter, - sdk.monsters.TheCowKing, sdk.monsters.ColdwormtheBurrower, sdk.monsters.Nihlathak - ].includes(this.classid); - }, - }, - isGhost: { - get: function () { - return [ - sdk.monsters.Ghost1, sdk.monsters.Wraith1, sdk.monsters.Specter1, - sdk.monsters.Apparition, sdk.monsters.DarkShape, sdk.monsters.Ghost2, sdk.monsters.Wraith2, sdk.monsters.Specter2 - ].includes(this.classid) || getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Wraith; - }, - }, - isDoll: { - get: function () { - return [ - sdk.monsters.BoneFetish1, sdk.monsters.BoneFetish2, sdk.monsters.BoneFetish3, - sdk.monsters.SoulKiller3, sdk.monsters.StygianDoll2, sdk.monsters.StygianDoll6, sdk.monsters.SoulKiller - ].includes(this.classid); - }, - }, - isMonsterObject: { - get: function () { - return [ - sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, - sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, sdk.monsters.FireTower, - sdk.monsters.BarricadeDoor1, sdk.monsters.BarricadeDoor2, sdk.monsters.BarricadeWall1, sdk.monsters.BarricadeWall2, - sdk.monsters.CatapultS, sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, sdk.monsters.CatapultW, - sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.Hut, - ].includes(this.classid); - }, - }, - isMonsterEgg: { - get: function () { - return [ - sdk.monsters.SandMaggotEgg, sdk.monsters.RockWormEgg, sdk.monsters.DevourerEgg, sdk.monsters.GiantLampreyEgg, - sdk.monsters.WorldKillerEgg1, sdk.monsters.WorldKillerEgg2 - ].includes(this.classid); - }, - }, - isMonsterNest: { - get: function () { - return [ - sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, - sdk.monsters.BloodWingNest, sdk.monsters.CloudStalkerNest, sdk.monsters.FeederNest, sdk.monsters.SuckerNest - ].includes(this.classid); - }, - }, - isBaalTentacle: { - get: function () { - return [ - sdk.monsters.Tentacle1, sdk.monsters.Tentacle2, - sdk.monsters.Tentacle3, sdk.monsters.Tentacle4, sdk.monsters.Tentacle5 - ].includes(this.classid); - }, - }, - isShaman: { - get: function () { - return [ - sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, - sdk.monsters.WarpedShaman, sdk.monsters.CarverShaman, sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2 - ].includes(this.classid); - }, - }, - isUnraveler: { - get: function () { - return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Unraveler; - }, - }, - isFallen: { - get: function () { - return [ - sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, - sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2 - ].includes(this.classid); - }, - }, - isBeetle: { - get: function () { - return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Scarab; - }, - }, - isWalking: { - get: function () { - return (this.mode === sdk.monsters.mode.Walking && (this.targetx !== this.x || this.targety !== this.y)); - } - }, - isRunning: { - get: function () { - return (this.mode === sdk.monsters.mode.Running && (this.targetx !== this.x || this.targety !== this.y)); - } - }, - isMoving: { - get: function () { - return (this.isWalking || this.isRunning); - }, - }, - isFrozen: { - get: function () { - return this.getState(sdk.states.FrozenSolid); - }, - }, - isChilled: { - get: function () { - return this.getState(sdk.states.Frozen); - }, - }, - extraStrong: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ExtraStrong); - }, - }, - extraFast: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ExtraFast); - }, - }, - cursed: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.Cursed); - }, - }, - magicResistant: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.MagicResistant); - }, - }, - fireEnchanted: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.FireEnchanted); - }, - }, - lightningEnchanted: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.LightningEnchanted); - }, - }, - coldEnchanted: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ColdEnchanted); - }, - }, - manBurn: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.ManaBurn); - }, - }, - teleportation: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.Teleportation); - }, - }, - spectralHit: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.SpectralHit); - }, - }, - stoneSkin: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.StoneSkin); - }, - }, - multiShot: { - get: function () { - if (!this.isMonster) return false; - return this.getEnchant(sdk.enchant.MultipleShots); - }, - }, - resPenalty: { - value: me.classic ? [0, 20, 50][me.diff] : [0, 40, 100][me.diff], - writable: true - }, - fireRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResFire) && (modifier += 75); - } - return this.getStat(sdk.stats.FireResist) - me.resPenalty - modifier; - } - }, - coldRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResCold) && (modifier += 75); - me.getState(sdk.states.Thawing) && (modifier += 50); - } - return this.getStat(sdk.stats.ColdResist) - me.resPenalty - modifier; - } - }, - lightRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResLighting) && (modifier += 75); - } - return this.getStat(sdk.stats.LightResist) - me.resPenalty - modifier; - } - }, - poisonRes: { - get: function () { - let modifier = 0; - if (this === me) { - me.getState(sdk.states.ShrineResPoison) && (modifier += 75); - me.getState(sdk.states.Antidote) && (modifier += 50); - } - return this.getStat(sdk.stats.PoisonResist) - me.resPenalty - modifier; - } - }, - hpPercent: { - get: function () { - return Math.round(this.hp * 100 / this.hpmax); - } - }, - attacking: { - get: function () { - if (this.type > sdk.unittype.Monster) throw new Error("Unit.attacking: Must be used with Monster or Player units."); - switch (this.type) { - case sdk.unittype.Player: - return [ - sdk.player.mode.Attacking1, sdk.player.mode.Attacking2, sdk.player.mode.CastingSkill, sdk.player.mode.ThrowingItem, - sdk.player.mode.Kicking, sdk.player.mode.UsingSkill1, sdk.player.mode.UsingSkill2, sdk.player.mode.UsingSkill3, - sdk.player.mode.UsingSkill4, sdk.player.mode.SkillActionSequence - ].includes(this.mode); - case sdk.unittype.Monster: - return [ - sdk.monsters.mode.Attacking1, sdk.monsters.mode.Attacking2, sdk.monsters.mode.CastingSkill, - sdk.monsters.mode.UsingSkill1, sdk.monsters.mode.UsingSkill2, sdk.monsters.mode.UsingSkill3, sdk.monsters.mode.UsingSkill4 - ].includes(this.mode); - default: - return false; - } - } - }, - idle: { - get: function () { - if (this.type > sdk.unittype.Player) throw new Error("Unit.idle: Must be used with player units."); - // Dead is pretty idle too - return (this.mode === sdk.player.mode.StandingOutsideTown || this.mode === sdk.player.mode.StandingInTown || this.mode === sdk.player.mode.Dead); - } - }, - gold: { - get: function () { - return this.getStat(sdk.stats.Gold) + this.getStat(sdk.stats.GoldBank); - } - }, - dead: { - get: function () { - switch (this.type) { - case sdk.unittype.Player: - return this.mode === sdk.player.mode.Death || this.mode === sdk.player.mode.Dead; - case sdk.unittype.Monster: - return this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead; - default: - return false; - } - } - }, - inTown: { - get: function () { - if (this.type > sdk.unittype.Player) throw new Error("Unit.inTown: Must be used with player units."); - return sdk.areas.Towns.includes(this.area); - } - } + isChampion: { + get: function () { + return (this.spectype & sdk.monsters.spectype.Champion) > 0; + }, + }, + isUnique: { + get: function () { + return (this.spectype & sdk.monsters.spectype.Unique) > 0; + }, + }, + isMinion: { + get: function () { + return (this.spectype & sdk.monsters.spectype.Minion) > 0; + }, + }, + isSuperUnique: { + get: function () { + return (this.spectype & (sdk.monsters.spectype.Super | sdk.monsters.spectype.Unique)) > 0; + }, + }, + isSpecial: { + get: function () { + return (this.isChampion || this.isUnique || this.isSuperUnique); + }, + }, + isPlayer: { + get: function () { + return this.type === sdk.unittype.Player; + }, + }, + isMonster: { + get: function () { + return this.type === sdk.unittype.Monster; + }, + }, + isNPC: { + get: function () { + return this.type === sdk.unittype.Monster && this.getStat(sdk.stats.Alignment) === 2; + }, + }, + // todo - monster types + isPrimeEvil: { + get: function () { + return [ + sdk.monsters.Andariel, sdk.monsters.Duriel, sdk.monsters.Mephisto, + sdk.monsters.Diablo, sdk.monsters.Baal, sdk.monsters.BaalClone, + sdk.monsters.UberDuriel, sdk.monsters.UberIzual, sdk.monsters.UberMephisto, + sdk.monsters.UberDiablo, sdk.monsters.UberBaal, sdk.monsters.Lilith, sdk.monsters.DiabloClone + ].includes(this.classid) || getBaseStat("monstats", this.classid, "primeevil"); + }, + }, + isBoss: { + get: function () { + return this.isPrimeEvil + || [ + sdk.monsters.TheSmith, sdk.monsters.BloodRaven, sdk.monsters.Radament, sdk.monsters.Griswold, + sdk.monsters.TheSummoner, sdk.monsters.Izual, sdk.monsters.Hephasto, sdk.monsters.KorlictheProtector, + sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian, sdk.monsters.ListerTheTormenter, + sdk.monsters.TheCowKing, sdk.monsters.ColdwormtheBurrower, sdk.monsters.Nihlathak + ].includes(this.classid); + }, + }, + isGhost: { + get: function () { + return [ + sdk.monsters.Ghost1, sdk.monsters.Wraith1, sdk.monsters.Specter1, + sdk.monsters.Apparition, sdk.monsters.DarkShape, sdk.monsters.Ghost2, + sdk.monsters.Wraith2, sdk.monsters.Specter2 + ].includes(this.classid) || getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Wraith; + }, + }, + isDoll: { + get: function () { + return [ + sdk.monsters.BoneFetish1, sdk.monsters.BoneFetish2, sdk.monsters.BoneFetish3, + sdk.monsters.SoulKiller3, sdk.monsters.StygianDoll2, sdk.monsters.StygianDoll6, sdk.monsters.SoulKiller + ].includes(this.classid); + }, + }, + isMonsterObject: { + get: function () { + return [ + sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, + sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, sdk.monsters.FireTower, + sdk.monsters.BarricadeDoor1, sdk.monsters.BarricadeDoor2, + sdk.monsters.BarricadeWall1, sdk.monsters.BarricadeWall2, + sdk.monsters.CatapultS, sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, sdk.monsters.CatapultW, + sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.Hut, + ].includes(this.classid); + }, + }, + isMonsterEgg: { + get: function () { + return [ + sdk.monsters.SandMaggotEgg, sdk.monsters.RockWormEgg, sdk.monsters.DevourerEgg, sdk.monsters.GiantLampreyEgg, + sdk.monsters.WorldKillerEgg1, sdk.monsters.WorldKillerEgg2 + ].includes(this.classid); + }, + }, + isMonsterNest: { + get: function () { + return [ + sdk.monsters.FoulCrowNest, sdk.monsters.BlackVultureNest, + sdk.monsters.BloodHawkNest, sdk.monsters.BloodHookNest, + sdk.monsters.BloodWingNest, sdk.monsters.CloudStalkerNest, + sdk.monsters.FeederNest, sdk.monsters.SuckerNest + ].includes(this.classid); + }, + }, + isBaalTentacle: { + get: function () { + return [ + sdk.monsters.Tentacle1, sdk.monsters.Tentacle2, + sdk.monsters.Tentacle3, sdk.monsters.Tentacle4, sdk.monsters.Tentacle5 + ].includes(this.classid); + }, + }, + isShaman: { + get: function () { + return [ + sdk.monsters.FallenShaman, sdk.monsters.CarverShaman2, sdk.monsters.DevilkinShaman2, sdk.monsters.DarkShaman1, + sdk.monsters.WarpedShaman, sdk.monsters.CarverShaman, sdk.monsters.DevilkinShaman, sdk.monsters.DarkShaman2 + ].includes(this.classid); + }, + }, + isUnraveler: { + get: function () { + return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Unraveler; + }, + }, + isFallen: { + get: function () { + return [ + sdk.monsters.Fallen, sdk.monsters.Carver2, sdk.monsters.Devilkin2, + sdk.monsters.DarkOne1, sdk.monsters.WarpedFallen, + sdk.monsters.Carver1, sdk.monsters.Devilkin, sdk.monsters.DarkOne2 + ].includes(this.classid); + }, + }, + isBeetle: { + get: function () { + return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Scarab; + }, + }, + isWalking: { + get: function () { + return (this.mode === sdk.monsters.mode.Walking && (this.targetx !== this.x || this.targety !== this.y)); + } + }, + isRunning: { + get: function () { + return (this.mode === sdk.monsters.mode.Running && (this.targetx !== this.x || this.targety !== this.y)); + } + }, + isMoving: { + get: function () { + return (this.isWalking || this.isRunning); + }, + }, + isFrozen: { + get: function () { + return this.getState(sdk.states.FrozenSolid); + }, + }, + isChilled: { + get: function () { + return this.getState(sdk.states.Frozen); + }, + }, + extraStrong: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ExtraStrong); + }, + }, + extraFast: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ExtraFast); + }, + }, + cursed: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.Cursed); + }, + }, + magicResistant: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.MagicResistant); + }, + }, + fireEnchanted: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.FireEnchanted); + }, + }, + lightningEnchanted: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.LightningEnchanted); + }, + }, + coldEnchanted: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ColdEnchanted); + }, + }, + manBurn: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.ManaBurn); + }, + }, + teleportation: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.Teleportation); + }, + }, + spectralHit: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.SpectralHit); + }, + }, + stoneSkin: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.StoneSkin); + }, + }, + multiShot: { + get: function () { + if (!this.isMonster) return false; + return this.getEnchant(sdk.enchant.MultipleShots); + }, + }, + resPenalty: { + value: me.classic ? [0, 20, 50][me.diff] : [0, 40, 100][me.diff], + writable: true + }, + fireRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResFire) && (modifier += 75); + } + return this.getStat(sdk.stats.FireResist) - me.resPenalty - modifier; + } + }, + coldRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResCold) && (modifier += 75); + me.getState(sdk.states.Thawing) && (modifier += 50); + } + return this.getStat(sdk.stats.ColdResist) - me.resPenalty - modifier; + } + }, + lightRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResLighting) && (modifier += 75); + } + return this.getStat(sdk.stats.LightResist) - me.resPenalty - modifier; + } + }, + poisonRes: { + get: function () { + let modifier = 0; + if (this === me) { + me.getState(sdk.states.ShrineResPoison) && (modifier += 75); + me.getState(sdk.states.Antidote) && (modifier += 50); + } + return this.getStat(sdk.stats.PoisonResist) - me.resPenalty - modifier; + } + }, + hpPercent: { + get: function () { + return Math.round(this.hp * 100 / this.hpmax); + } + }, + attacking: { + get: function () { + if (this.type > sdk.unittype.Monster) { + throw new Error("Unit.attacking: Must be used with Monster or Player units."); + } + switch (this.type) { + case sdk.unittype.Player: + return [ + sdk.player.mode.Attacking1, sdk.player.mode.Attacking2, + sdk.player.mode.CastingSkill, sdk.player.mode.ThrowingItem, + sdk.player.mode.Kicking, sdk.player.mode.UsingSkill1, + sdk.player.mode.UsingSkill2, sdk.player.mode.UsingSkill3, + sdk.player.mode.UsingSkill4, sdk.player.mode.SkillActionSequence + ].includes(this.mode); + case sdk.unittype.Monster: + return [ + sdk.monsters.mode.Attacking1, sdk.monsters.mode.Attacking2, + sdk.monsters.mode.CastingSkill, sdk.monsters.mode.UsingSkill1, + sdk.monsters.mode.UsingSkill2, sdk.monsters.mode.UsingSkill3, sdk.monsters.mode.UsingSkill4 + ].includes(this.mode); + default: + return false; + } + } + }, + idle: { + get: function () { + if (this.type > sdk.unittype.Player) throw new Error("Unit.idle: Must be used with player units."); + // Dead is pretty idle too + return (this.mode === sdk.player.mode.StandingOutsideTown + || this.mode === sdk.player.mode.StandingInTown || this.mode === sdk.player.mode.Dead); + } + }, + gold: { + get: function () { + return this.getStat(sdk.stats.Gold) + this.getStat(sdk.stats.GoldBank); + } + }, + dead: { + get: function () { + switch (this.type) { + case sdk.unittype.Player: + return this.mode === sdk.player.mode.Death || this.mode === sdk.player.mode.Dead; + case sdk.unittype.Monster: + return this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead; + default: + return false; + } + } + }, + inTown: { + get: function () { + if (this.type > sdk.unittype.Player) throw new Error("Unit.inTown: Must be used with player units."); + return sdk.areas.Towns.includes(this.area); + } + } }); /** * @extends ItemUnit */ Object.defineProperties(Unit.prototype, { - isEquipped: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Equipped; - } - }, - isEquippedCharm: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return (this.location === sdk.storage.Inventory && [sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(this.itemType)); - } - }, - isInInventory: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Inventory && this.mode === sdk.items.mode.inStorage; - } - }, - isInStash: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Stash && this.mode === sdk.items.mode.inStorage; - } - }, - isInCube: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Cube && this.mode === sdk.items.mode.inStorage; - } - }, - isInStorage: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.mode === sdk.items.mode.inStorage && [sdk.storage.Inventory, sdk.storage.Cube, sdk.storage.Stash].includes(this.location); - } - }, - isInBelt: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.location === sdk.storage.Belt && this.mode === sdk.items.mode.inBelt; - } - }, - isOnMain: { - get: function () { - if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; - return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); - } - }, - isOnSwap: { - get: function () { - if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; - switch (me.weaponswitch) { - case sdk.player.slot.Main: - return [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(this.bodylocation); - case sdk.player.slot.Secondary: - return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); - } - return false; - } - }, - identified: { - get: function () { - // Can't tell, as it isn't an item - if (this.type !== sdk.unittype.Item) return undefined; - // Is also true for white items - return this.getFlag(sdk.items.flags.Identified); - } - }, - ethereal: { - get: function () { - // Can't tell, as it isn't an item - if (this.type !== sdk.unittype.Item) return undefined; - return this.getFlag(sdk.items.flags.Ethereal); - } - }, - twoHanded: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return getBaseStat("items", this.classid, "2handed") === 1; - } - }, - oneOrTwoHanded: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return getBaseStat("items", this.classid, "1or2handed") === 1; - } - }, - strictlyTwoHanded: { - get: function () { - return this.twoHanded && !this.oneOrTwoHanded; - } - }, - runeword: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return !!this.getFlag(sdk.items.flags.Runeword); - } - }, - questItem: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return (this.itemType === sdk.items.type.Quest - || [ - sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, - sdk.items.quest.ViperAmulet, sdk.items.quest.DecoyGidbinn, sdk.items.quest.TheGidbinn, sdk.items.quest.KhalimsFlail, - sdk.items.quest.KhalimsWill, sdk.items.quest.HellForgeHammer, sdk.items.quest.StandardofHeroes - ].includes(this.classid)); - } - }, - sellable: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - if (this.getItemCost(sdk.items.cost.ToSell) <= 1) return false; - return (!this.questItem - && [ - sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction, sdk.items.quest.DiablosHorn, - sdk.items.quest.BaalsEye, sdk.items.quest.MephistosBrain, sdk.items.quest.TokenofAbsolution, sdk.items.quest.TwistedEssenceofSuffering, - sdk.items.quest.ChargedEssenceofHatred, sdk.items.quest.BurningEssenceofTerror, sdk.items.quest.FesteringEssenceofDestruction - ].indexOf(this.classid) === -1); - } - }, - lowquality: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.LowQuality; - }, - }, - normal: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Normal; - }, - }, - superior: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Superior; - }, - }, - magic: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Magic; - }, - }, - set: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Set; - }, - }, - rare: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Rare; - }, - }, - unique: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Unique; - }, - }, - crafted: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.quality === sdk.items.quality.Crafted; - }, - }, - sockets: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.getStat(sdk.stats.NumSockets); - }, - }, - onGroundOrDropping: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return (this.mode === sdk.items.mode.onGround || this.mode === sdk.items.mode.Dropping); - }, - }, - isShield: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads].includes(this.itemType); - }, - }, - isAnni: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.unique && this.itemType === sdk.items.type.SmallCharm; - }, - }, - isTorch: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.unique && this.itemType === sdk.items.type.LargeCharm; - }, - }, - isGheeds: { - get: function () { - if (this.type !== sdk.unittype.Item) return false; - return this.unique && this.itemType === sdk.items.type.GrandCharm; - }, - }, - prettyPrint: { - /** @this {ItemUnit} */ - get: function () { - if (this.type !== sdk.unittype.Item) return this.name; - if (this.fname === undefined) return typeof this.name === "string" ? this.name : "undefined"; - return this.fname.split("\n").reverse().join(" "); - } - }, - durabilityPercent: { - get: function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.durabilityPercent: Must be used on items."); - if (this.getStat(sdk.stats.Quantity) || !this.getStat(sdk.stats.MaxDurability)) return 100; - return Math.round(this.getStat(sdk.stats.Durability) * 100 / this.getStat(sdk.stats.MaxDurability)); - } - }, + isEquipped: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Equipped; + } + }, + isEquippedCharm: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return (this.location === sdk.storage.Inventory + && [sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(this.itemType)); + } + }, + isInInventory: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Inventory && this.mode === sdk.items.mode.inStorage; + } + }, + isInStash: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Stash && this.mode === sdk.items.mode.inStorage; + } + }, + isInCube: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Cube && this.mode === sdk.items.mode.inStorage; + } + }, + isInStorage: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.mode === sdk.items.mode.inStorage + && [sdk.storage.Inventory, sdk.storage.Cube, sdk.storage.Stash].includes(this.location); + } + }, + isInBelt: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.location === sdk.storage.Belt && this.mode === sdk.items.mode.inBelt; + } + }, + isOnMain: { + get: function () { + if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; + return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); + } + }, + isOnSwap: { + get: function () { + if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; + switch (me.weaponswitch) { + case sdk.player.slot.Main: + return [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(this.bodylocation); + case sdk.player.slot.Secondary: + return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); + } + return false; + } + }, + identified: { + get: function () { + // Can't tell, as it isn't an item + if (this.type !== sdk.unittype.Item) return undefined; + // Is also true for white items + return this.getFlag(sdk.items.flags.Identified); + } + }, + ethereal: { + get: function () { + // Can't tell, as it isn't an item + if (this.type !== sdk.unittype.Item) return undefined; + return this.getFlag(sdk.items.flags.Ethereal); + } + }, + twoHanded: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return getBaseStat("items", this.classid, "2handed") === 1; + } + }, + oneOrTwoHanded: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return getBaseStat("items", this.classid, "1or2handed") === 1; + } + }, + strictlyTwoHanded: { + get: function () { + return this.twoHanded && !this.oneOrTwoHanded; + } + }, + runeword: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return !!this.getFlag(sdk.items.flags.Runeword); + } + }, + questItem: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return (this.itemType === sdk.items.type.Quest + || [ + sdk.items.quest.HoradricMalus, sdk.items.quest.WirtsLeg, + sdk.items.quest.HoradricStaff, sdk.items.quest.ShaftoftheHoradricStaff, + sdk.items.quest.ViperAmulet, sdk.items.quest.DecoyGidbinn, + sdk.items.quest.TheGidbinn, sdk.items.quest.KhalimsFlail, + sdk.items.quest.KhalimsWill, sdk.items.quest.HellForgeHammer, sdk.items.quest.StandardofHeroes + ].includes(this.classid)); + } + }, + sellable: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + if (this.getItemCost(sdk.items.cost.ToSell) <= 1) return false; + return (!this.questItem + && [ + sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, + sdk.items.quest.KeyofDestruction, sdk.items.quest.DiablosHorn, + sdk.items.quest.BaalsEye, sdk.items.quest.MephistosBrain, + sdk.items.quest.TokenofAbsolution, sdk.items.quest.TwistedEssenceofSuffering, + sdk.items.quest.ChargedEssenceofHatred, sdk.items.quest.BurningEssenceofTerror, + sdk.items.quest.FesteringEssenceofDestruction + ].indexOf(this.classid) === -1); + } + }, + lowquality: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.LowQuality; + }, + }, + normal: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Normal; + }, + }, + superior: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Superior; + }, + }, + magic: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Magic; + }, + }, + set: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Set; + }, + }, + rare: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Rare; + }, + }, + unique: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Unique; + }, + }, + crafted: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.quality === sdk.items.quality.Crafted; + }, + }, + sockets: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.getStat(sdk.stats.NumSockets); + }, + }, + onGroundOrDropping: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return (this.mode === sdk.items.mode.onGround || this.mode === sdk.items.mode.Dropping); + }, + }, + isShield: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads].includes(this.itemType); + }, + }, + isAnni: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.unique && this.itemType === sdk.items.type.SmallCharm; + }, + }, + isTorch: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.unique && this.itemType === sdk.items.type.LargeCharm; + }, + }, + isGheeds: { + get: function () { + if (this.type !== sdk.unittype.Item) return false; + return this.unique && this.itemType === sdk.items.type.GrandCharm; + }, + }, + prettyPrint: { + /** @this {ItemUnit} */ + get: function () { + if (this.type !== sdk.unittype.Item) return this.name; + if (this.fname === undefined) return typeof this.name === "string" ? this.name : "undefined"; + return this.fname.split("\n").reverse().join(" "); + } + }, + durabilityPercent: { + get: function () { + if (this.type !== sdk.unittype.Item) throw new Error("Unit.durabilityPercent: Must be used on items."); + if (this.getStat(sdk.stats.Quantity) || !this.getStat(sdk.stats.MaxDurability)) return 100; + return Math.round(this.getStat(sdk.stats.Durability) * 100 / this.getStat(sdk.stats.MaxDurability)); + } + }, }); // Open NPC menu Unit.prototype.openMenu = function (addDelay) { - if (Config.PacketShopping) return Packet.openMenu(this); - if (this.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); - if (getUIFlag(sdk.uiflags.NPCMenu)) return true; + if (Config.PacketShopping) return Packet.openMenu(this); + if (this.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); + if (getUIFlag(sdk.uiflags.NPCMenu)) return true; - addDelay === undefined && (addDelay = 0); - let pingDelay = (me.gameReady ? me.ping : 125); + addDelay === undefined && (addDelay = 0); + let pingDelay = (me.gameReady ? me.ping : 125); - for (let i = 0; i < 5; i += 1) { - getDistance(me, this) > 4 && Pather.moveToUnit(this); + for (let i = 0; i < 5; i += 1) { + getDistance(me, this) > 4 && Pather.moveToUnit(this); - Misc.click(0, 0, this); - let tick = getTickCount(); + Misc.click(0, 0, this); + let tick = getTickCount(); - while (getTickCount() - tick < 5000) { - if (getUIFlag(sdk.uiflags.NPCMenu)) { - delay(Math.max(700 + pingDelay, 500 + pingDelay * 2 + addDelay * 500)); + while (getTickCount() - tick < 5000) { + if (getUIFlag(sdk.uiflags.NPCMenu)) { + delay(Math.max(700 + pingDelay, 500 + pingDelay * 2 + addDelay * 500)); - return true; - } + return true; + } - if (getInteractedNPC() && getTickCount() - tick > 1000) { - me.cancel(); - } + if (getInteractedNPC() && getTickCount() - tick > 1000) { + me.cancel(); + } - delay(100); - } + delay(100); + } - sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, this.gid); - delay(pingDelay * 2 + 1); - Packet.cancelNPC(this); - delay(pingDelay * 2 + 1); - Packet.flash(me.gid); - } + sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, this.gid); + delay(pingDelay * 2 + 1); + Packet.cancelNPC(this); + delay(pingDelay * 2 + 1); + Packet.flash(me.gid); + } - return false; + return false; }; // mode = "Gamble", "Repair" or "Shop" Unit.prototype.startTrade = function (mode) { - if (Config.PacketShopping) return Packet.startTrade(this, mode); - if (this.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); - console.log("Starting " + mode + " at " + this.name); - if (getUIFlag(sdk.uiflags.Shop)) return true; + if (Config.PacketShopping) return Packet.startTrade(this, mode); + if (this.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); + console.log("Starting " + mode + " at " + this.name); + if (getUIFlag(sdk.uiflags.Shop)) return true; - let menuId = mode === "Gamble" ? sdk.menu.Gamble : mode === "Repair" ? sdk.menu.TradeRepair : sdk.menu.Trade; + let menuId = mode === "Gamble" ? sdk.menu.Gamble : mode === "Repair" ? sdk.menu.TradeRepair : sdk.menu.Trade; - for (let i = 0; i < 3; i += 1) { - // Incremental delay on retries - if (this.openMenu(i)) { - Misc.useMenu(menuId); + for (let i = 0; i < 3; i += 1) { + // Incremental delay on retries + if (this.openMenu(i)) { + Misc.useMenu(menuId); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 1000) { - if (getUIFlag(sdk.uiflags.Shop) && this.itemcount > 0) { - delay(200); - console.log("Successfully started " + mode + " at " + this.name); + while (getTickCount() - tick < 1000) { + if (getUIFlag(sdk.uiflags.Shop) && this.itemcount > 0) { + delay(200); + console.log("Successfully started " + mode + " at " + this.name); - return true; - } + return true; + } - delay(25); - } + delay(25); + } - me.cancel(); - } - } + me.cancel(); + } + } - return false; + return false; }; /** @@ -681,220 +700,228 @@ Unit.prototype.startTrade = function (mode) { * @todo improve this, at the moment it's here as more of a proof of concept */ Unit.prototype.repairItem = function () { - // lets check if we have and can afford to repair this item - if (me.gold < this.getItemCost(2)) return false; - let npc = getInteractedNPC(); - if (!npc || npc.name.toLowerCase() !== Town.tasks[me.act - 1].Repair) return false; - // if (!this.startTrade("Repair")) return false; - let preDurability = this.getStat(sdk.stats.Durability); - new PacketBuilder().byte(0x35).dword(npc.gid).dword(this.gid).dword(1/* 1 for single item | 0 for all*/).dword(0).send(); - return Misc.poll(() => this.getStat(sdk.stats.Durability) !== preDurability, 500, 50); + // lets check if we have and can afford to repair this item + if (me.gold < this.getItemCost(2)) return false; + let npc = getInteractedNPC(); + if (!npc || npc.name.toLowerCase() !== Town.tasks[me.act - 1].Repair) return false; + // if (!this.startTrade("Repair")) return false; + let preDurability = this.getStat(sdk.stats.Durability); + new PacketBuilder() + .byte(0x35) + .dword(npc.gid) + .dword(this.gid) + .dword(1/* 1 for single item | 0 for all*/) + .dword(0) + .send(); + return Misc.poll(() => this.getStat(sdk.stats.Durability) !== preDurability, 500, 50); }; Unit.prototype.buy = function (shiftBuy, gamble) { - if (Config.PacketShopping) return Packet.buyItem(this, shiftBuy, gamble); - // Check if it's an item we want to buy - if (this.type !== sdk.unittype.Item) throw new Error("Unit.buy: Must be used on items."); + if (Config.PacketShopping) return Packet.buyItem(this, shiftBuy, gamble); + // Check if it's an item we want to buy + if (this.type !== sdk.unittype.Item) throw new Error("Unit.buy: Must be used on items."); - // Check if it's an item belonging to a NPC - if (!getUIFlag(sdk.uiflags.Shop) || (this.getParent() && this.getParent().gid !== getInteractedNPC().gid)) { - throw new Error("Unit.buy: Must be used in shops."); - } + // Check if it's an item belonging to a NPC + if (!getUIFlag(sdk.uiflags.Shop) || (this.getParent() && this.getParent().gid !== getInteractedNPC().gid)) { + throw new Error("Unit.buy: Must be used in shops."); + } - // Can we afford the item? - if (me.gold < this.getItemCost(sdk.items.cost.ToBuy)) return false; + // Can we afford the item? + if (me.gold < this.getItemCost(sdk.items.cost.ToBuy)) return false; - let oldGold = me.gold; - let itemCount = me.itemcount; + let oldGold = me.gold; + let itemCount = me.itemcount; - for (let i = 0; i < 3; i += 1) { - this.shop(shiftBuy ? 6 : 2); + for (let i = 0; i < 3; i += 1) { + this.shop(shiftBuy ? 6 : 2); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { - if ((shiftBuy && me.gold < oldGold) || itemCount !== me.itemcount) { - delay(500); + while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { + if ((shiftBuy && me.gold < oldGold) || itemCount !== me.itemcount) { + delay(500); - return true; - } + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; + return false; }; // Item owner name Unit.prototype.__defineGetter__("parentName", - function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.parentName: Must be used with item units."); + function () { + if (this.type !== sdk.unittype.Item) throw new Error("Unit.parentName: Must be used with item units."); - let parent = this.getParent(); + let parent = this.getParent(); - return parent ? parent.name : false; - }); + return parent ? parent.name : false; + }); // You MUST use a delay after Unit.sell() if using custom scripts. delay(500) works best, dynamic delay is used when identifying/selling (500 - item id time) Unit.prototype.sell = function () { - if (Config.PacketShopping) return Packet.sellItem(this); + if (Config.PacketShopping) return Packet.sellItem(this); - // Check if it's an item we want to buy - if (this.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); - if (!this.sellable) { - console.error((new Error("Item is unsellable"))); - return false; - } + // Check if it's an item we want to buy + if (this.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); + if (!this.sellable) { + console.error((new Error("Item is unsellable"))); + return false; + } - // Check if it's an item belonging to a NPC - if (!getUIFlag(sdk.uiflags.Shop)) throw new Error("Unit.sell: Must be used in shops."); + // Check if it's an item belonging to a NPC + if (!getUIFlag(sdk.uiflags.Shop)) throw new Error("Unit.sell: Must be used in shops."); - let itemCount = me.itemcount; + let itemCount = me.itemcount; - for (let i = 0; i < 5; i += 1) { - this.shop(1); + for (let i = 0; i < 5; i += 1) { + this.shop(1); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 2000) { - if (me.itemcount !== itemCount) { - //delay(500); + while (getTickCount() - tick < 2000) { + if (me.itemcount !== itemCount) { + //delay(500); - return true; - } + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; + return false; }; Unit.prototype.toCursor = function (usePacket = false) { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.toCursor: Must be used with items."); - if (me.itemoncursor && this.mode === sdk.items.mode.onCursor) return true; + if (this.type !== sdk.unittype.Item) throw new Error("Unit.toCursor: Must be used with items."); + if (me.itemoncursor && this.mode === sdk.items.mode.onCursor) return true; - this.location === sdk.storage.Stash && Town.openStash(); - this.location === sdk.storage.Cube && Cubing.openCube(); + this.location === sdk.storage.Stash && Town.openStash(); + this.location === sdk.storage.Cube && Cubing.openCube(); - if (usePacket) return Packet.itemToCursor(this); + if (usePacket) return Packet.itemToCursor(this); - for (let i = 0; i < 3; i += 1) { - try { - if (this.mode === sdk.items.mode.Equipped) { - // fix for equipped items (cubing viper staff for example) - clickItem(sdk.clicktypes.click.item.Left, this.bodylocation); - } else { - clickItem(sdk.clicktypes.click.item.Left, this); - } - } catch (e) { - return false; - } + for (let i = 0; i < 3; i += 1) { + try { + if (this.mode === sdk.items.mode.Equipped) { + // fix for equipped items (cubing viper staff for example) + clickItem(sdk.clicktypes.click.item.Left, this.bodylocation); + } else { + clickItem(sdk.clicktypes.click.item.Left, this); + } + } catch (e) { + return false; + } - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 1000) { - if (me.itemoncursor) { - delay(200); + while (getTickCount() - tick < 1000) { + if (me.itemoncursor) { + delay(200); - return true; - } + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; + return false; }; Unit.prototype.drop = function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.drop: Must be used with items. Unit Name: " + this.name); - if (!this.toCursor()) return false; + if (this.type !== sdk.unittype.Item) throw new Error("Unit.drop: Must be used with items. Unit Name: " + this.name); + if (!this.toCursor()) return false; - let tick = getTickCount(); - let timeout = Math.max(1000, me.ping * 6); + let tick = getTickCount(); + let timeout = Math.max(1000, me.ping * 6); - while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash) || !me.gameReady) { - if (getTickCount() - tick > timeout) return false; + while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash) || !me.gameReady) { + if (getTickCount() - tick > timeout) return false; - if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { - me.cancel(0); - } + if (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { + me.cancel(0); + } - delay(me.ping * 2 + 100); - } + delay(me.ping * 2 + 100); + } - for (let i = 0; i < 3; i += 1) { - clickMap(0, 0, me.x, me.y); - delay(40); - clickMap(2, 0, me.x, me.y); + for (let i = 0; i < 3; i += 1) { + clickMap(0, 0, me.x, me.y); + delay(40); + clickMap(2, 0, me.x, me.y); - tick = getTickCount(); + tick = getTickCount(); - while (getTickCount() - tick < 500) { - if (!me.itemoncursor) { - delay(200); + while (getTickCount() - tick < 500) { + if (!me.itemoncursor) { + delay(200); - return true; - } + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; + return false; }; /** * @description use consumable item, fixes issue with interact() returning false even if we used an item */ Unit.prototype.use = function () { - if (this === undefined || !this.type) return false; - if (this.type !== sdk.unittype.Item) throw new Error("Unit.use: Must be used with items. Unit Name: " + this.name); - if (!getBaseStat("items", this.classid, "useable")) throw new Error("Unit.use: Must be used with consumable items. Unit Name: " + this.name); - - let gid = this.gid; - let pingDelay = me.getPingDelay(); - let quantity = 0; - let iType = this.itemType; - let checkQuantity = false; - - if (me.mode === sdk.player.mode.SkillActionSequence) { - while (me.mode === sdk.player.mode.SkillActionSequence) { - delay (25); - } - } - - // make sure we don't have anything on cursor - if (me.itemoncursor) { - if (!Game.getCursorUnit().drop()) return false; - } - - switch (this.location) { - case sdk.storage.Stash: - case sdk.storage.Inventory: - if (this.isInStash && !Town.openStash()) return false; - // doesn't work, not sure why but it's missing something - //new PacketBuilder().byte(sdk.packets.send.UseItem).dword(gid).dword(this.x).dword(this.y).send(); - checkQuantity = iType === sdk.items.type.Book; - checkQuantity && (quantity = this.getStat(sdk.stats.Quantity)); - this.interact(); // use interact instead, was hoping to skip this since its really just doing the same thing over but oh well - - break; - case sdk.storage.Belt: - new PacketBuilder().byte(sdk.packets.send.UseBeltItem).dword(gid).dword(0).dword(0).send(); - - break; - default: - return false; - } - - if (checkQuantity) { - return Misc.poll(() => this.getStat(sdk.stats.Quantity) < quantity, 200 + pingDelay, 50); - } else { - return Misc.poll(() => !Game.getItem(-1, -1, gid), 200 + pingDelay, 50); - } + if (this === undefined || !this.type) return false; + if (this.type !== sdk.unittype.Item) throw new Error("Unit.use: Must be used with items. Unit Name: " + this.name); + if (!getBaseStat("items", this.classid, "useable")) { + throw new Error("Unit.use: Must be used with consumable items. Unit Name: " + this.name); + } + + let gid = this.gid; + let pingDelay = me.getPingDelay(); + let quantity = 0; + let iType = this.itemType; + let checkQuantity = false; + + if (me.mode === sdk.player.mode.SkillActionSequence) { + while (me.mode === sdk.player.mode.SkillActionSequence) { + delay (25); + } + } + + // make sure we don't have anything on cursor + if (me.itemoncursor) { + if (!Game.getCursorUnit().drop()) return false; + } + + switch (this.location) { + case sdk.storage.Stash: + case sdk.storage.Inventory: + if (this.isInStash && !Town.openStash()) return false; + // doesn't work, not sure why but it's missing something + //new PacketBuilder().byte(sdk.packets.send.UseItem).dword(gid).dword(this.x).dword(this.y).send(); + checkQuantity = iType === sdk.items.type.Book; + checkQuantity && (quantity = this.getStat(sdk.stats.Quantity)); + this.interact(); // use interact instead, was hoping to skip this since its really just doing the same thing over but oh well + + break; + case sdk.storage.Belt: + new PacketBuilder().byte(sdk.packets.send.UseBeltItem).dword(gid).dword(0).dword(0).send(); + + break; + default: + return false; + } + + if (checkQuantity) { + return Misc.poll(() => this.getStat(sdk.stats.Quantity) < quantity, 200 + pingDelay, 50); + } else { + return Misc.poll(() => !Game.getItem(-1, -1, gid), 200 + pingDelay, 50); + } }; /** @@ -912,46 +939,50 @@ Unit.prototype.use = function () { * @returns Unit[] */ Unit.prototype.checkItem = function (itemInfo) { - if (this === undefined || this.type > 1 || typeof itemInfo !== "object") return { have: false, item: null }; - - const itemObj = Object.assign({}, { - classid: -1, - itemtype: -1, - quality: -1, - runeword: null, - ethereal: null, - equipped: null, - basetype: null, - name: "" - }, itemInfo); - - // convert id into string - typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - - let items = this.getItemsEx() - .filter(function (item) { - return (!item.questItem - && (itemObj.classid === -1 || item.classid === itemObj.classid) - && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) - && (itemObj.quality === -1 || item.quality === itemObj.quality) - && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) - && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) - && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) - && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) - && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) - ); - }); - if (items.length > 0) { - return { - have: true, - item: copyUnit(items.first()) - }; - } else { - return { - have: false, - item: null - }; - } + if (this === undefined || this.type > 1 || typeof itemInfo !== "object") return { have: false, item: null }; + + const itemObj = Object.assign({}, { + classid: -1, + itemtype: -1, + quality: -1, + runeword: null, + ethereal: null, + equipped: null, + basetype: null, + name: "" + }, itemInfo); + + // convert id into string + typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); + + let items = this.getItemsEx() + .filter(function (item) { + return (!item.questItem + && (itemObj.classid === -1 || item.classid === itemObj.classid) + && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) + && (itemObj.quality === -1 || item.quality === itemObj.quality) + && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) + && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) + && (itemObj.equipped === null || ( + typeof itemObj.equipped === "number" + ? item.bodylocation === itemObj.equipped + : item.isEquipped === itemObj.equipped) + ) + && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) + && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) + ); + }); + if (items.length > 0) { + return { + have: true, + item: copyUnit(items.first()) + }; + } else { + return { + have: false, + item: null + }; + } }; /** @@ -969,48 +1000,52 @@ Unit.prototype.checkItem = function (itemInfo) { * @returns Unit[] */ Unit.prototype.findFirst = function (itemInfo = []) { - if (this === undefined || this.type > 1) return { have: false, item: null }; - if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return { have: false, item: null }; - let itemList = this.getItemsEx(); - - for (let i = 0; i < itemInfo.length; i++) { - const itemObj = Object.assign({}, { - classid: -1, - itemtype: -1, - quality: -1, - runeword: null, - ethereal: null, - equipped: null, - name: "" - }, itemInfo[i]); - - // convert id into string - typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - - let items = itemList - .filter(function (item) { - return (!item.questItem - && (itemObj.classid === -1 || item.classid === itemObj.classid) - && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) - && (itemObj.quality === -1 || item.quality === itemObj.quality) - && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) - && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) - && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) - && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) - ); - }); - if (items.length > 0) { - return { - have: true, - item: copyUnit(items.first()) - }; - } - } - - return { - have: false, - item: null - }; + if (this === undefined || this.type > 1) return { have: false, item: null }; + if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return { have: false, item: null }; + let itemList = this.getItemsEx(); + + for (let i = 0; i < itemInfo.length; i++) { + const itemObj = Object.assign({}, { + classid: -1, + itemtype: -1, + quality: -1, + runeword: null, + ethereal: null, + equipped: null, + name: "" + }, itemInfo[i]); + + // convert id into string + typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); + + let items = itemList + .filter(function (item) { + return (!item.questItem + && (itemObj.classid === -1 || item.classid === itemObj.classid) + && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) + && (itemObj.quality === -1 || item.quality === itemObj.quality) + && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) + && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) + && (itemObj.equipped === null || ( + typeof itemObj.equipped === "number" + ? item.bodylocation === itemObj.equipped + : item.isEquipped === itemObj.equipped) + ) + && (!itemObj.name || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) + ); + }); + if (items.length > 0) { + return { + have: true, + item: copyUnit(items.first()) + }; + } + } + + return { + have: false, + item: null + }; }; /** @@ -1028,58 +1063,62 @@ Unit.prototype.findFirst = function (itemInfo = []) { * @returns Boolean */ Unit.prototype.haveAll = function (itemInfo = [], returnIfSome = false) { - if (this === undefined || this.type > 1) return false; - // if an object but not an array convert to array - !Array.isArray(itemInfo) && typeof itemInfo === "object" && (itemInfo = [itemInfo]); - if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return false; - let itemList = this.getItemsEx(); - let haveAll = false; - let checkedGids = []; - - for (let i = 0; i < itemInfo.length; i++) { - const itemObj = Object.assign({}, { - classid: -1, - itemtype: -1, - quality: -1, - runeword: null, - ethereal: null, - equipped: null, - basetype: null, - name: "" - }, itemInfo[i]); - - // convert id into string - typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); - - let items = itemList - .filter(function (item) { - return (!item.questItem - && (checkedGids.indexOf(item.gid) === -1) - && (itemObj.classid === -1 || item.classid === itemObj.classid) - && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) - && (itemObj.quality === -1 || item.quality === itemObj.quality) - && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) - && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) - && (itemObj.equipped === null || (typeof itemObj.equipped === "number" ? item.bodylocation === itemObj.equipped : item.isEquipped === itemObj.equipped)) - && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) - && (!itemObj.name.length || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) - ); - }); - if (items.length > 0) { - if (returnIfSome) return true; - checkedGids.push(items.first().gid); - haveAll = true; - } else { - if (returnIfSome) continue; - return false; - } - } - - return haveAll; + if (this === undefined || this.type > 1) return false; + // if an object but not an array convert to array + !Array.isArray(itemInfo) && typeof itemInfo === "object" && (itemInfo = [itemInfo]); + if (!Array.isArray(itemInfo) || typeof itemInfo[0] !== "object") return false; + let itemList = this.getItemsEx(); + let haveAll = false; + let checkedGids = []; + + for (let i = 0; i < itemInfo.length; i++) { + const itemObj = Object.assign({}, { + classid: -1, + itemtype: -1, + quality: -1, + runeword: null, + ethereal: null, + equipped: null, + basetype: null, + name: "" + }, itemInfo[i]); + + // convert id into string + typeof itemObj.name === "number" && (itemObj.name = getLocaleString(itemObj.name)); + + let items = itemList + .filter(function (item) { + return (!item.questItem + && (checkedGids.indexOf(item.gid) === -1) + && (itemObj.classid === -1 || item.classid === itemObj.classid) + && (itemObj.itemtype === -1 || item.itemType === itemObj.itemtype) + && (itemObj.quality === -1 || item.quality === itemObj.quality) + && (itemObj.runeword === null || (item.runeword === itemObj.runeword)) + && (itemObj.ethereal === null || (item.ethereal === itemObj.ethereal)) + && (itemObj.equipped === null || ( + typeof itemObj.equipped === "number" + ? item.bodylocation === itemObj.equipped + : item.isEquipped === itemObj.equipped) + ) + && (itemObj.basetype === null || ((item.normal || item.superior) === itemObj.basetype)) + && (!itemObj.name.length || item.fname.toLowerCase().includes(itemObj.name.toLowerCase())) + ); + }); + if (items.length > 0) { + if (returnIfSome) return true; + checkedGids.push(items.first().gid); + haveAll = true; + } else { + if (returnIfSome) continue; + return false; + } + } + + return haveAll; }; Unit.prototype.haveSome = function (itemInfo = []) { - return this.haveAll(itemInfo, true); + return this.haveAll(itemInfo, true); }; /** @@ -1088,685 +1127,687 @@ Unit.prototype.haveSome = function (itemInfo = []) { * @returns Unit[] */ Unit.prototype.getItems = function (...args) { - let items = []; - let item = this.getItem.apply(this, args); + let items = []; + let item = this.getItem.apply(this, args); - if (item) { - do { - items.push(copyUnit(item)); - } while (item.getNext()); - } + if (item) { + do { + items.push(copyUnit(item)); + } while (item.getNext()); + } - return Array.isArray(items) ? items : []; + return Array.isArray(items) ? items : []; }; Unit.prototype.getItemsEx = function (...args) { - let items = []; - let item = this.getItem.apply(this, args); + let items = []; + let item = this.getItem.apply(this, args); - if (item) { - do { - items.push(copyUnit(item)); - } while (item.getNext()); - } + if (item) { + do { + items.push(copyUnit(item)); + } while (item.getNext()); + } - return items; + return items; }; Unit.prototype.getPrefix = function (id) { - switch (typeof id) { - case "number": - if (typeof this.prefixnums !== "object") return this.prefixnum === id; - - for (let i = 0; i < this.prefixnums.length; i += 1) { - if (id === this.prefixnums[i]) { - return true; - } - } - - break; - case "string": - if (typeof this.prefixes !== "object") { - return this.prefix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); - } - - for (let i = 0; i < this.prefixes.length; i += 1) { - if (id.replace(/\s+/g, "").toLowerCase() === this.prefixes[i].replace(/\s+/g, "").toLowerCase()) { - return true; - } - } - - break; - } - - return false; + switch (typeof id) { + case "number": + if (typeof this.prefixnums !== "object") return this.prefixnum === id; + + for (let i = 0; i < this.prefixnums.length; i += 1) { + if (id === this.prefixnums[i]) { + return true; + } + } + + break; + case "string": + if (typeof this.prefixes !== "object") { + return this.prefix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); + } + + for (let i = 0; i < this.prefixes.length; i += 1) { + if (id.replace(/\s+/g, "").toLowerCase() === this.prefixes[i].replace(/\s+/g, "").toLowerCase()) { + return true; + } + } + + break; + } + + return false; }; Unit.prototype.getSuffix = function (id) { - switch (typeof id) { - case "number": - if (typeof this.suffixnums !== "object") return this.suffixnum === id; - - for (let i = 0; i < this.suffixnums.length; i += 1) { - if (id === this.suffixnums[i]) { - return true; - } - } - - break; - case "string": - if (typeof this.suffixes !== "object") { - return this.suffix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); - } - - for (let i = 0; i < this.suffixes.length; i += 1) { - if (id.replace(/\s+/g, "").toLowerCase() === this.suffixes[i].replace(/\s+/g, "").toLowerCase()) { - return true; - } - } - - break; - } - - return false; + switch (typeof id) { + case "number": + if (typeof this.suffixnums !== "object") return this.suffixnum === id; + + for (let i = 0; i < this.suffixnums.length; i += 1) { + if (id === this.suffixnums[i]) { + return true; + } + } + + break; + case "string": + if (typeof this.suffixes !== "object") { + return this.suffix.replace(/\s+/g, "").toLowerCase() === id.replace(/\s+/g, "").toLowerCase(); + } + + for (let i = 0; i < this.suffixes.length; i += 1) { + if (id.replace(/\s+/g, "").toLowerCase() === this.suffixes[i].replace(/\s+/g, "").toLowerCase()) { + return true; + } + } + + break; + } + + return false; }; Unit.prototype.__defineGetter__("dexreq", - function () { - let ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - let baseReq = getBaseStat("items", this.classid, "reqdex"); - let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); + function () { + let ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + let baseReq = getBaseStat("items", this.classid, "reqdex"); + let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - return Math.max(finalReq, 0); - }); + return Math.max(finalReq, 0); + }); Unit.prototype.__defineGetter__("strreq", - function () { - let ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - let baseReq = getBaseStat("items", this.classid, "reqstr"); - let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); + function () { + let ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + let baseReq = getBaseStat("items", this.classid, "reqstr"); + let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - return Math.max(finalReq, 0); - }); + return Math.max(finalReq, 0); + }); Unit.prototype.__defineGetter__("itemclass", - function () { - if (getBaseStat("items", this.classid, "code") === undefined) return 0; - if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ultracode")) return 2; - if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ubercode")) return 1; + function () { + if (getBaseStat("items", this.classid, "code") === undefined) return 0; + if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ultracode")) return 2; + if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ubercode")) return 1; - return 0; - }); + return 0; + }); Unit.prototype.getStatEx = function (id, subid) { - let temp, rval, regex; - - switch (id) { - case sdk.stats.AllRes: - // calculates all res, doesn't exist though - // 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.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 (let 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); - } - - // can fail sometimes - !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 (let i = 0; i < temp[id].length; i += 1) { - if (temp[id][i] !== 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 (let 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 && (this.desc = this.description); - - if (this.desc) { - temp = 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 && (this.desc = this.description); - - if (this.desc) { - temp = 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; - } - } - - return (subid === undefined ? this.getStat(id) : this.getStat(id, subid)); + let temp, rval, regex; + + switch (id) { + case sdk.stats.AllRes: + // calculates all res, doesn't exist though + // 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.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 (let 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); + } + + // can fail sometimes + !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 (let i = 0; i < temp[id].length; i += 1) { + if (temp[id][i] !== 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 (let 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 && (this.desc = this.description); + + if (this.desc) { + temp = 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 && (this.desc = this.description); + + if (this.desc) { + temp = 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; + } + } + + return (subid === undefined ? this.getStat(id) : this.getStat(id, subid)); }; /* - _NTIPAliasColor["black"] = 3; - _NTIPAliasColor["lightblue"] = 4; - _NTIPAliasColor["darkblue"] = 5; - _NTIPAliasColor["crystalblue"] = 6; - _NTIPAliasColor["lightred"] = 7; - _NTIPAliasColor["darkred"] = 8; - _NTIPAliasColor["crystalred"] = 9; - _NTIPAliasColor["darkgreen"] = 11; - _NTIPAliasColor["crystalgreen"] = 12; - _NTIPAliasColor["lightyellow"] = 13; - _NTIPAliasColor["darkyellow"] = 14; - _NTIPAliasColor["lightgold"] = 15; - _NTIPAliasColor["darkgold"] = 16; - _NTIPAliasColor["lightpurple"] = 17; - _NTIPAliasColor["orange"] = 19; - _NTIPAliasColor["white"] = 20; + _NTIPAliasColor["black"] = 3; + _NTIPAliasColor["lightblue"] = 4; + _NTIPAliasColor["darkblue"] = 5; + _NTIPAliasColor["crystalblue"] = 6; + _NTIPAliasColor["lightred"] = 7; + _NTIPAliasColor["darkred"] = 8; + _NTIPAliasColor["crystalred"] = 9; + _NTIPAliasColor["darkgreen"] = 11; + _NTIPAliasColor["crystalgreen"] = 12; + _NTIPAliasColor["lightyellow"] = 13; + _NTIPAliasColor["darkyellow"] = 14; + _NTIPAliasColor["lightgold"] = 15; + _NTIPAliasColor["darkgold"] = 16; + _NTIPAliasColor["lightpurple"] = 17; + _NTIPAliasColor["orange"] = 19; + _NTIPAliasColor["white"] = 20; */ Unit.prototype.getColor = function () { - let colors; - let Color = { - black: 3, - lightblue: 4, - darkblue: 5, - crystalblue: 6, - lightred: 7, - darkred: 8, - crystalred: 9, - darkgreen: 11, - crystalgreen: 12, - lightyellow: 13, - darkyellow: 14, - lightgold: 15, - darkgold: 16, - lightpurple: 17, - orange: 19, - white: 20 - }; - - // check type - switch (this.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.Armor: - case sdk.items.type.Boots: - case sdk.items.type.Gloves: - case sdk.items.type.Belt: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - 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: - break; - default: - return -1; - } - - // check quality - if ([sdk.items.quality.Magic, sdk.items.quality.Set, sdk.items.quality.Rare, sdk.items.quality.Unique].indexOf(this.quality) === -1) { - return -1; - } - - if (this.quality === sdk.items.quality.Magic || this.quality === sdk.items.quality.Rare) { - colors = { - "Screaming": Color.orange, - "Howling": Color.orange, - "Wailing": Color.orange, - "Sapphire": Color.lightblue, - "Snowy": Color.lightblue, - "Shivering": Color.lightblue, - "Boreal": Color.lightblue, - "Hibernal": Color.lightblue, - "Ruby": Color.lightred, - "Amber": Color.lightyellow, - "Static": Color.lightyellow, - "Glowing": Color.lightyellow, - "Buzzing": Color.lightyellow, - "Arcing": Color.lightyellow, - "Shocking": Color.lightyellow, - "Emerald": Color.crystalgreen, - "Saintly": Color.darkgold, - "Holy": Color.darkgold, - "Godly": Color.darkgold, - "Visionary": Color.white, - "Mnemonic": Color.crystalblue, - "Bowyer's": Color.lightgold, - "Gymnastic": Color.lightgold, - "Spearmaiden's": Color.lightgold, - "Archer's": Color.lightgold, - "Athlete's": Color.lightgold, - "Lancer's": Color.lightgold, - "Charged": Color.lightgold, - "Blazing": Color.lightgold, - "Freezing": Color.lightgold, - "Glacial": Color.lightgold, - "Powered": Color.lightgold, - "Volcanic": Color.lightgold, - "Blighting": Color.lightgold, - "Noxious": Color.lightgold, - "Mojo": Color.lightgold, - "Cursing": Color.lightgold, - "Venomous": Color.lightgold, - "Golemlord's": Color.lightgold, - "Warden's": Color.lightgold, - "Hawk Branded": Color.lightgold, - "Commander's": Color.lightgold, - "Marshal's": Color.lightgold, - "Rose Branded": Color.lightgold, - "Guardian's": Color.lightgold, - "Veteran's": Color.lightgold, - "Resonant": Color.lightgold, - "Raging": Color.lightgold, - "Echoing": Color.lightgold, - "Furious": Color.lightgold, - "Master's": Color.lightgold, // there's 2x masters... - "Caretaker's": Color.lightgold, - "Terrene": Color.lightgold, - "Feral": Color.lightgold, - "Gaean": Color.lightgold, - "Communal": Color.lightgold, - "Keeper's": Color.lightgold, - "Sensei's": Color.lightgold, - "Trickster's": Color.lightgold, - "Psychic": Color.lightgold, - "Kenshi's": Color.lightgold, - "Cunning": Color.lightgold, - "Shadow": Color.lightgold, - "Faithful": Color.white, - "Priest's": Color.crystalgreen, - "Dragon's": Color.crystalblue, - "Vulpine": Color.crystalblue, - "Shimmering": Color.lightpurple, - "Rainbow": Color.lightpurple, - "Scintillating": Color.lightpurple, - "Prismatic": Color.lightpurple, - "Chromatic": Color.lightpurple, - "Hierophant's": Color.crystalgreen, - "Berserker's": Color.crystalgreen, - "Necromancer's": Color.crystalgreen, - "Witch-hunter's": Color.crystalgreen, - "Arch-Angel's": Color.crystalgreen, - "Valkyrie's": Color.crystalgreen, - "Massive": Color.darkgold, - "Savage": Color.darkgold, - "Merciless": Color.darkgold, - "Ferocious": Color.black, - "Grinding": Color.white, - "Cruel": Color.black, - "Gold": Color.lightgold, - "Platinum": Color.lightgold, - "Meteoric": Color.lightgold, - "Strange": Color.lightgold, - "Weird": Color.lightgold, - "Knight's": Color.darkgold, - "Lord's": Color.darkgold, - "Fool's": Color.white, - "King's": Color.darkgold, - //"Master's": Color.darkgold, - "Elysian": Color.darkgold, - "Fiery": Color.darkred, - "Smoldering": Color.darkred, - "Smoking": Color.darkred, - "Flaming": Color.darkred, - "Condensing": Color.darkred, - "Septic": Color.darkgreen, - "Foul": Color.darkgreen, - "Corrosive": Color.darkgreen, - "Toxic": Color.darkgreen, - "Pestilent": Color.darkgreen, - "of Quickness": Color.darkyellow, - "of the Glacier": Color.darkblue, - "of Winter": Color.darkblue, - "of Burning": Color.darkred, - "of Incineration": Color.darkred, - "of Thunder": Color.darkyellow, - "of Storms": Color.darkyellow, - "of Carnage": Color.black, - "of Slaughter": Color.black, - "of Butchery": Color.black, - "of Evisceration": Color.black, - "of Performance": Color.black, - "of Transcendence": Color.black, - "of Pestilence": Color.darkgreen, - "of Anthrax": Color.darkgreen, - "of the Locust": Color.crystalred, - "of the Lamprey": Color.crystalred, - "of the Wraith": Color.crystalred, - "of the Vampire": Color.crystalred, - "of Icebolt": Color.lightblue, - "of Nova": Color.crystalblue, - "of the Mammoth": Color.crystalred, - "of Frost Shield": Color.lightblue, - "of Nova Shield": Color.crystalblue, - "of Wealth": Color.lightgold, - "of Fortune": Color.lightgold, - "of Luck": Color.lightgold, - "of Perfection": Color.darkgold, - "of Regrowth": Color.crystalred, - "of Spikes": Color.orange, - "of Razors": Color.orange, - "of Swords": Color.orange, - "of Stability": Color.darkyellow, - "of the Colosuss": Color.crystalred, - "of the Squid": Color.crystalred, - "of the Whale": Color.crystalred, - "of Defiance": Color.darkred, - "of the Titan": Color.darkgold, - "of Atlas": Color.darkgold, - "of Wizardry": Color.darkgold - }; - - switch (this.itemType) { - case sdk.items.type.Boots: - colors["of Precision"] = Color.darkgold; - - break; - case sdk.items.type.Gloves: - colors["of Alacrity"] = Color.darkyellow; - colors["of the Leech"] = Color.crystalred; - colors["of the Bat"] = Color.crystalred; - colors["of the Giant"] = Color.darkgold; - - break; - } - } else if (this.set) { - if (this.identified) { - for (let i = 0; i < 127; i += 1) { - if (this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(16, i, 3)))) { - return getBaseStat(16, i, 12) > 20 ? -1 : getBaseStat(16, i, 12); - } - } - } else { - return Color.lightyellow; // Unidentified set item - } - } else if (this.unique) { - for (let i = 0; i < 401; i += 1) { - if (this.code === getBaseStat(17, i, 4).replace(/^\s+|\s+$/g, "") && this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(17, i, 2)))) { - return getBaseStat(17, i, 13) > 20 ? -1 : getBaseStat(17, i, 13); - } - } - } - - for (let i = 0; i < this.suffixes.length; i += 1) { - if (colors.hasOwnProperty(this.suffixes[i])) { - return colors[this.suffixes[i]]; - } - } - - for (let i = 0; i < this.prefixes.length; i += 1) { - if (colors.hasOwnProperty(this.prefixes[i])) { - return colors[this.prefixes[i]]; - } - } - - return -1; + let colors; + let Color = { + black: 3, + lightblue: 4, + darkblue: 5, + crystalblue: 6, + lightred: 7, + darkred: 8, + crystalred: 9, + darkgreen: 11, + crystalgreen: 12, + lightyellow: 13, + darkyellow: 14, + lightgold: 15, + darkgold: 16, + lightpurple: 17, + orange: 19, + white: 20 + }; + + // check type + switch (this.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.Armor: + case sdk.items.type.Boots: + case sdk.items.type.Gloves: + case sdk.items.type.Belt: + case sdk.items.type.AuricShields: + case sdk.items.type.VoodooHeads: + case sdk.items.type.Helm: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Circlet: + case sdk.items.type.Pelt: + 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: + break; + default: + return -1; + } + + // check quality + if ([sdk.items.quality.Magic, sdk.items.quality.Set, sdk.items.quality.Rare, sdk.items.quality.Unique] + .indexOf(this.quality) === -1) { + return -1; + } + + if (this.quality === sdk.items.quality.Magic || this.quality === sdk.items.quality.Rare) { + colors = { + "Screaming": Color.orange, + "Howling": Color.orange, + "Wailing": Color.orange, + "Sapphire": Color.lightblue, + "Snowy": Color.lightblue, + "Shivering": Color.lightblue, + "Boreal": Color.lightblue, + "Hibernal": Color.lightblue, + "Ruby": Color.lightred, + "Amber": Color.lightyellow, + "Static": Color.lightyellow, + "Glowing": Color.lightyellow, + "Buzzing": Color.lightyellow, + "Arcing": Color.lightyellow, + "Shocking": Color.lightyellow, + "Emerald": Color.crystalgreen, + "Saintly": Color.darkgold, + "Holy": Color.darkgold, + "Godly": Color.darkgold, + "Visionary": Color.white, + "Mnemonic": Color.crystalblue, + "Bowyer's": Color.lightgold, + "Gymnastic": Color.lightgold, + "Spearmaiden's": Color.lightgold, + "Archer's": Color.lightgold, + "Athlete's": Color.lightgold, + "Lancer's": Color.lightgold, + "Charged": Color.lightgold, + "Blazing": Color.lightgold, + "Freezing": Color.lightgold, + "Glacial": Color.lightgold, + "Powered": Color.lightgold, + "Volcanic": Color.lightgold, + "Blighting": Color.lightgold, + "Noxious": Color.lightgold, + "Mojo": Color.lightgold, + "Cursing": Color.lightgold, + "Venomous": Color.lightgold, + "Golemlord's": Color.lightgold, + "Warden's": Color.lightgold, + "Hawk Branded": Color.lightgold, + "Commander's": Color.lightgold, + "Marshal's": Color.lightgold, + "Rose Branded": Color.lightgold, + "Guardian's": Color.lightgold, + "Veteran's": Color.lightgold, + "Resonant": Color.lightgold, + "Raging": Color.lightgold, + "Echoing": Color.lightgold, + "Furious": Color.lightgold, + "Master's": Color.lightgold, // there's 2x masters... + "Caretaker's": Color.lightgold, + "Terrene": Color.lightgold, + "Feral": Color.lightgold, + "Gaean": Color.lightgold, + "Communal": Color.lightgold, + "Keeper's": Color.lightgold, + "Sensei's": Color.lightgold, + "Trickster's": Color.lightgold, + "Psychic": Color.lightgold, + "Kenshi's": Color.lightgold, + "Cunning": Color.lightgold, + "Shadow": Color.lightgold, + "Faithful": Color.white, + "Priest's": Color.crystalgreen, + "Dragon's": Color.crystalblue, + "Vulpine": Color.crystalblue, + "Shimmering": Color.lightpurple, + "Rainbow": Color.lightpurple, + "Scintillating": Color.lightpurple, + "Prismatic": Color.lightpurple, + "Chromatic": Color.lightpurple, + "Hierophant's": Color.crystalgreen, + "Berserker's": Color.crystalgreen, + "Necromancer's": Color.crystalgreen, + "Witch-hunter's": Color.crystalgreen, + "Arch-Angel's": Color.crystalgreen, + "Valkyrie's": Color.crystalgreen, + "Massive": Color.darkgold, + "Savage": Color.darkgold, + "Merciless": Color.darkgold, + "Ferocious": Color.black, + "Grinding": Color.white, + "Cruel": Color.black, + "Gold": Color.lightgold, + "Platinum": Color.lightgold, + "Meteoric": Color.lightgold, + "Strange": Color.lightgold, + "Weird": Color.lightgold, + "Knight's": Color.darkgold, + "Lord's": Color.darkgold, + "Fool's": Color.white, + "King's": Color.darkgold, + //"Master's": Color.darkgold, + "Elysian": Color.darkgold, + "Fiery": Color.darkred, + "Smoldering": Color.darkred, + "Smoking": Color.darkred, + "Flaming": Color.darkred, + "Condensing": Color.darkred, + "Septic": Color.darkgreen, + "Foul": Color.darkgreen, + "Corrosive": Color.darkgreen, + "Toxic": Color.darkgreen, + "Pestilent": Color.darkgreen, + "of Quickness": Color.darkyellow, + "of the Glacier": Color.darkblue, + "of Winter": Color.darkblue, + "of Burning": Color.darkred, + "of Incineration": Color.darkred, + "of Thunder": Color.darkyellow, + "of Storms": Color.darkyellow, + "of Carnage": Color.black, + "of Slaughter": Color.black, + "of Butchery": Color.black, + "of Evisceration": Color.black, + "of Performance": Color.black, + "of Transcendence": Color.black, + "of Pestilence": Color.darkgreen, + "of Anthrax": Color.darkgreen, + "of the Locust": Color.crystalred, + "of the Lamprey": Color.crystalred, + "of the Wraith": Color.crystalred, + "of the Vampire": Color.crystalred, + "of Icebolt": Color.lightblue, + "of Nova": Color.crystalblue, + "of the Mammoth": Color.crystalred, + "of Frost Shield": Color.lightblue, + "of Nova Shield": Color.crystalblue, + "of Wealth": Color.lightgold, + "of Fortune": Color.lightgold, + "of Luck": Color.lightgold, + "of Perfection": Color.darkgold, + "of Regrowth": Color.crystalred, + "of Spikes": Color.orange, + "of Razors": Color.orange, + "of Swords": Color.orange, + "of Stability": Color.darkyellow, + "of the Colosuss": Color.crystalred, + "of the Squid": Color.crystalred, + "of the Whale": Color.crystalred, + "of Defiance": Color.darkred, + "of the Titan": Color.darkgold, + "of Atlas": Color.darkgold, + "of Wizardry": Color.darkgold + }; + + switch (this.itemType) { + case sdk.items.type.Boots: + colors["of Precision"] = Color.darkgold; + + break; + case sdk.items.type.Gloves: + colors["of Alacrity"] = Color.darkyellow; + colors["of the Leech"] = Color.crystalred; + colors["of the Bat"] = Color.crystalred; + colors["of the Giant"] = Color.darkgold; + + break; + } + } else if (this.set) { + if (this.identified) { + for (let i = 0; i < 127; i += 1) { + if (this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(16, i, 3)))) { + return getBaseStat(16, i, 12) > 20 ? -1 : getBaseStat(16, i, 12); + } + } + } else { + return Color.lightyellow; // Unidentified set item + } + } else if (this.unique) { + for (let i = 0; i < 401; i += 1) { + if (this.code === getBaseStat(17, i, 4).replace(/^\s+|\s+$/g, "") + && this.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat(17, i, 2)))) { + return getBaseStat(17, i, 13) > 20 ? -1 : getBaseStat(17, i, 13); + } + } + } + + for (let i = 0; i < this.suffixes.length; i += 1) { + if (colors.hasOwnProperty(this.suffixes[i])) { + return colors[this.suffixes[i]]; + } + } + + for (let i = 0; i < this.prefixes.length; i += 1) { + if (colors.hasOwnProperty(this.prefixes[i])) { + return colors[this.prefixes[i]]; + } + } + + return -1; }; /** @@ -1778,452 +1819,466 @@ Unit.prototype.getColor = function () { * @throws Error */ Unit.prototype.castChargedSkill = function (...args) { - let skillId, x, y, unit, chargedItem, charge; - let chargedItems = []; - let validCharge = function (itemCharge) { - return itemCharge.skill === skillId && itemCharge.charges; - }; - - 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) { - throw Error("invalid arguments, expected 'me' object or 'item' unit"); - } - - // Called the function the unit, me. - if (this === me) { - if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); - - chargedItems = []; - - // Item must be equipped, or a charm in inventory - this.getItemsEx(-1) - .filter(item => item && (item.isEquipped || (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 - }); - } - } - } - }); - - if (chargedItems.length === 0) throw Error("Don't have the charged skill (" + skillId + "), or not enough charges"); - - chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; - - return chargedItem.castChargedSkill.apply(chargedItem, args); - } else if (this.type === sdk.unittype.Item) { - charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates - - if (!charge) throw Error("No charged skill on this item"); - - if (skillId) { - // Filter out all other charged skills - charge = charge.filter(item => (skillId && item.skill === skillId) && !!item.charges); - } else if (charge.length > 1) { - throw new Error("multiple charges on this item without a given skillId"); - } - - charge = charge.first(); - - if (charge) { - // Setting skill on hand - if (!Config.PacketCasting || Config.PacketCasting === 1 && skillId !== sdk.skills.Teleport) { - return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); // Non packet casting - } - - // Packet casting - sendPacket(1, sdk.packets.send.SelectSkill, 2, charge.skill, 1, 0x0, 1, 0x00, 4, this.gid); - // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet - - // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked - sendPacket(1, sdk.packets.send.RightSkillOnLocation, 2, x || me.x, 2, y || me.y); // Cast the skill - - return true; - } - } - - return false; + let skillId, x, y, unit, chargedItem, charge; + let chargedItems = []; + let validCharge = function (itemCharge) { + return itemCharge.skill === skillId && itemCharge.charges; + }; + + 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) { + throw Error("invalid arguments, expected 'me' object or 'item' unit"); + } + + // Called the function the unit, me. + if (this === me) { + if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); + + chargedItems = []; + + // Item must be equipped, or a charm in inventory + this.getItemsEx(-1) + .filter(item => item && (item.isEquipped || (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 + }); + } + } + } + }); + + if (chargedItems.length === 0) throw Error("Don't have the charged skill (" + skillId + "), or not enough charges"); + + chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; + + return chargedItem.castChargedSkill.apply(chargedItem, args); + } else if (this.type === sdk.unittype.Item) { + charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates + + if (!charge) throw Error("No charged skill on this item"); + + if (skillId) { + // Filter out all other charged skills + charge = charge.filter(item => (skillId && item.skill === skillId) && !!item.charges); + } else if (charge.length > 1) { + throw new Error("multiple charges on this item without a given skillId"); + } + + charge = charge.first(); + + if (charge) { + // Setting skill on hand + if (!Config.PacketCasting || Config.PacketCasting === 1 && skillId !== sdk.skills.Teleport) { + return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); // Non packet casting + } + + // Packet casting + sendPacket(1, sdk.packets.send.SelectSkill, 2, charge.skill, 1, 0x0, 1, 0x00, 4, this.gid); + // No need for a delay, since its TCP, the server recv's the next statement always after the send cast skill packet + + // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked + sendPacket(1, sdk.packets.send.RightSkillOnLocation, 2, x || me.x, 2, y || me.y); // Cast the skill + + return true; + } + } + + return false; }; /** * @description equip an item. */ Unit.prototype.equip = function (destLocation = undefined) { - if (this.isEquipped) return true; // Item already equiped - - const findspot = function (item) { - let tempspot = Storage.Stash.FindSpot(item); - - if (getUIFlag(sdk.uiflags.Stash) && tempspot) { - return { location: Storage.Stash.location, coord: tempspot }; - } - - tempspot = Storage.Inventory.FindSpot(item); - - return tempspot ? { location: Storage.Inventory.location, coord: tempspot } : false; - }; - const doubleHanded = [ - sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Polearm, sdk.items.type.Crossbow, - sdk.items.type.HandtoHand, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear - ]; - - // Not an item, or unidentified, or not enough stats - if (this.type !== sdk.unittype.Item || !this.getFlag(sdk.items.flags.Identified) - || this.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) - || this.dexreq > me.getStat(sdk.stats.Dexterity) - || this.strreq > me.getStat(sdk.stats.Strength)) { - return false; - } - - // If not a specific location is given, figure it out (can be useful to equip a double weapon) - !destLocation && (destLocation = this.getBodyLoc()); - // If destLocation isnt an array, make it one - !Array.isArray(destLocation) && (destLocation = [destLocation]); - - console.log("equiping " + this.name + " to bodylocation: " + destLocation.first()); - - let currentEquiped = me.getItemsEx(-1).filter(item => - destLocation.indexOf(item.bodylocation) !== -1 - || ( // Deal with double handed weapons - - (item.isOnMain) - && [sdk.body.RightArm, sdk.body.LeftArm].indexOf(destLocation) // in case destination is on the weapon/shield slot - && ( - doubleHanded.indexOf(this.itemType) !== -1 // this item is a double handed item - || doubleHanded.indexOf(item.itemType) !== -1 // current item is a double handed item - ) - ) - ).sort((a, b) => b - a); // shields first - - // if nothing is equipped at the moment, just equip it - if (!currentEquiped.length) { - clickItemAndWait(sdk.clicktypes.click.item.Left, this); - clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); - } else { - // unequip / swap items - currentEquiped.forEach((item, index) => { - // Last item, so swap instead of putting off first - if (index === (currentEquiped.length - 1)) { - print("swap " + this.name + " for " + item.name); - let oldLoc = { x: this.x, y: this.y, location: this.location }; - clickItemAndWait(sdk.clicktypes.click.item.Left, this); // Pick up current item - clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // the swap of items - // Find a spot for the current item - let spot = findspot(item); - - if (!spot) { // If no spot is found for the item, rollback - clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // swap again - clickItemAndWait(sdk.clicktypes.click.item.Left, oldLoc.x, oldLoc.y, oldLoc.location); // put item back on old spot - throw Error("cant find spot for unequipped item"); - } - - clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.y, spot.coord.x, spot.location); // put item on the found spot - - return; - } - - print("Unequip item first " + item.name); - // Incase multiple items are equipped - let spot = findspot(item); // Find a spot for the current item - - if (!spot) throw Error("cant find spot for unequipped item"); - - clickItemAndWait(sdk.clicktypes.click.item.Left, item.bodylocation); - clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.x, spot.coord.y, spot.location); - }); - } - - return { - success: this.bodylocation === destLocation.first(), - unequiped: currentEquiped, - rollback: () => currentEquiped.forEach(item => item.equip()) // Note; rollback only works if you had other items equipped before. - }; + if (this.isEquipped) return true; // Item already equiped + + const findspot = function (item) { + let tempspot = Storage.Stash.FindSpot(item); + + if (getUIFlag(sdk.uiflags.Stash) && tempspot) { + return { location: Storage.Stash.location, coord: tempspot }; + } + + tempspot = Storage.Inventory.FindSpot(item); + + return tempspot ? { location: Storage.Inventory.location, coord: tempspot } : false; + }; + const doubleHanded = [ + sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Polearm, sdk.items.type.Crossbow, + sdk.items.type.HandtoHand, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear + ]; + + // Not an item, or unidentified, or not enough stats + if (this.type !== sdk.unittype.Item || !this.getFlag(sdk.items.flags.Identified) + || this.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) + || this.dexreq > me.getStat(sdk.stats.Dexterity) + || this.strreq > me.getStat(sdk.stats.Strength)) { + return false; + } + + // If not a specific location is given, figure it out (can be useful to equip a double weapon) + !destLocation && (destLocation = this.getBodyLoc()); + // If destLocation isnt an array, make it one + !Array.isArray(destLocation) && (destLocation = [destLocation]); + + console.log("equiping " + this.name + " to bodylocation: " + destLocation.first()); + + let currentEquiped = me.getItemsEx(-1).filter(item => + destLocation.indexOf(item.bodylocation) !== -1 + || ( // Deal with double handed weapons + + (item.isOnMain) + && [sdk.body.RightArm, sdk.body.LeftArm].indexOf(destLocation) // in case destination is on the weapon/shield slot + && ( + doubleHanded.indexOf(this.itemType) !== -1 // this item is a double handed item + || doubleHanded.indexOf(item.itemType) !== -1 // current item is a double handed item + ) + ) + ).sort((a, b) => b - a); // shields first + + // if nothing is equipped at the moment, just equip it + if (!currentEquiped.length) { + clickItemAndWait(sdk.clicktypes.click.item.Left, this); + clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); + } else { + // unequip / swap items + currentEquiped.forEach((item, index) => { + // Last item, so swap instead of putting off first + if (index === (currentEquiped.length - 1)) { + print("swap " + this.name + " for " + item.name); + let oldLoc = { x: this.x, y: this.y, location: this.location }; + clickItemAndWait(sdk.clicktypes.click.item.Left, this); // Pick up current item + clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // the swap of items + // Find a spot for the current item + let spot = findspot(item); + + if (!spot) { // If no spot is found for the item, rollback + clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // swap again + clickItemAndWait(sdk.clicktypes.click.item.Left, oldLoc.x, oldLoc.y, oldLoc.location); // put item back on old spot + throw Error("cant find spot for unequipped item"); + } + + clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.y, spot.coord.x, spot.location); // put item on the found spot + + return; + } + + print("Unequip item first " + item.name); + // Incase multiple items are equipped + let spot = findspot(item); // Find a spot for the current item + + if (!spot) throw Error("cant find spot for unequipped item"); + + clickItemAndWait(sdk.clicktypes.click.item.Left, item.bodylocation); + clickItemAndWait(sdk.clicktypes.click.item.Left, spot.coord.x, spot.coord.y, spot.location); + }); + } + + return { + success: this.bodylocation === destLocation.first(), + unequiped: currentEquiped, + rollback: () => currentEquiped.forEach(item => item.equip()) // Note; rollback only works if you had other items equipped before. + }; }; Unit.prototype.getBodyLoc = function () { - const types = {}; - types[sdk.body.Head] = [sdk.items.type.Helm, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; // helm - types[sdk.body.Neck] = [sdk.items.type.Amulet]; // amulet - types[sdk.body.Armor] = [sdk.items.type.Armor]; // armor - types[sdk.body.RightArm] = [ - 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.HandtoHand, sdk.items.type.Orb, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.AssassinClaw - ]; // weapons - types[sdk.body.LeftArm] = [sdk.items.type.Shield, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads], // shields / Arrows / bolts - types[sdk.body.RingRight] = [sdk.items.type.Ring]; // ring slot 1 - types[sdk.body.RingLeft] = [sdk.items.type.Ring]; // ring slot 2 - types[sdk.body.Belt] = [sdk.items.type.Belt]; // belt - types[sdk.body.Feet] = [sdk.items.type.Boots]; // boots - types[sdk.body.Gloves] = [sdk.items.type.Gloves]; // gloves - //types[sdk.body.RightArmSecondary] = types[sdk.body.RightArm]; - //types[sdk.body.LeftArmSecondary] = types[sdk.body.LeftArm]; - let bodyLoc = []; - - for (let i in types) { - this.itemType && types[i].indexOf(this.itemType) !== -1 && bodyLoc.push(i); - } - - // Strings are hard to calculate with, parse to int - return bodyLoc.map(parseInt); + const types = {}; + types[sdk.body.Head] = [sdk.items.type.Helm, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; // helm + types[sdk.body.Neck] = [sdk.items.type.Amulet]; // amulet + types[sdk.body.Armor] = [sdk.items.type.Armor]; // armor + types[sdk.body.RightArm] = [ + 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.HandtoHand, sdk.items.type.Orb, + sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.AssassinClaw + ]; // weapons + types[sdk.body.LeftArm] = [ + sdk.items.type.Shield, sdk.items.type.BowQuiver, + sdk.items.type.CrossbowQuiver, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads + ], // shields / Arrows / bolts + types[sdk.body.RingRight] = [sdk.items.type.Ring]; // ring slot 1 + types[sdk.body.RingLeft] = [sdk.items.type.Ring]; // ring slot 2 + types[sdk.body.Belt] = [sdk.items.type.Belt]; // belt + types[sdk.body.Feet] = [sdk.items.type.Boots]; // boots + types[sdk.body.Gloves] = [sdk.items.type.Gloves]; // gloves + //types[sdk.body.RightArmSecondary] = types[sdk.body.RightArm]; + //types[sdk.body.LeftArmSecondary] = types[sdk.body.LeftArm]; + let bodyLoc = []; + + for (let i in types) { + this.itemType && types[i].indexOf(this.itemType) !== -1 && bodyLoc.push(i); + } + + // Strings are hard to calculate with, parse to int + return bodyLoc.map(parseInt); }; Unit.prototype.getRes = function (type, difficulty) { - if (!type || ![sdk.stats.FireResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, sdk.stats.LightningResist].includes(type)) { - return -1; - } - - difficulty === undefined || difficulty < 0 && (difficulty = 0); - difficulty > 2 && (difficulty = 2); - - let modifier = me.classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; - if (this === me) { - switch (type) { - case sdk.stats.FireResist: - me.getState(sdk.states.ShrineResFire) && (modifier += 75); - - break; - case sdk.stats.ColdResist: - me.getState(sdk.states.ShrineResCold) && (modifier += 75); - me.getState(sdk.states.Thawing) && (modifier += 50); - - break; - case sdk.stats.LightningResist: - me.getState(sdk.states.ShrineResLighting) && (modifier += 75); - - break; - case sdk.stats.PoisonResist: - me.getState(sdk.states.ShrineResPoison) && (modifier += 75); - me.getState(sdk.states.Antidote) && (modifier += 50); - - break; - } - } - return this.getStat(type) - modifier; + if (!type) return -1; + if (![sdk.stats.FireResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, sdk.stats.LightningResist].includes(type)) { + return -1; + } + + difficulty === undefined || difficulty < 0 && (difficulty = 0); + difficulty > 2 && (difficulty = 2); + + let modifier = me.classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; + if (this === me) { + switch (type) { + case sdk.stats.FireResist: + me.getState(sdk.states.ShrineResFire) && (modifier += 75); + + break; + case sdk.stats.ColdResist: + me.getState(sdk.states.ShrineResCold) && (modifier += 75); + me.getState(sdk.states.Thawing) && (modifier += 50); + + break; + case sdk.stats.LightningResist: + me.getState(sdk.states.ShrineResLighting) && (modifier += 75); + + break; + case sdk.stats.PoisonResist: + me.getState(sdk.states.ShrineResPoison) && (modifier += 75); + me.getState(sdk.states.Antidote) && (modifier += 50); + + break; + } + } + return this.getStat(type) - modifier; }; { - 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.defineProperties(Object.prototype, { - distance: { - get: function () { - return !me.gameReady ? NaN : /* Math.round */(getDistance.apply(null, [me, ...coords.apply(this)])); - }, - enumerable: false, - }, - }); - - 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.defineProperties(Object.prototype, { + distance: { + get: function () { + return !me.gameReady ? NaN : /* Math.round */(getDistance.apply(null, [me, ...coords.apply(this)])); + }, + enumerable: false, + }, + }); + + 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; + } + }); } Unit.prototype.hasEnchant = function (...enchants) { - if (!this.isMonster) return false; - for (let enchant of enchants) { - if (this.getEnchant(enchant)) return true; - } - return false; + if (!this.isMonster) return false; + for (let enchant of enchants) { + if (this.getEnchant(enchant)) return true; + } + return false; }; Unit.prototype.usingShield = function () { - if (this.type > sdk.unittype.Monster) return false; - // always switch to main hand if we are checking ourselves - this === me && me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - let shield = this.getItemsEx(-1, sdk.items.mode.Equipped).filter(s => s.isShield).first(); - return !!shield; + if (this.type > sdk.unittype.Monster) return false; + // always switch to main hand if we are checking ourselves + this === me && me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + let shield = this.getItemsEx(-1, sdk.items.mode.Equipped).filter(s => s.isShield).first(); + return !!shield; }; // something in here is causing demon imps in barricade towers to be skipped - todo: figure out what Unit.prototype.__defineGetter__("attackable", function () { - if (this === undefined || !copyUnit(this).x) return false; - if (this.type > sdk.unittype.Monster) return false; - // must be in same area - if (this.area !== me.area) return false; - // player and they are hostile - if (this.type === sdk.unittype.Player && getPlayerFlag(me.gid, this.gid, 8) && !this.dead) return true; - // Dead monster - if (this.hp === 0 || this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead) return false; - // Friendly monster/NPC - if (this.getStat(sdk.stats.Alignment) === 2) return false; - // catapults were returning a level of 0 and hanging up clear scripts - if (this.charlvl < 1) return false; - // neverCount base stat - hydras, traps etc. - if (!this.isMonsterObject && getBaseStat("monstats", this.classid, "neverCount")) { - return false; - } - // Monsters that are in flight - if ([ - sdk.monsters.CarrionBird1, sdk.monsters.UndeadScavenger, sdk.monsters.HellBuzzard, - sdk.monsters.WingedNightmare, sdk.monsters.SoulKiller2/*feel like this one is wrong*/, - sdk.monsters.CarrionBird2].includes(this.classid) && this.mode === sdk.monsters.mode.UsingSkill1) { - return false; - } - // Monsters that are Burrowed/Submerged - if ([ - sdk.monsters.SandMaggot, sdk.monsters.RockWorm, sdk.monsters.Devourer, sdk.monsters.GiantLamprey, sdk.monsters.WorldKiller2, - sdk.monsters.WaterWatcherLimb, sdk.monsters.RiverStalkerLimb, sdk.monsters.StygianWatcherLimb, - sdk.monsters.WaterWatcherHead, sdk.monsters.RiverStalkerHead, sdk.monsters.StygianWatcherHead].includes(this.classid) && this.mode === sdk.monsters.mode.Spawning) { - return false; - } - - return [sdk.monsters.ThroneBaal, sdk.monsters.Cow/*an evil force*/].indexOf(this.classid) === -1; + if (this === undefined || !copyUnit(this).x) return false; + if (this.type > sdk.unittype.Monster) return false; + // must be in same area + if (this.area !== me.area) return false; + // player and they are hostile + if (this.type === sdk.unittype.Player && getPlayerFlag(me.gid, this.gid, 8) && !this.dead) return true; + // Dead monster + if (this.hp === 0 || this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead) return false; + // Friendly monster/NPC + if (this.getStat(sdk.stats.Alignment) === 2) return false; + // catapults were returning a level of 0 and hanging up clear scripts + if (this.charlvl < 1) return false; + // neverCount base stat - hydras, traps etc. + if (!this.isMonsterObject && getBaseStat("monstats", this.classid, "neverCount")) { + return false; + } + // Monsters that are in flight + if ([ + sdk.monsters.CarrionBird1, sdk.monsters.UndeadScavenger, sdk.monsters.HellBuzzard, + sdk.monsters.WingedNightmare, sdk.monsters.SoulKiller2/*feel like this one is wrong*/, + sdk.monsters.CarrionBird2].includes(this.classid) && this.mode === sdk.monsters.mode.UsingSkill1) { + return false; + } + // Monsters that are Burrowed/Submerged + if ([ + sdk.monsters.SandMaggot, sdk.monsters.RockWorm, sdk.monsters.Devourer, + sdk.monsters.GiantLamprey, sdk.monsters.WorldKiller2, + sdk.monsters.WaterWatcherLimb, sdk.monsters.RiverStalkerLimb, sdk.monsters.StygianWatcherLimb, + sdk.monsters.WaterWatcherHead, sdk.monsters.RiverStalkerHead, sdk.monsters.StygianWatcherHead + ].includes(this.classid) && this.mode === sdk.monsters.mode.Spawning) { + return false; + } + + return [sdk.monsters.ThroneBaal, sdk.monsters.Cow/*an evil force*/].indexOf(this.classid) === -1; }); Object.defineProperty(Unit.prototype, "curseable", { - /** @this {Unit} */ - get: function () { - // must be player or monster - if (this === undefined || !copyUnit(this).x || this.type > 1) return false; - // Dead monster - if (this.hp === 0 || this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead) return false; - // attract can't be overridden - if (this.getState(sdk.states.Attract)) return false; - // "Possessed" - if (!!this.name && !!this.name.includes(getLocaleString(sdk.locale.text.Possessed))) return false; - if (this.type === sdk.unittype.Player && getPlayerFlag(me.gid, this.gid, 8) && !this.dead) return true; - // Friendly monster/NPC - if (this.getStat(sdk.stats.Alignment) === 2) return false; - // catapults were returning a level of 0 and hanging up clear scripts - if (this.charlvl < 1) return false; - // Monsters that are in flight - if ([ - sdk.monsters.CarrionBird1, sdk.monsters.UndeadScavenger, sdk.monsters.HellBuzzard, - sdk.monsters.WingedNightmare, sdk.monsters.SoulKiller2/*feel like this one is wrong*/, - sdk.monsters.CarrionBird2].includes(this.classid) && this.mode === sdk.monsters.mode.UsingSkill1) { - return false; - } - // Monsters that are Burrowed/Submerged - if ([ - sdk.monsters.SandMaggot, sdk.monsters.RockWorm, sdk.monsters.Devourer, sdk.monsters.GiantLamprey, sdk.monsters.WorldKiller2, - sdk.monsters.WaterWatcherLimb, sdk.monsters.RiverStalkerLimb, sdk.monsters.StygianWatcherLimb, - sdk.monsters.WaterWatcherHead, sdk.monsters.RiverStalkerHead, sdk.monsters.StygianWatcherHead].includes(this.classid) && this.mode === sdk.monsters.mode.Spawning) { - return false; - } - - return (!this.isMonsterObject && !this.isMonsterEgg && !this.isMonsterNest && !this.isBaalTentacle && [ - sdk.monsters.WaterWatcherLimb, sdk.monsters.WaterWatcherHead, sdk.monsters.Flavie, sdk.monsters.ThroneBaal, sdk.monsters.Cow - ].indexOf(this.classid) === -1); - } + /** @this {Unit} */ + get: function () { + // must be player or monster + if (this === undefined || !copyUnit(this).x || this.type > 1) return false; + // Dead monster + if (this.hp === 0 || this.mode === sdk.monsters.mode.Death || this.mode === sdk.monsters.mode.Dead) return false; + // attract can't be overridden + if (this.getState(sdk.states.Attract)) return false; + // "Possessed" + if (!!this.name && !!this.name.includes(getLocaleString(sdk.locale.text.Possessed))) return false; + if (this.type === sdk.unittype.Player && getPlayerFlag(me.gid, this.gid, 8) && !this.dead) return true; + // Friendly monster/NPC + if (this.getStat(sdk.stats.Alignment) === 2) return false; + // catapults were returning a level of 0 and hanging up clear scripts + if (this.charlvl < 1) return false; + // Monsters that are in flight + if ([ + sdk.monsters.CarrionBird1, sdk.monsters.UndeadScavenger, sdk.monsters.HellBuzzard, + sdk.monsters.WingedNightmare, sdk.monsters.SoulKiller2/*feel like this one is wrong*/, + sdk.monsters.CarrionBird2].includes(this.classid) && this.mode === sdk.monsters.mode.UsingSkill1) { + return false; + } + // Monsters that are Burrowed/Submerged + if ([ + sdk.monsters.SandMaggot, sdk.monsters.RockWorm, sdk.monsters.Devourer, + sdk.monsters.GiantLamprey, sdk.monsters.WorldKiller2, + sdk.monsters.WaterWatcherLimb, sdk.monsters.RiverStalkerLimb, sdk.monsters.StygianWatcherLimb, + sdk.monsters.WaterWatcherHead, sdk.monsters.RiverStalkerHead, sdk.monsters.StygianWatcherHead + ].includes(this.classid) && this.mode === sdk.monsters.mode.Spawning) { + return false; + } + + return (!this.isMonsterObject && !this.isMonsterEgg && !this.isMonsterNest && !this.isBaalTentacle && [ + sdk.monsters.WaterWatcherLimb, sdk.monsters.WaterWatcherHead, + sdk.monsters.Flavie, sdk.monsters.ThroneBaal, sdk.monsters.Cow + ].indexOf(this.classid) === -1); + } }); Unit.prototype.__defineGetter__("scareable", function () { - return this.curseable && !(this.isSpecial) && this.classid !== sdk.monsters.ListerTheTormenter; + return this.curseable && !(this.isSpecial) && this.classid !== sdk.monsters.ListerTheTormenter; }); Unit.prototype.getMobCount = function (range = 10, coll = 0, type = 0, noSpecialMobs = false) { - if (this === undefined) return 0; - const _this = this; - return getUnits(sdk.unittype.Monster) - .filter(function (mon) { - return mon.attackable && getDistance(_this, mon) < range - && (!type || ((type & mon.spectype) && !noSpecialMobs)) - && (!coll || !checkCollision(_this, mon, coll)); - }).length; + if (this === undefined) return 0; + const _this = this; + return getUnits(sdk.unittype.Monster) + .filter(function (mon) { + return mon.attackable && getDistance(_this, mon) < range + && (!type || ((type & mon.spectype) && !noSpecialMobs)) + && (!coll || !checkCollision(_this, mon, coll)); + }).length; }; Unit.prototype.checkForMobs = function (givenSettings = {}) { - if (this === undefined) return 0; - const _this = this; - const settings = Object.assign({ - range: 10, - count: 1, - coll: 0, - spectype: sdk.monsters.spectype.All - }, givenSettings); - let mob = Game.getMonster(); - let count = 0; - if (mob) { - do { - if (getDistance(_this, mob) < settings.range && mob.attackable - && (!settings.spectype || ((settings.spectype & mob.spectype))) - && (!settings.coll || !checkCollision(_this, mob, settings.coll))) { - count++; - } - if (count >= settings.count) { - return true; - } - } while (mob.getNext()); - } - return false; + if (this === undefined) return 0; + const _this = this; + const settings = Object.assign({ + range: 10, + count: 1, + coll: 0, + spectype: sdk.monsters.spectype.All + }, givenSettings); + let mob = Game.getMonster(); + let count = 0; + if (mob) { + do { + if (getDistance(_this, mob) < settings.range && mob.attackable + && (!settings.spectype || ((settings.spectype & mob.spectype))) + && (!settings.coll || !checkCollision(_this, mob, settings.coll))) { + count++; + } + if (count >= settings.count) { + return true; + } + } while (mob.getNext()); + } + return false; }; /** @@ -2232,8 +2287,8 @@ Unit.prototype.checkForMobs = function (givenSettings = {}) { * @returns {boolean} if unit is in specified area */ Unit.prototype.inArea = function (area = 0) { - if (this === undefined) return false; - return this.area === area; + if (this === undefined) return false; + return this.area === area; }; // should this be broken into two functions for item vs unit (player, monster, ect) @@ -2243,28 +2298,28 @@ Unit.prototype.inArea = function (area = 0) { * @returns {boolean} if unit matches the specified classid */ Unit.prototype.isUnit = function (classid = -1) { - if (this === undefined) return false; - return this.classid === classid; + if (this === undefined) return false; + return this.classid === classid; }; Object.defineProperty(Object.prototype, "has", { - writable: true, - enumerable: false, - configurable: true, - value: function (...args) { - if (this === undefined) return undefined; - return this[args[0]] !== undefined - ? typeof this[args[0]] === "function" - ? this[args[0]].apply(this, ([...args].slice(1))) - : this[args[0]] - : {}; - } + writable: true, + enumerable: false, + configurable: true, + value: function (...args) { + if (this === undefined) return undefined; + return this[args[0]] !== undefined + ? typeof this[args[0]] === "function" + ? this[args[0]].apply(this, ([...args].slice(1))) + : this[args[0]] + : {}; + } }); PresetUnit.prototype.realCoords = function () { - return { - area: this.level, // for some reason, preset units names the area "level" - x: this.roomx * 5 + this.x, - y: this.roomy * 5 + this.y, - }; + return { + area: this.level, // for some reason, preset units names the area "level" + x: this.roomx * 5 + this.x, + y: this.roomy * 5 + this.y, + }; }; diff --git a/d2bs/kolbot/libs/core/Runewords.js b/d2bs/kolbot/libs/core/Runewords.js index fec44e132..d6e53d23c 100644 --- a/d2bs/kolbot/libs/core/Runewords.js +++ b/d2bs/kolbot/libs/core/Runewords.js @@ -8,194 +8,194 @@ const Runeword = require("./GameData/RuneData"); const Runewords = { - needList: [], - pickitEntries: [], - validGids: [], - - init: function () { - if (!Config.MakeRunewords) return; - - Runewords.pickitEntries = []; - - // initiate pickit entries - for (let i = 0; i < Config.KeepRunewords.length; i += 1) { - let info = { - file: "Character Config", - line: Config.KeepRunewords[i] - }; - - let parsedLine = NTIP.ParseLineInt(Config.KeepRunewords[i], info); - parsedLine && this.pickitEntries.push(NTIP.ParseLineInt(Config.KeepRunewords[i], 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(); - }, - - /** + needList: [], + pickitEntries: [], + validGids: [], + + init: function () { + if (!Config.MakeRunewords) return; + + Runewords.pickitEntries = []; + + // initiate pickit entries + for (let i = 0; i < Config.KeepRunewords.length; i += 1) { + let info = { + file: "Character Config", + line: Config.KeepRunewords[i] + }; + + let parsedLine = NTIP.ParseLineInt(Config.KeepRunewords[i], info); + parsedLine && this.pickitEntries.push(NTIP.ParseLineInt(Config.KeepRunewords[i], 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(); + }, + + /** * Ensures this item isn't wanted by the CraftingSystem * @param {ItemUnit} item * @returns {boolean} * @todo Why only the crafting system? */ - validItem: function (item) { - return CraftingSystem.validGids.indexOf(item.gid) === -1; - }, + validItem: function (item) { + return CraftingSystem.validGids.indexOf(item.gid) === -1; + }, - /** + /** * build a list of needed runes. won't count runes until the base item is found for a given runeword * @returns {void} */ - buildLists: function () { - Runewords.validGids = []; - Runewords.needList = []; - let baseCheck; - let items = me.findItems(-1, sdk.items.mode.inStorage); - - for (let i = 0; i < Config.Runewords.length; i += 1) { - const [runeword, base, ethFlag] = Config.Runewords[i]; - - if (!baseCheck) { - baseCheck = this.getBase(runeword, base, (ethFlag || 0)) || this.getBase(runeword, base, (ethFlag || 0), true); - } - - if (this.getBase(runeword, base, (ethFlag || 0))) { - RuneLoop: - 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] && this.validItem(items[k])) { - this.validGids.push(items[k].gid); - items.splice(k, 1); - - k -= 1; - - continue RuneLoop; - } - } - - this.needList.push(runeword.runes[j]); - } - } - } - - // hel rune for rerolling purposes - if (baseCheck) { - let hel = me.getItem(sdk.items.runes.Hel, sdk.items.mode.inStorage); - - if (hel) { - do { - if (this.validGids.indexOf(hel.gid) === -1 && this.validItem(hel)) { - this.validGids.push(hel.gid); - - return; - } - } while (hel.getNext()); - } - - this.needList.push(sdk.items.runes.Hel); - } - }, - - /** + buildLists: function () { + Runewords.validGids = []; + Runewords.needList = []; + let baseCheck; + let items = me.findItems(-1, sdk.items.mode.inStorage); + + for (let i = 0; i < Config.Runewords.length; i += 1) { + const [runeword, base, ethFlag] = Config.Runewords[i]; + + if (!baseCheck) { + baseCheck = this.getBase(runeword, base, (ethFlag || 0)) || this.getBase(runeword, base, (ethFlag || 0), true); + } + + if (this.getBase(runeword, base, (ethFlag || 0))) { + RuneLoop: + 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] && this.validItem(items[k])) { + this.validGids.push(items[k].gid); + items.splice(k, 1); + + k -= 1; + + continue RuneLoop; + } + } + + this.needList.push(runeword.runes[j]); + } + } + } + + // hel rune for rerolling purposes + if (baseCheck) { + let hel = me.getItem(sdk.items.runes.Hel, sdk.items.mode.inStorage); + + if (hel) { + do { + if (this.validGids.indexOf(hel.gid) === -1 && this.validItem(hel)) { + this.validGids.push(hel.gid); + + return; + } + } while (hel.getNext()); + } + + this.needList.push(sdk.items.runes.Hel); + } + }, + + /** * @param {number} classid * @param {number} gid */ - update: function (classid, gid) { - for (let i = 0; i < this.needList.length; i += 1) { - if (this.needList[i] === classid) { - this.needList.splice(i, 1); + update: function (classid, gid) { + for (let i = 0; i < this.needList.length; i += 1) { + if (this.needList[i] === classid) { + this.needList.splice(i, 1); - i -= 1; + i -= 1; - break; - } - } + break; + } + } - this.validGids.push(gid); - }, + this.validGids.push(gid); + }, - /** + /** * returns an array of items that make a runeword if found, false if we don't have enough items for any * @returns {ItemUnit[] | boolean} */ - 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); + 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 + 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]; - let base = this.getBase(runeword, wantedBase, (ethFlag || 0)); // check base + const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; + 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; + }, - /** + /** * for pickit * @param {ItemUnit} unit * @returns {boolean} */ - checkItem: function (unit) { - if (!Config.MakeRunewords) return false; - return (unit.itemType === sdk.items.type.Rune && this.needList.includes(unit.classid)); - }, + checkItem: function (unit) { + if (!Config.MakeRunewords) return false; + return (unit.itemType === sdk.items.type.Rune && this.needList.includes(unit.classid)); + }, - /** + /** * for clearInventory - don't drop runes that are a part of runeword recipe * @param {ItemUnit} unit * @returns {boolean} */ - keepItem: function (unit) { - return this.validGids.includes(unit.gid); - }, + keepItem: function (unit) { + return this.validGids.includes(unit.gid); + }, - /** + /** * Get the base item based on classid and runeword recipe * @param {runeword} runeword * @param {ItemUnit | number} base - item or classid @@ -203,157 +203,161 @@ const Runewords = { * @param {boolean} [reroll] - optional reroll argument = gets a runeword that needs rerolling * @returns {ItemUnit | false} */ - 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 + 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()) || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries))) { - if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { - return copyUnit(item); - } - } - } - } while (typeof base !== "object" && item.getNext()); - } + if ((!reroll && !item.getItem()) || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries))) { + if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { + return copyUnit(item); + } + } + } + } while (typeof base !== "object" && item.getNext()); + } - return false; - }, + return false; + }, - /** + /** * @param {ItemUnit} base * @param {ItemUnit} rune * @returns {boolean} */ - socketItem: function (base, rune) { - if (!rune.toCursor()) return false; + socketItem: function (base, rune) { + if (!rune.toCursor()) return false; - for (let i = 0; i < 3; i += 1) { - clickItem(sdk.clicktypes.click.item.Left, base.x, base.y, base.location); + for (let i = 0; i < 3; i += 1) { + clickItem(sdk.clicktypes.click.item.Left, base.x, base.y, base.location); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 2000) { - if (!me.itemoncursor) { - delay(300); + while (getTickCount() - tick < 2000) { + if (!me.itemoncursor) { + delay(300); - return true; - } + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; - }, + return false; + }, - getScroll: function () { - let scroll = me.getItem(sdk.items.ScrollofTownPortal, sdk.items.mode.inStorage); // check if we already have the scroll - if (scroll) return scroll; + getScroll: function () { + let scroll = me.getItem(sdk.items.ScrollofTownPortal, sdk.items.mode.inStorage); // check if we already have the scroll + if (scroll) return scroll; - let npc = Town.initNPC("Shop"); - if (!npc) return false; + let npc = Town.initNPC("Shop"); + if (!npc) return false; - scroll = npc.getItem(sdk.items.ScrollofTownPortal); + scroll = npc.getItem(sdk.items.ScrollofTownPortal); - if (scroll) { - for (let i = 0; i < 3; i += 1) { - scroll.buy(true); + if (scroll) { + for (let i = 0; i < 3; i += 1) { + scroll.buy(true); - if (me.getItem(sdk.items.ScrollofTownPortal)) { - break; - } - } - } + if (me.getItem(sdk.items.ScrollofTownPortal)) { + break; + } + } + } - me.cancel(); + me.cancel(); - return me.getItem(sdk.items.ScrollofTownPortal, sdk.items.mode.inStorage); - }, + return me.getItem(sdk.items.ScrollofTownPortal, sdk.items.mode.inStorage); + }, - makeRunewords: function () { - if (!Config.MakeRunewords) return false; + makeRunewords: function () { + if (!Config.MakeRunewords) return false; - while (true) { - this.buildLists(); + while (true) { + this.buildLists(); - let items = this.checkRunewords(); // get a runeword. format = [base, runes...] + let items = this.checkRunewords(); // get a runeword. format = [base, runes...] - // can't make runewords - exit loop - if (!items) { - break; - } + // can't make runewords - exit loop + if (!items) { + break; + } - if (!Town.openStash()) return false; + if (!Town.openStash()) return false; - for (let i = 1; i < items.length; i += 1) { - this.socketItem(items[0], items[i]); - } + for (let i = 1; i < items.length; i += 1) { + this.socketItem(items[0], items[i]); + } - console.log("ÿc4Runewords: ÿc0Made runeword: " + items[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); - D2Bot.printToConsole("Made runeword: " + items[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""), sdk.colors.D2Bot.Green); + const madeItem = items[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""); - if (NTIP.CheckItem(items[0], this.pickitEntries)) { - Item.logger("Runeword Kept", items[0]); - Item.logItem("Runeword Kept", items[0]); - } - } + console.log("ÿc4Runewords: ÿc0Made runeword: " + madeItem); + D2Bot.printToConsole("Made runeword: " + madeItem, sdk.colors.D2Bot.Green); - me.cancel(); + if (NTIP.CheckItem(items[0], this.pickitEntries)) { + Item.logger("Runeword Kept", items[0]); + Item.logItem("Runeword Kept", items[0]); + } + } - this.rerollRunewords(); + me.cancel(); - return true; - }, + this.rerollRunewords(); - rerollRunewords: function () { - for (let i = 0; i < Config.Runewords.length; i += 1) { - let hel = me.getItem(sdk.items.runes.Hel, sdk.items.mode.inStorage); - if (!hel) return false; + return true; + }, - const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; - let base = this.getBase(runeword, wantedBase, (ethFlag || 0), true); // get a bad runeword + rerollRunewords: function () { + for (let i = 0; i < Config.Runewords.length; i += 1) { + let hel = me.getItem(sdk.items.runes.Hel, sdk.items.mode.inStorage); + if (!hel) return false; - if (base) { - let scroll = this.getScroll(); + const [runeword, wantedBase, ethFlag] = Config.Runewords[i]; + let base = this.getBase(runeword, wantedBase, (ethFlag || 0), true); // get a bad runeword - // 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; + if (base) { + let scroll = this.getScroll(); - // not a fatal error, if the cube can't be emptied, the func will return false on next cycle - if (!Storage.Cube.MoveTo(base) || !Storage.Cube.MoveTo(hel) || !Storage.Cube.MoveTo(scroll)) { - continue; - } + // 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; - // probably only happens on server crash - if (!Cubing.openCube()) return false; + // not a fatal error, if the cube can't be emptied, the func will return false on next cycle + if (!Storage.Cube.MoveTo(base) || !Storage.Cube.MoveTo(hel) || !Storage.Cube.MoveTo(scroll)) { + continue; + } - console.log("ÿc4Runewords: ÿc0Rerolling runeword: " + base.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "")); - D2Bot.printToConsole("Rerolling runeword: " + base.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""), sdk.colors.D2Bot.Green); - transmute(); - delay(500); + // probably only happens on server crash + if (!Cubing.openCube()) return false; - // can't pull the item out = no space = fail - if (!Cubing.emptyCube()) return false; - } - } + let baseRw = base.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""); - this.buildLists(); + console.log("ÿc4Runewords: ÿc0Rerolling runeword: " + baseRw); + D2Bot.printToConsole("Rerolling runeword: " + baseRw, sdk.colors.D2Bot.Green); + transmute(); + delay(500); - while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { - me.cancel(); - delay(300); - } + // can't pull the item out = no space = fail + if (!Cubing.emptyCube()) return false; + } + } - return true; - } + this.buildLists(); + + while (getUIFlag(sdk.uiflags.Cube) || getUIFlag(sdk.uiflags.Stash)) { + me.cancel(); + delay(300); + } + + return true; + } }; diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 4efd3723e..2f62a41c3 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -7,454 +7,480 @@ */ (function () { - const _SkillData = require("./GameData/SkillData"); - - /** - * @todo Move some of the precast functions here - */ - const Skill = { - usePvpRange: false, - charges: [], - needFloor: [ - sdk.skills.Blizzard, sdk.skills.Meteor, sdk.skills.Fissure, sdk.skills.Volcano, sdk.skills.ShockWeb, sdk.skills.LeapAttack, sdk.skills.Hydra - ], - missileSkills: [ - sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.ColdArrow, sdk.skills.MultipleShot, sdk.skills.PoisonJavelin, sdk.skills.ExplodingArrow, - sdk.skills.LightningBolt, sdk.skills.IceArrow, sdk.skills.GuidedArrow, sdk.skills.PlagueJavelin, sdk.skills.Strafe, sdk.skills.ImmolationArrow, - sdk.skills.FreezingArrow, sdk.skills.LightningFury, sdk.skills.ChargedBolt, sdk.skills.IceBolt, sdk.skills.FireBolt, sdk.skills.Inferno, - sdk.skills.IceBlast, sdk.skills.FireBall, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.GlacialSpike, sdk.skills.FrozenOrb, - sdk.skills.Teeth, sdk.skills.BoneSpear, sdk.skills.BoneSpirit, sdk.skills.HolyBolt, sdk.skills.FistoftheHeavens, sdk.skills.DoubleThrow, - sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, sdk.skills.Twister, sdk.skills.Tornado, sdk.skills.FireBlast - ], - - getClassSkillRange: function (classid = me.classid) { - switch (classid) { - case sdk.player.class.Amazon: - return [sdk.skills.MagicArrow, sdk.skills.LightningFury]; - case sdk.player.class.Sorceress: - return [sdk.skills.FireBolt, sdk.skills.ColdMastery]; - case sdk.player.class.Necromancer: - return [sdk.skills.AmplifyDamage, sdk.skills.Revive]; - case sdk.player.class.Paladin: - return [sdk.skills.Sacrifice, sdk.skills.Salvation]; - case sdk.player.class.Barbarian: - return [sdk.skills.Bash, sdk.skills.BattleCommand]; - case sdk.player.class.Druid: - return [sdk.skills.Raven, sdk.skills.Hurricane]; - case sdk.player.class.Assassin: - return [sdk.skills.FireBlast, sdk.skills.PhoenixStrike]; - default: - return [0, 0]; - } - }, - - // initialize our skill data - init: function () { - // reset check values - { - let [min, max] = Skill.getClassSkillRange(); - - for (let i = min; i <= max; i++) { - _SkillData.get(i).reset(); - } - } - // redo cta check - Precast.checkCTA(); - - switch (me.classid) { - case sdk.player.class.Amazon: - break; - case sdk.player.class.Sorceress: - if (Config.UseColdArmor === true) { - Precast.skills.coldArmor.best = (function () { - let coldArmor = [ - { skillId: sdk.skills.ShiverArmor, level: me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) }, - { skillId: sdk.skills.ChillingArmor, level: me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) }, - { skillId: sdk.skills.FrozenArmor, level: me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) }, - ].filter(skill => !!skill.level && skill.level > 0).sort((a, b) => b.level - a.level).first(); - return coldArmor !== undefined ? coldArmor.skillId : -1; - })(); - Precast.skills.coldArmor.duration = this.getDuration(Precast.skills.coldArmor.best); - } else { - Precast.skills.coldArmor.duration = this.getDuration(Config.UseColdArmor); - } - - break; - case sdk.player.class.Necromancer: - { - let bMax = me.getStat(sdk.stats.SkillBoneArmorMax); - bMax > 0 && (Precast.skills.boneArmor.max = bMax); - } - if (!!Config.Golem && Config.Golem !== "None") { - // todo: change Config.Golem to use skillid instead of 0, 1, 2, and 3 - } - break; - case sdk.player.class.Paladin: - // how to handle if someone manually equips a shield during game play, don't want to build entire item list if we don't need to - // maybe store gid of shield, would still require doing me.getItem(-1, 1, gid) everytime we wanted to cast but that's still less involved - // than getting every item we have and finding shield, for now keeping this. Checks during init if we have a shield or not - Precast.skills.holyShield.canUse = me.usingShield(); - - break; - case sdk.player.class.Barbarian: - Skill.canUse(sdk.skills.Shout) && (Precast.skills.shout.duration = this.getDuration(sdk.skills.Shout)); - Skill.canUse(sdk.skills.BattleOrders) && (Precast.skills.battleOrders.duration = this.getDuration(sdk.skills.BattleOrders)); - Skill.canUse(sdk.skills.BattleCommand) && (Precast.skills.battleCommand.duration = this.getDuration(sdk.skills.BattleCommand)); - - break; - case sdk.player.class.Druid: - if (!!Config.SummonAnimal && Config.SummonAnimal !== "None") { - // todo: change Config.SummonAnimal to use skillid instead of 0, 1, 2, and 3 - } - if (!!Config.SummonVine && Config.SummonVine !== "None") { - // todo: change Config.SummonVine to use skillid instead of 0, 1, 2, and 3 - } - if (!!Config.SummonSpirit && Config.SummonSpirit !== "None") { - // todo: change Config.SummonSpirit to use skillid instead of 0, 1, 2, and 3 - } - break; - case sdk.player.class.Assassin: - if (!!Config.SummonShadow) { - // todo: change Config.SummonShadow to use skillid instead of 0, 1, 2, and 3 - } - break; - } - }, - - /** - * @param {number} skillId - * @returns {boolean} - */ - canUse: function (skillId = -1) { - if (!_SkillData.has(skillId)) return false; - if (skillId <= sdk.skills.LeftHandSwing) return true; - return _SkillData.get(skillId).have(); - }, - - /** - * @param {number} skillId - * @returns {number} - */ - getDuration: function (skillId = -1) { - if (!_SkillData.has(skillId)) return 0; - return _SkillData.get(skillId).duration(); - }, - - /** - * @param {number} skillId - * @returns {number} - */ - getMaxSummonCount: function (skillId) { - if (!_SkillData.has(skillId)) return 0; - return _SkillData.get(skillId).summonCount(); - }, - - /** - * @param {number} skillId - * @returns {number} - */ - getRange: function (skillId) { - if (!_SkillData.has(skillId)) return 0; - return _SkillData.get(skillId).range(this.usePvpRange); - }, - - /** - * @param {number} skillId - * @returns {number} - */ - getAoE: function (skillId) { - if (!_SkillData.has(skillId)) return 0; - return _SkillData.get(skillId).AoE(); - }, - - /** - * @param {number} skillId - * @returns {number} - */ - getHand: function (skillId) { - if (!_SkillData.has(skillId)) return -1; - return _SkillData.get(skillId).hand; - }, - - /** - * @param {number} skillId - * @returns {number} - */ - getState: function (skillId) { - if (!_SkillData.has(skillId)) return 0; - return _SkillData.get(skillId).state; - }, - - /** - * @param {number} skillId - * @returns {number} - */ - getCharClass: function (skillId) { - if (!_SkillData.has(skillId)) return 0; - return _SkillData.get(skillId).charClass; - }, - - /** - * Get mana cost of the skill (mBot) - * @param {number} skillId - * @returns {number} - */ - getManaCost: function (skillId) { - if (!_SkillData.has(skillId)) return 0; - if (skillId < sdk.skills.MagicArrow) return 0; - return _SkillData.get(skillId).manaCost(); - }, - - /** - * Timed skills - * @param {number} skillId - * @returns {boolean} - */ - isTimed: function (skillId) { - if (!_SkillData.has(skillId)) return false; - return _SkillData.get(skillId).timed; - }, - - /** - * Skills that cn be cast in town - * @param {number} skillId - * @returns {boolean} - */ - townSkill: function (skillId = -1) { - if (!_SkillData.has(skillId)) return false; - return _SkillData.get(skillId).townSkill; - }, - - /** - * @param {number} skillId - * @returns {boolean} - */ - missileSkill: function (skillId = -1) { - if (!_SkillData.has(skillId)) return false; - return _SkillData.get(skillId).missleSkill; - }, - - /** - * Wereform skill check - * @param {number} skillId - * @returns {number} - */ - wereFormCheck: function (skillId) { - // we don't even have the skills to transform or we aren't transformed - add handler for wereform given by an item that is on switch - if (!Skill.canUse(sdk.skills.Werewolf) && !Skill.canUse(sdk.skills.Werebear)) return true; - const shared = new Set([ - sdk.skills.Attack, sdk.skills.Kick, sdk.skills.Raven, sdk.skills.PoisonCreeper, - sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, sdk.skills.HeartofWolverine, - sdk.skills.SummonDireWolf, sdk.skills.FireClaws, sdk.skills.SolarCreeper, sdk.skills.Hunger, - sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.Armageddon - ]); - const wolfOnly = new Set([sdk.skills.FeralRage, sdk.skills.Rabies, sdk.skills.Fury]); - const bearOnly = new Set([sdk.skills.Maul, sdk.skills.ShockWave]); - - let wolfForm = me.getState(sdk.states.Wearwolf); - if (wolfForm) return shared.has(skillId) || wolfOnly.has(skillId); - - let bearForm = me.getState(sdk.states.Wearbear); - if (bearForm) return shared.has(skillId) || bearOnly.has(skillId); - - // if we are not in either form, we can use any skill - return true; - }, - - // Put a skill on desired slot - setSkill: function (skillId, hand, item) { - // Check if the skill is already set - if (me.getSkill(hand === sdk.skills.hand.Right ? sdk.skills.get.RightId : sdk.skills.get.LeftId) === skillId) return true; - if (!item && !Skill.canUse(skillId)) return false; - - // Charged skills must be cast from right hand - if (hand === undefined || hand === sdk.skills.hand.RightShift || item) { - item && hand !== sdk.skills.hand.Right && console.warn("[ÿc9Warningÿc0] charged skills must be cast from right hand"); - hand = sdk.skills.hand.Right; - } - - return (me.setSkill(skillId, hand, item)); - }, - - // Change into werewolf or werebear - shapeShift: function (mode) { - let [skill, state] = (() => { - switch (mode.toString().toLowerCase()) { - case "0": - return [-1, -1]; - case "1": - case "werewolf": - return [sdk.skills.Werewolf, sdk.states.Wearwolf]; - case "2": - case "werebear": - return [sdk.skills.Werebear, sdk.states.Wearbear]; - default: - throw new Error("shapeShift: Invalid parameter"); - } - })(); - - // don't have wanted skill - if (!Skill.canUse(skill)) return false; - // already in wanted state - if (me.getState(state)) return true; - - let slot = Attack.getPrimarySlot(); - me.switchWeapons(Precast.getBetterSlot(skill)); - - try { - for (let i = 0; i < 3; i += 1) { - Skill.cast(skill, sdk.skills.hand.Right); - - if (Misc.poll(() => me.getState(state), 2000, 50)) { - return true; - } - } - - return false; - } finally { - me.weaponswitch !== slot && me.switchWeapons(slot); - } - }, - - // Change back to human shape - unShift: function () { - let [state, skill] = me.getState(sdk.states.Wearwolf) - ? [sdk.states.Wearwolf, sdk.skills.Werewolf] - : me.getState(sdk.states.Wearbear) - ? [sdk.states.Wearbear, sdk.skills.Werebear] - : [0, 0]; - if (!state) return true; - for (let i = 0; i < 3; i++) { - Skill.cast(skill); - - if (Misc.poll(() => !me.getState(state), 2000, 50)) { - return true; - } - } - - return false; - }, - - /** - * @param {Unit} unit - * @returns {boolean} - */ - useTK: function (unit) { - try { - if (!unit || !Skill.canUse(sdk.skills.Telekinesis) - || typeof unit !== "object" || unit.type !== sdk.unittype.Object - || unit.name.toLowerCase() === "dummy" - || (unit.name.toLowerCase() === "portal" && !me.inTown && unit.classid !== sdk.objects.ArcaneSanctuaryPortal) - || [sdk.objects.RedPortalToAct4, sdk.objects.WorldstonePortal, sdk.objects.RedPortal, sdk.objects.RedPortalToAct5].includes(unit.classid)) { - return false; - } - - return me.inTown || (me.mpPercent > 25); - } catch (e) { - return false; - } - }, - - // Cast a skill on self, Unit or coords - cast: function (skillId, hand, x, y, item) { - if (skillId === undefined) throw new Error("Unit.cast: Must supply a skill ID"); - switch (true) { - case me.inTown && !this.townSkill(skillId): - case !item && (this.getManaCost(skillId) > me.mp || !this.canUse(skillId)): - case !this.wereFormCheck(skillId): - return false; - } - - if (skillId === sdk.skills.Telekinesis && typeof x === "object" && Packet.telekinesis(x)) { - delay(250); - return true; - } - - 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)) { - delay(300); - } - - return false; - } - - if (skillId === sdk.skills.Teleport) { - if (typeof x === "number") { - const orgDist = [x, y].distance; - if (Packet.teleport(x, y)) { - return Misc.poll(() => [x, y].distance < orgDist, 300, 25); - } - } - } - - if (!this.setSkill(skillId, hand, item)) return false; - - if (Config.PacketCasting > 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]; - } - })(); - - 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); - - if (Misc.poll(() => me.attacking, 200, 20)) { - break; - } - } - - while (me.attacking) { - delay(10); - } - } - - // account for lag, state 121 doesn't kick in immediately - if (this.isTimed(skillId)) { - Misc.poll(() => me.skillDelay || [sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode), 100, 10); - } - - return true; - }, - }; - - Object.defineProperties(Skill, { - haveTK: { - get: function () { - return Skill.canUse(sdk.skills.Telekinesis); - }, - }, - }); - - // export to the global scope - global.Skill = Skill; + const _SkillData = require("./GameData/SkillData"); + + /** + * @todo Move some of the precast functions here + */ + const Skill = { + usePvpRange: false, + charges: [], + needFloor: [ + sdk.skills.Blizzard, sdk.skills.Meteor, sdk.skills.Fissure, + sdk.skills.Volcano, sdk.skills.ShockWeb, sdk.skills.LeapAttack, sdk.skills.Hydra + ], + missileSkills: [ + sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.ColdArrow, + sdk.skills.MultipleShot, sdk.skills.PoisonJavelin, sdk.skills.ExplodingArrow, + sdk.skills.LightningBolt, sdk.skills.IceArrow, sdk.skills.GuidedArrow, + sdk.skills.PlagueJavelin, sdk.skills.Strafe, sdk.skills.ImmolationArrow, + sdk.skills.FreezingArrow, sdk.skills.LightningFury, sdk.skills.ChargedBolt, + sdk.skills.IceBolt, sdk.skills.FireBolt, sdk.skills.Inferno, + sdk.skills.IceBlast, sdk.skills.FireBall, sdk.skills.Lightning, + sdk.skills.ChainLightning, sdk.skills.GlacialSpike, sdk.skills.FrozenOrb, + sdk.skills.Teeth, sdk.skills.BoneSpear, sdk.skills.BoneSpirit, + sdk.skills.HolyBolt, sdk.skills.FistoftheHeavens, sdk.skills.DoubleThrow, + sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, + sdk.skills.Twister, sdk.skills.Tornado, sdk.skills.FireBlast + ], + + getClassSkillRange: function (classid = me.classid) { + switch (classid) { + case sdk.player.class.Amazon: + return [sdk.skills.MagicArrow, sdk.skills.LightningFury]; + case sdk.player.class.Sorceress: + return [sdk.skills.FireBolt, sdk.skills.ColdMastery]; + case sdk.player.class.Necromancer: + return [sdk.skills.AmplifyDamage, sdk.skills.Revive]; + case sdk.player.class.Paladin: + return [sdk.skills.Sacrifice, sdk.skills.Salvation]; + case sdk.player.class.Barbarian: + return [sdk.skills.Bash, sdk.skills.BattleCommand]; + case sdk.player.class.Druid: + return [sdk.skills.Raven, sdk.skills.Hurricane]; + case sdk.player.class.Assassin: + return [sdk.skills.FireBlast, sdk.skills.PhoenixStrike]; + default: + return [0, 0]; + } + }, + + // initialize our skill data + init: function () { + // reset check values + { + let [min, max] = Skill.getClassSkillRange(); + + for (let i = min; i <= max; i++) { + _SkillData.get(i).reset(); + } + } + // redo cta check + Precast.checkCTA(); + + switch (me.classid) { + case sdk.player.class.Amazon: + break; + case sdk.player.class.Sorceress: + if (Config.UseColdArmor === true) { + Precast.skills.coldArmor.best = (function () { + let _coldSkill = (id) => ({ skillId: id, level: me.getSkill(id, sdk.skills.subindex.SoftPoints) }); + let coldArmor = [ + _coldSkill(sdk.skills.ShiverArmor), + _coldSkill(sdk.skills.ChillingArmor), + _coldSkill(sdk.skills.FrozenArmor), + ].filter(skill => !!skill.level && skill.level > 0).sort((a, b) => b.level - a.level).first(); + return coldArmor !== undefined ? coldArmor.skillId : -1; + })(); + Precast.skills.coldArmor.duration = this.getDuration(Precast.skills.coldArmor.best); + } else { + Precast.skills.coldArmor.duration = this.getDuration(Config.UseColdArmor); + } + + break; + case sdk.player.class.Necromancer: + { + let bMax = me.getStat(sdk.stats.SkillBoneArmorMax); + bMax > 0 && (Precast.skills.boneArmor.max = bMax); + } + if (!!Config.Golem && Config.Golem !== "None") { + // todo: change Config.Golem to use skillid instead of 0, 1, 2, and 3 + } + break; + case sdk.player.class.Paladin: + // how to handle if someone manually equips a shield during game play, don't want to build entire item list if we don't need to + // maybe store gid of shield, would still require doing me.getItem(-1, 1, gid) everytime we wanted to cast but that's still less involved + // than getting every item we have and finding shield, for now keeping this. Checks during init if we have a shield or not + Precast.skills.holyShield.canUse = me.usingShield(); + + break; + case sdk.player.class.Barbarian: + Skill.canUse(sdk.skills.Shout) && (Precast.skills.shout.duration = this.getDuration(sdk.skills.Shout)); + if (Skill.canUse(sdk.skills.BattleOrders)) { + Precast.skills.battleOrders.duration = this.getDuration(sdk.skills.BattleOrders); + } + if (Skill.canUse(sdk.skills.BattleCommand)) { + Precast.skills.battleCommand.duration = this.getDuration(sdk.skills.BattleCommand); + } + + break; + case sdk.player.class.Druid: + if (!!Config.SummonAnimal && Config.SummonAnimal !== "None") { + // todo: change Config.SummonAnimal to use skillid instead of 0, 1, 2, and 3 + } + if (!!Config.SummonVine && Config.SummonVine !== "None") { + // todo: change Config.SummonVine to use skillid instead of 0, 1, 2, and 3 + } + if (!!Config.SummonSpirit && Config.SummonSpirit !== "None") { + // todo: change Config.SummonSpirit to use skillid instead of 0, 1, 2, and 3 + } + break; + case sdk.player.class.Assassin: + if (!!Config.SummonShadow) { + // todo: change Config.SummonShadow to use skillid instead of 0, 1, 2, and 3 + } + break; + } + }, + + /** + * @param {number} skillId + * @returns {boolean} + */ + canUse: function (skillId = -1) { + if (!_SkillData.has(skillId)) return false; + if (skillId <= sdk.skills.LeftHandSwing) return true; + return _SkillData.get(skillId).have(); + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getDuration: function (skillId = -1) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).duration(); + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getMaxSummonCount: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).summonCount(); + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getRange: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).range(this.usePvpRange); + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getAoE: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).AoE(); + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getHand: function (skillId) { + if (!_SkillData.has(skillId)) return -1; + return _SkillData.get(skillId).hand; + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getState: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).state; + }, + + /** + * @param {number} skillId + * @returns {number} + */ + getCharClass: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).charClass; + }, + + /** + * Get mana cost of the skill (mBot) + * @param {number} skillId + * @returns {number} + */ + getManaCost: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + if (skillId < sdk.skills.MagicArrow) return 0; + return _SkillData.get(skillId).manaCost(); + }, + + /** + * Timed skills + * @param {number} skillId + * @returns {boolean} + */ + isTimed: function (skillId) { + if (!_SkillData.has(skillId)) return false; + return _SkillData.get(skillId).timed; + }, + + /** + * Skills that cn be cast in town + * @param {number} skillId + * @returns {boolean} + */ + townSkill: function (skillId = -1) { + if (!_SkillData.has(skillId)) return false; + return _SkillData.get(skillId).townSkill; + }, + + /** + * @param {number} skillId + * @returns {boolean} + */ + missileSkill: function (skillId = -1) { + if (!_SkillData.has(skillId)) return false; + return _SkillData.get(skillId).missleSkill; + }, + + /** + * Wereform skill check + * @param {number} skillId + * @returns {number} + */ + wereFormCheck: function (skillId) { + // we don't even have the skills to transform or we aren't transformed - add handler for wereform given by an item that is on switch + if (!Skill.canUse(sdk.skills.Werewolf) && !Skill.canUse(sdk.skills.Werebear)) return true; + const shared = new Set([ + sdk.skills.Attack, sdk.skills.Kick, sdk.skills.Raven, sdk.skills.PoisonCreeper, + sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, sdk.skills.HeartofWolverine, + sdk.skills.SummonDireWolf, sdk.skills.FireClaws, sdk.skills.SolarCreeper, sdk.skills.Hunger, + sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.Armageddon + ]); + const wolfOnly = new Set([sdk.skills.FeralRage, sdk.skills.Rabies, sdk.skills.Fury]); + const bearOnly = new Set([sdk.skills.Maul, sdk.skills.ShockWave]); + + let wolfForm = me.getState(sdk.states.Wearwolf); + if (wolfForm) return shared.has(skillId) || wolfOnly.has(skillId); + + let bearForm = me.getState(sdk.states.Wearbear); + if (bearForm) return shared.has(skillId) || bearOnly.has(skillId); + + // if we are not in either form, we can use any skill + return true; + }, + + // Put a skill on desired slot + setSkill: function (skillId, hand, item) { + // Check if the skill is already set + if (me.getSkill(hand === sdk.skills.hand.Right ? sdk.skills.get.RightId : sdk.skills.get.LeftId) === skillId) { + return true; + } + if (!item && !Skill.canUse(skillId)) return false; + + // Charged skills must be cast from right hand + if (hand === undefined || hand === sdk.skills.hand.RightShift || item) { + if (item && hand !== sdk.skills.hand.Right) { + console.warn("[ÿc9Warningÿc0] charged skills must be cast from right hand"); + } + hand = sdk.skills.hand.Right; + } + + return (me.setSkill(skillId, hand, item)); + }, + + // Change into werewolf or werebear + shapeShift: function (mode) { + let [skill, state] = (() => { + switch (mode.toString().toLowerCase()) { + case "0": + return [-1, -1]; + case "1": + case "werewolf": + return [sdk.skills.Werewolf, sdk.states.Wearwolf]; + case "2": + case "werebear": + return [sdk.skills.Werebear, sdk.states.Wearbear]; + default: + throw new Error("shapeShift: Invalid parameter"); + } + })(); + + // don't have wanted skill + if (!Skill.canUse(skill)) return false; + // already in wanted state + if (me.getState(state)) return true; + + let slot = Attack.getPrimarySlot(); + me.switchWeapons(Precast.getBetterSlot(skill)); + + try { + for (let i = 0; i < 3; i += 1) { + Skill.cast(skill, sdk.skills.hand.Right); + + if (Misc.poll(() => me.getState(state), 2000, 50)) { + return true; + } + } + + return false; + } finally { + me.weaponswitch !== slot && me.switchWeapons(slot); + } + }, + + // Change back to human shape + unShift: function () { + let [state, skill] = me.getState(sdk.states.Wearwolf) + ? [sdk.states.Wearwolf, sdk.skills.Werewolf] + : me.getState(sdk.states.Wearbear) + ? [sdk.states.Wearbear, sdk.skills.Werebear] + : [0, 0]; + if (!state) return true; + for (let i = 0; i < 3; i++) { + Skill.cast(skill); + + if (Misc.poll(() => !me.getState(state), 2000, 50)) { + return true; + } + } + + return false; + }, + + /** + * @param {Unit} unit + * @returns {boolean} + */ + useTK: function (unit) { + try { + if (!unit || !Skill.canUse(sdk.skills.Telekinesis) + || typeof unit !== "object" || unit.type !== sdk.unittype.Object + || unit.name.toLowerCase() === "dummy" + || (String.isEqual(unit.name, "portal") + && !me.inTown && unit.classid !== sdk.objects.ArcaneSanctuaryPortal) + || [ + sdk.objects.RedPortalToAct4, sdk.objects.WorldstonePortal, + sdk.objects.RedPortal, sdk.objects.RedPortalToAct5 + ].includes(unit.classid)) { + return false; + } + + return me.inTown || (me.mpPercent > 25); + } catch (e) { + return false; + } + }, + + // Cast a skill on self, Unit or coords + cast: function (skillId, hand, x, y, item) { + if (skillId === undefined) throw new Error("Unit.cast: Must supply a skill ID"); + switch (true) { + case me.inTown && !this.townSkill(skillId): + case !item && (this.getManaCost(skillId) > me.mp || !this.canUse(skillId)): + case !this.wereFormCheck(skillId): + return false; + } + + if (skillId === sdk.skills.Telekinesis && typeof x === "object" && Packet.telekinesis(x)) { + delay(250); + return true; + } + + 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)) { + delay(300); + } + + return false; + } + + if (skillId === sdk.skills.Teleport) { + if (typeof x === "number") { + const orgDist = [x, y].distance; + if (Packet.teleport(x, y)) { + return Misc.poll(() => [x, y].distance < orgDist, 300, 25); + } + } + } + + if (!this.setSkill(skillId, hand, item)) return false; + + if (Config.PacketCasting > 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]; + } + })(); + + 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); + + if (Misc.poll(() => me.attacking, 200, 20)) { + break; + } + } + + while (me.attacking) { + delay(10); + } + } + + // account for lag, state 121 doesn't kick in immediately + if (this.isTimed(skillId)) { + Misc.poll( + () => me.skillDelay || [sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode), + 100, + 10 + ); + } + + return true; + }, + }; + + Object.defineProperties(Skill, { + haveTK: { + get: function () { + return Skill.canUse(sdk.skills.Telekinesis); + }, + }, + }); + + // export to the global scope + global.Skill = Skill; })(); diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index dc4534e83..7cc691c25 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename Storage.js * @author McGod, kolton, esd1, theBGuy @@ -6,686 +7,688 @@ */ (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; - } - } - } - - /** + 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; - }; - - /** + 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; - }; - - /** + 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; + 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) { - // 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) { + // 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; - }; + 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(); + Container.prototype.SortItems = function (itemIdsLeft, itemIdsRight) { + Storage.Reload(); - this.cubeSpot(this.name); + this.cubeSpot(this.name); - itemIdsLeft === undefined && (itemIdsLeft = Config.SortSettings.ItemsSortedFromLeft); - itemIdsRight === undefined && (itemIdsRight = Config.SortSettings.ItemsSortedFromRight); + itemIdsLeft === undefined && (itemIdsLeft = Config.SortSettings.ItemsSortedFromLeft); + itemIdsRight === undefined && (itemIdsRight = Config.SortSettings.ItemsSortedFromRight); - 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, iy] = [item.y, 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, Config.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, Config.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; - }; - - /** + let [ix, iy] = [item.y, 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, Config.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, Config.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; + 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; - } - - 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 (Config.SortSettings.PrioritySorting && priorityClassIds && priorityClassIds.includes(item.classid) + 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; + } + + 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 (Config.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] - - 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; - }; - - /** + 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 + 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 || (Config.SortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 && Config.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 - } - - 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 = !(Config.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 }); - }; - - /** + 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 = !(Config.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; - - // handle opening cube - if (item.location === sdk.storage.Cube/* && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item) */) { - if (!getUIFlag(sdk.uiflags.Cube) && !Cubing.openCube()) return false; - // Cube -> Stash, must place item in inventory first - if (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; - }; - - /** + Container.prototype.MoveToSpot = function (item, mX, mY) { + let cItem, cube; + + // handle opening cube + if (item.location === sdk.storage.Cube/* && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item) */) { + if (!getUIFlag(sdk.uiflags.Cube) && !Cubing.openCube()) return false; + // Cube -> Stash, must place item in inventory first + if (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; + Container.prototype.MoveTo = function (item) { + let nPos; - try { - //Can we even fit it in here? - nPos = this.FindSpot(item); - if (!nPos) return false; + 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 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("ÿc9Storageÿc0: " + this.name + " has used " + this.UsedSpacePercent().toFixed(2) + "% of its total space"); - }; + console.log("ÿc9Storageÿ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; - - 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(); - }; - - /** + 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 : Config.SortSettings.PlugYStash ? 10 : 8; - this.Inventory = new Container("Inventory", 10, 4, 3); - this.TradeScreen = new Container("Inventory", 10, 4, 5); - this.Stash = new Container("Stash", (Config.SortSettings.PlugYStash ? 10 : 6), this.StashY, 7); - this.Belt = new Container("Belt", 4 * this.BeltSize(), 1, 2); - - /** + const Storage = new function () { + this.Init = () => { + this.StashY = me.classic ? 4 : Config.SortSettings.PlugYStash ? 10 : 8; + this.Inventory = new Container("Inventory", 10, 4, 3); + this.TradeScreen = new Container("Inventory", 10, 4, 5); + this.Stash = new Container("Stash", (Config.SortSettings.PlugYStash ? 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; + 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/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index a45fe0ddf..b24148731 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -9,41 +9,41 @@ * @enum {string} */ const NPC = { - Akara: getLocaleString(sdk.locale.npcs.Akara).toLowerCase(), - Gheed: getLocaleString(sdk.locale.npcs.Gheed).toLowerCase(), - Charsi: getLocaleString(sdk.locale.npcs.Charsi).toLowerCase(), - Kashya: getLocaleString(sdk.locale.npcs.Kashya).toLowerCase(), - Warriv: getLocaleString(sdk.locale.npcs.Warriv).toLowerCase(), - - Fara: getLocaleString(sdk.locale.npcs.Fara).toLowerCase(), - Drognan: getLocaleString(sdk.locale.npcs.Drognan).toLowerCase(), - Elzix: getLocaleString(sdk.locale.npcs.Elzix).toLowerCase(), - Greiz: getLocaleString(sdk.locale.npcs.Greiz).toLowerCase(), - Lysander: getLocaleString(sdk.locale.npcs.Lysander).toLowerCase(), - Jerhyn: getLocaleString(sdk.locale.npcs.Jerhyn).toLowerCase(), - Meshif: getLocaleString(sdk.locale.npcs.Meshif).toLowerCase(), - Atma: getLocaleString(sdk.locale.npcs.Atma).toLowerCase(), - - Ormus: getLocaleString(sdk.locale.npcs.Ormus).toLowerCase(), - Alkor: getLocaleString(sdk.locale.npcs.Alkor).toLowerCase(), - Hratli: getLocaleString(sdk.locale.npcs.Hratli).toLowerCase(), - Asheara: getLocaleString(sdk.locale.npcs.Asheara).toLowerCase(), - - Jamella: getLocaleString(sdk.locale.npcs.Jamella).toLowerCase(), - Halbu: getLocaleString(sdk.locale.npcs.Halbu).toLowerCase(), - Tyrael: getLocaleString(sdk.locale.npcs.Tyrael).toLowerCase(), - - Malah: getLocaleString(sdk.locale.npcs.Malah).toLowerCase(), - Anya: getLocaleString(sdk.locale.npcs.Anya).toLowerCase(), - Larzuk: getLocaleString(sdk.locale.npcs.Larzuk).toLowerCase(), - Qual_Kehk: getLocaleString(sdk.locale.npcs.QualKehk).toLowerCase(), - Nihlathak: getLocaleString(sdk.locale.npcs.Nihlathak2).toLowerCase(), - - Cain: getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase() + Akara: getLocaleString(sdk.locale.npcs.Akara).toLowerCase(), + Gheed: getLocaleString(sdk.locale.npcs.Gheed).toLowerCase(), + Charsi: getLocaleString(sdk.locale.npcs.Charsi).toLowerCase(), + Kashya: getLocaleString(sdk.locale.npcs.Kashya).toLowerCase(), + Warriv: getLocaleString(sdk.locale.npcs.Warriv).toLowerCase(), + + Fara: getLocaleString(sdk.locale.npcs.Fara).toLowerCase(), + Drognan: getLocaleString(sdk.locale.npcs.Drognan).toLowerCase(), + Elzix: getLocaleString(sdk.locale.npcs.Elzix).toLowerCase(), + Greiz: getLocaleString(sdk.locale.npcs.Greiz).toLowerCase(), + Lysander: getLocaleString(sdk.locale.npcs.Lysander).toLowerCase(), + Jerhyn: getLocaleString(sdk.locale.npcs.Jerhyn).toLowerCase(), + Meshif: getLocaleString(sdk.locale.npcs.Meshif).toLowerCase(), + Atma: getLocaleString(sdk.locale.npcs.Atma).toLowerCase(), + + Ormus: getLocaleString(sdk.locale.npcs.Ormus).toLowerCase(), + Alkor: getLocaleString(sdk.locale.npcs.Alkor).toLowerCase(), + Hratli: getLocaleString(sdk.locale.npcs.Hratli).toLowerCase(), + Asheara: getLocaleString(sdk.locale.npcs.Asheara).toLowerCase(), + + Jamella: getLocaleString(sdk.locale.npcs.Jamella).toLowerCase(), + Halbu: getLocaleString(sdk.locale.npcs.Halbu).toLowerCase(), + Tyrael: getLocaleString(sdk.locale.npcs.Tyrael).toLowerCase(), + + Malah: getLocaleString(sdk.locale.npcs.Malah).toLowerCase(), + Anya: getLocaleString(sdk.locale.npcs.Anya).toLowerCase(), + Larzuk: getLocaleString(sdk.locale.npcs.Larzuk).toLowerCase(), + Qual_Kehk: getLocaleString(sdk.locale.npcs.QualKehk).toLowerCase(), + Nihlathak: getLocaleString(sdk.locale.npcs.Nihlathak2).toLowerCase(), + + Cain: getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase() }; Object.defineProperty(NPC, "getAct", { - /** + /** * Returns the act(s) where the given NPC can be found. * * @memberof NPC @@ -51,2442 +51,2463 @@ Object.defineProperty(NPC, "getAct", { * @param {string} name - The name of the NPC. * @returns {Array} An array of act numbers where the NPC can be found. */ - value: function (name) { - if (name === NPC.Cain) return [me.act]; - if (name === NPC.Warriv) return [1, 2]; - if (name === NPC.Meshif) return [2, 3]; - switch (true) { - case [NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Warriv].includes(name): - return [1]; - case [NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Greiz, NPC.Lysander, NPC.Jerhyn, NPC.Atma].includes(name): - return [2]; - case [NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara].includes(name): - return [3]; - case [NPC.Jamella, NPC.Halbu, NPC.Tyrael].includes(name): - return [4]; - case [NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Nihlathak].includes(name): - return [5]; - } - return []; - }, - enumerable: false, + value: function (name) { + if (name === NPC.Cain) return [me.act]; + if (name === NPC.Warriv) return [1, 2]; + if (name === NPC.Meshif) return [2, 3]; + switch (true) { + case [NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Warriv].includes(name): + return [1]; + case [NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Greiz, NPC.Lysander, NPC.Jerhyn, NPC.Atma].includes(name): + return [2]; + case [NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara].includes(name): + return [3]; + case [NPC.Jamella, NPC.Halbu, NPC.Tyrael].includes(name): + return [4]; + case [NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Nihlathak].includes(name): + return [5]; + } + return []; + }, + enumerable: false, }); /** * @namespace */ const Town = { - telekinesis: true, - sellTimer: getTickCount(), // shop speedup test - /** + telekinesis: true, + sellTimer: getTickCount(), // shop speedup test + /** * @namespace */ - lastInteractedNPC: { - tick: 0, - /** + lastInteractedNPC: { + tick: 0, + /** * @type {{ name: string, gid: number, area: number}} */ - unit: {}, - /** + unit: {}, + /** * @param {NPCUnit} npc */ - set: function (npc) { - if (npc.hasOwnProperty("name") && Object.values(NPC).includes(npc.name)) { - // valid npc - Town.lastInteractedNPC.unit.name = npc.name.toLowerCase(); - Town.lastInteractedNPC.unit.gid = npc.gid; - Town.lastInteractedNPC.unit.area = me.area; - Town.lastInteractedNPC.tick = getTickCount(); - } - }, - get: function () { - try { - if (!this.unit.hasOwnProperty("name")) return getInteractedNPC(); - if (getTickCount() - this.tick > Time.seconds(15)) return getInteractedNPC(); - if (this.unit.area !== me.area) return getInteractedNPC(); - return this.unit; - } catch (e) { - Config.DebugMode.Town && console.error(e); - this.reset(); - Config.DebugMode.Town && console.debug("getting new npc"); - return getInteractedNPC(); - } - }, - reset: function () { - Town.lastInteractedNPC.unit = {}; - Town.lastInteractedNPC.tick = 0; - } - }, - - tasks: [ - { Heal: NPC.Akara, Shop: NPC.Akara, Gamble: NPC.Gheed, Repair: NPC.Charsi, Merc: NPC.Kashya, Key: NPC.Akara, CainID: NPC.Cain }, - { Heal: NPC.Fara, Shop: NPC.Drognan, Gamble: NPC.Elzix, Repair: NPC.Fara, Merc: NPC.Greiz, Key: NPC.Lysander, CainID: NPC.Cain }, - { Heal: NPC.Ormus, Shop: NPC.Ormus, Gamble: NPC.Alkor, Repair: NPC.Hratli, Merc: NPC.Asheara, Key: NPC.Hratli, CainID: NPC.Cain }, - { Heal: NPC.Jamella, Shop: NPC.Jamella, Gamble: NPC.Jamella, Repair: NPC.Halbu, Merc: NPC.Tyrael, Key: NPC.Jamella, CainID: NPC.Cain }, - { Heal: NPC.Malah, Shop: NPC.Malah, Gamble: NPC.Anya, Repair: NPC.Larzuk, Merc: NPC.Qual_Kehk, Key: NPC.Malah, CainID: NPC.Cain } - ], - - ignoredItemTypes: [ - // Items that won't be stashed - 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 - ], - - /** + set: function (npc) { + if (npc.hasOwnProperty("name") && Object.values(NPC).includes(npc.name)) { + // valid npc + Town.lastInteractedNPC.unit.name = npc.name.toLowerCase(); + Town.lastInteractedNPC.unit.gid = npc.gid; + Town.lastInteractedNPC.unit.area = me.area; + Town.lastInteractedNPC.tick = getTickCount(); + } + }, + get: function () { + try { + if (!this.unit.hasOwnProperty("name")) return getInteractedNPC(); + if (getTickCount() - this.tick > Time.seconds(15)) return getInteractedNPC(); + if (this.unit.area !== me.area) return getInteractedNPC(); + return this.unit; + } catch (e) { + Config.DebugMode.Town && console.error(e); + this.reset(); + Config.DebugMode.Town && console.debug("getting new npc"); + return getInteractedNPC(); + } + }, + reset: function () { + Town.lastInteractedNPC.unit = {}; + Town.lastInteractedNPC.tick = 0; + } + }, + + tasks: (function () { + let _taskObj = (heal, shop, gamble, repair, merc, key) => ( + { Heal: heal, Shop: shop, Gamble: gamble, Repair: repair, Merc: merc, Key: key, CainID: NPC.Cain } + ); + return [ + _taskObj(NPC.Akara, NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Akara), + _taskObj(NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Fara, NPC.Greiz, NPC.Lysander), + _taskObj(NPC.Ormus, NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara, NPC.Hratli), + _taskObj(NPC.Jamella, NPC.Jamella, NPC.Jamella, NPC.Halbu, NPC.Tyrael, NPC.Jamella), + ]; + })(), + + ignoredItemTypes: [ + // Items that won't be stashed + 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 + ], + + /** * @description Check if item type is included in the ignore types list * @param {number} type * @returns {boolean} If it is an item in the list */ - ignoreType: function (type) { - return Town.ignoredItemTypes.includes(type); - }, + ignoreType: function (type) { + return Town.ignoredItemTypes.includes(type); + }, - /** + /** * @param {boolean} repair */ - doChores: function (repair = false) { - delay(250); + doChores: function (repair = false) { + delay(250); - console.time("doChores"); - console.info(true); + console.time("doChores"); + console.info(true); - /** + /** * @todo Pre-build task list so we can more efficiently peform our chores */ - !me.inTown && this.goToTown(); - if (!Misc.poll(() => me.gameReady && me.inTown, 2000, 250)) throw new Error("Failed to go to town for chores"); - - let preAct = me.act; - Pather.allowBroadcast = false; - - // 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); - } - - me.switchWeapons(Attack.getPrimarySlot()); - - this.heal(); - this.identify(); - this.clearInventory(); - this.fillTome(sdk.items.TomeofTownPortal); - this.buyPotions(); - Config.FieldID.Enabled && this.fillTome(sdk.items.TomeofIdentify); - this.shopItems(); - this.buyKeys(); - this.repair(repair); - this.gamble(); - this.reviveMerc(); - Cubing.doCubing(); - Runewords.makeRunewords(); - this.stash(true); - this.checkQuestItems(); - !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); - - me.act !== preAct && this.goToTown(preAct); - me.cancelUIFlags(); - !me.barbarian && Precast.haveCTA === -1 && Precast.doPrecast(false); - - delay(250); - console.info(false, null, "doChores"); - Pather.allowBroadcast = true; - - return true; - }, - - /** + !me.inTown && this.goToTown(); + if (!Misc.poll(() => me.gameReady && me.inTown, 2000, 250)) throw new Error("Failed to go to town for chores"); + + let preAct = me.act; + Pather.allowBroadcast = false; + + // 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); + } + + me.switchWeapons(Attack.getPrimarySlot()); + + this.heal(); + this.identify(); + this.clearInventory(); + this.fillTome(sdk.items.TomeofTownPortal); + this.buyPotions(); + Config.FieldID.Enabled && this.fillTome(sdk.items.TomeofIdentify); + this.shopItems(); + this.buyKeys(); + this.repair(repair); + this.gamble(); + this.reviveMerc(); + Cubing.doCubing(); + Runewords.makeRunewords(); + this.stash(true); + this.checkQuestItems(); + !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); + + me.act !== preAct && this.goToTown(preAct); + me.cancelUIFlags(); + !me.barbarian && Precast.haveCTA === -1 && Precast.doPrecast(false); + + delay(250); + console.info(false, null, "doChores"); + Pather.allowBroadcast = true; + + return true; + }, + + /** * @param {string} name * @param {boolean} cancel * @returns {boolean | Unit} */ - npcInteract: function (name = "", cancel = true) { - // name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); - // what about finding the closest name in case someone mispells it? - let npcKey = Object.keys(NPC).find(key => String.isEqual(key, name)); - if (!npcKey) { - // @todo handle if NPC object key is used instead of common name - console.warn("Couldn't find " + name + " in NPC object"); - return false; - } - const npcName = NPC[npcKey]; - - !me.inTown && Town.goToTown(); + npcInteract: function (name = "", cancel = true) { + // name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); + // what about finding the closest name in case someone mispells it? + let npcKey = Object.keys(NPC).find(key => String.isEqual(key, name)); + if (!npcKey) { + // @todo handle if NPC object key is used instead of common name + console.warn("Couldn't find " + name + " in NPC object"); + return false; + } + const npcName = NPC[npcKey]; + + !me.inTown && Town.goToTown(); - if (!NPC.getAct(npcName).includes(me.act)) { - Town.goToTown(NPC.getAct(npcName)[0]); - } - - me.cancelUIFlags(); - - switch (npcName) { - case NPC.Jerhyn: - !Game.getNPC(NPC.Jerhyn) && Town.move("palace"); - break; - case NPC.Hratli: - if (!me.getQuest(sdk.quest.id.SpokeToHratli, sdk.quest.states.Completed)) { - Town.move(NPC.Meshif); - break; - } - // eslint-disable-next-line no-fallthrough - default: - Town.move(npcName); - } - - let npc = Game.getNPC(npcName); - - // In case Jerhyn is by Warriv - if (npcName === NPC.Jerhyn && !npc) { - me.cancel(); - Pather.moveTo(5166, 5206); - npc = Game.getNPC(npcName); - } - - Packet.flash(me.gid); - delay(40); - - if (npc && npc.openMenu()) { - cancel && me.cancel(); - return npc; - } - - return false; - }, - - /** + if (!NPC.getAct(npcName).includes(me.act)) { + Town.goToTown(NPC.getAct(npcName)[0]); + } + + me.cancelUIFlags(); + + switch (npcName) { + case NPC.Jerhyn: + !Game.getNPC(NPC.Jerhyn) && Town.move("palace"); + break; + case NPC.Hratli: + if (!me.getQuest(sdk.quest.id.SpokeToHratli, sdk.quest.states.Completed)) { + Town.move(NPC.Meshif); + break; + } + // eslint-disable-next-line no-fallthrough + default: + Town.move(npcName); + } + + let npc = Game.getNPC(npcName); + + // In case Jerhyn is by Warriv + if (npcName === NPC.Jerhyn && !npc) { + me.cancel(); + Pather.moveTo(5166, 5206); + npc = Game.getNPC(npcName); + } + + Packet.flash(me.gid); + delay(40); + + if (npc && npc.openMenu()) { + cancel && me.cancel(); + return npc; + } + + return false; + }, + + /** * @description handle quest consumables if we have them */ - checkQuestItems: function () { - // Act 1 - // Tools of the trade - if (!me.smith) { - let malus = me.getItem(sdk.items.quest.HoradricMalus); - !!malus && Town.goToTown(1) && Town.npcInteract("charsi"); - } - - // Act 2 - // Radament skill book - if (!me.radament) { - let book = me.getItem(sdk.quest.item.BookofSkill); - if (book) { - book.isInStash && this.openStash() && delay(300 + me.ping); - book.use(); - } - } - - // Act 3 - // Figurine -> Golden Bird - if (!me.goldenbird) { - if (me.getItem(sdk.quest.item.AJadeFigurine)) { - Town.goToTown(3) && Town.npcInteract("meshif"); - } - - // Golden Bird -> Ashes - if (me.getItem(sdk.items.quest.TheGoldenBird)) { - Town.goToTown(3) && Town.npcInteract("alkor"); - } - - // Potion of life - let pol = me.getItem(sdk.quest.item.PotofLife); - if (pol) { - pol.isInStash && this.openStash() && delay(300 + me.ping); - pol.use(); - } - } - - // LamEssen's Tome - if (!me.lamessen) { - let tome = me.getItem(sdk.quest.item.LamEsensTome); - if (tome) { - !me.inTown && Town.goToTown(3); - tome.isInStash && Town.openStash() && Storage.Inventory.MoveTo(tome); - Town.npcInteract("alkor"); - } - } - - // Scroll of resistance - if (!me.anya) { - let sor = me.getItem(sdk.items.quest.ScrollofResistance); - if (sor) { - sor.isInStash && this.openStash() && delay(300 + me.ping); - sor.use(); - } - } - }, - - /** + checkQuestItems: function () { + // Act 1 + // Tools of the trade + if (!me.smith) { + let malus = me.getItem(sdk.items.quest.HoradricMalus); + !!malus && Town.goToTown(1) && Town.npcInteract("charsi"); + } + + // Act 2 + // Radament skill book + if (!me.radament) { + let book = me.getItem(sdk.quest.item.BookofSkill); + if (book) { + book.isInStash && this.openStash() && delay(300 + me.ping); + book.use(); + } + } + + // Act 3 + // Figurine -> Golden Bird + if (!me.goldenbird) { + if (me.getItem(sdk.quest.item.AJadeFigurine)) { + Town.goToTown(3) && Town.npcInteract("meshif"); + } + + // Golden Bird -> Ashes + if (me.getItem(sdk.items.quest.TheGoldenBird)) { + Town.goToTown(3) && Town.npcInteract("alkor"); + } + + // Potion of life + let pol = me.getItem(sdk.quest.item.PotofLife); + if (pol) { + pol.isInStash && this.openStash() && delay(300 + me.ping); + pol.use(); + } + } + + // LamEssen's Tome + if (!me.lamessen) { + let tome = me.getItem(sdk.quest.item.LamEsensTome); + if (tome) { + !me.inTown && Town.goToTown(3); + tome.isInStash && Town.openStash() && Storage.Inventory.MoveTo(tome); + Town.npcInteract("alkor"); + } + } + + // Scroll of resistance + if (!me.anya) { + let sor = me.getItem(sdk.items.quest.ScrollofResistance); + if (sor) { + sor.isInStash && this.openStash() && delay(300 + me.ping); + sor.use(); + } + } + }, + + /** * @description Start a task and return the NPC Unit * @param {string} task * @param {string} reason * @returns {boolean | Unit} */ - initNPC: function (task = "", reason = "undefined") { - console.time("initNPC"); - console.info(true, reason); - task = task.capitalize(false); + initNPC: function (task = "", reason = "undefined") { + console.time("initNPC"); + console.info(true, reason); + task = task.capitalize(false); - delay(250); + delay(250); - /** @type {NPCUnit} */ - let npc = null; - let wantedNpc = this.tasks[me.act - 1][task] !== undefined ? this.tasks[me.act - 1][task] : "undefined"; - let justUseClosest = (["clearInventory", "sell"].includes(reason) && !me.getUnids().length); + /** @type {NPCUnit} */ + let npc = null; + let wantedNpc = this.tasks[me.act - 1][task] !== undefined ? this.tasks[me.act - 1][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) + 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; - } - } - - // we are just trying to clear our inventory, use the closest npc - // what if we have unid items? Should we use cain if he is closer than the npc with scrolls? - // for now it won't get here with unids - // need to also take into account what our next task is - if (justUseClosest) { - let npcs = this.tasks[me.act - 1]; - npc = getUnits(sdk.unittype.NPC) - .sort((a, b) => a.distance - b.distance) - .find(unit => [npcs.Shop, npcs.Repair].includes(unit.name.toLowerCase())); - } - - 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": - 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; - }, - - /** + me.cancelUIFlags(); + npc = null; + } + } + + // we are just trying to clear our inventory, use the closest npc + // what if we have unid items? Should we use cain if he is closer than the npc with scrolls? + // for now it won't get here with unids + // need to also take into account what our next task is + if (justUseClosest) { + let npcs = this.tasks[me.act - 1]; + npc = getUnits(sdk.unittype.NPC) + .sort((a, b) => a.distance - b.distance) + .find(unit => [npcs.Shop, npcs.Repair].includes(unit.name.toLowerCase())); + } + + 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": + 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; + }, + + /** * @description Go to a town healer if we are below certain hp/mp percent or have a status effect */ - heal: function () { - if (!me.needHealing()) return true; - return !!(this.initNPC("Heal", "heal")); - }, - - buyPotions: function () { - // Ain't got money fo' dat shyt - if (me.gold < 1000) 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; - } - } - - /** + heal: function () { + if (!me.needHealing()) return true; + return !!(this.initNPC("Heal", "heal")); + }, + + buyPotions: function () { + // Ain't got money fo' dat shyt + if (me.gold < 1000) 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; + } + } + + /** * @todo If we are set to cube rejuvs, allow buying potions once we have our gem */ - // 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 - me.normal && me.highestAct >= 4 && me.act < 4 && this.goToTown(4); - - 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) { - let useShift = this.shiftCheck(col, beltSize); - let pot = this.getPotion(npc, Config.BeltColumn[i]); - - if (pot) { - //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); - } else { - for (let j = 0; j < col[i]; j += 1) { - pot.buy(false); - } - } - } - } - - col = this.checkColumns(beltSize); // Re-initialize columns (needed because 1 shift-buy can fill multiple columns) - } + // 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 + me.normal && me.highestAct >= 4 && me.act < 4 && this.goToTown(4); + + 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) { + let useShift = this.shiftCheck(col, beltSize); + let pot = this.getPotion(npc, Config.BeltColumn[i]); + + if (pot) { + //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); + } else { + for (let j = 0; j < col[i]; j += 1) { + pot.buy(false); + } + } + } + } + + 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(); - - if (needBuffer && buffer.hp < Config.HPBuffer) { - for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { - let pot = this.getPotion(npc, "hp"); - !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); - } - } - - if (needBuffer && buffer.mp < Config.MPBuffer) { - for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { - let pot = this.getPotion(npc, "mp"); - !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); - } - } - - return true; - }, - - /** + // re-check + !needBuffer && (Config.HPBuffer > 0 || Config.MPBuffer > 0) && getNeededBuffer(); + + if (needBuffer && buffer.hp < Config.HPBuffer) { + for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { + let pot = this.getPotion(npc, "hp"); + !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); + } + } + + if (needBuffer && buffer.mp < Config.MPBuffer) { + for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { + let pot = this.getPotion(npc, "mp"); + !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); + } + } + + return true; + }, + + /** * @description Check when to shift-buy potions * @param {number} col * @param {0 | 1 | 2 | 3 | 4} beltSize */ - shiftCheck: function (col, beltSize) { - let fillType; - - for (let i = 0; i < col.length; i += 1) { - // Set type based on non-empty column - if (!fillType && col[i] > 0 && col[i] < beltSize) { - fillType = Config.BeltColumn[i]; - } - - if (col[i] >= beltSize) { - switch (Config.BeltColumn[i]) { - case "hp": - !fillType && (fillType = "hp"); - if (fillType !== "hp") return false; - - break; - case "mp": - !fillType && (fillType = "mp"); - if (fillType !== "mp") return false; - - break; - case "rv": // Empty rejuv column = can't shift-buy - return false; - } - } - } - - return true; - }, - - /** + shiftCheck: function (col, beltSize) { + let fillType; + + for (let i = 0; i < col.length; i += 1) { + // Set type based on non-empty column + if (!fillType && col[i] > 0 && col[i] < beltSize) { + fillType = Config.BeltColumn[i]; + } + + if (col[i] >= beltSize) { + switch (Config.BeltColumn[i]) { + case "hp": + !fillType && (fillType = "hp"); + if (fillType !== "hp") return false; + + break; + case "mp": + !fillType && (fillType = "mp"); + if (fillType !== "mp") return false; + + break; + case "rv": // Empty rejuv column = can't shift-buy + return false; + } + } + } + + return true; + }, + + /** * @description Return column status (needed potions in each column) * @param {0 | 1 | 2 | 3 | 4} beltSize * @returns {[number, number, number, number]} */ - checkColumns: function (beltSize) { - (typeof beltSize !== "number" || beltSize < 0 || beltSize > 4) && (beltSize = Storage.BeltSize()); - let col = [beltSize, beltSize, beltSize, beltSize]; - let pot = me.getItem(-1, sdk.items.mode.inBelt); + checkColumns: function (beltSize) { + (typeof beltSize !== "number" || beltSize < 0 || beltSize > 4) && (beltSize = Storage.BeltSize()); + let col = [beltSize, beltSize, beltSize, beltSize]; + let pot = me.getItem(-1, sdk.items.mode.inBelt); - // No potions - if (!pot) return col; + // No potions + if (!pot) return col; - do { - col[pot.x % 4] -= 1; - } while (pot.getNext()); + do { + col[pot.x % 4] -= 1; + } while (pot.getNext()); - return col; - }, + return col; + }, - /** + /** * @description Get the highest potion from current npc * @param {Unit} npc * @param {"hp" | "mp"} type * @param {1 | 2 | 3 | 4 | 5} highestPot * @returns {boolean | ItemUnit} */ - getPotion: function (npc, type, highestPot = 5) { - if (!type) return false; + getPotion: function (npc, type, highestPot = 5) { + if (!type) return false; - if (type === "hp" || type === "mp") { - for (let i = highestPot; i > 0; i -= 1) { - let result = npc.getItem(type + i); + if (type === "hp" || type === "mp") { + for (let i = highestPot; i > 0; i -= 1) { + let result = npc.getItem(type + i); - if (result) { - return result; - } - } - } + if (result) { + return result; + } + } + } - return false; - }, + return false; + }, - /** + /** * @param {number} classid */ - fillTome: function (classid) { - if (me.gold < 450) return false; - if (this.checkScrolls(classid) >= 13) return true; - - let npc = this.initNPC("Shop", "fillTome"); - if (!npc) return false; - - delay(500); - - if (classid === sdk.items.TomeofTownPortal && !me.getTome(sdk.items.TomeofTownPortal)) { - let tome = npc.getItem(sdk.items.TomeofTownPortal); - - if (tome && Storage.Inventory.CanFit(tome)) { - try { - tome.buy(); - } catch (e1) { - console.log(e1); - // Couldn't buy the tome, don't spam the scrolls - return false; - } - } else { - return false; - } - } - - let scroll = npc.getItem(classid === sdk.items.TomeofTownPortal ? sdk.items.ScrollofTownPortal : sdk.items.ScrollofIdentify); - if (!scroll) return false; - - try { - scroll.buy(true); - } catch (e2) { - console.log(e2.message); - - return false; - } - - return true; - }, - - /** + fillTome: function (classid) { + if (me.gold < 450) return false; + if (this.checkScrolls(classid) >= 13) return true; + + let npc = this.initNPC("Shop", "fillTome"); + if (!npc) return false; + + delay(500); + + if (classid === sdk.items.TomeofTownPortal && !me.getTome(sdk.items.TomeofTownPortal)) { + let tome = npc.getItem(sdk.items.TomeofTownPortal); + + if (tome && Storage.Inventory.CanFit(tome)) { + try { + tome.buy(); + } catch (e1) { + console.log(e1); + // Couldn't buy the tome, don't spam the scrolls + return false; + } + } else { + return false; + } + } + + const scrollID = classid === sdk.items.TomeofTownPortal + ? sdk.items.ScrollofTownPortal + : sdk.items.ScrollofIdentify; + let scroll = npc.getItem(scrollID); + if (!scroll) return false; + + try { + scroll.buy(true); + } catch (e2) { + console.log(e2.message); + + return false; + } + + return true; + }, + + /** * @param {number} id * @returns {number} quantity of scrolls in tome */ - checkScrolls: function (id) { - let tome = me.getTome(id); - - if (!tome) { - switch (id) { - case sdk.items.TomeofIdentify: - case "ibk": - return Config.FieldID.Enabled ? 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); - }, - - identify: function () { - !me.inShop && me.cancelUIFlags(); - if (Town.cainID()) return true; + checkScrolls: function (id) { + let tome = me.getTome(id); + + if (!tome) { + switch (id) { + case sdk.items.TomeofIdentify: + case "ibk": + return Config.FieldID.Enabled ? 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); + }, + + identify: function () { + !me.inShop && me.cancelUIFlags(); + if (Town.cainID()) return true; - let list = (Storage.Inventory.Compare(Config.Inventory) || []); - if (list.length === 0) return false; - - // Avoid unnecessary NPC visits - // Only unid items or sellable junk (low level) should trigger a NPC visit - if (!list.some(item => { - const unid = !item.identified; - const results = [Pickit.Result.UNID, Pickit.Result.TRASH]; - return ((unid || Config.LowGold > 0) && (results.includes(Pickit.checkItem(item).result))); - })) { - return false; - } - - 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 && Town.fillTome(sdk.items.TomeofIdentify); + let list = (Storage.Inventory.Compare(Config.Inventory) || []); + if (list.length === 0) return false; + + // Avoid unnecessary NPC visits + // Only unid items or sellable junk (low level) should trigger a NPC visit + if (!list.some(item => { + const unid = !item.identified; + const results = [Pickit.Result.UNID, Pickit.Result.TRASH]; + return ((unid || Config.LowGold > 0) && (results.includes(Pickit.checkItem(item).result))); + })) { + return false; + } + + 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 && Town.fillTome(sdk.items.TomeofIdentify); - MainLoop: - while (list.length > 0) { - const item = list.shift(); + MainLoop: + while (list.length > 0) { + const item = list.shift(); - if (!item.identified && item.isInInventory && !Town.ignoreType(item.itemType)) { - let result = Pickit.checkItem(item); + if (!item.identified && item.isInInventory && !Town.ignoreType(item.itemType)) { + let result = Pickit.checkItem(item); - switch (result.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(); + switch (result.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 = tome ? tome : me.getIdTool(); + break; + case Pickit.Result.UNID: + let idTool = tome ? tome : me.getIdTool(); - if (idTool) { - this.identifyItem(item, idTool); - } else { - let scroll = npc.getItem(sdk.items.ScrollofIdentify); + 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); + if (scroll) { + if (!Storage.Inventory.CanFit(scroll)) { + let tpTome = me.getTome(sdk.items.TomeofTownPortal); - if (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 (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; - } + if (!scroll) { + break MainLoop; + } - this.identifyItem(item, scroll); - } + this.identifyItem(item, scroll); + } - result = Pickit.checkItem(item); + result = Pickit.checkItem(item); - switch (result.result) { - case Pickit.Result.WANTED: - Item.logger("Kept", item); - Item.logItem("Kept", item, result.line); + switch (result.result) { + case Pickit.Result.WANTED: + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); - break; - case Pickit.Result.UNID: - case Pickit.Result.RUNEWORD: // (doesn't trigger normally) - break; - case Pickit.Result.CUBING: - Item.logger("Kept", item, "Cubing-Town"); - Cubing.update(); + break; + case Pickit.Result.UNID: + case Pickit.Result.RUNEWORD: // (doesn't trigger normally) + break; + case Pickit.Result.CUBING: + Item.logger("Kept", item, "Cubing-Town"); + Cubing.update(); - break; - case Pickit.Result.CRAFTING: - Item.logger("Kept", item, "CraftSys-Town"); - CraftingSystem.update(item); + break; + case Pickit.Result.CRAFTING: + Item.logger("Kept", item, "CraftSys-Town"); + CraftingSystem.update(item); - break; - default: - Item.logger("Sold", item); - item.sell(); + break; + default: + Item.logger("Sold", item); + item.sell(); - let timer = getTickCount() - this.sellTimer; // shop speedup test + let timer = getTickCount() - this.sellTimer; // shop speedup test - if (timer > 0 && timer < 500) { - delay(timer); - } + if (timer > 0 && timer < 500) { + delay(timer); + } - break; - } + break; + } - break; - } - } - } + break; + } + } + } - this.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls + this.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls - return true; - }, + return true; + }, - cainID: function () { - // Not enabled or Check if we may use Cain - minimum gold - if (!Config.CainID.Enable || me.gold < Config.CainID.MinGold) return false; + cainID: function () { + // Not enabled or Check if we may use Cain - minimum gold + if (!Config.CainID.Enable || me.gold < Config.CainID.MinGold) return false; - // Check if we're already in a shop. It would be pointless to go to Cain if so. - let npc = getInteractedNPC(); - if (npc && npc.name.toLowerCase() === this.tasks[me.act - 1].Shop) return false; + // Check if we're already in a shop. It would be pointless to go to Cain if so. + let npc = getInteractedNPC(); + if (npc && npc.name.toLowerCase() === this.tasks[me.act - 1].Shop) return false; - me.cancel(); - this.stash(false); + me.cancel(); + this.stash(false); - let unids = me.getUnids(); + let unids = me.getUnids(); - if (unids) { - // Check if we may use Cain - number of unid items - if (unids.length < Config.CainID.MinUnids) return false; + if (unids) { + // Check if we may use Cain - number of unid items + if (unids.length < Config.CainID.MinUnids) return false; - // Check if we may use Cain - kept unid items - for (let i = 0; i < unids.length; i += 1) { - if (Pickit.checkItem(unids[i]).result > 0) return false; - } + // Check if we may use Cain - kept unid items + for (let i = 0; i < unids.length; i += 1) { + if (Pickit.checkItem(unids[i]).result > 0) return false; + } - let cain = this.initNPC("CainID", "cainID"); - if (!cain) return false; + let cain = this.initNPC("CainID", "cainID"); + if (!cain) return false; - for (let i = 0; i < unids.length; i += 1) { - let result = Pickit.checkItem(unids[i]); + for (let i = 0; i < unids.length; i += 1) { + let result = Pickit.checkItem(unids[i]); - switch (result.result) { - case Pickit.Result.UNWANTED: - Item.logger("Dropped", unids[i], "cainID"); - unids[i].drop(); + switch (result.result) { + case Pickit.Result.UNWANTED: + Item.logger("Dropped", unids[i], "cainID"); + unids[i].drop(); - break; - case Pickit.Result.WANTED: - Item.logger("Kept", unids[i]); - Item.logItem("Kept", unids[i], result.line); + break; + case Pickit.Result.WANTED: + Item.logger("Kept", unids[i]); + Item.logItem("Kept", unids[i], result.line); - break; - default: - break; - } - } - } + break; + default: + break; + } + } + } - return true; - }, + return true; + }, - /** + /** * @param {ItemUnit} unit * @param {ItemUnit} tome * @param {Boolean} packetID * @returns {boolean} */ - identifyItem: function (unit, tome, packetID = false) { - if (Config.PacketShopping || packetID) return Packet.identifyItem(unit, tome); - if (!unit || unit.identified) return false; + identifyItem: function (unit, tome, packetID = false) { + if (Config.PacketShopping || packetID) return Packet.identifyItem(unit, tome); + if (!unit || unit.identified) return false; - Town.sellTimer = getTickCount(); // shop speedup test + Town.sellTimer = getTickCount(); // shop speedup test - CursorLoop: - for (let i = 0; i < 3; i += 1) { - clickItem(sdk.clicktypes.click.item.Right, tome); + CursorLoop: + for (let i = 0; i < 3; i += 1) { + clickItem(sdk.clicktypes.click.item.Right, tome); - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 500) { - if (getCursorType() === sdk.cursortype.Identify) { - break CursorLoop; - } + while (getTickCount() - tick < 500) { + if (getCursorType() === sdk.cursortype.Identify) { + break CursorLoop; + } - delay(10); - } - } + delay(10); + } + } - if (getCursorType() !== sdk.cursortype.Identify) return false; + if (getCursorType() !== sdk.cursortype.Identify) return false; - delay(270); + delay(270); - for (let i = 0; i < 3; i += 1) { - if (getCursorType() === sdk.cursortype.Identify) { - clickItem(sdk.clicktypes.click.item.Left, unit); - } + for (let i = 0; i < 3; i += 1) { + if (getCursorType() === sdk.cursortype.Identify) { + clickItem(sdk.clicktypes.click.item.Left, unit); + } - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < 500) { - if (unit.identified) { - delay(50); + while (getTickCount() - tick < 500) { + if (unit.identified) { + delay(50); - return true; - } + return true; + } - delay(10); - } + delay(10); + } - delay(300); - } + delay(300); + } - return false; - }, + return false; + }, - shopItems: function () { - if (!Config.MiniShopBot) return true; + shopItems: function () { + if (!Config.MiniShopBot) return true; - let npc = getInteractedNPC(); - if (!npc || !npc.itemcount) return false; - - let items = npc.getItemsEx().filter((item) => !Town.ignoreType(item.itemType)); - if (!items.length) return false; - - console.log("ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); - - for (let i = 0; i < items.length; i += 1) { - let item = items[i]; - let result = Pickit.checkItem(item); - - switch (result.result) { - case Pickit.Result.WANTED: - case Pickit.Result.CUBING: - case Pickit.Result.CRAFTING: - case Pickit.Result.RUNEWORD: - try { - if (Storage.Inventory.CanFit(item) && me.gold >= item.getItemCost(sdk.items.cost.ToBuy)) { - Item.logger("Shopped", item); - Item.logItem("Shopped", item, result.line); - item.buy(); - } - } catch (e) { - console.error(e); - } - } - - delay(2); - } - - return true; - }, - - /** + let npc = getInteractedNPC(); + if (!npc || !npc.itemcount) return false; + + let items = npc.getItemsEx().filter((item) => !Town.ignoreType(item.itemType)); + if (!items.length) return false; + + console.log("ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); + + for (let i = 0; i < items.length; i += 1) { + let item = items[i]; + let result = Pickit.checkItem(item); + + switch (result.result) { + case Pickit.Result.WANTED: + case Pickit.Result.CUBING: + case Pickit.Result.CRAFTING: + case Pickit.Result.RUNEWORD: + try { + if (Storage.Inventory.CanFit(item) && me.gold >= item.getItemCost(sdk.items.cost.ToBuy)) { + Item.logger("Shopped", item); + Item.logItem("Shopped", item, result.line); + item.buy(); + } + } catch (e) { + console.error(e); + } + } + + delay(2); + } + + return true; + }, + + /** * @type {number[]} */ - gambleIds: [], - - gamble: function () { - if (!this.needGamble() || Config.GambleItems.length === 0) return true; - 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(2); - let npc = this.initNPC("Gamble", "gamble"); - - if (!npc) return false; - - let list = []; - 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 { - if (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; - } - - me.overhead("Buy: " + items[i].name); - 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"); - me.overhead("Sell: " + newItem.name); - newItem.sell(); - - if (!Config.PacketShopping) { - delay(500); - } - - break; - } - } - } - } - - me.cancel(); - } - - return true; - }, - - needGamble: function () { - return Config.Gamble && me.gold >= Config.GambleGoldStart; - }, - - /** + gambleIds: [], + + gamble: function () { + if (!this.needGamble() || Config.GambleItems.length === 0) return true; + 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(2); + let npc = this.initNPC("Gamble", "gamble"); + + if (!npc) return false; + + let list = []; + 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 { + if (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; + } + + me.overhead("Buy: " + items[i].name); + 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"); + me.overhead("Sell: " + newItem.name); + newItem.sell(); + + if (!Config.PacketShopping) { + delay(500); + } + + break; + } + } + } + } + + me.cancel(); + } + + return true; + }, + + needGamble: function () { + return Config.Gamble && me.gold >= Config.GambleGoldStart; + }, + + /** * @param {ItemUnit[]} list */ - getGambledItem: function (list = []) { - let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + getGambledItem: function (list = []) { + let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - for (let i = 0; i < items.length; i += 1) { - if (list.indexOf(items[i].gid) === -1) { - for (let j = 0; j < 3; j += 1) { - if (items[i].identified) { - break; - } + for (let i = 0; i < items.length; i += 1) { + if (list.indexOf(items[i].gid) === -1) { + for (let j = 0; j < 3; j += 1) { + if (items[i].identified) { + break; + } - delay(100); - } + delay(100); + } - return items[i]; - } - } + return items[i]; + } + } - return false; - }, + return false; + }, - /** + /** * @param {number} quantity * @param {number | string} type * @param {boolean} [drink=false] * @param {boolean} [force=false] * @param {Unit} [npc=null] */ - buyPots: function (quantity = 0, type = undefined, drink = false, force = false, npc = null) { - if (!quantity || !type) return false; + buyPots: function (quantity = 0, type = undefined, drink = false, force = false, npc = null) { + if (!quantity || !type) return false; - // convert to classid if isn't one - typeof type === "string" && (type = (sdk.items[type.capitalize(true) + "Potion"] || false)); - if (!type) return false; - - // todo - change act in a3 if we are next to the wp as it's faster than going all the way to Alkor - // todo - compare distance Ormus -> Alkor compared to Ormus -> WP -> Akara - let potDealer = ["Akara", "Lysander", "Alkor", "Jamella", "Malah"][me.act - 1]; - - switch (type) { - case sdk.items.ThawingPotion: - // Don't buy if already at max res - if (!force && me.coldRes >= 75) return true; - console.info(null, "Current cold resistance: " + me.coldRes); - - break; - case sdk.items.AntidotePotion: - // Don't buy if already at max res - if (!force && me.poisonRes >= 75) return true; - console.info(null, "Current poison resistance: " + me.poisonRes); - - break; - case sdk.items.StaminaPotion: - // Don't buy if teleport or vigor - if (!force && (Skill.canUse(sdk.skills.Vigor) || Pather.canTeleport())) return true; - - break; - } - - try { - if (!!npc && npc.name.toLowerCase() === NPC[potDealer] && !getUIFlag(sdk.uiflags.Shop)) { - if (!npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); - } else { - me.cancelUIFlags(); - Town.move(NPC[potDealer]); - npc = Game.getNPC(NPC[potDealer]); - - if (!npc || !npc.openMenu() || !npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); - } - } catch (e) { - console.error(e); - - return false; - } - - let pot = npc.getItem(type); - if (!pot) { - console.warn("Couldn't find " + type + " from " + npc.name); - return false; - } - let name = (pot.name || ""); - - console.info(null, "Buying " + quantity + " " + name + "s"); - - for (let pots = 0; pots < quantity; pots++) { - if (!!pot && Storage.Inventory.CanFit(pot)) { - Packet.buyItem(pot, false); - } - } - - me.cancelUIFlags(); - drink && Town.drinkPots(type); - - return true; - }, - - /** + // convert to classid if isn't one + typeof type === "string" && (type = (sdk.items[type.capitalize(true) + "Potion"] || false)); + if (!type) return false; + + // can't buy pots if we are full + if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 1 })) return false; + + // todo - change act in a3 if we are next to the wp as it's faster than going all the way to Alkor + // todo - compare distance Ormus -> Alkor compared to Ormus -> WP -> Akara + let potDealer = ["Akara", "Lysander", "Alkor", "Jamella", "Malah"][me.act - 1]; + + switch (type) { + case sdk.items.ThawingPotion: + // Don't buy if already at max res + if (!force && me.coldRes >= 75) return true; + console.info(null, "Current cold resistance: " + me.coldRes); + + break; + case sdk.items.AntidotePotion: + // Don't buy if already at max res + if (!force && me.poisonRes >= 75) return true; + console.info(null, "Current poison resistance: " + me.poisonRes); + + break; + case sdk.items.StaminaPotion: + // Don't buy if teleport or vigor + if (!force && (Skill.canUse(sdk.skills.Vigor) || Pather.canTeleport())) return true; + + break; + } + + try { + if (!!npc && npc.name.toLowerCase() === NPC[potDealer] && !getUIFlag(sdk.uiflags.Shop)) { + if (!npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); + } else { + me.cancelUIFlags(); + Town.move(NPC[potDealer]); + npc = Game.getNPC(NPC[potDealer]); + + if (!npc || !npc.openMenu() || !npc.startTrade("Shop")) { + throw new Error("Failed to open " + npc.name + " trade menu"); + } + } + } catch (e) { + console.error(e); + + return false; + } + + let pot = npc.getItem(type); + if (!pot) { + console.warn("Couldn't find " + type + " from " + npc.name); + return false; + } + let name = (pot.name || ""); + + console.info(null, "Buying " + quantity + " " + name + "s"); + + for (let pots = 0; pots < quantity; pots++) { + if (!!pot && Storage.Inventory.CanFit(pot)) { + Packet.buyItem(pot, false); + } + } + + me.cancelUIFlags(); + drink && Town.drinkPots(type); + + return true; + }, + + /** * @param {number | string} type * @param {boolean} [log=true] * @returns {{ potName: string, quantity: number }} */ - drinkPots: function (type = undefined, log = true) { - // convert to classid if isn't one - typeof type === "string" && (type = (sdk.items[type.capitalize(true) + "Potion"] || false)); - - let name = ""; - let quantity = 0; - let chugs = me.getItemsEx(type).filter(pot => pot.isInInventory); - - if (chugs.length > 0) { - name = chugs.first().name; - chugs.forEach(function (pot) { - if (!!pot && pot.use()) { - quantity++; - } - }); - - log && name && console.info(null, "Drank " + quantity + " " + name + "s. Timer [" + Time.format(quantity * 30 * 1000) + "]"); - } else { - console.warn("couldn't find my pots"); - } - - return { - potName: name, - quantity: quantity - }; - }, - - buyKeys: function () { - if (!this.wantKeys()) return true; - - // avoid Hratli - me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); - - let npc = this.initNPC("Key", "buyKeys"); - if (!npc) return false; - - let key = npc.getItem("key"); - if (!key) return false; - - try { - key.buy(true); - } catch (e) { - console.error(e); - - return false; - } - - return true; - }, - - checkKeys: function () { - if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { - return 12; - } - - let count = 0; - let key = me.findItems(sdk.items.Key, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (key) { - for (let i = 0; i < key.length; i += 1) { - count += key[i].getStat(sdk.stats.Quantity); - } - } - - return count; - }, - - needKeys: function () { - return this.checkKeys() <= 0; - }, - - wantKeys: function () { - return this.checkKeys() <= 6; - }, - - /** + drinkPots: function (type = undefined, log = true) { + // convert to classid if isn't one + typeof type === "string" && (type = (sdk.items[type.capitalize(true) + "Potion"] || false)); + + let name = ""; + let quantity = 0; + let chugs = me.getItemsEx(type).filter(pot => pot.isInInventory); + + if (chugs.length > 0) { + name = chugs.first().name; + chugs.forEach(function (pot) { + if (!!pot && pot.use()) { + quantity++; + } + }); + + if (log && name) { + console.info(null, "Drank " + quantity + " " + name + "s. Timer [" + Time.format(quantity * 30 * 1000) + "]"); + } + } else { + console.warn("couldn't find my pots"); + } + + return { + potName: name, + quantity: quantity + }; + }, + + buyKeys: function () { + if (!this.wantKeys()) return true; + + // avoid Hratli + me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); + + let npc = this.initNPC("Key", "buyKeys"); + if (!npc) return false; + + let key = npc.getItem("key"); + if (!key) return false; + + try { + key.buy(true); + } catch (e) { + console.error(e); + + return false; + } + + return true; + }, + + checkKeys: function () { + if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 + || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { + return 12; + } + + return me.getItemsEx() + .filter(item => item.classid === sdk.items.Key && item.isInInventory) + .reduce((acc, curr) => acc + curr.getStat(sdk.stats.Quantity), 0); + }, + + needKeys: function () { + return this.checkKeys() <= 0; + }, + + wantKeys: function () { + return this.checkKeys() <= 6; + }, + + /** * @param {ItemUnit} item - Rune */ - repairIngredientCheck: function (item) { - if (!Config.CubeRepair) return false; - - let [have, needRal, needOrt] = [0, 0, 0]; - let items = this.getItemsForRepair(Config.RepairPercent, false); - - if (items.length) { - while (items.length > 0) { - switch (items.shift().itemType) { - case sdk.items.type.Shield: - case sdk.items.type.Armor: - case sdk.items.type.Boots: - case sdk.items.type.Gloves: - case sdk.items.type.Belt: - case sdk.items.type.VoodooHeads: - case sdk.items.type.AuricShields: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Pelt: - case sdk.items.type.Circlet: - needRal += 1; - - break; - default: - needOrt += 1; - - break; - } - } - } - - switch (item.classid) { - case sdk.items.runes.Ral: - needRal && (have = me.findItems(sdk.items.runes.Ral).length); - - return (!have || have < needRal); - case sdk.items.runes.Ort: - needOrt && (have = me.findItems(sdk.items.runes.Ort).length); - - return (!have || have < needOrt); - } - - return false; - }, - - cubeRepair: function () { - if (!Config.CubeRepair || !me.cube) return false; - - let items = this.getItemsForRepair(Config.RepairPercent, false) - .sort((a, b) => a.durabilityPercent - b.durabilityPercent); - - while (items.length > 0) { - this.cubeRepairItem(items.shift()); - } - - return true; - }, - - /** + repairIngredientCheck: function (item) { + if (!Config.CubeRepair) return false; + + let [have, needRal, needOrt] = [0, 0, 0]; + let items = this.getItemsForRepair(Config.RepairPercent, false); + + if (items.length) { + while (items.length > 0) { + switch (items.shift().itemType) { + case sdk.items.type.Shield: + case sdk.items.type.Armor: + case sdk.items.type.Boots: + case sdk.items.type.Gloves: + case sdk.items.type.Belt: + case sdk.items.type.VoodooHeads: + case sdk.items.type.AuricShields: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Pelt: + case sdk.items.type.Circlet: + needRal += 1; + + break; + default: + needOrt += 1; + + break; + } + } + } + + switch (item.classid) { + case sdk.items.runes.Ral: + needRal && (have = me.findItems(sdk.items.runes.Ral).length); + + return (!have || have < needRal); + case sdk.items.runes.Ort: + needOrt && (have = me.findItems(sdk.items.runes.Ort).length); + + return (!have || have < needOrt); + } + + return false; + }, + + cubeRepair: function () { + if (!Config.CubeRepair || !me.cube) return false; + + let items = this.getItemsForRepair(Config.RepairPercent, false) + .sort((a, b) => a.durabilityPercent - b.durabilityPercent); + + while (items.length > 0) { + this.cubeRepairItem(items.shift()); + } + + return true; + }, + + /** * @param {ItemUnit} item * @returns {boolean} */ - cubeRepairItem: function (item) { - if (!item.isInStorage) return false; - - let rune, cubeItems; - let bodyLoc = item.bodylocation; - - switch (item.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.Armor: - case sdk.items.type.Boots: - case sdk.items.type.Gloves: - case sdk.items.type.Belt: - case sdk.items.type.VoodooHeads: - case sdk.items.type.AuricShields: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Pelt: - case sdk.items.type.Circlet: - rune = me.getItem(sdk.items.runes.Ral); - - break; - default: - rune = me.getItem(sdk.items.runes.Ort); - - break; - } - - if (rune && Town.openStash() && Cubing.openCube() && Cubing.emptyCube()) { - for (let i = 0; i < 100; i += 1) { - if (!me.itemoncursor) { - if (Storage.Cube.MoveTo(item) && Storage.Cube.MoveTo(rune)) { - transmute(); - delay(1000 + me.ping); - } - - cubeItems = me.findItems(-1, -1, sdk.storage.Cube); // Get cube contents - - // We expect only one item in cube - cubeItems.length === 1 && cubeItems[0].toCursor(); - } - - if (me.itemoncursor) { - for (let i = 0; i < 3; i += 1) { - clickItem(sdk.clicktypes.click.item.Left, bodyLoc); - delay(me.ping * 2 + 500); - - if (cubeItems[0].bodylocation === bodyLoc) { - console.log(cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim() + " successfully repaired and equipped."); - D2Bot.console.logToConsole(cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim() + " successfully repaired and equipped.", sdk.colors.D2Bot.Green); - - return true; - } - } - } - - delay(200); - } - - Misc.errorReport("Failed to put repaired item back on."); - D2Bot.stop(); - } - - return false; - }, - - /** + cubeRepairItem: function (item) { + if (!item.isInStorage) return false; + + let rune, cubeItems; + let bodyLoc = item.bodylocation; + + switch (item.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.Armor: + case sdk.items.type.Boots: + case sdk.items.type.Gloves: + case sdk.items.type.Belt: + case sdk.items.type.VoodooHeads: + case sdk.items.type.AuricShields: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Pelt: + case sdk.items.type.Circlet: + rune = me.getItem(sdk.items.runes.Ral); + + break; + default: + rune = me.getItem(sdk.items.runes.Ort); + + break; + } + + if (rune && Town.openStash() && Cubing.openCube() && Cubing.emptyCube()) { + for (let i = 0; i < 100; i += 1) { + if (!me.itemoncursor) { + if (Storage.Cube.MoveTo(item) && Storage.Cube.MoveTo(rune)) { + transmute(); + delay(1000 + me.ping); + } + + cubeItems = me.findItems(-1, -1, sdk.storage.Cube); // Get cube contents + + // We expect only one item in cube + cubeItems.length === 1 && cubeItems[0].toCursor(); + } + + if (me.itemoncursor) { + for (let i = 0; i < 3; i += 1) { + clickItem(sdk.clicktypes.click.item.Left, bodyLoc); + delay(me.ping * 2 + 500); + + if (cubeItems[0].bodylocation === bodyLoc) { + let cubeItem = cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim(); + console.log(cubeItem + " successfully repaired and equipped."); + D2Bot.console.logToConsole(cubeItem + " successfully repaired and equipped.", sdk.colors.D2Bot.Green); + + return true; + } + } + } + + delay(200); + } + + Misc.errorReport("Failed to put repaired item back on."); + D2Bot.stop(); + } + + return false; + }, + + /** * @param {boolean} [force=false] */ - 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 true; - - 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(); - - if (bowCheck) { - let quiver = bowCheck === "bow" ? "aqv" : "cqv"; - let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); - !!myQuiver && myQuiver.drop(); + 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 true; + + 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(); + + if (bowCheck) { + let quiver = bowCheck === "bow" ? "aqv" : "cqv"; + let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); + !!myQuiver && myQuiver.drop(); - npc = this.initNPC("Repair", "repair"); - if (!npc) return false; - - quiver = npc.getItem(quiver); - !!quiver && quiver.buy(); - } - - break; - } - } - - return true; - }, - - needRepair: function () { - let quiver, repairAction = []; - let canAfford = me.gold >= me.getRepairCost(); - - // Arrow/Bolt check - let bowCheck = Attack.usingBow(); - - if (bowCheck) { - const quiverType = { bow: "aqv", crossbow: "cqv" }; - if (quiverType[bowCheck]) { - quiver = me.getItem(quiverType[bowCheck], sdk.items.mode.Equipped); - } - - if (!quiver) { // Out of arrows/bolts - repairAction.push("buyQuiver"); - } else { - let quantity = quiver.getStat(sdk.stats.Quantity); - - if (typeof quantity === "number" && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { - repairAction.push("buyQuiver"); - } - } - } - - // Repair durability/quantity/charges - if (canAfford) { - if (this.getItemsForRepair(Config.RepairPercent, true).length > 0) { - repairAction.push("repair"); - } - } else { - console.warn("Can't afford repairs."); - } - - return repairAction; - }, - - /** + npc = this.initNPC("Repair", "repair"); + if (!npc) return false; + + quiver = npc.getItem(quiver); + !!quiver && quiver.buy(); + } + + break; + } + } + + return true; + }, + + needRepair: function () { + let quiver, repairAction = []; + let canAfford = me.gold >= me.getRepairCost(); + + // Arrow/Bolt check + let bowCheck = Attack.usingBow(); + + if (bowCheck) { + const quiverType = { bow: "aqv", crossbow: "cqv" }; + if (quiverType[bowCheck]) { + quiver = me.getItem(quiverType[bowCheck], sdk.items.mode.Equipped); + } + + if (!quiver) { // Out of arrows/bolts + repairAction.push("buyQuiver"); + } else { + let quantity = quiver.getStat(sdk.stats.Quantity); + + if (typeof quantity === "number" + && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { + repairAction.push("buyQuiver"); + } + } + } + + // Repair durability/quantity/charges + if (canAfford) { + if (this.getItemsForRepair(Config.RepairPercent, true).length > 0) { + repairAction.push("repair"); + } + } else { + console.warn("Can't afford repairs."); + } + + return repairAction; + }, + + /** * @param {number} repairPercent * @param {boolean} chargedItems * @returns {ItemUnit[]} */ - getItemsForRepair: function (repairPercent, chargedItems) { - let itemList = []; - let item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - // 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; - // Durability check - default: - 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; - }, - - reviveMerc: function () { - if (!this.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(); - - 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")) { - return false; - } - } - - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (!!me.getMerc()) { - delay(Math.max(750, me.ping * 2)); - - break MainLoop; - } - - delay(200); - } - } - - Attack.checkInfinity(); - - if (!!me.getMerc()) { - // 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; - }, - - needMerc: function () { - 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.getMerc(); - - if (!!merc && !merc.dead) { - return false; - } - - delay(100); - } - - // In case we never had a merc and Config.UseMerc is still set to true for some odd reason - return true; - }, - - /** + getItemsForRepair: function (repairPercent, chargedItems) { + let itemList = []; + let item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + // 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") { + let _maxStack = (getBaseStat("items", item.classid, "maxstack") + item.getStat(sdk.stats.ExtraStack)); + if (quantity * 100 / _maxStack <= repairPercent) { + itemList.push(copyUnit(item)); + } + } + + break; + // Durability check + default: + 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; + }, + + reviveMerc: function () { + if (!this.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(); + + 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")) { + return false; + } + } + + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (!!me.getMerc()) { + delay(Math.max(750, me.ping * 2)); + + break MainLoop; + } + + delay(200); + } + } + + Attack.checkInfinity(); + + if (!!me.getMerc()) { + // 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; + }, + + needMerc: function () { + 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.getMerc(); + + if (!!merc && !merc.dead) { + return false; + } + + delay(100); + } + + // In case we never had a merc and Config.UseMerc is still set to true for some odd reason + return true; + }, + + /** * @param {ItemUnit} item */ - canStash: function (item) { - if (Town.ignoreType(item.itemType) + canStash: function (item) { + if (Town.ignoreType(item.itemType) || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) || !Town.canStashGem(item)) { - return false; - } - /** + return false; + } + /** * @todo add sorting here first if we can't fit the item */ - return Storage.Stash.CanFit(item); - }, + return Storage.Stash.CanFit(item); + }, - /** + /** * get ordered list of gems in inventory to use with Gem Hunter Script * @returns {ItemUnit[]} ordered list of relevant gems */ - getGemsInInv: function () { - let GemList = Config.GemHunter.GemList; - return me.getItemsEx() - .filter((p) => p.isInInventory && GemList.includes(p.classid)) - .sort((a, b) => GemList.indexOf(a.classid) - GemList.indexOf(b.classid)); - }, - - /** + getGemsInInv: function () { + let GemList = Config.GemHunter.GemList; + return me.getItemsEx() + .filter((p) => p.isInInventory && GemList.includes(p.classid)) + .sort((a, b) => GemList.indexOf(a.classid) - GemList.indexOf(b.classid)); + }, + + /** * get ordered list of gems in stash to use with Gem Hunter Script * @returns {ItemUnit[]} ordered list of relevant gems */ - getGemsInStash: function () { - let GemList = Config.GemHunter.GemList; - return me.getItemsEx() - .filter((p) => p.isInStash && GemList.includes(p.classid)) - .sort((a, b) => GemList.indexOf(a.classid) - GemList.indexOf(b.classid)); - }, - - /** + getGemsInStash: function () { + let GemList = Config.GemHunter.GemList; + return me.getItemsEx() + .filter((p) => p.isInStash && GemList.includes(p.classid)) + .sort((a, b) => GemList.indexOf(a.classid) - GemList.indexOf(b.classid)); + }, + + /** * gem check for use with Gem Hunter Script * @param {ItemUnit} item * @returns {boolean} if we should stash this gem */ - canStashGem: function (item) { - // we aren't using the gem hunter script or we aren't scanning for the gem shrines while moving - // for now we are only going to keep a gem in our invo while the script is active - if (Loader.scriptName() !== "GemHunter") return true; - // not in our list - if (Config.GemHunter.GemList.indexOf(item.classid) === -1) return true; - - let GemList = Config.GemHunter.GemList; - let gemsInStash = Town.getGemsInStash(); - let bestGeminStash = gemsInStash.length > 0 ? gemsInStash.first().classid : -1; - let gemsInInvo = Town.getGemsInInv(); - let bestGeminInv = gemsInInvo.length > 0 ? gemsInInvo.first().classid : -1; - - return ( - (GemList.indexOf(bestGeminStash) < GemList.indexOf(bestGeminInv)) // better one in stash + canStashGem: function (item) { + // we aren't using the gem hunter script or we aren't scanning for the gem shrines while moving + // for now we are only going to keep a gem in our invo while the script is active + if (Loader.scriptName() !== "GemHunter") return true; + // not in our list + if (Config.GemHunter.GemList.indexOf(item.classid) === -1) return true; + + let GemList = Config.GemHunter.GemList; + let gemsInStash = Town.getGemsInStash(); + let bestGeminStash = gemsInStash.length > 0 ? gemsInStash.first().classid : -1; + let gemsInInvo = Town.getGemsInInv(); + let bestGeminInv = gemsInInvo.length > 0 ? gemsInInvo.first().classid : -1; + + return ( + (GemList.indexOf(bestGeminStash) < GemList.indexOf(bestGeminInv)) // better one in stash || (GemList.indexOf(bestGeminInv) < GemList.indexOf(item.classid)) // better one in inv || (gemsInInvo.filter((p) => p.classid === item.classid).length > 1)); // another one in inv - }, + }, - /** + /** * move best gem from stash to inventory, if none in inventrory * to use with Gem Hunter Script * @returns {boolean} if any gem has been moved */ - getGem: function () { - if (Loader.scriptName() === "GemHunter") { - if (this.getGemsInInv().length === 0 && this.getGemsInStash().length > 0) { - let gem = this.getGemsInStash().first(); - Storage.Inventory.MoveTo(gem) && Item.logger("Inventoried", gem); - return true; - } - } - return false; - }, - - /** + getGem: function () { + if (Loader.scriptName() === "GemHunter") { + if (this.getGemsInInv().length === 0 && this.getGemsInStash().length > 0) { + let gem = this.getGemsInStash().first(); + Storage.Inventory.MoveTo(gem) && Item.logger("Inventoried", gem); + return true; + } + } + return false; + }, + + /** * @param {boolean} [stashGold=true] * @returns {boolean} */ - stash: function (stashGold = true) { - if (!this.needStash()) return true; + stash: function (stashGold = true) { + if (!this.needStash()) return true; - me.cancelUIFlags(); + me.cancelUIFlags(); - let items = Storage.Inventory.Compare(Config.Inventory); + let items = Storage.Inventory.Compare(Config.Inventory); - if (items) { - Config.SortSettings.SortStash && Storage.Stash.SortItems(); + if (items) { + Config.SortSettings.SortStash && Storage.Stash.SortItems(); - for (let i = 0; i < items.length; i += 1) { - if (this.canStash(items[i])) { - let result = false; - let pickResult = Pickit.checkItem(items[i]).result; + for (let i = 0; i < items.length; i += 1) { + if (this.canStash(items[i])) { + let result = false; + let pickResult = Pickit.checkItem(items[i]).result; - switch (true) { - case pickResult > Pickit.Result.UNWANTED && pickResult < Pickit.Result.TRASH: - case Cubing.keepItem(items[i]): - case Runewords.keepItem(items[i]): - case CraftingSystem.keepItem(items[i]): - result = true; - - break; - default: - break; - } - - if (result) { - Storage.Stash.MoveTo(items[i]) && Item.logger("Stashed", items[i]); - } - } - } - } - - // 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; - }, - - 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); - - for (let i = 0; i < items.length; i += 1) { - if (Storage.Stash.CanFit(items[i])) { - return true; - } - } - - return false; - }, - - openStash: function () { - if (getUIFlag(sdk.uiflags.Cube) && !Cubing.closeCube()) return false; - if (getUIFlag(sdk.uiflags.Stash)) return true; - - for (let i = 0; i < 5; i += 1) { - me.cancel(); - - if (this.move("stash")) { - let stash = Game.getObject(sdk.objects.Stash); - - if (stash) { - let pingDelay = me.getPingDelay(); - - if (Skill.useTK(stash)) { - // Fix for out of range telek - i > 0 && stash.distance > (23 - (i * 2)) && Pather.walkTo(stash.x, stash.y, (23 - (i * 2))); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stash); - } else { - Misc.click(0, 0, stash); - } - - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (getUIFlag(sdk.uiflags.Stash)) { - // allow UI to initialize - delay(100 + pingDelay * 2); - - return true; - } - - delay(100); - } - } - } - - Packet.flash(me.gid); - } - - return false; - }, - - getCorpse: function () { - let corpse, corpseList = []; - let timer = getTickCount(); - - // No equipped items - high chance of dying in last game, force retries - if (!me.getItem(-1, sdk.items.mode.Equipped)) { - corpse = Misc.poll(() => Game.getPlayer(me.name, sdk.player.mode.Dead), 2500, 500); - } else { - corpse = Game.getPlayer(me.name, sdk.player.mode.Dead); - } - - if (!corpse) return true; - - do { - if (corpse.dead && corpse.name === me.name && (getDistance(me.x, me.y, corpse.x, corpse.y) <= 20 || me.inTown)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - - while (corpseList.length > 0) { - if (me.dead) return false; - - let gid = corpseList[0].gid; - - Pather.moveToUnit(corpseList[0]); - Misc.click(0, 0, corpseList[0]); - delay(500); - - if (getTickCount() - timer > 3000) { - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 4); - !!coord && Pather.moveTo(coord.x, coord.y); - } - - if (getTickCount() - timer > 30000) { - D2Bot.console.logToConsole("Failed to get corpse, stopping.", sdk.colors.D2Bot.Red); - D2Bot.stop(); - } - - !Game.getPlayer(-1, -1, gid) && corpseList.shift(); - } - - me.classic && this.checkShard(); - // re-init skills since we started off without our body - Skill.init(); - - return true; - }, - - checkShard: function () { - let shard; - let check = { left: false, right: false }; - let item = me.getItem("bld", sdk.items.mode.inStorage); - - if (item) { - do { - if (item.isInInventory && item.unique) { - shard = copyUnit(item); - - break; - } - } while (item.getNext()); - } - - if (!shard) return true; - - item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - item.bodylocation === sdk.body.RightArm && (check.right = true); - item.bodylocation === sdk.body.LeftArm && (check.left = true); - } while (item.getNext()); - } - - if (!check.right) { - shard.toCursor(); - - while (me.itemoncursor) { - clickItem(sdk.clicktypes.click.item.Left, sdk.body.RightArm); - delay(500); - } - } else if (!check.left) { - shard.toCursor(); - - while (me.itemoncursor) { - clickItem(sdk.clicktypes.click.item.Left, sdk.body.LeftArm); - delay(500); - } - } - - return true; - }, - - 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; - }, - - clearScrolls: function () { - const scrolls = me.getItemsEx().filter((scroll) => scroll.isInInventory && scroll.itemType === sdk.items.type.Scroll); - if (!scrolls.length) return false; - const tpTome = scrolls.some(function (scroll) { - return scroll.classid === sdk.items.ScrollofTownPortal; - }) ? me.getTome(sdk.items.TomeofTownPortal) : false; - const idTome = scrolls.some(function (scroll) { - return scroll.classid === sdk.items.ScrollofIdentify; - }) ? me.getTome(sdk.items.TomeofIdentify) : false; - let currQuantity; - - for (let i = 0; !!scrolls && i < scrolls.length; i++) { - switch (scrolls[i].classid) { - case sdk.items.ScrollofTownPortal: - if (tpTome && tpTome.getStat(sdk.stats.Quantity) < 20) { - currQuantity = tpTome.getStat(sdk.stats.Quantity); - if (scrolls[i].toCursor()) { - clickItemAndWait(sdk.clicktypes.click.item.Left, tpTome.x, tpTome.y, tpTome.location); - - if (tpTome.getStat(sdk.stats.Quantity) > currQuantity) { - console.info(null, "Placed scroll in tp tome"); - - continue; - } - } - } - - break; - case sdk.items.ScrollofIdentify: - if (idTome && idTome.getStat(sdk.stats.Quantity) < 20) { - currQuantity = idTome.getStat(sdk.stats.Quantity); - if (scrolls[i].toCursor()) { - clickItemAndWait(sdk.clicktypes.click.item.Left, idTome.x, idTome.y, idTome.location); - - if (idTome.getStat(sdk.stats.Quantity) > currQuantity) { - console.info(null, "Placed scroll in id tome"); - - continue; - } - } - } - - if (Config.FieldID.Enabled && !idTome) { - // don't toss scrolls if we need them for field id but don't have a tome yet - low level chars - continue; - } - - break; - } - - // Might as well sell the item if already in shop - if (getUIFlag(sdk.uiflags.Shop) || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { - console.info(null, "Sell " + scrolls[i].name); - Item.logger("Sold", scrolls[i]); - scrolls[i].sell(); - } else { - Item.logger("Dropped", scrolls[i], "clearScrolls"); - scrolls[i].drop(); - } - } - - return true; - }, - - clearInventory: function () { - console.info(true); - console.time("clearInventory"); - - // 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 { - !!getInteractedNPC() && Misc.useMenu(sdk.menu.Trade); - } catch (e) { - console.error(e); - me.cancelUIFlags(); - } - } + switch (true) { + case pickResult > Pickit.Result.UNWANTED && pickResult < Pickit.Result.TRASH: + case Cubing.keepItem(items[i]): + case Runewords.keepItem(items[i]): + case CraftingSystem.keepItem(items[i]): + result = true; + + break; + default: + break; + } + + if (result) { + Storage.Stash.MoveTo(items[i]) && Item.logger("Stashed", items[i]); + } + } + } + } + + // 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; + }, + + 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); + + for (let i = 0; i < items.length; i += 1) { + if (Storage.Stash.CanFit(items[i])) { + return true; + } + } + + return false; + }, + + openStash: function () { + if (getUIFlag(sdk.uiflags.Cube) && !Cubing.closeCube()) return false; + if (getUIFlag(sdk.uiflags.Stash)) return true; + + for (let i = 0; i < 5; i += 1) { + me.cancel(); + + if (this.move("stash")) { + let stash = Game.getObject(sdk.objects.Stash); + + if (stash) { + let pingDelay = me.getPingDelay(); + + if (Skill.useTK(stash)) { + // Fix for out of range telek + i > 0 && stash.distance > (23 - (i * 2)) && Pather.walkTo(stash.x, stash.y, (23 - (i * 2))); + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stash); + } else { + Misc.click(0, 0, stash); + } + + let tick = getTickCount(); + + while (getTickCount() - tick < 5000) { + if (getUIFlag(sdk.uiflags.Stash)) { + // allow UI to initialize + delay(100 + pingDelay * 2); + + return true; + } + + delay(100); + } + } + } + + Packet.flash(me.gid); + } + + return false; + }, + + getCorpse: function () { + let corpse, corpseList = []; + let timer = getTickCount(); + + // No equipped items - high chance of dying in last game, force retries + if (!me.getItem(-1, sdk.items.mode.Equipped)) { + corpse = Misc.poll(() => Game.getPlayer(me.name, sdk.player.mode.Dead), 2500, 500); + } else { + corpse = Game.getPlayer(me.name, sdk.player.mode.Dead); + } + + if (!corpse) return true; + + do { + if (corpse.dead && corpse.name === me.name && (getDistance(me.x, me.y, corpse.x, corpse.y) <= 20 || me.inTown)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); + + while (corpseList.length > 0) { + if (me.dead) return false; + + let gid = corpseList[0].gid; + + Pather.moveToUnit(corpseList[0]); + Misc.click(0, 0, corpseList[0]); + delay(500); + + if (getTickCount() - timer > 3000) { + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 4); + !!coord && Pather.moveTo(coord.x, coord.y); + } + + if (getTickCount() - timer > 30000) { + D2Bot.console.logToConsole("Failed to get corpse, stopping.", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } + + !Game.getPlayer(-1, -1, gid) && corpseList.shift(); + } + + me.classic && this.checkShard(); + // re-init skills since we started off without our body + Skill.init(); + + return true; + }, + + checkShard: function () { + let shard; + let check = { left: false, right: false }; + let item = me.getItem("bld", sdk.items.mode.inStorage); + + if (item) { + do { + if (item.isInInventory && item.unique) { + shard = copyUnit(item); + + break; + } + } while (item.getNext()); + } + + if (!shard) return true; + + item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + item.bodylocation === sdk.body.RightArm && (check.right = true); + item.bodylocation === sdk.body.LeftArm && (check.left = true); + } while (item.getNext()); + } + + if (!check.right) { + shard.toCursor(); + + while (me.itemoncursor) { + clickItem(sdk.clicktypes.click.item.Left, sdk.body.RightArm); + delay(500); + } + } else if (!check.left) { + shard.toCursor(); + + while (me.itemoncursor) { + clickItem(sdk.clicktypes.click.item.Left, sdk.body.LeftArm); + delay(500); + } + } + + return true; + }, + + 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; + }, + + clearScrolls: function () { + const scrolls = me.getItemsEx() + .filter((scroll) => scroll.isInInventory && scroll.itemType === sdk.items.type.Scroll); + if (!scrolls.length) return false; + const tpTome = scrolls.some(function (scroll) { + return scroll.classid === sdk.items.ScrollofTownPortal; + }) ? me.getTome(sdk.items.TomeofTownPortal) : false; + const idTome = scrolls.some(function (scroll) { + return scroll.classid === sdk.items.ScrollofIdentify; + }) ? me.getTome(sdk.items.TomeofIdentify) : false; + let currQuantity; + + for (let i = 0; !!scrolls && i < scrolls.length; i++) { + switch (scrolls[i].classid) { + case sdk.items.ScrollofTownPortal: + if (tpTome && tpTome.getStat(sdk.stats.Quantity) < 20) { + currQuantity = tpTome.getStat(sdk.stats.Quantity); + if (scrolls[i].toCursor()) { + clickItemAndWait(sdk.clicktypes.click.item.Left, tpTome.x, tpTome.y, tpTome.location); + + if (tpTome.getStat(sdk.stats.Quantity) > currQuantity) { + console.info(null, "Placed scroll in tp tome"); + + continue; + } + } + } + + break; + case sdk.items.ScrollofIdentify: + if (idTome && idTome.getStat(sdk.stats.Quantity) < 20) { + currQuantity = idTome.getStat(sdk.stats.Quantity); + if (scrolls[i].toCursor()) { + clickItemAndWait(sdk.clicktypes.click.item.Left, idTome.x, idTome.y, idTome.location); + + if (idTome.getStat(sdk.stats.Quantity) > currQuantity) { + console.info(null, "Placed scroll in id tome"); + + continue; + } + } + } + + if (Config.FieldID.Enabled && !idTome) { + // don't toss scrolls if we need them for field id but don't have a tome yet - low level chars + continue; + } + + break; + } + + // Might as well sell the item if already in shop + if (getUIFlag(sdk.uiflags.Shop) + || (Config.PacketShopping && getInteractedNPC() && getInteractedNPC().itemcount > 0)) { + console.info(null, "Sell " + scrolls[i].name); + Item.logger("Sold", scrolls[i]); + scrolls[i].sell(); + } else { + Item.logger("Dropped", scrolls[i], "clearScrolls"); + scrolls[i].drop(); + } + } + + return true; + }, + + clearInventory: function () { + console.info(true); + console.time("clearInventory"); + + // 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 { + !!getInteractedNPC() && Misc.useMenu(sdk.menu.Trade); + } catch (e) { + console.error(e); + me.cancelUIFlags(); + } + } - // Remove potions in the wrong slot of our belt - this.clearBelt(); + // Remove potions in the wrong slot of our belt + this.clearBelt(); - // Return potions from inventory to belt - const beltSize = Storage.BeltSize(); - // belt 4x4 locations - /** + // Return potions from inventory to belt + const beltSize = Storage.BeltSize(); + // belt 4x4 locations + /** * 12 13 14 15 * 8 9 10 11 * 4 5 6 7 * 0 1 2 3 */ - const beltMax = (beltSize * 4); - const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; - let 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); - - Config.DebugMode.Town && potsInInventory.length > 0 && console.debug("clearInventory: start pots clean-up"); - // Start interating over all the pots we have in our inventory - potsInInventory.forEach(function (p) { - let moved = false; - // get free space in each slot of our belt - let freeSpace = Town.checkColumns(beltSize); - for (let i = 0; i < 4 && !moved; i++) { - // 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 = Town.checkColumns(beltSize)[i] === freeSpace[i] - 1; - } - Cubing.cursorCheck(); - } - }); - - // Cleanup remaining potions - Config.DebugMode.Town && console.debug("clearInventory: start clean-up remaining pots"); - let sellOrDrop = []; - 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 = []; - 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; - }); - - // 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); - } - } - - // Any leftover items from a failed ID (crashed game, disconnect etc.) - Config.DebugMode.Town && console.debug("clearInventory: start invo clean-up"); - let 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; - // Don't drop tomes, keys or potions or quest-items - // Don't throw cubing/runeword/crafting ingredients - if (ignoreTypes.indexOf(item.itemType) === -1 && item.sellable && !Cubing.keepItem(item) && !Runewords.keepItem(item) && !CraftingSystem.keepItem(item)) { - return true; - } - return false; - }); - - // add leftovers from potion cleanup - items = (items.length > 0 ? items.concat(sellOrDrop) : sellOrDrop.slice(0)); - - if (items.length > 0) { - let sell = [], drop = []; - // lets see if we have any items to sell - items.forEach(function (item) { - let result = Pickit.checkItem(item).result; - switch (result) { - case Pickit.Result.UNWANTED: - return drop.push(item); - case Pickit.Result.TRASH: - return sell.push(item); - } - return false; - }); - // we have items to sell, might as well sell the dropable items as well - if (sell.length) { - // should there be multiple attempts to interact with npc or if we fail should we move everything from the sell list to the drop list? - let npc; - for (let i = 0; i < 3 && !npc; i++) { - npc = Town.initNPC("Shop", "clearInventory"); - } - - // now lets sell them items - if (npc) { - [].concat(sell, drop.filter((item) => item.sellable)) - .forEach(function (item) { - let sold = false; // so we know to delay or not - try { - console.info(null, "Sell :: " + item.name); - Item.logger("Sold", item); - item.sell() && (sold = true); - } catch (e) { - console.error(e); - } - sold && delay(250); // would a rand delay be better? - }); - } - // now lets see if we need to drop anything, so lets exit the shop - me.cancelUIFlags(); - drop = drop.filter((item) => !!item && me.getItem(-1, sdk.items.mode.inStorage, item.gid)); - } - - if (drop.length) { - drop.forEach(function (item) { - let drop = false; // so we know to delay or not - try { - console.info(null, "Drop :: " + item.name); - Item.logger("Dropped", item, "clearInventory"); - item.drop() && (drop = true); - } catch (e) { - console.error(e); - } - drop && delay(50); - }); - } - } - - console.info(false, null, "clearInventory"); - - return true; - }, - - /** + const beltMax = (beltSize * 4); + const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; + let 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); + + Config.DebugMode.Town && potsInInventory.length > 0 && console.debug("clearInventory: start pots clean-up"); + // Start interating over all the pots we have in our inventory + potsInInventory.forEach(function (p) { + let moved = false; + // get free space in each slot of our belt + let freeSpace = Town.checkColumns(beltSize); + for (let i = 0; i < 4 && !moved; i++) { + // 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 = Town.checkColumns(beltSize)[i] === freeSpace[i] - 1; + } + Cubing.cursorCheck(); + } + }); + + // Cleanup remaining potions + Config.DebugMode.Town && console.debug("clearInventory: start clean-up remaining pots"); + let sellOrDrop = []; + 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 = []; + 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; + }); + + // 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); + } + } + + // Any leftover items from a failed ID (crashed game, disconnect etc.) + Config.DebugMode.Town && console.debug("clearInventory: start invo clean-up"); + let 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; + // Don't drop tomes, keys or potions or quest-items + // Don't throw cubing/runeword/crafting ingredients + if (ignoreTypes.indexOf(item.itemType) === -1 && item.sellable + && !Cubing.keepItem(item) && !Runewords.keepItem(item) && !CraftingSystem.keepItem(item)) { + return true; + } + return false; + }); + + // add leftovers from potion cleanup + items = (items.length > 0 ? items.concat(sellOrDrop) : sellOrDrop.slice(0)); + + if (items.length > 0) { + let sell = [], drop = []; + // lets see if we have any items to sell + items.forEach(function (item) { + let result = Pickit.checkItem(item).result; + switch (result) { + case Pickit.Result.UNWANTED: + return drop.push(item); + case Pickit.Result.TRASH: + return sell.push(item); + } + return false; + }); + // we have items to sell, might as well sell the dropable items as well + if (sell.length) { + // should there be multiple attempts to interact with npc or if we fail should we move everything from the sell list to the drop list? + let npc; + for (let i = 0; i < 3 && !npc; i++) { + npc = Town.initNPC("Shop", "clearInventory"); + } + + // now lets sell them items + if (npc) { + [].concat(sell, drop.filter((item) => item.sellable)) + .forEach(function (item) { + let sold = false; // so we know to delay or not + try { + console.info(null, "Sell :: " + item.name); + Item.logger("Sold", item); + item.sell() && (sold = true); + } catch (e) { + console.error(e); + } + sold && delay(250); // would a rand delay be better? + }); + } + // now lets see if we need to drop anything, so lets exit the shop + me.cancelUIFlags(); + drop = drop.filter((item) => !!item && me.getItem(-1, sdk.items.mode.inStorage, item.gid)); + } + + if (drop.length) { + drop.forEach(function (item) { + let drop = false; // so we know to delay or not + try { + console.info(null, "Drop :: " + item.name); + Item.logger("Dropped", item, "clearInventory"); + item.drop() && (drop = true); + } catch (e) { + console.error(e); + } + drop && delay(50); + }); + } + } + + console.info(false, null, "clearInventory"); + + return true; + }, + + /** * @todo figure out how to typedef this. */ - act: [{}, {}, {}, {}, {}], - - initialize: function () { - //console.log("Initialize town " + me.act); - - switch (me.act) { - case 1: - let wp = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1Waypoint); - let fireUnit = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1TownFire); - if (!fireUnit) return false; - - let fire = [fireUnit.roomx * 5 + fireUnit.x, fireUnit.roomy * 5 + fireUnit.y]; - - this.act[0].spot = {}; - this.act[0].spot.stash = [fire[0] - 7, fire[1] - 12]; - this.act[0].spot[NPC.Warriv] = [fire[0] - 5, fire[1] - 2]; - this.act[0].spot[NPC.Cain] = [fire[0] + 6, fire[1] - 5]; - this.act[0].spot[NPC.Kashya] = [fire[0] + 14, fire[1] - 4]; - this.act[0].spot[NPC.Akara] = [fire[0] + 56, fire[1] - 30]; - this.act[0].spot[NPC.Charsi] = [fire[0] - 39, fire[1] - 25]; - this.act[0].spot[NPC.Gheed] = [fire[0] - 34, fire[1] + 36]; - this.act[0].spot.portalspot = [fire[0] + 10, fire[1] + 18]; - this.act[0].spot.waypoint = [wp.roomx * 5 + wp.x, wp.roomy * 5 + wp.y]; - this.act[0].initialized = true; - - break; - case 2: - this.act[1].spot = {}; - this.act[1].spot[NPC.Fara] = [5124, 5082]; - this.act[1].spot[NPC.Cain] = [5124, 5082]; - this.act[1].spot[NPC.Lysander] = [5118, 5104]; - this.act[1].spot[NPC.Greiz] = [5033, 5053]; - this.act[1].spot[NPC.Elzix] = [5032, 5102]; - this.act[1].spot.palace = [5088, 5153]; - this.act[1].spot.sewers = [5221, 5181]; - this.act[1].spot[NPC.Meshif] = [5205, 5058]; - this.act[1].spot[NPC.Drognan] = [5097, 5035]; - this.act[1].spot[NPC.Atma] = [5137, 5060]; - this.act[1].spot[NPC.Warriv] = [5152, 5201]; - this.act[1].spot.portalspot = [5168, 5060]; - this.act[1].spot.stash = [5124, 5076]; - this.act[1].spot.waypoint = [5070, 5083]; - this.act[1].initialized = true; - - break; - case 3: - this.act[2].spot = {}; - this.act[2].spot[NPC.Meshif] = [5118, 5168]; - this.act[2].spot[NPC.Hratli] = [5223, 5048, 5127, 5172]; - this.act[2].spot[NPC.Ormus] = [5129, 5093]; - this.act[2].spot[NPC.Asheara] = [5043, 5093]; - this.act[2].spot[NPC.Alkor] = [5083, 5016]; - this.act[2].spot[NPC.Cain] = [5148, 5066]; - this.act[2].spot.stash = [5144, 5059]; - this.act[2].spot.portalspot = [5150, 5063]; - this.act[2].spot.waypoint = [5158, 5050]; - this.act[2].initialized = true; - - break; - case 4: - this.act[3].spot = {}; - this.act[3].spot[NPC.Cain] = [5027, 5027]; - this.act[3].spot[NPC.Halbu] = [5089, 5031]; - this.act[3].spot[NPC.Tyrael] = [5027, 5027]; - this.act[3].spot[NPC.Jamella] = [5088, 5054]; - this.act[3].spot.stash = [5022, 5040]; - this.act[3].spot.portalspot = [5045, 5042]; - this.act[3].spot.waypoint = [5043, 5018]; - this.act[3].initialized = true; - - break; - case 5: - this.act[4].spot = {}; - this.act[4].spot.portalspot = [5098, 5019]; - this.act[4].spot.stash = [5129, 5061]; - this.act[4].spot[NPC.Larzuk] = [5141, 5045]; - this.act[4].spot[NPC.Malah] = [5078, 5029]; - this.act[4].spot[NPC.Cain] = [5119, 5061]; - this.act[4].spot[NPC.Qual_Kehk] = [5066, 5083]; - this.act[4].spot[NPC.Anya] = [5112, 5120]; - this.act[4].spot.portal = [5118, 5120]; - this.act[4].spot.waypoint = [5113, 5068]; - this.act[4].spot[NPC.Nihlathak] = [5071, 5111]; - this.act[4].initialized = true; - - break; - } - - return true; - }, - - /** + act: [{}, {}, {}, {}, {}], + + initialize: function () { + //console.log("Initialize town " + me.act); + + switch (me.act) { + case 1: + let wp = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1Waypoint); + let fireUnit = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1TownFire); + if (!fireUnit) return false; + + let fire = [fireUnit.roomx * 5 + fireUnit.x, fireUnit.roomy * 5 + fireUnit.y]; + + this.act[0].spot = {}; + this.act[0].spot.stash = [fire[0] - 7, fire[1] - 12]; + this.act[0].spot[NPC.Warriv] = [fire[0] - 5, fire[1] - 2]; + this.act[0].spot[NPC.Cain] = [fire[0] + 6, fire[1] - 5]; + this.act[0].spot[NPC.Kashya] = [fire[0] + 14, fire[1] - 4]; + this.act[0].spot[NPC.Akara] = [fire[0] + 56, fire[1] - 30]; + this.act[0].spot[NPC.Charsi] = [fire[0] - 39, fire[1] - 25]; + this.act[0].spot[NPC.Gheed] = [fire[0] - 34, fire[1] + 36]; + this.act[0].spot.portalspot = [fire[0] + 10, fire[1] + 18]; + this.act[0].spot.waypoint = [wp.roomx * 5 + wp.x, wp.roomy * 5 + wp.y]; + this.act[0].initialized = true; + + break; + case 2: + this.act[1].spot = {}; + this.act[1].spot[NPC.Fara] = [5124, 5082]; + this.act[1].spot[NPC.Cain] = [5124, 5082]; + this.act[1].spot[NPC.Lysander] = [5118, 5104]; + this.act[1].spot[NPC.Greiz] = [5033, 5053]; + this.act[1].spot[NPC.Elzix] = [5032, 5102]; + this.act[1].spot.palace = [5088, 5153]; + this.act[1].spot.sewers = [5221, 5181]; + this.act[1].spot[NPC.Meshif] = [5205, 5058]; + this.act[1].spot[NPC.Drognan] = [5097, 5035]; + this.act[1].spot[NPC.Atma] = [5137, 5060]; + this.act[1].spot[NPC.Warriv] = [5152, 5201]; + this.act[1].spot.portalspot = [5168, 5060]; + this.act[1].spot.stash = [5124, 5076]; + this.act[1].spot.waypoint = [5070, 5083]; + this.act[1].initialized = true; + + break; + case 3: + this.act[2].spot = {}; + this.act[2].spot[NPC.Meshif] = [5118, 5168]; + this.act[2].spot[NPC.Hratli] = [5223, 5048, 5127, 5172]; + this.act[2].spot[NPC.Ormus] = [5129, 5093]; + this.act[2].spot[NPC.Asheara] = [5043, 5093]; + this.act[2].spot[NPC.Alkor] = [5083, 5016]; + this.act[2].spot[NPC.Cain] = [5148, 5066]; + this.act[2].spot.stash = [5144, 5059]; + this.act[2].spot.portalspot = [5150, 5063]; + this.act[2].spot.waypoint = [5158, 5050]; + this.act[2].initialized = true; + + break; + case 4: + this.act[3].spot = {}; + this.act[3].spot[NPC.Cain] = [5027, 5027]; + this.act[3].spot[NPC.Halbu] = [5089, 5031]; + this.act[3].spot[NPC.Tyrael] = [5027, 5027]; + this.act[3].spot[NPC.Jamella] = [5088, 5054]; + this.act[3].spot.stash = [5022, 5040]; + this.act[3].spot.portalspot = [5045, 5042]; + this.act[3].spot.waypoint = [5043, 5018]; + this.act[3].initialized = true; + + break; + case 5: + this.act[4].spot = {}; + this.act[4].spot.portalspot = [5098, 5019]; + this.act[4].spot.stash = [5129, 5061]; + this.act[4].spot[NPC.Larzuk] = [5141, 5045]; + this.act[4].spot[NPC.Malah] = [5078, 5029]; + this.act[4].spot[NPC.Cain] = [5119, 5061]; + this.act[4].spot[NPC.Qual_Kehk] = [5066, 5083]; + this.act[4].spot[NPC.Anya] = [5112, 5120]; + this.act[4].spot.portal = [5118, 5120]; + this.act[4].spot.waypoint = [5113, 5068]; + this.act[4].spot[NPC.Nihlathak] = [5071, 5111]; + this.act[4].initialized = true; + + break; + } + + return true; + }, + + /** * @param {string} spot * @returns {number} distance to town location */ - getDistance: function (spot = "") { - !me.inTown && this.goToTown(); - !this.act[me.act - 1].initialized && this.initialize(); - - // Act 5 wp->portalspot override - ActMap.cpp crash - if (me.act === 5 && spot === "portalspot" && getDistance(me.x, me.y, 5113, 5068) <= 8) { - return [5098, 5018].distance; - } - - if (typeof (this.act[me.act - 1].spot[spot]) === "object") { - return this.act[me.act - 1].spot[spot].distance; - } else { - return Infinity; - } - }, - - /** + getDistance: function (spot = "") { + !me.inTown && this.goToTown(); + !this.act[me.act - 1].initialized && this.initialize(); + + // Act 5 wp->portalspot override - ActMap.cpp crash + if (me.act === 5 && spot === "portalspot" && getDistance(me.x, me.y, 5113, 5068) <= 8) { + return [5098, 5018].distance; + } + + if (typeof (this.act[me.act - 1].spot[spot]) === "object") { + return this.act[me.act - 1].spot[spot].distance; + } else { + return Infinity; + } + }, + + /** * @param {string} spot * @param {boolean} [allowTK] * @returns {boolean} */ - move: function (spot = "", allowTK = true) { - !me.inTown && this.goToTown(); - !this.act[me.act - 1].initialized && this.initialize(); - - // act 5 static paths, ActMap.cpp seems to have issues with A5 - // should other towns have static paths? - if (me.act === 5) { - let path = []; - let returnWhenDone = false; + move: function (spot = "", allowTK = true) { + !me.inTown && this.goToTown(); + !this.act[me.act - 1].initialized && this.initialize(); + + // act 5 static paths, ActMap.cpp seems to have issues with A5 + // should other towns have static paths? + if (me.act === 5) { + let path = []; + let returnWhenDone = false; - // Act 5 wp->portalspot override - ActMap.cpp crash - if (spot === "portalspot" && getDistance(me.x, me.y, 5113, 5068) <= 8) { - path = [5113, 5068, 5108, 5051, 5106, 5046, 5104, 5041, 5102, 5027, 5098, 5018]; - returnWhenDone = true; - } - - if (["stash", "waypoint"].includes(spot)) { - // malah -> stash/wp - if (getDistance(me.x, me.y, 5081, 5031) <= 10) { - path = [5089, 5029, 5093, 5021, 5101, 5027, 5107, 5043, 5108, 5052]; - } else if (getDistance(me.x, me.y, 5099, 5020) <= 13) { - // portalspot -> stash/wp - path = [5102, 5031, 5107, 5042, 5108, 5052]; - } - } - - if (path.length) { - for (let i = 0; i < path.length; i += 2) { - Pather.walkTo(path[i], path[i + 1]); - } - - if (returnWhenDone) return true; - } - } - - for (let i = 0; i < 3; i += 1) { - i === 2 && (allowTK = false); - if (this.moveToSpot(spot, allowTK)) { - return true; - } - - Packet.flash(me.gid); - } - - return false; - }, - - /** + // Act 5 wp->portalspot override - ActMap.cpp crash + if (spot === "portalspot" && getDistance(me.x, me.y, 5113, 5068) <= 8) { + path = [5113, 5068, 5108, 5051, 5106, 5046, 5104, 5041, 5102, 5027, 5098, 5018]; + returnWhenDone = true; + } + + if (["stash", "waypoint"].includes(spot)) { + // malah -> stash/wp + if (getDistance(me.x, me.y, 5081, 5031) <= 10) { + path = [5089, 5029, 5093, 5021, 5101, 5027, 5107, 5043, 5108, 5052]; + } else if (getDistance(me.x, me.y, 5099, 5020) <= 13) { + // portalspot -> stash/wp + path = [5102, 5031, 5107, 5042, 5108, 5052]; + } + } + + if (path.length) { + for (let i = 0; i < path.length; i += 2) { + Pather.walkTo(path[i], path[i + 1]); + } + + if (returnWhenDone) return true; + } + } + + for (let i = 0; i < 3; i += 1) { + i === 2 && (allowTK = false); + if (this.moveToSpot(spot, allowTK)) { + return true; + } + + Packet.flash(me.gid); + } + + return false; + }, + + /** * @param {string} spot * @param {boolean} [allowTK] * @returns {boolean} */ - moveToSpot: function (spot = "", allowTK = true) { - let townSpot; - let longRange = (!Skill.haveTK && spot === "waypoint"); - let tkRange = (Skill.haveTK && allowTK && ["stash", "portalspot", "waypoint"].includes(spot)); - - if (!this.act[me.act - 1].hasOwnProperty("spot") || !this.act[me.act - 1].spot.hasOwnProperty(spot)) { - return false; - } - - if (typeof (this.act[me.act - 1].spot[spot]) === "object") { - townSpot = this.act[me.act - 1].spot[spot]; - } else { - return false; - } - - if (longRange) { - let path = getPath(me.area, townSpot[0], townSpot[1], me.x, me.y, 1, 8); - - if (path && path[1]) { - townSpot = [path[1].x, path[1].y]; - } - } - - for (let i = 0; i < townSpot.length; i += 2) { - //console.debug("moveToSpot: " + spot + " from " + me.x + ", " + me.y); - - if (tkRange) { - Pather.moveNear(townSpot[0], townSpot[1], 19); - } else if (getDistance(me, townSpot[i], townSpot[i + 1]) > 2) { - Pather.moveTo(townSpot[i], townSpot[i + 1], 3, false, true); - } - - switch (spot) { - case "stash": - if (!!Game.getObject(sdk.objects.Stash)) { - return true; - } - - break; - case "palace": - if (!!Game.getNPC(NPC.Jerhyn)) { - return true; - } - - break; - case "portalspot": - case "sewers": - if (tkRange && spot === "portalspot" && getDistance(me, townSpot[0], townSpot[1]) < 21) { - return true; - } - - if (getDistance(me, townSpot[i], townSpot[i + 1]) < 10) { - return true; - } - - break; - case "waypoint": - let wp = Game.getObject("waypoint"); - if (!!wp) { - !Skill.haveTK && wp.distance > 5 && Pather.moveToUnit(wp); - return true; - } - - break; - default: - if (!!Game.getNPC(spot)) { - return true; - } - - break; - } - } - - return false; - }, - - /** + moveToSpot: function (spot = "", allowTK = true) { + let townSpot; + let longRange = (!Skill.haveTK && spot === "waypoint"); + let tkRange = (Skill.haveTK && allowTK && ["stash", "portalspot", "waypoint"].includes(spot)); + + if (!this.act[me.act - 1].hasOwnProperty("spot") || !this.act[me.act - 1].spot.hasOwnProperty(spot)) { + return false; + } + + if (typeof (this.act[me.act - 1].spot[spot]) === "object") { + townSpot = this.act[me.act - 1].spot[spot]; + } else { + return false; + } + + if (longRange) { + let path = getPath(me.area, townSpot[0], townSpot[1], me.x, me.y, 1, 8); + + if (path && path[1]) { + townSpot = [path[1].x, path[1].y]; + } + } + + for (let i = 0; i < townSpot.length; i += 2) { + //console.debug("moveToSpot: " + spot + " from " + me.x + ", " + me.y); + + if (tkRange) { + Pather.moveNear(townSpot[0], townSpot[1], 19); + } else if (getDistance(me, townSpot[i], townSpot[i + 1]) > 2) { + Pather.moveTo(townSpot[i], townSpot[i + 1], 3, false, true); + } + + switch (spot) { + case "stash": + if (!!Game.getObject(sdk.objects.Stash)) { + return true; + } + + break; + case "palace": + if (!!Game.getNPC(NPC.Jerhyn)) { + return true; + } + + break; + case "portalspot": + case "sewers": + if (tkRange && spot === "portalspot" && getDistance(me, townSpot[0], townSpot[1]) < 21) { + return true; + } + + if (getDistance(me, townSpot[i], townSpot[i + 1]) < 10) { + return true; + } + + break; + case "waypoint": + let wp = Game.getObject("waypoint"); + if (!!wp) { + !Skill.haveTK && wp.distance > 5 && Pather.moveToUnit(wp); + return true; + } + + break; + default: + if (!!Game.getNPC(spot)) { + return true; + } + + break; + } + } + + return false; + }, + + /** * @param {1 | 2 | 3 | 4 | 5} act * @param {boolean} [wpmenu=false] * @returns {boolean} */ - goToTown: function (act = 0, wpmenu = false) { - if (!me.inTown) { - try { - if (!Pather.makePortal(true)) { - console.warn("Town.goToTown: Failed to make TP"); - } - if (!me.inTown && !Pather.usePortal(null, 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 || me.dead) return true; - let p = Game.getObject("portal"); - !!p && Misc.click(0, 0, p) && delay(100); - Misc.poll(() => me.idle, 1000, 100); - return me.inTown; - }, 700, 100)) { - // don't quit if this is a character that is allowed to die - if (Config.LifeChicken > 0) { - 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; - }, - - /** + goToTown: function (act = 0, wpmenu = false) { + if (!me.inTown) { + try { + if (!Pather.makePortal(true)) { + console.warn("Town.goToTown: Failed to make TP"); + } + if (!me.inTown && !Pather.usePortal(null, 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 || me.dead) return true; + let p = Game.getObject("portal"); + !!p && Misc.click(0, 0, p) && delay(100); + Misc.poll(() => me.idle, 1000, 100); + return me.inTown; + }, 700, 100)) { + // don't quit if this is a character that is allowed to die + if (Config.LifeChicken > 0) { + 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; + }, + + /** * @param {boolean} [repair] * @returns {boolean} */ - visitTown: function (repair = false) { - console.info(true); + visitTown: function (repair = false) { + console.info(true); - if (me.inTown) { - this.doChores(); - this.move("stash"); + if (me.inTown) { + this.doChores(); + this.move("stash"); - return true; - } + return true; + } - if (!me.canTpToTown()) { - console.warn("Unable to visit town"); - return false; - } + if (!me.canTpToTown()) { + console.warn("Unable to visit town"); + return false; + } - let preArea = me.area; - let preAct = me.act; + let preArea = me.area; + let preAct = me.act; - // not an essential function -> handle thrown errors - try { - this.goToTown(); - } catch (e) { - return false; - } + // not an essential function -> handle thrown errors + try { + this.goToTown(); + } catch (e) { + return false; + } - this.doChores(repair); + this.doChores(repair); - me.act !== preAct && this.goToTown(preAct); - this.move("portalspot"); + me.act !== preAct && this.goToTown(preAct); + this.move("portalspot"); - if (!Pather.usePortal(null, me.name)) { - try { - Pather.usePortal(preArea, me.name); - } catch (e) { - throw new Error("Town.visitTown: Failed to go back from town"); - } - } + if (!Pather.usePortal(null, me.name)) { + try { + Pather.usePortal(preArea, me.name); + } catch (e) { + throw new Error("Town.visitTown: Failed to go back from town"); + } + } - Config.PublicMode && Pather.makePortal(); - console.info(false, "CurrentArea: " + getAreaName(me.area)); + Config.PublicMode && Pather.makePortal(); + console.info(false, "CurrentArea: " + getAreaName(me.area)); - return true; - } + return true; + } }; diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index ad22a7f27..4bc1cfc6e 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -8,63 +8,63 @@ !isIncluded("Polyfill.js") && include("Polyfill.js"); (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 { - let temp = factory(); - Object.keys(temp).forEach(key => { - if (typeof root[key] === "undefined") { - root[key] = temp[key]; - } - }); - } + 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 { + let temp = factory(); + Object.keys(temp).forEach(key => { + if (typeof root[key] === "undefined") { + root[key] = temp[key]; + } + }); + } }(this, function() { - function ScriptError(message) { - this.name = "ScriptError"; - this.message = message || ""; - this.stack = (new Error()).stack; - } + function ScriptError(message) { + this.name = "ScriptError"; + this.message = message || ""; + this.stack = (new Error()).stack; + } - ScriptError.prototype = Object.create(Error.prototype); - ScriptError.prototype.constructor = ScriptError; + ScriptError.prototype = Object.create(Error.prototype); + ScriptError.prototype.constructor = ScriptError; - /** + /** * get all running threads and return them as an array * @returns {Script[]} */ - const getThreads = function () { - let threads = []; - let script = getScript(); + const getThreads = function () { + let threads = []; + let script = getScript(); - if (script) { - do { - threads.push(copyObj(script)); - } while (script.getNext()); - } + if (script) { + do { + threads.push(copyObj(script)); + } while (script.getNext()); + } - return threads; - }; + return threads; + }; - /** + /** * @param {...args} * @returns {Unit[]} */ - const getUnits = function (...args) { - let units = [], unit = getUnit.apply(null, args); - - if (!unit) { - return []; - } - do { - units.push(copyUnit(unit)); - } while (unit.getNext()); - return units; - }; - - /** + const getUnits = function (...args) { + let units = [], unit = getUnit.apply(null, args); + + if (!unit) { + return []; + } + do { + units.push(copyUnit(unit)); + } while (unit.getNext()); + return units; + }; + + /** * @typedef {Object} Args * @property {0 | 1 | 2} arg1 - where * @property {number | ItemUnit} arg2 - bodyLoc to click, item to click, x coord @@ -73,29 +73,29 @@ * * @param {...Args} args */ - const clickItemAndWait = function (...args) { - let timeout = getTickCount(), timedOut; - let before = !me.itemoncursor; + const clickItemAndWait = function (...args) { + let timeout = getTickCount(), timedOut; + let before = !me.itemoncursor; - clickItem.apply(undefined, args); - delay(Math.max(me.ping * 2, 250)); + clickItem.apply(undefined, args); + delay(Math.max(me.ping * 2, 250)); - while (true) { // Wait until item is picked up. - delay(3); + while (true) { // Wait until item is picked up. + delay(3); - if (before !== !!me.itemoncursor || (timedOut = getTickCount() - timeout > Math.min(1000, 100 + (me.ping * 4)))) { - break; // quit the loop of item on cursor has changed - } - } + if (before !== !!me.itemoncursor || (timedOut = getTickCount() - timeout > Math.min(1000, 100 + (me.ping * 4)))) { + break; // quit the loop of item on cursor has changed + } + } - delay(Math.max(me.ping, 50)); + delay(Math.max(me.ping, 50)); - // return item if we didnt timeout - return !timedOut; - }; + // return item if we didnt timeout + return !timedOut; + }; - /** + /** * @description clickMap doesn't return if we sucessfully clicked a unit just that a click was sent, this checks and returns that a units mode has changed * as a result of us clicking it. * @param {number} button @@ -103,35 +103,35 @@ * @param {Unit} unit * @returns {boolean} If a units mode has changed as a result of clicking it */ - const clickUnitAndWait = function (button, shift, unit) { - if (typeof (unit) !== "object") throw new Error("clickUnitAndWait: Third arg must be a Unit."); + const clickUnitAndWait = function (button, shift, unit) { + if (typeof (unit) !== "object") throw new Error("clickUnitAndWait: Third arg must be a Unit."); - let before = unit.mode; + let before = unit.mode; - me.blockMouse = true; - clickMap(button, shift, unit); - delay(Math.max(me.ping * 2, 250)); - clickMap(button + 2, shift, unit); - me.blockMouse = false; + me.blockMouse = true; + clickMap(button, shift, unit); + delay(Math.max(me.ping * 2, 250)); + clickMap(button + 2, shift, unit); + me.blockMouse = false; - let waitTick = getTickCount(); - let timeOut = Math.min(1000, 100 + (me.ping * 4)); + let waitTick = getTickCount(); + let timeOut = Math.min(1000, 100 + (me.ping * 4)); - while (getTickCount() - waitTick < timeOut) { - delay(30); + while (getTickCount() - waitTick < timeOut) { + delay(30); - // quit the loop if mode has changed - if (before !== unit.mode) { - break; - } - } + // quit the loop if mode has changed + if (before !== unit.mode) { + break; + } + } - delay(Math.max(me.ping + 1, 50)); + delay(Math.max(me.ping + 1, 50)); - return (before !== unit.mode); - }; + return (before !== unit.mode); + }; - /** + /** * @class * @description new PacketBuilder() - create new packet object * @example (Spoof 'reassign player' packet to client): @@ -141,462 +141,465 @@ * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` */ - function PacketBuilder () { - // globals DataView ArrayBuffer - if (this.__proto__.constructor !== PacketBuilder) throw new Error("PacketBuilder must be called with 'new' operator!"); - - let queue = [], count = 0; - - // accepts any number of arguments - let enqueue = (type, size) => (...args) => { - args.forEach(arg => { - if (type === "String") { - arg = stringToEUC(arg); - size = arg.length + 1; - } - - queue.push({ type: type, size: size, data: arg }); - count += size; - }); - - return this; - }; - - /** @description size = 4 */ - this.float = enqueue("Float32", 4); - /** @description size = 4 */ - this.dword = enqueue("Uint32", 4); - /** @description size = 2 */ - this.word = enqueue("Uint16", 2); - /** @description size = 1 */ - this.byte = enqueue("Uint8", 1); - this.string = enqueue("String"); - - this.buildDataView = () => { - let dv = new DataView(new ArrayBuffer(count)), i = 0; - queue.forEach(field => { - if (field.type === "String") { - for (let l = 0; l < field.data.length; l++) { - dv.setUint8(i++, field.data.charCodeAt(l), true); - } - - i += field.size - field.data.length; // fix index for field.size !== field.data.length - } else { - dv["set" + field.type](i, field.data, true); - i += field.size; - } - }); - - return dv; - }; - - this.send = () => (sendPacket(this.buildDataView().buffer), this); - this.spoof = () => (getPacket(this.buildDataView().buffer), this); - this.get = this.spoof; // same thing but spoof has clearer intent than get - } - - const areaNames = [ - "None", - "Rogue Encampment", - "Blood Moor", - "Cold Plains", - "Stony Field", - "Dark Wood", - "Black Marsh", - "Tamoe Highland", - "Den Of Evil", - "Cave Level 1", - "Underground Passage Level 1", - "Hole Level 1", - "Pit Level 1", - "Cave Level 2", - "Underground Passage Level 2", - "Hole Level 2", - "Pit Level 2", - "Burial Grounds", - "Crypt", - "Mausoleum", - "Forgotten Tower", - "Tower Cellar Level 1", - "Tower Cellar Level 2", - "Tower Cellar Level 3", - "Tower Cellar Level 4", - "Tower Cellar Level 5", - "Monastery Gate", - "Outer Cloister", - "Barracks", - "Jail Level 1", - "Jail Level 2", - "Jail Level 3", - "Inner Cloister", - "Cathedral", - "Catacombs Level 1", - "Catacombs Level 2", - "Catacombs Level 3", - "Catacombs Level 4", - "Tristram", - "Moo Moo Farm", - "Lut Gholein", - "Rocky Waste", - "Dry Hills", - "Far Oasis", - "Lost City", - "Valley Of Snakes", - "Canyon Of The Magi", - "Sewers Level 1", - "Sewers Level 2", - "Sewers Level 3", - "Harem Level 1", - "Harem Level 2", - "Palace Cellar Level 1", - "Palace Cellar Level 2", - "Palace Cellar Level 3", - "Stony Tomb Level 1", - "Halls Of The Dead Level 1", - "Halls Of The Dead Level 2", - "Claw Viper Temple Level 1", - "Stony Tomb Level 2", - "Halls Of The Dead Level 3", - "Claw Viper Temple Level 2", - "Maggot Lair Level 1", - "Maggot Lair Level 2", - "Maggot Lair Level 3", - "Ancient Tunnels", - "Tal Rashas Tomb #1", - "Tal Rashas Tomb #2", - "Tal Rashas Tomb #3", - "Tal Rashas Tomb #4", - "Tal Rashas Tomb #5", - "Tal Rashas Tomb #6", - "Tal Rashas Tomb #7", - "Duriels Lair", - "Arcane Sanctuary", - "Kurast Docktown", - "Spider Forest", - "Great Marsh", - "Flayer Jungle", - "Lower Kurast", - "Kurast Bazaar", - "Upper Kurast", - "Kurast Causeway", - "Travincal", - "Spider Cave", - "Spider Cavern", - "Swampy Pit Level 1", - "Swampy Pit Level 2", - "Flayer Dungeon Level 1", - "Flayer Dungeon Level 2", - "Swampy Pit Level 3", - "Flayer Dungeon Level 3", - "Sewers Level 1", - "Sewers Level 2", - "Ruined Temple", - "Disused Fane", - "Forgotten Reliquary", - "Forgotten Temple", - "Ruined Fane", - "Disused Reliquary", - "Durance Of Hate Level 1", - "Durance Of Hate Level 2", - "Durance Of Hate Level 3", - "The Pandemonium Fortress", - "Outer Steppes", - "Plains Of Despair", - "City Of The Damned", - "River Of Flame", - "Chaos Sanctuary", - "Harrogath", - "Bloody Foothills", - "Frigid Highlands", - "Arreat Plateau", - "Crystalline Passage", - "Frozen River", - "Glacial Trail", - "Drifter Cavern", - "Frozen Tundra", - "Ancient's Way", - "Icy Cellar", - "Arreat Summit", - "Nihlathak's Temple", - "Halls Of Anguish", - "Halls Of Pain", - "Halls Of Vaught", - "Abaddon", - "Pit Of Acheron", - "Infernal Pit", - "Worldstone Keep Level 1", - "Worldstone Keep Level 2", - "Worldstone Keep Level 3", - "Throne Of Destruction", - "The Worldstone Chamber", - "Matron's Den", - "Forgotten Sands", - "Furnace of Pain", - "Tristram" - ]; - - /** @param {number} area */ - const getAreaName = function (area) { - if (["number", "string"].indexOf(typeof area) === -1) return "undefined"; - if (typeof area === "string") return area; - return (areaNames[area] || "undefined"); - }; - - const Game = { - getDistance: function (...args) { - switch (args.length) { - case 0: - return Infinity; - case 1: - // getDistance(unit) - returns distance that unit is from me - if (typeof args[0] !== "object") return Infinity; - if (!args[0].hasOwnProperty("x")) return Infinity; - return Math.sqrt(Math.pow((me.x - args[0].x), 2) + Math.pow((me.y - args[0].y), 2)); - case 2: - // getDistance(x, y) - returns distance x, y is from me - // getDistance(unitA, unitB) - returns distace unitA is from unitB - if (typeof args[0] === "number" && typeof args[1] === "number") { - return Math.sqrt(Math.pow((me.x - args[0]), 2) + Math.pow((me.y - args[1]), 2)); - } else if (typeof args[0] === "object" && typeof args[1] === "object") { - if (!args[1].hasOwnProperty("x")) return Infinity; - return Math.sqrt(Math.pow((args[0].x - args[1].x), 2) + Math.pow((args[0].y - args[1].y), 2)); - } - return Infinity; - case 3: - // getDistance(unit, x, y) - returns distance x, y is from unit - if (typeof args[2] !== "number") return Infinity; - if (!args[0].hasOwnProperty("x")) return Infinity; - return Math.sqrt(Math.pow((args[0].x - args[1]), 2) + Math.pow((args[0].y - args[2]), 2)); - case 4: - // getDistance(x1, y1, x2, y2) - if (typeof args[0] !== "number" || typeof args[3] !== "number") return Infinity; - return Math.sqrt(Math.pow((args[0] - args[2]), 2) + Math.pow((args[1] - args[3]), 2)); - default: - return Infinity; - } - }, - /** + function PacketBuilder () { + // globals DataView ArrayBuffer + if (this.__proto__.constructor !== PacketBuilder) { + throw new Error("PacketBuilder must be called with 'new' operator!"); + } + + let queue = [], count = 0; + + // accepts any number of arguments + let enqueue = (type, size) => (...args) => { + args.forEach(arg => { + if (type === "String") { + arg = stringToEUC(arg); + size = arg.length + 1; + } + + queue.push({ type: type, size: size, data: arg }); + count += size; + }); + + return this; + }; + + /** @description size = 4 */ + this.float = enqueue("Float32", 4); + /** @description size = 4 */ + this.dword = enqueue("Uint32", 4); + /** @description size = 2 */ + this.word = enqueue("Uint16", 2); + /** @description size = 1 */ + this.byte = enqueue("Uint8", 1); + this.string = enqueue("String"); + + this.buildDataView = () => { + let dv = new DataView(new ArrayBuffer(count)), i = 0; + queue.forEach(field => { + if (field.type === "String") { + for (let l = 0; l < field.data.length; l++) { + dv.setUint8(i++, field.data.charCodeAt(l), true); + } + + i += field.size - field.data.length; // fix index for field.size !== field.data.length + } else { + dv["set" + field.type](i, field.data, true); + i += field.size; + } + }); + + return dv; + }; + + this.send = () => (sendPacket(this.buildDataView().buffer), this); + this.spoof = () => (getPacket(this.buildDataView().buffer), this); + this.get = this.spoof; // same thing but spoof has clearer intent than get + } + + const areaNames = [ + "None", + "Rogue Encampment", + "Blood Moor", + "Cold Plains", + "Stony Field", + "Dark Wood", + "Black Marsh", + "Tamoe Highland", + "Den Of Evil", + "Cave Level 1", + "Underground Passage Level 1", + "Hole Level 1", + "Pit Level 1", + "Cave Level 2", + "Underground Passage Level 2", + "Hole Level 2", + "Pit Level 2", + "Burial Grounds", + "Crypt", + "Mausoleum", + "Forgotten Tower", + "Tower Cellar Level 1", + "Tower Cellar Level 2", + "Tower Cellar Level 3", + "Tower Cellar Level 4", + "Tower Cellar Level 5", + "Monastery Gate", + "Outer Cloister", + "Barracks", + "Jail Level 1", + "Jail Level 2", + "Jail Level 3", + "Inner Cloister", + "Cathedral", + "Catacombs Level 1", + "Catacombs Level 2", + "Catacombs Level 3", + "Catacombs Level 4", + "Tristram", + "Moo Moo Farm", + "Lut Gholein", + "Rocky Waste", + "Dry Hills", + "Far Oasis", + "Lost City", + "Valley Of Snakes", + "Canyon Of The Magi", + "Sewers Level 1", + "Sewers Level 2", + "Sewers Level 3", + "Harem Level 1", + "Harem Level 2", + "Palace Cellar Level 1", + "Palace Cellar Level 2", + "Palace Cellar Level 3", + "Stony Tomb Level 1", + "Halls Of The Dead Level 1", + "Halls Of The Dead Level 2", + "Claw Viper Temple Level 1", + "Stony Tomb Level 2", + "Halls Of The Dead Level 3", + "Claw Viper Temple Level 2", + "Maggot Lair Level 1", + "Maggot Lair Level 2", + "Maggot Lair Level 3", + "Ancient Tunnels", + "Tal Rashas Tomb #1", + "Tal Rashas Tomb #2", + "Tal Rashas Tomb #3", + "Tal Rashas Tomb #4", + "Tal Rashas Tomb #5", + "Tal Rashas Tomb #6", + "Tal Rashas Tomb #7", + "Duriels Lair", + "Arcane Sanctuary", + "Kurast Docktown", + "Spider Forest", + "Great Marsh", + "Flayer Jungle", + "Lower Kurast", + "Kurast Bazaar", + "Upper Kurast", + "Kurast Causeway", + "Travincal", + "Spider Cave", + "Spider Cavern", + "Swampy Pit Level 1", + "Swampy Pit Level 2", + "Flayer Dungeon Level 1", + "Flayer Dungeon Level 2", + "Swampy Pit Level 3", + "Flayer Dungeon Level 3", + "Sewers Level 1", + "Sewers Level 2", + "Ruined Temple", + "Disused Fane", + "Forgotten Reliquary", + "Forgotten Temple", + "Ruined Fane", + "Disused Reliquary", + "Durance Of Hate Level 1", + "Durance Of Hate Level 2", + "Durance Of Hate Level 3", + "The Pandemonium Fortress", + "Outer Steppes", + "Plains Of Despair", + "City Of The Damned", + "River Of Flame", + "Chaos Sanctuary", + "Harrogath", + "Bloody Foothills", + "Frigid Highlands", + "Arreat Plateau", + "Crystalline Passage", + "Frozen River", + "Glacial Trail", + "Drifter Cavern", + "Frozen Tundra", + "Ancient's Way", + "Icy Cellar", + "Arreat Summit", + "Nihlathak's Temple", + "Halls Of Anguish", + "Halls Of Pain", + "Halls Of Vaught", + "Abaddon", + "Pit Of Acheron", + "Infernal Pit", + "Worldstone Keep Level 1", + "Worldstone Keep Level 2", + "Worldstone Keep Level 3", + "Throne Of Destruction", + "The Worldstone Chamber", + "Matron's Den", + "Forgotten Sands", + "Furnace of Pain", + "Tristram" + ]; + + /** @param {number} area */ + const getAreaName = function (area) { + if (["number", "string"].indexOf(typeof area) === -1) return "undefined"; + if (typeof area === "string") return area; + return (areaNames[area] || "undefined"); + }; + + const Game = { + getDistance: function (...args) { + switch (args.length) { + case 0: + return Infinity; + case 1: + // getDistance(unit) - returns distance that unit is from me + if (typeof args[0] !== "object") return Infinity; + if (!args[0].hasOwnProperty("x")) return Infinity; + return Math.sqrt(Math.pow((me.x - args[0].x), 2) + Math.pow((me.y - args[0].y), 2)); + case 2: + // getDistance(x, y) - returns distance x, y is from me + // getDistance(unitA, unitB) - returns distace unitA is from unitB + if (typeof args[0] === "number" && typeof args[1] === "number") { + return Math.sqrt(Math.pow((me.x - args[0]), 2) + Math.pow((me.y - args[1]), 2)); + } else if (typeof args[0] === "object" && typeof args[1] === "object") { + if (!args[1].hasOwnProperty("x")) return Infinity; + return Math.sqrt(Math.pow((args[0].x - args[1].x), 2) + Math.pow((args[0].y - args[1].y), 2)); + } + return Infinity; + case 3: + // getDistance(unit, x, y) - returns distance x, y is from unit + if (typeof args[2] !== "number") return Infinity; + if (!args[0].hasOwnProperty("x")) return Infinity; + return Math.sqrt(Math.pow((args[0].x - args[1]), 2) + Math.pow((args[0].y - args[2]), 2)); + case 4: + // getDistance(x1, y1, x2, y2) + if (typeof args[0] !== "number" || typeof args[3] !== "number") return Infinity; + return Math.sqrt(Math.pow((args[0] - args[2]), 2) + Math.pow((args[1] - args[3]), 2)); + default: + return Infinity; + } + }, + /** * @returns {ItemUnit | undefined} item on cursor */ - getCursorUnit: function () { - return getUnit(100); - }, - /** + getCursorUnit: function () { + return getUnit(100); + }, + /** * @returns {ItemUnit | undefined} item cursor is hovering over */ - getSelectedUnit: function () { - return getUnit(101); - }, - /** + getSelectedUnit: function () { + return getUnit(101); + }, + /** * @param {number | string} [id] * @param {number} [mode] * @param {number} [gid] * @returns {Player} */ - getPlayer: function (id, mode, gid) { - return getUnit(sdk.unittype.Player, id, mode, gid); - }, - /** + getPlayer: function (id, mode, gid) { + return getUnit(sdk.unittype.Player, id, mode, gid); + }, + /** * @param {number | string} [id] * @param {number} [mode] * @param {number} [gid] * @returns {Monster} */ - getMonster: function (id, mode, gid) { - return getUnit(sdk.unittype.Monster, id, mode, gid); - }, - /** + getMonster: function (id, mode, gid) { + return getUnit(sdk.unittype.Monster, id, mode, gid); + }, + /** * @param {number | string} [id] * @param {number} [mode] * @param {number} [gid] * @returns {Monster} */ - getNPC: function (id, mode, gid) { - return getUnit(sdk.unittype.NPC, id, mode, gid); - }, - /** + getNPC: function (id, mode, gid) { + return getUnit(sdk.unittype.NPC, id, mode, gid); + }, + /** * @param {number | string} [id] * @param {number} [mode] * @param {number} [gid] * @returns {ObjectUnit} */ - getObject: function (id, mode, gid) { - return getUnit(sdk.unittype.Object, id, mode, gid); - }, - /** + getObject: function (id, mode, gid) { + return getUnit(sdk.unittype.Object, id, mode, gid); + }, + /** * @param {number | string} [id] * @param {number} [mode] * @param {number} [gid] * @returns {Missile} */ - getMissile: function (id, mode, gid) { - return getUnit(sdk.unittype.Missile, id, mode, gid); - }, - /** + getMissile: function (id, mode, gid) { + return getUnit(sdk.unittype.Missile, id, mode, gid); + }, + /** * @param {number | string} [id] * @param {number} [mode] * @param {number} [gid] * @returns {ItemUnit} */ - getItem: function (id, mode, gid) { - return getUnit(sdk.unittype.Item, id, mode, gid); - }, - /** + getItem: function (id, mode, gid) { + return getUnit(sdk.unittype.Item, id, mode, gid); + }, + /** * @param {number | string} [id] * @param {number} [mode] * @param {number} [gid] * @returns {Tile} */ - getStairs: function (id, mode, gid) { - return getUnit(sdk.unittype.Stairs, id, mode, gid); - }, - /** + getStairs: function (id, mode, gid) { + return getUnit(sdk.unittype.Stairs, id, mode, gid); + }, + /** * @param {number} area * @param {number} id * @returns {PresetUnit} */ - getPresetMonster: function (area, id) { - !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Monster, id); - }, - /** + getPresetMonster: function (area, id) { + !area && (area = me.area); + return getPresetUnit(area, sdk.unittype.Monster, id); + }, + /** * @param {number} area * @param {number} id * @returns {PresetUnit[]} */ - getPresetMonsters: function (area, id) { - !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Monster, id); - }, - /** + getPresetMonsters: function (area, id) { + !area && (area = me.area); + return getPresetUnits(area, sdk.unittype.Monster, id); + }, + /** * @param {number} area * @param {number} id * @returns {PresetUnit} */ - getPresetObject: function (area, id) { - !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Object, id); - }, - /** + getPresetObject: function (area, id) { + !area && (area = me.area); + return getPresetUnit(area, sdk.unittype.Object, id); + }, + /** * @param {number} area * @param {number} id * @returns {PresetUnit[]} */ - getPresetObjects: function (area, id) { - !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Object, id); - }, - /** + getPresetObjects: function (area, id) { + !area && (area = me.area); + return getPresetUnits(area, sdk.unittype.Object, id); + }, + /** * @param {number} area * @param {number} id * @returns {PresetUnit} */ - getPresetStair: function (area, id) { - !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Stairs, id); - }, - /** + getPresetStair: function (area, id) { + !area && (area = me.area); + return getPresetUnit(area, sdk.unittype.Stairs, id); + }, + /** * @param {number} area * @param {number} id * @returns {PresetUnit[]} */ - getPresetStairs: function (area, id) { - !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Stairs, id); - }, - }; - - const Sort = { - // Sort units by comparing distance between the player - units: function (a, b) { - return Math.round(getDistance(me.x, me.y, a.x, a.y)) - Math.round(getDistance(me.x, me.y, b.x, b.y)); - }, - - // Sort preset units by comparing distance between the player (using preset x/y calculations) - presetUnits: function (a, b) { - return getDistance(me, a.roomx * 5 + a.x, a.roomy * 5 + a.y) - getDistance(me, b.roomx * 5 + b.x, b.roomy * 5 + b.y); - }, - - // Sort arrays of x,y coords by comparing distance between the player - points: function (a, b) { - return getDistance(me, a[0], a[1]) - getDistance(me, b[0], b[1]); - }, - - numbers: function (a, b) { - return a - b; - } - }; - - const Messaging = { - sendToScript: function (name, msg) { - let script = getScript(name); - - if (script && script.running) { - script.send(msg); - - return true; - } - - return false; - }, - - sendToProfile: function (profileName, mode, message, getResponse = false) { - let response; - - function copyDataEvent(mode2, msg) { - if (mode2 === mode) { - let obj; - - try { - obj = JSON.parse(msg); - } catch (e) { - return false; - } - - if (obj.hasOwnProperty("sender") && obj.sender === profileName) { - response = copyObj(obj); - } - - return true; - } - - return false; - } - - getResponse && addEventListener("copydata", copyDataEvent); - - if (!sendCopyData(null, profileName, mode, JSON.stringify({ message: message, sender: me.profile }))) { - //print("sendToProfile: failed to get response from " + profileName); - getResponse && removeEventListener("copydata", copyDataEvent); - - return false; - } - - if (getResponse) { - delay(200); - removeEventListener("copydata", copyDataEvent); - - if (!!response) { - return response; - } - - return false; - } - - return true; - } - }; - - return { - getThreads: getThreads, - getUnits: getUnits, - clickItemAndWait: clickItemAndWait, - clickUnitAndWait: clickUnitAndWait, - PacketBuilder: PacketBuilder, - getAreaName: getAreaName, - Game: Game, - Sort: Sort, - Messaging: Messaging, - ScriptError: ScriptError, - }; + getPresetStairs: function (area, id) { + !area && (area = me.area); + return getPresetUnits(area, sdk.unittype.Stairs, id); + }, + }; + + const Sort = { + // Sort units by comparing distance between the player + units: function (a, b) { + return Math.round(getDistance(me.x, me.y, a.x, a.y)) - Math.round(getDistance(me.x, me.y, b.x, b.y)); + }, + + // Sort preset units by comparing distance between the player (using preset x/y calculations) + presetUnits: function (a, b) { + return getDistance(me, a.roomx * 5 + a.x, a.roomy * 5 + a.y) + - getDistance(me, b.roomx * 5 + b.x, b.roomy * 5 + b.y); + }, + + // Sort arrays of x,y coords by comparing distance between the player + points: function (a, b) { + return getDistance(me, a[0], a[1]) - getDistance(me, b[0], b[1]); + }, + + numbers: function (a, b) { + return a - b; + } + }; + + const Messaging = { + sendToScript: function (name, msg) { + let script = getScript(name); + + if (script && script.running) { + script.send(msg); + + return true; + } + + return false; + }, + + sendToProfile: function (profileName, mode, message, getResponse = false) { + let response; + + function copyDataEvent(mode2, msg) { + if (mode2 === mode) { + let obj; + + try { + obj = JSON.parse(msg); + } catch (e) { + return false; + } + + if (obj.hasOwnProperty("sender") && obj.sender === profileName) { + response = copyObj(obj); + } + + return true; + } + + return false; + } + + getResponse && addEventListener("copydata", copyDataEvent); + + if (!sendCopyData(null, profileName, mode, JSON.stringify({ message: message, sender: me.profile }))) { + //print("sendToProfile: failed to get response from " + profileName); + getResponse && removeEventListener("copydata", copyDataEvent); + + return false; + } + + if (getResponse) { + delay(200); + removeEventListener("copydata", copyDataEvent); + + if (!!response) { + return response; + } + + return false; + } + + return true; + } + }; + + return { + getThreads: getThreads, + getUnits: getUnits, + clickItemAndWait: clickItemAndWait, + clickUnitAndWait: clickUnitAndWait, + PacketBuilder: PacketBuilder, + getAreaName: getAreaName, + Game: Game, + Sort: Sort, + Messaging: Messaging, + ScriptError: ScriptError, + }; })); diff --git a/d2bs/kolbot/libs/json2.js b/d2bs/kolbot/libs/json2.js index 6b87b5885..2d70be278 100644 --- a/d2bs/kolbot/libs/json2.js +++ b/d2bs/kolbot/libs/json2.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /* http://www.JSON.org/json2.js 2011-10-19 @@ -161,329 +162,329 @@ var JSON; if (!JSON) { - JSON = {}; + JSON = {}; } (function () { - //'use strict'; + //'use strict'; - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } - if (typeof Date.prototype.toJSON !== 'function') { + if (typeof Date.prototype.toJSON !== 'function') { - Date.prototype.toJSON = function (key) { + Date.prototype.toJSON = function (key) { - return isFinite(this.valueOf()) - ? this.getUTCFullYear() + '-' + + return isFinite(this.valueOf()) + ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' - : null; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' + : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { - return this.valueOf(); + return this.valueOf(); }; - } - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' - ? c - : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value && typeof value === 'object' && + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + + // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // Otherwise we must also replace the offending characters with safe escape + // sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' + ? c + : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + function str(key, holder) { + + // Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + + // If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. + value = value.toJSON(key); + } - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } + // If we were called with a replacer function, then call the replacer to + // obtain a replacement value. -// What happens next depends on the value's type. + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } - switch (typeof value) { - case 'string': - return quote(value); + // What happens next depends on the value's type. - case 'number': + switch (typeof value) { + case 'string': + return quote(value); -// JSON numbers must be finite. Encode non-finite numbers as null. + case 'number': - return isFinite(value) ? String(value) : 'null'; + // JSON numbers must be finite. Encode non-finite numbers as null. - case 'boolean': - case 'null': + return isFinite(value) ? String(value) : 'null'; -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. + case 'boolean': + case 'null': - return String(value); + // If the value is a boolean or null, convert it to a string. Note: + // typeof null does not produce 'null'. The case is included here in + // the remote chance that this gets fixed someday. -// If the type is 'object', we might be dealing with an object or an array or -// null. + return String(value); - case 'object': + // If the type is 'object', we might be dealing with an object or an array or + // null. -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. + case 'object': - if (!value) { - return 'null'; - } + // Due to a specification blunder in ECMAScript, typeof null is 'object', + // so watch out for that case. -// Make an array to hold the partial results of stringifying this object value. + if (!value) { + return 'null'; + } - gap += indent; - partial = []; + // Make an array to hold the partial results of stringifying this object value. -// Is the value an array? + gap += indent; + partial = []; - if (Object.prototype.toString.apply(value) === '[object Array]') { + // Is the value an array? -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. + if (Object.prototype.toString.apply(value) === '[object Array]') { - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } + // The value is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. -// Join all of the elements together, separated with commas, and wrap them in -// brackets. + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } - v = partial.length === 0 - ? '[]' - : gap - ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' - : '[' + partial.join(',') + ']'; - gap = mind; - return v; + // Join all of the elements together, separated with commas, and wrap them in + // brackets. + + v = partial.length === 0 + ? '[]' + : gap + ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' + : '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + + // If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); } + } + } + } else { -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. + // Otherwise, iterate through all of the keys in the object. - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 - ? '{}' - : gap - ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' - : '{' + partial.join(',') + '}'; - gap = mind; - return v; + } } - - return 'null'; + } + + // Join all of the member texts together, separated with commas, + // and wrap them in braces. + + v = partial.length === 0 + ? '{}' + : gap + ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' + : '{' + partial.join(',') + '}'; + gap = mind; + return v; } -// If the JSON object does not yet have a stringify method, give it one. + return 'null'; + } - if (typeof JSON.stringify !== 'function') { - JSON.stringify = function (value, replacer, space) { + // If the JSON object does not yet have a stringify method, give it one. -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { - var i; - gap = ''; - indent = ''; + // The stringify method takes a value and an optional replacer, and an optional + // space parameter, and returns a JSON text. The replacer can be a function + // that can replace values, or an array of strings that will select the keys. + // A default replacer method can be provided. Use of the space parameter can + // produce text that is more easily readable. -// If the space parameter is a number, make an indent string containing that -// many spaces. + var i; + gap = ''; + indent = ''; - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } + // If the space parameter is a number, make an indent string containing that + // many spaces. -// If the space parameter is a string, it will be used as the indent string. + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } - } else if (typeof space === 'string') { - indent = space; - } + // If the space parameter is a string, it will be used as the indent string. -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. + } else if (typeof space === 'string') { + indent = space; + } - rep = replacer; - if (replacer && typeof replacer !== 'function' && + // If there is a replacer, it must be a function or an array. + // Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } + throw new Error('JSON.stringify'); + } -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. + // Make a fake root object containing our value under the key of ''. + // Return the result of stringifying the value. - return str('', {'': value}); - }; - } + return str('', {'': value}); + }; + } -// If the JSON object does not yet have a parse method, give it one. + // If the JSON object does not yet have a parse method, give it one. - if (typeof JSON.parse !== 'function') { - JSON.parse = function (text, reviver) { + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { -// The parse method takes a text and an optional reviver function, and returns -// a JavaScript value if the text is a valid JSON text. + // The parse method takes a text and an optional reviver function, and returns + // a JavaScript value if the text is a valid JSON text. - var j; + var j; - function walk(holder, key) { + function walk(holder, key) { -// The walk method is used to recursively walk the resulting structure so -// that modifications can be made. + // The walk method is used to recursively walk the resulting structure so + // that modifications can be made. - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } } + } + } + return reviver.call(holder, key, value); + } -// Parsing happens in four stages. In the first stage, we replace certain -// Unicode characters with escape sequences. JavaScript handles many characters -// incorrectly, either silently deleting them, or treating them as line endings. + // Parsing happens in four stages. In the first stage, we replace certain + // Unicode characters with escape sequences. JavaScript handles many characters + // incorrectly, either silently deleting them, or treating them as line endings. - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - -// In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. -// But just to be safe, we want to reject all unexpected forms. - -// We split the second stage into 4 regexp operations in order to work around -// crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all -// open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - -// In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity -// in JavaScript: it can begin a block or an object literal. We wrap the text -// in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - -// In the optional fourth stage, we recursively walk the new structure, passing -// each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' - ? walk({'': j}, '') - : j; - } - -// If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } + }); + } + + // In the second stage, we run the text against regular expressions that look + // for non-JSON patterns. We are especially concerned with '()' and 'new' + // because they can cause invocation, and '=' because it can cause mutation. + // But just to be safe, we want to reject all unexpected forms. + + // We split the second stage into 4 regexp operations in order to work around + // crippling inefficiencies in IE's and Safari's regexp engines. First we + // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we + // replace all simple value tokens with ']' characters. Third, we delete all + // open brackets that follow a colon or comma or that begin the text. Finally, + // we look to see that the remaining characters are only whitespace or ']' or + // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + // In the third stage we use the eval function to compile the text into a + // JavaScript structure. The '{' operator is subject to a syntactic ambiguity + // in JavaScript: it can begin a block or an object literal. We wrap the text + // in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + + // In the optional fourth stage, we recursively walk the new structure, passing + // each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' + ? walk({'': j}, '') + : j; + } + + // If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } }()); diff --git a/d2bs/kolbot/libs/manualplay/MapMode.js b/d2bs/kolbot/libs/manualplay/MapMode.js index fcc883268..f38c58d98 100644 --- a/d2bs/kolbot/libs/manualplay/MapMode.js +++ b/d2bs/kolbot/libs/manualplay/MapMode.js @@ -6,61 +6,61 @@ */ const MapMode = { - mapHelperFilePath: "libs/manualplay/threads/maphelper.js", - include: function () { - let files = dopen("libs/manualplay/libs/").getFiles(); + mapHelperFilePath: "libs/manualplay/threads/maphelper.js", + include: function () { + let files = dopen("libs/manualplay/libs/").getFiles(); - Array.isArray(files) && files - .filter(file => file.endsWith(".js")) - .forEach(function (x) { - if (!isIncluded("manualplay/libs/" + x)) { - if (!include("manualplay/libs/" + x)) { - throw new Error("Failed to include " + "manualplay/libs/" + x); - } - } - }); - }, + Array.isArray(files) && files + .filter(file => file.endsWith(".js")) + .forEach(function (x) { + if (!isIncluded("manualplay/libs/" + x)) { + if (!include("manualplay/libs/" + x)) { + throw new Error("Failed to include " + "manualplay/libs/" + x); + } + } + }); + }, - generalSettings: function () { - Config.MapMode.UseOwnItemFilter = false; // set to true if you want to start with your own nip files as the loot filter vs starting with default. - // General - Config.WaypointMenu = true; - Config.MiniShopBot = false; // Scan items in NPC shops. - Config.PacketShopping = true; // Use packets to shop. Improves shopping speed. - Config.TownCheck = false; // Go to town if out of potions + generalSettings: function () { + Config.MapMode.UseOwnItemFilter = false; // set to true if you want to start with your own nip files as the loot filter vs starting with default. + // General + Config.WaypointMenu = true; + Config.MiniShopBot = false; // Scan items in NPC shops. + Config.PacketShopping = true; // Use packets to shop. Improves shopping speed. + Config.TownCheck = false; // Go to town if out of potions - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - Config.ShowCubingInfo = true; // Show cubing messages on console + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + Config.ShowCubingInfo = true; // Show cubing messages on console - // Gambling config - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; + // Gambling config + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - }, + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + }, }; diff --git a/d2bs/kolbot/libs/manualplay/config/Amazon.js b/d2bs/kolbot/libs/manualplay/config/Amazon.js index fcad5bf33..0f4463625 100644 --- a/d2bs/kolbot/libs/manualplay/config/Amazon.js +++ b/d2bs/kolbot/libs/manualplay/config/Amazon.js @@ -83,7 +83,7 @@ function LoadConfig() { // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Assassin.js b/d2bs/kolbot/libs/manualplay/config/Assassin.js index 1200fc96d..2111c82ac 100644 --- a/d2bs/kolbot/libs/manualplay/config/Assassin.js +++ b/d2bs/kolbot/libs/manualplay/config/Assassin.js @@ -83,7 +83,7 @@ function LoadConfig() { // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Barbarian.js b/d2bs/kolbot/libs/manualplay/config/Barbarian.js index 76114a390..651f00746 100644 --- a/d2bs/kolbot/libs/manualplay/config/Barbarian.js +++ b/d2bs/kolbot/libs/manualplay/config/Barbarian.js @@ -83,7 +83,7 @@ function LoadConfig() { // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Druid.js b/d2bs/kolbot/libs/manualplay/config/Druid.js index 64ab4cdc1..56782119d 100644 --- a/d2bs/kolbot/libs/manualplay/config/Druid.js +++ b/d2bs/kolbot/libs/manualplay/config/Druid.js @@ -83,7 +83,7 @@ function LoadConfig() { // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Necromancer.js b/d2bs/kolbot/libs/manualplay/config/Necromancer.js index 6fed93d6e..f2a86f5ec 100644 --- a/d2bs/kolbot/libs/manualplay/config/Necromancer.js +++ b/d2bs/kolbot/libs/manualplay/config/Necromancer.js @@ -83,7 +83,7 @@ function LoadConfig() { // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Paladin.js b/d2bs/kolbot/libs/manualplay/config/Paladin.js index f55bb752a..2f7c09589 100644 --- a/d2bs/kolbot/libs/manualplay/config/Paladin.js +++ b/d2bs/kolbot/libs/manualplay/config/Paladin.js @@ -83,7 +83,7 @@ function LoadConfig() { // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Sorceress.js b/d2bs/kolbot/libs/manualplay/config/Sorceress.js index c66f21c88..828eee6ba 100644 --- a/d2bs/kolbot/libs/manualplay/config/Sorceress.js +++ b/d2bs/kolbot/libs/manualplay/config/Sorceress.js @@ -86,7 +86,7 @@ function LoadConfig() { // General config Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js index 36910ce58..bd5e7c5bd 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename ActionHooks.js * @author theBGuy @@ -6,779 +7,779 @@ */ const ActionHooks = { - hooks: [], - portals: [], - frame: [], - action: null, - currArea: 0, - enabled: true, - prevAreas: [ - sdk.areas.None, sdk.areas.None, sdk.areas.RogueEncampment, sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland, sdk.areas.CaveLvl1, sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, - sdk.areas.PitLvl1, sdk.areas.ColdPlains, sdk.areas.BurialGrounds, sdk.areas.BurialGrounds, sdk.areas.BlackMarsh, sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, - sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TamoeHighland, sdk.areas.MonasteryGate, sdk.areas.OuterCloister, sdk.areas.Barracks, sdk.areas.JailLvl1, sdk.areas.JailLvl2, - sdk.areas.JailLvl3, sdk.areas.InnerCloister, sdk.areas.Cathedral, sdk.areas.CatacombsLvl1, sdk.areas.CatacombsLvl2, sdk.areas.CatacombsLvl3, sdk.areas.StonyField, sdk.areas.RogueEncampment, - sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.ArcaneSanctuary, sdk.areas.LutGholein, - sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.LutGholein, sdk.areas.HaremLvl1, sdk.areas.HaremLvl2, sdk.areas.PalaceCellarLvl1, sdk.areas.PalaceCellarLvl2, sdk.areas.RockyWaste, - sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl1, sdk.areas.ValleyofSnakes, sdk.areas.StonyTombLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.ClawViperTempleLvl1, sdk.areas.FarOasis, - sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.LostCity, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, - sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.RogueEncampment, sdk.areas.PalaceCellarLvl3, sdk.areas.RogueEncampment, sdk.areas.KurastDocktown, sdk.areas.SpiderForest, - sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, - sdk.areas.SpiderForest, sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.SwampyPitLvl1, sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, sdk.areas.SwampyPitLvl2, sdk.areas.FlayerDungeonLvl2, - sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.KurastBazaar, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.KurastCauseway, - sdk.areas.Travincal, sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2, sdk.areas.DuranceofHateLvl3, sdk.areas.PandemoniumFortress, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, - sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, sdk.areas.PandemoniumFortress, sdk.areas.Harrogath, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, - sdk.areas.CrystalizedPassage, sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.AncientsWay, sdk.areas.Harrogath, - sdk.areas.NihlathaksTemple, sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.FrozenTundra, sdk.areas.ArreatSummit, sdk.areas.WorldstoneLvl1, - sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath - ], - areaInfo: {}, - ctrlObj: { - 0: { - 3: "moveItemFromInvoToTrade", - 5: "moveItemFromTradeToInvo" - }, - 1: { - 3: "moveItemFromInvoToStash", - 7: "moveItemFromStashToInvo" - }, - 2: { - 3: "moveItemFromInvoToCube", - 6: "moveItemFromCubeToInvo" - }, - 3: "sellItem" - }, - blockKeyEvent: () => [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Waypoint, - sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen - ].some((flag) => getUIFlag(flag)), - - /** + hooks: [], + portals: [], + frame: [], + action: null, + currArea: 0, + enabled: true, + prevAreas: [ + sdk.areas.None, sdk.areas.None, sdk.areas.RogueEncampment, sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, + sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland, sdk.areas.CaveLvl1, sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, + sdk.areas.PitLvl1, sdk.areas.ColdPlains, sdk.areas.BurialGrounds, sdk.areas.BurialGrounds, sdk.areas.BlackMarsh, sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, + sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TamoeHighland, sdk.areas.MonasteryGate, sdk.areas.OuterCloister, sdk.areas.Barracks, sdk.areas.JailLvl1, sdk.areas.JailLvl2, + sdk.areas.JailLvl3, sdk.areas.InnerCloister, sdk.areas.Cathedral, sdk.areas.CatacombsLvl1, sdk.areas.CatacombsLvl2, sdk.areas.CatacombsLvl3, sdk.areas.StonyField, sdk.areas.RogueEncampment, + sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.ArcaneSanctuary, sdk.areas.LutGholein, + sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.LutGholein, sdk.areas.HaremLvl1, sdk.areas.HaremLvl2, sdk.areas.PalaceCellarLvl1, sdk.areas.PalaceCellarLvl2, sdk.areas.RockyWaste, + sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl1, sdk.areas.ValleyofSnakes, sdk.areas.StonyTombLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.ClawViperTempleLvl1, sdk.areas.FarOasis, + sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.LostCity, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, + sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.RogueEncampment, sdk.areas.PalaceCellarLvl3, sdk.areas.RogueEncampment, sdk.areas.KurastDocktown, sdk.areas.SpiderForest, + sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, + sdk.areas.SpiderForest, sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.SwampyPitLvl1, sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, sdk.areas.SwampyPitLvl2, sdk.areas.FlayerDungeonLvl2, + sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.KurastBazaar, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.KurastCauseway, + sdk.areas.Travincal, sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2, sdk.areas.DuranceofHateLvl3, sdk.areas.PandemoniumFortress, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, + sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, sdk.areas.PandemoniumFortress, sdk.areas.Harrogath, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, + sdk.areas.CrystalizedPassage, sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.AncientsWay, sdk.areas.Harrogath, + sdk.areas.NihlathaksTemple, sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.FrozenTundra, sdk.areas.ArreatSummit, sdk.areas.WorldstoneLvl1, + sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath + ], + areaInfo: {}, + ctrlObj: { + 0: { + 3: "moveItemFromInvoToTrade", + 5: "moveItemFromTradeToInvo" + }, + 1: { + 3: "moveItemFromInvoToStash", + 7: "moveItemFromStashToInvo" + }, + 2: { + 3: "moveItemFromInvoToCube", + 6: "moveItemFromCubeToInvo" + }, + 3: "sellItem" + }, + blockKeyEvent: () => [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Waypoint, + sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen + ].some((flag) => getUIFlag(flag)), + + /** * Set action based on key input * @param {number} keycode * @returns {void} * @todo this would probably be better as pushing to an action stack and implementing a timeout to prevent spamming the same action */ - event: function (keycode) { - if ([sdk.keys.Shift, sdk.keys.Alt].some(k => k === keycode)) { - return; - } - - ActionHooks.action = keycode; - }, - - getOnScreenLocation: function () { - let possibleLocs = [sdk.uiflags.TradePrompt, sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Shop]; - - for (let i = 0; i < possibleLocs.length; i++) { - if (getUIFlag(possibleLocs[i])) { - return possibleLocs.indexOf(possibleLocs[i]); - } - } - - return -1; - }, - - checkAction: function () { - let hook; - let unit, screenLoc; - let obj = { type: false, dest: false, action: false }; - let qolObj = { type: "qol", dest: false, action: false }; - - if (this.action) { - try { - // quick ones first - ends checkAction if one of these was true - if ([sdk.keys.Seven, sdk.keys.Eight, sdk.keys.Nine, sdk.keys.NumpadDash].includes(this.action)) { - if (this.blockKeyEvent()) return; - switch (this.action) { - case sdk.keys.Seven: - if (TextHooks.displaySettings) { - TextHooks.getHook("itemStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 7ÿc0: " + (ItemHooks.enabled ? "Enable" : "Disable") + " Item Filter"; - } - ItemHooks.enabled = !ItemHooks.enabled; - - break; - case sdk.keys.Eight: - if (TextHooks.displaySettings) { - TextHooks.getHook("monsterStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 8ÿc0: " + (MonsterHooks.enabled ? "Enable" : "Disable") + " Monsters"; - } - MonsterHooks.enabled = !MonsterHooks.enabled; - - break; - case sdk.keys.Nine: - if (TextHooks.displaySettings) { - TextHooks.getHook("vectorStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 9ÿc0: " + (VectorHooks.enabled ? "Enable" : "Disable") + " Vectors"; - } - VectorHooks.enabled = !VectorHooks.enabled; - - break; - case sdk.keys.NumpadDash: - if (ItemHooks.pickitEnabled) { - ItemHooks.pickitEnabled = false; - } else { - ItemHooks.pickitEnabled = true; - ItemHooks.flush(); + event: function (keycode) { + if ([sdk.keys.Shift, sdk.keys.Alt].some(k => k === keycode)) { + return; + } + + ActionHooks.action = keycode; + }, + + getOnScreenLocation: function () { + let possibleLocs = [sdk.uiflags.TradePrompt, sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Shop]; + + for (let i = 0; i < possibleLocs.length; i++) { + if (getUIFlag(possibleLocs[i])) { + return possibleLocs.indexOf(possibleLocs[i]); + } + } + + return -1; + }, + + checkAction: function () { + let hook; + let unit, screenLoc; + let obj = { type: false, dest: false, action: false }; + let qolObj = { type: "qol", dest: false, action: false }; + + if (this.action) { + try { + // quick ones first - ends checkAction if one of these was true + if ([sdk.keys.Seven, sdk.keys.Eight, sdk.keys.Nine, sdk.keys.NumpadDash].includes(this.action)) { + if (this.blockKeyEvent()) return; + switch (this.action) { + case sdk.keys.Seven: + if (TextHooks.displaySettings) { + TextHooks.getHook("itemStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 7ÿc0: " + (ItemHooks.enabled ? "Enable" : "Disable") + " Item Filter"; + } + ItemHooks.enabled = !ItemHooks.enabled; + + break; + case sdk.keys.Eight: + if (TextHooks.displaySettings) { + TextHooks.getHook("monsterStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 8ÿc0: " + (MonsterHooks.enabled ? "Enable" : "Disable") + " Monsters"; + } + MonsterHooks.enabled = !MonsterHooks.enabled; + + break; + case sdk.keys.Nine: + if (TextHooks.displaySettings) { + TextHooks.getHook("vectorStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 9ÿc0: " + (VectorHooks.enabled ? "Enable" : "Disable") + " Vectors"; + } + VectorHooks.enabled = !VectorHooks.enabled; + + break; + case sdk.keys.NumpadDash: + if (ItemHooks.pickitEnabled) { + ItemHooks.pickitEnabled = false; + } else { + ItemHooks.pickitEnabled = true; + ItemHooks.flush(); - if (!Hooks.saidMessage) { - showConsole(); - print("ÿc - hook = TextHooks.getHook("Next Act", TextHooks.qolHooks); - - break; - case sdk.keys.Ctrl: - unit = Game.getSelectedUnit(); - - if (!!unit) { - screenLoc = this.getOnScreenLocation(); - - switch (screenLoc) { - case 0: // Trade screen - case 1: // Stash - case 2: // Cube - qolObj.action = this.ctrlObj[screenLoc][unit.location]; - - break; - case 3: // Shop - qolObj.action = "sellItem"; - - break; - default: - break; - } - } - - break; - case sdk.keys.Five: - if (!me.inTown) { - me.getTpTool() && (qolObj.action = "makePortal"); - } else if (me.inTown) { - if (!getUIFlag(sdk.uiflags.Stash) && !getUIFlag(sdk.uiflags.TradePrompt) && !getUIFlag(sdk.uiflags.Inventory)) { - qolObj.action = "heal"; - } - } - - break; - case sdk.keys.Six: - if (!me.inTown) { - me.getTpTool() && (qolObj.action = "takePortal"); - } else if (me.inTown) { - if (!getUIFlag(sdk.uiflags.Stash) && !getUIFlag(sdk.uiflags.TradePrompt) && !getUIFlag(sdk.uiflags.Inventory)) { - qolObj.action = "openStash"; - } - } - - break; - case sdk.keys.Insert: - if (me.inTown) { - break; - } - - qolObj.action = "clear"; - - break; - } - } - - if (hook) { - Object.assign(obj, hook); - Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(obj)); - } else if (qolObj.action) { - Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(qolObj)); - } - } catch (e) { - console.error(e); - } finally { - ActionHooks.action = null; - } - } - }, - - check: function () { - if (!this.enabled) return; - - this.checkAction(); - - if (me.area !== this.currArea) { - this.flush(); - - while (!me.area || !me.gameReady) { - delay(150); - } - - this.add(me.area); - TextHooks.update(this.hooks.length); - ActionHooks.currArea = me.area; - } - }, - - yHookLoc: function () { - return 545 - (this.hooks.length * 10) + Hooks.resfix.y; - }, - - newHook: function (name = "", type = "", dest = null) { - let hookTxt = (() => { - switch (name) { - case "Next Area": - return "Num 0: "; - case "Previous Area": - return "ÿc1Num 1: "; - case "Side Area": - return "ÿc3Num 4: "; - case "POI2": - return "ÿc + hook = TextHooks.getHook("Next Act", TextHooks.qolHooks); + + break; + case sdk.keys.Ctrl: + unit = Game.getSelectedUnit(); + + if (!!unit) { + screenLoc = this.getOnScreenLocation(); + + switch (screenLoc) { + case 0: // Trade screen + case 1: // Stash + case 2: // Cube + qolObj.action = this.ctrlObj[screenLoc][unit.location]; + + break; + case 3: // Shop + qolObj.action = "sellItem"; + + break; + default: + break; + } + } + + break; + case sdk.keys.Five: + if (!me.inTown) { + me.getTpTool() && (qolObj.action = "makePortal"); + } else if (me.inTown) { + if (!getUIFlag(sdk.uiflags.Stash) && !getUIFlag(sdk.uiflags.TradePrompt) && !getUIFlag(sdk.uiflags.Inventory)) { + qolObj.action = "heal"; + } + } + + break; + case sdk.keys.Six: + if (!me.inTown) { + me.getTpTool() && (qolObj.action = "takePortal"); + } else if (me.inTown) { + if (!getUIFlag(sdk.uiflags.Stash) && !getUIFlag(sdk.uiflags.TradePrompt) && !getUIFlag(sdk.uiflags.Inventory)) { + qolObj.action = "openStash"; + } + } + + break; + case sdk.keys.Insert: + if (me.inTown) { + break; + } + + qolObj.action = "clear"; + + break; + } + } + + if (hook) { + Object.assign(obj, hook); + Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(obj)); + } else if (qolObj.action) { + Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(qolObj)); + } + } catch (e) { + console.error(e); + } finally { + ActionHooks.action = null; + } + } + }, + + check: function () { + if (!this.enabled) return; + + this.checkAction(); + + if (me.area !== this.currArea) { + this.flush(); + + while (!me.area || !me.gameReady) { + delay(150); + } + + this.add(me.area); + TextHooks.update(this.hooks.length); + ActionHooks.currArea = me.area; + } + }, + + yHookLoc: function () { + return 545 - (this.hooks.length * 10) + Hooks.resfix.y; + }, + + newHook: function (name = "", type = "", dest = null) { + let hookTxt = (() => { + switch (name) { + case "Next Area": + return "Num 0: "; + case "Previous Area": + return "ÿc1Num 1: "; + case "Side Area": + return "ÿc3Num 4: "; + case "POI2": + return "ÿc ex.target !== correctTomb) - .sort(function(a, b) { - return a.target - b.target; - }).reverse(); + add: function (area) { + let i, exits, wp, poi, nextCheck, infSeal, seisSeal, vizSeal, bossX; + let nextAreas = []; + + // Specific area override + nextAreas[sdk.areas.TamoeHighland] = sdk.areas.MonasteryGate; + nextAreas[sdk.areas.SpiderForest] = sdk.areas.FlayerJungle; + nextAreas[sdk.areas.GreatMarsh] = sdk.areas.FlayerJungle; + nextAreas[sdk.areas.CrystalizedPassage] = sdk.areas.GlacialTrail; + nextAreas[sdk.areas.GlacialTrail] = sdk.areas.FrozenTundra; + nextAreas[sdk.areas.AncientsWay] = sdk.areas.ArreatSummit; + me.inArea(sdk.areas.CanyonofMagic) && (nextAreas[sdk.areas.CanyonofMagic] = getRoom().correcttomb); + + switch (me.area) { + case sdk.areas.Tristram: + this.hooks.push({ + name: "POI2", + type: "unit", + action: { do: "openChest", id: sdk.quest.chest.Wirt }, + dest: { x: 25048, y: 5177 }, + hook: new Text("ÿc ex.target !== correctTomb) + .sort(function(a, b) { + return a.target - b.target; + }).reverse(); - let curr; - for (let i = 8; i > 4; i--) { - curr = currExits.shift(); - this.hooks.push({ - name: "POI" + (i - 3), - type: "area", - dest: curr.target, - hook: new Text("ÿc [sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain, sdk.areas.UberTristram].includes(portal.objtype))) { - TextHooks.displaySettings = false; - this.frame.push({ - name: "portalbox", - hook: new Box (Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0x0, 1, 0) - }); - - this.frame.push({ - name: "portalframe", - hook: new Frame(Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0) - }); - - Pather.getPortal(sdk.areas.MatronsDen) && this.portals.push({ - name: "Matron's Den", - type: "portal", - dest: sdk.areas.MatronsDen, - hook: new Text("ÿc1Num 5: Matron's Den", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y) - }); - - Pather.getPortal(sdk.areas.ForgottenSands) && this.portals.push({ - name: "Sands", - type: "portal", - dest: sdk.areas.ForgottenSands, - hook: new Text("ÿc1Num 6: Forgotten Sands", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 15) - }); - - Pather.getPortal(sdk.areas.FurnaceofPain) && this.portals.push({ - name: "Furnace", - type: "portal", - dest: sdk.areas.FurnaceofPain, - hook: new Text("ÿc1Num 7: Furnace of Pain", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 30) - }); - - Pather.getPortal(sdk.areas.UberTristram) && this.portals.push({ - name: "Uber Tristam", - type: "portal", - dest: sdk.areas.UberTristram, - hook: new Text("ÿc1Num 8: Uber Tristam", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 45) - }); - } - - let entrance = {x: 0, y: 0}; - - switch (me.area) { - case sdk.areas.Tristram: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.StonyField)); - - break; - case sdk.areas.MooMooFarm: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.RogueEncampment)); - - break; - case sdk.areas.CanyonofMagic: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.ArcaneSanctuary)); - - break; - case sdk.areas.ArcaneSanctuary: - this.hooks.push(this.newHook("Previous Area", "area", sdk.areas.PalaceCellarLvl3)); - this.hooks.push(this.newHook("Next Area", "area", sdk.areas.CanyonofMagic)); - - break; - case sdk.areas.NihlathaksTemple: - this.hooks.push({ - name: "Previous Area", - type: "unit", - action: {do: "usePortal", id: sdk.areas.Harrogath}, - dest: {x: 10071, y: 13305}, - hook: new Text("ÿc1Num 1: " + getAreaName(sdk.areas.Harrogath), Hooks.dashBoard.x + 5, this.yHookLoc()) - }); - - break; - case sdk.areas.Abaddon: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.FrigidHighlands)); - - break; - case sdk.areas.PitofAcheron: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.ArreatPlateau)); - - break; - case sdk.areas.InfernalPit: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.FrozenTundra)); - - break; - case sdk.areas.ForgottenSands: - me.inArea(sdk.areas.ForgottenSands) && (entrance = {x: 20193, y: 8693}); - // eslint-disable-next-line no-fallthrough - case sdk.areas.MatronsDen: - case sdk.areas.FurnaceofPain: - bossX = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - bossX && (entrance = this.areaInfo[me.area][bossX.x]); - // eslint-disable-next-line no-fallthrough - case sdk.areas.UberTristram: - me.inArea(sdk.areas.UberTristram) && (entrance = {x: 25105, y: 5140}); - - this.hooks.push({ - name: "Previous Area", - type: "unit", - action: {do: "usePortal", id: sdk.areas.Harrogath}, - dest: entrance, - hook: new Text("ÿc1Num 1: " + getAreaName(sdk.areas.Harrogath), Hooks.dashBoard.x + 5, this.yHookLoc()) - }); - - break; - } - - exits = getArea(area).exits; - - if (exits) { - for (i = 0; i < exits.length; i += 1) { - if (exits[i].target === this.prevAreas[me.area]) { - this.hooks.push(this.newHook("Previous Area", "area", this.prevAreas[me.area])); - - break; - } - } - - // Check nextAreas first - for (i = 0; i < exits.length; i += 1) { - if (exits[i].target === nextAreas[me.area]) { - this.hooks.push(this.newHook("Next Area", "area", nextAreas[me.area])); - nextCheck = true; - - break; - } - } - - // In case the area isn't in nextAreas array, use this.prevAreas array - if (!nextCheck) { - for (i = 0; i < exits.length; i += 1) { - if (exits[i].target === this.prevAreas.indexOf(me.area)) { - this.hooks.push(this.newHook("Next Area", "area", this.prevAreas.indexOf(me.area))); - - break; - } - } - } - } - - if (poi && poi.name === "Orifice") { - this.hooks.push(this.newHook("Next Area", "area", sdk.areas.DurielsLair)); - } - - if (me.inArea(sdk.areas.DuranceofHateLvl3)) { - this.hooks.push(this.newHook("Next Area", "area", sdk.areas.PandemoniumFortress)); - } - - if (me.inArea(sdk.areas.ThroneofDestruction)) { - this.hooks.push(this.newHook("Next Area", "area", sdk.areas.WorldstoneChamber)); - } - }, - - /** + let curr; + for (let i = 8; i > 4; i--) { + curr = currExits.shift(); + this.hooks.push({ + name: "POI" + (i - 3), + type: "area", + dest: curr.target, + hook: new Text("ÿc [sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain, sdk.areas.UberTristram].includes(portal.objtype))) { + TextHooks.displaySettings = false; + this.frame.push({ + name: "portalbox", + hook: new Box (Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0x0, 1, 0) + }); + + this.frame.push({ + name: "portalframe", + hook: new Frame(Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0) + }); + + Pather.getPortal(sdk.areas.MatronsDen) && this.portals.push({ + name: "Matron's Den", + type: "portal", + dest: sdk.areas.MatronsDen, + hook: new Text("ÿc1Num 5: Matron's Den", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y) + }); + + Pather.getPortal(sdk.areas.ForgottenSands) && this.portals.push({ + name: "Sands", + type: "portal", + dest: sdk.areas.ForgottenSands, + hook: new Text("ÿc1Num 6: Forgotten Sands", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 15) + }); + + Pather.getPortal(sdk.areas.FurnaceofPain) && this.portals.push({ + name: "Furnace", + type: "portal", + dest: sdk.areas.FurnaceofPain, + hook: new Text("ÿc1Num 7: Furnace of Pain", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 30) + }); + + Pather.getPortal(sdk.areas.UberTristram) && this.portals.push({ + name: "Uber Tristam", + type: "portal", + dest: sdk.areas.UberTristram, + hook: new Text("ÿc1Num 8: Uber Tristam", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 45) + }); + } + + let entrance = { x: 0, y: 0 }; + + switch (me.area) { + case sdk.areas.Tristram: + this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.StonyField)); + + break; + case sdk.areas.MooMooFarm: + this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.RogueEncampment)); + + break; + case sdk.areas.CanyonofMagic: + this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.ArcaneSanctuary)); + + break; + case sdk.areas.ArcaneSanctuary: + this.hooks.push(this.newHook("Previous Area", "area", sdk.areas.PalaceCellarLvl3)); + this.hooks.push(this.newHook("Next Area", "area", sdk.areas.CanyonofMagic)); + + break; + case sdk.areas.NihlathaksTemple: + this.hooks.push({ + name: "Previous Area", + type: "unit", + action: { do: "usePortal", id: sdk.areas.Harrogath }, + dest: { x: 10071, y: 13305 }, + hook: new Text("ÿc1Num 1: " + getAreaName(sdk.areas.Harrogath), Hooks.dashBoard.x + 5, this.yHookLoc()) + }); + + break; + case sdk.areas.Abaddon: + this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.FrigidHighlands)); + + break; + case sdk.areas.PitofAcheron: + this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.ArreatPlateau)); + + break; + case sdk.areas.InfernalPit: + this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.FrozenTundra)); + + break; + case sdk.areas.ForgottenSands: + me.inArea(sdk.areas.ForgottenSands) && (entrance = { x: 20193, y: 8693 }); + // eslint-disable-next-line no-fallthrough + case sdk.areas.MatronsDen: + case sdk.areas.FurnaceofPain: + bossX = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); + bossX && (entrance = this.areaInfo[me.area][bossX.x]); + // eslint-disable-next-line no-fallthrough + case sdk.areas.UberTristram: + me.inArea(sdk.areas.UberTristram) && (entrance = { x: 25105, y: 5140 }); + + this.hooks.push({ + name: "Previous Area", + type: "unit", + action: { do: "usePortal", id: sdk.areas.Harrogath }, + dest: entrance, + hook: new Text("ÿc1Num 1: " + getAreaName(sdk.areas.Harrogath), Hooks.dashBoard.x + 5, this.yHookLoc()) + }); + + break; + } + + exits = getArea(area).exits; + + if (exits) { + for (i = 0; i < exits.length; i += 1) { + if (exits[i].target === this.prevAreas[me.area]) { + this.hooks.push(this.newHook("Previous Area", "area", this.prevAreas[me.area])); + + break; + } + } + + // Check nextAreas first + for (i = 0; i < exits.length; i += 1) { + if (exits[i].target === nextAreas[me.area]) { + this.hooks.push(this.newHook("Next Area", "area", nextAreas[me.area])); + nextCheck = true; + + break; + } + } + + // In case the area isn't in nextAreas array, use this.prevAreas array + if (!nextCheck) { + for (i = 0; i < exits.length; i += 1) { + if (exits[i].target === this.prevAreas.indexOf(me.area)) { + this.hooks.push(this.newHook("Next Area", "area", this.prevAreas.indexOf(me.area))); + + break; + } + } + } + } + + if (poi && poi.name === "Orifice") { + this.hooks.push(this.newHook("Next Area", "area", sdk.areas.DurielsLair)); + } + + if (me.inArea(sdk.areas.DuranceofHateLvl3)) { + this.hooks.push(this.newHook("Next Area", "area", sdk.areas.PandemoniumFortress)); + } + + if (me.inArea(sdk.areas.ThroneofDestruction)) { + this.hooks.push(this.newHook("Next Area", "area", sdk.areas.WorldstoneChamber)); + } + }, + + /** * @param {number} seal * @returns {{ x: number, y: number, area: number }} */ - getDiabloSeals: function (seal) { - try { - let unit = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); - return unit.realCoords(); - } catch (e) { - return false; - } - }, - - getHook: function (name) { - for (let i = 0; i < this.hooks.length; i += 1) { - if (this.hooks[i].name === name) { - return this.hooks[i]; - } - } - - return false; - }, - - getPortalHook: function (name) { - for (let i = 0; i < this.portals.length; i += 1) { - if (this.portals[i].name === name) { - return this.portals[i]; - } - } - - return false; - }, - - flush: function () { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); - } - - while (this.portals.length) { - this.portals.shift().hook.remove(); - } - - while (this.frame.length) { - this.frame.shift().hook.remove(); - } - - this.currArea = 0; - } + getDiabloSeals: function (seal) { + try { + let unit = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); + return unit.realCoords(); + } catch (e) { + return false; + } + }, + + getHook: function (name) { + for (let i = 0; i < this.hooks.length; i += 1) { + if (this.hooks[i].name === name) { + return this.hooks[i]; + } + } + + return false; + }, + + getPortalHook: function (name) { + for (let i = 0; i < this.portals.length; i += 1) { + if (this.portals[i].name === name) { + return this.portals[i]; + } + } + + return false; + }, + + flush: function () { + while (this.hooks.length) { + this.hooks.shift().hook.remove(); + } + + while (this.portals.length) { + this.portals.shift().hook.remove(); + } + + while (this.frame.length) { + this.frame.shift().hook.remove(); + } + + this.currArea = 0; + } }; ActionHooks.areaInfo[sdk.areas.MatronsDen] = { - 11: {x: 20023, y: 7643}, - 20: {x: 20303, y: 7803}, - 21: {x: 20263, y: 7683}, + 11: { x: 20023, y: 7643 }, + 20: { x: 20303, y: 7803 }, + 21: { x: 20263, y: 7683 }, }; ActionHooks.areaInfo[sdk.areas.FurnaceofPain] = { - 14: {x: 20138, y: 14873}, - 15: {x: 20138, y: 14563}, + 14: { x: 20138, y: 14873 }, + 15: { x: 20138, y: 14563 }, }; diff --git a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js b/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js index 85a20ada0..7285f1c19 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js +++ b/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename HelpMenu.js * @author theBGuy @@ -6,157 +7,157 @@ */ const HelpMenu = new function () { - this.hooks = []; - this.box = []; - this.cleared = true; - this.helpBoxX = 715 + (me.screensize ? 0 : -160); - this.helpBoxY = 88 + 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)); - this.helpBoxTextX = 647 + (me.screensize ? 0 : -160); - this.helpBoxTextY = 88 + 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)) + 15; - this.action = []; - this.actionY = -1; - this.tick = 0; - this.chatCommands = { - "me": "Displays Character level, Area, and x/y coordinates", - "pick": "Pick items from the ground to inventory", - "hide": "Hide this menu", - "make": "create config file with current characters name", - "stash": "Stash items/gold from inventory", - "filltps": "Fill tp tome", - "cowportal": "Make cow portal as long as bot already has leg", - "uberportal": "Make uber portal(s) as long as bot already has key", - "ubertrist": "Make uber tristam portal as long as bot already has organs", - "useraddon": "Toggles useraddon mode", - "Ctrl": "Hover over an item then press Ctrl to move that item from one area to the next. In example: Stash to Inventory, Cube to Inventory, Inventory to TradeScreen, or Inventory to Shop (sellItem)", - "drop": { - "invo": "Drop all items in the inventory", - "stash": "Drop all items in the stash excluding the cube" - }, - "stack": { - "antidote": "Buy and Stack 10 antidote potions for 5 minutes of boosted poison resistance", - "thawing": "Buy and Stack 10 thawing potions for 5 minutes of boosted cold resistance", - "stamina": "Buy and Stack 10 stamina potions for 5 minutes of boosted stamina", - }, - "Num": { - "9:": "Stops current pathing action", - }, - }; - - this.hookHandler = function (click, x, y) { - // Left click - if (click === 0) { - // give a small timeout between clicks - if (getTickCount() - HelpMenu.tick > 1000) { - HelpMenu.action.push([x, y]); - } - // Block click - return true; - } - - return false; - }; - - this.addHook = function (text) { - this.hooks.push(new Text("ÿc4." + text, this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); - }; - - this.showMenu = function () { - this.cleared = false; - - let chatCommands = [ - "me", - "pick", - "hide", - "make", - "stash", - "filltps", - "cowportal", - "uberportal", - "ubertrist", - "useraddon", - "drop invo", - "drop stash", - "stack antidote", - "stack thawing", - "stack stamina", - ]; - - this.hooks.push(new Text("ÿc2Chat Commands:", this.helpBoxTextX, this.helpBoxTextY, 0, 0, 0)); - - for (let i = 0; i < chatCommands.length; i++) { - this.addHook(chatCommands[i]); - } - - this.hooks.push(new Text("ÿc2Key Commands:", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0)); - this.hooks.push(new Text("ÿc4 Ctrl : ÿc0Move Item", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); - this.hooks.push(new Text("ÿc4 Pause : ÿc1Pause Map", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Delete: ÿc1Quick Exit", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 End : ÿc1Stop Profile", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num 9: ÿc1Stop Action", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); - this.hooks.push(new Text("ÿc4 Num / : ÿc1Reload", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num + : ÿc0Show Stats", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num * : ÿc0Precast", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num . : ÿc0Log Character", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - - this.box.push(new Box(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 0x0, 1, 2)); - this.box.push(new Frame(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 2)); - this.box[this.box.length - 2].zorder = 0; - }; - - this.hideMenu = function () { - this.cleared = true; - - while (this.hooks.length > 0) { - this.hooks.shift().remove(); - } - - while (this.box.length > 0) { - this.box.shift().remove(); - } - - return; - }; - - this.sortHooks = function (h1, h2) { - return Math.abs(h1.y - HelpMenu.actionY) - Math.abs(h2.y - HelpMenu.actionY); - }; + this.hooks = []; + this.box = []; + this.cleared = true; + this.helpBoxX = 715 + (me.screensize ? 0 : -160); + this.helpBoxY = 88 + 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)); + this.helpBoxTextX = 647 + (me.screensize ? 0 : -160); + this.helpBoxTextY = 88 + 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)) + 15; + this.action = []; + this.actionY = -1; + this.tick = 0; + this.chatCommands = { + "me": "Displays Character level, Area, and x/y coordinates", + "pick": "Pick items from the ground to inventory", + "hide": "Hide this menu", + "make": "create config file with current characters name", + "stash": "Stash items/gold from inventory", + "filltps": "Fill tp tome", + "cowportal": "Make cow portal as long as bot already has leg", + "uberportal": "Make uber portal(s) as long as bot already has key", + "ubertrist": "Make uber tristam portal as long as bot already has organs", + "useraddon": "Toggles useraddon mode", + "Ctrl": "Hover over an item then press Ctrl to move that item from one area to the next. In example: Stash to Inventory, Cube to Inventory, Inventory to TradeScreen, or Inventory to Shop (sellItem)", + "drop": { + "invo": "Drop all items in the inventory", + "stash": "Drop all items in the stash excluding the cube" + }, + "stack": { + "antidote": "Buy and Stack 10 antidote potions for 5 minutes of boosted poison resistance", + "thawing": "Buy and Stack 10 thawing potions for 5 minutes of boosted cold resistance", + "stamina": "Buy and Stack 10 stamina potions for 5 minutes of boosted stamina", + }, + "Num": { + "9:": "Stops current pathing action", + }, + }; + + this.hookHandler = function (click, x, y) { + // Left click + if (click === 0) { + // give a small timeout between clicks + if (getTickCount() - HelpMenu.tick > 1000) { + HelpMenu.action.push([x, y]); + } + // Block click + return true; + } + + return false; + }; + + this.addHook = function (text) { + this.hooks.push(new Text("ÿc4." + text, this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); + }; + + this.showMenu = function () { + this.cleared = false; + + let chatCommands = [ + "me", + "pick", + "hide", + "make", + "stash", + "filltps", + "cowportal", + "uberportal", + "ubertrist", + "useraddon", + "drop invo", + "drop stash", + "stack antidote", + "stack thawing", + "stack stamina", + ]; + + this.hooks.push(new Text("ÿc2Chat Commands:", this.helpBoxTextX, this.helpBoxTextY, 0, 0, 0)); + + for (let i = 0; i < chatCommands.length; i++) { + this.addHook(chatCommands[i]); + } + + this.hooks.push(new Text("ÿc2Key Commands:", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0)); + this.hooks.push(new Text("ÿc4 Ctrl : ÿc0Move Item", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); + this.hooks.push(new Text("ÿc4 Pause : ÿc1Pause Map", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Delete: ÿc1Quick Exit", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 End : ÿc1Stop Profile", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num 9: ÿc1Stop Action", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); + this.hooks.push(new Text("ÿc4 Num / : ÿc1Reload", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num + : ÿc0Show Stats", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num * : ÿc0Precast", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num . : ÿc0Log Character", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + + this.box.push(new Box(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 0x0, 1, 2)); + this.box.push(new Frame(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 2)); + this.box[this.box.length - 2].zorder = 0; + }; + + this.hideMenu = function () { + this.cleared = true; + + while (this.hooks.length > 0) { + this.hooks.shift().remove(); + } + + while (this.box.length > 0) { + this.box.shift().remove(); + } + + return; + }; + + this.sortHooks = function (h1, h2) { + return Math.abs(h1.y - HelpMenu.actionY) - Math.abs(h2.y - HelpMenu.actionY); + }; }; let Worker = require("../../modules/Worker"); Worker.runInBackground.helpAction = function () { - while (HelpMenu.action.length > 0) { - HelpMenu.tick = getTickCount(); - HelpMenu.actionY = HelpMenu.action.shift()[1]; - // Sort hooks - HelpMenu.hooks.sort(HelpMenu.sortHooks); - - let cmd = HelpMenu.hooks[0].text.split(" ")[0].split(".")[1]; - let msgList = HelpMenu.hooks[0].text.split(" "); - - if (!HelpMenu.hooks[0].text.includes(".")) { - cmd = HelpMenu.hooks[0].text.split(" ")[1]; - } - - try { - let str = ""; - - if (msgList.length === 2 && typeof HelpMenu.chatCommands[cmd] === "object") { - str = HelpMenu.chatCommands[cmd][msgList[1]]; - } else if (msgList.length > 2 && typeof HelpMenu.chatCommands[cmd] === "object") { - str = HelpMenu.chatCommands[cmd][msgList[2]]; - } else { - str = HelpMenu.chatCommands[cmd]; - } - - !!str && me.overhead(str); - } catch (e) { - print(e); - me.overhead(cmd); - } - - delay(150); - } - - return true; + while (HelpMenu.action.length > 0) { + HelpMenu.tick = getTickCount(); + HelpMenu.actionY = HelpMenu.action.shift()[1]; + // Sort hooks + HelpMenu.hooks.sort(HelpMenu.sortHooks); + + let cmd = HelpMenu.hooks[0].text.split(" ")[0].split(".")[1]; + let msgList = HelpMenu.hooks[0].text.split(" "); + + if (!HelpMenu.hooks[0].text.includes(".")) { + cmd = HelpMenu.hooks[0].text.split(" ")[1]; + } + + try { + let str = ""; + + if (msgList.length === 2 && typeof HelpMenu.chatCommands[cmd] === "object") { + str = HelpMenu.chatCommands[cmd][msgList[1]]; + } else if (msgList.length > 2 && typeof HelpMenu.chatCommands[cmd] === "object") { + str = HelpMenu.chatCommands[cmd][msgList[2]]; + } else { + str = HelpMenu.chatCommands[cmd]; + } + + !!str && me.overhead(str); + } catch (e) { + print(e); + me.overhead(cmd); + } + + delay(150); + } + + return true; }; diff --git a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js index ce5b3b46a..fdcf9a2e1 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename ItemHooks.js * @author theBGuy @@ -7,274 +8,274 @@ // todo - clean up all the map stuff const ItemHooks = { - enabled: true, - pickitEnabled: false, - modifier: 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename)), - hooks: [], - ignoreItemTypes: [ - sdk.items.type.Gold, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.Book, sdk.items.type.Gem, sdk.items.type.Scroll, - sdk.items.type.MissilePotion, sdk.items.type.Key, sdk.items.type.Boots, sdk.items.type.Gloves, sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, - 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.ChippedGem, sdk.items.type.FlawedGem, sdk.items.type.StandardGem, sdk.items.type.FlawlessGem, sdk.items.type.PerfectgGem, - sdk.items.type.Amethyst, sdk.items.type.Diamond, sdk.items.type.Emerald, sdk.items.type.Ruby, sdk.items.type.Sapphire, sdk.items.type.Topaz, sdk.items.type.Skull - ], - codeById: new Map(), - codeByIdAndQuality: new Map(), - itemColorCode: [], - - /** + enabled: true, + pickitEnabled: false, + modifier: 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename)), + hooks: [], + ignoreItemTypes: [ + sdk.items.type.Gold, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.Book, sdk.items.type.Gem, sdk.items.type.Scroll, + sdk.items.type.MissilePotion, sdk.items.type.Key, sdk.items.type.Boots, sdk.items.type.Gloves, sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, + 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.ChippedGem, sdk.items.type.FlawedGem, sdk.items.type.StandardGem, sdk.items.type.FlawlessGem, sdk.items.type.PerfectgGem, + sdk.items.type.Amethyst, sdk.items.type.Diamond, sdk.items.type.Emerald, sdk.items.type.Ruby, sdk.items.type.Sapphire, sdk.items.type.Topaz, sdk.items.type.Skull + ], + codeById: new Map(), + codeByIdAndQuality: new Map(), + itemColorCode: [], + + /** * @param {number} id - classID of item * @param {string} setName * @param {string} uniqueName */ - addToCodeByClassIdAndQuality: function (id, setName = "", uniqueName = "") { - if (!id || (!setName && !uniqueName)) return; - // create our map structure - if (!this.codeByIdAndQuality.get(id)) { - let temp = []; - setName && temp.push([sdk.items.quality.Set, setName]); - uniqueName && temp.push([sdk.items.quality.Unique, uniqueName]); - this.codeByIdAndQuality.set(id, new Map(temp)); - } - }, - - check: function () { - if (!this.enabled) { - this.flush(); - - return; - } - - for (let i = 0; i < this.hooks.length; i++) { - if (!copyUnit(this.hooks[i].item).x) { - for (let j = 0; j < this.hooks[i].hook.length; j++) { - this.hooks[i].hook[j].remove(); - } - - this.hooks[i].name[0] && this.hooks[i].name[0].remove(); - this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); - this.hooks.splice(i, 1); - i -= 1; - this.flush(); - } - } - - let item = Game.getItem(); - - if (item) { - try { - do { - if (item.area === ActionHooks.currArea && item.onGroundOrDropping + addToCodeByClassIdAndQuality: function (id, setName = "", uniqueName = "") { + if (!id || (!setName && !uniqueName)) return; + // create our map structure + if (!this.codeByIdAndQuality.get(id)) { + let temp = []; + setName && temp.push([sdk.items.quality.Set, setName]); + uniqueName && temp.push([sdk.items.quality.Unique, uniqueName]); + this.codeByIdAndQuality.set(id, new Map(temp)); + } + }, + + check: function () { + if (!this.enabled) { + this.flush(); + + return; + } + + for (let i = 0; i < this.hooks.length; i++) { + if (!copyUnit(this.hooks[i].item).x) { + for (let j = 0; j < this.hooks[i].hook.length; j++) { + this.hooks[i].hook[j].remove(); + } + + this.hooks[i].name[0] && this.hooks[i].name[0].remove(); + this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); + this.hooks.splice(i, 1); + i -= 1; + this.flush(); + } + } + + let item = Game.getItem(); + + if (item) { + try { + do { + if (item.area === ActionHooks.currArea && item.onGroundOrDropping && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && !this.ignoreItemTypes.includes(item.itemType)))) { - if (this.pickitEnabled) { - if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(Pickit.checkItem(item).result) === -1) { - !this.getHook(item) && this.add(item); - } - } else { - !this.getHook(item) && this.add(item); - } - - this.getHook(item) && this.update(); - } else { - this.remove(item); - } - } while (item.getNext()); - } catch (e) { - console.error(e); - this.flush(); - } - } - }, - - update: function () { - for (let i = 0; i < this.hooks.length; i++) { - this.hooks[i].vector[0].x = me.x; - this.hooks[i].vector[0].y = me.y; - } - }, - - /** + if (this.pickitEnabled) { + if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(Pickit.checkItem(item).result) === -1) { + !this.getHook(item) && this.add(item); + } + } else { + !this.getHook(item) && this.add(item); + } + + this.getHook(item) && this.update(); + } else { + this.remove(item); + } + } while (item.getNext()); + } catch (e) { + console.error(e); + this.flush(); + } + } + }, + + update: function () { + for (let i = 0; i < this.hooks.length; i++) { + this.hooks[i].vector[0].x = me.x; + this.hooks[i].vector[0].y = me.y; + } + }, + + /** * @param {ItemUnit} item * @returns {string} */ - getName: function (item) { - let abbr = item.name.split(" "); - let abbrName = ""; + getName: function (item) { + let abbr = item.name.split(" "); + let abbrName = ""; - if (abbr[1]) { - abbrName += abbr[0] + "-"; + if (abbr[1]) { + abbrName += abbr[0] + "-"; - for (let i = 1; i < abbr.length; i++) { - abbrName += abbr[i].substring(0, 1); - } - } + for (let i = 1; i < abbr.length; i++) { + abbrName += abbr[i].substring(0, 1); + } + } - return !!abbrName ? abbrName : item.name; - }, + return !!abbrName ? abbrName : item.name; + }, - /** + /** * @description Create a new hook for a item with custom color and code based on type/quality/classid * @param {ItemUnit} item * @todo maybe make class wrappers for hooks and turn the hook array into a map? */ - newHook: function (item) { - let color = 0, code = "", arr = [], name = [], vector = []; - let eth = (item.ethereal ? "Eth: " : ""); - - switch (item.quality) { - case sdk.items.quality.Normal: - case sdk.items.quality.Superior: - switch (item.itemType) { - case sdk.items.type.Quest: - color = 0x9A; - code += (this.codeById.get(item.classid) || "ÿc8" + item.fname); - - break; - case sdk.items.type.Rune: - if (item.classid >= sdk.items.runes.Vex) { - [color, code] = [0x9B, "ÿc;" + item.fname]; - } else if (item.classid >= sdk.items.runes.Lum) { - [color, code] = [0x9A, "ÿc8" + item.fname]; - } else { - [color, code] = [0xA1, item.fname]; - } - - break; - default: - if (item.name && item.sockets !== 1) { - color = 0x20; - - if (item.runeword) { - code = item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""); - } else { - code = "ÿc0" + (item.sockets > 0 ? "[" + item.sockets + "]" : ""); - code += this.getName(item); - item.itemType === sdk.items.type.AuricShields && (code += "[R: " + item.getStat(sdk.stats.FireResist) + "]"); - code += "(" + item.ilvl + ")"; - } - } - - break; - } - - break; - case sdk.items.quality.Set: - case sdk.items.quality.Unique: - ({color, code} = this.itemColorCode[item.quality]); - - if (this.codeById.has(item.classid)) { - code += this.codeById.get(item.classid); - } - - switch (item.classid) { - case sdk.items.Ring: - case sdk.items.Amulet: - code += item.name + "(" + item.ilvl + ")"; + newHook: function (item) { + let color = 0, code = "", arr = [], name = [], vector = []; + let eth = (item.ethereal ? "Eth: " : ""); + + switch (item.quality) { + case sdk.items.quality.Normal: + case sdk.items.quality.Superior: + switch (item.itemType) { + case sdk.items.type.Quest: + color = 0x9A; + code += (this.codeById.get(item.classid) || "ÿc8" + item.fname); + + break; + case sdk.items.type.Rune: + if (item.classid >= sdk.items.runes.Vex) { + [color, code] = [0x9B, "ÿc;" + item.fname]; + } else if (item.classid >= sdk.items.runes.Lum) { + [color, code] = [0x9A, "ÿc8" + item.fname]; + } else { + [color, code] = [0xA1, item.fname]; + } + + break; + default: + if (item.name && item.sockets !== 1) { + color = 0x20; + + if (item.runeword) { + code = item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""); + } else { + code = "ÿc0" + (item.sockets > 0 ? "[" + item.sockets + "]" : ""); + code += this.getName(item); + item.itemType === sdk.items.type.AuricShields && (code += "[R: " + item.getStat(sdk.stats.FireResist) + "]"); + code += "(" + item.ilvl + ")"; + } + } + + break; + } + + break; + case sdk.items.quality.Set: + case sdk.items.quality.Unique: + ({ color, code } = this.itemColorCode[item.quality]); + + if (this.codeById.has(item.classid)) { + code += this.codeById.get(item.classid); + } + + switch (item.classid) { + case sdk.items.Ring: + case sdk.items.Amulet: + code += item.name + "(" + item.ilvl + ")"; - break; - default: - { - let check = this.codeByIdAndQuality.get(item.classid); - code += ((check && check.get(item.quality)) || item.name); - } + break; + default: + { + let check = this.codeByIdAndQuality.get(item.classid); + code += ((check && check.get(item.quality)) || item.name); + } - break; - } - - break; - case sdk.items.quality.Magic: - case sdk.items.quality.Rare: - if (item.name) { - ({color, code} = this.itemColorCode[item.quality]); - - code += (item.sockets > 0 ? "[" + item.sockets + "]" : ""); - code += this.getName(item); - code += "(" + item.ilvl + ")"; - } + break; + } + + break; + case sdk.items.quality.Magic: + case sdk.items.quality.Rare: + if (item.name) { + ({ color, code } = this.itemColorCode[item.quality]); + + code += (item.sockets > 0 ? "[" + item.sockets + "]" : ""); + code += this.getName(item); + code += "(" + item.ilvl + ")"; + } - break; - } - - !!code && name.push(new Text(eth + code, 665 + Hooks.resfix.x, 104 + this.modifier + (this.hooks.length * 14), color, 0, 0)); - vector.push(new Line(me.x, me.y, item.x, item.y, color, true)); - arr.push(new Line(item.x - 3, item.y, item.x + 3, item.y, color, true)); - arr.push(new Line(item.x, item.y - 3, item.x, item.y + 3, color, true)); - - return { - itemLoc: arr, - itemName: name, - vector: vector, - }; - }, - - /** + break; + } + + !!code && name.push(new Text(eth + code, 665 + Hooks.resfix.x, 104 + this.modifier + (this.hooks.length * 14), color, 0, 0)); + vector.push(new Line(me.x, me.y, item.x, item.y, color, true)); + arr.push(new Line(item.x - 3, item.y, item.x + 3, item.y, color, true)); + arr.push(new Line(item.x, item.y - 3, item.x, item.y + 3, color, true)); + + return { + itemLoc: arr, + itemName: name, + vector: vector, + }; + }, + + /** * Add new item hook to our hook array * @param {ItemUnit} item */ - add: function (item) { - if (item === undefined || !item.classid) { - return; - } - - let temp = this.newHook(item); - - this.hooks.push({ - item: copyUnit(item), - area: item.area, - hook: temp.itemLoc, - name: temp.itemName, - vector: temp.vector, - }); - }, - - /** + add: function (item) { + if (item === undefined || !item.classid) { + return; + } + + let temp = this.newHook(item); + + this.hooks.push({ + item: copyUnit(item), + area: item.area, + hook: temp.itemLoc, + name: temp.itemName, + vector: temp.vector, + }); + }, + + /** * Get item hook if it exists based on item parameters gid * @param {ItemUnit} item * @returns {{ item: ItemUnit, area: number, hook: Line, name: Text, vector: Line} | false} */ - getHook: function (item) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].item.gid === item.gid) { - return this.hooks[i].hook; - } - } + getHook: function (item) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].item.gid === item.gid) { + return this.hooks[i].hook; + } + } - return false; - }, + return false; + }, - /** + /** * @param {ItemUnit} item * @returns {boolean} */ - remove: function (item) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].item.gid === item.gid) { - for (let j = 0; j < this.hooks[i].hook.length; j++) { - this.hooks[i].hook[j].remove(); - } + remove: function (item) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].item.gid === item.gid) { + for (let j = 0; j < this.hooks[i].hook.length; j++) { + this.hooks[i].hook[j].remove(); + } - this.hooks[i].name[0] && this.hooks[i].name[0].remove(); - this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); - this.hooks.splice(i, 1); - - return true; - } - } - - return false; - }, - - flush: function () { - while (this.hooks.length) { - for (let j = 0; j < this.hooks[0].hook.length; j++) { - this.hooks[0].hook[j].remove(); - } - - this.hooks[0].name[0] && this.hooks[0].name[0].remove(); - this.hooks[0].vector[0] && this.hooks[0].vector[0].remove(); - this.hooks.shift(); - } - } + this.hooks[i].name[0] && this.hooks[i].name[0].remove(); + this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); + this.hooks.splice(i, 1); + + return true; + } + } + + return false; + }, + + flush: function () { + while (this.hooks.length) { + for (let j = 0; j < this.hooks[0].hook.length; j++) { + this.hooks[0].hook[j].remove(); + } + + this.hooks[0].name[0] && this.hooks[0].name[0].remove(); + this.hooks[0].vector[0] && this.hooks[0].vector[0].remove(); + this.hooks.shift(); + } + } }; /** diff --git a/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js b/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js index 32a60fea1..195a3193e 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js @@ -7,101 +7,101 @@ */ const MonsterHooks = { - hooks: [], - enabled: true, - - check: function () { - if (!this.enabled || me.inTown) { - this.flush(); - - return; - } - - for (let i = 0; i < this.hooks.length; i += 1) { - if (!copyUnit(this.hooks[i].unit).x) { - this.hooks[i].hook.remove(); - this.hooks.splice(i, 1); - - i -= 1; - } - } - - let unit = Game.getMonster(); - - if (unit) { - do { - if (unit.attackable) { - !this.getHook(unit) ? this.add(unit) : this.updateCoords(unit); - } else { - this.remove(unit); - } - } while (unit.getNext()); - } - }, - - // credit DetectiveSquirrel from his maphack https://github.com/DetectiveSquirrel/Kolbot-MapThread/blob/9c721a72a934518cfca1d1a05211b5e03b5b624f/kolbot/threads/MapThread.js#L2353 - specTypeColor: function (unit) { - switch (unit.spectype) { - case sdk.monsters.spectype.Minion: - return 3; - case sdk.monsters.spectype.Magic: - return 9; - case sdk.monsters.spectype.Unique: - return 11; - case sdk.monsters.spectype.SuperUnique: - return 2; - default: - return 8; - } - }, - - add: function (unit) { - this.hooks.push({ - unit: copyUnit(unit), - hook: new Text((unit.spectype & 0xF ? "O" : "X"), unit.x, unit.y, this.specTypeColor(unit), 1, null, true) - }); - }, - - updateCoords: function (unit) { - let hook = this.getHook(unit); - - if (!hook) { - return false; - } - - hook.x = unit.x; - hook.y = unit.y; - - return true; - }, - - getHook: function (unit) { - for (let i = 0; i < this.hooks.length; i += 1) { - if (this.hooks[i].unit.gid === unit.gid) { - return this.hooks[i].hook; - } - } - - return false; - }, - - remove: function (unit) { - for (let i = 0; i < this.hooks.length; i += 1) { - if (this.hooks[i].unit.gid === unit.gid) { - this.hooks[i].hook.remove(); - this.hooks.splice(i, 1); - - return true; - } - } - - return false; - }, - - flush: function () { - while (this.hooks.length) { - this.hooks[0].hook.remove(); - this.hooks.shift(); - } - } + hooks: [], + enabled: true, + + check: function () { + if (!this.enabled || me.inTown) { + this.flush(); + + return; + } + + for (let i = 0; i < this.hooks.length; i += 1) { + if (!copyUnit(this.hooks[i].unit).x) { + this.hooks[i].hook.remove(); + this.hooks.splice(i, 1); + + i -= 1; + } + } + + let unit = Game.getMonster(); + + if (unit) { + do { + if (unit.attackable) { + !this.getHook(unit) ? this.add(unit) : this.updateCoords(unit); + } else { + this.remove(unit); + } + } while (unit.getNext()); + } + }, + + // credit DetectiveSquirrel from his maphack https://github.com/DetectiveSquirrel/Kolbot-MapThread/blob/9c721a72a934518cfca1d1a05211b5e03b5b624f/kolbot/threads/MapThread.js#L2353 + specTypeColor: function (unit) { + switch (unit.spectype) { + case sdk.monsters.spectype.Minion: + return 3; + case sdk.monsters.spectype.Magic: + return 9; + case sdk.monsters.spectype.Unique: + return 11; + case sdk.monsters.spectype.SuperUnique: + return 2; + default: + return 8; + } + }, + + add: function (unit) { + this.hooks.push({ + unit: copyUnit(unit), + hook: new Text((unit.spectype & 0xF ? "O" : "X"), unit.x, unit.y, this.specTypeColor(unit), 1, null, true) + }); + }, + + updateCoords: function (unit) { + let hook = this.getHook(unit); + + if (!hook) { + return false; + } + + hook.x = unit.x; + hook.y = unit.y; + + return true; + }, + + getHook: function (unit) { + for (let i = 0; i < this.hooks.length; i += 1) { + if (this.hooks[i].unit.gid === unit.gid) { + return this.hooks[i].hook; + } + } + + return false; + }, + + remove: function (unit) { + for (let i = 0; i < this.hooks.length; i += 1) { + if (this.hooks[i].unit.gid === unit.gid) { + this.hooks[i].hook.remove(); + this.hooks.splice(i, 1); + + return true; + } + } + + return false; + }, + + flush: function () { + while (this.hooks.length) { + this.hooks[0].hook.remove(); + this.hooks.shift(); + } + } }; diff --git a/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js index 4cf04742b..6876b4a6c 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js @@ -6,86 +6,86 @@ */ const ShrineHooks = { - enabled: true, - hooks: [], - shrines: new Map(), - - check: function () { - if (!this.enabled || me.inTown) { - this.flush(); - - return; - } - - for (let i = 0; i < this.hooks.length; i++) { - if (!copyUnit(this.hooks[i].shrine).objtype) { - this.hooks[i].hook[0].remove(); - this.hooks.splice(i, 1); - - i -= 1; - } - } - - let shrine = Game.getObject(); - - if (shrine) { - do { - if (this.shrines.has(shrine.objtype) && shrine.name.toLowerCase().includes("shrine")) { - if (shrine.mode === sdk.objects.mode.Inactive) { - if (!this.getHook(shrine)) { - this.add(shrine); - } - } else { - this.remove(shrine); - } - } - } while (shrine.getNext()); - } - }, - - newHook: function (shrine) { - let typeName = this.shrines.get(shrine.objtype); - return typeName ? [new Text(typeName, shrine.x, shrine.y, 4, 6, 2, true)] : []; - }, - - add: function (shrine) { - if (!shrine.objtype) return; - - this.hooks.push({ - shrine: copyUnit(shrine), - hook: this.newHook(shrine) - }); - }, - - getHook: function (shrine) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].shrine.gid === shrine.gid) { - return this.hooks[i].hook; - } - } - - return false; - }, - - remove: function (shrine) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].shrine.gid === shrine.gid) { - this.hooks[i].hook[0].remove(); - this.hooks.splice(i, 1); - - return true; - } - } - - return false; - }, - - flush: function () { - while (this.hooks.length) { - this.hooks[0].hook[0].remove(); - this.hooks.shift(); - } - } + enabled: true, + hooks: [], + shrines: new Map(), + + check: function () { + if (!this.enabled || me.inTown) { + this.flush(); + + return; + } + + for (let i = 0; i < this.hooks.length; i++) { + if (!copyUnit(this.hooks[i].shrine).objtype) { + this.hooks[i].hook[0].remove(); + this.hooks.splice(i, 1); + + i -= 1; + } + } + + let shrine = Game.getObject(); + + if (shrine) { + do { + if (this.shrines.has(shrine.objtype) && shrine.name.toLowerCase().includes("shrine")) { + if (shrine.mode === sdk.objects.mode.Inactive) { + if (!this.getHook(shrine)) { + this.add(shrine); + } + } else { + this.remove(shrine); + } + } + } while (shrine.getNext()); + } + }, + + newHook: function (shrine) { + let typeName = this.shrines.get(shrine.objtype); + return typeName ? [new Text(typeName, shrine.x, shrine.y, 4, 6, 2, true)] : []; + }, + + add: function (shrine) { + if (!shrine.objtype) return; + + this.hooks.push({ + shrine: copyUnit(shrine), + hook: this.newHook(shrine) + }); + }, + + getHook: function (shrine) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].shrine.gid === shrine.gid) { + return this.hooks[i].hook; + } + } + + return false; + }, + + remove: function (shrine) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].shrine.gid === shrine.gid) { + this.hooks[i].hook[0].remove(); + this.hooks.splice(i, 1); + + return true; + } + } + + return false; + }, + + flush: function () { + while (this.hooks.length) { + this.hooks[0].hook[0].remove(); + this.hooks.shift(); + } + } }; ShrineHooks.shrines.set(sdk.shrines.Refilling, "Refilling"); diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js index 2940f4be3..efa7ab48a 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename TextHooks.js * @author theBGuy @@ -6,308 +7,308 @@ */ const TextHooks = { - enabled: true, - lastAct: 0, - wasInTown: true, - displayTitle: true, - displaySettings: true, - frameworkDisplayed: false, - frameYSizeScale: 0, - frameYLocScale: 0, - settingsModifer: 0, - dashBoardWidthScale: 0, - statusFrameYSize: 0, - qolFrameYSize: 0, - statusHookNames: ["pickitStatus", "vectorStatus", "monsterStatus", "itemStatus"], - qols: ["previousAct", "nextAct", "key6", "key5"], - statusHooks: [], - dashBoard: [], - qolHooks: [], - hooks: [], - yLocMapScale: {1: 40, 2: 30, 3: 20, 4: 10, 6: -10, 9: -40}, - modifier: 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)), - - getScale: function (hkLen) { - if (!!TextHooks.yLocMapScale[hkLen]) { - TextHooks.frameYSizeScale = (-1 * this.yLocMapScale[hkLen]); - TextHooks.frameYLocScale = this.yLocMapScale[hkLen]; - } else { - TextHooks.frameYSizeScale = 0; - TextHooks.frameYLocScale = 0; - } - - TextHooks.settingsModifer = Math.max(0, hkLen - 3); - }, - - check: function () { - if (!this.enabled) { - this.flush(); - - return; - } - - if (!this.frameworkDisplayed) { - !this.getHook("credits", this.hooks) && this.add("credits"); - !!me.gameserverip && !this.getHook("ip", this.hooks) && this.add("ip"); - TextHooks.lastAct = 0; // sorta hacky solution, but works this will cause qolBoard to update after being flushed from a uiflag - TextHooks.frameworkDisplayed = true; - } - - this.displaySettings ? !this.getHook("showSettings", this.statusHooks) && this.add("showSettings") : !this.getHook("hideSettings", this.statusHooks) && this.add("hideSettings"); - this.displayTitle && !this.getHook("title", this.hooks) && this.add("title"); - !this.getHook("ping", this.hooks) ? this.add("ping") : (this.getHook("ping", this.hooks).hook.text = "Ping: " + me.ping); - !this.getHook("time", this.hooks) ? this.add("time") : (this.getHook("time", this.hooks).hook.text = this.timer()); - }, - - update: function (hkLen = 0) { - let updateSettingsDisplay = (this.displaySettings && this.settingsModifer < Math.max(0, hkLen - 3)); - - this.getScale(hkLen); - this.add("dashboard"); - updateSettingsDisplay && this.add("showSettings"); - (this.lastAct !== me.act || this.wasInTown !== me.inTown || !this.getHook("qolBoard", this.qolHooks)) && this.add("qolBoard"); - }, - - hookHandler: function (click, x, y) { - function sortHooks(h1, h2) { - return Math.abs(h1.hook.y - y) - Math.abs(h2.hook.y - y); - } + enabled: true, + lastAct: 0, + wasInTown: true, + displayTitle: true, + displaySettings: true, + frameworkDisplayed: false, + frameYSizeScale: 0, + frameYLocScale: 0, + settingsModifer: 0, + dashBoardWidthScale: 0, + statusFrameYSize: 0, + qolFrameYSize: 0, + statusHookNames: ["pickitStatus", "vectorStatus", "monsterStatus", "itemStatus"], + qols: ["previousAct", "nextAct", "key6", "key5"], + statusHooks: [], + dashBoard: [], + qolHooks: [], + hooks: [], + yLocMapScale: { 1: 40, 2: 30, 3: 20, 4: 10, 6: -10, 9: -40 }, + modifier: 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)), + + getScale: function (hkLen) { + if (!!TextHooks.yLocMapScale[hkLen]) { + TextHooks.frameYSizeScale = (-1 * this.yLocMapScale[hkLen]); + TextHooks.frameYLocScale = this.yLocMapScale[hkLen]; + } else { + TextHooks.frameYSizeScale = 0; + TextHooks.frameYLocScale = 0; + } + + TextHooks.settingsModifer = Math.max(0, hkLen - 3); + }, + + check: function () { + if (!this.enabled) { + this.flush(); + + return; + } + + if (!this.frameworkDisplayed) { + !this.getHook("credits", this.hooks) && this.add("credits"); + !!me.gameserverip && !this.getHook("ip", this.hooks) && this.add("ip"); + TextHooks.lastAct = 0; // sorta hacky solution, but works this will cause qolBoard to update after being flushed from a uiflag + TextHooks.frameworkDisplayed = true; + } + + this.displaySettings ? !this.getHook("showSettings", this.statusHooks) && this.add("showSettings") : !this.getHook("hideSettings", this.statusHooks) && this.add("hideSettings"); + this.displayTitle && !this.getHook("title", this.hooks) && this.add("title"); + !this.getHook("ping", this.hooks) ? this.add("ping") : (this.getHook("ping", this.hooks).hook.text = "Ping: " + me.ping); + !this.getHook("time", this.hooks) ? this.add("time") : (this.getHook("time", this.hooks).hook.text = this.timer()); + }, + + update: function (hkLen = 0) { + let updateSettingsDisplay = (this.displaySettings && this.settingsModifer < Math.max(0, hkLen - 3)); + + this.getScale(hkLen); + this.add("dashboard"); + updateSettingsDisplay && this.add("showSettings"); + (this.lastAct !== me.act || this.wasInTown !== me.inTown || !this.getHook("qolBoard", this.qolHooks)) && this.add("qolBoard"); + }, + + hookHandler: function (click, x, y) { + function sortHooks(h1, h2) { + return Math.abs(h1.hook.y - y) - Math.abs(h2.hook.y - y); + } - if (click === 0) { - TextHooks.statusHooks.sort(sortHooks); + if (click === 0) { + TextHooks.statusHooks.sort(sortHooks); - if (TextHooks.statusHooks[0].name === "hideSettings" || TextHooks.statusHooks[0].name === "showSettings") { - TextHooks.displaySettings = !TextHooks.displaySettings; + if (TextHooks.statusHooks[0].name === "hideSettings" || TextHooks.statusHooks[0].name === "showSettings") { + TextHooks.displaySettings = !TextHooks.displaySettings; - return true; - } - } + return true; + } + } - return false; - }, + return false; + }, - add: function (name, hookArr = []) { - let orginalLen = hookArr.length; + add: function (name, hookArr = []) { + let orginalLen = hookArr.length; - switch (name) { - case "credits": - this.hooks.push({ - name: "credits", - hook: new Text("MM by theBGuy", 0, 600 + Hooks.resfix.y, 4, 0, 0) - }); - - break; - case "title": - this.hooks.push({ - name: "title", - hook: new Text(":: Running Map-Mode, enter .help in chat to see more commands ::", 0, 13, 4, 0, 0) - }); - - break; - case "ping": - this.hooks.push({ - name: "ping", - hook: new Text("Ping: " + me.ping, 785 + Hooks.resfix.x, 56 + this.modifier, 4, 1, 1) - }); - - break; - case "time": - this.hooks.push({ - name: "time", - hook: new Text(this.timer(), 785 + Hooks.resfix.x, 72 + this.modifier, 4, 1, 1) - }); - - break; - case "ip": - this.hooks.push({ - name: "ip", - hook: new Text("IP: " + (me.gameserverip.length > 0 ? me.gameserverip.split(".")[3] : "0"), 785 + Hooks.resfix.x, 88 + this.modifier, 4, 1, 1) - }); - - break; - case "dashboard": - while (this.dashBoard.length) { - this.dashBoard.shift().hook.remove(); - } - - this.dashBoard.push({ - name: "dashboard", - hook: new Box(Hooks.dashBoard.x, Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, 225, 60 + this.frameYSizeScale, 0x0, 1, 0) - }); - - this.dashBoard.push({ - name: "dashboardframe", - hook: new Frame(Hooks.dashBoard.x, Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, 225, 60 + this.frameYSizeScale, 0) - }); - - this.getHook("dashboard", this.dashBoard).hook.zorder = 0; - - break; - case "key5": - this.qolHooks.push({ - name: "key5", - hook: new Text("Key 5: " + (me.inTown ? "Heal" : "Make Portal"), Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) - }); - - break; - case "key6": - this.qolHooks.push({ - name: "key6", - hook: new Text("Key 6: " + (me.inTown ? "Open Stash" : "Go To Town"), Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) - }); - - break; - case "nextAct": - me.inTown && Pather.accessToAct(me.act + 1) && this.qolHooks.push({ - name: "Next Act", - dest: me.act + 1, - type: "actChange", - hook: new Text("Shift > : Next Act", Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) - }); - - break; - case "previousAct": - me.inTown && me.act > 1 && this.qolHooks.push({ - name: "Previous Act", - dest: me.act - 1, - type: "actChange", - hook: new Text("Shift < : Previous Act", Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) - }); - - break; - case "qolBoard": - TextHooks.qolFrameYSize = 50; - TextHooks.lastAct = me.act; - TextHooks.wasInTown = me.inTown; - - while (this.qolHooks.length) { - this.qolHooks.shift().hook.remove(); - } - - this.qols.forEach(function (hook) { - TextHooks.add(hook, TextHooks.qolHooks) && (TextHooks.qolFrameYSize -= 10); - }); - - this.qolHooks.push({ - name: "qolBoard", - hook: new Box(Hooks.qolBoard.x + Hooks.resfix.x, Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, 140, 60 + (-1 * this.qolFrameYSize), 0x0, 1, 0) - }); - - this.qolHooks.push({ - name: "qolFrame", - hook: new Frame(Hooks.qolBoard.x + Hooks.resfix.x, Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, 140, 60 + (-1 * this.qolFrameYSize), 0) - }); - - this.qolHooks[this.qolHooks.length - 2].hook.zorder = 0; - - break; - case "pickitStatus": - this.statusHooks.push({ - name: "pickitStatus", - hook: new Text("ÿc4N-Pad - ÿc0: " + (ItemHooks.pickitEnabled ? "ÿc orginalLen; - }, - - getHook: function (name, hookArr = []) { - for (let i = 0; i < hookArr.length; i++) { - if (hookArr[i].name === name) { - return hookArr[i]; - } - } - - return false; - }, - - timer: function () { - return " (" + new Date(getTickCount() - me.gamestarttime).toISOString().slice(11, -5) + ")"; - }, - - flush: function () { - if (!Hooks.enabled) { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); - } - - TextHooks.frameworkDisplayed = false; - } - - while (this.statusHooks.length) { - this.statusHooks.shift().hook.remove(); - } - - while (this.dashBoard.length) { - this.dashBoard.shift().hook.remove(); - } - - while (this.qolHooks.length) { - this.qolHooks.shift().hook.remove(); - } - }, + switch (name) { + case "credits": + this.hooks.push({ + name: "credits", + hook: new Text("MM by theBGuy", 0, 600 + Hooks.resfix.y, 4, 0, 0) + }); + + break; + case "title": + this.hooks.push({ + name: "title", + hook: new Text(":: Running Map-Mode, enter .help in chat to see more commands ::", 0, 13, 4, 0, 0) + }); + + break; + case "ping": + this.hooks.push({ + name: "ping", + hook: new Text("Ping: " + me.ping, 785 + Hooks.resfix.x, 56 + this.modifier, 4, 1, 1) + }); + + break; + case "time": + this.hooks.push({ + name: "time", + hook: new Text(this.timer(), 785 + Hooks.resfix.x, 72 + this.modifier, 4, 1, 1) + }); + + break; + case "ip": + this.hooks.push({ + name: "ip", + hook: new Text("IP: " + (me.gameserverip.length > 0 ? me.gameserverip.split(".")[3] : "0"), 785 + Hooks.resfix.x, 88 + this.modifier, 4, 1, 1) + }); + + break; + case "dashboard": + while (this.dashBoard.length) { + this.dashBoard.shift().hook.remove(); + } + + this.dashBoard.push({ + name: "dashboard", + hook: new Box(Hooks.dashBoard.x, Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, 225, 60 + this.frameYSizeScale, 0x0, 1, 0) + }); + + this.dashBoard.push({ + name: "dashboardframe", + hook: new Frame(Hooks.dashBoard.x, Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, 225, 60 + this.frameYSizeScale, 0) + }); + + this.getHook("dashboard", this.dashBoard).hook.zorder = 0; + + break; + case "key5": + this.qolHooks.push({ + name: "key5", + hook: new Text("Key 5: " + (me.inTown ? "Heal" : "Make Portal"), Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) + }); + + break; + case "key6": + this.qolHooks.push({ + name: "key6", + hook: new Text("Key 6: " + (me.inTown ? "Open Stash" : "Go To Town"), Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) + }); + + break; + case "nextAct": + me.inTown && Pather.accessToAct(me.act + 1) && this.qolHooks.push({ + name: "Next Act", + dest: me.act + 1, + type: "actChange", + hook: new Text("Shift > : Next Act", Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) + }); + + break; + case "previousAct": + me.inTown && me.act > 1 && this.qolHooks.push({ + name: "Previous Act", + dest: me.act - 1, + type: "actChange", + hook: new Text("Shift < : Previous Act", Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) + }); + + break; + case "qolBoard": + TextHooks.qolFrameYSize = 50; + TextHooks.lastAct = me.act; + TextHooks.wasInTown = me.inTown; + + while (this.qolHooks.length) { + this.qolHooks.shift().hook.remove(); + } + + this.qols.forEach(function (hook) { + TextHooks.add(hook, TextHooks.qolHooks) && (TextHooks.qolFrameYSize -= 10); + }); + + this.qolHooks.push({ + name: "qolBoard", + hook: new Box(Hooks.qolBoard.x + Hooks.resfix.x, Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, 140, 60 + (-1 * this.qolFrameYSize), 0x0, 1, 0) + }); + + this.qolHooks.push({ + name: "qolFrame", + hook: new Frame(Hooks.qolBoard.x + Hooks.resfix.x, Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, 140, 60 + (-1 * this.qolFrameYSize), 0) + }); + + this.qolHooks[this.qolHooks.length - 2].hook.zorder = 0; + + break; + case "pickitStatus": + this.statusHooks.push({ + name: "pickitStatus", + hook: new Text("ÿc4N-Pad - ÿc0: " + (ItemHooks.pickitEnabled ? "ÿc orginalLen; + }, + + getHook: function (name, hookArr = []) { + for (let i = 0; i < hookArr.length; i++) { + if (hookArr[i].name === name) { + return hookArr[i]; + } + } + + return false; + }, + + timer: function () { + return " (" + new Date(getTickCount() - me.gamestarttime).toISOString().slice(11, -5) + ")"; + }, + + flush: function () { + if (!Hooks.enabled) { + while (this.hooks.length) { + this.hooks.shift().hook.remove(); + } + + TextHooks.frameworkDisplayed = false; + } + + while (this.statusHooks.length) { + this.statusHooks.shift().hook.remove(); + } + + while (this.dashBoard.length) { + this.dashBoard.shift().hook.remove(); + } + + while (this.qolHooks.length) { + this.qolHooks.shift().hook.remove(); + } + }, }; diff --git a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js index d989d1608..a293a6a4e 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js @@ -6,390 +6,392 @@ */ const VectorHooks = { - enabled: true, - currArea: 0, - lastLoc: {x: 0, y: 0}, - names: [], - hooks: [], - nextAreas: (function() { - let nextAreas = []; - - // Specific area override - nextAreas[sdk.areas.TamoeHighland] = sdk.areas.MonasteryGate; - nextAreas[sdk.areas.SpiderForest] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.GreatMarsh] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.CrystalizedPassage] = sdk.areas.GlacialTrail; - nextAreas[sdk.areas.GlacialTrail] = sdk.areas.FrozenTundra; - nextAreas[sdk.areas.AncientsWay] = sdk.areas.ArreatSummit; - nextAreas[sdk.areas.ThroneofDestruction] = sdk.areas.WorldstoneChamber; - - return nextAreas; - })(), - - check: function () { - if (!this.enabled) { - this.flush(); - - return; - } - - if (me.area !== this.currArea) { - this.flush(); - - if (!me.area || !me.gameReady) return; - - try { - let exits = getArea().exits; - VectorHooks.currArea = me.area; - - if (exits) { - for (let i = 0; i < exits.length; i++) { - if (me.inArea(sdk.areas.CanyonofMagic)) { - this.add(exits[i].x, exits[i].y, exits[i].target === getRoom().correcttomb ? 0x69 : 0x99); - } else if (exits[i].target === this.nextAreas[me.area] && this.nextAreas[me.area]) { - this.add(exits[i].x, exits[i].y, 0x1F); - } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area) && this.nextAreas[me.area]) { - this.add(exits[i].x, exits[i].y, 0x99); - } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area)) { - this.add(exits[i].x, exits[i].y, 0x1F); - } else if (exits[i].target === ActionHooks.prevAreas[me.area]) { - this.add(exits[i].x, exits[i].y, 0x0A); - } else { - this.add(exits[i].x, exits[i].y, 0x99); - } - - this.addNames(exits[i]); - } - } - - let wp = this.getWP(); - wp && this.add(wp.x, wp.y, 0xA8); - let poi = this.getPOI(); - poi && this.add(poi.x, poi.y, 0x7D); - } catch (e) { - console.error(e); - } - } else if (me.x !== this.lastLoc.x || me.y !== this.lastLoc.y) { - this.update(); - } - }, - - add: function (x, y, color) { - this.hooks.push(new Line(me.x, me.y, x, y, color, true)); - }, - - addNames: function (area) { - this.names.push(new Text(getAreaName(area.target), area.x, area.y, 0, 6, 2, true)); - }, - - update: function () { - VectorHooks.lastLoc = {x: me.x, y: me.y}; - - for (let i = 0; i < this.hooks.length; i++) { - this.hooks[i].x = me.x; - this.hooks[i].y = me.y; - } - }, - - flush: function () { - while (this.hooks.length) { - this.hooks.shift().remove(); - } - - while (this.names.length) { - this.names.shift().remove(); - } - - VectorHooks.currArea = 0; - }, - - getWP: function () { - if (Pather.wpAreas.indexOf(me.area) === -1) return false; - - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - - if (preset) { - return preset.realCoords(); - } - } - - return false; - }, - - getPOI: function () { - let unit, name; - let poi = {}; - - switch (me.area) { - case sdk.areas.CaveLvl2: - case sdk.areas.HoleLvl2: - case sdk.areas.PitLvl2: - case sdk.areas.Crypt: - case sdk.areas.Mausoleum: - case sdk.areas.StonyTombLvl2: - case sdk.areas.AncientTunnels: - case sdk.areas.GreatMarsh: - case sdk.areas.SpiderCave: - case sdk.areas.SwampyPitLvl3: - case sdk.areas.DisusedFane: - case sdk.areas.ForgottenReliquary: - case sdk.areas.ForgottenTemple: - case sdk.areas.DisusedReliquary: - case sdk.areas.DrifterCavern: - case sdk.areas.IcyCellar: - case sdk.areas.Abaddon: - case sdk.areas.PitofAcheron: - case sdk.areas.InfernalPit: - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - poi = {name: "SuperChest", action: {do: "openChest", id: sdk.objects.SmallSparklyChest}}; - - break; - case sdk.areas.GlacialTrail: - case sdk.areas.HallsofAnguish: - case sdk.areas.HallsofPain: - unit = Game.getPresetObject(me.area, sdk.objects.LargeSparklyChest); - poi = {name: "SuperChest", action: {do: "openChest", id: sdk.objects.LargeSparklyChest}}; - - break; - case sdk.areas.ColdPlains: - unit = Game.getPresetStair(me.area, sdk.exits.preset.AreaEntrance); - name = "Cave Level 1"; - - break; - case sdk.areas.StonyField: - unit = Game.getPresetMonster(me.area, sdk.monsters.preset.Rakanishu); - poi = {name: "Cairn Stones", action: {do: "usePortal", id: sdk.areas.Tristram}}; - - break; - case sdk.areas.DarkWood: - unit = Game.getPresetObject(me.area, sdk.quest.chest.InifussTree); - name = "Tree"; - - break; - case sdk.areas.BlackMarsh: - unit = Game.getPresetStair(me.area, sdk.exits.preset.AreaEntrance); - name = "Hole Level 1"; - - break; - case sdk.areas.DenofEvil: - unit = Game.getPresetMonster(me.area, sdk.monsters.preset.Corpsefire); - name = "Corpsefire"; - - break; - case sdk.areas.BurialGrounds: - unit = Game.getPresetMonster(me.area, sdk.monsters.preset.BloodRaven); - name = "Bloodraven"; - - break; - case sdk.areas.TowerCellarLvl5: - unit = Game.getPresetObject(me.area, sdk.objects.SuperChest); - name = "Countess"; - - break; - case sdk.areas.Barracks: - unit = Game.getPresetObject(me.area, sdk.quest.chest.MalusHolder); - name = "Smith"; - - break; - case sdk.areas.Cathedral: - unit = {x: 20047, y: 4898}; - name = "BoneAsh"; - - break; - case sdk.areas.CatacombsLvl4: - unit = {x: 22549, y: 9520}; - name = "Andariel"; - - break; - case sdk.areas.Tristram: - unit = Game.getMonster(sdk.monsters.Griswold) ? Game.getMonster(sdk.monsters.Griswold) : {x: 25163, y: 5170}; - name = "Griswold"; - - break; - case sdk.areas.MooMooFarm: - unit = Game.getMonster(sdk.monsters.TheCowKing) ? Game.getMonster(sdk.monsters.TheCowKing) : Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing); - name = "Cow King"; - - break; - case sdk.areas.LutGholein: - unit = Game.getPresetStair(me.area, sdk.exits.preset.A2EnterSewersDoor); - name = "Sewer's Level 1"; - - break; - case sdk.areas.A2SewersLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricScrollChest); - name = "Radament"; - - break; - case sdk.areas.PalaceCellarLvl3: - unit = {x: 10073, y: 8670}; - poi = {name: "Arcane Sanctuary", action: {do: "usePortal"}}; - - break; - case sdk.areas.HallsoftheDeadLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricCubeChest); - poi = {name: "Cube", action: {do: "openChest", id: sdk.quest.chest.HoradricCubeChest}}; - - break; - case sdk.areas.ClawViperTempleLvl2: - unit = Game.getPresetObject(me.area, sdk.quest.chest.ViperAmuletChest); - poi = {name: "Amulet", action: {do: "openChest", id: sdk.quest.chest.ViperAmuletChest}}; - - break; - case sdk.areas.MaggotLairLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.ShaftoftheHoradricStaffChest); - poi = {name: "Staff", action: {do: "openChest", id: sdk.quest.chest.ShaftoftheHoradricStaffChest}}; - - break; - case sdk.areas.ArcaneSanctuary: - unit = Game.getPresetObject(me.area, sdk.quest.chest.Journal); - name = "Summoner"; - - break; - case sdk.areas.TalRashasTomb1: - case sdk.areas.TalRashasTomb2: - case sdk.areas.TalRashasTomb3: - case sdk.areas.TalRashasTomb4: - case sdk.areas.TalRashasTomb5: - case sdk.areas.TalRashasTomb6: - case sdk.areas.TalRashasTomb7: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricStaffHolder); - name = "Orifice"; - - if (!unit) { - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - name = "SuperChest"; - } - - break; - case sdk.areas.DurielsLair: - unit = {x: 22577, y: 15609}; - name = "Tyrael"; - - break; - case sdk.areas.FlayerJungle: - unit = Game.getPresetObject(me.area, sdk.quest.chest.GidbinnAltar); - name = "Gidbinn"; - - break; - case sdk.areas.KurastBazaar: - unit = Game.getPresetStair(me.area, sdk.exits.preset.A3EnterSewers); - name = "Sewer's Level 1"; - - break; - case sdk.areas.SpiderCavern: - unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsEyeChest); - poi = {name: "Eye", action: {do: "openChest", id: sdk.quest.chest.KhalimsEyeChest}}; - - break; - case sdk.areas.FlayerDungeonLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsBrainChest); - poi = {name: "Brain", action: {do: "openChest", id: sdk.quest.chest.KhalimsBrainChest}}; - - break; - case sdk.areas.A3SewersLvl2: - unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsHeartChest); - poi = {name: "Heart", action: {do: "openChest", id: sdk.quest.chest.KhalimsHeartChest}}; - - break; - case sdk.areas.RuinedTemple: - unit = Game.getPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder); - poi = {name: "Lam Esen", action: {do: "openChest", id: sdk.quest.chest.LamEsensTomeHolder}}; - - break; - case sdk.areas.Travincal: - unit = Game.getPresetObject(me.area, sdk.objects.CompellingOrb); - name = "Orb"; - - break; - case sdk.areas.DuranceofHateLvl3: - unit = {x: 17588, y: 8069}; - name = "Mephisto"; - - break; - case sdk.areas.PlainsofDespair: - unit = Game.getPresetMonster(me.area, sdk.monsters.Izual); - name = "Izual"; - - break; - case sdk.areas.RiverofFlame: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HellForge); - name = "Hephasto"; - - break; - case sdk.areas.ChaosSanctuary: - unit = Game.getPresetObject(me.area, sdk.objects.DiabloStar); - name = "Star"; - - break; - case sdk.areas.Harrogath: - unit = {x: 5112, y: 5120}; - poi = {name: "Anya Portal", action: {do: "usePortal", id: sdk.areas.NihlathaksTemple}}; - - break; - case sdk.areas.BloodyFoothills: - unit = {x: 3899, y: 5113}; - name = "Shenk"; - - break; - case sdk.areas.FrigidHighlands: - case sdk.areas.ArreatPlateau: - case sdk.areas.FrozenTundra: - unit = Game.getPresetObject(me.area, sdk.objects.RedPortal); - poi = {name: "Hell Entrance", action: {do: "usePortal"}}; - - break; - case sdk.areas.FrozenRiver: - unit = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform); - name = "Frozen Anya"; - - break; - case sdk.areas.NihlathaksTemple: - unit = {x: 10058, y: 13234}; - name = "Pindle"; - - break; - case sdk.areas.HallsofVaught: - unit = Game.getPresetObject(me.area, sdk.objects.NihlathaksPlatform); - name = "Nihlathak"; - - break; - case sdk.areas.ThroneofDestruction: - unit = {x: 15118, y: 5002}; - name = "Throne Room"; - - break; - case sdk.areas.WorldstoneChamber: - unit = Game.getMonster(sdk.monsters.Baal) ? Game.getMonster(sdk.monsters.Baal) : {x: 15134, y: 5923}; - name = "Baal"; - - break; - case sdk.areas.MatronsDen: - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - name = "Lilith"; - - break; - case sdk.areas.ForgottenSands: - unit = Game.getMonster(sdk.monsters.UberDuriel); - name = "Duriel"; - - break; - case sdk.areas.FurnaceofPain: - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - name = "Izual"; - - break; - } - - if (unit) { - name && !poi.name && (poi.name = name); - (unit instanceof PresetUnit) && (unit = unit.realCoords()); - [poi.x, poi.y] = [unit.x, unit.y]; - - return poi; - } - - return false; - } + enabled: true, + currArea: 0, + lastLoc: { x: 0, y: 0 }, + names: [], + hooks: [], + nextAreas: (function() { + let nextAreas = []; + + // Specific area override + nextAreas[sdk.areas.TamoeHighland] = sdk.areas.MonasteryGate; + nextAreas[sdk.areas.SpiderForest] = sdk.areas.FlayerJungle; + nextAreas[sdk.areas.GreatMarsh] = sdk.areas.FlayerJungle; + nextAreas[sdk.areas.CrystalizedPassage] = sdk.areas.GlacialTrail; + nextAreas[sdk.areas.GlacialTrail] = sdk.areas.FrozenTundra; + nextAreas[sdk.areas.AncientsWay] = sdk.areas.ArreatSummit; + nextAreas[sdk.areas.ThroneofDestruction] = sdk.areas.WorldstoneChamber; + + return nextAreas; + })(), + + check: function () { + if (!this.enabled) { + this.flush(); + + return; + } + + if (me.area !== this.currArea) { + this.flush(); + + if (!me.area || !me.gameReady) return; + + try { + let exits = getArea().exits; + VectorHooks.currArea = me.area; + + if (exits) { + for (let i = 0; i < exits.length; i++) { + if (me.inArea(sdk.areas.CanyonofMagic)) { + this.add(exits[i].x, exits[i].y, exits[i].target === getRoom().correcttomb ? 0x69 : 0x99); + } else if (exits[i].target === this.nextAreas[me.area] && this.nextAreas[me.area]) { + this.add(exits[i].x, exits[i].y, 0x1F); + } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area) && this.nextAreas[me.area]) { + this.add(exits[i].x, exits[i].y, 0x99); + } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area)) { + this.add(exits[i].x, exits[i].y, 0x1F); + } else if (exits[i].target === ActionHooks.prevAreas[me.area]) { + this.add(exits[i].x, exits[i].y, 0x0A); + } else { + this.add(exits[i].x, exits[i].y, 0x99); + } + + this.addNames(exits[i]); + } + } + + let wp = this.getWP(); + wp && this.add(wp.x, wp.y, 0xA8); + let poi = this.getPOI(); + poi && this.add(poi.x, poi.y, 0x7D); + } catch (e) { + console.error(e); + } + } else if (me.x !== this.lastLoc.x || me.y !== this.lastLoc.y) { + this.update(); + } + }, + + add: function (x, y, color) { + this.hooks.push(new Line(me.x, me.y, x, y, color, true)); + }, + + addNames: function (area) { + this.names.push(new Text(getAreaName(area.target), area.x, area.y, 0, 6, 2, true)); + }, + + update: function () { + VectorHooks.lastLoc = { x: me.x, y: me.y }; + + for (let i = 0; i < this.hooks.length; i++) { + this.hooks[i].x = me.x; + this.hooks[i].y = me.y; + } + }, + + flush: function () { + while (this.hooks.length) { + this.hooks.shift().remove(); + } + + while (this.names.length) { + this.names.shift().remove(); + } + + VectorHooks.currArea = 0; + }, + + getWP: function () { + if (Pather.wpAreas.indexOf(me.area) === -1) return false; + + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + + if (preset) { + return preset.realCoords(); + } + } + + return false; + }, + + getPOI: function () { + let unit, name; + let poi = {}; + + switch (me.area) { + case sdk.areas.CaveLvl2: + case sdk.areas.HoleLvl2: + case sdk.areas.PitLvl2: + case sdk.areas.Crypt: + case sdk.areas.Mausoleum: + case sdk.areas.StonyTombLvl2: + case sdk.areas.AncientTunnels: + case sdk.areas.GreatMarsh: + case sdk.areas.SpiderCave: + case sdk.areas.SwampyPitLvl3: + case sdk.areas.DisusedFane: + case sdk.areas.ForgottenReliquary: + case sdk.areas.ForgottenTemple: + case sdk.areas.DisusedReliquary: + case sdk.areas.DrifterCavern: + case sdk.areas.IcyCellar: + case sdk.areas.Abaddon: + case sdk.areas.PitofAcheron: + case sdk.areas.InfernalPit: + unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); + poi = { name: "SuperChest", action: { do: "openChest", id: sdk.objects.SmallSparklyChest } }; + + break; + case sdk.areas.GlacialTrail: + case sdk.areas.HallsofAnguish: + case sdk.areas.HallsofPain: + unit = Game.getPresetObject(me.area, sdk.objects.LargeSparklyChest); + poi = { name: "SuperChest", action: { do: "openChest", id: sdk.objects.LargeSparklyChest } }; + + break; + case sdk.areas.ColdPlains: + unit = Game.getPresetStair(me.area, sdk.exits.preset.AreaEntrance); + name = "Cave Level 1"; + + break; + case sdk.areas.StonyField: + unit = Game.getPresetMonster(me.area, sdk.monsters.preset.Rakanishu); + poi = { name: "Cairn Stones", action: { do: "usePortal", id: sdk.areas.Tristram } }; + + break; + case sdk.areas.DarkWood: + unit = Game.getPresetObject(me.area, sdk.quest.chest.InifussTree); + name = "Tree"; + + break; + case sdk.areas.BlackMarsh: + unit = Game.getPresetStair(me.area, sdk.exits.preset.AreaEntrance); + name = "Hole Level 1"; + + break; + case sdk.areas.DenofEvil: + unit = Game.getPresetMonster(me.area, sdk.monsters.preset.Corpsefire); + name = "Corpsefire"; + + break; + case sdk.areas.BurialGrounds: + unit = Game.getPresetMonster(me.area, sdk.monsters.preset.BloodRaven); + name = "Bloodraven"; + + break; + case sdk.areas.TowerCellarLvl5: + unit = Game.getPresetObject(me.area, sdk.objects.SuperChest); + name = "Countess"; + + break; + case sdk.areas.Barracks: + unit = Game.getPresetObject(me.area, sdk.quest.chest.MalusHolder); + name = "Smith"; + + break; + case sdk.areas.Cathedral: + unit = { x: 20047, y: 4898 }; + name = "BoneAsh"; + + break; + case sdk.areas.CatacombsLvl4: + unit = { x: 22549, y: 9520 }; + name = "Andariel"; + + break; + case sdk.areas.Tristram: + unit = Game.getMonster(sdk.monsters.Griswold) ? Game.getMonster(sdk.monsters.Griswold) : { x: 25163, y: 5170 }; + name = "Griswold"; + + break; + case sdk.areas.MooMooFarm: + unit = Game.getMonster(sdk.monsters.TheCowKing) + ? Game.getMonster(sdk.monsters.TheCowKing) + : Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing); + name = "Cow King"; + + break; + case sdk.areas.LutGholein: + unit = Game.getPresetStair(me.area, sdk.exits.preset.A2EnterSewersDoor); + name = "Sewer's Level 1"; + + break; + case sdk.areas.A2SewersLvl3: + unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricScrollChest); + name = "Radament"; + + break; + case sdk.areas.PalaceCellarLvl3: + unit = { x: 10073, y: 8670 }; + poi = { name: "Arcane Sanctuary", action: { do: "usePortal" } }; + + break; + case sdk.areas.HallsoftheDeadLvl3: + unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricCubeChest); + poi = { name: "Cube", action: { do: "openChest", id: sdk.quest.chest.HoradricCubeChest } }; + + break; + case sdk.areas.ClawViperTempleLvl2: + unit = Game.getPresetObject(me.area, sdk.quest.chest.ViperAmuletChest); + poi = { name: "Amulet", action: { do: "openChest", id: sdk.quest.chest.ViperAmuletChest } }; + + break; + case sdk.areas.MaggotLairLvl3: + unit = Game.getPresetObject(me.area, sdk.quest.chest.ShaftoftheHoradricStaffChest); + poi = { name: "Staff", action: { do: "openChest", id: sdk.quest.chest.ShaftoftheHoradricStaffChest } }; + + break; + case sdk.areas.ArcaneSanctuary: + unit = Game.getPresetObject(me.area, sdk.quest.chest.Journal); + name = "Summoner"; + + break; + case sdk.areas.TalRashasTomb1: + case sdk.areas.TalRashasTomb2: + case sdk.areas.TalRashasTomb3: + case sdk.areas.TalRashasTomb4: + case sdk.areas.TalRashasTomb5: + case sdk.areas.TalRashasTomb6: + case sdk.areas.TalRashasTomb7: + unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricStaffHolder); + name = "Orifice"; + + if (!unit) { + unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); + name = "SuperChest"; + } + + break; + case sdk.areas.DurielsLair: + unit = { x: 22577, y: 15609 }; + name = "Tyrael"; + + break; + case sdk.areas.FlayerJungle: + unit = Game.getPresetObject(me.area, sdk.quest.chest.GidbinnAltar); + name = "Gidbinn"; + + break; + case sdk.areas.KurastBazaar: + unit = Game.getPresetStair(me.area, sdk.exits.preset.A3EnterSewers); + name = "Sewer's Level 1"; + + break; + case sdk.areas.SpiderCavern: + unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsEyeChest); + poi = { name: "Eye", action: { do: "openChest", id: sdk.quest.chest.KhalimsEyeChest } }; + + break; + case sdk.areas.FlayerDungeonLvl3: + unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsBrainChest); + poi = { name: "Brain", action: { do: "openChest", id: sdk.quest.chest.KhalimsBrainChest } }; + + break; + case sdk.areas.A3SewersLvl2: + unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsHeartChest); + poi = { name: "Heart", action: { do: "openChest", id: sdk.quest.chest.KhalimsHeartChest } }; + + break; + case sdk.areas.RuinedTemple: + unit = Game.getPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder); + poi = { name: "Lam Esen", action: { do: "openChest", id: sdk.quest.chest.LamEsensTomeHolder } }; + + break; + case sdk.areas.Travincal: + unit = Game.getPresetObject(me.area, sdk.objects.CompellingOrb); + name = "Orb"; + + break; + case sdk.areas.DuranceofHateLvl3: + unit = { x: 17588, y: 8069 }; + name = "Mephisto"; + + break; + case sdk.areas.PlainsofDespair: + unit = Game.getPresetMonster(me.area, sdk.monsters.Izual); + name = "Izual"; + + break; + case sdk.areas.RiverofFlame: + unit = Game.getPresetObject(me.area, sdk.quest.chest.HellForge); + name = "Hephasto"; + + break; + case sdk.areas.ChaosSanctuary: + unit = Game.getPresetObject(me.area, sdk.objects.DiabloStar); + name = "Star"; + + break; + case sdk.areas.Harrogath: + unit = { x: 5112, y: 5120 }; + poi = { name: "Anya Portal", action: { do: "usePortal", id: sdk.areas.NihlathaksTemple } }; + + break; + case sdk.areas.BloodyFoothills: + unit = { x: 3899, y: 5113 }; + name = "Shenk"; + + break; + case sdk.areas.FrigidHighlands: + case sdk.areas.ArreatPlateau: + case sdk.areas.FrozenTundra: + unit = Game.getPresetObject(me.area, sdk.objects.RedPortal); + poi = { name: "Hell Entrance", action: { do: "usePortal" } }; + + break; + case sdk.areas.FrozenRiver: + unit = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform); + name = "Frozen Anya"; + + break; + case sdk.areas.NihlathaksTemple: + unit = { x: 10058, y: 13234 }; + name = "Pindle"; + + break; + case sdk.areas.HallsofVaught: + unit = Game.getPresetObject(me.area, sdk.objects.NihlathaksPlatform); + name = "Nihlathak"; + + break; + case sdk.areas.ThroneofDestruction: + unit = { x: 15118, y: 5002 }; + name = "Throne Room"; + + break; + case sdk.areas.WorldstoneChamber: + unit = Game.getMonster(sdk.monsters.Baal) ? Game.getMonster(sdk.monsters.Baal) : { x: 15134, y: 5923 }; + name = "Baal"; + + break; + case sdk.areas.MatronsDen: + unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); + name = "Lilith"; + + break; + case sdk.areas.ForgottenSands: + unit = Game.getMonster(sdk.monsters.UberDuriel); + name = "Duriel"; + + break; + case sdk.areas.FurnaceofPain: + unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); + name = "Izual"; + + break; + } + + if (unit) { + name && !poi.name && (poi.name = name); + (unit instanceof PresetUnit) && (unit = unit.realCoords()); + [poi.x, poi.y] = [unit.x, unit.y]; + + return poi; + } + + return false; + } }; diff --git a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js index 564e549e2..d6677ebab 100644 --- a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js @@ -8,26 +8,26 @@ includeIfNotIncluded("core/Attack.js"); Attack.init = function (notify = false) { - if (Config.Wereform) { - include("core/Attacks/wereform.js"); - } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { - print("Loading custom attack file"); - include("core/Attacks/" + Config.CustomClassAttack + ".js"); - } else { - include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); - } + if (Config.Wereform) { + include("core/Attacks/wereform.js"); + } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { + print("Loading custom attack file"); + include("core/Attacks/" + Config.CustomClassAttack + ".js"); + } else { + include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); + } - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { - notify && print("ÿc1Bad attack config. Don't expect your bot to attack."); - } + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { + notify && print("ÿc1Bad attack config. Don't expect your bot to attack."); + } - this.getPrimarySlot(); - Skill.init(); + this.getPrimarySlot(); + Skill.init(); - if (me.expansion) { - Precast.checkCTA(); - this.checkInfinity(); - this.checkAuradin(); - } + if (me.expansion) { + Precast.checkCTA(); + this.checkInfinity(); + this.checkAuradin(); + } }; diff --git a/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js b/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js index d04d9635e..c85fc542e 100644 --- a/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename ConfigOverrides.js * @author theBGuy @@ -10,104 +11,104 @@ includeIfNotIncluded("core/Config.js"); let original = Config.init; Config.init = function (notify) { - let configFilename = ""; - let classes = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; - - for (let i = 0; i < 5; i += 1) { - switch (i) { - case 0: // Custom config - includeIfNotIncluded("config/_customconfig.js"); - - for (let n in CustomConfig) { - if (CustomConfig.hasOwnProperty(n)) { - if (CustomConfig[n].indexOf(me.profile) > -1) { - if (notify) { - print("ÿc2Loading custom config: ÿc9" + n + ".js"); - } - - configFilename = n + ".js"; - - break; - } - } - } - - break; - case 1:// Class.Profile.js - configFilename = classes[me.classid] + "." + me.profile + ".js"; - - break; - case 2: // Realm.Class.Charname.js - configFilename = me.realm + "." + classes[me.classid] + "." + me.charname + ".js"; - - break; - case 3: // Class.Charname.js - configFilename = classes[me.classid] + "." + me.charname + ".js"; - - break; - case 4: // Profile.js - configFilename = me.profile + ".js"; - - break; - } - - if (configFilename && FileTools.exists("libs/manualplay/config/" + configFilename)) { - break; - } - } - - if (FileTools.exists("libs/manualplay/config/" + configFilename)) { - try { - if (!include("manualplay/config/" + configFilename)) { - throw new Error(); - } - } catch (e1) { - throw new Error("Failed to load character config."); - } - } else { - if (notify) { - print("ÿc1" + classes[me.classid] + "." + me.charname + ".js not found!"); // Use the primary format - print("ÿc1Loading default config."); - } - - try { - // Try to find default config - if (!FileTools.exists("libs/manualplay/config/" + classes[me.classid] + ".js")) { - D2Bot.printToConsole("Not going well? Read the guides: https://github.com/blizzhackers/documentation"); - throw new Error("ÿc1Default config not found. \nÿc9 Try reading the kolbot guides."); - } - - if (!include("manualplay/config/" + classes[me.classid] + ".js")) { - throw new Error("ÿc1Failed to load default config."); - } - } catch (e) { - print(e); - original(); - } - } - - try { - LoadConfig.call(); - } catch (e2) { - if (notify) { - print("ÿ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; - global.say = (what) => print("Tryed to say: " + what); - } - - try { - if (Config.AutoBuild.Enabled === true && !isIncluded("core/Auto/AutoBuild.js") && include("core/Auto/AutoBuild.js")) { - AutoBuild.initialize(); - } - } catch (e3) { - print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); - print(e3.toSource()); - } + let configFilename = ""; + let classes = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; + + for (let i = 0; i < 5; i += 1) { + switch (i) { + case 0: // Custom config + includeIfNotIncluded("config/_customconfig.js"); + + for (let n in CustomConfig) { + if (CustomConfig.hasOwnProperty(n)) { + if (CustomConfig[n].indexOf(me.profile) > -1) { + if (notify) { + print("ÿc2Loading custom config: ÿc9" + n + ".js"); + } + + configFilename = n + ".js"; + + break; + } + } + } + + break; + case 1:// Class.Profile.js + configFilename = classes[me.classid] + "." + me.profile + ".js"; + + break; + case 2: // Realm.Class.Charname.js + configFilename = me.realm + "." + classes[me.classid] + "." + me.charname + ".js"; + + break; + case 3: // Class.Charname.js + configFilename = classes[me.classid] + "." + me.charname + ".js"; + + break; + case 4: // Profile.js + configFilename = me.profile + ".js"; + + break; + } + + if (configFilename && FileTools.exists("libs/manualplay/config/" + configFilename)) { + break; + } + } + + if (FileTools.exists("libs/manualplay/config/" + configFilename)) { + try { + if (!include("manualplay/config/" + configFilename)) { + throw new Error(); + } + } catch (e1) { + throw new Error("Failed to load character config."); + } + } else { + if (notify) { + print("ÿc1" + classes[me.classid] + "." + me.charname + ".js not found!"); // Use the primary format + print("ÿc1Loading default config."); + } + + try { + // Try to find default config + if (!FileTools.exists("libs/manualplay/config/" + classes[me.classid] + ".js")) { + D2Bot.printToConsole("Not going well? Read the guides: https://github.com/blizzhackers/documentation"); + throw new Error("ÿc1Default config not found. \nÿc9 Try reading the kolbot guides."); + } + + if (!include("manualplay/config/" + classes[me.classid] + ".js")) { + throw new Error("ÿc1Failed to load default config."); + } + } catch (e) { + print(e); + original(); + } + } + + try { + LoadConfig.call(); + } catch (e2) { + if (notify) { + print("ÿ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; + global.say = (what) => print("Tryed to say: " + what); + } + + try { + if (Config.AutoBuild.Enabled === true && !isIncluded("core/Auto/AutoBuild.js") && include("core/Auto/AutoBuild.js")) { + AutoBuild.initialize(); + } + } catch (e3) { + print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); + print(e3.toSource()); + } }; diff --git a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js b/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js index 162d8664a..57d2094d8 100644 --- a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js @@ -8,156 +8,158 @@ includeIfNotIncluded("core/Misc.js"); Misc.openRedPortal = function (portalID) { - if (!me.getItem(sdk.quest.item.Cube)) return; + if (!me.getItem(sdk.quest.item.Cube)) return; - function getTome () { - let npc, tome, scroll; - let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + function getTome () { + 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"); + try { + if (tpTome.length < 2) { + npc = Town.initNPC("Shop", "buyTpTome"); - if (!getInteractedNPC()) { - throw new Error("Failed to find npc"); - } + if (!getInteractedNPC()) { + throw new Error("Failed to find npc"); + } - tome = npc.getItem(sdk.items.TomeofTownPortal); + 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); - tpTome.forEach(function (book) { - while (book.getStat(sdk.stats.Quantity) < 20) { - scroll = npc.getItem(sdk.items.ScrollofTownPortal); + 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); + tpTome.forEach(function (book) { + while (book.getStat(sdk.stats.Quantity) < 20) { + scroll = npc.getItem(sdk.items.ScrollofTownPortal); - if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { - scroll.buy(); - } else { - break; - } - - delay(20); - } - }); - } - } - } finally { - me.cancel(); - } - } - - try { - let materials, validMats = []; - - switch (portalID) { - case sdk.areas.MooMooFarm: - if (me.getQuest(sdk.quest.id.TheSearchForCain, 10)) { - throw new Error("Unable to open cow portal because cow king has been killed"); - } - - materials = [sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal]; - - break; - case sdk.areas.UberTristram: - materials = [sdk.quest.item.DiablosHorn, sdk.quest.item.BaalsEye, sdk.quest.item.MephistosBrain]; - - break; - default: - materials = [sdk.quest.item.KeyofTerror, sdk.quest.item.KeyofHate, sdk.quest.item.KeyofDestruction]; - - break; - } - - materials.forEach(function (mat) { - mat === sdk.items.TomeofTownPortal && getTome(); - let item = me.getItem(mat); - !!item && validMats.push(item); - }); - - if (validMats.length !== materials.length) throw new Error("Missing materials to open portal"); - - portalID === sdk.areas.MooMooFarm ? !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1) : !me.inArea(sdk.areas.Harrogath) && Town.goToTown(5); - - Town.move("stash"); - - if (portalID && Pather.getPortal(portalID)) throw new Error("Portal is already open"); - - Cubing.openCube(); - - if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); - - validMats.forEach(function (mat) { - return Storage.Cube.MoveTo(mat); - }); - - Cubing.openCube() && transmute(); - } catch (e) { - console.error(e); - } finally { - me.cancel(); - } + if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { + scroll.buy(); + } else { + break; + } + + delay(20); + } + }); + } + } + } finally { + me.cancel(); + } + } + + try { + let materials, validMats = []; + + switch (portalID) { + case sdk.areas.MooMooFarm: + if (me.getQuest(sdk.quest.id.TheSearchForCain, 10)) { + throw new Error("Unable to open cow portal because cow king has been killed"); + } + + materials = [sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal]; + + break; + case sdk.areas.UberTristram: + materials = [sdk.quest.item.DiablosHorn, sdk.quest.item.BaalsEye, sdk.quest.item.MephistosBrain]; + + break; + default: + materials = [sdk.quest.item.KeyofTerror, sdk.quest.item.KeyofHate, sdk.quest.item.KeyofDestruction]; + + break; + } + + materials.forEach(function (mat) { + mat === sdk.items.TomeofTownPortal && getTome(); + let item = me.getItem(mat); + !!item && validMats.push(item); + }); + + if (validMats.length !== materials.length) throw new Error("Missing materials to open portal"); + + portalID === sdk.areas.MooMooFarm + ? !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1) + : !me.inArea(sdk.areas.Harrogath) && Town.goToTown(5); + + Town.move("stash"); + + if (portalID && Pather.getPortal(portalID)) throw new Error("Portal is already open"); + + Cubing.openCube(); + + if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); + + validMats.forEach(function (mat) { + return Storage.Cube.MoveTo(mat); + }); + + Cubing.openCube() && transmute(); + } catch (e) { + console.error(e); + } finally { + me.cancel(); + } }; Misc.talkToTyrael = function () { - if (!me.inArea(sdk.areas.DurielsLair)) return false; - - Pather.walkTo(22621, 15711); - Pather.moveTo(22602, 15705); - Pather.moveTo(22579, 15704); - Pather.moveTo(22575, 15675); - Pather.moveTo(22579, 15655); - Pather.walkTo(22578, 15642); // walk trough door - Pather.moveTo(22578, 15618); - Pather.moveTo(22576, 15591); // tyreal - - let tyrael = Game.getNPC(NPC.Tyrael); - - if (tyrael) { - for (let i = 0; i < 3; i++) { - if (getDistance(me, tyrael) > 3) { - Pather.moveToUnit(tyrael); - } - - tyrael.interact(); - delay(1000 + me.ping); - me.cancel(); - - if (Pather.getPortal(null)) { - me.cancel(); - - break; - } - } - } - - return Pather.usePortal(null); + if (!me.inArea(sdk.areas.DurielsLair)) return false; + + Pather.walkTo(22621, 15711); + Pather.moveTo(22602, 15705); + Pather.moveTo(22579, 15704); + Pather.moveTo(22575, 15675); + Pather.moveTo(22579, 15655); + Pather.walkTo(22578, 15642); // walk trough door + Pather.moveTo(22578, 15618); + Pather.moveTo(22576, 15591); // tyreal + + let tyrael = Game.getNPC(NPC.Tyrael); + + if (tyrael) { + for (let i = 0; i < 3; i++) { + if (getDistance(me, tyrael) > 3) { + Pather.moveToUnit(tyrael); + } + + tyrael.interact(); + delay(1000 + me.ping); + me.cancel(); + + if (Pather.getPortal(null)) { + me.cancel(); + + break; + } + } + } + + return Pather.usePortal(null); }; Misc.dropItems = function (fromLoc) { - try { - if (!fromLoc) throw new Error("No location given"); - if (fromLoc === sdk.storage.Stash && !Town.openStash()) throw new Error("Failed to open stash"); - - let items = me.findItems(-1, sdk.items.mode.inStorage, fromLoc); - - if (items) { - while (items.length > 0) { - let item = items.shift(); - - if (item.classid === sdk.quest.item.Cube - || (item.isInInventory && [sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(item.itemType) && Storage.Inventory.IsLocked(item, Config.Inventory))) { - continue; - } else { - item.drop(); - } - } - } else { - throw new Error("Couldn't find any items"); - } - } catch (e) { - console.error(e); - } finally { - me.cancel(); - } + try { + if (!fromLoc) throw new Error("No location given"); + if (fromLoc === sdk.storage.Stash && !Town.openStash()) throw new Error("Failed to open stash"); + + let items = me.findItems(-1, sdk.items.mode.inStorage, fromLoc); + + if (items) { + while (items.length > 0) { + let item = items.shift(); + + if (item.classid === sdk.quest.item.Cube + || (item.isEquippedCharm && Storage.Inventory.IsLocked(item, Config.Inventory))) { + continue; + } else { + item.drop(); + } + } + } else { + throw new Error("Couldn't find any items"); + } + } catch (e) { + console.error(e); + } finally { + me.cancel(); + } }; diff --git a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js index 1090d7b2c..c20e33caf 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename PatherOverides.js * @author theBGuy @@ -9,398 +10,398 @@ includeIfNotIncluded("core/Pather.js"); Pather.stop = false; Pather.stopEvent = function (key) { - key === sdk.keys.Numpad9 && !me.idle && (Pather.stop = true); + key === sdk.keys.Numpad9 && !me.idle && (Pather.stop = true); }; Pather.changeAct = function (act) { - let npc, npcUnit, loc; - let wp, useWp = false; + let npc, npcUnit, loc; + let wp, useWp = false; - switch (act) { - case 1: - npc = "Warriv"; - loc = sdk.areas.RogueEncampment; + switch (act) { + case 1: + npc = "Warriv"; + loc = sdk.areas.RogueEncampment; - break; - case 2: - loc = sdk.areas.LutGholein; - npc = me.act === 1 ? "Warriv" : "Meshif"; + break; + case 2: + loc = sdk.areas.LutGholein; + npc = me.act === 1 ? "Warriv" : "Meshif"; - break; - case 3: - npc = "Meshif"; - loc = sdk.areas.KurastDocktown; + break; + case 3: + npc = "Meshif"; + loc = sdk.areas.KurastDocktown; - break; - case 5: - npc = "Tyrael"; - loc = sdk.areas.Harrogath; + break; + case 5: + npc = "Tyrael"; + loc = sdk.areas.Harrogath; - break; - } + break; + } - !me.inTown && Town.goToTown(); + !me.inTown && Town.goToTown(); - if (npc) { - npcUnit = Game.getNPC(NPC[npc]); - wp = Game.getObject("waypoint"); + if (npc) { + npcUnit = Game.getNPC(NPC[npc]); + wp = Game.getObject("waypoint"); - if (Pather.accessToAct(act) + if (Pather.accessToAct(act) && ((wp && !npcUnit) || (wp && npcUnit && getDistance(me, wp) < getDistance(me, npcUnit)) || (Town.getDistance("waypoint") < Town.getDistance(NPC[npc])))) { - useWp = true; - } - } else { - useWp = true; - } - - if (!npcUnit && !useWp) { - let timeout = getTickCount() + 3000; - - while (!npcUnit) { - if (timeout < getTickCount()) { - break; - } - - Town.move(NPC[npc]); - Packet.flash(me.gid); - delay(me.ping * 2 + 100); - npcUnit = Game.getNPC(NPC[npc]); - } - } - - if (npcUnit && !useWp) { - getDistance(me, npcUnit) > 5 && Town.move(NPC[npc]); - - for (let i = 0; i < 5; i += 1) { - sendPacket(1, sdk.packets.send.EntityAction, 4, 0, 4, npcUnit.gid, 4, loc); - delay(500 + me.ping); - - if (me.act === act) { - break; - } - } - } else if (useWp) { - Town.goToTown(act); - } else { - print("Failed to move to " + npc); - me.overhead("Failed to move to " + npc); - } - - while (!me.gameReady) { - delay(100); - } - - return me.act === act; + useWp = true; + } + } else { + useWp = true; + } + + if (!npcUnit && !useWp) { + let timeout = getTickCount() + 3000; + + while (!npcUnit) { + if (timeout < getTickCount()) { + break; + } + + Town.move(NPC[npc]); + Packet.flash(me.gid); + delay(me.ping * 2 + 100); + npcUnit = Game.getNPC(NPC[npc]); + } + } + + if (npcUnit && !useWp) { + getDistance(me, npcUnit) > 5 && Town.move(NPC[npc]); + + for (let i = 0; i < 5; i += 1) { + sendPacket(1, sdk.packets.send.EntityAction, 4, 0, 4, npcUnit.gid, 4, loc); + delay(500 + me.ping); + + if (me.act === act) { + break; + } + } + } else if (useWp) { + Town.goToTown(act); + } else { + print("Failed to move to " + npc); + me.overhead("Failed to move to " + npc); + } + + while (!me.gameReady) { + delay(100); + } + + return me.act === act; }; Pather.getWP = function (area, clearPath) { - area !== me.area && this.journeyTo(area); + area !== me.area && this.journeyTo(area); - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - if (preset) { - Skill.haveTK ? this.moveNearUnit(preset, 20, {clearSettings: {clearPath: clearPath}}) : this.moveToUnit(preset, 0, 0, clearPath); + if (preset) { + Skill.haveTK ? this.moveNearUnit(preset, 20, { clearSettings: { clearPath: clearPath } }) : this.moveToUnit(preset, 0, 0, clearPath); - let wp = Game.getObject("waypoint"); + let wp = Game.getObject("waypoint"); - if (wp) { - for (let j = 0; j < 10; j++) { - if (!getUIFlag(sdk.uiflags.Waypoint)) { - if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { - wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); - } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { - this.moveToUnit(wp) && Misc.click(0, 0, wp); - } - } + if (wp) { + for (let j = 0; j < 10; j++) { + if (!getUIFlag(sdk.uiflags.Waypoint)) { + if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { + wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { + this.moveToUnit(wp) && Misc.click(0, 0, wp); + } + } - if (getUIFlag(sdk.uiflags.Waypoint)) { - delay(500); + if (getUIFlag(sdk.uiflags.Waypoint)) { + delay(500); - // Keep wp menu open in town - !me.inTown && me.cancel(); + // Keep wp menu open in town + !me.inTown && me.cancel(); - return true; - } + return true; + } - delay(500); - } - } - } - } + delay(500); + } + } + } + } - return false; + return false; }; Pather.walkTo = function (x = undefined, y = undefined, minDist = undefined) { - while (!me.gameReady) { - delay(100); - } - - if (!x || !y) return false; - minDist === undefined && (minDist = me.inTown ? 2 : 4); - - let angle, angles, nTimer, whereToClick; - let nFail = 0; - let attemptCount = 0; - - // credit @Jaenster - // Stamina handler and Charge - if (!me.inTown && !me.dead) { - // Check if I have a stamina potion and use it if I do - if (me.staminaPercent <= 20) { - let stam = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((i) => i.classid === sdk.items.StaminaPotion && i.isInInventory).first(); - !!stam && !me.deadOrInSequence && stam.use(); - } - (me.running && me.staminaPercent <= 15) && me.walk(); - // the less stamina you have, the more you wait to recover - let recover = me.staminaMaxDuration < 30 ? 80 : 50; - (me.walking && me.staminaPercent >= recover) && me.run(); - if (Skill.canUse(sdk.skills.Charge) && me.mp >= 9 && getDistance(me.x, me.y, x, y) > 8 && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { - if (Skill.canUse(sdk.skills.Vigor)) { - Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); - } else if (!Config.Vigor && !Attack.auradin && Skill.canUse(sdk.skills.HolyFreeze)) { - // Useful in classic to keep mobs cold while you rush them - Skill.setSkill(sdk.skills.HolyFreeze, sdk.skills.hand.Right); - } - Misc.click(0, 1, x, y); - while (!me.idle) { - delay(40); - } - } - } - - (me.inTown && me.walking) && me.run(); - - while (getDistance(me.x, me.y, x, y) > minDist && !me.dead && !Pather.stop) { - if (me.paladin) { - Skill.canUse(sdk.skills.Vigor) ? Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right) : Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - } - - if (this.openDoors(x, y) && getDistance(me.x, me.y, x, y) <= minDist) { - return true; - } - - Misc.click(0, 0, x, y); - - attemptCount += 1; - nTimer = getTickCount(); - - while (!me.moving) { - if (me.dead || Pather.stop) return false; - - if ((getTickCount() - nTimer) > 500) { - if (nFail >= 3) return false; - - nFail += 1; - angle = Math.atan2(me.y - y, me.x - x); - angles = [Math.PI / 2, -Math.PI / 2]; - - for (let i = 0; i < angles.length; i += 1) { - // TODO: might need rework into getnearestwalkable - whereToClick = { - x: Math.round(Math.cos(angle + angles[i]) * 5 + me.x), - y: Math.round(Math.sin(angle + angles[i]) * 5 + me.y) - }; - - if (Attack.validSpot(whereToClick.x, whereToClick.y)) { - Misc.click(0, 0, whereToClick.x, whereToClick.y); - - let tick = getTickCount(); - - while (getDistance(me, whereToClick) > 2 && getTickCount() - tick < 1000) { - delay(40); - } - - break; - } - } - - break; - } - - delay(10); - } - - attemptCount > 1 && this.kickBarrels(x, y); - - // Wait until we're done walking - idle or dead - while (getDistance(me.x, me.y, x, y) > minDist && !me.idle) { - delay(10); - } - - if (attemptCount >= 3) return false; - } - - return !me.dead && getDistance(me.x, me.y, x, y) <= minDist; + while (!me.gameReady) { + delay(100); + } + + if (!x || !y) return false; + minDist === undefined && (minDist = me.inTown ? 2 : 4); + + let angle, angles, nTimer, whereToClick; + let nFail = 0; + let attemptCount = 0; + + // credit @Jaenster + // Stamina handler and Charge + if (!me.inTown && !me.dead) { + // Check if I have a stamina potion and use it if I do + if (me.staminaPercent <= 20) { + let stam = me.getItemsEx(-1, sdk.items.mode.inStorage).filter((i) => i.classid === sdk.items.StaminaPotion && i.isInInventory).first(); + !!stam && !me.deadOrInSequence && stam.use(); + } + (me.running && me.staminaPercent <= 15) && me.walk(); + // the less stamina you have, the more you wait to recover + let recover = me.staminaMaxDuration < 30 ? 80 : 50; + (me.walking && me.staminaPercent >= recover) && me.run(); + if (Skill.canUse(sdk.skills.Charge) && me.mp >= 9 && getDistance(me.x, me.y, x, y) > 8 && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { + if (Skill.canUse(sdk.skills.Vigor)) { + Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); + } else if (!Config.Vigor && !Attack.auradin && Skill.canUse(sdk.skills.HolyFreeze)) { + // Useful in classic to keep mobs cold while you rush them + Skill.setSkill(sdk.skills.HolyFreeze, sdk.skills.hand.Right); + } + Misc.click(0, 1, x, y); + while (!me.idle) { + delay(40); + } + } + } + + (me.inTown && me.walking) && me.run(); + + while (getDistance(me.x, me.y, x, y) > minDist && !me.dead && !Pather.stop) { + if (me.paladin) { + Skill.canUse(sdk.skills.Vigor) ? Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right) : Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + } + + if (this.openDoors(x, y) && getDistance(me.x, me.y, x, y) <= minDist) { + return true; + } + + Misc.click(0, 0, x, y); + + attemptCount += 1; + nTimer = getTickCount(); + + while (!me.moving) { + if (me.dead || Pather.stop) return false; + + if ((getTickCount() - nTimer) > 500) { + if (nFail >= 3) return false; + + nFail += 1; + angle = Math.atan2(me.y - y, me.x - x); + angles = [Math.PI / 2, -Math.PI / 2]; + + for (let i = 0; i < angles.length; i += 1) { + // TODO: might need rework into getnearestwalkable + whereToClick = { + x: Math.round(Math.cos(angle + angles[i]) * 5 + me.x), + y: Math.round(Math.sin(angle + angles[i]) * 5 + me.y) + }; + + if (Attack.validSpot(whereToClick.x, whereToClick.y)) { + Misc.click(0, 0, whereToClick.x, whereToClick.y); + + let tick = getTickCount(); + + while (getDistance(me, whereToClick) > 2 && getTickCount() - tick < 1000) { + delay(40); + } + + break; + } + } + + break; + } + + delay(10); + } + + attemptCount > 1 && this.kickBarrels(x, y); + + // Wait until we're done walking - idle or dead + while (getDistance(me.x, me.y, x, y) > minDist && !me.idle) { + delay(10); + } + + if (attemptCount >= 3) return false; + } + + return !me.dead && getDistance(me.x, me.y, x, y) <= minDist; }; Pather.teleportTo = function (x, y, maxRange = 5) { - for (let i = 0; i < 3; i++) { - Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); - let tick = getTickCount(); - let pingDelay = i === 0 ? 150 : me.getPingDelay(); + for (let i = 0; i < 3; i++) { + Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); + let tick = getTickCount(); + let pingDelay = i === 0 ? 150 : me.getPingDelay(); - while (getTickCount() - tick < Math.max(500, pingDelay * 2 + 200)) { - if ([x, y].distance < maxRange || Pather.stop) { - return true; - } + while (getTickCount() - tick < Math.max(500, pingDelay * 2 + 200)) { + if ([x, y].distance < maxRange || Pather.stop) { + return true; + } - delay(10); - } - } + delay(10); + } + } - return false; + return false; }; Pather.moveTo = function (x, y, retry, clearPath, pop) { - // Abort if dead - if (me.dead) return false; - - if (!x || !y) return false; // I don't think this is a fatal error so just return false - if (typeof x !== "number" || typeof y !== "number") throw new Error("moveTo: Coords must be numbers"); - if ([x, y].distance < 2) return true; - - for (let i = 0; i < this.cancelFlags.length; i += 1) { - getUIFlag(this.cancelFlags[i]) && me.cancel(); - } - - let fail = 0; - let node = {x: x, y: y}; - let cleared = false; - let leaped = false; - let invalidCheck = false; - let useTeleport = this.useTeleport(); - let tpMana = Skill.getManaCost(sdk.skills.Teleport); - let preSkill = me.getSkill(sdk.skills.get.RightId); - let annoyingArea = [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3].includes(me.area); - let clearSettings = { - clearPath: (!!clearPath || !useTeleport), // walking characters need to clear in front of them - range: 10, - specType: (typeof clearPath === "number" ? clearPath : 0), - }; - - retry === undefined && (retry = useTeleport ? 3 : 15); - clearPath === undefined && (clearPath = Config.AttackSkill.some(skillId => skillId > 0) && !useTeleport ? true : false); - pop === undefined && (pop = false); - let path = getPath(me.area, x, y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? (annoyingArea ? 30 : this.teleDistance) : this.walkDistance); - if (!path) throw new Error("moveTo: Failed to generate path."); - - path.reverse(); - 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 || Pather.stop) { - Pather.stop = false; // Reset value + // Abort if dead + if (me.dead) return false; + + if (!x || !y) return false; // I don't think this is a fatal error so just return false + if (typeof x !== "number" || typeof y !== "number") throw new Error("moveTo: Coords must be numbers"); + if ([x, y].distance < 2) return true; + + for (let i = 0; i < this.cancelFlags.length; i += 1) { + getUIFlag(this.cancelFlags[i]) && me.cancel(); + } + + let fail = 0; + let node = { x: x, y: y }; + let cleared = false; + let leaped = false; + let invalidCheck = false; + let useTeleport = this.useTeleport(); + let tpMana = Skill.getManaCost(sdk.skills.Teleport); + let preSkill = me.getSkill(sdk.skills.get.RightId); + let annoyingArea = [sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3].includes(me.area); + let clearSettings = { + clearPath: (!!clearPath || !useTeleport), // walking characters need to clear in front of them + range: 10, + specType: (typeof clearPath === "number" ? clearPath : 0), + }; + + retry === undefined && (retry = useTeleport ? 3 : 15); + clearPath === undefined && (clearPath = Config.AttackSkill.some(skillId => skillId > 0) && !useTeleport ? true : false); + pop === undefined && (pop = false); + let path = getPath(me.area, x, y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? (annoyingArea ? 30 : this.teleDistance) : this.walkDistance); + if (!path) throw new Error("moveTo: Failed to generate path."); + + path.reverse(); + 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 || Pather.stop) { + Pather.stop = false; // Reset value - return false; - } + return false; + } - for (let i = 0; i < this.cancelFlags.length; i++) { - getUIFlag(this.cancelFlags[i]) && me.cancel(); - } + for (let i = 0; i < this.cancelFlags.length; i++) { + getUIFlag(this.cancelFlags[i]) && me.cancel(); + } - node = path.shift(); + node = path.shift(); - /* Right now getPath's first node is our own position so it's not necessary to take it into account + /* Right now getPath's first node is our own position so it's not necessary to take it into account This will be removed if getPath changes */ - if (getDistance(me, node) > 2) { - 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 = this.getNearestWalkable(node.x, node.y, 15, 3, sdk.collision.BlockWalk); - - if (adjustedNode) { - node.x = adjustedNode[0]; - node.y = adjustedNode[1]; - invalidCheck && (invalidCheck = false); - } - - if (annoyingArea) { - clearSettings.overrideConfig = true; - clearSettings.range = 5; - } - - retry <= 3 && !useTeleport && (retry = 15); - } - - if (useTeleport && tpMana < me.mp ? Pather.teleportTo(node.x, node.y) : Pather.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { - if (Pather.stop) { - continue; // stops on next interation - } + if (getDistance(me, node) > 2) { + 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 = this.getNearestWalkable(node.x, node.y, 15, 3, sdk.collision.BlockWalk); + + if (adjustedNode) { + node.x = adjustedNode[0]; + node.y = adjustedNode[1]; + invalidCheck && (invalidCheck = false); + } + + if (annoyingArea) { + clearSettings.overrideConfig = true; + clearSettings.range = 5; + } + + retry <= 3 && !useTeleport && (retry = 15); + } + + if (useTeleport && tpMana < me.mp ? Pather.teleportTo(node.x, node.y) : Pather.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { + if (Pather.stop) { + continue; // stops on next interation + } - if (!me.inTown) { - if (this.recursion) { - this.recursion = false; - try { - NodeAction.go(clearSettings); - - if (getDistance(me, node.x, node.y) > 5) { - this.moveTo(node.x, node.y); - } - } finally { - this.recursion = true; - } - } - - Misc.townCheck(); - } - } else { - if (Pather.stop) { - continue; // stops on next interation - } - - if (!me.inTown) { - if (!useTeleport && ((me.checkForMobs({range: 10}) && Attack.clear(8)) || Pather.kickBarrels(node.x, node.y) || Pather.openDoors(node.x, node.y))) { - continue; - } - - if (fail > 0 && (!useTeleport || tpMana > me.mp)) { - // Don't go berserk on longer paths - if (!cleared && me.checkForMobs({range: 6}) && Attack.clear(5)) { - cleared = true; - } - - // Only do this once - if (fail > 1 && !leaped && Skill.canUse(sdk.skills.LeapAttack) && Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { - leaped = true; - } - } - } - - // Reduce node distance in new path - path = getPath(me.area, x, y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? rand(25, 35) : rand(10, 15)); - if (!path) throw new Error("moveNear: Failed to generate path."); - - path.reverse(); - PathDebug.drawPath(path); - pop && path.pop(); - - if (fail > 0) { - console.debug("move retry " + fail); - Packet.flash(me.gid); - - if (fail >= retry) { - break; - } - } - fail++; - } - } - - delay(5); - } - - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot()); - me.getSkill(sdk.skills.get.RightId) !== preSkill && Skill.setSkill(preSkill, sdk.skills.hand.Right); - PathDebug.removeHooks(); - - return getDistance(me, node.x, node.y) < 5; + if (!me.inTown) { + if (this.recursion) { + this.recursion = false; + try { + NodeAction.go(clearSettings); + + if (getDistance(me, node.x, node.y) > 5) { + this.moveTo(node.x, node.y); + } + } finally { + this.recursion = true; + } + } + + Misc.townCheck(); + } + } else { + if (Pather.stop) { + continue; // stops on next interation + } + + if (!me.inTown) { + if (!useTeleport && ((me.checkForMobs({ range: 10 }) && Attack.clear(8)) || Pather.kickBarrels(node.x, node.y) || Pather.openDoors(node.x, node.y))) { + continue; + } + + if (fail > 0 && (!useTeleport || tpMana > me.mp)) { + // Don't go berserk on longer paths + if (!cleared && me.checkForMobs({ range: 6 }) && Attack.clear(5)) { + cleared = true; + } + + // Only do this once + if (fail > 1 && !leaped && Skill.canUse(sdk.skills.LeapAttack) && Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, node.x, node.y)) { + leaped = true; + } + } + } + + // Reduce node distance in new path + path = getPath(me.area, x, y, me.x, me.y, useTeleport ? 1 : 0, useTeleport ? rand(25, 35) : rand(10, 15)); + if (!path) throw new Error("moveNear: Failed to generate path."); + + path.reverse(); + PathDebug.drawPath(path); + pop && path.pop(); + + if (fail > 0) { + console.debug("move retry " + fail); + Packet.flash(me.gid); + + if (fail >= retry) { + break; + } + } + fail++; + } + } + + delay(5); + } + + useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot()); + me.getSkill(sdk.skills.get.RightId) !== preSkill && Skill.setSkill(preSkill, sdk.skills.hand.Right); + PathDebug.removeHooks(); + + return getDistance(me, node.x, node.y) < 5; }; diff --git a/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js index aa1935733..98c9bd702 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js @@ -8,28 +8,28 @@ includeIfNotIncluded("core/Pickit.js"); Pickit.basicPickItems = function () { - let itemList = []; - let item = Game.getItem(); + let itemList = []; + let item = Game.getItem(); - if (item) { - do { - if (item.onGroundOrDropping) { - itemList.push(copyUnit(item)); - } - } while (item.getNext()); - } + if (item) { + do { + if (item.onGroundOrDropping) { + itemList.push(copyUnit(item)); + } + } while (item.getNext()); + } - while (itemList.length > 0) { - itemList.sort(this.sortFastPickItems); - item = copyUnit(itemList.shift()); + while (itemList.length > 0) { + itemList.sort(this.sortFastPickItems); + item = copyUnit(itemList.shift()); - // Check if the item unit is still valid - if (item.x !== undefined) { - if (this.canPick(item) && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType))) { - this.pickItem(item); - } - } - } + // Check if the item unit is still valid + if (item.x !== undefined) { + if (this.canPick(item) && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType))) { + this.pickItem(item); + } + } + } - return true; + return true; }; diff --git a/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js b/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js index e4c0c732a..306a1e88b 100644 --- a/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js @@ -8,41 +8,44 @@ includeIfNotIncluded("core/Town.js"); Town.stash = function (stashGold = true) { - me.cancel(); - - let items = me.getItemsEx() - .filter(function (item) { - return item.isInInventory && !(item.isEquippedCharm && (item.unique || Storage.Inventory.IsLocked(item, Config.Inventory))); - }) - .sort(function (a, b) { - if ((a.itemType >= sdk.items.type.Amethyst && a.itemType <= sdk.items.type.Skull) || a.itemType === sdk.items.type.Rune || a.unique) { - return -1; - } - - if ((b.itemType >= sdk.items.type.Amethyst && b.itemType <= sdk.items.type.Skull) || b.itemType === sdk.items.type.Rune || b.unique) { - return 1; - } - - return a.quality - b.quality; - }); - - if (items) { - for (let i = 0; i < items.length; i++) { - if (this.canStash(items[i])) { - Item.logger("Stashed", items[i]); - Storage.Stash.MoveTo(items[i]); - } - } - } - - // 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; + me.cancel(); + + let items = me.getItemsEx() + .filter(function (item) { + return item.isInInventory + && !(item.isEquippedCharm && (item.unique || Storage.Inventory.IsLocked(item, Config.Inventory))); + }) + .sort(function (a, b) { + if ((a.itemType >= sdk.items.type.Amethyst + && a.itemType <= sdk.items.type.Skull) || a.itemType === sdk.items.type.Rune || a.unique) { + return -1; + } + + if ((b.itemType >= sdk.items.type.Amethyst + && b.itemType <= sdk.items.type.Skull) || b.itemType === sdk.items.type.Rune || b.unique) { + return 1; + } + + return a.quality - b.quality; + }); + + if (items) { + for (let i = 0; i < items.length; i++) { + if (this.canStash(items[i])) { + Item.logger("Stashed", items[i]); + Storage.Stash.MoveTo(items[i]); + } + } + } + + // 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; }; diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index d519b6a56..02a681b0e 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -21,402 +21,405 @@ include("manualplay/MapMode.js"); MapMode.include(); function main() { - // getUnit test - getUnit(-1) === null && console.warn("getUnit bug detected"); + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); - console.log("ÿc9MapHelper loaded"); - - let obj = {type: false, dest: false, action: false}; - let action, fail = 0, x, y; - let mapThread = getScript("libs/manualplay/threads/mapthread.js"); - - const portalMap = {}; - portalMap[sdk.areas.Abaddon] = { - 14: [12638, 6373], - 15: [12638, 6063], - 20: [12708, 6063], - 25: [12948, 6128], - }; - portalMap[sdk.areas.PitofAcheron] = { - 14: [12638, 7873], - 15: [12638, 7563], - 20: [12708, 7563], - 25: [12948, 7628], - }; - portalMap[sdk.areas.InfernalPit] = { - 14: [12638, 9373], - 20: [12708, 9063], - 25: [12948, 9128], - }; - - Config.init(); - Attack.init(true); - Pickit.init(); - Storage.Init(); - addEventListener("scriptmsg", function (msg) { - action = msg; - }); - - this.togglePickThread = function () { - if (!Config.ManualPlayPick) return; - - const pickThread = getScript("threads/pickthread.js"); - - if (pickThread) { - if (pickThread.running) { - pickThread.pause(); - } else if (!pickThread.running) { - pickThread.resume(); - } - } - }; - - this.togglePause = function () { - if (mapThread) { - if (mapThread.running) { - print("pause mapthread"); - mapThread.pause(); - } else if (!mapThread.running) { - print("resume mapthread"); - mapThread.resume(); - - if (!mapThread.running) { - fail++; - - if (fail % 5 === 0 && !getScript("libs/manualplay/threads/mapthread.js")) { - print("MapThread shut down, exiting MapHelper"); + console.log("ÿc9MapHelper loaded"); + + let obj = { type: false, dest: false, action: false }; + let action, fail = 0, x, y; + let mapThread = getScript("libs/manualplay/threads/mapthread.js"); + + const portalMap = {}; + portalMap[sdk.areas.Abaddon] = { + 14: [12638, 6373], + 15: [12638, 6063], + 20: [12708, 6063], + 25: [12948, 6128], + }; + portalMap[sdk.areas.PitofAcheron] = { + 14: [12638, 7873], + 15: [12638, 7563], + 20: [12708, 7563], + 25: [12948, 7628], + }; + portalMap[sdk.areas.InfernalPit] = { + 14: [12638, 9373], + 20: [12708, 9063], + 25: [12948, 9128], + }; + + Config.init(); + Attack.init(true); + Pickit.init(); + Storage.Init(); + addEventListener("scriptmsg", function (msg) { + action = msg; + }); + + this.togglePickThread = function () { + if (!Config.ManualPlayPick) return; + + const pickThread = getScript("threads/pickthread.js"); + + if (pickThread) { + if (pickThread.running) { + pickThread.pause(); + } else if (!pickThread.running) { + pickThread.resume(); + } + } + }; + + this.togglePause = function () { + if (mapThread) { + if (mapThread.running) { + print("pause mapthread"); + mapThread.pause(); + } else if (!mapThread.running) { + print("resume mapthread"); + mapThread.resume(); + + if (!mapThread.running) { + fail++; + + if (fail % 5 === 0 && !getScript("libs/manualplay/threads/mapthread.js")) { + print("MapThread shut down, exiting MapHelper"); - return false; - } - } - } - } else if (!getScript("libs/manualplay/threads/mapthread.js")) { - print("MapThread shut down, exiting MapHelper"); - - return false; - } - - return true; - }; - - while (true) { - if (getUIFlag(sdk.uiflags.EscMenu)) { - delay(100); - mapThread.running && this.togglePause(); - } else { - if (!mapThread.running) { - if (!this.togglePause()) { - return; - } - } - } - - if (action) { - try { - let temp = JSON.parse(action); - temp && Object.assign(obj, temp); + return false; + } + } + } + } else if (!getScript("libs/manualplay/threads/mapthread.js")) { + print("MapThread shut down, exiting MapHelper"); + + return false; + } + + return true; + }; + + while (true) { + if (getUIFlag(sdk.uiflags.EscMenu)) { + delay(100); + mapThread.running && this.togglePause(); + } else { + if (!mapThread.running) { + if (!this.togglePause()) { + return; + } + } + } + + if (action) { + try { + let temp = JSON.parse(action); + temp && Object.assign(obj, temp); - addEventListener("keyup", Pather.stopEvent); - this.togglePickThread(); - - if (obj) { - let redPortal, chestLoc, king, unit; - - switch (obj.type) { - case "area": - if (obj.dest === sdk.areas.ArreatSummit) { - Pather.moveToExit(obj.dest, false); - } else if ([sdk.areas.CanyonofMagic, sdk.areas.A2SewersLvl1, sdk.areas.PalaceCellarLvl3, sdk.areas.PandemoniumFortress, sdk.areas.BloodyFoothills].includes(obj.dest)) { - Pather.journeyTo(obj.dest); - } else if (obj.dest === sdk.areas.DurielsLair) { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder, -11, 3); - - for (let i = 0; i < 3; i++) { - if (Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair)) { - break; - } - } - } else { - Pather.moveToExit(obj.dest, true); - } - - break; - case "unit": - if (me.inArea(sdk.areas.MooMooFarm) + addEventListener("keyup", Pather.stopEvent); + this.togglePickThread(); + + if (obj) { + let redPortal, chestLoc, king, unit; + + switch (obj.type) { + case "area": + if (obj.dest === sdk.areas.ArreatSummit) { + Pather.moveToExit(obj.dest, false); + } else if ([ + sdk.areas.CanyonofMagic, sdk.areas.A2SewersLvl1, + sdk.areas.PalaceCellarLvl3, sdk.areas.PandemoniumFortress, sdk.areas.BloodyFoothills + ].includes(obj.dest)) { + Pather.journeyTo(obj.dest); + } else if (obj.dest === sdk.areas.DurielsLair) { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder, -11, 3); + + for (let i = 0; i < 3; i++) { + if (Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair)) { + break; + } + } + } else { + Pather.moveToExit(obj.dest, true); + } + + break; + case "unit": + if (me.inArea(sdk.areas.MooMooFarm) || (me.inArea(sdk.areas.DurielsLair) && Misc.talkToTyrael())) { - break; - } - - Pather.moveToUnit(obj.dest, true); - - switch (me.area) { - case sdk.areas.ColdPlains: - Pather.moveToExit(sdk.areas.CaveLvl1, true); - - break; - case sdk.areas.BlackMarsh: - Pather.moveToExit(sdk.areas.HoleLvl1, true); - - break; - case sdk.areas.LutGholein: - Pather.useUnit(sdk.unittype.Stairs, sdk.exits.preset.A2EnterSewersDoor, sdk.areas.A2SewersLvl1); - - break; - case sdk.areas.KurastBazaar: - Pather.useUnit(sdk.unittype.Stairs, sdk.exits.preset.A3EnterSewers, sdk.areas.A3SewersLvl1); - - break; - } - - if (obj.action && typeof obj.action === "object") { - if (obj.action.do === "openChest") { - !!obj.action.id && Misc.openChest(obj.action.id); - } else if (obj.action.do === "usePortal") { - !!obj.action.id ? Pather.usePortal(obj.action.id) : Pather.usePortal(); - } - } - - break; - case "wp": - Pather.getWP(me.area); - - break; - case "actChange": - print("Going to act: " + obj.dest); - Pather.changeAct(obj.dest); - - break; - case "portal": - if (obj.dest === sdk.areas.WorldstoneChamber && Game.getMonster(sdk.monsters.ThroneBaal)) { - me.overhead("Can't enter Worldstone Chamber yet. Baal still in area"); + break; + } + + Pather.moveToUnit(obj.dest, true); + + switch (me.area) { + case sdk.areas.ColdPlains: + Pather.moveToExit(sdk.areas.CaveLvl1, true); + + break; + case sdk.areas.BlackMarsh: + Pather.moveToExit(sdk.areas.HoleLvl1, true); + + break; + case sdk.areas.LutGholein: + Pather.useUnit(sdk.unittype.Stairs, sdk.exits.preset.A2EnterSewersDoor, sdk.areas.A2SewersLvl1); + + break; + case sdk.areas.KurastBazaar: + Pather.useUnit(sdk.unittype.Stairs, sdk.exits.preset.A3EnterSewers, sdk.areas.A3SewersLvl1); + + break; + } + + if (obj.action && typeof obj.action === "object") { + if (obj.action.do === "openChest") { + !!obj.action.id && Misc.openChest(obj.action.id); + } else if (obj.action.do === "usePortal") { + !!obj.action.id ? Pather.usePortal(obj.action.id) : Pather.usePortal(); + } + } + + break; + case "wp": + Pather.getWP(me.area); + + break; + case "actChange": + print("Going to act: " + obj.dest); + Pather.changeAct(obj.dest); + + break; + case "portal": + if (obj.dest === sdk.areas.WorldstoneChamber && Game.getMonster(sdk.monsters.ThroneBaal)) { + me.overhead("Can't enter Worldstone Chamber yet. Baal still in area"); - break; - } else if (obj.dest === sdk.areas.WorldstoneChamber && !Game.getMonster(sdk.monsters.ThroneBaal)) { - redPortal = Game.getObject(sdk.objects.WorldstonePortal); - redPortal && Pather.usePortal(null, null, redPortal); - - break; - } - - switch (obj.dest) { - case sdk.areas.RogueEncampment: - king = Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing); - - switch (king.x) { - case 1: - Pather.moveTo(25183, 5923); - - break; - } - - break; - case sdk.areas.StonyField: - Pather.moveTo(25173, 5086); - redPortal = Pather.getPortal(obj.dest); - - break; - case sdk.areas.MooMooFarm: - redPortal = Pather.getPortal(obj.dest); - - break; - case sdk.areas.ArcaneSanctuary: - Pather.moveTo(12692, 5195); - redPortal = Pather.getPortal(obj.dest); - !redPortal && Pather.useWaypoint(obj.dest); - - break; - case sdk.areas.Harrogath: - Pather.moveTo(20193, 8693); - - break; - case sdk.areas.FrigidHighlands: - case sdk.areas.ArreatPlateau: - case sdk.areas.FrozenTundra: - chestLoc = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - - if (!chestLoc) { - break; - } - - [x, y] = portalMap[me.area][chestLoc.x]; - - Pather.moveTo(x, y); - Pather.usePortal(); - - break; - case sdk.areas.MatronsDen: - case sdk.areas.ForgottenSands: - case sdk.areas.FurnaceofPain: - case sdk.areas.UberTristram: - redPortal = Pather.getPortal(obj.dest); - - break; - default: - Pather.usePortal(obj.dest); + break; + } else if (obj.dest === sdk.areas.WorldstoneChamber && !Game.getMonster(sdk.monsters.ThroneBaal)) { + redPortal = Game.getObject(sdk.objects.WorldstonePortal); + redPortal && Pather.usePortal(null, null, redPortal); + + break; + } + + switch (obj.dest) { + case sdk.areas.RogueEncampment: + king = Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing); + + switch (king.x) { + case 1: + Pather.moveTo(25183, 5923); + + break; + } + + break; + case sdk.areas.StonyField: + Pather.moveTo(25173, 5086); + redPortal = Pather.getPortal(obj.dest); + + break; + case sdk.areas.MooMooFarm: + redPortal = Pather.getPortal(obj.dest); + + break; + case sdk.areas.ArcaneSanctuary: + Pather.moveTo(12692, 5195); + redPortal = Pather.getPortal(obj.dest); + !redPortal && Pather.useWaypoint(obj.dest); + + break; + case sdk.areas.Harrogath: + Pather.moveTo(20193, 8693); + + break; + case sdk.areas.FrigidHighlands: + case sdk.areas.ArreatPlateau: + case sdk.areas.FrozenTundra: + chestLoc = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); + + if (!chestLoc) { + break; + } + + [x, y] = portalMap[me.area][chestLoc.x]; + + Pather.moveTo(x, y); + Pather.usePortal(); + + break; + case sdk.areas.MatronsDen: + case sdk.areas.ForgottenSands: + case sdk.areas.FurnaceofPain: + case sdk.areas.UberTristram: + redPortal = Pather.getPortal(obj.dest); + + break; + default: + Pather.usePortal(obj.dest); - break; - } - - if (redPortal) { - Pather.moveToUnit(redPortal); - Pather.usePortal(null, null, redPortal); - } - - break; - case "qol": - switch (obj.action) { - case "heal": - Town.initNPC("Heal", "heal"); - - break; - case "openStash": - Town.openStash(); - - break; - case "stashItems": - Town.stash(true, true); - - break; - case "makePortal": - Pather.makePortal(); - - break; - case "takePortal": - Town.goToTown(); - - break; - case "clear": - Attack.clear(10); - - break; - case "cowportal": - Misc.openRedPortal(sdk.areas.MooMooFarm); - - break; - case "ubertrist": - Misc.openRedPortal(sdk.areas.UberTristram); - - break; - case "uberportal": - Misc.openRedPortal(); - - break; - case "filltps": - Town.fillTome(sdk.items.TomeofTownPortal); - me.cancel(); - - break; - case "moveItemFromInvoToStash": - case "moveItemFromStashToInvo": - unit = Game.getSelectedUnit(); - - switch (unit.location) { - case sdk.storage.Inventory: - Storage.Stash.CanFit(unit) && Storage.Stash.MoveTo(unit); - - break; - case sdk.storage.Stash: - Storage.Inventory.CanFit(unit) && Storage.Inventory.MoveTo(unit); - - break; - } - - break; - case "moveItemFromInvoToCube": - case "moveItemFromCubeToInvo": - unit = Game.getSelectedUnit(); - - switch (unit.location) { - case sdk.storage.Inventory: - Storage.Cube.CanFit(unit) && Storage.Cube.MoveTo(unit); - - break; - case sdk.storage.Cube: - Storage.Inventory.CanFit(unit) && Storage.Inventory.MoveTo(unit); - - break; - } - - break; - case "moveItemFromInvoToTrade": - case "moveItemFromTradeToInvo": - unit = Game.getSelectedUnit(); - - switch (unit.location) { - case sdk.storage.Inventory: - Storage.TradeScreen.CanFit(unit) && Storage.TradeScreen.MoveTo(unit); - - break; - case sdk.storage.TradeWindow: - if (Storage.Inventory.CanFit(unit)) { - Packet.itemToCursor(unit); - Storage.Inventory.MoveTo(unit); - } - - break; - } - - break; - case "pick": - Config.ManualPlayPick ? Pickit.pickItems() : Pickit.basicPickItems(); - - break; - case "sellItem": - unit = Game.getSelectedUnit(); - - if (unit.isInInventory && unit.sellable) { - try { - unit.sell(); - } catch (e) { - console.error(e); - } - } - - break; - } - - break; - case "drop": - switch (obj.action) { - case "invo": - Misc.dropItems(sdk.storage.Inventory); + break; + } + + if (redPortal) { + Pather.moveToUnit(redPortal); + Pather.usePortal(null, null, redPortal); + } + + break; + case "qol": + switch (obj.action) { + case "heal": + Town.initNPC("Heal", "heal"); + + break; + case "openStash": + Town.openStash(); + + break; + case "stashItems": + Town.stash(true, true); + + break; + case "makePortal": + Pather.makePortal(); + + break; + case "takePortal": + Town.goToTown(); + + break; + case "clear": + Attack.clear(10); + + break; + case "cowportal": + Misc.openRedPortal(sdk.areas.MooMooFarm); + + break; + case "ubertrist": + Misc.openRedPortal(sdk.areas.UberTristram); + + break; + case "uberportal": + Misc.openRedPortal(); + + break; + case "filltps": + Town.fillTome(sdk.items.TomeofTownPortal); + me.cancel(); + + break; + case "moveItemFromInvoToStash": + case "moveItemFromStashToInvo": + unit = Game.getSelectedUnit(); + + switch (unit.location) { + case sdk.storage.Inventory: + Storage.Stash.CanFit(unit) && Storage.Stash.MoveTo(unit); + + break; + case sdk.storage.Stash: + Storage.Inventory.CanFit(unit) && Storage.Inventory.MoveTo(unit); + + break; + } + + break; + case "moveItemFromInvoToCube": + case "moveItemFromCubeToInvo": + unit = Game.getSelectedUnit(); + + switch (unit.location) { + case sdk.storage.Inventory: + Storage.Cube.CanFit(unit) && Storage.Cube.MoveTo(unit); + + break; + case sdk.storage.Cube: + Storage.Inventory.CanFit(unit) && Storage.Inventory.MoveTo(unit); + + break; + } + + break; + case "moveItemFromInvoToTrade": + case "moveItemFromTradeToInvo": + unit = Game.getSelectedUnit(); + + switch (unit.location) { + case sdk.storage.Inventory: + Storage.TradeScreen.CanFit(unit) && Storage.TradeScreen.MoveTo(unit); + + break; + case sdk.storage.TradeWindow: + if (Storage.Inventory.CanFit(unit)) { + Packet.itemToCursor(unit); + Storage.Inventory.MoveTo(unit); + } + + break; + } + + break; + case "pick": + Config.ManualPlayPick ? Pickit.pickItems() : Pickit.basicPickItems(); + + break; + case "sellItem": + unit = Game.getSelectedUnit(); + + if (unit.isInInventory && unit.sellable) { + try { + unit.sell(); + } catch (e) { + console.error(e); + } + } + + break; + } + + break; + case "drop": + switch (obj.action) { + case "invo": + Misc.dropItems(sdk.storage.Inventory); - break; - case "stash": - Misc.dropItems(sdk.storage.Stash); - - break; - } - - break; - case "stack": - switch (obj.action) { - case "thawing": - Town.buyPots(10, "Thawing", true, true); + break; + case "stash": + Misc.dropItems(sdk.storage.Stash); + + break; + } + + break; + case "stack": + switch (obj.action) { + case "thawing": + Town.buyPots(10, "Thawing", true, true); - break; - case "antidote": - Town.buyPots(10, "Antidote", true, true); - - break; - case "stamina": - Town.buyPots(10, "Stamina", true, true); - - break; - } - - break; - } - } - } catch (e) { - console.error(e); - } finally { - action = false; - removeEventListener("keyup", Pather.stopEvent); - this.togglePickThread(); - } - } - - delay(20); - } + break; + case "antidote": + Town.buyPots(10, "Antidote", true, true); + + break; + case "stamina": + Town.buyPots(10, "Stamina", true, true); + + break; + } + + break; + } + } + } catch (e) { + console.error(e); + } finally { + action = false; + removeEventListener("keyup", Pather.stopEvent); + this.togglePickThread(); + } + } + + delay(20); + } } diff --git a/d2bs/kolbot/libs/manualplay/threads/MapThread.js b/d2bs/kolbot/libs/manualplay/threads/MapThread.js index 119297bfe..323a9bc99 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapThread.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename MapThread.js * @author theBGuy @@ -23,298 +24,298 @@ include("manualplay/MapMode.js"); MapMode.include(); const Hooks = { - dashBoard: {x: 113, y: 490}, - portalBoard: {x: 12, y: 432}, - qolBoard: {x: 545, y: 490}, - resfix: {x: (me.screensize ? 0 : -160), y: (me.screensize ? 0 : -120)}, - saidMessage: false, - userAddon: false, - enabled: true, - flushed: false, - - init: function () { - let files = dopen("libs/manualplay/hooks/").getFiles(); + dashBoard: { x: 113, y: 490 }, + portalBoard: { x: 12, y: 432 }, + qolBoard: { x: 545, y: 490 }, + resfix: { x: (me.screensize ? 0 : -160), y: (me.screensize ? 0 : -120) }, + saidMessage: false, + userAddon: false, + enabled: true, + flushed: false, + + init: function () { + let files = dopen("libs/manualplay/hooks/").getFiles(); - Array.isArray(files) && files - .filter(file => file.endsWith(".js")) - .forEach(function (x) { - if (!isIncluded("manualplay/hooks/" + x)) { - if (!include("manualplay/hooks/" + x)) { - throw new Error("Failed to include " + "manualplay/hooks/" + x); - } - } - }); - }, - - update: function () { - while (!me.gameReady) { - delay(100); - } - - if (!this.enabled) { - Hooks.enabled = getUIFlag(sdk.uiflags.AutoMap); - - return; - } - - ActionHooks.check(); - VectorHooks.check(); - MonsterHooks.check(); - ShrineHooks.check(); - ItemHooks.check(); - TextHooks.check(); - Hooks.flushed = false; - }, - - flush: function (flag) { - if (Hooks.flushed === flag) return true; - - if (flag === true) { - Hooks.enabled = false; - - MonsterHooks.flush(); - ShrineHooks.flush(); - TextHooks.flush(); - VectorHooks.flush(); - ActionHooks.flush(); - ItemHooks.flush(); - } else { - if (sdk.uiflags.Waypoint === flag) { - VectorHooks.flush(); - TextHooks.displaySettings = false; - TextHooks.check(); - } else if (sdk.uiflags.Inventory === flag && [sdk.uiflags.stash, sdk.uiflags.Cube, sdk.uiflags.TradePrompt].every((el) => !getUIFlag(el))) { - ItemHooks.flush(); - TextHooks.check(); - } else { - MonsterHooks.flush(); - ShrineHooks.flush(); - TextHooks.flush(); - VectorHooks.flush(); - ActionHooks.flush(); - ItemHooks.flush(); - } - } - - Hooks.flushed = flag; - - return true; - } + Array.isArray(files) && files + .filter(file => file.endsWith(".js")) + .forEach(function (x) { + if (!isIncluded("manualplay/hooks/" + x)) { + if (!include("manualplay/hooks/" + x)) { + throw new Error("Failed to include " + "manualplay/hooks/" + x); + } + } + }); + }, + + update: function () { + while (!me.gameReady) { + delay(100); + } + + if (!this.enabled) { + Hooks.enabled = getUIFlag(sdk.uiflags.AutoMap); + + return; + } + + ActionHooks.check(); + VectorHooks.check(); + MonsterHooks.check(); + ShrineHooks.check(); + ItemHooks.check(); + TextHooks.check(); + Hooks.flushed = false; + }, + + flush: function (flag) { + if (Hooks.flushed === flag) return true; + + if (flag === true) { + Hooks.enabled = false; + + MonsterHooks.flush(); + ShrineHooks.flush(); + TextHooks.flush(); + VectorHooks.flush(); + ActionHooks.flush(); + ItemHooks.flush(); + } else { + if (sdk.uiflags.Waypoint === flag) { + VectorHooks.flush(); + TextHooks.displaySettings = false; + TextHooks.check(); + } else if (sdk.uiflags.Inventory === flag && [sdk.uiflags.stash, sdk.uiflags.Cube, sdk.uiflags.TradePrompt].every((el) => !getUIFlag(el))) { + ItemHooks.flush(); + TextHooks.check(); + } else { + MonsterHooks.flush(); + ShrineHooks.flush(); + TextHooks.flush(); + VectorHooks.flush(); + ActionHooks.flush(); + ItemHooks.flush(); + } + } + + Hooks.flushed = flag; + + return true; + } }; 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"); - - console.log("ÿc9Map Thread Loaded."); - MapMode.include(); - Config.init(true); - LocalChat.init(); - Storage.Init(); - Pickit.init(true); - Hooks.init(); - - // load threads - me.automap = true; - load("libs/manualplay/threads/maphelper.js"); - load("libs/manualplay/threads/maptoolsthread.js"); - Config.ManualPlayPick && load("libs/manualplay/threads/pickthread.js"); - Config.PublicMode && load("threads/party.js"); - - const Worker = require("../../modules/Worker"); - const UnitInfo = new (require("../../modules/UnitInfo")); - - Worker.runInBackground.unitInfo = function () { - // always, maybe a timeout would be good though - UnitInfo.check(); - - // not being used atm - keep looping - if (!Hooks.userAddon) { - 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"); + + console.log("ÿc9Map Thread Loaded."); + MapMode.include(); + Config.init(true); + LocalChat.init(); + Storage.Init(); + Pickit.init(true); + Hooks.init(); + + // load threads + me.automap = true; + load("libs/manualplay/threads/maphelper.js"); + load("libs/manualplay/threads/maptoolsthread.js"); + Config.ManualPlayPick && load("libs/manualplay/threads/pickthread.js"); + Config.PublicMode && load("threads/party.js"); + + const Worker = require("../../modules/Worker"); + const UnitInfo = new (require("../../modules/UnitInfo")); + + Worker.runInBackground.unitInfo = function () { + // always, maybe a timeout would be good though + UnitInfo.check(); + + // not being used atm - keep looping + if (!Hooks.userAddon) { + return true; + } - UnitInfo.createInfo(Game.getSelectedUnit()); + UnitInfo.createInfo(Game.getSelectedUnit()); - return true; - }; + return true; + }; - const log = (msg = "") => { - me.overhead(msg); - console.log(msg); - }; + const log = (msg = "") => { + me.overhead(msg); + console.log(msg); + }; - if (Config.MapMode.UseOwnItemFilter) { - ItemHooks.pickitEnabled = true; - } + if (Config.MapMode.UseOwnItemFilter) { + ItemHooks.pickitEnabled = true; + } - const hideFlags = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, - sdk.uiflags.EscMenu, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Waypoint, sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, - sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen - ]; + const hideFlags = [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, + sdk.uiflags.EscMenu, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Waypoint, sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, + sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen + ]; - this.revealArea = function (area) { - !this.revealedAreas && (this.revealedAreas = []); + this.revealArea = function (area) { + !this.revealedAreas && (this.revealedAreas = []); - if (this.revealedAreas.indexOf(area) === -1) { - delay(500); + if (this.revealedAreas.indexOf(area) === -1) { + delay(500); - if (!getRoom()) { - return; - } + if (!getRoom()) { + return; + } - revealLevel(true); - this.revealedAreas.push(area); - } - }; - - // Run commands from chat - this.runCommand = function (msg) { - if (msg.length <= 1) return true; - - msg = msg.toLowerCase(); - let cmd = msg.split(" ")[0].split(".")[1]; - let msgList = msg.split(" "); - let qolObj = {type: "qol", dest: false, action: false}; - - switch (cmd) { - case "useraddon": - Hooks.userAddon = !Hooks.userAddon; - log("userAddon set to " + Hooks.userAddon); - - break; - case "me": - log("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); - - break; - case "stash": - me.inTown && (qolObj.action = "stashItems"); - - break; - case "pick": - case "cowportal": - case "uberportal": - case "filltps": - qolObj.action = cmd; - - break; - case "drop": - if (msgList.length < 2) { - print("ÿc1Missing arguments"); - break; - } - - qolObj.type = "drop"; - qolObj.action = msgList[1]; - - break; - case "stack": - if (msgList.length < 2) { - print("ÿc1Missing arguments"); - break; - } - - qolObj.type = "stack"; - qolObj.action = msgList[1]; - - break; - case "help": - if (HelpMenu.cleared) { - HelpMenu.showMenu(); - log("Click each command for more info"); - } - - break; - case "hide": - hideConsole(); - HelpMenu.hideMenu(); - TextHooks.displayTitle = false; - { - let tHook = TextHooks.getHook("title", TextHooks.hooks); - !!tHook && tHook.hook.remove(); - } - - break; - case "make": - { - let className = sdk.player.class.nameOf(me.classid); - if (!FileTools.exists("libs/manualplay/config/" + className + "." + me.name + ".js")) { - FileTools.copy("libs/manualplay/config/" + className + ".js", "libs/manualplay/config/" + className + "." + me.name + ".js"); - D2Bot.printToConsole("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); - log("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); - } - } - - break; - default: - print("ÿc1Invalid command : " + cmd); - - break; - } - - qolObj.action && Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(qolObj)); - - return true; - }; - - let onChatInput = (speaker, msg) => { - if (msg.length && msg[0] === ".") { - this.runCommand(msg); - - return true; - } - - return false; - }; - - addEventListener("chatinputblocker", onChatInput); - addEventListener("keyup", ActionHooks.event); - - while (true) { - while (!me.area || !me.gameReady) { - delay(100); - } - - let hideFlagFound = false; - - this.revealArea(me.area); + revealLevel(true); + this.revealedAreas.push(area); + } + }; + + // Run commands from chat + this.runCommand = function (msg) { + if (msg.length <= 1) return true; + + msg = msg.toLowerCase(); + let cmd = msg.split(" ")[0].split(".")[1]; + let msgList = msg.split(" "); + let qolObj = { type: "qol", dest: false, action: false }; + + switch (cmd) { + case "useraddon": + Hooks.userAddon = !Hooks.userAddon; + log("userAddon set to " + Hooks.userAddon); + + break; + case "me": + log("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); + + break; + case "stash": + me.inTown && (qolObj.action = "stashItems"); + + break; + case "pick": + case "cowportal": + case "uberportal": + case "filltps": + qolObj.action = cmd; + + break; + case "drop": + if (msgList.length < 2) { + print("ÿc1Missing arguments"); + break; + } + + qolObj.type = "drop"; + qolObj.action = msgList[1]; + + break; + case "stack": + if (msgList.length < 2) { + print("ÿc1Missing arguments"); + break; + } + + qolObj.type = "stack"; + qolObj.action = msgList[1]; + + break; + case "help": + if (HelpMenu.cleared) { + HelpMenu.showMenu(); + log("Click each command for more info"); + } + + break; + case "hide": + hideConsole(); + HelpMenu.hideMenu(); + TextHooks.displayTitle = false; + { + let tHook = TextHooks.getHook("title", TextHooks.hooks); + !!tHook && tHook.hook.remove(); + } + + break; + case "make": + { + let className = sdk.player.class.nameOf(me.classid); + if (!FileTools.exists("libs/manualplay/config/" + className + "." + me.name + ".js")) { + FileTools.copy("libs/manualplay/config/" + className + ".js", "libs/manualplay/config/" + className + "." + me.name + ".js"); + D2Bot.printToConsole("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); + log("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); + } + } + + break; + default: + print("ÿc1Invalid command : " + cmd); + + break; + } + + qolObj.action && Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(qolObj)); + + return true; + }; + + let onChatInput = (speaker, msg) => { + if (msg.length && msg[0] === ".") { + this.runCommand(msg); + + return true; + } + + return false; + }; + + addEventListener("chatinputblocker", onChatInput); + addEventListener("keyup", ActionHooks.event); + + while (true) { + while (!me.area || !me.gameReady) { + delay(100); + } + + let hideFlagFound = false; + + this.revealArea(me.area); - for (let i = 0; i < hideFlags.length; i++) { - if (getUIFlag(hideFlags[i])) { - Hooks.flush(hideFlags[i]); - ActionHooks.checkAction(); - hideFlagFound = true; - delay(100); + for (let i = 0; i < hideFlags.length; i++) { + if (getUIFlag(hideFlags[i])) { + Hooks.flush(hideFlags[i]); + ActionHooks.checkAction(); + hideFlagFound = true; + delay(100); - break; - } - } + break; + } + } - if (hideFlagFound) continue; + if (hideFlagFound) continue; - getUIFlag(sdk.uiflags.AutoMap) ? Hooks.update() : Hooks.flush(true) && (!HelpMenu.cleared && HelpMenu.hideMenu()); + getUIFlag(sdk.uiflags.AutoMap) ? Hooks.update() : Hooks.flush(true) && (!HelpMenu.cleared && HelpMenu.hideMenu()); - delay(20); + delay(20); - while (getUIFlag(sdk.uiflags.ShowItem)) { - ItemHooks.flush(); - } - } + while (getUIFlag(sdk.uiflags.ShowItem)) { + ItemHooks.flush(); + } + } } diff --git a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js index e78de8ed7..54bd76e43 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename MapToolsThread.js * @author theBGuy @@ -22,270 +23,270 @@ include("manualplay/MapMode.js"); MapMode.include(); function main() { - // getUnit test - getUnit(-1) === null && console.warn("getUnit bug detected"); + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); - console.log("ÿc9MapToolsThread loaded"); - - let ironGolem, debugInfo = {area: 0, currScript: "no entry"}; - let quitFlag = false; - let quitListDelayTime; - let canQuit = true; - - D2Bot.init(); - Config.init(false); - Pickit.init(false); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - for (let i = 0; i < 5; i += 1) { - Common.Toolsthread.timerLastDrink[i] = 0; - } - - // Reset core chicken - me.chickenhp = -1; - me.chickenmp = -1; - - // General functions - Common.Toolsthread.pauseScripts = [ - "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", - "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", - ]; - Common.Toolsthread.stopScripts = [ - "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", - "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", - ]; - - // Event functions - this.keyEvent = function (key) { - switch (key) { - case sdk.keys.PauseBreak: // pause default.dbj - Common.Toolsthread.togglePause(); - - break; - case sdk.keys.Delete: // quit current game - Common.Toolsthread.exit(); - - break; - case sdk.keys.End: // stop profile and log character - MuleLogger.logChar(); - delay(rand(Time.seconds(Config.QuitListDelay[0]), Time.seconds(Config.QuitListDelay[1]))); - D2Bot.printToConsole(me.profile + " - end run " + me.gamename); - D2Bot.stop(me.profile, 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)); - - break; - case sdk.keys.NumpadDecimal: - MuleLogger.logChar(); - me.overhead("Logged char: " + me.name); - - break; - case sdk.keys.NumpadSlash: // re-load default - console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); - Common.Toolsthread.stopDefault() && delay(Time.seconds(5)); - console.log("Starting default.dbj"); - load("default.dbj"); - - break; - case sdk.keys.NumpadStar: // precast - { - let preSkill = me.getSkill(sdk.skills.get.RightId); - - Precast.doPrecast(true); - Skill.setSkill(preSkill, sdk.skills.hand.Right); - } + console.log("ÿc9MapToolsThread loaded"); + + let ironGolem, debugInfo = { area: 0, currScript: "no entry" }; + let quitFlag = false; + let quitListDelayTime; + let canQuit = true; + + D2Bot.init(); + Config.init(false); + Pickit.init(false); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + + for (let i = 0; i < 5; i += 1) { + Common.Toolsthread.timerLastDrink[i] = 0; + } + + // Reset core chicken + me.chickenhp = -1; + me.chickenmp = -1; + + // General functions + Common.Toolsthread.pauseScripts = [ + "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", + "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", + ]; + Common.Toolsthread.stopScripts = [ + "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", + "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", + ]; + + // Event functions + this.keyEvent = function (key) { + switch (key) { + case sdk.keys.PauseBreak: // pause default.dbj + Common.Toolsthread.togglePause(); + + break; + case sdk.keys.Delete: // quit current game + Common.Toolsthread.exit(); + + break; + case sdk.keys.End: // stop profile and log character + MuleLogger.logChar(); + delay(rand(Time.seconds(Config.QuitListDelay[0]), Time.seconds(Config.QuitListDelay[1]))); + D2Bot.printToConsole(me.profile + " - end run " + me.gamename); + D2Bot.stop(me.profile, 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)); + + break; + case sdk.keys.NumpadDecimal: + MuleLogger.logChar(); + me.overhead("Logged char: " + me.name); + + break; + case sdk.keys.NumpadSlash: // re-load default + console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); + Common.Toolsthread.stopDefault() && delay(Time.seconds(5)); + console.log("Starting default.dbj"); + load("default.dbj"); + + break; + case sdk.keys.NumpadStar: // precast + { + let preSkill = me.getSkill(sdk.skills.get.RightId); + + Precast.doPrecast(true); + Skill.setSkill(preSkill, sdk.skills.hand.Right); + } - break; - } - }; - - this.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 (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; - } - - if (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: - if (Config.AntiHostile && param2 === 0x03) { // "%Player has declared hostility towards you." - 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; - } - - if (Config.SoJWaitTime && me.expansion) { - !!me.realm && D2Bot.printToConsole(param1 + " Stones of Jordan Sold to Merchants on IP " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); - // Messaging.sendToScript("default.dbj", "soj"); - } - - break; - case 0x12: // "Diablo Walks the Earth" - me.expansion && !!me.realm && D2Bot.printToConsole("Diablo Walks the Earth. " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); - - break; - } - }; - - this.scriptEvent = function (msg) { - switch (msg) { - case "toggleQuitlist": - canQuit = !canQuit; - - break; - case "quit": - quitFlag = true; - - break; - } - }; - - // Cache variables to prevent a bug where d2bs loses the reference to Config object - Config = copyObj(Config); - let tick = getTickCount(); - - addEventListener("keyup", this.keyEvent); - addEventListener("gameevent", this.gameEvent); - addEventListener("scriptmsg", this.scriptEvent); - - Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); - !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks - - // Start - while (true) { - try { - if (me.gameReady && !me.inTown) { - Config.UseHP > 0 && me.hpPercent < Config.UseHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Health); - Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); - - if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { - // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting - if (!me.inTown) { - D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - } - - Config.UseMP > 0 && me.mpPercent < Config.UseMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Mana); - Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); - - if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { - D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - - 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)) { - D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - } - } - - 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) { - D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - - mercHP < Config.UseMercHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercHealth); - mercHP < Config.UseMercRejuv && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercRejuv); - } - } - } - - if (Config.ViperCheck && getTickCount() - tick >= 250) { - Common.Toolsthread.checkVipers() && (quitFlag = true); - - tick = getTickCount(); - } - - Common.Toolsthread.checkPing(true) && (quitFlag = true); - } - } catch (e) { - Misc.errorReport(e, "MapToolsThread"); - takeScreenshot(); - - quitFlag = true; - } - - if (quitFlag && canQuit) { - if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { - me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); - continue; - } - Common.Toolsthread.checkPing(false); // In case of quitlist triggering first - Common.Toolsthread.exit(); - - break; - } - - if (debugInfo.area !== getAreaName(me.area)) { - debugInfo.area = getAreaName(me.area); - DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); - } - - delay(20); - } - - return true; + break; + } + }; + + this.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 (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; + } + + if (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: + if (Config.AntiHostile && param2 === 0x03) { // "%Player has declared hostility towards you." + 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; + } + + if (Config.SoJWaitTime && me.expansion) { + !!me.realm && D2Bot.printToConsole(param1 + " Stones of Jordan Sold to Merchants on IP " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); + // Messaging.sendToScript("default.dbj", "soj"); + } + + break; + case 0x12: // "Diablo Walks the Earth" + me.expansion && !!me.realm && D2Bot.printToConsole("Diablo Walks the Earth. " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); + + break; + } + }; + + this.scriptEvent = function (msg) { + switch (msg) { + case "toggleQuitlist": + canQuit = !canQuit; + + break; + case "quit": + quitFlag = true; + + break; + } + }; + + // Cache variables to prevent a bug where d2bs loses the reference to Config object + Config = copyObj(Config); + let tick = getTickCount(); + + addEventListener("keyup", this.keyEvent); + addEventListener("gameevent", this.gameEvent); + addEventListener("scriptmsg", this.scriptEvent); + + Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); + !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks + + // Start + while (true) { + try { + if (me.gameReady && !me.inTown) { + Config.UseHP > 0 && me.hpPercent < Config.UseHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Health); + Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + + if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { + // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting + if (!me.inTown) { + D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + } + + Config.UseMP > 0 && me.mpPercent < Config.UseMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Mana); + Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + + if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { + D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + + 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)) { + D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + } + } + + 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) { + D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + + mercHP < Config.UseMercHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercHealth); + mercHP < Config.UseMercRejuv && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercRejuv); + } + } + } + + if (Config.ViperCheck && getTickCount() - tick >= 250) { + Common.Toolsthread.checkVipers() && (quitFlag = true); + + tick = getTickCount(); + } + + Common.Toolsthread.checkPing(true) && (quitFlag = true); + } + } catch (e) { + Misc.errorReport(e, "MapToolsThread"); + takeScreenshot(); + + quitFlag = true; + } + + if (quitFlag && canQuit) { + if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { + me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); + continue; + } + Common.Toolsthread.checkPing(false); // In case of quitlist triggering first + Common.Toolsthread.exit(); + + break; + } + + if (debugInfo.area !== getAreaName(me.area)) { + debugInfo.area = getAreaName(me.area); + DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); + } + + delay(20); + } + + return true; } diff --git a/d2bs/kolbot/libs/manualplay/threads/PickThread.js b/d2bs/kolbot/libs/manualplay/threads/PickThread.js index a1e8e2472..c4a76392e 100644 --- a/d2bs/kolbot/libs/manualplay/threads/PickThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/PickThread.js @@ -25,37 +25,37 @@ include("manualplay/MapMode.js"); MapMode.include(); function main() { - console.log("ÿc9Pick Thread Loaded."); - Config.init(false); - Pickit.init(false); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - let noPick = false; - const UIFlagList = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, - sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.ConfigControls, sdk.uiflags.SubmitItem, - sdk.uiflags.Quest, sdk.uiflags.Waypoint, sdk.uiflags.Party, sdk.uiflags.Cube, sdk.uiflags.MercScreen - ]; - - addEventListener("itemaction", Pickit.itemEvent); - - while (true) { - for (let i = 0; i < UIFlagList.length; i++) { - if (getUIFlag(UIFlagList[i])) { - noPick = true; - break; - } - } - - if (!me.inTown && !noPick && !me.itemoncursor && Pickit.gidList.length > 0) { - Pickit.fastPick(1); - } - - noPick = false; - delay(100); - } + console.log("ÿc9Pick Thread Loaded."); + Config.init(false); + Pickit.init(false); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + + let noPick = false; + const UIFlagList = [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, + sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.ConfigControls, sdk.uiflags.SubmitItem, + sdk.uiflags.Quest, sdk.uiflags.Waypoint, sdk.uiflags.Party, sdk.uiflags.Cube, sdk.uiflags.MercScreen + ]; + + addEventListener("itemaction", Pickit.itemEvent); + + while (true) { + for (let i = 0; i < UIFlagList.length; i++) { + if (getUIFlag(UIFlagList[i])) { + noPick = true; + break; + } + } + + if (!me.inTown && !noPick && !me.itemoncursor && Pickit.gidList.length > 0) { + Pickit.fastPick(1); + } + + noPick = false; + delay(100); + } } diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index 6452c28e4..06d0ce4c1 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -3,7 +3,7 @@ * @author Jaenster, theBGuy(added the rest of the controls) */ (function (module) { - /** + /** * Not callable as a function * @constructor * @method Control.click(targetx, targety) @@ -15,249 +15,250 @@ * @param {number} xsize * @param {number} ysize */ - function Control(type, x, y, xsize, ysize) { - /** + function Control(type, x, y, xsize, ysize) { + /** * @private * @type {number} */ - this.type = type; + this.type = type; - /** + /** * @private * @type {number} */ - this.x = x; + this.x = x; - /** + /** * @private * @type {number} */ - this.y = y; + this.y = y; - /** + /** * @private * @type {number} */ - this.xsize = xsize; + this.xsize = xsize; - /** + /** * @private * @type {number} */ - this.ysize = ysize; + this.ysize = ysize; - return new Proxy(this, { - get: function (target, p) { - const passthroughFunc = ["click", "setText", "getText"]; + return new Proxy(this, { + get: function (target, p) { + const passthroughFunc = ["click", "setText", "getText"]; - if (p === "valueOf") { - return target; - } + if (p === "valueOf") { + return target; + } - const control = getControl(target.type, target.x, target.y, target.xsize, target.ysize); - if (p === "control") { - return control; - } + const control = getControl(target.type, target.x, target.y, target.xsize, target.ysize); + if (p === "control") { + return control; + } - // Relay on old ControlAction functions - if (passthroughFunc.indexOf(p) !== -1) { - return (...args) => ControlAction[p].apply(ControlAction, [target.type, target.x, target.y, target.xsize, target.ysize].concat(args)); - } + // Relay on old ControlAction functions + if (passthroughFunc.indexOf(p) !== -1) { + return (...args) => ControlAction[p] + .apply(ControlAction, [target.type, target.x, target.y, target.xsize, target.ysize].concat(args)); + } - // if control is found, and it's a property of the control - if (typeof control === "object" && control && control.hasOwnProperty(p)) { - return control[p]; - } + // if control is found, and it's a property of the control + if (typeof control === "object" && control && control.hasOwnProperty(p)) { + return control[p]; + } - // The target has it - if (target.hasOwnProperty(p)) { - return target[p]; - } + // The target has it + if (target.hasOwnProperty(p)) { + return target[p]; + } - return null; - } - }); - } + return null; + } + }); + } - // General Controls - Still non-exhaustive - Control.BottomLeftExit = new Control(sdk.controls.Button, 33, 572, 128, 35); - Control.BottomRightOk = new Control(sdk.controls.Button, 627, 572, 128, 35); - Control.OkCentered = new Control(sdk.controls.Button, 351, 337, 96, 32); - Control.OkCenteredText = new Control(sdk.controls.LabelBox, 268, 300, 264, 100); - Control.EnterAccountName = new Control(sdk.controls.TextBox, 322, 342, 162, 19); - Control.EnterAccountPassword = new Control(sdk.controls.TextBox, 322, 396, 162, 19); - Control.PopupYes = new Control(sdk.controls.Button, 421, 337, 96, 32); - Control.PopupNo = new Control(sdk.controls.Button, 281, 337, 96, 32); + // General Controls - Still non-exhaustive + Control.BottomLeftExit = new Control(sdk.controls.Button, 33, 572, 128, 35); + Control.BottomRightOk = new Control(sdk.controls.Button, 627, 572, 128, 35); + Control.OkCentered = new Control(sdk.controls.Button, 351, 337, 96, 32); + Control.OkCenteredText = new Control(sdk.controls.LabelBox, 268, 300, 264, 100); + Control.EnterAccountName = new Control(sdk.controls.TextBox, 322, 342, 162, 19); + Control.EnterAccountPassword = new Control(sdk.controls.TextBox, 322, 396, 162, 19); + Control.PopupYes = new Control(sdk.controls.Button, 421, 337, 96, 32); + Control.PopupNo = new Control(sdk.controls.Button, 281, 337, 96, 32); - // Main Menu Controls - { - Control.SplashScreen = new Control(sdk.controls.TextBox, 0, 599, 800, 600); - Control.D2SplashCopyright = new Control(sdk.controls.LabelBox, 100, 580, 600, 80); - Control.MainMenuD2Version = new Control(sdk.controls.LabelBox, 0, 599, 200, 40); - Control.MainMenuCredits = new Control(sdk.controls.Button, 264, 528, 135, 25); - Control.MainMenuCinematics = new Control(sdk.controls.Button, 402, 528, 135, 25); - Control.MainMenuExit = new Control(sdk.controls.Button, 264, 568, 272, 35); - Control.SinglePlayer = new Control(-1, 264, 324, 272, 35); - Control.BattleNet = new Control(-1, 264, 366, 272, 35); - Control.Gateway = new Control(sdk.controls.Button, 264, 391, 272, 25); - Control.OtherMultiplayer = new Control(-1, 264, 433, 272, 35); - } + // Main Menu Controls + { + Control.SplashScreen = new Control(sdk.controls.TextBox, 0, 599, 800, 600); + Control.D2SplashCopyright = new Control(sdk.controls.LabelBox, 100, 580, 600, 80); + Control.MainMenuD2Version = new Control(sdk.controls.LabelBox, 0, 599, 200, 40); + Control.MainMenuCredits = new Control(sdk.controls.Button, 264, 528, 135, 25); + Control.MainMenuCinematics = new Control(sdk.controls.Button, 402, 528, 135, 25); + Control.MainMenuExit = new Control(sdk.controls.Button, 264, 568, 272, 35); + Control.SinglePlayer = new Control(-1, 264, 324, 272, 35); + Control.BattleNet = new Control(-1, 264, 366, 272, 35); + Control.Gateway = new Control(sdk.controls.Button, 264, 391, 272, 25); + Control.OtherMultiplayer = new Control(-1, 264, 433, 272, 35); + } - // Login Menu Controls - { - Control.Login = new Control(-1, 264, 484, 272, 35); - Control.LoginHeading = new Control(sdk.controls.LabelBox, 200, 350, 400, 100); - Control.LoginErrorOk = new Control(sdk.controls.Button, 335, 412, 128, 35); - Control.LoginCancelWait = new Control(sdk.controls.Button, 330, 416, 128, 35); - Control.LoginCdKeyInUseBy = new Control(sdk.controls.LabelBox, 162, 270, 477, 50); - Control.LoginLodKeyInUseBy = new Control(sdk.controls.LabelBox, 158, 310, 485, 40); - Control.LoginInvalidCdKey = new Control(sdk.controls.LabelBox, 4, 162, 320, 477, 100); - Control.LoginUnableToConnect = new Control(sdk.controls.LabelBox, 158, 220, 485, 40); - Control.LoginErrorText = new Control(sdk.controls.LabelBox, 199, 377, 402, 140); - Control.LoginAccountSettings = new Control(sdk.controls.Button, 264, 528, 272, 35); - Control.UnableToConnectOk = new Control(sdk.controls.Button, 335, 450, 128, 35); - } + // Login Menu Controls + { + Control.Login = new Control(-1, 264, 484, 272, 35); + Control.LoginHeading = new Control(sdk.controls.LabelBox, 200, 350, 400, 100); + Control.LoginErrorOk = new Control(sdk.controls.Button, 335, 412, 128, 35); + Control.LoginCancelWait = new Control(sdk.controls.Button, 330, 416, 128, 35); + Control.LoginCdKeyInUseBy = new Control(sdk.controls.LabelBox, 162, 270, 477, 50); + Control.LoginLodKeyInUseBy = new Control(sdk.controls.LabelBox, 158, 310, 485, 40); + Control.LoginInvalidCdKey = new Control(sdk.controls.LabelBox, 4, 162, 320, 477, 100); + Control.LoginUnableToConnect = new Control(sdk.controls.LabelBox, 158, 220, 485, 40); + Control.LoginErrorText = new Control(sdk.controls.LabelBox, 199, 377, 402, 140); + Control.LoginAccountSettings = new Control(sdk.controls.Button, 264, 528, 272, 35); + Control.UnableToConnectOk = new Control(sdk.controls.Button, 335, 450, 128, 35); + } - // Other Multiplayer Menu Controls - { - Control.OpenBattleNet = new Control(-1, 264, 310, 272, 35); - Control.TcpIp = new Control(-1, 264, 350, 272, 35); - Control.OtherMultiplayerCancel = new Control(sdk.controls.Button, 264, 568, 272, 35); - } + // Other Multiplayer Menu Controls + { + Control.OpenBattleNet = new Control(-1, 264, 310, 272, 35); + Control.TcpIp = new Control(-1, 264, 350, 272, 35); + Control.OtherMultiplayerCancel = new Control(sdk.controls.Button, 264, 568, 272, 35); + } - // Gateway Menu Controls - { - Control.GatewayOk = new Control(sdk.controls.Button, 281, 538, 96, 32); - Control.GatewayCancel = new Control(sdk.controls.Button, 436, 538, 96, 32); - } + // Gateway Menu Controls + { + Control.GatewayOk = new Control(sdk.controls.Button, 281, 538, 96, 32); + Control.GatewayCancel = new Control(sdk.controls.Button, 436, 538, 96, 32); + } - // TCP/IP Menu Controls - { - Control.TcpIpHost = new Control(-1, 265, 206, 272, 35); - Control.TcpIpJoin = new Control(-1, 265, 264, 272, 35); - Control.TcpIpCancel = new Control(sdk.controls.Button, 39, 571, 128, 35); + // TCP/IP Menu Controls + { + Control.TcpIpHost = new Control(-1, 265, 206, 272, 35); + Control.TcpIpJoin = new Control(-1, 265, 264, 272, 35); + Control.TcpIpCancel = new Control(sdk.controls.Button, 39, 571, 128, 35); - Control.IPAdress = new Control(-1, 300, 268, -1, -1); - Control.IPAdressOk = new Control(-1, 421, 337, 96, 32); - } + Control.IPAdress = new Control(-1, 300, 268, -1, -1); + Control.IPAdressOk = new Control(-1, 421, 337, 96, 32); + } - // Create Account Menu Controls - { - Control.CreateNewAccount = new Control(sdk.controls.Button, 264, 572, 272, 35); - Control.ConfirmPassword = new Control(sdk.controls.TextBox, 322, 450, 162, 19); - } + // Create Account Menu Controls + { + Control.CreateNewAccount = new Control(sdk.controls.Button, 264, 572, 272, 35); + Control.ConfirmPassword = new Control(sdk.controls.TextBox, 322, 450, 162, 19); + } - // Terms of Use Menu Controls - { - Control.TermsOfUseAgree = new Control(sdk.controls.Button, 525, 513, 128, 35); - Control.TermsOfUseDisagree = new Control(sdk.controls.Button, 133, 513, 128, 35); + // Terms of Use Menu Controls + { + Control.TermsOfUseAgree = new Control(sdk.controls.Button, 525, 513, 128, 35); + Control.TermsOfUseDisagree = new Control(sdk.controls.Button, 133, 513, 128, 35); - Control.PleaseReadOk = new Control(sdk.controls.Button, 525, 513, 128, 35); - Control.PleaseReadCancel = new Control(sdk.controls.Button, 133, 513, 128, 35); - } + Control.PleaseReadOk = new Control(sdk.controls.Button, 525, 513, 128, 35); + Control.PleaseReadCancel = new Control(sdk.controls.Button, 133, 513, 128, 35); + } - // Email Menu Controls - { - Control.EmailSetEmail = new Control(sdk.controls.TextBox, 253, 342, 293, 19); - Control.EmailVerifyEmail = new Control(sdk.controls.TextBox, 253, 396, 293, 19); - Control.EmailRegister = new Control(sdk.controls.Button, 265, 527, 272, 35); - Control.EmailDontRegister = new Control(sdk.controls.Button, 265, 572, 272, 35); - Control.EmailDontRegisterContinue = new Control(sdk.controls.Button, 415, 412, 128, 35); - } + // Email Menu Controls + { + Control.EmailSetEmail = new Control(sdk.controls.TextBox, 253, 342, 293, 19); + Control.EmailVerifyEmail = new Control(sdk.controls.TextBox, 253, 396, 293, 19); + Control.EmailRegister = new Control(sdk.controls.Button, 265, 527, 272, 35); + Control.EmailDontRegister = new Control(sdk.controls.Button, 265, 572, 272, 35); + Control.EmailDontRegisterContinue = new Control(sdk.controls.Button, 415, 412, 128, 35); + } - // Character Select Menu Controls - { - Control.CharSelectCreate = new Control(sdk.controls.Button, 33, 528, 168, 60); - Control.CharSelectDelete = new Control(sdk.controls.Button, 433, 528, 168, 60); - Control.CharSelectConvert = new Control(sdk.controls.Button, 233, 528, 168, 60); - Control.CharSelectError = new Control(sdk.controls.LabelBox, 45, 318, 531, 140); - Control.CharSelectCharInfo0 = new Control(sdk.controls.LabelBox, 37, 178, 200, 92); - Control.CharSelectChar4 = new Control(sdk.controls.LabelBox, 237, 364, 72, 93); - Control.CharSelectChar6 = new Control(sdk.controls.LabelBox, 237, 457, 72, 93); - Control.CharSelectCurrentRealm = new Control(sdk.controls.LabelBox, 626, 100, 151, 44); - } + // Character Select Menu Controls + { + Control.CharSelectCreate = new Control(sdk.controls.Button, 33, 528, 168, 60); + Control.CharSelectDelete = new Control(sdk.controls.Button, 433, 528, 168, 60); + Control.CharSelectConvert = new Control(sdk.controls.Button, 233, 528, 168, 60); + Control.CharSelectError = new Control(sdk.controls.LabelBox, 45, 318, 531, 140); + Control.CharSelectCharInfo0 = new Control(sdk.controls.LabelBox, 37, 178, 200, 92); + Control.CharSelectChar4 = new Control(sdk.controls.LabelBox, 237, 364, 72, 93); + Control.CharSelectChar6 = new Control(sdk.controls.LabelBox, 237, 457, 72, 93); + Control.CharSelectCurrentRealm = new Control(sdk.controls.LabelBox, 626, 100, 151, 44); + } - // Character Create Menu Controls - { - Control.CharCreateCharName = new Control(sdk.controls.TextBox, 318, 510, 157, 16); - Control.CharCreateExpansion = new Control(sdk.controls.Button, 319, 540, 15, 16); - Control.CharCreateLadder = new Control(sdk.controls.Button, 319, 580, 15, 16); - Control.CharCreateHardcore = new Control(sdk.controls.Button, 319, 560, 15, 16); - // these two are the same as popup yes/no, should they be kept seperate or merged? - Control.CharCreateHCWarningOk = new Control(sdk.controls.Button, 421, 337, 96, 32); - Control.CharCreateHCWarningCancel = new Control(sdk.controls.Button, 281, 337, 96, 32); - } + // Character Create Menu Controls + { + Control.CharCreateCharName = new Control(sdk.controls.TextBox, 318, 510, 157, 16); + Control.CharCreateExpansion = new Control(sdk.controls.Button, 319, 540, 15, 16); + Control.CharCreateLadder = new Control(sdk.controls.Button, 319, 580, 15, 16); + Control.CharCreateHardcore = new Control(sdk.controls.Button, 319, 560, 15, 16); + // these two are the same as popup yes/no, should they be kept seperate or merged? + Control.CharCreateHCWarningOk = new Control(sdk.controls.Button, 421, 337, 96, 32); + Control.CharCreateHCWarningCancel = new Control(sdk.controls.Button, 281, 337, 96, 32); + } - // Lobby Menu Controls - { - Control.LobbyCharacterInfo = new Control(sdk.controls.LabelBox, 143, 588, 230, 87); - Control.LobbyEnterChat = new Control(sdk.controls.Button, 27, 480, 120, 20); - Control.LobbyChat = new Control(sdk.controls.LabelBox, 28, 410, 354, 298); - Control.LobbyServerDown = new Control(sdk.controls.LabelBox, 438, 300, 326, 150); - Control.LobbyLadder = new Control(sdk.controls.Button, 614, 490, 80, 20); - Control.LobbyHelp = new Control(sdk.controls.Button, 146, 480, 120, 20); - Control.LobbyQuit = new Control(sdk.controls.Button, 693, 490, 80, 20); - } + // Lobby Menu Controls + { + Control.LobbyCharacterInfo = new Control(sdk.controls.LabelBox, 143, 588, 230, 87); + Control.LobbyEnterChat = new Control(sdk.controls.Button, 27, 480, 120, 20); + Control.LobbyChat = new Control(sdk.controls.LabelBox, 28, 410, 354, 298); + Control.LobbyServerDown = new Control(sdk.controls.LabelBox, 438, 300, 326, 150); + Control.LobbyLadder = new Control(sdk.controls.Button, 614, 490, 80, 20); + Control.LobbyHelp = new Control(sdk.controls.Button, 146, 480, 120, 20); + Control.LobbyQuit = new Control(sdk.controls.Button, 693, 490, 80, 20); + } - // Join Game Menu Controls - { - Control.JoinGameWindow = new Control(sdk.controls.Button, 652, 469, 120, 20); - Control.JoinGame = new Control(sdk.controls.Button, 594, 433, 172, 32); - Control.JoinGameName = new Control(sdk.controls.TextBox, 432, 148, 155, 20); - Control.JoinGamePass = new Control(sdk.controls.TextBox, 606, 148, 155, 20); - Control.JoinGameList = new Control(sdk.controls.LabelBox, 432, 393, 160, 173); - Control.JoinGameDetails = new Control(sdk.controls.LabelBox, 609, 393, 143, 194); - Control.CancelJoinGame = new Control(sdk.controls.Button, 433, 433, 96, 32); - } + // Join Game Menu Controls + { + Control.JoinGameWindow = new Control(sdk.controls.Button, 652, 469, 120, 20); + Control.JoinGame = new Control(sdk.controls.Button, 594, 433, 172, 32); + Control.JoinGameName = new Control(sdk.controls.TextBox, 432, 148, 155, 20); + Control.JoinGamePass = new Control(sdk.controls.TextBox, 606, 148, 155, 20); + Control.JoinGameList = new Control(sdk.controls.LabelBox, 432, 393, 160, 173); + Control.JoinGameDetails = new Control(sdk.controls.LabelBox, 609, 393, 143, 194); + Control.CancelJoinGame = new Control(sdk.controls.Button, 433, 433, 96, 32); + } - // Create Game Menu Controls - { - Control.CreateGameWindow = new Control(sdk.controls.Button, 533, 469, 120, 20); - Control.CreateGame = new Control(sdk.controls.Button, 594, 433, 172, 32); - Control.CreateGameName = new Control(sdk.controls.TextBox, 432, 162, 158, 20); - Control.CreateGamePass = new Control(sdk.controls.TextBox, 432, 217, 158, 20); - Control.CreateGameDescription = new Control(sdk.controls.TextBox, 432, 268, 333, 20); - Control.CharacterDifferenceButton = new Control(sdk.controls.Button, 431, 341, 15, 16); - Control.CharacterDifference = new Control(sdk.controls.TextBox, 657, 342, 27, 20); - Control.MaxPlayerCount = new Control(sdk.controls.TextBox, 657, 308, 27, 20); - Control.Normal = new Control(sdk.controls.Button, 430, 381, 16, 16); - Control.Nightmare = new Control(sdk.controls.Button, 555, 381, 16, 16); - Control.Hell = new Control(sdk.controls.Button, 698, 381, 16, 16); - Control.CreateGameInLine = new Control(sdk.controls.LabelBox, 427, 234, 300, 100); - Control.CancelCreateGame = new Control(sdk.controls.Button, 433, 433, 96, 32); - } + // Create Game Menu Controls + { + Control.CreateGameWindow = new Control(sdk.controls.Button, 533, 469, 120, 20); + Control.CreateGame = new Control(sdk.controls.Button, 594, 433, 172, 32); + Control.CreateGameName = new Control(sdk.controls.TextBox, 432, 162, 158, 20); + Control.CreateGamePass = new Control(sdk.controls.TextBox, 432, 217, 158, 20); + Control.CreateGameDescription = new Control(sdk.controls.TextBox, 432, 268, 333, 20); + Control.CharacterDifferenceButton = new Control(sdk.controls.Button, 431, 341, 15, 16); + Control.CharacterDifference = new Control(sdk.controls.TextBox, 657, 342, 27, 20); + Control.MaxPlayerCount = new Control(sdk.controls.TextBox, 657, 308, 27, 20); + Control.Normal = new Control(sdk.controls.Button, 430, 381, 16, 16); + Control.Nightmare = new Control(sdk.controls.Button, 555, 381, 16, 16); + Control.Hell = new Control(sdk.controls.Button, 698, 381, 16, 16); + Control.CreateGameInLine = new Control(sdk.controls.LabelBox, 427, 234, 300, 100); + Control.CancelCreateGame = new Control(sdk.controls.Button, 433, 433, 96, 32); + } - // Channel Menu Controls - { - Control.LobbyChannel = new Control(sdk.controls.Button, 535, 490, 80, 20); - Control.LobbyChannelName = new Control(sdk.controls.LabelBox, 28, 138, 354, 60); - Control.LobbyChannelText = new Control(sdk.controls.TextBox, 432, 162, 155, 20); - Control.LobbyChannelOk = new Control(sdk.controls.Button, 671, 433, 96, 32); - Control.LobbyChannelCancel = new Control(sdk.controls.Button, 433, 433, 96, 32); - Control.LobbyChannelSend = new Control(sdk.controls.Button, 27, 470, 80, 20); - Control.LobbyChannelWhisper = new Control(sdk.controls.Button, 107, 470, 80, 20); - Control.LobbyChannelSquelch = new Control(sdk.controls.Button, 27, 490, 72, 20); - Control.LobbyChannelUnsquelch = new Control(sdk.controls.Button, 99, 490, 96, 20); - Control.LobbyChannelEmote = new Control(sdk.controls.Button, 195, 490, 72, 20); - Control.LobbyChannelChar0 = new Control(sdk.controls.Button, 40, 591, 60, 100); - Control.LobbyChannelChar1 = new Control(sdk.controls.Button, 100, 591, 60, 100); - Control.LobbyChannelChar2 = new Control(sdk.controls.Button, 160, 591, 60, 100); - Control.LobbyChannelChar3 = new Control(sdk.controls.Button, 220, 591, 60, 100); - Control.LobbyChannelChar4 = new Control(sdk.controls.Button, 280, 591, 60, 100); - Control.LobbyChannelChar5 = new Control(sdk.controls.Button, 340, 591, 60, 100); - Control.LobbyChannelChar6 = new Control(sdk.controls.Button, 400, 591, 60, 100); - Control.LobbyChannelChar7 = new Control(sdk.controls.Button, 460, 591, 60, 100); - Control.LobbyChannelChar8 = new Control(sdk.controls.Button, 520, 591, 60, 100); - Control.LobbyChannelChar9 = new Control(sdk.controls.Button, 580, 591, 60, 100); - Control.LobbyChannelChar10 = new Control(sdk.controls.Button, 640, 591, 60, 100); - } + // Channel Menu Controls + { + Control.LobbyChannel = new Control(sdk.controls.Button, 535, 490, 80, 20); + Control.LobbyChannelName = new Control(sdk.controls.LabelBox, 28, 138, 354, 60); + Control.LobbyChannelText = new Control(sdk.controls.TextBox, 432, 162, 155, 20); + Control.LobbyChannelOk = new Control(sdk.controls.Button, 671, 433, 96, 32); + Control.LobbyChannelCancel = new Control(sdk.controls.Button, 433, 433, 96, 32); + Control.LobbyChannelSend = new Control(sdk.controls.Button, 27, 470, 80, 20); + Control.LobbyChannelWhisper = new Control(sdk.controls.Button, 107, 470, 80, 20); + Control.LobbyChannelSquelch = new Control(sdk.controls.Button, 27, 490, 72, 20); + Control.LobbyChannelUnsquelch = new Control(sdk.controls.Button, 99, 490, 96, 20); + Control.LobbyChannelEmote = new Control(sdk.controls.Button, 195, 490, 72, 20); + Control.LobbyChannelChar0 = new Control(sdk.controls.Button, 40, 591, 60, 100); + Control.LobbyChannelChar1 = new Control(sdk.controls.Button, 100, 591, 60, 100); + Control.LobbyChannelChar2 = new Control(sdk.controls.Button, 160, 591, 60, 100); + Control.LobbyChannelChar3 = new Control(sdk.controls.Button, 220, 591, 60, 100); + Control.LobbyChannelChar4 = new Control(sdk.controls.Button, 280, 591, 60, 100); + Control.LobbyChannelChar5 = new Control(sdk.controls.Button, 340, 591, 60, 100); + Control.LobbyChannelChar6 = new Control(sdk.controls.Button, 400, 591, 60, 100); + Control.LobbyChannelChar7 = new Control(sdk.controls.Button, 460, 591, 60, 100); + Control.LobbyChannelChar8 = new Control(sdk.controls.Button, 520, 591, 60, 100); + Control.LobbyChannelChar9 = new Control(sdk.controls.Button, 580, 591, 60, 100); + Control.LobbyChannelChar10 = new Control(sdk.controls.Button, 640, 591, 60, 100); + } - // Single Player Difficulty Controls - { - Control.HellSP = new Control(-1, 264, 383, 272, 35); - Control.NightmareSP = new Control(-1, 264, 340, 272, 35); - Control.NormalSP = new Control(-1, 264, 297, 272, 35); - } + // Single Player Difficulty Controls + { + Control.HellSP = new Control(-1, 264, 383, 272, 35); + Control.NightmareSP = new Control(-1, 264, 340, 272, 35); + Control.NormalSP = new Control(-1, 264, 297, 272, 35); + } - module.exports = Control; + module.exports = Control; })(module); diff --git a/d2bs/kolbot/libs/modules/CopyData.js b/d2bs/kolbot/libs/modules/CopyData.js index c62be3178..1f4fd36ec 100644 --- a/d2bs/kolbot/libs/modules/CopyData.js +++ b/d2bs/kolbot/libs/modules/CopyData.js @@ -6,17 +6,17 @@ */ (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.CopyData = factory(); - console.trace(); - } + 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.CopyData = factory(); + console.trace(); + } }([].filter.constructor("return this")(), function() { - /** + /** * @class * @classdesc A class for creating and sending copy data packets. * @property {number} _mode - Defaults to 0, works for most D2Bot functions @@ -27,76 +27,76 @@ * @example Start mule profile "mule" * new CopyData().data("start", ["mule"]).send(); */ - function CopyData() { - if (this.__proto__.constructor !== CopyData) throw new Error("CopyData must be called with 'new' operator!"); + function CopyData() { + if (this.__proto__.constructor !== CopyData) throw new Error("CopyData must be called with 'new' operator!"); - /** + /** * @private * @type {string | number} - The handle to send the copy data to. */ - this._handle = D2Bot.handle || me.profile; + this._handle = D2Bot.handle || me.profile; - /** + /** * @private * @type {number} - The mode of the copy data packet. */ - this._mode = 0; + this._mode = 0; - /** + /** * @private * @type {string} - The data to send in the copy data */ - this._data = null; - } + this._data = null; + } - /** + /** * - D2Bot.handle is for any functions that act on ourselves * - Otherwise it is the D2Bot# profile name of the profile to act upon * @param {string | number} handle - The handle or profile to send the copy data to. */ - CopyData.prototype.handle = function (handle) { - this._handle = handle; - return this; - }; + CopyData.prototype.handle = function (handle) { + this._handle = handle; + return this; + }; - /** + /** * - 0 is for most functions, and the default value set * - 1 is for joinMe * - 3 is for requestGame * - 0xbbbb is for heartBeat * @param {number} mode - The mode of the copy data packet. */ - CopyData.prototype.mode = function (mode) { - this._mode = mode; - return this; - }; + CopyData.prototype.mode = function (mode) { + this._mode = mode; + return this; + }; - /** + /** * @param {string} [func] - The function to call from D2Bot# * @param {string[]} [args] - The additonal info needed for the function call */ - CopyData.prototype.data = function (func = "", args = []) { - if (func.includes("Item") || func === "printToConsole" || (func === "setTag" && typeof args[0] === "object")) { - args[0] = JSON.stringify(args[0]); - } - this._data = JSON.stringify({ - profile: me.profile, - func: func, - args: args - }); - return this; - }; + CopyData.prototype.data = function (func = "", args = []) { + if (func.includes("Item") || func === "printToConsole" || (func === "setTag" && typeof args[0] === "object")) { + args[0] = JSON.stringify(args[0]); + } + this._data = JSON.stringify({ + profile: me.profile, + func: func, + args: args + }); + return this; + }; - /** + /** * CopyData.data works for functions call d2bot# functions but what about gerneral use? * @todo handle passing custom data obj */ - CopyData.prototype.send = function () { - // check that data is set - this._data === null && this.data(); - return sendCopyData(null, this._handle, this._mode, this._data); - }; + CopyData.prototype.send = function () { + // check that data is set + this._data === null && this.data(); + return sendCopyData(null, this._handle, this._mode, this._data); + }; - return CopyData; + return CopyData; })); diff --git a/d2bs/kolbot/libs/modules/Deltas.js b/d2bs/kolbot/libs/modules/Deltas.js index fdc5aba2d..cc831cd77 100644 --- a/d2bs/kolbot/libs/modules/Deltas.js +++ b/d2bs/kolbot/libs/modules/Deltas.js @@ -4,36 +4,40 @@ * */ (function (module, require) { - const Worker = require("Worker"); - let instances = 0; + const Worker = require("Worker"); + let instances = 0; - /** @constructor + /** @constructor * @class Delta */ - module.exports = function (trackers) { - let active = true; - this.values = (Array.isArray(trackers) && (Array.isArray(trackers.first()) && trackers || [trackers])) || []; - this.track = function (checkerFn, callback) { - return this.values.push({fn: checkerFn, callback: callback, value: checkerFn()}); - }; - this.check = function () { - this.values.some(delta => { - let val = delta.fn(); + module.exports = function (trackers) { + let active = true; + this.values = (Array.isArray(trackers) && (Array.isArray(trackers.first()) && trackers || [trackers])) || []; + this.track = function (checkerFn, callback) { + return this.values.push({ fn: checkerFn, callback: callback, value: checkerFn() }); + }; + this.check = function () { + this.values.some(delta => { + let val = delta.fn(); - if (delta.value !== val) { - let ret = delta.callback(delta.value, val); - delta.value = val; + if (delta.value !== val) { + let ret = delta.callback(delta.value, val); + delta.value = val; - return ret; - } + return ret; + } - return null; - }); - }; + return null; + }); + }; - this.destroy = () => active = false; + this.destroy = () => active = false; - Worker.runInBackground["__delta" + (instances++)] = () => active && (this.check() || true); - return this; - }; + Worker.runInBackground["__delta" + (instances++)] = () => active && (this.check() || true); + return this; + }; -}).call(null, typeof module === "object" && module || {}, typeof require === "undefined" && (include("require.js") && require) || require); +}).call( + null, + typeof module === "object" && module || {}, + typeof require === "undefined" && (include("require.js") && require) || require +); diff --git a/d2bs/kolbot/libs/modules/Events.js b/d2bs/kolbot/libs/modules/Events.js index ccec6c572..5d824f1ff 100644 --- a/d2bs/kolbot/libs/modules/Events.js +++ b/d2bs/kolbot/libs/modules/Events.js @@ -5,47 +5,47 @@ */ (function (module, require) { - // eslint-disable-next-line no-unused-vars - const Events = module.exports = function () { - const Worker = require("Worker"), self = this; - - this.hooks = []; - - function Hook(name, callback) { - this.name = name; - this.callback = callback; - this.id = self.hooks.push(this) - 1; - this.__callback = callback; // used for once - } - - this.on = function (name, callback) { - if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; - return new Hook(name, callback); - }; - - this.trigger = function (name, ...args) { - return self.hooks.forEach(hook => !hook.name || hook.name === name && Worker.push(function () { - hook.callback.apply(hook, args); - })); - }; - - this.emit = this.trigger; // Alias for trigger - - this.once = function (name, callback) { - if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; - const hook = new Hook(name, function (...args) { - callback.apply(undefined, args); - delete self.hooks[this.id]; - }); - hook.__callback = callback; - }; - - this.off = function (name, callback) { - self.hooks.filter(hook => hook.__callback === callback).forEach(hook => { - delete self.hooks[hook.id]; - }); - }; - - this.removeListener = this.off; // Alias for remove - }; + // eslint-disable-next-line no-unused-vars + const Events = module.exports = function () { + const Worker = require("Worker"), self = this; + + this.hooks = []; + + function Hook(name, callback) { + this.name = name; + this.callback = callback; + this.id = self.hooks.push(this) - 1; + this.__callback = callback; // used for once + } + + this.on = function (name, callback) { + if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; + return new Hook(name, callback); + }; + + this.trigger = function (name, ...args) { + return self.hooks.forEach(hook => !hook.name || hook.name === name && Worker.push(function () { + hook.callback.apply(hook, args); + })); + }; + + this.emit = this.trigger; // Alias for trigger + + this.once = function (name, callback) { + if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; + const hook = new Hook(name, function (...args) { + callback.apply(undefined, args); + delete self.hooks[this.id]; + }); + hook.__callback = callback; + }; + + this.off = function (name, callback) { + self.hooks.filter(hook => hook.__callback === callback).forEach(hook => { + delete self.hooks[hook.id]; + }); + }; + + this.removeListener = this.off; // Alias for remove + }; })(module, require); diff --git a/d2bs/kolbot/libs/modules/Guard.js b/d2bs/kolbot/libs/modules/Guard.js index 54bfca515..aa843e78a 100644 --- a/d2bs/kolbot/libs/modules/Guard.js +++ b/d2bs/kolbot/libs/modules/Guard.js @@ -1,85 +1,93 @@ (function (module, require, thread) { - "use strict"; - const Messaging = require("./Messaging"); - const Worker = require("./Worker"); - const sdk = require("./sdk"); + "use strict"; + const Messaging = require("./Messaging"); + const Worker = require("./Worker"); + const sdk = require("./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 = element.visible = [sdk.uiflags.Inventory, - sdk.uiflags.SkillWindow, - sdk.uiflags.TradePrompt, - sdk.uiflags.Stash, - sdk.uiflags.Cube, - sdk.uiflags.QuickSkill].every(f => !getUIFlag(f)); - }; - } + 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 = 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()); +}).call( + null, + typeof module === "object" && module || {}, + typeof require === "undefined" && (include("require.js") && require) || require, + getScript.startAsThread() +); diff --git a/d2bs/kolbot/libs/modules/HTTP.js b/d2bs/kolbot/libs/modules/HTTP.js index 1e66b8561..e5d3cae4a 100644 --- a/d2bs/kolbot/libs/modules/HTTP.js +++ b/d2bs/kolbot/libs/modules/HTTP.js @@ -5,48 +5,48 @@ (function (module, require) { - const Socket = require("Socket"); - const Promise = require("Promise"); - - const defaultOptions = { - url: "", - headers: { - "User-Agent": "d2bs", - "Accept": "*/*", - }, - port: 80, - method: "GET", - data: "", // Fill with content for post - }; - - const HTTP = function (config = {}) { - config = Object.assign(defaultOptions, config); - if (!config.url) { - throw new Error("Must give a url to connect to"); - } - const [fullUrl, protocol, hostname, uri] = config.url.match(/^(.*:)\/\/([A-Za-z0-9\-\.]+)?(.*)/); - const socket = new Socket(hostname, config.port); - - socket.connect(); - if (!socket.connected) { - throw new Error("failed to connect to " + hostname); - } - - if (config.data.length) { // in case we send data - config.headers["Content-Length"] = config.data.length; - } - config.headers["Host"] = hostname; // required for HTTP/1.1 - - const data = [config.method + " " + uri + " " + "HTTP/1.1"]; - Object.keys(config.headers).forEach((key) => data.push(key + ": " + config.headers[key])); - - socket.send(data.join("\r\n") + "\r\n\r\n" + config.data); - - let recvd = false; // where we store recv'd data - socket.once("data", data => recvd = data); - return new Promise((resolve) => recvd && resolve(recvd)); - }; - - module.exports = HTTP; + const Socket = require("Socket"); + const Promise = require("Promise"); + + const defaultOptions = { + url: "", + headers: { + "User-Agent": "d2bs", + "Accept": "*/*", + }, + port: 80, + method: "GET", + data: "", // Fill with content for post + }; + + const HTTP = function (config = {}) { + config = Object.assign(defaultOptions, config); + if (!config.url) { + throw new Error("Must give a url to connect to"); + } + const [fullUrl, protocol, hostname, uri] = config.url.match(/^(.*:)\/\/([A-Za-z0-9\-\.]+)?(.*)/); + const socket = new Socket(hostname, config.port); + + socket.connect(); + if (!socket.connected) { + throw new Error("failed to connect to " + hostname); + } + + if (config.data.length) { // in case we send data + config.headers["Content-Length"] = config.data.length; + } + config.headers["Host"] = hostname; // required for HTTP/1.1 + + const data = [config.method + " " + uri + " " + "HTTP/1.1"]; + Object.keys(config.headers).forEach((key) => data.push(key + ": " + config.headers[key])); + + socket.send(data.join("\r\n") + "\r\n\r\n" + config.data); + + let recvd = false; // where we store recv'd data + socket.once("data", data => recvd = data); + return new Promise((resolve) => recvd && resolve(recvd)); + }; + + module.exports = HTTP; }).call(null, module, require); diff --git a/d2bs/kolbot/libs/modules/LocalChat.js b/d2bs/kolbot/libs/modules/LocalChat.js index 942f7650f..298e8d1e4 100644 --- a/d2bs/kolbot/libs/modules/LocalChat.js +++ b/d2bs/kolbot/libs/modules/LocalChat.js @@ -6,75 +6,80 @@ */ (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(["require", "../core/Util"], factory); - } else { - root.LocalChat = factory(); - console.trace(); - } + 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(["require", "../core/Util"], factory); + } else { + root.LocalChat = factory(); + console.trace(); + } }(this, function() { - // const { PacketBuilder } = require("../core/Util"); + // const { PacketBuilder } = require("../core/Util"); - const LocalChat = new function () { - const LOCAL_CHAT_ID = 0xD2BAAAA; - let toggle, proxy = say; + const LocalChat = new function () { + const LOCAL_CHAT_ID = 0xD2BAAAA; + let toggle, proxy = say; - let relay = (msg) => D2Bot.shoutGlobal(JSON.stringify({msg: msg, realm: me.realm, charname: me.charname, gamename: me.gamename }), LOCAL_CHAT_ID); + let relay = (msg) => D2Bot.shoutGlobal( + JSON.stringify({ msg: msg, realm: me.realm, charname: me.charname, gamename: me.gamename }), + LOCAL_CHAT_ID + ); - let onChatInput = (speaker, msg) => { - relay(msg); - return true; - }; + let onChatInput = (speaker, msg) => { + relay(msg); + return true; + }; - let onChatRecv = (mode, msg) => { - if (mode !== LOCAL_CHAT_ID) return; + let onChatRecv = (mode, msg) => { + if (mode !== LOCAL_CHAT_ID) return; - msg = JSON.parse(msg); + msg = JSON.parse(msg); - if (me.gamename === msg.gamename && me.realm === msg.realm) { - new PacketBuilder().byte(38).byte(1, me.locale).word(2, 0, 0).byte(90).string(msg.charname, msg.msg).get(); - } - }; + if (me.gamename === msg.gamename && me.realm === msg.realm) { + new PacketBuilder().byte(38).byte(1, me.locale).word(2, 0, 0).byte(90).string(msg.charname, msg.msg).get(); + } + }; - let onKeyEvent = (key) => { - if (toggle === key) { - this.init(true); - } - }; + let onKeyEvent = (key) => { + if (toggle === key) { + this.init(true); + } + }; - this.init = (cycle = false) => { - if (!Config.LocalChat.Enabled) return; + this.init = (cycle = false) => { + if (!Config.LocalChat.Enabled) return; - Config.LocalChat.Mode = (Config.LocalChat.Mode + cycle) % 3; - print("ÿc2LocalChat enabled. Mode: " + Config.LocalChat.Mode); + Config.LocalChat.Mode = (Config.LocalChat.Mode + cycle) % 3; + print("ÿc2LocalChat enabled. Mode: " + Config.LocalChat.Mode); - switch (Config.LocalChat.Mode) { - case 2: - removeEventListener("chatinputblocker", onChatInput); - addEventListener("chatinputblocker", onChatInput); - // eslint-disable-next-line no-fallthrough - case 1: - removeEventListener("copydata", onChatRecv); - addEventListener("copydata", onChatRecv); - say = (msg, force = false) => force ? proxy(msg) : relay(msg); - break; - case 0: - removeEventListener("chatinputblocker", onChatInput); - removeEventListener("copydata", onChatRecv); - say = proxy; - break; - } + switch (Config.LocalChat.Mode) { + case 2: + removeEventListener("chatinputblocker", onChatInput); + addEventListener("chatinputblocker", onChatInput); + // eslint-disable-next-line no-fallthrough + case 1: + removeEventListener("copydata", onChatRecv); + addEventListener("copydata", onChatRecv); + say = (msg, force = false) => force ? proxy(msg) : relay(msg); + break; + case 0: + removeEventListener("chatinputblocker", onChatInput); + removeEventListener("copydata", onChatRecv); + say = proxy; + break; + } - if (Config.LocalChat.Toggle) { - toggle = typeof Config.LocalChat.Toggle === "string" ? Config.LocalChat.Toggle.charCodeAt(0) : Config.LocalChat.Toggle; - Config.LocalChat.Toggle = false; - addEventListener("keyup", onKeyEvent); - } - }; - }; + if (Config.LocalChat.Toggle) { + toggle = typeof Config.LocalChat.Toggle === "string" + ? Config.LocalChat.Toggle.charCodeAt(0) + : Config.LocalChat.Toggle; + Config.LocalChat.Toggle = false; + addEventListener("keyup", onKeyEvent); + } + }; + }; - return LocalChat; + return LocalChat; })); diff --git a/d2bs/kolbot/libs/modules/Messaging.js b/d2bs/kolbot/libs/modules/Messaging.js index 7329de728..3b1587b04 100644 --- a/d2bs/kolbot/libs/modules/Messaging.js +++ b/d2bs/kolbot/libs/modules/Messaging.js @@ -5,34 +5,34 @@ (function (module, require) { - const myEvents = new (require("Events")); - const Worker = require("Worker"); - - - Worker.runInBackground.messaging = (new function () { - const workBench = []; - addEventListener("scriptmsg", data => workBench.push(data)); - - this.update = function () { - if (!workBench.length) return true; - - let work = workBench.splice(0, workBench.length); - work.filter(data => typeof data === "object" && data) - .forEach(function (data) { - Object.keys(data).forEach(function (item) { - myEvents.emit(item, data[item]); // Trigger those events - }); - }); - - return true; // always, to keep looping; - }; - }).update; - - module.exports = { - on: myEvents.on, - off: myEvents.off, - once: myEvents.once, - send: what => scriptBroadcast(what) - }; + const myEvents = new (require("Events")); + const Worker = require("Worker"); + + + Worker.runInBackground.messaging = (new function () { + const workBench = []; + addEventListener("scriptmsg", data => workBench.push(data)); + + this.update = function () { + if (!workBench.length) return true; + + let work = workBench.splice(0, workBench.length); + work.filter(data => typeof data === "object" && data) + .forEach(function (data) { + Object.keys(data).forEach(function (item) { + myEvents.emit(item, data[item]); // Trigger those events + }); + }); + + return true; // always, to keep looping; + }; + }).update; + + module.exports = { + on: myEvents.on, + off: myEvents.off, + once: myEvents.once, + send: what => scriptBroadcast(what) + }; })(module, require); diff --git a/d2bs/kolbot/libs/modules/Override.js b/d2bs/kolbot/libs/modules/Override.js index 030b35042..a7b496177 100644 --- a/d2bs/kolbot/libs/modules/Override.js +++ b/d2bs/kolbot/libs/modules/Override.js @@ -1,59 +1,59 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; } - return to.concat(ar || Array.prototype.slice.call(from)); + } + return to.concat(ar || Array.prototype.slice.call(from)); }; (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"], 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"], factory); + } })(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.Override = void 0; - var Override = /** @class */ (function () { - function Override(target, original, method) { - this.target = target; - if (typeof original !== 'string') { - this.original = original; - this.key = Object.keys(target).find(function (key) { return target[key] === original; }); - } - else { - this.original = undefined; - this.key = original; - } - this.method = method; - Override.all.push(this); + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Override = void 0; + var Override = /** @class */ (function () { + function Override(target, original, method) { + this.target = target; + if (typeof original !== 'string') { + this.original = original; + this.key = Object.keys(target).find(function (key) { return target[key] === original; }); + } + else { + this.original = undefined; + this.key = original; + } + this.method = method; + Override.all.push(this); + } + Override.prototype.apply = function () { + var _a = this, target = _a.target, key = _a.key, method = _a.method, original = _a.original; + target[key] = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; } - Override.prototype.apply = function () { - var _a = this, target = _a.target, key = _a.key, method = _a.method, original = _a.original; - target[key] = function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return method.apply(this, __spreadArray([original && original.bind(this)], args, true)); - }; - }; - Override.prototype.rollback = function () { - var _a = this, target = _a.target, key = _a.key, original = _a.original; - if (original) { - target[key] = original; - } - else { - delete target[key]; - } - }; - Override.all = []; - return Override; - }()); - exports.Override = Override; + return method.apply(this, __spreadArray([original && original.bind(this)], args, true)); + }; + }; + Override.prototype.rollback = function () { + var _a = this, target = _a.target, key = _a.key, original = _a.original; + if (original) { + target[key] = original; + } + else { + delete target[key]; + } + }; + Override.all = []; + return Override; + }()); + exports.Override = Override; }); diff --git a/d2bs/kolbot/libs/modules/Promise.js b/d2bs/kolbot/libs/modules/Promise.js index 61aa0282a..2d4c2b91d 100644 --- a/d2bs/kolbot/libs/modules/Promise.js +++ b/d2bs/kolbot/libs/modules/Promise.js @@ -5,95 +5,95 @@ */ (function (module, require) { - const Worker = require("Worker"); - /** + const Worker = require("Worker"); + /** * * @param {function({resolve},{reject}):boolean} callback * @constructor */ - const Promise = module.exports = function (callback) { - typeof Promise.__promiseCounter === "undefined" && (Promise.__promiseCounter = 0); - - this._after = []; - this._catchers = []; - this._finally = []; - this.stopped = false; - this.value = this; - const self = this; - - const final = function () { - typeof self._finally !== "undefined" && self._finally.forEach(function (callback) { - return callback(self.value); - }); - }, resolve = function (result) { - self.value = result; - self.stopped = true; - typeof self._after !== "undefined" && self._after.forEach(callback => Worker.push(function () { - return callback(result); - })); - Worker.push(final); - }, - reject = function (e) { - self.stopped = true; - typeof self._catchers !== "undefined" && self._catchers.forEach(callback => Worker.push(function () { - return callback(e); - })); - if (!Array.isArray(self._catchers) || !self._catchers.length) Misc.errorReport(e || (new Error)); - Worker.push(final); - }; - - - if (this.__proto__.constructor !== Promise) { - print((new Error).stack); - throw new Error("Promise must be called with 'new' operator!"); - } - - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf - // override the valueOf as primitive value function - this.valueOf = () => self.stopped ? self.value : self; - - this.then = function (handler) { - typeof self._after !== "undefined" && (self._after = []); - self._after.push(handler); - - return self; - }; - - this.catch = function (handler) { - typeof self._catchers !== "undefined" && (self._catchers = []); - self._catchers.push(handler); - - return self; - }; - - this.finally = function (handler) { - typeof self._finally !== "undefined" && (self._finally = []); - self._finally.push(handler); - - return self; - }; - - Worker.runInBackground["promise__" + (++Promise.__promiseCounter)] = function () { - try { - callback(resolve, reject); - } catch (e) { - reject(e); - } - return !self.stopped; - }; - }; - - /** + const Promise = module.exports = function (callback) { + typeof Promise.__promiseCounter === "undefined" && (Promise.__promiseCounter = 0); + + this._after = []; + this._catchers = []; + this._finally = []; + this.stopped = false; + this.value = this; + const self = this; + + const final = function () { + typeof self._finally !== "undefined" && self._finally.forEach(function (callback) { + return callback(self.value); + }); + }, resolve = function (result) { + self.value = result; + self.stopped = true; + typeof self._after !== "undefined" && self._after.forEach(callback => Worker.push(function () { + return callback(result); + })); + Worker.push(final); + }, + reject = function (e) { + self.stopped = true; + typeof self._catchers !== "undefined" && self._catchers.forEach(callback => Worker.push(function () { + return callback(e); + })); + if (!Array.isArray(self._catchers) || !self._catchers.length) Misc.errorReport(e || (new Error)); + Worker.push(final); + }; + + + if (this.__proto__.constructor !== Promise) { + print((new Error).stack); + throw new Error("Promise must be called with 'new' operator!"); + } + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf + // override the valueOf as primitive value function + this.valueOf = () => self.stopped ? self.value : self; + + this.then = function (handler) { + typeof self._after !== "undefined" && (self._after = []); + self._after.push(handler); + + return self; + }; + + this.catch = function (handler) { + typeof self._catchers !== "undefined" && (self._catchers = []); + self._catchers.push(handler); + + return self; + }; + + this.finally = function (handler) { + typeof self._finally !== "undefined" && (self._finally = []); + self._finally.push(handler); + + return self; + }; + + Worker.runInBackground["promise__" + (++Promise.__promiseCounter)] = function () { + try { + callback(resolve, reject); + } catch (e) { + reject(e); + } + return !self.stopped; + }; + }; + + /** * @description wait for an array of promises to be ran. * @param promises Array */ - Promise.all = function (promises) { - while (promises.some(x => !x.stopped)) { - delay(); - } - }; + Promise.all = function (promises) { + while (promises.some(x => !x.stopped)) { + delay(); + } + }; - Promise.allSettled = (promises) => new Promise(resolve => promises.every(x => x.stopped) && resolve(promises)); + Promise.allSettled = (promises) => new Promise(resolve => promises.every(x => x.stopped) && resolve(promises)); })(module, require); diff --git a/d2bs/kolbot/libs/modules/SimpleParty.js b/d2bs/kolbot/libs/modules/SimpleParty.js index 5c3eb3939..d85a029a9 100644 --- a/d2bs/kolbot/libs/modules/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/SimpleParty.js @@ -1,155 +1,162 @@ (function (module, require) { - const Worker = require("Worker"); - const NO_PARTY = 65535; - const PARTY_MEMBER = 1; - const ACCEPTABLE = 2; - const INVITED = 4; - const BUTTON_INVITE_ACCEPT_CANCEL = 2; - const BUTTON_LEAVE_PARTY = 3; - const BUTTON_HOSTILE = 4; - - print("ÿc2Kolbotÿc0 :: Simple party running"); - - const SimpleParty = {}; - - SimpleParty.biggestPartyId = function () { - let uniqueParties = []; - // Or add it and return the value - for (let party = getParty(); party.getNext();) { - ( - // Find this party - uniqueParties.find(u => u.partyid === party.partyid) - // Or create an instance of it - || ((uniqueParties.push({ - partyid: party.partyid, - used: 0 - }) && false) || uniqueParties[uniqueParties.length - 1]) - // Once we have the party object, increase field used - ).used++; - } - - // Filter out no party, if another party is found - uniqueParties.some(u => u.partyid !== NO_PARTY) && (uniqueParties = uniqueParties.filter(u => u.partyid !== NO_PARTY)); - - return (uniqueParties.sort((a, b) => b.used - a.used /*b-a = desc*/).first() || {partyid: -1}).partyid; - }; - - SimpleParty.acceptFirst = function () { - const toMd5Int = what => parseInt(md5(what).substr(0, 4), 16); //ToDo; do something with game number here - const names = []; - for (let party = getParty(); party.getNext();) { - if (party.partyid === NO_PARTY) { - names.push(party.name); - } - } - return names.filter(n => n !== me.name /*cant accept yourself ;)*/).sort((a, b) => toMd5Int(a) - toMd5Int(b)).first(); - }; - - SimpleParty.getFirstPartyMember = function () { - let myPartyId = ((() => (getParty() || {partyid: 0}).partyid))(); - for (let party = getParty(); party.getNext();) { - if (party.partyid === myPartyId && party.name !== me.charname) { - return party; - } - } - return undefined; - }; - - SimpleParty.invite = function (name) { - - for (let party = getParty(); party.getNext();) { - // If party member is - if (party.name === name && party.partyflag !== ACCEPTABLE && party.partyflag !== PARTY_MEMBER && party.partyid === NO_PARTY) { - clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); // Press the invite button - return true; - } - } - return false; - }; - - SimpleParty.timer = 0; - - if (getScript(true).name.toLowerCase() === "default.dbj") { - (Worker.runInBackground.party = (function () {// For now, we gonna do this in game with a single party - SimpleParty.timer = getTickCount(); - return function () { - // Set timer back on 3 seconds, or reset it and continue - if ((getTickCount() - SimpleParty.timer) < 3000 || (SimpleParty.timer = getTickCount()) && false) { - return true; - } - - if (Config.PublicMode !== true) { // Public mode 1/2/3 dont count. This is SimplyParty - return true; - } - - const myPartyId = ((() => (getParty() || {partyid: 0}).partyid))(); - if (!myPartyId) { - return true; // party ain't up yet - } - - const biggestPartyId = SimpleParty.biggestPartyId(); - - for (let party = getParty(), acceptFirst; party && party.getNext();) { - if (!(party && typeof party === "object")) { - continue; - } - - if (!(party.hasOwnProperty("life"))) { - continue; - } // Somehow not a party member - - // Deal with inviting - if ( // If no party is formed, or im member of the biggest party - party.partyflag !== INVITED && // Already invited - party.partyflag !== ACCEPTABLE && // Need to accept invite, so cant invite - party.partyflag !== PARTY_MEMBER && // cant party again with soemone - party.partyid === NO_PARTY // Can only invite someone that isnt in a party - && ( // If im not in a party, only if there is no party - myPartyId === NO_PARTY && biggestPartyId === NO_PARTY - // OR, if im part of the biggest party - || biggestPartyId === myPartyId - ) - ) { - // if player isn't invited, invite - clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); - } - - // Deal with accepting - if ( - party.partyflag === ACCEPTABLE - && myPartyId === NO_PARTY // Can only accept if we are not in a party - && party.partyid === biggestPartyId // Only accept if it is an invite to the biggest party - ) { - // Try to make all bots accept the same char first, to avoid confusion with multiple parties - if (biggestPartyId === NO_PARTY) { - // if acceptFirst isnt set, create it (to cache it, yet generate on demand) - if (!acceptFirst) { - acceptFirst = SimpleParty.acceptFirst(); - } - - if (acceptFirst !== party.name) { - continue; // Ignore party acceptation - } - } - - clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); - } - - // Deal with being in the wrong party. (we want to be in the biggest party) - if ( - party.partyflag === PARTY_MEMBER // We are in the same party - && biggestPartyId !== party.partyid // yet this party isnt the biggest party available - && biggestPartyId !== NO_PARTY // And the biggest party isnt no party - ) { - clickParty(party, BUTTON_LEAVE_PARTY); - } - - } - return true; - }; - })()); - } - - module.exports = SimpleParty; + const Worker = require("Worker"); + const NO_PARTY = 65535; + const PARTY_MEMBER = 1; + const ACCEPTABLE = 2; + const INVITED = 4; + const BUTTON_INVITE_ACCEPT_CANCEL = 2; + const BUTTON_LEAVE_PARTY = 3; + const BUTTON_HOSTILE = 4; + + print("ÿc2Kolbotÿc0 :: Simple party running"); + + const SimpleParty = {}; + + SimpleParty.biggestPartyId = function () { + let uniqueParties = []; + // Or add it and return the value + for (let party = getParty(); party.getNext();) { + ( + // Find this party + uniqueParties.find(u => u.partyid === party.partyid) + // Or create an instance of it + || ((uniqueParties.push({ + partyid: party.partyid, + used: 0 + }) && false) || uniqueParties[uniqueParties.length - 1]) + // Once we have the party object, increase field used + ).used++; + } + + // Filter out no party, if another party is found + if (uniqueParties.some(u => u.partyid !== NO_PARTY)) { + (uniqueParties = uniqueParties.filter(u => u.partyid !== NO_PARTY)); + } + return (uniqueParties.sort((a, b) => b.used - a.used /*b-a = desc*/).first() || { partyid: -1 }).partyid; + }; + + SimpleParty.acceptFirst = function () { + const toMd5Int = what => parseInt(md5(what).substr(0, 4), 16); //ToDo; do something with game number here + const names = []; + for (let party = getParty(); party.getNext();) { + if (party.partyid === NO_PARTY) { + names.push(party.name); + } + } + return names + .filter(n => n !== me.name /*cant accept yourself ;)*/) + .sort((a, b) => toMd5Int(a) - toMd5Int(b)) + .first(); + }; + + SimpleParty.getFirstPartyMember = function () { + let myPartyId = ((() => (getParty() || { partyid: 0 }).partyid))(); + for (let party = getParty(); party.getNext();) { + if (party.partyid === myPartyId && party.name !== me.charname) { + return party; + } + } + return undefined; + }; + + SimpleParty.invite = function (name) { + + for (let party = getParty(); party.getNext();) { + // If party member is + if (party.name === name + && party.partyflag !== ACCEPTABLE + && party.partyflag !== PARTY_MEMBER + && party.partyid === NO_PARTY) { + clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); // Press the invite button + return true; + } + } + return false; + }; + + SimpleParty.timer = 0; + + if (getScript(true).name.toLowerCase() === "default.dbj") { + (Worker.runInBackground.party = (function () {// For now, we gonna do this in game with a single party + SimpleParty.timer = getTickCount(); + return function () { + // Set timer back on 3 seconds, or reset it and continue + if ((getTickCount() - SimpleParty.timer) < 3000 || (SimpleParty.timer = getTickCount()) && false) { + return true; + } + + if (Config.PublicMode !== true) { // Public mode 1/2/3 dont count. This is SimplyParty + return true; + } + + const myPartyId = ((() => (getParty() || { partyid: 0 }).partyid))(); + if (!myPartyId) { + return true; // party ain't up yet + } + + const biggestPartyId = SimpleParty.biggestPartyId(); + + for (let party = getParty(), acceptFirst; party && party.getNext();) { + if (!(party && typeof party === "object")) { + continue; + } + + if (!(party.hasOwnProperty("life"))) { + continue; + } // Somehow not a party member + + // Deal with inviting + if ( // If no party is formed, or im member of the biggest party + party.partyflag !== INVITED && // Already invited + party.partyflag !== ACCEPTABLE && // Need to accept invite, so cant invite + party.partyflag !== PARTY_MEMBER && // cant party again with soemone + party.partyid === NO_PARTY // Can only invite someone that isnt in a party + && ( // If im not in a party, only if there is no party + myPartyId === NO_PARTY && biggestPartyId === NO_PARTY + // OR, if im part of the biggest party + || biggestPartyId === myPartyId + ) + ) { + // if player isn't invited, invite + clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); + } + + // Deal with accepting + if ( + party.partyflag === ACCEPTABLE + && myPartyId === NO_PARTY // Can only accept if we are not in a party + && party.partyid === biggestPartyId // Only accept if it is an invite to the biggest party + ) { + // Try to make all bots accept the same char first, to avoid confusion with multiple parties + if (biggestPartyId === NO_PARTY) { + // if acceptFirst isnt set, create it (to cache it, yet generate on demand) + if (!acceptFirst) { + acceptFirst = SimpleParty.acceptFirst(); + } + + if (acceptFirst !== party.name) { + continue; // Ignore party acceptation + } + } + + clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); + } + + // Deal with being in the wrong party. (we want to be in the biggest party) + if ( + party.partyflag === PARTY_MEMBER // We are in the same party + && biggestPartyId !== party.partyid // yet this party isnt the biggest party available + && biggestPartyId !== NO_PARTY // And the biggest party isnt no party + ) { + clickParty(party, BUTTON_LEAVE_PARTY); + } + + } + return true; + }; + })()); + } + + module.exports = SimpleParty; })(module, require); diff --git a/d2bs/kolbot/libs/modules/Socket.js b/d2bs/kolbot/libs/modules/Socket.js index 7dbadabad..6c4ea5065 100644 --- a/d2bs/kolbot/libs/modules/Socket.js +++ b/d2bs/kolbot/libs/modules/Socket.js @@ -5,53 +5,53 @@ (function (module, require, buildinSock) { - const Worker = require("Worker"); - const Events = require("Events"); - - /** @constructor Socket*/ - function Socket(hostname, port) { - typeof Socket.__socketCounter === "undefined" && (Socket.__socketCounter = 0); - this.connected = false; - const myEvents = new Events; - this.connect = () => (this.socket = buildinSock.open(hostname, port)) && (this.connected = true) && this; - - this.on = myEvents.on; - this.off = myEvents.off; - this.once = myEvents.once; - - const close = () => { - this.socket = null; - this.connected = false; - myEvents.emit("close", this); - }; - - this.recv = () => { - if (!this.connected || !this.socket || !this.socket.readable) return; - - const data = (() => { - try { - return this.socket.read(); - } catch (e) { - close(); - } - return undefined; - })(); - - data && myEvents.emit("data", data); - }; - - this.send = (data) => { - if (!data || !this.socket) return; - - try { - this.socket.send(data); - } catch (e) { - close(); - } - }; - - Worker.runInBackground["__socket__" + (++Socket.__socketCounter)] = () => this.recv() || this.send() || true; - } - - module.exports = Socket; + const Worker = require("Worker"); + const Events = require("Events"); + + /** @constructor Socket*/ + function Socket(hostname, port) { + typeof Socket.__socketCounter === "undefined" && (Socket.__socketCounter = 0); + this.connected = false; + const myEvents = new Events; + this.connect = () => (this.socket = buildinSock.open(hostname, port)) && (this.connected = true) && this; + + this.on = myEvents.on; + this.off = myEvents.off; + this.once = myEvents.once; + + const close = () => { + this.socket = null; + this.connected = false; + myEvents.emit("close", this); + }; + + this.recv = () => { + if (!this.connected || !this.socket || !this.socket.readable) return; + + const data = (() => { + try { + return this.socket.read(); + } catch (e) { + close(); + } + return undefined; + })(); + + data && myEvents.emit("data", data); + }; + + this.send = (data) => { + if (!data || !this.socket) return; + + try { + this.socket.send(data); + } catch (e) { + close(); + } + }; + + Worker.runInBackground["__socket__" + (++Socket.__socketCounter)] = () => this.recv() || this.send() || true; + } + + module.exports = Socket; }).call(null, module, require, Socket); diff --git a/d2bs/kolbot/libs/modules/Team.js b/d2bs/kolbot/libs/modules/Team.js index c2228bce5..2624d773b 100644 --- a/d2bs/kolbot/libs/modules/Team.js +++ b/d2bs/kolbot/libs/modules/Team.js @@ -7,158 +7,161 @@ (function (threadType) { - const others = []; - - const myEvents = new (require("Events")); - const Worker = require("Worker"); - const Messaging = require("Messaging"); - const defaultCopyDataMode = 0xC0FFFEE; - - const Team = { - on: myEvents.on, - off: myEvents.off, - once: myEvents.once, - send: function (who, what, mode = defaultCopyDataMode) { - what.profile = me.windowtitle; - return sendCopyData(null, who, mode || defaultCopyDataMode, JSON.stringify(what)); - }, - broadcast: (what, mode) => { - what.profile = me.windowtitle; - return others.forEach(other => sendCopyData(null, other.profile, mode || defaultCopyDataMode, JSON.stringify(what))); - }, - broadcastInGame: (what, mode) => { - what.profile = me.windowtitle; - others.forEach(function (other) { - for (const party = getParty(); party && party.getNext();) { - typeof party === "object" && party && party.hasOwnProperty("name") && party.name === other.name && sendCopyData(null, other.profile, mode || defaultCopyDataMode, JSON.stringify(what)); - } - }); - } - }; - - if (threadType === "thread") { - print("ÿc2Kolbotÿc0 :: Team thread started"); - - Messaging.on("Team", data => ( - typeof data === "object" && data && data.hasOwnProperty("call") && Team[data.call].apply(Team, data.hasOwnProperty("args") && data.args || []) - )); - - Worker.runInBackground.copydata = (new function () { - const workBench = []; - const updateOtherProfiles = function () { - const fileList = dopen("data/").getFiles(); - fileList && fileList.forEach(function (filename) { - let newContent, obj, profile = filename.split("").reverse().splice(5).reverse().join(""); // strip the last 5 chars (.json) = 5 chars - - - if (profile === me.windowtitle || !filename.endsWith(".json")) return; - try { - newContent = FileTools.readText("data/" + filename); - if (!newContent) return; // no content - } catch (e) { - print("Can't read: `" + "data/" + filename + "`"); - } - - - try { // try to convert to an object - obj = JSON.parse(newContent); - } catch (e) { - return; - } - - let other; - for (let i = 0, tmp; i < others.length; i++) { - tmp = others[i]; - if (tmp.hasOwnProperty("profile") && tmp.profile === profile) { - other = tmp; - break; - } - } - - if (!other) { - others.push(obj); - other = others[others.length - 1]; - } - - other.profile = profile; - Object.keys(obj).map(key => other[key] = obj[key]); - }); - }; - addEventListener("copydata", (mode, data) => workBench.push({mode: mode, data: data})); - - let timer = getTickCount() - Math.round((Math.random() * 2500) + 1000); // start with 3 seconds off - this.update = function () { - if (!((getTickCount() - timer) < 3500)) { // only ever 3 seconds update the entire team - timer = getTickCount(); - updateOtherProfiles(); - } - - // nothing to do? next - if (!workBench.length) return true; - const emit = workBench.splice(0, workBench.length).map( - function (obj) { // Convert to object, if we can - let data = obj.data; - try { - data = JSON.parse(data); - } catch (e) { - /* Dont care if we cant*/ - return {}; - } - return {mode: obj.mode, data: data}; - }) - .filter(obj => typeof obj === "object" && obj) - .filter(obj => typeof obj.data === "object" && obj.data) - .filter(obj => typeof obj.mode === "number" && obj.mode); - emit.length && Messaging.send({ - Team: { - emit: emit - } - }); - return true; // always, to keep looping; - }; - }).update; - - while (true) { - delay(1000); - } - - } else { - (function (module) { - const localTeam = module.exports = Team; // <-- some get overridden, but this still works for auto completion in your IDE - - // Filter out all Team functions that are linked to myEvent - Object.keys(Team) - .filter(key => !myEvents.hasOwnProperty(key) && typeof Team[key] === "function") - .forEach(key => module.exports[key] = (...args) => Messaging.send({ - Team: { - call: key, - args: args - } - })); - - Messaging.on("Team", msg => - typeof msg === "object" - && msg - && msg.hasOwnProperty("emit") - && Array.isArray(msg.emit) - && msg.emit.forEach(function (obj) { - - // Registered events on the mode - myEvents.emit(obj.mode, obj.data); - - // Only if data is set - typeof obj.data === "object" && obj.data && Object.keys(obj.data).forEach(function (item) { - - // For each item in the object, trigger an event - obj.data[item].reply = (what, mode) => localTeam.send(obj.data.profile, what, mode); - - // Registered events on a data item - myEvents.emit(item, obj.data[item]); - }); - }) - ); - })(module); - } + const others = []; + + const myEvents = new (require("Events")); + const Worker = require("Worker"); + const Messaging = require("Messaging"); + const defaultCopyDataMode = 0xC0FFFEE; + + const Team = { + on: myEvents.on, + off: myEvents.off, + once: myEvents.once, + send: function (who, what, mode = defaultCopyDataMode) { + what.profile = me.windowtitle; + return sendCopyData(null, who, mode || defaultCopyDataMode, JSON.stringify(what)); + }, + broadcast: (what, mode) => { + what.profile = me.windowtitle; + return others + .forEach(other => sendCopyData(null, other.profile, mode || defaultCopyDataMode, JSON.stringify(what))); + }, + broadcastInGame: (what, mode) => { + what.profile = me.windowtitle; + others.forEach(function (other) { + for (const party = getParty(); party && party.getNext();) { + if (typeof party === "object" && party && party.hasOwnProperty("name") && party.name === other.name) { + sendCopyData(null, other.profile, mode || defaultCopyDataMode, JSON.stringify(what)); + } + } + }); + } + }; + + if (threadType === "thread") { + print("ÿc2Kolbotÿc0 :: Team thread started"); + + Messaging.on("Team", data => ( + typeof data === "object" && data && data.hasOwnProperty("call") && Team[data.call].apply(Team, data.hasOwnProperty("args") && data.args || []) + )); + + Worker.runInBackground.copydata = (new function () { + const workBench = []; + const updateOtherProfiles = function () { + const fileList = dopen("data/").getFiles(); + fileList && fileList.forEach(function (filename) { + let newContent, obj, profile = filename.split("").reverse().splice(5).reverse().join(""); // strip the last 5 chars (.json) = 5 chars + + + if (profile === me.windowtitle || !filename.endsWith(".json")) return; + try { + newContent = FileTools.readText("data/" + filename); + if (!newContent) return; // no content + } catch (e) { + print("Can't read: `" + "data/" + filename + "`"); + } + + + try { // try to convert to an object + obj = JSON.parse(newContent); + } catch (e) { + return; + } + + let other; + for (let i = 0, tmp; i < others.length; i++) { + tmp = others[i]; + if (tmp.hasOwnProperty("profile") && tmp.profile === profile) { + other = tmp; + break; + } + } + + if (!other) { + others.push(obj); + other = others[others.length - 1]; + } + + other.profile = profile; + Object.keys(obj).map(key => other[key] = obj[key]); + }); + }; + addEventListener("copydata", (mode, data) => workBench.push({ mode: mode, data: data })); + + let timer = getTickCount() - Math.round((Math.random() * 2500) + 1000); // start with 3 seconds off + this.update = function () { + if (!((getTickCount() - timer) < 3500)) { // only ever 3 seconds update the entire team + timer = getTickCount(); + updateOtherProfiles(); + } + + // nothing to do? next + if (!workBench.length) return true; + const emit = workBench.splice(0, workBench.length).map( + function (obj) { // Convert to object, if we can + let data = obj.data; + try { + data = JSON.parse(data); + } catch (e) { + /* Dont care if we cant*/ + return {}; + } + return { mode: obj.mode, data: data }; + }) + .filter(obj => typeof obj === "object" && obj) + .filter(obj => typeof obj.data === "object" && obj.data) + .filter(obj => typeof obj.mode === "number" && obj.mode); + emit.length && Messaging.send({ + Team: { + emit: emit + } + }); + return true; // always, to keep looping; + }; + }).update; + + while (true) { + delay(1000); + } + + } else { + (function (module) { + const localTeam = module.exports = Team; // <-- some get overridden, but this still works for auto completion in your IDE + + // Filter out all Team functions that are linked to myEvent + Object.keys(Team) + .filter(key => !myEvents.hasOwnProperty(key) && typeof Team[key] === "function") + .forEach(key => module.exports[key] = (...args) => Messaging.send({ + Team: { + call: key, + args: args + } + })); + + Messaging.on("Team", msg => + typeof msg === "object" + && msg + && msg.hasOwnProperty("emit") + && Array.isArray(msg.emit) + && msg.emit.forEach(function (obj) { + + // Registered events on the mode + myEvents.emit(obj.mode, obj.data); + + // Only if data is set + typeof obj.data === "object" && obj.data && Object.keys(obj.data).forEach(function (item) { + + // For each item in the object, trigger an event + obj.data[item].reply = (what, mode) => localTeam.send(obj.data.profile, what, mode); + + // Registered events on a data item + myEvents.emit(item, obj.data[item]); + }); + }) + ); + })(module); + } })(getScript.startAsThread()); diff --git a/d2bs/kolbot/libs/modules/UnitInfo.js b/d2bs/kolbot/libs/modules/UnitInfo.js index 522083683..e1eb5c744 100644 --- a/d2bs/kolbot/libs/modules/UnitInfo.js +++ b/d2bs/kolbot/libs/modules/UnitInfo.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename UnitInfo.js * @author kolton, theBGuy @@ -8,252 +9,252 @@ include("core/prototypes.js"); (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.UnitInfo = 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.UnitInfo = factory(); + } }(this, function () { - /** + /** * @constructor */ - function UnitInfo () { - /** + function UnitInfo () { + /** * screen coordinate for info box * @private * @type {number} */ - this.x = 200; + this.x = 200; - /** + /** * screen coordinate for info box * @private * @type {number} */ - this.y = 250; + this.y = 250; - /** + /** * @private * @type {any[]} */ - this.hooks = []; + this.hooks = []; - /** + /** * @private * @type {number | null} */ - this.currentGid = null; + this.currentGid = null; - /** + /** * @private * @type {boolean} */ - this.cleared = true; + this.cleared = true; - /** + /** * @private * @type {{ x: number, y: number }} */ - this.resfix = { - x: (me.screensize ? 0 : -160), - y: (me.screensize ? 0 : -120) - }; - } + this.resfix = { + x: (me.screensize ? 0 : -160), + y: (me.screensize ? 0 : -120) + }; + } - /** + /** * Create info based on unit type * @param {Unit} unit */ - UnitInfo.prototype.createInfo = function (unit) { - if (typeof unit === "undefined") { - this.remove(); - } else { - // same unit, no changes so skip - if (this.currentGid === unit.gid) return; - // some hooks left over, remove them - this.hooks.length > 0 && this.remove(); - // set new gid - this.currentGid = unit.gid; - - switch (unit.type) { - case sdk.unittype.Player: - this.playerInfo(unit); - - break; - case sdk.unittype.Monster: - this.monsterInfo(unit); - - break; - case sdk.unittype.Object: - case sdk.unittype.Stairs: - this.objectInfo(unit); - - break; - case sdk.unittype.Item: - this.itemInfo(unit); - - break; - } - } - - }; - - /** + UnitInfo.prototype.createInfo = function (unit) { + if (typeof unit === "undefined") { + this.remove(); + } else { + // same unit, no changes so skip + if (this.currentGid === unit.gid) return; + // some hooks left over, remove them + this.hooks.length > 0 && this.remove(); + // set new gid + this.currentGid = unit.gid; + + switch (unit.type) { + case sdk.unittype.Player: + this.playerInfo(unit); + + break; + case sdk.unittype.Monster: + this.monsterInfo(unit); + + break; + case sdk.unittype.Object: + case sdk.unittype.Stairs: + this.objectInfo(unit); + + break; + case sdk.unittype.Item: + this.itemInfo(unit); + + break; + } + } + + }; + + /** * Check that selected unit is still valid */ - UnitInfo.prototype.check = function () { - // make sure things got cleaned up properly if we are supposedly cleared - if (this.hooks.length === 0 && this.cleared) return; - // don't deal with this when an item is on our cursor - if (me.itemoncursor) return; - let unit = Game.getSelectedUnit(); - if (typeof unit === "undefined" || unit.gid !== this.currentGid) { - this.remove(); - } - }; - - /** + UnitInfo.prototype.check = function () { + // make sure things got cleaned up properly if we are supposedly cleared + if (this.hooks.length === 0 && this.cleared) return; + // don't deal with this when an item is on our cursor + if (me.itemoncursor) return; + let unit = Game.getSelectedUnit(); + if (typeof unit === "undefined" || unit.gid !== this.currentGid) { + this.remove(); + } + }; + + /** * @private * @param {Player} unit */ - UnitInfo.prototype.playerInfo = function (unit) { - let string; - let frameXsize = 0; - let frameYsize = 20; - let items = unit.getItemsEx(); - - this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y, 4, 13, 2)); - - if (items.length) { - this.hooks.push(new Text("Equipped items:", this.x, this.y + 15, 4, 13, 2)); - frameYsize += 15; - - items.forEach((item, i) => { - if (item.runeword) { - string = item.fname.split("\n")[1] + "ÿc0 " + item.fname.split("\n")[0]; - } else { - string = Item.color(item, false) + (item.quality > sdk.items.quality.Magic && item.identified - ? item.fname.split("\n").reverse()[0].replace("ÿc4", "") - : item.name); - } - - this.hooks.push(new Text(string, this.x, this.y + (i + 2) * 15, 0, 13, 2)); - string.length > frameXsize && (frameXsize = string.length); - frameYsize += 15; - }); - } - - this.cleared = false; - - this.hooks.push(new Box(this.x + 2, this.y - 15, Math.round(frameXsize * 7.5) - 4, frameYsize, 0x0, 1, 2)); - this.hooks.push(new Frame(this.x, this.y - 15, Math.round(frameXsize * 7.5), frameYsize, 2)); - this.hooks[this.hooks.length - 2].zorder = 0; - }; - - /** + UnitInfo.prototype.playerInfo = function (unit) { + let string; + let frameXsize = 0; + let frameYsize = 20; + let items = unit.getItemsEx(); + + this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y, 4, 13, 2)); + + if (items.length) { + this.hooks.push(new Text("Equipped items:", this.x, this.y + 15, 4, 13, 2)); + frameYsize += 15; + + items.forEach((item, i) => { + if (item.runeword) { + string = item.fname.split("\n")[1] + "ÿc0 " + item.fname.split("\n")[0]; + } else { + string = Item.color(item, false) + (item.quality > sdk.items.quality.Magic && item.identified + ? item.fname.split("\n").reverse()[0].replace("ÿc4", "") + : item.name); + } + + this.hooks.push(new Text(string, this.x, this.y + (i + 2) * 15, 0, 13, 2)); + string.length > frameXsize && (frameXsize = string.length); + frameYsize += 15; + }); + } + + this.cleared = false; + + this.hooks.push(new Box(this.x + 2, this.y - 15, Math.round(frameXsize * 7.5) - 4, frameYsize, 0x0, 1, 2)); + this.hooks.push(new Frame(this.x, this.y - 15, Math.round(frameXsize * 7.5), frameYsize, 2)); + this.hooks[this.hooks.length - 2].zorder = 0; + }; + + /** * @private * @param {Monster} unit */ - UnitInfo.prototype.monsterInfo = function (unit) { - let frameYsize = 125; - - this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y, 4, 13, 2)); - this.hooks.push(new Text("HP percent: ÿc0" + Math.round(unit.hp * 100 / 128), this.x, this.y + 15, 4, 13, 2)); - this.hooks.push(new Text("Fire resist: ÿc0" + unit.getStat(sdk.stats.FireResist), this.x, this.y + 30, 4, 13, 2)); - this.hooks.push(new Text("Cold resist: ÿc0" + unit.getStat(sdk.stats.ColdResist), this.x, this.y + 45, 4, 13, 2)); - this.hooks.push(new Text("Lightning resist: ÿc0" + unit.getStat(sdk.stats.LightResist), this.x, this.y + 60, 4, 13, 2)); - this.hooks.push(new Text("Poison resist: ÿc0" + unit.getStat(sdk.stats.PoisonResist), this.x, this.y + 75, 4, 13, 2)); - this.hooks.push(new Text("Physical resist: ÿc0" + unit.getStat(sdk.stats.DamageResist), this.x, this.y + 90, 4, 13, 2)); - this.hooks.push(new Text("Magic resist: ÿc0" + unit.getStat(sdk.stats.MagicResist), this.x, this.y + 105, 4, 13, 2)); - - this.cleared = false; - - this.hooks.push(new Box(this.x + 2, this.y - 15, 136, frameYsize, 0x0, 1, 2)); - this.hooks.push(new Frame(this.x, this.y - 15, 140, frameYsize, 2)); - this.hooks[this.hooks.length - 2].zorder = 0; - }; - - /** + UnitInfo.prototype.monsterInfo = function (unit) { + let frameYsize = 125; + + this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y, 4, 13, 2)); + this.hooks.push(new Text("HP percent: ÿc0" + Math.round(unit.hp * 100 / 128), this.x, this.y + 15, 4, 13, 2)); + this.hooks.push(new Text("Fire resist: ÿc0" + unit.getStat(sdk.stats.FireResist), this.x, this.y + 30, 4, 13, 2)); + this.hooks.push(new Text("Cold resist: ÿc0" + unit.getStat(sdk.stats.ColdResist), this.x, this.y + 45, 4, 13, 2)); + this.hooks.push(new Text("Lightning resist: ÿc0" + unit.getStat(sdk.stats.LightResist), this.x, this.y + 60, 4, 13, 2)); + this.hooks.push(new Text("Poison resist: ÿc0" + unit.getStat(sdk.stats.PoisonResist), this.x, this.y + 75, 4, 13, 2)); + this.hooks.push(new Text("Physical resist: ÿc0" + unit.getStat(sdk.stats.DamageResist), this.x, this.y + 90, 4, 13, 2)); + this.hooks.push(new Text("Magic resist: ÿc0" + unit.getStat(sdk.stats.MagicResist), this.x, this.y + 105, 4, 13, 2)); + + this.cleared = false; + + this.hooks.push(new Box(this.x + 2, this.y - 15, 136, frameYsize, 0x0, 1, 2)); + this.hooks.push(new Frame(this.x, this.y - 15, 140, frameYsize, 2)); + this.hooks[this.hooks.length - 2].zorder = 0; + }; + + /** * @private * @param {ItemUnit} unit */ - UnitInfo.prototype.itemInfo = function (unit) { - let xpos = 60; - let ypos = (me.getMerc() ? 80 : 20) + (-1 * this.resfix.y); - let frameYsize = 65; + UnitInfo.prototype.itemInfo = function (unit) { + let xpos = 60; + let ypos = (me.getMerc() ? 80 : 20) + (-1 * this.resfix.y); + let frameYsize = 65; - this.hooks.push(new Text("Code: ÿc0" + unit.code, xpos, ypos + 0, 4, 13, 2)); - this.hooks.push(new Text("Classid: ÿc0" + unit.classid, xpos, ypos + 15, 4, 13, 2)); - this.hooks.push(new Text("Item Type: ÿc0" + unit.itemType, xpos, ypos + 30, 4, 13, 2)); - this.hooks.push(new Text("Item level: ÿc0" + unit.ilvl, xpos, ypos + 45, 4, 13, 2)); + this.hooks.push(new Text("Code: ÿc0" + unit.code, xpos, ypos + 0, 4, 13, 2)); + this.hooks.push(new Text("Classid: ÿc0" + unit.classid, xpos, ypos + 15, 4, 13, 2)); + this.hooks.push(new Text("Item Type: ÿc0" + unit.itemType, xpos, ypos + 30, 4, 13, 2)); + this.hooks.push(new Text("Item level: ÿc0" + unit.ilvl, xpos, ypos + 45, 4, 13, 2)); - this.cleared = false; - this.socketedItems = unit.getItemsEx(); + this.cleared = false; + this.socketedItems = unit.getItemsEx(); - if (this.socketedItems.length) { - this.hooks.push(new Text("Socketed with:", xpos, ypos + 60, 4, 13, 2)); - frameYsize += 15; + if (this.socketedItems.length) { + this.hooks.push(new Text("Socketed with:", xpos, ypos + 60, 4, 13, 2)); + frameYsize += 15; - for (let i = 0; i < this.socketedItems.length; i += 1) { - this.hooks.push(new Text(this.socketedItems[i].fname.split("\n").reverse().join(" "), xpos, ypos + (i + 5) * 15, 0, 13, 2)); + for (let i = 0; i < this.socketedItems.length; i += 1) { + this.hooks.push(new Text(this.socketedItems[i].fname.split("\n").reverse().join(" "), xpos, ypos + (i + 5) * 15, 0, 13, 2)); - frameYsize += 15; - } - } + frameYsize += 15; + } + } - if (unit.magic && unit.identified) { - this.hooks.push(new Text("Prefix: ÿc0" + unit.prefixnum, xpos, ypos + frameYsize - 5, 4, 13, 2)); - this.hooks.push(new Text("Suffix: ÿc0" + unit.suffixnum, xpos, ypos + frameYsize + 10, 4, 13, 2)); + if (unit.magic && unit.identified) { + this.hooks.push(new Text("Prefix: ÿc0" + unit.prefixnum, xpos, ypos + frameYsize - 5, 4, 13, 2)); + this.hooks.push(new Text("Suffix: ÿc0" + unit.suffixnum, xpos, ypos + frameYsize + 10, 4, 13, 2)); - frameYsize += 30; - } + frameYsize += 30; + } - if (unit.runeword) { - this.hooks.push(new Text("Prefix: ÿc0" + unit.prefixnum, xpos, ypos + frameYsize - 5, 4, 13, 2)); + if (unit.runeword) { + this.hooks.push(new Text("Prefix: ÿc0" + unit.prefixnum, xpos, ypos + frameYsize - 5, 4, 13, 2)); - frameYsize += 15; - } + frameYsize += 15; + } - this.hooks.push(new Box(xpos + 2, ypos - 15, 116, frameYsize, 0x0, 1, 2)); - this.hooks.push(new Frame(xpos, ypos - 15, 120, frameYsize, 2)); - this.hooks[this.hooks.length - 2].zorder = 0; - }; + this.hooks.push(new Box(xpos + 2, ypos - 15, 116, frameYsize, 0x0, 1, 2)); + this.hooks.push(new Frame(xpos, ypos - 15, 120, frameYsize, 2)); + this.hooks[this.hooks.length - 2].zorder = 0; + }; - /** + /** * @private * @param {ObjectUnit} unit */ - UnitInfo.prototype.objectInfo = function (unit) { - let frameYsize = 35; + UnitInfo.prototype.objectInfo = function (unit) { + let frameYsize = 35; - this.hooks.push(new Text("Type: ÿc0" + unit.type, this.x, this.y, 4, 13, 2)); - this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y + 15, 4, 13, 2)); + this.hooks.push(new Text("Type: ÿc0" + unit.type, this.x, this.y, 4, 13, 2)); + this.hooks.push(new Text("Classid: ÿc0" + unit.classid, this.x, this.y + 15, 4, 13, 2)); - if (!!unit.objtype) { - this.hooks.push(new Text((unit.name.toLowerCase().includes("shrine") ? "Type" : "Destination") + ": ÿc0" + unit.objtype, this.x, this.y + 30, 4, 13, 2)); + if (!!unit.objtype) { + this.hooks.push(new Text((unit.name.toLowerCase().includes("shrine") ? "Type" : "Destination") + ": ÿc0" + unit.objtype, this.x, this.y + 30, 4, 13, 2)); - frameYsize += 15; - } + frameYsize += 15; + } - this.cleared = false; + this.cleared = false; - this.hooks.push(new Box(this.x + 2, this.y - 15, 116, frameYsize, 0x0, 1, 2)); - this.hooks.push(new Frame(this.x, this.y - 15, 120, frameYsize, 2)); - this.hooks[this.hooks.length - 2].zorder = 0; - }; + this.hooks.push(new Box(this.x + 2, this.y - 15, 116, frameYsize, 0x0, 1, 2)); + this.hooks.push(new Frame(this.x, this.y - 15, 120, frameYsize, 2)); + this.hooks[this.hooks.length - 2].zorder = 0; + }; - UnitInfo.prototype.remove = function () { - while (this.hooks.length > 0) { - this.hooks.shift().remove(); - } + UnitInfo.prototype.remove = function () { + while (this.hooks.length > 0) { + this.hooks.shift().remove(); + } - this.currentGid = null; - this.cleared = true; - }; + this.currentGid = null; + this.cleared = true; + }; - return UnitInfo; + return UnitInfo; })); diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index aa392acbd..96bc2fda4 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -3,119 +3,120 @@ * @author Jaenster */ -(function(module, require) { - const recursiveCheck = function (stackNumber) { - let stack = new Error().stack.match(/[^\r\n]+/g); - let functionName = stack[stackNumber || 1].substr(0, stack[stackNumber || 1].indexOf("@")); - - for (let i = (stackNumber || 1) + 1; i < stack.length; i++) { - let curFunc = stack[i].substr(0, stack[i].indexOf("@")); - - if (functionName === curFunc) { - return true; - } // recursion appeared - } - - return false; - }; - - let Worker = module.exports = new (function () { - let work = [], workLowPrio = [], self = this; - - this.push = function (newWork) { - return work.push(newWork); - }; - - this.pushLowPrio = function (newWork) { - return workLowPrio.push(newWork); - }; - - const checker = function (val) { - try { - !self.workDisabled && val.length && val.splice(0, val.length).forEach(self.work); - } catch (error) { - if (!error.message.endsWith("too much recursion")) { - throw error; - } // keep on throwing - - print("[ÿc9Warningÿc0] Too much recursion"); - } - }; - - this.check = function () { - return checker(work); - }; - - this.checkLowPrio = function () { - return checker(workLowPrio); - }; - - this.work = function (what) { - return typeof what === "function" && what(self) || (Array.isArray(what) && what.forEach(self.work)); - }; - - /** +(function (module) { + const recursiveCheck = function (stackNumber) { + let stack = new Error().stack.match(/[^\r\n]+/g); + let functionName = stack[stackNumber || 1].substr(0, stack[stackNumber || 1].indexOf("@")); + + for (let i = (stackNumber || 1) + 1; i < stack.length; i++) { + let curFunc = stack[i].substr(0, stack[i].indexOf("@")); + + if (functionName === curFunc) { + return true; + } // recursion appeared + } + + return false; + }; + + const Worker = new (function () { + let work = [], workLowPrio = [], self = this; + + this.push = function (newWork) { + return work.push(newWork); + }; + + this.pushLowPrio = function (newWork) { + return workLowPrio.push(newWork); + }; + + const checker = function (val) { + try { + !self.workDisabled && val.length && val.splice(0, val.length).forEach(self.work); + } catch (error) { + if (!error.message.endsWith("too much recursion")) { + throw error; + } // keep on throwing + + print("[ÿc9Warningÿc0] Too much recursion"); + } + }; + + this.check = function () { + return checker(work); + }; + + this.checkLowPrio = function () { + return checker(workLowPrio); + }; + + this.work = function (what) { + return typeof what === "function" && what(self) || (Array.isArray(what) && what.forEach(self.work)); + }; + + /** * * @param {function({Worker}):boolean} callback */ - this.runInBackground = new Proxy({ processes: {} }, { - set: function (target, name, callback) { - if (target.processes.hasOwnProperty(name)) { - throw new Error("Process " + name + " already exists."); - } - target.processes[name] = { callback: callback, running: true, name: name }; - let proxyCallback = function () { - if (target.processes[name].running) { - target.processes[name].running = (callback() && self.pushLowPrio(proxyCallback) > -1); - } - if (!target.processes[name].running) { - delete target.processes[name]; - } - }; - self.pushLowPrio(proxyCallback); - }, - deleteProperty: function (target, name) { - if (!target.processes.hasOwnProperty(name)) { - throw new Error("Process " + name + " does not exists."); - } - target.processes[name].running = false; - delete target.processes[name]; - return true; - } - }); - - this.stopProcess = function (name) { - delete self.runInBackground.processes[name]; - }; - - global.await = function (promise) { - while (delay() && !promise.stopped) { - // - } - - return promise.value; - }; - - this.workDisabled = 0; - - global._delay = delay; // The original delay function - - // Override the delay function, to check for background work while we wait anyway - global.delay = function (amount) { - - let recursive = recursiveCheck(); - let start = getTickCount(); - amount = amount || 0; - - do { - self.check(); - global._delay(getTickCount() - start > 3 ? 3 : 1); - !recursive && self.checkLowPrio(); - } while (getTickCount() - start <= amount); - - return true; // always return true - }; - - this.recursiveCheck = recursiveCheck; - })(); -})(module, require); + this.runInBackground = new Proxy({ processes: {} }, { + set: function (target, name, callback) { + if (target.processes.hasOwnProperty(name)) { + throw new Error("Process " + name + " already exists."); + } + target.processes[name] = { callback: callback, running: true, name: name }; + let proxyCallback = function () { + if (target.processes[name].running) { + target.processes[name].running = (callback() && self.pushLowPrio(proxyCallback) > -1); + } + if (!target.processes[name].running) { + delete target.processes[name]; + } + }; + self.pushLowPrio(proxyCallback); + }, + deleteProperty: function (target, name) { + if (!target.processes.hasOwnProperty(name)) { + throw new Error("Process " + name + " does not exists."); + } + target.processes[name].running = false; + delete target.processes[name]; + return true; + } + }); + + this.stopProcess = function (name) { + delete self.runInBackground.processes[name]; + }; + + global.await = function (promise) { + while (delay() && !promise.stopped) { + // + } + + return promise.value; + }; + + this.workDisabled = 0; + + global._delay = delay; // The original delay function + + // Override the delay function, to check for background work while we wait anyway + global.delay = function (amount) { + + let recursive = recursiveCheck(); + let start = getTickCount(); + amount = amount || 0; + + do { + self.check(); + global._delay(getTickCount() - start > 3 ? 3 : 1); + !recursive && self.checkLowPrio(); + } while (getTickCount() - start <= amount); + + return true; // always return true + }; + + this.recursiveCheck = recursiveCheck; + })(); + module.exports = Worker; +})(module); diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index a6b7f80d5..80640326d 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -8,5090 +8,5193 @@ */ (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.sdk = 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.sdk = factory(); + } }(this, function () { - "use strict"; - /** + "use strict"; + /** * @exports sdk */ - const sdk = { - waypoints: { - Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539], - Act1: [0x01, 0x03, 0x04, 0x05, 0x06, 0x1b, 0x1d, 0x20, 0x23], - Act2: [0x28, 0x30, 0x2a, 0x39, 0x2b, 0x2c, 0x34, 0x4a, 0x2e], - Act3: [0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x65], - Act4: [0x67, 0x6a, 0x6b], - Act5: [0x6d, 0x6f, 0x70, 0x71, 0x73, 0x7b, 0x75, 0x76, 0x81] - }, + const sdk = { + waypoints: { + Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539], + Act1: [0x01, 0x03, 0x04, 0x05, 0x06, 0x1b, 0x1d, 0x20, 0x23], + Act2: [0x28, 0x30, 0x2a, 0x39, 0x2b, 0x2c, 0x34, 0x4a, 0x2e], + Act3: [0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x53, 0x65], + Act4: [0x67, 0x6a, 0x6b], + Act5: [0x6d, 0x6f, 0x70, 0x71, 0x73, 0x7b, 0x75, 0x76, 0x81] + }, - difficulty: { - Normal: 0, - Nightmare: 1, - Hell: 2, - Difficulties: ["Normal", "Nightmare", "Hell"], + difficulty: { + Normal: 0, + Nightmare: 1, + Hell: 2, + Difficulties: ["Normal", "Nightmare", "Hell"], - nameOf: function (difficulty) { - if (difficulty === undefined || typeof difficulty !== "number") return false; - if (difficulty < 0 || difficulty > 2) return false; - return ["Normal", "Nightmare", "Hell"][difficulty]; - } - }, + nameOf: function (difficulty) { + if (difficulty === undefined || typeof difficulty !== "number") return false; + if (difficulty < 0 || difficulty > 2) return false; + return ["Normal", "Nightmare", "Hell"][difficulty]; + } + }, - party: { - NoParty: 65535, - flag: { - Invite: 0, - InParty: 1, - Accept: 2, - Cancel: 4 - }, - controls: { - Hostile: 1, - InviteOrCancel: 2, - Leave: 3, - Ignore: 4, - Squelch: 5, - }, - }, + party: { + NoParty: 65535, + flag: { + Invite: 0, + InParty: 1, + Accept: 2, + Cancel: 4 + }, + controls: { + Hostile: 1, + InviteOrCancel: 2, + Leave: 3, + Ignore: 4, + Squelch: 5, + }, + }, - clicktypes: { - click: { - item: { - Left: 0, - Right: 1, - ShiftLeft: 2, // For belt - MercFromBelt: 3, // For belt - Mercenary: 4 // Give to merc - }, - map: { - LeftDown: 0, - LeftHold: 1, - LeftUp: 2, - RightDown: 3, - RightHold: 4, - RightUp: 5, - }, - }, - shift: { - NoShift: 0, - Shift: 1 - } - }, - /** + clicktypes: { + click: { + item: { + Left: 0, + Right: 1, + ShiftLeft: 2, // For belt + MercFromBelt: 3, // For belt + Mercenary: 4 // Give to merc + }, + map: { + LeftDown: 0, + LeftHold: 1, + LeftUp: 2, + RightDown: 3, + RightHold: 4, + RightUp: 5, + }, + }, + shift: { + NoShift: 0, + Shift: 1 + } + }, + /** * @notes * - I get cursortype 3 when I swap an item that is equipped, but I get 4 if I just unequip an item * - I get cursortype 3 if I pick an item from my inventory then hover it over another item * - I get cursortype 4 if I pick and item from my inventory and don't hover it over another item * - If I then hover it over an item it turns to 3 then stays 3 */ - cursortype: { - Empty: 1, - ItemOnUnitHover: 3, // see notes - ItemOnCursor: 4, // see notes - Identify: 6, - Repair: 7, - }, + cursortype: { + Empty: 1, + ItemOnUnitHover: 3, // see notes + ItemOnCursor: 4, // see notes + Identify: 6, + Repair: 7, + }, - collision: { - BlockWall: 0x01, - LineOfSight: 0x02, - Ranged: 0x04, - PlayerToWalk: 0x08, - DarkArea: 0x10, - Casting: 0x20, - Unknown: 0x40, - Players: 0x80, - Monsters: 0x100, - Items: 0x200, - Objects: 0x400, - ClosedDoor: 0x800, - IsOnFloor: 0x1000, - MonsterIsOnFloor: 0x1100, - MonsterIsOnFloorDarkArea: 0x1110, // in doorway - FriendlyNPC: 0x2000, - Unknown2: 0x4000, - DeadBodies: 0x8000, - MonsterObject: 0xFFFF, - BlockMissile: 0x80E, - WallOrRanged: 0x5, - BlockWalk: 0x1805, - FriendlyRanged: 0x2004, - }, + collision: { + BlockWall: 0x01, + LineOfSight: 0x02, + Ranged: 0x04, + PlayerToWalk: 0x08, + DarkArea: 0x10, + Casting: 0x20, + Unknown: 0x40, + Players: 0x80, + Monsters: 0x100, + Items: 0x200, + Objects: 0x400, + ClosedDoor: 0x800, + IsOnFloor: 0x1000, + MonsterIsOnFloor: 0x1100, + MonsterIsOnFloorDarkArea: 0x1110, // in doorway + FriendlyNPC: 0x2000, + Unknown2: 0x4000, + DeadBodies: 0x8000, + MonsterObject: 0xFFFF, + BlockMissile: 0x80E, + WallOrRanged: 0x5, + BlockWalk: 0x1805, + FriendlyRanged: 0x2004, + }, - areas: { - Towns: [1, 40, 75, 103, 109], - None: 0, + areas: { + Towns: [1, 40, 75, 103, 109], + None: 0, - // Act 1 - RogueEncampment: 1, - BloodMoor: 2, - ColdPlains: 3, - StonyField: 4, - DarkWood: 5, - BlackMarsh: 6, - TamoeHighland: 7, - DenofEvil: 8, - CaveLvl1: 9, - UndergroundPassageLvl1: 10, - HoleLvl1: 11, - PitLvl1: 12, - CaveLvl2: 13, - UndergroundPassageLvl2: 14, - HoleLvl2: 15, - PitLvl2: 16, - BurialGrounds: 17, - Crypt: 18, - Mausoleum: 19, - ForgottenTower: 20, - TowerCellarLvl1: 21, - TowerCellarLvl2: 22, - TowerCellarLvl3: 23, - TowerCellarLvl4: 24, - TowerCellarLvl5: 25, - MonasteryGate: 26, - OuterCloister: 27, - Barracks: 28, - JailLvl1: 29, - JailLvl2: 30, - JailLvl3: 31, - InnerCloister: 32, - Cathedral: 33, - CatacombsLvl1: 34, - CatacombsLvl2: 35, - CatacombsLvl3: 36, - CatacombsLvl4: 37, - Tristram: 38, - MooMooFarm: 39, + // Act 1 + RogueEncampment: 1, + BloodMoor: 2, + ColdPlains: 3, + StonyField: 4, + DarkWood: 5, + BlackMarsh: 6, + TamoeHighland: 7, + DenofEvil: 8, + CaveLvl1: 9, + UndergroundPassageLvl1: 10, + HoleLvl1: 11, + PitLvl1: 12, + CaveLvl2: 13, + UndergroundPassageLvl2: 14, + HoleLvl2: 15, + PitLvl2: 16, + BurialGrounds: 17, + Crypt: 18, + Mausoleum: 19, + ForgottenTower: 20, + TowerCellarLvl1: 21, + TowerCellarLvl2: 22, + TowerCellarLvl3: 23, + TowerCellarLvl4: 24, + TowerCellarLvl5: 25, + MonasteryGate: 26, + OuterCloister: 27, + Barracks: 28, + JailLvl1: 29, + JailLvl2: 30, + JailLvl3: 31, + InnerCloister: 32, + Cathedral: 33, + CatacombsLvl1: 34, + CatacombsLvl2: 35, + CatacombsLvl3: 36, + CatacombsLvl4: 37, + Tristram: 38, + MooMooFarm: 39, - // Act 2 - LutGholein: 40, - RockyWaste: 41, - DryHills: 42, - FarOasis: 43, - LostCity: 44, - ValleyofSnakes: 45, - CanyonofMagic: 46, - A2SewersLvl1: 47, - A2SewersLvl2: 48, - A2SewersLvl3: 49, - HaremLvl1: 50, - HaremLvl2: 51, - PalaceCellarLvl1: 52, - PalaceCellarLvl2: 53, - PalaceCellarLvl3: 54, - StonyTombLvl1: 55, - HallsoftheDeadLvl1: 56, - HallsoftheDeadLvl2: 57, - ClawViperTempleLvl1: 58, - StonyTombLvl2: 59, - HallsoftheDeadLvl3: 60, - ClawViperTempleLvl2: 61, - MaggotLairLvl1: 62, - MaggotLairLvl2: 63, - MaggotLairLvl3: 64, - AncientTunnels: 65, - TalRashasTomb1: 66, - TalRashasTomb2: 67, - TalRashasTomb3: 68, - TalRashasTomb4: 69, - TalRashasTomb5: 70, - TalRashasTomb6: 71, - TalRashasTomb7: 72, - DurielsLair: 73, - ArcaneSanctuary: 74, + // Act 2 + LutGholein: 40, + RockyWaste: 41, + DryHills: 42, + FarOasis: 43, + LostCity: 44, + ValleyofSnakes: 45, + CanyonofMagic: 46, + A2SewersLvl1: 47, + A2SewersLvl2: 48, + A2SewersLvl3: 49, + HaremLvl1: 50, + HaremLvl2: 51, + PalaceCellarLvl1: 52, + PalaceCellarLvl2: 53, + PalaceCellarLvl3: 54, + StonyTombLvl1: 55, + HallsoftheDeadLvl1: 56, + HallsoftheDeadLvl2: 57, + ClawViperTempleLvl1: 58, + StonyTombLvl2: 59, + HallsoftheDeadLvl3: 60, + ClawViperTempleLvl2: 61, + MaggotLairLvl1: 62, + MaggotLairLvl2: 63, + MaggotLairLvl3: 64, + AncientTunnels: 65, + TalRashasTomb1: 66, + TalRashasTomb2: 67, + TalRashasTomb3: 68, + TalRashasTomb4: 69, + TalRashasTomb5: 70, + TalRashasTomb6: 71, + TalRashasTomb7: 72, + DurielsLair: 73, + ArcaneSanctuary: 74, - // Act 3 - KurastDocktown: 75, - SpiderForest: 76, - GreatMarsh: 77, - FlayerJungle: 78, - LowerKurast: 79, - KurastBazaar: 80, - UpperKurast: 81, - KurastCauseway: 82, - Travincal: 83, - SpiderCave: 84, - SpiderCavern: 85, - SwampyPitLvl1: 86, - SwampyPitLvl2: 87, - FlayerDungeonLvl1: 88, - FlayerDungeonLvl2: 89, - SwampyPitLvl3: 90, - FlayerDungeonLvl3: 91, - A3SewersLvl1: 92, - A3SewersLvl2: 93, - RuinedTemple: 94, - DisusedFane: 95, - ForgottenReliquary: 96, - ForgottenTemple: 97, - RuinedFane: 98, - DisusedReliquary: 99, - DuranceofHateLvl1: 100, - DuranceofHateLvl2: 101, - DuranceofHateLvl3: 102, + // Act 3 + KurastDocktown: 75, + SpiderForest: 76, + GreatMarsh: 77, + FlayerJungle: 78, + LowerKurast: 79, + KurastBazaar: 80, + UpperKurast: 81, + KurastCauseway: 82, + Travincal: 83, + SpiderCave: 84, + SpiderCavern: 85, + SwampyPitLvl1: 86, + SwampyPitLvl2: 87, + FlayerDungeonLvl1: 88, + FlayerDungeonLvl2: 89, + SwampyPitLvl3: 90, + FlayerDungeonLvl3: 91, + A3SewersLvl1: 92, + A3SewersLvl2: 93, + RuinedTemple: 94, + DisusedFane: 95, + ForgottenReliquary: 96, + ForgottenTemple: 97, + RuinedFane: 98, + DisusedReliquary: 99, + DuranceofHateLvl1: 100, + DuranceofHateLvl2: 101, + DuranceofHateLvl3: 102, - // Act 4 - PandemoniumFortress: 103, - OuterSteppes: 104, - PlainsofDespair: 105, - CityoftheDamned: 106, - RiverofFlame: 107, - ChaosSanctuary: 108, + // Act 4 + PandemoniumFortress: 103, + OuterSteppes: 104, + PlainsofDespair: 105, + CityoftheDamned: 106, + RiverofFlame: 107, + ChaosSanctuary: 108, - // Act 5 - Harrogath: 109, - BloodyFoothills: 110, - FrigidHighlands: 111, - ArreatPlateau: 112, - CrystalizedPassage: 113, - FrozenRiver: 114, - GlacialTrail: 115, - DrifterCavern: 116, - FrozenTundra: 117, - AncientsWay: 118, - IcyCellar: 119, - ArreatSummit: 120, - NihlathaksTemple: 121, - HallsofAnguish: 122, - HallsofPain: 123, - HallsofVaught: 124, - Abaddon: 125, - PitofAcheron: 126, - InfernalPit: 127, - WorldstoneLvl1: 128, - WorldstoneLvl2: 129, - WorldstoneLvl3: 130, - ThroneofDestruction: 131, - WorldstoneChamber: 132, + // Act 5 + Harrogath: 109, + BloodyFoothills: 110, + FrigidHighlands: 111, + ArreatPlateau: 112, + CrystalizedPassage: 113, + FrozenRiver: 114, + GlacialTrail: 115, + DrifterCavern: 116, + FrozenTundra: 117, + AncientsWay: 118, + IcyCellar: 119, + ArreatSummit: 120, + NihlathaksTemple: 121, + HallsofAnguish: 122, + HallsofPain: 123, + HallsofVaught: 124, + Abaddon: 125, + PitofAcheron: 126, + InfernalPit: 127, + WorldstoneLvl1: 128, + WorldstoneLvl2: 129, + WorldstoneLvl3: 130, + ThroneofDestruction: 131, + WorldstoneChamber: 132, - // Ubers - MatronsDen: 133, - ForgottenSands: 134, - FurnaceofPain: 135, - UberTristram: 136, + // Ubers + MatronsDen: 133, + ForgottenSands: 134, + FurnaceofPain: 135, + UberTristram: 136, - actOf: function (area) { - switch (true) { - case area < sdk.areas.LutGholein: - return 1; - case area < sdk.areas.KurastDocktown: - return 2; - case area < sdk.areas.PandemoniumFortress: - return 3; - case area < sdk.areas.Harrogath: - return 4; - default: - return 5; - } - }, + actOf: function (area) { + switch (true) { + case area < sdk.areas.LutGholein: + return 1; + case area < sdk.areas.KurastDocktown: + return 2; + case area < sdk.areas.PandemoniumFortress: + return 3; + case area < sdk.areas.Harrogath: + return 4; + default: + return 5; + } + }, - townOf: function (area) { - switch (true) { - case area < sdk.areas.LutGholein: - return sdk.areas.RogueEncampment; - case area < sdk.areas.KurastDocktown: - return sdk.areas.LutGholein; - case area < sdk.areas.PandemoniumFortress: - return sdk.areas.KurastDocktown; - case area < sdk.areas.Harrogath: - return sdk.areas.PandemoniumFortress; - default: - return sdk.areas.Harrogath; - } - }, + townOf: function (area) { + switch (true) { + case area < sdk.areas.LutGholein: + return sdk.areas.RogueEncampment; + case area < sdk.areas.KurastDocktown: + return sdk.areas.LutGholein; + case area < sdk.areas.PandemoniumFortress: + return sdk.areas.KurastDocktown; + case area < sdk.areas.Harrogath: + return sdk.areas.PandemoniumFortress; + default: + return sdk.areas.Harrogath; + } + }, - townOfAct: function (act) { - switch (act) { - case 1: - return sdk.areas.RogueEncampment; - case 2: - return sdk.areas.LutGholein; - case 3: - return sdk.areas.KurastDocktown; - case 4: - return sdk.areas.PandemoniumFortress; - default: - return sdk.areas.Harrogath; - } - } - }, + townOfAct: function (act) { + switch (act) { + case 1: + return sdk.areas.RogueEncampment; + case 2: + return sdk.areas.LutGholein; + case 3: + return sdk.areas.KurastDocktown; + case 4: + return sdk.areas.PandemoniumFortress; + default: + return sdk.areas.Harrogath; + } + } + }, - skills: { - get: { - RightName: 0, - LeftName: 1, - RightId: 2, - LeftId: 3, - AllSkills: 4 - }, - hand: { - Right: 0, - Left: 1, - LeftNoShift: 2, - RightShift: 3, - }, - subindex: { - HardPoints: 0, - SoftPoints: 1 - }, - // General - Attack: 0, - Kick: 1, - Throw: 2, - Unsummon: 3, - LeftHandThrow: 4, - LeftHandSwing: 5, + skills: { + get: { + RightName: 0, + LeftName: 1, + RightId: 2, + LeftId: 3, + AllSkills: 4 + }, + hand: { + Right: 0, + Left: 1, + LeftNoShift: 2, + RightShift: 3, + }, + subindex: { + HardPoints: 0, + SoftPoints: 1 + }, + // General + Attack: 0, + Kick: 1, + Throw: 2, + Unsummon: 3, + LeftHandThrow: 4, + LeftHandSwing: 5, - // Amazon - MagicArrow: 6, - FireArrow: 7, - InnerSight: 8, - CriticalStrike: 9, - Jab: 10, - ColdArrow: 11, - MultipleShot: 12, - Dodge: 13, - PowerStrike: 14, - PoisonJavelin: 15, - ExplodingArrow: 16, - SlowMissiles: 17, - Avoid: 18, - Impale: 19, - LightningBolt: 20, - IceArrow: 21, - GuidedArrow: 22, - Penetrate: 23, - ChargedStrike: 24, - PlagueJavelin: 25, - Strafe: 26, - ImmolationArrow: 27, - Dopplezon: 28, - Decoy: 28, - Evade: 29, - Fend: 30, - FreezingArrow: 31, - Valkyrie: 32, - Pierce: 33, - LightningStrike: 34, - LightningFury: 35, + // Amazon + MagicArrow: 6, + FireArrow: 7, + InnerSight: 8, + CriticalStrike: 9, + Jab: 10, + ColdArrow: 11, + MultipleShot: 12, + Dodge: 13, + PowerStrike: 14, + PoisonJavelin: 15, + ExplodingArrow: 16, + SlowMissiles: 17, + Avoid: 18, + Impale: 19, + LightningBolt: 20, + IceArrow: 21, + GuidedArrow: 22, + Penetrate: 23, + ChargedStrike: 24, + PlagueJavelin: 25, + Strafe: 26, + ImmolationArrow: 27, + Dopplezon: 28, + Decoy: 28, + Evade: 29, + Fend: 30, + FreezingArrow: 31, + Valkyrie: 32, + Pierce: 33, + LightningStrike: 34, + LightningFury: 35, - // Sorc - FireBolt: 36, - Warmth: 37, - ChargedBolt: 38, - IceBolt: 39, - FrozenArmor: 40, - Inferno: 41, - StaticField: 42, - Telekinesis: 43, - FrostNova: 44, - IceBlast: 45, - Blaze: 46, - FireBall: 47, - Nova: 48, - Lightning: 49, - ShiverArmor: 50, - FireWall: 51, - Enchant: 52, - ChainLightning: 53, - Teleport: 54, - GlacialSpike: 55, - Meteor: 56, - ThunderStorm: 57, - EnergyShield: 58, - Blizzard: 59, - ChillingArmor: 60, - FireMastery: 61, - Hydra: 62, - LightningMastery: 63, - FrozenOrb: 64, - ColdMastery: 65, + // Sorc + FireBolt: 36, + Warmth: 37, + ChargedBolt: 38, + IceBolt: 39, + FrozenArmor: 40, + Inferno: 41, + StaticField: 42, + Telekinesis: 43, + FrostNova: 44, + IceBlast: 45, + Blaze: 46, + FireBall: 47, + Nova: 48, + Lightning: 49, + ShiverArmor: 50, + FireWall: 51, + Enchant: 52, + ChainLightning: 53, + Teleport: 54, + GlacialSpike: 55, + Meteor: 56, + ThunderStorm: 57, + EnergyShield: 58, + Blizzard: 59, + ChillingArmor: 60, + FireMastery: 61, + Hydra: 62, + LightningMastery: 63, + FrozenOrb: 64, + ColdMastery: 65, - // Necro - AmplifyDamage: 66, - Teeth: 67, - BoneArmor: 68, - SkeletonMastery: 69, - RaiseSkeleton: 70, - DimVision: 71, - Weaken: 72, - PoisonDagger: 73, - CorpseExplosion: 74, - ClayGolem: 75, - IronMaiden: 76, - Terror: 77, - BoneWall: 78, - GolemMastery: 79, - RaiseSkeletalMage: 80, - Confuse: 81, - LifeTap: 82, - PoisonExplosion: 83, - BoneSpear: 84, - BloodGolem: 85, - Attract: 86, - Decrepify: 87, - BonePrison: 88, - SummonResist: 89, - IronGolem: 90, - LowerResist: 91, - PoisonNova: 92, - BoneSpirit: 93, - FireGolem: 94, - Revive: 95, + // Necro + AmplifyDamage: 66, + Teeth: 67, + BoneArmor: 68, + SkeletonMastery: 69, + RaiseSkeleton: 70, + DimVision: 71, + Weaken: 72, + PoisonDagger: 73, + CorpseExplosion: 74, + ClayGolem: 75, + IronMaiden: 76, + Terror: 77, + BoneWall: 78, + GolemMastery: 79, + RaiseSkeletalMage: 80, + Confuse: 81, + LifeTap: 82, + PoisonExplosion: 83, + BoneSpear: 84, + BloodGolem: 85, + Attract: 86, + Decrepify: 87, + BonePrison: 88, + SummonResist: 89, + IronGolem: 90, + LowerResist: 91, + PoisonNova: 92, + BoneSpirit: 93, + FireGolem: 94, + Revive: 95, - // Paladin - Sacrifice: 96, - Smite: 97, - Might: 98, - Prayer: 99, - ResistFire: 100, - HolyBolt: 101, - HolyFire: 102, - Thorns: 103, - Defiance: 104, - ResistCold: 105, - Zeal: 106, - Charge: 107, - BlessedAim: 108, - Cleansing: 109, - ResistLightning: 110, - Vengeance: 111, - BlessedHammer: 112, - Concentration: 113, - HolyFreeze: 114, - Vigor: 115, - Conversion: 116, - HolyShield: 117, - HolyShock: 118, - Sanctuary: 119, - Meditation: 120, - FistoftheHeavens: 121, - Fanaticism: 122, - Conviction: 123, - Redemption: 124, - Salvation: 125, + // Paladin + Sacrifice: 96, + Smite: 97, + Might: 98, + Prayer: 99, + ResistFire: 100, + HolyBolt: 101, + HolyFire: 102, + Thorns: 103, + Defiance: 104, + ResistCold: 105, + Zeal: 106, + Charge: 107, + BlessedAim: 108, + Cleansing: 109, + ResistLightning: 110, + Vengeance: 111, + BlessedHammer: 112, + Concentration: 113, + HolyFreeze: 114, + Vigor: 115, + Conversion: 116, + HolyShield: 117, + HolyShock: 118, + Sanctuary: 119, + Meditation: 120, + FistoftheHeavens: 121, + Fanaticism: 122, + Conviction: 123, + Redemption: 124, + Salvation: 125, - // Barb - Bash: 126, - SwordMastery: 127, - AxeMastery: 128, - MaceMastery: 129, - Howl: 130, - FindPotion: 131, - Leap: 132, - DoubleSwing: 133, - PoleArmMastery: 134, - ThrowingMastery: 135, - SpearMastery: 136, - Taunt: 137, - Shout: 138, - Stun: 139, - DoubleThrow: 140, - IncreasedStamina: 141, - FindItem: 142, - LeapAttack: 143, - Concentrate: 144, - IronSkin: 145, - BattleCry: 146, - Frenzy: 147, - IncreasedSpeed: 148, - BattleOrders: 149, - GrimWard: 150, - Whirlwind: 151, - Berserk: 152, - NaturalResistance: 153, - WarCry: 154, - BattleCommand: 155, + // Barb + Bash: 126, + SwordMastery: 127, + AxeMastery: 128, + MaceMastery: 129, + Howl: 130, + FindPotion: 131, + Leap: 132, + DoubleSwing: 133, + PoleArmMastery: 134, + ThrowingMastery: 135, + SpearMastery: 136, + Taunt: 137, + Shout: 138, + Stun: 139, + DoubleThrow: 140, + IncreasedStamina: 141, + FindItem: 142, + LeapAttack: 143, + Concentrate: 144, + IronSkin: 145, + BattleCry: 146, + Frenzy: 147, + IncreasedSpeed: 148, + BattleOrders: 149, + GrimWard: 150, + Whirlwind: 151, + Berserk: 152, + NaturalResistance: 153, + WarCry: 154, + BattleCommand: 155, - // General stuff - IdentifyScroll: 217, - BookofIdentify: 218, - TownPortalScroll: 219, - BookofTownPortal: 220, + // General stuff + IdentifyScroll: 217, + BookofIdentify: 218, + TownPortalScroll: 219, + BookofTownPortal: 220, - // Druid - Raven: 221, - PoisonCreeper: 222, // External - PlaguePoppy: 222, // Internal - Werewolf: 223, // External - Wearwolf: 223, // Internal - Lycanthropy: 224, // External - ShapeShifting: 224, // Internal - Firestorm: 225, - OakSage: 226, - SpiritWolf: 227, // External - SummonSpiritWolf: 227, // Internal - Werebear: 228, // External - Wearbear: 228, // Internal - MoltenBoulder: 229, - ArcticBlast: 230, - CarrionVine: 231, // External - CycleofLife: 231, // Internal - FeralRage: 232, - Maul: 233, - Fissure: 234, // Internal - Eruption: 234, // Internal - CycloneArmor: 235, - HeartofWolverine: 236, - SummonDireWolf: 237, // External - SummonFenris: 237, // Internal - Rabies: 238, - FireClaws: 239, - Twister: 240, - SolarCreeper: 241, // External - Vines: 241, // Internal - Hunger: 242, - ShockWave: 243, - Volcano: 244, - Tornado: 245, - SpiritofBarbs: 246, - Grizzly: 247, // External - SummonGrizzly: 247, // Internal - Fury: 248, - Armageddon: 249, - Hurricane: 250, + // Druid + Raven: 221, + PoisonCreeper: 222, // External + PlaguePoppy: 222, // Internal + Werewolf: 223, // External + Wearwolf: 223, // Internal + Lycanthropy: 224, // External + ShapeShifting: 224, // Internal + Firestorm: 225, + OakSage: 226, + SpiritWolf: 227, // External + SummonSpiritWolf: 227, // Internal + Werebear: 228, // External + Wearbear: 228, // Internal + MoltenBoulder: 229, + ArcticBlast: 230, + CarrionVine: 231, // External + CycleofLife: 231, // Internal + FeralRage: 232, + Maul: 233, + Fissure: 234, // Internal + Eruption: 234, // Internal + CycloneArmor: 235, + HeartofWolverine: 236, + SummonDireWolf: 237, // External + SummonFenris: 237, // Internal + Rabies: 238, + FireClaws: 239, + Twister: 240, + SolarCreeper: 241, // External + Vines: 241, // Internal + Hunger: 242, + ShockWave: 243, + Volcano: 244, + Tornado: 245, + SpiritofBarbs: 246, + Grizzly: 247, // External + SummonGrizzly: 247, // Internal + Fury: 248, + Armageddon: 249, + Hurricane: 250, - // Assa - FireBlast: 251, // External - FireTrauma: 251, // Internal - ClawMastery: 252, - PsychicHammer: 253, - TigerStrike: 254, - DragonTalon: 255, - ShockWeb: 256, // External - ShockField: 256, // Internal - BladeSentinel: 257, - Quickness: 258, // Internal name - BurstofSpeed: 258, // Shown name - FistsofFire: 259, - DragonClaw: 260, - ChargedBoltSentry: 261, - WakeofFire: 262, // External - WakeofFireSentry: 262, // Internal - WeaponBlock: 263, - CloakofShadows: 264, - CobraStrike: 265, - BladeFury: 266, - Fade: 267, - ShadowWarrior: 268, - ClawsofThunder: 269, - DragonTail: 270, - LightningSentry: 271, - WakeofInferno: 272, // External - InfernoSentry: 272, // Internal - MindBlast: 273, - BladesofIce: 274, - DragonFlight: 275, - DeathSentry: 276, - BladeShield: 277, - Venom: 278, - ShadowMaster: 279, - PhoenixStrike: 280, // External - RoyalStrike: 280, // Internal - WakeofDestructionSentry: 281, // Not used? - Summoner: 500, // special - tabs: { - // Ama - BowandCrossbow: 0, - PassiveandMagic: 1, - JavelinandSpear: 2, + // Assa + FireBlast: 251, // External + FireTrauma: 251, // Internal + ClawMastery: 252, + PsychicHammer: 253, + TigerStrike: 254, + DragonTalon: 255, + ShockWeb: 256, // External + ShockField: 256, // Internal + BladeSentinel: 257, + Quickness: 258, // Internal name + BurstofSpeed: 258, // Shown name + FistsofFire: 259, + DragonClaw: 260, + ChargedBoltSentry: 261, + WakeofFire: 262, // External + WakeofFireSentry: 262, // Internal + WeaponBlock: 263, + CloakofShadows: 264, + CobraStrike: 265, + BladeFury: 266, + Fade: 267, + ShadowWarrior: 268, + ClawsofThunder: 269, + DragonTail: 270, + LightningSentry: 271, + WakeofInferno: 272, // External + InfernoSentry: 272, // Internal + MindBlast: 273, + BladesofIce: 274, + DragonFlight: 275, + DeathSentry: 276, + BladeShield: 277, + Venom: 278, + ShadowMaster: 279, + PhoenixStrike: 280, // External + RoyalStrike: 280, // Internal + WakeofDestructionSentry: 281, // Not used? + Summoner: 500, // special + tabs: { + // Ama + BowandCrossbow: 0, + PassiveandMagic: 1, + JavelinandSpear: 2, - // Sorc - Fire: 8, - Lightning: 9, - Cold: 10, + // Sorc + Fire: 8, + Lightning: 9, + Cold: 10, - // Necro - Curses: 16, - PoisonandBone: 17, - NecroSummoning: 18, + // Necro + Curses: 16, + PoisonandBone: 17, + NecroSummoning: 18, - // Pala - PalaCombat: 24, - Offensive: 25, - Defensive: 26, + // Pala + PalaCombat: 24, + Offensive: 25, + Defensive: 26, - // Barb - BarbCombat: 32, - Masteries: 33, - Warcries: 34, + // Barb + BarbCombat: 32, + Masteries: 33, + Warcries: 34, - // Druid - DruidSummon: 40, - ShapeShifting: 41, - Elemental: 42, + // Druid + DruidSummon: 40, + ShapeShifting: 41, + Elemental: 42, - // Assa - Traps: 48, - ShadowDisciplines: 49, - MartialArts: 50, - } - }, - skillTabs: undefined, + // Assa + Traps: 48, + ShadowDisciplines: 49, + MartialArts: 50, + } + }, + skillTabs: undefined, - quest: { - item: { - // Act 1 - WirtsLeg: 88, - HoradricMalus: 89, - ScrollofInifuss: 524, - KeytotheCairnStones: 525, - // Act 2 - FinishedStaff: 91, - "HoradricStaff": 91, - IncompleteStaff: 92, - "ShaftoftheHoradricStaff": 92, - ViperAmulet: 521, - "TopoftheHoradricStaff": 521, - Cube: 549, - BookofSkill: 552, - // Act 3 - "DecoyGidbinn": 86, - "TheGidbinn": 87, - KhalimsFlail: 173, - KhalimsWill: 174, - PotofLife: 545, - "AJadeFigurine": 546, - JadeFigurine: 546, - TheGoldenBird: 547, - LamEsensTome: 548, - KhalimsEye: 553, - KhalimsHeart: 554, - KhalimsBrain: 555, - // Act 4 - HellForgeHammer: 90, - Soulstone: 551, - "MephistosSoulstone": 551, - // Act 5 - MalahsPotion: 644, - "ScrollofKnowledge": 645, - ScrollofResistance: 646, - // Pandemonium Event - KeyofTerror: 647, - KeyofHate: 648, - KeyofDestruction: 649, - DiablosHorn: 650, - BaalsEye: 651, - MephistosBrain: 652, - StandardofHeroes: 658, - // Essences/Token - TokenofAbsolution: 653, - TwistedEssenceofSuffering: 654, - ChargedEssenceofHatred: 655, - BurningEssenceofTerror: 656, - FesteringEssenceofDestruction: 657, - // Misc - "TheBlackTowerKey": 544, - }, - items: [ - // act 1 - 88, 89, 524, 525, - // act 2 - 91, 92, 521, 549, 552, - // act 3 - 86, 87, 173, 174, 545, 546, 547, 548, 553, 554, 555, - // act 4 - 90, 551, - // act 5 - 644, 645, 646, - ], - chest: { - // act1 - StoneAlpha: 17, - StoneBeta: 18, - StoneGamma: 19, - StoneDelta: 20, - StoneLambda: 21, - StoneTheta: 22, // ? - CainsJail: 26, - InifussTree: 30, - MalusHolder: 108, - Wirt: 268, + quest: { + item: { + // Act 1 + WirtsLeg: 88, + HoradricMalus: 89, + ScrollofInifuss: 524, + KeytotheCairnStones: 525, + // Act 2 + FinishedStaff: 91, + "HoradricStaff": 91, + IncompleteStaff: 92, + "ShaftoftheHoradricStaff": 92, + ViperAmulet: 521, + "TopoftheHoradricStaff": 521, + Cube: 549, + BookofSkill: 552, + // Act 3 + "DecoyGidbinn": 86, + "TheGidbinn": 87, + KhalimsFlail: 173, + KhalimsWill: 174, + PotofLife: 545, + "AJadeFigurine": 546, + JadeFigurine: 546, + TheGoldenBird: 547, + LamEsensTome: 548, + KhalimsEye: 553, + KhalimsHeart: 554, + KhalimsBrain: 555, + // Act 4 + HellForgeHammer: 90, + Soulstone: 551, + "MephistosSoulstone": 551, + // Act 5 + MalahsPotion: 644, + "ScrollofKnowledge": 645, + ScrollofResistance: 646, + // Pandemonium Event + KeyofTerror: 647, + KeyofHate: 648, + KeyofDestruction: 649, + DiablosHorn: 650, + BaalsEye: 651, + MephistosBrain: 652, + StandardofHeroes: 658, + // Essences/Token + TokenofAbsolution: 653, + TwistedEssenceofSuffering: 654, + ChargedEssenceofHatred: 655, + BurningEssenceofTerror: 656, + FesteringEssenceofDestruction: 657, + // Misc + "TheBlackTowerKey": 544, + }, + items: [ + // act 1 + 88, 89, 524, 525, + // act 2 + 91, 92, 521, 549, 552, + // act 3 + 86, 87, 173, 174, 545, 546, 547, 548, 553, 554, 555, + // act 4 + 90, 551, + // act 5 + 644, 645, 646, + ], + chest: { + // act1 + StoneAlpha: 17, + StoneBeta: 18, + StoneGamma: 19, + StoneDelta: 20, + StoneLambda: 21, + StoneTheta: 22, // ? + CainsJail: 26, + InifussTree: 30, + MalusHolder: 108, + Wirt: 268, - // act 2 - ViperAmuletChest: 149, - HoradricStaffHolder: 152, - HoradricCubeChest: 354, - HoradricScrollChest: 355, - ShaftoftheHoradricStaffChest: 356, - Journal: 357, + // act 2 + ViperAmuletChest: 149, + HoradricStaffHolder: 152, + HoradricCubeChest: 354, + HoradricScrollChest: 355, + ShaftoftheHoradricStaffChest: 356, + Journal: 357, - // act 3 - ForestAltar: 81, - LamEsensTomeHolder: 193, - GidbinnAltar: 252, - KhalimsHeartChest: 405, - KhalimsBrainChest: 406, - KhalimsEyeChest: 407, + // act 3 + ForestAltar: 81, + LamEsensTomeHolder: 193, + GidbinnAltar: 252, + KhalimsHeartChest: 405, + KhalimsBrainChest: 406, + KhalimsEyeChest: 407, - // act 4 - HellForge: 376, + // act 4 + HellForge: 376, - // act 5 - BarbCage: 473, - FrozenAnya: 558, - AncientsAltar: 546, - }, - chests: [ - // act 1 - 17, 18, 19, 20, 21, 22, 26, 30, 108, - // act 2 - 149, 152, 354, 355, 356, 357, - // act 3 - 81, 193, 405, 406, 407, - // act 4 - 376, - // act 5 - 434, 558, 546 - ], - id: { - SpokeToWarriv: 0, - DenofEvil: 1, - SistersBurialGrounds: 2, - TheSearchForCain: 4, - ForgottenTower: 5, - ToolsoftheTrade: 3, - SistersToTheSlaughter: 6, - AbleToGotoActII: 7, - SpokeToJerhyn: 8, - RadamentsLair: 9, - TheHoradricStaff: 10, - TheTaintedSun: 11, - TheArcaneSanctuary: 12, - TheSummoner: 13, - TheSevenTombs: 14, - AbleToGotoActIII: 15, - SpokeToHratli: 16, - TheGoldenBird: 20, - BladeoftheOldReligion: 19, - KhalimsWill: 18, - LamEsensTome: 17, - TheBlackenedTemple: 21, - TheGuardian: 22, - AbleToGotoActIV: 23, - SpokeToTyrael: 24, - TheFallenAngel: 25, - HellsForge: 27, - TerrorsEnd: 26, - AbleToGotoActV: 28, - SiegeOnHarrogath: 35, - RescueonMountArreat: 36, - PrisonofIce: 37, - BetrayalofHarrogath: 38, - RiteofPassage: 39, - EyeofDestruction: 40, - Respec: 41, - }, - // just common states for now - states: { - Completed: 0, - ReqComplete: 1, - GreyedOut: 12, - PartyMemberComplete: 13, - CannotComplete: 14, - } - }, + // act 5 + BarbCage: 473, + FrozenAnya: 558, + AncientsAltar: 546, + }, + chests: [ + // act 1 + 17, 18, 19, 20, 21, 22, 26, 30, 108, + // act 2 + 149, 152, 354, 355, 356, 357, + // act 3 + 81, 193, 405, 406, 407, + // act 4 + 376, + // act 5 + 434, 558, 546 + ], + id: { + SpokeToWarriv: 0, + DenofEvil: 1, + SistersBurialGrounds: 2, + TheSearchForCain: 4, + ForgottenTower: 5, + ToolsoftheTrade: 3, + SistersToTheSlaughter: 6, + AbleToGotoActII: 7, + SpokeToJerhyn: 8, + RadamentsLair: 9, + TheHoradricStaff: 10, + TheTaintedSun: 11, + TheArcaneSanctuary: 12, + TheSummoner: 13, + TheSevenTombs: 14, + AbleToGotoActIII: 15, + SpokeToHratli: 16, + TheGoldenBird: 20, + BladeoftheOldReligion: 19, + KhalimsWill: 18, + LamEsensTome: 17, + TheBlackenedTemple: 21, + TheGuardian: 22, + AbleToGotoActIV: 23, + SpokeToTyrael: 24, + TheFallenAngel: 25, + HellsForge: 27, + TerrorsEnd: 26, + AbleToGotoActV: 28, + SiegeOnHarrogath: 35, + RescueonMountArreat: 36, + PrisonofIce: 37, + BetrayalofHarrogath: 38, + RiteofPassage: 39, + EyeofDestruction: 40, + Respec: 41, + }, + // just common states for now + states: { + Completed: 0, + ReqComplete: 1, + GreyedOut: 12, + PartyMemberComplete: 13, + CannotComplete: 14, + } + }, - // in game data - uiflags: { - Inventory: 0x01, - StatsWindow: 0x02, - QuickSkill: 0x03, - SkillWindow: 0x04, - ChatBox: 0x05, - NPCMenu: 0x08, - EscMenu: 0x09, - KeytotheCairnStonesScreen: 0x10, - AutoMap: 0x0A, - ConfigControls: 0x0B, - Shop: 0x0C, - ShowItem: 0x0D, - SubmitItem: 0x0E, - Quest: 0x0F, - QuestLog: 0x11, - StatusArea: 0x12, - Waypoint: 0x14, - MiniPanel: 0x15, - Party: 0x16, - TradePrompt: 0x17, - Msgs: 0x18, - Stash: 0x19, - Cube: 0x1A, - ShowBelt: 0x1F, - Help: 0x21, - MercScreen: 0x24, - ScrollWindow: 0x25 - }, + // in game data + uiflags: { + Inventory: 0x01, + StatsWindow: 0x02, + QuickSkill: 0x03, + SkillWindow: 0x04, + ChatBox: 0x05, + NPCMenu: 0x08, + EscMenu: 0x09, + KeytotheCairnStonesScreen: 0x10, + AutoMap: 0x0A, + ConfigControls: 0x0B, + Shop: 0x0C, + ShowItem: 0x0D, + SubmitItem: 0x0E, + Quest: 0x0F, + QuestLog: 0x11, + StatusArea: 0x12, + Waypoint: 0x14, + MiniPanel: 0x15, + Party: 0x16, + TradePrompt: 0x17, + Msgs: 0x18, + Stash: 0x19, + Cube: 0x1A, + ShowBelt: 0x1F, + Help: 0x21, + MercScreen: 0x24, + ScrollWindow: 0x25 + }, - menu: { - Respec: 0x2BA0, - Ok: 0x0D49, - Talk: 0x0D35, - Trade: 0x0D44, - TradeRepair: 0x0D06, - Imbue: 0x0FB1, - Gamble: 0x0D46, - Hire: 0x0D45, - GoEast: 0x0D36, - GoWest: 0x0D37, - IdentifyItems: 0x0FB4, - SailEast: 0x0D38, - SailWest: 0x0D39, - RessurectMerc: 0x1507, - AddSockets: 0x58DC, - Personalize: 0x58DD, - TravelToHarrogath: 0x58D2, - }, + menu: { + Respec: 0x2BA0, + Ok: 0x0D49, + Talk: 0x0D35, + Trade: 0x0D44, + TradeRepair: 0x0D06, + Imbue: 0x0FB1, + Gamble: 0x0D46, + Hire: 0x0D45, + GoEast: 0x0D36, + GoWest: 0x0D37, + IdentifyItems: 0x0FB4, + SailEast: 0x0D38, + SailWest: 0x0D39, + RessurectMerc: 0x1507, + AddSockets: 0x58DC, + Personalize: 0x58DD, + TravelToHarrogath: 0x58D2, + }, - // shrine types - shrines: { - Presets: [2, 81, 83, 170, 344, 197, 202], - Ids: [ - 2, 77, 81, 83, 84, 85, 93, 96, 97, 109, 116, 123, 124, - 133, 134, 135, 136, 150, 151, 164, 165, 166, 167, 168, - 170, 172, 173, 184, 190, 191, 197, 199, 200, 201, 202, - 206, 226, 231, 232, 236, 249, 260, 262, 263, 264, 265, - 275, 276, 277, 278, 279, 280, 281, 282, 299, 300, 302, - 303, 320, 325, 343, 344, 361, 414, 415, 421, 422, 423, - 427, 428, 464, 465, 472, 479, 483, 484, 488, 491, 492, - 495, 497, 499, 503, 509, 512, 520, 521, 522 - ], - None: 0, - Refilling: 1, - Health: 2, - Mana: 3, - HealthExchange: 4, - ManaExchange: 5, - Armor: 6, - Combat: 7, - ResistFire: 8, - ResistCold: 9, - ResistLightning: 10, - ResistPoison: 11, - Skill: 12, - ManaRecharge: 13, - Stamina: 14, - Experience: 15, - Enirhs: 16, - Portal: 17, - Gem: 18, - Fire: 19, - Monster: 20, - Exploding: 21, - Poison: 22 - }, + // shrine types + shrines: { + Presets: [2, 81, 83, 170, 344, 197, 202], + Ids: [ + 2, 77, 81, 83, 84, 85, 93, 96, 97, 109, 116, 123, 124, + 133, 134, 135, 136, 150, 151, 164, 165, 166, 167, 168, + 170, 172, 173, 184, 190, 191, 197, 199, 200, 201, 202, + 206, 226, 231, 232, 236, 249, 260, 262, 263, 264, 265, + 275, 276, 277, 278, 279, 280, 281, 282, 299, 300, 302, + 303, 320, 325, 343, 344, 361, 414, 415, 421, 422, 423, + 427, 428, 464, 465, 472, 479, 483, 484, 488, 491, 492, + 495, 497, 499, 503, 509, 512, 520, 521, 522 + ], + None: 0, + Refilling: 1, + Health: 2, + Mana: 3, + HealthExchange: 4, + ManaExchange: 5, + Armor: 6, + Combat: 7, + ResistFire: 8, + ResistCold: 9, + ResistLightning: 10, + ResistPoison: 11, + Skill: 12, + ManaRecharge: 13, + Stamina: 14, + Experience: 15, + Enirhs: 16, + Portal: 17, + Gem: 18, + Fire: 19, + Monster: 20, + Exploding: 21, + Poison: 22 + }, - // unit states - states: { - None: 0, - FrozenSolid: 1, - Poison: 2, - ResistFire: 3, - ResistCold: 4, - ResistLightning: 5, - ResistMagic: 6, - PlayerBody: 7, - ResistAll: 8, - AmplifyDamage: 9, - FrozenArmor: 10, - Frozen: 11, - Inferno: 12, - Blaze: 13, - BoneArmor: 14, - Concentrate: 15, - Enchant: 16, - InnerSight: 17, - SkillMove: 18, - Weaken: 19, - ChillingArmor: 20, - Stunned: 21, - SpiderLay: 22, - DimVision: 23, - Slowed: 24, - FetishAura: 25, - Shout: 26, - Taunt: 27, - Conviction: 28, - Convicted: 29, - EnergyShield: 30, - Venom: 31, - BattleOrders: 32, - Might: 33, - Prayer: 34, - HolyFire: 35, - Thorns: 36, - Defiance: 37, - ThunderStorm: 38, - LightningBolt: 39, - BlessedAim: 40, - Stamina: 41, - Concentration: 42, - Holywind: 43, - HolyFreeze: 43, - HolywindCold: 44, - HolyFreezeCold: 44, - Cleansing: 45, - HolyShock: 46, - Sanctuary: 47, - Meditation: 48, - Fanaticism: 49, - Redemption: 50, - BattleCommand: 51, - PreventHeal: 52, - Conversion: 53, - Uninterruptable: 54, - IronMaiden: 55, - Terror: 56, - Attract: 57, - LifeTap: 58, - Confuse: 59, - Decrepify: 60, - LowerResist: 61, - OpenWounds: 62, - Dopplezon: 63, - Decoy: 63, - CriticalStrike: 64, - Dodge: 65, - Avoid: 66, - Penetrate: 67, - Evade: 68, - Pierce: 69, - Warmth: 70, - FireMastery: 71, - LightningMastery: 72, - ColdMastery: 73, - SwordMastery: 74, - AxeMastery: 75, - MaceMastery: 76, - PoleArmMastery: 77, - ThrowingMastery: 78, - SpearMastery: 79, - IncreasedStamina: 80, - IronSkin: 81, - IncreasedSpeed: 82, - NaturalResistance: 83, - FingerMageCurse: 84, - NoManaReg: 85, - JustHit: 86, - SlowMissiles: 87, - ShiverArmor: 88, - BattleCry: 89, - Blue: 90, - Red: 91, - DeathDelay: 92, - Valkyrie: 93, - Frenzy: 94, - Berserk: 95, - Revive: 96, - ItemFullSet: 97, - SourceUnit: 98, - Redeemed: 99, - HealthPot: 100, - HolyShield: 101, - JustPortaled: 102, - MonFrenzy: 103, - CorpseNoDraw: 104, - Alignment: 105, - ManaPot: 106, - Shatter: 107, - SyncWarped: 108, - ConversionSave: 109, - Pregnat: 110, - Rabies: 112, - DefenceCurse: 113, - BloodMana: 114, - Burning: 115, - DragonFlight: 116, - Maul: 117, - CorpseNoSelect: 118, - ShadowWarrior: 119, - FeralRage: 120, - SkillDelay: 121, - ProgressiveDamage: 122, - ProgressiveSteal: 123, - ProgressiveOther: 124, - ProgressiveFire: 125, - ProgressiveCold: 126, - ProgressiveLighting: 127, - ShrineArmor: 128, - ShrineCombat: 129, - ShrineResLighting: 130, - ShrineResFire: 131, - ShrineResCold: 132, - ShrineResPoison: 133, - ShrineSkill: 134, - ShrineManaRegen: 135, - ShrineStamina: 136, - ShrineExperience: 137, - FenrisRage: 138, - Wolf: 139, - Wearwolf: 139, - Bear: 140, - Wearbear: 140, - Bloodlust: 141, - ChangeClass: 142, - Attached: 143, - Hurricane: 144, - Armageddon: 145, - Invis: 146, - Barbs: 147, - HeartofWolverine: 148, - OakSage: 149, - VineBeast: 150, - CycloneArmor: 151, - ClawMastery: 152, - CloakofShadows: 153, - Recyled: 154, - WeaponBlock: 155, - Cloaked: 156, - Quickness: 157, // Internal name - BurstofSpeed: 157, // External name - BladeShield: 158, - Fade: 159, - RestInPeace: 172, - Glowing: 175, - Delerium: 177, - Antidote: 178, - Thawing: 179, - StaminaPot: 180, - }, + // unit states + states: { + None: 0, + FrozenSolid: 1, + Poison: 2, + ResistFire: 3, + ResistCold: 4, + ResistLightning: 5, + ResistMagic: 6, + PlayerBody: 7, + ResistAll: 8, + AmplifyDamage: 9, + FrozenArmor: 10, + Frozen: 11, + Inferno: 12, + Blaze: 13, + BoneArmor: 14, + Concentrate: 15, + Enchant: 16, + InnerSight: 17, + SkillMove: 18, + Weaken: 19, + ChillingArmor: 20, + Stunned: 21, + SpiderLay: 22, + DimVision: 23, + Slowed: 24, + FetishAura: 25, + Shout: 26, + Taunt: 27, + Conviction: 28, + Convicted: 29, + EnergyShield: 30, + Venom: 31, + BattleOrders: 32, + Might: 33, + Prayer: 34, + HolyFire: 35, + Thorns: 36, + Defiance: 37, + ThunderStorm: 38, + LightningBolt: 39, + BlessedAim: 40, + Stamina: 41, + Concentration: 42, + Holywind: 43, + HolyFreeze: 43, + HolywindCold: 44, + HolyFreezeCold: 44, + Cleansing: 45, + HolyShock: 46, + Sanctuary: 47, + Meditation: 48, + Fanaticism: 49, + Redemption: 50, + BattleCommand: 51, + PreventHeal: 52, + Conversion: 53, + Uninterruptable: 54, + IronMaiden: 55, + Terror: 56, + Attract: 57, + LifeTap: 58, + Confuse: 59, + Decrepify: 60, + LowerResist: 61, + OpenWounds: 62, + Dopplezon: 63, + Decoy: 63, + CriticalStrike: 64, + Dodge: 65, + Avoid: 66, + Penetrate: 67, + Evade: 68, + Pierce: 69, + Warmth: 70, + FireMastery: 71, + LightningMastery: 72, + ColdMastery: 73, + SwordMastery: 74, + AxeMastery: 75, + MaceMastery: 76, + PoleArmMastery: 77, + ThrowingMastery: 78, + SpearMastery: 79, + IncreasedStamina: 80, + IronSkin: 81, + IncreasedSpeed: 82, + NaturalResistance: 83, + FingerMageCurse: 84, + NoManaReg: 85, + JustHit: 86, + SlowMissiles: 87, + ShiverArmor: 88, + BattleCry: 89, + Blue: 90, + Red: 91, + DeathDelay: 92, + Valkyrie: 93, + Frenzy: 94, + Berserk: 95, + Revive: 96, + ItemFullSet: 97, + SourceUnit: 98, + Redeemed: 99, + HealthPot: 100, + HolyShield: 101, + JustPortaled: 102, + MonFrenzy: 103, + CorpseNoDraw: 104, + Alignment: 105, + ManaPot: 106, + Shatter: 107, + SyncWarped: 108, + ConversionSave: 109, + Pregnat: 110, + Rabies: 112, + DefenceCurse: 113, + BloodMana: 114, + Burning: 115, + DragonFlight: 116, + Maul: 117, + CorpseNoSelect: 118, + ShadowWarrior: 119, + FeralRage: 120, + SkillDelay: 121, + ProgressiveDamage: 122, + ProgressiveSteal: 123, + ProgressiveOther: 124, + ProgressiveFire: 125, + ProgressiveCold: 126, + ProgressiveLighting: 127, + ShrineArmor: 128, + ShrineCombat: 129, + ShrineResLighting: 130, + ShrineResFire: 131, + ShrineResCold: 132, + ShrineResPoison: 133, + ShrineSkill: 134, + ShrineManaRegen: 135, + ShrineStamina: 136, + ShrineExperience: 137, + FenrisRage: 138, + Wolf: 139, + Wearwolf: 139, + Bear: 140, + Wearbear: 140, + Bloodlust: 141, + ChangeClass: 142, + Attached: 143, + Hurricane: 144, + Armageddon: 145, + Invis: 146, + Barbs: 147, + HeartofWolverine: 148, + OakSage: 149, + VineBeast: 150, + CycloneArmor: 151, + ClawMastery: 152, + CloakofShadows: 153, + Recyled: 154, + WeaponBlock: 155, + Cloaked: 156, + Quickness: 157, // Internal name + BurstofSpeed: 157, // External name + BladeShield: 158, + Fade: 159, + RestInPeace: 172, + Glowing: 175, + Delerium: 177, + Antidote: 178, + Thawing: 179, + StaminaPot: 180, + }, - enchant: { - RandName: 1, - HpMultiply: 2, - AddLightRadius: 3, - AddMLvl: 4, - ExtraStrong: 5, - ExtraFast: 6, - Cursed: 7, - MagicResistant: 8, - FireEnchanted: 9, - PoisonDeath: 10, - InsectDeath: 11, - ChainLightingDeath: 12, - IgnoreTargetDefense: 13, - UnknownMod: 14, - KillMinionsDeath: 15, - ChampMods: 16, - LightningEnchanted: 17, - ColdEnchanted: 18, - UnusedMercMod: 19, - ChargedBoltWhenStruck: 20, - TempSummoned: 21, - QuestMod: 22, - PoisonField: 23, - Thief: 24, - ManaBurn: 25, - Teleportation: 26, - SpectralHit: 27, - StoneSkin: 28, - MultipleShots: 29, - Aura: 30, - CorpseExplosion: 31, - FireExplosionOnDeath: 32, // not sure what the difference is between this and 9 - FreezeOnDeath: 33, - SelfResurrect: 34, - IceShatter: 35, - ChampStoned: 36, - ChampStats: 37, - ChampCurseImmune: 38, - }, + enchant: { + RandName: 1, + HpMultiply: 2, + AddLightRadius: 3, + AddMLvl: 4, + ExtraStrong: 5, + ExtraFast: 6, + Cursed: 7, + MagicResistant: 8, + FireEnchanted: 9, + PoisonDeath: 10, + InsectDeath: 11, + ChainLightingDeath: 12, + IgnoreTargetDefense: 13, + UnknownMod: 14, + KillMinionsDeath: 15, + ChampMods: 16, + LightningEnchanted: 17, + ColdEnchanted: 18, + UnusedMercMod: 19, + ChargedBoltWhenStruck: 20, + TempSummoned: 21, + QuestMod: 22, + PoisonField: 23, + Thief: 24, + ManaBurn: 25, + Teleportation: 26, + SpectralHit: 27, + StoneSkin: 28, + MultipleShots: 29, + Aura: 30, + CorpseExplosion: 31, + FireExplosionOnDeath: 32, // not sure what the difference is between this and 9 + FreezeOnDeath: 33, + SelfResurrect: 34, + IceShatter: 35, + ChampStoned: 36, + ChampStats: 37, + ChampCurseImmune: 38, + }, - // unit stats - stats: { - StunLength: 66, - VelocityPercent: 67, - OtherAnimrate: 69, - HpRegen: 74, + // unit stats + stats: { + StunLength: 66, + VelocityPercent: 67, + OtherAnimrate: 69, + HpRegen: 74, - LastBlockFrame: 95, - State: 98, - MonsterPlayerCount: 100, + LastBlockFrame: 95, + State: 98, + MonsterPlayerCount: 100, - CurseResistance: 109, - IronMaidenLevel: 129, - LifeTapLevel: 130, + CurseResistance: 109, + IronMaidenLevel: 129, + LifeTapLevel: 130, - Alignment: 172, - Target0: 173, - Target1: 174, - GoldLost: 175, - MinimumRequiredLevel: 176, - ConversionLevel: 176, - ConversionMaxHp: 177, - UnitDooverlay: 178, - AttackVsMontype: 179, - DamageVsMontype: 180, + Alignment: 172, + Target0: 173, + Target1: 174, + GoldLost: 175, + MinimumRequiredLevel: 176, + ConversionLevel: 176, + ConversionMaxHp: 177, + UnitDooverlay: 178, + AttackVsMontype: 179, + DamageVsMontype: 180, - ArmorOverridePercent: 182, - FireLength: 315, - BurningMin: 316, - BurningMax: 317, - ProgressiveDamage: 318, - ProgressiveSteal: 319, - ProgressiveOther: 320, - ProgressiveFire: 321, - ProgressiveCold: 322, - ProgressiveLightning: 323, - ProgressiveTohit: 325, - PoisonCount: 326, - DamageFramerate: 327, - PierceIdx: 328, + ArmorOverridePercent: 182, + FireLength: 315, + BurningMin: 316, + BurningMax: 317, + ProgressiveDamage: 318, + ProgressiveSteal: 319, + ProgressiveOther: 320, + ProgressiveFire: 321, + ProgressiveCold: 322, + ProgressiveLightning: 323, + ProgressiveTohit: 325, + PoisonCount: 326, + DamageFramerate: 327, + PierceIdx: 328, - ModifierListSkill: 350, - ModifierListLevel: 351, + ModifierListSkill: 350, + ModifierListLevel: 351, - LastSentHpPct: 352, - SourceUnitType: 353, - SourceUnitId: 354, + LastSentHpPct: 352, + SourceUnitType: 353, + SourceUnitId: 354, - SkillThornsPercent: 131, - SkillBoneArmor: 132, - SkillBoneArmorMax: 133, - SkillFade: 181, - SkillPoisonOverrideLength: 101, - SkillBypassUndead: 103, - SkillBypassDemons: 104, - SkillBypassBeasts: 106, - SkillHandofAthena: 161, - SkillStaminaPercent: 162, - SkillPassiveStaminaPercent: 163, - SkillConcentration: 164, - SkillEnchant: 165, - SkillPierce: 166, - SkillConviction: 167, - SkillChillingArmor: 168, - SkillFrenzy: 169, - SkillDecrepify: 170, - SkillArmorPercent: 171, + SkillThornsPercent: 131, + SkillBoneArmor: 132, + SkillBoneArmorMax: 133, + SkillFade: 181, + SkillPoisonOverrideLength: 101, + SkillBypassUndead: 103, + SkillBypassDemons: 104, + SkillBypassBeasts: 106, + SkillHandofAthena: 161, + SkillStaminaPercent: 162, + SkillPassiveStaminaPercent: 163, + SkillConcentration: 164, + SkillEnchant: 165, + SkillPierce: 166, + SkillConviction: 167, + SkillChillingArmor: 168, + SkillFrenzy: 169, + SkillDecrepify: 170, + SkillArmorPercent: 171, - Strength: 0, - Energy: 1, - Dexterity: 2, - Vitality: 3, - StatPts: 4, - NewSkills: 5, - HitPoints: 6, - MaxHp: 7, - Mana: 8, - MaxMana: 9, - Stamina: 10, - MaxStamina: 11, - Level: 12, - Experience: 13, - Gold: 14, - GoldBank: 15, - ArmorPercent: 16, - MaxDamagePercent: 17, - MinDamagePercent: 18, - EnhancedDamage: 18, - ToHit: 19, - ToBlock: 20, - MinDamage: 21, - MaxDamage: 22, - SecondaryMinDamage: 23, - SecondaryMaxDamage: 24, - DamagePercent: 25, - ManaRecovery: 26, - ManaRecoveryBonus: 27, - StaminaRecoveryBonus: 28, - LastExp: 29, - NextExp: 30, - ArmorClass: 31, - Defense: 31, - ArmorClassVsMissile: 32, - ArmorClassVsHth: 33, - NormalDamageReduction: 34, - MagicDamageReduction: 35, - DamageResist: 36, - MagicResist: 37, - MaxMagicResist: 38, - FireResist: 39, - MaxFireResist: 40, - LightResist: 41, - LightningResist: 41, - MaxLightResist: 42, - ColdResist: 43, - MaxColdResist: 44, - PoisonResist: 45, - MaxPoisonResist: 46, - DamageAura: 47, - FireMinDamage: 48, - FireMaxDamage: 49, - LightMinDamage: 50, - LightMaxDamage: 51, - MagicMinDamage: 52, - MagicMaxDamage: 53, - ColdMinDamage: 54, - ColdMaxDamage: 55, - ColdLength: 56, - PoisonMinDamage: 57, - PoisonMaxDamage: 58, - PoisonLength: 59, - LifeDrainMinDamage: 60, - LifeLeech: 60, - LifeDrainMaxDamage: 61, - ManaDrainMinDamage: 62, - ManaLeech: 62, - ManaDrainMaxDamage: 63, - StaminaDrainMinDamage: 64, - StaminaDrainMaxDamage: 65, - AttackRate: 68, - PreviousSkillRight: 181, - PreviousSkillMiddle: 182, - PreviousSkillLeft: 183, - PassiveFireMastery: 329, - PassiveLightningMastery: 330, - PassiveColdMastery: 331, - PassivePoisonMastery: 332, - PassiveFirePierce: 333, - PassiveLightningPierce: 334, - PassiveColdPierce: 335, - PassivePoisonPierce: 336, - PassiveCriticalStrike: 337, - PassiveDodge: 338, - PassiveAvoid: 339, - PassiveEvade: 340, - PassiveWarmth: 341, - PassiveMasteryMeleeTh: 342, - PassiveMasteryMeleeDmg: 343, - PassiveMasteryMeleeCrit: 344, - PassiveMasteryThrowTh: 345, - PassiveMasteryThrowDmg: 346, - PassiveMasteryThrowCrit: 347, - PassiveWeaponBlock: 348, - PassiveSummonResist: 349, - PassiveMagMastery: 357, - PassiveMagPierce: 358, - Quantity: 70, - Value: 71, - Durability: 72, - MaxDurability: 73, - MaxDurabilityPercent: 75, - MaxHpPercent: 76, - MaxManaPercent: 77, - AttackerTakesDamage: 78, - GoldBonus: 79, - MagicBonus: 80, - Knockback: 81, - TimeDuration: 82, - AddClassSkills: 83, - AddExperience: 85, - HealAfterKill: 86, - ReducedPrices: 87, - DoubleHerbDuration: 88, - LightRadius: 89, - LightColor: 90, - ReqPercent: 91, - LevelReq: 92, - FasterAttackRate: 93, - IAS: 93, - LevelReqPct: 94, - FasterMoveVelocity: 96, - FRW: 96, - NonClassSkill: 97, - OSkill: 97, - FasterGetHitRate: 99, - FHR: 99, - FasterBlockRate: 102, - FBR: 102, - FasterCastRate: 105, - FCR: 105, - SingleSkill: 107, - RestinPeace: 108, - PoisonLengthResist: 110, - NormalDamage: 111, - Howl: 112, - Stupidity: 113, - DamagetoMana: 114, - IgnoreTargetAc: 115, - IgnoreTargetDefense: 115, - FractionalTargetAc: 116, - PreventHeal: 117, - HalfFreezeDuration: 118, - ToHitPercent: 119, - DamageTargetAc: 120, - DemonDamagePercent: 121, - UndeadDamagePercent: 122, - DemontoHit: 123, - UndeadtoHit: 124, - Throwable: 125, - ElemSkill: 126, - AllSkills: 127, - AttackerTakesLightDamage: 128, - Freeze: 134, - OpenWounds: 135, - CrushingBlow: 136, - KickDamage: 137, - ManaAfterKill: 138, - HealAfterDemonKill: 139, - ExtraBlood: 140, - DeadlyStrike: 141, - AbsorbFirePercent: 142, - AbsorbFire: 143, - AbsorbLightPercent: 144, - AbsorbLight: 145, - AbsorbMagicPercent: 146, - AbsorbMagic: 147, - AbsorbColdPercent: 148, - AbsorbCold: 149, - AbsorbSlash: 262, - AbsorbCrush: 263, - AbsorbThrust: 264, - AbsorbSlashPercent: 265, - AbsorbCrushPercent: 266, - AbsorbThrustPercent: 267, - Slow: 150, - Indestructible: 152, - CannotbeFrozen: 153, - StaminaDrainPct: 154, - Reanimate: 155, - Pierce: 156, - MagicArrow: 157, - ExplosiveArrow: 158, - ThrowMinDamage: 159, - ThrowMaxDamage: 160, - AddSkillTab: 188, - NumSockets: 194, - SkillOnAura: 151, - SkillOnAttack: 195, - SkillOnKill: 196, - SkillOnDeath: 197, - SkillOnHit: 198, - SkillOnStrike: 198, - SkillOnLevelUp: 199, - SkillOnGetHit: 201, - SkillWhenStruck: 201, - ChargedSkill: 204, - PerLevelArmor: 214, - PerLevelArmorPercent: 215, - PerLevelHp: 216, - PerLevelMana: 217, - PerLevelMaxDamage: 218, - PerLevelMaxDamagePercent: 219, - PerLevelStrength: 220, - PerLevelDexterity: 221, - PerLevelEnergy: 222, - PerLevelVitality: 223, - PerLevelTohit: 224, - PerLevelTohitPercent: 225, - PerLevelColdDamageMax: 226, - PerLevelFireDamageMax: 227, - PerLevelLtngDamageMax: 228, - PerLevelPoisDamageMax: 229, - PerLevelResistCold: 230, - PerLevelResistFire: 231, - PerLevelResistLtng: 232, - PerLevelResistPois: 233, - PerLevelAbsorbCold: 234, - PerLevelAbsorbFire: 235, - PerLevelAbsorbLtng: 236, - PerLevelAbsorbPois: 237, - PerLevelThorns: 238, - PerLevelFindGold: 239, - PerLevelFindMagic: 240, - PerLevelRegenstamina: 241, - PerLevelStamina: 242, - PerLevelDamageDemon: 243, - PerLevelDamageUndead: 244, - PerLevelTohitDemon: 245, - PerLevelTohitUndead: 246, - PerLevelCrushingblow: 247, - PerLevelOpenwounds: 248, - PerLevelKickDamage: 249, - PerLevelDeadlystrike: 250, - PerLevelFindGems: 251, - ReplenishDurability: 252, - ReplenishQuantity: 253, - ExtraStack: 254, - Find: 255, - SlashDamage: 256, - SlashDamagePercent: 257, - CrushDamage: 258, - CrushDamagePercent: 259, - ThrustDamage: 260, - ThrustDamagePercent: 261, - ArmorByTime: 268, - ArmorPercentByTime: 269, - HpByTime: 270, - ManaByTime: 271, - MaxDamageByTime: 272, - MaxDamagePercentByTime: 273, - StrengthByTime: 274, - DexterityByTime: 275, - EnergyByTime: 276, - VitalityByTime: 277, - TohitByTime: 278, - TohitPercentByTime: 279, - ColdDamageMaxByTime: 280, - FireDamageMaxByTime: 281, - LtngDamageMaxByTime: 282, - PoisDamageMaxByTime: 283, - ResistColdByTime: 284, - ResistFireByTime: 285, - ResistLtngByTime: 286, - ResistPoisByTime: 287, - AbsorbColdByTime: 288, - AbsorbFireByTime: 289, - AbsorbLtngByTime: 290, - AbsorbPoisByTime: 291, - FindGoldByTime: 292, - FindMagicByTime: 293, - RegenstaminaByTime: 294, - StaminaByTime: 295, - DamageDemonByTime: 296, - DamageUndeadByTime: 297, - TohitDemonByTime: 298, - TohitUndeadByTime: 299, - CrushingBlowByTime: 300, - OpenWoundsByTime: 301, - KickDamageByTime: 302, - DeadlyStrikeByTime: 303, - FindGemsByTime: 304, - PierceCold: 305, - PierceFire: 306, - PierceLtng: 307, - PiercePois: 308, - DamageVsMonster: 309, - DamagePercentVsMonster: 310, - TohitVsMonster: 311, - TohitPercentVsMonster: 312, - AcVsMonster: 313, - AcPercentVsMonster: 314, - ExtraCharges: 324, - QuestDifficulty: 356, + Strength: 0, + Energy: 1, + Dexterity: 2, + Vitality: 3, + StatPts: 4, + NewSkills: 5, + HitPoints: 6, + MaxHp: 7, + Mana: 8, + MaxMana: 9, + Stamina: 10, + MaxStamina: 11, + Level: 12, + Experience: 13, + Gold: 14, + GoldBank: 15, + ArmorPercent: 16, + MaxDamagePercent: 17, + MinDamagePercent: 18, + EnhancedDamage: 18, + ToHit: 19, + ToBlock: 20, + MinDamage: 21, + MaxDamage: 22, + SecondaryMinDamage: 23, + SecondaryMaxDamage: 24, + DamagePercent: 25, + ManaRecovery: 26, + ManaRecoveryBonus: 27, + StaminaRecoveryBonus: 28, + LastExp: 29, + NextExp: 30, + ArmorClass: 31, + Defense: 31, + ArmorClassVsMissile: 32, + ArmorClassVsHth: 33, + NormalDamageReduction: 34, + MagicDamageReduction: 35, + DamageResist: 36, + MagicResist: 37, + MaxMagicResist: 38, + FireResist: 39, + MaxFireResist: 40, + LightResist: 41, + LightningResist: 41, + MaxLightResist: 42, + ColdResist: 43, + MaxColdResist: 44, + PoisonResist: 45, + MaxPoisonResist: 46, + DamageAura: 47, + FireMinDamage: 48, + FireMaxDamage: 49, + LightMinDamage: 50, + LightMaxDamage: 51, + MagicMinDamage: 52, + MagicMaxDamage: 53, + ColdMinDamage: 54, + ColdMaxDamage: 55, + ColdLength: 56, + PoisonMinDamage: 57, + PoisonMaxDamage: 58, + PoisonLength: 59, + LifeDrainMinDamage: 60, + LifeLeech: 60, + LifeDrainMaxDamage: 61, + ManaDrainMinDamage: 62, + ManaLeech: 62, + ManaDrainMaxDamage: 63, + StaminaDrainMinDamage: 64, + StaminaDrainMaxDamage: 65, + AttackRate: 68, + PreviousSkillRight: 181, + PreviousSkillMiddle: 182, + PreviousSkillLeft: 183, + PassiveFireMastery: 329, + PassiveLightningMastery: 330, + PassiveColdMastery: 331, + PassivePoisonMastery: 332, + PassiveFirePierce: 333, + PassiveLightningPierce: 334, + PassiveColdPierce: 335, + PassivePoisonPierce: 336, + PassiveCriticalStrike: 337, + PassiveDodge: 338, + PassiveAvoid: 339, + PassiveEvade: 340, + PassiveWarmth: 341, + PassiveMasteryMeleeTh: 342, + PassiveMasteryMeleeDmg: 343, + PassiveMasteryMeleeCrit: 344, + PassiveMasteryThrowTh: 345, + PassiveMasteryThrowDmg: 346, + PassiveMasteryThrowCrit: 347, + PassiveWeaponBlock: 348, + PassiveSummonResist: 349, + PassiveMagMastery: 357, + PassiveMagPierce: 358, + Quantity: 70, + Value: 71, + Durability: 72, + MaxDurability: 73, + MaxDurabilityPercent: 75, + MaxHpPercent: 76, + MaxManaPercent: 77, + AttackerTakesDamage: 78, + GoldBonus: 79, + MagicBonus: 80, + Knockback: 81, + TimeDuration: 82, + AddClassSkills: 83, + AddExperience: 85, + HealAfterKill: 86, + ReducedPrices: 87, + DoubleHerbDuration: 88, + LightRadius: 89, + LightColor: 90, + ReqPercent: 91, + LevelReq: 92, + FasterAttackRate: 93, + IAS: 93, + LevelReqPct: 94, + FasterMoveVelocity: 96, + FRW: 96, + NonClassSkill: 97, + OSkill: 97, + FasterGetHitRate: 99, + FHR: 99, + FasterBlockRate: 102, + FBR: 102, + FasterCastRate: 105, + FCR: 105, + SingleSkill: 107, + RestinPeace: 108, + PoisonLengthResist: 110, + NormalDamage: 111, + Howl: 112, + Stupidity: 113, + DamagetoMana: 114, + IgnoreTargetAc: 115, + IgnoreTargetDefense: 115, + FractionalTargetAc: 116, + PreventHeal: 117, + HalfFreezeDuration: 118, + ToHitPercent: 119, + DamageTargetAc: 120, + DemonDamagePercent: 121, + UndeadDamagePercent: 122, + DemontoHit: 123, + UndeadtoHit: 124, + Throwable: 125, + ElemSkill: 126, + AllSkills: 127, + AttackerTakesLightDamage: 128, + Freeze: 134, + OpenWounds: 135, + CrushingBlow: 136, + KickDamage: 137, + ManaAfterKill: 138, + HealAfterDemonKill: 139, + ExtraBlood: 140, + DeadlyStrike: 141, + AbsorbFirePercent: 142, + AbsorbFire: 143, + AbsorbLightPercent: 144, + AbsorbLight: 145, + AbsorbMagicPercent: 146, + AbsorbMagic: 147, + AbsorbColdPercent: 148, + AbsorbCold: 149, + AbsorbSlash: 262, + AbsorbCrush: 263, + AbsorbThrust: 264, + AbsorbSlashPercent: 265, + AbsorbCrushPercent: 266, + AbsorbThrustPercent: 267, + Slow: 150, + Indestructible: 152, + CannotbeFrozen: 153, + StaminaDrainPct: 154, + Reanimate: 155, + Pierce: 156, + MagicArrow: 157, + ExplosiveArrow: 158, + ThrowMinDamage: 159, + ThrowMaxDamage: 160, + AddSkillTab: 188, + NumSockets: 194, + SkillOnAura: 151, + SkillOnAttack: 195, + SkillOnKill: 196, + SkillOnDeath: 197, + SkillOnHit: 198, + SkillOnStrike: 198, + SkillOnLevelUp: 199, + SkillOnGetHit: 201, + SkillWhenStruck: 201, + ChargedSkill: 204, + PerLevelArmor: 214, + PerLevelArmorPercent: 215, + PerLevelHp: 216, + PerLevelMana: 217, + PerLevelMaxDamage: 218, + PerLevelMaxDamagePercent: 219, + PerLevelStrength: 220, + PerLevelDexterity: 221, + PerLevelEnergy: 222, + PerLevelVitality: 223, + PerLevelTohit: 224, + PerLevelTohitPercent: 225, + PerLevelColdDamageMax: 226, + PerLevelFireDamageMax: 227, + PerLevelLtngDamageMax: 228, + PerLevelPoisDamageMax: 229, + PerLevelResistCold: 230, + PerLevelResistFire: 231, + PerLevelResistLtng: 232, + PerLevelResistPois: 233, + PerLevelAbsorbCold: 234, + PerLevelAbsorbFire: 235, + PerLevelAbsorbLtng: 236, + PerLevelAbsorbPois: 237, + PerLevelThorns: 238, + PerLevelFindGold: 239, + PerLevelFindMagic: 240, + PerLevelRegenstamina: 241, + PerLevelStamina: 242, + PerLevelDamageDemon: 243, + PerLevelDamageUndead: 244, + PerLevelTohitDemon: 245, + PerLevelTohitUndead: 246, + PerLevelCrushingblow: 247, + PerLevelOpenwounds: 248, + PerLevelKickDamage: 249, + PerLevelDeadlystrike: 250, + PerLevelFindGems: 251, + ReplenishDurability: 252, + ReplenishQuantity: 253, + ExtraStack: 254, + Find: 255, + SlashDamage: 256, + SlashDamagePercent: 257, + CrushDamage: 258, + CrushDamagePercent: 259, + ThrustDamage: 260, + ThrustDamagePercent: 261, + ArmorByTime: 268, + ArmorPercentByTime: 269, + HpByTime: 270, + ManaByTime: 271, + MaxDamageByTime: 272, + MaxDamagePercentByTime: 273, + StrengthByTime: 274, + DexterityByTime: 275, + EnergyByTime: 276, + VitalityByTime: 277, + TohitByTime: 278, + TohitPercentByTime: 279, + ColdDamageMaxByTime: 280, + FireDamageMaxByTime: 281, + LtngDamageMaxByTime: 282, + PoisDamageMaxByTime: 283, + ResistColdByTime: 284, + ResistFireByTime: 285, + ResistLtngByTime: 286, + ResistPoisByTime: 287, + AbsorbColdByTime: 288, + AbsorbFireByTime: 289, + AbsorbLtngByTime: 290, + AbsorbPoisByTime: 291, + FindGoldByTime: 292, + FindMagicByTime: 293, + RegenstaminaByTime: 294, + StaminaByTime: 295, + DamageDemonByTime: 296, + DamageUndeadByTime: 297, + TohitDemonByTime: 298, + TohitUndeadByTime: 299, + CrushingBlowByTime: 300, + OpenWoundsByTime: 301, + KickDamageByTime: 302, + DeadlyStrikeByTime: 303, + FindGemsByTime: 304, + PierceCold: 305, + PierceFire: 306, + PierceLtng: 307, + PiercePois: 308, + DamageVsMonster: 309, + DamagePercentVsMonster: 310, + TohitVsMonster: 311, + TohitPercentVsMonster: 312, + AcVsMonster: 313, + AcPercentVsMonster: 314, + ExtraCharges: 324, + QuestDifficulty: 356, - // doesn't exist but define for prototypes - AllRes: 555, - }, + // doesn't exist but define for prototypes + AllRes: 555, + }, - // unit info - unittype: { - Player: 0, - NPC: 1, - Monster: 1, - Object: 2, - Missile: 3, - Item: 4, - Stairs: 5, // ToDo: might be more as stairs - }, + // unit info + unittype: { + Player: 0, + NPC: 1, + Monster: 1, + Object: 2, + Missile: 3, + Item: 4, + Stairs: 5, // ToDo: might be more as stairs + }, - player: { - flag: { - Ignore: 2, - Hostile: 8, - }, - slot: { - Main: 0, - Secondary: 1 - }, - move: { - Walk: 0, - Run: 1 - }, - mode: { // sdk.player.mode. - Death: 0, - StandingOutsideTown: 1, - Walking: 2, - Running: 3, - GettingHit: 4, - StandingInTown: 5, - WalkingInTown: 6, - Attacking1: 7, - Attacking2: 8, - Blocking: 9, - CastingSkill: 10, - ThrowingItem: 11, - Kicking: 12, - UsingSkill1: 13, - UsingSkill2: 14, - UsingSkill3: 15, - UsingSkill4: 16, - Dead: 17, - SkillActionSequence: 18, - KnockedBack: 19, - }, - class: { - Amazon: 0, - Sorceress: 1, - Necromancer: 2, - Paladin: 3, - Barbarian: 4, - Druid: 5, - Assassin: 6, + player: { + flag: { + Ignore: 2, + Hostile: 8, + }, + slot: { + Main: 0, + Secondary: 1 + }, + move: { + Walk: 0, + Run: 1 + }, + mode: { // sdk.player.mode. + Death: 0, + StandingOutsideTown: 1, + Walking: 2, + Running: 3, + GettingHit: 4, + StandingInTown: 5, + WalkingInTown: 6, + Attacking1: 7, + Attacking2: 8, + Blocking: 9, + CastingSkill: 10, + ThrowingItem: 11, + Kicking: 12, + UsingSkill1: 13, + UsingSkill2: 14, + UsingSkill3: 15, + UsingSkill4: 16, + Dead: 17, + SkillActionSequence: 18, + KnockedBack: 19, + }, + class: { + Amazon: 0, + Sorceress: 1, + Necromancer: 2, + Paladin: 3, + Barbarian: 4, + Druid: 5, + Assassin: 6, - nameOf: function (classid) { - if (classid === undefined || typeof classid !== "number") return false; - if (classid < 0 || classid > 6) return false; - return ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"][classid]; - } - } - }, + nameOf: function (classid) { + if (classid === undefined || typeof classid !== "number") return false; + if (classid < 0 || classid > 6) return false; + return ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"][classid]; + } + } + }, - npcs: { - // same as monsters but more clear to use units.npcs.mode - mode: { - Death: 0, - Standing: 1, - Walking: 2, - GettingHit: 3, - Attacking1: 4, - Attacking2: 5, - Blocking: 6, - CastingSkill: 7, - UsingSkill1: 8, - UsingSkill2: 9, - UsingSkill3: 10, - UsingSkill4: 11, - Dead: 12, - KnockedBack: 13, - Spawning: 14, - Running: 15 - }, + npcs: { + // same as monsters but more clear to use units.npcs.mode + mode: { + Death: 0, + Standing: 1, + Walking: 2, + GettingHit: 3, + Attacking1: 4, + Attacking2: 5, + Blocking: 6, + CastingSkill: 7, + UsingSkill1: 8, + UsingSkill2: 9, + UsingSkill3: 10, + UsingSkill4: 11, + Dead: 12, + KnockedBack: 13, + Spawning: 14, + Running: 15 + }, - Akara: 148, - Alkor: 254, - Asheara: 252, - WarrivAct1: 155, - WarrivAct2: 175, - Atma: 176, - Tyrael: 367, - Tyrael2: 251, - Tyrael3: 521, - Charsi: 154, - DeckardCain1: 146, - DeckardCain2: 244, - DeckardCain3: 245, - DeckardCain4: 246, - DeckardCain5: 265, - DeckardCain6: 520, - Drognan: 177, - Elzix: 199, - Fara: 178, - Gheed: 147, - Greiz: 198, - Halbu: 257, - Hratli: 253, - Jamella: 405, - Jerhyn: 201, - Kaelan: 331, - Kashya: 150, - Larzuk: 511, - Lysander: 202, - Malah: 513, - Meshif: 210, - Meshif2: 264, - Natalya: 297, - Ormus: 255, - NihlathakNPC: 526, - Qualkehk: 515, - RogueScout: 270, - TempleGuard1: 52, - TempleGuard2: 665, - TempleGuard3: 666, - Townguard1: 535, - Townguard2: 536, - }, + Akara: 148, + Alkor: 254, + Asheara: 252, + WarrivAct1: 155, + WarrivAct2: 175, + Atma: 176, + Tyrael: 367, + Tyrael2: 251, + Tyrael3: 521, + Charsi: 154, + DeckardCain1: 146, + DeckardCain2: 244, + DeckardCain3: 245, + DeckardCain4: 246, + DeckardCain5: 265, + DeckardCain6: 520, + Drognan: 177, + Elzix: 199, + Fara: 178, + Gheed: 147, + Greiz: 198, + Halbu: 257, + Hratli: 253, + Jamella: 405, + Jerhyn: 201, + Kaelan: 331, + Kashya: 150, + Larzuk: 511, + Lysander: 202, + Malah: 513, + Meshif: 210, + Meshif2: 264, + Natalya: 297, + Ormus: 255, + NihlathakNPC: 526, + Qualkehk: 515, + RogueScout: 270, + TempleGuard1: 52, + TempleGuard2: 665, + TempleGuard3: 666, + Townguard1: 535, + Townguard2: 536, + }, - objects: { - mode: { - Inactive: 0, - Interacted: 1, - Active: 2, - }, + objects: { + mode: { + Inactive: 0, + Interacted: 1, + Active: 2, + }, - chestIds: [ - 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 - ], + chestIds: [ + 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 + ], - // act1 - MoldyTome: 8, - A1TownFire: 39, - A1Waypoint: 119, - StoneAlpha: 17, - StoneBeta: 18, - StoneGamma: 19, - StoneDelta: 20, - StoneLambda: 21, - StoneTheta: 22, - CainsJail: 26, - InifussTree: 30, - Malus: 108, + // act1 + MoldyTome: 8, + A1TownFire: 39, + A1Waypoint: 119, + StoneAlpha: 17, + StoneBeta: 18, + StoneGamma: 19, + StoneDelta: 20, + StoneLambda: 21, + StoneTheta: 22, + CainsJail: 26, + InifussTree: 30, + Malus: 108, - // act 2 - A2Waypoint: 156, - A2UndergroundUpStairs: 22, - TrapDoorA2: 74, // ancienttunnel/sewers act 2 - DoorbyDockAct2: 75, // incorrect ? TODO: figure out what 75 really corresponds to since the door is obj type 5 with classid 20 - PortaltoDurielsLair: 100, - HoradricStaffHolder: 152, - ArcaneSanctuaryPortal: 298, - HoradricCubeChest: 354, - HoradricScrollChest: 355, - Journal: 357, + // act 2 + A2Waypoint: 156, + A2UndergroundUpStairs: 22, + TrapDoorA2: 74, // ancienttunnel/sewers act 2 + DoorbyDockAct2: 75, // incorrect ? TODO: figure out what 75 really corresponds to since the door is obj type 5 with classid 20 + PortaltoDurielsLair: 100, + HoradricStaffHolder: 152, + ArcaneSanctuaryPortal: 298, + HoradricCubeChest: 354, + HoradricScrollChest: 355, + Journal: 357, - // act 3 - A3Waypoint: 237, - ForestAltar: 81, - LamEsensTome: 193, - SewerStairsA3: 366, - SewerLever: 367, - DuranceEntryStairs: 386, - RedPortalToAct4: 342, - CompellingOrb: 404, + // act 3 + A3Waypoint: 237, + ForestAltar: 81, + LamEsensTome: 193, + SewerStairsA3: 366, + SewerLever: 367, + DuranceEntryStairs: 386, + RedPortalToAct4: 342, + CompellingOrb: 404, - // act 4 - A4Waypoint: 398, - SealGlow: 131, - DiabloStar: 255, - DiabloSealInfector: 392, - DiabloSealInfector2: 393, - DiabloSealSeis: 394, - DiabloSealVizier: 396, - DiabloSealVizier2: 395, - RedPortalToAct5: 566, // The one of tyreal + // act 4 + A4Waypoint: 398, + SealGlow: 131, + DiabloStar: 255, + DiabloSealInfector: 392, + DiabloSealInfector2: 393, + DiabloSealSeis: 394, + DiabloSealVizier: 396, + DiabloSealVizier2: 395, + RedPortalToAct5: 566, // The one of tyreal - // act 5 - A5Waypoint: 429, - SideCavesA5: 75, // FrozenRiver, DrifterCavern, IcyCellar - Act5Gate: 449, - KorlictheProtectorStatue: 474, - TalictheDefenderStatue: 475, - MadawctheGuardianStatue: 476, - AncientsAltar: 546, - ArreatEnterAncientsWay: 564, - ArreatEnterWorldstone: 547, - //AncientsDoor: 547, - AncientsDoor: 547, // Worldstone keep lvl 1 - FrozenAnya: 558, - FrozenAnyasPlatform: 460, - NihlathaksPlatform: 462, - WorldstonePortal: 563, + // act 5 + A5Waypoint: 429, + SideCavesA5: 75, // FrozenRiver, DrifterCavern, IcyCellar + Act5Gate: 449, + KorlictheProtectorStatue: 474, + TalictheDefenderStatue: 475, + MadawctheGuardianStatue: 476, + AncientsAltar: 546, + ArreatEnterAncientsWay: 564, + ArreatEnterWorldstone: 547, + //AncientsDoor: 547, + AncientsDoor: 547, // Worldstone keep lvl 1 + FrozenAnya: 558, + FrozenAnyasPlatform: 460, + NihlathaksPlatform: 462, + WorldstonePortal: 563, - FrigidHighlandsChest: 455, - IcyCellarChest: 397, + FrigidHighlandsChest: 455, + IcyCellarChest: 397, - SmallSparklyChest: 397, - LargeSparklyChest: 455, - SuperChest: 580, + SmallSparklyChest: 397, + LargeSparklyChest: 455, + SuperChest: 580, - // misc - BubblingPoolofBlood: 82, - HornShrine: 83, - Stash: 267, - BluePortal: 59, - RedPortal: 60, - Smoke: 401, - }, + // misc + BubblingPoolofBlood: 82, + HornShrine: 83, + Stash: 267, + BluePortal: 59, + RedPortal: 60, + Smoke: 401, + }, - exits: { - type: { - WalkThrough: 1, - Stairs: 2, - RedPortal: 60, - }, - preset: { - AreaEntrance: 0, // special - // act 1 - CaveHoleUp: 4, - CaveHoleLvl2: 5, - Crypt: 6, - Mausoleum: 7, - CryptMausExit: 8, - JailUpStairs: 13, - JailDownStairs: 14, - CathedralDownStairs: 15, - CathedralUpStairs: 16, - CatacombsUpStairs: 17, - CatacombsDownStairs: 18, + exits: { + type: { + WalkThrough: 1, + Stairs: 2, + RedPortal: 60, + }, + preset: { + AreaEntrance: 0, // special + // act 1 + CaveHoleUp: 4, + CaveHoleLvl2: 5, + Crypt: 6, + Mausoleum: 7, + CryptMausExit: 8, + JailUpStairs: 13, + JailDownStairs: 14, + CathedralDownStairs: 15, + CathedralUpStairs: 16, + CatacombsUpStairs: 17, + CatacombsDownStairs: 18, - // act 2 - A2SewersTrapDoor: 19, - A2EnterSewersDoor: 20, - A2ExitSewersDoor: 21, - A2UndergroundUpStairs: 22, - A2DownStairs: 23, - EnterHaremStairs: 24, - ExitHaremStairs: 25, - PreviousLevelHaremRight: 26, - PreviousLevelHaremLeft: 27, - NextLevelHaremRight: 28, - NextLevelHaremLeft: 29, - PreviousPalaceRight: 30, - PreviousPalaceLeft: 31, - NextLevelPalace: 32, - EnterStonyTomb: 33, - EnterHalls: 36, - EnterTalTomb1: 38, - EnterTalTomb2: 39, - EnterTalTomb3: 40, - EnterTalTomb4: 41, - EnterTalTomb5: 42, - EnterTalTomb6: 43, - EnterTalTomb7: 44, - PreviousAreaTomb: 45, - NextLevelTomb: 46, - EnterMaggotLair: 47, - PreviousAreaMaggotLair: 48, - NextLevelMaggotLair: 49, - AncientTunnelsTrapDoor: 50, - EntrancetoDurielsLair: 100, + // act 2 + A2SewersTrapDoor: 19, + A2EnterSewersDoor: 20, + A2ExitSewersDoor: 21, + A2UndergroundUpStairs: 22, + A2DownStairs: 23, + EnterHaremStairs: 24, + ExitHaremStairs: 25, + PreviousLevelHaremRight: 26, + PreviousLevelHaremLeft: 27, + NextLevelHaremRight: 28, + NextLevelHaremLeft: 29, + PreviousPalaceRight: 30, + PreviousPalaceLeft: 31, + NextLevelPalace: 32, + EnterStonyTomb: 33, + EnterHalls: 36, + EnterTalTomb1: 38, + EnterTalTomb2: 39, + EnterTalTomb3: 40, + EnterTalTomb4: 41, + EnterTalTomb5: 42, + EnterTalTomb6: 43, + EnterTalTomb7: 44, + PreviousAreaTomb: 45, + NextLevelTomb: 46, + EnterMaggotLair: 47, + PreviousAreaMaggotLair: 48, + NextLevelMaggotLair: 49, + AncientTunnelsTrapDoor: 50, + EntrancetoDurielsLair: 100, - // act 3 - EnterSpiderHole: 51, - ExitSpiderHole: 52, - EnterPit: 53, - EnterDungeon: 54, - PreviousAreaDungeon: 55, - NextLevelDungeon: 56, - A3EnterSewers: 57, - A3ExitSewersUpperK: 58, - A3SewersPreviousArea: 58, - A3ExitSewers: 59, - A3NextLevelSewers: 60, - EnterTemple: 61, - ExitTemple: 63, - EnterDurance: 64, - PreviousLevelDurance: 65, - NextLevelDurance: 68, - SewerStairsA3: 366, - DuranceEntryStairs: 386, + // act 3 + EnterSpiderHole: 51, + ExitSpiderHole: 52, + EnterPit: 53, + EnterDungeon: 54, + PreviousAreaDungeon: 55, + NextLevelDungeon: 56, + A3EnterSewers: 57, + A3ExitSewersUpperK: 58, + A3SewersPreviousArea: 58, + A3ExitSewers: 59, + A3NextLevelSewers: 60, + EnterTemple: 61, + ExitTemple: 63, + EnterDurance: 64, + PreviousLevelDurance: 65, + NextLevelDurance: 68, + SewerStairsA3: 366, + DuranceEntryStairs: 386, - // act 4 - EnterRiverStairs: 69, - ExitRiverStairs: 70, - // act 5 - EnterCrystal: 71, - A5ExitCave: 73, - A5NextLevelCave: 74, - EnterSubLevelCave: 75, - EnterNithsTemple: 76, - PreviousAreaNithsTemple: 77, - NextAreaNithsTemple: 78, - ArreatEnterAncientsWay: 79, - ArreatEnterWorldstone: 80, - PreviousAreaWorldstone: 81, - NextAreaWorldstone: 82, - }, - }, + // act 4 + EnterRiverStairs: 69, + ExitRiverStairs: 70, + // act 5 + EnterCrystal: 71, + A5ExitCave: 73, + A5NextLevelCave: 74, + EnterSubLevelCave: 75, + EnterNithsTemple: 76, + PreviousAreaNithsTemple: 77, + NextAreaNithsTemple: 78, + ArreatEnterAncientsWay: 79, + ArreatEnterWorldstone: 80, + PreviousAreaWorldstone: 81, + NextAreaWorldstone: 82, + }, + }, - monsters: { - preset: { - // Confirmed - Izual: 256, - Bishibosh: 734, - Bonebreak: 735, - Coldcrow: 736, - Rakanishu: 737, - TreeheadWoodFist: 738, - Griswold: 739, - TheCountess: 740, - PitspawnFouldog: 741, - FlamespiketheCrawler: 742, - BoneAsh: 743, - Radament: 744, - BloodwitchtheWild: 745, - Fangskin: 746, - Beetleburst: 747, - CreepingFeature: 748, - ColdwormtheBurrower: 749, - FireEye: 750, - DarkElder: 751, - TheSummoner: 752, - AncientKaatheSoulless: 753, - TheSmith: 754, - SszarktheBurning: 755, - WitchDoctorEndugu: 756, - Stormtree: 757, - BattlemaidSarina: 758, - IcehawkRiftwing: 759, - IsmailVilehand: 760, - GelebFlamefinger: 761, - BremmSparkfist: 762, - ToorcIcefist: 763, - WyandVoidfinger: 764, - MafferDragonhand: 765, - WingedDeath: 766, - Taintbreeder: 768, - RiftwraiththeCannibal: 769, - InfectorofSouls: 770, - LordDeSeis: 771, - GrandVizierofChaos: 772, - TheCowKing: 773, - Corpsefire: 774, - Hephasto: 775, - ShenktheOverseer: 776, - TalictheDefender: 777, - MadawctheGuardian: 778, - KorlictheProtector: 779, - AxeDweller: 780, - BonesawBreaker: 781, - DacFarren: 782, - EldritchtheRectifier: 783, - EyebacktheUnleashed: 784, - ThreshSocket: 785, - Pindleskin: 786, - SnapchipShatter: 787, - AnodizedElite: 788, - VinvearMolech: 789, - SharpToothSayer: 790, - MagmaTorquer: 791, - BlazeRipper: 792, - Frozenstein: 793, - Nihlathak: 794, - ColenzotheAnnihilator: 795, - AchmeltheCursed: 796, - BartuctheBloody: 797, - VentartheUnholy: 798, - ListertheTormentor: 799, - BloodRaven: 805, + monsters: { + preset: { + // Confirmed + Izual: 256, + Bishibosh: 734, + Bonebreak: 735, + Coldcrow: 736, + Rakanishu: 737, + TreeheadWoodFist: 738, + Griswold: 739, + TheCountess: 740, + PitspawnFouldog: 741, + FlamespiketheCrawler: 742, + BoneAsh: 743, + Radament: 744, + BloodwitchtheWild: 745, + Fangskin: 746, + Beetleburst: 747, + CreepingFeature: 748, + ColdwormtheBurrower: 749, + FireEye: 750, + DarkElder: 751, + TheSummoner: 752, + AncientKaatheSoulless: 753, + TheSmith: 754, + SszarktheBurning: 755, + WitchDoctorEndugu: 756, + Stormtree: 757, + BattlemaidSarina: 758, + IcehawkRiftwing: 759, + IsmailVilehand: 760, + GelebFlamefinger: 761, + BremmSparkfist: 762, + ToorcIcefist: 763, + WyandVoidfinger: 764, + MafferDragonhand: 765, + WingedDeath: 766, + Taintbreeder: 768, + RiftwraiththeCannibal: 769, + InfectorofSouls: 770, + LordDeSeis: 771, + GrandVizierofChaos: 772, + TheCowKing: 773, + Corpsefire: 774, + Hephasto: 775, + ShenktheOverseer: 776, + TalictheDefender: 777, + MadawctheGuardian: 778, + KorlictheProtector: 779, + AxeDweller: 780, + BonesawBreaker: 781, + DacFarren: 782, + EldritchtheRectifier: 783, + EyebacktheUnleashed: 784, + ThreshSocket: 785, + Pindleskin: 786, + SnapchipShatter: 787, + AnodizedElite: 788, + VinvearMolech: 789, + SharpToothSayer: 790, + MagmaTorquer: 791, + BlazeRipper: 792, + Frozenstein: 793, + Nihlathak: 794, + ColenzotheAnnihilator: 795, + AchmeltheCursed: 796, + BartuctheBloody: 797, + VentartheUnholy: 798, + ListertheTormentor: 799, + BloodRaven: 805, - // Unconfirmed - // Questionable - GriefGrumble: 741, // JailLvl2 - UniqueJailLvl3: 273, - UniqueArcaneSanctuary: 371, - }, - mode: { - Death: 0, - Standing: 1, - Walking: 2, - GettingHit: 3, - Attacking1: 4, - Attacking2: 5, - Blocking: 6, - CastingSkill: 7, - UsingSkill1: 8, - UsingSkill2: 9, - UsingSkill3: 10, - UsingSkill4: 11, - Dead: 12, - KnockedBack: 13, - Spawning: 14, - Running: 15 - }, - spectype: { - All: 0, - Super: 1, - Champion: 2, - Unique: 4, - SuperUnique: 5, - Magic: 6, - Minion: 8, - }, - // todo - determine what all these correlate to - type: { - Undead: 1, - Demon: 2, - Insect: 3, - Human: 4, - Construct: 5, - LowUndead: 6, - HighUndead: 7, - Skeleton: 8, - Zombie: 9, - BigHead: 10, - FoulCrow: 11, - Fallen: 12, - Brute: 13, - SandRaider: 14, - Wraith: 15, - CorruptRogue: 16, - Baboon: 17, - GoatMan: 18, - QuillRat: 19, - SandMaggot: 20, - Viper: 21, - SandLeaper: 22, - PantherWoman: 23, - Swarm: 24, - Scarab: 25, - Mummy: 26, - Unraveler: 27, - Vulture: 28, - Mosquito: 29, - WillowWisp: 30, - Arach: 31, - ThornHulk: 32, - Vampire: 33, - BatDemon: 34, - Fetish: 35, - Blunderbore: 36, - UndeadFetish: 37, - Zakarum: 38, - FrogDemon: 39, - Tentacle: 40, - FingerMage: 41, - Golem: 42, - Vilekind: 43, - Regurgitator: 44, - DoomKnight: 45, - CouncilMember: 46, - MegaDemon: 47, - Bovine: 48, - SeigeBeast: 49, - SnowYeti: 50, - Minion: 51, - Succubus: 52, - Overseer: 53, - Imp: 54, - FrozenHorror: 55, - BloodLord: 56, - DeathMauler: 57, - PutridDefiler: 58, - }, - DiablosBoneCage: 340, - Dummy1: 149, - Dummy2: 268, - AbyssKnight: 311, - Afflicted: 10, - Afflicted2: 580, - AlbinoRoach: 95, - Ancient1: 104, - Ancient2: 669, - Ancient3: 670, - Apparition: 41, - Arach1: 122, - Arach2: 685, - Assailant: 33, - Assailant2: 603, - BaalColdMage: 381, - Balrog1: 360, - Balrog2: 686, - Banished: 135, - Barbs: 422, - Bear1: 428, - Bear2: 431, - Beast: 441, - BerserkSlayer: 462, - BlackArcher: 163, - BlackLancer1: 168, - BlackLancer2: 617, - BlackLocusts: 88, - BlackRaptor1: 17, - BlackRaptor2: 592, - BlackRogue: 46, - BlackSoul1: 121, - BlackSoul2: 640, - BlackVultureNest: 208, - BloodBoss: 482, - BloodBringer: 443, - BloodClan1: 55, - BloodClan2: 588, - BloodDiver: 139, - BloodGolem: 290, - BloodHawk1: 16, - BloodHawk2: 591, - BloodHawkNest: 207, - BloodHook: 116, - BloodHookNest: 336, - BloodLord1: 134, - BloodLord2: 695, - BloodWing: 117, - BloodWingNest: 337, - Blunderbore1: 186, - Blunderbore2: 618, - BoneArcher1: 172, - BoneArcher2: 576, - BoneMage1: 275, - BoneMage2: 380, - BoneMage3: 384, - BoneMage4: 388, - BoneMage5: 624, - BoneWarrior1: 2, - BoneWarrior2: 648, - HellBovine: 391, - BrambleHulk: 128, - Brute: 24, - Bunny: 556, - BurningDead: 3, - BurningDeadArcher1: 173, - BurningDeadArcher2: 575, - BurningDeadArcher3: 577, - BurningDeadMage1: 276, - BurningDeadMage2: 385, - BurningDeadMage3: 389, - BurningDeadMage4: 621, - BurningSoul1: 641, - BurningSoul2: 120, - Cadaver1: 100, - Cadaver2: 703, - Cantor: 239, - CarrionBird1: 110, - CarrionBird2: 608, - Carver1: 642, - Carver2: 20, - CarverShaman: 645, - CarverShaman2: 59, - CaveLeaper1: 79, - CaveLeaper2: 629, - ClawViper1: 74, - ClawViper2: 594, - CloudStalker1: 18, - CloudStalker2: 593, - CloudStalkerNest: 209, - Combatant1: 522, - Combatant2: 523, - ConsumedFireBoar: 464, - ConsumedIceBoar: 463, - CorpseSpitter: 308, - Corpulent: 307, - Creature1: 248, - Creature2: 427, - Creeper: 413, - CrushBiest: 442, - Crusher: 26, - Damned1: 14, - Damned2: 584, - DarkArcher1: 162, - DarkArcher2: 614, - DarkFamiliar: 140, - DarkHunter: 43, - DarkLancer1: 167, - DarkLancer2: 616, - DarkLord1: 133, - DarkLord2: 697, - DarkOne1: 22, - DarkOne2: 644, - DarkRanger: 160, - DarkShaman1: 61, - DarkShaman2: 647, - DarkShape: 42, - DarkSpearwoman: 165, - DarkStalker: 45, - DeamonSteed: 445, - DeathClan1: 57, - DeathClan2: 589, - Decayed: 97, - DefiledWarrior: 440, - Defiler1: 546, - Defiler2: 547, - Defiler3: 548, - Defiler4: 549, - Defiler5: 550, - DesertWing: 136, - Destruction: 410, - Devilkin: 643, - Devilkin2: 21, - DevilkinShaman: 646, - DevilkinShaman2: 60, - Devourer: 70, - DevourerEgg: 192, - DevourerQueen: 286, - DevourerYoung: 182, - Disfigured: 13, - Disfigured2: 583, - Dominus1: 474, - Dominus2: 636, - DoomApe: 51, - DoomKnight: 310, - DoomKnight1: 699, - DoomKnight2: 700, - Drehya1: 512, - Drehya2: 527, - DriedCorpse: 96, - DrownedCarcass: 8, - DuneBeast: 48, - DungSoldier: 91, - Dweller: 247, - Eagle: 429, - Embalmed: 98, - Faithful: 236, - Fallen: 19, - FallenShaman: 58, - FanaticMinion: 461, - Feeder: 115, - FeederNest: 335, - Fenris: 421, - Fetish1: 142, - BoneFetish2: 213, - Fetish3: 397, - FetishShaman: 279, - Fiend1: 137, - Fiend2: 651, - FireBoar: 456, - FireTower: 372, - FlameSpider: 125, - Flayer1: 143, - BoneFetish3: 214, - Flayer3: 398, - Flayer4: 659, - Flayer5: 656, - FlayerShaman1: 280, - FlayerShaman2: 662, - FleshArcher: 164, - FleshBeast1: 301, - FleshBeast2: 678, - FleshHunter: 47, - FleshLancer: 169, - FleshSpawner1: 298, - FleshSpawner2: 676, - FlyingScimitar: 234, - FoulCrow: 15, - FoulCrow2: 590, - FoulCrowNest: 206, - FrenziedHellSpawn: 465, - FrenziedIceSpawn: 466, - GargantuanBeast: 28, - Geglash: 200, - Ghost1: 38, - Ghost2: 631, - Ghoul: 7, - GhoulLord1: 131, - GhoulLord2: 696, - GiantLamprey: 71, - GiantLampreyEgg: 193, - GiantLampreyQueen: 287, - GiantLampreyYoung: 183, - GiantUrchin: 317, - Gloam1: 118, - Gloam2: 639, - Gloombat1: 138, - Gloombat2: 650, - Gorbelly: 187, - GoreBearer: 444, - GreaterHellSpawn1: 459, - GreaterHellSpawn2: 684, - GreaterIceSpawn: 460, - Groper: 304, - Grotesque1: 300, - Grotesque2: 675, - GrotesqueWyrm1: 303, - GrotesqueWyrm2: 677, - Guardian1: 102, - Guardian2: 667, - Hawk: 419, - Heirophant1: 240, - Heirophant2: 241, - Heirophant3: 673, - Heirophant4: 674, - HellBuzzard: 112, - HellCat: 86, - HellClan1: 56, - HellClan2: 587, - HellSlinger: 376, - HellSpawn1: 457, - HellSpawn2: 683, - HellSwarm: 90, - HellWhip: 483, - HollowOne: 101, - Horror: 4, - Horror1: 501, - Horror2: 502, - Horror3: 503, - Horror4: 504, - Horror5: 505, - HorrorArcher1: 174, - HorrorArcher2: 579, - HorrorMage1: 277, - HorrorMage2: 382, - HorrorMage3: 386, - HorrorMage4: 390, - HorrorMage5: 623, - HorrorMage6: 625, - HorrorMage7: 626, - Hs1: 560, - HungryDead: 6, - Huntress1: 83, - Huntress2: 627, - Hut: 528, - Hydra1: 351, - Hydra2: 352, - Hydra3: 353, - IceBoar: 455, - IceSpawn: 458, - Imp1: 492, - Imp2: 493, - Imp3: 494, - Imp4: 495, - Imp5: 496, - Imp6: 688, - Imp7: 689, - Infidel1: 32, - Infidel2: 600, - InsaneHellSpawn: 467, - InsaneIceSpawn: 468, - Invader1: 31, - Invader2: 602, - Itchies: 87, - JungleHunter: 50, - JungleUrchin: 67, - Larva: 283, - Lasher: 480, - LightningSpire: 371, - Lord1: 506, - Lord2: 507, - Lord3: 508, - Lord4: 509, - Lord5: 510, - Lord6: 652, - Lord7: 653, - Maggot: 227, - Malachai: 408, - Marauder: 30, - Marauder2: 599, - Master: 418, - Mauler: 188, - Mauler1: 529, - Mauler12: 604, - Mauler2: 530, - Mauler3: 531, - Mauler4: 532, - Mauler5: 533, - Mauler6: 619, - MawFiend: 694, - MawFiend2: 309, - Council1: 345, - Council2: 346, - Council3: 347, - Council4: 557, - Minion1: 572, - Minion2: 573, - Enslaved: 453, - MinionSlayerSpawner: 485, - MinionSpawner: 484, - Misshapen1: 12, - Misshapen2: 582, - MoonClan1: 53, - MoonClan2: 585, - BaalSubjectMummy: 105, - Navi: 266, - Flavie: 266, - NightClan1: 54, - NightClan2: 586, - NightLord: 132, - NightMarauder: 295, - NightSlinger1: 375, - NightSlinger2: 395, - NightTiger: 85, - OblivionKnight1: 312, - OblivionKnight2: 701, - OblivionKnight3: 702, - OverLord: 481, - OverSeer: 479, - PitLord1: 361, - PitLord2: 687, - PitViper1: 76, - PitViper2: 595, - PlagueBearer: 9, - PlagueBugs: 89, - PoisonSpinner: 124, - PreservedDead: 99, - ProwlingDead: 438, - QuillBear: 313, - QuillRat1: 63, - QuillRat2: 605, - RatMan1: 141, - RatMan2: 396, - BoneFetish1: 212, - RatMan4: 407, - RatManShaman: 278, - RazorBeast: 316, - RazorPitDemon: 82, - RazorSpine1: 66, - RazorSpine2: 607, - ReanimatedHorde: 437, - Returned1: 1, - Returned2: 649, - ReturnedArcher1: 171, - ReturnedArcher2: 578, - ReturnedMage: 274, - ReturnedMage1: 379, - ReturnedMage2: 383, - ReturnedMage3: 387, - ReturnedMage4: 620, - ReturnedMage5: 622, - RiverStalkerHead: 262, - RiverStalkerLimb: 259, - RockDweller: 49, - RockWorm: 69, - RockWormEgg: 191, - RockWormQueen: 285, - RockWormYoung: 181, - RotWalker: 436, - SaberCat1: 84, - SaberCat2: 628, - Salamander1: 75, - Salamander2: 596, - SandFisher: 123, - SandLeaper: 78, - SandMaggot: 68, - SandMaggotEgg: 190, - SandMaggotYoung: 180, - SandRaider1: 29, - SandRaider2: 601, - SandWarrior: 92, - Scarab1: 93, - Scarab2: 654, - Sentry1: 411, - Sentry2: 412, - Sentry3: 415, - Sentry4: 416, - SerpentMagus1: 77, - SerpentMagus2: 598, - Sexton: 238, - Skeleton: 0, - SkeletonArcher: 170, - Slayerexp1: 454, - Slayerexp2: 682, - Slinger1: 373, - Slinger2: 610, - Slinger3: 611, - Slinger4: 612, - SnowYeti1: 446, - SnowYeti2: 447, - SnowYeti3: 448, - SnowYeti4: 449, - SoulKiller: 691, - SoulKiller1: 399, - SoulKiller2: 144, - SoulKiller3: 215, - SoulKiller4: 658, - SoulKiller5: 661, - SoulKillerShaman1: 664, - SoulKillerShaman2: 281, - SpearCat: 394, - SpearCat1: 374, - Specter1: 40, - Specter2: 633, - SpiderMagus: 126, - SpikeFiend1: 64, - SpikeFiend2: 606, - Spikefist: 130, - SpikeGiant: 314, - SteelWeevil1: 94, - SteelWeevil2: 655, - StormCaster1: 306, - StormCaster2: 693, - Strangler1: 305, - Strangler2: 692, - StygianDog: 302, - StygianDoll1: 145, - StygianDoll2: 216, - StygianDoll3: 400, - StygianDoll4: 660, - StygianDoll5: 657, - StygianDoll6: 690, - StygianDollShaman1: 663, - StygianDollShaman2: 282, - StygianFury: 476, - StygianHag: 299, - StygianHarlot: 471, - StygianWatcherHead: 263, - StygianWatcherLimb: 260, - Succubusexp1: 469, - Succubusexp2: 634, - Sucker: 114, - SuckerNest: 334, - Summoner: 250, - SwampGhost: 119, - Tainted: 11, - Tainted2: 581, - Taunt: 545, - Temptress1: 472, - Temptress2: 473, - Temptress3: 635, - Tentacle1: 562, - Tentacle2: 563, - Tentacle3: 564, - Tentacle4: 565, - Tentacle5: 566, - ThornBeast: 65, - ThornBrute: 315, - ThornedHulk1: 127, - ThornedHulk2: 609, - Thrasher: 129, - TombCreeper1: 80, - TombCreeper2: 630, - TombViper1: 73, - TombViper2: 597, - TrappedSoul1: 403, - TrappedSoul2: 404, - TreeLurker: 81, - UndeadScavenger: 111, - UnholyCorpse1: 439, - UnholyCorpse2: 698, - Unraveler1: 103, - Unraveler2: 668, - Urdar: 189, - VenomLord1: 362, - VenomLord2: 558, - VileArcher1: 161, - VileArcher2: 613, - VileHunter: 44, - VileLancer1: 166, - VileLancer2: 615, - VileTemptress: 470, - VileWitch1: 475, - VileWitch2: 638, - WailingBeast: 27, - WarpedFallen: 23, - WarpedShaman: 62, - Warrior: 417, - WaterWatcherHead: 261, - WaterWatcherLimb: 258, - WingedNightmare: 113, - Witch1: 637, - Witch2: 477, - Witch3: 478, - Wolf1: 359, - Wolf2: 420, - Wolf3: 430, - WolfRider1: 450, - WolfRider2: 451, - WolfRider3: 452, - WorldKiller1: 679, - WorldKiller2: 72, - WorldKillerEgg1: 681, - WorldKillerEgg2: 194, - WorldKillerQueen: 288, - WorldKillerYoung1: 680, - WorldKillerYoung2: 184, - Worm1: 551, - Worm2: 552, - Worm3: 553, - Worm4: 554, - Worm5: 555, - Wraith1: 39, - Wraith2: 632, - Yeti: 25, - Zakarumite: 235, - Zealot1: 237, - Zealot2: 671, - Zealot3: 672, - Zombie: 5, + // Unconfirmed + // Questionable + GriefGrumble: 741, // JailLvl2 + UniqueJailLvl3: 273, + UniqueArcaneSanctuary: 371, + }, + mode: { + Death: 0, + Standing: 1, + Walking: 2, + GettingHit: 3, + Attacking1: 4, + Attacking2: 5, + Blocking: 6, + CastingSkill: 7, + UsingSkill1: 8, + UsingSkill2: 9, + UsingSkill3: 10, + UsingSkill4: 11, + Dead: 12, + KnockedBack: 13, + Spawning: 14, + Running: 15 + }, + spectype: { + All: 0, + Super: 1, + Champion: 2, + Unique: 4, + SuperUnique: 5, + Magic: 6, + Minion: 8, + }, + // todo - determine what all these correlate to + type: { + Undead: 1, + Demon: 2, + Insect: 3, + Human: 4, + Construct: 5, + LowUndead: 6, + HighUndead: 7, + Skeleton: 8, + Zombie: 9, + BigHead: 10, + FoulCrow: 11, + Fallen: 12, + Brute: 13, + SandRaider: 14, + Wraith: 15, + CorruptRogue: 16, + Baboon: 17, + GoatMan: 18, + QuillRat: 19, + SandMaggot: 20, + Viper: 21, + SandLeaper: 22, + PantherWoman: 23, + Swarm: 24, + Scarab: 25, + Mummy: 26, + Unraveler: 27, + Vulture: 28, + Mosquito: 29, + WillowWisp: 30, + Arach: 31, + ThornHulk: 32, + Vampire: 33, + BatDemon: 34, + Fetish: 35, + Blunderbore: 36, + UndeadFetish: 37, + Zakarum: 38, + FrogDemon: 39, + Tentacle: 40, + FingerMage: 41, + Golem: 42, + Vilekind: 43, + Regurgitator: 44, + DoomKnight: 45, + CouncilMember: 46, + MegaDemon: 47, + Bovine: 48, + SeigeBeast: 49, + SnowYeti: 50, + Minion: 51, + Succubus: 52, + Overseer: 53, + Imp: 54, + FrozenHorror: 55, + BloodLord: 56, + DeathMauler: 57, + PutridDefiler: 58, + }, + DiablosBoneCage: 340, + Dummy1: 149, + Dummy2: 268, + AbyssKnight: 311, + Afflicted: 10, + Afflicted2: 580, + AlbinoRoach: 95, + Ancient1: 104, + Ancient2: 669, + Ancient3: 670, + Apparition: 41, + Arach1: 122, + Arach2: 685, + Assailant: 33, + Assailant2: 603, + BaalColdMage: 381, + Balrog1: 360, + Balrog2: 686, + Banished: 135, + Barbs: 422, + Bear1: 428, + Bear2: 431, + Beast: 441, + BerserkSlayer: 462, + BlackArcher: 163, + BlackLancer1: 168, + BlackLancer2: 617, + BlackLocusts: 88, + BlackRaptor1: 17, + BlackRaptor2: 592, + BlackRogue: 46, + BlackSoul1: 121, + BlackSoul2: 640, + BlackVultureNest: 208, + BloodBoss: 482, + BloodBringer: 443, + BloodClan1: 55, + BloodClan2: 588, + BloodDiver: 139, + BloodGolem: 290, + BloodHawk1: 16, + BloodHawk2: 591, + BloodHawkNest: 207, + BloodHook: 116, + BloodHookNest: 336, + BloodLord1: 134, + BloodLord2: 695, + BloodWing: 117, + BloodWingNest: 337, + Blunderbore1: 186, + Blunderbore2: 618, + BoneArcher1: 172, + BoneArcher2: 576, + BoneMage1: 275, + BoneMage2: 380, + BoneMage3: 384, + BoneMage4: 388, + BoneMage5: 624, + BoneWarrior1: 2, + BoneWarrior2: 648, + HellBovine: 391, + BrambleHulk: 128, + Brute: 24, + Bunny: 556, + BurningDead: 3, + BurningDeadArcher1: 173, + BurningDeadArcher2: 575, + BurningDeadArcher3: 577, + BurningDeadMage1: 276, + BurningDeadMage2: 385, + BurningDeadMage3: 389, + BurningDeadMage4: 621, + BurningSoul1: 641, + BurningSoul2: 120, + Cadaver1: 100, + Cadaver2: 703, + Cantor: 239, + CarrionBird1: 110, + CarrionBird2: 608, + Carver1: 642, + Carver2: 20, + CarverShaman: 645, + CarverShaman2: 59, + CaveLeaper1: 79, + CaveLeaper2: 629, + ClawViper1: 74, + ClawViper2: 594, + CloudStalker1: 18, + CloudStalker2: 593, + CloudStalkerNest: 209, + Combatant1: 522, + Combatant2: 523, + ConsumedFireBoar: 464, + ConsumedIceBoar: 463, + CorpseSpitter: 308, + Corpulent: 307, + Creature1: 248, + Creature2: 427, + Creeper: 413, + CrushBiest: 442, + Crusher: 26, + Damned1: 14, + Damned2: 584, + DarkArcher1: 162, + DarkArcher2: 614, + DarkFamiliar: 140, + DarkHunter: 43, + DarkLancer1: 167, + DarkLancer2: 616, + DarkLord1: 133, + DarkLord2: 697, + DarkOne1: 22, + DarkOne2: 644, + DarkRanger: 160, + DarkShaman1: 61, + DarkShaman2: 647, + DarkShape: 42, + DarkSpearwoman: 165, + DarkStalker: 45, + DeamonSteed: 445, + DeathClan1: 57, + DeathClan2: 589, + Decayed: 97, + DefiledWarrior: 440, + Defiler1: 546, + Defiler2: 547, + Defiler3: 548, + Defiler4: 549, + Defiler5: 550, + DesertWing: 136, + Destruction: 410, + Devilkin: 643, + Devilkin2: 21, + DevilkinShaman: 646, + DevilkinShaman2: 60, + Devourer: 70, + DevourerEgg: 192, + DevourerQueen: 286, + DevourerYoung: 182, + Disfigured: 13, + Disfigured2: 583, + Dominus1: 474, + Dominus2: 636, + DoomApe: 51, + DoomKnight: 310, + DoomKnight1: 699, + DoomKnight2: 700, + Drehya1: 512, + Drehya2: 527, + DriedCorpse: 96, + DrownedCarcass: 8, + DuneBeast: 48, + DungSoldier: 91, + Dweller: 247, + Eagle: 429, + Embalmed: 98, + Faithful: 236, + Fallen: 19, + FallenShaman: 58, + FanaticMinion: 461, + Feeder: 115, + FeederNest: 335, + Fenris: 421, + Fetish1: 142, + BoneFetish2: 213, + Fetish3: 397, + FetishShaman: 279, + Fiend1: 137, + Fiend2: 651, + FireBoar: 456, + FireTower: 372, + FlameSpider: 125, + Flayer1: 143, + BoneFetish3: 214, + Flayer3: 398, + Flayer4: 659, + Flayer5: 656, + FlayerShaman1: 280, + FlayerShaman2: 662, + FleshArcher: 164, + FleshBeast1: 301, + FleshBeast2: 678, + FleshHunter: 47, + FleshLancer: 169, + FleshSpawner1: 298, + FleshSpawner2: 676, + FlyingScimitar: 234, + FoulCrow: 15, + FoulCrow2: 590, + FoulCrowNest: 206, + FrenziedHellSpawn: 465, + FrenziedIceSpawn: 466, + GargantuanBeast: 28, + Geglash: 200, + Ghost1: 38, + Ghost2: 631, + Ghoul: 7, + GhoulLord1: 131, + GhoulLord2: 696, + GiantLamprey: 71, + GiantLampreyEgg: 193, + GiantLampreyQueen: 287, + GiantLampreyYoung: 183, + GiantUrchin: 317, + Gloam1: 118, + Gloam2: 639, + Gloombat1: 138, + Gloombat2: 650, + Gorbelly: 187, + GoreBearer: 444, + GreaterHellSpawn1: 459, + GreaterHellSpawn2: 684, + GreaterIceSpawn: 460, + Groper: 304, + Grotesque1: 300, + Grotesque2: 675, + GrotesqueWyrm1: 303, + GrotesqueWyrm2: 677, + Guardian1: 102, + Guardian2: 667, + Hawk: 419, + Heirophant1: 240, + Heirophant2: 241, + Heirophant3: 673, + Heirophant4: 674, + HellBuzzard: 112, + HellCat: 86, + HellClan1: 56, + HellClan2: 587, + HellSlinger: 376, + HellSpawn1: 457, + HellSpawn2: 683, + HellSwarm: 90, + HellWhip: 483, + HollowOne: 101, + Horror: 4, + Horror1: 501, + Horror2: 502, + Horror3: 503, + Horror4: 504, + Horror5: 505, + HorrorArcher1: 174, + HorrorArcher2: 579, + HorrorMage1: 277, + HorrorMage2: 382, + HorrorMage3: 386, + HorrorMage4: 390, + HorrorMage5: 623, + HorrorMage6: 625, + HorrorMage7: 626, + Hs1: 560, + HungryDead: 6, + Huntress1: 83, + Huntress2: 627, + Hut: 528, + Hydra1: 351, + Hydra2: 352, + Hydra3: 353, + IceBoar: 455, + IceSpawn: 458, + Imp1: 492, + Imp2: 493, + Imp3: 494, + Imp4: 495, + Imp5: 496, + Imp6: 688, + Imp7: 689, + Infidel1: 32, + Infidel2: 600, + InsaneHellSpawn: 467, + InsaneIceSpawn: 468, + Invader1: 31, + Invader2: 602, + Itchies: 87, + JungleHunter: 50, + JungleUrchin: 67, + Larva: 283, + Lasher: 480, + LightningSpire: 371, + Lord1: 506, + Lord2: 507, + Lord3: 508, + Lord4: 509, + Lord5: 510, + Lord6: 652, + Lord7: 653, + Maggot: 227, + Malachai: 408, + Marauder: 30, + Marauder2: 599, + Master: 418, + Mauler: 188, + Mauler1: 529, + Mauler12: 604, + Mauler2: 530, + Mauler3: 531, + Mauler4: 532, + Mauler5: 533, + Mauler6: 619, + MawFiend: 694, + MawFiend2: 309, + Council1: 345, + Council2: 346, + Council3: 347, + Council4: 557, + Minion1: 572, + Minion2: 573, + Enslaved: 453, + MinionSlayerSpawner: 485, + MinionSpawner: 484, + Misshapen1: 12, + Misshapen2: 582, + MoonClan1: 53, + MoonClan2: 585, + BaalSubjectMummy: 105, + Navi: 266, + Flavie: 266, + NightClan1: 54, + NightClan2: 586, + NightLord: 132, + NightMarauder: 295, + NightSlinger1: 375, + NightSlinger2: 395, + NightTiger: 85, + OblivionKnight1: 312, + OblivionKnight2: 701, + OblivionKnight3: 702, + OverLord: 481, + OverSeer: 479, + PitLord1: 361, + PitLord2: 687, + PitViper1: 76, + PitViper2: 595, + PlagueBearer: 9, + PlagueBugs: 89, + PoisonSpinner: 124, + PreservedDead: 99, + ProwlingDead: 438, + QuillBear: 313, + QuillRat1: 63, + QuillRat2: 605, + RatMan1: 141, + RatMan2: 396, + BoneFetish1: 212, + RatMan4: 407, + RatManShaman: 278, + RazorBeast: 316, + RazorPitDemon: 82, + RazorSpine1: 66, + RazorSpine2: 607, + ReanimatedHorde: 437, + Returned1: 1, + Returned2: 649, + ReturnedArcher1: 171, + ReturnedArcher2: 578, + ReturnedMage: 274, + ReturnedMage1: 379, + ReturnedMage2: 383, + ReturnedMage3: 387, + ReturnedMage4: 620, + ReturnedMage5: 622, + RiverStalkerHead: 262, + RiverStalkerLimb: 259, + RockDweller: 49, + RockWorm: 69, + RockWormEgg: 191, + RockWormQueen: 285, + RockWormYoung: 181, + RotWalker: 436, + SaberCat1: 84, + SaberCat2: 628, + Salamander1: 75, + Salamander2: 596, + SandFisher: 123, + SandLeaper: 78, + SandMaggot: 68, + SandMaggotEgg: 190, + SandMaggotYoung: 180, + SandRaider1: 29, + SandRaider2: 601, + SandWarrior: 92, + Scarab1: 93, + Scarab2: 654, + Sentry1: 411, + Sentry2: 412, + Sentry3: 415, + Sentry4: 416, + SerpentMagus1: 77, + SerpentMagus2: 598, + Sexton: 238, + Skeleton: 0, + SkeletonArcher: 170, + Slayerexp1: 454, + Slayerexp2: 682, + Slinger1: 373, + Slinger2: 610, + Slinger3: 611, + Slinger4: 612, + SnowYeti1: 446, + SnowYeti2: 447, + SnowYeti3: 448, + SnowYeti4: 449, + SoulKiller: 691, + SoulKiller1: 399, + SoulKiller2: 144, + SoulKiller3: 215, + SoulKiller4: 658, + SoulKiller5: 661, + SoulKillerShaman1: 664, + SoulKillerShaman2: 281, + SpearCat: 394, + SpearCat1: 374, + Specter1: 40, + Specter2: 633, + SpiderMagus: 126, + SpikeFiend1: 64, + SpikeFiend2: 606, + Spikefist: 130, + SpikeGiant: 314, + SteelWeevil1: 94, + SteelWeevil2: 655, + StormCaster1: 306, + StormCaster2: 693, + Strangler1: 305, + Strangler2: 692, + StygianDog: 302, + StygianDoll1: 145, + StygianDoll2: 216, + StygianDoll3: 400, + StygianDoll4: 660, + StygianDoll5: 657, + StygianDoll6: 690, + StygianDollShaman1: 663, + StygianDollShaman2: 282, + StygianFury: 476, + StygianHag: 299, + StygianHarlot: 471, + StygianWatcherHead: 263, + StygianWatcherLimb: 260, + Succubusexp1: 469, + Succubusexp2: 634, + Sucker: 114, + SuckerNest: 334, + Summoner: 250, + SwampGhost: 119, + Tainted: 11, + Tainted2: 581, + Taunt: 545, + Temptress1: 472, + Temptress2: 473, + Temptress3: 635, + Tentacle1: 562, + Tentacle2: 563, + Tentacle3: 564, + Tentacle4: 565, + Tentacle5: 566, + ThornBeast: 65, + ThornBrute: 315, + ThornedHulk1: 127, + ThornedHulk2: 609, + Thrasher: 129, + TombCreeper1: 80, + TombCreeper2: 630, + TombViper1: 73, + TombViper2: 597, + TrappedSoul1: 403, + TrappedSoul2: 404, + TreeLurker: 81, + UndeadScavenger: 111, + UnholyCorpse1: 439, + UnholyCorpse2: 698, + Unraveler1: 103, + Unraveler2: 668, + Urdar: 189, + VenomLord1: 362, + VenomLord2: 558, + VileArcher1: 161, + VileArcher2: 613, + VileHunter: 44, + VileLancer1: 166, + VileLancer2: 615, + VileTemptress: 470, + VileWitch1: 475, + VileWitch2: 638, + WailingBeast: 27, + WarpedFallen: 23, + WarpedShaman: 62, + Warrior: 417, + WaterWatcherHead: 261, + WaterWatcherLimb: 258, + WingedNightmare: 113, + Witch1: 637, + Witch2: 477, + Witch3: 478, + Wolf1: 359, + Wolf2: 420, + Wolf3: 430, + WolfRider1: 450, + WolfRider2: 451, + WolfRider3: 452, + WorldKiller1: 679, + WorldKiller2: 72, + WorldKillerEgg1: 681, + WorldKillerEgg2: 194, + WorldKillerQueen: 288, + WorldKillerYoung1: 680, + WorldKillerYoung2: 184, + Worm1: 551, + Worm2: 552, + Worm3: 553, + Worm4: 554, + Worm5: 555, + Wraith1: 39, + Wraith2: 632, + Yeti: 25, + Zakarumite: 235, + Zealot1: 237, + Zealot2: 671, + Zealot3: 672, + Zombie: 5, - // Bosses/Ubers - Andariel: 156, - Duriel: 211, - Mephisto: 242, - Diablo: 243, - DiabloClone: 333, - ThroneBaal: 543, - Baal: 544, - BaalClone: 570, - UberMephisto: 704, - UberBaal: 705, - UberIzual: 706, - Lilith: 707, - UberDuriel: 708, - UberDiablo: 709, + // Bosses/Ubers + Andariel: 156, + Duriel: 211, + Mephisto: 242, + Diablo: 243, + DiabloClone: 333, + ThroneBaal: 543, + Baal: 544, + BaalClone: 570, + UberMephisto: 704, + UberBaal: 705, + UberIzual: 706, + Lilith: 707, + UberDuriel: 708, + UberDiablo: 709, - // Mini-Bosses - TheSmith: 402, - BloodRaven: 267, - Radament: 229, - TheSummoner: 250, - Griswold: 365, - Izual: 256, - Hephasto: 409, - KorlictheProtector: 540, - TalictheDefender: 541, - MadawctheGuardian: 542, - ListerTheTormenter: 571, - TheCowKing: 743, // 773? - ColdwormtheBurrower: 284, - Nihlathak: 526, + // Mini-Bosses + TheSmith: 402, + BloodRaven: 267, + Radament: 229, + TheSummoner: 250, + Griswold: 365, + Izual: 256, + Hephasto: 409, + KorlictheProtector: 540, + TalictheDefender: 541, + MadawctheGuardian: 542, + ListerTheTormenter: 571, + TheCowKing: 743, // 773? + ColdwormtheBurrower: 284, + Nihlathak: 526, - // Objects - Turret1: 348, - Turret2: 349, - Turret3: 350, - CatapultS: 497, - CatapultE: 498, - CatapultSiege: 499, - CatapultW: 500, - Compellingorb: 366, - GargoyleTrap: 273, - MummyGenerator: 228, - Stairs: 559, - BarricadeDoor1: 432, - BarricadeDoor2: 433, - PrisonDoor: 434, - BarricadeTower: 435, - BarricadeWall1: 524, - BarricadeWall2: 525, + // Objects + Turret1: 348, + Turret2: 349, + Turret3: 350, + CatapultS: 497, + CatapultE: 498, + CatapultSiege: 499, + CatapultW: 500, + Compellingorb: 366, + GargoyleTrap: 273, + MummyGenerator: 228, + Stairs: 559, + BarricadeDoor1: 432, + BarricadeDoor2: 433, + PrisonDoor: 434, + BarricadeTower: 435, + BarricadeWall1: 524, + BarricadeWall2: 525, - // Misc? - Youngdiablo: 368, - Left: 525, - Life: 426, - Effect: 574, - Pet: 414, - Prince: 249, - POW: 534, - Right: 524, - Sage: 424, - Town: 514, - Cow: 179, - }, + // Misc? + Youngdiablo: 368, + Left: 525, + Life: 426, + Effect: 574, + Pet: 414, + Prince: 249, + POW: 534, + Right: 524, + Sage: 424, + Town: 514, + Cow: 179, + }, - summons: { - type: { - "Valkyrie": 2, - "Golem": 3, - "Skeleton": 4, - "SkeletonMage": 5, - "Revive": 6, - "Mercenary": 7, - "Dopplezon": 8, - "Raven": 10, - "SpiritWolf": 11, - "Fenris": 12, - "DireWolf": 12, - "Totem": 13, - "Spirit": 13, - "Vine": 14, - "Grizzly": 15, - "ShadowWarrior": 16, - "Shadow": 16, - "AssassinTrap": 17, - "Hydra": 19, - }, + summons: { + type: { + "Valkyrie": 2, + "Golem": 3, + "Skeleton": 4, + "SkeletonMage": 5, + "Revive": 6, + "Mercenary": 7, + "Dopplezon": 8, + "Raven": 10, + "SpiritWolf": 11, + "Fenris": 12, + "DireWolf": 12, + "Totem": 13, + "Spirit": 13, + "Vine": 14, + "Grizzly": 15, + "ShadowWarrior": 16, + "Shadow": 16, + "AssassinTrap": 17, + "Hydra": 19, + }, - mode: { - Death: 0, - Standing: 1, - Walking: 2, - GettingHit: 3, - Attacking1: 4, - Attacking2: 5, - Blocking: 6, - CastingSkill: 7, - UsingSkill1: 8, - UsingSkill2: 9, - UsingSkill3: 10, - UsingSkill4: 11, - Dead: 12, - KnockedBack: 13, - Spawning: 14, - Running: 15 - }, + mode: { + Death: 0, + Standing: 1, + Walking: 2, + GettingHit: 3, + Attacking1: 4, + Attacking2: 5, + Blocking: 6, + CastingSkill: 7, + UsingSkill1: 8, + UsingSkill2: 9, + UsingSkill3: 10, + UsingSkill4: 11, + Dead: 12, + KnockedBack: 13, + Spawning: 14, + Running: 15 + }, - ClayGolem: 289, - Dopplezon: 356, - Valkyrie: 357, - FireGolem: 292, - IronGolem: 291, - NecroMage: 364, - NecroSkeleton: 363, - Poppy: 425, - Wolverine: 423, - }, + ClayGolem: 289, + Dopplezon: 356, + Valkyrie: 357, + FireGolem: 292, + IronGolem: 291, + NecroMage: 364, + NecroSkeleton: 363, + Poppy: 425, + Wolverine: 423, + }, - mercs: { - mode: { - Death: 0, - Standing: 1, - Walking: 2, - GettingHit: 3, - Attacking1: 4, - Attacking2: 5, - Blocking: 6, - CastingSkill: 7, - UsingSkill1: 8, - UsingSkill2: 9, - UsingSkill3: 10, - UsingSkill4: 11, - Dead: 12, - KnockedBack: 13, - Spawning: 14, - Running: 15 - }, + mercs: { + mode: { + Death: 0, + Standing: 1, + Walking: 2, + GettingHit: 3, + Attacking1: 4, + Attacking2: 5, + Blocking: 6, + CastingSkill: 7, + UsingSkill1: 8, + UsingSkill2: 9, + UsingSkill3: 10, + UsingSkill4: 11, + Dead: 12, + KnockedBack: 13, + Spawning: 14, + Running: 15 + }, - Rogue: 271, - Guard: 338, - IronWolf: 359, - A5Barb: 561, - }, + Rogue: 271, + Guard: 338, + IronWolf: 359, + A5Barb: 561, + }, - missiles: { - DiabloLightning: 172, - FissureCrack1: 462, - FissureCrack2: 463, - }, + missiles: { + DiabloLightning: 172, + FissureCrack1: 462, + FissureCrack2: 463, + }, - storage: { - Equipped: 1, - Belt: 2, - Inventory: 3, - TradeWindow: 5, - Cube: 6, - Stash: 7, - }, + storage: { + Equipped: 1, + Belt: 2, + Inventory: 3, + TradeWindow: 5, + Cube: 6, + Stash: 7, + }, - node: { - NotOnPlayer: 0, - Storage: 1, - Belt: 2, - Equipped: 3, - Cursor: 4, - }, + node: { + NotOnPlayer: 0, + Storage: 1, + Belt: 2, + Equipped: 3, + Cursor: 4, + }, - // Same apply's for merc with less things available - body: { - None: 0, - Head: 1, - Neck: 2, - Torso: 3, - Armor: 3, - RightArm: 4, - LeftArm: 5, - RingRight: 6, - RingLeft: 7, - Belt: 8, - Feet: 9, - Gloves: 10, - RightArmSecondary: 11, - LeftArmSecondary: 12 - }, + // Same apply's for merc with less things available + body: { + None: 0, + Head: 1, + Neck: 2, + Torso: 3, + Armor: 3, + RightArm: 4, + LeftArm: 5, + RingRight: 6, + RingLeft: 7, + Belt: 8, + Feet: 9, + Gloves: 10, + RightArmSecondary: 11, + LeftArmSecondary: 12 + }, - items: { - cost: { - ToBuy: 0, - ToSell: 1, - ToRepair: 2, - }, - flags: { - Equipped: 0x00000001, - InSocket: 0x00000008, - Identified: 0x00000010, - OnActiveWeaponSlot: 0x00000040, - OnSwapWeaponSlot: 0x00000080, - Broken: 0x00000100, - FullRejuv: 0x00000400, - Socketed: 0x00000800, - InTradeGamble: 0x00002000, - NotInSocket: 0x00004000, - Ear: 0x00010000, - StartingItem: 0x00020000, - RuneQuestPotion: 0x00200000, - Ethereal: 0x00400000, - IsAnItem: 0x00800000, - Personalized: 0x01000000, - Runeword: 0x04000000, - }, - mode: { - inStorage: 0, //Item inven stash cube store = Item inven stash cube store - Equipped: 1, // Item equipped self or merc - inBelt: 2, // Item in belt - onGround: 3, // Item on ground - onCursor: 4, // Item on cursor - Dropping: 5, // Item being dropped - Socketed: 6 // Item socketed in item - }, - quality: { - LowQuality: 1, - Normal: 2, - Superior: 3, - Magic: 4, - Set: 5, - Rare: 6, - Unique: 7, - Crafted: 8, - }, - class: { - Normal: 0, - Exceptional: 1, - Elite: 2, - }, - type: { - Shield: 2, - Armor: 3, - Gold: 4, - BowQuiver: 5, - CrossbowQuiver: 6, - PlayerBodyPart: 7, - Herb: 8, - Potion: 9, - Ring: 10, - Elixir: 11, - Amulet: 12, - Charm: 13, - notused0: 14, - Boots: 15, - Gloves: 16, - notused1: 17, - Book: 18, - Belt: 19, - Gem: 20, - Torch: 21, - Scroll: 22, - notused2: 23, - Scepter: 24, - Wand: 25, - Staff: 26, - Bow: 27, - Axe: 28, - Club: 29, - Sword: 30, - Hammer: 31, - Knife: 32, - Spear: 33, - Polearm: 34, - Crossbow: 35, - Mace: 36, - Helm: 37, - MissilePotion: 38, - Quest: 39, - Bodypart: 40, - Key: 41, - ThrowingKnife: 42, - ThrowingAxe: 43, - Javelin: 44, - Weapon: 45, - MeleeWeapon: 46, - MissileWeapon: 47, - ThrownWeapon: 48, - ComboWeapon: 49, - AnyArmor: 50, - AnyShield: 51, - Miscellaneous: 52, - SocketFiller: 53, - Secondhand: 54, - StavesandRods: 55, - Missile: 56, - Blunt: 57, - Jewel: 58, - ClassSpecific: 59, - AmazonItem: 60, - BarbarianItem: 61, - NecromancerItem: 62, - PaladinItem: 63, - SorceressItem: 64, - AssassinItem: 65, - DruidItem: 66, - HandtoHand: 67, - Orb: 68, - VoodooHeads: 69, - AuricShields: 70, - PrimalHelm: 71, - Pelt: 72, - Cloak: 73, - Rune: 74, - Circlet: 75, - HealingPotion: 76, - ManaPotion: 77, - RejuvPotion: 78, - StaminaPotion: 79, - AntidotePotion: 80, - ThawingPotion: 81, - SmallCharm: 82, - LargeCharm: 83, - GrandCharm: 84, - AmazonBow: 85, - AmazonSpear: 86, - AmazonJavelin: 87, - AssassinClaw: 88, - MagicBowQuiv: 89, - MagicxBowQuiv: 90, - ChippedGem: 91, - FlawedGem: 92, - StandardGem: 93, - FlawlessGem: 94, - PerfectgGem: 95, - Amethyst: 96, - Diamond: 97, - Emerald: 98, - Ruby: 99, - Sapphire: 100, - Topaz: 101, - Skull: 102, - }, + items: { + cost: { + ToBuy: 0, + ToSell: 1, + ToRepair: 2, + }, + flags: { + Equipped: 0x00000001, + InSocket: 0x00000008, + Identified: 0x00000010, + OnActiveWeaponSlot: 0x00000040, + OnSwapWeaponSlot: 0x00000080, + Broken: 0x00000100, + FullRejuv: 0x00000400, + Socketed: 0x00000800, + InTradeGamble: 0x00002000, + NotInSocket: 0x00004000, + Ear: 0x00010000, + StartingItem: 0x00020000, + RuneQuestPotion: 0x00200000, + Ethereal: 0x00400000, + IsAnItem: 0x00800000, + Personalized: 0x01000000, + Runeword: 0x04000000, + }, + mode: { + inStorage: 0, //Item inven stash cube store = Item inven stash cube store + Equipped: 1, // Item equipped self or merc + inBelt: 2, // Item in belt + onGround: 3, // Item on ground + onCursor: 4, // Item on cursor + Dropping: 5, // Item being dropped + Socketed: 6 // Item socketed in item + }, + quality: { + LowQuality: 1, + Normal: 2, + Superior: 3, + Magic: 4, + Set: 5, + Rare: 6, + Unique: 7, + Crafted: 8, + }, + class: { + Normal: 0, + Exceptional: 1, + Elite: 2, + }, + type: { + Shield: 2, + Armor: 3, + Gold: 4, + BowQuiver: 5, + CrossbowQuiver: 6, + PlayerBodyPart: 7, + Herb: 8, + Potion: 9, + Ring: 10, + Elixir: 11, + Amulet: 12, + Charm: 13, + notused0: 14, + Boots: 15, + Gloves: 16, + notused1: 17, + Book: 18, + Belt: 19, + Gem: 20, + Torch: 21, + Scroll: 22, + notused2: 23, + Scepter: 24, + Wand: 25, + Staff: 26, + Bow: 27, + Axe: 28, + Club: 29, + Sword: 30, + Hammer: 31, + Knife: 32, + Spear: 33, + Polearm: 34, + Crossbow: 35, + Mace: 36, + Helm: 37, + MissilePotion: 38, + Quest: 39, + Bodypart: 40, + Key: 41, + ThrowingKnife: 42, + ThrowingAxe: 43, + Javelin: 44, + Weapon: 45, + MeleeWeapon: 46, + MissileWeapon: 47, + ThrownWeapon: 48, + ComboWeapon: 49, + AnyArmor: 50, + AnyShield: 51, + Miscellaneous: 52, + SocketFiller: 53, + Secondhand: 54, + StavesandRods: 55, + Missile: 56, + Blunt: 57, + Jewel: 58, + ClassSpecific: 59, + AmazonItem: 60, + BarbarianItem: 61, + NecromancerItem: 62, + PaladinItem: 63, + SorceressItem: 64, + AssassinItem: 65, + DruidItem: 66, + HandtoHand: 67, + Orb: 68, + VoodooHeads: 69, + AuricShields: 70, + PrimalHelm: 71, + Pelt: 72, + Cloak: 73, + Rune: 74, + Circlet: 75, + HealingPotion: 76, + ManaPotion: 77, + RejuvPotion: 78, + StaminaPotion: 79, + AntidotePotion: 80, + ThawingPotion: 81, + SmallCharm: 82, + LargeCharm: 83, + GrandCharm: 84, + AmazonBow: 85, + AmazonSpear: 86, + AmazonJavelin: 87, + AssassinClaw: 88, + MagicBowQuiv: 89, + MagicxBowQuiv: 90, + ChippedGem: 91, + FlawedGem: 92, + StandardGem: 93, + FlawlessGem: 94, + PerfectgGem: 95, + Amethyst: 96, + Diamond: 97, + Emerald: 98, + Ruby: 99, + Sapphire: 100, + Topaz: 101, + Skull: 102, + }, - // Weapons - "HandAxe": 0, - "Axe": 1, - "DoubleAxe": 2, - "MilitaryPick": 3, - "WarAxe": 4, - "LargeAxe": 5, - "BroadAxe": 6, - "BattleAxe": 7, - "GreatAxe": 8, - "GiantAxe": 9, - "Wand": 10, - "YewWand": 11, - "BoneWand": 12, - "GrimWand": 13, - "Club": 14, - "Scepter": 15, - "GrandScepter": 16, - "WarScepter": 17, - "SpikedClub": 18, - "Mace": 19, - "MorningStar": 20, - "Flail": 21, - "WarHammer": 22, - "Maul": 23, - "GreatMaul": 24, - "ShortSword": 25, - "Scimitar": 26, - "Sabre": 27, - "Falchion": 28, - "CrystalSword": 29, - "BroadSword": 30, - "LongSword": 31, - "WarSword": 32, - "Two_HandedSword": 33, - "Claymore": 34, - "GiantSword": 35, - "BastardSword": 36, - "Flamberge": 37, - "GreatSword": 38, - "Dagger": 39, - "Dirk": 40, - "Kris": 41, - "Blade": 42, - "ThrowingKnife": 43, - "ThrowingAxe": 44, - "BalancedKnife": 45, - "BalancedAxe": 46, - "Javelin": 47, - "Pilum": 48, - "ShortSpear": 49, - "Glaive": 50, - "ThrowingSpear": 51, - "Spear": 52, - "Trident": 53, - "Brandistock": 54, - "Spetum": 55, - "Pike": 56, - "Bardiche": 57, - "Voulge": 58, - "Scythe": 59, - "Poleaxe": 60, - "Halberd": 61, - "WarScythe": 62, - "ShortStaff": 63, - "LongStaff": 64, - "GnarledStaff": 65, - "BattleStaff": 66, - "WarStaff": 67, - "ShortBow": 68, - "HuntersBow": 69, - "LongBow": 70, - "CompositeBow": 71, - "ShortBattleBow": 72, - "LongBattleBow": 73, - "ShortWarBow": 74, - "LongWarBow": 75, - "LightCrossbow": 76, - "Crossbow": 77, - "HeavyCrossbow": 78, - "RepeatingCrossbow": 79, - "Hatchet": 93, - "Cleaver": 94, - "TwinAxe": 95, - "Crowbill": 96, - "Naga": 97, - "MilitaryAxe": 98, - "BeardedAxe": 99, - "Tabar": 100, - "GothicAxe": 101, - "AncientAxe": 102, - "BurntWand": 103, - "PetrifiedWand": 104, - "TombWand": 105, - "GraveWand": 106, - "Cudgel": 107, - "RuneScepter": 108, - "HolyWaterSprinkler": 109, - "DivineScepter": 110, - "BarbedClub": 111, - "FlangedMace": 112, - "JaggedStar": 113, - "Knout": 114, - "BattleHammer": 115, - "WarClub": 116, - "MarteldeFer": 117, - "Gladius": 118, - "Cutlass": 119, - "Shamshir": 120, - "Tulwar": 121, - "DimensionalBlade": 122, - "BattleSword": 123, - "RuneSword": 124, - "AncientSword": 125, - "Espandon": 126, - "DacianFalx": 127, - "TuskSword": 128, - "GothicSword": 129, - "Zweihander": 130, - "ExecutionerSword": 131, - "Poignard": 132, - "Rondel": 133, - "Cinquedeas": 134, - "Stiletto": 135, - "BattleDart": 136, - "Francisca": 137, - "WarDart": 138, - "Hurlbat": 139, - "WarJavelin": 140, - "GreatPilum": 141, - "Simbilan": 142, - "Spiculum": 143, - "Harpoon": 144, - "WarSpear": 145, - "Fuscina": 146, - "WarFork": 147, - "Yari": 148, - "Lance": 149, - "LochaberAxe": 150, - "Bill": 151, - "BattleScythe": 152, - "Partizan": 153, - "Bec_de_Corbin": 154, - "GrimScythe": 155, - "JoStaff": 156, - "Quarterstaff": 157, - "CedarStaff": 158, - "GothicStaff": 159, - "RuneStaff": 160, - "EdgeBow": 161, - "RazorBow": 162, - "CedarBow": 163, - "DoubleBow": 164, - "ShortSiegeBow": 165, - "LargeSiegeBow": 166, - "RuneBow": 167, - "GothicBow": 168, - "Arbalest": 169, - "SiegeCrossbow": 170, - "Ballista": 171, - "Chu_Ko_Nu": 172, - "Katar": 175, - "WristBlade": 176, - "HatchetHands": 177, - "Cestus": 178, - "Claws": 179, - "BladeTalons": 180, - "ScissorsKatar": 181, - "Quhab": 182, - "WristSpike": 183, - "Fascia": 184, - "HandScythe": 185, - "GreaterClaws": 186, - "GreaterTalons": 187, - "ScissorsQuhab": 188, - "Suwayyah": 189, - "WristSword": 190, - "WarFist": 191, - "BattleCestus": 192, - "FeralClaws": 193, - "RunicTalons": 194, - "ScissorsSuwayyah": 195, - "Tomahawk": 196, - "SmallCrescent": 197, - "EttinAxe": 198, - "WarSpike": 199, - "BerserkerAxe": 200, - "FeralAxe": 201, - "Silver_edgedAxe": 202, - "Decapitator": 203, - "ChampionAxe": 204, - "GloriousAxe": 205, - "PolishedWand": 206, - "GhostWand": 207, - "LichWand": 208, - "UnearthedWand": 209, - "Truncheon": 210, - "MightyScepter": 211, - "SeraphRod": 212, - "Caduceus": 213, - "TyrantClub": 214, - "ReinforcedMace": 215, - "DevilStar": 216, - "Scourge": 217, - "LegendaryMallet": 218, - "OgreMaul": 219, - "ThunderMaul": 220, - "Falcata": 221, - "Ataghan": 222, - "ElegantBlade": 223, - "HydraEdge": 224, - "PhaseBlade": 225, - "ConquestSword": 226, - "CrypticSword": 227, - "MythicalSword": 228, - "LegendSword": 229, - "HighlandBlade": 230, - "BalrogBlade": 231, - "ChampionSword": 232, - "ColossusSword": 233, - "ColossusBlade": 234, - "BoneKnife": 235, - "MithrilPoint": 236, - "FangedKnife": 237, - "LegendSpike": 238, - "FlyingKnife": 239, - "FlyingAxe": 240, - "WingedKnife": 241, - "WingedAxe": 242, - "HyperionJavelin": 243, - "StygianPilum": 244, - "BalrogSpear": 245, - "GhostGlaive": 246, - "WingedHarpoon": 247, - "HyperionSpear": 248, - "StygianPike": 249, - "Mancatcher": 250, - "GhostSpear": 251, - "WarPike": 252, - "OgreAxe": 253, - "ColossusVoulge": 254, - "Thresher": 255, - "CrypticAxe": 256, - "GreatPoleaxe": 257, - "GiantThresher": 258, - "WalkingStick": 259, - "Stalagmite": 260, - "ElderStaff": 261, - "Shillelagh": 262, - "ArchonStaff": 263, - "SpiderBow": 264, - "BladeBow": 265, - "ShadowBow": 266, - "GreatBow": 267, - "DiamondBow": 268, - "CrusaderBow": 269, - "WardBow": 270, - "HydraBow": 271, - "PelletBow": 272, - "GorgonCrossbow": 273, - "ColossusCrossbow": 274, - "DemonCrossbow": 275, - "EagleOrb": 276, - "SacredGlobe": 277, - "SmokedSphere": 278, - "ClaspedOrb": 279, - "JaredsStone": 280, - "StagBow": 281, - "ReflexBow": 282, - "MaidenSpear": 283, - "MaidenPike": 284, - "MaidenJavelin": 285, - "GlowingOrb": 286, - "CrystallineGlobe": 287, - "CloudySphere": 288, - "SparklingBall": 289, - "SwirlingCrystal": 290, - "AshwoodBow": 291, - "CeremonialBow": 292, - "CeremonialSpear": 293, - "CeremonialPike": 294, - "CeremonialJavelin": 295, - "HeavenlyStone": 296, - "EldritchOrb": 297, - "DemonHeart": 298, - "VortexOrb": 299, - "DimensionalShard": 300, - "MatriarchalBow": 301, - "GrandMatronBow": 302, - "MatriarchalSpear": 303, - "MatriarchalPike": 304, - "MatriarchalJavelin": 305, - "Cap": 306, - "SkullCap": 307, - "Helm": 308, - "FullHelm": 309, - "GreatHelm": 310, - "Crown": 311, - "Mask": 312, - "QuiltedArmor": 313, - "LeatherArmor": 314, - "HardLeatherArmor": 315, - "StuddedLeather": 316, - "RingMail": 317, - "ScaleMail": 318, - "ChainMail": 319, - "BreastPlate": 320, - "SplintMail": 321, - "PlateMail": 322, - "FieldPlate": 323, - "GothicPlate": 324, - "FullPlateMail": 325, - "AncientArmor": 326, - "LightPlate": 327, - "Buckler": 328, - "SmallShield": 329, - "LargeShield": 330, - "KiteShield": 331, - "TowerShield": 332, - "GothicShield": 333, - "LeatherGloves": 334, - "HeavyGloves": 335, - "ChainGloves": 336, - "LightGauntlets": 337, - "Gauntlets": 338, - "Boots": 339, - "HeavyBoots": 340, - "ChainBoots": 341, - "LightPlatedBoots": 342, - "Greaves": 343, - "Sash": 344, - "LightBelt": 345, - "Belt": 346, - "HeavyBelt": 347, - "PlatedBelt": 348, - "BoneHelm": 349, - "BoneShield": 350, - "SpikedShield": 351, - "WarHat": 352, - "Sallet": 353, - "Casque": 354, - "Basinet": 355, - "WingedHelm": 356, - "GrandCrown": 357, - "DeathMask": 358, - "GhostArmor": 359, - "SerpentskinArmor": 360, - "DemonhideArmor": 361, - "TrellisedArmor": 362, - "LinkedMail": 363, - "TigulatedMail": 364, - "MeshArmor": 365, - "Cuirass": 366, - "RussetArmor": 367, - "TemplarCoat": 368, - "SharktoothArmor": 369, - "EmbossedPlate": 370, - "ChaosArmor": 371, - "OrnatePlate": 372, - "MagePlate": 373, - "Defender": 374, - "RoundShield": 375, - "Scutum": 376, - "DragonShield": 377, - "Pavise": 378, - "AncientShield": 379, - "DemonhideGloves": 380, - "SharkskinGloves": 381, - "HeavyBracers": 382, - "BattleGauntlets": 383, - "WarGauntlets": 384, - "DemonhideBoots": 385, - "SharkskinBoots": 386, - "MeshBoots": 387, - "BattleBoots": 388, - "WarBoots": 389, - "DemonhideSash": 390, - "SharkskinBelt": 391, - "MeshBelt": 392, - "BattleBelt": 393, - "WarBelt": 394, - "GrimHelm": 395, - "GrimShield": 396, - "BarbedShield": 397, - "WolfHead": 398, - "HawkHelm": 399, - "Antlers": 400, - "FalconMask": 401, - "SpiritMask": 402, - "JawboneCap": 403, - "FangedHelm": 404, - "HornedHelm": 405, - "AssaultHelmet": 406, - "AvengerGuard": 407, - "Targe": 408, - "Rondache": 409, - "HeraldicShield": 410, - "AerinShield": 411, - "CrownShield": 412, - "PreservedHead": 413, - "ZombieHead": 414, - "UnravellerHead": 415, - "GargoyleHead": 416, - "DemonHead": 417, - "Circlet": 418, - "Coronet": 419, - "Tiara": 420, - "Diadem": 421, - "Shako": 422, - "Hydraskull": 423, - "Armet": 424, - "GiantConch": 425, - "SpiredHelm": 426, - "Corona": 427, - "Demonhead": 428, - "DuskShroud": 429, - "Wyrmhide": 430, - "ScarabHusk": 431, - "WireFleece": 432, - "DiamondMail": 433, - "LoricatedMail": 434, - "Boneweave": 435, - "GreatHauberk": 436, - "BalrogSkin": 437, - "HellforgePlate": 438, - "KrakenShell": 439, - "LacqueredPlate": 440, - "ShadowPlate": 441, - "SacredArmor": 442, - "ArchonPlate": 443, - "Heater": 444, - "Luna": 445, - "Hyperion": 446, - "Monarch": 447, - "Aegis": 448, - "Ward": 449, - "BrambleMitts": 450, - "VampireboneGloves": 451, - "Vambraces": 452, - "CrusaderGauntlets": 453, - "OgreGauntlets": 454, - "WyrmhideBoots": 455, - "ScarabshellBoots": 456, - "BoneweaveBoots": 457, - "MirroredBoots": 458, - "MyrmidonGreaves": 459, - "SpiderwebSash": 460, - "VampirefangBelt": 461, - "MithrilCoil": 462, - "TrollBelt": 463, - "ColossusGirdle": 464, - "BoneVisage": 465, - "TrollNest": 466, - "BladeBarrier": 467, - "AlphaHelm": 468, - "GriffonHeaddress": 469, - "HuntersGuise": 470, - "SacredFeathers": 471, - "TotemicMask": 472, - "JawboneVisor": 473, - "LionHelm": 474, - "RageMask": 475, - "SavageHelmet": 476, - "SlayerGuard": 477, - "AkaranTarge": 478, - "AkaranRondache": 479, - "ProtectorShield": 480, - "GildedShield": 481, - "RoyalShield": 482, - "MummifiedTrophy": 483, - "FetishTrophy": 484, - "SextonTrophy": 485, - "CantorTrophy": 486, - "HierophantTrophy": 487, - "BloodSpirit": 488, - "SunSpirit": 489, - "EarthSpirit": 490, - "SkySpirit": 491, - "DreamSpirit": 492, - "CarnageHelm": 493, - "FuryVisor": 494, - "DestroyerHelm": 495, - "ConquerorCrown": 496, - "GuardianCrown": 497, - "SacredTarge": 498, - "SacredRondache": 499, - "KurastShield": 500, - "ZakarumShield": 501, - "VortexShield": 502, - "MinionSkull": 503, - "HellspawnSkull": 504, - "OverseerSkull": 505, - "SuccubusSkull": 506, - "BloodlordSkull": 507, - "Amulet": 520, - "Ring": 522, - "Arrows": 526, - "Bolts": 528, - "Jewel": 643, + // Weapons + "HandAxe": 0, + "Axe": 1, + "DoubleAxe": 2, + "MilitaryPick": 3, + "WarAxe": 4, + "LargeAxe": 5, + "BroadAxe": 6, + "BattleAxe": 7, + "GreatAxe": 8, + "GiantAxe": 9, + "Wand": 10, + "YewWand": 11, + "BoneWand": 12, + "GrimWand": 13, + "Club": 14, + "Scepter": 15, + "GrandScepter": 16, + "WarScepter": 17, + "SpikedClub": 18, + "Mace": 19, + "MorningStar": 20, + "Flail": 21, + "WarHammer": 22, + "Maul": 23, + "GreatMaul": 24, + "ShortSword": 25, + "Scimitar": 26, + "Sabre": 27, + "Falchion": 28, + "CrystalSword": 29, + "BroadSword": 30, + "LongSword": 31, + "WarSword": 32, + "Two_HandedSword": 33, + "Claymore": 34, + "GiantSword": 35, + "BastardSword": 36, + "Flamberge": 37, + "GreatSword": 38, + "Dagger": 39, + "Dirk": 40, + "Kris": 41, + "Blade": 42, + "ThrowingKnife": 43, + "ThrowingAxe": 44, + "BalancedKnife": 45, + "BalancedAxe": 46, + "Javelin": 47, + "Pilum": 48, + "ShortSpear": 49, + "Glaive": 50, + "ThrowingSpear": 51, + "Spear": 52, + "Trident": 53, + "Brandistock": 54, + "Spetum": 55, + "Pike": 56, + "Bardiche": 57, + "Voulge": 58, + "Scythe": 59, + "Poleaxe": 60, + "Halberd": 61, + "WarScythe": 62, + "ShortStaff": 63, + "LongStaff": 64, + "GnarledStaff": 65, + "BattleStaff": 66, + "WarStaff": 67, + "ShortBow": 68, + "HuntersBow": 69, + "LongBow": 70, + "CompositeBow": 71, + "ShortBattleBow": 72, + "LongBattleBow": 73, + "ShortWarBow": 74, + "LongWarBow": 75, + "LightCrossbow": 76, + "Crossbow": 77, + "HeavyCrossbow": 78, + "RepeatingCrossbow": 79, + "Hatchet": 93, + "Cleaver": 94, + "TwinAxe": 95, + "Crowbill": 96, + "Naga": 97, + "MilitaryAxe": 98, + "BeardedAxe": 99, + "Tabar": 100, + "GothicAxe": 101, + "AncientAxe": 102, + "BurntWand": 103, + "PetrifiedWand": 104, + "TombWand": 105, + "GraveWand": 106, + "Cudgel": 107, + "RuneScepter": 108, + "HolyWaterSprinkler": 109, + "DivineScepter": 110, + "BarbedClub": 111, + "FlangedMace": 112, + "JaggedStar": 113, + "Knout": 114, + "BattleHammer": 115, + "WarClub": 116, + "MarteldeFer": 117, + "Gladius": 118, + "Cutlass": 119, + "Shamshir": 120, + "Tulwar": 121, + "DimensionalBlade": 122, + "BattleSword": 123, + "RuneSword": 124, + "AncientSword": 125, + "Espandon": 126, + "DacianFalx": 127, + "TuskSword": 128, + "GothicSword": 129, + "Zweihander": 130, + "ExecutionerSword": 131, + "Poignard": 132, + "Rondel": 133, + "Cinquedeas": 134, + "Stiletto": 135, + "BattleDart": 136, + "Francisca": 137, + "WarDart": 138, + "Hurlbat": 139, + "WarJavelin": 140, + "GreatPilum": 141, + "Simbilan": 142, + "Spiculum": 143, + "Harpoon": 144, + "WarSpear": 145, + "Fuscina": 146, + "WarFork": 147, + "Yari": 148, + "Lance": 149, + "LochaberAxe": 150, + "Bill": 151, + "BattleScythe": 152, + "Partizan": 153, + "Bec_de_Corbin": 154, + "GrimScythe": 155, + "JoStaff": 156, + "Quarterstaff": 157, + "CedarStaff": 158, + "GothicStaff": 159, + "RuneStaff": 160, + "EdgeBow": 161, + "RazorBow": 162, + "CedarBow": 163, + "DoubleBow": 164, + "ShortSiegeBow": 165, + "LargeSiegeBow": 166, + "RuneBow": 167, + "GothicBow": 168, + "Arbalest": 169, + "SiegeCrossbow": 170, + "Ballista": 171, + "Chu_Ko_Nu": 172, + "Katar": 175, + "WristBlade": 176, + "HatchetHands": 177, + "Cestus": 178, + "Claws": 179, + "BladeTalons": 180, + "ScissorsKatar": 181, + "Quhab": 182, + "WristSpike": 183, + "Fascia": 184, + "HandScythe": 185, + "GreaterClaws": 186, + "GreaterTalons": 187, + "ScissorsQuhab": 188, + "Suwayyah": 189, + "WristSword": 190, + "WarFist": 191, + "BattleCestus": 192, + "FeralClaws": 193, + "RunicTalons": 194, + "ScissorsSuwayyah": 195, + "Tomahawk": 196, + "SmallCrescent": 197, + "EttinAxe": 198, + "WarSpike": 199, + "BerserkerAxe": 200, + "FeralAxe": 201, + "Silver_edgedAxe": 202, + "Decapitator": 203, + "ChampionAxe": 204, + "GloriousAxe": 205, + "PolishedWand": 206, + "GhostWand": 207, + "LichWand": 208, + "UnearthedWand": 209, + "Truncheon": 210, + "MightyScepter": 211, + "SeraphRod": 212, + "Caduceus": 213, + "TyrantClub": 214, + "ReinforcedMace": 215, + "DevilStar": 216, + "Scourge": 217, + "LegendaryMallet": 218, + "OgreMaul": 219, + "ThunderMaul": 220, + "Falcata": 221, + "Ataghan": 222, + "ElegantBlade": 223, + "HydraEdge": 224, + "PhaseBlade": 225, + "ConquestSword": 226, + "CrypticSword": 227, + "MythicalSword": 228, + "LegendSword": 229, + "HighlandBlade": 230, + "BalrogBlade": 231, + "ChampionSword": 232, + "ColossusSword": 233, + "ColossusBlade": 234, + "BoneKnife": 235, + "MithrilPoint": 236, + "FangedKnife": 237, + "LegendSpike": 238, + "FlyingKnife": 239, + "FlyingAxe": 240, + "WingedKnife": 241, + "WingedAxe": 242, + "HyperionJavelin": 243, + "StygianPilum": 244, + "BalrogSpear": 245, + "GhostGlaive": 246, + "WingedHarpoon": 247, + "HyperionSpear": 248, + "StygianPike": 249, + "Mancatcher": 250, + "GhostSpear": 251, + "WarPike": 252, + "OgreAxe": 253, + "ColossusVoulge": 254, + "Thresher": 255, + "CrypticAxe": 256, + "GreatPoleaxe": 257, + "GiantThresher": 258, + "WalkingStick": 259, + "Stalagmite": 260, + "ElderStaff": 261, + "Shillelagh": 262, + "ArchonStaff": 263, + "SpiderBow": 264, + "BladeBow": 265, + "ShadowBow": 266, + "GreatBow": 267, + "DiamondBow": 268, + "CrusaderBow": 269, + "WardBow": 270, + "HydraBow": 271, + "PelletBow": 272, + "GorgonCrossbow": 273, + "ColossusCrossbow": 274, + "DemonCrossbow": 275, + "EagleOrb": 276, + "SacredGlobe": 277, + "SmokedSphere": 278, + "ClaspedOrb": 279, + "JaredsStone": 280, + "StagBow": 281, + "ReflexBow": 282, + "MaidenSpear": 283, + "MaidenPike": 284, + "MaidenJavelin": 285, + "GlowingOrb": 286, + "CrystallineGlobe": 287, + "CloudySphere": 288, + "SparklingBall": 289, + "SwirlingCrystal": 290, + "AshwoodBow": 291, + "CeremonialBow": 292, + "CeremonialSpear": 293, + "CeremonialPike": 294, + "CeremonialJavelin": 295, + "HeavenlyStone": 296, + "EldritchOrb": 297, + "DemonHeart": 298, + "VortexOrb": 299, + "DimensionalShard": 300, + "MatriarchalBow": 301, + "GrandMatronBow": 302, + "MatriarchalSpear": 303, + "MatriarchalPike": 304, + "MatriarchalJavelin": 305, + "Cap": 306, + "SkullCap": 307, + "Helm": 308, + "FullHelm": 309, + "GreatHelm": 310, + "Crown": 311, + "Mask": 312, + "QuiltedArmor": 313, + "LeatherArmor": 314, + "HardLeatherArmor": 315, + "StuddedLeather": 316, + "RingMail": 317, + "ScaleMail": 318, + "ChainMail": 319, + "BreastPlate": 320, + "SplintMail": 321, + "PlateMail": 322, + "FieldPlate": 323, + "GothicPlate": 324, + "FullPlateMail": 325, + "AncientArmor": 326, + "LightPlate": 327, + "Buckler": 328, + "SmallShield": 329, + "LargeShield": 330, + "KiteShield": 331, + "TowerShield": 332, + "GothicShield": 333, + "LeatherGloves": 334, + "HeavyGloves": 335, + "ChainGloves": 336, + "LightGauntlets": 337, + "Gauntlets": 338, + "Boots": 339, + "HeavyBoots": 340, + "ChainBoots": 341, + "LightPlatedBoots": 342, + "Greaves": 343, + "Sash": 344, + "LightBelt": 345, + "Belt": 346, + "HeavyBelt": 347, + "PlatedBelt": 348, + "BoneHelm": 349, + "BoneShield": 350, + "SpikedShield": 351, + "WarHat": 352, + "Sallet": 353, + "Casque": 354, + "Basinet": 355, + "WingedHelm": 356, + "GrandCrown": 357, + "DeathMask": 358, + "GhostArmor": 359, + "SerpentskinArmor": 360, + "DemonhideArmor": 361, + "TrellisedArmor": 362, + "LinkedMail": 363, + "TigulatedMail": 364, + "MeshArmor": 365, + "Cuirass": 366, + "RussetArmor": 367, + "TemplarCoat": 368, + "SharktoothArmor": 369, + "EmbossedPlate": 370, + "ChaosArmor": 371, + "OrnatePlate": 372, + "MagePlate": 373, + "Defender": 374, + "RoundShield": 375, + "Scutum": 376, + "DragonShield": 377, + "Pavise": 378, + "AncientShield": 379, + "DemonhideGloves": 380, + "SharkskinGloves": 381, + "HeavyBracers": 382, + "BattleGauntlets": 383, + "WarGauntlets": 384, + "DemonhideBoots": 385, + "SharkskinBoots": 386, + "MeshBoots": 387, + "BattleBoots": 388, + "WarBoots": 389, + "DemonhideSash": 390, + "SharkskinBelt": 391, + "MeshBelt": 392, + "BattleBelt": 393, + "WarBelt": 394, + "GrimHelm": 395, + "GrimShield": 396, + "BarbedShield": 397, + "WolfHead": 398, + "HawkHelm": 399, + "Antlers": 400, + "FalconMask": 401, + "SpiritMask": 402, + "JawboneCap": 403, + "FangedHelm": 404, + "HornedHelm": 405, + "AssaultHelmet": 406, + "AvengerGuard": 407, + "Targe": 408, + "Rondache": 409, + "HeraldicShield": 410, + "AerinShield": 411, + "CrownShield": 412, + "PreservedHead": 413, + "ZombieHead": 414, + "UnravellerHead": 415, + "GargoyleHead": 416, + "DemonHead": 417, + "Circlet": 418, + "Coronet": 419, + "Tiara": 420, + "Diadem": 421, + "Shako": 422, + "Hydraskull": 423, + "Armet": 424, + "GiantConch": 425, + "SpiredHelm": 426, + "Corona": 427, + "Demonhead": 428, + "DuskShroud": 429, + "Wyrmhide": 430, + "ScarabHusk": 431, + "WireFleece": 432, + "DiamondMail": 433, + "LoricatedMail": 434, + "Boneweave": 435, + "GreatHauberk": 436, + "BalrogSkin": 437, + "HellforgePlate": 438, + "KrakenShell": 439, + "LacqueredPlate": 440, + "ShadowPlate": 441, + "SacredArmor": 442, + "ArchonPlate": 443, + "Heater": 444, + "Luna": 445, + "Hyperion": 446, + "Monarch": 447, + "Aegis": 448, + "Ward": 449, + "BrambleMitts": 450, + "VampireboneGloves": 451, + "Vambraces": 452, + "CrusaderGauntlets": 453, + "OgreGauntlets": 454, + "WyrmhideBoots": 455, + "ScarabshellBoots": 456, + "BoneweaveBoots": 457, + "MirroredBoots": 458, + "MyrmidonGreaves": 459, + "SpiderwebSash": 460, + "VampirefangBelt": 461, + "MithrilCoil": 462, + "TrollBelt": 463, + "ColossusGirdle": 464, + "BoneVisage": 465, + "TrollNest": 466, + "BladeBarrier": 467, + "AlphaHelm": 468, + "GriffonHeaddress": 469, + "HuntersGuise": 470, + "SacredFeathers": 471, + "TotemicMask": 472, + "JawboneVisor": 473, + "LionHelm": 474, + "RageMask": 475, + "SavageHelmet": 476, + "SlayerGuard": 477, + "AkaranTarge": 478, + "AkaranRondache": 479, + "ProtectorShield": 480, + "GildedShield": 481, + "RoyalShield": 482, + "MummifiedTrophy": 483, + "FetishTrophy": 484, + "SextonTrophy": 485, + "CantorTrophy": 486, + "HierophantTrophy": 487, + "BloodSpirit": 488, + "SunSpirit": 489, + "EarthSpirit": 490, + "SkySpirit": 491, + "DreamSpirit": 492, + "CarnageHelm": 493, + "FuryVisor": 494, + "DestroyerHelm": 495, + "ConquerorCrown": 496, + "GuardianCrown": 497, + "SacredTarge": 498, + "SacredRondache": 499, + "KurastShield": 500, + "ZakarumShield": 501, + "VortexShield": 502, + "MinionSkull": 503, + "HellspawnSkull": 504, + "OverseerSkull": 505, + "SuccubusSkull": 506, + "BloodlordSkull": 507, + "Amulet": 520, + "Ring": 522, + "Arrows": 526, + "Bolts": 528, + "Jewel": 643, - // Misc? - "Elixir": 508, - "Torch": 527, - "Heart": 531, - "Brain": 532, - "Jawbone": 533, - "Eye": 534, - "Horn": 535, - "Tail": 536, - "Flag": 537, - "Fang": 538, - "Quill": 539, - "Soul": 540, - "Scalp": 541, - "Spleen": 542, - "Key": 543, - "Ear": 556, - "Herb": 602, - "anevilforce": 609, - // Potions, tomes/scrolls, gold - "TomeofTownPortal": 518, - "TomeofIdentify": 519, - "ScrollofTownPortal": 529, - "ScrollofIdentify": 530, - "RancidGasPotion": 80, - "OilPotion": 81, - "ChokingGasPotion": 82, - "ExplodingPotion": 83, - "StranglingGasPotion": 84, - "FulminatingPotion": 85, - "StaminaPotion": 513, - "AntidotePotion": 514, - "RejuvenationPotion": 515, - "FullRejuvenationPotion": 516, - "ThawingPotion": 517, - "MinorHealingPotion": 587, - "LightHealingPotion": 588, - "HealingPotion": 589, - "GreaterHealingPotion": 590, - "SuperHealingPotion": 591, - "MinorManaPotion": 592, - "LightManaPotion": 593, - "ManaPotion": 594, - "GreaterManaPotion": 595, - "SuperManaPotion": 596, - "Gold": 523, - // Charms - "SmallCharm": 603, - "LargeCharm": 604, - "GrandCharm": 605, + // Misc? + "Elixir": 508, + "Torch": 527, + "Heart": 531, + "Brain": 532, + "Jawbone": 533, + "Eye": 534, + "Horn": 535, + "Tail": 536, + "Flag": 537, + "Fang": 538, + "Quill": 539, + "Soul": 540, + "Scalp": 541, + "Spleen": 542, + "Key": 543, + "Ear": 556, + "Herb": 602, + "anevilforce": 609, + // Potions, tomes/scrolls, gold + "TomeofTownPortal": 518, + "TomeofIdentify": 519, + "ScrollofTownPortal": 529, + "ScrollofIdentify": 530, + "RancidGasPotion": 80, + "OilPotion": 81, + "ChokingGasPotion": 82, + "ExplodingPotion": 83, + "StranglingGasPotion": 84, + "FulminatingPotion": 85, + "StaminaPotion": 513, + "AntidotePotion": 514, + "RejuvenationPotion": 515, + "FullRejuvenationPotion": 516, + "ThawingPotion": 517, + "MinorHealingPotion": 587, + "LightHealingPotion": 588, + "HealingPotion": 589, + "GreaterHealingPotion": 590, + "SuperHealingPotion": 591, + "MinorManaPotion": 592, + "LightManaPotion": 593, + "ManaPotion": 594, + "GreaterManaPotion": 595, + "SuperManaPotion": 596, + "Gold": 523, + // Charms + "SmallCharm": 603, + "LargeCharm": 604, + "GrandCharm": 605, - quest: { - // Act 1 - WirtsLeg: 88, - HoradricMalus: 89, - ScrollofInifuss: 524, - KeytotheCairnStones: 525, - // Act 2 - FinishedStaff: 91, - "HoradricStaff": 91, - IncompleteStaff: 92, - "ShaftoftheHoradricStaff": 92, - ViperAmulet: 521, - "TopoftheHoradricStaff": 521, - Cube: 549, - BookofSkill: 552, - // Act 3 - "DecoyGidbinn": 86, - "TheGidbinn": 87, - KhalimsFlail: 173, - KhalimsWill: 174, - PotofLife: 545, - "AJadeFigurine": 546, - JadeFigurine: 546, - TheGoldenBird: 547, - LamEsensTome: 548, - KhalimsEye: 553, - KhalimsHeart: 554, - KhalimsBrain: 555, - // Act 4 - HellForgeHammer: 90, - Soulstone: 551, - "MephistosSoulstone": 551, - // Act 5 - MalahsPotion: 644, - "ScrollofKnowledge": 645, - ScrollofResistance: 646, - // Pandemonium Event - KeyofTerror: 647, - KeyofHate: 648, - KeyofDestruction: 649, - DiablosHorn: 650, - BaalsEye: 651, - MephistosBrain: 652, - StandardofHeroes: 658, - // Essences/Token - TokenofAbsolution: 653, - TwistedEssenceofSuffering: 654, - ChargedEssenceofHatred: 655, - BurningEssenceofTerror: 656, - FesteringEssenceofDestruction: 657, - // Misc - "TheBlackTowerKey": 544, - }, - runes: { - El: 610, - Eld: 611, - Tir: 612, - Nef: 613, - Eth: 614, - Ith: 615, - Tal: 616, - Ral: 617, - Ort: 618, - Thul: 619, - Amn: 620, - Sol: 621, - Shael: 622, - Dol: 623, - Hel: 624, - Io: 625, - Lum: 626, - Ko: 627, - Fal: 628, - Lem: 629, - Pul: 630, - Um: 631, - Mal: 632, - Ist: 633, - Gul: 634, - Vex: 635, - Ohm: 636, - Lo: 637, - Sur: 638, - Ber: 639, - Jah: 640, - Cham: 641, - Zod: 642, - }, - gems: { - Perfect: { - Amethyst: 561, - Topaz: 566, - Sapphire: 571, - Emerald: 576, - Ruby: 581, - Diamond: 586, - Skull: 601, - }, - Flawless: { - Amethyst: 560, - Topaz: 565, - Sapphire: 570, - Emerald: 575, - Ruby: 580, - Diamond: 585, - Skull: 600, - }, - Normal: { - Amethyst: 559, - Topaz: 564, - Sapphire: 569, - Emerald: 574, - Ruby: 579, - Diamond: 584, - Skull: 599, - }, - Flawed: { - Amethyst: 558, - Topaz: 563, - Sapphire: 568, - Emerald: 573, - Ruby: 578, - Diamond: 583, - Skull: 598, - }, - Chipped: { - Amethyst: 557, - Topaz: 562, - Sapphire: 567, - Emerald: 572, - Ruby: 577, - Diamond: 582, - Skull: 597, - }, - }, - }, + quest: { + // Act 1 + WirtsLeg: 88, + HoradricMalus: 89, + ScrollofInifuss: 524, + KeytotheCairnStones: 525, + // Act 2 + FinishedStaff: 91, + "HoradricStaff": 91, + IncompleteStaff: 92, + "ShaftoftheHoradricStaff": 92, + ViperAmulet: 521, + "TopoftheHoradricStaff": 521, + Cube: 549, + BookofSkill: 552, + // Act 3 + "DecoyGidbinn": 86, + "TheGidbinn": 87, + KhalimsFlail: 173, + KhalimsWill: 174, + PotofLife: 545, + "AJadeFigurine": 546, + JadeFigurine: 546, + TheGoldenBird: 547, + LamEsensTome: 548, + KhalimsEye: 553, + KhalimsHeart: 554, + KhalimsBrain: 555, + // Act 4 + HellForgeHammer: 90, + Soulstone: 551, + "MephistosSoulstone": 551, + // Act 5 + MalahsPotion: 644, + "ScrollofKnowledge": 645, + ScrollofResistance: 646, + // Pandemonium Event + KeyofTerror: 647, + KeyofHate: 648, + KeyofDestruction: 649, + DiablosHorn: 650, + BaalsEye: 651, + MephistosBrain: 652, + StandardofHeroes: 658, + // Essences/Token + TokenofAbsolution: 653, + TwistedEssenceofSuffering: 654, + ChargedEssenceofHatred: 655, + BurningEssenceofTerror: 656, + FesteringEssenceofDestruction: 657, + // Misc + "TheBlackTowerKey": 544, + }, + runes: { + El: 610, + Eld: 611, + Tir: 612, + Nef: 613, + Eth: 614, + Ith: 615, + Tal: 616, + Ral: 617, + Ort: 618, + Thul: 619, + Amn: 620, + Sol: 621, + Shael: 622, + Dol: 623, + Hel: 624, + Io: 625, + Lum: 626, + Ko: 627, + Fal: 628, + Lem: 629, + Pul: 630, + Um: 631, + Mal: 632, + Ist: 633, + Gul: 634, + Vex: 635, + Ohm: 636, + Lo: 637, + Sur: 638, + Ber: 639, + Jah: 640, + Cham: 641, + Zod: 642, + }, + gems: { + Perfect: { + Amethyst: 561, + Topaz: 566, + Sapphire: 571, + Emerald: 576, + Ruby: 581, + Diamond: 586, + Skull: 601, + }, + Flawless: { + Amethyst: 560, + Topaz: 565, + Sapphire: 570, + Emerald: 575, + Ruby: 580, + Diamond: 585, + Skull: 600, + }, + Normal: { + Amethyst: 559, + Topaz: 564, + Sapphire: 569, + Emerald: 574, + Ruby: 579, + Diamond: 584, + Skull: 599, + }, + Flawed: { + Amethyst: 558, + Topaz: 563, + Sapphire: 568, + Emerald: 573, + Ruby: 578, + Diamond: 583, + Skull: 598, + }, + Chipped: { + Amethyst: 557, + Topaz: 562, + Sapphire: 567, + Emerald: 572, + Ruby: 577, + Diamond: 582, + Skull: 597, + }, + }, + }, - // locale strings - locale: { - monsters: { - // bosses - Andariel: 3021, - Duriel: 3054, - Mephisto: 3062, - Diablo: 3060, - Baal: 3061, - // Mini bosses - BloodRaven: 3111, - TreeheadWoodFist: 2873, - TheCountess: 2875, - TheSmith: 2889, - Radament: 2879, - TheSummoner: 3099, - HephastoTheArmorer: 1067, - Izual: 1014, - ShenktheOverseer: 22435, - // Uniques - Corpsefire: 3319, - TheCowKing: 2850, - GrandVizierofChaos: 2851, - LordDeSeis: 2852, - InfectorofSouls: 2853, - RiftwraiththeCannibal: 2854, - Taintbreeder: 2855, - TheTormentor: 2856, - Darkwing: 2857, - MafferDragonhand: 2858, - WyandVoidbringer: 2859, - ToorcIcefist: 2860, - BremmSparkfist: 2861, - GelebFlamefinger: 2862, - IsmailVilehand: 2863, - IcehawkRiftwing: 2864, - BattlemaidSarina: 2865, - Stormtree: 2866, - WitchDoctorEndugu: 2867, - SszarkTheBurning: 2868, - Bishibosh: 2869, - Bonebreaker: 2870, - Coldcrow: 2871, - Rakanishu: 2872, - Griswold: 2874, - PitspawnFouldog: 2876, - FlamespiketheCrawler: 2877, - BoneAsh: 2878, - BloodwitchtheWild: 2880, - Fangskin: 2881, - Beetleburst: 2882, - CreepingFeature: 2883, - ColdwormtheBurrower: 2884, - FireEye: 2885, - DarkElder: 2886, - AncientKaatheSoulless: 2888, - SharpToothSayer: 22493, - SnapchipShatter: 22496, - Pindleskin: 22497, - ThreshSocket: 22498, - EyebacktheUnleashed: 22499, - EldritchtheRectifier: 22500, - DacFarren: 22501, - BonesawBreaker: 22502, - Frozenstein: 22504, - Rogue: 2897, - StygianDoll: 2898, - SoulKiller: 2899, - Flayer: 2900, - Fetish: 2901, - RatMan: 2902, - UndeadStygianDoll: 2903, - UndeadSoulKiller: 2904, - UndeadFlayer: 2905, - UndeadFetish: 2906, - UndeadRatMan: 2907, - DarkFamiliar: 2908, - BloodDiver: 2909, - Gloombat: 2910, - DesertWing: 2911, - TheBanished: 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, - HoradrimAncient: 2937, - BoneScarab: 2938, - SteelScarab: 2939, - Scarab: 2940, - DeathBeetle: 2941, - DungSoldier: 2942, - HellSwarm: 2943, - PlagueBugs: 2944, - BlackLocusts: 2945, - Itchies: 2946, - HellCat: 2947, - NightTiger: 2948, - SaberCat: 2949, - Huntress: 2950, - CliffLurker: 2951, - TreeLurker: 2952, - CaveLeaper: 2953, - TombCreeper: 2954, - SandLeaper: 2955, - TombViper: 2956, - PitViper: 2957, - Salamander: 2958, - ClawViper: 2959, - SerpentMagus: 2960, - BloodMaggot: 2961, - GiantLamprey: 2962, - Devourer: 2963, - RockWorm: 2964, - SandMaggot: 2965, - BushBarb: 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, - Wraith: 2989, - 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, - Horror: 3012, - Returned: 3013, - BurningDead: 3014, - BoneWarrior: 3015, - Damned: 3016, - Disfigured: 3017, - Misshapen: 3018, - Tainted: 3019, - Afflicted: 3020, - Camel: 3033, - Cadaver: 3034, - PreservedDead: 3035, - Embalmed: 3036, - DriedCorpse: 3037, - Decayed: 3038, - Urdar: 3039, - Mauler: 3040, - Gorebelly: 3041, - Blunderbore: 3042, - BloodMaggotYoung: 3043, - GiantLampreyYoung: 3044, - DevourerYoung: 3045, - RockWormYoung: 3046, - SandMaggotYoung: 3047, - BloodMaggotEgg: 3048, - GiantLampreyEgg: 3049, - DevourerEgg: 3050, - RockWormEgg: 3051, - SandMaggotEgg: 3052, - Maggot: 3053, - BloodHawkNest: 3055, - FlyingScimitar: 3056, - CloudStalkerNest: 3057, - BlackRaptorNest: 3058, - FoulCrowNest: 3059, - 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, - WarpedOne: 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, - StygianDollShaman: 3100, - SoulKillerShaman: 3101, - FlayerShaman: 3102, - FetishShaman: 3103, - RatManShaman: 3104, - HorrorMage: 3105, - BurningDeadMage: 3106, - BoneMage: 3107, - CorpseMage: 3108, - ReturnedMage: 3109, - GargoyleTrap: 3110, - NightMarauder: 3121, - FireGolem: 3122, - IronGolem: 3123, - BloodGolem: 3124, - ClayGolem: 3125, - BloodMaggotQueen: 3126, - GiantLampreyQueen: 3127, - DevourerQueen: 3128, - RockWormQueen: 3129, - SandMaggotQueen: 3130, - SlimePrince: 3131, - BogCreature: 3132, - SwampDweller: 3133, - BarbedGiant: 3134, - RazorBeast: 3135, - ThornBrute: 3136, - SpikeGiant: 3137, - QuillBear: 3138, - CouncilMember: 3139, - DarkWanderer: 3141, - HellSlinger: 3142, - NightSlinger: 3143, - SpearCat: 3144, - Slinger: 3145, - FireTower: 3146, - LightningSpire: 3147, - PitLord: 3148, - Balrog: 3149, - VenomLord: 3150, - IronWolf: 3151, - InvisoSpawner: 3152, - OblivionKnight: 3153, - Mage: 3154, - AbyssKnight: 3155, - FighterMage: 3156, - DoomKnight: 3157, - Fighter: 3158, - MawFiend: 3159, - CorpseSpitter: 3160, - Corpulent: 3161, - StormCaster: 3162, - Strangler: 3163, - DoomCaster: 3164, - GrotesqueWyrm: 3165, - StygianDog: 3166, - FleshBeast: 3167, - Grotesque: 3168, - StygianHag: 3169, - FleshSpawner: 3170, - RogueScout: 3171, - BloodWingNest: 3172, - BloodHookNest: 3173, - FeederNest: 3174, - SuckerNest: 3175, - Hydra: 3325, - }, - npcs: { - Asheara: 1008, - Hratli: 1009, - Alkor: 1010, - Ormus: 1011, - Natalya: 1012, - Tyrael: 1013, - Izual1: 1014, - Izual2: 1015, - Jamella: 1016, - Halbu: 1017, - Hadriel: 1018, - Hazade: 1019, - Alhizeer: 1020, - Azrael: 1021, - Ahsab: 1022, - Chalan: 1023, - Haseen: 1024, - Razan: 1025, - Emilio: 1026, - Pratham: 1027, - Fazel: 1028, - Jemali: 1029, - Kasim: 1030, - Gulzar: 1031, - Mizan: 1032, - Leharas: 1033, - Durga: 1034, - Neeraj: 1035, - Ilzan: 1036, - Zanarhi: 1037, - Waheed: 1038, - Vikhyat: 1039, - Jelani: 1040, - Barani: 1041, - Jabari: 1042, - Devak: 1043, - Raldin: 1044, - Telash: 1045, - Ajheed: 1046, - Narphet: 1047, - Khaleel: 1048, - Phaet: 1049, - Geshef: 1050, - Vanji: 1051, - Haphet: 1052, - Thadar: 1053, - Yatiraj: 1054, - Rhadge: 1055, - Yashied: 1056, - Lharhad: 1057, - Flux: 1058, - Scorch: 1059, - //Natalya: 3022, both 1012 and 3022 return Natalya? - DeckardCain: 2890, - Gheed: 2891, - Akara: 2892, - Kashya: 2893, - Charsi: 2894, - Warriv: 2895, - Drognan: 3023, - Atma: 3024, - Fara: 3025, - Lysander: 3026, - Jerhyn: 3028, - Geglash: 3029, - Elzix: 3030, - Greiz: 3031, - Flavie: 3112, - Kaelan: 3113, - Meshif: 3114, - Larzuk: 22476, - Anya: 22477, - Malah: 22478, - Nihlathak1: 22479, - QualKehk: 22480, - Guard: 22481, - Combatant: 22482, - Nihlathak2: 22483, - }, - items: { - KhalimsFlail: 1060, - KhalimsWill1: 1061, - KhalimsFlail2: 1062, - KhalimsWill2: 1063, - KhalimsEye: 1064, - KhalimsBrain: 1065, - KhalimsHeart: 1066, - ScrollofInifuss: 2216, - KeytotheCairnStones: 2217, - AJadeFigurine: 2227, - TheGoldenBird: 2228, - LamEsensTome1: 2229, - LamEsensTome2: 2230, - HoradricCube: 2231, - HoradricScroll: 2232, - MephistosSoulstone: 2233, - Ear: 2235, - AmuletoftheViper: 2697, - StaffofKings: 2698, - HoradricStaff: 2699, + // locale strings + locale: { + monsters: { + // bosses + Andariel: 3021, + Duriel: 3054, + Mephisto: 3062, + Diablo: 3060, + Baal: 3061, + // Mini bosses + BloodRaven: 3111, + TreeheadWoodFist: 2873, + TheCountess: 2875, + TheSmith: 2889, + Radament: 2879, + TheSummoner: 3099, + HephastoTheArmorer: 1067, + Izual: 1014, + ShenktheOverseer: 22435, + // Uniques + Corpsefire: 3319, + TheCowKing: 2850, + GrandVizierofChaos: 2851, + LordDeSeis: 2852, + InfectorofSouls: 2853, + RiftwraiththeCannibal: 2854, + Taintbreeder: 2855, + TheTormentor: 2856, + Darkwing: 2857, + MafferDragonhand: 2858, + WyandVoidbringer: 2859, + ToorcIcefist: 2860, + BremmSparkfist: 2861, + GelebFlamefinger: 2862, + IsmailVilehand: 2863, + IcehawkRiftwing: 2864, + BattlemaidSarina: 2865, + Stormtree: 2866, + WitchDoctorEndugu: 2867, + SszarkTheBurning: 2868, + Bishibosh: 2869, + Bonebreaker: 2870, + Coldcrow: 2871, + Rakanishu: 2872, + Griswold: 2874, + PitspawnFouldog: 2876, + FlamespiketheCrawler: 2877, + BoneAsh: 2878, + BloodwitchtheWild: 2880, + Fangskin: 2881, + Beetleburst: 2882, + CreepingFeature: 2883, + ColdwormtheBurrower: 2884, + FireEye: 2885, + DarkElder: 2886, + AncientKaatheSoulless: 2888, + SharpToothSayer: 22493, + SnapchipShatter: 22496, + Pindleskin: 22497, + ThreshSocket: 22498, + EyebacktheUnleashed: 22499, + EldritchtheRectifier: 22500, + DacFarren: 22501, + BonesawBreaker: 22502, + Frozenstein: 22504, + Rogue: 2897, + StygianDoll: 2898, + SoulKiller: 2899, + Flayer: 2900, + Fetish: 2901, + RatMan: 2902, + UndeadStygianDoll: 2903, + UndeadSoulKiller: 2904, + UndeadFlayer: 2905, + UndeadFetish: 2906, + UndeadRatMan: 2907, + DarkFamiliar: 2908, + BloodDiver: 2909, + Gloombat: 2910, + DesertWing: 2911, + TheBanished: 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, + HoradrimAncient: 2937, + BoneScarab: 2938, + SteelScarab: 2939, + Scarab: 2940, + DeathBeetle: 2941, + DungSoldier: 2942, + HellSwarm: 2943, + PlagueBugs: 2944, + BlackLocusts: 2945, + Itchies: 2946, + HellCat: 2947, + NightTiger: 2948, + SaberCat: 2949, + Huntress: 2950, + CliffLurker: 2951, + TreeLurker: 2952, + CaveLeaper: 2953, + TombCreeper: 2954, + SandLeaper: 2955, + TombViper: 2956, + PitViper: 2957, + Salamander: 2958, + ClawViper: 2959, + SerpentMagus: 2960, + BloodMaggot: 2961, + GiantLamprey: 2962, + Devourer: 2963, + RockWorm: 2964, + SandMaggot: 2965, + BushBarb: 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, + Wraith: 2989, + 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, + Horror: 3012, + Returned: 3013, + BurningDead: 3014, + BoneWarrior: 3015, + Damned: 3016, + Disfigured: 3017, + Misshapen: 3018, + Tainted: 3019, + Afflicted: 3020, + Camel: 3033, + Cadaver: 3034, + PreservedDead: 3035, + Embalmed: 3036, + DriedCorpse: 3037, + Decayed: 3038, + Urdar: 3039, + Mauler: 3040, + Gorebelly: 3041, + Blunderbore: 3042, + BloodMaggotYoung: 3043, + GiantLampreyYoung: 3044, + DevourerYoung: 3045, + RockWormYoung: 3046, + SandMaggotYoung: 3047, + BloodMaggotEgg: 3048, + GiantLampreyEgg: 3049, + DevourerEgg: 3050, + RockWormEgg: 3051, + SandMaggotEgg: 3052, + Maggot: 3053, + BloodHawkNest: 3055, + FlyingScimitar: 3056, + CloudStalkerNest: 3057, + BlackRaptorNest: 3058, + FoulCrowNest: 3059, + 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, + WarpedOne: 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, + StygianDollShaman: 3100, + SoulKillerShaman: 3101, + FlayerShaman: 3102, + FetishShaman: 3103, + RatManShaman: 3104, + HorrorMage: 3105, + BurningDeadMage: 3106, + BoneMage: 3107, + CorpseMage: 3108, + ReturnedMage: 3109, + GargoyleTrap: 3110, + NightMarauder: 3121, + FireGolem: 3122, + IronGolem: 3123, + BloodGolem: 3124, + ClayGolem: 3125, + BloodMaggotQueen: 3126, + GiantLampreyQueen: 3127, + DevourerQueen: 3128, + RockWormQueen: 3129, + SandMaggotQueen: 3130, + SlimePrince: 3131, + BogCreature: 3132, + SwampDweller: 3133, + BarbedGiant: 3134, + RazorBeast: 3135, + ThornBrute: 3136, + SpikeGiant: 3137, + QuillBear: 3138, + CouncilMember: 3139, + DarkWanderer: 3141, + HellSlinger: 3142, + NightSlinger: 3143, + SpearCat: 3144, + Slinger: 3145, + FireTower: 3146, + LightningSpire: 3147, + PitLord: 3148, + Balrog: 3149, + VenomLord: 3150, + IronWolf: 3151, + InvisoSpawner: 3152, + OblivionKnight: 3153, + Mage: 3154, + AbyssKnight: 3155, + FighterMage: 3156, + DoomKnight: 3157, + Fighter: 3158, + MawFiend: 3159, + CorpseSpitter: 3160, + Corpulent: 3161, + StormCaster: 3162, + Strangler: 3163, + DoomCaster: 3164, + GrotesqueWyrm: 3165, + StygianDog: 3166, + FleshBeast: 3167, + Grotesque: 3168, + StygianHag: 3169, + FleshSpawner: 3170, + RogueScout: 3171, + BloodWingNest: 3172, + BloodHookNest: 3173, + FeederNest: 3174, + SuckerNest: 3175, + Hydra: 3325, + }, + npcs: { + Asheara: 1008, + Hratli: 1009, + Alkor: 1010, + Ormus: 1011, + Natalya: 1012, + Tyrael: 1013, + Izual1: 1014, + Izual2: 1015, + Jamella: 1016, + Halbu: 1017, + Hadriel: 1018, + Hazade: 1019, + Alhizeer: 1020, + Azrael: 1021, + Ahsab: 1022, + Chalan: 1023, + Haseen: 1024, + Razan: 1025, + Emilio: 1026, + Pratham: 1027, + Fazel: 1028, + Jemali: 1029, + Kasim: 1030, + Gulzar: 1031, + Mizan: 1032, + Leharas: 1033, + Durga: 1034, + Neeraj: 1035, + Ilzan: 1036, + Zanarhi: 1037, + Waheed: 1038, + Vikhyat: 1039, + Jelani: 1040, + Barani: 1041, + Jabari: 1042, + Devak: 1043, + Raldin: 1044, + Telash: 1045, + Ajheed: 1046, + Narphet: 1047, + Khaleel: 1048, + Phaet: 1049, + Geshef: 1050, + Vanji: 1051, + Haphet: 1052, + Thadar: 1053, + Yatiraj: 1054, + Rhadge: 1055, + Yashied: 1056, + Lharhad: 1057, + Flux: 1058, + Scorch: 1059, + //Natalya: 3022, both 1012 and 3022 return Natalya? + DeckardCain: 2890, + Gheed: 2891, + Akara: 2892, + Kashya: 2893, + Charsi: 2894, + Warriv: 2895, + Drognan: 3023, + Atma: 3024, + Fara: 3025, + Lysander: 3026, + Jerhyn: 3028, + Geglash: 3029, + Elzix: 3030, + Greiz: 3031, + Flavie: 3112, + Kaelan: 3113, + Meshif: 3114, + Larzuk: 22476, + Anya: 22477, + Malah: 22478, + Nihlathak1: 22479, + QualKehk: 22480, + Guard: 22481, + Combatant: 22482, + Nihlathak2: 22483, + }, + items: { + KhalimsFlail: 1060, + KhalimsWill1: 1061, + KhalimsFlail2: 1062, + KhalimsWill2: 1063, + KhalimsEye: 1064, + KhalimsBrain: 1065, + KhalimsHeart: 1066, + ScrollofInifuss: 2216, + KeytotheCairnStones: 2217, + AJadeFigurine: 2227, + TheGoldenBird: 2228, + LamEsensTome1: 2229, + LamEsensTome2: 2230, + HoradricCube: 2231, + HoradricScroll: 2232, + MephistosSoulstone: 2233, + Ear: 2235, + AmuletoftheViper: 2697, + StaffofKings: 2698, + HoradricStaff: 2699, - // Sets - // Angelic Rainment - AngelicsSword: 10172, - AngelicsArmor: 10173, - AngelicsRing: 10174, - AngelicsAmulet: 10175, - // Arcannas Tricks - ArcannasAmulet: 10180, - ArcannasStaff: 10181, - ArcannasHelmet: 10182, - ArcannasArmor: 10183, - // Artic Gear - ArticsBow: 10176, - ArticsArmor: 10177, - ArticsBelt: 10178, - ArticsGloves: 10179, - // Berserkers Gear - BerserkersHelmet: 10166, - BerserkersAxe: 10167, - BerserkersArmor: 10168, - // Cathans Traps - CathansRing: 10147, - CathansAmulet: 10148, - CathansHelmet: 10149, - CathansArmor: 10150, - CathansStaff: 10151, - // Civerbs Gear - CiverbsShield: 10122, - CiverbsAmulet: 10123, - CiverbsScepter: 10124, - // Clegaws Brace - ClegawsSword: 10128, - ClegawsShield: 10129, - ClegawsGloves: 10130, - // Deaths Disguise - DeathsGloves: 10169, - DeathsBelt: 10170, - DeathsSword: 10171, - // Hsarus Defense - HsarusBoots: 10125, - HsarusShield: 10126, - HsarusBelt: 10127, - // Infernal Tools - InfernalsHelmet: 10163, - InfernalsWand: 10164, - InfernalsBelt: 10165, - // Irathas Finery - IrathasBelt: 10131, - IrathasHelmet: 10132, - IrathasGloves: 10133, - IrathasAmulet: 10134, - // Isenharts Armory - IsenhartsHelmet: 10135, - IsenhartsArmor: 10136, - IsenhartsShield: 10137, - IsenhartsSword: 10138, - // Milabrega Regalia - MilabregasArmor: 10143, - MilabregasHelmet: 10144, - MilabregasScepter: 10145, - MilabregasShield: 10146, - // Sigons - SigonsHelmet: 10157, - SigonsArmor: 10158, - SigonsGloves: 10159, - SigonsBoots: 10160, - SigonsBelt: 10161, - SigonsShield: 10162, - // Tancreds - TancredsPick: 10152, - TancredsArmor: 10153, - TancredsBoots: 10154, - TancredsAmulet: 10155, - TancredsHelmet: 10156, - // Vidalas - VidalasAmulet: 10139, - VidalasArmor: 10140, - VidalasBoots: 10141, - VidalasBow: 10142, + // Sets + // Angelic Rainment + AngelicsSword: 10172, + AngelicsArmor: 10173, + AngelicsRing: 10174, + AngelicsAmulet: 10175, + // Arcannas Tricks + ArcannasAmulet: 10180, + ArcannasStaff: 10181, + ArcannasHelmet: 10182, + ArcannasArmor: 10183, + // Artic Gear + ArticsBow: 10176, + ArticsArmor: 10177, + ArticsBelt: 10178, + ArticsGloves: 10179, + // Berserkers Gear + BerserkersHelmet: 10166, + BerserkersAxe: 10167, + BerserkersArmor: 10168, + // Cathans Traps + CathansRing: 10147, + CathansAmulet: 10148, + CathansHelmet: 10149, + CathansArmor: 10150, + CathansStaff: 10151, + // Civerbs Gear + CiverbsShield: 10122, + CiverbsAmulet: 10123, + CiverbsScepter: 10124, + // Clegaws Brace + ClegawsSword: 10128, + ClegawsShield: 10129, + ClegawsGloves: 10130, + // Deaths Disguise + DeathsGloves: 10169, + DeathsBelt: 10170, + DeathsSword: 10171, + // Hsarus Defense + HsarusBoots: 10125, + HsarusShield: 10126, + HsarusBelt: 10127, + // Infernal Tools + InfernalsHelmet: 10163, + InfernalsWand: 10164, + InfernalsBelt: 10165, + // Irathas Finery + IrathasBelt: 10131, + IrathasHelmet: 10132, + IrathasGloves: 10133, + IrathasAmulet: 10134, + // Isenharts Armory + IsenhartsHelmet: 10135, + IsenhartsArmor: 10136, + IsenhartsShield: 10137, + IsenhartsSword: 10138, + // Milabrega Regalia + MilabregasArmor: 10143, + MilabregasHelmet: 10144, + MilabregasScepter: 10145, + MilabregasShield: 10146, + // Sigons + SigonsHelmet: 10157, + SigonsArmor: 10158, + SigonsGloves: 10159, + SigonsBoots: 10160, + SigonsBelt: 10161, + SigonsShield: 10162, + // Tancreds + TancredsPick: 10152, + TancredsArmor: 10153, + TancredsBoots: 10154, + TancredsAmulet: 10155, + TancredsHelmet: 10156, + // Vidalas + VidalasAmulet: 10139, + VidalasArmor: 10140, + VidalasBoots: 10141, + VidalasBow: 10142, - // LoD Sets - // Aldurs's Legacy - AldursHelmet: 21697, - AldursArmor: 21698, - AldursBoots: 21700, - AldursMace: 21847, - // Bul-Kathos's Children - BulKathosBlade: 21688, - BulKathoSword: 21689, - // Cow Kings's Leathers - CowKingsHelmet: 21723, - CowKingsArmor: 21724, - CowKingsBoots: 21725, - // Disciples - DisciplesAmulet: 21717, - DisciplesGloves: 21718, - DisciplesBoots: 21719, - DisciplesArmor: 21720, - DisciplesBelt: 21721, - // Griswolds's Legacy - GriswoldsScepter: 21673, - GriswoldsShield: 21674, - GriswoldsArmor: 21675, - GriswoldsHelmet: 21676, - // Heaven's Brethren - HeavensMace: 21823, - HeavensHelmet: 21824, - HeavensShield: 21825, - HeavensArmor: 21826, - // Hwanin's - HwaninsHelmet: 21712, - HwaninsPolearm: 21713, - HwaninsArmor: 21714, - HwaninsBelt: 21821, - // IK - ImmortalKingsMaul: 21840, - ImmortalKingsBoots: 21841, - ImmortalKingsGloves: 21842, - ImmortalKingsBelt: 21843, - ImmortalKingsArmor: 21844, - ImmortalKingsHelmet: 21845, - // M'avina's - MavinasHelmet: 21702, - MavinasArmor: 21703, - MavinasGloves: 21704, - MavinasBelt: 21705, - MavinasBow: 21706, - // Natalya's - NatalyasHelmet: 21668, - NatalyasClaw: 21669, - NatalyasArmor: 21670, - NatalyasBoots: 21671, - // Naj's - NajsStaff: 21640, - NajsArmor: 21831, - NajsHelmet: 21832, - // Orphan's - OrphansHelmet: 21731, - OrphansBelt: 21732, - OrphansGloves: 21733, - OrphansShield: 21734, - // Sanders's - SandersGloves: 21876, - SandersBoots: 21877, - SandersHelmet: 21878, - SandersWand: 21879, - // Sazabi's - SazabisSword: 21708, - SazabisArmor: 21709, - SazabisHelmet: 21710, - // Tal - TalRashasBelt: 21816, - TalRashasAmulet: 21817, - TalRashasArmor: 21818, - TalRashasOrb: 21819, - TalRashasHelmet: 21820, - // Trang-Ouls - TrangOulsHelmet: 21661, - TrangOulsShield: 21662, - TrangOulsArmor: 21664, - TrangOulsGloves: 21665, - TrangOulsBelt: 21666, + // LoD Sets + // Aldurs's Legacy + AldursHelmet: 21697, + AldursArmor: 21698, + AldursBoots: 21700, + AldursMace: 21847, + // Bul-Kathos's Children + BulKathosBlade: 21688, + BulKathoSword: 21689, + // Cow Kings's Leathers + CowKingsHelmet: 21723, + CowKingsArmor: 21724, + CowKingsBoots: 21725, + // Disciples + DisciplesAmulet: 21717, + DisciplesGloves: 21718, + DisciplesBoots: 21719, + DisciplesArmor: 21720, + DisciplesBelt: 21721, + // Griswolds's Legacy + GriswoldsScepter: 21673, + GriswoldsShield: 21674, + GriswoldsArmor: 21675, + GriswoldsHelmet: 21676, + // Heaven's Brethren + HeavensMace: 21823, + HeavensHelmet: 21824, + HeavensShield: 21825, + HeavensArmor: 21826, + // Hwanin's + HwaninsHelmet: 21712, + HwaninsPolearm: 21713, + HwaninsArmor: 21714, + HwaninsBelt: 21821, + // IK + ImmortalKingsMaul: 21840, + ImmortalKingsBoots: 21841, + ImmortalKingsGloves: 21842, + ImmortalKingsBelt: 21843, + ImmortalKingsArmor: 21844, + ImmortalKingsHelmet: 21845, + // M'avina's + MavinasHelmet: 21702, + MavinasArmor: 21703, + MavinasGloves: 21704, + MavinasBelt: 21705, + MavinasBow: 21706, + // Natalya's + NatalyasHelmet: 21668, + NatalyasClaw: 21669, + NatalyasArmor: 21670, + NatalyasBoots: 21671, + // Naj's + NajsStaff: 21640, + NajsArmor: 21831, + NajsHelmet: 21832, + // Orphan's + OrphansHelmet: 21731, + OrphansBelt: 21732, + OrphansGloves: 21733, + OrphansShield: 21734, + // Sanders's + SandersGloves: 21876, + SandersBoots: 21877, + SandersHelmet: 21878, + SandersWand: 21879, + // Sazabi's + SazabisSword: 21708, + SazabisArmor: 21709, + SazabisHelmet: 21710, + // Tal + TalRashasBelt: 21816, + TalRashasAmulet: 21817, + TalRashasArmor: 21818, + TalRashasOrb: 21819, + TalRashasHelmet: 21820, + // Trang-Ouls + TrangOulsHelmet: 21661, + TrangOulsShield: 21662, + TrangOulsArmor: 21664, + TrangOulsGloves: 21665, + TrangOulsBelt: 21666, - // Uniques - // Quest/Misc - KeyofTerror: 11146, - KeyofHate: 11147, - KeyofDestruction: 11148, - DiablosHorn: 11149, - BaalsEye: 11150, - MephistosBrain: 11151, - StandardofHeroes: 11152, - HellfireTorch: 11153, - Annihilus: 21743, + // Uniques + // Quest/Misc + KeyofTerror: 11146, + KeyofHate: 11147, + KeyofDestruction: 11148, + DiablosHorn: 11149, + BaalsEye: 11150, + MephistosBrain: 11151, + StandardofHeroes: 11152, + HellfireTorch: 11153, + Annihilus: 21743, - // Unique Items - WitchwildString: 10911, - TitansRevenge: 21735, - LycandersAim: 21737, - ArreatsFace: 21744, - Homunculus: 21755, - JalalsMane: 21750, - HeraldofZakarum: 21758, - BloodRavensCharge: 21508, - Gimmershred: 21637, - MedusasGaze: 21516, - Rockstopper: 21519, - CrownofThieves: 21522, - BlackhornsFace: 21523, - TheSpiritShroud: 21524, - SkinoftheFlayedOne: 21525, - IronPelt: 21526, - SpiritForge: 21527, - CrowCaw: 21528, - DurielsShell: 21529, - SkulldersIre: 21530, - Toothrow: 21531, - AtmasWail: 21532, - BlackHades: 21533, - Corpsemourn: 21534, - QueHegans: 21535, - QueHegansWisdom: 21535, - Mosers: 21536, - MosersBlessedCircle: 21536, - Stormchaser: 21537, - TiamatsRubuke: 21538, - GerkesSanctuary: 21539, - RadamentsSphere: 21540, - Gravepalm: 21541, - Ghoulhide: 21542, - Hellmouth: 21543, - Infernostride: 21544, - Waterwalk: 21545, - Silkweave: 21546, - WarTraveler: 21547, - Razortail: 21548, - GloomsTrap: 21549, - Snowclash: 21550, - ThundergodsVigor: 21551, - LidlessWall: 21552, - LanceGuard: 21553, - Boneflame: 21555, - SteelPillar: 21556, - NightwingsVeil: 21557, - CrownofAges: 21559, - AndarielsVisage: 21560, - Dragonscale: 21562, - SteelCarapace: 21563, - RainbowFacet: 21565, - Ravenlore: 21566, - Boneshade: 21567, - Flamebellow: 21570, - DeathsFathom: 21571, - Wolfhowl: 21572, - SpiritWard: 21573, - KirasGuardian: 21574, - OrmusRobe: 21575, - GheedsFortune: 21576, - HalberdsReign: 21579, - DraculsGrasp: 21583, - Frostwind: 21584, - TemplarsMight: 21585, - EschutasTemper: 21586, // also 21620? - FirelizardsTalons: 21587, - SandstormTrek: 21588, - Marrowwalk: 21589, - HeavensLight: 21590, - ArachnidMesh: 21592, - NosferatusCoil: 21593, - Verdungos: 21595, - VerdungosHeartyCord: 21595, - CarrionWind: 21597, - GiantSkull: 21598, - AstreonsIronWard: 21599, - SaracensChance: 21608, - HighlordsWrath: 21609, - Ravenfrost: 21610, - Dwarfstar: 21611, - AtmasScarab: 21612, - Maras: 21613, - MarasKaleidoscope: 21613, - CrescentMoonAmulet: 21614, - TheRisingSun: 21615, - TheCatsEye: 21616, - BulKathosWeddingBand: 21617, - Metalgrid: 21619, - Stormshield: 21621, - BlackoakShield: 21622, - ArkainesValor: 21624, - TheGladiatorsBane: 21625, - HarlequinsCrest: 21627, - GuardianAngel: 21632, - TheGrandfather: 21643, - Doombringer: 21644, - TyraelsMight: 21645, - Lightsabre: 21646, - TheCraniumBasher: 21647, - DeathsWeb: 21650, - TheAtlantean: 21654, - CarinShard: 21658, - Coldkill: 21286, - ButchersCleaver: 21287, - Islestrike: 21289, - GuardianNaga: 21291, - SpellSteel: 21293, - SuicideBranch: 21297, - ArmofKingLeoric: 21299, - BlackhandKey: 21300, - DarkClanCrusher: 21301, - TheFetidSprinkler: 21304, - HandofBlessedLight: 21305, - Fleshrender: 21306, - SureshrillFrost: 21307, - Moonfall: 21308, - BaezilsVortex: 21309, - Earthshaker: 21310, - TheGavelofPain: 21312, - Bloodletter: 21313, - ColdstealEye: 21314, - Hexfire: 21315, - BladeofAliBaba: 21316, - Riftslash: 21317, - Headstriker: 21318, - PlagueBearer: 21319, - //TheAtlantean: 21320, - CrainteVomir: 21321, - BingSzWang: 21322, - TheVileHusk: 21323, - Cloudcrack: 21324, - TodesfaelleFlamme: 21325, - Swordguard: 21326, - Spineripper: 21327, - HeartCarver: 21328, - BlackbogsSharp: 21329, - Stormspike: 21330, - TheImpaler: 21331, - HoneSudan: 21334, - SpireofHonor: 21335, - TheMeatScraper: 21336, - BlackleachBlade: 21337, - AthenasWrath: 21338, - PierreTombaleCouant: 21339, - GrimsBurningDead: 21341, - Ribcracker: 21342, - ChromaticIre: 21343, - Warspear: 21344, - SkullCollector: 21345, - Skystrike: 21346, - //WitchwildString: 21349, - GoldstrikeArch: 21350, - PusSpitter: 21352, - VampireGaze: 21354, - StringofEars: 21355, - GoreRider: 21356, - LavaGout: 21357, - VenomGrip: 21358, - Visceratuant: 21359, - //GuardianAngel: 21360, - Shaftstop: 21361, - SkinofVipermagi: 21362, - Blackhorn: 21363, - ValkyrieWing: 21364, - PeasantCrown: 21365, - DemonMachine: 21366, - Riphook: 21369, - Razorswitch: 21370, - OndalsWisdom: 21375, - Deathbit: 21379, - Warshrike: 21380, - DemonLimb: 21387, - SteelShade: 21388, - TombReaver: 21389, - //DeathsWeb: 21390, - AngelsSong: 21393, - TheRedeemer: 21394, - Bonehew: 21398, - Steelrend: 21399, - AriocsNeedle: 21402, - SoulDrainer: 21407, - RuneMaster: 21408, - DeathCleaver: 21409, - ExecutionersJustice: 21410, - Leviathan: 21412, - WispProjector: 21417, - Lacerator: 21419, - MangSongsLesson: 21420, - Viperfork: 21421, - TheReapersToll: 21427, - SpiritKeeper: 21428, - Hellrack: 21429, - AlmaNegra: 21430, - DarkforceSpawn: 21431, - Ghostflame: 21438, - ShadowKiller: 21439, - GriffonsEye: 21442, - Thunderstroke: 21445, - DemonsArch: 21447, - DjinnSlayer: 21450, + // Unique Items + WitchwildString: 10911, + TitansRevenge: 21735, + LycandersAim: 21737, + ArreatsFace: 21744, + Homunculus: 21755, + JalalsMane: 21750, + HeraldofZakarum: 21758, + BloodRavensCharge: 21508, + Gimmershred: 21637, + MedusasGaze: 21516, + Rockstopper: 21519, + CrownofThieves: 21522, + BlackhornsFace: 21523, + TheSpiritShroud: 21524, + SkinoftheFlayedOne: 21525, + IronPelt: 21526, + SpiritForge: 21527, + CrowCaw: 21528, + DurielsShell: 21529, + SkulldersIre: 21530, + Toothrow: 21531, + AtmasWail: 21532, + BlackHades: 21533, + Corpsemourn: 21534, + QueHegans: 21535, + QueHegansWisdom: 21535, + Mosers: 21536, + MosersBlessedCircle: 21536, + Stormchaser: 21537, + TiamatsRubuke: 21538, + GerkesSanctuary: 21539, + RadamentsSphere: 21540, + Gravepalm: 21541, + Ghoulhide: 21542, + Hellmouth: 21543, + Infernostride: 21544, + Waterwalk: 21545, + Silkweave: 21546, + WarTraveler: 21547, + Razortail: 21548, + GloomsTrap: 21549, + Snowclash: 21550, + ThundergodsVigor: 21551, + LidlessWall: 21552, + LanceGuard: 21553, + Boneflame: 21555, + SteelPillar: 21556, + NightwingsVeil: 21557, + CrownofAges: 21559, + AndarielsVisage: 21560, + Dragonscale: 21562, + SteelCarapace: 21563, + RainbowFacet: 21565, + Ravenlore: 21566, + Boneshade: 21567, + Flamebellow: 21570, + DeathsFathom: 21571, + Wolfhowl: 21572, + SpiritWard: 21573, + KirasGuardian: 21574, + OrmusRobe: 21575, + GheedsFortune: 21576, + HalberdsReign: 21579, + DraculsGrasp: 21583, + Frostwind: 21584, + TemplarsMight: 21585, + EschutasTemper: 21586, // also 21620? + FirelizardsTalons: 21587, + SandstormTrek: 21588, + Marrowwalk: 21589, + HeavensLight: 21590, + ArachnidMesh: 21592, + NosferatusCoil: 21593, + Verdungos: 21595, + VerdungosHeartyCord: 21595, + CarrionWind: 21597, + GiantSkull: 21598, + AstreonsIronWard: 21599, + SaracensChance: 21608, + HighlordsWrath: 21609, + Ravenfrost: 21610, + Dwarfstar: 21611, + AtmasScarab: 21612, + Maras: 21613, + MarasKaleidoscope: 21613, + CrescentMoonAmulet: 21614, + TheRisingSun: 21615, + TheCatsEye: 21616, + BulKathosWeddingBand: 21617, + Metalgrid: 21619, + Stormshield: 21621, + BlackoakShield: 21622, + ArkainesValor: 21624, + TheGladiatorsBane: 21625, + HarlequinsCrest: 21627, + GuardianAngel: 21632, + TheGrandfather: 21643, + Doombringer: 21644, + TyraelsMight: 21645, + Lightsabre: 21646, + TheCraniumBasher: 21647, + DeathsWeb: 21650, + TheAtlantean: 21654, + CarinShard: 21658, + Coldkill: 21286, + ButchersCleaver: 21287, + Islestrike: 21289, + GuardianNaga: 21291, + SpellSteel: 21293, + SuicideBranch: 21297, + ArmofKingLeoric: 21299, + BlackhandKey: 21300, + DarkClanCrusher: 21301, + TheFetidSprinkler: 21304, + HandofBlessedLight: 21305, + Fleshrender: 21306, + SureshrillFrost: 21307, + Moonfall: 21308, + BaezilsVortex: 21309, + Earthshaker: 21310, + TheGavelofPain: 21312, + Bloodletter: 21313, + ColdstealEye: 21314, + Hexfire: 21315, + BladeofAliBaba: 21316, + Riftslash: 21317, + Headstriker: 21318, + PlagueBearer: 21319, + //TheAtlantean: 21320, + CrainteVomir: 21321, + BingSzWang: 21322, + TheVileHusk: 21323, + Cloudcrack: 21324, + TodesfaelleFlamme: 21325, + Swordguard: 21326, + Spineripper: 21327, + HeartCarver: 21328, + BlackbogsSharp: 21329, + Stormspike: 21330, + TheImpaler: 21331, + HoneSudan: 21334, + SpireofHonor: 21335, + TheMeatScraper: 21336, + BlackleachBlade: 21337, + AthenasWrath: 21338, + PierreTombaleCouant: 21339, + GrimsBurningDead: 21341, + Ribcracker: 21342, + ChromaticIre: 21343, + Warspear: 21344, + SkullCollector: 21345, + Skystrike: 21346, + //WitchwildString: 21349, + GoldstrikeArch: 21350, + PusSpitter: 21352, + VampireGaze: 21354, + StringofEars: 21355, + GoreRider: 21356, + LavaGout: 21357, + VenomGrip: 21358, + Visceratuant: 21359, + //GuardianAngel: 21360, + Shaftstop: 21361, + SkinofVipermagi: 21362, + Blackhorn: 21363, + ValkyrieWing: 21364, + PeasantCrown: 21365, + DemonMachine: 21366, + Riphook: 21369, + Razorswitch: 21370, + OndalsWisdom: 21375, + Deathbit: 21379, + Warshrike: 21380, + DemonLimb: 21387, + SteelShade: 21388, + TombReaver: 21389, + //DeathsWeb: 21390, + AngelsSong: 21393, + TheRedeemer: 21394, + Bonehew: 21398, + Steelrend: 21399, + AriocsNeedle: 21402, + SoulDrainer: 21407, + RuneMaster: 21408, + DeathCleaver: 21409, + ExecutionersJustice: 21410, + Leviathan: 21412, + WispProjector: 21417, + Lacerator: 21419, + MangSongsLesson: 21420, + Viperfork: 21421, + TheReapersToll: 21427, + SpiritKeeper: 21428, + Hellrack: 21429, + AlmaNegra: 21430, + DarkforceSpawn: 21431, + Ghostflame: 21438, + ShadowKiller: 21439, + GriffonsEye: 21442, + Thunderstroke: 21445, + DemonsArch: 21447, + DjinnSlayer: 21450, - // Runewords - AncientsPledge: 20507, - Armageddon: 20508, - Authority: 20509, - Beast: 20510, - Beauty: 20511, - Black: 20512, - Blood: 20513, - Bone: 20514, - Bramble: 20515, - Brand: 20516, - BreathoftheDying: 20517, - BrokenPromise: 20518, - CalltoArms: 20519, - ChainsofHonor: 20520, - Chance: 20521, - Chaos: 20522, - CrescentMoon: 20523, - Darkness: 20524, - Daylight: 20525, - Death: 20526, - Deception: 20527, - Delerium: 20528, - Desire: 20529, - Despair: 20530, - Destruction: 20531, - Doom: 20532, - Dragon: 20533, - Dread: 20534, - Dream: 20535, - Duress: 20536, - Edge: 20537, - Elation: 20538, - Enigma: 20539, - Enlightenment: 20540, - Envy: 20541, - Eternity: 20542, - Exile: 20543, - Faith: 20544, - Famine: 20545, - Flame: 20546, - Fortitude: 20547, - Fortune: 20548, - Friendship: 20549, - Fury: 20550, - Gloom: 20551, - Grief: 20553, - HandofJustice: 20554, - Harmony: 20555, - HeartoftheOak: 20557, - HolyThunder: 20560, - Honor: 20561, - Revenge: 20562, - Humility: 20563, - Hunger: 20564, - Ice: 20565, - Infinity: 20566, - Innocence: 20567, - Insight: 20568, - Jealousy: 20569, - Judgement: 20570, - KingsGrace: 20571, - Kingslayer: 20572, - KnightsVigil: 20573, - Knowledge: 20574, - LastWish: 20575, - Law: 20576, - Lawbringer: 20577, - Leaf: 20578, - Lightning: 20579, - Lionheart: 20580, - Lore: 20581, - Love: 20582, - Loyalty: 20583, - Lust: 20584, - Madness: 20585, - Malice: 20586, - Melody: 20587, - Memory: 20588, - Mist: 20589, - Morning: 20590, - Mystery: 20591, - Myth: 20592, - Nadir: 20593, - NaturesKingdom: 20594, - Night: 20595, - Oath: 20596, - Obedience: 20597, - Oblivion: 20598, - Obsession: 20599, - Passion: 20600, - Patience: 20601, - Patter: 20602, - Peace: 20603, - VoiceofReason: 20604, - Penitence: 20605, - Peril: 20606, - Pestilence: 20607, - Phoenix: 20608, - Piety: 20609, - PillarofFaith: 20610, - Plague: 20611, - Praise: 20612, - Prayer: 20613, - Pride: 20614, - Principle: 20615, - ProwessinBattle: 20616, - Prudence: 20617, - Punishment: 20618, - Purity: 20619, - Question: 20620, - Radiance: 20621, - Rain: 20622, - Reason: 20623, - Red: 20624, - Rhyme: 20625, - Rift: 20626, - Sanctuary: 20627, - Serendipity: 20628, - Shadow: 20629, - ShadowofDoubt: 20630, - Silence: 20631, - SirensSong: 20632, - Smoke: 20633, - Sorrow: 20634, - Spirit: 20635, - Splendor: 20636, - Starlight: 20637, - Stealth: 20638, - Steel: 20639, - StillWater: 20640, - Sting: 20641, - Stone: 20642, - Storm: 20643, - Strength: 20644, - Tempest: 20645, - Temptation: 20646, - Terror: 20647, - Thirst: 20648, - Thought: 20649, - Thunder: 20650, - Time: 20651, - Tradition: 20652, - Treachery: 20653, - Trust: 20654, - Truth: 20655, - UnbendingWill: 20656, - Valor: 20657, - Vengeance: 20658, - Venom: 20659, - Victory: 20660, - Voice: 20661, - Void: 20662, - War: 20663, - Water: 20664, - Wealth: 20665, - Whisper: 20666, - White: 20667, - Wind: 20668, - WingsofHope: 20669, - Wisdom: 20670, - Woe: 20671, - Wonder: 20672, - Wrath: 20673, - Youth: 20674, - Zephyr: 20675, - }, - dialog: { - youDoNotHaveEnoughGoldForThat: 3362 - }, + // Runewords + AncientsPledge: 20507, + Armageddon: 20508, + Authority: 20509, + Beast: 20510, + Beauty: 20511, + Black: 20512, + Blood: 20513, + Bone: 20514, + Bramble: 20515, + Brand: 20516, + BreathoftheDying: 20517, + BrokenPromise: 20518, + CalltoArms: 20519, + ChainsofHonor: 20520, + Chance: 20521, + Chaos: 20522, + CrescentMoon: 20523, + Darkness: 20524, + Daylight: 20525, + Death: 20526, + Deception: 20527, + Delerium: 20528, + Desire: 20529, + Despair: 20530, + Destruction: 20531, + Doom: 20532, + Dragon: 20533, + Dread: 20534, + Dream: 20535, + Duress: 20536, + Edge: 20537, + Elation: 20538, + Enigma: 20539, + Enlightenment: 20540, + Envy: 20541, + Eternity: 20542, + Exile: 20543, + Faith: 20544, + Famine: 20545, + Flame: 20546, + Fortitude: 20547, + Fortune: 20548, + Friendship: 20549, + Fury: 20550, + Gloom: 20551, + Grief: 20553, + HandofJustice: 20554, + Harmony: 20555, + HeartoftheOak: 20557, + HolyThunder: 20560, + Honor: 20561, + Revenge: 20562, + Humility: 20563, + Hunger: 20564, + Ice: 20565, + Infinity: 20566, + Innocence: 20567, + Insight: 20568, + Jealousy: 20569, + Judgement: 20570, + KingsGrace: 20571, + Kingslayer: 20572, + KnightsVigil: 20573, + Knowledge: 20574, + LastWish: 20575, + Law: 20576, + Lawbringer: 20577, + Leaf: 20578, + Lightning: 20579, + Lionheart: 20580, + Lore: 20581, + Love: 20582, + Loyalty: 20583, + Lust: 20584, + Madness: 20585, + Malice: 20586, + Melody: 20587, + Memory: 20588, + Mist: 20589, + Morning: 20590, + Mystery: 20591, + Myth: 20592, + Nadir: 20593, + NaturesKingdom: 20594, + Night: 20595, + Oath: 20596, + Obedience: 20597, + Oblivion: 20598, + Obsession: 20599, + Passion: 20600, + Patience: 20601, + Patter: 20602, + Peace: 20603, + VoiceofReason: 20604, + Penitence: 20605, + Peril: 20606, + Pestilence: 20607, + Phoenix: 20608, + Piety: 20609, + PillarofFaith: 20610, + Plague: 20611, + Praise: 20612, + Prayer: 20613, + Pride: 20614, + Principle: 20615, + ProwessinBattle: 20616, + Prudence: 20617, + Punishment: 20618, + Purity: 20619, + Question: 20620, + Radiance: 20621, + Rain: 20622, + Reason: 20623, + Red: 20624, + Rhyme: 20625, + Rift: 20626, + Sanctuary: 20627, + Serendipity: 20628, + Shadow: 20629, + ShadowofDoubt: 20630, + Silence: 20631, + SirensSong: 20632, + Smoke: 20633, + Sorrow: 20634, + Spirit: 20635, + Splendor: 20636, + Starlight: 20637, + Stealth: 20638, + Steel: 20639, + StillWater: 20640, + Sting: 20641, + Stone: 20642, + Storm: 20643, + Strength: 20644, + Tempest: 20645, + Temptation: 20646, + Terror: 20647, + Thirst: 20648, + Thought: 20649, + Thunder: 20650, + Time: 20651, + Tradition: 20652, + Treachery: 20653, + Trust: 20654, + Truth: 20655, + UnbendingWill: 20656, + Valor: 20657, + Vengeance: 20658, + Venom: 20659, + Victory: 20660, + Voice: 20661, + Void: 20662, + War: 20663, + Water: 20664, + Wealth: 20665, + Whisper: 20666, + White: 20667, + Wind: 20668, + WingsofHope: 20669, + Wisdom: 20670, + Woe: 20671, + Wonder: 20672, + Wrath: 20673, + Youth: 20674, + Zephyr: 20675, + }, + dialog: { + youDoNotHaveEnoughGoldForThat: 3362 + }, - text: { - RepairCost: 3330, - SellValue: 3331, - IdentifyCost: 3332, - ItemCannotBeTradedHere: 3333, - TradeRepair: 3334, - Buy: 3335, - Sell: 3336, - Heal: 3337, - Repair: 3338, - NextPage: 3339, - PreviousPage: 3340, - Transmute: 3341, - YourGold: 3342, - WhichItemShouldBeImbued: 3343, - Yes: 3344, - No: 3345, - Gold2: 3346, - Sell2: 3347, - Buy2: 3358, - Hire: 3349, - ToStrength: 3473, - ToDexterity: 3474, - Defense: 3481, - Identify: 3350, - Repair2: 3351, - EnhancedDefense: 3520, - Strength: 4060, - Dexterity: 4062, - Vitality: 4066, - Energy: 4069, - DoNotMeetLevelReqForThisGame: 5162, - CdKeyDisabled: 5199, - CdKeyInUseBy: 5200, - OnlyOneInstanceAtATime: 5201, - CdKeyIntendedForAnotherProduct: 5202, - InvalidPassword: 5207, - AccountDoesNotExist: 5208, - AccountIsCorrupted: 5209, - AccountMustBeAtLeast: 5217, - AccountCantBeMoreThan: 5218, - PasswordMustBeAtLeast: 5219, - PasswordCantBeMoreThan: 5220, - LoginError: 5224, - UsernameMustBeAtLeast: 5231, - UsernameIncludedIllegalChars: 5232, - UsernameIncludedDisallowedwords: 5233, - AccountNameAlreadyExist: 5239, - UnableToCreateAccount: 5249, - CannotCreateGamesDeadHCChar: 5304, - Disconnected: 5347, - UnableToIndentifyVersion: 5245, - BattlenetNotResponding: 5353, - BattlenetNotResponding2: 5354, - EnhancedDamage: 10038, - LoDKeyDisabled: 10913, - LodKeyInUseBy: 10914, - LoDKeyIntendedForAnotherProduct: 10915, - YourPositionInLineIs: 11026, - Gateway: 11049, - Ghostly: 11084, - Fanatic: 11085, - Possessed: 11086, - Berserker: 11087, - ExpiresIn: 11133, - CdKeyDisabledFromRealm: 11161, - }, + text: { + RepairCost: 3330, + SellValue: 3331, + IdentifyCost: 3332, + ItemCannotBeTradedHere: 3333, + TradeRepair: 3334, + Buy: 3335, + Sell: 3336, + Heal: 3337, + Repair: 3338, + NextPage: 3339, + PreviousPage: 3340, + Transmute: 3341, + YourGold: 3342, + WhichItemShouldBeImbued: 3343, + Yes: 3344, + No: 3345, + Gold2: 3346, + Sell2: 3347, + Buy2: 3358, + Hire: 3349, + ToStrength: 3473, + ToDexterity: 3474, + Defense: 3481, + Identify: 3350, + Repair2: 3351, + EnhancedDefense: 3520, + Strength: 4060, + Dexterity: 4062, + Vitality: 4066, + Energy: 4069, + DoNotMeetLevelReqForThisGame: 5162, + CdKeyDisabled: 5199, + CdKeyInUseBy: 5200, + OnlyOneInstanceAtATime: 5201, + CdKeyIntendedForAnotherProduct: 5202, + InvalidPassword: 5207, + AccountDoesNotExist: 5208, + AccountIsCorrupted: 5209, + AccountMustBeAtLeast: 5217, + AccountCantBeMoreThan: 5218, + PasswordMustBeAtLeast: 5219, + PasswordCantBeMoreThan: 5220, + LoginError: 5224, + UsernameMustBeAtLeast: 5231, + UsernameIncludedIllegalChars: 5232, + UsernameIncludedDisallowedwords: 5233, + AccountNameAlreadyExist: 5239, + UnableToIndentifyVersion: 5245, + UnableToCreateAccount: 5249, + CannotCreateGamesDeadHCChar: 5304, + Disconnected: 5347, + BattlenetNotResponding: 5353, + BattlenetNotResponding2: 5354, + HcCannotPlayWithSc: 5361, + ScCannotPlayWithHc: 5362, + CannotPlayInHellClassic: 5363, + CannotPlayInNightmareClassic: 5364, + EnhancedDamage: 10038, + ClassicCannotPlayWithXpac: 10101, + XpacCannotPlayWithClassic: 10102, + LoDKeyDisabled: 10913, + LodKeyInUseBy: 10914, + LoDKeyIntendedForAnotherProduct: 10915, + NonLadderCannotPlayWithLadder: 10929, + LadderCannotPlayWithNonLadder: 10930, + YourPositionInLineIs: 11026, + Gateway: 11049, + Ghostly: 11084, + Fanatic: 11085, + Possessed: 11086, + Berserker: 11087, + ExpiresIn: 11133, + CdKeyDisabledFromRealm: 11161, + CannotPlayInHellXpac: 21793, + CannotPlayInNightmareXpac: 21794, + }, - areas: { - // Act 1 - RogueEncampment: 5055, - BloodMoor: 5054, - ColdPlains: 5053, - StonyField: 5052, - DarkWood: 5051, - BlackMarsh: 5050, - TamoeHighland: 5049, - DenofEvil: 5048, - CaveLvl1: 5047, - UndergroundPassageLvl1: 5046, - HoleLvl1: 5045, - PitLvl1: 5044, - CaveLvl2: 5043, - UndergroundPassageLvl2: 5042, - HoleLvl2: 5041, - PitLvl2: 5040, - BurialGrounds: 5039, - Crypt: 5038, - Mausoleum: 5037, - ForgottenTower: 5036, - TowerCellarLvl1: 5035, - TowerCellarLvl2: 5034, - TowerCellarLvl3: 5033, - TowerCellarLvl4: 5032, - TowerCellarLvl5: 5031, - MonasteryGate: 5030, - OuterCloister: 5029, - Barracks: 5038, - JailLvl1: 5027, - JailLvl2: 5026, - JailLvl3: 5025, - InnerCloister: 5024, - Cathedral: 5023, - CatacombsLvl1: 5022, - CatacombsLvl2: 5021, - CatacombsLvl3: 5020, - CatacombsLvl4: 5019, - Tristram: 5018, - MooMooFarm: 788, + areas: { + // Act 1 + RogueEncampment: 5055, + BloodMoor: 5054, + ColdPlains: 5053, + StonyField: 5052, + DarkWood: 5051, + BlackMarsh: 5050, + TamoeHighland: 5049, + DenofEvil: 5048, + CaveLvl1: 5047, + UndergroundPassageLvl1: 5046, + HoleLvl1: 5045, + PitLvl1: 5044, + CaveLvl2: 5043, + UndergroundPassageLvl2: 5042, + HoleLvl2: 5041, + PitLvl2: 5040, + BurialGrounds: 5039, + Crypt: 5038, + Mausoleum: 5037, + ForgottenTower: 5036, + TowerCellarLvl1: 5035, + TowerCellarLvl2: 5034, + TowerCellarLvl3: 5033, + TowerCellarLvl4: 5032, + TowerCellarLvl5: 5031, + MonasteryGate: 5030, + OuterCloister: 5029, + Barracks: 5038, + JailLvl1: 5027, + JailLvl2: 5026, + JailLvl3: 5025, + InnerCloister: 5024, + Cathedral: 5023, + CatacombsLvl1: 5022, + CatacombsLvl2: 5021, + CatacombsLvl3: 5020, + CatacombsLvl4: 5019, + Tristram: 5018, + MooMooFarm: 788, - // Act 2 - LutGholein: 852, - RockyWaste: 851, - DryHills: 850, - FarOasis: 849, - LostCity: 848, - ValleyofSnakes: 847, - CanyonofMagic: 846, - A2SewersLvl1: 845, - A2SewersLvl2: 844, - A2SewersLvl3: 843, - HaremLvl1: 842, - HaremLvl2: 841, - PalaceCellarLvl1: 840, - PalaceCellarLvl2: 839, - PalaceCellarLvl3: 838, - StonyTombLvl1: 837, - HallsoftheDeadLvl1: 836, - HallsoftheDeadLvl2: 835, - ClawViperTempleLvl1: 834, - StonyTombLvl2: 833, - HallsoftheDeadLvl3: 832, - ClawViperTempleLvl2: 831, - MaggotLairLvl1: 830, - MaggotLairLvl2: 829, - MaggotLairLvl3: 828, - AncientTunnels: 827, - TalRashasTomb1: 826, - TalRashasTomb2: 826, - TalRashasTomb3: 826, - TalRashasTomb4: 826, - TalRashasTomb5: 826, - TalRashasTomb6: 826, - TalRashasTomb7: 826, - DurielsLair: 825, - ArcaneSanctuary: 824, + // Act 2 + LutGholein: 852, + RockyWaste: 851, + DryHills: 850, + FarOasis: 849, + LostCity: 848, + ValleyofSnakes: 847, + CanyonofMagic: 846, + A2SewersLvl1: 845, + A2SewersLvl2: 844, + A2SewersLvl3: 843, + HaremLvl1: 842, + HaremLvl2: 841, + PalaceCellarLvl1: 840, + PalaceCellarLvl2: 839, + PalaceCellarLvl3: 838, + StonyTombLvl1: 837, + HallsoftheDeadLvl1: 836, + HallsoftheDeadLvl2: 835, + ClawViperTempleLvl1: 834, + StonyTombLvl2: 833, + HallsoftheDeadLvl3: 832, + ClawViperTempleLvl2: 831, + MaggotLairLvl1: 830, + MaggotLairLvl2: 829, + MaggotLairLvl3: 828, + AncientTunnels: 827, + TalRashasTomb1: 826, + TalRashasTomb2: 826, + TalRashasTomb3: 826, + TalRashasTomb4: 826, + TalRashasTomb5: 826, + TalRashasTomb6: 826, + TalRashasTomb7: 826, + DurielsLair: 825, + ArcaneSanctuary: 824, - // Act 3 - KurastDocktown: 820, - SpiderForest: 819, - GreatMarsh: 818, - FlayerJungle: 817, - LowerKurast: 816, - KurastBazaar: 815, - UpperKurast: 814, - KurastCauseway: 813, - Travincal: 812, - SpiderCave: 810, - SpiderCavern: 811, - SwampyPitLvl1: 809, - SwampyPitLvl2: 808, - FlayerDungeonLvl1: 806, - FlayerDungeonLvl2: 805, - SwampyPitLvl3: 807, - FlayerDungeonLvl3: 804, - A3SewersLvl1: 845, - A3SewersLvl2: 844, - RuinedTemple: 803, - DisusedFane: 802, - ForgottenReliquary: 801, - ForgottenTemple: 800, - RuinedFane: 799, - DisusedReliquary: 798, - DuranceofHateLvl1: 797, - DuranceofHateLvl2: 796, - DuranceofHateLvl3: 795, + // Act 3 + KurastDocktown: 820, + SpiderForest: 819, + GreatMarsh: 818, + FlayerJungle: 817, + LowerKurast: 816, + KurastBazaar: 815, + UpperKurast: 814, + KurastCauseway: 813, + Travincal: 812, + SpiderCave: 810, + SpiderCavern: 811, + SwampyPitLvl1: 809, + SwampyPitLvl2: 808, + FlayerDungeonLvl1: 806, + FlayerDungeonLvl2: 805, + SwampyPitLvl3: 807, + FlayerDungeonLvl3: 804, + A3SewersLvl1: 845, + A3SewersLvl2: 844, + RuinedTemple: 803, + DisusedFane: 802, + ForgottenReliquary: 801, + ForgottenTemple: 800, + RuinedFane: 799, + DisusedReliquary: 798, + DuranceofHateLvl1: 797, + DuranceofHateLvl2: 796, + DuranceofHateLvl3: 795, - // Act 4 - PandemoniumFortress: 790, - OuterSteppes: 792, - PlainsofDespair: 793, - CityoftheDamned: 794, - RiverofFlame: 791, - ChaosSanctuary: 789, + // Act 4 + PandemoniumFortress: 790, + OuterSteppes: 792, + PlainsofDespair: 793, + CityoftheDamned: 794, + RiverofFlame: 791, + ChaosSanctuary: 789, - // Act 5 - Harrogath: 22646, - BloodyFoothills: 22647, - FrigidHighlands: 22648, - ArreatPlateau: 22649, - CrystalizedPassage: 22650, - FrozenRiver: 22651, - GlacialTrail: 22652, - DrifterCavern: 22653, - FrozenTundra: 22654, - AncientsWay: 22655, - IcyCellar: 22656, - ArreatSummit: 22657, - NihlathaksTemple: 22658, - HallsofAnguish: 22659, - HallsofPain: 22660, - HallsofVaught: 22662, - Abaddon: 21865, - PitofAcheron: 21866, - InfernalPit: 21867, - WorldstoneLvl1: 22663, - WorldstoneLvl2: 22664, - WorldstoneLvl3: 22665, - ThroneofDestruction: 22667, - WorldstoneChamber: 22666, + // Act 5 + Harrogath: 22646, + BloodyFoothills: 22647, + FrigidHighlands: 22648, + ArreatPlateau: 22649, + CrystalizedPassage: 22650, + FrozenRiver: 22651, + GlacialTrail: 22652, + DrifterCavern: 22653, + FrozenTundra: 22654, + AncientsWay: 22655, + IcyCellar: 22656, + ArreatSummit: 22657, + NihlathaksTemple: 22658, + HallsofAnguish: 22659, + HallsofPain: 22660, + HallsofVaught: 22662, + Abaddon: 21865, + PitofAcheron: 21866, + InfernalPit: 21867, + WorldstoneLvl1: 22663, + WorldstoneLvl2: 22664, + WorldstoneLvl3: 22665, + ThroneofDestruction: 22667, + WorldstoneChamber: 22666, - // Ubers - MatronsDen: 5389, - ForgottenSands: 5389, - FurnaceofPain: 5389, - UberTristram: 5018, - }, - }, + // Ubers + MatronsDen: 5389, + ForgottenSands: 5389, + FurnaceofPain: 5389, + UberTristram: 5018, + }, + }, - game: { - profiletype: { - SinglePlayer: 1, - Battlenet: 2, - OpenBattlenet: 3, - TcpIpHost: 4, - TcpIpJoin: 5 - }, + game: { + profiletype: { + SinglePlayer: 1, + Battlenet: 2, + OpenBattlenet: 3, + TcpIpHost: 4, + TcpIpJoin: 5 + }, - controls: { - Disabled: 4, - }, + controls: { + Disabled: 4, + }, - gametype: { - Classic: 0, - Expansion: 1, - }, + gametype: { + Classic: 0, + Expansion: 1, + }, - // out of game locations - locations: { - PreSplash: 0, - Lobby: 1, - WaitingInLine: 2, - LobbyChat: 3, - CreateGame: 4, - JoinGame: 5, - Ladder: 6, - ChannelList: 7, - MainMenu: 8, - Login: 9, - LoginError: 10, - LoginUnableToConnect: 11, - CharSelect: 12, - RealmDown: 13, - Disconnected: 14, - NewCharSelected: 15, - CharSelectPleaseWait: 16, - LobbyLostConnection: 17, - SplashScreen: 18, - CdKeyInUse: 19, - SelectDifficultySP: 20, - MainMenuConnecting: 21, - InvalidCdKey: 22, - CharSelectConnecting: 23, - ServerDown: 24, - LobbyPleaseWait: 25, - GameNameExists: 26, - GatewaySelect: 27, - GameDoesNotExist: 28, - CharacterCreate: 29, - OkCenteredErrorPopUp: 30, - TermsOfUse: 31, - CreateNewAccount: 32, - PleaseRead: 33, - RegisterEmail: 34, - Credits: 35, - Cinematics: 36, - CharChangeRealm: 37, - GameIsFull: 38, - OtherMultiplayer: 39, - TcpIp: 40, - TcpIpEnterIp: 41, - CharSelectNoChars: 42, - CharSelectChangeRealm: 43, - TcpIpUnableToConnect: 44, - }, - }, + // out of game locations + locations: { + PreSplash: 0, + Lobby: 1, + WaitingInLine: 2, + LobbyChat: 3, + CreateGame: 4, + JoinGame: 5, + Ladder: 6, + ChannelList: 7, + MainMenu: 8, + Login: 9, + LoginError: 10, + LoginUnableToConnect: 11, + CharSelect: 12, + RealmDown: 13, + Disconnected: 14, + NewCharSelected: 15, + CharSelectPleaseWait: 16, + LobbyLostConnection: 17, + SplashScreen: 18, + CdKeyInUse: 19, + SelectDifficultySP: 20, + MainMenuConnecting: 21, + InvalidCdKey: 22, + CharSelectConnecting: 23, + ServerDown: 24, + LobbyPleaseWait: 25, + GameNameExists: 26, + GatewaySelect: 27, + GameDoesNotExist: 28, + CharacterCreate: 29, + OkCenteredErrorPopUp: 30, + TermsOfUse: 31, + CreateNewAccount: 32, + PleaseRead: 33, + RegisterEmail: 34, + Credits: 35, + Cinematics: 36, + CharChangeRealm: 37, + GameIsFull: 38, + OtherMultiplayer: 39, + TcpIp: 40, + TcpIpEnterIp: 41, + CharSelectNoChars: 42, + CharSelectChangeRealm: 43, + TcpIpUnableToConnect: 44, + }, + }, - colors: { - White: "ÿc0", - Red: "ÿc1", - NeonGreen: "ÿc2", - Blue: "ÿc3", - DarkGold: "ÿc4", - Gray: "ÿc5", - Black: "ÿc6", - LightGold: "ÿc7", - Orange: "ÿc8", - Yellow: "ÿc9", - DarkGreen: "ÿc:", - Purple: "ÿc;", - Green: "ÿc<", - D2Bot: { - Black: 0, - Blue: 4, - Green: 5, - Gold: 6, - DarkGold: 7, - Orange: 8, - Red: 9, - Gray: 10 - } - }, + colors: { + White: "ÿc0", + Red: "ÿc1", + NeonGreen: "ÿc2", + Blue: "ÿc3", + DarkGold: "ÿc4", + Gray: "ÿc5", + Black: "ÿc6", + LightGold: "ÿc7", + Orange: "ÿc8", + Yellow: "ÿc9", + DarkGreen: "ÿc:", + Purple: "ÿc;", + Green: "ÿc<", + D2Bot: { + Black: 0, + Blue: 4, + Green: 5, + Gold: 6, + DarkGold: 7, + Orange: 8, + Red: 9, + Gray: 10 + } + }, - keys: { - Backspace: 8, - Tab: 9, - Enter: 13, - Shift: 16, - Ctrl: 17, - Alt: 18, - PauseBreak: 19, - CapsLock: 20, - Escape: 27, - Spacebar: 32, - PageUp: 33, - PageDown: 34, - End: 35, - Home: 36, - LeftArrow: 37, - UpArrow: 38, - RightArrow: 39, - DownArrow: 40, - Insert: 45, - Delete: 46, - Zero: 48, - One: 49, - Two: 50, - Three: 51, - Four: 52, - Five: 53, - Six: 54, - Seven: 55, - Eight: 56, - Nine: 57, - LeftWindowKey: 91, - RightWindowKey: 92, - SelectKey: 93, - Numpad0: 96, - Numpad1: 97, - Numpad2: 98, - Numpad3: 99, - Numpad4: 100, - Numpad5: 101, - Numpad6: 102, - Numpad7: 103, - Numpad8: 104, - Numpad9: 105, - NumpadStar: 106, - NumpadPlus: 107, - NumpadDash: 109, - NumpadDecimal: 110, - NumpadSlash: 111, - F1: 112, - F2: 113, - F3: 114, - F4: 115, - F5: 116, - F6: 117, - F7: 118, - F8: 119, - F9: 120, - F10: 121, - F11: 122, - F12: 123, - NumLock: 144, - ScrollLock: 145, - SemiColon: 186, - EqualSign: 187, - Comma: 188, - Dash: 189, - Period: 190, - ForwardSlash: 191, - GraveAccent: 192, - OpenBracket: 219, - BackSlash: 220, - CloseBracket: 221, - SingleQuote: 222, - code: { - Backspace: 0x08, - Tab: 0x09, - Clear: 0x0C, - Enter: 0x0D, - Shift: 0x10, - Ctrl: 0x11, - Alt: 0x12, - PauseBreak: 0x13, - CapsLock: 0x14, - Esc: 0x1B, - Space: 0x20, - PageUp: 0x21, - PageDown: 0x22, - End: 0x23, - Home: 0x24, - LeftArrow: 0x25, - UpArrow: 0x26, - RightArrow: 0x27, - DownArrow: 0x28, - Select: 0x29, - Print: 0x2A, - PrintScreen: 0x2C, - Insert: 0x2D, - Delete: 0x2E, - } - }, + keys: { + Backspace: 8, + Tab: 9, + Enter: 13, + Shift: 16, + Ctrl: 17, + Alt: 18, + PauseBreak: 19, + CapsLock: 20, + Escape: 27, + Spacebar: 32, + PageUp: 33, + PageDown: 34, + End: 35, + Home: 36, + LeftArrow: 37, + UpArrow: 38, + RightArrow: 39, + DownArrow: 40, + Insert: 45, + Delete: 46, + Zero: 48, + One: 49, + Two: 50, + Three: 51, + Four: 52, + Five: 53, + Six: 54, + Seven: 55, + Eight: 56, + Nine: 57, + LeftWindowKey: 91, + RightWindowKey: 92, + SelectKey: 93, + Numpad0: 96, + Numpad1: 97, + Numpad2: 98, + Numpad3: 99, + Numpad4: 100, + Numpad5: 101, + Numpad6: 102, + Numpad7: 103, + Numpad8: 104, + Numpad9: 105, + NumpadStar: 106, + NumpadPlus: 107, + NumpadDash: 109, + NumpadDecimal: 110, + NumpadSlash: 111, + F1: 112, + F2: 113, + F3: 114, + F4: 115, + F5: 116, + F6: 117, + F7: 118, + F8: 119, + F9: 120, + F10: 121, + F11: 122, + F12: 123, + NumLock: 144, + ScrollLock: 145, + SemiColon: 186, + EqualSign: 187, + Comma: 188, + Dash: 189, + Period: 190, + ForwardSlash: 191, + GraveAccent: 192, + OpenBracket: 219, + BackSlash: 220, + CloseBracket: 221, + SingleQuote: 222, + code: { + Backspace: 0x08, + Tab: 0x09, + Clear: 0x0C, + Enter: 0x0D, + Shift: 0x10, + Ctrl: 0x11, + Alt: 0x12, + PauseBreak: 0x13, + CapsLock: 0x14, + Esc: 0x1B, + Space: 0x20, + PageUp: 0x21, + PageDown: 0x22, + End: 0x23, + Home: 0x24, + LeftArrow: 0x25, + UpArrow: 0x26, + RightArrow: 0x27, + DownArrow: 0x28, + Select: 0x29, + Print: 0x2A, + PrintScreen: 0x2C, + Insert: 0x2D, + Delete: 0x2E, + } + }, - controls: { - TextBox: 1, - Image1: 2, - Image2: 3, - LabelBox: 4, - ScrollBar: 5, - Button: 6, - List: 7, - Timer: 8, - Smack: 9, - ProgressBar: 10, - Popup: 11, - AccountList: 12 - }, + controls: { + TextBox: 1, + Image1: 2, + Image2: 3, + LabelBox: 4, + ScrollBar: 5, + Button: 6, + List: 7, + Timer: 8, + Smack: 9, + ProgressBar: 10, + Popup: 11, + AccountList: 12 + }, - packets: { - send: { - WalkToLocation: 0x01, - WalkToEntity: 0x02, - RunToLocation: 0x03, - RunToEntity: 0x04, - LeftSkillOnLocation: 0x05, - LeftSkillOnEntity: 0x06, - LeftSkillOnEntityEx: 0x07, - LeftSkillOnLocationEx: 0x08, - LeftSkillOnEntityEx2: 0x09, - LeftSkillOnEntityEx3: 0x0A, - RightSkillOnLocation: 0x0C, - RightSkillOnEntity: 0x0D, - RightSkillOnEntityEx: 0x0E, - RightSkillOnLocationEx: 0x0F, - RightSkillOnEntityEx2: 0x10, - RightSkillOnEntityEx3: 0x11, - SetInfernoState: 0x12, - InteractWithEntity: 0x13, - OverheadMessage: 0x14, - Chat: 0x15, - PickupItem: 0x16, - DropItem: 0x17, - ItemToBuffer: 0x18, - PickupBufferItem: 0x19, - ItemToBody: 0x1A, - Swap2HandedItem: 0x1B, - PickupBodyItem: 0x1C, - SwitchBodyItem: 0x1D, - Switch1HandWith2Hand: 0x1E, - SwitchInventoryItem: 0x1F, - UseItem: 0x20, - StackItem: 0x21, - RemoveStackItem: 0x22, - ItemToBelt: 0x23, - RemoveBeltItem: 0x24, - SwitchBeltItem: 0x25, - UseBeltItem: 0x26, - IndentifyItem: 0x27, - InsertSocketItem: 0x28, - ScrollToMe: 0x29, - ItemToCube: 0x2A, - NPCInit: 0x2F, - NPCCancel: 0x30, - QuestMessage: 0x31, - NPCBuy: 0x32, - NPCSell: 0x33, - NPCIndentifyItems: 0x34, - Repair: 0x35, - HireMerc: 0x36, - IndentifyGamble: 0x37, - EntityAction: 0x38, - AddStat: 0x3A, - AddSkill: 0x3B, - SelectSkill: 0x3C, - ActivateItem: 0x3E, - CharacterPhrase: 0x3F, - UpdateQuests: 0x40, - Resurrect: 0x41, - StaffInOrifice: 0x44, - MercInteract: 0x46, - MercMove: 0x47, - BusyStateOff: 0x48, - Waypoint: 0x49, - RequestEntityUpdate: 0x4B, - Transmorgify: 0x4C, - PlayNPCMessage: 0x4D, - ClickButton: 0x4F, - DropGold: 0x50, - BindHotkey: 0x51, - StaminaOn: 0x53, - StaminaOff: 0x54, - QuestCompleted: 0x58, - MakeEntityMove: 0x59, - SquelchHostile: 0x5D, - Party: 0x5E, - UpdatePlayerPos: 0x5F, - SwapWeapon: 0x60, - MercItem: 0x61, - MercRessurect: 0x62, - LeaveGame: 0x69, - }, - recv: { - GameExit: 0x06, - MapReveal: 0x07, - MapHide: 0x08, - ReassignPlayer: 0x15, - SetSkill: 0x23, - Chat: 0x26, - UniqueEvents: 0x89, - WeaponSwitch: 0x97, - } - } - }; + packets: { + send: { + WalkToLocation: 0x01, + WalkToEntity: 0x02, + RunToLocation: 0x03, + RunToEntity: 0x04, + LeftSkillOnLocation: 0x05, + LeftSkillOnEntity: 0x06, + LeftSkillOnEntityEx: 0x07, + LeftSkillOnLocationEx: 0x08, + LeftSkillOnEntityEx2: 0x09, + LeftSkillOnEntityEx3: 0x0A, + RightSkillOnLocation: 0x0C, + RightSkillOnEntity: 0x0D, + RightSkillOnEntityEx: 0x0E, + RightSkillOnLocationEx: 0x0F, + RightSkillOnEntityEx2: 0x10, + RightSkillOnEntityEx3: 0x11, + SetInfernoState: 0x12, + InteractWithEntity: 0x13, + OverheadMessage: 0x14, + Chat: 0x15, + PickupItem: 0x16, + DropItem: 0x17, + ItemToBuffer: 0x18, + PickupBufferItem: 0x19, + ItemToBody: 0x1A, + Swap2HandedItem: 0x1B, + PickupBodyItem: 0x1C, + SwitchBodyItem: 0x1D, + Switch1HandWith2Hand: 0x1E, + SwitchInventoryItem: 0x1F, + UseItem: 0x20, + StackItem: 0x21, + RemoveStackItem: 0x22, + ItemToBelt: 0x23, + RemoveBeltItem: 0x24, + SwitchBeltItem: 0x25, + UseBeltItem: 0x26, + IndentifyItem: 0x27, + InsertSocketItem: 0x28, + ScrollToMe: 0x29, + ItemToCube: 0x2A, + NPCInit: 0x2F, + NPCCancel: 0x30, + QuestMessage: 0x31, + NPCBuy: 0x32, + NPCSell: 0x33, + NPCIndentifyItems: 0x34, + Repair: 0x35, + HireMerc: 0x36, + IndentifyGamble: 0x37, + EntityAction: 0x38, + AddStat: 0x3A, + AddSkill: 0x3B, + SelectSkill: 0x3C, + ActivateItem: 0x3E, + CharacterPhrase: 0x3F, + UpdateQuests: 0x40, + Resurrect: 0x41, + StaffInOrifice: 0x44, + MercInteract: 0x46, + MercMove: 0x47, + BusyStateOff: 0x48, + Waypoint: 0x49, + RequestEntityUpdate: 0x4B, + Transmorgify: 0x4C, + PlayNPCMessage: 0x4D, + ClickButton: 0x4F, + DropGold: 0x50, + BindHotkey: 0x51, + StaminaOn: 0x53, + StaminaOff: 0x54, + QuestCompleted: 0x58, + MakeEntityMove: 0x59, + SquelchHostile: 0x5D, + Party: 0x5E, + UpdatePlayerPos: 0x5F, + SwapWeapon: 0x60, + MercItem: 0x61, + MercRessurect: 0x62, + LeaveGame: 0x69, + }, + recv: { + GameExit: 0x06, + MapReveal: 0x07, + MapHide: 0x08, + ReassignPlayer: 0x15, + SetSkill: 0x23, + Chat: 0x26, + UniqueEvents: 0x89, + WeaponSwitch: 0x97, + } + } + }; - // Need to be set after its loaded - sdk.skillTabs = { - amazon: { - bowandcrossbow: { - id: 0, - skills: [sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.MultipleShot, sdk.skills.ExplodingArrow, sdk.skills.IceArrow, sdk.skills.GuidedArrow, sdk.skills.ImmolationArrow, sdk.skills.Strafe], - }, - passiveandmagic: { - id: 1, - skills: [sdk.skills.InnerSight, sdk.skills.CriticalStrike, sdk.skills.Dodge, sdk.skills.SlowMissiles, sdk.skills.Avoid, sdk.skills.Penetrate, sdk.skills.Dopplezon, sdk.skills.Evade, sdk.skills.Valkyrie, sdk.skills.Pierce], - }, - javelinandspear: { - id: 2, - skills: [sdk.skills.Jab, sdk.skills.PowerStrike, sdk.skills.PoisonJavelin, sdk.skills.Impale, sdk.skills.LightningBolt, sdk.skills.ChargedStrike, sdk.skills.PlagueJavelin, sdk.skills.Fend, sdk.skills.LightningStrike, sdk.skills.LightningFury], - }, - }, - sorc: { - fire: { - id: 8, - skills: [sdk.skills.FireBolt, sdk.skills.Warmth, sdk.skills.Inferno, sdk.skills.Blaze, sdk.skills.FireBall, sdk.skills.FireWall, sdk.skills.Enchant, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.Hydra], - }, - lightning: { - id: 9, - skills: [sdk.skills.ChargedBolt, sdk.skills.StaticField, sdk.skills.Telekinesis, sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, sdk.skills.Teleport, sdk.skills.ThunderStorm, sdk.skills.EnergyShield, sdk.skills.LightningMastery], - }, - cold: { - id: 10, - skills: [sdk.skills.IceBolt, sdk.skills.FrozenArmor, sdk.skills.FrostNova, sdk.skills.IceBlast, sdk.skills.ShiverArmor, sdk.skills.GlacialSpike, sdk.skills.Blizzard, sdk.skills.ChillingArmor, sdk.skills.FrozenOrb, sdk.skills.ColdMastery] - } - }, - necro: { - curse: { - id: 16, - skills: [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], - }, - poisonandbone: { - id: 17, - skills: [sdk.skills.Teeth, sdk.skills.BoneArmor, sdk.skills.PoisonDagger, sdk.skills.CorpseExplosion, sdk.skills.BoneWall, sdk.skills.PoisonExplosion, sdk.skills.BoneSpear, sdk.skills.BonePrison, sdk.skills.PoisonNova, sdk.skills.BoneSpirit], - }, - summoning: { - id: 18, - skills: [sdk.skills.SkeletonMastery, sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, sdk.skills.GolemMastery, sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, sdk.skills.SummonResist, sdk.skills.IronGolem, sdk.skills.FireGolem, sdk.skills.Revive] - } - }, - paladin: { - combat: { - id: 24, - skills: [sdk.skills.Sacrifice, sdk.skills.Smite, sdk.skills.HolyBolt, sdk.skills.Zeal, sdk.skills.Charge, sdk.skills.Vengeance, sdk.skills.BlessedHammer, sdk.skills.Conversion, sdk.skills.HolyShield, sdk.skills.FistoftheHeavens], - }, - offensiveaura: { - id: 25, - skills: [sdk.skills.Might, sdk.skills.HolyFire, sdk.skills.Thorns, sdk.skills.BlessedAim, sdk.skills.Concentration, sdk.skills.HolyFreeze, sdk.skills.HolyShock, sdk.skills.Sanctuary, sdk.skills.Fanaticism, sdk.skills.Conviction], - }, - defensiveaura: { - id: 26, - skills: [sdk.skills.Prayer, sdk.skills.ResistFire, sdk.skills.Defiance, sdk.skills.ResistCold, sdk.skills.Cleansing, sdk.skills.ResistLightning, sdk.skills.Vigor, sdk.skills.Meditation, sdk.skills.Redemption, sdk.skills.Salvation], - } - }, - barb: { - combat: { - id: 32, - skills: [sdk.skills.Bash, sdk.skills.Leap, sdk.skills.DoubleSwing, sdk.skills.Stun, sdk.skills.DoubleThrow, sdk.skills.LeapAttack, sdk.skills.Concentrate, sdk.skills.Frenzy, sdk.skills.Whirlwind, sdk.skills.Berserk], - }, - masteries: { - id: 33, - skills: [sdk.skills.SwordMastery, sdk.skills.AxeMastery, sdk.skills.MaceMastery, sdk.skills.PoleArmMastery, sdk.skills.ThrowingMastery, sdk.skills.SpearMastery, sdk.skills.IncreasedStamina, sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.NaturalResistance], - }, - warcries: { - id: 34, - skills: [sdk.skills.BattleOrders, sdk.skills.BattleCommand, sdk.skills.WarCry, sdk.skills.BattleCry, sdk.skills.GrimWard, sdk.skills.FindItem, sdk.skills.Shout, sdk.skills.Taunt, sdk.skills.Howl, sdk.skills.FindPotion], - } - }, - druid: { - summoning: { - id: 40, - skills: [sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.OakSage, sdk.skills.SummonSpiritWolf, sdk.skills.CarrionVine, sdk.skills.HeartofWolverine, sdk.skills.SummonDireWolf, sdk.skills.SolarCreeper, sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly], - }, - shapeshifting: { - id: 41, - skills: [sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Werebear, sdk.skills.FeralRage, sdk.skills.Maul, sdk.skills.Rabies, sdk.skills.FireClaws, sdk.skills.Hunger, sdk.skills.ShockWave, sdk.skills.Fury], - }, - elemental: { - id: 42, - skills: [sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, sdk.skills.Fissure, sdk.skills.CycloneArmor, sdk.skills.Twister, sdk.skills.Volcano, sdk.skills.Tornado, sdk.skills.Armageddon, sdk.skills.Hurricane] - } - }, - assassin: { - traps: { - id: 48, - skills: [sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.BladeSentinel, sdk.skills.ChargedBoltSentry, sdk.skills.WakeofFire, sdk.skills.BladeFury, sdk.skills.LightningSentry, sdk.skills.WakeofInferno, sdk.skills.DeathSentry, sdk.skills.BladeShield], - }, - shadowdisciplines: { - id: 49, - skills: [sdk.skills.ClawMastery, sdk.skills.PsychicHammer, sdk.skills.BurstofSpeed, sdk.skills.WeaponBlock, sdk.skills.CloakofShadows, sdk.skills.Fade, sdk.skills.ShadowWarrior, sdk.skills.MindBlast, sdk.skills.Venom, sdk.skills.ShadowMaster], - }, - martialarts: { - id: 50, - skills: [sdk.skills.TigerStrike, sdk.skills.DragonTalon, sdk.skills.FistsofFire, sdk.skills.DragonClaw, sdk.skills.CobraStrike, sdk.skills.ClawsofThunder, sdk.skills.DragonTail, sdk.skills.BladesofIce, sdk.skills.DragonFlight, sdk.skills.PhoenixStrike], - } - }, - }; + // Need to be set after its loaded + sdk.skillTabs = { + amazon: { + bowandcrossbow: { + id: 0, + skills: [ + sdk.skills.MagicArrow, sdk.skills.FireArrow, sdk.skills.MultipleShot, + sdk.skills.ExplodingArrow, sdk.skills.IceArrow, sdk.skills.GuidedArrow, + sdk.skills.ImmolationArrow, sdk.skills.Strafe + ], + }, + passiveandmagic: { + id: 1, + skills: [ + sdk.skills.InnerSight, sdk.skills.CriticalStrike, sdk.skills.Dodge, + sdk.skills.SlowMissiles, sdk.skills.Avoid, sdk.skills.Penetrate, + sdk.skills.Dopplezon, sdk.skills.Evade, sdk.skills.Valkyrie, sdk.skills.Pierce + ], + }, + javelinandspear: { + id: 2, + skills: [ + sdk.skills.Jab, sdk.skills.PowerStrike, sdk.skills.PoisonJavelin, + sdk.skills.Impale, sdk.skills.LightningBolt, sdk.skills.ChargedStrike, + sdk.skills.PlagueJavelin, sdk.skills.Fend, sdk.skills.LightningStrike, sdk.skills.LightningFury + ], + }, + }, + sorc: { + fire: { + id: 8, + skills: [ + sdk.skills.FireBolt, sdk.skills.Warmth, sdk.skills.Inferno, + sdk.skills.Blaze, sdk.skills.FireBall, sdk.skills.FireWall, + sdk.skills.Enchant, sdk.skills.Meteor, sdk.skills.FireMastery, sdk.skills.Hydra + ], + }, + lightning: { + id: 9, + skills: [ + sdk.skills.ChargedBolt, sdk.skills.StaticField, sdk.skills.Telekinesis, + sdk.skills.Nova, sdk.skills.Lightning, sdk.skills.ChainLightning, + sdk.skills.Teleport, sdk.skills.ThunderStorm, sdk.skills.EnergyShield, + sdk.skills.LightningMastery + ], + }, + cold: { + id: 10, + skills: [ + sdk.skills.IceBolt, sdk.skills.FrozenArmor, sdk.skills.FrostNova, + sdk.skills.IceBlast, sdk.skills.ShiverArmor, sdk.skills.GlacialSpike, + sdk.skills.Blizzard, sdk.skills.ChillingArmor, + sdk.skills.FrozenOrb, sdk.skills.ColdMastery + ] + } + }, + necro: { + curse: { + id: 16, + skills: [ + 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 + ], + }, + poisonandbone: { + id: 17, + skills: [ + sdk.skills.Teeth, sdk.skills.BoneArmor, sdk.skills.PoisonDagger, + sdk.skills.CorpseExplosion, sdk.skills.BoneWall, sdk.skills.PoisonExplosion, + sdk.skills.BoneSpear, sdk.skills.BonePrison, + sdk.skills.PoisonNova, sdk.skills.BoneSpirit + ], + }, + summoning: { + id: 18, + skills: [ + sdk.skills.SkeletonMastery, sdk.skills.RaiseSkeleton, sdk.skills.ClayGolem, + sdk.skills.GolemMastery, sdk.skills.RaiseSkeletalMage, sdk.skills.BloodGolem, + sdk.skills.SummonResist, sdk.skills.IronGolem, + sdk.skills.FireGolem, sdk.skills.Revive + ] + } + }, + paladin: { + combat: { + id: 24, + skills: [ + sdk.skills.Sacrifice, sdk.skills.Smite, sdk.skills.HolyBolt, + sdk.skills.Zeal, sdk.skills.Charge, sdk.skills.Vengeance, + sdk.skills.BlessedHammer, sdk.skills.Conversion, + sdk.skills.HolyShield, sdk.skills.FistoftheHeavens + ], + }, + offensiveaura: { + id: 25, + skills: [ + sdk.skills.Might, sdk.skills.HolyFire, sdk.skills.Thorns, + sdk.skills.BlessedAim, sdk.skills.Concentration, sdk.skills.HolyFreeze, + sdk.skills.HolyShock, sdk.skills.Sanctuary, + sdk.skills.Fanaticism, sdk.skills.Conviction + ], + }, + defensiveaura: { + id: 26, + skills: [ + sdk.skills.Prayer, sdk.skills.ResistFire, sdk.skills.Defiance, + sdk.skills.ResistCold, sdk.skills.Cleansing, sdk.skills.ResistLightning, + sdk.skills.Vigor, sdk.skills.Meditation, + sdk.skills.Redemption, sdk.skills.Salvation + ], + } + }, + barb: { + combat: { + id: 32, + skills: [ + sdk.skills.Bash, sdk.skills.Leap, sdk.skills.DoubleSwing, + sdk.skills.Stun, sdk.skills.DoubleThrow, sdk.skills.LeapAttack, + sdk.skills.Concentrate, sdk.skills.Frenzy, + sdk.skills.Whirlwind, sdk.skills.Berserk + ], + }, + masteries: { + id: 33, + skills: [ + sdk.skills.SwordMastery, sdk.skills.AxeMastery, sdk.skills.MaceMastery, sdk.skills.PoleArmMastery, + sdk.skills.ThrowingMastery, sdk.skills.SpearMastery, sdk.skills.IncreasedStamina, + sdk.skills.IronSkin, sdk.skills.IncreasedSpeed, sdk.skills.NaturalResistance + ], + }, + warcries: { + id: 34, + skills: [ + sdk.skills.BattleOrders, sdk.skills.BattleCommand, sdk.skills.WarCry, sdk.skills.BattleCry, + sdk.skills.GrimWard, sdk.skills.FindItem, sdk.skills.Shout, + sdk.skills.Taunt, sdk.skills.Howl, sdk.skills.FindPotion + ], + } + }, + druid: { + summoning: { + id: 40, + skills: [ + sdk.skills.Raven, sdk.skills.PoisonCreeper, sdk.skills.OakSage, + sdk.skills.SummonSpiritWolf, sdk.skills.CarrionVine, sdk.skills.HeartofWolverine, + sdk.skills.SummonDireWolf, sdk.skills.SolarCreeper, sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly + ], + }, + shapeshifting: { + id: 41, + skills: [ + sdk.skills.Werewolf, sdk.skills.Lycanthropy, sdk.skills.Werebear, sdk.skills.FeralRage, + sdk.skills.Maul, sdk.skills.Rabies, sdk.skills.FireClaws, + sdk.skills.Hunger, sdk.skills.ShockWave, sdk.skills.Fury + ], + }, + elemental: { + id: 42, + skills: [ + sdk.skills.Firestorm, sdk.skills.MoltenBoulder, sdk.skills.ArcticBlast, + sdk.skills.Fissure, sdk.skills.CycloneArmor, sdk.skills.Twister, + sdk.skills.Volcano, sdk.skills.Tornado, sdk.skills.Armageddon, sdk.skills.Hurricane + ] + } + }, + assassin: { + traps: { + id: 48, + skills: [ + sdk.skills.FireBlast, sdk.skills.ShockWeb, sdk.skills.BladeSentinel, + sdk.skills.ChargedBoltSentry, sdk.skills.WakeofFire, sdk.skills.BladeFury, + sdk.skills.LightningSentry, sdk.skills.WakeofInferno, sdk.skills.DeathSentry, sdk.skills.BladeShield + ], + }, + shadowdisciplines: { + id: 49, + skills: [ + sdk.skills.ClawMastery, sdk.skills.PsychicHammer, sdk.skills.BurstofSpeed, + sdk.skills.WeaponBlock, sdk.skills.CloakofShadows, sdk.skills.Fade, + sdk.skills.ShadowWarrior, sdk.skills.MindBlast, sdk.skills.Venom, sdk.skills.ShadowMaster + ], + }, + martialarts: { + id: 50, + skills: [ + sdk.skills.TigerStrike, sdk.skills.DragonTalon, sdk.skills.FistsofFire, + sdk.skills.DragonClaw, sdk.skills.CobraStrike, sdk.skills.ClawsofThunder, + sdk.skills.DragonTail, sdk.skills.BladesofIce, sdk.skills.DragonFlight, sdk.skills.PhoenixStrike + ], + } + }, + }; - return sdk; + return sdk; })); diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index 607c5b25d..297f4ed12 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -9,153 +9,153 @@ includeIfNotIncluded("oog/DataFile.js"); (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(["require", "../modules/CopyData"], factory); - } else { - root.D2Bot = 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(["require", "../modules/CopyData"], factory); + } else { + root.D2Bot = factory(); + } }([].filter.constructor("return this")(), function() { - const CopyData = require("../modules/CopyData"); + const CopyData = require("../modules/CopyData"); - const D2Bot = { - handle: 0, + const D2Bot = { + handle: 0, - init: function () { - let handle = DataFile.getStats().handle; + init: function () { + let handle = DataFile.getStats().handle; - if (handle) { - D2Bot.handle = handle; - } + if (handle) { + D2Bot.handle = handle; + } - return D2Bot.handle; - }, + return D2Bot.handle; + }, - sendMessage: function (handle, mode, msg) { - sendCopyData(null, handle, mode, msg); - }, + sendMessage: function (handle, mode, msg) { + sendCopyData(null, handle, mode, msg); + }, - printToConsole: function (msg, color, tooltip, trigger) { - let printObj = { - msg: ((new Date().dateStamp() + " ") + msg), - color: color || 0, - tooltip: tooltip || "", - trigger: trigger || "" - }; + printToConsole: function (msg, color, tooltip, trigger) { + let printObj = { + msg: ((new Date().dateStamp() + " ") + msg), + color: color || 0, + tooltip: tooltip || "", + trigger: trigger || "" + }; - new CopyData().data("printToConsole", [printObj]).send(); - }, + new CopyData().data("printToConsole", [printObj]).send(); + }, - printToItemLog: function (itemObj) { - new CopyData().data("printToItemLog", [itemObj]).send(); - }, + printToItemLog: function (itemObj) { + new CopyData().data("printToItemLog", [itemObj]).send(); + }, - uploadItem: function (itemObj) { - new CopyData().data("uploadItem", [itemObj]).send(); - }, + uploadItem: function (itemObj) { + new CopyData().data("uploadItem", [itemObj]).send(); + }, - writeToFile: function (filename, msg) { - new CopyData().data("writeToFile", [filename, msg]).send(); - }, + writeToFile: function (filename, msg) { + new CopyData().data("writeToFile", [filename, msg]).send(); + }, - postToIRC: function (ircProfile, recepient, msg) { - new CopyData().data("postToIRC", [ircProfile, recepient, msg]).send(); - }, + postToIRC: function (ircProfile, recepient, msg) { + new CopyData().data("postToIRC", [ircProfile, recepient, msg]).send(); + }, - ircEvent: function (mode) { - new CopyData().data("ircEvent", [mode ? "true" : "false"]).send(); - }, + ircEvent: function (mode) { + new CopyData().data("ircEvent", [mode ? "true" : "false"]).send(); + }, - notify: function (msg) { - new CopyData().data("notify", [msg]).send(); - }, + notify: function (msg) { + new CopyData().data("notify", [msg]).send(); + }, - saveItem: function (itemObj) { - new CopyData().data("saveItem", [itemObj]).send(); - }, + saveItem: function (itemObj) { + new CopyData().data("saveItem", [itemObj]).send(); + }, - updateStatus: function (msg) { - new CopyData().data("updateStatus", [msg]).send(); - }, + updateStatus: function (msg) { + new CopyData().data("updateStatus", [msg]).send(); + }, - updateRuns: function () { - new CopyData().data("updateRuns", []).send(); - }, + updateRuns: function () { + new CopyData().data("updateRuns", []).send(); + }, - updateChickens: function () { - new CopyData().data("updateChickens", []).send(); - }, + updateChickens: function () { + new CopyData().data("updateChickens", []).send(); + }, - updateDeaths: function () { - new CopyData().data("updateDeaths", []).send(); - }, + updateDeaths: function () { + new CopyData().data("updateDeaths", []).send(); + }, - requestGameInfo: function () { - new CopyData().data("requestGameInfo", []).send(); - }, + requestGameInfo: function () { + new CopyData().data("requestGameInfo", []).send(); + }, - restart: function (keySwap) { - new CopyData().data( - "restartProfile", - arguments.length > 0 ? [me.profile, keySwap] : [me.profile] - ).send(); - }, + restart: function (keySwap) { + new CopyData().data( + "restartProfile", + arguments.length > 0 ? [me.profile, keySwap] : [me.profile] + ).send(); + }, - CDKeyInUse: function () { - new CopyData().data("CDKeyInUse", []).send(); - }, + CDKeyInUse: function () { + new CopyData().data("CDKeyInUse", []).send(); + }, - CDKeyDisabled: function () { - new CopyData().data("CDKeyDisabled", []).send(); - }, + CDKeyDisabled: function () { + new CopyData().data("CDKeyDisabled", []).send(); + }, - CDKeyRD: function () { - new CopyData().data("CDKeyRD", []).send(); - }, + CDKeyRD: function () { + new CopyData().data("CDKeyRD", []).send(); + }, - stop: function (profile, release) { - !profile && (profile = me.profile); + stop: function (profile, release) { + !profile && (profile = me.profile); - new CopyData().data("stop", [profile, release ? "True" : "False"]).send(); - }, - - start: function (profile) { - new CopyData().data("start", [profile]).send(); - }, - - startSchedule: function (profile) { - new CopyData().data("startSchedule", [profile]).send(); - }, - - stopSchedule: function (profile) { - new CopyData().data("stopSchedule", [profile]).send(); - }, - - updateCount: function () { - new CopyData().data("updateCount", ["1"]).send(); - }, - - shoutGlobal: function (msg, mode) { - new CopyData().data("shoutGlobal", [msg, mode]).send(); - }, - - heartBeat: function () { - new CopyData().mode(0xbbbb).data("heartBeat", []).send(); - }, + new CopyData().data("stop", [profile, release ? "True" : "False"]).send(); + }, + + start: function (profile) { + new CopyData().data("start", [profile]).send(); + }, + + startSchedule: function (profile) { + new CopyData().data("startSchedule", [profile]).send(); + }, + + stopSchedule: function (profile) { + new CopyData().data("stopSchedule", [profile]).send(); + }, + + updateCount: function () { + new CopyData().data("updateCount", ["1"]).send(); + }, + + shoutGlobal: function (msg, mode) { + new CopyData().data("shoutGlobal", [msg, mode]).send(); + }, + + heartBeat: function () { + new CopyData().mode(0xbbbb).data("heartBeat", []).send(); + }, - sendWinMsg: function (wparam, lparam) { - new CopyData().data("winmsg", [wparam, lparam]).send(); - }, - - ingame: function () { - this.sendWinMsg(0x0086, 0x0000); - this.sendWinMsg(0x0006, 0x0002); - this.sendWinMsg(0x001c, 0x0000); - }, + sendWinMsg: function (wparam, lparam) { + new CopyData().data("winmsg", [wparam, lparam]).send(); + }, + + ingame: function () { + this.sendWinMsg(0x0086, 0x0000); + this.sendWinMsg(0x0006, 0x0002); + this.sendWinMsg(0x001c, 0x0000); + }, - /** + /** * Profile to profile communication * @param {string} profile * @param {string} gameName @@ -164,53 +164,53 @@ includeIfNotIncluded("oog/DataFile.js"); * @param {string} isUp * @param {number} delay */ - joinMe: function (profile, gameName, gameCount, gamePass, isUp, delay) { - let obj = { - gameName: (gameName + gameCount).toLowerCase(), - gamePass: (gamePass).toLowerCase(), - inGame: isUp === "yes", - delay: (delay || 0), - }; - - sendCopyData(null, profile, 1, JSON.stringify(obj)); - }, - - requestGame: function (profile) { - new CopyData().handle(profile).mode(3).send(); - }, - - getProfile: function () { - new CopyData().data("getProfile", []).send(); - }, - - setProfile: function (account, password, character, difficulty, realm, infoTag, gamePath) { - new CopyData().data( - "setProfile", - [account, password, character, difficulty, realm, infoTag, gamePath] - ).send(); - }, - - setTag: function (tag) { - new CopyData().data("setTag", [tag]).send(); - }, - - // Store info in d2bot# cache - store: function (info) { - this.remove(); - - new CopyData().data("store", [me.profile, info]).send(); - }, - - // Get info from d2bot# cache - retrieve: function () { - new CopyData().data("retrieve", [me.profile]).send(); - }, - - // Delete info from d2bot# cache - remove: function () { - new CopyData().data("delete", [me.profile]).send(); - } - }; - - return D2Bot; + joinMe: function (profile, gameName, gameCount, gamePass, isUp, delay) { + let obj = { + gameName: (gameName + gameCount).toLowerCase(), + gamePass: (gamePass).toLowerCase(), + inGame: isUp === "yes", + delay: (delay || 0), + }; + + sendCopyData(null, profile, 1, JSON.stringify(obj)); + }, + + requestGame: function (profile) { + new CopyData().handle(profile).mode(3).send(); + }, + + getProfile: function () { + new CopyData().data("getProfile", []).send(); + }, + + setProfile: function (account, password, character, difficulty, realm, infoTag, gamePath) { + new CopyData().data( + "setProfile", + [account, password, character, difficulty, realm, infoTag, gamePath] + ).send(); + }, + + setTag: function (tag) { + new CopyData().data("setTag", [tag]).send(); + }, + + // Store info in d2bot# cache + store: function (info) { + this.remove(); + + new CopyData().data("store", [me.profile, info]).send(); + }, + + // Get info from d2bot# cache + retrieve: function () { + new CopyData().data("retrieve", [me.profile]).send(); + }, + + // Delete info from d2bot# cache + remove: function () { + new CopyData().data("delete", [me.profile]).send(); + } + }; + + return D2Bot; })); diff --git a/d2bs/kolbot/libs/oog/DataFile.js b/d2bs/kolbot/libs/oog/DataFile.js index 8b68a2f2c..eec8a2eca 100644 --- a/d2bs/kolbot/libs/oog/DataFile.js +++ b/d2bs/kolbot/libs/oog/DataFile.js @@ -9,123 +9,123 @@ includeIfNotIncluded("oog/FileAction.js"); (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.DataFile = 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.DataFile = factory(); + } }(this, function() { - const DataFile = { - _default: { - runs: 0, - experience: 0, - deaths: 0, - lastArea: "", - gold: 0, - level: 0, - name: "", - gameName: "", - ingameTick: 0, - handle: 0, - nextGame: "" - }, - - create: function () { - FileAction.write("data/" + me.profile + ".json", JSON.stringify(this._default)); - - return this._default; - }, - - getObj: function () { - !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); + const DataFile = { + _default: { + runs: 0, + experience: 0, + deaths: 0, + lastArea: "", + gold: 0, + level: 0, + name: "", + gameName: "", + ingameTick: 0, + handle: 0, + nextGame: "" + }, + + create: function () { + FileAction.write("data/" + me.profile + ".json", JSON.stringify(this._default)); + + return this._default; + }, + + getObj: function () { + !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); - let obj; - let string = FileAction.read("data/" + me.profile + ".json"); + let obj; + let string = FileAction.read("data/" + me.profile + ".json"); - try { - obj = JSON.parse(string); - } catch (e) { - // If we failed, file might be corrupted, so create a new one - obj = this.create(); - obj.handle = D2Bot.handle; - } + try { + obj = JSON.parse(string); + } catch (e) { + // If we failed, file might be corrupted, so create a new one + obj = this.create(); + obj.handle = D2Bot.handle; + } - if (obj) { - return obj; - } + if (obj) { + return obj; + } - console.warn("Error reading DataFile. Using null values."); + console.warn("Error reading DataFile. Using null values."); - return this._default; - }, + return this._default; + }, - getStats: function () { - let obj = this.getObj(); - return clone(obj); - }, + getStats: function () { + let obj = this.getObj(); + return clone(obj); + }, - updateStats: function (arg, value) { - while (me.ingame && !me.gameReady) { - delay(100); - } + updateStats: function (arg, value) { + while (me.ingame && !me.gameReady) { + delay(100); + } - let statArr = []; + let statArr = []; - typeof arg === "object" && (statArr = arg.slice()); - typeof arg === "string" && statArr.push(arg); + typeof arg === "object" && (statArr = arg.slice()); + typeof arg === "string" && statArr.push(arg); - let obj = this.getObj(); + let obj = this.getObj(); - for (let i = 0; i < statArr.length; i += 1) { - switch (statArr[i]) { - case "experience": - obj.experience = me.getStat(sdk.stats.Experience); - obj.level = me.getStat(sdk.stats.Level); + for (let i = 0; i < statArr.length; i += 1) { + switch (statArr[i]) { + case "experience": + obj.experience = me.getStat(sdk.stats.Experience); + obj.level = me.getStat(sdk.stats.Level); - break; - case "lastArea": - if (obj.lastArea === getAreaName(me.area)) { - return; - } + break; + case "lastArea": + if (obj.lastArea === getAreaName(me.area)) { + return; + } - obj.lastArea = getAreaName(me.area); + obj.lastArea = getAreaName(me.area); - break; - case "gold": - if (!me.gameReady) { - break; - } + break; + case "gold": + if (!me.gameReady) { + break; + } - obj.gold = me.getStat(sdk.stats.Gold) + me.getStat(sdk.stats.GoldBank); + obj.gold = me.getStat(sdk.stats.Gold) + me.getStat(sdk.stats.GoldBank); - break; - case "name": - obj.name = me.name; + break; + case "name": + obj.name = me.name; - break; - case "ingameTick": - obj.ingameTick = getTickCount(); + break; + case "ingameTick": + obj.ingameTick = getTickCount(); - break; - case "deaths": - obj.deaths = (obj.deaths || 0) + 1; + break; + case "deaths": + obj.deaths = (obj.deaths || 0) + 1; - break; - default: - obj[statArr[i]] = value; + break; + default: + obj[statArr[i]] = value; - break; - } - } + break; + } + } - let string = JSON.stringify(obj); + let string = JSON.stringify(obj); - FileAction.write("data/" + me.profile + ".json", string); - } - }; + FileAction.write("data/" + me.profile + ".json", string); + } + }; - return DataFile; + return DataFile; })); diff --git a/d2bs/kolbot/libs/oog/FileAction.js b/d2bs/kolbot/libs/oog/FileAction.js index 5fb2f749f..14a537b62 100644 --- a/d2bs/kolbot/libs/oog/FileAction.js +++ b/d2bs/kolbot/libs/oog/FileAction.js @@ -14,83 +14,83 @@ */ const FileAction = { - read: function (path = "") { - if (!path) throw new Error("No path provided"); + read: function (path = "") { + if (!path) throw new Error("No path provided"); - let contents = ""; + let contents = ""; - for (let i = 0; i < 30; i++) { - try { - contents = FileTools.readText(path); + for (let i = 0; i < 30; i++) { + try { + contents = FileTools.readText(path); - if (contents) return contents; - } catch (e) { - // console.error(e, path); - } + if (contents) return contents; + } catch (e) { + // console.error(e, path); + } - // incremental delay - delay(100 + ((i % 5) * 100)); - } + // incremental delay + delay(100 + ((i % 5) * 100)); + } - return contents; - }, + return contents; + }, - write: function (path = "", msg = "") { - if (!path) throw new Error("No path provided"); + write: function (path = "", msg = "") { + if (!path) throw new Error("No path provided"); - // do we read the file to see if it has changed? - // for now keep the orginal behavior - for (let i = 0; i < 30; i++) { - try { - FileTools.writeText(path, msg); + // do we read the file to see if it has changed? + // for now keep the orginal behavior + for (let i = 0; i < 30; i++) { + try { + FileTools.writeText(path, msg); - break; - } catch (e) { - // console.error(e, path); - } + break; + } catch (e) { + // console.error(e, path); + } - delay(100 + ((i % 5) * 100)); - } + delay(100 + ((i % 5) * 100)); + } - return true; - }, + return true; + }, - append: function (path = "", msg = "") { - if (!path) throw new Error("No path provided"); + append: function (path = "", msg = "") { + if (!path) throw new Error("No path provided"); - // do we read the file to see if it has changed? - // for now keep the orginal behavior - for (let i = 0; i < 30; i++) { - try { - FileTools.appendText(path, msg); + // do we read the file to see if it has changed? + // for now keep the orginal behavior + for (let i = 0; i < 30; i++) { + try { + FileTools.appendText(path, msg); - break; - } catch (e) { - // console.error(e, path); - } + break; + } catch (e) { + // console.error(e, path); + } - delay(100 + ((i % 5) * 100)); - } + delay(100 + ((i % 5) * 100)); + } - return true; - }, + return true; + }, - parse: function (path = "") { - if (!path) throw new Error("No path provided"); - if (!FileTools.exists(path)) throw new Error("Can't parse file that doesn't exist"); + parse: function (path = "") { + if (!path) throw new Error("No path provided"); + if (!FileTools.exists(path)) throw new Error("Can't parse file that doesn't exist"); - let contents = ""; + let contents = ""; - try { - contents = FileAction.read(path); + try { + contents = FileAction.read(path); - if (contents) { - return JSON.parse(contents); - } - } catch (e) { - console.error(e, path); - } + if (contents) { + return JSON.parse(contents); + } + } catch (e) { + console.error(e, path); + } - return contents; - }, + return contents; + }, }; diff --git a/d2bs/kolbot/libs/oog/ShitList.js b/d2bs/kolbot/libs/oog/ShitList.js index e5b1f2929..5afbdb0b2 100644 --- a/d2bs/kolbot/libs/oog/ShitList.js +++ b/d2bs/kolbot/libs/oog/ShitList.js @@ -9,51 +9,51 @@ includeIfNotIncluded("oog/FileAction.js"); const ShitList = { - _default: { - shitlist: [] - }, - create: function () { - let string = JSON.stringify(this._default); + _default: { + shitlist: [] + }, + create: function () { + let string = JSON.stringify(this._default); - FileAction.write("shitlist.json", string); + FileAction.write("shitlist.json", string); - return obj; - }, + return obj; + }, - getObj: function () { - let obj; - let string = FileAction.read("shitlist.json"); + getObj: function () { + let obj; + let string = FileAction.read("shitlist.json"); - try { - obj = JSON.parse(string); - } catch (e) { - obj = this.create(); - } + try { + obj = JSON.parse(string); + } catch (e) { + obj = this.create(); + } - if (obj) { - return obj; - } + if (obj) { + return obj; + } - console.warn("Failed to read ShitList. Using null values"); + console.warn("Failed to read ShitList. Using null values"); - return this._default; - }, + return this._default; + }, - read: function () { - !FileTools.exists("shitlist.json") && this.create(); + read: function () { + !FileTools.exists("shitlist.json") && this.create(); - let obj = this.getObj(); + let obj = this.getObj(); - return obj.shitlist; - }, + return obj.shitlist; + }, - add: function (name) { - let obj = this.getObj(); + add: function (name) { + let obj = this.getObj(); - obj.shitlist.push(name); + obj.shitlist.push(name); - let string = JSON.stringify(obj); + let string = JSON.stringify(obj); - FileAction.write("shitlist.json", string); - } + FileAction.write("shitlist.json", string); + } }; diff --git a/d2bs/kolbot/libs/require.js b/d2bs/kolbot/libs/require.js index 33afc05b3..2cfbc6d8d 100644 --- a/d2bs/kolbot/libs/require.js +++ b/d2bs/kolbot/libs/require.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /* eslint-disable dot-notation */ /** * @filename require.js @@ -13,132 +14,132 @@ typeof global === "undefined" && (this["global"] = this); global["module"] = { exports: undefined }; global["exports"] = {}; function removeRelativePath(test) { - return test.replace(/\\/g, "/").split("/").reduce(function (acc, cur) { - if (!cur || cur === ".") return acc; - if (cur === "..") { - acc.pop(); - return acc; - } - acc.push(cur); - return acc; - - }, []).join("/"); + return test.replace(/\\/g, "/").split("/").reduce(function (acc, cur) { + if (!cur || cur === ".") return acc; + if (cur === "..") { + acc.pop(); + return acc; + } + acc.push(cur); + return acc; + + }, []).join("/"); } global.require = (function (include, isIncluded, print, notify) { - const debug = false; - - let depth = 0; - const modules = {}; - const obj = function require(field, path) { - const stack = new Error().stack.match(/[^\r\n]+/g); - let directory = stack[1].match(/.*?@.*?d2bs\\(kolbot\\?.*)\\.*(\.js|\.dbj):/)[1].replace("\\", "/") + "/"; - let filename = stack[1].match(/.*?@.*?d2bs\\kolbot\\?(.*)(\.js|\.dbj):/)[1]; - filename = filename.substr(filename.length - filename.split("").reverse().join("").indexOf("\\")); - // remove the name kolbot of the file - if (directory.startsWith("kolbot")) { - directory = directory.substr("kolbot".length); - } - - // remove the / from it - if (directory.startsWith("/")) { - directory = directory.substr(1); - } - - // strip off lib - if (directory.startsWith("lib")) { - directory = directory.substr(4); - } else { - directory = "../" + directory; // Add a extra recursive path, as we start out of the lib directory - } - - path = path || directory; - - let fullpath = removeRelativePath((path + field).replace(/\\/, "/")).toLowerCase(); - // remove lib again, if required in e.g. kolbot\tools but wants modules\whatever - if (fullpath.startsWith("lib")) { - fullpath = fullpath.substr(4); - } - const packageName = fullpath; - - const asNew = this.__proto__.constructor === require && ((...args) => new (Function.prototype.bind.apply(modules[packageName].exports, args))); - - if (field.hasOwnProperty("endsWith") && field.endsWith(".json")) { // Simply reads a json file - return modules[packageName] = File.open("libs/" + path + field, 0).readAllLines(); - } - - let nameShort; - try { - nameShort = (fullpath + ".js").match(/.*?\/(\w*).js$/)[1]; - } catch (e) { - // file in libs folder same as us - nameShort = (fullpath + ".js").match(/.*?\/?(\w*).js$/)[0]; - } - const moduleNameShort = nameShort; - - if (!isIncluded(fullpath + ".js") && !modules.hasOwnProperty(moduleNameShort)) { - if (debug) { - depth && notify && print("ÿc2Kolbotÿc0 :: - loading dependency of " + filename + ": " + moduleNameShort); - !depth && notify && print("ÿc2Kolbotÿc0 :: Loading module: " + moduleNameShort); - } - - let oldModule = Object.create(global["module"]); - let oldExports = Object.create(global["exports"]); - delete global["module"]; - delete global["exports"]; - global["module"] = { exports: null }; - global["exports"] = {}; - - // Include the file; - try { - depth++; - if (!include(fullpath + ".js")) { - const err = new Error("module " + fullpath + " not found"); - - // Rewrite the location of the error, to be more clear for the developer/user _where_ it crashes - const myStack = err.stack.match(/[^\r\n]+/g); - err.fileName = directory + myStack[1].match(/.*?@.*?d2bs\\kolbot\\?(.*)(\.js|\.dbj):/)[1]; - err.lineNumber = myStack[1].substr(stack[1].lastIndexOf(":") + 1); - myStack.unshift(); - err.stack = myStack.join("\r\n"); // rewrite stack - - throw err; - } - } finally { - depth--; - } - - if (!global["module"]["exports"] && Object.keys(global["exports"])) { // Incase its transpiled typescript - global["module"]["exports"] = global["exports"]; - } - - modules[packageName] = Object.create(global["module"]); - delete global["module"]; - delete global["exports"]; - global["module"] = oldModule; - global["exports"] = oldExports; - } - - if (!modules.hasOwnProperty(packageName)) throw Error("unexpected module error -- " + field); - - // If called as "new", fake an constructor - return asNew || modules[packageName].exports; - }; - obj.modules = modules; - return obj; + const debug = false; + + let depth = 0; + const modules = {}; + const obj = function require(field, path) { + const stack = new Error().stack.match(/[^\r\n]+/g); + let directory = stack[1].match(/.*?@.*?d2bs\\(kolbot\\?.*)\\.*(\.js|\.dbj):/)[1].replace("\\", "/") + "/"; + let filename = stack[1].match(/.*?@.*?d2bs\\kolbot\\?(.*)(\.js|\.dbj):/)[1]; + filename = filename.substr(filename.length - filename.split("").reverse().join("").indexOf("\\")); + // remove the name kolbot of the file + if (directory.startsWith("kolbot")) { + directory = directory.substr("kolbot".length); + } + + // remove the / from it + if (directory.startsWith("/")) { + directory = directory.substr(1); + } + + // strip off lib + if (directory.startsWith("lib")) { + directory = directory.substr(4); + } else { + directory = "../" + directory; // Add a extra recursive path, as we start out of the lib directory + } + + path = path || directory; + + let fullpath = removeRelativePath((path + field).replace(/\\/, "/")).toLowerCase(); + // remove lib again, if required in e.g. kolbot\tools but wants modules\whatever + if (fullpath.startsWith("lib")) { + fullpath = fullpath.substr(4); + } + const packageName = fullpath; + + const asNew = this.__proto__.constructor === require && ((...args) => new (Function.prototype.bind.apply(modules[packageName].exports, args))); + + if (field.hasOwnProperty("endsWith") && field.endsWith(".json")) { // Simply reads a json file + return modules[packageName] = File.open("libs/" + path + field, 0).readAllLines(); + } + + let nameShort; + try { + nameShort = (fullpath + ".js").match(/.*?\/(\w*).js$/)[1]; + } catch (e) { + // file in libs folder same as us + nameShort = (fullpath + ".js").match(/.*?\/?(\w*).js$/)[0]; + } + const moduleNameShort = nameShort; + + if (!isIncluded(fullpath + ".js") && !modules.hasOwnProperty(moduleNameShort)) { + if (debug) { + depth && notify && print("ÿc2Kolbotÿc0 :: - loading dependency of " + filename + ": " + moduleNameShort); + !depth && notify && print("ÿc2Kolbotÿc0 :: Loading module: " + moduleNameShort); + } + + let oldModule = Object.create(global["module"]); + let oldExports = Object.create(global["exports"]); + delete global["module"]; + delete global["exports"]; + global["module"] = { exports: null }; + global["exports"] = {}; + + // Include the file; + try { + depth++; + if (!include(fullpath + ".js")) { + const err = new Error("module " + fullpath + " not found"); + + // Rewrite the location of the error, to be more clear for the developer/user _where_ it crashes + const myStack = err.stack.match(/[^\r\n]+/g); + err.fileName = directory + myStack[1].match(/.*?@.*?d2bs\\kolbot\\?(.*)(\.js|\.dbj):/)[1]; + err.lineNumber = myStack[1].substr(stack[1].lastIndexOf(":") + 1); + myStack.unshift(); + err.stack = myStack.join("\r\n"); // rewrite stack + + throw err; + } + } finally { + depth--; + } + + if (!global["module"]["exports"] && Object.keys(global["exports"])) { // Incase its transpiled typescript + global["module"]["exports"] = global["exports"]; + } + + modules[packageName] = Object.create(global["module"]); + delete global["module"]; + delete global["exports"]; + global["module"] = oldModule; + global["exports"] = oldExports; + } + + if (!modules.hasOwnProperty(packageName)) throw Error("unexpected module error -- " + field); + + // If called as "new", fake an constructor + return asNew || modules[packageName].exports; + }; + obj.modules = modules; + return obj; })(include, isIncluded, print, getScript(true).name.toLowerCase().split("").reverse().splice(0, ".dbj".length).reverse().join("") === ".dbj"); getScript.startAsThread = function () { - let stack = new Error().stack.match(/[^\r\n]+/g), - filename = stack[1].match(/.*?@.*?d2bs\\kolbot\\(.*):/)[1]; + let stack = new Error().stack.match(/[^\r\n]+/g), + filename = stack[1].match(/.*?@.*?d2bs\\kolbot\\(.*):/)[1]; - if (getScript(true).name.toLowerCase() === filename.toLowerCase()) { - return "thread"; - } + if (getScript(true).name.toLowerCase() === filename.toLowerCase()) { + return "thread"; + } - if (!getScript(filename)) { - load(filename); - return "started"; - } + if (!getScript(filename)) { + load(filename); + return "started"; + } - return "loaded"; + return "loaded"; }; diff --git a/d2bs/kolbot/libs/scripts/Abaddon.js b/d2bs/kolbot/libs/scripts/Abaddon.js index 5eb7ff990..7751683dc 100644 --- a/d2bs/kolbot/libs/scripts/Abaddon.js +++ b/d2bs/kolbot/libs/scripts/Abaddon.js @@ -6,15 +6,16 @@ */ function Abaddon() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.RedPortal) || !Pather.usePortal(sdk.areas.Abaddon)) { - throw new Error("Failed to move to Abaddon"); - } + if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.RedPortal) + || !Pather.usePortal(sdk.areas.Abaddon)) { + throw new Error("Failed to move to Abaddon"); + } - Attack.clearLevel(Config.ClearType); + Attack.clearLevel(Config.ClearType); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/AncientTunnels.js b/d2bs/kolbot/libs/scripts/AncientTunnels.js index bebb6abcd..b4ea73f8a 100644 --- a/d2bs/kolbot/libs/scripts/AncientTunnels.js +++ b/d2bs/kolbot/libs/scripts/AncientTunnels.js @@ -6,24 +6,29 @@ */ function AncientTunnels() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.LostCity); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.LostCity); + Precast.doPrecast(true); - try { - Config.AncientTunnels.OpenChest && Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest) && Misc.openChests(5) && Pickit.pickItems(); - } catch (e) { - console.error(e); - } + try { + if (Config.AncientTunnels.OpenChest && Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest)) { + Misc.openChests(5) && Pickit.pickItems(); + } + } catch (e) { + console.error(e); + } - try { - Config.AncientTunnels.KillDarkElder && Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.DarkElder) && Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); - } catch (e) { - console.error(e); - } + try { + if (Config.AncientTunnels.KillDarkElder + && Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.DarkElder)) { + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); + } + } catch (e) { + console.error(e); + } - if (!Pather.moveToExit(sdk.areas.AncientTunnels, true)) throw new Error("Failed to move to Ancient Tunnels"); - Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit(sdk.areas.AncientTunnels, true)) throw new Error("Failed to move to Ancient Tunnels"); + Attack.clearLevel(Config.ClearType); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Andariel.js b/d2bs/kolbot/libs/scripts/Andariel.js index 2c3b77f48..62db9fd33 100644 --- a/d2bs/kolbot/libs/scripts/Andariel.js +++ b/d2bs/kolbot/libs/scripts/Andariel.js @@ -6,31 +6,33 @@ */ function Andariel () { - this.killAndariel = function () { - let target = Game.getMonster(sdk.monsters.Andariel); - if (!target) throw new Error("Andariel not found."); + const killAndariel = function () { + let target = Game.getMonster(sdk.monsters.Andariel); + if (!target) throw new Error("Andariel not found."); - Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Andariel); + Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Andariel); - for (let i = 0; i < 300 && target.attackable; i += 1) { - ClassAttack.doAttack(target); - target.distance <= 10 && Pather.moveTo(me.x > 22548 ? 22535 : 22560, 9520); - } + for (let i = 0; i < 300 && target.attackable; i += 1) { + ClassAttack.doAttack(target); + target.distance <= 10 && Pather.moveTo(me.x > 22548 ? 22535 : 22560, 9520); + } - return target.dead; - }; + return target.dead; + }; - Town.doChores(); - Pather.useWaypoint(sdk.areas.CatacombsLvl2); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.CatacombsLvl2); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true)) throw new Error("Failed to move to Catacombs Level 4"); + if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true)) { + throw new Error("Failed to move to Catacombs Level 4"); + } - Pather.moveTo(22549, 9520); - me.sorceress && me.classic ? this.killAndariel() : Attack.kill(sdk.monsters.Andariel); + Pather.moveTo(22549, 9520); + me.sorceress && me.classic ? killAndariel() : Attack.kill(sdk.monsters.Andariel); - delay(2000); // Wait for minions to die. - Pickit.pickItems(); + delay(2000); // Wait for minions to die. + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index 38601ee58..42974a214 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -16,233 +16,240 @@ */ function AutoBaal() { - // internal variables - let baalCheck, throneCheck, hotCheck, leader; // internal variables - const safeMsg = ["safe", "throne clear", "leechers can come", "tp is up", "1 clear"]; // safe message - casing doesn't matter - const baalMsg = ["baal"]; // baal message - casing doesn't matter - const hotMsg = ["hot", "warm", "dangerous", "lethal"]; // used for shrine hunt - - // chat event handler function, listen to what leader says - const chatEvent = function (nick, msg) { - // filter leader messages - if (nick === leader) { - // loop through all predefined messages to find a match - for (let i = 0; i < hotMsg.length; i += 1) { - // leader says a hot tp message - if (msg.toLowerCase().includes(hotMsg[i].toLowerCase()) && Config.AutoBaal.FindShrine === 1) { - hotCheck = true; // safe to enter baal chamber - - break; - } - } - - // loop through all predefined messages to find a match - for (let i = 0; i < safeMsg.length; i += 1) { - // leader says a safe tp message - if (msg.toLowerCase().includes(safeMsg[i].toLowerCase())) { - throneCheck = true; // safe to enter throne - - break; - } - } - - // loop through all predefined messages to find a match - for (let i = 0; i < baalMsg.length; i += 1) { - // leader says a baal message - if (msg.toLowerCase().includes(baalMsg[i].toLowerCase())) { - baalCheck = true; // safe to enter baal chamber - - break; - } - } - } - }; - - /** - * @todo maybe factor this out and make it useable for other leecher scripts? - */ - const longRangeSupport = function () { - switch (me.classid) { - case sdk.player.class.Necromancer: - ClassAttack.raiseArmy(50); - - if (Config.Curse[1] > 0) { - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged) - && monster.curseable && !monster.isSpecial && ClassAttack.canCurse(monster, Config.Curse[1])) { - Skill.cast(Config.Curse[1], sdk.skills.hand.Right, monster); - } - } while (monster.getNext()); - } - } - - break; - case sdk.player.class.Assassin: - if (Config.UseTraps && ClassAttack.checkTraps({x: 15095, y: 5037})) { - ClassAttack.placeTraps({x: 15095, y: 5037}, 5); - } - - break; - default: - break; - } - - let skills = [ - sdk.skills.ChargedStrike, sdk.skills.Lightning, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, - sdk.skills.BoneSpear, sdk.skills.BoneSpirit, sdk.skills.DoubleThrow, sdk.skills.Volcano - ]; - - if (!skills.some(skill => Config.AttackSkill[1] === skill || Config.AttackSkill[3] === skill)) { - return false; - } - - let monster = Game.getMonster(); - let monList = []; - - if (monster) { - do { - if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged)) { - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - - if (me.inArea(sdk.areas.ThroneofDestruction)) { - [15116, 5026].distance > 10 && Pather.moveTo(15116, 5026); - } - - let oldVal = Skill.usePvpRange; - Skill.usePvpRange = true; - - try { - while (monList.length) { - monList.sort(Sort.units); - monster = copyUnit(monList[0]); - - if (monster && monster.attackable) { - let index = monster.isSpecial ? 1 : 3; - - if (Config.AttackSkill[index] > -1 && Attack.checkResist(monster, Attack.getSkillElement(Config.AttackSkill[index]))) { - ClassAttack.doCast(monster, Config.AttackSkill[index], Config.AttackSkill[index + 1]); - } else { - monList.shift(); - } - } else { - monList.shift(); - } - - delay(5); - } - } finally { - Skill.usePvpRange = oldVal; - } - - return true; - }; - - // critical error - can't reach harrogath - if (!Town.goToTown(5)) throw new Error("Town.goToTown failed."); - - if (Config.Leader) { - leader = Config.Leader; - if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), Time.seconds(1))) throw new Error("AutoBaal: Leader not partied"); - } - - try { - addEventListener("chatmsg", chatEvent); - Config.AutoBaal.FindShrine === 2 && (hotCheck = true); - - Town.doChores(); - Town.move("portalspot"); - - // find the first player in throne of destruction - if (leader || (leader = Misc.autoLeaderDetect({destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area)}))) { - // do our stuff while partied - while (Misc.inMyParty(leader)) { - if (hotCheck) { - if (Config.AutoBaal.FindShrine) { - let i; - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - - for (i = sdk.areas.StonyField; i > 1; i--) { - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; - } - } - - if (i === 1) { - Town.goToTown(); - Pather.useWaypoint(sdk.areas.DarkWood); - - for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i++) { - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; - } - } - } - } - - Town.goToTown(5); - Town.move("portalspot"); - - hotCheck = false; - } - - // wait for throne signal - leader's safe message - if ((throneCheck || baalCheck) && me.inArea(sdk.areas.Harrogath)) { - print("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); - // move to a safe spot - Pather.moveTo(Config.AutoBaal.LeechSpot[0], Config.AutoBaal.LeechSpot[1]); - Precast.doPrecast(true); - Town.getCorpse(); - } - - !baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport && longRangeSupport(); - - // wait for baal signal - leader's baal message - if (baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { - // move closer to chamber portal - Pather.moveTo(15092, 5010); - Precast.doPrecast(false); - - // wait for baal to go through the portal - while (Game.getMonster(sdk.monsters.ThroneBaal)) { - delay(500); - } - - let portal = Game.getObject(sdk.objects.WorldstonePortal); - - delay(2000); // wait for others to enter first - helps with curses and tentacles from spawning around you - print("ÿc4AutoBaal: ÿc0Entering chamber."); - Pather.usePortal(null, null, portal) && Pather.moveTo(15166, 5903); // go to a safe position - Town.getCorpse(); - } - - let baal = Game.getMonster(sdk.monsters.Baal); - - if (baal) { - if (baal.dead) { - break; - } - - longRangeSupport(); - } - - me.mode === sdk.player.mode.Dead && me.revive(); - - delay(500); - } - } else { - throw new Error("Empty game."); - } - } finally { - removeEventListener("chatmsg", chatEvent); - } - - return true; + // internal variables + let baalCheck, throneCheck, hotCheck, leader; // internal variables + const safeMsg = ["safe", "throne clear", "leechers can come", "tp is up", "1 clear"]; // safe message - casing doesn't matter + const baalMsg = ["baal"]; // baal message - casing doesn't matter + const hotMsg = ["hot", "warm", "dangerous", "lethal"]; // used for shrine hunt + + // chat event handler function, listen to what leader says + const chatEvent = function (nick, msg) { + // filter leader messages + if (nick === leader) { + // loop through all predefined messages to find a match + for (let i = 0; i < hotMsg.length; i += 1) { + // leader says a hot tp message + if (msg.toLowerCase().includes(hotMsg[i].toLowerCase()) && Config.AutoBaal.FindShrine === 1) { + hotCheck = true; // safe to enter baal chamber + + break; + } + } + + // loop through all predefined messages to find a match + for (let i = 0; i < safeMsg.length; i += 1) { + // leader says a safe tp message + if (msg.toLowerCase().includes(safeMsg[i].toLowerCase())) { + throneCheck = true; // safe to enter throne + + break; + } + } + + // loop through all predefined messages to find a match + for (let i = 0; i < baalMsg.length; i += 1) { + // leader says a baal message + if (msg.toLowerCase().includes(baalMsg[i].toLowerCase())) { + baalCheck = true; // safe to enter baal chamber + + break; + } + } + } + }; + + /** + * @todo maybe factor this out and make it useable for other leecher scripts? + */ + const longRangeSupport = function () { + switch (me.classid) { + case sdk.player.class.Necromancer: + ClassAttack.raiseArmy(50); + + if (Config.Curse[1] > 0) { + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged) + && monster.curseable && !monster.isSpecial && ClassAttack.canCurse(monster, Config.Curse[1])) { + Skill.cast(Config.Curse[1], sdk.skills.hand.Right, monster); + } + } while (monster.getNext()); + } + } + + break; + case sdk.player.class.Assassin: + if (Config.UseTraps && ClassAttack.checkTraps({ x: 15095, y: 5037 })) { + ClassAttack.placeTraps({ x: 15095, y: 5037 }, 5); + } + + break; + default: + break; + } + + let skills = [ + sdk.skills.ChargedStrike, sdk.skills.Lightning, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, + sdk.skills.BoneSpear, sdk.skills.BoneSpirit, sdk.skills.DoubleThrow, sdk.skills.Volcano + ]; + + if (!skills.some(skill => Config.AttackSkill[1] === skill || Config.AttackSkill[3] === skill)) { + return false; + } + + let monster = Game.getMonster(); + let monList = []; + + if (monster) { + do { + if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged)) { + monList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + + if (me.inArea(sdk.areas.ThroneofDestruction)) { + [15116, 5026].distance > 10 && Pather.moveTo(15116, 5026); + } + + let oldVal = Skill.usePvpRange; + Skill.usePvpRange = true; + + try { + while (monList.length) { + monList.sort(Sort.units); + monster = copyUnit(monList[0]); + + if (monster && monster.attackable) { + let index = monster.isSpecial ? 1 : 3; + + if (Config.AttackSkill[index] > -1 + && Attack.checkResist(monster, Attack.getSkillElement(Config.AttackSkill[index]))) { + ClassAttack.doCast(monster, Config.AttackSkill[index], Config.AttackSkill[index + 1]); + } else { + monList.shift(); + } + } else { + monList.shift(); + } + + delay(5); + } + } finally { + Skill.usePvpRange = oldVal; + } + + return true; + }; + + // critical error - can't reach harrogath + if (!Town.goToTown(5)) throw new Error("Town.goToTown failed."); + + if (Config.Leader) { + leader = Config.Leader; + if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), Time.seconds(1))) { + throw new Error("AutoBaal: Leader not partied"); + } + } + + try { + addEventListener("chatmsg", chatEvent); + Config.AutoBaal.FindShrine === 2 && (hotCheck = true); + + Town.doChores(); + Town.move("portalspot"); + + // find the first player in throne of destruction + if (leader || (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ThroneofDestruction, + quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) + }))) { + // do our stuff while partied + while (Misc.inMyParty(leader)) { + if (hotCheck) { + if (Config.AutoBaal.FindShrine) { + let i; + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + + for (i = sdk.areas.StonyField; i > 1; i--) { + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } + } + + if (i === 1) { + Town.goToTown(); + Pather.useWaypoint(sdk.areas.DarkWood); + + for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i++) { + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } + } + } + } + + Town.goToTown(5); + Town.move("portalspot"); + + hotCheck = false; + } + + // wait for throne signal - leader's safe message + if ((throneCheck || baalCheck) && me.inArea(sdk.areas.Harrogath)) { + print("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + // move to a safe spot + Pather.moveTo(Config.AutoBaal.LeechSpot[0], Config.AutoBaal.LeechSpot[1]); + Precast.doPrecast(true); + Town.getCorpse(); + } + + if (!baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport) { + longRangeSupport(); + } + // wait for baal signal - leader's baal message + if (baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { + // move closer to chamber portal + Pather.moveTo(15092, 5010); + Precast.doPrecast(false); + + // wait for baal to go through the portal + while (Game.getMonster(sdk.monsters.ThroneBaal)) { + delay(500); + } + + let portal = Game.getObject(sdk.objects.WorldstonePortal); + + delay(2000); // wait for others to enter first - helps with curses and tentacles from spawning around you + print("ÿc4AutoBaal: ÿc0Entering chamber."); + Pather.usePortal(null, null, portal) && Pather.moveTo(15166, 5903); // go to a safe position + Town.getCorpse(); + } + + let baal = Game.getMonster(sdk.monsters.Baal); + + if (baal) { + if (baal.dead) { + break; + } + + longRangeSupport(); + } + + me.mode === sdk.player.mode.Dead && me.revive(); + + delay(500); + } + } else { + throw new Error("Empty game."); + } + } finally { + removeEventListener("chatmsg", chatEvent); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index a462f4d11..1853324ca 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -6,90 +6,90 @@ */ function Baal() { - include("core/Common/Baal.js"); - const announce = function () { - let count, string, souls, dolls; - let monster = Game.getMonster(); - - if (monster) { - count = 0; - - do { - if (monster.attackable && monster.y < 5094) { - monster.distance <= 40 && (count += 1); - !souls && monster.classid === sdk.monsters.BurningSoul1 && (souls = true); - !dolls && monster.classid === sdk.monsters.SoulKiller && (dolls = true); - } - } while (monster.getNext()); - } - - if (count > 30) { - string = "DEADLY!!!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else if (count > 20) { - string = "Lethal!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else if (count > 10) { - string = "Dangerous!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else if (count > 0) { - string = "Warm" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else { - string = "Cool TP. No immediate monsters."; - } - - if (souls) { - string += " Souls "; - dolls && (string += "and Dolls "); - string += "in area."; - } else if (dolls) { - string += " Dolls in area."; - } - - say(string); - }; - - Town.doChores(); - Config.RandomPrecast - ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) - : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); - !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { - throw new Error("Failed to move to Throne of Destruction."); - } - - Pather.moveToEx(15095, 5029, { callback: () => { - if (Config.Baal.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - say("Dolls found! NG."); - throw new ScriptError("Dolls found! NG."); - } - - if (Config.Baal.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { - say("Souls found! NG."); - throw new ScriptError("Souls found! NG."); - } - }}); - - if (Config.PublicMode) { - announce(); - Pather.moveTo(15118, 5002); - Pather.makePortal(); - say(Config.Baal.HotTPMessage); - Attack.clear(15); - } - - Common.Baal.clearThrone(); - - if (Config.PublicMode) { - Pather.moveTo(15118, 5045); - Pather.makePortal(); - say(Config.Baal.SafeTPMessage); - Precast.doPrecast(true); - } - - if (!Common.Baal.clearWaves()) { - throw new Error("Couldn't clear baal waves"); - } - - Config.Baal.KillBaal && Common.Baal.killBaal(); - - return true; + include("core/Common/Baal.js"); + const announce = function () { + let count, string, souls, dolls; + let monster = Game.getMonster(); + + if (monster) { + count = 0; + + do { + if (monster.attackable && monster.y < 5094) { + monster.distance <= 40 && (count += 1); + !souls && monster.classid === sdk.monsters.BurningSoul1 && (souls = true); + !dolls && monster.classid === sdk.monsters.SoulKiller && (dolls = true); + } + } while (monster.getNext()); + } + + if (count > 30) { + string = "DEADLY!!!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else if (count > 20) { + string = "Lethal!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else if (count > 10) { + string = "Dangerous!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else if (count > 0) { + string = "Warm" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else { + string = "Cool TP. No immediate monsters."; + } + + if (souls) { + string += " Souls "; + dolls && (string += "and Dolls "); + string += "in area."; + } else if (dolls) { + string += " Dolls in area."; + } + + say(string); + }; + + Town.doChores(); + Config.RandomPrecast + ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) + : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { + throw new Error("Failed to move to Throne of Destruction."); + } + + Pather.moveToEx(15095, 5029, { callback: () => { + if (Config.Baal.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + say("Dolls found! NG."); + throw new ScriptError("Dolls found! NG."); + } + + if (Config.Baal.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + say("Souls found! NG."); + throw new ScriptError("Souls found! NG."); + } + } }); + + if (Config.PublicMode) { + announce(); + Pather.moveTo(15118, 5002); + Pather.makePortal(); + say(Config.Baal.HotTPMessage); + Attack.clear(15); + } + + Common.Baal.clearThrone(); + + if (Config.PublicMode) { + Pather.moveTo(15118, 5045); + Pather.makePortal(); + say(Config.Baal.SafeTPMessage); + Precast.doPrecast(true); + } + + if (!Common.Baal.clearWaves()) { + throw new Error("Couldn't clear baal waves"); + } + + Config.Baal.KillBaal && Common.Baal.killBaal(); + + return true; } diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index d7e06273e..7dadd969f 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -13,454 +13,490 @@ */ function BaalAssistant () { - include("core/Common/Baal.js"); - let Leader = Config.Leader; - let Helper = Config.BaalAssistant.Helper; - let firstAttempt = true; - let quitFlag = false; - let [hotCheck, safeCheck, baalCheck, ngCheck, baalIsDead] = [false, false, false, false, false]; - let [ShrineStatus, secondAttempt, throneStatus, killTracker] = [false, false, false, false]; - - // convert all messages to lowercase - Config.BaalAssistant.HotTPMessage.forEach((msg, i) => { - Config.BaalAssistant.HotTPMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.SafeTPMessage.forEach((msg, i) => { - Config.BaalAssistant.SafeTPMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.BaalMessage.forEach((msg, i) => { - Config.BaalAssistant.BaalMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.NextGameMessage.forEach((msg, i) => { - Config.BaalAssistant.NextGameMessage[i] = msg.toLowerCase(); - }); - - const chatEvent = function (nick, msg) { - if (nick === Leader) { - if ((Config.BaalAssistant.DollQuit && msg === "Dolls found! NG.") - || (Config.BaalAssistant.SoulQuit && msg === "Souls found! NG.")) { - quitFlag = true; - - return; - } - - msg = msg.toLowerCase(); - - for (let i = 0; i < Config.BaalAssistant.HotTPMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.HotTPMessage[i])) { - hotCheck = true; - break; - } - } - - for (let i = 0; i < Config.BaalAssistant.SafeTPMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.SafeTPMessage[i])) { - safeCheck = true; - break; - } - } - - for (let i = 0; i < Config.BaalAssistant.BaalMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.BaalMessage[i])) { - baalCheck = true; - break; - } - } - - for (let i = 0; i < Config.BaalAssistant.NextGameMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.NextGameMessage[i])) { - ngCheck = true; - killTracker = true; - break; - } - } - } - }; - - const baalDeathEvent = function (bytes = []) { - if (!bytes.length || bytes.length !== 2) return; - - if (bytes[0] === sdk.packets.recv.UniqueEvents && bytes[1] === 0x13) { - baalIsDead = true; - } - }; - - const checkParty = function () { - for (let i = 0; i < Config.BaalAssistant.Wait; i += 1) { - let partycheck = getParty(); - if (partycheck) { - do { - if (partycheck.area === sdk.areas.ThroneofDestruction) return false; - if (partycheck.area === sdk.areas.RiverofFlame || partycheck.area === sdk.areas.ChaosSanctuary) return true; - } while (partycheck.getNext()); - } - - delay(1000); - } - - return false; - }; - - // Start - const Worker = require("../modules/Worker"); - - if (Leader) { - if (!Misc.poll(() => Misc.inMyParty(Leader), Time.seconds(30), 1000)) throw new Error("BaalAssistant: Leader not partied"); - } - - try { - addEventListener("chatmsg", chatEvent); - - let killLeaderTracker = false; - if (!Leader && (Config.BaalAssistant.KillNihlathak || Config.BaalAssistant.FastChaos)) { - // run background auto detect so we don't miss messages while running add ons - let leadTick = getTickCount(); - - Worker.runInBackground.leaderTracker = function () { - if (killLeaderTracker || killTracker) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) return false; - - let party = getParty(); - - if (party) { - do { - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { - Leader = party.name; - console.log(sdk.colors.DarkGold + "Autodected " + Leader); - return false; - } - } while (party.getNext()); - } - - return true; - }; - } - - Config.BaalAssistant.KillNihlathak && Loader.runScript("Nihlathak"); - Config.BaalAssistant.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); - - Town.goToTown(5); - Town.doChores(); - - if (Leader - || (Leader = Misc.autoLeaderDetect({destination: sdk.areas.WorldstoneLvl3, quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area)})) - || (Leader = Misc.autoLeaderDetect({destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area)}))) { - print("ÿc hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); - - if (!hotCheck) { - print("ÿc1Leader didn't tell me to start hunting for an experience shrine."); - ShrineStatus = true; - } - } - - // don't waste time looking for seal if party is already killing baal, he'll be dead by the time we find one - if (!ShrineStatus && !baalCheck) { - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - let i; - - for (i = sdk.areas.StonyField; i > sdk.areas.RogueEncampment; i -= 1) { - if (safeCheck) { - break; - } - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; - } - } - - if (!safeCheck) { - if (i === sdk.areas.RogueEncampment) { - Town.goToTown(); - Pather.useWaypoint(sdk.areas.DarkWood); - Precast.doPrecast(true); - - for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i += 1) { - if (safeCheck) { - break; - } - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; - } - } - } - } - } - - Town.goToTown(5); - ShrineStatus = true; - } - - if (firstAttempt && !secondAttempt && !safeCheck && !baalCheck && !me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { - !!Config.RandomPrecast ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); - } - - if (!me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { - if (Config.BaalAssistant.SkipTP) { - if (firstAttempt && !secondAttempt) { - !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) throw new Error("Failed to move to WSK3."); - - checkParty(); - let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); - entrance && Pather.moveTo(entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, entrance.y > me.y ? entrance.y - 5 : entrance.y + 5); - - if (!Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) || !Pather.moveTo(15118, 5002)) throw new Error("Failed to move to Throne of Destruction."); - - Pather.moveToEx(15095, 5029, { callback: () => { - if (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - console.log("Undead Soul Killers found, ending script."); - throw new ScriptError("Undead Soul Killers found, ending script."); - } - - if (Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { - console.log("Burning Souls found, ending script."); - throw new ScriptError("Burning Souls found, ending script."); - } - }}); - - Pather.moveTo(15118, 5002); - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - - secondAttempt = true; - safeCheck = true; - } else { - if (me.inTown) { - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - } - } - } else { - if (firstAttempt && !secondAttempt) { - !me.inArea(sdk.areas.Harrogath) && Pather.useWaypoint(sdk.areas.Harrogath); - Town.move("portalspot"); - - if (Config.BaalAssistant.WaitForSafeTP && !Misc.poll(() => safeCheck, Time.seconds(Config.BaalAssistant.Wait), 1000)) { - throw new Error("No safe TP message."); - } - - if ((Config.BaalAssistant.SoulQuit || Config.BaalAssistant.DollQuit) && quitFlag) { - throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); - } - - if (!Misc.poll(() => Pather.usePortal(sdk.areas.ThroneofDestruction, null), Time.seconds(Config.BaalAssistant.Wait), 1000)) { - throw new Error("No portals to Throne."); - } - - if ((Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { - throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); - } - - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - secondAttempt = true; - safeCheck = true; - } else { - if (me.inTown) { - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - } - } - } - } - - if (safeCheck && !baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { - if (!baalCheck && !throneStatus) { - if (Helper) { - Attack.clear(15); - Common.Baal.clearThrone(); - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - Precast.doPrecast(true); - } - - let tick = getTickCount(); - - MainLoop: while (true) { - if (Helper) { - if (getDistance(me, 15094, me.paladin ? 5029 : 5038) > 3) { - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - } - } - - if (!Game.getMonster(sdk.monsters.ThroneBaal)) { - break; - } - - switch (Common.Baal.checkThrone(Helper)) { - case 1: - Helper && Attack.clear(40); - tick = getTickCount(); - - break; - case 2: - Helper && Attack.clear(40); - tick = getTickCount(); - - break; - case 4: - Helper && Attack.clear(40); - tick = getTickCount(); - - break; - case 3: - Helper && Attack.clear(40) && Common.Baal.checkHydra(); - tick = getTickCount(); - - break; - case 5: - if (Helper) { - Attack.clear(40); - } else { - while ([sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2] - .map((unitId) => Game.getMonster(unitId)) - .filter(Boolean).some((unit) => unit.attackable)) { - delay(1000); - } - - delay(1000); - } - - break MainLoop; - default: - if (getTickCount() - tick < 7e3) { - if (me.paladin && me.getState(sdk.states.Poison) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { - break; - } - } - - if (Helper && !Common.Baal.preattack()) { - delay(100); - } - - break; - } - delay(10); - } - throneStatus = true; - baalCheck = true; - } - } - - if ((throneStatus || baalCheck) && Config.BaalAssistant.KillBaal && me.inArea(sdk.areas.ThroneofDestruction)) { - Helper ? Pather.moveTo(15090, 5008) && delay(2000) : Pather.moveTo(15090, 5010); - Precast.doPrecast(true); - !Helper && addEventListener("gamepacket", baalDeathEvent); - - while (Game.getMonster(sdk.monsters.ThroneBaal)) { - delay(500); - } - - let portal = Game.getObject(sdk.objects.WorldstonePortal); - - if (portal) { - delay((Helper ? 1000 : 4000)); - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } - - if (Helper) { - delay(1000); - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); - } else { - Pather.moveTo(15177, 5952); - let baal = Game.getMonster(sdk.monsters.Baal); - - while (!!baal && baal.attackable && !baalIsDead) { - delay(1000); - } - } - - } else { - // how to accurately know when to end script in the instance of no ngCheck - // listen for baal death packet maybe? - while (!ngCheck && !baalIsDead) { - delay(500); - } - } - - delay(500); - } - } catch (e) { - console.error(e); - } finally { - killTracker = true; - } - } else { - throw new Error("Empty game."); - } - } finally { - removeEventListener("chatmsg", chatEvent); - removeEventListener("gamepacket", baalDeathEvent); - } - - return true; + include("core/Common/Baal.js"); + let Leader = Config.Leader; + let Helper = Config.BaalAssistant.Helper; + let firstAttempt = true; + let quitFlag = false; + let [hotCheck, safeCheck, baalCheck, ngCheck, baalIsDead] = [false, false, false, false, false]; + let [ShrineStatus, secondAttempt, throneStatus, killTracker] = [false, false, false, false]; + + // convert all messages to lowercase + Config.BaalAssistant.HotTPMessage.forEach((msg, i) => { + Config.BaalAssistant.HotTPMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.SafeTPMessage.forEach((msg, i) => { + Config.BaalAssistant.SafeTPMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.BaalMessage.forEach((msg, i) => { + Config.BaalAssistant.BaalMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.NextGameMessage.forEach((msg, i) => { + Config.BaalAssistant.NextGameMessage[i] = msg.toLowerCase(); + }); + + const chatEvent = function (nick, msg) { + if (nick === Leader) { + if ((Config.BaalAssistant.DollQuit && msg === "Dolls found! NG.") + || (Config.BaalAssistant.SoulQuit && msg === "Souls found! NG.")) { + quitFlag = true; + + return; + } + + msg = msg.toLowerCase(); + + for (let i = 0; i < Config.BaalAssistant.HotTPMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.HotTPMessage[i])) { + hotCheck = true; + break; + } + } + + for (let i = 0; i < Config.BaalAssistant.SafeTPMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.SafeTPMessage[i])) { + safeCheck = true; + break; + } + } + + for (let i = 0; i < Config.BaalAssistant.BaalMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.BaalMessage[i])) { + baalCheck = true; + break; + } + } + + for (let i = 0; i < Config.BaalAssistant.NextGameMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.NextGameMessage[i])) { + ngCheck = true; + killTracker = true; + break; + } + } + } + }; + + const baalDeathEvent = function (bytes = []) { + if (!bytes.length || bytes.length !== 2) return; + + if (bytes[0] === sdk.packets.recv.UniqueEvents && bytes[1] === 0x13) { + baalIsDead = true; + } + }; + + const checkParty = function () { + for (let i = 0; i < Config.BaalAssistant.Wait; i += 1) { + let partycheck = getParty(); + if (partycheck) { + do { + if (partycheck.area === sdk.areas.ThroneofDestruction) return false; + if (partycheck.area === sdk.areas.RiverofFlame || partycheck.area === sdk.areas.ChaosSanctuary) return true; + } while (partycheck.getNext()); + } + + delay(1000); + } + + return false; + }; + + // Start + const Worker = require("../modules/Worker"); + + if (Leader) { + if (!Misc.poll(() => Misc.inMyParty(Leader), Time.seconds(30), 1000)) { + throw new Error("BaalAssistant: Leader not partied"); + } + } + + try { + addEventListener("chatmsg", chatEvent); + + let killLeaderTracker = false; + if (!Leader && (Config.BaalAssistant.KillNihlathak || Config.BaalAssistant.FastChaos)) { + // run background auto detect so we don't miss messages while running add ons + let leadTick = getTickCount(); + + Worker.runInBackground.leaderTracker = function () { + if (killLeaderTracker || killTracker) return false; + // check every 3 seconds + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) return false; + + let party = getParty(); + + if (party) { + do { + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { + Leader = party.name; + console.log(sdk.colors.DarkGold + "Autodected " + Leader); + return false; + } + } while (party.getNext()); + } + + return true; + }; + } + + Config.BaalAssistant.KillNihlathak && Loader.runScript("Nihlathak"); + Config.BaalAssistant.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); + + Town.goToTown(5); + Town.doChores(); + + if (Leader + || (Leader = Misc.autoLeaderDetect({ + destination: sdk.areas.WorldstoneLvl3, + quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area) + })) + || (Leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ThroneofDestruction, + quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) + }))) { + print("ÿc hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); + + if (!hotCheck) { + print("ÿc1Leader didn't tell me to start hunting for an experience shrine."); + ShrineStatus = true; + } + } + + // don't waste time looking for seal if party is already killing baal, he'll be dead by the time we find one + if (!ShrineStatus && !baalCheck) { + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + let i; + + for (i = sdk.areas.StonyField; i > sdk.areas.RogueEncampment; i -= 1) { + if (safeCheck) { + break; + } + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } + } + + if (!safeCheck) { + if (i === sdk.areas.RogueEncampment) { + Town.goToTown(); + Pather.useWaypoint(sdk.areas.DarkWood); + Precast.doPrecast(true); + + for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i += 1) { + if (safeCheck) { + break; + } + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } + } + } + } + } + + Town.goToTown(5); + ShrineStatus = true; + } + + if (firstAttempt && !secondAttempt && !safeCheck + && !baalCheck && !me.inArea(sdk.areas.ThroneofDestruction) + && !me.inArea(sdk.areas.WorldstoneChamber)) { + !!Config.RandomPrecast + ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) + : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + } + + if (!me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { + if (Config.BaalAssistant.SkipTP) { + if (firstAttempt && !secondAttempt) { + !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { + throw new Error("Failed to move to WSK3."); + } + + checkParty(); + let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); + if (entrance) { + let [x, y] = [ + entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, + entrance.y > me.y ? entrance.y - 5 : entrance.y + 5 + ]; + Pather.moveTo(x, y); + } + + if (!Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) || !Pather.moveTo(15118, 5002)) { + throw new Error("Failed to move to Throne of Destruction."); + } + + Pather.moveToEx(15095, 5029, { callback: () => { + if (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + console.log("Undead Soul Killers found, ending script."); + throw new ScriptError("Undead Soul Killers found, ending script."); + } + + if (Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + console.log("Burning Souls found, ending script."); + throw new ScriptError("Burning Souls found, ending script."); + } + } }); + + Pather.moveTo(15118, 5002); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + + secondAttempt = true; + safeCheck = true; + } else { + if (me.inTown) { + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + } + } + } else { + if (firstAttempt && !secondAttempt) { + !me.inArea(sdk.areas.Harrogath) && Pather.useWaypoint(sdk.areas.Harrogath); + Town.move("portalspot"); + + if (Config.BaalAssistant.WaitForSafeTP + && !Misc.poll(() => safeCheck, Time.seconds(Config.BaalAssistant.Wait), 1000)) { + throw new Error("No safe TP message."); + } + + if ((Config.BaalAssistant.SoulQuit || Config.BaalAssistant.DollQuit) && quitFlag) { + throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); + } + + if (!Misc.poll( + () => Pather.usePortal(sdk.areas.ThroneofDestruction, null), + Time.seconds(Config.BaalAssistant.Wait), + 1000 + )) { + throw new Error("No portals to Throne."); + } + + if ((Config.BaalAssistant.SoulQuit + && Game.getMonster(sdk.monsters.BurningSoul1)) + || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { + throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); + } + + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + secondAttempt = true; + safeCheck = true; + } else { + if (me.inTown) { + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + } + } + } + } + + if (safeCheck && !baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { + if (!baalCheck && !throneStatus) { + if (Helper) { + Attack.clear(15); + Common.Baal.clearThrone(); + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + Precast.doPrecast(true); + } + + let tick = getTickCount(); + + MainLoop: while (true) { + if (Helper) { + if (getDistance(me, 15094, me.paladin ? 5029 : 5038) > 3) { + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + } + } + + if (!Game.getMonster(sdk.monsters.ThroneBaal)) { + break; + } + + switch (Common.Baal.checkThrone(Helper)) { + case 1: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 2: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 4: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 3: + Helper && Attack.clear(40) && Common.Baal.checkHydra(); + tick = getTickCount(); + + break; + case 5: + if (Helper) { + Attack.clear(40); + } else { + while ([sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2] + .map((unitId) => Game.getMonster(unitId)) + .filter(Boolean).some((unit) => unit.attackable)) { + delay(1000); + } + + delay(1000); + } + + break MainLoop; + default: + if (getTickCount() - tick < 7e3) { + if (me.paladin && me.getState(sdk.states.Poison) + && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + break; + } + } + + if (Helper && !Common.Baal.preattack()) { + delay(100); + } + + break; + } + delay(10); + } + throneStatus = true; + baalCheck = true; + } + } + + if ((throneStatus || baalCheck) + && Config.BaalAssistant.KillBaal + && me.inArea(sdk.areas.ThroneofDestruction)) { + Helper ? Pather.moveTo(15090, 5008) && delay(2000) : Pather.moveTo(15090, 5010); + Precast.doPrecast(true); + !Helper && addEventListener("gamepacket", baalDeathEvent); + + while (Game.getMonster(sdk.monsters.ThroneBaal)) { + delay(500); + } + + let portal = Game.getObject(sdk.objects.WorldstonePortal); + + if (portal) { + delay((Helper ? 1000 : 4000)); + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); + } + + if (Helper) { + delay(1000); + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); + } else { + Pather.moveTo(15177, 5952); + let baal = Game.getMonster(sdk.monsters.Baal); + + while (!!baal && baal.attackable && !baalIsDead) { + delay(1000); + } + } + + } else { + // how to accurately know when to end script in the instance of no ngCheck + // listen for baal death packet maybe? + while (!ngCheck && !baalIsDead) { + delay(500); + } + } + + delay(500); + } + } catch (e) { + console.error(e); + } finally { + killTracker = true; + } + } else { + throw new Error("Empty game."); + } + } finally { + removeEventListener("chatmsg", chatEvent); + removeEventListener("gamepacket", baalDeathEvent); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 3aec326bf..766baf15b 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -6,109 +6,127 @@ */ function BaalHelper() { - include("core/Common/Baal.js"); - Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); - Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); - - Town.goToTown(5); - Town.doChores(); - Config.RandomPrecast && Precast.needOutOfTownCast() ? Precast.doRandomPrecast(true, sdk.areas.Harrogath) : Precast.doPrecast(true); - - if (Config.BaalHelper.SkipTP) { - !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) throw new Error("Failed to move to WSK3."); - if (!Misc.poll(() => { - let party = getParty(); - - if (party) { - do { - if ((!Config.Leader || party.name === Config.Leader) && party.area === sdk.areas.ThroneofDestruction) { - return true; - } - } while (party.getNext()); - } - - return false; - }, Time.minutes(Config.BaalHelper.Wait), 1000)) throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Throne)"); - - let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); - entrance && Pather.moveTo(entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, entrance.y > me.y ? entrance.y - 5 : entrance.y + 5); - - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) throw new Error("Failed to move to WSK3."); - if (!Pather.moveToExit(sdk.areas.ThroneofDestruction, true)) throw new Error("Failed to move to Throne of Destruction."); - Pather.moveToEx(15113, 5040, { callback: () => { - if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - console.log("Undead Soul Killers found, ending script."); - throw new ScriptError("Undead Soul Killers found, ending script."); - } - - if (Config.BaalHelper.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { - console.log("Burning Souls found, ending script."); - throw new ScriptError("Burning Souls found, ending script."); - } - }}); - } else { - Town.goToTown(5); - Town.move("portalspot"); - - let quitFlag = false; - - const chatEvent = function (nick, msg) { - if (nick === Config.Leader) { - if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") + include("core/Common/Baal.js"); + Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); + Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); + + Town.goToTown(5); + Town.doChores(); + Config.RandomPrecast && Precast.needOutOfTownCast() + ? Precast.doRandomPrecast(true, sdk.areas.Harrogath) + : Precast.doPrecast(true); + + if (Config.BaalHelper.SkipTP) { + !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { + throw new Error("Failed to move to WSK3."); + } + if (!Misc.poll(() => { + let party = getParty(); + + if (party) { + do { + if ((!Config.Leader || party.name === Config.Leader) && party.area === sdk.areas.ThroneofDestruction) { + return true; + } + } while (party.getNext()); + } + + return false; + }, Time.minutes(Config.BaalHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Throne)"); + } + + let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); + if (entrance) { + let [x, y] = [ + entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, + entrance.y > me.y ? entrance.y - 5 : entrance.y + 5 + ]; + Pather.moveTo(x, y); + } + + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { + throw new Error("Failed to move to WSK3."); + } + if (!Pather.moveToExit(sdk.areas.ThroneofDestruction, true)) { + throw new Error("Failed to move to Throne of Destruction."); + } + Pather.moveToEx(15113, 5040, { callback: () => { + if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + console.log("Undead Soul Killers found, ending script."); + throw new ScriptError("Undead Soul Killers found, ending script."); + } + + if (Config.BaalHelper.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + console.log("Burning Souls found, ending script."); + throw new ScriptError("Burning Souls found, ending script."); + } + } }); + } else { + Town.goToTown(5); + Town.move("portalspot"); + + let quitFlag = false; + + const chatEvent = function (nick, msg) { + if (nick === Config.Leader) { + if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { - quitFlag = true; - } - } - }; - - if (Config.BaalHelper.DollQuit || Config.BaalHelper.SoulQuit) { - addEventListener("chatmsg", chatEvent); - } - - try { - if (!Misc.poll(() => { - if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { - if (quitFlag) throw new ScriptError("Burning Souls or Dolls found, ending script."); - if (Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { - return true; - } - } - - return false; - }, Time.minutes(Config.BaalHelper.Wait), 1000)) throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); - } catch (e) { - console.log(e.message); - - return true; - } finally { - removeEventListener("chatmsg", chatEvent); - } - } - - if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - print("Undead Soul Killers found."); - - return true; - } - - Precast.doPrecast(false); - Attack.clear(15); - Common.Baal.clearThrone(); + quitFlag = true; + } + } + }; + + if (Config.BaalHelper.DollQuit || Config.BaalHelper.SoulQuit) { + addEventListener("chatmsg", chatEvent); + } + + try { + if (!Misc.poll(() => { + if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + if (quitFlag) throw new ScriptError("Burning Souls or Dolls found, ending script."); + if (Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + return true; + } + } + + return false; + }, Time.minutes(Config.BaalHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + } + } catch (e) { + console.log(e.message); + + return true; + } finally { + removeEventListener("chatmsg", chatEvent); + } + } + + if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + print("Undead Soul Killers found."); + + return true; + } + + Precast.doPrecast(false); + Attack.clear(15); + Common.Baal.clearThrone(); - if (!Common.Baal.clearWaves()) { - throw new Error("Couldn't clear baal waves"); - } - - if (Config.BaalHelper.KillBaal) { - Common.Baal.killBaal(); - } else { - Town.goToTown(); - while (true) { - delay(500); - } - } - - return true; + if (!Common.Baal.clearWaves()) { + throw new Error("Couldn't clear baal waves"); + } + + if (Config.BaalHelper.KillBaal) { + Common.Baal.killBaal(); + } else { + Town.goToTown(); + while (true) { + delay(500); + } + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/BattleOrders.js b/d2bs/kolbot/libs/scripts/BattleOrders.js index edff2e6c4..e520286e2 100644 --- a/d2bs/kolbot/libs/scripts/BattleOrders.js +++ b/d2bs/kolbot/libs/scripts/BattleOrders.js @@ -9,249 +9,265 @@ // todo - use profile <-> profile communication so we don't need to set char names, Maybe shout global? function BattleOrders () { - this.gaveBo = false; - this.totalBoed = []; - - const boMode = { - Give: 0, - Receive: 1 - }; - - // convert all names in getter to lowercase - Config.BattleOrders.Getters.forEach((name, index) => { - Config.BattleOrders.Getters[index] = name.toLowerCase(); - }); - - function checkForPlayers () { - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - } - - function log (msg = "") { - console.log(msg); - me.overhead(msg); - } - - function tardy () { - let party; - - AreaInfoLoop: - while (true) { - try { - checkForPlayers(); - } catch (e) { - if (Config.BattleOrders.Wait) { - let counter = 0; - print("Waiting " + Config.BattleOrders.Wait + " seconds for other players..."); - - Misc.poll(() => { - counter++; - me.overhead("Waiting " + Math.round(((tick + Time.seconds(Config.BattleOrders.Wait)) - getTickCount()) / 1000) + " Seconds for other players"); - if (counter % 5 === 0) { - return checkForPlayers(); - } - return false; - }, Time.seconds(Config.BattleOrders.Wait), Time.seconds(1)); - - continue; - } else { - console.error(e); - // emptry game, don't wait - return true; - } - } - - party = getParty(); - - if (party) { - do { - if (party.name !== me.name && party.area) { - break AreaInfoLoop; // Can read player area - } - } while (party.getNext()); - } - - delay(500); - } - - if (party) { - do { - if ([sdk.areas.MooMooFarm, sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { - log("ÿc1I'm late to BOs. Moving on..."); - - return true; - } - } while (party.getNext()); - } - - return false; // Not late; wait. - } - - // bo is AoE, lets build a list of all players near us so we can know who we boed - function giveBO () { - // more players might be showing up, give a moment and lets wait until the nearby player count is static - let nearPlayers = 0; - let tick = getTickCount(); + this.gaveBo = false; + this.totalBoed = []; + + const boMode = { + Give: 0, + Receive: 1 + }; + + // convert all names in getter to lowercase + Config.BattleOrders.Getters.forEach((name, index) => { + Config.BattleOrders.Getters[index] = name.toLowerCase(); + }); + + function checkForPlayers () { + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + } + + function log (msg = "") { + console.log(msg); + me.overhead(msg); + } + + function tardy () { + let party; + + AreaInfoLoop: + while (true) { + try { + checkForPlayers(); + } catch (e) { + if (Config.BattleOrders.Wait) { + let counter = 0; + print("Waiting " + Config.BattleOrders.Wait + " seconds for other players..."); + + Misc.poll(() => { + counter++; + me.overhead( + "Waiting " + Math.round(((tick + Time.seconds(Config.BattleOrders.Wait)) - getTickCount()) / 1000) + + " Seconds for other players" + ); + if (counter % 5 === 0) { + return checkForPlayers(); + } + return false; + }, Time.seconds(Config.BattleOrders.Wait), Time.seconds(1)); + + continue; + } else { + console.error(e); + // emptry game, don't wait + return true; + } + } + + party = getParty(); + + if (party) { + do { + if (party.name !== me.name && party.area) { + break AreaInfoLoop; // Can read player area + } + } while (party.getNext()); + } + + delay(500); + } + + if (party) { + do { + if ([ + sdk.areas.MooMooFarm, sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber + ].includes(party.area)) { + log("ÿc1I'm late to BOs. Moving on..."); + + return true; + } + } while (party.getNext()); + } + + return false; // Not late; wait. + } + + // bo is AoE, lets build a list of all players near us so we can know who we boed + function giveBO () { + // more players might be showing up, give a moment and lets wait until the nearby player count is static + let nearPlayers = 0; + let tick = getTickCount(); - // if we haven't already given a bo, lets wait to see if more players show up - if (!BattleOrders.gaveBo) { - nearPlayers = Misc.getNearbyPlayerCount(); - while (nearPlayers !== Config.BattleOrders.Getters.length) { - if (getTickCount() - tick >= Time.seconds(30)) { - log("Begin"); - - break; - } - - me.overhead("Waiting " + Math.round(((tick + Time.seconds(30)) - getTickCount()) / 1000) + " for all players to show up"); - nearPlayers = Misc.getNearbyPlayerCount(); - delay(1000); - } - } - - let boed = false; - let playersToBo = getUnits(sdk.unittype.Player) - .filter(p => Config.BattleOrders.Getters.includes(p.name.toLowerCase()) && p.distance < 20); - playersToBo.forEach(p => { - tick = getTickCount(); - - if (copyUnit(p).x) { - while (!p.getState(sdk.states.BattleOrders) && copyUnit(p).x) { - if (getTickCount() - tick >= Time.minutes(1)) { - log("ÿc1BO timeout fail."); - - if (Config.BattleOrders.QuitOnFailure) { - quit(); - } - - break; - } - - Precast.doPrecast(true); - delay(1000); - } - - this.totalBoed.indexOf(p.name.toLowerCase()) === -1 && this.totalBoed.push(p.name.toLowerCase()); - console.debug("Bo-ed " + p.name); - boed = true; - } - }); - - if (boed) { - delay(5000); - } - - return { - success: boed, - count: playersToBo.length - }; - } - - // START - Town.doChores(); - - try { - Pather.useWaypoint(sdk.areas.CatacombsLvl2, true); - } catch (wperror) { - log("ÿc1Failed to take waypoint."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - - return false; - } - - // don't bo until we are ready to do so - Precast.enabled = false; - Pather.moveTo(me.x + 6, me.y + 6); - - let tick = getTickCount(); - let failTimer = Time.minutes(2); - let nearPlayer; - - // Ready - Precast.enabled = true; - - MainLoop: - while (true) { - if (Config.BattleOrders.SkipIfTardy && tardy()) { - break; - } - - switch (Config.BattleOrders.Mode) { - case boMode.Give: - // check if anyone is near us - nearPlayer = Game.getPlayer(); - - if (nearPlayer) { - do { - if (nearPlayer.name !== me.name) { - let nearPlayerName = nearPlayer.name.toLowerCase(); - // there is a player near us and they are in the list of players to bo and in my party - if (Config.BattleOrders.Getters.includes(nearPlayerName) && !this.totalBoed.includes(nearPlayerName) && Misc.inMyParty(nearPlayerName)) { - let result = giveBO(); - if (result.success) { - if (result.count === Config.BattleOrders.Getters.length || this.totalBoed.length === Config.BattleOrders.Getters.length) { - // we bo-ed everyone we are set to, don't wait around any longer - break MainLoop; - } - // reset fail tick - tick = getTickCount(); - // shorten waiting time since we've already started giving out bo's - BattleOrders.gaveBo = true; - } - } - } else { - me.overhead("Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + " Seconds for other players"); - - if (getTickCount() - tick >= failTimer) { - log("ÿc1Give BO timeout fail."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - - break MainLoop; - } - } - } while (nearPlayer.getNext()); - } else { - me.overhead("Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + " Seconds for other players"); - - if (getTickCount() - tick >= failTimer) { - log("ÿc1Give BO timeout fail."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - - break MainLoop; - } - } - - break; - case boMode.Receive: - if (me.getState(sdk.states.BattleOrders)) { - log("Got bo-ed"); - delay(1000); - - break MainLoop; - } - - if (getTickCount() - tick >= failTimer) { - log("ÿc1BO timeout fail."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - - break MainLoop; - } - - break; - } - - delay(500); - } - - (Pather.useWaypoint(sdk.areas.RogueEncampment) || Town.goToTown()); - - // what's the point of this? - if (Config.BattleOrders.Mode === boMode.Give && Config.BattleOrders.Idle) { - for (let i = 0; i < Config.BattleOrders.Getters.length; i += 1) { - while (Misc.inMyParty(Config.BattleOrders.Getters[i])) { - delay(1000); - } - } - } - - return true; + // if we haven't already given a bo, lets wait to see if more players show up + if (!BattleOrders.gaveBo) { + nearPlayers = Misc.getNearbyPlayerCount(); + while (nearPlayers !== Config.BattleOrders.Getters.length) { + if (getTickCount() - tick >= Time.seconds(30)) { + log("Begin"); + + break; + } + + me.overhead( + "Waiting " + Math.round(((tick + Time.seconds(30)) - getTickCount()) / 1000) + + " for all players to show up" + ); + nearPlayers = Misc.getNearbyPlayerCount(); + delay(1000); + } + } + + let boed = false; + let playersToBo = getUnits(sdk.unittype.Player) + .filter(p => Config.BattleOrders.Getters.includes(p.name.toLowerCase()) && p.distance < 20); + playersToBo.forEach(p => { + tick = getTickCount(); + + if (copyUnit(p).x) { + while (!p.getState(sdk.states.BattleOrders) && copyUnit(p).x) { + if (getTickCount() - tick >= Time.minutes(1)) { + log("ÿc1BO timeout fail."); + + if (Config.BattleOrders.QuitOnFailure) { + quit(); + } + + break; + } + + Precast.doPrecast(true); + delay(1000); + } + + this.totalBoed.indexOf(p.name.toLowerCase()) === -1 && this.totalBoed.push(p.name.toLowerCase()); + console.debug("Bo-ed " + p.name); + boed = true; + } + }); + + if (boed) { + delay(5000); + } + + return { + success: boed, + count: playersToBo.length + }; + } + + // START + Town.doChores(); + + try { + Pather.useWaypoint(sdk.areas.CatacombsLvl2, true); + } catch (wperror) { + log("ÿc1Failed to take waypoint."); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + + return false; + } + + // don't bo until we are ready to do so + Precast.enabled = false; + Pather.moveTo(me.x + 6, me.y + 6); + + let tick = getTickCount(); + let failTimer = Time.minutes(2); + let nearPlayer; + + // Ready + Precast.enabled = true; + + MainLoop: + while (true) { + if (Config.BattleOrders.SkipIfTardy && tardy()) { + break; + } + + switch (Config.BattleOrders.Mode) { + case boMode.Give: + // check if anyone is near us + nearPlayer = Game.getPlayer(); + + if (nearPlayer) { + do { + if (nearPlayer.name !== me.name) { + let nearPlayerName = nearPlayer.name.toLowerCase(); + // there is a player near us and they are in the list of players to bo and in my party + if (Config.BattleOrders.Getters.includes(nearPlayerName) + && !this.totalBoed.includes(nearPlayerName) && Misc.inMyParty(nearPlayerName)) { + let result = giveBO(); + if (result.success) { + if (result.count === Config.BattleOrders.Getters.length + || this.totalBoed.length === Config.BattleOrders.Getters.length) { + // we bo-ed everyone we are set to, don't wait around any longer + break MainLoop; + } + // reset fail tick + tick = getTickCount(); + // shorten waiting time since we've already started giving out bo's + BattleOrders.gaveBo = true; + } + } + } else { + me.overhead( + "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + + " Seconds for other players" + ); + + if (getTickCount() - tick >= failTimer) { + log("ÿc1Give BO timeout fail."); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + + break MainLoop; + } + } + } while (nearPlayer.getNext()); + } else { + me.overhead( + "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + + " Seconds for other players" + ); + + if (getTickCount() - tick >= failTimer) { + log("ÿc1Give BO timeout fail."); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + + break MainLoop; + } + } + + break; + case boMode.Receive: + if (me.getState(sdk.states.BattleOrders)) { + log("Got bo-ed"); + delay(1000); + + break MainLoop; + } + + if (getTickCount() - tick >= failTimer) { + log("ÿc1BO timeout fail."); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + + break MainLoop; + } + + break; + } + + delay(500); + } + + (Pather.useWaypoint(sdk.areas.RogueEncampment) || Town.goToTown()); + + // what's the point of this? + if (Config.BattleOrders.Mode === boMode.Give && Config.BattleOrders.Idle) { + for (let i = 0; i < Config.BattleOrders.Getters.length; i += 1) { + while (Misc.inMyParty(Config.BattleOrders.Getters[i])) { + delay(1000); + } + } + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js index 7c3df77fa..cb0562cc3 100644 --- a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js +++ b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js @@ -6,17 +6,17 @@ */ function BattlemaidSarina() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.KurastBazaar); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.KurastBazaar); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) + if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { - throw new Error("Failed to move near Sarina"); - } + throw new Error("Failed to move near Sarina"); + } - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.BattlemaidSarina)); - Pickit.pickItems(); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.BattlemaidSarina)); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Bishibosh.js b/d2bs/kolbot/libs/scripts/Bishibosh.js index b00339252..055f3d35a 100644 --- a/d2bs/kolbot/libs/scripts/Bishibosh.js +++ b/d2bs/kolbot/libs/scripts/Bishibosh.js @@ -6,13 +6,13 @@ */ function Bishibosh() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); + 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/d2bs/kolbot/libs/scripts/BoBarbHelper.js b/d2bs/kolbot/libs/scripts/BoBarbHelper.js index 9e2fa3e9c..9aaab9302 100644 --- a/d2bs/kolbot/libs/scripts/BoBarbHelper.js +++ b/d2bs/kolbot/libs/scripts/BoBarbHelper.js @@ -7,98 +7,98 @@ */ function BoBarbHelper () { - if (!me.barbarian && Config.BoBarbHelper.Mode !== 0) return true; + if (!me.barbarian && Config.BoBarbHelper.Mode !== 0) return true; - const townNearbyMonster = true; // go to town if monsters nearby - const townLowMana = 20; // go refill mana if mana drops below this percent - const shouldHealMana = amount => me.mp < Math.floor(me.mpmax * amount / 100); + const townNearbyMonster = true; // go to town if monsters nearby + const townLowMana = 20; // go refill mana if mana drops below this percent + const shouldHealMana = amount => me.mp < Math.floor(me.mpmax * amount / 100); - const healMana = () => { - Pather.useWaypoint(sdk.areas.RogueEncampment); - Town.initNPC("Heal", "heal"); - Pather.useWaypoint(Config.BoBarbHelper.Wp); - }; + const healMana = () => { + Pather.useWaypoint(sdk.areas.RogueEncampment); + Town.initNPC("Heal", "heal"); + Pather.useWaypoint(Config.BoBarbHelper.Wp); + }; - const shouldBuff = unit => ( - Misc.inMyParty(unit) && + const shouldBuff = unit => ( + Misc.inMyParty(unit) && getDistance(me, unit) < 10 && unit.name !== me.name && !unit.dead && !unit.inTown - ); - - const giveBuff = () => { - const unit = Game.getPlayer(); - - do { - if (shouldBuff(unit)) { - Precast.doPrecast(true); - delay(50); - } - } while (unit.getNext()); - }; - - const monsterNear = () => { - const unit = Game.getMonster(); - - if (unit) { - do { - if (unit.attackable && getDistance(me, unit) < 20) { - return true; - } - } while (unit.getNext()); - } - - return false; - }; - - if (!Config.QuitList) { - showConsole(); - print("set Config.QuitList in character settings"); - print("if you don't I will idle indefinitely"); - } - - if (me.hardcore && Config.LifeChicken <= 0) { - showConsole(); - print("on HARDCORE"); - print("you should set Config.LifeChicken"); - print("monsters can find their way to wps ..."); - delay(2000); - hideConsole(); - me.overhead("set LifeChiken to 40"); - Config.LifeChicken = 40; - } - - shouldHealMana(townLowMana) && Town.initNPC("Heal", "heal"); - Town.heal(); // in case our life is low as well + ); + + const giveBuff = () => { + const unit = Game.getPlayer(); + + do { + if (shouldBuff(unit)) { + Precast.doPrecast(true); + delay(50); + } + } while (unit.getNext()); + }; + + const monsterNear = () => { + const unit = Game.getMonster(); + + if (unit) { + do { + if (unit.attackable && getDistance(me, unit) < 20) { + return true; + } + } while (unit.getNext()); + } + + return false; + }; + + if (!Config.QuitList) { + showConsole(); + print("set Config.QuitList in character settings"); + print("if you don't I will idle indefinitely"); + } + + if (me.hardcore && Config.LifeChicken <= 0) { + showConsole(); + print("on HARDCORE"); + print("you should set Config.LifeChicken"); + print("monsters can find their way to wps ..."); + delay(2000); + hideConsole(); + me.overhead("set LifeChiken to 40"); + Config.LifeChicken = 40; + } + + shouldHealMana(townLowMana) && Town.initNPC("Heal", "heal"); + Town.heal(); // in case our life is low as well - try { - Pather.useWaypoint(Config.BoBarbHelper.Wp); - } catch (e) { - showConsole(); - print("Failed to move to BO WP"); - print("make sure I have " + getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); - delay(20000); + try { + Pather.useWaypoint(Config.BoBarbHelper.Wp); + } catch (e) { + showConsole(); + print("Failed to move to BO WP"); + print("make sure I have " + getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); + delay(20000); - return true; - } + return true; + } - Pather.moveTo(me.x + 4, me.y + 4); + Pather.moveTo(me.x + 4, me.y + 4); - while (true) { - giveBuff(); + while (true) { + giveBuff(); - if (townNearbyMonster && monsterNear()) { - if (!Pather.useWaypoint(sdk.areas.RogueEncampment)) { - break; - } - } + if (townNearbyMonster && monsterNear()) { + if (!Pather.useWaypoint(sdk.areas.RogueEncampment)) { + break; + } + } - shouldHealMana(townLowMana) && healMana(); - delay(25); - } + shouldHealMana(townLowMana) && healMana(); + delay(25); + } - Town.goToTown(); + Town.goToTown(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/BoneAsh.js b/d2bs/kolbot/libs/scripts/BoneAsh.js index c226c2aa3..b5d70ce83 100644 --- a/d2bs/kolbot/libs/scripts/BoneAsh.js +++ b/d2bs/kolbot/libs/scripts/BoneAsh.js @@ -6,14 +6,14 @@ */ function BoneAsh() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); - if (!Pather.moveTo(20047, 4898)) throw new Error("Failed to move to Bone Ash"); + if (!Pather.moveTo(20047, 4898)) throw new Error("Failed to move to Bone Ash"); - Attack.kill(getLocaleString(sdk.locale.monsters.BoneAsh)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.BoneAsh)); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Bonesaw.js b/d2bs/kolbot/libs/scripts/Bonesaw.js index be04e3789..00eb61387 100644 --- a/d2bs/kolbot/libs/scripts/Bonesaw.js +++ b/d2bs/kolbot/libs/scripts/Bonesaw.js @@ -6,14 +6,17 @@ */ function Bonesaw() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.GlacialTrail); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.GlacialTrail); + Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.GlacialTrail, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 15, 15)) throw new Error("Failed to move to Bonesaw"); + if (!Pather.moveToPreset(sdk.areas.GlacialTrail, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 15, 15)) { + throw new Error("Failed to move to Bonesaw"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.BonesawBreaker)); - Config.Bonesaw.ClearDrifterCavern && Pather.moveToExit(sdk.areas.DrifterCavern, true) && Attack.clearLevel(Config.ClearType); - - return true; + Attack.kill(getLocaleString(sdk.locale.monsters.BonesawBreaker)); + if (Config.Bonesaw.ClearDrifterCavern && Pather.moveToExit(sdk.areas.DrifterCavern, true)) { + Attack.clearLevel(Config.ClearType); + } + return true; } diff --git a/d2bs/kolbot/libs/scripts/ChestMania.js b/d2bs/kolbot/libs/scripts/ChestMania.js index 9c3dda1d8..984446d58 100644 --- a/d2bs/kolbot/libs/scripts/ChestMania.js +++ b/d2bs/kolbot/libs/scripts/ChestMania.js @@ -8,24 +8,27 @@ // todo - if we have run ghostsbusters before this then some of these areas don't need to be re-run function ChestMania() { - Town.doChores(); + Town.doChores(); - for (let prop in Config.ChestMania) { - if (Config.ChestMania.hasOwnProperty(prop)) { - for (let i = 0; i < Config.ChestMania[prop].length; i += 1) { - const nextArea = Config.ChestMania[prop][i]; - if ([sdk.areas.BloodMoor, sdk.areas.RockyWaste, sdk.areas.SpiderForest, sdk.areas.OuterSteppes, sdk.areas.BloodyFoothills].includes(nextArea)) { - // if we precast as soon as we step out of town it sometimes crashes - so do precast somewhere else first - Precast.doRandomPrecast(false); - } - Pather.journeyTo(Config.ChestMania[prop][i]); - Precast.doPrecast(false); - Misc.openChestsInArea(Config.ChestMania[prop][i]); - } + for (let prop in Config.ChestMania) { + if (Config.ChestMania.hasOwnProperty(prop)) { + for (let i = 0; i < Config.ChestMania[prop].length; i += 1) { + const nextArea = Config.ChestMania[prop][i]; + if ([ + sdk.areas.BloodMoor, sdk.areas.RockyWaste, + sdk.areas.SpiderForest, sdk.areas.OuterSteppes, sdk.areas.BloodyFoothills + ].includes(nextArea)) { + // if we precast as soon as we step out of town it sometimes crashes - so do precast somewhere else first + Precast.doRandomPrecast(false); + } + Pather.journeyTo(Config.ChestMania[prop][i]); + Precast.doPrecast(false); + Misc.openChestsInArea(Config.ChestMania[prop][i]); + } - Town.doChores(); - } - } + Town.doChores(); + } + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js index 23a1043a0..4d6ec1169 100644 --- a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js +++ b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js @@ -8,123 +8,123 @@ // redo this, maybe different keys or chat commands instead? function ClassicChaosAssistant() { - include("core/Common/Diablo.js"); - let stargo, infgo, seisgo, vizgo, infseal, seisseal, vizseal, diablopickup, normalpickup = false; + include("core/Common/Diablo.js"); + let stargo, infgo, seisgo, vizgo, infseal, seisseal, vizseal, diablopickup, normalpickup = false; - addEventListener("keyup", - function (key) { - switch (key) { - case sdk.keys.Numpad1: - stargo = true; + addEventListener("keyup", + function (key) { + switch (key) { + case sdk.keys.Numpad1: + stargo = true; - break; - case sdk.keys.Numpad2: - infgo = true; + break; + case sdk.keys.Numpad2: + infgo = true; - break; - case sdk.keys.Numpad3: - infseal = true; + break; + case sdk.keys.Numpad3: + infseal = true; - break; - case sdk.keys.Numpad4: - seisgo = true; + break; + case sdk.keys.Numpad4: + seisgo = true; - break; - case sdk.keys.Numpad5: - seisseal = true; + break; + case sdk.keys.Numpad5: + seisseal = true; - break; - case sdk.keys.Numpad6: - vizgo = true; + break; + case sdk.keys.Numpad6: + vizgo = true; - break; - case sdk.keys.Numpad7: - vizseal = true; + break; + case sdk.keys.Numpad7: + vizseal = true; - break; - case sdk.keys.Numpad8: // (Open last seal, teleport to star and pickup for 30 seconds) - diablopickup = true; + break; + case sdk.keys.Numpad8: // (Open last seal, teleport to star and pickup for 30 seconds) + diablopickup = true; - break; - case sdk.keys.Numpad9: // (Pickup at current location) - normalpickup = true; + break; + case sdk.keys.Numpad9: // (Pickup at current location) + normalpickup = true; - break; - default: - break; - } - }); + break; + default: + break; + } + }); - while (true) { - switch (me.area) { - case sdk.areas.ChaosSanctuary: - if (infgo) { - Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); - Pather.makePortal() && say("Infector of Souls TP Up!"); - infgo = false; - } + while (true) { + switch (me.area) { + case sdk.areas.ChaosSanctuary: + if (infgo) { + Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); + Pather.makePortal() && say("Infector of Souls TP Up!"); + infgo = false; + } - if (seisgo) { - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); - Pather.makePortal() && say("Lord De Seis TP Up!"); - seisgo = false; - } + if (seisgo) { + Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); + Pather.makePortal() && say("Lord De Seis TP Up!"); + seisgo = false; + } - if (vizgo) { - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); - Pather.makePortal() && say("Grand Vizier of Chaos TP Up!"); - vizgo = false; - } + if (vizgo) { + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); + Pather.makePortal() && say("Grand Vizier of Chaos TP Up!"); + vizgo = false; + } - if (infseal) { - Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2); - Common.Diablo.openSeal(sdk.objects.DiabloSealInfector) && say("Infector of Souls spawned!"); - Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); - infseal = false; - } + if (infseal) { + Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2); + Common.Diablo.openSeal(sdk.objects.DiabloSealInfector) && say("Infector of Souls spawned!"); + Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); + infseal = false; + } - if (seisseal) { - Common.Diablo.openSeal(sdk.objects.DiabloSealSeis) && say("Lord De Seis spawned!"); - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); - seisseal = false; - } + if (seisseal) { + Common.Diablo.openSeal(sdk.objects.DiabloSealSeis) && say("Lord De Seis spawned!"); + Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); + seisseal = false; + } - if (vizseal) { - Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2) && say("Grand Vizier of Chaos spawned!"); - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); - vizseal = false; - } + if (vizseal) { + Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2) && say("Grand Vizier of Chaos spawned!"); + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); + vizseal = false; + } - if (diablopickup) { - Common.Diablo.openSeal(sdk.objects.DiabloSealVizier); - Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); - for (let i = 0; i < 300; i += 1) { - Pickit.pickItems(); - delay(100); - } - diablopickup = false; - } + if (diablopickup) { + Common.Diablo.openSeal(sdk.objects.DiabloSealVizier); + Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); + for (let i = 0; i < 300; i += 1) { + Pickit.pickItems(); + delay(100); + } + diablopickup = false; + } - if (normalpickup) { - Pickit.pickItems(); - normalpickup = false; - } + if (normalpickup) { + Pickit.pickItems(); + normalpickup = false; + } - break; - default: - if (stargo) { - if (me.inArea(sdk.areas.RiverofFlame)) { - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); - Common.Diablo.initLayout(); - break; - } - stargo = false; - } + break; + default: + if (stargo) { + if (me.inArea(sdk.areas.RiverofFlame)) { + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); + Common.Diablo.initLayout(); + break; + } + stargo = false; + } - break; - } + break; + } - delay(10); - } + delay(10); + } } diff --git a/d2bs/kolbot/libs/scripts/ClearAnyArea.js b/d2bs/kolbot/libs/scripts/ClearAnyArea.js index d98f317ee..281c7cc5b 100644 --- a/d2bs/kolbot/libs/scripts/ClearAnyArea.js +++ b/d2bs/kolbot/libs/scripts/ClearAnyArea.js @@ -6,15 +6,15 @@ */ function ClearAnyArea() { - Town.doChores(); + Town.doChores(); - for (let i = 0; i < Config.ClearAnyArea.AreaList.length; i += 1) { - try { - Pather.journeyTo(Config.ClearAnyArea.AreaList[i]) && Attack.clearLevel(Config.ClearType); - } catch (e) { - console.error(e); - } - } + for (let i = 0; i < Config.ClearAnyArea.AreaList.length; i += 1) { + try { + Pather.journeyTo(Config.ClearAnyArea.AreaList[i]) && Attack.clearLevel(Config.ClearType); + } catch (e) { + console.error(e); + } + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Coldcrow.js b/d2bs/kolbot/libs/scripts/Coldcrow.js index 462016930..83fccbc5a 100644 --- a/d2bs/kolbot/libs/scripts/Coldcrow.js +++ b/d2bs/kolbot/libs/scripts/Coldcrow.js @@ -6,14 +6,16 @@ */ function Coldcrow() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.CaveLvl1, true, false)) throw new Error("Failed to move to Cave"); - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Coldcrow, 0, 0, false)) throw new Error("Failed to move to Coldcrow"); + if (!Pather.moveToExit(sdk.areas.CaveLvl1, true, false)) throw new Error("Failed to move to Cave"); + if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Coldcrow, 0, 0, false)) { + throw new Error("Failed to move to Coldcrow"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.Coldcrow)); + Attack.kill(getLocaleString(sdk.locale.monsters.Coldcrow)); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Coldworm.js b/d2bs/kolbot/libs/scripts/Coldworm.js index fd03ba66a..e2d007fc5 100644 --- a/d2bs/kolbot/libs/scripts/Coldworm.js +++ b/d2bs/kolbot/libs/scripts/Coldworm.js @@ -6,28 +6,34 @@ */ function Coldworm() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FarOasis); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FarOasis); + Precast.doPrecast(true); - // Beetleburst, added by 13ack.Stab - if (Config.Coldworm.KillBeetleburst) { - try { - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Beetleburst)) throw new Error("Failed to move to Beetleburst"); - Attack.kill(getLocaleString(sdk.locale.monsters.Beetleburst)); - } catch (e) { - console.error(e); // not the main part of this script so simply log and move on - } - } + // Beetleburst, added by 13ack.Stab + if (Config.Coldworm.KillBeetleburst) { + try { + if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Beetleburst)) { + throw new Error("Failed to move to Beetleburst"); + } + Attack.kill(getLocaleString(sdk.locale.monsters.Beetleburst)); + } catch (e) { + console.error(e); // not the main part of this script so simply log and move on + } + } - if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true)) throw new Error("Failed to move to Coldworm"); + if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true)) { + throw new Error("Failed to move to Coldworm"); + } - if (Config.Coldworm.ClearMaggotLair) { - Attack.clearLevel(Config.ClearType); - } else { - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) throw new Error("Failed to move to Coldworm"); - Attack.kill(sdk.monsters.ColdwormtheBurrower); - } + if (Config.Coldworm.ClearMaggotLair) { + Attack.clearLevel(Config.ClearType); + } else { + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { + throw new Error("Failed to move to Coldworm"); + } + Attack.kill(sdk.monsters.ColdwormtheBurrower); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index e62fb2ffc..c2a8ae914 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -7,692 +7,710 @@ */ function ControlBot() { - const startTime = getTickCount(); + const startTime = getTickCount(); - /** + /** * @type {Object.} */ - const cmdNicks = {}; - /** + const cmdNicks = {}; + /** * @type {Object.} */ - const wpNicks = {}; - - let command, nick; - let shitList = []; - const greet = []; - - let controlCommands = ["help", "timeleft", "cows", "wps", "chant", "bo"]; - const commandDesc = { - "help": "Display commands", - "timeleft": "Remaining time left for this game", - "cows": "Open cow level", - "chant": "Enchant. AutoChant is " + (Config.ControlBot.Chant.AutoEnchant ? "ON" : "OFF"), - "wps": "Give waypoints", - "bo": "Bo at waypoint", - }; - - // remove commands we can't/aren't using - for (let i = 0; i < controlCommands.length; i++) { - switch (controlCommands[i]) { - case "cows": - if (!Config.ControlBot.Cows.MakeCows) { - controlCommands.splice(i, 1); - i--; - } - - break; - case "chant": - if (!Config.ControlBot.Chant.Enchant || !me.getSkill(sdk.skills.Enchant, sdk.skills.subindex.SoftPoints)) { - Config.ControlBot.Chant.Enchant = false; - Config.ControlBot.Chant.AutoEnchant = false; - controlCommands.splice(i, 1); - i--; - } - - break; - case "wps": - if (!Config.ControlBot.Wps.GiveWps) { - controlCommands.splice(i, 1); - i--; - } - - break; - case "bo": - if (!Config.ControlBot.Bo || (!me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) && Precast.haveCTA === -1)) { - Config.ControlBot.Bo = false; - controlCommands.splice(i, 1); - i--; - } - - break; - } - } - - /** + const wpNicks = {}; + + let command, nick; + let shitList = []; + const greet = []; + + let controlCommands = ["help", "timeleft", "cows", "wps", "chant", "bo"]; + const commandDesc = { + "help": "Display commands", + "timeleft": "Remaining time left for this game", + "cows": "Open cow level", + "chant": "Enchant. AutoChant is " + (Config.ControlBot.Chant.AutoEnchant ? "ON" : "OFF"), + "wps": "Give waypoints", + "bo": "Bo at waypoint", + }; + + // remove commands we can't/aren't using + for (let i = 0; i < controlCommands.length; i++) { + switch (controlCommands[i]) { + case "cows": + if (!Config.ControlBot.Cows.MakeCows) { + controlCommands.splice(i, 1); + i--; + } + + break; + case "chant": + if (!Config.ControlBot.Chant.Enchant || !me.getSkill(sdk.skills.Enchant, sdk.skills.subindex.SoftPoints)) { + Config.ControlBot.Chant.Enchant = false; + Config.ControlBot.Chant.AutoEnchant = false; + controlCommands.splice(i, 1); + i--; + } + + break; + case "wps": + if (!Config.ControlBot.Wps.GiveWps) { + controlCommands.splice(i, 1); + i--; + } + + break; + case "bo": + if (!Config.ControlBot.Bo + || (!me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) + && Precast.haveCTA === -1)) { + Config.ControlBot.Bo = false; + controlCommands.splice(i, 1); + i--; + } + + break; + } + } + + /** * @param {string} nick * @returns {boolean} */ - const enchant = function (nick) { - if (!Config.ControlBot.Chant.Enchant) return false; + const enchant = function (nick) { + if (!Config.ControlBot.Chant.Enchant) return false; - if (!Misc.inMyParty(nick)) { - say("Accept party invite, noob."); + if (!Misc.inMyParty(nick)) { + say("Accept party invite, noob."); - return false; - } + return false; + } - let unit = Game.getPlayer(nick); + let unit = Game.getPlayer(nick); - if (unit && unit.distance > 35) { - say("Get closer."); + if (unit && unit.distance > 35) { + say("Get closer."); - return false; - } + return false; + } - if (!unit) { - let partyUnit = getParty(nick); + if (!unit) { + let partyUnit = getParty(nick); - // wait until party area is readable? - if (partyUnit.inTown) { - say("Wait for me at waypoint."); - Town.goToTown(sdk.areas.actOf(partyUnit.area)); + // wait until party area is readable? + if (partyUnit.inTown) { + say("Wait for me at waypoint."); + Town.goToTown(sdk.areas.actOf(partyUnit.area)); - unit = Game.getPlayer(nick); - } else { - say("You need to be in one of the towns."); + unit = Game.getPlayer(nick); + } else { + say("You need to be in one of the towns."); - return false; - } - } + return false; + } + } - if (unit) { - do { - // player is alive - if (!unit.dead) { - if (unit.distance >= 35) { - say("You went too far away."); + if (unit) { + do { + // player is alive + if (!unit.dead) { + if (unit.distance >= 35) { + say("You went too far away."); - return false; - } + return false; + } - Packet.enchant(unit); - delay(500); - } - } while (unit.getNext()); - } else { - say("I don't see you"); - } + Packet.enchant(unit); + delay(500); + } + } while (unit.getNext()); + } else { + say("I don't see you"); + } - unit = Game.getMonster(); + unit = Game.getMonster(); - if (unit) { - do { - // merc or any other owned unit - if (unit.getParent() && unit.getParent().name === nick) { - Packet.enchant(unit); - delay(500); - } - } while (unit.getNext()); - } + if (unit) { + do { + // merc or any other owned unit + if (unit.getParent() && unit.getParent().name === nick) { + Packet.enchant(unit); + delay(500); + } + } while (unit.getNext()); + } - return true; - }; + return true; + }; - /** + /** * @param {string} nick * @returns {boolean} */ - const bo = function (nick) { - if (!Config.ControlBot.Bo) return false; - - if (!Misc.inMyParty(nick)) { - say("Accept party invite, noob."); - - return false; - } - - let partyUnit = getParty(nick); - - // wait until party area is readable? - if (partyUnit.inTown) { - say("Can't bo you in town noob, go to a waypoint"); - - return false; - } else if (Pather.wpAreas.includes(partyUnit.area)) { - Pather.useWaypoint(partyUnit.area); - } else { - say("Can't find you or you're not somewhere with a waypoint"); - - return false; - } - - let unit = Game.getPlayer(nick); - - if (unit && unit.distance > 15) { - say("Get closer."); - let waitTick = getTickCount(); - - while (unit && unit.distance > 15) { - if (getTickCount() - waitTick > 30e3) { - say("You took to long. Going back to town"); - return false; - } - delay(150); - } - } - - if (unit && unit.distance <= 15 && !unit.dead) { - Misc.poll(function () { - Precast.doPrecast(true); - return unit.getState(sdk.states.BattleOrders); - }, 5000, 1000); - Pather.useWaypoint(sdk.areas.RogueEncampment); - } else { - say("I don't see you"); - } - - return true; - }; - - /** + const bo = function (nick) { + if (!Config.ControlBot.Bo) return false; + + if (!Misc.inMyParty(nick)) { + say("Accept party invite, noob."); + + return false; + } + + let partyUnit = getParty(nick); + + // wait until party area is readable? + if (partyUnit.inTown) { + say("Can't bo you in town noob, go to a waypoint"); + + return false; + } else if (Pather.wpAreas.includes(partyUnit.area)) { + Pather.useWaypoint(partyUnit.area); + } else { + say("Can't find you or you're not somewhere with a waypoint"); + + return false; + } + + let unit = Game.getPlayer(nick); + + if (unit && unit.distance > 15) { + say("Get closer."); + let waitTick = getTickCount(); + + while (unit && unit.distance > 15) { + if (getTickCount() - waitTick > 30e3) { + say("You took to long. Going back to town"); + return false; + } + delay(150); + } + } + + if (unit && unit.distance <= 15 && !unit.dead) { + Misc.poll(function () { + Precast.doPrecast(true); + return unit.getState(sdk.states.BattleOrders); + }, 5000, 1000); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } else { + say("I don't see you"); + } + + return true; + }; + + /** * @type {Map= chantDuration - Time.minutes(1))) { + Packet.enchant(unit); + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + chantList.set(unit.name, { lastChant: getTickCount() }); + } + } + } + } while (unit.getNext()); + } + + unit = Game.getMonster(); + + if (unit) { + do { + if (unit.getParent() + && chanted.includes(unit.getParent().name) + && !unit.getState(sdk.states.Enchant) + && unit.distance <= 40) { + Packet.enchant(unit); + // not going to re-enchant the minions for now though, will think on how best to handle that later + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + } + } + } while (unit.getNext()); + } + + return true; + }; + + const getLeg = function () { + if (me.getItem(sdk.quest.item.WirtsLeg)) { + return me.getItem(sdk.quest.item.WirtsLeg); + } + + let leg, gid, wrongLeg; + + if (!Config.ControlBot.Cows.GetLeg) { + leg = Game.getItem(sdk.items.quest.WirtsLeg); + + if (leg) { + do { + if (leg.name.includes("ÿc1")) { + wrongLeg = true; + } else if (leg.distance <= 15) { + gid = leg.gid; + Pickit.pickItem(leg); + + return me.getItem(-1, -1, gid); + } + } while (leg.getNext()); + } + + say("Bring the leg " + (wrongLeg ? "from this difficulty" : "") + " close to me."); + + return false; + } + + if (!Pather.journeyTo(sdk.areas.Tristram)) { + say("Failed to enter Tristram :("); + Town.goToTown(); + + return false; + } + + Pather.moveTo(25048, 5177); + + let wirt = Game.getObject(sdk.quest.chest.Wirt); + + for (let i = 0; i < 8; i += 1) { + wirt.interact(); + delay(500); + + leg = Game.getItem(sdk.quest.item.WirtsLeg); + + if (leg) { + gid = leg.gid; + + Pickit.pickItem(leg); + Town.goToTown(); + + return me.getItem(-1, -1, gid); + } + } + + Town.goToTown(); + say("Failed to get the leg :("); + + return false; + }; + + const getTome = function () { + let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + + if (tpTome.length < 2) { + let npc = Town.initNPC("Shop", "buyTpTome"); + if (!getInteractedNPC()) throw new Error("Failed to find npc"); + + let 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); + tpTome.forEach(function (book) { + if (book.isInInventory) { + let scroll = npc.getItem(sdk.items.ScrollofTownPortal); + + while (book.getStat(sdk.stats.Quantity) < 20) { + if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { + scroll.buy(true); + } else { + break; + } + + delay(20); + } + } + }); + } else { + throw new Error("Failed to buy tome"); + } + } - const autoChant = function () { - if (!Config.ControlBot.Chant.Enchant) return false; + return tpTome.last(); + }; - let chanted = []; - let unit = Game.getPlayer(); - - if (unit) { - do { - if (unit.name !== me.name && !unit.dead && shitList.indexOf(unit.name) === -1 && Misc.inMyParty(unit.name) && unit.distance <= 40) { - // allow rechanting someone if it's going to run out soon for them - if ((!unit.getState(sdk.states.Enchant) || (chantList.has(unit.name) && getTickCount() - chantList.get(unit.name).lastChant) >= chantDuration - Time.minutes(1))) { - Packet.enchant(unit); - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); - chantList.set(unit.name, { lastChant: getTickCount() }); - } - } - } - } while (unit.getNext()); - } - - unit = Game.getMonster(); - - if (unit) { - do { - if (unit.getParent() && chanted.includes(unit.getParent().name) && !unit.getState(sdk.states.Enchant) && unit.distance <= 40) { - Packet.enchant(unit); - // not going to re-enchant the minions for now though, will think on how best to handle that later - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); - } - } - } while (unit.getNext()); - } - - return true; - }; - - const getLeg = function () { - if (me.getItem(sdk.quest.item.WirtsLeg)) { - return me.getItem(sdk.quest.item.WirtsLeg); - } - - let leg, gid, wrongLeg; - - if (!Config.ControlBot.Cows.GetLeg) { - leg = Game.getItem(sdk.items.quest.WirtsLeg); - - if (leg) { - do { - if (leg.name.includes("ÿc1")) { - wrongLeg = true; - } else if (leg.distance <= 15) { - gid = leg.gid; - Pickit.pickItem(leg); - - return me.getItem(-1, -1, gid); - } - } while (leg.getNext()); - } - - say("Bring the leg " + (wrongLeg ? "from this difficulty" : "") + " close to me."); - - return false; - } - - if (!Pather.journeyTo(sdk.areas.Tristram)) { - say("Failed to enter Tristram :("); - Town.goToTown(); - - return false; - } - - Pather.moveTo(25048, 5177); - - let wirt = Game.getObject(sdk.quest.chest.Wirt); - - for (let i = 0; i < 8; i += 1) { - wirt.interact(); - delay(500); - - leg = Game.getItem(sdk.quest.item.WirtsLeg); - - if (leg) { - gid = leg.gid; - - Pickit.pickItem(leg); - Town.goToTown(); - - return me.getItem(-1, -1, gid); - } - } - - Town.goToTown(); - say("Failed to get the leg :("); - - return false; - }; - - const getTome = function () { - let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (tpTome.length < 2) { - let npc = Town.initNPC("Shop", "buyTpTome"); - if (!getInteractedNPC()) throw new Error("Failed to find npc"); - - let 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); - tpTome.forEach(function (book) { - if (book.isInInventory) { - let scroll = npc.getItem(sdk.items.ScrollofTownPortal); - - while (book.getStat(sdk.stats.Quantity) < 20) { - if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { - scroll.buy(true); - } else { - break; - } - - delay(20); - } - } - }); - } else { - throw new Error("Failed to buy tome"); - } - } - - return tpTome.last(); - }; - - /** + /** * @param {string} nick * @returns {boolean} */ - const openPortal = function (nick) { - if (!Config.ControlBot.Cows.MakeCows) return false; - try { - if (!Misc.inMyParty(nick)) throw new Error("Accept party invite, noob."); - if (Pather.getPortal(sdk.areas.MooMooFarm)) throw new Error("Cow portal already open."); - // king dead or cain not saved - if (me.cows) throw new Error("Can't open the portal because I killed Cow King."); - if (Config.ControlBot.Cows.GetLeg && !me.tristram && !!Config.Leader && !getParty(Config.Leader)) { - throw new Error("Can't get leg because I don't have Cain quest."); - } - if (!me.diffCompleted) throw new Error("Final quest incomplete."); - } catch (e) { - say(e.message ? e.message : e); - return false; - } - - let leg = getLeg(); - if (!leg) return false; - - let tome = getTome(); - if (!tome) return false; - - if (!Town.openStash() || !Cubing.emptyCube() || !Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome) || !Cubing.openCube()) { - return false; - } - - transmute(); - delay(500); - - for (let i = 0; i < 10; i += 1) { - if (Pather.getPortal(sdk.areas.MooMooFarm)) { - return true; - } - - delay(200); - } - - say("Failed to open cow portal."); - - return false; - }; - - /** + const openPortal = function (nick) { + if (!Config.ControlBot.Cows.MakeCows) return false; + try { + if (!Misc.inMyParty(nick)) throw new Error("Accept party invite, noob."); + if (Pather.getPortal(sdk.areas.MooMooFarm)) throw new Error("Cow portal already open."); + // king dead or cain not saved + if (me.cows) throw new Error("Can't open the portal because I killed Cow King."); + if (Config.ControlBot.Cows.GetLeg && !me.tristram && !!Config.Leader && !getParty(Config.Leader)) { + throw new Error("Can't get leg because I don't have Cain quest."); + } + if (!me.diffCompleted) throw new Error("Final quest incomplete."); + } catch (e) { + say(e.message ? e.message : e); + return false; + } + + let leg = getLeg(); + if (!leg) return false; + + let tome = getTome(); + if (!tome) return false; + + if (!Town.openStash() + || !Cubing.emptyCube() + || !Storage.Cube.MoveTo(leg) + || !Storage.Cube.MoveTo(tome) + || !Cubing.openCube()) { + return false; + } + + transmute(); + delay(500); + + for (let i = 0; i < 10; i += 1) { + if (Pather.getPortal(sdk.areas.MooMooFarm)) { + return true; + } + + delay(200); + } + + say("Failed to open cow portal."); + + return false; + }; + + /** * @param {string} nick * @returns {string | boolean} */ - const getWpNick = function (nick) { - if (wpNicks.hasOwnProperty(nick)) { - if (wpNicks[nick].requests > 4) { - return "maxrequests"; - } + const getWpNick = function (nick) { + if (wpNicks.hasOwnProperty(nick)) { + if (wpNicks[nick].requests > 4) { + return "maxrequests"; + } - if (getTickCount() - wpNicks[nick].timer < 60000) { - return "mintime"; - } + if (getTickCount() - wpNicks[nick].timer < 60000) { + return "mintime"; + } - return true; - } + return true; + } - return false; - }; + return false; + }; - /** + /** * @param {string} nick * @returns {void} */ - const addWpNick = function (nick) { - wpNicks[nick] = { timer: getTickCount(), requests: 0 }; - }; + const addWpNick = function (nick) { + wpNicks[nick] = { timer: getTickCount(), requests: 0 }; + }; - /** + /** * @param {string} nick * @returns {boolean} */ - const giveWps = function (nick) { - if (!Config.ControlBot.Wps.GiveWps) return false; - if (!Misc.inMyParty(nick)) { - say("Accept party invite, noob."); - - return false; - } - - switch (getWpNick(nick)) { - case "maxrequests": - say(nick + ", you have spent all your waypoint requests for this game."); - - return false; - case "mintime": - say(nick + ", you may request waypoints every 60 seconds."); - - return false; - case false: - addWpNick(nick); - - break; - } - - let act = Misc.getPlayerAct(nick); - const wps = { - 1: [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.OuterCloister, sdk.areas.JailLvl1, sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2 - ], - 2: [ - sdk.areas.A2SewersLvl2, sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, - sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic - ], - 3: [ - sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, - sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2 - ], - 4: [sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame], - 5: [ - sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.CrystalizedPassage, - sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 - ] - }; - let wpList = wps[act]; - - for (let i = 0; i < wpList.length; i++) { - if (checkHostiles()) { - break; - } - - try { - Pather.useWaypoint(wpList[i], true); - Config.ControlBot.Wps.SecurePortal && Attack.securePosition(me.x, me.y, 20, 1000); - Pather.makePortal(); - say(getAreaName(me.area) + " TP up"); - - for (let timeout = 0; timeout < 20; timeout++) { - if (Game.getPlayer(nick)) { - break; - } - - delay(1000); - } - - if (timeout >= 20) { - say("Aborting wp giving."); - - break; - } - - delay(5000); - } catch (error) { - continue; - } - } - - Town.doChores(); - Town.goToTown(1); - Town.move("portalspot"); - - wpNicks[nick].requests += 1; - wpNicks[nick].timer = getTickCount(); - - return true; - }; - - const checkHostiles = function () { - let rval = false; - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8)) { - rval = true; - - if (Config.ShitList && shitList.indexOf(party.name) === -1) { - shitList.push(party.name); - } - } - } while (party.getNext()); - } - - return rval; - }; - - /** + const giveWps = function (nick) { + if (!Config.ControlBot.Wps.GiveWps) return false; + if (!Misc.inMyParty(nick)) { + say("Accept party invite, noob."); + + return false; + } + + switch (getWpNick(nick)) { + case "maxrequests": + say(nick + ", you have spent all your waypoint requests for this game."); + + return false; + case "mintime": + say(nick + ", you may request waypoints every 60 seconds."); + + return false; + case false: + addWpNick(nick); + + break; + } + + let act = Misc.getPlayerAct(nick); + const wps = { + 1: [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.DarkWood, sdk.areas.BlackMarsh, + sdk.areas.OuterCloister, sdk.areas.JailLvl1, sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2 + ], + 2: [ + sdk.areas.A2SewersLvl2, sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, + sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic + ], + 3: [ + sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, + sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2 + ], + 4: [sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame], + 5: [ + sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.CrystalizedPassage, + sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 + ] + }; + let wpList = wps[act]; + + for (let i = 0; i < wpList.length; i++) { + if (checkHostiles()) { + break; + } + + try { + Pather.useWaypoint(wpList[i], true); + Config.ControlBot.Wps.SecurePortal && Attack.securePosition(me.x, me.y, 20, 1000); + Pather.makePortal(); + say(getAreaName(me.area) + " TP up"); + + for (let timeout = 0; timeout < 20; timeout++) { + if (Game.getPlayer(nick)) { + break; + } + + delay(1000); + } + + if (timeout >= 20) { + say("Aborting wp giving."); + + break; + } + + delay(5000); + } catch (error) { + continue; + } + } + + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); + + wpNicks[nick].requests += 1; + wpNicks[nick].timer = getTickCount(); + + return true; + }; + + const checkHostiles = function () { + let rval = false; + let party = getParty(); + + if (party) { + do { + if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8)) { + rval = true; + + if (Config.ShitList && shitList.indexOf(party.name) === -1) { + shitList.push(party.name); + } + } + } while (party.getNext()); + } + + return rval; + }; + + /** * @param {string} command * @returns {boolean} */ - const floodCheck = function (command) { - if (!command || command.length < 2) return false; - let [cmd, nick] = command; + const floodCheck = function (command) { + if (!command || command.length < 2) return false; + let [cmd, nick] = command; - // ignore overhead messages - if (!nick) return true; - // ignore messages not related to our commands - if (controlCommands.indexOf(cmd.toLowerCase()) === -1) return false; - - if (!cmdNicks.hasOwnProperty(nick)) { - cmdNicks[nick] = { - firstCmd: getTickCount(), - commands: 0, - ignored: false - }; - } - - if (cmdNicks[nick].ignored) { - if (getTickCount() - cmdNicks[nick].ignored < 60000) { - return true; // ignore flooder - } - - // unignore flooder - cmdNicks[nick].ignored = false; - cmdNicks[nick].commands = 0; - } - - cmdNicks[nick].commands += 1; - - if (getTickCount() - cmdNicks[nick].firstCmd < 10000) { - if (cmdNicks[nick].commands > 5) { - cmdNicks[nick].ignored = getTickCount(); - - say(nick + ", you are being ignored for 60 seconds because of flooding."); - } - } else { - cmdNicks[nick].firstCmd = getTickCount(); - cmdNicks[nick].commands = 0; - } - - return false; - }; - - /** + // ignore overhead messages + if (!nick) return true; + // ignore messages not related to our commands + if (controlCommands.indexOf(cmd.toLowerCase()) === -1) return false; + + if (!cmdNicks.hasOwnProperty(nick)) { + cmdNicks[nick] = { + firstCmd: getTickCount(), + commands: 0, + ignored: false + }; + } + + if (cmdNicks[nick].ignored) { + if (getTickCount() - cmdNicks[nick].ignored < 60000) { + return true; // ignore flooder + } + + // unignore flooder + cmdNicks[nick].ignored = false; + cmdNicks[nick].commands = 0; + } + + cmdNicks[nick].commands += 1; + + if (getTickCount() - cmdNicks[nick].firstCmd < 10000) { + if (cmdNicks[nick].commands > 5) { + cmdNicks[nick].ignored = getTickCount(); + + say(nick + ", you are being ignored for 60 seconds because of flooding."); + } + } else { + cmdNicks[nick].firstCmd = getTickCount(); + cmdNicks[nick].commands = 0; + } + + return false; + }; + + /** * @param {string} nick * @param {string} msg * @returns {boolean} */ - function chatEvent(nick, msg) { - if (shitList.includes(nick)) { - say("No commands for the shitlisted."); - - return; - } + function chatEvent(nick, msg) { + if (shitList.includes(nick)) { + say("No commands for the shitlisted."); + + return; + } - command = [msg, nick]; - } + command = [msg, nick]; + } - // eslint-disable-next-line no-unused-vars - function gameEvent(mode, param1, param2, name1, name2) { - switch (mode) { - case 0x02: - // idle in town - me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); + // eslint-disable-next-line no-unused-vars + function gameEvent(mode, param1, param2, name1, name2) { + switch (mode) { + case 0x02: + // idle in town + me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); - break; - } - } + break; + } + } - // START - include("oog/ShitList.js"); - Config.ShitList && (shitList = ShitList.read()); + // START + include("oog/ShitList.js"); + Config.ShitList && (shitList = ShitList.read()); - try { - addEventListener("chatmsg", chatEvent); - addEventListener("gameevent", gameEvent); - Town.doChores(); - Town.goToTown(1); - Town.move("portalspot"); + try { + addEventListener("chatmsg", chatEvent); + addEventListener("gameevent", gameEvent); + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); - const spot = { x: me.x, y: me.y }; + const spot = { x: me.x, y: me.y }; - while (true) { - while (greet.length > 0) { - nick = greet.shift(); + while (true) { + while (greet.length > 0) { + nick = greet.shift(); - if (shitList.indexOf(nick) === -1) { - say("Welcome, " + nick + "! For a list of commands say 'help'"); - } - } + if (shitList.indexOf(nick) === -1) { + say("Welcome, " + nick + "! For a list of commands say 'help'"); + } + } - spot.distance > 10 && Pather.moveTo(spot.x, spot.y); + spot.distance > 10 && Pather.moveTo(spot.x, spot.y); - if (command && !floodCheck(command)) { - let hostile = checkHostiles(); + if (command && !floodCheck(command)) { + let hostile = checkHostiles(); - switch (command[0].toLowerCase()) { - case "help": - let str = ""; - controlCommands.forEach((cmd) => { - str += (cmd + " (" + commandDesc[cmd] + "), "); - }); + switch (command[0].toLowerCase()) { + case "help": + let str = ""; + controlCommands.forEach((cmd) => { + str += (cmd + " (" + commandDesc[cmd] + "), "); + }); - say("Commands:"); - say(str); + say("Commands:"); + say(str); - break; - case "timeleft": - let tick = Time.minutes(Config.ControlBot.GameLength) - getTickCount() + startTime; - let m = Math.floor(tick / 60000); - let s = Math.floor((tick / 1000) % 60); + break; + case "timeleft": + let tick = Time.minutes(Config.ControlBot.GameLength) - getTickCount() + startTime; + let m = Math.floor(tick / 60000); + let s = Math.floor((tick / 1000) % 60); - say("Time left: " + (m ? m + " minute" + (m > 1 ? "s" : "") + ", " : "") + s + " second" + (s > 1 ? "s." : ".")); + say( + "Time left: " + + (m ? m + " minute" + (m > 1 ? "s" : "") + + ", " : "") + s + " second" + (s > 1 ? "s." : ".") + ); - break; - case "chant": - enchant(command[1]); + break; + case "chant": + enchant(command[1]); - break; - case "cows": - if (hostile) { - say("Command disabled because of hostiles."); + break; + case "cows": + if (hostile) { + say("Command disabled because of hostiles."); - break; - } + break; + } - openPortal(command[1]); - me.cancel(); + openPortal(command[1]); + me.cancel(); - break; - case "wps": - if (hostile) { - say("Command disabled because of hostiles."); + break; + case "wps": + if (hostile) { + say("Command disabled because of hostiles."); - break; - } + break; + } - giveWps(command[1]); + giveWps(command[1]); - break; - case "bo": - if (hostile) { - say("Command disabled because of hostiles."); + break; + case "bo": + if (hostile) { + say("Command disabled because of hostiles."); - break; - } + break; + } - bo(command[1]); + bo(command[1]); - break; - } - } + break; + } + } - command = ""; + command = ""; - me.act > 1 && Town.goToTown(1); - Config.ControlBot.Chant.AutoEnchant && autoChant(); + me.act > 1 && Town.goToTown(1); + Config.ControlBot.Chant.AutoEnchant && autoChant(); - if (getTickCount() - startTime >= Time.minutes(Config.ControlBot.GameLength)) { - say((Config.ControlBot.EndMessage ? Config.ControlBot.EndMessage : "Bye")); - delay(1000); + if (getTickCount() - startTime >= Time.minutes(Config.ControlBot.GameLength)) { + say((Config.ControlBot.EndMessage ? Config.ControlBot.EndMessage : "Bye")); + delay(1000); - break; - } + break; + } - delay(200); - } - } finally { - removeEventListener("chatmsg", chatEvent); - removeEventListener("gameevent", gameEvent); - } + delay(200); + } + } finally { + removeEventListener("chatmsg", chatEvent); + removeEventListener("gameevent", gameEvent); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js index 3c80b1690..6d80632ce 100644 --- a/d2bs/kolbot/libs/scripts/Corpsefire.js +++ b/d2bs/kolbot/libs/scripts/Corpsefire.js @@ -6,17 +6,17 @@ */ function Corpsefire() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true) + if (!Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true) || !Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true)) { - throw new Error("Failed to move to Corpsefire"); - } + throw new Error("Failed to move to Corpsefire"); + } - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Corpsefire)); - Config.Corpsefire.ClearDen && Attack.clearLevel(); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Corpsefire)); + Config.Corpsefire.ClearDen && Attack.clearLevel(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Countess.js b/d2bs/kolbot/libs/scripts/Countess.js index 5417d7652..6c6b64713 100644 --- a/d2bs/kolbot/libs/scripts/Countess.js +++ b/d2bs/kolbot/libs/scripts/Countess.js @@ -6,30 +6,30 @@ */ function Countess() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.BlackMarsh); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.BlackMarsh); + Precast.doPrecast(true); - if (!Pather.moveToExit([ - sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, - sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5 - ], true)) throw new Error("Failed to move to Countess"); + if (!Pather.moveToExit([ + sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, + sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5 + ], true)) throw new Error("Failed to move to Countess"); - let poi = Game.getPresetObject(me.area, sdk.objects.SuperChest); + let poi = Game.getPresetObject(me.area, sdk.objects.SuperChest); - if (!poi) throw new Error("Failed to move to Countess (preset not found)"); + if (!poi) throw new Error("Failed to move to Countess (preset not found)"); - switch (poi.roomx * 5 + poi.x) { - case 12565: - Pather.moveTo(12578, 11043); - break; - case 12526: - Pather.moveTo(12548, 11083); - break; - } + switch (poi.roomx * 5 + poi.x) { + case 12565: + Pather.moveTo(12578, 11043); + break; + case 12526: + Pather.moveTo(12548, 11083); + break; + } - Attack.clear(20, 0, getLocaleString(sdk.locale.monsters.TheCountess)); - Config.OpenChests.Enabled && Misc.openChestsInArea(); + Attack.clear(20, 0, getLocaleString(sdk.locale.monsters.TheCountess)); + Config.OpenChests.Enabled && Misc.openChestsInArea(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Cows.js b/d2bs/kolbot/libs/scripts/Cows.js index 8ac0e3a11..61fa57bbf 100644 --- a/d2bs/kolbot/libs/scripts/Cows.js +++ b/d2bs/kolbot/libs/scripts/Cows.js @@ -6,148 +6,150 @@ */ function Cows() { - include("core/Common/Cows.js"); + include("core/Common/Cows.js"); - const getLeg = function () { - if (me.wirtsleg) return me.wirtsleg; + const getLeg = function () { + if (me.wirtsleg) return me.wirtsleg; - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); - if (!Misc.poll(() => { - let p = Pather.getPortal(sdk.areas.Tristram); - return (p && Pather.usePortal(sdk.areas.Tristram, null, p)); - }, Time.minutes(1), 1000)) { - throw new Error("Tristram portal not found"); - } - - Pather.moveTo(25048, 5177); - - let wirt = Game.getObject(sdk.quest.chest.Wirt); - - for (let i = 0; i < 8; i += 1) { - wirt.interact(); - delay(500); - - let leg = Game.getItem(sdk.quest.item.WirtsLeg); - - if (leg) { - let gid = leg.gid; - - Pickit.pickItem(leg); - Town.goToTown(); - - return me.getItem(-1, -1, gid); - } - } - - throw new Error("Failed to get the leg"); - }; - - const getTome = function () { - let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (tpTome.length < 2) { - let npc = Town.initNPC("Shop", "buyTpTome"); - - if (!getInteractedNPC()) { - throw new Error("Failed to find npc"); - } - - let 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); - tpTome.forEach(function (book) { - if (book.isInInventory) { - let scroll = npc.getItem(sdk.items.ScrollofTownPortal); - while (book.getStat(sdk.stats.Quantity) < 20) { - if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { - scroll.buy(true); - } else { - break; - } - - delay(20); - } - } - }); - } else { - throw new Error("Failed to buy tome"); - } - } - - return tpTome.last(); - }; - - const openPortal = function (leg, tome) { - if (!Town.openStash()) throw new Error("Failed to open stash"); - if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); - if (!Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome) || !Cubing.openCube()) { - throw new Error("Failed to cube leg and tome"); - } - - transmute(); - delay(1000); - me.cancelUIFlags(); - - for (let i = 0; i < 10; i += 1) { - if (Pather.getPortal(sdk.areas.MooMooFarm)) { - return true; - } - - delay(200); - } - - throw new Error("Portal not found"); - }; - - - // we can begin now - try { - if (!me.diffCompleted) throw new Error("Final quest incomplete."); - - Town.goToTown(1); - Town.doChores(); - Town.move("stash"); - - // Check to see if portal is already open, if not get the ingredients - if (!Pather.getPortal(sdk.areas.MooMooFarm)) { - if (Config.Cows.DontMakePortal) throw new Error("NOT PORTAL MAKER"); - if (!me.tristram) throw new Error("Cain quest incomplete"); - if (me.cows) throw new Error("Already killed the Cow King."); + if (!Misc.poll(() => { + let p = Pather.getPortal(sdk.areas.Tristram); + return (p && Pather.usePortal(sdk.areas.Tristram, null, p)); + }, Time.minutes(1), 1000)) { + throw new Error("Tristram portal not found"); + } + + Pather.moveTo(25048, 5177); + + let wirt = Game.getObject(sdk.quest.chest.Wirt); + + for (let i = 0; i < 8; i += 1) { + wirt.interact(); + delay(500); + + let leg = Game.getItem(sdk.quest.item.WirtsLeg); + + if (leg) { + let gid = leg.gid; + + Pickit.pickItem(leg); + Town.goToTown(); + + return me.getItem(-1, -1, gid); + } + } + + throw new Error("Failed to get the leg"); + }; + + const getTome = function () { + let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + + if (tpTome.length < 2) { + let npc = Town.initNPC("Shop", "buyTpTome"); + + if (!getInteractedNPC()) { + throw new Error("Failed to find npc"); + } + + let 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); + tpTome.forEach(function (book) { + if (book.isInInventory) { + let scroll = npc.getItem(sdk.items.ScrollofTownPortal); + while (book.getStat(sdk.stats.Quantity) < 20) { + if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { + scroll.buy(true); + } else { + break; + } + + delay(20); + } + } + }); + } else { + throw new Error("Failed to buy tome"); + } + } + + return tpTome.last(); + }; + + const openPortal = function (leg, tome) { + if (!Town.openStash()) throw new Error("Failed to open stash"); + if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); + if (!Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome) || !Cubing.openCube()) { + throw new Error("Failed to cube leg and tome"); + } + + transmute(); + delay(1000); + me.cancelUIFlags(); + + for (let i = 0; i < 10; i += 1) { + if (Pather.getPortal(sdk.areas.MooMooFarm)) { + return true; + } + + delay(200); + } + + throw new Error("Portal not found"); + }; + + + // we can begin now + try { + if (!me.diffCompleted) throw new Error("Final quest incomplete."); + + Town.goToTown(1); + Town.doChores(); + Town.move("stash"); + + // Check to see if portal is already open, if not get the ingredients + if (!Pather.getPortal(sdk.areas.MooMooFarm)) { + if (Config.Cows.DontMakePortal) throw new Error("NOT PORTAL MAKER"); + if (!me.tristram) throw new Error("Cain quest incomplete"); + if (me.cows) throw new Error("Already killed the Cow King."); - let leg = getLeg(); - let tome = getTome(); - openPortal(leg, tome); - } - } catch (e) { - typeof e === "object" && e.message && e.message !== "NOT PORTAL MAKER" && console.error(e); - - if (Misc.getPlayerCount() > 1) { - Town.goToTown(1); - Town.move("stash"); - console.log("ÿc9(Cows) :: ÿc0Waiting 1 minute to see if anyone else opens the cow portal"); - - if (!Misc.poll(() => Pather.getPortal(sdk.areas.MooMooFarm), Time.minutes(3), 2000)) throw new Error("No cow portal"); - } else { - return false; - } - } - - if (Config.Cows.JustMakePortal) { - if (Pather.getPortal(sdk.areas.MooMooFarm)) { - return true; - } else { - throw new Error("I failed to make cow portal"); - } - } - - Pather.usePortal(sdk.areas.MooMooFarm); - Precast.doPrecast(false); - Config.Cows.KillKing ? Attack.clearLevel() : Common.Cows.clearCowLevel(); - - return true; + let leg = getLeg(); + let tome = getTome(); + openPortal(leg, tome); + } + } catch (e) { + typeof e === "object" && e.message && e.message !== "NOT PORTAL MAKER" && console.error(e); + + if (Misc.getPlayerCount() > 1) { + Town.goToTown(1); + Town.move("stash"); + console.log("ÿc9(Cows) :: ÿc0Waiting 1 minute to see if anyone else opens the cow portal"); + + if (!Misc.poll(() => Pather.getPortal(sdk.areas.MooMooFarm), Time.minutes(3), 2000)) { + throw new Error("No cow portal"); + } + } else { + return false; + } + } + + if (Config.Cows.JustMakePortal) { + if (Pather.getPortal(sdk.areas.MooMooFarm)) { + return true; + } else { + throw new Error("I failed to make cow portal"); + } + } + + Pather.usePortal(sdk.areas.MooMooFarm); + Precast.doPrecast(false); + Config.Cows.KillKing ? Attack.clearLevel() : Common.Cows.clearCowLevel(); + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Crafting.js b/d2bs/kolbot/libs/scripts/Crafting.js index 12dc78abf..fea83f9ca 100644 --- a/d2bs/kolbot/libs/scripts/Crafting.js +++ b/d2bs/kolbot/libs/scripts/Crafting.js @@ -9,460 +9,460 @@ let info; let gameRequest = false; function Crafting() { - info = CraftingSystem.getInfo(); - - if (!info || !info.worker) throw new Error("Bad Crafting System config."); - - me.maxgametime = 0; - Town.goToTown(1); - Town.doChores(); - Town.move("stash"); - updateInfo(); - pickItems(); - - addEventListener("copydata", - function (mode, msg) { - let obj, rval; - - if (mode === 0) { - try { - obj = JSON.parse(msg); - } catch (e) { - return false; - } - - if (obj) { - switch (obj.name) { - case "GetGame": - if (info.Collectors.includes(obj.profile)) { - print("GetGame: " + obj.profile); - sendCopyData(null, obj.profile, 4, me.gamename + "/" + me.gamepassword); - - gameRequest = true; - } - - break; - case "GetSetInfo": - if (info.Collectors.includes(obj.profile)) { - print("GetSetInfo: " + obj.profile); - - rval = []; - - for (let i = 0; i < info.Sets.length; i += 1) { - rval.push(info.Sets[i].Enabled ? 1 : 0); - } - - print(rval); - - sendCopyData(null, obj.profile, 4, JSON.stringify({name: "SetInfo", value: rval})); - } - - break; - } - } - } - - return true; - }); - - for (let i = 0; i < Cubing.recipes.length; i += 1) { - Cubing.recipes[i].Level = 0; - } - - while (true) { - for (let i = 0; i < info.Sets.length; i += 1) { - switch (info.Sets[i].Type) { - case "crafting": - let num = 0; - let npcName = getNPCName(info.Sets[i].BaseItems); - - if (npcName) { - num = countItems(info.Sets[i].BaseItems, 4); - - if (num < info.Sets[i].SetAmount) { - shopStuff(npcName, info.Sets[i].BaseItems, info.Sets[i].SetAmount); - } - } - - break; - case "cubing": // Nothing to do currently - break; - case "runewords": // Nothing to do currently - break; - } - } - - me.act !== 1 && Town.goToTown(1) && Town.move("stash"); - - if (gameRequest) { - for (let i = 0; i < 10; i += 1) { - if (Misc.getPlayerCount() > 1) { - while (Misc.getPlayerCount() > 1) { - delay(200); - } - - break; - } else { - break; - } - } - - gameRequest = false; - } - - pickItems(); - Cubing.update(); - Runewords.buildLists(); - Cubing.doCubing(); - Runewords.makeRunewords(); - delay(2000); - } + info = CraftingSystem.getInfo(); + + if (!info || !info.worker) throw new Error("Bad Crafting System config."); + + me.maxgametime = 0; + Town.goToTown(1); + Town.doChores(); + Town.move("stash"); + updateInfo(); + pickItems(); + + addEventListener("copydata", + function (mode, msg) { + let obj, rval; + + if (mode === 0) { + try { + obj = JSON.parse(msg); + } catch (e) { + return false; + } + + if (obj) { + switch (obj.name) { + case "GetGame": + if (info.Collectors.includes(obj.profile)) { + print("GetGame: " + obj.profile); + sendCopyData(null, obj.profile, 4, me.gamename + "/" + me.gamepassword); + + gameRequest = true; + } + + break; + case "GetSetInfo": + if (info.Collectors.includes(obj.profile)) { + print("GetSetInfo: " + obj.profile); + + rval = []; + + for (let i = 0; i < info.Sets.length; i += 1) { + rval.push(info.Sets[i].Enabled ? 1 : 0); + } + + print(rval); + + sendCopyData(null, obj.profile, 4, JSON.stringify({ name: "SetInfo", value: rval })); + } + + break; + } + } + } + + return true; + }); + + for (let i = 0; i < Cubing.recipes.length; i += 1) { + Cubing.recipes[i].Level = 0; + } + + while (true) { + for (let i = 0; i < info.Sets.length; i += 1) { + switch (info.Sets[i].Type) { + case "crafting": + let num = 0; + let npcName = getNPCName(info.Sets[i].BaseItems); + + if (npcName) { + num = countItems(info.Sets[i].BaseItems, 4); + + if (num < info.Sets[i].SetAmount) { + shopStuff(npcName, info.Sets[i].BaseItems, info.Sets[i].SetAmount); + } + } + + break; + case "cubing": // Nothing to do currently + break; + case "runewords": // Nothing to do currently + break; + } + } + + me.act !== 1 && Town.goToTown(1) && Town.move("stash"); + + if (gameRequest) { + for (let i = 0; i < 10; i += 1) { + if (Misc.getPlayerCount() > 1) { + while (Misc.getPlayerCount() > 1) { + delay(200); + } + + break; + } else { + break; + } + } + + gameRequest = false; + } + + pickItems(); + Cubing.update(); + Runewords.buildLists(); + Cubing.doCubing(); + Runewords.makeRunewords(); + delay(2000); + } } function getNPCName(idList) { - for (let i = 0; i < idList.length; i += 1) { - switch (idList[i]) { - case sdk.items.LightBelt: - case sdk.items.SharkskinBelt: - return "elzix"; - case sdk.items.Belt: - case sdk.items.MeshBelt: - case sdk.items.LightPlatedBoots: - case sdk.items.BattleBoots: - return "fara"; - } - } - - return false; + for (let i = 0; i < idList.length; i += 1) { + switch (idList[i]) { + case sdk.items.LightBelt: + case sdk.items.SharkskinBelt: + return "elzix"; + case sdk.items.Belt: + case sdk.items.MeshBelt: + case sdk.items.LightPlatedBoots: + case sdk.items.BattleBoots: + return "fara"; + } + } + + return false; } function countItems(idList, quality) { - let count = 0; - let item = me.getItem(-1, sdk.items.mode.inStorage); - - if (item) { - do { - if (idList.includes(item.classid) && item.quality === quality) { - count += 1; - } - } while (item.getNext()); - } - - return count; + let count = 0; + let item = me.getItem(-1, sdk.items.mode.inStorage); + + if (item) { + do { + if (idList.includes(item.classid) && item.quality === quality) { + count += 1; + } + } while (item.getNext()); + } + + return count; } function updateInfo() { - if (info) { - let items = me.findItems(-1, sdk.items.mode.inStorage); - - for (let i = 0; i < info.Sets.length; i += 1) { - MainSwitch: - switch (info.Sets[i].Type) { - // Always enable crafting because the base can be shopped - // Recipes with bases that can't be shopped don't need to be used with CraftingSystem - case "crafting": - info.Sets[i].Enabled = true; - - break; - // Enable only if we have a viable item to cube - // Currently the base needs to be added manually to the crafter - case "cubing": - !items && (items = []); - - // Enable the recipe if we have an item that matches both bases list and Cubing list - // This is not a perfect check, it might not handle every case - for (let j = 0; j < items.length; j += 1) { - if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list + if (info) { + let items = me.findItems(-1, sdk.items.mode.inStorage); + + for (let i = 0; i < info.Sets.length; i += 1) { + MainSwitch: + switch (info.Sets[i].Type) { + // Always enable crafting because the base can be shopped + // Recipes with bases that can't be shopped don't need to be used with CraftingSystem + case "crafting": + info.Sets[i].Enabled = true; + + break; + // Enable only if we have a viable item to cube + // Currently the base needs to be added manually to the crafter + case "cubing": + !items && (items = []); + + // Enable the recipe if we have an item that matches both bases list and Cubing list + // This is not a perfect check, it might not handle every case + for (let j = 0; j < items.length; j += 1) { + if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list && AutoMule.cubingIngredient(items[j])) { // Item is a valid Cubing ingredient - print("Base found: " + items[j].classid); + print("Base found: " + items[j].classid); - info.Sets[i].Enabled = true; + info.Sets[i].Enabled = true; - break MainSwitch; - } - } + break MainSwitch; + } + } - info.Sets[i].Enabled = false; + info.Sets[i].Enabled = false; - break; - // Enable only if we have a viable runeword base - // Currently the base needs to be added manually to the crafter - case "runewords": - !items && (items = []); + break; + // Enable only if we have a viable runeword base + // Currently the base needs to be added manually to the crafter + case "runewords": + !items && (items = []); - // Enable the recipe if we have an item that matches both bases list and Cubing list - // This is not a perfect check, it might not handle every case - for (let j = 0; j < items.length; j += 1) { - if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list + // Enable the recipe if we have an item that matches both bases list and Cubing list + // This is not a perfect check, it might not handle every case + for (let j = 0; j < items.length; j += 1) { + if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list && runewordIngredient(items[j])) { // Item is a valid Runeword ingredient - print("Base found: " + items[j].classid); + print("Base found: " + items[j].classid); - info.Sets[i].Enabled = true; + info.Sets[i].Enabled = true; - break MainSwitch; - } - } + break MainSwitch; + } + } - info.Sets[i].Enabled = false; + info.Sets[i].Enabled = false; - break; - } - } + break; + } + } - return true; - } + return true; + } - return false; + return false; } function runewordIngredient(item) { - if (Runewords.validGids.includes(item.gid)) return true; + if (Runewords.validGids.includes(item.gid)) return true; - let baseGids = []; + let baseGids = []; - for (let i = 0; i < Config.Runewords.length; i += 1) { - let base = (Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) + for (let i = 0; i < Config.Runewords.length; i += 1) { + let base = (Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) || Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true)); - base && baseGids.push(base.gid); - } + base && baseGids.push(base.gid); + } - return baseGids.includes(item.gid); + return baseGids.includes(item.gid); } function pickItems() { - let items = []; - let item = Game.getItem(-1, sdk.items.mode.onGround); + let items = []; + let item = Game.getItem(-1, sdk.items.mode.onGround); - if (item) { - updateInfo(); + if (item) { + updateInfo(); - do { - if (checkItem(item) || item.classid === sdk.items.Gold || Pickit.checkItem(item).result > 0) { - items.push(copyUnit(item)); - } - } while (item.getNext()); - } + do { + if (checkItem(item) || item.classid === sdk.items.Gold || Pickit.checkItem(item).result > 0) { + items.push(copyUnit(item)); + } + } while (item.getNext()); + } - while (items.length) { - if (Pickit.canPick(items[0]) && Storage.Inventory.CanFit(items[0])) { - Pickit.pickItem(items[0]); - } + while (items.length) { + if (Pickit.canPick(items[0]) && Storage.Inventory.CanFit(items[0])) { + Pickit.pickItem(items[0]); + } - items.shift(); - delay(1); - } + items.shift(); + delay(1); + } - Town.stash(); + Town.stash(); } function checkItem(item) { - for (let i = 0; i < info.Sets.length; i += 1) { - if (info.Sets[i].Enabled) { - switch (info.Sets[i].Type) { - case "crafting": - // Magic item - // Valid crafting base - if (item.magic && info.Sets[i].BaseItems.includes(item.classid)) return true; - // Valid crafting ingredient - if (info.Sets[i].Ingredients.includes(item.classid)) return true; - - break; - case "cubing": - // There is no base check, item has to be put manually on the character - if (info.Sets[i].Ingredients.includes(item.classid)) return true; - - break; - case "runewords": - // There is no base check, item has to be put manually on the character - if (info.Sets[i].Ingredients.includes(item.classid)) return true; - - break; - } - } - } - - return false; + for (let i = 0; i < info.Sets.length; i += 1) { + if (info.Sets[i].Enabled) { + switch (info.Sets[i].Type) { + case "crafting": + // Magic item + // Valid crafting base + if (item.magic && info.Sets[i].BaseItems.includes(item.classid)) return true; + // Valid crafting ingredient + if (info.Sets[i].Ingredients.includes(item.classid)) return true; + + break; + case "cubing": + // There is no base check, item has to be put manually on the character + if (info.Sets[i].Ingredients.includes(item.classid)) return true; + + break; + case "runewords": + // There is no base check, item has to be put manually on the character + if (info.Sets[i].Ingredients.includes(item.classid)) return true; + + break; + } + } + } + + return false; } function shopStuff(npcId, classids, amount) { - print("shopStuff: " + npcId + " " + amount); + print("shopStuff: " + npcId + " " + amount); - let wpArea, town, path, menuId, npc; - let leadTimeout = 30; - let leadRetry = 3; + let wpArea, town, path, menuId, npc; + let leadTimeout = 30; + let leadRetry = 3; - this.mover = function (npc, path) { - path = this.processPath(npc, path); + this.mover = function (npc, path) { + path = this.processPath(npc, path); - for (let i = 0; i < path.length; i += 2) { - let j; + for (let i = 0; i < path.length; i += 2) { + let j; - Pather.moveTo(path[i] - 3, path[i + 1] - 3); - moveNPC(npc, path[i], path[i + 1]); // moving npc doesn't work, probably should be removed? + Pather.moveTo(path[i] - 3, path[i + 1] - 3); + moveNPC(npc, path[i], path[i + 1]); // moving npc doesn't work, probably should be removed? - for (j = 0; j < leadTimeout; j += 1) { - while (npc.mode === sdk.npcs.mode.Walking) { - delay(100); - } + for (j = 0; j < leadTimeout; j += 1) { + while (npc.mode === sdk.npcs.mode.Walking) { + delay(100); + } - if (getDistance(npc.x, npc.y, path[i], path[i + 1]) < 4) { - break; - } + if (getDistance(npc.x, npc.y, path[i], path[i + 1]) < 4) { + break; + } - if (j > 0 && j % leadRetry === 0) { - moveNPC(npc, path[i], path[i + 1]); - } + if (j > 0 && j % leadRetry === 0) { + moveNPC(npc, path[i], path[i + 1]); + } - delay(1000); - } + delay(1000); + } - if (j === leadTimeout) { - return false; - } - } + if (j === leadTimeout) { + return false; + } + } - delay(1000); + delay(1000); - return true; - }; + return true; + }; - this.processPath = function (npc, path) { - let cutIndex = 0; - let dist = 100; + this.processPath = function (npc, path) { + let cutIndex = 0; + let dist = 100; - for (let i = 0; i < path.length; i += 2) { - if (getDistance(npc, path[i], path[i + 1]) < dist) { - cutIndex = i; - dist = getDistance(npc, path[i], path[i + 1]); - } - } + for (let i = 0; i < path.length; i += 2) { + if (getDistance(npc, path[i], path[i + 1]) < dist) { + cutIndex = i; + dist = getDistance(npc, path[i], path[i + 1]); + } + } - return path.slice(cutIndex); - }; + return path.slice(cutIndex); + }; - this.shopItems = function (classids, amount) { - let npc = getInteractedNPC(); + this.shopItems = function (classids, amount) { + let npc = getInteractedNPC(); - if (npc) { - let items = npc.getItemsEx(); + if (npc) { + let items = npc.getItemsEx(); - if (items.length) { - for (let i = 0; i < items.length; i += 1) { - if (Storage.Inventory.CanFit(items[i]) + if (items.length) { + for (let i = 0; i < items.length; i += 1) { + if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && classids.includes(items[i].classid)) { - //print("Bought " + items[i].name); - items[i].buy(); + //print("Bought " + items[i].name); + items[i].buy(); - let num = countItems(classids, sdk.items.quality.Magic); + let num = countItems(classids, sdk.items.quality.Magic); - if (num >= amount) { - return true; - } - } - } - } - } + if (num >= amount) { + return true; + } + } + } + } + } - return gameRequest; - }; + return gameRequest; + }; - Town.doChores(); + Town.doChores(); - switch (npcId.toLowerCase()) { - case "fara": - if (!Town.goToTown(2) || !Town.move(NPC.Fara)) throw new Error("Failed to get to NPC"); + switch (npcId.toLowerCase()) { + case "fara": + if (!Town.goToTown(2) || !Town.move(NPC.Fara)) throw new Error("Failed to get to NPC"); - wpArea = sdk.areas.A2SewersLvl2; - town = sdk.areas.LutGholein; - path = [5112, 5094, 5092, 5096, 5078, 5098, 5070, 5085]; - menuId = "Repair"; - npc = Game.getNPC(NPC.Fara); - - break; - case "elzix": - if (!Town.goToTown(2) || !Town.move(NPC.Elzix)) throw new Error("Failed to get to NPC"); - - wpArea = sdk.areas.A2SewersLvl2; - town = sdk.areas.LutGholein; - path = [5038, 5099, 5059, 5102, 5068, 5090, 5067, 5086]; - menuId = "Shop"; - npc = Game.getNPC(NPC.Elzix); - - break; - case "drognan": - if (!Town.goToTown(2) || !Town.move(NPC.Drognan)) throw new Error("Failed to get to NPC"); + wpArea = sdk.areas.A2SewersLvl2; + town = sdk.areas.LutGholein; + path = [5112, 5094, 5092, 5096, 5078, 5098, 5070, 5085]; + menuId = "Repair"; + npc = Game.getNPC(NPC.Fara); + + break; + case "elzix": + if (!Town.goToTown(2) || !Town.move(NPC.Elzix)) throw new Error("Failed to get to NPC"); + + wpArea = sdk.areas.A2SewersLvl2; + town = sdk.areas.LutGholein; + path = [5038, 5099, 5059, 5102, 5068, 5090, 5067, 5086]; + menuId = "Shop"; + npc = Game.getNPC(NPC.Elzix); + + break; + case "drognan": + if (!Town.goToTown(2) || !Town.move(NPC.Drognan)) throw new Error("Failed to get to NPC"); - wpArea = sdk.areas.A2SewersLvl2; - town = sdk.areas.LutGholein; - path = [5093, 5049, 5088, 5060, 5093, 5079, 5078, 5087, 5070, 5085]; - menuId = "Shop"; - npc = Game.getNPC(NPC.Drognan); - - break; - case "ormus": - if (!Town.goToTown(3) || !Town.move(NPC.Ormus)) throw new Error("Failed to get to NPC"); + wpArea = sdk.areas.A2SewersLvl2; + town = sdk.areas.LutGholein; + path = [5093, 5049, 5088, 5060, 5093, 5079, 5078, 5087, 5070, 5085]; + menuId = "Shop"; + npc = Game.getNPC(NPC.Drognan); + + break; + case "ormus": + if (!Town.goToTown(3) || !Town.move(NPC.Ormus)) throw new Error("Failed to get to NPC"); - wpArea = sdk.areas.DuranceofHateLvl2; - town = sdk.areas.KurastDocktown; - path = [5147, 5089, 5156, 5075, 5157, 5063, 5160, 5050]; - menuId = "Shop"; - npc = Game.getNPC(NPC.Ormus); - - break; - case "anya": - if (!Town.goToTown(5) || !Town.move(NPC.Anya)) throw new Error("Failed to get to NPC"); + wpArea = sdk.areas.DuranceofHateLvl2; + town = sdk.areas.KurastDocktown; + path = [5147, 5089, 5156, 5075, 5157, 5063, 5160, 5050]; + menuId = "Shop"; + npc = Game.getNPC(NPC.Ormus); + + break; + case "anya": + if (!Town.goToTown(5) || !Town.move(NPC.Anya)) throw new Error("Failed to get to NPC"); - wpArea = sdk.areas.WorldstoneLvl2; - town = sdk.areas.Harrogath; - path = [5122, 5119, 5129, 5105, 5123, 5087, 5115, 5068]; - menuId = "Shop"; - npc = Game.getNPC(NPC.Anya); - - break; - case "malah": - if (!Town.goToTown(5) || !Town.move(NPC.Malah)) throw new Error("Failed to get to NPC"); + wpArea = sdk.areas.WorldstoneLvl2; + town = sdk.areas.Harrogath; + path = [5122, 5119, 5129, 5105, 5123, 5087, 5115, 5068]; + menuId = "Shop"; + npc = Game.getNPC(NPC.Anya); + + break; + case "malah": + if (!Town.goToTown(5) || !Town.move(NPC.Malah)) throw new Error("Failed to get to NPC"); - wpArea = sdk.areas.CrystalizedPassage; - town = sdk.areas.Harrogath; - path = [5077, 5032, 5089, 5025, 5100, 5021, 5106, 5051, 5116, 5071]; - menuId = "Shop"; - npc = Game.getNPC(NPC.Malah); + wpArea = sdk.areas.CrystalizedPassage; + town = sdk.areas.Harrogath; + path = [5077, 5032, 5089, 5025, 5100, 5021, 5106, 5051, 5116, 5071]; + menuId = "Shop"; + npc = Game.getNPC(NPC.Malah); - break; - default: - throw new Error("Invalid shopbot NPC."); - } + break; + default: + throw new Error("Invalid shopbot NPC."); + } - if (!npc) throw new Error("Failed to find NPC."); - if (!this.mover(npc, path)) throw new Error("Failed to move NPC"); + if (!npc) throw new Error("Failed to find NPC."); + if (!this.mover(npc, path)) throw new Error("Failed to move NPC"); - Town.move("waypoint"); + Town.move("waypoint"); - let tickCount = getTickCount(); + let tickCount = getTickCount(); - while (true) { - if (me.area === town) { - if (npc.startTrade(menuId)) { - if (this.shopItems(classids, amount)) return true; - } + while (true) { + if (me.area === town) { + if (npc.startTrade(menuId)) { + if (this.shopItems(classids, amount)) return true; + } - me.cancel(); - } + me.cancel(); + } - me.area === town && Pather.useWaypoint(wpArea); - me.area === wpArea && Pather.useWaypoint(town); + me.area === town && Pather.useWaypoint(wpArea); + me.area === wpArea && Pather.useWaypoint(town); - // end script 5 seconds before we need to exit - if (getTickCount() - tickCount > me.maxgametime - 5000) { - break; - } + // end script 5 seconds before we need to exit + if (getTickCount() - tickCount > me.maxgametime - 5000) { + break; + } - delay(5); - } + delay(5); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js index 5768416bf..3cf6d3bc6 100644 --- a/d2bs/kolbot/libs/scripts/CreepingFeature.js +++ b/d2bs/kolbot/libs/scripts/CreepingFeature.js @@ -6,13 +6,13 @@ */ function CreepingFeature() { - Town.doChores(); - Town.goToTown(2); + 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(); + 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; + return true; } diff --git a/d2bs/kolbot/libs/scripts/CrushTele.js b/d2bs/kolbot/libs/scripts/CrushTele.js index ae38c61bf..e470ed0af 100644 --- a/d2bs/kolbot/libs/scripts/CrushTele.js +++ b/d2bs/kolbot/libs/scripts/CrushTele.js @@ -6,49 +6,51 @@ */ function CrushTele() { - let go = false; + let go = false; - addEventListener("keyup", - function (key) { - key === sdk.keys.NumpadDash && (go = true); - } - ); + addEventListener("keyup", + function (key) { + key === sdk.keys.NumpadDash && (go = true); + } + ); - while (true) { - if (go) { - switch (me.area) { - case sdk.areas.CatacombsLvl2: - Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); - break; - case sdk.areas.HallsoftheDeadLvl2: - Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest); - break; - case sdk.areas.FarOasis: - Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); - break; - case sdk.areas.LostCity: - Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true); - break; - case sdk.areas.CanyonofMagic: - Pather.moveToExit(getRoom().correcttomb, true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder); - break; - case sdk.areas.ArcaneSanctuary: - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, 0, 0, false, true); - break; - case sdk.areas.DuranceofHateLvl2: - Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true); - break; - case sdk.areas.RiverofFlame: - Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, sdk.objects.DiabloStar); - break; - } + while (true) { + if (go) { + switch (me.area) { + case sdk.areas.CatacombsLvl2: + Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); + break; + case sdk.areas.HallsoftheDeadLvl2: + Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest); + break; + case sdk.areas.FarOasis: + Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); + break; + case sdk.areas.LostCity: + Pather.moveToExit([ + sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2 + ], true); + break; + case sdk.areas.CanyonofMagic: + Pather.moveToExit(getRoom().correcttomb, true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder); + break; + case sdk.areas.ArcaneSanctuary: + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, 0, 0, false, true); + break; + case sdk.areas.DuranceofHateLvl2: + Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true); + break; + case sdk.areas.RiverofFlame: + Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, sdk.objects.DiabloStar); + break; + } - go = false; - } + go = false; + } - delay(10); - } + delay(10); + } } diff --git a/d2bs/kolbot/libs/scripts/DeveloperMode.js b/d2bs/kolbot/libs/scripts/DeveloperMode.js index 85783ad6d..90b43af02 100644 --- a/d2bs/kolbot/libs/scripts/DeveloperMode.js +++ b/d2bs/kolbot/libs/scripts/DeveloperMode.js @@ -6,277 +6,277 @@ */ 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": - print("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); - me.overhead("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) { - print("ÿc1Missing arguments"); - } else { - action = msgList[1]; - } - - break; - case "done": - done = true; - - break; - case "testing": - test = true; - - break; - case "command": - if (msgList.length < 2) { - print("ÿc1Missing arguments"); - } else { - command = msgList.splice(1).join(" "); - } - - break; - case "watch": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (msgList[2] === "list") { - print("Watching sent packets : ÿc8" + watchSent.join(", ")); - break; - } - - watchSent.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); - break; - - case "recv": - if (msgList[2] === "list") { - print("Watching received packets : ÿc8" + watchRecv.join(", ")); - break; - } - - watchRecv.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); - break; - - default: - print("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - - case "!watch": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (watchSent.indexOf(msgList[2]) > -1) watchSent.splice(watchSent.indexOf(msgList[2]), 1); - print("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); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); - break; - - default: - print("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - - case "block": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (msgList[2] === "list") { - print("Blocking sent packets : ÿc8" + blockSent.join(", ")); - break; - } - - blockSent.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); - break; - - case "recv": - if (msgList[2] === "list") { - print("Blocking received packets : ÿc8" + blockRecv.join(", ")); - break; - } - - blockRecv.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); - break; - - default: - print("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - - case "!block": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); - break; - } - - switch (msgList[1].toLowerCase()) { - case "sent": - if (blockSent.indexOf(msgList[2]) > -1) blockSent.splice(blockSent.indexOf(msgList[2]), 1); - print("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); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); - break; - - default: - print("ÿc1Invalid argument : " + msgList[1]); - break; - } - - break; - } - }; - - // Received packet handler - 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); - 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 copiedConfig = copyObj(Config); - const UnitInfo = new (require("../modules/UnitInfo")); - - try { - console.log("starting developermode"); - me.overhead("Started developer mode"); - addEventListener("gamepacketsent", packetSent); - addEventListener("gamepacket", packetReceived); - Config.Silence = false; - - while (!done) { - if (action) { - includeIfNotIncluded("scripts/" + action + ".js"); - - UnitInfo.check(); - - if (isIncluded("scripts/" + action + ".js")) { - try { - Loader.runScript(action); - } catch (e) { - console.error(e); - } - } else { - console.warn("Failed to include: " + action); - } - - me.overhead("Done with action"); - action = false; - } - - if (command) { - UnitInfo.check(); - - try { - eval(command); - } catch (e) { - console.error(e); - } - - me.overhead("Done with action"); - command = false; - } - - if (userAddon) { - UnitInfo.createInfo(Game.getSelectedUnit()); - } - - if (test) { - me.overhead("done"); - test = false; - } - - delay(100); - } - } finally { - removeEventListener("gamepacketsent", packetSent); - removeEventListener("gamepacket", packetReceived); - Config = copiedConfig; - UnitInfo.remove(); - } + 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": + print("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); + me.overhead("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) { + print("ÿc1Missing arguments"); + } else { + action = msgList[1]; + } + + break; + case "done": + done = true; + + break; + case "testing": + test = true; + + break; + case "command": + if (msgList.length < 2) { + print("ÿc1Missing arguments"); + } else { + command = msgList.splice(1).join(" "); + } + + break; + case "watch": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (msgList[2] === "list") { + print("Watching sent packets : ÿc8" + watchSent.join(", ")); + break; + } + + watchSent.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); + break; + + case "recv": + if (msgList[2] === "list") { + print("Watching received packets : ÿc8" + watchRecv.join(", ")); + break; + } + + watchRecv.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); + break; + + default: + print("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + + case "!watch": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (watchSent.indexOf(msgList[2]) > -1) watchSent.splice(watchSent.indexOf(msgList[2]), 1); + print("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); + print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); + break; + + default: + print("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + + case "block": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (msgList[2] === "list") { + print("Blocking sent packets : ÿc8" + blockSent.join(", ")); + break; + } + + blockSent.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); + break; + + case "recv": + if (msgList[2] === "list") { + print("Blocking received packets : ÿc8" + blockRecv.join(", ")); + break; + } + + blockRecv.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); + break; + + default: + print("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + + case "!block": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); + break; + } + + switch (msgList[1].toLowerCase()) { + case "sent": + if (blockSent.indexOf(msgList[2]) > -1) blockSent.splice(blockSent.indexOf(msgList[2]), 1); + print("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); + print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); + break; + + default: + print("ÿc1Invalid argument : " + msgList[1]); + break; + } + + break; + } + }; + + // Received packet handler + 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); + 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 copiedConfig = copyObj(Config); + const UnitInfo = new (require("../modules/UnitInfo")); + + try { + console.log("starting developermode"); + me.overhead("Started developer mode"); + addEventListener("gamepacketsent", packetSent); + addEventListener("gamepacket", packetReceived); + Config.Silence = false; + + while (!done) { + if (action) { + includeIfNotIncluded("scripts/" + action + ".js"); + + UnitInfo.check(); + + if (isIncluded("scripts/" + action + ".js")) { + try { + Loader.runScript(action); + } catch (e) { + console.error(e); + } + } else { + console.warn("Failed to include: " + action); + } + + me.overhead("Done with action"); + action = false; + } + + if (command) { + UnitInfo.check(); + + try { + eval(command); + } catch (e) { + console.error(e); + } + + me.overhead("Done with action"); + command = false; + } + + if (userAddon) { + UnitInfo.createInfo(Game.getSelectedUnit()); + } + + if (test) { + me.overhead("done"); + test = false; + } + + delay(100); + } + } finally { + removeEventListener("gamepacketsent", packetSent); + removeEventListener("gamepacket", packetReceived); + Config = copiedConfig; + UnitInfo.remove(); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js index 8c9bd27b7..fa85a8bab 100644 --- a/d2bs/kolbot/libs/scripts/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -10,77 +10,81 @@ */ function Diablo() { - include("core/Common/Diablo.js"); - Pather._teleport = Pather.teleport; - Common.Diablo.clearRadius = Config.Diablo.ClearRadius; + include("core/Common/Diablo.js"); + Pather._teleport = Pather.teleport; + Common.Diablo.clearRadius = Config.Diablo.ClearRadius; - // START - Town.doChores(); - !!Config.RandomPrecast ? Precast.doRandomPrecast(true, sdk.areas.RiverofFlame) : Pather.useWaypoint(sdk.areas.RiverofFlame) && Precast.doPrecast(true); - !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); + // START + Town.doChores(); + !!Config.RandomPrecast + ? Precast.doRandomPrecast(true, sdk.areas.RiverofFlame) + : Pather.useWaypoint(sdk.areas.RiverofFlame) && Precast.doPrecast(true); + !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); - if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); + if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) { + throw new Error("Failed to move to Chaos Sanctuary"); + } - Common.Diablo.initLayout(); + Common.Diablo.initLayout(); - if (Config.Diablo.JustViz) { - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); - Config.PublicMode && Pather.makePortal(); - Common.Diablo.vizierSeal(true); + if (Config.Diablo.JustViz) { + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); + Config.PublicMode && Pather.makePortal(); + Common.Diablo.vizierSeal(true); - return true; - } + return true; + } - try { - if (Config.Diablo.Entrance && !Config.Diablo.Fast) { - Attack.clear(30, 0, false, Common.Diablo.sort); - Pather.moveTo(7790, 5544); + try { + if (Config.Diablo.Entrance && !Config.Diablo.Fast) { + Attack.clear(30, 0, false, Common.Diablo.sort); + Pather.moveTo(7790, 5544); - if (Config.PublicMode && Pather.makePortal()) { - say(Config.Diablo.EntranceTP); - if (Config.Diablo.WalkClear) { - Pather.teleport = false; - } - } + if (Config.PublicMode && Pather.makePortal()) { + say(Config.Diablo.EntranceTP); + if (Config.Diablo.WalkClear) { + Pather.teleport = false; + } + } - Pather.moveTo(7790, 5544); - Precast.doPrecast(true); - Attack.clear(30, 0, false, Common.Diablo.sort); - Common.Diablo.followPath(Common.Diablo.entranceToStar); - } else { - Pather.moveTo(7774, 5305); - Attack.clear(15, 0, false, Common.Diablo.sort); - } + Pather.moveTo(7790, 5544); + Precast.doPrecast(true); + Attack.clear(30, 0, false, Common.Diablo.sort); + Common.Diablo.followPath(Common.Diablo.entranceToStar); + } else { + Pather.moveTo(7774, 5305); + Attack.clear(15, 0, false, Common.Diablo.sort); + } - Pather.moveTo(7791, 5293); + Pather.moveTo(7791, 5293); - if (Config.PublicMode && Pather.makePortal()) { - say(Config.Diablo.StarTP); - Pather.teleport = !Config.Diablo.WalkClear && Pather._teleport; - } + if (Config.PublicMode && Pather.makePortal()) { + say(Config.Diablo.StarTP); + Pather.teleport = !Config.Diablo.WalkClear && Pather._teleport; + } - Attack.clear(30, 0, false, Common.Diablo.sort); + Attack.clear(30, 0, false, Common.Diablo.sort); - try { - Common.Diablo.runSeals(Config.Diablo.SealOrder); - // maybe instead of throwing error if we fail to open seal, add it to an array to re-check before diabloPrep then if that fails throw and error - Config.PublicMode && say(Config.Diablo.DiabloMsg); - console.log("Attempting to find Diablo"); - Common.Diablo.diabloPrep(); - } catch (error) { - console.warn("Diablo wasn't found. Checking seals."); - Common.Diablo.runSeals(Config.Diablo.SealOrder, true, true); - Common.Diablo.diabloPrep(); - } + try { + Common.Diablo.runSeals(Config.Diablo.SealOrder); + // maybe instead of throwing error if we fail to open seal, add it to an array to re-check before diabloPrep then if that fails throw and error + Config.PublicMode && say(Config.Diablo.DiabloMsg); + console.log("Attempting to find Diablo"); + Common.Diablo.diabloPrep(); + } catch (error) { + console.warn("Diablo wasn't found. Checking seals."); + Common.Diablo.runSeals(Config.Diablo.SealOrder, true, true); + Common.Diablo.diabloPrep(); + } - Attack.kill(sdk.monsters.Diablo); - Pickit.pickItems(); - Config.Diablo.SealLeader && say("done"); - } finally { - if (Pather.teleport !== Pather._teleport) { - Pather.teleport = Pather._teleport; - } - } + Attack.kill(sdk.monsters.Diablo); + Pickit.pickItems(); + Config.Diablo.SealLeader && say("done"); + } finally { + if (Pather.teleport !== Pather._teleport) { + Pather.teleport = Pather._teleport; + } + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index c0ae47858..c3ea03498 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -6,156 +6,174 @@ */ function DiabloHelper() { - include("core/Common/Diablo.js"); - this.Leader = Config.Leader; - Common.Diablo.waitForGlow = true; - Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; - Town.doChores(); - const Worker = require("../modules/Worker"); - - try { - addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + include("core/Common/Diablo.js"); + this.Leader = Config.Leader; + Common.Diablo.waitForGlow = true; + Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; + Town.doChores(); + const Worker = require("../modules/Worker"); + + try { + addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - if (Config.DiabloHelper.SkipIfBaal) { - let leadTick = getTickCount(); - - Worker.runInBackground.leaderTracker = function () { - if (Common.Diablo.done) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - let party = getParty(); - - if (party) { - do { - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { - if (Loader.scriptName() === "DiabloHelper") { - throw new Error("Party leader is running baal"); - } else { - // kill process - return false; - } - } - } while (party.getNext()); - } - - return true; - }; - } - - Config.DiabloHelper.SafePrecast && Precast.needOutOfTownCast() ? Precast.doRandomPrecast(true, sdk.areas.PandemoniumFortress) : Precast.doPrecast(true); - - if (Config.DiabloHelper.SkipTP) { - !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); - - if (!Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); - !Config.DiabloHelper.Entrance && Pather.moveTo(7774, 5305); - - if (!Misc.poll(() => { - let party = getParty(); - - if (party) { - do { - if ((!DiabloHelper.Leader || party.name === DiabloHelper.Leader) && party.area === sdk.areas.ChaosSanctuary) { - return true; - } - } while (party.getNext()); - } - - Attack.clear(30, 0, false, Common.Diablo.sort); - - return false; - }, Time.minutes(Config.DiabloHelper.Wait), 1000)) throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Chaos)"); - } else { - Town.goToTown(4); - Town.move("portalspot"); - !DiabloHelper.Leader && (DiabloHelper.Leader = Misc.autoLeaderDetect({destination: sdk.areas.ChaosSanctuary, quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), timeout: Time.minutes(2)})); - - if (!Misc.poll(() => { - if (Pather.getPortal(sdk.areas.ChaosSanctuary, Config.Leader || null) && Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader || null)) { - return true; - } - - return false; - }, Time.minutes(Config.DiabloHelper.Wait), 1000)) throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); - } - - Common.Diablo.initLayout(); - - let diaTick = getTickCount(); - - Worker.runInBackground.diaSpawned = function () { - if (Common.Diablo.done) return false; - // check every 1/4 second - if (getTickCount() - diaTick < 250) return true; - diaTick = getTickCount(); - - if (Common.Diablo.diabloSpawned) throw new Error("Diablo spawned"); - - return true; - }; - - try { - if (Config.DiabloHelper.Entrance && Common.Diablo.starCoords.distance > Common.Diablo.entranceCoords.distance) { - Attack.clear(35, 0, false, Common.Diablo.sort); - Common.Diablo.followPath(Common.Diablo.entranceToStar); - } else { - Pather.moveTo(7774, 5305); - Attack.clear(35, 0, false, Common.Diablo.sort); - } - - Pather.moveTo(7774, 5305); - Attack.clear(35, 0, false, Common.Diablo.sort); - Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); - Common.Diablo.moveToStar(); - Misc.poll(() => { - if (Common.Diablo.diabloSpawned) return true; - if (Game.getMonster(sdk.monsters.Diablo)) return true; - if ([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(Misc.getPlayerArea(DiabloHelper.Leader))) { - throw new Error("END"); - } - return false; - }, Time.minutes(2), 500); - } catch (e) { - let eMsg = e.message ? e.message : e; - console.log(eMsg); - - if (eMsg === "END") { - return true; - } - } - - try { - !Common.Diablo.diabloSpawned && (Common.Diablo.diaWaitTime += Time.minutes(1)); - console.log("Attempting to find Diablo"); - Common.Diablo.diabloPrep(); - } catch (error) { - console.log("Diablo wasn't found"); - if (Config.DiabloHelper.RecheckSeals) { - try { - console.log("Rechecking seals"); - Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); - Misc.poll(() => Common.Diablo.diabloSpawned, Time.minutes(2), 500); - Common.Diablo.diabloPrep(); - } catch (e2) { - // - } - } - } - - Attack.kill(sdk.monsters.Diablo); - Pickit.pickItems(); - } catch (e) { - console.error(e); - } finally { - Common.Diablo.done = true; - removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - } - - return true; + if (Config.DiabloHelper.SkipIfBaal) { + let leadTick = getTickCount(); + + Worker.runInBackground.leaderTracker = function () { + if (Common.Diablo.done) return false; + // check every 3 seconds + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + let party = getParty(); + + if (party) { + do { + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { + if (Loader.scriptName() === "DiabloHelper") { + throw new Error("Party leader is running baal"); + } else { + // kill process + return false; + } + } + } while (party.getNext()); + } + + return true; + }; + } + + Config.DiabloHelper.SafePrecast && Precast.needOutOfTownCast() + ? Precast.doRandomPrecast(true, sdk.areas.PandemoniumFortress) + : Precast.doPrecast(true); + + if (Config.DiabloHelper.SkipTP) { + !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); + + if (!Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); + !Config.DiabloHelper.Entrance && Pather.moveTo(7774, 5305); + + if (!Misc.poll(() => { + let party = getParty(); + + if (party) { + do { + if ((!DiabloHelper.Leader || party.name === DiabloHelper.Leader) + && party.area === sdk.areas.ChaosSanctuary) { + return true; + } + } while (party.getNext()); + } + + Attack.clear(30, 0, false, Common.Diablo.sort); + + return false; + }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Chaos)"); + } + } else { + Town.goToTown(4); + Town.move("portalspot"); + if (!DiabloHelper.Leader) { + DiabloHelper.Leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ChaosSanctuary, + quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), + timeout: Time.minutes(2) + }); + } + + if (!Misc.poll(() => { + if (Pather.getPortal(sdk.areas.ChaosSanctuary, Config.Leader || null) + && Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader || null)) { + return true; + } + + return false; + }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + } + } + + Common.Diablo.initLayout(); + + let diaTick = getTickCount(); + + Worker.runInBackground.diaSpawned = function () { + if (Common.Diablo.done) return false; + // check every 1/4 second + if (getTickCount() - diaTick < 250) return true; + diaTick = getTickCount(); + + if (Common.Diablo.diabloSpawned) throw new Error("Diablo spawned"); + + return true; + }; + + try { + if (Config.DiabloHelper.Entrance && Common.Diablo.starCoords.distance > Common.Diablo.entranceCoords.distance) { + Attack.clear(35, 0, false, Common.Diablo.sort); + Common.Diablo.followPath(Common.Diablo.entranceToStar); + } else { + Pather.moveTo(7774, 5305); + Attack.clear(35, 0, false, Common.Diablo.sort); + } + + Pather.moveTo(7774, 5305); + Attack.clear(35, 0, false, Common.Diablo.sort); + Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); + Common.Diablo.moveToStar(); + Misc.poll(() => { + if (Common.Diablo.diabloSpawned) return true; + if (Game.getMonster(sdk.monsters.Diablo)) return true; + if ([ + sdk.areas.WorldstoneLvl3, + sdk.areas.ThroneofDestruction, + sdk.areas.WorldstoneChamber + ].includes(Misc.getPlayerArea(DiabloHelper.Leader))) { + throw new Error("END"); + } + return false; + }, Time.minutes(2), 500); + } catch (e) { + let eMsg = e.message ? e.message : e; + console.log(eMsg); + + if (eMsg === "END") { + return true; + } + } + + try { + !Common.Diablo.diabloSpawned && (Common.Diablo.diaWaitTime += Time.minutes(1)); + console.log("Attempting to find Diablo"); + Common.Diablo.diabloPrep(); + } catch (error) { + console.log("Diablo wasn't found"); + if (Config.DiabloHelper.RecheckSeals) { + try { + console.log("Rechecking seals"); + Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); + Misc.poll(() => Common.Diablo.diabloSpawned, Time.minutes(2), 500); + Common.Diablo.diabloPrep(); + } catch (e2) { + // + } + } + } + + Attack.kill(sdk.monsters.Diablo); + Pickit.pickItems(); + } catch (e) { + console.error(e); + } finally { + Common.Diablo.done = true; + removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index ee84ab3ef..b66dac54d 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -6,50 +6,53 @@ */ function Duriel () { - const killDuriel = function () { - let target = Misc.poll(() => Game.getMonster(sdk.monsters.Duriel), 1000, 200); - if (!target) throw new Error("Duriel not found."); + const killDuriel = function () { + let target = Misc.poll(() => Game.getMonster(sdk.monsters.Duriel), 1000, 200); + if (!target) throw new Error("Duriel not found."); - Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Duriel); + Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Duriel); - for (let i = 0; i < 300 && target.attackable; i += 1) { - ClassAttack.doAttack(target); - target.distance <= 10 && Pather.moveTo(22638, me.y < target.y ? 15722 : 15693); - } + for (let i = 0; i < 300 && target.attackable; i += 1) { + ClassAttack.doAttack(target); + target.distance <= 10 && Pather.moveTo(22638, me.y < target.y ? 15722 : 15693); + } - return target.dead; - }; + return target.dead; + }; - if (!me.inArea(sdk.areas.CanyonofMagic)) { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CanyonofMagic); - } + if (!me.inArea(sdk.areas.CanyonofMagic)) { + Town.doChores(); + Pather.useWaypoint(sdk.areas.CanyonofMagic); + } - Precast.doPrecast(true); + Precast.doPrecast(true); - if (!Pather.moveToExit(getRoom().correcttomb, true)) throw new Error("Failed to move to Tal Rasha's Tomb"); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder, -11, 3)) throw new Error("Failed to move to Orifice"); + if (!Pather.moveToExit(getRoom().correcttomb, true)) throw new Error("Failed to move to Tal Rasha's Tomb"); + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder, -11, 3)) { + throw new Error("Failed to move to Orifice"); + } - me.hardcore && !me.sorceress && Attack.clear(5); + me.hardcore && !me.sorceress && Attack.clear(5); - let unit = Game.getObject(sdk.objects.PortaltoDurielsLair); + let unit = Game.getObject(sdk.objects.PortaltoDurielsLair); - if (Skill.useTK(unit)) { - Misc.poll(function () { - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit) && delay(100); - return me.inArea(sdk.areas.DurielsLair); - }, 1000, 200); - } + if (Skill.useTK(unit)) { + Misc.poll(function () { + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit) && delay(100); + return me.inArea(sdk.areas.DurielsLair); + }, 1000, 200); + } - if (!me.inArea(sdk.areas.DurielsLair) && (!unit || !Pather.useUnitEx({ unit: unit }, sdk.areas.DurielsLair))) { - Attack.clear(10); - 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); + } - if (!me.inArea(sdk.areas.DurielsLair)) throw new Error("Failed to move to Duriel"); + if (!me.inArea(sdk.areas.DurielsLair)) throw new Error("Failed to move to Duriel"); - me.sorceress && me.classic ? killDuriel() : Attack.kill(sdk.monsters.Duriel); - Pickit.pickItems(); + me.sorceress && me.classic ? killDuriel() : Attack.kill(sdk.monsters.Duriel); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js index d087bb134..9c6065508 100644 --- a/d2bs/kolbot/libs/scripts/Eldritch.js +++ b/d2bs/kolbot/libs/scripts/Eldritch.js @@ -6,36 +6,41 @@ */ function Eldritch() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); - let { x, y } = me; - Pather.moveTo(3745, 5084); - Attack.kill(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); + let { x, y } = me; + Pather.moveTo(3745, 5084); + Attack.kill(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); - try { - // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); - // Could this be causing crashes here? - if (Config.Eldritch.OpenChest && Pather.moveNearPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 10)) { - Misc.openChest(sdk.objects.FrigidHighlandsChest) && Pickit.pickItems(); - // check distance from current location to shenk and if far tp to town and use wp instead - ([x, y].distance > 120 || !Pather.canTeleport()) && Town.goToTown() && Pather.useWaypoint(sdk.areas.FrigidHighlands); - } - } catch (e) { - console.warn("(Eldritch) :: Failed to open chest. " + e); - } + try { + // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); + // Could this be causing crashes here? + if (Config.Eldritch.OpenChest + && Pather.moveNearPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 10)) { + Misc.openChest(sdk.objects.FrigidHighlandsChest) && Pickit.pickItems(); + // check distance from current location to shenk and if far tp to town and use wp instead + if ([x, y].distance > 120 || !Pather.canTeleport()) { + Town.goToTown() && Pather.useWaypoint(sdk.areas.FrigidHighlands); + } + } + } catch (e) { + console.warn("(Eldritch) :: Failed to open chest. " + e); + } - try { - if (Config.Eldritch.KillShenk && Pather.moveToExit(sdk.areas.BloodyFoothills, false) && Pather.moveTo(3876, 5130)) { - Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); - } - } catch (e) { - console.warn("(Eldritch) :: Failed to Kill Shenk. " + e); - } + try { + if (Config.Eldritch.KillShenk && Pather.moveToExit(sdk.areas.BloodyFoothills, false) && Pather.moveTo(3876, 5130)) { + Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + } + } catch (e) { + console.warn("(Eldritch) :: Failed to Kill Shenk. " + e); + } - if (Config.Eldritch.KillDacFarren && Pather.moveNearPreset(sdk.areas.BloodyFoothills, sdk.unittype.Monster, sdk.monsters.preset.DacFarren, 10) && Pather.moveTo(4478, 5108)) { - Attack.kill(getLocaleString(sdk.locale.monsters.DacFarren)); - } + if (Config.Eldritch.KillDacFarren + && Pather.moveNearPreset(sdk.areas.BloodyFoothills, sdk.unittype.Monster, sdk.monsters.preset.DacFarren, 10) + && Pather.moveTo(4478, 5108)) { + Attack.kill(getLocaleString(sdk.locale.monsters.DacFarren)); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js index d6f9bc947..88cf39dee 100644 --- a/d2bs/kolbot/libs/scripts/Endugu.js +++ b/d2bs/kolbot/libs/scripts/Endugu.js @@ -6,16 +6,16 @@ */ function Endugu() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FlayerJungle); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FlayerJungle); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3], true) + if (!Pather.moveToExit([sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3], true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest)) { - throw new Error("Failed to move to Endugu"); - } + throw new Error("Failed to move to Endugu"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.WitchDoctorEndugu)); + Attack.kill(getLocaleString(sdk.locale.monsters.WitchDoctorEndugu)); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Eyeback.js b/d2bs/kolbot/libs/scripts/Eyeback.js index f1073529b..2c0b1bac8 100644 --- a/d2bs/kolbot/libs/scripts/Eyeback.js +++ b/d2bs/kolbot/libs/scripts/Eyeback.js @@ -6,15 +6,15 @@ */ function Eyeback() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArreatPlateau); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArreatPlateau); + Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Monster, sdk.monsters.preset.EyebacktheUnleashed)) { - throw new Error("Failed to move to Eyeback the Unleashed"); - } + if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Monster, sdk.monsters.preset.EyebacktheUnleashed)) { + throw new Error("Failed to move to Eyeback the Unleashed"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.EyebacktheUnleashed)); + Attack.kill(getLocaleString(sdk.locale.monsters.EyebacktheUnleashed)); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Fangskin.js b/d2bs/kolbot/libs/scripts/Fangskin.js index 9be8a33a1..cb3f9f45f 100644 --- a/d2bs/kolbot/libs/scripts/Fangskin.js +++ b/d2bs/kolbot/libs/scripts/Fangskin.js @@ -6,19 +6,21 @@ */ function Fangskin() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.LostCity); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.LostCity); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true)) { - throw new Error("Failed to move to Fangskin"); - } + if (!Pather.moveToExit([ + sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2 + ], true)) { + throw new Error("Failed to move to Fangskin"); + } - // casters can kill fangskin from the altar spot for better safety - Pather.canTeleport() && Skill.getRange(Config.AttackSkill[1] > 10) && Pather.moveTo(15044, 14045); + // casters can kill fangskin from the altar spot for better safety + Pather.canTeleport() && Skill.getRange(Config.AttackSkill[1] > 10) && Pather.moveTo(15044, 14045); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Fangskin)); - Pickit.pickItems(); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Fangskin)); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index db0f15953..0589c0aec 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -9,7 +9,9 @@ * 3 - town manager * c - get corpse * p - pick items +* r - revive * s - toggle stop +* pre - precast * - tell specific character to perform action * @Attack * a - attack toggle for all @@ -55,644 +57,648 @@ */ function Follower() { - const QuestData = require("../core/GameData/QuestData"); - const commanders = []; - Config.Leader && commanders.push(Config.Leader); - let piece, skill; - let [allowSay, attack, openContainers, stop] = [true, true, true, false]; - let [leader, leaderUnit] = [null, null]; - let action = ""; - - const announce = function (msg = "") { - if (!allowSay) return; - say(msg); - }; - - /** + const QuestData = require("../core/GameData/QuestData"); + const commanders = []; + Config.Leader && commanders.push(Config.Leader); + let piece, skill; + let [allowSay, attack, openContainers, stop] = [true, true, true, false]; + let [leader, leaderUnit] = [null, null]; + let action = ""; + + const announce = function (msg = "") { + if (!allowSay) return; + say(msg); + }; + + /** * Change areas to where leader is * @param {Player} unit * @param {number} area * @returns {boolean} */ - const checkExit = function (unit, area) { - if (unit.inTown) return false; + const checkExit = function (unit, area) { + if (unit.inTown) return false; - let target; - let exits = getArea().exits; + let target; + let exits = getArea().exits; - for (let i = 0; i < exits.length; i += 1) { - if (exits[i].target === area) { - return 1; - } - } + for (let i = 0; i < exits.length; i += 1) { + if (exits[i].target === area) { + return 1; + } + } - if (unit.inTown) { - target = Game.getObject("waypoint"); + if (unit.inTown) { + target = Game.getObject("waypoint"); - if (target && getDistance(me, target) < 20) { - return 3; - } - } + if (target && getDistance(me, target) < 20) { + return 3; + } + } - target = Game.getObject("portal"); + target = Game.getObject("portal"); - if (target) { - do { - if (target.objtype === area) { - Pather.usePortal(null, null, target); + if (target) { + do { + if (target.objtype === area) { + Pather.usePortal(null, null, target); - return 2; - } - } while (target.getNext()); - } + return 2; + } + } while (target.getNext()); + } - // Arcane<->Cellar portal - if ((me.inArea(sdk.areas.ArcaneSanctuary) && area === sdk.areas.PalaceCellarLvl3) + // Arcane<->Cellar portal + if ((me.inArea(sdk.areas.ArcaneSanctuary) && area === sdk.areas.PalaceCellarLvl3) || (me.inArea(sdk.areas.PalaceCellarLvl3) && area === sdk.areas.ArcaneSanctuary)) { - Pather.usePortal(null); + Pather.usePortal(null); - return 4; - } + return 4; + } - // Tal-Rasha's tomb->Duriel's lair - if (me.area >= sdk.areas.TalRashasTomb1 && me.area <= sdk.areas.TalRashasTomb7 && area === sdk.areas.DurielsLair) { - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, area); + // Tal-Rasha's tomb->Duriel's lair + if (me.area >= sdk.areas.TalRashasTomb1 && me.area <= sdk.areas.TalRashasTomb7 && area === sdk.areas.DurielsLair) { + Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, area); - return 4; - } + return 4; + } - // Throne->Chamber - if (me.inArea(sdk.areas.ThroneofDestruction) && area === sdk.areas.WorldstoneChamber) { - target = Game.getObject(sdk.objects.WorldstonePortal); + // Throne->Chamber + if (me.inArea(sdk.areas.ThroneofDestruction) && area === sdk.areas.WorldstoneChamber) { + target = Game.getObject(sdk.objects.WorldstonePortal); - if (target) { - Pather.usePortal(null, null, target); + if (target) { + Pather.usePortal(null, null, target); - return 4; - } - } + return 4; + } + } - return false; - }; + return false; + }; - /** + /** * Talk to a NPC * @param {string} name * @returns {boolean} */ - const talk = function (name) { - try { - if (!me.inTown) throw new Error("I'm not in town!"); - if (typeof name !== "string") throw new Error("No NPC name given."); - Town.npcInteract(name); - - return true; - } catch (e) { - console.error(e); - announce( - (typeof e === "object" && e.message - ? e.message - : typeof e === "string" - ? e - : "Failed to talk to " + name) - ); - - return false; - } finally { - Town.move("portalspot"); - } - }; - - /** + const talk = function (name) { + try { + if (!me.inTown) throw new Error("I'm not in town!"); + if (typeof name !== "string") throw new Error("No NPC name given."); + Town.npcInteract(name); + + return true; + } catch (e) { + console.error(e); + announce( + (typeof e === "object" && e.message + ? e.message + : typeof e === "string" + ? e + : "Failed to talk to " + name) + ); + + return false; + } finally { + Town.move("portalspot"); + } + }; + + /** * Change act after completing last act quest * @param {number} act * @returns {boolean} */ - const changeAct = function (act) { - let preArea = me.area; - - if (me.area >= sdk.areas.townOfAct(act)) { - announce("My current act is higher than " + act); - return false; - } - - const npcTravel = new Map([ - [1, ["Warriv", sdk.areas.RogueEncampment]], - [2, [(me.act === 1 ? "Warriv" : "Meshif"), sdk.areas.LutGholein]], - [3, ["Meshif", sdk.areas.KurastDocktown]], - [4, ["", sdk.areas.PandemoniumFortress]], - [5, ["Tyrael", sdk.areas.Harrogath]], - ]); - - const preCheck = new Map([ - [ - 2, - () => QuestData.get(sdk.quest.id.SistersToTheSlaughter).complete(true) - ], - [ - 3, - () => { - if (QuestData.get(sdk.quest.id.TheSevenTombs).complete()) return true; - if (!QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/)) { - Town.npcInteract("Jerhyn"); - if (me.getTpTool()) { - Pather.moveToExit(sdk.areas.HaremLvl1, true); - Pather.usePortal(null) || Pather.makePortal(true); - } - } - return QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/); - } - ], - [ - 4, - () => { - if (me.inTown) { - if (!QuestData.get(sdk.quest.id.TheBlackenedTemple).complete()) { - Town.npcInteract("Cain"); - } - Town.move("portalspot"); - Pather.usePortal(sdk.areas.DuranceofHateLvl3, null); - } - return me.inArea(sdk.areas.DuranceofHateLvl3); - } - ], - [ - 5, - () => { - if (!QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/)) { - Town.npcInteract("Tyrael"); - } - return QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/); - } - ] - ]); - - if (!preCheck.get(act)()) { - announce("Failed act " + act + " precheck"); - return false; - } - - if (act !== 4) { - let [npc, loc] = npcTravel.get(act); - 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++) { - new PacketBuilder() - .byte(sdk.packets.send.EntityAction) - .dword(0) - .dword(npcUnit.gid) - .dword(loc) - .send(); - delay(1000); - - if (me.act === act) { - break; - } - } - } - } else { - if (me.inArea(sdk.areas.DuranceofHateLvl3)) { - let target = Game.getObject(sdk.objects.RedPortalToAct4); - target && Pather.moveTo(target.x - 3, target.y - 1); - - Pather.usePortal(null); - } - } - - while (!me.gameReady) { - delay(100); - } - - if (me.area === preArea) { - me.cancel(); - Town.move("portalspot"); - announce("Act change failed."); - - return false; - } - - Town.move("portalspot"); - announce("Act change successful."); - act === 2 && announce("Don't forget to talk to Drognan after getting the Viper Amulet!"); - - return true; - }; - - const pickPotions = function (range = 5) { - if (me.dead) return false; - - Town.clearBelt(); - - while (!me.idle) { - delay(40); - } - - let pickList = []; - let item = Game.getItem(); - - if (item) { - do { - if (item.onGroundOrDropping && item.itemType >= sdk.items.type.HealingPotion + const changeAct = function (act) { + let preArea = me.area; + + if (me.area >= sdk.areas.townOfAct(act)) { + announce("My current act is higher than " + act); + return false; + } + + const npcTravel = new Map([ + [1, ["Warriv", sdk.areas.RogueEncampment]], + [2, [(me.act === 1 ? "Warriv" : "Meshif"), sdk.areas.LutGholein]], + [3, ["Meshif", sdk.areas.KurastDocktown]], + [4, ["", sdk.areas.PandemoniumFortress]], + [5, ["Tyrael", sdk.areas.Harrogath]], + ]); + + const preCheck = new Map([ + [ + 2, + () => QuestData.get(sdk.quest.id.SistersToTheSlaughter).complete(true) + ], + [ + 3, + () => { + if (QuestData.get(sdk.quest.id.TheSevenTombs).complete()) return true; + if (!QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/)) { + Town.npcInteract("Jerhyn"); + if (me.getTpTool()) { + Pather.moveToExit(sdk.areas.HaremLvl1, true); + Pather.usePortal(null) || Pather.makePortal(true); + } + } + return QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/); + } + ], + [ + 4, + () => { + if (me.inTown) { + if (!QuestData.get(sdk.quest.id.TheBlackenedTemple).complete()) { + Town.npcInteract("Cain"); + } + Town.move("portalspot"); + Pather.usePortal(sdk.areas.DuranceofHateLvl3, null); + } + return me.inArea(sdk.areas.DuranceofHateLvl3); + } + ], + [ + 5, + () => { + if (!QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/)) { + Town.npcInteract("Tyrael"); + } + return QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/); + } + ] + ]); + + if (!preCheck.get(act)()) { + announce("Failed act " + act + " precheck"); + return false; + } + + if (act !== 4) { + let [npc, loc] = npcTravel.get(act); + 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++) { + new PacketBuilder() + .byte(sdk.packets.send.EntityAction) + .dword(0) + .dword(npcUnit.gid) + .dword(loc) + .send(); + delay(1000); + + if (me.act === act) { + break; + } + } + } + } else { + if (me.inArea(sdk.areas.DuranceofHateLvl3)) { + let target = Game.getObject(sdk.objects.RedPortalToAct4); + target && Pather.moveTo(target.x - 3, target.y - 1); + + Pather.usePortal(null); + } + } + + while (!me.gameReady) { + delay(100); + } + + if (me.area === preArea) { + me.cancel(); + Town.move("portalspot"); + announce("Act change failed."); + + return false; + } + + Town.move("portalspot"); + announce("Act change successful."); + act === 2 && announce("Don't forget to talk to Drognan after getting the Viper Amulet!"); + + return true; + }; + + const pickPotions = function (range = 5) { + if (me.dead) return false; + + Town.clearBelt(); + + while (!me.idle) { + delay(40); + } + + let pickList = []; + let item = Game.getItem(); + + if (item) { + do { + if (item.onGroundOrDropping && item.itemType >= sdk.items.type.HealingPotion && item.itemType <= sdk.items.type.RejuvPotion && item.distance <= range) { - pickList.push(copyUnit(item)); - } - } while (item.getNext()); - } + pickList.push(copyUnit(item)); + } + } while (item.getNext()); + } - pickList.sort(Pickit.sortItems); + pickList.sort(Pickit.sortItems); - while (pickList.length > 0) { - item = pickList.shift(); + while (pickList.length > 0) { + item = pickList.shift(); - if (item && copyUnit(item).x) { - let status = Pickit.checkItem(item).result; + if (item && copyUnit(item).x) { + let status = Pickit.checkItem(item).result; - if (status && Pickit.canPick(item)) { - Pickit.pickItem(item, status); - } - } - } + if (status && Pickit.canPick(item)) { + Pickit.pickItem(item, status); + } + } + } - return true; - }; + return true; + }; - /** + /** * @param {string} nick * @param {string} msg */ - const chatEvent = function (nick, msg) { - if (msg && nick === Config.Leader) { - switch (msg) { - case "tele": - case me.name + " tele": - Pather.teleport = !Pather.teleport; - announce("Teleport " + (Pather.teleport ? "on" : "off")); - - break; - case "tele off": - case me.name + " tele off": - Pather.teleport = false; - announce("Teleport off."); - - break; - case "tele on": - case me.name + " tele on": - Pather.teleport = true; - announce("Teleport on."); - - break; - case "a": - case me.name + " a": - attack = !attack; - announce("Attack " + (attack ? "on" : "off")); - - break; - case "flash": - Packet.flash(me.gid); - - break; - case "quiet": - allowSay = !allowSay; - - break; - case "aoff": - case me.name + " aoff": - attack = false; - announce("Attack off."); - - break; - case "aon": - case me.name + " aon": - attack = true; - announce("Attack on."); - - break; - case "quit": - case me.name + " quit": - quit(); - - break; - case "s": - case me.name + " s": - stop = !stop; - announce((stop ? "Stopping." : "Resuming.")); - - break; - case "r": - me.dead && me.revive(); - - break; - default: - let piecewise = msg.split(" "); - let who = piecewise.length > 1 && piecewise.first() || ""; - - if (me.paladin && msg.includes("aura ")) { - if (who === me.name || piece === "all") { - skill = parseInt(msg.split(" ")[2], 10); - - if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { - announce("Active aura is: " + skill); - - Config.AttackSkill[2] = skill; - Config.AttackSkill[4] = skill; - - Skill.setSkill(skill, sdk.skills.hand.Right); - } else { - announce("I don't have that aura."); - } - } - } else if (msg.includes("skill ")) { - if (charClass.includes(who) || who === me.name || who === "all") { - skill = parseInt(msg.split(" ")[2], 10); - - if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { - announce("Attack skill is: " + skill); - - Config.AttackSkill[1] = skill; - Config.AttackSkill[3] = skill; - } else { - announce("I don't have that skill."); - } - } - } else { - if (who && who !== me.name && who !== "all") { - return; - } - who && (msg = msg.replace(who, "").trim()); - action = msg; - } - - break; - } - } - - if (msg && msg.split(" ")[0] === "leader" && (commanders.includes(nick) || !commanders.length)) { - piece = msg.split(" ")[1]; - - if (typeof piece === "string") { - if (commanders.indexOf(piece) === -1) { - commanders.push(piece); - } - - announce("Switching leader to " + piece); - - Config.Leader = piece; - leader = Misc.findPlayer(Config.Leader); - leaderUnit = Misc.getPlayerUnit(Config.Leader); - } - } - }; - - - // START - addEventListener("chatmsg", chatEvent); - openContainers && Config.OpenChests.enabled && Config.OpenChests.Types.push("all"); + const chatEvent = function (nick, msg) { + if (msg && nick === Config.Leader) { + switch (msg) { + case "tele": + case me.name + " tele": + Pather.teleport = !Pather.teleport; + announce("Teleport " + (Pather.teleport ? "on" : "off")); + + break; + case "tele off": + case me.name + " tele off": + Pather.teleport = false; + announce("Teleport off."); + + break; + case "tele on": + case me.name + " tele on": + Pather.teleport = true; + announce("Teleport on."); + + break; + case "a": + case me.name + " a": + attack = !attack; + announce("Attack " + (attack ? "on" : "off")); + + break; + case "flash": + Packet.flash(me.gid); + + break; + case "quiet": + allowSay = !allowSay; + + break; + case "aoff": + case me.name + " aoff": + attack = false; + announce("Attack off."); + + break; + case "aon": + case me.name + " aon": + attack = true; + announce("Attack on."); + + break; + case "quit": + case me.name + " quit": + quit(); + + break; + case "s": + case me.name + " s": + stop = !stop; + announce((stop ? "Stopping." : "Resuming.")); + + break; + case "r": + me.dead && me.revive(); + + break; + default: + let piecewise = msg.split(" "); + let who = piecewise.length > 1 && piecewise.first() || ""; + + if (me.paladin && msg.includes("aura ")) { + if (who === me.name || piece === "all") { + skill = parseInt(msg.split(" ")[2], 10); + + if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { + announce("Active aura is: " + skill); + + Config.AttackSkill[2] = skill; + Config.AttackSkill[4] = skill; + + Skill.setSkill(skill, sdk.skills.hand.Right); + } else { + announce("I don't have that aura."); + } + } + } else if (msg.includes("skill ")) { + if (charClass.includes(who) || who === me.name || who === "all") { + skill = parseInt(msg.split(" ")[2], 10); + + if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { + announce("Attack skill is: " + skill); + + Config.AttackSkill[1] = skill; + Config.AttackSkill[3] = skill; + } else { + announce("I don't have that skill."); + } + } + } else { + if (who && who !== me.name && who !== "all") { + return; + } + who && (msg = msg.replace(who, "").trim()); + action = msg; + } + + break; + } + } + + if (msg && msg.split(" ")[0] === "leader" && (commanders.includes(nick) || !commanders.length)) { + piece = msg.split(" ")[1]; + + if (typeof piece === "string") { + if (commanders.indexOf(piece) === -1) { + commanders.push(piece); + } + + announce("Switching leader to " + piece); + + Config.Leader = piece; + leader = Misc.findPlayer(Config.Leader); + leaderUnit = Misc.getPlayerUnit(Config.Leader); + } + } + }; + + + // START + addEventListener("chatmsg", chatEvent); + openContainers && Config.OpenChests.enabled && Config.OpenChests.Types.push("all"); - // Override config values that use TP - Config.TownCheck = false; - Config.TownHP = 0; - Config.TownMP = 0; - const charClass = sdk.player.class.nameOf(me.classid).toLowerCase(); - leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.seconds(20), Time.seconds(1)); - - if (!leader) { - announce("Leader not found."); - delay(1000); - quit(); - } else { - announce("Leader found."); - } - - while (!Misc.inMyParty(Config.Leader)) { - delay(500); - } - - announce("Partied."); - - me.inTown && Town.move("portalspot"); - - // Main Loop - while (Misc.inMyParty(Config.Leader)) { - if (me.mode === sdk.player.mode.Dead) { - while (!me.inTown) { - me.revive(); - delay(1000); - } - - Town.move("portalspot"); - announce("I'm alive!"); - } - - while (stop) { - delay(500); - } - - if (!me.inTown) { - if (!leaderUnit || !copyUnit(leaderUnit).x) { - leaderUnit = Misc.getPlayerUnit(Config.Leader); - - if (leaderUnit) { - announce("Leader unit found."); - } - } - - if (!leaderUnit) { - let player = Game.getPlayer(); - - if (player) { - do { - if (player.name !== me.name) { - Pather.moveToUnit(player); - - break; - } - } while (player.getNext()); - } - } - - if (leaderUnit && getDistance(me.x, me.y, leaderUnit.x, leaderUnit.y) <= 60) { - if (getDistance(me.x, me.y, leaderUnit.x, leaderUnit.y) > 4) { - Pather.moveToUnit(leaderUnit); - } - } - - if (attack) { - Attack.clear(20, false, false, false, true); - pickPotions(20); - } - - me.paladin && Config.AttackSkill[2] > 0 && Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - - if (leader.area !== me.area && !me.inTown) { - while (leader.area === 0) { - delay(100); - } - - let result = checkExit(leader, leader.area); - - switch (result) { - case 1: - announce("Taking exit."); - delay(500); - Pather.moveToExit(leader.area, true); - - break; - case 2: - announce("Taking portal."); - - break; - case 3: - announce("Taking waypoint."); - delay(500); - Pather.useWaypoint(leader.area, true); - - break; - case 4: - announce("Special transit."); - - break; - } - - while (me.area === 0) { - delay(100); - } - - leaderUnit = Misc.getPlayerUnit(Config.Leader); - } - } - - MainSwitch: - switch (action) { - case "cow": - if (me.inArea(sdk.areas.RogueEncampment)) { - Town.move("portalspot"); - !Pather.usePortal(sdk.areas.MooMooFarm) && announce("Failed to use cow portal."); - } - - break; - case "move": - let coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); - Pather.moveTo(coord.x, coord.y); - - break; - case "wp": - case me.name + "wp": - if (me.inTown) { - break; - } - - delay(rand(1, 3) * 500); - - if (Game.getObject("waypoint")) { - for (let retry = 0; retry < 3; retry++) { - if (Pather.getWP(me.area)) { - announce("Got wp."); - break MainSwitch; - } - } - - announce("Failed to get wp."); - } - - me.cancel(); - - break; - case "c": - !me.inTown && Town.getCorpse(); - - break; - case "p": - announce("!Picking items."); - Pickit.pickItems(); - openContainers && Misc.openChests(20); - announce("!Done picking."); - - break; - case "1": - if (me.inTown && leader.inTown && Misc.getPlayerAct(Config.Leader) !== me.act) { - announce("Going to leader's town."); - Town.goToTown(Misc.getPlayerAct(Config.Leader)); - Town.move("portalspot"); - } else if (me.inTown) { - announce("Going outside."); - Town.goToTown(Misc.getPlayerAct(Config.Leader)); - Town.move("portalspot"); - - if (!Pather.usePortal(null, leader.name)) { - break; - } - - while (!Misc.getPlayerUnit(Config.Leader) && !me.dead) { - Attack.clear(10); - delay(200); - } - } - - break; - case "2": - if (!me.inTown) { - delay(150); - announce("Going to town."); - Pather.usePortal(null, leader.name); - } - - break; - case "3": - if (me.inTown) { - announce("Running town chores"); - Town.doChores(); - Town.move("portalspot"); - announce("Ready"); - } - - break; - case "h": - me.barbarian && Skill.cast(sdk.skills.Howl); - - break; - case "bo": - // checks if we have cta or warcries - Precast.needOutOfTownCast() && Precast.doPrecast(true); - - break; - case "a2": - case "a3": - case "a4": - case "a5": - changeAct(parseInt(action[1], 10)); - - break; - case me.name + " tp": - if (!Pather.makePortal()) { - announce("No TP scrolls or tomes."); - } - - break; - } - - if (action.includes("talk")) { - talk(action.split(" ")[1]); - } - - action = ""; - - delay(100); - } - - return true; + // Override config values that use TP + Config.TownCheck = false; + Config.TownHP = 0; + Config.TownMP = 0; + const charClass = sdk.player.class.nameOf(me.classid).toLowerCase(); + leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.seconds(20), Time.seconds(1)); + + if (!leader) { + announce("Leader not found."); + delay(1000); + quit(); + } else { + announce("Leader found."); + } + + while (!Misc.inMyParty(Config.Leader)) { + delay(500); + } + + announce("Partied."); + + me.inTown && Town.move("portalspot"); + + // Main Loop + while (Misc.inMyParty(Config.Leader)) { + if (me.mode === sdk.player.mode.Dead) { + while (!me.inTown) { + me.revive(); + delay(1000); + } + + Town.move("portalspot"); + announce("I'm alive!"); + } + + while (stop) { + delay(500); + } + + if (!me.inTown) { + if (!leaderUnit || !copyUnit(leaderUnit).x) { + leaderUnit = Misc.getPlayerUnit(Config.Leader); + + if (leaderUnit) { + announce("Leader unit found."); + } + } + + if (!leaderUnit) { + let player = Game.getPlayer(); + + if (player) { + do { + if (player.name !== me.name) { + Pather.moveToUnit(player); + + break; + } + } while (player.getNext()); + } + } + + if (leaderUnit && getDistance(me.x, me.y, leaderUnit.x, leaderUnit.y) <= 60) { + if (getDistance(me.x, me.y, leaderUnit.x, leaderUnit.y) > 4) { + Pather.moveToUnit(leaderUnit); + } + } + + if (attack) { + Attack.clear(20, false, false, false, true); + pickPotions(20); + } + + me.paladin && Config.AttackSkill[2] > 0 && Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + + if (leader.area !== me.area && !me.inTown) { + while (leader.area === 0) { + delay(100); + } + + let result = checkExit(leader, leader.area); + + switch (result) { + case 1: + announce("Taking exit."); + delay(500); + Pather.moveToExit(leader.area, true); + + break; + case 2: + announce("Taking portal."); + + break; + case 3: + announce("Taking waypoint."); + delay(500); + Pather.useWaypoint(leader.area, true); + + break; + case 4: + announce("Special transit."); + + break; + } + + while (me.area === 0) { + delay(100); + } + + leaderUnit = Misc.getPlayerUnit(Config.Leader); + } + } + + MainSwitch: + switch (action) { + case "cow": + if (me.inArea(sdk.areas.RogueEncampment)) { + Town.move("portalspot"); + !Pather.usePortal(sdk.areas.MooMooFarm) && announce("Failed to use cow portal."); + } + + break; + case "move": + let coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); + Pather.moveTo(coord.x, coord.y); + + break; + case "wp": + case me.name + "wp": + if (me.inTown) { + break; + } + + delay(rand(1, 3) * 500); + + if (Game.getObject("waypoint")) { + for (let retry = 0; retry < 3; retry++) { + if (Pather.getWP(me.area)) { + announce("Got wp."); + break MainSwitch; + } + } + + announce("Failed to get wp."); + } + + me.cancel(); + + break; + case "c": + !me.inTown && Town.getCorpse(); + + break; + case "p": + announce("!Picking items."); + Pickit.pickItems(); + openContainers && Misc.openChests(20); + announce("!Done picking."); + + break; + case "1": + if (me.inTown && leader.inTown && Misc.getPlayerAct(Config.Leader) !== me.act) { + announce("Going to leader's town."); + Town.goToTown(Misc.getPlayerAct(Config.Leader)); + Town.move("portalspot"); + } else if (me.inTown) { + announce("Going outside."); + Town.goToTown(Misc.getPlayerAct(Config.Leader)); + Town.move("portalspot"); + + if (!Pather.usePortal(null, leader.name)) { + break; + } + + while (!Misc.getPlayerUnit(Config.Leader) && !me.dead) { + Attack.clear(10); + delay(200); + } + } + + break; + case "2": + if (!me.inTown) { + delay(150); + announce("Going to town."); + Pather.usePortal(null, leader.name); + } + + break; + case "3": + if (me.inTown) { + announce("Running town chores"); + Town.doChores(); + Town.move("portalspot"); + announce("Ready"); + } + + break; + case "h": + me.barbarian && Skill.cast(sdk.skills.Howl); + + break; + case "pre": + Precast.doPrecast(true); + + break; + case "bo": + // checks if we have cta or warcries + Precast.needOutOfTownCast() && Precast.doPrecast(true); + + break; + case "a2": + case "a3": + case "a4": + case "a5": + changeAct(parseInt(action[1], 10)); + + break; + case me.name + " tp": + if (!Pather.makePortal()) { + announce("No TP scrolls or tomes."); + } + + break; + } + + if (action.includes("talk")) { + talk(action.split(" ")[1]); + } + + action = ""; + + delay(100); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Frozenstein.js b/d2bs/kolbot/libs/scripts/Frozenstein.js index 6133e4f5c..64892b4f6 100644 --- a/d2bs/kolbot/libs/scripts/Frozenstein.js +++ b/d2bs/kolbot/libs/scripts/Frozenstein.js @@ -6,16 +6,17 @@ */ function Frozenstein() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CrystalizedPassage); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.CrystalizedPassage); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, -5, -5)) { - throw new Error("Failed to move to Frozenstein"); - } + if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, -5, -5)) { + throw new Error("Failed to move to Frozenstein"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.Frozenstein)); - Config.Frozenstein.ClearFrozenRiver && Attack.clearLevel(Config.ClearType); + Attack.kill(getLocaleString(sdk.locale.monsters.Frozenstein)); + Config.Frozenstein.ClearFrozenRiver && Attack.clearLevel(Config.ClearType); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Gamble.js b/d2bs/kolbot/libs/scripts/Gamble.js index 227fe03ab..29125a5b2 100644 --- a/d2bs/kolbot/libs/scripts/Gamble.js +++ b/d2bs/kolbot/libs/scripts/Gamble.js @@ -6,56 +6,56 @@ */ function Gamble() { - let idleTick = 0; - let info = Gambling.getInfo(); - let needGold = false; + let idleTick = 0; + let info = Gambling.getInfo(); + let needGold = false; - if (!info) throw new Error("Bad Gambling System config."); + if (!info) throw new Error("Bad Gambling System config."); - me.maxgametime = 0; - Town.goToTown(1); + me.maxgametime = 0; + Town.goToTown(1); - addEventListener("copydata", - function (mode, msg) { - if (needGold && mode === 0 && info.goldFinders.indexOf(msg) > -1) { - print("Got game request from " + msg); - sendCopyData(null, msg, 4, me.gamename + "/" + me.gamepassword); - } - }); + addEventListener("copydata", + function (mode, msg) { + if (needGold && mode === 0 && info.goldFinders.indexOf(msg) > -1) { + print("Got game request from " + msg); + sendCopyData(null, msg, 4, me.gamename + "/" + me.gamepassword); + } + }); - while (true) { - Town.needGamble() ? Town.gamble() : (needGold = true) && (idleTick = 0); - Town.move("stash"); + while (true) { + Town.needGamble() ? Town.gamble() : (needGold = true) && (idleTick = 0); + Town.move("stash"); - while (needGold) { - // should there be a player count check before getting into this loop? - // Or maybe gamevent for player join/leave, or itemevent for gold dropping? - while (true) { - Town.needGamble() && (needGold = false); - Town.stash(); + while (needGold) { + // should there be a player count check before getting into this loop? + // Or maybe gamevent for player join/leave, or itemevent for gold dropping? + while (true) { + Town.needGamble() && (needGold = false); + Town.stash(); - let gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround); + let gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround); - if (!gold || !Pickit.canPick(gold)) { - break; - } + if (!gold || !Pickit.canPick(gold)) { + break; + } - Pickit.pickItem(gold); - delay(500); + Pickit.pickItem(gold); + delay(500); - } + } - if (needGold && getTickCount() - idleTick > 0) { - Packet.questRefresh(); - idleTick += rand(1200, 1500) * 1000; - } + if (needGold && getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += rand(1200, 1500) * 1000; + } - delay(500); - } + delay(500); + } - delay(1000); - } + delay(1000); + } - // eslint-disable-next-line no-unreachable - return true; + // eslint-disable-next-line no-unreachable + return true; } diff --git a/d2bs/kolbot/libs/scripts/GemHunter.js b/d2bs/kolbot/libs/scripts/GemHunter.js index dc5e4e4bb..856d05265 100644 --- a/d2bs/kolbot/libs/scripts/GemHunter.js +++ b/d2bs/kolbot/libs/scripts/GemHunter.js @@ -13,28 +13,28 @@ * - Take into account the next area and sort the shrines to bring us to the exit if its connected */ function GemHunter () { - Town.doChores(); - Town.getGem(); - if (Town.getGemsInInv().length === 0) { - print("ÿc4GemHunterÿc0: no gems in inventory - aborting."); - return false; - } + Town.doChores(); + Town.getGem(); + if (Town.getGemsInInv().length === 0) { + print("ÿc4GemHunterÿc0: no gems in inventory - aborting."); + return false; + } - for (let i = 0; i < Config.GemHunter.AreaList.length; i++) { - if (Town.getGemsInInv().length > 0) { - print("ÿc4GemHunterÿc0: Moving to " + getAreaName(Config.GemHunter.AreaList[i])); - Pather.journeyTo(Config.GemHunter.AreaList[i]); - if (i % 2 === 0) Precast.doPrecast(true); - if (Misc.getShrinesInArea(Config.GemHunter.AreaList[i], sdk.shrines.Gem, true)) { - Pickit.pickItems(); - print("ÿc4GemHunterÿc0: found a gem Shrine"); - if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { - print("ÿc4GemHunterÿc0: Getting a new Gem in Town."); - Town.visitTown(); // Go to Town and do chores. Will throw an error if it fails to return from Town. - Town.getGem(); - } - } - } - } - return true; + for (let i = 0; i < Config.GemHunter.AreaList.length; i++) { + if (Town.getGemsInInv().length > 0) { + print("ÿc4GemHunterÿc0: Moving to " + getAreaName(Config.GemHunter.AreaList[i])); + Pather.journeyTo(Config.GemHunter.AreaList[i]); + if (i % 2 === 0) Precast.doPrecast(true); + if (Misc.getShrinesInArea(Config.GemHunter.AreaList[i], sdk.shrines.Gem, true)) { + Pickit.pickItems(); + print("ÿc4GemHunterÿc0: found a gem Shrine"); + if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { + print("ÿc4GemHunterÿc0: Getting a new Gem in Town."); + Town.visitTown(); // Go to Town and do chores. Will throw an error if it fails to return from Town. + Town.getGem(); + } + } + } + } + return true; } diff --git a/d2bs/kolbot/libs/scripts/GetCube.js b/d2bs/kolbot/libs/scripts/GetCube.js index 7ff6efa6e..db62b980f 100644 --- a/d2bs/kolbot/libs/scripts/GetCube.js +++ b/d2bs/kolbot/libs/scripts/GetCube.js @@ -6,29 +6,30 @@ */ function GetCube() { - // Can't get the cube if we can't access the act - if (!Pather.accessToAct(2)) return false; + // Can't get the cube if we can't access the act + if (!Pather.accessToAct(2)) return false; - console.log("Getting cube"); - me.overhead("Getting cube"); + console.log("Getting cube"); + me.overhead("Getting cube"); - Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); - Precast.doPrecast(true); + Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); + Precast.doPrecast(true); - if (Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) && Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricCubeChest)) { - let chest = Game.getObject(sdk.quest.chest.HoradricCubeChest); + if (Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) + && Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricCubeChest)) { + let chest = Game.getObject(sdk.quest.chest.HoradricCubeChest); - if (chest) { - Misc.openChest(chest); - Misc.poll(function () { - let cube = Game.getItem(sdk.quest.item.Cube); - return !!cube && Pickit.pickItem(cube); - }, 1000, 2000); - } - } + if (chest) { + Misc.openChest(chest); + Misc.poll(function () { + let cube = Game.getItem(sdk.quest.item.Cube); + return !!cube && Pickit.pickItem(cube); + }, 1000, 2000); + } + } - Town.goToTown(); - let cube = me.getItem(sdk.quest.item.Cube); + Town.goToTown(); + let cube = me.getItem(sdk.quest.item.Cube); - return (!!cube && Storage.Stash.MoveTo(cube)); + return (!!cube && Storage.Stash.MoveTo(cube)); } diff --git a/d2bs/kolbot/libs/scripts/GetKeys.js b/d2bs/kolbot/libs/scripts/GetKeys.js index 75fc5eb02..157916967 100644 --- a/d2bs/kolbot/libs/scripts/GetKeys.js +++ b/d2bs/kolbot/libs/scripts/GetKeys.js @@ -6,31 +6,31 @@ */ function GetKeys() { - Town.doChores(); + Town.doChores(); - if (me.getItemsEx(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length < 3) { - try { - Loader.runScript("Countess"); - } catch (e) { - console.error("ÿc1Countess failed :: ", e); - } - } + if (me.getItemsEx(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length < 3) { + try { + Loader.runScript("Countess"); + } catch (e) { + console.error("ÿc1Countess failed :: ", e); + } + } - if (me.getItemsEx(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length < 3) { - try { - Loader.runScript("Summoner", () => Config.Summoner.FireEye = false); - } catch (e) { - console.error("ÿc1Summoner failed :: ", e); - } - } + if (me.getItemsEx(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length < 3) { + try { + Loader.runScript("Summoner", () => Config.Summoner.FireEye = false); + } catch (e) { + console.error("ÿc1Summoner failed :: ", e); + } + } - if (me.getItemsEx(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length < 3) { - try { - Loader.runScript("Nihlathak"); - } catch (e) { - console.error("ÿc1Nihlathak failed :: ", e); - } - } + if (me.getItemsEx(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length < 3) { + try { + Loader.runScript("Nihlathak"); + } catch (e) { + console.error("ÿc1Nihlathak failed :: ", e); + } + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/GhostBusters.js b/d2bs/kolbot/libs/scripts/GhostBusters.js index d62738eed..a0625c022 100644 --- a/d2bs/kolbot/libs/scripts/GhostBusters.js +++ b/d2bs/kolbot/libs/scripts/GhostBusters.js @@ -6,108 +6,108 @@ */ function GhostBusters() { - const clearGhosts = function () { - let room = getRoom(); - if (!room) return false; - - const rooms = []; - /** @param {Monster} monster */ - const check = (monster) => monster.isGhost && 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; - }; - - const tasks = new Map([ - ["cellar", () => { - Pather.useWaypoint(sdk.areas.BlackMarsh); - Precast.doPrecast(true); - - for (let i = sdk.areas.ForgottenTower; i <= sdk.areas.TowerCellarLvl5; i += 1) { - Pather.moveToExit(i, true) && clearGhosts(); - } - }], - ["jail", () => { - // gonna use inner cloister wp and travel backwards - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); - - for (let i = sdk.areas.JailLvl3; i >= sdk.areas.JailLvl1; i -= 1) { - Pather.moveToExit(i, true) && clearGhosts(); - } - }], - ["cathedral", () => { - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.Cathedral, true); - clearGhosts(); - }], - ["tombs", () => { - Pather.useWaypoint(sdk.areas.CanyonofMagic); - Precast.doPrecast(true); - - for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i += 1) { - Pather.moveToExit(i, true) && clearGhosts(); - Pather.moveToExit(sdk.areas.CanyonofMagic, true); - } - }], - ["flayerDungeon", () => { - Pather.useWaypoint(sdk.areas.FlayerJungle); - Precast.doPrecast(true); - - [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3].forEach(area => { - Pather.moveToExit(area, true) && clearGhosts(); - }); - }], - ["crystalinePassage", () => { - Pather.useWaypoint(sdk.areas.CrystalizedPassage); - Precast.doPrecast(true); - clearGhosts(); - Pather.moveToExit(sdk.areas.FrozenRiver, true) && clearGhosts(); - }], - ["glacialTrail", () => { - Pather.useWaypoint(sdk.areas.GlacialTrail); - Precast.doPrecast(true); - clearGhosts(); - Pather.moveToExit(sdk.areas.DrifterCavern, true) && clearGhosts(); - }], - ["icyCellar", () => { - Pather.useWaypoint(sdk.areas.AncientsWay); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.IcyCellar, true) && clearGhosts(); - }] - ]); - - tasks.forEach(task => { - Town.doChores(); - - try { - task(); - } finally { - Town.goToTown(); - } - }); - - return true; + const clearGhosts = function () { + let room = getRoom(); + if (!room) return false; + + const rooms = []; + /** @param {Monster} monster */ + const check = (monster) => monster.isGhost && 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; + }; + + const tasks = new Map([ + ["cellar", () => { + Pather.useWaypoint(sdk.areas.BlackMarsh); + Precast.doPrecast(true); + + for (let i = sdk.areas.ForgottenTower; i <= sdk.areas.TowerCellarLvl5; i += 1) { + Pather.moveToExit(i, true) && clearGhosts(); + } + }], + ["jail", () => { + // gonna use inner cloister wp and travel backwards + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); + + for (let i = sdk.areas.JailLvl3; i >= sdk.areas.JailLvl1; i -= 1) { + Pather.moveToExit(i, true) && clearGhosts(); + } + }], + ["cathedral", () => { + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.Cathedral, true); + clearGhosts(); + }], + ["tombs", () => { + Pather.useWaypoint(sdk.areas.CanyonofMagic); + Precast.doPrecast(true); + + for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i += 1) { + Pather.moveToExit(i, true) && clearGhosts(); + Pather.moveToExit(sdk.areas.CanyonofMagic, true); + } + }], + ["flayerDungeon", () => { + Pather.useWaypoint(sdk.areas.FlayerJungle); + Precast.doPrecast(true); + + [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3].forEach(area => { + Pather.moveToExit(area, true) && clearGhosts(); + }); + }], + ["crystalinePassage", () => { + Pather.useWaypoint(sdk.areas.CrystalizedPassage); + Precast.doPrecast(true); + clearGhosts(); + Pather.moveToExit(sdk.areas.FrozenRiver, true) && clearGhosts(); + }], + ["glacialTrail", () => { + Pather.useWaypoint(sdk.areas.GlacialTrail); + Precast.doPrecast(true); + clearGhosts(); + Pather.moveToExit(sdk.areas.DrifterCavern, true) && clearGhosts(); + }], + ["icyCellar", () => { + Pather.useWaypoint(sdk.areas.AncientsWay); + Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.IcyCellar, true) && clearGhosts(); + }] + ]); + + tasks.forEach(task => { + Town.doChores(); + + try { + task(); + } finally { + Town.goToTown(); + } + }); + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Hephasto.js b/d2bs/kolbot/libs/scripts/Hephasto.js index 5ba0a69ae..9cb31f93a 100644 --- a/d2bs/kolbot/libs/scripts/Hephasto.js +++ b/d2bs/kolbot/libs/scripts/Hephasto.js @@ -6,20 +6,22 @@ */ function Hephasto() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.RiverofFlame); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.RiverofFlame); + Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) throw new Error("Failed to move to Hephasto"); + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { + throw new Error("Failed to move to Hephasto"); + } - try { - Attack.kill(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); - } catch (e) { - print("Heph not found. Carry on"); - } + try { + Attack.kill(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); + } catch (e) { + print("Heph not found. Carry on"); + } - Pickit.pickItems(); - Config.Hephasto.ClearRiver && Attack.clearLevel(Config.Hephasto.ClearType); + Pickit.pickItems(); + Config.Hephasto.ClearRiver && Attack.clearLevel(Config.Hephasto.ClearType); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/IPHunter.js b/d2bs/kolbot/libs/scripts/IPHunter.js index 42d66b04c..6ba43c3f5 100644 --- a/d2bs/kolbot/libs/scripts/IPHunter.js +++ b/d2bs/kolbot/libs/scripts/IPHunter.js @@ -7,23 +7,26 @@ */ function IPHunter() { - let ip = Number(me.gameserverip.split(".")[3]); - - if (Config.IPHunter.IPList.indexOf(ip) > -1) { - D2Bot.printToConsole("IPHunter: IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword, sdk.colors.D2Bot.DarkGold); - print("IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword); - me.overhead(":D IP found! - [" + ip + "]"); - me.maxgametime = 0; - - for (let i = 12; i > 0; i -= 1) { - me.overhead(":D IP found! - [" + ip + "]" + (i - 1) + " beep left"); - beep(); // works if windows sounds are enabled - delay(250); - } - - while (true) { - - /* // remove comment if you want beeps at every movement + let ip = Number(me.gameserverip.split(".")[3]); + + if (Config.IPHunter.IPList.indexOf(ip) > -1) { + D2Bot.printToConsole( + "IPHunter: IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword, + sdk.colors.D2Bot.DarkGold + ); + print("IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword); + me.overhead(":D IP found! - [" + ip + "]"); + me.maxgametime = 0; + + for (let i = 12; i > 0; i -= 1) { + me.overhead(":D IP found! - [" + ip + "]" + (i - 1) + " beep left"); + beep(); // works if windows sounds are enabled + delay(250); + } + + while (true) { + + /* // remove comment if you want beeps at every movement for (let i = 12; i != 0; i -= 1) { me.overhead(":D IP found! - [" + ip + "]" + (i-1) + " beep left"); beep(); // works if windows sounds are enabled @@ -31,27 +34,27 @@ function IPHunter() { } */ - me.overhead(":D IP found! - [" + ip + "]"); - try { - Town.move("waypoint"); - Town.move("stash"); - } catch (e) { - // ensure it doesnt leave game by failing to walk due to desyncing. - } - - for (let i = (12 * 60); i > 0; i -= 1) { - me.overhead(":D IP found! - [" + ip + "] Next movement in: " + i + " sec."); - delay(1000); - } - } - } - - for (let i = (Config.IPHunter.GameLength * 60); i > 0; i -= 1) { - me.overhead(":( IP : [" + (ip) + "] NG: " + i + " sec"); - delay(1000); - } - - D2Bot.printToConsole("IPHunter: IP was [" + ip + "]", sdk.colors.D2Bot.Gray); - - return true; + me.overhead(":D IP found! - [" + ip + "]"); + try { + Town.move("waypoint"); + Town.move("stash"); + } catch (e) { + // ensure it doesnt leave game by failing to walk due to desyncing. + } + + for (let i = (12 * 60); i > 0; i -= 1) { + me.overhead(":D IP found! - [" + ip + "] Next movement in: " + i + " sec."); + delay(1000); + } + } + } + + for (let i = (Config.IPHunter.GameLength * 60); i > 0; i -= 1) { + me.overhead(":( IP : [" + (ip) + "] NG: " + i + " sec"); + delay(1000); + } + + D2Bot.printToConsole("IPHunter: IP was [" + ip + "]", sdk.colors.D2Bot.Gray); + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Icehawk.js b/d2bs/kolbot/libs/scripts/Icehawk.js index 1fc9a3234..741bbfe8b 100644 --- a/d2bs/kolbot/libs/scripts/Icehawk.js +++ b/d2bs/kolbot/libs/scripts/Icehawk.js @@ -6,15 +6,15 @@ */ function Icehawk() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.KurastBazaar); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.KurastBazaar); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2], false)) { - throw new Error("Failed to move to Icehawk"); - } + if (!Pather.moveToExit([sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2], false)) { + throw new Error("Failed to move to Icehawk"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.IcehawkRiftwing)); + Attack.kill(getLocaleString(sdk.locale.monsters.IcehawkRiftwing)); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Izual.js b/d2bs/kolbot/libs/scripts/Izual.js index c45f776f3..450e2e4c6 100644 --- a/d2bs/kolbot/libs/scripts/Izual.js +++ b/d2bs/kolbot/libs/scripts/Izual.js @@ -6,16 +6,16 @@ */ function Izual() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CityoftheDamned); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.CityoftheDamned); + Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.PlainsofDespair, sdk.unittype.Monster, sdk.monsters.Izual)) { - throw new Error("Failed to move to Izual."); - } + if (!Pather.moveToPreset(sdk.areas.PlainsofDespair, sdk.unittype.Monster, sdk.monsters.Izual)) { + throw new Error("Failed to move to Izual."); + } - Attack.kill(sdk.monsters.Izual); - Pickit.pickItems(); + Attack.kill(sdk.monsters.Izual); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/KillDclone.js b/d2bs/kolbot/libs/scripts/KillDclone.js index ff74a8a69..e5c2aceb0 100644 --- a/d2bs/kolbot/libs/scripts/KillDclone.js +++ b/d2bs/kolbot/libs/scripts/KillDclone.js @@ -6,19 +6,19 @@ */ function KillDclone() { - Pather.useWaypoint(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); + Pather.useWaypoint(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); - if (!Pather.usePortal(null)) { - throw new Error("Failed to move to Palace Cellar"); - } + if (!Pather.usePortal(null)) { + throw new Error("Failed to move to Palace Cellar"); + } - Attack.kill(sdk.monsters.DiabloClone); - Pickit.pickItems(); + Attack.kill(sdk.monsters.DiabloClone); + Pickit.pickItems(); - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); - } + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleAnni"); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/KurastTemples.js b/d2bs/kolbot/libs/scripts/KurastTemples.js index d1832c563..a0dcc96e1 100644 --- a/d2bs/kolbot/libs/scripts/KurastTemples.js +++ b/d2bs/kolbot/libs/scripts/KurastTemples.js @@ -6,32 +6,32 @@ */ function KurastTemples() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.KurastBazaar); + Town.doChores(); + Pather.useWaypoint(sdk.areas.KurastBazaar); - [ - { 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(area => { - try { - if (!me.inArea(area.base)) { - // maybe journeyTo instead? - if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to change area"); - } - let precastTimeout = getTickCount() + Time.minutes(2); - // @todo sort by distance - area.temples.forEach(temple => { - if (!Pather.moveToExit(temple, true)) throw new Error("Failed to move to the temple"); - Attack.clearLevel(Config.ClearType); - if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to move out of the temple"); - Precast.doPrecast((getTickCount() > precastTimeout)); - }); + [ + { 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(area => { + try { + if (!me.inArea(area.base)) { + // maybe journeyTo instead? + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to change area"); + } + let precastTimeout = getTickCount() + Time.minutes(2); + // @todo sort by distance + area.temples.forEach(temple => { + if (!Pather.moveToExit(temple, true)) throw new Error("Failed to move to the temple"); + Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to move out of the temple"); + Precast.doPrecast((getTickCount() > precastTimeout)); + }); - } catch (e) { - console.error(e); - } - }); + } catch (e) { + console.error(e); + } + }); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index 6b0071baa..312c81b98 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -6,277 +6,284 @@ */ function MFHelper() { - /** + /** * @todo We should be able to handle Diablo scripts then resume MFHelper, not sure how yet but doesn't make sense to have * helper just idle if leader does any of the a5 scripts before baal. I guess could re-order them in the configs but having * it broken up by act flows better */ - let player, playerAct, split; - let lastPrecast; + let player, playerAct, split; + let lastPrecast; - /** @type {{ task: string, msg: string, at: number, area: number }[]} */ - const taskList = []; - const tasks = ["kill", "clearlevel", "clear", "quit", "cows", "council", "goto", "nextup"]; + /** @type {{ task: string, msg: string, at: number, area: number }[]} */ + const taskList = []; + const tasks = ["kill", "clearlevel", "clear", "quit", "cows", "council", "goto", "nextup"]; - /** + /** * @param {string} name * @param {string} msg */ - function chatEvent (name, msg) { - if (!msg) return; - let msgShort = msg && msg.length ? msg.split(" ")[0] : ""; - if (!tasks.includes(msgShort)) return; - - if (!player) { - // anything else we need to consider here? - player = Misc.findPlayer(name); - } - - if (player && name === player.name) { - taskList.push({ task: msgShort, msg: msg, at: getTickCount(), area: player.area }); - } - } - - addEventListener("chatmsg", chatEvent); - Town.doChores(); - Town.move("portalspot"); - - if (Config.Leader) { - if (!Misc.poll(() => Misc.inMyParty(Config.Leader), 30e4, 1000)) { - throw new Error("MFHelper: Leader not partied"); - } - - player = Misc.findPlayer(Config.Leader); - } - - if (player) { - if (!Misc.poll(() => player.area, 120 * 60, 100 + me.ping)) { - throw new Error("Failed to wait for player area"); - } - - playerAct = Misc.getPlayerAct(Config.Leader); - - if (playerAct && playerAct !== me.act) { - Town.goToTown(playerAct); - Town.move("portalspot"); - } - } - - // START - while (true) { - if (me.dead) { - while (me.mode === sdk.player.mode.Death) { - delay(3); - } - - if (me.hardcore) { - D2Bot.printToConsole("(MFHelper) :: " + me.charname + " has died at level " + me.charlvl + ". Shutting down profile...", sdk.colors.D2Bot.Red); - D2Bot.stop(); - } - - while (!me.inTown) { - me.revive(); - delay(1000); - } - - Town.move("portalspot"); - console.log("revived!"); - } - - if (player) { - if (me.needHealing() && Town.heal()) { - Town.move("portalspot"); - } - - // Finish MFHelper script if leader is running Diablo or Baal - if ([sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(player.area)) { - break; - } - - if (taskList.length) { - console.debug("Leader area :: " + player.area); - - if (taskList[0].task === "quit") return true; - // check if any message is telling us to quit - if (taskList.find(el => el.task === "quit")) return true; + function chatEvent (name, msg) { + if (!msg) return; + let msgShort = msg && msg.length ? msg.split(" ")[0] : ""; + if (!tasks.includes(msgShort)) return; + + if (!player) { + // anything else we need to consider here? + player = Misc.findPlayer(name); + } + + if (player && name === player.name) { + taskList.push({ task: msgShort, msg: msg, at: getTickCount(), area: player.area }); + } + } + + addEventListener("chatmsg", chatEvent); + Town.doChores(); + Town.move("portalspot"); + + if (Config.Leader) { + if (!Misc.poll(() => Misc.inMyParty(Config.Leader), 30e4, 1000)) { + throw new Error("MFHelper: Leader not partied"); + } + + player = Misc.findPlayer(Config.Leader); + } + + if (player) { + if (!Misc.poll(() => player.area, 120 * 60, 100 + me.ping)) { + throw new Error("Failed to wait for player area"); + } + + playerAct = Misc.getPlayerAct(Config.Leader); + + if (playerAct && playerAct !== me.act) { + Town.goToTown(playerAct); + Town.move("portalspot"); + } + } + + // START + while (true) { + if (me.dead) { + while (me.mode === sdk.player.mode.Death) { + delay(3); + } + + if (me.hardcore) { + D2Bot.printToConsole( + "(MFHelper) :: " + me.charname + " has died at level " + + me.charlvl + ". Shutting down profile...", + sdk.colors.D2Bot.Red + ); + D2Bot.stop(); + } + + while (!me.inTown) { + me.revive(); + delay(1000); + } + + Town.move("portalspot"); + console.log("revived!"); + } + + if (player) { + if (me.needHealing() && Town.heal()) { + Town.move("portalspot"); + } + + // Finish MFHelper script if leader is running Diablo or Baal + if ([ + sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber + ].includes(player.area)) { + break; + } + + if (taskList.length) { + console.debug("Leader area :: " + player.area); + + if (taskList[0].task === "quit") return true; + // check if any message is telling us to quit + if (taskList.find(el => el.task === "quit")) return true; - // check if any message is telling us that nextup is diablo/baal - if (taskList.some(el => { - if (el.task === "nextup") { - let script = el.msg.split("nextup ")[1]; - - if (script && ["Diablo", "Baal"].includes(script)) { - console.log("ÿc4MFHelperÿc0: Ending script"); - return true; - } - } - - return false; - })) return true; - - // handled pre-reqs, now perform normal checks - let { task, msg, at, area } = taskList.shift(); - - switch (task) { - case "goto": - try { - // lets see if the task list contains any other goto messages, in case we were late - { - let gt = taskList.findIndex(el => el.task === "goto"); - if (gt > -1) { - // alright there is another so lets see where we should actually be going - for (let i = 0; i < gt - 1; i++) { - // feels hacky but this should remove all elements up to the next goto message, while preserving the order of list - ({ task, msg, at, area } = taskList.shift()); - } - } - } - - split = msg.substr(6); - console.log("ÿc4MFHelperÿc0: Goto " + split); + // check if any message is telling us that nextup is diablo/baal + if (taskList.some(el => { + if (el.task === "nextup") { + let script = el.msg.split("nextup ")[1]; + + if (script && ["Diablo", "Baal"].includes(script)) { + console.log("ÿc4MFHelperÿc0: Ending script"); + return true; + } + } + + return false; + })) return true; + + // handled pre-reqs, now perform normal checks + let { task, msg, at, area } = taskList.shift(); + + switch (task) { + case "goto": + try { + // lets see if the task list contains any other goto messages, in case we were late + { + let gt = taskList.findIndex(el => el.task === "goto"); + if (gt > -1) { + // alright there is another so lets see where we should actually be going + for (let i = 0; i < gt - 1; i++) { + // feels hacky but this should remove all elements up to the next goto message, while preserving the order of list + ({ task, msg, at, area } = taskList.shift()); + } + } + } + + split = msg.substr(6); + console.log("ÿc4MFHelperÿc0: Goto " + split); - if (!!parseInt(split, 10)) { - split = parseInt(split, 10); - } - - Town.goToTown(split, true); - Town.move("portalspot"); - } catch (townerror) { - console.log(townerror); - } - - break; - case "nextup": - split = msg.split("nextup ")[1]; - console.log("ÿc4MFHelperÿc0: NextUp " + split); - - break; - case "cows": - console.log("ÿc4MFHelperÿc0: Clear Cows"); - - if (Misc.poll(() => { - Town.goToTown(1) && Pather.usePortal(sdk.areas.MooMooFarm); - return me.inArea(sdk.areas.MooMooFarm); - }, Time.minutes(1), 500 + me.ping)) { - include("core/Common/Cows.js"); - Precast.doPrecast(false); - Common.Cows.clearCowLevel(); - delay(1000); - } else { - console.warn("Failed to use portal. Currently in area: " + me.area); - } - - break; - case "council": - if (!me.inArea(sdk.areas.Travincal) && Town.goToTown(3)) { - Town.move("portalspot"); - Misc.poll(() => Pather.usePortal(sdk.areas.Travincal, player.name), Time.seconds(15), 500 + me.ping); - } - console.log("ÿc4MFHelperÿc0: Kill Council"); - Attack.clearClassids(sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3); - - break; - default: - // alright first lets check how long its been since the command was given - // this probably needs to be adjusted but for now 3 minutes on any of theses tasks is probably too long - if (getTickCount() - at > Time.minutes(3)) continue; - - /** + if (!!parseInt(split, 10)) { + split = parseInt(split, 10); + } + + Town.goToTown(split, true); + Town.move("portalspot"); + } catch (townerror) { + console.log(townerror); + } + + break; + case "nextup": + split = msg.split("nextup ")[1]; + console.log("ÿc4MFHelperÿc0: NextUp " + split); + + break; + case "cows": + console.log("ÿc4MFHelperÿc0: Clear Cows"); + + if (Misc.poll(() => { + Town.goToTown(1) && Pather.usePortal(sdk.areas.MooMooFarm); + return me.inArea(sdk.areas.MooMooFarm); + }, Time.minutes(1), 500 + me.ping)) { + include("core/Common/Cows.js"); + Precast.doPrecast(false); + Common.Cows.clearCowLevel(); + delay(1000); + } else { + console.warn("Failed to use portal. Currently in area: " + me.area); + } + + break; + case "council": + if (!me.inArea(sdk.areas.Travincal) && Town.goToTown(3)) { + Town.move("portalspot"); + Misc.poll(() => Pather.usePortal(sdk.areas.Travincal, player.name), Time.seconds(15), 500 + me.ping); + } + console.log("ÿc4MFHelperÿc0: Kill Council"); + Attack.clearClassids(sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3); + + break; + default: + // alright first lets check how long its been since the command was given + // this probably needs to be adjusted but for now 3 minutes on any of theses tasks is probably too long + if (getTickCount() - at > Time.minutes(3)) continue; + + /** * @todo still think this section needs to be done better, we are using a snapshot of the player's area at the time * of the message but sometimes the area hasn't been updated yet, causing us to do dumb things like attempt to kill * while still in town. We can't just use the players area though because of towncheck/chicken. Feel like best solution * would be adding area into leaders message and just always parsing it from there */ - try { - split = msg.split(task + " ")[1]; - if (parseInt(split, 10)) { - split = parseInt(split, 10); - } - } catch (e) { - console.warn(e.message || "Failed to get id from message split"); - break; - } - - if (me.area !== area) { - !me.inTown && Town.goToTown(); - - if (me.act !== sdk.areas.actOf(area)) { - Town.goToTown(sdk.areas.actOf(area)); - Town.move("portalspot"); - } - - String.isEqual(task, "clearlevel") - ? (area = split) - : (player.area !== area && !player.inTown) && (area = player.area); - - try { - Misc.poll(() => Pather.usePortal(null, player.name), Time.seconds(15), 500 + me.ping); - } catch (e) { - console.warn(e.message || "Failed to take leader portal"); - continue; - } - } - - if (!me.inTown && me.area === area) { - let forceCast = false; - (!lastPrecast || getTickCount() - lastPrecast > Time.minutes(2)) && (forceCast = true) && (lastPrecast = getTickCount()); - Precast.doPrecast(forceCast); - } else if (!me.inTown && !me.inArea(player.area)) { - Town.goToTown(sdk.areas.actOf(player.area)); - continue; - } - - switch (task) { - case "kill": - console.log("ÿc4MFHelperÿc0: Kill " + split); - - try { - Attack.kill(split); - Pickit.pickItems(); - } catch (killerror) { - console.error(killerror); - } - - break; - case "clearlevel": - try { - console.log("ÿc4MFHelperÿc0: Clear Level " + getAreaName(split)); - - if (me.area !== split) { - console.debug("I am in the wrong area? My Area: " + getAreaName(me.area) + " Wanted Area :: " + getAreaName(split)); - Town.goToTown(sdk.areas.actOf(split)); - Town.move("portalspot"); - if (!Misc.poll(() => Pather.usePortal(split, player.name), Time.seconds(15), 500 + me.ping)) { - throw new Error("Failed to move to clearlevel area"); - } - } - Attack.clearLevel(Config.ClearType); - } catch (killerror2) { - console.error(killerror2); - } - - break; - case "clear": - console.log("ÿc4MFHelperÿc0: Clear " + split); - - try { - Attack.clear(15, 0, split); - } catch (killerror2) { - console.error(killerror2); - } - - break; - } - - if (!Pather.getPortal(sdk.areas.townOf(me.act)) || !Pather.usePortal(sdk.areas.townOf(me.act))) { - Town.goToTown(); - } - } - } - } - - delay(100); - } - - return true; + try { + split = msg.split(task + " ")[1]; + if (parseInt(split, 10)) { + split = parseInt(split, 10); + } + } catch (e) { + console.warn(e.message || "Failed to get id from message split"); + break; + } + + if (me.area !== area) { + !me.inTown && Town.goToTown(); + + if (me.act !== sdk.areas.actOf(area)) { + Town.goToTown(sdk.areas.actOf(area)); + Town.move("portalspot"); + } + + String.isEqual(task, "clearlevel") + ? (area = split) + : (player.area !== area && !player.inTown) && (area = player.area); + + try { + Misc.poll(() => Pather.usePortal(null, player.name), Time.seconds(15), 500 + me.ping); + } catch (e) { + console.warn(e.message || "Failed to take leader portal"); + continue; + } + } + + if (!me.inTown && me.area === area) { + let forceCast = false; + if (!lastPrecast || getTickCount() - lastPrecast > Time.minutes(2)) { + (forceCast = true) && (lastPrecast = getTickCount()); + } + Precast.doPrecast(forceCast); + } else if (!me.inTown && !me.inArea(player.area)) { + Town.goToTown(sdk.areas.actOf(player.area)); + continue; + } + + switch (task) { + case "kill": + console.log("ÿc4MFHelperÿc0: Kill " + split); + + try { + Attack.kill(split); + Pickit.pickItems(); + } catch (killerror) { + console.error(killerror); + } + + break; + case "clearlevel": + try { + console.log("ÿc4MFHelperÿc0: Clear Level " + getAreaName(split)); + + if (me.area !== split) { + Town.goToTown(sdk.areas.actOf(split)); + Town.move("portalspot"); + if (!Misc.poll(() => Pather.usePortal(split, player.name), Time.seconds(15), 500 + me.ping)) { + throw new Error("Failed to move to clearlevel area"); + } + } + Attack.clearLevel(Config.ClearType); + } catch (killerror2) { + console.error(killerror2); + } + + break; + case "clear": + console.log("ÿc4MFHelperÿc0: Clear " + split); + + try { + Attack.clear(15, 0, split); + } catch (killerror2) { + console.error(killerror2); + } + + break; + } + + if (!Pather.getPortal(sdk.areas.townOf(me.act)) || !Pather.usePortal(sdk.areas.townOf(me.act))) { + Town.goToTown(); + } + } + } + } + + delay(100); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Mausoleum.js b/d2bs/kolbot/libs/scripts/Mausoleum.js index b8d4c854c..60d87445f 100644 --- a/d2bs/kolbot/libs/scripts/Mausoleum.js +++ b/d2bs/kolbot/libs/scripts/Mausoleum.js @@ -6,40 +6,40 @@ */ function Mausoleum() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); - - if (Config.Mausoleum.KillBishibosh) { - Pather.moveToPreset(sdk.areas.ColdPlains, sdk.unittype.Monster, sdk.monsters.preset.Bishibosh); - Attack.kill(getLocaleString(sdk.locale.monsters.Bishibosh)); - Pickit.pickItems(); - } - - if (!Pather.moveToExit(sdk.areas.BurialGrounds, true)) throw new Error("Failed to move to Burial Grounds"); - - if (Config.Mausoleum.KillBloodRaven) { - Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven); - Attack.kill(getLocaleString(sdk.locale.monsters.BloodRaven)); - Pickit.pickItems(); - } - - try { - Pather.moveToExit(sdk.areas.Mausoleum, true) && Attack.clearLevel(Config.ClearType); - } catch (e) { - console.error(e); - } - - if (Config.Mausoleum.ClearCrypt) { - // Crypt exit is... awkward - if (!(Pather.moveToExit(sdk.areas.BurialGrounds, true) + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); + + if (Config.Mausoleum.KillBishibosh) { + Pather.moveToPreset(sdk.areas.ColdPlains, sdk.unittype.Monster, sdk.monsters.preset.Bishibosh); + Attack.kill(getLocaleString(sdk.locale.monsters.Bishibosh)); + Pickit.pickItems(); + } + + if (!Pather.moveToExit(sdk.areas.BurialGrounds, true)) throw new Error("Failed to move to Burial Grounds"); + + if (Config.Mausoleum.KillBloodRaven) { + Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven); + Attack.kill(getLocaleString(sdk.locale.monsters.BloodRaven)); + Pickit.pickItems(); + } + + try { + Pather.moveToExit(sdk.areas.Mausoleum, true) && Attack.clearLevel(Config.ClearType); + } catch (e) { + console.error(e); + } + + if (Config.Mausoleum.ClearCrypt) { + // Crypt exit is... awkward + if (!(Pather.moveToExit(sdk.areas.BurialGrounds, true) && Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Stairs, sdk.exits.preset.Crypt) && Pather.moveToExit(sdk.areas.Crypt, true))) { - throw new Error("Failed to move to Crypt"); - } + throw new Error("Failed to move to Crypt"); + } - Attack.clearLevel(Config.ClearType); - } + Attack.clearLevel(Config.ClearType); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js index d58ca7375..88f9affa2 100644 --- a/d2bs/kolbot/libs/scripts/Mephisto.js +++ b/d2bs/kolbot/libs/scripts/Mephisto.js @@ -6,144 +6,147 @@ */ function Mephisto() { - this.killMephisto = function () { - let pos = {}; - let attackCount = 0; - let meph = Game.getMonster(sdk.monsters.Mephisto); - if (!meph) throw new Error("Mephisto not found!"); - - Config.MFLeader && Pather.makePortal() && say("kill " + meph.classid); - - while (attackCount < 300 && meph.attackable(meph)) { - if (meph.mode === sdk.monsters.mode.Attacking2) { - let angle = Math.round(Math.atan2(me.y - meph.y, me.x - meph.x) * 180 / Math.PI); - let angles = me.y > meph.y ? [-30, -60, -90] : [30, 60, 90]; - - for (let i = 0; i < angles.length; i += 1) { - pos.dist = 18; - pos.x = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.x); - pos.y = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.y); - - if (Attack.validSpot(pos.x, pos.y)) { - me.overhead("move, bitch!"); - Pather.moveTo(pos.x, pos.y); - - break; - } - } - } - - if (ClassAttack.doAttack(meph) < 2) { - break; - } - - attackCount += 1; - } - - return meph.dead; - }; - - this.moat = function () { - let count = 0; - - delay(350); - Pather.moveTo(17563, 8072); - - let mephisto = Game.getMonster(sdk.monsters.Mephisto); - if (!mephisto) throw new Error("Mephisto not found."); - - delay(350); - Pather.moveTo(17575, 8086) && delay(350); - Pather.moveTo(17584, 8091) && delay(1200); - Pather.moveTo(17600, 8095) && delay(550); - Pather.moveTo(17610, 8094) && delay(2500); - Attack.clear(10); - Pather.moveTo(17610, 8094); - - let distance = getDistance(me, mephisto); - - while (distance > 27) { - count += 1; - - Pather.moveTo(17600, 8095) && delay(150); - Pather.moveTo(17584, 8091) && delay(150); - Pather.moveTo(17575, 8086) && delay(150); - Pather.moveTo(17563, 8072) && delay(350); - Pather.moveTo(17575, 8086) && delay(350); - Pather.moveTo(17584, 8091) && delay(1200); - Pather.moveTo(17600, 8095) && delay(550); - Pather.moveTo(17610, 8094) && delay(2500); - Attack.clear(10); - Pather.moveTo(17610, 8094); - - distance = getDistance(me, mephisto); - - if (count >= 5) { - throw new Error("Failed to lure Mephisto."); - } - } - - return true; - }; - - this.killCouncil = function () { - let coords = [17600, 8125, 17600, 8015, 17643, 8068]; - - for (let i = 0; i < coords.length; i += 2) { - Pather.moveTo(coords[i], coords[i + 1]); - Attack.clearList(Attack.getMob([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3], 0, 40)); - } - - return true; - }; - - Town.doChores(); - Pather.useWaypoint(sdk.areas.DuranceofHateLvl2); - Precast.doPrecast(true); - - if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true)) throw new Error("Failed to move to Durance Level 3"); - - Config.Mephisto.KillCouncil && this.killCouncil(); - - if (Config.Mephisto.TakeRedPortal) { - Pather.moveTo(17590, 8068); - delay(400); // Activate the bridge tile - } else { - Pather.moveTo(17566, 8069); - } - - if (me.sorceress && Config.Mephisto.MoatTrick && Pather.canTeleport()) { - this.moat(); - Skill.usePvpRange = true; - Attack.kill(sdk.monsters.Mephisto); - Skill.usePvpRange = false; - } else { - Attack.kill(sdk.monsters.Mephisto); - } - - Pickit.pickItems(); - - if (Config.OpenChests.Enabled) { - Pather.moveTo(17572, 8011) && Attack.openChests(5); - Pather.moveTo(17572, 8125) && Attack.openChests(5); - Pather.moveTo(17515, 8061) && Attack.openChests(5); - } - - if (Config.Mephisto.TakeRedPortal) { - Pather.moveTo(17590, 8068); - let tick = getTickCount(), time = 0; - - // Wait until bridge is there - while (getCollision(me.area, 17601, 8070, 17590, 8068) !== 0 && (time = getTickCount() - tick) < 2000) { - Pather.moveTo(17590, 8068); // Activate it - delay(3); - } - - // If bridge is there, and we can move to the location - if (time < 2000 && Pather.moveTo(17601, 8070)) { - Pather.usePortal(null); - } - } - - return true; + // eslint-disable-next-line no-unused-vars + const killMephisto = function () { + let pos = {}; + let attackCount = 0; + let meph = Game.getMonster(sdk.monsters.Mephisto); + if (!meph) throw new Error("Mephisto not found!"); + + Config.MFLeader && Pather.makePortal() && say("kill " + meph.classid); + + while (attackCount < 300 && meph.attackable(meph)) { + if (meph.mode === sdk.monsters.mode.Attacking2) { + let angle = Math.round(Math.atan2(me.y - meph.y, me.x - meph.x) * 180 / Math.PI); + let angles = me.y > meph.y ? [-30, -60, -90] : [30, 60, 90]; + + for (let i = 0; i < angles.length; i += 1) { + pos.dist = 18; + pos.x = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.x); + pos.y = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.y); + + if (Attack.validSpot(pos.x, pos.y)) { + me.overhead("move, bitch!"); + Pather.moveTo(pos.x, pos.y); + + break; + } + } + } + + if (ClassAttack.doAttack(meph) < 2) { + break; + } + + attackCount += 1; + } + + return meph.dead; + }; + + const moat = function () { + let count = 0; + + delay(350); + Pather.moveTo(17563, 8072); + + let mephisto = Game.getMonster(sdk.monsters.Mephisto); + if (!mephisto) throw new Error("Mephisto not found."); + + delay(350); + Pather.moveTo(17575, 8086) && delay(350); + Pather.moveTo(17584, 8091) && delay(1200); + Pather.moveTo(17600, 8095) && delay(550); + Pather.moveTo(17610, 8094) && delay(2500); + Attack.clear(10); + Pather.moveTo(17610, 8094); + + let distance = getDistance(me, mephisto); + + while (distance > 27) { + count += 1; + + Pather.moveTo(17600, 8095) && delay(150); + Pather.moveTo(17584, 8091) && delay(150); + Pather.moveTo(17575, 8086) && delay(150); + Pather.moveTo(17563, 8072) && delay(350); + Pather.moveTo(17575, 8086) && delay(350); + Pather.moveTo(17584, 8091) && delay(1200); + Pather.moveTo(17600, 8095) && delay(550); + Pather.moveTo(17610, 8094) && delay(2500); + Attack.clear(10); + Pather.moveTo(17610, 8094); + + distance = getDistance(me, mephisto); + + if (count >= 5) { + throw new Error("Failed to lure Mephisto."); + } + } + + return true; + }; + + const killCouncil = function () { + let coords = [17600, 8125, 17600, 8015, 17643, 8068]; + + for (let i = 0; i < coords.length; i += 2) { + Pather.moveTo(coords[i], coords[i + 1]); + Attack.clearList(Attack.getMob([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3], 0, 40)); + } + + return true; + }; + + Town.doChores(); + Pather.useWaypoint(sdk.areas.DuranceofHateLvl2); + Precast.doPrecast(true); + + if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true)) { + throw new Error("Failed to move to Durance Level 3"); + } + + Config.Mephisto.KillCouncil && killCouncil(); + + if (Config.Mephisto.TakeRedPortal) { + Pather.moveTo(17590, 8068); + delay(400); // Activate the bridge tile + } else { + Pather.moveTo(17566, 8069); + } + + if (me.sorceress && Config.Mephisto.MoatTrick && Pather.canTeleport()) { + moat(); + Skill.usePvpRange = true; + Attack.kill(sdk.monsters.Mephisto); + Skill.usePvpRange = false; + } else { + Attack.kill(sdk.monsters.Mephisto); + } + + Pickit.pickItems(); + + if (Config.OpenChests.Enabled) { + Pather.moveTo(17572, 8011) && Attack.openChests(5); + Pather.moveTo(17572, 8125) && Attack.openChests(5); + Pather.moveTo(17515, 8061) && Attack.openChests(5); + } + + if (Config.Mephisto.TakeRedPortal) { + Pather.moveTo(17590, 8068); + let tick = getTickCount(), time = 0; + + // Wait until bridge is there + while (getCollision(me.area, 17601, 8070, 17590, 8068) !== 0 && (time = getTickCount() - tick) < 2000) { + Pather.moveTo(17590, 8068); // Activate it + delay(3); + } + + // If bridge is there, and we can move to the location + if (time < 2000 && Pather.moveTo(17601, 8070)) { + Pather.usePortal(null); + } + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js index 7f2f1a56e..c27ba2bc1 100644 --- a/d2bs/kolbot/libs/scripts/Nihlathak.js +++ b/d2bs/kolbot/libs/scripts/Nihlathak.js @@ -6,32 +6,34 @@ */ function Nihlathak() { - Town.goToTown(5); - Town.doChores(); + Town.goToTown(5); + Town.doChores(); - !Pather.initialized && Pather.useWaypoint(null, true); + !Pather.initialized && Pather.useWaypoint(null, true); - // UseWaypoint if set to or if we already have it - if (Config.Nihlathak.UseWaypoint || getWaypoint(Pather.wpAreas.indexOf(sdk.areas.HallsofPain))) { - Pather.useWaypoint(sdk.areas.HallsofPain); - } else { - Pather.journeyTo(sdk.areas.NihlathaksTemple) && Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain], true); - } + // UseWaypoint if set to or if we already have it + if (Config.Nihlathak.UseWaypoint || getWaypoint(Pather.wpAreas.indexOf(sdk.areas.HallsofPain))) { + Pather.useWaypoint(sdk.areas.HallsofPain); + } else { + if (Pather.journeyTo(sdk.areas.NihlathaksTemple)) { + Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain], true); + } + } - Precast.doPrecast(false); + Precast.doPrecast(false); - if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) throw new Error("Failed to go to Nihlathak"); + if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) throw new Error("Failed to go to Nihlathak"); - // faster detection of TombVipers - Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { callback: () => { - if (Config.Nihlathak.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { - console.log("Tomb Vipers found."); - throw new ScriptError("Tomb Vipers found."); - } - }}); + // faster detection of TombVipers + Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { callback: () => { + if (Config.Nihlathak.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); + throw new ScriptError("Tomb Vipers found."); + } + } }); - Attack.kill(sdk.monsters.Nihlathak); - Pickit.pickItems(); + Attack.kill(sdk.monsters.Nihlathak); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index 8c0895d3b..7db9b72db 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -18,564 +18,574 @@ */ function OrgTorch () { - let currentGameInfo = null; - - const portalMode = { - MiniUbers: 0, - UberTristram: 1 - }; - - const OrgTorchData = { - filePath: "logs/OrgTorch-" + me.profile + ".json", - _default: { gamename: me.gamename, doneAreas: [] }, - - create: function () { - FileTools.writeText(this.filePath, JSON.stringify(this._default)); - return this._default; - }, - - read: function () { - let obj = {}; - try { - let string = FileTools.readText(this.filePath); - obj = JSON.parse(string); - } catch (e) { - return this._default; - } - - return obj; - }, - - update: function (newData) { - let data = this.read(); - Object.assign(data, newData); - FileTools.writeText(this.filePath, JSON.stringify(data)); - }, - - remove: function () { - return FileTools.remove(this.filePath); - } - }; - - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - const getQuestItem = function (item) { - if (item) { - let id = item.classid; - let canFit = Storage.Inventory.CanFit(item); - if (!canFit && Pickit.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); - Town.visitTown(); - !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); - } - } - return Pickit.pickItem(item); - }; - - // Identify & mule - const checkTorch = function () { - if (me.inArea(sdk.areas.UberTristram)) { - Pather.moveTo(25105, 5140); - Pather.usePortal(sdk.areas.Harrogath); - } - - Town.doChores(); - - if (!Config.OrgTorch.MakeTorch) return false; - - let torch = me.checkItem({ classid: sdk.items.LargeCharm, quality: sdk.items.quality.Unique }); - - if (torch.have && Pickit.checkItem(torch.item).result === Pickit.Result.WANTED) { - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleTorch"); - scriptBroadcast("quit"); - } - - return true; - } - - return false; - }; - - /** - * Try to lure a monster - wait until it's close enough - * @param {number} bossId - * @returns {boolean} - * @todo redo this - * - should, lure boss AWAY from the others and to us - * - create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses - */ - const lure = function (bossId) { - let unit = Game.getMonster(bossId); - - if (unit) { - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (unit.distance <= 10) { - return true; - } - - delay(50); - } - } - - return false; - }; - - /** - * Check if we have complete sets of organs - * @returns {boolean} - */ - const completeSetCheck = function () { - let [horns, brains, eyes] = [0, 0, 0]; - me.getItemsEx() - .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) - .forEach(i => { - switch (i.classid) { - case sdk.items.quest.DiablosHorn: - return (horns++); - case sdk.items.quest.MephistosBrain: - return (brains++); - case sdk.items.quest.BaalsEye: - return (eyes++); - default: return 0; - } - }); - - if (!horns || !brains || !eyes) { - return false; - } - - // We just need one set to make a torch - if (Config.OrgTorch.MakeTorch) { - return horns > 0 && brains > 0 && eyes > 0; - } - - return horns === brains && horns === eyes && brains === eyes; - }; - - /** - * Get fade in River of Flames - only works if we are wearing an item with ctc Fade - * @returns {boolean} - * @todo equipping an item from storage if we have it - */ - const getFade = function () { - if (Config.OrgTorch.GetFade && !me.getState(sdk.states.Fade) - && me.haveSome([ - { name: sdk.locale.items.Treachery, equipped: true }, - { name: sdk.locale.items.LastWish, equipped: true }, - { name: sdk.locale.items.SpiritWard, equipped: true } - ])) { - console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); - // lets figure out what fade item we have before we leave town - let fadeItem = me.findFirst([ - {name: sdk.locale.items.Treachery, equipped: true}, - {name: sdk.locale.items.LastWish, equipped: true}, - {name: sdk.locale.items.SpiritWard, equipped: true} - ]); - - Pather.useWaypoint(sdk.areas.RiverofFlame); - Precast.doPrecast(true); - // check if item is on switch - let mainSlot; - - Pather.moveTo(7811, 5872); - - if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { - mainSlot = me.weaponswitch; - me.switchWeapons(sdk.player.slot.Secondary); - } - - Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - - while (!me.getState(sdk.states.Fade)) { - delay(100); - } - - mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); - - console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.Green + "Fade Achieved"); - } - - return true; - }; - - /** - * Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram - * @param {number} mode - * @returns {ObjectUnit | false} - */ - const openPortal = function (mode) { - let item1 = mode === portalMode.MiniUbers ? me.findItem("pk1", sdk.items.mode.inStorage) : me.findItem("dhn", sdk.items.mode.inStorage); - let item2 = mode === portalMode.MiniUbers ? me.findItem("pk2", sdk.items.mode.inStorage) : me.findItem("bey", sdk.items.mode.inStorage); - let item3 = mode === portalMode.MiniUbers ? me.findItem("pk3", sdk.items.mode.inStorage) : me.findItem("mbr", 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); - - let portal = Game.getObject(sdk.objects.RedPortal); - - if (portal) { - do { - switch (mode) { - case portalMode.MiniUbers: - if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype) - && currentGameInfo.doneAreas.indexOf(portal.objtype) === -1) { - return copyUnit(portal); - } - - break; - case portalMode.UberTristram: - if (portal.objtype === sdk.areas.UberTristram) { - return copyUnit(portal); - } - - break; - } - } while (portal.getNext()); - } - } - - return false; - }; - - const matronsDen = function () { - let dHorns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length; - - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.MatronsDen, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); - Attack.kill(sdk.monsters.Lilith); - Pickit.pickItems(); - getQuestItem(Game.getItem(sdk.items.quest.DiablosHorn)); - Town.goToTown(); - - // we sucessfully picked up the horn - return (me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length > dHorns); - }; - - const forgottenSands = function () { - let bEyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length; - - Precast.doPrecast(true); - - let nodes = [ - {x: 20196, y: 8694}, - {x: 20308, y: 8588}, - {x: 20187, y: 8639}, - {x: 20100, y: 8550}, - {x: 20103, y: 8688}, - {x: 20144, y: 8709}, - {x: 20263, y: 8811}, - {x: 20247, y: 8665}, - ]; - - try { - for (let i = 0; i < nodes.length; i++) { - Pather.moveTo(nodes[i].x, nodes[i].y); - delay(500); - - if (Game.getMonster(sdk.monsters.UberDuriel)) { - break; - } - - let eye = Game.getItem(sdk.items.quest.BaalsEye, sdk.items.mode.onGround); - - if (eye && Pickit.pickItem(eye)) { - throw new Error("Found an picked wanted organ"); - } - } - - Attack.kill(sdk.monsters.UberDuriel); - Pickit.pickItems(); - getQuestItem(Game.getItem(sdk.items.quest.BaalsEye)); - Town.goToTown(); - } catch (e) { - // - } - - // we sucessfully picked up the eye - return (me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length > bEyes); - }; - - const furnance = function () { - let mBrain = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length; - - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); - Attack.kill(sdk.monsters.UberIzual); - Pickit.pickItems(); - getQuestItem(Game.getItem(sdk.items.quest.MephistosBrain)); - Town.goToTown(); - - // we sucessfully picked up the brain - return (me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length > mBrain); - }; - - /** - * @todo re-write this, lure doesn't always work and other classes can do ubers - */ - const uberTrist = function () { - let skillBackup; - let useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); - - Pather.moveTo(25068, 5078); - Precast.doPrecast(true); - - let nodes = [ - {x: 25040, y: 5101}, - {x: 25040, y: 5166}, - {x: 25122, y: 5170}, - ]; - - for (let i = 0; i < nodes.length; i++) { - Pather.moveTo(nodes[i].x, nodes[i].y); - } - - useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - lure(sdk.monsters.UberMephisto); - Pather.moveTo(25129, 5198); - useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - lure(sdk.monsters.UberMephisto); - - if (!Game.getMonster(sdk.monsters.UberMephisto)) { - Pather.moveTo(25122, 5170); - } - - if (useSalvation) { - skillBackup = Config.AttackSkill[2]; - Config.AttackSkill[2] = sdk.skills.Salvation; - - Attack.init(); - } - - Attack.kill(sdk.monsters.UberMephisto); - - if (skillBackup && useSalvation) { - Config.AttackSkill[2] = skillBackup; - - Attack.init(); - } - - Pather.moveTo(25162, 5141); - delay(3250); - - if (!Game.getMonster(sdk.monsters.UberDiablo)) { - Pather.moveTo(25122, 5170); - } - - Attack.kill(sdk.monsters.UberDiablo); - - if (!Game.getMonster(sdk.monsters.UberBaal)) { - Pather.moveTo(25122, 5170); - } - - Attack.kill(sdk.monsters.UberBaal); - Pickit.pickItems(); - currentGameInfo.doneAreas.push(sdk.areas.UberTristram) && OrgTorchData.update(currentGameInfo); - checkTorch(); - }; - - /** - * Do mini ubers or Tristram based on area we're already in - * @param {number} portalId - */ - const pandemoniumRun = function (portalId) { - switch (me.area) { - case sdk.areas.MatronsDen: - if (matronsDen()) { - currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); - } - - break; - case sdk.areas.ForgottenSands: - if (forgottenSands()) { - currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); - } - - break; - case sdk.areas.FurnaceofPain: - if (furnance()) { - currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); - } - - break; - case sdk.areas.UberTristram: - uberTrist(); - - break; - } - }; - - /** - * @param {ObjectUnit} portal - */ - const runEvent = function (portal) { - if (portal) { - if (Config.OrgTorch.PreGame.Antidote.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Antidote.Drink > 0) { - Town.buyPots(Config.OrgTorch.PreGame.Antidote.Drink, "Antidote", true, true); - } - if (Config.OrgTorch.PreGame.Thawing.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Thawing.Drink > 0) { - Town.buyPots(Config.OrgTorch.PreGame.Thawing.Drink, "Thawing", true, true); - } - Town.move("stash"); - console.log("taking portal: " + portal.objtype); - Pather.usePortal(null, null, portal); - pandemoniumRun(portal.objtype); - } - }; - - // ################# // - /* ##### START ##### */ - // ################# // - - // make sure we are picking the organs - Config.PickitFiles.length === 0 && NTIP.OpenFile("pickit/keyorg.nip", true); - - FileTools.exists(OrgTorchData.filePath) && (currentGameInfo = OrgTorchData.read()); - - if (!currentGameInfo || currentGameInfo.gamename !== me.gamename) { - currentGameInfo = OrgTorchData.create(); - } - - let portal; - let [tkeys, hkeys, dkeys] = [0, 0, 0]; - let [brains, eyes, horns] = [0, 0, 0]; - - me.getItemsEx() - .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) - .forEach(i => { - switch (i.classid) { - case sdk.items.quest.KeyofTerror: - return (tkeys++); - case sdk.items.quest.KeyofHate: - return (hkeys++); - case sdk.items.quest.KeyofDestruction: - return (dkeys++); - case sdk.items.quest.DiablosHorn: - return (horns++); - case sdk.items.quest.MephistosBrain: - return (brains++); - case sdk.items.quest.BaalsEye: - return (eyes++); - default: return 0; - } - }); - - // Do town chores and quit if MakeTorch is true and we have a torch. - checkTorch(); - - // Wait for other bots to drop off their keys. This works only if TorchSystem.js is configured properly. - Config.OrgTorch.WaitForKeys && TorchSystem.waitForKeys(); - - Town.goToTown(5); - Town.move("stash"); - - let redPortals = getUnits(sdk.unittype.Object, sdk.objects.RedPortal) - .filter(el => [sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain, sdk.areas.UberTristram].includes(el.objtype)); - let miniPortals = 0; - let keySetsReq = 3; - let tristOpen = false; - - if (redPortals.length > 0) { - redPortals.forEach(function (portal) { - if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype)) { - miniPortals++; - keySetsReq--; - } else if (portal.objtype === sdk.areas.UberTristram) { - tristOpen = true; - } - }); - } else { - // possible same game name but different day and data file never got deleted - currentGameInfo.doneAreas.length > 0 && (currentGameInfo = OrgTorchData.create()); - } - - // End the script if we don't have enough keys nor organs - if ((tkeys < keySetsReq || hkeys < keySetsReq || dkeys < keySetsReq) && (brains < 1 || eyes < 1 || horns < 1) && !tristOpen) { - console.log("Not enough keys or organs."); - OrgTorchData.remove(); - - return true; - } - - Config.UseMerc = false; - - // We have enough keys, do mini ubers - if (tkeys >= keySetsReq && hkeys >= keySetsReq && dkeys >= keySetsReq) { - getFade(); - Town.goToTown(5); - console.log("Making organs."); - D2Bot.printToConsole("OrgTorch: Making organs.", sdk.colors.D2Bot.DarkGold); - Town.move("stash"); - - // there are already open portals lets check our info on them - if (miniPortals > 0) { - for (let i = 0; i < miniPortals; i++) { - // mini-portal is up but its not in our done areas, probably chickend during it, lets try again - if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(redPortals[i].objtype) - && !currentGameInfo.doneAreas.includes(redPortals[i].objtype)) { - portal = redPortals[i]; - runEvent(portal); - } - } - } - - for (let i = 0; i < keySetsReq; i += 1) { - // Abort if we have a complete set of organs - // If Config.OrgTorch.MakeTorch is false, check after at least one portal is made - if ((Config.OrgTorch.MakeTorch || i > 0) && completeSetCheck()) { - break; - } - - portal = openPortal(portalMode.MiniUbers); - runEvent(portal); - } - } - - // Don't make torches if not configured to OR if the char already has one - if (!Config.OrgTorch.MakeTorch || checkTorch()) { - OrgTorchData.remove(); - - return true; - } - - // Count organs - brains = me.findItems("mbr", sdk.items.mode.inStorage).length || 0; - eyes = me.findItems("bey", sdk.items.mode.inStorage).length || 0; - horns = me.findItems("dhn", sdk.items.mode.inStorage).length || 0; - - // We have enough organs, do Tristram - or trist is open we may have chickened and came back so check it - // if trist was already open when we joined should we run that first? - if ((brains && eyes && horns) || tristOpen) { - getFade(); - Town.goToTown(5); - Town.move("stash"); - - if (!tristOpen) { - console.log("Making torch"); - D2Bot.printToConsole("OrgTorch: Making torch.", sdk.colors.D2Bot.DarkGold); - portal = openPortal(portalMode.UberTristram); - } else { - portal = Pather.getPortal(sdk.areas.UberTristram); - } - - runEvent(portal); - OrgTorchData.remove(); - } - - return true; + let currentGameInfo = null; + + const portalMode = { + MiniUbers: 0, + UberTristram: 1 + }; + + const OrgTorchData = { + filePath: "logs/OrgTorch-" + me.profile + ".json", + _default: { gamename: me.gamename, doneAreas: [] }, + + create: function () { + FileTools.writeText(this.filePath, JSON.stringify(this._default)); + return this._default; + }, + + read: function () { + let obj = {}; + try { + let string = FileTools.readText(this.filePath); + obj = JSON.parse(string); + } catch (e) { + return this._default; + } + + return obj; + }, + + update: function (newData) { + let data = this.read(); + Object.assign(data, newData); + FileTools.writeText(this.filePath, JSON.stringify(data)); + }, + + remove: function () { + return FileTools.remove(this.filePath); + } + }; + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + const getQuestItem = function (item) { + if (item) { + let id = item.classid; + let canFit = Storage.Inventory.CanFit(item); + if (!canFit && Pickit.canMakeRoom()) { + console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); + Town.visitTown(); + !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); + } + } + return Pickit.pickItem(item); + }; + + // Identify & mule + const checkTorch = function () { + if (me.inArea(sdk.areas.UberTristram)) { + Pather.moveTo(25105, 5140); + Pather.usePortal(sdk.areas.Harrogath); + } + + Town.doChores(); + + if (!Config.OrgTorch.MakeTorch) return false; + + let torch = me.checkItem({ classid: sdk.items.LargeCharm, quality: sdk.items.quality.Unique }); + + if (torch.have && Pickit.checkItem(torch.item).result === Pickit.Result.WANTED) { + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleTorch"); + scriptBroadcast("quit"); + } + + return true; + } + + return false; + }; + + /** + * Try to lure a monster - wait until it's close enough + * @param {number} bossId + * @returns {boolean} + * @todo redo this + * - should, lure boss AWAY from the others and to us + * - create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses + */ + const lure = function (bossId) { + let unit = Game.getMonster(bossId); + + if (unit) { + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (unit.distance <= 10) { + return true; + } + + delay(50); + } + } + + return false; + }; + + /** + * Check if we have complete sets of organs + * @returns {boolean} + */ + const completeSetCheck = function () { + let [horns, brains, eyes] = [0, 0, 0]; + me.getItemsEx() + .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) + .forEach(i => { + switch (i.classid) { + case sdk.items.quest.DiablosHorn: + return (horns++); + case sdk.items.quest.MephistosBrain: + return (brains++); + case sdk.items.quest.BaalsEye: + return (eyes++); + default: return 0; + } + }); + + if (!horns || !brains || !eyes) { + return false; + } + + // We just need one set to make a torch + if (Config.OrgTorch.MakeTorch) { + return horns > 0 && brains > 0 && eyes > 0; + } + + return horns === brains && horns === eyes && brains === eyes; + }; + + /** + * Get fade in River of Flames - only works if we are wearing an item with ctc Fade + * @returns {boolean} + * @todo equipping an item from storage if we have it + */ + const getFade = function () { + if (Config.OrgTorch.GetFade && !me.getState(sdk.states.Fade) + && me.haveSome([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ])) { + console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); + // lets figure out what fade item we have before we leave town + let fadeItem = me.findFirst([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ]); + + Pather.useWaypoint(sdk.areas.RiverofFlame); + Precast.doPrecast(true); + // check if item is on switch + let mainSlot; + + Pather.moveTo(7811, 5872); + + if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { + mainSlot = me.weaponswitch; + me.switchWeapons(sdk.player.slot.Secondary); + } + + Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + + while (!me.getState(sdk.states.Fade)) { + delay(100); + } + + mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); + + console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.Green + "Fade Achieved"); + } + + return true; + }; + + /** + * Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram + * @param {number} mode + * @returns {ObjectUnit | false} + */ + const openPortal = function (mode) { + let item1 = mode === portalMode.MiniUbers + ? me.findItem("pk1", sdk.items.mode.inStorage) + : me.findItem("dhn", sdk.items.mode.inStorage); + let item2 = mode === portalMode.MiniUbers + ? me.findItem("pk2", sdk.items.mode.inStorage) + : me.findItem("bey", sdk.items.mode.inStorage); + let item3 = mode === portalMode.MiniUbers + ? me.findItem("pk3", sdk.items.mode.inStorage) + : me.findItem("mbr", 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); + + let portal = Game.getObject(sdk.objects.RedPortal); + + if (portal) { + do { + switch (mode) { + case portalMode.MiniUbers: + if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype) + && currentGameInfo.doneAreas.indexOf(portal.objtype) === -1) { + return copyUnit(portal); + } + + break; + case portalMode.UberTristram: + if (portal.objtype === sdk.areas.UberTristram) { + return copyUnit(portal); + } + + break; + } + } while (portal.getNext()); + } + } + + return false; + }; + + const matronsDen = function () { + let dHorns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length; + + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.MatronsDen, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); + Attack.kill(sdk.monsters.Lilith); + Pickit.pickItems(); + getQuestItem(Game.getItem(sdk.items.quest.DiablosHorn)); + Town.goToTown(); + + // we sucessfully picked up the horn + return (me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length > dHorns); + }; + + const forgottenSands = function () { + let bEyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length; + + Precast.doPrecast(true); + + let nodes = [ + { x: 20196, y: 8694 }, + { x: 20308, y: 8588 }, + { x: 20187, y: 8639 }, + { x: 20100, y: 8550 }, + { x: 20103, y: 8688 }, + { x: 20144, y: 8709 }, + { x: 20263, y: 8811 }, + { x: 20247, y: 8665 }, + ]; + + try { + for (let i = 0; i < nodes.length; i++) { + Pather.moveTo(nodes[i].x, nodes[i].y); + delay(500); + + if (Game.getMonster(sdk.monsters.UberDuriel)) { + break; + } + + let eye = Game.getItem(sdk.items.quest.BaalsEye, sdk.items.mode.onGround); + + if (eye && Pickit.pickItem(eye)) { + throw new Error("Found an picked wanted organ"); + } + } + + Attack.kill(sdk.monsters.UberDuriel); + Pickit.pickItems(); + getQuestItem(Game.getItem(sdk.items.quest.BaalsEye)); + Town.goToTown(); + } catch (e) { + // + } + + // we sucessfully picked up the eye + return (me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length > bEyes); + }; + + const furnance = function () { + let mBrain = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length; + + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); + Attack.kill(sdk.monsters.UberIzual); + Pickit.pickItems(); + getQuestItem(Game.getItem(sdk.items.quest.MephistosBrain)); + Town.goToTown(); + + // we sucessfully picked up the brain + return (me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length > mBrain); + }; + + /** + * @todo re-write this, lure doesn't always work and other classes can do ubers + */ + const uberTrist = function () { + let skillBackup; + let useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); + + Pather.moveTo(25068, 5078); + Precast.doPrecast(true); + + let nodes = [ + { x: 25040, y: 5101 }, + { x: 25040, y: 5166 }, + { x: 25122, y: 5170 }, + ]; + + for (let i = 0; i < nodes.length; i++) { + Pather.moveTo(nodes[i].x, nodes[i].y); + } + + useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + lure(sdk.monsters.UberMephisto); + Pather.moveTo(25129, 5198); + useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + lure(sdk.monsters.UberMephisto); + + if (!Game.getMonster(sdk.monsters.UberMephisto)) { + Pather.moveTo(25122, 5170); + } + + if (useSalvation) { + skillBackup = Config.AttackSkill[2]; + Config.AttackSkill[2] = sdk.skills.Salvation; + + Attack.init(); + } + + Attack.kill(sdk.monsters.UberMephisto); + + if (skillBackup && useSalvation) { + Config.AttackSkill[2] = skillBackup; + + Attack.init(); + } + + Pather.moveTo(25162, 5141); + delay(3250); + + if (!Game.getMonster(sdk.monsters.UberDiablo)) { + Pather.moveTo(25122, 5170); + } + + Attack.kill(sdk.monsters.UberDiablo); + + if (!Game.getMonster(sdk.monsters.UberBaal)) { + Pather.moveTo(25122, 5170); + } + + Attack.kill(sdk.monsters.UberBaal); + Pickit.pickItems(); + currentGameInfo.doneAreas.push(sdk.areas.UberTristram) && OrgTorchData.update(currentGameInfo); + checkTorch(); + }; + + /** + * Do mini ubers or Tristram based on area we're already in + * @param {number} portalId + */ + const pandemoniumRun = function (portalId) { + switch (me.area) { + case sdk.areas.MatronsDen: + if (matronsDen()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + } + + break; + case sdk.areas.ForgottenSands: + if (forgottenSands()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + } + + break; + case sdk.areas.FurnaceofPain: + if (furnance()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + } + + break; + case sdk.areas.UberTristram: + uberTrist(); + + break; + } + }; + + /** + * @param {ObjectUnit} portal + */ + const runEvent = function (portal) { + if (portal) { + if (Config.OrgTorch.PreGame.Antidote.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Antidote.Drink > 0) { + Town.buyPots(Config.OrgTorch.PreGame.Antidote.Drink, "Antidote", true, true); + } + if (Config.OrgTorch.PreGame.Thawing.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Thawing.Drink > 0) { + Town.buyPots(Config.OrgTorch.PreGame.Thawing.Drink, "Thawing", true, true); + } + Town.move("stash"); + console.log("taking portal: " + portal.objtype); + Pather.usePortal(null, null, portal); + pandemoniumRun(portal.objtype); + } + }; + + // ################# // + /* ##### START ##### */ + // ################# // + + // make sure we are picking the organs + Config.PickitFiles.length === 0 && NTIP.OpenFile("pickit/keyorg.nip", true); + + FileTools.exists(OrgTorchData.filePath) && (currentGameInfo = OrgTorchData.read()); + + if (!currentGameInfo || currentGameInfo.gamename !== me.gamename) { + currentGameInfo = OrgTorchData.create(); + } + + let portal; + let [tkeys, hkeys, dkeys] = [0, 0, 0]; + let [brains, eyes, horns] = [0, 0, 0]; + + me.getItemsEx() + .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) + .forEach(i => { + switch (i.classid) { + case sdk.items.quest.KeyofTerror: + return (tkeys++); + case sdk.items.quest.KeyofHate: + return (hkeys++); + case sdk.items.quest.KeyofDestruction: + return (dkeys++); + case sdk.items.quest.DiablosHorn: + return (horns++); + case sdk.items.quest.MephistosBrain: + return (brains++); + case sdk.items.quest.BaalsEye: + return (eyes++); + default: return 0; + } + }); + + // Do town chores and quit if MakeTorch is true and we have a torch. + checkTorch(); + + // Wait for other bots to drop off their keys. This works only if TorchSystem.js is configured properly. + Config.OrgTorch.WaitForKeys && TorchSystem.waitForKeys(); + + Town.goToTown(5); + Town.move("stash"); + + let redPortals = getUnits(sdk.unittype.Object, sdk.objects.RedPortal) + .filter(el => [ + sdk.areas.MatronsDen, sdk.areas.ForgottenSands, + sdk.areas.FurnaceofPain, sdk.areas.UberTristram + ].includes(el.objtype)); + let miniPortals = 0; + let keySetsReq = 3; + let tristOpen = false; + + if (redPortals.length > 0) { + redPortals.forEach(function (portal) { + if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype)) { + miniPortals++; + keySetsReq--; + } else if (portal.objtype === sdk.areas.UberTristram) { + tristOpen = true; + } + }); + } else { + // possible same game name but different day and data file never got deleted + currentGameInfo.doneAreas.length > 0 && (currentGameInfo = OrgTorchData.create()); + } + + // End the script if we don't have enough keys nor organs + if ((tkeys < keySetsReq || hkeys < keySetsReq || dkeys < keySetsReq) + && (brains < 1 || eyes < 1 || horns < 1) && !tristOpen) { + console.log("Not enough keys or organs."); + OrgTorchData.remove(); + + return true; + } + + Config.UseMerc = false; + + // We have enough keys, do mini ubers + if (tkeys >= keySetsReq && hkeys >= keySetsReq && dkeys >= keySetsReq) { + getFade(); + Town.goToTown(5); + console.log("Making organs."); + D2Bot.printToConsole("OrgTorch: Making organs.", sdk.colors.D2Bot.DarkGold); + Town.move("stash"); + + // there are already open portals lets check our info on them + if (miniPortals > 0) { + for (let i = 0; i < miniPortals; i++) { + // mini-portal is up but its not in our done areas, probably chickend during it, lets try again + if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(redPortals[i].objtype) + && !currentGameInfo.doneAreas.includes(redPortals[i].objtype)) { + portal = redPortals[i]; + runEvent(portal); + } + } + } + + for (let i = 0; i < keySetsReq; i += 1) { + // Abort if we have a complete set of organs + // If Config.OrgTorch.MakeTorch is false, check after at least one portal is made + if ((Config.OrgTorch.MakeTorch || i > 0) && completeSetCheck()) { + break; + } + + portal = openPortal(portalMode.MiniUbers); + runEvent(portal); + } + } + + // Don't make torches if not configured to OR if the char already has one + if (!Config.OrgTorch.MakeTorch || checkTorch()) { + OrgTorchData.remove(); + + return true; + } + + // Count organs + brains = me.findItems("mbr", sdk.items.mode.inStorage).length || 0; + eyes = me.findItems("bey", sdk.items.mode.inStorage).length || 0; + horns = me.findItems("dhn", sdk.items.mode.inStorage).length || 0; + + // We have enough organs, do Tristram - or trist is open we may have chickened and came back so check it + // if trist was already open when we joined should we run that first? + if ((brains && eyes && horns) || tristOpen) { + getFade(); + Town.goToTown(5); + Town.move("stash"); + + if (!tristOpen) { + console.log("Making torch"); + D2Bot.printToConsole("OrgTorch: Making torch.", sdk.colors.D2Bot.DarkGold); + portal = openPortal(portalMode.UberTristram); + } else { + portal = Pather.getPortal(sdk.areas.UberTristram); + } + + runEvent(portal); + OrgTorchData.remove(); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/OuterSteppes.js b/d2bs/kolbot/libs/scripts/OuterSteppes.js index 8e6f6712b..2b5b39463 100644 --- a/d2bs/kolbot/libs/scripts/OuterSteppes.js +++ b/d2bs/kolbot/libs/scripts/OuterSteppes.js @@ -6,13 +6,13 @@ */ function OuterSteppes() { - if (!Town.goToTown(4)) throw new Error("Failed to go to act 4"); - Town.doChores(); - // force random precast because currently bugs if we precast as soon as we go from inTown to out of town - Precast.doRandomPrecast(true); - if (!Pather.journeyTo(sdk.areas.OuterSteppes)) throw new Error("Failed to move to Outer Steppes"); + if (!Town.goToTown(4)) throw new Error("Failed to go to act 4"); + Town.doChores(); + // force random precast because currently bugs if we precast as soon as we go from inTown to out of town + Precast.doRandomPrecast(true); + if (!Pather.journeyTo(sdk.areas.OuterSteppes)) throw new Error("Failed to move to Outer Steppes"); - Attack.clearLevel(Config.ClearType); + Attack.clearLevel(Config.ClearType); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Pindleskin.js b/d2bs/kolbot/libs/scripts/Pindleskin.js index df6929e00..7e915902c 100644 --- a/d2bs/kolbot/libs/scripts/Pindleskin.js +++ b/d2bs/kolbot/libs/scripts/Pindleskin.js @@ -6,45 +6,47 @@ */ function Pindleskin() { - Town.goToTown((Config.Pindleskin.UseWaypoint ? undefined : 5)); - Town.doChores(); - - if (Config.Pindleskin.UseWaypoint) { - Pather.useWaypoint(sdk.areas.HallsofPain); - Precast.doPrecast(true); - - if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.NihlathaksTemple], true)) { - throw new Error("Failed to move to Nihlahak's Temple"); - } - } else { - if (!Pather.journeyTo(sdk.areas.NihlathaksTemple)) throw new Error("Failed to use portal."); - Precast.doPrecast(true); - } - - Pather.moveTo(10058, 13234); - - try { - Attack.kill(getLocaleString(sdk.locale.monsters.Pindleskin)); - } catch (e) { - console.error(e); - } - - if (Config.Pindleskin.KillNihlathak) { - if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.HallsofVaught], true)) throw new Error("Failed to move to Halls of Vaught"); - - // faster detection of TombVipers - Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { offX: 10, offY: 10, callback: () => { - if (Config.Pindleskin.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { - console.log("Tomb Vipers found."); - throw new ScriptError("Tomb Vipers found."); - } - }}); - - Config.Pindleskin.ClearVipers && Attack.clearList(Attack.getMob(sdk.monsters.TombViper2, 0, 20)); - - Attack.kill(sdk.monsters.Nihlathak); - Pickit.pickItems(); - } - - return true; + Town.goToTown((Config.Pindleskin.UseWaypoint ? undefined : 5)); + Town.doChores(); + + if (Config.Pindleskin.UseWaypoint) { + Pather.useWaypoint(sdk.areas.HallsofPain); + Precast.doPrecast(true); + + if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.NihlathaksTemple], true)) { + throw new Error("Failed to move to Nihlahak's Temple"); + } + } else { + if (!Pather.journeyTo(sdk.areas.NihlathaksTemple)) throw new Error("Failed to use portal."); + Precast.doPrecast(true); + } + + Pather.moveTo(10058, 13234); + + try { + Attack.kill(getLocaleString(sdk.locale.monsters.Pindleskin)); + } catch (e) { + console.error(e); + } + + if (Config.Pindleskin.KillNihlathak) { + if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.HallsofVaught], true)) { + throw new Error("Failed to move to Halls of Vaught"); + } + + // faster detection of TombVipers + Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { offX: 10, offY: 10, callback: () => { + if (Config.Pindleskin.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); + throw new ScriptError("Tomb Vipers found."); + } + } }); + + Config.Pindleskin.ClearVipers && Attack.clearList(Attack.getMob(sdk.monsters.TombViper2, 0, 20)); + + Attack.kill(sdk.monsters.Nihlathak); + Pickit.pickItems(); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Pit.js b/d2bs/kolbot/libs/scripts/Pit.js index d5c81ed57..003a8d8dd 100644 --- a/d2bs/kolbot/libs/scripts/Pit.js +++ b/d2bs/kolbot/libs/scripts/Pit.js @@ -6,15 +6,19 @@ */ function Pit() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.BlackMarsh); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(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"); - Config.Pit.ClearPit1 && Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit([sdk.areas.TamoeHighland, sdk.areas.PitLvl1], true)) { + throw new Error("Failed to move to Pit level 1"); + } + Config.Pit.ClearPit1 && Attack.clearLevel(Config.ClearType); - if (!Pather.moveToExit(sdk.areas.PitLvl2, true, Config.Pit.ClearPath)) throw new Error("Failed to move to Pit level 2"); - Attack.clearLevel(); + if (!Pather.moveToExit(sdk.areas.PitLvl2, true, Config.Pit.ClearPath)) { + throw new Error("Failed to move to Pit level 2"); + } + Attack.clearLevel(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js index 552817087..510b6c2c2 100644 --- a/d2bs/kolbot/libs/scripts/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -8,216 +8,216 @@ // @notes: can't do duriel or meph because all the extra tasks. this is not meant to be autoplay or self rush function Questing () { - const log = (msg = "", errorMsg = false) => { - me.overhead(msg); - console.log("ÿc9(Questing) :: " + (errorMsg ? "ÿc1" : "ÿc0") + msg); - }; + const log = (msg = "", errorMsg = false) => { + me.overhead(msg); + console.log("ÿc9(Questing) :: " + (errorMsg ? "ÿc1" : "ÿc0") + msg); + }; - /** + /** * @param {ItemUnit} item * @returns {boolean} */ - const getQuestItem = (item) => { - if (item) { - let id = item.classid; - let canFit = Storage.Inventory.CanFit(item); - if (!canFit && Pickit.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); - Town.visitTown(); - !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); - } - } - return Pickit.pickItem(item); - }; - - const den = function () { - log("starting den"); - - Town.doChores(); - if (!Pather.journeyTo(sdk.areas.DenofEvil)) throw new Error("den failed"); - Precast.doPrecast(true); - Attack.clearLevel(); - Town.goToTown() && Town.npcInteract("Akara"); - }; - - const smith = function () { - log("starting smith"); - include("core/Common/Smith.js"); - Common.Smith(); - }; - - const cain = function () { - include("core/Common/Cain.js"); - log("starting cain"); - - Town.doChores(); - Common.Cain.run(); - }; - - const andy = function () { - log("starting andy"); - - Town.doChores(); - if (!Pather.journeyTo(sdk.areas.CatacombsLvl4)) throw new Error("andy failed"); - Pather.moveTo(22582, 9612); - - 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(); - } - } - - Attack.kill(sdk.monsters.Andariel); - Town.goToTown(); - Town.npcInteract("Warriv", false); - Misc.useMenu(sdk.menu.GoEast); - }; - - const radament = function () { - log("starting radament"); - - if (!Pather.journeyTo(sdk.areas.A2SewersLvl3)) { - throw new Error("radament failed"); - } - - Precast.doPrecast(true); - - if (!Pather.moveToPreset(sdk.areas.A2SewersLvl3, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest)) { - throw new Error("radament failed"); - } - - Attack.kill(sdk.monsters.Radament); - - let book = Game.getItem(sdk.quest.item.BookofSkill); - getQuestItem(book); - - Town.goToTown(); - Town.npcInteract("Atma"); - }; - - const lamEssen = function () { - log("starting lam essen"); - - if (!Pather.journeyTo(sdk.areas.RuinedTemple)) { - throw new Error("Lam Essen quest failed"); - } - - Precast.doPrecast(true); - - if (!Pather.moveToPreset(sdk.areas.RuinedTemple, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { - throw new Error("Lam Essen quest failed"); - } - - Misc.openChest(sdk.quest.chest.LamEsensTomeHolder); - let book = Misc.poll(() => Game.getItem(sdk.quest.item.LamEsensTome), 1000, 100); - getQuestItem(book); - Town.goToTown(); - Town.npcInteract("Alkor"); - }; - - const izual = function () { - log("starting izual"); - if (!Loader.runScript("Izual")) throw new Error("izual failed"); - Town.goToTown(); - Town.npcInteract("Tyrael"); - }; - - const diablo = function () { - log("starting diablo"); - if (!Loader.runScript("Diablo")) throw new Error(); - Town.goToTown(4); - - Game.getObject(sdk.objects.RedPortalToAct5) - ? Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath) - : Town.npcInteract("Tyrael", false) && Misc.useMenu(sdk.menu.TravelToHarrogath); - }; + const getQuestItem = (item) => { + if (item) { + let id = item.classid; + let canFit = Storage.Inventory.CanFit(item); + if (!canFit && Pickit.canMakeRoom()) { + console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); + Town.visitTown(); + !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); + } + } + return Pickit.pickItem(item); + }; + + const den = function () { + log("starting den"); + + Town.doChores(); + if (!Pather.journeyTo(sdk.areas.DenofEvil)) throw new Error("den failed"); + Precast.doPrecast(true); + Attack.clearLevel(); + Town.goToTown() && Town.npcInteract("Akara"); + }; + + const smith = function () { + log("starting smith"); + include("core/Common/Smith.js"); + Common.Smith(); + }; + + const cain = function () { + include("core/Common/Cain.js"); + log("starting cain"); + + Town.doChores(); + Common.Cain.run(); + }; + + const andy = function () { + log("starting andy"); + + Town.doChores(); + if (!Pather.journeyTo(sdk.areas.CatacombsLvl4)) throw new Error("andy failed"); + Pather.moveTo(22582, 9612); + + 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(); + } + } + + Attack.kill(sdk.monsters.Andariel); + Town.goToTown(); + Town.npcInteract("Warriv", false); + Misc.useMenu(sdk.menu.GoEast); + }; + + const radament = function () { + log("starting radament"); + + if (!Pather.journeyTo(sdk.areas.A2SewersLvl3)) { + throw new Error("radament failed"); + } + + Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.A2SewersLvl3, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest)) { + throw new Error("radament failed"); + } + + Attack.kill(sdk.monsters.Radament); + + let book = Game.getItem(sdk.quest.item.BookofSkill); + getQuestItem(book); + + Town.goToTown(); + Town.npcInteract("Atma"); + }; + + const lamEssen = function () { + log("starting lam essen"); + + if (!Pather.journeyTo(sdk.areas.RuinedTemple)) { + throw new Error("Lam Essen quest failed"); + } + + Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.RuinedTemple, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { + throw new Error("Lam Essen quest failed"); + } + + Misc.openChest(sdk.quest.chest.LamEsensTomeHolder); + let book = Misc.poll(() => Game.getItem(sdk.quest.item.LamEsensTome), 1000, 100); + getQuestItem(book); + Town.goToTown(); + Town.npcInteract("Alkor"); + }; + + const izual = function () { + log("starting izual"); + if (!Loader.runScript("Izual")) throw new Error("izual failed"); + Town.goToTown(); + Town.npcInteract("Tyrael"); + }; + + const diablo = function () { + log("starting diablo"); + if (!Loader.runScript("Diablo")) throw new Error(); + Town.goToTown(4); + + Game.getObject(sdk.objects.RedPortalToAct5) + ? Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath) + : Town.npcInteract("Tyrael", false) && Misc.useMenu(sdk.menu.TravelToHarrogath); + }; - const shenk = function () { - log("starting shenk"); - - if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { - throw new Error("shenk failed"); - } - - Precast.doPrecast(true); - Pather.moveTo(3883, 5113); - Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); - Town.goToTown(); - Town.npcInteract("Larzuk"); - }; - - const barbs = function () { - log("starting barb rescue"); + const shenk = function () { + log("starting shenk"); + + if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { + throw new Error("shenk failed"); + } + + Precast.doPrecast(true); + Pather.moveTo(3883, 5113); + Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + Town.goToTown(); + Town.npcInteract("Larzuk"); + }; + + const barbs = function () { + log("starting barb rescue"); - if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { - throw new Error("barbs failed"); - } - Precast.doPrecast(true); + if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { + throw new Error("barbs failed"); + } + Precast.doPrecast(true); - let barbs = (Game.getPresetObjects(me.area, sdk.quest.chest.BarbCage) || []); - if (!barbs.length) throw new Error("Couldn't find the barbs"); + let barbs = (Game.getPresetObjects(me.area, sdk.quest.chest.BarbCage) || []); + if (!barbs.length) throw new Error("Couldn't find the barbs"); - let coords = []; + let coords = []; - // Dark-f: x-3 - for (let cage = 0; cage < barbs.length; cage += 1) { - coords.push({ - x: barbs[cage].roomx * 5 + barbs[cage].x - 3, - y: barbs[cage].roomy * 5 + barbs[cage].y - }); - } + // Dark-f: x-3 + for (let cage = 0; cage < barbs.length; cage += 1) { + coords.push({ + x: barbs[cage].roomx * 5 + barbs[cage].x - 3, + y: barbs[cage].roomy * 5 + barbs[cage].y + }); + } - for (let i = 0; i < coords.length; i += 1) { - log((i + 1) + "/" + coords.length); - Pather.moveToUnit(coords[i], 2, 0); - let door = Game.getMonster(sdk.monsters.PrisonDoor); + for (let i = 0; i < coords.length; i += 1) { + log((i + 1) + "/" + coords.length); + Pather.moveToUnit(coords[i], 2, 0); + let door = Game.getMonster(sdk.monsters.PrisonDoor); - if (door) { - Pather.moveToUnit(door, 1, 0); - Attack.kill(door); - } + if (door) { + Pather.moveToUnit(door, 1, 0); + Attack.kill(door); + } - delay(1500 + me.ping); - } + delay(1500 + me.ping); + } - Town.npcInteract("qual_kehk"); - }; + Town.npcInteract("qual_kehk"); + }; - const anya = function () { - log("starting anya"); + const anya = function () { + log("starting anya"); - if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { - if (!Pather.journeyTo(sdk.areas.FrozenRiver)) { - throw new Error("anya failed"); - } + if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { + if (!Pather.journeyTo(sdk.areas.FrozenRiver)) { + throw new Error("anya failed"); + } - Precast.doPrecast(true); + Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.FrozenRiver, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { - throw new Error("Anya quest failed"); - } + if (!Pather.moveToPreset(sdk.areas.FrozenRiver, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { + throw new Error("Anya quest failed"); + } - delay(1000); - - let frozenanya = Game.getObject(sdk.objects.FrozenAnya); + delay(1000); + + let 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 @@ -225,195 +225,199 @@ function Questing () { * 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"); - - /** + 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 (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"); + 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 + 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(); - } - } - } - } - } - - /** + 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 lets handle completing the quest as we have freed anya */ - if (Misc.checkQuest(sdk.quest.id.PrisonofIce, sdk.quest.states.ReqComplete)) { - /** + 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"); - } + 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 */ - let scroll = me.scrollofresistance; - !!scroll && scroll.use(); - } - }; - - // @theBGuy - const ancients = function () { - include("core/Common/Ancients.js"); - log("starting ancients"); - Town.doChores(); - - if (!Pather.journeyTo(sdk.areas.ArreatSummit)) throw new Error("ancients failed"); - - // ancients prep - Town.doChores(); - [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion] - .forEach(p => Town.buyPots(10, p, true)); - - let tempConfig = copyObj(Config); // save and update config settings - let townChicken = getScript("threads/townchicken.js"); - townChicken && townChicken.running && townChicken.stop(); - - Config.TownCheck = false; - Config.MercWatch = false; - Config.TownHP = 0; - Config.TownMP = 0; - Config.HPBuffer = 15; - Config.MPBuffer = 15; - Config.LifeChicken = 10; - - log("updated settings"); - - Town.buyPotions(); - // re-enter Arreat Summit - if (!Pather.usePortal(sdk.areas.ArreatSummit, me.name)) { - log("Failed to take portal back to Arreat Summit", true); - Pather.journeyTo(sdk.areas.ArreatSummit); - } + 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 */ + let scroll = me.scrollofresistance; + !!scroll && scroll.use(); + } + }; + + // @theBGuy + const ancients = function () { + include("core/Common/Ancients.js"); + log("starting ancients"); + Town.doChores(); + + if (!Pather.journeyTo(sdk.areas.ArreatSummit)) throw new Error("ancients failed"); + + // ancients prep + Town.doChores(); + [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion] + .forEach(p => Town.buyPots(10, p, true)); + + let tempConfig = copyObj(Config); // save and update config settings + let townChicken = getScript("threads/townchicken.js"); + townChicken && townChicken.running && townChicken.stop(); + + Config.TownCheck = false; + Config.MercWatch = false; + Config.TownHP = 0; + Config.TownMP = 0; + Config.HPBuffer = 15; + Config.MPBuffer = 15; + Config.LifeChicken = 10; + + log("updated settings"); + + Town.buyPotions(); + // re-enter Arreat Summit + if (!Pather.usePortal(sdk.areas.ArreatSummit, me.name)) { + log("Failed to take portal back to Arreat Summit", true); + Pather.journeyTo(sdk.areas.ArreatSummit); + } - Precast.doPrecast(true); + Precast.doPrecast(true); - // move to altar - if (!Pather.moveToPreset(sdk.areas.ArreatSummit, sdk.unittype.Object, sdk.quest.chest.AncientsAltar)) { - log("Failed to move to ancients' altar", true); - } + // move to altar + if (!Pather.moveToPreset(sdk.areas.ArreatSummit, sdk.unittype.Object, sdk.quest.chest.AncientsAltar)) { + log("Failed to move to ancients' altar", true); + } - Common.Ancients.touchAltar(); - Common.Ancients.startAncients(true); + Common.Ancients.touchAltar(); + Common.Ancients.startAncients(true); - me.cancel(); - Config = tempConfig; - log("restored settings"); - Precast.doPrecast(true); - - // reload town chicken in case we are doing others scripts after this one finishes - let townChick = getScript("threads/TownChicken.js"); - (Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick) && load("threads/TownChicken.js"); - - try { - if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { - Pather.moveToExit([sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2], true); - Pather.getWP(sdk.areas.WorldstoneLvl2); - } - } catch (err) { - log("Cleared Ancients. Failed to get WSK Waypoint", true); - } - }; - - const baal = function () { - log("starting baal"); - // just run baal script? I mean why re-invent the wheel here - Loader.runScript("Baal"); - Town.goToTown(5); - }; - - const tasks = (function () { - /** + me.cancel(); + Config = tempConfig; + log("restored settings"); + Precast.doPrecast(true); + + // reload town chicken in case we are doing others scripts after this one finishes + let townChick = getScript("threads/TownChicken.js"); + if ((Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick)) { + load("threads/TownChicken.js"); + } + + try { + if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { + Pather.moveToExit([sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2], true); + Pather.getWP(sdk.areas.WorldstoneLvl2); + } + } catch (err) { + log("Cleared Ancients. Failed to get WSK Waypoint", true); + } + }; + + const baal = function () { + log("starting baal"); + // just run baal script? I mean why re-invent the wheel here + Loader.runScript("Baal"); + Town.goToTown(5); + }; + + const tasks = (function () { + /** * @constructor * @param {function(): void} task * @param {() => boolean} preReq * @param {() => boolean} complete */ - function Task (task, preReq, complete) { - this.run = task; - this.preReq = (preReq || (() => true)); - this.complete = (complete || (() => false)); - } - return [ - new Task(den, () => true, () => me.den), - new Task(smith, () => me.charlvl > 9, () => me.smith || me.imbue), - new Task(cain, () => true, () => me.cain), - new Task(andy, () => true, () => me.andariel), - new Task(radament, () => me.accessToAct(2), () => me.radament), - new Task(lamEssen, () => me.accessToAct(3), () => me.lamessen), - new Task(izual, () => me.accessToAct(4), () => me.izual), - new Task(diablo, () => me.accessToAct(4), () => me.diablo), - new Task(shenk, () => me.accessToAct(5), () => me.shenk || me.larzuk), - new Task(barbs, () => me.accessToAct(5), () => me.barbrescue), - new Task(anya, () => me.accessToAct(5), () => me.anya), - new Task(ancients, () => me.accessToAct(5) && me.charlvl > [20, 40, 60][me.diff], () => me.ancients), - new Task(baal, () => me.accessToAct(5) && me.ancients, () => me.baal), - ]; - })(); - - !me.inTown && Town.doChores(); - - for (let task of tasks) { - if (task.preReq() && !task.complete()) { - try { - task.run(); - } catch (e) { - console.error(e); - } - } - } - - if (Config.Questing.StopProfile || Loader.scriptList.length === 1) { - D2Bot.printToConsole("All quests done. Stopping profile.", sdk.colors.D2Bot.Green); - D2Bot.stop(); - } else { - log("ÿc9(Questing) :: ÿc2Complete"); - // reload town chicken in case we are doing others scripts after this one finishes - let townChick = getScript("threads/TownChicken.js"); - (Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick) && load("threads/TownChicken.js"); - } - - return true; + function Task (task, preReq, complete) { + this.run = task; + this.preReq = (preReq || (() => true)); + this.complete = (complete || (() => false)); + } + return [ + new Task(den, () => true, () => me.den), + new Task(smith, () => me.charlvl > 9, () => me.smith || me.imbue), + new Task(cain, () => true, () => me.cain), + new Task(andy, () => true, () => me.andariel), + new Task(radament, () => me.accessToAct(2), () => me.radament), + new Task(lamEssen, () => me.accessToAct(3), () => me.lamessen), + new Task(izual, () => me.accessToAct(4), () => me.izual), + new Task(diablo, () => me.accessToAct(4), () => me.diablo), + new Task(shenk, () => me.accessToAct(5), () => me.shenk || me.larzuk), + new Task(barbs, () => me.accessToAct(5), () => me.barbrescue), + new Task(anya, () => me.accessToAct(5), () => me.anya), + new Task(ancients, () => me.accessToAct(5) && me.charlvl > [20, 40, 60][me.diff], () => me.ancients), + new Task(baal, () => me.accessToAct(5) && me.ancients, () => me.baal), + ]; + })(); + + !me.inTown && Town.doChores(); + + for (let task of tasks) { + if (task.preReq() && !task.complete()) { + try { + task.run(); + } catch (e) { + console.error(e); + } + } + } + + if (Config.Questing.StopProfile || Loader.scriptList.length === 1) { + D2Bot.printToConsole("All quests done. Stopping profile.", sdk.colors.D2Bot.Green); + D2Bot.stop(); + } else { + log("ÿc9(Questing) :: ÿc2Complete"); + // reload town chicken in case we are doing others scripts after this one finishes + let townChick = getScript("threads/TownChicken.js"); + if ((Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick)) { + load("threads/TownChicken.js"); + } + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Radament.js b/d2bs/kolbot/libs/scripts/Radament.js index 842c0fd9d..c5558087a 100644 --- a/d2bs/kolbot/libs/scripts/Radament.js +++ b/d2bs/kolbot/libs/scripts/Radament.js @@ -6,17 +6,18 @@ */ function Radament() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.A2SewersLvl2); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.A2SewersLvl2); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.A2SewersLvl3, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest)) { - throw new Error("Failed to move to Radament"); - } + if (!Pather.moveToExit(sdk.areas.A2SewersLvl3, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest)) { + throw new Error("Failed to move to Radament"); + } - Attack.kill(sdk.monsters.Radament); - Pickit.pickItems(); - Attack.openChests(20); + Attack.kill(sdk.monsters.Radament); + Pickit.pickItems(); + Attack.openChests(20); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Rakanishu.js b/d2bs/kolbot/libs/scripts/Rakanishu.js index 0f619d524..143083bdf 100644 --- a/d2bs/kolbot/libs/scripts/Rakanishu.js +++ b/d2bs/kolbot/libs/scripts/Rakanishu.js @@ -6,19 +6,21 @@ */ function Rakanishu() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) throw new Error("Failed to move to Rakanishu"); - Attack.kill(getLocaleString(sdk.locale.monsters.Rakanishu)); + if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) { + throw new Error("Failed to move to Rakanishu"); + } + Attack.kill(getLocaleString(sdk.locale.monsters.Rakanishu)); - if (Config.Rakanishu.KillGriswold && Pather.getPortal(sdk.areas.Tristram)) { - if (!Pather.usePortal(sdk.areas.Tristram)) throw new Error("Failed to move to Tristram"); + if (Config.Rakanishu.KillGriswold && Pather.getPortal(sdk.areas.Tristram)) { + if (!Pather.usePortal(sdk.areas.Tristram)) throw new Error("Failed to move to Tristram"); - Pather.moveTo(25149, 5180); - Attack.clear(20, 0xF, sdk.monsters.Griswold); - } + Pather.moveTo(25149, 5180); + Attack.clear(20, 0xF, sdk.monsters.Griswold); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js index b6da89e0a..64c07fc99 100644 --- a/d2bs/kolbot/libs/scripts/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename Rushee.js * @author kolton, theBGuy @@ -8,1055 +9,1073 @@ let Overrides = require("../modules/Override"); new Overrides.Override(Town, Town.goToTown, function(orignal, act, wpmenu) { - try { - orignal(act, wpmenu); + try { + orignal(act, wpmenu); - return true; - } catch (e) { - print(e); + return true; + } catch (e) { + print(e); - return Pather.useWaypoint(sdk.areas.townOf(me.area)); - } + return Pather.useWaypoint(sdk.areas.townOf(me.area)); + } }).apply(); new Overrides.Override(Pather, Pather.getWP, function(orignal, area, clearPath) { - if (area !== me.area) return false; - - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - - if (preset) { - let x = (preset.roomx * 5 + preset.x); - let y = (preset.roomy * 5 + preset.y); - if (!me.inTown && [x, y].distance > 15) return false; - - Skill.haveTK ? this.moveNearUnit(preset, 20, {clearSettings: {clearPath: clearPath}}) : this.moveToUnit(preset, 0, 0, clearPath); - - let wp = Game.getObject("waypoint"); - - if (wp) { - for (let j = 0; j < 10; j++) { - if (!getUIFlag(sdk.uiflags.Waypoint)) { - if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { - wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); - } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { - this.moveToUnit(wp) && Misc.click(0, 0, wp); - } - } - - if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { - delay(500); - me.cancelUIFlags(); - - return true; - } - - // handle getUnit bug - if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { - Town.getDistance("waypoint") > 5 && Town.move("waypoint"); - Misc.click(0, 0, wp); - } - - delay(500); - } - } - } - } - - return false; + if (area !== me.area) return false; + + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + + if (preset) { + let x = (preset.roomx * 5 + preset.x); + let y = (preset.roomy * 5 + preset.y); + if (!me.inTown && [x, y].distance > 15) return false; + + Skill.haveTK + ? this.moveNearUnit(preset, 20, { clearSettings: { clearPath: clearPath } }) + : this.moveToUnit(preset, 0, 0, clearPath); + + let wp = Game.getObject("waypoint"); + + if (wp) { + for (let j = 0; j < 10; j++) { + if (!getUIFlag(sdk.uiflags.Waypoint)) { + if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { + wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { + this.moveToUnit(wp) && Misc.click(0, 0, wp); + } + } + + if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { + delay(500); + me.cancelUIFlags(); + + return true; + } + + // handle getUnit bug + if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { + Town.getDistance("waypoint") > 5 && Town.move("waypoint"); + Misc.click(0, 0, wp); + } + + delay(500); + } + } + } + } + + return false; }).apply(); function Rushee() { - let act, leader, target, done = false; - let actions = []; + let act, leader, target, done = false; + let actions = []; - this.log = function (msg = "", sayMsg = false) { - print(msg); - sayMsg && say(msg); - }; - - this.useScrollOfRes = function () { - let scroll = me.scrollofresistance; - if (scroll) { - clickItem(sdk.clicktypes.click.item.Right, scroll); - print("Using scroll of resistance"); - } - }; - - this.revive = function () { - while (me.mode === sdk.player.mode.Death) { - delay(40); - } + this.log = function (msg = "", sayMsg = false) { + print(msg); + sayMsg && say(msg); + }; + + this.useScrollOfRes = function () { + let scroll = me.scrollofresistance; + if (scroll) { + clickItem(sdk.clicktypes.click.item.Right, scroll); + print("Using scroll of resistance"); + } + }; + + this.revive = function () { + while (me.mode === sdk.player.mode.Death) { + delay(40); + } - if (me.mode === sdk.player.mode.Dead) { - me.revive(); + if (me.mode === sdk.player.mode.Dead) { + me.revive(); - while (!me.inTown) { - delay(40); - } - } - }; + while (!me.inTown) { + delay(40); + } + } + }; - // todo - map the chest to classid so we only need to pass in one value - this.getQuestItem = function (classid, chestid) { - let tick = getTickCount(); + // todo - map the chest to classid so we only need to pass in one value + this.getQuestItem = function (classid, chestid) { + let tick = getTickCount(); - if (me.getItem(classid)) { - this.log("Already have: " + classid); - return true; - } + if (me.getItem(classid)) { + this.log("Already have: " + classid); + return true; + } - if (me.inTown) return false; + if (me.inTown) return false; - let chest = Game.getObject(chestid); + let chest = Game.getObject(chestid); - if (!chest) { - this.log("Couldn't find: " + chestid); - return false; - } + if (!chest) { + this.log("Couldn't find: " + chestid); + return false; + } - for (let i = 0; i < 5; i++) { - if (Misc.openChest(chest)) { - break; - } - this.log("Failed to open chest: Attempt[" + (i + 1) + "]"); - let coord = CollMap.getRandCoordinate(chest.x, -4, 4, chest.y, -4, 4); - coord && Pather.moveTo(coord.x, coord.y); - } + for (let i = 0; i < 5; i++) { + if (Misc.openChest(chest)) { + break; + } + this.log("Failed to open chest: Attempt[" + (i + 1) + "]"); + let coord = CollMap.getRandCoordinate(chest.x, -4, 4, chest.y, -4, 4); + coord && Pather.moveTo(coord.x, coord.y); + } - let item = Game.getItem(classid); + let item = Game.getItem(classid); - if (!item) { - if (getTickCount() - tick < 500) { - delay(500); - } + if (!item) { + if (getTickCount() - tick < 500) { + delay(500); + } - return false; - } + return false; + } - return Pickit.pickItem(item) && delay(1000); - }; + return Pickit.pickItem(item) && delay(1000); + }; - this.checkQuestMonster = function (classid) { - let monster = Game.getMonster(classid); + this.checkQuestMonster = function (classid) { + let monster = Game.getMonster(classid); - if (monster) { - while (!monster.dead) { - delay(500); - } + if (monster) { + while (!monster.dead) { + delay(500); + } - return true; - } + return true; + } - return false; - }; + return false; + }; - this.tyraelTalk = function () { - let npc = Game.getNPC(NPC.Tyrael); + this.tyraelTalk = function () { + let npc = Game.getNPC(NPC.Tyrael); - if (!npc) return false; + if (!npc) return false; - for (let i = 0; i < 3; i += 1) { - npc.distance > 3 && Pather.moveToUnit(npc); - npc.interact(); - delay(1000 + me.ping); - me.cancel(); + for (let i = 0; i < 3; i += 1) { + npc.distance > 3 && Pather.moveToUnit(npc); + npc.interact(); + delay(1000 + me.ping); + me.cancel(); - if (Pather.getPortal(null)) { - me.cancel(); + if (Pather.getPortal(null)) { + me.cancel(); - break; - } - } + break; + } + } - return Pather.usePortal(null) || Pather.usePortal(null, Config.Leader); - }; + return Pather.usePortal(null) || Pather.usePortal(null, Config.Leader); + }; - this.cubeStaff = function () { - let shaft = me.shaft; - let amulet = me.amulet; + this.cubeStaff = function () { + let shaft = me.shaft; + let amulet = me.amulet; - if (!shaft || !amulet) return false; + if (!shaft || !amulet) return false; - Storage.Cube.MoveTo(amulet); - Storage.Cube.MoveTo(shaft); - Cubing.openCube(); - print("making staff"); - transmute(); - delay(750 + me.ping); + Storage.Cube.MoveTo(amulet); + Storage.Cube.MoveTo(shaft); + Cubing.openCube(); + print("making staff"); + transmute(); + delay(750 + me.ping); - let staff = me.completestaff; + let staff = me.completestaff; - if (!staff) return false; + if (!staff) return false; - Storage.Inventory.MoveTo(staff); - me.cancel(); + Storage.Inventory.MoveTo(staff); + me.cancel(); - return true; - }; + return true; + }; - this.placeStaff = function () { - let tick = getTickCount(); - let orifice = Game.getObject(sdk.quest.chest.HoradricStaffHolder); - if (!orifice) return false; + this.placeStaff = function () { + let tick = getTickCount(); + let orifice = Game.getObject(sdk.quest.chest.HoradricStaffHolder); + if (!orifice) return false; - Misc.openChest(orifice); + Misc.openChest(orifice); - let staff = me.completestaff; + let staff = me.completestaff; - if (!staff) { - if (getTickCount() - tick < 500) { - delay(500); - } + if (!staff) { + if (getTickCount() - tick < 500) { + delay(500); + } - return false; - } + return false; + } - staff.toCursor(); - submitItem(); - delay(750 + me.ping); + staff.toCursor(); + submitItem(); + delay(750 + me.ping); - // unbug cursor - let item = me.findItem(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); + // unbug cursor + let item = me.findItem(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - if (item && item.toCursor()) { - Storage.Inventory.MoveTo(item); - } + if (item && item.toCursor()) { + Storage.Inventory.MoveTo(item); + } - return true; - }; + return true; + }; - this.changeAct = function (act) { - let preArea = me.area; + this.changeAct = function (act) { + let preArea = me.area; - if (me.mode === sdk.player.mode.Dead) { - me.revive(); + if (me.mode === sdk.player.mode.Dead) { + me.revive(); - while (!me.inTown) { - delay(500); - } - } + while (!me.inTown) { + delay(500); + } + } - if (me.act === act || me.act > act) return true; + if (me.act === act || me.act > act) return true; - try { - switch (act) { - case 2: - if (!Town.npcInteract("Warriv", false)) return false; - Misc.useMenu(sdk.menu.GoEast); + try { + switch (act) { + case 2: + if (!Town.npcInteract("Warriv", false)) return false; + Misc.useMenu(sdk.menu.GoEast); - break; - case 3: - // Non Quester needs to talk to Townsfolk to enable Harem TP - if (!Config.Rushee.Quester) { - // Talk to Atma - if (!Town.npcInteract("Atma")) { - break; - } - } + break; + case 3: + // Non Quester needs to talk to Townsfolk to enable Harem TP + if (!Config.Rushee.Quester) { + // Talk to Atma + if (!Town.npcInteract("Atma")) { + break; + } + } - Pather.usePortal(sdk.areas.HaremLvl1, Config.Leader); - Pather.moveToExit(sdk.areas.LutGholein, true); + Pather.usePortal(sdk.areas.HaremLvl1, Config.Leader); + Pather.moveToExit(sdk.areas.LutGholein, true); - if (!Town.npcInteract("Jerhyn")) { - Pather.moveTo(5166, 5206); - - return false; - } - - me.cancel(); - Pather.moveToExit(sdk.areas.HaremLvl1, true); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - - if (!Town.npcInteract("Meshif", false)) return false; - Misc.useMenu(sdk.menu.SailEast); - - break; - case 4: - if (me.inTown) { - Town.npcInteract("Cain"); - Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader); - } else { - delay(1500); - } - - Pather.moveTo(17591, 8070); - Pather.usePortal(null); - - break; - case 5: - Town.npcInteract("Tyrael", false); - delay(me.ping + 1); - - if (Game.getObject(sdk.objects.RedPortalToAct5)) { - me.cancel(); - Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath); - } else { - Misc.useMenu(sdk.menu.TravelToHarrogath); - } - - break; - } - - delay(1000 + me.ping * 2); - - while (!me.area) { - delay(500); - } - - if (me.area === preArea) { - me.cancel(); - Town.move("portalspot"); - this.log("Act change failed.", Config.LocalChat.Enabled); - - return false; - } - - this.log("Act change done.", Config.LocalChat.Enabled); - } catch (e) { - return false; - } - - return true; - }; - - this.getQuestInfo = function (id) { - // note: end bosses double printed to match able to go to act flag - let quests = [ - ["cain", sdk.quest.id.TheSearchForCain], ["andariel", sdk.quest.id.SistersToTheSlaughter], ["andariel", sdk.quest.id.AbleToGotoActII], - ["radament", sdk.quest.id.RadamentsLair], ["cube", sdk.quest.id.TheHoradricStaff], ["amulet", sdk.quest.id.TheTaintedSun], - ["summoner", sdk.quest.id.TheArcaneSanctuary], ["duriel", sdk.quest.id.TheSevenTombs], ["duriel", sdk.quest.id.AbleToGotoActIII], - ["lamesen", sdk.quest.id.LamEsensTome], ["travincal", sdk.quest.id.TheBlackenedTemple], ["mephisto", sdk.quest.id.TheGuardian], ["mephisto", sdk.quest.id.AbleToGotoActIV], - ["izual", sdk.quest.id.TheFallenAngel], ["diablo", sdk.quest.id.TerrorsEnd], ["diablo", sdk.quest.id.AbleToGotoActV], - ["shenk", sdk.quest.id.SiegeOnHarrogath], ["anya", sdk.quest.id.PrisonofIce], ["ancients", sdk.quest.id.RiteofPassage], ["baal", sdk.quest.id.EyeofDestruction] - ]; - - let quest = quests.find(element => element[1] === id); - - return (!!quest ? quest[0] : ""); - }; - - this.nonQuesterNPCTalk = false; - - addEventListener("chatmsg", - function (who, msg) { - if (who === Config.Leader) { - actions.push(msg); - } - }); - - // START - Town.goToTown(me.highestAct); - me.inTown && Town.move("portalspot"); - - // if we can't find our leader after 5 minutes, I'm thinking they aren't showing up. Lets not wait around forever - leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.minutes(5), 1000); - if (!leader) throw new Error("Failed to find my rusher"); - - Config.Rushee.Quester ? this.log("Leader found", Config.LocalChat.Enabled) : console.log("Leader Found: " + Config.Leader); - - // lets figure out if we either are the bumper or have a bumper so we know if we need to stop at the end of the rush - let bumperLevelReq = [20, 40, 60][me.diff]; - // ensure we are the right level to go to next difficulty if not on classic - let nextGame = (Config.Rushee.Bumper && (me.classic || me.charlvl >= bumperLevelReq)); - if (!nextGame) { - // we aren't the bumper, lets figure out if anyone else is a bumper - // hell is the end of a rush so always end profile after - if (Misc.getPlayerCount() > 2 && !me.hell) { - // there is more than just us and the rusher in game - so check party level - nextGame = Misc.checkPartyLevel(bumperLevelReq, leader.name); - } - } - console.debug("Is this our last run? " + (nextGame ? "No" : "Yes")); - - while (true) { - // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces - try { - if (actions.length > 0) { - switch (actions[0]) { - case "all in": - switch (leader.area) { - case sdk.areas.A2SewersLvl3: - // Pick Book of Skill, use Book of Skill - Town.move("portalspot"); - Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); - delay(500); - - while (true) { - target = Game.getItem(sdk.quest.item.BookofSkill); - - if (!target) { - break; - } - - Pickit.pickItem(target); - delay(250); - target = me.getItem(sdk.quest.item.BookofSkill); - - if (target) { - print("Using book of skill"); - clickItem(sdk.clicktypes.click.item.Right, target); - - break; - } - } - - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - actions.shift(); - - break; - } - - actions.shift(); - - break; - case "questinfo": - if (!Config.Rushee.Quester) { - actions.shift(); - - break; - } - - say("highestquest " + this.getQuestInfo(me.highestQuestDone)); - actions.shift(); - - break; - case "wpinfo": - if (!Config.Rushee.Quester) { - actions.shift(); - - break; - } - - // go activate wp if we don't know our wps yet - !getWaypoint(1) && Pather.getWP(me.area); - - let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { - if (area === sdk.areas.HallsofPain) return false; - if (me.classic && area >= sdk.areas.Harrogath) return false; - if (getWaypoint(Pather.wpAreas.indexOf(area))) return false; - return true; - }); - - say("mywps " + JSON.stringify(myWps)); - actions.shift(); - - break; - case "wp": - if (!me.inTown && !Town.goToTown()) { - this.log("I can't get to town :(", Config.LocalChat.Enabled); - - break; - } - - act = Misc.getPlayerAct(leader); - - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } - - // make sure we talk to cain to access durance - leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); + if (!Town.npcInteract("Jerhyn")) { + Pather.moveTo(5166, 5206); + + return false; + } + + me.cancel(); + Pather.moveToExit(sdk.areas.HaremLvl1, true); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + + if (!Town.npcInteract("Meshif", false)) return false; + Misc.useMenu(sdk.menu.SailEast); + + break; + case 4: + if (me.inTown) { + Town.npcInteract("Cain"); + Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader); + } else { + delay(1500); + } + + Pather.moveTo(17591, 8070); + Pather.usePortal(null); + + break; + case 5: + Town.npcInteract("Tyrael", false); + delay(me.ping + 1); + + if (Game.getObject(sdk.objects.RedPortalToAct5)) { + me.cancel(); + Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath); + } else { + Misc.useMenu(sdk.menu.TravelToHarrogath); + } + + break; + } + + delay(1000 + me.ping * 2); + + while (!me.area) { + delay(500); + } + + if (me.area === preArea) { + me.cancel(); + Town.move("portalspot"); + this.log("Act change failed.", Config.LocalChat.Enabled); + + return false; + } + + this.log("Act change done.", Config.LocalChat.Enabled); + } catch (e) { + return false; + } + + return true; + }; + + this.getQuestInfo = function (id) { + // note: end bosses double printed to match able to go to act flag + let quests = [ + ["cain", sdk.quest.id.TheSearchForCain], + ["andariel", sdk.quest.id.SistersToTheSlaughter], + ["andariel", sdk.quest.id.AbleToGotoActII], + ["radament", sdk.quest.id.RadamentsLair], + ["cube", sdk.quest.id.TheHoradricStaff], + ["amulet", sdk.quest.id.TheTaintedSun], + ["summoner", sdk.quest.id.TheArcaneSanctuary], + ["duriel", sdk.quest.id.TheSevenTombs], + ["duriel", sdk.quest.id.AbleToGotoActIII], + ["lamesen", sdk.quest.id.LamEsensTome], + ["travincal", sdk.quest.id.TheBlackenedTemple], + ["mephisto", sdk.quest.id.TheGuardian], + ["mephisto", sdk.quest.id.AbleToGotoActIV], + ["izual", sdk.quest.id.TheFallenAngel], + ["diablo", sdk.quest.id.TerrorsEnd], + ["diablo", sdk.quest.id.AbleToGotoActV], + ["shenk", sdk.quest.id.SiegeOnHarrogath], + ["anya", sdk.quest.id.PrisonofIce], + ["ancients", sdk.quest.id.RiteofPassage], + ["baal", sdk.quest.id.EyeofDestruction] + ]; + + let quest = quests.find(element => element[1] === id); + + return (!!quest ? quest[0] : ""); + }; + + this.nonQuesterNPCTalk = false; + + addEventListener("chatmsg", + function (who, msg) { + if (who === Config.Leader) { + actions.push(msg); + } + }); + + // START + Town.goToTown(me.highestAct); + me.inTown && Town.move("portalspot"); + + // if we can't find our leader after 5 minutes, I'm thinking they aren't showing up. Lets not wait around forever + leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.minutes(5), 1000); + if (!leader) throw new Error("Failed to find my rusher"); + + Config.Rushee.Quester + ? this.log("Leader found", Config.LocalChat.Enabled) + : console.log("Leader Found: " + Config.Leader); + + // lets figure out if we either are the bumper or have a bumper so we know if we need to stop at the end of the rush + let bumperLevelReq = [20, 40, 60][me.diff]; + // ensure we are the right level to go to next difficulty if not on classic + let nextGame = (Config.Rushee.Bumper && (me.classic || me.charlvl >= bumperLevelReq)); + if (!nextGame) { + // we aren't the bumper, lets figure out if anyone else is a bumper + // hell is the end of a rush so always end profile after + if (Misc.getPlayerCount() > 2 && !me.hell) { + // there is more than just us and the rusher in game - so check party level + nextGame = Misc.checkPartyLevel(bumperLevelReq, leader.name); + } + } + console.debug("Is this our last run? " + (nextGame ? "No" : "Yes")); + + while (true) { + // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces + try { + if (actions.length > 0) { + switch (actions[0]) { + case "all in": + switch (leader.area) { + case sdk.areas.A2SewersLvl3: + // Pick Book of Skill, use Book of Skill + Town.move("portalspot"); + Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); + delay(500); + + while (true) { + target = Game.getItem(sdk.quest.item.BookofSkill); + + if (!target) { + break; + } + + Pickit.pickItem(target); + delay(250); + target = me.getItem(sdk.quest.item.BookofSkill); + + if (target) { + print("Using book of skill"); + clickItem(sdk.clicktypes.click.item.Right, target); + + break; + } + } + + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + actions.shift(); + + break; + } + + actions.shift(); + + break; + case "questinfo": + if (!Config.Rushee.Quester) { + actions.shift(); + + break; + } + + say("highestquest " + this.getQuestInfo(me.highestQuestDone)); + actions.shift(); + + break; + case "wpinfo": + if (!Config.Rushee.Quester) { + actions.shift(); + + break; + } + + // go activate wp if we don't know our wps yet + !getWaypoint(1) && Pather.getWP(me.area); + + let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { + if (area === sdk.areas.HallsofPain) return false; + if (me.classic && area >= sdk.areas.Harrogath) return false; + if (getWaypoint(Pather.wpAreas.indexOf(area))) return false; + return true; + }); + + say("mywps " + JSON.stringify(myWps)); + actions.shift(); + + break; + case "wp": + if (!me.inTown && !Town.goToTown()) { + this.log("I can't get to town :(", Config.LocalChat.Enabled); + + break; + } + + act = Misc.getPlayerAct(leader); + + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } + + // make sure we talk to cain to access durance + leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); - // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas - (!Config.Rushee.Quester && !this.nonQuesterNPCTalk) && (this.nonQuesterNPCTalk = true); - - Town.getDistance("portalspot") > 10 && Town.move("portalspot"); - if (Pather.usePortal(null, Config.Leader) && Pather.getWP(me.area) && Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) && Town.move("portalspot")) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); - } else { - // check for bugged portal - let p = Game.getObject("portal"); - let preArea = me.area; - if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) + // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas + (!Config.Rushee.Quester && !this.nonQuesterNPCTalk) && (this.nonQuesterNPCTalk = true); + + Town.getDistance("portalspot") > 10 && Town.move("portalspot"); + if (Pather.usePortal(null, Config.Leader) && Pather.getWP(me.area) && Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) && Town.move("portalspot")) { + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + } else { + // check for bugged portal + let p = Game.getObject("portal"); + let preArea = me.area; + if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); - } else { - this.log("Failed to get wp", Config.LocalChat.Enabled); - !me.inTown && Town.goToTown(); - } - } - - actions.shift(); - - break; - case "1": - while (!leader.area) { - delay(500); - } - - act = Misc.getPlayerAct(leader); - - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } - - // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester - if (this.nonQuesterNPCTalk) { - console.debug("Leader Area: " + getAreaName(leader.area)); - - switch (leader.area) { - case sdk.areas.ClawViperTempleLvl2: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Drognan")) { - actions.shift(); - console.debug("drognan done"); - } - - break; - case sdk.areas.ArcaneSanctuary: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Atma")) { - actions.shift(); - console.debug("atma done"); - } - - break; - case sdk.areas.Travincal: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) || Misc.checkQuest(sdk.quest.id.TheGuardian, 8), Time.seconds(20), 1000)); - if (Town.npcInteract("Cain")) { - actions.shift(); - console.debug("cain done"); - } - - break; - case sdk.areas.ArreatSummit: - Misc.poll(() => (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Malah")) { - actions.shift(); - console.debug("malah done"); - } - - break; - } - - me.inTown && Town.move("portalspot"); - } - - if (!Config.Rushee.Quester) { - actions.shift(); - - break; - } - - switch (leader.area) { - case sdk.areas.StonyField: - if (!Pather.usePortal(sdk.areas.StonyField, Config.Leader)) { - this.log("Failed to us portal to stony field", Config.LocalChat.Enabled); - break; - } - - let stones = [ - Game.getObject(sdk.quest.chest.StoneAlpha), - Game.getObject(sdk.quest.chest.StoneBeta), - Game.getObject(sdk.quest.chest.StoneGamma), - Game.getObject(sdk.quest.chest.StoneDelta), - Game.getObject(sdk.quest.chest.StoneLambda) - ]; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (Misc.openChest(stone)) { - stones.splice(i, 1); - i--; - } - delay(10); - } - } - - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram)) { - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + } else { + this.log("Failed to get wp", Config.LocalChat.Enabled); + !me.inTown && Town.goToTown(); + } + } + + actions.shift(); + + break; + case "1": + while (!leader.area) { + delay(500); + } + + act = Misc.getPlayerAct(leader); + + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } + + // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester + if (this.nonQuesterNPCTalk) { + console.debug("Leader Area: " + getAreaName(leader.area)); + + switch (leader.area) { + case sdk.areas.ClawViperTempleLvl2: + Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); + if (Town.npcInteract("Drognan")) { + actions.shift(); + console.debug("drognan done"); + } + + break; + case sdk.areas.ArcaneSanctuary: + Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); + if (Town.npcInteract("Atma")) { + actions.shift(); + console.debug("atma done"); + } + + break; + case sdk.areas.Travincal: + Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) || Misc.checkQuest(sdk.quest.id.TheGuardian, 8), Time.seconds(20), 1000)); + if (Town.npcInteract("Cain")) { + actions.shift(); + console.debug("cain done"); + } + + break; + case sdk.areas.ArreatSummit: + Misc.poll(() => (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); + if (Town.npcInteract("Malah")) { + actions.shift(); + console.debug("malah done"); + } + + break; + } + + me.inTown && Town.move("portalspot"); + } + + if (!Config.Rushee.Quester) { + actions.shift(); + + break; + } + + switch (leader.area) { + case sdk.areas.StonyField: + if (!Pather.usePortal(sdk.areas.StonyField, Config.Leader)) { + this.log("Failed to us portal to stony field", Config.LocalChat.Enabled); + break; + } + + let stones = [ + Game.getObject(sdk.quest.chest.StoneAlpha), + Game.getObject(sdk.quest.chest.StoneBeta), + Game.getObject(sdk.quest.chest.StoneGamma), + Game.getObject(sdk.quest.chest.StoneDelta), + Game.getObject(sdk.quest.chest.StoneLambda) + ]; + + while (stones.some((stone) => !stone.mode)) { + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; + + if (Misc.openChest(stone)) { + stones.splice(i, 1); + i--; + } + delay(10); + } + } + + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.getPortal(sdk.areas.Tristram)) { + Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - break; - } - } - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.DarkWood: - if (!Pather.usePortal(sdk.areas.DarkWood, Config.Leader)) { - this.log("Failed to use portal to dark wood", Config.LocalChat.Enabled); - break; - } - - this.getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); - delay(500); - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + break; + } + } + Town.move("portalspot"); + actions.shift(); + + break; + case sdk.areas.DarkWood: + if (!Pather.usePortal(sdk.areas.DarkWood, Config.Leader)) { + this.log("Failed to use portal to dark wood", Config.LocalChat.Enabled); + break; + } + + this.getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); + delay(500); + Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - if (Town.npcInteract("Akara")) { - this.log("Akara done", Config.LocalChat.Enabled); - } - - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.Tristram: - if (!Pather.usePortal(sdk.areas.Tristram, Config.Leader)) { - this.log("Failed to use portal to Tristram", Config.LocalChat.Enabled); - break; - } - - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - Town.npcInteract("Akara") && this.log("Akara done", Config.LocalChat.Enabled); - } - } - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.CatacombsLvl4: - if (!Pather.usePortal(sdk.areas.CatacombsLvl4, Config.Leader)) { - this.log("Failed to use portal to catacombs", Config.LocalChat.Enabled); - break; - } - - target = Pather.getPortal(null, Config.Leader); - target && Pather.walkTo(target.x, target.y); - - actions.shift(); - - break; - case sdk.areas.A2SewersLvl3: - Town.move("portalspot"); - - if (Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader)) { - actions.shift(); - } - - break; - case sdk.areas.HallsoftheDeadLvl3: - Pather.usePortal(sdk.areas.HallsoftheDeadLvl3, Config.Leader); - this.getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - - actions.shift(); - - break; - case sdk.areas.ClawViperTempleLvl2: - Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); - this.getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + if (Town.npcInteract("Akara")) { + this.log("Akara done", Config.LocalChat.Enabled); + } + + Town.move("portalspot"); + actions.shift(); + + break; + case sdk.areas.Tristram: + if (!Pather.usePortal(sdk.areas.Tristram, Config.Leader)) { + this.log("Failed to use portal to Tristram", Config.LocalChat.Enabled); + break; + } + + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { + Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + Town.npcInteract("Akara") && this.log("Akara done", Config.LocalChat.Enabled); + } + } + Town.move("portalspot"); + actions.shift(); + + break; + case sdk.areas.CatacombsLvl4: + if (!Pather.usePortal(sdk.areas.CatacombsLvl4, Config.Leader)) { + this.log("Failed to use portal to catacombs", Config.LocalChat.Enabled); + break; + } + + target = Pather.getPortal(null, Config.Leader); + target && Pather.walkTo(target.x, target.y); + + actions.shift(); + + break; + case sdk.areas.A2SewersLvl3: + Town.move("portalspot"); + + if (Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader)) { + actions.shift(); + } + + break; + case sdk.areas.HallsoftheDeadLvl3: + Pather.usePortal(sdk.areas.HallsoftheDeadLvl3, Config.Leader); + this.getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + + actions.shift(); + + break; + case sdk.areas.ClawViperTempleLvl2: + Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); + this.getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - if (Town.npcInteract("Drognan")) { - actions.shift(); - say("drognan done", Config.LocalChat.Enabled); - } - - Town.move("portalspot"); - - break; - case sdk.areas.MaggotLairLvl3: - Pather.usePortal(sdk.areas.MaggotLairLvl3, Config.Leader); - this.getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); - delay(500); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - this.cubeStaff(); - - actions.shift(); - - break; - case sdk.areas.ArcaneSanctuary: - if (!Pather.usePortal(sdk.areas.ArcaneSanctuary, Config.Leader)) { - break; - } - - actions.shift(); - - break; - case sdk.areas.TalRashasTomb1: - case sdk.areas.TalRashasTomb2: - case sdk.areas.TalRashasTomb3: - case sdk.areas.TalRashasTomb4: - case sdk.areas.TalRashasTomb5: - case sdk.areas.TalRashasTomb6: - case sdk.areas.TalRashasTomb7: - Pather.usePortal(null, Config.Leader); - this.placeStaff(); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - actions.shift(); - - break; - case sdk.areas.DurielsLair: - Pather.usePortal(sdk.areas.DurielsLair, Config.Leader); - this.tyraelTalk(); - - actions.shift(); - - break; - case sdk.areas.Travincal: - if (!Pather.usePortal(sdk.areas.Travincal, Config.Leader)) { - me.cancel(); - - break; - } - - actions.shift(); - - break; - case sdk.areas.RuinedTemple: - if (!Pather.usePortal(sdk.areas.RuinedTemple, Config.Leader)) { - me.cancel(); - - break; - } - - this.getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); - Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); - Town.npcInteract("Alkor"); - Town.move("portalspot"); - actions.shift(); - - - break; - case sdk.areas.DuranceofHateLvl3: - if (!Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader)) { - me.cancel(); - - break; - } - - actions.shift(); - - break; - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - if (Pather.usePortal(null, Config.Leader)) { - actions.shift(); - } - - break; - case sdk.areas.ChaosSanctuary: - Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); - Pather.moveTo(7762, 5268); - Packet.flash(me.gid); - delay(500); - Pather.walkTo(7763, 5267, 2); - - while (!Game.getMonster(sdk.monsters.Diablo)) { - delay(500); - } - - Pather.moveTo(7763, 5267); - actions.shift(); - - break; - case sdk.areas.BloodyFoothills: - Pather.usePortal(sdk.areas.BloodyFoothills, Config.Leader); - actions.shift(); - - break; - case sdk.areas.FrozenRiver: - Town.npcInteract("Malah"); - - Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader); - delay(500); - - target = Game.getObject(sdk.objects.FrozenAnya); - - if (target) { - Pather.moveToUnit(target); - Misc.poll(() => { - Packet.entityInteract(target); - delay(100); - return !Game.getObject(sdk.objects.FrozenAnya); - }, 1000, 200); - delay(1000); - me.cancel(); - } - - actions.shift(); - - break; - default: // unsupported area - actions.shift(); - - break; - } - - break; - case "2": // Go back to town and check quest - if (!Config.Rushee.Quester) { - // Non-questers can piggyback off quester out messages - switch (leader.area) { - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete) && Town.npcInteract("Tyrael"); - - break; - case sdk.areas.BloodyFoothills: - me.act === 5 && Town.npcInteract("Larzuk"); - - break; - case sdk.areas.FrozenRiver: - if (me.act === 5) { - Town.npcInteract("Malah"); - this.useScrollOfRes(); - } - - break; - } - - actions.shift(); - - break; - } - - this.revive(); - - switch (me.area) { - case sdk.areas.CatacombsLvl4: - // Go to town if not there, break if procedure fails - if (!me.inTown && !Pather.usePortal(sdk.areas.RogueEncampment)) { - break; - } - - if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { - D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); - quit(); - } - - actions.shift(); - - break; - case sdk.areas.A2SewersLvl3: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - break; - } - - actions.shift(); - - break; - case sdk.areas.ArcaneSanctuary: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - break; - } - - Town.npcInteract("Atma"); - - if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); - quit(); - } - - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.Travincal: - if (!me.inTown && !Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { - break; - } - - Town.npcInteract("Cain"); - - if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); - quit(); - } - - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.DuranceofHateLvl3: - if (!Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { - break; - } - - actions.shift(); - - break; - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - break; - } - - if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { - Town.npcInteract("Tyrael"); - Town.move("portalspot"); - } - - actions.shift(); - - break; - case sdk.areas.ChaosSanctuary: - me.classic && D2Bot.restart(); - - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - break; - } - - actions.shift(); - - break; - case sdk.areas.BloodyFoothills: - if (!me.inTown && !Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - break; - } - - Town.npcInteract("Larzuk"); - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.FrozenRiver: - if (!me.inTown && !Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader)) { - break; - } - - Town.npcInteract("Malah"); - this.useScrollOfRes(); - Town.move("portalspot"); - - actions.shift(); - - break; - default: - Town.move("portalspot"); - actions.shift(); - - break; - } - - break; - case "3": // Bumper - if (!Config.Rushee.Bumper) { - actions.shift(); - - break; - } - - while (!leader.area) { - delay(500); - } - - act = Misc.getPlayerAct(leader); - - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } - - switch (leader.area) { - case sdk.areas.ArreatSummit: - if (!Pather.usePortal(sdk.areas.ArreatSummit, Config.Leader)) { - break; - } - - // Wait until portal is gone - while (Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { - delay(500); - } - - // Wait until portal is up again - while (!Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { - delay(500); - } - - if (!Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - break; - } - - actions.shift(); - - break; - case sdk.areas.WorldstoneChamber: - if (!Pather.usePortal(sdk.areas.WorldstoneChamber, Config.Leader)) { - break; - } - - actions.shift(); - - break; - } - - break; - case "quit": - done = true; - - break; - case "exit": - case "bye ~": - if (!nextGame) { - D2Bot.printToConsole("Rush Complete"); - D2Bot.stop(); - } else { - D2Bot.restart(); - } - - break; - case "a2": - case "a3": - case "a4": - case "a5": - act = actions[0].toString()[1]; - !!act && (act = (parseInt(act, 10) || me.act + 1)); - - if (!this.changeAct(act)) { - break; - } - - Town.move("portalspot"); - actions.shift(); - - break; - case me.name + " quest": - say("I am quester."); - Config.Rushee.Quester = true; - - actions.shift(); - - break; - case "leader": - console.log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - Config.LocalChat.Enabled && say(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - actions.shift(); - - break; - default: // Invalid command - actions.shift(); - - break; - } - } - } catch (e) { - if (me.mode === sdk.player.mode.Dead) { - me.revive(); - - while (!me.inTown) { - delay(500); - } - } - } - - if (getUIFlag(sdk.uiflags.TradePrompt)) { - me.cancel(); - } - - if (done) { - break; - } - - delay(500); - } - - done && quit(); - - return true; + if (Town.npcInteract("Drognan")) { + actions.shift(); + say("drognan done", Config.LocalChat.Enabled); + } + + Town.move("portalspot"); + + break; + case sdk.areas.MaggotLairLvl3: + Pather.usePortal(sdk.areas.MaggotLairLvl3, Config.Leader); + this.getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); + delay(500); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + this.cubeStaff(); + + actions.shift(); + + break; + case sdk.areas.ArcaneSanctuary: + if (!Pather.usePortal(sdk.areas.ArcaneSanctuary, Config.Leader)) { + break; + } + + actions.shift(); + + break; + case sdk.areas.TalRashasTomb1: + case sdk.areas.TalRashasTomb2: + case sdk.areas.TalRashasTomb3: + case sdk.areas.TalRashasTomb4: + case sdk.areas.TalRashasTomb5: + case sdk.areas.TalRashasTomb6: + case sdk.areas.TalRashasTomb7: + Pather.usePortal(null, Config.Leader); + this.placeStaff(); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + actions.shift(); + + break; + case sdk.areas.DurielsLair: + Pather.usePortal(sdk.areas.DurielsLair, Config.Leader); + this.tyraelTalk(); + + actions.shift(); + + break; + case sdk.areas.Travincal: + if (!Pather.usePortal(sdk.areas.Travincal, Config.Leader)) { + me.cancel(); + + break; + } + + actions.shift(); + + break; + case sdk.areas.RuinedTemple: + if (!Pather.usePortal(sdk.areas.RuinedTemple, Config.Leader)) { + me.cancel(); + + break; + } + + this.getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); + Town.npcInteract("Alkor"); + Town.move("portalspot"); + actions.shift(); + + + break; + case sdk.areas.DuranceofHateLvl3: + if (!Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader)) { + me.cancel(); + + break; + } + + actions.shift(); + + break; + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + if (Pather.usePortal(null, Config.Leader)) { + actions.shift(); + } + + break; + case sdk.areas.ChaosSanctuary: + Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); + Pather.moveTo(7762, 5268); + Packet.flash(me.gid); + delay(500); + Pather.walkTo(7763, 5267, 2); + + while (!Game.getMonster(sdk.monsters.Diablo)) { + delay(500); + } + + Pather.moveTo(7763, 5267); + actions.shift(); + + break; + case sdk.areas.BloodyFoothills: + Pather.usePortal(sdk.areas.BloodyFoothills, Config.Leader); + actions.shift(); + + break; + case sdk.areas.FrozenRiver: + Town.npcInteract("Malah"); + + Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader); + delay(500); + + target = Game.getObject(sdk.objects.FrozenAnya); + + if (target) { + Pather.moveToUnit(target); + Misc.poll(() => { + Packet.entityInteract(target); + delay(100); + return !Game.getObject(sdk.objects.FrozenAnya); + }, 1000, 200); + delay(1000); + me.cancel(); + } + + actions.shift(); + + break; + default: // unsupported area + actions.shift(); + + break; + } + + break; + case "2": // Go back to town and check quest + if (!Config.Rushee.Quester) { + // Non-questers can piggyback off quester out messages + switch (leader.area) { + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete) && Town.npcInteract("Tyrael"); + + break; + case sdk.areas.BloodyFoothills: + me.act === 5 && Town.npcInteract("Larzuk"); + + break; + case sdk.areas.FrozenRiver: + if (me.act === 5) { + Town.npcInteract("Malah"); + this.useScrollOfRes(); + } + + break; + } + + actions.shift(); + + break; + } + + this.revive(); + + switch (me.area) { + case sdk.areas.CatacombsLvl4: + // Go to town if not there, break if procedure fails + if (!me.inTown && !Pather.usePortal(sdk.areas.RogueEncampment)) { + break; + } + + if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { + D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); + quit(); + } + + actions.shift(); + + break; + case sdk.areas.A2SewersLvl3: + if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { + break; + } + + actions.shift(); + + break; + case sdk.areas.ArcaneSanctuary: + if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { + break; + } + + Town.npcInteract("Atma"); + + if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); + quit(); + } + + Town.move("portalspot"); + actions.shift(); + + break; + case sdk.areas.Travincal: + if (!me.inTown && !Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { + break; + } + + Town.npcInteract("Cain"); + + if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); + quit(); + } + + Town.move("portalspot"); + actions.shift(); + + break; + case sdk.areas.DuranceofHateLvl3: + if (!Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { + break; + } + + actions.shift(); + + break; + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { + break; + } + + if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { + Town.npcInteract("Tyrael"); + Town.move("portalspot"); + } + + actions.shift(); + + break; + case sdk.areas.ChaosSanctuary: + me.classic && D2Bot.restart(); + + if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { + break; + } + + actions.shift(); + + break; + case sdk.areas.BloodyFoothills: + if (!me.inTown && !Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { + break; + } + + Town.npcInteract("Larzuk"); + Town.move("portalspot"); + actions.shift(); + + break; + case sdk.areas.FrozenRiver: + if (!me.inTown && !Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader)) { + break; + } + + Town.npcInteract("Malah"); + this.useScrollOfRes(); + Town.move("portalspot"); + + actions.shift(); + + break; + default: + Town.move("portalspot"); + actions.shift(); + + break; + } + + break; + case "3": // Bumper + if (!Config.Rushee.Bumper) { + actions.shift(); + + break; + } + + while (!leader.area) { + delay(500); + } + + act = Misc.getPlayerAct(leader); + + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } + + switch (leader.area) { + case sdk.areas.ArreatSummit: + if (!Pather.usePortal(sdk.areas.ArreatSummit, Config.Leader)) { + break; + } + + // Wait until portal is gone + while (Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { + delay(500); + } + + // Wait until portal is up again + while (!Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { + delay(500); + } + + if (!Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { + break; + } + + actions.shift(); + + break; + case sdk.areas.WorldstoneChamber: + if (!Pather.usePortal(sdk.areas.WorldstoneChamber, Config.Leader)) { + break; + } + + actions.shift(); + + break; + } + + break; + case "quit": + done = true; + + break; + case "exit": + case "bye ~": + if (!nextGame) { + D2Bot.printToConsole("Rush Complete"); + D2Bot.stop(); + } else { + D2Bot.restart(); + } + + break; + case "a2": + case "a3": + case "a4": + case "a5": + act = actions[0].toString()[1]; + !!act && (act = (parseInt(act, 10) || me.act + 1)); + + if (!this.changeAct(act)) { + break; + } + + Town.move("portalspot"); + actions.shift(); + + break; + case me.name + " quest": + say("I am quester."); + Config.Rushee.Quester = true; + + actions.shift(); + + break; + case "leader": + console.log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); + Config.LocalChat.Enabled && say(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); + actions.shift(); + + break; + default: // Invalid command + actions.shift(); + + break; + } + } + } catch (e) { + if (me.mode === sdk.player.mode.Dead) { + me.revive(); + + while (!me.inTown) { + delay(500); + } + } + } + + if (getUIFlag(sdk.uiflags.TradePrompt)) { + me.cancel(); + } + + if (done) { + break; + } + + delay(500); + } + + done && quit(); + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Rusher.js b/d2bs/kolbot/libs/scripts/Rusher.js index 175238450..b387350af 100644 --- a/d2bs/kolbot/libs/scripts/Rusher.js +++ b/d2bs/kolbot/libs/scripts/Rusher.js @@ -15,214 +15,218 @@ */ function Rusher() { - load("threads/rushthread.js"); - delay(500); - - let i, command, master, commandSplit0; - let commands = []; - let sequence = [ - "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", - "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" - ]; - let rushThread = getScript("threads/rushthread.js"); - - this.reloadThread = function () { - rushThread = getScript("threads/rushthread.js"); - rushThread && rushThread.stop(); - - delay(500); - load("threads/rushthread.js"); - - rushThread = getScript("threads/rushthread.js"); - - delay(500); - }; - - this.getPartyAct = function () { - let party = getParty(); - let minArea = 999; - - do { - if (party.name !== me.name) { - while (!party.area) { - me.overhead("Waiting for party area info"); - delay(500); - } - - if (party.area < minArea) { - minArea = party.area; - } - } - } while (party.getNext()); - - return sdk.areas.actOf(minArea); - }; - - this.chatEvent = function (nick, msg) { - if (nick !== me.name) { - if (typeof msg !== "string") return; - switch (msg) { - case "master": - if (!master) { - say(nick + " is my master."); - - master = nick; - } else { - say("I already have a master."); - } - - break; - case "release": - if (nick === master) { - say("I have no master now."); - - master = false; - } else { - say("I'm only accepting commands from my master."); - } - - break; - case "quit": - if (nick === master) { - say("bye ~"); - scriptBroadcast("quit"); - } else { - say("I'm only accepting commands from my master."); - } - - break; - default: - if (msg && msg.match(/^do \w|^clear \d|^pause$|^resume$/gi)) { - if (nick === master) { - commands.push(msg); - } else { - say("I'm only accepting commands from my master."); - } - } else if (msg && msg.includes("highestquest")) { - if (!!master && nick === master || !master) { - command = msg; - } else { - say("I'm only accepting commands from my master."); - } - } - - break; - } - } - }; - - addEventListener("chatmsg", this.chatEvent); - - while (Misc.getPartyCount() < Math.min(8, Config.Rusher.WaitPlayerCount)) { - me.overhead("Waiting for players to join"); - delay(500); - } - - // Skip to a higher act if all party members are there - switch (this.getPartyAct()) { - case 2: - say("Party is in act 2, starting from act 2"); - rushThread.send("skiptoact 2"); - - break; - case 3: - say("Party is in act 3, starting from act 3"); - rushThread.send("skiptoact 3"); - - break; - case 4: - say("Party is in act 4, starting from act 4"); - rushThread.send("skiptoact 4"); - - break; - case 5: - say("Party is in act 5, starting from act 5"); - rushThread.send("skiptoact 5"); - - break; - } - - // get info from master - let tick = getTickCount(); - let askAgain = 1; - say("questinfo"); - while (!command) { - // wait up to 3 minutes - if (getTickCount() - tick > Time.minutes(3)) { - break; - } - - if (getTickCount() - tick > Time.minutes(askAgain)) { - say("questinfo"); - askAgain++; - } - } - - if (command) { - commandSplit0 = command.split(" ")[1]; - !!commandSplit0 && sequence.some(el => el.toLowerCase() === commandSplit0) && rushThread.send(command.toLowerCase()); - } - - delay(200); - rushThread.send("go"); - - while (true) { - if (commands.length > 0) { - command = commands.shift(); - - switch (command) { - case "pause": - if (rushThread.running) { - say("Pausing"); - - rushThread.pause(); - } - - break; - case "resume": - if (!rushThread.running) { - say("Resuming"); - - rushThread.resume(); - } - - break; - default: - if (typeof command === "string") { - commandSplit0 = command.split(" ")[0]; - - if (commandSplit0 === undefined) { - break; - } - - if (commandSplit0.toLowerCase() === "do") { - for (i = 0; i < sequence.length; i += 1) { - if (command.split(" ")[1] && sequence[i].match(command.split(" ")[1], "gi")) { - this.reloadThread(); - rushThread.send(command.split(" ")[1]); - - break; - } - } - - i === sequence.length && say("Invalid sequence"); - } else if (commandSplit0.toLowerCase() === "clear") { - if (!isNaN(parseInt(command.split(" ")[1], 10)) && parseInt(command.split(" ")[1], 10) > 0 && parseInt(command.split(" ")[1], 10) <= 132) { - this.reloadThread(); - rushThread.send(command); - } else { - say("Invalid area"); - } - } - } - - break; - } - } - - delay(100); - } - - // eslint-disable-next-line no-unreachable - return true; + load("threads/rushthread.js"); + delay(500); + + let i, command, master, commandSplit0; + let commands = []; + let sequence = [ + "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", + "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" + ]; + let rushThread = getScript("threads/rushthread.js"); + + this.reloadThread = function () { + rushThread = getScript("threads/rushthread.js"); + rushThread && rushThread.stop(); + + delay(500); + load("threads/rushthread.js"); + + rushThread = getScript("threads/rushthread.js"); + + delay(500); + }; + + this.getPartyAct = function () { + let party = getParty(); + let minArea = 999; + + do { + if (party.name !== me.name) { + while (!party.area) { + me.overhead("Waiting for party area info"); + delay(500); + } + + if (party.area < minArea) { + minArea = party.area; + } + } + } while (party.getNext()); + + return sdk.areas.actOf(minArea); + }; + + this.chatEvent = function (nick, msg) { + if (nick !== me.name) { + if (typeof msg !== "string") return; + switch (msg) { + case "master": + if (!master) { + say(nick + " is my master."); + + master = nick; + } else { + say("I already have a master."); + } + + break; + case "release": + if (nick === master) { + say("I have no master now."); + + master = false; + } else { + say("I'm only accepting commands from my master."); + } + + break; + case "quit": + if (nick === master) { + say("bye ~"); + scriptBroadcast("quit"); + } else { + say("I'm only accepting commands from my master."); + } + + break; + default: + if (msg && msg.match(/^do \w|^clear \d|^pause$|^resume$/gi)) { + if (nick === master) { + commands.push(msg); + } else { + say("I'm only accepting commands from my master."); + } + } else if (msg && msg.includes("highestquest")) { + if (!!master && nick === master || !master) { + command = msg; + } else { + say("I'm only accepting commands from my master."); + } + } + + break; + } + } + }; + + addEventListener("chatmsg", this.chatEvent); + + while (Misc.getPartyCount() < Math.min(8, Config.Rusher.WaitPlayerCount)) { + me.overhead("Waiting for players to join"); + delay(500); + } + + // Skip to a higher act if all party members are there + switch (this.getPartyAct()) { + case 2: + say("Party is in act 2, starting from act 2"); + rushThread.send("skiptoact 2"); + + break; + case 3: + say("Party is in act 3, starting from act 3"); + rushThread.send("skiptoact 3"); + + break; + case 4: + say("Party is in act 4, starting from act 4"); + rushThread.send("skiptoact 4"); + + break; + case 5: + say("Party is in act 5, starting from act 5"); + rushThread.send("skiptoact 5"); + + break; + } + + // get info from master + let tick = getTickCount(); + let askAgain = 1; + say("questinfo"); + while (!command) { + // wait up to 3 minutes + if (getTickCount() - tick > Time.minutes(3)) { + break; + } + + if (getTickCount() - tick > Time.minutes(askAgain)) { + say("questinfo"); + askAgain++; + } + } + + if (command) { + commandSplit0 = command.split(" ")[1]; + if (!!commandSplit0 && sequence.some(el => el.toLowerCase() === commandSplit0)) { + rushThread.send(command.toLowerCase()); + } + } + + delay(200); + rushThread.send("go"); + + while (true) { + if (commands.length > 0) { + command = commands.shift(); + + switch (command) { + case "pause": + if (rushThread.running) { + say("Pausing"); + + rushThread.pause(); + } + + break; + case "resume": + if (!rushThread.running) { + say("Resuming"); + + rushThread.resume(); + } + + break; + default: + if (typeof command === "string") { + commandSplit0 = command.split(" ")[0]; + + if (commandSplit0 === undefined) { + break; + } + + if (commandSplit0.toLowerCase() === "do") { + for (i = 0; i < sequence.length; i += 1) { + if (command.split(" ")[1] && sequence[i].match(command.split(" ")[1], "gi")) { + this.reloadThread(); + rushThread.send(command.split(" ")[1]); + + break; + } + } + + i === sequence.length && say("Invalid sequence"); + } else if (commandSplit0.toLowerCase() === "clear") { + if (!isNaN(parseInt(command.split(" ")[1], 10)) + && parseInt(command.split(" ")[1], 10) > 0 + && parseInt(command.split(" ")[1], 10) <= 132) { + this.reloadThread(); + rushThread.send(command); + } else { + say("Invalid area"); + } + } + } + + break; + } + } + + delay(100); + } + + // eslint-disable-next-line no-unreachable + return true; } diff --git a/d2bs/kolbot/libs/scripts/SealLeecher.js b/d2bs/kolbot/libs/scripts/SealLeecher.js index 4b462c2ea..6529afbaf 100644 --- a/d2bs/kolbot/libs/scripts/SealLeecher.js +++ b/d2bs/kolbot/libs/scripts/SealLeecher.js @@ -6,94 +6,94 @@ */ function SealLeecher() { - let commands = []; - - Town.goToTown(4); - Town.doChores(); - Town.move("portalspot"); - - if (!Config.Leader) { - D2Bot.printToConsole("You have to set Config.Leader"); - D2Bot.stop(); - - return false; - } - - let chatEvent = function (nick, msg) { - if (nick === Config.Leader) { - commands.push(msg); - } - }; - - try { - addEventListener("chatmsg", chatEvent); - - // Wait until leader is partied - while (!Misc.inMyParty(Config.Leader)) { - delay(1000); - } - - while (Misc.inMyParty(Config.Leader)) { - if (commands.length > 0) { - let command = commands.shift(); - - switch (command) { - case "in": - if (me.inTown) { - Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); - delay(250); - } - - if (getDistance(me, 7761, 5267) < 10) { - Pather.walkTo(7761, 5267, 2); - } - - break; - case "out": - if (!me.inTown) { - Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); - } - - break; - case "done": - if (!me.inTown) { - Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); - } - - return true; // End script - } - } - - if (me.dead) { - while (me.mode === sdk.player.mode.Death) { - delay(40); - } - - me.revive(); - - while (!me.inTown) { - delay(40); - } - } - - if (!me.inTown) { - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable && monster.distance < 20) { - me.overhead("HOT"); - Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); - } - } while (monster.getNext()); - } - } - - delay(100); - } - } finally { - removeEventListener("chatmsg", chatEvent); - } - - return true; + let commands = []; + + Town.goToTown(4); + Town.doChores(); + Town.move("portalspot"); + + if (!Config.Leader) { + D2Bot.printToConsole("You have to set Config.Leader"); + D2Bot.stop(); + + return false; + } + + let chatEvent = function (nick, msg) { + if (nick === Config.Leader) { + commands.push(msg); + } + }; + + try { + addEventListener("chatmsg", chatEvent); + + // Wait until leader is partied + while (!Misc.inMyParty(Config.Leader)) { + delay(1000); + } + + while (Misc.inMyParty(Config.Leader)) { + if (commands.length > 0) { + let command = commands.shift(); + + switch (command) { + case "in": + if (me.inTown) { + Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); + delay(250); + } + + if (getDistance(me, 7761, 5267) < 10) { + Pather.walkTo(7761, 5267, 2); + } + + break; + case "out": + if (!me.inTown) { + Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); + } + + break; + case "done": + if (!me.inTown) { + Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); + } + + return true; // End script + } + } + + if (me.dead) { + while (me.mode === sdk.player.mode.Death) { + delay(40); + } + + me.revive(); + + while (!me.inTown) { + delay(40); + } + } + + if (!me.inTown) { + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable && monster.distance < 20) { + me.overhead("HOT"); + Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); + } + } while (monster.getNext()); + } + } + + delay(100); + } + } finally { + removeEventListener("chatmsg", chatEvent); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/SharpTooth.js b/d2bs/kolbot/libs/scripts/SharpTooth.js index 928ea1b1f..9659d8c68 100644 --- a/d2bs/kolbot/libs/scripts/SharpTooth.js +++ b/d2bs/kolbot/libs/scripts/SharpTooth.js @@ -6,16 +6,18 @@ */ function SharpTooth() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); - // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); - // Could this be causing crashes here? - if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Monster, sdk.monsters.preset.SharpToothSayer)) throw new Error("Failed to move to Sharptooth Slayer"); + // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); + // Could this be causing crashes here? + if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Monster, sdk.monsters.preset.SharpToothSayer)) { + throw new Error("Failed to move to Sharptooth Slayer"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.SharpToothSayer)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.SharpToothSayer)); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js index 1b1282cc4..3c4800f00 100644 --- a/d2bs/kolbot/libs/scripts/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -6,306 +6,312 @@ */ function ShopBot() { - const overlayText = { - title: new Text("kolbot shopbot", 50, 245, 2, 1), - cycles: new Text("Cycles in last minute:", 50, 260, 2, 1), - frequency: new Text("Valid item frequency:", 50, 275, 2, 1), - totalCycles: new Text("Total cycles:", 50, 290, 2, 1), - }; - - let tickCount; - let cycles = 0; - let validItems = 0; - let totalCycles = 0; - - Pather.teleport = false; - const pickEntries = []; - const npcs = {}; - - const buildPickList = function () { - let nipfile, filepath = "pickit/shopbot.nip"; - let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); - - if (!FileTools.exists(filepath)) { - Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); - return false; - } - - try { - nipfile = File.open(filepath, 0); - } catch (fileError) { - Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); - } - - if (!nipfile) return false; - - let lines = nipfile.readAllLines(); - nipfile.close(); - - for (let i = 0; i < lines.length; i += 1) { - let info = { - line: i + 1, - file: filename, - string: lines[i] - }; - - let line = NTIP.ParseLineInt(lines[i], info); - line && pickEntries.push(line); - } - - return true; - }; - - /** + const overlayText = { + title: new Text("kolbot shopbot", 50, 245, 2, 1), + cycles: new Text("Cycles in last minute:", 50, 260, 2, 1), + frequency: new Text("Valid item frequency:", 50, 275, 2, 1), + totalCycles: new Text("Total cycles:", 50, 290, 2, 1), + }; + + let tickCount; + let cycles = 0; + let validItems = 0; + let totalCycles = 0; + + Pather.teleport = false; + const pickEntries = []; + const npcs = {}; + + const buildPickList = function () { + let nipfile, filepath = "pickit/shopbot.nip"; + let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); + + if (!FileTools.exists(filepath)) { + Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); + return false; + } + + try { + nipfile = File.open(filepath, 0); + } catch (fileError) { + Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); + } + + if (!nipfile) return false; + + let lines = nipfile.readAllLines(); + nipfile.close(); + + for (let i = 0; i < lines.length; i += 1) { + let info = { + line: i + 1, + file: filename, + string: lines[i] + }; + + let line = NTIP.ParseLineInt(lines[i], info); + line && pickEntries.push(line); + } + + return true; + }; + + /** * Interact and open the menu of an NPC unit * @param {NPCUnit} npc * @returns {boolean} */ - const openMenu = function (npc) { - if (!npc || npc.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); + const openMenu = function (npc) { + if (!npc || npc.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); - let interactedNPC = getInteractedNPC(); + let interactedNPC = getInteractedNPC(); - if (interactedNPC && interactedNPC.name !== npc.name) { - Packet.cancelNPC(interactedNPC); - me.cancel(); - } + if (interactedNPC && interactedNPC.name !== npc.name) { + Packet.cancelNPC(interactedNPC); + me.cancel(); + } - if (getUIFlag(sdk.uiflags.NPCMenu)) return true; + if (getUIFlag(sdk.uiflags.NPCMenu)) return true; - for (let i = 0; i < 10; i += 1) { - npc.distance > 5 && Pather.walkTo(npc.x, npc.y); + for (let i = 0; i < 10; i += 1) { + npc.distance > 5 && Pather.walkTo(npc.x, npc.y); - if (!getUIFlag(sdk.uiflags.NPCMenu)) { - Packet.entityInteract(npc); - sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, npc.gid); - } + if (!getUIFlag(sdk.uiflags.NPCMenu)) { + Packet.entityInteract(npc); + sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, npc.gid); + } - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < Math.max(Math.round((i + 1) * 250 / (i / 3 + 1)), me.ping + 1)) { - if (getUIFlag(sdk.uiflags.NPCMenu)) { - return true; - } + while (getTickCount() - tick < Math.max(Math.round((i + 1) * 250 / (i / 3 + 1)), me.ping + 1)) { + if (getUIFlag(sdk.uiflags.NPCMenu)) { + return true; + } - delay(10); - } - } + delay(10); + } + } - me.cancel(); + me.cancel(); - return false; - }; + return false; + }; - /** + /** * @param {NPCUnit} npc * @param {number} menuId * @returns {boolean} */ - const shopItems = function (npc, menuId) { - let bought; + const shopItems = function (npc, menuId) { + let bought; - if (!Storage.Inventory.CanFit({ sizex: 2, sizey: 4 }) && AutoMule.getMuleItems().length > 0) { - D2Bot.printToConsole("Mule triggered"); - scriptBroadcast("mule"); - scriptBroadcast("quit"); - return true; - } + if (!Storage.Inventory.CanFit({ sizex: 2, sizey: 4 }) && AutoMule.getMuleItems().length > 0) { + D2Bot.printToConsole("Mule triggered"); + scriptBroadcast("mule"); + scriptBroadcast("quit"); + return true; + } - if (!npc) return false; + if (!npc) return false; - for (let i = 0; i < 10; i += 1) { - delay(150); + for (let i = 0; i < 10; i += 1) { + delay(150); - i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, 1, 4, npc.gid, 4, 0); + i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, 1, 4, npc.gid, 4, 0); - if (npc.itemcount > 0) { - break; - } - } + if (npc.itemcount > 0) { + break; + } + } - let items = npc.getItemsEx().filter(function (item) { - return (Config.ShopBot.ScanIDs.includes(item.classid) || Config.ShopBot.ScanIDs.length === 0); - }); + let items = npc.getItemsEx().filter(function (item) { + return (Config.ShopBot.ScanIDs.includes(item.classid) || Config.ShopBot.ScanIDs.length === 0); + }); - if (!items.length) return false; + if (!items.length) return false; - me.overhead(npc.itemcount + " items, " + items.length + " valid"); + me.overhead(npc.itemcount + " items, " + items.length + " valid"); - validItems += items.length; - overlayText.frequency.text = "Valid base items / cycle: " + ((validItems / totalCycles).toFixed(2).toString()); + validItems += items.length; + overlayText.frequency.text = "Valid base items / cycle: " + ((validItems / totalCycles).toFixed(2).toString()); - for (let i = 0; i < items.length; i += 1) { - if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && + for (let i = 0; i < items.length; i += 1) { + if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && NTIP.CheckItem(items[i], pickEntries) - ) { - beep(); - D2Bot.printToConsole("Match found!", sdk.colors.D2Bot.DarkGold); - delay(1000); - - if (npc.startTrade(menuId)) { - Item.logItem("Shopped", items[i]); - items[i].buy(); - bought = true; - } - - Config.ShopBot.QuitOnMatch && scriptBroadcast("quit"); - } - } - - if (bought) { - me.cancelUIFlags(); - Town.stash(); - } - - return true; - }; - - /** + ) { + beep(); + D2Bot.printToConsole("Match found!", sdk.colors.D2Bot.DarkGold); + delay(1000); + + if (npc.startTrade(menuId)) { + Item.logItem("Shopped", items[i]); + items[i].buy(); + bought = true; + } + + Config.ShopBot.QuitOnMatch && scriptBroadcast("quit"); + } + } + + if (bought) { + me.cancelUIFlags(); + Town.stash(); + } + + return true; + }; + + /** * @param {string} name * @returns {boolean} */ - const shopAtNPC = function (name) { - let wp, menuId = "Shop"; - - switch (name) { - case NPC.Charsi: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Akara: - case NPC.Gheed: - wp = sdk.areas.RogueEncampment; - - break; - case NPC.Fara: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Elzix: - case NPC.Drognan: - wp = sdk.areas.LutGholein; - - break; - case NPC.Hratli: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Asheara: - case NPC.Ormus: - wp = sdk.areas.KurastDocktown; - - break; - case NPC.Halbu: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Jamella: - wp = sdk.areas.PandemoniumFortress; - - break; - case NPC.Larzuk: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Malah: - case NPC.Anya: - wp = sdk.areas.Harrogath; - - break; - default: - throw new Error("Invalid NPC"); - } - - if (!me.inArea(wp) && !Pather.useWaypoint(wp)) return false; - - let npc = npcs[name] || Game.getNPC(name); - - if (!npc || npc.type !== sdk.unittype.NPC || npc.distance > 5) { - npc = Town.npcInteract(name); - } - - if (!npc) return false; - - !npcs[name] && (npcs[name] = copyUnit(npc)); - Config.ShopBot.CycleDelay && delay(Config.ShopBot.CycleDelay); - openMenu(npc) && shopItems(npc, menuId); - - return true; - }; - - // START - for (let i = 0; i < Config.ShopBot.ScanIDs.length; i += 1) { - if (isNaN(Config.ShopBot.ScanIDs[i])) { - if (NTIPAliasClassID.hasOwnProperty(Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase())) { - Config.ShopBot.ScanIDs[i] = NTIPAliasClassID[Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase()]; - } else { - Misc.errorReport("ÿc1Invalid ShopBot entry:ÿc0 " + Config.ShopBot.ScanIDs[i]); - Config.ShopBot.ScanIDs.splice(i, 1); - i -= 1; - } - } - } - - typeof Config.ShopBot.ShopNPC === "string" && (Config.ShopBot.ShopNPC = [Config.ShopBot.ShopNPC]); - - for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { - Config.ShopBot.ShopNPC[i] = Config.ShopBot.ShopNPC[i].toLowerCase(); - } - - if (Config.ShopBot.MinGold && me.gold < Config.ShopBot.MinGold) return true; - - buildPickList(); - print("Shopbot: Pickit entries: " + pickEntries.length); - Town.doChores(); - - tickCount = getTickCount(); - - while (!Config.ShopBot.Cycles || totalCycles < Config.ShopBot.Cycles) { - if (getTickCount() - tickCount >= 60 * 1000) { - overlayText.cycles.text = "Cycles in last minute: " + cycles.toString(); - overlayText.totalCycles.text = "Total cycles: " + totalCycles.toString(); - cycles = 0; - tickCount = getTickCount(); - } - - for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { - shopAtNPC(Config.ShopBot.ShopNPC[i]); - } - - if (me.inTown) { - let area = getArea(); - let wp = Game.getPresetObject(me.area, [ - sdk.objects.A1Waypoint, sdk.objects.A2Waypoint, sdk.objects.A3Waypoint, sdk.objects.A4Waypoint, sdk.objects.A5Waypoint - ][me.act - 1]); - let wpX = wp.roomx * 5 + wp.x; - let wpY = wp.roomy * 5 + wp.y; - let redPortal = (getUnits(sdk.unittype.Object, sdk.objects.RedPortal).sort((a, b) => a.distance - b.distance)).first(); - let exit = area.exits[0]; - - for (let i = 1; i < area.exits.length; i++) { - if (getDistance(me, exit) > getDistance(me, area.exits[i])) { - exit = area.exits[i]; - } - } - - if ([sdk.areas.RogueEncampment, sdk.areas.Harrogath].includes(me.area) && !!redPortal && redPortal.distance < 20 + const shopAtNPC = function (name) { + let wp, menuId = "Shop"; + + switch (name) { + case NPC.Charsi: + menuId = "Repair"; + // eslint-disable-next-line no-fallthrough + case NPC.Akara: + case NPC.Gheed: + wp = sdk.areas.RogueEncampment; + + break; + case NPC.Fara: + menuId = "Repair"; + // eslint-disable-next-line no-fallthrough + case NPC.Elzix: + case NPC.Drognan: + wp = sdk.areas.LutGholein; + + break; + case NPC.Hratli: + menuId = "Repair"; + // eslint-disable-next-line no-fallthrough + case NPC.Asheara: + case NPC.Ormus: + wp = sdk.areas.KurastDocktown; + + break; + case NPC.Halbu: + menuId = "Repair"; + // eslint-disable-next-line no-fallthrough + case NPC.Jamella: + wp = sdk.areas.PandemoniumFortress; + + break; + case NPC.Larzuk: + menuId = "Repair"; + // eslint-disable-next-line no-fallthrough + case NPC.Malah: + case NPC.Anya: + wp = sdk.areas.Harrogath; + + break; + default: + throw new Error("Invalid NPC"); + } + + if (!me.inArea(wp) && !Pather.useWaypoint(wp)) return false; + + let npc = npcs[name] || Game.getNPC(name); + + if (!npc || npc.type !== sdk.unittype.NPC || npc.distance > 5) { + npc = Town.npcInteract(name); + } + + if (!npc) return false; + + !npcs[name] && (npcs[name] = copyUnit(npc)); + Config.ShopBot.CycleDelay && delay(Config.ShopBot.CycleDelay); + openMenu(npc) && shopItems(npc, menuId); + + return true; + }; + + // START + for (let i = 0; i < Config.ShopBot.ScanIDs.length; i += 1) { + if (isNaN(Config.ShopBot.ScanIDs[i])) { + if (NTIPAliasClassID.hasOwnProperty(Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase())) { + Config.ShopBot.ScanIDs[i] = NTIPAliasClassID[Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase()]; + } else { + Misc.errorReport("ÿc1Invalid ShopBot entry:ÿc0 " + Config.ShopBot.ScanIDs[i]); + Config.ShopBot.ScanIDs.splice(i, 1); + i -= 1; + } + } + } + + typeof Config.ShopBot.ShopNPC === "string" && (Config.ShopBot.ShopNPC = [Config.ShopBot.ShopNPC]); + + for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { + Config.ShopBot.ShopNPC[i] = Config.ShopBot.ShopNPC[i].toLowerCase(); + } + + if (Config.ShopBot.MinGold && me.gold < Config.ShopBot.MinGold) return true; + + buildPickList(); + print("Shopbot: Pickit entries: " + pickEntries.length); + Town.doChores(); + + tickCount = getTickCount(); + + while (!Config.ShopBot.Cycles || totalCycles < Config.ShopBot.Cycles) { + if (getTickCount() - tickCount >= 60 * 1000) { + overlayText.cycles.text = "Cycles in last minute: " + cycles.toString(); + overlayText.totalCycles.text = "Total cycles: " + totalCycles.toString(); + cycles = 0; + tickCount = getTickCount(); + } + + for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { + shopAtNPC(Config.ShopBot.ShopNPC[i]); + } + + if (me.inTown) { + let area = getArea(); + let wp = Game.getPresetObject(me.area, [ + sdk.objects.A1Waypoint, sdk.objects.A2Waypoint, + sdk.objects.A3Waypoint, sdk.objects.A4Waypoint, sdk.objects.A5Waypoint + ][me.act - 1]); + let wpX = wp.roomx * 5 + wp.x; + let wpY = wp.roomy * 5 + wp.y; + let redPortal = (getUnits(sdk.unittype.Object, sdk.objects.RedPortal) + .sort((a, b) => a.distance - b.distance)) + .first(); + let exit = area.exits[0]; + + for (let i = 1; i < area.exits.length; i++) { + if (getDistance(me, exit) > getDistance(me, area.exits[i])) { + exit = area.exits[i]; + } + } + + if ([sdk.areas.RogueEncampment, sdk.areas.Harrogath].includes(me.area) && !!redPortal && redPortal.distance < 20 && Pather.usePortal(null, null, redPortal)) { - delay(3000); - Pather.usePortal(sdk.areas.townOf(me.area)); - - if (totalCycles === 0) { - delay(10000); - } - - delay(1500); - } else if (getDistance(me, exit) < (getDistance(me, wpX, wpY) + 6)) { - Pather.moveToExit(me.area + 1, true); - Pather.moveToExit(me.area - 1, true); - } else { - Pather.useWaypoint([sdk.areas.CatacombsLvl2, sdk.areas.A2SewersLvl2, sdk.areas.DuranceofHateLvl2, sdk.areas.RiverofFlame, sdk.areas.CrystalizedPassage][me.act - 1]); - } - } - - cycles += 1; - totalCycles += 1; - } - - return true; + delay(3000); + Pather.usePortal(sdk.areas.townOf(me.area)); + + if (totalCycles === 0) { + delay(10000); + } + + delay(1500); + } else if (getDistance(me, exit) < (getDistance(me, wpX, wpY) + 6)) { + Pather.moveToExit(me.area + 1, true); + Pather.moveToExit(me.area - 1, true); + } else { + Pather.useWaypoint([ + sdk.areas.CatacombsLvl2, sdk.areas.A2SewersLvl2, + sdk.areas.DuranceofHateLvl2, sdk.areas.RiverofFlame, sdk.areas.CrystalizedPassage + ][me.act - 1]); + } + } + + cycles += 1; + totalCycles += 1; + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Smith.js b/d2bs/kolbot/libs/scripts/Smith.js index 4dc8c4687..5fba16231 100644 --- a/d2bs/kolbot/libs/scripts/Smith.js +++ b/d2bs/kolbot/libs/scripts/Smith.js @@ -6,16 +6,16 @@ */ function Smith() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.OuterCloister); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.OuterCloister); + Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { - throw new Error("Failed to move to the Smith"); - } + if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { + throw new Error("Failed to move to the Smith"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Snapchip.js b/d2bs/kolbot/libs/scripts/Snapchip.js index f72bea856..3c2478d08 100644 --- a/d2bs/kolbot/libs/scripts/Snapchip.js +++ b/d2bs/kolbot/libs/scripts/Snapchip.js @@ -6,16 +6,17 @@ */ function Snapchip() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.AncientsWay); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.AncientsWay); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.IcyCellar, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SmallSparklyChest)) { - throw new Error("Failed to move to Snapchip Shatter"); - } + if (!Pather.moveToExit(sdk.areas.IcyCellar, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SmallSparklyChest)) { + throw new Error("Failed to move to Snapchip Shatter"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.SnapchipShatter)); - Config.Snapchip.ClearIcyCellar && Attack.clearLevel(Config.ClearType); + Attack.kill(getLocaleString(sdk.locale.monsters.SnapchipShatter)); + Config.Snapchip.ClearIcyCellar && Attack.clearLevel(Config.ClearType); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Stormtree.js b/d2bs/kolbot/libs/scripts/Stormtree.js index 2f191bf7d..0f891ecf4 100644 --- a/d2bs/kolbot/libs/scripts/Stormtree.js +++ b/d2bs/kolbot/libs/scripts/Stormtree.js @@ -6,15 +6,15 @@ */ function Stormtree() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.LowerKurast); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.LowerKurast); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.FlayerJungle, true)) { - throw new Error("Failed to move to Stormtree"); - } + if (!Pather.moveToExit(sdk.areas.FlayerJungle, true)) { + throw new Error("Failed to move to Stormtree"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.Stormtree)); + Attack.kill(getLocaleString(sdk.locale.monsters.Stormtree)); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index f6c5a0758..004a04689 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -6,54 +6,58 @@ */ function Summoner () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); - - if (Config.Summoner.FireEye) { - try { - if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); - } catch (e) { - console.error(e); - } - } - - if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) throw new Error("Failed to move back to arcane"); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, -3, -3)) throw new Error("Failed to move to Summoner"); - - Attack.clear(15, 0, sdk.monsters.TheSummoner); - - // always take portal, faster access to wp - // first check if portal is already up - let portal = Game.getObject(sdk.objects.RedPortal); - - if (!portal || !Pather.usePortal(null, null, portal)) { - for (let i = 0; i < 5; i++) { - // couldn't find portal, attempt to interact with journal - let journal = Game.getObject(sdk.objects.Journal); - - // couldnt find journal? Move to it's preset - if (!journal) { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); - continue; - } else if (journal && journal.distance > (18 - i)) { - Pather.moveNearUnit(journal, 13); - } - - Packet.entityInteract(journal); - Misc.poll(() => getIsTalkingNPC() || Game.getObject(sdk.objects.RedPortal), 2000, 200); - me.cancel() && me.cancel(); - - if (Pather.usePortal(sdk.areas.CanyonofMagic)) { - break; - } - } - } - - if (me.inArea(sdk.areas.CanyonofMagic)) { - Loader.scriptName(1) === "Duriel" ? Loader.skipTown.push("Duriel") : Pather.useWaypoint(sdk.areas.LutGholein); - } - - return true; + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); + + if (Config.Summoner.FireEye) { + try { + if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); + } catch (e) { + console.error(e); + } + } + + if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) { + throw new Error("Failed to move back to arcane"); + } + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, -3, -3)) { + throw new Error("Failed to move to Summoner"); + } + + Attack.clear(15, 0, sdk.monsters.TheSummoner); + + // always take portal, faster access to wp + // first check if portal is already up + let portal = Game.getObject(sdk.objects.RedPortal); + + if (!portal || !Pather.usePortal(null, null, portal)) { + for (let i = 0; i < 5; i++) { + // couldn't find portal, attempt to interact with journal + let journal = Game.getObject(sdk.objects.Journal); + + // couldnt find journal? Move to it's preset + if (!journal) { + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); + continue; + } else if (journal && journal.distance > (18 - i)) { + Pather.moveNearUnit(journal, 13); + } + + Packet.entityInteract(journal); + Misc.poll(() => getIsTalkingNPC() || Game.getObject(sdk.objects.RedPortal), 2000, 200); + me.cancel() && me.cancel(); + + if (Pather.usePortal(sdk.areas.CanyonofMagic)) { + break; + } + } + } + + if (me.inArea(sdk.areas.CanyonofMagic)) { + Loader.scriptName(1) === "Duriel" ? Loader.skipTown.push("Duriel") : Pather.useWaypoint(sdk.areas.LutGholein); + } + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Synch.js b/d2bs/kolbot/libs/scripts/Synch.js index b1fb25a8f..9800e0caf 100644 --- a/d2bs/kolbot/libs/scripts/Synch.js +++ b/d2bs/kolbot/libs/scripts/Synch.js @@ -10,49 +10,49 @@ let uRdyMsg = "I'm rdy, u?"; let rdyMsg = "rdy"; function messageHandler(nick, msg) { - if (nick !== me.name) { - if (msg === uRdyMsg) { - say(rdyMsg); - Synched = true; - } else if (msg === rdyMsg) { - Synched = true; - } else if (msg === "Yo, I'm rdy, u?") { - say("No"); - quit(); - } - } + if (nick !== me.name) { + if (msg === uRdyMsg) { + say(rdyMsg); + Synched = true; + } else if (msg === rdyMsg) { + Synched = true; + } else if (msg === "Yo, I'm rdy, u?") { + say("No"); + quit(); + } + } } function Synch() { - let i, party, j; + let i, party, j; - addEventListener("chatmsg", messageHandler); + addEventListener("chatmsg", messageHandler); - delay(1000); - say(uRdyMsg); + delay(1000); + say(uRdyMsg); - for (i = 0; i < 720 && !Synched; i += 1) { - delay(1000); + for (i = 0; i < 720 && !Synched; i += 1) { + delay(1000); - for (j = 0; j < Config.Synch.WaitFor.length; j += 1) { - party = getParty(Config.Synch.WaitFor[j]); - if (!party) { - D2Bot.printToConsole("WaitFor not in game: " + + for (j = 0; j < Config.Synch.WaitFor.length; j += 1) { + party = getParty(Config.Synch.WaitFor[j]); + if (!party) { + D2Bot.printToConsole("WaitFor not in game: " + Config.Synch.WaitFor[j] + " so quitting."); - removeEventListener("chatmsg", messageHandler); - quit(); - return false; - } - } - } + removeEventListener("chatmsg", messageHandler); + quit(); + return false; + } + } + } - if (!Synched) { - D2Bot.printToConsole("Failed to sync."); - quit(); - } + if (!Synched) { + D2Bot.printToConsole("Failed to sync."); + quit(); + } - removeEventListener("chatmsg", messageHandler); + removeEventListener("chatmsg", messageHandler); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Synch2.js b/d2bs/kolbot/libs/scripts/Synch2.js index d2a34db01..1bd658f0e 100644 --- a/d2bs/kolbot/libs/scripts/Synch2.js +++ b/d2bs/kolbot/libs/scripts/Synch2.js @@ -10,53 +10,53 @@ let uRdyMsg2 = "Yo, I'm rdy, u?"; let rdyMsg2 = "Let's go"; function messageHandler2(nick, msg) { - if (nick !== me.name) { - if (msg === uRdyMsg2) { - say(rdyMsg2); - Synched2 = true; - } else if (msg === rdyMsg2) { - Synched2 = true; - } else if (msg === "I'm rdy, u?") { - say("No"); - quit(); - } - } + if (nick !== me.name) { + if (msg === uRdyMsg2) { + say(rdyMsg2); + Synched2 = true; + } else if (msg === rdyMsg2) { + Synched2 = true; + } else if (msg === "I'm rdy, u?") { + say("No"); + quit(); + } + } } function Synch2() { - let i, party, j; + let i, party, j; - addEventListener("chatmsg", messageHandler2); + addEventListener("chatmsg", messageHandler2); - delay(1000); - say(uRdyMsg2); + delay(1000); + say(uRdyMsg2); - delay(1000); + delay(1000); - for (i = 0; i < 720 && !Synched2; i += 1) { - for (j = 0; j < Config.Synch.WaitFor.length; j += 1) { - party = getParty(Config.Synch.WaitFor[j]); - if (!party) { - D2Bot.printToConsole("WaitFor not in game: " + + for (i = 0; i < 720 && !Synched2; i += 1) { + for (j = 0; j < Config.Synch.WaitFor.length; j += 1) { + party = getParty(Config.Synch.WaitFor[j]); + if (!party) { + D2Bot.printToConsole("WaitFor not in game: " + Config.Synch.WaitFor[j] + " so quitting."); - removeEventListener("chatmsg", messageHandler2); - quit(); - return false; - } - } + removeEventListener("chatmsg", messageHandler2); + quit(); + return false; + } + } - delay(1000); - } + delay(1000); + } - if (!Synched) { - D2Bot.printToConsole("Failed to sync."); - quit(); - } + if (!Synched) { + D2Bot.printToConsole("Failed to sync."); + quit(); + } - delay(1000); + delay(1000); - removeEventListener("chatmsg", messageHandler2); + removeEventListener("chatmsg", messageHandler2); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Test.js b/d2bs/kolbot/libs/scripts/Test.js index 864048f3f..daa6dde31 100644 --- a/d2bs/kolbot/libs/scripts/Test.js +++ b/d2bs/kolbot/libs/scripts/Test.js @@ -6,33 +6,33 @@ */ function Test() { - print("ÿc8TESTING"); + print("ÿc8TESTING"); - let c; + let c; - function KeyDown(key) { - key === sdk.keys.Insert && (c = true); - } + function KeyDown(key) { + key === sdk.keys.Insert && (c = true); + } - addEventListener("keydown", KeyDown); + addEventListener("keydown", KeyDown); - while (true) { - if (c) { - try { - doTest(); - } catch (qq) { - print("failed"); - print(qq + " " + qq.fileName + " " + qq.lineNumber); - } + while (true) { + if (c) { + try { + doTest(); + } catch (qq) { + print("failed"); + print(qq + " " + qq.fileName + " " + qq.lineNumber); + } - c = false; - } + c = false; + } - delay(10); - } + delay(10); + } } function doTest() { - print("test"); - print("done"); + print("test"); + print("done"); } diff --git a/d2bs/kolbot/libs/scripts/ThreshSocket.js b/d2bs/kolbot/libs/scripts/ThreshSocket.js index 0b90cc7b7..d7ef6f565 100644 --- a/d2bs/kolbot/libs/scripts/ThreshSocket.js +++ b/d2bs/kolbot/libs/scripts/ThreshSocket.js @@ -6,16 +6,16 @@ */ function ThreshSocket() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArreatPlateau); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArreatPlateau); + Precast.doPrecast(true); - // ArreatPlateau returns invalid size with getBaseStat('leveldefs', 112, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); - // Could this be causing crashes here? Would it be better to go from crystal to Arreat instead? - if (!Pather.moveToExit(sdk.areas.CrystalizedPassage, false)) throw new Error("Failed to move to Thresh Socket"); + // ArreatPlateau returns invalid size with getBaseStat('leveldefs', 112, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); + // Could this be causing crashes here? Would it be better to go from crystal to Arreat instead? + if (!Pather.moveToExit(sdk.areas.CrystalizedPassage, false)) throw new Error("Failed to move to Thresh Socket"); - Attack.kill(getLocaleString(sdk.locale.monsters.ThreshSocket)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.ThreshSocket)); + Pickit.pickItems(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Tombs.js b/d2bs/kolbot/libs/scripts/Tombs.js index d7990df1c..5afdb933b 100644 --- a/d2bs/kolbot/libs/scripts/Tombs.js +++ b/d2bs/kolbot/libs/scripts/Tombs.js @@ -6,27 +6,27 @@ */ function Tombs() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CanyonofMagic); - Precast.doPrecast(true); - const correctTomb = getRoom().correcttomb; + Town.doChores(); + Pather.useWaypoint(sdk.areas.CanyonofMagic); + Precast.doPrecast(true); + const correctTomb = getRoom().correcttomb; - for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i++) { - try { - if (!Pather.journeyTo(i, true)) throw new Error("Failed to move to tomb"); + for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i++) { + try { + if (!Pather.journeyTo(i, true)) throw new Error("Failed to move to tomb"); - Attack.clearLevel(Config.ClearType); + Attack.clearLevel(Config.ClearType); - if (Config.Tombs.KillDuriel && me.inArea(correctTomb)) { - Pather.journeyTo(sdk.areas.DurielsLair) && Attack.kill(sdk.monsters.Duriel); - Pather.journeyTo(sdk.areas.CanyonofMagic); - } + if (Config.Tombs.KillDuriel && me.inArea(correctTomb)) { + Pather.journeyTo(sdk.areas.DurielsLair) && Attack.kill(sdk.monsters.Duriel); + Pather.journeyTo(sdk.areas.CanyonofMagic); + } - if (!Pather.moveToExit(sdk.areas.CanyonofMagic, true)) throw new Error("Failed to move to Canyon"); - } catch (e) { - console.error(e); - } - } + if (!Pather.moveToExit(sdk.areas.CanyonofMagic, true)) throw new Error("Failed to move to Canyon"); + } catch (e) { + console.error(e); + } + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Travincal.js b/d2bs/kolbot/libs/scripts/Travincal.js index f101d8729..180669423 100644 --- a/d2bs/kolbot/libs/scripts/Travincal.js +++ b/d2bs/kolbot/libs/scripts/Travincal.js @@ -6,70 +6,70 @@ */ function Travincal() { - this.buildList = function (checkColl) { - let monsterList = []; - let monster = Game.getMonster(); + this.buildList = function (checkColl) { + let monsterList = []; + let monster = Game.getMonster(); - if (monster) { - do { - if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].includes(monster.classid) + if (monster) { + do { + if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].includes(monster.classid) && monster.attackable && (!checkColl || !checkCollision(me, monster, sdk.collision.BlockWall))) { - monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } + monsterList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } - return monsterList; - }; + return monsterList; + }; - Town.doChores(); - Pather.useWaypoint(sdk.areas.Travincal); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.Travincal); + Precast.doPrecast(true); - let orgX = me.x; - let orgY = me.y; + let orgX = me.x; + let orgY = me.y; - if (Config.Travincal.PortalLeech) { - Pather.moveTo(orgX + 85, orgY - 139); - Attack.securePosition(orgX + 70, orgY - 139, 25, 2000); - Attack.securePosition(orgX + 100, orgY - 139, 25, 2000); - Attack.securePosition(orgX + 85, orgY - 139, 25, 5000); - Pather.moveTo(orgX + 85, orgY - 139); - Pather.makePortal(); - delay(1000); - Precast.doPrecast(true); - } + if (Config.Travincal.PortalLeech) { + Pather.moveTo(orgX + 85, orgY - 139); + Attack.securePosition(orgX + 70, orgY - 139, 25, 2000); + Attack.securePosition(orgX + 100, orgY - 139, 25, 2000); + Attack.securePosition(orgX + 85, orgY - 139, 25, 5000); + Pather.moveTo(orgX + 85, orgY - 139); + Pather.makePortal(); + delay(1000); + Precast.doPrecast(true); + } - if (Skill.canUse(sdk.skills.LeapAttack) && !Pather.canTeleport()) { - let coords = [60, -53, 64, -72, 78, -72, 74, -88]; + if (Skill.canUse(sdk.skills.LeapAttack) && !Pather.canTeleport()) { + let coords = [60, -53, 64, -72, 78, -72, 74, -88]; - for (let i = 0; i < coords.length; i += 2) { - if (i % 4 === 0) { - Pather.moveTo(orgX + coords[i], orgY + coords[i + 1]); - } else { - Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, orgX + coords[i], orgY + coords[i + 1]); - Attack.clearList(this.buildList(1)); - } - } + for (let i = 0; i < coords.length; i += 2) { + if (i % 4 === 0) { + Pather.moveTo(orgX + coords[i], orgY + coords[i + 1]); + } else { + Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, orgX + coords[i], orgY + coords[i + 1]); + Attack.clearList(this.buildList(1)); + } + } - Attack.clearList(this.buildList(0)); - } else { - Pather.moveTo(orgX + 101, orgY - 56); + Attack.clearList(this.buildList(0)); + } else { + Pather.moveTo(orgX + 101, orgY - 56); - // Stack Merc - if (me.barbarian && !Pather.canTeleport() && me.expansion) { - Pather.moveToExit([sdk.areas.DuranceofHateLvl1, sdk.areas.Travincal], true); - } + // Stack Merc + if (me.barbarian && !Pather.canTeleport() && me.expansion) { + Pather.moveToExit([sdk.areas.DuranceofHateLvl1, sdk.areas.Travincal], true); + } - if (Config.MFLeader) { - Pather.makePortal(); - say("council " + me.area); - } + if (Config.MFLeader) { + Pather.makePortal(); + say("council " + me.area); + } - Attack.clearList(this.buildList(0)); - } + Attack.clearList(this.buildList(0)); + } - Config.MFLeader && Config.PublicMode && say("travdone"); + Config.MFLeader && Config.PublicMode && say("travdone"); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/TravincalLeech.js b/d2bs/kolbot/libs/scripts/TravincalLeech.js index 72a8a32f0..3d26e713d 100644 --- a/d2bs/kolbot/libs/scripts/TravincalLeech.js +++ b/d2bs/kolbot/libs/scripts/TravincalLeech.js @@ -14,70 +14,72 @@ */ function TravincalLeech () { - include("core/Common/Leecher.js"); - let leader; - let done = false; + include("core/Common/Leecher.js"); + let leader; + let done = false; - const chatEvent = function (nick, msg) { - if (nick === leader && msg.toLowerCase() === "travdone") { - done = true; - } - }; + const chatEvent = function (nick, msg) { + if (nick === leader && msg.toLowerCase() === "travdone") { + done = true; + } + }; - Town.goToTown(3); - Town.doChores(); - Town.move("portalspot"); + Town.goToTown(3); + Town.doChores(); + Town.move("portalspot"); - if (Config.Leader) { - leader = Config.Leader; - if (!Misc.poll(() => Misc.inMyParty(leader), Time.minutes(2), 1000)) throw new Error("TristramLeech: Leader not partied"); - } + if (Config.Leader) { + leader = Config.Leader; + if (!Misc.poll(() => Misc.inMyParty(leader), Time.minutes(2), 1000)) { + throw new Error("TristramLeech: Leader not partied"); + } + } - !leader && (leader = Misc.autoLeaderDetect({ - destination: sdk.areas.Travincal, - quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), - timeout: Time.minutes(5) - })); + !leader && (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.Travincal, + quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), + timeout: Time.minutes(5) + })); - if (leader) { - try { - const Worker = require("../modules/Worker"); - addEventListener("chatmsg", chatEvent); + if (leader) { + try { + const Worker = require("../modules/Worker"); + addEventListener("chatmsg", chatEvent); - Common.Leecher.killLeaderTracker = false; - Common.Leecher.leader = leader; - Common.Leecher.currentScript = Loader.scriptName(); - Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; + Common.Leecher.killLeaderTracker = false; + Common.Leecher.leader = leader; + Common.Leecher.currentScript = Loader.scriptName(); + Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; - while (Misc.inMyParty(Common.Leecher.leader)) { - if (done) return true; + while (Misc.inMyParty(Common.Leecher.leader)) { + if (done) return true; - if (me.inTown && Pather.getPortal(sdk.areas.Travincal, Common.Leecher.leader)) { - Pather.usePortal(sdk.areas.Travincal, Common.Leecher.leader); - Town.getCorpse(); - } + if (me.inTown && Pather.getPortal(sdk.areas.Travincal, Common.Leecher.leader)) { + Pather.usePortal(sdk.areas.Travincal, Common.Leecher.leader); + Town.getCorpse(); + } - if (me.mode === sdk.player.mode.Dead) { - me.revive(); + if (me.mode === sdk.player.mode.Dead) { + me.revive(); - while (!me.inTown) { - delay(100); - } + while (!me.inTown) { + delay(100); + } - Town.move("portalspot"); - } + Town.move("portalspot"); + } - delay(100); - } - } catch (e) { - console.error(e); - } finally { - removeEventListener("chatmsg", chatEvent); - Common.Leecher.killLeaderTracker = true; - } - } else { - console.warn("No leader found"); - } + delay(100); + } + } catch (e) { + console.error(e); + } finally { + removeEventListener("chatmsg", chatEvent); + Common.Leecher.killLeaderTracker = true; + } + } else { + console.warn("No leader found"); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Treehead.js b/d2bs/kolbot/libs/scripts/Treehead.js index d98bdcf2d..a1c850e25 100644 --- a/d2bs/kolbot/libs/scripts/Treehead.js +++ b/d2bs/kolbot/libs/scripts/Treehead.js @@ -6,15 +6,15 @@ */ function Treehead() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.DarkWood); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.DarkWood); + Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - throw new Error("Failed to move to Treehead"); - } + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + throw new Error("Failed to move to Treehead"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.TreeheadWoodFist)); + Attack.kill(getLocaleString(sdk.locale.monsters.TreeheadWoodFist)); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js index 9f70229c9..f1c6c6a1b 100644 --- a/d2bs/kolbot/libs/scripts/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -6,55 +6,63 @@ */ function Tristram () { - Pather._teleport = Pather.teleport; - - // complete quest if its not complete - if (!me.getQuest(sdk.quest.id.TheSearchForCain, 4) && !me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { - include("core/Common/Cain.js"); - Common.Cain.run(); - } - - MainLoop: - while (true) { - switch (true) { - case me.inTown: - Town.doChores(); - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - - break; - case me.inArea(sdk.areas.StonyField): - if (!Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) { - throw new Error("Failed to move to Rakanishu"); - } - - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Rakanishu)); - - while (!Pather.usePortal(sdk.areas.Tristram)) { - Attack.securePosition(me.x, me.y, 10, 1000); - } - - break; - case me.inArea(sdk.areas.Tristram): - let redPortal = Game.getObject(sdk.objects.RedPortal); - !!redPortal && Pather.moveTo(redPortal.x, redPortal.y + 6); - - if (Config.Tristram.PortalLeech) { - Pather.makePortal(); - delay(1000); - Pather.teleport = !Config.Tristram.WalkClear && Pather._teleport; - } - - Config.Tristram.PortalLeech ? Attack.clearLevel(0) : Attack.clearLevel(Config.ClearType); - - break MainLoop; - default: - break MainLoop; - } - } - - Config.MFLeader && Config.PublicMode && say("tristdone"); - Pather.teleport = Pather._teleport; - - return true; + Pather._teleport = Pather.teleport; + + // complete quest if its not complete + if (!me.getQuest(sdk.quest.id.TheSearchForCain, 4) + && !me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { + include("core/Common/Cain.js"); + Common.Cain.run(); + } + + MainLoop: + while (true) { + switch (true) { + case me.inTown: + Town.doChores(); + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + + break; + case me.inArea(sdk.areas.StonyField): + if (!Pather.moveToPreset( + sdk.areas.StonyField, + sdk.unittype.Monster, + sdk.monsters.preset.Rakanishu, + 0, 0, + false, + true) + ) { + throw new Error("Failed to move to Rakanishu"); + } + + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Rakanishu)); + + while (!Pather.usePortal(sdk.areas.Tristram)) { + Attack.securePosition(me.x, me.y, 10, 1000); + } + + break; + case me.inArea(sdk.areas.Tristram): + let redPortal = Game.getObject(sdk.objects.RedPortal); + !!redPortal && Pather.moveTo(redPortal.x, redPortal.y + 6); + + if (Config.Tristram.PortalLeech) { + Pather.makePortal(); + delay(1000); + Pather.teleport = !Config.Tristram.WalkClear && Pather._teleport; + } + + Config.Tristram.PortalLeech ? Attack.clearLevel(0) : Attack.clearLevel(Config.ClearType); + + break MainLoop; + default: + break MainLoop; + } + } + + Config.MFLeader && Config.PublicMode && say("tristdone"); + Pather.teleport = Pather._teleport; + + return true; } diff --git a/d2bs/kolbot/libs/scripts/TristramLeech.js b/d2bs/kolbot/libs/scripts/TristramLeech.js index 51c054116..0f5b4043d 100644 --- a/d2bs/kolbot/libs/scripts/TristramLeech.js +++ b/d2bs/kolbot/libs/scripts/TristramLeech.js @@ -6,111 +6,120 @@ */ function TristramLeech () { - include("core/Common/Leecher.js"); - let done = false; - let whereisleader, leader; + include("core/Common/Leecher.js"); + let done = false; + let whereisleader, leader; - const chatEvent = function (nick, msg) { - if (nick === leader && msg.toLowerCase() === "tristdone") { - done = true; - } - }; + const chatEvent = function (nick, msg) { + if (nick === leader && msg.toLowerCase() === "tristdone") { + done = true; + } + }; - Town.doChores(); - Town.goToTown(1); - Town.move("portalspot"); - - if (Config.Leader) { - leader = (Config.Leader || Config.TristramLeech.Leader); - if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), 1000)) throw new Error("TristramLeech: Leader not partied"); - } - - !leader && (leader = Misc.autoLeaderDetect({ - destination: sdk.areas.Tristram, - quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), - timeout: Time.minutes(5) - })); - - if (leader) { - try { - const Worker = require("../modules/Worker"); - addEventListener("chatmsg", chatEvent); - - Common.Leecher.leader = leader; - Common.Leecher.currentScript = Loader.scriptName(); - Common.Leecher.killLeaderTracker = false; - Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; - - if (!Misc.poll(() => { - if (done) return true; - if (Pather.getPortal(sdk.areas.Tristram, Config.Leader || null) && Pather.usePortal(sdk.areas.Tristram, Config.Leader || null)) { - return true; - } - - return false; - }, Time.minutes(5), 1000)) throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); - - Precast.doPrecast(true); - delay(3000); - - whereisleader = Misc.poll(() => { - let lead = getParty(leader); - - if (lead.area === sdk.areas.Tristram) { - return lead; - } - - return false; - }, Time.minutes(3), 1000); + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); + + if (Config.Leader) { + leader = (Config.Leader || Config.TristramLeech.Leader); + if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), 1000)) { + throw new Error("TristramLeech: Leader not partied"); + } + } + + !leader && (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.Tristram, + quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), + timeout: Time.minutes(5) + })); + + if (leader) { + try { + const Worker = require("../modules/Worker"); + addEventListener("chatmsg", chatEvent); + + Common.Leecher.leader = leader; + Common.Leecher.currentScript = Loader.scriptName(); + Common.Leecher.killLeaderTracker = false; + Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; + + if (!Misc.poll(() => { + if (done) return true; + if (Pather.getPortal(sdk.areas.Tristram, Config.Leader || null) + && Pather.usePortal(sdk.areas.Tristram, Config.Leader || null)) { + return true; + } + + return false; + }, Time.minutes(5), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + } + + Precast.doPrecast(true); + delay(3000); + + whereisleader = Misc.poll(() => { + let lead = getParty(leader); + + if (lead.area === sdk.areas.Tristram) { + return lead; + } + + return false; + }, Time.minutes(3), 1000); - while (true) { - if (done) return true; - - whereisleader = getParty(leader); - let leaderUnit = Misc.getPlayerUnit(leader); - - if (whereisleader.area !== sdk.areas.Tristram && !Misc.poll(() => { - let lead = getParty(leader); - - if (lead.area === sdk.areas.Tristram) { - return true; - } - - return false; - }, Time.minutes(3), 1000)) { - console.log("Leader wasn't in tristram for longer than 3 minutes, End script"); - - break; - } - - if (whereisleader.area === me.area) { - try { - if (copyUnit(leaderUnit).x) { - Config.TristramLeech.Helper && leaderUnit.distance > 4 && Pather.moveToUnit(leaderUnit) && Attack.clear(10); - !Config.TristramLeech.Helper && leaderUnit.distance > 20 && Pather.moveNearUnit(leaderUnit, 15); - } else { - Config.TristramLeech.Helper && Pather.moveTo(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y) && Attack.clear(10); - !Config.TristramLeech.Helper && Pather.moveNear(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y, 15); - } - } catch (err) { - if (whereisleader.area === me.area) { - Config.TristramLeech.Helper && Pather.moveTo(whereisleader.x, whereisleader.y) && Attack.clear(10); - !Config.TristramLeech.Helper && Pather.moveNear(whereisleader.x, whereisleader.y, 15); - } - } - } - - delay(100); - } - } catch (e) { - console.error(e); - } finally { - removeEventListener("chatmsg", chatEvent); - Common.Leecher.killLeaderTracker = true; - } - } - - if (!me.inTown && Town.goToTown()) throw new Error("Failed to get back to town"); - - return true; + while (true) { + if (done) return true; + + whereisleader = getParty(leader); + let leaderUnit = Misc.getPlayerUnit(leader); + + if (whereisleader.area !== sdk.areas.Tristram && !Misc.poll(() => { + let lead = getParty(leader); + + if (lead.area === sdk.areas.Tristram) { + return true; + } + + return false; + }, Time.minutes(3), 1000)) { + console.log("Leader wasn't in tristram for longer than 3 minutes, End script"); + + break; + } + + if (whereisleader.area === me.area) { + try { + if (copyUnit(leaderUnit).x) { + if (Config.TristramLeech.Helper && leaderUnit.distance > 4) { + Pather.moveToUnit(leaderUnit) && Attack.clear(10); + } + !Config.TristramLeech.Helper && leaderUnit.distance > 20 && Pather.moveNearUnit(leaderUnit, 15); + } else { + if (Config.TristramLeech.Helper) { + Pather.moveTo(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y) && Attack.clear(10); + } + !Config.TristramLeech.Helper && Pather.moveNear(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y, 15); + } + } catch (err) { + if (whereisleader.area === me.area) { + Config.TristramLeech.Helper && Pather.moveTo(whereisleader.x, whereisleader.y) && Attack.clear(10); + !Config.TristramLeech.Helper && Pather.moveNear(whereisleader.x, whereisleader.y, 15); + } + } + } + + delay(100); + } + } catch (e) { + console.error(e); + } finally { + removeEventListener("chatmsg", chatEvent); + Common.Leecher.killLeaderTracker = true; + } + } + + if (!me.inTown && Town.goToTown()) throw new Error("Failed to get back to town"); + + return true; } diff --git a/d2bs/kolbot/libs/scripts/UndergroundPassage.js b/d2bs/kolbot/libs/scripts/UndergroundPassage.js index a8bec7035..6e3dde7ad 100644 --- a/d2bs/kolbot/libs/scripts/UndergroundPassage.js +++ b/d2bs/kolbot/libs/scripts/UndergroundPassage.js @@ -6,15 +6,15 @@ */ function UndergroundPassage() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.UndergroundPassageLvl1, sdk.areas.UndergroundPassageLvl2], true)) { - throw new Error("Failed to move to Underground passage level 2"); - } + if (!Pather.moveToExit([sdk.areas.UndergroundPassageLvl1, sdk.areas.UndergroundPassageLvl2], true)) { + throw new Error("Failed to move to Underground passage level 2"); + } - Attack.clearLevel(); + Attack.clearLevel(); - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/UserAddon.js b/d2bs/kolbot/libs/scripts/UserAddon.js index e2d8a2776..1793f69a1 100644 --- a/d2bs/kolbot/libs/scripts/UserAddon.js +++ b/d2bs/kolbot/libs/scripts/UserAddon.js @@ -9,90 +9,92 @@ */ function UserAddon () { - let i, title, dummy, command = ""; - const UnitInfo = new (require("../modules/UnitInfo")); - const className = sdk.player.class.nameOf(me.classid); - const flags = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, - sdk.uiflags.Quest, sdk.uiflags.Msgs, sdk.uiflags.Stash, sdk.uiflags.Shop, sdk.uiflags.EscMenu, sdk.uiflags.Cube - ]; - - const keyEvent = function (key) { - switch (key) { - case sdk.keys.Spacebar: - FileTools.copy("libs/config/" + className + ".js", "libs/config/" + className + "." + me.name + ".js"); - D2Bot.printToConsole("libs/config/" + className + "." + me.name + ".js has been created."); - D2Bot.printToConsole("Please configure your bot and start it again."); - D2Bot.stop(); - - break; - } - }; - - const onChatInput = (speaker, msg) => { - if (msg.length && msg[0] === ".") { - command = str.split(" ")[0].split(".")[1]; - - return true; - } - - return false; - }; - - try { - // Make sure the item event is loaded - why though? - !Config.FastPick && addEventListener("itemaction", Pickit.itemEvent); - addEventListener("chatinputblocker", onChatInput); - - if (!FileTools.exists("libs/config/" + className + "." + me.name + ".js")) { - console.log("ÿc4UserAddonÿc0: Press HOME and then press SPACE if you want to create character config."); - addEventListener("keyup", keyEvent); - showConsole(); - } - - while (true) { - for (i = 0; i < flags.length; i += 1) { - if (getUIFlag(flags[i])) { - if (title) { - title.remove(); - dummy.remove(); - - title = false; - dummy = false; - } - - break; - } - } - - if (i === flags.length && !title) { - title = new Text(":: kolbot user addon ::", 400, 525, 4, 0, 2); - dummy = new Text("`", 1, 1); // Prevents crash - } - - UnitInfo.check(); - - if (command && command.toLowerCase() === "done") { - console.log("ÿc4UserAddon ÿc1ended"); - - return true; - } else { - console.log(command); - command = ""; - } - - Pickit.fastPick(); - UnitInfo.createInfo(Game.getSelectedUnit()); - - delay(20); - } - } finally { - removeEventListener("keyup", keyEvent); - removeEventListener("itemaction", Pickit.itemEvent); - removeEventListener("chatinputblocker", onChatInput); - // ensure hooks are properly disposed of - !!title && title.remove(); - dummy && dummy.remove(); - UnitInfo.remove(); - } + let i, title, dummy, command = ""; + const UnitInfo = new (require("../modules/UnitInfo")); + const className = sdk.player.class.nameOf(me.classid); + const flags = [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, + sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, + sdk.uiflags.Quest, sdk.uiflags.Msgs, sdk.uiflags.Stash, + sdk.uiflags.Shop, sdk.uiflags.EscMenu, sdk.uiflags.Cube + ]; + + const keyEvent = function (key) { + switch (key) { + case sdk.keys.Spacebar: + FileTools.copy("libs/config/" + className + ".js", "libs/config/" + className + "." + me.name + ".js"); + D2Bot.printToConsole("libs/config/" + className + "." + me.name + ".js has been created."); + D2Bot.printToConsole("Please configure your bot and start it again."); + D2Bot.stop(); + + break; + } + }; + + const onChatInput = (speaker, msg) => { + if (msg.length && msg[0] === ".") { + command = str.split(" ")[0].split(".")[1]; + + return true; + } + + return false; + }; + + try { + // Make sure the item event is loaded - why though? + !Config.FastPick && addEventListener("itemaction", Pickit.itemEvent); + addEventListener("chatinputblocker", onChatInput); + + if (!FileTools.exists("libs/config/" + className + "." + me.name + ".js")) { + console.log("ÿc4UserAddonÿc0: Press HOME and then press SPACE if you want to create character config."); + addEventListener("keyup", keyEvent); + showConsole(); + } + + while (true) { + for (i = 0; i < flags.length; i += 1) { + if (getUIFlag(flags[i])) { + if (title) { + title.remove(); + dummy.remove(); + + title = false; + dummy = false; + } + + break; + } + } + + if (i === flags.length && !title) { + title = new Text(":: kolbot user addon ::", 400, 525, 4, 0, 2); + dummy = new Text("`", 1, 1); // Prevents crash + } + + UnitInfo.check(); + + if (command && command.toLowerCase() === "done") { + console.log("ÿc4UserAddon ÿc1ended"); + + return true; + } else { + console.log(command); + command = ""; + } + + Pickit.fastPick(); + UnitInfo.createInfo(Game.getSelectedUnit()); + + delay(20); + } + } finally { + removeEventListener("keyup", keyEvent); + removeEventListener("itemaction", Pickit.itemEvent); + removeEventListener("chatinputblocker", onChatInput); + // ensure hooks are properly disposed of + !!title && title.remove(); + dummy && dummy.remove(); + UnitInfo.remove(); + } } diff --git a/d2bs/kolbot/libs/scripts/WPGetter.js b/d2bs/kolbot/libs/scripts/WPGetter.js index 1bfc0e89e..f36291da2 100644 --- a/d2bs/kolbot/libs/scripts/WPGetter.js +++ b/d2bs/kolbot/libs/scripts/WPGetter.js @@ -6,22 +6,23 @@ */ function WPGetter() { - Town.doChores(); - Town.goToTown(); - Pather.getWP(me.area); + Town.doChores(); + Town.goToTown(); + Pather.getWP(me.area); - let highestAct = me.highestAct; - let lastWP = sdk.areas.townOfAct((highestAct === 5 ? highestAct : highestAct + 1)); - lastWP === sdk.areas.Harrogath && (lastWP = me.baal ? sdk.areas.WorldstoneLvl2 : sdk.areas.AncientsWay); - let wpsToGet = Pather.nonTownWpAreas.filter((wp) => wp < lastWP && wp !== sdk.areas.HallsofPain && !getWaypoint(Pather.wpAreas.indexOf(wp))); + let highestAct = me.highestAct; + let lastWP = sdk.areas.townOfAct((highestAct === 5 ? highestAct : highestAct + 1)); + lastWP === sdk.areas.Harrogath && (lastWP = me.baal ? sdk.areas.WorldstoneLvl2 : sdk.areas.AncientsWay); + let wpsToGet = Pather.nonTownWpAreas + .filter((wp) => wp < lastWP && wp !== sdk.areas.HallsofPain && !getWaypoint(Pather.wpAreas.indexOf(wp))); - console.debug(wpsToGet); + console.debug(wpsToGet); - for (let i = 0; i < wpsToGet.length; i += 1) { - Pather.getWP(wpsToGet[i]); - delay(500); - Town.checkScrolls(sdk.items.TomeofTownPortal) < 10 && Town.doChores(); - } + for (let i = 0; i < wpsToGet.length; i += 1) { + Pather.getWP(wpsToGet[i]); + delay(500); + Town.checkScrolls(sdk.items.TomeofTownPortal) < 10 && Town.doChores(); + } - return true; + return true; } diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index d34b2e0cc..7a6c83ca4 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -6,414 +6,419 @@ */ function Wakka () { - include("core/Common/Diablo.js"); - const timeout = Config.Wakka.Wait; - const [minDist, maxDist] = [50, 80]; - const internals = { - safeTP: false, - coordsInit: false, - vizCoords: [], - seisCoords: [], - infCoords: [], - vizClear: false, - seisClear: false, - infClear: false, - }; - - let portal, tick; - let leader = ""; - let [leaderUnit, leaderPartyUnit] = [null, null]; - - const checkMonsters = function (range = 15, dodge = false) { - let monList = []; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.y < 5565 && monster.attackable && monster.distance <= range) { - if (!dodge) return true; - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - - if (!monList.length) return false; - - monList.sort(Sort.units); - - if (monList[0].distance < 25 && !checkCollision(me, monList[0], sdk.collision.Ranged)) { - Attack.deploy(monList[0], 25, 5, 15); - } - - return true; - }; - - const getCoords = function () { - if (!internals.coordsInit) { - Common.Diablo.initLayout(); - internals.vizCoords = Common.Diablo.vizLayout === 1 ? [7707, 5274] : [7708, 5298]; - internals.seisCoords = Common.Diablo.seisLayout === 1 ? [7812, 5223] : [7809, 5193]; - internals.infCoords = Common.Diablo.infLayout === 1 ? [7868, 5294] : [7882, 5306]; - internals.coordsInit = true; - } - }; - - const checkBoss = function (name) { - let glow = Game.getObject(sdk.objects.SealGlow); - - if (glow) { - for (let i = 0; i < 10; i += 1) { - let boss = Game.getMonster(name); - - if (boss) { - while (!boss.dead) { - delay(500); - } + include("core/Common/Diablo.js"); + const timeout = Config.Wakka.Wait; + const [minDist, maxDist] = [50, 80]; + const internals = { + safeTP: false, + coordsInit: false, + vizCoords: [], + seisCoords: [], + infCoords: [], + vizClear: false, + seisClear: false, + infClear: false, + }; + + let portal, tick; + let leader = ""; + let [leaderUnit, leaderPartyUnit] = [null, null]; + + const checkMonsters = function (range = 15, dodge = false) { + let monList = []; + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.y < 5565 && monster.attackable && monster.distance <= range) { + if (!dodge) return true; + monList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + + if (!monList.length) return false; + + monList.sort(Sort.units); + + if (monList[0].distance < 25 && !checkCollision(me, monList[0], sdk.collision.Ranged)) { + Attack.deploy(monList[0], 25, 5, 15); + } + + return true; + }; + + const getCoords = function () { + if (!internals.coordsInit) { + Common.Diablo.initLayout(); + internals.vizCoords = Common.Diablo.vizLayout === 1 ? [7707, 5274] : [7708, 5298]; + internals.seisCoords = Common.Diablo.seisLayout === 1 ? [7812, 5223] : [7809, 5193]; + internals.infCoords = Common.Diablo.infLayout === 1 ? [7868, 5294] : [7882, 5306]; + internals.coordsInit = true; + } + }; + + const checkBoss = function (name) { + let glow = Game.getObject(sdk.objects.SealGlow); + + if (glow) { + for (let i = 0; i < 10; i += 1) { + let boss = Game.getMonster(name); + + if (boss) { + while (!boss.dead) { + delay(500); + } - return true; - } - - delay(500); - } - - return true; - } - - return false; - }; - - const getCorpse = function () { - me.mode === sdk.player.mode.Dead && me.revive(); - - let rval = false; - let corpse = Game.getPlayer(me.name, sdk.player.mode.Dead); - - if (corpse) { - do { - if (corpse.distance <= 15) { - Pather.moveToUnit(corpse); - corpse.interact(); - delay(500); - - rval = true; - } - } while (corpse.getNext()); - } - - return rval; - }; - - const followPath = function (dest) { - let path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 10); - if (!path) throw new Error("Failed go get path"); - - while (path.length > 0) { - if (me.mode === sdk.player.mode.Dead || me.inTown) return false; - (!leaderUnit || !copyUnit(leaderUnit).x) && (leaderUnit = Game.getPlayer(leader)); - - if (leaderUnit) { - // monsters nearby - don't move - if (checkMonsters(45, true) && leaderUnit.distance <= maxDist) { - path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); - delay(200); - - continue; - } - - // leader within minDist range - don't move - if (leaderUnit.distance <= minDist) { - delay(200); - - continue; - } - - // make sure distance to next node isn't too hot - if ([path[0].x, path[0].y].mobCount({ range: 15 }) !== 0) { - console.log("Mobs at next node"); - // mobs, stay where we are - delay(200); - - continue; - } - } else { - // leaderUnit out of getUnit range but leader is still within reasonable distance - check party unit's coords! - leaderPartyUnit = getParty(leader); - - if (leaderPartyUnit) { - // leader went to town - don't move - if (leaderPartyUnit.area !== me.area) { - delay(200); - - continue; - } - - // if there's monsters between the leecher and leader, wait until monsters are dead or leader is out of maxDist range - if (checkMonsters(45, true) && getDistance(me, leaderPartyUnit.x, leaderPartyUnit.y) <= maxDist) { - path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); - - delay(200); - - continue; - } - } - } - - Pather.moveTo(path[0].x, path[0].y) && path.shift(); - // no mobs around us, so it's safe to pick - !me.checkForMobs({ range: 10, coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor) }) && Pickit.pickItems(5); - getCorpse(); - } - - return true; - }; - - const getLeaderUnitArea = function () { - (!leaderUnit || !copyUnit(leaderUnit).x) && (leaderUnit = Game.getPlayer(leader)); - return !!leaderUnit ? leaderUnit.area : getParty(leader).area; - }; - - const log = function (msg = "") { - me.overhead(msg); - console.log(msg); - }; - - // START - Town.goToTown(4); - Town.move("portalspot"); - - if (Config.Leader) { - leader = Config.Leader; - if (!Misc.poll(() => Misc.inMyParty(leader), 30e3, 1000)) throw new Error("Wakka: Leader not partied"); - } - - !leader && (leader = Misc.autoLeaderDetect({ - destination: sdk.areas.ChaosSanctuary, - quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), - timeout: timeout * 60e3 - })); - Town.doChores(); - - if (leader) { - addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - const Worker = require("../modules/Worker"); - - try { - if (Config.Wakka.SkipIfBaal) { - let leadTick = getTickCount(); - let killLeaderTracker = false; - - Worker.runInBackground.leaderTracker = function () { - if (Common.Diablo.done || killLeaderTracker) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { - if (Loader.scriptName() === "Wakka") { - killLeaderTracker = true; - throw new Error("Party leader is running baal"); - } else { - // kill process - return false; - } - } - - return true; - }; - } - - let levelTick = getTickCount(); - let killLevelTracker = false; - - Worker.runInBackground.levelTracker = function () { - if (Common.Diablo.done || killLevelTracker) return false; - // check every 3 seconds - if (getTickCount() - levelTick < 3000) return true; - levelTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - - if (me.charlvl >= Config.Wakka.StopAtLevel) { - Config.Wakka.StopProfile && D2Bot.stop(); - - if (Loader.scriptName() === "Wakka") { - killLevelTracker = true; - throw new Error("Reached wanted level"); - } else { - // kill process - return false; - } - } - - return true; - }; - - while (Misc.inMyParty(leader)) { - try { - switch (me.area) { - case sdk.areas.PandemoniumFortress: - portal = Pather.getPortal(sdk.areas.ChaosSanctuary, null); - - if (portal) { - !internals.safeTP && delay(5000); - Pather.usePortal(sdk.areas.ChaosSanctuary, null); - Precast.doPrecast(true); - } - - break; - case sdk.areas.ChaosSanctuary: - try { - let diaTick = getTickCount(); - - Worker.runInBackground.diaSpawned = function () { - if (Common.Diablo.done || (internals.vizClear && internals.seisClear && internals.infClear)) return false; - // check every 1/4 second - if (getTickCount() - diaTick < 250) return true; - diaTick = getTickCount(); - - if (Common.Diablo.diabloSpawned) { - internals.vizClear = true; - internals.seisClear = true; - internals.infClear = true; - throw new Error("Diablo spawned"); - } - - return true; - }; - - if (!internals.safeTP) { - if (checkMonsters(25, false)) { - log("hot tp"); - Pather.usePortal(sdk.areas.PandemoniumFortress, null); - getCorpse(); - - break; - } else { - getCoords(); - internals.safeTP = true; - } - } - - if (!internals.vizClear) { - if (!followPath(internals.vizCoords)) { - console.debug("Failed to move to viz"); - break; - } - - if (checkBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { - log("vizier dead"); - internals.vizClear = true; - Precast.doPrecast(true); - tick = getTickCount(); - - while (getTickCount() - tick >= 5000) { - delay(100); - } - } - - break; - } - - if (internals.vizClear && !internals.seisClear) { - if (!followPath(internals.seisCoords)) { - console.debug("Failed to move to seis"); - break; - } + return true; + } + + delay(500); + } + + return true; + } + + return false; + }; + + const getCorpse = function () { + me.mode === sdk.player.mode.Dead && me.revive(); + + let rval = false; + let corpse = Game.getPlayer(me.name, sdk.player.mode.Dead); + + if (corpse) { + do { + if (corpse.distance <= 15) { + Pather.moveToUnit(corpse); + corpse.interact(); + delay(500); + + rval = true; + } + } while (corpse.getNext()); + } + + return rval; + }; + + const followPath = function (dest) { + let path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 10); + if (!path) throw new Error("Failed go get path"); + + while (path.length > 0) { + if (me.mode === sdk.player.mode.Dead || me.inTown) return false; + (!leaderUnit || !copyUnit(leaderUnit).x) && (leaderUnit = Game.getPlayer(leader)); + + if (leaderUnit) { + // monsters nearby - don't move + if (checkMonsters(45, true) && leaderUnit.distance <= maxDist) { + path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); + delay(200); + + continue; + } + + // leader within minDist range - don't move + if (leaderUnit.distance <= minDist) { + delay(200); + + continue; + } + + // make sure distance to next node isn't too hot + if ([path[0].x, path[0].y].mobCount({ range: 15 }) !== 0) { + console.log("Mobs at next node"); + // mobs, stay where we are + delay(200); + + continue; + } + } else { + // leaderUnit out of getUnit range but leader is still within reasonable distance - check party unit's coords! + leaderPartyUnit = getParty(leader); + + if (leaderPartyUnit) { + // leader went to town - don't move + if (leaderPartyUnit.area !== me.area) { + delay(200); + + continue; + } + + // if there's monsters between the leecher and leader, wait until monsters are dead or leader is out of maxDist range + if (checkMonsters(45, true) && getDistance(me, leaderPartyUnit.x, leaderPartyUnit.y) <= maxDist) { + path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); + + delay(200); + + continue; + } + } + } + + Pather.moveTo(path[0].x, path[0].y) && path.shift(); + // no mobs around us, so it's safe to pick + !me.checkForMobs({ + range: 10, + coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor) + }) && Pickit.pickItems(5); + getCorpse(); + } + + return true; + }; + + const getLeaderUnitArea = function () { + (!leaderUnit || !copyUnit(leaderUnit).x) && (leaderUnit = Game.getPlayer(leader)); + return !!leaderUnit ? leaderUnit.area : getParty(leader).area; + }; + + const log = function (msg = "") { + me.overhead(msg); + console.log(msg); + }; + + // START + Town.goToTown(4); + Town.move("portalspot"); + + if (Config.Leader) { + leader = Config.Leader; + if (!Misc.poll(() => Misc.inMyParty(leader), 30e3, 1000)) throw new Error("Wakka: Leader not partied"); + } + + !leader && (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ChaosSanctuary, + quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), + timeout: timeout * 60e3 + })); + Town.doChores(); + + if (leader) { + addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + const Worker = require("../modules/Worker"); + + try { + if (Config.Wakka.SkipIfBaal) { + let leadTick = getTickCount(); + let killLeaderTracker = false; + + Worker.runInBackground.leaderTracker = function () { + if (Common.Diablo.done || killLeaderTracker) return false; + // check every 3 seconds + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { + if (Loader.scriptName() === "Wakka") { + killLeaderTracker = true; + throw new Error("Party leader is running baal"); + } else { + // kill process + return false; + } + } + + return true; + }; + } + + let levelTick = getTickCount(); + let killLevelTracker = false; + + Worker.runInBackground.levelTracker = function () { + if (Common.Diablo.done || killLevelTracker) return false; + // check every 3 seconds + if (getTickCount() - levelTick < 3000) return true; + levelTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + + if (me.charlvl >= Config.Wakka.StopAtLevel) { + Config.Wakka.StopProfile && D2Bot.stop(); + + if (Loader.scriptName() === "Wakka") { + killLevelTracker = true; + throw new Error("Reached wanted level"); + } else { + // kill process + return false; + } + } + + return true; + }; + + while (Misc.inMyParty(leader)) { + try { + switch (me.area) { + case sdk.areas.PandemoniumFortress: + portal = Pather.getPortal(sdk.areas.ChaosSanctuary, null); + + if (portal) { + !internals.safeTP && delay(5000); + Pather.usePortal(sdk.areas.ChaosSanctuary, null); + Precast.doPrecast(true); + } + + break; + case sdk.areas.ChaosSanctuary: + try { + let diaTick = getTickCount(); + + Worker.runInBackground.diaSpawned = function () { + if (Common.Diablo.done || (internals.vizClear && internals.seisClear && internals.infClear)) { + return false; + } + // check every 1/4 second + if (getTickCount() - diaTick < 250) return true; + diaTick = getTickCount(); + + if (Common.Diablo.diabloSpawned) { + internals.vizClear = true; + internals.seisClear = true; + internals.infClear = true; + throw new Error("Diablo spawned"); + } + + return true; + }; + + if (!internals.safeTP) { + if (checkMonsters(25, false)) { + log("hot tp"); + Pather.usePortal(sdk.areas.PandemoniumFortress, null); + getCorpse(); + + break; + } else { + getCoords(); + internals.safeTP = true; + } + } + + if (!internals.vizClear) { + if (!followPath(internals.vizCoords)) { + console.debug("Failed to move to viz"); + break; + } + + if (checkBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { + log("vizier dead"); + internals.vizClear = true; + Precast.doPrecast(true); + tick = getTickCount(); + + while (getTickCount() - tick >= 5000) { + delay(100); + } + } + + break; + } + + if (internals.vizClear && !internals.seisClear) { + if (!followPath(internals.seisCoords)) { + console.debug("Failed to move to seis"); + break; + } - if (checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { - log("seis dead"); - internals.seisClear = true; - Precast.doPrecast(true); - tick = getTickCount(); - - while (getTickCount() - tick >= 7000) { - delay(100); - } - } - - break; - } - - if (internals.vizClear && internals.seisClear && !internals.infClear) { - if (!followPath(internals.infCoords)) { - console.debug("Failed to move to infector"); - break; - } - - if (checkBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { - log("infector dead"); - internals.infClear = true; - Precast.doPrecast(true); - tick = getTickCount(); - - while (getTickCount() - tick >= 2000) { - delay(100); - } - } - - break; - } - - Pather.moveTo(7767, 5263); - Misc.poll(() => { - if (Common.Diablo.diabloSpawned) return true; - if (Game.getMonster(sdk.monsters.Diablo)) return true; - return false; - }, Time.minutes(2), 500); - } catch (e) { - console.log((e.message ? e.message : e)); - } - - if (internals.vizClear && internals.seisClear && internals.infClear) { - Pather.moveTo(7767, 5263); - - let diablo = Misc.poll(() => Game.getMonster(sdk.monsters.Diablo), Time.minutes(3), 500); - - if (diablo) { - while (!diablo.dead) { - delay(100); - } - log("Diablo is dead"); - - if (!me.canTpToTown() || !Town.goToTown()) { - Pather.usePortal(sdk.areas.PandemoniumFortress); - } - - return true; - } else { - log("Couldn't find diablo"); - } - } - - break; - } - - me.dead && me.revive(); - - delay(200); - } catch (e) { - console.error(e); - - return true; - } - } - } catch (e) { - // - } finally { - Common.Diablo.done; - removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - } - } else { - throw new Error("No leader found"); - } - - log("Wakka complete"); - - return true; + if (checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { + log("seis dead"); + internals.seisClear = true; + Precast.doPrecast(true); + tick = getTickCount(); + + while (getTickCount() - tick >= 7000) { + delay(100); + } + } + + break; + } + + if (internals.vizClear && internals.seisClear && !internals.infClear) { + if (!followPath(internals.infCoords)) { + console.debug("Failed to move to infector"); + break; + } + + if (checkBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { + log("infector dead"); + internals.infClear = true; + Precast.doPrecast(true); + tick = getTickCount(); + + while (getTickCount() - tick >= 2000) { + delay(100); + } + } + + break; + } + + Pather.moveTo(7767, 5263); + Misc.poll(() => { + if (Common.Diablo.diabloSpawned) return true; + if (Game.getMonster(sdk.monsters.Diablo)) return true; + return false; + }, Time.minutes(2), 500); + } catch (e) { + console.log((e.message ? e.message : e)); + } + + if (internals.vizClear && internals.seisClear && internals.infClear) { + Pather.moveTo(7767, 5263); + + let diablo = Misc.poll(() => Game.getMonster(sdk.monsters.Diablo), Time.minutes(3), 500); + + if (diablo) { + while (!diablo.dead) { + delay(100); + } + log("Diablo is dead"); + + if (!me.canTpToTown() || !Town.goToTown()) { + Pather.usePortal(sdk.areas.PandemoniumFortress); + } + + return true; + } else { + log("Couldn't find diablo"); + } + } + + break; + } + + me.dead && me.revive(); + + delay(200); + } catch (e) { + console.error(e); + + return true; + } + } + } catch (e) { + // + } finally { + Common.Diablo.done; + removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + } + } else { + throw new Error("No leader found"); + } + + log("Wakka complete"); + + return true; } diff --git a/d2bs/kolbot/libs/scripts/Worldstone.js b/d2bs/kolbot/libs/scripts/Worldstone.js index df8c5b807..723099657 100644 --- a/d2bs/kolbot/libs/scripts/Worldstone.js +++ b/d2bs/kolbot/libs/scripts/Worldstone.js @@ -6,34 +6,34 @@ */ function Worldstone() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - Precast.doPrecast(true); - /** + Town.doChores(); + 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); + /** @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); - } + 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); + Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) && Attack.clearLevel(Config.ClearType); - return true; + return true; } diff --git a/d2bs/kolbot/libs/starter/AdvancedConfig.js b/d2bs/kolbot/libs/starter/AdvancedConfig.js index 318139da7..704c8035a 100644 --- a/d2bs/kolbot/libs/starter/AdvancedConfig.js +++ b/d2bs/kolbot/libs/starter/AdvancedConfig.js @@ -7,8 +7,8 @@ */ (function (module) { - module.exports = { - /* Features: + module.exports = { + /* Features: Override channel for each profile, Override join delay for each profile Override default values for JoinChannel, FirstJoinMessage, AnnounceGames and AfterGameMessage per profile @@ -34,13 +34,13 @@ } */ - // Put your lines under this one. Multiple entries are separated by commas. No comma after the last one. + // Put your lines under this one. Multiple entries are separated by commas. No comma after the last one. - "Test": { - JoinChannel: "op nnqry", - JoinDelay: 3, - AnnounceGames: true, - AnnounceMessage: "Joining game" // output: Joining game Baals-23 - }, - }; + "Test": { + JoinChannel: "op nnqry", + JoinDelay: 3, + AnnounceGames: true, + AnnounceMessage: "Joining game" // output: Joining game Baals-23 + }, + }; })(module); diff --git a/d2bs/kolbot/libs/starter/StarterConfig.js b/d2bs/kolbot/libs/starter/StarterConfig.js index 871b85d26..02081b3be 100644 --- a/d2bs/kolbot/libs/starter/StarterConfig.js +++ b/d2bs/kolbot/libs/starter/StarterConfig.js @@ -7,38 +7,38 @@ */ (function (module) { - module.exports = { - MinGameTime: 360, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby - PingQuitDelay: 30, // Time in seconds to wait in lobby after quitting due to high ping - CreateGameDelay: rand(5, 15), // Seconds to wait before creating a new game - ResetCount: 999, // Reset game count back to 1 every X games. - CharacterDifference: 99, // Character level difference. Set to false to disable character difference. - MaxPlayerCount: 8, // Max amount of players in game between 1 and 8 - GameDescription: "", // Game description when creating a game - StopOnDeadHardcore: true, // Stop profile character has died on hardcore mode + module.exports = { + MinGameTime: 360, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby + PingQuitDelay: 30, // Time in seconds to wait in lobby after quitting due to high ping + CreateGameDelay: rand(5, 15), // Seconds to wait before creating a new game + ResetCount: 999, // Reset game count back to 1 every X games. + CharacterDifference: 99, // Character level difference. Set to false to disable character difference. + MaxPlayerCount: 8, // Max amount of players in game between 1 and 8 + GameDescription: "", // Game description when creating a game + StopOnDeadHardcore: true, // Stop profile character has died on hardcore mode - // ChannelConfig can override these options for individual profiles. - JoinChannel: "", // Default channel. - FirstJoinMessage: "", // Default join message. Can be an array of messages - ChatActionsDelay: 2, // Seconds to wait in lobby before entering a channel - AnnounceGames: false, // Default value - AnnounceMessage: "", // Message to announce before making game - AfterGameMessage: "", // Default message after a finished game. Can be an array of messages + // ChannelConfig can override these options for individual profiles. + JoinChannel: "", // Default channel. + FirstJoinMessage: "", // Default join message. Can be an array of messages + ChatActionsDelay: 2, // Seconds to wait in lobby before entering a channel + AnnounceGames: false, // Default value + AnnounceMessage: "", // Message to announce before making game + AfterGameMessage: "", // Default message after a finished game. Can be an array of messages - InvalidPasswordDelay: 10, // Minutes to wait after getting Invalid Password message - VersionErrorDelay: rand(5, 30), // Seconds to wait after 'unable to identify version' message - SwitchKeyDelay: 5, // Seconds to wait before switching a used/banned key or after realm down - CrashDelay: rand(120, 150), // Seconds to wait after a d2 window crash - FTJDelay: 120, // Seconds to wait after failing to create a game - RealmDownDelay: 3, // Minutes to wait after getting Realm Down message - UnableToConnectDelay: 5, // Minutes to wait after Unable To Connect message - TCPIPNoHostDelay: 5, // Seconds to wait after Cannot Connect To Server message - CDKeyInUseDelay: 5, // Minutes to wait before connecting again if CD-Key is in use. - ConnectingTimeout: 60, // Seconds to wait before cancelling the 'Connecting...' screen - PleaseWaitTimeout: 60, // Seconds to wait before cancelling the 'Please Wait...' screen - WaitInLineTimeout: 3600, // Seconds to wait before cancelling the 'Waiting in Line...' screen - WaitOutQueueRestriction: true, // Wait out queue if we are restricted, queue time > 10000 - WaitOutQueueExitToMenu: false, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby - GameDoesNotExistTimeout: 30, // Seconds to wait before cancelling the 'Game does not exist.' screen - }; + InvalidPasswordDelay: 10, // Minutes to wait after getting Invalid Password message + VersionErrorDelay: rand(5, 30), // Seconds to wait after 'unable to identify version' message + SwitchKeyDelay: 5, // Seconds to wait before switching a used/banned key or after realm down + CrashDelay: rand(120, 150), // Seconds to wait after a d2 window crash + FTJDelay: 120, // Seconds to wait after failing to create a game + RealmDownDelay: 3, // Minutes to wait after getting Realm Down message + UnableToConnectDelay: 5, // Minutes to wait after Unable To Connect message + TCPIPNoHostDelay: 5, // Seconds to wait after Cannot Connect To Server message + CDKeyInUseDelay: 5, // Minutes to wait before connecting again if CD-Key is in use. + ConnectingTimeout: 60, // Seconds to wait before cancelling the 'Connecting...' screen + PleaseWaitTimeout: 60, // Seconds to wait before cancelling the 'Please Wait...' screen + WaitInLineTimeout: 3600, // Seconds to wait before cancelling the 'Waiting in Line...' screen + WaitOutQueueRestriction: true, // Wait out queue if we are restricted, queue time > 10000 + WaitOutQueueExitToMenu: false, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby + GameDoesNotExistTimeout: 30, // Seconds to wait before cancelling the 'Game does not exist.' screen + }; })(module); diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index ca384607e..013e1ef67 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename AutoMule.js * @author kolton, theBGuy @@ -8,766 +9,766 @@ */ const AutoMule = { - /** @type {Object.} */ - Mules: Object.assign({}, require("./MuleConfig", null, false)), + /** @type {Object.} */ + Mules: Object.assign({}, require("./MuleConfig", null, false)), - /** @type {Object.} */ - TorchAnniMules: Object.assign({}, require("./TorchAnniMules", null, false)), + /** @type {Object.} */ + TorchAnniMules: Object.assign({}, require("./TorchAnniMules", null, false)), - inGame: false, - check: false, - torchAnniCheck: false, - gids: [], + inGame: false, + check: false, + torchAnniCheck: false, + gids: [], - // ################################## // - /* ##### Master/Muler Functions ##### */ - // ################################## // + // ################################## // + /* ##### Master/Muler Functions ##### */ + // ################################## // - /** + /** * Get mule and torchanni mule info if it exists * @returns {muleObj | {}} */ - getInfo: function () { - let info; - - for (let i in this.Mules) { - if (this.Mules.hasOwnProperty(i)) { - for (let j = 0; j < this.Mules[i].enabledProfiles.length; j += 1) { - if (String.isEqual(this.Mules[i].enabledProfiles[j], me.profile)) { - !info && (info = {}); - info.muleInfo = this.Mules[i]; - - break; - } - } - } - } - - for (let i in this.TorchAnniMules) { - if (this.TorchAnniMules.hasOwnProperty(i)) { - for (let j = 0; j < this.TorchAnniMules[i].enabledProfiles.length; j += 1) { - if (String.isEqual(this.TorchAnniMules[i].enabledProfiles[j], me.profile)) { - !info && (info = {}); - info.torchMuleInfo = this.TorchAnniMules[i]; - - break; - } - } - } - } - - return info; - }, - - muleCheck: function () { - let info = this.getInfo(); - - if (info && info.hasOwnProperty("muleInfo")) { - let items = this.getMuleItems(); - - if (info.muleInfo.hasOwnProperty("usedStashTrigger") && info.muleInfo.hasOwnProperty("usedInventoryTrigger") + getInfo: function () { + let info; + + for (let i in this.Mules) { + if (this.Mules.hasOwnProperty(i)) { + for (let j = 0; j < this.Mules[i].enabledProfiles.length; j += 1) { + if (String.isEqual(this.Mules[i].enabledProfiles[j], me.profile)) { + !info && (info = {}); + info.muleInfo = this.Mules[i]; + + break; + } + } + } + } + + for (let i in this.TorchAnniMules) { + if (this.TorchAnniMules.hasOwnProperty(i)) { + for (let j = 0; j < this.TorchAnniMules[i].enabledProfiles.length; j += 1) { + if (String.isEqual(this.TorchAnniMules[i].enabledProfiles[j], me.profile)) { + !info && (info = {}); + info.torchMuleInfo = this.TorchAnniMules[i]; + + break; + } + } + } + } + + return info; + }, + + muleCheck: function () { + let info = this.getInfo(); + + if (info && info.hasOwnProperty("muleInfo")) { + let items = this.getMuleItems(); + + if (info.muleInfo.hasOwnProperty("usedStashTrigger") && info.muleInfo.hasOwnProperty("usedInventoryTrigger") && Storage.Inventory.UsedSpacePercent() >= info.muleInfo.usedInventoryTrigger && Storage.Stash.UsedSpacePercent() >= info.muleInfo.usedStashTrigger && items.length > 0) { - D2Bot.printToConsole("MuleCheck triggered!", sdk.colors.D2Bot.DarkGold); + D2Bot.printToConsole("MuleCheck triggered!", sdk.colors.D2Bot.DarkGold); - return true; - } + return true; + } - for (let i = 0; i < items.length; i += 1) { - if (this.matchItem(items[i], Config.AutoMule.Trigger)) { - D2Bot.printToConsole("MuleCheck triggered!", sdk.colors.D2Bot.DarkGold); - return true; - } - } - } + for (let i = 0; i < items.length; i += 1) { + if (this.matchItem(items[i], Config.AutoMule.Trigger)) { + D2Bot.printToConsole("MuleCheck triggered!", sdk.colors.D2Bot.DarkGold); + return true; + } + } + } - return false; - }, + return false; + }, - /** + /** * Find a mule that matches our wanted check * @returns {muleObj | false} */ - getMule: function () { - let info = this.getInfo(); - - if (info) { - if (this.check && info.hasOwnProperty("muleInfo")) return info.muleInfo; - if (this.torchAnniCheck && info.hasOwnProperty("torchMuleInfo")) return info.torchMuleInfo; - } - - return false; - }, - - outOfGameCheck: function () { - if (!this.check && !this.torchAnniCheck) return false; - - let muleObj = this.getMule(); - if (!muleObj) return false; - - function muleCheckEvent(mode, msg) { - mode === 10 && (muleInfo = JSON.parse(msg)); - } - - let stopCheck = false; - let once = false; - let muleInfo = { status: "" }; - let failCount = 0; - let Controls = require("../../modules/Control"); - - if (!muleObj.continuousMule || !muleObj.skipMuleResponse) { - addEventListener("copydata", muleCheckEvent); - } - - if (muleObj.continuousMule) { - D2Bot.printToConsole("Starting mule.", sdk.colors.D2Bot.DarkGold); - D2Bot.start(muleObj.muleProfile); - } else { - D2Bot.printToConsole("Starting " + (this.torchAnniCheck === 2 ? "anni " : this.torchAnniCheck === 1 ? "torch " : "") + "mule profile: " + muleObj.muleProfile, sdk.colors.D2Bot.DarkGold); - } - - MainLoop: - while (true) { - // Set status to ready if using continuous mule with no response check - if (muleObj.continuousMule && muleObj.skipMuleResponse) { - muleInfo.status = "ready"; + getMule: function () { + let info = this.getInfo(); + + if (info) { + if (this.check && info.hasOwnProperty("muleInfo")) return info.muleInfo; + if (this.torchAnniCheck && info.hasOwnProperty("torchMuleInfo")) return info.torchMuleInfo; + } + + return false; + }, + + outOfGameCheck: function () { + if (!this.check && !this.torchAnniCheck) return false; + + let muleObj = this.getMule(); + if (!muleObj) return false; + + function muleCheckEvent(mode, msg) { + mode === 10 && (muleInfo = JSON.parse(msg)); + } + + let stopCheck = false; + let once = false; + let muleInfo = { status: "" }; + let failCount = 0; + let Controls = require("../../modules/Control"); + + if (!muleObj.continuousMule || !muleObj.skipMuleResponse) { + addEventListener("copydata", muleCheckEvent); + } + + if (muleObj.continuousMule) { + D2Bot.printToConsole("Starting mule.", sdk.colors.D2Bot.DarkGold); + D2Bot.start(muleObj.muleProfile); + } else { + D2Bot.printToConsole("Starting " + (this.torchAnniCheck === 2 ? "anni " : this.torchAnniCheck === 1 ? "torch " : "") + "mule profile: " + muleObj.muleProfile, sdk.colors.D2Bot.DarkGold); + } + + MainLoop: + while (true) { + // Set status to ready if using continuous mule with no response check + if (muleObj.continuousMule && muleObj.skipMuleResponse) { + muleInfo.status = "ready"; - // If nothing received our copy data start the mule profile - } else if (!sendCopyData(null, muleObj.muleProfile, 10, JSON.stringify({ profile: me.profile, mode: this.torchAnniCheck || 0 })) && !muleObj.continuousMule) { - // if the mule profile isn't already running and there is a profile to be stopped, stop it before starting the mule profile - if (!stopCheck && muleObj.stopProfile && !String.isEqual(me.profile, muleObj.stopProfile)) { - D2Bot.stop(muleObj.stopProfile, muleObj.stopProfileKeyRelease); - stopCheck = true; - delay(2000); // prevents cd-key in use error if using -skiptobnet on mule profile - } - - D2Bot.start(muleObj.muleProfile); - } - - delay(1000); - - switch (muleInfo.status) { - case "loading": - if (!muleObj.continuousMule && !stopCheck && muleObj.stopProfile && !String.isEqual(me.profile, muleObj.stopProfile)) { - D2Bot.stop(muleObj.stopProfile, muleObj.stopProfileKeyRelease); - - stopCheck = true; - } - - failCount += 1; - - break; - case "busy": - case "begin": - D2Bot.printToConsole("Mule profile is busy.", sdk.colors.D2Bot.Red); - - break MainLoop; - case "ready": - Starter.LocationEvents.openJoinGameWindow(); - - delay(2000); - - AutoMule.inGame = true; - me.blockMouse = true; - - try { - joinGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); - } catch (joinError) { - delay(100); - } - - me.blockMouse = false; - - // Untested change 11.Feb.14. - for (let i = 0; i < 8; i += 1) { - delay(1000); - - if (me.ingame && me.gameReady) { - break MainLoop; - } - } - - if (!once && getLocation() === sdk.game.locations.GameIsFull) { - Controls.CreateGameWindow.click(); - Starter.LocationEvents.openJoinGameWindow(); - // how long should we wait? - once = true; - let date = new Date(); - let dateString = "[" + new Date( - date.getTime() + Time.minutes(3) - (date.getTimezoneOffset() * 60000) - ).toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; - console.log("Game is full so lets hangout for a bit before we try again. Next attempt at " + dateString); - } - - if (muleObj.continuousMule && muleObj.skipMuleResponse && !me.ingame) { - D2Bot.printToConsole("Unable to join mule game", sdk.colors.D2Bot.Red); - - break MainLoop; - } - - break; - default: - failCount += 1; - - break; - } - - if (failCount >= 260) { - D2Bot.printToConsole("No response from mule profile.", sdk.colors.D2Bot.Red); - - break; - } - } - - removeEventListener("copydata", muleCheckEvent); - - while (me.ingame) { - delay(1000); - } - - AutoMule.inGame = false; - AutoMule.check = false; - AutoMule.torchAnniCheck = false; - - // No response - stop mule profile - if (!muleObj.continuousMule) { - if (failCount >= 60) { - D2Bot.stop(muleObj.muleProfile, true); - delay(1000); - } + // If nothing received our copy data start the mule profile + } else if (!sendCopyData(null, muleObj.muleProfile, 10, JSON.stringify({ profile: me.profile, mode: this.torchAnniCheck || 0 })) && !muleObj.continuousMule) { + // if the mule profile isn't already running and there is a profile to be stopped, stop it before starting the mule profile + if (!stopCheck && muleObj.stopProfile && !String.isEqual(me.profile, muleObj.stopProfile)) { + D2Bot.stop(muleObj.stopProfile, muleObj.stopProfileKeyRelease); + stopCheck = true; + delay(2000); // prevents cd-key in use error if using -skiptobnet on mule profile + } + + D2Bot.start(muleObj.muleProfile); + } + + delay(1000); + + switch (muleInfo.status) { + case "loading": + if (!muleObj.continuousMule && !stopCheck && muleObj.stopProfile && !String.isEqual(me.profile, muleObj.stopProfile)) { + D2Bot.stop(muleObj.stopProfile, muleObj.stopProfileKeyRelease); + + stopCheck = true; + } + + failCount += 1; + + break; + case "busy": + case "begin": + D2Bot.printToConsole("Mule profile is busy.", sdk.colors.D2Bot.Red); + + break MainLoop; + case "ready": + Starter.LocationEvents.openJoinGameWindow(); + + delay(2000); + + AutoMule.inGame = true; + me.blockMouse = true; + + try { + joinGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); + } catch (joinError) { + delay(100); + } + + me.blockMouse = false; + + // Untested change 11.Feb.14. + for (let i = 0; i < 8; i += 1) { + delay(1000); + + if (me.ingame && me.gameReady) { + break MainLoop; + } + } + + if (!once && getLocation() === sdk.game.locations.GameIsFull) { + Controls.CreateGameWindow.click(); + Starter.LocationEvents.openJoinGameWindow(); + // how long should we wait? + once = true; + let date = new Date(); + let dateString = "[" + new Date( + date.getTime() + Time.minutes(3) - (date.getTimezoneOffset() * 60000) + ).toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; + console.log("Game is full so lets hangout for a bit before we try again. Next attempt at " + dateString); + } + + if (muleObj.continuousMule && muleObj.skipMuleResponse && !me.ingame) { + D2Bot.printToConsole("Unable to join mule game", sdk.colors.D2Bot.Red); + + break MainLoop; + } + + break; + default: + failCount += 1; + + break; + } + + if (failCount >= 260) { + D2Bot.printToConsole("No response from mule profile.", sdk.colors.D2Bot.Red); + + break; + } + } + + removeEventListener("copydata", muleCheckEvent); + + while (me.ingame) { + delay(1000); + } + + AutoMule.inGame = false; + AutoMule.check = false; + AutoMule.torchAnniCheck = false; + + // No response - stop mule profile + if (!muleObj.continuousMule) { + if (failCount >= 60) { + D2Bot.stop(muleObj.muleProfile, true); + delay(1000); + } - if (stopCheck && muleObj.stopProfile) { - D2Bot.start(muleObj.stopProfile); - } - } + if (stopCheck && muleObj.stopProfile) { + D2Bot.start(muleObj.stopProfile); + } + } - return true; - }, + return true; + }, - inGameCheck: function () { - let muleObj, tick; - let begin = false; - let timeout = Time.minutes(4); // Ingame mule timeout - let status = "muling"; + inGameCheck: function () { + let muleObj, tick; + let begin = false; + let timeout = Time.minutes(4); // Ingame mule timeout + let status = "muling"; - // Single player - if (!me.gamename) return false; + // Single player + if (!me.gamename) return false; - let info = this.getInfo(); + let info = this.getInfo(); - // Profile is not a part of AutoMule - if (!info) return false; + // Profile is not a part of AutoMule + if (!info) return false; - // Profile is not in mule or torch mule game - if (!((info.hasOwnProperty("muleInfo") && String.isEqual(me.gamename, info.muleInfo.muleGameName[0])) + // Profile is not in mule or torch mule game + if (!((info.hasOwnProperty("muleInfo") && String.isEqual(me.gamename, info.muleInfo.muleGameName[0])) || (info.hasOwnProperty("torchMuleInfo") && String.isEqual(me.gamename, info.torchMuleInfo.muleGameName[0])))) { - return false; - } - - function dropStatusEvent (mode, msg) { - if (mode === 10) { - switch (JSON.parse(msg).status) { - case "report": // reply to status request - sendCopyData(null, muleObj.muleProfile, 12, JSON.stringify({ status: status })); - - break; - case "quit": // quit command - status = "quit"; - - break; - } - } - } - - function muleModeEvent (msg) { - switch (msg) { - case "2": - case "1": - AutoMule.torchAnniCheck = Number(msg); - - break; - case "0": - AutoMule.check = true; - } - } - - try { - addEventListener("scriptmsg", muleModeEvent); - scriptBroadcast("getMuleMode"); - delay(500); - - if (!this.check && !this.torchAnniCheck) { - throw new Error("Error - Unable to determine mule mode"); - } - - muleObj = this.getMule(); - me.maxgametime = 0; - - !muleObj.continuousMule && addEventListener("copydata", dropStatusEvent); - - if (!Town.goToTown(1)) { - throw new Error("Error - Failed to go to Act 1"); - } - - Town.move("stash"); - - if (muleObj.continuousMule) { - print("ÿc4AutoMuleÿc0: Looking for valid mule"); - tick = getTickCount(); - - while (getTickCount() - tick < timeout) { - if (this.verifyMulePrefix(muleObj.charPrefix)) { - print("ÿc4AutoMuleÿc0: Found valid mule"); - begin = true; - - break; - } - - delay(2000); - } - - if (!begin) { - throw new Error("Error - Unable to find mule character"); - } - } else { - console.debug("MuleProfile :: " + muleObj.muleProfile); - sendCopyData(null, muleObj.muleProfile, 11, "begin"); - } - - let gameType = this.torchAnniCheck === 2 ? " anni" : this.torchAnniCheck === 1 ? " torch" : ""; - print("ÿc4AutoMuleÿc0: In" + gameType + " mule game."); - D2Bot.updateStatus("AutoMule: In" + gameType + " mule game."); + return false; + } + + function dropStatusEvent (mode, msg) { + if (mode === 10) { + switch (JSON.parse(msg).status) { + case "report": // reply to status request + sendCopyData(null, muleObj.muleProfile, 12, JSON.stringify({ status: status })); + + break; + case "quit": // quit command + status = "quit"; + + break; + } + } + } + + function muleModeEvent (msg) { + switch (msg) { + case "2": + case "1": + AutoMule.torchAnniCheck = Number(msg); + + break; + case "0": + AutoMule.check = true; + } + } + + try { + addEventListener("scriptmsg", muleModeEvent); + scriptBroadcast("getMuleMode"); + delay(500); + + if (!this.check && !this.torchAnniCheck) { + throw new Error("Error - Unable to determine mule mode"); + } + + muleObj = this.getMule(); + me.maxgametime = 0; + + !muleObj.continuousMule && addEventListener("copydata", dropStatusEvent); + + if (!Town.goToTown(1)) { + throw new Error("Error - Failed to go to Act 1"); + } + + Town.move("stash"); + + if (muleObj.continuousMule) { + print("ÿc4AutoMuleÿc0: Looking for valid mule"); + tick = getTickCount(); + + while (getTickCount() - tick < timeout) { + if (this.verifyMulePrefix(muleObj.charPrefix)) { + print("ÿc4AutoMuleÿc0: Found valid mule"); + begin = true; + + break; + } + + delay(2000); + } + + if (!begin) { + throw new Error("Error - Unable to find mule character"); + } + } else { + console.debug("MuleProfile :: " + muleObj.muleProfile); + sendCopyData(null, muleObj.muleProfile, 11, "begin"); + } + + let gameType = this.torchAnniCheck === 2 ? " anni" : this.torchAnniCheck === 1 ? " torch" : ""; + print("ÿc4AutoMuleÿc0: In" + gameType + " mule game."); + D2Bot.updateStatus("AutoMule: In" + gameType + " mule game."); - if (this.torchAnniCheck === 2) { - this.dropCharm(true); - } else if (this.torchAnniCheck === 1) { - this.dropCharm(false); - } else { - this.dropStuff(); - } - - status = "done"; - tick = getTickCount(); - - while (true) { - if (muleObj.continuousMule) { - if (this.isFinished()) { - D2Bot.printToConsole("Done muling.", sdk.colors.D2Bot.DarkGold); - status = "quit"; - } else { - delay(5000); - } - } - - if (status === "quit") { - break; - } - - if (getTickCount() - tick > timeout) { - if (Misc.getPlayerCount() > 1) { - // we aren't alone currently so chill for a bit longer - tick = getTickCount(); - Packet.questRefresh(); // to prevent disconnect from idleing - } else { - D2Bot.printToConsole("Mule didn't rejoin. Picking up items.", sdk.colors.D2Bot.Red); - Misc.useItemLog = false; // Don't log items picked back up in town. - Pickit.pickItems(); - - break; - } - } - - delay(500); - } + if (this.torchAnniCheck === 2) { + this.dropCharm(true); + } else if (this.torchAnniCheck === 1) { + this.dropCharm(false); + } else { + this.dropStuff(); + } + + status = "done"; + tick = getTickCount(); + + while (true) { + if (muleObj.continuousMule) { + if (this.isFinished()) { + D2Bot.printToConsole("Done muling.", sdk.colors.D2Bot.DarkGold); + status = "quit"; + } else { + delay(5000); + } + } + + if (status === "quit") { + break; + } + + if (getTickCount() - tick > timeout) { + if (Misc.getPlayerCount() > 1) { + // we aren't alone currently so chill for a bit longer + tick = getTickCount(); + Packet.questRefresh(); // to prevent disconnect from idleing + } else { + D2Bot.printToConsole("Mule didn't rejoin. Picking up items.", sdk.colors.D2Bot.Red); + Misc.useItemLog = false; // Don't log items picked back up in town. + Pickit.pickItems(); + + break; + } + } + + delay(500); + } - return true; - } catch (e) { - console.error(e); - - return false; - } finally { - removeEventListener("scriptmsg", muleModeEvent); - removeEventListener("copydata", dropStatusEvent); + return true; + } catch (e) { + console.error(e); + + return false; + } finally { + removeEventListener("scriptmsg", muleModeEvent); + removeEventListener("copydata", dropStatusEvent); - if (!muleObj.continuousMule) { - D2Bot.stop(muleObj.muleProfile, true); - delay(1000); - muleObj.stopProfile && D2Bot.start(muleObj.stopProfile); - } - - delay(2000); - quit(); - } - }, - - /** + if (!muleObj.continuousMule) { + D2Bot.stop(muleObj.muleProfile, true); + delay(1000); + muleObj.stopProfile && D2Bot.start(muleObj.stopProfile); + } + + delay(2000); + quit(); + } + }, + + /** * finished if no items are on ground */ - isFinished: function () { - let item = Game.getItem(); + isFinished: function () { + let item = Game.getItem(); - if (item) { - do { - // check if the items we dropped are on the ground still - if (getDistance(me, item) < 20 && item.onGroundOrDropping && AutoMule.gids.includes(item.gid)) { - return false; - } - } while (item.getNext()); - } + if (item) { + do { + // check if the items we dropped are on the ground still + if (getDistance(me, item) < 20 && item.onGroundOrDropping && AutoMule.gids.includes(item.gid)) { + return false; + } + } while (item.getNext()); + } - // we are finished so reset gid list - AutoMule.gids.length = 0; + // we are finished so reset gid list + AutoMule.gids.length = 0; - return true; - }, + return true; + }, - /** + /** * make sure mule character is in game * @param {string} mulePrefix */ - verifyMulePrefix: function (mulePrefix) { - try { - let player = getParty(); - - if (player) { - let regex = new RegExp(mulePrefix, "i"); - - do { - // case insensitive matching - if (player.name.match(regex)) { - return true; - } - } while (player.getNext()); - } - } catch (e) { - delay(100); - } - - return false; - }, - - /** + verifyMulePrefix: function (mulePrefix) { + try { + let player = getParty(); + + if (player) { + let regex = new RegExp(mulePrefix, "i"); + + do { + // case insensitive matching + if (player.name.match(regex)) { + return true; + } + } while (player.getNext()); + } + } catch (e) { + delay(100); + } + + return false; + }, + + /** * Transfer items to waiting mule * @returns {boolean} */ - dropStuff: function () { - if (!Town.openStash()) return false; + dropStuff: function () { + if (!Town.openStash()) return false; - let items = (this.getMuleItems() || []); - if (items.length === 0) return false; - AutoMule.gids = items.map(i => i.gid); + let items = (this.getMuleItems() || []); + if (items.length === 0) return false; + AutoMule.gids = items.map(i => i.gid); - D2Bot.printToConsole("AutoMule: Transfering " + items.length + " items.", sdk.colors.D2Bot.DarkGold); - D2Bot.printToConsole("AutoMule: " + JSON.stringify(items.map(i => i.prettyPrint)), sdk.colors.D2Bot.DarkGold); + D2Bot.printToConsole("AutoMule: Transfering " + items.length + " items.", sdk.colors.D2Bot.DarkGold); + D2Bot.printToConsole("AutoMule: " + JSON.stringify(items.map(i => i.prettyPrint)), sdk.colors.D2Bot.DarkGold); - items.forEach(item => item.drop()); - delay(1000); - me.cancel(); + items.forEach(item => item.drop()); + delay(1000); + me.cancel(); - // clean up stash - Storage.Stash.SortItems(); - me.cancelUIFlags(); + // clean up stash + Storage.Stash.SortItems(); + me.cancelUIFlags(); - return true; - }, + return true; + }, - /** + /** * @param {ItemUnit} item * @param {string[] | number[]} list * @returns {boolean} */ - matchItem: function (item, list) { - let parsedPickit = [], 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); - parsedLine && parsedPickit.push(parsedLine); - } - } - - return (classIDs.includes(item.classid) || NTIP.CheckItem(item, parsedPickit)); - }, - - /** + matchItem: function (item, list) { + let parsedPickit = [], 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); + parsedLine && parsedPickit.push(parsedLine); + } + } + + return (classIDs.includes(item.classid) || NTIP.CheckItem(item, parsedPickit)); + }, + + /** * get a list of items to mule * @returns {ItemUnit[] | false} */ - getMuleItems: function () { - let info = this.getInfo(); - if (!info || !info.hasOwnProperty("muleInfo")) return false; + getMuleItems: function () { + let info = this.getInfo(); + if (!info || !info.hasOwnProperty("muleInfo")) return false; - const muleOrphans = !!(info.muleInfo.hasOwnProperty("muleOrphans") && info.muleInfo.muleOrphans); + const muleOrphans = !!(info.muleInfo.hasOwnProperty("muleOrphans") && info.muleInfo.muleOrphans); - /** + /** * @param {ItemUnit} item */ - const isAKey = (item) => [sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction].includes(item.classid); + 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)); - - 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 cube/torch/annihilus - if (item.isAnni || item.isTorch || item.classid === sdk.quest.item.Cube) return false; - // don't mule items in locked spots - 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 - if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return false; - // don't mule keys if part of torchsystem - 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 - let pResult = Pickit.checkItem(item).result; - // if it's a junk item, we don't want it - if ([Pickit.Result.UNID, Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pResult)) return (item.isInStash && muleOrphans); - // we've made it this far, we want it - return true; - }); - - return items; - }, - - /** + const isWanted = (item) => (AutoMule.cubingIngredient(item) || AutoMule.runewordIngredient(item) || AutoMule.utilityIngredient(item)); + + 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 cube/torch/annihilus + if (item.isAnni || item.isTorch || item.classid === sdk.quest.item.Cube) return false; + // don't mule items in locked spots + 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 + if (isWanted(item) && !AutoMule.matchItem(item, Config.AutoMule.Force.concat(Config.AutoMule.Trigger))) return false; + // don't mule keys if part of torchsystem + 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 + let pResult = Pickit.checkItem(item).result; + // if it's a junk item, we don't want it + if ([Pickit.Result.UNID, Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pResult)) return (item.isInStash && muleOrphans); + // we've made it this far, we want it + return true; + }); + + return items; + }, + + /** * Wanted by CraftingSystem * @param {ItemUnit} item * @returns {boolean} */ - utilityIngredient: function (item) { - return (!!item && CraftingSystem.validGids.includes(item.gid)); - }, + utilityIngredient: function (item) { + return (!!item && CraftingSystem.validGids.includes(item.gid)); + }, - /** + /** * check if an item is a cubing ingredient * @param {ItemUnit} item * @returns {boolean} */ - cubingIngredient: function (item) { - if (!item) return false; + cubingIngredient: function (item) { + if (!item) return false; - for (let i = 0; i < Cubing.validIngredients.length; i += 1) { - if (item.gid === Cubing.validIngredients[i].gid) { - return true; - } - } + for (let i = 0; i < Cubing.validIngredients.length; i += 1) { + if (item.gid === Cubing.validIngredients[i].gid) { + return true; + } + } - return false; - }, + return false; + }, - /** + /** * check if an item is a runeword ingrediend - rune, empty base or bad rolled base * @param {ItemUnit} item * @returns {boolean} */ - runewordIngredient: function (item) { - if (!item) return false; - if (Runewords.validGids.includes(item.gid)) return true; + runewordIngredient: function (item) { + if (!item) return false; + if (Runewords.validGids.includes(item.gid)) return true; - if (!this.baseGids) { - AutoMule.baseGids = []; + if (!this.baseGids) { + AutoMule.baseGids = []; - for (let i = 0; i < Config.Runewords.length; i += 1) { - let base = Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) || Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true); - base && this.baseGids.push(base.gid); - } - } + for (let i = 0; i < Config.Runewords.length; i += 1) { + let base = Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) || Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true); + base && this.baseGids.push(base.gid); + } + } - return this.baseGids.includes(item.gid); - }, + return this.baseGids.includes(item.gid); + }, - /** + /** * Drop Anni or Gheeds * @param {boolean} dropAnni * @returns {boolean} */ - dropCharm: function (dropAnni) { - if (!Town.openStash()) return false; + dropCharm: function (dropAnni) { + if (!Town.openStash()) return false; - let item; + let item; - if (dropAnni) { - item = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + if (dropAnni) { + item = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - if (item && !Storage.Inventory.IsLocked(item, Config.Inventory)) { - D2Bot.printToConsole("AutoMule: Transfering Anni.", sdk.colors.D2Bot.DarkGold); - } else { - return false; - } - } else { - item = me.findItem(sdk.items.LargeCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + if (item && !Storage.Inventory.IsLocked(item, Config.Inventory)) { + D2Bot.printToConsole("AutoMule: Transfering Anni.", sdk.colors.D2Bot.DarkGold); + } else { + return false; + } + } else { + item = me.findItem(sdk.items.LargeCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); - if (item) { - D2Bot.printToConsole("AutoMule: Transfering Torch.", sdk.colors.D2Bot.DarkGold); - } else { - return false; - } - } + if (item) { + D2Bot.printToConsole("AutoMule: Transfering Torch.", sdk.colors.D2Bot.DarkGold); + } else { + return false; + } + } - item.drop(); - delay(1000); - me.cancel() && me.cancel(); + item.drop(); + delay(1000); + me.cancel() && me.cancel(); - return true; - }, + return true; + }, - // ################################## // - /* ######### Mule Functions ######### */ - // ################################## // + // ################################## // + /* ######### Mule Functions ######### */ + // ################################## // - /** + /** * @param {{ profile: string, mode: number }} info * @returns {{ profile: string, mode: number }} master info */ - getMaster: function (info) { - let muleObj = info.mode === 1 ? this.TorchAnniMules : this.Mules; - - for (let i in muleObj) { - if (muleObj.hasOwnProperty(i)) { - for (let j in muleObj[i]) { - if (muleObj[i].hasOwnProperty(j) && j === "enabledProfiles") { - for (let k = 0; k < muleObj[i][j].length; k += 1) { - if (String.isEqual(muleObj[i][j][k], info.profile)) { - return { - profile: muleObj[i][j][k], - mode: info.mode - }; - } - } - } - } - } - } - - return false; - }, - - /** + getMaster: function (info) { + let muleObj = info.mode === 1 ? this.TorchAnniMules : this.Mules; + + for (let i in muleObj) { + if (muleObj.hasOwnProperty(i)) { + for (let j in muleObj[i]) { + if (muleObj[i].hasOwnProperty(j) && j === "enabledProfiles") { + for (let k = 0; k < muleObj[i][j].length; k += 1) { + if (String.isEqual(muleObj[i][j][k], info.profile)) { + return { + profile: muleObj[i][j][k], + mode: info.mode + }; + } + } + } + } + } + } + + return false; + }, + + /** * @param {number} mode - mule mode * @param {string} master - profile that whats to mule * @param {boolean} continuous - whether we are continuous or not */ - getMuleObject: function (mode, master, continuous = false) { - mode = mode || 0; - let mule = mode > 0 ? this.TorchAnniMules : this.Mules; + getMuleObject: function (mode, master, continuous = false) { + mode = mode || 0; + let mule = mode > 0 ? this.TorchAnniMules : this.Mules; - for (let i in mule) { - if (mule.hasOwnProperty(i)) { - if (mule[i].muleProfile && mule[i].enabledProfiles && String.isEqual(mule[i].muleProfile, me.profile) + for (let i in mule) { + if (mule.hasOwnProperty(i)) { + if (mule[i].muleProfile && mule[i].enabledProfiles && String.isEqual(mule[i].muleProfile, me.profile) && (continuous || mule[i].enabledProfiles.includes(master))) { - return mule[i]; - } - } - } + return mule[i]; + } + } + } - return false; - }, + return false; + }, - /** + /** * @param {number} mode * @param {string} master * @param {boolean} continuous * @returns {string} */ - getMuleFilename: function (mode, master, continuous = false) { - mode = mode || 0; - let mule = mode > 0 ? this.TorchAnniMules : this.Mules; - let file; - - // Iterate through mule object - for (let i in mule) { - if (mule.hasOwnProperty(i)) { - // Mule profile matches config - if (mule[i].muleProfile && String.isEqual(mule[i].muleProfile, me.profile) && (continuous || mule[i].enabledProfiles.includes(master))) { - file = mode === 0 ? "logs/AutoMule." + i + ".json" : "logs/TorchMule." + i + ".json"; + getMuleFilename: function (mode, master, continuous = false) { + mode = mode || 0; + let mule = mode > 0 ? this.TorchAnniMules : this.Mules; + let file; + + // Iterate through mule object + for (let i in mule) { + if (mule.hasOwnProperty(i)) { + // Mule profile matches config + if (mule[i].muleProfile && String.isEqual(mule[i].muleProfile, me.profile) && (continuous || mule[i].enabledProfiles.includes(master))) { + file = mode === 0 ? "logs/AutoMule." + i + ".json" : "logs/TorchMule." + i + ".json"; - // If file exists check for valid info - if (FileTools.exists(file)) { - try { - let jsonStr = FileTools.readText(file); - let jsonObj = JSON.parse(jsonStr); - - // Return filename containing correct mule info - if (mule[i].accountPrefix && jsonObj.account && jsonObj.account.match(mule[i].accountPrefix)) { - return file; - } - } catch (e) { - print(e); - } - } else { - return file; - } - } - } - } - - // File exists but doesn't contain valid info - remake - FileTools.remove(file); - - return file; - }, - - /** + // If file exists check for valid info + if (FileTools.exists(file)) { + try { + let jsonStr = FileTools.readText(file); + let jsonObj = JSON.parse(jsonStr); + + // Return filename containing correct mule info + if (mule[i].accountPrefix && jsonObj.account && jsonObj.account.match(mule[i].accountPrefix)) { + return file; + } + } catch (e) { + print(e); + } + } else { + return file; + } + } + } + } + + // File exists but doesn't contain valid info - remake + FileTools.remove(file); + + return file; + }, + + /** * Get whether this is a regular mule or a torch/anni mule */ - getMuleMode: function() { - for (let i in this.Mules) { - if (this.Mules.hasOwnProperty(i)) { - if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { - return 0; - } - } - } + getMuleMode: function() { + for (let i in this.Mules) { + if (this.Mules.hasOwnProperty(i)) { + if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { + return 0; + } + } + } - for (let i in this.TorchAnniMules) { - if (this.TorchAnniMules.hasOwnProperty(i)) { - if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { - return 1; - } - } - } - - return 0; - }, - - /** + for (let i in this.TorchAnniMules) { + if (this.TorchAnniMules.hasOwnProperty(i)) { + if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { + return 1; + } + } + } + + return 0; + }, + + /** * Get whether this is a normal mule or continous mule */ - isContinousMule: function () { - for (let i in this.Mules) { - if (this.Mules.hasOwnProperty(i)) { - if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { - return this.Mules[i].continuousMule; - } - } - } + isContinousMule: function () { + for (let i in this.Mules) { + if (this.Mules.hasOwnProperty(i)) { + if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { + return this.Mules[i].continuousMule; + } + } + } - for (let i in this.TorchAnniMules) { - if (this.TorchAnniMules.hasOwnProperty(i)) { - if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { - return this.TorchAnniMules[i].continuousMule; - } - } - } - - return false; - } + for (let i in this.TorchAnniMules) { + if (this.TorchAnniMules.hasOwnProperty(i)) { + if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { + return this.TorchAnniMules[i].continuousMule; + } + } + } + + return false; + } }; diff --git a/d2bs/kolbot/libs/systems/automule/MuleConfig.js b/d2bs/kolbot/libs/systems/automule/MuleConfig.js index b5b2fe26c..7d2fdcaca 100644 --- a/d2bs/kolbot/libs/systems/automule/MuleConfig.js +++ b/d2bs/kolbot/libs/systems/automule/MuleConfig.js @@ -6,38 +6,38 @@ */ (function (module) { - module.exports = { - "Mule1": { - muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. - accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. - accountPassword: "", // Account password. - charPrefix: "", // Character prefix. Suffix added automatically when making characters. - realm: "", // Available options: "useast", "uswest", "europe", "asia" - expansion: true, - ladder: true, - hardcore: false, - charsPerAcc: 18, // Maximum number of mules to create per account (between 1 to 18) + module.exports = { + "Mule1": { + muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. + accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. + accountPassword: "", // Account password. + charPrefix: "", // Character prefix. Suffix added automatically when making characters. + realm: "", // Available options: "useast", "uswest", "europe", "asia" + expansion: true, + ladder: true, + hardcore: false, + charsPerAcc: 18, // Maximum number of mules to create per account (between 1 to 18) - // Game name and password of the mule game. Never use the same game name as for mule logger. - muleGameName: ["", ""], // ["gamename", "password"] + // Game name and password of the mule game. Never use the same game name as for mule logger. + muleGameName: ["", ""], // ["gamename", "password"] - // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], - enabledProfiles: [""], + // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], + enabledProfiles: [""], - // Stop a profile prior to muling. Useful when running 8 bots without proxies. - stopProfile: "", - stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. + // Stop a profile prior to muling. Useful when running 8 bots without proxies. + stopProfile: "", + stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. - // Trigger muling at the end of a game if used space in stash and inventory is equal to or more than given percent. - usedStashTrigger: 80, - usedInventoryTrigger: 80, + // Trigger muling at the end of a game if used space in stash and inventory is equal to or more than given percent. + usedStashTrigger: 80, + usedInventoryTrigger: 80, - // Mule items that have been stashed at some point but are no longer in pickit. - muleOrphans: true, - // Continuous Mule settings - continuousMule: false, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. - skipMuleResponse: false, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. - onlyLogWhenFull: false // Only log character when full, solves an issue with droppers attempting to use characters who are already in game - }, - }; + // Mule items that have been stashed at some point but are no longer in pickit. + muleOrphans: true, + // Continuous Mule settings + continuousMule: false, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. + skipMuleResponse: false, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. + onlyLogWhenFull: false // Only log character when full, solves an issue with droppers attempting to use characters who are already in game + }, + }; })(module); diff --git a/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js b/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js index a22d0530d..b2c117b33 100644 --- a/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js +++ b/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js @@ -14,32 +14,32 @@ * @note Each mule will hold either a Torch or an Anni, but not both. As soon as the current mule has either one, a new one will be created. */ (function (module) { - module.exports = { - "Mule1": { - muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. - accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. - accountPassword: "", // Account password. - charPrefix: "", // Character prefix. Suffix added automatically when making characters. - realm: "", // Available options: "useast", "uswest", "europe", "asia" - expansion: true, - ladder: true, - hardcore: false, - charsPerAcc: 8, // Maximum number of mules to create per account (between 1 to 18) + module.exports = { + "Mule1": { + muleProfile: "", // The name of mule profile in d2bot#. It will be started and stopped when needed. + accountPrefix: "", // Account prefix. Numbers added automatically when making accounts. + accountPassword: "", // Account password. + charPrefix: "", // Character prefix. Suffix added automatically when making characters. + realm: "", // Available options: "useast", "uswest", "europe", "asia" + expansion: true, + ladder: true, + hardcore: false, + charsPerAcc: 8, // Maximum number of mules to create per account (between 1 to 18) - // Game name and password of the mule game. Never use the same game name as for mule logger. - muleGameName: ["", ""], // ["gamename", "password"] + // Game name and password of the mule game. Never use the same game name as for mule logger. + muleGameName: ["", ""], // ["gamename", "password"] - // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], - enabledProfiles: [""], + // List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"], + enabledProfiles: [""], - // Stop a profile prior to muling. Useful when running 8 bots without proxies. - stopProfile: "", - stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. + // Stop a profile prior to muling. Useful when running 8 bots without proxies. + stopProfile: "", + stopProfileKeyRelease: false, // true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. - // Continuous Mule settings - continuousMule: true, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. - skipMuleResponse: true, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. - onlyLogWhenFull: true // Only log character when full, solves an issue with droppers attempting to use characters who are already in game - }, - }; + // Continuous Mule settings + continuousMule: true, // Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. + skipMuleResponse: true, // Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. + onlyLogWhenFull: true // Only log character when full, solves an issue with droppers attempting to use characters who are already in game + }, + }; })(module); diff --git a/d2bs/kolbot/libs/systems/crafting/CraftingSystem.js b/d2bs/kolbot/libs/systems/crafting/CraftingSystem.js index c80018ea0..0a85cd2d8 100644 --- a/d2bs/kolbot/libs/systems/crafting/CraftingSystem.js +++ b/d2bs/kolbot/libs/systems/crafting/CraftingSystem.js @@ -17,31 +17,31 @@ CraftingSystem.Teams = Object.assign({}, require("./TeamsConfig", null, false)); // Get the Crafting System information for current profile CraftingSystem.getInfo = function () { - for (let i in CraftingSystem.Teams) { - if (CraftingSystem.Teams.hasOwnProperty(i)) { - for (let j = 0; j < CraftingSystem.Teams[i].Collectors.length; j += 1) { - if (CraftingSystem.Teams[i].Collectors[j].toLowerCase() === me.profile.toLowerCase()) { - let info = CraftingSystem.Teams[i]; - info.collector = true; - info.worker = false; - - return info; - } - } - - for (let j = 0; j < CraftingSystem.Teams[i].Workers.length; j += 1) { - if (CraftingSystem.Teams[i].Workers[j].toLowerCase() === me.profile.toLowerCase()) { - let info = CraftingSystem.Teams[i]; - info.collector = false; - info.worker = true; - - return info; - } - } - } - } - - return false; + for (let i in CraftingSystem.Teams) { + if (CraftingSystem.Teams.hasOwnProperty(i)) { + for (let j = 0; j < CraftingSystem.Teams[i].Collectors.length; j += 1) { + if (CraftingSystem.Teams[i].Collectors[j].toLowerCase() === me.profile.toLowerCase()) { + let info = CraftingSystem.Teams[i]; + info.collector = true; + info.worker = false; + + return info; + } + } + + for (let j = 0; j < CraftingSystem.Teams[i].Workers.length; j += 1) { + if (CraftingSystem.Teams[i].Workers[j].toLowerCase() === me.profile.toLowerCase()) { + let info = CraftingSystem.Teams[i]; + info.collector = false; + info.worker = true; + + return info; + } + } + } + } + + return false; }; // ################################################# @@ -52,99 +52,99 @@ CraftingSystem.check = false; CraftingSystem.inGame = false; CraftingSystem.outOfGameCheck = function () { - if (!CraftingSystem.check) return false; + if (!CraftingSystem.check) return false; - let info = CraftingSystem.getInfo(); + let info = CraftingSystem.getInfo(); - function scriptMsg(msg) { - let obj; + function scriptMsg(msg) { + let obj; - try { - obj = JSON.parse(msg); - } catch (e) { - return false; - } + try { + obj = JSON.parse(msg); + } catch (e) { + return false; + } - obj.name === "RequestWorker" && scriptBroadcast(JSON.stringify({name: "WorkerName", value: worker.name})); + obj.name === "RequestWorker" && scriptBroadcast(JSON.stringify({ name: "WorkerName", value: worker.name })); - return true; - } + return true; + } - if (info && info.collector) { - let worker = CraftingSystem.getWorker(); + if (info && info.collector) { + let worker = CraftingSystem.getWorker(); - if (worker && worker.game) { - D2Bot.printToConsole("CraftingSystem: Transfering items.", sdk.colors.D2Bot.DarkGold); - D2Bot.updateStatus("CraftingSystem: In game."); - addEventListener("scriptmsg", scriptMsg); + if (worker && worker.game) { + D2Bot.printToConsole("CraftingSystem: Transfering items.", sdk.colors.D2Bot.DarkGold); + D2Bot.updateStatus("CraftingSystem: In game."); + addEventListener("scriptmsg", scriptMsg); - CraftingSystem.inGame = true; - me.blockMouse = true; + CraftingSystem.inGame = true; + me.blockMouse = true; - delay(2000); - joinGame(worker.game[0], worker.game[1]); + delay(2000); + joinGame(worker.game[0], worker.game[1]); - me.blockMouse = false; + me.blockMouse = false; - delay(5000); + delay(5000); - while (me.ingame) { - delay(1000); - } + while (me.ingame) { + delay(1000); + } - CraftingSystem.inGame = false; - CraftingSystem.check = false; + CraftingSystem.inGame = false; + CraftingSystem.check = false; - removeEventListener("scriptmsg", scriptMsg); + removeEventListener("scriptmsg", scriptMsg); - return true; - } - } + return true; + } + } - return false; + return false; }; CraftingSystem.getWorker = function () { - let rval = { - game: false, - name: false - }; - let info = CraftingSystem.getInfo(); + let rval = { + game: false, + name: false + }; + let info = CraftingSystem.getInfo(); - function checkEvent(mode, msg) { - if (mode === 4) { - for (let i = 0; i < info.CraftingGames.length; i += 1) { - if (info.CraftingGames[i] && msg.match(info.CraftingGames[i], "i")) { - rval.game = msg.split("/"); + function checkEvent(mode, msg) { + if (mode === 4) { + for (let i = 0; i < info.CraftingGames.length; i += 1) { + if (info.CraftingGames[i] && msg.match(info.CraftingGames[i], "i")) { + rval.game = msg.split("/"); - break; - } - } - } - } + break; + } + } + } + } - if (info && info.collector) { - addEventListener("copydata", checkEvent); + if (info && info.collector) { + addEventListener("copydata", checkEvent); - rval.game = false; + rval.game = false; - for (let i = 0; i < info.Workers.length; i += 1) { - sendCopyData(null, info.Workers[i], 0, JSON.stringify({name: "GetGame", profile: me.profile})); - delay(100); + for (let i = 0; i < info.Workers.length; i += 1) { + sendCopyData(null, info.Workers[i], 0, JSON.stringify({ name: "GetGame", profile: me.profile })); + delay(100); - if (rval.game) { - rval.name = info.Workers[i]; + if (rval.game) { + rval.name = info.Workers[i]; - break; - } - } + break; + } + } - removeEventListener("copydata", checkEvent); + removeEventListener("copydata", checkEvent); - return rval; - } + return rval; + } - return false; + return false; }; // ############################################# @@ -152,22 +152,22 @@ CraftingSystem.getWorker = function () { // ############################################# CraftingSystem.inGameCheck = function () { - let info = CraftingSystem.getInfo(); - - if (info && info.collector) { - for (let i = 0; i < info.CraftingGames.length; i += 1) { - if (info.CraftingGames[i] && me.gamename.match(info.CraftingGames[i], "i")) { - CraftingSystem.dropItems(); - me.cancel(); - delay(5000); - quit(); - - return true; - } - } - } - - return false; + let info = CraftingSystem.getInfo(); + + if (info && info.collector) { + for (let i = 0; i < info.CraftingGames.length; i += 1) { + if (info.CraftingGames[i] && me.gamename.match(info.CraftingGames[i], "i")) { + CraftingSystem.dropItems(); + me.cancel(); + delay(5000); + quit(); + + return true; + } + } + } + + return false; }; CraftingSystem.neededItems = []; @@ -177,277 +177,281 @@ CraftingSystem.fullSets = []; // Check whether item can be used for crafting CraftingSystem.validItem = function (item) { - switch (item.itemType) { - case sdk.items.type.Jewel: - // Use junk jewels only - return NTIP.CheckItem(item) === Pickit.Result.UNWANTED; - } + switch (item.itemType) { + case sdk.items.type.Jewel: + // Use junk jewels only + return NTIP.CheckItem(item) === Pickit.Result.UNWANTED; + } - return true; + return true; }; // Check if the item should be picked for crafting CraftingSystem.checkItem = function (item) { - let info = CraftingSystem.getInfo(); + let info = CraftingSystem.getInfo(); - if (info) { - for (let i = 0; i < CraftingSystem.neededItems.length; i += 1) { - if (item.classid === CraftingSystem.neededItems[i] && CraftingSystem.validItem(item)) { - return true; - } - } - } + if (info) { + for (let i = 0; i < CraftingSystem.neededItems.length; i += 1) { + if (item.classid === CraftingSystem.neededItems[i] && CraftingSystem.validItem(item)) { + return true; + } + } + } - return false; + return false; }; // Check if the item should be kept or dropped CraftingSystem.keepItem = function (item) { - let info = CraftingSystem.getInfo(); + let info = CraftingSystem.getInfo(); - if (info) { - if (info.collector) return CraftingSystem.validGids.includes(item.gid); + if (info) { + if (info.collector) return CraftingSystem.validGids.includes(item.gid); - if (info.worker) { - // Let pickit decide whether to keep crafted - return item.crafted ? false : true; - } - } + if (info.worker) { + // Let pickit decide whether to keep crafted + return item.crafted ? false : true; + } + } - return false; + return false; }; // Collect ingredients only if a worker needs them CraftingSystem.getSetInfoFromWorker = function (workerName) { - let setInfo = false; - let info = CraftingSystem.getInfo(); + let setInfo = false; + let info = CraftingSystem.getInfo(); - function copyData(mode, msg) { - let obj; + function copyData(mode, msg) { + let obj; - if (mode === 4) { - try { - obj = JSON.parse(msg); - } catch (e) { - return false; - } + if (mode === 4) { + try { + obj = JSON.parse(msg); + } catch (e) { + return false; + } - obj && obj.name === "SetInfo" && (setInfo = obj.value); - } + obj && obj.name === "SetInfo" && (setInfo = obj.value); + } - return true; - } + return true; + } - if (info && info.collector) { - addEventListener("copydata", copyData); - sendCopyData(null, workerName, 0, JSON.stringify({name: "GetSetInfo", profile: me.profile})); - delay(100); + if (info && info.collector) { + addEventListener("copydata", copyData); + sendCopyData(null, workerName, 0, JSON.stringify({ name: "GetSetInfo", profile: me.profile })); + delay(100); - if (setInfo !== false) { - removeEventListener("copydata", copyData); + if (setInfo !== false) { + removeEventListener("copydata", copyData); - return setInfo; - } + return setInfo; + } - removeEventListener("copydata", copyData); - } + removeEventListener("copydata", copyData); + } - return false; + return false; }; CraftingSystem.init = function (name) { - let info = CraftingSystem.getInfo(); - - if (info && info.collector) { - for (let i = 0; i < info.Sets.length; i += 1) { - info.Sets[i].Enabled = false; - } - - let setInfo = CraftingSystem.getSetInfoFromWorker(name); - - if (setInfo) { - for (let i = 0; i < setInfo.length; i += 1) { - if (setInfo[i] === 1 && info.Sets[i].Enabled === false) { - info.Sets[i].Enabled = true; - } - } - } - } + let info = CraftingSystem.getInfo(); + + if (info && info.collector) { + for (let i = 0; i < info.Sets.length; i += 1) { + info.Sets[i].Enabled = false; + } + + let setInfo = CraftingSystem.getSetInfoFromWorker(name); + + if (setInfo) { + for (let i = 0; i < setInfo.length; i += 1) { + if (setInfo[i] === 1 && info.Sets[i].Enabled === false) { + info.Sets[i].Enabled = true; + } + } + } + } }; // Build global lists of needed items and valid ingredients CraftingSystem.buildLists = function (onlyNeeded) { - let info = CraftingSystem.getInfo(); + let info = CraftingSystem.getInfo(); - if (info && info.collector) { - CraftingSystem.neededItems = []; - CraftingSystem.validGids = []; - CraftingSystem.fullSets = []; - CraftingSystem.itemList = me.findItems(-1, sdk.items.mode.inStorage); + if (info && info.collector) { + CraftingSystem.neededItems = []; + CraftingSystem.validGids = []; + CraftingSystem.fullSets = []; + CraftingSystem.itemList = me.findItems(-1, sdk.items.mode.inStorage); - for (let i = 0; i < info.Sets.length; i += 1) { - if (!onlyNeeded || info.Sets[i].Enabled) { - CraftingSystem.checkSet(info.Sets[i]); - } - } + for (let i = 0; i < info.Sets.length; i += 1) { + if (!onlyNeeded || info.Sets[i].Enabled) { + CraftingSystem.checkSet(info.Sets[i]); + } + } - return true; - } + return true; + } - return false; + return false; }; // Check which ingredients a set needs and has CraftingSystem.checkSet = function (set) { - let rval = {}; - let setNeeds = []; - let setHas = []; - - // Get what set needs - // Multiply by SetAmount - for (let amount = 0; amount < set.SetAmount; amount += 1) { - for (let i = 0; i < set.Ingredients.length; i += 1) { - setNeeds.push(set.Ingredients[i]); - } - } - - // Remove what set already has - for (let i = 0; i < setNeeds.length; i += 1) { - for (let j = 0; j < CraftingSystem.itemList.length; j += 1) { - if (CraftingSystem.itemList[j].classid === setNeeds[i]) { - setHas.push(CraftingSystem.itemList[j].gid); - setNeeds.splice(i, 1); - CraftingSystem.itemList.splice(j, 1); - - i -= 1; - j -= 1; - } - } - } - - // The set is complete - setNeeds.length === 0 && CraftingSystem.fullSets.push(setHas.slice()); - - CraftingSystem.neededItems = CraftingSystem.neededItems.concat(setNeeds); - CraftingSystem.validGids = CraftingSystem.validGids.concat(setHas); - - CraftingSystem.neededItems.sort(Sort.numbers); - CraftingSystem.validGids.sort(Sort.numbers); - - return rval; + let rval = {}; + let setNeeds = []; + let setHas = []; + + // Get what set needs + // Multiply by SetAmount + for (let amount = 0; amount < set.SetAmount; amount += 1) { + for (let i = 0; i < set.Ingredients.length; i += 1) { + setNeeds.push(set.Ingredients[i]); + } + } + + // Remove what set already has + for (let i = 0; i < setNeeds.length; i += 1) { + for (let j = 0; j < CraftingSystem.itemList.length; j += 1) { + if (CraftingSystem.itemList[j].classid === setNeeds[i]) { + setHas.push(CraftingSystem.itemList[j].gid); + setNeeds.splice(i, 1); + CraftingSystem.itemList.splice(j, 1); + + i -= 1; + j -= 1; + } + } + } + + // The set is complete + setNeeds.length === 0 && CraftingSystem.fullSets.push(setHas.slice()); + + CraftingSystem.neededItems = CraftingSystem.neededItems.concat(setNeeds); + CraftingSystem.validGids = CraftingSystem.validGids.concat(setHas); + + CraftingSystem.neededItems.sort(Sort.numbers); + CraftingSystem.validGids.sort(Sort.numbers); + + return rval; }; // Update lists when a valid ingredient is picked CraftingSystem.update = function (item) { - CraftingSystem.neededItems.splice(CraftingSystem.neededItems.indexOf(item.classid), 1); - CraftingSystem.validGids.push(item.gid); + CraftingSystem.neededItems.splice(CraftingSystem.neededItems.indexOf(item.classid), 1); + CraftingSystem.validGids.push(item.gid); - return true; + return true; }; // Cube flawless gems if the ingredient is a perfect gem CraftingSystem.checkSubrecipes = function () { - for (let i = 0; i < CraftingSystem.neededItems.length; i += 1) { - switch (CraftingSystem.neededItems[i]) { - case sdk.items.gems.Perfect.Amethyst: - case sdk.items.gems.Perfect.Topaz: - case sdk.items.gems.Perfect.Sapphire: - case sdk.items.gems.Perfect.Emerald: - case sdk.items.gems.Perfect.Ruby: - case sdk.items.gems.Perfect.Diamond: - case sdk.items.gems.Perfect.Skull: - if (Cubing.subRecipes.indexOf(CraftingSystem.neededItems[i]) === -1) { - Cubing.subRecipes.push(CraftingSystem.neededItems[i]); - Cubing.recipes.push({ - Ingredients: [CraftingSystem.neededItems[i] - 1, CraftingSystem.neededItems[i] - 1, CraftingSystem.neededItems[i] - 1], - Index: 0, - AlwaysEnabled: true, - MainRecipe: "Crafting" - }); - } - - break; - } - } - - return true; + for (let i = 0; i < CraftingSystem.neededItems.length; i += 1) { + switch (CraftingSystem.neededItems[i]) { + case sdk.items.gems.Perfect.Amethyst: + case sdk.items.gems.Perfect.Topaz: + case sdk.items.gems.Perfect.Sapphire: + case sdk.items.gems.Perfect.Emerald: + case sdk.items.gems.Perfect.Ruby: + case sdk.items.gems.Perfect.Diamond: + case sdk.items.gems.Perfect.Skull: + if (Cubing.subRecipes.indexOf(CraftingSystem.neededItems[i]) === -1) { + Cubing.subRecipes.push(CraftingSystem.neededItems[i]); + Cubing.recipes.push({ + Ingredients: [ + CraftingSystem.neededItems[i] - 1, + CraftingSystem.neededItems[i] - 1, + CraftingSystem.neededItems[i] - 1 + ], + Index: 0, + AlwaysEnabled: true, + MainRecipe: "Crafting" + }); + } + + break; + } + } + + return true; }; // Check if there are any complete ingredient sets CraftingSystem.checkFullSets = function () { - let info = CraftingSystem.getInfo(); + let info = CraftingSystem.getInfo(); - if (info && info.collector) { - for (let i = 0; i < info.Workers.length; i += 1) { - CraftingSystem.init(info.Workers[i]); - CraftingSystem.buildLists(true); + if (info && info.collector) { + for (let i = 0; i < info.Workers.length; i += 1) { + CraftingSystem.init(info.Workers[i]); + CraftingSystem.buildLists(true); - if (CraftingSystem.fullSets.length) { - return true; - } - } - } + if (CraftingSystem.fullSets.length) { + return true; + } + } + } - return false; + return false; }; // Drop complete ingredient sets CraftingSystem.dropItems = function () { - Town.goToTown(1); - Town.move("stash"); - Town.openStash(); + Town.goToTown(1); + Town.move("stash"); + Town.openStash(); - let worker; + let worker; - function scriptMsg(msg) { - let obj; + function scriptMsg(msg) { + let obj; - try { - obj = JSON.parse(msg); - } catch (e) { - return false; - } + try { + obj = JSON.parse(msg); + } catch (e) { + return false; + } - !!obj && obj.name === "WorkerName" && (worker = obj.value); + !!obj && obj.name === "WorkerName" && (worker = obj.value); - return true; - } + return true; + } - addEventListener("scriptmsg", scriptMsg); - scriptBroadcast(JSON.stringify({name: "RequestWorker"})); - delay(100); + addEventListener("scriptmsg", scriptMsg); + scriptBroadcast(JSON.stringify({ name: "RequestWorker" })); + delay(100); - if (worker) { - CraftingSystem.init(worker); - CraftingSystem.buildLists(true); - removeEventListener("scriptmsg", scriptMsg); + if (worker) { + CraftingSystem.init(worker); + CraftingSystem.buildLists(true); + removeEventListener("scriptmsg", scriptMsg); - while (CraftingSystem.fullSets.length) { - let gidList = CraftingSystem.fullSets.shift(); + while (CraftingSystem.fullSets.length) { + let gidList = CraftingSystem.fullSets.shift(); - while (gidList.length) { - let item = me.getItem(-1, -1, gidList.shift()); - !!item && item.drop(); - } - } + while (gidList.length) { + let item = me.getItem(-1, -1, gidList.shift()); + !!item && item.drop(); + } + } - CraftingSystem.dropGold(); - delay(1000); - me.cancel(); - } + CraftingSystem.dropGold(); + delay(1000); + me.cancel(); + } - return true; + return true; }; CraftingSystem.dropGold = function () { - Town.goToTown(1); - Town.move("stash"); - - if (me.getStat(sdk.stats.Gold) >= 10000) { - gold(10000); - } else if (me.getStat(sdk.stats.GoldBank) + me.getStat(sdk.stats.Gold) >= 10000) { - Town.openStash(); - gold(10000 - me.getStat(sdk.stats.Gold), 4); - gold(10000); - } + Town.goToTown(1); + Town.move("stash"); + + if (me.getStat(sdk.stats.Gold) >= 10000) { + gold(10000); + } else if (me.getStat(sdk.stats.GoldBank) + me.getStat(sdk.stats.Gold) >= 10000) { + Town.openStash(); + gold(10000 - me.getStat(sdk.stats.Gold), 4); + gold(10000); + } }; diff --git a/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js b/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js index 2942c4983..a75ee6f49 100644 --- a/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js +++ b/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js @@ -6,40 +6,40 @@ */ (function (module) { - module.exports = { - "Team 1": { - // List of profiles that will collect ingredients - Collectors: [], + module.exports = { + "Team 1": { + // List of profiles that will collect ingredients + Collectors: [], - // List of profiles that will craft/reroll items - Workers: [], + // List of profiles that will craft/reroll items + Workers: [], - // List of Worker game names (without the numbers) - CraftingGames: [], + // List of Worker game names (without the numbers) + CraftingGames: [], - /* BaseItems - list of base item class ids + /* BaseItems - list of base item class ids * Ingredients - list of recipe ingredients * SetAmount - number of full sets to gather before transfering * Type - the type of recipe. Available options: "crafting", "runewords", "cubing" */ - Sets: [ - // LLD Crafting + Sets: [ + // LLD Crafting - // Caster Belt set, char lvl 29 - // Light Belt classid 345, shopped at nightmare Elzix - // Sharkskin Belt classid 391, shopped at nightmare Elzix - //{BaseItems: [345, 391], Ingredients: [615, 643, 561], SetAmount: 2, Type: "crafting"}, + // Caster Belt set, char lvl 29 + // Light Belt classid 345, shopped at nightmare Elzix + // Sharkskin Belt classid 391, shopped at nightmare Elzix + //{BaseItems: [345, 391], Ingredients: [615, 643, 561], SetAmount: 2, Type: "crafting"}, - // Runeword Making + // Runeword Making - // Spirit Runeset + Hel - //{BaseItems: [29, 30, 31], Ingredients: [616, 618, 619, 620, 624], SetAmount: 2, Type: "runewords"}, + // Spirit Runeset + Hel + //{BaseItems: [29, 30, 31], Ingredients: [616, 618, 619, 620, 624], SetAmount: 2, Type: "runewords"}, - // Misc. Cubing + // Misc. Cubing - // Reroll rare Diadem - //{BaseItems: [421], Ingredients: [601, 601, 601], SetAmount: 1, Type: "cubing"} - ] - }, - }; + // Reroll rare Diadem + //{BaseItems: [421], Ingredients: [601, 601, 601], SetAmount: 1, Type: "cubing"} + ] + }, + }; })(module); diff --git a/d2bs/kolbot/libs/systems/gambling/Gambling.js b/d2bs/kolbot/libs/systems/gambling/Gambling.js index fa769230c..8b5e38bbf 100644 --- a/d2bs/kolbot/libs/systems/gambling/Gambling.js +++ b/d2bs/kolbot/libs/systems/gambling/Gambling.js @@ -10,150 +10,150 @@ */ const Gambling = { - // load configuration file - Teams: Object.assign({}, require("./TeamsConfig", null, false)), + // load configuration file + Teams: Object.assign({}, require("./TeamsConfig", null, false)), - inGame: false, + inGame: false, - getInfo: function (profile) { - !profile && (profile = me.profile); + getInfo: function (profile) { + !profile && (profile = me.profile); - for (let i in this.Teams) { - if (this.Teams.hasOwnProperty(i)) { - for (let j = 0; j < this.Teams[i].goldFinders.length; j += 1) { - if (this.Teams[i].goldFinders[j].toLowerCase() === profile.toLowerCase()) { - this.Teams[i].goldFinder = true; - this.Teams[i].gambler = false; + for (let i in this.Teams) { + if (this.Teams.hasOwnProperty(i)) { + for (let j = 0; j < this.Teams[i].goldFinders.length; j += 1) { + if (this.Teams[i].goldFinders[j].toLowerCase() === profile.toLowerCase()) { + this.Teams[i].goldFinder = true; + this.Teams[i].gambler = false; - return this.Teams[i]; - } - } + return this.Teams[i]; + } + } - for (let j = 0; j < this.Teams[i].gamblers.length; j += 1) { - if (this.Teams[i].gamblers[j].toLowerCase() === profile.toLowerCase()) { - this.Teams[i].goldFinder = false; - this.Teams[i].gambler = true; + for (let j = 0; j < this.Teams[i].gamblers.length; j += 1) { + if (this.Teams[i].gamblers[j].toLowerCase() === profile.toLowerCase()) { + this.Teams[i].goldFinder = false; + this.Teams[i].gambler = true; - return this.Teams[i]; - } - } - } - } + return this.Teams[i]; + } + } + } + } - return false; - }, + return false; + }, - inGameCheck: function () { - let info = this.getInfo(); + inGameCheck: function () { + let info = this.getInfo(); - if (info && info.goldFinder) { - for (let i = 0; i < info.gambleGames.length; i += 1) { - if (info.gambleGames[i] && me.gamename.match(info.gambleGames[i], "i")) { - this.dropGold(); - DataFile.updateStats("gold"); - delay(5000); - quit(); + if (info && info.goldFinder) { + for (let i = 0; i < info.gambleGames.length; i += 1) { + if (info.gambleGames[i] && me.gamename.match(info.gambleGames[i], "i")) { + this.dropGold(); + DataFile.updateStats("gold"); + delay(5000); + quit(); - return true; - } - } - } + return true; + } + } + } - return false; - }, + return false; + }, - dropGold: function () { - let info = this.getInfo(); + dropGold: function () { + let info = this.getInfo(); - if (info && info.goldFinder) { - Town.goToTown(1); - Town.move("stash"); + if (info && info.goldFinder) { + Town.goToTown(1); + Town.move("stash"); - while (me.getStat(sdk.stats.Gold) + me.getStat(sdk.stats.GoldBank) > info.goldReserve) { - gold(me.getStat(sdk.stats.Gold)); // drop current gold - Town.openStash(); + while (me.getStat(sdk.stats.Gold) + me.getStat(sdk.stats.GoldBank) > info.goldReserve) { + gold(me.getStat(sdk.stats.Gold)); // drop current gold + Town.openStash(); - // check stashed gold vs max carrying capacity - if (me.getStat(sdk.stats.GoldBank) <= me.getStat(sdk.stats.Level) * 1e4) { - // leave minGold in stash, pick the rest - gold(me.getStat(sdk.stats.GoldBank) - info.goldReserve, 4); - } else { - // pick max carrying capacity - gold(me.getStat(sdk.stats.Level) * 1e4, 4); - } + // check stashed gold vs max carrying capacity + if (me.getStat(sdk.stats.GoldBank) <= me.getStat(sdk.stats.Level) * 1e4) { + // leave minGold in stash, pick the rest + gold(me.getStat(sdk.stats.GoldBank) - info.goldReserve, 4); + } else { + // pick max carrying capacity + gold(me.getStat(sdk.stats.Level) * 1e4, 4); + } - delay(1000); - } - } - }, + delay(1000); + } + } + }, - outOfGameCheck: function () { - let info = this.getInfo(); + outOfGameCheck: function () { + let info = this.getInfo(); - if (info && info.goldFinder && DataFile.getStats().gold >= info.goldTrigger) { - let game = this.getGame(); + if (info && info.goldFinder && DataFile.getStats().gold >= info.goldTrigger) { + let game = this.getGame(); - if (game) { - D2Bot.printToConsole("Joining gold drop game.", sdk.colors.D2Bot.DarkGold); + if (game) { + D2Bot.printToConsole("Joining gold drop game.", sdk.colors.D2Bot.DarkGold); - this.inGame = true; - me.blockMouse = true; + this.inGame = true; + me.blockMouse = true; - delay(2000); - joinGame(game[0], game[1]); + delay(2000); + joinGame(game[0], game[1]); - me.blockMouse = false; + me.blockMouse = false; - delay(5000); + delay(5000); - while (me.ingame) { - delay(1000); - } + while (me.ingame) { + delay(1000); + } - this.inGame = false; + this.inGame = false; - return true; - } - } + return true; + } + } - return false; - }, + return false; + }, - getGame: function () { - let game; - let info = this.getInfo(); + getGame: function () { + let game; + let info = this.getInfo(); - if (!info || !info.goldFinder) { - return false; - } + if (!info || !info.goldFinder) { + return false; + } - function checkEvent(mode, msg) { - if (mode === 4) { - for (let i = 0; i < info.gambleGames.length; i += 1) { - if (info.gambleGames[i] && msg.match(info.gambleGames[i], "i")) { - game = msg.split("/"); + function checkEvent(mode, msg) { + if (mode === 4) { + for (let i = 0; i < info.gambleGames.length; i += 1) { + if (info.gambleGames[i] && msg.match(info.gambleGames[i], "i")) { + game = msg.split("/"); - break; - } - } - } - } + break; + } + } + } + } - addEventListener("copydata", checkEvent); + addEventListener("copydata", checkEvent); - game = null; + game = null; - for (let i = 0; i < info.gamblers.length; i += 1) { - sendCopyData(null, info.gamblers[i], 0, me.profile); - delay(100); + for (let i = 0; i < info.gamblers.length; i += 1) { + sendCopyData(null, info.gamblers[i], 0, me.profile); + delay(100); - if (game) { - break; - } - } + if (game) { + break; + } + } - removeEventListener("copydata", checkEvent); + removeEventListener("copydata", checkEvent); - return game; - } + return game; + } }; diff --git a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js index 02846546e..ff9f13409 100644 --- a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js +++ b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js @@ -6,8 +6,8 @@ */ (function (module) { - module.exports = { - /** + module.exports = { + /** Setting up: "Gamble Team 1": { // Put a unique team name here. @@ -25,13 +25,13 @@ Once set up properly, the gold finders will run their own games and join gamblers' games when they're out of gold. */ - "Gamble Team 1": { - goldFinders: [""], - gamblers: [""], - gambleGames: [""], - - goldTrigger: 2500000, - goldReserve: 200000 - }, - }; + "Gamble Team 1": { + goldFinders: [""], + gamblers: [""], + gambleGames: [""], + + goldTrigger: 2500000, + goldReserve: 200000 + }, + }; })(module); diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js index 0eac8344a..63eb3013d 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -8,198 +8,201 @@ include("systems/mulelogger/MuleLogger.js"); const GameAction = { - // keeping with the general structure changes this section should probably be in its own config file - // but its not a lot so does it really need to be? - LogNames: true, // Put account/character name on the picture - LogItemLevel: true, // Add item level to the picture - LogEquipped: false, // include equipped items - LogMerc: false, // include items merc has equipped (if alive) - SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) - IngameTime: 60, // Time to wait before leaving game - - task: null, - // don't edit - init: function (task) { - GameAction.task = JSON.parse(task); - - if (this.task["data"] && typeof this.task.data === "string") { - this.task.data = JSON.parse(this.task.data); - } - - MuleLogger.LogNames = this.LogNames; - MuleLogger.LogItemLevel = this.LogItemLevel; - MuleLogger.LogEquipped = this.LogEquipped; - MuleLogger.LogMerc = this.LogMerc; - MuleLogger.SaveScreenShot = this.SaveScreenShot; - - return true; - }, - - update: function (action, data) { - if (typeof action !== "string") throw new Error("Action must be a string!"); + // keeping with the general structure changes this section should probably be in its own config file + // but its not a lot so does it really need to be? + LogNames: true, // Put account/character name on the picture + LogItemLevel: true, // Add item level to the picture + LogEquipped: false, // include equipped items + LogMerc: false, // include items merc has equipped (if alive) + SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) + IngameTime: 60, // Time to wait before leaving game + + task: null, + // don't edit + init: function (task) { + GameAction.task = JSON.parse(task); + + if (this.task["data"] && typeof this.task.data === "string") { + this.task.data = JSON.parse(this.task.data); + } + + MuleLogger.LogNames = this.LogNames; + MuleLogger.LogItemLevel = this.LogItemLevel; + MuleLogger.LogEquipped = this.LogEquipped; + MuleLogger.LogMerc = this.LogMerc; + MuleLogger.SaveScreenShot = this.SaveScreenShot; + + return true; + }, + + update: function (action, data) { + if (typeof action !== "string") throw new Error("Action must be a string!"); - typeof data !== "string" && (data = JSON.stringify(data)); - - D2Bot.printToConsole(data); - - let tag = JSON.parse(JSON.stringify(this.task)); // deep copy - tag.action = action; - tag.data = data; - D2Bot.setTag(tag); - }, - - gameInfo: function () { - let gi = { gameName: null, gamePass: null }; - - switch (this.task.action) { - case "doMule": - gi = null; - - break; // create random game - case "doDrop": - gi.gameName = this.task.data.gameName; - gi.gamePass = this.task.data.gamePass; - - break; // join game - default: - gi = null; - - break; - } - - return gi; - }, - - getLogin: function () { - let li = { realm: null, account: null, password: null }; - - (this.task && this.task.data) && (li.password = this.load(this.task.hash)); - - // drop specific object - if (this.task.data["items"] && this.task.data.items.length > 0) { - li.realm = this.task.data.items[0].realm; - li.account = this.task.data.items[0].account; - } + typeof data !== "string" && (data = JSON.stringify(data)); + + D2Bot.printToConsole(data); + + let tag = JSON.parse(JSON.stringify(this.task)); // deep copy + tag.action = action; + tag.data = data; + D2Bot.setTag(tag); + }, + + gameInfo: function () { + let gi = { gameName: null, gamePass: null }; + + switch (this.task.action) { + case "doMule": + gi = null; + + break; // create random game + case "doDrop": + gi.gameName = this.task.data.gameName; + gi.gamePass = this.task.data.gamePass; + + break; // join game + default: + gi = null; + + break; + } + + return gi; + }, + + getLogin: function () { + let li = { realm: null, account: null, password: null }; + + (this.task && this.task.data) && (li.password = this.load(this.task.hash)); + + // drop specific object + if (this.task.data["items"] && this.task.data.items.length > 0) { + li.realm = this.task.data.items[0].realm; + li.account = this.task.data.items[0].account; + } - // mule log specific objects - this.task.data["realm"] && (li.realm = this.task.data.realm); - this.task.data["account"] && (li.account = this.task.data.account); + // mule log specific objects + this.task.data["realm"] && (li.realm = this.task.data.realm); + this.task.data["account"] && (li.account = this.task.data.account); - if (!li.password || !li.account || !li.realm) { - this.update("done", "Realm, Account, or Password was invalid!"); - D2Bot.stop(); - delay(500); - } - - return li; - }, - - getCharacters: function () { - let chars = []; + if (!li.password || !li.account || !li.realm) { + this.update("done", "Realm, Account, or Password was invalid!"); + D2Bot.stop(); + delay(500); + } + + return li; + }, + + getCharacters: function () { + let chars = []; - // drop specific object - if (this.task.data["items"]) { - for (let i = 0; i < this.task.data.items.length; i += 1) { - if (chars.indexOf(this.task.data.items[i].character) === -1) { - chars.push(this.task.data.items[i].character); - } - } - } - - // mule log specific object - this.task.data["chars"] && (chars = this.task.data.chars); - - return chars; - }, - - inGameCheck: function () { - if (getScript("D2BotGameAction.dbj")) { - while (!this["task"]) { - D2Bot.getProfile(); - delay(500); - } - - switch (this.task.action) { - case "doMule": - MuleLogger.logChar(); - - break; - case "doDrop": - this.dropItems(this.task.data.items); - MuleLogger.logChar(); - - break; - default: - break; - } - - while ((getTickCount() - me.gamestarttime) < this.IngameTime * 1000) { - delay(1000); - } - - try { - quit(); - } finally { - while (me.ingame) { - delay(100); - } - } - - return true; - } - - return false; - }, - - load: function (hash) { - let filename = "data/secure/" + hash + ".txt"; - - if (!FileTools.exists(filename)) { - this.update("done", "File " + filename + " does not exist!"); - D2Bot.stop(); - delay(5000); - quitGame(); // pretty sure quitGame crashes? - } - - return FileTools.readText(filename); - }, - - save: function (hash, data) { - let filename = "data/secure/" + hash + ".txt"; - FileTools.writeText(filename, data); - }, - - dropItems: function (droplist) { - if (!droplist) return; - - while (!me.gameReady) { - delay(100); - } - - let items = me.getItemsEx(); - - if (!items || !items.length) return; - - for (let i = 0; i < droplist.length; i += 1) { - if (droplist[i].character !== me.charname) { - continue; - } - - let info = droplist[i].itemid.split(":"); //":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y; - - let classid = info[1]; - let loc = info[2]; - let unitX = info[3]; - let unitY = info[4]; - - // for debug purposes - print("classid: " + classid + " location: " + loc + " X: " + unitX + " Y: " + unitY); - - for (let j = 0; j < items.length; j += 1) { - if (items[j].classid.toString() === classid && items[j].location.toString() === loc && items[j].x.toString() === unitX && items[j].y.toString() === unitY) { - items[j].drop(); - } - } - } - }, + // drop specific object + if (this.task.data["items"]) { + for (let i = 0; i < this.task.data.items.length; i += 1) { + if (chars.indexOf(this.task.data.items[i].character) === -1) { + chars.push(this.task.data.items[i].character); + } + } + } + + // mule log specific object + this.task.data["chars"] && (chars = this.task.data.chars); + + return chars; + }, + + inGameCheck: function () { + if (getScript("D2BotGameAction.dbj")) { + while (!this["task"]) { + D2Bot.getProfile(); + delay(500); + } + + switch (this.task.action) { + case "doMule": + MuleLogger.logChar(); + + break; + case "doDrop": + this.dropItems(this.task.data.items); + MuleLogger.logChar(); + + break; + default: + break; + } + + while ((getTickCount() - me.gamestarttime) < this.IngameTime * 1000) { + delay(1000); + } + + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } + + return true; + } + + return false; + }, + + load: function (hash) { + let filename = "data/secure/" + hash + ".txt"; + + if (!FileTools.exists(filename)) { + this.update("done", "File " + filename + " does not exist!"); + D2Bot.stop(); + delay(5000); + quitGame(); // pretty sure quitGame crashes? + } + + return FileTools.readText(filename); + }, + + save: function (hash, data) { + let filename = "data/secure/" + hash + ".txt"; + FileTools.writeText(filename, data); + }, + + dropItems: function (droplist) { + if (!droplist) return; + + while (!me.gameReady) { + delay(100); + } + + let items = me.getItemsEx(); + + if (!items || !items.length) return; + + for (let i = 0; i < droplist.length; i += 1) { + if (droplist[i].character !== me.charname) { + continue; + } + + let info = droplist[i].itemid.split(":"); //":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y; + + let classid = info[1]; + let loc = info[2]; + let unitX = info[3]; + let unitY = info[4]; + + // for debug purposes + print("classid: " + classid + " location: " + loc + " X: " + unitX + " Y: " + unitY); + + for (let j = 0; j < items.length; j += 1) { + if (items[j].classid.toString() === classid + && items[j].location.toString() === loc + && items[j].x.toString() === unitX + && items[j].y.toString() === unitY) { + items[j].drop(); + } + } + } + }, }; diff --git a/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js b/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js index b63ba1590..3f664b55b 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js +++ b/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js @@ -6,19 +6,19 @@ */ (function (module) { - module.exports = { - LogGame: ["", ""], // ["gamename", "password"] - LogNames: true, // Put account/character name on the picture - LogItemLevel: true, // Add item level to the picture - LogEquipped: true, // include equipped items - LogMerc: true, // include items merc has equipped (if alive) - SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) - AutoPerm: true, // override InGameTime to perm character - IngameTime: rand(60, 120), // (180, 210) to avoid RD, increase it to (7230, 7290) for mule perming + module.exports = { + LogGame: ["", ""], // ["gamename", "password"] + LogNames: true, // Put account/character name on the picture + LogItemLevel: true, // Add item level to the picture + LogEquipped: true, // include equipped items + LogMerc: true, // include items merc has equipped (if alive) + SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) + AutoPerm: true, // override InGameTime to perm character + IngameTime: rand(60, 120), // (180, 210) to avoid RD, increase it to (7230, 7290) for mule perming - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - LogAccounts: { - /* Format: + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + LogAccounts: { + /* Format: "account1/password1/realm": ["charname1", "charname2 etc"], "account2/password2/realm": ["charnameX", "charnameY etc"], "account3/password3/realm": ["all"] @@ -29,8 +29,8 @@ Enter Individual entries are separated with a comma below */ - "exampleAcc/pa33word3/realm": ["all"], - }, - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - }; + "exampleAcc/pa33word3/realm": ["all"], + }, + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + }; })(module); diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index a7c880369..949d10da8 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename MuleLogger.js * @author kolton, theBGuy @@ -6,195 +7,195 @@ */ const MuleLogger = { - // configuration file loaded at bottom - inGameCheck: function () { - if (getScript("D2BotMuleLog.dbj") && this.LogGame[0] && me.gamename.match(this.LogGame[0], "i")) { - print("ÿc4MuleLoggerÿc0: Logging items on " + me.account + " - " + me.name + "."); - D2Bot.printToConsole("MuleLogger: Logging items on " + me.account + " - " + me.name + ".", sdk.colors.D2Bot.DarkGold); - this.logChar(); - let stayInGame = this.IngameTime; - let tick = getTickCount() + rand(1500, 1750) * 1000; // trigger anti-idle every ~30 minutes - - if (this.AutoPerm) { - let permInfo = this.loadPermedStatus(); - - if (!!permInfo.charname) { - if (permInfo.charname === me.charname && !permInfo.perm) { - stayInGame = rand(7230, 7290); - } - } - } - - while ((getTickCount() - me.gamestarttime) < Time.seconds(stayInGame)) { - me.overhead("ÿc2Log items done. ÿc4Stay in " + "ÿc4game more:ÿc0 " + Math.floor(stayInGame - (getTickCount() - me.gamestarttime) / 1000) + " sec"); - - delay(1000); - - if ((getTickCount() - tick) > 0) { - Packet.questRefresh(); // quest status refresh, working as anti-idle - tick += rand(1500, 1750) * 1000; - } - } - - try { - quit(); - } finally { - while (me.ingame) { - delay(100); - } - } - - return true; - } - - return false; - }, - - savePermedStatus: function (charPermInfo = {}) { - FileTools.writeText("logs/MuleLogPermInfo.json", JSON.stringify(charPermInfo)); - }, - - loadPermedStatus: function () { - if (!FileTools.exists("logs/MuleLogPermInfo.json")) throw new Error("File logs/MuleLogPermInfo.json does not exist!"); - let info = (FileTools.readText("logs/MuleLogPermInfo.json")); - return info ? JSON.parse(info) : {}; - }, - - load: function (hash) { - let filename = "data/secure/" + hash + ".txt"; - if (!FileTools.exists(filename)) throw new Error("File " + filename + " does not exist!"); - return FileTools.readText(filename); - }, - - save: function (hash, data) { - let filename = "data/secure/" + hash + ".txt"; - FileTools.writeText(filename, data); - }, - - remove: function () { - FileTools.remove("logs/MuleLog.json"); - FileTools.remove("logs/MuleLogPermInfo.json"); - }, - - // Log kept item stats in the manager. - logItem: function (unit, logIlvl = this.LogItemLevel) { - if (!isIncluded("core/misc.js")) { - include("core/misc.js"); - } - - 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) + "$" + unit.gid + ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); - let color = unit.getColor(); - let code = Item.getItemCode(unit); - let sock = unit.getItemsEx(); - - if (sock.length) { - 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], logIlvl); - } - } - } - - return { - itemColor: color, - image: code, - title: name, - description: desc, - header: header, - sockets: Item.getItemSockets(unit) - }; - }, - - logChar: function (logIlvl = this.LogItemLevel, logName = this.LogNames, saveImg = this.SaveScreenShot) { - while (!me.gameReady) { - delay(100); - } - - // try again if db is locked!! - ItemDB is from https://github.com/dzik87/D2Dropper - // maybe just add it to the core? It's not going to work without an update - if (FileTools.exists("libs/ItemDB.js") && (isIncluded("ItemDB.js") || include("ItemDB.js"))) { - while (!ItemDB.init(false)) { - delay(1000); - } - } - - let items = me.getItemsEx(); - if (!items.length) return; - - let folder, realm = me.realm || "Single Player"; - let finalString = ""; - - if (!FileTools.exists("mules/" + realm)) { - folder = dopen("mules"); - - folder.create(realm); - } - - if (!FileTools.exists("mules/" + realm + "/" + me.account)) { - folder = dopen("mules/" + realm); - - folder.create(me.account); - } - - // from bottom up: merc, equipped, inventory, stash, cube - items.sort(function (a, b) { - if (a.mode < b.mode) return -1; - if (a.mode > b.mode) return 1; - if (a.location === sdk.storage.Cube) return -1; - if (b.location === sdk.storage.Cube) return 1; - return b.location - a.location; - }); - - for (let i = 0; i < items.length; i += 1) { - if ((this.LogEquipped || items[i].isInStorage) && (items[i].quality > sdk.items.quality.Normal || !Item.skipItem(items[i].classid))) { - let parsedItem = this.logItem(items[i], logIlvl); - - // Log names to saved image - logName && (parsedItem.header = (me.account || "Single Player") + " / " + me.name); - // Save image to kolbot/images/ - saveImg && D2Bot.saveItem(parsedItem); - // 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); - - items[i].isEquipped && (parsedItem.title += (items[i].isOnSwap ? " (secondary equipped)" : " (equipped)")); - items[i].isInInventory && (parsedItem.title += " (inventory)"); - items[i].isInStash && (parsedItem.title += " (stash)"); - items[i].isInCube && (parsedItem.title += " (cube)"); - - let string = JSON.stringify(parsedItem); - finalString += (string + "\n"); - } - } - - if (this.LogMerc) { - let merc = Misc.poll(() => me.getMerc(), 1000, 100); - - if (merc) { - let mercItems = merc.getItemsEx(); - - for (let i = 0; i < mercItems.length; i += 1) { - let parsedItem = this.logItem(mercItems[i]); - parsedItem.title += " (merc)"; - let string = JSON.stringify(parsedItem); - finalString += (string + "\n"); - saveImg && D2Bot.saveItem(parsedItem); - } - } - } - - // hcl = hardcore class ladder - // sen = softcore expan nonladder - FileTools.writeText("mules/" + realm + "/" + me.account + "/" + me.name + "." + ( me.playertype ? "h" : "s" ) + (me.gametype ? "e" : "c" ) + ( me.ladder > 0 ? "l" : "n" ) + ".txt", finalString); - print("Item logging done."); - } + // configuration file loaded at bottom + inGameCheck: function () { + if (getScript("D2BotMuleLog.dbj") && this.LogGame[0] && me.gamename.match(this.LogGame[0], "i")) { + print("ÿc4MuleLoggerÿc0: Logging items on " + me.account + " - " + me.name + "."); + D2Bot.printToConsole("MuleLogger: Logging items on " + me.account + " - " + me.name + ".", sdk.colors.D2Bot.DarkGold); + this.logChar(); + let stayInGame = this.IngameTime; + let tick = getTickCount() + rand(1500, 1750) * 1000; // trigger anti-idle every ~30 minutes + + if (this.AutoPerm) { + let permInfo = this.loadPermedStatus(); + + if (!!permInfo.charname) { + if (permInfo.charname === me.charname && !permInfo.perm) { + stayInGame = rand(7230, 7290); + } + } + } + + while ((getTickCount() - me.gamestarttime) < Time.seconds(stayInGame)) { + me.overhead("ÿc2Log items done. ÿc4Stay in " + "ÿc4game more:ÿc0 " + Math.floor(stayInGame - (getTickCount() - me.gamestarttime) / 1000) + " sec"); + + delay(1000); + + if ((getTickCount() - tick) > 0) { + Packet.questRefresh(); // quest status refresh, working as anti-idle + tick += rand(1500, 1750) * 1000; + } + } + + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } + + return true; + } + + return false; + }, + + savePermedStatus: function (charPermInfo = {}) { + FileTools.writeText("logs/MuleLogPermInfo.json", JSON.stringify(charPermInfo)); + }, + + loadPermedStatus: function () { + if (!FileTools.exists("logs/MuleLogPermInfo.json")) throw new Error("File logs/MuleLogPermInfo.json does not exist!"); + let info = (FileTools.readText("logs/MuleLogPermInfo.json")); + return info ? JSON.parse(info) : {}; + }, + + load: function (hash) { + let filename = "data/secure/" + hash + ".txt"; + if (!FileTools.exists(filename)) throw new Error("File " + filename + " does not exist!"); + return FileTools.readText(filename); + }, + + save: function (hash, data) { + let filename = "data/secure/" + hash + ".txt"; + FileTools.writeText(filename, data); + }, + + remove: function () { + FileTools.remove("logs/MuleLog.json"); + FileTools.remove("logs/MuleLogPermInfo.json"); + }, + + // Log kept item stats in the manager. + logItem: function (unit, logIlvl = this.LogItemLevel) { + if (!isIncluded("core/misc.js")) { + include("core/misc.js"); + } + + 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) + "$" + unit.gid + ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); + let color = unit.getColor(); + let code = Item.getItemCode(unit); + let sock = unit.getItemsEx(); + + if (sock.length) { + 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], logIlvl); + } + } + } + + return { + itemColor: color, + image: code, + title: name, + description: desc, + header: header, + sockets: Item.getItemSockets(unit) + }; + }, + + logChar: function (logIlvl = this.LogItemLevel, logName = this.LogNames, saveImg = this.SaveScreenShot) { + while (!me.gameReady) { + delay(100); + } + + // try again if db is locked!! - ItemDB is from https://github.com/dzik87/D2Dropper + // maybe just add it to the core? It's not going to work without an update + if (FileTools.exists("libs/ItemDB.js") && (isIncluded("ItemDB.js") || include("ItemDB.js"))) { + while (!ItemDB.init(false)) { + delay(1000); + } + } + + let items = me.getItemsEx(); + if (!items.length) return; + + let folder, realm = me.realm || "Single Player"; + let finalString = ""; + + if (!FileTools.exists("mules/" + realm)) { + folder = dopen("mules"); + + folder.create(realm); + } + + if (!FileTools.exists("mules/" + realm + "/" + me.account)) { + folder = dopen("mules/" + realm); + + folder.create(me.account); + } + + // from bottom up: merc, equipped, inventory, stash, cube + items.sort(function (a, b) { + if (a.mode < b.mode) return -1; + if (a.mode > b.mode) return 1; + if (a.location === sdk.storage.Cube) return -1; + if (b.location === sdk.storage.Cube) return 1; + return b.location - a.location; + }); + + for (let i = 0; i < items.length; i += 1) { + if ((this.LogEquipped || items[i].isInStorage) && (items[i].quality > sdk.items.quality.Normal || !Item.skipItem(items[i].classid))) { + let parsedItem = this.logItem(items[i], logIlvl); + + // Log names to saved image + logName && (parsedItem.header = (me.account || "Single Player") + " / " + me.name); + // Save image to kolbot/images/ + saveImg && D2Bot.saveItem(parsedItem); + // 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); + + items[i].isEquipped && (parsedItem.title += (items[i].isOnSwap ? " (secondary equipped)" : " (equipped)")); + items[i].isInInventory && (parsedItem.title += " (inventory)"); + items[i].isInStash && (parsedItem.title += " (stash)"); + items[i].isInCube && (parsedItem.title += " (cube)"); + + let string = JSON.stringify(parsedItem); + finalString += (string + "\n"); + } + } + + if (this.LogMerc) { + let merc = Misc.poll(() => me.getMerc(), 1000, 100); + + if (merc) { + let mercItems = merc.getItemsEx(); + + for (let i = 0; i < mercItems.length; i += 1) { + let parsedItem = this.logItem(mercItems[i]); + parsedItem.title += " (merc)"; + let string = JSON.stringify(parsedItem); + finalString += (string + "\n"); + saveImg && D2Bot.saveItem(parsedItem); + } + } + } + + // hcl = hardcore class ladder + // sen = softcore expan nonladder + FileTools.writeText("mules/" + realm + "/" + me.account + "/" + me.name + "." + ( me.playertype ? "h" : "s" ) + (me.gametype ? "e" : "c" ) + ( me.ladder > 0 ? "l" : "n" ) + ".txt", finalString); + print("Item logging done."); + } }; // load configuration file and apply settings to MuleLogger, has to be after the namespace is created (function() { - Object.assign(MuleLogger, require("./LoggerConfig", null, false)); + Object.assign(MuleLogger, require("./LoggerConfig", null, false)); })(); diff --git a/d2bs/kolbot/libs/systems/torch/FarmerConfig.js b/d2bs/kolbot/libs/systems/torch/FarmerConfig.js index 6b5e33738..06e7baa4f 100644 --- a/d2bs/kolbot/libs/systems/torch/FarmerConfig.js +++ b/d2bs/kolbot/libs/systems/torch/FarmerConfig.js @@ -6,10 +6,10 @@ */ (function (module) { - module.exports = { - // ############################ S E T U P ########################################## + module.exports = { + // ############################ S E T U P ########################################## - /* Each uber killer profile can have their own army of key finders + /* Each uber killer profile can have their own army of key finders Multiple entries are separated with a comma Example config: @@ -30,16 +30,16 @@ } */ - // Edit here! + // Edit here! - "PROFILE NAME": { // Farmer profile name - // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], - KeyFinderProfiles: [""], + "PROFILE NAME": { // Farmer profile name + // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], + KeyFinderProfiles: [""], - // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", - FarmGame: "" - }, + // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", + FarmGame: "" + }, - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - }; + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + }; })(module); diff --git a/d2bs/kolbot/libs/systems/torch/TorchSystem.js b/d2bs/kolbot/libs/systems/torch/TorchSystem.js index 1659c879c..8b192d3f0 100644 --- a/d2bs/kolbot/libs/systems/torch/TorchSystem.js +++ b/d2bs/kolbot/libs/systems/torch/TorchSystem.js @@ -7,359 +7,363 @@ */ const TorchSystem = { - // load configuration file - FarmerProfiles: Object.assign({}, require("./FarmerConfig", null, false)), - - // Don't touch - inGame: false, - check: false, - - getFarmers: function () { - let list = []; - - for (let i in this.FarmerProfiles) { - if (this.FarmerProfiles.hasOwnProperty(i)) { - for (let j = 0; j < this.FarmerProfiles[i].KeyFinderProfiles.length; j += 1) { - if (String.isEqual(this.FarmerProfiles[i].KeyFinderProfiles[j], me.profile)) { - this.FarmerProfiles[i].profile = i; - - list.push(this.FarmerProfiles[i]); - } - } - } - } - - return list.length > 0 ? list : false; - }, - - isFarmer: function () { - if (this.FarmerProfiles.hasOwnProperty(me.profile)) { - this.FarmerProfiles[me.profile].profile = me.profile; - - return this.FarmerProfiles[me.profile]; - } - - return false; - }, - - inGameCheck: function () { - let farmers = this.getFarmers(); - if (!farmers) return false; - - for (let i = 0; i < farmers.length; i += 1) { - if (farmers[i].FarmGame.length > 0 && me.gamename.toLowerCase().match(farmers[i].FarmGame.toLowerCase())) { - print("ÿc4Torch Systemÿc0: In Farm game."); - D2Bot.printToConsole("Torch System: Transfering keys.", sdk.colors.D2Bot.DarkGold); - D2Bot.updateStatus("Torch System: In game."); - Town.goToTown(1); - - if (Town.openStash()) { - let neededItems = this.keyCheck(); - - if (neededItems) { - for (let n in neededItems) { - if (neededItems.hasOwnProperty(n)) { - while (neededItems[n].length) { - neededItems[n].shift().drop(); - } - } - } - } - } - - if (me.getStat(sdk.stats.Gold) >= 100000) { - gold(100000); - } - - delay(5000); + // load configuration file + FarmerProfiles: Object.assign({}, require("./FarmerConfig", null, false)), + + // Don't touch + inGame: false, + check: false, + + getFarmers: function () { + let list = []; + + for (let i in this.FarmerProfiles) { + if (this.FarmerProfiles.hasOwnProperty(i)) { + for (let j = 0; j < this.FarmerProfiles[i].KeyFinderProfiles.length; j += 1) { + if (String.isEqual(this.FarmerProfiles[i].KeyFinderProfiles[j], me.profile)) { + this.FarmerProfiles[i].profile = i; + + list.push(this.FarmerProfiles[i]); + } + } + } + } + + return list.length > 0 ? list : false; + }, + + isFarmer: function () { + if (this.FarmerProfiles.hasOwnProperty(me.profile)) { + this.FarmerProfiles[me.profile].profile = me.profile; + + return this.FarmerProfiles[me.profile]; + } + + return false; + }, + + inGameCheck: function () { + let farmers = this.getFarmers(); + if (!farmers) return false; + + for (let i = 0; i < farmers.length; i += 1) { + if (farmers[i].FarmGame.length > 0 && me.gamename.toLowerCase().match(farmers[i].FarmGame.toLowerCase())) { + print("ÿc4Torch Systemÿc0: In Farm game."); + D2Bot.printToConsole("Torch System: Transfering keys.", sdk.colors.D2Bot.DarkGold); + D2Bot.updateStatus("Torch System: In game."); + Town.goToTown(1); + + if (Town.openStash()) { + let neededItems = this.keyCheck(); + + if (neededItems) { + for (let n in neededItems) { + if (neededItems.hasOwnProperty(n)) { + while (neededItems[n].length) { + neededItems[n].shift().drop(); + } + } + } + } + } + + if (me.getStat(sdk.stats.Gold) >= 100000) { + gold(100000); + } + + delay(5000); - try { - quit(); - } finally { - while (me.ingame) { - delay(100); - } - } - - return true; - } - } - - return false; - }, - - keyCheck: function () { - let neededItems = {}; - let farmers = this.getFarmers(); - if (!farmers) return false; - - function keyCheckEvent(mode, msg) { - if (mode === 6) { - let obj = JSON.parse(msg); + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } + + return true; + } + } + + return false; + }, + + keyCheck: function () { + let neededItems = {}; + let farmers = this.getFarmers(); + if (!farmers) return false; + + function keyCheckEvent(mode, msg) { + if (mode === 6) { + let obj = JSON.parse(msg); - if (obj.name === "neededItems") { - let item; - - for (let i in obj.value) { - if (obj.value.hasOwnProperty(i) && obj.value[i] > 0) { - switch (i) { - case "pk1": - case "pk2": - case "pk3": - item = me.getItem(i); - - if (item) { - do { - if (!neededItems[i]) { - neededItems[i] = []; - } - - neededItems[i].push(copyUnit(item)); + if (obj.name === "neededItems") { + let item; + + for (let i in obj.value) { + if (obj.value.hasOwnProperty(i) && obj.value[i] > 0) { + switch (i) { + case "pk1": + case "pk2": + case "pk3": + item = me.getItem(i); + + if (item) { + do { + if (!neededItems[i]) { + neededItems[i] = []; + } + + neededItems[i].push(copyUnit(item)); - if (neededItems[i].length >= obj.value[i]) { - break; - } - } while (item.getNext()); - } + if (neededItems[i].length >= obj.value[i]) { + break; + } + } while (item.getNext()); + } - break; - case "rv": - item = me.getItem(); + break; + case "rv": + item = me.getItem(); - if (item) { - do { - if (item.code === "rvs" || item.code === "rvl") { - if (!neededItems[i]) { - neededItems[i] = []; - } + if (item) { + do { + if (item.code === "rvs" || item.code === "rvl") { + if (!neededItems[i]) { + neededItems[i] = []; + } - neededItems[i].push(copyUnit(item)); + neededItems[i].push(copyUnit(item)); - if (neededItems[i].length >= Math.min(2, obj.value[i])) { - break; - } - } - } while (item.getNext()); - } + if (neededItems[i].length >= Math.min(2, obj.value[i])) { + break; + } + } + } while (item.getNext()); + } - break; - } - } - } - } - } - } + break; + } + } + } + } + } + } - addEventListener("copydata", keyCheckEvent); + addEventListener("copydata", keyCheckEvent); - // TODO: one mfer for multiple farmers handling - for (let i = 0; i < farmers.length; i += 1) { - sendCopyData(null, farmers[i].profile, 6, JSON.stringify({name: "keyCheck", profile: me.profile})); - delay(250); + // TODO: one mfer for multiple farmers handling + for (let i = 0; i < farmers.length; i += 1) { + sendCopyData(null, farmers[i].profile, 6, JSON.stringify({ name: "keyCheck", profile: me.profile })); + delay(250); - if (neededItems.hasOwnProperty("pk1") || neededItems.hasOwnProperty("pk2") || neededItems.hasOwnProperty("pk3")) { - removeEventListener("copydata", keyCheckEvent); + if (neededItems.hasOwnProperty("pk1") || neededItems.hasOwnProperty("pk2") || neededItems.hasOwnProperty("pk3")) { + removeEventListener("copydata", keyCheckEvent); - return neededItems; - } - } + return neededItems; + } + } - removeEventListener("copydata", keyCheckEvent); + removeEventListener("copydata", keyCheckEvent); - return false; - }, + return false; + }, - outOfGameCheck: function () { - if (!this.check) return false; - TorchSystem.check = false; + outOfGameCheck: function () { + if (!this.check) return false; + TorchSystem.check = false; - let game; + let game; - function checkEvent(mode, msg) { - let farmers = TorchSystem.getFarmers(); + function checkEvent(mode, msg) { + let farmers = TorchSystem.getFarmers(); - if (mode === 6) { - let obj = JSON.parse(msg); + if (mode === 6) { + let obj = JSON.parse(msg); - if (obj && obj.name === "gameName") { - for (let i = 0; i < farmers.length; i += 1) { - if (obj.value.gameName.toLowerCase().match(farmers[i].FarmGame.toLowerCase())) { - game = [obj.value.gameName, obj.value.password]; - } - } - } - } + if (obj && obj.name === "gameName") { + for (let i = 0; i < farmers.length; i += 1) { + if (obj.value.gameName.toLowerCase().match(farmers[i].FarmGame.toLowerCase())) { + game = [obj.value.gameName, obj.value.password]; + } + } + } + } - return true; - } + return true; + } - let farmers = this.getFarmers(); - if (!farmers) return false; + let farmers = this.getFarmers(); + if (!farmers) return false; - addEventListener("copydata", checkEvent); + addEventListener("copydata", checkEvent); - for (let i = 0; i < farmers.length; i += 1) { - sendCopyData(null, farmers[i].profile, 6, JSON.stringify({name: "gameCheck", profile: me.profile})); - delay(500); + for (let i = 0; i < farmers.length; i += 1) { + sendCopyData(null, farmers[i].profile, 6, JSON.stringify({ name: "gameCheck", profile: me.profile })); + delay(500); - if (game) { - break; - } - } + if (game) { + break; + } + } - removeEventListener("copydata", checkEvent); + removeEventListener("copydata", checkEvent); - if (game) { - delay(2000); + if (game) { + delay(2000); - TorchSystem.inGame = true; - me.blockMouse = true; + TorchSystem.inGame = true; + me.blockMouse = true; - joinGame(game[0], game[1]); - - me.blockMouse = false; + joinGame(game[0], game[1]); + + me.blockMouse = false; + + delay(5000); - delay(5000); + while (me.ingame) { + delay(1000); + } - while (me.ingame) { - delay(1000); - } + TorchSystem.inGame = false; + + return true; + } - TorchSystem.inGame = false; + return false; + }, - return true; - } + waitForKeys: function () { + let timer = getTickCount(); + let busy = false; + let busyTick; + let tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; + let hkeys = me.findItems("pk2", sdk.items.mode.inStorage).length || 0; + let dkeys = me.findItems("pk3", sdk.items.mode.inStorage).length || 0; + let neededItems = { pk1: 0, pk2: 0, pk3: 0, rv: 0 }; - return false; - }, + // Check whether the killer is alone in the game + const aloneInGame = function () { + return (Misc.getPlayerCount() <= 1); + }; - waitForKeys: function () { - let timer = getTickCount(); - let busy = false; - let busyTick; - let tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; - let hkeys = me.findItems("pk2", sdk.items.mode.inStorage).length || 0; - let dkeys = me.findItems("pk3", sdk.items.mode.inStorage).length || 0; - let neededItems = {pk1: 0, pk2: 0, pk3: 0, rv: 0}; + const juvCheck = function () { + let needJuvs = 0; + let col = Town.checkColumns(Storage.BeltSize()); - // Check whether the killer is alone in the game - const aloneInGame = function () { - return (Misc.getPlayerCount() <= 1); - }; + for (let i = 0; i < 4; i += 1) { + if (Config.BeltColumn[i] === "rv") { + needJuvs += col[i]; + } + } - const juvCheck = function () { - let needJuvs = 0; - let col = Town.checkColumns(Storage.BeltSize()); + console.log("Need " + needJuvs + " juvs."); - for (let i = 0; i < 4; i += 1) { - if (Config.BeltColumn[i] === "rv") { - needJuvs += col[i]; - } - } + return needJuvs; + }; - console.log("Need " + needJuvs + " juvs."); + // Check if current character is the farmer + let farmer = TorchSystem.isFarmer(); - return needJuvs; - }; + const torchSystemEvent = function (mode, msg) { + let obj, farmer; - // Check if current character is the farmer - let farmer = TorchSystem.isFarmer(); + if (mode === 6) { + farmer = TorchSystem.isFarmer(); - const torchSystemEvent = function (mode, msg) { - let obj, farmer; + if (farmer) { + obj = JSON.parse(msg); + + if (obj) { + switch (obj.name) { + case "gameCheck": + if (busy) { + break; + } + + if (farmer.KeyFinderProfiles.includes(obj.profile)) { + print("Got game request from: " + obj.profile); + sendCopyData( + null, obj.profile, 6, + JSON.stringify({ name: "gameName", value: { gameName: me.gamename, password: me.gamepassword } }) + ); - if (mode === 6) { - farmer = TorchSystem.isFarmer(); + busy = true; + busyTick = getTickCount(); + } - if (farmer) { - obj = JSON.parse(msg); - - if (obj) { - switch (obj.name) { - case "gameCheck": - if (busy) { - break; - } - - if (farmer.KeyFinderProfiles.includes(obj.profile)) { - print("Got game request from: " + obj.profile); - sendCopyData(null, obj.profile, 6, JSON.stringify({name: "gameName", value: {gameName: me.gamename, password: me.gamepassword}})); - - busy = true; - busyTick = getTickCount(); - } - - break; - case "keyCheck": - if (farmer.KeyFinderProfiles.includes(obj.profile)) { - print("Got key count request from: " + obj.profile); - - // Get the number of needed keys - neededItems = {pk1: 3 - tkeys, pk2: 3 - hkeys, pk3: 3 - dkeys, rv: juvCheck()}; - sendCopyData(null, obj.profile, 6, JSON.stringify({name: "neededItems", value: neededItems})); - } - - break; - } - } - } - } - }; - - // Register event that will communicate with key hunters, go to Act 1 town and wait by stash - addEventListener("copydata", torchSystemEvent); - Town.goToTown(1); - Town.move("stash"); - - try { - while (true) { - // Abort if the current character isn't a farmer - if (!farmer) { - break; - } - - // Free up inventory - Town.needStash() && Town.stash(); - - // Get the number keys - tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; - hkeys = me.findItems("pk2", sdk.items.mode.inStorage).length || 0; - dkeys = me.findItems("pk3", sdk.items.mode.inStorage).length || 0; - - // Stop the loop if we have enough keys or if wait time expired - if (((tkeys >= 3 && hkeys >= 3 && dkeys >= 3) - || (Config.OrgTorch.WaitTimeout && (getTickCount() - timer > Time.minutes(Config.OrgTorch.WaitTimeout)))) + break; + case "keyCheck": + if (farmer.KeyFinderProfiles.includes(obj.profile)) { + print("Got key count request from: " + obj.profile); + + // Get the number of needed keys + neededItems = { pk1: 3 - tkeys, pk2: 3 - hkeys, pk3: 3 - dkeys, rv: juvCheck() }; + sendCopyData(null, obj.profile, 6, JSON.stringify({ name: "neededItems", value: neededItems })); + } + + break; + } + } + } + } + }; + + // Register event that will communicate with key hunters, go to Act 1 town and wait by stash + addEventListener("copydata", torchSystemEvent); + Town.goToTown(1); + Town.move("stash"); + + try { + while (true) { + // Abort if the current character isn't a farmer + if (!farmer) { + break; + } + + // Free up inventory + Town.needStash() && Town.stash(); + + // Get the number keys + tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; + hkeys = me.findItems("pk2", sdk.items.mode.inStorage).length || 0; + dkeys = me.findItems("pk3", sdk.items.mode.inStorage).length || 0; + + // Stop the loop if we have enough keys or if wait time expired + if (((tkeys >= 3 && hkeys >= 3 && dkeys >= 3) + || (Config.OrgTorch.WaitTimeout + && (getTickCount() - timer > Time.minutes(Config.OrgTorch.WaitTimeout)))) && aloneInGame()) { - break; - } - - if (busy) { - while (getTickCount() - busyTick < 30000) { - if (!aloneInGame()) { - break; - } - - delay(100); - } - - if (getTickCount() - busyTick > 30000 || aloneInGame()) { - busy = false; - } - } - - // Wait for other characters to leave - while (!aloneInGame()) { - delay(500); - } - - delay(1000); - - // Pick the keys after the hunters drop them and leave the game - Pickit.pickItems(); - } - } finally { - removeEventListener("copydata", torchSystemEvent); - } - }, + break; + } + + if (busy) { + while (getTickCount() - busyTick < 30000) { + if (!aloneInGame()) { + break; + } + + delay(100); + } + + if (getTickCount() - busyTick > 30000 || aloneInGame()) { + busy = false; + } + } + + // Wait for other characters to leave + while (!aloneInGame()) { + delay(500); + } + + delay(1000); + + // Pick the keys after the hunters drop them and leave the game + Pickit.pickItems(); + } + } finally { + removeEventListener("copydata", torchSystemEvent); + } + }, }; diff --git a/d2bs/kolbot/threads/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js index b7877dc6f..b387bbb01 100644 --- a/d2bs/kolbot/threads/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename AntiHostile.js * @author kolton @@ -16,371 +17,371 @@ include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); function main() { - // Variables and functions - let player, attackCount, prevPos, check, missile, outside; - let charClass = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; - let hostiles = []; - - // AntiHostile gets game event info from ToolsThread - this.scriptEvent = function (msg) { - if (!msg || typeof msg !== "string") return; + // Variables and functions + let player, attackCount, prevPos, check, missile, outside; + let charClass = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; + let hostiles = []; + + // AntiHostile gets game event info from ToolsThread + this.scriptEvent = function (msg) { + if (!msg || typeof msg !== "string") return; - switch (msg.split(" ")[0]) { - case "remove": // Remove a hostile player that left the game - if (hostiles.indexOf(msg.split(" ")[1]) > -1) { - hostiles.splice(hostiles.indexOf(msg.split(" ")[1]), 1); - } - - break; - case "mugshot": // Take a screenshot and log the kill - D2Bot.printToConsole(msg.split(" ")[1] + " has been neutralized.", sdk.colors.D2Bot.Blue); - hideConsole(); - delay(500); - takeScreenshot(); - - break; - } - }; - - // Find all hostile players and add their names to the 'hostiles' list - this.findHostiles = function () { - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8) && hostiles.indexOf(party.name) === -1) { - D2Bot.printToConsole(party.name + " (Level " + party.level + " " + charClass[party.classid] + ")" + " has declared hostility.", sdk.colors.D2Bot.Orange); - hostiles.push(party.name); - } - } while (party.getNext()); - } - - return true; - }; - - // Pause default so actions don't conflict - this.pause = function () { - let script = getScript("default.dbj"); - - if (script && script.running) { - print("ÿc1Pausing."); - script.pause(); - } - }; - - // Resume default - this.resume = function () { - let script = getScript("default.dbj"); - - if (script && !script.running) { - print("ÿc2Resuming."); - script.resume(); - } - }; - - // Find hostile player Units - this.findPlayer = function () { - for (let i = 0; i < hostiles.length; i += 1) { - let player = Game.getPlayer(hostiles[i]); - - if (player) { - do { - if (!player.dead && getPlayerFlag(me.gid, player.gid, 8) && !player.inTown && !me.inTown) { - return player; - } - } while (player.getNext()); - } - } - - return false; - }; - - // Find a missile type - this.findMissile = function (owner, id, range) { - range === undefined && (range = 999); - - let missile = Game.getMissile(id); - if (!missile) return false; - - do { - if (missile.owner === owner.gid && getDistance(owner, missile) < range) { - return missile; - } - } while (missile.getNext()); - - return false; - }; - - this.checkSummons = function (player) { - if (!player) return false; - let name = player.name; - let unit = Game.getMonster(); - - if (unit) { - do { - // Revives and spirit wolves - if (unit.getParent() && unit.getParent().name === name && (unit.getState(sdk.states.Revive) || unit.classid === sdk.monsters.Wolf2)) { - return true; - } - } while (unit.getNext()); - } - - return false; - }; - - // Init config and attacks - D2Bot.init(); - Config.init(); - Attack.init(); - Storage.Init(); - - // Use PVP range for attacks - Skill.usePvpRange = true; - - // Attack sequence adjustments - this only affects the AntiHostile thread - if (Skill.canUse(sdk.skills.MindBlast) && [sdk.skills.FireBlast, sdk.skills.ShockWeb].includes(Config.AttackSkill[1])) { - Config.AttackSkill[1] = sdk.skills.MindBlast; - ClassAttack.trapRange = 40; - } - - // A simple but fast player dodge function - this.moveAway = function (unit, range) { - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - let angles = [0, 45, -45, 90, -90, 135, -135, 180]; - - for (let i = 0; i < angles.length; i += 1) { - // Avoid the position where the player actually tries to move to - let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); // unit.targetx - let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); // unit.targety - - if (Attack.validSpot(coordx, coordy)) { - return Pather.moveTo(coordx, coordy); - } - } - - return false; - }; - - addEventListener("scriptmsg", this.scriptEvent); - print("ÿc2Anti-Hostile thread loaded."); - - // Main Loop - while (true) { - if (me.gameReady) { - // Scan for hostiles - this.findHostiles(); - - if (hostiles.length > 0 && (Config.HostileAction === 0 || (Config.HostileAction === 1 && me.inTown))) { - if (Config.TownOnHostile) { - print("ÿc1Hostility detected, going to town."); - this.pause(); - - if (!me.inTown) { - outside = true; - } - - try { - Town.goToTown(); - } catch (e) { - console.error(e + " Failed to go to town. Quitting."); - scriptBroadcast("quit"); // quit if failed to go to town - } - - while (hostiles.length > 0) { - delay(500); - } - - if (outside) { - outside = false; - Pather.usePortal(null, me.name); - } - - this.resume(); - } else { - scriptBroadcast("quit"); - } - - delay(500); - - continue; - } - - // Mode 3 - Spam entrance (still experimental) - if (Config.HostileAction === 3 && hostiles.length > 0 && me.inArea(sdk.areas.ThroneofDestruction)) { - switch (me.classid) { - case sdk.player.class.Sorceress: - prevPos = {x: me.x, y: me.y}; - this.pause(); - Pather.moveTo(15103, 5247); - - while (!this.findPlayer() && hostiles.length > 0) { - if (!me.skillDelay) { - Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099, 5237); - } else { - if (Config.AttackSkill[2] > -1) { - Skill.cast(Config.AttackSkill[2], Skill.getHand(Config.AttackSkill[2]), 15099, 5237); - } else { - while (me.skillDelay) { - delay(40); - } - } - } - } - - break; - case sdk.player.class.Druid: - // Don't bother if it's not a tornado druid - if (Config.AttackSkill[1] !== sdk.skills.Tornado) { - break; - } - - prevPos = {x: me.x, y: me.y}; - this.pause(); - Pather.moveTo(15103, 5247); - - while (!this.findPlayer() && hostiles.length > 0) { - // Tornado path is a function of target x. Slight randomization will make sure it can't always miss - Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099 + rand(-2, 2), 5237); - } - - break; - case sdk.player.class.Assassin: - prevPos = {x: me.x, y: me.y}; - this.pause(); - Pather.moveTo(15103, 5247); - - while (!this.findPlayer() && hostiles.length > 0) { - if (Config.UseTraps) { - check = ClassAttack.checkTraps({x: 15099, y: 5242, classid: 544}); - - if (check) { - ClassAttack.placeTraps({x: 15099, y: 5242, classid: 544}, 5); - } - } - - Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099, 5237); - - while (me.skillDelay) { - delay(40); - } - } - - break; - } - } - - // Player left, return to old position - if (!hostiles.length && prevPos) { - Pather.moveTo(prevPos.x, prevPos.y); - this.resume(); - - // Reset position - prevPos = false; - } - - player = this.findPlayer(); - - if (player) { - // Mode 1 - Quit if hostile player is nearby - if (Config.HostileAction === 1) { - if (Config.TownOnHostile) { - print("ÿc1Hostile player nearby, going to town."); - this.pause(); - - if (!me.inTown) { - outside = true; - } - - try { - Town.goToTown(); - } catch (e) { - print(e + " Failed to go to town. Quitting."); - scriptBroadcast("quit"); // quit if failed to go to town - } - - while (hostiles.length > 0) { - delay(500); - } - - if (outside) { - outside = false; - Pather.usePortal(null, me.name); - } - - this.resume(); - } else { - scriptBroadcast("quit"); - } - - delay(500); - - continue; - } - - // Kill the hostile player - if (!prevPos) { - prevPos = {x: me.x, y: me.y}; - } - - this.pause(); - - Config.UseMerc = false; // Don't go revive the merc mid-fight - attackCount = 0; - - while (attackCount < 100) { - // Invalidated Unit (out of getUnit range) or player in town - if (!copyUnit(player).x || player.inTown || me.mode === sdk.player.mode.Dead) { - break; - } - - ClassAttack.doAttack(player, false); - - // Specific attack additions - switch (me.classid) { - case sdk.player.class.Sorceress: - case sdk.player.class.Necromancer: - // Dodge missiles - experimental - missile = Game.getMissile(); - - if (missile) { - do { - if (getPlayerFlag(me.gid, missile.owner, 8) && (getDistance(me, missile) < 15 || (missile.targetx && getDistance(me, missile.targetx, missile.targety) < 15))) { - this.moveAway(missile, Skill.getRange(Config.AttackSkill[1])); - - break; - } - } while (missile.getNext()); - } - - // Move away if the player is too close or if he tries to move too close (telestomp) - if (Skill.getRange(Config.AttackSkill[1]) > 20 && (getDistance(me, player) < 30 || (player.targetx && getDistance(me, player.targetx, player.targety) < 15))) { - this.moveAway(player, Skill.getRange(Config.AttackSkill[1])); - } - - break; - case sdk.player.class.Paladin: - // Smite summoners - if (Config.AttackSkill[1] === sdk.skills.BlessedHammer && Skill.canUse(sdk.skills.Smite)) { - if ([sdk.player.class.Necromancer, sdk.player.class.Druid].includes(player.classid) && getDistance(me, player) < 4 && this.checkSummons(player)) { - Skill.cast(sdk.skills.Smite, sdk.skills.hand.Left, player); - } - } - - break; - } - - attackCount += 1; - - if (player.dead) { - break; - } - } - - Pather.moveTo(prevPos.x, prevPos.y); - this.resume(); - } - } - - delay(200); - } + switch (msg.split(" ")[0]) { + case "remove": // Remove a hostile player that left the game + if (hostiles.indexOf(msg.split(" ")[1]) > -1) { + hostiles.splice(hostiles.indexOf(msg.split(" ")[1]), 1); + } + + break; + case "mugshot": // Take a screenshot and log the kill + D2Bot.printToConsole(msg.split(" ")[1] + " has been neutralized.", sdk.colors.D2Bot.Blue); + hideConsole(); + delay(500); + takeScreenshot(); + + break; + } + }; + + // Find all hostile players and add their names to the 'hostiles' list + this.findHostiles = function () { + let party = getParty(); + + if (party) { + do { + if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8) && hostiles.indexOf(party.name) === -1) { + D2Bot.printToConsole(party.name + " (Level " + party.level + " " + charClass[party.classid] + ")" + " has declared hostility.", sdk.colors.D2Bot.Orange); + hostiles.push(party.name); + } + } while (party.getNext()); + } + + return true; + }; + + // Pause default so actions don't conflict + this.pause = function () { + let script = getScript("default.dbj"); + + if (script && script.running) { + print("ÿc1Pausing."); + script.pause(); + } + }; + + // Resume default + this.resume = function () { + let script = getScript("default.dbj"); + + if (script && !script.running) { + print("ÿc2Resuming."); + script.resume(); + } + }; + + // Find hostile player Units + this.findPlayer = function () { + for (let i = 0; i < hostiles.length; i += 1) { + let player = Game.getPlayer(hostiles[i]); + + if (player) { + do { + if (!player.dead && getPlayerFlag(me.gid, player.gid, 8) && !player.inTown && !me.inTown) { + return player; + } + } while (player.getNext()); + } + } + + return false; + }; + + // Find a missile type + this.findMissile = function (owner, id, range) { + range === undefined && (range = 999); + + let missile = Game.getMissile(id); + if (!missile) return false; + + do { + if (missile.owner === owner.gid && getDistance(owner, missile) < range) { + return missile; + } + } while (missile.getNext()); + + return false; + }; + + this.checkSummons = function (player) { + if (!player) return false; + let name = player.name; + let unit = Game.getMonster(); + + if (unit) { + do { + // Revives and spirit wolves + if (unit.getParent() && unit.getParent().name === name && (unit.getState(sdk.states.Revive) || unit.classid === sdk.monsters.Wolf2)) { + return true; + } + } while (unit.getNext()); + } + + return false; + }; + + // Init config and attacks + D2Bot.init(); + Config.init(); + Attack.init(); + Storage.Init(); + + // Use PVP range for attacks + Skill.usePvpRange = true; + + // Attack sequence adjustments - this only affects the AntiHostile thread + if (Skill.canUse(sdk.skills.MindBlast) && [sdk.skills.FireBlast, sdk.skills.ShockWeb].includes(Config.AttackSkill[1])) { + Config.AttackSkill[1] = sdk.skills.MindBlast; + ClassAttack.trapRange = 40; + } + + // A simple but fast player dodge function + this.moveAway = function (unit, range) { + let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); + let angles = [0, 45, -45, 90, -90, 135, -135, 180]; + + for (let i = 0; i < angles.length; i += 1) { + // Avoid the position where the player actually tries to move to + let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); // unit.targetx + let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); // unit.targety + + if (Attack.validSpot(coordx, coordy)) { + return Pather.moveTo(coordx, coordy); + } + } + + return false; + }; + + addEventListener("scriptmsg", this.scriptEvent); + print("ÿc2Anti-Hostile thread loaded."); + + // Main Loop + while (true) { + if (me.gameReady) { + // Scan for hostiles + this.findHostiles(); + + if (hostiles.length > 0 && (Config.HostileAction === 0 || (Config.HostileAction === 1 && me.inTown))) { + if (Config.TownOnHostile) { + print("ÿc1Hostility detected, going to town."); + this.pause(); + + if (!me.inTown) { + outside = true; + } + + try { + Town.goToTown(); + } catch (e) { + console.error(e + " Failed to go to town. Quitting."); + scriptBroadcast("quit"); // quit if failed to go to town + } + + while (hostiles.length > 0) { + delay(500); + } + + if (outside) { + outside = false; + Pather.usePortal(null, me.name); + } + + this.resume(); + } else { + scriptBroadcast("quit"); + } + + delay(500); + + continue; + } + + // Mode 3 - Spam entrance (still experimental) + if (Config.HostileAction === 3 && hostiles.length > 0 && me.inArea(sdk.areas.ThroneofDestruction)) { + switch (me.classid) { + case sdk.player.class.Sorceress: + prevPos = { x: me.x, y: me.y }; + this.pause(); + Pather.moveTo(15103, 5247); + + while (!this.findPlayer() && hostiles.length > 0) { + if (!me.skillDelay) { + Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099, 5237); + } else { + if (Config.AttackSkill[2] > -1) { + Skill.cast(Config.AttackSkill[2], Skill.getHand(Config.AttackSkill[2]), 15099, 5237); + } else { + while (me.skillDelay) { + delay(40); + } + } + } + } + + break; + case sdk.player.class.Druid: + // Don't bother if it's not a tornado druid + if (Config.AttackSkill[1] !== sdk.skills.Tornado) { + break; + } + + prevPos = { x: me.x, y: me.y }; + this.pause(); + Pather.moveTo(15103, 5247); + + while (!this.findPlayer() && hostiles.length > 0) { + // Tornado path is a function of target x. Slight randomization will make sure it can't always miss + Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099 + rand(-2, 2), 5237); + } + + break; + case sdk.player.class.Assassin: + prevPos = { x: me.x, y: me.y }; + this.pause(); + Pather.moveTo(15103, 5247); + + while (!this.findPlayer() && hostiles.length > 0) { + if (Config.UseTraps) { + check = ClassAttack.checkTraps({ x: 15099, y: 5242, classid: 544 }); + + if (check) { + ClassAttack.placeTraps({ x: 15099, y: 5242, classid: 544 }, 5); + } + } + + Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099, 5237); + + while (me.skillDelay) { + delay(40); + } + } + + break; + } + } + + // Player left, return to old position + if (!hostiles.length && prevPos) { + Pather.moveTo(prevPos.x, prevPos.y); + this.resume(); + + // Reset position + prevPos = false; + } + + player = this.findPlayer(); + + if (player) { + // Mode 1 - Quit if hostile player is nearby + if (Config.HostileAction === 1) { + if (Config.TownOnHostile) { + print("ÿc1Hostile player nearby, going to town."); + this.pause(); + + if (!me.inTown) { + outside = true; + } + + try { + Town.goToTown(); + } catch (e) { + print(e + " Failed to go to town. Quitting."); + scriptBroadcast("quit"); // quit if failed to go to town + } + + while (hostiles.length > 0) { + delay(500); + } + + if (outside) { + outside = false; + Pather.usePortal(null, me.name); + } + + this.resume(); + } else { + scriptBroadcast("quit"); + } + + delay(500); + + continue; + } + + // Kill the hostile player + if (!prevPos) { + prevPos = { x: me.x, y: me.y }; + } + + this.pause(); + + Config.UseMerc = false; // Don't go revive the merc mid-fight + attackCount = 0; + + while (attackCount < 100) { + // Invalidated Unit (out of getUnit range) or player in town + if (!copyUnit(player).x || player.inTown || me.mode === sdk.player.mode.Dead) { + break; + } + + ClassAttack.doAttack(player, false); + + // Specific attack additions + switch (me.classid) { + case sdk.player.class.Sorceress: + case sdk.player.class.Necromancer: + // Dodge missiles - experimental + missile = Game.getMissile(); + + if (missile) { + do { + if (getPlayerFlag(me.gid, missile.owner, 8) && (getDistance(me, missile) < 15 || (missile.targetx && getDistance(me, missile.targetx, missile.targety) < 15))) { + this.moveAway(missile, Skill.getRange(Config.AttackSkill[1])); + + break; + } + } while (missile.getNext()); + } + + // Move away if the player is too close or if he tries to move too close (telestomp) + if (Skill.getRange(Config.AttackSkill[1]) > 20 && (getDistance(me, player) < 30 || (player.targetx && getDistance(me, player.targetx, player.targety) < 15))) { + this.moveAway(player, Skill.getRange(Config.AttackSkill[1])); + } + + break; + case sdk.player.class.Paladin: + // Smite summoners + if (Config.AttackSkill[1] === sdk.skills.BlessedHammer && Skill.canUse(sdk.skills.Smite)) { + if ([sdk.player.class.Necromancer, sdk.player.class.Druid].includes(player.classid) && getDistance(me, player) < 4 && this.checkSummons(player)) { + Skill.cast(sdk.skills.Smite, sdk.skills.hand.Left, player); + } + } + + break; + } + + attackCount += 1; + + if (player.dead) { + break; + } + } + + Pather.moveTo(prevPos.x, prevPos.y); + this.resume(); + } + } + + delay(200); + } } diff --git a/d2bs/kolbot/threads/AntiIdle.js b/d2bs/kolbot/threads/AntiIdle.js index b1d63c9a4..e05f739b4 100644 --- a/d2bs/kolbot/threads/AntiIdle.js +++ b/d2bs/kolbot/threads/AntiIdle.js @@ -10,16 +10,16 @@ include("core/Util.js"); include("core/Packet.js"); function main () { - console.log("ÿc3Start AntiIdle"); - let idleTick = Time.seconds(getTickCount() + rand(1200, 1500)); + console.log("ÿc3Start AntiIdle"); + let idleTick = Time.seconds(getTickCount() + rand(1200, 1500)); - while (true) { - if (me.ingame && me.gameReady) { - if (getTickCount() - idleTick > 0) { - Packet.questRefresh(); - idleTick += Time.seconds(rand(1200, 1500)); - console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); - } - } - } + while (true) { + if (me.ingame && me.gameReady) { + if (getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + } + } } diff --git a/d2bs/kolbot/threads/AreaWatcher.js b/d2bs/kolbot/threads/AreaWatcher.js index e66d9be86..383513b48 100644 --- a/d2bs/kolbot/threads/AreaWatcher.js +++ b/d2bs/kolbot/threads/AreaWatcher.js @@ -13,21 +13,21 @@ includeCoreLibs(); */ function main() { - let _default = getScript("default.dbj"); - console.log("ÿc3Start AreaWatcher"); + let _default = getScript("default.dbj"); + console.log("ÿc3Start AreaWatcher"); - while (true) { - try { - if (me.gameReady && me.ingame && !me.inTown) { - // additonal check for wierd behavior - it shouldn't be possbile to run out of town in less than 30 seconds in game - if (getTickCount() - me.gamestarttime < Time.seconds(30)) continue; - !!_default && _default.stop(); - D2Bot.printToConsole("Saved from suicide walk!"); - !!_default && !_default.running ? quit() : D2Bot.restart(); - } - } catch (e) { - console.warn("AreaWatcher failed somewhere. ", e); - } - delay(1000); - } + while (true) { + try { + if (me.gameReady && me.ingame && !me.inTown) { + // additonal check for wierd behavior - it shouldn't be possbile to run out of town in less than 30 seconds in game + if (getTickCount() - me.gamestarttime < Time.seconds(30)) continue; + !!_default && _default.stop(); + D2Bot.printToConsole("Saved from suicide walk!"); + !!_default && !_default.running ? quit() : D2Bot.restart(); + } + } catch (e) { + console.warn("AreaWatcher failed somewhere. ", e); + } + delay(1000); + } } diff --git a/d2bs/kolbot/threads/AutoBuildThread.js b/d2bs/kolbot/threads/AutoBuildThread.js index ab1b27908..16dba39c9 100644 --- a/d2bs/kolbot/threads/AutoBuildThread.js +++ b/d2bs/kolbot/threads/AutoBuildThread.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename AutoBuildThread.js * @author alogwe @@ -23,10 +24,10 @@ Config.init(); // includes libs/core/AutoBuild.js const debug = !!Config.AutoBuild.DebugMode; 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) + getLocaleString(sdk.locale.text.Strength), + getLocaleString(sdk.locale.text.Energy), + getLocaleString(sdk.locale.text.Dexterity), + getLocaleString(sdk.locale.text.Vitality) ]; let prevLevel = me.charlvl; @@ -34,196 +35,196 @@ let prevLevel = me.charlvl; 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; - } + 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; + 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 + 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 () { - let stats = AutoBuildTemplate[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" + let stats = AutoBuildTemplate[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") && (sdk.stats.Strength <= id && id <= sdk.stats.Vitality); - - 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] + } + + // We silently ignore stats set to -1 + for (let i = 0; i < len; i++) { + let id = stats[i]; + let statIsValid = (typeof id === "number") && (sdk.stats.Strength <= id && id <= sdk.stats.Vitality); + + 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; + return spentEveryPoint; } function getTemplateFilename () { - let buildType = Config.AutoBuild.Template; - let templateFilename = "config/Builds/" + sdk.player.class.nameOf(me.classid) + "." + buildType + ".js"; - return templateFilename; + let buildType = Config.AutoBuild.Template; + let templateFilename = "config/Builds/" + sdk.player.class.nameOf(me.classid) + "." + buildType + ".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 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 + 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 () { - let skills = AutoBuildTemplate[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" + + let skills = AutoBuildTemplate[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; + } + + // 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; } /* @@ -233,37 +234,37 @@ function spendSkillPoints () { */ function main () { - try { - AutoBuild.print("Loaded helper thread"); - - 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 false; - } + try { + AutoBuild.print("Loaded helper thread"); + + 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 false; + } } diff --git a/d2bs/kolbot/threads/CloneKilla.js b/d2bs/kolbot/threads/CloneKilla.js index 60a6a6b53..c6f8324e1 100644 --- a/d2bs/kolbot/threads/CloneKilla.js +++ b/d2bs/kolbot/threads/CloneKilla.js @@ -18,32 +18,32 @@ include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); function main() { - D2Bot.init(); - Config.init(); - Pickit.init(); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - include("scripts/KillDclone.js"); + D2Bot.init(); + Config.init(); + Pickit.init(); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + include("scripts/KillDclone.js"); - if (typeof KillDclone === "function") { - try { - D2Bot.printToConsole("Trying to kill DClone.", sdk.colors.D2Bot.DarkGold); - KillDclone.call(); - } catch (e) { - Misc.errorReport(e, "CloneKilla.js"); - } - } + if (typeof KillDclone === "function") { + try { + D2Bot.printToConsole("Trying to kill DClone.", sdk.colors.D2Bot.DarkGold); + KillDclone.call(); + } catch (e) { + Misc.errorReport(e, "CloneKilla.js"); + } + } - try { - quit(); - } finally { - while (me.ingame) { - delay(100); - } - } + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } - return true; + return true; } diff --git a/d2bs/kolbot/threads/HeartBeat.js b/d2bs/kolbot/threads/HeartBeat.js index cd7969b00..264ea0831 100644 --- a/d2bs/kolbot/threads/HeartBeat.js +++ b/d2bs/kolbot/threads/HeartBeat.js @@ -6,48 +6,48 @@ */ function main() { - include("critical.js"); // required - D2Bot.init(); - console.log("Heartbeat loaded"); - - function togglePause() { - let script = getScript(); - - if (script) { - do { - if (script.name.includes(".dbj")) { - if (script.running) { - console.log("ÿc1Pausing ÿc0" + script.name); - script.pause(); - } else { - console.log("ÿc2Resuming ÿc0" + script.name); - script.resume(); - } - } - } while (script.getNext()); - } - - return true; - } - - // Event functions - function KeyEvent(key) { - switch (key) { - case sdk.keys.PauseBreak: - if (me.ingame) { - break; - } - - togglePause(); - - break; - } - } - - addEventListener("keyup", KeyEvent); - - while (true) { - D2Bot.heartBeat(); - delay(1000); - } + include("critical.js"); // required + D2Bot.init(); + console.log("Heartbeat loaded"); + + function togglePause() { + let script = getScript(); + + if (script) { + do { + if (script.name.includes(".dbj")) { + if (script.running) { + console.log("ÿc1Pausing ÿc0" + script.name); + script.pause(); + } else { + console.log("ÿc2Resuming ÿc0" + script.name); + script.resume(); + } + } + } while (script.getNext()); + } + + return true; + } + + // Event functions + function KeyEvent(key) { + switch (key) { + case sdk.keys.PauseBreak: + if (me.ingame) { + break; + } + + togglePause(); + + break; + } + } + + addEventListener("keyup", KeyEvent); + + while (true) { + D2Bot.heartBeat(); + delay(1000); + } } diff --git a/d2bs/kolbot/threads/Party.js b/d2bs/kolbot/threads/Party.js index 268291c2c..d90d83b52 100644 --- a/d2bs/kolbot/threads/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -19,224 +19,228 @@ include("systems/gameaction/GameAction.js"); include("oog/ShitList.js"); function main() { - Config.init(); + Config.init(); - /** @type {string[][]} */ - let [shitList, scriptList] = [[], []]; - let myPartyId, player, currScript; - let playerLevels = {}; - let partyTick = getTickCount(); + /** @type {string[][]} */ + let [shitList, scriptList] = [[], []]; + let myPartyId, player, currScript; + let playerLevels = {}; + let partyTick = getTickCount(); - if (!me.gameserverip) { - console.log("Shutting down party thread, it's not needed on single player"); - return true; - } + if (!me.gameserverip) { + console.log("Shutting down party thread, it's not needed on single player"); + return true; + } - /** + /** * Format the event message here to prevent repetitive code * @param {string[]} arr * @param {Player | string} player * @param {string} [killer] */ - const eventMsg = (arr, player, killer) => { - try { - typeof player === "string" && (player = getParty(player)); - - if (!player || player.name === me.name) return ""; - return (arr - .random() - .format( - ["$name", player.name], - ["$level", player.level], - ["$class", sdk.player.class.nameOf(player.classid)], - ["$killer", killer] - ) - ); - } catch (e1) { - return ""; - } - }; - - const gameEvent = function (mode, param1, param2, name1, name2) { - let msg = ""; - - switch (mode) { - case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." - if (Config.Greetings.length > 0) { - msg = eventMsg(Config.Greetings, name1); - } - - break; - case 0x06: // "%Name1 was Slain by %Name2" - if (Config.DeathMessages.length > 0) { - msg = eventMsg(Config.DeathMessages, name1, name2); - } - - break; - } - - if (msg) { - say(msg); - } - }; - - if (Config.Greetings.length > 0 || Config.DeathMessages.length > 0) { - addEventListener("gameevent", gameEvent); - } - - let quitting = false; - let partyCheck = false; - - const scriptEvent = function (msg) { - if (!!msg && typeof msg === "string") { - switch (msg) { - case "hostileCheck": - partyCheck = true; - - break; - case "quit": - console.debug("Quiting"); - quitting = true; - - break; - default: - let obj; - - try { - obj = JSON.parse(msg); - } catch (e) { - return; - } - - if (obj && obj.hasOwnProperty("currScript")) { - currScript = obj.currScript; - } - - break; - } - } - }; - - addEventListener("scriptmsg", scriptEvent); - - console.log("ÿc2Party thread loaded. Mode: " + (Config.PublicMode === 2 ? "Accept" : "Invite")); - - if (Config.ShitList || Config.UnpartyShitlisted) { - shitList = ShitList.read(); - - console.log(shitList.length + " entries in shit list."); - } - - if (Config.PartyAfterScript) { - scriptList = []; - - for (let i in Scripts) { - if (Scripts.hasOwnProperty(i) && !!Scripts[i]) { - scriptList.push(i); - } - } - } - - // Main loop - while (true) { - if (quitting) { - // we intercepted quit message to toolsthread, go ahead an shut down - return true; - } - - /** + const eventMsg = (arr, player, killer) => { + try { + typeof player === "string" && (player = getParty(player)); + + if (!player || player.name === me.name) return ""; + return (arr + .random() + .format( + ["$name", player.name], + ["$level", player.level], + ["$class", sdk.player.class.nameOf(player.classid)], + ["$killer", killer] + ) + ); + } catch (e1) { + return ""; + } + }; + + const gameEvent = function (mode, param1, param2, name1, name2) { + let msg = ""; + + switch (mode) { + case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." + if (Config.Greetings.length > 0) { + msg = eventMsg(Config.Greetings, name1); + } + + break; + case 0x06: // "%Name1 was Slain by %Name2" + if (Config.DeathMessages.length > 0) { + msg = eventMsg(Config.DeathMessages, name1, name2); + } + + break; + } + + if (msg) { + say(msg); + } + }; + + if (Config.Greetings.length > 0 || Config.DeathMessages.length > 0) { + addEventListener("gameevent", gameEvent); + } + + let quitting = false; + let partyCheck = false; + + const scriptEvent = function (msg) { + if (!!msg && typeof msg === "string") { + switch (msg) { + case "hostileCheck": + partyCheck = true; + + break; + case "quit": + console.debug("Quiting"); + quitting = true; + + break; + default: + let obj; + + try { + obj = JSON.parse(msg); + } catch (e) { + return; + } + + if (obj && obj.hasOwnProperty("currScript")) { + currScript = obj.currScript; + } + + break; + } + } + }; + + addEventListener("scriptmsg", scriptEvent); + + console.log("ÿc2Party thread loaded. Mode: " + (Config.PublicMode === 2 ? "Accept" : "Invite")); + + if (Config.ShitList || Config.UnpartyShitlisted) { + shitList = ShitList.read(); + + console.log(shitList.length + " entries in shit list."); + } + + if (Config.PartyAfterScript) { + scriptList = []; + + for (let i in Scripts) { + if (Scripts.hasOwnProperty(i) && !!Scripts[i]) { + scriptList.push(i); + } + } + } + + // Main loop + while (true) { + if (quitting) { + // we intercepted quit message to toolsthread, go ahead an shut down + return true; + } + + /** * @todo if we are already partied with everyone in game, then this doesn't need to keep checking unless an event happens * e.g. someone joins/leaves game or someone declares hostility * the exception to that is if we are running with Config.Congratulations, in which case we do need to constantly monitor changes */ - if (me.gameReady && (!Config.PartyAfterScript || scriptList.indexOf(currScript) > scriptList.indexOf(Config.PartyAfterScript))) { - player = getParty(); - - if (player) { - myPartyId = player.partyid; - - while (player.getNext()) { - switch (Config.PublicMode) { - case 1: // Invite others - case 3: // Invite others but never accept - if (getPlayerFlag(me.gid, player.gid, sdk.player.flag.Hostile)) { - if (Config.ShitList && shitList.indexOf(player.name) === -1) { - say(player.name + " has been shitlisted."); - shitList.push(player.name); - ShitList.add(player.name); - } - - if (player.partyflag === sdk.party.flag.Cancel) { - clickParty(player, sdk.party.controls.InviteOrCancel); // cancel invitation - delay(100); - } - - break; - } - - if (Config.ShitList && shitList.includes(player.name)) { - break; - } - - if (player.partyflag !== sdk.party.flag.Cancel && player.partyflag !== sdk.party.flag.Accept && player.partyid === sdk.party.NoParty) { - clickParty(player, sdk.party.controls.InviteOrCancel); - delay(100); - } - - if (Config.PublicMode === 3) { - break; - } - // eslint-disable-next-line no-fallthrough - case 2: // Accept invites - if (myPartyId === sdk.party.NoParty) { - if (Config.Leader && player.name !== Config.Leader) { - break; - } - - if (player.partyflag === sdk.party.flag.Accept && (getTickCount() - partyTick >= 2000 || Config.FastParty)) { - clickParty(player, sdk.party.controls.InviteOrCancel); - delay(100); - } - } - - break; - } - - if (Config.UnpartyShitlisted) { - // Add new hostile players to temp shitlist, leader should have Config.ShitList set to true to update the permanent list. - if (getPlayerFlag(me.gid, player.gid, sdk.player.flag.Hostile) && shitList.indexOf(player.name) === -1) { - shitList.push(player.name); - } - - if (shitList.includes(player.name) && myPartyId !== sdk.party.NoParty && player.partyid === myPartyId) { - // Only the one sending invites should say this. - if ([1, 3].includes(Config.PublicMode)) { - say(player.name + " is shitlisted. Do not invite them."); - } - - clickParty(player, sdk.party.controls.Leave); - delay(100); - } - } - - if (Config.Congratulations.length > 0) { - if (player.name !== me.name) { - if (!playerLevels[player.name]) { - playerLevels[player.name] = player.level; - } - - if (player.level > playerLevels[player.name]) { - let msg = eventMsg(Config.Congratulations, player); - msg && say(msg); + if (me.gameReady + && (!Config.PartyAfterScript || scriptList.indexOf(currScript) > scriptList.indexOf(Config.PartyAfterScript))) { + player = getParty(); + + if (player) { + myPartyId = player.partyid; + + while (player.getNext()) { + switch (Config.PublicMode) { + case 1: // Invite others + case 3: // Invite others but never accept + if (getPlayerFlag(me.gid, player.gid, sdk.player.flag.Hostile)) { + if (Config.ShitList && shitList.indexOf(player.name) === -1) { + say(player.name + " has been shitlisted."); + shitList.push(player.name); + ShitList.add(player.name); + } + + if (player.partyflag === sdk.party.flag.Cancel) { + clickParty(player, sdk.party.controls.InviteOrCancel); // cancel invitation + delay(100); + } + + break; + } + + if (Config.ShitList && shitList.includes(player.name)) { + break; + } + + if (player.partyflag !== sdk.party.flag.Cancel + && player.partyflag !== sdk.party.flag.Accept + && player.partyid === sdk.party.NoParty) { + clickParty(player, sdk.party.controls.InviteOrCancel); + delay(100); + } + + if (Config.PublicMode === 3) { + break; + } + // eslint-disable-next-line no-fallthrough + case 2: // Accept invites + if (myPartyId === sdk.party.NoParty) { + if (Config.Leader && player.name !== Config.Leader) { + break; + } + + if (player.partyflag === sdk.party.flag.Accept + && (getTickCount() - partyTick >= 2000 || Config.FastParty)) { + clickParty(player, sdk.party.controls.InviteOrCancel); + delay(100); + } + } + + break; + } + + if (Config.UnpartyShitlisted) { + // Add new hostile players to temp shitlist, leader should have Config.ShitList set to true to update the permanent list. + if (getPlayerFlag(me.gid, player.gid, sdk.player.flag.Hostile) && shitList.indexOf(player.name) === -1) { + shitList.push(player.name); + } + + if (shitList.includes(player.name) && myPartyId !== sdk.party.NoParty && player.partyid === myPartyId) { + // Only the one sending invites should say this. + if ([1, 3].includes(Config.PublicMode)) { + say(player.name + " is shitlisted. Do not invite them."); + } + + clickParty(player, sdk.party.controls.Leave); + delay(100); + } + } + + if (Config.Congratulations.length > 0) { + if (player.name !== me.name) { + if (!playerLevels[player.name]) { + playerLevels[player.name] = player.level; + } + + if (player.level > playerLevels[player.name]) { + let msg = eventMsg(Config.Congratulations, player); + msg && say(msg); - playerLevels[player.name] = player.level; - } - } - } - } - } - } - - delay(500); - } + playerLevels[player.name] = player.level; + } + } + } + } + } + } + + delay(500); + } } diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index f96cf06ba..4ea3f8b66 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename RushThread.js * @author kolton, theBGuy @@ -20,1151 +21,1151 @@ const Overrides = require("../libs/modules/Override"); let count = 0; let silentNameTracker = []; let wpsToGive = Pather.nonTownWpAreas.slice(0).filter(function (area) { - if (area === sdk.areas.HallsofPain) return false; - if (me.classic && area >= sdk.areas.Harrogath) return false; - return true; + if (area === sdk.areas.HallsofPain) return false; + if (me.classic && area >= sdk.areas.Harrogath) return false; + return true; }); function wpEvent (who, msg) { - if (typeof msg === "string" && msg === "gotwp" || msg === "Failed to get wp") { - count++; - !silentNameTracker.includes(who) && silentNameTracker.push(who); - } + if (typeof msg === "string" && msg === "gotwp" || msg === "Failed to get wp") { + count++; + !silentNameTracker.includes(who) && silentNameTracker.push(who); + } } function giveWP () { - let wp = Game.getObject("waypoint"); - let success = false; - if (wp && !me.inTown && wpsToGive.includes(me.area)) { - try { - addEventListener("chatmsg", wpEvent); - let playerCount = Misc.getPartyCount(); - let mobCount = getUnits(sdk.unittype.Monster).filter(mon => mon.distance <= 15 && mon.attackable).length; - mobCount > 0 && Attack.securePosition(me.x, me.y, 15, Time.seconds(30), true); - wp.distance > 5 && Pather.moveToUnit(wp); - Pather.makePortal(); - say("wp"); - let tick = getTickCount(); - while (getTickCount() - tick < Time.minutes(2)) { - let player = Game.getPlayer(); - if (player) { - do { - if (player.name !== me.name && !silentNameTracker.includes(player.name)) { - silentNameTracker.push(player.name); - } - } while (player.getNext()); - } - if (count === playerCount || (silentNameTracker.length === playerCount && Misc.getNearbyPlayerCount() === 0)) { - wpsToGive.remove(me.area); - success = true; - break; - } - delay(50); - } - } catch (e) { - console.error(e); - Config.LocalChat.Enabled && say("Failed to give wp"); - } finally { - removeEventListener("chatmsg", wpEvent); - silentNameTracker = []; - count = 0; - } - return success; - } - - return false; + let wp = Game.getObject("waypoint"); + let success = false; + if (wp && !me.inTown && wpsToGive.includes(me.area)) { + try { + addEventListener("chatmsg", wpEvent); + let playerCount = Misc.getPartyCount(); + let mobCount = getUnits(sdk.unittype.Monster).filter(mon => mon.distance <= 15 && mon.attackable).length; + mobCount > 0 && Attack.securePosition(me.x, me.y, 15, Time.seconds(30), true); + wp.distance > 5 && Pather.moveToUnit(wp); + Pather.makePortal(); + say("wp"); + let tick = getTickCount(); + while (getTickCount() - tick < Time.minutes(2)) { + let player = Game.getPlayer(); + if (player) { + do { + if (player.name !== me.name && !silentNameTracker.includes(player.name)) { + silentNameTracker.push(player.name); + } + } while (player.getNext()); + } + if (count === playerCount || (silentNameTracker.length === playerCount && Misc.getNearbyPlayerCount() === 0)) { + wpsToGive.remove(me.area); + success = true; + break; + } + delay(50); + } + } catch (e) { + console.error(e); + Config.LocalChat.Enabled && say("Failed to give wp"); + } finally { + removeEventListener("chatmsg", wpEvent); + silentNameTracker = []; + count = 0; + } + return success; + } + + return false; } new Overrides.Override(Pather, Pather.useWaypoint, function(orignal, targetArea, check) { - if (orignal(targetArea, check)) { - return (Config.Rusher.GiveWps && giveWP()) || true; - } else { - print("failed"); + if (orignal(targetArea, check)) { + return (Config.Rusher.GiveWps && giveWP()) || true; + } else { + print("failed"); - return false; - } + return false; + } }).apply(); function main () { - let tick; + let tick; - this.log = function (msg = "", sayMsg = true) { - console.log(msg); - sayMsg && say(msg); - }; + this.log = function (msg = "", sayMsg = true) { + console.log(msg); + sayMsg && say(msg); + }; - this.playerIn = function (area) { - !area && (area = me.area); + this.playerIn = function (area) { + !area && (area = me.area); - let party = getParty(); + let party = getParty(); - if (party) { - do { - if (party.name !== me.name && party.area === area) { - return true; - } - } while (party.getNext()); - } + if (party) { + do { + if (party.name !== me.name && party.area === area) { + return true; + } + } while (party.getNext()); + } - return false; - }; + return false; + }; - this.bumperCheck = function () { - let bumperLevelReq = [20, 40, 60][me.diff]; - return Misc.checkPartyLevel(bumperLevelReq); - }; + this.bumperCheck = function () { + let bumperLevelReq = [20, 40, 60][me.diff]; + return Misc.checkPartyLevel(bumperLevelReq); + }; - this.playersInAct = function (act) { - !act && (act = me.act); + this.playersInAct = function (act) { + !act && (act = me.act); - let area = sdk.areas.townOfAct(act); - let party = getParty(); + let area = sdk.areas.townOfAct(act); + let party = getParty(); - if (party) { - do { - if (party.name !== me.name && party.area !== area) { - return false; - } - } while (party.getNext()); - } + if (party) { + do { + if (party.name !== me.name && party.area !== area) { + return false; + } + } while (party.getNext()); + } - return true; - }; + return true; + }; - this.andariel = function () { - this.log("starting andariel"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.CatacombsLvl2, true) && Precast.doPrecast(true); + this.andariel = function () { + this.log("starting andariel"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.CatacombsLvl2, true) && Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true) + if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true) || !Pather.moveTo(22582, 9612)) { - throw new Error("andy failed"); - } + throw new Error("andy failed"); + } - Pather.makePortal(); - Attack.securePosition(me.x, me.y, 40, 3000, true); - this.log("1"); + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 40, 3000, true); + this.log("1"); - while (!this.playerIn()) { - Pather.moveTo(22582, 9612); - delay(250); - } + while (!this.playerIn()) { + Pather.moveTo(22582, 9612); + delay(250); + } - Attack.kill(sdk.monsters.Andariel); - this.log("2"); - Pather.moveTo(22582, 9612); + Attack.kill(sdk.monsters.Andariel); + this.log("2"); + Pather.moveTo(22582, 9612); - while (this.playerIn()) { - delay(250); - } + while (this.playerIn()) { + delay(250); + } - Pather.usePortal(null, me.name); - this.log("a2"); - Town.goToTown(2); + Pather.usePortal(null, me.name); + this.log("a2"); + Town.goToTown(2); - while (!this.playersInAct(2)) { - delay(250); - } + while (!this.playersInAct(2)) { + delay(250); + } - return true; - }; + return true; + }; - this.cube = function () { - if (me.normal) { - this.log("starting cube"); - Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); - Precast.doPrecast(true); + this.cube = function () { + if (me.normal) { + this.log("starting cube"); + Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) + if (!Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { - throw new Error("cube failed"); - } + throw new Error("cube failed"); + } - Pather.makePortal(); - Attack.securePosition(me.x, me.y, 30, 3000, true); - this.log("1"); + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 30, 3000, true); + this.log("1"); - while (!this.playerIn()) { - delay(100); - } + while (!this.playerIn()) { + delay(100); + } - while (this.playerIn()) { - delay(100); - } + while (this.playerIn()) { + delay(100); + } - Pather.usePortal(null, me.name); - } + Pather.usePortal(null, me.name); + } - return true; - }; + return true; + }; - this.amulet = function () { - this.log("starting amulet"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.LostCity, true) && Precast.doPrecast(true); + this.amulet = function () { + this.log("starting amulet"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.LostCity, true) && Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true) + if (!Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true) || !Pather.moveTo(15044, 14045)) { - throw new Error("amulet failed"); - } + throw new Error("amulet failed"); + } - Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); - this.log("1"); + this.log("1"); - while (!this.playerIn()) { - delay(100); - } + while (!this.playerIn()) { + delay(100); + } - while (this.playerIn()) { - delay(100); - } + while (this.playerIn()) { + delay(100); + } - Pather.usePortal(null, me.name); + Pather.usePortal(null, me.name); - return true; - }; + return true; + }; - this.staff = function () { - this.log("starting staff"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.FarOasis, true) && Precast.doPrecast(true); + this.staff = function () { + this.log("starting staff"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FarOasis, true) && Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true) + if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { - throw new Error("staff failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, 30, 3000, true); - this.log("1"); - - while (!this.playerIn()) { - delay(100); - } - - while (this.playerIn()) { - delay(100); - } - - Pather.usePortal(null, me.name); - - return true; - }; - - this.summoner = function () { - // right up 25449 5081 (25431, 5011) - // left up 25081 5446 (25011, 5446) - // right down 25830 5447 (25866, 5431) - // left down 25447 5822 (25431, 5861) - - this.log("starting summoner"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArcaneSanctuary, true) && Precast.doPrecast(true); - - let preset = Game.getPresetObject(sdk.areas.ArcaneSanctuary, sdk.quest.chest.Journal); - let spot = {}; - - switch (preset.roomx * 5 + preset.x) { - case 25011: - spot = {x: 25081, y: 5446}; - break; - case 25866: - spot = {x: 25830, y: 5447}; - break; - case 25431: - switch (preset.roomy * 5 + preset.y) { - case 5011: - spot = {x: 25449, y: 5081}; - break; - case 5861: - spot = {x: 25447, y: 5822}; - break; - } - - break; - } - - if (!Pather.moveToUnit(spot)) { - throw new Error("summoner failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000); - this.log("1"); - - while (!this.playerIn()) { - Pather.moveToUnit(spot); - Attack.securePosition(me.x, me.y, 25, 500); - delay(250); - } - - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); - Attack.kill(sdk.monsters.Summoner); - this.log("2"); - - while (this.playerIn()) { - delay(100); - } - - Pickit.pickItems(); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); - - let redPortal = Game.getObject(sdk.objects.RedPortal); - - if (!redPortal || !this.usePortal(null, null, redPortal)) { - if (!Misc.poll(() => { - let journal = Game.getObject(sdk.quest.chest.Journal); - - if (journal && journal.interact()) { - delay(1000); - me.cancel(); - } - - redPortal = Pather.getPortal(sdk.areas.CanyonofMagic); - - return (redPortal && Pather.usePortal(null, null, redPortal)); - })) throw new Error("summoner failed"); - } - - return true; - }; - - this.duriel = function () { - this.log("starting duriel"); - - if (me.inTown) { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CanyonofMagic, true); - } else { - giveWP(); - } - - Precast.doPrecast(true); - - if (!Pather.moveToExit(getRoom().correcttomb, true) + throw new Error("staff failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 30, 3000, true); + this.log("1"); + + while (!this.playerIn()) { + delay(100); + } + + while (this.playerIn()) { + delay(100); + } + + Pather.usePortal(null, me.name); + + return true; + }; + + this.summoner = function () { + // right up 25449 5081 (25431, 5011) + // left up 25081 5446 (25011, 5446) + // right down 25830 5447 (25866, 5431) + // left down 25447 5822 (25431, 5861) + + this.log("starting summoner"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArcaneSanctuary, true) && Precast.doPrecast(true); + + let preset = Game.getPresetObject(sdk.areas.ArcaneSanctuary, sdk.quest.chest.Journal); + let spot = {}; + + switch (preset.roomx * 5 + preset.x) { + case 25011: + spot = { x: 25081, y: 5446 }; + break; + case 25866: + spot = { x: 25830, y: 5447 }; + break; + case 25431: + switch (preset.roomy * 5 + preset.y) { + case 5011: + spot = { x: 25449, y: 5081 }; + break; + case 5861: + spot = { x: 25447, y: 5822 }; + break; + } + + break; + } + + if (!Pather.moveToUnit(spot)) { + throw new Error("summoner failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 25, 3000); + this.log("1"); + + while (!this.playerIn()) { + Pather.moveToUnit(spot); + Attack.securePosition(me.x, me.y, 25, 500); + delay(250); + } + + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); + Attack.kill(sdk.monsters.Summoner); + this.log("2"); + + while (this.playerIn()) { + delay(100); + } + + Pickit.pickItems(); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); + + let redPortal = Game.getObject(sdk.objects.RedPortal); + + if (!redPortal || !this.usePortal(null, null, redPortal)) { + if (!Misc.poll(() => { + let journal = Game.getObject(sdk.quest.chest.Journal); + + if (journal && journal.interact()) { + delay(1000); + me.cancel(); + } + + redPortal = Pather.getPortal(sdk.areas.CanyonofMagic); + + return (redPortal && Pather.usePortal(null, null, redPortal)); + })) throw new Error("summoner failed"); + } + + return true; + }; + + this.duriel = function () { + this.log("starting duriel"); + + if (me.inTown) { + Town.doChores(); + Pather.useWaypoint(sdk.areas.CanyonofMagic, true); + } else { + giveWP(); + } + + Precast.doPrecast(true); + + if (!Pather.moveToExit(getRoom().correcttomb, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder)) { - throw new Error("duriel failed"); - } + throw new Error("duriel failed"); + } - Pather.makePortal(); - Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); - this.log("1"); + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); + this.log("1"); - while (!this.playerIn()) { - delay(100); - } + while (!this.playerIn()) { + delay(100); + } - while (this.playerIn()) { - delay(100); - } + while (this.playerIn()) { + delay(100); + } - while (!Game.getObject(sdk.objects.PortaltoDurielsLair)) { - delay(500); - } + while (!Game.getObject(sdk.objects.PortaltoDurielsLair)) { + delay(500); + } - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); - Attack.kill(sdk.monsters.Duriel); - Pickit.pickItems(); + Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); + Attack.kill(sdk.monsters.Duriel); + Pickit.pickItems(); - Pather.teleport = false; + Pather.teleport = false; - Pather.moveTo(22579, 15706); + Pather.moveTo(22579, 15706); - Pather.teleport = true; + Pather.teleport = true; - Pather.moveTo(22577, 15649, 10); - Pather.moveTo(22577, 15609, 10); - Pather.makePortal(); - this.log("1"); + Pather.moveTo(22577, 15649, 10); + Pather.moveTo(22577, 15609, 10); + Pather.makePortal(); + this.log("1"); - while (!this.playerIn()) { - delay(100); - } + while (!this.playerIn()) { + delay(100); + } - if (!Pather.usePortal(null, me.name)) { - Town.goToTown(); - } + if (!Pather.usePortal(null, me.name)) { + Town.goToTown(); + } - Pather.useWaypoint(sdk.areas.PalaceCellarLvl1); - Pather.moveToExit([sdk.areas.HaremLvl2, sdk.areas.HaremLvl1], true); - Pather.moveTo(10022, 5047); - this.log("a3"); - Town.goToTown(3); - Town.doChores(); + Pather.useWaypoint(sdk.areas.PalaceCellarLvl1); + Pather.moveToExit([sdk.areas.HaremLvl2, sdk.areas.HaremLvl1], true); + Pather.moveTo(10022, 5047); + this.log("a3"); + Town.goToTown(3); + Town.doChores(); - while (!this.playersInAct(3)) { - delay(250); - } + while (!this.playersInAct(3)) { + delay(250); + } - return true; - }; + return true; + }; - // re-write to prevent fail to complete quest due to killing council from to far away - this.travincal = function () { - this.log("starting travincal"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.Travincal, true) && Precast.doPrecast(true); + // re-write to prevent fail to complete quest due to killing council from to far away + this.travincal = function () { + this.log("starting travincal"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.Travincal, true) && Precast.doPrecast(true); - let coords = [me.x, me.y]; + let coords = [me.x, me.y]; - Pather.moveTo(coords[0] + 23, coords[1] - 102); - Pather.makePortal(); - Attack.securePosition(me.x, me.y, 40, 3000); - this.log("1"); + Pather.moveTo(coords[0] + 23, coords[1] - 102); + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 40, 3000); + this.log("1"); - while (!this.playerIn()) { - delay(250); - } + while (!this.playerIn()) { + delay(250); + } - Pather.moveTo(coords[0] + 30, coords[1] - 134); - Pather.moveTo(coords[0] + 86, coords[1] - 130); - Pather.moveTo(coords[0] + 71, coords[1] - 94); - Attack.securePosition(me.x, me.y, 40, 3000); + Pather.moveTo(coords[0] + 30, coords[1] - 134); + Pather.moveTo(coords[0] + 86, coords[1] - 130); + Pather.moveTo(coords[0] + 71, coords[1] - 94); + Attack.securePosition(me.x, me.y, 40, 3000); - Pather.moveTo(coords[0] + 23, coords[1] - 102); - Pather.makePortal(); - this.log("2"); - Pather.usePortal(null, me.name); + Pather.moveTo(coords[0] + 23, coords[1] - 102); + Pather.makePortal(); + this.log("2"); + Pather.usePortal(null, me.name); - return true; - }; + return true; + }; - this.mephisto = function () { - this.log("starting mephisto"); + this.mephisto = function () { + this.log("starting mephisto"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true) && Pather.moveTo(17692, 8023) && Pather.makePortal(); - delay(2000); - this.log("1"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true) && Pather.moveTo(17692, 8023) && Pather.makePortal(); + delay(2000); + this.log("1"); - while (!this.playerIn()) { - delay(250); - } + while (!this.playerIn()) { + delay(250); + } - Pather.moveTo(17591, 8070); - Attack.kill(sdk.monsters.Mephisto); - Pickit.pickItems(); - Pather.moveTo(17692, 8023) && Pather.makePortal(); - this.log("2"); + Pather.moveTo(17591, 8070); + Attack.kill(sdk.monsters.Mephisto); + Pickit.pickItems(); + Pather.moveTo(17692, 8023) && Pather.makePortal(); + this.log("2"); - while (this.playerIn()) { - delay(250); - } + while (this.playerIn()) { + delay(250); + } - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 40, 3000); + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 40, 3000); - let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - if (hydra) { - do { - while (!hydra.dead && hydra.hp > 0) { - delay(500); - } - } while (hydra.getNext()); - } + if (hydra) { + do { + while (!hydra.dead && hydra.hp > 0) { + delay(500); + } + } while (hydra.getNext()); + } - Pather.makePortal(); - Pather.moveTo(17581, 8070); - this.log("1"); + Pather.makePortal(); + Pather.moveTo(17581, 8070); + this.log("1"); - while (!this.playerIn()) { - delay(250); - } - - this.log("a4"); - - while (!this.playersInAct(4)) { - delay(250); - } - - delay(2000); - Pather.usePortal(null); - - return true; - }; - - this.diablo = function () { - include("core/Common/Diablo.js"); - this.log("starting diablo"); - - function inviteIn () { - Pather.moveTo(7763, 5267) && Pather.makePortal(); - Pather.moveTo(7727, 5267); - this.log("1"); - - while (!this.playerIn()) { - delay(250); - } - - return true; - } - - Town.doChores(); - Pather.useWaypoint(sdk.areas.RiverofFlame); - Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); + while (!this.playerIn()) { + delay(250); + } + + this.log("a4"); + + while (!this.playersInAct(4)) { + delay(250); + } + + delay(2000); + Pather.usePortal(null); + + return true; + }; + + this.diablo = function () { + include("core/Common/Diablo.js"); + this.log("starting diablo"); + + function inviteIn () { + Pather.moveTo(7763, 5267) && Pather.makePortal(); + Pather.moveTo(7727, 5267); + this.log("1"); + + while (!this.playerIn()) { + delay(250); + } + + return true; + } + + Town.doChores(); + Pather.useWaypoint(sdk.areas.RiverofFlame); + Precast.doPrecast(true); + if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); - Common.Diablo.initLayout(); - Config.Diablo.Fast = true; - Config.Diablo.SealLeader = false; + Common.Diablo.initLayout(); + Config.Diablo.Fast = true; + Config.Diablo.SealLeader = false; - try { - Common.Diablo.runSeals(Config.Diablo.SealOrder); - print("Attempting to find Diablo"); - inviteIn() && Common.Diablo.diabloPrep(); - } catch (error) { - print("Diablo wasn't found. Checking seals."); - Common.Diablo.runSeals(Config.Diablo.SealOrder); - inviteIn() && Common.Diablo.diabloPrep(); - } - - Attack.kill(sdk.monsters.Diablo); - this.log("2"); - - if (me.expansion) { - this.log("a5"); - - while (!this.playersInAct(5)) { - delay(250); - } - } - - Pickit.pickItems(); - !Pather.usePortal(null, me.name) && Town.goToTown(); - - return true; - }; - - this.ancients = function () { - if (me.hell && !Config.Rusher.HellAncients) { - if (!Config.Rusher.GiveWps) { - this.log("Hell rush complete~"); - delay(500); - quit(); - } - - return false; - } - - if (!this.bumperCheck()) { - if (!Config.Rusher.GiveWps) { - this.log("No eligible bumpers detected. Rush complete~"); - delay(500); - quit(); - } - - return false; - } - - include("core/Common/Ancients.js"); - this.log("starting ancients"); - - Town.doChores(); - Pather.useWaypoint(sdk.areas.AncientsWay, true) && Precast.doPrecast(true); - - if (!Pather.moveToExit(sdk.areas.ArreatSummit, true)) { - throw new Error("Failed to go to Ancients way."); - } - - Pather.moveTo(10089, 12622); - Pather.makePortal(); - this.log("3"); - - while (!this.playerIn()) { - delay(250); - } - - Pather.moveTo(10048, 12628); - Common.Ancients.touchAltar(); - Common.Ancients.startAncients(); - - Pather.moveTo(10089, 12622); - me.cancel(); - Pather.makePortal(); - - while (this.playerIn()) { - delay(100); - } - - !Pather.usePortal(null, me.name) && Town.goToTown(); - - return true; - }; - - this.baal = function () { - if (me.hell) { - if (!Config.Rusher.GiveWps) { - this.log("Baal not done in Hell ~Hell rush complete~"); - delay(500); - quit(); - } - wpsToGive.remove(sdk.areas.WorldstoneLvl2); - - return false; - } - - if (!this.bumperCheck()) { - if (!Config.Rusher.GiveWps) { - this.log("No eligible bumpers detected. ~Rush complete~"); - delay(500); - quit(); - } - wpsToGive.remove(sdk.areas.WorldstoneLvl2); - - return false; - } - - include("core/Common/Baal.js"); - this.log("starting baal"); - - if (me.inTown) { - Town.doChores(); - Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); - - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { - throw new Error("Failed to move to Throne of Destruction."); - } - } - - Pather.moveTo(15113, 5040); - Attack.clear(15); - Common.Baal.clearThrone(); - - if (!Common.Baal.clearWaves()) { - throw new Error("Couldn't clear baal waves"); - } - - Common.Baal.clearThrone(); - me.checkForMobs({range: 30}) && this.clearWaves(); // ensure waves are actually done - Pather.moveTo(15090, 5008); - delay(5000); - Precast.doPrecast(true); - Misc.poll(() => !Game.getMonster(sdk.monsters.ThroneBaal), Time.minutes(3), 1000); - - let portal = Game.getObject(sdk.objects.WorldstonePortal); - - if (portal) { - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } - - Pather.moveTo(15213, 5908); - Pather.makePortal(); - Pather.moveTo(15170, 5950); - delay(1000); - this.log("3"); - - while (!this.playerIn()) { - delay(250); - } - - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); - - return true; - }; - - this.clearArea = function (area) { - Pather.journeyTo(area); - Attack.clearLevel(0); - this.log("Done clearing area: " + area); - }; - - // Quests - this.cain = function () { - if (!Config.Rusher.Cain) return true; - - this.log("starting cain"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.DarkWood, true) && Precast.doPrecast(true); - - if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - throw new Error("Failed to move to Tree of Inifuss"); - } - - let tree = Game.getObject(sdk.quest.chest.InifussTree); - !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Attack.securePosition(me.x, me.y, 40, 3000, true); - !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Pather.makePortal(); - this.log("1"); - tick = getTickCount(); - - while (getTickCount() - tick < Time.minutes(2)) { - if (tree.mode) { - break; - } - Attack.securePosition(me.x, me.y, 20, 1000); - } - - Pather.usePortal(1) || Town.goToTown(); - Pather.useWaypoint(sdk.areas.StonyField, true); - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); - Attack.securePosition(me.x, me.y, 40, 3000, true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); - Pather.makePortal(); - this.log("1"); - - tick = getTickCount(); - - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.usePortal(sdk.areas.Tristram)) { - break; - } - Attack.securePosition(me.x, me.y, 35, 1000); - } - - if (me.inArea(sdk.areas.Tristram)) { - Pather.moveTo(me.x, me.y + 6); - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - - if (gibbet && !gibbet.mode) { - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.CainsJail, 0, 0, true, true)) { - throw new Error("Failed to move to Cain's Jail"); - } - - Attack.securePosition(gibbet.x, gibbet.y, 20, 3000); - Pather.makePortal(); - this.log("1"); - - tick = getTickCount(); - - while (getTickCount() - tick < Time.minutes(2)) { - if (gibbet.mode) { - break; - } - Attack.securePosition(me.x, me.y, 10, 1000); - } - } - } - - return true; - }; - - this.radament = function () { - if (!Config.Rusher.Radament) return false; - - this.log("starting radament"); - - let moveIntoPos = function (unit, range) { - let coords = []; - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 105, -105, 120, -120, 135, -135, 150, -150, 180]; - - for (let i = 0; i < angles.length; i += 1) { - let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); - let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); - - try { - if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { - coords.push({ - x: coordx, - y: coordy - }); - } - } catch (e) { - continue; - } - } - - if (coords.length > 0) { - coords.sort(Sort.units); - - return Pather.moveToUnit(coords[0]); - } - - return false; - }; - - Pather.useWaypoint(sdk.areas.A2SewersLvl2, true) && Precast.doPrecast(false); - Pather.moveToExit(sdk.areas.A2SewersLvl3, true); - - let radaPreset = Game.getPresetObject(sdk.areas.A2SewersLvl3, sdk.quest.chest.HoradricScrollChest); - let radaCoords = { - area: sdk.areas.A2SewersLvl3, - x: radaPreset.roomx * 5 + radaPreset.x, - y: radaPreset.roomy * 5 + radaPreset.y - }; - - moveIntoPos(radaCoords, 50); - let rada = Misc.poll(() => Game.getMonster(sdk.monsters.Radament), 1500, 500); - - rada ? moveIntoPos(rada, 60) : print("radament unit not found"); - Attack.securePosition(me.x, me.y, 35, 3000); - Pather.makePortal(); - this.log("1"); + try { + Common.Diablo.runSeals(Config.Diablo.SealOrder); + print("Attempting to find Diablo"); + inviteIn() && Common.Diablo.diabloPrep(); + } catch (error) { + print("Diablo wasn't found. Checking seals."); + Common.Diablo.runSeals(Config.Diablo.SealOrder); + inviteIn() && Common.Diablo.diabloPrep(); + } + + Attack.kill(sdk.monsters.Diablo); + this.log("2"); + + if (me.expansion) { + this.log("a5"); + + while (!this.playersInAct(5)) { + delay(250); + } + } + + Pickit.pickItems(); + !Pather.usePortal(null, me.name) && Town.goToTown(); + + return true; + }; + + this.ancients = function () { + if (me.hell && !Config.Rusher.HellAncients) { + if (!Config.Rusher.GiveWps) { + this.log("Hell rush complete~"); + delay(500); + quit(); + } + + return false; + } + + if (!this.bumperCheck()) { + if (!Config.Rusher.GiveWps) { + this.log("No eligible bumpers detected. Rush complete~"); + delay(500); + quit(); + } + + return false; + } + + include("core/Common/Ancients.js"); + this.log("starting ancients"); + + Town.doChores(); + Pather.useWaypoint(sdk.areas.AncientsWay, true) && Precast.doPrecast(true); + + if (!Pather.moveToExit(sdk.areas.ArreatSummit, true)) { + throw new Error("Failed to go to Ancients way."); + } + + Pather.moveTo(10089, 12622); + Pather.makePortal(); + this.log("3"); + + while (!this.playerIn()) { + delay(250); + } + + Pather.moveTo(10048, 12628); + Common.Ancients.touchAltar(); + Common.Ancients.startAncients(); + + Pather.moveTo(10089, 12622); + me.cancel(); + Pather.makePortal(); + + while (this.playerIn()) { + delay(100); + } + + !Pather.usePortal(null, me.name) && Town.goToTown(); + + return true; + }; + + this.baal = function () { + if (me.hell) { + if (!Config.Rusher.GiveWps) { + this.log("Baal not done in Hell ~Hell rush complete~"); + delay(500); + quit(); + } + wpsToGive.remove(sdk.areas.WorldstoneLvl2); + + return false; + } + + if (!this.bumperCheck()) { + if (!Config.Rusher.GiveWps) { + this.log("No eligible bumpers detected. ~Rush complete~"); + delay(500); + quit(); + } + wpsToGive.remove(sdk.areas.WorldstoneLvl2); + + return false; + } + + include("core/Common/Baal.js"); + this.log("starting baal"); + + if (me.inTown) { + Town.doChores(); + Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { + throw new Error("Failed to move to Throne of Destruction."); + } + } + + Pather.moveTo(15113, 5040); + Attack.clear(15); + Common.Baal.clearThrone(); + + if (!Common.Baal.clearWaves()) { + throw new Error("Couldn't clear baal waves"); + } + + Common.Baal.clearThrone(); + me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done + Pather.moveTo(15090, 5008); + delay(5000); + Precast.doPrecast(true); + Misc.poll(() => !Game.getMonster(sdk.monsters.ThroneBaal), Time.minutes(3), 1000); + + let portal = Game.getObject(sdk.objects.WorldstonePortal); + + if (portal) { + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); + } + + Pather.moveTo(15213, 5908); + Pather.makePortal(); + Pather.moveTo(15170, 5950); + delay(1000); + this.log("3"); + + while (!this.playerIn()) { + delay(250); + } + + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); + + return true; + }; + + this.clearArea = function (area) { + Pather.journeyTo(area); + Attack.clearLevel(0); + this.log("Done clearing area: " + area); + }; + + // Quests + this.cain = function () { + if (!Config.Rusher.Cain) return true; + + this.log("starting cain"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.DarkWood, true) && Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + throw new Error("Failed to move to Tree of Inifuss"); + } + + let tree = Game.getObject(sdk.quest.chest.InifussTree); + !!tree && tree.distance > 5 && Pather.moveToUnit(tree); + Attack.securePosition(me.x, me.y, 40, 3000, true); + !!tree && tree.distance > 5 && Pather.moveToUnit(tree); + Pather.makePortal(); + this.log("1"); + tick = getTickCount(); + + while (getTickCount() - tick < Time.minutes(2)) { + if (tree.mode) { + break; + } + Attack.securePosition(me.x, me.y, 20, 1000); + } + + Pather.usePortal(1) || Town.goToTown(); + Pather.useWaypoint(sdk.areas.StonyField, true); + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); + Attack.securePosition(me.x, me.y, 40, 3000, true); + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); + Pather.makePortal(); + this.log("1"); + + tick = getTickCount(); + + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.usePortal(sdk.areas.Tristram)) { + break; + } + Attack.securePosition(me.x, me.y, 35, 1000); + } + + if (me.inArea(sdk.areas.Tristram)) { + Pather.moveTo(me.x, me.y + 6); + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + + if (gibbet && !gibbet.mode) { + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.CainsJail, 0, 0, true, true)) { + throw new Error("Failed to move to Cain's Jail"); + } + + Attack.securePosition(gibbet.x, gibbet.y, 20, 3000); + Pather.makePortal(); + this.log("1"); + + tick = getTickCount(); + + while (getTickCount() - tick < Time.minutes(2)) { + if (gibbet.mode) { + break; + } + Attack.securePosition(me.x, me.y, 10, 1000); + } + } + } + + return true; + }; + + this.radament = function () { + if (!Config.Rusher.Radament) return false; + + this.log("starting radament"); + + let moveIntoPos = function (unit, range) { + let coords = []; + let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); + let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 105, -105, 120, -120, 135, -135, 150, -150, 180]; + + for (let i = 0; i < angles.length; i += 1) { + let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); + let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); + + try { + if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { + coords.push({ + x: coordx, + y: coordy + }); + } + } catch (e) { + continue; + } + } + + if (coords.length > 0) { + coords.sort(Sort.units); + + return Pather.moveToUnit(coords[0]); + } + + return false; + }; + + Pather.useWaypoint(sdk.areas.A2SewersLvl2, true) && Precast.doPrecast(false); + Pather.moveToExit(sdk.areas.A2SewersLvl3, true); + + let radaPreset = Game.getPresetObject(sdk.areas.A2SewersLvl3, sdk.quest.chest.HoradricScrollChest); + let radaCoords = { + area: sdk.areas.A2SewersLvl3, + x: radaPreset.roomx * 5 + radaPreset.x, + y: radaPreset.roomy * 5 + radaPreset.y + }; + + moveIntoPos(radaCoords, 50); + let rada = Misc.poll(() => Game.getMonster(sdk.monsters.Radament), 1500, 500); + + rada ? moveIntoPos(rada, 60) : print("radament unit not found"); + Attack.securePosition(me.x, me.y, 35, 3000); + Pather.makePortal(); + this.log("1"); - while (!this.playerIn()) { - delay(200); - } + while (!this.playerIn()) { + delay(200); + } - Attack.kill(sdk.monsters.Radament); + Attack.kill(sdk.monsters.Radament); - let returnSpot = { - x: me.x, - y: me.y - }; + let returnSpot = { + x: me.x, + y: me.y + }; - this.log("2"); - Pickit.pickItems(); - Attack.securePosition(me.x, me.y, 30, 3000); + this.log("2"); + Pickit.pickItems(); + Attack.securePosition(me.x, me.y, 30, 3000); - while (this.playerIn()) { - delay(200); - } + while (this.playerIn()) { + delay(200); + } - Pather.moveToUnit(returnSpot); - Pather.makePortal(); - this.log("all in"); + Pather.moveToUnit(returnSpot); + Pather.makePortal(); + this.log("all in"); - while (!this.playerIn()) { - delay(200); - } + while (!this.playerIn()) { + delay(200); + } - Misc.poll(() => !Game.getItem(sdk.quest.item.BookofSkill), 30000, 1000); + Misc.poll(() => !Game.getItem(sdk.quest.item.BookofSkill), 30000, 1000); - while (this.playerIn()) { - delay(200); - } + while (this.playerIn()) { + delay(200); + } - Pather.usePortal(null, null); + Pather.usePortal(null, null); - return true; - }; + return true; + }; - this.lamesen = function () { - if (!Config.Rusher.LamEsen) return false; + this.lamesen = function () { + if (!Config.Rusher.LamEsen) return false; - this.log("starting lamesen"); + this.log("starting lamesen"); - if (!Town.goToTown() || !Pather.useWaypoint(sdk.areas.KurastBazaar, true)) { - throw new Error("Lam Essen quest failed"); - } + if (!Town.goToTown() || !Pather.useWaypoint(sdk.areas.KurastBazaar, true)) { + throw new Error("Lam Essen quest failed"); + } - Precast.doPrecast(false); + Precast.doPrecast(false); - if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) + if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { - throw new Error("Lam Essen quest failed"); - } + throw new Error("Lam Essen quest failed"); + } - Attack.securePosition(me.x, me.y, 30, 2000); - Pather.makePortal(); - this.log("1"); + Attack.securePosition(me.x, me.y, 30, 2000); + Pather.makePortal(); + this.log("1"); - while (!this.playerIn()) { - delay(200); - } + while (!this.playerIn()) { + delay(200); + } - while (this.playerIn()) { - delay(200); - } + while (this.playerIn()) { + delay(200); + } - Pather.usePortal(null, null); + Pather.usePortal(null, null); - return true; - }; + return true; + }; - this.izual = function () { - if (!Config.Rusher.Izual) return false; + this.izual = function () { + if (!Config.Rusher.Izual) return false; - this.log("starting izual"); + this.log("starting izual"); - let moveIntoPos = function (unit, range) { - let coords = []; - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 105, -105, 120, -120, 135, -135, 150, -150, 180]; + let moveIntoPos = function (unit, range) { + let coords = []; + let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); + let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 105, -105, 120, -120, 135, -135, 150, -150, 180]; - for (let i = 0; i < angles.length; i += 1) { - let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); - let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); + for (let i = 0; i < angles.length; i += 1) { + let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); + let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); - try { - if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { - coords.push({ - x: coordx, - y: coordy - }); - } - } catch (e) { - continue; - } - } + try { + if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { + coords.push({ + x: coordx, + y: coordy + }); + } + } catch (e) { + continue; + } + } - if (coords.length > 0) { - coords.sort(Sort.units); + if (coords.length > 0) { + coords.sort(Sort.units); - return Pather.moveToUnit(coords[0]); - } + return Pather.moveToUnit(coords[0]); + } - return false; - }; + return false; + }; - Pather.useWaypoint(sdk.areas.CityoftheDamned, true) && Precast.doPrecast(false); - Pather.moveToExit(sdk.areas.PlainsofDespair, true); + Pather.useWaypoint(sdk.areas.CityoftheDamned, true) && Precast.doPrecast(false); + Pather.moveToExit(sdk.areas.PlainsofDespair, true); - let izualPreset = Game.getPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual); - let izualCoords = { - area: sdk.areas.PlainsofDespair, - x: izualPreset.roomx * 5 + izualPreset.x, - y: izualPreset.roomy * 5 + izualPreset.y - }; + let izualPreset = Game.getPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual); + let izualCoords = { + area: sdk.areas.PlainsofDespair, + x: izualPreset.roomx * 5 + izualPreset.x, + y: izualPreset.roomy * 5 + izualPreset.y + }; - moveIntoPos(izualCoords, 50); - let izual = Misc.poll(() => Game.getMonster(sdk.monsters.Izual), 1500, 500); + moveIntoPos(izualCoords, 50); + let izual = Misc.poll(() => Game.getMonster(sdk.monsters.Izual), 1500, 500); - izual ? moveIntoPos(izual, 60) : print("izual unit not found"); + izual ? moveIntoPos(izual, 60) : print("izual unit not found"); - let returnSpot = { - x: me.x, - y: me.y - }; + let returnSpot = { + x: me.x, + y: me.y + }; - Attack.securePosition(me.x, me.y, 30, 3000); - Pather.makePortal(); - this.log("1"); + Attack.securePosition(me.x, me.y, 30, 3000); + Pather.makePortal(); + this.log("1"); - while (!this.playerIn()) { - delay(200); - } + while (!this.playerIn()) { + delay(200); + } - Attack.kill(sdk.monsters.Izual); - Pickit.pickItems(); - this.log("2"); - Pather.moveToUnit(returnSpot); + Attack.kill(sdk.monsters.Izual); + Pickit.pickItems(); + this.log("2"); + Pather.moveToUnit(returnSpot); - while (this.playerIn()) { - delay(200); - } + while (this.playerIn()) { + delay(200); + } - Pather.usePortal(null, null); + Pather.usePortal(null, null); - return true; - }; + return true; + }; - this.shenk = function () { - if (!Config.Rusher.Shenk) return false; + this.shenk = function () { + if (!Config.Rusher.Shenk) return false; - this.log("starting shenk"); + this.log("starting shenk"); - Pather.useWaypoint(sdk.areas.FrigidHighlands, true) && Precast.doPrecast(false); - Pather.moveTo(3846, 5120); - Attack.securePosition(me.x, me.y, 30, 3000); - Pather.makePortal(); - this.log("1"); + Pather.useWaypoint(sdk.areas.FrigidHighlands, true) && Precast.doPrecast(false); + Pather.moveTo(3846, 5120); + Attack.securePosition(me.x, me.y, 30, 3000); + Pather.makePortal(); + this.log("1"); - while (!this.playerIn()) { - delay(200); - } + while (!this.playerIn()) { + delay(200); + } - Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); - Pickit.pickItems(); - Pather.moveTo(3846, 5120); - this.log("2"); + Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + Pickit.pickItems(); + Pather.moveTo(3846, 5120); + this.log("2"); - while (this.playerIn()) { - delay(200); - } + while (this.playerIn()) { + delay(200); + } - Pather.usePortal(null, null); + Pather.usePortal(null, null); - return true; - }; + return true; + }; - this.anya = function () { - if (!Config.Rusher.Anya) return false; + this.anya = function () { + if (!Config.Rusher.Anya) return false; - !me.inTown && Town.goToTown(); + !me.inTown && Town.goToTown(); - this.log("starting anya"); + this.log("starting anya"); - if (!Pather.useWaypoint(sdk.areas.CrystalizedPassage, true)) { - throw new Error("Anya quest failed"); - } + if (!Pather.useWaypoint(sdk.areas.CrystalizedPassage, true)) { + throw new Error("Anya quest failed"); + } - Precast.doPrecast(false); + Precast.doPrecast(false); - if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) + if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { - throw new Error("Anya quest failed"); - } + throw new Error("Anya quest failed"); + } - Attack.securePosition(me.x, me.y, 30, 2000); + Attack.securePosition(me.x, me.y, 30, 2000); - let anya = Game.getObject(sdk.objects.FrozenAnya); + let anya = Game.getObject(sdk.objects.FrozenAnya); - if (anya) { - Pather.moveToUnit(anya); - // Rusher should be able to interact so quester can get the potion without entering - Packet.entityInteract(anya); - delay(1000 + me.ping); - me.cancel(); - } + if (anya) { + Pather.moveToUnit(anya); + // Rusher should be able to interact so quester can get the potion without entering + Packet.entityInteract(anya); + delay(1000 + me.ping); + me.cancel(); + } - Pather.makePortal(); - this.log("1"); - - while (!this.playerIn()) { - delay(200); - } - - Misc.poll(() => !Game.getObject(sdk.objects.FrozenAnya), 30000, 1000); - - this.log("2"); // Mainly for non-questers to know when to get the scroll of resistance - - while (this.playerIn()) { - delay(200); - } - - Pather.usePortal(null, null); - - return true; - }; - - this.givewps = function () { - if (!Config.Rusher.GiveWps) return false; - - let wpsLeft = wpsToGive.slice(0); - console.log(JSON.stringify(wpsLeft)); - - wpsLeft.forEach(function (wp) { - Town.checkScrolls(sdk.items.TomeofTownPortal) <= 5 && (Pather.useWaypoint(sdk.areas.townOf(me.area)) || Town.goToTown()) && Town.doChores(); - Pather.useWaypoint(wp); - }); - - return true; - }; - - console.log(sdk.colors.LightGold + "Loading RushThread"); - - let command = ""; - let current = 0; - let commandsplit = ""; - let check = -1; - let sequence = [ - "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", - "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" - ]; - - this.scriptEvent = function (msg) { - if (typeof msg === "string") { - command = msg; - } - }; - - addEventListener("scriptmsg", this.scriptEvent); - - // START - Config.init(false); - Pickit.init(false); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - Config.MFLeader = false; - - while (true) { - if (command) { - switch (command) { - case "go": - // End run if entire sequence is done or if Config.Rusher.LastRun is done - if (current >= sequence.length || (Config.Rusher.LastRun && current > sequence.indexOf(Config.Rusher.LastRun))) { - delay(3000); - this.log("bye ~"); - console.log("Current sequence length: " + current + " sequence length: " + sequence.length); - - while (Misc.getPlayerCount() > 1) { - delay(1000); - } - - scriptBroadcast("quit"); - - break; - } - - Town.doChores(); - - try { - this[sequence[current]](); - } catch (sequenceError) { - this.log(sequenceError.message); - this.log("2"); - Town.goToTown(); - } - - current += 1; - command = "go"; - - break; - default: - if (typeof command === "string") { - if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "skiptoact") { - if (!isNaN(parseInt(command.split(" ")[1], 10))) { - switch (parseInt(command.split(" ")[1], 10)) { - case 2: - current = sequence.indexOf("andariel") + 1; - Town.goToTown(2); - - break; - case 3: - current = sequence.indexOf("duriel") + 1; - Town.goToTown(3); - - break; - case 4: - current = sequence.indexOf("mephisto") + 1; - Town.goToTown(4); - - break; - case 5: - current = sequence.indexOf("diablo") + 1; - Town.goToTown(5); - - break; - } - } - - command = ""; - } else if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "clear") { - this.clearArea(Number(command.split(" ")[1])); - Town.goToTown(); - - command = "go"; - } else if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "highestquest") { - command.split(" ")[1] !== undefined && (commandsplit = command.split(" ")[1]); - check = sequence.findIndex(i => i === commandsplit); - check > -1 && (current = check + 1); - - command = ""; - } else { - for (let i = 0; i < sequence.length; i += 1) { - if (command && sequence[i].match(command, "gi")) { - current = i; - - break; - } - } - - Town.goToTown(); - - command = "go"; - - break; - } - } - - break; - } - } - - delay(100); - } + Pather.makePortal(); + this.log("1"); + + while (!this.playerIn()) { + delay(200); + } + + Misc.poll(() => !Game.getObject(sdk.objects.FrozenAnya), 30000, 1000); + + this.log("2"); // Mainly for non-questers to know when to get the scroll of resistance + + while (this.playerIn()) { + delay(200); + } + + Pather.usePortal(null, null); + + return true; + }; + + this.givewps = function () { + if (!Config.Rusher.GiveWps) return false; + + let wpsLeft = wpsToGive.slice(0); + console.log(JSON.stringify(wpsLeft)); + + wpsLeft.forEach(function (wp) { + Town.checkScrolls(sdk.items.TomeofTownPortal) <= 5 && (Pather.useWaypoint(sdk.areas.townOf(me.area)) || Town.goToTown()) && Town.doChores(); + Pather.useWaypoint(wp); + }); + + return true; + }; + + console.log(sdk.colors.LightGold + "Loading RushThread"); + + let command = ""; + let current = 0; + let commandsplit = ""; + let check = -1; + let sequence = [ + "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", + "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" + ]; + + this.scriptEvent = function (msg) { + if (typeof msg === "string") { + command = msg; + } + }; + + addEventListener("scriptmsg", this.scriptEvent); + + // START + Config.init(false); + Pickit.init(false); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + + Config.MFLeader = false; + + while (true) { + if (command) { + switch (command) { + case "go": + // End run if entire sequence is done or if Config.Rusher.LastRun is done + if (current >= sequence.length || (Config.Rusher.LastRun && current > sequence.indexOf(Config.Rusher.LastRun))) { + delay(3000); + this.log("bye ~"); + console.log("Current sequence length: " + current + " sequence length: " + sequence.length); + + while (Misc.getPlayerCount() > 1) { + delay(1000); + } + + scriptBroadcast("quit"); + + break; + } + + Town.doChores(); + + try { + this[sequence[current]](); + } catch (sequenceError) { + this.log(sequenceError.message); + this.log("2"); + Town.goToTown(); + } + + current += 1; + command = "go"; + + break; + default: + if (typeof command === "string") { + if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "skiptoact") { + if (!isNaN(parseInt(command.split(" ")[1], 10))) { + switch (parseInt(command.split(" ")[1], 10)) { + case 2: + current = sequence.indexOf("andariel") + 1; + Town.goToTown(2); + + break; + case 3: + current = sequence.indexOf("duriel") + 1; + Town.goToTown(3); + + break; + case 4: + current = sequence.indexOf("mephisto") + 1; + Town.goToTown(4); + + break; + case 5: + current = sequence.indexOf("diablo") + 1; + Town.goToTown(5); + + break; + } + } + + command = ""; + } else if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "clear") { + this.clearArea(Number(command.split(" ")[1])); + Town.goToTown(); + + command = "go"; + } else if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "highestquest") { + command.split(" ")[1] !== undefined && (commandsplit = command.split(" ")[1]); + check = sequence.findIndex(i => i === commandsplit); + check > -1 && (current = check + 1); + + command = ""; + } else { + for (let i = 0; i < sequence.length; i += 1) { + if (command && sequence[i].match(command, "gi")) { + current = i; + + break; + } + } + + Town.goToTown(); + + command = "go"; + + break; + } + } + + break; + } + } + + delay(100); + } } diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 8bdea750c..ad1eed8b4 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * @filename ToolsThread.js * @author kolton, theBGuy @@ -19,439 +20,439 @@ include("systems/gameaction/GameAction.js"); let Overrides = require("../libs/modules/Override"); new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { - let monster = orignal({ skipBlocked: false, skipImmune: false }); - return (monster ? " to " + monster.name : ""); + let monster = orignal({ skipBlocked: false, skipImmune: false }); + return (monster ? " to " + monster.name : ""); }).apply(); function main() { - // getUnit test - getUnit(-1) === null && console.warn("getUnit bug detected"); + // getUnit test + getUnit(-1) === null && console.warn("getUnit bug detected"); - let ironGolem, debugInfo = { area: 0, currScript: "no entry" }; - let [quitFlag, antiIdle, townChicken] = [false, false, false]; - let quitListDelayTime; - let idleTick = 0; - let canQuit = true; - - console.log("ÿc3Start ToolsThread script"); - D2Bot.init(); - Config.init(false); - Pickit.init(false); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - for (let i = 0; i < 5; i += 1) { - Common.Toolsthread.timerLastDrink[i] = 0; - } - - // Reset core chicken - me.chickenhp = -1; - me.chickenmp = -1; - - // General functions - Common.Toolsthread.pauseScripts = [ - "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", - "threads/party.js", "threads/rushthread.js" - ]; - Common.Toolsthread.stopScripts = [ - "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", - "threads/party.js", "threads/rushthread.js", "libs//modules/guard.js" - ]; - - // Event functions - this.keyEvent = function (key) { - switch (key) { - case sdk.keys.PauseBreak: // pause running threads - Common.Toolsthread.togglePause(townChicken); - - break; - case sdk.keys.Delete: // quit current game - Common.Toolsthread.exit(); - - break; - case sdk.keys.End: // stop profile and log character - MuleLogger.logChar(); - delay(rand(Time.seconds(Config.QuitListDelay[0]), Time.seconds(Config.QuitListDelay[1]))); - D2Bot.printToConsole(me.profile + " - end run " + me.gamename); - D2Bot.stop(me.profile, true); - - 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)); - - break; - case sdk.keys.Numpad5: // force automule check - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { - if (AutoMule.getMuleItems().length > 0) { - print("ÿc2Mule triggered"); - scriptBroadcast("mule"); - Common.Toolsthread.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 - MuleLogger.logChar(); - me.overhead("Logged char: " + me.name); - - break; - case sdk.keys.NumpadDash: // log our items to item log ? should this try to get nearest player? Isn't that what it was meant for - { - // check if we are hovering the mouse over somebody - let selectedUnit = Game.getSelectedUnit(); - if (selectedUnit && selectedUnit.isPlayer) { - me.overhead("logging " + selectedUnit.name); - // the unit is a valid player lets log thier stuff...muhahaha - Misc.spy(selectedUnit.name); - } else { - me.overhead("logging my stuff"); - // just log ourselves - Misc.spy(me.name); - } - } - - break; - case sdk.keys.NumpadDecimal: // dump item info - { - let itemString = ""; - let generalString = ""; - let itemToCheck = Game.getSelectedUnit(); - - if (!!itemToCheck) { - Cubing.update(); - Runewords.buildLists(); - CraftingSystem.buildLists(); - - let pResult = Pickit.checkItem(itemToCheck); - let pString = "ÿc4Pickit: ÿc0" + pResult.result + " ÿc7Line: ÿc0" + pResult.line + "\n"; - let nResult = NTIP.CheckItem(itemToCheck, false, true); - let nString = "ÿc4NTIP.CheckItem: ÿc0" + nResult.result + " ÿc7Line: ÿc0" + nResult.line + "\n"; - - itemString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") + let ironGolem, debugInfo = { area: 0, currScript: "no entry" }; + let [quitFlag, antiIdle, townChicken] = [false, false, false]; + let quitListDelayTime; + let idleTick = 0; + let canQuit = true; + + console.log("ÿc3Start ToolsThread script"); + D2Bot.init(); + Config.init(false); + Pickit.init(false); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + + for (let i = 0; i < 5; i += 1) { + Common.Toolsthread.timerLastDrink[i] = 0; + } + + // Reset core chicken + me.chickenhp = -1; + me.chickenmp = -1; + + // General functions + Common.Toolsthread.pauseScripts = [ + "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", + "threads/party.js", "threads/rushthread.js" + ]; + Common.Toolsthread.stopScripts = [ + "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", + "threads/party.js", "threads/rushthread.js", "libs//modules/guard.js" + ]; + + // Event functions + this.keyEvent = function (key) { + switch (key) { + case sdk.keys.PauseBreak: // pause running threads + Common.Toolsthread.togglePause(townChicken); + + break; + case sdk.keys.Delete: // quit current game + Common.Toolsthread.exit(); + + break; + case sdk.keys.End: // stop profile and log character + MuleLogger.logChar(); + delay(rand(Time.seconds(Config.QuitListDelay[0]), Time.seconds(Config.QuitListDelay[1]))); + D2Bot.printToConsole(me.profile + " - end run " + me.gamename); + D2Bot.stop(me.profile, true); + + 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)); + + break; + case sdk.keys.Numpad5: // force automule check + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { + if (AutoMule.getMuleItems().length > 0) { + print("ÿc2Mule triggered"); + scriptBroadcast("mule"); + Common.Toolsthread.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 + MuleLogger.logChar(); + me.overhead("Logged char: " + me.name); + + break; + case sdk.keys.NumpadDash: // log our items to item log ? should this try to get nearest player? Isn't that what it was meant for + { + // check if we are hovering the mouse over somebody + let selectedUnit = Game.getSelectedUnit(); + if (selectedUnit && selectedUnit.isPlayer) { + me.overhead("logging " + selectedUnit.name); + // the unit is a valid player lets log thier stuff...muhahaha + Misc.spy(selectedUnit.name); + } else { + me.overhead("logging my stuff"); + // just log ourselves + Misc.spy(me.name); + } + } + + break; + case sdk.keys.NumpadDecimal: // dump item info + { + let itemString = ""; + let generalString = ""; + let itemToCheck = Game.getSelectedUnit(); + + if (!!itemToCheck) { + Cubing.update(); + Runewords.buildLists(); + CraftingSystem.buildLists(); + + let pResult = Pickit.checkItem(itemToCheck); + let pString = "ÿc4Pickit: ÿc0" + pResult.result + " ÿc7Line: ÿc0" + pResult.line + "\n"; + let nResult = NTIP.CheckItem(itemToCheck, false, true); + let nString = "ÿc4NTIP.CheckItem: ÿc0" + nResult.result + " ÿc7Line: ÿc0" + nResult.line + "\n"; + + itemString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality + "| ÿc4Gid: ÿc0" + itemToCheck.gid + "\nÿc4ItemMode: ÿc0" + itemToCheck.mode + "| ÿc4Location: ÿc0" + itemToCheck.location + "| ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation; - generalString = pString + nString + generalString = pString + nString + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck); - } + } - console.log("ÿc2*************Item Info Start*************"); - console.log(itemString); - console.log("ÿc2Systems Info Start"); - console.log(generalString); - console.log("ÿ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"); - Common.Toolsthread.stopDefault() && delay(Time.seconds(5)); - console.log("Starting default.dbj"); - load("default.dbj"); - - break; - } - }; - - this.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; - } - - if (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"); - } - - if (Config.PublicMode) { - scriptBroadcast("hostileCheck"); - } - - break; - case 0x11: // "%Param1 Stones of Jordan Sold to Merchants" - if (Config.DCloneQuit === 2) { - D2Bot.printToConsole("SoJ sold in game. Leaving."); - - quitFlag = true; - - break; - } - - if (Config.SoJWaitTime && me.expansion) { - !!me.realm && D2Bot.printToConsole(param1 + " Stones of Jordan Sold to Merchants on IP " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); - Messaging.sendToScript("default.dbj", "soj"); - } - - break; - case 0x12: // "Diablo Walks the Earth" - if (Config.DCloneQuit > 0) { - D2Bot.printToConsole("Diablo walked in game. Leaving."); - - quitFlag = true; - - break; - } - - if (Config.StopOnDClone && me.expansion) { - D2Bot.printToConsole("Diablo Walks the Earth", sdk.colors.D2Bot.DarkGold); - Common.Toolsthread.cloneWalked = true; - - Common.Toolsthread.togglePause(); - Town.goToTown(); - showConsole(); - print("ÿc4Diablo Walks the Earth"); - - me.maxgametime = 0; - - if (Config.KillDclone && load("threads/clonekilla.js")) { - break; - } else { - antiIdle = true; - } - } - - break; - } - }; - - this.scriptEvent = function (msg) { - if (!!msg && typeof msg === "string") { - switch (msg) { - case "townChickenOn": - townChicken = true; - - break; - case "townChickenOff": - townChicken = false; - - break; - case "toggleQuitlist": - canQuit = !canQuit; - - break; - case "quit": - console.debug("Quiting"); - quitFlag = true; - - break; - case "datadump": - console.log("ÿc8Systems Data Dump: ÿc2Start"); - console.log("ÿc8Cubing"); - console.log("ÿc9Cubing Valid Itemsÿc0", Cubing.validIngredients); - console.log("ÿc9Cubing Needed Itemsÿc0", Cubing.neededIngredients); - console.log("ÿc8Runeword"); - console.log("ÿc9Runeword Valid Itemsÿc0", Runewords.validGids); - console.log("ÿc9Runeword Needed Itemsÿc0", Runewords.needList); - console.log("ÿc8Systems Data Dump: ÿc1****************Info 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: - let obj; - - try { - obj = JSON.parse(msg); - } catch (e) { - return; - } - - if (obj) { - obj.hasOwnProperty("currScript") && (debugInfo.currScript = obj.currScript); - obj.hasOwnProperty("lastAction") && (debugInfo.lastAction = obj.lastAction); - - DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); - } - - break; - } - } - }; - - // Cache variables to prevent a bug where d2bs loses the reference to Config object - Config = copyObj(Config); - let tick = getTickCount(); - - addEventListener("keyup", this.keyEvent); - addEventListener("gameevent", this.gameEvent); - addEventListener("scriptmsg", this.scriptEvent); - - Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); - !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks - - // Start - while (true) { - try { - if (me.gameReady && !me.inTown) { - Config.UseHP > 0 && me.hpPercent < Config.UseHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Health); - Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); - - /** + console.log("ÿc2*************Item Info Start*************"); + console.log(itemString); + console.log("ÿc2Systems Info Start"); + console.log(generalString); + console.log("ÿ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"); + Common.Toolsthread.stopDefault() && delay(Time.seconds(5)); + console.log("Starting default.dbj"); + load("default.dbj"); + + break; + } + }; + + this.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; + } + + if (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"); + } + + if (Config.PublicMode) { + scriptBroadcast("hostileCheck"); + } + + break; + case 0x11: // "%Param1 Stones of Jordan Sold to Merchants" + if (Config.DCloneQuit === 2) { + D2Bot.printToConsole("SoJ sold in game. Leaving."); + + quitFlag = true; + + break; + } + + if (Config.SoJWaitTime && me.expansion) { + !!me.realm && D2Bot.printToConsole(param1 + " Stones of Jordan Sold to Merchants on IP " + me.gameserverip.split(".")[3], sdk.colors.D2Bot.DarkGold); + Messaging.sendToScript("default.dbj", "soj"); + } + + break; + case 0x12: // "Diablo Walks the Earth" + if (Config.DCloneQuit > 0) { + D2Bot.printToConsole("Diablo walked in game. Leaving."); + + quitFlag = true; + + break; + } + + if (Config.StopOnDClone && me.expansion) { + D2Bot.printToConsole("Diablo Walks the Earth", sdk.colors.D2Bot.DarkGold); + Common.Toolsthread.cloneWalked = true; + + Common.Toolsthread.togglePause(); + Town.goToTown(); + showConsole(); + print("ÿc4Diablo Walks the Earth"); + + me.maxgametime = 0; + + if (Config.KillDclone && load("threads/clonekilla.js")) { + break; + } else { + antiIdle = true; + } + } + + break; + } + }; + + this.scriptEvent = function (msg) { + if (!!msg && typeof msg === "string") { + switch (msg) { + case "townChickenOn": + townChicken = true; + + break; + case "townChickenOff": + townChicken = false; + + break; + case "toggleQuitlist": + canQuit = !canQuit; + + break; + case "quit": + console.debug("Quiting"); + quitFlag = true; + + break; + case "datadump": + console.log("ÿc8Systems Data Dump: ÿc2Start"); + console.log("ÿc8Cubing"); + console.log("ÿc9Cubing Valid Itemsÿc0", Cubing.validIngredients); + console.log("ÿc9Cubing Needed Itemsÿc0", Cubing.neededIngredients); + console.log("ÿc8Runeword"); + console.log("ÿc9Runeword Valid Itemsÿc0", Runewords.validGids); + console.log("ÿc9Runeword Needed Itemsÿc0", Runewords.needList); + console.log("ÿc8Systems Data Dump: ÿc1****************Info 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: + let obj; + + try { + obj = JSON.parse(msg); + } catch (e) { + return; + } + + if (obj) { + obj.hasOwnProperty("currScript") && (debugInfo.currScript = obj.currScript); + obj.hasOwnProperty("lastAction") && (debugInfo.lastAction = obj.lastAction); + + DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); + } + + break; + } + } + }; + + // Cache variables to prevent a bug where d2bs loses the reference to Config object + Config = copyObj(Config); + let tick = getTickCount(); + + addEventListener("keyup", this.keyEvent); + addEventListener("gameevent", this.gameEvent); + addEventListener("scriptmsg", this.scriptEvent); + + Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); + !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks + + // Start + while (true) { + try { + if (me.gameReady && !me.inTown) { + Config.UseHP > 0 && me.hpPercent < Config.UseHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Health); + Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + + /** * Feel like potting and lifechicken should actually be seperate threads */ - if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { - // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting - if (!me.inTown) { - D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - } - - Config.UseMP > 0 && me.mpPercent < Config.UseMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Mana); - Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); - - if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { - D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - - 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)) { - D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - } - } - - 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) { - D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); - - break; - } - - mercHP < Config.UseMercHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercHealth); - mercHP < Config.UseMercRejuv && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercRejuv); - } - } - } - - if (Config.ViperCheck && getTickCount() - tick >= 250) { - Common.Toolsthread.checkVipers() && (quitFlag = true); - - tick = getTickCount(); - } - - Common.Toolsthread.checkPing(true) && (quitFlag = true); - } - - if (antiIdle) { - tick = getTickCount(); - - while (getTickCount() - tick < Time.minutes(Config.DCloneWaitTime)) { - if (getTickCount() - idleTick > 0) { - Packet.questRefresh(); - idleTick += rand(1200, 1500) * 1000; - let timeStr = Time.format(idleTick - getTickCount()); - me.overhead("Diablo Walks the Earth! - Next packet in: (" + timeStr + ")"); - print("Sent anti-idle packet, next refresh in: (" + timeStr + ")"); - } - } - } - } catch (e) { - Misc.errorReport(e, "ToolsThread"); - - quitFlag = true; - } - - if (debugInfo.area !== getAreaName(me.area)) { - debugInfo.area = getAreaName(me.area); - DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); - } - - if (quitFlag && canQuit) { - if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { - // should there be a check if we are in the middle of interacting with an npc? Seems quitting game in the middle causes crashes - // only ancedotal evidence currently - if (getTickCount() < quitListDelayTime - 4000) { - me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); - } - } else { - Common.Toolsthread.checkPing(false); // In case of quitlist triggering first - Common.Toolsthread.exit(); - - return true; - } - } - - delay(20); - } - - return true; + if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { + // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting + if (!me.inTown) { + D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + } + + Config.UseMP > 0 && me.mpPercent < Config.UseMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Mana); + Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + + if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { + D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + + 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)) { + D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + } + } + + 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) { + D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + Common.Toolsthread.exit(true); + + break; + } + + mercHP < Config.UseMercHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercHealth); + mercHP < Config.UseMercRejuv && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercRejuv); + } + } + } + + if (Config.ViperCheck && getTickCount() - tick >= 250) { + Common.Toolsthread.checkVipers() && (quitFlag = true); + + tick = getTickCount(); + } + + Common.Toolsthread.checkPing(true) && (quitFlag = true); + } + + if (antiIdle) { + tick = getTickCount(); + + while (getTickCount() - tick < Time.minutes(Config.DCloneWaitTime)) { + if (getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += rand(1200, 1500) * 1000; + let timeStr = Time.format(idleTick - getTickCount()); + me.overhead("Diablo Walks the Earth! - Next packet in: (" + timeStr + ")"); + print("Sent anti-idle packet, next refresh in: (" + timeStr + ")"); + } + } + } + } catch (e) { + Misc.errorReport(e, "ToolsThread"); + + quitFlag = true; + } + + if (debugInfo.area !== getAreaName(me.area)) { + debugInfo.area = getAreaName(me.area); + DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); + } + + if (quitFlag && canQuit) { + if (typeof quitListDelayTime !== "undefined" && getTickCount() < quitListDelayTime) { + // should there be a check if we are in the middle of interacting with an npc? Seems quitting game in the middle causes crashes + // only ancedotal evidence currently + if (getTickCount() < quitListDelayTime - 4000) { + me.overhead("Quitting in " + Math.round((quitListDelayTime - getTickCount()) / 1000) + " Seconds"); + } + } else { + Common.Toolsthread.checkPing(false); // In case of quitlist triggering first + Common.Toolsthread.exit(); + + return true; + } + } + + delay(20); + } + + return true; } diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js index 6216ac18e..e051db320 100644 --- a/d2bs/kolbot/threads/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -14,124 +14,124 @@ includeCoreLibs(); includeSystemLibs(); function main() { - let townCheck = false; - const scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; - - // override broadCastIntent - shouldn't be called at all in this thread - Pather.broadcastIntent = () => {}; - - const pause = function () { - for (let i = 0; i < scripts.length; i += 1) { - let script = getScript(scripts[i]); - - if (scripts[i] === "default.dbj" && !script) { - !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); - } - - if (script && script.running) { - script.pause(); - scripts[i] === "default.dbj" && print("ÿc1Pausing."); - } - } - - return true; - }; - - const resume = function () { - for (let i = 0; i < scripts.length; i += 1) { - let script = getScript(scripts[i]); - - if (script && !script.running && scripts[i] !== "default.dbj") { - script.resume(); - } else if (scripts[i] === "default.dbj") { - // resume only if clonekilla isn't running - if (!getScript("threads/clonekilla.js")) { - if (script && !script.running) { - console.log("ÿc2Resuming."); - script.resume(); - } else { - if (!script) { - // default has crashed? We shouldn't be running then. Is toolsthread still up? - // if yes try to still quit normally, otherwise quit from here - !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); - } - } - } - } - } - - return true; - }; - - addEventListener("scriptmsg", - function (msg) { - if (typeof msg === "string" && msg === "townCheck") { - townCheck = true; - // maybe check for quit broadcast as well? If we are preparing to quit we shouldn't really be townchickening - } - }); - - // Init config and attacks - console.log("ÿc3Start TownChicken thread"); - D2Bot.init(); - Config.init(); - Pickit.init(); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - let checkHP = Config.TownHP > 0; - let checkMP = Config.TownMP > 0; - - // START - // test for getUnit bug - Game.getMonster() === null && console.warn("getUnit is bugged"); - - while (true) { - if (!me.inTown && (townCheck + let townCheck = false; + const scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; + + // override broadCastIntent - shouldn't be called at all in this thread + Pather.broadcastIntent = () => {}; + + const pause = function () { + for (let i = 0; i < scripts.length; i += 1) { + let script = getScript(scripts[i]); + + if (scripts[i] === "default.dbj" && !script) { + !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + } + + if (script && script.running) { + script.pause(); + scripts[i] === "default.dbj" && print("ÿc1Pausing."); + } + } + + return true; + }; + + const resume = function () { + for (let i = 0; i < scripts.length; i += 1) { + let script = getScript(scripts[i]); + + if (script && !script.running && scripts[i] !== "default.dbj") { + script.resume(); + } else if (scripts[i] === "default.dbj") { + // resume only if clonekilla isn't running + if (!getScript("threads/clonekilla.js")) { + if (script && !script.running) { + console.log("ÿc2Resuming."); + script.resume(); + } else { + if (!script) { + // default has crashed? We shouldn't be running then. Is toolsthread still up? + // if yes try to still quit normally, otherwise quit from here + !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + } + } + } + } + } + + return true; + }; + + addEventListener("scriptmsg", + function (msg) { + if (typeof msg === "string" && msg === "townCheck") { + townCheck = true; + // maybe check for quit broadcast as well? If we are preparing to quit we shouldn't really be townchickening + } + }); + + // Init config and attacks + console.log("ÿc3Start TownChicken thread"); + D2Bot.init(); + Config.init(); + Pickit.init(); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + + let checkHP = Config.TownHP > 0; + let checkMP = Config.TownMP > 0; + + // START + // test for getUnit bug + Game.getMonster() === null && console.warn("getUnit is bugged"); + + while (true) { + if (!me.inTown && (townCheck // should TownHP/MP check be in toolsthread? // We would then be able to remove all game interaction checks until we get a townCheck msg || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { - // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area - if (me.dead && Config.LifeChicken <= 0) { - console.log("ÿc1TownChicken :: ÿc0We are dead and LifeChicken is set to 0"); - while (me.dead) delay(100); - - continue; - } - if (!me.canTpToTown()) { - townCheck = false; - - continue; - } - pause(); - - while (!me.gameReady) { - if (me.dead) return; - - delay(100); - } - - try { - Messaging.sendToScript("threads/Toolsthread.js", "townChickenOn"); - console.log("ÿc8(TownChicken) :: ÿc0Going to town"); - me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); - Town.visitTown(); - } catch (e) { - Misc.errorReport(e, "TownChicken.js"); - scriptBroadcast("quit"); - - return; - } finally { - resume(); - - townCheck = false; - Messaging.sendToScript("threads/Toolsthread.js", "townChickenOff"); - } - } - - delay(50); - } + // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area + if (me.dead && Config.LifeChicken <= 0) { + console.log("ÿc1TownChicken :: ÿc0We are dead and LifeChicken is set to 0"); + while (me.dead) delay(100); + + continue; + } + if (!me.canTpToTown()) { + townCheck = false; + + continue; + } + pause(); + + while (!me.gameReady) { + if (me.dead) return; + + delay(100); + } + + try { + Messaging.sendToScript("threads/Toolsthread.js", "townChickenOn"); + console.log("ÿc8(TownChicken) :: ÿc0Going to town"); + me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); + Town.visitTown(); + } catch (e) { + Misc.errorReport(e, "TownChicken.js"); + scriptBroadcast("quit"); + + return; + } finally { + resume(); + + townCheck = false; + Messaging.sendToScript("threads/Toolsthread.js", "townChickenOff"); + } + } + + delay(50); + } } From b6e176f7bea83b2508719db3bb45338a4cb547fe Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 26 May 2023 19:22:31 -0400 Subject: [PATCH 127/758] Formatting attempt 2 - eslint sucks, didn't convert a whole lot of the indentation from tabs to space --- d2bs/kolbot/D2BotBlank.dbj | 4 +- d2bs/kolbot/D2BotChannel.dbj | 8 +- d2bs/kolbot/D2BotCleaner.dbj | 84 +-- d2bs/kolbot/D2BotFollow.dbj | 28 +- d2bs/kolbot/D2BotGameAction.dbj | 8 +- d2bs/kolbot/D2BotMule.dbj | 34 +- d2bs/kolbot/D2BotMuleLog.dbj | 4 +- d2bs/kolbot/D2BotPubJoin.dbj | 24 +- d2bs/kolbot/default.dbj | 10 +- d2bs/kolbot/libs/OOG.js | 180 +++--- d2bs/kolbot/libs/core/Attack.js | 376 ++++++------- d2bs/kolbot/libs/core/Attacks/Amazon.js | 4 +- d2bs/kolbot/libs/core/Attacks/Assassin.js | 8 +- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 64 +-- d2bs/kolbot/libs/core/Attacks/Druid.js | 2 +- d2bs/kolbot/libs/core/Attacks/Paladin.js | 20 +- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 6 +- d2bs/kolbot/libs/core/Auto/AutoSkill.js | 32 +- d2bs/kolbot/libs/core/Auto/AutoStat.js | 28 +- d2bs/kolbot/libs/core/Common/Baal.js | 8 +- d2bs/kolbot/libs/core/Common/Cain.js | 4 +- d2bs/kolbot/libs/core/Common/Diablo.js | 94 ++-- d2bs/kolbot/libs/core/Common/Smith.js | 2 +- d2bs/kolbot/libs/core/Config.js | 4 +- d2bs/kolbot/libs/core/Experience.js | 38 +- d2bs/kolbot/libs/core/GameData/MonsterData.js | 74 +-- d2bs/kolbot/libs/core/GameData/QuestData.js | 42 +- d2bs/kolbot/libs/core/GameData/RuneData.js | 52 +- d2bs/kolbot/libs/core/GameData/ShrineData.js | 2 +- d2bs/kolbot/libs/core/GameData/SkillData.js | 150 ++--- d2bs/kolbot/libs/core/Item.js | 4 +- d2bs/kolbot/libs/core/Loader.js | 24 +- d2bs/kolbot/libs/core/Me.js | 10 +- d2bs/kolbot/libs/core/Misc.js | 118 ++-- d2bs/kolbot/libs/core/NTItemParser.js | 2 +- d2bs/kolbot/libs/core/Packet.js | 170 +++--- d2bs/kolbot/libs/core/Pather.js | 532 +++++++++--------- d2bs/kolbot/libs/core/Precast.js | 54 +- d2bs/kolbot/libs/core/Runewords.js | 74 +-- d2bs/kolbot/libs/core/Storage.js | 112 ++-- d2bs/kolbot/libs/core/Town.js | 300 +++++----- d2bs/kolbot/libs/core/Util.js | 190 +++---- d2bs/kolbot/libs/manualplay/MapMode.js | 4 +- d2bs/kolbot/libs/manualplay/config/Amazon.js | 380 ++++++------- .../kolbot/libs/manualplay/config/Assassin.js | 398 ++++++------- .../libs/manualplay/config/Barbarian.js | 376 ++++++------- d2bs/kolbot/libs/manualplay/config/Druid.js | 384 ++++++------- .../libs/manualplay/config/Necromancer.js | 426 +++++++------- d2bs/kolbot/libs/manualplay/config/Paladin.js | 384 ++++++------- .../libs/manualplay/config/Sorceress.js | 388 ++++++------- .../libs/manualplay/hooks/ActionHooks.js | 30 +- .../kolbot/libs/manualplay/hooks/ItemHooks.js | 52 +- .../kolbot/libs/manualplay/hooks/TextHooks.js | 4 +- .../libs/manualplay/libs/MiscOverrides.js | 4 +- .../libs/manualplay/libs/PatherOverrides.js | 16 +- .../libs/manualplay/libs/TownOverrides.js | 6 +- .../libs/manualplay/threads/MapHelper.js | 16 +- .../libs/manualplay/threads/MapThread.js | 10 +- .../libs/manualplay/threads/MapToolsThread.js | 4 +- d2bs/kolbot/libs/modules/Control.js | 54 +- d2bs/kolbot/libs/modules/CopyData.js | 70 +-- d2bs/kolbot/libs/modules/Deltas.js | 2 +- d2bs/kolbot/libs/modules/Guard.js | 6 +- d2bs/kolbot/libs/modules/Promise.js | 14 +- d2bs/kolbot/libs/modules/UnitInfo.js | 80 +-- d2bs/kolbot/libs/modules/Worker.js | 6 +- d2bs/kolbot/libs/modules/sdk.js | 18 +- d2bs/kolbot/libs/oog/D2Bot.js | 16 +- d2bs/kolbot/libs/oog/DataFile.js | 2 +- d2bs/kolbot/libs/oog/ShitList.js | 2 +- d2bs/kolbot/libs/scripts/BaalHelper.js | 4 +- d2bs/kolbot/libs/scripts/BattleOrders.js | 2 +- d2bs/kolbot/libs/scripts/BattlemaidSarina.js | 2 +- d2bs/kolbot/libs/scripts/BoBarbHelper.js | 10 +- .../libs/scripts/ClassicChaosAssistant.js | 20 +- d2bs/kolbot/libs/scripts/ControlBot.js | 66 +-- d2bs/kolbot/libs/scripts/Corpsefire.js | 2 +- d2bs/kolbot/libs/scripts/Cows.js | 4 +- d2bs/kolbot/libs/scripts/Crafting.js | 22 +- d2bs/kolbot/libs/scripts/CreepingFeature.js | 2 +- d2bs/kolbot/libs/scripts/DeveloperMode.js | 2 +- d2bs/kolbot/libs/scripts/DiabloHelper.js | 2 +- d2bs/kolbot/libs/scripts/Eldritch.js | 2 +- d2bs/kolbot/libs/scripts/Endugu.js | 2 +- d2bs/kolbot/libs/scripts/Follower.js | 38 +- d2bs/kolbot/libs/scripts/IPHunter.js | 12 +- d2bs/kolbot/libs/scripts/MFHelper.js | 28 +- d2bs/kolbot/libs/scripts/Mausoleum.js | 4 +- d2bs/kolbot/libs/scripts/Questing.js | 54 +- d2bs/kolbot/libs/scripts/Rushee.js | 14 +- d2bs/kolbot/libs/scripts/ShopBot.js | 28 +- d2bs/kolbot/libs/scripts/Synch.js | 2 +- d2bs/kolbot/libs/scripts/Synch2.js | 2 +- d2bs/kolbot/libs/scripts/Tombs.js | 6 +- d2bs/kolbot/libs/scripts/Travincal.js | 2 +- d2bs/kolbot/libs/scripts/TravincalLeech.js | 4 +- d2bs/kolbot/libs/scripts/TristramLeech.js | 6 +- d2bs/kolbot/libs/scripts/Wakka.js | 4 +- d2bs/kolbot/libs/scripts/Worldstone.js | 12 +- d2bs/kolbot/libs/starter/AdvancedConfig.js | 42 +- d2bs/kolbot/libs/systems/automule/AutoMule.js | 146 ++--- .../libs/systems/crafting/TeamsConfig.js | 8 +- .../libs/systems/gambling/TeamsConfig.js | 22 +- .../libs/systems/gameaction/GameAction.js | 2 +- .../libs/systems/mulelogger/LoggerConfig.js | 16 +- .../kolbot/libs/systems/torch/FarmerConfig.js | 30 +- d2bs/kolbot/libs/systems/torch/TorchSystem.js | 8 +- d2bs/kolbot/threads/AntiHostile.js | 2 +- d2bs/kolbot/threads/AntiIdle.js | 2 +- d2bs/kolbot/threads/AreaWatcher.js | 2 +- d2bs/kolbot/threads/AutoBuildThread.js | 6 +- d2bs/kolbot/threads/Party.js | 20 +- d2bs/kolbot/threads/RushThread.js | 20 +- d2bs/kolbot/threads/ToolsThread.js | 14 +- d2bs/kolbot/threads/TownChicken.js | 6 +- 115 files changed, 3420 insertions(+), 3420 deletions(-) diff --git a/d2bs/kolbot/D2BotBlank.dbj b/d2bs/kolbot/D2BotBlank.dbj index dcea8cda8..446965655 100644 --- a/d2bs/kolbot/D2BotBlank.dbj +++ b/d2bs/kolbot/D2BotBlank.dbj @@ -12,7 +12,7 @@ function main() { if (!FileTools.exists("data/" + me.profile + ".json")) { DataFile.create(); } - + addEventListener("copydata", Starter.receiveCopyData); while (!Starter.handle) { @@ -31,7 +31,7 @@ function main() { while (true) { delay(1000); - + if (me.gameReady) { Starter.isUp === "no" && (Starter.isUp = "yes"); if (me.ingame) { diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 835763efb..dfc0a5450 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -254,10 +254,10 @@ function locationAction (location) { let newLines = lines .map((line, index) => { if (index > 0 - // eslint-disable-next-line no-useless-escape - && !line.match(/\<.*\>/, "gi") - && !line.match(" has left", "gi") - && !line.match(" has joined", "gi")) { + // eslint-disable-next-line no-useless-escape + && !line.match(/\<.*\>/, "gi") + && !line.match(" has left", "gi") + && !line.match(" has joined", "gi")) { line = lines[index - 1] + line; } return line; diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index e715c5c43..c8dabb35e 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -31,47 +31,47 @@ Starter.Config.DelayBetweenAccounts = rand(15, 30); //Seconds to wait before cle */ const AccountsToClean = { /* Format: - "account1/password1/realm": ["charname1", "charname2"], - "account2/password2/realm": ["charnameX", "charnameY"], - "account3/password3/realm": ["all"] + "account1/password1/realm": ["charname1", "charname2"], + "account2/password2/realm": ["charnameX", "charnameY"], + "account3/password3/realm": ["all"] - To clean a full account, put "account/password/realm": ["all"] + To clean a full account, put "account/password/realm": ["all"] - realm = useast, uswest, europe, asia + realm = useast, uswest, europe, asia - for singleplayer follow format "singleplayer": ["charname1", "charname2"] + for singleplayer follow format "singleplayer": ["charname1", "charname2"] - Individual entries are separated with a comma. - */ + Individual entries are separated with a comma. + */ /* Example: - "MyAcc1/tempPass/useast": ["soloSorc"], - "singleplayer": ["solobarb"], - */ + "MyAcc1/tempPass/useast": ["soloSorc"], + "singleplayer": ["solobarb"], + */ // Enter your lines under here }; const CharactersToExclude = [""]; - + // NEW STUFF - Please enter your profile name exactly as is const profiles = [ /* Format. Enter in profile exactly the way it appears in D2Bot# - "SCL-ZON123", - "hcnl-pal123", - */ + "SCL-ZON123", + "hcnl-pal123", + */ // Enter your lines under here ]; /* - If you have a lot of profiles that are clones this can be used as an easier way to clean all of them - { - profilePrefix: this is everthing before the suffix numbers. Ex: mypal01 or sccl-pal-001, ect - profileSuffixStart: this is the suffix to start at, Ex: 01 or 001 or 1, all the profiles need have the same format. CANNOT HAVE scl-pal-1 and scl-pal-001 - end: the ending profile suffix, this is used to stop the loop. If you are doing scl-pal-001 to scl-pal-100 (that'd be alot) then 100 would go here - } + If you have a lot of profiles that are clones this can be used as an easier way to clean all of them + { + profilePrefix: this is everthing before the suffix numbers. Ex: mypal01 or sccl-pal-001, ect + profileSuffixStart: this is the suffix to start at, Ex: 01 or 001 or 1, all the profiles need have the same format. CANNOT HAVE scl-pal-1 and scl-pal-001 + end: the ending profile suffix, this is used to stop the loop. If you are doing scl-pal-001 to scl-pal-100 (that'd be alot) then 100 would go here + } */ const AdvancedProfileCleanerConfig = [ // { @@ -83,12 +83,12 @@ const AdvancedProfileCleanerConfig = [ ]; /* Generate accounts to entirely clean ("all") - to use this, set generateAccounts to true and setup the rest of the parameters - - it will generates accounts from start to stop range(included) : - account1/password/realm - account2/password/realm - etc... + to use this, set generateAccounts to true and setup the rest of the parameters + + it will generates accounts from start to stop range(included) : + account1/password/realm + account2/password/realm + etc... */ const AdvancedCleanerConfig = { @@ -191,13 +191,13 @@ function dataCleaner () { profileExists = true; } } - + if (FileTools.exists(dataFP)) { Starter.Config.SaveFiles && FileTools.copy(dataFP, savePath + "Old.json"); FileTools.remove(dataFP); profileExists = true; } - + if (FileTools.exists(gameTimeFP)) { Starter.Config.SaveFiles && FileTools.copy(gameTimeFP, savePath + "-GameTimeOld.json"); FileTools.remove(gameTimeFP); @@ -207,7 +207,7 @@ function dataCleaner () { Starter.Config.SaveFiles && FileTools.copy(charDataFP, savePath + "-CharDataOld.json"); FileTools.remove(charDataFP); } - + if (FileTools.exists(lvlPerfFP)) { Starter.Config.SaveFiles && FileTools.copy(lvlPerfFP, savePath + "-LevelingPerformanceOld.csv"); FileTools.remove(lvlPerfFP); @@ -217,7 +217,7 @@ function dataCleaner () { Starter.Config.SaveFiles && FileTools.copy(scrPerfFP, savePath + "-ScriptPerformanceOld.csv"); FileTools.remove(scrPerfFP); } - + if (Starter.Config.SaveFiles && profileExists && soloplayProfile) { D2Bot.printToConsole("D2BotCleaner: Files saved to -> libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j, sdk.colors.D2Bot.Gold); } @@ -231,7 +231,7 @@ function dataCleaner () { D2Bot.printToConsole("D2BotCleaner: Done cleaning files", sdk.colors.D2Bot.Gold); } - + function parseInfo () { for (let i in AccountsToClean) { if (AccountsToClean.hasOwnProperty(i) && typeof i === "string") { @@ -239,7 +239,7 @@ function parseInfo () { chars.push(AccountsToClean[i]); } } - + if (AdvancedCleanerConfig.generateAccounts) { for (let index = rangeStart; index <= rangeStop ; index += 1) { accounts.push(AdvancedCleanerConfig.accountPrefix + index + "/" + AdvancedCleanerConfig.accountPassword + "/" + AdvancedCleanerConfig.accountRealm); @@ -254,7 +254,7 @@ function parseInfo () { D2Bot.stop(me.profile, true); } } - + function deleteAllCharacters () { let characters = ControlAction.getCharacters(); for (let character of characters) { @@ -302,14 +302,14 @@ function locationAction (location) { D2Bot.printToConsole("D2BotCleaner: Done cleaning accounts!", sdk.colors.D2Bot.Gold); D2Bot.stop(me.profile, true); } - + if (!firstAccount) { for (i = 0 ; i < Starter.Config.DelayBetweenAccounts; i += 1) { D2Bot.updateStatus("Waiting " + (Starter.Config.DelayBetweenAccounts - i) + "s for next account"); delay(1e3); } } - + firstAccount = false; if (FileTools.exists("logs/D2BotCleaner.json")) { @@ -336,7 +336,7 @@ function locationAction (location) { D2Bot.printToConsole("D2BotCleaner: Cleaning account:" + currAcc + " , Character list: " + charList, sdk.colors.D2Bot.Gold); FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); - + if (currAcc.toLowerCase() === "singleplayer") { Controls.SinglePlayer.click(); } else if (currAccInfo.length === 3) { @@ -365,7 +365,7 @@ function locationAction (location) { break; } } - + if (!charList.length) { Controls.BottomLeftExit.click(); @@ -389,7 +389,7 @@ function locationAction (location) { } } } - + let charInfo = { charName: charList[0] }; CharactersToExclude.indexOf(charInfo) === -1 && ControlAction.deleteCharacter(charInfo); delay(500); @@ -397,14 +397,14 @@ function locationAction (location) { currChar = charList.shift(); obj.currChar = currChar; - + // last char in acc = trigger next acc if (!charList.length) { accounts.shift(); chars.shift(); Controls.BottomLeftExit.click(); } - + FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); break; @@ -415,7 +415,7 @@ function locationAction (location) { case sdk.game.locations.CharacterCreate: case sdk.game.locations.NewCharSelected: Controls.BottomLeftExit.click(); - + break; case sdk.game.locations.CharSelectPleaseWait: !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 063931d8e..afc14adfb 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -18,15 +18,15 @@ Starter.Config.JoinRetryDelay = 5; // Time in seconds to wait before next join a * @todo this section should be in it's own config leaving this file only containing core logic */ /* Join game settings - Format: "leader's profile": ["leecher 1 profile", "leecher 2 profile", ...] - If you want everyone to join the same leader, use "leader's profile": ["all"] - NOTE: Use PROFILE names (profile matches window title), NOT character/account names - leader:leecher groups need to be divided by a comma - example: - let JoinSettings = { - "lead1": ["follow1", "follow2"], - "lead2": ["follow3", "follow4"] - }; + Format: "leader's profile": ["leecher 1 profile", "leecher 2 profile", ...] + If you want everyone to join the same leader, use "leader's profile": ["all"] + NOTE: Use PROFILE names (profile matches window title), NOT character/account names + leader:leecher groups need to be divided by a comma + example: + let JoinSettings = { + "lead1": ["follow1", "follow2"], + "lead2": ["follow3", "follow4"] + }; */ const JoinSettings = { @@ -200,9 +200,9 @@ function locationAction (location) { } /** - * @todo handle rejoin, need to keep track of game averages and when requesting game from a leader who's game we left get the current game time - * and see if there is x amount of time left that makes it worth it vs waiting for next. - */ + * @todo handle rejoin, need to keep track of game averages and when requesting game from a leader who's game we left get the current game time + * and see if there is x amount of time left that makes it worth it vs waiting for next. + */ if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { Controls.JoinGameName.setText(Starter.joinInfo.gameName); @@ -375,8 +375,8 @@ function locationAction (location) { } if (Controls.IPAdress.setText(Object.keys(Starter.joinInfo).length ? Starter.joinInfo.gameName : "localhost") - && Controls.IPAdressOk.click() - && Starter.locationTimeout(2e3, sdk.game.locations.TcpIpEnterIp)) { + && Controls.IPAdressOk.click() + && Starter.locationTimeout(2e3, sdk.game.locations.TcpIpEnterIp)) { getLocation() === sdk.game.locations.CharSelect && login(me.profile); } } catch (e) { diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index eb95c0262..55240e077 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -76,7 +76,7 @@ function locationAction (location) { D2Bot.stop(me.profile, true); break; } - + if (!Starter.LocationEvents.openCreateGameWindow()) { break; } @@ -103,7 +103,7 @@ function locationAction (location) { D2Bot.stop(me.profile, true); break; } - + D2Bot.updateStatus("Creating Game"); // remove level restriction @@ -129,7 +129,7 @@ function locationAction (location) { Starter.locationTimeout(5000, location); Starter.lastGameStatus = "pending"; - + break; case sdk.game.locations.JoinGame: // Join Game D2Bot.updateStatus("Join Game"); @@ -178,7 +178,7 @@ function locationAction (location) { case sdk.game.locations.CharSelect: // Character Select // Reset ftj counter ftj = 0; - + // Single Player screen fix if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { Controls.BottomLeftExit.click(); diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 6b0ac4ec8..07991ad29 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -123,8 +123,8 @@ const Mule = { statusString: "", /** - * @description background worker to prevent idle disconnect - */ + * @description background worker to prevent idle disconnect + */ antiIdle: function () { if (!Starter.inGame) return true; if (!me.ingame || getTickCount() - me.gamestarttime < Time.minutes(1) || !me.gameReady) return true; @@ -146,9 +146,9 @@ const Mule = { }, /** - * @description background worker to prevent suicide walks - * @todo figure out why this bugs out when used - */ + * @description background worker to prevent suicide walks + * @todo figure out why this bugs out when used + */ areaWatcher: function () { if (!Starter.inGame) return true; // run area check every half second @@ -200,7 +200,7 @@ const Mule = { } while (!Attack.validSpot(coord.x, coord.y)); Pather.moveToUnit(coord); - + Storage.Init(); Starter.inGame = true; if (Mule.continuousMule) { @@ -230,8 +230,8 @@ const Mule = { }, /** - * @todo check if there are any other profiles that need to mule while we are already in game? - */ + * @todo check if there are any other profiles that need to mule while we are already in game? + */ done: function () { !muleObj.onlyLogWhenFull && MuleLogger.logChar(); @@ -273,7 +273,7 @@ const Mule = { Mule.quit(); }, - + quit: function () { ["default.dbj", "threads/AntiIdle.js", "threads/AreaWatcher.js"].forEach(thread => { let script = getScript(thread); @@ -296,20 +296,20 @@ const Mule = { }, /** - * @todo handle when a bot wants to mule while we are refreshing the game - */ + * @todo handle when a bot wants to mule while we are refreshing the game + */ gameRefresh: function () { if (!this.continuousMule) return this.quit(); console.log("MaxGameTime Reached: "); removeEventListener("gameevent", gameEvent); Mule.quit(); - + Starter.firstLogin = true; console.log("updating runs"); D2Bot.updateRuns(); status = "ready"; Starter.inGame = false; - + delay(1000); Controls.LobbyQuit.click(); // Quit from Lobby ControlAction.timeoutDelay("Refresh game", 330 * 1000); // 5.5 minutes @@ -318,8 +318,8 @@ const Mule = { }, /** - * @param {number} time - */ + * @param {number} time + */ ingameTimeout: function (time) { let tick = getTickCount(); @@ -379,7 +379,7 @@ const Mule = { cursorItem.drop(); } } - + return true; }, @@ -597,7 +597,7 @@ function locationAction (location) { console.debug("Failed to get in game, current location: " + getLocation() + " inGame? " + me.ingame + " area? " + me.area); break; } - + status = "pending"; break; diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 6a4297248..ba1bb7d3e 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -34,7 +34,7 @@ let chars = []; function parseInfo() { usingDroper && parseDropperAccounts(accounts, chars); - + for (let i in MuleLogger.LogAccounts) { if (MuleLogger.LogAccounts.hasOwnProperty(i) && typeof i === "string") { accounts.push(i); @@ -178,7 +178,7 @@ function locationAction (location) { if (FileTools.exists("logs/MuleLog.json")) { obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); - + if (obj.currChar) { for (i = 0; i < charList.length; i += 1) { if (charList[i] === obj.currChar) { diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index f6769c62e..e48bc4c2d 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -22,23 +22,23 @@ Starter.Config.AttemptNextGameRetrys = 5; * @todo this section should be in it's own config leaving this file only containing core logic */ /** - IncludeFilter config: + IncludeFilter config: - Multiple entries in the same array mean AND: - ["baal", "-"] = game has to contain "baal" and "-" + Multiple entries in the same array mean AND: + ["baal", "-"] = game has to contain "baal" and "-" - Different entries mean OR: - ["baal"], - ["diablo"] - will join games with either "baal" or "diablo" in their name + Different entries mean OR: + ["baal"], + ["diablo"] + will join games with either "baal" or "diablo" in their name - Similar rules apply to ExcludeFilter: + Similar rules apply to ExcludeFilter: - ["somebaal", "-"] ignores games with "somebaal" and "-" in the game name + ["somebaal", "-"] ignores games with "somebaal" and "-" in the game name - ["somebaal"], - ["somediablo"] - this will ignore all games with "somebaal" or "somediablo" in their names + ["somebaal"], + ["somediablo"] + this will ignore all games with "somebaal" or "somediablo" in their names */ const IncludeFilter = [ diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 128b08a01..7a75165f1 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -51,15 +51,15 @@ function main () { 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); } @@ -147,7 +147,7 @@ function main () { if (Config.PublicMode) { Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); } - + Config.AntiHostile && load("threads/AntiHostile.js"); if (Config.FastPick) { @@ -162,7 +162,7 @@ function main () { 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); diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index a73188063..c1fe433c8 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -32,11 +32,11 @@ includeIfNotIncluded("oog/D2Bot.js"); // required realms: { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }, /** - * @param {string} text - * @param {number} time - in milliseconds - * @param {Function} [stopfunc] - * @param {*} [arg] - */ + * @param {string} text + * @param {number} time - in milliseconds + * @param {Function} [stopfunc] + * @param {*} [arg] + */ timeoutDelay: function (text, time, stopfunc, arg) { let currTime = 0; let endTime = getTickCount() + time; @@ -61,15 +61,15 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {number} type - * @param {number} x - * @param {number} y - * @param {number} xsize - * @param {number} ysize - * @param {number} targetx - * @param {number} targety - * @returns {boolean} - */ + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + * @param {number} targetx + * @param {number} targety + * @returns {boolean} + */ click: function (type, x, y, xsize, ysize, targetx, targety) { let control = getControl(type, x, y, xsize, ysize); @@ -85,14 +85,14 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {number} type - * @param {number} x - * @param {number} y - * @param {number} xsize - * @param {number} ysize - * @param {string} text - * @returns {boolean} - */ + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + * @param {string} text + * @returns {boolean} + */ setText: function (type, x, y, xsize, ysize, text) { if (!text) return false; @@ -114,13 +114,13 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {number} type - * @param {number} x - * @param {number} y - * @param {number} xsize - * @param {number} ysize - * @returns {string[] | false} - */ + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + * @returns {string[] | false} + */ getText: function (type, x, y, xsize, ysize) { let control = getControl(type, x, y, xsize, ysize); @@ -195,20 +195,20 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @typedef {Object} CharacterInfo - * @property {string} charName - * @property {string} charClass - * @property {number} charLevel - * @property {boolean} expansion - * @property {boolean} hardcore - * @property {boolean} ladder - */ + * @typedef {Object} CharacterInfo + * @property {string} charName + * @property {string} charClass + * @property {number} charLevel + * @property {boolean} expansion + * @property {boolean} hardcore + * @property {boolean} ladder + */ /** - * @param {CharacterInfo} info - * @param {boolean} [startFromTop] - * @returns {Control | false} - */ + * @param {CharacterInfo} info + * @param {boolean} [startFromTop] + * @returns {Control | false} + */ findCharacter: function (info, startFromTop = true) { let count = 0; let tick = getTickCount(); @@ -293,9 +293,9 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {CharacterInfo} info - * @returns {boolean} - */ + * @param {CharacterInfo} info + * @returns {boolean} + */ getPermStatus: function (info) { let expireStr = getLocaleString(sdk.locale.text.ExpiresIn); expireStr = expireStr.slice(0, expireStr.indexOf("%")).trim(); @@ -310,9 +310,9 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * get character position - useless? this doesn't take any arguments to even check the character - * @returns {number} - */ + * get character position - useless? this doesn't take any arguments to even check the character + * @returns {number} + */ getPosition: function () { let position = 0; @@ -334,9 +334,9 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {CharacterInfo} info - * @returns {boolean} - */ + * @param {CharacterInfo} info + * @returns {boolean} + */ makeCharacter: function (info) { me.blockMouse = true; !info.charClass && (info.charClass = "barbarian"); @@ -416,9 +416,9 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {CharacterInfo} info - * @returns {boolean} - */ + * @param {CharacterInfo} info + * @returns {boolean} + */ deleteCharacter: function (info) { let control = this.findCharacter(info); if (!control) return false; @@ -443,9 +443,9 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {CharacterInfo} info - * @returns {boolean} - */ + * @param {CharacterInfo} info + * @returns {boolean} + */ convertCharacter: function (info) { let control = this.findCharacter(info); if (!control) return false; @@ -453,7 +453,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required if (control.getText().find(el => el.toLowerCase().includes("expansion"))) { console.warn(info.charName + " already expansion"); console.debug(control, "\n", control.getText()); - + return false; } @@ -477,13 +477,13 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * @param {CharacterInfo} info - * @param {boolean} startFromTop - * @returns {boolean} - */ + * @param {CharacterInfo} info + * @param {boolean} startFromTop + * @returns {boolean} + */ loginCharacter: function (info, startFromTop = true) { me.blockMouse = true; - + try { MainLoop: // cycle until in lobby or in game @@ -492,7 +492,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required case sdk.game.locations.CharSelect: let control = this.findCharacter(info, startFromTop); if (!control) return false; - + control.click(); Controls.BottomRightOk.click(); Starter.locationTimeout(sdk.game.locations.CharSelect, 5000); @@ -529,7 +529,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required if (!email || !email.length) { email = Starter.randomString(null, true); } - + while (getLocation() !== sdk.game.locations.CharSelect) { switch (getLocation()) { case sdk.game.locations.RegisterEmail: @@ -543,7 +543,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required // todo test what conditions get here other than email not matching D2Bot.printToConsole("Failed to set email"); Controls.LoginErrorOk.click(); - + return false; case sdk.game.locations.CharSelectNoChars: // fresh acc @@ -558,7 +558,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required me.blockMouse = true; let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; - + // cycle until in empty char screen MainLoop: while (getLocation() !== sdk.game.locations.CharSelectNoChars) { @@ -788,7 +788,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required me.blockMouse = false; }, - + getGameList: function () { let text = Controls.JoinGameList.getText(); @@ -838,7 +838,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; case sdk.game.locations.SelectDifficultySP: Starter.LocationEvents.selectDifficultySP(); - + break; case sdk.game.locations.SplashScreen: ControlAction.click(); @@ -857,7 +857,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; } - + Controls.OtherMultiplayer.click(); break; @@ -888,7 +888,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required if (me.charname !== Starter.profileInfo.charName) { Controls.LobbyQuit.click(); - + break; } @@ -904,7 +904,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; } } - + // handling Enter Ip inside entry for now so that location === sucess return (me.ingame || getLocation() === [sdk.game.locations.TcpIpEnterIp]); } @@ -962,7 +962,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required setNextGame: function (gameInfo = {}) { let nextGame = (gameInfo.gameName || this.randomString(null, true)); - + if ((this.gameCount + 1 >= Starter.Config.ResetCount) || (nextGame.length + this.gameCount + 1 > 15)) { nextGame += "1"; } else { @@ -1028,10 +1028,10 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }, /** - * Handle copy data event - * @param {number} mode - * @param {object | string} msg - */ + * Handle copy data event + * @param {number} mode + * @param {object | string} msg + */ receiveCopyData: function (mode, msg) { let obj; @@ -1230,7 +1230,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } else { Controls.UnableToConnectOk.click(); ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); - + return; } @@ -1271,7 +1271,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required Controls.LoginErrorOk.click(); delay(1000); Controls.BottomLeftExit.click(); - + while (true) { delay(1000); } @@ -1307,12 +1307,12 @@ includeIfNotIncluded("oog/D2Bot.js"); // required // Click create char button on infinite "connecting" screen Controls.CharSelectCreate.click(); delay(1000); - + Controls.BottomLeftExit.click(); delay(1000); - + if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; - + Controls.BottomLeftExit.click(); Starter.gameInfo.rdBlocker && D2Bot.restart(); @@ -1433,17 +1433,17 @@ includeIfNotIncluded("oog/D2Bot.js"); // required i !== text.length - 1 && (string += " "); } } - + switch (string) { case getLocaleString(sdk.locale.text.UnableToIndentifyVersion): Controls.UnableToConnectOk.click(); ControlAction.timeoutDelay("Version error", Starter.Config.VersionErrorDelay * 1000); - + break; default: // Regular UTC and everything else Controls.UnableToConnectOk.click(); ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 1000 * 60); - + break; } @@ -1522,8 +1522,8 @@ includeIfNotIncluded("oog/D2Bot.js"); // required if (getLocation() === sdk.game.locations.MainMenu) { if (Profile().type === sdk.game.profiletype.SinglePlayer - && Starter.firstRun - && Controls.SinglePlayer.click()) { + && Starter.firstRun + && Controls.SinglePlayer.click()) { return true; } } @@ -1532,18 +1532,18 @@ includeIfNotIncluded("oog/D2Bot.js"); // required 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))) { + || ((Profile().type !== sdk.game.profiletype.Battlenet && Controls.CharSelectCurrentRealm.control))) { Controls.BottomLeftExit.click(); - + return false; } } // 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 { login(me.profile); } catch (e) { diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index c83ea8e45..5085b0788 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -55,10 +55,10 @@ const Attack = { }, /** - * @description check if slot has items - * @param {0 | 1} slot - * @returns {boolean} If weapon slot has an item equipped - */ + * @description check if slot has items + * @param {0 | 1} slot + * @returns {boolean} If weapon slot has an item equipped + */ checkSlot: function (slot = me.weaponswitch) { let item = me.getItem(-1, sdk.items.mode.Equipped); @@ -80,9 +80,9 @@ const Attack = { }, /** - * @description Automatically determine primary weapon slot, Weapon slot with items that isn't a CTA - * @returns {0 | 1 | -1} Primary weapon slot - */ + * @description Automatically determine primary weapon slot, Weapon slot with items that isn't a CTA + * @returns {0 | 1 | -1} Primary weapon slot + */ getPrimarySlot: function () { // determine primary slot if not set if (Config.PrimarySlot === -1) { @@ -114,17 +114,17 @@ const Attack = { }, /** - * - * @param {Monster} unit - * @returns {[number, number] | boolean} - * @todo add checking for other options than just name/classid - * - option for based on spectype - * - option for based on enchant/aura - */ + * + * @param {Monster} unit + * @returns {[number, number] | boolean} + * @todo add checking for other options than just name/classid + * - option for based on spectype + * - option for based on enchant/aura + */ getCustomAttack: function (unit) { // Check if unit got invalidated if (!unit || !unit.name || !copyUnit(unit).x) return false; - + for (let i in Config.CustomAttack) { if (Config.CustomAttack.hasOwnProperty(i)) { // if it contains numbers but is a string, convert to an int @@ -152,10 +152,10 @@ const Attack = { }, /** - * @depreciated - * @description Get items with charges - isn't used anywhere - * @returns {boolean} - */ + * @depreciated + * @description Get items with charges - isn't used anywhere + * @returns {boolean} + */ getCharges: function () { !Skill.charges && (Skill.charges = []); @@ -197,9 +197,9 @@ const Attack = { }, /** - * @description Check if player or his merc are using Infinity, and adjust resistance checks based on that - * @returns {boolean} - */ + * @description Check if player or his merc are using Infinity, and adjust resistance checks based on that + * @returns {boolean} + */ checkInfinity: function () { if (me.classic) return false; @@ -217,9 +217,9 @@ const Attack = { }, /** - * @description Check if player is using Dragon, Dream, HoJ, or Ice, and adjust resistance checks based on that - * @returns {boolean} - */ + * @description Check if player is using Dragon, Dream, HoJ, or Ice, and adjust resistance checks based on that + * @returns {boolean} + */ checkAuradin: function () { Attack.auradin = me.haveSome([ { name: sdk.locale.items.Dragon, equipped: true }, { name: sdk.locale.items.Dream, equipped: true }, @@ -230,10 +230,10 @@ const Attack = { }, /** - * @description check if we can telestomp a unit - * @param {Unit} unit - * @returns {boolean} - */ + * @description check if we can telestomp a unit + * @param {Unit} unit + * @returns {boolean} + */ canTeleStomp: function (unit) { if (!unit || !unit.attackable) return false; return Config.TeleStomp && Config.UseMerc && Pather.canTeleport() @@ -241,10 +241,10 @@ const Attack = { }, /** - * @description Kill a monster based on its classId, can pass a unit as well - * @param {Unit | number} classId - * @returns {boolean} If we managed to kill the unit - */ + * @description Kill a monster based on its classId, can pass a unit as well + * @param {Unit | number} classId + * @returns {boolean} If we managed to kill the unit + */ kill: function (classId) { if (!classId || Config.AttackSkill[1] < 0) return false; let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); @@ -282,7 +282,7 @@ const Attack = { 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); @@ -341,11 +341,11 @@ const Attack = { }, /** - * @description hurt a unit to a certain percentage of life left - * @param {string | number | Unit} classId - * @param {number} percent - * @returns {boolean} - */ + * @description hurt a unit to a certain percentage of life left + * @param {string | number | Unit} classId + * @param {number} percent + * @returns {boolean} + */ hurt: function (classId, percent) { if (!classId || !percent) return false; let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); @@ -395,10 +395,10 @@ const Attack = { }, /** - * @description Determine scariness of monster for monster sorting - * @param {Unit} unit - * @returns {number} scariness - */ + * @description Determine scariness of monster for monster sorting + * @param {Unit} unit + * @returns {number} scariness + */ getScarinessLevel: function (unit) { // todo - define summonertype prototype let scariness = 0; @@ -430,15 +430,15 @@ const Attack = { }, /** - * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster - * @param {number} [range=25] - * @param {number} [spectype=0] - * @param {number | Unit} [bossId] - * @param {Function} [sortfunc] - * @param {boolean} [pickit] - * @returns {boolean} - * @todo change to passing an object - */ + * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster + * @param {number} [range=25] + * @param {number} [spectype=0] + * @param {number | Unit} [bossId] + * @param {Function} [sortfunc] + * @param {boolean} [pickit] + * @returns {boolean} + * @todo change to passing an object + */ clear: function (range, spectype, bossId, sortfunc, pickit = true) { while (!me.gameReady) { delay(40); @@ -504,7 +504,7 @@ const Attack = { while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { if (me.dead) return false; - + boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); monsterList.sort(sortfunc); // target = copyUnit(monsterList[0]); @@ -512,7 +512,7 @@ const Attack = { if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range || (this.getScarinessLevel(target) > 7 && target.distance <= range)) - && target.attackable) { + && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); Misc.townCheck(); tick = getTickCount(); @@ -554,7 +554,7 @@ const Attack = { let hammerCheck = me.paladin && checkSkill === sdk.skills.BlessedHammer; if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, checkSkill) - || (hammerCheck && !ClassAttack.getHammerPosition(target)))) { + || (hammerCheck && !ClassAttack.getHammerPosition(target)))) { skillCheck = Config.AttackSkill[secAttack]; } else { skillCheck = checkSkill; @@ -587,8 +587,8 @@ const Attack = { } /** - * @todo allow for more aggressive horking here - */ + * @todo allow for more aggressive horking here + */ if (target.dead || Config.FastPick || Config.FastFindItem) { if (boss && boss.gid === target.gid && target.dead) { killedBoss = true; @@ -642,13 +642,13 @@ const Attack = { }, /** - * @description clear all monsters based on classid arguments - * @param {...number} ids - * @returns {boolean} - * @todo - * - Should there be a range parameter for this? - * - Should we keep track of where we started from? - */ + * @description clear all monsters based on classid arguments + * @param {...number} ids + * @returns {boolean} + * @todo + * - Should there be a range parameter for this? + * - Should we keep track of where we started from? + */ clearClassids: function (...ids) { // lets keep track of where we started from and move back when done const { x, y } = me; @@ -684,13 +684,13 @@ const Attack = { }, /** - * @description Filter monsters based on classId, spectype and range - * @param {number} classid - * @param {number} spectype - * @param {number} range - * @param {Unit | {x: number, y: number}} center - * @returns {Monster[]} - */ + * @description Filter monsters based on classId, spectype and range + * @param {number} classid + * @param {number} spectype + * @param {number} range + * @param {Unit | {x: number, y: number}} center + * @returns {Monster[]} + */ getMob: function (classid, spectype, range, center) { let monsterList = []; let monster = Game.getMonster(); @@ -706,7 +706,7 @@ const Attack = { if (monster) { do { if (getDistance(center.x, center.y, monster.x, monster.y) <= range - && (!spectype || (monster.spectype & spectype)) && monster.attackable) { + && (!spectype || (monster.spectype & spectype)) && monster.attackable) { monsterList.push(copyUnit(monster)); } } while (monster.getNext()); @@ -719,7 +719,7 @@ const Attack = { if (monster) { do { if (classid.includes(monster.classid) && getDistance(center.x, center.y, monster.x, monster.y) <= range - && (!spectype || (monster.spectype & spectype)) && monster.attackable) { + && (!spectype || (monster.spectype & spectype)) && monster.attackable) { monsterList.push(copyUnit(monster)); } } while (monster.getNext()); @@ -732,12 +732,12 @@ const Attack = { }, /** - * @description Clear an already formed array of monstas - * @param {Function | Array} mainArg - * @param {Function} [sortFunc] - * @param {boolean} [refresh] - * @returns {boolean} - */ + * @description Clear an already formed array of monstas + * @param {Function | Array} mainArg + * @param {Function} [sortFunc] + * @param {boolean} [refresh] + * @returns {boolean} + */ clearList: function (mainArg, sortFunc, refresh) { /** @type {Monster[]} */ let monsterList; @@ -859,14 +859,14 @@ const Attack = { }, /** - * @param {number} x - * @param {number} y - * @param {number} [range=15] - * @param {number} [timer=3000] - time in ms - * @param {boolean} [skipBlocked=true] - * @param {boolean} [special=false] - * @returns {void} - */ + * @param {number} x + * @param {number} y + * @param {number} [range=15] + * @param {number} [timer=3000] - time in ms + * @param {boolean} [skipBlocked=true] + * @param {boolean} [special=false] + * @returns {void} + */ securePosition: function (x, y, range = 15, timer = 3000, skipBlocked = true, special = false) { let tick; @@ -882,8 +882,8 @@ const Attack = { if (monster) { do { if (getDistance(monster, x, y) <= range && monster.attackable && this.canAttack(monster) - && (!skipBlocked || !checkCollision(me, monster, skipBlocked)) - && (Pather.canTeleport() || !checkCollision(me, monster, sdk.collision.BlockWall))) { + && (!skipBlocked || !checkCollision(me, monster, skipBlocked)) + && (Pather.canTeleport() || !checkCollision(me, monster, sdk.collision.BlockWall))) { monList.push(copyUnit(monster)); } } while (monster.getNext()); @@ -915,10 +915,10 @@ const Attack = { }, /** - * @description Draw lines around a room on minimap - * @param {Room} room - * @param {number} color - */ + * @description Draw lines around a room on minimap + * @param {Room} room + * @param {number} color + */ markRoom: function (room, color) { let arr = []; const [rX, rY] = [room.x * 5, room.y * 5]; @@ -930,8 +930,8 @@ const Attack = { }, /** - * @description Count uniques in current area within getUnit range - */ + * @description Count uniques in current area within getUnit range + */ countUniques: function () { !Attack.uniques && (Attack.uniques = 0); !Attack.ignoredGids && (Attack.ignoredGids = []); @@ -949,9 +949,9 @@ const Attack = { }, /** - * @description Store average unique monsters counted in area during run - * @param {number} area - */ + * @description Store average unique monsters counted in area during run + * @param {number} area + */ storeStatistics: function (area) { !FileTools.exists("statistics.json") && FileAction.write("statistics.json", "{}"); @@ -978,10 +978,10 @@ const Attack = { }, /** - * @description Clear an entire area based on monster spectype - * @param {number} spectype - * @returns {boolean} - */ + * @description Clear an entire area based on monster spectype + * @param {number} spectype + * @returns {boolean} + */ clearLevel: function (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]); @@ -1002,7 +1002,7 @@ const Attack = { do { rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); } while (room.getNext()); - + if (Config.MFLeader && rooms.length > 0) { Pather.makePortal(); console.log("clearlevel " + getAreaName(currentArea)); @@ -1064,13 +1064,13 @@ const Attack = { }, /** - * @description Sort monsters based on distance, spectype and classId (summoners are attacked first) - * @param {Unit} unitA - * @param {Unit} unitB - * @returns {boolean} - * @todo Think this needs a collison check included for non tele chars, might prevent choosing - * closer mob that is actually behind a wall vs the one we pass trying to get behind the wall - */ + * @description Sort monsters based on distance, spectype and classId (summoners are attacked first) + * @param {Unit} unitA + * @param {Unit} unitB + * @returns {boolean} + * @todo Think this needs a collison check included for non tele chars, might prevent choosing + * closer mob that is actually behind a wall vs the one we pass trying to get behind the wall + */ sortMonsters: function (unitA, unitB) { // No special sorting for were-form if (Config.Wereform) return getDistance(me, unitA) - getDistance(me, unitB); @@ -1149,23 +1149,23 @@ const Attack = { }, /** - * @description Check if a set of coords is valid/accessable - * @param {number} x - * @param {number} y - * @param {number} [skill=-1] - * @param {number} [unitid=0] - * @returns {boolean} If the spot is a valid location for walking/casting/attack - * @todo re-work this for more info: - * - casting skills can go over non-floors - excluding bliz/meteor - not sure if any others - * - physical skills can't, need to exclude monster objects though - * - splash skills can go through some objects, however some objects are cast blockers - */ + * @description Check if a set of coords is valid/accessable + * @param {number} x + * @param {number} y + * @param {number} [skill=-1] + * @param {number} [unitid=0] + * @returns {boolean} If the spot is a valid location for walking/casting/attack + * @todo re-work this for more info: + * - casting skills can go over non-floors - excluding bliz/meteor - not sure if any others + * - physical skills can't, need to exclude monster objects though + * - splash skills can go through some objects, however some objects are cast blockers + */ validSpot: function (x, y, skill = -1, unitid = 0) { // Just in case if (!me.area || !x || !y) return false; // for now this just returns true and we leave getting into position to the actual class attack files if (Skill.missileSkills.includes(skill) - || ([sdk.skills.Blizzard, sdk.skills.Meteor].includes(skill) + || ([sdk.skills.Blizzard, sdk.skills.Meteor].includes(skill) && unitid > 0 && !getBaseStat("monstats", unitid, "flying"))) { return true; } @@ -1210,12 +1210,12 @@ const Attack = { }, /** - * @description Open chests when clearing - * @param {number} range - * @param {number} [x=me.x] - * @param {number} [y=me.y] - * @returns {boolean} - */ + * @description Open chests when clearing + * @param {number} range + * @param {number} [x=me.x] + * @param {number} [y=me.y] + * @returns {boolean} + */ openChests: function (range, x, y) { if (!Config.OpenChests.Enabled) return false; (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); @@ -1245,10 +1245,10 @@ const Attack = { }, /** - * @description build list of attackable monsters currently around us - * @param {function(): boolean} check - callback function to build list - * @returns {Array | []} - */ + * @description build list of attackable monsters currently around us + * @param {function(): boolean} check - callback function to build list + * @returns {Array | []} + */ buildMonsterList: function (check = () => true) { let monList = []; let monster = Game.getMonster(); @@ -1265,12 +1265,12 @@ const Attack = { }, /** - * @param {Unit} unit - * @param {number} distance - * @param {number} spread - * @param {number} range - * @returns {{x: number, y: number}} x/y coords of safe spot - */ + * @param {Unit} unit + * @param {number} distance + * @param {number} spread + * @param {number} range + * @returns {{x: number, y: number}} x/y coords of safe spot + */ findSafeSpot: function (unit, distance, spread, range) { if (arguments.length < 4) throw new Error("deploy: Not enough arguments supplied"); @@ -1368,10 +1368,10 @@ const Attack = { }, /** - * @description checks if we should skip a monster - * @param {Unit} unit - * @returns {Boolean} If we should skip this monster - */ + * @description checks if we should skip a monster + * @param {Unit} unit + * @returns {Boolean} If we should skip this monster + */ skipCheck: function (unit) { if (me.inArea(sdk.areas.ThroneofDestruction)) return false; if (unit.isSpecial && Config.SkipException && Config.SkipException.includes(unit.name)) { @@ -1477,10 +1477,10 @@ const Attack = { }, /** - * @description Get element by skill number - * @param {number} skillId - * @returns {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none" | false} - */ + * @description Get element by skill number + * @param {number} skillId + * @returns {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none" | false} + */ getSkillElement: function (skillId) { let elements = ["physical", "fire", "lightning", "magic", "cold", "poison", "none"]; @@ -1509,11 +1509,11 @@ const Attack = { }, /** - * @description Get a monster's resistance to specified element - * @param {Unit | Monster} unit - * @param {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"} type - * @returns {number} - */ + * @description Get a monster's resistance to specified element + * @param {Unit | Monster} unit + * @param {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"} type + * @returns {number} + */ getResist: function (unit, type) { // some scripts pass empty units in throne room if (!unit || !unit.getStat) return 100; @@ -1631,10 +1631,10 @@ const Attack = { }, /** - * Check if we have valid skills to attack a monster - * @param {Monster} unit - * @returns {boolean} - */ + * Check if we have valid skills to attack a monster + * @param {Monster} unit + * @returns {boolean} + */ canAttack: function (unit) { if (unit.isMonster) { // Unique/Champion @@ -1660,9 +1660,9 @@ const Attack = { }, /** - * Detect use of bows/crossbows - * @returns {string | false} - */ + * Detect use of bows/crossbows + * @returns {string | false} + */ usingBow: function () { let item = me.getItem(-1, sdk.items.mode.Equipped); @@ -1684,14 +1684,14 @@ const Attack = { }, /** - * Find an optimal attack position and move or walk to it - * @param {Unit} unit - * @param {number} distance - * @param {number} coll - * @param {boolean} walk - * @param {boolean} force - * @returns {boolean} sucessfully found and moved into position - */ + * Find an optimal attack position and move or walk to it + * @param {Unit} unit + * @param {number} distance + * @param {number} coll + * @param {boolean} walk + * @param {boolean} force + * @returns {boolean} sucessfully found and moved into position + */ getIntoPosition: function (unit, distance, coll, walk, force) { if (!unit || !unit.x || !unit.y) return false; @@ -1700,8 +1700,8 @@ const Attack = { force && console.debug("Forcing new position"); /** - * @todo If we've disabled tele for walking clear, allow use of tele specifically for repositioning - */ + * @todo If we've disabled tele for walking clear, allow use of tele specifically for repositioning + */ if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { if (walk) { @@ -1760,7 +1760,7 @@ const Attack = { if (Config.DebugMode.Path && force) { console.debug( "Sucessfully got into position. orginal Loc: " + orgX + "/" + orgY - + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance + + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance ); } return true; @@ -1775,10 +1775,10 @@ const Attack = { }, /** - * Find the nearest monster to us with optional exception parameters - * @param {{ skipBlocked?: boolean, skipImmune?: boolean, skipGid?: number}} givenSettings - * @returns {Monster | false} - */ + * Find the nearest monster to us with optional exception parameters + * @param {{ skipBlocked?: boolean, skipImmune?: boolean, skipGid?: number}} givenSettings + * @returns {Monster | false} + */ getNearestMonster: function (givenSettings = {}) { const settings = Object.assign({}, { skipBlocked: true, @@ -1796,9 +1796,9 @@ const Attack = { let distance = getDistance(me, monster); if (distance < range - && (settings.skipGid === -1 || monster.gid !== settings.skipGid) - && (!settings.skipBlocked || !checkCollision(me, monster, sdk.collision.WallOrRanged)) - && (!settings.skipImmune || Attack.canAttack(monster))) { + && (settings.skipGid === -1 || monster.gid !== settings.skipGid) + && (!settings.skipBlocked || !checkCollision(me, monster, sdk.collision.WallOrRanged)) + && (!settings.skipImmune || Attack.canAttack(monster))) { range = distance; gid = monster.gid; } @@ -1810,10 +1810,10 @@ const Attack = { }, /** - * Check valid corpse for Redemption/Horking/Summoning - * @param {Monster} unit - * @returns {boolean} valid corpse - */ + * Check valid corpse for Redemption/Horking/Summoning + * @param {Monster} unit + * @returns {boolean} valid corpse + */ checkCorpse: function (unit) { if (!unit || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) return false; if (unit.classid <= sdk.monsters.BurningDeadArcher2 && !getBaseStat("monstats2", unit.classid, "corpseSel")) { @@ -1826,11 +1826,11 @@ const Attack = { }, /** - * Get valid corpses for Redemption/Horking/Summoning - * @param {Monster} unit - * @param {number} range - * @returns {Monster[]} - */ + * Get valid corpses for Redemption/Horking/Summoning + * @param {Monster} unit + * @param {number} range + * @returns {Monster[]} + */ checkNearCorpses: function (unit, range = 15) { let corpses = getUnits(sdk.unittype.Monster).filter(function (corpse) { return getDistance(corpse, unit) <= range && Attack.checkCorpse(corpse); @@ -1839,9 +1839,9 @@ const Attack = { }, /** - * @param {Monster} unit - * @returns {boolean} - */ + * @param {Monster} unit + * @returns {boolean} + */ whirlwind: function (unit) { if (!unit.attackable) return true; diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 6768d9631..6e1e2485c 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -145,7 +145,7 @@ const ClassAttack = { } let closeMob = Attack.getNearestMonster({ skipGid: gid }); - + if (!!closeMob) { let findSkill = this.decideSkill(closeMob); if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { @@ -176,7 +176,7 @@ const ClassAttack = { // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; Config.TeleSwitch && me.switchToPrimary(); - + // Arrow/bolt check if (this.bowCheck) { switch (true) { diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 130850626..deebc5d38 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -59,7 +59,7 @@ const ClassAttack = { 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) + || (checkCollision(me, unit, sdk.collision.BlockWall) && (getCollision(me.area, unit.x, unit.y) & sdk.collision.BlockWall))) { return Attack.Result.FAILED; } @@ -162,7 +162,7 @@ const ClassAttack = { // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); - + let walk; let classid = unit.classid; @@ -234,7 +234,7 @@ const ClassAttack = { // getDistance crashes when using an object with x, y props, that's why it's unit.x, unit.y and not unit // is this still a thing ^^? todo: test it if (me.getMinionCount(sdk.summons.type.AssassinTrap) === 0 || !this.lastTrapPos.hasOwnProperty("x") - || getDistance(unit.x, unit.y, this.lastTrapPos.x, this.lastTrapPos.y) > 15) { + || getDistance(unit.x, unit.y, this.lastTrapPos.x, this.lastTrapPos.y) > 15) { return 5; } @@ -260,7 +260,7 @@ const ClassAttack = { && [ sdk.monsters.Duriel, sdk.monsters.Mephisto, sdk.monsters.Diablo, sdk.monsters.Baal ].includes(unit.classid)) - || (unit.hasOwnProperty("type") && unit.isPlayer)) { + || (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); diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index 3ef67d8c8..cd8d7a055 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -13,10 +13,10 @@ const ClassAttack = { /** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {AttackResult} - */ + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ doAttack: function (unit, preattack = false) { if (!unit) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); @@ -64,11 +64,11 @@ const ClassAttack = { && Attack.checkResist(unit, Config.LowManaSkill[0])) { attackSkill = Config.LowManaSkill[0]; } - + // low weapon-quantity -> use secondary skill if we can if (attackSkill === sdk.skills.DoubleThrow && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) - && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { + && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { attackSkill = Config.AttackSkill[index + 1]; } @@ -77,9 +77,9 @@ const ClassAttack = { }, /** - * Check if we need to precast, repair items, or perform findItem - * @param {boolean} pickit - determines if we use findItem or not - */ + * Check if we need to precast, repair items, or perform findItem + * @param {boolean} pickit - determines if we use findItem or not + */ afterAttack: function (pickit = true) { Precast.doPrecast(false); @@ -91,22 +91,22 @@ const ClassAttack = { }, /** - * @param {Monster} unit - * @param {number} attackSkill - * @returns {AttackResult} - */ + * @param {Monster} unit + * @param {number} attackSkill + * @returns {AttackResult} + */ doCast: function (unit, attackSkill = -1) { if (attackSkill < 0) return Attack.Result.CANTATTACK; // check if unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); - + switch (attackSkill) { case sdk.skills.Whirlwind: /** - * @todo we sometimes struggle getting into position because of monsters which is dumb since we can - * just whirl through them, so that needs to be fixed - */ + * @todo we sometimes struggle getting into position because of monsters which is dumb since we can + * just whirl through them, so that needs to be fixed + */ 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; @@ -139,10 +139,10 @@ const ClassAttack = { }, /** - * Check whether there are any monsters in range that are attackable - * @param {number} range - * @returns {boolean} - */ + * Check whether there are any monsters in range that are attackable + * @param {number} range + * @returns {boolean} + */ checkCloseMonsters: function (range = 10) { const [mainAttElm, secAttElm] = [ Attack.getSkillElement(Config.AttackSkill[1]), @@ -153,8 +153,8 @@ const ClassAttack = { if (monster) { do { if (monster.distance <= range && monster.attackable && !checkCollision(me, monster, sdk.collision.Ranged) - && (Attack.checkResist(monster, monster.isSpecial ? mainAttElm : secAttElm)) - || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, secAttElm))) { + && (Attack.checkResist(monster, monster.isSpecial ? mainAttElm : secAttElm)) + || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, secAttElm))) { return true; } } while (monster.getNext()); @@ -164,10 +164,10 @@ const ClassAttack = { }, /** - * Use findItem skill to hork bodies - * @param {number} range - * @returns {boolean} - */ + * Use findItem skill to hork bodies + * @param {number} range + * @returns {boolean} + */ findItem: function (range = 10) { if (!Skill.canUse(sdk.skills.FindItem)) return false; @@ -254,17 +254,17 @@ const ClassAttack = { }, /** - * Check if corpse is horkable - * @param {Monster} unit - * @returns {boolean} - */ + * Check if corpse is horkable + * @param {Monster} unit + * @returns {boolean} + */ checkCorpse: function (unit) { if (!unit || !copyUnit(unit).x || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) { return false; } if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 - && unit.spectype === sdk.monsters.spectype.All) { + && unit.spectype === sdk.monsters.spectype.All) { return false; } diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index 63f9bcbe5..339dd364d 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -138,7 +138,7 @@ const ClassAttack = { // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); - + let walk; let classid = unit.classid; diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index e5fc23a71..46b13bf9f 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -9,10 +9,10 @@ const ClassAttack = { attackAuras: [sdk.skills.HolyFire, sdk.skills.HolyFreeze, sdk.skills.HolyShock], /** - * @param {Unit} unit - * @param {boolean} [preattack] - * @returns {AttackResult} - */ + * @param {Unit} unit + * @param {boolean} [preattack] + * @returns {AttackResult} + */ doAttack: function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); @@ -140,14 +140,14 @@ const ClassAttack = { // only proceed with other checks if we can use redemption and the config values aren't 0 if (Skill.canUse(sdk.skills.Redemption) && Config.Redemption.some(v => v > 0)) { if ((me.hpPercent < Config.Redemption[0] || me.mpPercent < Config.Redemption[1]) - && Attack.checkNearCorpses(me) > 2 && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { + && Attack.checkNearCorpses(me) > 2 && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { delay(1500); } } /** - * @todo add config options for these and possibly add to Pather.walkTo - */ + * @todo add config options for these and possibly add to Pather.walkTo + */ // if (Skill.canUse(sdk.skills.Cleansing) // && ([sdk.states.AmplifyDamage, sdk.states.Decrepify].some(s => me.getState(s)) || me.hpPercent < 70 && me.getState(sdk.states.Poison)) // && !me.checkForMobs({range: 12, coll: sdk.collision.BlockWall}) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { @@ -166,7 +166,7 @@ const ClassAttack = { // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); - + switch (attackSkill) { case sdk.skills.BlessedHammer: // todo: add doll avoid to other classes @@ -250,7 +250,7 @@ const ClassAttack = { 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)) { @@ -346,7 +346,7 @@ const ClassAttack = { return true; } else if (!canTele && ([check.x, check.y].distance < 1 && !CollMap.checkColl(unit, check, sdk.collision.BlockWalk, 0)) - || ([check.x, check.y].distance <= 4 && me.getMobCount(6) > 2)) { + || ([check.x, check.y].distance <= 4 && me.getMobCount(6) > 2)) { return true; } } diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index fbcbf37e1..1b4920b38 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -60,7 +60,7 @@ const ClassAttack = { if (Config.MercWatch && Town.needMerc()) { if (Town.visitTown()) { print("mercwatch"); - + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { console.debug("Lost reference to unit"); return Attack.Result.SUCCESS; @@ -172,7 +172,7 @@ const ClassAttack = { } let closeMob = Attack.getNearestMonster({ skipGid: gid }); - + if (!!closeMob) { let findSkill = this.decideSkill(closeMob); if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { @@ -198,7 +198,7 @@ const ClassAttack = { // unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); - + let walk, noMana = false; let classid = unit.classid; diff --git a/d2bs/kolbot/libs/core/Auto/AutoSkill.js b/d2bs/kolbot/libs/core/Auto/AutoSkill.js index cec5b6e93..575713cb6 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoSkill.js +++ b/d2bs/kolbot/libs/core/Auto/AutoSkill.js @@ -10,22 +10,22 @@ const AutoSkill = new function () { this.save = 0; /* skillBuildOrder - array of skill points to spend in order - save - number of skill points that will not be spent and saved - - skillBuildOrder Settings - Set skillBuildOrder in the array form: [[skill, count, satisfy], [skill, count, satisfy], ... [skill, count, satisfy]] - skill - skill id number (see /sdk/txt/skills.txt) - count - maximum number of skill points to allocate for that skill - satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - - skillBuildOrder = [ - [37, 1, true], [42, 1, true], [54, 1, true], //warmth, static, teleport - [59, 1, false], [55, 7, true], [45, 13, true], //blizzard, glacial spike, ice blast - [59, 7, false], [65, 1, true], //blizzard, cold mastery - [59, 20, false], [65, 20, true], //max blizzard, max cold mastery - [55, 20, true], [45, 20, true], //max glacial spike, max ice blast - ]; - */ + save - number of skill points that will not be spent and saved + + skillBuildOrder Settings + Set skillBuildOrder in the array form: [[skill, count, satisfy], [skill, count, satisfy], ... [skill, count, satisfy]] + skill - skill id number (see /sdk/txt/skills.txt) + count - maximum number of skill points to allocate for that skill + satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + + skillBuildOrder = [ + [37, 1, true], [42, 1, true], [54, 1, true], //warmth, static, teleport + [59, 1, false], [55, 7, true], [45, 13, true], //blizzard, glacial spike, ice blast + [59, 7, false], [65, 1, true], //blizzard, cold mastery + [59, 20, false], [65, 20, true], //max blizzard, max cold mastery + [55, 20, true], [45, 20, true], //max glacial spike, max ice blast + ]; + */ //a function to return false if have all prereqs or a skill if not this.needPreReq = function (skillid) { diff --git a/d2bs/kolbot/libs/core/Auto/AutoStat.js b/d2bs/kolbot/libs/core/Auto/AutoStat.js index a76ebbe43..5e4a8cdea 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoStat.js +++ b/d2bs/kolbot/libs/core/Auto/AutoStat.js @@ -13,22 +13,22 @@ const AutoStat = new function () { this.bulkStat = true; /* statBuildOrder - array of stat points to spend in order - save - remaining stat points that will not be spent and saved. - block - an integer value set to desired block chance. This is ignored in classic. - bulkStat - set true to spend multiple stat points at once (up to 100), or false to spend 1 point at a time. + save - remaining stat points that will not be spent and saved. + block - an integer value set to desired block chance. This is ignored in classic. + bulkStat - set true to spend multiple stat points at once (up to 100), or false to spend 1 point at a time. - statBuildOrder Settings - The script will stat in the order of precedence. You may want to stat strength or dexterity first. + statBuildOrder Settings + The script will stat in the order of precedence. You may want to stat strength or dexterity first. - Set stats to desired integer value, and it will stat *hard points up to the desired value. - You can also set to string value "all", and it will spend all the remaining points. - Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + Set stats to desired integer value, and it will stat *hard points up to the desired value. + You can also set to string value "all", and it will spend all the remaining points. + Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - statBuildOrder = [ - ["strength", 25], ["energy", 75], ["vitality", 75], - ["strength", 55], ["vitality", "all"] - ]; - */ + statBuildOrder = [ + ["strength", 25], ["energy", 75], ["vitality", 75], + ["strength", 55], ["vitality", "all"] + ]; + */ this.getBlock = function () { if (!me.usingShield()) return this.block; @@ -614,7 +614,7 @@ const AutoStat = new function () { this.addStatPoint = function () { this.remaining = me.getStat(sdk.stats.StatPts); - + let hardStats; for (let i = 0; i < this.statBuildOrder.length; i += 1) { diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index e7e73bef4..9501827cc 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -46,8 +46,8 @@ if (monster) { do { if (monster.attackable - && monster.y < 5080 - && (monster.x > 15072 && monster.x < 15118)) { + && monster.y < 5080 + && (monster.x > 15072 && monster.x < 15118)) { switch (monster.classid) { case sdk.monsters.WarpedFallen: case sdk.monsters.WarpedShaman: @@ -235,7 +235,7 @@ 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 @@ -248,7 +248,7 @@ if (Config.AttackSkill[3] === sdk.skills.Tornado) { [15094, 5029].distance > 3 && Pather.moveTo(15106, 5041); - + break; } // eslint-disable-next-line no-fallthrough diff --git a/d2bs/kolbot/libs/core/Common/Cain.js b/d2bs/kolbot/libs/core/Common/Cain.js index 7f4dfe0d1..29a9c2c9a 100644 --- a/d2bs/kolbot/libs/core/Common/Cain.js +++ b/d2bs/kolbot/libs/core/Common/Cain.js @@ -58,7 +58,7 @@ Pickit.pickItem(scroll); Town.goToTown(); Town.npcInteract("Akara"); - + break; case Game.getItem(sdk.quest.item.ScrollofInifuss): Town.goToTown(1); @@ -107,7 +107,7 @@ while (getTickCount() - tick < Time.minutes(2)) { if (Pather.getPortal(sdk.areas.Tristram)) { Pather.usePortal(sdk.areas.Tristram); - + break; } } diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 1316da7d8..61c3eb661 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -8,19 +8,19 @@ (function (Common) { typeof Common !== "object" && (Common = {}); /** - * @todo - * - keep track of seals opened and bosses killed to - * - improve targetting when using getBoss, sometimes we run to the location we want to attack from while running past the boss - * I see this mostly with viz, also have seen seis fail due to not being close enough, even though he spawned - */ + * @todo + * - keep track of seals opened and bosses killed to + * - improve targetting when using getBoss, sometimes we run to the location we want to attack from while running past the boss + * I see this mostly with viz, also have seen seis fail due to not being close enough, even though he spawned + */ Object.defineProperty(Common, "Diablo", { /** - * @namespace Common - * - * @typedef Diablo - * @property {boolean} diabloSpawned - * - */ + * @namespace Common + * + * @typedef Diablo + * @property {boolean} diabloSpawned + * + */ value: { diabloSpawned: false, diaWaitTime: Time.seconds(30), @@ -96,7 +96,7 @@ if (!seal) throw new Error("Seal preset not found. Can't continue."); if (sealPreset.roomy * 5 + sealPreset.y === value - || sealPreset.roomx * 5 + sealPreset.x === value) { + || sealPreset.roomx * 5 + sealPreset.x === value) { return 1; } @@ -104,10 +104,10 @@ }, /** - * - VizLayout - 1 = "Y", 2 = "L" - * - SeisLayout - 1 = "2", 2 = "5" - * - InfLayout - 1 = "I", 2 = "J" - */ + * - VizLayout - 1 = "Y", 2 = "L" + * - SeisLayout - 1 = "2", 2 = "5" + * - InfLayout - 1 = "I", 2 = "J" + */ initLayout: function () { // 1 = "Y", 2 = "L" Common.Diablo.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); @@ -118,10 +118,10 @@ }, /** - * Follow static path - * @param {number[][]} path - * @returns {void} - */ + * Follow static path + * @param {number[][]} path + * @returns {void} + */ followPath: function (path) { if (Config.Diablo.Fast) { let last = path.last(); @@ -129,7 +129,7 @@ Pather.moveToUnit(lastNode); return; } - + for (let i = 0; i < path.length; i++) { this.cleared.length > 0 && this.clearStrays(); @@ -179,9 +179,9 @@ }, /** - * @param {number[] | string[]} sealOrder - * @param {boolean} openSeals - */ + * @param {number[] | string[]} sealOrder + * @param {boolean} openSeals + */ runSeals: function (sealOrder, openSeals = true, recheck = false) { print("seal order: " + sealOrder); Common.Diablo.sealOrder = sealOrder; @@ -209,16 +209,16 @@ }, /** - * Attempt casting telekinesis on seal to activate it - * @param {Unit} seal - * @returns {boolean} - */ + * Attempt casting telekinesis on seal to activate it + * @param {Unit} seal + * @returns {boolean} + */ tkSeal: function (seal) { if (!Skill.useTK(seal)) return false; for (let i = 0; i < 5; i++) { seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); - + if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, seal) && Misc.poll(() => seal.mode, 1000, 100)) { break; @@ -229,10 +229,10 @@ }, /** - * Open one of diablos seals - * @param {number} classid - * @returns {boolean} - */ + * Open one of diablos seals + * @param {number} classid + * @returns {boolean} + */ openSeal: function (classid) { let seal; const mainSeal = [ @@ -312,9 +312,9 @@ }, /** - * @param {boolean} openSeal - * @returns {boolean} - */ + * @param {boolean} openSeal + * @returns {boolean} + */ vizierSeal: function (openSeal = true) { print("Viz layout " + Common.Diablo.vizLayout); let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); @@ -339,9 +339,9 @@ return viz && (viz.distance < Skill.getRange(Config.AttackSkill[1]) || viz.dead); }; /** - * @todo better coords or maybe a delay, viz appears in different locations and sometimes its right where we are moving to - * which is okay for hammerdins or melee chars but not for soft chars like sorcs - */ + * @todo better coords or maybe a delay, viz appears in different locations and sometimes its right where we are moving to + * which is okay for hammerdins or melee chars but not for soft chars like sorcs + */ Common.Diablo.vizLayout === 1 ? Pather.moveToEx(7691, 5292, { callback: cb }) : Pather.moveToEx(7695, 5316, { callback: cb }); @@ -356,9 +356,9 @@ }, /** - * @param {boolean} openSeal - * @returns {boolean} - */ + * @param {boolean} openSeal + * @returns {boolean} + */ seisSeal: function (openSeal = true) { print("Seis layout " + Common.Diablo.seisLayout); let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); @@ -402,9 +402,9 @@ }, /** - * @param {boolean} openSeal - * @returns {boolean} - */ + * @param {boolean} openSeal + * @returns {boolean} + */ infectorSeal: function (openSeal = true) { Precast.doPrecast(true); print("Inf layout " + Common.Diablo.infLayout); @@ -418,7 +418,7 @@ } distCheck.distance > 70 && this.followPath(Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - + let cb = () => { let inf = Game.getMonster(getLocaleString(sdk.locale.monsters.InfectorofSouls)); return inf && (inf.distance < Skill.getRange(Config.AttackSkill[1]) || inf.dead); @@ -497,7 +497,7 @@ } } } - + return false; }, diff --git a/d2bs/kolbot/libs/core/Common/Smith.js b/d2bs/kolbot/libs/core/Common/Smith.js index b0640c6fb..f709cebb8 100644 --- a/d2bs/kolbot/libs/core/Common/Smith.js +++ b/d2bs/kolbot/libs/core/Common/Smith.js @@ -21,7 +21,7 @@ Pickit.pickItem(malus); Town.goToTown(); Town.npcInteract("Charsi"); - + return !!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete); }, configurable: true, diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 0d87f2874..e1f623b16 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -281,8 +281,8 @@ let Config = { Recipes: [], MakeRunewords: false, /** - * @type {[runeword, string | number, ?boolean][]} - */ + * @type {[runeword, string | number, ?boolean][]} + */ Runewords: [], KeepRunewords: [], LadderOveride: false, diff --git a/d2bs/kolbot/libs/core/Experience.js b/d2bs/kolbot/libs/core/Experience.js index e4bfe9f32..4d6a73185 100644 --- a/d2bs/kolbot/libs/core/Experience.js +++ b/d2bs/kolbot/libs/core/Experience.js @@ -8,9 +8,9 @@ const Experience = { /** - * @todo combine this and nextExp into key-value pairs 1-99 - * Experience[me.charlvl].total and Experience[me.charlvl].next - */ + * @todo combine this and nextExp into key-value pairs 1-99 + * Experience[me.charlvl].total and Experience[me.charlvl].next + */ totalExp: [ 0, 0, 500, 1500, 3750, 7875, 14175, 22680, 32886, 44396, 57715, 72144, 90180, 112725, 140906, 176132, 220165, 275207, 344008, 430010, 537513, 671891, 839864, 1049830, 1312287, @@ -55,43 +55,43 @@ const Experience = { [7933, 51344, 77016], [8037, 52250, 78375], [8141, 53156, 79734], [8245, 54062, 81093], [8349, 54968, 82452], [8453, 55874, 83811], [160000, 160000, 160000] ], /** - * Percent progress into the current level. Format: xx.xx% - */ + * Percent progress into the current level. Format: xx.xx% + */ progress: function () { return me.getStat(sdk.stats.Level) === 99 ? 0 : (((me.getStat(sdk.stats.Experience) - this.totalExp[me.getStat(sdk.stats.Level)]) / this.nextExp[me.getStat(sdk.stats.Level)]) * 100).toFixed(2); }, /** - * Total experience gained in current run - */ + * Total experience gained in current run + */ gain: function () { return (me.getStat(sdk.stats.Experience) - DataFile.getStats().experience); }, /** - * Percent experience gained in current run - */ + * Percent experience gained in current run + */ gainPercent: function () { return me.getStat(sdk.stats.Level) === 99 ? 0 : (this.gain() * 100 / this.nextExp[me.getStat(sdk.stats.Level)]).toFixed(6); }, /** - * Runs until next level - */ + * Runs until next level + */ runsToLevel: function () { return Math.round(((100 - this.progress()) / 100) * this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); }, /** - * Total runs needed for next level (not counting current progress) - */ + * Total runs needed for next level (not counting current progress) + */ totalRunsToLevel: function () { return Math.round(this.nextExp[me.getStat(sdk.stats.Level)] / this.gain()); }, /** - * Total time till next level - */ + * Total time till next level + */ timeToLevel: function () { let tTLrawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000)).toString(); let tTLrawtimeToLevel = this.runsToLevel() * tTLrawSeconds; @@ -106,8 +106,8 @@ const Experience = { }, /** - * Get Game Time - */ + * Get Game Time + */ getGameTime: function () { let rawMinutes = Math.floor((getTickCount() - me.gamestarttime) / 60000).toString(); let rawSeconds = (Math.floor((getTickCount() - me.gamestarttime) / 1000) % 60).toString(); @@ -119,8 +119,8 @@ const Experience = { }, /** - * Log to manager - */ + * Log to manager + */ log: function () { let gain = this.gain(); let progress = this.progress(); diff --git a/d2bs/kolbot/libs/core/GameData/MonsterData.js b/d2bs/kolbot/libs/core/GameData/MonsterData.js index 73ef80dd8..4e80b0cd2 100644 --- a/d2bs/kolbot/libs/core/GameData/MonsterData.js +++ b/d2bs/kolbot/libs/core/GameData/MonsterData.js @@ -9,48 +9,48 @@ 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 - */ - + * @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, diff --git a/d2bs/kolbot/libs/core/GameData/QuestData.js b/d2bs/kolbot/libs/core/GameData/QuestData.js index 6bfbdbaf4..ccb709f36 100644 --- a/d2bs/kolbot/libs/core/GameData/QuestData.js +++ b/d2bs/kolbot/libs/core/GameData/QuestData.js @@ -7,8 +7,8 @@ (function (module) { /** - * @todo Fill out more, items for quests, npcs, etc - */ + * @todo Fill out more, items for quests, npcs, etc + */ const QuestData = (function () { let _lastRefresh = 0; @@ -27,12 +27,12 @@ _lastRefresh = getTickCount(); } }; - + /** - * @constructor - * @param {number} questId - * @param {number} act - */ + * @constructor + * @param {number} questId + * @param {number} act + */ function Quest (questId, act) { this.id = questId; this.act = act; @@ -72,10 +72,10 @@ }; /** - * @param {number} state - quest state (0 - 15) - * @param {boolean} complete - if true, will check if state bit is 1 (active) otherwise 0 (inactive) - * @returns {boolean} - */ + * @param {number} state - quest state (0 - 15) + * @param {boolean} complete - if true, will check if state bit is 1 (active) otherwise 0 (inactive) + * @returns {boolean} + */ Quest.prototype.checkState = function (state, complete = true) { // handle the ones we already know if (state === sdk.quest.states.Completed && this.completed) return complete; @@ -141,20 +141,20 @@ questMap.set(questId, new Quest(questId, act + 1)); } }); - + return { /** - * @param {number} questId - * @returns {Quest | undefined} - */ + * @param {number} questId + * @returns {Quest | undefined} + */ get: function (questId) { return questMap.get(questId); }, /** - * @param {number} questId - * @returns {boolean} - */ + * @param {number} questId + * @returns {boolean} + */ has: function (questId) { return questMap.has(questId); }, @@ -166,9 +166,9 @@ }, /** - * @param {number} questId - * @returns {number} - */ + * @param {number} questId + * @returns {number} + */ getActForQuest: function (questId) { return questMap.get(questId).act; }, diff --git a/d2bs/kolbot/libs/core/GameData/RuneData.js b/d2bs/kolbot/libs/core/GameData/RuneData.js index 824bc56a1..fdda86ca8 100644 --- a/d2bs/kolbot/libs/core/GameData/RuneData.js +++ b/d2bs/kolbot/libs/core/GameData/RuneData.js @@ -41,14 +41,14 @@ return [iType]; } } - + /** - * @constructor - * @param {string} name - The name of the recipe. - * @param {number} sockets - The number of sockets required for the recipe. - * @param {number[]} runes - Array of insertable IDs required for the recipe. - * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. - */ + * @constructor + * @param {string} name - The name of the recipe. + * @param {number} sockets - The number of sockets required for the recipe. + * @param {number[]} runes - Array of insertable IDs required for the recipe. + * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. + */ function RunewordObj (name, sockets, runes, itemTypes) { this.name = name; this.sockets = sockets; @@ -70,40 +70,40 @@ }; /** - * Finds a runeword by name. - * @param {string} name - The name of the runeword. - * @returns {Runeword} - The runeword object. - */ + * Finds a runeword by name. + * @param {string} name - The name of the runeword. + * @returns {Runeword} - The runeword object. + */ const findByName = function (name) { return runewords.find(r => String.isEqual(r.name, name)); }; /** - * Find all runewords that have the given rune. - * @param {number} rune - classid of rune - * @returns {Array} - */ + * Find all runewords that have the given rune. + * @param {number} rune - classid of rune + * @returns {Array} + */ const findByRune = function (rune) { return runewords.filter(r => r.runes.includes(rune)); }; /** - * Find all runewords that can be applied to the given item type. - * @param {number} type - item type - * @returns {Array} - */ + * Find all runewords that can be applied to the given item type. + * @param {number} type - item type + * @returns {Array} + */ const findByType = function (type) { return runewords.filter(r => r.itemTypes.includes(type)); }; /** - * Create a new non standard runeword. - * @param {string} name - The name of the recipe. - * @param {number} sockets - The number of sockets required for the recipe. - * @param {number[]} runes - Array of insertable IDs required for the recipe. - * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. - * @returns {runeword} - The new runeword object. - */ + * Create a new non standard runeword. + * @param {string} name - The name of the recipe. + * @param {number} sockets - The number of sockets required for the recipe. + * @param {number[]} runes - Array of insertable IDs required for the recipe. + * @param {number[]} itemTypes - Array of item type IDs the recipe can be applied to. + * @returns {runeword} - The new runeword object. + */ const addRuneword = function (name, sockets, runes, itemTypes) { if (!name || !sockets || !runes || !itemTypes) return false; !Array.isArray(runes) && (runes = [runes]); diff --git a/d2bs/kolbot/libs/core/GameData/ShrineData.js b/d2bs/kolbot/libs/core/GameData/ShrineData.js index 85397152a..efbe2d9a1 100644 --- a/d2bs/kolbot/libs/core/GameData/ShrineData.js +++ b/d2bs/kolbot/libs/core/GameData/ShrineData.js @@ -35,7 +35,7 @@ shrineMap.set(sdk.shrines.Monster, new Shrine()); shrineMap.set(sdk.shrines.Exploding, new Shrine()); shrineMap.set(sdk.shrines.Poison, new Shrine()); - + return { get: function (shrineType) { return shrineMap.get(shrineType); diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index fb60cd1f2..7b197a07f 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -8,14 +8,14 @@ (function (module) { /** - * @typedef {Object} SkillInterface - * @property {number} hand - * @property {boolean} [missile] - * @property {number | () => number} range - * @property {number} [state] - * @property {() => boolean} [condition] - * @property {() => number} [summonCount] - */ + * @typedef {Object} SkillInterface + * @property {number} hand + * @property {boolean} [missile] + * @property {number | () => number} range + * @property {number} [state] + * @property {() => boolean} [condition] + * @property {() => number} [summonCount] + */ /** @type {Map} */ const skillMap = new Map(); @@ -226,7 +226,7 @@ range: 1, duration: () => ( ((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) - + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) ), }); @@ -276,7 +276,7 @@ range: 1, duration: () => ( ((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) - + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) ), }); @@ -329,7 +329,7 @@ range: 1, duration: () => ( ((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) - + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.HardPoints)) * 10)) ), }); @@ -794,7 +794,7 @@ state: sdk.states.Shout, duration: () => ( ((10 + me.getSkill(sdk.skills.Shout, sdk.skills.subindex.SoftPoints) * 10) - + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + + ((me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) ), }); @@ -863,7 +863,7 @@ state: sdk.states.BattleOrders, duration: () => ( ((20 + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) * 10) - + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.HardPoints)) * 5)) ), }); @@ -902,7 +902,7 @@ state: sdk.states.BattleCommand, duration: () => ( ((10 * me.getSkill(sdk.skills.BattleCommand, sdk.skills.subindex.SoftPoints) - 5) - + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + + ((me.getSkill(sdk.skills.Shout, sdk.skills.subindex.HardPoints) + me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.HardPoints)) * 5)) ), }); @@ -991,7 +991,7 @@ let range = Math.floor(((33 + (2 * skLvl)) / 4) * (2 / 3)); // Druid using this on physical immunes needs the monsters to be within range of hurricane range > 6 && Config.AttackSkill[5] === sdk.skills.ArcticBlast && (range = 6); - + return range; }, }); @@ -1283,9 +1283,9 @@ ]; /** - * @constructor - * @param {number} skillId - */ + * @constructor + * @param {number} skillId + */ function Skill (skillId) { let _skillData = skillMap.get(skillId); /** @type {number} */ @@ -1325,85 +1325,85 @@ this.damageType = damageTypes[getBaseStat("skills", skillId, "EType")]; /** - * @private - * @type {number | () => number} - */ + * @private + * @type {number | () => number} + */ this._range = (_skillData.range || 1); /** - * @private - * @type {() => number} - */ + * @private + * @type {() => number} + */ this._AoE = (_skillData.AoE || (() => 0)); /** - * @private - * @type {() => number} - */ + * @private + * @type {() => number} + */ this._duration = (_skillData.duration || (() => 0)); /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._manaCost = Infinity; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._mana = getBaseStat("skills", this.skillId, "mana"); /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._minMana = getBaseStat("skills", this.skillId, "minmana"); /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._lvlMana = getBaseStat("skills", this.skillId, "lvlmana"); let effectiveShift = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._manaShift = (effectiveShift[getBaseStat("skills", this.skillId, "manashift")] / 256); /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._bestSlot = 0; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._dmg = 0; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._hardPoints = 0; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this._softPoints = 0; /** - * @private - * @type {boolean} - */ + * @private + * @type {boolean} + */ this._checked = false; } /** - * @this Skill - * @returns {number} - */ + * @this Skill + * @returns {number} + */ Skill.prototype.duration = function () { return Time.seconds(this._duration()); }; /** - * @this Skill - * @returns {number} - */ + * @this Skill + * @returns {number} + */ Skill.prototype.manaCost = function () { if (this._manaCost !== Infinity) return this._manaCost; if (this.skillId < sdk.skills.MagicArrow) { @@ -1426,26 +1426,26 @@ }; /** - * @this Skill - * @property {boolean} pvpRange - * @returns {number} - */ + * @this Skill + * @property {boolean} pvpRange + * @returns {number} + */ Skill.prototype.range = function (pvpRange = false) { return typeof this._range === "function" ? this._range(pvpRange) : this._range; }; /** - * @this Skill - * @returns {number} - */ + * @this Skill + * @returns {number} + */ Skill.prototype.AoE = function () { return this._AoE(); }; /** - * @this Skill - * @returns {boolean} - */ + * @this Skill + * @returns {boolean} + */ Skill.prototype.have = function () { if (!this.condition()) return false; if (this._hardPoints > 0) return true; @@ -1466,8 +1466,8 @@ }; /** - * @todo Damage calculations, best slot, etc. - */ + * @todo Damage calculations, best slot, etc. + */ /** @type {Map { switch (unit.quality) { case sdk.items.quality.Set: @@ -396,7 +396,7 @@ const Item = { case sdk.items.quality.Unique: for (let i = 0; i < 401; i += 1) { if (unit.code === getBaseStat("uniqueitems", i, 4).trim() - && unit.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat("uniqueitems", i, 2)))) { + && unit.fname.split("\n").reverse()[0].includes(getLocaleString(getBaseStat("uniqueitems", i, 2)))) { return getBaseStat("uniqueitems", i, "invfile"); } } diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 316cfee67..ff8dc47f6 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -163,10 +163,10 @@ const Loader = { let duration = Time.elapsed(tick); console.log( "ÿc7" + script + " :: ÿc0Complete\n" - + "ÿc2 Statistics:\n" - + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" - + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" - + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) + + "ÿc2 Statistics:\n" + + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" + + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) ); } } @@ -180,7 +180,7 @@ const Loader = { // remove script function from global scope, so it can be cleared by GC delete global[script]; } - + if (reconfiguration) { print("ÿc2Reverting back unmodified config properties."); this.copy(unmodifiedConfig, Config); @@ -196,14 +196,14 @@ const Loader = { let reconfiguration, unmodifiedConfig = {}; let failed = false; 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; } @@ -251,10 +251,10 @@ const Loader = { let duration = Time.elapsed(tick); console.log( mainScriptStr + "ÿc7" + script + " :: ÿc0Complete\n" - + "ÿc2 Statistics:\n" - + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" - + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" - + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) + + "ÿc2 Statistics:\n" + + "ÿc7 - Duration: ÿc0" + (Time.format(duration)) + "\n" + + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) ); } } @@ -273,7 +273,7 @@ const Loader = { } this.tempList.pop(); - + if (reconfiguration) { print("ÿc2Reverting back unmodified config properties."); this.copy(unmodifiedConfig, Config); diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index c4f103db8..04748c232 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -178,7 +178,7 @@ me.needPotions = function () { 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) { const pots = { @@ -197,7 +197,7 @@ me.needPotions = function () { // quick check if ((Config.BeltColumn.includes("hp") && !pots.hp.length) - || (Config.BeltColumn.includes("mp") && !pots.mp.length)) { + || (Config.BeltColumn.includes("mp") && !pots.mp.length)) { return true; } @@ -611,9 +611,9 @@ Object.defineProperties(me, { const QuestData = require("./GameData/QuestData"); /** - * @param {number} act - * @returns {boolean} - */ + * @param {number} act + * @returns {boolean} + */ me.accessToAct = function (act) { if (act === 1) return true; return me.highestAct >= act; diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index ed600a559..766c3186c 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -8,13 +8,13 @@ const Misc = { /** - * Click something - * @param {number} button - * @param {number} shift - * @param {number | Unit} [x] - * @param {number} [y] - * @returns {boolean} - */ + * Click something + * @param {number} button + * @param {number} shift + * @param {number | Unit} [x] + * @param {number} [y] + * @returns {boolean} + */ click: function (button, shift, x, y) { if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); @@ -55,10 +55,10 @@ const Misc = { }, /** - * Check if a player is in your party - * @param {string} name - * @returns {boolean} - */ + * Check if a player is in your party + * @param {string} name + * @returns {boolean} + */ inMyParty: function (name) { if (me.name === name) return true; @@ -171,7 +171,7 @@ const Misc = { if (party) { let myPartyId = party.partyid; - + do { if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name) { print(party.name); @@ -213,11 +213,11 @@ const Misc = { }, /** - * autoleader by Ethic - refactored by theBGuy - * Autodetect leader for leech scripts by looking to see who first enters a certain area - * @param {{ destination: number | number[], quitIf?: Function, timeout?: number }} givenSettings - * @returns - */ + * autoleader by Ethic - refactored by theBGuy + * Autodetect leader for leech scripts by looking to see who first enters a certain area + * @param {{ destination: number | number[], quitIf?: Function, timeout?: number }} givenSettings + * @returns + */ autoLeaderDetect: function (givenSettings = {}) { const settings = Object.assign({}, { destination: -1, @@ -262,13 +262,13 @@ const Misc = { }, /** - * @description Open a chest Unit (takes chestID or unit) - * @param {Unit | number} unit - * @returns {boolean} If we opened the chest - */ + * @description Open a chest Unit (takes chestID or unit) + * @param {Unit | number} unit + * @returns {boolean} If we opened the chest + */ 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 @@ -309,7 +309,7 @@ const Misc = { openChestsInArea: function (area, chestIds = []) { !area && (area = me.area); area !== me.area && Pather.journeyTo(area); - + let presetUnits = Game.getPresetObjects(area); if (!presetUnits) return false; @@ -520,15 +520,15 @@ const Misc = { }, /** - * 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 - * - Sometimes it seems like calling getPresetObjects to quickly after taking an exit causes a crash, only anecdotal evidence though. Test delays - * - Add the rest of the preset shrine id's to look for - */ + * 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 + * - Sometimes it seems like calling getPresetObjects to quickly after taking an exit causes a crash, only anecdotal evidence though. Test delays + * - Add the rest of the preset shrine id's to look for + */ getShrinesInArea: function (area, type, use) { let shrineLocs = []; let shrineIds = [2, 81, 83]; @@ -639,10 +639,10 @@ const Misc = { screenshotErrors: true, /** - * Report script errors to logs/ScriptErrorLog.txt - * @param {Error | string} error - * @param {string} [script] - */ + * Report script errors to logs/ScriptErrorLog.txt + * @param {Error | string} error + * @param {string} [script] + */ errorReport: function (error, script) { let msg, oogmsg, filemsg, source, stack; let stackLog = ""; @@ -702,19 +702,19 @@ const Misc = { }, /** - * @param {string} msg - * @returns {void} - */ + * @param {string} msg + * @returns {void} + */ debugLog: function (msg) { if (!Config.Debug) return; debugLog(me.profile + ": " + msg); }, /** - * Use a NPC menu. Experimental function, subject to change - * @param {number} id - string number (with exception of Ressurect merc). - * @returns {boolean} - */ + * Use a NPC menu. Experimental function, subject to change + * @param {number} id - string number (with exception of Ressurect merc). + * @returns {boolean} + */ useMenu: function (id) { //print("useMenu " + getLocaleString(id)); @@ -751,12 +751,12 @@ const Misc = { }, /** - * @template T - * @param {function(): T} check - * @param {number} [timeout=6000] - * @param {number} [sleep=40] - * @returns {T | false} - */ + * @template T + * @param {function(): T} check + * @param {number} [timeout=6000] + * @param {number} [sleep=40] + * @returns {T | false} + */ poll: function (check, timeout = 6000, sleep = 40) { let ret, start = getTickCount(); @@ -772,9 +772,9 @@ const Misc = { }, /** - * @param {number[]} excluded - * @returns {number[] | null} array of UI flags that are set, or null if none are set - */ + * @param {number[]} excluded + * @returns {number[] | null} array of UI flags that are set, or null if none are set + */ getUIFlags: function (excluded = []) { if (!me.gameReady) return null; @@ -797,10 +797,10 @@ const Misc = { }, /** - * @param {number} id - * @param {number} state - * @returns {0 | 1} - */ + * @param {number} id + * @param {number} state + * @returns {0 | 1} + */ checkQuest: function (id, state) { Packet.questRefresh(); delay(500); @@ -808,9 +808,9 @@ const Misc = { }, /** - * @param {number} questID - * @returns {number[]} List of set quest states - */ + * @param {number} questID + * @returns {number[]} List of set quest states + */ getQuestStates: function (questID) { if (!me.gameReady) return []; Packet.questRefresh(); diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index b4c4a95d1..069064116 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -78,7 +78,7 @@ NTIP.OpenFile = function (filepath, notify) { if (!lines[i].toLowerCase().match("tier")) { NTIP_CheckListNoTier.push(line); } - + stringArray.push(info); } } diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 49cc00dc9..307e69e47 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -7,10 +7,10 @@ const Packet = { /** - * Interact and open the menu of an NPC - * @param {NPCUnit} unit - * @returns {boolean} - */ + * Interact and open the menu of an NPC + * @param {NPCUnit} unit + * @returns {boolean} + */ openMenu: function (unit) { if (unit.type !== sdk.unittype.NPC) throw new Error("openMenu: Must be used on NPCs."); if (getUIFlag(sdk.uiflags.NPCMenu)) return true; @@ -48,11 +48,11 @@ const Packet = { }, /** - * Start a trade action with an NPC - * @param {NPCUnit} unit - * @param {number} mode - * @returns {boolean} - */ + * Start a trade action with an NPC + * @param {NPCUnit} unit + * @param {number} mode + * @returns {boolean} + */ startTrade: function (unit, mode) { if (unit.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); if (getUIFlag(sdk.uiflags.Shop)) return true; @@ -78,12 +78,12 @@ const Packet = { }, /** - * Buy an item from an interacted NPC - * @param {NPCUnit} unit - * @param {boolean} shiftBuy - * @param {boolean} gamble - * @returns {boolean} - */ + * Buy an item from an interacted NPC + * @param {NPCUnit} unit + * @param {boolean} shiftBuy + * @param {boolean} gamble + * @returns {boolean} + */ buyItem: function (unit, shiftBuy, gamble) { let oldGold = me.gold; let itemCount = me.itemcount; @@ -122,13 +122,13 @@ const Packet = { }, /** - * Buy scrolls from an interacted NPC, we need this as a seperate check because itemcount doesn't change - * if the scroll goes into the tome automatically. - * @param {NPCUnit} unit - * @param {ItemUnit} [tome] - * @param {boolean} [shiftBuy] - * @returns {boolean} - */ + * Buy scrolls from an interacted NPC, we need this as a seperate check because itemcount doesn't change + * if the scroll goes into the tome automatically. + * @param {NPCUnit} unit + * @param {ItemUnit} [tome] + * @param {boolean} [shiftBuy] + * @returns {boolean} + */ buyScroll: function (unit, tome, shiftBuy) { let oldGold = me.gold; let itemCount = me.itemcount; @@ -165,10 +165,10 @@ const Packet = { }, /** - * Sell a item to a NPC - * @param {ItemUnit} unit - * @returns {boolean} - */ + * Sell a item to a NPC + * @param {ItemUnit} unit + * @returns {boolean} + */ sellItem: function (unit) { // Check if it's an item we want to buy if (unit.type !== sdk.unittype.Item) throw new Error("Unit.sell: Must be used on items."); @@ -197,10 +197,10 @@ const Packet = { }, /** - * @param {ItemUnit} unit - * @param {ItemUnit} tome - * @returns {boolean} - */ + * @param {ItemUnit} unit + * @param {ItemUnit} tome + * @returns {boolean} + */ identifyItem: function (unit, tome) { if (!unit || unit.identified) return false; @@ -244,9 +244,9 @@ const Packet = { }, /** - * @param {ItemUnit} item - * @returns {boolean} - */ + * @param {ItemUnit} item + * @returns {boolean} + */ itemToCursor: function (item) { // Something already on cursor if (me.itemoncursor) { @@ -278,9 +278,9 @@ const Packet = { }, /** - * @param {ItemUnit} item - * @returns {boolean} - */ + * @param {ItemUnit} item + * @returns {boolean} + */ dropItem: function (item) { if (!this.itemToCursor(item)) return false; @@ -299,9 +299,9 @@ const Packet = { }, /** - * @param {ItemUnit} item - * @returns {boolean} - */ + * @param {ItemUnit} item + * @returns {boolean} + */ givePotToMerc: function (item) { if (!item) return false; if (![ @@ -320,20 +320,20 @@ const Packet = { }, /** - * @param {ItemUnit} item - * @param {number} xLoc - * @returns {boolean} - */ + * @param {ItemUnit} item + * @param {number} xLoc + * @returns {boolean} + */ placeInBelt: function (item, xLoc) { item.toCursor(true) && new PacketBuilder().byte(sdk.packets.send.ItemToBelt).dword(item.gid).dword(xLoc).send(); return Misc.poll(() => item.isInBelt, 500, 100); }, /** - * @param {ItemUnit} who - * @param {boolean} toCursor - * @returns {boolean} - */ + * @param {ItemUnit} who + * @param {boolean} toCursor + * @returns {boolean} + */ click: function (who, toCursor = false) { if (!who || !copyUnit(who).x) return false; new PacketBuilder() @@ -346,9 +346,9 @@ const Packet = { }, /** - * @param {Unit} who - * @returns {boolean} - */ + * @param {Unit} who + * @returns {boolean} + */ entityInteract: function (who) { if (!who || !copyUnit(who).x) return false; new PacketBuilder().byte(sdk.packets.send.InteractWithEntity).dword(who.type).dword(who.gid).send(); @@ -356,9 +356,9 @@ const Packet = { }, /** - * @param {NPCUnit} who - * @returns {boolean} - */ + * @param {NPCUnit} who + * @returns {boolean} + */ cancelNPC: function (who) { if (!who || !copyUnit(who).x) return false; new PacketBuilder().byte(sdk.packets.send.NPCCancel).dword(who.type).dword(who.gid).send(); @@ -366,9 +366,9 @@ const Packet = { }, /** - * @param {ItemUnit} pot - * @returns {boolean} - */ + * @param {ItemUnit} pot + * @returns {boolean} + */ useBeltItemForMerc: function (pot) { if (!pot) return false; sendPacket(1, sdk.packets.send.UseBeltItem, 4, pot.gid, 4, 1, 4, 0); @@ -389,7 +389,7 @@ const Packet = { hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocationEx : sdk.packets.send.LeftSkillOnLocationEx; - + let endT = getTickCount() + duration; // has to be cast normally first with a click before held packet is sent sendPacket(1, nHand, 2, wX, 2, wY); @@ -400,10 +400,10 @@ const Packet = { }, /** - * @param {number} hand - * @param {Monster | ItemUnit | ObjectUnit} who - * @returns {boolean} - */ + * @param {number} hand + * @param {Monster | ItemUnit | ObjectUnit} who + * @returns {boolean} + */ unitCast: function (hand, who) { hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnEntityEx3 @@ -412,9 +412,9 @@ const Packet = { }, /** - * @param {Monster | ItemUnit | ObjectUnit} who - * @returns {boolean} - */ + * @param {Monster | ItemUnit | ObjectUnit} who + * @returns {boolean} + */ telekinesis: function (who) { if (!who || !Skill.setSkill(sdk.skills.Telekinesis, sdk.skills.hand.Right)) return false; sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); @@ -422,9 +422,9 @@ const Packet = { }, /** - * @param {Player | Monster | MercUnit} who - * @returns {boolean} - */ + * @param {Player | Monster | MercUnit} who + * @returns {boolean} + */ enchant: function (who) { if (!who || !Skill.setSkill(sdk.skills.Enchant, sdk.skills.hand.Right)) return false; sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); @@ -432,10 +432,10 @@ const Packet = { }, /** - * @param {number} wX - * @param {number} wY - * @returns {boolean} - */ + * @param {number} wX + * @param {number} wY + * @returns {boolean} + */ teleport: function (wX, wY) { if (![wX, wY].every(n => typeof n === "number")) return false; if (!Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right)) return false; @@ -448,12 +448,12 @@ const Packet = { // }, /** - * @deprecated - * @param {number} x - * @param {number} y - * @param {number} maxDist - * @returns {boolean} - */ + * @deprecated + * @param {number} x + * @param {number} y + * @param {number} maxDist + * @returns {boolean} + */ teleWalk: function (x, y, maxDist = 5) { !Packet.telewalkTick && (Packet.telewalkTick = 0); @@ -482,10 +482,10 @@ const Packet = { }, /** - * Request entity update - * @param {number} gid - * @param {number} wait - */ + * Request entity update + * @param {number} gid + * @param {number} wait + */ flash: function (gid, wait = 0) { wait === 0 && (wait = 300 + (me.gameReady ? 2 * me.ping : 300)); sendPacket(1, sdk.packets.send.RequestEntityUpdate, 4, 0, 4, gid); @@ -496,10 +496,10 @@ const Packet = { }, /** - * @deprecated - * @param {number} stat - * @param {number} value - */ + * @deprecated + * @param {number} stat + * @param {number} value + */ changeStat: function (stat, value) { if (value > 0) { getPacket(1, 0x1d, 1, stat, 1, value); diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 0285ada77..4749c8010 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -11,15 +11,15 @@ */ const NodeAction = { /** - * @type {number[]} - */ + * @type {number[]} + */ shrinesToIgnore: [], enabled: true, /** - * Run all the functions within NodeAction (except for itself) - * @param {clearSettings} arg - */ + * Run all the functions within NodeAction (except for itself) + * @param {clearSettings} arg + */ go: function (arg) { if (!this.enabled) return; for (let i in this) { @@ -30,10 +30,10 @@ const NodeAction = { }, /** - * Kill monsters while pathing - * @param {clearSettings} arg - * @returns {void} - */ + * Kill monsters while pathing + * @param {clearSettings} arg + * @returns {void} + */ killMonsters: function (arg = {}) { if (arg.hasOwnProperty("allowClearing") && !arg.allowClearing) return; @@ -77,8 +77,8 @@ const NodeAction = { }, /** - * Open chests while pathing - */ + * Open chests while pathing + */ popChests: function () { // fastPick check? should only open chests if surrounding monsters have been cleared or if fastPick is active // note: clear of surrounding monsters of the spectype we are set to clear @@ -86,8 +86,8 @@ const NodeAction = { }, /** - * Scan shrines while pathing - */ + * Scan shrines while pathing + */ getShrines: function () { Config.ScanShrines.length > 0 && Misc.scanShrines(null, this.shrinesToIgnore); } @@ -95,16 +95,16 @@ const NodeAction = { const PathDebug = { /** - * @type {Line[]} - */ + * @type {Line[]} + */ hooks: [], enableHooks: false, /** - * Draw our path on the screen - * @param {PathNode[]} path - * @returns {void} - */ + * Draw our path on the screen + * @param {PathNode[]} path + * @returns {void} + */ drawPath: function (path) { if (!this.enableHooks) return; @@ -126,12 +126,12 @@ const PathDebug = { }, /** - * Check if a set of coords are a set path - * @param {PathNode[]} path - * @param {number} x - * @param {number} y - * @returns {boolean} - */ + * Check if a set of coords are a set path + * @param {PathNode[]} path + * @param {number} x + * @param {number} y + * @returns {boolean} + */ coordsInPath: function (path, x, y) { for (let i = 0; i < path.length; i += 1) { if (getDistance(x, y, path[i].x, path[i].y) < 5) { @@ -199,8 +199,8 @@ const Pather = { }, /** - * @todo Handle rare bug where teleport skill dissapears from enigma - */ + * @todo Handle rare bug where teleport skill dissapears from enigma + */ canTeleport: function () { return this.teleport && (Skill.canUse(sdk.skills.Teleport) || me.getStat(sdk.stats.OSkill, sdk.skills.Teleport)); }, @@ -212,17 +212,17 @@ const Pather = { }, /** - * @typedef {object} spotOnDistanceSettings - * @property {number} [area] - * @property {number} [reductionType] - * @property {number} [coll] - * @property {boolean} [returnSpotOnError] - * - * @param {PathNode} spot - * @param {number} distance - * @param {spotOnDistanceSettings} givenSettings - * @returns {PathNode} - */ + * @typedef {object} spotOnDistanceSettings + * @property {number} [area] + * @property {number} [reductionType] + * @property {number} [coll] + * @property {boolean} [returnSpotOnError] + * + * @param {PathNode} spot + * @param {number} distance + * @param {spotOnDistanceSettings} givenSettings + * @returns {PathNode} + */ spotOnDistance: function (spot, distance, givenSettings = {}) { const spotSettings = Object.assign({}, { area: me.area, @@ -232,7 +232,7 @@ const Pather = { }, givenSettings); let nodes = (getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, spotSettings.reductionType, 4) || []); - + if (!nodes.length) { if (spotSettings.reductionType === 2) { // try again with walking reduction @@ -243,39 +243,39 @@ const Pather = { return (nodes.find((node) => getDistance(spot.x, spot.y, node.x, node.y) < distance && Pather.checkSpot(node.x, node.y, spotSettings.coll)) - || (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y })); + || (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y })); }, /** - * @typedef {object} pathSettings - * @property {boolean} [allowTeleport] - * @property {boolean} [allowClearing] - * @property {boolean} [allowTown] - * @property {boolean} [allowPicking] - * @property {number} [minDist] - * @property {number} [retry] - * @property {boolean} [pop] - * @property {boolean} [returnSpotOnError] - * @property {Function} [callback] - * @property {clearSettings} [clearSettings] - * - * @typedef {object} clearSettings - * @property {boolean} [clearSettings.clearPath] - * @property {number} [clearSettings.range] - * @property {number} [clearSettings.specType] - * @property {Function} [clearSettings.sort] - * - * @param {PathNode | Unit | PresetUnit} target - * @param {pathSettings} givenSettings - * @returns {boolean} - */ + * @typedef {object} pathSettings + * @property {boolean} [allowTeleport] + * @property {boolean} [allowClearing] + * @property {boolean} [allowTown] + * @property {boolean} [allowPicking] + * @property {number} [minDist] + * @property {number} [retry] + * @property {boolean} [pop] + * @property {boolean} [returnSpotOnError] + * @property {Function} [callback] + * @property {clearSettings} [clearSettings] + * + * @typedef {object} clearSettings + * @property {boolean} [clearSettings.clearPath] + * @property {number} [clearSettings.range] + * @property {number} [clearSettings.specType] + * @property {Function} [clearSettings.sort] + * + * @param {PathNode | Unit | PresetUnit} target + * @param {pathSettings} givenSettings + * @returns {boolean} + */ move: function (target, givenSettings = {}) { // Abort if dead if (me.dead) return false; /** - * assign settings - * @type {pathSettings} - */ + * assign settings + * @type {pathSettings} + */ const settings = Object.assign({}, { clearSettings: { }, @@ -408,8 +408,8 @@ const Pather = { } else { if (!me.inTown) { /** - * @todo I think some of this needs to be re-worked, I've noticed recursive Attacking/Picking - */ + * @todo I think some of this needs to be re-worked, I've noticed recursive Attacking/Picking + */ if (!useTeleport && settings.allowClearing) { let tempRange = (annoyingArea ? 5 : 10); // allowed to clear so lets see if any mobs are around us @@ -454,11 +454,11 @@ const Pather = { } /** - * 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) @@ -510,47 +510,47 @@ const Pather = { }, /** - * - * @param {number} x - * @param {number} y - * @param {number} minDist - * @param {pathSettings} givenSettings - * @returns {boolean} - */ + * + * @param {number} x + * @param {number} y + * @param {number} minDist + * @param {pathSettings} givenSettings + * @returns {boolean} + */ moveNear: function (x, y, minDist, givenSettings = {}) { return Pather.move({ x: x, y: y }, Object.assign({ minDist: minDist }, givenSettings)); }, /** - * @param {number} x - the x coord to move to - * @param {number} y - the y coord to move to - * @param {number} retry - number of attempts before aborting - * @param {boolean} clearPath - kill monsters while moving - * @param {boolean} pop - remove last node - * @returns {boolean} - */ + * @param {number} x - the x coord to move to + * @param {number} y - the y coord to move to + * @param {number} retry - number of attempts before aborting + * @param {boolean} clearPath - kill monsters while moving + * @param {boolean} pop - remove last node + * @returns {boolean} + */ moveTo: function (x, y, retry, clearPath, pop) { return Pather.move({ x: x, y: y }, { retry: retry, pop: pop, allowClearing: clearPath }); }, /** - * - * @param {number} x - * @param {number} y - * @param {pathSettings} givenSettings - * @returns - */ + * + * @param {number} x + * @param {number} y + * @param {pathSettings} givenSettings + * @returns + */ moveToEx: function (x, y, givenSettings = {}) { return Pather.move({ x: x, y: y }, givenSettings); }, /** - * @param {number} x - the x coord to teleport to - * @param {number} y - the y coord to teleport to - * @param {number} [maxRange] - max acceptable distance from node - * @returns {boolean} - * @todo does this need a validLocation check? - maybe if we fail once check the spot - */ + * @param {number} x - the x coord to teleport to + * @param {number} y - the y coord to teleport to + * @param {number} [maxRange] - max acceptable distance from node + * @returns {boolean} + * @todo does this need a validLocation check? - maybe if we fail once check the spot + */ teleportTo: function (x, y, maxRange = 5) { for (let i = 0; i < 3; i += 1) { Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); @@ -570,11 +570,11 @@ const Pather = { }, /** - * @param {number} x - the x coord to teleport to - * @param {number} y - the y coord to teleport to - * @param {number} [minDist] - minimal distance from x/y before returning true - * @returns {boolean} - sucessfully moved within minDist - */ + * @param {number} x - the x coord to teleport to + * @param {number} y - the y coord to teleport to + * @param {number} [minDist] - minimal distance from x/y before returning true + * @returns {boolean} - sucessfully moved within minDist + */ walkTo: function (x, y, minDist) { while (!me.gameReady) { delay(100); @@ -587,8 +587,8 @@ const Pather = { let [nFail, attemptCount] = [0, 0]; /** - * @todo add cleansing/meditation here as well - */ + * @todo add cleansing/meditation here as well + */ // credit @Jaenster // Stamina handler and Charge if (!me.inTown) { @@ -703,11 +703,11 @@ const Pather = { }, /** - * If there is a door in our path, open it so we can continue moving - * @param {number} x - the x coord of the node close to the door - * @param {number} y - the y coord of the node close to the door - * @returns {boolean} true if we opened any doors that were in our way - */ + * If there is a door in our path, open it so we can continue moving + * @param {number} x - the x coord of the node close to the door + * @param {number} y - the y coord of the node close to the door + * @returns {boolean} true if we opened any doors that were in our way + */ openDoors: function (x, y) { if (me.inTown && me.act !== 5) return false; @@ -766,7 +766,7 @@ const Pather = { } let monstawall = Game.getMonster("barricade"); - + if (monstawall) { do { if (monstawall.hp > 0 && (getDistance(monstawall, x, y) < 4 @@ -782,11 +782,11 @@ const Pather = { }, /** - * Small and annoying things like barrels can block our path, open them if they are near us - * @param {number} x - the x coord of the node close to the barrel - * @param {number} y - the y coord of the node close to the barrel - * @returns {boolean} true if we kicked any barrels that were in our way - */ + * Small and annoying things like barrels can block our path, open them if they are near us + * @param {number} x - the x coord of the node close to the barrel + * @param {number} y - the y coord of the node close to the barrel + * @returns {boolean} true if we kicked any barrels that were in our way + */ kickBarrels: function (x, y) { if (me.inTown) return false; @@ -801,8 +801,8 @@ const Pather = { let barrels = getUnits(sdk.unittype.Object) .filter(function (el) { return (el.name && el.mode === sdk.objects.mode.Inactive - && _things.includes(el.name.toLowerCase()) - && ((getDistance(el, x, y) < 4 && el.distance < 9) || el.distance < 4)); + && _things.includes(el.name.toLowerCase()) + && ((getDistance(el, x, y) < 4 && el.distance < 9) || el.distance < 4)); }); let brokeABarrel = false; @@ -830,14 +830,14 @@ const Pather = { }, /** - * Move to unit - * @param {Unit} unit - unit to move to - * @param {number} [offX] - offset from unit's x coord - * @param {number} [offY] - offset from unit's x coord - * @param {boolean} [clearPath] - kill monsters while moving - * @param {boolean} [pop] - remove last node - * @returns {boolean} Sucessfully moved to unit - */ + * Move to unit + * @param {Unit} unit - unit to move to + * @param {number} [offX] - offset from unit's x coord + * @param {number} [offY] - offset from unit's x coord + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved to unit + */ moveToUnit: function (unit, offX, offY, clearPath, pop) { const useTeleport = this.useTeleport(); @@ -860,12 +860,12 @@ const Pather = { }, /** - * Move near unit - * @param {Unit} unit - unit to move near - * @param {boolean} [clearPath] - kill monsters while moving - * @param {boolean} [pop] - remove last node - * @returns {boolean} Sucessfully moved near unit - */ + * Move near unit + * @param {Unit} unit - unit to move near + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved near unit + */ moveNearUnit: function (unit, minDist, clearPath, pop = false) { const useTeleport = this.useTeleport(); minDist === undefined && (minDist = me.inTown ? 2 : 5); @@ -883,15 +883,15 @@ const Pather = { }, /** - * Move near preset unit - * @param {number} area - area of the preset unit - * @param {number} unitType - type of the preset unit - * @param {number} unitId - preset unit id - * @param {number} [minDist] - minimum distance from unit - * @param {boolean} [clearPath] - kill monsters while moving - * @param {boolean} [pop] - remove last node - * @returns {boolean} Sucessfully moved near unit - */ + * Move near preset unit + * @param {number} area - area of the preset unit + * @param {number} unitType - type of the preset unit + * @param {number} unitId - preset unit id + * @param {number} [minDist] - minimum distance from unit + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved near unit + */ moveNearPreset: function (area, unitType, unitId, minDist, clearPath = false, pop = false) { if (area === undefined || unitType === undefined || unitId === undefined) { throw new Error("moveNearPreset: Invalid parameters."); @@ -916,16 +916,16 @@ const Pather = { }, /** - * Move to preset unit - * @param {number} area - area of the preset unit - * @param {number} unitType - type of the preset unit - * @param {number} unitId - preset unit id - * @param {number} [offX] - offset from unit's x coord - * @param {number} [offY] - offset from unit's x coord - * @param {boolean} [clearPath] - kill monsters while moving - * @param {boolean} [pop] - remove last node - * @returns {boolean} Sucessfully moved to unit - */ + * Move to preset unit + * @param {number} area - area of the preset unit + * @param {number} unitType - type of the preset unit + * @param {number} unitId - preset unit id + * @param {number} [offX] - offset from unit's x coord + * @param {number} [offY] - offset from unit's x coord + * @param {boolean} [clearPath] - kill monsters while moving + * @param {boolean} [pop] - remove last node + * @returns {boolean} Sucessfully moved to unit + */ moveToPreset: function (area, unitType, unitId, offX, offY, clearPath, pop) { if (area === undefined || unitType === undefined || unitId === undefined) { throw new Error("moveToPreset: Invalid parameters."); @@ -954,16 +954,16 @@ const Pather = { }, /** - * @todo - * moveTo/NearPresetTile - */ - + * @todo + * moveTo/NearPresetTile + */ + /** - * - * @param {number} area - * @param {number} unitId - * @param {pathSettings} givenSettings - */ + * + * @param {number} area + * @param {number} unitId + * @param {pathSettings} givenSettings + */ moveToPresetObject: function (area, unitId, givenSettings = {}) { if (area === undefined || unitId === undefined) { throw new Error("moveToPreset: Invalid parameters."); @@ -990,11 +990,11 @@ const Pather = { }, /** - * - * @param {number} area - * @param {number} unitId - * @param {pathSettings} givenSettings - */ + * + * @param {number} area + * @param {number} unitId + * @param {pathSettings} givenSettings + */ moveToPresetMonster: function (area, unitId, givenSettings = {}) { if (area === undefined || unitId === undefined) { throw new Error("moveToPreset: Invalid parameters."); @@ -1021,10 +1021,10 @@ const Pather = { }, /** - * @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 - */ + * @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 + */ moveToExit: function (targetArea, use, givenSettings = {}) { if (targetArea === undefined) return false; @@ -1110,10 +1110,10 @@ const Pather = { }, /** - * @param {number} area - * @param {number} exit - * @returns {number} - */ + * @param {number} area + * @param {number} exit + * @returns {number} + */ getDistanceToExit: function (area, exit) { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); @@ -1127,10 +1127,10 @@ const Pather = { }, /** - * @param {number} area - * @param {number} exit - * @returns {PathNode | false} - */ + * @param {number} area + * @param {number} exit + * @returns {PathNode | false} + */ getExitCoords: function (area, exit) { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); @@ -1145,9 +1145,9 @@ const Pather = { /** - * @param {number} area - the id of area to search for the room nearest to the player character - * @returns {[number, number] | false} - */ + * @param {number} area - the id of area to search for the room nearest to the player character + * @returns {[number, number] | false} + */ getNearestRoom: function (area) { let x, y, minDist = 10000; @@ -1177,9 +1177,9 @@ const Pather = { }, /** - * @param {number} targetArea - area id of where the unit leads to - * @returns {boolean} - */ + * @param {number} targetArea - area id of where the unit leads to + * @returns {boolean} + */ openExit: function (targetArea) { switch (true) { case targetArea === sdk.areas.AncientTunnels: @@ -1204,10 +1204,10 @@ const Pather = { }, /** - * @param {UnitType} type - type of the unit to open - * @param {number} id - id of the unit to open - * @returns {boolean} - */ + * @param {UnitType} type - type of the unit to open + * @param {number} id - id of the unit to open + * @returns {boolean} + */ openUnit: function (type, id) { let unit = Misc.poll(() => getUnit(type, id), 1000, 200); if (!unit) throw new Error("openUnit: Unit not found. ID: " + unit); @@ -1233,12 +1233,12 @@ const Pather = { }, /** - * @param {UnitType} type - type of the unit to use - * @param {number} id - id of the unit to use - * @param {number} targetArea - area id of where the unit leads to - * @returns {boolean} - * @todo should use an object as param, or be changed to able to take an already found unit as a param - */ + * @param {UnitType} type - type of the unit to use + * @param {number} id - id of the unit to use + * @param {number} targetArea - area id of where the unit leads to + * @returns {boolean} + * @todo should use an object as param, or be changed to able to take an already found unit as a param + */ useUnit: function (type, id, targetArea) { let unit = Misc.poll(() => getUnit(type, id), 2000, 200); let preArea = me.area; @@ -1254,7 +1254,7 @@ const Pather = { MainLoop: for (let i = 0; i < 5; i += 1) { let usetk = (i < 2 && Skill.useTK(unit)); - + if (unit.distance > 5) { usetk ? this.moveNearUnit(unit, 20) : this.moveToUnit(unit); // try to activate it once @@ -1280,7 +1280,7 @@ const Pather = { } if (type === sdk.unittype.Object && id === sdk.objects.RedPortalToAct4 && me.inArea(sdk.areas.DuranceofHateLvl3) - && targetArea === sdk.areas.PandemoniumFortress + && targetArea === sdk.areas.PandemoniumFortress && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); } @@ -1319,9 +1319,9 @@ const Pather = { allowBroadcast: true, /** - * Meant for use as a MfLeader to let MfHelpers know where to go next - * @param {number} targetArea - area id - */ + * Meant for use as a MfLeader to let MfHelpers know where to go next + * @param {number} targetArea - area id + */ broadcastIntent: function broadcastIntent(targetArea) { if (Config.MFLeader && Pather.allowBroadcast) { let targetAct = sdk.areas.actOf(targetArea); @@ -1330,10 +1330,10 @@ const Pather = { }, /** - * @param {number} targetArea - id of the area to enter - * @param {boolean} check - force the waypoint menu - * @returns {boolean} - */ + * @param {number} targetArea - id of the area to enter + * @param {boolean} check - force the waypoint menu + * @returns {boolean} + */ useWaypoint: function useWaypoint(targetArea, check = false) { switch (targetArea) { case undefined: @@ -1377,13 +1377,13 @@ const Pather = { } /** - * @todo If we start in a3 and want to go to a2, use Meshif - * somehow need to take into account reason for wanting to change act though, e.g. If we were - * going to a2 to revive a merc then running to the a3 waypoint takes us close to our goal. - * On the other hand though if we are going to a2 to take a portal then using meshif makes sense - * extending so far as not just starting next to him but finishing chores anywhere around ormus/wp - * if we are all the way at Alkor then it wouldn't make sense - */ + * @todo If we start in a3 and want to go to a2, use Meshif + * somehow need to take into account reason for wanting to change act though, e.g. If we were + * going to a2 to revive a merc then running to the a3 waypoint takes us close to our goal. + * On the other hand though if we are going to a2 to take a portal then using meshif makes sense + * extending so far as not just starting next to him but finishing chores anywhere around ormus/wp + * if we are all the way at Alkor then it wouldn't make sense + */ if (!getUIFlag(sdk.uiflags.Waypoint) && Town.getDistance("waypoint") > (Skill.haveTK ? 20 : 5)) { Town.move("waypoint"); @@ -1518,9 +1518,9 @@ const Pather = { }, /** - * @param {boolean} use - use the portal that was made - * @returns {Unit | boolean} - */ + * @param {boolean} use - use the portal that was made + * @returns {Unit | boolean} + */ makePortal: function (use = false) { if (me.inTown) return true; @@ -1538,7 +1538,7 @@ const Pather = { .first(); !!oldPortal && (oldGid = oldPortal.gid); - + if (tpTool.use() || Game.getObject("portal")) { let tick = getTickCount(); @@ -1577,11 +1577,11 @@ const Pather = { }, /** - * @param {number} [targetArea] - id of the area the portal leads to - * @param {string} [owner] - name of the portal's owner - * @param {ObjectUnit} [unit] - use existing portal unit - * @returns {boolean} - */ + * @param {number} [targetArea] - id of the area the portal leads to + * @param {string} [owner] - name of the portal's owner + * @param {ObjectUnit} [unit] - use existing portal unit + * @returns {boolean} + */ usePortal: function (targetArea, owner, unit) { if (targetArea && me.area === targetArea) return true; @@ -1597,7 +1597,7 @@ const Pather = { if (portal) { if (portal.objtype === sdk.areas.DuranceofHateLvl3 && portal.getParent() !== me.name - && !Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { + && !Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { throw new Error("Cannot access meph through someone elses portal without first completing Travincal"); } let redPortal = portal.classid === sdk.objects.RedPortal; @@ -1632,7 +1632,7 @@ const Pather = { } else { let timeTillNextPortal = Math.max(3, Math.round(2500 - (getTickCount() - this.lastPortalTick))); delay(timeTillNextPortal); - + continue; } } @@ -1677,10 +1677,10 @@ const Pather = { }, /** - * @param {number} targetArea - id of the area the portal leads to - * @param {string} owner - name of the portal's owner - * @returns {ObjectUnit | false} - */ + * @param {number} targetArea - id of the area the portal leads to + * @param {string} owner - name of the portal's owner + * @returns {ObjectUnit | false} + */ getPortal: function (targetArea, owner) { let portal = Game.getObject("portal"); @@ -1715,14 +1715,14 @@ const Pather = { }, /** - * @param {number} x - the starting x coord - * @param {number} y - the starting y coord - * @param {number} range - maximum allowed range from the starting coords - * @param {number} step - distance between each checked dot on the grid - * @param {number} coll - collision flag to avoid - * @param {number} size - * @returns {[number, number] | false} - */ + * @param {number} x - the starting x coord + * @param {number} y - the starting y coord + * @param {number} range - maximum allowed range from the starting coords + * @param {number} step - distance between each checked dot on the grid + * @param {number} coll - collision flag to avoid + * @param {number} size + * @returns {[number, number] | false} + */ getNearestWalkable: function (x, y, range, step, coll, size) { !step && (step = 1); coll === undefined && (coll = sdk.collision.BlockWall); @@ -1759,13 +1759,13 @@ const Pather = { }, /** - * @param {number} x - the x coord to check - * @param {number} y - the y coord to check - * @param {number} coll - collision flag to search for - * @param {boolean} cacheOnly - use only cached room data - * @param {number} size - * @returns {boolean} - */ + * @param {number} x - the x coord to check + * @param {number} y - the y coord to check + * @param {number} coll - collision flag to search for + * @param {boolean} cacheOnly - use only cached room data + * @param {number} size + * @returns {boolean} + */ checkSpot: function (x, y, coll, cacheOnly, size) { coll === undefined && (coll = sdk.collision.BlockWall); !size && (size = 1); @@ -1786,9 +1786,9 @@ const Pather = { }, /** - * @param {number} act - the act number to check for access - * @returns {boolean} - */ + * @param {number} act - the act number to check for access + * @returns {boolean} + */ accessToAct: function (act) { switch (act) { // Act 1 is always accessible @@ -1809,10 +1809,10 @@ const Pather = { }, /** - * @param {number} area - the id of area to get the waypoint in - * @param {boolean} [clearPath] - * @returns {boolean} - */ + * @param {number} area - the id of area to get the waypoint in + * @param {boolean} [clearPath] + * @returns {boolean} + */ getWP: function (area, clearPath) { area !== me.area && this.journeyTo(area); @@ -1859,10 +1859,10 @@ const Pather = { }, /** - * @param {number} area - the id of area to move to - * @returns {boolean} - * @todo refactor this, it's rather messy - */ + * @param {number} area - the id of area to move to + * @returns {boolean} + * @todo refactor this, it's rather messy + */ journeyTo: function (area) { if (area === undefined) return false; console.time("journeyTo"); @@ -1912,7 +1912,7 @@ const Pather = { if (!me.inTown) { Precast.doPrecast(false); - + if (this.wpAreas.includes(currArea) && !getWaypoint(this.wpAreas.indexOf(currArea))) { this.getWP(currArea); } @@ -2054,12 +2054,12 @@ const Pather = { plotCourse_openedWpMenu: false, /** - * Plot a course to a specific area - * @param {number} src - starting area id - * @param {number} dest - destination area id - * @returns {{ course: number[], useWP: boolean } | false} - * @todo this needs more checks - */ + * Plot a course to a specific area + * @param {number} src - starting area id + * @param {number} dest - destination area id + * @returns {{ course: number[], useWP: boolean } | false} + * @todo this needs more checks + */ plotCourse: function (dest, src) { let node, prevArea; let useWP = false; @@ -2133,9 +2133,9 @@ const Pather = { if (this.areasConnected(node.from, node.to)) { // If we have this wp we can start from there if ((me.inTown // check wp in town - || ((src !== previousAreas[dest] && dest !== previousAreas[src]) // check wp if areas aren't linked - && previousAreas[src] !== previousAreas[dest])) // check wp if areas aren't linked with a common area - && Pather.wpAreas.indexOf(node.from) > 0 && getWaypoint(Pather.wpAreas.indexOf(node.from)) + || ((src !== previousAreas[dest] && dest !== previousAreas[src]) // check wp if areas aren't linked + && previousAreas[src] !== previousAreas[dest])) // check wp if areas aren't linked with a common area + && Pather.wpAreas.indexOf(node.from) > 0 && getWaypoint(Pather.wpAreas.indexOf(node.from)) ) { if (node.from !== src) { useWP = true; @@ -2184,12 +2184,12 @@ const Pather = { }, /** - * Check if two areas are connected - * @param {number} src - starting area id - * @param {number} dest - destination area id - * @returns {boolean} - * @todo this needs more checks - */ + * Check if two areas are connected + * @param {number} src - starting area id + * @param {number} dest - destination area id + * @returns {boolean} + * @todo this needs more checks + */ areasConnected: function (src, dest) { if (src === sdk.areas.CanyonofMagic && dest === sdk.areas.ArcaneSanctuary) { return false; diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index e16c83d02..329687f50 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -47,12 +47,12 @@ const Precast = { }, /** - * Easier Shout/Bo/Bc casting with state checks to ensure it was casted - * @param {number} skillId - * @param {number | Unit} x - * @param {number} [y] - * @returns {boolean} - */ + * Easier Shout/Bo/Bc casting with state checks to ensure it was casted + * @param {number} skillId + * @param {number | Unit} x + * @param {number} [y] + * @returns {boolean} + */ warCries: function (skillId, x, y) { if (!skillId || x === undefined) return false; const states = {}; @@ -110,9 +110,9 @@ const Precast = { }, /** - * @param {boolean} force - * @returns {boolean} - */ + * @param {boolean} force + * @returns {boolean} + */ precastCTA: function (force = false) { if (!Config.UseCta || this.haveCTA === -1 || me.classic || me.barbarian || me.inTown || me.shapeshifted) { return false; @@ -143,11 +143,11 @@ const Precast = { }, /** - * Check which slot (primary or secondary) gives us the most skillpoints in a skill - * @param {number} skillId - * @returns {0 | 1} best slot to give us the most skillpoints in a skill - * @todo Move this to be part of the SkillData class - */ + * Check which slot (primary or secondary) gives us the most skillpoints in a skill + * @param {number} skillId + * @returns {0 | 1} best slot to give us the most skillpoints in a skill + * @todo Move this to be part of the SkillData class + */ getBetterSlot: function (skillId) { if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; @@ -196,8 +196,8 @@ const Precast = { let [sumCurr, sumSwap] = [0, 0]; const sumStats = function (item) { return (item.getStat(sdk.stats.AllSkills) - + item.getStat(sdk.stats.AddClassSkills, classid) + item.getStat(sdk.stats.AddSkillTab, skillTab) - + item.getStat(sdk.stats.SingleSkill, skillId) + item.getStat(sdk.stats.NonClassSkill, skillId)); + + item.getStat(sdk.stats.AddClassSkills, classid) + item.getStat(sdk.stats.AddSkillTab, skillTab) + + item.getStat(sdk.stats.SingleSkill, skillId) + item.getStat(sdk.stats.NonClassSkill, skillId)); }; me.getItemsEx() @@ -249,7 +249,7 @@ const Precast = { if (Config.PacketCasting > 1 || usePacket) { Config.DebugMode.Skill && console.debug("Packet casting: " + skillId); - + switch (typeof x) { case "number": Packet.castSkill(sdk.skills.hand.Right, x, y); @@ -400,12 +400,12 @@ const Precast = { // should the config check still be included even though its part of Skill.init? /** - * @description Handle precast related skills - * @param {boolean} force - force re-cast of all precast skills - * @param {boolean} partial - force re-cast of all state related precast skills - * @returns {boolean} sucessfully casted - * @todo durations - */ + * @description Handle precast related skills + * @param {boolean} force - force re-cast of all precast skills + * @param {boolean} partial - force re-cast of all state related precast skills + * @returns {boolean} sucessfully casted + * @todo durations + */ doPrecast: function (force = false, partial = false) { if (!this.enabled) return false; @@ -418,8 +418,8 @@ const Precast = { // Force BO 30 seconds before it expires if (Precast.haveCTA > -1) { forceBo = (force || partial - || (getTickCount() - Precast.skills.battleOrders.tick >= Precast.skills.battleOrders.duration - 30000) - || !me.getState(sdk.states.BattleCommand)); + || (getTickCount() - Precast.skills.battleOrders.tick >= Precast.skills.battleOrders.duration - 30000) + || !me.getState(sdk.states.BattleCommand)); forceBo && this.precastCTA(forceBo); } @@ -443,7 +443,7 @@ const Precast = { let choosenSkill = (typeof Config.UseColdArmor === "number" && Skill.canUse(Config.UseColdArmor) ? Config.UseColdArmor : (Precast.skills.coldArmor.best || -1)); - + if (Precast.skills.coldArmor.tick > 0 && Precast.skills.coldArmor.duration > Time.seconds(45)) { if (getTickCount() - Precast.skills.coldArmor.tick >= Precast.skills.coldArmor.duration - Time.seconds(30)) { force = true; @@ -480,7 +480,7 @@ const Precast = { break; case sdk.player.class.Necromancer: if (Skill.canUse(sdk.skills.BoneArmor) - && (force || this.skills.boneArmor.armorPercent() < 75 || !me.getState(sdk.states.BoneArmor))) { + && (force || this.skills.boneArmor.armorPercent() < 75 || !me.getState(sdk.states.BoneArmor))) { this.cast(sdk.skills.BoneArmor); this.skills.boneArmor.max === 0 && (this.skills.boneArmor.max = me.getStat(sdk.stats.SkillBoneArmorMax)); } diff --git a/d2bs/kolbot/libs/core/Runewords.js b/d2bs/kolbot/libs/core/Runewords.js index d6e53d23c..335a347f6 100644 --- a/d2bs/kolbot/libs/core/Runewords.js +++ b/d2bs/kolbot/libs/core/Runewords.js @@ -50,19 +50,19 @@ const Runewords = { }, /** - * Ensures this item isn't wanted by the CraftingSystem - * @param {ItemUnit} item - * @returns {boolean} - * @todo Why only the crafting system? - */ + * Ensures this item isn't wanted by the CraftingSystem + * @param {ItemUnit} item + * @returns {boolean} + * @todo Why only the crafting system? + */ validItem: function (item) { return CraftingSystem.validGids.indexOf(item.gid) === -1; }, /** - * build a list of needed runes. won't count runes until the base item is found for a given runeword - * @returns {void} - */ + * build a list of needed runes. won't count runes until the base item is found for a given runeword + * @returns {void} + */ buildLists: function () { Runewords.validGids = []; Runewords.needList = []; @@ -114,9 +114,9 @@ const Runewords = { }, /** - * @param {number} classid - * @param {number} gid - */ + * @param {number} classid + * @param {number} gid + */ update: function (classid, gid) { for (let i = 0; i < this.needList.length; i += 1) { if (this.needList[i] === classid) { @@ -132,9 +132,9 @@ const Runewords = { }, /** - * returns an array of items that make a runeword if found, false if we don't have enough items for any - * @returns {ItemUnit[] | boolean} - */ + * returns an array of items that make a runeword if found, false if we don't have enough items for any + * @returns {ItemUnit[] | boolean} + */ 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); @@ -177,32 +177,32 @@ const Runewords = { }, /** - * for pickit - * @param {ItemUnit} unit - * @returns {boolean} - */ + * for pickit + * @param {ItemUnit} unit + * @returns {boolean} + */ checkItem: function (unit) { if (!Config.MakeRunewords) return false; return (unit.itemType === sdk.items.type.Rune && this.needList.includes(unit.classid)); }, /** - * for clearInventory - don't drop runes that are a part of runeword recipe - * @param {ItemUnit} unit - * @returns {boolean} - */ + * for clearInventory - don't drop runes that are a part of runeword recipe + * @param {ItemUnit} unit + * @returns {boolean} + */ keepItem: function (unit) { return this.validGids.includes(unit.gid); }, /** - * 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} - */ + * 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} + */ getBase: function (runeword, base, ethFlag, reroll) { let item = typeof base === "object" ? base @@ -211,11 +211,11 @@ const Runewords = { if (item) { do { if (item && item.quality < sdk.items.quality.Magic - && item.sockets === runeword.sockets && runeword.itemTypes.includes(item.itemType)) { + && 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 - */ + * 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()) || (reroll && item.getItem() && !NTIP.CheckItem(item, this.pickitEntries))) { if (!ethFlag || (ethFlag === Roll.Eth && item.ethereal) || (ethFlag === Roll.NonEth && !item.ethereal)) { @@ -230,10 +230,10 @@ const Runewords = { }, /** - * @param {ItemUnit} base - * @param {ItemUnit} rune - * @returns {boolean} - */ + * @param {ItemUnit} base + * @param {ItemUnit} rune + * @returns {boolean} + */ socketItem: function (base, rune) { if (!rune.toCursor()) return false; diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index 7cc691c25..1bae579d2 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -8,12 +8,12 @@ (function() { /** - * @constructor - * @param {string} name - container name - * @param {number} width - container width - * @param {number} height - container height - * @param {number} location - container location - */ + * @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; @@ -35,8 +35,8 @@ } /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ Container.prototype.Mark = function (item) { let x, y; @@ -61,9 +61,9 @@ }; /** - * @param {ItemUnit} item - * @param {number[][]} baseRef - */ + * @param {ItemUnit} item + * @param {number[][]} baseRef + */ Container.prototype.IsLocked = function (item, baseRef) { let h, w; let reference = baseRef.slice(0); @@ -116,8 +116,8 @@ }; /** - * @param {string} name - */ + * @param {string} name + */ Container.prototype.cubeSpot = function (name) { if (name !== "Stash") return true; @@ -142,16 +142,16 @@ }; /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ Container.prototype.CanFit = function (item) { return (!!this.FindSpot(item)); }; /** - * @param {number[]} itemIdsLeft - * @param {number[]} itemIdsRight - */ + * @param {number[]} itemIdsLeft + * @param {number[]} itemIdsRight + */ Container.prototype.SortItems = function (itemIdsLeft, itemIdsRight) { Storage.Reload(); @@ -176,7 +176,7 @@ if (item.classid === sdk.quest.item.Cube && item.isInStash && item.x === 0 && item.y === 0) { continue; // dont touch the cube } - + let [ix, iy] = [item.y, item.x]; // x and y are backwards! if (this.location !== item.location) { @@ -236,18 +236,18 @@ }; /** - * @param {ItemUnit} item - * @param {boolean} reverseX - * @param {boolean} reverseY - * @param {number[]} 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; /** - * @todo review this to see why it sometimes fails when there is actually enough room - */ + * @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; @@ -293,16 +293,16 @@ let bufferItemQuality = this.itemList[this.buffer[x][y] - 1].quality; if (Config.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! + && !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]))) { + || (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) { @@ -342,10 +342,10 @@ }; /** - * @param {ItemUnit} item - * @param {{x: number, y: number}} location - * @param {boolean} force - */ + * @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] = [[], []]; @@ -355,16 +355,16 @@ // Make sure it's a valid item and item is in a priority sorting list if (!item || !item.classid - || (Config.SortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 - && Config.SortSettings.ItemsSortedFromLeftPriority.indexOf(item.classid) === -1 - && !force)) { + || (Config.SortSettings.ItemsSortedFromRightPriority.indexOf(item.classid) === -1 + && Config.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))) { + || ((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 } @@ -417,10 +417,10 @@ }; /** - * @param {ItemUnit} item - * @param {number} mX - * @param {number} mY - */ + * @param {ItemUnit} item + * @param {number} mX + * @param {number} mY + */ Container.prototype.MoveToSpot = function (item, mX, mY) { let cItem, cube; @@ -489,8 +489,8 @@ }; /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ Container.prototype.MoveTo = function (item) { let nPos; @@ -543,8 +543,8 @@ }; /** - * @param {number[][]} baseRef - */ + * @param {number[][]} baseRef + */ Container.prototype.Compare = function (baseRef) { let h, w, n, item, itemList, reference; @@ -592,8 +592,8 @@ }; /** - * @type {storage} Storage - */ + * @type {storage} Storage + */ const Storage = new function () { this.Init = () => { this.StashY = me.classic ? 4 : Config.SortSettings.PlugYStash ? 10 : 8; @@ -603,10 +603,10 @@ 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]} - */ + * @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]; diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index b24148731..d307bc410 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -44,13 +44,13 @@ const NPC = { Object.defineProperty(NPC, "getAct", { /** - * Returns the act(s) where the given NPC can be found. - * - * @memberof NPC - * @method getAct - * @param {string} name - The name of the NPC. - * @returns {Array} An array of act numbers where the NPC can be found. - */ + * Returns the act(s) where the given NPC can be found. + * + * @memberof NPC + * @method getAct + * @param {string} name - The name of the NPC. + * @returns {Array} An array of act numbers where the NPC can be found. + */ value: function (name) { if (name === NPC.Cain) return [me.act]; if (name === NPC.Warriv) return [1, 2]; @@ -79,17 +79,17 @@ const Town = { telekinesis: true, sellTimer: getTickCount(), // shop speedup test /** - * @namespace - */ + * @namespace + */ lastInteractedNPC: { tick: 0, /** - * @type {{ name: string, gid: number, area: number}} - */ + * @type {{ name: string, gid: number, area: number}} + */ unit: {}, /** - * @param {NPCUnit} npc - */ + * @param {NPCUnit} npc + */ set: function (npc) { if (npc.hasOwnProperty("name") && Object.values(NPC).includes(npc.name)) { // valid npc @@ -139,17 +139,17 @@ const Town = { ], /** - * @description Check if item type is included in the ignore types list - * @param {number} type - * @returns {boolean} If it is an item in the list - */ + * @description Check if item type is included in the ignore types list + * @param {number} type + * @returns {boolean} If it is an item in the list + */ ignoreType: function (type) { return Town.ignoredItemTypes.includes(type); }, /** - * @param {boolean} repair - */ + * @param {boolean} repair + */ doChores: function (repair = false) { delay(250); @@ -157,8 +157,8 @@ const Town = { console.info(true); /** - * @todo Pre-build task list so we can more efficiently peform our chores - */ + * @todo Pre-build task list so we can more efficiently peform our chores + */ !me.inTown && this.goToTown(); if (!Misc.poll(() => me.gameReady && me.inTown, 2000, 250)) throw new Error("Failed to go to town for chores"); @@ -202,10 +202,10 @@ const Town = { }, /** - * @param {string} name - * @param {boolean} cancel - * @returns {boolean | Unit} - */ + * @param {string} name + * @param {boolean} cancel + * @returns {boolean | Unit} + */ npcInteract: function (name = "", cancel = true) { // name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); // what about finding the closest name in case someone mispells it? @@ -218,7 +218,7 @@ const Town = { const npcName = NPC[npcKey]; !me.inTown && Town.goToTown(); - + if (!NPC.getAct(npcName).includes(me.act)) { Town.goToTown(NPC.getAct(npcName)[0]); } @@ -260,8 +260,8 @@ const Town = { }, /** - * @description handle quest consumables if we have them - */ + * @description handle quest consumables if we have them + */ checkQuestItems: function () { // Act 1 // Tools of the trade @@ -321,11 +321,11 @@ const Town = { }, /** - * @description Start a task and return the NPC Unit - * @param {string} task - * @param {string} reason - * @returns {boolean | Unit} - */ + * @description Start a task and return the NPC Unit + * @param {string} task + * @param {string} reason + * @returns {boolean | Unit} + */ initNPC: function (task = "", reason = "undefined") { console.time("initNPC"); console.info(true, reason); @@ -337,7 +337,7 @@ const Town = { let npc = null; let wantedNpc = this.tasks[me.act - 1][task] !== undefined ? this.tasks[me.act - 1][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(); @@ -347,8 +347,8 @@ const Town = { if (npc) { let npcName = npc.name.toLowerCase(); if (!justUseClosest && ((npcName !== wantedNpc) - // Jamella gamble fix - || (task === "Gamble" && npcName === NPC.Jamella))) { + // Jamella gamble fix + || (task === "Gamble" && npcName === NPC.Jamella))) { me.cancelUIFlags(); npc = null; } @@ -433,8 +433,8 @@ const Town = { }, /** - * @description Go to a town healer if we are below certain hp/mp percent or have a status effect - */ + * @description Go to a town healer if we are below certain hp/mp percent or have a status effect + */ heal: function () { if (!me.needHealing()) return true; return !!(this.initNPC("Heal", "heal")); @@ -482,8 +482,8 @@ const Town = { } /** - * @todo If we are set to cube rejuvs, allow buying potions once we have our gem - */ + * @todo If we are set to cube rejuvs, allow buying potions once we have our gem + */ // We have enough potions in inventory (buffer.mp >= Config.MPBuffer && buffer.hp >= Config.HPBuffer) && (needBuffer = false); @@ -536,7 +536,7 @@ const Town = { 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(); @@ -558,10 +558,10 @@ const Town = { }, /** - * @description Check when to shift-buy potions - * @param {number} col - * @param {0 | 1 | 2 | 3 | 4} beltSize - */ + * @description Check when to shift-buy potions + * @param {number} col + * @param {0 | 1 | 2 | 3 | 4} beltSize + */ shiftCheck: function (col, beltSize) { let fillType; @@ -593,10 +593,10 @@ const Town = { }, /** - * @description Return column status (needed potions in each column) - * @param {0 | 1 | 2 | 3 | 4} beltSize - * @returns {[number, number, number, number]} - */ + * @description Return column status (needed potions in each column) + * @param {0 | 1 | 2 | 3 | 4} beltSize + * @returns {[number, number, number, number]} + */ checkColumns: function (beltSize) { (typeof beltSize !== "number" || beltSize < 0 || beltSize > 4) && (beltSize = Storage.BeltSize()); let col = [beltSize, beltSize, beltSize, beltSize]; @@ -613,12 +613,12 @@ const Town = { }, /** - * @description Get the highest potion from current npc - * @param {Unit} npc - * @param {"hp" | "mp"} type - * @param {1 | 2 | 3 | 4 | 5} highestPot - * @returns {boolean | ItemUnit} - */ + * @description Get the highest potion from current npc + * @param {Unit} npc + * @param {"hp" | "mp"} type + * @param {1 | 2 | 3 | 4 | 5} highestPot + * @returns {boolean | ItemUnit} + */ getPotion: function (npc, type, highestPot = 5) { if (!type) return false; @@ -636,8 +636,8 @@ const Town = { }, /** - * @param {number} classid - */ + * @param {number} classid + */ fillTome: function (classid) { if (me.gold < 450) return false; if (this.checkScrolls(classid) >= 13) return true; @@ -681,9 +681,9 @@ const Town = { }, /** - * @param {number} id - * @returns {number} quantity of scrolls in tome - */ + * @param {number} id + * @returns {number} quantity of scrolls in tome + */ checkScrolls: function (id) { let tome = me.getTome(id); @@ -704,7 +704,7 @@ const Town = { identify: function () { !me.inShop && me.cancelUIFlags(); if (Town.cainID()) return true; - + let list = (Storage.Inventory.Compare(Config.Inventory) || []); if (list.length === 0) return false; @@ -864,11 +864,11 @@ const Town = { }, /** - * @param {ItemUnit} unit - * @param {ItemUnit} tome - * @param {Boolean} packetID - * @returns {boolean} - */ + * @param {ItemUnit} unit + * @param {ItemUnit} tome + * @param {Boolean} packetID + * @returns {boolean} + */ identifyItem: function (unit, tome, packetID = false) { if (Config.PacketShopping || packetID) return Packet.identifyItem(unit, tome); if (!unit || unit.identified) return false; @@ -919,7 +919,7 @@ const Town = { shopItems: function () { if (!Config.MiniShopBot) return true; - + let npc = getInteractedNPC(); if (!npc || !npc.itemcount) return false; @@ -955,8 +955,8 @@ const Town = { }, /** - * @type {number[]} - */ + * @type {number[]} + */ gambleIds: [], gamble: function () { @@ -1058,8 +1058,8 @@ const Town = { }, /** - * @param {ItemUnit[]} list - */ + * @param {ItemUnit[]} list + */ getGambledItem: function (list = []) { let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); @@ -1081,15 +1081,15 @@ const Town = { }, /** - * @param {number} quantity - * @param {number | string} type - * @param {boolean} [drink=false] - * @param {boolean} [force=false] - * @param {Unit} [npc=null] - */ + * @param {number} quantity + * @param {number | string} type + * @param {boolean} [drink=false] + * @param {boolean} [force=false] + * @param {Unit} [npc=null] + */ buyPots: function (quantity = 0, type = undefined, drink = false, force = false, npc = null) { if (!quantity || !type) return false; - + // convert to classid if isn't one typeof type === "string" && (type = (sdk.items[type.capitalize(true) + "Potion"] || false)); if (!type) return false; @@ -1161,10 +1161,10 @@ const Town = { }, /** - * @param {number | string} type - * @param {boolean} [log=true] - * @returns {{ potName: string, quantity: number }} - */ + * @param {number | string} type + * @param {boolean} [log=true] + * @returns {{ potName: string, quantity: number }} + */ drinkPots: function (type = undefined, log = true) { // convert to classid if isn't one typeof type === "string" && (type = (sdk.items[type.capitalize(true) + "Potion"] || false)); @@ -1219,7 +1219,7 @@ const Town = { checkKeys: function () { if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 - || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { + || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { return 12; } @@ -1237,8 +1237,8 @@ const Town = { }, /** - * @param {ItemUnit} item - Rune - */ + * @param {ItemUnit} item - Rune + */ repairIngredientCheck: function (item) { if (!Config.CubeRepair) return false; @@ -1297,9 +1297,9 @@ const Town = { }, /** - * @param {ItemUnit} item - * @returns {boolean} - */ + * @param {ItemUnit} item + * @returns {boolean} + */ cubeRepairItem: function (item) { if (!item.isInStorage) return false; @@ -1366,8 +1366,8 @@ const Town = { }, /** - * @param {boolean} [force=false] - */ + * @param {boolean} [force=false] + */ repair: function (force = false) { if (this.cubeRepair()) return true; @@ -1393,7 +1393,7 @@ const Town = { let quiver = bowCheck === "bow" ? "aqv" : "cqv"; let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); !!myQuiver && myQuiver.drop(); - + npc = this.initNPC("Repair", "repair"); if (!npc) return false; @@ -1446,10 +1446,10 @@ const Town = { }, /** - * @param {number} repairPercent - * @param {boolean} chargedItems - * @returns {ItemUnit[]} - */ + * @param {number} repairPercent + * @param {boolean} chargedItems + * @returns {ItemUnit[]} + */ getItemsForRepair: function (repairPercent, chargedItems) { let itemList = []; let item = me.getItem(-1, sdk.items.mode.Equipped); @@ -1585,24 +1585,24 @@ const Town = { }, /** - * @param {ItemUnit} item - */ + * @param {ItemUnit} item + */ canStash: function (item) { if (Town.ignoreType(item.itemType) - || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) - || !Town.canStashGem(item)) { + || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) + || !Town.canStashGem(item)) { return false; } /** - * @todo add sorting here first if we can't fit the item - */ + * @todo add sorting here first if we can't fit the item + */ return Storage.Stash.CanFit(item); }, /** - * get ordered list of gems in inventory to use with Gem Hunter Script - * @returns {ItemUnit[]} ordered list of relevant gems - */ + * get ordered list of gems in inventory to use with Gem Hunter Script + * @returns {ItemUnit[]} ordered list of relevant gems + */ getGemsInInv: function () { let GemList = Config.GemHunter.GemList; return me.getItemsEx() @@ -1611,9 +1611,9 @@ const Town = { }, /** - * get ordered list of gems in stash to use with Gem Hunter Script - * @returns {ItemUnit[]} ordered list of relevant gems - */ + * get ordered list of gems in stash to use with Gem Hunter Script + * @returns {ItemUnit[]} ordered list of relevant gems + */ getGemsInStash: function () { let GemList = Config.GemHunter.GemList; return me.getItemsEx() @@ -1622,10 +1622,10 @@ const Town = { }, /** - * gem check for use with Gem Hunter Script - * @param {ItemUnit} item - * @returns {boolean} if we should stash this gem - */ + * gem check for use with Gem Hunter Script + * @param {ItemUnit} item + * @returns {boolean} if we should stash this gem + */ canStashGem: function (item) { // we aren't using the gem hunter script or we aren't scanning for the gem shrines while moving // for now we are only going to keep a gem in our invo while the script is active @@ -1641,15 +1641,15 @@ const Town = { return ( (GemList.indexOf(bestGeminStash) < GemList.indexOf(bestGeminInv)) // better one in stash - || (GemList.indexOf(bestGeminInv) < GemList.indexOf(item.classid)) // better one in inv - || (gemsInInvo.filter((p) => p.classid === item.classid).length > 1)); // another one in inv + || (GemList.indexOf(bestGeminInv) < GemList.indexOf(item.classid)) // better one in inv + || (gemsInInvo.filter((p) => p.classid === item.classid).length > 1)); // another one in inv }, /** - * move best gem from stash to inventory, if none in inventrory - * to use with Gem Hunter Script - * @returns {boolean} if any gem has been moved - */ + * move best gem from stash to inventory, if none in inventrory + * to use with Gem Hunter Script + * @returns {boolean} if any gem has been moved + */ getGem: function () { if (Loader.scriptName() === "GemHunter") { if (this.getGemsInInv().length === 0 && this.getGemsInStash().length > 0) { @@ -1662,9 +1662,9 @@ const Town = { }, /** - * @param {boolean} [stashGold=true] - * @returns {boolean} - */ + * @param {boolean} [stashGold=true] + * @returns {boolean} + */ stash: function (stashGold = true) { if (!this.needStash()) return true; @@ -1674,12 +1674,12 @@ const Town = { if (items) { Config.SortSettings.SortStash && Storage.Stash.SortItems(); - + for (let i = 0; i < items.length; i += 1) { if (this.canStash(items[i])) { let result = false; let pickResult = Pickit.checkItem(items[i]).result; - + switch (true) { case pickResult > Pickit.Result.UNWANTED && pickResult < Pickit.Result.TRASH: case Cubing.keepItem(items[i]): @@ -1982,7 +1982,7 @@ const Town = { me.cancelUIFlags(); } } - + // Remove potions in the wrong slot of our belt this.clearBelt(); @@ -1990,11 +1990,11 @@ const Town = { const beltSize = Storage.BeltSize(); // belt 4x4 locations /** - * 12 13 14 15 - * 8 9 10 11 - * 4 5 6 7 - * 0 1 2 3 - */ + * 12 13 14 15 + * 8 9 10 11 + * 4 5 6 7 + * 0 1 2 3 + */ const beltMax = (beltSize * 4); const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; let potsInInventory = me.getItemsEx() @@ -2091,7 +2091,7 @@ const Town = { // Don't drop tomes, keys or potions or quest-items // Don't throw cubing/runeword/crafting ingredients if (ignoreTypes.indexOf(item.itemType) === -1 && item.sellable - && !Cubing.keepItem(item) && !Runewords.keepItem(item) && !CraftingSystem.keepItem(item)) { + && !Cubing.keepItem(item) && !Runewords.keepItem(item) && !CraftingSystem.keepItem(item)) { return true; } return false; @@ -2162,8 +2162,8 @@ const Town = { }, /** - * @todo figure out how to typedef this. - */ + * @todo figure out how to typedef this. + */ act: [{}, {}, {}, {}, {}], initialize: function () { @@ -2256,9 +2256,9 @@ const Town = { }, /** - * @param {string} spot - * @returns {number} distance to town location - */ + * @param {string} spot + * @returns {number} distance to town location + */ getDistance: function (spot = "") { !me.inTown && this.goToTown(); !this.act[me.act - 1].initialized && this.initialize(); @@ -2276,10 +2276,10 @@ const Town = { }, /** - * @param {string} spot - * @param {boolean} [allowTK] - * @returns {boolean} - */ + * @param {string} spot + * @param {boolean} [allowTK] + * @returns {boolean} + */ move: function (spot = "", allowTK = true) { !me.inTown && this.goToTown(); !this.act[me.act - 1].initialized && this.initialize(); @@ -2289,7 +2289,7 @@ const Town = { if (me.act === 5) { let path = []; let returnWhenDone = false; - + // Act 5 wp->portalspot override - ActMap.cpp crash if (spot === "portalspot" && getDistance(me.x, me.y, 5113, 5068) <= 8) { path = [5113, 5068, 5108, 5051, 5106, 5046, 5104, 5041, 5102, 5027, 5098, 5018]; @@ -2328,10 +2328,10 @@ const Town = { }, /** - * @param {string} spot - * @param {boolean} [allowTK] - * @returns {boolean} - */ + * @param {string} spot + * @param {boolean} [allowTK] + * @returns {boolean} + */ moveToSpot: function (spot = "", allowTK = true) { let townSpot; let longRange = (!Skill.haveTK && spot === "waypoint"); @@ -2409,10 +2409,10 @@ const Town = { }, /** - * @param {1 | 2 | 3 | 4 | 5} act - * @param {boolean} [wpmenu=false] - * @returns {boolean} - */ + * @param {1 | 2 | 3 | 4 | 5} act + * @param {boolean} [wpmenu=false] + * @returns {boolean} + */ goToTown: function (act = 0, wpmenu = false) { if (!me.inTown) { try { @@ -2464,9 +2464,9 @@ const Town = { }, /** - * @param {boolean} [repair] - * @returns {boolean} - */ + * @param {boolean} [repair] + * @returns {boolean} + */ visitTown: function (repair = false) { console.info(true); diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 4bc1cfc6e..12c970893 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -32,9 +32,9 @@ ScriptError.prototype.constructor = ScriptError; /** - * get all running threads and return them as an array - * @returns {Script[]} - */ + * get all running threads and return them as an array + * @returns {Script[]} + */ const getThreads = function () { let threads = []; let script = getScript(); @@ -49,9 +49,9 @@ }; /** - * @param {...args} - * @returns {Unit[]} - */ + * @param {...args} + * @returns {Unit[]} + */ const getUnits = function (...args) { let units = [], unit = getUnit.apply(null, args); @@ -65,14 +65,14 @@ }; /** - * @typedef {Object} Args - * @property {0 | 1 | 2} arg1 - where - * @property {number | ItemUnit} arg2 - bodyLoc to click, item to click, x coord - * @property {number} [arg3] - y coord - * @property {number} [arg4] - location - * - * @param {...Args} args - */ + * @typedef {Object} Args + * @property {0 | 1 | 2} arg1 - where + * @property {number | ItemUnit} arg2 - bodyLoc to click, item to click, x coord + * @property {number} [arg3] - y coord + * @property {number} [arg4] - location + * + * @param {...Args} args + */ const clickItemAndWait = function (...args) { let timeout = getTickCount(), timedOut; let before = !me.itemoncursor; @@ -96,13 +96,13 @@ }; /** - * @description clickMap doesn't return if we sucessfully clicked a unit just that a click was sent, this checks and returns that a units mode has changed - * as a result of us clicking it. - * @param {number} button - * @param {0 | 1} shift - * @param {Unit} unit - * @returns {boolean} If a units mode has changed as a result of clicking it - */ + * @description clickMap doesn't return if we sucessfully clicked a unit just that a click was sent, this checks and returns that a units mode has changed + * as a result of us clicking it. + * @param {number} button + * @param {0 | 1} shift + * @param {Unit} unit + * @returns {boolean} If a units mode has changed as a result of clicking it + */ const clickUnitAndWait = function (button, shift, unit) { if (typeof (unit) !== "object") throw new Error("clickUnitAndWait: Third arg must be a Unit."); @@ -113,13 +113,13 @@ delay(Math.max(me.ping * 2, 250)); clickMap(button + 2, shift, unit); me.blockMouse = false; - + let waitTick = getTickCount(); let timeOut = Math.min(1000, 100 + (me.ping * 4)); while (getTickCount() - waitTick < timeOut) { delay(30); - + // quit the loop if mode has changed if (before !== unit.mode) { break; @@ -132,15 +132,15 @@ }; /** - * @class - * @description new PacketBuilder() - create new packet object - * @example (Spoof 'reassign player' packet to client): - * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); - * @example (Spoof 'player move' packet to server): - * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); - * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` - * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` - */ + * @class + * @description new PacketBuilder() - create new packet object + * @example (Spoof 'reassign player' packet to client): + * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); + * @example (Spoof 'player move' packet to server): + * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); + * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` + * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` + */ function PacketBuilder () { // globals DataView ArrayBuffer if (this.__proto__.constructor !== PacketBuilder) { @@ -378,130 +378,130 @@ } }, /** - * @returns {ItemUnit | undefined} item on cursor - */ + * @returns {ItemUnit | undefined} item on cursor + */ getCursorUnit: function () { return getUnit(100); }, /** - * @returns {ItemUnit | undefined} item cursor is hovering over - */ + * @returns {ItemUnit | undefined} item cursor is hovering over + */ getSelectedUnit: function () { return getUnit(101); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Player} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Player} + */ getPlayer: function (id, mode, gid) { return getUnit(sdk.unittype.Player, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Monster} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Monster} + */ getMonster: function (id, mode, gid) { return getUnit(sdk.unittype.Monster, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Monster} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Monster} + */ getNPC: function (id, mode, gid) { return getUnit(sdk.unittype.NPC, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {ObjectUnit} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {ObjectUnit} + */ getObject: function (id, mode, gid) { return getUnit(sdk.unittype.Object, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Missile} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Missile} + */ getMissile: function (id, mode, gid) { return getUnit(sdk.unittype.Missile, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {ItemUnit} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {ItemUnit} + */ getItem: function (id, mode, gid) { return getUnit(sdk.unittype.Item, id, mode, gid); }, /** - * @param {number | string} [id] - * @param {number} [mode] - * @param {number} [gid] - * @returns {Tile} - */ + * @param {number | string} [id] + * @param {number} [mode] + * @param {number} [gid] + * @returns {Tile} + */ getStairs: function (id, mode, gid) { return getUnit(sdk.unittype.Stairs, id, mode, gid); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ getPresetMonster: function (area, id) { !area && (area = me.area); return getPresetUnit(area, sdk.unittype.Monster, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit[]} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ getPresetMonsters: function (area, id) { !area && (area = me.area); return getPresetUnits(area, sdk.unittype.Monster, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ getPresetObject: function (area, id) { !area && (area = me.area); return getPresetUnit(area, sdk.unittype.Object, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit[]} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ getPresetObjects: function (area, id) { !area && (area = me.area); return getPresetUnits(area, sdk.unittype.Object, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit} + */ getPresetStair: function (area, id) { !area && (area = me.area); return getPresetUnit(area, sdk.unittype.Stairs, id); }, /** - * @param {number} area - * @param {number} id - * @returns {PresetUnit[]} - */ + * @param {number} area + * @param {number} id + * @returns {PresetUnit[]} + */ getPresetStairs: function (area, id) { !area && (area = me.area); return getPresetUnits(area, sdk.unittype.Stairs, id); diff --git a/d2bs/kolbot/libs/manualplay/MapMode.js b/d2bs/kolbot/libs/manualplay/MapMode.js index f38c58d98..56fa9e2fb 100644 --- a/d2bs/kolbot/libs/manualplay/MapMode.js +++ b/d2bs/kolbot/libs/manualplay/MapMode.js @@ -9,7 +9,7 @@ const MapMode = { mapHelperFilePath: "libs/manualplay/threads/maphelper.js", include: function () { let files = dopen("libs/manualplay/libs/").getFiles(); - + Array.isArray(files) && files .filter(file => file.endsWith(".js")) .forEach(function (x) { @@ -28,7 +28,7 @@ const MapMode = { Config.MiniShopBot = false; // Scan items in NPC shops. Config.PacketShopping = true; // Use packets to shop. Improves shopping speed. Config.TownCheck = false; // Go to town if out of potions - + // Additional item info log settings. All info goes to \logs\ItemLog.txt Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; diff --git a/d2bs/kolbot/libs/manualplay/config/Amazon.js b/d2bs/kolbot/libs/manualplay/config/Amazon.js index 0f4463625..9e35ceb1c 100644 --- a/d2bs/kolbot/libs/manualplay/config/Amazon.js +++ b/d2bs/kolbot/libs/manualplay/config/Amazon.js @@ -18,194 +18,194 @@ include("manualplay/MapMode.js"); function LoadConfig() { - MapMode.generalSettings(); - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - // Chicken settings - Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - - /* Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - - // Pickit config. Default folder is kolbot/pickit. - Config.PickitFiles.push("kolton.nip"); - Config.PickitFiles.push("LLD.nip"); - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - - // Public game options - - // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - // LocalChat messages will only be visible on clients running on the same PC - Config.LocalChat.Enabled = false; // enable the LocalChat system - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // MF Switch - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 35; BAD: Config.AttackSkill[1] = -35; - */ - - Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - // Class specific config - Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. - Config.SummonValkyrie = true; // Summon Valkyrie - - /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + MapMode.generalSettings(); + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + // Chicken settings + Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + + /* Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + + // Pickit config. Default folder is kolbot/pickit. + Config.PickitFiles.push("kolton.nip"); + Config.PickitFiles.push("LLD.nip"); + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + + // Public game options + + // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + // LocalChat messages will only be visible on clients running on the same PC + Config.LocalChat.Enabled = false; // enable the LocalChat system + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // MF Switch + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 35; BAD: Config.AttackSkill[1] = -35; + */ + + Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + // Class specific config + Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. + Config.SummonValkyrie = true; // Summon Valkyrie + + /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/manualplay/config/Assassin.js b/d2bs/kolbot/libs/manualplay/config/Assassin.js index 2111c82ac..2ab138f75 100644 --- a/d2bs/kolbot/libs/manualplay/config/Assassin.js +++ b/d2bs/kolbot/libs/manualplay/config/Assassin.js @@ -18,203 +18,203 @@ include("manualplay/MapMode.js"); function LoadConfig() { - MapMode.generalSettings(); - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - // Chicken settings - Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - - /* Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - - // Pickit config. Default folder is kolbot/pickit. - Config.PickitFiles.push("kolton.nip"); - Config.PickitFiles.push("LLD.nip"); - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - - // Public game options - - // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - // LocalChat messages will only be visible on clients running on the same PC - Config.LocalChat.Enabled = false; // enable the LocalChat system - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // MF Switch - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 251; BAD: Config.AttackSkill[1] = -251; - * Don't put LS/DS/WoF/WoI here! Use Config. UseTraps, Config.Traps and Config.BossTraps - */ - - Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - // Class specific config - Config.UseTraps = true; // Set to true to use traps - Config.Traps = [271, 271, 271, 276, 276]; // Skill IDs for traps to be cast on all mosters except act bosses. - Config.BossTraps = [271, 271, 271, 271, 271]; // Skill IDs for traps to be cast on act bosses. - - Config.SummonShadow = "Master"; // 0 = don't summon, 1 or "Warrior" = summon Shadow Warrior, 2 or "Master" = summon Shadow Master - Config.UseFade = true; // Set to true to use Fade prebuff. - Config.UseBoS = false; // Set to true to use Burst of Speed prebuff. TODO: Casting in town + UseFade compatibility - Config.UseVenom = false; // Set to true to use Venom prebuff. Set to false if you don't have the skill and have Arachnid Mesh - it will cause connection drop otherwise. - Config.UseCloakofShadows = true; // Set to true to use Cloak of Shadows while fighting. Useful for blinding regular monsters/minions. - Config.AggressiveCloak = false; // Move into Cloak range or cast if already close - - /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + MapMode.generalSettings(); + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + // Chicken settings + Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + + /* Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + + // Pickit config. Default folder is kolbot/pickit. + Config.PickitFiles.push("kolton.nip"); + Config.PickitFiles.push("LLD.nip"); + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + + // Public game options + + // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + // LocalChat messages will only be visible on clients running on the same PC + Config.LocalChat.Enabled = false; // enable the LocalChat system + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // MF Switch + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 251; BAD: Config.AttackSkill[1] = -251; + * Don't put LS/DS/WoF/WoI here! Use Config. UseTraps, Config.Traps and Config.BossTraps + */ + + Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + // Class specific config + Config.UseTraps = true; // Set to true to use traps + Config.Traps = [271, 271, 271, 276, 276]; // Skill IDs for traps to be cast on all mosters except act bosses. + Config.BossTraps = [271, 271, 271, 271, 271]; // Skill IDs for traps to be cast on act bosses. + + Config.SummonShadow = "Master"; // 0 = don't summon, 1 or "Warrior" = summon Shadow Warrior, 2 or "Master" = summon Shadow Master + Config.UseFade = true; // Set to true to use Fade prebuff. + Config.UseBoS = false; // Set to true to use Burst of Speed prebuff. TODO: Casting in town + UseFade compatibility + Config.UseVenom = false; // Set to true to use Venom prebuff. Set to false if you don't have the skill and have Arachnid Mesh - it will cause connection drop otherwise. + Config.UseCloakofShadows = true; // Set to true to use Cloak of Shadows while fighting. Useful for blinding regular monsters/minions. + Config.AggressiveCloak = false; // Move into Cloak range or cast if already close + + /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/manualplay/config/Barbarian.js b/d2bs/kolbot/libs/manualplay/config/Barbarian.js index 651f00746..58aac3409 100644 --- a/d2bs/kolbot/libs/manualplay/config/Barbarian.js +++ b/d2bs/kolbot/libs/manualplay/config/Barbarian.js @@ -18,192 +18,192 @@ include("manualplay/MapMode.js"); function LoadConfig() { - MapMode.generalSettings(); - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - // Chicken settings - Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - - /* Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - - // Pickit config. Default folder is kolbot/pickit. - Config.PickitFiles.push("kolton.nip"); - Config.PickitFiles.push("LLD.nip"); - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - - // Public game options - - // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - // LocalChat messages will only be visible on clients running on the same PC - Config.LocalChat.Enabled = false; // enable the LocalChat system - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 1; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // MF Switch - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 151; BAD: Config.AttackSkill[1] = -151; - */ - - Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill for bosses. - Config.AttackSkill[2] = -1; // Backup/Immune skill for bosses. - Config.AttackSkill[3] = -1; // Primary skill for others. - Config.AttackSkill[4] = -1; // Backup/Immune skill for others. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - // Class specific config - Config.FindItem = false; // Use Find Item skill on corpses after clearing. - Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills - Config.UseWarcries = true; // use battle orders, battle command, and shout if we have them - - /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + MapMode.generalSettings(); + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + // Chicken settings + Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + + /* Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + + // Pickit config. Default folder is kolbot/pickit. + Config.PickitFiles.push("kolton.nip"); + Config.PickitFiles.push("LLD.nip"); + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + + // Public game options + + // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + // LocalChat messages will only be visible on clients running on the same PC + Config.LocalChat.Enabled = false; // enable the LocalChat system + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 1; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // MF Switch + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 151; BAD: Config.AttackSkill[1] = -151; + */ + + Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill for bosses. + Config.AttackSkill[2] = -1; // Backup/Immune skill for bosses. + Config.AttackSkill[3] = -1; // Primary skill for others. + Config.AttackSkill[4] = -1; // Backup/Immune skill for others. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + // Class specific config + Config.FindItem = false; // Use Find Item skill on corpses after clearing. + Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills + Config.UseWarcries = true; // use battle orders, battle command, and shout if we have them + + /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/manualplay/config/Druid.js b/d2bs/kolbot/libs/manualplay/config/Druid.js index 56782119d..c51606e04 100644 --- a/d2bs/kolbot/libs/manualplay/config/Druid.js +++ b/d2bs/kolbot/libs/manualplay/config/Druid.js @@ -18,196 +18,196 @@ include("manualplay/MapMode.js"); function LoadConfig() { - MapMode.generalSettings(); - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - // Chicken settings - Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - - /* Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - - // Pickit config. Default folder is kolbot/pickit. - Config.PickitFiles.push("kolton.nip"); - Config.PickitFiles.push("LLD.nip"); - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - - // Public game options - - // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - // LocalChat messages will only be visible on clients running on the same PC - Config.LocalChat.Enabled = false; // enable the LocalChat system - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // MF Switch - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 245; BAD: Config.AttackSkill[1] = -245; - */ - - Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - // Class specific config - Config.SummonRaven = false; - Config.SummonAnimal = "Grizzly"; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly - Config.SummonSpirit = "Oak Sage"; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" - Config.SummonVine = "Poison Creeper"; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" - - /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + MapMode.generalSettings(); + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + // Chicken settings + Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + + /* Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + + // Pickit config. Default folder is kolbot/pickit. + Config.PickitFiles.push("kolton.nip"); + Config.PickitFiles.push("LLD.nip"); + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + + // Public game options + + // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + // LocalChat messages will only be visible on clients running on the same PC + Config.LocalChat.Enabled = false; // enable the LocalChat system + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // MF Switch + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 245; BAD: Config.AttackSkill[1] = -245; + */ + + Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + // Class specific config + Config.SummonRaven = false; + Config.SummonAnimal = "Grizzly"; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly + Config.SummonSpirit = "Oak Sage"; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" + Config.SummonVine = "Poison Creeper"; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" + + /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/manualplay/config/Necromancer.js b/d2bs/kolbot/libs/manualplay/config/Necromancer.js index f2a86f5ec..727e69d5b 100644 --- a/d2bs/kolbot/libs/manualplay/config/Necromancer.js +++ b/d2bs/kolbot/libs/manualplay/config/Necromancer.js @@ -18,217 +18,217 @@ include("manualplay/MapMode.js"); function LoadConfig() { - MapMode.generalSettings(); - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - // Chicken settings - Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - - /* Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - - // Pickit config. Default folder is kolbot/pickit. - Config.PickitFiles.push("kolton.nip"); - Config.PickitFiles.push("LLD.nip"); - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - - // Public game options - - // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - // LocalChat messages will only be visible on clients running on the same PC - Config.LocalChat.Enabled = false; // enable the LocalChat system - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // MF Switch - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 84; BAD: Config.AttackSkill[1] = -84; - */ - - Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - // Class specific config - Config.Curse[0] = 0; // Boss curse. Use skill number or set to 0 to disable. - Config.Curse[1] = 0; // Other monsters curse. Use skill number or set to 0 to disable. - - /* Custom curses for monster - * Can use monster name or classid - * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; - * Optional 3rd parameter for spectype, leave blank to use on all - 0x00 Normal Monster - 0x01 Super Unique - 0x02 Champion - 0x04 Boss - 0x08 Minion - Example: Config.CustomCurse = [["HellBovine", 60], [571, 87], ["SkeletonArcher", 71, 0x00]]; - */ - Config.CustomCurse = []; - - Config.ExplodeCorpses = 0; // Explode corpses. Use skill number or 0 to disable. 74 = Corpse Explosion, 83 = Poison Explosion - Config.Golem = "None"; // Golem. 0 or "None" = don't summon, 1 or "Clay" = Clay Golem, 2 or "Blood" = Blood Golem, 3 or "Fire" = Fire Golem - Config.Skeletons = 0; // Number of skeletons to raise. Set to "max" to auto detect, set to 0 to disable. - Config.SkeletonMages = 0; // Number of skeleton mages to raise. Set to "max" to auto detect, set to 0 to disable. - Config.Revives = 0; // Number of revives to raise. Set to "max" to auto detect, set to 0 to disable. - Config.PoisonNovaDelay = 2; // Delay between two Poison Novas in seconds. - Config.ActiveSummon = false; // Raise dead between each attack. If false, it will raise after clearing a spot. - Config.ReviveUnstackable = true; // Revive monsters that can move freely after you teleport. - Config.IronGolemChicken = 30; // Exit game if Iron Golem's life is less or equal to designated percent. - - /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + MapMode.generalSettings(); + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + // Chicken settings + Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + + /* Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + + // Pickit config. Default folder is kolbot/pickit. + Config.PickitFiles.push("kolton.nip"); + Config.PickitFiles.push("LLD.nip"); + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + + // Public game options + + // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + // LocalChat messages will only be visible on clients running on the same PC + Config.LocalChat.Enabled = false; // enable the LocalChat system + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // MF Switch + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 84; BAD: Config.AttackSkill[1] = -84; + */ + + Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + // Class specific config + Config.Curse[0] = 0; // Boss curse. Use skill number or set to 0 to disable. + Config.Curse[1] = 0; // Other monsters curse. Use skill number or set to 0 to disable. + + /* Custom curses for monster + * Can use monster name or classid + * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; + * Optional 3rd parameter for spectype, leave blank to use on all + 0x00 Normal Monster + 0x01 Super Unique + 0x02 Champion + 0x04 Boss + 0x08 Minion + Example: Config.CustomCurse = [["HellBovine", 60], [571, 87], ["SkeletonArcher", 71, 0x00]]; + */ + Config.CustomCurse = []; + + Config.ExplodeCorpses = 0; // Explode corpses. Use skill number or 0 to disable. 74 = Corpse Explosion, 83 = Poison Explosion + Config.Golem = "None"; // Golem. 0 or "None" = don't summon, 1 or "Clay" = Clay Golem, 2 or "Blood" = Blood Golem, 3 or "Fire" = Fire Golem + Config.Skeletons = 0; // Number of skeletons to raise. Set to "max" to auto detect, set to 0 to disable. + Config.SkeletonMages = 0; // Number of skeleton mages to raise. Set to "max" to auto detect, set to 0 to disable. + Config.Revives = 0; // Number of revives to raise. Set to "max" to auto detect, set to 0 to disable. + Config.PoisonNovaDelay = 2; // Delay between two Poison Novas in seconds. + Config.ActiveSummon = false; // Raise dead between each attack. If false, it will raise after clearing a spot. + Config.ReviveUnstackable = true; // Revive monsters that can move freely after you teleport. + Config.IronGolemChicken = 30; // Exit game if Iron Golem's life is less or equal to designated percent. + + /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/manualplay/config/Paladin.js b/d2bs/kolbot/libs/manualplay/config/Paladin.js index 2f7c09589..cffec69f3 100644 --- a/d2bs/kolbot/libs/manualplay/config/Paladin.js +++ b/d2bs/kolbot/libs/manualplay/config/Paladin.js @@ -18,196 +18,196 @@ include("manualplay/MapMode.js"); function LoadConfig() { - MapMode.generalSettings(); - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - // Chicken settings - Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - - /* Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - - // Pickit config. Default folder is kolbot/pickit. - Config.PickitFiles.push("kolton.nip"); - Config.PickitFiles.push("LLD.nip"); - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - - // Public game options - - // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - // LocalChat messages will only be visible on clients running on the same PC - Config.LocalChat.Enabled = false; // enable the LocalChat system - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // MF Switch - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 96; BAD: Config.AttackSkill[1] = -96; - */ - - Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - // Class specific config - Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. - Config.Vigor = true; // Swith to Vigor when running - Config.Charge = true; // Use Charge when running - Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] - - /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + MapMode.generalSettings(); + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + // Chicken settings + Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + + /* Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + + // Pickit config. Default folder is kolbot/pickit. + Config.PickitFiles.push("kolton.nip"); + Config.PickitFiles.push("LLD.nip"); + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + + // Public game options + + // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + // LocalChat messages will only be visible on clients running on the same PC + Config.LocalChat.Enabled = false; // enable the LocalChat system + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // MF Switch + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 96; BAD: Config.AttackSkill[1] = -96; + */ + + Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + // Class specific config + Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. + Config.Vigor = true; // Swith to Vigor when running + Config.Charge = true; // Use Charge when running + Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] + + /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/manualplay/config/Sorceress.js b/d2bs/kolbot/libs/manualplay/config/Sorceress.js index 828eee6ba..890cec220 100644 --- a/d2bs/kolbot/libs/manualplay/config/Sorceress.js +++ b/d2bs/kolbot/libs/manualplay/config/Sorceress.js @@ -18,198 +18,198 @@ include("manualplay/MapMode.js"); function LoadConfig() { - MapMode.generalSettings(); - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - // Chicken settings - Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - - /* Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "mp", "mp", "rv"]; - - // Pickit config. Default folder is kolbot/pickit. - Config.PickitFiles.push("kolton.nip"); - Config.PickitFiles.push("LLD.nip"); - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - - // Public game options - Config.QuitList = ["unfairsocks"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = [15, 30]; - - // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - // LocalChat messages will only be visible on clients running on the same PC - Config.LocalChat.Enabled = false; // enable the LocalChat system - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // MF Switch - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 56; BAD: Config.AttackSkill[1] = -56; - */ - - Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - // Class specific config - Config.CastStatic = 60; // Cast static until the target is at designated life percent. 100 = disabled. - Config.StaticList = []; // List of monster NAMES or CLASSIDS to static. Example: Config.StaticList = ["Andariel", 243]; - Config.UseTelekinesis = true; // Use telekinesis on units that allow it. Example: Shrines, Waypoints, Chests, and Portals - - /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + MapMode.generalSettings(); + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + // Chicken settings + Config.LifeChicken = 0; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + + /* Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "mp", "mp", "rv"]; + + // Pickit config. Default folder is kolbot/pickit. + Config.PickitFiles.push("kolton.nip"); + Config.PickitFiles.push("LLD.nip"); + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + + // Public game options + Config.QuitList = ["unfairsocks"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = [15, 30]; + + // If LocalChat is enabled, chat can be sent via 'sendCopyData' instead of BNET + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + // LocalChat messages will only be visible on clients running on the same PC + Config.LocalChat.Enabled = false; // enable the LocalChat system + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 0; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // If Config.Leader is set, the bot will only accept invites from leader. If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // MF Switch + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. GOOD: Config.AttackSkill[1] = 56; BAD: Config.AttackSkill[1] = -56; + */ + + Config.PrimarySlot = -1; // Set to use specific weapon slot as primary weapon slot: -1 = disabled, 0 = slot I, 1 = slot II + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // Dodge only if HP percent is less than or equal to Config.DodgeHP. 100 = always dodge. + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.TeleStomp = false; // Use merc to attack bosses if they're immune to attacks, but not to physical damage + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + // Class specific config + Config.CastStatic = 60; // Cast static until the target is at designated life percent. 100 = disabled. + Config.StaticList = []; // List of monster NAMES or CLASSIDS to static. Example: Config.StaticList = ["Andariel", 243]; + Config.UseTelekinesis = true; // Use telekinesis on units that allow it. Example: Shrines, Waypoints, Chests, and Portals + + /* AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js index bd5e7c5bd..892c4e001 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js @@ -55,11 +55,11 @@ const ActionHooks = { ].some((flag) => getUIFlag(flag)), /** - * Set action based on key input - * @param {number} keycode - * @returns {void} - * @todo this would probably be better as pushing to an action stack and implementing a timeout to prevent spamming the same action - */ + * Set action based on key input + * @param {number} keycode + * @returns {void} + * @todo this would probably be better as pushing to an action stack and implementing a timeout to prevent spamming the same action + */ event: function (keycode) { if ([sdk.keys.Shift, sdk.keys.Alt].some(k => k === keycode)) { return; @@ -119,7 +119,7 @@ const ActionHooks = { } else { ItemHooks.pickitEnabled = true; ItemHooks.flush(); - + if (!Hooks.saidMessage) { showConsole(); print("ÿc 4; i--) { curr = currExits.shift(); @@ -725,9 +725,9 @@ const ActionHooks = { }, /** - * @param {number} seal - * @returns {{ x: number, y: number, area: number }} - */ + * @param {number} seal + * @returns {{ x: number, y: number, area: number }} + */ getDiabloSeals: function (seal) { try { let unit = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); diff --git a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js index fdcf9a2e1..8d8a3385c 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js @@ -24,10 +24,10 @@ const ItemHooks = { itemColorCode: [], /** - * @param {number} id - classID of item - * @param {string} setName - * @param {string} uniqueName - */ + * @param {number} id - classID of item + * @param {string} setName + * @param {string} uniqueName + */ addToCodeByClassIdAndQuality: function (id, setName = "", uniqueName = "") { if (!id || (!setName && !uniqueName)) return; // create our map structure @@ -66,7 +66,7 @@ const ItemHooks = { try { do { if (item.area === ActionHooks.currArea && item.onGroundOrDropping - && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && !this.ignoreItemTypes.includes(item.itemType)))) { + && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && !this.ignoreItemTypes.includes(item.itemType)))) { if (this.pickitEnabled) { if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(Pickit.checkItem(item).result) === -1) { !this.getHook(item) && this.add(item); @@ -95,9 +95,9 @@ const ItemHooks = { }, /** - * @param {ItemUnit} item - * @returns {string} - */ + * @param {ItemUnit} item + * @returns {string} + */ getName: function (item) { let abbr = item.name.split(" "); let abbrName = ""; @@ -114,10 +114,10 @@ const ItemHooks = { }, /** - * @description Create a new hook for a item with custom color and code based on type/quality/classid - * @param {ItemUnit} item - * @todo maybe make class wrappers for hooks and turn the hook array into a map? - */ + * @description Create a new hook for a item with custom color and code based on type/quality/classid + * @param {ItemUnit} item + * @todo maybe make class wrappers for hooks and turn the hook array into a map? + */ newHook: function (item) { let color = 0, code = "", arr = [], name = [], vector = []; let eth = (item.ethereal ? "Eth: " : ""); @@ -171,14 +171,14 @@ const ItemHooks = { case sdk.items.Ring: case sdk.items.Amulet: code += item.name + "(" + item.ilvl + ")"; - + break; default: { let check = this.codeByIdAndQuality.get(item.classid); code += ((check && check.get(item.quality)) || item.name); } - + break; } @@ -192,7 +192,7 @@ const ItemHooks = { code += this.getName(item); code += "(" + item.ilvl + ")"; } - + break; } @@ -209,9 +209,9 @@ const ItemHooks = { }, /** - * Add new item hook to our hook array - * @param {ItemUnit} item - */ + * Add new item hook to our hook array + * @param {ItemUnit} item + */ add: function (item) { if (item === undefined || !item.classid) { return; @@ -229,10 +229,10 @@ const ItemHooks = { }, /** - * Get item hook if it exists based on item parameters gid - * @param {ItemUnit} item - * @returns {{ item: ItemUnit, area: number, hook: Line, name: Text, vector: Line} | false} - */ + * Get item hook if it exists based on item parameters gid + * @param {ItemUnit} item + * @returns {{ item: ItemUnit, area: number, hook: Line, name: Text, vector: Line} | false} + */ getHook: function (item) { for (let i = 0; i < this.hooks.length; i++) { if (this.hooks[i].item.gid === item.gid) { @@ -244,16 +244,16 @@ const ItemHooks = { }, /** - * @param {ItemUnit} item - * @returns {boolean} - */ + * @param {ItemUnit} item + * @returns {boolean} + */ remove: function (item) { for (let i = 0; i < this.hooks.length; i++) { if (this.hooks[i].item.gid === item.gid) { for (let j = 0; j < this.hooks[i].hook.length; j++) { this.hooks[i].hook[j].remove(); } - + this.hooks[i].name[0] && this.hooks[i].name[0].remove(); this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); this.hooks.splice(i, 1); diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js index efa7ab48a..fb2a22bbc 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js @@ -73,7 +73,7 @@ const TextHooks = { function sortHooks(h1, h2) { return Math.abs(h1.hook.y - y) - Math.abs(h2.hook.y - y); } - + if (click === 0) { TextHooks.statusHooks.sort(sortHooks); @@ -89,7 +89,7 @@ const TextHooks = { add: function (name, hookArr = []) { let orginalLen = hookArr.length; - + switch (name) { case "credits": this.hooks.push({ diff --git a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js b/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js index 57d2094d8..5b8a53bb8 100644 --- a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js @@ -30,7 +30,7 @@ Misc.openRedPortal = function (portalID) { tpTome.forEach(function (book) { while (book.getStat(sdk.stats.Quantity) < 20) { scroll = npc.getItem(sdk.items.ScrollofTownPortal); - + if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { scroll.buy(); } else { @@ -148,7 +148,7 @@ Misc.dropItems = function (fromLoc) { let item = items.shift(); if (item.classid === sdk.quest.item.Cube - || (item.isEquippedCharm && Storage.Inventory.IsLocked(item, Config.Inventory))) { + || (item.isEquippedCharm && Storage.Inventory.IsLocked(item, Config.Inventory))) { continue; } else { item.drop(); diff --git a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js index c20e33caf..43e53bc7f 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js @@ -41,15 +41,15 @@ Pather.changeAct = function (act) { } !me.inTown && Town.goToTown(); - + if (npc) { npcUnit = Game.getNPC(NPC[npc]); wp = Game.getObject("waypoint"); if (Pather.accessToAct(act) - && ((wp && !npcUnit) - || (wp && npcUnit && getDistance(me, wp) < getDistance(me, npcUnit)) - || (Town.getDistance("waypoint") < Town.getDistance(NPC[npc])))) { + && ((wp && !npcUnit) + || (wp && npcUnit && getDistance(me, wp) < getDistance(me, npcUnit)) + || (Town.getDistance("waypoint") < Town.getDistance(NPC[npc])))) { useWp = true; } } else { @@ -299,7 +299,7 @@ Pather.moveTo = function (x, y, retry, clearPath, pop) { // Abort if dead if (me.dead || Pather.stop) { Pather.stop = false; // Reset value - + return false; } @@ -310,8 +310,8 @@ Pather.moveTo = function (x, y, retry, clearPath, pop) { node = path.shift(); /* Right now getPath's first node is our own position so it's not necessary to take it into account - This will be removed if getPath changes - */ + This will be removed if getPath changes + */ if (getDistance(me, node) > 2) { 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? @@ -336,7 +336,7 @@ Pather.moveTo = function (x, y, retry, clearPath, pop) { if (Pather.stop) { continue; // stops on next interation } - + if (!me.inTown) { if (this.recursion) { this.recursion = false; diff --git a/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js b/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js index 306a1e88b..28b823db8 100644 --- a/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/TownOverrides.js @@ -13,16 +13,16 @@ Town.stash = function (stashGold = true) { let items = me.getItemsEx() .filter(function (item) { return item.isInInventory - && !(item.isEquippedCharm && (item.unique || Storage.Inventory.IsLocked(item, Config.Inventory))); + && !(item.isEquippedCharm && (item.unique || Storage.Inventory.IsLocked(item, Config.Inventory))); }) .sort(function (a, b) { if ((a.itemType >= sdk.items.type.Amethyst - && a.itemType <= sdk.items.type.Skull) || a.itemType === sdk.items.type.Rune || a.unique) { + && a.itemType <= sdk.items.type.Skull) || a.itemType === sdk.items.type.Rune || a.unique) { return -1; } if ((b.itemType >= sdk.items.type.Amethyst - && b.itemType <= sdk.items.type.Skull) || b.itemType === sdk.items.type.Rune || b.unique) { + && b.itemType <= sdk.items.type.Skull) || b.itemType === sdk.items.type.Rune || b.unique) { return 1; } diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 02a681b0e..15c2a621f 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -23,7 +23,7 @@ MapMode.include(); function main() { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); - + console.log("ÿc9MapHelper loaded"); let obj = { type: false, dest: false, action: false }; @@ -85,7 +85,7 @@ function main() { if (fail % 5 === 0 && !getScript("libs/manualplay/threads/mapthread.js")) { print("MapThread shut down, exiting MapHelper"); - + return false; } } @@ -115,7 +115,7 @@ function main() { try { let temp = JSON.parse(action); temp && Object.assign(obj, temp); - + addEventListener("keyup", Pather.stopEvent); this.togglePickThread(); @@ -146,7 +146,7 @@ function main() { break; case "unit": if (me.inArea(sdk.areas.MooMooFarm) - || (me.inArea(sdk.areas.DurielsLair) && Misc.talkToTyrael())) { + || (me.inArea(sdk.areas.DurielsLair) && Misc.talkToTyrael())) { break; } @@ -192,7 +192,7 @@ function main() { case "portal": if (obj.dest === sdk.areas.WorldstoneChamber && Game.getMonster(sdk.monsters.ThroneBaal)) { me.overhead("Can't enter Worldstone Chamber yet. Baal still in area"); - + break; } else if (obj.dest === sdk.areas.WorldstoneChamber && !Game.getMonster(sdk.monsters.ThroneBaal)) { redPortal = Game.getObject(sdk.objects.WorldstonePortal); @@ -256,7 +256,7 @@ function main() { break; default: Pather.usePortal(obj.dest); - + break; } @@ -383,7 +383,7 @@ function main() { switch (obj.action) { case "invo": Misc.dropItems(sdk.storage.Inventory); - + break; case "stash": Misc.dropItems(sdk.storage.Stash); @@ -396,7 +396,7 @@ function main() { switch (obj.action) { case "thawing": Town.buyPots(10, "Thawing", true, true); - + break; case "antidote": Town.buyPots(10, "Antidote", true, true); diff --git a/d2bs/kolbot/libs/manualplay/threads/MapThread.js b/d2bs/kolbot/libs/manualplay/threads/MapThread.js index 323a9bc99..a3871b620 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapThread.js @@ -35,7 +35,7 @@ const Hooks = { init: function () { let files = dopen("libs/manualplay/hooks/").getFiles(); - + Array.isArray(files) && files .filter(file => file.endsWith(".js")) .forEach(function (x) { @@ -150,7 +150,7 @@ function main() { if (!Hooks.userAddon) { return true; } - + UnitInfo.createInfo(Game.getSelectedUnit()); return true; @@ -176,11 +176,11 @@ function main() { if (this.revealedAreas.indexOf(area) === -1) { delay(500); - + if (!getRoom()) { return; } - + revealLevel(true); this.revealedAreas.push(area); } @@ -296,7 +296,7 @@ function main() { let hideFlagFound = false; this.revealArea(me.area); - + for (let i = 0; i < hideFlags.length; i++) { if (getUIFlag(hideFlags[i])) { Hooks.flush(hideFlags[i]); diff --git a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js index 54bd76e43..b46b986d6 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js @@ -25,7 +25,7 @@ MapMode.include(); function main() { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); - + console.log("ÿc9MapToolsThread loaded"); let ironGolem, debugInfo = { area: 0, currScript: "no entry" }; @@ -105,7 +105,7 @@ function main() { Precast.doPrecast(true); Skill.setSkill(preSkill, sdk.skills.hand.Right); } - + break; } }; diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index 06d0ce4c1..9d3f37b38 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -4,46 +4,46 @@ */ (function (module) { /** - * Not callable as a function - * @constructor - * @method Control.click(targetx, targety) - * @method Control.setText(text) - * @method Control.getText(text) - * @param {number} type - * @param {number} x - * @param {number} y - * @param {number} xsize - * @param {number} ysize - */ + * Not callable as a function + * @constructor + * @method Control.click(targetx, targety) + * @method Control.setText(text) + * @method Control.getText(text) + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + */ function Control(type, x, y, xsize, ysize) { /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.type = type; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.x = x; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.y = y; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.xsize = xsize; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.ysize = ysize; return new Proxy(this, { @@ -252,7 +252,7 @@ Control.LobbyChannelChar9 = new Control(sdk.controls.Button, 580, 591, 60, 100); Control.LobbyChannelChar10 = new Control(sdk.controls.Button, 640, 591, 60, 100); } - + // Single Player Difficulty Controls { Control.HellSP = new Control(-1, 264, 383, 272, 35); diff --git a/d2bs/kolbot/libs/modules/CopyData.js b/d2bs/kolbot/libs/modules/CopyData.js index 1f4fd36ec..583b193df 100644 --- a/d2bs/kolbot/libs/modules/CopyData.js +++ b/d2bs/kolbot/libs/modules/CopyData.js @@ -17,64 +17,64 @@ } }([].filter.constructor("return this")(), function() { /** - * @class - * @classdesc A class for creating and sending copy data packets. - * @property {number} _mode - Defaults to 0, works for most D2Bot functions - * @property {number | string} _handle - Defaults to value of D2Bot.handle, works for any D2Bot - * functions that act on ourselves - * @example Request a game from "scl-sorc-001" profile - * new CopyData().handle("scl-sorc-001").mode(3).send(); - * @example Start mule profile "mule" - * new CopyData().data("start", ["mule"]).send(); - */ + * @class + * @classdesc A class for creating and sending copy data packets. + * @property {number} _mode - Defaults to 0, works for most D2Bot functions + * @property {number | string} _handle - Defaults to value of D2Bot.handle, works for any D2Bot + * functions that act on ourselves + * @example Request a game from "scl-sorc-001" profile + * new CopyData().handle("scl-sorc-001").mode(3).send(); + * @example Start mule profile "mule" + * new CopyData().data("start", ["mule"]).send(); + */ function CopyData() { if (this.__proto__.constructor !== CopyData) throw new Error("CopyData must be called with 'new' operator!"); /** - * @private - * @type {string | number} - The handle to send the copy data to. - */ + * @private + * @type {string | number} - The handle to send the copy data to. + */ this._handle = D2Bot.handle || me.profile; /** - * @private - * @type {number} - The mode of the copy data packet. - */ + * @private + * @type {number} - The mode of the copy data packet. + */ this._mode = 0; /** - * @private - * @type {string} - The data to send in the copy data - */ + * @private + * @type {string} - The data to send in the copy data + */ this._data = null; } /** - * - D2Bot.handle is for any functions that act on ourselves - * - Otherwise it is the D2Bot# profile name of the profile to act upon - * @param {string | number} handle - The handle or profile to send the copy data to. - */ + * - D2Bot.handle is for any functions that act on ourselves + * - Otherwise it is the D2Bot# profile name of the profile to act upon + * @param {string | number} handle - The handle or profile to send the copy data to. + */ CopyData.prototype.handle = function (handle) { this._handle = handle; return this; }; /** - * - 0 is for most functions, and the default value set - * - 1 is for joinMe - * - 3 is for requestGame - * - 0xbbbb is for heartBeat - * @param {number} mode - The mode of the copy data packet. - */ + * - 0 is for most functions, and the default value set + * - 1 is for joinMe + * - 3 is for requestGame + * - 0xbbbb is for heartBeat + * @param {number} mode - The mode of the copy data packet. + */ CopyData.prototype.mode = function (mode) { this._mode = mode; return this; }; /** - * @param {string} [func] - The function to call from D2Bot# - * @param {string[]} [args] - The additonal info needed for the function call - */ + * @param {string} [func] - The function to call from D2Bot# + * @param {string[]} [args] - The additonal info needed for the function call + */ CopyData.prototype.data = function (func = "", args = []) { if (func.includes("Item") || func === "printToConsole" || (func === "setTag" && typeof args[0] === "object")) { args[0] = JSON.stringify(args[0]); @@ -88,9 +88,9 @@ }; /** - * CopyData.data works for functions call d2bot# functions but what about gerneral use? - * @todo handle passing custom data obj - */ + * CopyData.data works for functions call d2bot# functions but what about gerneral use? + * @todo handle passing custom data obj + */ CopyData.prototype.send = function () { // check that data is set diff --git a/d2bs/kolbot/libs/modules/Deltas.js b/d2bs/kolbot/libs/modules/Deltas.js index cc831cd77..7de278c81 100644 --- a/d2bs/kolbot/libs/modules/Deltas.js +++ b/d2bs/kolbot/libs/modules/Deltas.js @@ -8,7 +8,7 @@ let instances = 0; /** @constructor - * @class Delta */ + * @class Delta */ module.exports = function (trackers) { let active = true; this.values = (Array.isArray(trackers) && (Array.isArray(trackers.first()) && trackers || [trackers])) || []; diff --git a/d2bs/kolbot/libs/modules/Guard.js b/d2bs/kolbot/libs/modules/Guard.js index aa843e78a..0fcd11e6a 100644 --- a/d2bs/kolbot/libs/modules/Guard.js +++ b/d2bs/kolbot/libs/modules/Guard.js @@ -19,9 +19,9 @@ ); /** - * @constructor - * @param {function():string} callback - */ + * @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); diff --git a/d2bs/kolbot/libs/modules/Promise.js b/d2bs/kolbot/libs/modules/Promise.js index 2d4c2b91d..71a17a141 100644 --- a/d2bs/kolbot/libs/modules/Promise.js +++ b/d2bs/kolbot/libs/modules/Promise.js @@ -7,10 +7,10 @@ (function (module, require) { const Worker = require("Worker"); /** - * - * @param {function({resolve},{reject}):boolean} callback - * @constructor - */ + * + * @param {function({resolve},{reject}):boolean} callback + * @constructor + */ const Promise = module.exports = function (callback) { typeof Promise.__promiseCounter === "undefined" && (Promise.__promiseCounter = 0); @@ -84,9 +84,9 @@ }; /** - * @description wait for an array of promises to be ran. - * @param promises Array - */ + * @description wait for an array of promises to be ran. + * @param promises Array + */ Promise.all = function (promises) { while (promises.some(x => !x.stopped)) { delay(); diff --git a/d2bs/kolbot/libs/modules/UnitInfo.js b/d2bs/kolbot/libs/modules/UnitInfo.js index e1eb5c744..115a298af 100644 --- a/d2bs/kolbot/libs/modules/UnitInfo.js +++ b/d2bs/kolbot/libs/modules/UnitInfo.js @@ -19,45 +19,45 @@ include("core/prototypes.js"); } }(this, function () { /** - * @constructor - */ + * @constructor + */ function UnitInfo () { /** - * screen coordinate for info box - * @private - * @type {number} - */ + * screen coordinate for info box + * @private + * @type {number} + */ this.x = 200; /** - * screen coordinate for info box - * @private - * @type {number} - */ + * screen coordinate for info box + * @private + * @type {number} + */ this.y = 250; /** - * @private - * @type {any[]} - */ + * @private + * @type {any[]} + */ this.hooks = []; /** - * @private - * @type {number | null} - */ + * @private + * @type {number | null} + */ this.currentGid = null; /** - * @private - * @type {boolean} - */ + * @private + * @type {boolean} + */ this.cleared = true; /** - * @private - * @type {{ x: number, y: number }} - */ + * @private + * @type {{ x: number, y: number }} + */ this.resfix = { x: (me.screensize ? 0 : -160), y: (me.screensize ? 0 : -120) @@ -65,9 +65,9 @@ include("core/prototypes.js"); } /** - * Create info based on unit type - * @param {Unit} unit - */ + * Create info based on unit type + * @param {Unit} unit + */ UnitInfo.prototype.createInfo = function (unit) { if (typeof unit === "undefined") { this.remove(); @@ -103,8 +103,8 @@ include("core/prototypes.js"); }; /** - * Check that selected unit is still valid - */ + * Check that selected unit is still valid + */ UnitInfo.prototype.check = function () { // make sure things got cleaned up properly if we are supposedly cleared if (this.hooks.length === 0 && this.cleared) return; @@ -117,9 +117,9 @@ include("core/prototypes.js"); }; /** - * @private - * @param {Player} unit - */ + * @private + * @param {Player} unit + */ UnitInfo.prototype.playerInfo = function (unit) { let string; let frameXsize = 0; @@ -155,9 +155,9 @@ include("core/prototypes.js"); }; /** - * @private - * @param {Monster} unit - */ + * @private + * @param {Monster} unit + */ UnitInfo.prototype.monsterInfo = function (unit) { let frameYsize = 125; @@ -178,9 +178,9 @@ include("core/prototypes.js"); }; /** - * @private - * @param {ItemUnit} unit - */ + * @private + * @param {ItemUnit} unit + */ UnitInfo.prototype.itemInfo = function (unit) { let xpos = 60; let ypos = (me.getMerc() ? 80 : 20) + (-1 * this.resfix.y); @@ -224,9 +224,9 @@ include("core/prototypes.js"); }; /** - * @private - * @param {ObjectUnit} unit - */ + * @private + * @param {ObjectUnit} unit + */ UnitInfo.prototype.objectInfo = function (unit) { let frameYsize = 35; @@ -245,7 +245,7 @@ include("core/prototypes.js"); this.hooks.push(new Frame(this.x, this.y - 15, 120, frameYsize, 2)); this.hooks[this.hooks.length - 2].zorder = 0; }; - + UnitInfo.prototype.remove = function () { while (this.hooks.length > 0) { this.hooks.shift().remove(); diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index 96bc2fda4..98386ec49 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -55,9 +55,9 @@ }; /** - * - * @param {function({Worker}):boolean} callback - */ + * + * @param {function({Worker}):boolean} callback + */ this.runInBackground = new Proxy({ processes: {} }, { set: function (target, name, callback) { if (target.processes.hasOwnProperty(name)) { diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 80640326d..dc626b1e5 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -19,8 +19,8 @@ }(this, function () { "use strict"; /** - * @exports sdk - */ + * @exports sdk + */ const sdk = { waypoints: { Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539], @@ -85,12 +85,12 @@ } }, /** - * @notes - * - I get cursortype 3 when I swap an item that is equipped, but I get 4 if I just unequip an item - * - I get cursortype 3 if I pick an item from my inventory then hover it over another item - * - I get cursortype 4 if I pick and item from my inventory and don't hover it over another item - * - If I then hover it over an item it turns to 3 then stays 3 - */ + * @notes + * - I get cursortype 3 when I swap an item that is equipped, but I get 4 if I just unequip an item + * - I get cursortype 3 if I pick an item from my inventory then hover it over another item + * - I get cursortype 4 if I pick and item from my inventory and don't hover it over another item + * - If I then hover it over an item it turns to 3 then stays 3 + */ cursortype: { Empty: 1, ItemOnUnitHover: 3, // see notes @@ -2807,7 +2807,7 @@ Topaz: 101, Skull: 102, }, - + // Weapons "HandAxe": 0, "Axe": 1, diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index 297f4ed12..f2b2dbfa0 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -156,14 +156,14 @@ includeIfNotIncluded("oog/DataFile.js"); }, /** - * Profile to profile communication - * @param {string} profile - * @param {string} gameName - * @param {number} gameCount - * @param {string} gamePass - * @param {string} isUp - * @param {number} delay - */ + * Profile to profile communication + * @param {string} profile + * @param {string} gameName + * @param {number} gameCount + * @param {string} gamePass + * @param {string} isUp + * @param {number} delay + */ joinMe: function (profile, gameName, gameCount, gamePass, isUp, delay) { let obj = { gameName: (gameName + gameCount).toLowerCase(), diff --git a/d2bs/kolbot/libs/oog/DataFile.js b/d2bs/kolbot/libs/oog/DataFile.js index eec8a2eca..5c74a110b 100644 --- a/d2bs/kolbot/libs/oog/DataFile.js +++ b/d2bs/kolbot/libs/oog/DataFile.js @@ -41,7 +41,7 @@ includeIfNotIncluded("oog/FileAction.js"); getObj: function () { !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); - + let obj; let string = FileAction.read("data/" + me.profile + ".json"); diff --git a/d2bs/kolbot/libs/oog/ShitList.js b/d2bs/kolbot/libs/oog/ShitList.js index 5afbdb0b2..76991e04c 100644 --- a/d2bs/kolbot/libs/oog/ShitList.js +++ b/d2bs/kolbot/libs/oog/ShitList.js @@ -41,7 +41,7 @@ const ShitList = { read: function () { !FileTools.exists("shitlist.json") && this.create(); - + let obj = this.getObj(); return obj.shitlist; diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 766baf15b..d5db83007 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -73,7 +73,7 @@ function BaalHelper() { const chatEvent = function (nick, msg) { if (nick === Config.Leader) { if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") - || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { + || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { quitFlag = true; } } @@ -114,7 +114,7 @@ function BaalHelper() { Precast.doPrecast(false); Attack.clear(15); Common.Baal.clearThrone(); - + if (!Common.Baal.clearWaves()) { throw new Error("Couldn't clear baal waves"); } diff --git a/d2bs/kolbot/libs/scripts/BattleOrders.js b/d2bs/kolbot/libs/scripts/BattleOrders.js index e520286e2..e9b484425 100644 --- a/d2bs/kolbot/libs/scripts/BattleOrders.js +++ b/d2bs/kolbot/libs/scripts/BattleOrders.js @@ -96,7 +96,7 @@ function BattleOrders () { // more players might be showing up, give a moment and lets wait until the nearby player count is static let nearPlayers = 0; let tick = getTickCount(); - + // if we haven't already given a bo, lets wait to see if more players show up if (!BattleOrders.gaveBo) { nearPlayers = Misc.getNearbyPlayerCount(); diff --git a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js index cb0562cc3..71c15592b 100644 --- a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js +++ b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js @@ -11,7 +11,7 @@ function BattlemaidSarina() { Precast.doPrecast(true); if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { throw new Error("Failed to move near Sarina"); } diff --git a/d2bs/kolbot/libs/scripts/BoBarbHelper.js b/d2bs/kolbot/libs/scripts/BoBarbHelper.js index 9aaab9302..a0af3ae64 100644 --- a/d2bs/kolbot/libs/scripts/BoBarbHelper.js +++ b/d2bs/kolbot/libs/scripts/BoBarbHelper.js @@ -21,10 +21,10 @@ function BoBarbHelper () { const shouldBuff = unit => ( Misc.inMyParty(unit) && - getDistance(me, unit) < 10 && - unit.name !== me.name && - !unit.dead && - !unit.inTown + getDistance(me, unit) < 10 && + unit.name !== me.name && + !unit.dead && + !unit.inTown ); const giveBuff = () => { @@ -71,7 +71,7 @@ function BoBarbHelper () { shouldHealMana(townLowMana) && Town.initNPC("Heal", "heal"); Town.heal(); // in case our life is low as well - + try { Pather.useWaypoint(Config.BoBarbHelper.Wp); } catch (e) { diff --git a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js index 4d6ec1169..a57f24e1a 100644 --- a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js +++ b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js @@ -16,39 +16,39 @@ function ClassicChaosAssistant() { switch (key) { case sdk.keys.Numpad1: stargo = true; - + break; case sdk.keys.Numpad2: infgo = true; - + break; case sdk.keys.Numpad3: infseal = true; - + break; case sdk.keys.Numpad4: seisgo = true; - + break; case sdk.keys.Numpad5: seisseal = true; - + break; case sdk.keys.Numpad6: vizgo = true; - + break; case sdk.keys.Numpad7: vizseal = true; - + break; case sdk.keys.Numpad8: // (Open last seal, teleport to star and pickup for 30 seconds) diablopickup = true; - + break; case sdk.keys.Numpad9: // (Pickup at current location) normalpickup = true; - + break; default: break; @@ -124,7 +124,7 @@ function ClassicChaosAssistant() { break; } - + delay(10); } } diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index c2a8ae914..291e4a245 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -8,14 +8,14 @@ function ControlBot() { const startTime = getTickCount(); - + /** - * @type {Object.} - */ + * @type {Object.} + */ const cmdNicks = {}; /** - * @type {Object.} - */ + * @type {Object.} + */ const wpNicks = {}; let command, nick; @@ -72,9 +72,9 @@ function ControlBot() { } /** - * @param {string} nick - * @returns {boolean} - */ + * @param {string} nick + * @returns {boolean} + */ const enchant = function (nick) { if (!Config.ControlBot.Chant.Enchant) return false; @@ -142,9 +142,9 @@ function ControlBot() { }; /** - * @param {string} nick - * @returns {boolean} - */ + * @param {string} nick + * @returns {boolean} + */ const bo = function (nick) { if (!Config.ControlBot.Bo) return false; @@ -198,8 +198,8 @@ function ControlBot() { }; /** - * @type {Map 4) { @@ -416,17 +416,17 @@ function ControlBot() { }; /** - * @param {string} nick - * @returns {void} - */ + * @param {string} nick + * @returns {void} + */ const addWpNick = function (nick) { wpNicks[nick] = { timer: getTickCount(), requests: 0 }; }; /** - * @param {string} nick - * @returns {boolean} - */ + * @param {string} nick + * @returns {boolean} + */ const giveWps = function (nick) { if (!Config.ControlBot.Wps.GiveWps) return false; if (!Misc.inMyParty(nick)) { @@ -533,13 +533,13 @@ function ControlBot() { }; /** - * @param {string} command - * @returns {boolean} - */ + * @param {string} command + * @returns {boolean} + */ const floodCheck = function (command) { if (!command || command.length < 2) return false; let [cmd, nick] = command; - + // ignore overhead messages if (!nick) return true; // ignore messages not related to our commands @@ -580,10 +580,10 @@ function ControlBot() { }; /** - * @param {string} nick - * @param {string} msg - * @returns {boolean} - */ + * @param {string} nick + * @param {string} msg + * @returns {boolean} + */ function chatEvent(nick, msg) { if (shitList.includes(nick)) { say("No commands for the shitlisted."); diff --git a/d2bs/kolbot/libs/scripts/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js index 6d80632ce..e7b3387bb 100644 --- a/d2bs/kolbot/libs/scripts/Corpsefire.js +++ b/d2bs/kolbot/libs/scripts/Corpsefire.js @@ -11,7 +11,7 @@ function Corpsefire() { Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true)) { throw new Error("Failed to move to Corpsefire"); } diff --git a/d2bs/kolbot/libs/scripts/Cows.js b/d2bs/kolbot/libs/scripts/Cows.js index 61fa57bbf..c70b89cc0 100644 --- a/d2bs/kolbot/libs/scripts/Cows.js +++ b/d2bs/kolbot/libs/scripts/Cows.js @@ -7,7 +7,7 @@ function Cows() { include("core/Common/Cows.js"); - + const getLeg = function () { if (me.wirtsleg) return me.wirtsleg; @@ -118,7 +118,7 @@ function Cows() { if (Config.Cows.DontMakePortal) throw new Error("NOT PORTAL MAKER"); if (!me.tristram) throw new Error("Cain quest incomplete"); if (me.cows) throw new Error("Already killed the Cow King."); - + let leg = getLeg(); let tome = getTome(); openPortal(leg, tome); diff --git a/d2bs/kolbot/libs/scripts/Crafting.js b/d2bs/kolbot/libs/scripts/Crafting.js index fea83f9ca..a114e32b3 100644 --- a/d2bs/kolbot/libs/scripts/Crafting.js +++ b/d2bs/kolbot/libs/scripts/Crafting.js @@ -173,7 +173,7 @@ function updateInfo() { // This is not a perfect check, it might not handle every case for (let j = 0; j < items.length; j += 1) { if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list - && AutoMule.cubingIngredient(items[j])) { // Item is a valid Cubing ingredient + && AutoMule.cubingIngredient(items[j])) { // Item is a valid Cubing ingredient print("Base found: " + items[j].classid); info.Sets[i].Enabled = true; @@ -194,7 +194,7 @@ function updateInfo() { // This is not a perfect check, it might not handle every case for (let j = 0; j < items.length; j += 1) { if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list - && runewordIngredient(items[j])) { // Item is a valid Runeword ingredient + && runewordIngredient(items[j])) { // Item is a valid Runeword ingredient print("Base found: " + items[j].classid); info.Sets[i].Enabled = true; @@ -222,7 +222,7 @@ function runewordIngredient(item) { for (let i = 0; i < Config.Runewords.length; i += 1) { let base = (Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) - || Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true)); + || Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true)); base && baseGids.push(base.gid); } @@ -350,9 +350,9 @@ function shopStuff(npcId, classids, amount) { if (items.length) { for (let i = 0; i < items.length; i += 1) { if (Storage.Inventory.CanFit(items[i]) - && Pickit.canPick(items[i]) - && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) - && classids.includes(items[i].classid)) { + && Pickit.canPick(items[i]) + && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) + && classids.includes(items[i].classid)) { //print("Bought " + items[i].name); items[i].buy(); @@ -375,7 +375,7 @@ function shopStuff(npcId, classids, amount) { switch (npcId.toLowerCase()) { case "fara": if (!Town.goToTown(2) || !Town.move(NPC.Fara)) throw new Error("Failed to get to NPC"); - + wpArea = sdk.areas.A2SewersLvl2; town = sdk.areas.LutGholein; path = [5112, 5094, 5092, 5096, 5078, 5098, 5070, 5085]; @@ -395,7 +395,7 @@ function shopStuff(npcId, classids, amount) { break; case "drognan": if (!Town.goToTown(2) || !Town.move(NPC.Drognan)) throw new Error("Failed to get to NPC"); - + wpArea = sdk.areas.A2SewersLvl2; town = sdk.areas.LutGholein; path = [5093, 5049, 5088, 5060, 5093, 5079, 5078, 5087, 5070, 5085]; @@ -405,7 +405,7 @@ function shopStuff(npcId, classids, amount) { break; case "ormus": if (!Town.goToTown(3) || !Town.move(NPC.Ormus)) throw new Error("Failed to get to NPC"); - + wpArea = sdk.areas.DuranceofHateLvl2; town = sdk.areas.KurastDocktown; path = [5147, 5089, 5156, 5075, 5157, 5063, 5160, 5050]; @@ -415,7 +415,7 @@ function shopStuff(npcId, classids, amount) { break; case "anya": if (!Town.goToTown(5) || !Town.move(NPC.Anya)) throw new Error("Failed to get to NPC"); - + wpArea = sdk.areas.WorldstoneLvl2; town = sdk.areas.Harrogath; path = [5122, 5119, 5129, 5105, 5123, 5087, 5115, 5068]; @@ -425,7 +425,7 @@ function shopStuff(npcId, classids, amount) { break; case "malah": if (!Town.goToTown(5) || !Town.move(NPC.Malah)) throw new Error("Failed to get to NPC"); - + wpArea = sdk.areas.CrystalizedPassage; town = sdk.areas.Harrogath; path = [5077, 5032, 5089, 5025, 5100, 5021, 5106, 5051, 5116, 5071]; diff --git a/d2bs/kolbot/libs/scripts/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js index 3cf6d3bc6..0f6e8920a 100644 --- a/d2bs/kolbot/libs/scripts/CreepingFeature.js +++ b/d2bs/kolbot/libs/scripts/CreepingFeature.js @@ -8,7 +8,7 @@ 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)); diff --git a/d2bs/kolbot/libs/scripts/DeveloperMode.js b/d2bs/kolbot/libs/scripts/DeveloperMode.js index 90b43af02..d8f9b5077 100644 --- a/d2bs/kolbot/libs/scripts/DeveloperMode.js +++ b/d2bs/kolbot/libs/scripts/DeveloperMode.js @@ -277,6 +277,6 @@ function DeveloperMode() { Config = copiedConfig; UnitInfo.remove(); } - + return true; } diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index c3ea03498..cc6ba2267 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -15,7 +15,7 @@ function DiabloHelper() { try { addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - + if (Config.DiabloHelper.SkipIfBaal) { let leadTick = getTickCount(); diff --git a/d2bs/kolbot/libs/scripts/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js index 9c6065508..7208a4a5d 100644 --- a/d2bs/kolbot/libs/scripts/Eldritch.js +++ b/d2bs/kolbot/libs/scripts/Eldritch.js @@ -12,7 +12,7 @@ function Eldritch() { let { x, y } = me; Pather.moveTo(3745, 5084); Attack.kill(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); - + try { // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); // Could this be causing crashes here? diff --git a/d2bs/kolbot/libs/scripts/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js index 88cf39dee..9d08dca26 100644 --- a/d2bs/kolbot/libs/scripts/Endugu.js +++ b/d2bs/kolbot/libs/scripts/Endugu.js @@ -11,7 +11,7 @@ function Endugu() { Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest)) { throw new Error("Failed to move to Endugu"); } diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 0589c0aec..d14531f73 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -71,11 +71,11 @@ function Follower() { }; /** - * Change areas to where leader is - * @param {Player} unit - * @param {number} area - * @returns {boolean} - */ + * Change areas to where leader is + * @param {Player} unit + * @param {number} area + * @returns {boolean} + */ const checkExit = function (unit, area) { if (unit.inTown) return false; @@ -110,7 +110,7 @@ function Follower() { // Arcane<->Cellar portal if ((me.inArea(sdk.areas.ArcaneSanctuary) && area === sdk.areas.PalaceCellarLvl3) - || (me.inArea(sdk.areas.PalaceCellarLvl3) && area === sdk.areas.ArcaneSanctuary)) { + || (me.inArea(sdk.areas.PalaceCellarLvl3) && area === sdk.areas.ArcaneSanctuary)) { Pather.usePortal(null); return 4; @@ -138,10 +138,10 @@ function Follower() { }; /** - * Talk to a NPC - * @param {string} name - * @returns {boolean} - */ + * Talk to a NPC + * @param {string} name + * @returns {boolean} + */ const talk = function (name) { try { if (!me.inTown) throw new Error("I'm not in town!"); @@ -166,10 +166,10 @@ function Follower() { }; /** - * Change act after completing last act quest - * @param {number} act - * @returns {boolean} - */ + * Change act after completing last act quest + * @param {number} act + * @returns {boolean} + */ const changeAct = function (act) { let preArea = me.area; @@ -310,7 +310,7 @@ function Follower() { if (item) { do { if (item.onGroundOrDropping && item.itemType >= sdk.items.type.HealingPotion - && item.itemType <= sdk.items.type.RejuvPotion && item.distance <= range) { + && item.itemType <= sdk.items.type.RejuvPotion && item.distance <= range) { pickList.push(copyUnit(item)); } } while (item.getNext()); @@ -334,9 +334,9 @@ function Follower() { }; /** - * @param {string} nick - * @param {string} msg - */ + * @param {string} nick + * @param {string} msg + */ const chatEvent = function (nick, msg) { if (msg && nick === Config.Leader) { switch (msg) { @@ -464,7 +464,7 @@ function Follower() { // START addEventListener("chatmsg", chatEvent); openContainers && Config.OpenChests.enabled && Config.OpenChests.Types.push("all"); - + // Override config values that use TP Config.TownCheck = false; Config.TownHP = 0; diff --git a/d2bs/kolbot/libs/scripts/IPHunter.js b/d2bs/kolbot/libs/scripts/IPHunter.js index 6ba43c3f5..c3868dea5 100644 --- a/d2bs/kolbot/libs/scripts/IPHunter.js +++ b/d2bs/kolbot/libs/scripts/IPHunter.js @@ -27,12 +27,12 @@ function IPHunter() { while (true) { /* // remove comment if you want beeps at every movement - for (let i = 12; i != 0; i -= 1) { - me.overhead(":D IP found! - [" + ip + "]" + (i-1) + " beep left"); - beep(); // works if windows sounds are enabled - delay(250); - } - */ + for (let i = 12; i != 0; i -= 1) { + me.overhead(":D IP found! - [" + ip + "]" + (i-1) + " beep left"); + beep(); // works if windows sounds are enabled + delay(250); + } + */ me.overhead(":D IP found! - [" + ip + "]"); try { diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index 312c81b98..f8bd00409 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -7,10 +7,10 @@ function MFHelper() { /** - * @todo We should be able to handle Diablo scripts then resume MFHelper, not sure how yet but doesn't make sense to have - * helper just idle if leader does any of the a5 scripts before baal. I guess could re-order them in the configs but having - * it broken up by act flows better - */ + * @todo We should be able to handle Diablo scripts then resume MFHelper, not sure how yet but doesn't make sense to have + * helper just idle if leader does any of the a5 scripts before baal. I guess could re-order them in the configs but having + * it broken up by act flows better + */ let player, playerAct, split; let lastPrecast; @@ -19,9 +19,9 @@ function MFHelper() { const tasks = ["kill", "clearlevel", "clear", "quit", "cows", "council", "goto", "nextup"]; /** - * @param {string} name - * @param {string} msg - */ + * @param {string} name + * @param {string} msg + */ function chatEvent (name, msg) { if (!msg) return; let msgShort = msg && msg.length ? msg.split(" ")[0] : ""; @@ -105,7 +105,7 @@ function MFHelper() { if (taskList[0].task === "quit") return true; // check if any message is telling us to quit if (taskList.find(el => el.task === "quit")) return true; - + // check if any message is telling us that nextup is diablo/baal if (taskList.some(el => { if (el.task === "nextup") { @@ -140,7 +140,7 @@ function MFHelper() { split = msg.substr(6); console.log("ÿc4MFHelperÿc0: Goto " + split); - + if (!!parseInt(split, 10)) { split = parseInt(split, 10); } @@ -188,11 +188,11 @@ function MFHelper() { if (getTickCount() - at > Time.minutes(3)) continue; /** - * @todo still think this section needs to be done better, we are using a snapshot of the player's area at the time - * of the message but sometimes the area hasn't been updated yet, causing us to do dumb things like attempt to kill - * while still in town. We can't just use the players area though because of towncheck/chicken. Feel like best solution - * would be adding area into leaders message and just always parsing it from there - */ + * @todo still think this section needs to be done better, we are using a snapshot of the player's area at the time + * of the message but sometimes the area hasn't been updated yet, causing us to do dumb things like attempt to kill + * while still in town. We can't just use the players area though because of towncheck/chicken. Feel like best solution + * would be adding area into leaders message and just always parsing it from there + */ try { split = msg.split(task + " ")[1]; if (parseInt(split, 10)) { diff --git a/d2bs/kolbot/libs/scripts/Mausoleum.js b/d2bs/kolbot/libs/scripts/Mausoleum.js index 60d87445f..88ff1782e 100644 --- a/d2bs/kolbot/libs/scripts/Mausoleum.js +++ b/d2bs/kolbot/libs/scripts/Mausoleum.js @@ -33,8 +33,8 @@ function Mausoleum() { if (Config.Mausoleum.ClearCrypt) { // Crypt exit is... awkward if (!(Pather.moveToExit(sdk.areas.BurialGrounds, true) - && Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Stairs, sdk.exits.preset.Crypt) - && Pather.moveToExit(sdk.areas.Crypt, true))) { + && Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Stairs, sdk.exits.preset.Crypt) + && Pather.moveToExit(sdk.areas.Crypt, true))) { throw new Error("Failed to move to Crypt"); } diff --git a/d2bs/kolbot/libs/scripts/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js index 510b6c2c2..74a64c481 100644 --- a/d2bs/kolbot/libs/scripts/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -14,9 +14,9 @@ function Questing () { }; /** - * @param {ItemUnit} item - * @returns {boolean} - */ + * @param {ItemUnit} item + * @returns {boolean} + */ const getQuestItem = (item) => { if (item) { let id = item.classid; @@ -218,13 +218,13 @@ function Questing () { let 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 - */ + * 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); @@ -240,9 +240,9 @@ function Questing () { 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 - */ + * 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"); @@ -252,7 +252,7 @@ function Questing () { }, 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); @@ -271,19 +271,19 @@ function Questing () { } /** - * Now lets handle completing the quest as we have freed 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 - */ + * 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 - */ + * 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"); } @@ -327,7 +327,7 @@ function Questing () { log("Failed to take portal back to Arreat Summit", true); Pather.journeyTo(sdk.areas.ArreatSummit); } - + Precast.doPrecast(true); // move to altar @@ -337,7 +337,7 @@ function Questing () { Common.Ancients.touchAltar(); Common.Ancients.startAncients(true); - + me.cancel(); Config = tempConfig; log("restored settings"); @@ -368,11 +368,11 @@ function Questing () { const tasks = (function () { /** - * @constructor - * @param {function(): void} task - * @param {() => boolean} preReq - * @param {() => boolean} complete - */ + * @constructor + * @param {function(): void} task + * @param {() => boolean} preReq + * @param {() => boolean} complete + */ function Task (task, preReq, complete) { this.run = task; this.preReq = (preReq || (() => true)); diff --git a/d2bs/kolbot/libs/scripts/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js index 64c07fc99..19d0c5d5a 100644 --- a/d2bs/kolbot/libs/scripts/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -258,10 +258,10 @@ function Rushee() { break; } } - + Pather.usePortal(sdk.areas.HaremLvl1, Config.Leader); Pather.moveToExit(sdk.areas.LutGholein, true); - + if (!Town.npcInteract("Jerhyn")) { Pather.moveTo(5166, 5206); @@ -478,7 +478,7 @@ function Rushee() { // make sure we talk to cain to access durance leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); - + // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas (!Config.Rushee.Quester && !this.nonQuesterNPCTalk) && (this.nonQuesterNPCTalk = true); @@ -490,7 +490,7 @@ function Rushee() { let p = Game.getObject("portal"); let preArea = me.area; if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) - && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { + && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { me.inTown && Config.LocalChat.Enabled && say("gotwp"); } else { this.log("Failed to get wp", Config.LocalChat.Enabled); @@ -593,7 +593,7 @@ function Rushee() { while (getTickCount() - tick < Time.minutes(2)) { if (Pather.getPortal(sdk.areas.Tristram)) { Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - + break; } } @@ -610,7 +610,7 @@ function Rushee() { this.getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); delay(500); Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - + if (Town.npcInteract("Akara")) { this.log("Akara done", Config.LocalChat.Enabled); } @@ -670,7 +670,7 @@ function Rushee() { Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); this.getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - + if (Town.npcInteract("Drognan")) { actions.shift(); say("drognan done", Config.LocalChat.Enabled); diff --git a/d2bs/kolbot/libs/scripts/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js index 3c4800f00..226f7cd2b 100644 --- a/d2bs/kolbot/libs/scripts/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -57,10 +57,10 @@ function ShopBot() { }; /** - * Interact and open the menu of an NPC unit - * @param {NPCUnit} npc - * @returns {boolean} - */ + * Interact and open the menu of an NPC unit + * @param {NPCUnit} npc + * @returns {boolean} + */ const openMenu = function (npc) { if (!npc || npc.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); @@ -98,10 +98,10 @@ function ShopBot() { }; /** - * @param {NPCUnit} npc - * @param {number} menuId - * @returns {boolean} - */ + * @param {NPCUnit} npc + * @param {number} menuId + * @returns {boolean} + */ const shopItems = function (npc, menuId) { let bought; @@ -137,8 +137,8 @@ function ShopBot() { for (let i = 0; i < items.length; i += 1) { if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && - me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && - NTIP.CheckItem(items[i], pickEntries) + me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && + NTIP.CheckItem(items[i], pickEntries) ) { beep(); D2Bot.printToConsole("Match found!", sdk.colors.D2Bot.DarkGold); @@ -163,9 +163,9 @@ function ShopBot() { }; /** - * @param {string} name - * @returns {boolean} - */ + * @param {string} name + * @returns {boolean} + */ const shopAtNPC = function (name) { let wp, menuId = "Shop"; @@ -289,7 +289,7 @@ function ShopBot() { } if ([sdk.areas.RogueEncampment, sdk.areas.Harrogath].includes(me.area) && !!redPortal && redPortal.distance < 20 - && Pather.usePortal(null, null, redPortal)) { + && Pather.usePortal(null, null, redPortal)) { delay(3000); Pather.usePortal(sdk.areas.townOf(me.area)); diff --git a/d2bs/kolbot/libs/scripts/Synch.js b/d2bs/kolbot/libs/scripts/Synch.js index 9800e0caf..231266da0 100644 --- a/d2bs/kolbot/libs/scripts/Synch.js +++ b/d2bs/kolbot/libs/scripts/Synch.js @@ -38,7 +38,7 @@ function Synch() { party = getParty(Config.Synch.WaitFor[j]); if (!party) { D2Bot.printToConsole("WaitFor not in game: " + - Config.Synch.WaitFor[j] + " so quitting."); + Config.Synch.WaitFor[j] + " so quitting."); removeEventListener("chatmsg", messageHandler); quit(); diff --git a/d2bs/kolbot/libs/scripts/Synch2.js b/d2bs/kolbot/libs/scripts/Synch2.js index 1bd658f0e..020ebda64 100644 --- a/d2bs/kolbot/libs/scripts/Synch2.js +++ b/d2bs/kolbot/libs/scripts/Synch2.js @@ -38,7 +38,7 @@ function Synch2() { party = getParty(Config.Synch.WaitFor[j]); if (!party) { D2Bot.printToConsole("WaitFor not in game: " + - Config.Synch.WaitFor[j] + " so quitting."); + Config.Synch.WaitFor[j] + " so quitting."); removeEventListener("chatmsg", messageHandler2); quit(); diff --git a/d2bs/kolbot/libs/scripts/Tombs.js b/d2bs/kolbot/libs/scripts/Tombs.js index 5afdb933b..54bd5a741 100644 --- a/d2bs/kolbot/libs/scripts/Tombs.js +++ b/d2bs/kolbot/libs/scripts/Tombs.js @@ -14,14 +14,14 @@ function Tombs() { for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i++) { try { if (!Pather.journeyTo(i, true)) throw new Error("Failed to move to tomb"); - + Attack.clearLevel(Config.ClearType); - + if (Config.Tombs.KillDuriel && me.inArea(correctTomb)) { Pather.journeyTo(sdk.areas.DurielsLair) && Attack.kill(sdk.monsters.Duriel); Pather.journeyTo(sdk.areas.CanyonofMagic); } - + if (!Pather.moveToExit(sdk.areas.CanyonofMagic, true)) throw new Error("Failed to move to Canyon"); } catch (e) { console.error(e); diff --git a/d2bs/kolbot/libs/scripts/Travincal.js b/d2bs/kolbot/libs/scripts/Travincal.js index 180669423..323c008ba 100644 --- a/d2bs/kolbot/libs/scripts/Travincal.js +++ b/d2bs/kolbot/libs/scripts/Travincal.js @@ -13,7 +13,7 @@ function Travincal() { if (monster) { do { if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].includes(monster.classid) - && monster.attackable && (!checkColl || !checkCollision(me, monster, sdk.collision.BlockWall))) { + && monster.attackable && (!checkColl || !checkCollision(me, monster, sdk.collision.BlockWall))) { monsterList.push(copyUnit(monster)); } } while (monster.getNext()); diff --git a/d2bs/kolbot/libs/scripts/TravincalLeech.js b/d2bs/kolbot/libs/scripts/TravincalLeech.js index 3d26e713d..4e8649f88 100644 --- a/d2bs/kolbot/libs/scripts/TravincalLeech.js +++ b/d2bs/kolbot/libs/scripts/TravincalLeech.js @@ -50,7 +50,7 @@ function TravincalLeech () { Common.Leecher.leader = leader; Common.Leecher.currentScript = Loader.scriptName(); Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; - + while (Misc.inMyParty(Common.Leecher.leader)) { if (done) return true; @@ -58,7 +58,7 @@ function TravincalLeech () { Pather.usePortal(sdk.areas.Travincal, Common.Leecher.leader); Town.getCorpse(); } - + if (me.mode === sdk.player.mode.Dead) { me.revive(); diff --git a/d2bs/kolbot/libs/scripts/TristramLeech.js b/d2bs/kolbot/libs/scripts/TristramLeech.js index 0f5b4043d..b071289ab 100644 --- a/d2bs/kolbot/libs/scripts/TristramLeech.js +++ b/d2bs/kolbot/libs/scripts/TristramLeech.js @@ -9,13 +9,13 @@ function TristramLeech () { include("core/Common/Leecher.js"); let done = false; let whereisleader, leader; - + const chatEvent = function (nick, msg) { if (nick === leader && msg.toLowerCase() === "tristdone") { done = true; } }; - + Town.doChores(); Town.goToTown(1); Town.move("portalspot"); @@ -67,7 +67,7 @@ function TristramLeech () { return false; }, Time.minutes(3), 1000); - + while (true) { if (done) return true; diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 7a6c83ca4..9f877c932 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -69,7 +69,7 @@ function Wakka () { while (!boss.dead) { delay(500); } - + return true; } @@ -330,7 +330,7 @@ function Wakka () { console.debug("Failed to move to seis"); break; } - + if (checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { log("seis dead"); internals.seisClear = true; diff --git a/d2bs/kolbot/libs/scripts/Worldstone.js b/d2bs/kolbot/libs/scripts/Worldstone.js index 723099657..7d453d251 100644 --- a/d2bs/kolbot/libs/scripts/Worldstone.js +++ b/d2bs/kolbot/libs/scripts/Worldstone.js @@ -10,12 +10,12 @@ 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 - */ - + * 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); diff --git a/d2bs/kolbot/libs/starter/AdvancedConfig.js b/d2bs/kolbot/libs/starter/AdvancedConfig.js index 704c8035a..8e8c23a19 100644 --- a/d2bs/kolbot/libs/starter/AdvancedConfig.js +++ b/d2bs/kolbot/libs/starter/AdvancedConfig.js @@ -9,31 +9,31 @@ (function (module) { module.exports = { /* Features: - Override channel for each profile, Override join delay for each profile - Override default values for JoinChannel, FirstJoinMessage, AnnounceGames and AfterGameMessage per profile + Override channel for each profile, Override join delay for each profile + Override default values for JoinChannel, FirstJoinMessage, AnnounceGames and AfterGameMessage per profile - * Format *: - "Profile Name": {JoinDelay: number_of_seconds} - or - "Profile Name": {JoinChannel: "channel name"} - or - "Profile Name": {JoinChannel: "channel name", JoinDelay: number_of_seconds} + * Format *: + "Profile Name": {JoinDelay: number_of_seconds} + or + "Profile Name": {JoinChannel: "channel name"} + or + "Profile Name": {JoinChannel: "channel name", JoinDelay: number_of_seconds} - * Example * (don't edit this - it's just an example): + * Example * (don't edit this - it's just an example): - "MyProfile1": {JoinDelay: 3}, - "MyProfile2": {JoinChannel: "some channel"}, - "MyProfile3": {JoinChannel: "some other channel", JoinDelay: 11} - "MyProfile4": {AnnounceGames: true, AnnounceMessage: "Joining game"} // announce game you are joining + "MyProfile1": {JoinDelay: 3}, + "MyProfile2": {JoinChannel: "some channel"}, + "MyProfile3": {JoinChannel: "some other channel", JoinDelay: 11} + "MyProfile4": {AnnounceGames: true, AnnounceMessage: "Joining game"} // announce game you are joining - "Profile Name": { - JoinChannel: "channel name", - FirstJoinMessage: "first message", -OR- ["join msg 1", "join msg 2"], - AnnounceGames: true, - AfterGameMessage: "message after a finished run" -OR- ["msg 1", msg 2"] - } - */ - + "Profile Name": { + JoinChannel: "channel name", + FirstJoinMessage: "first message", -OR- ["join msg 1", "join msg 2"], + AnnounceGames: true, + AfterGameMessage: "message after a finished run" -OR- ["msg 1", msg 2"] + } + */ + // Put your lines under this one. Multiple entries are separated by commas. No comma after the last one. "Test": { diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index 013e1ef67..d4853030a 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -11,7 +11,7 @@ const AutoMule = { /** @type {Object.} */ Mules: Object.assign({}, require("./MuleConfig", null, false)), - + /** @type {Object.} */ TorchAnniMules: Object.assign({}, require("./TorchAnniMules", null, false)), @@ -25,9 +25,9 @@ const AutoMule = { // ################################## // /** - * Get mule and torchanni mule info if it exists - * @returns {muleObj | {}} - */ + * Get mule and torchanni mule info if it exists + * @returns {muleObj | {}} + */ getInfo: function () { let info; @@ -67,8 +67,8 @@ const AutoMule = { let items = this.getMuleItems(); if (info.muleInfo.hasOwnProperty("usedStashTrigger") && info.muleInfo.hasOwnProperty("usedInventoryTrigger") - && Storage.Inventory.UsedSpacePercent() >= info.muleInfo.usedInventoryTrigger - && Storage.Stash.UsedSpacePercent() >= info.muleInfo.usedStashTrigger && items.length > 0) { + && Storage.Inventory.UsedSpacePercent() >= info.muleInfo.usedInventoryTrigger + && Storage.Stash.UsedSpacePercent() >= info.muleInfo.usedStashTrigger && items.length > 0) { D2Bot.printToConsole("MuleCheck triggered!", sdk.colors.D2Bot.DarkGold); return true; @@ -86,9 +86,9 @@ const AutoMule = { }, /** - * Find a mule that matches our wanted check - * @returns {muleObj | false} - */ + * Find a mule that matches our wanted check + * @returns {muleObj | false} + */ getMule: function () { let info = this.getInfo(); @@ -132,7 +132,7 @@ const AutoMule = { // Set status to ready if using continuous mule with no response check if (muleObj.continuousMule && muleObj.skipMuleResponse) { muleInfo.status = "ready"; - + // If nothing received our copy data start the mule profile } else if (!sendCopyData(null, muleObj.muleProfile, 10, JSON.stringify({ profile: me.profile, mode: this.torchAnniCheck || 0 })) && !muleObj.continuousMule) { // if the mule profile isn't already running and there is a profile to be stopped, stop it before starting the mule profile @@ -261,7 +261,7 @@ const AutoMule = { // Profile is not in mule or torch mule game if (!((info.hasOwnProperty("muleInfo") && String.isEqual(me.gamename, info.muleInfo.muleGameName[0])) - || (info.hasOwnProperty("torchMuleInfo") && String.isEqual(me.gamename, info.torchMuleInfo.muleGameName[0])))) { + || (info.hasOwnProperty("torchMuleInfo") && String.isEqual(me.gamename, info.torchMuleInfo.muleGameName[0])))) { return false; } @@ -338,7 +338,7 @@ const AutoMule = { let gameType = this.torchAnniCheck === 2 ? " anni" : this.torchAnniCheck === 1 ? " torch" : ""; print("ÿc4AutoMuleÿc0: In" + gameType + " mule game."); D2Bot.updateStatus("AutoMule: In" + gameType + " mule game."); - + if (this.torchAnniCheck === 2) { this.dropCharm(true); } else if (this.torchAnniCheck === 1) { @@ -380,7 +380,7 @@ const AutoMule = { delay(500); } - + return true; } catch (e) { console.error(e); @@ -389,7 +389,7 @@ const AutoMule = { } finally { removeEventListener("scriptmsg", muleModeEvent); removeEventListener("copydata", dropStatusEvent); - + if (!muleObj.continuousMule) { D2Bot.stop(muleObj.muleProfile, true); delay(1000); @@ -402,8 +402,8 @@ const AutoMule = { }, /** - * finished if no items are on ground - */ + * finished if no items are on ground + */ isFinished: function () { let item = Game.getItem(); @@ -423,9 +423,9 @@ const AutoMule = { }, /** - * make sure mule character is in game - * @param {string} mulePrefix - */ + * make sure mule character is in game + * @param {string} mulePrefix + */ verifyMulePrefix: function (mulePrefix) { try { let player = getParty(); @@ -448,9 +448,9 @@ const AutoMule = { }, /** - * Transfer items to waiting mule - * @returns {boolean} - */ + * Transfer items to waiting mule + * @returns {boolean} + */ dropStuff: function () { if (!Town.openStash()) return false; @@ -460,7 +460,7 @@ const AutoMule = { D2Bot.printToConsole("AutoMule: Transfering " + items.length + " items.", sdk.colors.D2Bot.DarkGold); D2Bot.printToConsole("AutoMule: " + JSON.stringify(items.map(i => i.prettyPrint)), sdk.colors.D2Bot.DarkGold); - + items.forEach(item => item.drop()); delay(1000); me.cancel(); @@ -473,10 +473,10 @@ const AutoMule = { }, /** - * @param {ItemUnit} item - * @param {string[] | number[]} list - * @returns {boolean} - */ + * @param {ItemUnit} item + * @param {string[] | number[]} list + * @returns {boolean} + */ matchItem: function (item, list) { let parsedPickit = [], classIDs = []; @@ -500,9 +500,9 @@ const AutoMule = { }, /** - * get a list of items to mule - * @returns {ItemUnit[] | false} - */ + * get a list of items to mule + * @returns {ItemUnit[] | false} + */ getMuleItems: function () { let info = this.getInfo(); if (!info || !info.hasOwnProperty("muleInfo")) return false; @@ -510,15 +510,15 @@ const AutoMule = { const muleOrphans = !!(info.muleInfo.hasOwnProperty("muleOrphans") && info.muleInfo.muleOrphans); /** - * @param {ItemUnit} 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 - */ + * 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)); let items = me.getItemsEx() @@ -549,19 +549,19 @@ const AutoMule = { }, /** - * Wanted by CraftingSystem - * @param {ItemUnit} item - * @returns {boolean} - */ + * Wanted by CraftingSystem + * @param {ItemUnit} item + * @returns {boolean} + */ utilityIngredient: function (item) { return (!!item && CraftingSystem.validGids.includes(item.gid)); }, /** - * check if an item is a cubing ingredient - * @param {ItemUnit} item - * @returns {boolean} - */ + * check if an item is a cubing ingredient + * @param {ItemUnit} item + * @returns {boolean} + */ cubingIngredient: function (item) { if (!item) return false; @@ -575,10 +575,10 @@ const AutoMule = { }, /** - * check if an item is a runeword ingrediend - rune, empty base or bad rolled base - * @param {ItemUnit} item - * @returns {boolean} - */ + * check if an item is a runeword ingrediend - rune, empty base or bad rolled base + * @param {ItemUnit} item + * @returns {boolean} + */ runewordIngredient: function (item) { if (!item) return false; if (Runewords.validGids.includes(item.gid)) return true; @@ -596,10 +596,10 @@ const AutoMule = { }, /** - * Drop Anni or Gheeds - * @param {boolean} dropAnni - * @returns {boolean} - */ + * Drop Anni or Gheeds + * @param {boolean} dropAnni + * @returns {boolean} + */ dropCharm: function (dropAnni) { if (!Town.openStash()) return false; @@ -635,9 +635,9 @@ const AutoMule = { // ################################## // /** - * @param {{ profile: string, mode: number }} info - * @returns {{ profile: string, mode: number }} master info - */ + * @param {{ profile: string, mode: number }} info + * @returns {{ profile: string, mode: number }} master info + */ getMaster: function (info) { let muleObj = info.mode === 1 ? this.TorchAnniMules : this.Mules; @@ -662,10 +662,10 @@ const AutoMule = { }, /** - * @param {number} mode - mule mode - * @param {string} master - profile that whats to mule - * @param {boolean} continuous - whether we are continuous or not - */ + * @param {number} mode - mule mode + * @param {string} master - profile that whats to mule + * @param {boolean} continuous - whether we are continuous or not + */ getMuleObject: function (mode, master, continuous = false) { mode = mode || 0; let mule = mode > 0 ? this.TorchAnniMules : this.Mules; @@ -673,7 +673,7 @@ const AutoMule = { for (let i in mule) { if (mule.hasOwnProperty(i)) { if (mule[i].muleProfile && mule[i].enabledProfiles && String.isEqual(mule[i].muleProfile, me.profile) - && (continuous || mule[i].enabledProfiles.includes(master))) { + && (continuous || mule[i].enabledProfiles.includes(master))) { return mule[i]; } } @@ -683,11 +683,11 @@ const AutoMule = { }, /** - * @param {number} mode - * @param {string} master - * @param {boolean} continuous - * @returns {string} - */ + * @param {number} mode + * @param {string} master + * @param {boolean} continuous + * @returns {string} + */ getMuleFilename: function (mode, master, continuous = false) { mode = mode || 0; let mule = mode > 0 ? this.TorchAnniMules : this.Mules; @@ -699,7 +699,7 @@ const AutoMule = { // Mule profile matches config if (mule[i].muleProfile && String.isEqual(mule[i].muleProfile, me.profile) && (continuous || mule[i].enabledProfiles.includes(master))) { file = mode === 0 ? "logs/AutoMule." + i + ".json" : "logs/TorchMule." + i + ".json"; - + // If file exists check for valid info if (FileTools.exists(file)) { try { @@ -727,8 +727,8 @@ const AutoMule = { }, /** - * Get whether this is a regular mule or a torch/anni mule - */ + * Get whether this is a regular mule or a torch/anni mule + */ getMuleMode: function() { for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { @@ -737,7 +737,7 @@ const AutoMule = { } } } - + for (let i in this.TorchAnniMules) { if (this.TorchAnniMules.hasOwnProperty(i)) { if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { @@ -750,8 +750,8 @@ const AutoMule = { }, /** - * Get whether this is a normal mule or continous mule - */ + * Get whether this is a normal mule or continous mule + */ isContinousMule: function () { for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { @@ -760,7 +760,7 @@ const AutoMule = { } } } - + for (let i in this.TorchAnniMules) { if (this.TorchAnniMules.hasOwnProperty(i)) { if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { diff --git a/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js b/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js index a75ee6f49..45a0f6e55 100644 --- a/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js +++ b/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js @@ -18,10 +18,10 @@ CraftingGames: [], /* BaseItems - list of base item class ids - * Ingredients - list of recipe ingredients - * SetAmount - number of full sets to gather before transfering - * Type - the type of recipe. Available options: "crafting", "runewords", "cubing" - */ + * Ingredients - list of recipe ingredients + * SetAmount - number of full sets to gather before transfering + * Type - the type of recipe. Available options: "crafting", "runewords", "cubing" + */ Sets: [ // LLD Crafting diff --git a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js index ff9f13409..02846546e 100644 --- a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js +++ b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js @@ -6,8 +6,8 @@ */ (function (module) { - module.exports = { - /** + module.exports = { + /** Setting up: "Gamble Team 1": { // Put a unique team name here. @@ -25,13 +25,13 @@ Once set up properly, the gold finders will run their own games and join gamblers' games when they're out of gold. */ - "Gamble Team 1": { - goldFinders: [""], - gamblers: [""], - gambleGames: [""], - - goldTrigger: 2500000, - goldReserve: 200000 - }, - }; + "Gamble Team 1": { + goldFinders: [""], + gamblers: [""], + gambleGames: [""], + + goldTrigger: 2500000, + goldReserve: 200000 + }, + }; })(module); diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js index 63eb3013d..3d762d107 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -37,7 +37,7 @@ const GameAction = { update: function (action, data) { if (typeof action !== "string") throw new Error("Action must be a string!"); - + typeof data !== "string" && (data = JSON.stringify(data)); D2Bot.printToConsole(data); diff --git a/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js b/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js index 3f664b55b..3bb3603c4 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js +++ b/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js @@ -15,20 +15,20 @@ SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) AutoPerm: true, // override InGameTime to perm character IngameTime: rand(60, 120), // (180, 210) to avoid RD, increase it to (7230, 7290) for mule perming - + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LogAccounts: { /* Format: - "account1/password1/realm": ["charname1", "charname2 etc"], - "account2/password2/realm": ["charnameX", "charnameY etc"], - "account3/password3/realm": ["all"] + "account1/password1/realm": ["charname1", "charname2 etc"], + "account2/password2/realm": ["charnameX", "charnameY etc"], + "account3/password3/realm": ["all"] - To log a full account, put "account/password/realm": ["all"] + To log a full account, put "account/password/realm": ["all"] - realm = useast, uswest, europe or asia + realm = useast, uswest, europe or asia - Enter Individual entries are separated with a comma below - */ + Enter Individual entries are separated with a comma below + */ "exampleAcc/pa33word3/realm": ["all"], }, // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // diff --git a/d2bs/kolbot/libs/systems/torch/FarmerConfig.js b/d2bs/kolbot/libs/systems/torch/FarmerConfig.js index 06e7baa4f..6290b9d07 100644 --- a/d2bs/kolbot/libs/systems/torch/FarmerConfig.js +++ b/d2bs/kolbot/libs/systems/torch/FarmerConfig.js @@ -10,25 +10,25 @@ // ############################ S E T U P ########################################## /* Each uber killer profile can have their own army of key finders - Multiple entries are separated with a comma - Example config: + Multiple entries are separated with a comma + Example config: - "Farmer 1": { // Farmer profile name - // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], - KeyFinderProfiles: ["mf 1", "mf 2"], + "Farmer 1": { // Farmer profile name + // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], + KeyFinderProfiles: ["mf 1", "mf 2"], - // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", - FarmGame: "torch1-" - }, + // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", + FarmGame: "torch1-" + }, - "Farmer 2": { // Farmer profile name - // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], - KeyFinderProfiles: ["mf 3", "mf 4"], + "Farmer 2": { // Farmer profile name + // Put key finder profiles here. Example - KeyFinderProfiles: ["MF 1", "MF 2"], + KeyFinderProfiles: ["mf 3", "mf 4"], - // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", - FarmGame: "torch2-" - } - */ + // Put the game name of uber killer here (without numbers). Key finders will join this game to drop keys. Example - FarmGame: "Ubers-", + FarmGame: "torch2-" + } + */ // Edit here! diff --git a/d2bs/kolbot/libs/systems/torch/TorchSystem.js b/d2bs/kolbot/libs/systems/torch/TorchSystem.js index 8b192d3f0..0ca63d695 100644 --- a/d2bs/kolbot/libs/systems/torch/TorchSystem.js +++ b/d2bs/kolbot/libs/systems/torch/TorchSystem.js @@ -72,7 +72,7 @@ const TorchSystem = { } delay(5000); - + try { quit(); } finally { @@ -96,7 +96,7 @@ const TorchSystem = { function keyCheckEvent(mode, msg) { if (mode === 6) { let obj = JSON.parse(msg); - + if (obj.name === "neededItems") { let item; @@ -331,9 +331,9 @@ const TorchSystem = { // Stop the loop if we have enough keys or if wait time expired if (((tkeys >= 3 && hkeys >= 3 && dkeys >= 3) - || (Config.OrgTorch.WaitTimeout + || (Config.OrgTorch.WaitTimeout && (getTickCount() - timer > Time.minutes(Config.OrgTorch.WaitTimeout)))) - && aloneInGame()) { + && aloneInGame()) { break; } diff --git a/d2bs/kolbot/threads/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js index b387bbb01..67b2e5a49 100644 --- a/d2bs/kolbot/threads/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -25,7 +25,7 @@ function main() { // AntiHostile gets game event info from ToolsThread this.scriptEvent = function (msg) { if (!msg || typeof msg !== "string") return; - + switch (msg.split(" ")[0]) { case "remove": // Remove a hostile player that left the game if (hostiles.indexOf(msg.split(" ")[1]) > -1) { diff --git a/d2bs/kolbot/threads/AntiIdle.js b/d2bs/kolbot/threads/AntiIdle.js index e05f739b4..3fde5a8fb 100644 --- a/d2bs/kolbot/threads/AntiIdle.js +++ b/d2bs/kolbot/threads/AntiIdle.js @@ -12,7 +12,7 @@ include("core/Packet.js"); function main () { console.log("ÿc3Start AntiIdle"); let idleTick = Time.seconds(getTickCount() + rand(1200, 1500)); - + while (true) { if (me.ingame && me.gameReady) { if (getTickCount() - idleTick > 0) { diff --git a/d2bs/kolbot/threads/AreaWatcher.js b/d2bs/kolbot/threads/AreaWatcher.js index 383513b48..cfa63c63f 100644 --- a/d2bs/kolbot/threads/AreaWatcher.js +++ b/d2bs/kolbot/threads/AreaWatcher.js @@ -15,7 +15,7 @@ includeCoreLibs(); function main() { let _default = getScript("default.dbj"); console.log("ÿc3Start AreaWatcher"); - + while (true) { try { if (me.gameReady && me.ingame && !me.inTown) { diff --git a/d2bs/kolbot/threads/AutoBuildThread.js b/d2bs/kolbot/threads/AutoBuildThread.js index 16dba39c9..7623387f9 100644 --- a/d2bs/kolbot/threads/AutoBuildThread.js +++ b/d2bs/kolbot/threads/AutoBuildThread.js @@ -93,7 +93,7 @@ function spendStatPoints () { 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"); + + "\nOnly the first " + len + " stats " + stats.slice(0, len).join(", ") + " will be added"); } // We silently ignore stats set to -1 @@ -116,7 +116,7 @@ function spendStatPoints () { } } 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); + + ",\t1:" + STAT_ID_TO_NAME[1] + ",\t2:" + STAT_ID_TO_NAME[2] + ",\t3:" + STAT_ID_TO_NAME[3] + errorMessage); } } @@ -185,7 +185,7 @@ function spendSkillPoints () { 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"); + "\nOnly the first " + len + " skills " + skills.slice(0, len).join(", ") + " will be added"); } // We silently ignore skills set to -1 diff --git a/d2bs/kolbot/threads/Party.js b/d2bs/kolbot/threads/Party.js index d90d83b52..b0eae0902 100644 --- a/d2bs/kolbot/threads/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -33,11 +33,11 @@ function main() { } /** - * Format the event message here to prevent repetitive code - * @param {string[]} arr - * @param {Player | string} player - * @param {string} [killer] - */ + * Format the event message here to prevent repetitive code + * @param {string[]} arr + * @param {Player | string} player + * @param {string} [killer] + */ const eventMsg = (arr, player, killer) => { try { typeof player === "string" && (player = getParty(player)); @@ -145,10 +145,10 @@ function main() { } /** - * @todo if we are already partied with everyone in game, then this doesn't need to keep checking unless an event happens - * e.g. someone joins/leaves game or someone declares hostility - * the exception to that is if we are running with Config.Congratulations, in which case we do need to constantly monitor changes - */ + * @todo if we are already partied with everyone in game, then this doesn't need to keep checking unless an event happens + * e.g. someone joins/leaves game or someone declares hostility + * the exception to that is if we are running with Config.Congratulations, in which case we do need to constantly monitor changes + */ if (me.gameReady && (!Config.PartyAfterScript || scriptList.indexOf(currScript) > scriptList.indexOf(Config.PartyAfterScript))) { player = getParty(); @@ -232,7 +232,7 @@ function main() { if (player.level > playerLevels[player.name]) { let msg = eventMsg(Config.Congratulations, player); msg && say(msg); - + playerLevels[player.name] = player.level; } } diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index 4ea3f8b66..8762d7bf4 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -81,7 +81,7 @@ new Overrides.Override(Pather, Pather.useWaypoint, function(orignal, targetArea, return (Config.Rusher.GiveWps && giveWP()) || true; } else { print("failed"); - + return false; } }).apply(); @@ -138,7 +138,7 @@ function main () { Pather.useWaypoint(sdk.areas.CatacombsLvl2, true) && Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true) - || !Pather.moveTo(22582, 9612)) { + || !Pather.moveTo(22582, 9612)) { throw new Error("andy failed"); } @@ -177,7 +177,7 @@ function main () { Precast.doPrecast(true); if (!Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { throw new Error("cube failed"); } @@ -205,7 +205,7 @@ function main () { Pather.useWaypoint(sdk.areas.LostCity, true) && Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true) - || !Pather.moveTo(15044, 14045)) { + || !Pather.moveTo(15044, 14045)) { throw new Error("amulet failed"); } @@ -233,7 +233,7 @@ function main () { Pather.useWaypoint(sdk.areas.FarOasis, true) && Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { throw new Error("staff failed"); } @@ -345,7 +345,7 @@ function main () { Precast.doPrecast(true); if (!Pather.moveToExit(getRoom().correcttomb, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder)) { throw new Error("duriel failed"); } @@ -507,11 +507,11 @@ function main () { Pather.useWaypoint(sdk.areas.RiverofFlame); Precast.doPrecast(true); if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); - + Common.Diablo.initLayout(); Config.Diablo.Fast = true; Config.Diablo.SealLeader = false; - + try { Common.Diablo.runSeals(Config.Diablo.SealOrder); print("Attempting to find Diablo"); @@ -851,7 +851,7 @@ function main () { Precast.doPrecast(false); if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { throw new Error("Lam Essen quest failed"); } @@ -992,7 +992,7 @@ function main () { Precast.doPrecast(false); if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { throw new Error("Anya quest failed"); } diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index ad1eed8b4..4f4120763 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -27,7 +27,7 @@ new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { function main() { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); - + let ironGolem, debugInfo = { area: 0, currScript: "no entry" }; let [quitFlag, antiIdle, townChicken] = [false, false, false]; let quitListDelayTime; @@ -145,12 +145,12 @@ function main() { let nString = "ÿc4NTIP.CheckItem: ÿc0" + nResult.result + " ÿc7Line: ÿc0" + nResult.line + "\n"; itemString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") - + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality + "| ÿc4Gid: ÿc0" + itemToCheck.gid - + "\nÿc4ItemMode: ÿc0" + itemToCheck.mode + "| ÿc4Location: ÿc0" + itemToCheck.location + "| ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation; + + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality + "| ÿc4Gid: ÿc0" + itemToCheck.gid + + "\nÿc4ItemMode: ÿc0" + itemToCheck.mode + "| ÿc4Location: ÿc0" + itemToCheck.location + "| ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation; generalString = pString + nString - + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck); + + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck); } - + console.log("ÿc2*************Item Info Start*************"); console.log(itemString); console.log("ÿc2Systems Info Start"); @@ -346,8 +346,8 @@ function main() { Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); /** - * Feel like potting and lifechicken should actually be seperate threads - */ + * Feel like potting and lifechicken should actually be seperate threads + */ if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting if (!me.inTown) { diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js index e051db320..fd48da8fa 100644 --- a/d2bs/kolbot/threads/TownChicken.js +++ b/d2bs/kolbot/threads/TownChicken.js @@ -91,9 +91,9 @@ function main() { while (true) { if (!me.inTown && (townCheck - // should TownHP/MP check be in toolsthread? - // We would then be able to remove all game interaction checks until we get a townCheck msg - || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { + // should TownHP/MP check be in toolsthread? + // We would then be able to remove all game interaction checks until we get a townCheck msg + || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area if (me.dead && Config.LifeChicken <= 0) { console.log("ÿc1TownChicken :: ÿc0We are dead and LifeChicken is set to 0"); From cf7ea367ed04556380b9eb8b7a94e31543d98f12 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 26 May 2023 19:49:04 -0400 Subject: [PATCH 128/758] Update Follower.js - add chug command to have followers buy/drink special pots --- d2bs/kolbot/libs/scripts/Follower.js | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index d14531f73..5e6d37389 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -38,6 +38,11 @@ * @Town * a2-5 - move to appropriate act (after quest) !NOTE: Disable 'no sound' or game will crash! * talk - talk to a npc in town +* chug - buy and drink special potions potions from Akara +* *** type can be: a, antidote, t, thawing, s, stamina *** +* *** amount is optional, default is 10 *** +* *** example: "chug a 20" will buy and drink 20 antidote potions *** +* *** can be used to specify a character *** * @Misc * quiet - stop announcing in chat * cow - enter red cow portal @@ -50,7 +55,7 @@ * quit - exit game * @todo * run - run a script -* run - run a script on +* run - run a script on * skills - list current attack skills * skills - list current attack skills for * @@ -689,12 +694,27 @@ function Follower() { } break; - } + default: + if (action.includes("talk")) { + talk(action.split(" ")[1]); + } else if (action.includes("chug")) { + let temp = action.toLowerCase().split(" "); + let [, type, amount] = temp; + amount === undefined && (amount = 10); + typeof amount === "string" && (amount = parseInt(amount, 10)); + + if (type === "a" || type === "antidote") { + Town.buyPots(amount, sdk.items.AntidotePotion, true, true); + } else if (type === "t" || type === "thawing") { + Town.buyPots(amount, sdk.items.ThawingPotion, true, true); + } else if (type === "s" || type === "stamina") { + Town.buyPots(amount, sdk.items.StaminaPotion, true, true); + } + } - if (action.includes("talk")) { - talk(action.split(" ")[1]); } + action = ""; delay(100); From f4a3c0be655e7da15c425331b3de2b790e9796fe Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 26 May 2023 20:16:42 -0400 Subject: [PATCH 129/758] Update Follower.js - fix actions, the who check was causing most of them to fail. --- d2bs/kolbot/libs/scripts/Follower.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 5e6d37389..eae761e05 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -64,6 +64,8 @@ function Follower() { const QuestData = require("../core/GameData/QuestData"); const commanders = []; + /** @type {Set} */ + const _players = new Set(); Config.Leader && commanders.push(Config.Leader); let piece, skill; let [allowSay, attack, openContainers, stop] = [true, true, true, false]; @@ -75,6 +77,19 @@ function Follower() { say(msg); }; + const playerInGame = function (name = "") { + if (!name) return false; + if (_players.has(name.toLowerCase())) return true; + let player = getParty(); + + if (player) { + do { + _players.add(player.name.toLowerCase()); + } while (player.getNext()); + } + return _players.has(name.toLowerCase()); + }; + /** * Change areas to where leader is * @param {Player} unit @@ -437,10 +452,13 @@ function Follower() { } } } else { - if (who && who !== me.name && who !== "all") { - return; + if (who) { + if (who === me.name || who === "all") { + msg = msg.replace(who, "").trim(); + } else if (playerInGame(who)) { + return; + } } - who && (msg = msg.replace(who, "").trim()); action = msg; } From a05b6c27d4c77e07a1740e85250d13770f482175 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 27 May 2023 11:27:06 -0400 Subject: [PATCH 130/758] Update Skill.js - Fix shared wereform skills, it was missing werewolf/werebear --- d2bs/kolbot/libs/core/Skill.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 2f62a41c3..9e446d7a3 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -255,9 +255,13 @@ // we don't even have the skills to transform or we aren't transformed - add handler for wereform given by an item that is on switch if (!Skill.canUse(sdk.skills.Werewolf) && !Skill.canUse(sdk.skills.Werebear)) return true; const shared = new Set([ - sdk.skills.Attack, sdk.skills.Kick, sdk.skills.Raven, sdk.skills.PoisonCreeper, - sdk.skills.OakSage, sdk.skills.SpiritWolf, sdk.skills.CarrionVine, sdk.skills.HeartofWolverine, - sdk.skills.SummonDireWolf, sdk.skills.FireClaws, sdk.skills.SolarCreeper, sdk.skills.Hunger, + sdk.skills.Attack, sdk.skills.Kick, + sdk.skills.Raven, sdk.skills.Werewolf, + sdk.skills.Werebear, sdk.skills.PoisonCreeper, + sdk.skills.OakSage, sdk.skills.SpiritWolf, + sdk.skills.CarrionVine, sdk.skills.HeartofWolverine, + sdk.skills.SummonDireWolf, sdk.skills.FireClaws, + sdk.skills.SolarCreeper, sdk.skills.Hunger, sdk.skills.SpiritofBarbs, sdk.skills.SummonGrizzly, sdk.skills.Armageddon ]); const wolfOnly = new Set([sdk.skills.FeralRage, sdk.skills.Rabies, sdk.skills.Fury]); From 878993c6a92f85169f3fc3d795c271baffc6d998 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 May 2023 18:45:41 -0400 Subject: [PATCH 131/758] Update SkillData.js - add missing enchant duration --- d2bs/kolbot/libs/core/GameData/SkillData.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 7b197a07f..c5822bb8a 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -288,6 +288,7 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.Enchant, + duration: () => (120 + (24 * me.getSkill(sdk.skills.Enchant, sdk.skills.subindex.SoftPoints))), }); skillMap.set(sdk.skills.ChainLightning, { hand: sdk.skills.hand.Left, From 8cd15da55409488bf4c250fbc7f96f672658188e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 May 2023 18:47:01 -0400 Subject: [PATCH 132/758] Update ControlBot.js - start of controlbot rebuild, todo is rusher commands --- d2bs/kolbot/libs/scripts/ControlBot.js | 626 +++++++++++++------------ 1 file changed, 314 insertions(+), 312 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 291e4a245..951d1bf6d 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -6,139 +6,154 @@ * */ -function ControlBot() { +function ControlBot () { const startTime = getTickCount(); + const chantDuration = Skill.getDuration(sdk.skills.Enchant); - /** - * @type {Object.} - */ - const cmdNicks = {}; - /** - * @type {Object.} - */ - const wpNicks = {}; + /** @constructor */ + function PlayerTracker () { + this.firstCmd = getTickCount(); + this.commands = 0; + this.ignored = false; + } - let command, nick; - let shitList = []; - const greet = []; + PlayerTracker.prototype.resetCmds = function () { + this.firstCmd = getTickCount(); + this.commands = 0; + }; - let controlCommands = ["help", "timeleft", "cows", "wps", "chant", "bo"]; - const commandDesc = { - "help": "Display commands", - "timeleft": "Remaining time left for this game", - "cows": "Open cow level", - "chant": "Enchant. AutoChant is " + (Config.ControlBot.Chant.AutoEnchant ? "ON" : "OFF"), - "wps": "Give waypoints", - "bo": "Bo at waypoint", + PlayerTracker.prototype.unIgnore = function () { + this.ignored = false; + this.commands = 0; }; - // remove commands we can't/aren't using - for (let i = 0; i < controlCommands.length; i++) { - switch (controlCommands[i]) { - case "cows": - if (!Config.ControlBot.Cows.MakeCows) { - controlCommands.splice(i, 1); - i--; - } + /** @constructor */ + function ChantTracker () { + this.lastChant = getTickCount(); + } - break; - case "chant": - if (!Config.ControlBot.Chant.Enchant || !me.getSkill(sdk.skills.Enchant, sdk.skills.subindex.SoftPoints)) { - Config.ControlBot.Chant.Enchant = false; - Config.ControlBot.Chant.AutoEnchant = false; - controlCommands.splice(i, 1); - i--; - } + ChantTracker.prototype.reChant = function () { + return getTickCount() - this.lastChant >= chantDuration - Time.minutes(1); + }; - break; - case "wps": - if (!Config.ControlBot.Wps.GiveWps) { - controlCommands.splice(i, 1); - i--; - } + ChantTracker.prototype.update = function () { + this.lastChant = getTickCount(); + }; - break; - case "bo": - if (!Config.ControlBot.Bo - || (!me.getSkill(sdk.skills.BattleOrders, sdk.skills.subindex.SoftPoints) - && Precast.haveCTA === -1)) { - Config.ControlBot.Bo = false; - controlCommands.splice(i, 1); - i--; - } + /** @type {Object.} */ + const cmdNicks = {}; + /** @type {Object.} */ + const wpNicks = {}; + /** @type {Set} */ + const shitList = new Set(); + /** @type {Array} */ + const greet = []; + /** @type {Map} */ + const wps = new Map([ + [1, [ + sdk.areas.ColdPlains, sdk.areas.StonyField, + sdk.areas.DarkWood, sdk.areas.BlackMarsh, + sdk.areas.OuterCloister, sdk.areas.JailLvl1, + sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2 + ] + ], + [2, [ + sdk.areas.A2SewersLvl2, sdk.areas.DryHills, + sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, + sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, + sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic + ] + ], + [3, [ + sdk.areas.SpiderForest, sdk.areas.GreatMarsh, + sdk.areas.FlayerJungle, sdk.areas.LowerKurast, + sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2 + ] + ], + [4, [ + sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame + ] + ], + [5, [ + sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, + sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, + sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 + ] + ] + ]); + + let command, nick; /** * @param {string} nick * @returns {boolean} */ const enchant = function (nick) { - if (!Config.ControlBot.Chant.Enchant) return false; - - if (!Misc.inMyParty(nick)) { - say("Accept party invite, noob."); - - return false; - } - - let unit = Game.getPlayer(nick); + try { + if (!Misc.inMyParty(nick)) { + throw new Error("Accept party invite, noob."); + } - if (unit && unit.distance > 35) { - say("Get closer."); + let unit = Game.getPlayer(nick); - return false; - } + if (unit && unit.distance > 35) { + throw new Error("Get closer."); + } - if (!unit) { - let partyUnit = getParty(nick); + if (!unit) { + let partyUnit = getParty(nick); - // wait until party area is readable? - if (partyUnit.inTown) { + if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { + throw new Error("You need to be in one of the towns."); + } + // wait until party area is readable? say("Wait for me at waypoint."); Town.goToTown(sdk.areas.actOf(partyUnit.area)); unit = Game.getPlayer(nick); - } else { - say("You need to be in one of the towns."); - - return false; } - } - - if (unit) { - do { - // player is alive - if (!unit.dead) { - if (unit.distance >= 35) { - say("You went too far away."); - return false; + if (unit) { + do { + // player is alive + if (!unit.dead) { + if (unit.distance >= 35) { + throw new Error("You went too far away."); + } + Packet.enchant(unit); + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chantList.has(unit.name) + ? chantList.get(unit.name).update() + : chantList.set(unit.name, new ChantTracker()); + } } + } while (unit.getNext()); + } else { + say("I don't see you"); + } - Packet.enchant(unit); - delay(500); - } - } while (unit.getNext()); - } else { - say("I don't see you"); - } + unit = Game.getMonster(); - unit = Game.getMonster(); + if (unit) { + do { + // merc or any other owned unit + if (unit.getParent() && unit.getParent().name === nick) { + Packet.enchant(unit); + delay(500); + } + } while (unit.getNext()); + } - if (unit) { - do { - // merc or any other owned unit - if (unit.getParent() && unit.getParent().name === nick) { - Packet.enchant(unit); - delay(500); - } - } while (unit.getNext()); + return true; + } catch (e) { + say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + + return false; } - - return true; }; /** @@ -148,61 +163,47 @@ function ControlBot() { const bo = function (nick) { if (!Config.ControlBot.Bo) return false; - if (!Misc.inMyParty(nick)) { - say("Accept party invite, noob."); - - return false; - } - - let partyUnit = getParty(nick); + try { + if (!Misc.inMyParty(nick)) { + throw new Error("Accept party invite, noob."); + } - // wait until party area is readable? - if (partyUnit.inTown) { - say("Can't bo you in town noob, go to a waypoint"); + let partyUnit = getParty(nick); - return false; - } else if (Pather.wpAreas.includes(partyUnit.area)) { + // wait until party area is readable? + if (!Misc.poll(() => Pather.wpAreas.includes(partyUnit.area), 500, 50)) { + throw new Error("Can't find you or you're not somewhere with a waypoint"); + } Pather.useWaypoint(partyUnit.area); - } else { - say("Can't find you or you're not somewhere with a waypoint"); - return false; - } - - let unit = Game.getPlayer(nick); + let unit = Game.getPlayer(nick); - if (unit && unit.distance > 15) { - say("Get closer."); - let waitTick = getTickCount(); - - while (unit && unit.distance > 15) { - if (getTickCount() - waitTick > 30e3) { - say("You took to long. Going back to town"); - return false; + if (unit && unit.distance > 15) { + say("Get closer."); + + if (!Misc.poll(() => unit.distance <= 15, Time.seconds(30), 50)) { + throw new Error("You took to long. Going back to town"); } - delay(150); } - } - if (unit && unit.distance <= 15 && !unit.dead) { - Misc.poll(function () { - Precast.doPrecast(true); - return unit.getState(sdk.states.BattleOrders); - }, 5000, 1000); - Pather.useWaypoint(sdk.areas.RogueEncampment); - } else { - say("I don't see you"); - } + if (unit && unit.distance <= 15 && !unit.dead) { + Misc.poll(function () { + Precast.doPrecast(true); + return unit.getState(sdk.states.BattleOrders); + }, 5000, 1000); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } else { + throw new Error("I don't see you"); + } - return true; + return true; + } catch (e) { + say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + + return false; + } }; - /** - * @type {Map= chantDuration - Time.minutes(1))) { - Packet.enchant(unit); - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); - chantList.set(unit.name, { lastChant: getTickCount() }); - } + if (unit === me.name || unit.dead) continue; + if (shitList.has(unit.name)) continue; + if (!Misc.inMyParty(unit.name) || unit.distance > 40) continue; + // allow rechanting someone if it's going to run out soon for them + if (!unit.getState(sdk.states.Enchant) + || (chantList.has(unit.name) && chantList.get(unit.name).reChant())) { + Packet.enchant(unit); + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + chantList.has(unit.name) + ? chantList.get(unit.name).update() + : chantList.set(unit.name, new ChantTracker()); } } } while (unit.getNext()); @@ -428,89 +428,78 @@ function ControlBot() { * @returns {boolean} */ const giveWps = function (nick) { - if (!Config.ControlBot.Wps.GiveWps) return false; - if (!Misc.inMyParty(nick)) { - say("Accept party invite, noob."); - - return false; - } + let next = false; + const nextWatcher = function (who, msg) { + if (who !== nick) return; + if (msg === "next") { + next = true; + } + }; - switch (getWpNick(nick)) { - case "maxrequests": - say(nick + ", you have spent all your waypoint requests for this game."); + try { + if (!Misc.inMyParty(nick)) { + throw new Error("Accept party invite, noob."); + } - return false; - case "mintime": - say(nick + ", you may request waypoints every 60 seconds."); + let reqCheck = getWpNick(nick); + if (reqCheck) { + let _eMsg = reqCheck === "maxrequests" + ? ", you have spent all your waypoint requests for this game." + : ", you may request waypoints every 60 seconds."; + throw new Error(nick + _eMsg); + } - return false; - case false: addWpNick(nick); - break; - } + let act = Misc.getPlayerAct(nick); + if (!wps.has(act)) return false; + addEventListener("chatmsg", nextWatcher); - let act = Misc.getPlayerAct(nick); - const wps = { - 1: [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.OuterCloister, sdk.areas.JailLvl1, sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2 - ], - 2: [ - sdk.areas.A2SewersLvl2, sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, - sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic - ], - 3: [ - sdk.areas.SpiderForest, sdk.areas.GreatMarsh, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, - sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2 - ], - 4: [sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame], - 5: [ - sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.CrystalizedPassage, - sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 - ] - }; - let wpList = wps[act]; + for (let wp of wps.get(act)) { + if (checkHostiles()) { + break; + } - for (let i = 0; i < wpList.length; i++) { - if (checkHostiles()) { - break; - } + try { + if (next) { + next = false; + continue; + } + Pather.useWaypoint(wp, true); + if (Config.ControlBot.Wps.SecurePortal) { + Attack.securePosition(me.x, me.y, 20, 1000); + } + Pather.makePortal(); + say(getAreaName(me.area) + " TP up"); - try { - Pather.useWaypoint(wpList[i], true); - Config.ControlBot.Wps.SecurePortal && Attack.securePosition(me.x, me.y, 20, 1000); - Pather.makePortal(); - say(getAreaName(me.area) + " TP up"); + if (!Misc.poll(() => (Game.getPlayer(nick) || next), Time.seconds(30), Time.seconds(1))) { + say("Aborting wp giving."); - for (let timeout = 0; timeout < 20; timeout++) { - if (Game.getPlayer(nick)) { break; } + next = false; - delay(1000); - } - - if (timeout >= 20) { - say("Aborting wp giving."); - - break; + delay(5000); + } catch (error) { + continue; } - - delay(5000); - } catch (error) { - continue; } - } - Town.doChores(); - Town.goToTown(1); - Town.move("portalspot"); + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); - wpNicks[nick].requests += 1; - wpNicks[nick].timer = getTickCount(); + wpNicks[nick].requests += 1; + wpNicks[nick].timer = getTickCount(); - return true; + return true; + } catch (e) { + say(e.message ? e.message : e); + + return false; + } finally { + removeEventListener("chatmsg", nextWatcher); + } }; const checkHostiles = function () { @@ -522,8 +511,8 @@ function ControlBot() { if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8)) { rval = true; - if (Config.ShitList && shitList.indexOf(party.name) === -1) { - shitList.push(party.name); + if (Config.ShitList && !shitList.has(party.name)) { + shitList.add(party.name); } } } while (party.getNext()); @@ -543,37 +532,31 @@ function ControlBot() { // ignore overhead messages if (!nick) return true; // ignore messages not related to our commands - if (controlCommands.indexOf(cmd.toLowerCase()) === -1) return false; + if (!actions.has(cmd.toLowerCase())) return false; if (!cmdNicks.hasOwnProperty(nick)) { - cmdNicks[nick] = { - firstCmd: getTickCount(), - commands: 0, - ignored: false - }; + cmdNicks[nick] = new PlayerTracker(); } if (cmdNicks[nick].ignored) { - if (getTickCount() - cmdNicks[nick].ignored < 60000) { + if (getTickCount() - cmdNicks[nick].ignored < Time.minutes(1)) { return true; // ignore flooder } // unignore flooder - cmdNicks[nick].ignored = false; - cmdNicks[nick].commands = 0; + cmdNicks[nick].unIgnore(); } cmdNicks[nick].commands += 1; - if (getTickCount() - cmdNicks[nick].firstCmd < 10000) { + if (getTickCount() - cmdNicks[nick].firstCmd < Time.seconds(10)) { if (cmdNicks[nick].commands > 5) { cmdNicks[nick].ignored = getTickCount(); say(nick + ", you are being ignored for 60 seconds because of flooding."); } } else { - cmdNicks[nick].firstCmd = getTickCount(); - cmdNicks[nick].commands = 0; + cmdNicks[nick].resetCmds(); } return false; @@ -584,18 +567,16 @@ function ControlBot() { * @param {string} msg * @returns {boolean} */ - function chatEvent(nick, msg) { - if (shitList.includes(nick)) { + function chatEvent (nick, msg) { + if (shitList.has(nick)) { say("No commands for the shitlisted."); - - return; + } else { + command = [msg, nick]; } - - command = [msg, nick]; } // eslint-disable-next-line no-unused-vars - function gameEvent(mode, param1, param2, name1, name2) { + function gameEvent (mode, param1, param2, name1, name2) { switch (mode) { case 0x02: // idle in town @@ -605,9 +586,92 @@ function ControlBot() { } } + /** @type {Map { + str += (key + " (" + value.desc + "), "); + }); + say("Commands: " + str); + } + }); + _actions.set("timeleft", { + desc: "Remaining time left for this game", + hostileCheck: false, + run: function () { + let tick = Time.minutes(Config.ControlBot.GameLength) - getTickCount() + startTime; + let m = Math.floor(tick / 60000); + let s = Math.floor((tick / 1000) % 60); + + say( + "Time left: " + + (m ? m + " minute" + (m > 1 ? "s" : "") + + ", " : "") + s + " second" + (s > 1 ? "s." : ".") + ); + } + }); + + if (Config.ControlBot.Chant.Enchant + && Skill.canUse(sdk.skills.Enchant)) { + ["chant", "enchant"] + .forEach(key => _actions.set(key, { + desc: "Give enchant", + hostileCheck: false, + run: enchant + })); + } + + if (Config.ControlBot.Cows.MakeCows && !me.cows) { + _actions.set("cows", { + desc: "Open cow level", + hostileCheck: true, + run: openPortal + }); + } + + if (Config.ControlBot.Wps.GiveWps) { + _actions.set("wps", { + desc: "Give waypoints in act", + hostileCheck: true, + run: giveWps + }); + } + + if (Config.ControlBot.Bo + && (Skill.canUse(sdk.skills.BattleOrders) || Precast.haveCTA > 0)) { + _actions.set("bo", { + desc: "Bo at waypoint", + hostileCheck: true, + run: bo + }); + } + + return _actions; + })(); + + const runAction = function (command) { + if (!command || command.length < 2) return false; + let [cmd, nick] = command; + + if (!actions.has(cmd.toLowerCase())) return false; + let action = actions.get(cmd.toLowerCase()); + if (action.hostileCheck && checkHostiles()) { + say("Command disabled because of hostiles."); + return false; + } + + return action.run(nick); + }; + // START include("oog/ShitList.js"); - Config.ShitList && (shitList = ShitList.read()); + Config.ShitList && shitList.add(ShitList.read()); try { addEventListener("chatmsg", chatEvent); @@ -616,81 +680,19 @@ function ControlBot() { Town.goToTown(1); Town.move("portalspot"); - const spot = { x: me.x, y: me.y }; - while (true) { while (greet.length > 0) { nick = greet.shift(); - if (shitList.indexOf(nick) === -1) { + if (!shitList.has(nick)) { say("Welcome, " + nick + "! For a list of commands say 'help'"); } } - spot.distance > 10 && Pather.moveTo(spot.x, spot.y); + Town.getDistance("portalspot") > 5 && Town.move("portalspot"); if (command && !floodCheck(command)) { - let hostile = checkHostiles(); - - switch (command[0].toLowerCase()) { - case "help": - let str = ""; - controlCommands.forEach((cmd) => { - str += (cmd + " (" + commandDesc[cmd] + "), "); - }); - - say("Commands:"); - say(str); - - break; - case "timeleft": - let tick = Time.minutes(Config.ControlBot.GameLength) - getTickCount() + startTime; - let m = Math.floor(tick / 60000); - let s = Math.floor((tick / 1000) % 60); - - say( - "Time left: " - + (m ? m + " minute" + (m > 1 ? "s" : "") - + ", " : "") + s + " second" + (s > 1 ? "s." : ".") - ); - - break; - case "chant": - enchant(command[1]); - - break; - case "cows": - if (hostile) { - say("Command disabled because of hostiles."); - - break; - } - - openPortal(command[1]); - me.cancel(); - - break; - case "wps": - if (hostile) { - say("Command disabled because of hostiles."); - - break; - } - - giveWps(command[1]); - - break; - case "bo": - if (hostile) { - say("Command disabled because of hostiles."); - - break; - } - - bo(command[1]); - - break; - } + runAction(command); } command = ""; From d32699b55ae3d19f3ab3303ef50a20fd06f4841e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 May 2023 18:50:42 -0400 Subject: [PATCH 133/758] Update Follower.js - quite a bit of refactor done. Working to improve the chat msg system and remove the large switch statements. Currently have abstracted most of the main functionality away from the mainloop. - added `taxi` command, can either take an areaId or has a list of places it can be commanded to travel --- d2bs/kolbot/libs/scripts/Follower.js | 994 +++++++++++++++++---------- 1 file changed, 640 insertions(+), 354 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index eae761e05..59986b338 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -15,44 +15,48 @@ * - tell specific character to perform action * @Attack * a - attack toggle for all -* a - attack toggle for * aon - attack on for all -* aon - attack on for * aoff - attack off for all -* aoff - attack off for +* - tell specific character to perform action * @Teleport *** characters without teleport skill will ignore tele command *** * tele - toggle teleport for all -* tele - toggle teleport for * tele on - teleport on for all -* tele on - teleport on for * tele off - teleport off for all -* tele off - teleport off for -* @Skills *** refer to skills.txt *** -* all skill - change skill for all. refer to skills.txt -* skill - change skill for -* skill - change skill for all characters of certain class -* *** any part of class name will do *** for example: "sorc skill 36", "zon skill 0", "din skill 106" -* Auras: *** refer to skills.txt *** +* - tell specific character to perform action +* @Skills *** refer to skills.txt or modules/sdk.js *** +* skill - change skill character(s) +* ~~~~~~~~~~~~~~~~~~~ Examples ~~~~~~~~~~~~~~~~~~~~~~ +* | NOTE: *** any part of class name will do *** | +* | "sorc skill 36", "zon skill 0", "din skill 106" | +* | "all skill 0", "myhdin skill 112" | +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Auras: *** refer to skills.txt or modules/sdk.js *** * all aura - change aura for all paladins * aura - change aura for * @Town * a2-5 - move to appropriate act (after quest) !NOTE: Disable 'no sound' or game will crash! * talk - talk to a npc in town * chug - buy and drink special potions potions from Akara -* *** type can be: a, antidote, t, thawing, s, stamina *** -* *** amount is optional, default is 10 *** -* *** example: "chug a 20" will buy and drink 20 antidote potions *** -* *** can be used to specify a character *** +* ~~~~~~~~~~~~~~~~~~~ Examples ~~~~~~~~~~~~~~~~~~~~~~ +* | NOTE: *** type can be: a, antidote, t, thawing, s, stamina *** | +* | "chug a 20" will buy and drink 20 antidote potions | +* | "chug t" will buy and drink 10 thawing potions | +* | "slowpoke chug s 5" slowpoke to buy and drink 5 stamina potions| +* |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| +* - tell specific character to perform action * @Misc +* tp - make a TP. Needs a TP tome if not using custom libs. * quiet - stop announcing in chat * cow - enter red cow portal * wp - all players activate a nearby wp -* wp - activates a nearby wp * bo - barbarian precast -* tp - make a TP. Needs a TP tome if not using custom libs. * move - move in a random direction (use if you're stuck by followers) * reload - reload script. Use only in case of emergency, or after editing character config. +* taxi - travels to area and makes portal. +* taxi - travels to area and makes portal. See taxiMap below or use "taxi list" for list of areas. +* taxi list - list of areas for taxi command * quit - exit game +* - tell specific character to perform action * @todo * run - run a script * run - run a script on @@ -61,22 +65,402 @@ * */ -function Follower() { +function Follower () { const QuestData = require("../core/GameData/QuestData"); - const commanders = []; + /** @type {Map} */ + const areas = new Map(); /** @type {Set} */ const _players = new Set(); - Config.Leader && commanders.push(Config.Leader); - let piece, skill; + /** @type {Array} */ + const actions = []; + /** @type {Set} */ + const commanders = new Set(); + Config.Leader && commanders.add(Config.Leader); let [allowSay, attack, openContainers, stop] = [true, true, true, false]; - let [leader, leaderUnit] = [null, null]; - let action = ""; + + /** @type {Map _actions.set(key, () => { + Pather.teleport = !Pather.teleport; + announce("Teleport " + (Pather.teleport ? "on" : "off")); + })); + ["tele off", (me.name + " tele off")] + .forEach(key => _actions.set(key, () => { + Pather.teleport = false; + announce("Teleport off."); + })); + ["tele on", (me.name + " tele on")] + .forEach(key => _actions.set(key, () => { + Pather.teleport = true; + announce("Teleport on."); + })); + ["a", (me.name + " a")] + .forEach(key => _actions.set(key, () => { + attack = !attack; + announce("Attack " + (attack ? "on" : "off")); + })); + ["aon", (me.name + " aon")] + .forEach(key => _actions.set(key, () => { + attack = true; + announce("Attack on."); + })); + ["aoff", (me.name + " aoff")] + .forEach(key => _actions.set(key, () => { + attack = false; + announce("Attack off."); + })); + ["s", (me.name + " s")] + .forEach(key => _actions.set(key, () => { + stop = !stop; + announce((stop ? "Stopping." : "Resuming.")); + })); + ["quiet", (me.name + " quiet")] + .forEach(key => _actions.set(key, () => { + allowSay = !allowSay; + console.log("Allow say: " + allowSay); + })); + + return _actions; + })(); + + /** @type {Map _actions.set(key, () => { + Packet.flash(me.gid, me.getPingDelay()); + })); + ["quit", (me.name + " quit")] + .forEach(key => _actions.set(key, () => { + quit(); + })); + ["r", (me.name + " r")] + .forEach(key => _actions.set(key, () => { + revive(); + })); + ["move", (me.name + " move")] + .forEach(key => _actions.set(key, () => { + let coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); + Pather.moveTo(coord.x, coord.y); + })); + ["pre", (me.name + " pre")] + .forEach(key => _actions.set(key, () => { + Precast.doPrecast(true); + })); + ["bo", (me.name + " bo")] + .forEach(key => _actions.set(key, () => { + !me.inTown && Precast.doPrecast(true); + })); + ["h", (me.name + " h")] + .forEach(key => _actions.set(key, () => { + !me.inTown && Skill.cast(sdk.skills.Howl); + })); + + return _actions; + })(); + + /** @type {Map _actions.set(key, () => { + if (me.inArea(sdk.areas.MooMooFarm)) return; + Town.goToTown(1); + Town.move("portalspot"); + if (!Pather.usePortal(sdk.areas.MooMooFarm)) { + announce("Failed to enter red cow portal."); + } + })); + ["wp", (me.name + " wp")] + .forEach(key => _actions.set(key, () => { + if (me.inTown) return; + if (getWaypoint(Pather.wpAreas.indexOf(me.area))) return; + if (!Pather.wpAreas.includes(me.area)) return; + if (Pather.getWP(me.area)) { + announce("Got Wp in " + getAreaName(me.area)); + } + })); + ["c", (me.name + " c")] + .forEach(key => _actions.set(key, () => { + !me.inTown && Town.getCorpse(); + })); + ["p", (me.name + " p")] + .forEach(key => _actions.set(key, () => { + announce("!Picking items."); + Pickit.pickItems(); + openContainers && Misc.openChests(20); + announce("!Done picking."); + })); + ["1", (me.name + " 1")] + .forEach(key => _actions.set(key, () => { + if (me.inTown && Leader.partyUnit.inTown && Misc.getPlayerAct(Config.Leader) !== me.act) { + announce("Going to leader's town."); + Town.goToTown(Misc.getPlayerAct(Config.Leader)); + Town.move("portalspot"); + } else if (me.inTown) { + announce("Going outside."); + Town.goToTown(Misc.getPlayerAct(Config.Leader)); + Town.move("portalspot"); + + if (!Pather.usePortal(null, Leader.partyUnit.name)) { + if (!checkExit(Leader.partyUnit, Leader.partyUnit.area)) { + return; + } + } + + let _timeout = getTickCount() + Time.minutes(2); + while (!Misc.getPlayerUnit(Config.Leader) && !me.dead) { + if (getTickCount() > _timeout) { + announce("Leader not found."); + Town.goToTown(); + break; + } + Attack.clear(10); + delay(200); + } + } + })); + ["2", (me.name + " 2")] + .forEach(key => _actions.set(key, () => { + if (!me.inTown) { + delay(150); + announce("Going to town."); + getUnits(sdk.unittype.Object) + .filter(unit => unit.classid === sdk.objects.BluePortal + && unit.area === me.area && [Leader.partyUnit.name, me.name].includes(unit.getParent())) + .sort((a, b) => a.distance - b.distance) + .some(portal => Pather.usePortal(null, null, portal)); + // Pather.usePortal(null, Leader.unit.name) || Pather.usePortal(sdk.areas.townOf(me.area)); + } + })); + ["3", (me.name + " 3")] + .forEach(key => _actions.set(key, () => { + if (!me.inTown) return; + announce("Running town chores"); + Town.doChores(); + Town.move("portalspot"); + announce("Ready"); + })); + ["a2", "a3", "a4", "a5"] + .forEach((key, index) => { + _actions.set(key, () => { changeAct(index + 2); }); + _actions.set(me.name + " " + key, () => { changeAct(index + 2); }); + }); + _actions.set(me.name + " tp", () => { + if (!Pather.makePortal()) { + announce("No TP scrolls or tomes."); + } + }); + + return _actions; + })(); + + /** @type {Map { + Pather.journeyTo(sdk.areas.DenofEvil); + } + ], + [ + "bloodraven", () => { + Pather.journeyTo(sdk.areas.BurialGrounds); + Pather.moveNearPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven, 15); + } + ], + [ + "tree", () => { + Pather.journeyTo(sdk.areas.DarkWood); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5); + } + ], + [ + "trist", () => { + Pather.journeyTo(sdk.areas.Tristram); + } + ], + [ + "pit", () => { + Pather.journeyTo(sdk.areas.PitLvl1); + } + ], + [ + "countess", () => { + Pather.journeyTo(sdk.areas.TowerCellarLvl5); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); + } + ], + [ + "andy", () => { + Pather.journeyTo(sdk.areas.CatacombsLvl4); + } + ], + [ + "rad", () => { + Pather.journeyTo(sdk.areas.A2SewersLvl3); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricScrollChest, 5); + } + ], + [ + "cube", () => { + Pather.journeyTo(sdk.areas.HallsoftheDeadLvl3); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricCubeChest, 15); + } + ], + [ + "amulet", () => { + Pather.journeyTo(sdk.areas.ClawViperTempleLvl2); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ViperAmuletChest); + } + ], + [ + "staff", () => { + Pather.journeyTo(sdk.areas.HallsoftheDeadLvl3); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); + } + ], + [ + "summoner", () => { + Pather.journeyTo(sdk.areas.ArcaneSanctuary); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.Journal, 15); + } + ], + [ + "staff-altar", () => { + Pather.journeyTo(sdk.areas.CanyonofMagic); + Pather.moveToExit(getRoom().correcttomb, true); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricStaffHolder, 5); + } + ], + [ + "duriel", () => { + Pather.journeyTo(sdk.areas.DurielsLair); + } + ], + [ + "eye", () => { + Pather.journeyTo(sdk.areas.SpiderCavern); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsEyeChest); + } + ], + [ + "brain", () => { + Pather.journeyTo(sdk.areas.FlayerDungeonLvl3); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest); + } + ], + [ + "heart", () => { + Pather.journeyTo(sdk.areas.A3SewersLvl2); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsHeartChest); + } + ], + [ + "council", () => { + Pather.journeyTo(sdk.areas.Travincal); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.CompellingOrb); + } + ], + [ + "meph", () => { + Pather.journeyTo(sdk.areas.DuranceofHateLvl3); + Pather.moveTo(17590, 8068); + } + ], + [ + "izual", () => { + Pather.journeyTo(sdk.areas.PlainsofDespair); + Pather.moveNearPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Izual, 20); + } + ], + [ + "forge", () => { + Pather.journeyTo(sdk.areas.RiverofFlame); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge, 5); + } + ], + [ + "chaos", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + } + ], + [ + "viz", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealVizier); + } + ], + [ + "seis", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealSeis); + } + ], + [ + "infector", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealInfector); + } + ], + [ + "anya", () => { + Pather.journeyTo(sdk.areas.FrozenRiver); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, 5); + } + ], + [ + "ancients", () => { + Pather.journeyTo(sdk.areas.ArreatSummit); + } + ], + [ + "nith", () => { + Pather.journeyTo(sdk.areas.HallsofVaught); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform); + } + ], + [ + "throne", () => { + Pather.journeyTo(sdk.areas.ThroneofDestruction); + } + ], + ]); + + /** + * @todo allow user to use skill name and try to match it to skill id + */ + const skillsMap = (function () { + const _skills = new Map(); + + for (let value of Object.values(sdk.skills)) { + if (typeof value === "number") { + _skills.set(getSkillById(value), value); + } + } + + return _skills; + })(); const announce = function (msg = "") { if (!allowSay) return; say(msg); }; + const revive = function () { + while (!me.inTown) { + me.revive(); + delay(1000); + } + + Town.move("portalspot"); + announce("I'm alive!"); + }; + const playerInGame = function (name = "") { if (!name) return false; if (_players.has(name.toLowerCase())) return true; @@ -92,38 +476,48 @@ function Follower() { /** * Change areas to where leader is - * @param {Player} unit + * @param {Party} unit * @param {number} area * @returns {boolean} */ const checkExit = function (unit, area) { - if (unit.inTown) return false; - - let target; - let exits = getArea().exits; + if (unit.inTown && me.inTown) return false; - for (let i = 0; i < exits.length; i += 1) { - if (exits[i].target === area) { - return 1; + /** @type {Exit[]} */ + let exits = []; + if (areas.has(me.area)) { + exits = areas.get(me.area).exits; + } else { + let _area = getArea(me.area); + if (_area) { + exits = _area.exits; + areas.set(me.area, _area); + } + } + + for (let exit of exits) { + if (exit.target === area) { + announce("Taking exit to " + getAreaName(exit.target)); + return Pather.moveToExit(area, true); } } if (unit.inTown) { - target = Game.getObject("waypoint"); + let wp = Game.getObject("waypoint"); - if (target && getDistance(me, target) < 20) { - return 3; + if (wp && wp.distance < 30) { + announce("Taking waypoint to " + getAreaName(area)); + return Pather.useWaypoint(area, true); } } - target = Game.getObject("portal"); + let target = Game.getObject("portal"); if (target) { do { if (target.objtype === area) { - Pather.usePortal(null, null, target); - - return 2; + announce("Taking portal to " + getAreaName(area)); + return Pather.usePortal(null, null, target); } } while (target.getNext()); } @@ -131,26 +525,31 @@ function Follower() { // Arcane<->Cellar portal if ((me.inArea(sdk.areas.ArcaneSanctuary) && area === sdk.areas.PalaceCellarLvl3) || (me.inArea(sdk.areas.PalaceCellarLvl3) && area === sdk.areas.ArcaneSanctuary)) { - Pather.usePortal(null); - - return 4; + announce("Special transit to " + getAreaName(area)); + return Pather.usePortal(null); } // Tal-Rasha's tomb->Duriel's lair - if (me.area >= sdk.areas.TalRashasTomb1 && me.area <= sdk.areas.TalRashasTomb7 && area === sdk.areas.DurielsLair) { - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, area); + if (me.area >= sdk.areas.TalRashasTomb1 + && me.area <= sdk.areas.TalRashasTomb7 + && area === sdk.areas.DurielsLair) { + announce("Special transit to " + getAreaName(area)); + return Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, area); + } - return 4; + // durance 3 -> pandemonium fortress + if (me.inArea(sdk.areas.DuranceofHateLvl3) && area === sdk.areas.PandemoniumFortress) { + announce("Special transit to " + getAreaName(area)); + return Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); } // Throne->Chamber if (me.inArea(sdk.areas.ThroneofDestruction) && area === sdk.areas.WorldstoneChamber) { - target = Game.getObject(sdk.objects.WorldstonePortal); + let wsp = Game.getObject(sdk.objects.WorldstonePortal); - if (target) { - Pather.usePortal(null, null, target); - - return 4; + if (wsp) { + announce("Special transit to " + getAreaName(area)); + return Pather.usePortal(null, null, wsp); } } @@ -359,133 +758,84 @@ function Follower() { */ const chatEvent = function (nick, msg) { if (msg && nick === Config.Leader) { - switch (msg) { - case "tele": - case me.name + " tele": - Pather.teleport = !Pather.teleport; - announce("Teleport " + (Pather.teleport ? "on" : "off")); + if (toggleActions.has(msg)) { + toggleActions.get(msg)(); - break; - case "tele off": - case me.name + " tele off": - Pather.teleport = false; - announce("Teleport off."); + return; + } else if (quickActions.has(msg)) { + quickActions.get(msg)(); - break; - case "tele on": - case me.name + " tele on": - Pather.teleport = true; - announce("Teleport on."); + return; + } else if (specialActions.has(msg)) { + actions.push(msg); - break; - case "a": - case me.name + " a": - attack = !attack; - announce("Attack " + (attack ? "on" : "off")); - - break; - case "flash": - Packet.flash(me.gid); - - break; - case "quiet": - allowSay = !allowSay; - - break; - case "aoff": - case me.name + " aoff": - attack = false; - announce("Attack off."); - - break; - case "aon": - case me.name + " aon": - attack = true; - announce("Attack on."); - - break; - case "quit": - case me.name + " quit": - quit(); - - break; - case "s": - case me.name + " s": - stop = !stop; - announce((stop ? "Stopping." : "Resuming.")); - - break; - case "r": - me.dead && me.revive(); - - break; - default: - let piecewise = msg.split(" "); - let who = piecewise.length > 1 && piecewise.first() || ""; + return; + } + let piecewise = msg.split(" "); + let who = piecewise.length > 1 && piecewise.first() || ""; + const isForMe = (charClass.includes(who) || who === me.name || who === "all"); - if (me.paladin && msg.includes("aura ")) { - if (who === me.name || piece === "all") { - skill = parseInt(msg.split(" ")[2], 10); + if (me.paladin && isForMe && msg.includes("aura ")) { + let aura = parseInt(msg.split(" ")[2], 10); - if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { - announce("Active aura is: " + skill); + if (me.getSkill(aura, sdk.skills.subindex.SoftPoints)) { + announce("Active aura is: " + aura); - Config.AttackSkill[2] = skill; - Config.AttackSkill[4] = skill; + Config.AttackSkill[2] = aura; + Config.AttackSkill[4] = aura; - Skill.setSkill(skill, sdk.skills.hand.Right); - } else { - announce("I don't have that aura."); - } - } - } else if (msg.includes("skill ")) { - if (charClass.includes(who) || who === me.name || who === "all") { - skill = parseInt(msg.split(" ")[2], 10); + Skill.setSkill(aura, sdk.skills.hand.Right); + } else { + announce("I don't have that aura."); + } + } else if (isForMe && msg.includes("skill ")) { + let skill = parseInt(msg.split(" ")[2], 10); - if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { - announce("Attack skill is: " + skill); + if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { + announce("Attack skill is: " + skill); - Config.AttackSkill[1] = skill; - Config.AttackSkill[3] = skill; - } else { - announce("I don't have that skill."); - } - } + Config.AttackSkill[1] = skill; + Config.AttackSkill[3] = skill; } else { - if (who) { - if (who === me.name || who === "all") { - msg = msg.replace(who, "").trim(); - } else if (playerInGame(who)) { - return; - } + announce("I don't have that skill."); + } + } else { + if (who) { + if (isForMe) { + msg = msg.replace(who, "").trim(); + } else if (playerInGame(who)) { + return; } - action = msg; } - - break; + actions.push(msg); } - } - - if (msg && msg.split(" ")[0] === "leader" && (commanders.includes(nick) || !commanders.length)) { - piece = msg.split(" ")[1]; + } else if (msg && msg.split(" ")[0] === "leader" && (commanders.has(nick) || !commanders.size)) { + let piece = msg.split(" ")[1]; if (typeof piece === "string") { - if (commanders.indexOf(piece) === -1) { - commanders.push(piece); - } - + commanders.add(piece); announce("Switching leader to " + piece); Config.Leader = piece; - leader = Misc.findPlayer(Config.Leader); - leaderUnit = Misc.getPlayerUnit(Config.Leader); + Leader.partyUnit = Misc.findPlayer(Config.Leader); + Leader.unit = Misc.getPlayerUnit(Config.Leader); } } }; + const gameEvent = function (mode, param1, param2, name1, name2) { + if (name1 === Config.Leader + && mode === 0x07 + && param1 === 0x02 + && param2 === 0x09) { + recheck = true; + } + }; // START + let recheck = false; addEventListener("chatmsg", chatEvent); + addEventListener("gameevent", gameEvent); openContainers && Config.OpenChests.enabled && Config.OpenChests.Types.push("all"); // Override config values that use TP @@ -493,34 +843,53 @@ function Follower() { Config.TownHP = 0; Config.TownMP = 0; const charClass = sdk.player.class.nameOf(me.classid).toLowerCase(); - leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.seconds(20), Time.seconds(1)); + const Leader = new function () { + /** @type {Party} */ + this.partyUnit = null; + /** @type {Player} */ + this.unit = null; + }; + + Leader.partyUnit = Misc.poll( + () => Misc.findPlayer(Config.Leader), + Time.minutes(3), + Time.seconds(1) + ); - if (!leader) { + if (!Leader.partyUnit) { announce("Leader not found."); - delay(1000); - quit(); - } else { - announce("Leader found."); + scriptBroadcast("quit"); + + return true; } + announce("Leader found :: " + Leader.partyUnit.name); + while (!Misc.inMyParty(Config.Leader)) { delay(500); } announce("Partied."); + if (Leader.partyUnit.inTown && !me.inTown) { + announce("Going to leader's town."); + Town.goToTown(sdk.areas.actOf(Leader.partyUnit.area)); + } me.inTown && Town.move("portalspot"); + Leader.unit = Misc.getPlayerUnit(Config.Leader); // Main Loop - while (Misc.inMyParty(Config.Leader)) { - if (me.mode === sdk.player.mode.Dead) { - while (!me.inTown) { - me.revive(); - delay(1000); + while (true) { + if (recheck) { + if (!Misc.poll(() => Misc.inMyParty(Config.Leader), Time.minutes(1), Time.seconds(1))) { + announce("Leader left party."); + + break; } - - Town.move("portalspot"); - announce("I'm alive!"); + recheck = false; + } + if (me.mode === sdk.player.mode.Dead) { + revive(); } while (stop) { @@ -528,215 +897,132 @@ function Follower() { } if (!me.inTown) { - if (!leaderUnit || !copyUnit(leaderUnit).x) { - leaderUnit = Misc.getPlayerUnit(Config.Leader); - - if (leaderUnit) { - announce("Leader unit found."); - } - } - - if (!leaderUnit) { - let player = Game.getPlayer(); - - if (player) { - do { - if (player.name !== me.name) { - Pather.moveToUnit(player); + try { + if (!Leader.unit) throw new Error("Leader not found."); + if (!Leader.partyUnit) throw new Error("party unit not found."); + if (me.inArea(Leader.partyUnit.area) && copyUnit(Leader.unit).x) { + if (Leader.unit.distance > 10) { + // Pather.moveToUnit(Leader.unit); + Pather.moveToEx( + Leader.unit.x, Leader.unit.y, { callback: () => ( + Leader.unit && Leader.unit.distance < 10 + ) } + ); + } + } else { + if (!me.inArea(Leader.partyUnit.area) && !me.inTown) { + while (Leader.partyUnit.area === 0) delay(100); + checkExit(Leader.partyUnit, Leader.partyUnit.area); - break; + while (me.area === 0) { + delay(100); } - } while (player.getNext()); + } } - } - - if (leaderUnit && getDistance(me.x, me.y, leaderUnit.x, leaderUnit.y) <= 60) { - if (getDistance(me.x, me.y, leaderUnit.x, leaderUnit.y) > 4) { - Pather.moveToUnit(leaderUnit); + } catch (e) { + console.error(e); + if (Leader.partyUnit) { + if (me.inArea(Leader.partyUnit.area)) { + Pather.moveToEx( + Leader.partyUnit.x, Leader.partyUnit.y, { callback: () => { + Leader.unit = Misc.getPlayerUnit(Config.Leader); + return Leader.unit && Leader.unit.distance < 10; + } } + ); + } else if (Leader.partyUnit.inTown) { + // go back to town if there are there for awhile + if (!Misc.poll( + () => (Leader.unit = Misc.getPlayerUnit(Config.Leader)), + Time.seconds(45), + Time.seconds(1)) + ) { + Town.goToTown(sdk.areas.actOf(Leader.partyUnit.area)); + Town.move("portalspot"); + } + } + } else { + Leader.partyUnit = Misc.findPlayer(Config.Leader); } } if (attack) { + // custom attack needs to be done so we can keep track of leader while we attack in break + // early if leader moves on Attack.clear(20, false, false, false, true); pickPotions(20); } - me.paladin && Config.AttackSkill[2] > 0 && Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - - if (leader.area !== me.area && !me.inTown) { - while (leader.area === 0) { - delay(100); - } - - let result = checkExit(leader, leader.area); - - switch (result) { - case 1: - announce("Taking exit."); - delay(500); - Pather.moveToExit(leader.area, true); - - break; - case 2: - announce("Taking portal."); - - break; - case 3: - announce("Taking waypoint."); - delay(500); - Pather.useWaypoint(leader.area, true); - - break; - case 4: - announce("Special transit."); - - break; - } - - while (me.area === 0) { - delay(100); - } - - leaderUnit = Misc.getPlayerUnit(Config.Leader); - } - } - - MainSwitch: - switch (action) { - case "cow": - if (me.inArea(sdk.areas.RogueEncampment)) { - Town.move("portalspot"); - !Pather.usePortal(sdk.areas.MooMooFarm) && announce("Failed to use cow portal."); + if (me.paladin && Config.AttackSkill[2] > 0) { + Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); } - - break; - case "move": - let coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); - Pather.moveTo(coord.x, coord.y); - - break; - case "wp": - case me.name + "wp": - if (me.inTown) { - break; - } - - delay(rand(1, 3) * 500); - - if (Game.getObject("waypoint")) { - for (let retry = 0; retry < 3; retry++) { - if (Pather.getWP(me.area)) { - announce("Got wp."); - break MainSwitch; - } - } - - announce("Failed to get wp."); - } - - me.cancel(); - - break; - case "c": - !me.inTown && Town.getCorpse(); - - break; - case "p": - announce("!Picking items."); - Pickit.pickItems(); - openContainers && Misc.openChests(20); - announce("!Done picking."); - - break; - case "1": - if (me.inTown && leader.inTown && Misc.getPlayerAct(Config.Leader) !== me.act) { - announce("Going to leader's town."); - Town.goToTown(Misc.getPlayerAct(Config.Leader)); - Town.move("portalspot"); - } else if (me.inTown) { - announce("Going outside."); - Town.goToTown(Misc.getPlayerAct(Config.Leader)); - Town.move("portalspot"); - - if (!Pather.usePortal(null, leader.name)) { - break; - } - - while (!Misc.getPlayerUnit(Config.Leader) && !me.dead) { - Attack.clear(10); - delay(200); - } - } - - break; - case "2": - if (!me.inTown) { - delay(150); - announce("Going to town."); - Pather.usePortal(null, leader.name); - } - - break; - case "3": - if (me.inTown) { - announce("Running town chores"); + } else if (!actions.length && getTickCount() - Town.lastChores > Time.minutes(3)) { + // no actions currently, lets do some town chores + if (me.gold > 1000 + && (me.needPotions() || Town.checkScrolls(sdk.items.TomeofTownPortal) < 5)) { Town.doChores(); - Town.move("portalspot"); - announce("Ready"); - } - - break; - case "h": - me.barbarian && Skill.cast(sdk.skills.Howl); - - break; - case "pre": - Precast.doPrecast(true); - - break; - case "bo": - // checks if we have cta or warcries - Precast.needOutOfTownCast() && Precast.doPrecast(true); - - break; - case "a2": - case "a3": - case "a4": - case "a5": - changeAct(parseInt(action[1], 10)); - - break; - case me.name + " tp": - if (!Pather.makePortal()) { - announce("No TP scrolls or tomes."); } + Town.getDistance("portalspot") > 5 && Town.move("portalspot"); + } - break; - default: - if (action.includes("talk")) { - talk(action.split(" ")[1]); - } else if (action.includes("chug")) { - let temp = action.toLowerCase().split(" "); - let [, type, amount] = temp; - amount === undefined && (amount = 10); - typeof amount === "string" && (amount = parseInt(amount, 10)); - - if (type === "a" || type === "antidote") { - Town.buyPots(amount, sdk.items.AntidotePotion, true, true); - } else if (type === "t" || type === "thawing") { - Town.buyPots(amount, sdk.items.ThawingPotion, true, true); - } else if (type === "s" || type === "stamina") { - Town.buyPots(amount, sdk.items.StaminaPotion, true, true); + if (actions.length) { + let action = actions.shift(); + + if (specialActions.has(action)) { + specialActions.get(action)(); + } else { + switch (action) { + case "reload": + case me.name + "reload": + scriptBroadcast("reload"); + + return true; + default: + console.log(action); + if (action.includes("talk")) { + talk(action.split(" ")[1]); + } else if (action.includes("chug")) { + let temp = action.toLowerCase().split(" "); + let [, type, amount] = temp; + amount === undefined && (amount = 10); + typeof amount === "string" && (amount = parseInt(amount, 10)); + + if (type === "a" || type === "antidote") { + Town.buyPots(amount, sdk.items.AntidotePotion, true, true); + } else if (type === "t" || type === "thawing") { + Town.buyPots(amount, sdk.items.ThawingPotion, true, true); + } else if (type === "s" || type === "stamina") { + Town.buyPots(amount, sdk.items.StaminaPotion, true, true); + } + } else if (action.includes("taxi")) { + let [, where] = action.split(" "); + console.log(where); + if (where) { + try { + if (taxiMap.has(where)) { + taxiMap.get(where)(); + } else { + let _areaId = parseInt(where, 10); + Pather.journeyTo(_areaId); + } + Pather.makePortal(); + } catch (e) { + console.error(e); + announce("Failed to taxi to " + where); + Town.goToTown(); + } + } else if (action === "taxi list") { + announce("Taxi destinations: " + Array.from(taxiMap.keys()).join(", ")); + } + } } } - } - - action = ""; - delay(100); } + removeEventListener("chatmsg", chatEvent); + removeEventListener("gameevent", gameEvent); + return true; } From c391eb4f0476ad395b3a50a1f85e9c96036d69f4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 May 2023 18:51:28 -0400 Subject: [PATCH 134/758] Update Town.js - add lastChores tracker --- d2bs/kolbot/libs/core/Town.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index d307bc410..9c140d1a3 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -78,6 +78,7 @@ Object.defineProperty(NPC, "getAct", { const Town = { telekinesis: true, sellTimer: getTickCount(), // shop speedup test + lastChores: 0, /** * @namespace */ @@ -197,6 +198,7 @@ const Town = { delay(250); console.info(false, null, "doChores"); Pather.allowBroadcast = true; + Town.lastChores = getTickCount(); return true; }, @@ -2416,7 +2418,11 @@ const Town = { goToTown: function (act = 0, wpmenu = false) { if (!me.inTown) { try { - if (!Pather.makePortal(true)) { + // this can save us spamming portals + let oldPortal = Pather.getPortal(sdk.areas.townOf(me.area), me.name); + // if (oldPortal && Pather.usePortal(null, me.name, oldPortal)) + if ((oldPortal && !Pather.usePortal(null, me.name, oldPortal)) + || !Pather.makePortal(true)) { console.warn("Town.goToTown: Failed to make TP"); } if (!me.inTown && !Pather.usePortal(null, me.name)) { From b9a832f919f74f0be62dc2babfda93b892e6a03f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 May 2023 19:02:16 -0400 Subject: [PATCH 135/758] Update AutoSkill.js - fix previous skill check when `i` is 0 --- d2bs/kolbot/libs/core/Auto/AutoSkill.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Auto/AutoSkill.js b/d2bs/kolbot/libs/core/Auto/AutoSkill.js index 575713cb6..663912251 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoSkill.js +++ b/d2bs/kolbot/libs/core/Auto/AutoSkill.js @@ -69,10 +69,9 @@ const AutoSkill = new function () { } // check to see if skill count in previous array is satisfied - const _prevHardPoints = me.getSkill(inputArray[i - 1][0], sdk.skills.subindex.HardPoints); - if (i > 0 && inputArray[i - 1][2] - && (!_prevHardPoints ? 0 : _prevHardPoints) < inputArray[i - 1][1]) { - return false; + if (i > 0 && inputArray[i - 1][2]) { + const _prevHardPoints = (me.getSkill(inputArray[i - 1][0], sdk.skills.subindex.HardPoints) || 0); + if (_prevHardPoints < inputArray[i - 1][1]) return false; } if (me.getSkill(inputArray[i][0], sdk.skills.subindex.HardPoints) From 00238eba16c6af48d00d383cd497ce2603332746 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 May 2023 20:37:13 -0400 Subject: [PATCH 136/758] Update Town.js - fix deletion of act 5 tasks object --- d2bs/kolbot/libs/core/Town.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 9c140d1a3..0cc25578e 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -128,6 +128,7 @@ const Town = { _taskObj(NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Fara, NPC.Greiz, NPC.Lysander), _taskObj(NPC.Ormus, NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara, NPC.Hratli), _taskObj(NPC.Jamella, NPC.Jamella, NPC.Jamella, NPC.Halbu, NPC.Tyrael, NPC.Jamella), + _taskObj(NPC.Malah, NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Malah) ]; })(), From 450feeb7b219741ab8097259e0c772de4987e5a4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 May 2023 00:30:52 -0400 Subject: [PATCH 137/758] small bits of script cleanup - refactored some of the for-loops into for...of loops - load antiidle thread in IPHunter, sometimes it still dropped when just using movements. - sort temples by distance to speed up KurastTemples a bit --- d2bs/kolbot/libs/scripts/ClearAnyArea.js | 8 ++-- d2bs/kolbot/libs/scripts/GemHunter.js | 8 ++-- d2bs/kolbot/libs/scripts/IPHunter.js | 19 +++++---- d2bs/kolbot/libs/scripts/KurastTemples.js | 22 ++++++---- d2bs/kolbot/libs/scripts/Mephisto.js | 48 +++++++++++++--------- d2bs/kolbot/libs/scripts/Travincal.js | 49 ++++++++++------------- d2bs/kolbot/libs/scripts/WPGetter.js | 13 +++--- 7 files changed, 90 insertions(+), 77 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ClearAnyArea.js b/d2bs/kolbot/libs/scripts/ClearAnyArea.js index 281c7cc5b..b78638545 100644 --- a/d2bs/kolbot/libs/scripts/ClearAnyArea.js +++ b/d2bs/kolbot/libs/scripts/ClearAnyArea.js @@ -5,12 +5,14 @@ * */ -function ClearAnyArea() { +function ClearAnyArea () { Town.doChores(); - for (let i = 0; i < Config.ClearAnyArea.AreaList.length; i += 1) { + for (let area of Config.ClearAnyArea.AreaList) { try { - Pather.journeyTo(Config.ClearAnyArea.AreaList[i]) && Attack.clearLevel(Config.ClearType); + if (Pather.journeyTo(area)) { + Attack.clearLevel(Config.ClearType); + } } catch (e) { console.error(e); } diff --git a/d2bs/kolbot/libs/scripts/GemHunter.js b/d2bs/kolbot/libs/scripts/GemHunter.js index 856d05265..452e8feca 100644 --- a/d2bs/kolbot/libs/scripts/GemHunter.js +++ b/d2bs/kolbot/libs/scripts/GemHunter.js @@ -20,12 +20,12 @@ function GemHunter () { return false; } - for (let i = 0; i < Config.GemHunter.AreaList.length; i++) { + for (let area of Config.GemHunter.AreaList) { if (Town.getGemsInInv().length > 0) { - print("ÿc4GemHunterÿc0: Moving to " + getAreaName(Config.GemHunter.AreaList[i])); - Pather.journeyTo(Config.GemHunter.AreaList[i]); + print("ÿc4GemHunterÿc0: Moving to " + getAreaName(area)); + Pather.journeyTo(area); if (i % 2 === 0) Precast.doPrecast(true); - if (Misc.getShrinesInArea(Config.GemHunter.AreaList[i], sdk.shrines.Gem, true)) { + if (Misc.getShrinesInArea(area, sdk.shrines.Gem, true)) { Pickit.pickItems(); print("ÿc4GemHunterÿc0: found a gem Shrine"); if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { diff --git a/d2bs/kolbot/libs/scripts/IPHunter.js b/d2bs/kolbot/libs/scripts/IPHunter.js index c3868dea5..0782c8d67 100644 --- a/d2bs/kolbot/libs/scripts/IPHunter.js +++ b/d2bs/kolbot/libs/scripts/IPHunter.js @@ -2,19 +2,19 @@ * @filename IPHunter.js * @author kolton, Mercoory * @desc search for a "hot" IP and stop if the correct server is found -* @changes 2020.01 - more beeps and movements (anti drop measure) when IP is found; overhead messages with countdown timer; logs to D2Bot console +* @changes 2020.01 - more beeps and movements (anti drop measure) when IP is found +* overhead messages with countdown timer; logs to D2Bot console * */ -function IPHunter() { - let ip = Number(me.gameserverip.split(".")[3]); +function IPHunter () { + const ip = Number(me.gameserverip.split(".")[3]); - if (Config.IPHunter.IPList.indexOf(ip) > -1) { - D2Bot.printToConsole( - "IPHunter: IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword, - sdk.colors.D2Bot.DarkGold - ); - print("IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword); + if (Config.IPHunter.IPList.includes(ip)) { + load("threads/antiidle.js"); + const foundMsg = "IPHunter: IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword; + D2Bot.printToConsole(foundMsg, sdk.colors.D2Bot.DarkGold); + console.log(foundMsg); me.overhead(":D IP found! - [" + ip + "]"); me.maxgametime = 0; @@ -25,7 +25,6 @@ function IPHunter() { } while (true) { - /* // remove comment if you want beeps at every movement for (let i = 12; i != 0; i -= 1) { me.overhead(":D IP found! - [" + ip + "]" + (i-1) + " beep left"); diff --git a/d2bs/kolbot/libs/scripts/KurastTemples.js b/d2bs/kolbot/libs/scripts/KurastTemples.js index a0dcc96e1..fbbe85119 100644 --- a/d2bs/kolbot/libs/scripts/KurastTemples.js +++ b/d2bs/kolbot/libs/scripts/KurastTemples.js @@ -5,7 +5,7 @@ * */ -function KurastTemples() { +function KurastTemples () { Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar); @@ -20,13 +20,19 @@ function KurastTemples() { if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to change area"); } let precastTimeout = getTickCount() + Time.minutes(2); - // @todo sort by distance - area.temples.forEach(temple => { - if (!Pather.moveToExit(temple, true)) throw new Error("Failed to move to the temple"); - Attack.clearLevel(Config.ClearType); - if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to move out of the temple"); - Precast.doPrecast((getTickCount() > precastTimeout)); - }); + /** @type {Map area.temples.some(temple => temple === exit.target)) + .forEach(exit => _temples.set(exit.target, { x: exit.x, y: exit.y })); + area.temples + .sort((a, b) => _temples.get(a).distance - _temples.get(b).distance) + .forEach(temple => { + if (!Pather.moveToExit(temple, true)) throw new Error("Failed to move to the temple"); + Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to move out of the temple"); + Precast.doPrecast((getTickCount() > precastTimeout)); + }); } catch (e) { console.error(e); diff --git a/d2bs/kolbot/libs/scripts/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js index 88f9affa2..7890875cb 100644 --- a/d2bs/kolbot/libs/scripts/Mephisto.js +++ b/d2bs/kolbot/libs/scripts/Mephisto.js @@ -5,7 +5,7 @@ * */ -function Mephisto() { +function Mephisto () { // eslint-disable-next-line no-unused-vars const killMephisto = function () { let pos = {}; @@ -45,7 +45,13 @@ function Mephisto() { }; const moat = function () { - let count = 0; + /** + * @param {number} x + * @param {number} y + * @param {number} duration + * @returns {{ x: number, y: number, duration: number }} + */ + const _posObj = (x, y, duration) => ({ x: x, y: y, duration: duration }); delay(350); Pather.moveTo(17563, 8072); @@ -54,26 +60,28 @@ function Mephisto() { if (!mephisto) throw new Error("Mephisto not found."); delay(350); - Pather.moveTo(17575, 8086) && delay(350); - Pather.moveTo(17584, 8091) && delay(1200); - Pather.moveTo(17600, 8095) && delay(550); - Pather.moveTo(17610, 8094) && delay(2500); + [ + _posObj(17575, 8086, 350), _posObj(17584, 8091, 1200), + _posObj(17600, 8095, 550), _posObj(17610, 8094, 2500) + ].forEach(pos => Pather.moveTo(pos.x, pos.y) && delay(pos.duration)); + Attack.clear(10); Pather.moveTo(17610, 8094); + const _lurePositions = [ + _posObj(17600, 8095, 150), _posObj(17584, 8091, 150), + _posObj(17575, 8086, 150), _posObj(17563, 8072, 350), + _posObj(17575, 8086, 350), _posObj(17584, 8091, 1200), + _posObj(17600, 8095, 550), _posObj(17610, 8094, 2500) + ]; + let count = 0; let distance = getDistance(me, mephisto); while (distance > 27) { count += 1; - Pather.moveTo(17600, 8095) && delay(150); - Pather.moveTo(17584, 8091) && delay(150); - Pather.moveTo(17575, 8086) && delay(150); - Pather.moveTo(17563, 8072) && delay(350); - Pather.moveTo(17575, 8086) && delay(350); - Pather.moveTo(17584, 8091) && delay(1200); - Pather.moveTo(17600, 8095) && delay(550); - Pather.moveTo(17610, 8094) && delay(2500); + _lurePositions + .forEach(pos => Pather.moveTo(pos.x, pos.y) && delay(pos.duration)); Attack.clear(10); Pather.moveTo(17610, 8094); @@ -88,11 +96,12 @@ function Mephisto() { }; const killCouncil = function () { - let coords = [17600, 8125, 17600, 8015, 17643, 8068]; + const councilMembers = [sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3]; + const coords = [[17600, 8125], [17600, 8015], [17643, 8068]]; - for (let i = 0; i < coords.length; i += 2) { - Pather.moveTo(coords[i], coords[i + 1]); - Attack.clearList(Attack.getMob([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3], 0, 40)); + for (let [x, y] of coords) { + Pather.moveTo(x, y); + Attack.clearList(Attack.getMob(councilMembers, 0, 40)); } return true; @@ -137,7 +146,8 @@ function Mephisto() { let tick = getTickCount(), time = 0; // Wait until bridge is there - while (getCollision(me.area, 17601, 8070, 17590, 8068) !== 0 && (time = getTickCount() - tick) < 2000) { + while (getCollision(me.area, 17601, 8070, 17590, 8068) !== 0 + && (time = getTickCount() - tick) < 2000) { Pather.moveTo(17590, 8068); // Activate it delay(3); } diff --git a/d2bs/kolbot/libs/scripts/Travincal.js b/d2bs/kolbot/libs/scripts/Travincal.js index 323c008ba..e48cd3bbd 100644 --- a/d2bs/kolbot/libs/scripts/Travincal.js +++ b/d2bs/kolbot/libs/scripts/Travincal.js @@ -5,29 +5,17 @@ * */ -function Travincal() { - this.buildList = function (checkColl) { - let monsterList = []; - let monster = Game.getMonster(); - - if (monster) { - do { - if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].includes(monster.classid) - && monster.attackable && (!checkColl || !checkCollision(me, monster, sdk.collision.BlockWall))) { - monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - - return monsterList; - }; - +function Travincal () { Town.doChores(); Pather.useWaypoint(sdk.areas.Travincal); Precast.doPrecast(true); - let orgX = me.x; - let orgY = me.y; + const [orgX, orgY] = [me.x, me.y]; + + /** @param {Monster} mon */ + const councilMember = (mon) => ( + [sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].includes(mon.classid) + ); if (Config.Travincal.PortalLeech) { Pather.moveTo(orgX + 85, orgY - 139); @@ -41,18 +29,25 @@ function Travincal() { } if (Skill.canUse(sdk.skills.LeapAttack) && !Pather.canTeleport()) { - let coords = [60, -53, 64, -72, 78, -72, 74, -88]; + const coords = [[60, -53], [64, -72], [78, -72], [74, -88]]; + + for (let i = 0; i < coords.length; i++) { + let [x, y] = coords[i]; - for (let i = 0; i < coords.length; i += 2) { - if (i % 4 === 0) { - Pather.moveTo(orgX + coords[i], orgY + coords[i + 1]); + if (i % 2 === 0) { + Pather.moveTo(orgX + x, orgY + y); } else { - Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, orgX + coords[i], orgY + coords[i + 1]); - Attack.clearList(this.buildList(1)); + Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, orgX + x, orgY + y); + Attack.clearList( + Attack.buildMonsterList( + /** @param {Monster} mon */ + (mon) => councilMember(mon) && !checkCollision(me, mon, sdk.collision.BlockWall) + ) + ); } } - Attack.clearList(this.buildList(0)); + Attack.clearList(Attack.buildMonsterList(councilMember)); } else { Pather.moveTo(orgX + 101, orgY - 56); @@ -66,7 +61,7 @@ function Travincal() { say("council " + me.area); } - Attack.clearList(this.buildList(0)); + Attack.clearList(Attack.buildMonsterList(councilMember)); } Config.MFLeader && Config.PublicMode && say("travdone"); diff --git a/d2bs/kolbot/libs/scripts/WPGetter.js b/d2bs/kolbot/libs/scripts/WPGetter.js index f36291da2..be363c003 100644 --- a/d2bs/kolbot/libs/scripts/WPGetter.js +++ b/d2bs/kolbot/libs/scripts/WPGetter.js @@ -1,11 +1,11 @@ /** * @filename WPGetter.js -* @author kolton +* @author kolton, theBGuy * @desc Get wps we don't have * */ -function WPGetter() { +function WPGetter () { Town.doChores(); Town.goToTown(); Pather.getWP(me.area); @@ -18,10 +18,11 @@ function WPGetter() { console.debug(wpsToGet); - for (let i = 0; i < wpsToGet.length; i += 1) { - Pather.getWP(wpsToGet[i]); - delay(500); - Town.checkScrolls(sdk.items.TomeofTownPortal) < 10 && Town.doChores(); + for (let wp of wpsToGet) { + Pather.getWP(wp); + if (Town.checkScrolls(sdk.items.TomeofTownPortal) < 10) { + Town.doChores(); + } } return true; From 60c3f1144932197e62f45b8c5e405e38530d71de Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 May 2023 00:50:23 -0400 Subject: [PATCH 138/758] Update ShrineData.js - small refactor. Initialize `_shrines` map instead of setting each after creation --- d2bs/kolbot/libs/core/GameData/ShrineData.js | 57 ++++++++++---------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/ShrineData.js b/d2bs/kolbot/libs/core/GameData/ShrineData.js index efbe2d9a1..db13ee49d 100644 --- a/d2bs/kolbot/libs/core/GameData/ShrineData.js +++ b/d2bs/kolbot/libs/core/GameData/ShrineData.js @@ -12,49 +12,50 @@ this.duration = duration || 0; this.regenTime = Time.minutes(regen) || Infinity; } - const shrineMap = new Map(); - shrineMap.set(sdk.shrines.Refilling, new Shrine(0, 0, 2)); - shrineMap.set(sdk.shrines.Health, new Shrine(0, 0, 5)); - shrineMap.set(sdk.shrines.Mana, new Shrine(0, 0, 5)); - shrineMap.set(sdk.shrines.HealthExchange, new Shrine()); - shrineMap.set(sdk.shrines.ManaExchange, new Shrine()); - shrineMap.set(sdk.shrines.Armor, new Shrine(sdk.states.ShrineArmor, 2400, 5)); - shrineMap.set(sdk.shrines.Combat, new Shrine(sdk.states.ShrineCombat, 2400, 5)); - shrineMap.set(sdk.shrines.ResistFire, new Shrine(sdk.states.ShrineResFire, 3600, 5)); - shrineMap.set(sdk.shrines.ResistCold, new Shrine(sdk.states.ShrineResCold, 3600, 5)); - shrineMap.set(sdk.shrines.ResistLightning, new Shrine(sdk.states.ShrineResLighting, 3600, 5)); - shrineMap.set(sdk.shrines.ResistPoison, new Shrine(sdk.states.ShrineResPoison, 3600, 5)); - shrineMap.set(sdk.shrines.Skill, new Shrine(sdk.states.ShrineSkill, 2400, 5)); - shrineMap.set(sdk.shrines.ManaRecharge, new Shrine(sdk.states.ShrineManaRegen, 2400, 5)); - shrineMap.set(sdk.shrines.Stamina, new Shrine(sdk.states.ShrineStamina, 4800, 5)); - shrineMap.set(sdk.shrines.Experience, new Shrine(sdk.states.ShrineResCold, 3600)); - shrineMap.set(sdk.shrines.Enirhs, new Shrine()); - shrineMap.set(sdk.shrines.Portal, new Shrine()); - shrineMap.set(sdk.shrines.Gem, new Shrine()); - shrineMap.set(sdk.shrines.Fire, new Shrine()); - shrineMap.set(sdk.shrines.Monster, new Shrine()); - shrineMap.set(sdk.shrines.Exploding, new Shrine()); - shrineMap.set(sdk.shrines.Poison, new Shrine()); + const _shrines = new Map([ + [sdk.shrines.Refilling, new Shrine(sdk.shrines.None, 0, 2)], + [sdk.shrines.Health, new Shrine(sdk.shrines.None, 0, 5)], + [sdk.shrines.Mana, new Shrine(sdk.shrines.None, 0, 5)], + [sdk.shrines.HealthExchange, new Shrine()], + [sdk.shrines.ManaExchange, new Shrine()], + [sdk.shrines.Armor, new Shrine(sdk.states.ShrineArmor, 2400, 5)], + [sdk.shrines.Combat, new Shrine(sdk.states.ShrineCombat, 2400, 5)], + [sdk.shrines.ResistFire, new Shrine(sdk.states.ShrineResFire, 3600, 5)], + [sdk.shrines.ResistCold, new Shrine(sdk.states.ShrineResCold, 3600, 5)], + [sdk.shrines.ResistLightning, new Shrine(sdk.states.ShrineResLighting, 3600, 5)], + [sdk.shrines.ResistPoison, new Shrine(sdk.states.ShrineResPoison, 3600, 5)], + [sdk.shrines.Skill, new Shrine(sdk.states.ShrineSkill, 2400, 5)], + [sdk.shrines.ManaRecharge, new Shrine(sdk.states.ShrineManaRegen, 2400, 5)], + [sdk.shrines.Stamina, new Shrine(sdk.states.ShrineStamina, 4800, 5)], + [sdk.shrines.Experience, new Shrine(sdk.states.ShrineResCold, 3600)], + [sdk.shrines.Enirhs, new Shrine()], + [sdk.shrines.Portal, new Shrine()], + [sdk.shrines.Gem, new Shrine()], + [sdk.shrines.Fire, new Shrine()], + [sdk.shrines.Monster, new Shrine()], + [sdk.shrines.Exploding, new Shrine()], + [sdk.shrines.Poison, new Shrine()], + ]); return { get: function (shrineType) { - return shrineMap.get(shrineType); + return _shrines.get(shrineType); }, has: function (shrineType) { - return shrineMap.has(shrineType); + return _shrines.has(shrineType); }, getState: function (shrineType) { - return shrineMap.get(shrineType).state || 0; + return _shrines.get(shrineType).state || 0; }, getDuration: function (shrineType) { - return shrineMap.get(shrineType).duration || 0; + return _shrines.get(shrineType).duration || 0; }, getRegenTime: function (shrineType) { - return shrineMap.get(shrineType).regenTime || Infinity; + return _shrines.get(shrineType).regenTime || Infinity; }, }; })(); From 0fd8d949e6777e5ad5418520bcb0b2275bb3b1e3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 May 2023 11:55:47 -0400 Subject: [PATCH 139/758] Update Skill.d.ts - update the type defs --- d2bs/kolbot/sdk/types/Skill.d.ts | 69 +++++++++++++------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts index 893ec0c0b..817577c18 100644 --- a/d2bs/kolbot/sdk/types/Skill.d.ts +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -1,44 +1,31 @@ export {}; declare global { - function SkillData(skillId: number): void; - class SkillData { - constructor(skillId: number); - hardpoints: boolean; - checked: boolean; - manaCost: any; - condition: Function; - have(skill: number): any; - } - namespace Skill { - let usePvpRange: boolean; - const manaCostList: object; - const needFloor: number[]; - const missileSkills: number[]; - const charges: any[]; - const haveTK: boolean; - - namespace skills { - const all: { - [x: number]: SkillData; - }; - let initialized: boolean; - function init(): void; - function have(skill: number): boolean; - function reset(): void; - } - function init(): void; - function canUse(skillId: number): boolean; - function getDuration(skillId: number): number; - function getMaxSummonCount(skillId: number): number; - function getRange(skillId: any): number; - function getHand(skillId: any): number; - function cast(skillId: number, hand?: number, x?: number, y?: number, item?: ItemUnit | undefined): boolean; - function cast(skillId: number, hand?: number, unit?: Unit): boolean; - function setSkill(skillId: any, hand?: any, item?: any): boolean; - function isTimed(skillId: any): boolean; - function wereFormCheck(skillId: any): boolean; - function townSkill(skillId: any): boolean; - function getManaCost(skillId: any): number; - function useTK(unit: Unit): boolean; - } + namespace Skill { + let usePvpRange: boolean; + const manaCostList: object; + const needFloor: number[]; + const missileSkills: number[]; + const charges: any[]; + + function getClassSkillRange(classid?: number): [number, number]; + function init(): void; + function canUse(skillId: number): boolean; + function getDuration(skillId: number): number; + function getMaxSummonCount(skillId: number): number; + function getRange(skillId: number): number; + function getAoE(skillId: number): number; + function getHand(skillId: number): number; + function getState(skillId: number): number; + function getManaCost(skillId: number): number; + function isTimed(skillId: number): boolean; + function townSkill(skillId: number): boolean; + function missileSkill(skillId: number): boolean; + function wereFormCheck(skillId: number): boolean; + function setSkill(skillId: number, hand?: number, item?: any): boolean; + function shapeShift(mode: number | string): boolean; + function unShift(): boolean; + function useTK(unit: Unit): boolean; + function cast(skillId: number, hand?: number, x?: number, y?: number, item?: ItemUnit | undefined): boolean; + function cast(skillId: number, hand?: number, unit?: Unit): boolean; + } } From 829070323561ca37ce6af8be243f145f60335565 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 30 May 2023 00:21:29 -0400 Subject: [PATCH 140/758] Update Pather.js - fix typo in `PathAction` --- d2bs/kolbot/libs/core/Pather.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 4749c8010..e438847e6 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -314,7 +314,7 @@ const Pather = { function PathAction () { this.at = 0; /** @type {PathNode} */ - this.from = { x: null, y: null }; + this.node = { x: null, y: null }; } let fail = 0; @@ -430,12 +430,12 @@ const Pather = { 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 })) { + && 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]; } } } @@ -444,11 +444,11 @@ const Pather = { 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 })) { + || 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]; } } } @@ -462,11 +462,11 @@ const Pather = { 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 })) { + || 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]; } } } @@ -1322,7 +1322,7 @@ const Pather = { * Meant for use as a MfLeader to let MfHelpers know where to go next * @param {number} targetArea - area id */ - broadcastIntent: function broadcastIntent(targetArea) { + broadcastIntent: function broadcastIntent (targetArea) { if (Config.MFLeader && Pather.allowBroadcast) { let targetAct = sdk.areas.actOf(targetArea); me.act !== targetAct && say("goto A" + targetAct); @@ -1334,7 +1334,7 @@ const Pather = { * @param {boolean} check - force the waypoint menu * @returns {boolean} */ - useWaypoint: function useWaypoint(targetArea, check = false) { + useWaypoint: function useWaypoint (targetArea, check = false) { switch (targetArea) { case undefined: throw new Error("useWaypoint: Invalid targetArea parameter: " + targetArea); From 4e88ee901736f444bea4d4e1c75bc4af711f4165 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 30 May 2023 11:05:10 -0400 Subject: [PATCH 141/758] Update SkillData.js - typo, `Config.SummonValkyrie` not `Config.UseValkyrie` --- d2bs/kolbot/libs/core/GameData/SkillData.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index c5822bb8a..e77779e83 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -182,7 +182,7 @@ hand: sdk.skills.hand.Right, range: 30, summonCount: () => 1, - condition: () => Config.UseValkyrie, + condition: () => Config.SummonValkyrie, }); skillMap.set(sdk.skills.Pierce, { hand: -1, @@ -1211,7 +1211,7 @@ skillMap.set(sdk.skills.ShadowWarrior, { hand: sdk.skills.hand.Right, range: 30, - // condition: () => Config.UseValkyrie, + // condition: () => Config.SummonValkyrie, summonCount: () => 1, }); skillMap.set(sdk.skills.ClawsofThunder, { @@ -1268,7 +1268,7 @@ skillMap.set(sdk.skills.ShadowMaster, { hand: sdk.skills.hand.Right, range: 30, - // condition: () => Config.UseValkyrie, + // condition: () => Config.SummonValkyrie, summonCount: () => 1, }); skillMap.set(sdk.skills.RoyalStrike, { From c2da5ae28ceaa622d6b4a5d6fad5f9eb671ccb38 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:58:39 -0400 Subject: [PATCH 142/758] Remove forced packet tk from `Skill.cast` --- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 2 +- d2bs/kolbot/libs/core/Common/Cain.js | 2 +- d2bs/kolbot/libs/core/Common/Diablo.js | 2 +- d2bs/kolbot/libs/core/Misc.js | 4 ++-- d2bs/kolbot/libs/core/Packet.js | 8 +++++++- d2bs/kolbot/libs/core/Pather.js | 10 +++++----- d2bs/kolbot/libs/core/Skill.js | 14 +++++++++----- d2bs/kolbot/libs/core/Town.js | 2 +- d2bs/kolbot/libs/scripts/Duriel.js | 2 +- d2bs/kolbot/libs/scripts/Rushee.js | 2 +- 10 files changed, 29 insertions(+), 19 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 1b4920b38..7ccc55dbf 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -176,7 +176,7 @@ const ClassAttack = { if (!!closeMob) { let findSkill = this.decideSkill(closeMob); if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { - (Skill.haveTK && Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)); + (Skill.haveTK && Packet.telekinesis(unit)); } } } diff --git a/d2bs/kolbot/libs/core/Common/Cain.js b/d2bs/kolbot/libs/core/Common/Cain.js index 29a9c2c9a..a6fe32ebf 100644 --- a/d2bs/kolbot/libs/core/Common/Cain.js +++ b/d2bs/kolbot/libs/core/Common/Cain.js @@ -15,7 +15,7 @@ let useTK = (stone.distance > 5 && Skill.useTK(stone) && i === 0); if (useTK) { stone.distance > 13 && Attack.getIntoPosition(stone, 13, sdk.collision.Ranged); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stone)) { + if (!Packet.telekinesis(stone)) { console.debug("Failed to tk: attempt: " + i); continue; } diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 61c3eb661..4d5626be7 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -219,7 +219,7 @@ for (let i = 0; i < 5; i++) { seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, seal) + if (Packet.telekinesis(seal) && Misc.poll(() => seal.mode, 1000, 100)) { break; } diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 766c3186c..b5f59f7fc 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -284,7 +284,7 @@ const Misc = { let useTK = (unit.distance > 5 && Skill.useTK(unit) && i < 3); if (useTK) { unit.distance > 13 && Attack.getIntoPosition(unit, 13, sdk.collision.WallOrRanged); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { + if (!Packet.telekinesis(unit)) { console.debug("Failed to tk: attempt: " + i); continue; } @@ -502,7 +502,7 @@ const Misc = { for (let i = 0; i < 3; i++) { if (Skill.useTK(unit) && i < 2) { unit.distance > 21 && Pather.moveNearUnit(unit, 20); - if (!Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit)) { + if (!Packet.telekinesis(unit)) { Attack.getIntoPosition(unit, 20, sdk.collision.WallOrRanged); } } else { diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 307e69e47..454bb0ce8 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -417,7 +417,13 @@ const Packet = { */ telekinesis: function (who) { if (!who || !Skill.setSkill(sdk.skills.Telekinesis, sdk.skills.hand.Right)) return false; - sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); + if (Skill.getManaCost(sdk.skills.Telekinesis) > me.mp) return false; + if (me.shapeshifted) return false; + new PacketBuilder() + .byte(sdk.packets.send.RightSkillOnEntityEx3) + .dword(who.type) + .dword(who.gid) + .send(); return true; }, diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index e438847e6..86559bdaf 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1259,7 +1259,7 @@ const Pather = { usetk ? this.moveNearUnit(unit, 20) : this.moveToUnit(unit); // try to activate it once if (usetk && i === 0 && unit.mode === sdk.objects.mode.Inactive && unit.distance < 21) { - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit); + Packet.telekinesis(unit); } } @@ -1289,7 +1289,7 @@ const Pather = { type === sdk.unittype.Stairs ? Misc.click(0, 0, unit) : usetk && unit.distance > 5 - ? Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit) + ? Packet.telekinesis(unit) : Packet.entityInteract(unit); delay(300); @@ -1398,7 +1398,7 @@ const Pather = { if (useTK && !getUIFlag(sdk.uiflags.Waypoint)) { wp.distance > 21 && Pather.moveNearUnit(wp, 20); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + Packet.telekinesis(wp); } else if (!me.inTown && wp.distance > 7) { this.moveToUnit(wp); } @@ -1609,7 +1609,7 @@ const Pather = { ? Town.move("portalspot") : Pather.moveNearUnit(portal, 20); } - if (Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, portal)) { + if (Packet.telekinesis(portal)) { if (Misc.poll(() => { if (me.area !== preArea) { Pather.lastPortalTick = getTickCount(); @@ -1829,7 +1829,7 @@ const Pather = { if (!getUIFlag(sdk.uiflags.Waypoint)) { if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + Packet.telekinesis(wp); } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { this.moveToUnit(wp) && Misc.click(0, 0, wp); } diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 9e446d7a3..206d1fa77 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -34,6 +34,15 @@ sdk.skills.Twister, sdk.skills.Tornado, sdk.skills.FireBlast ], + /** + * @param {number} skillId + * @returns {SkillDataInfo} + */ + get: function (skillId = -1) { + if (!_SkillData.has(skillId)) return null; + return _SkillData.get(skillId); + }, + getClassSkillRange: function (classid = me.classid) { switch (classid) { case sdk.player.class.Amazon: @@ -389,11 +398,6 @@ return false; } - if (skillId === sdk.skills.Telekinesis && typeof x === "object" && Packet.telekinesis(x)) { - delay(250); - return true; - } - hand === undefined && (hand = this.getHand(skillId)); x === undefined && (x = me.x); y === undefined && (y = me.y); diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 0cc25578e..b11294879 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1746,7 +1746,7 @@ const Town = { if (Skill.useTK(stash)) { // Fix for out of range telek i > 0 && stash.distance > (23 - (i * 2)) && Pather.walkTo(stash.x, stash.y, (23 - (i * 2))); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, stash); + Packet.telekinesis(stash); } else { Misc.click(0, 0, stash); } diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index b66dac54d..6a876911b 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -38,7 +38,7 @@ function Duriel () { if (Skill.useTK(unit)) { Misc.poll(function () { - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, unit) && delay(100); + Packet.telekinesis(unit) && delay(100); return me.inArea(sdk.areas.DurielsLair); }, 1000, 200); } diff --git a/d2bs/kolbot/libs/scripts/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js index 19d0c5d5a..cf371e53c 100644 --- a/d2bs/kolbot/libs/scripts/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -42,7 +42,7 @@ new Overrides.Override(Pather, Pather.getWP, function(orignal, area, clearPath) if (!getUIFlag(sdk.uiflags.Waypoint)) { if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + Packet.telekinesis(wp); } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { this.moveToUnit(wp) && Misc.click(0, 0, wp); } From 6e4b9b3c1828f258f841ec966056ea4601229f58 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 1 Jun 2023 19:25:49 -0400 Subject: [PATCH 143/758] Little bit of town cleanup - Changed Town.tasks to be a map so we can do `Town.tasks.get(me.act)` instead of `Town.task[me.act -1]` - Changed Town.gambleIds to be a set, it makes checking it easier and ensures no duplicates - changed the index based for loops to for...of loops for better readability --- d2bs/kolbot/libs/core/Prototypes.js | 2 +- d2bs/kolbot/libs/core/Town.js | 144 ++++++++++++++-------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 4bd61389b..7511d0660 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -703,7 +703,7 @@ Unit.prototype.repairItem = function () { // lets check if we have and can afford to repair this item if (me.gold < this.getItemCost(2)) return false; let npc = getInteractedNPC(); - if (!npc || npc.name.toLowerCase() !== Town.tasks[me.act - 1].Repair) return false; + if (!npc || npc.name.toLowerCase() !== Town.tasks.get(me.act).Repair) return false; // if (!this.startTrade("Repair")) return false; let preDurability = this.getStat(sdk.stats.Durability); new PacketBuilder() diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index b11294879..f73395a1a 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -120,16 +120,24 @@ const Town = { }, tasks: (function () { + /** + * @param {string} heal + * @param {string} shop + * @param {string} gamble + * @param {string} repair + * @param {string} merc + * @param {string} key + */ let _taskObj = (heal, shop, gamble, repair, merc, key) => ( { Heal: heal, Shop: shop, Gamble: gamble, Repair: repair, Merc: merc, Key: key, CainID: NPC.Cain } ); - return [ - _taskObj(NPC.Akara, NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Akara), - _taskObj(NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Fara, NPC.Greiz, NPC.Lysander), - _taskObj(NPC.Ormus, NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara, NPC.Hratli), - _taskObj(NPC.Jamella, NPC.Jamella, NPC.Jamella, NPC.Halbu, NPC.Tyrael, NPC.Jamella), - _taskObj(NPC.Malah, NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Malah) - ]; + return new Map([ + [1, _taskObj(NPC.Akara, NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Akara)], + [2, _taskObj(NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Fara, NPC.Greiz, NPC.Lysander)], + [3, _taskObj(NPC.Ormus, NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara, NPC.Hratli)], + [4, _taskObj(NPC.Jamella, NPC.Jamella, NPC.Jamella, NPC.Halbu, NPC.Tyrael, NPC.Jamella)], + [5, _taskObj(NPC.Malah, NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Malah)] + ]); })(), ignoredItemTypes: [ @@ -173,7 +181,7 @@ const Town = { Skill.cast(sdk.skills.BurstofSpeed, sdk.skills.hand.Right); } - me.switchWeapons(Attack.getPrimarySlot()); + me.switchToPrimary(); this.heal(); this.identify(); @@ -212,7 +220,8 @@ const Town = { npcInteract: function (name = "", cancel = true) { // name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); // what about finding the closest name in case someone mispells it? - let npcKey = Object.keys(NPC).find(key => String.isEqual(key, name)); + let npcKey = Object.keys(NPC) + .find(key => String.isEqual(key, name)); if (!npcKey) { // @todo handle if NPC object key is used instead of common name console.warn("Couldn't find " + name + " in NPC object"); @@ -338,7 +347,9 @@ const Town = { /** @type {NPCUnit} */ let npc = null; - let wantedNpc = this.tasks[me.act - 1][task] !== undefined ? this.tasks[me.act - 1][task] : "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); if (getUIFlag(sdk.uiflags.NPCMenu)) { @@ -362,7 +373,7 @@ const Town = { // for now it won't get here with unids // need to also take into account what our next task is if (justUseClosest) { - let npcs = this.tasks[me.act - 1]; + let npcs = Town.tasks.get(me.act); npc = getUnits(sdk.unittype.NPC) .sort((a, b) => a.distance - b.distance) .find(unit => [npcs.Shop, npcs.Repair].includes(unit.name.toLowerCase())); @@ -824,7 +835,7 @@ const Town = { // Check if we're already in a shop. It would be pointless to go to Cain if so. let npc = getInteractedNPC(); - if (npc && npc.name.toLowerCase() === this.tasks[me.act - 1].Shop) return false; + if (npc && npc.name.toLowerCase() === Town.tasks.get(me.act).Shop) return false; me.cancel(); this.stash(false); @@ -836,25 +847,25 @@ const Town = { if (unids.length < Config.CainID.MinUnids) return false; // Check if we may use Cain - kept unid items - for (let i = 0; i < unids.length; i += 1) { - if (Pickit.checkItem(unids[i]).result > 0) return false; + for (let item of unids) { + if (Pickit.checkItem(item).result > 0) return false; } let cain = this.initNPC("CainID", "cainID"); if (!cain) return false; - for (let i = 0; i < unids.length; i += 1) { - let result = Pickit.checkItem(unids[i]); + for (let item of unids) { + let result = Pickit.checkItem(item); switch (result.result) { case Pickit.Result.UNWANTED: - Item.logger("Dropped", unids[i], "cainID"); - unids[i].drop(); + Item.logger("Dropped", item, "cainID"); + item.drop(); break; case Pickit.Result.WANTED: - Item.logger("Kept", unids[i]); - Item.logItem("Kept", unids[i], result.line); + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); break; default: @@ -926,13 +937,13 @@ const Town = { let npc = getInteractedNPC(); if (!npc || !npc.itemcount) return false; - let items = npc.getItemsEx().filter((item) => !Town.ignoreType(item.itemType)); + let items = npc.getItemsEx() + .filter((item) => !Town.ignoreType(item.itemType)); if (!items.length) return false; console.log("ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); - for (let i = 0; i < items.length; i += 1) { - let item = items[i]; + for (let item of items) { let result = Pickit.checkItem(item); switch (result.result) { @@ -957,29 +968,27 @@ const Town = { return true; }, - /** - * @type {number[]} - */ - gambleIds: [], + /** @type {Set} */ + gambleIds: new Set(), gamble: function () { if (!this.needGamble() || Config.GambleItems.length === 0) return true; - if (this.gambleIds.length === 0) { + if (this.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())) { - this.gambleIds.push(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())) { + this.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 { - this.gambleIds.push(Config.GambleItems[i]); + this.gambleIds.add(item); } } } - if (this.gambleIds.length === 0) return true; + if (this.gambleIds.size === 0) return true; // avoid Alkor me.act === 3 && this.goToTown(2); @@ -1002,18 +1011,18 @@ const Town = { if (item) { do { - if (this.gambleIds.includes(item.classid)) { + if (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])) { + for (let item of items) { + if (!Storage.Inventory.CanFit(item)) { return false; } - me.overhead("Buy: " + items[i].name); - items[i].buy(false, true); + me.overhead("Buy: " + item.name); + item.buy(false, true); let newItem = this.getGambledItem(list); if (newItem) { @@ -1066,17 +1075,17 @@ const Town = { getGambledItem: function (list = []) { let items = me.findItems(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - for (let i = 0; i < items.length; i += 1) { - if (list.indexOf(items[i].gid) === -1) { + for (let item of items) { + if (list.indexOf(item.gid) === -1) { for (let j = 0; j < 3; j += 1) { - if (items[i].identified) { + if (item.identified) { break; } delay(100); } - return items[i]; + return item; } } @@ -1678,16 +1687,16 @@ const Town = { if (items) { Config.SortSettings.SortStash && Storage.Stash.SortItems(); - for (let i = 0; i < items.length; i += 1) { - if (this.canStash(items[i])) { + for (let item of items) { + if (this.canStash(item)) { let result = false; - let pickResult = Pickit.checkItem(items[i]).result; + let pickResult = Pickit.checkItem(item).result; switch (true) { case pickResult > Pickit.Result.UNWANTED && pickResult < Pickit.Result.TRASH: - case Cubing.keepItem(items[i]): - case Runewords.keepItem(items[i]): - case CraftingSystem.keepItem(items[i]): + case Cubing.keepItem(item): + case Runewords.keepItem(item): + case CraftingSystem.keepItem(item): result = true; break; @@ -1696,7 +1705,7 @@ const Town = { } if (result) { - Storage.Stash.MoveTo(items[i]) && Item.logger("Stashed", items[i]); + Storage.Stash.MoveTo(item) && Item.logger("Stashed", item); } } } @@ -1715,19 +1724,14 @@ const Town = { }, needStash: function () { - if (Config.StashGold && me.getStat(sdk.stats.Gold) >= Config.StashGold && me.getStat(sdk.stats.GoldBank) < 25e5) { + 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); - - 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) || []) + .some(item => Storage.Stash.CanFit(item)); }, openStash: function () { @@ -1745,23 +1749,19 @@ const Town = { if (Skill.useTK(stash)) { // Fix for out of range telek - i > 0 && stash.distance > (23 - (i * 2)) && Pather.walkTo(stash.x, stash.y, (23 - (i * 2))); + if (i > 0 && stash.distance > (23 - (i * 2))) { + Pather.walkTo(stash.x, stash.y, (23 - (i * 2))); + } Packet.telekinesis(stash); } else { Misc.click(0, 0, stash); } - let tick = getTickCount(); - - while (getTickCount() - tick < 5000) { - if (getUIFlag(sdk.uiflags.Stash)) { - // allow UI to initialize - delay(100 + pingDelay * 2); - - return true; - } + if (Misc.poll(() => getUIFlag(sdk.uiflags.Stash), Time.seconds(5), 100)) { + // allow UI to initialize + delay(100 + pingDelay * (i + 1)); - delay(100); + return true; } } } From 3a20ec0b18ea9d6c07b6727850569251705d2d80 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 3 Jun 2023 19:47:48 -0400 Subject: [PATCH 144/758] Update Prototypes.js - fix crash during `Unit.toCursor` --- d2bs/kolbot/libs/core/Prototypes.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 7511d0660..204c9ab92 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -796,6 +796,10 @@ Unit.prototype.sell = function () { return false; }; +/** + * @this ItemUnit + * @param {boolean} usePacket + */ Unit.prototype.toCursor = function (usePacket = false) { if (this.type !== sdk.unittype.Item) throw new Error("Unit.toCursor: Must be used with items."); if (me.itemoncursor && this.mode === sdk.items.mode.onCursor) return true; @@ -808,6 +812,10 @@ Unit.prototype.toCursor = function (usePacket = false) { for (let i = 0; i < 3; i += 1) { try { if (this.mode === sdk.items.mode.Equipped) { + if (this.isOnSwap) { + // fix crash when item is equipped on switch and we try to move it directly to cursor + me.switchWeapons(sdk.player.slot.Secondary); + } // fix for equipped items (cubing viper staff for example) clickItem(sdk.clicktypes.click.item.Left, this.bodylocation); } else { @@ -834,7 +842,9 @@ Unit.prototype.toCursor = function (usePacket = false) { }; Unit.prototype.drop = function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.drop: Must be used with items. Unit Name: " + this.name); + if (this.type !== sdk.unittype.Item) { + throw new Error("Unit.drop: Must be used with items. Unit Name: " + this.name); + } if (!this.toCursor()) return false; let tick = getTickCount(); From f143e0c572e4e061b34ddea5cbbe59c638aa4fe9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 3 Jun 2023 19:49:32 -0400 Subject: [PATCH 145/758] some type definition updates --- d2bs/kolbot/sdk/types/Pather.d.ts | 66 +++++++++++++++---------------- d2bs/kolbot/sdk/types/Skill.d.ts | 36 +++++++++++++++++ d2bs/kolbot/sdk/types/sdk.d.ts | 10 +++++ 3 files changed, 79 insertions(+), 33 deletions(-) diff --git a/d2bs/kolbot/sdk/types/Pather.d.ts b/d2bs/kolbot/sdk/types/Pather.d.ts index e408d4a57..e6a8da194 100644 --- a/d2bs/kolbot/sdk/types/Pather.d.ts +++ b/d2bs/kolbot/sdk/types/Pather.d.ts @@ -1,37 +1,37 @@ export {}; declare global { - const Pather: { - wpAreas: number[]; - walkDistance: number; - teleDistance: number; - teleport: boolean, - cancelFlags: number[], - recursion: boolean, - lastPortalTick: 0, - getWalkDistance(x: number, y: number, area?: number, xx?: number, yy?: number, reductionType?: 0 | 1 | 2, radius?: number) - useTeleport(): boolean, - moveTo(x: number, y: number, retry?: number | undefined, clearPath?: boolean | undefined, pop?: boolean | undefined): boolean, - teleportTo(x: any, y: any, maxRange?: any): void, - walkTo(x: any, y: any, minDist?: number | undefined): boolean, - openDoors(x: any, y: any): boolean, - moveToUnit(unit: PathNode, offX?: undefined, offY?: undefined, clearPath?: undefined, pop?: undefined): boolean, - moveToPreset(area: any, unitType: any, unitId: any, offX?: any, offY?: any, clearPath?: any, pop?: any): boolean, - moveToExit(targetArea: any, use?: any, clearPath?: any): void, - getNearestRoom(area: any): void, - openExit(targetArea: any): void, - openUnit(type: any, id: any): void, - useUnit(type: any, id: any, targetArea: any): boolean, - useWaypoint(targetArea: number | null, check?: boolean): boolean - makePortal(use?: boolean | undefined): void, - usePortal(targetArea?: number | null, owner?: string | undefined, unit?: undefined): boolean, - getPortal(targetArea: any, owner?: any): ObjectUnit | false, - getNearestWalkable(x: any, y: any, range: any, step: any, coll: any, size?: any): [number, number] | false, - checkSpot(x: any, y: any, coll: any, cacheOnly: any, size: any): void, - accessToAct(act: number): boolean, - getWP(area: any, clearPath?: any): boolean, - journeyTo(area: any): boolean, - plotCourse(dest: any, src: any): false|{course:number[]}, - areasConnected(src: any, dest: any): void, - getAreaName(area: number): void, + namespace Pather { + const wpAreas: number[]; + let walkDistance: number; + let teleDistance: number; + let teleport: boolean; + const cancelFlags: number[]; + let recursion: boolean; + let lastPortalTick: 0; + function getWalkDistance(x: number, y: number, area?: number, xx?: number, yy?: number, reductionType?: 0 | 1 | 2, radius?: number): number; + function useTeleport(): boolean; + function moveTo(x: number, y: number, retry?: number | undefined, clearPath?: boolean | undefined, pop?: boolean | undefined): boolean; + function teleportTo(x: any, y: any, maxRange?: any): void; + function walkTo(x: any, y: any, minDist?: number | undefined): boolean; + function openDoors(x: any, y: any): boolean; + function moveToUnit(unit: PathNode, offX?: undefined, offY?: undefined, clearPath?: undefined, pop?: undefined): boolean; + function moveToPreset(area: any, unitType: any, unitId: any, offX?: any, offY?: any, clearPath?: any, pop?: any): boolean; + function moveToExit(targetArea: any, use?: any, clearPath?: any): void; + function getNearestRoom(area: any): void; + function openExit(targetArea: any): void; + function openUnit(type: any, id: any): void; + function useUnit(type: any, id: any, targetArea: any): boolean; + function useWaypoint(targetArea: number | null, check?: boolean): boolean; + function makePortal(use?: boolean | undefined): void; + function usePortal(targetArea?: number | null, owner?: string | undefined, unit?: undefined): boolean; + function getPortal(targetArea: any, owner?: any): ObjectUnit | false; + function getNearestWalkable(x: any, y: any, range: any, step: any, coll: any, size?: any): [number, number] | false; + function checkSpot(x: any, y: any, coll: any, cacheOnly: any, size: any): void; + function accessToAct(act: number): boolean; + function getWP(area: any, clearPath?: any): boolean; + function journeyTo(area: any): boolean; + function plotCourse(dest: any, src: any): false|{course:number[]}; + function areasConnected(src: any, dest: any): void; + function getAreaName(area: number): void; } } diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts index 817577c18..78e38382b 100644 --- a/d2bs/kolbot/sdk/types/Skill.d.ts +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -1,5 +1,41 @@ export {}; declare global { + class SkillDataInfo { + skillId: number; + hand: number; + state: number; + summonCount: () => number; + condition: () => boolean; + townSkill: boolean; + timed: boolean; + missleSkill: boolean; + charClass: number; + reqLevel: number; + preReqs: number[]; + damageType: string; + private _range: number | (() => number); + private _AoE: () => number; + private _duration: () => number; + private _manaCost: number; + private _mana: number; + private _minMana: number; + private _lvlMana: number; + private _manaShift: number; + private _bestSlot: number; + private _dmg: number; + private _hardPoints: number; + private _softPoints: number; + private _checked: boolean; + + constructor(skillId: number); + + duration(): number; + manaCost(): number; + range(pvpRange?: boolean): number; + AoE(): number; + have(): boolean; + reset(): void; + } namespace Skill { let usePvpRange: boolean; const manaCostList: object; diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 702e51876..a1cfedaa6 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4444,10 +4444,18 @@ declare global { const UnableToIndentifyVersion: 5245; const BattlenetNotResponding: 5353; const BattlenetNotResponding2: 5354; + const HcCannotPlayWithSc: 5361; + const ScCannotPlayWithHc: 5362; + const CannotPlayInHellClassic: 5363; + const CannotPlayInNightmareClassic: 5364; const EnhancedDamage: 10038; + const ClassicCannotPlayWithXpac: 10101; + const XpacCannotPlayWithClassic: 10102; const LoDKeyDisabled: 10913; const LodKeyInUseBy: 10914; const LoDKeyIntendedForAnotherProduct: 10915; + const NonLadderCannotPlayWithLadder: 10929; + const LadderCannotPlayWithNonLadder: 10930; const YourPositionInLineIs: 11026; const Gateway: 11049; const Ghostly: 11084; @@ -4456,6 +4464,8 @@ declare global { const Berserker: 11087; const ExpiresIn: 11133; const CdKeyDisabledFromRealm: 11161; + const CannotPlayInHellXpac: 21793; + const CannotPlayInNightmareXpac: 21794; } namespace areas { From 9bb2779b89cffb2944174c7d36aeea2adc28a4d8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 Jun 2023 12:08:39 -0400 Subject: [PATCH 146/758] Update Polyfill.js - add `Set.toString` polyfill and change argmap from arrow to function expression --- d2bs/kolbot/libs/Polyfill.js | 60 ++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 80ff8a287..2805343c1 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -89,7 +89,7 @@ String.prototype.capitalize = function (downcase = false) { return this.charAt(0).toUpperCase() + (downcase ? this.slice(1).toLowerCase() : this.slice(1)); }; -String.prototype.padEnd = function padEnd(targetLength, padString) { +String.prototype.padEnd = function padEnd (targetLength, padString) { targetLength = targetLength >> 0; //floor if number or convert non-number to 0; padString = String(typeof padString !== "undefined" ? padString : " "); if (this.length > targetLength) { @@ -103,7 +103,7 @@ String.prototype.padEnd = function padEnd(targetLength, padString) { } }; -String.prototype.padStart = function padStart(targetLength, padString) { +String.prototype.padStart = function padStart (targetLength, padString) { targetLength = targetLength >> 0; //floor if number or convert non-number to 0; padString = String(typeof padString !== "undefined" ? padString : " "); if (this.length > targetLength) { @@ -117,7 +117,7 @@ String.prototype.padStart = function padStart(targetLength, padString) { } }; -String.prototype.repeat = function(count) { +String.prototype.repeat = function (count) { "use strict"; if (this == null) throw new TypeError("can't convert " + this + " to object"); let str = "" + this; @@ -344,7 +344,7 @@ if (!Array.from) { }; // The length property of the from method is 1. - return function from(arrayLike/*, mapFn, thisArg */) { + return function from (arrayLike/*, mapFn, thisArg */) { // 1. Let C be the this value. let C = this; @@ -566,7 +566,7 @@ if (!Array.prototype.last) { */ if (!Array.prototype.flat) { Object.defineProperty(Array.prototype, "flat", { - value: function flat() { + value: function flat () { let depth = arguments.length > 0 ? isNaN(arguments[0]) ? 1 : Number(arguments[0]) : 1; return depth ? Array.prototype.reduce.call(this, function (acc, cur) { @@ -592,7 +592,7 @@ if (!Array.prototype.flat) { */ if (!Array.of) { Object.defineProperty(Array, "of", { - value: function of() { + value: function of () { return Array.prototype.slice.call(arguments); }, configurable: true, @@ -607,7 +607,7 @@ if (!Array.of) { * @return {Array} */ if (!Array.prototype.toReversed) { - Array.prototype.toReversed = function() { + Array.prototype.toReversed = function () { return this.slice().reverse(); }; } @@ -621,7 +621,7 @@ if (!Array.prototype.toReversed) { * @returns {Array} A new array with the elements sorted in ascending order. */ if (!Array.prototype.toSorted) { - Array.prototype.toSorted = function(compareFunction) { + Array.prototype.toSorted = function (compareFunction) { return this.slice().sort(compareFunction); }; } @@ -638,7 +638,7 @@ if (!Array.prototype.toSorted) { * @returns {Array} A new array with the modified elements. */ if (!Array.prototype.toSpliced) { - Array.prototype.toSpliced = function(start, deleteCount) { + Array.prototype.toSpliced = function (start, deleteCount) { const newArr = this.slice(); const items = Array.prototype.slice.call(arguments, 2); Array.prototype.splice.apply(newArr, [start, deleteCount].concat(items)); @@ -662,7 +662,7 @@ if (!Array.prototype.toSpliced) { */ if (typeof Object.assign !== "function") { Object.defineProperty(Object, "assign", { - value: function assign(target) { + value: function assign (target) { if (target === null) { throw new TypeError("Cannot convert undefined or null to object"); } @@ -713,7 +713,7 @@ if (!global.hasOwnProperty("require")) { !isIncluded("require.js") && include("require.js"); return cache; // cache is loaded by require.js }, - set: function(v) { + set: function (v) { cache = v; } }); @@ -768,7 +768,7 @@ Math.percentDifference = function (value1, value2) { */ if (typeof Map.prototype.forEach !== "function") { - Map.prototype.forEach = function(callbackFn, thisArg) { + Map.prototype.forEach = function (callbackFn, thisArg) { thisArg = thisArg || this; for (let [key, value] of this.entries()) { callbackFn.call(thisArg, value, key, this); @@ -776,7 +776,7 @@ if (typeof Map.prototype.forEach !== "function") { }; } -Map.prototype.toString = function() { +Map.prototype.toString = function () { let obj = {}; for (let [key, value] of this.entries()) { obj[key] = value; @@ -787,7 +787,7 @@ Map.prototype.toString = function() { /** * @returns {Array} */ -Map.prototype.keys = function() { +Map.prototype.keys = function () { let keys = []; // eslint-disable-next-line no-unused-vars for (let [key, _value] of this.entries()) { @@ -796,7 +796,7 @@ Map.prototype.keys = function() { return keys; }; -Map.prototype.values = function() { +Map.prototype.values = function () { let values = []; // eslint-disable-next-line no-unused-vars for (let [_key, value] of this.entries()) { @@ -822,7 +822,7 @@ Map.prototype.values = function() { */ if (typeof Set.prototype.forEach !== "function") { - Set.prototype.forEach = function(callbackFn, thisArg) { + Set.prototype.forEach = function (callbackFn, thisArg) { thisArg = thisArg || this; for (let item of this) { callbackFn.call(thisArg, item, item, this); @@ -831,7 +831,7 @@ if (typeof Set.prototype.forEach !== "function") { } if (typeof Set.prototype.keys !== "function") { - Set.prototype.keys = function() { + Set.prototype.keys = function () { let keys = []; for (let item of this) { keys.push(item); @@ -841,7 +841,7 @@ if (typeof Set.prototype.keys !== "function") { } if (typeof Set.prototype.values !== "function") { - Set.prototype.values = function() { + Set.prototype.values = function () { let values = []; for (let item of this) { values.push(item); @@ -851,7 +851,7 @@ if (typeof Set.prototype.values !== "function") { } if (typeof Set.prototype.entries !== "function") { - Set.prototype.entries = function() { + Set.prototype.entries = function () { let entries = []; for (let item of this) { entries.push([item, item]); @@ -860,7 +860,7 @@ if (typeof Set.prototype.entries !== "function") { }; } -Set.prototype.isSuperset = function(subset) { +Set.prototype.isSuperset = function (subset) { for (let item of subset) { if (!this.has(item)) { return false; @@ -869,7 +869,7 @@ Set.prototype.isSuperset = function(subset) { return true; }; -Set.prototype.union = function(setB) { +Set.prototype.union = function (setB) { let union = new Set(this); for (let item of setB) { union.add(item); @@ -877,7 +877,7 @@ Set.prototype.union = function(setB) { return union; }; -Set.prototype.intersection = function(setB) { +Set.prototype.intersection = function (setB) { let intersection = new Set(); for (let item of setB) { if (this.has(item)) { @@ -887,7 +887,7 @@ Set.prototype.intersection = function(setB) { return intersection; }; -Set.prototype.symmetricDifference = function(setB) { +Set.prototype.symmetricDifference = function (setB) { let difference = new Set(this); for (let item of setB) { if (difference.has(item)) { @@ -899,7 +899,7 @@ Set.prototype.symmetricDifference = function(setB) { return difference; }; -Set.prototype.difference = function(setB) { +Set.prototype.difference = function (setB) { let difference = new Set(this); for (let item of setB) { difference.delete(item); @@ -907,6 +907,10 @@ Set.prototype.difference = function(setB) { return difference; }; +Set.prototype.toString = function () { + return JSON.stringify(this.values()); +}; + /** * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // * ~~~~~~~~~~~~~~~~~~~~~~~~~~ console Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @@ -927,7 +931,7 @@ Set.prototype.difference = function(setB) { global.console = global.console || (function () { const console = {}; - const argMap = (el) => { + const argMap = function (el) { switch (typeof el) { case "undefined": return "undefined"; @@ -948,11 +952,15 @@ Set.prototype.difference = function(setB) { } if (el instanceof Map) { return el.toString(); + } else if (el instanceof Set) { + return el.toString(); } if (Array.isArray(el)) { // handle multidimensional arrays return JSON.stringify( - el.map(inner => Array.isArray(inner) ? inner.map(argMap) : inner) + el.map(function (inner) { + return Array.isArray(inner) ? inner.map(argMap) : inner; + }) ); } return JSON.stringify(el); From 7d7e972b1ac1573aa2d7a915764ca3d7519f47ef Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 9 Jun 2023 12:20:25 -0400 Subject: [PATCH 147/758] Update OOG.js - should fix login error --- d2bs/kolbot/libs/OOG.js | 746 +++++++++++++++++++++------------------- 1 file changed, 396 insertions(+), 350 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index c1fe433c8..6c9dce875 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -24,7 +24,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } else { Object.assign(root, factory()); } -}([].filter.constructor("return this")(), function() { +}([].filter.constructor("return this")(), function () { const Controls = require("./modules/Control"); const ControlAction = { @@ -808,7 +808,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return false; }, - getQueueTime: function() { + getQueueTime: function () { // You are in line to create a game.,Try joining a game to avoid waiting.,,Your position in line is: ÿc02912 const text = Controls.CreateGameInLine.getText(); if (text && text.indexOf(getLocaleString(sdk.locale.text.YourPositionInLineIs)) > -1) { @@ -963,7 +963,8 @@ includeIfNotIncluded("oog/D2Bot.js"); // required setNextGame: function (gameInfo = {}) { let nextGame = (gameInfo.gameName || this.randomString(null, true)); - if ((this.gameCount + 1 >= Starter.Config.ResetCount) || (nextGame.length + this.gameCount + 1 > 15)) { + if ((this.gameCount + 1 >= Starter.Config.ResetCount) + || (nextGame.length + this.gameCount + 1 > 15)) { nextGame += "1"; } else { nextGame += (this.gameCount + 1); @@ -1033,23 +1034,21 @@ includeIfNotIncluded("oog/D2Bot.js"); // required * @param {object | string} msg */ receiveCopyData: function (mode, msg) { - let obj; + if (msg === "Handle" && typeof mode === "number") { + // console.debug("Recieved Handle :: " + mode); + Starter.handle = mode; - if (msg === "Handle") { - console.debug("Recieved Handle :: ", mode); + return; } - msg === "Handle" && typeof mode === "number" && (Starter.handle = mode); + + let obj = JSON.parse(msg); switch (mode) { case 1: // JoinInfo - obj = JSON.parse(msg); - console.debug("Recieved Join Info :: ", obj); Object.assign(Starter.joinInfo, obj); break; case 2: // Game info - obj = JSON.parse(msg); - console.debug("Recieved Game Info :: "); Object.assign(Starter.gameInfo, obj); break; @@ -1063,7 +1062,6 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } if (Starter.gameInfo.hasOwnProperty("gameName")) { - obj = JSON.parse(msg); console.debug("Recieved Game Request :: ", obj.profile); if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { @@ -1073,8 +1071,17 @@ includeIfNotIncluded("oog/D2Bot.js"); // required D2Bot.joinMe(obj.profile, me.gamename, "", me.gamepassword, Starter.isUp); } else { // If we haven't made it to the lobby yet but are already getting game requests, stop the spam by telling followers to delay - let delay = (Starter.delay === 0 && !me.ingame && getLocation() !== sdk.game.locations.CreateGame) ? 3000 : Starter.delay; - D2Bot.joinMe(obj.profile, Starter.gameInfo.gameName, Starter.gameCount, Starter.gameInfo.gamePass, Starter.isUp, delay); + let delay = (Starter.delay === 0 && !me.ingame && getLocation() !== sdk.game.locations.CreateGame) + ? 3000 + : Starter.delay; + D2Bot.joinMe( + obj.profile, + Starter.gameInfo.gameName, + Starter.gameCount, + Starter.gameInfo.gamePass, + Starter.isUp, + delay + ); } } } @@ -1085,19 +1092,20 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; case 61732: // Cached info retreival - msg !== "null" && (Starter.gameInfo.crashInfo = JSON.parse(msg)); + msg !== "null" && (Starter.gameInfo.crashInfo = obj); break; case 1638: // getProfile try { - obj = JSON.parse(msg); Starter.profileInfo.profile = me.profile; Starter.profileInfo.account = obj.account; Starter.profileInfo.charName = obj.Character; obj.Realm = obj.Realm.toLowerCase(); - Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) ? "us" + obj.Realm : obj.Realm; + Starter.profileInfo.realm = ["east", "west"].includes(obj.Realm) + ? "us" + obj.Realm + : obj.Realm; } catch (e) { - print(e); + console.error(e); } break; @@ -1108,7 +1116,9 @@ includeIfNotIncluded("oog/D2Bot.js"); // required !len && (len = rand(5, 14)); let rval = ""; - let letters = useNumbers ? "abcdefghijklmnopqrstuvwxyz0123456789" : "abcdefghijklmnopqrstuvwxyz"; + let letters = useNumbers + ? "abcdefghijklmnopqrstuvwxyz0123456789" + : "abcdefghijklmnopqrstuvwxyz"; for (let i = 0; i < len; i += 1) { rval += letters[rand(0, letters.length - 1)]; @@ -1130,61 +1140,45 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return rval; }, - LocationEvents: { - selectDifficultySP: function () { - let diff = (Starter.gameInfo.difficulty || "Highest"); - diff === "Highest" && (diff = "Hell"); // starts from top with fall-through to select highest - - switch (diff) { - case "Hell": - if (Controls.HellSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { - break; - } - // eslint-disable-next-line no-fallthrough - case "Nightmare": - if (Controls.NightmareSP.click() && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { - break; - } - // eslint-disable-next-line no-fallthrough - case "Normal": - Controls.NormalSP.click(); - - break; - } - return Starter.locationTimeout(5e3, sdk.game.locations.SelectDifficultySP); - }, - - loginError: function () { - let cdkeyError = false; - let defaultPrint = true; - let string = ""; - let text = getLocation() === sdk.game.locations.LoginError - ? Controls.LoginErrorText.getText() - : Controls.LoginCdKeyInUseBy.getText(); - - if (text) { - for (let i = 0; i < text.length; i += 1) { - string += text[i]; - i !== text.length - 1 && (string += " "); - } + LocationEvents: (function () { + /** + * @param {Control} control + * @returns {string} + */ + const parseControlText = function (control) { + if (!control) return ""; + let text = control.getText(); + if (!text || !text.length) return ""; + return text.join(" "); + }; + + const _locMap = new Map([ + [sdk.game.locations.LoginError, function () { + let string = parseControlText(Controls.LoginErrorText); 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(); + case getLocaleString(sdk.locale.text.InvalidPassword): + case getLocaleString(5208): // Invalid account + D2Bot.updateStatus("Invalid Account Name"); + D2Bot.printToConsole("Invalid Account Name :: " + Starter.profileInfo.account); + D2Bot.stop(true); break; - case getLocaleString(sdk.locale.text.InvalidPassword): - D2Bot.printToConsole("Invalid Password"); - ControlAction.timeoutDelay("Invalid password delay", Starter.Config.InvalidPasswordDelay * 6e4); - D2Bot.printToConsole("Invalid Password - Restart"); - D2Bot.restart(); + case getLocaleString(sdk.locale.text.UnableToCreateAccount): + case getLocaleString(5239): // An account name already exists + if (!Starter.accountExists) { + Starter.accountExists = true; + Controls.LoginErrorOk.click(); + Starter.locationTimeout(1000, sdk.game.locations.LoginError); + Controls.BottomLeftExit.click(); + Starter.locationTimeout(1000, sdk.game.locations.CreateNewAccount); + return true; + } + D2Bot.updateStatus("Account name already exists :: " + Starter.profileInfo.account); + D2Bot.printToConsole("Account name already exists :: " + Starter.profileInfo.account); + D2Bot.stop(true); break; case getLocaleString(sdk.locale.text.AccountDoesNotExist): @@ -1192,106 +1186,106 @@ includeIfNotIncluded("oog/D2Bot.js"); // required Starter.makeAccount = true; Controls.LoginErrorOk.click(); - return; + return true; } else { D2Bot.printToConsole(string); D2Bot.updateStatus(string); + D2Bot.stop(true); } break; - case getLocaleString(sdk.locale.text.AccountIsCorrupted): - case getLocaleString(sdk.locale.text.UnableToCreateAccount): - D2Bot.printToConsole(string); - D2Bot.updateStatus(string); - - break; - 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.CdKeyIntendedForAnotherProduct): case getLocaleString(sdk.locale.text.LoDKeyIntendedForAnotherProduct): case getLocaleString(sdk.locale.text.CdKeyDisabled): case getLocaleString(sdk.locale.text.LoDKeyDisabled): - cdkeyError = true; - - break; - case getLocaleString(sdk.locale.text.CdKeyInUseBy): - string += (" " + Controls.LoginLodKeyInUseBy.getText()); - D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyInUse(); + D2Bot.updateStatus("Disabled CDKey"); + D2Bot.printToConsole("Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyDisabled(); if (Starter.gameInfo.switchKeys) { - cdkeyError = true; + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); } else { - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("LoD key in use", Starter.Config.CDKeyInUseDelay * 6e4); - - return; + D2Bot.stop(); } break; - case getLocaleString(sdk.locale.text.LoginError): + case getLocaleString(sdk.locale.text.Disconnected): + ControlAction.timeoutDelay("Disconnected from battle.net", Time.minutes(1)); + return Controls.LoginErrorOk.click(); 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; + ControlAction.timeoutDelay("[R/D] - " + string, Time.minutes(10)); + return Controls.LoginErrorOk.click(); 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(); } - } - Controls.LoginErrorOk.click(); - delay(1000); - Controls.BottomLeftExit.click(); - - while (true) { - delay(1000); + break; } - } - }, - charSelectError: function () { - let string = ""; - let text = Controls.CharSelectError.getText(); - let currentLoc = getLocation(); + return Controls.LoginErrorOk.click(); + }], + [sdk.game.locations.OkCenteredErrorPopUp, function () { + let string = parseControlText(Controls.OkCenteredText); - if (text) { - for (let i = 0; i < text.length; i += 1) { - string += text[i]; - i !== text.length - 1 && (string += " "); + switch (string) { + case getLocaleString(sdk.locale.text.CannotCreateGamesDeadHCChar): + Starter.deadCheck = true; + return Controls.OkCentered.click(); + 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; + default: + D2Bot.updateStatus("Error"); + D2Bot.printToConsole("Error - " + string); + + break; } + Controls.OkCentered.click(); + ControlAction.timeoutDelay("Error", Time.minutes(1)); - if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { - D2Bot.updateStatus("Realm Disabled CDKey"); - D2Bot.printToConsole("Realm Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); + return true; + }], + [sdk.game.locations.CdKeyInUse, function () { + let string = parseControlText(Controls.LoginCdKeyInUseBy); + + if (string === getLocaleString(sdk.locale.text.CdKeyInUseBy)) { + let who = Controls.LoginCdKeyInUseBy.getText(); + D2Bot.printToConsole(Starter.gameInfo.mpq + " " + string + " " + who, sdk.colors.D2Bot.Gold); + D2Bot.CDKeyInUse(); + Controls.UnableToConnectOk.click(); + + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + ControlAction.timeoutDelay("Cd key in use by: " + who, Starter.Config.CDKeyInUseDelay * 6e4); + } + } + return true; + }], + [sdk.game.locations.InvalidCdKey, function () { + let string = parseControlText(Controls.LoginInvalidCdKey); + + if (string === getLocaleString(sdk.locale.text.CdKeyIntendedForAnotherProduct) + || string === getLocaleString(sdk.locale.text.LoDKeyIntendedForAnotherProduct)) { + D2Bot.updateStatus("Invalid CDKey"); + D2Bot.printToConsole("Invalid CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); D2Bot.CDKeyDisabled(); if (Starter.gameInfo.switchKeys) { @@ -1301,286 +1295,338 @@ includeIfNotIncluded("oog/D2Bot.js"); // required D2Bot.stop(); } } - } - - if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, currentLoc)) { - // Click create char button on infinite "connecting" screen - Controls.CharSelectCreate.click(); - delay(1000); - - Controls.BottomLeftExit.click(); - delay(1000); - - if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; - - Controls.BottomLeftExit.click(); - Starter.gameInfo.rdBlocker && D2Bot.restart(); - - return false; - } - - return true; - }, - - realmDown: function () { - D2Bot.updateStatus("Realm Down"); - delay(1000); + return true; + }], + ]); + + return { + selectDifficultySP: function () { + let diff = (Starter.gameInfo.difficulty || "Highest"); + diff === "Highest" && (diff = "Hell"); // starts from top with fall-through to select highest + + switch (diff) { + case "Hell": + if (Controls.HellSP.click() + && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { + break; + } + // eslint-disable-next-line no-fallthrough + case "Nightmare": + if (Controls.NightmareSP.click() + && Starter.locationTimeout(1e3, sdk.game.locations.SelectDifficultySP)) { + break; + } + // eslint-disable-next-line no-fallthrough + case "Normal": + Controls.NormalSP.click(); - if (!Controls.BottomLeftExit.click()) return; + break; + } + return Starter.locationTimeout(5e3, sdk.game.locations.SelectDifficultySP); + }, - Starter.updateCount(); - ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); - D2Bot.CDKeyRD(); + loginError: function () { + let _loc = getLocation(); - if (Starter.gameInfo.switchKeys && !Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole("Realm Down - Changing CD-Key"); - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.printToConsole("Realm Down - Restart"); - D2Bot.restart(); - } - }, + if (_locMap.has(_loc)) { + _locMap.get(_loc)(); + } else { + D2Bot.printToConsole("Unhandled location: " + _loc); + ControlAction.timeoutDelay("Unhandled location: " + _loc, Time.minutes(10)); + D2Bot.restart(); + } + }, - waitingInLine: function () { - let queue = ControlAction.getQueueTime(); - let currentLoc = getLocation(); + charSelectError: function () { + let string = parseControlText(Controls.CharSelectError); + let currentLoc = getLocation(); - if (queue > 0) { - switch (true) { - case (queue < 10000): - D2Bot.updateStatus("Waiting line... Queue: " + queue); + if (string) { + 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 stuck here for too long, game creation likely failed. Exit to char selection and try again. - if (queue < 10) { - if (!Starter.locationTimeout(Starter.Config.WaitInLineTimeout * 1e3, currentLoc)) { - print("Failed to create game"); - Controls.CancelCreateGame.click(); - Controls.LobbyQuit.click(); - delay(1000); + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); } } + } - break; - case (queue > 10000): - if (Starter.Config.WaitOutQueueRestriction) { - D2Bot.updateStatus("Waiting out Queue restriction: " + queue); - } else { - print("Restricted... Queue: " + queue); - D2Bot.printToConsole("Restricted... Queue: " + queue, sdk.colors.D2Bot.Red); - Controls.CancelCreateGame.click(); - - if (Starter.Config.WaitOutQueueExitToMenu) { - Controls.LobbyQuit.click(); - delay(1000); - Controls.BottomLeftExit.click(); - } - - // Wait out each queue as 1 sec and add extra 10 min - ControlAction.timeoutDelay("Restricted", (queue + 600) * 1000); - } + if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, currentLoc)) { + // Click create char button on infinite "connecting" screen + Controls.CharSelectCreate.click(); + delay(1000); + + Controls.BottomLeftExit.click(); + delay(1000); + + if (getLocation() !== sdk.game.locations.CharSelectConnecting) return true; + + Controls.BottomLeftExit.click(); + Starter.gameInfo.rdBlocker && D2Bot.restart(); - break; + return false; } - } - }, - gameDoesNotExist: function () { - let currentLoc = getLocation(); - console.log("Game doesn't exist"); + return true; + }, - if (Starter.gameInfo.rdBlocker) { - D2Bot.printToConsole(Starter.gameInfo.mpq + " is probably flagged.", sdk.colors.D2Bot.Gold); + realmDown: function () { + D2Bot.updateStatus("Realm Down"); + delay(1000); + + if (!Controls.BottomLeftExit.click()) return; - if (Starter.gameInfo.switchKeys) { + Starter.updateCount(); + ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); + D2Bot.CDKeyRD(); + + if (Starter.gameInfo.switchKeys && !Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole("Realm Down - Changing CD-Key"); ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); D2Bot.restart(true); + } else { + D2Bot.printToConsole("Realm Down - Restart"); + D2Bot.restart(); } - } else { - Starter.locationTimeout(Starter.Config.GameDoesNotExistTimeout * 1e3, currentLoc); - } - - Starter.lastGameStatus = "ready"; - }, + }, + + waitingInLine: function () { + let queue = ControlAction.getQueueTime(); + let currentLoc = getLocation(); + + if (queue > 0) { + switch (true) { + case (queue < 10000): + D2Bot.updateStatus("Waiting line... Queue: " + queue); + + // If stuck here for too long, game creation likely failed. Exit to char selection and try again. + if (queue < 10) { + if (!Starter.locationTimeout(Starter.Config.WaitInLineTimeout * 1e3, currentLoc)) { + print("Failed to create game"); + Controls.CancelCreateGame.click(); + Controls.LobbyQuit.click(); + delay(1000); + } + } - unableToConnect: function () { - let currentLoc = getLocation(); + break; + case (queue > 10000): + if (Starter.Config.WaitOutQueueRestriction) { + D2Bot.updateStatus("Waiting out Queue restriction: " + queue); + } else { + print("Restricted... Queue: " + queue); + D2Bot.printToConsole("Restricted... Queue: " + queue, sdk.colors.D2Bot.Red); + Controls.CancelCreateGame.click(); - if (getLocation() === sdk.game.locations.TcpIpUnableToConnect) { - D2Bot.updateStatus("Unable To Connect TCP/IP"); - Starter.connectFail && ControlAction.timeoutDelay("Unable to Connect", Starter.Config.TCPIPNoHostDelay * 1e3); - Controls.OkCentered.click(); - Starter.connectFail = !Starter.connectFail; - } else { - D2Bot.updateStatus("Unable To Connect"); + if (Starter.Config.WaitOutQueueExitToMenu) { + Controls.LobbyQuit.click(); + delay(1000); + Controls.BottomLeftExit.click(); + } - if (Starter.connectFailRetry < 2) { - Starter.connectFailRetry++; - Controls.UnableToConnectOk.click(); + // Wait out each queue as 1 sec and add extra 10 min + ControlAction.timeoutDelay("Restricted", (queue + 600) * 1000); + } - return; + break; + } } + }, - Starter.connectFailRetry >= 2 && (Starter.connectFail = true); + gameDoesNotExist: function () { + let currentLoc = getLocation(); + console.log("Game doesn't exist"); - if (Starter.connectFail && !Starter.locationTimeout(10e4, currentLoc)) { - let string = ""; - let text = Controls.LoginUnableToConnect.getText(); + if (Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole(Starter.gameInfo.mpq + " is probably flagged.", sdk.colors.D2Bot.Gold); - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; - i !== text.length - 1 && (string += " "); - } + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); } - - switch (string) { - case getLocaleString(sdk.locale.text.UnableToIndentifyVersion): - Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("Version error", Starter.Config.VersionErrorDelay * 1000); - - break; - default: // Regular UTC and everything else + } else { + Starter.locationTimeout(Starter.Config.GameDoesNotExistTimeout * 1e3, currentLoc); + } + + Starter.lastGameStatus = "ready"; + }, + + unableToConnect: function () { + let currentLoc = getLocation(); + + if (getLocation() === sdk.game.locations.TcpIpUnableToConnect) { + D2Bot.updateStatus("Unable To Connect TCP/IP"); + Starter.connectFail && ControlAction.timeoutDelay("Unable to Connect", Starter.Config.TCPIPNoHostDelay * 1e3); + Controls.OkCentered.click(); + Starter.connectFail = !Starter.connectFail; + } else { + D2Bot.updateStatus("Unable To Connect"); + + if (Starter.connectFailRetry < 2) { + Starter.connectFailRetry++; Controls.UnableToConnectOk.click(); - ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 1000 * 60); - - break; - } - Starter.connectFail = false; - } + return; + } - if (!Controls.UnableToConnectOk.click()) { - return; - } + Starter.connectFailRetry >= 2 && (Starter.connectFail = true); - Starter.connectFail = true; - Starter.connectFailRetry = 0; - } - }, + if (Starter.connectFail && !Starter.locationTimeout(10e4, currentLoc)) { + let string = parseControlText(Controls.LoginUnableToConnect); + + switch (string) { + case getLocaleString(sdk.locale.text.UnableToIndentifyVersion): + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("Version error", Starter.Config.VersionErrorDelay * 1000); + + break; + default: // Regular UTC and everything else + Controls.UnableToConnectOk.click(); + ControlAction.timeoutDelay("Unable to Connect", Starter.Config.UnableToConnectDelay * 1000 * 60); + + break; + } - openCreateGameWindow: function () { - let currentLoc = getLocation(); + Starter.connectFail = false; + } - if (!Controls.CreateGameWindow.click()) { - return true; - } + if (!Controls.UnableToConnectOk.click()) { + return; + } - // dead HardCore character - if (Controls.CreateGameWindow.control && Controls.CreateGameWindow.disabled === sdk.game.controls.Disabled) { - if (Starter.Config.StopOnDeadHardcore) { - D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Shutting down, better luck next time", sdk.colors.D2Bot.Gold); - D2Bot.stop(); - } else { - D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Better luck next time", sdk.colors.D2Bot.Gold); - D2Bot.updateStatus(Profile().character + " has died. They shall be remembered...maybe. Better luck next time"); - Starter.deadCheck = true; - Controls.LobbyQuit.click(); + Starter.connectFail = true; + Starter.connectFailRetry = 0; } + }, - return false; - } + openCreateGameWindow: function () { + let currentLoc = getLocation(); - // in case create button gets bugged - if (!Starter.locationTimeout(5000, currentLoc)) { if (!Controls.CreateGameWindow.click()) { return true; } - if (!Controls.JoinGameWindow.click()) { - return true; + // dead HardCore character + if (Controls.CreateGameWindow.control && Controls.CreateGameWindow.disabled === sdk.game.controls.Disabled) { + if (Starter.Config.StopOnDeadHardcore) { + D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Shutting down, better luck next time", sdk.colors.D2Bot.Gold); + D2Bot.stop(); + } else { + D2Bot.printToConsole(Profile().character + " has died. They shall be remembered...maybe. Better luck next time", sdk.colors.D2Bot.Gold); + D2Bot.updateStatus(Profile().character + " has died. They shall be remembered...maybe. Better luck next time"); + Starter.deadCheck = true; + Controls.LobbyQuit.click(); + } + + return false; } - } - return (getLocation() === sdk.game.locations.CreateGame); - }, + // in case create button gets bugged + if (!Starter.locationTimeout(5000, currentLoc)) { + if (!Controls.CreateGameWindow.click()) { + return true; + } - openJoinGameWindow: function () { - let currentLoc = getLocation(); + if (!Controls.JoinGameWindow.click()) { + return true; + } + } - if (!Controls.JoinGameWindow.click()) { - return; - } + return (getLocation() === sdk.game.locations.CreateGame); + }, - // in case create button gets bugged - if (!Starter.locationTimeout(5000, currentLoc)) { - if (!Controls.CreateGameWindow.click()) { - return; - } + openJoinGameWindow: function () { + let currentLoc = getLocation(); if (!Controls.JoinGameWindow.click()) { return; } - } - }, - login: function (otherMultiCheck = false) { - Starter.inGame && (Starter.inGame = false); - if (otherMultiCheck && [sdk.game.gametype.SinglePlayer, sdk.game.gametype.BattleNet].indexOf(Profile().type) === -1) { - return ControlAction.loginOtherMultiplayer(); - } + // in case create button gets bugged + if (!Starter.locationTimeout(5000, currentLoc)) { + if (!Controls.CreateGameWindow.click()) { + return; + } - if (getLocation() === sdk.game.locations.MainMenu) { - if (Profile().type === sdk.game.profiletype.SinglePlayer - && Starter.firstRun - && Controls.SinglePlayer.click()) { - return true; + if (!Controls.JoinGameWindow.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.BottomLeftExit.click(); - - return false; + login: function (otherMultiCheck = false) { + Starter.inGame && (Starter.inGame = false); + if (otherMultiCheck && [sdk.game.gametype.SinglePlayer, sdk.game.gametype.BattleNet].indexOf(Profile().type) === -1) { + return ControlAction.loginOtherMultiplayer(); } - } - // 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"); + if (getLocation() === sdk.game.locations.MainMenu) { + if (Profile().type === sdk.game.profiletype.SinglePlayer + && Starter.firstRun + && Controls.SinglePlayer.click()) { + return true; + } + } + + // 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.BottomLeftExit.click(); - try { - login(me.profile); - } catch (e) { - if (getLocation() === sdk.game.locations.CharSelect && Starter.loginRetry < 2) { - if (!ControlAction.findCharacter(Starter.profileInfo)) { - // dead hardcore character on sp - if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { - // Exit from that pop-up - Controls.OkCentered.click(); - D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); - D2Bot.stop(); + return false; + } + } + + // 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 { + login(me.profile); + } catch (e) { + if (getLocation() === sdk.game.locations.CharSelect && Starter.loginRetry < 2) { + if (!ControlAction.findCharacter(Starter.profileInfo)) { + // dead hardcore character on sp + if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { + // Exit from that pop-up + Controls.OkCentered.click(); + D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } else { + Starter.loginRetry++; + } } else { - Starter.loginRetry++; + login(me.profile); } + } else if (getLocation() === sdk.game.locations.TcpIpEnterIp && Profile().type === sdk.game.profiletype.TcpIpJoin) { + return true; // handled in its own case } else { - login(me.profile); + print(e + " " + getLocation()); } - } else if (getLocation() === sdk.game.locations.TcpIpEnterIp && Profile().type === sdk.game.profiletype.TcpIpJoin) { - return true; // handled in its own case - } else { - print(e + " " + getLocation()); } - } - return true; - }, + return true; + }, - otherMultiplayerSelect: function () { - if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { - Controls.TcpIp.click() && (Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpJoin.click()); - } else if (Profile().type === sdk.game.profiletype.OpenBattlenet) { - Controls.OpenBattleNet.click(); - } else { - Controls.OtherMultiplayerCancel.click(); + otherMultiplayerSelect: function () { + if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { + Controls.TcpIp.click() && (Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpJoin.click()); + } else if (Profile().type === sdk.game.profiletype.OpenBattlenet) { + Controls.OpenBattleNet.click(); + } else { + Controls.OtherMultiplayerCancel.click(); + } } - } - }, + }; + })(), }; return { From 252a20113c030654da9164d3b0a00e94e8b27d5f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 00:16:59 -0400 Subject: [PATCH 148/758] Update ShitList.js - some typedefs and fixed where the file was created at --- d2bs/kolbot/libs/oog/ShitList.js | 61 +++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/libs/oog/ShitList.js b/d2bs/kolbot/libs/oog/ShitList.js index 76991e04c..7d55ff012 100644 --- a/d2bs/kolbot/libs/oog/ShitList.js +++ b/d2bs/kolbot/libs/oog/ShitList.js @@ -10,19 +10,38 @@ includeIfNotIncluded("oog/FileAction.js"); const ShitList = { _default: { + /** @type {Array} */ shitlist: [] }, + _path: "logs/shitlist.json", + _list: new Set(), + + /** + * @private + * @returns {{ shitlist: Array }}} + */ create: function () { let string = JSON.stringify(this._default); + FileAction.write(this._path, string); + + return Object.assign({}, this._default); + }, - FileAction.write("shitlist.json", string); + reset: function () { + let string = JSON.stringify(this._default); + FileAction.write(this._path, string); + this._list.clear(); - return obj; + return Object.assign({}, this._default); }, + /** + * @private + * @returns {{ shitlist: Array }}} + */ getObj: function () { let obj; - let string = FileAction.read("shitlist.json"); + let string = FileAction.read(this._path); try { obj = JSON.parse(string); @@ -36,24 +55,48 @@ const ShitList = { console.warn("Failed to read ShitList. Using null values"); - return this._default; + return Object.assign({}, this._default); }, + /** @param {Array} name */ read: function () { - !FileTools.exists("shitlist.json") && this.create(); - + if (!FileTools.exists(this._path)) { + return this.create().shitlist; + } let obj = this.getObj(); - + if (!this._list.size) { + obj.shitlist.forEach(name => this._list.add(name)); + } return obj.shitlist; }, + /** @param {string} name */ add: function (name) { let obj = this.getObj(); - obj.shitlist.push(name); + this._list.add(name); let string = JSON.stringify(obj); - FileAction.write("shitlist.json", string); + FileAction.write(this._path, string); + }, + + /** @param {string} name */ + remove: function (name) { + let obj = this.getObj(); + let index = obj.shitlist.indexOf(name); + if (index === -1) return false; + obj.shitlist.splice(index, 1); + this._list.delete(name); + + let string = JSON.stringify(obj); + + FileAction.write(this._path, string); + return true; + }, + + /** @param {string} name */ + has: function (name) { + return this._list.has(name); } }; From 2384893c0294fc85b87f0dd46903ea285b35d7aa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 00:17:58 -0400 Subject: [PATCH 149/758] Update AntiHostile.js - handle updating shitlist --- d2bs/kolbot/threads/AntiHostile.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/threads/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js index 67b2e5a49..1e4b7aadd 100644 --- a/d2bs/kolbot/threads/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -16,7 +16,9 @@ includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -function main() { +include("oog/ShitList.js"); + +function main () { // Variables and functions let player, attackCount, prevPos, check, missile, outside; let charClass = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; @@ -49,9 +51,14 @@ function main() { if (party) { do { - if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8) && hostiles.indexOf(party.name) === -1) { + if (party.name !== me.name + && getPlayerFlag(me.gid, party.gid, 8) + && hostiles.indexOf(party.name) === -1) { D2Bot.printToConsole(party.name + " (Level " + party.level + " " + charClass[party.classid] + ")" + " has declared hostility.", sdk.colors.D2Bot.Orange); hostiles.push(party.name); + if (Config.ShitList) { + ShitList.add(party.name); + } } } while (party.getNext()); } @@ -139,7 +146,8 @@ function main() { Skill.usePvpRange = true; // Attack sequence adjustments - this only affects the AntiHostile thread - if (Skill.canUse(sdk.skills.MindBlast) && [sdk.skills.FireBlast, sdk.skills.ShockWeb].includes(Config.AttackSkill[1])) { + if (Skill.canUse(sdk.skills.MindBlast) + && [sdk.skills.FireBlast, sdk.skills.ShockWeb].includes(Config.AttackSkill[1])) { Config.AttackSkill[1] = sdk.skills.MindBlast; ClassAttack.trapRange = 40; } From 0cf1c6c17645c8f9f5cd749171289f035b5ba3c1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 00:18:40 -0400 Subject: [PATCH 150/758] Update SimpleParty.js - handle unpartying and ignoring shitlisted players --- d2bs/kolbot/libs/modules/SimpleParty.js | 41 ++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/SimpleParty.js b/d2bs/kolbot/libs/modules/SimpleParty.js index d85a029a9..fc7afcdbb 100644 --- a/d2bs/kolbot/libs/modules/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/SimpleParty.js @@ -1,4 +1,9 @@ (function (module, require) { + // party thread specific + include("oog/ShitList.js"); + const shitList = (Config.ShitList || Config.UnpartyShitlisted) + ? ShitList.read() + : []; const Worker = require("Worker"); const NO_PARTY = 65535; const PARTY_MEMBER = 1; @@ -117,10 +122,26 @@ || biggestPartyId === myPartyId ) ) { + if (getPlayerFlag(me.gid, party.gid, sdk.player.flag.Hostile)) { + if (shitList.includes(party.name)) { + say(party.name + " has been shitlisted."); + shitList.push(party.name); + ShitList.add(party.name); + } + + if (player.partyflag === sdk.party.flag.Cancel) { + clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); // cancel invitation + } + + continue; + } else if (shitList.includes(party.name)) { + continue; + } + // if player isn't invited, invite clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); } - + // Deal with accepting if ( party.partyflag === ACCEPTABLE @@ -137,6 +158,9 @@ if (acceptFirst !== party.name) { continue; // Ignore party acceptation } + if (shitList.includes(party.name)) { + continue; + } } clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); @@ -151,6 +175,21 @@ clickParty(party, BUTTON_LEAVE_PARTY); } + if (Config.UnpartyShitlisted) { + // Add new hostile players to temp shitlist, leader should have Config.ShitList set to true to update the permanent list. + if (getPlayerFlag(me.gid, party.gid, sdk.player.flag.Hostile) + && shitList.includes(party.name)) { + shitList.push(player.name); + } + + if (shitList.includes(player.name) + && myPartyId !== NO_PARTY + && party.partyid === myPartyId) { + console.log("Unpartying shitlisted player: " + party.name); + clickParty(party, BUTTON_LEAVE_PARTY); + delay(100); + } + } } return true; }; From a9d4695c54c72251a81fc09516a2fb34905a961e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 00:20:19 -0400 Subject: [PATCH 151/758] Update ShitList.js - don't add duplicates --- d2bs/kolbot/libs/oog/ShitList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/oog/ShitList.js b/d2bs/kolbot/libs/oog/ShitList.js index 7d55ff012..a77e3767d 100644 --- a/d2bs/kolbot/libs/oog/ShitList.js +++ b/d2bs/kolbot/libs/oog/ShitList.js @@ -73,6 +73,7 @@ const ShitList = { /** @param {string} name */ add: function (name) { let obj = this.getObj(); + if (obj.shitlist.includes(name)) return; obj.shitlist.push(name); this._list.add(name); From 54f435c87d481fd823bbe7d9901c33706ae5c0b2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 11:52:27 -0400 Subject: [PATCH 152/758] 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 --- d2bs/kolbot/default.dbj | 46 +++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 7a75165f1..2e1800abe 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -1,9 +1,9 @@ -/* eslint-disable max-len */ /** * @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 @@ -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) { @@ -119,7 +126,11 @@ function main () { addEventListener("copydata", this.copyDataEvent); // GameAction/AutoMule/TorchSystem/Gambling/Crafting handler - if (GameAction.inGameCheck() || AutoMule.inGameCheck() || TorchSystem.inGameCheck() || Gambling.inGameCheck() || CraftingSystem.inGameCheck()) { + if (GameAction.inGameCheck() + || AutoMule.inGameCheck() + || TorchSystem.inGameCheck() + || Gambling.inGameCheck() + || CraftingSystem.inGameCheck()) { return true; } @@ -128,8 +139,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.debugInfo.currScript, 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 + "\n" + + "Experience decreased by " + (stats.experience - me.getStat(sdk.stats.Experience)), + sdk.colors.D2Bot.Red + ); DataFile.updateStats("deaths"); D2Bot.updateDeaths(); } @@ -138,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"); @@ -172,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); + }); } } @@ -202,7 +222,10 @@ function main () { Town.goToTown(); while (getTickCount() - startTime < Config.MinGameTime * 1000) { - me.overhead("Stalling for " + Math.round(((startTime + (Config.MinGameTime * 1000)) - getTickCount()) / 1000) + " Seconds"); + me.overhead( + "Stalling for " + + Math.round(((startTime + Time.seconds(Config.MinGameTime)) - getTickCount()) / 1000) + " Seconds" + ); delay(1000); } } catch (e1) { @@ -252,7 +275,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 329fb246b422420948b6b79ac358d5aee83c04ec Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:43:47 -0400 Subject: [PATCH 153/758] typedef updates - mostly just formatting, but removed some functions that don't exist anymore like `Misc.itemLogger`, ect --- d2bs/kolbot/sdk/globals.d.ts | 2282 +++--- d2bs/kolbot/sdk/types/Attack.d.ts | 126 +- d2bs/kolbot/sdk/types/AutoMule.d.ts | 190 +- d2bs/kolbot/sdk/types/Config.d.ts | 1104 +-- d2bs/kolbot/sdk/types/Cubing.d.ts | 40 +- d2bs/kolbot/sdk/types/Item.d.ts | 36 +- d2bs/kolbot/sdk/types/Loader.d.ts | 24 +- d2bs/kolbot/sdk/types/Misc.d.ts | 67 +- d2bs/kolbot/sdk/types/NTIP.d.ts | 8 +- d2bs/kolbot/sdk/types/OOG.d.ts | 134 +- d2bs/kolbot/sdk/types/Pather.d.ts | 68 +- d2bs/kolbot/sdk/types/Pickit.d.ts | 64 +- d2bs/kolbot/sdk/types/Runewords.d.ts | 224 +- d2bs/kolbot/sdk/types/Skill.d.ts | 1 + d2bs/kolbot/sdk/types/Storage.d.ts | 172 +- d2bs/kolbot/sdk/types/Town.d.ts | 222 +- d2bs/kolbot/sdk/types/Util.d.ts | 242 +- d2bs/kolbot/sdk/types/sdk.d.ts | 9645 +++++++++++++------------- 18 files changed, 7345 insertions(+), 7304 deletions(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index bfc92137e..7bf369843 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -18,1130 +18,1162 @@ /// declare global { - interface Error { - fileName: string; - lineNumber: number; - } - - interface ArrayConstructor { - /** - * Creates a new Array instance with a variable number of elements passed as arguments. - * - * @param {...T[]} items The elements to include in the array. - * ```ts - * const arr = Array.of(1, 2, 3, 4, 5); - * ``` - * @returns {Array} A new array with the provided elements. - */ - of(...items: T[]): T[]; - } - - interface Array { - includes(searchElement: T): boolean; - find(predicate: (value: T, index: number, obj: Int8Array) => boolean, thisArg?: any): T | undefined; - first(): T | undefined; - last(): T | undefined; - findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number; - intersection(other: T[]): T[]; - difference(other: T[]): T[]; - symmetricDifference(other: T[]): T[]; - flat(depth?: number): T[]; - compactMap(callback: (value: T, index: number, obj: T[]) => any, thisArg?: any): any[]; - filterHighDistance(step: number): any[] - isEqual(t: T[]): boolean - remove(val: T): T[] - random(): T; - /** - * Creates a new array by sorting the elements of the original array. - * - * @param {(function(a: any, b: any): number) | undefined} compareFn Function used to determine the order of the elements. - * It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive - * value otherwise. If omitted, the elements are sorted in ascending, ASCII character order. - * ```ts - * [11,2,22,1].toSorted((a, b) => a - b) - * ``` - * @returns {Array} A new array with the sorted elements, leaving the orignal intact. - */ - toSorted(compareFn?: ((a: T, b: T) => number) | undefined): T[]; - /** - * Creates a new array with the elements of the original array in reversed order. - * Without mutating the original array. - * - * @returns {Array} A new array with the reversed elements. - */ - toReversed(): T[]; - /** - * Creates a new array by removing and/or adding elements from/to the original array. - * - * @param {number} start The index at which to start changing the array. - * @param {number} deleteCount The number of elements to remove starting from the `start` index. - * @param {...T[]} items The elements to add to the array. - * @returns {Array} A new array with the removed elements and optionally added elements. - */ - toSpliced(start: number, deleteCount?: number, ...items: T[]): T[]; - } - - interface String { - lcsGraph(compareToThis: string): { a: string, b: string, graph: Uint16Array[]} - diffCount(a:string): number; - startsWith(a: string): boolean; - capitalize(downCase: boolean): string; - format(...pairs: Array): string; - padStart(targetLength: number, padString: string): string; - padEnd(targetLength: number, padString: string): string; - } - - interface StringConstructor { - static isEqual(str1: string, str2: string): boolean; - } - - interface ObjectConstructor { - assign(target: T, source: U): T & U; - assign(target: T, source1: U, source2: V): T & U & V; - assign(target: T, source1: U, source2: V, source3: W): T & U & V & W; - assign(target: object, ...sources: any[]): any; - values(source: object): any[]; - entries(source: object): any[][]; - is(o1: any, o2: any): boolean; - } - - interface Object { - distance: number, - path: PathNode[] | undefined; - - setPrototypeOf(obj: object, proto: object) - } - - class ScriptError extends Error { - } - - type actType = { initialized: boolean, spot: { [data: string]: [number, number] } }; - type potType = 'hp' | 'mp' | 'rv'; - - class Hook { - color: number; - visible: boolean; - - /** - * The horizontal alignment - * - 0 - Left - * - 1 - Right - * - 2 - Center - */ - align: number; - - /** - * The z-order of the Hook (what it covers up and is covered by). - */ - zorder: number; - - /** - * How much of the controls underneath the Hook should show through. - */ - opacity: number; - - /** - * Whether the Hook is in automap coordinate space (true) or screen coordinate space (false). - */ - automap: boolean; - - remove(): void; - } - - class Line extends Hook { - constructor(x: number, y: number, x2: number, y2: number, color: number, visible: boolean, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); - /** - * The first x coordinate of the Line. - */ - x: number; - - /** - * The first y coordinate of the Line. - */ - y: number; - - /** - * The end x coordinate of the Line. - */ - x2: number; - - /** - * The end y coordinate of the Line. - */ - y2: number; - } - - class Text extends Hook { - constructor( - text: string, - x: number, - y: number, - color: number, - font: number, - align: number, - automap: boolean, - ClickHandler?: Function, - HoverHandler?: Function - ); - text: string; - /** - * The x coordinate (left) of the Text. - */ - x: number; - - /** - * The y coordinate (top) of the Text. - */ - y: number; - } - - class Box extends Hook { - constructor(x: number, y: number, xsize: number, ysize: number, color: number, opacity: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); - /** - * The x coordinate (left) of the Box. - */ - x: number; - - /** - * The y coordinate (top) of the Box. - */ - y: number; - - /** - * The xsize (width) of the Box. - */ - xsize: number; - - /** - * The ysize (height) of the Box. - */ - ysize: number; - } - - class Frame extends Box { - } - - interface ClassAttack { - doAttack(unit: Monster, preattack?: boolean): number - afterAttack(any?: any): void - doCast(unit: Monster, timedSkill: number, untimedSkill: number): number - - // Self defined - decideSkill(unit: Monster, skipSkill?: number[]): [number, number] - } - - /** - * @todo Figure out what each of these actually returns to properly document them - */ - class File { - readable: boolean; - writable: boolean; - seekable: boolean; - mode: number; - binaryMode: boolean; - length: number; - path: string; - position: number; - eof: boolean; - accessed: number; - created: number; - modified: number; - autoflush: boolean; - - static open(path: string, mode: number): File; - close(): File; - reopen(): File; - read(count: number): string[]; - read(count: number): ArrayBuffer[]; - readLine(): string; - readAllLines(): string[]; - readAll(): string; - write(): void; - seek(n: number): any; - seek(n: number, isLines: boolean, fromStart: boolean): any; - flush(): void; - reset(): void; - end(): void; - } - - const FileTools: { - readText(filename: string) - writeText(filename: string, data: string) - appendText(filename: string, data: string) - exists(filename: string): Boolean; - } - - function getCollision(area: number, x: number, y: number, x2: number, y2: number) - - function getDistance(unit: PathNode, other: PathNode): number; - function getDistance(unit: PathNode, x: number, y: number): number; - - /************************************* - * Unit description * - * Needs expansion * - *************************************/ - - type UnitType = 0 | 1 | 2 | 3 | 4 | 5; - interface Unit { - readonly type: UnitType; - readonly classid: number; - readonly mode: number; - readonly name: string; - readonly act: any; - readonly gid: number; - readonly x: number; - readonly y: number; - readonly area: number; - readonly hp: number; - readonly hpmax: number; - readonly mp: number; - readonly mpmax: number; - readonly stamina: number; - readonly staminamax: number; - readonly charlvl: number; - readonly owner: number; - readonly ownertype: number; - readonly uniqueid: number; - - } - - class Unit { - readonly attackable: boolean; - readonly dead: boolean; - readonly islocked: boolean; - readonly distance: number; - - readonly targetx: number; - readonly targety: number; - readonly idle: boolean; - readonly isPlayer: boolean; - readonly isNPC: boolean; - readonly isMonster: boolean; - readonly attackable: boolean; - readonly rawStrength: number; - readonly rawDexterity: number; - readonly fireRes: number; - readonly coldRes: number; - readonly lightRes: number; - readonly poisonRes: number; - readonly hpPercent: number; - readonly prettyPrint: string; - - // D2BS built in - getNext(): Unit | false; - interact(): boolean; - interact(area: number): boolean; - getItem(classId?: number, mode?: number, unitId?: number): ItemUnit | false; - getItem(name?: string, mode?: number, unitId?: number): ItemUnit | false; - getItems(...args: any[]): ItemUnit[] | false; - getMerc(): MercUnit; - getMercHP(): number | false; - /** - * @param type - - * - `me.getSkill(0)` : Name of skill on right hand - * - `me.getSkill(1)` : Name of skill on left hand - * - `me.getSkill(2)` : ID of skill on right hand - * - `me.getSkill(3)` : ID of skill on left hand - * - `me.getSkill(4)` : Array of all skills in format [skillId, hardPoints, softPoints, ...repeat] - */ - getSkill(type: 0 | 1 | 2 | 3 | 4): number | number[]; - getSkill(skillId: number, type: 0 | 1, item?: ItemUnit): number; - getStat(index: number, subid?: number, extra?: number): number; - getState(index: number, subid?: number): boolean; - getQuest(quest: number, subid: number): number - getParent(): Unit | string; - getMinionCount(): number; - - // additions from kolbot - getStatEx(one: number, sub?: number): number; - getItemsEx(classId?: number, mode?: number, unitId?: number): ItemUnit[]; - getItemsEx(name?: string, mode?: number, unitId?: number): ItemUnit[]; - inArea(area: number): boolean; - checkForMobs(givenSettings: { - range?: number; - count?: number; - coll?: number; - spectype: number - }): boolean - } - - type PlayerType = 0; - class Player extends Unit { - public type: PlayerType; - } - - type MonsterType = 1; - interface Monster extends Unit { - } - - class Monster extends Unit { - public type: MonsterType; - readonly isChampion: boolean; - readonly isUnique: boolean; - readonly isMinion: boolean; - readonly isSuperUnique: boolean; - readonly isSpecial: boolean; - readonly isWalking: boolean; - readonly isRunning: boolean; - readonly isMoving: boolean; - readonly isChilled: boolean; - readonly isFrozen: boolean; - readonly currentVelocity: number; - readonly isPrimeEvil: boolean; - readonly isBoss: boolean; - readonly isGhost: boolean; - readonly isDoll: boolean; - readonly isMonsterObject: boolean; - readonly isMonsterEgg: boolean; - readonly isMonsterNest: boolean; - readonly isBaalTentacle: boolean; - readonly isShaman: boolean; - readonly isUnraveler: boolean; - readonly isFallen: boolean; - readonly isBeetle: boolean; - readonly extraStrong: boolean; - readonly extraFast: boolean; - readonly cursed: boolean; - readonly magicResistant: boolean; - readonly fireEnchanted: boolean; - readonly lightningEnchanted: boolean; - readonly coldEnchanted: boolean; - readonly manaBurn: boolean; - readonly teleportation: boolean; - readonly spectralHit: boolean; - readonly stoneSkin: boolean; - readonly multiShot: boolean; - readonly charlvl: number; - readonly spectype: number; - readonly curseable: boolean; - readonly scareable: boolean; - - getEnchant(type: number): boolean; - hasEnchant(...enchants: number): boolean - } - - class NPCUnit extends Unit { - public type: MonsterType; - readonly itemcount: number; - - openMenu(): boolean; - useMenu(): boolean; - startTrade: (mode: any) => (any | boolean); - } - - class MercUnit extends Monster { - equip(destination: number | undefined, item: ItemUnit) - } - - interface ObjectUnit extends Unit { - } - - type ObjectType = 2; - class ObjectUnit extends Unit { - public type: ObjectType; - objtype: number; - } - - type MissileType = 3; - class Missile extends Unit { - public readonly type: MissileType; - hits(position: PathNode): boolean; - } - - type ItemType = 4; - interface ItemUnit extends Unit { - - } - - class ItemUnit extends Unit { - // todo define item modes - public readonly type: ItemType; - readonly code: string; - readonly prefixes: string[]; - readonly suffixes: string[]; - readonly prefixnum: number; - readonly suffixnum: number; - readonly prefixenums: number[]; - readonly suffixnums: number[]; - readonly fname: string; - readonly quality: number; - readonly node: number; - readonly location: number; - readonly sizex: number; - readonly sizey: number; - readonly itemType: number; - readonly bodylocation: number; - readonly ilvl: number; - readonly lvlreq: number; - readonly gfx: number; - readonly description: string; - - // additional, not from d2bs - readonly identified: boolean; - readonly isEquipped: boolean - readonly dexreq: number - readonly strreq: number - readonly isInInventory: boolean; - readonly isInStash: boolean; - readonly isInCube: boolean; - readonly isInStorage: boolean; - readonly isInBelt: boolean; - readonly isOnMain: boolean; - readonly isOnSwap: boolean; - readonly runeword: boolean; - readonly questItem: boolean; - readonly ethereal: boolean; - readonly twoHanded: boolean - readonly oneOrTwoHanded: boolean; - readonly strictlyTwoHanded: boolean; - readonly sellable: boolean; - readonly lowQuality: boolean; - readonly normal: boolean; - readonly superior: boolean; - readonly magic: boolean; - readonly set: boolean; - readonly rare: boolean; - readonly unique: boolean; - readonly crafted: boolean; - readonly sockets: number; - readonly onGroundOrDropping: boolean; - readonly isShield: boolean; - readonly isAnni: boolean; - readonly isTorch: boolean; - readonly isGheeds: boolean; - - getColor(): number; - getBodyLoc(): number[]; - getFlags(): number; - getFlag(flag: number): boolean; - // shop(mode: ShopModes): boolean; - getItemCost(type?: 0 | 1 | 2): number; - sell(): boolean; - drop(): boolean; - equip(slot?: number): boolean; - buy(shift?: boolean, gamble?: boolean): boolean; - sellOrDrop():void - toCursor():boolean - use(): boolean; - } - - type TileType = 5; - class Tile extends Unit { - public type: TileType; - } - - interface MeType extends Unit { - public type: PlayerType; - readonly account: string; - readonly charname: string; - readonly diff: 0 | 1 | 2; - readonly maxdiff: 0 | 1 | 2; - readonly gamestarttime: number; - readonly gametype: 0 | 1; - readonly itemoncursor: boolean; - readonly ladder: number; - readonly ping: number; - readonly fps: number; - readonly locale: number; - readonly playertype: 0|1; - readonly realm: string; - readonly realmshort: string; - readonly mercrevivecost: number; - chickenhp: number; - chickenmp: number; - quitonhostile: boolean; - readonly gameReady: boolean; - readonly profile: string; - readonly pid: number; - readonly charflags: number; - readonly screensize: number; - readonly windowtitle: string; - readonly ingame: boolean; - quitonerror: boolean; - maxgametime: number; - readonly gamepassword: string; - readonly gamestarttime: number; - readonly gamename: string; - readonly gameserverip: string; - readonly itemcount: number; - readonly classid: 0 | 1 | 2 | 3 | 4 | 5 | 6; - readonly weaponswitch: 0|1; - readonly gameReady: boolean; - blockMouse: boolean; - blockKeys: boolean; - runwalk: number; - automap: boolean; - - readonly expansion: boolean; - readonly classic: boolean; - readonly softcore: boolean; - readonly hardcore: boolean; - readonly normal: boolean; - readonly nightmare: boolean; - readonly hell: boolean; - readonly sorceress: boolean; - readonly amazon: boolean; - readonly necromancer: boolean; - readonly paladin: boolean; - readonly barbarian: boolean; - readonly assassin: boolean; - readonly druid: boolean; - readonly hpPercent: number; - readonly mpPercent: number; - readonly gold: number; - readonly inTown: boolean; - readonly highestAct: 1 | 2 | 3 | 4 | 5; - readonly staminaPercent: number; - readonly staminaDrainPerSec: number; - readonly staminaTimeLeft: number; - readonly staminaMaxDuration: number; - readonly inShop: boolean; - readonly skillDelay: boolean; - readonly highestAct: 1 | 2 | 3 | 4 | 5; - readonly highestQuestDone: number; - readonly den: boolean; - readonly bloodraven: boolean; - readonly smith: boolean; - readonly imbue: boolean; - readonly cain: boolean; - readonly tristram: boolean; - readonly countess: boolean; - readonly andariel: boolean; - readonly radament: boolean; - readonly horadricstaff: boolean; - readonly summoner: boolean; - readonly duriel: boolean; - readonly goldenbird: boolean; - readonly lamessen: boolean; - readonly gidbinn: boolean; - readonly travincal: boolean; - readonly mephisto: boolean; - readonly izual: boolean; - readonly hellforge: boolean; - readonly diablo: boolean; - readonly shenk: boolean; - readonly larzuk: boolean; - readonly savebarby: boolean; - readonly barbrescue: boolean; - readonly anya: boolean; - readonly ancients: boolean; - readonly baal: boolean; - readonly cows: boolean; - readonly respec: boolean; - readonly diffCompleted: boolean; - wirtsleg: ItemUnit; - cube: ItemUnit; - shaft: ItemUnit; - amulet: ItemUnit; - staff: ItemUnit; - completestaff: ItemUnit; - eye: ItemUnit; - brain: ItemUnit; - heart: ItemUnit; - khalimswill: ItemUnit; - khalimsflail: ItemUnit; - malahspotion: ItemUnit; - scrollofresistance: ItemUnit; - readonly walking: boolean; - readonly running: boolean; - readonly deadOrInSequence: boolean; - readonly moving: boolean; - readonly FCR: number; - readonly FHR: number; - readonly FBR: number; - readonly IAS: number; - readonly shapeshifted: boolean; - - haveWaypoint(area: number): boolean; - overhead(msg: string): void; - repair(): boolean; - revive(): void; - move(x: number, y: number): boolean; - setSkill(): boolean; - cancel(number?: number): boolean; - inArea(area: number): boolean; - switchToPrimary(): boolean; - checkItem(itemInfo: { - classid?: number; - itemtype?: number; - quality?: number; - runeword?: boolean; - ethereal?: boolean; - name?: string | number; - equipped?: boolean | number; - }): {have: boolean; item: ItemUnit | null}; - haveSome(arg0: { name: number; equipped: boolean; }[]): any; - equip(destination: number | undefined, item: ItemUnit); - getRepairCost(): number; - findItems(param: number, number?: number, number2?: number): ItemUnit[]; - usingShield(): boolean; - walk(): void; - run(): void; - getPingDelay(): number; - findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; - findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; - cancelUIFlags(): boolean; - switchWeapons(slot: 0 | 1): boolean; - castingFrames(skillId: number, fcr?: number, charClass?: number): number; - castingDuration(skillId: number, fcr?: number, charClass?: number): number; - getWeaponQuantity(weaponLoc: number): number; - needPotions(): boolean; - getTpTool(): ItemUnit | null; - getIdTool(): ItemUnit | null; - canTpToTown(): boolean; - needHealing(): boolean; - getTome(id: number): ItemUnit | null; - getUnids(): ItemUnit[]; - fieldID(): boolean; - switchToPrimary(): boolean; - haveWaypoint(area: number): boolean; - } - - const me: MeType - - type PathNode = { x: number, y: number } - - function getUnit(type: 4, name?: string, mode?: number, unitId?: number): ItemUnit - function getUnit(type: 4, classId?: number, mode?: number, unitId?: number): ItemUnit - function getUnit(type: 1, name?: string, mode?: number, unitId?: number): Monster - function getUnit(type: 1, classId?: number, mode?: number, unitId?: number): Monster - function getUnit(type?: number, name?: string, mode?: number, unitId?: number): Unit - function getUnit(type?: number, classId?: number, mode?: number, unitId?: number): Unit - - function getPath(area: number, fromX: number, fromY: number, toX: number, toY: number, reductionType: 0 | 1 | 2, radius: number): PathNode[] | false - function getCollision(area: number, x: number, y: number) - function getMercHP(): number - function getCursorType(type: 1 | 3 | 6): boolean - function getCursorType(): number - function getSkillByName(name: string): number - function getSkillById(id: number): string - function getLocaleString(id: number): string - - // Never seen in the wild, not sure about arguments - function getTextSize(name: string, size: number) - function getThreadPriority(): number - function getUIFlag(flag: number): boolean - function getTradeInfo(mode: 0 | 1 | 2): boolean - function getWaypoint(id: number): boolean - - class Script { - running: boolean; - name: string; - type: boolean; - threadid: number; - memory: number; - - getNext(): Script; - pause(): boolean; - resume(): boolean; - join(): void; - stop(): boolean; - send(): void; - } - - function getScript(name?: string | boolean): Script | false - function getScripts(): Script | false - - class Room { - area: number; - correcttomb: number; - x: number; - y: number; - xsize: number; - ysize: number; - - getNext(): Room | false; - getNearby(): Room[]; - isInRoom(unit: PathNode):boolean - isInRoom(x:number, y:number):boolean - } - - function getRoom(area: number, x: number, y: number): Room | false - function getRoom(x: number, y: number): Room | false - function getRoom(area: number): Room | false - function getRoom(): Room | false - - class Party { - x: number; - y: number; - area: number; - gid: number; - life: number; - partyflag: number; - partyid: number; - name: string; - classid: number; - level: number; - inTown: any; - - getNext(): Party | false; - } - - function getParty(unit?: Unit): Party | false - - class PresetUnit { - id: number; - x: number; - y: number; - roomx: number; - roomy: number; - level: number; - - getNext(): PresetUnit | false; - realCoords(): { area: number, x: number, y: number }; - } - - type PresetObject = { - area: number, - id: number, - type: number, - x: number, - y: number, - } - - function getPresetUnit(area?: number, objType?: number, classid?: number): PresetUnit | false - function getPresetUnit(area?: number, objType?: 2, classid?: number): PresetUnit | false - function getPresetUnits(area?: number, objType?: number, classid?: number): PresetUnit[] | false - - interface Exit extends Object { - x: number, - y: number, - type: number, - target: number, - tileid: number, - level: number, - } - - class Area { - name: string; - x: number; - xsize: number; - y: number; - ysize: number; - id: number; - exits: Exit[]; - - getNext(): Area | false; - } - - function getArea(id?: number): Area | false - function getBaseStat(table: string, row: number, column: string | number): number | string - function getBaseStat(row: number, column: string): number | string - - /** - * @todo get a better understanding of Control - */ - class Control { - /** - * The text of the control - */ - text: string; - - /** - * The x coordinate of the control - */ - x: number; - - /** - * The y coordinate of the control - */ - y: number; - - /** - * The xsize (width) of the control - */ - xsize: number; - - /** - * The ysize (height) of the control - */ - ysize: number; - - /** - * The state of the control - * - Disabled - 4 - * @todo figure out the rest - */ - state: number; - - /** - * Return whether or not the Control holds a password (starred out text). - */ - password: boolean; - - /** - * The type of control - * - 1 - TextBox - * - 2 - Image - * - 3 - Image2 - * - 4 - LabelBox - * - 5 - ScrollBar - * - 6 - Button - * - 7 - List - * - 8 - Timer - * - 9 - Smack - * - 10 - ProgressBar - * - 11 - Popup - * - 12 - AccountList - */ - type: number; - cursorpos: any; - selectstart: any; - selectend: any; - disabled: number; - - getNext(): Control | undefined; - click(x?: number, y?: number): void; - setText(text: string): void; - getText(): string[]; - } - - type Profile = { - type: number, - ip: number, - username: string, - gateway: string, - character: string, - difficulty: number, - maxloginTime: number, - maxCharacterSelectTime: number, - } - function Profile(): Profile; - - function getControl(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control | false - function getControls(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control[] - function getPlayerFlag(meGid: number, otherGid: number, type: number): boolean - function getTickCount(): number - function getInteractedNPC(): NPCUnit | false - function getIsTalkingNPC(): boolean - function getDialogLines(): { handler() }[] | false - function print(what: string): void - function stringToEUC(arg: any): [] - function utf8ToEuc(arg: any): [] - function delay(ms: number): void - function load(file: string): boolean - function isIncluded(file: string): boolean - function include(file: string): boolean - function stacktrace(): true - function rand(from: number, to: number): number - function copy(what: string): void - function paste(): string - - function sendCopyData(noIdea: null, handle: number | string, mode: number, data: string): void; - - function sendDDE() - function keystate() - - type eventName = 'gamepacket' | 'scriptmsg' | 'copydata' | 'keyup' | 'keydown'; - - function addEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void - function addEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void - function addEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void - function addEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void - function addEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number|string) => void)): void - function addEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void - function addEventListener(eventType: eventName, callback: ((...args: any) => void)): void - - function removeEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void - function removeEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void - function removeEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void - function removeEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void - function removeEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number) => void)): void - function removeEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void - function removeEventListener(eventType: eventName, callback: ((...args: any) => void)): void - - function clearEvent() - function clearAllEvents() - function js_strict() - function version(): number - function scriptBroadcast(what: string | object): void - function sqlite_version() - function sqlite_memusage() - - type directory = { - getFiles(): string[]; - getFolders(): string[]; - create(what?: string): boolean; - }; - function dopen(what?: string): directory | false; - function debugLog(text: string): void - function showConsole(): void - function hideConsole(): void - - // out of game functions - function login(name?: string): void - function selectCharacter() - function createGame() - function joinGame() - function addProfile() - function getLocation():number - function loadMpq() - - // game functions that don't have anything to do with gathering data - function submitItem(): void - function getMouseCoords() - function copyUnit(unit: S): S - function clickMap(type: 0 | 1 | 2 | 3, shift: 0 | 1, x: number, y: number) - function acceptTrade() - function tradeOk() - function beep(id?: number) - - function clickItem(where: 0 | 1 | 2, bodyLocation: number) - function clickItem(where: 0 | 1 | 2, item: ItemUnit) - function clickItem(where: 0 | 1 | 2, x: number, y: number) - function clickItem(where: 0 | 1 | 2, x: number, y: number, location: number) - - function getDistance(a: Unit, b: Unit): number - function getDistance(a: Unit, toX: number, toY: number): number - function getDistance(fromX: number, fromY: number, b: Unit): number - function getDistance(fromX: number, fromY: number, toX: number, toY: number): number - - function gold(amount: number, changeType?: 0 | 1 | 2 | 3 | 4): void - function checkCollision(a: Unit, b: Unit, type: number): boolean - function playSound(num: number): void - function quit(): never - function quitGame(): never - function say(what: string): void - function clickParty(player: Party, type: 0 | 1 | 2 | 3 | 4) - function weaponSwitch(): void - function transmute(): void - function useStatPoint(type: number): void - function useSkillPoint(type: number): void - function takeScreenshot(): void - function moveNPC(npc: Monster, x: number, y: number): void - - function getPacket(buffer: ArrayBuffer): void - function getPacket(...args: { size: number, data: number }[]): void - - function sendPacket(buffer: ArrayBuffer): void - function sendPacket(...number: number[]): void - - function getIP(): string - function sendKey(key: number): void - function revealLevel(unknown: true): void - - // hash functions - function md5(str: string): string - function sha1(str: string): string - function sha256(str: string): string - function sha384(str: string): string - function sha512(str: string): string - function md5_file(str: string): string - function sha1_file(str: string): string - function sha256_file(str: string): string - function sha384_file(str: string): string - function sha512_file(str: string): string - - interface Console { - static log(...whatever: any[]): void - static debug(...whatever: any[]): void - static warn(...whatever: any[]): void - static error(...whatever: any[]): void - static time(name: string): void; - static timeEnd(name: string): void; - static trace(): void; - static info(start: boolean, msg: string, timer: string): void; - } - const console: Console; - - function includeIfNotIncluded(file?: string): boolean; - function includeCoreLibs(obj: { exclude: string[] }): boolean; - function includeSystemLibs(): boolean; - function clone(obj: Date | any[] | object): ThisParameterType; - function copyObj(from: object): object; - - interface StarterConfig { - MinGameTime: number, - MaxGameTime?: number, - PingQuitDelay: number, - CreateGameDelay: number, - ResetCount: number - CharacterDifference: number, - MaxPlayerCount: number, - StopOnDeadHardcore: boolean, - - JoinChannel: string, - FirstJoinMessage: string, - ChatActionsDelay: number, - AnnounceGames: boolean, - AnnounceMessage: string, - AfterGameMessage: string, - - InvalidPasswordDelay: number, // Minutes to wait after getting Invalid Password message - VersionErrorDelay: number, // Seconds to wait after 'unable to identify version' message - SwitchKeyDelay: number, // Seconds to wait before switching a used/banned key or after realm down - CrashDelay: number, // Seconds to wait after a d2 window crash - FTJDelay: number, // Seconds to wait after failing to create a game - RealmDownDelay: number, // Minutes to wait after getting Realm Down message - UnableToConnectDelay: number, // Minutes to wait after Unable To Connect message - TCPIPNoHostDelay: number, // Seconds to wait after Cannot Connect To Server message - CDKeyInUseDelay: number, // Minutes to wait before connecting again if CD-Key is in use. - ConnectingTimeout: number, // Seconds to wait before cancelling the 'Connecting...' screen - PleaseWaitTimeout: number, // Seconds to wait before cancelling the 'Please Wait...' screen - WaitInLineTimeout: number, // Seconds to wait before cancelling the 'Waiting in Line...' screen - WaitOutQueueRestriction: boolean, // Wait out queue if we are restricted, queue time > 10000 - WaitOutQueueExitToMenu: boolean, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby - GameDoesNotExistTimeout: number, // Seconds to wait before cancelling the 'Game does not exist.' screen - } - - interface StarterInterface { - Config: StarterConfig, - useChat: boolean, - pingQuit: boolean, - inGame: boolean, - firstLogin: boolean, - firstRun: boolean, - isUp: "yes"|"no", - loginRetry: number, - deadCheck: boolean, - chatActionsDone: boolean, - gameStart: boolean, - gameCount: number, - lastGameStatus: string, - handle: number | null, - connectFail: boolean, - connectFailRetry: number, - makeAccount: false, - channelNotify: boolean, - chanInfo: { - joinChannel: string, - firstMsg: string, - afterMsg: string, - announce: boolean, - }, - gameInfo: { - error: string, - crashInfo: { - currScript: number, - area: number, - }, - switchKeys: boolean, - }, - joinInfo: {}, - profileInfo: {}, - - sayMsg(string: string): void, - timer(tick: number): string, - locationTimeout(time: number, location: number): boolean, - setNextGame(gameInfo: { gameName: string }): void, - updateCount(): void, - scriptMsgEvent(msg: string): void, - receiveCopyData(mode: number, msg: string | object): void, - randomString(len?: number, useNumbers?: boolean): string, - randomNumberString(len?: number): string, - } - - const Starter: StarterInterface; - - namespace Time { - function seconds(seconds: number): number; - function minutes(minutes: number): number; - function format(ms: number): string; - function toSeconds(ms: number): number; - function toMinutes(ms: number): number; - function toHours(ms: number): number; - function toDays(ms: number): number; - function elapsed(start: number): number; - } + interface Error { + fileName: string; + lineNumber: number; + } + + interface ArrayConstructor { + /** + * Creates a new Array instance with a variable number of elements passed as arguments. + * + * @param {...T[]} items The elements to include in the array. + * ```ts + * const arr = Array.of(1, 2, 3, 4, 5); + * ``` + * @returns {Array} A new array with the provided elements. + */ + of(...items: T[]): T[]; + } + + interface Array { + includes(searchElement: T): boolean; + find(predicate: (value: T, index: number, obj: Int8Array) => boolean, thisArg?: any): T | undefined; + first(): T | undefined; + last(): T | undefined; + findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number; + intersection(other: T[]): T[]; + difference(other: T[]): T[]; + symmetricDifference(other: T[]): T[]; + flat(depth?: number): T[]; + compactMap(callback: (value: T, index: number, obj: T[]) => any, thisArg?: any): any[]; + filterHighDistance(step: number): any[] + isEqual(t: T[]): boolean + remove(val: T): T[] + random(): T; + /** + * Creates a new array by sorting the elements of the original array. + * + * @param {(function(a: any, b: any): number) | undefined} compareFn Function used to determine the order of the elements. + * It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive + * value otherwise. If omitted, the elements are sorted in ascending, ASCII character order. + * ```ts + * [11,2,22,1].toSorted((a, b) => a - b) + * ``` + * @returns {Array} A new array with the sorted elements, leaving the orignal intact. + */ + toSorted(compareFn?: ((a: T, b: T) => number) | undefined): T[]; + /** + * Creates a new array with the elements of the original array in reversed order. + * Without mutating the original array. + * + * @returns {Array} A new array with the reversed elements. + */ + toReversed(): T[]; + /** + * Creates a new array by removing and/or adding elements from/to the original array. + * + * @param {number} start The index at which to start changing the array. + * @param {number} deleteCount The number of elements to remove starting from the `start` index. + * @param {...T[]} items The elements to add to the array. + * @returns {Array} A new array with the removed elements and optionally added elements. + */ + toSpliced(start: number, deleteCount?: number, ...items: T[]): T[]; + } + + interface String { + lcsGraph(compareToThis: string): { a: string, b: string, graph: Uint16Array[]} + diffCount(a:string): number; + startsWith(a: string): boolean; + capitalize(downCase: boolean): string; + format(...pairs: Array): string; + padStart(targetLength: number, padString: string): string; + padEnd(targetLength: number, padString: string): string; + } + + interface StringConstructor { + static isEqual(str1: string, str2: string): boolean; + } + + interface ObjectConstructor { + assign(target: T, source: U): T & U; + assign(target: T, source1: U, source2: V): T & U & V; + assign(target: T, source1: U, source2: V, source3: W): T & U & V & W; + assign(target: object, ...sources: any[]): any; + values(source: object): any[]; + entries(source: object): any[][]; + is(o1: any, o2: any): boolean; + } + + interface Object { + readonly distance: number; + path: PathNode[] | undefined; + + setPrototypeOf(obj: object, proto: object); + } + + interface Set { + union(other: Set): Set; + intersection(other: Set): Set; + difference(other: Set): Set; + symmetricDifference(other: Set): Set; + } + + class ScriptError extends Error { + } + + type actType = { initialized: boolean, spot: { [data: string]: [number, number] } }; + type potType = 'hp' | 'mp' | 'rv'; + + class Hook { + color: number; + visible: boolean; + + /** + * The horizontal alignment + * - 0 - Left + * - 1 - Right + * - 2 - Center + */ + align: number; + + /** + * The z-order of the Hook (what it covers up and is covered by). + */ + zorder: number; + + /** + * How much of the controls underneath the Hook should show through. + */ + opacity: number; + + /** + * Whether the Hook is in automap coordinate space (true) or screen coordinate space (false). + */ + automap: boolean; + + remove(): void; + } + + class Line extends Hook { + constructor(x: number, y: number, x2: number, y2: number, color: number, visible: boolean, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); + /** + * The first x coordinate of the Line. + */ + x: number; + + /** + * The first y coordinate of the Line. + */ + y: number; + + /** + * The end x coordinate of the Line. + */ + x2: number; + + /** + * The end y coordinate of the Line. + */ + y2: number; + } + + class Text extends Hook { + constructor( + text: string, + x: number, + y: number, + color: number, + font: number, + align: number, + automap: boolean, + ClickHandler?: Function, + HoverHandler?: Function + ); + text: string; + /** + * The x coordinate (left) of the Text. + */ + x: number; + + /** + * The y coordinate (top) of the Text. + */ + y: number; + } + + class Box extends Hook { + constructor(x: number, y: number, xsize: number, ysize: number, color: number, opacity: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); + /** + * The x coordinate (left) of the Box. + */ + x: number; + + /** + * The y coordinate (top) of the Box. + */ + y: number; + + /** + * The xsize (width) of the Box. + */ + xsize: number; + + /** + * The ysize (height) of the Box. + */ + ysize: number; + } + + class Frame extends Box { + } + + interface ClassAttack { + doAttack(unit: Monster, preattack?: boolean): number + afterAttack(any?: any): void + doCast(unit: Monster, timedSkill: number, untimedSkill: number): number + + // Self defined + decideSkill(unit: Monster, skipSkill?: number[]): [number, number] + } + + /** + * @todo Figure out what each of these actually returns to properly document them + */ + class FileClass { + readable: boolean; + writable: boolean; + seekable: boolean; + mode: number; + binaryMode: boolean; + length: number; + path: string; + position: number; + eof: boolean; + accessed: number; + created: number; + modified: number; + autoflush: boolean; + + static open(path: string, mode: number): File; + close(): File; + reopen(): File; + read(count: number): string[]; + read(count: number): ArrayBuffer[]; + readLine(): string; + readAllLines(): string[]; + readAll(): string; + write(): void; + seek(n: number): any; + seek(n: number, isLines: boolean, fromStart: boolean): any; + flush(): void; + reset(): void; + end(): void; + } + const FILE_READ: 0; + const FILE_WRITE: 1; + const FILE_APPEND: 2; + + const FileTools: { + readText(filename: string) + writeText(filename: string, data: string) + appendText(filename: string, data: string) + exists(filename: string): Boolean; + } + + function getCollision(area: number, x: number, y: number, x2: number, y2: number) + + function getDistance(unit: PathNode, other: PathNode): number; + function getDistance(unit: PathNode, x: number, y: number): number; + + /************************************* + * Unit description * + * Needs expansion * + *************************************/ + + type UnitType = 0 | 1 | 2 | 3 | 4 | 5; + interface Unit { + readonly type: UnitType; + readonly classid: number; + readonly mode: number; + readonly name: string; + readonly act: any; + readonly gid: number; + readonly x: number; + readonly y: number; + readonly area: number; + readonly hp: number; + readonly hpmax: number; + readonly mp: number; + readonly mpmax: number; + readonly stamina: number; + readonly staminamax: number; + readonly charlvl: number; + readonly owner: number; + readonly ownertype: number; + readonly uniqueid: number; + } + + class Unit { + readonly attackable: boolean; + readonly dead: boolean; + readonly islocked: boolean; + readonly distance: number; + + readonly targetx: number; + readonly targety: number; + readonly idle: boolean; + readonly isPlayer: boolean; + readonly isNPC: boolean; + readonly isMonster: boolean; + readonly attackable: boolean; + readonly rawStrength: number; + readonly rawDexterity: number; + readonly fireRes: number; + readonly coldRes: number; + readonly lightRes: number; + readonly poisonRes: number; + readonly hpPercent: number; + readonly prettyPrint: string; + + // D2BS built in + getNext(): Unit | false; + interact(): boolean; + interact(area: number): boolean; + getItem(classId?: number, mode?: number, unitId?: number): ItemUnit | false; + getItem(name?: string, mode?: number, unitId?: number): ItemUnit | false; + getItems(...args: any[]): ItemUnit[] | false; + getMerc(): MercUnit; + getMercHP(): number | false; + /** + * @param type - + * - `me.getSkill(0)` : Name of skill on right hand + * - `me.getSkill(1)` : Name of skill on left hand + * - `me.getSkill(2)` : ID of skill on right hand + * - `me.getSkill(3)` : ID of skill on left hand + * - `me.getSkill(4)` : Array of all skills in format [skillId, hardPoints, softPoints, ...repeat] + */ + getSkill(type: 0 | 1 | 2 | 3 | 4): number | number[]; + getSkill(skillId: number, type: 0 | 1, item?: ItemUnit): number; + getStat(index: number, subid?: number, extra?: number): number; + getState(index: number, subid?: number): boolean; + getQuest(quest: number, subid: number): number + getParent(): Unit | string; + getMinionCount(): number; + + // additions from kolbot + getStatEx(one: number, sub?: number): number; + getItemsEx(classId?: number, mode?: number, unitId?: number): ItemUnit[]; + getItemsEx(name?: string, mode?: number, unitId?: number): ItemUnit[]; + inArea(area: number): boolean; + checkForMobs(givenSettings: { + range?: number; + count?: number; + coll?: number; + spectype: number + }): boolean + } + + type PlayerType = 0; + class Player extends Unit { + public type: PlayerType; + } + + type MonsterType = 1; + interface Monster extends Unit { + } + + class Monster extends Unit { + public type: MonsterType; + readonly isChampion: boolean; + readonly isUnique: boolean; + readonly isMinion: boolean; + readonly isSuperUnique: boolean; + readonly isSpecial: boolean; + readonly isWalking: boolean; + readonly isRunning: boolean; + readonly isMoving: boolean; + readonly isChilled: boolean; + readonly isFrozen: boolean; + readonly currentVelocity: number; + readonly isPrimeEvil: boolean; + readonly isBoss: boolean; + readonly isGhost: boolean; + readonly isDoll: boolean; + readonly isMonsterObject: boolean; + readonly isMonsterEgg: boolean; + readonly isMonsterNest: boolean; + readonly isBaalTentacle: boolean; + readonly isShaman: boolean; + readonly isUnraveler: boolean; + readonly isFallen: boolean; + readonly isBeetle: boolean; + readonly extraStrong: boolean; + readonly extraFast: boolean; + readonly cursed: boolean; + readonly magicResistant: boolean; + readonly fireEnchanted: boolean; + readonly lightningEnchanted: boolean; + readonly coldEnchanted: boolean; + readonly manaBurn: boolean; + readonly teleportation: boolean; + readonly spectralHit: boolean; + readonly stoneSkin: boolean; + readonly multiShot: boolean; + readonly charlvl: number; + readonly spectype: number; + readonly curseable: boolean; + readonly scareable: boolean; + + getEnchant(type: number): boolean; + hasEnchant(...enchants: number): boolean + } + + class NPCUnit extends Unit { + public type: MonsterType; + readonly itemcount: number; + + openMenu(): boolean; + useMenu(): boolean; + startTrade: (mode: any) => (any | boolean); + } + + class MercUnit extends Monster { + equip(destination: number | undefined, item: ItemUnit) + } + + interface ObjectUnit extends Unit { + } + + type ObjectType = 2; + class ObjectUnit extends Unit { + public type: ObjectType; + objtype: number; + } + + type MissileType = 3; + class Missile extends Unit { + public readonly type: MissileType; + hits(position: PathNode): boolean; + } + + type ItemType = 4; + interface ItemUnit extends Unit { + + } + + class ItemUnit extends Unit { + // todo define item modes + public readonly type: ItemType; + readonly code: string; + readonly prefixes: string[]; + readonly suffixes: string[]; + readonly prefixnum: number; + readonly suffixnum: number; + readonly prefixenums: number[]; + readonly suffixnums: number[]; + readonly fname: string; + readonly quality: number; + readonly node: number; + readonly location: number; + readonly sizex: number; + readonly sizey: number; + readonly itemType: number; + readonly bodylocation: number; + readonly ilvl: number; + readonly lvlreq: number; + readonly gfx: number; + readonly description: string; + + // additional, not from d2bs + readonly identified: boolean; + readonly isEquipped: boolean + readonly dexreq: number + readonly strreq: number + readonly isInInventory: boolean; + readonly isInStash: boolean; + readonly isInCube: boolean; + readonly isInStorage: boolean; + readonly isInBelt: boolean; + readonly isOnMain: boolean; + readonly isOnSwap: boolean; + readonly runeword: boolean; + readonly questItem: boolean; + readonly ethereal: boolean; + readonly twoHanded: boolean + readonly oneOrTwoHanded: boolean; + readonly strictlyTwoHanded: boolean; + readonly sellable: boolean; + readonly lowQuality: boolean; + readonly normal: boolean; + readonly superior: boolean; + readonly magic: boolean; + readonly set: boolean; + readonly rare: boolean; + readonly unique: boolean; + readonly crafted: boolean; + readonly sockets: number; + readonly onGroundOrDropping: boolean; + readonly isShield: boolean; + readonly isAnni: boolean; + readonly isTorch: boolean; + readonly isGheeds: boolean; + + getColor(): number; + getBodyLoc(): number[]; + getFlags(): number; + getFlag(flag: number): boolean; + // shop(mode: ShopModes): boolean; + getItemCost(type?: 0 | 1 | 2): number; + sell(): boolean; + drop(): boolean; + equip(slot?: number): boolean; + buy(shift?: boolean, gamble?: boolean): boolean; + sellOrDrop():void + toCursor():boolean + use(): boolean; + } + + type TileType = 5; + class Tile extends Unit { + public type: TileType; + } + + interface MeType extends Unit { + public type: PlayerType; + readonly account: string; + readonly charname: string; + readonly diff: 0 | 1 | 2; + readonly maxdiff: 0 | 1 | 2; + readonly gamestarttime: number; + readonly gametype: 0 | 1; + readonly itemoncursor: boolean; + readonly ladder: number; + readonly ping: number; + readonly fps: number; + readonly locale: number; + readonly playertype: 0|1; + readonly realm: string; + readonly realmshort: string; + readonly mercrevivecost: number; + chickenhp: number; + chickenmp: number; + quitonhostile: boolean; + readonly gameReady: boolean; + readonly profile: string; + readonly pid: number; + readonly charflags: number; + readonly screensize: number; + readonly windowtitle: string; + readonly ingame: boolean; + quitonerror: boolean; + maxgametime: number; + readonly gamepassword: string; + readonly gamestarttime: number; + readonly gamename: string; + readonly gameserverip: string; + readonly itemcount: number; + readonly classid: 0 | 1 | 2 | 3 | 4 | 5 | 6; + readonly weaponswitch: 0|1; + readonly gameReady: boolean; + blockMouse: boolean; + blockKeys: boolean; + runwalk: number; + automap: boolean; + + readonly expansion: boolean; + readonly classic: boolean; + readonly softcore: boolean; + readonly hardcore: boolean; + readonly normal: boolean; + readonly nightmare: boolean; + readonly hell: boolean; + readonly sorceress: boolean; + readonly amazon: boolean; + readonly necromancer: boolean; + readonly paladin: boolean; + readonly barbarian: boolean; + readonly assassin: boolean; + readonly druid: boolean; + readonly hpPercent: number; + readonly mpPercent: number; + readonly gold: number; + readonly inTown: boolean; + readonly highestAct: 1 | 2 | 3 | 4 | 5; + readonly staminaPercent: number; + readonly staminaDrainPerSec: number; + readonly staminaTimeLeft: number; + readonly staminaMaxDuration: number; + readonly inShop: boolean; + readonly skillDelay: boolean; + readonly highestAct: 1 | 2 | 3 | 4 | 5; + readonly highestQuestDone: number; + readonly den: boolean; + readonly bloodraven: boolean; + readonly smith: boolean; + readonly imbue: boolean; + readonly cain: boolean; + readonly tristram: boolean; + readonly countess: boolean; + readonly andariel: boolean; + readonly radament: boolean; + readonly horadricstaff: boolean; + readonly summoner: boolean; + readonly duriel: boolean; + readonly goldenbird: boolean; + readonly lamessen: boolean; + readonly gidbinn: boolean; + readonly travincal: boolean; + readonly mephisto: boolean; + readonly izual: boolean; + readonly hellforge: boolean; + readonly diablo: boolean; + readonly shenk: boolean; + readonly larzuk: boolean; + readonly savebarby: boolean; + readonly barbrescue: boolean; + readonly anya: boolean; + readonly ancients: boolean; + readonly baal: boolean; + readonly cows: boolean; + readonly respec: boolean; + readonly diffCompleted: boolean; + wirtsleg: ItemUnit; + cube: ItemUnit; + shaft: ItemUnit; + amulet: ItemUnit; + staff: ItemUnit; + completestaff: ItemUnit; + eye: ItemUnit; + brain: ItemUnit; + heart: ItemUnit; + khalimswill: ItemUnit; + khalimsflail: ItemUnit; + malahspotion: ItemUnit; + scrollofresistance: ItemUnit; + readonly walking: boolean; + readonly running: boolean; + readonly deadOrInSequence: boolean; + readonly moving: boolean; + readonly FCR: number; + readonly FHR: number; + readonly FBR: number; + readonly IAS: number; + readonly shapeshifted: boolean; + + haveWaypoint(area: number): boolean; + overhead(msg: string): void; + repair(): boolean; + revive(): void; + move(x: number, y: number): boolean; + setSkill(): boolean; + cancel(number?: number): boolean; + inArea(area: number): boolean; + switchToPrimary(): boolean; + checkItem(itemInfo: { + classid?: number; + itemtype?: number; + quality?: number; + runeword?: boolean; + ethereal?: boolean; + name?: string | number; + equipped?: boolean | number; + }): {have: boolean; item: ItemUnit | null}; + haveSome(arg0: { name: number; equipped: boolean; }[]): any; + equip(destination: number | undefined, item: ItemUnit); + getRepairCost(): number; + findItems(param: number, number?: number, number2?: number): ItemUnit[]; + usingShield(): boolean; + walk(): void; + run(): void; + getPingDelay(): number; + findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; + findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; + cancelUIFlags(): boolean; + switchWeapons(slot: 0 | 1): boolean; + castingFrames(skillId: number, fcr?: number, charClass?: number): number; + castingDuration(skillId: number, fcr?: number, charClass?: number): number; + getWeaponQuantity(weaponLoc: number): number; + needPotions(): boolean; + getTpTool(): ItemUnit | null; + getIdTool(): ItemUnit | null; + canTpToTown(): boolean; + needHealing(): boolean; + getTome(id: number): ItemUnit | null; + getUnids(): ItemUnit[]; + fieldID(): boolean; + switchToPrimary(): boolean; + haveWaypoint(area: number): boolean; + } + + const me: MeType + + // type PathNode = { + // x: number, + // y: number + // }; + interface PathNode { + x: number; + y: number; + readonly distance: number; + } + + function getUnit(type: 4, name?: string, mode?: number, unitId?: number): ItemUnit + function getUnit(type: 4, classId?: number, mode?: number, unitId?: number): ItemUnit + function getUnit(type: 1, name?: string, mode?: number, unitId?: number): Monster + function getUnit(type: 1, classId?: number, mode?: number, unitId?: number): Monster + function getUnit(type?: number, name?: string, mode?: number, unitId?: number): Unit + function getUnit(type?: number, classId?: number, mode?: number, unitId?: number): Unit + + function getPath(area: number, fromX: number, fromY: number, toX: number, toY: number, reductionType: 0 | 1 | 2, radius: number): PathNode[] | false + function getCollision(area: number, x: number, y: number) + function getMercHP(): number + function getCursorType(type: 1 | 3 | 6): boolean + function getCursorType(): number + function getSkillByName(name: string): number + function getSkillById(id: number): string + function getLocaleString(id: number): string + + // Never seen in the wild, not sure about arguments + function getTextSize(name: string, size: number) + function getThreadPriority(): number + function getUIFlag(flag: number): boolean + function getTradeInfo(mode: 0 | 1 | 2): boolean + function getWaypoint(id: number): boolean + + class Script { + running: boolean; + name: string; + type: boolean; + threadid: number; + memory: number; + + getNext(): Script; + pause(): boolean; + resume(): boolean; + join(): void; + stop(): boolean; + send(): void; + } + + function getScript(name?: string | boolean): Script | false + function getScripts(): Script | false + + class Room { + area: number; + correcttomb: number; + x: number; + y: number; + xsize: number; + ysize: number; + + getNext(): Room | false; + getNearby(): Room[]; + isInRoom(unit: PathNode):boolean + isInRoom(x:number, y:number):boolean + } + + function getRoom(area: number, x: number, y: number): Room | false + function getRoom(x: number, y: number): Room | false + function getRoom(area: number): Room | false + function getRoom(): Room | false + + class Party { + x: number; + y: number; + area: number; + gid: number; + life: number; + partyflag: number; + partyid: number; + name: string; + classid: number; + level: number; + inTown: any; + + getNext(): Party | false; + } + + function getParty(unit?: Unit): Party | false + + class PresetUnit { + id: number; + x: number; + y: number; + roomx: number; + roomy: number; + level: number; + readonly distance: number; + + getNext(): PresetUnit | false; + realCoords(): { area: number, x: number, y: number }; + } + + type PresetObject = { + area: number, + id: number, + type: number, + x: number, + y: number, + } + + function getPresetUnit(area?: number, objType?: number, classid?: number): PresetUnit | false + function getPresetUnit(area?: number, objType?: 2, classid?: number): PresetUnit | false + function getPresetUnits(area?: number, objType?: number, classid?: number): PresetUnit[] | false + + interface Exit extends Object { + x: number, + y: number, + type: number, + target: number, + tileid: number, + level: number, + } + + class Area { + name: string; + x: number; + xsize: number; + y: number; + ysize: number; + id: number; + exits: Exit[]; + + getNext(): Area | false; + } + + function getArea(id?: number): Area | false + function getBaseStat(table: string, row: number, column: string | number): number | string + function getBaseStat(row: number, column: string): number | string + + /** + * @todo get a better understanding of Control + */ + class Control { + /** + * The text of the control + */ + text: string; + + /** + * The x coordinate of the control + */ + x: number; + + /** + * The y coordinate of the control + */ + y: number; + + /** + * The xsize (width) of the control + */ + xsize: number; + + /** + * The ysize (height) of the control + */ + ysize: number; + + /** + * The state of the control + * - Disabled - 4 + * @todo figure out the rest + */ + state: number; + + /** + * Return whether or not the Control holds a password (starred out text). + */ + password: boolean; + + /** + * The type of control + * - 1 - TextBox + * - 2 - Image + * - 3 - Image2 + * - 4 - LabelBox + * - 5 - ScrollBar + * - 6 - Button + * - 7 - List + * - 8 - Timer + * - 9 - Smack + * - 10 - ProgressBar + * - 11 - Popup + * - 12 - AccountList + */ + type: number; + cursorpos: any; + selectstart: any; + selectend: any; + disabled: number; + + getNext(): Control | undefined; + click(x?: number, y?: number): void; + setText(text: string): void; + getText(): string[]; + } + + type Profile = { + type: number, + ip: number, + username: string, + gateway: string, + character: string, + difficulty: number, + maxloginTime: number, + maxCharacterSelectTime: number, + } + function Profile(): Profile; + + class SQLite { + constructor(database: string, isNew: boolean); + execute(query: string): boolean; + query(query: string): SQLiteQuery; + lastRowId: number; + close(): void; + } + + class SQLiteQuery { + next(): boolean; + ready: boolean; + getColumnValue(index: number): any; + } + + function getControl(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control | false + function getControls(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control[] + function getPlayerFlag(meGid: number, otherGid: number, type: number): boolean + function getTickCount(): number + function getInteractedNPC(): NPCUnit | false + function getIsTalkingNPC(): boolean + function getDialogLines(): { handler() }[] | false + function print(what: string): void + function stringToEUC(arg: any): [] + function utf8ToEuc(arg: any): [] + function delay(ms: number): void + function load(file: string): boolean + function isIncluded(file: string): boolean + function include(file: string): boolean + function stacktrace(): true + function rand(from: number, to: number): number + function copy(what: string): void + function paste(): string + + function sendCopyData(noIdea: null, handle: number | string, mode: number, data: string): void; + + function sendDDE() + function keystate() + + type eventName = 'gamepacket' | 'scriptmsg' | 'copydata' | 'keyup' | 'keydown' | 'itemaction' | 'chatmsg'; + + function addEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void + function addEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void + function addEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void + function addEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void + function addEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number|string) => void)): void + function addEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void + function addEventListener(eventType: eventName, callback: ((...args: any) => void)): void + + function removeEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void + function removeEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void + function removeEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void + function removeEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void + function removeEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number) => void)): void + function removeEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void + function removeEventListener(eventType: eventName, callback: ((...args: any) => void)): void + + function clearEvent() + function clearAllEvents() + function js_strict() + function version(): number + function scriptBroadcast(what: string | object): void + function sqlite_version() + function sqlite_memusage() + + type directory = { + getFiles(): string[]; + getFolders(): string[]; + create(what?: string): boolean; + }; + function dopen(what?: string): directory | false; + function debugLog(text: string): void + function showConsole(): void + function hideConsole(): void + + // out of game functions + function login(name?: string): void + function selectCharacter() + function createGame() + function joinGame() + function addProfile() + function getLocation():number + function loadMpq() + + // game functions that don't have anything to do with gathering data + function submitItem(): void + function getMouseCoords() + function copyUnit(unit: S): S + function clickMap(type: 0 | 1 | 2 | 3, shift: 0 | 1, x: number, y: number) + function acceptTrade() + function tradeOk() + function beep(id?: number) + + function clickItem(where: 0 | 1 | 2, bodyLocation: number) + function clickItem(where: 0 | 1 | 2, item: ItemUnit) + function clickItem(where: 0 | 1 | 2, x: number, y: number) + function clickItem(where: 0 | 1 | 2, x: number, y: number, location: number) + + function getDistance(a: Unit, b: Unit): number + function getDistance(a: Unit, toX: number, toY: number): number + function getDistance(fromX: number, fromY: number, b: Unit): number + function getDistance(fromX: number, fromY: number, toX: number, toY: number): number + + function gold(amount: number, changeType?: 0 | 1 | 2 | 3 | 4): void + function checkCollision(a: Unit, b: Unit, type: number): boolean + function playSound(num: number): void + function quit(): never + function quitGame(): never + function say(what: string): void + function clickParty(player: Party, type: 0 | 1 | 2 | 3 | 4) + function weaponSwitch(): void + function transmute(): void + function useStatPoint(type: number): void + function useSkillPoint(type: number): void + function takeScreenshot(): void + function moveNPC(npc: Monster, x: number, y: number): void + + function getPacket(buffer: ArrayBuffer): void + function getPacket(...args: { size: number, data: number }[]): void + + function sendPacket(buffer: ArrayBuffer): void + function sendPacket(...number: number[]): void + + function getIP(): string + function sendKey(key: number): void + function revealLevel(unknown: true): void + + // hash functions + function md5(str: string): string + function sha1(str: string): string + function sha256(str: string): string + function sha384(str: string): string + function sha512(str: string): string + function md5_file(str: string): string + function sha1_file(str: string): string + function sha256_file(str: string): string + function sha384_file(str: string): string + function sha512_file(str: string): string + + interface Console { + static log(...whatever: any[]): void + static debug(...whatever: any[]): void + static warn(...whatever: any[]): void + static error(...whatever: any[]): void + static time(name: string): void; + static timeEnd(name: string): void; + static trace(): void; + static info(start: boolean, msg: string, timer: string): void; + } + const console: Console; + + function includeIfNotIncluded(file?: string): boolean; + function includeCoreLibs(obj: { exclude: string[] }): boolean; + function includeSystemLibs(): boolean; + function clone(obj: Date | any[] | object): ThisParameterType; + function copyObj(from: object): object; + + interface StarterConfig { + MinGameTime: number, + MaxGameTime?: number, + PingQuitDelay: number, + CreateGameDelay: number, + ResetCount: number + CharacterDifference: number, + MaxPlayerCount: number, + StopOnDeadHardcore: boolean, + + JoinChannel: string, + FirstJoinMessage: string, + ChatActionsDelay: number, + AnnounceGames: boolean, + AnnounceMessage: string, + AfterGameMessage: string, + + InvalidPasswordDelay: number, // Minutes to wait after getting Invalid Password message + VersionErrorDelay: number, // Seconds to wait after 'unable to identify version' message + SwitchKeyDelay: number, // Seconds to wait before switching a used/banned key or after realm down + CrashDelay: number, // Seconds to wait after a d2 window crash + FTJDelay: number, // Seconds to wait after failing to create a game + RealmDownDelay: number, // Minutes to wait after getting Realm Down message + UnableToConnectDelay: number, // Minutes to wait after Unable To Connect message + TCPIPNoHostDelay: number, // Seconds to wait after Cannot Connect To Server message + CDKeyInUseDelay: number, // Minutes to wait before connecting again if CD-Key is in use. + ConnectingTimeout: number, // Seconds to wait before cancelling the 'Connecting...' screen + PleaseWaitTimeout: number, // Seconds to wait before cancelling the 'Please Wait...' screen + WaitInLineTimeout: number, // Seconds to wait before cancelling the 'Waiting in Line...' screen + WaitOutQueueRestriction: boolean, // Wait out queue if we are restricted, queue time > 10000 + WaitOutQueueExitToMenu: boolean, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby + GameDoesNotExistTimeout: number, // Seconds to wait before cancelling the 'Game does not exist.' screen + } + + interface StarterInterface { + Config: StarterConfig, + useChat: boolean, + pingQuit: boolean, + inGame: boolean, + firstLogin: boolean, + firstRun: boolean, + isUp: "yes"|"no", + loginRetry: number, + deadCheck: boolean, + chatActionsDone: boolean, + gameStart: boolean, + gameCount: number, + lastGameStatus: string, + handle: number | null, + connectFail: boolean, + connectFailRetry: number, + makeAccount: false, + channelNotify: boolean, + chanInfo: { + joinChannel: string, + firstMsg: string, + afterMsg: string, + announce: boolean, + }, + gameInfo: { + error: string, + crashInfo: { + currScript: number, + area: number, + }, + switchKeys: boolean, + }, + joinInfo: {}, + profileInfo: {}, + + sayMsg(string: string): void, + timer(tick: number): string, + locationTimeout(time: number, location: number): boolean, + setNextGame(gameInfo: { gameName: string }): void, + updateCount(): void, + scriptMsgEvent(msg: string): void, + receiveCopyData(mode: number, msg: string | object): void, + randomString(len?: number, useNumbers?: boolean): string, + randomNumberString(len?: number): string, + } + + const Starter: StarterInterface; + + namespace Time { + function seconds(seconds: number): number; + function minutes(minutes: number): number; + function format(ms: number): string; + function toSeconds(ms: number): number; + function toMinutes(ms: number): number; + function toHours(ms: number): number; + function toDays(ms: number): number; + function elapsed(start: number): number; + } } export {}; diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index a41e76320..8e6246f8d 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -1,66 +1,66 @@ export {}; declare global { - interface AttackResult { - FAILED: 0, - SUCCESS: 1, - CANTATTACK: 2, // need to fix the ambiguity between this result and Failed - NEEDMANA: 3 - } - namespace Attack { - const infinity: boolean; - const auradin: boolean; - const monsterObjects: number[]; - const Result: AttackResult; - function init(): void; - function checkSlot(slot?: 0 | 1): boolean; - function getPrimarySlot(): 0 | 1; - function getCustomAttack(unit: Unit): boolean | [number, number]; - function getCharges(): boolean; - function checkInfinity(): boolean; - function checkAuradin(): boolean; - function canTeleStomp(unit: Unit): boolean; - function kill(classId: number | Unit): boolean; - function hurt(classId: string | number | Unit, percent: number): boolean; - function getScarinessLevel(unit: Unit): number; - function clear(range?: number, spectype?: number, bossId?: number | Unit, sortfunc?: Function, pickit?: boolean): boolean; - function clearClassids(...ids: number[]): boolean; - function getMob(classid: number, spectype: number, range: number, center: Unit | { - x: number; - y: number; - }): Monster[]; - function clearList(mainArg: Function | Unit[], sortFunc?: Function, refresh?: boolean): boolean; - function securePosition(x: number, y: number, range?: number, timer?: number, skipBlocked?: boolean, special?: boolean): void; - function markRoom(room: Room, color: number): void; - function countUniques(): void; - function storeStatistics(area: number): void; - function clearLevel(spectype?: number): boolean; - function sortMonsters(unitA: Unit, unitB: Unit): boolean; - function validSpot(x: number, y: number, skill?: number, unitid?: number): boolean; - function openChests(range: number, x?: number, y?: number): boolean; - function buildMonsterList(): [] | Monster[]; - function findSafeSpot(unit: Unit, distance: number, spread: number, range: number, ...args: any[]): { - x: number; - y: number; - }; - function deploy(unit: any, distance: any, spread: any, range: any, ...args: any[]): boolean; - function getMonsterCount(x: any, y: any, range: any, list: any): number; - function buildGrid(xmin: any, xmax: any, ymin: any, ymax: any, spread: any): { - x: any; - y: any; - coll: number; - }[]; - function skipCheck(unit: Unit): boolean; - function getSkillElement(skillId: number): false | "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"; - function getResist(unit: Unit, type: "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"): boolean; - function getLowerResistPercent(): number; - function getConvictionPercent(): number; - function checkResist(unit: any, val: any, maxres?: number): boolean; - function canAttack(unit: any): boolean; - function usingBow(): false | "bow" | "crossbow"; - function getIntoPosition(unit: any, distance: any, coll: any, walk: any): boolean; - function getNearestMonster(givenSettings?: {}): any; - function checkCorpse(unit: any): boolean; - function checkNearCorpses(unit: any, range?: number): any; - function whirlwind(unit: any): boolean; - } + interface AttackResult { + FAILED: 0, + SUCCESS: 1, + CANTATTACK: 2, // need to fix the ambiguity between this result and Failed + NEEDMANA: 3 + } + namespace Attack { + const infinity: boolean; + const auradin: boolean; + const monsterObjects: number[]; + const Result: AttackResult; + function init(): void; + function checkSlot(slot?: 0 | 1): boolean; + function getPrimarySlot(): 0 | 1; + function getCustomAttack(unit: Unit): boolean | [number, number]; + function getCharges(): boolean; + function checkInfinity(): boolean; + function checkAuradin(): boolean; + function canTeleStomp(unit: Monster | Player): boolean; + function kill(classId: number | Unit): boolean; + function hurt(classId: string | number | Unit, percent: number): boolean; + function getScarinessLevel(unit: Unit): number; + function clear(range?: number, spectype?: number, bossId?: number | Unit, sortfunc?: Function, pickit?: boolean): boolean; + function clearClassids(...ids: number[]): boolean; + function getMob(classid: number, spectype: number, range: number, center: Unit | { + x: number; + y: number; + }): Monster[]; + function clearList(mainArg: Function | Unit[], sortFunc?: Function, refresh?: boolean): boolean; + function securePosition(x: number, y: number, range?: number, timer?: number, skipBlocked?: boolean, special?: boolean): void; + function markRoom(room: Room, color: number): void; + function countUniques(): void; + function storeStatistics(area: number): void; + function clearLevel(spectype?: number): boolean; + function sortMonsters(unitA: Unit, unitB: Unit): boolean; + function validSpot(x: number, y: number, skill?: number, unitid?: number): boolean; + function openChests(range: number, x?: number, y?: number): boolean; + function buildMonsterList(): [] | Monster[]; + function findSafeSpot(unit: Unit, distance: number, spread: number, range: number, ...args: any[]): { + x: number; + y: number; + }; + function deploy(unit: Monster, distance: any, spread: any, range: any, ...args: any[]): boolean; + function getMonsterCount(x: any, y: any, range: any, list: any): number; + function buildGrid(xmin: any, xmax: any, ymin: any, ymax: any, spread: any): { + x: any; + y: any; + coll: number; + }[]; + function skipCheck(unit: Monster): boolean; + function getSkillElement(skillId: number): false | "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"; + function getResist(unit: Monster, type: "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"): boolean; + function getLowerResistPercent(): number; + function getConvictionPercent(): number; + function checkResist(unit: Monster, val: any, maxres?: number): boolean; + function canAttack(unit: Monster): boolean; + function usingBow(): false | "bow" | "crossbow"; + function getIntoPosition(unit: Monster, distance: any, coll: any, walk: any): boolean; + function getNearestMonster(givenSettings?: {}): any; + function checkCorpse(unit: Monster): boolean; + function checkNearCorpses(unit: Monster, range?: number): any; + function whirlwind(unit: Monster | Player): boolean; + } } diff --git a/d2bs/kolbot/sdk/types/AutoMule.d.ts b/d2bs/kolbot/sdk/types/AutoMule.d.ts index 2035c529e..669d8235b 100644 --- a/d2bs/kolbot/sdk/types/AutoMule.d.ts +++ b/d2bs/kolbot/sdk/types/AutoMule.d.ts @@ -1,101 +1,101 @@ // @ts-nocheck export {}; declare global { - export type muleObj = { - /** - * - The name of mule profile in d2bot#. It will be started and stopped when needed. - */ - muleProfile: string; - /** - * - Account prefix. Numbers added automatically when making accounts. - */ - accountPrefix: string; - /** - * - Account password - */ - accountPassword: string; - /** - * - Character prefix. Suffix added automatically when making characters. - */ - charPrefix: string; - /** - * - Available options: "useast", "uswest", "europe", "asia" - */ - realm: string; - /** - * - expansion character - */ - expansion: boolean; - /** - * - ladder character - */ - ladder: boolean; - /** - * - Maximum number of mules to create per account (between 1 to 18) - */ - charsPerAcc: number; - /** - * - Game name and password of the mule game. Never use the same game name as for mule logger. - */ - muleGameName: string[]; - /** - * - List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"] - */ - enabledProfiles: string[]; - /** - * - Stop a profile prior to muling. Useful when running 8 bots without proxies. - */ - stopProfile: string; - /** - * - true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. - */ - stopProfileKeyRelease: boolean; - /** - * - Trigger muling at the end of a game if used space in stash greater than or equal to given percent. - */ - usedStashTrigger: number; - /** - * - Trigger muling at the end of a game if used space in inventory greater than or equal to given percent. - */ - usedInventoryTrigger: number; - /** - * - Mule items that have been stashed at some point but are no longer in pickit. - */ - muleOrphans: boolean; - /** - * - Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. - */ - continuousMule: boolean; - /** - * - Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. - */ - skipMuleResponse: boolean; - /** - * - Only log character when full, solves an issue with droppers attempting to use characters who are already in game - */ - onlyLogWhenFull: boolean; - }; - export const AutoMule: { - Mules: { - [x: string]: muleObj; + export type muleObj = { + /** + * - The name of mule profile in d2bot#. It will be started and stopped when needed. + */ + muleProfile: string; + /** + * - Account prefix. Numbers added automatically when making accounts. + */ + accountPrefix: string; + /** + * - Account password + */ + accountPassword: string; + /** + * - Character prefix. Suffix added automatically when making characters. + */ + charPrefix: string; + /** + * - Available options: "useast", "uswest", "europe", "asia" + */ + realm: string; + /** + * - expansion character + */ + expansion: boolean; + /** + * - ladder character + */ + ladder: boolean; + /** + * - Maximum number of mules to create per account (between 1 to 18) + */ + charsPerAcc: number; + /** + * - Game name and password of the mule game. Never use the same game name as for mule logger. + */ + muleGameName: string[]; + /** + * - List of profiles that will mule items. Example: enabledProfiles: ["profile 1", "profile 2"] + */ + enabledProfiles: string[]; + /** + * - Stop a profile prior to muling. Useful when running 8 bots without proxies. + */ + stopProfile: string; + /** + * - true = stopProfile key will get released on stop. useful when using 100% of your keys for botting. + */ + stopProfileKeyRelease: boolean; + /** + * - Trigger muling at the end of a game if used space in stash greater than or equal to given percent. + */ + usedStashTrigger: number; + /** + * - Trigger muling at the end of a game if used space in inventory greater than or equal to given percent. + */ + usedInventoryTrigger: number; + /** + * - Mule items that have been stashed at some point but are no longer in pickit. + */ + muleOrphans: boolean; + /** + * - Mule stays in game for continuous muling. muleProfile must be dedicated and started manually. + */ + continuousMule: boolean; + /** + * - Skip mule response check and attempt to join mule game. Useful if mule is shared and/or ran on different system. + */ + skipMuleResponse: boolean; + /** + * - Only log character when full, solves an issue with droppers attempting to use characters who are already in game + */ + onlyLogWhenFull: boolean; + }; + export const AutoMule: { + Mules: { + [x: string]: muleObj; }; - TorchAnniMules: { - [x: string]: muleObj; + TorchAnniMules: { + [x: string]: muleObj; }; - getInfo(): boolean | muleObj - muleCheck(): void - getMule(): void - outOfGameCheck(): void - inGameCheck(): void - dropStuff(): void - matchItem(item: ItemUnit, list: any): void - getMuleItems(): ItemUnit[] - utilityIngredient(item: ItemUnit): void - cubingIngredient(item: ItemUnit): void - runewordIngredient(item: ItemUnit): void - dropCharm(dropAnni: any): void - getMaster(info: any): void - getMuleObject(mode: any, master: any): muleObj | undefined - getMuleFilename(mode: any, master: any): void - } + getInfo(): boolean | muleObj + muleCheck(): void + getMule(): void + outOfGameCheck(): void + inGameCheck(): void + dropStuff(): void + matchItem(item: ItemUnit, list: any): void + getMuleItems(): ItemUnit[] + utilityIngredient(item: ItemUnit): void + cubingIngredient(item: ItemUnit): void + runewordIngredient(item: ItemUnit): void + dropCharm(dropAnni: any): void + getMaster(info: any): void + getMuleObject(mode: any, master: any): muleObj | undefined + getMuleFilename(mode: any, master: any): void + } } diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 2bb85b1c5..46dc3582c 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -6,557 +6,557 @@ */ declare global { - // interface Scripts { [data: string]: Partial | boolean } - namespace Config { - function init(notify: any): void; - const Loaded: boolean; - const DebugMode: boolean; - const StartDelay: number; - const PickDelay: number; - const AreaDelay: number; - const MinGameTime: number; - const MaxGameTime: number; - const LifeChicken: number; - const ManaChicken: number; - const UseHP: number; - const UseMP: number; - const UseRejuvHP: number; - const UseRejuvMP: number; - const UseMercHP: number; - const UseMercRejuv: number; - const MercChicken: number; - const IronGolemChicken: number; - const HealHP: number; - const HealMP: number; - const HealStatus: boolean; - const TownHP: number; - const TownMP: number; - namespace StackThawingPots { - const enabled: boolean; - const quantity: number; - } - namespace StackAntidotePots { - const enabled_1: boolean; - export { enabled_1 as enabled }; - const quantity_1: number; - export { quantity_1 as quantity }; - } - namespace StackStaminaPots { - const enabled_2: boolean; - export { enabled_2 as enabled }; - const quantity_2: number; - export { quantity_2 as quantity }; - } - const AutoMap: boolean; - const LastMessage: string; - const UseMerc: boolean; - const MercWatch: boolean; - const LowGold: number; - const StashGold: number; - namespace FieldID { - const Enabled: boolean; - const PacketID: boolean; - const UsedSpace: number; - } - namespace DroppedItemsAnnounce { - const Enable: boolean; - const Quality: any[]; - const LogToOOG: boolean; - const OOGQuality: any[]; - } - namespace CainID { - const Enable_1: boolean; - export { Enable_1 as Enable }; - export const MinGold: number; - export const MinUnids: number; - } - const Inventory: number[][]; - namespace LocalChat { - const Enabled_1: boolean; - export { Enabled_1 as Enabled }; - export const Toggle: boolean; - export const Mode: number; - } - const Silence: boolean; - const PublicMode: boolean; - const PartyAfterScript: boolean; - const Greetings: any[]; - const DeathMessages: any[]; - const Congratulations: any[]; - const ShitList: boolean; - const UnpartyShitlisted: boolean; - const Leader: string; - const QuitList: any[]; - const QuitListMode: number; - const QuitListDelay: any[]; - const HPBuffer: number; - const MPBuffer: number; - const RejuvBuffer: number; - const PickRange: number; - const MakeRoom: boolean; - const ClearInvOnStart: boolean; - const FastPick: boolean; - const ManualPlayPick: boolean; - namespace OpenChests { - const Enabled_2: boolean; - export { Enabled_2 as Enabled }; - export const Range: number; - export const Types: string[]; - } - const PickitFiles: any[]; - const BeltColumn: any[]; - const MinColumn: any[]; - const SkipId: any[]; - const SkipEnchant: any[]; - const SkipImmune: any[]; - const SkipAura: any[]; - const SkipException: any[]; - const ScanShrines: any[]; - const Debug: boolean; - namespace AutoMule { - const Trigger: any[]; - const Force: any[]; - const Exclude: any[]; - } - const ItemInfo: boolean; - const ItemInfoQuality: any[]; - const LogKeys: boolean; - const LogOrgans: boolean; - const LogLowRunes: boolean; - const LogMiddleRunes: boolean; - const LogHighRunes: boolean; - const LogLowGems: boolean; - const LogHighGems: boolean; - const SkipLogging: any[]; - const ShowCubingInfo: boolean; - const Cubing: boolean; - const CubeRepair: boolean; - const RepairPercent: number; - const Recipes: any[]; - const MakeRunewords: boolean; - const Runewords: any[][]; - const KeepRunewords: any[]; - const Gamble: boolean; - const GambleItems: any[]; - const GambleGoldStart: number; - const GambleGoldStop: number; - const MiniShopBot: boolean; - const TeleSwitch: boolean; - const MFSwitchPercent: number; - const PrimarySlot: number; - const LogExperience: boolean; - const TownCheck: boolean; - const PingQuit: { - Ping: number; - Duration: number; - }[]; - const PacketShopping: boolean; - const FCR: number; - const FHR: number; - const FBR: number; - const IAS: number; - const PacketCasting: number; - const WaypointMenu: boolean; - const AntiHostile: boolean; - const RandomPrecast: boolean; - const HostileAction: number; - const TownOnHostile: boolean; - const ViperCheck: boolean; - const StopOnDClone: boolean; - const SoJWaitTime: number; - const KillDclone: boolean; - const DCloneQuit: boolean; - const DCloneWaitTime: number; - const FastParty: boolean; - const AutoEquip: boolean; - const ChampionBias: number; - const UseCta: boolean; - const Dodge: boolean; - const DodgeRange: number; - const DodgeHP: number; - const AttackSkill: any[]; - const LowManaSkill: any[]; - const CustomAttack: {}; - const TeleStomp: boolean; - const NoTele: boolean; - const ClearType: boolean; - const ClearPath: boolean; - const BossPriority: boolean; - const MaxAttackCount: number; - const LightningFuryDelay: number; - const UseInnerSight: boolean; - const UseSlowMissiles: boolean; - const UseDecoy: boolean; - const SummonValkyrie: boolean; - const UseTelekinesis: boolean; - const CastStatic: boolean; - const StaticList: any[]; - const UseEnergyShield: boolean; - const UseColdArmor: boolean; - const Golem: number; - const ActiveSummon: boolean; - const Skeletons: number; - const SkeletonMages: number; - const Revives: number; - const ReviveUnstackable: boolean; - const PoisonNovaDelay: number; - const Curse: any[]; - const CustomCurse: any[]; - const ExplodeCorpses: number; - const Redemption: number[]; - const Charge: boolean; - const Vigor: boolean; - const AvoidDolls: boolean; - const FindItem: boolean; - const FindItemSwitch: boolean; - const UseWarcries: boolean; - const Wereform: number; - const SummonRaven: number; - const SummonAnimal: number; - const SummonVine: number; - const SummonSpirit: number; - const UseTraps: boolean; - const Traps: any[]; - const BossTraps: any[]; - const UseFade: boolean; - const UseBoS: boolean; - const UseVenom: boolean; - const UseBladeShield: boolean; - const UseCloakofShadows: boolean; - const AggressiveCloak: boolean; - const SummonShadow: boolean; - const CustomClassAttack: string; - namespace MapMode { - const UseOwnItemFilter: boolean; - } - const MFLeader: boolean; - namespace Mausoleum { - const KillBishibosh: boolean; - const KillBloodRaven: boolean; - const ClearCrypt: boolean; - } - namespace Cows { - const DontMakePortal: boolean; - const JustMakePortal: boolean; - const KillKing: boolean; - } - namespace Tombs { - const KillDuriel: boolean; - } - namespace Eldritch { - const OpenChest: boolean; - const KillSharptooth: boolean; - const KillShenk: boolean; - const KillDacFarren: boolean; - } - namespace Pindleskin { - const UseWaypoint: boolean; - const KillNihlathak: boolean; - const ViperQuit: boolean; - } - namespace Nihlathak { - const ViperQuit_1: boolean; - export { ViperQuit_1 as ViperQuit }; - const UseWaypoint_1: boolean; - export { UseWaypoint_1 as UseWaypoint }; - } - namespace Pit { - const ClearPath_1: boolean; - export { ClearPath_1 as ClearPath }; - export const ClearPit1: boolean; - } - namespace Snapchip { - const ClearIcyCellar: boolean; - } - namespace Frozenstein { - const ClearFrozenRiver: boolean; - } - namespace Rakanishu { - const KillGriswold: boolean; - } - namespace AutoBaal { - const Leader_1: string; - export { Leader_1 as Leader }; - export const FindShrine: boolean; - export const LeechSpot: number[]; - export const LongRangeSupport: boolean; - } - namespace KurastChests { - const LowerKurast: boolean; - const Bazaar: boolean; - const Sewers1: boolean; - const Sewers2: boolean; - } - namespace Countess { - const KillGhosts: boolean; - } - namespace Baal { - const DollQuit: boolean; - const SoulQuit: boolean; - const KillBaal: boolean; - const HotTPMessage: string; - const SafeTPMessage: string; - const BaalMessage: string; - } - namespace BaalAssistant { - const KillNihlathak_1: boolean; - export { KillNihlathak_1 as KillNihlathak }; - export const FastChaos: boolean; - export const Wait: number; - export const Helper: boolean; - export const GetShrine: boolean; - export const GetShrineWaitForHotTP: boolean; - const DollQuit_1: boolean; - export { DollQuit_1 as DollQuit }; - const SoulQuit_1: boolean; - export { SoulQuit_1 as SoulQuit }; - export const SkipTP: boolean; - export const WaitForSafeTP: boolean; - const KillBaal_1: boolean; - export { KillBaal_1 as KillBaal }; - const HotTPMessage_1: any[]; - export { HotTPMessage_1 as HotTPMessage }; - const SafeTPMessage_1: any[]; - export { SafeTPMessage_1 as SafeTPMessage }; - const BaalMessage_1: any[]; - export { BaalMessage_1 as BaalMessage }; - export const NextGameMessage: any[]; - } - namespace BaalHelper { - const Wait_1: number; - export { Wait_1 as Wait }; - const KillNihlathak_2: boolean; - export { KillNihlathak_2 as KillNihlathak }; - const FastChaos_1: boolean; - export { FastChaos_1 as FastChaos }; - const DollQuit_2: boolean; - export { DollQuit_2 as DollQuit }; - const KillBaal_2: boolean; - export { KillBaal_2 as KillBaal }; - const SkipTP_1: boolean; - export { SkipTP_1 as SkipTP }; - } - namespace Corpsefire { - const ClearDen: boolean; - } - namespace Hephasto { - export const ClearRiver: boolean; - const ClearType_1: boolean; - export { ClearType_1 as ClearType }; - } - namespace Diablo { - const WalkClear: boolean; - const Entrance: boolean; - const JustViz: boolean; - const SealLeader: boolean; - const Fast: boolean; - const SealWarning: string; - const EntranceTP: string; - const StarTP: string; - const DiabloMsg: string; - const ClearRadius: number; - const SealOrder: string[]; - } - namespace DiabloHelper { - const Wait_2: number; - export { Wait_2 as Wait }; - const Entrance_1: boolean; - export { Entrance_1 as Entrance }; - export const SkipIfBaal: boolean; - const SkipTP_2: boolean; - export { SkipTP_2 as SkipTP }; - export const OpenSeals: boolean; - export const SafePrecast: boolean; - const ClearRadius_1: number; - export { ClearRadius_1 as ClearRadius }; - const SealOrder_1: string[]; - export { SealOrder_1 as SealOrder }; - export const RecheckSeals: boolean; - } - namespace MFHelper { - const BreakClearLevel: boolean; - } - namespace Wakka { - const Wait_3: number; - export { Wait_3 as Wait }; - export const StopAtLevel: number; - export const StopProfile: boolean; - const SkipIfBaal_1: boolean; - export { SkipIfBaal_1 as SkipIfBaal }; - } - namespace BattleOrders { - const Mode_1: number; - export { Mode_1 as Mode }; - export const Getters: any[]; - export const Idle: boolean; - export const QuitOnFailure: boolean; - export const SkipIfTardy: boolean; - const Wait_4: number; - export { Wait_4 as Wait }; - } - namespace BoBarbHelper { - const Mode_2: number; - export { Mode_2 as Mode }; - export const Wp: number; - } - namespace ControlBot { - export const Bo: boolean; - export namespace Cows_1 { - const MakeCows: boolean; - const GetLeg: boolean; - } - export { Cows_1 as Cows }; - export namespace Chant { - const Enchant: boolean; - const AutoEnchant: boolean; - } - export namespace Wps { - const GiveWps: boolean; - const SecurePortal: boolean; - } - export const EndMessage: string; - export const GameLength: number; - } - namespace IPHunter { - export const IPList: any[]; - const GameLength_1: number; - export { GameLength_1 as GameLength }; - } - namespace Follower { - const Leader_2: string; - export { Leader_2 as Leader }; - } - namespace Mephisto { - const MoatTrick: boolean; - const KillCouncil: boolean; - const TakeRedPortal: boolean; - } - namespace ShopBot { - const ScanIDs: any[]; - const ShopNPC: string; - const CycleDelay: number; - const QuitOnMatch: boolean; - } - namespace Coldworm { - const KillBeetleburst: boolean; - const ClearMaggotLair: boolean; - } - namespace Summoner { - const FireEye: boolean; - } - namespace AncientTunnels { - const OpenChest_1: boolean; - export { OpenChest_1 as OpenChest }; - export const KillDarkElder: boolean; - } - namespace OrgTorch { - const WaitForKeys: boolean; - const WaitTimeout: boolean; - const UseSalvation: boolean; - const GetFade: boolean; - const MakeTorch: boolean; - namespace PreGame { - namespace Thawing { - const Drink: number; - const At: any[]; - } - namespace Antidote { - const Drink_1: number; - export { Drink_1 as Drink }; - const At_1: any[]; - export { At_1 as At }; - } - } - } - namespace Synch { - const WaitFor: any[]; - } - namespace TristramLeech { - const Leader_3: string; - export { Leader_3 as Leader }; - const Helper_1: boolean; - export { Helper_1 as Helper }; - const Wait_5: number; - export { Wait_5 as Wait }; - } - namespace TravincalLeech { - const Leader_4: string; - export { Leader_4 as Leader }; - const Helper_2: boolean; - export { Helper_2 as Helper }; - const Wait_6: number; - export { Wait_6 as Wait }; - } - namespace Tristram { - export const PortalLeech: boolean; - const WalkClear_1: boolean; - export { WalkClear_1 as WalkClear }; - } - namespace Travincal { - const PortalLeech_1: boolean; - export { PortalLeech_1 as PortalLeech }; - } - namespace SkillStat { - const Skills: any[]; - } - namespace Bonesaw { - const ClearDrifterCavern: boolean; - } - namespace ChestMania { - const Act1: any[]; - const Act2: any[]; - const Act3: any[]; - const Act4: any[]; - const Act5: any[]; - } - namespace ClearAnyArea { - const AreaList: any[]; - } - namespace Rusher { - export const WaitPlayerCount: number; - export const Cain: boolean; - export const Radament: boolean; - export const LamEsen: boolean; - export const Izual: boolean; - export const Shenk: boolean; - export const Anya: boolean; - export const HellAncients: boolean; - const GiveWps_1: boolean; - export { GiveWps_1 as GiveWps }; - export const LastRun: string; - } - namespace Rushee { - const Quester: boolean; - const Bumper: boolean; - } - namespace Questing { - const StopProfile_1: boolean; - export { StopProfile_1 as StopProfile }; - } - namespace AutoSkill { - const Enabled_3: boolean; - export { Enabled_3 as Enabled }; - export const Build: any[]; - export const Save: number; - } - namespace AutoStat { - const Enabled_4: boolean; - export { Enabled_4 as Enabled }; - const Build_1: any[]; - export { Build_1 as Build }; - const Save_1: number; - export { Save_1 as Save }; - export const BlockChance: number; - export const UseBulk: boolean; - } - namespace AutoBuild { - const Enabled_5: boolean; - export { Enabled_5 as Enabled }; - export const Template: string; - export const Verbose: boolean; - const DebugMode_1: boolean; - export { DebugMode_1 as DebugMode }; - } - } + // interface Scripts { [data: string]: Partial | boolean } + namespace Config { + function init(notify: any): void; + const Loaded: boolean; + const DebugMode: boolean; + const StartDelay: number; + const PickDelay: number; + const AreaDelay: number; + const MinGameTime: number; + const MaxGameTime: number; + const LifeChicken: number; + const ManaChicken: number; + const UseHP: number; + const UseMP: number; + const UseRejuvHP: number; + const UseRejuvMP: number; + const UseMercHP: number; + const UseMercRejuv: number; + const MercChicken: number; + const IronGolemChicken: number; + const HealHP: number; + const HealMP: number; + const HealStatus: boolean; + const TownHP: number; + const TownMP: number; + namespace StackThawingPots { + const enabled: boolean; + const quantity: number; + } + namespace StackAntidotePots { + const enabled_1: boolean; + export { enabled_1 as enabled }; + const quantity_1: number; + export { quantity_1 as quantity }; + } + namespace StackStaminaPots { + const enabled_2: boolean; + export { enabled_2 as enabled }; + const quantity_2: number; + export { quantity_2 as quantity }; + } + const AutoMap: boolean; + const LastMessage: string; + const UseMerc: boolean; + const MercWatch: boolean; + const LowGold: number; + const StashGold: number; + namespace FieldID { + const Enabled: boolean; + const PacketID: boolean; + const UsedSpace: number; + } + namespace DroppedItemsAnnounce { + const Enable: boolean; + const Quality: any[]; + const LogToOOG: boolean; + const OOGQuality: any[]; + } + namespace CainID { + const Enable_1: boolean; + export { Enable_1 as Enable }; + export const MinGold: number; + export const MinUnids: number; + } + const Inventory: number[][]; + namespace LocalChat { + const Enabled_1: boolean; + export { Enabled_1 as Enabled }; + export const Toggle: boolean; + export const Mode: number; + } + const Silence: boolean; + const PublicMode: boolean; + const PartyAfterScript: boolean; + const Greetings: any[]; + const DeathMessages: any[]; + const Congratulations: any[]; + const ShitList: boolean; + const UnpartyShitlisted: boolean; + const Leader: string; + const QuitList: any[]; + const QuitListMode: number; + const QuitListDelay: any[]; + const HPBuffer: number; + const MPBuffer: number; + const RejuvBuffer: number; + const PickRange: number; + const MakeRoom: boolean; + const ClearInvOnStart: boolean; + const FastPick: boolean; + const ManualPlayPick: boolean; + namespace OpenChests { + const Enabled_2: boolean; + export { Enabled_2 as Enabled }; + export const Range: number; + export const Types: string[]; + } + const PickitFiles: any[]; + const BeltColumn: any[]; + const MinColumn: any[]; + const SkipId: any[]; + const SkipEnchant: any[]; + const SkipImmune: any[]; + const SkipAura: any[]; + const SkipException: any[]; + const ScanShrines: any[]; + const Debug: boolean; + namespace AutoMule { + const Trigger: any[]; + const Force: any[]; + const Exclude: any[]; + } + const ItemInfo: boolean; + const ItemInfoQuality: any[]; + const LogKeys: boolean; + const LogOrgans: boolean; + const LogLowRunes: boolean; + const LogMiddleRunes: boolean; + const LogHighRunes: boolean; + const LogLowGems: boolean; + const LogHighGems: boolean; + const SkipLogging: any[]; + const ShowCubingInfo: boolean; + const Cubing: boolean; + const CubeRepair: boolean; + const RepairPercent: number; + const Recipes: any[]; + const MakeRunewords: boolean; + const Runewords: any[][]; + const KeepRunewords: any[]; + const Gamble: boolean; + const GambleItems: any[]; + const GambleGoldStart: number; + const GambleGoldStop: number; + const MiniShopBot: boolean; + const TeleSwitch: boolean; + const MFSwitchPercent: number; + const PrimarySlot: number; + const LogExperience: boolean; + const TownCheck: boolean; + const PingQuit: { + Ping: number; + Duration: number; + }[]; + const PacketShopping: boolean; + const FCR: number; + const FHR: number; + const FBR: number; + const IAS: number; + const PacketCasting: number; + const WaypointMenu: boolean; + const AntiHostile: boolean; + const RandomPrecast: boolean; + const HostileAction: number; + const TownOnHostile: boolean; + const ViperCheck: boolean; + const StopOnDClone: boolean; + const SoJWaitTime: number; + const KillDclone: boolean; + const DCloneQuit: boolean; + const DCloneWaitTime: number; + const FastParty: boolean; + const AutoEquip: boolean; + const ChampionBias: number; + const UseCta: boolean; + const Dodge: boolean; + const DodgeRange: number; + const DodgeHP: number; + const AttackSkill: any[]; + const LowManaSkill: any[]; + const CustomAttack: {}; + const TeleStomp: boolean; + const NoTele: boolean; + const ClearType: boolean; + const ClearPath: boolean; + const BossPriority: boolean; + const MaxAttackCount: number; + const LightningFuryDelay: number; + const UseInnerSight: boolean; + const UseSlowMissiles: boolean; + const UseDecoy: boolean; + const SummonValkyrie: boolean; + const UseTelekinesis: boolean; + const CastStatic: boolean; + const StaticList: any[]; + const UseEnergyShield: boolean; + const UseColdArmor: boolean; + const Golem: number; + const ActiveSummon: boolean; + const Skeletons: number; + const SkeletonMages: number; + const Revives: number; + const ReviveUnstackable: boolean; + const PoisonNovaDelay: number; + const Curse: any[]; + const CustomCurse: any[]; + const ExplodeCorpses: number; + const Redemption: number[]; + const Charge: boolean; + const Vigor: boolean; + const AvoidDolls: boolean; + const FindItem: boolean; + const FindItemSwitch: boolean; + const UseWarcries: boolean; + const Wereform: number; + const SummonRaven: number; + const SummonAnimal: number; + const SummonVine: number; + const SummonSpirit: number; + const UseTraps: boolean; + const Traps: any[]; + const BossTraps: any[]; + const UseFade: boolean; + const UseBoS: boolean; + const UseVenom: boolean; + const UseBladeShield: boolean; + const UseCloakofShadows: boolean; + const AggressiveCloak: boolean; + const SummonShadow: boolean; + const CustomClassAttack: string; + namespace MapMode { + const UseOwnItemFilter: boolean; + } + const MFLeader: boolean; + namespace Mausoleum { + const KillBishibosh: boolean; + const KillBloodRaven: boolean; + const ClearCrypt: boolean; + } + namespace Cows { + const DontMakePortal: boolean; + const JustMakePortal: boolean; + const KillKing: boolean; + } + namespace Tombs { + const KillDuriel: boolean; + } + namespace Eldritch { + const OpenChest: boolean; + const KillSharptooth: boolean; + const KillShenk: boolean; + const KillDacFarren: boolean; + } + namespace Pindleskin { + const UseWaypoint: boolean; + const KillNihlathak: boolean; + const ViperQuit: boolean; + } + namespace Nihlathak { + const ViperQuit_1: boolean; + export { ViperQuit_1 as ViperQuit }; + const UseWaypoint_1: boolean; + export { UseWaypoint_1 as UseWaypoint }; + } + namespace Pit { + const ClearPath_1: boolean; + export { ClearPath_1 as ClearPath }; + export const ClearPit1: boolean; + } + namespace Snapchip { + const ClearIcyCellar: boolean; + } + namespace Frozenstein { + const ClearFrozenRiver: boolean; + } + namespace Rakanishu { + const KillGriswold: boolean; + } + namespace AutoBaal { + const Leader_1: string; + export { Leader_1 as Leader }; + export const FindShrine: boolean; + export const LeechSpot: number[]; + export const LongRangeSupport: boolean; + } + namespace KurastChests { + const LowerKurast: boolean; + const Bazaar: boolean; + const Sewers1: boolean; + const Sewers2: boolean; + } + namespace Countess { + const KillGhosts: boolean; + } + namespace Baal { + const DollQuit: boolean; + const SoulQuit: boolean; + const KillBaal: boolean; + const HotTPMessage: string; + const SafeTPMessage: string; + const BaalMessage: string; + } + namespace BaalAssistant { + const KillNihlathak_1: boolean; + export { KillNihlathak_1 as KillNihlathak }; + export const FastChaos: boolean; + export const Wait: number; + export const Helper: boolean; + export const GetShrine: boolean; + export const GetShrineWaitForHotTP: boolean; + const DollQuit_1: boolean; + export { DollQuit_1 as DollQuit }; + const SoulQuit_1: boolean; + export { SoulQuit_1 as SoulQuit }; + export const SkipTP: boolean; + export const WaitForSafeTP: boolean; + const KillBaal_1: boolean; + export { KillBaal_1 as KillBaal }; + const HotTPMessage_1: any[]; + export { HotTPMessage_1 as HotTPMessage }; + const SafeTPMessage_1: any[]; + export { SafeTPMessage_1 as SafeTPMessage }; + const BaalMessage_1: any[]; + export { BaalMessage_1 as BaalMessage }; + export const NextGameMessage: any[]; + } + namespace BaalHelper { + const Wait_1: number; + export { Wait_1 as Wait }; + const KillNihlathak_2: boolean; + export { KillNihlathak_2 as KillNihlathak }; + const FastChaos_1: boolean; + export { FastChaos_1 as FastChaos }; + const DollQuit_2: boolean; + export { DollQuit_2 as DollQuit }; + const KillBaal_2: boolean; + export { KillBaal_2 as KillBaal }; + const SkipTP_1: boolean; + export { SkipTP_1 as SkipTP }; + } + namespace Corpsefire { + const ClearDen: boolean; + } + namespace Hephasto { + export const ClearRiver: boolean; + const ClearType_1: boolean; + export { ClearType_1 as ClearType }; + } + namespace Diablo { + const WalkClear: boolean; + const Entrance: boolean; + const JustViz: boolean; + const SealLeader: boolean; + const Fast: boolean; + const SealWarning: string; + const EntranceTP: string; + const StarTP: string; + const DiabloMsg: string; + const ClearRadius: number; + const SealOrder: string[]; + } + namespace DiabloHelper { + const Wait_2: number; + export { Wait_2 as Wait }; + const Entrance_1: boolean; + export { Entrance_1 as Entrance }; + export const SkipIfBaal: boolean; + const SkipTP_2: boolean; + export { SkipTP_2 as SkipTP }; + export const OpenSeals: boolean; + export const SafePrecast: boolean; + const ClearRadius_1: number; + export { ClearRadius_1 as ClearRadius }; + const SealOrder_1: string[]; + export { SealOrder_1 as SealOrder }; + export const RecheckSeals: boolean; + } + namespace MFHelper { + const BreakClearLevel: boolean; + } + namespace Wakka { + const Wait_3: number; + export { Wait_3 as Wait }; + export const StopAtLevel: number; + export const StopProfile: boolean; + const SkipIfBaal_1: boolean; + export { SkipIfBaal_1 as SkipIfBaal }; + } + namespace BattleOrders { + const Mode_1: number; + export { Mode_1 as Mode }; + export const Getters: any[]; + export const Idle: boolean; + export const QuitOnFailure: boolean; + export const SkipIfTardy: boolean; + const Wait_4: number; + export { Wait_4 as Wait }; + } + namespace BoBarbHelper { + const Mode_2: number; + export { Mode_2 as Mode }; + export const Wp: number; + } + namespace ControlBot { + export const Bo: boolean; + export namespace Cows_1 { + const MakeCows: boolean; + const GetLeg: boolean; + } + export { Cows_1 as Cows }; + export namespace Chant { + const Enchant: boolean; + const AutoEnchant: boolean; + } + export namespace Wps { + const GiveWps: boolean; + const SecurePortal: boolean; + } + export const EndMessage: string; + export const GameLength: number; + } + namespace IPHunter { + export const IPList: any[]; + const GameLength_1: number; + export { GameLength_1 as GameLength }; + } + namespace Follower { + const Leader_2: string; + export { Leader_2 as Leader }; + } + namespace Mephisto { + const MoatTrick: boolean; + const KillCouncil: boolean; + const TakeRedPortal: boolean; + } + namespace ShopBot { + const ScanIDs: any[]; + const ShopNPC: string; + const CycleDelay: number; + const QuitOnMatch: boolean; + } + namespace Coldworm { + const KillBeetleburst: boolean; + const ClearMaggotLair: boolean; + } + namespace Summoner { + const FireEye: boolean; + } + namespace AncientTunnels { + const OpenChest_1: boolean; + export { OpenChest_1 as OpenChest }; + export const KillDarkElder: boolean; + } + namespace OrgTorch { + const WaitForKeys: boolean; + const WaitTimeout: boolean; + const UseSalvation: boolean; + const GetFade: boolean; + const MakeTorch: boolean; + namespace PreGame { + namespace Thawing { + const Drink: number; + const At: any[]; + } + namespace Antidote { + const Drink_1: number; + export { Drink_1 as Drink }; + const At_1: any[]; + export { At_1 as At }; + } + } + } + namespace Synch { + const WaitFor: any[]; + } + namespace TristramLeech { + const Leader_3: string; + export { Leader_3 as Leader }; + const Helper_1: boolean; + export { Helper_1 as Helper }; + const Wait_5: number; + export { Wait_5 as Wait }; + } + namespace TravincalLeech { + const Leader_4: string; + export { Leader_4 as Leader }; + const Helper_2: boolean; + export { Helper_2 as Helper }; + const Wait_6: number; + export { Wait_6 as Wait }; + } + namespace Tristram { + export const PortalLeech: boolean; + const WalkClear_1: boolean; + export { WalkClear_1 as WalkClear }; + } + namespace Travincal { + const PortalLeech_1: boolean; + export { PortalLeech_1 as PortalLeech }; + } + namespace SkillStat { + const Skills: any[]; + } + namespace Bonesaw { + const ClearDrifterCavern: boolean; + } + namespace ChestMania { + const Act1: any[]; + const Act2: any[]; + const Act3: any[]; + const Act4: any[]; + const Act5: any[]; + } + namespace ClearAnyArea { + const AreaList: any[]; + } + namespace Rusher { + export const WaitPlayerCount: number; + export const Cain: boolean; + export const Radament: boolean; + export const LamEsen: boolean; + export const Izual: boolean; + export const Shenk: boolean; + export const Anya: boolean; + export const HellAncients: boolean; + const GiveWps_1: boolean; + export { GiveWps_1 as GiveWps }; + export const LastRun: string; + } + namespace Rushee { + const Quester: boolean; + const Bumper: boolean; + } + namespace Questing { + const StopProfile_1: boolean; + export { StopProfile_1 as StopProfile }; + } + namespace AutoSkill { + const Enabled_3: boolean; + export { Enabled_3 as Enabled }; + export const Build: any[]; + export const Save: number; + } + namespace AutoStat { + const Enabled_4: boolean; + export { Enabled_4 as Enabled }; + const Build_1: any[]; + export { Build_1 as Build }; + const Save_1: number; + export { Save_1 as Save }; + export const BlockChance: number; + export const UseBulk: boolean; + } + namespace AutoBuild { + const Enabled_5: boolean; + export { Enabled_5 as Enabled }; + export const Template: string; + export const Verbose: boolean; + const DebugMode_1: boolean; + export { DebugMode_1 as DebugMode }; + } + } } export {}; diff --git a/d2bs/kolbot/sdk/types/Cubing.d.ts b/d2bs/kolbot/sdk/types/Cubing.d.ts index 274c6cb7e..30cc3782a 100644 --- a/d2bs/kolbot/sdk/types/Cubing.d.ts +++ b/d2bs/kolbot/sdk/types/Cubing.d.ts @@ -1,23 +1,23 @@ export {}; declare global { - const Cubing: { - init(): void - buildGemList(): void - getCube(): void - buildRecipes(): void - buildLists(): void - clearSubRecipes(): void - update(): void - checkRecipe(recipe: any): void - getRecipeNeeds(index: any): void - checkItem(unit: any): boolean - keepItem(unit: any): boolean - validItem(unit: any, recipe: any): void - doCubing(): void - cursorCheck(): void - openCube(): void - closeCube(): void - emptyCube(): void - makeRevPots(): void - } + namespace Cubing { + function init(): void; + function buildGemList(): void; + function getCube(): void; + function buildRecipes(): void; + function buildLists(): void; + function clearSubRecipes(): void; + function update(): void; + function checkRecipe(recipe: any): void; + function getRecipeNeeds(index: any): void; + function checkItem(unit: any): boolean; + function keepItem(unit: any): boolean; + function validItem(unit: any, recipe: any): void; + function doCubing(): void; + function cursorCheck(): void; + function openCube(): void; + function closeCube(): void; + function emptyCube(): void; + function makeRevPots(): void; + } } diff --git a/d2bs/kolbot/sdk/types/Item.d.ts b/d2bs/kolbot/sdk/types/Item.d.ts index cbb96366b..f4db04829 100644 --- a/d2bs/kolbot/sdk/types/Item.d.ts +++ b/d2bs/kolbot/sdk/types/Item.d.ts @@ -1,22 +1,22 @@ export {}; declare global { - namespace Item { - let useItemLog: boolean; + namespace Item { + let useItemLog: boolean; - function qualityToName(quality : number): string; - function color(unit: ItemUnit, type: boolean): string; - function hasTier(item: ItemUnit): boolean; - function canEquip(item: ItemUnit): boolean; - function equip(item: ItemUnit, bodyLoc: number): boolean; - function getEquippedItem(bodyLoc: number): { classid: number, tier: number }; - function getBodyLoc(item: ItemUnit): number[]; - function autoEquipCheck(item: ItemUnit): boolean; - function autoEquip(): boolean; - function getItemDesc(unit: ItemUnit, logILvl: boolean): string; - function getItemCode(unit: ItemUnit): string; - function getItemSockets(unit: ItemUnit): ItemUnit[]; - function logger(action: string, unit: ItemUnit, text?: string): string; - function logItem(action: string, unit: ItemUnit, keptLine?: string): boolean; - function skipItem(id: number): boolean; - } + function qualityToName(quality : number): string; + function color(unit: ItemUnit, type: boolean): string; + function hasTier(item: ItemUnit): boolean; + function canEquip(item: ItemUnit): boolean; + function equip(item: ItemUnit, bodyLoc: number): boolean; + function getEquippedItem(bodyLoc: number): { classid: number, tier: number }; + function getBodyLoc(item: ItemUnit): number[]; + function autoEquipCheck(item: ItemUnit): boolean; + function autoEquip(): boolean; + function getItemDesc(unit: ItemUnit, logILvl: boolean): string; + function getItemCode(unit: ItemUnit): string; + function getItemSockets(unit: ItemUnit): ItemUnit[]; + function logger(action: string, unit: ItemUnit, text?: string): string; + function logItem(action: string, unit: ItemUnit, keptLine?: string): boolean; + function skipItem(id: number): boolean; + } } diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index c39c6aeba..c7e23aa68 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -1,16 +1,16 @@ export {}; declare global { - const Loader: { - fileList: string[], - scriptList: string[], - scriptIndex: number, - skipTown: string[], + namespace Loader { + const fileList: string[] + const scriptList: string[]; + const scriptIndex: number; + const skipTown: string[]; - init: () => void, - getScripts: () => void, - clone: (obj: any) => void, - copy: (from: any, to: any) => void, - loadScripts: () => void, - scriptName: (offset?: number) => void, - } + function init(): void; + function getScripts(): void; + function clone(obj: any): void; + function copy(from: any, to: any): void; + function loadScripts(): void; + function scriptName(offset?: number): void; + } } diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index f58d21f27..eb5ea2fba 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -1,41 +1,34 @@ export{}; declare global { - namespace Misc { - const screenshotErrors: any; - const errorConsolePrint: any; - const useItemLog: boolean; - - function click(button: number, shift: number, unit: Unit): void; - function click(button: number, shift: number, x: Unit, y: undefined): void; - function inMyParty(name: any): void; - function findPlayer(name: any): void; - function getPlayerUnit(name: any): void; - function getPlayerAct(player: any): void; - function getNearbyPlayerCount(): void; - function getPlayerCount(): void; - function openChest(unit: any): boolean; - function openChestsInArea(area?: any, chestIds?: any): void; - function openChests(range: any): void; - function scanShrines(range: any): void; - function getShrine(unit: any): void; - function getShrinesInArea(area: any, type: any, use: any): void; - function getItemDesc(unit: any): void; - function getItemSockets(unit: any): void; - function itemLogger(action: string, unit: Unit, text?: string | undefined): void; - function logItem(action: string, unit: ItemUnit | undefined, keptLine?: any): void; - function skipItem(id: any): void; - function shapeShift(mode: any): void; - function unShift(): void; - function townCheck(boolean?: boolean): void; - function spy(name: any): void; - function fileAction(path: any, mode: any, msg: any): void; - function errorReport(error: Error | string, script?: string): void; - function debugLog(msg: any): void; - function useMenu(id: number): void; - function clone(obj: any): void; - function copy(from: any): void; - function poll(check: () => T, timeout?: number, sleep?: number): T; - function getUIFlags(excluded?: []): void; - } + namespace Misc { + const screenshotErrors: any; + const errorConsolePrint: any; + const useItemLog: boolean; + + function click(button: number, shift: number, unit: Unit): void; + function click(button: number, shift: number, x: Unit, y: undefined): void; + function inMyParty(name: any): void; + function findPlayer(name: any): void; + function getPlayerUnit(name: any): void; + function getPlayerAct(player: any): void; + function getNearbyPlayerCount(): void; + function getPlayerCount(): void; + function openChest(unit: any): boolean; + function openChestsInArea(area?: any, chestIds?: any): void; + function openChests(range: any): void; + function scanShrines(range: any): void; + function getShrine(unit: any): void; + function getShrinesInArea(area: any, type: any, use: any): void; + function skipItem(id: any): void; + function shapeShift(mode: any): void; + function unShift(): void; + function townCheck(boolean?: boolean): void; + function spy(name: any): void; + function errorReport(error: Error | string, script?: string): void; + function debugLog(msg: any): void; + function useMenu(id: number): void; + function poll(check: () => T, timeout?: number, sleep?: number): T; + function getUIFlags(excluded?: []): void; + } } diff --git a/d2bs/kolbot/sdk/types/NTIP.d.ts b/d2bs/kolbot/sdk/types/NTIP.d.ts index 722adef2d..b722921f2 100644 --- a/d2bs/kolbot/sdk/types/NTIP.d.ts +++ b/d2bs/kolbot/sdk/types/NTIP.d.ts @@ -1,7 +1,7 @@ export {}; declare global { - const NTIP: { - OpenFile(string, boolean); - CheckItem(unit: Unit, entryList?: [] | false, verbose?: boolean) - } + namespace NTIP { + function OpenFile(filepath: string, notify: boolean): void; + function CheckItem(unit: Unit, entryList?: [] | false, verbose?: boolean): void; + } } diff --git a/d2bs/kolbot/sdk/types/OOG.d.ts b/d2bs/kolbot/sdk/types/OOG.d.ts index 881ce0c9f..bbd9afef7 100644 --- a/d2bs/kolbot/sdk/types/OOG.d.ts +++ b/d2bs/kolbot/sdk/types/OOG.d.ts @@ -1,67 +1,75 @@ -export {}; +// @ts-nocheck declare global { - export const DataFile: { - create(): void - getObj(): void - getStats(): any - updateStats(arg: any, value?: any): void - } + namespace DataFile { + function create(): void + function getObj(): void + function getStats(): any + function updateStats(arg: any, value?: any): void + } + + namespace FileAction { + function read(path: string): string; + function write(path: string, msg: string): boolean; + function append(path: string, msg: string): boolean; + function parse(path: string): any; + } - export const D2Bot: { - handle: number, - init(): void - sendMessage(handle: any, mode: any, msg: any): void - printToConsole(msg: string, color?: number, tooltip?: undefined, trigger?: undefined): void - printToItemLog(itemObj: any): void - uploadItem(itemObj: any): void - writeToFile(filename: any, msg: any): void - postToIRC(ircProfile: any, recepient: any, msg: any): void - ircEvent(mode: any): void - notify(msg: any): void - saveItem(itemObj: any): void - updateStatus(msg: any): void - updateRuns(): void - updateChickens(): void - updateDeaths(): void - requestGameInfo(): void - restart(keySwap?: boolean): void - CDKeyInUse(): void - CDKeyDisabled(): void - CDKeyRD(): void - stop(profile?: undefined, release?: undefined): void - start(profile: any): void - startSchedule(profile: any): void - stopSchedule(profile: any): void - updateCount(): void - shoutGlobal(msg: any, mode: any): void - heartBeat(): void - sendWinMsg(wparam: any, lparam: any): void - ingame(): void - joinMe(profile: any, gameName: any, gameCount: any, gamePass: any, isUp: any): void - requestGame(profile: any): void - getProfile(): void - setProfile(account: any, password: any, character: any, difficulty: any, realm: any, infoTag: any, gamePath: any): void - setTag(tag: any): void - store(info: any): void - retrieve(): void - remove(): void - } + export const D2Bot: { + handle: number, + init(): void + sendMessage(handle: any, mode: any, msg: any): void + printToConsole(msg: string, color?: number, tooltip?: undefined, trigger?: undefined): void + printToItemLog(itemObj: any): void + uploadItem(itemObj: any): void + writeToFile(filename: any, msg: any): void + postToIRC(ircProfile: any, recepient: any, msg: any): void + ircEvent(mode: any): void + notify(msg: any): void + saveItem(itemObj: any): void + updateStatus(msg: any): void + updateRuns(): void + updateChickens(): void + updateDeaths(): void + requestGameInfo(): void + restart(keySwap?: boolean): void + CDKeyInUse(): void + CDKeyDisabled(): void + CDKeyRD(): void + stop(profile?: undefined, release?: undefined): void + start(profile: any): void + startSchedule(profile: any): void + stopSchedule(profile: any): void + updateCount(): void + shoutGlobal(msg: any, mode: any): void + heartBeat(): void + sendWinMsg(wparam: any, lparam: any): void + ingame(): void + joinMe(profile: any, gameName: any, gameCount: any, gamePass: any, isUp: any): void + requestGame(profile: any): void + getProfile(): void + setProfile(account: any, password: any, character: any, difficulty: any, realm: any, infoTag: any, gamePath: any): void + setTag(tag: any): void + store(info: any): void + retrieve(): void + remove(): void + } - export const ControlAction: { - timeoutDelay(text: any, time: any, stopfunc?: any, arg?: any):void - click(type: any, x: any, y: any, xsize: any, ysize: any):void - setText(type: any, x: any, y: any, xsize: any, ysize: any, text: any):void - getText(type: any, x: any, y: any, xsize: any, ysize: any):string - joinChannel(channel: any):void - createGame(name: any, pass: any, diff: any, delay: any):void - clickRealm(realm: 0|1|2|3):void - loginAccount(info: any):void - makeAccount(info: any):void - findCharacter(info: any):void - getCharacters():void - getPosition():void - loginCharacter(info: any, startFromTop?: boolean):void - makeCharacter(info: any):void - getGameList():void - } + export const ControlAction: { + timeoutDelay(text: any, time: any, stopfunc?: any, arg?: any):void + click(type: any, x: any, y: any, xsize: any, ysize: any):void + setText(type: any, x: any, y: any, xsize: any, ysize: any, text: any):void + getText(type: any, x: any, y: any, xsize: any, ysize: any):string + joinChannel(channel: any):void + createGame(name: any, pass: any, diff: any, delay: any):void + clickRealm(realm: 0|1|2|3):void + loginAccount(info: any):void + makeAccount(info: any):void + findCharacter(info: any):void + getCharacters():void + getPosition():void + loginCharacter(info: any, startFromTop?: boolean):void + makeCharacter(info: any):void + getGameList():void + } } +export {}; diff --git a/d2bs/kolbot/sdk/types/Pather.d.ts b/d2bs/kolbot/sdk/types/Pather.d.ts index e6a8da194..fc4db1c59 100644 --- a/d2bs/kolbot/sdk/types/Pather.d.ts +++ b/d2bs/kolbot/sdk/types/Pather.d.ts @@ -1,37 +1,37 @@ export {}; declare global { - namespace Pather { - const wpAreas: number[]; - let walkDistance: number; - let teleDistance: number; - let teleport: boolean; - const cancelFlags: number[]; - let recursion: boolean; - let lastPortalTick: 0; - function getWalkDistance(x: number, y: number, area?: number, xx?: number, yy?: number, reductionType?: 0 | 1 | 2, radius?: number): number; - function useTeleport(): boolean; - function moveTo(x: number, y: number, retry?: number | undefined, clearPath?: boolean | undefined, pop?: boolean | undefined): boolean; - function teleportTo(x: any, y: any, maxRange?: any): void; - function walkTo(x: any, y: any, minDist?: number | undefined): boolean; - function openDoors(x: any, y: any): boolean; - function moveToUnit(unit: PathNode, offX?: undefined, offY?: undefined, clearPath?: undefined, pop?: undefined): boolean; - function moveToPreset(area: any, unitType: any, unitId: any, offX?: any, offY?: any, clearPath?: any, pop?: any): boolean; - function moveToExit(targetArea: any, use?: any, clearPath?: any): void; - function getNearestRoom(area: any): void; - function openExit(targetArea: any): void; - function openUnit(type: any, id: any): void; - function useUnit(type: any, id: any, targetArea: any): boolean; - function useWaypoint(targetArea: number | null, check?: boolean): boolean; - function makePortal(use?: boolean | undefined): void; - function usePortal(targetArea?: number | null, owner?: string | undefined, unit?: undefined): boolean; - function getPortal(targetArea: any, owner?: any): ObjectUnit | false; - function getNearestWalkable(x: any, y: any, range: any, step: any, coll: any, size?: any): [number, number] | false; - function checkSpot(x: any, y: any, coll: any, cacheOnly: any, size: any): void; - function accessToAct(act: number): boolean; - function getWP(area: any, clearPath?: any): boolean; - function journeyTo(area: any): boolean; - function plotCourse(dest: any, src: any): false|{course:number[]}; - function areasConnected(src: any, dest: any): void; - function getAreaName(area: number): void; - } + namespace Pather { + const wpAreas: number[]; + let walkDistance: number; + let teleDistance: number; + let teleport: boolean; + const cancelFlags: number[]; + let recursion: boolean; + let lastPortalTick: 0; + function getWalkDistance(x: number, y: number, area?: number, xx?: number, yy?: number, reductionType?: 0 | 1 | 2, radius?: number): number; + function useTeleport(): boolean; + function moveTo(x: number, y: number, retry?: number | undefined, clearPath?: boolean | undefined, pop?: boolean | undefined): boolean; + function teleportTo(x: any, y: any, maxRange?: any): void; + function walkTo(x: any, y: any, minDist?: number | undefined): boolean; + function openDoors(x: any, y: any): boolean; + function moveToUnit(unit: PathNode, offX?: undefined, offY?: undefined, clearPath?: undefined, pop?: undefined): boolean; + function moveToPreset(area: any, unitType: any, unitId: any, offX?: any, offY?: any, clearPath?: any, pop?: any): boolean; + function moveToExit(targetArea: any, use?: any, clearPath?: any): void; + function getNearestRoom(area: any): void; + function openExit(targetArea: any): void; + function openUnit(type: any, id: any): void; + function useUnit(type: any, id: any, targetArea: any): boolean; + function useWaypoint(targetArea: number | null, check?: boolean): boolean; + function makePortal(use?: boolean | undefined): void; + function usePortal(targetArea?: number | null, owner?: string | undefined, unit?: undefined): boolean; + function getPortal(targetArea: any, owner?: any): ObjectUnit | false; + function getNearestWalkable(x: any, y: any, range: any, step: any, coll: any, size?: any): [number, number] | false; + function checkSpot(x: any, y: any, coll: any, cacheOnly: any, size: any): void; + function accessToAct(act: number): boolean; + function getWP(area: any, clearPath?: any): boolean; + function journeyTo(area: any): boolean; + function plotCourse(dest: any, src: any): false|{course:number[]}; + function areasConnected(src: any, dest: any): void; + function getAreaName(area: number): void; + } } diff --git a/d2bs/kolbot/sdk/types/Pickit.d.ts b/d2bs/kolbot/sdk/types/Pickit.d.ts index 8a600b2f0..a8df9e378 100644 --- a/d2bs/kolbot/sdk/types/Pickit.d.ts +++ b/d2bs/kolbot/sdk/types/Pickit.d.ts @@ -1,34 +1,38 @@ export {}; declare global { - type PickitResult = { - UNID: -1, - UNWANTED: 0, - WANTED: 1, - CUBING: 2, - RUNEWORD: 3, - TRASH: 4, - CRAFTING: 5, - UTILITY: 6 - }; - namespace Pickit { - const gidList: number[]; - let invoLocked: boolean; - let beltSize: 1 | 2 | 3 | 4; - const ignoreLog: number[]; // Ignored item types for item logging - const Result: PickitResult; - const tkable: number[]; - const essentials: number[]; + type PickitResult = { + UNID: -1, + UNWANTED: 0, + WANTED: 1, + CUBING: 2, + RUNEWORD: 3, + TRASH: 4, + CRAFTING: 5, + UTILITY: 6 + }; + namespace Pickit { + const gidList: number[]; + let invoLocked: boolean; + let beltSize: 1 | 2 | 3 | 4; + const ignoreLog: number[]; // Ignored item types for item logging + const Result: PickitResult; + const tkable: number[]; + const essentials: number[]; - function init(notify: any): void; - function itemEvent(gid?: number, mode?: number, code?: number, global?: number): void; - function sortItems(unitA: Unit, unitB: Unit): number; - function sortFastPickItems(unitA: Unit, unitB: Unit): number; - function checkBelt(): boolean; - function canPick(unit: ItemUnit): boolean; - function checkItem(unit: ItemUnit): { result: PickitResult, line: null | number }; - function pickItem(unit: ItemUnit, status?: PickitResult, keptLine?: any, retry?: number): { result: PickitResult, line: string | null }; - function canMakeRoom(): boolean; - function pickItems(range?: number): boolean; - function fastPick(): boolean; - } + function init(notify: any): void; + function itemEvent(gid?: number, mode?: number, code?: number, global?: number): void; + function sortItems(unitA: Unit, unitB: Unit): number; + function sortFastPickItems(unitA: Unit, unitB: Unit): number; + function checkBelt(): boolean; + function canPick(unit: ItemUnit): boolean; + function checkItem(unit: ItemUnit): { result: PickitResult, line: null | number }; + function pickItem( + unit: ItemUnit, + status?: PickitResult, + keptLine?: any, retry?: number + ): { result: PickitResult, line: string | null }; + function canMakeRoom(): boolean; + function pickItems(range?: number): boolean; + function fastPick(): boolean; + } } diff --git a/d2bs/kolbot/sdk/types/Runewords.d.ts b/d2bs/kolbot/sdk/types/Runewords.d.ts index ab9cd56c3..418657d8d 100644 --- a/d2bs/kolbot/sdk/types/Runewords.d.ts +++ b/d2bs/kolbot/sdk/types/Runewords.d.ts @@ -1,122 +1,122 @@ export {}; declare global { - /** - * @property {string} name - The name of the runeword. - * @property {number} sockets - The number of sockets required for the item. - * @property {Array} runes - Array of rune IDs required for the runeword. - * @property {Array} itemTypes - Array of item type IDs the runeword can be applied to. - * @method ladderRestricted - Returns true if we are unable to make the runeword because we are not on ladder. - */ - interface runeword { - name: string; - sockets: number; - runes: number[]; - itemTypes: number[]; - _ladder: boolean; - reqLvl: number; - ladderRestricted: () => boolean; - } + /** + * @property {string} name - The name of the runeword. + * @property {number} sockets - The number of sockets required for the item. + * @property {Array} runes - Array of rune IDs required for the runeword. + * @property {Array} itemTypes - Array of item type IDs the runeword can be applied to. + * @method ladderRestricted - Returns true if we are unable to make the runeword because we are not on ladder. + */ + interface runeword { + name: string; + sockets: number; + runes: number[]; + itemTypes: number[]; + _ladder: boolean; + reqLvl: number; + ladderRestricted: () => boolean; + } - namespace Runeword { - const AncientsPledge: runeword; - const Black: runeword; - const Fury: runeword; - const HolyThunder: runeword; - const Honor: runeword; - const KingsGrace: runeword; - const Leaf: runeword; - const Lionheart: runeword; - const Lore: runeword; - const Malice: runeword; - const Melody: runeword; - const Memory: runeword; - const Nadir: runeword; - const Radiance: runeword; - const Rhyme: runeword; - const Silence: runeword; - const Smoke: runeword; - const Stealth: runeword; - const Steel: runeword; - const Strength: runeword; - const Venom: runeword; - const Wealth: runeword; - const White: runeword; - const Zephyr: runeword; - const Beast: runeword; - const Bramble: runeword; - const BreathoftheDying: runeword; - const CallToArms: runeword; - const ChainsofHonor: runeword; - const Chaos: runeword; - const CrescentMoon: runeword; - const Delirium: runeword; - const Doom: runeword; - const Duress: runeword; - const Enigma: runeword; - const Eternity: runeword; - const Exile: runeword; - const Famine: runeword; - const Gloom: runeword; - const HandofJustice: runeword; - const HeartoftheOak: runeword; - const Kingslayer: runeword; - const Passion: runeword; - const Prudence: runeword; - const Sanctuary: runeword; - const Splendor: runeword; - const Stone: runeword; - const Wind: runeword; - const Brand: runeword; - const Death: runeword; - const Destruction: runeword; - const Dragon: runeword; - const Dream: runeword; - const Edge: runeword; - const Faith: runeword; - const Fortitude: runeword; - const Grief: runeword; - const Harmony: runeword; - const Ice: runeword; - const Infinity: runeword; - const Insight: runeword; - const LastWish: runeword; - const Lawbringer: runeword; - const Oath: runeword; - const Obedience: runeword; - const Phoenix: runeword; - const Pride: runeword; - const Rift: runeword; - const Spirit: runeword; - const VoiceofReason: runeword; - const Wrath: runeword; - const Bone: runeword; - const Enlightenment: runeword; - const Myth: runeword; - const Peace: runeword; - const Principle: runeword; - const Rain: runeword; - const Treachery: runeword; - const Test: runeword; + namespace Runeword { + const AncientsPledge: runeword; + const Black: runeword; + const Fury: runeword; + const HolyThunder: runeword; + const Honor: runeword; + const KingsGrace: runeword; + const Leaf: runeword; + const Lionheart: runeword; + const Lore: runeword; + const Malice: runeword; + const Melody: runeword; + const Memory: runeword; + const Nadir: runeword; + const Radiance: runeword; + const Rhyme: runeword; + const Silence: runeword; + const Smoke: runeword; + const Stealth: runeword; + const Steel: runeword; + const Strength: runeword; + const Venom: runeword; + const Wealth: runeword; + const White: runeword; + const Zephyr: runeword; + const Beast: runeword; + const Bramble: runeword; + const BreathoftheDying: runeword; + const CallToArms: runeword; + const ChainsofHonor: runeword; + const Chaos: runeword; + const CrescentMoon: runeword; + const Delirium: runeword; + const Doom: runeword; + const Duress: runeword; + const Enigma: runeword; + const Eternity: runeword; + const Exile: runeword; + const Famine: runeword; + const Gloom: runeword; + const HandofJustice: runeword; + const HeartoftheOak: runeword; + const Kingslayer: runeword; + const Passion: runeword; + const Prudence: runeword; + const Sanctuary: runeword; + const Splendor: runeword; + const Stone: runeword; + const Wind: runeword; + const Brand: runeword; + const Death: runeword; + const Destruction: runeword; + const Dragon: runeword; + const Dream: runeword; + const Edge: runeword; + const Faith: runeword; + const Fortitude: runeword; + const Grief: runeword; + const Harmony: runeword; + const Ice: runeword; + const Infinity: runeword; + const Insight: runeword; + const LastWish: runeword; + const Lawbringer: runeword; + const Oath: runeword; + const Obedience: runeword; + const Phoenix: runeword; + const Pride: runeword; + const Rift: runeword; + const Spirit: runeword; + const VoiceofReason: runeword; + const Wrath: runeword; + const Bone: runeword; + const Enlightenment: runeword; + const Myth: runeword; + const Peace: runeword; + const Principle: runeword; + const Rain: runeword; + const Treachery: runeword; + const Test: runeword; - function findByName(name: string): runeword | undefined; + function findByName(name: string): runeword | undefined; function findByRune(rune: number): runeword[]; function findByType(type: number): runeword[]; function addRuneword(name: string, sockets: number, runes: number | number[], itemTypes: number | number[]): runeword | boolean; - } + } - namespace Runewords { - function init(): void - function validItem(item: any): void - function buildLists(): void - function update(classid: any, gid: any): void - function checkRunewords(): void - function checkItem(unit: any): boolean - function keepItem(unit: any): boolean - function getBase(runeword: any, base: any, ethFlag: any, reroll: any): void - function socketItem(base: any, rune: any): void - function getScroll(): void - function makeRunewords(): void - function rerollRunewords(): void - } + namespace Runewords { + function init(): void + function validItem(item: any): void + function buildLists(): void + function update(classid: any, gid: any): void + function checkRunewords(): void + function checkItem(unit: any): boolean + function keepItem(unit: any): boolean + function getBase(runeword: any, base: any, ethFlag: any, reroll: any): void + function socketItem(base: any, rune: any): void + function getScroll(): void + function makeRunewords(): void + function rerollRunewords(): void + } } diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts index 78e38382b..cdeb9b00a 100644 --- a/d2bs/kolbot/sdk/types/Skill.d.ts +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -43,6 +43,7 @@ declare global { const missileSkills: number[]; const charges: any[]; + function get (skillId: number): SkillDataInfo; function getClassSkillRange(classid?: number): [number, number]; function init(): void; function canUse(skillId: number): boolean; diff --git a/d2bs/kolbot/sdk/types/Storage.d.ts b/d2bs/kolbot/sdk/types/Storage.d.ts index 85629c831..b45158e1a 100644 --- a/d2bs/kolbot/sdk/types/Storage.d.ts +++ b/d2bs/kolbot/sdk/types/Storage.d.ts @@ -1,95 +1,95 @@ // @ts-nocheck export {}; declare global { - function Container(name: string, width: number, height: number, location: number): void; - interface Container { + function Container(name: string, width: number, height: number, location: number): void; + interface Container { constructor(name: string, width: number, height: number, location: number): Container; /** The name of the container */ - name: string; - /** The width of the container */ - width: number; - /** The height of the container */ - height: number; - /** The location of the container */ - location: number; - /** A 2D array to store the containers items */ - buffer: number[][]; - /** A list of the items in the container */ - itemList: ItemUnit[]; - /** The number of open positions in the container */ - openPositions: number; + name: string; + /** The width of the container */ + width: number; + /** The height of the container */ + height: number; + /** The location of the container */ + location: number; + /** A 2D array to store the containers items */ + buffer: number[][]; + /** A list of the items in the container */ + itemList: ItemUnit[]; + /** The number of open positions in the container */ + openPositions: number; - /** - * A function that marks an item in the container's buffer and adds it to the item list. - * @param item - */ - Mark(item: ItemUnit): boolean; - - /** - * A function that checks if an item is locked in the container. - * @param item - * @param baseRef - */ - IsLocked(item: ItemUnit, baseRef: number[][]): boolean - - /** - * A function that resets the container's buffer and item list. - */ - Reset(): void - - /** - * A function that checks if an item can fit in the container. - * @param item - */ - CanFit(item: ItemUnit): boolean - - /** - * A function that finds a spot for an item in the container. - * @param item - */ - FindSpot(item: ItemUnit): PathNode | false - - /** - * A function that moves an item to a location in a container - * @param item - */ - MoveTo(item: ItemUnit): boolean - - /** - * A function that dumps the information about the container to the console - */ - Dump(): void - - /** - * A function that returns the amount of space used in this container - */ - UsedSpacePercent(): void - - /** - * A function the returns an item list in comparison to a given reference array - * @param baseRef - */ - Compare(baseRef: number[][]): ItemUnit[] | false - - /** - * returns a string representation of the source object - * @deprecated - */ - toSource(): string - } + /** + * A function that marks an item in the container's buffer and adds it to the item list. + * @param item + */ + Mark(item: ItemUnit): boolean; + + /** + * A function that checks if an item is locked in the container. + * @param item + * @param baseRef + */ + IsLocked(item: ItemUnit, baseRef: number[][]): boolean + + /** + * A function that resets the container's buffer and item list. + */ + Reset(): void + + /** + * A function that checks if an item can fit in the container. + * @param item + */ + CanFit(item: ItemUnit): boolean + + /** + * A function that finds a spot for an item in the container. + * @param item + */ + FindSpot(item: ItemUnit): PathNode | false + + /** + * A function that moves an item to a location in a container + * @param item + */ + MoveTo(item: ItemUnit): boolean + + /** + * A function that dumps the information about the container to the console + */ + Dump(): void + + /** + * A function that returns the amount of space used in this container + */ + UsedSpacePercent(): void + + /** + * A function the returns an item list in comparison to a given reference array + * @param baseRef + */ + Compare(baseRef: number[][]): ItemUnit[] | false + + /** + * returns a string representation of the source object + * @deprecated + */ + toSource(): string + } - type storage = { - StashY: 4 | 8 | 10; - Inventory: Container; - TradeScreen: Container; - Stash: Container; - Belt: Container; - Cube: Container; - InvRef: number[]; + type storage = { + StashY: 4 | 8 | 10; + Inventory: Container; + TradeScreen: Container; + Stash: Container; + Belt: Container; + Cube: Container; + InvRef: number[]; - BeltSize(): 1 | 2 | 3 | 4; - Reload(): void; - Init(): void; - } - const Storage: storage; + BeltSize(): 1 | 2 | 3 | 4; + Reload(): void; + Init(): void; + } + const Storage: storage; } diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts index 05fc86e0c..42a83acc1 100644 --- a/d2bs/kolbot/sdk/types/Town.d.ts +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -1,114 +1,116 @@ // @ts-nocheck declare global { - type NPC = string; - export namespace NPC { - function getAct(name: string): number[]; - const Akara: string; - const Gheed: string; - const Charsi: string; - const Kashya: string; - const Warriv: string; - const Fara: string; - const Drognan: string; - const Elzix: string; - const Greiz: string; - const Lysander: string; - const Jerhyn: string; - const Meshif: string; - const Atma: string; - const Ormus: string; - const Alkor: string; - const Hratli: string; - const Asheara: string; - const Jamella: string; - const Halbu: string; - const Tyrael: string; - const Malah: string; - const Anya: string; - const Larzuk: string; - const Qual_Kehk: string; - const Nihlathak: string; - const Cain: string; - } - export namespace Town { - const telekinesis: boolean; - const sellTimer: number; - export namespace lastInteractedNPC { - const unit: Unit; - const tick: number; - function set(npc: Unit): void; - function get(): any; - function reset(): void; - } - const tasks: { - Heal: NPC; - Shop: NPC; - Gamble: NPC; - Repair: NPC; - Merc: NPC; - Key: NPC; - CainID: NPC; - }[]; - const ignoredItemTypes: any[]; - function needPotions(): boolean; - function doChores(repair?: boolean): boolean; - function npcInteract(name?: string, cancel?: boolean): boolean | Unit; - function checkQuestItems(): void; - function getTpTool(): ItemUnit; - function getIdTool(): ItemUnit; - function canTpToTown(): boolean; - function initNPC(task?: string, reason?: string): boolean | Unit; - function heal(): boolean; - function needHealing(): boolean; - function buyPotions(): boolean; - function shiftCheck(col: number, beltSize: 0 | 2 | 1 | 4 | 3): boolean; - function checkColumns(beltSize: 0 | 2 | 1 | 4 | 3): [number, number, number, number]; - function getPotion(npc: Unit, type: "hp" | "mp", highestPot?: 2 | 1 | 4 | 3 | 5): boolean | ItemUnit; - function fillTome(classid: number): boolean; - function checkScrolls(id: number): number; - function identify(): boolean; - function cainID(): boolean; - function fieldID(): boolean; - function getUnids(): false | ItemUnit[]; - function identifyItem(unit: ItemUnit, tome: ItemUnit, packetID?: boolean): boolean; - function shopItems(): boolean; - const gambleIds: any[]; - function gamble(): boolean; - function needGamble(): boolean; - function getGambledItem(list?: any[]): false | ItemUnit; - function buyPots(quantity?: number, type?: string | number, drink?: boolean, force?: boolean, npc?: Unit): boolean; - function drinkPots(type?: string | number, log?: boolean): { - potName: string; - quantity: number; - }; - function buyKeys(): boolean; - function checkKeys(): number; - function needKeys(): boolean; - function wantKeys(): boolean; - function repairIngredientCheck(item: ItemUnit): boolean; - function cubeRepair(): boolean; - function cubeRepairItem(item: ItemUnit): boolean; - function repair(force?: boolean): boolean; - function needRepair(): string[]; - function getItemsForRepair(repairPercent: number, chargedItems: boolean): ItemUnit[]; - function reviveMerc(): boolean; - function needMerc(): boolean; - function canStash(item: ItemUnit): boolean; - function stash(stashGold?: boolean): boolean; - function needStash(): boolean; - function openStash(): boolean; - function getCorpse(): boolean; - function checkShard(): boolean; - function clearBelt(): boolean; - function clearScrolls(): boolean; - function clearInventory(): boolean; - const act: {}[]; - function initialize(): boolean; - function getDistance(spot?: string): number; - function move(spot?: string, allowTK?: boolean): boolean; - function moveToSpot(spot?: string, allowTK?: boolean): boolean; - function goToTown(act?: 2 | 1 | 4 | 3 | 5, wpmenu?: boolean): boolean; - function visitTown(repair?: boolean): boolean; - } + type NPC = string; + export namespace NPC { + function getAct(name: string): number[]; + const Akara: string; + const Gheed: string; + const Charsi: string; + const Kashya: string; + const Warriv: string; + const Fara: string; + const Drognan: string; + const Elzix: string; + const Greiz: string; + const Lysander: string; + const Jerhyn: string; + const Meshif: string; + const Atma: string; + const Ormus: string; + const Alkor: string; + const Hratli: string; + const Asheara: string; + const Jamella: string; + const Halbu: string; + const Tyrael: string; + const Malah: string; + const Anya: string; + const Larzuk: string; + const Qual_Kehk: string; + const Nihlathak: string; + const Cain: string; + } + export namespace Town { + let telekinesis: boolean; + let sellTimer: number; + let lastChores: number; + + export namespace lastInteractedNPC { + const unit: Unit; + const tick: number; + function set(npc: Unit): void; + function get(): any; + function reset(): void; + } + const tasks: { + Heal: NPC; + Shop: NPC; + Gamble: NPC; + Repair: NPC; + Merc: NPC; + Key: NPC; + CainID: NPC; + }[]; + const ignoredItemTypes: any[]; + function needPotions(): boolean; + function doChores(repair?: boolean): boolean; + function npcInteract(name?: string, cancel?: boolean): boolean | Unit; + function checkQuestItems(): void; + function getTpTool(): ItemUnit; + function getIdTool(): ItemUnit; + function canTpToTown(): boolean; + function initNPC(task?: string, reason?: string): boolean | Unit; + function heal(): boolean; + function needHealing(): boolean; + function buyPotions(): boolean; + function shiftCheck(col: number, beltSize: 0 | 2 | 1 | 4 | 3): boolean; + function checkColumns(beltSize: 0 | 2 | 1 | 4 | 3): [number, number, number, number]; + function getPotion(npc: Unit, type: "hp" | "mp", highestPot?: 2 | 1 | 4 | 3 | 5): boolean | ItemUnit; + function fillTome(classid: number): boolean; + function checkScrolls(id: number): number; + function identify(): boolean; + function cainID(): boolean; + function fieldID(): boolean; + function getUnids(): false | ItemUnit[]; + function identifyItem(unit: ItemUnit, tome: ItemUnit, packetID?: boolean): boolean; + function shopItems(): boolean; + const gambleIds: any[]; + function gamble(): boolean; + function needGamble(): boolean; + function getGambledItem(list?: any[]): false | ItemUnit; + function buyPots(quantity?: number, type?: string | number, drink?: boolean, force?: boolean, npc?: Unit): boolean; + function drinkPots(type?: string | number, log?: boolean): { + potName: string; + quantity: number; + }; + function buyKeys(): boolean; + function checkKeys(): number; + function needKeys(): boolean; + function wantKeys(): boolean; + function repairIngredientCheck(item: ItemUnit): boolean; + function cubeRepair(): boolean; + function cubeRepairItem(item: ItemUnit): boolean; + function repair(force?: boolean): boolean; + function needRepair(): string[]; + function getItemsForRepair(repairPercent: number, chargedItems: boolean): ItemUnit[]; + function reviveMerc(): boolean; + function needMerc(): boolean; + function canStash(item: ItemUnit): boolean; + function stash(stashGold?: boolean): boolean; + function needStash(): boolean; + function openStash(): boolean; + function getCorpse(): boolean; + function checkShard(): boolean; + function clearBelt(): boolean; + function clearScrolls(): boolean; + function clearInventory(): boolean; + const act: {}[]; + function initialize(): boolean; + function getDistance(spot?: string): number; + function move(spot?: string, allowTK?: boolean): boolean; + function moveToSpot(spot?: string, allowTK?: boolean): boolean; + function goToTown(act?: 2 | 1 | 4 | 3 | 5, wpmenu?: boolean): boolean; + function visitTown(repair?: boolean): boolean; + } } export {}; diff --git a/d2bs/kolbot/sdk/types/Util.d.ts b/d2bs/kolbot/sdk/types/Util.d.ts index ebeede7b6..987b2a193 100644 --- a/d2bs/kolbot/sdk/types/Util.d.ts +++ b/d2bs/kolbot/sdk/types/Util.d.ts @@ -1,133 +1,133 @@ // @ts-nocheck declare global { - /** - * @constructor - * @description new PacketBuilder() - create new packet object - * @example (Spoof 'reassign player' packet to client): - * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); - * @example (Spoof 'player move' packet to server): - * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); - * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` - * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` - */ - function PacketBuilder(): void; - class PacketBuilder { - /** @description size = 4 */ - float(a: number): this - /** @description size = 4 */ - dword(a: number): this - /** @description size = 2 */ - word(a: number): this - /** @description size = 1 */ - byte(a: number): this - string(a: any): this - send(): this - spoof(): this - } + /** + * @constructor + * @description new PacketBuilder() - create new packet object + * @example (Spoof 'reassign player' packet to client): + * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); + * @example (Spoof 'player move' packet to server): + * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); + * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` + * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` + */ + function PacketBuilder(): void; + class PacketBuilder { + /** @description size = 4 */ + float(a: number): this + /** @description size = 4 */ + dword(a: number): this + /** @description size = 2 */ + word(a: number): this + /** @description size = 1 */ + byte(a: number): this + string(a: any): this + send(): this + spoof(): this + } - /** - * @class - * @classdesc A class for creating and sending copy data packets. - * @property {number} _mode - Defaults to 0, works for most D2Bot functions - * @property {number | string} _handle - Defaults to value of D2Bot.handle, works for any D2Bot - * functions that act on ourselves - * @example Request a game from "scl-sorc-001" profile - * new CopyData().handle("scl-sorc-001").mode(3).send(); - * @example Start mule profile "mule" - * new CopyData().data("start", ["mule"]).send(); - */ - function CopyData(): void; - class CopyData { - /** - * @private - * @type {string | number} - The handle to send the copy data to. - */ - private _handle: string | number; + /** + * @class + * @classdesc A class for creating and sending copy data packets. + * @property {number} _mode - Defaults to 0, works for most D2Bot functions + * @property {number | string} _handle - Defaults to value of D2Bot.handle, works for any D2Bot + * functions that act on ourselves + * @example Request a game from "scl-sorc-001" profile + * new CopyData().handle("scl-sorc-001").mode(3).send(); + * @example Start mule profile "mule" + * new CopyData().data("start", ["mule"]).send(); + */ + function CopyData(): void; + class CopyData { + /** + * @private + * @type {string | number} - The handle to send the copy data to. + */ + private _handle: string | number; - /** - * @private - * @type {number} - The mode of the copy data packet. - */ - private _mode: number; + /** + * @private + * @type {number} - The mode of the copy data packet. + */ + private _mode: number; - /** - * @private - * @type {string} - The data to send in the copy data - */ - private _data: string; + /** + * @private + * @type {string} - The data to send in the copy data + */ + private _data: string; - /** - * - D2Bot.handle is for any functions that act on ourselves - * - Otherwise it is the D2Bot# profile name of the profile to act upon - * @param {string | number} handle - The handle or profile to send the copy data to. - */ - handle(handle: string | number): CopyData; + /** + * - D2Bot.handle is for any functions that act on ourselves + * - Otherwise it is the D2Bot# profile name of the profile to act upon + * @param {string | number} handle - The handle or profile to send the copy data to. + */ + handle(handle: string | number): CopyData; - /** - * - 0 is for most functions, and the default value set - * - 1 is for joinMe - * - 3 is for requestGame - * - 0xbbbb is for heartBeat - * @param {number} mode - The mode of the copy data packet. - */ - mode(mode: number): CopyData; + /** + * - 0 is for most functions, and the default value set + * - 1 is for joinMe + * - 3 is for requestGame + * - 0xbbbb is for heartBeat + * @param {number} mode - The mode of the copy data packet. + */ + mode(mode: number): CopyData; - /** - * @param {string} [func] - The function to call from D2Bot# - * @param {string[]} [args] - The additonal info needed for the function call - */ - data(func?: string, args?: string[]): CopyData; - send(): void; - } + /** + * @param {string} [func] - The function to call from D2Bot# + * @param {string[]} [args] - The additonal info needed for the function call + */ + data(func?: string, args?: string[]): CopyData; + send(): void; + } - function getThreads(): Script[]; - function getUnits(type: MonsterType, name?: string, mode?: number, unitId?: number): Monster[]; - function getUnits(type: MonsterType, classId?: number, mode?: number, unitId?: number): Monster[]; - function getUnits(type: ObjectType, name?: string, mode?: number, unitId?: number): ObjectUnit[]; - function getUnits(type: ObjectType, classId?: number, mode?: number, unitId?: number): ObjectUnit[]; - function getUnit(type?: MissileType, name?: string, mode?: number, unitId?: number): Missile[] - function getUnit(type?: MissileType, classId?: number, mode?: number, unitId?: number): Missile[] - function getUnits(type: ItemType, name?: string, mode?: number, unitId?: number): ItemUnit[]; - function getUnits(type: ItemType, classId?: number, mode?: number, unitId?: number): ItemUnit[]; - function getUnits(type: TileType, name?: string, mode?: number, unitId?: number): Tile[]; - function getUnits(type: TileType, classId?: number, mode?: number, unitId?: number): Tile[]; - function getUnits(...args: any[]): Unit[]; - function clickItemAndWait(...args: Args[]): boolean; - function clickUnitAndWait(button: number, shift: 0 | 1, unit: Unit): boolean; - const LocalChat: object; - const areaNames: string[]; - function getAreaName(area: number): string; - namespace Game { - function getDistance(...args: any[]): number; - - function getCursorUnit(): ItemUnit; - function getSelectedUnit(): ItemUnit; - function getPlayer(id: any, mode: any, gid: any): Player; - function getMonster(id?: string | number, mode?: number, gid?: number): Monster; - function getNPC(id?: string | number, mode?: number, gid?: number): NPCUnit; - function getObject(id?: string | number, mode?: number, gid?: number): ObjectUnit; - function getMissile(id?: string | number, mode?: number, gid?: number): Missile; - function getItem(id?: string | number, mode?: number, gid?: number): ItemUnit; - function getStairs(id?: string | number, mode?: number, gid?: number): Tile; - function getPresetMonster(area: number, id: number): PresetUnit; - function getPresetMonsters(area: number, id: number): PresetUnit[]; - function getPresetObject(area: number, id: number): PresetUnit; - function getPresetObjects(area: number, id: number): PresetUnit[]; - function getPresetStair(area: number, id: number): PresetUnit; - function getPresetStairs(area: number, id: number): PresetUnit[]; - } - type Args = { - arg1: 0 | 1 | 2; - arg2: number | ItemUnit; - arg3?: number; - arg4?: number; - }; + function getThreads(): Script[]; + function getUnits(type: MonsterType, name?: string, mode?: number, unitId?: number): Monster[]; + function getUnits(type: MonsterType, classId?: number, mode?: number, unitId?: number): Monster[]; + function getUnits(type: ObjectType, name?: string, mode?: number, unitId?: number): ObjectUnit[]; + function getUnits(type: ObjectType, classId?: number, mode?: number, unitId?: number): ObjectUnit[]; + function getUnit(type?: MissileType, name?: string, mode?: number, unitId?: number): Missile[] + function getUnit(type?: MissileType, classId?: number, mode?: number, unitId?: number): Missile[] + function getUnits(type: ItemType, name?: string, mode?: number, unitId?: number): ItemUnit[]; + function getUnits(type: ItemType, classId?: number, mode?: number, unitId?: number): ItemUnit[]; + function getUnits(type: TileType, name?: string, mode?: number, unitId?: number): Tile[]; + function getUnits(type: TileType, classId?: number, mode?: number, unitId?: number): Tile[]; + function getUnits(...args: any[]): Unit[]; + function clickItemAndWait(...args: Args[]): boolean; + function clickUnitAndWait(button: number, shift: 0 | 1, unit: Unit): boolean; + const LocalChat: object; + const areaNames: string[]; + function getAreaName(area: number): string; + namespace Game { + function getDistance(...args: any[]): number; + + function getCursorUnit(): ItemUnit; + function getSelectedUnit(): ItemUnit; + function getPlayer(id: any, mode: any, gid: any): Player; + function getMonster(id?: string | number, mode?: number, gid?: number): Monster; + function getNPC(id?: string | number, mode?: number, gid?: number): NPCUnit; + function getObject(id?: string | number, mode?: number, gid?: number): ObjectUnit; + function getMissile(id?: string | number, mode?: number, gid?: number): Missile; + function getItem(id?: string | number, mode?: number, gid?: number): ItemUnit; + function getStairs(id?: string | number, mode?: number, gid?: number): Tile; + function getPresetMonster(area: number, id: number): PresetUnit; + function getPresetMonsters(area: number, id: number): PresetUnit[]; + function getPresetObject(area: number, id: number): PresetUnit; + function getPresetObjects(area: number, id: number): PresetUnit[]; + function getPresetStair(area: number, id: number): PresetUnit; + function getPresetStairs(area: number, id: number): PresetUnit[]; + } + type Args = { + arg1: 0 | 1 | 2; + arg2: number | ItemUnit; + arg3?: number; + arg4?: number; + }; - namespace Sort { - function units(a: Unit, b: Unit): number; - function presetUnits(a: PresetUnit, b: PresetUnit): number; - function points(a: [number, number], b: [number, number]): number; - function numbers(a: number, b: number): number; - } + namespace Sort { + function units(a: Unit, b: Unit): number; + function presetUnits(a: PresetUnit, b: PresetUnit): number; + function points(a: [number, number], b: [number, number]): number; + function numbers(a: number, b: number): number; + } } export {}; diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index a1cfedaa6..50d08b830 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -1,4929 +1,4930 @@ declare global { - namespace sdk { - export namespace waypoints { - const Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; - const Act1: number[]; - const Act2: number[]; - const Act3: number[]; - const Act4: number[]; - const Act5: number[]; - } + namespace sdk { + export namespace waypoints { + const Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; + const Act1: number[]; + const Act2: number[]; + const Act3: number[]; + const Act4: number[]; + const Act5: number[]; + } - export namespace difficulty { - const Normal: 0; - const Nightmare: 1; - const Hell: 2; - const Difficulties: ["Normal", "Nightmare", "Hell"]; + export namespace difficulty { + const Normal: 0; + const Nightmare: 1; + const Hell: 2; + const Difficulties: ["Normal", "Nightmare", "Hell"]; - const nameOf: (diff: 0 | 1 | 2) => "Normal" | "Nightmare" | "Hell" | false; - } + const nameOf: (diff: 0 | 1 | 2) => "Normal" | "Nightmare" | "Hell" | false; + } - export namespace party { - const NoParty: 65535 - namespace flag { - const Invite: 0; - const InParty: 1; - const Accept: 2; - const Cancel: 4; - } - namespace controls { - const Hostile: 1; - const InviteOrCancel: 2; - const Leave: 3; - const Ignore: 4; - const Squelch: 5; - } - } + export namespace party { + const NoParty: 65535 + namespace flag { + const Invite: 0; + const InParty: 1; + const Accept: 2; + const Cancel: 4; + } + namespace controls { + const Hostile: 1; + const InviteOrCancel: 2; + const Leave: 3; + const Ignore: 4; + const Squelch: 5; + } + } - export namespace clicktypes { - namespace click { - namespace item { - const Left: 0; - const Right: 1; - const ShiftLeft: 2; // For belt - const MercFromBelt: 3; // For belt - const Mercenary: 4 // Give to merc - } - namespace map { - const LeftDown: 0; - const LeftHold: 1; - const LeftUp: 2; - const RightDown: 3; - const RightHold: 4; - const RightUp: 5; - } - } - namespace shift { - const NoShift: 0; - const Shift: 1 - } - } + export namespace clicktypes { + namespace click { + namespace item { + const Left: 0; + const Right: 1; + const ShiftLeft: 2; // For belt + const MercFromBelt: 3; // For belt + const Mercenary: 4 // Give to merc + } + namespace map { + const LeftDown: 0; + const LeftHold: 1; + const LeftUp: 2; + const RightDown: 3; + const RightHold: 4; + const RightUp: 5; + } + } + namespace shift { + const NoShift: 0; + const Shift: 1 + } + } - export namespace cursortype { - const Empty: 1; - const ItemOnUnitHover: 3; // see notes - const ItemOnCursor: 4; // see notes - const Identify: 6; - const Repair: 7; - } + export namespace cursortype { + const Empty: 1; + const ItemOnUnitHover: 3; // see notes + const ItemOnCursor: 4; // see notes + const Identify: 6; + const Repair: 7; + } - export namespace collision { - const BlockWall: 0x01; - const LineOfSight: 0x02; - const Ranged: 0x04; - const PlayerToWalk: 0x08; - const DarkArea: 0x10; - const Casting: 0x20; - const Unknown: 0x40; - const Players: 0x80; - const Monsters: 0x100; - const Items: 0x200; - const Objects: 0x400; - const ClosedDoor: 0x800; - const IsOnFloor: 0x1000; - const MonsterIsOnFloor: 0x1100; - const MonsterIsOnFloorDarkArea: 0x1110; // in doorway - const FriendlyNPC: 0x2000; - const Unknown2: 0x4000; - const DeadBodies: 0x8000; - const MonsterObject: 0xFFFF; - const BlockMissile: 0x80E; - const WallOrRanged: 0x5; - const BlockWalk: 0x1805; - const FriendlyRanged: 0x2004; - } + export namespace collision { + const BlockWall: 0x01; + const LineOfSight: 0x02; + const Ranged: 0x04; + const PlayerToWalk: 0x08; + const DarkArea: 0x10; + const Casting: 0x20; + const Unknown: 0x40; + const Players: 0x80; + const Monsters: 0x100; + const Items: 0x200; + const Objects: 0x400; + const ClosedDoor: 0x800; + const IsOnFloor: 0x1000; + const MonsterIsOnFloor: 0x1100; + const MonsterIsOnFloorDarkArea: 0x1110; // in doorway + const FriendlyNPC: 0x2000; + const Unknown2: 0x4000; + const DeadBodies: 0x8000; + const MonsterObject: 0xFFFF; + const BlockMissile: 0x80E; + const WallOrRanged: 0x5; + const BlockWalk: 0x1805; + const FriendlyRanged: 0x2004; + } - export namespace areas { - const Towns: [1, 40, 75, 103, 109]; - const None: 0; + export namespace areas { + const Towns: [1, 40, 75, 103, 109]; + const None: 0; - // Act 1 - const RogueEncampment: 1; - const BloodMoor: 2; - const ColdPlains: 3; - const StonyField: 4; - const DarkWood: 5; - const BlackMarsh: 6; - const TamoeHighland: 7; - const DenofEvil: 8; - const CaveLvl1: 9; - const UndergroundPassageLvl1: 10; - const HoleLvl1: 11; - const PitLvl1: 12; - const CaveLvl2: 13; - const UndergroundPassageLvl2: 14; - const HoleLvl2: 15; - const PitLvl2: 16; - const BurialGrounds: 17; - const Crypt: 18; - const Mausoleum: 19; - const ForgottenTower: 20; - const TowerCellarLvl1: 21; - const TowerCellarLvl2: 22; - const TowerCellarLvl3: 23; - const TowerCellarLvl4: 24; - const TowerCellarLvl5: 25; - const MonasteryGate: 26; - const OuterCloister: 27; - const Barracks: 28; - const JailLvl1: 29; - const JailLvl2: 30; - const JailLvl3: 31; - const InnerCloister: 32; - const Cathedral: 33; - const CatacombsLvl1: 34; - const CatacombsLvl2: 35; - const CatacombsLvl3: 36; - const CatacombsLvl4: 37; - const Tristram: 38; - const MooMooFarm: 39; + // Act 1 + const RogueEncampment: 1; + const BloodMoor: 2; + const ColdPlains: 3; + const StonyField: 4; + const DarkWood: 5; + const BlackMarsh: 6; + const TamoeHighland: 7; + const DenofEvil: 8; + const CaveLvl1: 9; + const UndergroundPassageLvl1: 10; + const HoleLvl1: 11; + const PitLvl1: 12; + const CaveLvl2: 13; + const UndergroundPassageLvl2: 14; + const HoleLvl2: 15; + const PitLvl2: 16; + const BurialGrounds: 17; + const Crypt: 18; + const Mausoleum: 19; + const ForgottenTower: 20; + const TowerCellarLvl1: 21; + const TowerCellarLvl2: 22; + const TowerCellarLvl3: 23; + const TowerCellarLvl4: 24; + const TowerCellarLvl5: 25; + const MonasteryGate: 26; + const OuterCloister: 27; + const Barracks: 28; + const JailLvl1: 29; + const JailLvl2: 30; + const JailLvl3: 31; + const InnerCloister: 32; + const Cathedral: 33; + const CatacombsLvl1: 34; + const CatacombsLvl2: 35; + const CatacombsLvl3: 36; + const CatacombsLvl4: 37; + const Tristram: 38; + const MooMooFarm: 39; - // Act 2 - const LutGholein: 40; - const RockyWaste: 41; - const DryHills: 42; - const FarOasis: 43; - const LostCity: 44; - const ValleyofSnakes: 45; - const CanyonofMagic: 46; - const A2SewersLvl1: 47; - const A2SewersLvl2: 48; - const A2SewersLvl3: 49; - const HaremLvl1: 50; - const HaremLvl2: 51; - const PalaceCellarLvl1: 52; - const PalaceCellarLvl2: 53; - const PalaceCellarLvl3: 54; - const StonyTombLvl1: 55; - const HallsoftheDeadLvl1: 56; - const HallsoftheDeadLvl2: 57; - const ClawViperTempleLvl1: 58; - const StonyTombLvl2: 59; - const HallsoftheDeadLvl3: 60; - const ClawViperTempleLvl2: 61; - const MaggotLairLvl1: 62; - const MaggotLairLvl2: 63; - const MaggotLairLvl3: 64; - const AncientTunnels: 65; - const TalRashasTomb1: 66; - const TalRashasTomb2: 67; - const TalRashasTomb3: 68; - const TalRashasTomb4: 69; - const TalRashasTomb5: 70; - const TalRashasTomb6: 71; - const TalRashasTomb7: 72; - const DurielsLair: 73; - const ArcaneSanctuary: 74; + // Act 2 + const LutGholein: 40; + const RockyWaste: 41; + const DryHills: 42; + const FarOasis: 43; + const LostCity: 44; + const ValleyofSnakes: 45; + const CanyonofMagic: 46; + const A2SewersLvl1: 47; + const A2SewersLvl2: 48; + const A2SewersLvl3: 49; + const HaremLvl1: 50; + const HaremLvl2: 51; + const PalaceCellarLvl1: 52; + const PalaceCellarLvl2: 53; + const PalaceCellarLvl3: 54; + const StonyTombLvl1: 55; + const HallsoftheDeadLvl1: 56; + const HallsoftheDeadLvl2: 57; + const ClawViperTempleLvl1: 58; + const StonyTombLvl2: 59; + const HallsoftheDeadLvl3: 60; + const ClawViperTempleLvl2: 61; + const MaggotLairLvl1: 62; + const MaggotLairLvl2: 63; + const MaggotLairLvl3: 64; + const AncientTunnels: 65; + const TalRashasTomb1: 66; + const TalRashasTomb2: 67; + const TalRashasTomb3: 68; + const TalRashasTomb4: 69; + const TalRashasTomb5: 70; + const TalRashasTomb6: 71; + const TalRashasTomb7: 72; + const DurielsLair: 73; + const ArcaneSanctuary: 74; - // Act 3 - const KurastDocktown: 75; - const SpiderForest: 76; - const GreatMarsh: 77; - const FlayerJungle: 78; - const LowerKurast: 79; - const KurastBazaar: 80; - const UpperKurast: 81; - const KurastCauseway: 82; - const Travincal: 83; - const SpiderCave: 84; - const SpiderCavern: 85; - const SwampyPitLvl1: 86; - const SwampyPitLvl2: 87; - const FlayerDungeonLvl1: 88; - const FlayerDungeonLvl2: 89; - const SwampyPitLvl3: 90; - const FlayerDungeonLvl3: 91; - const A3SewersLvl1: 92; - const A3SewersLvl2: 93; - const RuinedTemple: 94; - const DisusedFane: 95; - const ForgottenReliquary: 96; - const ForgottenTemple: 97; - const RuinedFane: 98; - const DisusedReliquary: 99; - const DuranceofHateLvl1: 100; - const DuranceofHateLvl2: 101; - const DuranceofHateLvl3: 102; + // Act 3 + const KurastDocktown: 75; + const SpiderForest: 76; + const GreatMarsh: 77; + const FlayerJungle: 78; + const LowerKurast: 79; + const KurastBazaar: 80; + const UpperKurast: 81; + const KurastCauseway: 82; + const Travincal: 83; + const SpiderCave: 84; + const SpiderCavern: 85; + const SwampyPitLvl1: 86; + const SwampyPitLvl2: 87; + const FlayerDungeonLvl1: 88; + const FlayerDungeonLvl2: 89; + const SwampyPitLvl3: 90; + const FlayerDungeonLvl3: 91; + const A3SewersLvl1: 92; + const A3SewersLvl2: 93; + const RuinedTemple: 94; + const DisusedFane: 95; + const ForgottenReliquary: 96; + const ForgottenTemple: 97; + const RuinedFane: 98; + const DisusedReliquary: 99; + const DuranceofHateLvl1: 100; + const DuranceofHateLvl2: 101; + const DuranceofHateLvl3: 102; - // Act 4 - const PandemoniumFortress: 103; - const OuterSteppes: 104; - const PlainsofDespair: 105; - const CityoftheDamned: 106; - const RiverofFlame: 107; - const ChaosSanctuary: 108; + // Act 4 + const PandemoniumFortress: 103; + const OuterSteppes: 104; + const PlainsofDespair: 105; + const CityoftheDamned: 106; + const RiverofFlame: 107; + const ChaosSanctuary: 108; - // Act 5 - const Harrogath: 109; - const BloodyFoothills: 110; - const FrigidHighlands: 111; - const ArreatPlateau: 112; - const CrystalizedPassage: 113; - const FrozenRiver: 114; - const GlacialTrail: 115; - const DrifterCavern: 116; - const FrozenTundra: 117; - const AncientsWay: 118; - const IcyCellar: 119; - const ArreatSummit: 120; - const NihlathaksTemple: 121; - const HallsofAnguish: 122; - const HallsofPain: 123; - const HallsofVaught: 124; - const Abaddon: 125; - const PitofAcheron: 126; - const InfernalPit: 127; - const WorldstoneLvl1: 128; - const WorldstoneLvl2: 129; - const WorldstoneLvl3: 130; - const ThroneofDestruction: 131; - const WorldstoneChamber: 132; + // Act 5 + const Harrogath: 109; + const BloodyFoothills: 110; + const FrigidHighlands: 111; + const ArreatPlateau: 112; + const CrystalizedPassage: 113; + const FrozenRiver: 114; + const GlacialTrail: 115; + const DrifterCavern: 116; + const FrozenTundra: 117; + const AncientsWay: 118; + const IcyCellar: 119; + const ArreatSummit: 120; + const NihlathaksTemple: 121; + const HallsofAnguish: 122; + const HallsofPain: 123; + const HallsofVaught: 124; + const Abaddon: 125; + const PitofAcheron: 126; + const InfernalPit: 127; + const WorldstoneLvl1: 128; + const WorldstoneLvl2: 129; + const WorldstoneLvl3: 130; + const ThroneofDestruction: 131; + const WorldstoneChamber: 132; - // Ubers - const MatronsDen: 133; - const ForgottenSands: 134; - const FurnaceofPain: 135; - const UberTristram: 136; + // Ubers + const MatronsDen: 133; + const ForgottenSands: 134; + const FurnaceofPain: 135; + const UberTristram: 136; - const actOf: (act: number) => 1 | 2 | 3 | 4 | 5; - const townOf: (townArea: number) => 1 | 40 | 75 | 103 | 109; - const townOfAct: (act: 1 | 2 | 3 | 4 | 5) => 1 | 40 | 75 | 103 | 109; - } - - export namespace skills { - namespace get { - const RightName: 0; - const LeftName: 1; - const RightId: 2; - const LeftId: 3; - const AllSkills: 4 - } - namespace hand { - const Right: 0; - const Left: 1; - const LeftNoShift: 2; - const RightShift: 3; - } - namespace subindex { - const HardPoints: 0; - const SoftPoints: 1 - } - // General - const Attack: 0; - const Kick: 1; - const Throw: 2; - const Unsummon: 3; - const LeftHandThrow: 4; - const LeftHandSwing: 5; + const actOf: (act: number) => 1 | 2 | 3 | 4 | 5; + const townOf: (townArea: number) => 1 | 40 | 75 | 103 | 109; + const townOfAct: (act: 1 | 2 | 3 | 4 | 5) => 1 | 40 | 75 | 103 | 109; + } + + export namespace skills { + namespace get { + const RightName: 0; + const LeftName: 1; + const RightId: 2; + const LeftId: 3; + const AllSkills: 4 + } + namespace hand { + const Right: 0; + const Left: 1; + const LeftNoShift: 2; + const RightShift: 3; + } + namespace subindex { + const HardPoints: 0; + const SoftPoints: 1 + } + // General + const Attack: 0; + const Kick: 1; + const Throw: 2; + const Unsummon: 3; + const LeftHandThrow: 4; + const LeftHandSwing: 5; - // Amazon - const MagicArrow: 6; - const FireArrow: 7; - const InnerSight: 8; - const CriticalStrike: 9; - const Jab: 10; - const ColdArrow: 11; - const MultipleShot: 12; - const Dodge: 13; - const PowerStrike: 14; - const PoisonJavelin: 15; - const ExplodingArrow: 16; - const SlowMissiles: 17; - const Avoid: 18; - const Impale: 19; - const LightningBolt: 20; - const IceArrow: 21; - const GuidedArrow: 22; - const Penetrate: 23; - const ChargedStrike: 24; - const PlagueJavelin: 25; - const Strafe: 26; - const ImmolationArrow: 27; - const Dopplezon: 28; - const Decoy: 28; - const Evade: 29; - const Fend: 30; - const FreezingArrow: 31; - const Valkyrie: 32; - const Pierce: 33; - const LightningStrike: 34; - const LightningFury: 35; + // Amazon + const MagicArrow: 6; + const FireArrow: 7; + const InnerSight: 8; + const CriticalStrike: 9; + const Jab: 10; + const ColdArrow: 11; + const MultipleShot: 12; + const Dodge: 13; + const PowerStrike: 14; + const PoisonJavelin: 15; + const ExplodingArrow: 16; + const SlowMissiles: 17; + const Avoid: 18; + const Impale: 19; + const LightningBolt: 20; + const IceArrow: 21; + const GuidedArrow: 22; + const Penetrate: 23; + const ChargedStrike: 24; + const PlagueJavelin: 25; + const Strafe: 26; + const ImmolationArrow: 27; + const Dopplezon: 28; + const Decoy: 28; + const Evade: 29; + const Fend: 30; + const FreezingArrow: 31; + const Valkyrie: 32; + const Pierce: 33; + const LightningStrike: 34; + const LightningFury: 35; - // Sorc - const FireBolt: 36; - const Warmth: 37; - const ChargedBolt: 38; - const IceBolt: 39; - const FrozenArmor: 40; - const Inferno: 41; - const StaticField: 42; - const Telekinesis: 43; - const FrostNova: 44; - const IceBlast: 45; - const Blaze: 46; - const FireBall: 47; - const Nova: 48; - const Lightning: 49; - const ShiverArmor: 50; - const FireWall: 51; - const Enchant: 52; - const ChainLightning: 53; - const Teleport: 54; - const GlacialSpike: 55; - const Meteor: 56; - const ThunderStorm: 57; - const EnergyShield: 58; - const Blizzard: 59; - const ChillingArmor: 60; - const FireMastery: 61; - const Hydra: 62; - const LightningMastery: 63; - const FrozenOrb: 64; - const ColdMastery: 65; + // Sorc + const FireBolt: 36; + const Warmth: 37; + const ChargedBolt: 38; + const IceBolt: 39; + const FrozenArmor: 40; + const Inferno: 41; + const StaticField: 42; + const Telekinesis: 43; + const FrostNova: 44; + const IceBlast: 45; + const Blaze: 46; + const FireBall: 47; + const Nova: 48; + const Lightning: 49; + const ShiverArmor: 50; + const FireWall: 51; + const Enchant: 52; + const ChainLightning: 53; + const Teleport: 54; + const GlacialSpike: 55; + const Meteor: 56; + const ThunderStorm: 57; + const EnergyShield: 58; + const Blizzard: 59; + const ChillingArmor: 60; + const FireMastery: 61; + const Hydra: 62; + const LightningMastery: 63; + const FrozenOrb: 64; + const ColdMastery: 65; - // Necro - const AmplifyDamage: 66; - const Teeth: 67; - const BoneArmor: 68; - const SkeletonMastery: 69; - const RaiseSkeleton: 70; - const DimVision: 71; - const Weaken: 72; - const PoisonDagger: 73; - const CorpseExplosion: 74; - const ClayGolem: 75; - const IronMaiden: 76; - const Terror: 77; - const BoneWall: 78; - const GolemMastery: 79; - const RaiseSkeletalMage: 80; - const Confuse: 81; - const LifeTap: 82; - const PoisonExplosion: 83; - const BoneSpear: 84; - const BloodGolem: 85; - const Attract: 86; - const Decrepify: 87; - const BonePrison: 88; - const SummonResist: 89; - const IronGolem: 90; - const LowerResist: 91; - const PoisonNova: 92; - const BoneSpirit: 93; - const FireGolem: 94; - const Revive: 95; + // Necro + const AmplifyDamage: 66; + const Teeth: 67; + const BoneArmor: 68; + const SkeletonMastery: 69; + const RaiseSkeleton: 70; + const DimVision: 71; + const Weaken: 72; + const PoisonDagger: 73; + const CorpseExplosion: 74; + const ClayGolem: 75; + const IronMaiden: 76; + const Terror: 77; + const BoneWall: 78; + const GolemMastery: 79; + const RaiseSkeletalMage: 80; + const Confuse: 81; + const LifeTap: 82; + const PoisonExplosion: 83; + const BoneSpear: 84; + const BloodGolem: 85; + const Attract: 86; + const Decrepify: 87; + const BonePrison: 88; + const SummonResist: 89; + const IronGolem: 90; + const LowerResist: 91; + const PoisonNova: 92; + const BoneSpirit: 93; + const FireGolem: 94; + const Revive: 95; - // Paladin - const Sacrifice: 96; - const Smite: 97; - const Might: 98; - const Prayer: 99; - const ResistFire: 100; - const HolyBolt: 101; - const HolyFire: 102; - const Thorns: 103; - const Defiance: 104; - const ResistCold: 105; - const Zeal: 106; - const Charge: 107; - const BlessedAim: 108; - const Cleansing: 109; - const ResistLightning: 110; - const Vengeance: 111; - const BlessedHammer: 112; - const Concentration: 113; - const HolyFreeze: 114; - const Vigor: 115; - const Conversion: 116; - const HolyShield: 117; - const HolyShock: 118; - const Sanctuary: 119; - const Meditation: 120; - const FistoftheHeavens: 121; - const Fanaticism: 122; - const Conviction: 123; - const Redemption: 124; - const Salvation: 125; + // Paladin + const Sacrifice: 96; + const Smite: 97; + const Might: 98; + const Prayer: 99; + const ResistFire: 100; + const HolyBolt: 101; + const HolyFire: 102; + const Thorns: 103; + const Defiance: 104; + const ResistCold: 105; + const Zeal: 106; + const Charge: 107; + const BlessedAim: 108; + const Cleansing: 109; + const ResistLightning: 110; + const Vengeance: 111; + const BlessedHammer: 112; + const Concentration: 113; + const HolyFreeze: 114; + const Vigor: 115; + const Conversion: 116; + const HolyShield: 117; + const HolyShock: 118; + const Sanctuary: 119; + const Meditation: 120; + const FistoftheHeavens: 121; + const Fanaticism: 122; + const Conviction: 123; + const Redemption: 124; + const Salvation: 125; - // Barb - const Bash: 126; - const SwordMastery: 127; - const AxeMastery: 128; - const MaceMastery: 129; - const Howl: 130; - const FindPotion: 131; - const Leap: 132; - const DoubleSwing: 133; - const PoleArmMastery: 134; - const ThrowingMastery: 135; - const SpearMastery: 136; - const Taunt: 137; - const Shout: 138; - const Stun: 139; - const DoubleThrow: 140; - const IncreasedStamina: 141; - const FindItem: 142; - const LeapAttack: 143; - const Concentrate: 144; - const IronSkin: 145; - const BattleCry: 146; - const Frenzy: 147; - const IncreasedSpeed: 148; - const BattleOrders: 149; - const GrimWard: 150; - const Whirlwind: 151; - const Berserk: 152; - const NaturalResistance: 153; - const WarCry: 154; - const BattleCommand: 155; + // Barb + const Bash: 126; + const SwordMastery: 127; + const AxeMastery: 128; + const MaceMastery: 129; + const Howl: 130; + const FindPotion: 131; + const Leap: 132; + const DoubleSwing: 133; + const PoleArmMastery: 134; + const ThrowingMastery: 135; + const SpearMastery: 136; + const Taunt: 137; + const Shout: 138; + const Stun: 139; + const DoubleThrow: 140; + const IncreasedStamina: 141; + const FindItem: 142; + const LeapAttack: 143; + const Concentrate: 144; + const IronSkin: 145; + const BattleCry: 146; + const Frenzy: 147; + const IncreasedSpeed: 148; + const BattleOrders: 149; + const GrimWard: 150; + const Whirlwind: 151; + const Berserk: 152; + const NaturalResistance: 153; + const WarCry: 154; + const BattleCommand: 155; - // General stuff - const IdentifyScroll: 217; - const BookofIdentify: 218; - const TownPortalScroll: 219; - const BookofTownPortal: 220; + // General stuff + const IdentifyScroll: 217; + const BookofIdentify: 218; + const TownPortalScroll: 219; + const BookofTownPortal: 220; - // Druid - const Raven: 221; - const PoisonCreeper: 222; // External - const PlaguePoppy: 222; // Internal - const Werewolf: 223; // External - const Wearwolf: 223; // Internal - const Lycanthropy: 224; // External - const ShapeShifting: 224; // Internal - const Firestorm: 225; - const OakSage: 226; - const SpiritWolf: 227; // External - const SummonSpiritWolf: 227; // Internal - const Werebear: 228; // External - const Wearbear: 228; // Internal - const MoltenBoulder: 229; - const ArcticBlast: 230; - const CarrionVine: 231; // External - const CycleofLife: 231; // Internal - const FeralRage: 232; - const Maul: 233; - const Fissure: 234; // Internal - const Eruption: 234; // Internal - const CycloneArmor: 235; - const HeartofWolverine: 236; - const SummonDireWolf: 237; // External - const SummonFenris: 237; // Internal - const Rabies: 238; - const FireClaws: 239; - const Twister: 240; - const SolarCreeper: 241; // External - const Vines: 241; // Internal - const Hunger: 242; - const ShockWave: 243; - const Volcano: 244; - const Tornado: 245; - const SpiritofBarbs: 246; - const Grizzly: 247; // External - const SummonGrizzly: 247; // Internal - const Fury: 248; - const Armageddon: 249; - const Hurricane: 250; + // Druid + const Raven: 221; + const PoisonCreeper: 222; // External + const PlaguePoppy: 222; // Internal + const Werewolf: 223; // External + const Wearwolf: 223; // Internal + const Lycanthropy: 224; // External + const ShapeShifting: 224; // Internal + const Firestorm: 225; + const OakSage: 226; + const SpiritWolf: 227; // External + const SummonSpiritWolf: 227; // Internal + const Werebear: 228; // External + const Wearbear: 228; // Internal + const MoltenBoulder: 229; + const ArcticBlast: 230; + const CarrionVine: 231; // External + const CycleofLife: 231; // Internal + const FeralRage: 232; + const Maul: 233; + const Fissure: 234; // Internal + const Eruption: 234; // Internal + const CycloneArmor: 235; + const HeartofWolverine: 236; + const SummonDireWolf: 237; // External + const SummonFenris: 237; // Internal + const Rabies: 238; + const FireClaws: 239; + const Twister: 240; + const SolarCreeper: 241; // External + const Vines: 241; // Internal + const Hunger: 242; + const ShockWave: 243; + const Volcano: 244; + const Tornado: 245; + const SpiritofBarbs: 246; + const Grizzly: 247; // External + const SummonGrizzly: 247; // Internal + const Fury: 248; + const Armageddon: 249; + const Hurricane: 250; - // Assa - const FireBlast: 251; // External - const FireTrauma: 251; // Internal - const ClawMastery: 252; - const PsychicHammer: 253; - const TigerStrike: 254; - const DragonTalon: 255; - const ShockWeb: 256; // External - const ShockField: 256; // Internal - const BladeSentinel: 257; - const Quickness: 258; // Internal name - const BurstofSpeed: 258; // Shown name - const FistsofFire: 259; - const DragonClaw: 260; - const ChargedBoltSentry: 261; - const WakeofFire: 262; // External - const WakeofFireSentry: 262; // Internal - const WeaponBlock: 263; - const CloakofShadows: 264; - const CobraStrike: 265; - const BladeFury: 266; - const Fade: 267; - const ShadowWarrior: 268; - const ClawsofThunder: 269; - const DragonTail: 270; - const LightningSentry: 271; - const WakeofInferno: 272; // External - const InfernoSentry: 272; // Internal - const MindBlast: 273; - const BladesofIce: 274; - const DragonFlight: 275; - const DeathSentry: 276; - const BladeShield: 277; - const Venom: 278; - const ShadowMaster: 279; - const PhoenixStrike: 280; // External - const RoyalStrike: 280; // Internal - const WakeofDestructionSentry: 281; // Not used? - const Summoner: 500; // special - namespace tabs { - // Ama - const BowandCrossbow: 0; - const PassiveandMagic: 1; - const JavelinandSpear: 2; + // Assa + const FireBlast: 251; // External + const FireTrauma: 251; // Internal + const ClawMastery: 252; + const PsychicHammer: 253; + const TigerStrike: 254; + const DragonTalon: 255; + const ShockWeb: 256; // External + const ShockField: 256; // Internal + const BladeSentinel: 257; + const Quickness: 258; // Internal name + const BurstofSpeed: 258; // Shown name + const FistsofFire: 259; + const DragonClaw: 260; + const ChargedBoltSentry: 261; + const WakeofFire: 262; // External + const WakeofFireSentry: 262; // Internal + const WeaponBlock: 263; + const CloakofShadows: 264; + const CobraStrike: 265; + const BladeFury: 266; + const Fade: 267; + const ShadowWarrior: 268; + const ClawsofThunder: 269; + const DragonTail: 270; + const LightningSentry: 271; + const WakeofInferno: 272; // External + const InfernoSentry: 272; // Internal + const MindBlast: 273; + const BladesofIce: 274; + const DragonFlight: 275; + const DeathSentry: 276; + const BladeShield: 277; + const Venom: 278; + const ShadowMaster: 279; + const PhoenixStrike: 280; // External + const RoyalStrike: 280; // Internal + const WakeofDestructionSentry: 281; // Not used? + const Summoner: 500; // special + namespace tabs { + // Ama + const BowandCrossbow: 0; + const PassiveandMagic: 1; + const JavelinandSpear: 2; - // Sorc - const Fire: 8; - const Lightning: 9; - const Cold: 10; + // Sorc + const Fire: 8; + const Lightning: 9; + const Cold: 10; - // Necro - const Curses: 16; - const PoisonandBone: 17; - const NecroSummoning: 18; + // Necro + const Curses: 16; + const PoisonandBone: 17; + const NecroSummoning: 18; - // Pala - const PalaCombat: 24; - const Offensive: 25; - const Defensive: 26; + // Pala + const PalaCombat: 24; + const Offensive: 25; + const Defensive: 26; - // Barb - const BarbCombat: 32; - const Masteries: 33; - const Warcries: 34; + // Barb + const BarbCombat: 32; + const Masteries: 33; + const Warcries: 34; - // Druid - const DruidSummon: 40; - const ShapeShifting: 41; - const Elemental: 42; + // Druid + const DruidSummon: 40; + const ShapeShifting: 41; + const Elemental: 42; - // Assa - const Traps: 48; - const ShadowDisciplines: 49; - const MartialArts: 50; - } - } - export const skillTabs: undefined + // Assa + const Traps: 48; + const ShadowDisciplines: 49; + const MartialArts: 50; + } + } + export const skillTabs: undefined - export namespace quest { - export namespace item { - // Act 1 - const WirtsLeg: 88; - const HoradricMalus: 89; - const ScrollofInifuss: 524; - const KeytotheCairnStones: 525; - // Act 2 - const FinishedStaff: 91; - const HoradricStaff: 91; - const IncompleteStaff: 92; - const ShaftoftheHoradricStaff: 92; - const ViperAmulet: 521; - const TopoftheHoradricStaff: 521; - const Cube: 549; - const BookofSkill: 552; - // Act 3 - const DecoyGidbinn: 86; - const TheGidbinn: 87; - const KhalimsFlail: 173; - const KhalimsWill: 174; - const PotofLife: 545; - const AJadeFigurine: 546; - const JadeFigurine: 546; - const TheGoldenBird: 547; - const LamEsensTome: 548; - const KhalimsEye: 553; - const KhalimsHeart: 554; - const KhalimsBrain: 555; - // Act 4 - const HellForgeHammer: 90; - const Soulstone: 551; - const MephistosSoulstone: 551; - // Act 5 - const MalahsPotion: 644; - const ScrollofKnowledge: 645; - const ScrollofResistance: 646; - // Pandemonium Event - const KeyofTerror: 647; - const KeyofHate: 648; - const KeyofDestruction: 649; - const DiablosHorn: 650; - const BaalsEye: 651; - const MephistosBrain: 652; - const StandardofHeroes: 658; - // Essences/Token - const TokenofAbsolution: 653; - const TwistedEssenceofSuffering: 654; - const ChargedEssenceofHatred: 655; - const BurningEssenceofTerror: 656; - const FesteringEssenceofDestruction: 657; - // Misc - const TheBlackTowerKey: 544; - } - const items: [ - // act 1 - 88, 89, 524, 525, - // act 2 - 91, 92, 521, 549, 552, - // act 3 - 86, 87, 173, 174, 545, 546, 547, 548, 553, 554, 555, - // act 4 - 90, 551, - // act 5 - 644, 645, 646, - ]; - export namespace chest { - // act1 - const StoneAlpha: 17; - const StoneBeta: 18; - const StoneGamma: 19; - const StoneDelta: 20; - const StoneLambda: 21; - const StoneTheta: 22; // ? - const CainsJail: 26; - const InifussTree: 30; - const MalusHolder: 108; - const Wirt: 268; + export namespace quest { + export namespace item { + // Act 1 + const WirtsLeg: 88; + const HoradricMalus: 89; + const ScrollofInifuss: 524; + const KeytotheCairnStones: 525; + // Act 2 + const FinishedStaff: 91; + const HoradricStaff: 91; + const IncompleteStaff: 92; + const ShaftoftheHoradricStaff: 92; + const ViperAmulet: 521; + const TopoftheHoradricStaff: 521; + const Cube: 549; + const BookofSkill: 552; + // Act 3 + const DecoyGidbinn: 86; + const TheGidbinn: 87; + const KhalimsFlail: 173; + const KhalimsWill: 174; + const PotofLife: 545; + const AJadeFigurine: 546; + const JadeFigurine: 546; + const TheGoldenBird: 547; + const LamEsensTome: 548; + const KhalimsEye: 553; + const KhalimsHeart: 554; + const KhalimsBrain: 555; + // Act 4 + const HellForgeHammer: 90; + const Soulstone: 551; + const MephistosSoulstone: 551; + // Act 5 + const MalahsPotion: 644; + const ScrollofKnowledge: 645; + const ScrollofResistance: 646; + // Pandemonium Event + const KeyofTerror: 647; + const KeyofHate: 648; + const KeyofDestruction: 649; + const DiablosHorn: 650; + const BaalsEye: 651; + const MephistosBrain: 652; + const StandardofHeroes: 658; + // Essences/Token + const TokenofAbsolution: 653; + const TwistedEssenceofSuffering: 654; + const ChargedEssenceofHatred: 655; + const BurningEssenceofTerror: 656; + const FesteringEssenceofDestruction: 657; + // Misc + const TheBlackTowerKey: 544; + } + const items: [ + // act 1 + 88, 89, 524, 525, + // act 2 + 91, 92, 521, 549, 552, + // act 3 + 86, 87, 173, 174, 545, 546, 547, 548, 553, 554, 555, + // act 4 + 90, 551, + // act 5 + 644, 645, 646, + ]; + export namespace chest { + // act1 + const StoneAlpha: 17; + const StoneBeta: 18; + const StoneGamma: 19; + const StoneDelta: 20; + const StoneLambda: 21; + const StoneTheta: 22; // ? + const CainsJail: 26; + const InifussTree: 30; + const MalusHolder: 108; + const Wirt: 268; - // act 2 - const ViperAmuletChest: 149; - const HoradricStaffHolder: 152; - const HoradricCubeChest: 354; - const HoradricScrollChest: 355; - const ShaftoftheHoradricStaffChest: 356; - const Journal: 357; + // act 2 + const ViperAmuletChest: 149; + const HoradricStaffHolder: 152; + const HoradricCubeChest: 354; + const HoradricScrollChest: 355; + const ShaftoftheHoradricStaffChest: 356; + const Journal: 357; - // act 3 - const ForestAltar: 81; - const LamEsensTomeHolder: 193; - const GidbinnAltar: 252; - const KhalimsHeartChest: 405; - const KhalimsBrainChest: 406; - const KhalimsEyeChest: 407; + // act 3 + const ForestAltar: 81; + const LamEsensTomeHolder: 193; + const GidbinnAltar: 252; + const KhalimsHeartChest: 405; + const KhalimsBrainChest: 406; + const KhalimsEyeChest: 407; - // act 4 - const HellForge: 376; + // act 4 + const HellForge: 376; - // act 5 - const BarbCage: 473; - const FrozenAnya: 558; - const AncientsAltar: 546; - } - const chests: [ - // act 1 - 17, 18, 19, 20, 21, 22, 26, 30, 108, - // act 2 - 149, 152, 354, 355, 356, 357, - // act 3 - 81, 193, 405, 406, 407, - // act 4 - 376, - // act 5 - 434, 558, 546 - ]; - export namespace id { - const SpokeToWarriv: 0; - const DenofEvil: 1; - const SistersBurialGrounds: 2; - const TheSearchForCain: 4; - const ForgottenTower: 5; - const ToolsoftheTrade: 3; - const SistersToTheSlaughter: 6; - const AbleToGotoActII: 7; - const SpokeToJerhyn: 8; - const RadamentsLair: 9; - const TheHoradricStaff: 10; - const TheTaintedSun: 11; - const TheArcaneSanctuary: 12; - const TheSummoner: 13; - const TheSevenTombs: 14; - const AbleToGotoActIII: 15; - const SpokeToHratli: 16; - const TheGoldenBird: 20; - const BladeoftheOldReligion: 19; - const KhalimsWill: 18; - const LamEsensTome: 17; - const TheBlackenedTemple: 21; - const TheGuardian: 22; - const AbleToGotoActIV: 23; - const SpokeToTyrael: 24; - const TheFallenAngel: 25; - const HellsForge: 27; - const TerrorsEnd: 26; - const AbleToGotoActV: 28; - const SiegeOnHarrogath: 35; - const RescueonMountArreat: 36; - const PrisonofIce: 37; - const BetrayalofHarrogath: 38; - const RiteofPassage: 39; - const EyeofDestruction: 40; - const Respec: 41; - } - // just common states for now - namespace states { - const Completed: 0; - const ReqComplete: 1; - const GreyedOut: 12; - const PartyMemberComplete: 13; - const CannotComplete: 14; - } - } + // act 5 + const BarbCage: 473; + const FrozenAnya: 558; + const AncientsAltar: 546; + } + const chests: [ + // act 1 + 17, 18, 19, 20, 21, 22, 26, 30, 108, + // act 2 + 149, 152, 354, 355, 356, 357, + // act 3 + 81, 193, 405, 406, 407, + // act 4 + 376, + // act 5 + 434, 558, 546 + ]; + export namespace id { + const SpokeToWarriv: 0; + const DenofEvil: 1; + const SistersBurialGrounds: 2; + const TheSearchForCain: 4; + const ForgottenTower: 5; + const ToolsoftheTrade: 3; + const SistersToTheSlaughter: 6; + const AbleToGotoActII: 7; + const SpokeToJerhyn: 8; + const RadamentsLair: 9; + const TheHoradricStaff: 10; + const TheTaintedSun: 11; + const TheArcaneSanctuary: 12; + const TheSummoner: 13; + const TheSevenTombs: 14; + const AbleToGotoActIII: 15; + const SpokeToHratli: 16; + const TheGoldenBird: 20; + const BladeoftheOldReligion: 19; + const KhalimsWill: 18; + const LamEsensTome: 17; + const TheBlackenedTemple: 21; + const TheGuardian: 22; + const AbleToGotoActIV: 23; + const SpokeToTyrael: 24; + const TheFallenAngel: 25; + const HellsForge: 27; + const TerrorsEnd: 26; + const AbleToGotoActV: 28; + const SiegeOnHarrogath: 35; + const RescueonMountArreat: 36; + const PrisonofIce: 37; + const BetrayalofHarrogath: 38; + const RiteofPassage: 39; + const EyeofDestruction: 40; + const Respec: 41; + } + // just common states for now + namespace states { + const Completed: 0; + const ReqComplete: 1; + const GreyedOut: 12; + const PartyMemberComplete: 13; + const CannotComplete: 14; + } + } - // in game data - export namespace uiflags { - const Inventory: 0x01; - const StatsWindow: 0x02; - const QuickSkill: 0x03; - const SkillWindow: 0x04; - const ChatBox: 0x05; - const NPCMenu: 0x08; - const EscMenu: 0x09; - const KeytotheCairnStonesScreen: 0x10; - const AutoMap: 0x0A; - const ConfigControls: 0x0B; - const Shop: 0x0C; - const ShowItem: 0x0D; - const SubmitItem: 0x0E; - const Quest: 0x0F; - const QuestLog: 0x11; - const StatusArea: 0x12; - const Waypoint: 0x14; - const MiniPanel: 0x15; - const Party: 0x16; - const TradePrompt: 0x17; - const Msgs: 0x18; - const Stash: 0x19; - const Cube: 0x1A; - const ShowBelt: 0x1F; - const Help: 0x21; - const MercScreen: 0x24; - const ScrollWindow: 0x25 - } + // in game data + export namespace uiflags { + const Inventory: 0x01; + const StatsWindow: 0x02; + const QuickSkill: 0x03; + const SkillWindow: 0x04; + const ChatBox: 0x05; + const NPCMenu: 0x08; + const EscMenu: 0x09; + const KeytotheCairnStonesScreen: 0x10; + const AutoMap: 0x0A; + const ConfigControls: 0x0B; + const Shop: 0x0C; + const ShowItem: 0x0D; + const SubmitItem: 0x0E; + const Quest: 0x0F; + const QuestLog: 0x11; + const StatusArea: 0x12; + const Waypoint: 0x14; + const MiniPanel: 0x15; + const Party: 0x16; + const TradePrompt: 0x17; + const Msgs: 0x18; + const Stash: 0x19; + const Cube: 0x1A; + const ShowBelt: 0x1F; + const Help: 0x21; + const MercScreen: 0x24; + const ScrollWindow: 0x25 + } - export namespace menu { - const Respec: 0x2BA0; - const Ok: 0x0D49; - const Talk: 0x0D35; - const Trade: 0x0D44; - const TradeRepair: 0x0D06; - const Imbue: 0x0FB1; - const Gamble: 0x0D46; - const Hire: 0x0D45; - const GoEast: 0x0D36; - const GoWest: 0x0D37; - const IdentifyItems: 0x0FB4; - const SailEast: 0x0D38; - const SailWest: 0x0D39; - const RessurectMerc: 0x1507; - const AddSockets: 0x58DC; - const Personalize: 0x58DD; - const TravelToHarrogath: 0x58D2; - } + export namespace menu { + const Respec: 0x2BA0; + const Ok: 0x0D49; + const Talk: 0x0D35; + const Trade: 0x0D44; + const TradeRepair: 0x0D06; + const Imbue: 0x0FB1; + const Gamble: 0x0D46; + const Hire: 0x0D45; + const GoEast: 0x0D36; + const GoWest: 0x0D37; + const IdentifyItems: 0x0FB4; + const SailEast: 0x0D38; + const SailWest: 0x0D39; + const RessurectMerc: 0x1507; + const AddSockets: 0x58DC; + const Personalize: 0x58DD; + const TravelToHarrogath: 0x58D2; + } - // shrine types - export namespace shrines { - const Presets: [2, 81, 83, 170, 344, 197, 202]; - const Ids: [ - 2, 77, 81, 83, 84, 85, 93, 96, 97, 109, 116, 123, 124, - 133, 134, 135, 136, 150, 151, 164, 165, 166, 167, 168, - 170, 172, 173, 184, 190, 191, 197, 199, 200, 201, 202, - 206, 226, 231, 232, 236, 249, 260, 262, 263, 264, 265, - 275, 276, 277, 278, 279, 280, 281, 282, 299, 300, 302, - 303, 320, 325, 343, 344, 361, 414, 415, 421, 422, 423, - 427, 428, 464, 465, 472, 479, 483, 484, 488, 491, 492, - 495, 497, 499, 503, 509, 512, 520, 521, 522 - ]; - const None: 0; - const Refilling: 1; - const Health: 2; - const Mana: 3; - const HealthExchange: 4; - const ManaExchange: 5; - const Armor: 6; - const Combat: 7; - const ResistFire: 8; - const ResistCold: 9; - const ResistLightning: 10; - const ResistPoison: 11; - const Skill: 12; - const ManaRecharge: 13; - const Stamina: 14; - const Experience: 15; - const Enirhs: 16; - const Portal: 17; - const Gem: 18; - const Fire: 19; - const Monster: 20; - const Exploding: 21; - const Poison: 22 - } + // shrine types + export namespace shrines { + const Presets: [2, 81, 83, 170, 344, 197, 202]; + const Ids: [ + 2, 77, 81, 83, 84, 85, 93, 96, 97, 109, 116, 123, 124, + 133, 134, 135, 136, 150, 151, 164, 165, 166, 167, 168, + 170, 172, 173, 184, 190, 191, 197, 199, 200, 201, 202, + 206, 226, 231, 232, 236, 249, 260, 262, 263, 264, 265, + 275, 276, 277, 278, 279, 280, 281, 282, 299, 300, 302, + 303, 320, 325, 343, 344, 361, 414, 415, 421, 422, 423, + 427, 428, 464, 465, 472, 479, 483, 484, 488, 491, 492, + 495, 497, 499, 503, 509, 512, 520, 521, 522 + ]; + const None: 0; + const Refilling: 1; + const Health: 2; + const Mana: 3; + const HealthExchange: 4; + const ManaExchange: 5; + const Armor: 6; + const Combat: 7; + const ResistFire: 8; + const ResistCold: 9; + const ResistLightning: 10; + const ResistPoison: 11; + const Skill: 12; + const ManaRecharge: 13; + const Stamina: 14; + const Experience: 15; + const Enirhs: 16; + const Portal: 17; + const Gem: 18; + const Fire: 19; + const Monster: 20; + const Exploding: 21; + const Poison: 22 + } - // unit states - export namespace states { - const None: 0; - const FrozenSolid: 1; - const Poison: 2; - const ResistFire: 3; - const ResistCold: 4; - const ResistLightning: 5; - const ResistMagic: 6; - const PlayerBody: 7; - const ResistAll: 8; - const AmplifyDamage: 9; - const FrozenArmor: 10; - const Frozen: 11; - const Inferno: 12; - const Blaze: 13; - const BoneArmor: 14; - const Concentrate: 15; - const Enchant: 16; - const InnerSight: 17; - const SkillMove: 18; - const Weaken: 19; - const ChillingArmor: 20; - const Stunned: 21; - const SpiderLay: 22; - const DimVision: 23; - const Slowed: 24; - const FetishAura: 25; - const Shout: 26; - const Taunt: 27; - const Conviction: 28; - const Convicted: 29; - const EnergyShield: 30; - const Venom: 31; - const BattleOrders: 32; - const Might: 33; - const Prayer: 34; - const HolyFire: 35; - const Thorns: 36; - const Defiance: 37; - const ThunderStorm: 38; - const LightningBolt: 39; - const BlessedAim: 40; - const Stamina: 41; - const Concentration: 42; - const Holywind: 43; - const HolyFreeze: 43; - const HolywindCold: 44; - const HolyFreezeCold: 44; - const Cleansing: 45; - const HolyShock: 46; - const Sanctuary: 47; - const Meditation: 48; - const Fanaticism: 49; - const Redemption: 50; - const BattleCommand: 51; - const PreventHeal: 52; - const Conversion: 53; - const Uninterruptable: 54; - const IronMaiden: 55; - const Terror: 56; - const Attract: 57; - const LifeTap: 58; - const Confuse: 59; - const Decrepify: 60; - const LowerResist: 61; - const OpenWounds: 62; - const Dopplezon: 63; - const Decoy: 63; - const CriticalStrike: 64; - const Dodge: 65; - const Avoid: 66; - const Penetrate: 67; - const Evade: 68; - const Pierce: 69; - const Warmth: 70; - const FireMastery: 71; - const LightningMastery: 72; - const ColdMastery: 73; - const SwordMastery: 74; - const AxeMastery: 75; - const MaceMastery: 76; - const PoleArmMastery: 77; - const ThrowingMastery: 78; - const SpearMastery: 79; - const IncreasedStamina: 80; - const IronSkin: 81; - const IncreasedSpeed: 82; - const NaturalResistance: 83; - const FingerMageCurse: 84; - const NoManaReg: 85; - const JustHit: 86; - const SlowMissiles: 87; - const ShiverArmor: 88; - const BattleCry: 89; - const Blue: 90; - const Red: 91; - const DeathDelay: 92; - const Valkyrie: 93; - const Frenzy: 94; - const Berserk: 95; - const Revive: 96; - const ItemFullSet: 97; - const SourceUnit: 98; - const Redeemed: 99; - const HealthPot: 100; - const HolyShield: 101; - const JustPortaled: 102; - const MonFrenzy: 103; - const CorpseNoDraw: 104; - const Alignment: 105; - const ManaPot: 106; - const Shatter: 107; - const SyncWarped: 108; - const ConversionSave: 109; - const Pregnat: 110; - const Rabies: 112; - const DefenceCurse: 113; - const BloodMana: 114; - const Burning: 115; - const DragonFlight: 116; - const Maul: 117; - const CorpseNoSelect: 118; - const ShadowWarrior: 119; - const FeralRage: 120; - const SkillDelay: 121; - const ProgressiveDamage: 122; - const ProgressiveSteal: 123; - const ProgressiveOther: 124; - const ProgressiveFire: 125; - const ProgressiveCold: 126; - const ProgressiveLighting: 127; - const ShrineArmor: 128; - const ShrineCombat: 129; - const ShrineResLighting: 130; - const ShrineResFire: 131; - const ShrineResCold: 132; - const ShrineResPoison: 133; - const ShrineSkill: 134; - const ShrineManaRegen: 135; - const ShrineStamina: 136; - const ShrineExperience: 137; - const FenrisRage: 138; - const Wolf: 139; - const Wearwolf: 139; - const Bear: 140; - const Wearbear: 140; - const Bloodlust: 141; - const ChangeClass: 142; - const Attached: 143; - const Hurricane: 144; - const Armageddon: 145; - const Invis: 146; - const Barbs: 147; - const HeartofWolverine: 148; - const OakSage: 149; - const VineBeast: 150; - const CycloneArmor: 151; - const ClawMastery: 152; - const CloakofShadows: 153; - const Recyled: 154; - const WeaponBlock: 155; - const Cloaked: 156; - const Quickness: 157; // Internal name - const BurstofSpeed: 157; // External name - const BladeShield: 158; - const Fade: 159; - const RestInPeace: 172; - const Glowing: 175; - const Delerium: 177; - const Antidote: 178; - const Thawing: 179; - const StaminaPot: 180; - } + // unit states + export namespace states { + const None: 0; + const FrozenSolid: 1; + const Poison: 2; + const ResistFire: 3; + const ResistCold: 4; + const ResistLightning: 5; + const ResistMagic: 6; + const PlayerBody: 7; + const ResistAll: 8; + const AmplifyDamage: 9; + const FrozenArmor: 10; + const Frozen: 11; + const Inferno: 12; + const Blaze: 13; + const BoneArmor: 14; + const Concentrate: 15; + const Enchant: 16; + const InnerSight: 17; + const SkillMove: 18; + const Weaken: 19; + const ChillingArmor: 20; + const Stunned: 21; + const SpiderLay: 22; + const DimVision: 23; + const Slowed: 24; + const FetishAura: 25; + const Shout: 26; + const Taunt: 27; + const Conviction: 28; + const Convicted: 29; + const EnergyShield: 30; + const Venom: 31; + const BattleOrders: 32; + const Might: 33; + const Prayer: 34; + const HolyFire: 35; + const Thorns: 36; + const Defiance: 37; + const ThunderStorm: 38; + const LightningBolt: 39; + const BlessedAim: 40; + const Stamina: 41; + const Concentration: 42; + const Holywind: 43; + const HolyFreeze: 43; + const HolywindCold: 44; + const HolyFreezeCold: 44; + const Cleansing: 45; + const HolyShock: 46; + const Sanctuary: 47; + const Meditation: 48; + const Fanaticism: 49; + const Redemption: 50; + const BattleCommand: 51; + const PreventHeal: 52; + const Conversion: 53; + const Uninterruptable: 54; + const IronMaiden: 55; + const Terror: 56; + const Attract: 57; + const LifeTap: 58; + const Confuse: 59; + const Decrepify: 60; + const LowerResist: 61; + const OpenWounds: 62; + const Dopplezon: 63; + const Decoy: 63; + const CriticalStrike: 64; + const Dodge: 65; + const Avoid: 66; + const Penetrate: 67; + const Evade: 68; + const Pierce: 69; + const Warmth: 70; + const FireMastery: 71; + const LightningMastery: 72; + const ColdMastery: 73; + const SwordMastery: 74; + const AxeMastery: 75; + const MaceMastery: 76; + const PoleArmMastery: 77; + const ThrowingMastery: 78; + const SpearMastery: 79; + const IncreasedStamina: 80; + const IronSkin: 81; + const IncreasedSpeed: 82; + const NaturalResistance: 83; + const FingerMageCurse: 84; + const NoManaReg: 85; + const JustHit: 86; + const SlowMissiles: 87; + const ShiverArmor: 88; + const BattleCry: 89; + const Blue: 90; + const Red: 91; + const DeathDelay: 92; + const Valkyrie: 93; + const Frenzy: 94; + const Berserk: 95; + const Revive: 96; + const ItemFullSet: 97; + const SourceUnit: 98; + const Redeemed: 99; + const HealthPot: 100; + const HolyShield: 101; + const JustPortaled: 102; + const MonFrenzy: 103; + const CorpseNoDraw: 104; + const Alignment: 105; + const ManaPot: 106; + const Shatter: 107; + const SyncWarped: 108; + const ConversionSave: 109; + const Pregnat: 110; + const Rabies: 112; + const DefenceCurse: 113; + const BloodMana: 114; + const Burning: 115; + const DragonFlight: 116; + const Maul: 117; + const CorpseNoSelect: 118; + const ShadowWarrior: 119; + const FeralRage: 120; + const SkillDelay: 121; + const ProgressiveDamage: 122; + const ProgressiveSteal: 123; + const ProgressiveOther: 124; + const ProgressiveFire: 125; + const ProgressiveCold: 126; + const ProgressiveLighting: 127; + const ShrineArmor: 128; + const ShrineCombat: 129; + const ShrineResLighting: 130; + const ShrineResFire: 131; + const ShrineResCold: 132; + const ShrineResPoison: 133; + const ShrineSkill: 134; + const ShrineManaRegen: 135; + const ShrineStamina: 136; + const ShrineExperience: 137; + const FenrisRage: 138; + const Wolf: 139; + const Wearwolf: 139; + const Bear: 140; + const Wearbear: 140; + const Bloodlust: 141; + const ChangeClass: 142; + const Attached: 143; + const Hurricane: 144; + const Armageddon: 145; + const Invis: 146; + const Barbs: 147; + const HeartofWolverine: 148; + const OakSage: 149; + const VineBeast: 150; + const CycloneArmor: 151; + const ClawMastery: 152; + const CloakofShadows: 153; + const Recyled: 154; + const WeaponBlock: 155; + const Cloaked: 156; + const Quickness: 157; // Internal name + const BurstofSpeed: 157; // External name + const BladeShield: 158; + const Fade: 159; + const RestInPeace: 172; + const Glowing: 175; + const Delerium: 177; + const Antidote: 178; + const Thawing: 179; + const StaminaPot: 180; + } - export namespace enchant { - const RandName: 1; - const HpMultiply: 2; - const AddLightRadius: 3; - const AddMLvl: 4; - const ExtraStrong: 5; - const ExtraFast: 6; - const Cursed: 7; - const MagicResistant: 8; - const FireEnchanted: 9; - const PoisonDeath: 10; - const InsectDeath: 11; - const ChainLightingDeath: 12; - const IgnoreTargetDefense: 13; - const UnknownMod: 14; - const KillMinionsDeath: 15; - const ChampMods: 16; - const LightningEnchanted: 17; - const ColdEnchanted: 18; - const UnusedMercMod: 19; - const ChargedBoltWhenStruck: 20; - const TempSummoned: 21; - const QuestMod: 22; - const PoisonField: 23; - const Thief: 24; - const ManaBurn: 25; - const Teleportation: 26; - const SpectralHit: 27; - const StoneSkin: 28; - const MultipleShots: 29; - const Aura: 30; - const CorpseExplosion: 31; - const FireExplosionOnDeath: 32; // not sure what the difference is between this and 9 - const FreezeOnDeath: 33; - const SelfResurrect: 34; - const IceShatter: 35; - const ChampStoned: 36; - const ChampStats: 37; - const ChampCurseImmune: 38; - } + export namespace enchant { + const RandName: 1; + const HpMultiply: 2; + const AddLightRadius: 3; + const AddMLvl: 4; + const ExtraStrong: 5; + const ExtraFast: 6; + const Cursed: 7; + const MagicResistant: 8; + const FireEnchanted: 9; + const PoisonDeath: 10; + const InsectDeath: 11; + const ChainLightingDeath: 12; + const IgnoreTargetDefense: 13; + const UnknownMod: 14; + const KillMinionsDeath: 15; + const ChampMods: 16; + const LightningEnchanted: 17; + const ColdEnchanted: 18; + const UnusedMercMod: 19; + const ChargedBoltWhenStruck: 20; + const TempSummoned: 21; + const QuestMod: 22; + const PoisonField: 23; + const Thief: 24; + const ManaBurn: 25; + const Teleportation: 26; + const SpectralHit: 27; + const StoneSkin: 28; + const MultipleShots: 29; + const Aura: 30; + const CorpseExplosion: 31; + const FireExplosionOnDeath: 32; // not sure what the difference is between this and 9 + const FreezeOnDeath: 33; + const SelfResurrect: 34; + const IceShatter: 35; + const ChampStoned: 36; + const ChampStats: 37; + const ChampCurseImmune: 38; + } - // unit stats - export namespace stats { - const StunLength: 66; - const VelocityPercent: 67; - const OtherAnimrate: 69; - const HpRegen: 74; + // unit stats + export namespace stats { + const StunLength: 66; + const VelocityPercent: 67; + const OtherAnimrate: 69; + const HpRegen: 74; - const LastBlockFrame: 95; - const State: 98; - const MonsterPlayerCount: 100; + const LastBlockFrame: 95; + const State: 98; + const MonsterPlayerCount: 100; - const CurseResistance: 109; - const IronMaidenLevel: 129; - const LifeTapLevel: 130; + const CurseResistance: 109; + const IronMaidenLevel: 129; + const LifeTapLevel: 130; - const Alignment: 172; - const Target0: 173; - const Target1: 174; - const GoldLost: 175; - const MinimumRequiredLevel: 176; - const ConversionLevel: 176; - const ConversionMaxHp: 177; - const UnitDooverlay: 178; - const AttackVsMontype: 179; - const DamageVsMontype: 180; + const Alignment: 172; + const Target0: 173; + const Target1: 174; + const GoldLost: 175; + const MinimumRequiredLevel: 176; + const ConversionLevel: 176; + const ConversionMaxHp: 177; + const UnitDooverlay: 178; + const AttackVsMontype: 179; + const DamageVsMontype: 180; - const ArmorOverridePercent: 182; - const FireLength: 315; - const BurningMin: 316; - const BurningMax: 317; - const ProgressiveDamage: 318; - const ProgressiveSteal: 319; - const ProgressiveOther: 320; - const ProgressiveFire: 321; - const ProgressiveCold: 322; - const ProgressiveLightning: 323; - const ProgressiveTohit: 325; - const PoisonCount: 326; - const DamageFramerate: 327; - const PierceIdx: 328; + const ArmorOverridePercent: 182; + const FireLength: 315; + const BurningMin: 316; + const BurningMax: 317; + const ProgressiveDamage: 318; + const ProgressiveSteal: 319; + const ProgressiveOther: 320; + const ProgressiveFire: 321; + const ProgressiveCold: 322; + const ProgressiveLightning: 323; + const ProgressiveTohit: 325; + const PoisonCount: 326; + const DamageFramerate: 327; + const PierceIdx: 328; - const ModifierListSkill: 350; - const ModifierListLevel: 351; + const ModifierListSkill: 350; + const ModifierListLevel: 351; - const LastSentHpPct: 352; - const SourceUnitType: 353; - const SourceUnitId: 354; + const LastSentHpPct: 352; + const SourceUnitType: 353; + const SourceUnitId: 354; - const SkillThornsPercent: 131; - const SkillBoneArmor: 132; - const SkillBoneArmorMax: 133; - const SkillFade: 181; - const SkillPoisonOverrideLength: 101; - const SkillBypassUndead: 103; - const SkillBypassDemons: 104; - const SkillBypassBeasts: 106; - const SkillHandofAthena: 161; - const SkillStaminaPercent: 162; - const SkillPassiveStaminaPercent: 163; - const SkillConcentration: 164; - const SkillEnchant: 165; - const SkillPierce: 166; - const SkillConviction: 167; - const SkillChillingArmor: 168; - const SkillFrenzy: 169; - const SkillDecrepify: 170; - const SkillArmorPercent: 171; + const SkillThornsPercent: 131; + const SkillBoneArmor: 132; + const SkillBoneArmorMax: 133; + const SkillFade: 181; + const SkillPoisonOverrideLength: 101; + const SkillBypassUndead: 103; + const SkillBypassDemons: 104; + const SkillBypassBeasts: 106; + const SkillHandofAthena: 161; + const SkillStaminaPercent: 162; + const SkillPassiveStaminaPercent: 163; + const SkillConcentration: 164; + const SkillEnchant: 165; + const SkillPierce: 166; + const SkillConviction: 167; + const SkillChillingArmor: 168; + const SkillFrenzy: 169; + const SkillDecrepify: 170; + const SkillArmorPercent: 171; - const Strength: 0; - const Energy: 1; - const Dexterity: 2; - const Vitality: 3; - const StatPts: 4; - const NewSkills: 5; - const HitPoints: 6; - const MaxHp: 7; - const Mana: 8; - const MaxMana: 9; - const Stamina: 10; - const MaxStamina: 11; - const Level: 12; - const Experience: 13; - const Gold: 14; - const GoldBank: 15; - const ArmorPercent: 16; - const MaxDamagePercent: 17; - const MinDamagePercent: 18; - const EnhancedDamage: 18; - const ToHit: 19; - const ToBlock: 20; - const MinDamage: 21; - const MaxDamage: 22; - const SecondaryMinDamage: 23; - const SecondaryMaxDamage: 24; - const DamagePercent: 25; - const ManaRecovery: 26; - const ManaRecoveryBonus: 27; - const StaminaRecoveryBonus: 28; - const LastExp: 29; - const NextExp: 30; - const ArmorClass: 31; - const Defense: 31; - const ArmorClassVsMissile: 32; - const ArmorClassVsHth: 33; - const NormalDamageReduction: 34; - const MagicDamageReduction: 35; - const DamageResist: 36; - const MagicResist: 37; - const MaxMagicResist: 38; - const FireResist: 39; - const MaxFireResist: 40; - const LightResist: 41; - const LightningResist: 41; - const MaxLightResist: 42; - const ColdResist: 43; - const MaxColdResist: 44; - const PoisonResist: 45; - const MaxPoisonResist: 46; - const DamageAura: 47; - const FireMinDamage: 48; - const FireMaxDamage: 49; - const LightMinDamage: 50; - const LightMaxDamage: 51; - const MagicMinDamage: 52; - const MagicMaxDamage: 53; - const ColdMinDamage: 54; - const ColdMaxDamage: 55; - const ColdLength: 56; - const PoisonMinDamage: 57; - const PoisonMaxDamage: 58; - const PoisonLength: 59; - const LifeDrainMinDamage: 60; - const LifeLeech: 60; - const LifeDrainMaxDamage: 61; - const ManaDrainMinDamage: 62; - const ManaLeech: 62; - const ManaDrainMaxDamage: 63; - const StaminaDrainMinDamage: 64; - const StaminaDrainMaxDamage: 65; - const AttackRate: 68; - const PreviousSkillRight: 181; - const PreviousSkillMiddle: 182; - const PreviousSkillLeft: 183; - const PassiveFireMastery: 329; - const PassiveLightningMastery: 330; - const PassiveColdMastery: 331; - const PassivePoisonMastery: 332; - const PassiveFirePierce: 333; - const PassiveLightningPierce: 334; - const PassiveColdPierce: 335; - const PassivePoisonPierce: 336; - const PassiveCriticalStrike: 337; - const PassiveDodge: 338; - const PassiveAvoid: 339; - const PassiveEvade: 340; - const PassiveWarmth: 341; - const PassiveMasteryMeleeTh: 342; - const PassiveMasteryMeleeDmg: 343; - const PassiveMasteryMeleeCrit: 344; - const PassiveMasteryThrowTh: 345; - const PassiveMasteryThrowDmg: 346; - const PassiveMasteryThrowCrit: 347; - const PassiveWeaponBlock: 348; - const PassiveSummonResist: 349; - const PassiveMagMastery: 357; - const PassiveMagPierce: 358; - const Quantity: 70; - const Value: 71; - const Durability: 72; - const MaxDurability: 73; - const MaxDurabilityPercent: 75; - const MaxHpPercent: 76; - const MaxManaPercent: 77; - const AttackerTakesDamage: 78; - const GoldBonus: 79; - const MagicBonus: 80; - const Knockback: 81; - const TimeDuration: 82; - const AddClassSkills: 83; - const AddExperience: 85; - const HealAfterKill: 86; - const ReducedPrices: 87; - const DoubleHerbDuration: 88; - const LightRadius: 89; - const LightColor: 90; - const ReqPercent: 91; - const LevelReq: 92; - const FasterAttackRate: 93; - const IAS: 93; - const LevelReqPct: 94; - const FasterMoveVelocity: 96; - const FRW: 96; - const NonClassSkill: 97; - const OSkill: 97; - const FasterGetHitRate: 99; - const FHR: 99; - const FasterBlockRate: 102; - const FBR: 102; - const FasterCastRate: 105; - const FCR: 105; - const SingleSkill: 107; - const RestinPeace: 108; - const PoisonLengthResist: 110; - const NormalDamage: 111; - const Howl: 112; - const Stupidity: 113; - const DamagetoMana: 114; - const IgnoreTargetAc: 115; - const IgnoreTargetDefense: 115; - const FractionalTargetAc: 116; - const PreventHeal: 117; - const HalfFreezeDuration: 118; - const ToHitPercent: 119; - const DamageTargetAc: 120; - const DemonDamagePercent: 121; - const UndeadDamagePercent: 122; - const DemontoHit: 123; - const UndeadtoHit: 124; - const Throwable: 125; - const ElemSkill: 126; - const AllSkills: 127; - const AttackerTakesLightDamage: 128; - const Freeze: 134; - const OpenWounds: 135; - const CrushingBlow: 136; - const KickDamage: 137; - const ManaAfterKill: 138; - const HealAfterDemonKill: 139; - const ExtraBlood: 140; - const DeadlyStrike: 141; - const AbsorbFirePercent: 142; - const AbsorbFire: 143; - const AbsorbLightPercent: 144; - const AbsorbLight: 145; - const AbsorbMagicPercent: 146; - const AbsorbMagic: 147; - const AbsorbColdPercent: 148; - const AbsorbCold: 149; - const AbsorbSlash: 262; - const AbsorbCrush: 263; - const AbsorbThrust: 264; - const AbsorbSlashPercent: 265; - const AbsorbCrushPercent: 266; - const AbsorbThrustPercent: 267; - const Slow: 150; - const Indestructible: 152; - const CannotbeFrozen: 153; - const StaminaDrainPct: 154; - const Reanimate: 155; - const Pierce: 156; - const MagicArrow: 157; - const ExplosiveArrow: 158; - const ThrowMinDamage: 159; - const ThrowMaxDamage: 160; - const AddSkillTab: 188; - const NumSockets: 194; - const SkillOnAura: 151; - const SkillOnAttack: 195; - const SkillOnKill: 196; - const SkillOnDeath: 197; - const SkillOnHit: 198; - const SkillOnStrike: 198; - const SkillOnLevelUp: 199; - const SkillOnGetHit: 201; - const SkillWhenStruck: 201; - const ChargedSkill: 204; - const PerLevelArmor: 214; - const PerLevelArmorPercent: 215; - const PerLevelHp: 216; - const PerLevelMana: 217; - const PerLevelMaxDamage: 218; - const PerLevelMaxDamagePercent: 219; - const PerLevelStrength: 220; - const PerLevelDexterity: 221; - const PerLevelEnergy: 222; - const PerLevelVitality: 223; - const PerLevelTohit: 224; - const PerLevelTohitPercent: 225; - const PerLevelColdDamageMax: 226; - const PerLevelFireDamageMax: 227; - const PerLevelLtngDamageMax: 228; - const PerLevelPoisDamageMax: 229; - const PerLevelResistCold: 230; - const PerLevelResistFire: 231; - const PerLevelResistLtng: 232; - const PerLevelResistPois: 233; - const PerLevelAbsorbCold: 234; - const PerLevelAbsorbFire: 235; - const PerLevelAbsorbLtng: 236; - const PerLevelAbsorbPois: 237; - const PerLevelThorns: 238; - const PerLevelFindGold: 239; - const PerLevelFindMagic: 240; - const PerLevelRegenstamina: 241; - const PerLevelStamina: 242; - const PerLevelDamageDemon: 243; - const PerLevelDamageUndead: 244; - const PerLevelTohitDemon: 245; - const PerLevelTohitUndead: 246; - const PerLevelCrushingblow: 247; - const PerLevelOpenwounds: 248; - const PerLevelKickDamage: 249; - const PerLevelDeadlystrike: 250; - const PerLevelFindGems: 251; - const ReplenishDurability: 252; - const ReplenishQuantity: 253; - const ExtraStack: 254; - const Find: 255; - const SlashDamage: 256; - const SlashDamagePercent: 257; - const CrushDamage: 258; - const CrushDamagePercent: 259; - const ThrustDamage: 260; - const ThrustDamagePercent: 261; - const ArmorByTime: 268; - const ArmorPercentByTime: 269; - const HpByTime: 270; - const ManaByTime: 271; - const MaxDamageByTime: 272; - const MaxDamagePercentByTime: 273; - const StrengthByTime: 274; - const DexterityByTime: 275; - const EnergyByTime: 276; - const VitalityByTime: 277; - const TohitByTime: 278; - const TohitPercentByTime: 279; - const ColdDamageMaxByTime: 280; - const FireDamageMaxByTime: 281; - const LtngDamageMaxByTime: 282; - const PoisDamageMaxByTime: 283; - const ResistColdByTime: 284; - const ResistFireByTime: 285; - const ResistLtngByTime: 286; - const ResistPoisByTime: 287; - const AbsorbColdByTime: 288; - const AbsorbFireByTime: 289; - const AbsorbLtngByTime: 290; - const AbsorbPoisByTime: 291; - const FindGoldByTime: 292; - const FindMagicByTime: 293; - const RegenstaminaByTime: 294; - const StaminaByTime: 295; - const DamageDemonByTime: 296; - const DamageUndeadByTime: 297; - const TohitDemonByTime: 298; - const TohitUndeadByTime: 299; - const CrushingBlowByTime: 300; - const OpenWoundsByTime: 301; - const KickDamageByTime: 302; - const DeadlyStrikeByTime: 303; - const FindGemsByTime: 304; - const PierceCold: 305; - const PierceFire: 306; - const PierceLtng: 307; - const PiercePois: 308; - const DamageVsMonster: 309; - const DamagePercentVsMonster: 310; - const TohitVsMonster: 311; - const TohitPercentVsMonster: 312; - const AcVsMonster: 313; - const AcPercentVsMonster: 314; - const ExtraCharges: 324; - const QuestDifficulty: 356; + const Strength: 0; + const Energy: 1; + const Dexterity: 2; + const Vitality: 3; + const StatPts: 4; + const NewSkills: 5; + const HitPoints: 6; + const MaxHp: 7; + const Mana: 8; + const MaxMana: 9; + const Stamina: 10; + const MaxStamina: 11; + const Level: 12; + const Experience: 13; + const Gold: 14; + const GoldBank: 15; + const ArmorPercent: 16; + const MaxDamagePercent: 17; + const MinDamagePercent: 18; + const EnhancedDamage: 18; + const ToHit: 19; + const ToBlock: 20; + const MinDamage: 21; + const MaxDamage: 22; + const SecondaryMinDamage: 23; + const SecondaryMaxDamage: 24; + const DamagePercent: 25; + const ManaRecovery: 26; + const ManaRecoveryBonus: 27; + const StaminaRecoveryBonus: 28; + const LastExp: 29; + const NextExp: 30; + const ArmorClass: 31; + const Defense: 31; + const ArmorClassVsMissile: 32; + const ArmorClassVsHth: 33; + const NormalDamageReduction: 34; + const MagicDamageReduction: 35; + const DamageResist: 36; + const MagicResist: 37; + const MaxMagicResist: 38; + const FireResist: 39; + const MaxFireResist: 40; + const LightResist: 41; + const LightningResist: 41; + const MaxLightResist: 42; + const ColdResist: 43; + const MaxColdResist: 44; + const PoisonResist: 45; + const MaxPoisonResist: 46; + const DamageAura: 47; + const FireMinDamage: 48; + const FireMaxDamage: 49; + const LightMinDamage: 50; + const LightMaxDamage: 51; + const MagicMinDamage: 52; + const MagicMaxDamage: 53; + const ColdMinDamage: 54; + const ColdMaxDamage: 55; + const ColdLength: 56; + const PoisonMinDamage: 57; + const PoisonMaxDamage: 58; + const PoisonLength: 59; + const LifeDrainMinDamage: 60; + const LifeLeech: 60; + const LifeDrainMaxDamage: 61; + const ManaDrainMinDamage: 62; + const ManaLeech: 62; + const ManaDrainMaxDamage: 63; + const StaminaDrainMinDamage: 64; + const StaminaDrainMaxDamage: 65; + const AttackRate: 68; + const PreviousSkillRight: 181; + const PreviousSkillMiddle: 182; + const PreviousSkillLeft: 183; + const PassiveFireMastery: 329; + const PassiveLightningMastery: 330; + const PassiveColdMastery: 331; + const PassivePoisonMastery: 332; + const PassiveFirePierce: 333; + const PassiveLightningPierce: 334; + const PassiveColdPierce: 335; + const PassivePoisonPierce: 336; + const PassiveCriticalStrike: 337; + const PassiveDodge: 338; + const PassiveAvoid: 339; + const PassiveEvade: 340; + const PassiveWarmth: 341; + const PassiveMasteryMeleeTh: 342; + const PassiveMasteryMeleeDmg: 343; + const PassiveMasteryMeleeCrit: 344; + const PassiveMasteryThrowTh: 345; + const PassiveMasteryThrowDmg: 346; + const PassiveMasteryThrowCrit: 347; + const PassiveWeaponBlock: 348; + const PassiveSummonResist: 349; + const PassiveMagMastery: 357; + const PassiveMagPierce: 358; + const Quantity: 70; + const Value: 71; + const Durability: 72; + const MaxDurability: 73; + const MaxDurabilityPercent: 75; + const MaxHpPercent: 76; + const MaxManaPercent: 77; + const AttackerTakesDamage: 78; + const GoldBonus: 79; + const MagicBonus: 80; + const Knockback: 81; + const TimeDuration: 82; + const AddClassSkills: 83; + const AddExperience: 85; + const HealAfterKill: 86; + const ReducedPrices: 87; + const DoubleHerbDuration: 88; + const LightRadius: 89; + const LightColor: 90; + const ReqPercent: 91; + const LevelReq: 92; + const FasterAttackRate: 93; + const IAS: 93; + const LevelReqPct: 94; + const FasterMoveVelocity: 96; + const FRW: 96; + const NonClassSkill: 97; + const OSkill: 97; + const FasterGetHitRate: 99; + const FHR: 99; + const FasterBlockRate: 102; + const FBR: 102; + const FasterCastRate: 105; + const FCR: 105; + const SingleSkill: 107; + const RestinPeace: 108; + const PoisonLengthResist: 110; + const NormalDamage: 111; + const Howl: 112; + const Stupidity: 113; + const DamagetoMana: 114; + const IgnoreTargetAc: 115; + const IgnoreTargetDefense: 115; + const FractionalTargetAc: 116; + const PreventHeal: 117; + const HalfFreezeDuration: 118; + const ToHitPercent: 119; + const DamageTargetAc: 120; + const DemonDamagePercent: 121; + const UndeadDamagePercent: 122; + const DemontoHit: 123; + const UndeadtoHit: 124; + const Throwable: 125; + const ElemSkill: 126; + const AllSkills: 127; + const AttackerTakesLightDamage: 128; + const Freeze: 134; + const OpenWounds: 135; + const CrushingBlow: 136; + const KickDamage: 137; + const ManaAfterKill: 138; + const HealAfterDemonKill: 139; + const ExtraBlood: 140; + const DeadlyStrike: 141; + const AbsorbFirePercent: 142; + const AbsorbFire: 143; + const AbsorbLightPercent: 144; + const AbsorbLight: 145; + const AbsorbMagicPercent: 146; + const AbsorbMagic: 147; + const AbsorbColdPercent: 148; + const AbsorbCold: 149; + const AbsorbSlash: 262; + const AbsorbCrush: 263; + const AbsorbThrust: 264; + const AbsorbSlashPercent: 265; + const AbsorbCrushPercent: 266; + const AbsorbThrustPercent: 267; + const Slow: 150; + const Indestructible: 152; + const CannotbeFrozen: 153; + const StaminaDrainPct: 154; + const Reanimate: 155; + const Pierce: 156; + const MagicArrow: 157; + const ExplosiveArrow: 158; + const ThrowMinDamage: 159; + const ThrowMaxDamage: 160; + const AddSkillTab: 188; + const NumSockets: 194; + const SkillOnAura: 151; + const SkillOnAttack: 195; + const SkillOnKill: 196; + const SkillOnDeath: 197; + const SkillOnHit: 198; + const SkillOnStrike: 198; + const SkillOnLevelUp: 199; + const SkillOnGetHit: 201; + const SkillWhenStruck: 201; + const ChargedSkill: 204; + const PerLevelArmor: 214; + const PerLevelArmorPercent: 215; + const PerLevelHp: 216; + const PerLevelMana: 217; + const PerLevelMaxDamage: 218; + const PerLevelMaxDamagePercent: 219; + const PerLevelStrength: 220; + const PerLevelDexterity: 221; + const PerLevelEnergy: 222; + const PerLevelVitality: 223; + const PerLevelTohit: 224; + const PerLevelTohitPercent: 225; + const PerLevelColdDamageMax: 226; + const PerLevelFireDamageMax: 227; + const PerLevelLtngDamageMax: 228; + const PerLevelPoisDamageMax: 229; + const PerLevelResistCold: 230; + const PerLevelResistFire: 231; + const PerLevelResistLtng: 232; + const PerLevelResistPois: 233; + const PerLevelAbsorbCold: 234; + const PerLevelAbsorbFire: 235; + const PerLevelAbsorbLtng: 236; + const PerLevelAbsorbPois: 237; + const PerLevelThorns: 238; + const PerLevelFindGold: 239; + const PerLevelFindMagic: 240; + const PerLevelRegenstamina: 241; + const PerLevelStamina: 242; + const PerLevelDamageDemon: 243; + const PerLevelDamageUndead: 244; + const PerLevelTohitDemon: 245; + const PerLevelTohitUndead: 246; + const PerLevelCrushingblow: 247; + const PerLevelOpenwounds: 248; + const PerLevelKickDamage: 249; + const PerLevelDeadlystrike: 250; + const PerLevelFindGems: 251; + const ReplenishDurability: 252; + const ReplenishQuantity: 253; + const ExtraStack: 254; + const Find: 255; + const SlashDamage: 256; + const SlashDamagePercent: 257; + const CrushDamage: 258; + const CrushDamagePercent: 259; + const ThrustDamage: 260; + const ThrustDamagePercent: 261; + const ArmorByTime: 268; + const ArmorPercentByTime: 269; + const HpByTime: 270; + const ManaByTime: 271; + const MaxDamageByTime: 272; + const MaxDamagePercentByTime: 273; + const StrengthByTime: 274; + const DexterityByTime: 275; + const EnergyByTime: 276; + const VitalityByTime: 277; + const TohitByTime: 278; + const TohitPercentByTime: 279; + const ColdDamageMaxByTime: 280; + const FireDamageMaxByTime: 281; + const LtngDamageMaxByTime: 282; + const PoisDamageMaxByTime: 283; + const ResistColdByTime: 284; + const ResistFireByTime: 285; + const ResistLtngByTime: 286; + const ResistPoisByTime: 287; + const AbsorbColdByTime: 288; + const AbsorbFireByTime: 289; + const AbsorbLtngByTime: 290; + const AbsorbPoisByTime: 291; + const FindGoldByTime: 292; + const FindMagicByTime: 293; + const RegenstaminaByTime: 294; + const StaminaByTime: 295; + const DamageDemonByTime: 296; + const DamageUndeadByTime: 297; + const TohitDemonByTime: 298; + const TohitUndeadByTime: 299; + const CrushingBlowByTime: 300; + const OpenWoundsByTime: 301; + const KickDamageByTime: 302; + const DeadlyStrikeByTime: 303; + const FindGemsByTime: 304; + const PierceCold: 305; + const PierceFire: 306; + const PierceLtng: 307; + const PiercePois: 308; + const DamageVsMonster: 309; + const DamagePercentVsMonster: 310; + const TohitVsMonster: 311; + const TohitPercentVsMonster: 312; + const AcVsMonster: 313; + const AcPercentVsMonster: 314; + const ExtraCharges: 324; + const QuestDifficulty: 356; - // doesn't exist but define for prototypes - const AllRes: 555; - } + // doesn't exist but define for prototypes + const AllRes: 555; + } - // unit info - export namespace unittype { - const Player: 0; - const NPC: 1; - const Monster: 1; - const Object: 2; - const Missile: 3; - const Item: 4; - const Stairs: 5; // const ToDo: might be more as stairs - } + // unit info + export namespace unittype { + const Player: 0; + const NPC: 1; + const Monster: 1; + const Object: 2; + const Missile: 3; + const Item: 4; + const Stairs: 5; // const ToDo: might be more as stairs + } - export namespace player { - export namespace flag { - const Ignore: 2; - const Hostile: 8; - } - export namespace slot { - const Main: 0; - const Secondary: 1 - } - export namespace move { - const Walk: 0; - const Run: 1 - } - export namespace mode { // sdk.player.mode. - const Death: 0; - const StandingOutsideTown: 1; - const Walking: 2; - const Running: 3; - const GettingHit: 4; - const StandingInTown: 5; - const WalkingInTown: 6; - const Attacking1: 7; - const Attacking2: 8; - const Blocking: 9; - const CastingSkill: 10; - const ThrowingItem: 11; - const Kicking: 12; - const UsingSkill1: 13; - const UsingSkill2: 14; - const UsingSkill3: 15; - const UsingSkill4: 16; - const Dead: 17; - const SkillActionSequence: 18; - const KnockedBack: 19; - } - namespace _class { - const Amazon: 0; - const Sorceress: 1; - const Necromancer: 2; - const Paladin: 3; - const Barbarian: 4; - const Druid: 5; - const Assassin: 6; + export namespace player { + export namespace flag { + const Ignore: 2; + const Hostile: 8; + } + export namespace slot { + const Main: 0; + const Secondary: 1 + } + export namespace move { + const Walk: 0; + const Run: 1 + } + export namespace mode { // sdk.player.mode. + const Death: 0; + const StandingOutsideTown: 1; + const Walking: 2; + const Running: 3; + const GettingHit: 4; + const StandingInTown: 5; + const WalkingInTown: 6; + const Attacking1: 7; + const Attacking2: 8; + const Blocking: 9; + const CastingSkill: 10; + const ThrowingItem: 11; + const Kicking: 12; + const UsingSkill1: 13; + const UsingSkill2: 14; + const UsingSkill3: 15; + const UsingSkill4: 16; + const Dead: 17; + const SkillActionSequence: 18; + const KnockedBack: 19; + } + namespace _class { + const Amazon: 0; + const Sorceress: 1; + const Necromancer: 2; + const Paladin: 3; + const Barbarian: 4; + const Druid: 5; + const Assassin: 6; - const nameOf: (classid: 0 | 1 | 2 | 3 | 4 | 5 | 6) => "Amazon" | "Sorceress" | "Necromancer" | "Paladin" | "Barbarian" | "Druid" | "Assassin" | false; - } - export { _class as class }; - } + const nameOf: (classid: 0 | 1 | 2 | 3 | 4 | 5 | 6) => "Amazon" | "Sorceress" | "Necromancer" | "Paladin" | "Barbarian" | "Druid" | "Assassin" | false; + } + export { _class as class }; + } - export namespace npcs { - // same as monsters but more clear to use units.npcs.mode - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15 - } + export namespace npcs { + // same as monsters but more clear to use units.npcs.mode + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } - const Akara: 148; - const Alkor: 254; - const Asheara: 252; - const WarrivAct1: 155; - const WarrivAct2: 175; - const Atma: 176; - const Tyrael: 367; - const Tyrael2: 251; - const Tyrael3: 521; - const Charsi: 154; - const DeckardCain1: 146; - const DeckardCain2: 244; - const DeckardCain3: 245; - const DeckardCain4: 246; - const DeckardCain5: 265; - const DeckardCain6: 520; - const Drognan: 177; - const Elzix: 199; - const Fara: 178; - const Gheed: 147; - const Greiz: 198; - const Halbu: 257; - const Hratli: 253; - const Jamella: 405; - const Jerhyn: 201; - const Kaelan: 331; - const Kashya: 150; - const Larzuk: 511; - const Lysander: 202; - const Malah: 513; - const Meshif: 210; - const Meshif2: 264; - const Natalya: 297; - const Ormus: 255; - const NihlathakNPC: 526; - const Qualkehk: 515; - const RogueScout: 270; - const TempleGuard1: 52; - const TempleGuard2: 665; - const TempleGuard3: 666; - const Townguard1: 535; - const Townguard2: 536; - } + const Akara: 148; + const Alkor: 254; + const Asheara: 252; + const WarrivAct1: 155; + const WarrivAct2: 175; + const Atma: 176; + const Tyrael: 367; + const Tyrael2: 251; + const Tyrael3: 521; + const Charsi: 154; + const DeckardCain1: 146; + const DeckardCain2: 244; + const DeckardCain3: 245; + const DeckardCain4: 246; + const DeckardCain5: 265; + const DeckardCain6: 520; + const Drognan: 177; + const Elzix: 199; + const Fara: 178; + const Gheed: 147; + const Greiz: 198; + const Halbu: 257; + const Hratli: 253; + const Jamella: 405; + const Jerhyn: 201; + const Kaelan: 331; + const Kashya: 150; + const Larzuk: 511; + const Lysander: 202; + const Malah: 513; + const Meshif: 210; + const Meshif2: 264; + const Natalya: 297; + const Ormus: 255; + const NihlathakNPC: 526; + const Qualkehk: 515; + const RogueScout: 270; + const TempleGuard1: 52; + const TempleGuard2: 665; + const TempleGuard3: 666; + const Townguard1: 535; + const Townguard2: 536; + } - export namespace objects { - namespace mode { - const Inactive: 0; - const Interacted: 1; - const Active: 2; - } + export namespace objects { + namespace mode { + const Inactive: 0; + const Interacted: 1; + const Active: 2; + } - const chestIds: [ - 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 - ]; + const chestIds: [ + 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 + ]; - // act1 - const MoldyTome: 8; - const A1TownFire: 39; - const A1Waypoint: 119; - const StoneAlpha: 17; - const StoneBeta: 18; - const StoneGamma: 19; - const StoneDelta: 20; - const StoneLambda: 21; - const StoneTheta: 22; - const CainsJail: 26; - const InifussTree: 30; - const Malus: 108; + // act1 + const MoldyTome: 8; + const A1TownFire: 39; + const A1Waypoint: 119; + const StoneAlpha: 17; + const StoneBeta: 18; + const StoneGamma: 19; + const StoneDelta: 20; + const StoneLambda: 21; + const StoneTheta: 22; + const CainsJail: 26; + const InifussTree: 30; + const Malus: 108; - // act 2 - const A2Waypoint: 156; - const A2UndergroundUpStairs: 22; - const TrapDoorA2: 74; // ancienttunnel/sewers act 2 - const DoorbyDockAct2: 75; // incorrect ? const TODO: figure out what 75 really corresponds to since the door is obj type 5 with classid 20 - const PortaltoDurielsLair: 100; - const HoradricStaffHolder: 152; - const ArcaneSanctuaryPortal: 298; - const HoradricCubeChest: 354; - const HoradricScrollChest: 355; - const Journal: 357; + // act 2 + const A2Waypoint: 156; + const A2UndergroundUpStairs: 22; + const TrapDoorA2: 74; // ancienttunnel/sewers act 2 + const DoorbyDockAct2: 75; // incorrect ? const TODO: figure out what 75 really corresponds to since the door is obj type 5 with classid 20 + const PortaltoDurielsLair: 100; + const HoradricStaffHolder: 152; + const ArcaneSanctuaryPortal: 298; + const HoradricCubeChest: 354; + const HoradricScrollChest: 355; + const Journal: 357; - // act 3 - const A3Waypoint: 237; - const ForestAltar: 81; - const LamEsensTome: 193; - const SewerStairsA3: 366; - const SewerLever: 367; - const DuranceEntryStairs: 386; - const RedPortalToAct4: 342; - const CompellingOrb: 404; + // act 3 + const A3Waypoint: 237; + const ForestAltar: 81; + const LamEsensTome: 193; + const SewerStairsA3: 366; + const SewerLever: 367; + const DuranceEntryStairs: 386; + const RedPortalToAct4: 342; + const CompellingOrb: 404; - // act 4 - const A4Waypoint: 398; - const SealGlow: 131; - const DiabloStar: 255; - const DiabloSealInfector: 392; - const DiabloSealInfector2: 393; - const DiabloSealSeis: 394; - const DiabloSealVizier: 396; - const DiabloSealVizier2: 395; - const RedPortalToAct5: 566; // The one of tyreal + // act 4 + const A4Waypoint: 398; + const SealGlow: 131; + const DiabloStar: 255; + const DiabloSealInfector: 392; + const DiabloSealInfector2: 393; + const DiabloSealSeis: 394; + const DiabloSealVizier: 396; + const DiabloSealVizier2: 395; + const RedPortalToAct5: 566; // The one of tyreal - // act 5 - const A5Waypoint: 429; - const SideCavesA5: 75; // FrozenRiver, DrifterCavern; IcyCellar - const Act5Gate: 449; - const KorlictheProtectorStatue: 474; - const TalictheDefenderStatue: 475; - const MadawctheGuardianStatue: 476; - const AncientsAltar: 546; - const ArreatEnterAncientsWay: 564; - const ArreatEnterWorldstone: 547; - //const AncientsDoor: 547; - const AncientsDoor: 547; // Worldstone keep lvl 1 - const FrozenAnya: 558; - const FrozenAnyasPlatform: 460; - const NihlathaksPlatform: 462; - const WorldstonePortal: 563; + // act 5 + const A5Waypoint: 429; + const SideCavesA5: 75; // FrozenRiver, DrifterCavern; IcyCellar + const Act5Gate: 449; + const KorlictheProtectorStatue: 474; + const TalictheDefenderStatue: 475; + const MadawctheGuardianStatue: 476; + const AncientsAltar: 546; + const ArreatEnterAncientsWay: 564; + const ArreatEnterWorldstone: 547; + //const AncientsDoor: 547; + const AncientsDoor: 547; // Worldstone keep lvl 1 + const FrozenAnya: 558; + const FrozenAnyasPlatform: 460; + const NihlathaksPlatform: 462; + const WorldstonePortal: 563; - const FrigidHighlandsChest: 455; - const IcyCellarChest: 397; + const FrigidHighlandsChest: 455; + const IcyCellarChest: 397; - const SmallSparklyChest: 397; - const LargeSparklyChest: 455; - const SuperChest: 580; + const SmallSparklyChest: 397; + const LargeSparklyChest: 455; + const SuperChest: 580; - // misc - const BubblingPoolofBlood: 82; - const HornShrine: 83; - const Stash: 267; - const BluePortal: 59; - const RedPortal: 60; - const Smoke: 401; - } + // misc + const BubblingPoolofBlood: 82; + const HornShrine: 83; + const Stash: 267; + const BluePortal: 59; + const RedPortal: 60; + const Smoke: 401; + } - export namespace exits { - namespace type { - const WalkThrough: 1; - const Stairs: 2; - const RedPortal: 60; - } - namespace preset { - const AreaEntrance: 0; // special - // act 1 - const CaveHoleUp: 4; - const CaveHoleLvl2: 5; - const Crypt: 6; - const Mausoleum: 7; - const CryptMausExit: 8; - const JailUpStairs: 13; - const JailDownStairs: 14; - const CathedralDownStairs: 15; - const CathedralUpStairs: 16; - const CatacombsUpStairs: 17; - const CatacombsDownStairs: 18; + export namespace exits { + namespace type { + const WalkThrough: 1; + const Stairs: 2; + const RedPortal: 60; + } + namespace preset { + const AreaEntrance: 0; // special + // act 1 + const CaveHoleUp: 4; + const CaveHoleLvl2: 5; + const Crypt: 6; + const Mausoleum: 7; + const CryptMausExit: 8; + const JailUpStairs: 13; + const JailDownStairs: 14; + const CathedralDownStairs: 15; + const CathedralUpStairs: 16; + const CatacombsUpStairs: 17; + const CatacombsDownStairs: 18; - // act 2 - const A2SewersTrapDoor: 19; - const A2EnterSewersDoor: 20; - const A2ExitSewersDoor: 21; - const A2UndergroundUpStairs: 22; - const A2DownStairs: 23; - const EnterHaremStairs: 24; - const ExitHaremStairs: 25; - const PreviousLevelHaremRight: 26; - const PreviousLevelHaremLeft: 27; - const NextLevelHaremRight: 28; - const NextLevelHaremLeft: 29; - const PreviousPalaceRight: 30; - const PreviousPalaceLeft: 31; - const NextLevelPalace: 32; - const EnterStonyTomb: 33; - const EnterHalls: 36; - const EnterTalTomb1: 38; - const EnterTalTomb2: 39; - const EnterTalTomb3: 40; - const EnterTalTomb4: 41; - const EnterTalTomb5: 42; - const EnterTalTomb6: 43; - const EnterTalTomb7: 44; - const PreviousAreaTomb: 45; - const NextLevelTomb: 46; - const EnterMaggotLair: 47; - const PreviousAreaMaggotLair: 48; - const NextLevelMaggotLair: 49; - const AncientTunnelsTrapDoor: 50; - const EntrancetoDurielsLair: 100; + // act 2 + const A2SewersTrapDoor: 19; + const A2EnterSewersDoor: 20; + const A2ExitSewersDoor: 21; + const A2UndergroundUpStairs: 22; + const A2DownStairs: 23; + const EnterHaremStairs: 24; + const ExitHaremStairs: 25; + const PreviousLevelHaremRight: 26; + const PreviousLevelHaremLeft: 27; + const NextLevelHaremRight: 28; + const NextLevelHaremLeft: 29; + const PreviousPalaceRight: 30; + const PreviousPalaceLeft: 31; + const NextLevelPalace: 32; + const EnterStonyTomb: 33; + const EnterHalls: 36; + const EnterTalTomb1: 38; + const EnterTalTomb2: 39; + const EnterTalTomb3: 40; + const EnterTalTomb4: 41; + const EnterTalTomb5: 42; + const EnterTalTomb6: 43; + const EnterTalTomb7: 44; + const PreviousAreaTomb: 45; + const NextLevelTomb: 46; + const EnterMaggotLair: 47; + const PreviousAreaMaggotLair: 48; + const NextLevelMaggotLair: 49; + const AncientTunnelsTrapDoor: 50; + const EntrancetoDurielsLair: 100; - // act 3 - const EnterSpiderHole: 51; - const ExitSpiderHole: 52; - const EnterPit: 53; - const EnterDungeon: 54; - const PreviousAreaDungeon: 55; - const NextLevelDungeon: 56; - const A3EnterSewers: 57; - const A3ExitSewersUpperK: 58; - const A3SewersPreviousArea: 58; - const A3ExitSewers: 59; - const A3NextLevelSewers: 60; - const EnterTemple: 61; - const ExitTemple: 63; - const EnterDurance: 64; - const PreviousLevelDurance: 65; - const NextLevelDurance: 68; - const SewerStairsA3: 366; - const DuranceEntryStairs: 386; + // act 3 + const EnterSpiderHole: 51; + const ExitSpiderHole: 52; + const EnterPit: 53; + const EnterDungeon: 54; + const PreviousAreaDungeon: 55; + const NextLevelDungeon: 56; + const A3EnterSewers: 57; + const A3ExitSewersUpperK: 58; + const A3SewersPreviousArea: 58; + const A3ExitSewers: 59; + const A3NextLevelSewers: 60; + const EnterTemple: 61; + const ExitTemple: 63; + const EnterDurance: 64; + const PreviousLevelDurance: 65; + const NextLevelDurance: 68; + const SewerStairsA3: 366; + const DuranceEntryStairs: 386; - // act 4 - const EnterRiverStairs: 69; - const ExitRiverStairs: 70; - // act 5 - const EnterCrystal: 71; - const A5ExitCave: 73; - const A5NextLevelCave: 74; - const EnterSubLevelCave: 75; - const EnterNithsTemple: 76; - const PreviousAreaNithsTemple: 77; - const NextAreaNithsTemple: 78; - const ArreatEnterAncientsWay: 79; - const ArreatEnterWorldstone: 80; - const PreviousAreaWorldstone: 81; - const NextAreaWorldstone: 82; - } - } - - export namespace monsters { - namespace preset { - // Confirmed - const Izual: 256; - const Bishibosh: 734; - const Bonebreak: 735; - const Coldcrow: 736; - const Rakanishu: 737; - const TreeheadWoodFist: 738; - const Griswold: 739; - const TheCountess: 740; - const PitspawnFouldog: 741; - const FlamespiketheCrawler: 742; - const BoneAsh: 743; - const Radament: 744; - const BloodwitchtheWild: 745; - const Fangskin: 746; - const Beetleburst: 747; - const CreepingFeature: 748; - const ColdwormtheBurrower: 749; - const FireEye: 750; - const DarkElder: 751; - const TheSummoner: 752; - const AncientKaatheSoulless: 753; - const TheSmith: 754; - const SszarktheBurning: 755; - const WitchDoctorEndugu: 756; - const Stormtree: 757; - const BattlemaidSarina: 758; - const IcehawkRiftwing: 759; - const IsmailVilehand: 760; - const GelebFlamefinger: 761; - const BremmSparkfist: 762; - const ToorcIcefist: 763; - const WyandVoidfinger: 764; - const MafferDragonhand: 765; - const WingedDeath: 766; - const Taintbreeder: 768; - const RiftwraiththeCannibal: 769; - const InfectorofSouls: 770; - const LordDeSeis: 771; - const GrandVizierofChaos: 772; - const TheCowKing: 773; - const Corpsefire: 774; - const Hephasto: 775; - const ShenktheOverseer: 776; - const TalictheDefender: 777; - const MadawctheGuardian: 778; - const KorlictheProtector: 779; - const AxeDweller: 780; - const BonesawBreaker: 781; - const DacFarren: 782; - const EldritchtheRectifier: 783; - const EyebacktheUnleashed: 784; - const ThreshSocket: 785; - const Pindleskin: 786; - const SnapchipShatter: 787; - const AnodizedElite: 788; - const VinvearMolech: 789; - const SharpToothSayer: 790; - const MagmaTorquer: 791; - const BlazeRipper: 792; - const Frozenstein: 793; - const Nihlathak: 794; - const ColenzotheAnnihilator: 795; - const AchmeltheCursed: 796; - const BartuctheBloody: 797; - const VentartheUnholy: 798; - const ListertheTormentor: 799; - const BloodRaven: 805; + // act 4 + const EnterRiverStairs: 69; + const ExitRiverStairs: 70; + // act 5 + const EnterCrystal: 71; + const A5ExitCave: 73; + const A5NextLevelCave: 74; + const EnterSubLevelCave: 75; + const EnterNithsTemple: 76; + const PreviousAreaNithsTemple: 77; + const NextAreaNithsTemple: 78; + const ArreatEnterAncientsWay: 79; + const ArreatEnterWorldstone: 80; + const PreviousAreaWorldstone: 81; + const NextAreaWorldstone: 82; + } + } + + export namespace monsters { + namespace preset { + // Confirmed + const Izual: 256; + const Bishibosh: 734; + const Bonebreak: 735; + const Coldcrow: 736; + const Rakanishu: 737; + const TreeheadWoodFist: 738; + const Griswold: 739; + const TheCountess: 740; + const PitspawnFouldog: 741; + const FlamespiketheCrawler: 742; + const BoneAsh: 743; + const Radament: 744; + const BloodwitchtheWild: 745; + const Fangskin: 746; + const Beetleburst: 747; + const CreepingFeature: 748; + const ColdwormtheBurrower: 749; + const FireEye: 750; + const DarkElder: 751; + const TheSummoner: 752; + const AncientKaatheSoulless: 753; + const TheSmith: 754; + const SszarktheBurning: 755; + const WitchDoctorEndugu: 756; + const Stormtree: 757; + const BattlemaidSarina: 758; + const IcehawkRiftwing: 759; + const IsmailVilehand: 760; + const GelebFlamefinger: 761; + const BremmSparkfist: 762; + const ToorcIcefist: 763; + const WyandVoidfinger: 764; + const MafferDragonhand: 765; + const WingedDeath: 766; + const Taintbreeder: 768; + const RiftwraiththeCannibal: 769; + const InfectorofSouls: 770; + const LordDeSeis: 771; + const GrandVizierofChaos: 772; + const TheCowKing: 773; + const Corpsefire: 774; + const Hephasto: 775; + const ShenktheOverseer: 776; + const TalictheDefender: 777; + const MadawctheGuardian: 778; + const KorlictheProtector: 779; + const AxeDweller: 780; + const BonesawBreaker: 781; + const DacFarren: 782; + const EldritchtheRectifier: 783; + const EyebacktheUnleashed: 784; + const ThreshSocket: 785; + const Pindleskin: 786; + const SnapchipShatter: 787; + const AnodizedElite: 788; + const VinvearMolech: 789; + const SharpToothSayer: 790; + const MagmaTorquer: 791; + const BlazeRipper: 792; + const Frozenstein: 793; + const Nihlathak: 794; + const ColenzotheAnnihilator: 795; + const AchmeltheCursed: 796; + const BartuctheBloody: 797; + const VentartheUnholy: 798; + const ListertheTormentor: 799; + const BloodRaven: 805; - // Unconfirmed - // Questionable - const GriefGrumble: 741; // JailLvl2 - const UniqueJailLvl3: 273; - const UniqueArcaneSanctuary: 371; - } - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15 - } - namespace spectype { - const All: 0; - const Super: 1; - const Champion: 2; - const Unique: 4; - const SuperUnique: 5; - const Magic: 6; - const Minion: 8; - } - // todo - determine what all these correlate to - namespace type { - const Undead: 1; - const Demon: 2; - const Insect: 3; - const Human: 4; - const Construct: 5; - const LowUndead: 6; - const HighUndead: 7; - const Skeleton: 8; - const Zombie: 9; - const BigHead: 10; - const FoulCrow: 11; - const Fallen: 12; - const Brute: 13; - const SandRaider: 14; - const Wraith: 15; - const CorruptRogue: 16; - const Baboon: 17; - const GoatMan: 18; - const QuillRat: 19; - const SandMaggot: 20; - const Viper: 21; - const SandLeaper: 22; - const PantherWoman: 23; - const Swarm: 24; - const Scarab: 25; - const Mummy: 26; - const Unraveler: 27; - const Vulture: 28; - const Mosquito: 29; - const WillowWisp: 30; - const Arach: 31; - const ThornHulk: 32; - const Vampire: 33; - const BatDemon: 34; - const Fetish: 35; - const Blunderbore: 36; - const UndeadFetish: 37; - const Zakarum: 38; - const FrogDemon: 39; - const Tentacle: 40; - const FingerMage: 41; - const Golem: 42; - const Vilekind: 43; - const Regurgitator: 44; - const DoomKnight: 45; - const CouncilMember: 46; - const MegaDemon: 47; - const Bovine: 48; - const SeigeBeast: 49; - const SnowYeti: 50; - const Minion: 51; - const Succubus: 52; - const Overseer: 53; - const Imp: 54; - const FrozenHorror: 55; - const BloodLord: 56; - const DeathMauler: 57; - const PutridDefiler: 58; - } - const DiablosBoneCage: 340; - const Dummy1: 149; - const Dummy2: 268; - const AbyssKnight: 311; - const Afflicted: 10; - const Afflicted2: 580; - const AlbinoRoach: 95; - const Ancient1: 104; - const Ancient2: 669; - const Ancient3: 670; - const Apparition: 41; - const Arach1: 122; - const Arach2: 685; - const Assailant: 33; - const Assailant2: 603; - const BaalColdMage: 381; - const Balrog1: 360; - const Balrog2: 686; - const Banished: 135; - const Barbs: 422; - const Bear1: 428; - const Bear2: 431; - const Beast: 441; - const BerserkSlayer: 462; - const BlackArcher: 163; - const BlackLancer1: 168; - const BlackLancer2: 617; - const BlackLocusts: 88; - const BlackRaptor1: 17; - const BlackRaptor2: 592; - const BlackRogue: 46; - const BlackSoul1: 121; - const BlackSoul2: 640; - const BlackVultureNest: 208; - const BloodBoss: 482; - const BloodBringer: 443; - const BloodClan1: 55; - const BloodClan2: 588; - const BloodDiver: 139; - const BloodGolem: 290; - const BloodHawk1: 16; - const BloodHawk2: 591; - const BloodHawkNest: 207; - const BloodHook: 116; - const BloodHookNest: 336; - const BloodLord1: 134; - const BloodLord2: 695; - const BloodWing: 117; - const BloodWingNest: 337; - const Blunderbore1: 186; - const Blunderbore2: 618; - const BoneArcher1: 172; - const BoneArcher2: 576; - const BoneMage1: 275; - const BoneMage2: 380; - const BoneMage3: 384; - const BoneMage4: 388; - const BoneMage5: 624; - const BoneWarrior1: 2; - const BoneWarrior2: 648; - const HellBovine: 391; - const BrambleHulk: 128; - const Brute: 24; - const Bunny: 556; - const BurningDead: 3; - const BurningDeadArcher1: 173; - const BurningDeadArcher2: 575; - const BurningDeadArcher3: 577; - const BurningDeadMage1: 276; - const BurningDeadMage2: 385; - const BurningDeadMage3: 389; - const BurningDeadMage4: 621; - const BurningSoul1: 641; - const BurningSoul2: 120; - const Cadaver1: 100; - const Cadaver2: 703; - const Cantor: 239; - const CarrionBird1: 110; - const CarrionBird2: 608; - const Carver1: 642; - const Carver2: 20; - const CarverShaman: 645; - const CarverShaman2: 59; - const CaveLeaper1: 79; - const CaveLeaper2: 629; - const ClawViper1: 74; - const ClawViper2: 594; - const CloudStalker1: 18; - const CloudStalker2: 593; - const CloudStalkerNest: 209; - const Combatant1: 522; - const Combatant2: 523; - const ConsumedFireBoar: 464; - const ConsumedIceBoar: 463; - const CorpseSpitter: 308; - const Corpulent: 307; - const Creature1: 248; - const Creature2: 427; - const Creeper: 413; - const CrushBiest: 442; - const Crusher: 26; - const Damned1: 14; - const Damned2: 584; - const DarkArcher1: 162; - const DarkArcher2: 614; - const DarkFamiliar: 140; - const DarkHunter: 43; - const DarkLancer1: 167; - const DarkLancer2: 616; - const DarkLord1: 133; - const DarkLord2: 697; - const DarkOne1: 22; - const DarkOne2: 644; - const DarkRanger: 160; - const DarkShaman1: 61; - const DarkShaman2: 647; - const DarkShape: 42; - const DarkSpearwoman: 165; - const DarkStalker: 45; - const DeamonSteed: 445; - const DeathClan1: 57; - const DeathClan2: 589; - const Decayed: 97; - const DefiledWarrior: 440; - const Defiler1: 546; - const Defiler2: 547; - const Defiler3: 548; - const Defiler4: 549; - const Defiler5: 550; - const DesertWing: 136; - const Destruction: 410; - const Devilkin: 643; - const Devilkin2: 21; - const DevilkinShaman: 646; - const DevilkinShaman2: 60; - const Devourer: 70; - const DevourerEgg: 192; - const DevourerQueen: 286; - const DevourerYoung: 182; - const Disfigured: 13; - const Disfigured2: 583; - const Dominus1: 474; - const Dominus2: 636; - const DoomApe: 51; - const DoomKnight: 310; - const DoomKnight1: 699; - const DoomKnight2: 700; - const Drehya1: 512; - const Drehya2: 527; - const DriedCorpse: 96; - const DrownedCarcass: 8; - const DuneBeast: 48; - const DungSoldier: 91; - const Dweller: 247; - const Eagle: 429; - const Embalmed: 98; - const Faithful: 236; - const Fallen: 19; - const FallenShaman: 58; - const FanaticMinion: 461; - const Feeder: 115; - const FeederNest: 335; - const Fenris: 421; - const Fetish1: 142; - const BoneFetish2: 213; - const Fetish3: 397; - const FetishShaman: 279; - const Fiend1: 137; - const Fiend2: 651; - const FireBoar: 456; - const FireTower: 372; - const FlameSpider: 125; - const Flayer1: 143; - const BoneFetish3: 214; - const Flayer3: 398; - const Flayer4: 659; - const Flayer5: 656; - const FlayerShaman1: 280; - const FlayerShaman2: 662; - const FleshArcher: 164; - const FleshBeast1: 301; - const FleshBeast2: 678; - const FleshHunter: 47; - const FleshLancer: 169; - const FleshSpawner1: 298; - const FleshSpawner2: 676; - const FlyingScimitar: 234; - const FoulCrow: 15; - const FoulCrow2: 590; - const FoulCrowNest: 206; - const FrenziedHellSpawn: 465; - const FrenziedIceSpawn: 466; - const GargantuanBeast: 28; - const Geglash: 200; - const Ghost1: 38; - const Ghost2: 631; - const Ghoul: 7; - const GhoulLord1: 131; - const GhoulLord2: 696; - const GiantLamprey: 71; - const GiantLampreyEgg: 193; - const GiantLampreyQueen: 287; - const GiantLampreyYoung: 183; - const GiantUrchin: 317; - const Gloam1: 118; - const Gloam2: 639; - const Gloombat1: 138; - const Gloombat2: 650; - const Gorbelly: 187; - const GoreBearer: 444; - const GreaterHellSpawn1: 459; - const GreaterHellSpawn2: 684; - const GreaterIceSpawn: 460; - const Groper: 304; - const Grotesque1: 300; - const Grotesque2: 675; - const GrotesqueWyrm1: 303; - const GrotesqueWyrm2: 677; - const Guardian1: 102; - const Guardian2: 667; - const Hawk: 419; - const Heirophant1: 240; - const Heirophant2: 241; - const Heirophant3: 673; - const Heirophant4: 674; - const HellBuzzard: 112; - const HellCat: 86; - const HellClan1: 56; - const HellClan2: 587; - const HellSlinger: 376; - const HellSpawn1: 457; - const HellSpawn2: 683; - const HellSwarm: 90; - const HellWhip: 483; - const HollowOne: 101; - const Horror: 4; - const Horror1: 501; - const Horror2: 502; - const Horror3: 503; - const Horror4: 504; - const Horror5: 505; - const HorrorArcher1: 174; - const HorrorArcher2: 579; - const HorrorMage1: 277; - const HorrorMage2: 382; - const HorrorMage3: 386; - const HorrorMage4: 390; - const HorrorMage5: 623; - const HorrorMage6: 625; - const HorrorMage7: 626; - const Hs1: 560; - const HungryDead: 6; - const Huntress1: 83; - const Huntress2: 627; - const Hut: 528; - const Hydra1: 351; - const Hydra2: 352; - const Hydra3: 353; - const IceBoar: 455; - const IceSpawn: 458; - const Imp1: 492; - const Imp2: 493; - const Imp3: 494; - const Imp4: 495; - const Imp5: 496; - const Imp6: 688; - const Imp7: 689; - const Infidel1: 32; - const Infidel2: 600; - const InsaneHellSpawn: 467; - const InsaneIceSpawn: 468; - const Invader1: 31; - const Invader2: 602; - const Itchies: 87; - const JungleHunter: 50; - const JungleUrchin: 67; - const Larva: 283; - const Lasher: 480; - const LightningSpire: 371; - const Lord1: 506; - const Lord2: 507; - const Lord3: 508; - const Lord4: 509; - const Lord5: 510; - const Lord6: 652; - const Lord7: 653; - const Maggot: 227; - const Malachai: 408; - const Marauder: 30; - const Marauder2: 599; - const Master: 418; - const Mauler: 188; - const Mauler1: 529; - const Mauler12: 604; - const Mauler2: 530; - const Mauler3: 531; - const Mauler4: 532; - const Mauler5: 533; - const Mauler6: 619; - const MawFiend: 694; - const MawFiend2: 309; - const Council1: 345; - const Council2: 346; - const Council3: 347; - const Council4: 557; - const Minion1: 572; - const Minion2: 573; - const Enslaved: 453; - const MinionSlayerSpawner: 485; - const MinionSpawner: 484; - const Misshapen1: 12; - const Misshapen2: 582; - const MoonClan1: 53; - const MoonClan2: 585; - const BaalSubjectMummy: 105; - const Navi: 266; - const Flavie: 266; - const NightClan1: 54; - const NightClan2: 586; - const NightLord: 132; - const NightMarauder: 295; - const NightSlinger1: 375; - const NightSlinger2: 395; - const NightTiger: 85; - const OblivionKnight1: 312; - const OblivionKnight2: 701; - const OblivionKnight3: 702; - const OverLord: 481; - const OverSeer: 479; - const PitLord1: 361; - const PitLord2: 687; - const PitViper1: 76; - const PitViper2: 595; - const PlagueBearer: 9; - const PlagueBugs: 89; - const PoisonSpinner: 124; - const PreservedDead: 99; - const ProwlingDead: 438; - const QuillBear: 313; - const QuillRat1: 63; - const QuillRat2: 605; - const RatMan1: 141; - const RatMan2: 396; - const BoneFetish1: 212; - const RatMan4: 407; - const RatManShaman: 278; - const RazorBeast: 316; - const RazorPitDemon: 82; - const RazorSpine1: 66; - const RazorSpine2: 607; - const ReanimatedHorde: 437; - const Returned1: 1; - const Returned2: 649; - const ReturnedArcher1: 171; - const ReturnedArcher2: 578; - const ReturnedMage: 274; - const ReturnedMage1: 379; - const ReturnedMage2: 383; - const ReturnedMage3: 387; - const ReturnedMage4: 620; - const ReturnedMage5: 622; - const RiverStalkerHead: 262; - const RiverStalkerLimb: 259; - const RockDweller: 49; - const RockWorm: 69; - const RockWormEgg: 191; - const RockWormQueen: 285; - const RockWormYoung: 181; - const RotWalker: 436; - const SaberCat1: 84; - const SaberCat2: 628; - const Salamander1: 75; - const Salamander2: 596; - const SandFisher: 123; - const SandLeaper: 78; - const SandMaggot: 68; - const SandMaggotEgg: 190; - const SandMaggotYoung: 180; - const SandRaider1: 29; - const SandRaider2: 601; - const SandWarrior: 92; - const Scarab1: 93; - const Scarab2: 654; - const Sentry1: 411; - const Sentry2: 412; - const Sentry3: 415; - const Sentry4: 416; - const SerpentMagus1: 77; - const SerpentMagus2: 598; - const Sexton: 238; - const Skeleton: 0; - const SkeletonArcher: 170; - const Slayerexp1: 454; - const Slayerexp2: 682; - const Slinger1: 373; - const Slinger2: 610; - const Slinger3: 611; - const Slinger4: 612; - const SnowYeti1: 446; - const SnowYeti2: 447; - const SnowYeti3: 448; - const SnowYeti4: 449; - const SoulKiller: 691; - const SoulKiller1: 399; - const SoulKiller2: 144; - const SoulKiller3: 215; - const SoulKiller4: 658; - const SoulKiller5: 661; - const SoulKillerShaman1: 664; - const SoulKillerShaman2: 281; - const SpearCat: 394; - const SpearCat1: 374; - const Specter1: 40; - const Specter2: 633; - const SpiderMagus: 126; - const SpikeFiend1: 64; - const SpikeFiend2: 606; - const Spikefist: 130; - const SpikeGiant: 314; - const SteelWeevil1: 94; - const SteelWeevil2: 655; - const StormCaster1: 306; - const StormCaster2: 693; - const Strangler1: 305; - const Strangler2: 692; - const StygianDog: 302; - const StygianDoll1: 145; - const StygianDoll2: 216; - const StygianDoll3: 400; - const StygianDoll4: 660; - const StygianDoll5: 657; - const StygianDoll6: 690; - const StygianDollShaman1: 663; - const StygianDollShaman2: 282; - const StygianFury: 476; - const StygianHag: 299; - const StygianHarlot: 471; - const StygianWatcherHead: 263; - const StygianWatcherLimb: 260; - const Succubusexp1: 469; - const Succubusexp2: 634; - const Sucker: 114; - const SuckerNest: 334; - const Summoner: 250; - const SwampGhost: 119; - const Tainted: 11; - const Tainted2: 581; - const Taunt: 545; - const Temptress1: 472; - const Temptress2: 473; - const Temptress3: 635; - const Tentacle1: 562; - const Tentacle2: 563; - const Tentacle3: 564; - const Tentacle4: 565; - const Tentacle5: 566; - const ThornBeast: 65; - const ThornBrute: 315; - const ThornedHulk1: 127; - const ThornedHulk2: 609; - const Thrasher: 129; - const TombCreeper1: 80; - const TombCreeper2: 630; - const TombViper1: 73; - const TombViper2: 597; - const TrappedSoul1: 403; - const TrappedSoul2: 404; - const TreeLurker: 81; - const UndeadScavenger: 111; - const UnholyCorpse1: 439; - const UnholyCorpse2: 698; - const Unraveler1: 103; - const Unraveler2: 668; - const Urdar: 189; - const VenomLord1: 362; - const VenomLord2: 558; - const VileArcher1: 161; - const VileArcher2: 613; - const VileHunter: 44; - const VileLancer1: 166; - const VileLancer2: 615; - const VileTemptress: 470; - const VileWitch1: 475; - const VileWitch2: 638; - const WailingBeast: 27; - const WarpedFallen: 23; - const WarpedShaman: 62; - const Warrior: 417; - const WaterWatcherHead: 261; - const WaterWatcherLimb: 258; - const WingedNightmare: 113; - const Witch1: 637; - const Witch2: 477; - const Witch3: 478; - const Wolf1: 359; - const Wolf2: 420; - const Wolf3: 430; - const WolfRider1: 450; - const WolfRider2: 451; - const WolfRider3: 452; - const WorldKiller1: 679; - const WorldKiller2: 72; - const WorldKillerEgg1: 681; - const WorldKillerEgg2: 194; - const WorldKillerQueen: 288; - const WorldKillerYoung1: 680; - const WorldKillerYoung2: 184; - const Worm1: 551; - const Worm2: 552; - const Worm3: 553; - const Worm4: 554; - const Worm5: 555; - const Wraith1: 39; - const Wraith2: 632; - const Yeti: 25; - const Zakarumite: 235; - const Zealot1: 237; - const Zealot2: 671; - const Zealot3: 672; - const Zombie: 5; + // Unconfirmed + // Questionable + const GriefGrumble: 741; // JailLvl2 + const UniqueJailLvl3: 273; + const UniqueArcaneSanctuary: 371; + } + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } + namespace spectype { + const All: 0; + const Super: 1; + const Champion: 2; + const Unique: 4; + const SuperUnique: 5; + const Magic: 6; + const Minion: 8; + } + // todo - determine what all these correlate to + namespace type { + const Undead: 1; + const Demon: 2; + const Insect: 3; + const Human: 4; + const Construct: 5; + const LowUndead: 6; + const HighUndead: 7; + const Skeleton: 8; + const Zombie: 9; + const BigHead: 10; + const FoulCrow: 11; + const Fallen: 12; + const Brute: 13; + const SandRaider: 14; + const Wraith: 15; + const CorruptRogue: 16; + const Baboon: 17; + const GoatMan: 18; + const QuillRat: 19; + const SandMaggot: 20; + const Viper: 21; + const SandLeaper: 22; + const PantherWoman: 23; + const Swarm: 24; + const Scarab: 25; + const Mummy: 26; + const Unraveler: 27; + const Vulture: 28; + const Mosquito: 29; + const WillowWisp: 30; + const Arach: 31; + const ThornHulk: 32; + const Vampire: 33; + const BatDemon: 34; + const Fetish: 35; + const Blunderbore: 36; + const UndeadFetish: 37; + const Zakarum: 38; + const FrogDemon: 39; + const Tentacle: 40; + const FingerMage: 41; + const Golem: 42; + const Vilekind: 43; + const Regurgitator: 44; + const DoomKnight: 45; + const CouncilMember: 46; + const MegaDemon: 47; + const Bovine: 48; + const SeigeBeast: 49; + const SnowYeti: 50; + const Minion: 51; + const Succubus: 52; + const Overseer: 53; + const Imp: 54; + const FrozenHorror: 55; + const BloodLord: 56; + const DeathMauler: 57; + const PutridDefiler: 58; + } + const DiablosBoneCage: 340; + const DiablosBoneCage2: 342; + const Dummy1: 149; + const Dummy2: 268; + const AbyssKnight: 311; + const Afflicted: 10; + const Afflicted2: 580; + const AlbinoRoach: 95; + const Ancient1: 104; + const Ancient2: 669; + const Ancient3: 670; + const Apparition: 41; + const Arach1: 122; + const Arach2: 685; + const Assailant: 33; + const Assailant2: 603; + const BaalColdMage: 381; + const Balrog1: 360; + const Balrog2: 686; + const Banished: 135; + const Barbs: 422; + const Bear1: 428; + const Bear2: 431; + const Beast: 441; + const BerserkSlayer: 462; + const BlackArcher: 163; + const BlackLancer1: 168; + const BlackLancer2: 617; + const BlackLocusts: 88; + const BlackRaptor1: 17; + const BlackRaptor2: 592; + const BlackRogue: 46; + const BlackSoul1: 121; + const BlackSoul2: 640; + const BlackVultureNest: 208; + const BloodBoss: 482; + const BloodBringer: 443; + const BloodClan1: 55; + const BloodClan2: 588; + const BloodDiver: 139; + const BloodGolem: 290; + const BloodHawk1: 16; + const BloodHawk2: 591; + const BloodHawkNest: 207; + const BloodHook: 116; + const BloodHookNest: 336; + const BloodLord1: 134; + const BloodLord2: 695; + const BloodWing: 117; + const BloodWingNest: 337; + const Blunderbore1: 186; + const Blunderbore2: 618; + const BoneArcher1: 172; + const BoneArcher2: 576; + const BoneMage1: 275; + const BoneMage2: 380; + const BoneMage3: 384; + const BoneMage4: 388; + const BoneMage5: 624; + const BoneWarrior1: 2; + const BoneWarrior2: 648; + const HellBovine: 391; + const BrambleHulk: 128; + const Brute: 24; + const Bunny: 556; + const BurningDead: 3; + const BurningDeadArcher1: 173; + const BurningDeadArcher2: 575; + const BurningDeadArcher3: 577; + const BurningDeadMage1: 276; + const BurningDeadMage2: 385; + const BurningDeadMage3: 389; + const BurningDeadMage4: 621; + const BurningSoul1: 641; + const BurningSoul2: 120; + const Cadaver1: 100; + const Cadaver2: 703; + const Cantor: 239; + const CarrionBird1: 110; + const CarrionBird2: 608; + const Carver1: 642; + const Carver2: 20; + const CarverShaman: 645; + const CarverShaman2: 59; + const CaveLeaper1: 79; + const CaveLeaper2: 629; + const ClawViper1: 74; + const ClawViper2: 594; + const CloudStalker1: 18; + const CloudStalker2: 593; + const CloudStalkerNest: 209; + const Combatant1: 522; + const Combatant2: 523; + const ConsumedFireBoar: 464; + const ConsumedIceBoar: 463; + const CorpseSpitter: 308; + const Corpulent: 307; + const Creature1: 248; + const Creature2: 427; + const Creeper: 413; + const CrushBiest: 442; + const Crusher: 26; + const Damned1: 14; + const Damned2: 584; + const DarkArcher1: 162; + const DarkArcher2: 614; + const DarkFamiliar: 140; + const DarkHunter: 43; + const DarkLancer1: 167; + const DarkLancer2: 616; + const DarkLord1: 133; + const DarkLord2: 697; + const DarkOne1: 22; + const DarkOne2: 644; + const DarkRanger: 160; + const DarkShaman1: 61; + const DarkShaman2: 647; + const DarkShape: 42; + const DarkSpearwoman: 165; + const DarkStalker: 45; + const DeamonSteed: 445; + const DeathClan1: 57; + const DeathClan2: 589; + const Decayed: 97; + const DefiledWarrior: 440; + const Defiler1: 546; + const Defiler2: 547; + const Defiler3: 548; + const Defiler4: 549; + const Defiler5: 550; + const DesertWing: 136; + const Destruction: 410; + const Devilkin: 643; + const Devilkin2: 21; + const DevilkinShaman: 646; + const DevilkinShaman2: 60; + const Devourer: 70; + const DevourerEgg: 192; + const DevourerQueen: 286; + const DevourerYoung: 182; + const Disfigured: 13; + const Disfigured2: 583; + const Dominus1: 474; + const Dominus2: 636; + const DoomApe: 51; + const DoomKnight: 310; + const DoomKnight1: 699; + const DoomKnight2: 700; + const Drehya1: 512; + const Drehya2: 527; + const DriedCorpse: 96; + const DrownedCarcass: 8; + const DuneBeast: 48; + const DungSoldier: 91; + const Dweller: 247; + const Eagle: 429; + const Embalmed: 98; + const Faithful: 236; + const Fallen: 19; + const FallenShaman: 58; + const FanaticMinion: 461; + const Feeder: 115; + const FeederNest: 335; + const Fenris: 421; + const Fetish1: 142; + const BoneFetish2: 213; + const Fetish3: 397; + const FetishShaman: 279; + const Fiend1: 137; + const Fiend2: 651; + const FireBoar: 456; + const FireTower: 372; + const FlameSpider: 125; + const Flayer1: 143; + const BoneFetish3: 214; + const Flayer3: 398; + const Flayer4: 659; + const Flayer5: 656; + const FlayerShaman1: 280; + const FlayerShaman2: 662; + const FleshArcher: 164; + const FleshBeast1: 301; + const FleshBeast2: 678; + const FleshHunter: 47; + const FleshLancer: 169; + const FleshSpawner1: 298; + const FleshSpawner2: 676; + const FlyingScimitar: 234; + const FoulCrow: 15; + const FoulCrow2: 590; + const FoulCrowNest: 206; + const FrenziedHellSpawn: 465; + const FrenziedIceSpawn: 466; + const GargantuanBeast: 28; + const Geglash: 200; + const Ghost1: 38; + const Ghost2: 631; + const Ghoul: 7; + const GhoulLord1: 131; + const GhoulLord2: 696; + const GiantLamprey: 71; + const GiantLampreyEgg: 193; + const GiantLampreyQueen: 287; + const GiantLampreyYoung: 183; + const GiantUrchin: 317; + const Gloam1: 118; + const Gloam2: 639; + const Gloombat1: 138; + const Gloombat2: 650; + const Gorbelly: 187; + const GoreBearer: 444; + const GreaterHellSpawn1: 459; + const GreaterHellSpawn2: 684; + const GreaterIceSpawn: 460; + const Groper: 304; + const Grotesque1: 300; + const Grotesque2: 675; + const GrotesqueWyrm1: 303; + const GrotesqueWyrm2: 677; + const Guardian1: 102; + const Guardian2: 667; + const Hawk: 419; + const Heirophant1: 240; + const Heirophant2: 241; + const Heirophant3: 673; + const Heirophant4: 674; + const HellBuzzard: 112; + const HellCat: 86; + const HellClan1: 56; + const HellClan2: 587; + const HellSlinger: 376; + const HellSpawn1: 457; + const HellSpawn2: 683; + const HellSwarm: 90; + const HellWhip: 483; + const HollowOne: 101; + const Horror: 4; + const Horror1: 501; + const Horror2: 502; + const Horror3: 503; + const Horror4: 504; + const Horror5: 505; + const HorrorArcher1: 174; + const HorrorArcher2: 579; + const HorrorMage1: 277; + const HorrorMage2: 382; + const HorrorMage3: 386; + const HorrorMage4: 390; + const HorrorMage5: 623; + const HorrorMage6: 625; + const HorrorMage7: 626; + const Hs1: 560; + const HungryDead: 6; + const Huntress1: 83; + const Huntress2: 627; + const Hut: 528; + const Hydra1: 351; + const Hydra2: 352; + const Hydra3: 353; + const IceBoar: 455; + const IceSpawn: 458; + const Imp1: 492; + const Imp2: 493; + const Imp3: 494; + const Imp4: 495; + const Imp5: 496; + const Imp6: 688; + const Imp7: 689; + const Infidel1: 32; + const Infidel2: 600; + const InsaneHellSpawn: 467; + const InsaneIceSpawn: 468; + const Invader1: 31; + const Invader2: 602; + const Itchies: 87; + const JungleHunter: 50; + const JungleUrchin: 67; + const Larva: 283; + const Lasher: 480; + const LightningSpire: 371; + const Lord1: 506; + const Lord2: 507; + const Lord3: 508; + const Lord4: 509; + const Lord5: 510; + const Lord6: 652; + const Lord7: 653; + const Maggot: 227; + const Malachai: 408; + const Marauder: 30; + const Marauder2: 599; + const Master: 418; + const Mauler: 188; + const Mauler1: 529; + const Mauler12: 604; + const Mauler2: 530; + const Mauler3: 531; + const Mauler4: 532; + const Mauler5: 533; + const Mauler6: 619; + const MawFiend: 694; + const MawFiend2: 309; + const Council1: 345; + const Council2: 346; + const Council3: 347; + const Council4: 557; + const Minion1: 572; + const Minion2: 573; + const Enslaved: 453; + const MinionSlayerSpawner: 485; + const MinionSpawner: 484; + const Misshapen1: 12; + const Misshapen2: 582; + const MoonClan1: 53; + const MoonClan2: 585; + const BaalSubjectMummy: 105; + const Navi: 266; + const Flavie: 266; + const NightClan1: 54; + const NightClan2: 586; + const NightLord: 132; + const NightMarauder: 295; + const NightSlinger1: 375; + const NightSlinger2: 395; + const NightTiger: 85; + const OblivionKnight1: 312; + const OblivionKnight2: 701; + const OblivionKnight3: 702; + const OverLord: 481; + const OverSeer: 479; + const PitLord1: 361; + const PitLord2: 687; + const PitViper1: 76; + const PitViper2: 595; + const PlagueBearer: 9; + const PlagueBugs: 89; + const PoisonSpinner: 124; + const PreservedDead: 99; + const ProwlingDead: 438; + const QuillBear: 313; + const QuillRat1: 63; + const QuillRat2: 605; + const RatMan1: 141; + const RatMan2: 396; + const BoneFetish1: 212; + const RatMan4: 407; + const RatManShaman: 278; + const RazorBeast: 316; + const RazorPitDemon: 82; + const RazorSpine1: 66; + const RazorSpine2: 607; + const ReanimatedHorde: 437; + const Returned1: 1; + const Returned2: 649; + const ReturnedArcher1: 171; + const ReturnedArcher2: 578; + const ReturnedMage: 274; + const ReturnedMage1: 379; + const ReturnedMage2: 383; + const ReturnedMage3: 387; + const ReturnedMage4: 620; + const ReturnedMage5: 622; + const RiverStalkerHead: 262; + const RiverStalkerLimb: 259; + const RockDweller: 49; + const RockWorm: 69; + const RockWormEgg: 191; + const RockWormQueen: 285; + const RockWormYoung: 181; + const RotWalker: 436; + const SaberCat1: 84; + const SaberCat2: 628; + const Salamander1: 75; + const Salamander2: 596; + const SandFisher: 123; + const SandLeaper: 78; + const SandMaggot: 68; + const SandMaggotEgg: 190; + const SandMaggotYoung: 180; + const SandRaider1: 29; + const SandRaider2: 601; + const DeathBeetle: 92; + const Scarab1: 93; + const Scarab2: 654; + const Sentry1: 411; + const Sentry2: 412; + const Sentry3: 415; + const Sentry4: 416; + const SerpentMagus1: 77; + const SerpentMagus2: 598; + const Sexton: 238; + const Skeleton: 0; + const SkeletonArcher: 170; + const Slayerexp1: 454; + const Slayerexp2: 682; + const Slinger1: 373; + const Slinger2: 610; + const Slinger3: 611; + const Slinger4: 612; + const SnowYeti1: 446; + const SnowYeti2: 447; + const SnowYeti3: 448; + const SnowYeti4: 449; + const SoulKiller: 691; + const SoulKiller1: 399; + const SoulKiller2: 144; + const SoulKiller3: 215; + const SoulKiller4: 658; + const SoulKiller5: 661; + const SoulKillerShaman1: 664; + const SoulKillerShaman2: 281; + const SpearCat: 394; + const SpearCat1: 374; + const Specter1: 40; + const Specter2: 633; + const SpiderMagus: 126; + const SpikeFiend1: 64; + const SpikeFiend2: 606; + const Spikefist: 130; + const SpikeGiant: 314; + const SteelWeevil1: 94; + const SteelWeevil2: 655; + const StormCaster1: 306; + const StormCaster2: 693; + const Strangler1: 305; + const Strangler2: 692; + const StygianDog: 302; + const StygianDoll1: 145; + const StygianDoll2: 216; + const StygianDoll3: 400; + const StygianDoll4: 660; + const StygianDoll5: 657; + const StygianDoll6: 690; + const StygianDollShaman1: 663; + const StygianDollShaman2: 282; + const StygianFury: 476; + const StygianHag: 299; + const StygianHarlot: 471; + const StygianWatcherHead: 263; + const StygianWatcherLimb: 260; + const Succubusexp1: 469; + const Succubusexp2: 634; + const Sucker: 114; + const SuckerNest: 334; + const Summoner: 250; + const SwampGhost: 119; + const Tainted: 11; + const Tainted2: 581; + const Taunt: 545; + const Temptress1: 472; + const Temptress2: 473; + const Temptress3: 635; + const Tentacle1: 562; + const Tentacle2: 563; + const Tentacle3: 564; + const Tentacle4: 565; + const Tentacle5: 566; + const ThornBeast: 65; + const ThornBrute: 315; + const ThornedHulk1: 127; + const ThornedHulk2: 609; + const Thrasher: 129; + const TombCreeper1: 80; + const TombCreeper2: 630; + const TombViper1: 73; + const TombViper2: 597; + const TrappedSoul1: 403; + const TrappedSoul2: 404; + const TreeLurker: 81; + const UndeadScavenger: 111; + const UnholyCorpse1: 439; + const UnholyCorpse2: 698; + const Unraveler1: 103; + const Unraveler2: 668; + const Urdar: 189; + const VenomLord1: 362; + const VenomLord2: 558; + const VileArcher1: 161; + const VileArcher2: 613; + const VileHunter: 44; + const VileLancer1: 166; + const VileLancer2: 615; + const VileTemptress: 470; + const VileWitch1: 475; + const VileWitch2: 638; + const WailingBeast: 27; + const WarpedFallen: 23; + const WarpedShaman: 62; + const Warrior: 417; + const WaterWatcherHead: 261; + const WaterWatcherLimb: 258; + const WingedNightmare: 113; + const Witch1: 637; + const Witch2: 477; + const Witch3: 478; + const Wolf1: 359; + const Wolf2: 420; + const Wolf3: 430; + const WolfRider1: 450; + const WolfRider2: 451; + const WolfRider3: 452; + const WorldKiller1: 679; + const WorldKiller2: 72; + const WorldKillerEgg1: 681; + const WorldKillerEgg2: 194; + const WorldKillerQueen: 288; + const WorldKillerYoung1: 680; + const WorldKillerYoung2: 184; + const Worm1: 551; + const Worm2: 552; + const Worm3: 553; + const Worm4: 554; + const Worm5: 555; + const Wraith1: 39; + const Wraith2: 632; + const Yeti: 25; + const Zakarumite: 235; + const Zealot1: 237; + const Zealot2: 671; + const Zealot3: 672; + const Zombie: 5; - // Bosses/Ubers - const Andariel: 156; - const Duriel: 211; - const Mephisto: 242; - const Diablo: 243; - const DiabloClone: 333; - const ThroneBaal: 543; - const Baal: 544; - const BaalClone: 570; - const UberMephisto: 704; - const UberBaal: 705; - const UberIzual: 706; - const Lilith: 707; - const UberDuriel: 708; - const UberDiablo: 709; + // Bosses/Ubers + const Andariel: 156; + const Duriel: 211; + const Mephisto: 242; + const Diablo: 243; + const DiabloClone: 333; + const ThroneBaal: 543; + const Baal: 544; + const BaalClone: 570; + const UberMephisto: 704; + const UberBaal: 705; + const UberIzual: 706; + const Lilith: 707; + const UberDuriel: 708; + const UberDiablo: 709; - // Mini-Bosses - const TheSmith: 402; - const BloodRaven: 267; - const Radament: 229; - const TheSummoner: 250; - const Griswold: 365; - const Izual: 256; - const Hephasto: 409; - const KorlictheProtector: 540; - const TalictheDefender: 541; - const MadawctheGuardian: 542; - const ListerTheTormenter: 571; - const TheCowKing: 743; - const ColdwormtheBurrower: 284; - const Nihlathak: 526; + // Mini-Bosses + const TheSmith: 402; + const BloodRaven: 267; + const Radament: 229; + const TheSummoner: 250; + const Griswold: 365; + const Izual: 256; + const Hephasto: 409; + const KorlictheProtector: 540; + const TalictheDefender: 541; + const MadawctheGuardian: 542; + const ListerTheTormenter: 571; + const TheCowKing: 743; + const ColdwormtheBurrower: 284; + const Nihlathak: 526; - // Objects - const Turret1: 348; - const Turret2: 349; - const Turret3: 350; - const CatapultS: 497; - const CatapultE: 498; - const CatapultSiege: 499; - const CatapultW: 500; - const Compellingorb: 366; - const GargoyleTrap: 273; - const MummyGenerator: 228; - const Stairs: 559; - const BarricadeDoor1: 432; - const BarricadeDoor2: 433; - const PrisonDoor: 434; - const BarricadeTower: 435; - const BarricadeWall1: 524; - const BarricadeWall2: 525; + // Objects + const Turret1: 348; + const Turret2: 349; + const Turret3: 350; + const CatapultS: 497; + const CatapultE: 498; + const CatapultSiege: 499; + const CatapultW: 500; + const Compellingorb: 366; + const GargoyleTrap: 273; + const MummyGenerator: 228; + const Stairs: 559; + const BarricadeDoor1: 432; + const BarricadeDoor2: 433; + const PrisonDoor: 434; + const BarricadeTower: 435; + const BarricadeWall1: 524; + const BarricadeWall2: 525; - // Misc? - const Youngdiablo: 368; - const Left: 525; - const Life: 426; - const Effect: 574; - const Pet: 414; - const Prince: 249; - const POW: 534; - const Right: 524; - const Sage: 424; - const Town: 514; - const Cow: 179; - } + // Misc? + const Youngdiablo: 368; + const Left: 525; + const Life: 426; + const Effect: 574; + const Pet: 414; + const Prince: 249; + const POW: 534; + const Right: 524; + const Sage: 424; + const Town: 514; + const Cow: 179; + } - export namespace summons { - namespace type { - const Valkyrie: 2; - const Golem: 3; - const Skeleton: 4; - const SkeletonMage: 5; - const Revive: 6; - const Mercenary: 7; - const Dopplezon: 8; - const Raven: 10; - const SpiritWolf: 11; - const Fenris: 12; - const DireWolf: 12; - const Totem: 13; - const Spirit: 13; - const Vine: 14; - const Grizzly: 15; - const ShadowWarrior: 16; - const Shadow: 16; - const AssassinTrap: 17; - const Hydra: 19; - } + export namespace summons { + namespace type { + const Valkyrie: 2; + const Golem: 3; + const Skeleton: 4; + const SkeletonMage: 5; + const Revive: 6; + const Mercenary: 7; + const Dopplezon: 8; + const Raven: 10; + const SpiritWolf: 11; + const Fenris: 12; + const DireWolf: 12; + const Totem: 13; + const Spirit: 13; + const Vine: 14; + const Grizzly: 15; + const ShadowWarrior: 16; + const Shadow: 16; + const AssassinTrap: 17; + const Hydra: 19; + } - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15 - } + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } - const ClayGolem: 289; - const Dopplezon: 356; - const Valkyrie: 357; - const FireGolem: 292; - const IronGolem: 291; - const NecroMage: 364; - const NecroSkeleton: 363; - const Poppy: 425; - const Wolverine: 423; - } + const ClayGolem: 289; + const Dopplezon: 356; + const Valkyrie: 357; + const FireGolem: 292; + const IronGolem: 291; + const NecroMage: 364; + const NecroSkeleton: 363; + const Poppy: 425; + const Wolverine: 423; + } - export namespace mercs { - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15 - } + export namespace mercs { + namespace mode { + const Death: 0; + const Standing: 1; + const Walking: 2; + const GettingHit: 3; + const Attacking1: 4; + const Attacking2: 5; + const Blocking: 6; + const CastingSkill: 7; + const UsingSkill1: 8; + const UsingSkill2: 9; + const UsingSkill3: 10; + const UsingSkill4: 11; + const Dead: 12; + const KnockedBack: 13; + const Spawning: 14; + const Running: 15 + } - const Rogue: 271; - const Guard: 338; - const IronWolf: 359; - const A5Barb: 561; - } + const Rogue: 271; + const Guard: 338; + const IronWolf: 359; + const A5Barb: 561; + } - export namespace missiles { - const DiabloLightning: 172; - const FissureCrack1: 462; - const FissureCrack2: 463; - } + export namespace missiles { + const DiabloLightning: 172; + const FissureCrack1: 462; + const FissureCrack2: 463; + } - export namespace storage { - const Equipped: 1; - const Belt: 2; - const Inventory: 3; - const TradeWindow: 5; - const Cube: 6; - const Stash: 7; - } + export namespace storage { + const Equipped: 1; + const Belt: 2; + const Inventory: 3; + const TradeWindow: 5; + const Cube: 6; + const Stash: 7; + } - export namespace node { - const NotOnPlayer: 0; - const Storage: 1; - const Belt: 2; - const Equipped: 3; - const Cursor: 4; - } + export namespace node { + const NotOnPlayer: 0; + const Storage: 1; + const Belt: 2; + const Equipped: 3; + const Cursor: 4; + } - // Same apply's for merc with less things available - export namespace body { - const None: 0; - const Head: 1; - const Neck: 2; - const Torso: 3; - const Armor: 3; - const RightArm: 4; - const LeftArm: 5; - const RingRight: 6; - const RingLeft: 7; - const Belt: 8; - const Feet: 9; - const Gloves: 10; - const RightArmSecondary: 11; - const LeftArmSecondary: 12 - } + // Same apply's for merc with less things available + export namespace body { + const None: 0; + const Head: 1; + const Neck: 2; + const Torso: 3; + const Armor: 3; + const RightArm: 4; + const LeftArm: 5; + const RingRight: 6; + const RingLeft: 7; + const Belt: 8; + const Feet: 9; + const Gloves: 10; + const RightArmSecondary: 11; + const LeftArmSecondary: 12 + } - export namespace items { - export namespace cost { - const ToBuy: 0; - const ToSell: 1; - const ToRepair: 2; - } - export namespace flags { - const Equipped: 0x00000001; - const InSocket: 0x00000008; - const Identified: 0x00000010; - const OnActiveWeaponSlot: 0x00000040; - const OnSwapWeaponSlot: 0x00000080; - const Broken: 0x00000100; - const FullRejuv: 0x00000400; - const Socketed: 0x00000800; - const InTradeGamble: 0x00002000; - const NotInSocket: 0x00004000; - const Ear: 0x00010000; - const StartingItem: 0x00020000; - const RuneQuestPotion: 0x00200000; - const Ethereal: 0x00400000; - const IsAnItem: 0x00800000; - const Personalized: 0x01000000; - const Runeword: 0x04000000; - } - export namespace mode { - const inStorage: 0; //Item inven stash cube store = Item inven stash cube store - const Equipped: 1; // Item equipped self or merc - const inBelt: 2; // Item in belt - const onGround: 3; // Item on ground - const onCursor: 4; // Item on cursor - const Dropping: 5; // Item being dropped - const Socketed: 6 // Item socketed in item - } - export namespace quality { - const LowQuality: 1; - const Normal: 2; - const Superior: 3; - const Magic: 4; - const Set: 5; - const Rare: 6; - const Unique: 7; - const Crafted: 8; - } - export namespace _class1 { - const Normal: 0; - const Exceptional: 1; - const Elite: 2; - } - export { _class1 as class }; - export namespace type { - const Shield: 2; - const Armor: 3; - const Gold: 4; - const BowQuiver: 5; - const CrossbowQuiver: 6; - const PlayerBodyPart: 7; - const Herb: 8; - const Potion: 9; - const Ring: 10; - const Elixir: 11; - const Amulet: 12; - const Charm: 13; - const notused0: 14; - const Boots: 15; - const Gloves: 16; - const notused1: 17; - const Book: 18; - const Belt: 19; - const Gem: 20; - const Torch: 21; - const Scroll: 22; - const notused2: 23; - const Scepter: 24; - const Wand: 25; - const Staff: 26; - const Bow: 27; - const Axe: 28; - const Club: 29; - const Sword: 30; - const Hammer: 31; - const Knife: 32; - const Spear: 33; - const Polearm: 34; - const Crossbow: 35; - const Mace: 36; - const Helm: 37; - const MissilePotion: 38; - const Quest: 39; - const Bodypart: 40; - const Key: 41; - const ThrowingKnife: 42; - const ThrowingAxe: 43; - const Javelin: 44; - const Weapon: 45; - const MeleeWeapon: 46; - const MissileWeapon: 47; - const ThrownWeapon: 48; - const ComboWeapon: 49; - const AnyArmor: 50; - const AnyShield: 51; - const Miscellaneous: 52; - const SocketFiller: 53; - const Secondhand: 54; - const StavesandRods: 55; - const Missile: 56; - const Blunt: 57; - const Jewel: 58; - const ClassSpecific: 59; - const AmazonItem: 60; - const BarbarianItem: 61; - const NecromancerItem: 62; - const PaladinItem: 63; - const SorceressItem: 64; - const AssassinItem: 65; - const DruidItem: 66; - const HandtoHand: 67; - const Orb: 68; - const VoodooHeads: 69; - const AuricShields: 70; - const PrimalHelm: 71; - const Pelt: 72; - const Cloak: 73; - const Rune: 74; - const Circlet: 75; - const HealingPotion: 76; - const ManaPotion: 77; - const RejuvPotion: 78; - const StaminaPotion: 79; - const AntidotePotion: 80; - const ThawingPotion: 81; - const SmallCharm: 82; - const LargeCharm: 83; - const GrandCharm: 84; - const AmazonBow: 85; - const AmazonSpear: 86; - const AmazonJavelin: 87; - const AssassinClaw: 88; - const MagicBowQuiv: 89; - const MagicxBowQuiv: 90; - const ChippedGem: 91; - const FlawedGem: 92; - const StandardGem: 93; - const FlawlessGem: 94; - const PerfectgGem: 95; - const Amethyst: 96; - const Diamond: 97; - const Emerald: 98; - const Ruby: 99; - const Sapphire: 100; - const Topaz: 101; - const Skull: 102; - } - - // Weapons - export const HandAxe: 0; - export const Axe: 1; - export const DoubleAxe: 2; - export const MilitaryPick: 3; - export const WarAxe: 4; - export const LargeAxe: 5; - export const BroadAxe: 6; - export const BattleAxe: 7; - export const GreatAxe: 8; - export const GiantAxe: 9; - export const Wand: 10; - export const YewWand: 11; - export const BoneWand: 12; - export const GrimWand: 13; - export const Club: 14; - export const Scepter: 15; - export const GrandScepter: 16; - export const WarScepter: 17; - export const SpikedClub: 18; - export const Mace: 19; - export const MorningStar: 20; - export const Flail: 21; - export const WarHammer: 22; - export const Maul: 23; - export const GreatMaul: 24; - export const ShortSword: 25; - export const Scimitar: 26; - export const Sabre: 27; - export const Falchion: 28; - export const CrystalSword: 29; - export const BroadSword: 30; - export const LongSword: 31; - export const WarSword: 32; - export const Two_HandedSword: 33; - export const Claymore: 34; - export const GiantSword: 35; - export const BastardSword: 36; - export const Flamberge: 37; - export const GreatSword: 38; - export const Dagger: 39; - export const Dirk: 40; - export const Kris: 41; - export const Blade: 42; - export const ThrowingKnife: 43; - export const ThrowingAxe: 44; - export const BalancedKnife: 45; - export const BalancedAxe: 46; - export const Javelin: 47; - export const Pilum: 48; - export const ShortSpear: 49; - export const Glaive: 50; - export const ThrowingSpear: 51; - export const Spear: 52; - export const Trident: 53; - export const Brandistock: 54; - export const Spetum: 55; - export const Pike: 56; - export const Bardiche: 57; - export const Voulge: 58; - export const Scythe: 59; - export const Poleaxe: 60; - export const Halberd: 61; - export const WarScythe: 62; - export const ShortStaff: 63; - export const LongStaff: 64; - export const GnarledStaff: 65; - export const BattleStaff: 66; - export const WarStaff: 67; - export const ShortBow: 68; - export const HuntersBow: 69; - export const LongBow: 70; - export const CompositeBow: 71; - export const ShortBattleBow: 72; - export const LongBattleBow: 73; - export const ShortWarBow: 74; - export const LongWarBow: 75; - export const LightCrossbow: 76; - export const Crossbow: 77; - export const HeavyCrossbow: 78; - export const RepeatingCrossbow: 79; - export const Hatchet: 93; - export const Cleaver: 94; - export const TwinAxe: 95; - export const Crowbill: 96; - export const Naga: 97; - export const MilitaryAxe: 98; - export const BeardedAxe: 99; - export const Tabar: 100; - export const GothicAxe: 101; - export const AncientAxe: 102; - export const BurntWand: 103; - export const PetrifiedWand: 104; - export const TombWand: 105; - export const GraveWand: 106; - export const Cudgel: 107; - export const RuneScepter: 108; - export const HolyWaterSprinkler: 109; - export const DivineScepter: 110; - export const BarbedClub: 111; - export const FlangedMace: 112; - export const JaggedStar: 113; - export const Knout: 114; - export const BattleHammer: 115; - export const WarClub: 116; - export const MarteldeFer: 117; - export const Gladius: 118; - export const Cutlass: 119; - export const Shamshir: 120; - export const Tulwar: 121; - export const DimensionalBlade: 122; - export const BattleSword: 123; - export const RuneSword: 124; - export const AncientSword: 125; - export const Espandon: 126; - export const DacianFalx: 127; - export const TuskSword: 128; - export const GothicSword: 129; - export const Zweihander: 130; - export const ExecutionerSword: 131; - export const Poignard: 132; - export const Rondel: 133; - export const Cinquedeas: 134; - export const Stiletto: 135; - export const BattleDart: 136; - export const Francisca: 137; - export const WarDart: 138; - export const Hurlbat: 139; - export const WarJavelin: 140; - export const GreatPilum: 141; - export const Simbilan: 142; - export const Spiculum: 143; - export const Harpoon: 144; - export const WarSpear: 145; - export const Fuscina: 146; - export const WarFork: 147; - export const Yari: 148; - export const Lance: 149; - export const LochaberAxe: 150; - export const Bill: 151; - export const BattleScythe: 152; - export const Partizan: 153; - export const Bec_de_Corbin: 154; - export const GrimScythe: 155; - export const JoStaff: 156; - export const Quarterstaff: 157; - export const CedarStaff: 158; - export const GothicStaff: 159; - export const RuneStaff: 160; - export const EdgeBow: 161; - export const RazorBow: 162; - export const CedarBow: 163; - export const DoubleBow: 164; - export const ShortSiegeBow: 165; - export const LargeSiegeBow: 166; - export const RuneBow: 167; - export const GothicBow: 168; - export const Arbalest: 169; - export const SiegeCrossbow: 170; - export const Ballista: 171; - export const Chu_Ko_Nu: 172; - export const Katar: 175; - export const WristBlade: 176; - export const HatchetHands: 177; - export const Cestus: 178; - export const Claws: 179; - export const BladeTalons: 180; - export const ScissorsKatar: 181; - export const Quhab: 182; - export const WristSpike: 183; - export const Fascia: 184; - export const HandScythe: 185; - export const GreaterClaws: 186; - export const GreaterTalons: 187; - export const ScissorsQuhab: 188; - export const Suwayyah: 189; - export const WristSword: 190; - export const WarFist: 191; - export const BattleCestus: 192; - export const FeralClaws: 193; - export const RunicTalons: 194; - export const ScissorsSuwayyah: 195; - export const Tomahawk: 196; - export const SmallCrescent: 197; - export const EttinAxe: 198; - export const WarSpike: 199; - export const BerserkerAxe: 200; - export const FeralAxe: 201; - export const Silver_edgedAxe: 202; - export const Decapitator: 203; - export const ChampionAxe: 204; - export const GloriousAxe: 205; - export const PolishedWand: 206; - export const GhostWand: 207; - export const LichWand: 208; - export const UnearthedWand: 209; - export const Truncheon: 210; - export const MightyScepter: 211; - export const SeraphRod: 212; - export const Caduceus: 213; - export const TyrantClub: 214; - export const ReinforcedMace: 215; - export const DevilStar: 216; - export const Scourge: 217; - export const LegendaryMallet: 218; - export const OgreMaul: 219; - export const ThunderMaul: 220; - export const Falcata: 221; - export const Ataghan: 222; - export const ElegantBlade: 223; - export const HydraEdge: 224; - export const PhaseBlade: 225; - export const ConquestSword: 226; - export const CrypticSword: 227; - export const MythicalSword: 228; - export const LegendSword: 229; - export const HighlandBlade: 230; - export const BalrogBlade: 231; - export const ChampionSword: 232; - export const ColossusSword: 233; - export const ColossusBlade: 234; - export const BoneKnife: 235; - export const MithrilPoint: 236; - export const FangedKnife: 237; - export const LegendSpike: 238; - export const FlyingKnife: 239; - export const FlyingAxe: 240; - export const WingedKnife: 241; - export const WingedAxe: 242; - export const HyperionJavelin: 243; - export const StygianPilum: 244; - export const BalrogSpear: 245; - export const GhostGlaive: 246; - export const WingedHarpoon: 247; - export const HyperionSpear: 248; - export const StygianPike: 249; - export const Mancatcher: 250; - export const GhostSpear: 251; - export const WarPike: 252; - export const OgreAxe: 253; - export const ColossusVoulge: 254; - export const Thresher: 255; - export const CrypticAxe: 256; - export const GreatPoleaxe: 257; - export const GiantThresher: 258; - export const WalkingStick: 259; - export const Stalagmite: 260; - export const ElderStaff: 261; - export const Shillelagh: 262; - export const ArchonStaff: 263; - export const SpiderBow: 264; - export const BladeBow: 265; - export const ShadowBow: 266; - export const GreatBow: 267; - export const DiamondBow: 268; - export const CrusaderBow: 269; - export const WardBow: 270; - export const HydraBow: 271; - export const PelletBow: 272; - export const GorgonCrossbow: 273; - export const ColossusCrossbow: 274; - export const DemonCrossbow: 275; - export const EagleOrb: 276; - export const SacredGlobe: 277; - export const SmokedSphere: 278; - export const ClaspedOrb: 279; - export const JaredsStone: 280; - export const StagBow: 281; - export const ReflexBow: 282; - export const MaidenSpear: 283; - export const MaidenPike: 284; - export const MaidenJavelin: 285; - export const GlowingOrb: 286; - export const CrystallineGlobe: 287; - export const CloudySphere: 288; - export const SparklingBall: 289; - export const SwirlingCrystal: 290; - export const AshwoodBow: 291; - export const CeremonialBow: 292; - export const CeremonialSpear: 293; - export const CeremonialPike: 294; - export const CeremonialJavelin: 295; - export const HeavenlyStone: 296; - export const EldritchOrb: 297; - export const DemonHeart: 298; - export const VortexOrb: 299; - export const DimensionalShard: 300; - export const MatriarchalBow: 301; - export const GrandMatronBow: 302; - export const MatriarchalSpear: 303; - export const MatriarchalPike: 304; - export const MatriarchalJavelin: 305; - export const Cap: 306; - export const SkullCap: 307; - export const Helm: 308; - export const FullHelm: 309; - export const GreatHelm: 310; - export const Crown: 311; - export const Mask: 312; - export const QuiltedArmor: 313; - export const LeatherArmor: 314; - export const HardLeatherArmor: 315; - export const StuddedLeather: 316; - export const RingMail: 317; - export const ScaleMail: 318; - export const ChainMail: 319; - export const BreastPlate: 320; - export const SplintMail: 321; - export const PlateMail: 322; - export const FieldPlate: 323; - export const GothicPlate: 324; - export const FullPlateMail: 325; - export const AncientArmor: 326; - export const LightPlate: 327; - export const Buckler: 328; - export const SmallShield: 329; - export const LargeShield: 330; - export const KiteShield: 331; - export const TowerShield: 332; - export const GothicShield: 333; - export const LeatherGloves: 334; - export const HeavyGloves: 335; - export const ChainGloves: 336; - export const LightGauntlets: 337; - export const Gauntlets: 338; - export const Boots: 339; - export const HeavyBoots: 340; - export const ChainBoots: 341; - export const LightPlatedBoots: 342; - export const Greaves: 343; - export const Sash: 344; - export const LightBelt: 345; - export const Belt: 346; - export const HeavyBelt: 347; - export const PlatedBelt: 348; - export const BoneHelm: 349; - export const BoneShield: 350; - export const SpikedShield: 351; - export const WarHat: 352; - export const Sallet: 353; - export const Casque: 354; - export const Basinet: 355; - export const WingedHelm: 356; - export const GrandCrown: 357; - export const DeathMask: 358; - export const GhostArmor: 359; - export const SerpentskinArmor: 360; - export const DemonhideArmor: 361; - export const TrellisedArmor: 362; - export const LinkedMail: 363; - export const TigulatedMail: 364; - export const MeshArmor: 365; - export const Cuirass: 366; - export const RussetArmor: 367; - export const TemplarCoat: 368; - export const SharktoothArmor: 369; - export const EmbossedPlate: 370; - export const ChaosArmor: 371; - export const OrnatePlate: 372; - export const MagePlate: 373; - export const Defender: 374; - export const RoundShield: 375; - export const Scutum: 376; - export const DragonShield: 377; - export const Pavise: 378; - export const AncientShield: 379; - export const DemonhideGloves: 380; - export const SharkskinGloves: 381; - export const HeavyBracers: 382; - export const BattleGauntlets: 383; - export const WarGauntlets: 384; - export const DemonhideBoots: 385; - export const SharkskinBoots: 386; - export const MeshBoots: 387; - export const BattleBoots: 388; - export const WarBoots: 389; - export const DemonhideSash: 390; - export const SharkskinBelt: 391; - export const MeshBelt: 392; - export const BattleBelt: 393; - export const WarBelt: 394; - export const GrimHelm: 395; - export const GrimShield: 396; - export const BarbedShield: 397; - export const WolfHead: 398; - export const HawkHelm: 399; - export const Antlers: 400; - export const FalconMask: 401; - export const SpiritMask: 402; - export const JawboneCap: 403; - export const FangedHelm: 404; - export const HornedHelm: 405; - export const AssaultHelmet: 406; - export const AvengerGuard: 407; - export const Targe: 408; - export const Rondache: 409; - export const HeraldicShield: 410; - export const AerinShield: 411; - export const CrownShield: 412; - export const PreservedHead: 413; - export const ZombieHead: 414; - export const UnravellerHead: 415; - export const GargoyleHead: 416; - export const DemonHead: 417; - export const Circlet: 418; - export const Coronet: 419; - export const Tiara: 420; - export const Diadem: 421; - export const Shako: 422; - export const Hydraskull: 423; - export const Armet: 424; - export const GiantConch: 425; - export const SpiredHelm: 426; - export const Corona: 427; - export const Demonhead: 428; - export const DuskShroud: 429; - export const Wyrmhide: 430; - export const ScarabHusk: 431; - export const WireFleece: 432; - export const DiamondMail: 433; - export const LoricatedMail: 434; - export const Boneweave: 435; - export const GreatHauberk: 436; - export const BalrogSkin: 437; - export const HellforgePlate: 438; - export const KrakenShell: 439; - export const LacqueredPlate: 440; - export const ShadowPlate: 441; - export const SacredArmor: 442; - export const ArchonPlate: 443; - export const Heater: 444; - export const Luna: 445; - export const Hyperion: 446; - export const Monarch: 447; - export const Aegis: 448; - export const Ward: 449; - export const BrambleMitts: 450; - export const VampireboneGloves: 451; - export const Vambraces: 452; - export const CrusaderGauntlets: 453; - export const OgreGauntlets: 454; - export const WyrmhideBoots: 455; - export const ScarabshellBoots: 456; - export const BoneweaveBoots: 457; - export const MirroredBoots: 458; - export const MyrmidonGreaves: 459; - export const SpiderwebSash: 460; - export const VampirefangBelt: 461; - export const MithrilCoil: 462; - export const TrollBelt: 463; - export const ColossusGirdle: 464; - export const BoneVisage: 465; - export const TrollNest: 466; - export const BladeBarrier: 467; - export const AlphaHelm: 468; - export const GriffonHeaddress: 469; - export const HuntersGuise: 470; - export const SacredFeathers: 471; - export const TotemicMask: 472; - export const JawboneVisor: 473; - export const LionHelm: 474; - export const RageMask: 475; - export const SavageHelmet: 476; - export const SlayerGuard: 477; - export const AkaranTarge: 478; - export const AkaranRondache: 479; - export const ProtectorShield: 480; - export const GildedShield: 481; - export const RoyalShield: 482; - export const MummifiedTrophy: 483; - export const FetishTrophy: 484; - export const SextonTrophy: 485; - export const CantorTrophy: 486; - export const HierophantTrophy: 487; - export const BloodSpirit: 488; - export const SunSpirit: 489; - export const EarthSpirit: 490; - export const SkySpirit: 491; - export const DreamSpirit: 492; - export const CarnageHelm: 493; - export const FuryVisor: 494; - export const DestroyerHelm: 495; - export const ConquerorCrown: 496; - export const GuardianCrown: 497; - export const SacredTarge: 498; - export const SacredRondache: 499; - export const KurastShield: 500; - export const ZakarumShield: 501; - export const VortexShield: 502; - export const MinionSkull: 503; - export const HellspawnSkull: 504; - export const OverseerSkull: 505; - export const SuccubusSkull: 506; - export const BloodlordSkull: 507; - export const Amulet: 520; - export const Ring: 522; - export const Arrows: 526; - export const Bolts: 528; - export const Jewel: 643; + export namespace items { + export namespace cost { + const ToBuy: 0; + const ToSell: 1; + const ToRepair: 2; + } + export namespace flags { + const Equipped: 0x00000001; + const InSocket: 0x00000008; + const Identified: 0x00000010; + const OnActiveWeaponSlot: 0x00000040; + const OnSwapWeaponSlot: 0x00000080; + const Broken: 0x00000100; + const FullRejuv: 0x00000400; + const Socketed: 0x00000800; + const InTradeGamble: 0x00002000; + const NotInSocket: 0x00004000; + const Ear: 0x00010000; + const StartingItem: 0x00020000; + const RuneQuestPotion: 0x00200000; + const Ethereal: 0x00400000; + const IsAnItem: 0x00800000; + const Personalized: 0x01000000; + const Runeword: 0x04000000; + } + export namespace mode { + const inStorage: 0; //Item inven stash cube store = Item inven stash cube store + const Equipped: 1; // Item equipped self or merc + const inBelt: 2; // Item in belt + const onGround: 3; // Item on ground + const onCursor: 4; // Item on cursor + const Dropping: 5; // Item being dropped + const Socketed: 6 // Item socketed in item + } + export namespace quality { + const LowQuality: 1; + const Normal: 2; + const Superior: 3; + const Magic: 4; + const Set: 5; + const Rare: 6; + const Unique: 7; + const Crafted: 8; + } + export namespace _class1 { + const Normal: 0; + const Exceptional: 1; + const Elite: 2; + } + export { _class1 as class }; + export namespace type { + const Shield: 2; + const Armor: 3; + const Gold: 4; + const BowQuiver: 5; + const CrossbowQuiver: 6; + const PlayerBodyPart: 7; + const Herb: 8; + const Potion: 9; + const Ring: 10; + const Elixir: 11; + const Amulet: 12; + const Charm: 13; + const notused0: 14; + const Boots: 15; + const Gloves: 16; + const notused1: 17; + const Book: 18; + const Belt: 19; + const Gem: 20; + const Torch: 21; + const Scroll: 22; + const notused2: 23; + const Scepter: 24; + const Wand: 25; + const Staff: 26; + const Bow: 27; + const Axe: 28; + const Club: 29; + const Sword: 30; + const Hammer: 31; + const Knife: 32; + const Spear: 33; + const Polearm: 34; + const Crossbow: 35; + const Mace: 36; + const Helm: 37; + const MissilePotion: 38; + const Quest: 39; + const Bodypart: 40; + const Key: 41; + const ThrowingKnife: 42; + const ThrowingAxe: 43; + const Javelin: 44; + const Weapon: 45; + const MeleeWeapon: 46; + const MissileWeapon: 47; + const ThrownWeapon: 48; + const ComboWeapon: 49; + const AnyArmor: 50; + const AnyShield: 51; + const Miscellaneous: 52; + const SocketFiller: 53; + const Secondhand: 54; + const StavesandRods: 55; + const Missile: 56; + const Blunt: 57; + const Jewel: 58; + const ClassSpecific: 59; + const AmazonItem: 60; + const BarbarianItem: 61; + const NecromancerItem: 62; + const PaladinItem: 63; + const SorceressItem: 64; + const AssassinItem: 65; + const DruidItem: 66; + const HandtoHand: 67; + const Orb: 68; + const VoodooHeads: 69; + const AuricShields: 70; + const PrimalHelm: 71; + const Pelt: 72; + const Cloak: 73; + const Rune: 74; + const Circlet: 75; + const HealingPotion: 76; + const ManaPotion: 77; + const RejuvPotion: 78; + const StaminaPotion: 79; + const AntidotePotion: 80; + const ThawingPotion: 81; + const SmallCharm: 82; + const LargeCharm: 83; + const GrandCharm: 84; + const AmazonBow: 85; + const AmazonSpear: 86; + const AmazonJavelin: 87; + const AssassinClaw: 88; + const MagicBowQuiv: 89; + const MagicxBowQuiv: 90; + const ChippedGem: 91; + const FlawedGem: 92; + const StandardGem: 93; + const FlawlessGem: 94; + const PerfectgGem: 95; + const Amethyst: 96; + const Diamond: 97; + const Emerald: 98; + const Ruby: 99; + const Sapphire: 100; + const Topaz: 101; + const Skull: 102; + } + + // Weapons + export const HandAxe: 0; + export const Axe: 1; + export const DoubleAxe: 2; + export const MilitaryPick: 3; + export const WarAxe: 4; + export const LargeAxe: 5; + export const BroadAxe: 6; + export const BattleAxe: 7; + export const GreatAxe: 8; + export const GiantAxe: 9; + export const Wand: 10; + export const YewWand: 11; + export const BoneWand: 12; + export const GrimWand: 13; + export const Club: 14; + export const Scepter: 15; + export const GrandScepter: 16; + export const WarScepter: 17; + export const SpikedClub: 18; + export const Mace: 19; + export const MorningStar: 20; + export const Flail: 21; + export const WarHammer: 22; + export const Maul: 23; + export const GreatMaul: 24; + export const ShortSword: 25; + export const Scimitar: 26; + export const Sabre: 27; + export const Falchion: 28; + export const CrystalSword: 29; + export const BroadSword: 30; + export const LongSword: 31; + export const WarSword: 32; + export const Two_HandedSword: 33; + export const Claymore: 34; + export const GiantSword: 35; + export const BastardSword: 36; + export const Flamberge: 37; + export const GreatSword: 38; + export const Dagger: 39; + export const Dirk: 40; + export const Kris: 41; + export const Blade: 42; + export const ThrowingKnife: 43; + export const ThrowingAxe: 44; + export const BalancedKnife: 45; + export const BalancedAxe: 46; + export const Javelin: 47; + export const Pilum: 48; + export const ShortSpear: 49; + export const Glaive: 50; + export const ThrowingSpear: 51; + export const Spear: 52; + export const Trident: 53; + export const Brandistock: 54; + export const Spetum: 55; + export const Pike: 56; + export const Bardiche: 57; + export const Voulge: 58; + export const Scythe: 59; + export const Poleaxe: 60; + export const Halberd: 61; + export const WarScythe: 62; + export const ShortStaff: 63; + export const LongStaff: 64; + export const GnarledStaff: 65; + export const BattleStaff: 66; + export const WarStaff: 67; + export const ShortBow: 68; + export const HuntersBow: 69; + export const LongBow: 70; + export const CompositeBow: 71; + export const ShortBattleBow: 72; + export const LongBattleBow: 73; + export const ShortWarBow: 74; + export const LongWarBow: 75; + export const LightCrossbow: 76; + export const Crossbow: 77; + export const HeavyCrossbow: 78; + export const RepeatingCrossbow: 79; + export const Hatchet: 93; + export const Cleaver: 94; + export const TwinAxe: 95; + export const Crowbill: 96; + export const Naga: 97; + export const MilitaryAxe: 98; + export const BeardedAxe: 99; + export const Tabar: 100; + export const GothicAxe: 101; + export const AncientAxe: 102; + export const BurntWand: 103; + export const PetrifiedWand: 104; + export const TombWand: 105; + export const GraveWand: 106; + export const Cudgel: 107; + export const RuneScepter: 108; + export const HolyWaterSprinkler: 109; + export const DivineScepter: 110; + export const BarbedClub: 111; + export const FlangedMace: 112; + export const JaggedStar: 113; + export const Knout: 114; + export const BattleHammer: 115; + export const WarClub: 116; + export const MarteldeFer: 117; + export const Gladius: 118; + export const Cutlass: 119; + export const Shamshir: 120; + export const Tulwar: 121; + export const DimensionalBlade: 122; + export const BattleSword: 123; + export const RuneSword: 124; + export const AncientSword: 125; + export const Espandon: 126; + export const DacianFalx: 127; + export const TuskSword: 128; + export const GothicSword: 129; + export const Zweihander: 130; + export const ExecutionerSword: 131; + export const Poignard: 132; + export const Rondel: 133; + export const Cinquedeas: 134; + export const Stiletto: 135; + export const BattleDart: 136; + export const Francisca: 137; + export const WarDart: 138; + export const Hurlbat: 139; + export const WarJavelin: 140; + export const GreatPilum: 141; + export const Simbilan: 142; + export const Spiculum: 143; + export const Harpoon: 144; + export const WarSpear: 145; + export const Fuscina: 146; + export const WarFork: 147; + export const Yari: 148; + export const Lance: 149; + export const LochaberAxe: 150; + export const Bill: 151; + export const BattleScythe: 152; + export const Partizan: 153; + export const Bec_de_Corbin: 154; + export const GrimScythe: 155; + export const JoStaff: 156; + export const Quarterstaff: 157; + export const CedarStaff: 158; + export const GothicStaff: 159; + export const RuneStaff: 160; + export const EdgeBow: 161; + export const RazorBow: 162; + export const CedarBow: 163; + export const DoubleBow: 164; + export const ShortSiegeBow: 165; + export const LargeSiegeBow: 166; + export const RuneBow: 167; + export const GothicBow: 168; + export const Arbalest: 169; + export const SiegeCrossbow: 170; + export const Ballista: 171; + export const Chu_Ko_Nu: 172; + export const Katar: 175; + export const WristBlade: 176; + export const HatchetHands: 177; + export const Cestus: 178; + export const Claws: 179; + export const BladeTalons: 180; + export const ScissorsKatar: 181; + export const Quhab: 182; + export const WristSpike: 183; + export const Fascia: 184; + export const HandScythe: 185; + export const GreaterClaws: 186; + export const GreaterTalons: 187; + export const ScissorsQuhab: 188; + export const Suwayyah: 189; + export const WristSword: 190; + export const WarFist: 191; + export const BattleCestus: 192; + export const FeralClaws: 193; + export const RunicTalons: 194; + export const ScissorsSuwayyah: 195; + export const Tomahawk: 196; + export const SmallCrescent: 197; + export const EttinAxe: 198; + export const WarSpike: 199; + export const BerserkerAxe: 200; + export const FeralAxe: 201; + export const Silver_edgedAxe: 202; + export const Decapitator: 203; + export const ChampionAxe: 204; + export const GloriousAxe: 205; + export const PolishedWand: 206; + export const GhostWand: 207; + export const LichWand: 208; + export const UnearthedWand: 209; + export const Truncheon: 210; + export const MightyScepter: 211; + export const SeraphRod: 212; + export const Caduceus: 213; + export const TyrantClub: 214; + export const ReinforcedMace: 215; + export const DevilStar: 216; + export const Scourge: 217; + export const LegendaryMallet: 218; + export const OgreMaul: 219; + export const ThunderMaul: 220; + export const Falcata: 221; + export const Ataghan: 222; + export const ElegantBlade: 223; + export const HydraEdge: 224; + export const PhaseBlade: 225; + export const ConquestSword: 226; + export const CrypticSword: 227; + export const MythicalSword: 228; + export const LegendSword: 229; + export const HighlandBlade: 230; + export const BalrogBlade: 231; + export const ChampionSword: 232; + export const ColossusSword: 233; + export const ColossusBlade: 234; + export const BoneKnife: 235; + export const MithrilPoint: 236; + export const FangedKnife: 237; + export const LegendSpike: 238; + export const FlyingKnife: 239; + export const FlyingAxe: 240; + export const WingedKnife: 241; + export const WingedAxe: 242; + export const HyperionJavelin: 243; + export const StygianPilum: 244; + export const BalrogSpear: 245; + export const GhostGlaive: 246; + export const WingedHarpoon: 247; + export const HyperionSpear: 248; + export const StygianPike: 249; + export const Mancatcher: 250; + export const GhostSpear: 251; + export const WarPike: 252; + export const OgreAxe: 253; + export const ColossusVoulge: 254; + export const Thresher: 255; + export const CrypticAxe: 256; + export const GreatPoleaxe: 257; + export const GiantThresher: 258; + export const WalkingStick: 259; + export const Stalagmite: 260; + export const ElderStaff: 261; + export const Shillelagh: 262; + export const ArchonStaff: 263; + export const SpiderBow: 264; + export const BladeBow: 265; + export const ShadowBow: 266; + export const GreatBow: 267; + export const DiamondBow: 268; + export const CrusaderBow: 269; + export const WardBow: 270; + export const HydraBow: 271; + export const PelletBow: 272; + export const GorgonCrossbow: 273; + export const ColossusCrossbow: 274; + export const DemonCrossbow: 275; + export const EagleOrb: 276; + export const SacredGlobe: 277; + export const SmokedSphere: 278; + export const ClaspedOrb: 279; + export const JaredsStone: 280; + export const StagBow: 281; + export const ReflexBow: 282; + export const MaidenSpear: 283; + export const MaidenPike: 284; + export const MaidenJavelin: 285; + export const GlowingOrb: 286; + export const CrystallineGlobe: 287; + export const CloudySphere: 288; + export const SparklingBall: 289; + export const SwirlingCrystal: 290; + export const AshwoodBow: 291; + export const CeremonialBow: 292; + export const CeremonialSpear: 293; + export const CeremonialPike: 294; + export const CeremonialJavelin: 295; + export const HeavenlyStone: 296; + export const EldritchOrb: 297; + export const DemonHeart: 298; + export const VortexOrb: 299; + export const DimensionalShard: 300; + export const MatriarchalBow: 301; + export const GrandMatronBow: 302; + export const MatriarchalSpear: 303; + export const MatriarchalPike: 304; + export const MatriarchalJavelin: 305; + export const Cap: 306; + export const SkullCap: 307; + export const Helm: 308; + export const FullHelm: 309; + export const GreatHelm: 310; + export const Crown: 311; + export const Mask: 312; + export const QuiltedArmor: 313; + export const LeatherArmor: 314; + export const HardLeatherArmor: 315; + export const StuddedLeather: 316; + export const RingMail: 317; + export const ScaleMail: 318; + export const ChainMail: 319; + export const BreastPlate: 320; + export const SplintMail: 321; + export const PlateMail: 322; + export const FieldPlate: 323; + export const GothicPlate: 324; + export const FullPlateMail: 325; + export const AncientArmor: 326; + export const LightPlate: 327; + export const Buckler: 328; + export const SmallShield: 329; + export const LargeShield: 330; + export const KiteShield: 331; + export const TowerShield: 332; + export const GothicShield: 333; + export const LeatherGloves: 334; + export const HeavyGloves: 335; + export const ChainGloves: 336; + export const LightGauntlets: 337; + export const Gauntlets: 338; + export const Boots: 339; + export const HeavyBoots: 340; + export const ChainBoots: 341; + export const LightPlatedBoots: 342; + export const Greaves: 343; + export const Sash: 344; + export const LightBelt: 345; + export const Belt: 346; + export const HeavyBelt: 347; + export const PlatedBelt: 348; + export const BoneHelm: 349; + export const BoneShield: 350; + export const SpikedShield: 351; + export const WarHat: 352; + export const Sallet: 353; + export const Casque: 354; + export const Basinet: 355; + export const WingedHelm: 356; + export const GrandCrown: 357; + export const DeathMask: 358; + export const GhostArmor: 359; + export const SerpentskinArmor: 360; + export const DemonhideArmor: 361; + export const TrellisedArmor: 362; + export const LinkedMail: 363; + export const TigulatedMail: 364; + export const MeshArmor: 365; + export const Cuirass: 366; + export const RussetArmor: 367; + export const TemplarCoat: 368; + export const SharktoothArmor: 369; + export const EmbossedPlate: 370; + export const ChaosArmor: 371; + export const OrnatePlate: 372; + export const MagePlate: 373; + export const Defender: 374; + export const RoundShield: 375; + export const Scutum: 376; + export const DragonShield: 377; + export const Pavise: 378; + export const AncientShield: 379; + export const DemonhideGloves: 380; + export const SharkskinGloves: 381; + export const HeavyBracers: 382; + export const BattleGauntlets: 383; + export const WarGauntlets: 384; + export const DemonhideBoots: 385; + export const SharkskinBoots: 386; + export const MeshBoots: 387; + export const BattleBoots: 388; + export const WarBoots: 389; + export const DemonhideSash: 390; + export const SharkskinBelt: 391; + export const MeshBelt: 392; + export const BattleBelt: 393; + export const WarBelt: 394; + export const GrimHelm: 395; + export const GrimShield: 396; + export const BarbedShield: 397; + export const WolfHead: 398; + export const HawkHelm: 399; + export const Antlers: 400; + export const FalconMask: 401; + export const SpiritMask: 402; + export const JawboneCap: 403; + export const FangedHelm: 404; + export const HornedHelm: 405; + export const AssaultHelmet: 406; + export const AvengerGuard: 407; + export const Targe: 408; + export const Rondache: 409; + export const HeraldicShield: 410; + export const AerinShield: 411; + export const CrownShield: 412; + export const PreservedHead: 413; + export const ZombieHead: 414; + export const UnravellerHead: 415; + export const GargoyleHead: 416; + export const DemonHead: 417; + export const Circlet: 418; + export const Coronet: 419; + export const Tiara: 420; + export const Diadem: 421; + export const Shako: 422; + export const Hydraskull: 423; + export const Armet: 424; + export const GiantConch: 425; + export const SpiredHelm: 426; + export const Corona: 427; + export const Demonhead: 428; + export const DuskShroud: 429; + export const Wyrmhide: 430; + export const ScarabHusk: 431; + export const WireFleece: 432; + export const DiamondMail: 433; + export const LoricatedMail: 434; + export const Boneweave: 435; + export const GreatHauberk: 436; + export const BalrogSkin: 437; + export const HellforgePlate: 438; + export const KrakenShell: 439; + export const LacqueredPlate: 440; + export const ShadowPlate: 441; + export const SacredArmor: 442; + export const ArchonPlate: 443; + export const Heater: 444; + export const Luna: 445; + export const Hyperion: 446; + export const Monarch: 447; + export const Aegis: 448; + export const Ward: 449; + export const BrambleMitts: 450; + export const VampireboneGloves: 451; + export const Vambraces: 452; + export const CrusaderGauntlets: 453; + export const OgreGauntlets: 454; + export const WyrmhideBoots: 455; + export const ScarabshellBoots: 456; + export const BoneweaveBoots: 457; + export const MirroredBoots: 458; + export const MyrmidonGreaves: 459; + export const SpiderwebSash: 460; + export const VampirefangBelt: 461; + export const MithrilCoil: 462; + export const TrollBelt: 463; + export const ColossusGirdle: 464; + export const BoneVisage: 465; + export const TrollNest: 466; + export const BladeBarrier: 467; + export const AlphaHelm: 468; + export const GriffonHeaddress: 469; + export const HuntersGuise: 470; + export const SacredFeathers: 471; + export const TotemicMask: 472; + export const JawboneVisor: 473; + export const LionHelm: 474; + export const RageMask: 475; + export const SavageHelmet: 476; + export const SlayerGuard: 477; + export const AkaranTarge: 478; + export const AkaranRondache: 479; + export const ProtectorShield: 480; + export const GildedShield: 481; + export const RoyalShield: 482; + export const MummifiedTrophy: 483; + export const FetishTrophy: 484; + export const SextonTrophy: 485; + export const CantorTrophy: 486; + export const HierophantTrophy: 487; + export const BloodSpirit: 488; + export const SunSpirit: 489; + export const EarthSpirit: 490; + export const SkySpirit: 491; + export const DreamSpirit: 492; + export const CarnageHelm: 493; + export const FuryVisor: 494; + export const DestroyerHelm: 495; + export const ConquerorCrown: 496; + export const GuardianCrown: 497; + export const SacredTarge: 498; + export const SacredRondache: 499; + export const KurastShield: 500; + export const ZakarumShield: 501; + export const VortexShield: 502; + export const MinionSkull: 503; + export const HellspawnSkull: 504; + export const OverseerSkull: 505; + export const SuccubusSkull: 506; + export const BloodlordSkull: 507; + export const Amulet: 520; + export const Ring: 522; + export const Arrows: 526; + export const Bolts: 528; + export const Jewel: 643; - // Misc? - const Elixir: 508; - const Torch: 527; - const Heart: 531; - const Brain: 532; - const Jawbone: 533; - const Eye: 534; - const Horn: 535; - const Tail: 536; - const Flag: 537; - const Fang: 538; - const Quill: 539; - const Soul: 540; - const Scalp: 541; - const Spleen: 542; - const Key: 543; - const Ear: 556; - const Herb: 602; - const anevilforce: 609; - // Potions, tomes/scrolls, gold - export const TomeofTownPortal: 518; - export const TomeofIdentify: 519; - export const ScrollofTownPortal: 529; - export const ScrollofIdentify: 530; - export const RancidGasPotion: 80; - export const OilPotion: 81; - export const ChokingGasPotion: 82; - export const ExplodingPotion: 83; - export const StranglingGasPotion: 84; - export const FulminatingPotion: 85; - export const StaminaPotion: 513; - export const AntidotePotion: 514; - export const RejuvenationPotion: 515; - export const FullRejuvenationPotion: 516; - export const ThawingPotion: 517; - export const MinorHealingPotion: 587; - export const LightHealingPotion: 588; - export const HealingPotion: 589; - export const GreaterHealingPotion: 590; - export const SuperHealingPotion: 591; - export const MinorManaPotion: 592; - export const LightManaPotion: 593; - export const ManaPotion: 594; - export const GreaterManaPotion: 595; - export const SuperManaPotion: 596; - export const Gold: 523; - // Charms - export const SmallCharm: 603; - export const LargeCharm: 604; - export const GrandCharm: 605; + // Misc? + const Elixir: 508; + const Torch: 527; + const Heart: 531; + const Brain: 532; + const Jawbone: 533; + const Eye: 534; + const Horn: 535; + const Tail: 536; + const Flag: 537; + const Fang: 538; + const Quill: 539; + const Soul: 540; + const Scalp: 541; + const Spleen: 542; + const Key: 543; + const Ear: 556; + const Herb: 602; + const anevilforce: 609; + // Potions, tomes/scrolls, gold + export const TomeofTownPortal: 518; + export const TomeofIdentify: 519; + export const ScrollofTownPortal: 529; + export const ScrollofIdentify: 530; + export const RancidGasPotion: 80; + export const OilPotion: 81; + export const ChokingGasPotion: 82; + export const ExplodingPotion: 83; + export const StranglingGasPotion: 84; + export const FulminatingPotion: 85; + export const StaminaPotion: 513; + export const AntidotePotion: 514; + export const RejuvenationPotion: 515; + export const FullRejuvenationPotion: 516; + export const ThawingPotion: 517; + export const MinorHealingPotion: 587; + export const LightHealingPotion: 588; + export const HealingPotion: 589; + export const GreaterHealingPotion: 590; + export const SuperHealingPotion: 591; + export const MinorManaPotion: 592; + export const LightManaPotion: 593; + export const ManaPotion: 594; + export const GreaterManaPotion: 595; + export const SuperManaPotion: 596; + export const Gold: 523; + // Charms + export const SmallCharm: 603; + export const LargeCharm: 604; + export const GrandCharm: 605; - export namespace quest { - // Act 1 - const WirtsLeg: 88; - const HoradricMalus: 89; - const ScrollofInifuss: 524; - const KeytotheCairnStones: 525; - // Act 2 - const FinishedStaff: 91; - const HoradricStaff: 91; - const IncompleteStaff: 92; - const ShaftoftheHoradricStaff: 92; - const ViperAmulet: 521; - const TopoftheHoradricStaff: 521; - const Cube: 549; - const BookofSkill: 552; - // Act 3 - const DecoyGidbinn: 86; - const TheGidbinn: 87; - const KhalimsFlail: 173; - const KhalimsWill: 174; - const PotofLife: 545; - const AJadeFigurine: 546; - const JadeFigurine: 546; - const TheGoldenBird: 547; - const LamEsensTome: 548; - const KhalimsEye: 553; - const KhalimsHeart: 554; - const KhalimsBrain: 555; - // Act 4 - const HellForgeHammer: 90; - const Soulstone: 551; - const MephistosSoulstone: 551; - // Act 5 - const MalahsPotion: 644; - const ScrollofKnowledge: 645; - const ScrollofResistance: 646; - // Pandemonium Event - const KeyofTerror: 647; - const KeyofHate: 648; - const KeyofDestruction: 649; - const DiablosHorn: 650; - const BaalsEye: 651; - const MephistosBrain: 652; - const StandardofHeroes: 658; - // Essences/Token - const TokenofAbsolution: 653; - const TwistedEssenceofSuffering: 654; - const ChargedEssenceofHatred: 655; - const BurningEssenceofTerror: 656; - const FesteringEssenceofDestruction: 657; - // Misc - const TheBlackTowerKey: 544; - } - export namespace runes { - const El: 610; - const Eld: 611; - const Tir: 612; - const Nef: 613; - const Eth: 614; - const Ith: 615; - const Tal: 616; - const Ral: 617; - const Ort: 618; - const Thul: 619; - const Amn: 620; - const Sol: 621; - const Shael: 622; - const Dol: 623; - const Hel: 624; - const Io: 625; - const Lum: 626; - const Ko: 627; - const Fal: 628; - const Lem: 629; - const Pul: 630; - const Um: 631; - const Mal: 632; - const Ist: 633; - const Gul: 634; - const Vex: 635; - const Ohm: 636; - const Lo: 637; - const Sur: 638; - const Ber: 639; - const Jah: 640; - const Cham: 641; - const Zod: 642; - } - export namespace gems { - export namespace Perfect { - const Amethyst: 561; - const Topaz: 566; - const Sapphire: 571; - const Emerald: 576; - const Ruby: 581; - const Diamond: 586; - const Skull: 601; - } - export namespace Flawless { - const Amethyst: 560; - const Topaz: 565; - const Sapphire: 570; - const Emerald: 575; - const Ruby: 580; - const Diamond: 585; - const Skull: 600; - } - export namespace Normal { - const Amethyst: 559; - const Topaz: 564; - const Sapphire: 569; - const Emerald: 574; - const Ruby: 579; - const Diamond: 584; - const Skull: 599; - } - export namespace Flawed { - const Amethyst: 558; - const Topaz: 563; - const Sapphire: 568; - const Emerald: 573; - const Ruby: 578; - const Diamond: 583; - const Skull: 598; - } - export namespace Chipped { - const Amethyst: 557; - const Topaz: 562; - const Sapphire: 567; - const Emerald: 572; - const Ruby: 577; - const Diamond: 582; - const Skull: 597; - } - } - } + export namespace quest { + // Act 1 + const WirtsLeg: 88; + const HoradricMalus: 89; + const ScrollofInifuss: 524; + const KeytotheCairnStones: 525; + // Act 2 + const FinishedStaff: 91; + const HoradricStaff: 91; + const IncompleteStaff: 92; + const ShaftoftheHoradricStaff: 92; + const ViperAmulet: 521; + const TopoftheHoradricStaff: 521; + const Cube: 549; + const BookofSkill: 552; + // Act 3 + const DecoyGidbinn: 86; + const TheGidbinn: 87; + const KhalimsFlail: 173; + const KhalimsWill: 174; + const PotofLife: 545; + const AJadeFigurine: 546; + const JadeFigurine: 546; + const TheGoldenBird: 547; + const LamEsensTome: 548; + const KhalimsEye: 553; + const KhalimsHeart: 554; + const KhalimsBrain: 555; + // Act 4 + const HellForgeHammer: 90; + const Soulstone: 551; + const MephistosSoulstone: 551; + // Act 5 + const MalahsPotion: 644; + const ScrollofKnowledge: 645; + const ScrollofResistance: 646; + // Pandemonium Event + const KeyofTerror: 647; + const KeyofHate: 648; + const KeyofDestruction: 649; + const DiablosHorn: 650; + const BaalsEye: 651; + const MephistosBrain: 652; + const StandardofHeroes: 658; + // Essences/Token + const TokenofAbsolution: 653; + const TwistedEssenceofSuffering: 654; + const ChargedEssenceofHatred: 655; + const BurningEssenceofTerror: 656; + const FesteringEssenceofDestruction: 657; + // Misc + const TheBlackTowerKey: 544; + } + export namespace runes { + const El: 610; + const Eld: 611; + const Tir: 612; + const Nef: 613; + const Eth: 614; + const Ith: 615; + const Tal: 616; + const Ral: 617; + const Ort: 618; + const Thul: 619; + const Amn: 620; + const Sol: 621; + const Shael: 622; + const Dol: 623; + const Hel: 624; + const Io: 625; + const Lum: 626; + const Ko: 627; + const Fal: 628; + const Lem: 629; + const Pul: 630; + const Um: 631; + const Mal: 632; + const Ist: 633; + const Gul: 634; + const Vex: 635; + const Ohm: 636; + const Lo: 637; + const Sur: 638; + const Ber: 639; + const Jah: 640; + const Cham: 641; + const Zod: 642; + } + export namespace gems { + export namespace Perfect { + const Amethyst: 561; + const Topaz: 566; + const Sapphire: 571; + const Emerald: 576; + const Ruby: 581; + const Diamond: 586; + const Skull: 601; + } + export namespace Flawless { + const Amethyst: 560; + const Topaz: 565; + const Sapphire: 570; + const Emerald: 575; + const Ruby: 580; + const Diamond: 585; + const Skull: 600; + } + export namespace Normal { + const Amethyst: 559; + const Topaz: 564; + const Sapphire: 569; + const Emerald: 574; + const Ruby: 579; + const Diamond: 584; + const Skull: 599; + } + export namespace Flawed { + const Amethyst: 558; + const Topaz: 563; + const Sapphire: 568; + const Emerald: 573; + const Ruby: 578; + const Diamond: 583; + const Skull: 598; + } + export namespace Chipped { + const Amethyst: 557; + const Topaz: 562; + const Sapphire: 567; + const Emerald: 572; + const Ruby: 577; + const Diamond: 582; + const Skull: 597; + } + } + } - // locale strings - export namespace locale { - export namespace monsters { - // bosses - const Andariel: 3021; - const Duriel: 3054; - const Mephisto: 3062; - const Diablo: 3060; - const Baal: 3061; - // Mini bosses - const BloodRaven: 3111; - const TreeheadWoodFist: 2873; - const TheCountess: 2875; - const TheSmith: 2889; - const Radament: 2879; - const TheSummoner: 3099; - const HephastoTheArmorer: 1067; - const Izual: 1014; - const ShenktheOverseer: 22435; - // Uniques - const Corpsefire: 3319; - const TheCowKing: 2850; - const GrandVizierofChaos: 2851; - const LordDeSeis: 2852; - const InfectorofSouls: 2853; - const RiftwraiththeCannibal: 2854; - const Taintbreeder: 2855; - const TheTormentor: 2856; - const Darkwing: 2857; - const MafferDragonhand: 2858; - const WyandVoidbringer: 2859; - const ToorcIcefist: 2860; - const BremmSparkfist: 2861; - const GelebFlamefinger: 2862; - const IsmailVilehand: 2863; - const IcehawkRiftwing: 2864; - const BattlemaidSarina: 2865; - const Stormtree: 2866; - const WitchDoctorEndugu: 2867; - const SszarkTheBurning: 2868; - const Bishibosh: 2869; - const Bonebreaker: 2870; - const Coldcrow: 2871; - const Rakanishu: 2872; - const Griswold: 2874; - const PitspawnFouldog: 2876; - const FlamespiketheCrawler: 2877; - const BoneAsh: 2878; - const BloodwitchtheWild: 2880; - const Fangskin: 2881; - const Beetleburst: 2882; - const CreepingFeature: 2883; - const ColdwormtheBurrower: 2884; - const FireEye: 2885; - const DarkElder: 2886; - const AncientKaatheSoulless: 2888; - const SharpToothSayer: 22493; - const SnapchipShatter: 22496; - const Pindleskin: 22497; - const ThreshSocket: 22498; - const EyebacktheUnleashed: 22499; - const EldritchtheRectifier: 22500; - const DacFarren: 22501; - const BonesawBreaker: 22502; - const Frozenstein: 22504; - const Rogue: 2897; - const StygianDoll: 2898; - const SoulKiller: 2899; - const Flayer: 2900; - const Fetish: 2901; - const RatMan: 2902; - const UndeadStygianDoll: 2903; - const UndeadSoulKiller: 2904; - const UndeadFlayer: 2905; - const UndeadFetish: 2906; - const UndeadRatMan: 2907; - const DarkFamiliar: 2908; - const BloodDiver: 2909; - const Gloombat: 2910; - const DesertWing: 2911; - const TheBanished: 2912; - const BloodLord: 2913; - const DarkLord: 2914; - const NightLord: 2915; - const GhoulLord: 2916; - const Spikefist: 2917; - const Thrasher: 2918; - const BrambleHulk: 2919; - const ThornedHulk: 2920; - const SpiderMagus: 2921; - const FlameSpider: 2922; - const PoisonSpinner: 2923; - const SandFisher: 2924; - const Arach: 2925; - const BloodWing: 2926; - const BloodHook: 2927; - const Feeder: 2928; - const Sucker: 2929; - const WingedNightmare: 2930; - const HellBuzzard: 2931; - const UndeadScavenger: 2932; - const CarrionBird: 2933; - const Unraveler: 2934; - const Guardian: 2935; - const HollowOne: 2936; - const HoradrimAncient: 2937; - const BoneScarab: 2938; - const SteelScarab: 2939; - const Scarab: 2940; - const DeathBeetle: 2941; - const DungSoldier: 2942; - const HellSwarm: 2943; - const PlagueBugs: 2944; - const BlackLocusts: 2945; - const Itchies: 2946; - const HellCat: 2947; - const NightTiger: 2948; - const SaberCat: 2949; - const Huntress: 2950; - const CliffLurker: 2951; - const TreeLurker: 2952; - const CaveLeaper: 2953; - const TombCreeper: 2954; - const SandLeaper: 2955; - const TombViper: 2956; - const PitViper: 2957; - const Salamander: 2958; - const ClawViper: 2959; - const SerpentMagus: 2960; - const BloodMaggot: 2961; - const GiantLamprey: 2962; - const Devourer: 2963; - const RockWorm: 2964; - const SandMaggot: 2965; - const BushBarb: 2966; - const RazorSpine: 2967; - const ThornBeast: 2968; - const SpikeFiend: 2969; - const QuillRat: 2970; - const HellClan: 2971; - const MoonClan: 2972; - const NightClan: 2973; - const DeathClan: 2974; - const BloodClan: 2975; - const TempleGuard: 2976; - const DoomApe: 2977; - const JungleHunter: 2978; - const RockDweller: 2979; - const DuneBeast: 2980; - const FleshHunter: 2981; - const BlackRogue: 2982; - const DarkStalker: 2983; - const VileHunter: 2984; - const DarkHunter: 2985; - const DarkShape: 2986; - const Apparition: 2987; - const Specter: 2988; - const Wraith: 2989; - const Ghost: 2990; - const Assailant: 2991; - const Infidel: 2992; - const Invader: 2993; - const Marauder: 2994; - const SandRaider: 2995; - const GargantuanBeast: 2996; - const WailingBeast: 2997; - const Yeti: 2998; - const Crusher: 2999; - const Brute: 3000; - const CloudStalker: 3001; - const BlackVulture: 3002; - const BlackRaptor: 3003; - const BloodHawk: 3004; - const FoulCrow: 3005; - const PlagueBearer: 3006; - const Ghoul: 3007; - const DrownedCarcass: 3008; - const HungryDead: 3009; - const Zombie: 3010; - const Horror: 3012; - const Returned: 3013; - const BurningDead: 3014; - const BoneWarrior: 3015; - const Damned: 3016; - const Disfigured: 3017; - const Misshapen: 3018; - const Tainted: 3019; - const Afflicted: 3020; - const Camel: 3033; - const Cadaver: 3034; - const PreservedDead: 3035; - const Embalmed: 3036; - const DriedCorpse: 3037; - const Decayed: 3038; - const Urdar: 3039; - const Mauler: 3040; - const Gorebelly: 3041; - const Blunderbore: 3042; - const BloodMaggotYoung: 3043; - const GiantLampreyYoung: 3044; - const DevourerYoung: 3045; - const RockWormYoung: 3046; - const SandMaggotYoung: 3047; - const BloodMaggotEgg: 3048; - const GiantLampreyEgg: 3049; - const DevourerEgg: 3050; - const RockWormEgg: 3051; - const SandMaggotEgg: 3052; - const Maggot: 3053; - const BloodHawkNest: 3055; - const FlyingScimitar: 3056; - const CloudStalkerNest: 3057; - const BlackRaptorNest: 3058; - const FoulCrowNest: 3059; - const Cantor: 3063; - const Heirophant: 3064; - const Sexton: 3065; - const Zealot: 3066; - const Faithful: 3067; - const Zakarumite: 3068; - const BlackSoul: 3069; - const BurningSoul: 3070; - const SwampGhost: 3071; - const Gloam: 3072; - const WarpedShaman: 3073; - const DarkShaman: 3074; - const DevilkinShaman: 3075; - const CarverShaman: 3076; - const FallenShaman: 3077; - const WarpedOne: 3078; - const DarkOne: 3079; - const Devilkin: 3080; - const Carver: 3081; - const Fallen: 3082; - const ReturnedArcher: 3083; - const HorrorArcher: 3084; - const BurningDeadArcher: 3085; - const BoneArcher: 3086; - const CorpseArcher: 3087; - const SkeletonArcher: 3088; - const FleshLancer: 3089; - const BlackLancer: 3090; - const DarkLancer: 3091; - const VileLancer: 3092; - const DarkSpearwoman: 3093; - const FleshArcher: 3094; - const BlackArcher: 3095; - const DarkRanger: 3096; - const VileArcher: 3097; - const DarkArcher: 3098; - const StygianDollShaman: 3100; - const SoulKillerShaman: 3101; - const FlayerShaman: 3102; - const FetishShaman: 3103; - const RatManShaman: 3104; - const HorrorMage: 3105; - const BurningDeadMage: 3106; - const BoneMage: 3107; - const CorpseMage: 3108; - const ReturnedMage: 3109; - const GargoyleTrap: 3110; - const NightMarauder: 3121; - const FireGolem: 3122; - const IronGolem: 3123; - const BloodGolem: 3124; - const ClayGolem: 3125; - const BloodMaggotQueen: 3126; - const GiantLampreyQueen: 3127; - const DevourerQueen: 3128; - const RockWormQueen: 3129; - const SandMaggotQueen: 3130; - const SlimePrince: 3131; - const BogCreature: 3132; - const SwampDweller: 3133; - const BarbedGiant: 3134; - const RazorBeast: 3135; - const ThornBrute: 3136; - const SpikeGiant: 3137; - const QuillBear: 3138; - const CouncilMember: 3139; - const DarkWanderer: 3141; - const HellSlinger: 3142; - const NightSlinger: 3143; - const SpearCat: 3144; - const Slinger: 3145; - const FireTower: 3146; - const LightningSpire: 3147; - const PitLord: 3148; - const Balrog: 3149; - const VenomLord: 3150; - const IronWolf: 3151; - const InvisoSpawner: 3152; - const OblivionKnight: 3153; - const Mage: 3154; - const AbyssKnight: 3155; - const FighterMage: 3156; - const DoomKnight: 3157; - const Fighter: 3158; - const MawFiend: 3159; - const CorpseSpitter: 3160; - const Corpulent: 3161; - const StormCaster: 3162; - const Strangler: 3163; - const DoomCaster: 3164; - const GrotesqueWyrm: 3165; - const StygianDog: 3166; - const FleshBeast: 3167; - const Grotesque: 3168; - const StygianHag: 3169; - const FleshSpawner: 3170; - const RogueScout: 3171; - const BloodWingNest: 3172; - const BloodHookNest: 3173; - const FeederNest: 3174; - const SuckerNest: 3175; - const Hydra: 3325; - } - namespace npcs { - const Asheara: 1008; - const Hratli: 1009; - const Alkor: 1010; - const Ormus: 1011; - const Natalya: 1012; - const Tyrael: 1013; - const Izual1: 1014; - const Izual2: 1015; - const Jamella: 1016; - const Halbu: 1017; - const Hadriel: 1018; - const Hazade: 1019; - const Alhizeer: 1020; - const Azrael: 1021; - const Ahsab: 1022; - const Chalan: 1023; - const Haseen: 1024; - const Razan: 1025; - const Emilio: 1026; - const Pratham: 1027; - const Fazel: 1028; - const Jemali: 1029; - const Kasim: 1030; - const Gulzar: 1031; - const Mizan: 1032; - const Leharas: 1033; - const Durga: 1034; - const Neeraj: 1035; - const Ilzan: 1036; - const Zanarhi: 1037; - const Waheed: 1038; - const Vikhyat: 1039; - const Jelani: 1040; - const Barani: 1041; - const Jabari: 1042; - const Devak: 1043; - const Raldin: 1044; - const Telash: 1045; - const Ajheed: 1046; - const Narphet: 1047; - const Khaleel: 1048; - const Phaet: 1049; - const Geshef: 1050; - const Vanji: 1051; - const Haphet: 1052; - const Thadar: 1053; - const Yatiraj: 1054; - const Rhadge: 1055; - const Yashied: 1056; - const Lharhad: 1057; - const Flux: 1058; - const Scorch: 1059; - //const Natalya: 3022; both 1012 and 3022 return Natalya? - const DeckardCain: 2890; - const Gheed: 2891; - const Akara: 2892; - const Kashya: 2893; - const Charsi: 2894; - const Warriv: 2895; - const Drognan: 3023; - const Atma: 3024; - const Fara: 3025; - const Lysander: 3026; - const Jerhyn: 3028; - const Geglash: 3029; - const Elzix: 3030; - const Greiz: 3031; - const Flavie: 3112; - const Kaelan: 3113; - const Meshif: 3114; - const Larzuk: 22476; - const Anya: 22477; - const Malah: 22478; - const Nihlathak1: 22479; - const QualKehk: 22480; - const Guard: 22481; - const Combatant: 22482; - const Nihlathak2: 22483; - } - namespace items { - const KhalimsFlail: 1060; - const KhalimsWill1: 1061; - const KhalimsFlail2: 1062; - const KhalimsWill2: 1063; - const KhalimsEye: 1064; - const KhalimsBrain: 1065; - const KhalimsHeart: 1066; - const ScrollofInifuss: 2216; - const KeytotheCairnStones: 2217; - const AJadeFigurine: 2227; - const TheGoldenBird: 2228; - const LamEsensTome1: 2229; - const LamEsensTome2: 2230; - const HoradricCube: 2231; - const HoradricScroll: 2232; - const MephistosSoulstone: 2233; - const Ear: 2235; - const AmuletoftheViper: 2697; - const StaffofKings: 2698; - const HoradricStaff: 2699; + // locale strings + export namespace locale { + export namespace monsters { + // bosses + const Andariel: 3021; + const Duriel: 3054; + const Mephisto: 3062; + const Diablo: 3060; + const Baal: 3061; + // Mini bosses + const BloodRaven: 3111; + const TreeheadWoodFist: 2873; + const TheCountess: 2875; + const TheSmith: 2889; + const Radament: 2879; + const TheSummoner: 3099; + const HephastoTheArmorer: 1067; + const Izual: 1014; + const ShenktheOverseer: 22435; + // Uniques + const Corpsefire: 3319; + const TheCowKing: 2850; + const GrandVizierofChaos: 2851; + const LordDeSeis: 2852; + const InfectorofSouls: 2853; + const RiftwraiththeCannibal: 2854; + const Taintbreeder: 2855; + const TheTormentor: 2856; + const Darkwing: 2857; + const MafferDragonhand: 2858; + const WyandVoidbringer: 2859; + const ToorcIcefist: 2860; + const BremmSparkfist: 2861; + const GelebFlamefinger: 2862; + const IsmailVilehand: 2863; + const IcehawkRiftwing: 2864; + const BattlemaidSarina: 2865; + const Stormtree: 2866; + const WitchDoctorEndugu: 2867; + const SszarkTheBurning: 2868; + const Bishibosh: 2869; + const Bonebreaker: 2870; + const Coldcrow: 2871; + const Rakanishu: 2872; + const Griswold: 2874; + const PitspawnFouldog: 2876; + const FlamespiketheCrawler: 2877; + const BoneAsh: 2878; + const BloodwitchtheWild: 2880; + const Fangskin: 2881; + const Beetleburst: 2882; + const CreepingFeature: 2883; + const ColdwormtheBurrower: 2884; + const FireEye: 2885; + const DarkElder: 2886; + const AncientKaatheSoulless: 2888; + const SharpToothSayer: 22493; + const SnapchipShatter: 22496; + const Pindleskin: 22497; + const ThreshSocket: 22498; + const EyebacktheUnleashed: 22499; + const EldritchtheRectifier: 22500; + const DacFarren: 22501; + const BonesawBreaker: 22502; + const Frozenstein: 22504; + const Rogue: 2897; + const StygianDoll: 2898; + const SoulKiller: 2899; + const Flayer: 2900; + const Fetish: 2901; + const RatMan: 2902; + const UndeadStygianDoll: 2903; + const UndeadSoulKiller: 2904; + const UndeadFlayer: 2905; + const UndeadFetish: 2906; + const UndeadRatMan: 2907; + const DarkFamiliar: 2908; + const BloodDiver: 2909; + const Gloombat: 2910; + const DesertWing: 2911; + const TheBanished: 2912; + const BloodLord: 2913; + const DarkLord: 2914; + const NightLord: 2915; + const GhoulLord: 2916; + const Spikefist: 2917; + const Thrasher: 2918; + const BrambleHulk: 2919; + const ThornedHulk: 2920; + const SpiderMagus: 2921; + const FlameSpider: 2922; + const PoisonSpinner: 2923; + const SandFisher: 2924; + const Arach: 2925; + const BloodWing: 2926; + const BloodHook: 2927; + const Feeder: 2928; + const Sucker: 2929; + const WingedNightmare: 2930; + const HellBuzzard: 2931; + const UndeadScavenger: 2932; + const CarrionBird: 2933; + const Unraveler: 2934; + const Guardian: 2935; + const HollowOne: 2936; + const HoradrimAncient: 2937; + const BoneScarab: 2938; + const SteelScarab: 2939; + const Scarab: 2940; + const DeathBeetle: 2941; + const DungSoldier: 2942; + const HellSwarm: 2943; + const PlagueBugs: 2944; + const BlackLocusts: 2945; + const Itchies: 2946; + const HellCat: 2947; + const NightTiger: 2948; + const SaberCat: 2949; + const Huntress: 2950; + const CliffLurker: 2951; + const TreeLurker: 2952; + const CaveLeaper: 2953; + const TombCreeper: 2954; + const SandLeaper: 2955; + const TombViper: 2956; + const PitViper: 2957; + const Salamander: 2958; + const ClawViper: 2959; + const SerpentMagus: 2960; + const BloodMaggot: 2961; + const GiantLamprey: 2962; + const Devourer: 2963; + const RockWorm: 2964; + const SandMaggot: 2965; + const BushBarb: 2966; + const RazorSpine: 2967; + const ThornBeast: 2968; + const SpikeFiend: 2969; + const QuillRat: 2970; + const HellClan: 2971; + const MoonClan: 2972; + const NightClan: 2973; + const DeathClan: 2974; + const BloodClan: 2975; + const TempleGuard: 2976; + const DoomApe: 2977; + const JungleHunter: 2978; + const RockDweller: 2979; + const DuneBeast: 2980; + const FleshHunter: 2981; + const BlackRogue: 2982; + const DarkStalker: 2983; + const VileHunter: 2984; + const DarkHunter: 2985; + const DarkShape: 2986; + const Apparition: 2987; + const Specter: 2988; + const Wraith: 2989; + const Ghost: 2990; + const Assailant: 2991; + const Infidel: 2992; + const Invader: 2993; + const Marauder: 2994; + const SandRaider: 2995; + const GargantuanBeast: 2996; + const WailingBeast: 2997; + const Yeti: 2998; + const Crusher: 2999; + const Brute: 3000; + const CloudStalker: 3001; + const BlackVulture: 3002; + const BlackRaptor: 3003; + const BloodHawk: 3004; + const FoulCrow: 3005; + const PlagueBearer: 3006; + const Ghoul: 3007; + const DrownedCarcass: 3008; + const HungryDead: 3009; + const Zombie: 3010; + const Horror: 3012; + const Returned: 3013; + const BurningDead: 3014; + const BoneWarrior: 3015; + const Damned: 3016; + const Disfigured: 3017; + const Misshapen: 3018; + const Tainted: 3019; + const Afflicted: 3020; + const Camel: 3033; + const Cadaver: 3034; + const PreservedDead: 3035; + const Embalmed: 3036; + const DriedCorpse: 3037; + const Decayed: 3038; + const Urdar: 3039; + const Mauler: 3040; + const Gorebelly: 3041; + const Blunderbore: 3042; + const BloodMaggotYoung: 3043; + const GiantLampreyYoung: 3044; + const DevourerYoung: 3045; + const RockWormYoung: 3046; + const SandMaggotYoung: 3047; + const BloodMaggotEgg: 3048; + const GiantLampreyEgg: 3049; + const DevourerEgg: 3050; + const RockWormEgg: 3051; + const SandMaggotEgg: 3052; + const Maggot: 3053; + const BloodHawkNest: 3055; + const FlyingScimitar: 3056; + const CloudStalkerNest: 3057; + const BlackRaptorNest: 3058; + const FoulCrowNest: 3059; + const Cantor: 3063; + const Heirophant: 3064; + const Sexton: 3065; + const Zealot: 3066; + const Faithful: 3067; + const Zakarumite: 3068; + const BlackSoul: 3069; + const BurningSoul: 3070; + const SwampGhost: 3071; + const Gloam: 3072; + const WarpedShaman: 3073; + const DarkShaman: 3074; + const DevilkinShaman: 3075; + const CarverShaman: 3076; + const FallenShaman: 3077; + const WarpedOne: 3078; + const DarkOne: 3079; + const Devilkin: 3080; + const Carver: 3081; + const Fallen: 3082; + const ReturnedArcher: 3083; + const HorrorArcher: 3084; + const BurningDeadArcher: 3085; + const BoneArcher: 3086; + const CorpseArcher: 3087; + const SkeletonArcher: 3088; + const FleshLancer: 3089; + const BlackLancer: 3090; + const DarkLancer: 3091; + const VileLancer: 3092; + const DarkSpearwoman: 3093; + const FleshArcher: 3094; + const BlackArcher: 3095; + const DarkRanger: 3096; + const VileArcher: 3097; + const DarkArcher: 3098; + const StygianDollShaman: 3100; + const SoulKillerShaman: 3101; + const FlayerShaman: 3102; + const FetishShaman: 3103; + const RatManShaman: 3104; + const HorrorMage: 3105; + const BurningDeadMage: 3106; + const BoneMage: 3107; + const CorpseMage: 3108; + const ReturnedMage: 3109; + const GargoyleTrap: 3110; + const NightMarauder: 3121; + const FireGolem: 3122; + const IronGolem: 3123; + const BloodGolem: 3124; + const ClayGolem: 3125; + const BloodMaggotQueen: 3126; + const GiantLampreyQueen: 3127; + const DevourerQueen: 3128; + const RockWormQueen: 3129; + const SandMaggotQueen: 3130; + const SlimePrince: 3131; + const BogCreature: 3132; + const SwampDweller: 3133; + const BarbedGiant: 3134; + const RazorBeast: 3135; + const ThornBrute: 3136; + const SpikeGiant: 3137; + const QuillBear: 3138; + const CouncilMember: 3139; + const DarkWanderer: 3141; + const HellSlinger: 3142; + const NightSlinger: 3143; + const SpearCat: 3144; + const Slinger: 3145; + const FireTower: 3146; + const LightningSpire: 3147; + const PitLord: 3148; + const Balrog: 3149; + const VenomLord: 3150; + const IronWolf: 3151; + const InvisoSpawner: 3152; + const OblivionKnight: 3153; + const Mage: 3154; + const AbyssKnight: 3155; + const FighterMage: 3156; + const DoomKnight: 3157; + const Fighter: 3158; + const MawFiend: 3159; + const CorpseSpitter: 3160; + const Corpulent: 3161; + const StormCaster: 3162; + const Strangler: 3163; + const DoomCaster: 3164; + const GrotesqueWyrm: 3165; + const StygianDog: 3166; + const FleshBeast: 3167; + const Grotesque: 3168; + const StygianHag: 3169; + const FleshSpawner: 3170; + const RogueScout: 3171; + const BloodWingNest: 3172; + const BloodHookNest: 3173; + const FeederNest: 3174; + const SuckerNest: 3175; + const Hydra: 3325; + } + namespace npcs { + const Asheara: 1008; + const Hratli: 1009; + const Alkor: 1010; + const Ormus: 1011; + const Natalya: 1012; + const Tyrael: 1013; + const Izual1: 1014; + const Izual2: 1015; + const Jamella: 1016; + const Halbu: 1017; + const Hadriel: 1018; + const Hazade: 1019; + const Alhizeer: 1020; + const Azrael: 1021; + const Ahsab: 1022; + const Chalan: 1023; + const Haseen: 1024; + const Razan: 1025; + const Emilio: 1026; + const Pratham: 1027; + const Fazel: 1028; + const Jemali: 1029; + const Kasim: 1030; + const Gulzar: 1031; + const Mizan: 1032; + const Leharas: 1033; + const Durga: 1034; + const Neeraj: 1035; + const Ilzan: 1036; + const Zanarhi: 1037; + const Waheed: 1038; + const Vikhyat: 1039; + const Jelani: 1040; + const Barani: 1041; + const Jabari: 1042; + const Devak: 1043; + const Raldin: 1044; + const Telash: 1045; + const Ajheed: 1046; + const Narphet: 1047; + const Khaleel: 1048; + const Phaet: 1049; + const Geshef: 1050; + const Vanji: 1051; + const Haphet: 1052; + const Thadar: 1053; + const Yatiraj: 1054; + const Rhadge: 1055; + const Yashied: 1056; + const Lharhad: 1057; + const Flux: 1058; + const Scorch: 1059; + //const Natalya: 3022; both 1012 and 3022 return Natalya? + const DeckardCain: 2890; + const Gheed: 2891; + const Akara: 2892; + const Kashya: 2893; + const Charsi: 2894; + const Warriv: 2895; + const Drognan: 3023; + const Atma: 3024; + const Fara: 3025; + const Lysander: 3026; + const Jerhyn: 3028; + const Geglash: 3029; + const Elzix: 3030; + const Greiz: 3031; + const Flavie: 3112; + const Kaelan: 3113; + const Meshif: 3114; + const Larzuk: 22476; + const Anya: 22477; + const Malah: 22478; + const Nihlathak1: 22479; + const QualKehk: 22480; + const Guard: 22481; + const Combatant: 22482; + const Nihlathak2: 22483; + } + namespace items { + const KhalimsFlail: 1060; + const KhalimsWill1: 1061; + const KhalimsFlail2: 1062; + const KhalimsWill2: 1063; + const KhalimsEye: 1064; + const KhalimsBrain: 1065; + const KhalimsHeart: 1066; + const ScrollofInifuss: 2216; + const KeytotheCairnStones: 2217; + const AJadeFigurine: 2227; + const TheGoldenBird: 2228; + const LamEsensTome1: 2229; + const LamEsensTome2: 2230; + const HoradricCube: 2231; + const HoradricScroll: 2232; + const MephistosSoulstone: 2233; + const Ear: 2235; + const AmuletoftheViper: 2697; + const StaffofKings: 2698; + const HoradricStaff: 2699; - // Sets - // Angelic Rainment - const AngelicsSword: 10172; - const AngelicsArmor: 10173; - const AngelicsRing: 10174; - const AngelicsAmulet: 10175; - // Arcannas Tricks - const ArcannasAmulet: 10180; - const ArcannasStaff: 10181; - const ArcannasHelmet: 10182; - const ArcannasArmor: 10183; - // Artic Gear - const ArticsBow: 10176; - const ArticsArmor: 10177; - const ArticsBelt: 10178; - const ArticsGloves: 10179; - // Berserkers Gear - const BerserkersHelmet: 10166; - const BerserkersAxe: 10167; - const BerserkersArmor: 10168; - // Cathans Traps - const CathansRing: 10147; - const CathansAmulet: 10148; - const CathansHelmet: 10149; - const CathansArmor: 10150; - const CathansStaff: 10151; - // Civerbs Gear - const CiverbsShield: 10122; - const CiverbsAmulet: 10123; - const CiverbsScepter: 10124; - // Clegaws Brace - const ClegawsSword: 10128; - const ClegawsShield: 10129; - const ClegawsGloves: 10130; - // Deaths Disguise - const DeathsGloves: 10169; - const DeathsBelt: 10170; - const DeathsSword: 10171; - // Hsarus Defense - const HsarusBoots: 10125; - const HsarusShield: 10126; - const HsarusBelt: 10127; - // Infernal Tools - const InfernalsHelmet: 10163; - const InfernalsWand: 10164; - const InfernalsBelt: 10165; - // Irathas Finery - const IrathasBelt: 10131; - const IrathasHelmet: 10132; - const IrathasGloves: 10133; - const IrathasAmulet: 10134; - // Isenharts Armory - const IsenhartsHelmet: 10135; - const IsenhartsArmor: 10136; - const IsenhartsShield: 10137; - const IsenhartsSword: 10138; - // Milabrega Regalia - const MilabregasArmor: 10143; - const MilabregasHelmet: 10144; - const MilabregasScepter: 10145; - const MilabregasShield: 10146; - // Sigons - const SigonsHelmet: 10157; - const SigonsArmor: 10158; - const SigonsGloves: 10159; - const SigonsBoots: 10160; - const SigonsBelt: 10161; - const SigonsShield: 10162; - // Tancreds - const TancredsPick: 10152; - const TancredsArmor: 10153; - const TancredsBoots: 10154; - const TancredsAmulet: 10155; - const TancredsHelmet: 10156; - // Vidalas - const VidalasAmulet: 10139; - const VidalasArmor: 10140; - const VidalasBoots: 10141; - const VidalasBow: 10142; + // Sets + // Angelic Rainment + const AngelicsSword: 10172; + const AngelicsArmor: 10173; + const AngelicsRing: 10174; + const AngelicsAmulet: 10175; + // Arcannas Tricks + const ArcannasAmulet: 10180; + const ArcannasStaff: 10181; + const ArcannasHelmet: 10182; + const ArcannasArmor: 10183; + // Artic Gear + const ArticsBow: 10176; + const ArticsArmor: 10177; + const ArticsBelt: 10178; + const ArticsGloves: 10179; + // Berserkers Gear + const BerserkersHelmet: 10166; + const BerserkersAxe: 10167; + const BerserkersArmor: 10168; + // Cathans Traps + const CathansRing: 10147; + const CathansAmulet: 10148; + const CathansHelmet: 10149; + const CathansArmor: 10150; + const CathansStaff: 10151; + // Civerbs Gear + const CiverbsShield: 10122; + const CiverbsAmulet: 10123; + const CiverbsScepter: 10124; + // Clegaws Brace + const ClegawsSword: 10128; + const ClegawsShield: 10129; + const ClegawsGloves: 10130; + // Deaths Disguise + const DeathsGloves: 10169; + const DeathsBelt: 10170; + const DeathsSword: 10171; + // Hsarus Defense + const HsarusBoots: 10125; + const HsarusShield: 10126; + const HsarusBelt: 10127; + // Infernal Tools + const InfernalsHelmet: 10163; + const InfernalsWand: 10164; + const InfernalsBelt: 10165; + // Irathas Finery + const IrathasBelt: 10131; + const IrathasHelmet: 10132; + const IrathasGloves: 10133; + const IrathasAmulet: 10134; + // Isenharts Armory + const IsenhartsHelmet: 10135; + const IsenhartsArmor: 10136; + const IsenhartsShield: 10137; + const IsenhartsSword: 10138; + // Milabrega Regalia + const MilabregasArmor: 10143; + const MilabregasHelmet: 10144; + const MilabregasScepter: 10145; + const MilabregasShield: 10146; + // Sigons + const SigonsHelmet: 10157; + const SigonsArmor: 10158; + const SigonsGloves: 10159; + const SigonsBoots: 10160; + const SigonsBelt: 10161; + const SigonsShield: 10162; + // Tancreds + const TancredsPick: 10152; + const TancredsArmor: 10153; + const TancredsBoots: 10154; + const TancredsAmulet: 10155; + const TancredsHelmet: 10156; + // Vidalas + const VidalasAmulet: 10139; + const VidalasArmor: 10140; + const VidalasBoots: 10141; + const VidalasBow: 10142; - // LoD Sets - // Aldurs's Legacy - const AldursHelmet: 21697; - const AldursArmor: 21698; - const AldursBoots: 21700; - const AldursMace: 21847; - // Bul-Kathos's Children - const BulKathosBlade: 21688; - const BulKathoSword: 21689; - // Cow Kings's Leathers - const CowKingsHelmet: 21723; - const CowKingsArmor: 21724; - const CowKingsBoots: 21725; - // Disciples - const DisciplesAmulet: 21717; - const DisciplesGloves: 21718; - const DisciplesBoots: 21719; - const DisciplesArmor: 21720; - const DisciplesBelt: 21721; - // Griswolds's Legacy - const GriswoldsScepter: 21673; - const GriswoldsShield: 21674; - const GriswoldsArmor: 21675; - const GriswoldsHelmet: 21676; - // Heaven's Brethren - const HeavensMace: 21823; - const HeavensHelmet: 21824; - const HeavensShield: 21825; - const HeavensArmor: 21826; - // Hwanin's - const HwaninsHelmet: 21712; - const HwaninsPolearm: 21713; - const HwaninsArmor: 21714; - const HwaninsBelt: 21821; - // IK - const ImmortalKingsMaul: 21840; - const ImmortalKingsBoots: 21841; - const ImmortalKingsGloves: 21842; - const ImmortalKingsBelt: 21843; - const ImmortalKingsArmor: 21844; - const ImmortalKingsHelmet: 21845; - // M'avina's - const MavinasHelmet: 21702; - const MavinasArmor: 21703; - const MavinasGloves: 21704; - const MavinasBelt: 21705; - const MavinasBow: 21706; - // Natalya's - const NatalyasHelmet: 21668; - const NatalyasClaw: 21669; - const NatalyasArmor: 21670; - const NatalyasBoots: 21671; - // Naj's - const NajsStaff: 21640; - const NajsArmor: 21831; - const NajsHelmet: 21832; - // Orphan's - const OrphansHelmet: 21731; - const OrphansBelt: 21732; - const OrphansGloves: 21733; - const OrphansShield: 21734; - // Sanders's - const SandersGloves: 21876; - const SandersBoots: 21877; - const SandersHelmet: 21878; - const SandersWand: 21879; - // Sazabi's - const SazabisSword: 21708; - const SazabisArmor: 21709; - const SazabisHelmet: 21710; - // Tal - const TalRashasBelt: 21816; - const TalRashasAmulet: 21817; - const TalRashasArmor: 21818; - const TalRashasOrb: 21819; - const TalRashasHelmet: 21820; - // Trang-Ouls - const TrangOulsHelmet: 21661; - const TrangOulsShield: 21662; - const TrangOulsArmor: 21664; - const TrangOulsGloves: 21665; - const TrangOulsBelt: 21666; + // LoD Sets + // Aldurs's Legacy + const AldursHelmet: 21697; + const AldursArmor: 21698; + const AldursBoots: 21700; + const AldursMace: 21847; + // Bul-Kathos's Children + const BulKathosBlade: 21688; + const BulKathoSword: 21689; + // Cow Kings's Leathers + const CowKingsHelmet: 21723; + const CowKingsArmor: 21724; + const CowKingsBoots: 21725; + // Disciples + const DisciplesAmulet: 21717; + const DisciplesGloves: 21718; + const DisciplesBoots: 21719; + const DisciplesArmor: 21720; + const DisciplesBelt: 21721; + // Griswolds's Legacy + const GriswoldsScepter: 21673; + const GriswoldsShield: 21674; + const GriswoldsArmor: 21675; + const GriswoldsHelmet: 21676; + // Heaven's Brethren + const HeavensMace: 21823; + const HeavensHelmet: 21824; + const HeavensShield: 21825; + const HeavensArmor: 21826; + // Hwanin's + const HwaninsHelmet: 21712; + const HwaninsPolearm: 21713; + const HwaninsArmor: 21714; + const HwaninsBelt: 21821; + // IK + const ImmortalKingsMaul: 21840; + const ImmortalKingsBoots: 21841; + const ImmortalKingsGloves: 21842; + const ImmortalKingsBelt: 21843; + const ImmortalKingsArmor: 21844; + const ImmortalKingsHelmet: 21845; + // M'avina's + const MavinasHelmet: 21702; + const MavinasArmor: 21703; + const MavinasGloves: 21704; + const MavinasBelt: 21705; + const MavinasBow: 21706; + // Natalya's + const NatalyasHelmet: 21668; + const NatalyasClaw: 21669; + const NatalyasArmor: 21670; + const NatalyasBoots: 21671; + // Naj's + const NajsStaff: 21640; + const NajsArmor: 21831; + const NajsHelmet: 21832; + // Orphan's + const OrphansHelmet: 21731; + const OrphansBelt: 21732; + const OrphansGloves: 21733; + const OrphansShield: 21734; + // Sanders's + const SandersGloves: 21876; + const SandersBoots: 21877; + const SandersHelmet: 21878; + const SandersWand: 21879; + // Sazabi's + const SazabisSword: 21708; + const SazabisArmor: 21709; + const SazabisHelmet: 21710; + // Tal + const TalRashasBelt: 21816; + const TalRashasAmulet: 21817; + const TalRashasArmor: 21818; + const TalRashasOrb: 21819; + const TalRashasHelmet: 21820; + // Trang-Ouls + const TrangOulsHelmet: 21661; + const TrangOulsShield: 21662; + const TrangOulsArmor: 21664; + const TrangOulsGloves: 21665; + const TrangOulsBelt: 21666; - // Uniques - // Quest/Misc - const KeyofTerror: 11146; - const KeyofHate: 11147; - const KeyofDestruction: 11148; - const DiablosHorn: 11149; - const BaalsEye: 11150; - const MephistosBrain: 11151; - const StandardofHeroes: 11152; - const HellfireTorch: 11153; - const Annihilus: 21743; + // Uniques + // Quest/Misc + const KeyofTerror: 11146; + const KeyofHate: 11147; + const KeyofDestruction: 11148; + const DiablosHorn: 11149; + const BaalsEye: 11150; + const MephistosBrain: 11151; + const StandardofHeroes: 11152; + const HellfireTorch: 11153; + const Annihilus: 21743; - // Unique Items - const WitchwildString: 10911; - const TitansRevenge: 21735; - const LycandersAim: 21737; - const ArreatsFace: 21744; - const Homunculus: 21755; - const JalalsMane: 21750; - const HeraldofZakarum: 21758; - const BloodRavensCharge: 21508; - const Gimmershred: 21637; - const MedusasGaze: 21516; - const Rockstopper: 21519; - const CrownofThieves: 21522; - const BlackhornsFace: 21523; - const TheSpiritShroud: 21524; - const SkinoftheFlayedOne: 21525; - const IronPelt: 21526; - const SpiritForge: 21527; - const CrowCaw: 21528; - const DurielsShell: 21529; - const SkulldersIre: 21530; - const Toothrow: 21531; - const AtmasWail: 21532; - const BlackHades: 21533; - const Corpsemourn: 21534; - const QueHegans: 21535; - const QueHegansWisdom: 21535; - const Mosers: 21536; - const MosersBlessedCircle: 21536; - const Stormchaser: 21537; - const TiamatsRubuke: 21538; - const GerkesSanctuary: 21539; - const RadamentsSphere: 21540; - const Gravepalm: 21541; - const Ghoulhide: 21542; - const Hellmouth: 21543; - const Infernostride: 21544; - const Waterwalk: 21545; - const Silkweave: 21546; - const WarTraveler: 21547; - const Razortail: 21548; - const GloomsTrap: 21549; - const Snowclash: 21550; - const ThundergodsVigor: 21551; - const LidlessWall: 21552; - const LanceGuard: 21553; - const Boneflame: 21555; - const SteelPillar: 21556; - const NightwingsVeil: 21557; - const CrownofAges: 21559; - const AndarielsVisage: 21560; - const Dragonscale: 21562; - const SteelCarapace: 21563; - const RainbowFacet: 21565; - const Ravenlore: 21566; - const Boneshade: 21567; - const Flamebellow: 21570; - const DeathsFathom: 21571; - const Wolfhowl: 21572; - const SpiritWard: 21573; - const KirasGuardian: 21574; - const OrmusRobe: 21575; - const GheedsFortune: 21576; - const HalberdsReign: 21579; - const DraculsGrasp: 21583; - const Frostwind: 21584; - const TemplarsMight: 21585; - const EschutasTemper: 21586; // also 21620? - const FirelizardsTalons: 21587; - const SandstormTrek: 21588; - const Marrowwalk: 21589; - const HeavensLight: 21590; - const ArachnidMesh: 21592; - const NosferatusCoil: 21593; - const Verdungos: 21595; - const VerdungosHeartyCord: 21595; - const CarrionWind: 21597; - const GiantSkull: 21598; - const AstreonsIronWard: 21599; - const SaracensChance: 21608; - const HighlordsWrath: 21609; - const Ravenfrost: 21610; - const Dwarfstar: 21611; - const AtmasScarab: 21612; - const Maras: 21613; - const MarasKaleidoscope: 21613; - const CrescentMoonAmulet: 21614; - const TheRisingSun: 21615; - const TheCatsEye: 21616; - const BulKathosWeddingBand: 21617; - const Metalgrid: 21619; - const Stormshield: 21621; - const BlackoakShield: 21622; - const ArkainesValor: 21624; - const TheGladiatorsBane: 21625; - const HarlequinsCrest: 21627; - const GuardianAngel: 21632; - const TheGrandfather: 21643; - const Doombringer: 21644; - const TyraelsMight: 21645; - const Lightsabre: 21646; - const TheCraniumBasher: 21647; - const DeathsWeb: 21650; - const TheAtlantean: 21654; - const CarinShard: 21658; - const Coldkill: 21286; - const ButchersCleaver: 21287; - const Islestrike: 21289; - const GuardianNaga: 21291; - const SpellSteel: 21293; - const SuicideBranch: 21297; - const ArmofKingLeoric: 21299; - const BlackhandKey: 21300; - const DarkClanCrusher: 21301; - const TheFetidSprinkler: 21304; - const HandofBlessedLight: 21305; - const Fleshrender: 21306; - const SureshrillFrost: 21307; - const Moonfall: 21308; - const BaezilsVortex: 21309; - const Earthshaker: 21310; - const TheGavelofPain: 21312; - const Bloodletter: 21313; - const ColdstealEye: 21314; - const Hexfire: 21315; - const BladeofAliBaba: 21316; - const Riftslash: 21317; - const Headstriker: 21318; - const PlagueBearer: 21319; - //const TheAtlantean: 21320; - const CrainteVomir: 21321; - const BingSzWang: 21322; - const TheVileHusk: 21323; - const Cloudcrack: 21324; - const TodesfaelleFlamme: 21325; - const Swordguard: 21326; - const Spineripper: 21327; - const HeartCarver: 21328; - const BlackbogsSharp: 21329; - const Stormspike: 21330; - const TheImpaler: 21331; - const HoneSudan: 21334; - const SpireofHonor: 21335; - const TheMeatScraper: 21336; - const BlackleachBlade: 21337; - const AthenasWrath: 21338; - const PierreTombaleCouant: 21339; - const GrimsBurningDead: 21341; - const Ribcracker: 21342; - const ChromaticIre: 21343; - const Warspear: 21344; - const SkullCollector: 21345; - const Skystrike: 21346; - //const WitchwildString: 21349; - const GoldstrikeArch: 21350; - const PusSpitter: 21352; - const VampireGaze: 21354; - const StringofEars: 21355; - const GoreRider: 21356; - const LavaGout: 21357; - const VenomGrip: 21358; - const Visceratuant: 21359; - //const GuardianAngel: 21360; - const Shaftstop: 21361; - const SkinofVipermagi: 21362; - const Blackhorn: 21363; - const ValkyrieWing: 21364; - const PeasantCrown: 21365; - const DemonMachine: 21366; - const Riphook: 21369; - const Razorswitch: 21370; - const OndalsWisdom: 21375; - const Deathbit: 21379; - const Warshrike: 21380; - const DemonLimb: 21387; - const SteelShade: 21388; - const TombReaver: 21389; - //const DeathsWeb: 21390; - const AngelsSong: 21393; - const TheRedeemer: 21394; - const Bonehew: 21398; - const Steelrend: 21399; - const AriocsNeedle: 21402; - const SoulDrainer: 21407; - const RuneMaster: 21408; - const DeathCleaver: 21409; - const ExecutionersJustice: 21410; - const Leviathan: 21412; - const WispProjector: 21417; - const Lacerator: 21419; - const MangSongsLesson: 21420; - const Viperfork: 21421; - const TheReapersToll: 21427; - const SpiritKeeper: 21428; - const Hellrack: 21429; - const AlmaNegra: 21430; - const DarkforceSpawn: 21431; - const Ghostflame: 21438; - const ShadowKiller: 21439; - const GriffonsEye: 21442; - const Thunderstroke: 21445; - const DemonsArch: 21447; - const DjinnSlayer: 21450; + // Unique Items + const WitchwildString: 10911; + const TitansRevenge: 21735; + const LycandersAim: 21737; + const ArreatsFace: 21744; + const Homunculus: 21755; + const JalalsMane: 21750; + const HeraldofZakarum: 21758; + const BloodRavensCharge: 21508; + const Gimmershred: 21637; + const MedusasGaze: 21516; + const Rockstopper: 21519; + const CrownofThieves: 21522; + const BlackhornsFace: 21523; + const TheSpiritShroud: 21524; + const SkinoftheFlayedOne: 21525; + const IronPelt: 21526; + const SpiritForge: 21527; + const CrowCaw: 21528; + const DurielsShell: 21529; + const SkulldersIre: 21530; + const Toothrow: 21531; + const AtmasWail: 21532; + const BlackHades: 21533; + const Corpsemourn: 21534; + const QueHegans: 21535; + const QueHegansWisdom: 21535; + const Mosers: 21536; + const MosersBlessedCircle: 21536; + const Stormchaser: 21537; + const TiamatsRubuke: 21538; + const GerkesSanctuary: 21539; + const RadamentsSphere: 21540; + const Gravepalm: 21541; + const Ghoulhide: 21542; + const Hellmouth: 21543; + const Infernostride: 21544; + const Waterwalk: 21545; + const Silkweave: 21546; + const WarTraveler: 21547; + const Razortail: 21548; + const GloomsTrap: 21549; + const Snowclash: 21550; + const ThundergodsVigor: 21551; + const LidlessWall: 21552; + const LanceGuard: 21553; + const Boneflame: 21555; + const SteelPillar: 21556; + const NightwingsVeil: 21557; + const CrownofAges: 21559; + const AndarielsVisage: 21560; + const Dragonscale: 21562; + const SteelCarapace: 21563; + const RainbowFacet: 21565; + const Ravenlore: 21566; + const Boneshade: 21567; + const Flamebellow: 21570; + const DeathsFathom: 21571; + const Wolfhowl: 21572; + const SpiritWard: 21573; + const KirasGuardian: 21574; + const OrmusRobe: 21575; + const GheedsFortune: 21576; + const HalberdsReign: 21579; + const DraculsGrasp: 21583; + const Frostwind: 21584; + const TemplarsMight: 21585; + const EschutasTemper: 21586; // also 21620? + const FirelizardsTalons: 21587; + const SandstormTrek: 21588; + const Marrowwalk: 21589; + const HeavensLight: 21590; + const ArachnidMesh: 21592; + const NosferatusCoil: 21593; + const Verdungos: 21595; + const VerdungosHeartyCord: 21595; + const CarrionWind: 21597; + const GiantSkull: 21598; + const AstreonsIronWard: 21599; + const SaracensChance: 21608; + const HighlordsWrath: 21609; + const Ravenfrost: 21610; + const Dwarfstar: 21611; + const AtmasScarab: 21612; + const Maras: 21613; + const MarasKaleidoscope: 21613; + const CrescentMoonAmulet: 21614; + const TheRisingSun: 21615; + const TheCatsEye: 21616; + const BulKathosWeddingBand: 21617; + const Metalgrid: 21619; + const Stormshield: 21621; + const BlackoakShield: 21622; + const ArkainesValor: 21624; + const TheGladiatorsBane: 21625; + const HarlequinsCrest: 21627; + const GuardianAngel: 21632; + const TheGrandfather: 21643; + const Doombringer: 21644; + const TyraelsMight: 21645; + const Lightsabre: 21646; + const TheCraniumBasher: 21647; + const DeathsWeb: 21650; + const TheAtlantean: 21654; + const CarinShard: 21658; + const Coldkill: 21286; + const ButchersCleaver: 21287; + const Islestrike: 21289; + const GuardianNaga: 21291; + const SpellSteel: 21293; + const SuicideBranch: 21297; + const ArmofKingLeoric: 21299; + const BlackhandKey: 21300; + const DarkClanCrusher: 21301; + const TheFetidSprinkler: 21304; + const HandofBlessedLight: 21305; + const Fleshrender: 21306; + const SureshrillFrost: 21307; + const Moonfall: 21308; + const BaezilsVortex: 21309; + const Earthshaker: 21310; + const TheGavelofPain: 21312; + const Bloodletter: 21313; + const ColdstealEye: 21314; + const Hexfire: 21315; + const BladeofAliBaba: 21316; + const Riftslash: 21317; + const Headstriker: 21318; + const PlagueBearer: 21319; + //const TheAtlantean: 21320; + const CrainteVomir: 21321; + const BingSzWang: 21322; + const TheVileHusk: 21323; + const Cloudcrack: 21324; + const TodesfaelleFlamme: 21325; + const Swordguard: 21326; + const Spineripper: 21327; + const HeartCarver: 21328; + const BlackbogsSharp: 21329; + const Stormspike: 21330; + const TheImpaler: 21331; + const HoneSudan: 21334; + const SpireofHonor: 21335; + const TheMeatScraper: 21336; + const BlackleachBlade: 21337; + const AthenasWrath: 21338; + const PierreTombaleCouant: 21339; + const GrimsBurningDead: 21341; + const Ribcracker: 21342; + const ChromaticIre: 21343; + const Warspear: 21344; + const SkullCollector: 21345; + const Skystrike: 21346; + //const WitchwildString: 21349; + const GoldstrikeArch: 21350; + const PusSpitter: 21352; + const VampireGaze: 21354; + const StringofEars: 21355; + const GoreRider: 21356; + const LavaGout: 21357; + const VenomGrip: 21358; + const Visceratuant: 21359; + //const GuardianAngel: 21360; + const Shaftstop: 21361; + const SkinofVipermagi: 21362; + const Blackhorn: 21363; + const ValkyrieWing: 21364; + const PeasantCrown: 21365; + const DemonMachine: 21366; + const Riphook: 21369; + const Razorswitch: 21370; + const OndalsWisdom: 21375; + const Deathbit: 21379; + const Warshrike: 21380; + const DemonLimb: 21387; + const SteelShade: 21388; + const TombReaver: 21389; + //const DeathsWeb: 21390; + const AngelsSong: 21393; + const TheRedeemer: 21394; + const Bonehew: 21398; + const Steelrend: 21399; + const AriocsNeedle: 21402; + const SoulDrainer: 21407; + const RuneMaster: 21408; + const DeathCleaver: 21409; + const ExecutionersJustice: 21410; + const Leviathan: 21412; + const WispProjector: 21417; + const Lacerator: 21419; + const MangSongsLesson: 21420; + const Viperfork: 21421; + const TheReapersToll: 21427; + const SpiritKeeper: 21428; + const Hellrack: 21429; + const AlmaNegra: 21430; + const DarkforceSpawn: 21431; + const Ghostflame: 21438; + const ShadowKiller: 21439; + const GriffonsEye: 21442; + const Thunderstroke: 21445; + const DemonsArch: 21447; + const DjinnSlayer: 21450; - // Runewords - const AncientsPledge: 20507; - const Armageddon: 20508; - const Authority: 20509; - const Beast: 20510; - const Beauty: 20511; - const Black: 20512; - const Blood: 20513; - const Bone: 20514; - const Bramble: 20515; - const Brand: 20516; - const BreathoftheDying: 20517; - const BrokenPromise: 20518; - const CalltoArms: 20519; - const ChainsofHonor: 20520; - const Chance: 20521; - const Chaos: 20522; - const CrescentMoon: 20523; - const Darkness: 20524; - const Daylight: 20525; - const Death: 20526; - const Deception: 20527; - const Delerium: 20528; - const Desire: 20529; - const Despair: 20530; - const Destruction: 20531; - const Doom: 20532; - const Dragon: 20533; - const Dread: 20534; - const Dream: 20535; - const Duress: 20536; - const Edge: 20537; - const Elation: 20538; - const Enigma: 20539; - const Enlightenment: 20540; - const Envy: 20541; - const Eternity: 20542; - const Exile: 20543; - const Faith: 20544; - const Famine: 20545; - const Flame: 20546; - const Fortitude: 20547; - const Fortune: 20548; - const Friendship: 20549; - const Fury: 20550; - const Gloom: 20551; - const Grief: 20553; - const HandofJustice: 20554; - const Harmony: 20555; - const HeartoftheOak: 20557; - const HolyThunder: 20560; - const Honor: 20561; - const Revenge: 20562; - const Humility: 20563; - const Hunger: 20564; - const Ice: 20565; - const Infinity: 20566; - const Innocence: 20567; - const Insight: 20568; - const Jealousy: 20569; - const Judgement: 20570; - const KingsGrace: 20571; - const Kingslayer: 20572; - const KnightsVigil: 20573; - const Knowledge: 20574; - const LastWish: 20575; - const Law: 20576; - const Lawbringer: 20577; - const Leaf: 20578; - const Lightning: 20579; - const Lionheart: 20580; - const Lore: 20581; - const Love: 20582; - const Loyalty: 20583; - const Lust: 20584; - const Madness: 20585; - const Malice: 20586; - const Melody: 20587; - const Memory: 20588; - const Mist: 20589; - const Morning: 20590; - const Mystery: 20591; - const Myth: 20592; - const Nadir: 20593; - const NaturesKingdom: 20594; - const Night: 20595; - const Oath: 20596; - const Obedience: 20597; - const Oblivion: 20598; - const Obsession: 20599; - const Passion: 20600; - const Patience: 20601; - const Patter: 20602; - const Peace: 20603; - const VoiceofReason: 20604; - const Penitence: 20605; - const Peril: 20606; - const Pestilence: 20607; - const Phoenix: 20608; - const Piety: 20609; - const PillarofFaith: 20610; - const Plague: 20611; - const Praise: 20612; - const Prayer: 20613; - const Pride: 20614; - const Principle: 20615; - const ProwessinBattle: 20616; - const Prudence: 20617; - const Punishment: 20618; - const Purity: 20619; - const Question: 20620; - const Radiance: 20621; - const Rain: 20622; - const Reason: 20623; - const Red: 20624; - const Rhyme: 20625; - const Rift: 20626; - const Sanctuary: 20627; - const Serendipity: 20628; - const Shadow: 20629; - const ShadowofDoubt: 20630; - const Silence: 20631; - const SirensSong: 20632; - const Smoke: 20633; - const Sorrow: 20634; - const Spirit: 20635; - const Splendor: 20636; - const Starlight: 20637; - const Stealth: 20638; - const Steel: 20639; - const StillWater: 20640; - const Sting: 20641; - const Stone: 20642; - const Storm: 20643; - const Strength: 20644; - const Tempest: 20645; - const Temptation: 20646; - const Terror: 20647; - const Thirst: 20648; - const Thought: 20649; - const Thunder: 20650; - const Time: 20651; - const Tradition: 20652; - const Treachery: 20653; - const Trust: 20654; - const Truth: 20655; - const UnbendingWill: 20656; - const Valor: 20657; - const Vengeance: 20658; - const Venom: 20659; - const Victory: 20660; - const Voice: 20661; - const Void: 20662; - const War: 20663; - const Water: 20664; - const Wealth: 20665; - const Whisper: 20666; - const White: 20667; - const Wind: 20668; - const WingsofHope: 20669; - const Wisdom: 20670; - const Woe: 20671; - const Wonder: 20672; - const Wrath: 20673; - const Youth: 20674; - const Zephyr: 20675; - } - namespace dialog { - const youDoNotHaveEnoughGoldForThat: 3362 - } + // Runewords + const AncientsPledge: 20507; + const Armageddon: 20508; + const Authority: 20509; + const Beast: 20510; + const Beauty: 20511; + const Black: 20512; + const Blood: 20513; + const Bone: 20514; + const Bramble: 20515; + const Brand: 20516; + const BreathoftheDying: 20517; + const BrokenPromise: 20518; + const CalltoArms: 20519; + const ChainsofHonor: 20520; + const Chance: 20521; + const Chaos: 20522; + const CrescentMoon: 20523; + const Darkness: 20524; + const Daylight: 20525; + const Death: 20526; + const Deception: 20527; + const Delerium: 20528; + const Desire: 20529; + const Despair: 20530; + const Destruction: 20531; + const Doom: 20532; + const Dragon: 20533; + const Dread: 20534; + const Dream: 20535; + const Duress: 20536; + const Edge: 20537; + const Elation: 20538; + const Enigma: 20539; + const Enlightenment: 20540; + const Envy: 20541; + const Eternity: 20542; + const Exile: 20543; + const Faith: 20544; + const Famine: 20545; + const Flame: 20546; + const Fortitude: 20547; + const Fortune: 20548; + const Friendship: 20549; + const Fury: 20550; + const Gloom: 20551; + const Grief: 20553; + const HandofJustice: 20554; + const Harmony: 20555; + const HeartoftheOak: 20557; + const HolyThunder: 20560; + const Honor: 20561; + const Revenge: 20562; + const Humility: 20563; + const Hunger: 20564; + const Ice: 20565; + const Infinity: 20566; + const Innocence: 20567; + const Insight: 20568; + const Jealousy: 20569; + const Judgement: 20570; + const KingsGrace: 20571; + const Kingslayer: 20572; + const KnightsVigil: 20573; + const Knowledge: 20574; + const LastWish: 20575; + const Law: 20576; + const Lawbringer: 20577; + const Leaf: 20578; + const Lightning: 20579; + const Lionheart: 20580; + const Lore: 20581; + const Love: 20582; + const Loyalty: 20583; + const Lust: 20584; + const Madness: 20585; + const Malice: 20586; + const Melody: 20587; + const Memory: 20588; + const Mist: 20589; + const Morning: 20590; + const Mystery: 20591; + const Myth: 20592; + const Nadir: 20593; + const NaturesKingdom: 20594; + const Night: 20595; + const Oath: 20596; + const Obedience: 20597; + const Oblivion: 20598; + const Obsession: 20599; + const Passion: 20600; + const Patience: 20601; + const Patter: 20602; + const Peace: 20603; + const VoiceofReason: 20604; + const Penitence: 20605; + const Peril: 20606; + const Pestilence: 20607; + const Phoenix: 20608; + const Piety: 20609; + const PillarofFaith: 20610; + const Plague: 20611; + const Praise: 20612; + const Prayer: 20613; + const Pride: 20614; + const Principle: 20615; + const ProwessinBattle: 20616; + const Prudence: 20617; + const Punishment: 20618; + const Purity: 20619; + const Question: 20620; + const Radiance: 20621; + const Rain: 20622; + const Reason: 20623; + const Red: 20624; + const Rhyme: 20625; + const Rift: 20626; + const Sanctuary: 20627; + const Serendipity: 20628; + const Shadow: 20629; + const ShadowofDoubt: 20630; + const Silence: 20631; + const SirensSong: 20632; + const Smoke: 20633; + const Sorrow: 20634; + const Spirit: 20635; + const Splendor: 20636; + const Starlight: 20637; + const Stealth: 20638; + const Steel: 20639; + const StillWater: 20640; + const Sting: 20641; + const Stone: 20642; + const Storm: 20643; + const Strength: 20644; + const Tempest: 20645; + const Temptation: 20646; + const Terror: 20647; + const Thirst: 20648; + const Thought: 20649; + const Thunder: 20650; + const Time: 20651; + const Tradition: 20652; + const Treachery: 20653; + const Trust: 20654; + const Truth: 20655; + const UnbendingWill: 20656; + const Valor: 20657; + const Vengeance: 20658; + const Venom: 20659; + const Victory: 20660; + const Voice: 20661; + const Void: 20662; + const War: 20663; + const Water: 20664; + const Wealth: 20665; + const Whisper: 20666; + const White: 20667; + const Wind: 20668; + const WingsofHope: 20669; + const Wisdom: 20670; + const Woe: 20671; + const Wonder: 20672; + const Wrath: 20673; + const Youth: 20674; + const Zephyr: 20675; + } + namespace dialog { + const youDoNotHaveEnoughGoldForThat: 3362 + } - namespace text { - const RepairCost: 3330; - const SellValue: 3331; - const IdentifyCost: 3332; - const ItemCannotBeTradedHere: 3333; - const TradeRepair: 3334; - const Buy: 3335; - const Sell: 3336; - const Heal: 3337; - const Repair: 3338; - const NextPage: 3339; - const PreviousPage: 3340; - const Transmute: 3341; - const YourGold: 3342; - const WhichItemShouldBeImbued: 3343; - const Yes: 3344; - const No: 3345; - const Gold2: 3346; - const Sell2: 3347; - const Buy2: 3358; - const Hire: 3349; - const ToStrength: 3473; - const ToDexterity: 3474; - const Defense: 3481; - const Identify: 3350; - const Repair2: 3351; - const EnhancedDefense: 3520; - const Strength: 4060; - const Dexterity: 4062; - const Vitality: 4066; - const Energy: 4069; - const DoNotMeetLevelReqForThisGame: 5162; - const CdKeyDisabled: 5199; - const CdKeyInUseBy: 5200; - const OnlyOneInstanceAtATime: 5201; - const CdKeyIntendedForAnotherProduct: 5202; - const InvalidPassword: 5207; - const AccountDoesNotExist: 5208; - const AccountIsCorrupted: 5209; - const AccountMustBeAtLeast: 5217; - const AccountCantBeMoreThan: 5218; - const PasswordMustBeAtLeast: 5219; - const PasswordCantBeMoreThan: 5220; - const LoginError: 5224; - const UsernameMustBeAtLeast: 5231; - const UsernameIncludedIllegalChars: 5232; - const UsernameIncludedDisallowedwords: 5233; - const AccountNameAlreadyExist: 5239; - const UnableToCreateAccount: 5249; - const CannotCreateGamesDeadHCChar: 5304; - const Disconnected: 5347; - const UnableToIndentifyVersion: 5245; - const BattlenetNotResponding: 5353; - const BattlenetNotResponding2: 5354; - const HcCannotPlayWithSc: 5361; - const ScCannotPlayWithHc: 5362; - const CannotPlayInHellClassic: 5363; - const CannotPlayInNightmareClassic: 5364; - const EnhancedDamage: 10038; - const ClassicCannotPlayWithXpac: 10101; - const XpacCannotPlayWithClassic: 10102; - const LoDKeyDisabled: 10913; - const LodKeyInUseBy: 10914; - const LoDKeyIntendedForAnotherProduct: 10915; - const NonLadderCannotPlayWithLadder: 10929; - const LadderCannotPlayWithNonLadder: 10930; - const YourPositionInLineIs: 11026; - const Gateway: 11049; - const Ghostly: 11084; - const Fanatic: 11085; - const Possessed: 11086; - const Berserker: 11087; - const ExpiresIn: 11133; - const CdKeyDisabledFromRealm: 11161; - const CannotPlayInHellXpac: 21793; - const CannotPlayInNightmareXpac: 21794; - } + namespace text { + const RepairCost: 3330; + const SellValue: 3331; + const IdentifyCost: 3332; + const ItemCannotBeTradedHere: 3333; + const TradeRepair: 3334; + const Buy: 3335; + const Sell: 3336; + const Heal: 3337; + const Repair: 3338; + const NextPage: 3339; + const PreviousPage: 3340; + const Transmute: 3341; + const YourGold: 3342; + const WhichItemShouldBeImbued: 3343; + const Yes: 3344; + const No: 3345; + const Gold2: 3346; + const Sell2: 3347; + const Buy2: 3358; + const Hire: 3349; + const ToStrength: 3473; + const ToDexterity: 3474; + const Defense: 3481; + const Identify: 3350; + const Repair2: 3351; + const EnhancedDefense: 3520; + const Strength: 4060; + const Dexterity: 4062; + const Vitality: 4066; + const Energy: 4069; + const DoNotMeetLevelReqForThisGame: 5162; + const CdKeyDisabled: 5199; + const CdKeyInUseBy: 5200; + const OnlyOneInstanceAtATime: 5201; + const CdKeyIntendedForAnotherProduct: 5202; + const InvalidPassword: 5207; + const AccountDoesNotExist: 5208; + const AccountIsCorrupted: 5209; + const AccountMustBeAtLeast: 5217; + const AccountCantBeMoreThan: 5218; + const PasswordMustBeAtLeast: 5219; + const PasswordCantBeMoreThan: 5220; + const LoginError: 5224; + const UsernameMustBeAtLeast: 5231; + const UsernameIncludedIllegalChars: 5232; + const UsernameIncludedDisallowedwords: 5233; + const AccountNameAlreadyExist: 5239; + const UnableToCreateAccount: 5249; + const CannotCreateGamesDeadHCChar: 5304; + const Disconnected: 5347; + const UnableToIndentifyVersion: 5245; + const BattlenetNotResponding: 5353; + const BattlenetNotResponding2: 5354; + const HcCannotPlayWithSc: 5361; + const ScCannotPlayWithHc: 5362; + const CannotPlayInHellClassic: 5363; + const CannotPlayInNightmareClassic: 5364; + const EnhancedDamage: 10038; + const ClassicCannotPlayWithXpac: 10101; + const XpacCannotPlayWithClassic: 10102; + const LoDKeyDisabled: 10913; + const LodKeyInUseBy: 10914; + const LoDKeyIntendedForAnotherProduct: 10915; + const NonLadderCannotPlayWithLadder: 10929; + const LadderCannotPlayWithNonLadder: 10930; + const YourPositionInLineIs: 11026; + const Gateway: 11049; + const Ghostly: 11084; + const Fanatic: 11085; + const Possessed: 11086; + const Berserker: 11087; + const ExpiresIn: 11133; + const CdKeyDisabledFromRealm: 11161; + const CannotPlayInHellXpac: 21793; + const CannotPlayInNightmareXpac: 21794; + } - namespace areas { - // Act 1 - const RogueEncampment: 5055; - const BloodMoor: 5054; - const ColdPlains: 5053; - const StonyField: 5052; - const DarkWood: 5051; - const BlackMarsh: 5050; - const TamoeHighland: 5049; - const DenofEvil: 5048; - const CaveLvl1: 5047; - const UndergroundPassageLvl1: 5046; - const HoleLvl1: 5045; - const PitLvl1: 5044; - const CaveLvl2: 5043; - const UndergroundPassageLvl2: 5042; - const HoleLvl2: 5041; - const PitLvl2: 5040; - const BurialGrounds: 5039; - const Crypt: 5038; - const Mausoleum: 5037; - const ForgottenTower: 5036; - const TowerCellarLvl1: 5035; - const TowerCellarLvl2: 5034; - const TowerCellarLvl3: 5033; - const TowerCellarLvl4: 5032; - const TowerCellarLvl5: 5031; - const MonasteryGate: 5030; - const OuterCloister: 5029; - const Barracks: 5038; - const JailLvl1: 5027; - const JailLvl2: 5026; - const JailLvl3: 5025; - const InnerCloister: 5024; - const Cathedral: 5023; - const CatacombsLvl1: 5022; - const CatacombsLvl2: 5021; - const CatacombsLvl3: 5020; - const CatacombsLvl4: 5019; - const Tristram: 5018; - const MooMooFarm: 788; + namespace areas { + // Act 1 + const RogueEncampment: 5055; + const BloodMoor: 5054; + const ColdPlains: 5053; + const StonyField: 5052; + const DarkWood: 5051; + const BlackMarsh: 5050; + const TamoeHighland: 5049; + const DenofEvil: 5048; + const CaveLvl1: 5047; + const UndergroundPassageLvl1: 5046; + const HoleLvl1: 5045; + const PitLvl1: 5044; + const CaveLvl2: 5043; + const UndergroundPassageLvl2: 5042; + const HoleLvl2: 5041; + const PitLvl2: 5040; + const BurialGrounds: 5039; + const Crypt: 5038; + const Mausoleum: 5037; + const ForgottenTower: 5036; + const TowerCellarLvl1: 5035; + const TowerCellarLvl2: 5034; + const TowerCellarLvl3: 5033; + const TowerCellarLvl4: 5032; + const TowerCellarLvl5: 5031; + const MonasteryGate: 5030; + const OuterCloister: 5029; + const Barracks: 5038; + const JailLvl1: 5027; + const JailLvl2: 5026; + const JailLvl3: 5025; + const InnerCloister: 5024; + const Cathedral: 5023; + const CatacombsLvl1: 5022; + const CatacombsLvl2: 5021; + const CatacombsLvl3: 5020; + const CatacombsLvl4: 5019; + const Tristram: 5018; + const MooMooFarm: 788; - // Act 2 - const LutGholein: 852; - const RockyWaste: 851; - const DryHills: 850; - const FarOasis: 849; - const LostCity: 848; - const ValleyofSnakes: 847; - const CanyonofMagic: 846; - const A2SewersLvl1: 845; - const A2SewersLvl2: 844; - const A2SewersLvl3: 843; - const HaremLvl1: 842; - const HaremLvl2: 841; - const PalaceCellarLvl1: 840; - const PalaceCellarLvl2: 839; - const PalaceCellarLvl3: 838; - const StonyTombLvl1: 837; - const HallsoftheDeadLvl1: 836; - const HallsoftheDeadLvl2: 835; - const ClawViperTempleLvl1: 834; - const StonyTombLvl2: 833; - const HallsoftheDeadLvl3: 832; - const ClawViperTempleLvl2: 831; - const MaggotLairLvl1: 830; - const MaggotLairLvl2: 829; - const MaggotLairLvl3: 828; - const AncientTunnels: 827; - const TalRashasTomb1: 826; - const TalRashasTomb2: 826; - const TalRashasTomb3: 826; - const TalRashasTomb4: 826; - const TalRashasTomb5: 826; - const TalRashasTomb6: 826; - const TalRashasTomb7: 826; - const DurielsLair: 825; - const ArcaneSanctuary: 824; + // Act 2 + const LutGholein: 852; + const RockyWaste: 851; + const DryHills: 850; + const FarOasis: 849; + const LostCity: 848; + const ValleyofSnakes: 847; + const CanyonofMagic: 846; + const A2SewersLvl1: 845; + const A2SewersLvl2: 844; + const A2SewersLvl3: 843; + const HaremLvl1: 842; + const HaremLvl2: 841; + const PalaceCellarLvl1: 840; + const PalaceCellarLvl2: 839; + const PalaceCellarLvl3: 838; + const StonyTombLvl1: 837; + const HallsoftheDeadLvl1: 836; + const HallsoftheDeadLvl2: 835; + const ClawViperTempleLvl1: 834; + const StonyTombLvl2: 833; + const HallsoftheDeadLvl3: 832; + const ClawViperTempleLvl2: 831; + const MaggotLairLvl1: 830; + const MaggotLairLvl2: 829; + const MaggotLairLvl3: 828; + const AncientTunnels: 827; + const TalRashasTomb1: 826; + const TalRashasTomb2: 826; + const TalRashasTomb3: 826; + const TalRashasTomb4: 826; + const TalRashasTomb5: 826; + const TalRashasTomb6: 826; + const TalRashasTomb7: 826; + const DurielsLair: 825; + const ArcaneSanctuary: 824; - // Act 3 - const KurastDocktown: 820; - const SpiderForest: 819; - const GreatMarsh: 818; - const FlayerJungle: 817; - const LowerKurast: 816; - const KurastBazaar: 815; - const UpperKurast: 814; - const KurastCauseway: 813; - const Travincal: 812; - const SpiderCave: 810; - const SpiderCavern: 811; - const SwampyPitLvl1: 809; - const SwampyPitLvl2: 808; - const FlayerDungeonLvl1: 806; - const FlayerDungeonLvl2: 805; - const SwampyPitLvl3: 807; - const FlayerDungeonLvl3: 804; - const A3SewersLvl1: 845; - const A3SewersLvl2: 844; - const RuinedTemple: 803; - const DisusedFane: 802; - const ForgottenReliquary: 801; - const ForgottenTemple: 800; - const RuinedFane: 799; - const DisusedReliquary: 798; - const DuranceofHateLvl1: 797; - const DuranceofHateLvl2: 796; - const DuranceofHateLvl3: 795; + // Act 3 + const KurastDocktown: 820; + const SpiderForest: 819; + const GreatMarsh: 818; + const FlayerJungle: 817; + const LowerKurast: 816; + const KurastBazaar: 815; + const UpperKurast: 814; + const KurastCauseway: 813; + const Travincal: 812; + const SpiderCave: 810; + const SpiderCavern: 811; + const SwampyPitLvl1: 809; + const SwampyPitLvl2: 808; + const FlayerDungeonLvl1: 806; + const FlayerDungeonLvl2: 805; + const SwampyPitLvl3: 807; + const FlayerDungeonLvl3: 804; + const A3SewersLvl1: 845; + const A3SewersLvl2: 844; + const RuinedTemple: 803; + const DisusedFane: 802; + const ForgottenReliquary: 801; + const ForgottenTemple: 800; + const RuinedFane: 799; + const DisusedReliquary: 798; + const DuranceofHateLvl1: 797; + const DuranceofHateLvl2: 796; + const DuranceofHateLvl3: 795; - // Act 4 - const PandemoniumFortress: 790; - const OuterSteppes: 792; - const PlainsofDespair: 793; - const CityoftheDamned: 794; - const RiverofFlame: 791; - const ChaosSanctuary: 789; + // Act 4 + const PandemoniumFortress: 790; + const OuterSteppes: 792; + const PlainsofDespair: 793; + const CityoftheDamned: 794; + const RiverofFlame: 791; + const ChaosSanctuary: 789; - // Act 5 - const Harrogath: 22646; - const BloodyFoothills: 22647; - const FrigidHighlands: 22648; - const ArreatPlateau: 22649; - const CrystalizedPassage: 22650; - const FrozenRiver: 22651; - const GlacialTrail: 22652; - const DrifterCavern: 22653; - const FrozenTundra: 22654; - const AncientsWay: 22655; - const IcyCellar: 22656; - const ArreatSummit: 22657; - const NihlathaksTemple: 22658; - const HallsofAnguish: 22659; - const HallsofPain: 22660; - const HallsofVaught: 22662; - const Abaddon: 21865; - const PitofAcheron: 21866; - const InfernalPit: 21867; - const WorldstoneLvl1: 22663; - const WorldstoneLvl2: 22664; - const WorldstoneLvl3: 22665; - const ThroneofDestruction: 22667; - const WorldstoneChamber: 22666; + // Act 5 + const Harrogath: 22646; + const BloodyFoothills: 22647; + const FrigidHighlands: 22648; + const ArreatPlateau: 22649; + const CrystalizedPassage: 22650; + const FrozenRiver: 22651; + const GlacialTrail: 22652; + const DrifterCavern: 22653; + const FrozenTundra: 22654; + const AncientsWay: 22655; + const IcyCellar: 22656; + const ArreatSummit: 22657; + const NihlathaksTemple: 22658; + const HallsofAnguish: 22659; + const HallsofPain: 22660; + const HallsofVaught: 22662; + const Abaddon: 21865; + const PitofAcheron: 21866; + const InfernalPit: 21867; + const WorldstoneLvl1: 22663; + const WorldstoneLvl2: 22664; + const WorldstoneLvl3: 22665; + const ThroneofDestruction: 22667; + const WorldstoneChamber: 22666; - // Ubers - const MatronsDen: 5389; - const ForgottenSands: 5389; - const FurnaceofPain: 5389; - const UberTristram: 5018; - } - } + // Ubers + const MatronsDen: 5389; + const ForgottenSands: 5389; + const FurnaceofPain: 5389; + const UberTristram: 5018; + } + } - export namespace game { - namespace profiletype { - const SinglePlayer: 1; - const Battlenet: 2; - const OpenBattlenet: 3; - const TcpIpHost: 4; - const TcpIpJoin: 5 - } + export namespace game { + namespace profiletype { + const SinglePlayer: 1; + const Battlenet: 2; + const OpenBattlenet: 3; + const TcpIpHost: 4; + const TcpIpJoin: 5 + } - namespace controls { - const Disabled: 4; - } + namespace controls { + const Disabled: 4; + } - namespace gametype { - const Classic: 0; - const Expansion: 1; - } + namespace gametype { + const Classic: 0; + const Expansion: 1; + } - // out of game locations - namespace locations { - const PreSplash: 0; - const Lobby: 1; - const WaitingInLine: 2; - const LobbyChat: 3; - const CreateGame: 4; - const JoinGame: 5; - const Ladder: 6; - const ChannelList: 7; - const MainMenu: 8; - const Login: 9; - const LoginError: 10; - const LoginUnableToConnect: 11; - const CharSelect: 12; - const RealmDown: 13; - const Disconnected: 14; - const NewCharSelected: 15; - const CharSelectPleaseWait: 16; - const LobbyLostConnection: 17; - const SplashScreen: 18; - const CdKeyInUse: 19; - const SelectDifficultySP: 20; - const MainMenuConnecting: 21; - const InvalidCdKey: 22; - const CharSelectConnecting: 23; - const ServerDown: 24; - const LobbyPleaseWait: 25; - const GameNameExists: 26; - const GatewaySelect: 27; - const GameDoesNotExist: 28; - const CharacterCreate: 29; - const OkCenteredErrorPopUp: 30; - const TermsOfUse: 31; - const CreateNewAccount: 32; - const PleaseRead: 33; - const RegisterEmail: 34; - const Credits: 35; - const Cinematics: 36; - const CharChangeRealm: 37; - const GameIsFull: 38; - const OtherMultiplayer: 39; - const TcpIp: 40; - const TcpIpEnterIp: 41; - const CharSelectNoChars: 42; - const CharSelectChangeRealm: 43; - const TcpIpUnableToConnect: 44; - } - } - - export namespace colors { - const White: "ÿc0"; - const Red: "ÿc1"; - const NeonGreen: "ÿc2"; - const Blue: "ÿc3"; - const DarkGold: "ÿc4"; - const Gray: "ÿc5"; - const Black: "ÿc6"; - const LightGold: "ÿc7"; - const Orange: "ÿc8"; - const Yellow: "ÿc9"; - const DarkGreen: "ÿconst c:"; - const Purple: "ÿc;"; - const Green: "ÿc<"; - namespace D2Bot { - const Black: 0; - const Blue: 4; - const Green: 5; - const Gold: 6; - const DarkGold: 7; - const Orange: 8; - const Red: 9; - const Gray: 10 - } - } + // out of game locations + namespace locations { + const PreSplash: 0; + const Lobby: 1; + const WaitingInLine: 2; + const LobbyChat: 3; + const CreateGame: 4; + const JoinGame: 5; + const Ladder: 6; + const ChannelList: 7; + const MainMenu: 8; + const Login: 9; + const LoginError: 10; + const LoginUnableToConnect: 11; + const CharSelect: 12; + const RealmDown: 13; + const Disconnected: 14; + const NewCharSelected: 15; + const CharSelectPleaseWait: 16; + const LobbyLostConnection: 17; + const SplashScreen: 18; + const CdKeyInUse: 19; + const SelectDifficultySP: 20; + const MainMenuConnecting: 21; + const InvalidCdKey: 22; + const CharSelectConnecting: 23; + const ServerDown: 24; + const LobbyPleaseWait: 25; + const GameNameExists: 26; + const GatewaySelect: 27; + const GameDoesNotExist: 28; + const CharacterCreate: 29; + const OkCenteredErrorPopUp: 30; + const TermsOfUse: 31; + const CreateNewAccount: 32; + const PleaseRead: 33; + const RegisterEmail: 34; + const Credits: 35; + const Cinematics: 36; + const CharChangeRealm: 37; + const GameIsFull: 38; + const OtherMultiplayer: 39; + const TcpIp: 40; + const TcpIpEnterIp: 41; + const CharSelectNoChars: 42; + const CharSelectChangeRealm: 43; + const TcpIpUnableToConnect: 44; + } + } + + export namespace colors { + const White: "ÿc0"; + const Red: "ÿc1"; + const NeonGreen: "ÿc2"; + const Blue: "ÿc3"; + const DarkGold: "ÿc4"; + const Gray: "ÿc5"; + const Black: "ÿc6"; + const LightGold: "ÿc7"; + const Orange: "ÿc8"; + const Yellow: "ÿc9"; + const DarkGreen: "ÿconst c:"; + const Purple: "ÿc;"; + const Green: "ÿc<"; + namespace D2Bot { + const Black: 0; + const Blue: 4; + const Green: 5; + const Gold: 6; + const DarkGold: 7; + const Orange: 8; + const Red: 9; + const Gray: 10 + } + } - export namespace keys { - const Backspace: 8; - const Tab: 9; - const Enter: 13; - const Shift: 16; - const Ctrl: 17; - const Alt: 18; - const PauseBreak: 19; - const CapsLock: 20; - const Escape: 27; - const Spacebar: 32; - const PageUp: 33; - const PageDown: 34; - const End: 35; - const Home: 36; - const LeftArrow: 37; - const UpArrow: 38; - const RightArrow: 39; - const DownArrow: 40; - const Insert: 45; - const Delete: 46; - const Zero: 48; - const One: 49; - const Two: 50; - const Three: 51; - const Four: 52; - const Five: 53; - const Six: 54; - const Seven: 55; - const Eight: 56; - const Nine: 57; - const LeftWindowKey: 91; - const RightWindowKey: 92; - const SelectKey: 93; - const Numpad0: 96; - const Numpad1: 97; - const Numpad2: 98; - const Numpad3: 99; - const Numpad4: 100; - const Numpad5: 101; - const Numpad6: 102; - const Numpad7: 103; - const Numpad8: 104; - const Numpad9: 105; - const NumpadStar: 106; - const NumpadPlus: 107; - const NumpadDash: 109; - const NumpadDecimal: 110; - const NumpadSlash: 111; - const F1: 112; - const F2: 113; - const F3: 114; - const F4: 115; - const F5: 116; - const F6: 117; - const F7: 118; - const F8: 119; - const F9: 120; - const F10: 121; - const F11: 122; - const F12: 123; - const NumLock: 144; - const ScrollLock: 145; - const SemiColon: 186; - const EqualSign: 187; - const Comma: 188; - const Dash: 189; - const Period: 190; - const ForwardSlash: 191; - const GraveAccent: 192; - const OpenBracket: 219; - const BackSlash: 220; - const CloseBracket: 221; - const SingleQuote: 222; - namespace code { - const Backspace: 0x08; - const Tab: 0x09; - const Clear: 0x0C; - const Enter: 0x0D; - const Shift: 0x10; - const Ctrl: 0x11; - const Alt: 0x12; - const PauseBreak: 0x13; - const CapsLock: 0x14; - const Esc: 0x1B; - const Space: 0x20; - const PageUp: 0x21; - const PageDown: 0x22; - const End: 0x23; - const Home: 0x24; - const LeftArrow: 0x25; - const UpArrow: 0x26; - const RightArrow: 0x27; - const DownArrow: 0x28; - const Select: 0x29; - const Print: 0x2A; - const PrintScreen: 0x2C; - const Insert: 0x2D; - const Delete: 0x2E; - } - } + export namespace keys { + const Backspace: 8; + const Tab: 9; + const Enter: 13; + const Shift: 16; + const Ctrl: 17; + const Alt: 18; + const PauseBreak: 19; + const CapsLock: 20; + const Escape: 27; + const Spacebar: 32; + const PageUp: 33; + const PageDown: 34; + const End: 35; + const Home: 36; + const LeftArrow: 37; + const UpArrow: 38; + const RightArrow: 39; + const DownArrow: 40; + const Insert: 45; + const Delete: 46; + const Zero: 48; + const One: 49; + const Two: 50; + const Three: 51; + const Four: 52; + const Five: 53; + const Six: 54; + const Seven: 55; + const Eight: 56; + const Nine: 57; + const LeftWindowKey: 91; + const RightWindowKey: 92; + const SelectKey: 93; + const Numpad0: 96; + const Numpad1: 97; + const Numpad2: 98; + const Numpad3: 99; + const Numpad4: 100; + const Numpad5: 101; + const Numpad6: 102; + const Numpad7: 103; + const Numpad8: 104; + const Numpad9: 105; + const NumpadStar: 106; + const NumpadPlus: 107; + const NumpadDash: 109; + const NumpadDecimal: 110; + const NumpadSlash: 111; + const F1: 112; + const F2: 113; + const F3: 114; + const F4: 115; + const F5: 116; + const F6: 117; + const F7: 118; + const F8: 119; + const F9: 120; + const F10: 121; + const F11: 122; + const F12: 123; + const NumLock: 144; + const ScrollLock: 145; + const SemiColon: 186; + const EqualSign: 187; + const Comma: 188; + const Dash: 189; + const Period: 190; + const ForwardSlash: 191; + const GraveAccent: 192; + const OpenBracket: 219; + const BackSlash: 220; + const CloseBracket: 221; + const SingleQuote: 222; + namespace code { + const Backspace: 0x08; + const Tab: 0x09; + const Clear: 0x0C; + const Enter: 0x0D; + const Shift: 0x10; + const Ctrl: 0x11; + const Alt: 0x12; + const PauseBreak: 0x13; + const CapsLock: 0x14; + const Esc: 0x1B; + const Space: 0x20; + const PageUp: 0x21; + const PageDown: 0x22; + const End: 0x23; + const Home: 0x24; + const LeftArrow: 0x25; + const UpArrow: 0x26; + const RightArrow: 0x27; + const DownArrow: 0x28; + const Select: 0x29; + const Print: 0x2A; + const PrintScreen: 0x2C; + const Insert: 0x2D; + const Delete: 0x2E; + } + } - export namespace controls { - const TextBox: 1; - const Image1: 2; - const Image2: 3; - const LabelBox: 4; - const ScrollBar: 5; - const Button: 6; - const List: 7; - const Timer: 8; - const Smack: 9; - const ProgressBar: 10; - const Popup: 11; - const AccountList: 12 - } + export namespace controls { + const TextBox: 1; + const Image1: 2; + const Image2: 3; + const LabelBox: 4; + const ScrollBar: 5; + const Button: 6; + const List: 7; + const Timer: 8; + const Smack: 9; + const ProgressBar: 10; + const Popup: 11; + const AccountList: 12 + } - export namespace packets { - namespace send { - const WalkToLocation: 0x01; - const WalkToEntity: 0x02; - const RunToLocation: 0x03; - const RunToEntity: 0x04; - const LeftSkillOnLocation: 0x05; - const LeftSkillOnEntity: 0x06; - const LeftSkillOnEntityEx: 0x07; - const LeftSkillOnLocationEx: 0x08; - const LeftSkillOnEntityEx2: 0x09; - const LeftSkillOnEntityEx3: 0x0A; - const RightSkillOnLocation: 0x0C; - const RightSkillOnEntity: 0x0D; - const RightSkillOnEntityEx: 0x0E; - const RightSkillOnLocationEx: 0x0F; - const RightSkillOnEntityEx2: 0x10; - const RightSkillOnEntityEx3: 0x11; - const SetInfernoState: 0x12; - const InteractWithEntity: 0x13; - const OverheadMessage: 0x14; - const Chat: 0x15; - const PickupItem: 0x16; - const DropItem: 0x17; - const ItemToBuffer: 0x18; - const PickupBufferItem: 0x19; - const ItemToBody: 0x1A; - const Swap2HandedItem: 0x1B; - const PickupBodyItem: 0x1C; - const SwitchBodyItem: 0x1D; - const Switch1HandWith2Hand: 0x1E; - const SwitchInventoryItem: 0x1F; - const UseItem: 0x20; - const StackItem: 0x21; - const RemoveStackItem: 0x22; - const ItemToBelt: 0x23; - const RemoveBeltItem: 0x24; - const SwitchBeltItem: 0x25; - const UseBeltItem: 0x26; - const IndentifyItem: 0x27; - const InsertSocketItem: 0x28; - const ScrollToMe: 0x29; - const ItemToCube: 0x2A; - const NPCInit: 0x2F; - const NPCCancel: 0x30; - const QuestMessage: 0x31; - const NPCBuy: 0x32; - const NPCSell: 0x33; - const NPCIndentifyItems: 0x34; - const Repair: 0x35; - const HireMerc: 0x36; - const IndentifyGamble: 0x37; - const EntityAction: 0x38; - const AddStat: 0x3A; - const AddSkill: 0x3B; - const SelectSkill: 0x3C; - const ActivateItem: 0x3E; - const CharacterPhrase: 0x3F; - const UpdateQuests: 0x40; - const Resurrect: 0x41; - const StaffInOrifice: 0x44; - const MercInteract: 0x46; - const MercMove: 0x47; - const BusyStateOff: 0x48; - const Waypoint: 0x49; - const RequestEntityUpdate: 0x4B; - const Transmorgify: 0x4C; - const PlayNPCMessage: 0x4D; - const ClickButton: 0x4F; - const DropGold: 0x50; - const BindHotkey: 0x51; - const StaminaOn: 0x53; - const StaminaOff: 0x54; - const QuestCompleted: 0x58; - const MakeEntityMove: 0x59; - const SquelchHostile: 0x5D; - const Party: 0x5E; - const UpdatePlayerPos: 0x5F; - const SwapWeapon: 0x60; - const MercItem: 0x61; - const MercRessurect: 0x62; - const LeaveGame: 0x69; - } - namespace recv { - const GameExit: 0x06; - const MapReveal: 0x07; - const MapHide: 0x08; - const ReassignPlayer: 0x15; - const SetSkill: 0x23; - const Chat: 0x26; - const UniqueEvents: 0x89; - const WeaponSwitch: 0x97; - } - } - } + export namespace packets { + namespace send { + const WalkToLocation: 0x01; + const WalkToEntity: 0x02; + const RunToLocation: 0x03; + const RunToEntity: 0x04; + const LeftSkillOnLocation: 0x05; + const LeftSkillOnEntity: 0x06; + const LeftSkillOnEntityEx: 0x07; + const LeftSkillOnLocationEx: 0x08; + const LeftSkillOnEntityEx2: 0x09; + const LeftSkillOnEntityEx3: 0x0A; + const RightSkillOnLocation: 0x0C; + const RightSkillOnEntity: 0x0D; + const RightSkillOnEntityEx: 0x0E; + const RightSkillOnLocationEx: 0x0F; + const RightSkillOnEntityEx2: 0x10; + const RightSkillOnEntityEx3: 0x11; + const SetInfernoState: 0x12; + const InteractWithEntity: 0x13; + const OverheadMessage: 0x14; + const Chat: 0x15; + const PickupItem: 0x16; + const DropItem: 0x17; + const ItemToBuffer: 0x18; + const PickupBufferItem: 0x19; + const ItemToBody: 0x1A; + const Swap2HandedItem: 0x1B; + const PickupBodyItem: 0x1C; + const SwitchBodyItem: 0x1D; + const Switch1HandWith2Hand: 0x1E; + const SwitchInventoryItem: 0x1F; + const UseItem: 0x20; + const StackItem: 0x21; + const RemoveStackItem: 0x22; + const ItemToBelt: 0x23; + const RemoveBeltItem: 0x24; + const SwitchBeltItem: 0x25; + const UseBeltItem: 0x26; + const IndentifyItem: 0x27; + const InsertSocketItem: 0x28; + const ScrollToMe: 0x29; + const ItemToCube: 0x2A; + const NPCInit: 0x2F; + const NPCCancel: 0x30; + const QuestMessage: 0x31; + const NPCBuy: 0x32; + const NPCSell: 0x33; + const NPCIndentifyItems: 0x34; + const Repair: 0x35; + const HireMerc: 0x36; + const IndentifyGamble: 0x37; + const EntityAction: 0x38; + const AddStat: 0x3A; + const AddSkill: 0x3B; + const SelectSkill: 0x3C; + const ActivateItem: 0x3E; + const CharacterPhrase: 0x3F; + const UpdateQuests: 0x40; + const Resurrect: 0x41; + const StaffInOrifice: 0x44; + const MercInteract: 0x46; + const MercMove: 0x47; + const BusyStateOff: 0x48; + const Waypoint: 0x49; + const RequestEntityUpdate: 0x4B; + const Transmorgify: 0x4C; + const PlayNPCMessage: 0x4D; + const ClickButton: 0x4F; + const DropGold: 0x50; + const BindHotkey: 0x51; + const StaminaOn: 0x53; + const StaminaOff: 0x54; + const QuestCompleted: 0x58; + const MakeEntityMove: 0x59; + const SquelchHostile: 0x5D; + const Party: 0x5E; + const UpdatePlayerPos: 0x5F; + const SwapWeapon: 0x60; + const MercItem: 0x61; + const MercRessurect: 0x62; + const LeaveGame: 0x69; + } + namespace recv { + const GameExit: 0x06; + const MapReveal: 0x07; + const MapHide: 0x08; + const ReassignPlayer: 0x15; + const SetSkill: 0x23; + const Chat: 0x26; + const UniqueEvents: 0x89; + const WeaponSwitch: 0x97; + } + } + } } export {}; From fca43fa9e1ee97226e9f936000ef7a3b6e497fff Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 13 Jun 2023 12:33:14 -0400 Subject: [PATCH 154/758] add MuleLogger.d.ts - typedef mulelogger and update the include for dropper setup - remove ItemDB addition in MuleLogger.js --- d2bs/kolbot/D2BotMuleLog.dbj | 17 +-- .../libs/systems/mulelogger/MuleLogger.d.ts | 66 +++++++++++ .../libs/systems/mulelogger/MuleLogger.js | 104 +++++++++++++----- 3 files changed, 152 insertions(+), 35 deletions(-) create mode 100644 d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index ba1bb7d3e..3d3a1c10b 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -1,9 +1,10 @@ -/* eslint-disable max-len */ /** * @filename D2BotMuleLogger.dbj * @author kolton, theBGuy * @desc Entry script for Mulelogger.js * +* @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/mulelogger/MuleLogger")} */ include("critical.js"); // required @@ -17,22 +18,22 @@ Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/ba // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds // only libs we should need here as te rest of the actions are performed from default.dbj thread -include("DropperSetup.js"); +include("systems/dropper/DropperSetup.js"); include("systems/mulelogger/MuleLogger.js"); -let Controls = require("./modules/Control"); +const Controls = require("./libs/modules/Control"); if (!FileTools.exists("data/" + me.profile + ".json")) { DataFile.create(); } let currAcc; -let usingDroper = isIncluded("DropperSetup.js"); +let usingDroper = isIncluded("systems/dropper/DropperSetup.js"); let charList = []; let accounts = []; let chars = []; -function parseInfo() { +function parseInfo () { usingDroper && parseDropperAccounts(accounts, chars); for (let i in MuleLogger.LogAccounts) { @@ -166,7 +167,9 @@ function locationAction (location) { break; case sdk.game.locations.CharSelect: // Single Player screen fix - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control && Controls.BottomLeftExit.click()) { + if (getLocation() === sdk.game.locations.CharSelect + && !Controls.CharSelectCurrentRealm.control + && Controls.BottomLeftExit.click()) { break; } @@ -295,7 +298,7 @@ function locationAction (location) { } } -function main() { +function main () { addEventListener("copydata", Starter.receiveCopyData); while (!Starter.handle) { diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts new file mode 100644 index 000000000..8b96d0ad9 --- /dev/null +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts @@ -0,0 +1,66 @@ +declare global { + namespace MuleLogger { + const LogGame: [string, string]; + let LogNames: boolean; + let LogItemLevel: boolean; + let LogEquipped: boolean; + let LogMerc: boolean; + let SaveScreenShot: boolean; + let AutoPerm: boolean; + let IngameTime: number; + const LogAccounts: { [account: string]: string[] }; + + function inGameCheck(): boolean; + /** + * Save perm status to logs/MuleLogPermInfo.json. + * @param charPermInfo - The character's permanent status information. + */ + function savePermedStatus(charPermInfo?: { charname: string; perm: boolean }): void; + + /** + * Load perm status from logs/MuleLogPermInfo.json. + * @returns The character's permanent status information. + */ + function loadPermedStatus(): { charname: string; perm: boolean }; + + /** + * @param hash - The hash value. + * @returns The loaded data. + */ + function load(hash: string): string; + + /** + * @param hash - The hash value. + * @param data - The data to save. + */ + function save(hash: string, data: string): void; + + function remove(): void; + + /** + * Log kept item stats in the manager. + * @param unit - The item unit. + * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. + * @returns The logged item information. + */ + function logItem(unit: ItemUnit, logIlvl?: boolean): { + itemColor: string; + image: string; + title: string; + description: string; + header: string; + sockets: any; // Update the type of `sockets` as needed + }; + + /** + * Log character to D2Bot# itemviewer. + * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. + * @param logName - Log the character's name. Default: `LogNames` value. + * @param saveImg - Save the item image. Default: `SaveScreenShot` value. + */ + function logChar(logIlvl?: boolean, logName?: boolean, saveImg?: boolean): void; + + // Add more functions and properties as needed + } +} +export {}; \ No newline at end of file diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 949d10da8..31271e555 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -51,22 +51,42 @@ const MuleLogger = { return false; }, + /** + * Save perm status to logs/MuleLogPermInfo.json. + * @param {{ charname: string, perm: boolean }} charPermInfo + */ savePermedStatus: function (charPermInfo = {}) { FileTools.writeText("logs/MuleLogPermInfo.json", JSON.stringify(charPermInfo)); }, + /** + * Load perm status from logs/MuleLogPermInfo.json. + * @return {{ charname: string, perm: boolean }} + */ loadPermedStatus: function () { - if (!FileTools.exists("logs/MuleLogPermInfo.json")) throw new Error("File logs/MuleLogPermInfo.json does not exist!"); + if (!FileTools.exists("logs/MuleLogPermInfo.json")) { + throw new Error("File logs/MuleLogPermInfo.json does not exist!"); + } let info = (FileTools.readText("logs/MuleLogPermInfo.json")); return info ? JSON.parse(info) : {}; }, + /** + * @param {string} hash + * @returns {string} + */ load: function (hash) { let filename = "data/secure/" + hash + ".txt"; - if (!FileTools.exists(filename)) throw new Error("File " + filename + " does not exist!"); + if (!FileTools.exists(filename)) { + throw new Error("File " + filename + " does not exist!"); + } return FileTools.readText(filename); }, + /** + * @param {string} hash + * @param {string} data + */ save: function (hash, data) { let filename = "data/secure/" + hash + ".txt"; FileTools.writeText(filename, data); @@ -77,15 +97,33 @@ const MuleLogger = { FileTools.remove("logs/MuleLogPermInfo.json"); }, - // Log kept item stats in the manager. + /** + * Log kept item stats in the manager. + * @param {ItemUnit} unit + * @param {boolean} [logIlvl] + */ logItem: function (unit, logIlvl = this.LogItemLevel) { - if (!isIncluded("core/misc.js")) { - include("core/misc.js"); - } + includeIfNotIncluded("core/misc.js"); 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) + "$" + unit.gid + ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : ""); + let name = ( + unit.itemType + "_" + + unit.fname + .split("\n") + .reverse() + .join(" ") + .replace(/(y|ÿ)c[0-9!"+<:;.*]|\/|\\/g, "") + .trim() + ); + let desc = ( + Item.getItemDesc(unit, logIlvl) + "$" + + unit.gid + ":" + + unit.classid + ":" + + unit.location + ":" + + unit.x + ":" + + unit.y + + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : "") + ); let color = unit.getColor(); let code = Item.getItemCode(unit); let sock = unit.getItemsEx(); @@ -109,19 +147,17 @@ const MuleLogger = { }; }, + /** + * Log character to D2Bot# itemviewer. + * @param {boolean} [logIlvl] + * @param {boolean} [logName] + * @param {boolean} [saveImg] + */ logChar: function (logIlvl = this.LogItemLevel, logName = this.LogNames, saveImg = this.SaveScreenShot) { while (!me.gameReady) { delay(100); } - // try again if db is locked!! - ItemDB is from https://github.com/dzik87/D2Dropper - // maybe just add it to the core? It's not going to work without an update - if (FileTools.exists("libs/ItemDB.js") && (isIncluded("ItemDB.js") || include("ItemDB.js"))) { - while (!ItemDB.init(false)) { - delay(1000); - } - } - let items = me.getItemsEx(); if (!items.length) return; @@ -149,9 +185,10 @@ const MuleLogger = { return b.location - a.location; }); - for (let i = 0; i < items.length; i += 1) { - if ((this.LogEquipped || items[i].isInStorage) && (items[i].quality > sdk.items.quality.Normal || !Item.skipItem(items[i].classid))) { - let parsedItem = this.logItem(items[i], logIlvl); + for (let item of items) { + if ((this.LogEquipped || item.isInStorage) + && (item.quality > sdk.items.quality.Normal || !Item.skipItem(item.classid))) { + let parsedItem = this.logItem(item, logIlvl); // Log names to saved image logName && (parsedItem.header = (me.account || "Single Player") + " / " + me.name); @@ -162,10 +199,10 @@ const MuleLogger = { // Remove itemtype_ prefix from the name parsedItem.title = parsedItem.title.substr(parsedItem.title.indexOf("_") + 1); - items[i].isEquipped && (parsedItem.title += (items[i].isOnSwap ? " (secondary equipped)" : " (equipped)")); - items[i].isInInventory && (parsedItem.title += " (inventory)"); - items[i].isInStash && (parsedItem.title += " (stash)"); - items[i].isInCube && (parsedItem.title += " (cube)"); + item.isEquipped && (parsedItem.title += (item.isOnSwap ? " (secondary equipped)" : " (equipped)")); + item.isInInventory && (parsedItem.title += " (inventory)"); + item.isInStash && (parsedItem.title += " (stash)"); + item.isInCube && (parsedItem.title += " (cube)"); let string = JSON.stringify(parsedItem); finalString += (string + "\n"); @@ -173,13 +210,15 @@ const MuleLogger = { } if (this.LogMerc) { - let merc = Misc.poll(() => me.getMerc(), 1000, 100); + let merc = Misc.poll(function () { + return me.getMerc(); + }, 1000, 100); if (merc) { let mercItems = merc.getItemsEx(); - for (let i = 0; i < mercItems.length; i += 1) { - let parsedItem = this.logItem(mercItems[i]); + for (let item of mercItems) { + let parsedItem = this.logItem(item); parsedItem.title += " (merc)"; let string = JSON.stringify(parsedItem); finalString += (string + "\n"); @@ -190,12 +229,21 @@ const MuleLogger = { // hcl = hardcore class ladder // sen = softcore expan nonladder - FileTools.writeText("mules/" + realm + "/" + me.account + "/" + me.name + "." + ( me.playertype ? "h" : "s" ) + (me.gametype ? "e" : "c" ) + ( me.ladder > 0 ? "l" : "n" ) + ".txt", finalString); + FileTools.writeText( + "mules/" + realm + "/" + + me.account + "/" + + me.name + "." + + ( me.playertype ? "h" : "s" ) + + (me.gametype ? "e" : "c" ) + + ( me.ladder > 0 ? "l" : "n" ) + + ".txt", + finalString + ); print("Item logging done."); } }; // load configuration file and apply settings to MuleLogger, has to be after the namespace is created -(function() { +(function () { Object.assign(MuleLogger, require("./LoggerConfig", null, false)); })(); From ef20a54706ad7ff8edd91b08a0f8724274e91ce4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:09:53 -0400 Subject: [PATCH 155/758] Update default.dbj - move dropper handler so any profile can access it if it exists. It handles overriding `MuleLogger.logChar` --- d2bs/kolbot/default.dbj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 2e1800abe..6194fdb1b 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -60,11 +60,13 @@ function main () { // MuleLogger handler if (MuleLogger.inGameCheck()) return true; + // Dropper handler + if (FileTools.exists("libs/systems/dropper/ItemDB.js")) { + include("systems/dropper/ItemDB.js"); + } + // 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) { From 49eb16670c817500a910c08fa1d3e2423cc4a3b4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:41:06 -0400 Subject: [PATCH 156/758] Update Worker.js - should fix ability to stop background processes --- d2bs/kolbot/libs/modules/Worker.js | 45 ++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index 98386ec49..51f431324 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -20,7 +20,11 @@ }; const Worker = new (function () { - let work = [], workLowPrio = [], self = this; + const self = this; + const work = []; + const workLowPrio = []; + /** @private */ + this.workDisabled = false; this.push = function (newWork) { return work.push(newWork); @@ -31,8 +35,9 @@ }; const checker = function (val) { + if (self.workDisabled) return; try { - !self.workDisabled && val.length && val.splice(0, val.length).forEach(self.work); + val.length && val.splice(0, val.length).forEach(self.work); } catch (error) { if (!error.message.endsWith("too much recursion")) { throw error; @@ -55,7 +60,6 @@ }; /** - * * @param {function({Worker}):boolean} callback */ this.runInBackground = new Proxy({ processes: {} }, { @@ -63,13 +67,18 @@ if (target.processes.hasOwnProperty(name)) { throw new Error("Process " + name + " already exists."); } - target.processes[name] = { callback: callback, running: true, name: name }; + target.processes[name] = { + callback: callback, + running: true, + name: name + }; let proxyCallback = function () { - if (target.processes[name].running) { - target.processes[name].running = (callback() && self.pushLowPrio(proxyCallback) > -1); - } + if (!target.processes[name]) return; + target.processes[name].running = callback(); if (!target.processes[name].running) { delete target.processes[name]; + } else { + self.pushLowPrio(proxyCallback); } }; self.pushLowPrio(proxyCallback); @@ -85,24 +94,36 @@ }); this.stopProcess = function (name) { + if (typeof self.runInBackground === "undefined" + || typeof self.runInBackground.processes === "undefined") { + return; + } + if (typeof self.runInBackground.processes[name] === "undefined") { + return; + } delete self.runInBackground.processes[name]; }; + /** @param {Promise<*>} promise */ global.await = function (promise) { - while (delay() && !promise.stopped) { + while (delay(1) && !promise.stopped) { // } - return promise.value; }; - this.workDisabled = 0; - global._delay = delay; // The original delay function + /** + * Just makes it easier to peform a delay + * @param {number} amount + */ + this.timeout = function (amount) { + return global._delay(amount); + }; + // Override the delay function, to check for background work while we wait anyway global.delay = function (amount) { - let recursive = recursiveCheck(); let start = getTickCount(); amount = amount || 0; From da7c273c7ceec7cf9e7adb0748aaf069d30f18b4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:43:43 -0400 Subject: [PATCH 157/758] Update D2BotMule.dbj - fix including skill.js out of game - fix account already exists case --- d2bs/kolbot/D2BotMule.dbj | 74 +++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 07991ad29..b4bd77fff 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /** * @filename D2BotMule.dbj * @author kolton, theBGuy @@ -27,16 +26,16 @@ Starter.Config.MakeAccountOnFailure = true; // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds // globals needed for core gameplay -includeCoreLibs(); +includeCoreLibs({ exclude: ["Skill.js"] }); // system libs includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -const Controls = require("./modules/Control"); -const Overrides = require("./modules/Override"); -const Worker = require("./modules/Worker"); +const Controls = require("./libs/modules/Control"); +const Overrides = require("./libs/modules/Override"); +const Worker = require("./libs/modules/Worker"); /** @global */ let master, muleMode, muleFilename, maxCharCount; @@ -181,6 +180,7 @@ const Mule = { return false; } + includeIfNotIncluded("core/Skill.js"); Starter.firstLogin ? (Starter.firstLogin = false) : (status = "begin"); status !== "begin" && (status = "ready"); Mule.statusString = "In " + (muleMode === 2 ? "anni " : muleMode === 1 ? "torch " : "") + "mule game."; @@ -208,9 +208,7 @@ const Mule = { addEventListener("gameevent", gameEvent); } // Worker.runInBackground.areaWatcher = Mule.areaWatcher; // bugs for some reason, quits game then on re-join d2bot spams In mule game for ~30seconds - if (Worker.runInBackground.antiIdle === undefined) { - Worker.runInBackground.antiIdle = Mule.antiIdle; - } + Worker.runInBackground.antiIdle = Mule.antiIdle; return true; }, @@ -275,6 +273,7 @@ const Mule = { }, quit: function () { + Worker.stopProcess("antiIdle"); ["default.dbj", "threads/AntiIdle.js", "threads/AreaWatcher.js"].forEach(thread => { let script = getScript(thread); if (script && script.running && script.stop()) { @@ -396,8 +395,13 @@ const Mule = { if (!Mule.clearedJunk) { me.getItemsEx() - .filter(item => item.isInInventory && Town.ignoreType(item.itemType) && (muleMode === 0 || item.classid !== sdk.items.ScrollofIdentify)) - .forEach(item => { + .filter(function (item) { + return (item.isInInventory + && Town.ignoreType(item.itemType) + && (muleMode === 0 || item.classid !== sdk.items.ScrollofIdentify) + ); + }) + .forEach(function (item) { try { item.drop(); } catch (e) { @@ -407,7 +411,12 @@ const Mule = { Mule.clearedJunk = true; // only do this once } - const getItems = () => getUnits(sdk.unittype.Item).filter(i => i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType)); + const getItems = function () { + return getUnits(sdk.unittype.Item) + .filter(function (i) { + return i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType); + }); + }; while (me.gameReady) { if (masterStatus.status === "done" || Mule.continuousMule || override) { @@ -426,7 +435,7 @@ const Mule = { } // pick large items first by sorting items by size in descending order and move gheed's charm to the end of the list - list.sort(function(a, b) { + list.sort(function (a, b) { if (a.isGheeds && !Pickit.canPick(a)) return 1; if (b.isGheeds && !Pickit.canPick(b)) return -1; @@ -438,7 +447,8 @@ const Mule = { let canFit = Storage.Inventory.CanFit(item); // Torch and Anni handling - if (muleMode > 0 && item.unique && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(item.classid) && !Pickit.canPick(item)) { + if (muleMode > 0 && item.unique + && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(item.classid) && !Pickit.canPick(item)) { let msg = item.classid === sdk.items.LargeCharm ? "Mule already has a Torch." : "Mule already has a Anni."; D2Bot.printToConsole(msg, sdk.colors.D2Bot.DarkGold); rval = "next"; @@ -665,9 +675,12 @@ function locationAction (location) { }; if (Starter.makeAccount) { - ControlAction.makeAccount(info); - D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); - Starter.makeAccount = false; + if (ControlAction.makeAccount(info)) { + D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); + Starter.makeAccount = false; + } else { + MuleData.nextAccount(); + } break; } @@ -675,6 +688,26 @@ function locationAction (location) { MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); !ControlAction.loginAccount(info) && (Starter.makeAccount = true); + break; + case sdk.game.locations.CreateNewAccount: + if (Starter.makeAccount) { + obj = MuleData.read(); + info = { + realm: muleObj.realm, + account: obj.account, + password: muleObj.accountPassword + }; + + if (ControlAction.makeAccount(info)) { + D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); + Starter.makeAccount = false; + } else { + MuleData.nextAccount(); + } + + break; + } + break; case sdk.game.locations.LoginError: case sdk.game.locations.InvalidCdKey: @@ -737,7 +770,8 @@ function locationAction (location) { } // Can't create character, button greyed out = high likelyhood of realm down - if (getLocation() === sdk.game.locations.CharSelectNoChars && Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { + if (getLocation() === sdk.game.locations.CharSelectNoChars + && Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { D2Bot.updateStatus("Realm Down"); delay(1000); @@ -986,7 +1020,7 @@ function main () { Mule.done(); return; - // can't fit more items, get to next character or account + // can't fit more items, get to next character or account case "next": Mule.next(); @@ -1003,7 +1037,9 @@ function main () { } if (Mule.continuousMule) { - if (Starter.Config.MaxGameTime > 0 && getTickCount() - me.gamestarttime > Time.minutes(Starter.Config.MaxGameTime) && Mule.foreverAlone()) { + if (Starter.Config.MaxGameTime > 0 + && getTickCount() - me.gamestarttime > Time.minutes(Starter.Config.MaxGameTime) + && Mule.foreverAlone()) { Mule.gameRefresh(); break; From 4c54c4223b9599d5baabe8b96af9e908eeaa9d2f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:44:16 -0400 Subject: [PATCH 158/758] Update OOG.js - fix account already exists case --- d2bs/kolbot/libs/OOG.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 6c9dce875..8e1adb869 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -609,6 +609,10 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } break; + case sdk.game.locations.LoginError: + Controls.LoginErrorOk.click(); + + return false; default: break; } @@ -1159,8 +1163,6 @@ includeIfNotIncluded("oog/D2Bot.js"); // required switch (string) { case getLocaleString(sdk.locale.text.UsernameIncludedIllegalChars): case getLocaleString(sdk.locale.text.UsernameIncludedDisallowedwords): - case getLocaleString(sdk.locale.text.InvalidPassword): - case getLocaleString(5208): // Invalid account D2Bot.updateStatus("Invalid Account Name"); D2Bot.printToConsole("Invalid Account Name :: " + Starter.profileInfo.account); D2Bot.stop(true); @@ -1181,6 +1183,8 @@ includeIfNotIncluded("oog/D2Bot.js"); // required D2Bot.stop(true); break; + case getLocaleString(sdk.locale.text.InvalidPassword): + case getLocaleString(5208): // Invalid account case getLocaleString(sdk.locale.text.AccountDoesNotExist): if (!!Starter.Config.MakeAccountOnFailure) { Starter.makeAccount = true; From d33f6428e0c667679528d6e623d8b993c0955b26 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:03:11 -0400 Subject: [PATCH 159/758] update mulelogger - added back the dropper handler, I dislike having it here but easiest solution for now --- d2bs/kolbot/default.dbj | 10 +++---- .../libs/systems/mulelogger/MuleLogger.js | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 6194fdb1b..12d03dc02 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -4,6 +4,7 @@ * @desc gets executed upon gamejoin, main thread for bot * * @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/mulelogger/MuleLogger")} */ js_strict(true); include("critical.js"); // required @@ -56,15 +57,10 @@ function main () { getScript(true).stop(); // kill this thread return true; } - + // MuleLogger handler if (MuleLogger.inGameCheck()) return true; - - // Dropper handler - if (FileTools.exists("libs/systems/dropper/ItemDB.js")) { - include("systems/dropper/ItemDB.js"); - } - + // don't load default for dropper/mules if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { load("threads/AreaWatcher.js"); diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 31271e555..28b14cf96 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -7,7 +7,17 @@ */ const MuleLogger = { - // configuration file loaded at bottom + // ~~~ DON'T TOUCH, configuration file loaded at bottom. Use LoggerConfig.js ~~~ // + LogGame: ["", ""], // ["gamename", "password"] + LogNames: true, // Put account/character name on the picture + LogItemLevel: true, // Add item level to the picture + LogEquipped: true, // include equipped items + LogMerc: true, // include items merc has equipped (if alive) + SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) + AutoPerm: true, // override InGameTime to perm character + IngameTime: 0, // (180, 210) to avoid RD, increase it to (7230, 7290) for mule perming + LogAccounts: {}, + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // inGameCheck: function () { if (getScript("D2BotMuleLog.dbj") && this.LogGame[0] && me.gamename.match(this.LogGame[0], "i")) { print("ÿc4MuleLoggerÿc0: Logging items on " + me.account + " - " + me.name + "."); @@ -27,7 +37,10 @@ const MuleLogger = { } while ((getTickCount() - me.gamestarttime) < Time.seconds(stayInGame)) { - me.overhead("ÿc2Log items done. ÿc4Stay in " + "ÿc4game more:ÿc0 " + Math.floor(stayInGame - (getTickCount() - me.gamestarttime) / 1000) + " sec"); + me.overhead( + "ÿc2Log items done. ÿc4Stay in " + "ÿc4game more:ÿc0 " + + Math.floor(stayInGame - (getTickCount() - me.gamestarttime) / 1000) + " sec" + ); delay(1000); @@ -155,7 +168,15 @@ const MuleLogger = { */ logChar: function (logIlvl = this.LogItemLevel, logName = this.LogNames, saveImg = this.SaveScreenShot) { while (!me.gameReady) { - delay(100); + delay(3); + } + + // Dropper handler, todo figure out another way to do this + if (isIncluded("systems/dropper/ItemDB.js") || include("systems/dropper/ItemDB.js")) { + /** @typedef {import("../dropper/ItemDB")} */ + while (!ItemDB.init(false)) { + delay(1000); + } } let items = me.getItemsEx(); From ec6a3d92d23661c08c1fdb090b5dbc2e0b981479 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 16 Jun 2023 23:42:22 -0400 Subject: [PATCH 160/758] Update Town.js - better usage of closestnpc, helps to keep us from choosing closest npc only to have to run to a farther one for repair/potions - add use of Atma in act2 if we just want to heal --- d2bs/kolbot/libs/core/Town.js | 51 +++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index f73395a1a..26065d357 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -368,15 +368,45 @@ const Town = { } } - // we are just trying to clear our inventory, use the closest npc - // what if we have unid items? Should we use cain if he is closer than the npc with scrolls? - // for now it won't get here with unids - // need to also take into account what our next task is + /** + * 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); - npc = getUnits(sdk.unittype.NPC) - .sort((a, b) => a.distance - b.distance) - .find(unit => [npcs.Shop, npcs.Repair].includes(unit.name.toLowerCase())); + let _needPots = me.needPotions(); + let _needRepair = Town.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") { @@ -412,6 +442,10 @@ const Town = { break; case "Heal": + if (String.isEqual(npc.name, NPC.Atma)) { + // prevent crash due to atma not being a shoppable npc + me.cancelUIFlags(); + } break; } @@ -2267,7 +2301,8 @@ const Town = { !this.act[me.act - 1].initialized && this.initialize(); // Act 5 wp->portalspot override - ActMap.cpp crash - if (me.act === 5 && spot === "portalspot" && getDistance(me.x, me.y, 5113, 5068) <= 8) { + if (me.act === 5 && spot === "portalspot" + && getDistance(me.x, me.y, 5113, 5068) <= 8) { return [5098, 5018].distance; } From b738f06a18ba6f5ce8efb01a0f59d65d3c7153d3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 16 Jun 2023 23:46:58 -0400 Subject: [PATCH 161/758] Update Town.js - fix trying to init an npc while we are in stash/cube ect --- d2bs/kolbot/libs/core/Town.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 26065d357..17a606fe8 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -366,6 +366,8 @@ const Town = { me.cancelUIFlags(); npc = null; } + } else { + me.cancelUIFlags(); } /** From 8c36c0acbf94c61208b7ce432e36f398971fa86b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 16 Jun 2023 23:47:58 -0400 Subject: [PATCH 162/758] Update ToolsThread.js - add reload ability to toolsthread --- d2bs/kolbot/threads/ToolsThread.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 4f4120763..27c3c4a03 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -24,7 +24,7 @@ new Overrides.Override(Attack, Attack.getNearestMonster, function (orignal) { return (monster ? " to " + monster.name : ""); }).apply(); -function main() { +function main () { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); @@ -284,6 +284,13 @@ function main() { console.debug("Quiting"); quitFlag = true; + break; + case "reload": + console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); + Common.Toolsthread.stopDefault() && delay(Time.seconds(5)); + console.log("Starting default.dbj"); + load("default.dbj"); + break; case "datadump": console.log("ÿc8Systems Data Dump: ÿc2Start"); From 22c769a7e9384a505a90de5737e8751d3f9bfb7d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 Jun 2023 19:03:09 -0400 Subject: [PATCH 163/758] small cleanup - charged item handling - moved `Attack.getCharges` into `Skill` - made `Skill.castCharges`, basic usage with some simple error checks. Can use charges that are on switch and will swap back after - small cleanup in attack, added more to the Bad Attack Config warning since people still ask what it means, print current attack skills so they can see them - don't check for infinity on us or merc if its impossible - don't check for auradin rw's if its impossible --- d2bs/kolbot/libs/core/Attack.js | 142 ++++++++++++++------------------ d2bs/kolbot/libs/core/Skill.js | 88 +++++++++++++++++++- 2 files changed, 147 insertions(+), 83 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 5085b0788..7041b3e69 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -3,22 +3,23 @@ * @author kolton, theBGuy * @desc handle player attacks * - * @typedef {import("../../sdk/globals")} */ -/** - * Attack - * @global - */ + const Attack = { infinity: false, auradin: false, monsterObjects: [ - sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, - sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, sdk.monsters.FireTower, - sdk.monsters.BarricadeDoor1, sdk.monsters.BarricadeDoor2, sdk.monsters.BarricadeWall1, sdk.monsters.BarricadeWall2, - sdk.monsters.CatapultS, sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, sdk.monsters.CatapultW, - sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.Hut, + sdk.monsters.Turret1, sdk.monsters.Turret2, + sdk.monsters.Turret3, sdk.monsters.MummyGenerator, + sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, + sdk.monsters.FireTower, sdk.monsters.BarricadeDoor1, + sdk.monsters.BarricadeDoor2, sdk.monsters.BarricadeWall1, + sdk.monsters.BarricadeWall2, sdk.monsters.CatapultS, + sdk.monsters.CatapultE, sdk.monsters.CatapultSiege, + sdk.monsters.CatapultW, sdk.monsters.BarricadeTower, + sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, + sdk.monsters.DiablosBoneCage2, sdk.monsters.Hut, ], Result: { FAILED: 0, @@ -41,7 +42,10 @@ const Attack = { if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { showConsole(); - console.warn("ÿc1Bad attack config. Don't expect your bot to attack."); + console.warn( + "ÿc1Bad attack config. Don't expect your bot to attack." + "\n" + + "ÿc0AttackSkills: ", Config.AttackSkill + ); } this.getPrimarySlot(); @@ -90,7 +94,7 @@ const Attack = { Config.PrimarySlot = sdk.player.slot.Main; } else { // Always start on main-hand - me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); + me.switchWeapons(sdk.player.slot.Main); // have cta if ((Precast.haveCTA > -1) || Precast.checkCTA()) { // have item on non-cta slot - set non-cta slot as primary @@ -151,67 +155,30 @@ const Attack = { return false; }, - /** - * @depreciated - * @description Get items with charges - isn't used anywhere - * @returns {boolean} - */ - getCharges: function () { - !Skill.charges && (Skill.charges = []); - - let item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - 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) { - Skill.charges.push({ - unit: copyUnit(item), - gid: item.gid, - 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 { - Skill.charges.push({ - unit: copyUnit(item), - gid: item.gid, - skill: stats[sdk.stats.ChargedSkill].skill, - level: stats[sdk.stats.ChargedSkill].level, - charges: stats[sdk.stats.ChargedSkill].charges, - maxcharges: stats[sdk.stats.ChargedSkill].maxcharges - }); - } - } - } while (item.getNext()); - } - - return true; - }, - /** * @description Check if player or his merc are using Infinity, and adjust resistance checks based on that * @returns {boolean} */ checkInfinity: function () { - if (me.classic) return false; + // don't check if classic or under 63 - not possibile to either equip or have merc use + if (me.classic || me.charlvl < 63) return false; - let merc; // check if we have a merc and they aren't dead - Config.UseMerc && !me.mercrevivecost && (merc = Misc.poll(() => me.getMerc(), 1000, 100)); - - // Check merc infinity - !!merc && (Attack.infinity = merc.checkItem({ name: sdk.locale.items.Infinity }).have); + if (Config.UseMerc && me.mercrevivecost === 0) { + let merc = Misc.poll(function () { + return me.getMerc(); + }, 1000, 100); + // only merc who can use it + if (merc && merc.classid === sdk.mercs.Guard) { + Attack.infinity = merc.checkItem({ name: sdk.locale.items.Infinity }).have; + if (Attack.infinity) return true; + } + } // Check player infinity - only check if merc doesn't have - !Attack.infinity && (Attack.infinity = me.checkItem({ name: sdk.locale.items.Infinity, equipped: true }).have); + if (!Attack.infinity) { + Attack.infinity = me.checkItem({ name: sdk.locale.items.Infinity, equipped: true }).have; + } return Attack.infinity; }, @@ -221,9 +188,13 @@ const Attack = { * @returns {boolean} */ checkAuradin: function () { + // dragon lvl 61, dream lvl 65, hoj lvl 67, ice lvl 65 + if (me.charlvl < 61) return false; Attack.auradin = me.haveSome([ - { name: sdk.locale.items.Dragon, equipped: true }, { name: sdk.locale.items.Dream, equipped: true }, - { name: sdk.locale.items.HandofJustice, equipped: true }, { name: sdk.locale.items.Ice, equipped: true }, + { name: sdk.locale.items.Dragon, equipped: true }, + { name: sdk.locale.items.Dream, equipped: true }, + { name: sdk.locale.items.HandofJustice, equipped: true }, + { name: sdk.locale.items.Ice, equipped: true }, ]); return Attack.auradin; @@ -236,8 +207,13 @@ const Attack = { */ canTeleStomp: function (unit) { if (!unit || !unit.attackable) return false; - return Config.TeleStomp && Config.UseMerc && Pather.canTeleport() - && Attack.checkResist(unit, "physical") && !!me.getMerc() && Attack.validSpot(unit.x, unit.y); + return ( + Config.TeleStomp && Config.UseMerc + && Pather.canTeleport() + && Attack.checkResist(unit, "physical") + && !!me.getMerc() + && Attack.validSpot(unit.x, unit.y) + ); }, /** @@ -247,7 +223,9 @@ const Attack = { */ kill: function (classId) { if (!classId || Config.AttackSkill[1] < 0) return false; - let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); + let target = (typeof classId === "object" + ? classId + : Misc.poll(() => Game.getMonster(classId), 2000, 100)); if (!target) { console.warn("Attack.kill: Target not found"); @@ -348,7 +326,9 @@ const Attack = { */ hurt: function (classId, percent) { if (!classId || !percent) return false; - let target = (typeof classId === "object" ? classId : Misc.poll(() => Game.getMonster(classId), 2000, 100)); + let target = (typeof classId === "object" + ? classId + : Misc.poll(() => Game.getMonster(classId), 2000, 100)); if (!target) { console.warn("Attack.hurt: Target not found"); @@ -396,7 +376,7 @@ const Attack = { /** * @description Determine scariness of monster for monster sorting - * @param {Unit} unit + * @param {Monster} unit * @returns {number} scariness */ getScarinessLevel: function (unit) { @@ -434,7 +414,7 @@ const Attack = { * @param {number} [range=25] * @param {number} [spectype=0] * @param {number | Unit} [bossId] - * @param {Function} [sortfunc] + * @param {(a: T, b: T) => number} [sortfunc] * @param {boolean} [pickit] * @returns {boolean} * @todo change to passing an object @@ -960,16 +940,15 @@ const Attack = { if (obj) { if (obj[area] === undefined) { obj[area] = { - runs: 0, - averageUniques: 0 + runs: 1, + averageUniques: (Attack.uniques).toFixed(4) }; + } else { + let { averageUniques, runs } = obj[area]; + obj[area].averageUniques = ((averageUniques * runs + Attack.uniques) / (runs + 1)).toFixed(4); + obj[area].runs += 1; } - obj[area].averageUniques = ( - (obj[area].averageUniques * obj[area].runs + Attack.uniques) / (obj[area].runs + 1) - ).toFixed(4); - obj[area].runs += 1; - FileAction.write("statistics.json", JSON.stringify(obj)); } @@ -983,7 +962,7 @@ const Attack = { * @returns {boolean} */ clearLevel: function (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]); } @@ -1227,7 +1206,8 @@ const Attack = { if (unit) { do { - if (unit.name && getDistance(unit, x, y) <= range && ids.includes(unit.name.toLowerCase())) { + if (unit.name && getDistance(unit, x, y) <= range + && ids.includes(unit.name.toLowerCase())) { list.push(copyUnit(unit)); } } while (unit.getNext()); diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 206d1fa77..2ae1f5c2b 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -64,6 +64,59 @@ } }, + /** + * @description Get items with charges + * @returns {boolean} + */ + getCharges: function () { + Skill.charges = []; + /** + * @typedef {Object} Charge + * @property {number} skill + * @property {number} level + * @property {number} charges + * @property {number} maxcharges + */ + /** + * @constructor + * @param {Charge} charge + * @param {ItemUnit} unit + */ + function ChargedSkill (charge, unit) { + this.skill = charge.skill; + this.level = charge.level; + this.charges = charge.charges; + this.maxcharges = charge.maxcharges; + this.gid = unit.gid; + this.unit = copyUnit(unit); + } + + let item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + let stats = item.getStat(-2); + if (!stats.hasOwnProperty(sdk.stats.ChargedSkill)) continue; + + /** @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) { + // handle wierd case were we get undefined charge + if (!charge || !charge.skill) continue; + if (Skill.charges.find(c => c.gid === item.gid && c.skill === charge.skill)) { + continue; + } + Skill.charges.push(new ChargedSkill(charge, item)); + } + } while (item.getNext()); + } + + return true; + }, + // initialize our skill data init: function () { // reset check values @@ -74,8 +127,11 @@ _SkillData.get(i).reset(); } } - // redo cta check - Precast.checkCTA(); + if (me.expansion) { + // redo cta check + Precast.checkCTA(); + Skill.getCharges(); + } switch (me.classid) { case sdk.player.class.Amazon: @@ -111,6 +167,7 @@ // maybe store gid of shield, would still require doing me.getItem(-1, 1, gid) everytime we wanted to cast but that's still less involved // than getting every item we have and finding shield, for now keeping this. Checks during init if we have a shield or not Precast.skills.holyShield.canUse = me.usingShield(); + Precast.skills.holyShield.duration = this.getDuration(sdk.skills.HolyShield); break; case sdk.player.class.Barbarian: @@ -479,6 +536,33 @@ return true; }, + + /** + * Basic use of charged skill casting + * @param {number} skillId + * @param {Unit | { x: number, y: number }} unit + * @returns {boolean} + */ + castCharges: function (skillId, unit) { + if (!Skill.charges) return false; + let charge = Skill.charges.find(c => c.skill === skillId); + if (!charge) return false; + let item = me.getItem(-1, sdk.items.mode.Equipped, charge.gid); + if (!item) return false; + if (!unit) unit = me; + const weaponSwitch = me.weaponswitch; + if ([sdk.body.RightArmSecondary, sdk.body.RightArmSecondary].includes(item.bodylocation)) { + me.switchWeapons(weaponSwitch ^ 1); + } + try { + return item.castChargedSkill(skillId, unit.x, unit.y); + } finally { + if (weaponSwitch !== me.weaponswitch) { + me.switchWeapons(weaponSwitch); + } + charge.charges--; + } + }, }; Object.defineProperties(Skill, { From 41e79fee9540e28d153f653fffdcf6f533d3d35d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 19 Jun 2023 12:53:18 -0400 Subject: [PATCH 164/758] Update Town.js - should fix getting stuck inside the building with atma --- d2bs/kolbot/libs/core/Town.js | 41 +++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 17a606fe8..16bce399d 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -2327,32 +2327,46 @@ const Town = { // act 5 static paths, ActMap.cpp seems to have issues with A5 // should other towns have static paths? if (me.act === 5) { + /** @type {Array<[number, number]>} */ let path = []; let returnWhenDone = false; // Act 5 wp->portalspot override - ActMap.cpp crash if (spot === "portalspot" && getDistance(me.x, me.y, 5113, 5068) <= 8) { - path = [5113, 5068, 5108, 5051, 5106, 5046, 5104, 5041, 5102, 5027, 5098, 5018]; + path = [[5113, 5068], [5108, 5051], [5106, 5046], [5104, 5041], [5102, 5027], [5098, 5018]]; returnWhenDone = true; } if (["stash", "waypoint"].includes(spot)) { // malah -> stash/wp if (getDistance(me.x, me.y, 5081, 5031) <= 10) { - path = [5089, 5029, 5093, 5021, 5101, 5027, 5107, 5043, 5108, 5052]; + path = [[5089, 5029], [5093, 5021], [5101, 5027], [5107, 5043], [5108, 5052]]; } else if (getDistance(me.x, me.y, 5099, 5020) <= 13) { // portalspot -> stash/wp - path = [5102, 5031, 5107, 5042, 5108, 5052]; + path = [[5102, 5031], [5107, 5042], [5108, 5052]]; } } if (path.length) { - for (let i = 0; i < path.length; i += 2) { - Pather.walkTo(path[i], path[i + 1]); - } + path.forEach(function (node) { + Pather.walkTo(node[0], node[1]); + }); if (returnWhenDone) return true; } + } else if (me.act === 2 && me.y < 5049 && !String.isEqual(spot, NPC.Atma)) { + // we are inside the building, if Atma is blocking the entrance we need the side door + let atma = Game.getNPC(NPC.Atma); + // console.debug("atma", atma); + // todo - might need to consider her targetx/y coords as well + if (atma && (atma.x === 5136 || atma.x === 5137) + && (atma.y >= 5048 && atma.y <= 5050)) { + // 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]); + }); + return true; + } } for (let i = 0; i < 3; i += 1) { @@ -2376,8 +2390,10 @@ const Town = { let townSpot; let longRange = (!Skill.haveTK && spot === "waypoint"); let tkRange = (Skill.haveTK && allowTK && ["stash", "portalspot", "waypoint"].includes(spot)); + const npcSpot = Object.values(NPC).includes(spot.toLowerCase()); - if (!this.act[me.act - 1].hasOwnProperty("spot") || !this.act[me.act - 1].spot.hasOwnProperty(spot)) { + if (!this.act[me.act - 1].hasOwnProperty("spot") + || !this.act[me.act - 1].spot.hasOwnProperty(spot)) { return false; } @@ -2396,23 +2412,24 @@ const Town = { } for (let i = 0; i < townSpot.length; i += 2) { - //console.debug("moveToSpot: " + spot + " from " + me.x + ", " + me.y); + // console.debug("moveToSpot: " + spot + " from " + me.x + ", " + me.y); if (tkRange) { Pather.moveNear(townSpot[0], townSpot[1], 19); } else if (getDistance(me, townSpot[i], townSpot[i + 1]) > 2) { + if (npcSpot && Game.getNPC(spot)) return true; Pather.moveTo(townSpot[i], townSpot[i + 1], 3, false, true); } switch (spot) { case "stash": - if (!!Game.getObject(sdk.objects.Stash)) { + if (Game.getObject(sdk.objects.Stash)) { return true; } break; case "palace": - if (!!Game.getNPC(NPC.Jerhyn)) { + if (Game.getNPC(NPC.Jerhyn)) { return true; } @@ -2430,14 +2447,14 @@ const Town = { break; case "waypoint": let wp = Game.getObject("waypoint"); - if (!!wp) { + if (wp) { !Skill.haveTK && wp.distance > 5 && Pather.moveToUnit(wp); return true; } break; default: - if (!!Game.getNPC(spot)) { + if (Game.getNPC(spot)) { return true; } From 5283fd2566c3aa3ca1d6dfd35c5632d8ac86b6ed Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 20 Jun 2023 00:20:50 -0400 Subject: [PATCH 165/758] Update Item.js - fix `Item.logger`, during replacment of `Misc.fileAction` the file write got replaced with a read - small bits of refactoring --- d2bs/kolbot/libs/core/Item.js | 235 ++++++++++++++++------------------ 1 file changed, 113 insertions(+), 122 deletions(-) diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index 0aa719e4f..7194a9cd1 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -11,11 +11,16 @@ * @todo fix the max-len warnings + redo majority of this file */ const Item = { + /** @param {number} quality */ qualityToName: function (quality) { let qualNames = ["", "lowquality", "normal", "superior", "magic", "set", "rare", "unique", "crafted"]; return qualNames[quality]; }, + /** + * @param {ItemUnit} unit + * @param {boolean} [type] + */ color: function (unit, type) { type === undefined && (type = true); @@ -50,10 +55,12 @@ const Item = { return "ÿc0"; }, + /** @param {ItemUnit} item */ hasTier: function (item) { return Config.AutoEquip && NTIP.GetTier(item) > 0; }, + /** @param {ItemUnit} item */ canEquip: function (item) { // Not an item or unid if (!item || item.type !== sdk.unittype.Item || !item.identified) return false; @@ -65,7 +72,12 @@ const Item = { return true; }, - // Equips an item and throws away the old equipped item + /** + * Equips an item and throws away the old equipped item + * @param {ItemUnit} item + * @param {number} bodyLoc + * @returns {boolean} + */ equip: function (item, bodyLoc) { if (!this.canEquip(item)) return false; @@ -97,7 +109,7 @@ const Item = { }, getEquippedItem: function (bodyLoc) { - let item = me.getItem(); + let item = me.getItem(-1, sdk.items.mode.Equipped); if (item) { do { @@ -117,87 +129,27 @@ const Item = { }; }, + /** @param {ItemUnit} item */ getBodyLoc: function (item) { - let bodyLoc; + if (!item) return [-1]; + let bodyLoc = item.getBodyLoc(); - switch (item.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.AuricShields: - case sdk.items.type.VoodooHeads: - case sdk.items.type.BowQuiver: - case sdk.items.type.CrossbowQuiver: - bodyLoc = sdk.body.LeftArm; - - break; - case sdk.items.type.Armor: - bodyLoc = sdk.body.Armor; - - break; - case sdk.items.type.Ring: - bodyLoc = [sdk.body.RingRight, sdk.body.RingLeft]; - - break; - case sdk.items.type.Amulet: - bodyLoc = sdk.body.Neck; - - break; - case sdk.items.type.Boots: - bodyLoc = sdk.body.Feet; - - break; - case sdk.items.type.Gloves: - bodyLoc = sdk.body.Gloves; - - break; - case sdk.items.type.Belt: - bodyLoc = sdk.body.Belt; - - break; - case sdk.items.type.Helm: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Circlet: - case sdk.items.type.Pelt: - bodyLoc = sdk.body.Head; - - break; - 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: - bodyLoc = me.barbarian ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - - break; - case sdk.items.type.HandtoHand: - case sdk.items.type.AssassinClaw: - bodyLoc = me.assassin ? [sdk.body.RightArm, sdk.body.LeftArm] : sdk.body.RightArm; - - break; - default: - return false; + if (bodyLoc.first() === sdk.body.RightArm) { + if (me.barbarian) { + if (!item.strictlyTwoHanded) { + return [sdk.body.RightArm, sdk.body.LeftArm]; + } + } else if (me.assassin) { + if ([sdk.items.type.HandtoHand, sdk.items.type.AssassinClaw].includes(item.itemType)) { + return [sdk.body.RightArm, sdk.body.LeftArm]; + } + } } - !Array.isArray(bodyLoc) && (bodyLoc = [bodyLoc]); - return bodyLoc; }, + /** @param {ItemUnit} item */ autoEquipCheck: function (item) { if (!Config.AutoEquip) return true; @@ -224,58 +176,54 @@ const Item = { autoEquip: function () { if (!Config.AutoEquip) return true; - let items = me.findItems(-1, sdk.items.mode.inStorage); - - if (!items) return false; - - function sortEq(a, b) { + function sortEq (a, b) { if (Item.canEquip(a)) return -1; if (Item.canEquip(b)) return 1; return 0; } - me.cancel(); - - // Remove items without tier - for (let i = 0; i < items.length; i += 1) { - if (NTIP.GetTier(items[i]) === 0) { - items.splice(i, 1); + let items = me.getItemsEx(-1, sdk.items.mode.inStorage) + .filter(function (item) { + return NTIP.GetTier(item) > 0; + }); + if (!items.length) return false; - i -= 1; - } - } + me.cancel(); while (items.length > 0) { items.sort(sortEq); let tier = NTIP.GetTier(items[0]); + if ((tier <= 0 || !items[0].isInStorage) && items.shift()) { + continue; + } let bodyLoc = this.getBodyLoc(items[0]); - if (tier > 0 && bodyLoc) { - for (let j = 0; j < bodyLoc.length; j += 1) { - // khalim's will adjustment - const equippedItem = this.getEquippedItem(bodyLoc[j]); - if (items[0].isInStorage - && tier > equippedItem.tier && equippedItem.classid !== sdk.items.quest.KhalimsWill) { - if (!items[0].identified) { - let tome = me.findItem(sdk.items.TomeofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (tome && tome.getStat(sdk.stats.Quantity) > 0) { - items[0].isInStash && Town.openStash(); - Town.identifyItem(items[0], tome); - } - } - - let gid = items[0].gid; - console.log(items[0].name); + for (let loc of bodyLoc) { + // khalim's will adjustment + const equippedItem = this.getEquippedItem(loc); + if (equippedItem.classid === sdk.items.quest.KhalimsWill) { + continue; + } + if (tier > equippedItem.tier) { + if (!items[0].identified) { + let tome = me.getTome(sdk.items.TomeofIdentify); - if (this.equip(items[0], bodyLoc[j])) { - Item.logItem("Equipped", me.getItem(-1, -1, gid)); + if (tome && tome.getStat(sdk.stats.Quantity) > 0) { + items[0].isInStash && Town.openStash(); + Town.identifyItem(items[0], tome); } + } - break; + let gid = items[0].gid; + console.log(items[0].name); + + if (this.equip(items[0], loc)) { + Item.logItem("Equipped", me.getItem(-1, -1, gid)); } + + break; } } @@ -285,6 +233,11 @@ const Item = { return true; }, + /** + * @param {ItemUnit} unit + * @param {boolean} logILvl + * @returns {string} + */ getItemDesc: function (unit, logILvl = true) { let stringColor = ""; let desc = unit.description; @@ -325,6 +278,7 @@ const Item = { return desc; }, + /** @param {ItemUnit} unit */ getItemCode: function (unit) { if (unit === undefined) return ""; @@ -408,18 +362,26 @@ const Item = { if (!code) { // Tiara/Diadem - code = ["ci2", "ci3"].includes(unit.code) ? unit.code : (getBaseStat("items", unit.classid, "normcode") || unit.code); + code = ["ci2", "ci3"].includes(unit.code) + ? unit.code + : (getBaseStat("items", unit.classid, "normcode") || unit.code); code = code.replace(" ", ""); - [sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(unit.itemType) && (code += (unit.gfx + 1)); + [ + sdk.items.type.Ring, sdk.items.type.Amulet, + sdk.items.type.Jewel, sdk.items.type.SmallCharm, + sdk.items.type.LargeCharm, sdk.items.type.GrandCharm + ].includes(unit.itemType) && (code += (unit.gfx + 1)); } return code; }, + /** @param {ItemUnit} unit */ getItemSockets: function (unit) { let code; let sockets = unit.sockets; let subItems = unit.getItemsEx(); + /** @type {ItemUnit[]} */ let tempArray = []; if (subItems.length) { @@ -470,7 +432,11 @@ const Item = { if (tempArray[i]) { code = tempArray[i].code; - if ([sdk.items.type.Ring, sdk.items.type.Amulet, sdk.items.type.Jewel, sdk.items.type.SmallCharm, sdk.items.type.LargeCharm, sdk.items.type.GrandCharm].includes(tempArray[i].itemType)) { + if ([ + sdk.items.type.Ring, sdk.items.type.Amulet, + sdk.items.type.Jewel, sdk.items.type.SmallCharm, + sdk.items.type.LargeCharm, sdk.items.type.GrandCharm + ].includes(tempArray[i].itemType)) { code += (tempArray[i].gfx + 1); } } else { @@ -485,6 +451,11 @@ const Item = { useItemLog: true, // Might be a bit dirty + /** + * @param {string} action + * @param {ItemUnit} unit + * @param {string} text + */ logger: function (action, unit, text) { if (!Config.ItemInfo || !this.useItemLog) return false; @@ -521,10 +492,19 @@ const Item = { break; } - return FileAction.read("logs/ItemLog.txt", dateString + " <" + me.profile + "> <" + action + "> (" + Item.qualityToName(unit.quality) + ") " + desc + (text ? " {" + text + "}" : "") + "\n"); + return FileAction.append( + "logs/ItemLog.txt", + dateString + " <" + me.profile + "> <" + action + "> (" + Item.qualityToName(unit.quality) + ") " + + desc + (text ? " {" + text + "}" : "") + "\n" + ); }, - // Log kept item stats in the manager. + /** + * Log kept item stats in the manager. + * @param {string} action + * @param {ItemUnit} unit + * @param {string} keptLine + */ logItem: function (action, unit, keptLine) { if (!this.useItemLog) return false; if (!Config.LogKeys && ["pk1", "pk2", "pk3"].includes(unit.code)) return false; @@ -579,15 +559,26 @@ const Item = { return true; }, - // skip low items: MuleLogger + /** + * skip low items: MuleLogger + * @param {number} id + */ skipItem: function (id) { return [ - sdk.items.HandAxe, sdk.items.Wand, sdk.items.Club, sdk.items.ShortSword, sdk.items.Javelin, sdk.items.ShortStaff, sdk.items.Katar, - sdk.items.Buckler, sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.RejuvenationPotion, sdk.items.FullRejuvenationPotion, - sdk.items.ThawingPotion, sdk.items.TomeofTownPortal, sdk.items.TomeofIdentify, sdk.items.ScrollofIdentify, sdk.items.ScrollofTownPortal, - sdk.items.Key, 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.HandAxe, sdk.items.Wand, sdk.items.Club, + sdk.items.ShortSword, sdk.items.Javelin, + sdk.items.ShortStaff, sdk.items.Katar, + sdk.items.Buckler, sdk.items.StaminaPotion, + sdk.items.AntidotePotion, sdk.items.RejuvenationPotion, + sdk.items.FullRejuvenationPotion, + sdk.items.ThawingPotion, sdk.items.TomeofTownPortal, + sdk.items.TomeofIdentify, sdk.items.ScrollofIdentify, + sdk.items.ScrollofTownPortal, sdk.items.Key, + 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 ].includes(id); }, }; From a628d43bf927d96ca5d4a00b143f0a6eddd2a865 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:27:45 -0400 Subject: [PATCH 166/758] Update OOG.js - don't parse the msg by default --- d2bs/kolbot/libs/OOG.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 8e1adb869..b20e9431f 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1045,14 +1045,16 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return; } - let obj = JSON.parse(msg); + let obj = null; switch (mode) { case 1: // JoinInfo + obj = JSON.parse(msg); Object.assign(Starter.joinInfo, obj); break; case 2: // Game info + obj = JSON.parse(msg); Object.assign(Starter.gameInfo, obj); break; @@ -1066,6 +1068,7 @@ includeIfNotIncluded("oog/D2Bot.js"); // required } if (Starter.gameInfo.hasOwnProperty("gameName")) { + obj = JSON.parse(msg); console.debug("Recieved Game Request :: ", obj.profile); if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(Profile().type)) { @@ -1096,11 +1099,13 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; case 61732: // Cached info retreival + obj = JSON.parse(msg); msg !== "null" && (Starter.gameInfo.crashInfo = obj); break; case 1638: // getProfile try { + obj = JSON.parse(msg); Starter.profileInfo.profile = me.profile; Starter.profileInfo.account = obj.account; Starter.profileInfo.charName = obj.Character; From 456a23dec8f0636eca7a0f6d4867ba76215690da Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:02:39 -0400 Subject: [PATCH 167/758] Update Duriel.js - fix attempt to use lair entrance twice --- d2bs/kolbot/libs/scripts/Duriel.js | 48 +++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index 6a876911b..284c37925 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -7,10 +7,14 @@ function Duriel () { const killDuriel = function () { - let target = Misc.poll(() => Game.getMonster(sdk.monsters.Duriel), 1000, 200); + let target = Misc.poll(function () { + return Game.getMonster(sdk.monsters.Duriel); + }, 1000, 200); if (!target) throw new Error("Duriel not found."); - Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Duriel); + if (Config.MFLeader && Pather.makePortal()) { + say("kill " + sdk.monsters.Duriel); + } for (let i = 0; i < 300 && target.attackable; i += 1) { ClassAttack.doAttack(target); @@ -27,31 +31,47 @@ function Duriel () { Precast.doPrecast(true); - if (!Pather.moveToExit(getRoom().correcttomb, true)) throw new Error("Failed to move to Tal Rasha's Tomb"); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder, -11, 3)) { + if (!Pather.moveToExit(getRoom().correcttomb, true)) { + throw new Error("Failed to move to Tal Rasha's Tomb"); + } + /** @type {ObjectUnit} */ + let lairEntrance = null; + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricStaffHolder, + { offX: -11, offY: 3, callback: function () { + lairEntrance = Game.getObject(sdk.objects.PortaltoDurielsLair); + return lairEntrance && lairEntrance.distance < 20; + } })) { throw new Error("Failed to move to Orifice"); } - me.hardcore && !me.sorceress && Attack.clear(5); - - let unit = Game.getObject(sdk.objects.PortaltoDurielsLair); - - if (Skill.useTK(unit)) { + // me.hardcore && !me.sorceress && Attack.clear(5); + if (lairEntrance && Skill.useTK(lairEntrance)) { + if (lairEntrance.distance > 20) { + Attack.getIntoPosition(lairEntrance, 20, sdk.collision.LineOfSight); + } Misc.poll(function () { - Packet.telekinesis(unit) && delay(100); + Packet.telekinesis(lairEntrance) && delay(100); return me.inArea(sdk.areas.DurielsLair); }, 1000, 200); } + let [type, id, target] = [ + 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)) { + && !Pather.useUnit(type, id, target)) { Attack.clear(10); - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); + Pather.useUnit(type, id, target); } - if (!me.inArea(sdk.areas.DurielsLair)) throw new Error("Failed to move to Duriel"); + if (!me.inArea(sdk.areas.DurielsLair)) { + throw new Error("Failed to move to Duriel"); + } - me.sorceress && me.classic ? killDuriel() : Attack.kill(sdk.monsters.Duriel); + me.sorceress && me.classic + ? killDuriel() + : Attack.kill(sdk.monsters.Duriel); Pickit.pickItems(); return true; From ecdeacfab44569d61acf23c71456f19678b0ee28 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 28 Jun 2023 20:05:09 -0400 Subject: [PATCH 168/758] Update Prototypes.js - alot of cleanup - removed most of the __defineGetters in favor of Object.defineProperties - added charclass prop, returns the charclass of an item if it has a specific one or your own classid which is a bit hacky but easier to work with than the 255 value it returned otherwise - fixed isOnMain to correctly return slot 1 as main for checks - added openUnit and useUnit prototypes for ObjectUnits and stairs/tiles. Want to use this more and refactor pather at some point --- d2bs/kolbot/libs/core/Prototypes.js | 454 +++++++++++++++++++--------- 1 file changed, 308 insertions(+), 146 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 204c9ab92..59fdf57b9 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -393,6 +393,61 @@ Object.defineProperties(Unit.prototype, { * @extends ItemUnit */ Object.defineProperties(Unit.prototype, { + strreq: { + /** @this {ItemUnit} */ + get: function () { + if (this.type !== sdk.unittype.Item) return false; + let ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + let baseReq = getBaseStat("items", this.classid, "reqstr"); + let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); + + return Math.max(finalReq, 0); + } + }, + dexreq: { + /** @this {ItemUnit} */ + get: function () { + if (this.type !== sdk.unittype.Item) return false; + let ethereal = this.getFlag(sdk.items.flags.Ethereal); + let reqModifier = this.getStat(sdk.stats.ReqPercent); + let baseReq = getBaseStat("items", this.classid, "reqdex"); + let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); + + return Math.max(finalReq, 0); + } + }, + parentName: { + /** @this {ItemUnit} */ + get: function () { + if (this.type !== sdk.unittype.Item) return false; + let parent = this.getParent(); + + return parent ? parent.name : false; + } + }, + itemclass: { + /** @this {ItemUnit} */ + get: function () { + if (this.type !== sdk.unittype.Item) return false; + const itemCode = getBaseStat("items", this.classid, "code"); + if (itemCode === undefined) return 0; + if (itemCode === getBaseStat(0, this.classid, "ultracode")) return 2; + if (itemCode === getBaseStat(0, this.classid, "ubercode")) return 1; + + return 0; + } + }, + charclass: { + /** @this {ItemUnit} */ + get: function () { + if (this.type !== sdk.unittype.Item) return false; + let charclass = getBaseStat("items", this.classid, "charclass"); + // hacky? Essentially just using this to check if we can use the item and if the item doesn't have a specific + // class requirement, we'll just assume it's for our class. As this makes the actualy checks easy + return charclass === 255 ? me.classid : charclass; + } + }, isEquipped: { get: function () { if (this.type !== sdk.unittype.Item) return false; @@ -438,21 +493,29 @@ Object.defineProperties(Unit.prototype, { } }, isOnMain: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; - return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); + switch (me.weaponswitch) { + case sdk.player.slot.Secondary: + return [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(this.bodylocation); + case sdk.player.slot.Main: + default: + return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); + } } }, isOnSwap: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item || this.location !== sdk.storage.Equipped) return false; switch (me.weaponswitch) { case sdk.player.slot.Main: return [sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(this.bodylocation); case sdk.player.slot.Secondary: + default: return [sdk.body.RightArm, sdk.body.LeftArm].includes(this.bodylocation); } - return false; } }, identified: { @@ -622,7 +685,12 @@ Object.defineProperties(Unit.prototype, { }, }); -// Open NPC menu +/** + * Open NPC menu + * @this {NPCUnit} + * @param {number} [addDelay] + * @returns {boolean} + */ Unit.prototype.openMenu = function (addDelay) { if (Config.PacketShopping) return Packet.openMenu(this); if (this.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); @@ -632,7 +700,9 @@ Unit.prototype.openMenu = function (addDelay) { let pingDelay = (me.gameReady ? me.ping : 125); for (let i = 0; i < 5; i += 1) { - getDistance(me, this) > 4 && Pather.moveToUnit(this); + if (getDistance(me, this) > 4) { + Pather.moveNearUnit(this, 4); + } Misc.click(0, 0, this); let tick = getTickCount(); @@ -661,14 +731,22 @@ Unit.prototype.openMenu = function (addDelay) { return false; }; -// mode = "Gamble", "Repair" or "Shop" +/** + * @this {NPCUnit} + * @param {string} mode "Gamble", "Repair" or "Shop" + * @returns {boolean} + */ Unit.prototype.startTrade = function (mode) { if (Config.PacketShopping) return Packet.startTrade(this, mode); if (this.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); console.log("Starting " + mode + " at " + this.name); if (getUIFlag(sdk.uiflags.Shop)) return true; - let menuId = mode === "Gamble" ? sdk.menu.Gamble : mode === "Repair" ? sdk.menu.TradeRepair : sdk.menu.Trade; + let menuId = mode === "Gamble" + ? sdk.menu.Gamble + : mode === "Repair" + ? sdk.menu.TradeRepair + : sdk.menu.Trade; for (let i = 0; i < 3; i += 1) { // Incremental delay on retries @@ -751,16 +829,6 @@ Unit.prototype.buy = function (shiftBuy, gamble) { return false; }; -// Item owner name -Unit.prototype.__defineGetter__("parentName", - function () { - if (this.type !== sdk.unittype.Item) throw new Error("Unit.parentName: Must be used with item units."); - - let parent = this.getParent(); - - return parent ? parent.name : false; - }); - // You MUST use a delay after Unit.sell() if using custom scripts. delay(500) works best, dynamic delay is used when identifying/selling (500 - item id time) Unit.prototype.sell = function () { if (Config.PacketShopping) return Packet.sellItem(this); @@ -1134,7 +1202,7 @@ Unit.prototype.haveSome = function (itemInfo = []) { /** * @description Return the items of a player, or an empty array * @param args - * @returns Unit[] + * @returns {ItemUnit[]} */ Unit.prototype.getItems = function (...args) { let items = []; @@ -1220,35 +1288,6 @@ Unit.prototype.getSuffix = function (id) { return false; }; -Unit.prototype.__defineGetter__("dexreq", - function () { - let ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - let baseReq = getBaseStat("items", this.classid, "reqdex"); - let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - - return Math.max(finalReq, 0); - }); - -Unit.prototype.__defineGetter__("strreq", - function () { - let ethereal = this.getFlag(sdk.items.flags.Ethereal); - let reqModifier = this.getStat(sdk.stats.ReqPercent); - let baseReq = getBaseStat("items", this.classid, "reqstr"); - let finalReq = baseReq + Math.floor(baseReq * reqModifier / 100) - (ethereal ? 10 : 0); - - return Math.max(finalReq, 0); - }); - -Unit.prototype.__defineGetter__("itemclass", - function () { - if (getBaseStat("items", this.classid, "code") === undefined) return 0; - if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ultracode")) return 2; - if (getBaseStat("items", this.classid, "code") === getBaseStat(0, this.classid, "ubercode")) return 1; - - return 0; - }); - Unit.prototype.getStatEx = function (id, subid) { let temp, rval, regex; @@ -1821,16 +1860,17 @@ Unit.prototype.getColor = function () { }; /** - * @description Used upon item units like ArachnidMesh.castChargedSkill([skillId]) or directly on the "me" unit me.castChargedSkill(278); - * @param {int} skillId = undefined - * @param {int} x = undefined - * @param {int} y = undefined - * @return boolean + * @description Used upon item units like ArachnidMesh.castChargedSkill([skillId]) + * or directly on the "me" unit me.castChargedSkill(278); + * @param {number} skillId + * @param {number} x + * @param {number} y + * @returns {boolean} * @throws Error */ Unit.prototype.castChargedSkill = function (...args) { let skillId, x, y, unit, chargedItem, charge; - let chargedItems = []; + /** @param {Charge} itemCharge */ let validCharge = function (itemCharge) { return itemCharge.skill === skillId && itemCharge.charges; }; @@ -1879,37 +1919,21 @@ Unit.prototype.castChargedSkill = function (...args) { // Called the function the unit, me. if (this === me) { if (!skillId) throw Error("Must supply skillId on me.castChargedSkill"); + if (!Skill.charges.length || !Skill.charges.some(validCharge)) { + // only rebuild list if we are unsure if we have the skill + Skill.getCharges(); + } + if (!Skill.charges.length) return false; + let chargedItems = Skill.charges.filter(validCharge); - chargedItems = []; - - // Item must be equipped, or a charm in inventory - this.getItemsEx(-1) - .filter(item => item && (item.isEquipped || (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 - }); - } - } - } - }); - - if (chargedItems.length === 0) throw Error("Don't have the charged skill (" + skillId + "), or not enough charges"); - - chargedItem = chargedItems.sort((a, b) => a.charge.level - b.charge.level).first().item; + if (chargedItems.length === 0) { + throw Error("Don't have the charged skill (" + skillId + "), or not enough charges"); + } + chargedItem = chargedItems + .sort(function (a, b) { + return b.charge.level - a.charge.level; + }).first().unit; return chargedItem.castChargedSkill.apply(chargedItem, args); } else if (this.type === sdk.unittype.Item) { charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates @@ -1928,15 +1952,27 @@ Unit.prototype.castChargedSkill = function (...args) { if (charge) { // Setting skill on hand if (!Config.PacketCasting || Config.PacketCasting === 1 && skillId !== sdk.skills.Teleport) { - return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); // Non packet casting + // Non packet casting + return Skill.cast(skillId, sdk.skills.hand.Right, x || me.x, y || me.y, this); } // Packet casting - sendPacket(1, sdk.packets.send.SelectSkill, 2, charge.skill, 1, 0x0, 1, 0x00, 4, this.gid); + // Setting skill on hand + new PacketBuilder() + .byte(sdk.packets.send.SelectSkill) + .word(charge.skill) + .byte(0x00) + .byte(0x00) + .dword(this.gid) + .send(); // 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(); // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked - sendPacket(1, sdk.packets.send.RightSkillOnLocation, 2, x || me.x, 2, y || me.y); // Cast the skill return true; } @@ -1946,11 +1982,16 @@ Unit.prototype.castChargedSkill = function (...args) { }; /** + * @this {ItemUnit} * @description equip an item. + * @param {number | number[]} [destLocation] */ -Unit.prototype.equip = function (destLocation = undefined) { +Unit.prototype.equip = function (destLocation) { if (this.isEquipped) return true; // Item already equiped + /** @type {ItemUnit} */ + const _self = this; + /** @param {ItemUnit} */ const findspot = function (item) { let tempspot = Storage.Stash.FindSpot(item); @@ -1962,38 +2003,33 @@ Unit.prototype.equip = function (destLocation = undefined) { return tempspot ? { location: Storage.Inventory.location, coord: tempspot } : false; }; - const doubleHanded = [ - sdk.items.type.Staff, sdk.items.type.Bow, sdk.items.type.Polearm, sdk.items.type.Crossbow, - sdk.items.type.HandtoHand, sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear - ]; // Not an item, or unidentified, or not enough stats - if (this.type !== sdk.unittype.Item || !this.getFlag(sdk.items.flags.Identified) - || this.getStat(sdk.stats.LevelReq) > me.getStat(sdk.stats.Level) - || this.dexreq > me.getStat(sdk.stats.Dexterity) - || this.strreq > me.getStat(sdk.stats.Strength)) { + if (_self.type !== sdk.unittype.Item || !_self.identified + || _self.lvlreq > me.getStat(sdk.stats.Level) + || _self.dexreq > me.getStat(sdk.stats.Dexterity) + || _self.strreq > me.getStat(sdk.stats.Strength)) { return false; } // If not a specific location is given, figure it out (can be useful to equip a double weapon) - !destLocation && (destLocation = this.getBodyLoc()); + !destLocation && (destLocation = _self.getBodyLoc()); // If destLocation isnt an array, make it one !Array.isArray(destLocation) && (destLocation = [destLocation]); - console.log("equiping " + this.name + " to bodylocation: " + destLocation.first()); + console.log("equiping " + _self.prettyPrint + " to bodylocation: " + destLocation.first()); - let currentEquiped = me.getItemsEx(-1).filter(item => - destLocation.indexOf(item.bodylocation) !== -1 - || ( // Deal with double handed weapons - - (item.isOnMain) - && [sdk.body.RightArm, sdk.body.LeftArm].indexOf(destLocation) // in case destination is on the weapon/shield slot - && ( - doubleHanded.indexOf(this.itemType) !== -1 // this item is a double handed item - || doubleHanded.indexOf(item.itemType) !== -1 // current item is a double handed item - ) - ) - ).sort((a, b) => b - a); // shields first + let currentEquiped = me.getItemsEx(-1) + .filter(function (item) { + return (destLocation.includes(item.bodylocation) + || ( item.isOnMain// Deal with double handed weapons + && [sdk.body.RightArm, sdk.body.LeftArm].indexOf(destLocation) // in case destination is on the weapon/shield slot + && (item.strictlyTwoHanded || _self.strictlyTwoHanded) // one of the items is strictly two handed + ) + ); + }).sort(function (a, b) { + return b - a; + }); // shields first // if nothing is equipped at the moment, just equip it if (!currentEquiped.length) { @@ -2001,12 +2037,12 @@ Unit.prototype.equip = function (destLocation = undefined) { clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); } else { // unequip / swap items - currentEquiped.forEach((item, index) => { + currentEquiped.forEach(function (item, index) { // Last item, so swap instead of putting off first if (index === (currentEquiped.length - 1)) { - print("swap " + this.name + " for " + item.name); - let oldLoc = { x: this.x, y: this.y, location: this.location }; - clickItemAndWait(sdk.clicktypes.click.item.Left, this); // Pick up current item + print("swap " + _self.name + " for " + item.name); + let oldLoc = { x: _self.x, y: _self.y, location: _self.location }; + clickItemAndWait(sdk.clicktypes.click.item.Left, _self); // Pick up current item clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // the swap of items // Find a spot for the current item let spot = findspot(item); @@ -2040,50 +2076,60 @@ Unit.prototype.equip = function (destLocation = undefined) { }; }; +/** + * @this {ItemUnit} + * @returns {number[]} + */ Unit.prototype.getBodyLoc = function () { - const types = {}; - types[sdk.body.Head] = [sdk.items.type.Helm, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]; // helm - types[sdk.body.Neck] = [sdk.items.type.Amulet]; // amulet - types[sdk.body.Armor] = [sdk.items.type.Armor]; // armor - types[sdk.body.RightArm] = [ - 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.HandtoHand, sdk.items.type.Orb, - sdk.items.type.AmazonBow, sdk.items.type.AmazonSpear, sdk.items.type.AmazonJavelin, sdk.items.type.AssassinClaw - ]; // weapons - types[sdk.body.LeftArm] = [ - sdk.items.type.Shield, sdk.items.type.BowQuiver, - sdk.items.type.CrossbowQuiver, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads - ], // shields / Arrows / bolts - types[sdk.body.RingRight] = [sdk.items.type.Ring]; // ring slot 1 - types[sdk.body.RingLeft] = [sdk.items.type.Ring]; // ring slot 2 - types[sdk.body.Belt] = [sdk.items.type.Belt]; // belt - types[sdk.body.Feet] = [sdk.items.type.Boots]; // boots - types[sdk.body.Gloves] = [sdk.items.type.Gloves]; // gloves - //types[sdk.body.RightArmSecondary] = types[sdk.body.RightArm]; - //types[sdk.body.LeftArmSecondary] = types[sdk.body.LeftArm]; + const _types = new Map([ + [sdk.body.Head, [sdk.items.type.Helm, sdk.items.type.Pelt, sdk.items.type.PrimalHelm]], + [sdk.body.Neck, [sdk.items.type.Amulet]], + [sdk.body.Armor, [sdk.items.type.Armor]], + [sdk.body.RightArm, [ + 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.Javelin, sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, + sdk.items.type.MissilePotion, sdk.items.type.Javelin, sdk.items.type.Orb, + sdk.items.type.HandtoHand, sdk.items.type.AmazonBow, + sdk.items.type.AmazonSpear + ]], // right arm + [sdk.body.LeftArm, [ + sdk.items.type.Shield, sdk.items.type.BowQuiver, + sdk.items.type.CrossbowQuiver, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads + ]], // left arm + [sdk.body.RingRight, [sdk.items.type.Ring]], + [sdk.body.RingLeft, [sdk.items.type.Ring]], + [sdk.body.Belt, [sdk.items.type.Belt]], + [sdk.body.Feet, [sdk.items.type.Boots]], + [sdk.body.Gloves, [sdk.items.type.Gloves]], + ]); let bodyLoc = []; - - for (let i in types) { - this.itemType && types[i].indexOf(this.itemType) !== -1 && bodyLoc.push(i); + + for (let [key, value] of _types) { + if (value.includes(this.itemType)) { + bodyLoc.push(key); + } } - // Strings are hard to calculate with, parse to int - return bodyLoc.map(parseInt); + return bodyLoc; }; Unit.prototype.getRes = function (type, difficulty) { if (!type) return -1; - if (![sdk.stats.FireResist, sdk.stats.ColdResist, sdk.stats.PoisonResist, sdk.stats.LightningResist].includes(type)) { + if (![ + sdk.stats.FireResist, sdk.stats.ColdResist, + sdk.stats.PoisonResist, sdk.stats.LightningResist + ].includes(type)) { return -1; } difficulty === undefined || difficulty < 0 && (difficulty = 0); difficulty > 2 && (difficulty = 2); - let modifier = me.classic ? [0, 20, 50][difficulty] : [0, 40, 100][difficulty]; + let modifier = me.classic + ? [0, 20, 50][difficulty] + : [0, 40, 100][difficulty]; if (this === me) { switch (type) { case sdk.stats.FireResist: @@ -2167,9 +2213,12 @@ Unit.prototype.hasEnchant = function (...enchants) { Unit.prototype.usingShield = function () { if (this.type > sdk.unittype.Monster) return false; // always switch to main hand if we are checking ourselves - this === me && me.weaponswitch !== sdk.player.slot.Main && me.switchWeapons(sdk.player.slot.Main); - let shield = this.getItemsEx(-1, sdk.items.mode.Equipped).filter(s => s.isShield).first(); - return !!shield; + if (this === me && me.weaponswitch !== sdk.player.slot.Main) { + me.switchWeapons(sdk.player.slot.Main); + } + return this.getItemsEx(-1, sdk.items.mode.Equipped) + .filter(s => s.isShield) + .first(); }; // something in here is causing demon imps in barricade towers to be skipped - todo: figure out what @@ -2211,7 +2260,7 @@ Unit.prototype.__defineGetter__("attackable", function () { }); Object.defineProperty(Unit.prototype, "curseable", { - /** @this {Unit} */ + /** @this {Player | Monster} */ get: function () { // must be player or monster if (this === undefined || !copyUnit(this).x || this.type > 1) return false; @@ -2333,3 +2382,116 @@ PresetUnit.prototype.realCoords = function () { y: this.roomy * 5 + this.y, }; }; + +Unit.prototype.openUnit = function () { + if (this === undefined) return false; + if (this.type !== sdk.unittype.Object && this.type !== sdk.unittype.Stairs) { + return false; + } + if (this.mode !== sdk.objects.mode.Inactive) return true; + + for (let i = 0; i < 3; i += 1) { + let usetk = (i < 2 && Skill.useTK(this)); + + if (this.distance > 5) { + Pather.moveNearUnit(this, (usetk ? 20 : 5) - i); + } + + delay(300); + // try to activate it once + if (usetk && i === 0 && this.distance < 21) { + Packet.telekinesis(this); + } else { + Packet.entityInteract(this); + } + + const _self = this; + if (Misc.poll(function () { + return _self.mode !== sdk.objects.mode.Inactive; + }, 2000, 60)) { + delay(100); + + return true; + } + + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); + !!coord && Pather.moveTo(coord.x, coord.y); + } + + return false; +}; + +Unit.prototype.useUnit = function (targetArea) { + if (this === undefined) return false; + if (this.type !== sdk.unittype.Object && this.type !== sdk.unittype.Stairs) { + return false; + } + const preArea = me.area; + + MainLoop: + for (let i = 0; i < 5; i += 1) { + let usetk = (i < 2 && Skill.useTK(this)); + + if (this.distance > 5) { + Pather.moveNearUnit(this, (usetk ? 20 : 5)); + // try to activate it once + if (usetk && i === 0 && this.mode === sdk.objects.mode.Inactive && this.distance < 21) { + Packet.telekinesis(this); + } + } + + if (this.type === sdk.unittype.Object && this.mode === sdk.objects.mode.Inactive) { + if (me.inArea(sdk.areas.Travincal) && targetArea === sdk.areas.DuranceofHateLvl1) { + if (!me.blackendTemple) { + throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); + } + } else if (me.inArea(sdk.areas.ArreatSummit) && targetArea === sdk.areas.WorldstoneLvl1) { + if (!me.ancients) { + throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); + } + } + + me.inArea(sdk.areas.A3SewersLvl1) + ? Pather.openUnit(sdk.unittype.Object, sdk.objects.SewerLever) + : this.openUnit(); + } + + if (this.type === sdk.unittype.Object + && this.classid === sdk.objects.RedPortalToAct4 + && me.inArea(sdk.areas.DuranceofHateLvl3) + && targetArea === sdk.areas.PandemoniumFortress + && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { + throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); + } + + delay(300); + this.type === sdk.unittype.Stairs + ? Misc.click(0, 0, this) + : usetk && this.distance > 5 + ? Packet.telekinesis(this) + : Packet.entityInteract(this); + delay(300); + + let tick = getTickCount(); + + while (getTickCount() - tick < 3000) { + if ((!targetArea && me.area !== preArea) || me.area === targetArea) { + delay(200); + + break MainLoop; + } + + delay(10); + } + + i > 2 && Packet.flash(me.gid); + let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); + !!coord && Pather.moveTo(coord.x, coord.y); + } + + while (!me.idle && !me.gameReady) { + delay(40); + } + + return targetArea ? me.area === targetArea : me.area !== preArea; +}; From 52f767cf50a5e9d850331416c7a0a44046106c4c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:35:55 -0400 Subject: [PATCH 169/758] handle threads that don't have a main function - ty jaenster for this --- d2bs/kolbot/libs/modules/Guard.js | 16 +++++++++++----- d2bs/kolbot/libs/modules/Team.js | 23 ++++++++++++++--------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/d2bs/kolbot/libs/modules/Guard.js b/d2bs/kolbot/libs/modules/Guard.js index 0fcd11e6a..ccec68235 100644 --- a/d2bs/kolbot/libs/modules/Guard.js +++ b/d2bs/kolbot/libs/modules/Guard.js @@ -1,4 +1,4 @@ -(function (module, require, thread) { +(function (module, require, thread, globalThis) { "use strict"; const Messaging = require("./Messaging"); const Worker = require("./Worker"); @@ -22,7 +22,7 @@ * @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 = () => { @@ -66,12 +66,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 } }); @@ -89,5 +94,6 @@ null, typeof module === "object" && module || {}, typeof require === "undefined" && (include("require.js") && require) || require, - getScript.startAsThread() + getScript.startAsThread(), + [].filter.constructor("return this")() ); diff --git a/d2bs/kolbot/libs/modules/Team.js b/d2bs/kolbot/libs/modules/Team.js index 2624d773b..773df9db5 100644 --- a/d2bs/kolbot/libs/modules/Team.js +++ b/d2bs/kolbot/libs/modules/Team.js @@ -5,8 +5,7 @@ */ !isIncluded("require.js") && include("require.js"); // load the require.js -(function (threadType) { - +(function (threadType, globalThis) { const others = []; const myEvents = new (require("Events")); @@ -43,7 +42,10 @@ print("ÿc2Kolbotÿc0 :: Team thread started"); Messaging.on("Team", data => ( - typeof data === "object" && data && data.hasOwnProperty("call") && Team[data.call].apply(Team, data.hasOwnProperty("args") && data.args || []) + typeof data === "object" && data + && data.hasOwnProperty("call") + && Team[data.call].apply(Team, data.hasOwnProperty("args") + && data.args || []) )); Worker.runInBackground.copydata = (new function () { @@ -121,10 +123,15 @@ }; }).update; - while (true) { - delay(1000); - } + let quiting = false; + addEventListener("scriptmsg", data => data === "quit" && (quiting = true)); + // eslint-disable-next-line dot-notation + globalThis["main"] = function () { + while (!quiting) delay(3); + //@ts-ignore + getScript(true).stop(); + }; } else { (function (module) { const localTeam = module.exports = Team; // <-- some get overridden, but this still works for auto completion in your IDE @@ -162,6 +169,4 @@ ); })(module); } - - -})(getScript.startAsThread()); +})(getScript.startAsThread(), [].filter.constructor("return this")()); From 154160795ab4f865911025b460bac3bf067867e0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 30 Jun 2023 15:27:53 -0400 Subject: [PATCH 170/758] Update Town.js - refactored Town.initialize. The only town with a dynamic map is a1 so the others can be statically defined - changed it from an array to an object as it was pointless to always have to do me.act -1 - Atma usage should be fixed now and better move to npc movement. Added a callback into pather move so we should no longer run past our wanted npc - Fixed returning from Town.move after using backdoor to atma's tavern --- d2bs/kolbot/libs/core/Town.js | 233 +++++++++++++++++++--------------- 1 file changed, 128 insertions(+), 105 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 16bce399d..d327094d3 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -2199,96 +2199,115 @@ const Town = { return true; }, - - /** - * @todo figure out how to typedef this. - */ - act: [{}, {}, {}, {}, {}], + + act: { + 1: { + spot: (function () { + const _spot = {}; + _spot.stash = [0, 0]; + _spot[NPC.Warriv] = [0, 0]; + _spot[NPC.Cain] = [0, 0]; + _spot[NPC.Kashya] = [0, 0]; + _spot[NPC.Akara] = [0, 0]; + _spot[NPC.Charsi] = [0, 0]; + _spot[NPC.Gheed] = [0, 0]; + _spot.portalspot = [0, 0]; + _spot.waypoint = [0, 0]; + _spot.initialized = false; + return _spot; + })(), + }, + 2: { + spot: (function () { + const _spot = {}; + _spot[NPC.Fara] = [5124, 5082]; + _spot[NPC.Cain] = [5124, 5082]; + _spot[NPC.Lysander] = [5118, 5104]; + _spot[NPC.Greiz] = [5033, 5053]; + _spot[NPC.Elzix] = [5032, 5102]; + _spot[NPC.Jerhyn] = [5088, 5153]; + _spot[NPC.Meshif] = [5205, 5058]; + _spot[NPC.Drognan] = [5097, 5035]; + _spot[NPC.Atma] = [5137, 5060]; + _spot[NPC.Warriv] = [5152, 5201]; + _spot.palace = [5088, 5153]; + _spot.sewers = [5221, 5181]; + _spot.portalspot = [5168, 5060]; + _spot.stash = [5124, 5076]; + _spot.waypoint = [5070, 5083]; + _spot.initialized = true; + return _spot; + })(), + }, + 3: { + spot: (function () { + const _spot = {}; + _spot[NPC.Meshif] = [5118, 5168]; + _spot[NPC.Hratli] = [5223, 5048, 5127, 5172]; + _spot[NPC.Ormus] = [5129, 5093]; + _spot[NPC.Asheara] = [5043, 5093]; + _spot[NPC.Alkor] = [5083, 5016]; + _spot[NPC.Cain] = [5148, 5066]; + _spot.stash = [5144, 5059]; + _spot.portalspot = [5150, 5063]; + _spot.waypoint = [5158, 5050]; + _spot.initialized = true; + return _spot; + })(), + }, + 4: { + spot: (function () { + const _spot = {}; + _spot[NPC.Cain] = [5027, 5027]; + _spot[NPC.Halbu] = [5089, 5031]; + _spot[NPC.Tyrael] = [5027, 5027]; + _spot[NPC.Jamella] = [5088, 5054]; + _spot.stash = [5022, 5040]; + _spot.portalspot = [5045, 5042]; + _spot.waypoint = [5043, 5018]; + _spot.initialized = true; + return _spot; + })(), + }, + 5: { + spot: (function () { + const _spot = {}; + _spot[NPC.Larzuk] = [5141, 5045]; + _spot[NPC.Malah] = [5078, 5029]; + _spot[NPC.Cain] = [5119, 5061]; + _spot[NPC.Qual_Kehk] = [5066, 5083]; + _spot[NPC.Anya] = [5112, 5120]; + _spot[NPC.Nihlathak] = [5071, 5111]; + _spot.stash = [5129, 5061]; + _spot.portalspot = [5098, 5019]; + _spot.portal = [5118, 5120]; + _spot.waypoint = [5113, 5068]; + _spot.initialized = true; + return _spot; + })(), + } + }, initialize: function () { - //console.log("Initialize town " + me.act); - - switch (me.act) { - case 1: + // console.log("Initialize town " + me.act); + if (!this.act[me.act].spot.initialized && me.act === 1) { + // act 1 is the only act that needs to be initialized let wp = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1Waypoint); let fireUnit = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1TownFire); if (!fireUnit) return false; let fire = [fireUnit.roomx * 5 + fireUnit.x, fireUnit.roomy * 5 + fireUnit.y]; - - this.act[0].spot = {}; - this.act[0].spot.stash = [fire[0] - 7, fire[1] - 12]; - this.act[0].spot[NPC.Warriv] = [fire[0] - 5, fire[1] - 2]; - this.act[0].spot[NPC.Cain] = [fire[0] + 6, fire[1] - 5]; - this.act[0].spot[NPC.Kashya] = [fire[0] + 14, fire[1] - 4]; - this.act[0].spot[NPC.Akara] = [fire[0] + 56, fire[1] - 30]; - this.act[0].spot[NPC.Charsi] = [fire[0] - 39, fire[1] - 25]; - this.act[0].spot[NPC.Gheed] = [fire[0] - 34, fire[1] + 36]; - this.act[0].spot.portalspot = [fire[0] + 10, fire[1] + 18]; - this.act[0].spot.waypoint = [wp.roomx * 5 + wp.x, wp.roomy * 5 + wp.y]; - this.act[0].initialized = true; - - break; - case 2: - this.act[1].spot = {}; - this.act[1].spot[NPC.Fara] = [5124, 5082]; - this.act[1].spot[NPC.Cain] = [5124, 5082]; - this.act[1].spot[NPC.Lysander] = [5118, 5104]; - this.act[1].spot[NPC.Greiz] = [5033, 5053]; - this.act[1].spot[NPC.Elzix] = [5032, 5102]; - this.act[1].spot.palace = [5088, 5153]; - this.act[1].spot.sewers = [5221, 5181]; - this.act[1].spot[NPC.Meshif] = [5205, 5058]; - this.act[1].spot[NPC.Drognan] = [5097, 5035]; - this.act[1].spot[NPC.Atma] = [5137, 5060]; - this.act[1].spot[NPC.Warriv] = [5152, 5201]; - this.act[1].spot.portalspot = [5168, 5060]; - this.act[1].spot.stash = [5124, 5076]; - this.act[1].spot.waypoint = [5070, 5083]; + this.act[1].spot.stash = [fire[0] - 7, fire[1] - 12]; + this.act[1].spot.stash = [fire[0] - 7, fire[1] - 12]; + this.act[1].spot[NPC.Warriv] = [fire[0] - 5, fire[1] - 2]; + this.act[1].spot[NPC.Cain] = [fire[0] + 6, fire[1] - 5]; + this.act[1].spot[NPC.Kashya] = [fire[0] + 14, fire[1] - 4]; + this.act[1].spot[NPC.Akara] = [fire[0] + 56, fire[1] - 30]; + this.act[1].spot[NPC.Charsi] = [fire[0] - 39, fire[1] - 25]; + this.act[1].spot[NPC.Gheed] = [fire[0] - 34, fire[1] + 36]; + this.act[1].spot.portalspot = [fire[0] + 10, fire[1] + 18]; + this.act[1].spot.waypoint = [wp.roomx * 5 + wp.x, wp.roomy * 5 + wp.y]; this.act[1].initialized = true; - - break; - case 3: - this.act[2].spot = {}; - this.act[2].spot[NPC.Meshif] = [5118, 5168]; - this.act[2].spot[NPC.Hratli] = [5223, 5048, 5127, 5172]; - this.act[2].spot[NPC.Ormus] = [5129, 5093]; - this.act[2].spot[NPC.Asheara] = [5043, 5093]; - this.act[2].spot[NPC.Alkor] = [5083, 5016]; - this.act[2].spot[NPC.Cain] = [5148, 5066]; - this.act[2].spot.stash = [5144, 5059]; - this.act[2].spot.portalspot = [5150, 5063]; - this.act[2].spot.waypoint = [5158, 5050]; - this.act[2].initialized = true; - - break; - case 4: - this.act[3].spot = {}; - this.act[3].spot[NPC.Cain] = [5027, 5027]; - this.act[3].spot[NPC.Halbu] = [5089, 5031]; - this.act[3].spot[NPC.Tyrael] = [5027, 5027]; - this.act[3].spot[NPC.Jamella] = [5088, 5054]; - this.act[3].spot.stash = [5022, 5040]; - this.act[3].spot.portalspot = [5045, 5042]; - this.act[3].spot.waypoint = [5043, 5018]; - this.act[3].initialized = true; - - break; - case 5: - this.act[4].spot = {}; - this.act[4].spot.portalspot = [5098, 5019]; - this.act[4].spot.stash = [5129, 5061]; - this.act[4].spot[NPC.Larzuk] = [5141, 5045]; - this.act[4].spot[NPC.Malah] = [5078, 5029]; - this.act[4].spot[NPC.Cain] = [5119, 5061]; - this.act[4].spot[NPC.Qual_Kehk] = [5066, 5083]; - this.act[4].spot[NPC.Anya] = [5112, 5120]; - this.act[4].spot.portal = [5118, 5120]; - this.act[4].spot.waypoint = [5113, 5068]; - this.act[4].spot[NPC.Nihlathak] = [5071, 5111]; - this.act[4].initialized = true; - - break; } return true; @@ -2300,7 +2319,7 @@ const Town = { */ getDistance: function (spot = "") { !me.inTown && this.goToTown(); - !this.act[me.act - 1].initialized && this.initialize(); + !Town.act[me.act].initialized && this.initialize(); // Act 5 wp->portalspot override - ActMap.cpp crash if (me.act === 5 && spot === "portalspot" @@ -2308,8 +2327,8 @@ const Town = { return [5098, 5018].distance; } - if (typeof (this.act[me.act - 1].spot[spot]) === "object") { - return this.act[me.act - 1].spot[spot].distance; + if (typeof (Town.act[me.act].spot[spot]) === "object") { + return Town.act[me.act].spot[spot].distance; } else { return Infinity; } @@ -2322,7 +2341,7 @@ const Town = { */ move: function (spot = "", allowTK = true) { !me.inTown && this.goToTown(); - !this.act[me.act - 1].initialized && this.initialize(); + !Town.act[me.act].initialized && this.initialize(); // act 5 static paths, ActMap.cpp seems to have issues with A5 // should other towns have static paths? @@ -2365,7 +2384,6 @@ const Town = { [[5140, 5038], [5148, 5031], [5154, 5025], [5161, 5030]].forEach(function (node) { Pather.walkTo(node[0], node[1]); }); - return true; } } @@ -2387,21 +2405,16 @@ const Town = { * @returns {boolean} */ moveToSpot: function (spot = "", allowTK = true) { - let townSpot; - let longRange = (!Skill.haveTK && spot === "waypoint"); - let tkRange = (Skill.haveTK && allowTK && ["stash", "portalspot", "waypoint"].includes(spot)); - const npcSpot = Object.values(NPC).includes(spot.toLowerCase()); - - if (!this.act[me.act - 1].hasOwnProperty("spot") - || !this.act[me.act - 1].spot.hasOwnProperty(spot)) { + if (!Town.act[me.act].hasOwnProperty("spot") + || !Town.act[me.act].spot.hasOwnProperty(spot) + || typeof (Town.act[me.act].spot[spot]) !== "object") { return false; } - if (typeof (this.act[me.act - 1].spot[spot]) === "object") { - townSpot = this.act[me.act - 1].spot[spot]; - } else { - return false; - } + const npcSpot = Object.values(NPC).includes(spot.toLowerCase()); + let longRange = (!Skill.haveTK && spot === "waypoint"); + let tkRange = (Skill.haveTK && allowTK && ["stash", "portalspot", "waypoint"].includes(spot)); + let townSpot = Town.act[me.act].spot[spot]; if (longRange) { let path = getPath(me.area, townSpot[0], townSpot[1], me.x, me.y, 1, 8); @@ -2412,13 +2425,22 @@ const Town = { } for (let i = 0; i < townSpot.length; i += 2) { - // console.debug("moveToSpot: " + spot + " from " + me.x + ", " + me.y); + const [x, y] = [townSpot[i], townSpot[i + 1]]; + // console.debug("moveToSpot: " + spot + " " + x + "/" + y + " from " + me.x + "/" + me.y); if (tkRange) { Pather.moveNear(townSpot[0], townSpot[1], 19); - } else if (getDistance(me, townSpot[i], townSpot[i + 1]) > 2) { - if (npcSpot && Game.getNPC(spot)) return true; - Pather.moveTo(townSpot[i], townSpot[i + 1], 3, false, true); + } else if (getDistance(me, x, y) > 2) { + if (npcSpot) { + let npc = Game.getNPC(spot); + if (npc && npc.distance < 5) return true; + Pather.move({ x: x, y: y }, { callback: function () { + let npc = Game.getNPC(spot); + return npc && npc.distance < 5; + } }); + } else { + Pather.moveTo(x, y, 3, false); + } } switch (spot) { @@ -2436,11 +2458,12 @@ const Town = { break; case "portalspot": case "sewers": - if (tkRange && spot === "portalspot" && getDistance(me, townSpot[0], townSpot[1]) < 21) { + if (tkRange && spot === "portalspot" + && getDistance(me, townSpot[0], townSpot[1]) < 21) { return true; } - if (getDistance(me, townSpot[i], townSpot[i + 1]) < 10) { + if (getDistance(me, x, y) < 10) { return true; } From b6cae5b64f5a59278ca2a36372bf6e49cd4d33ae Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 2 Jul 2023 19:50:46 -0400 Subject: [PATCH 171/758] Update Util.js - add additional info to the presetunit error message --- d2bs/kolbot/libs/core/Util.js | 48 ++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 12c970893..58bc6fd91 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -21,8 +21,8 @@ } }); } -}(this, function() { - function ScriptError(message) { +}(this, function () { + function ScriptError (message) { this.name = "ScriptError"; this.message = message || ""; this.stack = (new Error()).stack; @@ -459,7 +459,12 @@ */ getPresetMonster: function (area, id) { !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Monster, id); + try { + return getPresetUnit(area, sdk.unittype.Monster, id); + } catch (e) { + e.message += " Area: " + area + " Id: " + id; + throw e; + } }, /** * @param {number} area @@ -468,7 +473,12 @@ */ getPresetMonsters: function (area, id) { !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Monster, id); + try { + return getPresetUnits(area, sdk.unittype.Monster, id); + } catch (e) { + e.message += " Area: " + area + " Id: " + id; + throw e; + } }, /** * @param {number} area @@ -477,7 +487,12 @@ */ getPresetObject: function (area, id) { !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Object, id); + try { + return getPresetUnit(area, sdk.unittype.Object, id); + } catch (e) { + e.message += " Area: " + area + " Id: " + id; + throw e; + } }, /** * @param {number} area @@ -486,7 +501,12 @@ */ getPresetObjects: function (area, id) { !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Object, id); + try { + return getPresetUnits(area, sdk.unittype.Object, id); + } catch (e) { + e.message += " Area: " + area + " Id: " + id; + throw e; + } }, /** * @param {number} area @@ -495,7 +515,12 @@ */ getPresetStair: function (area, id) { !area && (area = me.area); - return getPresetUnit(area, sdk.unittype.Stairs, id); + try { + return getPresetUnit(area, sdk.unittype.Stairs, id); + } catch (e) { + e.message += " Area: " + area + " Id: " + id; + throw e; + } }, /** * @param {number} area @@ -504,7 +529,12 @@ */ getPresetStairs: function (area, id) { !area && (area = me.area); - return getPresetUnits(area, sdk.unittype.Stairs, id); + try { + return getPresetUnits(area, sdk.unittype.Stairs, id); + } catch (e) { + e.message += " Area: " + area + " Id: " + id; + throw e; + } }, }; @@ -546,7 +576,7 @@ sendToProfile: function (profileName, mode, message, getResponse = false) { let response; - function copyDataEvent(mode2, msg) { + function copyDataEvent (mode2, msg) { if (mode2 === mode) { let obj; From 565a527d3945c32096f43f5dcd829248d38ae0c9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 3 Jul 2023 00:36:43 -0400 Subject: [PATCH 172/758] Change chestlist.txt to md file - not sure these are even used but does provide a nice table to reference --- .../kolbot/libs/config/Templates/chestlist.md | 22 +++++++++++++++ .../libs/config/Templates/chestlist.txt | 28 ------------------- 2 files changed, 22 insertions(+), 28 deletions(-) create mode 100644 d2bs/kolbot/libs/config/Templates/chestlist.md delete mode 100644 d2bs/kolbot/libs/config/Templates/chestlist.txt diff --git a/d2bs/kolbot/libs/config/Templates/chestlist.md b/d2bs/kolbot/libs/config/Templates/chestlist.md new file mode 100644 index 000000000..33b066acf --- /dev/null +++ b/d2bs/kolbot/libs/config/Templates/chestlist.md @@ -0,0 +1,22 @@ +| Act | ID | Area Name | --- | Act | ID | Area Name | +| --- | --- | ----------- | --- | --- | --- |------------ | +| `1` | | | --- | `2` | | +| | 15 | Hole Level 2 | | | 55 | Stony Tomb | +| | 13 | Cave Level 2 | | | 65 | Ancient Tunnels | +| | 16 | Pit Level 2 | | | 66-72 | Tal Tombs | +| | 14 | Passage Level 2 | | | | | +| | 25 | Tower Level 5 | | | | | +| | 18 | Crypt | | | | | +| | 19 | Mausoleum | | | | | +| | | +| | | | | | | +| Act | ID | Area Name | --- | Act | ID | Area Name | +| `3` | | | --- | `5` | | +| | 84 | Spider Cave | | | 115 | Glacial Trail | +| | 85 | Spider Cavern | | | 116 | Drifter Cavern| +| | 77 | Great Marsh | | | 119 | Frozen Cellar | +| | 90 | Swampy Pit Lvl3 | | | 125 | Abbadon | +| | 79 | Lower Kurast | | | 126 | Pit of Archeon| +| | 80 | Kurast Bazaar | | | 127 | Infernal Pit | +| | 92 | Sewers Level 1 | | | | | +| | 93 | Sewers Level 2 | | | | | \ No newline at end of file diff --git a/d2bs/kolbot/libs/config/Templates/chestlist.txt b/d2bs/kolbot/libs/config/Templates/chestlist.txt deleted file mode 100644 index b71d3ffd0..000000000 --- a/d2bs/kolbot/libs/config/Templates/chestlist.txt +++ /dev/null @@ -1,28 +0,0 @@ -ID Act1 -15 - Hole Level 2 -13 - Cave Level 2 -16 - Pit Level 2 -14 - Passage Level 2 -25 - Tower Level 5 -18 - Crypta -19 - Mausoleum - Act2 -55 - stony Tomb -65 - Ancient Tunnels -66-72 - Tal Tombs - Act3 -84 - SpiderCave -85 - SpiderCavern -77 - Marsh -90 - Smapy Grave -79 - lower Kurast -80 - Kurast Basar -92 - Sewers Level 1 -93 - Sewers Level 2 - Act5 -115 - Glacial Trail -116 - Drifter Cavern -119 - FrozenCellar -125 - Abbadon -126 - Archeon -127 - Infernal \ No newline at end of file From 540810e787585c927bfe0ad1cbb03275b7f76436 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 00:41:01 -0400 Subject: [PATCH 173/758] Update Wakka.js - little bit of cleanup --- d2bs/kolbot/libs/scripts/Wakka.js | 342 ++++++++++++++++-------------- 1 file changed, 180 insertions(+), 162 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 9f877c932..bea8750cc 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -10,6 +10,7 @@ function Wakka () { const timeout = Config.Wakka.Wait; const [minDist, maxDist] = [50, 80]; const internals = { + died: false, safeTP: false, coordsInit: false, vizCoords: [], @@ -20,7 +21,7 @@ function Wakka () { infClear: false, }; - let portal, tick; + let tick; let leader = ""; let [leaderUnit, leaderPartyUnit] = [null, null]; @@ -51,39 +52,59 @@ function Wakka () { const getCoords = function () { if (!internals.coordsInit) { Common.Diablo.initLayout(); - internals.vizCoords = Common.Diablo.vizLayout === 1 ? [7707, 5274] : [7708, 5298]; - internals.seisCoords = Common.Diablo.seisLayout === 1 ? [7812, 5223] : [7809, 5193]; - internals.infCoords = Common.Diablo.infLayout === 1 ? [7868, 5294] : [7882, 5306]; + internals.vizCoords = Common.Diablo.vizLayout === 1 + ? [7707, 5274] + : [7708, 5298]; + internals.seisCoords = Common.Diablo.seisLayout === 1 + ? [7812, 5223] + : [7809, 5193]; + internals.infCoords = Common.Diablo.infLayout === 1 + ? [7868, 5294] + : [7882, 5306]; internals.coordsInit = true; } }; + /** @param {string} name */ const checkBoss = function (name) { let glow = Game.getObject(sdk.objects.SealGlow); + if (!glow) return false; - if (glow) { - for (let i = 0; i < 10; i += 1) { - let boss = Game.getMonster(name); + for (let i = 0; i < 10; i += 1) { + let boss = Game.getMonster(name); - if (boss) { - while (!boss.dead) { - delay(500); - } - - return true; + if (boss) { + while (!boss.dead) { + delay(500); } - - delay(500); + + return true; } - return true; + delay(500); } - return false; + return true; + }; + + const revive = function () { + if (me.mode === sdk.player.mode.Death) { + while (me.mode !== sdk.player.mode.Dead) { + delay(3); + } + } else if (me.dead) { + if (Config.LifeChicken <= 0) { + console.log("I Died...reviving"); + internals.died = true; + me.revive(); + } else { + scriptBroadcast("quit"); + } + } }; const getCorpse = function () { - me.mode === sdk.player.mode.Dead && me.revive(); + revive(); let rval = false; let corpse = Game.getPlayer(me.name, sdk.player.mode.Dead); @@ -103,13 +124,16 @@ function Wakka () { return rval; }; + /** @param {[number, number]} dest */ const followPath = function (dest) { let path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 10); if (!path) throw new Error("Failed go get path"); while (path.length > 0) { if (me.mode === sdk.player.mode.Dead || me.inTown) return false; - (!leaderUnit || !copyUnit(leaderUnit).x) && (leaderUnit = Game.getPlayer(leader)); + if (!leaderUnit || !copyUnit(leaderUnit).x) { + leaderUnit = Game.getPlayer(leader); + } if (leaderUnit) { // monsters nearby - don't move @@ -170,9 +194,14 @@ function Wakka () { return true; }; + /** @returns {number} */ const getLeaderUnitArea = function () { - (!leaderUnit || !copyUnit(leaderUnit).x) && (leaderUnit = Game.getPlayer(leader)); - return !!leaderUnit ? leaderUnit.area : getParty(leader).area; + if (!leaderUnit || !copyUnit(leaderUnit).x) { + leaderUnit = Game.getPlayer(leader); + } + return !!leaderUnit + ? leaderUnit.area + : getParty(leader).area; }; const log = function (msg = "") { @@ -186,7 +215,10 @@ function Wakka () { if (Config.Leader) { leader = Config.Leader; - if (!Misc.poll(() => Misc.inMyParty(leader), 30e3, 1000)) throw new Error("Wakka: Leader not partied"); + if (!Misc.poll(() => Misc.inMyParty(leader), 30e3, 1000)) { + console.warn("Wakka: Leader not partied. Using autodetect"); + leader = ""; + } } !leader && (leader = Misc.autoLeaderDetect({ @@ -195,59 +227,31 @@ function Wakka () { timeout: timeout * 60e3 })); Town.doChores(); + if (!leader) throw new Error("Wakka: Leader not found"); - if (leader) { - addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - const Worker = require("../modules/Worker"); - - try { - if (Config.Wakka.SkipIfBaal) { - let leadTick = getTickCount(); - let killLeaderTracker = false; - - Worker.runInBackground.leaderTracker = function () { - if (Common.Diablo.done || killLeaderTracker) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { - if (Loader.scriptName() === "Wakka") { - killLeaderTracker = true; - throw new Error("Party leader is running baal"); - } else { - // kill process - return false; - } - } - - return true; - }; - } + addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + const Worker = require("../modules/Worker"); - let levelTick = getTickCount(); - let killLevelTracker = false; + try { + if (Config.Wakka.SkipIfBaal) { + let leadTick = getTickCount(); + let killLeaderTracker = false; - Worker.runInBackground.levelTracker = function () { - if (Common.Diablo.done || killLevelTracker) return false; + Worker.runInBackground.leaderTracker = function () { + if (Common.Diablo.done || killLeaderTracker) return false; // check every 3 seconds - if (getTickCount() - levelTick < 3000) return true; - levelTick = getTickCount(); + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); // check again in another 3 seconds if game wasn't ready if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - if (me.charlvl >= Config.Wakka.StopAtLevel) { - Config.Wakka.StopProfile && D2Bot.stop(); - + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { if (Loader.scriptName() === "Wakka") { - killLevelTracker = true; - throw new Error("Reached wanted level"); + killLeaderTracker = true; + throw new Error("Party leader is running baal"); } else { // kill process return false; @@ -256,61 +260,81 @@ function Wakka () { return true; }; + } - while (Misc.inMyParty(leader)) { - try { - switch (me.area) { - case sdk.areas.PandemoniumFortress: - portal = Pather.getPortal(sdk.areas.ChaosSanctuary, null); + let levelTick = getTickCount(); + let killLevelTracker = false; - if (portal) { - !internals.safeTP && delay(5000); - Pather.usePortal(sdk.areas.ChaosSanctuary, null); - Precast.doPrecast(true); - } + Worker.runInBackground.levelTracker = function () { + if (Common.Diablo.done || killLevelTracker) return false; + // check every 3 seconds + if (getTickCount() - levelTick < 3000) return true; + levelTick = getTickCount(); - break; - case sdk.areas.ChaosSanctuary: - try { - let diaTick = getTickCount(); + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; - Worker.runInBackground.diaSpawned = function () { - if (Common.Diablo.done || (internals.vizClear && internals.seisClear && internals.infClear)) { - return false; - } - // check every 1/4 second - if (getTickCount() - diaTick < 250) return true; - diaTick = getTickCount(); + if (me.charlvl >= Config.Wakka.StopAtLevel) { + Config.Wakka.StopProfile && D2Bot.stop(); - if (Common.Diablo.diabloSpawned) { - internals.vizClear = true; - internals.seisClear = true; - internals.infClear = true; - throw new Error("Diablo spawned"); - } + if (Loader.scriptName() === "Wakka") { + killLevelTracker = true; + throw new Error("Reached wanted level"); + } else { + // kill process + return false; + } + } - return true; - }; + return true; + }; - if (!internals.safeTP) { - if (checkMonsters(25, false)) { - log("hot tp"); - Pather.usePortal(sdk.areas.PandemoniumFortress, null); - getCorpse(); + let diaTick = getTickCount(); - break; - } else { - getCoords(); - internals.safeTP = true; - } - } + Worker.runInBackground.diaSpawned = function () { + if (Common.Diablo.done || (internals.vizClear && internals.seisClear && internals.infClear)) { + return false; + } + // check every 1/4 second + if (getTickCount() - diaTick < 250) return true; + diaTick = getTickCount(); + + if (Common.Diablo.diabloSpawned) { + internals.vizClear = true; + internals.seisClear = true; + internals.infClear = true; + throw new Error("Diablo spawned"); + } - if (!internals.vizClear) { - if (!followPath(internals.vizCoords)) { - console.debug("Failed to move to viz"); - break; - } + return true; + }; + + while (Misc.inMyParty(leader)) { + try { + if (me.inArea(sdk.areas.PandemoniumFortress)) { + let portal = Pather.getPortal(sdk.areas.ChaosSanctuary, null); + if (portal) { + !internals.safeTP && delay(5000); + Pather.usePortal(sdk.areas.ChaosSanctuary, null); + Precast.doPrecast(true); + } + } else if (me.inArea(sdk.areas.ChaosSanctuary)) { + try { + if (!internals.safeTP) { + if (checkMonsters(25, false)) { + log("hot tp"); + Pather.usePortal(sdk.areas.PandemoniumFortress, null); + + break; + } else { + getCoords(); + internals.safeTP = true; + } + } + + if (!internals.vizClear) { + if (followPath(internals.vizCoords)) { if (checkBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { log("vizier dead"); internals.vizClear = true; @@ -321,16 +345,13 @@ function Wakka () { delay(100); } } - - break; + } else { + console.debug("Failed to move to viz"); } + } - if (internals.vizClear && !internals.seisClear) { - if (!followPath(internals.seisCoords)) { - console.debug("Failed to move to seis"); - break; - } - + if (internals.vizClear && !internals.seisClear) { + if (followPath(internals.seisCoords)) { if (checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { log("seis dead"); internals.seisClear = true; @@ -341,16 +362,13 @@ function Wakka () { delay(100); } } - - break; + } else { + console.debug("Failed to move to seis"); } + } - if (internals.vizClear && internals.seisClear && !internals.infClear) { - if (!followPath(internals.infCoords)) { - console.debug("Failed to move to infector"); - break; - } - + if (internals.vizClear && internals.seisClear && !internals.infClear) { + if (followPath(internals.infCoords)) { if (checkBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { log("infector dead"); internals.infClear = true; @@ -361,61 +379,61 @@ function Wakka () { delay(100); } } - - break; + } else { + console.debug("Failed to move to infector"); } + } + if (internals.vizClear && internals.seisClear && internals.infClear) { Pather.moveTo(7767, 5263); - Misc.poll(() => { + Misc.poll(function () { if (Common.Diablo.diabloSpawned) return true; if (Game.getMonster(sdk.monsters.Diablo)) return true; return false; }, Time.minutes(2), 500); - } catch (e) { - console.log((e.message ? e.message : e)); } + } catch (e) { + console.error((e.message ? e.message : e)); + } - if (internals.vizClear && internals.seisClear && internals.infClear) { - Pather.moveTo(7767, 5263); - - let diablo = Misc.poll(() => Game.getMonster(sdk.monsters.Diablo), Time.minutes(3), 500); + if (internals.vizClear && internals.seisClear && internals.infClear) { + Pather.moveTo(7767, 5263); - if (diablo) { - while (!diablo.dead) { - delay(100); - } - log("Diablo is dead"); + let diablo = Misc.poll(function () { + return Game.getMonster(sdk.monsters.Diablo); + }, Time.minutes(3), 500); - if (!me.canTpToTown() || !Town.goToTown()) { - Pather.usePortal(sdk.areas.PandemoniumFortress); - } + if (diablo) { + while (!diablo.dead) { + delay(100); + } + log("Diablo is dead"); - return true; - } else { - log("Couldn't find diablo"); + if (!me.canTpToTown() || !Town.goToTown()) { + Pather.usePortal(sdk.areas.PandemoniumFortress); } - } - break; + return true; + } else { + log("Couldn't find diablo"); + } } + } - me.dead && me.revive(); + revive(); - delay(200); - } catch (e) { - console.error(e); + delay(200); + } catch (e) { + console.error(e); - return true; - } + return true; } - } catch (e) { - // - } finally { - Common.Diablo.done; - removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); } - } else { - throw new Error("No leader found"); + } catch (e) { + // console.error(e); + } finally { + Common.Diablo.done; + removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); } log("Wakka complete"); From 730051bfb1f66948ab506f7d433f15adff05a21f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 00:42:24 -0400 Subject: [PATCH 174/758] Update ChestMania.js - refactor, made it cleaner --- d2bs/kolbot/libs/scripts/ChestMania.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ChestMania.js b/d2bs/kolbot/libs/scripts/ChestMania.js index 984446d58..7461baae8 100644 --- a/d2bs/kolbot/libs/scripts/ChestMania.js +++ b/d2bs/kolbot/libs/scripts/ChestMania.js @@ -7,28 +7,26 @@ // todo - if we have run ghostsbusters before this then some of these areas don't need to be re-run -function ChestMania() { +function ChestMania () { Town.doChores(); - for (let prop in Config.ChestMania) { - if (Config.ChestMania.hasOwnProperty(prop)) { - for (let i = 0; i < Config.ChestMania[prop].length; i += 1) { - const nextArea = Config.ChestMania[prop][i]; + Object.values(Config.ChestMania) + .forEach(act => { + for (let area of act) { if ([ sdk.areas.BloodMoor, sdk.areas.RockyWaste, sdk.areas.SpiderForest, sdk.areas.OuterSteppes, sdk.areas.BloodyFoothills - ].includes(nextArea)) { + ].includes(area)) { // if we precast as soon as we step out of town it sometimes crashes - so do precast somewhere else first Precast.doRandomPrecast(false); } - Pather.journeyTo(Config.ChestMania[prop][i]); + Pather.journeyTo(area); Precast.doPrecast(false); - Misc.openChestsInArea(Config.ChestMania[prop][i]); + Misc.openChestsInArea(area); } Town.doChores(); - } - } + }); return true; } From e5d3cfde9786edf0408b1482f688139a00453242 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 00:43:19 -0400 Subject: [PATCH 175/758] Update Loader.js - keep track of completed scripts, todo is use this later to know if we've been through a certain area --- d2bs/kolbot/libs/core/Loader.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index ff8dc47f6..71d8867f7 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -10,6 +10,7 @@ const Loader = { scriptList: [], scriptIndex: -1, skipTown: ["Test", "Follower"], + doneScripts: new Set(), init: function () { this.getScripts(); @@ -168,6 +169,7 @@ const Loader = { + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) ); + this.doneScripts.add(script); } } } catch (error) { @@ -256,6 +258,7 @@ const Loader = { + "ÿc7 - Experience Gained: ÿc0" + gain + "\n" + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) ); + this.doneScripts.add(script); } } } catch (error) { From 0a3c47df348c03b79e507aa2aaf6cc76f178b1a0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 11:40:57 -0400 Subject: [PATCH 176/758] Update Wakka.js - don't break from loop, this was leftover from structure change --- d2bs/kolbot/libs/scripts/Wakka.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index bea8750cc..d67d46ac0 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -326,7 +326,7 @@ function Wakka () { log("hot tp"); Pather.usePortal(sdk.areas.PandemoniumFortress, null); - break; + continue; } else { getCoords(); internals.safeTP = true; From 7970938f05e58c36c5b87e19625e390e83e11f5f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 12:23:29 -0400 Subject: [PATCH 177/758] Update Prototypes.js - typo for charclass prototype --- d2bs/kolbot/libs/core/Prototypes.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 59fdf57b9..361573722 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -442,7 +442,7 @@ Object.defineProperties(Unit.prototype, { /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; - let charclass = getBaseStat("items", this.classid, "charclass"); + let charclass = getBaseStat("itemtypes", this.itemType, "class"); // hacky? Essentially just using this to check if we can use the item and if the item doesn't have a specific // class requirement, we'll just assume it's for our class. As this makes the actualy checks easy return charclass === 255 ? me.classid : charclass; @@ -1869,7 +1869,13 @@ Unit.prototype.getColor = function () { * @throws Error */ Unit.prototype.castChargedSkill = function (...args) { - let skillId, x, y, unit, chargedItem, charge; + let skillId, x, y; + /** @type {Monster} */ + let unit; + /** @type {ItemUnit} */ + let chargedItem; + /** @type {Charge} */ + let charge; /** @param {Charge} itemCharge */ let validCharge = function (itemCharge) { return itemCharge.skill === skillId && itemCharge.charges; From 47c2a0406c4b1bea52092a1a8806ac0e075f9fe2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 13:09:50 -0400 Subject: [PATCH 178/758] Update Pickit.js - little bit of cleanup, should handle the trying to make room recursion --- d2bs/kolbot/libs/core/Pickit.js | 103 ++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 6d593a9aa..35ed744ff 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -9,7 +9,7 @@ * @namespace Pickit */ const Pickit = { - gidList: [], + gidList: new Set(), invoLocked: true, beltSize: 1, /** @enum */ @@ -63,7 +63,7 @@ const Pickit = { // eslint-disable-next-line no-unused-vars itemEvent: function (gid, mode, code, global) { if (gid > 0 && mode === 0) { - Pickit.gidList.push(gid); + Pickit.gidList.add(gid); } }, @@ -347,27 +347,26 @@ const Pickit = { this.picked = false; } - let gid = (unit.gid || -1); + const itemCount = me.itemcount; const cancelFlags = [ - sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, - sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube + sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, + sdk.uiflags.Waypoint, sdk.uiflags.Shop, + sdk.uiflags.Stash, sdk.uiflags.Cube ]; - let itemCount = me.itemcount; - let item = gid > -1 ? Game.getItem(-1, -1, gid) : 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); } - let stats = new ItemStats(item); - let tkMana = stats.useTk ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 : Infinity; + const stats = new ItemStats(item); + const tkMana = stats.useTk + ? Skill.getManaCost(sdk.skills.Telekinesis) * 2 + : Infinity; MainLoop: for (let i = 0; i < retry; i += 1) { @@ -545,6 +544,7 @@ const Pickit = { 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]; // why wait for idle? while (!me.idle) { @@ -557,26 +557,32 @@ const Pickit = { 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); + })) { Town.clearBelt(); } while (Pickit.pickList.length > 0) { if (me.dead) return false; Pickit.pickList.sort(this.sortItems); - const check = Pickit.pickList.shift(); - // get the actual item again - const itemToPick = Game.getItem(check.classid, -1, check.gid); + const currItem = Pickit.pickList[0]; - if (!itemToPick || Pickit.ignoreList.has(itemToPick.gid)) { + if (Pickit.ignoreList.has(currItem.gid)) { + Pickit.pickList.shift(); + + continue; + } + + // get the real item + const _item = Game.getItem(-1, -1, currItem.gid); + if (!_item || copyUnit(_item).x === undefined) { Pickit.pickList.shift(); continue; @@ -584,28 +590,29 @@ const Pickit = { // 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 (itemToPick && copyUnit(itemToPick).x !== undefined && itemToPick.onGroundOrDropping - && (Pather.useTeleport() || me.inTown || !checkCollision(me, itemToPick, 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(itemToPick); + let status = this.checkItem(_item); - if (status.result && this.canPick(itemToPick)) { + if (status.result && this.canPick(_item)) { // Override canFit for scrolls, potions and gold - let canFit = (Storage.Inventory.CanFit(itemToPick) || Pickit.essentials.includes(itemToPick.itemType)); + let canFit = (Storage.Inventory.CanFit(_item) || Pickit.essentials.includes(_item.itemType)); // 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 = (itemToPick.gid !== undefined && Storage.Inventory.CanFit(itemToPick))); + me.fieldID() && (canFit = (_item.gid !== undefined && Storage.Inventory.CanFit(_item))); } // Try to make room by selling items in town if (!canFit) { + let usedSpace = Storage.Inventory.UsedSpacePercent(); // 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(itemToPick) + itemToPick.name); + console.log("ÿc7Trying to make room for " + Item.color(_item) + _item.name); // Go to town and do town chores - if (Town.visitTown()) { + if (Town.visitTown() && Storage.Inventory.UsedSpacePercent() < usedSpace) { // 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(); @@ -613,16 +620,16 @@ const Pickit = { } // Town visit failed - abort - console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.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(itemToPick).x !== undefined) { - Item.logger("No room for", itemToPick); - console.warn("ÿc7Not enough room for " + Item.color(itemToPick) + itemToPick.name); - Pickit.ignoreList.add(itemToPick.gid); + if (copyUnit(_item).x !== undefined) { + Item.logger("No room for", _item); + console.warn("ÿc7Not enough room for " + Item.color(_item) + _item.name); + Pickit.ignoreList.add(_item.gid); if (canUseMule) { console.debug("Attempt to trigger automule"); needMule = true; @@ -634,9 +641,9 @@ const Pickit = { // Item can fit - pick it up if (canFit) { - let picked = this.pickItem(itemToPick, status.result, status.line); + let picked = this.pickItem(_item, status.result, status.line); if (!picked) { - console.warn("Failed to pick item " + itemToPick.prettyPrint); + console.warn("Failed to pick item " + _item.prettyPrint); break; } @@ -660,20 +667,26 @@ const Pickit = { * @param {number} retry */ fastPick: function (retry = 3) { - let item, itemList = []; + let item; + const _removeList = []; + const itemList = []; - while (this.gidList.length > 0) { - let gid = this.gidList.shift(); + for (let gid of this.gidList) { + _removeList.push(gid); item = Game.getItem(-1, -1, gid); - if (item && item.onGroundOrDropping && (!Town.ignoreType(item.itemType) || (item.itemType >= sdk.items.type.HealingPotion && item.itemType <= sdk.items.type.RejuvPotion)) - && item.itemType !== sdk.items.type.Gold && getDistance(me, item) <= Config.PickRange) { + && item.itemType !== sdk.items.type.Gold + && getDistance(me, item) <= Config.PickRange) { itemList.push(copyUnit(item)); } } + while (_removeList.length > 0) { + this.gidList.delete(_removeList.shift()); + } + while (itemList.length > 0) { itemList.sort(this.sortFastPickItems); let check = itemList.shift(); From 811314aa716b039cc2f7082cc91617aa65383e74 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:10:30 -0400 Subject: [PATCH 179/758] Update Pickit.js - forgot to add this --- d2bs/kolbot/libs/core/Pickit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 35ed744ff..72a601f88 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -650,6 +650,7 @@ const Pickit = { } } } + Pickit.pickList.shift(); } // Quit current game and transfer the items to mule From 10d6de540343998f3f3bff9d6b4e21b02ea82bc0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 17:39:26 -0400 Subject: [PATCH 180/758] Update SkillData.js - typo, was meant to be a function not a static range --- d2bs/kolbot/libs/core/GameData/SkillData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index e77779e83..bd645df4b 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -233,7 +233,7 @@ skillMap.set(sdk.skills.Inferno, { hand: sdk.skills.hand.Left, missile: true, - range: ((17 + (me.getSkill(sdk.skills.Inferno, sdk.skills.subindex.SoftPoints) * 3) / 4) * 2 / 3), + range: () => ((17 + (me.getSkill(sdk.skills.Inferno, sdk.skills.subindex.SoftPoints) * 3) / 4) * 2 / 3), }); skillMap.set(sdk.skills.StaticField, { hand: sdk.skills.hand.Right, From 06b7b5de4cfd52cd9d6acaea2911c74915da2bed Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 17:48:41 -0400 Subject: [PATCH 181/758] Update SkillData.js - mismatched parenthesis --- d2bs/kolbot/libs/core/GameData/SkillData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index bd645df4b..586412759 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -233,7 +233,7 @@ skillMap.set(sdk.skills.Inferno, { hand: sdk.skills.hand.Left, missile: true, - range: () => ((17 + (me.getSkill(sdk.skills.Inferno, sdk.skills.subindex.SoftPoints) * 3) / 4) * 2 / 3), + range: () => (((17 + (me.getSkill(sdk.skills.Inferno, sdk.skills.subindex.SoftPoints) * 3)) / 4) * 2 / 3), }); skillMap.set(sdk.skills.StaticField, { hand: sdk.skills.hand.Right, From ff89b298ecce6bd9b548c47b2e8f98626cc231b9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 7 Jul 2023 23:59:57 -0400 Subject: [PATCH 182/758] Update Pickit.js - fix returning early from failed town check --- d2bs/kolbot/libs/core/Pickit.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 72a601f88..44b7dae7b 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -612,17 +612,23 @@ const Pickit = { console.log("ÿc7Trying to make room for " + Item.color(_item) + _item.name); // Go to town and do town chores - if (Town.visitTown() && Storage.Inventory.UsedSpacePercent() < usedSpace) { + 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(); - return this.pickItems(); + if (Storage.Inventory.UsedSpacePercent() < usedSpace) { + console.log( + "ÿc7Made room for " + Item.color(_item) + _item.prettyPrint + + " (" + usedSpace + "% -> " + Storage.Inventory.UsedSpacePercent() + "%)" + ); + Pickit.ignoreList.clear(); + return this.pickItems(); + } + } else { + // Town visit failed - abort + console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(_item) + _item.name); + + return false; } - - // Town visit failed - abort - console.warn("Failed to visit town. ÿc7Not enough room for " + Item.color(_item) + _item.name); - - return false; } // Can't make room - trigger automule From 7aefb300fa657aea900b65b2a38df8b694db4cb1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:35:32 -0400 Subject: [PATCH 183/758] Update Town.js - fix in building check --- d2bs/kolbot/libs/core/Town.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index d327094d3..a9352df6d 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -339,8 +339,7 @@ const Town = { * @returns {boolean | Unit} */ initNPC: function (task = "", reason = "undefined") { - console.time("initNPC"); - console.info(true, reason); + console.info(true, reason, "initNPC"); task = task.capitalize(false); delay(250); @@ -2373,7 +2372,9 @@ const Town = { if (returnWhenDone) return true; } - } else if (me.act === 2 && me.y < 5049 && !String.isEqual(spot, NPC.Atma)) { + } else if (me.act === 2 + && me.x > 5122 && me.y < 5049 + && !String.isEqual(spot, NPC.Atma)) { // we are inside the building, if Atma is blocking the entrance we need the side door let atma = Game.getNPC(NPC.Atma); // console.debug("atma", atma); From b1586403dcd5cd395c7e70ba1756e1aa7235fb6c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:56:29 -0400 Subject: [PATCH 184/758] type definitions --- d2bs/kolbot/sdk/globals.d.ts | 19 ++++- d2bs/kolbot/sdk/types/OOG.d.ts | 2 +- d2bs/kolbot/sdk/types/Packet.d.ts | 115 +++++++++++++++++++++++++++++ d2bs/kolbot/sdk/types/Pather.d.ts | 59 +++++++++++---- d2bs/kolbot/sdk/types/Skill.d.ts | 21 +++++- d2bs/kolbot/sdk/types/Storage.d.ts | 2 +- d2bs/kolbot/sdk/types/Town.d.ts | 8 +- d2bs/kolbot/sdk/types/Util.d.ts | 5 ++ 8 files changed, 206 insertions(+), 25 deletions(-) create mode 100644 d2bs/kolbot/sdk/types/Packet.d.ts diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 7bf369843..8a902c641 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -/// +/// /// /// /// @@ -122,6 +122,7 @@ declare global { class ScriptError extends Error { } + type Act = 1 | 2 | 3 | 4 | 5; type actType = { initialized: boolean, spot: { [data: string]: [number, number] } }; type potType = 'hp' | 'mp' | 'rv'; @@ -424,6 +425,7 @@ declare global { readonly spectype: number; readonly curseable: boolean; readonly scareable: boolean; + readonly attacking: boolean; getEnchant(type: number): boolean; hasEnchant(...enchants: number): boolean @@ -449,6 +451,8 @@ declare global { class ObjectUnit extends Unit { public type: ObjectType; objtype: number; + openUnit(): boolean; + useUnit(targetArea?: number): boolean; } type MissileType = 3; @@ -459,7 +463,8 @@ declare global { type ItemType = 4; interface ItemUnit extends Unit { - + castChargedSkill(skillId: number, target?: Unit): boolean; + castChargedSkill(skillId: number, x: number, y: number): boolean; } class ItemUnit extends Unit { @@ -488,8 +493,9 @@ declare global { // additional, not from d2bs readonly identified: boolean; readonly isEquipped: boolean - readonly dexreq: number - readonly strreq: number + readonly dexreq: number; + readonly strreq: number; + readonly charclass: number; readonly isInInventory: boolean; readonly isInStash: boolean; readonly isInCube: boolean; @@ -537,6 +543,7 @@ declare global { type TileType = 5; class Tile extends Unit { public type: TileType; + useUnit(targetArea?: number): boolean; } interface MeType extends Unit { @@ -623,6 +630,7 @@ declare global { readonly goldenbird: boolean; readonly lamessen: boolean; readonly gidbinn: boolean; + readonly blackendTemple: boolean; readonly travincal: boolean; readonly mephisto: boolean; readonly izual: boolean; @@ -660,6 +668,7 @@ declare global { readonly FBR: number; readonly IAS: number; readonly shapeshifted: boolean; + readonly attacking: boolean; haveWaypoint(area: number): boolean; overhead(msg: string): void; @@ -704,6 +713,8 @@ declare global { fieldID(): boolean; switchToPrimary(): boolean; haveWaypoint(area: number): boolean; + castChargedSkill(skillId: number, target?: Unit): boolean; + castChargedSkill(skillId: number, x: number, y: number): boolean; } const me: MeType diff --git a/d2bs/kolbot/sdk/types/OOG.d.ts b/d2bs/kolbot/sdk/types/OOG.d.ts index bbd9afef7..a326e9710 100644 --- a/d2bs/kolbot/sdk/types/OOG.d.ts +++ b/d2bs/kolbot/sdk/types/OOG.d.ts @@ -58,7 +58,7 @@ declare global { timeoutDelay(text: any, time: any, stopfunc?: any, arg?: any):void click(type: any, x: any, y: any, xsize: any, ysize: any):void setText(type: any, x: any, y: any, xsize: any, ysize: any, text: any):void - getText(type: any, x: any, y: any, xsize: any, ysize: any):string + getText(type: any, x: any, y: any, xsize: any, ysize: any):string[] joinChannel(channel: any):void createGame(name: any, pass: any, diff: any, delay: any):void clickRealm(realm: 0|1|2|3):void diff --git a/d2bs/kolbot/sdk/types/Packet.d.ts b/d2bs/kolbot/sdk/types/Packet.d.ts new file mode 100644 index 000000000..46419f771 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Packet.d.ts @@ -0,0 +1,115 @@ +// @ts-nocheck +declare global { + namespace Packet { + /** + * Interact and open the menu of an NPC + * @param {NPCUnit} unit + * @returns {boolean} + */ + function openMenu(unit: NPCUnit): boolean; + + /** + * Start a trade action with an NPC + * @param {NPCUnit} unit + * @param {number} mode + * @returns {boolean} + */ + function startTrade(unit: NPCUnit, mode: number): boolean; + + /** + * Buy an item from an interacted NPC + * @param {NPCUnit} unit + * @param {boolean} shiftBuy + * @param {boolean} gamble + * @returns {boolean} + */ + function buyItem(unit: NPCUnit, shiftBuy: boolean, gamble: boolean): boolean; + + /** + * Buy scrolls from an interacted NPC, we need this as a separate check because itemcount doesn't change + * if the scroll goes into the tome automatically. + * @param {NPCUnit} unit + * @param {ItemUnit} [tome] + * @param {boolean} [shiftBuy] + * @returns {boolean} + */ + function buyScroll(unit: NPCUnit, tome?: ItemUnit, shiftBuy?: boolean): boolean; + + /** + * Sell an item to a NPC + * @param {ItemUnit} unit + * @returns {boolean} + */ + function sellItem(unit: ItemUnit): boolean; + + /** + * @param {ItemUnit} unit + * @param {ItemUnit} tome + * @returns {boolean} + */ + function identifyItem(unit: ItemUnit, tome: ItemUnit): boolean; + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + function itemToCursor(item: ItemUnit): boolean; + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + function dropItem(item: ItemUnit): boolean; + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + function givePotToMerc(item: ItemUnit): boolean; + + /** + * @param {ItemUnit} item + * @param {number} xLoc + * @returns {boolean} + */ + function placeInBelt(item: ItemUnit, xLoc: number): boolean; + + /** + * @param {ItemUnit} who + * @param {boolean} toCursor + * @returns {boolean} + */ + function click(who: ItemUnit, toCursor?: boolean): boolean; + + /** + * @param {Unit} who + * @returns {boolean} + */ + function entityInteract(who: Unit): boolean; + + /** + * @param {NPCUnit} who + * @returns {boolean} + */ + function cancelNPC(who: NPCUnit): boolean; + + /** + * @param {ItemUnit} pot + * @returns {boolean} + */ + function useBeltItemForMerc(pot: ItemUnit): boolean; + function castSkill(hand: number, wX: number, wY: number): void; + function castAndHoldSkill(hand: number, wX: number, wY: number, duration?: number): void; + function unitCast(hand: number, who: Monster | ItemUnit | ObjectUnit): void; + function telekinesis(who: Monster | ItemUnit | ObjectUnit): boolean; + function enchant(who: Monster | Player | MercUnit): boolean; + function teleport(wX: number, wY: number): boolean; + function teleWalk(x: number, y: number, maxDist: number): boolean; + function questRefresh(): void; + function flash(gid?: number, wait?: number): void; + function changeStat(stat: number, value: number): void; + function addListener(packetType: number | number[], callback: (packet: number) => any): null; + function removeListener(callback: (packet: number) => any): null; + } +} +export {}; \ No newline at end of file diff --git a/d2bs/kolbot/sdk/types/Pather.d.ts b/d2bs/kolbot/sdk/types/Pather.d.ts index fc4db1c59..8b9d70dae 100644 --- a/d2bs/kolbot/sdk/types/Pather.d.ts +++ b/d2bs/kolbot/sdk/types/Pather.d.ts @@ -1,5 +1,25 @@ export {}; declare global { + interface PathSettings { + allowNodeActions?: boolean; + allowTeleport?: boolean; + allowClearing?: boolean; + allowTown?: boolean; + allowPicking?: boolean; + minDist?: number; + retry?: number; + pop?: boolean; + returnSpotOnError?: boolean; + callback?: () => void; + clearSettings?: ClearSettings; + } + + interface ClearSettings { + clearPath?: boolean; + range?: number; + specType?: number; + sort?: () => void; + } namespace Pather { const wpAreas: number[]; let walkDistance: number; @@ -8,6 +28,8 @@ declare global { const cancelFlags: number[]; let recursion: boolean; let lastPortalTick: 0; + let allowBroadcast: boolean; + function getWalkDistance(x: number, y: number, area?: number, xx?: number, yy?: number, reductionType?: 0 | 1 | 2, radius?: number): number; function useTeleport(): boolean; function moveTo(x: number, y: number, retry?: number | undefined, clearPath?: boolean | undefined, pop?: boolean | undefined): boolean; @@ -16,22 +38,31 @@ declare global { function openDoors(x: any, y: any): boolean; function moveToUnit(unit: PathNode, offX?: undefined, offY?: undefined, clearPath?: undefined, pop?: undefined): boolean; function moveToPreset(area: any, unitType: any, unitId: any, offX?: any, offY?: any, clearPath?: any, pop?: any): boolean; - function moveToExit(targetArea: any, use?: any, clearPath?: any): void; - function getNearestRoom(area: any): void; - function openExit(targetArea: any): void; - function openUnit(type: any, id: any): void; + function moveToPresetObject(area: number, unitId: number, givenSettings?: PathSettings): boolean; + function moveToPresetMonster(area: number, unitId: number, givenSettings?: PathSettings): boolean; + function moveToExit(targetArea: any, use?: any, givenSettings?: PathSettings): boolean; + function getDistanceToExit(area?: number, exit?: number): number; + function getExitCoords(area?: number, exit?: number): PathNode | false; + function getNearestRoom(area: number): [number, number] | false; + function openExit(targetArea: number): boolean; + function openUnit(type: number, id: number): void; function useUnit(type: any, id: any, targetArea: any): boolean; - function useWaypoint(targetArea: number | null, check?: boolean): boolean; - function makePortal(use?: boolean | undefined): void; + function broadcastIntent(targetArea: number): void; + function useWaypoint(targetArea: number | null | "random", check?: boolean): boolean; + function makePortal(use?: boolean | undefined): ObjectUnit | boolean; function usePortal(targetArea?: number | null, owner?: string | undefined, unit?: undefined): boolean; - function getPortal(targetArea: any, owner?: any): ObjectUnit | false; - function getNearestWalkable(x: any, y: any, range: any, step: any, coll: any, size?: any): [number, number] | false; - function checkSpot(x: any, y: any, coll: any, cacheOnly: any, size: any): void; + function getPortal(targetArea: number, owner?: any): ObjectUnit | false; + function getNearestWalkable( + x: number, y: number, range: number, step: number, coll: number, size?: number + ): [number, number] | false; + function checkSpot( + x: number, y: number, coll: number, cacheOnly: boolean, size: number + ): boolean; + /** @deprecated use `me.accessToAct(act)` instead */ function accessToAct(act: number): boolean; - function getWP(area: any, clearPath?: any): boolean; - function journeyTo(area: any): boolean; - function plotCourse(dest: any, src: any): false|{course:number[]}; - function areasConnected(src: any, dest: any): void; - function getAreaName(area: number): void; + function getWP(area: number, clearPath?: boolean): boolean; + function journeyTo(area: number): boolean; + function plotCourse(dest: number, src: number): false | { course: number[], useWP: boolean }; + function areasConnected(src: number, dest: number): void; } } diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts index cdeb9b00a..810d72474 100644 --- a/d2bs/kolbot/sdk/types/Skill.d.ts +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -36,15 +36,33 @@ declare global { have(): boolean; reset(): void; } + + type Charge = { + skill: number; + level: number; + charges: number; + maxcharges: number; + }; + + class ChargedSkill { + skill: number; + level: number; + charges: number; + maxCharges: number; + gid: number; + unit: ItemUnit; + update(item: ItemUnit): void; + } namespace Skill { let usePvpRange: boolean; const manaCostList: object; const needFloor: number[]; const missileSkills: number[]; - const charges: any[]; + const charges: ChargedSkill[]; function get (skillId: number): SkillDataInfo; function getClassSkillRange(classid?: number): [number, number]; + function getCharges(): boolean; function init(): void; function canUse(skillId: number): boolean; function getDuration(skillId: number): number; @@ -64,5 +82,6 @@ declare global { function useTK(unit: Unit): boolean; function cast(skillId: number, hand?: number, x?: number, y?: number, item?: ItemUnit | undefined): boolean; function cast(skillId: number, hand?: number, unit?: Unit): boolean; + function castCharges(skillId: number, unit: Unit | { x: number, y: number }): boolean; } } diff --git a/d2bs/kolbot/sdk/types/Storage.d.ts b/d2bs/kolbot/sdk/types/Storage.d.ts index b45158e1a..ac82b3b9d 100644 --- a/d2bs/kolbot/sdk/types/Storage.d.ts +++ b/d2bs/kolbot/sdk/types/Storage.d.ts @@ -63,7 +63,7 @@ declare global { /** * A function that returns the amount of space used in this container */ - UsedSpacePercent(): void + UsedSpacePercent(): number /** * A function the returns an item list in comparison to a given reference array diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts index 42a83acc1..917b017b8 100644 --- a/d2bs/kolbot/sdk/types/Town.d.ts +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -42,7 +42,7 @@ declare global { function get(): any; function reset(): void; } - const tasks: { + const tasks: Map; const ignoredItemTypes: any[]; function needPotions(): boolean; function doChores(repair?: boolean): boolean; - function npcInteract(name?: string, cancel?: boolean): boolean | Unit; + function npcInteract(name?: string, cancel?: boolean): boolean | NPCUnit; function checkQuestItems(): void; function getTpTool(): ItemUnit; function getIdTool(): ItemUnit; function canTpToTown(): boolean; - function initNPC(task?: string, reason?: string): boolean | Unit; + function initNPC(task?: string, reason?: string): boolean | NPCUnit; function heal(): boolean; function needHealing(): boolean; function buyPotions(): boolean; diff --git a/d2bs/kolbot/sdk/types/Util.d.ts b/d2bs/kolbot/sdk/types/Util.d.ts index 987b2a193..2112d11e7 100644 --- a/d2bs/kolbot/sdk/types/Util.d.ts +++ b/d2bs/kolbot/sdk/types/Util.d.ts @@ -123,6 +123,11 @@ declare global { arg4?: number; }; + namespace Messaging { + function sendToScript(name: string, message: string): boolean; + function sendToProfile(profile: string, mode: number, msg: string, getResponse?: boolean): boolean; + } + namespace Sort { function units(a: Unit, b: Unit): number; function presetUnits(a: PresetUnit, b: PresetUnit): number; From 547dfbf10849395f0efbf4b48739da7d844d3de6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 13 Jul 2023 12:14:10 -0400 Subject: [PATCH 185/758] Update Town.js - hanlde alkor for stacking potions, in general unless we are already over there it wastes a lot of time to run to him so change acts for easier to access npc --- d2bs/kolbot/libs/core/Town.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index a9352df6d..c7579df3b 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1146,7 +1146,14 @@ const Town = { // todo - change act in a3 if we are next to the wp as it's faster than going all the way to Alkor // todo - compare distance Ormus -> Alkor compared to Ormus -> WP -> Akara - let potDealer = ["Akara", "Lysander", "Alkor", "Jamella", "Malah"][me.act - 1]; + const potDealers = new Map([ + [1, NPC.Akara], + [2, NPC.Lysander], + [3, NPC.Alkor], + [4, NPC.Jamella], + [5, NPC.Malah], + ]); + let potDealer = potDealers.get(me.act); switch (type) { case sdk.items.ThawingPotion: @@ -1168,13 +1175,18 @@ const Town = { break; } + if (potDealer === NPC.Alkor && Town.getDistance(potDealer) > 10) { + Town.goToTown(me.highestAct >= 4 ? 4 : 1); + potDealer = potDealers.get(me.act); + } + try { - if (!!npc && npc.name.toLowerCase() === NPC[potDealer] && !getUIFlag(sdk.uiflags.Shop)) { + if (!!npc && npc.name.toLowerCase() === potDealer && !getUIFlag(sdk.uiflags.Shop)) { if (!npc.startTrade("Shop")) throw new Error("Failed to open " + npc.name + " trade menu"); } else { me.cancelUIFlags(); - Town.move(NPC[potDealer]); - npc = Game.getNPC(NPC[potDealer]); + Town.move(potDealer); + npc = Game.getNPC(potDealer); if (!npc || !npc.openMenu() || !npc.startTrade("Shop")) { throw new Error("Failed to open " + npc.name + " trade menu"); From d99c2f5edf0e97afa342733ad190639285467ac5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:31:09 -0400 Subject: [PATCH 186/758] General cleanup - added special case for summoner in SkillData - added `me.blackendTemple` - swapping out usage of `Pather.accessToAct` for `me.accessToAct` --- d2bs/kolbot/libs/core/Attacks/Assassin.js | 20 +++++- d2bs/kolbot/libs/core/GameData/SkillData.js | 42 ++++++++++- d2bs/kolbot/libs/core/Item.js | 21 ++---- d2bs/kolbot/libs/core/Loader.js | 2 +- d2bs/kolbot/libs/core/Me.js | 77 ++++++++++++++++----- d2bs/kolbot/libs/core/Misc.js | 10 +-- d2bs/kolbot/libs/core/NTItemParser.js | 15 ++-- d2bs/kolbot/libs/core/Packet.js | 17 +++-- d2bs/kolbot/libs/core/Runewords.js | 10 +-- d2bs/kolbot/libs/core/Town.js | 6 +- d2bs/kolbot/libs/core/Util.js | 3 +- d2bs/kolbot/libs/scripts/Countess.js | 2 +- d2bs/kolbot/libs/scripts/Nihlathak.js | 2 +- d2bs/kolbot/libs/scripts/Treehead.js | 2 +- d2bs/kolbot/threads/Party.js | 2 +- 15 files changed, 160 insertions(+), 71 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index deebc5d38..0de6152ae 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -9,6 +9,11 @@ const ClassAttack = { lastTrapPos: {}, trapRange: 20, + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ doAttack: function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); @@ -75,7 +80,9 @@ const ClassAttack = { } // 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)) { timedSkill = checkSkill; @@ -86,7 +93,9 @@ const ClassAttack = { } // 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, classid)) { untimedSkill = checkSkill; @@ -155,7 +164,12 @@ const ClassAttack = { Precast.doPrecast(false); }, - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ doCast: function (unit, timedSkill = -1, untimedSkill = -1) { // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 586412759..98a84fe87 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -1310,7 +1310,7 @@ /** @type {number} */ this.reqLevel = getBaseStat("skills", skillId, "reqlevel"); /** @type {number[]} */ - this.preReqs = (() => { + this.preReqs = (function () { let preReqs = []; for (let t = sdk.stats.PreviousSkillLeft; t >= sdk.stats.PreviousSkillRight; t--) { @@ -1479,6 +1479,46 @@ SkillData.set(i, new Skill(i)); } + /** + * Not an actual skill, but used for pure summoner so needs to be handled + */ + SkillData.set(sdk.skills.Summoner, new function () { + this.skillId = sdk.skills.Summoner; + this.reqLevel = 1; + this.preReqs = []; + this.damageType = "physical"; + this._range = 40; + this._AoE = 0; + this._duration = 0; + this._manaCost = 0; + this._mana = 0; + this._minMana = 0; + this._lvlMana = 0; + this._manaShift = 0; + this._bestSlot = 0; + this._dmg = 0; + this._hardPoints = 0; + this._softPoints = 0; + this._checked = false; + + this.duration = function () { + return 0; + }; + this.manaCost = function () { + return 0; + }; + this.range = function () { + return 40; + }; + this.AoE = function () { + return 0; + }; + this.have = function () { + return true; + }; + this.reset = function () {}; + }); + // Export module.exports = SkillData; })(module); diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index 7194a9cd1..fd0e514b2 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -109,23 +109,14 @@ const Item = { }, getEquippedItem: function (bodyLoc) { - let item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - if (item.bodylocation === bodyLoc) { - return { - classid: item.classid, - tier: NTIP.GetTier(item) - }; - } - } while (item.getNext()); - } + let item = me.getItemsEx(-1, sdk.items.mode.Equipped) + .filter(function (item) { + return item.bodylocation === bodyLoc; + }).first(); - // Don't have anything equipped in there return { - classid: -1, - tier: -1 + classid: item ? item.classid : -1, + tier: item ? NTIP.GetTier(item) : -1 }; }, diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 71d8867f7..6b673eb9b 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -97,7 +97,7 @@ const Loader = { } // handle getting cube here instead of from Cubing.doCubing - if (Config.Cubing && !me.getItem(sdk.quest.item.Cube) && Pather.accessToAct(2)) { + if (Config.Cubing && !me.getItem(sdk.quest.item.Cube) && me.accessToAct(2)) { // we can actually get the cube - fixes bug causing level 1's to crash Loader.runScript("GetCube"); } diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 04748c232..71832d373 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -12,13 +12,17 @@ * @desciption Set me.runwalk to 0 (walk) * @returns {void} */ -me.walk = () => me.runwalk = sdk.player.move.Walk; +me.walk = function () { + me.runwalk = sdk.player.move.Walk; +}; /** * @desciption Set me.runwalk to 1 (run) * @returns {void} */ -me.run = () => me.runwalk = sdk.player.move.Run; +me.run = function () { + me.runwalk = sdk.player.move.Run; +}; /** * @description Calling me.ping can bug sometimes so check if game is in ready state. @@ -96,7 +100,8 @@ me.cancelUIFlags = function () { * @returns {boolean} */ me.switchWeapons = function (slot) { - if (this.gametype === sdk.game.gametype.Classic || (slot !== undefined && this.weaponswitch === slot)) { + if (this.gametype === sdk.game.gametype.Classic + || (slot !== undefined && this.weaponswitch === slot)) { return true; } @@ -106,8 +111,11 @@ me.switchWeapons = function (slot) { let originalSlot = this.weaponswitch; let switched = false; - let packetHandler = (bytes) => bytes.length > 0 - && bytes[0] === sdk.packets.recv.WeaponSwitch && (switched = true) && false; // false to not block + /** @param {number[]} bytes */ + const packetHandler = function (bytes) { + return bytes.length > 0 + && bytes[0] === sdk.packets.recv.WeaponSwitch && (switched = true) && false; // false to not block + }; try { addEventListener("gamepacket", packetHandler); @@ -169,7 +177,9 @@ me.castingDuration = function (skillId, fcr = me.FCR, charClass = me.classid) { }; 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(i => i.bodylocation === weaponLoc) + .first(); return !!currItem ? currItem.getStat(sdk.stats.Quantity) : 0; }; @@ -235,24 +245,42 @@ me.needPotions = function () { /** @returns {ItemUnit | null} */ me.getTpTool = function () { const items = me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((item) => item.isInInventory - && [sdk.items.ScrollofTownPortal, sdk.items.TomeofTownPortal].includes(item.classid)); + .filter(function (item) { + return item.isInInventory + && [ + sdk.items.ScrollofTownPortal, + sdk.items.TomeofTownPortal + ].includes(item.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; + }); return scroll ? scroll : null; }; /** @returns {ItemUnit | null} */ me.getIdTool = function () { const items = me.getItemsEx() - .filter((i) => i.isInInventory && [sdk.items.ScrollofIdentify, sdk.items.TomeofIdentify].includes(i.classid)); + .filter(function (i) { + return i.isInInventory + && [ + sdk.items.ScrollofIdentify, + sdk.items.TomeofIdentify + ].includes(i.classid); + }); if (!items.length) return null; - let tome = items - .find((i) => i.isInInventory && i.classid === sdk.items.TomeofIdentify && i.getStat(sdk.stats.Quantity) > 0); + let tome = items.find(function (i) { + return i.classid === sdk.items.TomeofIdentify && i.getStat(sdk.stats.Quantity) > 0; + }); if (tome) return tome; - let scroll = items.find((i) => i.isInInventory && i.classid === sdk.items.ScrollofIdentify); + let scroll = items.find(function (i) { + return i.classid === sdk.items.ScrollofIdentify; + }); return scroll ? scroll : null; }; @@ -285,7 +313,9 @@ me.needHealing = function () { sdk.states.Weaken, sdk.states.Decrepify, sdk.states.LowerResist - ].some((state) => me.getState(state))); + ].some(function (state) { + return me.getState(state); + })); }; /** @@ -300,7 +330,9 @@ me.getTome = function (id) { me.getUnids = function () { return me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((item) => item.isInInventory && !item.identified); + .filter(function (item) { + return item.isInInventory && !item.identified; + }); }; // Identify items while in the field if we have a id tome @@ -627,7 +659,9 @@ Object.defineProperties(me, { QuestData.get(sdk.quest.id.AbleToGotoActIII).complete(), QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(), QuestData.get(sdk.quest.id.AbleToGotoActV).complete()]; - let index = acts.findIndex((i) => !i); // find first false, returns between 1 and 5 + let index = acts.findIndex(function (i) { + return !i; + }); // find first false, returns between 1 and 5 return index === -1 ? 5 : index; } }, @@ -638,7 +672,9 @@ Object.defineProperties(me, { // check if we've completed main part but not used our reward if ([ - sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, sdk.quest.id.ToolsoftheTrade + sdk.quest.id.RescueonMountArreat, + sdk.quest.id.SiegeOnHarrogath, + sdk.quest.id.ToolsoftheTrade ].includes(i) && QuestData.get(i).complete(true)) { return i; } @@ -727,6 +763,11 @@ Object.defineProperties(me, { return QuestData.get(sdk.quest.id.KhalimsWill).complete(); } }, + blackendTemple: { + get: function () { + return QuestData.get(sdk.quest.id.TheBlackenedTemple).complete(); + } + }, mephisto: { get: function () { return QuestData.get(sdk.quest.id.AbleToGotoActIV).complete(); diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index b5f59f7fc..1301adee0 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -173,7 +173,9 @@ const Misc = { let myPartyId = party.partyid; do { - if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name) { + if (party.partyid !== sdk.party.NoParty + && party.partyid === myPartyId + && party.name !== me.name) { print(party.name); count += 1; } @@ -829,9 +831,3 @@ const Misc = { return questStates; } }; -// (function() { -// // todo figure out why the Misc.d.ts file only works right if Misc is wrapped in a IEFE - -// // export to global scope -// global.Misc = Misc; -// })(); diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 069064116..ecbc836c1 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -91,7 +91,6 @@ NTIP.OpenFile = function (filepath, notify) { }; NTIP.CheckQuantityOwned = function (item_type, item_stats) { - let item; let num = 0; let items = me.getItemsEx(); @@ -101,23 +100,19 @@ NTIP.CheckQuantityOwned = function (item_type, item_stats) { return 0; } - for (let i = 0; i < items.length; i += 1) { - if (items[i].mode === sdk.items.mode.inStorage && items[i].location === sdk.storage.Stash) { - item = items[i]; - + for (let item of items) { + if (item.mode === sdk.items.mode.inStorage + && item.location === sdk.storage.Stash) { if ((item_type !== null && typeof item_type === "function" && item_type(item)) || item_type === null) { if ((item_stats !== null && typeof item_stats === "function" && item_stats(item)) || item_stats === null) { num += 1; } } - } else if (items[i].mode === sdk.items.mode.inStorage && items[i].location === sdk.storage.Inventory) { // inv check - item = items[i]; - + } else if (item.mode === sdk.items.mode.inStorage + && item.location === sdk.storage.Inventory) { // inv check if ((item_type !== null && typeof item_type === "function" && item_type(item)) || item_type === null) { if ((item_stats !== null && typeof item_stats === "function" && item_stats(item)) || item_stats === null) { - //if (Config.Inventory[items[i].y][items[i].x] > 0) { // we check only space that is supposed to be free num += 1; - //} } } } diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 454bb0ce8..0178ee8cc 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -17,7 +17,9 @@ const Packet = { let pingDelay = (me.gameReady ? me.ping : 125); for (let i = 0; i < 5; i += 1) { - unit.distance > 4 && Pather.moveToUnit(unit); + if (getDistance(me, unit) > 4) { + Pather.moveNearUnit(unit, 4); + } Packet.entityInteract(unit); let tick = getTickCount(); @@ -36,8 +38,11 @@ const Packet = { delay(100); } - // sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, unit.gid); - new PacketBuilder().byte(sdk.packets.send.NPCInit).dword(1).dword(unit.gid).send(); + new PacketBuilder() + .byte(sdk.packets.send.NPCInit) + .dword(1) + .dword(unit.gid) + .send(); delay(pingDelay + 1 * 2); Packet.cancelNPC(unit); delay(pingDelay + 1 * 2); @@ -179,8 +184,12 @@ const Packet = { let itemCount = me.itemcount; let npc = getInteractedNPC(); - if (!npc) return false; + let _npcs = Town.tasks.get(me.act); + if (![_npcs.Shop, _npcs.Gamble, _npcs.Repair, _npcs.Key].includes(npc.name.toLowerCase())) { + console.warn("Unit.sell: NPC is not a shop, gamble, repair or key NPC."); + return false; + } for (let i = 0; i < 5; i += 1) { sendPacket(1, sdk.packets.send.NPCSell, 4, npc.gid, 4, unit.gid, 4, 0, 4, 0); diff --git a/d2bs/kolbot/libs/core/Runewords.js b/d2bs/kolbot/libs/core/Runewords.js index 335a347f6..8bfb89a0b 100644 --- a/d2bs/kolbot/libs/core/Runewords.js +++ b/d2bs/kolbot/libs/core/Runewords.js @@ -18,14 +18,16 @@ const Runewords = { Runewords.pickitEntries = []; // initiate pickit entries - for (let i = 0; i < Config.KeepRunewords.length; i += 1) { + for (let entry of Config.KeepRunewords) { let info = { file: "Character Config", - line: Config.KeepRunewords[i] + line: entry }; - let parsedLine = NTIP.ParseLineInt(Config.KeepRunewords[i], info); - parsedLine && this.pickitEntries.push(NTIP.ParseLineInt(Config.KeepRunewords[i], info)); + let parsedLine = NTIP.ParseLineInt(entry, info); + if (parsedLine) { + this.pickitEntries.push(parsedLine); + } } // change text to classid diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index c7579df3b..ab61440b9 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1257,7 +1257,7 @@ const Town = { if (!this.wantKeys()) return true; // avoid Hratli - me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); + me.act === 3 && this.goToTown(me.accessToAct(4) ? 4 : 2); let npc = this.initNPC("Key", "buyKeys"); if (!npc) return false; @@ -1439,7 +1439,7 @@ const Town = { for (let i = 0; i < repairAction.length; i += 1) { switch (repairAction[i]) { case "repair": - me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); + me.act === 3 && this.goToTown(me.accessToAct(4) ? 4 : 2); npc = this.initNPC("Repair", "repair"); if (!npc) return false; me.repair(); @@ -1575,7 +1575,7 @@ const Town = { let preArea = me.area; // avoid Aheara - me.act === 3 && this.goToTown(Pather.accessToAct(4) ? 4 : 2); + me.act === 3 && this.goToTown(me.accessToAct(4) ? 4 : 2); let npc = this.initNPC("Merc", "reviveMerc"); if (!npc) return false; diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 58bc6fd91..0f574c7bb 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -147,7 +147,8 @@ throw new Error("PacketBuilder must be called with 'new' operator!"); } - let queue = [], count = 0; + let queue = []; + let count = 0; // accepts any number of arguments let enqueue = (type, size) => (...args) => { diff --git a/d2bs/kolbot/libs/scripts/Countess.js b/d2bs/kolbot/libs/scripts/Countess.js index 6c6b64713..47014a720 100644 --- a/d2bs/kolbot/libs/scripts/Countess.js +++ b/d2bs/kolbot/libs/scripts/Countess.js @@ -5,7 +5,7 @@ * */ -function Countess() { +function Countess () { Town.doChores(); Pather.useWaypoint(sdk.areas.BlackMarsh); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js index c27ba2bc1..21a5b8f0d 100644 --- a/d2bs/kolbot/libs/scripts/Nihlathak.js +++ b/d2bs/kolbot/libs/scripts/Nihlathak.js @@ -5,7 +5,7 @@ * */ -function Nihlathak() { +function Nihlathak () { Town.goToTown(5); Town.doChores(); diff --git a/d2bs/kolbot/libs/scripts/Treehead.js b/d2bs/kolbot/libs/scripts/Treehead.js index a1c850e25..31c31e4b9 100644 --- a/d2bs/kolbot/libs/scripts/Treehead.js +++ b/d2bs/kolbot/libs/scripts/Treehead.js @@ -5,7 +5,7 @@ * */ -function Treehead() { +function Treehead () { Town.doChores(); Pather.useWaypoint(sdk.areas.DarkWood); Precast.doPrecast(true); diff --git a/d2bs/kolbot/threads/Party.js b/d2bs/kolbot/threads/Party.js index b0eae0902..6b585ed02 100644 --- a/d2bs/kolbot/threads/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -18,7 +18,7 @@ include("systems/gameaction/GameAction.js"); // party thread specific include("oog/ShitList.js"); -function main() { +function main () { Config.init(); /** @type {string[][]} */ From 10f7ba15f4ceedbf99f7415ce909288013f2af8c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:36:05 -0400 Subject: [PATCH 187/758] Update Pather.js - added PathAction.update prototype - use useUnit/openUnit prototypes - add Precast.enabled check for using blaze - add allowNodeActions flag to PathSettings --- d2bs/kolbot/libs/core/Pather.js | 190 ++++++++++---------------------- 1 file changed, 58 insertions(+), 132 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 86559bdaf..2ed89d5b3 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -10,9 +10,7 @@ * @todo this needs to be re-worked */ const NodeAction = { - /** - * @type {number[]} - */ + /** @type {number[]} */ shrinesToIgnore: [], enabled: true, @@ -94,9 +92,7 @@ const NodeAction = { }; const PathDebug = { - /** - * @type {Line[]} - */ + /** @type {Line[]} */ hooks: [], enableHooks: false, @@ -152,8 +148,11 @@ const Pather = { teleDistance: 40, lastPortalTick: 0, cancelFlags: [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, sdk.uiflags.Waypoint, - sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.TradePrompt, sdk.uiflags.Stash, sdk.uiflags.Cube + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, + sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, + sdk.uiflags.Waypoint, sdk.uiflags.Party, + sdk.uiflags.Shop, sdk.uiflags.Quest, + sdk.uiflags.TradePrompt, sdk.uiflags.Stash, sdk.uiflags.Cube ], wpAreas: [ sdk.areas.RogueEncampment, sdk.areas.ColdPlains, sdk.areas.StonyField, @@ -248,6 +247,7 @@ const Pather = { /** * @typedef {object} pathSettings + * @property {boolean} [allowNodeActions] * @property {boolean} [allowTeleport] * @property {boolean} [allowClearing] * @property {boolean} [allowTown] @@ -279,6 +279,7 @@ const Pather = { const settings = Object.assign({}, { clearSettings: { }, + allowNodeActions: true, allowTeleport: true, allowClearing: true, allowTown: true, @@ -317,6 +318,13 @@ const Pather = { 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 node = { x: target.x, y: target.y }; @@ -328,7 +336,9 @@ const Pather = { getUIFlag(this.cancelFlags[i]) && me.cancel(); } - if (typeof target.x !== "number" || typeof target.y !== "number") throw new Error("move: Coords must be numbers"); + if (typeof target.x !== "number" || typeof target.y !== "number") { + throw new Error("move: Coords must be numbers"); + } if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, sdk.collision.BlockMissile, 5)) return true; let useTeleport = settings.allowTeleport && this.useTeleport(); @@ -381,7 +391,7 @@ const Pather = { let adjustedNode = this.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); } @@ -392,7 +402,7 @@ const Pather = { if (useTeleport && tpMana <= me.mp ? this.teleportTo(node.x, node.y) : this.walkTo(node.x, node.y, (fail > 0 || me.inTown) ? 2 : 4)) { - if (!me.inTown) { + if (settings.allowNodeActions && !me.inTown) { if (Pather.recursion) { Pather.recursion = false; try { @@ -407,35 +417,19 @@ const Pather = { } } else { if (!me.inTown) { - /** - * @todo I think some of this needs to be re-worked, I've noticed recursive Attacking/Picking - */ - 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 && (this.kickBarrels(node.x, node.y) || this.openDoors(node.x, node.y))) { continue; } - if (fail > 0 && (!useTeleport || tpMana > me.mp)) { + 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)) { - console.debug("Cleared Node"); - cleared.at = getTickCount(); - [cleared.node.x, cleared.node.y] = [node.x, node.y]; + if (Attack.clear(10, null, null, null, settings.allowPicking) === Attack.Result.SUCCESS) { + cleared.update(node); } } } @@ -447,8 +441,7 @@ const Pather = { || 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); } } } @@ -465,8 +458,7 @@ const Pather = { || 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); } } } @@ -595,7 +587,9 @@ const Pather = { // Check if I have a stamina potion and use it if I do if (me.staminaPercent <= 20) { let stam = me.getItemsEx(-1, sdk.items.mode.inStorage) - .filter((i) => i.classid === sdk.items.StaminaPotion && i.isInInventory) + .filter(function (i) { + return i.classid === sdk.items.StaminaPotion && i.isInInventory; + }) .first(); !!stam && !me.deadOrInSequence && stam.use(); } @@ -618,7 +612,7 @@ const Pather = { } } - if (Skill.canUse(sdk.skills.Blaze) + if (Precast.enabled && Skill.canUse(sdk.skills.Blaze) && me.mp > (Skill.getManaCost(sdk.skills.Blaze) * 2) && !me.getState(sdk.states.Blaze)) { Skill.cast(sdk.skills.Blaze); @@ -846,7 +840,9 @@ const Pather = { clearPath === undefined && (clearPath = false); pop === undefined && (pop = false); - if (!unit || !unit.hasOwnProperty("x") || !unit.hasOwnProperty("y")) throw new Error("moveToUnit: Invalid unit."); + if (!unit || !unit.hasOwnProperty("x") || !unit.hasOwnProperty("y")) { + throw new Error("moveToUnit: Invalid unit."); + } (unit instanceof PresetUnit) && (unit = { x: unit.roomx * 5 + unit.x, y: unit.roomy * 5 + unit.y }); let [x, y] = [unit.x + offX, unit.y + offY]; @@ -1209,27 +1205,12 @@ const Pather = { * @returns {boolean} */ openUnit: function (type, id) { + /** @type {ObjectUnit | Tile} */ let unit = Misc.poll(() => getUnit(type, id), 1000, 200); if (!unit) throw new Error("openUnit: Unit not found. ID: " + unit); if (unit.mode !== sdk.objects.mode.Inactive) return true; - for (let i = 0; i < 3; i += 1) { - unit.distance > 5 && this.moveToUnit(unit); - - delay(300); - Packet.entityInteract(unit); - - if (Misc.poll(() => unit.mode !== sdk.objects.mode.Inactive, 2000, 60)) { - delay(100); - - return true; - } - - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); - !!coord && this.moveTo(coord.x, coord.y); - } - - return false; + return unit.openUnit(); }, /** @@ -1240,9 +1221,8 @@ const Pather = { * @todo should use an object as param, or be changed to able to take an already found unit as a param */ useUnit: function (type, id, targetArea) { + /** @type {ObjectUnit | Tile} */ let unit = Misc.poll(() => getUnit(type, id), 2000, 200); - let preArea = me.area; - if (!unit) { throw new Error( "useUnit: Unit not found. TYPE: " + type + " ID: " + id @@ -1250,71 +1230,8 @@ const Pather = { + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "") ); } - - MainLoop: - for (let i = 0; i < 5; i += 1) { - let usetk = (i < 2 && Skill.useTK(unit)); - - if (unit.distance > 5) { - usetk ? this.moveNearUnit(unit, 20) : this.moveToUnit(unit); - // try to activate it once - if (usetk && i === 0 && unit.mode === sdk.objects.mode.Inactive && unit.distance < 21) { - Packet.telekinesis(unit); - } - } - - if (type === sdk.unittype.Object && unit.mode === sdk.objects.mode.Inactive) { - if (me.inArea(sdk.areas.Travincal) && targetArea === sdk.areas.DuranceofHateLvl1) { - if (me.getQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) !== 1) { - throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); - } - } else if (me.inArea(sdk.areas.ArreatSummit) && targetArea === sdk.areas.WorldstoneLvl1) { - if (me.getQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed) !== 1) { - throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); - } - } - - me.inArea(sdk.areas.A3SewersLvl1) - ? this.openUnit(sdk.unittype.Object, sdk.objects.SewerLever) - : this.openUnit(sdk.unittype.Object, id); - } - - if (type === sdk.unittype.Object && id === sdk.objects.RedPortalToAct4 && me.inArea(sdk.areas.DuranceofHateLvl3) - && targetArea === sdk.areas.PandemoniumFortress - && me.getQuest(sdk.quest.id.TheGuardian, sdk.quest.states.Completed) !== 1) { - throw new Error("useUnit: Incomplete quest. TargetArea: " + getAreaName(targetArea)); - } - - delay(300); - type === sdk.unittype.Stairs - ? Misc.click(0, 0, unit) - : usetk && unit.distance > 5 - ? Packet.telekinesis(unit) - : Packet.entityInteract(unit); - delay(300); - - let tick = getTickCount(); - - while (getTickCount() - tick < 3000) { - if ((!targetArea && me.area !== preArea) || me.area === targetArea) { - delay(200); - - break MainLoop; - } - - delay(10); - } - - i > 2 && Packet.flash(me.gid); - let coord = CollMap.getRandCoordinate(me.x, -1, 1, me.y, -1, 1, 3); - !!coord && this.moveTo(coord.x, coord.y); - } - - while (!me.idle && !me.gameReady) { - delay(40); - } - - return targetArea ? me.area === targetArea : me.area !== preArea; + + return unit.useUnit(targetArea); }, allowBroadcast: true, @@ -1534,7 +1451,9 @@ const Pather = { if (!tpTool) return false; let oldPortal = getUnits(sdk.unittype.Object, "portal") - .filter((p) => p.getParent() === me.name) + .filter(function (p) { + return p.getParent() === me.name; + }) .first(); !!oldPortal && (oldGid = oldPortal.gid); @@ -1544,7 +1463,9 @@ const Pather = { while (getTickCount() - tick < Math.max(500 + i * 100, pingDelay * 2 + 100)) { let portal = getUnits(sdk.unittype.Object, "portal") - .filter((p) => p.getParent() === me.name && p.gid !== oldGid) + .filter(function (p) { + return p.getParent() === me.name && p.gid !== oldGid; + }) .first(); if (portal) { @@ -1610,7 +1531,7 @@ const Pather = { : Pather.moveNearUnit(portal, 20); } if (Packet.telekinesis(portal)) { - if (Misc.poll(() => { + if (Misc.poll(function () { if (me.area !== preArea) { Pather.lastPortalTick = getTickCount(); delay(100); @@ -1786,6 +1707,7 @@ const Pather = { }, /** + * @deprecated use `me.accessToAct(act)` instead * @param {number} act - the act number to check for access * @returns {boolean} */ @@ -1865,8 +1787,6 @@ const Pather = { */ journeyTo: function (area) { if (area === undefined) return false; - console.time("journeyTo"); - let target, retry = 0; if (area !== sdk.areas.DurielsLair) { @@ -1876,8 +1796,10 @@ const Pather = { this.wpAreas.indexOf(me.area) === -1 && (target.useWP = true); } - console.info(true, "Course :: " + target.course); - area === sdk.areas.PandemoniumFortress && me.inArea(sdk.areas.DuranceofHateLvl3) && (target.useWP = false); + console.info(true, "Course :: " + target.course, "journeyTo"); + if (area === sdk.areas.PandemoniumFortress && me.inArea(sdk.areas.DuranceofHateLvl3)) { + target.useWP = false; + } target.useWP && Town.goToTown(); // handle variable flayer jungle entrances @@ -1913,7 +1835,8 @@ const Pather = { if (!me.inTown) { Precast.doPrecast(false); - if (this.wpAreas.includes(currArea) && !getWaypoint(this.wpAreas.indexOf(currArea))) { + if (this.wpAreas.includes(currArea) + && !getWaypoint(this.wpAreas.indexOf(currArea))) { this.getWP(currArea); } } @@ -1940,9 +1863,12 @@ const Pather = { this.usePortal(null); } else if (currArea === sdk.areas.ArcaneSanctuary && targetArea === sdk.areas.PalaceCellarLvl3) { // Arcane Sanctuary -> Palace Cellar 3 - Skill.haveTK - ? this.moveNearPreset(currArea, sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal, 20) - : this.moveToPreset(currArea, sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal); + this.moveNearPreset( + currArea, + sdk.unittype.Object, + sdk.objects.ArcaneSanctuaryPortal, + (Skill.haveTK ? 20 : 5) + ); unit = Misc.poll(() => Game.getObject(sdk.objects.ArcaneSanctuaryPortal)); unit && Pather.useUnit(sdk.unittype.Object, sdk.objects.ArcaneSanctuaryPortal, sdk.areas.PalaceCellarLvl3); } else if (currArea === sdk.areas.ArcaneSanctuary && targetArea === sdk.areas.CanyonofMagic) { From 7292fc39ced3675f580e99b6e77813b51f9dc3e7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:39:22 -0400 Subject: [PATCH 188/758] Update Precast.js - add needSooon to durations precasts - change `boneArmor.armorPercent` -> `boneArmor.remaining` --- d2bs/kolbot/libs/core/Precast.js | 133 +++++++++++++++++++------------ 1 file changed, 83 insertions(+), 50 deletions(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 329687f50..1111ac487 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -19,30 +19,47 @@ const Precast = { coldArmor: { best: false, duration: 0, - tick: 0 + tick: 0, + needSoon: function (seconds = 30) { + return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); + }, }, boneArmor: { max: 0, - armorPercent: function () { - return this.max > 0 ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) : 100; + remaining: function () { + return this.max > 0 + ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) + : 100; }, }, holyShield: { canUse: false, duration: 0, - tick: 0 + tick: 0, + needSoon: function (seconds = 30) { + return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); + }, }, shout: { duration: 0, - tick: 0 + tick: 0, + needSoon: function (seconds = 30) { + return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); + }, }, battleOrders: { duration: 0, - tick: 0 + tick: 0, + needSoon: function () { + return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); + }, }, battleCommand: { duration: 0, - tick: 0 + tick: 0, + needSoon: function () { + return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); + }, }, }, @@ -55,11 +72,13 @@ const Precast = { */ warCries: function (skillId, x, y) { if (!skillId || x === undefined) return false; - const states = {}; - states[sdk.skills.Shout] = sdk.states.Shout; - states[sdk.skills.BattleOrders] = sdk.states.BattleOrders; - states[sdk.skills.BattleCommand] = sdk.states.BattleCommand; - if (states[skillId] === undefined) return false; + if (![sdk.skills.Shout, sdk.skills.BattleOrders, sdk.skills.BattleCommand].includes(skillId)) { + return false; + } + const _state = Skill.getState(skillId); + const _stateCheck = function () { + return me.getState(_state); + }; for (let i = 0; i < 3; i++) { try { @@ -67,7 +86,8 @@ const Precast = { throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); } // Right hand + No Shift - let clickType = 3, shift = sdk.clicktypes.shift.NoShift; + let clickType = 3; + let shift = sdk.clicktypes.shift.NoShift; MainLoop: for (let n = 0; n < 3; n += 1) { @@ -88,7 +108,7 @@ const Precast = { delay(10); } - if (Misc.poll(() => me.getState(states[skillId]), 300, 50)) return true; + if (Misc.poll(_stateCheck, 300, 50)) return true; } catch (e) { console.error(e); return false; @@ -151,7 +171,7 @@ const Precast = { getBetterSlot: function (skillId) { if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; - let [classid, skillTab] = (() => { + let [classid, skillTab] = (function () { switch (skillId) { case sdk.skills.FrozenArmor: case sdk.skills.ShiverArmor: @@ -220,7 +240,9 @@ const Precast = { }, cast: function (skillId, x = me.x, y = me.y, dontSwitch = false) { - if (!skillId || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) return false; + if (!skillId || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { + return false; + } if (Skill.getManaCost(skillId) > me.mp) return false; let swap = me.weaponswitch; @@ -418,17 +440,21 @@ const Precast = { // Force BO 30 seconds before it expires if (Precast.haveCTA > -1) { forceBo = (force || partial - || (getTickCount() - Precast.skills.battleOrders.tick >= Precast.skills.battleOrders.duration - 30000) + || Precast.skills.battleOrders.needSoon() || !me.getState(sdk.states.BattleCommand)); forceBo && this.precastCTA(forceBo); } - const needToCast = (state) => (force || partial || !me.getState(state)); + /** @param {number} state */ + const needToCast = function (state) { + return (force || partial || !me.getState(state)); + }; switch (me.classid) { case sdk.player.class.Amazon: - Skill.canUse(sdk.skills.Valkyrie) && (buffSummons = this.summon(sdk.skills.Valkyrie, sdk.summons.type.Valkyrie)); - + if (Skill.canUse(sdk.skills.Valkyrie)) { + buffSummons = Precast.summon(sdk.skills.Valkyrie, sdk.summons.type.Valkyrie); + } break; case sdk.player.class.Sorceress: if (Skill.canUse(sdk.skills.ThunderStorm) && needToCast(sdk.states.ThunderStorm)) { @@ -444,27 +470,33 @@ const Precast = { ? Config.UseColdArmor : (Precast.skills.coldArmor.best || -1)); - if (Precast.skills.coldArmor.tick > 0 && Precast.skills.coldArmor.duration > Time.seconds(45)) { - if (getTickCount() - Precast.skills.coldArmor.tick >= Precast.skills.coldArmor.duration - Time.seconds(30)) { + if (Precast.skills.coldArmor.needSoon(45)) { + if (Precast.skills.coldArmor.needSoon(25)) { force = true; } } switch (choosenSkill) { case sdk.skills.FrozenArmor: if (needToCast(sdk.states.FrozenArmor)) { - Precast.cast(sdk.skills.FrozenArmor) && (Precast.skills.coldArmor.tick = getTickCount()); + if (Precast.cast(sdk.skills.FrozenArmor)) { + Precast.skills.coldArmor.tick = getTickCount(); + } } break; case sdk.skills.ChillingArmor: if (needToCast(sdk.states.ChillingArmor)) { - Precast.cast(sdk.skills.ChillingArmor) && (Precast.skills.coldArmor.tick = getTickCount()); + if (Precast.cast(sdk.skills.ChillingArmor)) { + Precast.skills.coldArmor.tick = getTickCount(); + } } break; case sdk.skills.ShiverArmor: if (needToCast(sdk.states.ShiverArmor)) { - Precast.cast(sdk.skills.ShiverArmor) && (Precast.skills.coldArmor.tick = getTickCount()); + if (Precast.cast(sdk.skills.ShiverArmor)) { + Precast.skills.coldArmor.tick = getTickCount(); + } } break; @@ -480,24 +512,24 @@ const Precast = { break; case sdk.player.class.Necromancer: if (Skill.canUse(sdk.skills.BoneArmor) - && (force || this.skills.boneArmor.armorPercent() < 75 || !me.getState(sdk.states.BoneArmor))) { + && (force || this.skills.boneArmor.remaining() < 75 || !me.getState(sdk.states.BoneArmor))) { this.cast(sdk.skills.BoneArmor); - this.skills.boneArmor.max === 0 && (this.skills.boneArmor.max = me.getStat(sdk.stats.SkillBoneArmorMax)); + if (this.skills.boneArmor.max === 0) { + this.skills.boneArmor.max = me.getStat(sdk.stats.SkillBoneArmorMax); + } } - (() => { + (function () { switch (Config.Golem) { case 1: case "Clay": - return this.summon(sdk.skills.ClayGolem, sdk.summons.type.Golem); + return Precast.summon(sdk.skills.ClayGolem, sdk.summons.type.Golem); case 2: case "Blood": - return this.summon(sdk.skills.BloodGolem, sdk.summons.type.Golem); + return Precast.summon(sdk.skills.BloodGolem, sdk.summons.type.Golem); case 3: case "Fire": - return this.summon(sdk.skills.FireGolem, sdk.summons.type.Golem); - case 0: - case "None": + return Precast.summon(sdk.skills.FireGolem, sdk.summons.type.Golem); default: return false; } @@ -508,7 +540,8 @@ const Precast = { break; case sdk.player.class.Paladin: if (Skill.canUse(sdk.skills.HolyShield) - && Precast.skills.holyShield.canUse && needToCast(sdk.states.HolyShield)) { + && Precast.skills.holyShield.canUse + && (Precast.skills.holyShield.needSoon() || needToCast(sdk.states.HolyShield))) { this.cast(sdk.skills.HolyShield); } @@ -539,53 +572,53 @@ const Precast = { this.cast(sdk.skills.CycloneArmor); } - Skill.canUse(sdk.skills.Raven) && this.summon(sdk.skills.Raven, sdk.summons.type.Raven); + Skill.canUse(sdk.skills.Raven) && Precast.summon(sdk.skills.Raven, sdk.summons.type.Raven); - buffSummons = (() => { + buffSummons = (function () { switch (Config.SummonAnimal) { case 1: case "Spirit Wolf": - return (this.summon(sdk.skills.SummonSpiritWolf, sdk.summons.type.SpiritWolf) || buffSummons); + return (Precast.summon(sdk.skills.SummonSpiritWolf, sdk.summons.type.SpiritWolf) || buffSummons); case 2: case "Dire Wolf": - return (this.summon(sdk.skills.SummonDireWolf, sdk.summons.type.DireWolf) || buffSummons); + return (Precast.summon(sdk.skills.SummonDireWolf, sdk.summons.type.DireWolf) || buffSummons); case 3: case "Grizzly": - return (this.summon(sdk.skills.SummonGrizzly, sdk.summons.type.Grizzly) || buffSummons); + return (Precast.summon(sdk.skills.SummonGrizzly, sdk.summons.type.Grizzly) || buffSummons); default: return buffSummons; } })(); - buffSummons = (() => { + buffSummons = (function () { switch (Config.SummonVine) { case 1: case "Poison Creeper": - return (this.summon(sdk.skills.PoisonCreeper, sdk.summons.type.Vine) || buffSummons); + return (Precast.summon(sdk.skills.PoisonCreeper, sdk.summons.type.Vine) || buffSummons); case 2: case "Carrion Vine": - return (this.summon(sdk.skills.CarrionVine, sdk.summons.type.Vine) || buffSummons); + return (Precast.summon(sdk.skills.CarrionVine, sdk.summons.type.Vine) || buffSummons); case 3: case "Solar Creeper": - return (this.summon(sdk.skills.SolarCreeper, sdk.summons.type.Vine) || buffSummons); + return (Precast.summon(sdk.skills.SolarCreeper, sdk.summons.type.Vine) || buffSummons); default: return buffSummons; } })(); - buffSummons = (() => { + buffSummons = (function () { switch (Config.SummonSpirit) { case 1: case "Oak Sage": // to prevent false chickens when we cast oak before getting bo-ed if (me.hardcore && !me.getState(sdk.states.BattleOrders) && me.inTown) return buffSummons; - return (this.summon(sdk.skills.OakSage, sdk.summons.type.Spirit) || buffSummons); + return (Precast.summon(sdk.skills.OakSage, sdk.summons.type.Spirit) || buffSummons); case 2: case "Heart of Wolverine": - return (this.summon(sdk.skills.HeartofWolverine, sdk.summons.type.Spirit) || buffSummons); + return (Precast.summon(sdk.skills.HeartofWolverine, sdk.summons.type.Spirit) || buffSummons); case 3: case "Spirit of Barbs": - return this.summon(sdk.skills.SpiritofBarbs, sdk.summons.type.Spirit) || buffSummons; + return Precast.summon(sdk.skills.SpiritofBarbs, sdk.summons.type.Spirit) || buffSummons; default: return buffSummons; } @@ -613,14 +646,14 @@ const Precast = { this.cast(sdk.skills.BurstofSpeed); } - buffSummons = (() => { + buffSummons = (function () { switch (Config.SummonShadow) { case 1: case "Warrior": - return this.summon(sdk.skills.ShadowWarrior, sdk.summons.type.Shadow); + return Precast.summon(sdk.skills.ShadowWarrior, sdk.summons.type.Shadow); case 2: case "Master": - return this.summon(sdk.skills.ShadowMaster, sdk.summons.type.Shadow); + return Precast.summon(sdk.skills.ShadowMaster, sdk.summons.type.Shadow); default: return false; } From 809a908fec3c43b02d3989aae6b26f967ae95c7a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 16 Jul 2023 10:44:36 -0400 Subject: [PATCH 189/758] Update Precast.js - add missing parameter --- d2bs/kolbot/libs/core/Precast.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 1111ac487..79492ec98 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -50,14 +50,14 @@ const Precast = { battleOrders: { duration: 0, tick: 0, - needSoon: function () { + needSoon: function (seconds = 30) { return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); }, }, battleCommand: { duration: 0, tick: 0, - needSoon: function () { + needSoon: function (seconds = 30) { return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); }, }, From 5779e987f08272308d32d28d990ad87233ada1eb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 19 Jul 2023 18:52:17 -0400 Subject: [PATCH 190/758] Update Pather.js - add handler when using warriv to travel --- d2bs/kolbot/libs/core/Pather.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 2ed89d5b3..23e80faca 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1267,10 +1267,9 @@ const Pather = { break; } - console.time("useWaypoint"); const destName = targetArea ? getAreaName(targetArea) : targetArea; Pather.broadcastIntent(targetArea); - console.info(true, "ÿc7targetArea: ÿc0" + destName + " ÿc7myArea: ÿc0" + getAreaName(me.area)); + console.info(true, "ÿc7targetArea: ÿc0" + destName + " ÿc7myArea: ÿc0" + getAreaName(me.area), "useWaypoint"); MainLoop: for (let i = 0; i < 12; i += 1) { @@ -1289,6 +1288,9 @@ const Pather = { if (!Misc.poll(() => me.gameReady && me.inArea(sdk.areas.RogueEncampment), 2000, 100)) { throw new Error("Failed to go to act 1 using Warriv"); } + if (me.inArea(targetArea)) { + break; + } } } } From 1153a159771ee5b8ffcb0bd1ad7d24194609b4d6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 22 Jul 2023 01:16:55 -0400 Subject: [PATCH 191/758] Update Barbarian.js - fix checkCloseMonsters function, had mismatched parenthesis - fix `reference to undefined variable walk` --- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 30 ++++++++++++++-------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index cd8d7a055..f79823ec7 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -8,7 +8,6 @@ /** * @todo * - Add howl - * - Fix item find bug */ const ClassAttack = { @@ -99,7 +98,7 @@ const ClassAttack = { if (attackSkill < 0) return Attack.Result.CANTATTACK; // check if unit became invalidated if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); + (Config.TeleSwitch || Config.FindItemSwitch) && me.switchToPrimary(); switch (attackSkill) { case sdk.skills.Whirlwind: @@ -122,7 +121,7 @@ const ClassAttack = { } if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - walk = (Skill.getRange(attackSkill) < 4 + let walk = (Skill.getRange(attackSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) ); @@ -152,9 +151,14 @@ const ClassAttack = { if (monster) { do { - if (monster.distance <= range && monster.attackable && !checkCollision(me, monster, sdk.collision.Ranged) - && (Attack.checkResist(monster, monster.isSpecial ? mainAttElm : secAttElm)) - || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, secAttElm))) { + if (monster.distance <= range + && monster.attackable + && !checkCollision(me, monster, sdk.collision.Ranged) + && ( + Attack.checkResist(monster, monster.isSpecial ? mainAttElm : secAttElm) + || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, secAttElm)) + ) + ) { return true; } } while (monster.getNext()); @@ -188,6 +192,7 @@ const ClassAttack = { while (corpseList.length > 0) { if (this.checkCloseMonsters(5)) { + console.debug("Monsters nearby, clearing"); Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); Attack.clear(10, false, false, false, false); Pather.moveToEx(orgX, orgY, { allowPicking: false }); @@ -259,17 +264,18 @@ const ClassAttack = { * @returns {boolean} */ checkCorpse: function (unit) { - if (!unit || !copyUnit(unit).x - || (unit.mode !== sdk.monsters.mode.Death && unit.mode !== sdk.monsters.mode.Dead)) { + if (!unit || !copyUnit(unit).x || !unit.dead) { return false; } if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 && unit.spectype === sdk.monsters.spectype.All) { + // why ignore all normal monsters? return false; } // monstats2 doesn't contain guest monsters info. sigh.. - if (unit.classid <= sdk.monsters.BurningDeadArcher2 && !getBaseStat("monstats2", unit.classid, "corpseSel")) { + if (unit.classid <= sdk.monsters.BurningDeadArcher2 + && !getBaseStat("monstats2", unit.classid, "corpseSel")) { return false; } @@ -278,8 +284,10 @@ const ClassAttack = { sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect ]; - return !!(unit.distance <= 25 + return (unit.distance <= 25 && !checkCollision(me, unit, sdk.collision.Ranged) - && states.every(state => !unit.getState(state))); + && states.every(function (state) { + return !unit.getState(state); + })); } }; From ac8886bd8ed98591b8656d8d3585934f01cafda4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Aug 2023 16:17:50 -0400 Subject: [PATCH 192/758] Update Storage.js - fix "item-prop" is read-only, items that had been lost/used/removed in some way prior to sorting or checking room were returning an undefined gid, causing them to enter the conditional --- d2bs/kolbot/libs/core/Storage.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index 1bae579d2..1ce2dc03b 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -6,7 +6,7 @@ * */ -(function() { +(function () { /** * @constructor * @param {string} name - container name @@ -14,7 +14,7 @@ * @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; @@ -101,10 +101,8 @@ }; Container.prototype.Reset = function () { - let h, w; - - for (h = 0; h < this.height; h += 1) { - for (w = 0; w < this.width; w += 1) { + for (let h = 0; h < this.height; h += 1) { + for (let w = 0; w < this.width; w += 1) { this.buffer[h][w] = 0; } } @@ -173,7 +171,10 @@ 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.isInStash + && item.x === 0 + && item.y === 0) { continue; // dont touch the cube } @@ -236,7 +237,7 @@ }; /** - * @param {ItemUnit} item + * @param {ItemUnit | { sizex: number, sizey: number }} item * @param {boolean} reverseX * @param {boolean} reverseY * @param {number[]} priorityClassIds @@ -259,7 +260,7 @@ Storage.Reload(); - if (item.sizex && item.sizey && item.gid === undefined) { + if (item.sizex && item.sizey && !(item instanceof Unit)) { // 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; @@ -455,7 +456,9 @@ 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: From 7b3ce07543d7702db2da7c8a1827db2ef891f48d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:06:18 -0400 Subject: [PATCH 193/758] Update Skill.js - keep better track of when charged skills are used, add check to see if we still have the item before selecting it from our charges list --- d2bs/kolbot/libs/core/Skill.js | 58 +++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 2ae1f5c2b..8a2a4d7df 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -14,6 +14,7 @@ */ const Skill = { usePvpRange: false, + /** @type {ChargedSkill[]} */ charges: [], needFloor: [ sdk.skills.Blizzard, sdk.skills.Meteor, sdk.skills.Fissure, @@ -71,17 +72,10 @@ getCharges: function () { Skill.charges = []; /** - * @typedef {Object} Charge - * @property {number} skill - * @property {number} level - * @property {number} charges - * @property {number} maxcharges - */ - /** - * @constructor - * @param {Charge} charge - * @param {ItemUnit} unit - */ + * @constructor + * @param {Charge} charge + * @param {ItemUnit} unit + */ function ChargedSkill (charge, unit) { this.skill = charge.skill; this.level = charge.level; @@ -91,6 +85,23 @@ this.unit = copyUnit(unit); } + /** @param {ItemUnit} [item] */ + ChargedSkill.prototype.update = function (item) { + if (!item) { + item = me.getItem(-1, -1, this.gid); + } + if (!item) return; + let charges = item.getStat(-2)[sdk.stats.ChargedSkill]; + if (!(charges instanceof Array)) charges = [charges]; + let charge = charges.find(c => c.skill === this.skill); + if (charge) { + this.level = charge.level; + this.charges = charge.charges; + this.maxcharges = charge.maxcharges; + this.unit = copyUnit(item); + } + }; + let item = me.getItem(-1, sdk.items.mode.Equipped); if (item) { @@ -475,7 +486,9 @@ if (typeof x === "number") { const orgDist = [x, y].distance; if (Packet.teleport(x, y)) { - return Misc.poll(() => [x, y].distance < orgDist, 300, 25); + return Misc.poll(function () { + return [x, y].distance < orgDist; + }, 300, 25); } } } @@ -527,10 +540,9 @@ // account for lag, state 121 doesn't kick in immediately if (this.isTimed(skillId)) { - Misc.poll( - () => me.skillDelay || [sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode), - 100, - 10 + Misc.poll(function () { + return me.skillDelay || [sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode); + }, 100, 10 ); } @@ -544,8 +556,14 @@ * @returns {boolean} */ castCharges: function (skillId, unit) { - if (!Skill.charges) return false; - let charge = Skill.charges.find(c => c.skill === skillId); + if (!Skill.charges.length) return false; + let charge = Skill.charges + .filter(c => c.skill === skillId && c.charges > 0) + .sort(function (a, b) { + return b.level - a.level; + }).find(function (c) { + return me.getItem(-1, sdk.items.mode.Equipped, c.gid); + }); if (!charge) return false; let item = me.getItem(-1, sdk.items.mode.Equipped, charge.gid); if (!item) return false; @@ -560,7 +578,9 @@ if (weaponSwitch !== me.weaponswitch) { me.switchWeapons(weaponSwitch); } - charge.charges--; + if (item) { + charge.update(item); + } } }, }; From 551a03af7dfca799115e41a012120f408f0cdafb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:54:52 -0400 Subject: [PATCH 194/758] Update SkillData.js - Fix missing states for sorc skills --- d2bs/kolbot/libs/core/GameData/SkillData.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 98a84fe87..8387cefba 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -224,6 +224,7 @@ skillMap.set(sdk.skills.FrozenArmor, { hand: sdk.skills.hand.Right, range: 1, + state: sdk.states.FrozenArmor, duration: () => ( ((12 * me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.HardPoints) @@ -274,6 +275,7 @@ skillMap.set(sdk.skills.ShiverArmor, { hand: sdk.skills.hand.Right, range: 1, + state: sdk.states.ShiverArmor, duration: () => ( ((12 * me.getSkill(sdk.skills.ShiverArmor, sdk.skills.subindex.SoftPoints) + 108) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) @@ -311,6 +313,7 @@ skillMap.set(sdk.skills.ThunderStorm, { hand: sdk.skills.hand.Right, range: 1, + state: sdk.states.ThunderStorm, townSkill: true, duration: () => (24 + (8 * me.getSkill(sdk.skills.ThunderStorm, sdk.skills.subindex.SoftPoints))), }); @@ -328,6 +331,7 @@ skillMap.set(sdk.skills.ChillingArmor, { hand: sdk.skills.hand.Right, range: 1, + state: sdk.states.ChillingArmor, duration: () => ( ((6 * me.getSkill(sdk.skills.ChillingArmor, sdk.skills.subindex.SoftPoints) + 138) + ((me.getSkill(sdk.skills.FrozenArmor, sdk.skills.subindex.HardPoints) From 20fbd2216289384c9e68fb7c586a22871aea7506 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:57:05 -0400 Subject: [PATCH 195/758] Update Town.js - little cleanup --- d2bs/kolbot/libs/core/Town.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index ab61440b9..2fc824072 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -2307,16 +2307,16 @@ const Town = { let fireUnit = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1TownFire); if (!fireUnit) return false; - let fire = [fireUnit.roomx * 5 + fireUnit.x, fireUnit.roomy * 5 + fireUnit.y]; - this.act[1].spot.stash = [fire[0] - 7, fire[1] - 12]; - this.act[1].spot.stash = [fire[0] - 7, fire[1] - 12]; - this.act[1].spot[NPC.Warriv] = [fire[0] - 5, fire[1] - 2]; - this.act[1].spot[NPC.Cain] = [fire[0] + 6, fire[1] - 5]; - this.act[1].spot[NPC.Kashya] = [fire[0] + 14, fire[1] - 4]; - this.act[1].spot[NPC.Akara] = [fire[0] + 56, fire[1] - 30]; - this.act[1].spot[NPC.Charsi] = [fire[0] - 39, fire[1] - 25]; - this.act[1].spot[NPC.Gheed] = [fire[0] - 34, fire[1] + 36]; - this.act[1].spot.portalspot = [fire[0] + 10, fire[1] + 18]; + const fire = fireUnit.realCoords(); + this.act[1].spot.stash = [fire.x - 7, fire.y - 12]; + this.act[1].spot.fire = [fire.x, fire.y]; + this.act[1].spot[NPC.Warriv] = [fire.x - 5, fire.y - 2]; + this.act[1].spot[NPC.Cain] = [fire.x + 6, fire.y - 5]; + this.act[1].spot[NPC.Kashya] = [fire.x + 14, fire.y - 4]; + this.act[1].spot[NPC.Akara] = [fire.x + 56, fire.y - 30]; + this.act[1].spot[NPC.Charsi] = [fire.x - 39, fire.y - 25]; + this.act[1].spot[NPC.Gheed] = [fire.x - 34, fire.y + 36]; + this.act[1].spot.portalspot = [fire.x + 10, fire.y + 18]; this.act[1].spot.waypoint = [wp.roomx * 5 + wp.x, wp.roomy * 5 + wp.y]; this.act[1].initialized = true; } @@ -2502,7 +2502,7 @@ const Town = { }, /** - * @param {1 | 2 | 3 | 4 | 5} act + * @param {Act} act * @param {boolean} [wpmenu=false] * @returns {boolean} */ From 52fc8f6a43aa8d8751114720545e6b3c376446b1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 14:23:49 -0400 Subject: [PATCH 196/758] refactor Precast - cleaned up the skills object, made it a map of PrecastSkill class - removed `Precast.warcries` - cleaned up the summons sections of `doPrecast` for each class by having the config value initialized to the actual skillid during `Skill.init` - changed `Precast.enchant` to privately keep track of chants, wanted to prevent rechanting - keep track of lastCast for each of the state based skills - track shieldGid to prevent paladins from attempting to cast holyshield without wearing a shield --- d2bs/kolbot/libs/core/Precast.js | 1113 ++++++++++++++---------------- d2bs/kolbot/libs/core/Skill.js | 166 +++-- 2 files changed, 652 insertions(+), 627 deletions(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 79492ec98..e323837bd 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -5,692 +5,631 @@ * */ -const Precast = { - enabled: true, - haveCTA: -1, - bestSlot: {}, - - // TODO: build better method of keeping track of duration based skills so we can reduce resource usage - // build obj -> figure out which skills we have -> calc duration -> assign tick of last casted -> track tick (background worker maybe?) - // would reduce checking have skill and state calls, just let tick = getTickCount(); -> obj.some((el) => tick - el.lastTick > el.duration) -> true then cast - // would probably make sense to just re-cast everything (except summons) if one of our skills is about to run out rather than do this process again 3 seconds later - skills: { - // Not sure how I want to handle cold armors - coldArmor: { - best: false, - duration: 0, - tick: 0, - needSoon: function (seconds = 30) { - return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); - }, - }, - boneArmor: { - max: 0, - remaining: function () { - return this.max > 0 - ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) - : 100; - }, - }, - holyShield: { - canUse: false, - duration: 0, - tick: 0, - needSoon: function (seconds = 30) { - return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); - }, - }, - shout: { - duration: 0, - tick: 0, - needSoon: function (seconds = 30) { - return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); - }, - }, - battleOrders: { - duration: 0, - tick: 0, - needSoon: function (seconds = 30) { - return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); - }, - }, - battleCommand: { - duration: 0, - tick: 0, - needSoon: function (seconds = 30) { - return (getTickCount() - this.tick) > (this.duration - Time.seconds(seconds)); - }, - }, - }, - +const Precast = (function () { + includeIfNotIncluded("core/Skill.js"); /** - * Easier Shout/Bo/Bc casting with state checks to ensure it was casted + * @constructor * @param {number} skillId - * @param {number | Unit} x - * @param {number} [y] - * @returns {boolean} */ - warCries: function (skillId, x, y) { - if (!skillId || x === undefined) return false; - if (![sdk.skills.Shout, sdk.skills.BattleOrders, sdk.skills.BattleCommand].includes(skillId)) { - return false; + function PrecastSkill (skillId) { + this.skillId = skillId; + this.state = Skill.getState(skillId); + this.lastCast = 0; + this.duration = 0; + } + PrecastSkill.prototype.canUse = function () { + return Skill.canUse(this.skillId); + }; + PrecastSkill.prototype.remaining = function () { + if (!this.duration) { + this.duration = Skill.getDuration(this.skillId); } - const _state = Skill.getState(skillId); - const _stateCheck = function () { - return me.getState(_state); - }; - - for (let i = 0; i < 3; i++) { - try { - if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) { - throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); - } - // Right hand + No Shift - let clickType = 3; - let shift = 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; - } + const pRemaining = 100 * (1 - (getTickCount() - this.lastCast) / this.duration); + return Math.max(0, Math.min(100, pRemaining)); + }; + PrecastSkill.prototype.needSoon = function (percent = 25) { + return this.remaining() < percent; + }; + PrecastSkill.prototype.needToCast = function (force = false, percent = 25) { + if (!this.canUse()) return false; + return force || !me.getState(this.state) || this.needSoon(percent); + }; + return { + enabled: true, + /** @type {number} */ + coldArmor: null, + shieldGid: 0, + haveCTA: -1, + bestSlot: {}, + + // TODO: build better method of keeping track of duration based skills so we can reduce resource usage + // build obj -> figure out which skills we have -> calc duration -> assign tick of last casted -> track tick (background worker maybe?) + // would reduce checking have skill and state calls, just let tick = getTickCount(); -> obj.some((el) => tick - el.lastTick > el.duration) -> true then cast + // would probably make sense to just re-cast everything (except summons) if one of our skills is about to run out rather than do this process again 3 seconds later + skills: new Map([ + [sdk.skills.FrozenArmor, new PrecastSkill(sdk.skills.FrozenArmor)], + [sdk.skills.ShiverArmor, new PrecastSkill(sdk.skills.ShiverArmor)], + [sdk.skills.ChillingArmor, new PrecastSkill(sdk.skills.ChillingArmor)], + [sdk.skills.Enchant, new PrecastSkill(sdk.skills.Enchant)], + [sdk.skills.ThunderStorm, new PrecastSkill(sdk.skills.ThunderStorm)], + [sdk.skills.EnergyShield, new PrecastSkill(sdk.skills.EnergyShield)], + [sdk.skills.HolyShield, new PrecastSkill(sdk.skills.HolyShield)], + [sdk.skills.Shout, new PrecastSkill(sdk.skills.Shout)], + [sdk.skills.BattleOrders, new PrecastSkill(sdk.skills.BattleOrders)], + [sdk.skills.BattleCommand, new PrecastSkill(sdk.skills.BattleCommand)], + [sdk.skills.CycloneArmor, new PrecastSkill(sdk.skills.CycloneArmor)], + [sdk.skills.Hurricane, new PrecastSkill(sdk.skills.Hurricane)], + [sdk.skills.Armageddon, new PrecastSkill(sdk.skills.Armageddon)], + [sdk.skills.Fade, new PrecastSkill(sdk.skills.Fade)], + [sdk.skills.BurstofSpeed, new PrecastSkill(sdk.skills.BurstofSpeed)], + [sdk.skills.BladeShield, new PrecastSkill(sdk.skills.BladeShield)], + [sdk.skills.Venom, new PrecastSkill(sdk.skills.Venom)], + [sdk.skills.BoneArmor, { + skillId: sdk.skills.BoneArmor, + state: sdk.states.BoneArmor, + lastCast: 0, + max: 0, + canUse: function () { + return Skill.canUse(this.skillId); + }, + remaining: function () { + return this.max > 0 + ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) + : 100; + }, + needSoon: function (percent = 25) { + return this.remaining() < percent; + }, + needToCast: function (force = false, percent = 25) { + if (!this.canUse()) return false; + return force || !me.getState(this.state) || this.needSoon(percent); + }, + }], + ]), + nonPacketSkills: new Set([ + 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, + ]), - delay(20); - } - } + checkCTA: function () { + if (this.haveCTA > -1) return true; - while (me.attacking) { - delay(10); - } + let check = me.checkItem({ name: sdk.locale.items.CalltoArms, equipped: true }); - if (Misc.poll(_stateCheck, 300, 50)) return true; - } catch (e) { - console.error(e); - return false; + if (check.have) { + Precast.haveCTA = check.item.isOnSwap ? 1 : 0; } - } - return false; - }, - - checkCTA: function () { - if (this.haveCTA > -1) return true; - let check = me.checkItem({ name: sdk.locale.items.CalltoArms, equipped: true }); - - if (check.have) { - Precast.haveCTA = check.item.isOnSwap ? 1 : 0; - } - - return this.haveCTA > -1; - }, - - /** - * @param {boolean} force - * @returns {boolean} - */ - precastCTA: function (force = false) { - if (!Config.UseCta || this.haveCTA === -1 || me.classic || me.barbarian || me.inTown || me.shapeshifted) { - return false; - } - if (!force && me.getState(sdk.states.BattleOrders)) return true; - - if (this.haveCTA > -1) { - let slot = me.weaponswitch; - let { x, y } = me; - - me.switchWeapons(this.haveCTA); - this.cast(sdk.skills.BattleCommand, x, y, true); - this.cast(sdk.skills.BattleCommand, x, y, true); - this.cast(sdk.skills.BattleOrders, x, y, true); + return this.haveCTA > -1; + }, - this.skills.battleOrders.tick = getTickCount(); - // does this need to be re-calculated everytime? if no autobuild should really just be done when we initialize - if (!this.skills.battleOrders.duration) { - this.skills.battleOrders.duration = Skill.getDuration(sdk.skills.BattleOrders); + /** + * @param {boolean} force + * @returns {boolean} + */ + precastCTA: function (force = false) { + if (!Config.UseCta || this.haveCTA === -1 || me.classic || me.barbarian || me.inTown || me.shapeshifted) { + return false; } + if (!force && me.getState(sdk.states.BattleOrders)) return true; - me.switchWeapons(slot); - - return true; - } + if (this.haveCTA > -1) { + const slot = me.weaponswitch; + const { x, y } = me; - return false; - }, + me.switchWeapons(this.haveCTA); + this.cast(sdk.skills.BattleCommand, x, y, false); + this.cast(sdk.skills.BattleCommand, x, y, false); + this.cast(sdk.skills.BattleOrders, x, y, false); - /** - * Check which slot (primary or secondary) gives us the most skillpoints in a skill - * @param {number} skillId - * @returns {0 | 1} best slot to give us the most skillpoints in a skill - * @todo Move this to be part of the SkillData class - */ - getBetterSlot: function (skillId) { - if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; - - let [classid, skillTab] = (function () { - switch (skillId) { - case sdk.skills.FrozenArmor: - case sdk.skills.ShiverArmor: - case sdk.skills.ChillingArmor: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Cold]; - case sdk.skills.Enchant: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Fire]; - case sdk.skills.ThunderStorm: - case sdk.skills.EnergyShield: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Lightning]; - case sdk.skills.BoneArmor: - return [sdk.player.class.Necromancer, sdk.skills.tabs.PoisonandBone]; - case sdk.skills.HolyShield: - return [sdk.player.class.Paladin, sdk.skills.tabs.PalaCombat]; - case sdk.skills.Taunt: - case sdk.skills.FindItem: - case sdk.skills.BattleCry: - case sdk.skills.WarCry: - case sdk.skills.Shout: - case sdk.skills.BattleOrders: - case sdk.skills.BattleCommand: - return [sdk.player.class.Barbarian, sdk.skills.tabs.Warcries]; - case sdk.skills.CycloneArmor: - return [sdk.player.class.Druid, sdk.skills.tabs.Elemental]; - case sdk.skills.Werewolf: - case sdk.skills.Werebear: - return [sdk.player.class.Druid, sdk.skills.tabs.ShapeShifting]; - case sdk.skills.BurstofSpeed: - case sdk.skills.Fade: - return [sdk.player.class.Assassin, sdk.skills.tabs.ShadowDisciplines]; - case sdk.skills.BladeShield: - return [sdk.player.class.Assassin, sdk.skills.tabs.MartialArts]; - default: - return [-1, -1]; - } - })(); - - if (classid < 0) return me.weaponswitch; + // does this need to be re-calculated everytime? if no autobuild should really just be done when we initialize + if (!Precast.skills.get(sdk.skills.BattleOrders).duration) { + this.skills.get(sdk.skills.BattleOrders).duration = Skill.getDuration(sdk.skills.BattleOrders); + } - me.weaponswitch !== 0 && me.switchWeapons(0); + me.switchWeapons(slot); - let [sumCurr, sumSwap] = [0, 0]; - const sumStats = function (item) { - return (item.getStat(sdk.stats.AllSkills) - + item.getStat(sdk.stats.AddClassSkills, classid) + item.getStat(sdk.stats.AddSkillTab, skillTab) - + item.getStat(sdk.stats.SingleSkill, skillId) + item.getStat(sdk.stats.NonClassSkill, skillId)); - }; + return true; + } - me.getItemsEx() - .filter(item => item.isEquipped && [ - sdk.body.RightArm, sdk.body.LeftArm, sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary - ].includes(item.bodylocation)) - .forEach(function (item) { - if (item.isOnMain) { - sumCurr += sumStats(item); - return; - } + return false; + }, - if (item.isOnSwap) { - sumSwap += sumStats(item); - return; + /** + * Check which slot (primary or secondary) gives us the most skillpoints in a skill + * @param {number} skillId + * @returns {0 | 1} best slot to give us the most skillpoints in a skill + * @todo Move this to be part of the SkillData class + */ + getBetterSlot: function (skillId) { + if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; + + let [classid, skillTab] = (function () { + switch (skillId) { + case sdk.skills.FrozenArmor: + case sdk.skills.ShiverArmor: + case sdk.skills.ChillingArmor: + return [sdk.player.class.Sorceress, sdk.skills.tabs.Cold]; + case sdk.skills.Enchant: + return [sdk.player.class.Sorceress, sdk.skills.tabs.Fire]; + case sdk.skills.ThunderStorm: + case sdk.skills.EnergyShield: + return [sdk.player.class.Sorceress, sdk.skills.tabs.Lightning]; + case sdk.skills.BoneArmor: + return [sdk.player.class.Necromancer, sdk.skills.tabs.PoisonandBone]; + case sdk.skills.HolyShield: + return [sdk.player.class.Paladin, sdk.skills.tabs.PalaCombat]; + case sdk.skills.Taunt: + case sdk.skills.FindItem: + case sdk.skills.BattleCry: + case sdk.skills.WarCry: + case sdk.skills.Shout: + case sdk.skills.BattleOrders: + case sdk.skills.BattleCommand: + return [sdk.player.class.Barbarian, sdk.skills.tabs.Warcries]; + case sdk.skills.CycloneArmor: + return [sdk.player.class.Druid, sdk.skills.tabs.Elemental]; + case sdk.skills.Werewolf: + case sdk.skills.Werebear: + return [sdk.player.class.Druid, sdk.skills.tabs.ShapeShifting]; + case sdk.skills.BurstofSpeed: + case sdk.skills.Fade: + return [sdk.player.class.Assassin, sdk.skills.tabs.ShadowDisciplines]; + case sdk.skills.BladeShield: + return [sdk.player.class.Assassin, sdk.skills.tabs.MartialArts]; + default: + return [-1, -1]; } - }); - this.bestSlot[skillId] = (sumSwap > sumCurr) ? me.weaponswitch ^ 1 : me.weaponswitch; - return this.bestSlot[skillId]; - }, + })(); - cast: function (skillId, x = me.x, y = me.y, dontSwitch = false) { - if (!skillId || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { - return false; - } - if (Skill.getManaCost(skillId) > me.mp) return false; + if (classid < 0) return me.weaponswitch; + + me.weaponswitch !== 0 && me.switchWeapons(0); + + let [sumCurr, sumSwap] = [0, 0]; + const sumStats = function (item) { + return (item.getStat(sdk.stats.AllSkills) + + item.getStat(sdk.stats.AddClassSkills, classid) + item.getStat(sdk.stats.AddSkillTab, skillTab) + + item.getStat(sdk.stats.SingleSkill, skillId) + item.getStat(sdk.stats.NonClassSkill, skillId)); + }; + + me.getItemsEx() + .filter(item => item.isEquipped && [ + sdk.body.RightArm, sdk.body.LeftArm, sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary + ].includes(item.bodylocation)) + .forEach(function (item) { + if (item.isOnMain) { + sumCurr += sumStats(item); + return; + } - let swap = me.weaponswitch; - let success = true; - // don't use packet casting with summons - or boing - 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); - (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); + if (item.isOnSwap) { + sumSwap += sumStats(item); + return; + } + }); + this.bestSlot[skillId] = (sumSwap > sumCurr) ? me.weaponswitch ^ 1 : me.weaponswitch; + return this.bestSlot[skillId]; + }, - try { - !dontSwitch && me.switchWeapons(this.getBetterSlot(skillId)); - if (me.getSkill(sdk.skills.get.RightId) !== skillId && !me.setSkill(skillId, sdk.skills.hand.Right)) { - throw new Error("Failed to set " + getSkillById(skillId) + " on hand"); - } - if ([sdk.skills.Shout, sdk.skills.BattleOrders, sdk.skills.BattleCommand].includes(skillId)) { - return this.warCries(skillId, x, y); + cast: function (skillId, x = me.x, y = me.y, allowSwitch = true) { + if (!skillId || !Skill.wereFormCheck(skillId) || (me.inTown && !Skill.townSkill(skillId))) { + return false; } + if (Skill.getManaCost(skillId) > me.mp) return false; - if (Config.PacketCasting > 1 || usePacket) { - Config.DebugMode.Skill && console.debug("Packet casting: " + skillId); - - switch (typeof x) { - case "number": - Packet.castSkill(sdk.skills.hand.Right, x, y); - - break; - case "object": - Packet.unitCast(sdk.skills.hand.Right, x); + const swap = me.weaponswitch; + // don't use packet casting with summons - or boing + const usePacket = !Precast.nonPacketSkills.has(skillId); + const state = Precast.skills.has(skillId) + ? Precast.skills.get(skillId).state + : 0; + (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); - break; + try { + allowSwitch && me.switchWeapons(this.getBetterSlot(skillId)); + if (me.getSkill(sdk.skills.get.RightId) !== skillId + && !me.setSkill(skillId, sdk.skills.hand.Right)) { + throw new Error( + "Failed to set " + getSkillById(skillId) + " on hand." + + "Current: " + getSkillById(me.getSkill(sdk.skills.get.RightId))); } - delay(250); - } else { - // Right hand + No Shift - let clickType = 3, shift = 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; + if (Config.PacketCasting > 1 || usePacket) { + Config.DebugMode.Skill && console.debug("Packet casting: " + skillId); + + if (typeof x === "number") { + Packet.castSkill(sdk.skills.hand.Right, x, y); + } else if (typeof x === "object") { + Packet.unitCast(sdk.skills.hand.Right, x); + } + delay(250); + } else { + // Right hand + No Shift + const clickType = sdk.clicktypes.click.map.RightDown; + const shift = sdk.clicktypes.shift.NoShift; + + 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); + + if (Misc.poll(function () { + return me.attacking; + }, 200, 20)) { + break; } + } - delay(20); + while (me.attacking) { + delay(10); } } - while (me.attacking) { - delay(10); + // account for lag, state 121 doesn't kick in immediately + if (Skill.isTimed(skillId)) { + Misc.poll(function () { + return ( + me.skillDelay + || me.mode === sdk.player.mode.GettingHit + || me.mode === sdk.player.mode.Blocking + ); + }, 100, 10); } - } - - // account for lag, state 121 doesn't kick in immediately - if (Skill.isTimed(skillId)) { - for (let i = 0; i < 10; i += 1) { - if ([sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode) || me.skillDelay) { - break; - } - - delay(10); + if (Precast.skills.has(skillId)) { + Precast.skills.get(skillId).lastCast = getTickCount(); } + return state ? me.getState(state) : true; + } catch (e) { + console.error(e); + return false; + } finally { + allowSwitch && me.switchWeapons(swap); } - } catch (e) { - console.error(e); - success = false; - } - - !dontSwitch && me.switchWeapons(swap); - - return success; - }, - - summon: function (skillId, minionType) { - if (!Skill.canUse(skillId)) return false; + }, - let rv, retry = 0; - let count = Skill.getMaxSummonCount(skillId); + summon: function (skillId, minionType) { + if (!Skill.canUse(skillId)) return false; - while (me.getMinionCount(minionType) < count) { - rv = true; + let rv, retry = 0; + let count = Skill.getMaxSummonCount(skillId); - if (retry > count * 2) { - if (me.inTown) { - Town.heal() && me.cancelUIFlags(); - Town.move("portalspot"); - Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); - } else { - let coord = CollMap.getRandCoordinate(me.x, -6, 6, me.y, -6, 6); + while (me.getMinionCount(minionType) < count) { + rv = true; - // Keep bots from getting stuck trying to summon - if (!!coord && Attack.validSpot(coord.x, coord.y)) { - Pather.moveTo(coord.x, coord.y); + if (retry > count * 2) { + if (me.inTown) { + Town.heal() && me.cancelUIFlags(); + Town.move("portalspot"); Skill.cast(skillId, sdk.skills.hand.Right, me.x, me.y); + } else { + let 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); + } } - } - if (me.getMinionCount(minionType) === count) { - return true; - } else { - console.warn("Failed to summon minion " + skillId); + if (me.getMinionCount(minionType) === count) { + return true; + } else { + console.warn("Failed to summon minion " + skillId); - return false; + return false; + } } - } - // todo - only delay if we are close to the mana amount we need based on our mana regen rate or potion state - // also take into account surrounding mobs so we don't delay for mana in the middle of a mob pack - if (Skill.getManaCost(skillId) > me.mp) { - if (!Misc.poll(() => me.mp >= Skill.getManaCost(skillId), 500, 100)) { - retry++; - continue; + // todo - only delay if we are close to the mana amount we need based on our mana regen rate or potion state + // also take into account surrounding mobs so we don't delay for mana in the middle of a mob pack + if (Skill.getManaCost(skillId) > me.mp) { + if (!Misc.poll(() => me.mp >= Skill.getManaCost(skillId), 500, 100)) { + retry++; + continue; + } } - } - let coord = CollMap.getRandCoordinate(me.x, -4, 4, me.y, -4, 4); + let coord = CollMap.getRandCoordinate(me.x, -4, 4, me.y, -4, 4); - if (!!coord && Attack.validSpot(coord.x, coord.y)) { - Skill.cast(skillId, sdk.skills.hand.Right, coord.x, coord.y); + if (!!coord && Attack.validSpot(coord.x, coord.y)) { + Skill.cast(skillId, sdk.skills.hand.Right, coord.x, coord.y); - if (me.getMinionCount(minionType) === count) { - break; - } else { - retry++; + if (me.getMinionCount(minionType) === count) { + break; + } else { + retry++; + } } - } - delay(200); - } - - return !!rv; - }, + delay(200); + } - enchant: function () { - let unit, slot = me.weaponswitch, chanted = []; + return !!rv; + }, - me.switchWeapons(this.getBetterSlot(sdk.skills.Enchant)); + enchant: (function () { + let chantDuration = 0; - // Player - unit = Game.getPlayer(); + /** @constructor */ + function ChantTracker () { + this.lastChant = getTickCount(); + } - if (unit) { - do { - if (!unit.dead && Misc.inMyParty(unit.name) && unit.distance <= 40) { - Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); - chanted.push(unit.name); + ChantTracker.prototype.reChant = function () { + return getTickCount() - this.lastChant >= chantDuration - Time.seconds(10); + }; + + ChantTracker.prototype.update = function () { + this.lastChant = getTickCount(); + }; + + /** @type {Map 40) continue; + if (!unit.getState(sdk.states.Enchant) + || (chantList.has(unit.name) && chantList.get(unit.name).reChant())) { + Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 100)) { + chanted.push(unit.name); + chantList.has(unit.name) + ? chantList.get(unit.name).update() + : chantList.set(unit.name, new ChantTracker()); + } + } + } while (unit.getNext()); } - } while (unit.getNext()); - } - // Minion - unit = Game.getMonster(); + // Minion + unit = Game.getMonster(); - if (unit) { - do { - if (unit.getParent() && chanted.includes(unit.getParent().name) && unit.distance <= 40) { - Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); + if (unit) { + do { + if (unit.getParent() + && chanted.includes(unit.getParent().name) + && unit.distance <= 40 + && !unit.getState(sdk.states.Enchant)) { + Skill.cast(sdk.skills.Enchant, sdk.skills.hand.Right, unit); + } + } while (unit.getNext()); } - } while (unit.getNext()); - } - - me.switchWeapons(slot); - - return true; - }, - - // should the config check still be included even though its part of Skill.init? - /** - * @description Handle precast related skills - * @param {boolean} force - force re-cast of all precast skills - * @param {boolean} partial - force re-cast of all state related precast skills - * @returns {boolean} sucessfully casted - * @todo durations - */ - doPrecast: function (force = false, partial = false) { - if (!this.enabled) return false; - - while (!me.gameReady) { - delay(40); - } - - let [buffSummons, forceBo] = [false, false]; - - // Force BO 30 seconds before it expires - if (Precast.haveCTA > -1) { - forceBo = (force || partial - || Precast.skills.battleOrders.needSoon() - || !me.getState(sdk.states.BattleCommand)); - forceBo && this.precastCTA(forceBo); - } - - /** @param {number} state */ - const needToCast = function (state) { - return (force || partial || !me.getState(state)); - }; - switch (me.classid) { - case sdk.player.class.Amazon: - if (Skill.canUse(sdk.skills.Valkyrie)) { - buffSummons = Precast.summon(sdk.skills.Valkyrie, sdk.summons.type.Valkyrie); - } - break; - case sdk.player.class.Sorceress: - if (Skill.canUse(sdk.skills.ThunderStorm) && needToCast(sdk.states.ThunderStorm)) { - this.cast(sdk.skills.ThunderStorm); + me.switchWeapons(slot); + + return true; + }; + })(), + + // should the config check still be included even though its part of Skill.init? + /** + * @description Handle precast related skills + * @param {boolean} force - force re-cast of all precast skills + * @param {boolean} partial - force re-cast of all state related precast skills + * @returns {boolean} sucessfully casted + * @todo durations + */ + doPrecast: function (force = false, partial = false) { + if (!this.enabled) return false; + + while (!me.gameReady) { + delay(40); } - if (Skill.canUse(sdk.skills.EnergyShield) && needToCast(sdk.states.EnergyShield)) { - this.cast(sdk.skills.EnergyShield); + let [buffSummons, forceBo] = [false, false]; + + // Force BO 30 seconds before it expires + if (Precast.haveCTA > -1) { + forceBo = (force || partial + || Precast.skills.get(sdk.skills.BattleOrders).remaining() < 25 + || !me.getState(sdk.states.BattleCommand)); + forceBo && this.precastCTA(forceBo); } - if (Config.UseColdArmor) { - let choosenSkill = (typeof Config.UseColdArmor === "number" && Skill.canUse(Config.UseColdArmor) - ? Config.UseColdArmor - : (Precast.skills.coldArmor.best || -1)); - - if (Precast.skills.coldArmor.needSoon(45)) { - if (Precast.skills.coldArmor.needSoon(25)) { - force = true; - } + switch (me.classid) { + case sdk.player.class.Amazon: + if (Skill.canUse(sdk.skills.Valkyrie)) { + buffSummons = Precast.summon(sdk.skills.Valkyrie, sdk.summons.type.Valkyrie); + } + break; + case sdk.player.class.Sorceress: + if (Precast.skills.get(sdk.skills.ThunderStorm).needToCast(force || partial)) { + this.cast(sdk.skills.ThunderStorm); } - switch (choosenSkill) { - case sdk.skills.FrozenArmor: - if (needToCast(sdk.states.FrozenArmor)) { - if (Precast.cast(sdk.skills.FrozenArmor)) { - Precast.skills.coldArmor.tick = getTickCount(); - } - } - break; - case sdk.skills.ChillingArmor: - if (needToCast(sdk.states.ChillingArmor)) { - if (Precast.cast(sdk.skills.ChillingArmor)) { - Precast.skills.coldArmor.tick = getTickCount(); - } - } + if (Precast.skills.get(sdk.skills.EnergyShield).needToCast(force || partial)) { + this.cast(sdk.skills.EnergyShield); + } - break; - case sdk.skills.ShiverArmor: - if (needToCast(sdk.states.ShiverArmor)) { - if (Precast.cast(sdk.skills.ShiverArmor)) { - Precast.skills.coldArmor.tick = getTickCount(); + if (Config.UseColdArmor) { + let choosenSkill = (typeof Config.UseColdArmor === "number" && Skill.canUse(Config.UseColdArmor) + ? Config.UseColdArmor + : (Precast.coldArmor || -1)); + + if (choosenSkill && Precast.skills.has(choosenSkill)) { + if (Precast.skills.get(choosenSkill).needToCast(force || partial)) { + Precast.cast(choosenSkill); } } - - break; - default: - break; } - } - if (Skill.canUse(sdk.skills.Enchant) && needToCast(sdk.states.Enchant)) { - this.enchant(); - } - - break; - case sdk.player.class.Necromancer: - if (Skill.canUse(sdk.skills.BoneArmor) - && (force || this.skills.boneArmor.remaining() < 75 || !me.getState(sdk.states.BoneArmor))) { - this.cast(sdk.skills.BoneArmor); - if (this.skills.boneArmor.max === 0) { - this.skills.boneArmor.max = me.getStat(sdk.stats.SkillBoneArmorMax); + if (Precast.skills.get(sdk.skills.Enchant).needToCast(force || partial)) { + this.enchant(); } - } - (function () { - switch (Config.Golem) { - case 1: - case "Clay": - return Precast.summon(sdk.skills.ClayGolem, sdk.summons.type.Golem); - case 2: - case "Blood": - return Precast.summon(sdk.skills.BloodGolem, sdk.summons.type.Golem); - case 3: - case "Fire": - return Precast.summon(sdk.skills.FireGolem, sdk.summons.type.Golem); - default: - return false; + break; + case sdk.player.class.Necromancer: + if (Precast.skills.get(sdk.skills.BoneArmor).needToCast(force, 75)) { + this.cast(sdk.skills.BoneArmor); + if (Precast.skills.get(sdk.skills.BoneArmor).max === 0) { + Precast.skills.get(sdk.skills.BoneArmor).max = me.getStat(sdk.stats.SkillBoneArmorMax); + } } - })(); - Config.ActiveSummon && ClassAttack.raiseArmy(); + if (!!Config.Golem && Config.Golem !== "None") { + Precast.summon(Config.Golem, sdk.summons.type.Golem); + } - break; - case sdk.player.class.Paladin: - if (Skill.canUse(sdk.skills.HolyShield) - && Precast.skills.holyShield.canUse - && (Precast.skills.holyShield.needSoon() || needToCast(sdk.states.HolyShield))) { - this.cast(sdk.skills.HolyShield); - } + Config.ActiveSummon && ClassAttack.raiseArmy(); - break; - case sdk.player.class.Barbarian: // - TODO: durations - if (!Config.UseWarcries) { break; - } - let needShout = (Skill.canUse(sdk.skills.Shout) && needToCast(sdk.states.Shout)); - let needBo = (Skill.canUse(sdk.skills.BattleOrders) && needToCast(sdk.states.BattleOrders)); - let needBc = (Skill.canUse(sdk.skills.BattleCommand) && needToCast(sdk.states.BattleCommand)); - - if (needShout || needBo || needBc) { - let primary = Attack.getPrimarySlot(); - let { x, y } = me; - (needBo || needBc) && me.switchWeapons(this.getBetterSlot(sdk.skills.BattleOrders)); + case sdk.player.class.Paladin: + 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); + } + } - needBc && this.cast(sdk.skills.BattleCommand, x, y, true); - needBo && this.cast(sdk.skills.BattleOrders, x, y, true); - needShout && this.cast(sdk.skills.Shout, x, y, true); + break; + case sdk.player.class.Barbarian: // - TODO: durations + if (!Config.UseWarcries) { + break; + } + 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)); - me.weaponswitch !== primary && me.switchWeapons(primary); - } + if (needShout || needBo || needBc) { + let primary = Attack.getPrimarySlot(); + let { x, y } = me; + (needBo || needBc) && me.switchWeapons(this.getBetterSlot(sdk.skills.BattleOrders)); - break; - case sdk.player.class.Druid: - if (Skill.canUse(sdk.skills.CycloneArmor) && needToCast(sdk.states.CycloneArmor)) { - this.cast(sdk.skills.CycloneArmor); - } + needBc && this.cast(sdk.skills.BattleCommand, x, y, false); + needBo && this.cast(sdk.skills.BattleOrders, x, y, false); + needShout && this.cast(sdk.skills.Shout, x, y, false); - Skill.canUse(sdk.skills.Raven) && Precast.summon(sdk.skills.Raven, sdk.summons.type.Raven); - - buffSummons = (function () { - switch (Config.SummonAnimal) { - case 1: - case "Spirit Wolf": - return (Precast.summon(sdk.skills.SummonSpiritWolf, sdk.summons.type.SpiritWolf) || buffSummons); - case 2: - case "Dire Wolf": - return (Precast.summon(sdk.skills.SummonDireWolf, sdk.summons.type.DireWolf) || buffSummons); - case 3: - case "Grizzly": - return (Precast.summon(sdk.skills.SummonGrizzly, sdk.summons.type.Grizzly) || buffSummons); - default: - return buffSummons; + me.weaponswitch !== primary && me.switchWeapons(primary); } - })(); - buffSummons = (function () { - switch (Config.SummonVine) { - case 1: - case "Poison Creeper": - return (Precast.summon(sdk.skills.PoisonCreeper, sdk.summons.type.Vine) || buffSummons); - case 2: - case "Carrion Vine": - return (Precast.summon(sdk.skills.CarrionVine, sdk.summons.type.Vine) || buffSummons); - case 3: - case "Solar Creeper": - return (Precast.summon(sdk.skills.SolarCreeper, sdk.summons.type.Vine) || buffSummons); - default: - return buffSummons; + break; + case sdk.player.class.Druid: + if (Precast.skills.get(sdk.skills.CycloneArmor).needToCast(force || partial)) { + this.cast(sdk.skills.CycloneArmor); } - })(); - buffSummons = (function () { - switch (Config.SummonSpirit) { - case 1: - case "Oak Sage": - // to prevent false chickens when we cast oak before getting bo-ed - if (me.hardcore && !me.getState(sdk.states.BattleOrders) && me.inTown) return buffSummons; - return (Precast.summon(sdk.skills.OakSage, sdk.summons.type.Spirit) || buffSummons); - case 2: - case "Heart of Wolverine": - return (Precast.summon(sdk.skills.HeartofWolverine, sdk.summons.type.Spirit) || buffSummons); - case 3: - case "Spirit of Barbs": - return Precast.summon(sdk.skills.SpiritofBarbs, sdk.summons.type.Spirit) || buffSummons; - default: - return buffSummons; + Skill.canUse(sdk.skills.Raven) && Precast.summon(sdk.skills.Raven, sdk.summons.type.Raven); + + buffSummons = (function () { + switch (Config.SummonAnimal) { + case sdk.skills.SummonSpiritWolf: + return (Precast.summon(sdk.skills.SummonSpiritWolf, sdk.summons.type.SpiritWolf) || buffSummons); + case sdk.skills.SummonDireWolf: + return (Precast.summon(sdk.skills.SummonDireWolf, sdk.summons.type.DireWolf) || buffSummons); + case sdk.skills.SummonGrizzly: + return (Precast.summon(sdk.skills.SummonGrizzly, sdk.summons.type.Grizzly) || buffSummons); + default: + return buffSummons; + } + })(); + + if (!!Config.SummonVine && Config.SummonVine !== "None") { + buffSummons = Precast.summon(Config.SummonVine, sdk.summons.type.Vine); } - })(); - if (Skill.canUse(sdk.skills.Hurricane) && needToCast(sdk.states.Hurricane)) { - this.cast(sdk.skills.Hurricane); - } + if (!!Config.SummonSpirit && Config.SummonSpirit !== "None") { + buffSummons = ( + Config.SummonSpirit === sdk.skills.OakSage + && me.hardcore + && !me.getState(sdk.states.BattleOrders) + && me.inTown + ) + ? buffSummons + : Precast.summon(Config.SummonSpirit, sdk.summons.type.Spirit); + } - break; - case sdk.player.class.Assassin: - if (Skill.canUse(sdk.skills.Fade) && needToCast(sdk.states.Fade)) { - this.cast(sdk.skills.Fade); - } + if (Precast.skills.get(sdk.skills.Hurricane).needToCast(force || partial)) { + this.cast(sdk.skills.Hurricane); + } - if (Skill.canUse(sdk.skills.Venom) && needToCast(sdk.states.Venom)) { - this.cast(sdk.skills.Venom); - } + break; + case sdk.player.class.Assassin: + if (Precast.skills.get(sdk.skills.Fade).needToCast(force || partial)) { + this.cast(sdk.skills.Fade); + } - if (Skill.canUse(sdk.skills.BladeShield) && needToCast(sdk.states.BladeShield)) { - this.cast(sdk.skills.BladeShield); - } + if (Precast.skills.get(sdk.skills.Venom).needToCast(force || partial)) { + this.cast(sdk.skills.Venom); + } - if (!Config.UseFade && Skill.canUse(sdk.skills.BurstofSpeed) && needToCast(sdk.states.BurstofSpeed)) { - this.cast(sdk.skills.BurstofSpeed); - } + if (Precast.skills.get(sdk.skills.BladeShield).needToCast(force || partial)) { + this.cast(sdk.skills.BladeShield); + } - buffSummons = (function () { - switch (Config.SummonShadow) { - case 1: - case "Warrior": - return Precast.summon(sdk.skills.ShadowWarrior, sdk.summons.type.Shadow); - case 2: - case "Master": - return Precast.summon(sdk.skills.ShadowMaster, sdk.summons.type.Shadow); - default: - return false; + if (!Config.UseFade && Precast.skills.get(sdk.skills.BurstofSpeed).needToCast(force || partial)) { + this.cast(sdk.skills.BurstofSpeed); } - })(); - break; - } + if (!!Config.SummonShadow && !!Config.SummonShadow !== "None") { + buffSummons = Precast.summon(Config.SummonShadow, sdk.summons.type.Shadow); + } - buffSummons && this.haveCTA > -1 && this.precastCTA(force); - me.switchWeapons(Attack.getPrimarySlot()); + break; + } - return true; - }, + buffSummons && this.haveCTA > -1 && this.precastCTA(force); + me.switchWeapons(Attack.getPrimarySlot()); - needOutOfTownCast: function () { - return Skill.canUse(sdk.skills.Shout) || Skill.canUse(sdk.skills.BattleOrders) || Precast.checkCTA(); - }, + return true; + }, - doRandomPrecast: function (force = false, goToWhenDone = undefined) { - let returnTo = (goToWhenDone && typeof goToWhenDone === "number" ? goToWhenDone : me.area); + needOutOfTownCast: function () { + return Skill.canUse(sdk.skills.Shout) || Skill.canUse(sdk.skills.BattleOrders) || Precast.checkCTA(); + }, - try { - // Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time and you can precast in town - if (Precast.needOutOfTownCast()) { - Pather.useWaypoint("random") && Precast.doPrecast(force); - } else { - Precast.doPrecast(force); - } - Pather.useWaypoint(returnTo); - } catch (e) { - console.error(e); - } finally { - if (me.area !== returnTo && (!Pather.useWaypoint(returnTo) || !Pather.useWaypoint(sdk.areas.townOf(me.area)))) { - Pather.journeyTo(returnTo); + doRandomPrecast: function (force = false, goToWhenDone = undefined) { + const returnTo = (goToWhenDone && typeof goToWhenDone === "number" + ? goToWhenDone + : me.area); + + try { + // Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time and you can precast in town + if (Precast.needOutOfTownCast()) { + Pather.useWaypoint("random") && Precast.doPrecast(force); + } else { + Precast.doPrecast(force); + } + Pather.useWaypoint(returnTo); + } catch (e) { + console.error(e); + } finally { + if (me.area !== returnTo && (!Pather.useWaypoint(returnTo) || !Pather.useWaypoint(sdk.areas.townOf(me.area)))) { + Pather.journeyTo(returnTo); + } } - } - return (me.area === returnTo); - }, -}; + return (me.area === returnTo); + }, + }; +})(); diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 8a2a4d7df..3c3d63769 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -149,8 +149,8 @@ break; case sdk.player.class.Sorceress: if (Config.UseColdArmor === true) { - Precast.skills.coldArmor.best = (function () { - let _coldSkill = (id) => ({ skillId: id, level: me.getSkill(id, sdk.skills.subindex.SoftPoints) }); + Precast.coldArmor = (function () { + const _coldSkill = (id) => ({ skillId: id, level: me.getSkill(id, sdk.skills.subindex.SoftPoints) }); let coldArmor = [ _coldSkill(sdk.skills.ShiverArmor), _coldSkill(sdk.skills.ChillingArmor), @@ -158,53 +158,127 @@ ].filter(skill => !!skill.level && skill.level > 0).sort((a, b) => b.level - a.level).first(); return coldArmor !== undefined ? coldArmor.skillId : -1; })(); - Precast.skills.coldArmor.duration = this.getDuration(Precast.skills.coldArmor.best); + if (Precast.coldArmor > 0) { + Precast.skills.get(Precast.coldArmor).duration = this.getDuration(Precast.coldArmor); + } } else { - Precast.skills.coldArmor.duration = this.getDuration(Config.UseColdArmor); + Precast.skills.get(Config.UseColdArmor).duration = this.getDuration(Config.UseColdArmor); } break; case sdk.player.class.Necromancer: { let bMax = me.getStat(sdk.stats.SkillBoneArmorMax); - bMax > 0 && (Precast.skills.boneArmor.max = bMax); + bMax > 0 && (Precast.skills.get(sdk.skills.BoneArmor).max = bMax); } if (!!Config.Golem && Config.Golem !== "None") { - // todo: change Config.Golem to use skillid instead of 0, 1, 2, and 3 + Config.Golem = (function () { + switch (Config.Golem) { + case 1: + case "Clay": + return sdk.skills.ClayGolem; + case 2: + case "Blood": + return sdk.skills.BloodGolem; + case 3: + case "Fire": + return sdk.skills.FireGolem; + default: + return Config.Golem; + } + })(); } break; case sdk.player.class.Paladin: // how to handle if someone manually equips a shield during game play, don't want to build entire item list if we don't need to // maybe store gid of shield, would still require doing me.getItem(-1, 1, gid) everytime we wanted to cast but that's still less involved // than getting every item we have and finding shield, for now keeping this. Checks during init if we have a shield or not - Precast.skills.holyShield.canUse = me.usingShield(); - Precast.skills.holyShield.duration = this.getDuration(sdk.skills.HolyShield); + let shield = me.usingShield(); + if (shield) { + Precast.shieldGid = shield.gid; + } + Precast.skills.get(sdk.skills.HolyShield).duration = this.getDuration(sdk.skills.HolyShield); break; case sdk.player.class.Barbarian: - Skill.canUse(sdk.skills.Shout) && (Precast.skills.shout.duration = this.getDuration(sdk.skills.Shout)); + if (Skill.canUse(sdk.skills.Shout)) { + Precast.skills.get(sdk.skills.Shout).duration = this.getDuration(sdk.skills.Shout); + } if (Skill.canUse(sdk.skills.BattleOrders)) { - Precast.skills.battleOrders.duration = this.getDuration(sdk.skills.BattleOrders); + Precast.skills.get(sdk.skills.BattleOrders).duration = this.getDuration(sdk.skills.BattleOrders); } if (Skill.canUse(sdk.skills.BattleCommand)) { - Precast.skills.battleCommand.duration = this.getDuration(sdk.skills.BattleCommand); + Precast.skills.get(sdk.skills.BattleCommand).duration = this.getDuration(sdk.skills.BattleCommand); } break; case sdk.player.class.Druid: if (!!Config.SummonAnimal && Config.SummonAnimal !== "None") { - // todo: change Config.SummonAnimal to use skillid instead of 0, 1, 2, and 3 + Config.SummonAnimal = (function () { + switch (Config.SummonAnimal) { + case 1: + case "Spirit Wolf": + return sdk.skills.SummonSpiritWolf; + case 2: + case "Dire Wolf": + return sdk.skills.SummonDireWolf; + case 3: + case "Grizzly": + return sdk.skills.SummonGrizzly; + default: + return Config.SummonAnimal; + } + })(); } if (!!Config.SummonVine && Config.SummonVine !== "None") { - // todo: change Config.SummonVine to use skillid instead of 0, 1, 2, and 3 + Config.SummonVine = (function () { + switch (Config.SummonVine) { + case 1: + case "Poison Creeper": + return sdk.skills.PoisonCreeper; + case 2: + case "Carrion Vine": + return sdk.skills.CarrionVine; + case 3: + case "Solar Creeper": + return sdk.skills.SolarCreeper; + default: + return Config.SummonVine; + } + })(); } if (!!Config.SummonSpirit && Config.SummonSpirit !== "None") { - // todo: change Config.SummonSpirit to use skillid instead of 0, 1, 2, and 3 + Config.SummonSpirit = (function () { + switch (Config.SummonSpirit) { + case 1: + case "Oak Sage": + return sdk.skills.OakSage; + case 2: + case "Heart of Wolverine": + return sdk.skills.HeartofWolverine; + case 3: + case "Spirit of Barbs": + return sdk.skills.SpiritofBarbs; + default: + return Config.SummonSpirit; + } + })(); } break; case sdk.player.class.Assassin: - if (!!Config.SummonShadow) { - // todo: change Config.SummonShadow to use skillid instead of 0, 1, 2, and 3 + if (!!Config.SummonShadow && !!Config.SummonShadow !== "None") { + Config.SummonShadow = (function () { + switch (Config.SummonShadow) { + case 1: + case "Warrior": + return sdk.skills.ShadowWarrior; + case 2: + case "Master": + return sdk.skills.ShadowMaster; + default: + return Config.SummonShadow; + } + })(); } break; } @@ -356,8 +430,11 @@ // Put a skill on desired slot setSkill: function (skillId, hand, item) { + const checkHand = (hand === sdk.skills.hand.Right + ? sdk.skills.get.RightId + : sdk.skills.get.LeftId); // Check if the skill is already set - if (me.getSkill(hand === sdk.skills.hand.Right ? sdk.skills.get.RightId : sdk.skills.get.LeftId) === skillId) { + if (me.getSkill(checkHand) === skillId) { return true; } if (!item && !Skill.canUse(skillId)) return false; @@ -375,7 +452,7 @@ // Change into werewolf or werebear shapeShift: function (mode) { - let [skill, state] = (() => { + const [skill, state] = (() => { switch (mode.toString().toLowerCase()) { case "0": return [-1, -1]; @@ -394,15 +471,18 @@ if (!Skill.canUse(skill)) return false; // already in wanted state if (me.getState(state)) return true; + const _stateCheck = function () { + return me.getState(state); + }; - let slot = Attack.getPrimarySlot(); + const slot = Attack.getPrimarySlot(); me.switchWeapons(Precast.getBetterSlot(skill)); try { for (let i = 0; i < 3; i += 1) { Skill.cast(skill, sdk.skills.hand.Right); - if (Misc.poll(() => me.getState(state), 2000, 50)) { + if (Misc.poll(_stateCheck, 2000, 50)) { return true; } } @@ -415,16 +495,19 @@ // Change back to human shape unShift: function () { - let [state, skill] = me.getState(sdk.states.Wearwolf) + const [state, skill] = me.getState(sdk.states.Wearwolf) ? [sdk.states.Wearwolf, sdk.skills.Werewolf] : me.getState(sdk.states.Wearbear) ? [sdk.states.Wearbear, sdk.skills.Werebear] : [0, 0]; if (!state) return true; + const _stateCheck = function () { + return !me.getState(state); + }; for (let i = 0; i < 3; i++) { Skill.cast(skill); - if (Misc.poll(() => !me.getState(state), 2000, 50)) { + if (Misc.poll(_stateCheck, 2000, 50)) { return true; } } @@ -496,20 +579,14 @@ if (!this.setSkill(skillId, hand, item)) return false; if (Config.PacketCasting > 1) { - switch (typeof x) { - case "number": - Packet.castSkill(hand, x, y); - delay(250); - - break; - case "object": - Packet.unitCast(hand, x); - delay(250); - - break; + if (typeof x === "number") { + Packet.castSkill(sdk.skills.hand.Right, x, y); + } else if (typeof x === "object") { + Packet.unitCast(sdk.skills.hand.Right, x); } + delay(250); } 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]; @@ -524,11 +601,17 @@ })(); for (let n = 0; n < 3; n += 1) { - typeof x === "object" ? clickMap(clickType, shift, x) : clickMap(clickType, shift, x, y); + 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); + typeof x === "object" + ? clickMap(clickType + 2, shift, x) + : clickMap(clickType + 2, shift, x, y); - if (Misc.poll(() => me.attacking, 200, 20)) { + if (Misc.poll(function () { + return me.attacking; + }, 200, 20)) { break; } } @@ -541,9 +624,12 @@ // account for lag, state 121 doesn't kick in immediately if (this.isTimed(skillId)) { Misc.poll(function () { - return me.skillDelay || [sdk.player.mode.GettingHit, sdk.player.mode.Blocking].includes(me.mode); - }, 100, 10 - ); + return ( + me.skillDelay + || me.mode === sdk.player.mode.GettingHit + || me.mode === sdk.player.mode.Blocking + ); + }, 100, 10); } return true; From 6fbff359b4301a1cb9727cb1951b662c3c36e3dd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 16:33:07 -0400 Subject: [PATCH 197/758] minor formatting --- d2bs/kolbot/libs/core/Attack.js | 4 ++-- d2bs/kolbot/libs/core/Prototypes.js | 4 +++- d2bs/kolbot/libs/scripts/AutoBaal.js | 2 +- d2bs/kolbot/libs/scripts/BaalHelper.js | 2 +- d2bs/kolbot/libs/scripts/Bonesaw.js | 2 +- d2bs/kolbot/libs/scripts/ChestMania.js | 26 ++++++++++++++------- d2bs/kolbot/libs/scripts/CreepingFeature.js | 2 +- d2bs/kolbot/libs/scripts/DeveloperMode.js | 4 ++-- d2bs/kolbot/libs/scripts/Endugu.js | 2 +- d2bs/kolbot/libs/scripts/Eyeback.js | 4 ++-- d2bs/kolbot/libs/scripts/GetCube.js | 2 +- d2bs/kolbot/libs/scripts/GetKeys.js | 2 +- d2bs/kolbot/libs/scripts/GhostBusters.js | 6 +++-- d2bs/kolbot/libs/scripts/KillDclone.js | 2 +- 14 files changed, 38 insertions(+), 26 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 7041b3e69..f954071e9 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1044,8 +1044,8 @@ const Attack = { /** * @description Sort monsters based on distance, spectype and classId (summoners are attacked first) - * @param {Unit} unitA - * @param {Unit} unitB + * @param {Monster} unitA + * @param {Monster} unitB * @returns {boolean} * @todo Think this needs a collison check included for non tele chars, might prevent choosing * closer mob that is actually behind a wall vs the one we pass trying to get behind the wall diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 361573722..14a471d11 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -2223,7 +2223,9 @@ Unit.prototype.usingShield = function () { me.switchWeapons(sdk.player.slot.Main); } return this.getItemsEx(-1, sdk.items.mode.Equipped) - .filter(s => s.isShield) + .filter(function (el) { + return el.isShield; + }) .first(); }; diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index 42974a214..2b1006e87 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -15,7 +15,7 @@ * - should this listen for baal death packet? */ -function AutoBaal() { +function AutoBaal () { // internal variables let baalCheck, throneCheck, hotCheck, leader; // internal variables const safeMsg = ["safe", "throne clear", "leechers can come", "tp is up", "1 clear"]; // safe message - casing doesn't matter diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index d5db83007..cbfaae86f 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -5,7 +5,7 @@ * */ -function BaalHelper() { +function BaalHelper () { include("core/Common/Baal.js"); Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); diff --git a/d2bs/kolbot/libs/scripts/Bonesaw.js b/d2bs/kolbot/libs/scripts/Bonesaw.js index 00eb61387..72725642e 100644 --- a/d2bs/kolbot/libs/scripts/Bonesaw.js +++ b/d2bs/kolbot/libs/scripts/Bonesaw.js @@ -5,7 +5,7 @@ * */ -function Bonesaw() { +function Bonesaw () { Town.doChores(); Pather.useWaypoint(sdk.areas.GlacialTrail); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/ChestMania.js b/d2bs/kolbot/libs/scripts/ChestMania.js index 7461baae8..db7352bf1 100644 --- a/d2bs/kolbot/libs/scripts/ChestMania.js +++ b/d2bs/kolbot/libs/scripts/ChestMania.js @@ -1,6 +1,6 @@ /** * @filename ChestMania.js -* @author kolton +* @author kolton, theBGuy * @desc Open chests in configured areas * */ @@ -9,20 +9,28 @@ function ChestMania () { Town.doChores(); + const nextToTown = [ + sdk.areas.BloodMoor, + sdk.areas.RockyWaste, + sdk.areas.SpiderForest, + sdk.areas.OuterSteppes, + sdk.areas.BloodyFoothills + ]; Object.values(Config.ChestMania) - .forEach(act => { + .forEach(function (act) { for (let area of act) { - if ([ - sdk.areas.BloodMoor, sdk.areas.RockyWaste, - sdk.areas.SpiderForest, sdk.areas.OuterSteppes, sdk.areas.BloodyFoothills - ].includes(area)) { + if (nextToTown.includes(area)) { // if we precast as soon as we step out of town it sometimes crashes - so do precast somewhere else first Precast.doRandomPrecast(false); } - Pather.journeyTo(area); - Precast.doPrecast(false); - Misc.openChestsInArea(area); + try { + Pather.journeyTo(area); + Precast.doPrecast(false); + Misc.openChestsInArea(area); + } catch (e) { + console.error(e); + } } Town.doChores(); diff --git a/d2bs/kolbot/libs/scripts/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js index 0f6e8920a..6055da8c8 100644 --- a/d2bs/kolbot/libs/scripts/CreepingFeature.js +++ b/d2bs/kolbot/libs/scripts/CreepingFeature.js @@ -5,7 +5,7 @@ * */ -function CreepingFeature() { +function CreepingFeature () { Town.doChores(); Town.goToTown(2); diff --git a/d2bs/kolbot/libs/scripts/DeveloperMode.js b/d2bs/kolbot/libs/scripts/DeveloperMode.js index d8f9b5077..1f6d354c3 100644 --- a/d2bs/kolbot/libs/scripts/DeveloperMode.js +++ b/d2bs/kolbot/libs/scripts/DeveloperMode.js @@ -5,7 +5,7 @@ * */ -function DeveloperMode() { +function DeveloperMode () { let [done, action, command, userAddon, test] = [false, false, false, false, false]; let [watchSent, watchRecv, blockSent, blockRecv] = [[], [], [], []]; const runCommand = function (msg) { @@ -168,7 +168,7 @@ function DeveloperMode() { }; // Received packet handler - const packetReceived = function(pBytes) { + const packetReceived = function (pBytes) { let ID = pBytes[0].toString(16); // Block received packets from list diff --git a/d2bs/kolbot/libs/scripts/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js index 9d08dca26..5b734c6c0 100644 --- a/d2bs/kolbot/libs/scripts/Endugu.js +++ b/d2bs/kolbot/libs/scripts/Endugu.js @@ -5,7 +5,7 @@ * */ -function Endugu() { +function Endugu () { Town.doChores(); Pather.useWaypoint(sdk.areas.FlayerJungle); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Eyeback.js b/d2bs/kolbot/libs/scripts/Eyeback.js index 2c0b1bac8..915de3fcf 100644 --- a/d2bs/kolbot/libs/scripts/Eyeback.js +++ b/d2bs/kolbot/libs/scripts/Eyeback.js @@ -5,12 +5,12 @@ * */ -function Eyeback() { +function Eyeback () { Town.doChores(); Pather.useWaypoint(sdk.areas.ArreatPlateau); Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Monster, sdk.monsters.preset.EyebacktheUnleashed)) { + if (!Pather.moveToPresetMonster(sdk.areas.FrigidHighlands, sdk.monsters.preset.EyebacktheUnleashed)) { throw new Error("Failed to move to Eyeback the Unleashed"); } diff --git a/d2bs/kolbot/libs/scripts/GetCube.js b/d2bs/kolbot/libs/scripts/GetCube.js index db62b980f..8631b61b5 100644 --- a/d2bs/kolbot/libs/scripts/GetCube.js +++ b/d2bs/kolbot/libs/scripts/GetCube.js @@ -7,7 +7,7 @@ function GetCube() { // Can't get the cube if we can't access the act - if (!Pather.accessToAct(2)) return false; + if (!me.accessToAct(2)) return false; console.log("Getting cube"); me.overhead("Getting cube"); diff --git a/d2bs/kolbot/libs/scripts/GetKeys.js b/d2bs/kolbot/libs/scripts/GetKeys.js index 157916967..523c5907f 100644 --- a/d2bs/kolbot/libs/scripts/GetKeys.js +++ b/d2bs/kolbot/libs/scripts/GetKeys.js @@ -5,7 +5,7 @@ * */ -function GetKeys() { +function GetKeys () { Town.doChores(); if (me.getItemsEx(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length < 3) { diff --git a/d2bs/kolbot/libs/scripts/GhostBusters.js b/d2bs/kolbot/libs/scripts/GhostBusters.js index a0625c022..334710bea 100644 --- a/d2bs/kolbot/libs/scripts/GhostBusters.js +++ b/d2bs/kolbot/libs/scripts/GhostBusters.js @@ -5,14 +5,16 @@ * */ -function GhostBusters() { +function GhostBusters () { const clearGhosts = function () { let room = getRoom(); if (!room) return false; const rooms = []; /** @param {Monster} monster */ - const check = (monster) => monster.isGhost && monster.distance <= 30; + const check = function (monster) { + return monster.isGhost && monster.distance <= 30; + }; do { rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); diff --git a/d2bs/kolbot/libs/scripts/KillDclone.js b/d2bs/kolbot/libs/scripts/KillDclone.js index e5c2aceb0..3a8642879 100644 --- a/d2bs/kolbot/libs/scripts/KillDclone.js +++ b/d2bs/kolbot/libs/scripts/KillDclone.js @@ -5,7 +5,7 @@ * */ -function KillDclone() { +function KillDclone () { Pather.useWaypoint(sdk.areas.ArcaneSanctuary); Precast.doPrecast(true); From 34a8187249bf33f4177b5b8749bf225f54a26ba5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 16:37:59 -0400 Subject: [PATCH 198/758] Update ToolsThread.js - minor cleaup --- d2bs/kolbot/threads/ToolsThread.js | 47 ++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 27c3c4a03..42589916c 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -54,11 +54,13 @@ function main () { // General functions Common.Toolsthread.pauseScripts = [ - "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", + "default.dbj", "threads/townchicken.js", + "threads/autobuildthread.js", "threads/antihostile.js", "threads/party.js", "threads/rushthread.js" ]; Common.Toolsthread.stopScripts = [ - "default.dbj", "threads/townchicken.js", "threads/autobuildthread.js", "threads/antihostile.js", + "default.dbj", "threads/townchicken.js", + "threads/autobuildthread.js", "threads/antihostile.js", "threads/party.js", "threads/rushthread.js", "libs//modules/guard.js" ]; @@ -144,11 +146,20 @@ function main () { let nResult = NTIP.CheckItem(itemToCheck, false, true); let nString = "ÿc4NTIP.CheckItem: ÿc0" + nResult.result + " ÿc7Line: ÿc0" + nResult.line + "\n"; - itemString = "ÿc4ItemName: ÿc0" + itemToCheck.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "") - + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + "| ÿc4Classid: ÿc0" + itemToCheck.classid + "| ÿc4Quality: ÿc0" + itemToCheck.quality + "| ÿc4Gid: ÿc0" + itemToCheck.gid - + "\nÿc4ItemMode: ÿc0" + itemToCheck.mode + "| ÿc4Location: ÿc0" + itemToCheck.location + "| ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation; + itemString = ( + "ÿc4ItemName: ÿc0" + itemToCheck.prettyPrint + + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType + + "| ÿc4Classid: ÿc0" + itemToCheck.classid + + "| ÿc4Quality: ÿc0" + itemToCheck.quality + + "| ÿc4Gid: ÿc0" + itemToCheck.gid + + "\nÿc4ItemMode: ÿc0" + itemToCheck.mode + + "| ÿc4Location: ÿc0" + itemToCheck.location + + "| ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation + ); generalString = pString + nString - + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck); + + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) + + " | ÿc4Runeword Item: ÿc0" + Runewords.keepItem(itemToCheck) + + " | ÿc4Crafting Item: ÿc0" + CraftingSystem.keepItem(itemToCheck); } console.log("ÿc2*************Item Info Start*************"); @@ -349,24 +360,36 @@ function main () { while (true) { try { if (me.gameReady && !me.inTown) { - Config.UseHP > 0 && me.hpPercent < Config.UseHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Health); - Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + if (Config.UseHP > 0 && me.hpPercent < Config.UseHP) { + Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Health); + } + if (Config.UseRejuvHP > 0 && me.hpPercent < Config.UseRejuvHP) { + Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + } /** * Feel like potting and lifechicken should actually be seperate threads */ - if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken) { + if (Config.LifeChicken > 0 && me.hpPercent <= Config.LifeChicken && !me.inTown) { // takes a moment sometimes for townchicken to actually get to town so re-check that we aren't in town before quitting if (!me.inTown) { - D2Bot.printToConsole("Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red); + D2Bot.printToConsole( + "Life Chicken (" + me.hp + "/" + me.hpmax + ")" + Attack.getNearestMonster() + + " in " + getAreaName(me.area) + ". Ping: " + me.ping, + sdk.colors.D2Bot.Red + ); Common.Toolsthread.exit(true); break; } } - Config.UseMP > 0 && me.mpPercent < Config.UseMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Mana); - Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + if (Config.UseMP > 0 && me.mpPercent < Config.UseMP) { + Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Mana); + } + if (Config.UseRejuvMP > 0 && me.mpPercent < Config.UseRejuvMP) { + Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.Rejuv); + } if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); From 9c4b43e04b122d5c8fc5381eb25eb9df866f8117 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 23:31:42 -0400 Subject: [PATCH 199/758] Add summonType to SkillData - update skills that have a summonType - add getter in Skill.js --- d2bs/kolbot/libs/core/GameData/SkillData.js | 31 +++++++++++++++++++++ d2bs/kolbot/libs/core/Skill.js | 9 ++++++ 2 files changed, 40 insertions(+) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 8387cefba..ce0c13602 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -13,6 +13,7 @@ * @property {boolean} [missile] * @property {number | () => number} range * @property {number} [state] + * @property {number} [summonType] * @property {() => boolean} [condition] * @property {() => number} [summonCount] */ @@ -160,6 +161,7 @@ skillMap.set(sdk.skills.Decoy, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.Dopplezon, duration: () => ((10 + me.getSkill(sdk.skills.Decoy, sdk.skills.subindex.SoftPoints) * 5)), condition: () => Config.UseDecoy, }); @@ -181,6 +183,7 @@ skillMap.set(sdk.skills.Valkyrie, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.Valkyrie, summonCount: () => 1, condition: () => Config.SummonValkyrie, }); @@ -346,6 +349,7 @@ skillMap.set(sdk.skills.Hydra, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.Hydra, duration: () => 10, AoE: () => 14, }); @@ -390,6 +394,7 @@ skillMap.set(sdk.skills.RaiseSkeleton, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Skeleton, summonCount: () => { let skillNum = me.getSkill(sdk.skills.RaiseSkeleton, sdk.skills.subindex.SoftPoints); return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); @@ -430,6 +435,7 @@ skillMap.set(sdk.skills.ClayGolem, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Golem, summonCount: () => 1, }); skillMap.set(sdk.skills.IronMaiden, { @@ -466,6 +472,7 @@ skillMap.set(sdk.skills.RaiseSkeletalMage, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.SkeletonMage, summonCount: () => { let skillNum = me.getSkill(sdk.skills.RaiseSkeletalMage, sdk.skills.subindex.SoftPoints); return skillNum < 4 ? skillNum : (Math.floor(skillNum / 3) + 2); @@ -507,6 +514,7 @@ skillMap.set(sdk.skills.BloodGolem, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Golem, summonCount: () => 1, }); skillMap.set(sdk.skills.Attract, { @@ -543,6 +551,7 @@ skillMap.set(sdk.skills.IronGolem, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Golem, summonCount: () => 1, }); skillMap.set(sdk.skills.LowerResist, { @@ -566,11 +575,13 @@ skillMap.set(sdk.skills.FireGolem, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Golem, summonCount: () => 1, }); skillMap.set(sdk.skills.Revive, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Revive, summonCount: () => me.getSkill(sdk.skills.Revive, sdk.skills.subindex.SoftPoints), }); } @@ -876,6 +887,7 @@ hand: sdk.skills.hand.RightShift, range: 40, state: sdk.states.Terror, + summonType: sdk.summons.type.Totem, duration: () => 40, AoE: () => (2 + me.getSkill(sdk.skills.GrimWard, sdk.skills.subindex.SoftPoints) * (2 / 3)), }); @@ -936,12 +948,14 @@ skillMap.set(sdk.skills.Raven, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Raven, summonCount: () => Math.min(me.getSkill(sdk.skills.Raven, sdk.skills.subindex.SoftPoints), 5), condition: () => Config.SummonRaven, }); skillMap.set(sdk.skills.PoisonCreeper, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Vine, // condition: () => (typeof Config.SummonVine === "string" // ? Config.SummonVine.toLowerCase() === "poison" // : Config.SummonVine === sdk.skills.PoisonCreeper), @@ -965,6 +979,7 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.OakSage, + summonType: sdk.summons.type.Spirit, // condition: () => (typeof Config.SummonSpirit === "string" // ? Config.SummonSpirit.toLowerCase() === "oak" // : Config.SummonSpirit === sdk.skills.OakSage), @@ -973,6 +988,7 @@ skillMap.set(sdk.skills.SpiritWolf, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.SpiritWolf, // condition: () => (typeof Config.SummonAnimal === "string" // ? Config.SummonAnimal.toLowerCase() === "spirit wolf" // : Config.SummonAnimal === sdk.skills.SpiritWolf), @@ -1003,6 +1019,7 @@ skillMap.set(sdk.skills.CarrionVine, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Vine, // condition: () => (typeof Config.SummonVine === "string" // ? Config.SummonVine.toLowerCase() === "carion" // : Config.SummonVine === sdk.skills.CarrionVine), @@ -1033,6 +1050,7 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.HeartofWolverine, + summonType: sdk.summons.type.Spirit, // condition: () => (typeof Config.SummonSpirit === "string" // ? Config.SummonSpirit.toLowerCase() === "wolverine" // : Config.SummonSpirit === sdk.skills.HeartofWolverine), @@ -1041,6 +1059,7 @@ skillMap.set(sdk.skills.SummonDireWolf, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.DireWolf, // condition: () => (typeof Config.SummonAnimal === "string" // ? Config.SummonAnimal.toLowerCase() === "dire wolf" // : Config.SummonAnimal === sdk.skills.SummonDireWolf), @@ -1064,6 +1083,7 @@ skillMap.set(sdk.skills.SolarCreeper, { hand: sdk.skills.hand.Right, range: 40, + summonType: sdk.summons.type.Vine, // condition: () => (typeof Config.SummonVine === "string" // ? Config.SummonVine.toLowerCase() === "solar" // : Config.SummonVine === sdk.skills.SolarCreeper), @@ -1091,6 +1111,7 @@ hand: sdk.skills.hand.Right, range: 40, state: sdk.states.Barbs, + summonType: sdk.summons.type.Spirit, // condition: () => (typeof Config.SummonSpirit === "string" // ? Config.SummonSpirit.toLowerCase() === "barbs" // : Config.SummonSpirit === sdk.skills.SpiritofBarbs), @@ -1100,6 +1121,7 @@ hand: sdk.skills.hand.Right, range: 40, timed: true, + summonType: sdk.summons.type.Grizzly, // condition: () => (typeof Config.SummonAnimal === "string" // ? Config.SummonAnimal.toLowerCase() === "grizzly" // : Config.SummonAnimal === sdk.skills.SummonGrizzly), @@ -1179,11 +1201,13 @@ skillMap.set(sdk.skills.ChargedBoltSentry, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.AssassinTrap, summonCount: () => 5, }); skillMap.set(sdk.skills.WakeofFireSentry, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.AssassinTrap, summonCount: () => 5, }); skillMap.set(sdk.skills.WeaponBlock, { @@ -1215,6 +1239,7 @@ skillMap.set(sdk.skills.ShadowWarrior, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.Shadow, // condition: () => Config.SummonValkyrie, summonCount: () => 1, }); @@ -1229,11 +1254,13 @@ skillMap.set(sdk.skills.LightningSentry, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.AssassinTrap, summonCount: () => 5, }); skillMap.set(sdk.skills.InfernoSentry, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.AssassinTrap, summonCount: () => 5, }); skillMap.set(sdk.skills.MindBlast, { @@ -1252,6 +1279,7 @@ skillMap.set(sdk.skills.DeathSentry, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.AssassinTrap, summonCount: () => 5, }); skillMap.set(sdk.skills.BladeShield, { @@ -1272,6 +1300,7 @@ skillMap.set(sdk.skills.ShadowMaster, { hand: sdk.skills.hand.Right, range: 30, + summonType: sdk.summons.type.Shadow, // condition: () => Config.SummonValkyrie, summonCount: () => 1, }); @@ -1301,6 +1330,8 @@ this.state = (_skillData.state || sdk.states.None); /** @type {() => number} */ this.summonCount = (_skillData.summonCount || (() => 0)); + /** @type {number} */ + this.summonType = (_skillData.summonType || 0); /** @type {() => boolean} */ this.condition = (_skillData.condition || (() => true)); /** @type {boolean} */ diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 3c3d63769..96abb82c5 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -312,6 +312,15 @@ return _SkillData.get(skillId).summonCount(); }, + /** + * @param {number} skillId + * @returns {number} + */ + getSummonType: function (skillId) { + if (!_SkillData.has(skillId)) return 0; + return _SkillData.get(skillId).summonType; + }, + /** * @param {number} skillId * @returns {number} From 7d1774c37c5d07933175e20cd8cf8e355b274340 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 17 Aug 2023 23:52:59 -0400 Subject: [PATCH 200/758] Update Skill.js - fix typo --- d2bs/kolbot/libs/core/Skill.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 96abb82c5..c1d188b21 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -589,9 +589,9 @@ if (Config.PacketCasting > 1) { if (typeof x === "number") { - Packet.castSkill(sdk.skills.hand.Right, x, y); + Packet.castSkill(hand, x, y); } else if (typeof x === "object") { - Packet.unitCast(sdk.skills.hand.Right, x); + Packet.unitCast(hand, x); } delay(250); } else { From 3462a6c1187577c3951be133ddf073f6fa21fd31 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:26:24 -0400 Subject: [PATCH 201/758] Create Locations.js - Simplify the repeated locations in each entry file. --- d2bs/kolbot/libs/oog/Locations.js | 417 ++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 d2bs/kolbot/libs/oog/Locations.js diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js new file mode 100644 index 000000000..89bc810d6 --- /dev/null +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -0,0 +1,417 @@ +/** +* @filename Locations.js +* @author theBGuy +* @desc Map of the out of game locations +* +*/ + +(function (module) { + const Controls = require("../modules/Control"); + /** + * @param {Control} control + * @returns {string} + */ + const parseControlText = function (control) { + if (!control) return ""; + let text = control.getText(); + if (!text || !text.length) return ""; + return text.join(" "); + }; + /** + * @param {number[]} locations + * @param {(location?: number) => any} action + */ + const addLocations = function (locations, action) { + locations.forEach(function (loc) { + _loc.set(loc, action); + }); + }; + const pType = Profile().type; + /** + * Default locations written as if bot is running d2botlead + */ + const _loc = new Map([ + [sdk.game.locations.GatewaySelect, + function () { + Controls.GatewayCancel.click(); + } + ], + [sdk.game.locations.OtherMultiplayer, + function () { + const pType = Profile().type; + if ([sdk.game.profiletype.TcpIpHost, sdk.game.profiletype.TcpIpJoin].includes(pType)) { + if (Controls.TcpIp.click()) { + pType === sdk.game.profiletype.TcpIpHost + ? Controls.TcpIpHost.click() + : Controls.TcpIpJoin.click(); + } + } else if (pType === sdk.game.profiletype.OpenBattlenet) { + Controls.OpenBattleNet.click(); + } else { + Controls.OtherMultiplayerCancel.click(); + } + } + ], + [sdk.game.locations.TcpIpEnterIp, + function () { + Controls.PopupNo.click(); + } + ], + [sdk.game.locations.MainMenu, + function () { + switch (pType) { + case sdk.game.profiletype.OpenBattlenet: + case sdk.game.profiletype.TcpIpHost: + case sdk.game.profiletype.TcpIpJoin: + Controls.OtherMultiplayer.click(); + + break; + case sdk.game.profiletype.Battlenet: + Controls.BattleNet.click(); + + break; + case sdk.game.profiletype.SinglePlayer: + default: + Controls.SinglePlayer.click(); + + break; + } + } + ], + [sdk.game.locations.MainMenuConnecting, + function (location) { + if (!Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location)) { + Controls.LoginCancelWait.click(); + } + } + ], + [sdk.game.locations.OkCenteredErrorPopUp, + function () { + Controls.OkCentered.click(); + Controls.BottomLeftExit.click(); + } + ], + [sdk.game.locations.CharSelectNoChars, + function () { + Starter.LocationEvents.charSelectError(); + } + ], + [sdk.game.locations.CharSelectConnecting, + function () { + Starter.LocationEvents.charSelectError(); + } + ], + [sdk.game.locations.CharSelectPleaseWait, + function (location) { + if (!Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location)) { + Controls.OkCentered.click(); + } + } + ], + [sdk.game.locations.SelectDifficultySP, + function () { + Starter.LocationEvents.selectDifficultySP(); + } + ], + [sdk.game.locations.RealmDown, + function () { + Starter.LocationEvents.realmDown(); + } + ], + [sdk.game.locations.GameNameExists, + function () { + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + } + ], + [sdk.game.locations.GameDoesNotExist, + function () { + Starter.LocationEvents.gameDoesNotExist(); + } + ], + [sdk.game.locations.CreateGame, + function () { + 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(); + } + + if (typeof Starter.Config.MaxPlayerCount === "number") { + Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); + } + + // Get game name if there is none + while (!Starter.gameInfo.gameName) { + D2Bot.requestGameInfo(); + delay(500); + } + + // 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(); + } + + const gameName = (Starter.gameInfo.gameName === "?" + ? Starter.randomString(null, true) + : Starter.gameInfo.gameName + Starter.gameCount); + const gamePass = (Starter.gameInfo.gamePass === "?" + ? Starter.randomString(null, true) + : Starter.gameInfo.gamePass); + + ControlAction.createGame( + gameName, + gamePass, + Starter.gameInfo.difficulty, + Starter.Config.CreateGameDelay * 1000 + ); + + Starter.lastGameStatus = "pending"; + Starter.setNextGame(Starter.gameInfo); + Starter.locationTimeout(10000, location); + } + ], + [sdk.game.locations.WaitingInLine, + function () { + Starter.LocationEvents.waitingInLine(); + } + ], + [sdk.game.locations.TcpIp, + function () { + pType === sdk.game.profiletype.TcpIpHost + ? Controls.TcpIpHost.click() + : Controls.TcpIpCancel.click(); + } + ], + [sdk.game.locations.Lobby, + function () { + D2Bot.updateStatus("Lobby"); + + me.blockKeys = false; + Starter.loginRetry = 0; + !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) { + ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + } + } + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + 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.LocationEvents.openCreateGameWindow(); + } + ], + [sdk.game.locations.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 (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + 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; + + // check that we are in the channel we are supposed to be in + if (Starter.chanInfo.joinChannel.length) { + let chanName = Controls.LobbyChannelName.getText(); + chanName && (chanName = chanName.toString()); + chanName && (chanName = chanName.slice(0, chanName.indexOf("(") - 1)); + Starter.chanInfo.joinChannel.indexOf(chanName) === -1 && (Starter.chatActionsDone = false); + } + + if (Starter.chanInfo.afterMsg) { + if (typeof Starter.chanInfo.afterMsg === "string") { + Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]; + } + + for (let msg of Starter.chanInfo.afterMsg) { + Starter.sayMsg(msg); + delay(500); + } + } + } + + if (!Starter.chatActionsDone) { + Starter.chatActionsDone = true; + Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; + Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; + + if (Starter.chanInfo.joinChannel) { + typeof Starter.chanInfo.joinChannel === "string" && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); + typeof Starter.chanInfo.firstMsg === "string" && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); + + for (let i = 0; i < Starter.chanInfo.joinChannel.length; i += 1) { + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + + if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { + Starter.useChat = true; + } else { + print("ÿ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; + + Starter.LocationEvents.openCreateGameWindow(); + } + ], + ]); + addLocations([sdk.game.locations.PreSplash, sdk.game.locations.SplashScreen], + function () { + ControlAction.click(); + } + ); + addLocations( + [ + sdk.game.locations.JoinGame, + sdk.game.locations.Ladder, + sdk.game.locations.ChannelList + ], + function () { + Starter.LocationEvents.openCreateGameWindow(); + } + ); + addLocations([sdk.game.locations.Login, sdk.game.locations.CharSelect], + function () { + const otherMulti = [ + sdk.game.profiletype.TcpIpHost, + sdk.game.profiletype.OpenBattlenet + ].includes(pType); + Starter.LocationEvents.login(otherMulti); + } + ); + addLocations([sdk.game.locations.CharSelectConnecting, sdk.game.locations.CharSelectNoChars], + function () { + Starter.LocationEvents.charSelectError(); + } + ); + addLocations([sdk.game.locations.LoginUnableToConnect, sdk.game.locations.TcpIpUnableToConnect], + function () { + Starter.LocationEvents.unableToConnect(); + } + ); + addLocations([sdk.game.locations.CharSelectPleaseWait, sdk.game.locations.LobbyPleaseWait], + function (location) { + if (!Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location)) { + Controls.OkCentered.click(); + } + } + ); + addLocations([sdk.game.locations.Disconnected, sdk.game.locations.LobbyLostConnection], + function () { + D2Bot.updateStatus("Disconnected/LostConnection"); + delay(1000); + Controls.OkCentered.click(); + } + ); + addLocations( + [ + sdk.game.locations.LoginError, + sdk.game.locations.InvalidCdKey, + sdk.game.locations.CdKeyInUse, + ], + function () { + Starter.LocationEvents.loginError(); + } + ); + addLocations( + [ + sdk.game.locations.CreateNewAccount, + sdk.game.locations.CharacterCreate, + sdk.game.locations.NewCharSelected, + ], + function () { + Controls.BottomLeftExit.click(); + } + ); + addLocations([sdk.game.locations.GameNameExists, sdk.game.locations.GameIsFull], + function () { + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + } + ); + + module.exports = { + locations: _loc, + addLocations: addLocations, + parseControlText: parseControlText, + }; +})(module); From 86304dac37ea021dbad9c0066a51f8090a29e31d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Aug 2023 17:10:17 -0400 Subject: [PATCH 202/758] Create CleanerConfig.js - move cleaner configuration to it's own systems file and out of the entry --- .../libs/systems/cleaner/CleanerConfig.js | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 d2bs/kolbot/libs/systems/cleaner/CleanerConfig.js diff --git a/d2bs/kolbot/libs/systems/cleaner/CleanerConfig.js b/d2bs/kolbot/libs/systems/cleaner/CleanerConfig.js new file mode 100644 index 000000000..8bfb4824e --- /dev/null +++ b/d2bs/kolbot/libs/systems/cleaner/CleanerConfig.js @@ -0,0 +1,125 @@ +/** +* @filename CleanerConfig.js +* @author theBGuy +* @desc Configuration file for Cleaner system +* +*/ + +(function (module) { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + // D2BotCleaner settings - for global settings @see libs/starter/StarterConfig.js + // New Stuff: + // DataCleaner - to delete old files associated with running kolbot or SoloPlay + // SaveFiles - to save important SoloPlay files to SoloPlay/Data/ for performance review + //***********************************************************************************************************************// + // DataCleaner and SaveFiles can both be used for cleaning/saving files without having to delete associated characters // + //***********************************************************************************************************************// + const CleanerConfig = { + /** + * Always run this when re-using a profile with Kolbot-SoloPlay + */ + DataCleaner: true, + /** + * NOTE: Only works on SoloPlay profiles. + * Highly recommened to run this if using the peformance tracking system and wish to review them later + */ + SaveFiles: false, + /** + * Seconds to wait before cleaning next account + * If doing 10+ accounts recommended to increase this delay to rand(30, 60) prevent R/D + */ + DelayBetweenAccounts: rand(15, 30), + }; + + /** + * @todo this section should be in it's own config leaving this file only containing core logic + * @example Format + * "account1/password1/realm": ["charname1", "charname2"], + * "account2/password2/realm": ["charnameX", "charnameY"], + * "account3/password3/realm": ["all"] + * + * // To clean a full account, put "account/password/realm": ["all"] + * + * // realm = useast, uswest, europe, asia + * + * // for singleplayer follow format "singleplayer": ["charname1", "charname2"] + * + * // Individual entries are separated with a comma. + * @example + * "MyAcc1/tempPass/useast": ["soloSorc"], + * "singleplayer": ["solobarb"], + * + * @type {Object} + */ + const AccountsToClean = { + // Enter your lines under here + }; + + const CharactersToExclude = [""]; + + /** + * NEW STUFF - Please enter your profile name exactly as it appears in D2Bot# + * @example + * "SCL-ZON123", "hcnl-pal123", + * @type {string[]} + */ + const profiles = [ + // Enter your lines under here + + ]; + + /** + * @description If you have a lot of profiles that are clones this can be used as an easier way to clean all of them + * @param {string} profilePrefix + * - this is everthing before the suffix numbers. Ex: mypal01 or sccl-pal-001, ect + * @param {string} profileSuffixStart + * - this is the suffix to start at, Ex: 01 or 001 or 1, all the profiles need have the same format. + * CANNOT HAVE scl-pal-1 and scl-pal-001 + * @param {string} end + * - the ending profile suffix, this is used to stop the loop. + * If you are doing scl-pal-001 to scl-pal-100 (that'd be alot) then 100 would go here + * @example + * // This will clean all profiles from scl-sorc-002 to scl-sorc-009 + * { + * profilePrefix: "scl-sorc-", + * profileSuffixStart: "002", + * end: "009" + * } + * @type {Array<{profilePrefix: string, profileSuffixStart: string, end: string}>} + */ + const AdvancedProfileCleanerConfig = [ + // { + // profilePrefix: "scl-sorc-", + // profileSuffixStart: "002", + // end: "009" + // }, + // Your lines under here + ]; + + /** + * @description Generate accounts to entirely clean ("all") + * - To use this, set `generateAccounts` to true and setup the rest of the parameters + * + * - It will generates accounts from start to stop range(included): + * account1/password/realm + * account2/password/realm + * etc... + */ + const AdvancedCleanerConfig = { + generateAccounts: false, + accountPrefix: "account", + accountPassword: "password", + accountRealm: "realm", + rangeStart: 1, + rangeStop: 10 + }; + + module.exports = { + CleanerConfig: CleanerConfig, + AccountsToClean: AccountsToClean, + CharactersToExclude: CharactersToExclude, + profiles: profiles, + AdvancedProfileCleanerConfig: AdvancedProfileCleanerConfig, + AdvancedCleanerConfig: AdvancedCleanerConfig + }; +})(module); From 2390fcb11038706021ebe5b98fdb571e8fd15c9c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Aug 2023 17:11:33 -0400 Subject: [PATCH 203/758] Update D2BotCleaner.dbj - remove configuration settings from entry, CleanerConfig is it's own file now - refactor using the Locations map --- d2bs/kolbot/D2BotCleaner.dbj | 342 ++++++++++++----------------------- 1 file changed, 118 insertions(+), 224 deletions(-) diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index c8dabb35e..57f174dd3 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /** * @filename D2BotCleaner.dbj * @author theBGuy @@ -11,96 +10,19 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // D2BotCleaner settings - for global settings @see libs/starter/StarterConfig.js -// New Stuff: -// DataCleaner - to delete old files associated with running kolbot or SoloPlay -// SaveFiles - to save important SoloPlay files to SoloPlay/Data/ for performance review -//***********************************************************************************************************************// -// DataCleaner and SaveFiles can both be used for cleaning/saving files without having to delete associated characters // -//***********************************************************************************************************************// -Starter.Config.DataCleaner = true; // Always run this when re-using a profile with Kolbot-SoloPlay -Starter.Config.SaveFiles = false; // NOTE: Only works on SoloPlay profiles, Highly recommened to run this if using the peformance tracking system and wish to review them later - -// Old Stuff -Starter.Config.DelayBetweenAccounts = rand(15, 30); //Seconds to wait before cleaning next account, if doing 10+ accounts recommended to increase this delay to rand(30, 60) prevent R/D - +const { + CleanerConfig, + AccountsToClean, + CharactersToExclude, + profiles, + AdvancedProfileCleanerConfig, + AdvancedCleanerConfig, +} = require("./libs/systems/cleaner/CleanerConfig"); +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -/** - * @todo this section should be in it's own config leaving this file only containing core logic - */ -const AccountsToClean = { - /* Format: - "account1/password1/realm": ["charname1", "charname2"], - "account2/password2/realm": ["charnameX", "charnameY"], - "account3/password3/realm": ["all"] - - To clean a full account, put "account/password/realm": ["all"] - - realm = useast, uswest, europe, asia - - for singleplayer follow format "singleplayer": ["charname1", "charname2"] - - Individual entries are separated with a comma. - */ - - /* Example: - "MyAcc1/tempPass/useast": ["soloSorc"], - "singleplayer": ["solobarb"], - */ - - // Enter your lines under here - -}; - -const CharactersToExclude = [""]; - -// NEW STUFF - Please enter your profile name exactly as is -const profiles = [ - /* Format. Enter in profile exactly the way it appears in D2Bot# - "SCL-ZON123", - "hcnl-pal123", - */ - // Enter your lines under here - -]; - -/* - If you have a lot of profiles that are clones this can be used as an easier way to clean all of them - { - profilePrefix: this is everthing before the suffix numbers. Ex: mypal01 or sccl-pal-001, ect - profileSuffixStart: this is the suffix to start at, Ex: 01 or 001 or 1, all the profiles need have the same format. CANNOT HAVE scl-pal-1 and scl-pal-001 - end: the ending profile suffix, this is used to stop the loop. If you are doing scl-pal-001 to scl-pal-100 (that'd be alot) then 100 would go here - } -*/ -const AdvancedProfileCleanerConfig = [ - // { - // profilePrefix: "scl-sorc-", - // profileSuffixStart: "002", - // end: "009" - // }, - // Your lines under here -]; - -/* Generate accounts to entirely clean ("all") - to use this, set generateAccounts to true and setup the rest of the parameters - - it will generates accounts from start to stop range(included) : - account1/password/realm - account2/password/realm - etc... -*/ - -const AdvancedCleanerConfig = { - generateAccounts: false, - accountPrefix: "account", - accountPassword: "password", - accountRealm: "realm", - rangeStart: 1, - rangeStop: 10 -}; - -let Controls = require("./modules/Control"); +let Controls = require("./libs/modules/Control"); if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; @@ -108,8 +30,9 @@ if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { let currAcc, charList, realm; let firstAccount = true; -let accounts = []; -let chars = []; +let obj = {}; +const accounts = []; +const chars = []; function dataCleaner () { if (AdvancedProfileCleanerConfig.length) { @@ -128,40 +51,47 @@ function dataCleaner () { }); } if (!profiles.length) { - D2Bot.printToConsole("D2BotCleaner: No profiles entered to clean. If this was a mistake, fill out profile information under NEW STUFF. Exiting dataCleaner and moving on to clean characters...", sdk.colors.D2Bot.Gold); + D2Bot.printToConsole( + "D2BotCleaner: No profiles entered to clean. " + + "If this was a mistake, fill out profile information under NEW STUFF. " + + "Exiting dataCleaner and moving on to clean characters...", sdk.colors.D2Bot.Gold + ); return; } - let charClass; let folder, j; - let charClassMap = { "ZON": "amazon", "SOR": "sorceress", "NEC": "necromancer", "PAL": "paladin", "BAR": "barbarian", "DRU": "druid", "SIN": "assassin" }; - - for (let i = 0; i < profiles.length; i++) { - let buildCheck = profiles[i].toUpperCase().split("-"); - buildCheck[1] = buildCheck[1].toString().substring(0, 3).toUpperCase(); - let charType = buildCheck[0].includes("CC") ? "Classic" : "Expansion"; + const charClassMap = new Map([ + ["ZON", "amazon"], + ["SOR", "sorceress"], + ["NEC", "necromancer"], + ["PAL", "paladin"], + ["BAR", "barbarian"], + ["DRU", "druid"], + ["SIN", "assassin"] + ]); + + for (let profile of profiles) { + const buildCheck = profile.toUpperCase().split("-"); + const charClass = charClassMap.get(buildCheck[1].substring(0, 3)) || "undefined"; + buildCheck[1] = buildCheck[1].substring(0, 3); + const charType = buildCheck[0].includes("CC") ? "Classic" : "Expansion"; let profileExists = false; - let soloplayProfile = false; + const soloplayProfile = charClass !== "undefined"; // Filepaths - let dataFP = "data/" + profiles[i] + ".json"; - let gameTimeFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-GameTime" + ".json"; - let charDataFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-CharData" + ".json"; - let lvlPerfFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-LevelingPerformance" + ".csv"; - let scrPerfFP = "libs/SoloPlay/Data/" + profiles[i] + "/" + profiles[i] + "-ScriptPerformance" + ".csv"; + const dataFP = "data/" + profile + ".json"; + const gameTimeFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-GameTime" + ".json"; + const charDataFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-CharData" + ".json"; + const lvlPerfFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-LevelingPerformance" + ".csv"; + const scrPerfFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-ScriptPerformance" + ".csv"; let savePath = "logs/"; // default value in case something goes wrong with assigning actual savePath - if (charClassMap[buildCheck[1]]) { - charClass = charClassMap[buildCheck[1]]; - soloplayProfile = true; - } else { - //D2Bot.printToConsole("D2BotCleaner: Failed to get charClass. Please check that your profile was entered correctly under NEW STUFF.", sdk.colors.D2Bot.Gold); - //print("Invalid profile name, couldn't set character class"); - charClass = "undefined"; - } - - if (Starter.Config.SaveFiles && soloplayProfile) { - if (FileTools.exists(dataFP) || FileTools.exists(gameTimeFP) || FileTools.exists(charDataFP) || FileTools.exists(lvlPerfFP) || FileTools.exists(scrPerfFP)) { + if (CleanerConfig.SaveFiles && soloplayProfile) { + if (FileTools.exists(dataFP) + || FileTools.exists(gameTimeFP) + || FileTools.exists(charDataFP) + || FileTools.exists(lvlPerfFP) + || FileTools.exists(scrPerfFP)) { // Create folder to copy files to if (!FileTools.exists("libs/SoloPlay/Data/" + charType)) { folder = dopen("libs/SoloPlay/Data"); @@ -187,43 +117,43 @@ function dataCleaner () { folder.create(j.toString()); } - savePath = "libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString() + "/" + profiles[i]; + savePath = "libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString() + "/" + profile; profileExists = true; } } if (FileTools.exists(dataFP)) { - Starter.Config.SaveFiles && FileTools.copy(dataFP, savePath + "Old.json"); + CleanerConfig.SaveFiles && FileTools.copy(dataFP, savePath + "Old.json"); FileTools.remove(dataFP); profileExists = true; } if (FileTools.exists(gameTimeFP)) { - Starter.Config.SaveFiles && FileTools.copy(gameTimeFP, savePath + "-GameTimeOld.json"); + CleanerConfig.SaveFiles && FileTools.copy(gameTimeFP, savePath + "-GameTimeOld.json"); FileTools.remove(gameTimeFP); } if (FileTools.exists(charDataFP)) { - Starter.Config.SaveFiles && FileTools.copy(charDataFP, savePath + "-CharDataOld.json"); + CleanerConfig.SaveFiles && FileTools.copy(charDataFP, savePath + "-CharDataOld.json"); FileTools.remove(charDataFP); } if (FileTools.exists(lvlPerfFP)) { - Starter.Config.SaveFiles && FileTools.copy(lvlPerfFP, savePath + "-LevelingPerformanceOld.csv"); + CleanerConfig.SaveFiles && FileTools.copy(lvlPerfFP, savePath + "-LevelingPerformanceOld.csv"); FileTools.remove(lvlPerfFP); } if (FileTools.exists(scrPerfFP)) { - Starter.Config.SaveFiles && FileTools.copy(scrPerfFP, savePath + "-ScriptPerformanceOld.csv"); + CleanerConfig.SaveFiles && FileTools.copy(scrPerfFP, savePath + "-ScriptPerformanceOld.csv"); FileTools.remove(scrPerfFP); } - if (Starter.Config.SaveFiles && profileExists && soloplayProfile) { + if (CleanerConfig.SaveFiles && profileExists && soloplayProfile) { D2Bot.printToConsole("D2BotCleaner: Files saved to -> libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j, sdk.colors.D2Bot.Gold); } if (profileExists) { - D2Bot.printToConsole("D2BotCleaner: Cleaned files for -> " + profiles[i], sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("D2BotCleaner: Cleaned files for -> " + profile, sdk.colors.D2Bot.Gold); } delay(500); @@ -242,7 +172,8 @@ function parseInfo () { if (AdvancedCleanerConfig.generateAccounts) { for (let index = rangeStart; index <= rangeStop ; index += 1) { - accounts.push(AdvancedCleanerConfig.accountPrefix + index + "/" + AdvancedCleanerConfig.accountPassword + "/" + AdvancedCleanerConfig.accountRealm); + const { accountPrefix, accountPassword, accountRealm } = AdvancedCleanerConfig; + accounts.push(accountPrefix + index + "/" + accountPassword + "/" + accountRealm); chars.push(["all"]); } } @@ -250,7 +181,7 @@ function parseInfo () { if (!accounts.length) { FileTools.remove("logs/D2BotCleaner.json"); D2Bot.printToConsole("D2BotCleaner: No accounts entered. Exiting...", sdk.colors.D2Bot.Gold); - ControlAction.timeoutDelay("Exiting in: ", 3 * 1e3); + ControlAction.timeoutDelay("Exiting in: ", Time.seconds(3)); D2Bot.stop(me.profile, true); } } @@ -269,34 +200,39 @@ function deleteAllCharacters () { return true; } -function locationAction (location) { - let i, currChar, - obj = {}; - - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); +const { + locations, + addLocations +} = require("./libs/oog/Locations"); - break; - case sdk.game.locations.WaitingInLine: +locations.set(sdk.game.locations.WaitingInLine, + function () { Controls.CancelCreateGame.click(); - - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - case sdk.game.locations.CreateGame: - case sdk.game.locations.JoinGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameDoesNotExist: - case sdk.game.locations.GameIsFull: + } +); +addLocations( + [ + sdk.game.locations.Lobby, + sdk.game.locations.LobbyChat, + sdk.game.locations.CreateGame, + sdk.game.locations.JoinGame, + sdk.game.locations.Ladder, + sdk.game.locations.ChannelList, + sdk.game.locations.GameNameExists, + sdk.game.locations.GameDoesNotExist, + sdk.game.locations.GameIsFull, + ], + function () { Controls.LobbyQuit.click(); - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.SplashScreen: + } +); +addLocations( + [ + sdk.game.locations.MainMenu, + sdk.game.locations.Login, + sdk.game.locations.SplashScreen, + ], + function () { if (!accounts.length) { FileTools.remove("logs/D2BotCleaner.json"); D2Bot.printToConsole("D2BotCleaner: Done cleaning accounts!", sdk.colors.D2Bot.Gold); @@ -304,10 +240,7 @@ function locationAction (location) { } if (!firstAccount) { - for (i = 0 ; i < Starter.Config.DelayBetweenAccounts; i += 1) { - D2Bot.updateStatus("Waiting " + (Starter.Config.DelayBetweenAccounts - i) + "s for next account"); - delay(1e3); - } + ControlAction.timeoutDelay("Waiting for next account in: ", Time.seconds(CleanerConfig.DelayBetweenAccounts)); } firstAccount = false; @@ -316,7 +249,7 @@ function locationAction (location) { obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); if (obj.currAcc) { - for (i = 0; i < accounts.length; i += 1) { + for (let i = 0; i < accounts.length; i += 1) { if (accounts[i].split("/")[0] === obj.currAcc) { accounts.splice(0, i); chars.splice(0, i); @@ -343,33 +276,27 @@ function locationAction (location) { realm = currAccInfo[2].toLowerCase(); ControlAction.loginAccount({ account: currAcc, password: currAccInfo[1], realm: realm }); } - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.CharSelect: - case sdk.game.locations.CharSelectNoChars: + } +); +addLocations( + [ + sdk.game.locations.CharSelect, + sdk.game.locations.CharSelectNoChars, + ], + function () { // Single Player screen fix if (currAcc.toLowerCase() !== "singleplayer") { if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { Controls.BottomLeftExit.click(); - break; + return; } } if (!charList.length) { Controls.BottomLeftExit.click(); - break; + return; } if (charList[0] === "all") { @@ -379,7 +306,7 @@ function locationAction (location) { obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); if (obj.currChar) { - for (i = 0; i < charList.length; i += 1) { + for (let i = 0; i < charList.length; i += 1) { if (charList[i] === obj.currChar) { // Remove the previous currChar as well charList.splice(0, i + 1); @@ -395,7 +322,7 @@ function locationAction (location) { delay(500); } - currChar = charList.shift(); + let currChar = charList.shift(); obj.currChar = currChar; // last char in acc = trigger next acc @@ -406,56 +333,20 @@ function locationAction (location) { } FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.CharacterCreate: - case sdk.game.locations.NewCharSelected: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: // Main Menu - Connecting - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: // Server Down - not much to do but wait.. - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); + } +); + +/** @param {number} loc */ +function locationAction (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); } - - break; + } catch (e) { + console.error(e); } } @@ -478,7 +369,10 @@ function main () { if (Starter.gameInfo.error) { if (!!DataFile.getStats().debugInfo) { Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray + ); } ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); @@ -487,7 +381,7 @@ function main () { DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); - Starter.Config.DataCleaner && dataCleaner(); + CleanerConfig.DataCleaner && dataCleaner(); !accounts.length && parseInfo(); while (true) { From 23dcb679c15b8301387031bc50f07f1902e7d511 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Aug 2023 17:46:02 -0400 Subject: [PATCH 204/758] typedef updates --- d2bs/kolbot/sdk/types/OOG.d.ts | 113 ++++++++++++++++++++++++++----- d2bs/kolbot/sdk/types/Skill.d.ts | 1 + 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/sdk/types/OOG.d.ts b/d2bs/kolbot/sdk/types/OOG.d.ts index a326e9710..791de0f70 100644 --- a/d2bs/kolbot/sdk/types/OOG.d.ts +++ b/d2bs/kolbot/sdk/types/OOG.d.ts @@ -54,22 +54,103 @@ declare global { remove(): void } - export const ControlAction: { - timeoutDelay(text: any, time: any, stopfunc?: any, arg?: any):void - click(type: any, x: any, y: any, xsize: any, ysize: any):void - setText(type: any, x: any, y: any, xsize: any, ysize: any, text: any):void - getText(type: any, x: any, y: any, xsize: any, ysize: any):string[] - joinChannel(channel: any):void - createGame(name: any, pass: any, diff: any, delay: any):void - clickRealm(realm: 0|1|2|3):void - loginAccount(info: any):void - makeAccount(info: any):void - findCharacter(info: any):void - getCharacters():void - getPosition():void - loginCharacter(info: any, startFromTop?: boolean):void - makeCharacter(info: any):void - getGameList():void + namespace ControlAction { + let mutedKey: boolean; + enum realms { + 'uswest' = 0, + 'useast' = 1, + 'asia' = 2, + 'europe' = 3 + }; + type ControlParams = { + type: number, + x: number, + y: number, + xsize: number, + ysize: number, + }; + type CharacterInfo = { + charName: string; + charClass: string; + charLevel: number; + expansion: boolean; + hardcore: boolean; + ladder: boolean; + }; + type AccountInfo = { + account: string; + password: string; + realm: realms; + }; + function timeoutDelay( + text: string, + time: number, + stopfunc?: (arg: any) => boolean, + arg?: any + ): void; + // function click( + // ...params: [targetx: number, targety: number, ...rest: ControlParams] + // ): boolean; + // function setText( + // text: string, + // ...params: ControlParams + // ): boolean; + // function getText( + // ...params: ControlParams + // ): string[]; + function click( + type: number, + x: number, + y: number, + xsize: number, + ysize: number, + targetx: number, + targety: number, + ): boolean; + + function setText( + type: number, + x: number, + y: number, + xsize: number, + ysize: number, + text: string + ): boolean; + + function getText( + type: number, + x: number, + y: number, + xsize: number, + ysize: number + ): string[]; + + function parseText( + type: number, + x: number, + y: number, + xsize: number, + ysize: number + ): string; + + function scrollDown(): void; + function clickRealm(realm: realms): boolean; + function findCharacter(info: CharacterInfo): Control | false; + function getCharacters(): string[]; + function getPermStatus(info: CharacterInfo): boolean; + function getPosition(): number; + function makeCharacter(info: CharacterInfo): boolean; + function deleteCharacter(info: CharacterInfo): boolean; + function convertCharacter(info: CharacterInfo): boolean; + function loginCharacter(info: CharacterInfo, startFromTop?: boolean): boolean; + function setEmail(email: string, domain?: string): boolean; + function makeAccount(info: AccountInfo): boolean; + function loginAccount(info: AccountInfo): boolean; + function joinChannel(channel: string): boolean; + function createGame(name: string, pass: string, diff: string, delay: number): void; + function getGameList(): { gameName: string, players: number }[] | false; + function getQueueTime(): number; + function loginOtherMultiplayer(): boolean; } } export {}; diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts index 810d72474..6c9269fb5 100644 --- a/d2bs/kolbot/sdk/types/Skill.d.ts +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -55,6 +55,7 @@ declare global { } namespace Skill { let usePvpRange: boolean; + const haveTK: boolean; const manaCostList: object; const needFloor: number[]; const missileSkills: number[]; From 2a94565f1557f8e8d6f916294c9f9d017b2acb8c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:11:31 -0400 Subject: [PATCH 205/758] Create GameAction.d.ts --- .../libs/systems/gameaction/GameAction.d.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts new file mode 100644 index 000000000..749de673b --- /dev/null +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts @@ -0,0 +1,23 @@ +// @ts-nocheck +declare global { + namespace GameAction { + let LogNames: boolean; + let LogItemLevel: boolean; + let LogEquipped: boolean; + let LogMerc: boolean; + let SaveScreenShot: boolean; + let IngameTime: number; + let task: any; // Update the type of `task` as needed + + function init(task: any): void; + function update(action: string, data: any): void; + function gameInfo(): { gameName: string, gamePass: string }; + function getLogin(): { realm: string, account: string, password: string }; + function getCharacters(): string[]; + function inGameCheck(): boolean; + function load(hash: string): string; + function save(hash: string, data: string): void; + function dropItems(dropList: string[]): void; + } +} +export {}; \ No newline at end of file From e2ba6e5eff9000c1fb9f86af2a868ea26a66195b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:22:37 -0400 Subject: [PATCH 206/758] Create `modules/workers` directory - initiate process of moving files --- .../libs/modules/{ => workers}/Guard.js | 0 .../libs/modules/{ => workers}/SimpleParty.js | 0 .../libs/modules/workers/TownChicken.js | 137 ++++++++++++++++++ 3 files changed, 137 insertions(+) rename d2bs/kolbot/libs/modules/{ => workers}/Guard.js (100%) rename d2bs/kolbot/libs/modules/{ => workers}/SimpleParty.js (100%) create mode 100644 d2bs/kolbot/libs/modules/workers/TownChicken.js diff --git a/d2bs/kolbot/libs/modules/Guard.js b/d2bs/kolbot/libs/modules/workers/Guard.js similarity index 100% rename from d2bs/kolbot/libs/modules/Guard.js rename to d2bs/kolbot/libs/modules/workers/Guard.js diff --git a/d2bs/kolbot/libs/modules/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js similarity index 100% rename from d2bs/kolbot/libs/modules/SimpleParty.js rename to d2bs/kolbot/libs/modules/workers/SimpleParty.js diff --git a/d2bs/kolbot/libs/modules/workers/TownChicken.js b/d2bs/kolbot/libs/modules/workers/TownChicken.js new file mode 100644 index 000000000..86cfe46ef --- /dev/null +++ b/d2bs/kolbot/libs/modules/workers/TownChicken.js @@ -0,0 +1,137 @@ +/** +* @filename TownChicken.js +* @author kolton, theBGuy +* @desc handle town chicken +* +*/ +js_strict(true); +include("critical.js"); + +// add core library +includeCoreLibs(); + +// handle systems +includeSystemLibs(); + +function main () { + let townCheck = false; + const scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; + + // override broadCastIntent - shouldn't be called at all in this thread + Pather.broadcastIntent = () => {}; + + const pause = function () { + for (let i = 0; i < scripts.length; i += 1) { + let script = getScript(scripts[i]); + + if (scripts[i] === "default.dbj" && !script) { + !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + } + + if (script && script.running) { + script.pause(); + scripts[i] === "default.dbj" && print("ÿc1Pausing."); + } + } + + return true; + }; + + const resume = function () { + for (let i = 0; i < scripts.length; i += 1) { + let script = getScript(scripts[i]); + + if (script && !script.running && scripts[i] !== "default.dbj") { + script.resume(); + } else if (scripts[i] === "default.dbj") { + // resume only if clonekilla isn't running + if (!getScript("threads/clonekilla.js")) { + if (script && !script.running) { + console.log("ÿc2Resuming."); + script.resume(); + } else { + if (!script) { + // default has crashed? We shouldn't be running then. Is toolsthread still up? + // if yes try to still quit normally, otherwise quit from here + !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + } + } + } + } + } + + return true; + }; + + addEventListener("scriptmsg", + function (msg) { + if (typeof msg === "string" && msg === "townCheck") { + townCheck = true; + // maybe check for quit broadcast as well? If we are preparing to quit we shouldn't really be townchickening + } + }); + + // Init config and attacks + console.log("ÿc3Start TownChicken thread"); + D2Bot.init(); + Config.init(); + Pickit.init(); + Attack.init(); + Storage.Init(); + CraftingSystem.buildLists(); + Runewords.init(); + Cubing.init(); + + let checkHP = Config.TownHP > 0; + let checkMP = Config.TownMP > 0; + + // START + // test for getUnit bug + Game.getMonster() === null && console.warn("getUnit is bugged"); + + while (true) { + if (!me.inTown && (townCheck + // should TownHP/MP check be in toolsthread? + // We would then be able to remove all game interaction checks until we get a townCheck msg + || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { + // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area + if (me.dead && Config.LifeChicken <= 0) { + console.log("ÿc1TownChicken :: ÿc0We are dead and LifeChicken is set to 0"); + while (me.dead) delay(100); + + continue; + } + if (!me.canTpToTown()) { + townCheck = false; + + continue; + } + pause(); + + while (!me.gameReady) { + if (me.dead) return; + + delay(100); + } + + try { + Messaging.sendToScript("threads/Toolsthread.js", "townChickenOn"); + console.log("ÿc8(TownChicken) :: ÿc0Going to town"); + me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); + Town.visitTown(); + } catch (e) { + Misc.errorReport(e, "TownChicken.js"); + scriptBroadcast("quit"); + + return; + } finally { + resume(); + + townCheck = false; + Messaging.sendToScript("threads/Toolsthread.js", "townChickenOff"); + } + } + + delay(50); + } +} From 2149ddec9aaad4b876b1031387c473c874d0b6ad Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:23:41 -0400 Subject: [PATCH 207/758] update relative paths for modules --- d2bs/kolbot/libs/modules/workers/Guard.js | 8 +-- .../libs/modules/workers/SimpleParty.js | 54 +++++++++++-------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/d2bs/kolbot/libs/modules/workers/Guard.js b/d2bs/kolbot/libs/modules/workers/Guard.js index ccec68235..9bee4af09 100644 --- a/d2bs/kolbot/libs/modules/workers/Guard.js +++ b/d2bs/kolbot/libs/modules/workers/Guard.js @@ -1,8 +1,8 @@ (function (module, require, thread, globalThis) { "use strict"; - const Messaging = require("./Messaging"); - const Worker = require("./Worker"); - const sdk = require("./sdk"); + const Messaging = require("../Messaging"); + const Worker = require("../Worker"); + const sdk = require("../sdk"); switch (thread) { case "thread": { @@ -42,7 +42,7 @@ // 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); } diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index fc7afcdbb..5c3d598df 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -4,14 +4,14 @@ const shitList = (Config.ShitList || Config.UnpartyShitlisted) ? ShitList.read() : []; - const Worker = require("Worker"); + const Worker = require("../Worker"); const NO_PARTY = 65535; const PARTY_MEMBER = 1; const ACCEPTABLE = 2; const INVITED = 4; const BUTTON_INVITE_ACCEPT_CANCEL = 2; const BUTTON_LEAVE_PARTY = 3; - const BUTTON_HOSTILE = 4; + // const BUTTON_HOSTILE = 4; print("ÿc2Kolbotÿc0 :: Simple party running"); @@ -19,25 +19,35 @@ SimpleParty.biggestPartyId = function () { let uniqueParties = []; - // Or add it and return the value - for (let party = getParty(); party.getNext();) { - ( - // Find this party - uniqueParties.find(u => u.partyid === party.partyid) - // Or create an instance of it - || ((uniqueParties.push({ - partyid: party.partyid, - used: 0 - }) && false) || uniqueParties[uniqueParties.length - 1]) - // Once we have the party object, increase field used - ).used++; - } + try { + // Or add it and return the value + for (let party = getParty(); party.getNext();) { + ( + // Find this party + uniqueParties.find(u => u.partyid === party.partyid) + // Or create an instance of it + || ((uniqueParties.push({ + partyid: party.partyid, + used: 0 + }) && false) || uniqueParties[uniqueParties.length - 1]) + // Once we have the party object, increase field used + ).used++; + } - // Filter out no party, if another party is found - if (uniqueParties.some(u => u.partyid !== NO_PARTY)) { - (uniqueParties = uniqueParties.filter(u => u.partyid !== NO_PARTY)); + // Filter out no party, if another party is found + if (uniqueParties.some(u => u.partyid !== NO_PARTY)) { + (uniqueParties = uniqueParties.filter(u => u.partyid !== NO_PARTY)); + } + return (uniqueParties + .sort(function (a, b) { + /* b-a = desc */ + return b.used - a.used; + }).first() || { partyid: -1 }).partyid; + } catch (e) { + console.error(e); + + return -1; } - return (uniqueParties.sort((a, b) => b.used - a.used /*b-a = desc*/).first() || { partyid: -1 }).partyid; }; SimpleParty.acceptFirst = function () { @@ -86,11 +96,13 @@ SimpleParty.timer = getTickCount(); return function () { // Set timer back on 3 seconds, or reset it and continue - if ((getTickCount() - SimpleParty.timer) < 3000 || (SimpleParty.timer = getTickCount()) && false) { + if ((getTickCount() - SimpleParty.timer) < 3000 + || (SimpleParty.timer = getTickCount()) && false) { return true; } - if (Config.PublicMode !== true) { // Public mode 1/2/3 dont count. This is SimplyParty + // Public mode 1/2/3 dont count. This is SimplyParty + if (Config.PublicMode !== true) { return true; } From 78baf9a04c62f1d8557ee7bba137ca611d856517 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:25:10 -0400 Subject: [PATCH 208/758] Update TownChicken.js - turn townchicken into a worker module - This is far more stable than letting townchicken run as a thread, the consistent pausing/unpausing of the main thread seems to cause d2bs to crash --- .../libs/modules/workers/TownChicken.js | 431 +++++++++++++----- 1 file changed, 327 insertions(+), 104 deletions(-) diff --git a/d2bs/kolbot/libs/modules/workers/TownChicken.js b/d2bs/kolbot/libs/modules/workers/TownChicken.js index 86cfe46ef..af44ce8f2 100644 --- a/d2bs/kolbot/libs/modules/workers/TownChicken.js +++ b/d2bs/kolbot/libs/modules/workers/TownChicken.js @@ -1,137 +1,360 @@ /** * @filename TownChicken.js -* @author kolton, theBGuy -* @desc handle town chicken +* @author theBGuy +* @desc TownChicken background worker thread * */ -js_strict(true); -include("critical.js"); -// add core library -includeCoreLibs(); +(function (module, require, Worker) { + // Only load this in global scope + if (getScript(true).name.toLowerCase() === "default.dbj") { + /** + * @param {number} [targetArea] + * @param {string} [owner] + * @param {ObjectUnit} [unit] + * @param {Unit} [dummy] + * @returns {boolean} + */ + const usePortal = function (targetArea, owner, unit, dummy) { + if (targetArea && me.inArea(targetArea)) return true; -// handle systems -includeSystemLibs(); + me.cancelUIFlags(); -function main () { - let townCheck = false; - const scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; + const townAreaCheck = (area = 0) => sdk.areas.Towns.includes(area); + const preArea = me.area; + const leavingTown = townAreaCheck(preArea); - // override broadCastIntent - shouldn't be called at all in this thread - Pather.broadcastIntent = () => {}; + for (let i = 0; i < 13; i += 1) { + if (me.dead) return false; + if (targetArea ? me.inArea(targetArea) : me.area !== preArea) return true; - const pause = function () { - for (let i = 0; i < scripts.length; i += 1) { - let script = getScript(scripts[i]); + (i > 0 && owner && me.inTown) && Town.move("portalspot"); - if (scripts[i] === "default.dbj" && !script) { - !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); - } + const portal = unit + ? copyUnit(unit) + : Pather.getPortal(targetArea, owner); - if (script && script.running) { - script.pause(); - scripts[i] === "default.dbj" && print("ÿc1Pausing."); - } - } - - return true; - }; - - const resume = function () { - for (let i = 0; i < scripts.length; i += 1) { - let script = getScript(scripts[i]); - - if (script && !script.running && scripts[i] !== "default.dbj") { - script.resume(); - } else if (scripts[i] === "default.dbj") { - // resume only if clonekilla isn't running - if (!getScript("threads/clonekilla.js")) { - if (script && !script.running) { - console.log("ÿc2Resuming."); - script.resume(); + if (portal && portal.area === me.area) { + const useTk = me.inTown && Skill.useTK(portal) && i < 3; + if (useTk) { + if (portal.distance > 21) { + me.inTown && me.act === 5 + ? Town.move("portalspot") + : Pather.moveNearUnit(portal, 20); + } + if (Packet.telekinesis(portal) + && Misc.poll(function () { + return targetArea ? me.inArea(targetArea) : me.area !== preArea; + })) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } } else { - if (!script) { - // default has crashed? We shouldn't be running then. Is toolsthread still up? - // if yes try to still quit normally, otherwise quit from here - !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); + if (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(function () { + return me.area !== preArea, 1000, 100; + })) { + Pather.lastPortalTick = getTickCount(); + delay(100); + + return true; + } + } + Packet.flash(me.gid); } + + delay(250); } - } - return true; - }; + return (targetArea ? me.inArea(targetArea) : me.area !== preArea); + }; - addEventListener("scriptmsg", - function (msg) { - if (typeof msg === "string" && msg === "townCheck") { - townCheck = true; - // maybe check for quit broadcast as well? If we are preparing to quit we shouldn't really be townchickening + /** + * @param {boolean} use + * @returns {boolean} + */ + 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(function (p) { + return 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(function (p) { + return 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); } - }); - - // Init config and attacks - console.log("ÿc3Start TownChicken thread"); - D2Bot.init(); - Config.init(); - Pickit.init(); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - let checkHP = Config.TownHP > 0; - let checkMP = Config.TownMP > 0; - - // START - // test for getUnit bug - Game.getMonster() === null && console.warn("getUnit is bugged"); - - while (true) { - if (!me.inTown && (townCheck - // should TownHP/MP check be in toolsthread? - // We would then be able to remove all game interaction checks until we get a townCheck msg - || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { - // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area - if (me.dead && Config.LifeChicken <= 0) { - console.log("ÿc1TownChicken :: ÿc0We are dead and LifeChicken is set to 0"); - while (me.dead) delay(100); - - continue; + + return false; + }; + + /** + * @param {Act} act + * @param {boolean} wpmenu + * @returns {boolean} + */ + 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(function () { + if (me.inTown) return true; + let p = Game.getObject("portal"); + console.debug(p); + !!p && Misc.click(0, 0, p) && delay(100); + Misc.poll(function () { + return 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 (!me.canTpToTown()) { - townCheck = false; - continue; + 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"); + } } - pause(); - while (!me.gameReady) { - if (me.dead) return; + return true; + }; + + const visitTown = function () { + console.log("ÿc8Start ÿc0:: ÿc8visitTown"); + + const preArea = me.area; + const preAct = sdk.areas.actOf(preArea); - delay(100); + 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 { - Messaging.sendToScript("threads/Toolsthread.js", "townChickenOn"); - console.log("ÿc8(TownChicken) :: ÿc0Going to town"); - me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); - Town.visitTown(); + goToTown(); } catch (e) { - Misc.errorReport(e, "TownChicken.js"); - scriptBroadcast("quit"); + return false; + } + + const { x, y } = me; - return; - } finally { - resume(); + Town.doChores(); - townCheck = false; - Messaging.sendToScript("threads/Toolsthread.js", "townChickenOff"); + 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"); + } } - } - delay(50); + console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + getAreaName(me.area)); + + return me.area === preArea; + }; + + let townCheck = false; + + Misc.townCheck = function () { + return false; + }; + + let waitTick = getTickCount(); + let potTick = getTickCount(); + let _recursion = false; + + // Start + Worker.runInBackground.TownChicken = function () { + if (getTickCount() - waitTick < 100) return true; + if (_recursion) 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.needBeltPots() || (Config.OpenChests.Enabled && Town.needKeys())) { + shouldChicken = true; + } + } + + if (shouldChicken) { + let t4 = getTickCount(); + try { + _recursion = true; + console.log("ÿc8(TownChicken) :: ÿc0Going to town"); + me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); + Attack.stopClear = true; + + visitTown(); + } catch (e) { + Misc.errorReport(e, "TownChicken.js"); + scriptBroadcast("quit"); + + return false; + } finally { + _recursion = false; + Packet.flash(me.gid, 100); + console.log("ÿc8(TownChicken) :: ÿc0Took: " + Time.format(getTickCount() - t4) + " to visit town."); + [Attack.stopClear, townCheck] = [false, false]; + } + } + + return true; + }; + + console.log("ÿc2Kolbotÿc0 :: TownChicken running"); } -} +})(module, require, typeof Worker === "object" && Worker || require("../Worker")); From 2e865f69e3c90c8de466bce20a26f9d5a6157c11 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:27:34 -0400 Subject: [PATCH 209/758] remove references to townCheck - This is built in to the new townchicken module --- d2bs/kolbot/libs/core/Attack.js | 4 ---- d2bs/kolbot/libs/core/Attacks/Amazon.js | 7 ++----- d2bs/kolbot/libs/core/Attacks/Assassin.js | 7 ++----- d2bs/kolbot/libs/core/Attacks/Druid.js | 7 ++----- d2bs/kolbot/libs/core/Attacks/Necromancer.js | 7 ++----- d2bs/kolbot/libs/core/Attacks/Paladin.js | 7 ++----- d2bs/kolbot/libs/core/Pather.js | 2 +- 7 files changed, 11 insertions(+), 30 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index f954071e9..ccb1017b2 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -259,8 +259,6 @@ const Attack = { Config.MFLeader && Pather.makePortal() && say("kill " + classId); 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); @@ -494,7 +492,6 @@ const Attack = { || (this.getScarinessLevel(target) > 7 && target.distance <= range)) && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Misc.townCheck(); tick = getTickCount(); if (!logged && boss && boss.gid === target.gid) { @@ -754,7 +751,6 @@ const Attack = { if (target.x !== undefined && target.attackable) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Misc.townCheck(); // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); let i; let result = ClassAttack.doAttack(target, attackCount % 15 === 0); diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 6e1e2485c..262b43256 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -119,12 +119,9 @@ const ClassAttack = { 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 || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); } - if (!unit) return Attack.Result.SUCCESS; if (Town.needMerc()) { diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 0de6152ae..7ca953f42 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -125,12 +125,9 @@ const ClassAttack = { 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 || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); } - if (!unit) return Attack.Result.SUCCESS; if (Town.needMerc()) { diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index 339dd364d..5497e3893 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -92,12 +92,9 @@ const ClassAttack = { 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 || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); } - if (!unit) return Attack.Result.SUCCESS; if (Town.needMerc()) { diff --git a/d2bs/kolbot/libs/core/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js index dffa037da..9e0625e5c 100644 --- a/d2bs/kolbot/libs/core/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -247,12 +247,9 @@ const ClassAttack = { 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 || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); } - if (!unit) return Attack.Result.SUCCESS; if (Town.needMerc()) { diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 46b13bf9f..79d6dd09d 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -99,12 +99,9 @@ const ClassAttack = { 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 || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); } - if (!unit) return Attack.Result.SUCCESS; if (Town.needMerc()) { diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 23e80faca..c136f831b 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -413,7 +413,7 @@ const Pather = { } } - settings.allowTown && Misc.townCheck(); + // settings.allowTown && Misc.townCheck(); } } else { if (!me.inTown) { From afb8b3d7a82e2673bb17ae792e32538e776162ae Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:28:24 -0400 Subject: [PATCH 210/758] Update Me.js - separate out needPotions into needBeltPots and needBufferPots --- d2bs/kolbot/libs/core/Me.js | 95 +++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 71832d373..cb8329f4e 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -242,6 +242,101 @@ me.needPotions = function () { return false; }; +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?) + 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(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) { + 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.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(); +// }; + /** @returns {ItemUnit | null} */ me.getTpTool = function () { const items = me.getItemsEx(-1, sdk.items.mode.inStorage) From 3b0c27850cab0400dd1d0cc8fb13f2b1c1ec574b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:29:13 -0400 Subject: [PATCH 211/758] Update Sorceress.js - missed this while removing references to townCheck --- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 7ccc55dbf..32348496c 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -146,12 +146,9 @@ const ClassAttack = { 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 || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); } - if (!unit) return Attack.Result.SUCCESS; if (Town.needMerc()) { From 7f659f9f5f96bd673452c8464065ab228ef2b716 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:29:53 -0400 Subject: [PATCH 212/758] Update Tools.js - cleanup the tools thread common functions --- d2bs/kolbot/libs/core/Common/Tools.js | 94 ++++++++++++++------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index 1d8a582ae..a95e4b97c 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -8,25 +8,25 @@ (function (Common) { typeof Common !== "object" && (Common = {}); Object.defineProperty(Common, "Toolsthread", { - value: { - pots: { + value: new function Toolsthread () { + this.pots = { Health: 0, Mana: 1, Rejuv: 2, MercHealth: 3, MercRejuv: 4 - }, - pingTimer: [], - pauseScripts: [], - stopScripts: [], - timerLastDrink: [], - cloneWalked: false, + }; + this.pingTimer = []; + this.pauseScripts = []; + this.stopScripts = []; + this.timerLastDrink = []; + this.cloneWalked = false; /** * @param {boolean} print * @returns {boolean} */ - checkPing: function (print = true) { + this.checkPing = function (print = true) { // Quit after at least 5 seconds in game if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; @@ -60,12 +60,12 @@ return false; }, - initQuitList: function () { + this.initQuitList = function () { let temp = []; - for (let i = 0; i < Config.QuitList.length; i += 1) { - if (FileTools.exists("data/" + Config.QuitList[i] + ".json")) { - let string = FileAction.read("data/" + Config.QuitList[i] + ".json"); + for (let profile of Config.QuitList) { + if (FileTools.exists("data/" + profile + ".json")) { + let string = FileAction.read("data/" + profile + ".json"); if (string) { let obj = JSON.parse(string); @@ -78,23 +78,22 @@ } Config.QuitList = temp.slice(0); - }, + }; - togglePause: function (townChicken = false) { - for (let i = 0; i < this.pauseScripts.length; i++) { - let script = getScript(this.pauseScripts[i]); + this.togglePause = function () { + for (let curr of this.pauseScripts) { + let script = getScript(curr); if (script) { if (script.running) { - this.pauseScripts[i] === "default.dbj" && console.log("ÿc1Pausing."); + curr === "default.dbj" && console.log("ÿc1Pausing."); // don't pause townchicken during clone walk - if (this.pauseScripts[i] !== "threads/townchicken.js" || !this.cloneWalked) { + if (curr !== "threads/townchicken.js" || !this.cloneWalked) { script.pause(); } } else { - if (this.pauseScripts[i] === "default.dbj") { - if (townChicken) continue; // don't resume default if we are in the middle of townChicken + if (curr === "default.dbj") { console.log("ÿc2Resuming."); } script.resume(); @@ -105,10 +104,10 @@ return true; }, - stopDefault: function () { - for (let i = 0; i < this.stopScripts.length; i++) { + this.stopDefault = function () { + for (let curr of this.stopScripts) { try { - let script = getScript(this.stopScripts[i]); + let script = getScript(curr); !!script && script.running && script.stop(); } catch (e) { console.error(e); @@ -116,9 +115,9 @@ } return true; - }, + }; - exit: function (chickenExit = false) { + this.exit = function (chickenExit = false) { try { chickenExit && D2Bot.updateChickens(); Config.LogExperience && Experience.log(); @@ -132,17 +131,20 @@ } return true; - }, + }; /** * @param {number} pottype * @param {number} type * @returns {ItemUnit | false} */ - getPotion: function (pottype, type) { + this.getPotion = function (pottype, type) { if (!pottype) return false; - let items = me.getItemsEx().filter((item) => item.itemType === pottype); + let items = me.getItemsEx() + .filter(function (item) { + return item.itemType === pottype; + }); if (items.length === 0) return false; // Get highest id = highest potion first @@ -150,27 +152,27 @@ return b.classid - a.classid; }); - for (let i = 0; i < items.length; i += 1) { - if (type < this.pots.MercHealth && items[i].isInInventory && items[i].itemType === pottype) { + for (let item of items) { + if (type < this.pots.MercHealth && item.isInInventory && item.itemType === pottype) { console.log("ÿc2Drinking potion from inventory."); - return items[i]; + return item; } - if (items[i].isInBelt && items[i].itemType === pottype) { + if (item.isInBelt && item.itemType === pottype) { console.log("ÿc2" + (type > 2 ? "Giving Merc" : "Drinking") + " potion from belt."); - return items[i]; + return item; } } return false; - }, + }; /** * @param {number} type * @returns {boolean} * @todo add stamina/thawing/antidote pot drinking here */ - drinkPotion: function (type) { + this.drinkPotion = function (type) { if (type === undefined) return false; let tNow = getTickCount(); @@ -230,7 +232,9 @@ } try { - type < this.pots.MercHealth ? potion.interact() : Packet.useBeltItemForMerc(potion); + type < this.pots.MercHealth + ? potion.interact() + : Packet.useBeltItemForMerc(potion); } catch (e) { console.error(e); } @@ -241,9 +245,9 @@ } return false; - }, + }; - checkVipers: function () { + this.checkVipers = function () { let monster = Game.getMonster(sdk.monsters.TombViper2); if (monster) { @@ -261,9 +265,9 @@ } return false; - }, + }; - getIronGolem: function () { + this.getIronGolem = function () { let golem = Game.getMonster(sdk.summons.IronGolem); if (golem) { @@ -277,9 +281,9 @@ } return false; - }, + }; - getNearestPreset: function () { + this.getNearestPreset = function () { let id; let unit = getPresetUnits(me.area); let dist = 99; @@ -298,7 +302,7 @@ * @param {MeType | MercUnit} unit * @returns {string} */ - getStatsString: function (unit) { + this.getStatsString = function (unit) { let realFCR = unit.getStat(sdk.stats.FCR); let realIAS = unit.getStat(sdk.stats.IAS); let realFBR = unit.getStat(sdk.stats.FBR); @@ -352,7 +356,7 @@ + (unit.getStat(sdk.stats.CannotbeFrozen) > 0 ? "ÿc3Cannot be Frozenÿc1\n" : "\n"); return str; - }, + }; }, configurable: true, }); From 0128d53a48c47672b2dfe0890f6ba9bfbffb0441 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:31:26 -0400 Subject: [PATCH 213/758] small cleanups - mostly just formatting --- d2bs/kolbot/libs/core/Common/Baal.js | 50 ++++++++++++++++----------- d2bs/kolbot/libs/core/Item.js | 15 ++++---- d2bs/kolbot/libs/core/NTItemParser.js | 29 +++++++++++----- 3 files changed, 56 insertions(+), 38 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 9501827cc..1af0a5c4b 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -8,8 +8,8 @@ (function (Common) { typeof Common !== "object" && (Common = {}); Object.defineProperty(Common, "Baal", { - value: { - throneCoords: { + value: new function Baal () { + this.throneCoords = { bottomLeft: { x: 15072, y: 5073 }, bottomRight: { x: 15118, y: 5073 }, bottomCenter: { x: 15093, y: 5073 }, @@ -17,14 +17,21 @@ topLeft: { x: 15072, y: 5002 }, topRight: { x: 15118, y: 5002 }, baal: { x: 15090, y: 5014 }, - }, + }; - checkHydra: function () { + this.checkHydra = function () { let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); if (hydra) { do { - if (hydra.mode !== sdk.monsters.mode.Dead && hydra.getStat(sdk.stats.Alignment) !== 2) { - Pather.moveTo(15072, 5002); + if (hydra.mode !== sdk.monsters.mode.Dead + && hydra.getStat(sdk.stats.Alignment) !== 2) { + let _pos = [ + this.throneCoords.bottomLeft, this.throneCoords.bottomRight, + this.throneCoords.topRight, this.throneCoords.topLeft, + ].sort(function (a, b) { + return getDistance(me, a) - getDistance(me, b); + }).first(); + Pather.moveTo(_pos.x, _pos.y); while (hydra.mode !== sdk.monsters.mode.Dead) { delay(500); if (!copyUnit(hydra).x) { @@ -38,9 +45,9 @@ } return true; - }, + }; - checkThrone: function (clear = true) { + this.checkThrone = function (clear = true) { let monster = Game.getMonster(); if (monster) { @@ -74,9 +81,9 @@ } return false; - }, + }; - clearThrone: function () { + this.clearThrone = function () { if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; let monList = []; @@ -106,17 +113,18 @@ { x: 15098, y: 5040 }, { x: 15099, y: 5022 }, { x: 15086, y: 5024 }, { x: 15079, y: 5014 } ]; - return pos.forEach((node) => { + return pos.forEach(function (node) { // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { + if ([node.x, node.y].distance < 35 + && [node.x, node.y].mobCount({ range: 30 }) === 0) { return; } Pather.moveTo(node.x, node.y); Attack.clear(30); }); - }, + }; - preattack: function () { + this.preattack = function () { switch (me.classid) { case sdk.player.class.Sorceress: if ([ @@ -163,9 +171,9 @@ } return false; - }, + }; - clearWaves: function () { + this.clearWaves = function () { Pather.moveTo(15094, me.paladin ? 5029 : 5038); let tick = getTickCount(); @@ -203,7 +211,7 @@ 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); } @@ -269,16 +277,16 @@ this.clearThrone(); return true; - }, + }; - killBaal: function () { + this.killBaal = function () { if (me.inArea(sdk.areas.ThroneofDestruction)) { Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done Pather.moveTo(15090, 5008); delay(5000); Precast.doPrecast(true); - Misc.poll(() => { + Misc.poll(function () { if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({ range: 15 })) { Common.Baal.clearThrone(); Pather.moveTo(15090, 5008); @@ -304,7 +312,7 @@ } return false; - } + }; }, configurable: true, }); diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index fd0e514b2..0e4b92403 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -506,23 +506,22 @@ const Item = { 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 skip of Config.SkipLogging) { + if (skip === unit.classid || skip === unit.code) return false; } let lastArea; - let name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); + const name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); + const color = (unit.getColor() || -1); + const code = this.getItemCode(unit); + const sock = unit.getItem(); let desc = this.getItemDesc(unit); - let color = (unit.getColor() || -1); if (action.match("kept", "i")) { lastArea = DataFile.getStats().lastArea; lastArea && (desc += ("\n\\xffc0Area: " + lastArea)); } - let code = this.getItemCode(unit); - let sock = unit.getItem(); - if (sock) { do { if (sock.itemType === sdk.items.type.Jewel) { @@ -535,7 +534,7 @@ const Item = { keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); desc += "$" + (unit.ethereal ? ":eth" : ""); - let itemObj = { + const itemObj = { title: action + " " + name, description: desc, image: code, diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index ecbc836c1..ee3272f17 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -41,10 +41,10 @@ NTIP.OpenFile = function (filepath, notify) { return false; } - let nipfile, lines; + let nipfile; let tick = getTickCount(); - let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); let entries = 0; + const filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); try { nipfile = File.open(filepath, 0); @@ -58,11 +58,11 @@ NTIP.OpenFile = function (filepath, notify) { return false; } - lines = nipfile.readAllLines(); + let lines = nipfile.readAllLines(); nipfile.close(); - for (let i = 0; i < lines.length; i += 1) { - let info = { + for (let i = 0; i < lines.length; i++) { + const info = { line: i + 1, file: filename, string: lines[i] @@ -71,7 +71,7 @@ NTIP.OpenFile = function (filepath, notify) { let line = NTIP.ParseLineInt(lines[i], info); if (line) { - entries += 1; + entries++; NTIP_CheckList.push(line); @@ -84,7 +84,11 @@ NTIP.OpenFile = function (filepath, notify) { } if (notify) { - print("ÿ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; @@ -196,13 +200,20 @@ NTIP.GetTier = NTIP.generateTierFunc("Tier"); */ NTIP.GetMercTier = NTIP.generateTierFunc("Merctier"); +/** + * @param {ItemUnit} item + * @param {[(item) => Boolean, (item) => Boolean, (item) => Boolean]} entryList + * @param {boolean} verbose + */ NTIP.CheckItem = function (item, entryList, verbose) { let i, num; let rval = {}; let result = 0; - let list = entryList ? entryList : NTIP_CheckList; - let identified = item.getFlag(sdk.items.flags.Identified); + const list = entryList + ? entryList + : NTIP_CheckList; + const identified = item.getFlag(sdk.items.flags.Identified); for (i = 0; i < list.length; i++) { try { From 4f2a1903f20fee0b89a62e3ec61eec6549f2bbe2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:33:15 -0400 Subject: [PATCH 214/758] Update default.dbj - updated to use townchicken module - import gameaction typedef --- d2bs/kolbot/default.dbj | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 12d03dc02..e9617f833 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -5,6 +5,7 @@ * * @typedef {import("./sdk/globals")} * @typedef {import("./libs/systems/mulelogger/MuleLogger")} +* @typedef {import("./libs/systems/gameaction/GameAction")} */ js_strict(true); include("critical.js"); // required @@ -62,7 +63,7 @@ function main () { if (MuleLogger.inGameCheck()) return true; // don't load default for dropper/mules - if (getScript("D2BotDropper.dbj") || getScript("D2BotMule.dbj")) { + if (getScript("D2BotDropper.dbj") || getScript("d2botmule.dbj")) { load("threads/AreaWatcher.js"); while (me.ingame) { @@ -151,15 +152,17 @@ function main () { // Load threads load("threads/ToolsThread.js"); if (Config.TownCheck || Config.TownHP > 0 || Config.TownMP > 0) { - load("threads/TownChicken.js"); + require("libs/modules/workers/TownChicken"); } - if (Config.DebugMode.Stack && FileTools.exists("libs/modules/Guard.js")) { - require("libs/modules/Guard"); + if (Config.DebugMode.Stack) { + require("libs/modules/workers/Guard"); } if (Config.PublicMode) { - Config.PublicMode === true ? require("libs/modules/SimpleParty") : load("threads/Party.js"); + Config.PublicMode === true + ? require("libs/modules/workers/SimpleParty") + : load("threads/Party.js"); } Config.AntiHostile && load("threads/AntiHostile.js"); @@ -256,8 +259,8 @@ function main () { break; case "object": - for (let i = 0; i < Config.LastMessage.length; i += 1) { - say(Config.LastMessage[i].replace("$nextgame", DataFile.getStats().nextGame, "i")); + for (let msg of Config.LastMessage) { + say(msg.replace("$nextgame", DataFile.getStats().nextGame, "i")); } break; From 43bc0ac3f3911a1e033e18a9929d34545c71852e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 10:46:30 -0400 Subject: [PATCH 215/758] remove old townchicken thread --- d2bs/kolbot/threads/ToolsThread.js | 37 ++++---- d2bs/kolbot/threads/TownChicken.js | 137 ----------------------------- 2 files changed, 17 insertions(+), 157 deletions(-) delete mode 100644 d2bs/kolbot/threads/TownChicken.js diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 42589916c..87307b25e 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -54,18 +54,23 @@ function main () { // General functions Common.Toolsthread.pauseScripts = [ - "default.dbj", "threads/townchicken.js", - "threads/autobuildthread.js", "threads/antihostile.js", - "threads/party.js", "threads/rushthread.js" + "default.dbj", + "threads/autobuildthread.js", + "threads/antihostile.js", + "threads/party.js", + "threads/rushthread.js" ]; Common.Toolsthread.stopScripts = [ - "default.dbj", "threads/townchicken.js", - "threads/autobuildthread.js", "threads/antihostile.js", - "threads/party.js", "threads/rushthread.js", "libs//modules/guard.js" + "default.dbj", + "threads/autobuildthread.js", + "threads/antihostile.js", + "threads/party.js", + "threads/rushthread.js", + "libs//modules//workers//guard.js" ]; // Event functions - this.keyEvent = function (key) { + const keyEvent = function (key) { switch (key) { case sdk.keys.PauseBreak: // pause running threads Common.Toolsthread.togglePause(townChicken); @@ -188,7 +193,7 @@ function main () { } }; - this.gameEvent = function (mode, param1, param2, name1, 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." @@ -276,17 +281,9 @@ function main () { } }; - this.scriptEvent = function (msg) { + const scriptEvent = function (msg) { if (!!msg && typeof msg === "string") { switch (msg) { - case "townChickenOn": - townChicken = true; - - break; - case "townChickenOff": - townChicken = false; - - break; case "toggleQuitlist": canQuit = !canQuit; @@ -349,9 +346,9 @@ function main () { Config = copyObj(Config); let tick = getTickCount(); - addEventListener("keyup", this.keyEvent); - addEventListener("gameevent", this.gameEvent); - addEventListener("scriptmsg", this.scriptEvent); + addEventListener("keyup", keyEvent); + addEventListener("gameevent", gameEvent); + addEventListener("scriptmsg", scriptEvent); Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks diff --git a/d2bs/kolbot/threads/TownChicken.js b/d2bs/kolbot/threads/TownChicken.js deleted file mode 100644 index fd48da8fa..000000000 --- a/d2bs/kolbot/threads/TownChicken.js +++ /dev/null @@ -1,137 +0,0 @@ -/** -* @filename TownChicken.js -* @author kolton, theBGuy -* @desc handle town chicken -* -*/ -js_strict(true); -include("critical.js"); - -// add core library -includeCoreLibs(); - -// handle systems -includeSystemLibs(); - -function main() { - let townCheck = false; - const scripts = ["default.dbj", "threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; - - // override broadCastIntent - shouldn't be called at all in this thread - Pather.broadcastIntent = () => {}; - - const pause = function () { - for (let i = 0; i < scripts.length; i += 1) { - let script = getScript(scripts[i]); - - if (scripts[i] === "default.dbj" && !script) { - !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); - } - - if (script && script.running) { - script.pause(); - scripts[i] === "default.dbj" && print("ÿc1Pausing."); - } - } - - return true; - }; - - const resume = function () { - for (let i = 0; i < scripts.length; i += 1) { - let script = getScript(scripts[i]); - - if (script && !script.running && scripts[i] !== "default.dbj") { - script.resume(); - } else if (scripts[i] === "default.dbj") { - // resume only if clonekilla isn't running - if (!getScript("threads/clonekilla.js")) { - if (script && !script.running) { - console.log("ÿc2Resuming."); - script.resume(); - } else { - if (!script) { - // default has crashed? We shouldn't be running then. Is toolsthread still up? - // if yes try to still quit normally, otherwise quit from here - !!getScript("threads/toolsthread.js") ? scriptBroadcast("quit") : quit(); - } - } - } - } - } - - return true; - }; - - addEventListener("scriptmsg", - function (msg) { - if (typeof msg === "string" && msg === "townCheck") { - townCheck = true; - // maybe check for quit broadcast as well? If we are preparing to quit we shouldn't really be townchickening - } - }); - - // Init config and attacks - console.log("ÿc3Start TownChicken thread"); - D2Bot.init(); - Config.init(); - Pickit.init(); - Attack.init(); - Storage.Init(); - CraftingSystem.buildLists(); - Runewords.init(); - Cubing.init(); - - let checkHP = Config.TownHP > 0; - let checkMP = Config.TownMP > 0; - - // START - // test for getUnit bug - Game.getMonster() === null && console.warn("getUnit is bugged"); - - while (true) { - if (!me.inTown && (townCheck - // should TownHP/MP check be in toolsthread? - // We would then be able to remove all game interaction checks until we get a townCheck msg - || ((checkHP && me.hpPercent < Config.TownHP) || (checkMP && me.mpPercent < Config.TownMP)))) { - // canTpToTown should maybe be overrided here to quit if we can't tp to town but isn't just because we are in non-tp-able area - if (me.dead && Config.LifeChicken <= 0) { - console.log("ÿc1TownChicken :: ÿc0We are dead and LifeChicken is set to 0"); - while (me.dead) delay(100); - - continue; - } - if (!me.canTpToTown()) { - townCheck = false; - - continue; - } - pause(); - - while (!me.gameReady) { - if (me.dead) return; - - delay(100); - } - - try { - Messaging.sendToScript("threads/Toolsthread.js", "townChickenOn"); - console.log("ÿc8(TownChicken) :: ÿc0Going to town"); - me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); - Town.visitTown(); - } catch (e) { - Misc.errorReport(e, "TownChicken.js"); - scriptBroadcast("quit"); - - return; - } finally { - resume(); - - townCheck = false; - Messaging.sendToScript("threads/Toolsthread.js", "townChickenOff"); - } - } - - delay(50); - } -} From bcbfc575961ced983206cf4a77d04ff4a5cac9c2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Aug 2023 11:51:26 -0400 Subject: [PATCH 216/758] Update Questing.js - remove old reference to townchicken thread --- d2bs/kolbot/libs/scripts/Questing.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js index 74a64c481..9790cc2eb 100644 --- a/d2bs/kolbot/libs/scripts/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -412,11 +412,6 @@ function Questing () { D2Bot.stop(); } else { log("ÿc9(Questing) :: ÿc2Complete"); - // reload town chicken in case we are doing others scripts after this one finishes - let townChick = getScript("threads/TownChicken.js"); - if ((Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick)) { - load("threads/TownChicken.js"); - } } return true; From 061a19758fae4392e5acb0fdf1353e0b05cf573d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 00:56:22 -0400 Subject: [PATCH 217/758] basic charge skill usage - add handler into doAttack to use a charged skill - add example in `_BaseConfigFIle` --- d2bs/kolbot/libs/config/_BaseConfigFile.js | 5 +++++ d2bs/kolbot/libs/core/Attacks/Amazon.js | 10 ++++++++++ d2bs/kolbot/libs/core/Attacks/Assassin.js | 10 ++++++++++ d2bs/kolbot/libs/core/Attacks/Barbarian.js | 10 ++++++++++ d2bs/kolbot/libs/core/Attacks/Druid.js | 10 ++++++++++ d2bs/kolbot/libs/core/Attacks/Paladin.js | 10 ++++++++++ d2bs/kolbot/libs/core/Attacks/Sorceress.js | 15 +++++++++++++++ d2bs/kolbot/libs/core/Config.js | 4 ++++ 8 files changed, 74 insertions(+) diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 0fe43554e..a6164f291 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -507,6 +507,11 @@ // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + Config.ChargeCast = { + skill: sdk.skills.LowerResist, + spectype: 0x7, + }; Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 262b43256..354764b7e 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -72,6 +72,16 @@ const ClassAttack = { } } + if (Config.ChargeCast.skill > -1) { + let cRange = Skill.getRange(Config.ChargeCast.skill); + let cState = Skill.getState(Config.ChargeCast.skill); + if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) + && (!cState || !unit.getState(cState)) + && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { + Skill.castCharges(Config.ChargeCast.skill, unit); + } + } + if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 7ca953f42..0bb7b2426 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -29,6 +29,16 @@ const ClassAttack = { } } + if (Config.ChargeCast.skill > -1) { + let cRange = Skill.getRange(Config.ChargeCast.skill); + let cState = Skill.getState(Config.ChargeCast.skill); + if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) + && (!cState || !unit.getState(cState)) + && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { + Skill.castCharges(Config.ChargeCast.skill, unit); + } + } + if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index f79823ec7..50ea81328 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -32,6 +32,16 @@ const ClassAttack = { } } + if (Config.ChargeCast.skill > -1) { + let cRange = Skill.getRange(Config.ChargeCast.skill); + let cState = Skill.getState(Config.ChargeCast.skill); + if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) + && (!cState || !unit.getState(cState)) + && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { + Skill.castCharges(Config.ChargeCast.skill, unit); + } + } + if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Attack.getSkillElement(Config.AttackSkill[0])) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index 5497e3893..33ca71df2 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -30,6 +30,16 @@ const ClassAttack = { Skill.cast(sdk.skills.CycloneArmor, sdk.skills.hand.Right); } + if (Config.ChargeCast.skill > -1) { + let cRange = Skill.getRange(Config.ChargeCast.skill); + let cState = Skill.getState(Config.ChargeCast.skill); + if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) + && (!cState || !unit.getState(cState)) + && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { + Skill.castCharges(Config.ChargeCast.skill, unit); + } + } + if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 79d6dd09d..4dcdb87a6 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -29,6 +29,16 @@ const ClassAttack = { } } + if (Config.ChargeCast.skill > -1) { + let cRange = Skill.getRange(Config.ChargeCast.skill); + let cState = Skill.getState(Config.ChargeCast.skill); + if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) + && (!cState || !unit.getState(cState)) + && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { + Skill.castCharges(Config.ChargeCast.skill, unit); + } + } + if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 32348496c..108810f44 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -52,6 +52,11 @@ const ClassAttack = { return skills; }, + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ doAttack: function (unit, preattack = false) { if (!unit) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); @@ -78,6 +83,16 @@ const ClassAttack = { Skill.cast(sdk.skills.ThunderStorm, sdk.skills.hand.Right); } + if (Config.ChargeCast.skill > -1) { + let cRange = Skill.getRange(Config.ChargeCast.skill); + let cState = Skill.getState(Config.ChargeCast.skill); + if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) + && (!cState || !unit.getState(cState)) + && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { + Skill.castCharges(Config.ChargeCast.skill, unit); + } + } + if (preattack && Config.AttackSkill[0] > 0 && Attack.checkResist(unit, Config.AttackSkill[0]) && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0]))) { diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index e1f623b16..ff95100e3 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -344,6 +344,10 @@ let Config = { ClearPath: false, BossPriority: false, MaxAttackCount: 300, + ChargeCast: { + skill: -1, + spectype: 0x7, + }, // Amazon specific LightningFuryDelay: 0, From b76692d5eb7f814107d0b88d53eec716d93defe3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 18:50:03 -0400 Subject: [PATCH 218/758] Update Precast.js - fix cyclone armors lack of duration and no checks for percent of armor left. (Need to figure out what stat that correlates to) --- d2bs/kolbot/libs/core/Precast.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index e323837bd..1122fdebe 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -541,18 +541,9 @@ const Precast = (function () { Skill.canUse(sdk.skills.Raven) && Precast.summon(sdk.skills.Raven, sdk.summons.type.Raven); - buffSummons = (function () { - switch (Config.SummonAnimal) { - case sdk.skills.SummonSpiritWolf: - return (Precast.summon(sdk.skills.SummonSpiritWolf, sdk.summons.type.SpiritWolf) || buffSummons); - case sdk.skills.SummonDireWolf: - return (Precast.summon(sdk.skills.SummonDireWolf, sdk.summons.type.DireWolf) || buffSummons); - case sdk.skills.SummonGrizzly: - return (Precast.summon(sdk.skills.SummonGrizzly, sdk.summons.type.Grizzly) || buffSummons); - default: - return buffSummons; - } - })(); + if (!!Config.SummonAnimal && Config.SummonAnimal !== "None") { + buffSummons = Precast.summon(Config.SummonAnimal, Skill.getSummonType(Config.SummonAnimal)); + } if (!!Config.SummonVine && Config.SummonVine !== "None") { buffSummons = Precast.summon(Config.SummonVine, sdk.summons.type.Vine); @@ -633,3 +624,8 @@ const Precast = (function () { }, }; })(); +// handle cyclone armor for lack of duration +Precast.skills.get(sdk.skills.CycloneArmor).needToCast = function (force = false) { + if (!this.canUse()) return false; + return force || !me.getState(this.state); +}; From 253fea17189bc44cc22540d44f333c914c13c599 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 22:32:46 -0400 Subject: [PATCH 219/758] Update ToolsThread.js - fix shutting down guard with new location --- d2bs/kolbot/threads/ToolsThread.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 87307b25e..fd9925e5a 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -66,7 +66,7 @@ function main () { "threads/antihostile.js", "threads/party.js", "threads/rushthread.js", - "libs//modules//workers//guard.js" + "libs\\\\modules\\workers\\guard.js" // why? ]; // Event functions @@ -375,7 +375,6 @@ function main () { + " in " + getAreaName(me.area) + ". Ping: " + me.ping, sdk.colors.D2Bot.Red ); - Common.Toolsthread.exit(true); break; } @@ -390,7 +389,6 @@ function main () { if (Config.ManaChicken > 0 && me.mpPercent <= Config.ManaChicken) { D2Bot.printToConsole("Mana Chicken: (" + me.mp + "/" + me.mpmax + ") in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); break; } @@ -404,7 +402,6 @@ function main () { // ironGolem.hpmax is bugged with BO if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); break; } @@ -419,13 +416,16 @@ function main () { if (mercHP > 0 && merc.mode !== sdk.monsters.mode.Dead) { if (mercHP < Config.MercChicken) { D2Bot.printToConsole("Merc Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); - Common.Toolsthread.exit(true); break; } - mercHP < Config.UseMercHP && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercHealth); - mercHP < Config.UseMercRejuv && Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercRejuv); + if (mercHP < Config.UseMercHP) { + Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercHealth); + } + if (mercHP < Config.UseMercRejuv) { + Common.Toolsthread.drinkPotion(Common.Toolsthread.pots.MercRejuv); + } } } } @@ -472,14 +472,14 @@ function main () { } } else { Common.Toolsthread.checkPing(false); // In case of quitlist triggering first - Common.Toolsthread.exit(); - return true; + break; } } delay(20); } + Common.Toolsthread.exit(); return true; } From fa1893dace24e52503f7b0e386be4b5cab6761fa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 22:33:00 -0400 Subject: [PATCH 220/758] Update Guard.js - notify that guard has started --- d2bs/kolbot/libs/modules/workers/Guard.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/modules/workers/Guard.js b/d2bs/kolbot/libs/modules/workers/Guard.js index 9bee4af09..35413a6b2 100644 --- a/d2bs/kolbot/libs/modules/workers/Guard.js +++ b/d2bs/kolbot/libs/modules/workers/Guard.js @@ -75,6 +75,7 @@ break; } case "started": { + console.log("ÿc2Kolbotÿc0 :: Guard running"); let sendStack = getTickCount(); Worker.push(function highPrio () { Worker.push(highPrio); From bab46a4a5f5c7e205686f4202709179dc72f4c08 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 23:23:02 -0400 Subject: [PATCH 221/758] Update Mausoleum.js - change sorcs to not move on top of bloodraven --- d2bs/kolbot/libs/scripts/Mausoleum.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Mausoleum.js b/d2bs/kolbot/libs/scripts/Mausoleum.js index 88ff1782e..d41330d68 100644 --- a/d2bs/kolbot/libs/scripts/Mausoleum.js +++ b/d2bs/kolbot/libs/scripts/Mausoleum.js @@ -5,13 +5,13 @@ * */ -function Mausoleum() { +function Mausoleum () { Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); if (Config.Mausoleum.KillBishibosh) { - Pather.moveToPreset(sdk.areas.ColdPlains, sdk.unittype.Monster, sdk.monsters.preset.Bishibosh); + Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh); Attack.kill(getLocaleString(sdk.locale.monsters.Bishibosh)); Pickit.pickItems(); } @@ -19,7 +19,9 @@ function Mausoleum() { if (!Pather.moveToExit(sdk.areas.BurialGrounds, true)) throw new Error("Failed to move to Burial Grounds"); if (Config.Mausoleum.KillBloodRaven) { - Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven); + Pather.moveToPresetMonster(sdk.areas.BurialGrounds, sdk.monsters.preset.BloodRaven, { + minDist: me.sorceress && Pather.canTeleport() ? 30 : 5 + }); Attack.kill(getLocaleString(sdk.locale.monsters.BloodRaven)); Pickit.pickItems(); } From c8feec12dc09bc33a94b521e749bea1327eef54d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 23:38:31 -0400 Subject: [PATCH 222/758] Update Packet.js - cleanup. Swapped most of the `sendPacket` calls with `new PacketBuilder` it's easier to read - marked `Packet.openMenu` as depreciated, to be removed in the future. There was only a single line difference between it and `Unit.openMenu` to changed the prototype to handle using packet vs not --- d2bs/kolbot/libs/core/Packet.js | 132 ++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 30 deletions(-) diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 0178ee8cc..53d387d17 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -8,6 +8,8 @@ const Packet = { /** * Interact and open the menu of an NPC + * @deprecated there was only one line difference between this and Unit.openMenu + * added the line to Unit.openMenu to save defining this function * @param {NPCUnit} unit * @returns {boolean} */ @@ -65,11 +67,18 @@ const Packet = { const gamble = mode === "Gamble"; console.info(true, mode + " at " + unit.name); - if (this.openMenu(unit)) { + if (unit.openMenu()) { for (let i = 0; i < 10; i += 1) { delay(200); - i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, gamble ? 2 : 1, 4, unit.gid, 4, 0); + if (i % 2 === 0) { + new PacketBuilder() + .byte(sdk.packets.send.EntityAction) + .dword(gamble ? 2 : 1) + .dword(unit.gid) + .dword(0) + .send(); + } if (unit.itemcount > 0) { delay(200); @@ -101,7 +110,6 @@ const Packet = { if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; for (let i = 0; i < 3; i += 1) { - // sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : gamble ? 0x2 : 0x0, 4, 0); new PacketBuilder() .byte(sdk.packets.send.NPCBuy) .dword(npc.gid) @@ -109,7 +117,6 @@ const Packet = { .dword(shiftBuy ? 0x80000000 : gamble ? 0x2 : 0x0) .dword(0) .send(); - let tick = getTickCount(); while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { @@ -151,8 +158,13 @@ const Packet = { if (oldGold < unit.getItemCost(sdk.items.cost.ToBuy)) return false; for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.NPCBuy, 4, npc.gid, 4, unit.gid, 4, shiftBuy ? 0x80000000 : 0x0, 4, 0); - + new PacketBuilder() + .byte(sdk.packets.send.NPCBuy) + .dword(npc.gid) + .dword(unit.gid) + .dword(shiftBuy ? 0x80000000 : 0x0) + .dword(0) + .send(); let tick = getTickCount(); while (getTickCount() - tick < Math.max(2000, me.ping * 2 + 500)) { @@ -192,8 +204,13 @@ const Packet = { } for (let i = 0; i < 5; i += 1) { - sendPacket(1, sdk.packets.send.NPCSell, 4, npc.gid, 4, unit.gid, 4, 0, 4, 0); - + new PacketBuilder() + .byte(sdk.packets.send.NPCSell) + .dword(npc.gid) + .dword(unit.gid) + .dword(0) + .dword(0) + .send(); let tick = getTickCount(); while (getTickCount() - tick < 2000) { @@ -212,11 +229,17 @@ const Packet = { */ identifyItem: function (unit, tome) { if (!unit || unit.identified) return false; + const identify = function () { + new PacketBuilder() + .byte(sdk.packets.send.IndentifyItem) + .dword(unit.gid) + .dword(tome.gid) + .send(); + }; CursorLoop: for (let i = 0; i < 3; i += 1) { - sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); - + identify(); let tick = getTickCount(); while (getTickCount() - tick < 2000) { @@ -234,7 +257,7 @@ const Packet = { for (let i = 0; i < 3; i += 1) { if (getCursorType() === sdk.cursortype.Identify) { - sendPacket(1, sdk.packets.send.IndentifyItem, 4, unit.gid, 4, tome.gid); + identify(); } let tick = getTickCount(); @@ -294,9 +317,11 @@ const Packet = { if (!this.itemToCursor(item)) return false; for (let i = 0; i < 15; i += 1) { - sendPacket(1, sdk.packets.send.DropItem, 4, item.gid); - - let tick = getTickCount(); + new PacketBuilder() + .byte(sdk.packets.send.DropItem) + .dword(item.gid) + .send(); + const tick = getTickCount(); while (getTickCount() - tick < Math.max(500, me.ping * 2 + 200)) { if (!me.itemoncursor) return true; @@ -321,8 +346,10 @@ const Packet = { } if (item.isInBelt) return this.useBeltItemForMerc(item); if (item.isInInventory && this.itemToCursor(item)) { - sendPacket(1, sdk.packets.send.MercItem, 2, 0); - + new PacketBuilder() + .byte(sdk.packets.send.MercItem) + .word(0) + .send(); return true; } return false; @@ -334,8 +361,16 @@ const Packet = { * @returns {boolean} */ placeInBelt: function (item, xLoc) { - item.toCursor(true) && new PacketBuilder().byte(sdk.packets.send.ItemToBelt).dword(item.gid).dword(xLoc).send(); - return Misc.poll(() => item.isInBelt, 500, 100); + if (item.toCursor(true)) { + new PacketBuilder() + .byte(sdk.packets.send.ItemToBelt) + .dword(item.gid) + .dword(xLoc) + .send(); + } + return Misc.poll(function () { + return item.isInBelt; + }, 500, 100); }, /** @@ -360,7 +395,11 @@ const Packet = { */ entityInteract: function (who) { if (!who || !copyUnit(who).x) return false; - new PacketBuilder().byte(sdk.packets.send.InteractWithEntity).dword(who.type).dword(who.gid).send(); + new PacketBuilder() + .byte(sdk.packets.send.InteractWithEntity) + .dword(who.type) + .dword(who.gid) + .send(); return true; }, @@ -370,7 +409,11 @@ const Packet = { */ cancelNPC: function (who) { if (!who || !copyUnit(who).x) return false; - new PacketBuilder().byte(sdk.packets.send.NPCCancel).dword(who.type).dword(who.gid).send(); + new PacketBuilder() + .byte(sdk.packets.send.NPCCancel) + .dword(who.type) + .dword(who.gid) + .send(); return true; }, @@ -380,7 +423,12 @@ const Packet = { */ useBeltItemForMerc: function (pot) { if (!pot) return false; - sendPacket(1, sdk.packets.send.UseBeltItem, 4, pot.gid, 4, 1, 4, 0); + new PacketBuilder() + .byte(sdk.packets.send.UseBeltItem) + .dword(pot.gid) + .dword(1) + .dword(0) + .send(); return true; }, @@ -388,22 +436,34 @@ const Packet = { hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocation : sdk.packets.send.LeftSkillOnLocation; - sendPacket(1, hand, 2, wX, 2, wY); + new PacketBuilder() + .byte(hand) + .word(wX) + .word(wY) + .send(); }, castAndHoldSkill: function (hand, wX, wY, duration = 1000) { - let nHand = (hand === sdk.skills.hand.Right) + /** @param {number} byte */ + const cast = function (byte) { + new PacketBuilder() + .byte(byte) + .word(wX) + .word(wY) + .send(); + }; + const nHand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocation : sdk.packets.send.LeftSkillOnLocation; hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnLocationEx : sdk.packets.send.LeftSkillOnLocationEx; - let endT = getTickCount() + duration; + const endTime = getTickCount() + duration; // has to be cast normally first with a click before held packet is sent - sendPacket(1, nHand, 2, wX, 2, wY); - while (getTickCount() < endT) { - sendPacket(1, hand, 2, wX, 2, wY); + cast(nHand); + while (getTickCount() < endTime) { + cast(hand); delay(25); } }, @@ -417,7 +477,11 @@ const Packet = { hand = (hand === sdk.skills.hand.Right) ? sdk.packets.send.RightSkillOnEntityEx3 : sdk.packets.send.LeftSkillOnEntityEx3; - sendPacket(1, hand, 4, who.type, 4, who.gid); + new PacketBuilder() + .byte(hand) + .dword(who.type) + .dword(who.gid) + .send(); }, /** @@ -442,7 +506,11 @@ const Packet = { */ enchant: function (who) { if (!who || !Skill.setSkill(sdk.skills.Enchant, sdk.skills.hand.Right)) return false; - sendPacket(1, sdk.packets.send.RightSkillOnEntityEx3, 4, who.type, 4, who.gid); + new PacketBuilder() + .byte(sdk.packets.send.RightSkillOnEntityEx3) + .dword(who.type) + .dword(who.gid) + .send(); return true; }, @@ -454,7 +522,11 @@ const Packet = { teleport: function (wX, wY) { if (![wX, wY].every(n => typeof n === "number")) return false; if (!Skill.setSkill(sdk.skills.Teleport, sdk.skills.hand.Right)) return false; - new PacketBuilder().byte(sdk.packets.send.RightSkillOnLocation).word(wX).word(wY).send(); + new PacketBuilder() + .byte(sdk.packets.send.RightSkillOnLocation) + .word(wX) + .word(wY) + .send(); return true; }, From d4b126e6bf181bcc2dd1bcafd7ac5b6d384489ae Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 23:39:42 -0400 Subject: [PATCH 223/758] Update Prototypes.js - added packet vs non handler for `Unit.openMenu` - cleaned up jsdoc comment for haveAll/some/ and checkItem --- d2bs/kolbot/libs/core/Prototypes.js | 92 ++++++++++++++--------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 14a471d11..1255cd99f 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -692,19 +692,20 @@ Object.defineProperties(Unit.prototype, { * @returns {boolean} */ Unit.prototype.openMenu = function (addDelay) { - if (Config.PacketShopping) return Packet.openMenu(this); if (this.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); if (getUIFlag(sdk.uiflags.NPCMenu)) return true; addDelay === undefined && (addDelay = 0); - let pingDelay = (me.gameReady ? me.ping : 125); + const pingDelay = (me.gameReady ? me.ping : 125); for (let i = 0; i < 5; i += 1) { if (getDistance(me, this) > 4) { Pather.moveNearUnit(this, 4); } - Misc.click(0, 0, this); + Config.PacketShopping + ? Packet.entityInteract(this) + : Misc.click(0, 0, this); let tick = getTickCount(); while (getTickCount() - tick < 5000) { @@ -714,14 +715,19 @@ Unit.prototype.openMenu = function (addDelay) { return true; } - if (getInteractedNPC() && getTickCount() - tick > 1000) { + if ((getTickCount() - tick > 1000 && getInteractedNPC()) + || (getTickCount() - tick > 500 && getIsTalkingNPC())) { me.cancel(); } delay(100); } - sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, this.gid); + new PacketBuilder() + .byte(sdk.packets.send.NPCInit) + .dword(1) + .dword(this.gid) + .send(); delay(pingDelay * 2 + 1); Packet.cancelNPC(this); delay(pingDelay * 2 + 1); @@ -742,7 +748,7 @@ Unit.prototype.startTrade = function (mode) { console.log("Starting " + mode + " at " + this.name); if (getUIFlag(sdk.uiflags.Shop)) return true; - let menuId = mode === "Gamble" + const menuId = mode === "Gamble" ? sdk.menu.Gamble : mode === "Repair" ? sdk.menu.TradeRepair @@ -852,8 +858,6 @@ Unit.prototype.sell = function () { while (getTickCount() - tick < 2000) { if (me.itemcount !== itemCount) { - //delay(500); - return true; } @@ -981,15 +985,19 @@ Unit.prototype.use = function () { case sdk.storage.Inventory: if (this.isInStash && !Town.openStash()) return false; // doesn't work, not sure why but it's missing something - //new PacketBuilder().byte(sdk.packets.send.UseItem).dword(gid).dword(this.x).dword(this.y).send(); + // new PacketBuilder().byte(sdk.packets.send.UseItem).dword(gid).dword(this.x).dword(this.y).send(); checkQuantity = iType === sdk.items.type.Book; checkQuantity && (quantity = this.getStat(sdk.stats.Quantity)); this.interact(); // use interact instead, was hoping to skip this since its really just doing the same thing over but oh well break; case sdk.storage.Belt: - new PacketBuilder().byte(sdk.packets.send.UseBeltItem).dword(gid).dword(0).dword(0).send(); - + new PacketBuilder() + .byte(sdk.packets.send.UseBeltItem) + .dword(gid) + .dword(0) + .dword(0) + .send(); break; default: return false; @@ -1002,22 +1010,27 @@ Unit.prototype.use = function () { } }; +/** + * @typedef {Object} ItemInfo + * @property {number} [classid] + * @property {number} [itemtype] + * @property {number} [quality] + * @property {boolean} [runeword] + * @property {boolean} [ethereal] + * @property {boolean | number} [equipped] + * @property {boolean} [basetype] + * @property {string | number} [name] + */ + /** * @description Returns item given by itemInfo - * @param itemInfo object - - * { - * classid: Number, - * itemtype: Number, - * quality: Number, - * runeword: Boolean, - * ethereal: Boolean, - * name: getLocaleString(id) || localeStringId, - * equipped: Boolean || Number (bodylocation) - * } - * @returns Unit[] + * @param {ItemInfo} itemInfo + * @returns {ItemUnit[]} */ Unit.prototype.checkItem = function (itemInfo) { - if (this === undefined || this.type > 1 || typeof itemInfo !== "object") return { have: false, item: null }; + if (this === undefined || this.type > 1 || typeof itemInfo !== "object") { + return { have: false, item: null }; + } const itemObj = Object.assign({}, { classid: -1, @@ -1065,17 +1078,8 @@ Unit.prototype.checkItem = function (itemInfo) { /** * @description Returns first item given by itemInfo - * @param itemInfo array of objects - - * { - * classid: Number, - * itemtype: Number, - * quality: Number, - * runeword: Boolean, - * ethereal: Boolean, - * name: getLocaleString(id) || localeStringId, - * equipped: Boolean || Number (bodylocation) - * } - * @returns Unit[] + * @param {ItemInfo} itemInfo + * @returns {ItemUnit[]} */ Unit.prototype.findFirst = function (itemInfo = []) { if (this === undefined || this.type > 1) return { have: false, item: null }; @@ -1127,18 +1131,9 @@ Unit.prototype.findFirst = function (itemInfo = []) { }; /** - * @description Returns boolean if we have all the items given by itemInfo - * @param itemInfo array of objects - - * { - * classid: Number, - * itemtype: Number, - * quality: Number, - * runeword: Boolean, - * ethereal: Boolean, - * name: getLocaleString(id) || localeStringId, - * equipped: Boolean || Number (bodylocation) - * } - * @returns Boolean + * @description Check if we have all the items given by itemInfo + * @param {ItemInfo} itemInfo + * @returns {boolean} */ Unit.prototype.haveAll = function (itemInfo = [], returnIfSome = false) { if (this === undefined || this.type > 1) return false; @@ -1195,6 +1190,11 @@ Unit.prototype.haveAll = function (itemInfo = [], returnIfSome = false) { return haveAll; }; +/** + * @description Check if we have some of the items given by itemInfo + * @param {ItemInfo[]} itemInfo + * @returns {boolean} + */ Unit.prototype.haveSome = function (itemInfo = []) { return this.haveAll(itemInfo, true); }; From cd266dcf1c9043c04d0b973841f06066934ab617 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Aug 2023 23:44:02 -0400 Subject: [PATCH 224/758] Update Skill.js - handle thrown errors, still unsure why sometimes the item reference is lost between when calling `castChargedSkill` but it's non-critical so don't end the script due to it --- d2bs/kolbot/libs/core/Skill.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index c1d188b21..551a1b594 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -652,15 +652,17 @@ */ castCharges: function (skillId, unit) { if (!Skill.charges.length) return false; - let charge = Skill.charges - .filter(c => c.skill === skillId && c.charges > 0) + const charge = Skill.charges + .filter(function (c) { + return c.skill === skillId && c.charges > 0; + }) .sort(function (a, b) { return b.level - a.level; - }).find(function (c) { - return me.getItem(-1, sdk.items.mode.Equipped, c.gid); + }).find(function (charge) { + return me.getItem(-1, sdk.items.mode.Equipped, charge.gid); }); if (!charge) return false; - let item = me.getItem(-1, sdk.items.mode.Equipped, charge.gid); + const item = me.getItem(-1, sdk.items.mode.Equipped, charge.gid); if (!item) return false; if (!unit) unit = me; const weaponSwitch = me.weaponswitch; @@ -669,6 +671,11 @@ } try { return item.castChargedSkill(skillId, unit.x, unit.y); + } catch (e) { + console.error(e); + // maybe rebuild list? + // Skill.getCharges(); + return false; } finally { if (weaponSwitch !== me.weaponswitch) { me.switchWeapons(weaponSwitch); From 9940079f55e160fb7d717abffb87207b3bbfd805 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Aug 2023 00:28:53 -0400 Subject: [PATCH 225/758] Update UserAddon.js - add option to toggle flag that shows unit info. Command is `.info` in chat - fix typo in onChatInput --- d2bs/kolbot/libs/scripts/UserAddon.js | 29 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/UserAddon.js b/d2bs/kolbot/libs/scripts/UserAddon.js index 1793f69a1..55b4da4bb 100644 --- a/d2bs/kolbot/libs/scripts/UserAddon.js +++ b/d2bs/kolbot/libs/scripts/UserAddon.js @@ -10,6 +10,7 @@ function UserAddon () { let i, title, dummy, command = ""; + let showInfo = true; const UnitInfo = new (require("../modules/UnitInfo")); const className = sdk.player.class.nameOf(me.classid); const flags = [ @@ -31,10 +32,14 @@ function UserAddon () { } }; - const onChatInput = (speaker, msg) => { + /** + * @param {string} speaker + * @param {string} msg + * @returns {boolean} + */ + const onChatInput = function (speaker, msg) { if (msg.length && msg[0] === ".") { - command = str.split(" ")[0].split(".")[1]; - + command = msg.split(" ")[0].split(".")[1]; return true; } @@ -74,21 +79,25 @@ function UserAddon () { UnitInfo.check(); - if (command && command.toLowerCase() === "done") { - console.log("ÿc4UserAddon ÿc1ended"); - - return true; - } else { - console.log(command); + if (command) { + console.debug(command); + if (command.toLowerCase() === "done") { + return true; + } else if (command.toLowerCase() === "info") { + showInfo = !showInfo; + } command = ""; } Pickit.fastPick(); - UnitInfo.createInfo(Game.getSelectedUnit()); + if (showInfo) { + UnitInfo.createInfo(Game.getSelectedUnit()); + } delay(20); } } finally { + console.log("ÿc4UserAddon ÿc1ended"); removeEventListener("keyup", keyEvent); removeEventListener("itemaction", Pickit.itemEvent); removeEventListener("chatinputblocker", onChatInput); From baf056cd5ba9b232a86c6831ee9a32c3bb960504 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Aug 2023 14:52:00 -0400 Subject: [PATCH 226/758] Update Pickit.js - check to see if it was us that picked the item, instead of relying on it simply no longer being of mode `onGroundOrDropping` when we go to log it --- d2bs/kolbot/libs/core/Pickit.js | 51 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 44b7dae7b..cee301530 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -62,6 +62,7 @@ const Pickit = { // eslint-disable-next-line no-unused-vars itemEvent: function (gid, mode, code, global) { + // console.log("gid: " + gid, " mode: " + mode, " code: " + code, " global: " + global); if (gid > 0 && mode === 0) { Pickit.gidList.add(gid); } @@ -289,8 +290,12 @@ const Pickit = { if (Config.GemHunter.GemList.some((p) => [unit.classid - 1, unit.classid].includes(p))) { // base and upgraded gem will be kept let _items = me.getItemsEx(unit.classid, sdk.items.mode.inStorage) - .filter(i => i.gid !== unit.gid - && !CraftingSystem.checkItem(i) && !Cubing.checkItem(i) && !Runewords.checkItem(i)); + .filter(function (i) { + return i.gid !== unit.gid + && !CraftingSystem.checkItem(i) + && !Cubing.checkItem(i) + && !Runewords.checkItem(i); + }); if (_items.length === 0) return resultObj(Pickit.Result.WANTED, "GemHunter"); } } @@ -335,6 +340,7 @@ const Pickit = { * @param {ItemUnit} unit */ function ItemStats (unit) { + this.gid = unit.gid; this.ilvl = unit.ilvl; this.type = unit.itemType; this.classid = unit.classid; @@ -353,9 +359,8 @@ const Pickit = { sdk.uiflags.Waypoint, sdk.uiflags.Shop, sdk.uiflags.Stash, sdk.uiflags.Cube ]; - const gid = unit.gid; - let item = Game.getItem(-1, -1, gid); + let item = Game.getItem(-1, -1, unit.gid); if (!item) return false; if (cancelFlags.some(function (flag) { return getUIFlag(flag); })) { @@ -372,11 +377,11 @@ const Pickit = { for (let i = 0; i < retry; i += 1) { if (me.dead) return false; // recursion appeared - if (this.track.lastItem === gid) return true; + if (this.track.lastItem === stats.gid) return true; // can't find the item - if (!Game.getItem(-1, -1, gid)) return false; + if (!Game.getItem(-1, -1, stats.gid)) return false; - if (me.getItem(item.classid, -1, item.gid)) { + if (me.getItem(stats.classid, -1, stats.gid)) { console.debug("Already picked item"); return true; } @@ -402,19 +407,21 @@ const Pickit = { continue; } // we had to move, lets check to see if it's still there - if (me.getItem(-1, -1, gid)) { + if (me.getItem(stats.classid, -1, stats.gid)) { // we picked the item during another process - recursion happened // this has pontential to skip logging an item return true; } - if (!Game.getItem(-1, -1, gid)) { + if (!Game.getItem(stats.classid, -1, stats.gid)) { // it's gone so don't continue, return false; } } // use packet first, if we fail and not using fast pick use click - (Config.FastPick || i < 1) ? Packet.click(item) : Misc.click(0, 0, item); + (Config.FastPick || i < 1) + ? Packet.click(item) + : Misc.click(0, 0, item); } let tick = getTickCount(); @@ -459,36 +466,38 @@ const Pickit = { stats.useTk = false; } - stats.picked = me.itemcount > itemCount || !!me.getItem(-1, -1, gid); + stats.picked = me.itemcount > itemCount || !!me.getItem(stats.classid, -1, stats.gid); if (stats.picked) { DataFile.updateStats("lastArea"); - let _common = "ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")"; + const _common = "ÿc7Picked up " + stats.color + stats.name + " ÿc0(ilvl " + stats.ilvl + ")"; + const pickedItem = me.getItem(stats.classid, -1, stats.gid); + if (!pickedItem) return false; switch (status) { case Pickit.Result.WANTED: console.log(_common + (keptLine ? " (" + keptLine + ")" : "")); - if (this.ignoreLog.indexOf(stats.type) === -1) { - Item.logger("Kept", item); - Item.logItem("Kept", item, keptLine); + if (Pickit.ignoreLog.indexOf(pickedItem.itemType) === -1) { + Item.logger("Kept", pickedItem); + Item.logItem("Kept", pickedItem, keptLine); } break; case Pickit.Result.CUBING: console.log(_common + " (Cubing)"); - Item.logger("Kept", item, "Cubing " + me.findItems(item.classid).length); + Item.logger("Kept", pickedItem, "Cubing " + me.findItems(pickedItem.classid).length); Cubing.update(); break; case Pickit.Result.RUNEWORD: console.log(_common + " (Runewords)"); - Item.logger("Kept", item, "Runewords"); - Runewords.update(stats.classid, gid); + Item.logger("Kept", pickedItem, "Runewords"); + Runewords.update(pickedItem.classid, pickedItem.gid); break; case Pickit.Result.CRAFTING: console.log(_common + " (Crafting System)"); - CraftingSystem.update(item); + CraftingSystem.update(pickedItem); break; default: @@ -497,7 +506,7 @@ const Pickit = { break; } - this.track.lastItem = item.gid; + this.track.lastItem = pickedItem.gid; } return true; @@ -512,7 +521,7 @@ const Pickit = { let items = Storage.Inventory.Compare(Config.Inventory) || []; if (items.length) { - return items.some(item => { + return items.some(function (item) { switch (Pickit.checkItem(item).result) { case Pickit.Result.UNID: // For low level chars that can't actually get id scrolls -> prevent an infinite loop From 7b5cfe973e0343e5150039d800af66f1e1f0a1a8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Aug 2023 14:53:30 -0400 Subject: [PATCH 227/758] include gameReady check - occasionally these workers were running while wp screen was still loading causing game not ready warnings --- d2bs/kolbot/libs/modules/workers/Guard.js | 11 +++++------ d2bs/kolbot/libs/modules/workers/SimpleParty.js | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/modules/workers/Guard.js b/d2bs/kolbot/libs/modules/workers/Guard.js index 35413a6b2..758cc91f6 100644 --- a/d2bs/kolbot/libs/modules/workers/Guard.js +++ b/d2bs/kolbot/libs/modules/workers/Guard.js @@ -7,7 +7,7 @@ switch (thread) { case "thread": { Worker.runInBackground.stackTrace = (new function () { - let self = this; + const self = this; let stack; let myStack = ""; @@ -19,15 +19,15 @@ ); /** - * @constructor - * @param {function():string} callback - */ + * @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, + element.visible = me.gameReady && [sdk.uiflags.Inventory, sdk.uiflags.SkillWindow, sdk.uiflags.TradePrompt, sdk.uiflags.Stash, @@ -60,7 +60,6 @@ this.hooks.filter(hook => hook.hasOwnProperty("update") && typeof hook.update === "function" && hook.update()); return true; }; - }).update; let quiting = false; diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index 5c3d598df..a3d846cb7 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -96,7 +96,7 @@ SimpleParty.timer = getTickCount(); return function () { // Set timer back on 3 seconds, or reset it and continue - if ((getTickCount() - SimpleParty.timer) < 3000 + if (((getTickCount() - SimpleParty.timer) < 3000 || !me.gameReady) || (SimpleParty.timer = getTickCount()) && false) { return true; } From 72efec5446e9e729e41a91aa958cea19d92a2418 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Aug 2023 18:13:18 -0400 Subject: [PATCH 228/758] Create PubJoinConfig.js --- .../libs/systems/pubjoin/PubJoinConfig.js | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js diff --git a/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js b/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js new file mode 100644 index 000000000..7c6dba24e --- /dev/null +++ b/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js @@ -0,0 +1,49 @@ +/** +* @filename PubJoinConfig.js +* @author theBGuy +* @desc Configuration file for D2BotPubJoin system +* +*/ + +(function (module) { + /** + * @description includeFilter format + * @example Multiple entries in the same array mean AND + * // game has to contain "baal" and "-" + * const includeFilter = ["baal", "-"]; + * + * @example Multiple entries in different arrays mean OR + * // will join games with either "baal" or "diablo" in their name + * const includeFilter = [ + * ["baal"], + * ["diablo"] + * ]; + * @type {Array>} + */ + const includeFilter = [ + [""] + ]; + + /** + * @description excludeFilter format + * @example Multiple entries in the same array mean AND + * // ignores games that contain "baal" and "-" + * const includeFilter = ["baal", "-"]; + * + * @example Multiple entries in different arrays mean OR + * // will ignore games with either "baal" or "diablo" in their name + * const includeFilter = [ + * ["baal"], + * ["diablo"] + * ]; + * @type {Array>} + */ + const excludeFilter = [ + [""] + ]; + + module.exports = { + includeFilter: includeFilter, + excludeFilter: excludeFilter, + }; +})(module); From aba0bad8731cf81f9578e4e88d5db98d37d07eac Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Aug 2023 18:13:52 -0400 Subject: [PATCH 229/758] Create FollowConfig.js --- .../libs/systems/follow/FollowConfig.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 d2bs/kolbot/libs/systems/follow/FollowConfig.js diff --git a/d2bs/kolbot/libs/systems/follow/FollowConfig.js b/d2bs/kolbot/libs/systems/follow/FollowConfig.js new file mode 100644 index 000000000..f0960dfce --- /dev/null +++ b/d2bs/kolbot/libs/systems/follow/FollowConfig.js @@ -0,0 +1,28 @@ +/** +* @filename FollowConfig.js +* @author theBGuy +* @desc Configuration file for D2BotFollow system +* +*/ + +(function (module) { + /** + * @description Join game settings + * - Format: "leader's profile": ["leecher 1 profile", "leecher 2 profile", ...] + * - If you want everyone to join the same leader, use "leader's profile": ["all"] + * - NOTE: Use *PROFILE* names (profile matches window title), NOT character/account names + * - leader:leecher groups need to be divided by a comma + * @example + * const JoinSettings = { + * "lead1": ["follow1", "follow2"], + * "lead2": ["follow3", "follow4"] + * }; + */ + const JoinSettings = { + "Leader": ["Leecher"], + }; + + module.exports = { + JoinSettings: JoinSettings, + }; +})(module); From 2c6222a595b0ce3c14df33a7440fb381b417a3b8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Aug 2023 18:15:14 -0400 Subject: [PATCH 230/758] Update D2BotPubJoin.dbj - refactor pubjoin. Cleaned up location handling, moved config logic out of the entry with the exception of starter config settings. Unsure what to do with that for now --- d2bs/kolbot/D2BotPubJoin.dbj | 490 +++++++++++++++-------------------- 1 file changed, 213 insertions(+), 277 deletions(-) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index e48bc4c2d..308f4c59f 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /** * @filename D2BotPubJoin.dbj * @author kolton, theBGuy @@ -14,99 +13,25 @@ Starter.Config.ResetCount = 0; // Reset game count back to 1 every X games. Starter.Config.JoinDelay = 10; // Seconds to wait between join attempts Starter.Config.AttemptNextGame = true; // after joining a game, attempt incrementing game count and joining next game rather than looking for it in game list Starter.Config.AttemptNextGameRetrys = 5; +const { + includeFilter, + excludeFilter +} = require("./libs/systems/pubjoin/PubJoinConfig"); // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -/** - * @todo this section should be in it's own config leaving this file only containing core logic - */ -/** - IncludeFilter config: - - Multiple entries in the same array mean AND: - ["baal", "-"] = game has to contain "baal" and "-" - - Different entries mean OR: - ["baal"], - ["diablo"] - will join games with either "baal" or "diablo" in their name - - Similar rules apply to ExcludeFilter: - - ["somebaal", "-"] ignores games with "somebaal" and "-" in the game name - - ["somebaal"], - ["somediablo"] - this will ignore all games with "somebaal" or "somediablo" in their names -*/ - -const IncludeFilter = [ - [""] -]; - -const ExcludeFilter = [ - [""] -]; - // ############################################################################### -function includeCheck (game) { - // No filters - if (!IncludeFilter.length) return true; - - for (let i = 0; i < IncludeFilter.length; i += 1) { - let j; - for (j = 0; j < IncludeFilter[i].length; j += 1) { - // Break the inner loop if an element didn't match or if an element is invalid - if (!IncludeFilter[i][j] || !game.match(IncludeFilter[i][j], "gi")) { - break; - } - } - - // All elements matched - if (j === IncludeFilter[i].length) { - return true; - } - } - - return false; -} - -function excludeCheck (game) { - // No filters - if (!ExcludeFilter.length) return true; - - for (let i = 0; i < ExcludeFilter.length; i += 1) { - let j; - for (j = 0; j < ExcludeFilter[i].length; j += 1) { - // Break the inner loop if an element didn't match or if an element is invalid - if (!ExcludeFilter[i][j] || !game.match(ExcludeFilter[i][j], "gi")) { - break; - } - } - - // All elements matched - if (j === ExcludeFilter[i].length) { - return false; - } - } - - return true; -} - // the only things we really need from these are their oog checks includeSystemLibs(); -let Controls = require("./modules/Control"); -let Overrides = require("./modules/Override"); +const Overrides = require("./modules/Override"); 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; new Overrides.Override(Starter, Starter.setNextGame, function (orignal, gameName) { function incrementString (text) { @@ -126,247 +51,258 @@ if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { let lastGameTick, retry = 0; let retryTick = 0; -function locationAction (location) { +const locationAction = (function () { let gameToJoin, doneGames, gameList; - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations + } = require("./libs/oog/Locations"); + + /** @param {string} game */ + const includeCheck = function (game) { + // No filters + if (!includeFilter.length) return true; + + for (let filterSet of includeFilter) { + let conditionsMatched = true; + + for (let condition of filterSet) { + // Break the inner loop if an element didn't match or if an element is invalid + if (!condition || !game.match(condition, "gi")) { + conditionsMatched = false; + break; + } + } - me.blockKeys = false; - Starter.loginRetry = 0; - !Starter.firstLogin && (Starter.firstLogin = true); - Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); - Starter.loginFail = 0; + // All elements matched + if (conditionsMatched) { + return true; + } + } - if (Starter.Config.PingQuitDelay && Starter.pingQuit) { - ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); + return false; + }; - Starter.pingQuit = false; - } + /** @param {string} game */ + const excludeCheck = function (game) { + // No filters + if (!excludeFilter.length) return true; - if (Starter.inGame || Starter.gameInfo.error) { - !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); + for (let filterSet of excludeFilter) { + let conditionsMatched = true; - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + for (let condition of filterSet) { + // Break the inner loop if an element didn't match or if an element is invalid + if (!condition || !game.match(condition, "gi")) { + conditionsMatched = false; + break; + } } - } - if (Starter.inGame) { - if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; + // All elements matched + if (conditionsMatched) { + return false; } + } - print("updating runs"); - D2Bot.updateRuns(); + return true; + }; - lastGameTick = getTickCount(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; + locations.set(sdk.game.locations.Lobby, + function () { + D2Bot.updateStatus("Lobby"); - if (Starter.Config.ResetCount && Starter.gameCount >= Starter.Config.ResetCount) { - Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); - } - } + me.blockKeys = false; + Starter.loginRetry = 0; + !Starter.firstLogin && (Starter.firstLogin = true); + Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); + Starter.loginFail = 0; - Starter.LocationEvents.openJoinGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - Controls.CancelCreateGame.click(); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.LobbyChat: - case sdk.game.locations.CreateGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Starter.LocationEvents.openJoinGameWindow(); - - break; - case sdk.game.locations.JoinGame: - // Don't join immediately after previous game to avoid FTJ - if (getTickCount() - lastGameTick < 5000) { - ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); - } + if (Starter.Config.PingQuitDelay && Starter.pingQuit) { + ControlAction.timeoutDelay("Ping Delay", Starter.Config.PingQuitDelay * 1e3); - if (Starter.Config.AttemptNextGame && retry < Starter.Config.AttemptNextGameRetrys) { - let ng = DataFile.getStats().nextGame; + Starter.pingQuit = false; + } - if (ng && (retry === 0 || (getTickCount() - retryTick > Starter.Config.JoinDelay * 1e3))) { - gameToJoin = ng; - console.debug(gameToJoin); + if (Starter.inGame || Starter.gameInfo.error) { + !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); - me.blockMouse = true; + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay( + "Min game time wait", + Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount() + ); + } + } - try { - joinGame(gameToJoin, ""); - } catch (joinErr) { - print(joinErr); + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + return; } - retry++; - retryTick = getTickCount(); - me.blockMouse = false; + print("updating runs"); + D2Bot.updateRuns(); - Starter.locationTimeout(5000, location); + lastGameTick = getTickCount(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; - if (getLocation() === sdk.game.locations.GameDoesNotExist) { - Starter.LocationEvents.openJoinGameWindow(); + if (Starter.Config.ResetCount && Starter.gameCount >= Starter.Config.ResetCount) { + Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); } } + + Starter.LocationEvents.openJoinGameWindow(); + } + ); + addLocations([sdk.game.locations.WaitingInLine, sdk.game.locations.CreateGame], + function () { + Controls.CancelCreateGame.click(); + Controls.JoinGameWindow.click(); + } + ); + addLocations( + [ + sdk.game.locations.LobbyChat, sdk.game.locations.CreateGame, + sdk.game.locations.Ladder, sdk.game.locations.ChannelList + ], + function () { + Starter.LocationEvents.openJoinGameWindow(); } + ); + locations.set(sdk.game.locations.JoinGame, + function (location) { + // Don't join immediately after previous game to avoid FTJ + if (getTickCount() - lastGameTick < 5000) { + ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); + } - for (let i = 0; i < 5; i += 1) { - gameList = ControlAction.getGameList(); + if (Starter.Config.AttemptNextGame && retry < Starter.Config.AttemptNextGameRetrys) { + let ng = DataFile.getStats().nextGame; - if (gameList && gameList.length > 0) { - break; - } + if (ng && (retry === 0 || (getTickCount() - retryTick > Starter.Config.JoinDelay * 1e3))) { + gameToJoin = ng; + console.debug(gameToJoin); - delay(1000); - } + me.blockMouse = true; + + try { + joinGame(gameToJoin, ""); + } catch (joinErr) { + print(joinErr); + } - console.debug(gameList); + retry++; + retryTick = getTickCount(); + me.blockMouse = false; - if (gameList) { - doneGames = []; - gameToJoin = false; - FileTools.exists("logs/doneGames.json") && (doneGames = JSON.parse(FileAction.read("logs/doneGames.json"))); + Starter.locationTimeout(5000, location); - gameList.sort(function (a, b) { - return b.players - a.players; - }); + if (getLocation() === sdk.game.locations.GameDoesNotExist) { + Starter.LocationEvents.openJoinGameWindow(); + } + } + } - for (let i = 0; i < gameList.length; i += 1) { - if (doneGames.indexOf(gameList[i].gameName) === -1 && includeCheck(gameList[i].gameName) && excludeCheck(gameList[i].gameName)) { - console.log("ÿc7Game: " + gameList[i].gameName + ", Players: " + gameList[i].players); - gameToJoin = gameList[i].gameName; + for (let i = 0; i < 5; i += 1) { + gameList = ControlAction.getGameList(); + if (gameList && gameList.length > 0) { break; } + + delay(1000); } - if (gameToJoin) { - doneGames.length >= 20 && doneGames.shift(); - doneGames.push(gameToJoin); - FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); + console.debug(gameList); + + if (gameList) { + doneGames = []; + gameToJoin = false; + if (FileTools.exists("logs/doneGames.json")) { + doneGames = JSON.parse(FileAction.read("logs/doneGames.json")); + } - me.blockMouse = true; + gameList.sort(function (a, b) { + return b.players - a.players; + }); - try { - joinGame(gameToJoin, ""); - } catch (joinErr) { - print(joinErr); + for (let i = 0; i < gameList.length; i += 1) { + if (doneGames.indexOf(gameList[i].gameName) === -1 + && includeCheck(gameList[i].gameName) + && excludeCheck(gameList[i].gameName)) { + console.log("ÿc7Game: " + gameList[i].gameName + ", Players: " + gameList[i].players); + gameToJoin = gameList[i].gameName; + + break; + } } - me.blockMouse = false; + if (gameToJoin) { + doneGames.length >= 20 && doneGames.shift(); + doneGames.push(gameToJoin); + FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); + + me.blockMouse = true; - Starter.locationTimeout(5000, location); + try { + joinGame(gameToJoin, ""); + } catch (joinErr) { + print(joinErr); + } + + me.blockMouse = false; + + Starter.locationTimeout(5000, location); + } } } - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.SplashScreen: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - Starter.LocationEvents.login(); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameIsFull: - Controls.CreateGameWindow.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - - break; - case sdk.game.locations.CharacterCreate: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); - - break; - case sdk.game.locations.TcpIp: - Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); - - break; - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); + ); + locations.set(sdk.game.locations.SelectDifficultySP, + function () { + hideConsole(); + sendKey(sdk.keys.Escape); } + ); + addLocations([sdk.game.locations.GameNameExists, sdk.game.locations.GameIsFull], + function () { + Controls.CreateGameWindow.click(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + } + ); + locations.set(sdk.game.locations.TcpIp, + function () { + Controls.TcpIpCancel.click(); + } + ); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + console.debug("Running location: " + loc); + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; +})(); - break; - } -} - -function main() { +function main () { debugLog(me.profile); addEventListener("copydata", Starter.receiveCopyData); addEventListener("scriptmsg", Starter.scriptMsgEvent); @@ -438,7 +374,7 @@ function main() { Starter.isUp = "no"; - locationAction(getLocation()); + locationAction.run(getLocation()); delay(1000); } } From 52c1602ec4f9d05f00abcad5992400ab0097b52e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Aug 2023 18:20:29 -0400 Subject: [PATCH 231/758] Update D2BotPubJoin.dbj - remove debug print --- d2bs/kolbot/D2BotPubJoin.dbj | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index 308f4c59f..7dec2721b 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -3,6 +3,7 @@ * @author kolton, theBGuy * @desc Entry script for following public games * +* @typedef {import("./sdk/globals")} */ include("critical.js"); // required @@ -290,7 +291,6 @@ const locationAction = (function () { try { let func = locations.get(loc); if (typeof func === "function") { - console.debug("Running location: " + loc); func(loc); } else if (loc !== undefined && loc !== null) { console.log("Unhandled location: " + loc); @@ -329,7 +329,11 @@ function main () { delay(200); if (Starter.gameInfo.crashInfo) { - D2Bot.printToConsole("Crash Info: Script: " + Starter.gameInfo.crashInfo.currScript + " Area: " + Starter.gameInfo.crashInfo.area, sdk.colors.D2Bot.Gray); + D2Bot.printToConsole( + "Crash Info: Script: " + Starter.gameInfo.crashInfo.currScript + + " Area: " + Starter.gameInfo.crashInfo.area, + sdk.colors.D2Bot.Gray + ); } ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); @@ -366,7 +370,10 @@ function main () { Starter.setNextGame(me.gamename); } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + D2Bot.updateStatus( + me.charname + " | Game: " + (me.gamename || "singleplayer") + + Starter.timer(Starter.gameStart) + ); } delay(1000); From 8996c0dd4dadb22c9fe88f912688130d07c20e0a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:22:29 -0400 Subject: [PATCH 232/758] Update SimpleParty.js - when building acceptfirst list, check that the partyflag is currently acceptable. Fixes bug that we were selecting a party member who we invited but wasn't accepting --- d2bs/kolbot/libs/modules/workers/SimpleParty.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index a3d846cb7..231a8463f 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -13,8 +13,6 @@ const BUTTON_LEAVE_PARTY = 3; // const BUTTON_HOSTILE = 4; - print("ÿc2Kolbotÿc0 :: Simple party running"); - const SimpleParty = {}; SimpleParty.biggestPartyId = function () { @@ -54,7 +52,7 @@ const toMd5Int = what => parseInt(md5(what).substr(0, 4), 16); //ToDo; do something with game number here const names = []; for (let party = getParty(); party.getNext();) { - if (party.partyid === NO_PARTY) { + if (party.partyid === NO_PARTY && party.partyflag === ACCEPTABLE) { names.push(party.name); } } @@ -92,14 +90,20 @@ SimpleParty.timer = 0; if (getScript(true).name.toLowerCase() === "default.dbj") { - (Worker.runInBackground.party = (function () {// For now, we gonna do this in game with a single party + // For now, we gonna do this in game with a single party + (Worker.runInBackground.party = (function () { + console.log("ÿc2Kolbotÿc0 :: Simple party running"); SimpleParty.timer = getTickCount(); return function () { // Set timer back on 3 seconds, or reset it and continue - if (((getTickCount() - SimpleParty.timer) < 3000 || !me.gameReady) + if ((getTickCount() - SimpleParty.timer) < 3000 || (SimpleParty.timer = getTickCount()) && false) { return true; } + if (!me.gameReady) { + SimpleParty.timer = getTickCount(); + return true; + } // Public mode 1/2/3 dont count. This is SimplyParty if (Config.PublicMode !== true) { From 5f8a123222f76ca087673da4399cf05cd86bf35c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:25:23 -0400 Subject: [PATCH 233/758] Update Locations.js - handle first login and checking/selecting realm from main menu - include handler on first join if clicking doesn't move us to the next location (seen this while using charon) --- d2bs/kolbot/libs/oog/Locations.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index 89bc810d6..e19e4fc93 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -67,7 +67,9 @@ break; case sdk.game.profiletype.Battlenet: + ControlAction.clickRealm(ControlAction.realms[Starter.profileInfo.realm]); Controls.BattleNet.click(); + Starter.firstLogin && (Starter.firstLogin = false); break; case sdk.game.profiletype.SinglePlayer: @@ -334,8 +336,10 @@ ], ]); addLocations([sdk.game.locations.PreSplash, sdk.game.locations.SplashScreen], - function () { + function (location) { ControlAction.click(); + Starter.locationTimeout(5000, location); + getLocation() === sdk.game.locations.PreSplash && sendKey(0x0D); } ); addLocations( From 1c2eee11e6d6e04e92e195c9c3f1b2f0196ced1b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:26:24 -0400 Subject: [PATCH 234/758] Update Pickit.js - handle item disappearing -> causing undefined gid --- d2bs/kolbot/libs/core/Pickit.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index cee301530..2f6ba3fea 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -360,8 +360,10 @@ const Pickit = { sdk.uiflags.Stash, sdk.uiflags.Cube ]; + if (!unit.gid) return false; let item = Game.getItem(-1, -1, unit.gid); if (!item) return false; + if (!item.onGroundOrDropping) return false; if (cancelFlags.some(function (flag) { return getUIFlag(flag); })) { delay(500); From 2c98287949756aa8a0c6c40b369f1f97a36c3442 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:27:58 -0400 Subject: [PATCH 235/758] Update D2BotFollow.dbj - convert to new format of using locations map and moving config logic out of the entry --- d2bs/kolbot/D2BotFollow.dbj | 524 ++++++++++++++++-------------------- 1 file changed, 229 insertions(+), 295 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index afc14adfb..17568be4c 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -1,55 +1,31 @@ -/* eslint-disable max-len */ /** * @filename D2BotFollow.dbj * @author kolton, theBGuy * @desc Entry script for following bots running on the same pc * +* @typedef {import("./sdk/globals")} */ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // D2BotFollow specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.JoinRetryDelay = 5; // Time in seconds to wait before next join attempt +const { + JoinSettings +} = require("./libs/systems/follow/FollowConfig"); // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -/** - * @todo this section should be in it's own config leaving this file only containing core logic - */ -/* Join game settings - Format: "leader's profile": ["leecher 1 profile", "leecher 2 profile", ...] - If you want everyone to join the same leader, use "leader's profile": ["all"] - NOTE: Use PROFILE names (profile matches window title), NOT character/account names - leader:leecher groups need to be divided by a comma - example: - let JoinSettings = { - "lead1": ["follow1", "follow2"], - "lead2": ["follow3", "follow4"] - }; -*/ - -const JoinSettings = { - "Leader": ["Leecher"], - "map": ["all"] -}; - // the only things we really need from these are their oog checks includeSystemLibs(); -const Controls = require("./libs/modules/Control"); const Overrides = require("./libs/modules/Override"); 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 lastGameTick, leader = ""; -let announced = false; -let lastGame = []; +delete Starter.AdvancedConfig; if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; @@ -68,332 +44,286 @@ new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode } }).apply(); -function joinCheck (leader) { - D2Bot.requestGame(leader); - delay(500); +const locationAction = (function () { + let announced = false; + let lastGameTick, leader = ""; + + const lastGame = []; + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations + } = require("./libs/oog/Locations"); + + /** @param {string} leader */ + const joinCheck = function (leader) { + D2Bot.requestGame(leader); + delay(500); - if (!Starter.joinInfo.inGame || (lastGame.length && lastGame.indexOf(Starter.joinInfo.gameName) === -1)) { - D2Bot.printToConsole("Game is finished. Stopping join delay."); - Starter.gameInfo.gameName = ""; - Starter.gameInfo.gamePass = ""; + if (!Starter.joinInfo.inGame || (lastGame.length && lastGame.indexOf(Starter.joinInfo.gameName) === -1)) { + D2Bot.printToConsole("Game is finished. Stopping join delay."); + Starter.gameInfo.gameName = ""; + Starter.gameInfo.gamePass = ""; - return true; - } + return true; + } - return false; -} + return false; + }; -function locationAction (location) { - if (me.ingame || location === undefined) { - return; - } + locations.set(sdk.game.locations.Lobby, + function () { + D2Bot.updateStatus("Lobby"); - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); + me.blockKeys = false; + Starter.loginRetry = 0; + !Starter.firstLogin && (Starter.firstLogin = true); - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); + if (Starter.Config.JoinChannel !== "") { + Controls.LobbyEnterChat.click(); - me.blockKeys = false; - Starter.loginRetry = 0; - !Starter.firstLogin && (Starter.firstLogin = true); + return; + } - if (Starter.Config.JoinChannel !== "") { - Controls.LobbyEnterChat.click(); + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + return; + } - break; - } + console.log("updating runs"); + D2Bot.updateRuns(); - if (Starter.inGame) { - if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; + lastGameTick = getTickCount(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; } - print("updating runs"); - D2Bot.updateRuns(); - - lastGameTick = getTickCount(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; + Starter.LocationEvents.openJoinGameWindow(); } + ); + addLocations([sdk.game.locations.WaitingInLine, sdk.game.locations.CreateGame], + function () { + Controls.CancelCreateGame.click(); + Controls.JoinGameWindow.click(); + } + ); + locations.set(sdk.game.locations.LobbyChat, + function () { + D2Bot.updateStatus("Lobby Chat"); + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + return; + } - Starter.LocationEvents.openJoinGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - case sdk.game.locations.CreateGame: - Controls.CancelCreateGame.click(); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby Chat"); + console.log("updating runs"); + D2Bot.updateRuns(); - if (Starter.inGame) { - if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; + lastGameTick = getTickCount(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; } - print("updating runs"); - D2Bot.updateRuns(); - - lastGameTick = getTickCount(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - } - - if (!Starter.chatActionsDone) { - Starter.chatActionsDone = true; + if (!Starter.chatActionsDone) { + Starter.chatActionsDone = true; - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); - say("/j " + Starter.Config.JoinChannel); - delay(1000); + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + say("/j " + Starter.Config.JoinChannel); + delay(1000); - if (Starter.Config.FirstJoinMessage !== "") { - say(Starter.Config.FirstJoinMessage); - delay(500); + if (Starter.Config.FirstJoinMessage !== "") { + say(Starter.Config.FirstJoinMessage); + delay(500); + } } - } - Starter.LocationEvents.openJoinGameWindow(); - - break; - case sdk.game.locations.JoinGame: - D2Bot.updateStatus("Join Game"); + Starter.LocationEvents.openJoinGameWindow(); + } + ); + locations.set(sdk.game.locations.JoinGame, + function (location) { + D2Bot.updateStatus("Join Game"); - if (!leader) { - leader = []; + if (!leader) { + leader = []; - for (let i in JoinSettings) { - if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { - for (let j = 0; j < JoinSettings[i].length; j += 1) { - if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { - leader.push(i); + for (let i in JoinSettings) { + if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { + for (let j = 0; j < JoinSettings[i].length; j += 1) { + if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { + leader.push(i); + } } } } } - } - if (!leader || !leader.length && !announced) { - print("No leader"); - D2Bot.printToConsole("No leader"); - announced = true; + if (!leader || !leader.length && !announced) { + console.log("No leader"); + D2Bot.printToConsole("No leader"); + announced = true; - break; - } + return; + } - JoinLoop2: - for (let i = 0; i < 5; i += 1) { - for (let j = 0; j < leader.length; j += 1) { - Starter.joinInfo = {}; - D2Bot.requestGame(leader[j]); - delay(100); + JoinLoop2: + for (let i = 0; i < 5; i += 1) { + for (let j = 0; j < leader.length; j += 1) { + Starter.joinInfo = {}; + D2Bot.requestGame(leader[j]); + delay(100); - if (!Starter.joinInfo.hasOwnProperty("gameName") || Starter.joinInfo.gameName === "") { - delay(500); - continue; - } + if (!Starter.joinInfo.hasOwnProperty("gameName") || Starter.joinInfo.gameName === "") { + delay(500); + continue; + } - /** - * @todo handle rejoin, need to keep track of game averages and when requesting game from a leader who's game we left get the current game time - * and see if there is x amount of time left that makes it worth it vs waiting for next. - */ + /** + * @todo handle rejoin, need to keep track of game averages and when requesting game from a + * leader who's game we left get the current game time + * and see if there is x amount of time left that makes it worth it vs waiting for next. + */ - if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { - Controls.JoinGameName.setText(Starter.joinInfo.gameName); - Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); + if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { + Controls.JoinGameName.setText(Starter.joinInfo.gameName); + Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); - if (Starter.lastGameStatus === "pending" || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { - D2Bot.printToConsole("Failed to join game"); - ControlAction.timeoutDelay("Join Delay", Starter.Config.JoinRetryDelay * 1000, joinCheck(leader[j])); - D2Bot.updateRuns(); - D2Bot.requestGame(leader[j]); - delay(200); + if (Starter.lastGameStatus === "pending" || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { + D2Bot.printToConsole("Failed to join game"); + ControlAction.timeoutDelay("Join Delay", Starter.Config.JoinRetryDelay * 1000, joinCheck(leader[j])); + D2Bot.updateRuns(); + D2Bot.requestGame(leader[j]); + delay(200); - if (!Starter.joinInfo.inGame) { - Starter.lastGameStatus = "ready"; + if (!Starter.joinInfo.inGame) { + Starter.lastGameStatus = "ready"; - break; + break; + } } - } - if (!Starter.joinInfo.inGame) { - if (Starter.joinInfo.delay) { - ControlAction.timeoutDelay("Leader Delay", Starter.joinInfo.delay); + if (!Starter.joinInfo.inGame) { + if (Starter.joinInfo.delay) { + ControlAction.timeoutDelay("Leader Delay", Starter.joinInfo.delay); + } + continue; } - continue; - } - // Don't join immediately after previous game to avoid FTJ - if (getTickCount() - lastGameTick < 5000) { - ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); - } + // Don't join immediately after previous game to avoid FTJ + if (getTickCount() - lastGameTick < 5000) { + ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); + } - print("joining game " + Starter.joinInfo.gameName); + console.log("joining game " + Starter.joinInfo.gameName); - if (typeof Starter.Config.JoinDelay === "number") { - ControlAction.timeoutDelay("Custom Join Delay", Starter.Config.JoinDelay * 1e3); - } + if (typeof Starter.Config.JoinDelay === "number") { + ControlAction.timeoutDelay("Custom Join Delay", Starter.Config.JoinDelay * 1e3); + } - me.blockMouse = true; + me.blockMouse = true; - DataFile.updateStats("gameName", Starter.joinInfo.gameName); - Controls.JoinGame.click(); + DataFile.updateStats("gameName", Starter.joinInfo.gameName); + Controls.JoinGame.click(); - me.blockMouse = false; + me.blockMouse = false; - lastGame.push(Starter.joinInfo.gameName); + lastGame.push(Starter.joinInfo.gameName); - // Might need a fixed number. Right now it stores 1 game per leader. - lastGame.length > leader.length && lastGame.shift(); + // Might need a fixed number. Right now it stores 1 game per leader. + lastGame.length > leader.length && lastGame.shift(); - Starter.lastGameStatus = "pending"; - Starter.locationTimeout(15000, location); + Starter.lastGameStatus = "pending"; + Starter.locationTimeout(15000, location); - break JoinLoop2; - } else { - // for now, if leader is in game and it's the last game we were in. delay to prevent copyData spam - if (lastGame.includes(Starter.joinInfo.gameName)) { - delay((Starter.joinInfo.inGame ? 5000 : 2000)); + break JoinLoop2; + } else { + // for now, if leader is in game and it's the last game we were in. delay to prevent copyData spam + if (lastGame.includes(Starter.joinInfo.gameName)) { + delay((Starter.joinInfo.inGame ? 5000 : 2000)); + } } } } } - - break; - case sdk.game.locations.Ladder: - break; - case sdk.game.locations.ChannelList: - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - case sdk.game.locations.SplashScreen: - Starter.LocationEvents.login([sdk.game.gametype.TcpIpJoin, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); - - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - - break; - case sdk.game.locations.GameIsFull: - D2Bot.printToConsole("Game is full"); - Controls.JoinGameWindow.click(); - lastGame.push(Starter.joinInfo.gameName); - Starter.lastGameStatus = "ready"; - - break; - case sdk.game.locations.OtherMultiplayer: - Profile().type === sdk.game.profiletype.TcpIpJoin ? Controls.TcpIp.click() : Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - Profile().type === sdk.game.profiletype.TcpIpJoin ? Controls.TcpIpJoin.click() : Controls.TcpIpCancel.click(); - - break; - case sdk.game.locations.TcpIpEnterIp: - try { - if (!leader) { - leader = []; - - for (let i in JoinSettings) { - if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { - for (let j = 0; j < JoinSettings[i].length; j += 1) { - if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { - leader.push(i); + ); + locations.set(sdk.game.locations.GameIsFull, + function () { + D2Bot.printToConsole("Game is full"); + Controls.JoinGameWindow.click(); + lastGame.push(Starter.joinInfo.gameName); + Starter.lastGameStatus = "ready"; + } + ); + locations.set(sdk.game.locations.TcpIp, + function () { + Profile().type === sdk.game.profiletype.TcpIpJoin + ? Controls.TcpIpJoin.click() + : Controls.TcpIpCancel.click(); + } + ); + locations.set(sdk.game.locations.TcpIpEnterIp, + function () { + try { + if (!leader) { + leader = []; + + for (let i in JoinSettings) { + if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { + for (let j = 0; j < JoinSettings[i].length; j += 1) { + if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { + leader.push(i); + } } } } } - } - mainLoop: - for (let i = 0; i < 3; i++) { - for (let j = 0; j < leader.length; j++) { - D2Bot.requestGame(leader[j]); + mainLoop: + for (let i = 0; i < 3; i++) { + for (let j = 0; j < leader.length; j++) { + D2Bot.requestGame(leader[j]); - if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "") { - break mainLoop; + if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "") { + break mainLoop; + } } } - } - if (Controls.IPAdress.setText(Object.keys(Starter.joinInfo).length ? Starter.joinInfo.gameName : "localhost") - && Controls.IPAdressOk.click() - && Starter.locationTimeout(2e3, sdk.game.locations.TcpIpEnterIp)) { - getLocation() === sdk.game.locations.CharSelect && login(me.profile); + if (Controls.IPAdress.setText(Object.keys(Starter.joinInfo).length ? Starter.joinInfo.gameName : "localhost") + && Controls.IPAdressOk.click() + && Starter.locationTimeout(2e3, sdk.game.locations.TcpIpEnterIp)) { + getLocation() === sdk.game.locations.CharSelect && login(me.profile); + } + } catch (e) { + console.error(e); } - } catch (e) { - print(e); - } - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); } - - break; - } -} + ); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; +})(); function main () { debugLog(me.profile); @@ -421,7 +351,11 @@ function main () { if (!!DataFile.getStats().debugInfo) { Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, + sdk.colors.D2Bot.Gray + ); } ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); @@ -432,7 +366,7 @@ function main () { while (!Object.keys(Starter.profileInfo).length) { D2Bot.getProfile(); - print("Getting Profile"); + console.log("Getting Profile"); delay(500); } @@ -442,7 +376,7 @@ function main () { // returns false when switching acts so we can't use while if (me.gameReady) { if (!Starter.inGame) { - print("ÿc4Updating Status"); + console.log("ÿc4Updating Status"); Starter.lastGameStatus = "ingame"; Starter.inGame = true; Starter.gameStart = getTickCount(); @@ -456,7 +390,7 @@ function main () { delay(1000); } - locationAction(getLocation()); + locationAction.run(getLocation()); delay(1000); } } From 3434e2d4922e4e4b57b6256b0f5a6264b11675f9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:29:31 -0400 Subject: [PATCH 236/758] Update globals.d.ts - add `Array.at` definition - add `me.accessToAct` definition --- d2bs/kolbot/sdk/globals.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 8a902c641..5f2b56ca7 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -41,6 +41,7 @@ declare global { find(predicate: (value: T, index: number, obj: Int8Array) => boolean, thisArg?: any): T | undefined; first(): T | undefined; last(): T | undefined; + at(index: number): T | undefined; findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number; intersection(other: T[]): T[]; difference(other: T[]): T[]; @@ -671,6 +672,7 @@ declare global { readonly attacking: boolean; haveWaypoint(area: number): boolean; + accessToAct(act: number): boolean; overhead(msg: string): void; repair(): boolean; revive(): void; From d5f0ca5f53607ed93a3f803084734ce3eed83aa2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:43:02 -0400 Subject: [PATCH 237/758] Update D2BotPubJoin.dbj - add min player count option - show charlvl in status bar --- d2bs/kolbot/D2BotPubJoin.dbj | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index 7dec2721b..beec61eed 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -14,6 +14,7 @@ Starter.Config.ResetCount = 0; // Reset game count back to 1 every X games. Starter.Config.JoinDelay = 10; // Seconds to wait between join attempts Starter.Config.AttemptNextGame = true; // after joining a game, attempt incrementing game count and joining next game rather than looking for it in game list Starter.Config.AttemptNextGameRetrys = 5; +Starter.Config.MinPlayers = 1; // Minimum players in game to join const { includeFilter, excludeFilter @@ -222,8 +223,6 @@ const locationAction = (function () { delay(1000); } - console.debug(gameList); - if (gameList) { doneGames = []; gameToJoin = false; @@ -231,16 +230,18 @@ const locationAction = (function () { doneGames = JSON.parse(FileAction.read("logs/doneGames.json")); } - gameList.sort(function (a, b) { - return b.players - a.players; - }); + gameList + .sort(function (a, b) { + return b.players - a.players; + }); - for (let i = 0; i < gameList.length; i += 1) { - if (doneGames.indexOf(gameList[i].gameName) === -1 - && includeCheck(gameList[i].gameName) - && excludeCheck(gameList[i].gameName)) { - console.log("ÿc7Game: " + gameList[i].gameName + ", Players: " + gameList[i].players); - gameToJoin = gameList[i].gameName; + for (let { gameName, players } of gameList) { + if (players < Starter.Config.MinPlayers) continue; + if (doneGames.indexOf(gameName) === -1 + && includeCheck(gameName) + && excludeCheck(gameName)) { + console.log("ÿc7Game: " + gameName + ", Players: " + players); + gameToJoin = gameName; break; } @@ -371,7 +372,7 @@ function main () { } D2Bot.updateStatus( - me.charname + " | Game: " + (me.gamename || "singleplayer") + me.charname + " (" + me.charlvl + ") | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart) ); } From 562493b226efcbdc96559bca29fcf29f01b334df Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:50:15 -0400 Subject: [PATCH 238/758] Update D2BotLead.dbj - convert lead entry to new format --- d2bs/kolbot/D2BotLead.dbj | 314 ++++---------------------------------- 1 file changed, 30 insertions(+), 284 deletions(-) diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 72c7d4ce2..38d92757e 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /* eslint-disable no-fallthrough */ /** * @filename D2BotLead.dbj @@ -21,294 +20,34 @@ const Controls = require("./libs/modules/Control"); if (typeof Starter.AdvancedConfig[me.profile] === "object") { Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); -} else { - // no need to carry around the reference then - delete Starter.AdvancedConfig; } +delete Starter.AdvancedConfig; if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; } -function locationAction (location) { - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - Starter.locationTimeout(5000, location); - getLocation() === sdk.game.locations.PreSplash && sendKey(0x0D); - - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); - - me.blockKeys = false; - Starter.loginRetry = 0; - !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(); - - break; - } - - 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } - - print("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.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); - - break; - case sdk.game.locations.LobbyChat: - 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 (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; - } - - print("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; - - // check that we are in the channel we are supposed to be in - if (Starter.chanInfo.joinChannel.length) { - let chanName = Controls.LobbyChannelName.getText(); - chanName && (chanName = chanName.toString()); - chanName && (chanName = chanName.slice(0, chanName.indexOf("(") - 1)); - Starter.chanInfo.joinChannel.indexOf(chanName) === -1 && (Starter.chatActionsDone = false); - } - - if (Starter.chanInfo.afterMsg) { - if (typeof Starter.chanInfo.afterMsg === "string") { - Starter.chanInfo.afterMsg = [Starter.chanInfo.afterMsg]; - } - - for (let i = 0; i < Starter.chanInfo.afterMsg.length; i += 1) { - Starter.sayMsg(Starter.chanInfo.afterMsg[i]); - delay(500); +const locationAction = (function () { + const { + locations, + } = require("./libs/oog/Locations"); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); } + } catch (e) { + console.error(e); } - } - - if (!Starter.chatActionsDone) { - Starter.chatActionsDone = true; - Starter.chanInfo.joinChannel = Starter.Config.JoinChannel; - Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; - - if (Starter.chanInfo.joinChannel) { - typeof Starter.chanInfo.joinChannel === "string" && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); - typeof Starter.chanInfo.firstMsg === "string" && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); - - for (let i = 0; i < Starter.chanInfo.joinChannel.length; i += 1) { - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); - - if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { - Starter.useChat = true; - } else { - print("ÿ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; - - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.CreateGame: - 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()); - - // Get game name if there is none - while (!Starter.gameInfo.gameName) { - D2Bot.requestGameInfo(); - delay(500); - } - - // 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(); - } - - let gameName = (Starter.gameInfo.gameName === "?" ? Starter.randomString(null, true) : Starter.gameInfo.gameName + Starter.gameCount); - let gamePass = (Starter.gameInfo.gamePass === "?" ? Starter.randomString(null, true) : Starter.gameInfo.gamePass); - - ControlAction.createGame(gameName, gamePass, Starter.gameInfo.difficulty, Starter.Config.CreateGameDelay * 1000); - - Starter.lastGameStatus = "pending"; - Starter.setNextGame(Starter.gameInfo); - Starter.locationTimeout(10000, location); - - break; - case sdk.game.locations.JoinGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.SplashScreen: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - Starter.LocationEvents.login([sdk.game.gametype.TcpIpHost, sdk.game.profiletype.OpenBattlenet].includes(Profile().type)); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); - - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameIsFull: - Controls.CreateGameWindow.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - - break; - case sdk.game.locations.CharacterCreate: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); - - break; - case sdk.game.locations.TcpIp: - Profile().type === sdk.game.profiletype.TcpIpHost ? Controls.TcpIpHost.click() : Controls.TcpIpCancel.click(); - - break; - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } -} + }, + }; +})(); function main () { debugLog(me.profile); @@ -342,7 +81,11 @@ function main () { if (!!DataFile.getStats().debugInfo) { Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, + sdk.colors.D2Bot.Gray + ); } ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); @@ -367,7 +110,10 @@ function main () { DataFile.updateStats("ingameTick"); } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + D2Bot.updateStatus( + me.charname + " (" + me.charlvl + ") | Game: " + (me.gamename || "singleplayer") + + Starter.timer(Starter.gameStart) + ); } delay(1000); @@ -375,7 +121,7 @@ function main () { Starter.isUp = "no"; - locationAction(getLocation()); + locationAction.run(getLocation()); delay(1000); } } From 9e730bbc835dc729418294ddf7f37244ea939044 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:24:17 -0400 Subject: [PATCH 239/758] Update D2BotMuleLog.dbj - convert mulelog to new format --- d2bs/kolbot/D2BotMuleLog.dbj | 430 ++++++++++++++++------------------- 1 file changed, 201 insertions(+), 229 deletions(-) diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 3d3a1c10b..444bf6ff3 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -21,20 +21,15 @@ Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/ba include("systems/dropper/DropperSetup.js"); include("systems/mulelogger/MuleLogger.js"); -const Controls = require("./libs/modules/Control"); - if (!FileTools.exists("data/" + me.profile + ".json")) { DataFile.create(); } -let currAcc; -let usingDroper = isIncluded("systems/dropper/DropperSetup.js"); -let charList = []; -let accounts = []; -let chars = []; - -function parseInfo () { - usingDroper && parseDropperAccounts(accounts, chars); +const usingDropper = isIncluded("systems/dropper/DropperSetup.js"); +const accounts = []; +const chars = []; +const parseInfo = function () { + usingDropper && parseDropperAccounts(accounts, chars); for (let i in MuleLogger.LogAccounts) { if (MuleLogger.LogAccounts.hasOwnProperty(i) && typeof i === "string") { @@ -42,261 +37,234 @@ function parseInfo () { chars.push(MuleLogger.LogAccounts[i]); } } -} - -function locationAction (location) { - let i, currChar, - obj = {}; +}; +const locationAction = (function () { + let currAcc; + let charList = []; + let obj = {}; + + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations + } = require("./libs/oog/Locations"); + + addLocations([sdk.game.locations.Lobby, sdk.game.locations.LobbyChat], + function () { + D2Bot.updateStatus("Lobby"); + + if (Starter.inGame) { + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay( + "Min game time wait", + Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount() + ); + } - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); + console.log("updating runs"); + D2Bot.updateRuns(); + delay(1000); - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + Controls.LobbyQuit.click(); - if (Starter.inGame) { - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + return; } - print("updating runs"); - D2Bot.updateRuns(); - delay(1000); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - Controls.LobbyQuit.click(); - - break; + Starter.LocationEvents.openCreateGameWindow(); } + ); + addLocations([sdk.game.locations.WaitingInLine, sdk.game.locations.CreateGame], + function () { + Controls.CancelCreateGame.click(); + Controls.JoinGameWindow.click(); + } + ); + locations.set(sdk.game.locations.CreateGame, + function (location) { + D2Bot.updateStatus("Creating Game"); + + // remove level restriction + if (Controls.CharacterDifference.disabled === 5) { + Controls.CharacterDifferenceButton.click(); + } - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); - - break; - case sdk.game.locations.CreateGame: - D2Bot.updateStatus("Creating Game"); - - // remove level restriction - Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); + // Max number of players + Controls.MaxPlayerCount.setText("8"); - // Max number of players - Controls.MaxPlayerCount.setText("8"); + if (Starter.gameCount >= 99) { + Starter.gameCount = 1; - if (Starter.gameCount >= 99) { - Starter.gameCount = 1; + DataFile.updateStats("runs", Starter.gameCount); + } - DataFile.updateStats("runs", Starter.gameCount); - } + if (Starter.lastGameStatus === "pending") { + D2Bot.printToConsole("Failed to create game"); - if (Starter.lastGameStatus === "pending") { - D2Bot.printToConsole("Failed to create game"); + Starter.gameCount += 1; + } - Starter.gameCount += 1; + ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); + createGame(MuleLogger.LogGame[0] + Starter.gameCount, MuleLogger.LogGame[1], 0); + Starter.locationTimeout(5000, location); + Starter.lastGameStatus = "pending"; } - - ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); - createGame(MuleLogger.LogGame[0] + Starter.gameCount, MuleLogger.LogGame[1], 0); - Starter.locationTimeout(5000, location); - Starter.lastGameStatus = "pending"; - - break; - case sdk.game.locations.JoinGame: - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Starter.LocationEvents.openCreateGameWindow(); - - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.SplashScreen: - if (!accounts.length) { - MuleLogger.remove(); - D2Bot.printToConsole("Done logging mules!"); - D2Bot.stop(); - - break; + ); + addLocations( + [ + sdk.game.locations.JoinGame, + sdk.game.locations.Ladder, + sdk.game.locations.ChannelList, + ], + function () { + Starter.LocationEvents.openCreateGameWindow(); } + ); + addLocations( + [ + sdk.game.locations.MainMenu, + sdk.game.locations.Login, + sdk.game.locations.SplashScreen, + ], + function () { + if (!accounts.length) { + MuleLogger.remove(); + D2Bot.printToConsole("Done logging mules!"); + D2Bot.stop(); + + return; + } - if (FileTools.exists("logs/MuleLog.json")) { - obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); + if (FileTools.exists("logs/MuleLog.json")) { + obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); - if (obj.currAcc) { - for (i = 0; i < accounts.length; i += 1) { - if (accounts[i].split("/")[0] === obj.currAcc) { - accounts.splice(0, i); - chars.splice(0, i); - i -= 1; + if (obj.currAcc) { + for (let i = 0; i < accounts.length; i += 1) { + if (accounts[i].split("/")[0] === obj.currAcc) { + accounts.splice(0, i); + chars.splice(0, i); + i -= 1; - break; + break; + } } } } - } - currAcc = accounts[0]; - currAcc = currAcc.split("/"); - charList = chars[0]; - obj.currAcc = currAcc[0]; + currAcc = accounts[0]; + currAcc = currAcc.split("/"); + charList = chars[0]; + obj.currAcc = currAcc[0]; - print("ÿc4Mule Loggerÿc2: Login account: " + currAcc[0]); - MuleLogger.save(md5(currAcc[2].toLowerCase() + currAcc[0].toLowerCase()), currAcc[1]); + console.log("ÿc4Mule Loggerÿc2: Login account: " + currAcc[0]); + MuleLogger.save(md5(currAcc[2].toLowerCase() + currAcc[0].toLowerCase()), currAcc[1]); - if (ControlAction.loginAccount({ account: currAcc[0], password: currAcc[1], realm: currAcc[2] })) { - accounts.shift(); // remove current account from the list - } - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.CharSelect: - // Single Player screen fix - if (getLocation() === sdk.game.locations.CharSelect - && !Controls.CharSelectCurrentRealm.control - && Controls.BottomLeftExit.click()) { - break; + if (ControlAction.loginAccount({ account: currAcc[0], password: currAcc[1], realm: currAcc[2] })) { + accounts.shift(); // remove current account from the list + } } + ); + locations.set(sdk.game.locations.CharSelect, + function () { + // Single Player screen fix + if (getLocation() === sdk.game.locations.CharSelect + && !Controls.CharSelectCurrentRealm.control + && Controls.BottomLeftExit.click()) { + return; + } - if (!charList.length && Controls.BottomLeftExit.click()) { - break; - } + if (!charList.length && Controls.BottomLeftExit.click()) { + return; + } - charList[0] === "all" && (charList = ControlAction.getCharacters()); + charList[0] === "all" && (charList = ControlAction.getCharacters()); - if (FileTools.exists("logs/MuleLog.json")) { - obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); - - if (obj.currChar) { - for (i = 0; i < charList.length; i += 1) { - if (charList[i] === obj.currChar) { - // Remove the previous currChar as well - charList.splice(0, i + 1); + if (FileTools.exists("logs/MuleLog.json")) { + obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); + + if (obj.currChar) { + for (let i = 0; i < charList.length; i += 1) { + if (charList[i] === obj.currChar) { + // Remove the previous currChar as well + charList.splice(0, i + 1); - break; + break; + } } } } - } - - // last char in acc = trigger next acc - if (!charList.length) { - print("No more characters"); - accounts.shift(); // remove current account from the list - chars.shift(); - - break; - } - currChar = charList.shift(); - obj.currChar = currChar; + // last char in acc = trigger next acc + if (!charList.length) { + console.log("No more characters"); + accounts.shift(); // remove current account from the list + chars.shift(); - print("ÿc4Mule Loggerÿc2: Login character: " + currChar); - FileTools.writeText("logs/MuleLog.json", JSON.stringify(obj)); + return; + } - if (MuleLogger.AutoPerm) { - let characterStatus = { - charname: currChar, - perm: ControlAction.getPermStatus({ charName: currChar }) - }; - MuleLogger.savePermedStatus(characterStatus); - } + let currChar = charList.shift(); + obj.currChar = currChar; - ControlAction.loginCharacter({ charName: currChar }); + console.log("ÿc4Mule Loggerÿc2: Login character: " + currChar); + FileTools.writeText("logs/MuleLog.json", JSON.stringify(obj)); - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); + if (MuleLogger.AutoPerm) { + let characterStatus = { + charname: currChar, + perm: ControlAction.getPermStatus({ charName: currChar }) + }; + MuleLogger.savePermedStatus(characterStatus); + } - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.NewCharSelected: - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - Starter.LocationEvents.selectDifficultySP(); - - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - if (!Starter.LocationEvents.charSelectError()) { - accounts.shift(); // remove current account from the list - chars.shift(); + ControlAction.loginCharacter({ charName: currChar }); } - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - case sdk.game.locations.GameDoesNotExist: - Controls.CreateGameWindow.click(); - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameIsFull: - D2Bot.printToConsole("Game is full"); - Starter.lastGameStatus = "ready"; - delay(500); - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.OtherMultiplayer: - // probably should implement way to use open bnet - Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); + ); + addLocations([sdk.game.locations.CharSelectConnecting, sdk.game.locations.CharSelectNoChars], + function () { + if (!Starter.LocationEvents.charSelectError()) { + accounts.shift(); // remove current account from the list + chars.shift(); + } + } + ); + locations.set(sdk.game.locations.GameIsFull, + function () { + D2Bot.printToConsole("Game is full"); + Starter.lastGameStatus = "ready"; delay(500); - D2Bot.restart(); + Controls.JoinGameWindow.click(); } - - break; - } -} + ); + locations.set(sdk.game.locations.OtherMultiplayer, + function () { + Controls.OtherMultiplayerCancel.click(); + } + ); + locations.set(sdk.game.locations.TcpIp, + function () { + Controls.TcpIpCancel.click(); + } + ); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; +})(); function main () { addEventListener("copydata", Starter.receiveCopyData); @@ -325,10 +293,14 @@ function main () { parseInfo(); if (Starter.gameInfo.error) { - if (!!DataFile.getStats().debugInfo) { + if (DataFile.getStats().debugInfo) { Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, + sdk.colors.D2Bot.Gray + ); } ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); @@ -343,7 +315,7 @@ function main () { // returns false when switching acts so we can't use while if (me.gameReady) { if (!Starter.inGame) { - print("Updating Status"); + console.log("Updating Status"); Starter.lastGameStatus = "ingame"; Starter.inGame = true; Starter.gameStart = getTickCount(); @@ -356,7 +328,7 @@ function main () { delay(1000); } - locationAction(getLocation()); + locationAction.run(getLocation()); delay(1000); } } From ba22248606563f2bec7f3b1be177887d7b2c0be0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:13:24 -0400 Subject: [PATCH 240/758] Add SkillCycloneArmor stat to sdk - Found out both bone armor and cyclone armor share a stat but for readability added it as `SkillCycloneArmor` --- d2bs/kolbot/libs/modules/sdk.js | 5 ++++- d2bs/kolbot/sdk/types/sdk.d.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index dc626b1e5..ec92fb454 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -1154,7 +1154,9 @@ SkillThornsPercent: 131, SkillBoneArmor: 132, + SkillCycloneArmor: 132, SkillBoneArmorMax: 133, + SkillCycloneArmorMax: 133, SkillFade: 181, SkillPoisonOverrideLength: 101, SkillBypassUndead: 103, @@ -1950,6 +1952,7 @@ PutridDefiler: 58, }, DiablosBoneCage: 340, + DiablosBoneCage2: 342, Dummy1: 149, Dummy2: 268, AbyssKnight: 311, @@ -2329,7 +2332,7 @@ SandMaggotYoung: 180, SandRaider1: 29, SandRaider2: 601, - SandWarrior: 92, + DeathBeetle: 92, Scarab1: 93, Scarab2: 654, Sentry1: 411, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 50d08b830..dcb2de81d 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -1081,7 +1081,9 @@ declare global { const SkillThornsPercent: 131; const SkillBoneArmor: 132; + const SkillCycloneArmor: 132; const SkillBoneArmorMax: 133; + const SkillCycloneArmorMax: 133; const SkillFade: 181; const SkillPoisonOverrideLength: 101; const SkillBypassUndead: 103; From d1194f578dfa1ea66a906e182fb8ab74579f59e0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:56:39 -0400 Subject: [PATCH 241/758] Update Precast.js - found cyclonearmor max stat, update to use it - extend `PrecastSkill` class for the absorb armor skills - add `PrecastSkill.update` method --- d2bs/kolbot/libs/core/Precast.js | 57 ++++++++++++++++---------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 1122fdebe..7d23c476c 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -34,6 +34,31 @@ const Precast = (function () { if (!this.canUse()) return false; return force || !me.getState(this.state) || this.needSoon(percent); }; + PrecastSkill.prototype.update = function () { + this.lastCast = getTickCount(); + }; + + /** + * @constructor + * @augments PrecastSkill + * @param {number} skillId + */ + function PrecastArmorSkill (skillId) { + PrecastSkill.call(this, skillId); + this.max = 0; + } + PrecastArmorSkill.prototype = Object.create(PrecastSkill.prototype); + PrecastArmorSkill.prototype.constructor = PrecastArmorSkill; + + PrecastArmorSkill.prototype.remaining = function () { + return this.max > 0 + ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) + : 0; + }; + PrecastArmorSkill.prototype.update = function () { + this.lastCast = getTickCount(); + this.max = me.getStat(sdk.stats.SkillBoneArmorMax); + }; return { enabled: true, /** @type {number} */ @@ -57,34 +82,14 @@ const Precast = (function () { [sdk.skills.Shout, new PrecastSkill(sdk.skills.Shout)], [sdk.skills.BattleOrders, new PrecastSkill(sdk.skills.BattleOrders)], [sdk.skills.BattleCommand, new PrecastSkill(sdk.skills.BattleCommand)], - [sdk.skills.CycloneArmor, new PrecastSkill(sdk.skills.CycloneArmor)], [sdk.skills.Hurricane, new PrecastSkill(sdk.skills.Hurricane)], [sdk.skills.Armageddon, new PrecastSkill(sdk.skills.Armageddon)], [sdk.skills.Fade, new PrecastSkill(sdk.skills.Fade)], [sdk.skills.BurstofSpeed, new PrecastSkill(sdk.skills.BurstofSpeed)], [sdk.skills.BladeShield, new PrecastSkill(sdk.skills.BladeShield)], [sdk.skills.Venom, new PrecastSkill(sdk.skills.Venom)], - [sdk.skills.BoneArmor, { - skillId: sdk.skills.BoneArmor, - state: sdk.states.BoneArmor, - lastCast: 0, - max: 0, - canUse: function () { - return Skill.canUse(this.skillId); - }, - remaining: function () { - return this.max > 0 - ? Math.round(me.getStat(sdk.stats.SkillBoneArmor) * 100 / this.max) - : 100; - }, - needSoon: function (percent = 25) { - return this.remaining() < percent; - }, - needToCast: function (force = false, percent = 25) { - if (!this.canUse()) return false; - return force || !me.getState(this.state) || this.needSoon(percent); - }, - }], + [sdk.skills.BoneArmor, new PrecastArmorSkill(sdk.skills.BoneArmor)], + [sdk.skills.CycloneArmor, new PrecastArmorSkill(sdk.skills.CycloneArmor)], ]), nonPacketSkills: new Set([ sdk.skills.Valkyrie, sdk.skills.Decoy, sdk.skills.RaiseSkeleton, @@ -287,11 +292,12 @@ const Precast = (function () { }, 100, 10); } if (Precast.skills.has(skillId)) { - Precast.skills.get(skillId).lastCast = getTickCount(); + Precast.skills.get(skillId).update(); } return state ? me.getState(state) : true; } catch (e) { console.error(e); + return false; } finally { allowSwitch && me.switchWeapons(swap); @@ -624,8 +630,3 @@ const Precast = (function () { }, }; })(); -// handle cyclone armor for lack of duration -Precast.skills.get(sdk.skills.CycloneArmor).needToCast = function (force = false) { - if (!this.canUse()) return false; - return force || !me.getState(this.state); -}; From 53500967905d0922b9d9fd52fbcfc6a96b1ddc77 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 14:25:01 -0400 Subject: [PATCH 242/758] Update SkillData.js - add skillTab --- d2bs/kolbot/libs/core/GameData/SkillData.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index ce0c13602..30d8ce1ea 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -1316,6 +1316,14 @@ "None", "None", "Physical" ]; + /** + * probably an easier way to get tab id from getBaseStat + * @type {{ id: number, skills: number[] }[]} + */ + const skillTabs = Object.keys(sdk.skillTabs) + .map((key) => Object.values(sdk.skillTabs[key])) + .flat(); + /** * @constructor * @param {number} skillId @@ -1343,6 +1351,10 @@ /** @type {number} */ this.charClass = getBaseStat("skills", skillId, "charClass"); /** @type {number} */ + this.skillTab = (function () { + return skillTabs.find((tab) => tab.skills.includes(skillId)) || { id: -1 }; + })().id; + /** @type {number} */ this.reqLevel = getBaseStat("skills", skillId, "reqlevel"); /** @type {number[]} */ this.preReqs = (function () { From f7bc61bc73a3ed24c2b05cd071f401622dc949d7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 14:25:29 -0400 Subject: [PATCH 243/758] Update Skill.js - add getter for skillTab --- d2bs/kolbot/libs/core/Skill.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 551a1b594..bf93db111 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -213,6 +213,10 @@ break; case sdk.player.class.Druid: + { + let cMax = me.getStat(sdk.stats.SkillCycloneArmorMax); + cMax > 0 && (Precast.skills.get(sdk.skills.CycloneArmor).max = cMax); + } if (!!Config.SummonAnimal && Config.SummonAnimal !== "None") { Config.SummonAnimal = (function () { switch (Config.SummonAnimal) { @@ -362,10 +366,19 @@ * @returns {number} */ getCharClass: function (skillId) { - if (!_SkillData.has(skillId)) return 0; + if (!_SkillData.has(skillId)) return -1; return _SkillData.get(skillId).charClass; }, + /** + * @param {number} skillId + * @returns {number} + */ + getSkillTab: function (skillId) { + if (!_SkillData.has(skillId)) return -1; + return _SkillData.get(skillId).skillTab; + }, + /** * Get mana cost of the skill (mBot) * @param {number} skillId From c6786d8a13a3a35cb21561955fe44acb79dee1ea Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 14:35:34 -0400 Subject: [PATCH 244/758] Update Precast.js - clean up `getBetterSlot` method --- d2bs/kolbot/libs/core/Precast.js | 44 ++++---------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 7d23c476c..be571a2e0 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -155,45 +155,11 @@ const Precast = (function () { getBetterSlot: function (skillId) { if (this.bestSlot[skillId] !== undefined) return this.bestSlot[skillId]; - let [classid, skillTab] = (function () { - switch (skillId) { - case sdk.skills.FrozenArmor: - case sdk.skills.ShiverArmor: - case sdk.skills.ChillingArmor: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Cold]; - case sdk.skills.Enchant: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Fire]; - case sdk.skills.ThunderStorm: - case sdk.skills.EnergyShield: - return [sdk.player.class.Sorceress, sdk.skills.tabs.Lightning]; - case sdk.skills.BoneArmor: - return [sdk.player.class.Necromancer, sdk.skills.tabs.PoisonandBone]; - case sdk.skills.HolyShield: - return [sdk.player.class.Paladin, sdk.skills.tabs.PalaCombat]; - case sdk.skills.Taunt: - case sdk.skills.FindItem: - case sdk.skills.BattleCry: - case sdk.skills.WarCry: - case sdk.skills.Shout: - case sdk.skills.BattleOrders: - case sdk.skills.BattleCommand: - return [sdk.player.class.Barbarian, sdk.skills.tabs.Warcries]; - case sdk.skills.CycloneArmor: - return [sdk.player.class.Druid, sdk.skills.tabs.Elemental]; - case sdk.skills.Werewolf: - case sdk.skills.Werebear: - return [sdk.player.class.Druid, sdk.skills.tabs.ShapeShifting]; - case sdk.skills.BurstofSpeed: - case sdk.skills.Fade: - return [sdk.player.class.Assassin, sdk.skills.tabs.ShadowDisciplines]; - case sdk.skills.BladeShield: - return [sdk.player.class.Assassin, sdk.skills.tabs.MartialArts]; - default: - return [-1, -1]; - } - })(); - - if (classid < 0) return me.weaponswitch; + const [classid, skillTab] = [ + Skill.getCharClass(skillId), Skill.getSkillTab(skillId) + ]; + + if (classid < 0 || classid === 255) return me.weaponswitch; me.weaponswitch !== 0 && me.switchWeapons(0); From af126ab7ef7e7872b12d7ef188b9cefeab3d3ee8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Aug 2023 14:35:59 -0400 Subject: [PATCH 245/758] Update Skill.d.ts --- d2bs/kolbot/sdk/types/Skill.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts index 6c9269fb5..2e5996d62 100644 --- a/d2bs/kolbot/sdk/types/Skill.d.ts +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -4,6 +4,7 @@ declare global { skillId: number; hand: number; state: number; + summonType: number; summonCount: () => number; condition: () => boolean; townSkill: boolean; @@ -68,10 +69,13 @@ declare global { function canUse(skillId: number): boolean; function getDuration(skillId: number): number; function getMaxSummonCount(skillId: number): number; + function getSummonType(skillId: number): number; function getRange(skillId: number): number; function getAoE(skillId: number): number; function getHand(skillId: number): number; function getState(skillId: number): number; + function getCharClass(skillId: number): number; + function getSkillTab(skillId: number): number; function getManaCost(skillId: number): number; function isTimed(skillId: number): boolean; function townSkill(skillId: number): boolean; From 111871ff5f80636716aa0eea875cec453d7a54ad Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:03:55 -0400 Subject: [PATCH 246/758] Create ChannelConfig.js --- .../libs/systems/channel/ChannelConfig.js | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 d2bs/kolbot/libs/systems/channel/ChannelConfig.js diff --git a/d2bs/kolbot/libs/systems/channel/ChannelConfig.js b/d2bs/kolbot/libs/systems/channel/ChannelConfig.js new file mode 100644 index 000000000..5c68fa231 --- /dev/null +++ b/d2bs/kolbot/libs/systems/channel/ChannelConfig.js @@ -0,0 +1,41 @@ +/** +* @filename ChannelConfig.js +* @author theBGuy +* @desc Configuration file for D2BotChannel system +* +*/ + +(function (module) { + const ChannelConfig = { + SkipMutedKey: true, + MutedKeyTrigger: "Your account has had all chat privileges suspended.", + JoinDelay: 10, // Seconds to wait between announcement and clicking join + JoinRetry: 5, // Amount of times to re-attempt joining game + // watch for whisper event instead? + FriendListQuery: 0, // Seconds between "/f l" retries. 0 = disable. To prevent spamming when using set time rand(80, 160) + /** + * @typedef {Object} GameInfo + * @property {string} game + * @property {string} password + * + * @type {GameInfo[]} + * @example + * Games: [ + * { game: "baal-", password: "" }, + * ], + */ + Games: [ + { game: "", password: "" }, + ], + /** + * Leaders in game character name, only use this if the leader is using announce in the chat. + * Can be an array or names ["somename", "somename2"] + * @type {string[]} + */ + Follow: [], + }; + + module.exports = { + ChannelConfig: ChannelConfig, + }; +})(module); From 1e4776ce7ef821dd8aa1d4c6b466ef3090241889 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:07:49 -0400 Subject: [PATCH 247/758] Update D2BotChannel.dbj - rebuild channel joining, simplified being able to follow games by player name vs game names, with checks for private vs public games - easier format for setting up game/pass info - new format for entry --- d2bs/kolbot/D2BotChannel.dbj | 734 +++++++++++++++++------------------ 1 file changed, 355 insertions(+), 379 deletions(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index dfc0a5450..f8d2a32ab 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -1,476 +1,445 @@ -/* eslint-disable max-len */ /** * @filename D2BotChannel.dbj * @author kolton, theBGuy * @desc Entry script for following bots using channels * +* @typedef {import("./sdk/globals")} */ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // D2BotChannel specific settings - for global settings see libs/starter/StarterConfig.js -Starter.Config.Games = [""]; // List of games to look for. Example: Games: ["some baal-", "chaos run-"], -Starter.Config.Passwords = [""]; // List of game passwords. Each array in Games array should have a matching element in Passwords. Use "" for blank pw. -Starter.Config.JoinDelay = 10; // Seconds to wait between announcement and clicking join -Starter.Config.JoinRetry = 5; // Amount of times to re-attempt joining game -Starter.Config.FriendListQuery = 0; // Seconds between "/f l" retries. 0 = disable. To prevent spamming when using set time rand(80, 160) -Starter.Config.SkipMutedKey = true; -Starter.Config.MutedKeyTrigger = "Your account has had all chat privileges suspended."; -Starter.Config.Follow = []; // leader's in game character name, only use this if the leader is using announce in the chat, can be an array or names ["somename", "somename2"] - +const { + ChannelConfig, +} = require("./libs/systems/channel/ChannelConfig"); +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds // Starter.Config.JoinChannel = ""; // Default channel. - +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // the only things we really need from these are their oog checks includeSystemLibs(); -let Controls = require("./modules/Control"); -let Overrides = require("./modules/Override"); - 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; -let channelTick = getTickCount(); -let fListTick = 0; -let retry = 0; -let badGames = []; -let lastText; -let joinInfo = { +/** @type {Set} */ +const badGames = new Set(); +const watch = { + player: "", + status: "", +}; +/** @type {Array<{ name: string, msg: string }>} */ +const messageQueue = []; +const joinInfo = { gameName: "", gamePass: "", oldGame: "", - inGame: false + inGame: false, + joinChannel: Starter.Config.JoinChannel, }; if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; } -new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { - if (mode === 3) { - Starter.isUp = (me.gameReady ? "yes" : "no"); - if (!me.gameReady) { - return; +/** + * @param {string} name + * @param {string} msg + * @returns {void} + */ +function ChannelChatHandler (name, msg) { + if (me.ingame) return; + if (/[joined|left] the channel/g.test(msg)) { + if (!watch.player && ChannelConfig.Follow.length === 0) return; + if (watch.player === name) { + watch.status = msg.includes("joined") ? "joined" : "left"; } - Starter.gameInfo.gameName = (me.gamename || ""); - Starter.gameInfo.gamePass = (me.gamepassword || ""); - } else { - orignal(mode, msg); + return; } -}).apply(); - -function locationAction (location) { - let i, n, string, text, regex, fullText, lines; - - MainSwitch: - switch (location) { - case sdk.game.locations.PreSplash: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - D2Bot.updateStatus("Lobby"); + messageQueue.push({ name: name, msg: msg }); +} - me.blockKeys = false; - Starter.loginRetry = 0; - !Starter.firstLogin && (Starter.firstLogin = true); - Controls.LobbyEnterChat.click(); +const locationAction = (function () { + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations, + parseControlText + } = require("./libs/oog/Locations"); + + const pollQueue = function (timeout = Time.seconds(30)) { + const preLen = messageQueue.length; + const start = getTickCount(); + while (getTickCount() - start < timeout) { + if (messageQueue.length > preLen) return true; + delay(100); + } + return messageQueue.length > preLen; + }; + + let lastText; + let channelTick = getTickCount(); + let fListTick = 0; + let retry = 0; + + locations.set(sdk.game.locations.Lobby, + function () { + D2Bot.updateStatus("Lobby"); + + me.blockKeys = false; + Starter.loginRetry = 0; + !Starter.firstLogin && (Starter.firstLogin = true); + Controls.LobbyEnterChat.click(); + } + ); + locations.set(sdk.game.locations.LobbyChat, + function () { + D2Bot.updateStatus("Lobby Chat"); + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + return; + } - break; - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby Chat"); + console.log("updating runs"); + D2Bot.updateRuns(); - if (Starter.inGame) { - if (AutoMule.outOfGameCheck() || TorchSystem.outOfGameCheck() || Gambling.outOfGameCheck() || CraftingSystem.outOfGameCheck()) { - break; + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + retry = 0; } - print("updating runs"); - D2Bot.updateRuns(); - - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; - retry = 0; - } - - // Muted key handler - fullText = ""; - lines = Controls.LobbyChat.getText(); - - if (!lines) { - break; - } + // Muted key handler + let lines = Controls.LobbyChat.getText(); + if (!lines) return; - fullText = lines.join(" ").replace(/\s+/g, " "); + let fullText = lines.join(" ").replace(/\s+/g, " "); - if (fullText.match(Starter.Config.MutedKeyTrigger.replace(/\s+/g, " "), "gi")) { - D2Bot.printToConsole(Starter.gameInfo.mpq + " is muted.", sdk.colors.D2Bot.Gold); + if (fullText.match(ChannelConfig.MutedKeyTrigger.replace(/\s+/g, " "), "gi")) { + D2Bot.printToConsole(Starter.gameInfo.mpq + " is muted.", sdk.colors.D2Bot.Gold); - ControlAction.mutedKey = true; + ControlAction.mutedKey = true; - if (Starter.Config.SkipMutedKey) { - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.stop(); + if (ChannelConfig.SkipMutedKey) { + if (Starter.gameInfo.switchKeys) { + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.stop(); + } } } - } - - if (!ControlAction.mutedKey && (!Starter.chatActionsDone || getTickCount() - channelTick >= 120e3)) { - if (Starter.Config.JoinChannel !== "") { - joinInfo.joinChannel = Starter.Config.JoinChannel; + if (!ControlAction.mutedKey && (!Starter.chatActionsDone || getTickCount() - channelTick >= 120e3)) { if (joinInfo.joinChannel) { if (ControlAction.joinChannel(joinInfo.joinChannel)) { Starter.useChat = true; } else { - print("Unable to join channel, chat messages disabled."); + console.warn("Unable to join channel, chat messages disabled."); Starter.useChat = false; } } - } - - if (Starter.Config.Follow.length > 0 && !Starter.channelNotify) { - Starter.sayMsg("/d2notify"); - lines = Controls.LobbyChat.getText(); - - if (!lines) { - break; - } - if (lines.some(line => line.match("notifications are disabled"))) { - Starter.sayMsg("/d2notify"); - lines = Controls.LobbyChat.getText(); + // Added !chatActionsDone condition to prevent spam + if (Starter.Config.FirstJoinMessage !== "" && !Starter.chatActionsDone) { + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + Starter.sayMsg(Starter.Config.FirstJoinMessage); + delay(500); } - if (lines.some(line => line.match("notifications are enabled"))) { - Starter.channelNotify = true; - } + Starter.chatActionsDone = true; + channelTick = getTickCount(); } - // Added !chatActionsDone condition to prevent spam - if (Starter.Config.FirstJoinMessage !== "" && !Starter.chatActionsDone) { - ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); - Starter.sayMsg(Starter.Config.FirstJoinMessage); - delay(500); - } - - Starter.chatActionsDone = true; - channelTick = getTickCount(); - } - - if (Starter.Config.FriendListQuery > 0 && getTickCount() - fListTick >= Starter.Config.FriendListQuery * 1000) { - say("/f l"); + StatusSwitch: + switch (Starter.lastGameStatus) { + case "pending": // Most likely FTJ (can't detect it directly) + let string = parseControlText(Controls.LobbyServerDown); + + switch (string) { + case getLocaleString(sdk.locale.text.DoNotMeetLevelReqForThisGame): + case getLocaleString(sdk.locale.text.HcCannotPlayWithSc): + case getLocaleString(sdk.locale.text.ScCannotPlayWithHc): + case getLocaleString(sdk.locale.text.CannotPlayInHellClassic): + case getLocaleString(sdk.locale.text.CannotPlayInHellXpac): + case getLocaleString(sdk.locale.text.CannotPlayInNightmareClassic): + case getLocaleString(sdk.locale.text.CannotPlayInNightmareXpac): + case getLocaleString(sdk.locale.text.NonLadderCannotPlayWithLadder): + case getLocaleString(sdk.locale.text.LadderCannotPlayWithNonLadder): + console.log(string); + retry = ChannelConfig.JoinRetry; + + break StatusSwitch; + } - fListTick = getTickCount(); - } + retry += 1; - switch (Starter.lastGameStatus) { - case "pending": // Most likely FTJ (can't detect it directly) - string = ""; - text = Controls.LobbyServerDown.getText(); + D2Bot.updateRuns(); - if (text) { - for (i = 0; i < text.length; i += 1) { - string += text[i]; + if (retry < ChannelConfig.JoinRetry) { + Controls.JoinGameWindow.click(); - if (i !== text.length - 1) { - string += " "; - } + return; } - // Didn't meet level restriction - if (string === getLocaleString(sdk.locale.text.DoNotMeetLevelReqForThisGame)) { - print(string); + break; + case "DNE": // Game didn't exist + retry += 1; - retry = Starter.Config.JoinRetry; + break; + case "FULL": // Game is full + retry = ChannelConfig.JoinRetry; - break; - } + break; } - retry += 1; - - D2Bot.updateRuns(); - - if (retry < Starter.Config.JoinRetry) { - Controls.JoinGameWindow.click(); + if (retry >= ChannelConfig.JoinRetry) { + D2Bot.printToConsole("Failed to join " + joinInfo.gameName + ". Aborting."); + badGames.add(joinInfo.gameName); - break MainSwitch; + Starter.lastGameStatus = "ready"; + joinInfo.oldGame = joinInfo.gameName; + retry = 0; } - break; - case "DNE": // Game didn't exist - retry += 1; - - break; - case "FULL": // Game is full - retry = Starter.Config.JoinRetry; - - break; - } - - if (retry >= Starter.Config.JoinRetry) { - D2Bot.printToConsole("Failed to join " + joinInfo.gameName + ". Aborting."); - badGames.push(joinInfo.gameName); - - Starter.lastGameStatus = "ready"; - joinInfo.oldGame = joinInfo.gameName; - retry = 0; - } - - fullText = ""; - lines = Controls.LobbyChat.getText(); - - if (!lines) { - break; - } - - fullText = lines.join(" ").replace(/\s+/g, " "); - - if (lastText === fullText) { - if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { - // we have a game and nothing else has changed since last announcement so go ahead and try joining again - Controls.JoinGameWindow.click(); - } - // nothing has changed since our last check so break - break; - } + if (ChannelConfig.FriendListQuery > 0 + && getTickCount() - fListTick >= Time.seconds(ChannelConfig.FriendListQuery)) { + say("/f l"); + fListTick = getTickCount(); + pollQueue(); + // we should also include list of friends to actually follow rather than just game names + for (let chat of messageQueue) { + let { msg } = chat; + // if its not a message from us, ignore it. When we type /f l, the who is us + // if (!name.split("*")[0] !== me.charname) continue; + let match = new RegExp("^.*?(\\w+).*in the game\\s+(\\w+)(\\s+\\(private\\))?\\.", "gm").exec(msg); + if (match) { + let [,, gameName, _private] = match; + // check if this is a game we are looking for - what about just following the friend in all games that are not private? + for (let gInfo of ChannelConfig.Games) { + if (gameName.match(gInfo.game, "gi") + && !String.isEqual(gameName, joinInfo.oldGame) + && !badGames.has(gameName)) { + // check if the game is private + if (_private && gInfo.password) { + // we can't join if the game is private and we don't have a password + if (!gInfo.password) continue; + joinInfo.gamePass = gInfo.password; + } + joinInfo.gameName = gameName; - lastText = fullText; - - // we are set to follow a specific leader, lets look for their messages - if (Starter.Config.Follow.length > 0) { - let newLines = lines - .map((line, index) => { - if (index > 0 - // eslint-disable-next-line no-useless-escape - && !line.match(/\<.*\>/, "gi") - && !line.match(" has left", "gi") - && !line.match(" has joined", "gi")) { - line = lines[index - 1] + line; + // we have a match so lets try joining + Controls.JoinGameWindow.click(); + + break; + } + } } - return line; - }) - .filter(line => line.match("Next game is", "gi")); - - if (newLines.length === 0) { - break; + } } - for (n = 0; n < Starter.Config.Follow.length; n++) { - let test = []; - - newLines.forEach(element => { - if (element.includes(Starter.Config.Follow[n])) { - test.push(element); - } - }); - - if (test.length === 0) continue; - test.reverse(); + fullText = ""; + lines = Controls.LobbyChat.getText(); + if (!lines) return; - for (let msg = 0; msg < test.length; msg++) { - let checkName = test[msg].toString(); - let hasPass = checkName.indexOf("/") > -1; + fullText = lines.join(" ").replace(/\s+/g, " "); - let gName = (checkName.slice(checkName.indexOf("is ") + 3, (hasPass ? checkName.indexOf("/") : undefined)) || "").trim(); - if (gName.length > 15) continue; // invalid game name - let gPass = (hasPass ? checkName.slice(checkName.lastIndexOf("/") + 1) : ""); - if (gPass.length > 15) continue; // invalid game pass - joinInfo.gameName = gName; - joinInfo.gamePass = gPass; - console.debug(joinInfo.gameName + " " + joinInfo.gamePass); - - if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { - // wait until leader has left the channel - if (Starter.Config.JoinDelay && Starter.channelNotify) { - let wTick = getTickCount(); - - while (true) { - lines = Controls.LobbyChat.getText(); - - if (!lines || (getTickCount() - wTick > Time.minutes(1))) { - break; - } - - if (lines.some(line => line.match(Starter.Config.Follow[n] + " has left"))) { - break; - } + if (lastText === fullText) { + if (joinInfo.gameName + && joinInfo.gameName !== joinInfo.oldGame + && !badGames.has(joinInfo.gameName)) { + // we have a game and nothing else has changed since last announcement so go ahead and try joining again + Controls.JoinGameWindow.click(); + } + // nothing has changed since our last check so break + return; + } - delay(2000); + lastText = fullText; + + while (messageQueue.length > 0) { + const chat = messageQueue.shift(); + // lets determine if this is a player message or internal message + if (!chat.name || chat.name.split("*")[0] === me.charname) continue; + // is this a game announcement? + if (!chat.msg.toLowerCase().includes("next game is")) continue; + // capture the game name and password if there is one + let match = new RegExp(/^Next game is\s+([a-zA-Z0-9_-]+)(?:\/\/(\w+))?$/gm).exec(chat.msg); + if (!match) continue; + // extract capture groups from regex + let [, gameName, gamePass] = match; + + // double check that this is a valid game name + if (gameName.length > 15 || badGames.has(gameName)) continue; + + // handle following player names + if (ChannelConfig.Follow.length > 0) { + for (let follow of ChannelConfig.Follow) { + if (chat.name.split("*")[0].toLowerCase() === follow.toLowerCase()) { + // alright so this is a game we want to follow, lets set the game name and password + console.log("Joining game: " + gameName + "//" + gamePass); + // wait for the player to leave the channel + watch.player = chat.name; + watch.status = "channel"; + let timeout = getTickCount() + Time.minutes(1); + console.log("Waiting for " + watch.player + " to leave the channel"); + + while (watch.status !== "left" && getTickCount() < timeout) { + delay(100); } - } - Controls.JoinGameWindow.click(); - - break; + joinInfo.gameName = gameName; + joinInfo.gamePass = gamePass; + // we have a game and nothing else has changed since last announcement so go ahead and try joining again + Controls.JoinGameWindow.click(); + return; + } } } - } - } - - // we are just trying to follow game names - for (let n = 0; n < Starter.Config.Games.length; n += 1) { - if (Starter.Config.Games[n] === "") continue; - regex = new RegExp("\\W+" + Starter.Config.Games[n].toLowerCase() + "\\d+", "gi"); - joinInfo.gameName = fullText.match(regex); - if (joinInfo.gameName) { - // use last match and trim it - joinInfo.gameName = joinInfo.gameName[joinInfo.gameName.length - 1].toString().replace(/^\W*/, ""); - joinInfo.gamePass = Starter.Config.Passwords[n] || ""; - - if (joinInfo.gameName && joinInfo.gameName !== joinInfo.oldGame && badGames.indexOf(joinInfo.gameName) === -1) { + // check if this is a game we want to join + for (let { game } of ChannelConfig.Games) { + if (game === "") continue; + if (!gameName.toLowerCase().includes(game.toLowerCase())) continue; + // alright so this gamename is one we were looking for, lets confirm it is not a bad game + if (badGames.has(gameName)) continue; + // alright so this is a game we want to join, lets set the game name and password + console.log("Joining game: " + gameName + "//" + gamePass); + // wait for the player to leave the channel + watch.player = chat.name; + watch.status = "channel"; + let timeout = getTickCount() + Time.minutes(1); + console.log("Waiting for " + watch.player + " to leave the channel"); + + while (watch.status !== "left" && getTickCount() < timeout) { + delay(100); + } + if (watch.status !== "left") continue; + // player who announced has left chat so lets set the game name and password and try to join + joinInfo.gameName = gameName; + joinInfo.gamePass = gamePass || ""; + // lets click the join game button Controls.JoinGameWindow.click(); - - break; + // break out of the main loop + return; } } + // maybe poll the queue here? Or delay until the next fl query tick if we are using it } - - break; - case sdk.game.locations.WaitingInLine: - case sdk.game.locations.CreateGame: - Controls.CancelCreateGame.click(); - - break; - case sdk.game.locations.JoinGame: - if (joinInfo.oldGame === joinInfo.gameName || badGames.includes(joinInfo.gameName)) { - Controls.CancelJoinGame.click(); + ); + addLocations([sdk.game.locations.WaitingInLine, sdk.game.locations.CreateGame], + function () { + Controls.CancelCreateGame.click(); } + ); + locations.set(sdk.game.locations.JoinGame, + function (location) { + if (joinInfo.oldGame === joinInfo.gameName || badGames.has(joinInfo.gameName)) { + Controls.CancelJoinGame.click(); + } - D2Bot.updateStatus("Join Game"); + D2Bot.updateStatus("Join Game"); - if (joinInfo.gameName !== "") { - print("ÿc2Joining ÿc0" + joinInfo.gameName); - Controls.JoinGameName.setText(joinInfo.gameName); - Controls.JoinGamePass.setText(joinInfo.gamePass); + if (joinInfo.gameName !== "") { + console.log("ÿc2Joining ÿc0" + joinInfo.gameName); + Controls.JoinGameName.setText(joinInfo.gameName); + Controls.JoinGamePass.setText(joinInfo.gamePass); - if (Starter.Config.AnnounceGames && Starter.Config.AnnounceMessage) { - Starter.sayMsg(Starter.Config.AnnounceMessage + " " + joinInfo.gameName); - } + if (Starter.Config.AnnounceGames && Starter.Config.AnnounceMessage) { + Starter.sayMsg(Starter.Config.AnnounceMessage + " " + joinInfo.gameName); + } - // Only delay on first join - the rest is handled by GameDoesNotExistTimeout. Any other case is instant fail (ie. full game). - if (retry === 0 || Starter.lastGameStatus === "pending") { - ControlAction.timeoutDelay("Join Game Delay", Starter.Config.JoinDelay * 1e3); - } + // Only delay on first join - the rest is handled by GameDoesNotExistTimeout. Any other case is instant fail (ie. full game). + if (retry === 0 || Starter.lastGameStatus === "pending") { + ControlAction.timeoutDelay("Join Game Delay", ChannelConfig.JoinDelay * 1e3); + } - me.blockMouse = true; + me.blockMouse = true; - Controls.JoinGame.click(); + Controls.JoinGame.click(); - me.blockMouse = false; - Starter.lastGameStatus = "pending"; + me.blockMouse = false; + Starter.lastGameStatus = "pending"; - Starter.locationTimeout(5000, location); + Starter.locationTimeout(5000, location); + } } - - break; - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - case sdk.game.locations.CharSelect: - case sdk.game.locations.SplashScreen: - Starter.LocationEvents.login(); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - case sdk.game.locations.CharSelectNoChars: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Starter.LocationEvents.gameDoesNotExist(); - Starter.lastGameStatus = "DNE"; - - break; - case sdk.game.locations.GameIsFull: - badGames.push(joinInfo.gameName); - Controls.JoinGameWindow.click(); - Controls.CancelCreateGame.click(); - Starter.lastGameStatus = "FULL"; - - break; - case sdk.game.locations.OtherMultiplayer: - Starter.LocationEvents.otherMultiplayerSelect(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); + ); + locations.set(sdk.game.locations.SelectDifficultySP, + function () { + hideConsole(); + sendKey(sdk.keys.Escape); } + ); + locations.set(sdk.game.locations.GameNameExists, + function () { + Starter.LocationEvents.openJoinGameWindow(); + } + ); + locations.set(sdk.game.locations.GameDoesNotExist, + function () { + Starter.LocationEvents.gameDoesNotExist(); + Starter.lastGameStatus = "DNE"; + } + ); + locations.set(sdk.game.locations.GameIsFull, + function () { + badGames.add(joinInfo.gameName); + Controls.JoinGameWindow.click(); + Controls.CancelCreateGame.click(); + Starter.lastGameStatus = "FULL"; + } + ); + locations.set(sdk.game.locations.TcpIp, + function () { + Controls.TcpIpCancel.click(); + } + ); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; +})(); + +function main () { + const Overrides = require("./modules/Override"); + new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { + if (mode === 3) { + Starter.isUp = (me.gameReady ? "yes" : "no"); + if (!me.gameReady) { + return; + } + Starter.gameInfo.gameName = (me.gamename || ""); + Starter.gameInfo.gamePass = (me.gamepassword || ""); + } else { + orignal(mode, msg); + } + }).apply(); - break; - } -} - -function main() { addEventListener("copydata", Starter.receiveCopyData); addEventListener("scriptmsg", Starter.scriptMsgEvent); + addEventListener("chatmsg", ChannelChatHandler); while (!Starter.handle) { delay(100); @@ -490,7 +459,11 @@ function main() { if (Starter.gameInfo.error) { if (!!DataFile.getStats().debugInfo) { Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, + sdk.colors.D2Bot.Gray + ); } ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); @@ -501,7 +474,7 @@ function main() { while (!Object.keys(Starter.profileInfo).length) { D2Bot.getProfile(); - print("Getting Profile"); + console.log("Getting Profile"); delay(500); } @@ -513,9 +486,9 @@ function main() { joinInfo.inGame = true; if (!Starter.inGame) { - print("Updating Status"); + console.log("Updating Status"); - badGames.push(joinInfo.gameName); + badGames.add(joinInfo.gameName); joinInfo.oldGame = me.gamename; Starter.lastGameStatus = "ingame"; Starter.inGame = true; @@ -524,7 +497,10 @@ function main() { DataFile.updateStats("runs", Starter.gameCount); } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + D2Bot.updateStatus( + me.charname + " (" + me.charlvl + ") | Game: " + me.gamename + + Starter.timer(Starter.gameStart) + ); } delay(1000); @@ -532,7 +508,7 @@ function main() { joinInfo.inGame = false; - locationAction(getLocation()); + locationAction.run(getLocation()); delay(1000); } } From 9659a2949fdba548761c31f6ab8f205857466ce6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 17:44:04 -0400 Subject: [PATCH 248/758] Update Locations.js - add missing parameter --- d2bs/kolbot/libs/oog/Locations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index e19e4fc93..cea50a536 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -133,7 +133,7 @@ } ], [sdk.game.locations.CreateGame, - function () { + function (location) { D2Bot.updateStatus("Creating Game"); if (typeof Starter.Config.CharacterDifference === "number") { From 300fe6260d43589d7ba8e32fc99468706d92ab95 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 18:01:16 -0400 Subject: [PATCH 249/758] Update D2BotGameAction.dbj - convert gameaction to new format --- d2bs/kolbot/D2BotGameAction.dbj | 467 +++++++++++++++----------------- 1 file changed, 217 insertions(+), 250 deletions(-) diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index 55240e077..f242808af 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -1,9 +1,11 @@ -/* eslint-disable max-len */ /** * @filename D2BotGameAction.dbj * @author noah, theBGuy * @desc Entry script for limedrop * +* @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/mulelogger/MuleLogger")} +* @typedef {import("./libs/systems/gameaction/GameAction")} */ include("critical.js"); // required @@ -21,8 +23,7 @@ includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -let Controls = require("./modules/Control"); -let Overrides = require("./modules/Override"); +const Overrides = require("./modules/Override"); if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { Starter.firstRun = true; @@ -32,301 +33,263 @@ let tag, charList; let ftj = 0; let creatingActions = ["doMule"]; -new Overrides.Override(Starter, Starter.receiveCopyData, function(orignal, mode, msg) { +new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode, msg) { if (mode === 3) return; if (mode === 1638) { - print("Recieved Profile Info"); + console.log("Recieved Profile Info"); tag = JSON.parse(msg).Tag; } orignal(mode, msg); }).apply(); -function locationAction (location) { - let i, string, text, currChar; +const locationAction = (function () { + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations, + parseControlText + } = require("./libs/oog/Locations"); + + addLocations([sdk.game.locations.Lobby, sdk.game.locations.LobbyChat], + function () { + D2Bot.updateStatus("Lobby"); + + if (Starter.inGame) { + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay( + "Min game time wait", + Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount() + ); + } - switch (location) { - case sdk.game.locations.PreSplash: - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); + console.log("updating runs"); + D2Bot.updateRuns(); + delay(1000); - if (Starter.inGame) { - if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); - } + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; - print("updating runs"); - D2Bot.updateRuns(); - delay(1000); + Controls.LobbyQuit.click(); - Starter.gameCount += 1; - Starter.lastGameStatus = "ready"; - Starter.inGame = false; + return; + } - Controls.LobbyQuit.click(); + // a game name was specified + if (GameAction.gameInfo() !== null) { + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to join game!"); + D2Bot.stop(me.profile, true); + return; + } + + if (!Starter.LocationEvents.openCreateGameWindow()) { + return; + } - break; - } + Starter.LocationEvents.openJoinGameWindow(); + } else { + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to create game!"); + D2Bot.stop(me.profile, true); + return; + } - // a game name was specified - if (GameAction.gameInfo() !== null) { - if (++ftj > 5) { - GameAction.update("done", "GameAction failed to join game!"); - D2Bot.stop(me.profile, true); - break; - } - - if (!Starter.LocationEvents.openCreateGameWindow()) { - break; + Starter.LocationEvents.openCreateGameWindow(); } - - Starter.LocationEvents.openJoinGameWindow(); - } else { - if (++ftj > 5) { + } + ); + locations.set(sdk.game.locations.CreateGame, + function (location) { + if (creatingActions.indexOf(JSON.parse(tag).action) < 0) { GameAction.update("done", "GameAction failed to create game!"); D2Bot.stop(me.profile, true); - break; + return; } + + D2Bot.updateStatus("Creating Game"); - Starter.LocationEvents.openCreateGameWindow(); - } - - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); - - break; - case sdk.game.locations.CreateGame: - if (creatingActions.indexOf(JSON.parse(tag).action) < 0) { - GameAction.update("done", "GameAction failed to create game!"); - D2Bot.stop(me.profile, true); - break; - } - - D2Bot.updateStatus("Creating Game"); - - // remove level restriction - Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); + // remove level restriction + if (Controls.CharacterDifference.disabled === 5) { + Controls.CharacterDifferenceButton.click(); + } - // Max number of players - Controls.MaxPlayerCount.setText("8"); + // Max number of players + Controls.MaxPlayerCount.setText("8"); - if (Starter.gameCount >= 99) { - Starter.gameCount = 1; + if (Starter.gameCount >= 99) { + Starter.gameCount = 1; - DataFile.updateStats("runs", Starter.gameCount); - } + DataFile.updateStats("runs", Starter.gameCount); + } - if (Starter.lastGameStatus === "pending") { - D2Bot.printToConsole("Failed to create game"); + if (Starter.lastGameStatus === "pending") { + D2Bot.printToConsole("Failed to create game"); - Starter.gameCount += 1; - } + Starter.gameCount += 1; + } - ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); - ControlAction.createGame(Starter.gameInfo.gameName + Starter.gameCount, Starter.gameInfo.gamePass, 0); - Starter.locationTimeout(5000, location); + ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); + ControlAction.createGame(Starter.gameInfo.gameName + Starter.gameCount, Starter.gameInfo.gamePass, 0); + Starter.locationTimeout(5000, location); - Starter.lastGameStatus = "pending"; - - break; - case sdk.game.locations.JoinGame: // Join Game - D2Bot.updateStatus("Join Game"); - let joinInfo = GameAction.gameInfo(); - - joinGame(joinInfo.gameName, joinInfo.gamePass); - Starter.locationTimeout(5000, location); - - break; - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - Controls.LobbyChannelCancel.click(); - - break; - case sdk.game.locations.MainMenu: // Main Menu - case sdk.game.locations.Login: // Login - case sdk.game.locations.SplashScreen: // D2 Splash - !charList && (charList = GameAction.getCharacters()); - - // last char in list - if (!charList || !charList.length) { - GameAction.update("done", "GameAction has completed task"); - D2Bot.stop(me.profile, true); - delay(5000); - break; + Starter.lastGameStatus = "pending"; } - - ControlAction.loginAccount(GameAction.getLogin()); - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.CharSelect: // Character Select - // Reset ftj counter - ftj = 0; - - // Single Player screen fix - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.BottomLeftExit.click(); - - break; + ); + locations.set(sdk.game.locations.JoinGame, + function (location) { + D2Bot.updateStatus("Join Game"); + let joinInfo = GameAction.gameInfo(); + + joinGame(joinInfo.gameName, joinInfo.gamePass); + Starter.locationTimeout(5000, location); } - - // last char in list - if (!charList || !charList.length) { - GameAction.update("done", "GameAction has completed task"); - D2Bot.stop(me.profile, true); - delay(5000); - break; + ); + addLocations([sdk.game.locations.Ladder, sdk.game.locations.ChannelList], + function () { + Controls.LobbyChannelCancel.click(); } + ); + addLocations([sdk.game.locations.MainMenu, sdk.game.locations.Login, sdk.game.locations.SplashScreen], + function () { + !charList && (charList = GameAction.getCharacters()); - // "" empty string means all characters - if (charList[0].length === 0) { - charList = ControlAction.getCharacters(); - - // empty account + // last char in list if (!charList || !charList.length) { - GameAction.update("done", "Account has no chars!"); + GameAction.update("done", "GameAction has completed task"); D2Bot.stop(me.profile, true); delay(5000); - break; + return; } - } - - currChar = charList.shift(); - print("ÿc4Game Actionÿc2: Login character: " + currChar); - ControlAction.loginCharacter({ charName: currChar }); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.NewCharSelected: // New Character - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.ServerDown: // Server Down - not much to do but wait.. - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: // Lobby - Game Name Exists - if (++ftj > 5) { - GameAction.update("done", "GameAction failed to create game!"); - D2Bot.stop(me.profile, true); - break; + ControlAction.loginAccount(GameAction.getLogin()); } - ControlAction.timeoutDelay("Game Already Exists", 5e3); - Controls.CreateGameWindow.click(); - - break; - case sdk.game.locations.GatewaySelect: // Gateway Select - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: // Lobby - Game Does Not Exist - if (++ftj > 5) { - GameAction.update("done", "GameAction failed to join game!"); - D2Bot.stop(me.profile, true); - break; - } - ControlAction.timeoutDelay("Game Doesn't Exist", 5e3); - Controls.JoinGameWindow.click(); + ); + locations.set(sdk.game.locations.CharSelect, + function () { + // Reset ftj counter + ftj = 0; + + // Single Player screen fix + if (getLocation() === sdk.game.locations.CharSelect + && !Controls.CharSelectCurrentRealm.control) { + Controls.BottomLeftExit.click(); - break; - case sdk.game.locations.GameIsFull: // Game is full - D2Bot.printToConsole("Game is full"); - Starter.lastGameStatus = "ready"; - delay(500); - Controls.JoinGameWindow.click(); + return; + } - break; - case sdk.game.locations.CharSelectNoChars: // Empty character screen - // TODO: see if this is needed in case 12 too - string = ""; - text = Controls.CharSelectError.getText(); + // last char in list + if (!charList || !charList.length) { + GameAction.update("done", "GameAction has completed task"); + D2Bot.stop(me.profile, true); + delay(5000); + return; + } - if (text) { - for (i = 0; i < text.length; i += 1) { - string += text[i]; + // "" empty string means all characters + if (charList[0].length === 0) { + charList = ControlAction.getCharacters(); - if (i !== text.length - 1) { - string += " "; + // empty account + if (!charList || !charList.length) { + GameAction.update("done", "Account has no chars!"); + D2Bot.stop(me.profile, true); + delay(5000); + return; } } - if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { // CDKey disabled from realm play - D2Bot.updateStatus("Realm Disabled CDKey"); - D2Bot.printToConsole("Realm Disabled CDKey: " + Starter.gameInfo.mpq, sdk.colors.D2Bot.Gold); - D2Bot.CDKeyDisabled(); + let currChar = charList.shift(); - if (Starter.gameInfo.switchKeys) { - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - GameAction.update("done", "GameAction has failed in location 42"); - D2Bot.stop(me.profile, true); - } + console.log("ÿc4Game Actionÿc2: Login character: " + currChar); + ControlAction.loginCharacter({ charName: currChar }); + } + ); + locations.set(sdk.game.locations.SelectDifficultySP, + function () { + hideConsole(); + sendKey(sdk.keys.Escape); + } + ); + locations.set(sdk.game.locations.GameNameExists, + function () { + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to create game!"); + D2Bot.stop(me.profile, true); + return; } + ControlAction.timeoutDelay("Game Already Exists", 5e3); + Controls.CreateGameWindow.click(); } - - if (!Starter.locationTimeout(5000, location)) { - GameAction.update("done", "Account has no chars! location 42"); - D2Bot.stop(me.profile, true); + ); + locations.set(sdk.game.locations.GameDoesNotExist, + function () { + if (++ftj > 5) { + GameAction.update("done", "GameAction failed to join game!"); + D2Bot.stop(me.profile, true); + return; + } + ControlAction.timeoutDelay("Game Doesn't Exist", 5e3); + Controls.JoinGameWindow.click(); } - - break; - case sdk.game.locations.OtherMultiplayer: - Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined) { - D2Bot.printToConsole("Unhandled location " + location); + ); + locations.set(sdk.game.locations.GameIsFull, + function () { + D2Bot.printToConsole("Game is full"); + Starter.lastGameStatus = "ready"; delay(500); - D2Bot.restart(); + Controls.JoinGameWindow.click(); } + ); + locations.set(sdk.game.locations.CharSelectNoChars, + function (location) { + // TODO: see if this is needed in case 12 too + let string = parseControlText(Controls.CharSelectError); + + if (string) { + if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { // CDKey disabled from realm play + 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 { + GameAction.update("done", "GameAction has failed in location 42"); + D2Bot.stop(me.profile, true); + } + } + } - break; - } -} + if (!Starter.locationTimeout(5000, location)) { + GameAction.update("done", "Account has no chars! location 42"); + D2Bot.stop(me.profile, true); + } + } + ); + locations.set(sdk.game.locations.TcpIp, + function () { + Controls.TcpIpCancel.click(); + } + ); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; +})(); function main () { addEventListener("copydata", Starter.receiveCopyData); @@ -366,7 +329,11 @@ function main () { if (!!DataFile.getStats().debugInfo) { Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; - D2Bot.printToConsole("Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, sdk.colors.D2Bot.Gray); + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, + sdk.colors.D2Bot.Gray + ); } ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); @@ -381,7 +348,7 @@ function main () { // returns false when switching acts so we can't use while if (me.gameReady) { if (!Starter.inGame) { - print("Updating Status"); + console.log("Updating Status"); D2Bot.updateStatus("Game: " + me.gamename); Starter.lastGameStatus = "ingame"; From 992d613d77bc0960dda925d12c1974369a7afca9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 18:40:20 -0400 Subject: [PATCH 250/758] Update D2BotChannel.dbj - safety check using `/whois` command when waiting for player to leave the channel --- d2bs/kolbot/D2BotChannel.dbj | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index f8d2a32ab..7f95bef4a 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -323,7 +323,24 @@ const locationAction = (function () { while (watch.status !== "left" && getTickCount() < timeout) { delay(100); } - if (watch.status !== "left") continue; + if (watch.status !== "left") { + let pName = watch.player.split("*")[0]; + say("/whois " + pName); + pollQueue(); + if (messageQueue.length > 0) { + let inGame = messageQueue + .filter(function (chat) { + return chat.msg.includes(pName); + }) + .some(function (chat) { + return new RegExp(/in .+? game/gi).test(chat.msg); + }); + if (!inGame) { + console.log("Player left channel, but is not in game. Skipping."); + continue; + } + } + } // player who announced has left chat so lets set the game name and password and try to join joinInfo.gameName = gameName; joinInfo.gamePass = gamePass || ""; From 8b91a98f7f91b6b41c1f8e6bdd4a5a6f9f1edc82 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 19:04:39 -0400 Subject: [PATCH 251/758] Update D2BotChannel.dbj - channelWatcher map to keep track of everyone joining/leaving the channel. Noticed occasionally the bot got hungup while waiting because the game maker left as soon as they announced game so we were late to assigning their name to the watch variable --- d2bs/kolbot/D2BotChannel.dbj | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 7f95bef4a..29c6f69cf 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -34,6 +34,8 @@ const watch = { }; /** @type {Array<{ name: string, msg: string }>} */ const messageQueue = []; +/** @type {Map} */ +const channelWatcher = new Map(); const joinInfo = { gameName: "", gamePass: "", @@ -54,10 +56,7 @@ if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { function ChannelChatHandler (name, msg) { if (me.ingame) return; if (/[joined|left] the channel/g.test(msg)) { - if (!watch.player && ChannelConfig.Follow.length === 0) return; - if (watch.player === name) { - watch.status = msg.includes("joined") ? "joined" : "left"; - } + channelWatcher.set(name, msg.includes("joined") ? "joined" : "left"); return; } messageQueue.push({ name: name, msg: msg }); @@ -289,11 +288,11 @@ const locationAction = (function () { console.log("Joining game: " + gameName + "//" + gamePass); // wait for the player to leave the channel watch.player = chat.name; - watch.status = "channel"; let timeout = getTickCount() + Time.minutes(1); console.log("Waiting for " + watch.player + " to leave the channel"); - - while (watch.status !== "left" && getTickCount() < timeout) { + + while (channelWatcher.get(watch.player) !== "left" + && getTickCount() < timeout) { delay(100); } @@ -316,14 +315,14 @@ const locationAction = (function () { console.log("Joining game: " + gameName + "//" + gamePass); // wait for the player to leave the channel watch.player = chat.name; - watch.status = "channel"; let timeout = getTickCount() + Time.minutes(1); console.log("Waiting for " + watch.player + " to leave the channel"); - while (watch.status !== "left" && getTickCount() < timeout) { + while (channelWatcher.get(watch.player) !== "left" + && getTickCount() < timeout) { delay(100); } - if (watch.status !== "left") { + if (channelWatcher.get(watch.player) !== "left") { let pName = watch.player.split("*")[0]; say("/whois " + pName); pollQueue(); @@ -506,6 +505,7 @@ function main () { console.log("Updating Status"); badGames.add(joinInfo.gameName); + channelWatcher.clear(); joinInfo.oldGame = me.gamename; Starter.lastGameStatus = "ingame"; Starter.inGame = true; From 6c70de8c4fa0758ef5cb979e2717d7d7d0469243 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 23:52:39 -0400 Subject: [PATCH 252/758] automule configs -> `automule/config` - move the files into sub directory, breaks uniformity I was having with the other systems folders but makes more sense to me --- d2bs/kolbot/libs/systems/automule/{ => config}/MuleConfig.js | 0 d2bs/kolbot/libs/systems/automule/{ => config}/TorchAnniMules.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename d2bs/kolbot/libs/systems/automule/{ => config}/MuleConfig.js (100%) rename d2bs/kolbot/libs/systems/automule/{ => config}/TorchAnniMules.js (100%) diff --git a/d2bs/kolbot/libs/systems/automule/MuleConfig.js b/d2bs/kolbot/libs/systems/automule/config/MuleConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/automule/MuleConfig.js rename to d2bs/kolbot/libs/systems/automule/config/MuleConfig.js diff --git a/d2bs/kolbot/libs/systems/automule/TorchAnniMules.js b/d2bs/kolbot/libs/systems/automule/config/TorchAnniMules.js similarity index 100% rename from d2bs/kolbot/libs/systems/automule/TorchAnniMules.js rename to d2bs/kolbot/libs/systems/automule/config/TorchAnniMules.js From cf5f9078e696e71cf80c6ebddd1c65addf38d07c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 23:54:19 -0400 Subject: [PATCH 253/758] move mule specific methods into own file - breakup AutoMule.js so the master/muler methods are not mixed. Makes handling the mules in game logic stuff easier --- d2bs/kolbot/libs/systems/automule/AutoMule.js | 272 +++------- d2bs/kolbot/libs/systems/automule/Mule.js | 482 ++++++++++++++++++ 2 files changed, 563 insertions(+), 191 deletions(-) create mode 100644 d2bs/kolbot/libs/systems/automule/Mule.js diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index d4853030a..b40e239ad 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -10,15 +10,20 @@ const AutoMule = { /** @type {Object.} */ - Mules: Object.assign({}, require("./MuleConfig", null, false)), + Mules: Object.assign({}, + require("./config/MuleConfig", null, false) + ), /** @type {Object.} */ - TorchAnniMules: Object.assign({}, require("./TorchAnniMules", null, false)), + TorchAnniMules: Object.assign({}, + require("./config/TorchAnniMules", null, false) + ), inGame: false, check: false, torchAnniCheck: false, - gids: [], + gids: new Set(), + baseGids: new Set(), // ################################## // /* ##### Master/Muler Functions ##### */ @@ -33,8 +38,8 @@ const AutoMule = { for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { - for (let j = 0; j < this.Mules[i].enabledProfiles.length; j += 1) { - if (String.isEqual(this.Mules[i].enabledProfiles[j], me.profile)) { + for (let profile of this.Mules[i].enabledProfiles) { + if (String.isEqual(profile, "all") || String.isEqual(profile, me.profile)) { !info && (info = {}); info.muleInfo = this.Mules[i]; @@ -46,8 +51,8 @@ const AutoMule = { for (let i in this.TorchAnniMules) { if (this.TorchAnniMules.hasOwnProperty(i)) { - for (let j = 0; j < this.TorchAnniMules[i].enabledProfiles.length; j += 1) { - if (String.isEqual(this.TorchAnniMules[i].enabledProfiles[j], me.profile)) { + for (let profile of this.TorchAnniMules[i].enabledProfiles) { + if (String.isEqual(profile, "all") || String.isEqual(profile, me.profile)) { !info && (info = {}); info.torchMuleInfo = this.TorchAnniMules[i]; @@ -106,7 +111,7 @@ const AutoMule = { let muleObj = this.getMule(); if (!muleObj) return false; - function muleCheckEvent(mode, msg) { + function muleCheckEvent (mode, msg) { mode === 10 && (muleInfo = JSON.parse(msg)); } @@ -124,9 +129,15 @@ const AutoMule = { D2Bot.printToConsole("Starting mule.", sdk.colors.D2Bot.DarkGold); D2Bot.start(muleObj.muleProfile); } else { - D2Bot.printToConsole("Starting " + (this.torchAnniCheck === 2 ? "anni " : this.torchAnniCheck === 1 ? "torch " : "") + "mule profile: " + muleObj.muleProfile, sdk.colors.D2Bot.DarkGold); + D2Bot.printToConsole( + "Starting " + (this.torchAnniCheck === 2 ? "anni " : this.torchAnniCheck === 1 ? "torch " : "") + + "mule profile: " + muleObj.muleProfile, + sdk.colors.D2Bot.DarkGold + ); } + const mulePayload = JSON.stringify({ profile: me.profile, mode: this.torchAnniCheck || 0 }); + MainLoop: while (true) { // Set status to ready if using continuous mule with no response check @@ -134,7 +145,7 @@ const AutoMule = { muleInfo.status = "ready"; // If nothing received our copy data start the mule profile - } else if (!sendCopyData(null, muleObj.muleProfile, 10, JSON.stringify({ profile: me.profile, mode: this.torchAnniCheck || 0 })) && !muleObj.continuousMule) { + } else if (!sendCopyData(null, muleObj.muleProfile, 10, mulePayload) && !muleObj.continuousMule) { // if the mule profile isn't already running and there is a profile to be stopped, stop it before starting the mule profile if (!stopCheck && muleObj.stopProfile && !String.isEqual(me.profile, muleObj.stopProfile)) { D2Bot.stop(muleObj.stopProfile, muleObj.stopProfileKeyRelease); @@ -149,7 +160,8 @@ const AutoMule = { switch (muleInfo.status) { case "loading": - if (!muleObj.continuousMule && !stopCheck && muleObj.stopProfile && !String.isEqual(me.profile, muleObj.stopProfile)) { + if (!muleObj.continuousMule && !stopCheck && muleObj.stopProfile + && !String.isEqual(me.profile, muleObj.stopProfile)) { D2Bot.stop(muleObj.stopProfile, muleObj.stopProfileKeyRelease); stopCheck = true; @@ -335,7 +347,11 @@ const AutoMule = { sendCopyData(null, muleObj.muleProfile, 11, "begin"); } - let gameType = this.torchAnniCheck === 2 ? " anni" : this.torchAnniCheck === 1 ? " torch" : ""; + let gameType = this.torchAnniCheck === 2 + ? " anni" + : this.torchAnniCheck === 1 + ? " torch" + : ""; print("ÿc4AutoMuleÿc0: In" + gameType + " mule game."); D2Bot.updateStatus("AutoMule: In" + gameType + " mule game."); @@ -410,14 +426,16 @@ const AutoMule = { if (item) { do { // check if the items we dropped are on the ground still - if (getDistance(me, item) < 20 && item.onGroundOrDropping && AutoMule.gids.includes(item.gid)) { + if (getDistance(me, item) < 20 + && item.onGroundOrDropping + && AutoMule.gids.has(item.gid)) { return false; } } while (item.getNext()); } // we are finished so reset gid list - AutoMule.gids.length = 0; + AutoMule.gids.clear(); return true; }, @@ -456,7 +474,7 @@ const AutoMule = { let items = (this.getMuleItems() || []); if (items.length === 0) return false; - AutoMule.gids = items.map(i => i.gid); + items.forEach(item => AutoMule.gids.add(item.gid)); D2Bot.printToConsole("AutoMule: Transfering " + items.length + " items.", sdk.colors.D2Bot.DarkGold); D2Bot.printToConsole("AutoMule: " + JSON.stringify(items.map(i => i.prettyPrint)), sdk.colors.D2Bot.DarkGold); @@ -509,17 +527,26 @@ const AutoMule = { const muleOrphans = !!(info.muleInfo.hasOwnProperty("muleOrphans") && info.muleInfo.muleOrphans); - /** - * @param {ItemUnit} item - */ - const isAKey = (item) => [sdk.items.quest.KeyofTerror, sdk.items.quest.KeyofHate, sdk.items.quest.KeyofDestruction].includes(item.classid); + /** @param {ItemUnit} item */ + 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)); + const isWanted = function (item) { + return (AutoMule.cubingIngredient(item) + || AutoMule.runewordIngredient(item) + || AutoMule.utilityIngredient(item) + ); + }; let items = me.getItemsEx() .filter(function (item) { @@ -532,7 +559,9 @@ const AutoMule = { // don't mule items in locked spots 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 - 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 if (isAKey(item) && TorchSystem.getFarmers() && TorchSystem.isFarmer()) return false; // we've gotten this far, mule items that are on the force list @@ -540,7 +569,9 @@ const AutoMule = { // alright that handles the basics -- now normal pickit check let pResult = Pickit.checkItem(item).result; // if it's a junk item, we don't want it - if ([Pickit.Result.UNID, Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pResult)) return (item.isInStash && muleOrphans); + if ([Pickit.Result.UNID, Pickit.Result.UNWANTED, Pickit.Result.TRASH].includes(pResult)) { + return (item.isInStash && muleOrphans); + } // we've made it this far, we want it return true; }); @@ -554,7 +585,8 @@ const AutoMule = { * @returns {boolean} */ utilityIngredient: function (item) { - return (!!item && CraftingSystem.validGids.includes(item.gid)); + if (!item) return false; + return CraftingSystem.validGids.includes(item.gid); }, /** @@ -565,17 +597,13 @@ const AutoMule = { cubingIngredient: function (item) { if (!item) return false; - for (let i = 0; i < Cubing.validIngredients.length; i += 1) { - if (item.gid === Cubing.validIngredients[i].gid) { - return true; - } - } - - return false; + return Cubing.validIngredients.some(function (ingred) { + return (item.gid === ingred.gid); + }); }, /** - * check if an item is a runeword ingrediend - rune, empty base or bad rolled base + * check if an item is a runeword ingredient - rune, empty base or bad rolled base * @param {ItemUnit} item * @returns {boolean} */ @@ -583,16 +611,17 @@ const AutoMule = { if (!item) return false; if (Runewords.validGids.includes(item.gid)) return true; - if (!this.baseGids) { - AutoMule.baseGids = []; - + if (!this.baseGids.size) { for (let i = 0; i < Config.Runewords.length; i += 1) { - let base = Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0)) || Runewords.getBase(Config.Runewords[i][0], Config.Runewords[i][1], (Config.Runewords[i][2] || 0), true); - base && this.baseGids.push(base.gid); + const [runeword, base, ethFlag] = Config.Runewords[i]; + let baseItem = (Runewords.getBase(runeword, base, (ethFlag || 0)) + || Runewords.getBase(runeword, base, (ethFlag || 0), true) + ); + baseItem && this.baseGids.add(baseItem.gid); } } - return this.baseGids.includes(item.gid); + return this.baseGids.has(item.gid); }, /** @@ -604,23 +633,26 @@ const AutoMule = { if (!Town.openStash()) return false; let item; + let items = me.getItemsEx() + .filter(function (item) { + return item.isInStorage && item.isCharm && item.unique; + }); + if (!items.length) return false; if (dropAnni) { - item = me.findItem(sdk.items.SmallCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + item = items.find(function (item) { + return item.isAnni && !Storage.Inventory.IsLocked(item, Config.Inventory); + }); + if (!item) return false; - if (item && !Storage.Inventory.IsLocked(item, Config.Inventory)) { - D2Bot.printToConsole("AutoMule: Transfering Anni.", sdk.colors.D2Bot.DarkGold); - } else { - return false; - } + D2Bot.printToConsole("AutoMule: Transfering Anni.", sdk.colors.D2Bot.DarkGold); } else { - item = me.findItem(sdk.items.LargeCharm, sdk.items.mode.inStorage, -1, sdk.items.quality.Unique); + item = items.find(function (item) { + return item.isGheeds; + }); + if (!item) return false; - if (item) { - D2Bot.printToConsole("AutoMule: Transfering Torch.", sdk.colors.D2Bot.DarkGold); - } else { - return false; - } + D2Bot.printToConsole("AutoMule: Transfering Gheeds.", sdk.colors.D2Bot.DarkGold); } item.drop(); @@ -629,146 +661,4 @@ const AutoMule = { return true; }, - - // ################################## // - /* ######### Mule Functions ######### */ - // ################################## // - - /** - * @param {{ profile: string, mode: number }} info - * @returns {{ profile: string, mode: number }} master info - */ - getMaster: function (info) { - let muleObj = info.mode === 1 ? this.TorchAnniMules : this.Mules; - - for (let i in muleObj) { - if (muleObj.hasOwnProperty(i)) { - for (let j in muleObj[i]) { - if (muleObj[i].hasOwnProperty(j) && j === "enabledProfiles") { - for (let k = 0; k < muleObj[i][j].length; k += 1) { - if (String.isEqual(muleObj[i][j][k], info.profile)) { - return { - profile: muleObj[i][j][k], - mode: info.mode - }; - } - } - } - } - } - } - - return false; - }, - - /** - * @param {number} mode - mule mode - * @param {string} master - profile that whats to mule - * @param {boolean} continuous - whether we are continuous or not - */ - getMuleObject: function (mode, master, continuous = false) { - mode = mode || 0; - let mule = mode > 0 ? this.TorchAnniMules : this.Mules; - - for (let i in mule) { - if (mule.hasOwnProperty(i)) { - if (mule[i].muleProfile && mule[i].enabledProfiles && String.isEqual(mule[i].muleProfile, me.profile) - && (continuous || mule[i].enabledProfiles.includes(master))) { - return mule[i]; - } - } - } - - return false; - }, - - /** - * @param {number} mode - * @param {string} master - * @param {boolean} continuous - * @returns {string} - */ - getMuleFilename: function (mode, master, continuous = false) { - mode = mode || 0; - let mule = mode > 0 ? this.TorchAnniMules : this.Mules; - let file; - - // Iterate through mule object - for (let i in mule) { - if (mule.hasOwnProperty(i)) { - // Mule profile matches config - if (mule[i].muleProfile && String.isEqual(mule[i].muleProfile, me.profile) && (continuous || mule[i].enabledProfiles.includes(master))) { - file = mode === 0 ? "logs/AutoMule." + i + ".json" : "logs/TorchMule." + i + ".json"; - - // If file exists check for valid info - if (FileTools.exists(file)) { - try { - let jsonStr = FileTools.readText(file); - let jsonObj = JSON.parse(jsonStr); - - // Return filename containing correct mule info - if (mule[i].accountPrefix && jsonObj.account && jsonObj.account.match(mule[i].accountPrefix)) { - return file; - } - } catch (e) { - print(e); - } - } else { - return file; - } - } - } - } - - // File exists but doesn't contain valid info - remake - FileTools.remove(file); - - return file; - }, - - /** - * Get whether this is a regular mule or a torch/anni mule - */ - getMuleMode: function() { - for (let i in this.Mules) { - if (this.Mules.hasOwnProperty(i)) { - if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { - return 0; - } - } - } - - for (let i in this.TorchAnniMules) { - if (this.TorchAnniMules.hasOwnProperty(i)) { - if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { - return 1; - } - } - } - - return 0; - }, - - /** - * Get whether this is a normal mule or continous mule - */ - isContinousMule: function () { - for (let i in this.Mules) { - if (this.Mules.hasOwnProperty(i)) { - if (this.Mules[i].muleProfile && String.isEqual(this.Mules[i].muleProfile, me.profile)) { - return this.Mules[i].continuousMule; - } - } - } - - for (let i in this.TorchAnniMules) { - if (this.TorchAnniMules.hasOwnProperty(i)) { - if (this.TorchAnniMules[i].muleProfile && String.isEqual(this.TorchAnniMules[i].muleProfile, me.profile)) { - return this.TorchAnniMules[i].continuousMule; - } - } - } - - return false; - } }; diff --git a/d2bs/kolbot/libs/systems/automule/Mule.js b/d2bs/kolbot/libs/systems/automule/Mule.js new file mode 100644 index 000000000..df56cd019 --- /dev/null +++ b/d2bs/kolbot/libs/systems/automule/Mule.js @@ -0,0 +1,482 @@ +/** +* @filename Mule.js +* @author theBGuy +* @desc Main lib for the Mule +* +* @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/mulelogger/MuleLogger")} +*/ + +/** + * Mule Data object manipulates external mule datafile + */ +const MuleData = { + _default: { + account: "", + accNum: 0, + character: "", + charNum: 0, + fullChars: [], + torchChars: [] + }, + fileName: "", + // create a new mule datafile + create: function () { + let string = JSON.stringify(this._default); + FileTools.writeText(this.fileName, string); + }, + + // read data from the mule datafile and return the data object + read: function () { + try { + let string = FileTools.readText(this.fileName); + let obj = JSON.parse(string); + + return obj; + } catch (e) { + console.error(e); + this.create(); + + return this._default; + } + }, + + // write a data object to the mule datafile + write: function (obj) { + let string = JSON.stringify(obj); + FileTools.writeText(this.fileName, string); + }, + + // set next account - increase account number in mule datafile + nextAccount: function () { + let obj = MuleData.read(); + + obj.accNum += 1; + obj.account = Mule.obj.accountPrefix + obj.accNum; + + MuleData.write(Object.assign(this._default, { accNum: obj.accNum, account: obj.account })); + + return obj.account; + }, + + nextChar: function () { + console.trace(); + let charSuffix = ""; + const charNumbers = "abcdefghijklmnopqrstuvwxyz"; + const obj = MuleData.read(); + + // dirty + obj.charNum > 25 && (obj.charNum = 0); + let num = obj.accNum.toString(); + + for (let i = 0; i < num.length; i++) { + charSuffix += charNumbers[parseInt(num[i], 10)]; + } + + charSuffix += charNumbers[obj.charNum]; + obj.charNum = obj.charNum + 1; + obj.character = Mule.obj.charPrefix + charSuffix; + + MuleData.write(obj); + + return obj.character; + }, +}; + +const Mule = { + /** @type {muleObj} */ + obj: null, + minGameTime: 0, + maxGameTime: 0, + continuous: false, + makeNext: false, + next: false, + refresh: false, + master: "", + mode: -1, + fileName: "", + startTick: 0, + status: "loading", + statusString: "", + masterStatus: { status: "" }, + droppedGids: new Set(), + + waitForMaster: function () { + console.log("Waiting for muler"); + // forever alone check? + Misc.poll(() => Mule.status === "begin", Time.minutes(3), 100); + + if (Mule.status !== "begin") { + if (Mule.foreverAlone() && !getUnits(sdk.unittype.Item).filter(i => i.onGroundOrDropping).length) { + D2Bot.printToConsole("Nobody joined - stopping.", sdk.colors.D2Bot.Red); + D2Bot.stop(me.profile, true); + } else { + console.debug("No response from master, but items on ground. Setting status to begin."); + Mule.status = "begin"; + } + } + + me.overhead("begin"); + console.debug("begin"); + }, + /** + * @todo check if there are any other profiles that need to mule while we are already in game? + */ + done: function () { + !Mule.obj.onlyLogWhenFull && MuleLogger.logChar(); + + let obj = MuleData.read(); + + if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { + obj.torchChars.push(me.name); + } + + MuleData.write(obj); + D2Bot.printToConsole("Done muling.", sdk.colors.D2Bot.DarkGold); + console.log("Done muling"); + sendCopyData(null, Mule.master, 10, JSON.stringify({ status: "quit" })); + D2Bot.stop(me.profile, true); + }, + + nextChar: function () { + MuleLogger.logChar(); + delay(500); + + // Mule.makeNext = true; + let obj = MuleData.read(); + + if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { + obj.torchChars.push(me.name); + } + + if (obj.fullChars.indexOf(me.name) === -1) { + obj.fullChars.push(me.name); + MuleData.write(obj); + } + let nextMule = MuleData.nextChar(); + D2Bot.printToConsole("Mule full, getting next character (" + nextMule + " )", sdk.colors.D2Bot.DarkGold); + + if (Mule.minGameTime && getTickCount() - Mule.startTick < Mule.minGameTime * 1000) { + while (getTickCount() - Mule.startTick < Mule.minGameTime * 1000) { + me.overhead( + "Stalling for " + Math.round(((Mule.startTick + (Mule.minGameTime * 1000)) - getTickCount()) / 1000) + + " Seconds" + ); + delay(1000); + } + } + + Mule.quit(); + }, + + quit: function () { + Mule.cursorCheck(); + console.log("ÿc8Mule game duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); + + try { + quit(); + } finally { + while (me.ingame) { + delay(100); + } + } + + return true; + }, + foreverAlone: function () { + let party = getParty(); + + if (party) { + do { + if (party.name !== me.name) return false; + } while (party.getNext()); + } + + return true; + }, + + checkAnniTorch: function () { + while (!me.gameReady) { + delay(500); + } + + return me.getItemsEx() + .some(i => i.isInStorage && (i.isAnni || i.isTorch)); + }, + + stashItems: function () { + me.getItemsEx() + .filter(item => item.isInInventory) + .sort((a, b) => (b.sizex * b.sizey - a.sizex * a.sizey)) + .forEach(item => { + Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item); + }); + + return true; + }, + + cursorCheck: function () { + let cursorItem = Game.getCursorUnit(); + + if (cursorItem) { + if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { + console.warn("Can't place " + cursorItem.prettyPrint + " in inventory"); + cursorItem.drop(); + } + } + + return true; + }, + + getGroundItems: function () { + return getUnits(sdk.unittype.Item) + .filter(i => i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType)); + }, + + pickItems: function () { + /** @type {ItemUnit[]} */ + let list = []; + let waitTick = getTickCount(); + let rval = "ready"; + let override = false; + + if (!Mule.clearedJunk) { + me.getItemsEx() + .filter(function (item) { + return (item.isInInventory + && Town.ignoreType(item.itemType) + && (Mule.mode === 0 || item.classid !== sdk.items.ScrollofIdentify) + ); + }) + .forEach(function (item) { + try { + item.drop(); + } catch (e) { + console.warn("Failed to drop an item."); + } + }); + Mule.clearedJunk = true; // only do this once + } + + while (me.gameReady) { + if (Mule.masterStatus.status === "done" || Mule.continuous || override) { + override = false; + let item = Game.getItem(); + + if (item) { + list = Mule.getGroundItems(); + Mule.droppedGids.forEach(function (gid) { + if (gid > 0 && !list.some(i => i.gid === gid)) { + item = Game.getItem(-1, -1, gid); + if (item && !Town.ignoreType(item.itemType)) { + list.push(item); + } + } + }); + Mule.droppedGids.clear(); + } + + // If and only if there is nothing left are we "done" + if (list.length === 0) { + rval = Mule.continuous ? "ready" : "done"; + + break; + } + + // pick large items first by sorting items by size in descending order and move gheed's charm to the end of the list + list.sort(function (a, b) { + if (a.isGheeds && !Pickit.canPick(a)) return 1; + if (b.isGheeds && !Pickit.canPick(b)) return -1; + + return (b.sizex * b.sizey - a.sizex * a.sizey); + }); + + while (list.length > 0) { + item = list.shift(); + let canFit = Storage.Inventory.CanFit(item); + + // Torch and Anni handling + if (Mule.mode > 0 && (item.isAnni || item.isTorch) && !Pickit.canPick(item)) { + let msg = item.classid === sdk.items.LargeCharm + ? "Mule already has a Torch." + : "Mule already has a Anni."; + D2Bot.printToConsole(msg, sdk.colors.D2Bot.DarkGold); + rval = "next"; + } + + // Gheed's Fortune handling + if (item.isGheeds && !Pickit.canPick(item)) { + D2Bot.printToConsole("Mule already has Gheed's.", sdk.colors.D2Bot.DarkGold); + rval = "next"; + } + + if (!canFit && Mule.stashItems()) { + canFit = Storage.Inventory.CanFit(item); + } + + canFit + ? Pickit.pickItem(item) + : (rval = "next"); + } + + if (rval === "next") { + break; + } + } else { + if (!Mule.continuous) { + sendCopyData(null, Mule.master, 10, JSON.stringify({ status: "report" })); + Misc.poll(() => Mule.masterStatus.status === "done", Time.seconds(5), 50); + } else { + if (getTickCount() - waitTick > Time.minutes(10)) { + break; + } + } + // safety check + if (getTickCount() - waitTick > Time.minutes(3) && Mule.getGroundItems().length) { + override = true; + } + } + + delay(500); + } + + return rval; + }, + + /** + * @param {number} time + */ + ingameTimeout: function (time) { + let tick = getTickCount(); + + while (getTickCount() - tick < time) { + if (me.ingame && me.gameReady && !!me.area) break; + + // game doesn't exist, might need more locs + if (getLocation() === sdk.game.locations.GameDoesNotExist) { + break; + } + + delay(100); + } + + return (me.ingame && me.gameReady && !!me.area); + }, + + /** + * @param {{ profile: string, mode: number }} info + * @returns {{ profile: string, mode: number }} master info + */ + getMaster: function (info) { + const muleObj = info.mode === 1 + ? AutoMule.TorchAnniMules + : AutoMule.Mules; + + for (let i in muleObj) { + if (muleObj.hasOwnProperty(i)) { + const { enabledProfiles } = muleObj[i]; + if (!enabledProfiles.length) continue; + for (let profile of enabledProfiles) { + if (String.isEqual(profile, info.profile)) { + return { + profile: profile, + mode: info.mode + }; + } else if (profile === "all") { + return { + profile: info.profile, // set whoever is asking as master + mode: info.mode + }; + } + } + } + } + + return false; + }, + + /** + * @param {number} mode + * @param {string} master + * @param {boolean} continuous + * @returns {string} + */ + getMuleFilename: function (mode, master, continuous = false) { + mode = mode || 0; + let file; + let mule = mode > 0 ? AutoMule.TorchAnniMules : AutoMule.Mules; + + // Iterate through mule object + for (let i in mule) { + if (mule.hasOwnProperty(i)) { + const { + muleProfile, + enabledProfiles, + accountPrefix, + } = mule[i]; + // Mule profile matches config + if (muleProfile && String.isEqual(muleProfile, me.profile) + && (continuous || enabledProfiles.includes(master) || enabledProfiles.includes("all"))) { + file = mode === 0 + ? "logs/AutoMule." + i + ".json" + : "logs/TorchMule." + i + ".json"; + + // If file exists check for valid info + if (FileTools.exists(file)) { + try { + let jsonStr = FileTools.readText(file); + let jsonObj = JSON.parse(jsonStr); + + // Return filename containing correct mule info + if (accountPrefix && jsonObj.account && jsonObj.account.match(accountPrefix)) { + return file; + } + } catch (e) { + console.error(e); + } + } else { + return file; + } + } + } + } + + // File exists but doesn't contain valid info - remake + FileTools.remove(file); + + return file; + }, + + /** @returns {{ mode: number, obj: muleObj }[]} */ + getMuleInfo: function (master = "") { + const data = []; + /** + * @param {muleObj} muleObj + * @param {number} mode + */ + const checkObj = function (muleObj, mode) { + let { muleProfile, enabledProfiles, continuousMule } = muleObj; + if (muleProfile && String.isEqual(muleProfile, me.profile)) { + if (continuousMule || enabledProfiles.includes(master) || enabledProfiles.includes("all")) { + data.push({ + mode: mode, + obj: muleObj, + }); + } + } + }; + + for (let i in AutoMule.Mules) { + if (AutoMule.Mules.hasOwnProperty(i)) { + checkObj(AutoMule.Mules[i], 0); + } + } + + for (let i in AutoMule.TorchAnniMules) { + if (AutoMule.TorchAnniMules.hasOwnProperty(i)) { + checkObj(AutoMule.TorchAnniMules[i], 1); + } + } + return data; + }, +}; From ed3befe98d54f09f4fc9613b7c8bab5b03a292df Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Aug 2023 23:55:29 -0400 Subject: [PATCH 254/758] Create main.js - create new main thread for mule. We kill default.dbj and load this so we don't have to worry about kolbots other stuff as muling is its own separate system --- d2bs/kolbot/libs/systems/automule/main.js | 215 ++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 d2bs/kolbot/libs/systems/automule/main.js diff --git a/d2bs/kolbot/libs/systems/automule/main.js b/d2bs/kolbot/libs/systems/automule/main.js new file mode 100644 index 000000000..6d0f012fd --- /dev/null +++ b/d2bs/kolbot/libs/systems/automule/main.js @@ -0,0 +1,215 @@ +/** +* @filename main.js +* @author theBGuy +* @desc Executed upon game join, main thread for mule +* +* @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/mulelogger/MuleLogger")} +*/ + +js_strict(true); +include("critical.js"); // required + +// globals needed for core gameplay +includeCoreLibs(); + +// system libs +includeSystemLibs(); +include("systems/automule/Mule.js"); +include("systems/mulelogger/MuleLogger.js"); + +function main () { + D2Bot.init(); // Get D2Bot# handle + D2Bot.ingame(); + + while (!me.gameReady) { + delay(50); + } + + // load heartbeat if it isn't already running + let _heartbeat = getScript("threads/heartbeat.js"); + if (!_heartbeat || !_heartbeat.running) { + load("threads/heartbeat.js"); + } + + const Worker = require("../../modules/Worker"); + const Delta = new (require("../../modules/Deltas")); + + Worker.runInBackground.areaWatcher = (function () { + let areaTick = 0; + return function () { + // run area check every half second + if (getTickCount() - areaTick < 500) return true; + areaTick = getTickCount(); + // check that we are actually in game and that we've been there longer than a minute + if (getLocation() !== null || getTickCount() - me.gamestarttime < Time.minutes(1)) return true; + + if (me.ingame && me.gameReady && me.area > 0) { + if (me.area !== sdk.areas.RogueEncampment) { + console.warn("Preventing Suicide Walk! Current Area: " + me.area); + console.trace(); + + Mule.quit(); + } + } + + return true; + }; + })(); + + Worker.runInBackground.antiIdle = (function () { + let idleTick = 0; + return function () { + if (!me.ingame || getTickCount() - me.gamestarttime < Time.minutes(1) || !me.gameReady) return true; + if (idleTick === 0) { + idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); + console.log("Anti-idle refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + if (me.gameReady) { + if (getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + } else if (getLocation() !== null) { + idleTick = 0; + } + + return true; + }; + })(); + + // START + const EntryScript = getScript("D2BotMule.dbj"); + + Delta.track(() => Mule.status, () => EntryScript.send({ status: Mule.status })); + + addEventListener("itemaction", function (gid, mode, code) { + if (gid > 0 && mode === 2) { + console.log("gid: " + gid, " mode: " + mode + " code: " + code); + Mule.droppedGids.add(gid); + // Mule.status = "begin"; + } + }); + addEventListener("scriptmsg", function (msg) { + if (typeof msg === "object") { + if (msg.hasOwnProperty("obj")) { + // Object.assign(Mule, msg); + Mule.obj = msg.obj; + Mule.mode = msg.mode; + Mule.master = msg.master; + Mule.next = msg.next; + Mule.minGameTime = msg.minGameTime; + Mule.maxGameTime = msg.maxGameTime; + Mule.continuous = msg.obj.continuousMule; + MuleData.fileName = msg.fileName; + } + } + }); + addEventListener("copydata", function (mode, msg) { + switch (mode) { + case 10: // mule request + let obj = JSON.parse(msg); + + if (Mule.continuous) { + sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "ready" })); + } else { + if (!Mule.master) { + let masterInfo = Mule.getMaster(obj); + + if (masterInfo) { + Mule.master = masterInfo.profile; + Mule.mode = masterInfo.mode; + } + } else { + // come back to this to allow multiple mulers + if (obj.profile === Mule.master) { + sendCopyData(null, Mule.master, 10, JSON.stringify({ status: Mule.status })); + } else { + sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "busy" })); + } + } + } + + break; + case 11: // begin item pickup + Mule.status = "begin"; + + break; + case 12: // get master's status + Mule.masterStatus = JSON.parse(msg); + + break; + } + }); + EntryScript.send("mule_init"); + Misc.poll(() => Mule.obj !== null, Time.seconds(30), 100); + Mule.startTick = getTickCount(); + + if (Mule.next) { + // we had to make a new mule, if we are here then items need to be picked up + Mule.status = "begin"; + Mule.masterStatus = { status: "done" }; + } + + Mule.status !== "begin" && (Mule.status = "ready"); + Mule.recheckTick = getTickCount(); + + Town.goToTown(1); + Town.move("stash"); + + Storage.Init(); + + if (Mule.continuous) { + !Mule.obj.onlyLogWhenFull && MuleLogger.logChar(); + } + console.log("~~~Mule init complete~~~"); + // check the ground for items + if (Mule.getGroundItems().length > 0) { + Mule.status = "begin"; + } + + if (!Mule.continuous) { + Mule.waitForMaster(); + } + + while (me.ingame) { + if (Mule.status === "begin") { + Mule.status = Mule.pickItems(); + + switch (Mule.status) { + // done picking, tell the master to leave game and kill mule profile + case "done": + Mule.done(); + + return true; + // can't fit more items, get to next character or account + case "next": + EntryScript.send("next"); + Mule.nextChar(); + + return true; + } + } else if (Mule.droppedGids.size > 0 && Mule.status === "ready") { + Mule.status = "begin"; + } + + if (Town.getDistance("stash") > 10) { + Town.move("stash"); + } + + if (Mule.continuous) { + if (Mule.maxGameTime > 0 + && getTickCount() - me.gamestarttime > Time.minutes(Mule.maxGameTime) + && Mule.foreverAlone()) { + console.log("~~~MaxGameTime Reached~~~"); + EntryScript.send("refresh"); + Mule.quit(); + + break; + } + } + delay(1000); + } + return true; +} From 8356bf6f1d0e386ca4f22862f15fbcd071fb56db Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:00:39 -0400 Subject: [PATCH 255/758] Update D2BotMule.dbj - rebuild mule entry, removed ingame logic. --- d2bs/kolbot/D2BotMule.dbj | 1355 ++++++++++++------------------------- 1 file changed, 438 insertions(+), 917 deletions(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index b4bd77fff..6d7fbb9e1 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -8,10 +8,6 @@ js_strict(true); include("critical.js"); // required -/** - * @todo redo how muleing is handled, really dislike how it's handled here - */ - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // D2BotMule specific settings - for global settings see libs/starter/StarterConfig.js Starter.Config.MinGameTime = 30; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby @@ -24,920 +20,470 @@ Starter.Config.MakeAccountOnFailure = true; // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds - -// globals needed for core gameplay -includeCoreLibs({ exclude: ["Skill.js"] }); - +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // system libs includeSystemLibs(); +include("systems/automule/Mule.js"); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -const Controls = require("./libs/modules/Control"); -const Overrides = require("./libs/modules/Override"); -const Worker = require("./libs/modules/Worker"); - -/** @global */ -let master, muleMode, muleFilename, maxCharCount; -let [makeNext] = [false]; -let status = "loading"; -let masterStatus = { status: "" }; -/** @type {muleObj} */ -let muleObj; - -/** - * Mule Data object manipulates external mule datafile - */ -const MuleData = { - _default: { - account: "", - accNum: 0, - character: "", - charNum: 0, - fullChars: [], - torchChars: [] - }, - // create a new mule datafile - create: function () { - let string = JSON.stringify(this._default); - FileTools.writeText(muleFilename, string); - }, - - // read data from the mule datafile and return the data object - read: function () { - let string = FileTools.readText(muleFilename); - let obj = JSON.parse(string); - - return obj; - }, - - // write a data object to the mule datafile - write: function (obj) { - let string = JSON.stringify(obj); - FileTools.writeText(muleFilename, string); - }, - - // set next account - increase account number in mule datafile - nextAccount: function () { - let obj = MuleData.read(); - - obj.accNum += 1; - obj.account = muleObj.accountPrefix + obj.accNum; - - MuleData.write(Object.assign(this._default, { accNum: obj.accNum, account: obj.account })); - - return obj.account; - }, - - nextChar: function () { - let charSuffix = ""; - let charNumbers = "abcdefghijklmnopqrstuvwxyz"; - let obj = MuleData.read(); - - // dirty - obj.charNum > 25 && (obj.charNum = 0); - let num = obj.accNum.toString(); - - for (let i = 0; i < num.length; i++) { - charSuffix += charNumbers[parseInt(num[i], 10)]; - } +if (!FileTools.exists("data/" + me.profile + ".json") + && DataFile.create()) { + Starter.firstRun = true; +} - charSuffix += charNumbers[obj.charNum]; - obj.charNum = obj.charNum + 1; - obj.character = muleObj.charPrefix + charSuffix; - - MuleData.write(obj); - - return obj.character; - }, -}; - -const Mule = { - continuousMule: false, - clearedJunk: false, - startTick: 0, - idleTick: 0, - areaTick: 0, - recheckTick: 0, - statusString: "", - - /** - * @description background worker to prevent idle disconnect - */ - antiIdle: function () { - if (!Starter.inGame) return true; - if (!me.ingame || getTickCount() - me.gamestarttime < Time.minutes(1) || !me.gameReady) return true; - if (Mule.idleTick === 0) { - Mule.idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); - console.log("Game start, anti-idle refresh in: (" + Time.format(Mule.idleTick - getTickCount()) + ")"); - } - if (me.gameReady) { - if (getTickCount() - Mule.idleTick > 0) { - Packet.questRefresh(); - Mule.idleTick += Time.seconds(rand(1200, 1500)); - console.log("Sent anti-idle packet, next refresh in: (" + Time.format(Mule.idleTick - getTickCount()) + ")"); +function main () { + const inGameThread = "libs/systems/automule/main.js"; + const locationAction = (function () { + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations, + parseControlText + } = require("./libs/oog/Locations"); + + locations.set(sdk.game.locations.OtherMultiplayer, + function () { + Controls.OtherMultiplayerCancel.click(); } - } else if (getLocation() !== null) { - Mule.idleTick = 0; - } - - return true; - }, - - /** - * @description background worker to prevent suicide walks - * @todo figure out why this bugs out when used - */ - areaWatcher: function () { - if (!Starter.inGame) return true; - // run area check every half second - if (getTickCount() - Mule.areaTick < 500) return true; - Mule.areaTick = getTickCount(); - // check that we are actually in game and that we've been there longer than a minute - if (getLocation() !== null || getTickCount() - me.gamestarttime < Time.minutes(1)) return true; - console.debug("Check Area: " + me.area); - - if (me.ingame && me.gameReady && me.area > 0) { - if (me.area !== sdk.areas.RogueEncampment) { - console.warn("Preventing Suicide Walk! Current Area: " + me.area); - console.trace(); - - Mule.quit(); + ); + locations.set(sdk.game.locations.TcpIpEnterIp, + function () { + Controls.PopupNo.click(); } - } - - return true; - }, - - init: function () { - Mule.startTick = getTickCount(); - - while ((getLocation() !== null) && !!me.area && getTickCount() - Mule.startTick < Time.seconds(10)) { - delay(200); - } - - if (getLocation() !== null || !me.ingame || !me.gameReady || !me.inTown) { - return false; - } - - includeIfNotIncluded("core/Skill.js"); - Starter.firstLogin ? (Starter.firstLogin = false) : (status = "begin"); - status !== "begin" && (status = "ready"); - Mule.statusString = "In " + (muleMode === 2 ? "anni " : muleMode === 1 ? "torch " : "") + "mule game."; - - D2Bot.updateStatus(Mule.statusString + " Status: " + status); - D2Bot.printToConsole(Mule.statusString, sdk.colors.D2Bot.DarkGold); - Mule.recheckTick = getTickCount(); - - Town.goToTown(1); - Town.move("stash"); - - // Move away from stash so we don't block muler - let coord = {}; - do { - delay(1000); - coord = CollMap.getRandCoordinate(me.x, -6, 6, me.y, -6, 6); - } while (!Attack.validSpot(coord.x, coord.y)); - - Pather.moveToUnit(coord); - - Storage.Init(); - Starter.inGame = true; - if (Mule.continuousMule) { - !muleObj.onlyLogWhenFull && MuleLogger.logChar(); - addEventListener("gameevent", gameEvent); - } - // Worker.runInBackground.areaWatcher = Mule.areaWatcher; // bugs for some reason, quits game then on re-join d2bot spams In mule game for ~30seconds - Worker.runInBackground.antiIdle = Mule.antiIdle; - - return true; - }, - - waitForMaster: function () { - console.log("Waiting for muler"); - // forever alone check? - Misc.poll(() => status === "begin", Time.minutes(3), 100); - - if (status !== "begin") { - D2Bot.printToConsole("Nobody joined - stopping.", sdk.colors.D2Bot.Red); - D2Bot.stop(me.profile, true); - } - - me.overhead("begin"); - console.debug("begin"); - }, - - /** - * @todo check if there are any other profiles that need to mule while we are already in game? - */ - done: function () { - !muleObj.onlyLogWhenFull && MuleLogger.logChar(); - - let obj = MuleData.read(); + ); + locations.set(sdk.game.locations.MainMenu, + function () { + if (!Mule.obj) return; // don't do anything if we don't have a mule profile + if (Mule.obj.realm) { + ControlAction.clickRealm(ControlAction.realms[Mule.obj.realm]); + } + Controls.BattleNet.click(); + } + ); + locations.set(sdk.game.locations.Login, + function () { + if (Mule.makeNext) { + // why? + Mule.makeNext = false; + } + let obj = MuleData.read(); - if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { - obj.torchChars.push(me.name); - } + if (!obj.account || obj.account.indexOf(Mule.obj.accountPrefix) < 0) { + MuleData.nextAccount(); + obj = MuleData.read(); + } - MuleData.write(obj); - D2Bot.printToConsole("Done muling.", sdk.colors.D2Bot.DarkGold); - console.log("Done muling"); - sendCopyData(null, master, 10, JSON.stringify({ status: "quit" })); - D2Bot.stop(me.profile, true); - }, + let info = { + realm: Mule.obj.realm, + account: obj.account, + password: Mule.obj.accountPassword + }; - next: function () { - MuleLogger.logChar(); - delay(500); + if (Starter.makeAccount) { + if (ControlAction.makeAccount(info)) { + D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); + Starter.makeAccount = false; + } else { + MuleData.nextAccount(); + } - [makeNext] = [true]; - let obj = MuleData.read(); + return; + } - if (Mule.checkAnniTorch() && obj.torchChars.indexOf(me.name) === -1) { - obj.torchChars.push(me.name); - } + MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); + if (!ControlAction.loginAccount(info)) { + Starter.makeAccount = true; + } + } + ); + locations.set(sdk.game.locations.CreateNewAccount, + function () { + if (Starter.makeAccount) { + let obj = MuleData.read(); + let info = { + realm: Mule.obj.realm, + account: obj.account, + password: Mule.obj.accountPassword + }; + + if (ControlAction.makeAccount(info)) { + D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); + Starter.makeAccount = false; + } else { + MuleData.nextAccount(); + } + } + } + ); + locations.set(sdk.game.locations.CharSelectNoChars, + function () { + if (!Controls.CharSelectCurrentRealm.control) { + Controls.BottomLeftExit.click(); + } else if (Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { + D2Bot.updateStatus("Realm Down"); + delay(1000); + + if (!Controls.BottomLeftExit.click()) { + return; + } - obj.fullChars.push(me.name); - MuleData.write(obj); - MuleData.nextChar(); - D2Bot.printToConsole("Mule full, getting next character.", sdk.colors.D2Bot.DarkGold); + Starter.updateCount(); + ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); + D2Bot.CDKeyRD(); - if (Starter.Config.MinGameTime && getTickCount() - Mule.startTick < Starter.Config.MinGameTime * 1000) { - while (getTickCount() - Mule.startTick < Starter.Config.MinGameTime * 1000) { - me.overhead("Stalling for " + Math.round(((Mule.startTick + (Starter.Config.MinGameTime * 1000)) - getTickCount()) / 1000) + " Seconds"); - delay(1000); + if (Starter.gameInfo.switchKeys) { + D2Bot.printToConsole("Realm Down - Changing CD-Key"); + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.restart(); + } + } } - } - - Mule.quit(); - }, - - quit: function () { - Worker.stopProcess("antiIdle"); - ["default.dbj", "threads/AntiIdle.js", "threads/AreaWatcher.js"].forEach(thread => { - let script = getScript(thread); - if (script && script.running && script.stop()) { - delay(100); + ); + locations.set(sdk.game.locations.SelectDifficultySP, + function () { + hideConsole(); + sendKey(sdk.keys.Escape); } - }); - Mule.cursorCheck(); - console.log("ÿc8Mule game duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); - - try { - quit(); - } finally { - while (me.ingame) { - delay(100); + ); + locations.set(sdk.game.locations.GameNameExists, + function () { + Controls.JoinGameWindow.click(); } - } - - return true; - }, - - /** - * @todo handle when a bot wants to mule while we are refreshing the game - */ - gameRefresh: function () { - if (!this.continuousMule) return this.quit(); - console.log("MaxGameTime Reached: "); - removeEventListener("gameevent", gameEvent); - Mule.quit(); - - Starter.firstLogin = true; - console.log("updating runs"); - D2Bot.updateRuns(); - status = "ready"; - Starter.inGame = false; - - delay(1000); - Controls.LobbyQuit.click(); // Quit from Lobby - ControlAction.timeoutDelay("Refresh game", 330 * 1000); // 5.5 minutes - - return true; - }, - - /** - * @param {number} time - */ - ingameTimeout: function (time) { - let tick = getTickCount(); - - while (getTickCount() - tick < time) { - if (me.ingame && me.gameReady && !!me.area) break; - - // game doesn't exist, might need more locs - if (getLocation() === sdk.game.locations.GameDoesNotExist) { - break; + ); + locations.set(sdk.game.locations.GameDoesNotExist, + function () { + Controls.CreateGameWindow.click(); } + ); + locations.set(sdk.game.locations.CreateGame, + function () { + D2Bot.updateStatus("Creating Game"); + + // remove level restriction + if (Controls.CharacterDifference.disabled === 5) { + Controls.CharacterDifferenceButton.click(); + } + // Max number of players + Controls.MaxPlayerCount.setText("8"); - delay(100); - } - - return (me.ingame && me.gameReady && !!me.area); - }, - - foreverAlone: function () { - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name) return false; - } while (party.getNext()); - } - - return true; - }, - - checkAnniTorch: function () { - while (!me.gameReady) { - delay(500); - } + delay(2000); - return me.getItemsEx() - .filter(i => i.isInStorage && i.unique && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(i.classid)) - .some(i => [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(i.classid)); - }, - - stashItems: function () { - me.getItemsEx() - .filter(item => item.isInInventory) - .sort((a, b) => (b.sizex * b.sizey - a.sizex * a.sizey)) - .forEach(item => { - Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item); - }); - - return true; - }, - - cursorCheck: function () { - let cursorItem = Game.getCursorUnit(); - - if (cursorItem) { - if (!Storage.Inventory.CanFit(cursorItem) || !Storage.Inventory.MoveTo(cursorItem)) { - console.warn("Can't place " + cursorItem.prettyPrint + " in inventory"); - cursorItem.drop(); - } - } - - return true; - }, - - pickItems: function () { - let waitTick = getTickCount(); - let rval = "fail"; - let list = []; - let override = false; - - while (!me.name || !me.gameReady) { - if (!me.ingame) return rval; - delay(100); - } + // FTJ handler + if (Mule.status === "pending") { + D2Bot.printToConsole("Failed to create game"); + ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); + D2Bot.updateRuns(); + } - if (!Mule.clearedJunk) { - me.getItemsEx() - .filter(function (item) { - return (item.isInInventory - && Town.ignoreType(item.itemType) - && (muleMode === 0 || item.classid !== sdk.items.ScrollofIdentify) + createGame(Mule.obj.muleGameName[0], Mule.obj.muleGameName[1]); + if (!Mule.ingameTimeout(Time.minutes(1))) { + console.debug( + "Failed to get in game, current location: " + getLocation() + + " inGame? " + me.ingame + " area? " + me.area ); - }) - .forEach(function (item) { - try { - item.drop(); - } catch (e) { - console.warn("Failed to drop an item."); - } - }); - Mule.clearedJunk = true; // only do this once - } - - const getItems = function () { - return getUnits(sdk.unittype.Item) - .filter(function (i) { - return i.distance < 20 && i.onGroundOrDropping && !Town.ignoreType(i.itemType); - }); - }; - - while (me.gameReady) { - if (masterStatus.status === "done" || Mule.continuousMule || override) { - override = false; - let item = Game.getItem(); - - if (item) { - list = getItems(); + return; } - - // If and only if there is nothing left are we "done" - if (list.length === 0) { - rval = Mule.continuousMule ? "ready" : "done"; - - break; + + Mule.status = "pending"; + } + ); + locations.set(sdk.game.locations.JoinGame, + function () { + D2Bot.updateStatus("Join Game"); + + if (Mule.status === "pending") { + D2Bot.printToConsole("Failed to join game"); + ControlAction.timeoutDelay("Join Delay", Starter.Config.FTJDelay * 1000); + D2Bot.updateRuns(); } - // pick large items first by sorting items by size in descending order and move gheed's charm to the end of the list - list.sort(function (a, b) { - if (a.isGheeds && !Pickit.canPick(a)) return 1; - if (b.isGheeds && !Pickit.canPick(b)) return -1; - - return (b.sizex * b.sizey - a.sizex * a.sizey); - }); - - while (list.length > 0) { - item = list.shift(); - let canFit = Storage.Inventory.CanFit(item); - - // Torch and Anni handling - if (muleMode > 0 && item.unique - && [sdk.items.SmallCharm, sdk.items.LargeCharm].includes(item.classid) && !Pickit.canPick(item)) { - let msg = item.classid === sdk.items.LargeCharm ? "Mule already has a Torch." : "Mule already has a Anni."; - D2Bot.printToConsole(msg, sdk.colors.D2Bot.DarkGold); - rval = "next"; - } - - // Gheed's Fortune handling - if (item.isGheeds && !Pickit.canPick(item)) { - D2Bot.printToConsole("Mule already has Gheed's.", sdk.colors.D2Bot.DarkGold); - rval = "next"; - } + if (Starter.inGame) { + console.log("updating runs"); + D2Bot.updateRuns(); + Mule.status = "ready"; + Starter.inGame = false; + } - if (!canFit && Mule.stashItems()) { - canFit = Storage.Inventory.CanFit(item); + if (Mule.refresh + || (Mule.makeNext && !String.isEqual(me.charname, MuleData.read().character))) { + Controls.LobbyQuit.click(); // Quit from Lobby + if (Mule.makeNext) { + console.debug("Next mule: " + MuleData.read().character + " current: " + me.charname); } - - if (canFit) { - Pickit.pickItem(item); - } else { - rval = "next"; + if (Mule.refresh) { + ControlAction.timeoutDelay("Refresh game", 330 * 1000); // 5.5 minutes + Mule.refresh = false; } + return; } - if (rval === "next") { - break; + if (!Mule.continuous) { + D2Bot.requestGame(Mule.master); + delay(100); } - } else { - if (!Mule.continuousMule) { - sendCopyData(null, master, 10, JSON.stringify({ status: "report" })); - Misc.poll(() => masterStatus.status === "done", Time.seconds(5), 50); - } else { - if (getTickCount() - waitTick > Time.minutes(10)) { - break; - } - } - // safety check - if (getTickCount() - waitTick > Time.minutes(3) && getItems().length) { - // why are there items on the ground but we haven't recieved the go ahead? - console.debug("Anyone in game? " + Mule.foreverAlone(), masterStatus, Starter.firstLogin); - override = true; - } - } - delay(500); - } + delay(2000); - return rval; - }, -}; - -new Overrides.Override (Starter, Starter.receiveCopyData, function (orignal, mode, msg) { - if (mode === 3) return; - // master/mule communication function - switch (mode) { - case 10: // mule request - let obj = JSON.parse(msg); - - if (Mule.continuousMule && me.ingame) { - sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "ready" })); - } else { - if (!master) { - let masterInfo = AutoMule.getMaster(obj); - - if (masterInfo) { - master = masterInfo.profile; - muleMode = masterInfo.mode; - } - } else { - if (obj.profile === master) { - sendCopyData(null, master, 10, JSON.stringify({ status: status })); + if (Object.keys(Starter.joinInfo).length + && Starter.joinInfo.gameName !== "" + && Starter.joinInfo.inGame) { + joinGame(Starter.joinInfo.gameName, Starter.joinInfo.gamePass); } else { - sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "busy" })); + joinGame(Mule.obj.muleGameName[0], Mule.obj.muleGameName[1]); } - } - } - - break; - case 11: // begin item pickup - status = "begin"; - break; - case 12: // get master's status - masterStatus = JSON.parse(msg); - console.debug(msg); - - break; - default: - orignal(mode, msg); - } -}).apply(); - -new Overrides.Override (Starter, Starter.updateCount, function () { - D2Bot.updateCount(); - delay(1000); - Controls.BattleNet.click(); + !Starter.firstLogin && (Mule.status = "pending"); - let obj = MuleData.read(); - let info = { - realm: muleObj.realm, - account: obj.account, - password: muleObj.accountPassword - }; - - MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); - ControlAction.loginAccount(info); - delay(1000); - Controls.BottomLeftExit.click(); -}).apply(); - -function locationAction (location) { - let obj, info, string, text; - - switch (location) { - case sdk.game.locations.PreSplash: - case sdk.game.locations.SplashScreen: - ControlAction.click(); - - break; - case sdk.game.locations.Lobby: - case sdk.game.locations.LobbyChat: - D2Bot.updateStatus("Lobby"); - - if (Starter.inGame) { - print("updating runs"); - D2Bot.updateRuns(); - status = "ready"; - Starter.inGame = false; - } - - if (makeNext) { - Controls.LobbyQuit.click(); - } else { - Starter.LocationEvents.openJoinGameWindow(); - } - - break; - case sdk.game.locations.CreateGame: - D2Bot.updateStatus("Creating Game"); - - // remove level restriction - Controls.CharacterDifference.disabled === 5 && Controls.CharacterDifferenceButton.click(); + if (Mule.ingameTimeout(Time.minutes(1))) { + console.debug("Ingame timeout done."); + } - // Max number of players - Controls.MaxPlayerCount.setText("8"); + // could not join game + if (getLocation() === sdk.game.locations.Lobby && !me.ingame) { + Controls.CreateGameWindow.click(); + } + } + ); + addLocations([sdk.game.locations.TcpIp, sdk.game.locations.NewCharSelected], + function () { + Controls.BottomLeftExit.click(); + } + ); + addLocations([sdk.game.locations.CharSelect, sdk.game.locations.CharSelectNoChars], + function () { + let string = parseControlText(Controls.CharSelectError.control); + + if (string) { + 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(me.profile, true); + } + } + } - delay(2000); + // Single Player screen fix + // TODO: see if this is still needed. d2bs doesn't load scripts twice anymore + if (!Controls.CharSelectCurrentRealm.control) { + Controls.BottomLeftExit.click(); - // FTJ handler - if (status === "pending") { - D2Bot.printToConsole("Failed to create game"); - ControlAction.timeoutDelay("FTJ delay", Starter.Config.FTJDelay * 1e3); - D2Bot.updateRuns(); - } + return; + } - createGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); - if (!Mule.ingameTimeout(Time.minutes(1))) { - console.debug("Failed to get in game, current location: " + getLocation() + " inGame? " + me.ingame + " area? " + me.area); - break; - } - - status = "pending"; + // Can't create character, button greyed out = high likelyhood of realm down + if (getLocation() === sdk.game.locations.CharSelectNoChars + && Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { + D2Bot.updateStatus("Realm Down"); + delay(1000); - break; - case sdk.game.locations.WaitingInLine: - Starter.LocationEvents.waitingInLine(); + if (!Controls.BottomLeftExit.click()) { + return; + } - break; - case sdk.game.locations.JoinGame: - D2Bot.updateStatus("Join Game"); + Starter.updateCount(); + ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); + D2Bot.CDKeyRD(); - if (status === "pending") { - D2Bot.printToConsole("Failed to join game"); - ControlAction.timeoutDelay("Join Delay", Starter.Config.FTJDelay * 1000); - D2Bot.updateRuns(); - } + if (Starter.gameInfo.switchKeys) { + D2Bot.printToConsole("Realm Down - Changing CD-Key"); + ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); + D2Bot.restart(true); + } else { + D2Bot.restart(); + } + } - if (!Mule.continuousMule) { - D2Bot.requestGame(master); - delay(100); - } + let obj = MuleData.read(); + const maxCharCount = (Mule.obj.charsPerAcc > 0 ? Math.min(Mule.obj.charsPerAcc, 18) : 8); - if (Starter.inGame) { - print("updating runs"); - D2Bot.updateRuns(); - status = "ready"; - Starter.inGame = false; - } + if (Mule.makeNext) { + if (obj.fullChars.length >= maxCharCount + || (Mule.mode > 0 && obj.torchChars.length >= maxCharCount)) { + Controls.BottomLeftExit.click(); + MuleData.nextAccount(); - delay(2000); + return; + } - if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "" && Starter.joinInfo.inGame) { - joinGame(Starter.joinInfo.gameName, Starter.joinInfo.gamePass); - } else { - joinGame(muleObj.muleGameName[0], muleObj.muleGameName[1]); - } + Mule.makeNext = false; + } - !Starter.firstLogin && (status = "pending"); + if (!obj.character || obj.character.indexOf(Mule.obj.charPrefix) < 0) { + MuleData.nextChar(); - if (Mule.ingameTimeout(Time.minutes(1))) { - console.debug("Ingame timeout done."); - } + obj = MuleData.read(); + } - // could not join game - getLocation() === sdk.game.locations.Lobby && !me.ingame && Controls.CreateGameWindow.click(); + const info = { + account: obj.account, + charName: obj.character, + ladder: Mule.obj.ladder, + hardcore: Mule.obj.hardcore, + expansion: Mule.obj.expansion, + charClass: "amazon" + }; - break; - case sdk.game.locations.Ladder: - case sdk.game.locations.ChannelList: - break; - case sdk.game.locations.MainMenu: - case sdk.game.locations.Login: - makeNext && (makeNext = false); + if (Mule.mode > 0 && obj.torchChars.includes(info.charName)) { + MuleData.nextChar(); - obj = MuleData.read(); + return; + } - if (!obj.account || obj.account.indexOf(muleObj.accountPrefix) < 0) { - MuleData.nextAccount(); - obj = MuleData.read(); - } + if (ControlAction.findCharacter(info)) { + ControlAction.loginCharacter(info, false); + } else { + // premade account that's already full + if (ControlAction.getCharacters().length >= maxCharCount) { + Controls.BottomLeftExit.click(); + MuleData.nextAccount(); - info = { - realm: muleObj.realm, - account: obj.account, - password: muleObj.accountPassword - }; + return; + } - if (Starter.makeAccount) { - if (ControlAction.makeAccount(info)) { - D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); - Starter.makeAccount = false; - } else { - MuleData.nextAccount(); - } + if (!ControlAction.makeCharacter(info)) { + // TODO: check if acc is full and cancel location 15 and 29 if true + MuleData.nextChar(); - break; - } + return; + } - MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); - !ControlAction.loginAccount(info) && (Starter.makeAccount = true); - - break; - case sdk.game.locations.CreateNewAccount: - if (Starter.makeAccount) { - obj = MuleData.read(); - info = { - realm: muleObj.realm, - account: obj.account, - password: muleObj.accountPassword - }; - - if (ControlAction.makeAccount(info)) { - D2Bot.printToConsole("Made account: " + info.account, sdk.colors.D2Bot.DarkGold); - Starter.makeAccount = false; - } else { - MuleData.nextAccount(); + D2Bot.printToConsole("Made character: " + info.charName, sdk.colors.D2Bot.DarkGold); + } } - - break; - } - - break; - case sdk.game.locations.LoginError: - case sdk.game.locations.InvalidCdKey: - case sdk.game.locations.CdKeyInUse: - Starter.LocationEvents.loginError(); - - break; - case sdk.game.locations.LoginUnableToConnect: - case sdk.game.locations.TcpIpUnableToConnect: - Starter.LocationEvents.unableToConnect(); - - break; - case sdk.game.locations.RealmDown: - Starter.LocationEvents.realmDown(); - - break; - case sdk.game.locations.Disconnected: - case sdk.game.locations.LobbyLostConnection: - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); - Controls.OkCentered.click(); - - break; - case sdk.game.locations.CharSelect: - case sdk.game.locations.NewCharSelected: - case sdk.game.locations.CharacterCreate: - case sdk.game.locations.CharSelectNoChars: - string = ""; - text = Controls.CharSelectError.getText(); - - if (text) { - for (let i = 0; i < text.length; i++) { - string += text[i]; - - if (i !== text.length - 1) { - string += " "; + ); + addLocations([sdk.game.locations.Lobby, sdk.game.locations.LobbyChat], + function () { + D2Bot.updateStatus("Lobby"); + + if (Starter.inGame) { + if (Mule.refresh) { + Controls.LobbyQuit.click(); // Quit from Lobby + ControlAction.timeoutDelay("Refresh game", 330 * 1000); // 5.5 minutes + Mule.refresh = false; + + return; + } + console.log("updating runs"); + D2Bot.updateRuns(); + Mule.status = "ready"; + Starter.inGame = false; } + Mule.makeNext + ? Controls.LobbyQuit.click() + : Starter.LocationEvents.openJoinGameWindow(); } + ); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; + })(); + + const Overrides = require("./libs/modules/Override"); + new Overrides.Override (Starter, Starter.receiveCopyData, function (orignal, mode, msg) { + if (mode === 3) return; + // master/mule communication function + switch (mode) { + case 10: // mule request + if (me.ingame) return; + let obj = JSON.parse(msg); + + if (Mule.continuous && me.ingame) { + sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "ready" })); + } else { + if (!Mule.master) { + let masterInfo = Mule.getMaster(obj); - if (string === getLocaleString(sdk.locale.text.CdKeyDisabledFromRealm)) { // CDKey disabled from realm play - 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); + if (masterInfo) { + Mule.master = masterInfo.profile; + Mule.mode = masterInfo.mode; + } } else { - D2Bot.stop(me.profile, true); + // come back to this to allow multiple mulers + if (obj.profile === Mule.master) { + sendCopyData(null, Mule.master, 10, JSON.stringify({ status: Mule.status })); + } else { + sendCopyData(null, obj.profile, 10, JSON.stringify({ status: "busy" })); + } } } - } - - // Single Player screen fix - // TODO: see if this is still needed. d2bs doesn't load scripts twice anymore - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { - Controls.BottomLeftExit.click(); break; + case 11: // begin item pickup + case 12: // get master's status + break; + default: + orignal(mode, msg); } + }).apply(); - // Can't create character, button greyed out = high likelyhood of realm down - if (getLocation() === sdk.game.locations.CharSelectNoChars - && Controls.CharSelectCreate.disabled === sdk.game.controls.Disabled) { - D2Bot.updateStatus("Realm Down"); - delay(1000); - - if (!Controls.BottomLeftExit.click()) { - break; - } - - Starter.updateCount(); - ControlAction.timeoutDelay("Realm Down", Starter.Config.RealmDownDelay * 6e4); - D2Bot.CDKeyRD(); - - if (Starter.gameInfo.switchKeys) { - D2Bot.printToConsole("Realm Down - Changing CD-Key"); - ControlAction.timeoutDelay("Key switch delay", Starter.Config.SwitchKeyDelay * 1000); - D2Bot.restart(true); - } else { - D2Bot.restart(); - } - } - - obj = MuleData.read(); - maxCharCount = (muleObj.charsPerAcc > 0 ? Math.min(muleObj.charsPerAcc, 18) : 8); - - if (makeNext) { - if (obj.fullChars.length >= maxCharCount || (muleMode > 0 && obj.torchChars.length >= maxCharCount)) { - Controls.BottomLeftExit.click(); - MuleData.nextAccount(); - - break; - } - - makeNext = false; - } - - if (!obj.character || obj.character.indexOf(muleObj.charPrefix) < 0) { - MuleData.nextChar(); - - obj = MuleData.read(); - } + new Overrides.Override (Starter, Starter.updateCount, function () { + D2Bot.updateCount(); + delay(1000); + Controls.BattleNet.click(); - info = { + let obj = MuleData.read(); + let info = { + realm: Mule.obj.realm, account: obj.account, - charName: obj.character, - ladder: muleObj.ladder, - hardcore: muleObj.hardcore, - expansion: muleObj.expansion, - charClass: "amazon" + password: Mule.obj.accountPassword }; - if (muleMode > 0 && obj.torchChars.includes(info.charName)) { - MuleData.nextChar(); - - break; - } - - if (ControlAction.findCharacter(info)) { - ControlAction.loginCharacter(info, false); - } else { - // premade account that's already full - if (ControlAction.getCharacters().length >= maxCharCount) { - Controls.BottomLeftExit.click(); - MuleData.nextAccount(); + MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); + ControlAction.loginAccount(info); + delay(1000); + Controls.BottomLeftExit.click(); + }).apply(); - break; + addEventListener("copydata", Starter.receiveCopyData); + addEventListener("scriptmsg", function (msg) { + if (typeof msg === "string") { + if (msg === "mule_init") { + getScript(inGameThread).send({ + obj: Mule.obj, + mode: Mule.mode, + master: Mule.master, + fileName: MuleData.fileName, + next: Mule.next, + minGameTime: Starter.Config.MinGameTime, + maxGameTime: Starter.Config.MaxGameTime + }); + Mule.refresh = false; + Mule.next = false; + } else if (msg === "refresh") { + Mule.refresh = true; + } else if (msg === "next") { + Mule.makeNext = true; + // maybe hacky? I want to let the newly created mule know that it's a next mule instead of first join + Mule.next = true; } - - if (!ControlAction.makeCharacter(info)) { - // TODO: check if acc is full and cancel location 15 and 29 if true - MuleData.nextChar(); - - break; + } else if (typeof msg === "object") { + if (msg.hasOwnProperty("status")) { + Mule.status = msg.status; } - - D2Bot.printToConsole("Made character: " + info.charName, sdk.colors.D2Bot.DarkGold); } - - break; - case sdk.game.locations.CharSelectPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.SelectDifficultySP: - break; - case sdk.game.locations.MainMenuConnecting: - !Starter.locationTimeout(Starter.Config.ConnectingTimeout * 1e3, location) && Controls.LoginCancelWait.click(); - - break; - case sdk.game.locations.CharSelectConnecting: - Starter.LocationEvents.charSelectError(); - - break; - case sdk.game.locations.LobbyPleaseWait: - !Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location) && Controls.OkCentered.click(); - - break; - case sdk.game.locations.GameNameExists: - Controls.JoinGameWindow.click(); - - break; - case sdk.game.locations.GatewaySelect: - Controls.GatewayCancel.click(); - - break; - case sdk.game.locations.GameDoesNotExist: - Controls.CreateGameWindow.click(); - - break; - case sdk.game.locations.OkCenteredErrorPopUp: - Controls.OkCentered.click(); - Controls.BottomLeftExit.click(); - - break; - case sdk.game.locations.ServerDown: - case sdk.game.locations.GameIsFull: - break; - case sdk.game.locations.OtherMultiplayer: - // probably should implement way to use open-bnet - Controls.OtherMultiplayerCancel.click(); - - break; - case sdk.game.locations.TcpIp: - case sdk.game.locations.TcpIpEnterIp: - Controls.TcpIpCancel.click(); - - break; - default: - if (location !== undefined && location !== null) { - D2Bot.printToConsole("Unhandled location " + location); - delay(500); - D2Bot.restart(); - } - - break; - } -} - -// item event check instead? -// eslint-disable-next-line no-unused-vars -function gameEvent (mode, param1, param2, name1, name2) { - if (!me.ingame || !me.gameReady || !me.name) { - return; - } - - 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 (Mule.foreverAlone()) { - console.debug("Waiting"); - status = "ready"; - } - - break; - case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." - if (name1.trim() !== me.name.trim()) { - console.debug("begin"); - status = "begin"; - } - - break; - } -} - -function main () { - // basics -- don't touch - addEventListener("copydata", Starter.receiveCopyData); + }); while (!Starter.handle) { delay(100); @@ -955,112 +501,87 @@ function main () { D2Bot.updateRuns(); // we need the mule to swap keys somehow after all delay(1000); - // mule/master data initialization block - Mule.continuousMule = AutoMule.isContinousMule(); + const muleObjs = Mule.getMuleInfo(); - if (Mule.continuousMule) { - console.log("Continuous Mule Mode Started"); - muleMode = AutoMule.getMuleMode(); - muleObj = AutoMule.getMuleObject(muleMode, "", true); - muleFilename = AutoMule.getMuleFilename(muleMode, "", true); + if (muleObjs.length === 1) { + // we can assign our info directly + Mule.obj = muleObjs[0].obj; + Mule.mode = muleObjs[0].mode; + Mule.continuous = muleObjs[0].obj.continuousMule; + // we can use any of the enabled profiles + MuleData.fileName = Mule.getMuleFilename(Mule.mode, Mule.obj.enabledProfiles[0], Mule.continuous); } else { - // Wait for master before login = give room to determine muling mode (normal or torch) - while (!master) { + // we need to wait for confirmation from the master + while (!Mule.master) { delay(100); } - - console.log("Master found: " + master); - - muleObj = AutoMule.getMuleObject(muleMode, master); - muleFilename = AutoMule.getMuleFilename(muleMode, master); + console.log("Master found: " + Mule.master + " Mode: " + Mule.mode); + Mule.obj = muleObjs.find(function (muleObj) { + return muleObj.obj.enabledProfiles.includes(Mule.master) || muleObj.obj.enabledProfiles.includes("all"); + }); + Mule.continuous = Mule.obj.continuousMule; + MuleData.fileName = Mule.getMuleFilename(Mule.mode, Mule.master, Mule.continuous); } - console.log("Mule filename: " + muleFilename); + console.log("Mule filename: " + MuleData.fileName); try { - // ugly solution to uglier problem - pickItem area update - !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); - // create mule datafile if it doesn't exist - !FileTools.exists(muleFilename) && MuleData.create(); + !FileTools.exists(MuleData.fileName) && MuleData.create(); let obj = MuleData.read(); - obj.account && obj.account.indexOf(muleObj.accountPrefix) < 0 && MuleData.create(); + if (obj.account && obj.account.indexOf(Mule.obj.accountPrefix) < 0) { + MuleData.create(); + } } catch (e) { // probably should try again if fails to make file or shut down instead of continuing loop console.warn("Caught exception creating data files."); console.error(e); - D2Bot.printToConsole("DataFileException: " + e.message + " (" + e.fileName.substring(e.fileName.lastIndexOf("\\") + 1, e.fileName.length) + " #" + e.lineNumber + ")"); + D2Bot.printToConsole( + "DataFileException: " + e.message + + " (" + e.fileName.substring(e.fileName.lastIndexOf("\\") + 1, e.fileName.length) + + " #" + e.lineNumber + ")" + ); } // begin - MainLoop: while (true) { try { while (me.ingame) { if (me.gameReady) { - if (!Starter.inGame) { - if (!Mule.init()) { - console.debug("Failed to init. Trying again. Ingame and ready? " + (me.ingame && me.gameReady && !!me.area)); - delay(1000); - continue; - } - } - - if (!Mule.continuousMule) { - Mule.waitForMaster(); - } - - D2Bot.updateStatus(Mule.statusString + " Status: " + status + Starter.timer(me.gamestarttime)); - - if (status === "begin") { - switch (Mule.pickItems()) { - // done picking, tell the master to leave game and kill mule profile - case "done": - Mule.done(); + Starter.isUp = "yes"; - return; - // can't fit more items, get to next character or account - case "next": - Mule.next(); - - // should fix hitting gameRefresh when making a new character - continue MainLoop; - case "ready": - Mule.recheckTick = getTickCount(); - - break; - case "fail": - // Try again - break; - } - } - - if (Mule.continuousMule) { - if (Starter.Config.MaxGameTime > 0 - && getTickCount() - me.gamestarttime > Time.minutes(Starter.Config.MaxGameTime) - && Mule.foreverAlone()) { - Mule.gameRefresh(); - - break; - } else if (getTickCount() - Mule.recheckTick > Time.minutes(10)) { - // recheck every 10 minutes? - status = "begin"; - } + if (!Starter.inGame) { + Starter.gameStart = getTickCount(); + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + Mule.statusString = ( + "In " + + (Mule.mode === 2 ? "anni " : Mule.mode === 1 ? "torch " : "") + + "mule game." + ); + D2Bot.printToConsole(Mule.statusString, sdk.colors.D2Bot.DarkGold); + + DataFile.updateStats("runs", Starter.gameCount); + DataFile.updateStats("ingameTick"); } + D2Bot.updateStatus(Mule.statusString + " Status: " + Mule.status + Starter.timer(me.gamestarttime)); } delay(1000); } - if (!me.ingame) { - delay(1000); - locationAction(getLocation()); - } - } catch (e2) { - console.warn("Caught an exception in the main loop."); - console.error(e2); - D2Bot.printToConsole("MainLoopException: " + e2.message + " (" + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + " #" + e2.lineNumber + ")"); + Starter.isUp = "no"; + + locationAction.run(getLocation()); + delay(1000); + } catch (e) { + console.error(e); + D2Bot.printToConsole( + "MainLoopException: " + e.message + + " (" + e.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e.fileName.length) + + " #" + e.lineNumber + ")" + ); } delay(100); From 5f416363defb0046ab06b95c13084c03c427e9a0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:00:58 -0400 Subject: [PATCH 256/758] Update AutoMule.d.ts - typedef --- d2bs/kolbot/sdk/types/AutoMule.d.ts | 49 ++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/sdk/types/AutoMule.d.ts b/d2bs/kolbot/sdk/types/AutoMule.d.ts index 669d8235b..3bf039648 100644 --- a/d2bs/kolbot/sdk/types/AutoMule.d.ts +++ b/d2bs/kolbot/sdk/types/AutoMule.d.ts @@ -94,8 +94,49 @@ declare global { cubingIngredient(item: ItemUnit): void runewordIngredient(item: ItemUnit): void dropCharm(dropAnni: any): void - getMaster(info: any): void - getMuleObject(mode: any, master: any): muleObj | undefined - getMuleFilename(mode: any, master: any): void - } + }; + export namespace Mule { + let obj: muleObj; + let minGameTime: number; + let maxGameTime: number; + let continuous: boolean; + let makeNext: boolean; + let refresh: boolean; + let master: string; + let mode: number; + let startTick: number; + let status: string; + let statusString: string; + let masterStatus: { status: string }; + + function init(): void; + function gameRefresh(): void; + function ingameTimeout(): boolean; + function getMaster(info: { profile: string, mode: number }): { profile: string, mode: number } + function getMuleFilename(mode?: number, master: string): string; + function getMuleInfo(master?: string): { mode: number, obj: muleObj }[]; + }; + export namespace MuleData { + type MuleDataObj = { + account: string; + accNum: number; + character: string; + charNum: number; + realm: string; + expansion: boolean; + ladder: boolean; + fullChars: number[]; + torchChars: number[]; + }; + const _default: MuleDataObj; + let fileName: string; + function create(): void; + function read(): MuleDataObj; + function write(data: Partial): void; + function nextAccount(): string; + function nextChar(): string; + }; + export namespace LocationAction { + function run(): void; + }; } From 39b872acebf6a368fd4074bc04e3f84b3ef51386 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:13:35 -0400 Subject: [PATCH 257/758] move mapthread into root of manualplay dir --- d2bs/kolbot/libs/manualplay/{threads => }/MapThread.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename d2bs/kolbot/libs/manualplay/{threads => }/MapThread.js (100%) diff --git a/d2bs/kolbot/libs/manualplay/threads/MapThread.js b/d2bs/kolbot/libs/manualplay/MapThread.js similarity index 100% rename from d2bs/kolbot/libs/manualplay/threads/MapThread.js rename to d2bs/kolbot/libs/manualplay/MapThread.js From 95e7d5354d3ced88640ccb66521af9e1297625ec Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:15:00 -0400 Subject: [PATCH 258/758] rename mapthread -> main.js - renaming all separate main threads to be `main.js` --- d2bs/kolbot/libs/manualplay/{MapThread.js => main.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename d2bs/kolbot/libs/manualplay/{MapThread.js => main.js} (100%) diff --git a/d2bs/kolbot/libs/manualplay/MapThread.js b/d2bs/kolbot/libs/manualplay/main.js similarity index 100% rename from d2bs/kolbot/libs/manualplay/MapThread.js rename to d2bs/kolbot/libs/manualplay/main.js From f82cd268d67fd8c708e55d2bf1c6bd79a865736b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:27:19 -0400 Subject: [PATCH 259/758] small bit of cleanup with the name change - fix the filepaths now that the MapThread.js file was moved and renamed - little bit of formatting as well, need to come back and redo a lot of manualplay --- d2bs/kolbot/libs/manualplay/main.js | 77 ++++++++++++------- .../libs/manualplay/threads/MapHelper.js | 32 ++++---- .../libs/manualplay/threads/MapToolsThread.js | 30 +++++--- .../libs/manualplay/threads/PickThread.js | 2 +- 4 files changed, 84 insertions(+), 57 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index a3871b620..e2ec0aabf 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -1,10 +1,11 @@ /* eslint-disable max-len */ /** -* @filename MapThread.js +* @filename main.js * @author theBGuy -* @credits kolton for orginal MapThread, isid0re for the box/frame style, laz for gamepacketsent event handler -* @desc MapThread used with D2BotMap.dbj -* +* @credits kolton for orginal MapThread, +* isid0re for the box/frame style, +* laz for gamepacketsent event handler +* @desc main thread for D2BotMap.dbj */ js_strict(true); include("critical.js"); // required @@ -18,7 +19,7 @@ include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); // main thread specific -const LocalChat = require("../../modules/LocalChat"); +const LocalChat = require("../modules/LocalChat"); include("manualplay/MapMode.js"); MapMode.include(); @@ -84,7 +85,7 @@ const Hooks = { VectorHooks.flush(); TextHooks.displaySettings = false; TextHooks.check(); - } else if (sdk.uiflags.Inventory === flag && [sdk.uiflags.stash, sdk.uiflags.Cube, sdk.uiflags.TradePrompt].every((el) => !getUIFlag(el))) { + } else if (sdk.uiflags.Inventory === flag && [sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.TradePrompt].every((el) => !getUIFlag(el))) { ItemHooks.flush(); TextHooks.check(); } else { @@ -103,7 +104,7 @@ const Hooks = { } }; -function main() { +function main () { D2Bot.init(); // Get D2Bot# handle D2Bot.ingame(); @@ -122,7 +123,10 @@ 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"); + } console.log("ÿc9Map Thread Loaded."); MapMode.include(); @@ -139,8 +143,8 @@ function main() { Config.ManualPlayPick && load("libs/manualplay/threads/pickthread.js"); Config.PublicMode && load("threads/party.js"); - const Worker = require("../../modules/Worker"); - const UnitInfo = new (require("../../modules/UnitInfo")); + const Worker = require("../modules/Worker"); + const UnitInfo = new (require("../modules/UnitInfo")); Worker.runInBackground.unitInfo = function () { // always, maybe a timeout would be good though @@ -156,7 +160,7 @@ function main() { return true; }; - const log = (msg = "") => { + const log = function (msg = "") { me.overhead(msg); console.log(msg); }; @@ -166,15 +170,20 @@ function main() { } const hideFlags = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, - sdk.uiflags.EscMenu, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Waypoint, sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, - sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, + sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, + sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, + sdk.uiflags.Shop, sdk.uiflags.Quest, + sdk.uiflags.Waypoint, sdk.uiflags.TradePrompt, + sdk.uiflags.Msgs, sdk.uiflags.Stash, + sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen ]; + /** @type {Set} */ + const revealedAreas = new Set(); - this.revealArea = function (area) { - !this.revealedAreas && (this.revealedAreas = []); - - if (this.revealedAreas.indexOf(area) === -1) { + /** @param {number} area */ + const revealArea = function (area) { + if (!revealedAreas.has(area)) { delay(500); if (!getRoom()) { @@ -182,12 +191,16 @@ function main() { } revealLevel(true); - this.revealedAreas.push(area); + revealedAreas.add(area); } }; - // Run commands from chat - this.runCommand = function (msg) { + /** + * Run commands from chat + * @param {string} msg + * @returns {boolean} + */ + const runCommand = function (msg) { if (msg.length <= 1) return true; msg = msg.toLowerCase(); @@ -218,7 +231,7 @@ function main() { break; case "drop": if (msgList.length < 2) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); break; } @@ -228,7 +241,7 @@ function main() { break; case "stack": if (msgList.length < 2) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); break; } @@ -265,7 +278,7 @@ function main() { break; default: - print("ÿc1Invalid command : " + cmd); + console.warn("ÿc1Invalid command : " + cmd); break; } @@ -275,9 +288,14 @@ function main() { return true; }; - let onChatInput = (speaker, msg) => { + /** + * @param {string} speaker + * @param {string} msg + * @returns {boolean} + */ + const onChatInput = function (speaker, msg) { if (msg.length && msg[0] === ".") { - this.runCommand(msg); + runCommand(msg); return true; } @@ -287,6 +305,7 @@ function main() { addEventListener("chatinputblocker", onChatInput); addEventListener("keyup", ActionHooks.event); + // addEventListener("itemaction", Pickit.itemEvent); while (true) { while (!me.area || !me.gameReady) { @@ -295,7 +314,7 @@ function main() { let hideFlagFound = false; - this.revealArea(me.area); + revealArea(me.area); for (let i = 0; i < hideFlags.length; i++) { if (getUIFlag(hideFlags[i])) { @@ -310,7 +329,9 @@ function main() { if (hideFlagFound) continue; - getUIFlag(sdk.uiflags.AutoMap) ? Hooks.update() : Hooks.flush(true) && (!HelpMenu.cleared && HelpMenu.hideMenu()); + getUIFlag(sdk.uiflags.AutoMap) + ? Hooks.update() + : Hooks.flush(true) && (!HelpMenu.cleared && HelpMenu.hideMenu()); delay(20); diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 15c2a621f..6fd469fbf 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -2,7 +2,7 @@ * @filename MapHelper.js * @author theBGuy * @credits kolton -* @desc MapHelper used in conjuction with MapThread.js +* @desc MapHelper used in conjuction with main.js * */ js_strict(true); @@ -20,7 +20,7 @@ include("systems/gameaction/GameAction.js"); include("manualplay/MapMode.js"); MapMode.include(); -function main() { +function main () { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); @@ -28,7 +28,7 @@ function main() { let obj = { type: false, dest: false, action: false }; let action, fail = 0, x, y; - let mapThread = getScript("libs/manualplay/threads/mapthread.js"); + const mapThread = getScript("libs/manualplay/main.js"); const portalMap = {}; portalMap[sdk.areas.Abaddon] = { @@ -57,7 +57,7 @@ function main() { action = msg; }); - this.togglePickThread = function () { + const togglePickThread = function () { if (!Config.ManualPlayPick) return; const pickThread = getScript("threads/pickthread.js"); @@ -71,27 +71,27 @@ function main() { } }; - this.togglePause = function () { + const togglePause = function () { if (mapThread) { if (mapThread.running) { - print("pause mapthread"); + console.log("pause mapthread"); mapThread.pause(); } else if (!mapThread.running) { - print("resume mapthread"); + console.log("resume mapthread"); mapThread.resume(); if (!mapThread.running) { fail++; - if (fail % 5 === 0 && !getScript("libs/manualplay/threads/mapthread.js")) { - print("MapThread shut down, exiting MapHelper"); + if (fail % 5 === 0 && !getScript("libs/manualplay/main.js")) { + console.log("MapThread shut down, exiting MapHelper"); return false; } } } - } else if (!getScript("libs/manualplay/threads/mapthread.js")) { - print("MapThread shut down, exiting MapHelper"); + } else if (!getScript("libs/manualplay/main.js")) { + console.log("MapThread shut down, exiting MapHelper"); return false; } @@ -102,10 +102,10 @@ function main() { while (true) { if (getUIFlag(sdk.uiflags.EscMenu)) { delay(100); - mapThread.running && this.togglePause(); + mapThread.running && togglePause(); } else { if (!mapThread.running) { - if (!this.togglePause()) { + if (!togglePause()) { return; } } @@ -117,7 +117,7 @@ function main() { temp && Object.assign(obj, temp); addEventListener("keyup", Pather.stopEvent); - this.togglePickThread(); + togglePickThread(); if (obj) { let redPortal, chestLoc, king, unit; @@ -185,7 +185,7 @@ function main() { break; case "actChange": - print("Going to act: " + obj.dest); + console.log("Going to act: " + obj.dest); Pather.changeAct(obj.dest); break; @@ -416,7 +416,7 @@ function main() { } finally { action = false; removeEventListener("keyup", Pather.stopEvent); - this.togglePickThread(); + togglePickThread(); } } diff --git a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js index b46b986d6..e37703df0 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js @@ -22,7 +22,7 @@ include("systems/gameaction/GameAction.js"); include("manualplay/MapMode.js"); MapMode.include(); -function main() { +function main () { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); @@ -52,18 +52,24 @@ function main() { // General functions Common.Toolsthread.pauseScripts = [ - "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", - "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", + "libs/manualplay/main.js", + "libs/manualplay/threads/pickthread.js", + "libs/manualplay/threads/maphelper.js", + "threads/antihostile.js", + "threads/party.js", ]; Common.Toolsthread.stopScripts = [ - "default.dbj", "threads/townchicken.js", "libs/manualplay/threads/pickthread.js", - "threads/antihostile.js", "threads/party.js", "libs/manualplay/threads/maphelper.js", + "libs/manualplay/main.js", + "libs/manualplay/threads/pickthread.js", + "libs/manualplay/threads/maphelper.js", + "threads/antihostile.js", + "threads/party.js", ]; // Event functions - this.keyEvent = function (key) { + const keyEvent = function (key) { switch (key) { - case sdk.keys.PauseBreak: // pause default.dbj + case sdk.keys.PauseBreak: // pause main.dbj Common.Toolsthread.togglePause(); break; @@ -110,7 +116,7 @@ function main() { } }; - this.gameEvent = function (mode, param1, param2, name1, 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." @@ -168,7 +174,7 @@ function main() { } }; - this.scriptEvent = function (msg) { + const scriptEvent = function (msg) { switch (msg) { case "toggleQuitlist": canQuit = !canQuit; @@ -185,9 +191,9 @@ function main() { Config = copyObj(Config); let tick = getTickCount(); - addEventListener("keyup", this.keyEvent); - addEventListener("gameevent", this.gameEvent); - addEventListener("scriptmsg", this.scriptEvent); + addEventListener("keyup", keyEvent); + addEventListener("gameevent", gameEvent); + addEventListener("scriptmsg", scriptEvent); Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks diff --git a/d2bs/kolbot/libs/manualplay/threads/PickThread.js b/d2bs/kolbot/libs/manualplay/threads/PickThread.js index c4a76392e..8cb06d210 100644 --- a/d2bs/kolbot/libs/manualplay/threads/PickThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/PickThread.js @@ -24,7 +24,7 @@ include("systems/mulelogger/MuleLogger.js"); include("manualplay/MapMode.js"); MapMode.include(); -function main() { +function main () { console.log("ÿc9Pick Thread Loaded."); Config.init(false); Pickit.init(false); From 0d3780574e5b936b5a378833dc1b76b53db107bc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:29:52 -0400 Subject: [PATCH 260/758] Update default.dbj - fix filepaths for manualplay and muling now that they both have been moved and have main.js files --- d2bs/kolbot/default.dbj | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index e9617f833..6d1566790 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -54,7 +54,14 @@ function main () { // map mode runs in it's own thread if (getScript("d2botmap.dbj")) { - load("libs/manualplay/threads/mapthread.js"); + load("libs/manualplay/main.js"); + getScript(true).stop(); // kill this thread + return true; + } + + // muling runs in it's own thread + if (getScript("d2botmule.dbj")) { + load("libs/systems/automule/main.js"); getScript(true).stop(); // kill this thread return true; } @@ -63,7 +70,7 @@ function main () { if (MuleLogger.inGameCheck()) return true; // don't load default for dropper/mules - if (getScript("D2BotDropper.dbj") || getScript("d2botmule.dbj")) { + if (getScript("D2BotDropper.dbj")) { load("threads/AreaWatcher.js"); while (me.ingame) { @@ -229,8 +236,8 @@ function main () { ); delay(1000); } - } catch (e1) { - print(e1); + } catch (e) { + console.error(e); } } @@ -247,8 +254,8 @@ function main () { sojCounter += 1; } - } catch (e2) { - print(e2); + } catch (e) { + console.error(e); } } From 7909c657fdf65456c2ca00b5f0791501ed3e53ac Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:42:02 -0400 Subject: [PATCH 261/758] Update D2BotMule.dbj - show gamename + mulename in status bar --- d2bs/kolbot/D2BotMule.dbj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 6d7fbb9e1..b84a11362 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -565,7 +565,11 @@ function main () { DataFile.updateStats("runs", Starter.gameCount); DataFile.updateStats("ingameTick"); } - D2Bot.updateStatus(Mule.statusString + " Status: " + Mule.status + Starter.timer(me.gamestarttime)); + D2Bot.updateStatus( + me.charname + " | Game: " + me.gamename + + " | " + Mule.statusString + " Status: " + Mule.status + + Starter.timer(me.gamestarttime) + ); } delay(1000); From b405e431c9d078563c485c66b5e4a2bc02d8eede Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 01:26:28 -0400 Subject: [PATCH 262/758] Update D2BotMap.dbj - easier to read format for status bar --- d2bs/kolbot/D2BotMap.dbj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/D2BotMap.dbj b/d2bs/kolbot/D2BotMap.dbj index b9b6e6053..58efe4d53 100644 --- a/d2bs/kolbot/D2BotMap.dbj +++ b/d2bs/kolbot/D2BotMap.dbj @@ -35,8 +35,8 @@ function main () { Starter.isUp === "no" && (Starter.isUp = "yes"); if (me.ingame) { D2Bot.updateStatus( - "(Char: " + me.charname + ") (Game: " - + (me.gamename || "singleplayer") + ") (Level: " + me.charlvl + ")" + me.charname + " (" + me.charlvl + ") | Game: " + (me.gamename || "singleplayer") + + Starter.timer(Starter.gameStart) ); } } else { From 9baa62539120a2fba9c1f2d21f2d6938e4e959e8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:40:31 -0400 Subject: [PATCH 263/758] update workers to check for main.js - allow these modules to work with others systems that don't use default.dbj as the main thread --- d2bs/kolbot/libs/modules/workers/SimpleParty.js | 2 +- d2bs/kolbot/libs/modules/workers/TownChicken.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index 231a8463f..5fa99d36f 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -89,7 +89,7 @@ SimpleParty.timer = 0; - if (getScript(true).name.toLowerCase() === "default.dbj") { + if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { // For now, we gonna do this in game with a single party (Worker.runInBackground.party = (function () { console.log("ÿc2Kolbotÿc0 :: Simple party running"); diff --git a/d2bs/kolbot/libs/modules/workers/TownChicken.js b/d2bs/kolbot/libs/modules/workers/TownChicken.js index af44ce8f2..4ad348d09 100644 --- a/d2bs/kolbot/libs/modules/workers/TownChicken.js +++ b/d2bs/kolbot/libs/modules/workers/TownChicken.js @@ -7,7 +7,7 @@ (function (module, require, Worker) { // Only load this in global scope - if (getScript(true).name.toLowerCase() === "default.dbj") { + if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { /** * @param {number} [targetArea] * @param {string} [owner] From 88fed5c05e8cb2fc845e1ae14c82a5a6c2494d88 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:41:33 -0400 Subject: [PATCH 264/758] Update main.js - add back loading the simpleparty module if user sets PublicMode to true --- d2bs/kolbot/libs/manualplay/main.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index e2ec0aabf..f45c99829 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -141,7 +141,11 @@ function main () { load("libs/manualplay/threads/maphelper.js"); load("libs/manualplay/threads/maptoolsthread.js"); Config.ManualPlayPick && load("libs/manualplay/threads/pickthread.js"); - Config.PublicMode && load("threads/party.js"); + if (Config.PublicMode) { + Config.PublicMode === true + ? require("../modules/workers/SimpleParty") + : load("threads/Party.js"); + } const Worker = require("../modules/Worker"); const UnitInfo = new (require("../modules/UnitInfo")); From 739ef54cd2c7f9b448fa22289f86820f7a919e2f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 13:06:36 -0400 Subject: [PATCH 265/758] Update Town.js - small bits of cleanup - remove `lastInteractedNPC` was no longer using it --- d2bs/kolbot/libs/core/Town.js | 225 ++++++++++++++++------------------ 1 file changed, 105 insertions(+), 120 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 2fc824072..279e5c2d5 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -5,53 +5,45 @@ * */ -/** - * @enum {string} - */ -const NPC = { - Akara: getLocaleString(sdk.locale.npcs.Akara).toLowerCase(), - Gheed: getLocaleString(sdk.locale.npcs.Gheed).toLowerCase(), - Charsi: getLocaleString(sdk.locale.npcs.Charsi).toLowerCase(), - Kashya: getLocaleString(sdk.locale.npcs.Kashya).toLowerCase(), - Warriv: getLocaleString(sdk.locale.npcs.Warriv).toLowerCase(), - - Fara: getLocaleString(sdk.locale.npcs.Fara).toLowerCase(), - Drognan: getLocaleString(sdk.locale.npcs.Drognan).toLowerCase(), - Elzix: getLocaleString(sdk.locale.npcs.Elzix).toLowerCase(), - Greiz: getLocaleString(sdk.locale.npcs.Greiz).toLowerCase(), - Lysander: getLocaleString(sdk.locale.npcs.Lysander).toLowerCase(), - Jerhyn: getLocaleString(sdk.locale.npcs.Jerhyn).toLowerCase(), - Meshif: getLocaleString(sdk.locale.npcs.Meshif).toLowerCase(), - Atma: getLocaleString(sdk.locale.npcs.Atma).toLowerCase(), - - Ormus: getLocaleString(sdk.locale.npcs.Ormus).toLowerCase(), - Alkor: getLocaleString(sdk.locale.npcs.Alkor).toLowerCase(), - Hratli: getLocaleString(sdk.locale.npcs.Hratli).toLowerCase(), - Asheara: getLocaleString(sdk.locale.npcs.Asheara).toLowerCase(), - - Jamella: getLocaleString(sdk.locale.npcs.Jamella).toLowerCase(), - Halbu: getLocaleString(sdk.locale.npcs.Halbu).toLowerCase(), - Tyrael: getLocaleString(sdk.locale.npcs.Tyrael).toLowerCase(), - - Malah: getLocaleString(sdk.locale.npcs.Malah).toLowerCase(), - Anya: getLocaleString(sdk.locale.npcs.Anya).toLowerCase(), - Larzuk: getLocaleString(sdk.locale.npcs.Larzuk).toLowerCase(), - Qual_Kehk: getLocaleString(sdk.locale.npcs.QualKehk).toLowerCase(), - Nihlathak: getLocaleString(sdk.locale.npcs.Nihlathak2).toLowerCase(), - - Cain: getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase() -}; +const NPC = (new function NPC () { + this.Akara = getLocaleString(sdk.locale.npcs.Akara).toLowerCase(); + this.Gheed = getLocaleString(sdk.locale.npcs.Gheed).toLowerCase(); + this.Charsi = getLocaleString(sdk.locale.npcs.Charsi).toLowerCase(); + this.Kashya = getLocaleString(sdk.locale.npcs.Kashya).toLowerCase(); + this.Warriv = getLocaleString(sdk.locale.npcs.Warriv).toLowerCase(); + + this.Fara = getLocaleString(sdk.locale.npcs.Fara).toLowerCase(); + this.Drognan = getLocaleString(sdk.locale.npcs.Drognan).toLowerCase(); + this.Elzix = getLocaleString(sdk.locale.npcs.Elzix).toLowerCase(); + this.Greiz = getLocaleString(sdk.locale.npcs.Greiz).toLowerCase(); + this.Lysander = getLocaleString(sdk.locale.npcs.Lysander).toLowerCase(); + this.Jerhyn = getLocaleString(sdk.locale.npcs.Jerhyn).toLowerCase(); + this.Meshif = getLocaleString(sdk.locale.npcs.Meshif).toLowerCase(); + this.Atma = getLocaleString(sdk.locale.npcs.Atma).toLowerCase(); + + this.Ormus = getLocaleString(sdk.locale.npcs.Ormus).toLowerCase(); + this.Alkor = getLocaleString(sdk.locale.npcs.Alkor).toLowerCase(); + this.Hratli = getLocaleString(sdk.locale.npcs.Hratli).toLowerCase(); + this.Asheara = getLocaleString(sdk.locale.npcs.Asheara).toLowerCase(); + + this.Jamella = getLocaleString(sdk.locale.npcs.Jamella).toLowerCase(); + this.Halbu = getLocaleString(sdk.locale.npcs.Halbu).toLowerCase(); + this.Tyrael = getLocaleString(sdk.locale.npcs.Tyrael).toLowerCase(); + + this.Malah = getLocaleString(sdk.locale.npcs.Malah).toLowerCase(); + this.Anya = getLocaleString(sdk.locale.npcs.Anya).toLowerCase(); + this.Larzuk = getLocaleString(sdk.locale.npcs.Larzuk).toLowerCase(); + this.Qual_Kehk = getLocaleString(sdk.locale.npcs.QualKehk).toLowerCase(); + this.Nihlathak = getLocaleString(sdk.locale.npcs.Nihlathak2).toLowerCase(); + + this.Cain = getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase(); -Object.defineProperty(NPC, "getAct", { /** * Returns the act(s) where the given NPC can be found. - * - * @memberof NPC - * @method getAct * @param {string} name - The name of the NPC. * @returns {Array} An array of act numbers where the NPC can be found. */ - value: function (name) { + this.getAct = function (name) { if (name === NPC.Cain) return [me.act]; if (name === NPC.Warriv) return [1, 2]; if (name === NPC.Meshif) return [2, 3]; @@ -68,8 +60,10 @@ Object.defineProperty(NPC, "getAct", { return [5]; } return []; - }, - enumerable: false, + }; + Object.defineProperty(this, "getAct", { + enumerable: false, + }); }); /** @@ -79,45 +73,6 @@ const Town = { telekinesis: true, sellTimer: getTickCount(), // shop speedup test lastChores: 0, - /** - * @namespace - */ - lastInteractedNPC: { - tick: 0, - /** - * @type {{ name: string, gid: number, area: number}} - */ - unit: {}, - /** - * @param {NPCUnit} npc - */ - set: function (npc) { - if (npc.hasOwnProperty("name") && Object.values(NPC).includes(npc.name)) { - // valid npc - Town.lastInteractedNPC.unit.name = npc.name.toLowerCase(); - Town.lastInteractedNPC.unit.gid = npc.gid; - Town.lastInteractedNPC.unit.area = me.area; - Town.lastInteractedNPC.tick = getTickCount(); - } - }, - get: function () { - try { - if (!this.unit.hasOwnProperty("name")) return getInteractedNPC(); - if (getTickCount() - this.tick > Time.seconds(15)) return getInteractedNPC(); - if (this.unit.area !== me.area) return getInteractedNPC(); - return this.unit; - } catch (e) { - Config.DebugMode.Town && console.error(e); - this.reset(); - Config.DebugMode.Town && console.debug("getting new npc"); - return getInteractedNPC(); - } - }, - reset: function () { - Town.lastInteractedNPC.unit = {}; - Town.lastInteractedNPC.tick = 0; - } - }, tasks: (function () { /** @@ -163,8 +118,7 @@ const Town = { doChores: function (repair = false) { delay(250); - console.time("doChores"); - console.info(true); + console.info(true, null, "doChores"); /** * @todo Pre-build task list so we can more efficiently peform our chores @@ -220,8 +174,9 @@ const Town = { npcInteract: function (name = "", cancel = true) { // name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); // what about finding the closest name in case someone mispells it? - let npcKey = Object.keys(NPC) - .find(key => String.isEqual(key, name)); + const npcKey = Object.keys(NPC).find(function (key) { + return String.isEqual(key, name); + }); if (!npcKey) { // @todo handle if NPC object key is used instead of common name console.warn("Couldn't find " + name + " in NPC object"); @@ -349,7 +304,10 @@ const Town = { 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"); @@ -418,7 +376,8 @@ const Town = { } } - if (!npc || npc.area !== me.area || (!getUIFlag(sdk.uiflags.NPCMenu) && !npc.openMenu())) { + if (!npc || npc.area !== me.area + || (!getUIFlag(sdk.uiflags.NPCMenu) /* && !Town.move(wantedNpc) */ && !npc.openMenu())) { throw new Error("Couldn't interact with npc"); } @@ -468,7 +427,9 @@ const Town = { 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"); @@ -499,10 +460,11 @@ const Town = { let [needPots, needBuffer, specialCheck] = [false, true, false]; let col = this.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); + 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: @@ -518,14 +480,17 @@ const Town = { (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; } // 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; } } @@ -547,15 +512,21 @@ const Town = { 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((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); 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(function (i) { + return i.isInInventory; + }) + .first(); !!pot && Packet.placeInBelt(pot, i); pots.shift(); } else { @@ -567,7 +538,7 @@ const Town = { for (let i = 0; i < 4; i += 1) { if (col[i] > 0) { - let useShift = this.shiftCheck(col, beltSize); + const useShift = this.shiftCheck(col, beltSize); let pot = this.getPotion(npc, Config.BeltColumn[i]); if (pot) { @@ -759,7 +730,7 @@ const Town = { // 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) { const unid = !item.identified; const results = [Pickit.Result.UNID, Pickit.Result.TRASH]; return ((unid || Config.LowGold > 0) && (results.includes(Pickit.checkItem(item).result))); @@ -771,7 +742,9 @@ const Town = { if (!npc) return false; let tome = me.getTome(sdk.items.TomeofIdentify); - !!tome && tome.getStat(sdk.stats.Quantity) < list.length && Town.fillTome(sdk.items.TomeofIdentify); + if (!!tome && tome.getStat(sdk.stats.Quantity) < list.length) { + Town.fillTome(sdk.items.TomeofIdentify); + } MainLoop: while (list.length > 0) { @@ -972,16 +945,17 @@ const Town = { let npc = getInteractedNPC(); if (!npc || !npc.itemcount) return false; - let items = npc.getItemsEx() - .filter((item) => !Town.ignoreType(item.itemType)); + const items = npc.getItemsEx().filter(function (item) { + return !Town.ignoreType(item.itemType); + }); if (!items.length) return false; console.log("ÿc4MiniShopBotÿc0: Scanning " + npc.itemcount + " items."); for (let item of items) { - let result = Pickit.checkItem(item); + const { result, line } = Pickit.checkItem(item); - switch (result.result) { + switch (result) { case Pickit.Result.WANTED: case Pickit.Result.CUBING: case Pickit.Result.CRAFTING: @@ -989,7 +963,7 @@ const Town = { try { if (Storage.Inventory.CanFit(item) && me.gold >= item.getItemCost(sdk.items.cost.ToBuy)) { Item.logger("Shopped", item); - Item.logItem("Shopped", item, result.line); + Item.logItem("Shopped", item, line); item.buy(); } } catch (e) { @@ -1283,8 +1257,12 @@ const Town = { } return me.getItemsEx() - .filter(item => item.classid === sdk.items.Key && item.isInInventory) - .reduce((acc, curr) => acc + curr.getStat(sdk.stats.Quantity), 0); + .filter(function (item) { + return item.classid === sdk.items.Key && item.isInInventory; + }) + .reduce(function (acc, curr) { + return acc + curr.getStat(sdk.stats.Quantity); + }, 0); }, needKeys: function () { @@ -1346,7 +1324,9 @@ const Town = { if (!Config.CubeRepair || !me.cube) return false; let items = this.getItemsForRepair(Config.RepairPercent, false) - .sort((a, b) => a.durabilityPercent - b.durabilityPercent); + .sort(function (a, b) { + return a.durabilityPercent - b.durabilityPercent; + }); while (items.length > 0) { this.cubeRepairItem(items.shift()); @@ -1363,7 +1343,7 @@ const Town = { if (!item.isInStorage) return false; let rune, cubeItems; - let bodyLoc = item.bodylocation; + const bodyLoc = item.bodylocation; switch (item.itemType) { case sdk.items.type.Shield: @@ -1468,14 +1448,19 @@ const Town = { }, needRepair: function () { - let quiver, repairAction = []; - let canAfford = me.gold >= me.getRepairCost(); + const repairAction = []; + if (getInteractedNPC() && !getUIFlag(sdk.uiflags.Shop)) { + // fix crash with d2bs + me.cancel(); + } + const canAfford = me.gold >= me.getRepairCost(); + const quiverType = { bow: "aqv", crossbow: "cqv" }; // Arrow/Bolt check - let bowCheck = Attack.usingBow(); + const bowCheck = Attack.usingBow(); if (bowCheck) { - const quiverType = { bow: "aqv", crossbow: "cqv" }; + let quiver; if (quiverType[bowCheck]) { quiver = me.getItem(quiverType[bowCheck], sdk.items.mode.Equipped); } @@ -2385,14 +2370,13 @@ const Town = { if (returnWhenDone) return true; } } else if (me.act === 2 - && me.x > 5122 && me.y < 5049 + && me.x > 5122 && me.y <= 5049 && !String.isEqual(spot, NPC.Atma)) { // we are inside the building, if Atma is blocking the entrance we need the side door let atma = Game.getNPC(NPC.Atma); - // console.debug("atma", atma); // todo - might need to consider her targetx/y coords as well if (atma && (atma.x === 5136 || atma.x === 5137) - && (atma.y >= 5048 && atma.y <= 5050)) { + && (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]); @@ -2423,6 +2407,7 @@ const Town = { || typeof (Town.act[me.act].spot[spot]) !== "object") { return false; } + if (Town.getDistance(spot) < 5) return true; const npcSpot = Object.values(NPC).includes(spot.toLowerCase()); let longRange = (!Skill.haveTK && spot === "waypoint"); @@ -2507,6 +2492,10 @@ const Town = { * @returns {boolean} */ goToTown: function (act = 0, wpmenu = false) { + if (!act) return true; + if (act < 1 || act > 5) throw new Error("Town.goToTown: Invalid act"); + if (act > me.highestAct) return false; + if (!me.inTown) { try { // this can save us spamming portals @@ -2545,10 +2534,6 @@ const Town = { } } - 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); From 372110f08e26688d03be3477742af8a3e1f973c5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 13:09:59 -0400 Subject: [PATCH 266/758] Update Town.js - typo, this need to be below the `!inTown` check --- d2bs/kolbot/libs/core/Town.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 279e5c2d5..4ab42aab2 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -2492,10 +2492,6 @@ const Town = { * @returns {boolean} */ goToTown: function (act = 0, wpmenu = false) { - if (!act) return true; - if (act < 1 || act > 5) throw new Error("Town.goToTown: Invalid act"); - if (act > me.highestAct) return false; - if (!me.inTown) { try { // this can save us spamming portals @@ -2534,6 +2530,10 @@ const Town = { } } + 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); From d717516b74005275578a025a34c8b39f9e585267 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 19:31:02 -0400 Subject: [PATCH 267/758] Update main.js - track movement and perform quest refresh to prevent idle disconnect --- d2bs/kolbot/libs/manualplay/main.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index f45c99829..66cc4b911 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -164,6 +164,32 @@ function main () { return true; }; + Worker.runInBackground.antiIdle = (function () { + const last = { + area: me.area, + x: me.x, + y: me.y, + idleTick: getTickCount() + Time.seconds(rand(1200, 1500)), + }; + + return function () { + if (!me.gameReady) return true; + if (last.area !== me.area || last.distance > 10) { + last.area = me.area; + last.x = me.x; + last.y = me.y; + last.idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); + } + + if (getTickCount() - last.idleTick > 0) { + Packet.questRefresh(); + last.idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(last.idleTick - getTickCount()) + ")"); + } + return true; + }; + })(); + const log = function (msg = "") { me.overhead(msg); console.log(msg); From 37d4f87534dec966c5557315e03676cd8d414cb7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Aug 2023 20:21:01 -0400 Subject: [PATCH 268/758] typedefs --- d2bs/kolbot/sdk/types/Loader.d.ts | 6 ++++++ d2bs/kolbot/sdk/types/Town.d.ts | 11 ++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index c7e23aa68..a11f4b6af 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -13,4 +13,10 @@ declare global { function loadScripts(): void; function scriptName(offset?: number): void; } + + type Scripts = { + [key: string]: boolean; + }; + + const Scripts: Scripts; } diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts index 917b017b8..4606f79e2 100644 --- a/d2bs/kolbot/sdk/types/Town.d.ts +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -1,7 +1,7 @@ // @ts-nocheck declare global { type NPC = string; - export namespace NPC { + namespace NPC { function getAct(name: string): number[]; const Akara: string; const Gheed: string; @@ -30,18 +30,11 @@ declare global { const Nihlathak: string; const Cain: string; } - export namespace Town { + namespace Town { let telekinesis: boolean; let sellTimer: number; let lastChores: number; - export namespace lastInteractedNPC { - const unit: Unit; - const tick: number; - function set(npc: Unit): void; - function get(): any; - function reset(): void; - } const tasks: Map Date: Fri, 25 Aug 2023 15:01:03 -0400 Subject: [PATCH 269/758] add String prototypes `at` and `unshift` - `String.prototype.at` returns the character at a specified index - `String.prototype.unshift` concats a string to the beginning and returns the modifed string --- d2bs/kolbot/libs/Polyfill.js | 60 ++++++++++++++++++++++++++++++------ d2bs/kolbot/sdk/globals.d.ts | 2 ++ 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 2805343c1..459db37de 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -47,7 +47,11 @@ String.prototype.lcsGraph = function (compareToThis) { } } - return { a: this.toString(), b: compareToThis, graph: graph }; + return { + a: this.toString(), + b: compareToThis, + graph: graph + }; }; String.prototype.diffCount = function (stringB) { @@ -199,7 +203,7 @@ if (!String.isEqual) { String.prototype.format = function (...pairs) { if (!pairs.length) return this; let newString = this; - pairs.forEach(pair => { + pairs.forEach(function (pair) { let [match, replace] = pair; if (match === undefined || replace === undefined) return; newString = newString.replace(match, replace); @@ -207,6 +211,24 @@ String.prototype.format = function (...pairs) { return newString; }; +if (!String.prototype.at) { + String.prototype.at = function (pos) { + if (pos < 0) { + pos += this.length; + } + if (pos < 0 || pos >= this.length) return undefined; + return this[pos]; + }; +} + +if (!String.prototype.unshift) { + /** @param {string} str */ + String.prototype.unshift = function (str) { + if (typeof str !== "string") return this; + return str + this; + }; +} + /** * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Array Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @@ -426,6 +448,8 @@ if (!Array.prototype.compactMap) { // Returns a random object in array if (!Array.prototype.random) { Array.prototype.random = function () { + if (this.length === 0) return null; + if (this.length === 1) return this[0]; return this[Math.floor((Math.random() * this.length))]; }; } @@ -541,7 +565,7 @@ Array.prototype.fill = function (value, start = 0, end = undefined) { /** * @description Return the first element or undefined - * @return undefined|* + * @return {undefined | *} */ if (!Array.prototype.first) { Array.prototype.first = function () { @@ -551,7 +575,7 @@ if (!Array.prototype.first) { /** * @description Return the last element or undefined - * @return undefined|* + * @return {undefined | *} */ if (!Array.prototype.last) { Array.prototype.last = function () { @@ -562,7 +586,7 @@ if (!Array.prototype.last) { /** * @description Flatten an array with depth parameter. * @see https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/flat - * @return array + * @return {Array<*>} */ if (!Array.prototype.flat) { Object.defineProperty(Array.prototype, "flat", { @@ -615,7 +639,8 @@ if (!Array.prototype.toReversed) { /** * Creates a new array with the elements of the original array sorted in ascending order. * - * @param {Function} [compareFunction] A function that defines the sort order. + * @template T + * @param {function(T, T): number} [compareFunction] A function that defines the sort order. * If omitted, the elements are sorted in ascending order based on their string conversion. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted * @returns {Array} A new array with the elements sorted in ascending order. @@ -690,13 +715,19 @@ if (typeof Object.assign !== "function") { if (!Object.values) { Object.values = function (source) { - return Object.keys(source).map(function (k) { return source[k]; }); + 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]]; }); + return Object.keys(source) + .map(function (k) { + return [k, source[k]]; + }); }; } @@ -908,7 +939,11 @@ Set.prototype.difference = function (setB) { }; Set.prototype.toString = function () { - return JSON.stringify(this.values()); + let arr = []; + for (let item of this) { + arr.push(item); + } + return JSON.stringify(arr); }; /** @@ -1210,7 +1245,12 @@ if (!global.hasOwnProperty("includeCoreLibs")) { } // always include util first includeIfNotIncluded("core/Util.js"); - files.filter(file => file.endsWith(".js") && !obj.exclude.includes(file) && !file.match("util.js", "gi")) + files + .filter(function (file) { + return file.endsWith(".js") + && !obj.exclude.includes(file) + && !file.match("util.js", "gi"); + }) .forEach(function (x) { if (!includeIfNotIncluded("core/" + x)) { throw new Error("Failed to include core/" + x); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 5f2b56ca7..0411bfda6 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -90,6 +90,8 @@ declare global { format(...pairs: Array): string; padStart(targetLength: number, padString: string): string; padEnd(targetLength: number, padString: string): string; + at(index: number): string | undefined; + unshift(str: string): string; } interface StringConstructor { From 18cebb7f37e3e2beff7ef86ecfd151bf856a156c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:31:25 -0400 Subject: [PATCH 270/758] Update NTItemParser.js - minify checkItem a bit --- d2bs/kolbot/libs/core/NTItemParser.js | 194 +++++++++----------------- 1 file changed, 67 insertions(+), 127 deletions(-) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index ee3272f17..eb3a3d48a 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -335,8 +335,18 @@ NTIP.CheckItem = function (item, entryList, verbose) { return result; }; +/** @param {string} ch */ NTIP.IsSyntaxInt = function (ch) { - return (ch === "!" || ch === "%" || ch === "&" || (ch >= "(" && ch <= "+") || ch === "-" || ch === "/" || (ch >= ":" && ch <= "?") || ch === "|"); + return ( + ch === "!" + || ch === "%" + || ch === "&" + || (ch >= "(" && ch <= "+") + || ch === "-" + || ch === "/" + || (ch >= ":" && ch <= "?") + || ch === "|" + ); }; NTIP.ParseLineInt = function (input, info) { @@ -354,6 +364,44 @@ 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([ + ["color", NTIPAliasColor], + ["type", NTIPAliasType], + ["name", NTIPAliasClassID], + ["classid", NTIPAliasClassID], + ["class", NTIPAliasClass], + ["quality", NTIPAliasQuality], + ["flag", NTIPAliasFlag], + ["stat", NTIPAliasStat], + ]); + p_result = input.split("#"); if (p_result[0] && p_result[0].length > 4) { @@ -366,87 +414,25 @@ NTIP.ParseLineInt = function (input, info) { property = p_section[i].substring(0, p_end - 1); switch (property) { - 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("; - } else { - p_result[0] += "item.getFlag("; - } - - p_end += 2; - - 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("; - } - - 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; - 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) { @@ -473,69 +459,23 @@ NTIP.ParseLineInt = function (input, info) { 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]; - - 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]; + case "prefix": + case "suffix": + p_result[0] += "\"" + p_keyword + "\")"; break; - case "class": - if (NTIPAliasClass[p_keyword] === undefined) { - Misc.errorReport("Unknown class: " + p_keyword + " File: " + info.file + " Line: " + info.line); + default: + if (!_aliases.has(property)) { + Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); return false; - } - - 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); + } else if (_aliases.get(property)[p_keyword] === undefined) { + Misc.errorReport("Unknown " + property + ": " + 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; - } - - p_result[0] += NTIPAliasFlag[p_keyword] + ")"; - - break; - case "prefix": - case "suffix": - p_result[0] += "\"" + p_keyword + "\")"; + p_result[0] += _aliases.get(property)[p_keyword]; + property === "flag" && (p_result[0] += ")"); break; } From d3cb01398d79bd4356f0527873c67b42d28c47ee Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 25 Aug 2023 23:58:35 -0400 Subject: [PATCH 271/758] Update NTItemParser.js - defined shorthand aliases --- d2bs/kolbot/libs/core/NTItemParser.js | 244 ++++++++++++++------------ 1 file changed, 127 insertions(+), 117 deletions(-) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index eb3a3d48a..c028d77ce 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -365,6 +365,13 @@ NTIP.ParseLineInt = function (input, info) { } const _props = new Map([ + ["classid", "item.classid"], + ["name", "item.classid"], + ["type", "item.itemType"], + ["class", "item.itemclass"], + ["quality", "item.quality"], + ["level", "item.ilvl"], + ["flag", "item.getFlag("], ["wsm", 'getBaseStat("items", item.classid, "speed")'], ["weaponspeed", 'getBaseStat("items", item.classid, "speed")'], ["minimumsockets", 'getBaseStat("items", item.classid, "gemsockets")'], @@ -372,12 +379,6 @@ NTIP.ParseLineInt = function (input, info) { ["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")'], @@ -386,12 +387,22 @@ 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("] ]); const _aliases = new Map([ + ["n", "name"], + ["id", "classid"], + ["t", "type"], + ["q", "quality"], + ["lvl", "level"], + ["f", "flag"], + ["hc", "hardcore"], + ["cl", "classic"], + ]); + + const _lists = new Map([ ["color", NTIPAliasColor], ["type", NTIPAliasType], ["name", NTIPAliasClassID], @@ -404,155 +415,154 @@ NTIP.ParseLineInt = function (input, info) { p_result = input.split("#"); - if (p_result[0] && p_result[0].length > 4) { - p_section = p_result[0].split("["); + try { + 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 "flag": - case "prefix": - case "suffix": - if (p_section[i][p_end] === "!") { - p_result[0] += "!" + _props.get(property); - } else { - p_result[0] += _props.get(property); + if (_aliases.has(property)) { + property = _aliases.get(property); } - p_end += 2; + switch (property) { + case "flag": + case "prefix": + case "suffix": + if (p_section[i][p_end] === "!") { + p_result[0] += "!" + _props.get(property); + } else { + p_result[0] += _props.get(property); + } - break; - default: - if (!_props.has(property)) { - Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); - - return false; - } - p_result[0] += _props.get(property); - } + p_end += 2; - for (p_start = p_end; p_end < p_section[i].length; p_end += 1) { - if (!NTIP.IsSyntaxInt(p_section[i][p_end])) { break; + default: + if (!_props.has(property)) { + throw new Error("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); + } + p_result[0] += _props.get(property); } - } - 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; + } + } - if (p_section[i].substring(p_start, p_end) === "=") { - Misc.errorReport("Unexpected = at line " + info.line + " in " + info.file); + p_result[0] += p_section[i].substring(p_start, p_end); - 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) === "=") { + throw new Error("Unexpected = at line " + info.line + " in " + info.file); } - } - p_keyword = 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; + } + } - if (isNaN(p_keyword)) { - switch (property) { - case "prefix": - case "suffix": - p_result[0] += "\"" + p_keyword + "\")"; + p_keyword = p_section[i].substring(p_start, p_end); - break; - default: - if (!_aliases.has(property)) { - Misc.errorReport("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); + if (isNaN(p_keyword)) { + switch (property) { + case "prefix": + case "suffix": + p_result[0] += "\"" + p_keyword + "\")"; - return false; - } else if (_aliases.get(property)[p_keyword] === undefined) { - Misc.errorReport("Unknown " + property + ": " + p_keyword + " File: " + info.file + " Line: " + info.line); + break; + default: + if (!_lists.has(property)) { + throw new Error("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); + } else if (_lists.get(property)[p_keyword] === undefined) { + throw new Error("Unknown " + property + ": " + p_keyword + " File: " + info.file + " Line: " + info.line); + } + p_result[0] += _lists.get(property)[p_keyword]; + property === "flag" && (p_result[0] += ")"); - return false; + break; } - p_result[0] += _aliases.get(property)[p_keyword]; - property === "flag" && (p_result[0] += ")"); - - break; - } - } else { - if (property === "flag" || property === "prefix" || property === "suffix") { - p_result[0] += p_keyword + ")"; } else { - p_result[0] += p_keyword; + 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); + p_result[0] += p_section[i].substring(p_end); + } + } else { + p_result[0] = ""; } - } 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]; + 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); + 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); + if (isNaN(p_keyword)) { + if (NTIPAliasStat[p_keyword] === undefined) { + throw new Error("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] += "item.getStatEx(" + NTIPAliasStat[p_keyword] + ")"; - } else { - p_result[1] += "item.getStatEx(" + p_keyword + ")"; + p_result[1] += p_section[i].substring(p_end + 1); } - - p_result[1] += p_section[i].substring(p_end + 1); + } else { + p_result[1] = ""; } - } else { - p_result[1] = ""; - } - if (p_result[2] && p_result[2].length > 0) { - p_section = p_result[2].split("["); - p_result[2] = {}; + 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); + 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) { - case "maxquantity": - value = Number(p_section[i].split("==")[1].match(/\d+/g)); + let keyword = p_keyword.toLowerCase(); + switch (keyword) { + case "maxquantity": + value = Number(p_section[i].split("==")[1].match(/\d+/g)); - if (!isNaN(value)) { - p_result[2].MaxQuantity = value; - } + if (!isNaN(value)) { + p_result[2].MaxQuantity = value; + } - break; - case "merctier": - case "tier": - try { - // p_result[2].Tier = function(item) { return value }; - 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; + case "merctier": + case "tier": + try { + // p_result[2].Tier = function(item) { return value }; + 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) { + throw new Error("ÿc1Pickit Tier (" + keyword + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); + } + break; + default: + throw new Error("Unknown 3rd part keyword: " + p_keyword.toLowerCase() + " File: " + info.file + " Line: " + info.line); } - break; - - default: - Misc.errorReport("Unknown 3rd part keyword: " + p_keyword.toLowerCase() + " File: " + info.file + " Line: " + info.line); - return false; } } + } catch (e) { + Misc.errorReport(e); + + 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) { @@ -561,7 +571,7 @@ NTIP.ParseLineInt = function (input, info) { } 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 + return null; // failed load this line so return false } } else { p_result[i] = undefined; From 1ffa53da9a40673c5c1400ac64f69c3dba45adb1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 26 Aug 2023 00:57:55 -0400 Subject: [PATCH 272/758] Update NTItemParser.js - add charlvl prop so we can easily do `[name] == minorhealingpotion && [charlvl] <= 20` - added shorthand for charlvl -> `clvl` - added shorthand for level -> `ilvl` --- d2bs/kolbot/libs/core/NTItemParser.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index c028d77ce..b717632fb 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -370,6 +370,7 @@ NTIP.ParseLineInt = function (input, info) { ["type", "item.itemType"], ["class", "item.itemclass"], ["quality", "item.quality"], + ["charlvl", "me.charlvl"], ["level", "item.ilvl"], ["flag", "item.getFlag("], ["wsm", 'getBaseStat("items", item.classid, "speed")'], @@ -397,9 +398,11 @@ NTIP.ParseLineInt = function (input, info) { ["t", "type"], ["q", "quality"], ["lvl", "level"], + ["ilvl", "level"], ["f", "flag"], ["hc", "hardcore"], ["cl", "classic"], + ["clvl", "charlvl"], ]); const _lists = new Map([ From 4054d06192b0555a6fb775ff1296a96017fb2b5a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 26 Aug 2023 02:53:09 -0400 Subject: [PATCH 273/758] little bit of formatting --- d2bs/kolbot/libs/scripts/Abaddon.js | 4 ++-- d2bs/kolbot/libs/scripts/AncientTunnels.js | 6 +++--- d2bs/kolbot/libs/scripts/Baal.js | 2 +- d2bs/kolbot/libs/scripts/BattlemaidSarina.js | 4 ++-- d2bs/kolbot/libs/scripts/Bishibosh.js | 4 ++-- d2bs/kolbot/libs/scripts/BoneAsh.js | 2 +- .../kolbot/libs/scripts/ClassicChaosAssistant.js | 2 +- d2bs/kolbot/libs/scripts/Coldcrow.js | 2 +- d2bs/kolbot/libs/scripts/Coldworm.js | 6 +++--- d2bs/kolbot/libs/scripts/Corpsefire.js | 2 +- d2bs/kolbot/libs/scripts/Cows.js | 2 +- d2bs/kolbot/libs/scripts/Crafting.js | 16 ++++++++-------- d2bs/kolbot/libs/scripts/CreepingFeature.js | 2 +- d2bs/kolbot/libs/scripts/CrushTele.js | 2 +- d2bs/kolbot/libs/scripts/Diablo.js | 2 +- d2bs/kolbot/libs/scripts/DiabloHelper.js | 2 +- d2bs/kolbot/libs/scripts/Eldritch.js | 2 +- d2bs/kolbot/libs/scripts/Endugu.js | 2 +- d2bs/kolbot/libs/scripts/Fangskin.js | 2 +- d2bs/kolbot/libs/scripts/Follower.js | 1 + d2bs/kolbot/libs/scripts/Frozenstein.js | 2 +- d2bs/kolbot/libs/scripts/Gamble.js | 2 +- d2bs/kolbot/libs/scripts/GetCube.js | 2 +- d2bs/kolbot/libs/scripts/Hephasto.js | 4 ++-- d2bs/kolbot/libs/scripts/Icehawk.js | 2 +- d2bs/kolbot/libs/scripts/Izual.js | 4 ++-- d2bs/kolbot/libs/scripts/Pindleskin.js | 2 +- d2bs/kolbot/libs/scripts/Pit.js | 2 +- d2bs/kolbot/libs/scripts/Radament.js | 4 ++-- d2bs/kolbot/libs/scripts/Rakanishu.js | 2 +- d2bs/kolbot/libs/scripts/Rusher.js | 14 +++++++------- d2bs/kolbot/libs/scripts/SharpTooth.js | 4 ++-- d2bs/kolbot/libs/scripts/Smith.js | 4 ++-- d2bs/kolbot/libs/scripts/Snapchip.js | 4 ++-- d2bs/kolbot/libs/scripts/Stormtree.js | 2 +- d2bs/kolbot/libs/scripts/Summoner.js | 8 +++++--- d2bs/kolbot/libs/scripts/ThreshSocket.js | 2 +- 37 files changed, 67 insertions(+), 64 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Abaddon.js b/d2bs/kolbot/libs/scripts/Abaddon.js index 7751683dc..69d2995a4 100644 --- a/d2bs/kolbot/libs/scripts/Abaddon.js +++ b/d2bs/kolbot/libs/scripts/Abaddon.js @@ -5,12 +5,12 @@ * */ -function Abaddon() { +function Abaddon () { Town.doChores(); Pather.useWaypoint(sdk.areas.FrigidHighlands); Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.RedPortal) + if (!Pather.moveToPresetObject(sdk.areas.FrigidHighlands, sdk.objects.RedPortal) || !Pather.usePortal(sdk.areas.Abaddon)) { throw new Error("Failed to move to Abaddon"); } diff --git a/d2bs/kolbot/libs/scripts/AncientTunnels.js b/d2bs/kolbot/libs/scripts/AncientTunnels.js index b4ea73f8a..1d9d0576f 100644 --- a/d2bs/kolbot/libs/scripts/AncientTunnels.js +++ b/d2bs/kolbot/libs/scripts/AncientTunnels.js @@ -5,13 +5,13 @@ * */ -function AncientTunnels() { +function AncientTunnels () { Town.doChores(); Pather.useWaypoint(sdk.areas.LostCity); Precast.doPrecast(true); try { - if (Config.AncientTunnels.OpenChest && Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest)) { + if (Config.AncientTunnels.OpenChest && Pather.moveToPresetObject(me.area, sdk.objects.SuperChest)) { Misc.openChests(5) && Pickit.pickItems(); } } catch (e) { @@ -20,7 +20,7 @@ function AncientTunnels() { try { if (Config.AncientTunnels.KillDarkElder - && Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.DarkElder)) { + && Pather.moveToPresetMonster(me.area, sdk.monsters.preset.DarkElder)) { Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); } } catch (e) { diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index 1853324ca..c0b22a435 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -5,7 +5,7 @@ * */ -function Baal() { +function Baal () { include("core/Common/Baal.js"); const announce = function () { let count, string, souls, dolls; diff --git a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js index 71c15592b..c8bb7ca7a 100644 --- a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js +++ b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js @@ -5,13 +5,13 @@ * */ -function BattlemaidSarina() { +function BattlemaidSarina () { Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar); Precast.doPrecast(true); if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder)) { throw new Error("Failed to move near Sarina"); } diff --git a/d2bs/kolbot/libs/scripts/Bishibosh.js b/d2bs/kolbot/libs/scripts/Bishibosh.js index 055f3d35a..a370b2414 100644 --- a/d2bs/kolbot/libs/scripts/Bishibosh.js +++ b/d2bs/kolbot/libs/scripts/Bishibosh.js @@ -5,12 +5,12 @@ * */ -function Bishibosh() { +function Bishibosh () { Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.ColdPlains, sdk.unittype.Monster, sdk.monsters.preset.Bishibosh); + Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh); Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Bishibosh)); Pickit.pickItems(); diff --git a/d2bs/kolbot/libs/scripts/BoneAsh.js b/d2bs/kolbot/libs/scripts/BoneAsh.js index b5d70ce83..c0600e315 100644 --- a/d2bs/kolbot/libs/scripts/BoneAsh.js +++ b/d2bs/kolbot/libs/scripts/BoneAsh.js @@ -5,7 +5,7 @@ * */ -function BoneAsh() { +function BoneAsh () { Town.doChores(); Pather.useWaypoint(sdk.areas.InnerCloister); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js index a57f24e1a..b5966a8e5 100644 --- a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js +++ b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js @@ -7,7 +7,7 @@ // redo this, maybe different keys or chat commands instead? -function ClassicChaosAssistant() { +function ClassicChaosAssistant () { include("core/Common/Diablo.js"); let stargo, infgo, seisgo, vizgo, infseal, seisseal, vizseal, diablopickup, normalpickup = false; diff --git a/d2bs/kolbot/libs/scripts/Coldcrow.js b/d2bs/kolbot/libs/scripts/Coldcrow.js index 83fccbc5a..0b4fa642f 100644 --- a/d2bs/kolbot/libs/scripts/Coldcrow.js +++ b/d2bs/kolbot/libs/scripts/Coldcrow.js @@ -5,7 +5,7 @@ * */ -function Coldcrow() { +function Coldcrow () { Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Coldworm.js b/d2bs/kolbot/libs/scripts/Coldworm.js index e2d007fc5..d95ff5ea3 100644 --- a/d2bs/kolbot/libs/scripts/Coldworm.js +++ b/d2bs/kolbot/libs/scripts/Coldworm.js @@ -5,7 +5,7 @@ * */ -function Coldworm() { +function Coldworm () { Town.doChores(); Pather.useWaypoint(sdk.areas.FarOasis); Precast.doPrecast(true); @@ -13,7 +13,7 @@ function Coldworm() { // Beetleburst, added by 13ack.Stab if (Config.Coldworm.KillBeetleburst) { try { - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Beetleburst)) { + if (!Pather.moveToPresetMonster(me.area, sdk.monsters.preset.Beetleburst)) { throw new Error("Failed to move to Beetleburst"); } Attack.kill(getLocaleString(sdk.locale.monsters.Beetleburst)); @@ -29,7 +29,7 @@ function Coldworm() { if (Config.Coldworm.ClearMaggotLair) { Attack.clearLevel(Config.ClearType); } else { - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { throw new Error("Failed to move to Coldworm"); } Attack.kill(sdk.monsters.ColdwormtheBurrower); diff --git a/d2bs/kolbot/libs/scripts/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js index e7b3387bb..78bfae95a 100644 --- a/d2bs/kolbot/libs/scripts/Corpsefire.js +++ b/d2bs/kolbot/libs/scripts/Corpsefire.js @@ -5,7 +5,7 @@ * */ -function Corpsefire() { +function Corpsefire () { Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Cows.js b/d2bs/kolbot/libs/scripts/Cows.js index c70b89cc0..9975a40a8 100644 --- a/d2bs/kolbot/libs/scripts/Cows.js +++ b/d2bs/kolbot/libs/scripts/Cows.js @@ -5,7 +5,7 @@ * */ -function Cows() { +function Cows () { include("core/Common/Cows.js"); const getLeg = function () { diff --git a/d2bs/kolbot/libs/scripts/Crafting.js b/d2bs/kolbot/libs/scripts/Crafting.js index a114e32b3..987856504 100644 --- a/d2bs/kolbot/libs/scripts/Crafting.js +++ b/d2bs/kolbot/libs/scripts/Crafting.js @@ -8,7 +8,7 @@ let info; let gameRequest = false; -function Crafting() { +function Crafting () { info = CraftingSystem.getInfo(); if (!info || !info.worker) throw new Error("Bad Crafting System config."); @@ -119,7 +119,7 @@ function Crafting() { } } -function getNPCName(idList) { +function getNPCName (idList) { for (let i = 0; i < idList.length; i += 1) { switch (idList[i]) { case sdk.items.LightBelt: @@ -136,7 +136,7 @@ function getNPCName(idList) { return false; } -function countItems(idList, quality) { +function countItems (idList, quality) { let count = 0; let item = me.getItem(-1, sdk.items.mode.inStorage); @@ -151,7 +151,7 @@ function countItems(idList, quality) { return count; } -function updateInfo() { +function updateInfo () { if (info) { let items = me.findItems(-1, sdk.items.mode.inStorage); @@ -215,7 +215,7 @@ function updateInfo() { return false; } -function runewordIngredient(item) { +function runewordIngredient (item) { if (Runewords.validGids.includes(item.gid)) return true; let baseGids = []; @@ -230,7 +230,7 @@ function runewordIngredient(item) { return baseGids.includes(item.gid); } -function pickItems() { +function pickItems () { let items = []; let item = Game.getItem(-1, sdk.items.mode.onGround); @@ -256,7 +256,7 @@ function pickItems() { Town.stash(); } -function checkItem(item) { +function checkItem (item) { for (let i = 0; i < info.Sets.length; i += 1) { if (info.Sets[i].Enabled) { switch (info.Sets[i].Type) { @@ -285,7 +285,7 @@ function checkItem(item) { return false; } -function shopStuff(npcId, classids, amount) { +function shopStuff (npcId, classids, amount) { print("shopStuff: " + npcId + " " + amount); let wpArea, town, path, menuId, npc; diff --git a/d2bs/kolbot/libs/scripts/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js index 6055da8c8..5cec063f4 100644 --- a/d2bs/kolbot/libs/scripts/CreepingFeature.js +++ b/d2bs/kolbot/libs/scripts/CreepingFeature.js @@ -10,7 +10,7 @@ function CreepingFeature () { Town.goToTown(2); Pather.journeyTo(sdk.areas.StonyTombLvl2); - Pather.moveToPreset(sdk.areas.StonyTombLvl2, sdk.unittype.Monster, sdk.monsters.preset.CreepingFeature); + Pather.moveToPresetMonster(sdk.areas.StonyTombLvl2, sdk.monsters.preset.CreepingFeature); Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.CreepingFeature)); Pickit.pickItems(); diff --git a/d2bs/kolbot/libs/scripts/CrushTele.js b/d2bs/kolbot/libs/scripts/CrushTele.js index e470ed0af..b09b718d7 100644 --- a/d2bs/kolbot/libs/scripts/CrushTele.js +++ b/d2bs/kolbot/libs/scripts/CrushTele.js @@ -5,7 +5,7 @@ * */ -function CrushTele() { +function CrushTele () { let go = false; addEventListener("keyup", diff --git a/d2bs/kolbot/libs/scripts/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js index fa85a8bab..fa00eb9e4 100644 --- a/d2bs/kolbot/libs/scripts/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -9,7 +9,7 @@ * */ -function Diablo() { +function Diablo () { include("core/Common/Diablo.js"); Pather._teleport = Pather.teleport; Common.Diablo.clearRadius = Config.Diablo.ClearRadius; diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index cc6ba2267..d4a6c3cde 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -5,7 +5,7 @@ * */ -function DiabloHelper() { +function DiabloHelper () { include("core/Common/Diablo.js"); this.Leader = Config.Leader; Common.Diablo.waitForGlow = true; diff --git a/d2bs/kolbot/libs/scripts/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js index 7208a4a5d..015aebce8 100644 --- a/d2bs/kolbot/libs/scripts/Eldritch.js +++ b/d2bs/kolbot/libs/scripts/Eldritch.js @@ -5,7 +5,7 @@ * */ -function Eldritch() { +function Eldritch () { Town.doChores(); Pather.useWaypoint(sdk.areas.FrigidHighlands); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js index 5b734c6c0..b4f136180 100644 --- a/d2bs/kolbot/libs/scripts/Endugu.js +++ b/d2bs/kolbot/libs/scripts/Endugu.js @@ -11,7 +11,7 @@ function Endugu () { Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest)) { + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsBrainChest)) { throw new Error("Failed to move to Endugu"); } diff --git a/d2bs/kolbot/libs/scripts/Fangskin.js b/d2bs/kolbot/libs/scripts/Fangskin.js index cb3f9f45f..b91960d39 100644 --- a/d2bs/kolbot/libs/scripts/Fangskin.js +++ b/d2bs/kolbot/libs/scripts/Fangskin.js @@ -5,7 +5,7 @@ * */ -function Fangskin() { +function Fangskin () { Town.doChores(); Pather.useWaypoint(sdk.areas.LostCity); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 59986b338..04d1afcf5 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -824,6 +824,7 @@ function Follower () { }; const gameEvent = function (mode, param1, param2, name1, name2) { + console.log("gameevent", mode, param1, param2, name1, name2); if (name1 === Config.Leader && mode === 0x07 && param1 === 0x02 diff --git a/d2bs/kolbot/libs/scripts/Frozenstein.js b/d2bs/kolbot/libs/scripts/Frozenstein.js index 64892b4f6..d56fc06b4 100644 --- a/d2bs/kolbot/libs/scripts/Frozenstein.js +++ b/d2bs/kolbot/libs/scripts/Frozenstein.js @@ -5,7 +5,7 @@ * */ -function Frozenstein() { +function Frozenstein () { Town.doChores(); Pather.useWaypoint(sdk.areas.CrystalizedPassage); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Gamble.js b/d2bs/kolbot/libs/scripts/Gamble.js index 29125a5b2..ae27c6c5a 100644 --- a/d2bs/kolbot/libs/scripts/Gamble.js +++ b/d2bs/kolbot/libs/scripts/Gamble.js @@ -5,7 +5,7 @@ * */ -function Gamble() { +function Gamble () { let idleTick = 0; let info = Gambling.getInfo(); let needGold = false; diff --git a/d2bs/kolbot/libs/scripts/GetCube.js b/d2bs/kolbot/libs/scripts/GetCube.js index 8631b61b5..0b6c2afcc 100644 --- a/d2bs/kolbot/libs/scripts/GetCube.js +++ b/d2bs/kolbot/libs/scripts/GetCube.js @@ -5,7 +5,7 @@ * */ -function GetCube() { +function GetCube () { // Can't get the cube if we can't access the act if (!me.accessToAct(2)) return false; diff --git a/d2bs/kolbot/libs/scripts/Hephasto.js b/d2bs/kolbot/libs/scripts/Hephasto.js index 9cb31f93a..e862fdecf 100644 --- a/d2bs/kolbot/libs/scripts/Hephasto.js +++ b/d2bs/kolbot/libs/scripts/Hephasto.js @@ -5,12 +5,12 @@ * */ -function Hephasto() { +function Hephasto () { Town.doChores(); Pather.useWaypoint(sdk.areas.RiverofFlame); Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge)) { + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge)) { throw new Error("Failed to move to Hephasto"); } diff --git a/d2bs/kolbot/libs/scripts/Icehawk.js b/d2bs/kolbot/libs/scripts/Icehawk.js index 741bbfe8b..186281ed0 100644 --- a/d2bs/kolbot/libs/scripts/Icehawk.js +++ b/d2bs/kolbot/libs/scripts/Icehawk.js @@ -5,7 +5,7 @@ * */ -function Icehawk() { +function Icehawk () { Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Izual.js b/d2bs/kolbot/libs/scripts/Izual.js index 450e2e4c6..010e232f0 100644 --- a/d2bs/kolbot/libs/scripts/Izual.js +++ b/d2bs/kolbot/libs/scripts/Izual.js @@ -5,12 +5,12 @@ * */ -function Izual() { +function Izual () { Town.doChores(); Pather.useWaypoint(sdk.areas.CityoftheDamned); Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.PlainsofDespair, sdk.unittype.Monster, sdk.monsters.Izual)) { + if (!Pather.moveToPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual)) { throw new Error("Failed to move to Izual."); } diff --git a/d2bs/kolbot/libs/scripts/Pindleskin.js b/d2bs/kolbot/libs/scripts/Pindleskin.js index 7e915902c..ff4aca090 100644 --- a/d2bs/kolbot/libs/scripts/Pindleskin.js +++ b/d2bs/kolbot/libs/scripts/Pindleskin.js @@ -5,7 +5,7 @@ * */ -function Pindleskin() { +function Pindleskin () { Town.goToTown((Config.Pindleskin.UseWaypoint ? undefined : 5)); Town.doChores(); diff --git a/d2bs/kolbot/libs/scripts/Pit.js b/d2bs/kolbot/libs/scripts/Pit.js index 003a8d8dd..300d81476 100644 --- a/d2bs/kolbot/libs/scripts/Pit.js +++ b/d2bs/kolbot/libs/scripts/Pit.js @@ -5,7 +5,7 @@ * */ -function Pit() { +function Pit () { Town.doChores(); Pather.useWaypoint(sdk.areas.BlackMarsh); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Radament.js b/d2bs/kolbot/libs/scripts/Radament.js index c5558087a..5a9d175ed 100644 --- a/d2bs/kolbot/libs/scripts/Radament.js +++ b/d2bs/kolbot/libs/scripts/Radament.js @@ -5,13 +5,13 @@ * */ -function Radament() { +function Radament () { Town.doChores(); Pather.useWaypoint(sdk.areas.A2SewersLvl2); Precast.doPrecast(true); if (!Pather.moveToExit(sdk.areas.A2SewersLvl3, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest)) { + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricScrollChest)) { throw new Error("Failed to move to Radament"); } diff --git a/d2bs/kolbot/libs/scripts/Rakanishu.js b/d2bs/kolbot/libs/scripts/Rakanishu.js index 143083bdf..1544cfacb 100644 --- a/d2bs/kolbot/libs/scripts/Rakanishu.js +++ b/d2bs/kolbot/libs/scripts/Rakanishu.js @@ -5,7 +5,7 @@ * */ -function Rakanishu() { +function Rakanishu () { Town.doChores(); Pather.useWaypoint(sdk.areas.StonyField); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Rusher.js b/d2bs/kolbot/libs/scripts/Rusher.js index b387350af..c1c5e985d 100644 --- a/d2bs/kolbot/libs/scripts/Rusher.js +++ b/d2bs/kolbot/libs/scripts/Rusher.js @@ -14,7 +14,7 @@ * */ -function Rusher() { +function Rusher () { load("threads/rushthread.js"); delay(500); @@ -26,7 +26,7 @@ function Rusher() { ]; let rushThread = getScript("threads/rushthread.js"); - this.reloadThread = function () { + const reloadThread = function () { rushThread = getScript("threads/rushthread.js"); rushThread && rushThread.stop(); @@ -38,7 +38,7 @@ function Rusher() { delay(500); }; - this.getPartyAct = function () { + const getPartyAct = function () { let party = getParty(); let minArea = 999; @@ -58,7 +58,7 @@ function Rusher() { return sdk.areas.actOf(minArea); }; - this.chatEvent = function (nick, msg) { + const chatEvent = function (nick, msg) { if (nick !== me.name) { if (typeof msg !== "string") return; switch (msg) { @@ -111,7 +111,7 @@ function Rusher() { } }; - addEventListener("chatmsg", this.chatEvent); + addEventListener("chatmsg", chatEvent); while (Misc.getPartyCount() < Math.min(8, Config.Rusher.WaitPlayerCount)) { me.overhead("Waiting for players to join"); @@ -119,7 +119,7 @@ function Rusher() { } // Skip to a higher act if all party members are there - switch (this.getPartyAct()) { + switch (getPartyAct()) { case 2: say("Party is in act 2, starting from act 2"); rushThread.send("skiptoact 2"); @@ -212,7 +212,7 @@ function Rusher() { if (!isNaN(parseInt(command.split(" ")[1], 10)) && parseInt(command.split(" ")[1], 10) > 0 && parseInt(command.split(" ")[1], 10) <= 132) { - this.reloadThread(); + reloadThread(); rushThread.send(command); } else { say("Invalid area"); diff --git a/d2bs/kolbot/libs/scripts/SharpTooth.js b/d2bs/kolbot/libs/scripts/SharpTooth.js index 9659d8c68..9c66a67fa 100644 --- a/d2bs/kolbot/libs/scripts/SharpTooth.js +++ b/d2bs/kolbot/libs/scripts/SharpTooth.js @@ -5,14 +5,14 @@ * */ -function SharpTooth() { +function SharpTooth () { Town.doChores(); Pather.useWaypoint(sdk.areas.FrigidHighlands); Precast.doPrecast(true); // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); // Could this be causing crashes here? - if (!Pather.moveToPreset(sdk.areas.FrigidHighlands, sdk.unittype.Monster, sdk.monsters.preset.SharpToothSayer)) { + if (!Pather.moveToPresetMonster(sdk.areas.FrigidHighlands, sdk.monsters.preset.SharpToothSayer)) { throw new Error("Failed to move to Sharptooth Slayer"); } diff --git a/d2bs/kolbot/libs/scripts/Smith.js b/d2bs/kolbot/libs/scripts/Smith.js index 5fba16231..00799d314 100644 --- a/d2bs/kolbot/libs/scripts/Smith.js +++ b/d2bs/kolbot/libs/scripts/Smith.js @@ -5,12 +5,12 @@ * */ -function Smith() { +function Smith () { Town.doChores(); Pather.useWaypoint(sdk.areas.OuterCloister); Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { + if (!Pather.moveToPresetObject(sdk.areas.Barracks, sdk.quest.chest.MalusHolder)) { throw new Error("Failed to move to the Smith"); } diff --git a/d2bs/kolbot/libs/scripts/Snapchip.js b/d2bs/kolbot/libs/scripts/Snapchip.js index 3c2478d08..b116164d9 100644 --- a/d2bs/kolbot/libs/scripts/Snapchip.js +++ b/d2bs/kolbot/libs/scripts/Snapchip.js @@ -5,13 +5,13 @@ * */ -function Snapchip() { +function Snapchip () { Town.doChores(); Pather.useWaypoint(sdk.areas.AncientsWay); Precast.doPrecast(true); if (!Pather.moveToExit(sdk.areas.IcyCellar, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SmallSparklyChest)) { + || !Pather.moveToPresetObject(me.area, sdk.objects.SmallSparklyChest)) { throw new Error("Failed to move to Snapchip Shatter"); } diff --git a/d2bs/kolbot/libs/scripts/Stormtree.js b/d2bs/kolbot/libs/scripts/Stormtree.js index 0f891ecf4..17a7fe9ce 100644 --- a/d2bs/kolbot/libs/scripts/Stormtree.js +++ b/d2bs/kolbot/libs/scripts/Stormtree.js @@ -5,7 +5,7 @@ * */ -function Stormtree() { +function Stormtree () { Town.doChores(); Pather.useWaypoint(sdk.areas.LowerKurast); Precast.doPrecast(true); diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index 004a04689..ff3a3f32e 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -22,7 +22,7 @@ function Summoner () { if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) { throw new Error("Failed to move back to arcane"); } - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, -3, -3)) { + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.Journal, { offX: -3, offY: -3 })) { throw new Error("Failed to move to Summoner"); } @@ -39,7 +39,7 @@ function Summoner () { // couldnt find journal? Move to it's preset if (!journal) { - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.Journal); + Pather.moveToPresetObject(me.area, sdk.objects.Journal); continue; } else if (journal && journal.distance > (18 - i)) { Pather.moveNearUnit(journal, 13); @@ -56,7 +56,9 @@ function Summoner () { } if (me.inArea(sdk.areas.CanyonofMagic)) { - Loader.scriptName(1) === "Duriel" ? Loader.skipTown.push("Duriel") : Pather.useWaypoint(sdk.areas.LutGholein); + Loader.scriptName(1) === "Duriel" + ? Loader.skipTown.push("Duriel") + : Pather.useWaypoint(sdk.areas.LutGholein); } return true; diff --git a/d2bs/kolbot/libs/scripts/ThreshSocket.js b/d2bs/kolbot/libs/scripts/ThreshSocket.js index d7ef6f565..4159b0008 100644 --- a/d2bs/kolbot/libs/scripts/ThreshSocket.js +++ b/d2bs/kolbot/libs/scripts/ThreshSocket.js @@ -5,7 +5,7 @@ * */ -function ThreshSocket() { +function ThreshSocket () { Town.doChores(); Pather.useWaypoint(sdk.areas.ArreatPlateau); Precast.doPrecast(true); From 106841485cc436a8e24b3f56ce7bdba099de36b4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 28 Aug 2023 14:59:00 -0400 Subject: [PATCH 274/758] Fix getting inital mule data - method wanted master name before we had it - fix getting mule filename if we have the same profile with multiple muleobjs, this would be bad practice but handle it anyway --- d2bs/kolbot/D2BotMule.dbj | 11 +++++++++++ d2bs/kolbot/libs/systems/automule/Mule.js | 23 +++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index b84a11362..9e80ffd5f 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -510,6 +510,17 @@ function main () { Mule.continuous = muleObjs[0].obj.continuousMule; // we can use any of the enabled profiles MuleData.fileName = Mule.getMuleFilename(Mule.mode, Mule.obj.enabledProfiles[0], Mule.continuous); + } else if (muleObjs.some(muleObj => muleObj.obj.continuousMule)) { + // continuous mule doesn't wait for master profiles + // find the obj and we can assign our info directly + let _muleObj = muleObjs.find(function (muleObj) { + return muleObj.obj.continuousMule; + }); + Mule.obj = _muleObj.obj; + Mule.mode = _muleObj.mode; + Mule.continuous = _muleObj.obj.continuousMule; + // we can use any of the enabled profiles + MuleData.fileName = Mule.getMuleFilename(Mule.mode, Mule.obj.enabledProfiles[0], Mule.continuous); } else { // we need to wait for confirmation from the master while (!Mule.master) { diff --git a/d2bs/kolbot/libs/systems/automule/Mule.js b/d2bs/kolbot/libs/systems/automule/Mule.js index df56cd019..3bdcac8ea 100644 --- a/d2bs/kolbot/libs/systems/automule/Mule.js +++ b/d2bs/kolbot/libs/systems/automule/Mule.js @@ -283,7 +283,10 @@ const Mule = { break; } - // pick large items first by sorting items by size in descending order and move gheed's charm to the end of the list + /** + * pick large items first by sorting items by size in descending order + * and move gheed's charm to the end of the list + */ list.sort(function (a, b) { if (a.isGheeds && !Pickit.canPick(a)) return 1; if (b.isGheeds && !Pickit.canPick(b)) return -1; @@ -404,7 +407,7 @@ const Mule = { getMuleFilename: function (mode, master, continuous = false) { mode = mode || 0; let file; - let mule = mode > 0 ? AutoMule.TorchAnniMules : AutoMule.Mules; + const mule = mode > 0 ? AutoMule.TorchAnniMules : AutoMule.Mules; // Iterate through mule object for (let i in mule) { @@ -413,10 +416,12 @@ const Mule = { muleProfile, enabledProfiles, accountPrefix, + continuousMule, } = mule[i]; // Mule profile matches config if (muleProfile && String.isEqual(muleProfile, me.profile) && (continuous || enabledProfiles.includes(master) || enabledProfiles.includes("all"))) { + if (continuous && !continuousMule) continue; file = mode === 0 ? "logs/AutoMule." + i + ".json" : "logs/TorchMule." + i + ".json"; @@ -448,21 +453,19 @@ const Mule = { }, /** @returns {{ mode: number, obj: muleObj }[]} */ - getMuleInfo: function (master = "") { + getMuleInfo: function () { const data = []; /** * @param {muleObj} muleObj * @param {number} mode */ const checkObj = function (muleObj, mode) { - let { muleProfile, enabledProfiles, continuousMule } = muleObj; + let { muleProfile } = muleObj; if (muleProfile && String.isEqual(muleProfile, me.profile)) { - if (continuousMule || enabledProfiles.includes(master) || enabledProfiles.includes("all")) { - data.push({ - mode: mode, - obj: muleObj, - }); - } + data.push({ + mode: mode, + obj: muleObj, + }); } }; From ac312dc97c7cdcb4e9d73d5b89b2de193d4af38d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 29 Aug 2023 00:17:19 -0400 Subject: [PATCH 275/758] Update Baal.js - fix hard delay, only wait until throne baal is gone --- d2bs/kolbot/libs/core/Common/Baal.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 1af0a5c4b..56dce164c 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -284,7 +284,9 @@ Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done Pather.moveTo(15090, 5008); - delay(5000); + Misc.poll(function () { + return !Game.getMonster(sdk.monsters.ThroneBaal); + }, Time.seconds(5), 100); Precast.doPrecast(true); Misc.poll(function () { if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({ range: 15 })) { From 12d405b643d961f377b041f495d659af3e42dfc7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:01:17 -0400 Subject: [PATCH 276/758] Update D2BotGameAction.dbj - fix reference to old method --- d2bs/kolbot/D2BotGameAction.dbj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index f242808af..88b65d683 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -362,7 +362,7 @@ function main () { delay(1000); } - locationAction(getLocation()); + locationAction.run(getLocation()); delay(1000); } } From a206cc8eac7d0484ce1f920cf99df508d47e8be3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:03:16 -0400 Subject: [PATCH 277/758] Update D2BotMule.dbj - fix typo --- d2bs/kolbot/D2BotMule.dbj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 9e80ffd5f..f79e67358 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -529,7 +529,7 @@ function main () { console.log("Master found: " + Mule.master + " Mode: " + Mule.mode); Mule.obj = muleObjs.find(function (muleObj) { return muleObj.obj.enabledProfiles.includes(Mule.master) || muleObj.obj.enabledProfiles.includes("all"); - }); + }).obj; Mule.continuous = Mule.obj.continuousMule; MuleData.fileName = Mule.getMuleFilename(Mule.mode, Mule.master, Mule.continuous); } From b6ef7c7d19b305e4c3ab68de78ebd60028ce6c09 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:07:35 -0400 Subject: [PATCH 278/758] Update D2BotMuleLog.dbj - release key when mulelogging is done --- d2bs/kolbot/D2BotMuleLog.dbj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 444bf6ff3..5100585fc 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -132,7 +132,7 @@ const locationAction = (function () { if (!accounts.length) { MuleLogger.remove(); D2Bot.printToConsole("Done logging mules!"); - D2Bot.stop(); + D2Bot.stop(me.profile, true); return; } @@ -285,7 +285,7 @@ function main () { if (Starter.gameInfo.rdBlocker) { D2Bot.printToConsole("You must disable RD Blocker for Mule Logger to work properly. Stopping."); - D2Bot.stop(); + D2Bot.stop(me.profile, true); return; } From 31942c8168ed7411386014a099fc474dc6b5f29a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 31 Aug 2023 18:11:22 -0400 Subject: [PATCH 279/758] Update D2BotMuleLog.dbj - remove duplicate WaitingInLine and CreateGame locations - add ftj handler, retry 5 times with increased wait time and if still failing log it and move to the next --- d2bs/kolbot/D2BotMuleLog.dbj | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 5100585fc..80a056fed 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -42,6 +42,7 @@ const locationAction = (function () { let currAcc; let charList = []; let obj = {}; + let ftjRetry = 0; const Controls = require("./libs/modules/Control"); const { @@ -76,12 +77,6 @@ const locationAction = (function () { Starter.LocationEvents.openCreateGameWindow(); } ); - addLocations([sdk.game.locations.WaitingInLine, sdk.game.locations.CreateGame], - function () { - Controls.CancelCreateGame.click(); - Controls.JoinGameWindow.click(); - } - ); locations.set(sdk.game.locations.CreateGame, function (location) { D2Bot.updateStatus("Creating Game"); @@ -101,9 +96,17 @@ const locationAction = (function () { } if (Starter.lastGameStatus === "pending") { + Starter.gameCount += 1; + ftjRetry++; D2Bot.printToConsole("Failed to create game"); + ControlAction.timeoutDelay("FTJ delay", Time.minutes(ftjRetry)); + if (ftjRetry > 5) { + ftjRetry = 0; + D2Bot.printToConsole("FTJ limit reached, failed to log: " + me.name); + Controls.LobbyQuit.click(); - Starter.gameCount += 1; + return; + } } ControlAction.timeoutDelay("Make Game Delay", Starter.Config.CreateGameDelay * 1e3); From ba8993f829e774808abb48bf1a809d2f4a0c0f3a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:59:39 -0400 Subject: [PATCH 280/758] Update OOG.js - cleanup mostly (always) - added `ControlAction.parseText` - accepts control location/type and returns it's text if any - added param `randNameOnFail` to `ControlAction.makeCharacter` - fixed the initialization of `Starter.profileInfo` object, the properties of the object parsed are all capitalized --- d2bs/kolbot/libs/OOG.js | 271 +++++++++++++++++++++++++--------------- 1 file changed, 173 insertions(+), 98 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index b20e9431f..7aec7cf3c 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -29,13 +29,20 @@ includeIfNotIncluded("oog/D2Bot.js"); // required const ControlAction = { mutedKey: false, - realms: { "uswest": 0, "useast": 1, "asia": 2, "europe": 3 }, + realms: { + "uswest": 0, + "west": 0, + "useast": 1, + "east": 1, + "asia": 2, + "europe": 3 + }, /** * @param {string} text * @param {number} time - in milliseconds - * @param {Function} [stopfunc] - * @param {*} [arg] + * @param {(arg: any) => boolean} [stopfunc] + * @param {any} [arg] */ timeoutDelay: function (text, time, stopfunc, arg) { let currTime = 0; @@ -74,8 +81,11 @@ includeIfNotIncluded("oog/D2Bot.js"); // required let control = getControl(type, x, y, xsize, ysize); if (!control) { - print("control not found " + type + " " + x + " " + y + " " + xsize + " " + ysize + " location " + getLocation()); - + console.error( + "control not found " + type + " " + + x + " " + y + " " + xsize + " " + ysize + + " location " + getLocation() + ); return false; } @@ -104,8 +114,11 @@ includeIfNotIncluded("oog/D2Bot.js"); // required currText = control.getText(); - if (currText && ((typeof currText === "string" && currText === text) || (typeof currText === "object" && currText.includes(text)))) { - return true; + if (currText) { + if ((typeof currText === "string" && currText === text) + || (typeof currText === "object" && currText.includes(text))) { + return true; + } } control.setText(text); @@ -127,6 +140,22 @@ includeIfNotIncluded("oog/D2Bot.js"); // required return (!!control ? control.getText() : false); }, + /** + * @param {number} type + * @param {number} x + * @param {number} y + * @param {number} xsize + * @param {number} ysize + * @returns {string} + */ + parseText: function (type, x, y, xsize, ysize) { + let control = getControl(type, x, y, xsize, ysize); + if (!control) return ""; + let text = control.getText(); + if (!text || !text.length) return ""; + return text.join(" "); + }, + // ~~~ Start of general functions ~~~ // scrollDown: function () { me.blockMouse = true; @@ -231,10 +260,12 @@ includeIfNotIncluded("oog/D2Bot.js"); // required do { let text = control.getText(); - if (text instanceof Array && typeof text[1] === "string") { + if (Array.isArray(text) && typeof text[1] === "string") { count++; if (String.isEqual(text[1], info.charName)) { + if (info.ladder && !text.some(el => el.includes("LADDER"))) continue; + // how to check hardcore? return control; } } @@ -335,84 +366,91 @@ includeIfNotIncluded("oog/D2Bot.js"); // required /** * @param {CharacterInfo} info + * @param {boolean} [randNameOnFail] * @returns {boolean} */ - makeCharacter: function (info) { - me.blockMouse = true; - !info.charClass && (info.charClass = "barbarian"); - (!info.charName || info.charName.length < 2 || info.charName.length > 15) && (info.charName = Starter.randomString(8, false)); - info.charName.match(/\d+/g) && (info.charName.replace(/\d+/g, "")); - !info.expansion && ["druid", "assassin"].includes(info.charClass) && (info.expansion = true); - - let clickCoords = []; - /** @type {Map 15) { + info.charName = Starter.randomString(8, false); + } + info.charName.match(/\d+/g) && (info.charName.replace(/\d+/g, "")); + if (!info.expansion && ["druid", "assassin"].includes(info.charClass)) { + info.expansion = true; + } - return false; - } + let clickCoords = []; + /** @type {Map Date: Sun, 3 Sep 2023 02:46:20 -0400 Subject: [PATCH 281/758] Update DataFile.js - add `DataFile.read` method, allows for a bot to read anothers datafile - add currentGame prop to datafiles - change stringfy to format it to be readable --- d2bs/kolbot/libs/oog/DataFile.js | 57 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/d2bs/kolbot/libs/oog/DataFile.js b/d2bs/kolbot/libs/oog/DataFile.js index 5c74a110b..9cc0dc041 100644 --- a/d2bs/kolbot/libs/oog/DataFile.js +++ b/d2bs/kolbot/libs/oog/DataFile.js @@ -17,28 +17,48 @@ includeIfNotIncluded("oog/FileAction.js"); } else { root.DataFile = factory(); } -}(this, function() { +}(this, function () { const DataFile = { _default: { - runs: 0, + handle: 0, + name: "", + level: 0, experience: 0, + gold: 0, deaths: 0, + runs: 0, lastArea: "", - gold: 0, - level: 0, - name: "", - gameName: "", ingameTick: 0, - handle: 0, + gameName: "", + currentGame: "", nextGame: "" }, create: function () { - FileAction.write("data/" + me.profile + ".json", JSON.stringify(this._default)); + FileAction.write("data/" + me.profile + ".json", JSON.stringify(this._default, null, 2)); return this._default; }, + /** + * @param {string} profile + * @returns {DataFileObj | null} + */ + read: function (profile) { + if (!profile) return null; + if (!FileTools.exists("data/" + profile + ".json")) return null; + let string = FileAction.read("data/" + profile + ".json"); + + try { + let obj = JSON.parse(string); + return obj; + } catch (e) { + console.error(e); + + return null; + } + }, + getObj: function () { !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); @@ -74,13 +94,16 @@ includeIfNotIncluded("oog/FileAction.js"); let statArr = []; - typeof arg === "object" && (statArr = arg.slice()); - typeof arg === "string" && statArr.push(arg); + if (Array.isArray(arg)) { + statArr = arg.slice(); + } else if (typeof arg === "string") { + statArr.push(arg); + } let obj = this.getObj(); - for (let i = 0; i < statArr.length; i += 1) { - switch (statArr[i]) { + for (let prop of statArr) { + switch (prop) { case "experience": obj.experience = me.getStat(sdk.stats.Experience); obj.level = me.getStat(sdk.stats.Level); @@ -103,7 +126,11 @@ includeIfNotIncluded("oog/FileAction.js"); break; case "name": - obj.name = me.name; + obj.name = me.charname; + + break; + case "currentGame": + obj.currentGame = me.ingame ? me.gamename : ""; break; case "ingameTick": @@ -115,13 +142,13 @@ includeIfNotIncluded("oog/FileAction.js"); break; default: - obj[statArr[i]] = value; + obj[prop] = value; break; } } - let string = JSON.stringify(obj); + let string = JSON.stringify(obj, null, 2); FileAction.write("data/" + me.profile + ".json", string); } From 4d95436ab3be627c813c6a01ff8ba8728b5710a6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 3 Sep 2023 02:47:29 -0400 Subject: [PATCH 282/758] Update Locations.js - reset currentGame when we hit the lobby locations --- d2bs/kolbot/libs/oog/Locations.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index cea50a536..10324af83 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -217,6 +217,7 @@ if (Starter.inGame || Starter.gameInfo.error) { !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); + DataFile.updateStats("currentGame", ""); if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); } @@ -252,6 +253,7 @@ Starter.lastGameStatus === "pending" && (Starter.gameCount += 1); if (Starter.inGame || Starter.gameInfo.error) { + DataFile.updateStats("currentGame", ""); !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { From 1d037d64b355e783c82581e94279bf2247fd6653 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 3 Sep 2023 02:51:50 -0400 Subject: [PATCH 283/758] Update D2BotLead.dbj - use a message queue to process copydata events - update current game once in game --- d2bs/kolbot/D2BotLead.dbj | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 38d92757e..07fa375dc 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -51,7 +51,26 @@ const locationAction = (function () { function main () { debugLog(me.profile); - addEventListener("copydata", Starter.receiveCopyData); + + const Worker = require("./libs/modules/Worker"); + Worker.runInBackground.copyData = (function () { + const workBench = []; + addEventListener("copydata", function (mode, msg) { + workBench.push({ mode: mode, msg: msg }); + }); + + return function () { + if (!workBench.length) return true; + + while (workBench.length) { + const { mode, msg } = workBench.shift(); + Starter.receiveCopyData(mode, msg); + } + + return true; + }; + })(); + // addEventListener("copydata", Starter.receiveCopyData); addEventListener("scriptmsg", Starter.scriptMsgEvent); while (!Starter.handle) { @@ -106,8 +125,7 @@ function main () { Starter.lastGameStatus = "ingame"; Starter.inGame = true; - DataFile.updateStats("runs", Starter.gameCount); - DataFile.updateStats("ingameTick"); + DataFile.updateStats(["runs", "ingameTick", "currentGame"], Starter.gameCount); } D2Bot.updateStatus( From 311f6e38ec800e1bab541553e925243d777ebe72 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 3 Sep 2023 13:18:46 -0400 Subject: [PATCH 284/758] Update D2BotFollow.dbj - fix copydata spam from follower <-> leader. With the added prop of currentGame to datafiles we can ask once just to ensure that profile is active then we can read the datafile to see if the game has changed rather than spam request game every 2-5 seconds which can cause the leader to crash --- d2bs/kolbot/D2BotFollow.dbj | 50 ++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 17568be4c..ceb7ede14 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -49,6 +49,10 @@ const locationAction = (function () { let lastGameTick, leader = ""; const lastGame = []; + /** + * @type {Map} + */ + const tracker = new Map(); const Controls = require("./libs/modules/Control"); const { locations, @@ -152,21 +156,19 @@ const locationAction = (function () { function (location) { D2Bot.updateStatus("Join Game"); - if (!leader) { - leader = []; - + if (!tracker.size) { for (let i in JoinSettings) { if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { - for (let j = 0; j < JoinSettings[i].length; j += 1) { - if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { - leader.push(i); + for (let profile of JoinSettings[i]) { + if (profile === me.profile || profile === "all") { + tracker.set(i, { lastAsked: 0, game: "" }); } } } } } - if (!leader || !leader.length && !announced) { + if (!tracker.size && !announced) { console.log("No leader"); D2Bot.printToConsole("No leader"); announced = true; @@ -176,9 +178,23 @@ const locationAction = (function () { JoinLoop2: for (let i = 0; i < 5; i += 1) { - for (let j = 0; j < leader.length; j += 1) { + for (let [leader, check] of tracker) { + if (getTickCount() - check.lastAsked < Time.minutes(5)) { + if (check.game) { + // alright so we've already asked this profile and recieved a game but we didn't want to join + // instead of asking them again and spamming copydata. Lets read their datafile and see if they + // are still in game. If they are we'll wait for them to leave and ask again. If they aren't we'll + // ask again immediately. + let data = DataFile.read(leader); + if (data && !!data.currentGame && String.isEqual(data.currentGame, check.game)) { + D2Bot.updateStatus("Waiting for " + leader + " to leave " + check.game); + delay(Time.seconds(i + 1)); + continue; + } + } + } Starter.joinInfo = {}; - D2Bot.requestGame(leader[j]); + D2Bot.requestGame(leader); delay(100); if (!Starter.joinInfo.hasOwnProperty("gameName") || Starter.joinInfo.gameName === "") { @@ -191,7 +207,7 @@ const locationAction = (function () { * leader who's game we left get the current game time * and see if there is x amount of time left that makes it worth it vs waiting for next. */ - + tracker.set(leader, { lastAsked: getTickCount(), game: Starter.joinInfo.gameName }); if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { Controls.JoinGameName.setText(Starter.joinInfo.gameName); Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); @@ -272,14 +288,12 @@ const locationAction = (function () { locations.set(sdk.game.locations.TcpIpEnterIp, function () { try { - if (!leader) { - leader = []; - + if (!tracker.size) { for (let i in JoinSettings) { if (JoinSettings.hasOwnProperty(i) && typeof i === "string") { - for (let j = 0; j < JoinSettings[i].length; j += 1) { - if (JoinSettings[i][j] === me.profile || JoinSettings[i][j] === "all") { - leader.push(i); + for (let profile of JoinSettings[i]) { + if (profile === me.profile || profile === "all") { + tracker.set(i, { lastAsked: 0, game: "" }); } } } @@ -288,8 +302,8 @@ const locationAction = (function () { mainLoop: for (let i = 0; i < 3; i++) { - for (let j = 0; j < leader.length; j++) { - D2Bot.requestGame(leader[j]); + for (let [leader] of tracker) { + D2Bot.requestGame(leader); if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "") { break mainLoop; From dafae9aafd41286f18d00f8644fd79faec32d3f3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 3 Sep 2023 13:19:22 -0400 Subject: [PATCH 285/758] Update Polyfill.js - make sure both params are strings to prevent type error --- d2bs/kolbot/libs/Polyfill.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 459db37de..f57fad670 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -187,6 +187,7 @@ if (!String.isEqual) { * @returns {boolean} */ String.isEqual = function (str1, str2) { + if (typeof str1 !== "string" || typeof str2 !== "string") return false; return str1.toLowerCase() === str2.toLowerCase(); }; } From 654b404aeb9648b9b5e5071b5c0575b2deb9feb6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 3 Sep 2023 15:35:55 -0400 Subject: [PATCH 286/758] Create Idle.js - simple idle script to run as a companion to ControlBot --- d2bs/kolbot/libs/scripts/Idle.js | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 d2bs/kolbot/libs/scripts/Idle.js diff --git a/d2bs/kolbot/libs/scripts/Idle.js b/d2bs/kolbot/libs/scripts/Idle.js new file mode 100644 index 000000000..2d937f8ab --- /dev/null +++ b/d2bs/kolbot/libs/scripts/Idle.js @@ -0,0 +1,58 @@ +/** +* @filename Idle.js +* @author theBGuy +* @desc Idle companion script +* +*/ + +function Idle () { + const greet = []; + // eslint-disable-next-line no-unused-vars + function gameEvent (mode, param1, param2, name1, name2) { + switch (mode) { + case 0x02: + // idle in town + me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); + + break; + } + } + + try { + const startTime = getTickCount(); + let idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); + if (Config.Idle.Advertise) { + addEventListener("gameevent", gameEvent); + } + + while (true) { + if (!me.inArea(sdk.areas.RogueEncampment)) { + Town.goToTown(1); + } else if (Town.getDistance("stash") > 10) { + Town.move("stash"); + } + if (Config.Idle.MaxGameLength > 0 + && getTickCount() - startTime > Time.minutes(Config.Idle.MaxGameLength)) { + break; + } + if (Config.Idle.Advertise) { + while (greet.length) { + let name = greet.shift(); + say("!Welcome " + name + ". " + Config.Idle.AdvertiseMessage); + } + } + if (getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + + delay(1000); + } + + return true; + } finally { + // cleanup + removeEventListener("gameevent", gameEvent); + } +} From 33beec5ed630fcfb9bc7d118f973f2da85a9d1c1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 3 Sep 2023 16:18:54 -0400 Subject: [PATCH 287/758] Update NTItemAlias.js - add missing oskill stats --- d2bs/kolbot/libs/core/GameData/NTItemAlias.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/NTItemAlias.js b/d2bs/kolbot/libs/core/GameData/NTItemAlias.js index 1b47e4f05..86353cb38 100644 --- a/d2bs/kolbot/libs/core/GameData/NTItemAlias.js +++ b/d2bs/kolbot/libs/core/GameData/NTItemAlias.js @@ -936,17 +936,32 @@ NTIPAliasStat["itemnonclassskill"] = 97; // Amazon NTIPAliasStat["plusskillcriticalstrike"] = [97, 9]; NTIPAliasStat["plusskillguidedarrow"] = [97, 22]; +NTIPAliasStat["plusskillvalkyrie"] = [97, sdk.skills.Valkyrie]; // Sorceress -NTIPAliasStat["plusskillteleport"] = [97, 54]; +NTIPAliasStat["plusskillwarmth"] = [97, sdk.skills.Warmth]; +NTIPAliasStat["plusskillinferno"] = [97, sdk.skills.Inferno]; +NTIPAliasStat["plusskillfireball"] = [97, sdk.skills.FireBall]; +NTIPAliasStat["plusskillfirewall"] = [97, sdk.skills.FireWall]; +NTIPAliasStat["plusskillteleport"] = [97, sdk.skills.Teleport]; +NTIPAliasStat["plusskillmeteor"] = [97, sdk.skills.Meteor]; +NTIPAliasStat["plusskillfiremastery"] = [97, sdk.skills.FireMastery]; +NTIPAliasStat["plusskillhydra"] = [97, sdk.skills.Hydra]; // Barbarian +NTIPAliasStat["plusskillbattlecry"] = [97, 146]; NTIPAliasStat["plusskillbattleorders"] = [97, 149]; NTIPAliasStat["plusskillbattlecommand"] = [97, 155]; -NTIPAliasStat["plusskillbattlecry"] = [97, 146]; +NTIPAliasStat["plusskillwhirlwind"] = [97, sdk.skills.Whirlwind]; +NTIPAliasStat["plusskillberserk"] = [97, sdk.skills.Berserk]; // Druid NTIPAliasStat["plusskillwerewolf"] = [97, 223]; +NTIPAliasStat["plusskillwerebear"] = [97, sdk.skills.Werebear]; NTIPAliasStat["plusskillshapeshifting"] = [97, 224]; NTIPAliasStat["plusskilllycanthropy"] = [97, 224]; NTIPAliasStat["plusskillsummonspiritwolf"] = [97, 227]; NTIPAliasStat["plusskillferalrage"] = [97, 232]; +NTIPAliasStat["plusskillarticblast"] = [97, sdk.skills.ArcticBlast]; +// paladin +NTIPAliasStat["plusskillzeal"] = [97, sdk.skills.Zeal]; +NTIPAliasStat["plusskillvengeance"] = [97, sdk.skills.Vengeance]; NTIPAliasStat["state"] = 98; NTIPAliasStat["itemfastergethitrate"] = 99; NTIPAliasStat["fhr"] = 99; From 37d1bcd2012ddad71fec75542415ede4ca5a6cd9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:25:54 -0400 Subject: [PATCH 288/758] Update Me.js - add `me.clearBelt` method - add `me.cleanUpInvoPotions` method - add `me.needStash` method - add `me.needMerc` method - add `me.needRepair` method - add `me.needKeys` method - add `me.getItemsForRepair` method - add `me.checkKeys` method - each of these depreciates a corresponding Town method. They've been moved to `me` because they aren't actually town methods as they are only acting on ourselves --- d2bs/kolbot/libs/core/Me.js | 294 +++++++++++++++++++++++++++++++++++- 1 file changed, 289 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index cb8329f4e..533760657 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -78,13 +78,16 @@ me.findItems = function (id = -1, mode = -1, loc = false) { me.cancelUIFlags = function () { while (!me.gameReady) { - delay(25); + delay(3); } const flags = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, - sdk.uiflags.Waypoint, sdk.uiflags.Party, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Stash, - sdk.uiflags.Cube, sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.SubmitItem + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, + sdk.uiflags.SkillWindow, sdk.uiflags.NPCMenu, + sdk.uiflags.Waypoint, sdk.uiflags.Party, + sdk.uiflags.Shop, sdk.uiflags.Quest, + sdk.uiflags.Stash, sdk.uiflags.Cube, + sdk.uiflags.KeytotheCairnStonesScreen, sdk.uiflags.SubmitItem ]; for (let i = 0; i < flags.length; i++) { @@ -178,11 +181,117 @@ me.castingDuration = function (skillId, fcr = me.FCR, charClass = me.classid) { me.getWeaponQuantity = function (weaponLoc = sdk.body.RightArm) { let currItem = me.getItemsEx(-1, sdk.items.mode.Equipped) - .filter(i => i.bodylocation === weaponLoc) + .filter(function (i) { + return i.bodylocation === weaponLoc; + }) .first(); return !!currItem ? currItem.getStat(sdk.stats.Quantity) : 0; }; +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; +}; + +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 + const needCleanup = Storage.Belt.checkColumns(beltSize).some(slot => slot > 0); + + if (needCleanup) { + const potsInInventory = me.getItemsEx() + .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; + }); + + if (potsInInventory.length > 0 && Config.DebugMode.Town) { + 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)) { + const 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(function () { + return !me.itemoncursor; + }, 300, 30); + moved = Storage.Belt.checkColumns(beltSize)[i] === freeSpace[i] - 1; + } + Cubing.cursorCheck(); + } + }); + } + + return true; +}; + me.needPotions = function () { // we aren't using MinColumn if none of the values are set if (!Config.MinColumn.some(el => el > 0)) return false; @@ -413,6 +522,101 @@ me.needHealing = function () { })); }; +/** + * @description Check if stashing is needed, based on character config + * @returns {boolean} + */ +me.needStash = function () { + if (Config.StashGold + && me.getStat(sdk.stats.Gold) >= Config.StashGold + && me.getStat(sdk.stats.GoldBank) < 25e5) { + return true; + } + + return (Storage.Inventory.Compare(Config.Inventory) || []) + .some(function (item) { + return Storage.Stash.CanFit(item); + }); +}; + +/** + * @description Check if reviving merc is needed, based on character config + * @returns {boolean} + */ +me.needMerc = function () { + if (me.classic || !Config.UseMerc || me.gold < me.mercrevivecost || me.mercrevivecost === 0) { + return false; + } + + 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.getMerc(); + + if (!!merc && !merc.dead) { + return false; + } + + delay(100); + } + + // In case we never had a merc and Config.UseMerc is still set to true for some odd reason + return true; +}; + +me.needRepair = function () { + const repairAction = []; + if (getInteractedNPC() && !getUIFlag(sdk.uiflags.Shop)) { + console.debug("Checking need repair: Currently at NPC"); + // fix crash with d2bs + me.cancel(); + } + const canAfford = me.gold >= me.getRepairCost(); + const quiverType = { bow: "aqv", crossbow: "cqv" }; + + // Arrow/Bolt check + const bowCheck = Attack.usingBow(); + + if (bowCheck) { + let quiver; + if (quiverType[bowCheck]) { + quiver = me.getItem(quiverType[bowCheck], sdk.items.mode.Equipped); + } + + if (!quiver) { // Out of arrows/bolts + repairAction.push("buyQuiver"); + } else { + let quantity = quiver.getStat(sdk.stats.Quantity); + + if (typeof quantity === "number" + && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { + repairAction.push("buyQuiver"); + } + } + } + + // Repair durability/quantity/charges + if (canAfford) { + if (me.getItemsForRepair(Config.RepairPercent, true).length > 0) { + repairAction.push("repair"); + } + } else { + console.warn("Can't afford repairs."); + } + + return repairAction; +}; + +/** + * @description Check if buying keys is needed, based on character config + * @returns {boolean} + */ +me.needKeys = function () { + return me.checkKeys() <= 0; +}; + /** * @param {number} id * @returns {ItemUnit | null} @@ -430,6 +634,86 @@ me.getUnids = function () { }); }; +/** + * @param {number} repairPercent + * @param {boolean} chargedItems + * @returns {ItemUnit[]} + */ +me.getItemsForRepair = function (repairPercent, chargedItems) { + let itemList = []; + let item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + // Skip ethereal items + if (item.ethereal) continue; + // 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") { + let _maxStack = (getBaseStat("items", item.classid, "maxstack") + item.getStat(sdk.stats.ExtraStack)); + if (quantity * 100 / _maxStack <= repairPercent) { + itemList.push(copyUnit(item)); + } + } + + break; + // Durability check + default: + 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.checkKeys = function () { + if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 + || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { + return 12; + } + + return me.getItemsEx() + .filter(function (item) { + return item.classid === sdk.items.Key && item.isInInventory; + }) + .reduce(function (acc, curr) { + return acc + curr.getStat(sdk.stats.Quantity); + }, 0); +}; + // Identify items while in the field if we have a id tome me.fieldID = function () { let list = me.getUnids(); From 8557c7cbee04cf9f5d2d228856a318ae20f3e51d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:34:02 -0400 Subject: [PATCH 289/758] update with new `me` methods --- d2bs/kolbot/libs/core/Attacks/Amazon.js | 8 +- d2bs/kolbot/libs/core/Attacks/Assassin.js | 4 +- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 6 +- d2bs/kolbot/libs/core/Attacks/Druid.js | 4 +- d2bs/kolbot/libs/core/Attacks/Necromancer.js | 4 +- d2bs/kolbot/libs/core/Attacks/Paladin.js | 31 +- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 4 +- d2bs/kolbot/libs/core/Attacks/Wereform.js | 2 +- d2bs/kolbot/libs/core/Common/Tools.js | 2 +- d2bs/kolbot/libs/core/Config.js | 17 +- d2bs/kolbot/libs/core/Misc.js | 10 +- d2bs/kolbot/libs/core/Pickit.js | 7 +- d2bs/kolbot/libs/core/Town.js | 700 +++++++----------- .../kolbot/libs/manualplay/hooks/TextHooks.js | 2 +- .../libs/manualplay/libs/PatherOverrides.js | 6 +- .../libs/modules/workers/TownChicken.js | 2 +- d2bs/kolbot/libs/scripts/Follower.js | 2 +- d2bs/kolbot/threads/HeartBeat.js | 6 +- 18 files changed, 335 insertions(+), 482 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 354764b7e..ffd05759f 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -59,9 +59,9 @@ const ClassAttack = { if (!unit) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; - let needRepair = Town.needRepair(); + let needRepair = me.needRepair(); - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { print("towncheck"); if (Town.visitTown(!!needRepair.length)) { @@ -134,7 +134,7 @@ const ClassAttack = { } if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { @@ -170,7 +170,7 @@ const ClassAttack = { afterAttack: function () { Precast.doPrecast(false); - let needRepair = (Town.needRepair() || []); + let needRepair = (me.needRepair() || []); // Repair check, mainly to restock arrows needRepair.length > 0 && Town.visitTown(true); diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 0bb7b2426..5d61739e3 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -19,7 +19,7 @@ const ClassAttack = { Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { print("mercwatch"); if (Town.visitTown()) { @@ -140,7 +140,7 @@ const ClassAttack = { } if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index 50ea81328..d596d60af 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -20,9 +20,9 @@ const ClassAttack = { if (!unit) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; - let needRepair = Town.needRepair(); + let needRepair = me.needRepair(); - if ((Config.MercWatch && Town.needMerc()) || needRepair.length > 0) { + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { print("towncheck"); if (Town.visitTown(!!needRepair.length)) { @@ -92,7 +92,7 @@ const ClassAttack = { afterAttack: function (pickit = true) { Precast.doPrecast(false); - let needRepair = (Town.needRepair() || []); + let needRepair = (me.needRepair() || []); // Repair check needRepair.length > 0 && Town.visitTown(true); diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index 33ca71df2..da5c84e6f 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -11,7 +11,7 @@ const ClassAttack = { Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { print("mercwatch"); if (Town.visitTown()) { @@ -107,7 +107,7 @@ const ClassAttack = { } if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/d2bs/kolbot/libs/core/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js index 9e0625e5c..fc5ab57f9 100644 --- a/d2bs/kolbot/libs/core/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -137,7 +137,7 @@ const ClassAttack = { let [timedSkill, untimedSkill, customCurse] = [-1, -1, -1]; const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { print("mercwatch"); if (Town.visitTown()) { @@ -252,7 +252,7 @@ const ClassAttack = { } if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 4dcdb87a6..ff6faf755 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -18,7 +18,7 @@ const ClassAttack = { Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { print("mercwatch"); if (Town.visitTown()) { @@ -114,7 +114,7 @@ const ClassAttack = { } if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { @@ -320,10 +320,15 @@ const ClassAttack = { return false; }, + /** + * @param {Monster | Player} unit + * @returns {boolean} + */ getHammerPosition: function (unit) { - let x, y, positions, baseId = getBaseStat("monstats", unit.classid, "baseid"); + let x, y, positions; + const canTele = Pather.canTeleport(); + const baseId = getBaseStat("monstats", unit.classid, "baseid"); let size = getBaseStat("monstats2", baseId, "sizex"); - let canTele = Pather.canTeleport(); // in case base stat returns something outrageous (typeof size !== "number" || size < 1 || size > 3) && (size = 3); @@ -361,29 +366,33 @@ const ClassAttack = { 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, sdk.collision.BlockWalk, 0)) { + if (Attack.validSpot(check.x, check.y) + && !CollMap.checkColl(unit, check, sdk.collision.BlockWalk, 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); + // console.debug("Failed to find a hammer position for " + unit.name + " distance from me: " + unit.distance); return false; }, reposition: function (x, y) { if (typeof x !== "number" || typeof y !== "number") return false; - if ([x, y].distance > 0) { + const node = { x: x, y: y }; + if (node.distance > 0) { if (Pather.useTeleport()) { - [x, y].distance > 30 ? Pather.moveTo(x, y) : Pather.teleportTo(x, y, 3); + node.distance > 30 + ? Pather.moveTo(x, y) + : Pather.teleportTo(x, y, 3); } else { - if ([x, y].distance <= 4) { + if (node.distance <= 4) { Misc.click(0, 0, x, y); - } else if (!CollMap.checkColl(me, { x: x, y: y }, sdk.collision.BlockWalk, 3)) { + } else if (!CollMap.checkColl(me, node, sdk.collision.BlockWalk, 3)) { Pather.walkTo(x, y); } else { // don't clear while trying to reposition - Pather.moveToEx(x, y, { clearSettings: { allowClearing: false } }); + Pather.move(node, { clearSettings: { allowClearing: false } }); } delay(200); diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 108810f44..2591619e3 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -62,7 +62,7 @@ const ClassAttack = { Config.TeleSwitch && me.switchToPrimary(); let gid = unit.gid; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { if (Town.visitTown()) { print("mercwatch"); @@ -166,7 +166,7 @@ const ClassAttack = { } if (!unit) return Attack.Result.SUCCESS; - if (Town.needMerc()) { + if (me.needMerc()) { if (Config.MercWatch && mercRevive++ < 1) { Town.visitTown(); } else { diff --git a/d2bs/kolbot/libs/core/Attacks/Wereform.js b/d2bs/kolbot/libs/core/Attacks/Wereform.js index 54d137d67..3f013d74f 100644 --- a/d2bs/kolbot/libs/core/Attacks/Wereform.js +++ b/d2bs/kolbot/libs/core/Attacks/Wereform.js @@ -31,7 +31,7 @@ const ClassAttack = (function () { if (!unit) return Attack.Result.SUCCESS; let gid = unit.gid; - if (Config.MercWatch && Town.needMerc()) { + if (Config.MercWatch && me.needMerc()) { console.debug("mercwatch"); if (Town.visitTown()) { diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index a95e4b97c..07f7b0d50 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -126,7 +126,7 @@ quit(); } finally { while (me.ingame) { - delay(100); + delay(3); } } diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index ff95100e3..63e5ca6a7 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -60,8 +60,8 @@ let Config = { } } else { if (notify) { - print("ÿc1" + className + "." + me.charname + ".js not found!"); // Use the primary format - print("ÿc1Loading default config."); + console.log("ÿc1" + className + "." + me.charname + ".js not found!"); // Use the primary format + console.log("ÿc1Loading default config."); } // Try to find default config @@ -74,6 +74,7 @@ let Config = { if (!include("config/" + className + ".js")) { throw new Error(); } + Config._defaultLoaded = true; } catch (e) { throw new Error("ÿc1Failed to load default config."); } @@ -84,7 +85,7 @@ let Config = { Config.Loaded = true; } catch (e2) { if (notify) { - // print("ÿc8Error in " + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + "(line " + e2.lineNumber + "): " + e2.message); + // console.log("ÿc8Error in " + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + "(line " + e2.lineNumber + "): " + e2.message); console.error(e2); throw new Error("Config.init: Error in character config."); @@ -94,7 +95,7 @@ let 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) => print("Tryed to say: " + what); + global.say = (what) => console.log("Tryed to say: " + what); } try { @@ -102,12 +103,13 @@ let Config = { AutoBuild.initialize(); } } catch (e3) { - print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); + console.log("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); console.error(e3); } }, // dev + _defaultLoaded: false, Loaded: false, DebugMode: { Path: false, @@ -557,6 +559,11 @@ let Config = { Mode: -1, Wp: 35 }, + Idle: { + Advertise: false, + AdvertiseMessage: "", + MaxGameLength: 0, + }, ControlBot: { Bo: false, Cows: { diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 1301adee0..b5fbcab5c 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -593,7 +593,7 @@ const Misc = { if (Config.TownCheck && !me.inTown) { try { - if (me.needPotions() || (Config.OpenChests.Enabled && Town.needKeys())) { + if (me.needPotions() || (Config.OpenChests.Enabled && me.needKeys())) { check = true; } } catch (e) { @@ -650,7 +650,7 @@ const Misc = { let stackLog = ""; let date = new Date(); - let dateString = "[" + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)) + const dateString = "[" + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)) .toISOString().slice(0, -5).replace(/-/g, "/").replace("T", " ") + "]"; if (typeof error === "string") { @@ -660,8 +660,10 @@ const Misc = { } 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: " + me.area + ", Ping:" + me.ping + ", Game: " + me.gamename + ")"; + oogmsg = ( + "Error in " + script + " (" + source + " #" + error.lineNumber + ") " + error.message + + " (Area: " + me.area + ", Ping:" + me.ping + ", Game: " + me.gamename + ")" + ); filemsg = dateString + " <" + me.profile + "> " + msg.replace(/ÿc[0-9!"+<:;.*]/gi, "") + "\n"; if (error.hasOwnProperty("stack")) { diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 2f6ba3fea..405eafd6b 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -404,7 +404,8 @@ const Pickit = { continue; } } else { - if (item.distance > (Config.FastPick || i < 1 ? 6 : 4) || checkCollision(me, item, sdk.collision.BlockWall)) { + if (item.distance > (Config.FastPick || i < 1 ? 6 : 4) + || checkCollision(me, item, sdk.collision.BlockWall)) { if (!Pather.moveToEx(item.x, item.y, { retry: 3, allowPicking: false, minDist: 4 })) { continue; } @@ -446,7 +447,7 @@ const Pickit = { if (!item.onGroundOrDropping) { switch (stats.classid) { case sdk.items.Key: - console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc7(" + Town.checkKeys() + "/12)"); + console.log("ÿc7Picked up " + stats.color + stats.name + " ÿc7(" + me.checkKeys() + "/12)"); return true; case sdk.items.ScrollofTownPortal: @@ -577,7 +578,7 @@ const Pickit = { if (Pickit.pickList.some(function (el) { return _pots.includes(el.itemType); })) { - Town.clearBelt(); + me.clearBelt(); } while (Pickit.pickList.length > 0) { diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 4ab42aab2..04c9d3730 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -124,10 +124,15 @@ const Town = { * @todo Pre-build task list so we can more efficiently peform our chores */ - !me.inTown && this.goToTown(); - if (!Misc.poll(() => me.gameReady && me.inTown, 2000, 250)) throw new Error("Failed to go to town for chores"); + !me.inTown && Town.goToTown(); + const readyInTown = function () { + return me.gameReady && me.inTown; + }; + if (!Misc.poll(readyInTown, 2000, 250)) { + throw new Error("Failed to go to town for chores"); + } - let preAct = me.act; + const preAct = me.act; Pather.allowBroadcast = false; // Burst of speed while in town @@ -137,24 +142,24 @@ const Town = { me.switchToPrimary(); - this.heal(); - this.identify(); - this.clearInventory(); - this.fillTome(sdk.items.TomeofTownPortal); - this.buyPotions(); - Config.FieldID.Enabled && this.fillTome(sdk.items.TomeofIdentify); - this.shopItems(); - this.buyKeys(); - this.repair(repair); - this.gamble(); - this.reviveMerc(); + Town.heal(); + Town.identify(); + Town.clearInventory(); + Town.fillTome(sdk.items.TomeofTownPortal); + Town.buyPotions(); + Config.FieldID.Enabled && Town.fillTome(sdk.items.TomeofIdentify); + Town.shopItems(); + Town.buyKeys(); + Town.repair(repair); + Town.gamble(); + Town.reviveMerc(); Cubing.doCubing(); Runewords.makeRunewords(); - this.stash(true); - this.checkQuestItems(); - !!me.getItem(sdk.items.TomeofTownPortal) && this.clearScrolls(); + Town.stash(true); + Town.checkQuestItems(); + !!me.getItem(sdk.items.TomeofTownPortal) && Town.clearScrolls(); - me.act !== preAct && this.goToTown(preAct); + me.act !== preAct && Town.goToTown(preAct); me.cancelUIFlags(); !me.barbarian && Precast.haveCTA === -1 && Precast.doPrecast(false); @@ -172,7 +177,6 @@ const Town = { * @returns {boolean | Unit} */ npcInteract: function (name = "", cancel = true) { - // name = name.includes("_") ? "Qual_Kehk" : name.capitalize(true); // what about finding the closest name in case someone mispells it? const npcKey = Object.keys(NPC).find(function (key) { return String.isEqual(key, name); @@ -233,8 +237,9 @@ const Town = { // Act 1 // Tools of the trade if (!me.smith) { - let malus = me.getItem(sdk.items.quest.HoradricMalus); - !!malus && Town.goToTown(1) && Town.npcInteract("charsi"); + if (me.getItem(sdk.items.quest.HoradricMalus)) { + Town.goToTown(1) && Town.npcInteract("charsi"); + } } // Act 2 @@ -242,7 +247,7 @@ const Town = { if (!me.radament) { let book = me.getItem(sdk.quest.item.BookofSkill); if (book) { - book.isInStash && this.openStash() && delay(300 + me.ping); + book.isInStash && Town.openStash(); book.use(); } } @@ -262,7 +267,7 @@ const Town = { // Potion of life let pol = me.getItem(sdk.quest.item.PotofLife); if (pol) { - pol.isInStash && this.openStash() && delay(300 + me.ping); + pol.isInStash && Town.openStash(); pol.use(); } } @@ -281,7 +286,7 @@ const Town = { if (!me.anya) { let sor = me.getItem(sdk.items.quest.ScrollofResistance); if (sor) { - sor.isInStash && this.openStash() && delay(300 + me.ping); + sor.isInStash && Town.openStash(); sor.use(); } } @@ -338,7 +343,7 @@ const Town = { let choices = new Set(); let npcs = Town.tasks.get(me.act); let _needPots = me.needPotions(); - let _needRepair = Town.needRepair().length > 0; + let _needRepair = me.needRepair().length > 0; if (_needPots && _needRepair) { if (me.act === 2) { choices = new Set([npcs.Key, npcs.Repair]); @@ -371,7 +376,7 @@ const Town = { if (!npc && wantedNpc !== "undefined") { npc = Game.getNPC(wantedNpc); - if (!npc && this.move(wantedNpc)) { + if (!npc && Town.move(wantedNpc)) { npc = Game.getNPC(wantedNpc); } } @@ -429,7 +434,7 @@ const Town = { Misc.poll(function () { return me.gameReady; - }, 2000, 250); + }, 2000, 3); if (task === "Heal") { Config.DebugMode.Town && console.debug("Checking if we are frozen"); @@ -447,18 +452,18 @@ const Town = { */ heal: function () { if (!me.needHealing()) return true; - return !!(this.initNPC("Heal", "heal")); + return !!(Town.initNPC("Heal", "heal")); }, buyPotions: function () { // Ain't got money fo' dat shyt if (me.gold < 1000) return false; - this.clearBelt(); + me.clearBelt(); const buffer = { hp: 0, mp: 0 }; const beltSize = Storage.BeltSize(); let [needPots, needBuffer, specialCheck] = [false, true, false]; - let col = this.checkColumns(beltSize); + let col = Town.checkColumns(beltSize); const getNeededBuffer = function () { [buffer.hp, buffer.mp] = [0, 0]; @@ -506,9 +511,9 @@ const Town = { 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 - me.normal && me.highestAct >= 4 && me.act < 4 && this.goToTown(4); + me.normal && me.highestAct >= 4 && me.act < 4 && Town.goToTown(4); - let npc = this.initNPC("Shop", "buyPotions"); + 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 @@ -538,11 +543,11 @@ const Town = { for (let i = 0; i < 4; i += 1) { if (col[i] > 0) { - const useShift = this.shiftCheck(col, beltSize); - let pot = this.getPotion(npc, Config.BeltColumn[i]); + const useShift = Town.shiftCheck(col, beltSize); + let pot = Town.getPotion(npc, Config.BeltColumn[i]); if (pot) { - //console.log("ÿ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); @@ -554,7 +559,7 @@ const Town = { } } - col = this.checkColumns(beltSize); // Re-initialize columns (needed because 1 shift-buy can fill multiple columns) + col = Town.checkColumns(beltSize); // Re-initialize columns (needed because 1 shift-buy can fill multiple columns) } // re-check @@ -562,14 +567,14 @@ const Town = { if (needBuffer && buffer.hp < Config.HPBuffer) { for (let i = 0; i < Config.HPBuffer - buffer.hp; i += 1) { - let pot = this.getPotion(npc, "hp"); + let pot = Town.getPotion(npc, "hp"); !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); } } if (needBuffer && buffer.mp < Config.MPBuffer) { for (let i = 0; i < Config.MPBuffer - buffer.mp; i += 1) { - let pot = this.getPotion(npc, "mp"); + let pot = Town.getPotion(npc, "mp"); !!pot && Storage.Inventory.CanFit(pot) && pot.buy(false); } } @@ -660,9 +665,9 @@ const Town = { */ fillTome: function (classid) { if (me.gold < 450) return false; - if (this.checkScrolls(classid) >= 13) return true; + if (Town.checkScrolls(classid) >= 13) return true; - let npc = this.initNPC("Shop", "fillTome"); + let npc = Town.initNPC("Shop", "fillTome"); if (!npc) return false; delay(500); @@ -749,90 +754,88 @@ const Town = { MainLoop: while (list.length > 0) { const item = list.shift(); + if (item.identified || !item.isInInventory || Town.ignoreType(item.itemType)) continue; + let result = Pickit.checkItem(item); - if (!item.identified && item.isInInventory && !Town.ignoreType(item.itemType)) { - let result = Pickit.checkItem(item); + switch (result.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(); - switch (result.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 = tome ? tome : me.getIdTool(); + break; + case Pickit.Result.UNID: + let idTool = tome ? tome : me.getIdTool(); - if (idTool) { - this.identifyItem(item, idTool); - } else { - let scroll = npc.getItem(sdk.items.ScrollofIdentify); + if (idTool) { + Town.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); + if (scroll) { + if (!Storage.Inventory.CanFit(scroll)) { + let tpTome = me.getTome(sdk.items.TomeofTownPortal); - if (tpTome) { - tpTome.sell(); - delay(500); - } + if (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); + delay(500); - if (!scroll) { - break MainLoop; - } + Storage.Inventory.CanFit(scroll) && scroll.buy(); + } + + scroll = me.findItem(sdk.items.ScrollofIdentify, sdk.items.mode.inStorage, sdk.storage.Inventory); - this.identifyItem(item, scroll); + if (!scroll) { + break MainLoop; } - result = Pickit.checkItem(item); + Town.identifyItem(item, scroll); + } - switch (result.result) { - case Pickit.Result.WANTED: - Item.logger("Kept", item); - Item.logItem("Kept", item, result.line); + result = Pickit.checkItem(item); - break; - case Pickit.Result.UNID: - case Pickit.Result.RUNEWORD: // (doesn't trigger normally) - break; - case Pickit.Result.CUBING: - Item.logger("Kept", item, "Cubing-Town"); - Cubing.update(); + switch (result.result) { + case Pickit.Result.WANTED: + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); - break; - case Pickit.Result.CRAFTING: - Item.logger("Kept", item, "CraftSys-Town"); - CraftingSystem.update(item); + break; + case Pickit.Result.UNID: + case Pickit.Result.RUNEWORD: // (doesn't trigger normally) + break; + case Pickit.Result.CUBING: + Item.logger("Kept", item, "Cubing-Town"); + Cubing.update(); - break; - default: - Item.logger("Sold", item); - item.sell(); + break; + case Pickit.Result.CRAFTING: + Item.logger("Kept", item, "CraftSys-Town"); + CraftingSystem.update(item); - let timer = getTickCount() - this.sellTimer; // shop speedup test + break; + default: + Item.logger("Sold", item); + item.sell(); - if (timer > 0 && timer < 500) { - delay(timer); - } + let timer = getTickCount() - Town.sellTimer; // shop speedup test - break; + if (timer > 0 && timer < 500) { + delay(timer); } break; } + + break; } } - this.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls + Town.fillTome(sdk.items.TomeofTownPortal); // Check for TP tome in case it got sold for ID scrolls return true; }, @@ -846,7 +849,7 @@ const Town = { if (npc && npc.name.toLowerCase() === Town.tasks.get(me.act).Shop) return false; me.cancel(); - this.stash(false); + Town.stash(false); let unids = me.getUnids(); @@ -859,7 +862,7 @@ const Town = { if (Pickit.checkItem(item).result > 0) return false; } - let cain = this.initNPC("CainID", "cainID"); + let cain = Town.initNPC("CainID", "cainID"); if (!cain) return false; for (let item of unids) { @@ -892,45 +895,39 @@ const Town = { * @returns {boolean} */ identifyItem: function (unit, tome, packetID = false) { + if (!unit || unit.identified || !tome) return false; if (Config.PacketShopping || packetID) return Packet.identifyItem(unit, tome); - if (!unit || unit.identified) return false; Town.sellTimer = getTickCount(); // shop speedup test - CursorLoop: + const idOnCursor = function () { + return getCursorType() === sdk.cursortype.Identify; + }; + const unitIdentified = function () { + return unit.identified; + }; + for (let i = 0; i < 3; i += 1) { clickItem(sdk.clicktypes.click.item.Right, tome); - let tick = getTickCount(); - - while (getTickCount() - tick < 500) { - if (getCursorType() === sdk.cursortype.Identify) { - break CursorLoop; - } - - delay(10); + if (Misc.poll(idOnCursor, 500, 10)) { + break; } } - if (getCursorType() !== sdk.cursortype.Identify) return false; + if (!idOnCursor()) return false; delay(270); for (let i = 0; i < 3; i += 1) { - if (getCursorType() === sdk.cursortype.Identify) { + if (idOnCursor()) { clickItem(sdk.clicktypes.click.item.Left, unit); } - let tick = getTickCount(); - - while (getTickCount() - tick < 500) { - if (unit.identified) { - delay(50); - - return true; - } + if (Misc.poll(unitIdentified, 500, 10)) { + delay(25); - delay(10); + return true; } delay(300); @@ -981,27 +978,27 @@ const Town = { gambleIds: new Set(), gamble: function () { - if (!this.needGamble() || Config.GambleItems.length === 0) return true; - if (this.gambleIds.size === 0) { + if (!Town.needGamble() || Config.GambleItems.length === 0) return true; + if (Town.gambleIds.size === 0) { // change text to classid for (let item of Config.GambleItems) { if (isNaN(item)) { if (NTIPAliasClassID.hasOwnProperty(item.replace(/\s+/g, "").toLowerCase())) { - this.gambleIds.add(NTIPAliasClassID[item.replace(/\s+/g, "").toLowerCase()]); + Town.gambleIds.add(NTIPAliasClassID[item.replace(/\s+/g, "").toLowerCase()]); } else { Misc.errorReport("ÿc1Invalid gamble entry:ÿc0 " + item); } } else { - this.gambleIds.add(item); + Town.gambleIds.add(item); } } } - if (this.gambleIds.size === 0) return true; + if (Town.gambleIds.size === 0) return true; // avoid Alkor - me.act === 3 && this.goToTown(2); - let npc = this.initNPC("Gamble", "gamble"); + me.act === 3 && Town.goToTown(2); + let npc = Town.initNPC("Gamble", "gamble"); if (!npc) return false; @@ -1032,7 +1029,7 @@ const Town = { me.overhead("Buy: " + item.name); item.buy(false, true); - let newItem = this.getGambledItem(list); + let newItem = Town.getGambledItem(list); if (newItem) { let result = Pickit.checkItem(newItem); @@ -1228,12 +1225,12 @@ const Town = { }, buyKeys: function () { - if (!this.wantKeys()) return true; + if (me.checkKeys() >= 6) return true; // avoid Hratli - me.act === 3 && this.goToTown(me.accessToAct(4) ? 4 : 2); + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); - let npc = this.initNPC("Key", "buyKeys"); + let npc = Town.initNPC("Key", "buyKeys"); if (!npc) return false; let key = npc.getItem("key"); @@ -1250,27 +1247,15 @@ const Town = { return true; }, + /** @deprecated Use `me.checkKeys` instead */ checkKeys: function () { - if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 - || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { - return 12; - } - - return me.getItemsEx() - .filter(function (item) { - return item.classid === sdk.items.Key && item.isInInventory; - }) - .reduce(function (acc, curr) { - return acc + curr.getStat(sdk.stats.Quantity); - }, 0); + console.debug("Town.checkKeys is deprecated, use me.checkKeys instead"); + return me.checkKeys(); }, + /** @deprecated Use `me.needKeys` instead */ needKeys: function () { - return this.checkKeys() <= 0; - }, - - wantKeys: function () { - return this.checkKeys() <= 6; + return me.needKeys(); }, /** @@ -1280,7 +1265,7 @@ const Town = { if (!Config.CubeRepair) return false; let [have, needRal, needOrt] = [0, 0, 0]; - let items = this.getItemsForRepair(Config.RepairPercent, false); + let items = me.getItemsForRepair(Config.RepairPercent, false); if (items.length) { while (items.length > 0) { @@ -1323,13 +1308,13 @@ const Town = { cubeRepair: function () { if (!Config.CubeRepair || !me.cube) return false; - let items = this.getItemsForRepair(Config.RepairPercent, false) + let items = me.getItemsForRepair(Config.RepairPercent, false) .sort(function (a, b) { return a.durabilityPercent - b.durabilityPercent; }); while (items.length > 0) { - this.cubeRepairItem(items.shift()); + Town.cubeRepairItem(items.shift()); } return true; @@ -1408,19 +1393,19 @@ const Town = { * @param {boolean} [force=false] */ repair: function (force = false) { - if (this.cubeRepair()) return true; + if (Town.cubeRepair()) return true; let npc; - let repairAction = this.needRepair(); + let repairAction = me.needRepair(); force && repairAction.indexOf("repair") === -1 && repairAction.push("repair"); if (!repairAction || !repairAction.length) return true; - for (let i = 0; i < repairAction.length; i += 1) { - switch (repairAction[i]) { + for (let action of repairAction) { + switch (action) { case "repair": - me.act === 3 && this.goToTown(me.accessToAct(4) ? 4 : 2); - npc = this.initNPC("Repair", "repair"); + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); + npc = Town.initNPC("Repair", "repair"); if (!npc) return false; me.repair(); @@ -1433,7 +1418,7 @@ const Town = { let myQuiver = me.getItem(quiver, sdk.items.mode.Equipped); !!myQuiver && myQuiver.drop(); - npc = this.initNPC("Repair", "repair"); + npc = Town.initNPC("Repair", "repair"); if (!npc) return false; quiver = npc.getItem(quiver); @@ -1447,122 +1432,32 @@ const Town = { return true; }, + /** @deprecated Use `me.needRepair` instead */ needRepair: function () { - const repairAction = []; - if (getInteractedNPC() && !getUIFlag(sdk.uiflags.Shop)) { - // fix crash with d2bs - me.cancel(); - } - const canAfford = me.gold >= me.getRepairCost(); - const quiverType = { bow: "aqv", crossbow: "cqv" }; - - // Arrow/Bolt check - const bowCheck = Attack.usingBow(); - - if (bowCheck) { - let quiver; - if (quiverType[bowCheck]) { - quiver = me.getItem(quiverType[bowCheck], sdk.items.mode.Equipped); - } - - if (!quiver) { // Out of arrows/bolts - repairAction.push("buyQuiver"); - } else { - let quantity = quiver.getStat(sdk.stats.Quantity); - - if (typeof quantity === "number" - && quantity * 100 / getBaseStat("items", quiver.classid, "maxstack") <= Config.RepairPercent) { - repairAction.push("buyQuiver"); - } - } - } - - // Repair durability/quantity/charges - if (canAfford) { - if (this.getItemsForRepair(Config.RepairPercent, true).length > 0) { - repairAction.push("repair"); - } - } else { - console.warn("Can't afford repairs."); - } - - return repairAction; + console.debug("me.needRepair is deprecated. Use me.needRepair instead."); + return me.needRepair(); }, /** + * @deprecated Use `me.getItemsForRepair` instead * @param {number} repairPercent * @param {boolean} chargedItems * @returns {ItemUnit[]} */ getItemsForRepair: function (repairPercent, chargedItems) { - let itemList = []; - let item = me.getItem(-1, sdk.items.mode.Equipped); + console.debug("Town.getItemsForRepair is deprecated. Use me.getItemsForRepair instead."); - if (item) { - do { - // 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") { - let _maxStack = (getBaseStat("items", item.classid, "maxstack") + item.getStat(sdk.stats.ExtraStack)); - if (quantity * 100 / _maxStack <= repairPercent) { - itemList.push(copyUnit(item)); - } - } - - break; - // Durability check - default: - 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; + return me.getItemsForRepair(repairPercent, chargedItems); }, reviveMerc: function () { - if (!this.needMerc()) return true; + if (!me.needMerc()) return true; let preArea = me.area; // avoid Aheara - me.act === 3 && this.goToTown(me.accessToAct(4) ? 4 : 2); + me.act === 3 && Town.goToTown(me.accessToAct(4) ? 4 : 2); - let npc = this.initNPC("Merc", "reviveMerc"); + let npc = Town.initNPC("Merc", "reviveMerc"); if (!npc) return false; MainLoop: @@ -1609,23 +1504,10 @@ const Town = { return false; }, + /** @deprecated Use `me.needMerc` instead */ needMerc: function () { - 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.getMerc(); - - if (!!merc && !merc.dead) { - return false; - } - - delay(100); - } - - // In case we never had a merc and Config.UseMerc is still set to true for some odd reason - return true; + console.debug("Town.needMerc is deprecated. Use me.needMerc instead."); + return me.needMerc(); }, /** @@ -1696,8 +1578,8 @@ const Town = { */ getGem: function () { if (Loader.scriptName() === "GemHunter") { - if (this.getGemsInInv().length === 0 && this.getGemsInStash().length > 0) { - let gem = this.getGemsInStash().first(); + if (Town.getGemsInInv().length === 0 && Town.getGemsInStash().length > 0) { + let gem = Town.getGemsInStash().first(); Storage.Inventory.MoveTo(gem) && Item.logger("Inventoried", gem); return true; } @@ -1710,7 +1592,7 @@ const Town = { * @returns {boolean} */ stash: function (stashGold = true) { - if (!this.needStash()) return true; + if (!me.needStash()) return true; me.cancelUIFlags(); @@ -1720,7 +1602,7 @@ const Town = { Config.SortSettings.SortStash && Storage.Stash.SortItems(); for (let item of items) { - if (this.canStash(item)) { + if (Town.canStash(item)) { let result = false; let pickResult = Pickit.checkItem(item).result; @@ -1745,7 +1627,8 @@ const Town = { // 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 && Town.openStash()) { gold(me.getStat(sdk.stats.Gold), 3); delay(1000); // allow UI to initialize me.cancel(); @@ -1755,25 +1638,23 @@ const Town = { return true; }, + /** @deprecated Use `me.needStash` instead */ needStash: function () { - if (Config.StashGold - && me.getStat(sdk.stats.Gold) >= Config.StashGold - && me.getStat(sdk.stats.GoldBank) < 25e5) { - return true; - } - - return (Storage.Inventory.Compare(Config.Inventory) || []) - .some(item => Storage.Stash.CanFit(item)); + console.debug("Town.needStash is deprecated, use me.needStash instead"); + return me.needStash(); }, openStash: function () { if (getUIFlag(sdk.uiflags.Cube) && !Cubing.closeCube()) return false; if (getUIFlag(sdk.uiflags.Stash)) return true; + const stashOpened = function () { + return getUIFlag(sdk.uiflags.Stash); + }; for (let i = 0; i < 5; i += 1) { me.cancel(); - if (this.move("stash")) { + if (Town.move("stash")) { let stash = Game.getObject(sdk.objects.Stash); if (stash) { @@ -1789,7 +1670,7 @@ const Town = { Misc.click(0, 0, stash); } - if (Misc.poll(() => getUIFlag(sdk.uiflags.Stash), Time.seconds(5), 100)) { + if (Misc.poll(stashOpened, Time.seconds(5), 100)) { // allow UI to initialize delay(100 + pingDelay * (i + 1)); @@ -1818,7 +1699,8 @@ const Town = { if (!corpse) return true; do { - if (corpse.dead && corpse.name === me.name && (getDistance(me.x, me.y, corpse.x, corpse.y) <= 20 || me.inTown)) { + if (corpse.dead && corpse.name === me.name + && (getDistance(me.x, me.y, corpse.x, corpse.y) <= 20 || me.inTown)) { corpseList.push(copyUnit(corpse)); } } while (corpse.getNext()); @@ -1845,7 +1727,7 @@ const Town = { !Game.getPlayer(-1, -1, gid) && corpseList.shift(); } - me.classic && this.checkShard(); + me.classic && Town.checkShard(); // re-init skills since we started off without our body Skill.init(); @@ -1897,50 +1779,18 @@ const Town = { return true; }, + /** @deprecated Use `me.clearBelt` */ 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); - } - } + console.debug("Town.clearBelt is deprecated, use me.clearBelt instead"); - return true; + return me.clearBelt(); }, clearScrolls: function () { const scrolls = me.getItemsEx() - .filter((scroll) => scroll.isInInventory && scroll.itemType === sdk.items.type.Scroll); + .filter(function (scroll) { + return scroll.isInInventory && scroll.itemType === sdk.items.type.Scroll; + }); if (!scrolls.length) return false; const tpTome = scrolls.some(function (scroll) { return scroll.classid === sdk.items.ScrollofTownPortal; @@ -2005,12 +1855,12 @@ const Town = { }, clearInventory: function () { - console.info(true); - console.time("clearInventory"); + console.info(true, null, "clearInventory"); // 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); @@ -2019,78 +1869,55 @@ const Town = { } // Remove potions in the wrong slot of our belt - this.clearBelt(); + me.clearBelt(); // Return potions from inventory to belt - const beltSize = Storage.BeltSize(); - // belt 4x4 locations - /** - * 12 13 14 15 - * 8 9 10 11 - * 4 5 6 7 - * 0 1 2 3 - */ - const beltMax = (beltSize * 4); - const beltCapRef = [(0 + beltMax), (1 + beltMax), (2 + beltMax), (3 + beltMax)]; - let 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); - - Config.DebugMode.Town && potsInInventory.length > 0 && console.debug("clearInventory: start pots clean-up"); - // Start interating over all the pots we have in our inventory - potsInInventory.forEach(function (p) { - let moved = false; - // get free space in each slot of our belt - let freeSpace = Town.checkColumns(beltSize); - for (let i = 0; i < 4 && !moved; i++) { - // 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 = Town.checkColumns(beltSize)[i] === freeSpace[i] - 1; - } - Cubing.cursorCheck(); - } - }); + me.cleanUpInvoPotions(); // Cleanup remaining potions Config.DebugMode.Town && console.debug("clearInventory: start clean-up remaining pots"); let sellOrDrop = []; - 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)); + let potsInInventory = me.getItemsEx() + .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 = []; - 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)); - } + 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()); + } - return false; - }); + /** + * @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) { @@ -2116,7 +1943,7 @@ const Town = { // Any leftover items from a failed ID (crashed game, disconnect etc.) Config.DebugMode.Town && console.debug("clearInventory: start invo clean-up"); - let ignoreTypes = [ + const ignoreTypes = [ sdk.items.type.Book, sdk.items.type.Key, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion ]; @@ -2133,10 +1960,16 @@ const Town = { }); // add leftovers from potion cleanup - items = (items.length > 0 ? items.concat(sellOrDrop) : sellOrDrop.slice(0)); + items = (items.length > 0 + ? items.concat(sellOrDrop) + : sellOrDrop.slice(0) + ); if (items.length > 0) { - let sell = [], drop = []; + /** @type {ItemUnit[]} */ + let sell = []; + /** @type {ItemUnit[]} */ + let drop = []; // lets see if we have any items to sell items.forEach(function (item) { let result = Pickit.checkItem(item).result; @@ -2173,7 +2006,9 @@ const Town = { } // now lets see if we need to drop anything, so lets exit the shop me.cancelUIFlags(); - drop = drop.filter((item) => !!item && me.getItem(-1, sdk.items.mode.inStorage, item.gid)); + drop = drop.filter(function (item) { + return !!item && me.getItem(-1, sdk.items.mode.inStorage, item.gid); + }); } if (drop.length) { @@ -2286,24 +2121,24 @@ const Town = { initialize: function () { // console.log("Initialize town " + me.act); - if (!this.act[me.act].spot.initialized && me.act === 1) { + if (!Town.act[me.act].spot.initialized && me.act === 1) { // act 1 is the only act that needs to be initialized let wp = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1Waypoint); let fireUnit = Game.getPresetObject(sdk.areas.RogueEncampment, sdk.objects.A1TownFire); if (!fireUnit) return false; const fire = fireUnit.realCoords(); - this.act[1].spot.stash = [fire.x - 7, fire.y - 12]; - this.act[1].spot.fire = [fire.x, fire.y]; - this.act[1].spot[NPC.Warriv] = [fire.x - 5, fire.y - 2]; - this.act[1].spot[NPC.Cain] = [fire.x + 6, fire.y - 5]; - this.act[1].spot[NPC.Kashya] = [fire.x + 14, fire.y - 4]; - this.act[1].spot[NPC.Akara] = [fire.x + 56, fire.y - 30]; - this.act[1].spot[NPC.Charsi] = [fire.x - 39, fire.y - 25]; - this.act[1].spot[NPC.Gheed] = [fire.x - 34, fire.y + 36]; - this.act[1].spot.portalspot = [fire.x + 10, fire.y + 18]; - this.act[1].spot.waypoint = [wp.roomx * 5 + wp.x, wp.roomy * 5 + wp.y]; - this.act[1].initialized = true; + Town.act[1].spot.stash = [fire.x - 7, fire.y - 12]; + Town.act[1].spot.fire = [fire.x, fire.y]; + Town.act[1].spot[NPC.Warriv] = [fire.x - 5, fire.y - 2]; + Town.act[1].spot[NPC.Cain] = [fire.x + 6, fire.y - 5]; + Town.act[1].spot[NPC.Kashya] = [fire.x + 14, fire.y - 4]; + Town.act[1].spot[NPC.Akara] = [fire.x + 56, fire.y - 30]; + Town.act[1].spot[NPC.Charsi] = [fire.x - 39, fire.y - 25]; + Town.act[1].spot[NPC.Gheed] = [fire.x - 34, fire.y + 36]; + Town.act[1].spot.portalspot = [fire.x + 10, fire.y + 18]; + Town.act[1].spot.waypoint = [wp.roomx * 5 + wp.x, wp.roomy * 5 + wp.y]; + Town.act[1].initialized = true; } return true; @@ -2314,8 +2149,8 @@ const Town = { * @returns {number} distance to town location */ getDistance: function (spot = "") { - !me.inTown && this.goToTown(); - !Town.act[me.act].initialized && this.initialize(); + !me.inTown && Town.goToTown(); + !Town.act[me.act].initialized && Town.initialize(); // Act 5 wp->portalspot override - ActMap.cpp crash if (me.act === 5 && spot === "portalspot" @@ -2336,8 +2171,8 @@ const Town = { * @returns {boolean} */ move: function (spot = "", allowTK = true) { - !me.inTown && this.goToTown(); - !Town.act[me.act].initialized && this.initialize(); + !me.inTown && Town.goToTown(); + !Town.act[me.act].initialized && Town.initialize(); // act 5 static paths, ActMap.cpp seems to have issues with A5 // should other towns have static paths? @@ -2374,6 +2209,7 @@ const Town = { && !String.isEqual(spot, NPC.Atma)) { // we are inside the building, if Atma is blocking the entrance we need the side door let atma = Game.getNPC(NPC.Atma); + console.debug(" me { x: " + me.x + ", y: " + me.y + " } atma { x: " + atma.x + ", y: " + atma.y + " }"); // todo - might need to consider her targetx/y coords as well if (atma && (atma.x === 5136 || atma.x === 5137) && (atma.y >= 5048 && atma.y <= 5051)) { @@ -2386,7 +2222,7 @@ const Town = { for (let i = 0; i < 3; i += 1) { i === 2 && (allowTK = false); - if (this.moveToSpot(spot, allowTK)) { + if (Town.moveToSpot(spot, allowTK)) { return true; } @@ -2410,8 +2246,8 @@ const Town = { if (Town.getDistance(spot) < 5) return true; const npcSpot = Object.values(NPC).includes(spot.toLowerCase()); - let longRange = (!Skill.haveTK && spot === "waypoint"); - let tkRange = (Skill.haveTK && allowTK && ["stash", "portalspot", "waypoint"].includes(spot)); + const longRange = (!Skill.haveTK && spot === "waypoint"); + const tkRange = (Skill.haveTK && allowTK && ["stash", "portalspot", "waypoint"].includes(spot)); let townSpot = Town.act[me.act].spot[spot]; if (longRange) { @@ -2423,21 +2259,22 @@ const Town = { } for (let i = 0; i < townSpot.length; i += 2) { - const [x, y] = [townSpot[i], townSpot[i + 1]]; - // console.debug("moveToSpot: " + spot + " " + x + "/" + y + " from " + me.x + "/" + me.y); + /** @type {PathNode} */ + const node = { x: townSpot[i], y: townSpot[i + 1] }; + // console.debug("moveToSpot: " + spot + " " + node.x + "/" + node.y + " from " + me.x + "/" + me.y); if (tkRange) { Pather.moveNear(townSpot[0], townSpot[1], 19); - } else if (getDistance(me, x, y) > 2) { + } else if (node.distance > 2) { if (npcSpot) { let npc = Game.getNPC(spot); if (npc && npc.distance < 5) return true; - Pather.move({ x: x, y: y }, { callback: function () { + Pather.move(node, { callback: function () { let npc = Game.getNPC(spot); return npc && npc.distance < 5; } }); } else { - Pather.moveTo(x, y, 3, false); + Pather.move(node, { retry: 3 }); } } @@ -2461,7 +2298,7 @@ const Town = { return true; } - if (getDistance(me, x, y) < 10) { + if (node.distance < 10) { return true; } @@ -2496,7 +2333,6 @@ const Town = { try { // this can save us spamming portals let oldPortal = Pather.getPortal(sdk.areas.townOf(me.area), me.name); - // if (oldPortal && Pather.usePortal(null, me.name, oldPortal)) if ((oldPortal && !Pather.usePortal(null, me.name, oldPortal)) || !Pather.makePortal(true)) { console.warn("Town.goToTown: Failed to make TP"); @@ -2553,8 +2389,8 @@ const Town = { console.info(true); if (me.inTown) { - this.doChores(); - this.move("stash"); + Town.doChores(); + Town.move("stash"); return true; } @@ -2564,20 +2400,20 @@ const Town = { return false; } - let preArea = me.area; - let preAct = me.act; + const preArea = me.area; + const preAct = me.act; // not an essential function -> handle thrown errors try { - this.goToTown(); + Town.goToTown(); } catch (e) { return false; } - this.doChores(repair); + Town.doChores(repair); - me.act !== preAct && this.goToTown(preAct); - this.move("portalspot"); + me.act !== preAct && Town.goToTown(preAct); + Town.move("portalspot"); if (!Pather.usePortal(null, me.name)) { try { diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js index fb2a22bbc..e95b3e064 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js @@ -159,7 +159,7 @@ const TextHooks = { break; case "nextAct": - me.inTown && Pather.accessToAct(me.act + 1) && this.qolHooks.push({ + me.inTown && me.accessToAct(me.act + 1) && this.qolHooks.push({ name: "Next Act", dest: me.act + 1, type: "actChange", diff --git a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js index 43e53bc7f..9f6f52c83 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js @@ -46,7 +46,7 @@ Pather.changeAct = function (act) { npcUnit = Game.getNPC(NPC[npc]); wp = Game.getObject("waypoint"); - if (Pather.accessToAct(act) + if (me.accessToAct(act) && ((wp && !npcUnit) || (wp && npcUnit && getDistance(me, wp) < getDistance(me, npcUnit)) || (Town.getDistance("waypoint") < Town.getDistance(NPC[npc])))) { @@ -112,7 +112,7 @@ Pather.getWP = function (area, clearPath) { if (!getUIFlag(sdk.uiflags.Waypoint)) { if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Skill.cast(sdk.skills.Telekinesis, sdk.skills.hand.Right, wp); + Packet.telekinesis(wp); } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { this.moveToUnit(wp) && Misc.click(0, 0, wp); } @@ -350,8 +350,6 @@ Pather.moveTo = function (x, y, retry, clearPath, pop) { this.recursion = true; } } - - Misc.townCheck(); } } else { if (Pather.stop) { diff --git a/d2bs/kolbot/libs/modules/workers/TownChicken.js b/d2bs/kolbot/libs/modules/workers/TownChicken.js index 4ad348d09..c722192ee 100644 --- a/d2bs/kolbot/libs/modules/workers/TownChicken.js +++ b/d2bs/kolbot/libs/modules/workers/TownChicken.js @@ -325,7 +325,7 @@ if (!Config.TownCheck) return true; // can we chicken? if (!me.canTpToTown()) return true; - if (me.needBeltPots() || (Config.OpenChests.Enabled && Town.needKeys())) { + if (me.needBeltPots() || (Config.OpenChests.Enabled && me.needKeys())) { shouldChicken = true; } } diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 04d1afcf5..72c3654da 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -717,7 +717,7 @@ function Follower () { const pickPotions = function (range = 5) { if (me.dead) return false; - Town.clearBelt(); + me.clearBelt(); while (!me.idle) { delay(40); diff --git a/d2bs/kolbot/threads/HeartBeat.js b/d2bs/kolbot/threads/HeartBeat.js index 264ea0831..dd0efc209 100644 --- a/d2bs/kolbot/threads/HeartBeat.js +++ b/d2bs/kolbot/threads/HeartBeat.js @@ -5,12 +5,12 @@ * */ -function main() { +function main () { include("critical.js"); // required D2Bot.init(); console.log("Heartbeat loaded"); - function togglePause() { + function togglePause () { let script = getScript(); if (script) { @@ -31,7 +31,7 @@ function main() { } // Event functions - function KeyEvent(key) { + function KeyEvent (key) { switch (key) { case sdk.keys.PauseBreak: if (me.ingame) { From 0b80b98e1027b9f89a2beb1b6d3e98615c43fe4a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:35:09 -0400 Subject: [PATCH 290/758] create NPC.js - removed NPC namespace from Town.js --- d2bs/kolbot/libs/core/NPC.js | 67 +++++++++++++++++++++++++++++++++++ d2bs/kolbot/libs/core/Town.js | 64 --------------------------------- 2 files changed, 67 insertions(+), 64 deletions(-) create mode 100644 d2bs/kolbot/libs/core/NPC.js diff --git a/d2bs/kolbot/libs/core/NPC.js b/d2bs/kolbot/libs/core/NPC.js new file mode 100644 index 000000000..b7a6c8301 --- /dev/null +++ b/d2bs/kolbot/libs/core/NPC.js @@ -0,0 +1,67 @@ +/** +* @filename NPC.js +* @author kolton, theBGuy +* @desc Handle NPC object +* +*/ + +const NPC = (new function NPC () { + this.Akara = getLocaleString(sdk.locale.npcs.Akara).toLowerCase(); + this.Gheed = getLocaleString(sdk.locale.npcs.Gheed).toLowerCase(); + this.Charsi = getLocaleString(sdk.locale.npcs.Charsi).toLowerCase(); + this.Kashya = getLocaleString(sdk.locale.npcs.Kashya).toLowerCase(); + this.Warriv = getLocaleString(sdk.locale.npcs.Warriv).toLowerCase(); + + this.Fara = getLocaleString(sdk.locale.npcs.Fara).toLowerCase(); + this.Drognan = getLocaleString(sdk.locale.npcs.Drognan).toLowerCase(); + this.Elzix = getLocaleString(sdk.locale.npcs.Elzix).toLowerCase(); + this.Greiz = getLocaleString(sdk.locale.npcs.Greiz).toLowerCase(); + this.Lysander = getLocaleString(sdk.locale.npcs.Lysander).toLowerCase(); + this.Jerhyn = getLocaleString(sdk.locale.npcs.Jerhyn).toLowerCase(); + this.Meshif = getLocaleString(sdk.locale.npcs.Meshif).toLowerCase(); + this.Atma = getLocaleString(sdk.locale.npcs.Atma).toLowerCase(); + + this.Ormus = getLocaleString(sdk.locale.npcs.Ormus).toLowerCase(); + this.Alkor = getLocaleString(sdk.locale.npcs.Alkor).toLowerCase(); + this.Hratli = getLocaleString(sdk.locale.npcs.Hratli).toLowerCase(); + this.Asheara = getLocaleString(sdk.locale.npcs.Asheara).toLowerCase(); + + this.Jamella = getLocaleString(sdk.locale.npcs.Jamella).toLowerCase(); + this.Halbu = getLocaleString(sdk.locale.npcs.Halbu).toLowerCase(); + this.Tyrael = getLocaleString(sdk.locale.npcs.Tyrael).toLowerCase(); + + this.Malah = getLocaleString(sdk.locale.npcs.Malah).toLowerCase(); + this.Anya = getLocaleString(sdk.locale.npcs.Anya).toLowerCase(); + this.Larzuk = getLocaleString(sdk.locale.npcs.Larzuk).toLowerCase(); + this.Qual_Kehk = getLocaleString(sdk.locale.npcs.QualKehk).toLowerCase(); + this.Nihlathak = getLocaleString(sdk.locale.npcs.Nihlathak2).toLowerCase(); + + this.Cain = getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase(); + + /** + * Returns the act(s) where the given NPC can be found. + * @param {string} name - The name of the NPC. + * @returns {Array} An array of act numbers where the NPC can be found. + */ + this.getAct = function (name) { + if (name === NPC.Cain) return [me.act]; + if (name === NPC.Warriv) return [1, 2]; + if (name === NPC.Meshif) return [2, 3]; + switch (true) { + case [NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Warriv].includes(name): + return [1]; + case [NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Greiz, NPC.Lysander, NPC.Jerhyn, NPC.Atma].includes(name): + return [2]; + case [NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara].includes(name): + return [3]; + case [NPC.Jamella, NPC.Halbu, NPC.Tyrael].includes(name): + return [4]; + case [NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Nihlathak].includes(name): + return [5]; + } + return []; + }; + Object.defineProperty(this, "getAct", { + enumerable: false, + }); +}); diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 04c9d3730..fe513924b 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -5,70 +5,6 @@ * */ -const NPC = (new function NPC () { - this.Akara = getLocaleString(sdk.locale.npcs.Akara).toLowerCase(); - this.Gheed = getLocaleString(sdk.locale.npcs.Gheed).toLowerCase(); - this.Charsi = getLocaleString(sdk.locale.npcs.Charsi).toLowerCase(); - this.Kashya = getLocaleString(sdk.locale.npcs.Kashya).toLowerCase(); - this.Warriv = getLocaleString(sdk.locale.npcs.Warriv).toLowerCase(); - - this.Fara = getLocaleString(sdk.locale.npcs.Fara).toLowerCase(); - this.Drognan = getLocaleString(sdk.locale.npcs.Drognan).toLowerCase(); - this.Elzix = getLocaleString(sdk.locale.npcs.Elzix).toLowerCase(); - this.Greiz = getLocaleString(sdk.locale.npcs.Greiz).toLowerCase(); - this.Lysander = getLocaleString(sdk.locale.npcs.Lysander).toLowerCase(); - this.Jerhyn = getLocaleString(sdk.locale.npcs.Jerhyn).toLowerCase(); - this.Meshif = getLocaleString(sdk.locale.npcs.Meshif).toLowerCase(); - this.Atma = getLocaleString(sdk.locale.npcs.Atma).toLowerCase(); - - this.Ormus = getLocaleString(sdk.locale.npcs.Ormus).toLowerCase(); - this.Alkor = getLocaleString(sdk.locale.npcs.Alkor).toLowerCase(); - this.Hratli = getLocaleString(sdk.locale.npcs.Hratli).toLowerCase(); - this.Asheara = getLocaleString(sdk.locale.npcs.Asheara).toLowerCase(); - - this.Jamella = getLocaleString(sdk.locale.npcs.Jamella).toLowerCase(); - this.Halbu = getLocaleString(sdk.locale.npcs.Halbu).toLowerCase(); - this.Tyrael = getLocaleString(sdk.locale.npcs.Tyrael).toLowerCase(); - - this.Malah = getLocaleString(sdk.locale.npcs.Malah).toLowerCase(); - this.Anya = getLocaleString(sdk.locale.npcs.Anya).toLowerCase(); - this.Larzuk = getLocaleString(sdk.locale.npcs.Larzuk).toLowerCase(); - this.Qual_Kehk = getLocaleString(sdk.locale.npcs.QualKehk).toLowerCase(); - this.Nihlathak = getLocaleString(sdk.locale.npcs.Nihlathak2).toLowerCase(); - - this.Cain = getLocaleString(sdk.locale.npcs.DeckardCain).toLowerCase(); - - /** - * Returns the act(s) where the given NPC can be found. - * @param {string} name - The name of the NPC. - * @returns {Array} An array of act numbers where the NPC can be found. - */ - this.getAct = function (name) { - if (name === NPC.Cain) return [me.act]; - if (name === NPC.Warriv) return [1, 2]; - if (name === NPC.Meshif) return [2, 3]; - switch (true) { - case [NPC.Akara, NPC.Gheed, NPC.Charsi, NPC.Kashya, NPC.Warriv].includes(name): - return [1]; - case [NPC.Fara, NPC.Drognan, NPC.Elzix, NPC.Greiz, NPC.Lysander, NPC.Jerhyn, NPC.Atma].includes(name): - return [2]; - case [NPC.Ormus, NPC.Alkor, NPC.Hratli, NPC.Asheara].includes(name): - return [3]; - case [NPC.Jamella, NPC.Halbu, NPC.Tyrael].includes(name): - return [4]; - case [NPC.Malah, NPC.Anya, NPC.Larzuk, NPC.Qual_Kehk, NPC.Nihlathak].includes(name): - return [5]; - } - return []; - }; - Object.defineProperty(this, "getAct", { - enumerable: false, - }); -}); - -/** - * @namespace - */ const Town = { telekinesis: true, sellTimer: getTickCount(), // shop speedup test From e046c34f4cc3c682407239fe1f98051cd58848cd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:36:34 -0400 Subject: [PATCH 291/758] small bits of formatting --- d2bs/kolbot/libs/core/Attack.js | 7 +-- d2bs/kolbot/libs/core/Packet.js | 37 ++++++--------- d2bs/kolbot/libs/core/Pather.js | 71 ++++++++++++----------------- d2bs/kolbot/libs/scripts/Bonesaw.js | 2 +- 4 files changed, 47 insertions(+), 70 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index ccb1017b2..80c95afba 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -324,9 +324,11 @@ const Attack = { */ hurt: function (classId, percent) { if (!classId || !percent) return false; - let target = (typeof classId === "object" + const target = (typeof classId === "object" ? classId - : Misc.poll(() => Game.getMonster(classId), 2000, 100)); + : Misc.poll(function () { + return Game.getMonster(classId); + }, 2000, 100)); if (!target) { console.warn("Attack.hurt: Target not found"); @@ -1678,7 +1680,6 @@ const Attack = { /** * @todo If we've disabled tele for walking clear, allow use of tele specifically for repositioning */ - if (distance < 4 && (!unit.hasOwnProperty("mode") || !unit.dead)) { if (walk) { if (unit.distance > 8 || checkCollision(me, unit, coll)) { diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 53d387d17..09ce2ff69 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -236,39 +236,28 @@ const Packet = { .dword(tome.gid) .send(); }; + const idOnCursor = function () { + return getCursorType() === sdk.cursortype.Identify; + }; + const unitIdentified = function () { + return unit.identified; + }; - CursorLoop: for (let i = 0; i < 3; i += 1) { identify(); - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (getCursorType() === sdk.cursortype.Identify) { - break CursorLoop; - } - - delay(10); + if (Misc.poll(idOnCursor, 2000, 10)) { + break; } } - if (getCursorType() !== sdk.cursortype.Identify) { - return false; - } + if (!idOnCursor()) return false; for (let i = 0; i < 3; i += 1) { - if (getCursorType() === sdk.cursortype.Identify) { - identify(); - } - - let tick = getTickCount(); + idOnCursor() && identify(); + if (Misc.poll(unitIdentified, 2000, 10)) { + delay(25); - while (getTickCount() - tick < 2000) { - if (unit.identified) { - delay(50); - return true; - } - - delay(10); + return true; } } diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index c136f831b..ceb8df102 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -300,7 +300,9 @@ const Pather = { // set settings.clearSettings equal to the now properly asssigned clearSettings settings.clearSettings = clearSettings; - !settings.allowClearing && settings.allowClearing !== undefined && (settings.clearSettings.allowClearing = false); + if (!settings.allowClearing && settings.allowClearing !== undefined) { + settings.clearSettings.allowClearing = false; + } (target instanceof PresetUnit) && (target = target.realCoords()); if (settings.minDist > 3) { @@ -545,7 +547,9 @@ const Pather = { */ teleportTo: function (x, y, maxRange = 5) { for (let i = 0; i < 3; i += 1) { - Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); + Config.PacketCasting > 0 + ? Packet.teleport(x, y) + : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); let tick = getTickCount(); let pingDelay = i === 0 ? 150 : me.getPingDelay(); @@ -1269,7 +1273,11 @@ const Pather = { const destName = targetArea ? getAreaName(targetArea) : targetArea; Pather.broadcastIntent(targetArea); - console.info(true, "ÿc7targetArea: ÿc0" + destName + " ÿc7myArea: ÿc0" + getAreaName(me.area), "useWaypoint"); + console.info( + true, + "ÿc7targetArea: ÿc0" + destName + " ÿc7myArea: ÿc0" + getAreaName(me.area), + "useWaypoint" + ); MainLoop: for (let i = 0; i < 12; i += 1) { @@ -1510,13 +1518,18 @@ const Pather = { me.cancelUIFlags(); - let preArea = me.area; + const preArea = me.area; + const changedArea = function () { + return me.area !== preArea; + }; for (let i = 0; i < 10; i += 1) { if (me.dead) return false; i > 0 && me.inTown && Town.move("portalspot"); - let portal = unit ? copyUnit(unit) : this.getPortal(targetArea, owner); + const portal = unit + ? copyUnit(unit) + : this.getPortal(targetArea, owner); if (portal) { if (portal.objtype === sdk.areas.DuranceofHateLvl3 && portal.getParent() !== me.name @@ -1533,16 +1546,9 @@ const Pather = { : Pather.moveNearUnit(portal, 20); } if (Packet.telekinesis(portal)) { - if (Misc.poll(function () { - if (me.area !== preArea) { - Pather.lastPortalTick = getTickCount(); - delay(100); - - return true; - } - - return false; - }, 500, 50)) { + if (Misc.poll(changedArea, 500, 50)) { + Pather.lastPortalTick = getTickCount(); + delay(100); return true; } } @@ -1575,17 +1581,11 @@ const Pather = { } } - let tick = getTickCount(); - - while (getTickCount() - tick < 500) { - if (me.area !== preArea) { - Pather.lastPortalTick = getTickCount(); - delay(100); + if (Misc.poll(changedArea, 500, 3)) { + Pather.lastPortalTick = getTickCount(); + delay(100); - return true; - } - - delay(10); + break; } i > 1 && Packet.flash(me.gid); @@ -1714,22 +1714,7 @@ const Pather = { * @returns {boolean} */ accessToAct: function (act) { - switch (act) { - // Act 1 is always accessible - case 1: - return true; - // For the other acts, check the "Able to go to Act *" quests - case 2: - return me.getQuest(sdk.quest.id.AbleToGotoActII, sdk.quest.states.Completed) === 1; - case 3: - return me.getQuest(sdk.quest.id.AbleToGotoActIII, sdk.quest.states.Completed) === 1; - case 4: - return me.getQuest(sdk.quest.id.AbleToGotoActIV, sdk.quest.states.Completed) === 1; - case 5: - return me.expansion && me.getQuest(sdk.quest.id.AbleToGotoActV, sdk.quest.states.Completed) === 1; - default: - return false; - } + return me.accessToAct(act); }, /** @@ -1744,7 +1729,9 @@ const Pather = { let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); if (preset) { - Skill.haveTK ? Pather.moveNearUnit(preset, 20, clearPath) : Pather.moveToUnit(preset, 0, 0, clearPath); + Skill.haveTK + ? Pather.moveNearUnit(preset, 20, clearPath) + : Pather.moveToUnit(preset, 0, 0, clearPath); let wp = Game.getObject("waypoint"); diff --git a/d2bs/kolbot/libs/scripts/Bonesaw.js b/d2bs/kolbot/libs/scripts/Bonesaw.js index 72725642e..f0286a766 100644 --- a/d2bs/kolbot/libs/scripts/Bonesaw.js +++ b/d2bs/kolbot/libs/scripts/Bonesaw.js @@ -10,7 +10,7 @@ function Bonesaw () { Pather.useWaypoint(sdk.areas.GlacialTrail); Precast.doPrecast(true); - if (!Pather.moveToPreset(sdk.areas.GlacialTrail, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 15, 15)) { + if (!Pather.moveToPresetObject(sdk.areas.GlacialTrail, sdk.objects.LargeSparklyChest, { offX: 15, offY: 15 })) { throw new Error("Failed to move to Bonesaw"); } From acc016ff133bf8f139b68c2072e1af358057dbf6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:18:48 -0400 Subject: [PATCH 292/758] Update Me.js - fix bug with the jump from 35 (Siege) -> 28 (AbleToGoToActV) --- d2bs/kolbot/libs/core/Me.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 533760657..a6a49e088 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -1047,6 +1047,7 @@ Object.defineProperties(me, { highestQuestDone: { get: function () { for (let i = sdk.quest.id.Respec; i >= sdk.quest.id.SpokeToWarriv; i--) { + if (!QuestData.has(i)) continue; if (QuestData.get(i).complete()) return i; // check if we've completed main part but not used our reward From 95290612f103cc9ac79f65d93d8804e1e2c66bca Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:14:44 -0400 Subject: [PATCH 293/758] Update D2BotFollow.dbj - display charlvl in status bar --- d2bs/kolbot/D2BotFollow.dbj | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index ceb7ede14..cd4c7d65d 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -46,7 +46,7 @@ new Overrides.Override(Starter, Starter.receiveCopyData, function (orignal, mode const locationAction = (function () { let announced = false; - let lastGameTick, leader = ""; + let lastGameTick; const lastGame = []; /** @@ -212,7 +212,8 @@ const locationAction = (function () { Controls.JoinGameName.setText(Starter.joinInfo.gameName); Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); - if (Starter.lastGameStatus === "pending" || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { + if (Starter.lastGameStatus === "pending" + || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { D2Bot.printToConsole("Failed to join game"); ControlAction.timeoutDelay("Join Delay", Starter.Config.JoinRetryDelay * 1000, joinCheck(leader[j])); D2Bot.updateRuns(); @@ -398,7 +399,10 @@ function main () { DataFile.updateStats("runs", Starter.gameCount); } - D2Bot.updateStatus(Starter.profileInfo.charName + " | Game: " + (me.gamename || "singleplayer") + Starter.timer(Starter.gameStart)); + D2Bot.updateStatus( + me.charname + " (" + me.charlvl + ") | Game: " + (me.gamename || "singleplayer") + + Starter.timer(Starter.gameStart) + ); } delay(1000); From b19d28ae598e13773f8be57a511a4b4abfeabe1b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 5 Sep 2023 02:47:50 -0400 Subject: [PATCH 294/758] Update TownChicken.js - adding back pausing other threads as its still needed when running a secondary main thread. Todo is testing antihostile and clonekilla as workers --- .../libs/modules/workers/TownChicken.js | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/workers/TownChicken.js b/d2bs/kolbot/libs/modules/workers/TownChicken.js index c722192ee..0c702aae7 100644 --- a/d2bs/kolbot/libs/modules/workers/TownChicken.js +++ b/d2bs/kolbot/libs/modules/workers/TownChicken.js @@ -203,7 +203,9 @@ !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"); + if (!me.inTown && !usePortal(sdk.areas.townOf(me.area))) { + throw new Error("Town.goToTown: Failed to take TP"); + } } } catch (e) { let tpTool = me.getTpTool(); @@ -244,6 +246,21 @@ return true; }; + const threads = ["threads/antihostile.js", "threads/rushthread.js", "threads/CloneKilla.js"]; + const togglePause = function () { + for (let thread of threads) { + let script = getScript(thread); + + if (script) { + script.running + ? script.pause() + : script.resume(); + } + } + + return true; + }; + const visitTown = function () { console.log("ÿc8Start ÿc0:: ÿc8visitTown"); @@ -262,6 +279,8 @@ me.cancelUIFlags(); try { goToTown(); + while (!me.area) delay (3); + if (!me.inTown) return false; } catch (e) { return false; } @@ -334,6 +353,7 @@ let t4 = getTickCount(); try { _recursion = true; + togglePause(); console.log("ÿc8(TownChicken) :: ÿc0Going to town"); me.overhead("ÿc8(TownChicken) :: ÿc0Going to town"); Attack.stopClear = true; @@ -346,6 +366,7 @@ return false; } finally { _recursion = false; + togglePause(); Packet.flash(me.gid, 100); console.log("ÿc8(TownChicken) :: ÿc0Took: " + Time.format(getTickCount() - t4) + " to visit town."); [Attack.stopClear, townCheck] = [false, false]; From a32de9d91c563d2626b3c64d611a2af244d4787d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 5 Sep 2023 20:50:35 -0400 Subject: [PATCH 295/758] Update Me.js - add `me.haveWaypoint`, makes for an easier way to check for a wp instead of having to do `getWaypoint(Pather.wpAreas.indexOf(area))` --- d2bs/kolbot/libs/core/Me.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index a6a49e088..61d11ed8e 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -1030,6 +1030,17 @@ Object.defineProperties(me, { return me.highestAct >= act; }; + /** + * Easier way to check if you have a waypoint + * @param {number} area + * @returns {boolean} + */ + me.haveWaypoint = function (area) { + let areaIndex = Pather.wpAreas.indexOf(area); + if (areaIndex === -1) return false; + return getWaypoint(areaIndex); + }; + Object.defineProperties(me, { highestAct: { get: function () { From bf39bbbea74b6ad071197d093cdf343b2aaebd7c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 5 Sep 2023 20:53:06 -0400 Subject: [PATCH 296/758] skip announcing kill/clear/goTo while running dia/baa - skip announcing kill/clear/goto while running dia/baal related scripts as the announcement is meant for mfhelper but mfhelper is disabled for those scripts --- d2bs/kolbot/libs/core/Attack.js | 15 +++++++++++++-- d2bs/kolbot/libs/core/Pather.js | 6 +++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 80c95afba..10c535580 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -256,7 +256,13 @@ const Attack = { let lastLoc = { x: me.x, y: me.y }; let tick = getTickCount(); console.log("ÿc7Kill ÿc0:: " + who); - Config.MFLeader && Pather.makePortal() && say("kill " + classId); + if (Config.MFLeader + // mfhelper is disabled for these scripts so announcing is pointless + && !Loader.scriptName(0).toLowerCase().includes("diablo") + && !Loader.scriptName(0).toLowerCase().includes("baal") + && Pather.makePortal()) { + say("kill " + classId); + } 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. @@ -458,7 +464,12 @@ const Attack = { } ({ orgx, orgy } = { orgx: boss.x, orgy: boss.y }); - if (Config.MFLeader && !!bossId && Pather.makePortal()) { + if (Config.MFLeader + && !!bossId + // mfhelper is disabled for these scripts so announcing is pointless + && !Loader.scriptName(0).toLowerCase().includes("diablo") + && !Loader.scriptName(0).toLowerCase().includes("baal") + && Pather.makePortal()) { say("clear " + (["number", "string"].includes(typeof bossId) ? bossId : bossId.name)); } } else { diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index ceb8df102..cf81dd79e 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1244,7 +1244,11 @@ const Pather = { * @param {number} targetArea - area id */ broadcastIntent: function broadcastIntent (targetArea) { - if (Config.MFLeader && Pather.allowBroadcast) { + if (Config.MFLeader + && Pather.allowBroadcast + // mfhelper is disabled for these scripts so announcing is pointless + && !Loader.scriptName(0).toLowerCase().includes("diablo") + && !Loader.scriptName(0).toLowerCase().includes("baal")) { let targetAct = sdk.areas.actOf(targetArea); me.act !== targetAct && say("goto A" + targetAct); } From 57e585f8aed0f1ad692606875a80046edd1c2578 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:13:33 -0400 Subject: [PATCH 297/758] Update D2BotFollow.dbj - reverted leader datafile check, something about how I was doing this was causing followers to skip some games - added incremental delay so we can still reduce some of the copydata spam --- d2bs/kolbot/D2BotFollow.dbj | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index cd4c7d65d..1fd0a78b5 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -179,20 +179,6 @@ const locationAction = (function () { JoinLoop2: for (let i = 0; i < 5; i += 1) { for (let [leader, check] of tracker) { - if (getTickCount() - check.lastAsked < Time.minutes(5)) { - if (check.game) { - // alright so we've already asked this profile and recieved a game but we didn't want to join - // instead of asking them again and spamming copydata. Lets read their datafile and see if they - // are still in game. If they are we'll wait for them to leave and ask again. If they aren't we'll - // ask again immediately. - let data = DataFile.read(leader); - if (data && !!data.currentGame && String.isEqual(data.currentGame, check.game)) { - D2Bot.updateStatus("Waiting for " + leader + " to leave " + check.game); - delay(Time.seconds(i + 1)); - continue; - } - } - } Starter.joinInfo = {}; D2Bot.requestGame(leader); delay(100); @@ -208,16 +194,17 @@ const locationAction = (function () { * and see if there is x amount of time left that makes it worth it vs waiting for next. */ tracker.set(leader, { lastAsked: getTickCount(), game: Starter.joinInfo.gameName }); - if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { + if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 + || Starter.lastGameStatus === "pending") { Controls.JoinGameName.setText(Starter.joinInfo.gameName); Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); if (Starter.lastGameStatus === "pending" || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { D2Bot.printToConsole("Failed to join game"); - ControlAction.timeoutDelay("Join Delay", Starter.Config.JoinRetryDelay * 1000, joinCheck(leader[j])); + ControlAction.timeoutDelay("Join Delay", Starter.Config.JoinRetryDelay * 1000, joinCheck(leader)); D2Bot.updateRuns(); - D2Bot.requestGame(leader[j]); + D2Bot.requestGame(leader); delay(200); if (!Starter.joinInfo.inGame) { @@ -228,9 +215,7 @@ const locationAction = (function () { } if (!Starter.joinInfo.inGame) { - if (Starter.joinInfo.delay) { - ControlAction.timeoutDelay("Leader Delay", Starter.joinInfo.delay); - } + ControlAction.timeoutDelay("Leader Delay", (Starter.joinInfo.delay || 1000) + Time.seconds(3)); continue; } @@ -255,7 +240,7 @@ const locationAction = (function () { lastGame.push(Starter.joinInfo.gameName); // Might need a fixed number. Right now it stores 1 game per leader. - lastGame.length > leader.length && lastGame.shift(); + lastGame.length > tracker.size && lastGame.shift(); Starter.lastGameStatus = "pending"; Starter.locationTimeout(15000, location); @@ -264,7 +249,10 @@ const locationAction = (function () { } else { // for now, if leader is in game and it's the last game we were in. delay to prevent copyData spam if (lastGame.includes(Starter.joinInfo.gameName)) { - delay((Starter.joinInfo.inGame ? 5000 : 2000)); + ControlAction.timeoutDelay( + "Waiting for new game from " + leader, + Time.seconds((Starter.joinInfo.inGame ? 5 : 2) * (i + 1)) + ); } } } From 2776327e0143b0dca2f1a94d7053bc413809b046 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 6 Sep 2023 02:22:04 -0400 Subject: [PATCH 298/758] Update Pickit.js - slightly faster gold pickup, if we can grab it without moving then do so immediately - fix `Failed to pick item undefined` line, the reason this happens is generally someone else picked up the item before we did but we still managed to get here so save the item name and use it here so at least we can know what we missed --- d2bs/kolbot/libs/core/Pickit.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 405eafd6b..e62c524a1 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -365,7 +365,7 @@ const Pickit = { if (!item) return false; if (!item.onGroundOrDropping) return false; - if (cancelFlags.some(function (flag) { return getUIFlag(flag); })) { + if (cancelFlags.some(getUIFlag)) { delay(500); me.cancel(0); } @@ -376,7 +376,7 @@ const Pickit = { : Infinity; MainLoop: - for (let i = 0; i < retry; i += 1) { + for (let i = 0; i < retry; i++) { if (me.dead) return false; // recursion appeared if (this.track.lastItem === stats.gid) return true; @@ -406,7 +406,7 @@ const Pickit = { } else { if (item.distance > (Config.FastPick || i < 1 ? 6 : 4) || checkCollision(me, item, sdk.collision.BlockWall)) { - if (!Pather.moveToEx(item.x, item.y, { retry: 3, allowPicking: false, minDist: 4 })) { + if (!Pather.move(item, { retry: 3, allowPicking: false, minDist: 4 })) { continue; } // we had to move, lets check to see if it's still there @@ -568,6 +568,9 @@ const Pickit = { if (item) { do { if (Pickit.ignoreList.has(item.gid)) continue; + if (item.classid === sdk.items.Gold && item.distance <= 4 && Pickit.canPick(item)) { + if (Pickit.pickItem(item, Pickit.Result.WANTED, "gold", 1)) continue; + } if (Pickit.pickList.some(el => el.gid === item.gid)) continue; if (item.onGroundOrDropping && item.distance <= range) { Pickit.pickList.push(copyUnit(item)); @@ -599,6 +602,7 @@ const Pickit = { continue; } + const itemName = _item.prettyPrint; // 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 @@ -661,7 +665,7 @@ const Pickit = { if (canFit) { let picked = this.pickItem(_item, status.result, status.line); if (!picked) { - console.warn("Failed to pick item " + _item.prettyPrint); + console.warn("Failed to pick item " + itemName); break; } From 59279899ff30bcc92dec53b3b722805ff8a7d589 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:28:52 -0400 Subject: [PATCH 299/758] create autorush system directory - create RushConfig to simplify setting up rusher/rushee. No more messing with each config file just setup here - Create AutoRush for the quests scripts, moving it here so we can import them as needed in other scripts namely ControlBot and Follower --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 1190 +++++++++++++++++ .../libs/systems/autorush/RushConfig.js | 166 +++ d2bs/kolbot/libs/systems/autorush/index.d.ts | 44 + 3 files changed, 1400 insertions(+) create mode 100644 d2bs/kolbot/libs/systems/autorush/AutoRush.js create mode 100644 d2bs/kolbot/libs/systems/autorush/RushConfig.js create mode 100644 d2bs/kolbot/libs/systems/autorush/index.d.ts diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js new file mode 100644 index 000000000..d562562ea --- /dev/null +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -0,0 +1,1190 @@ +/** +* @filename AutoRush.js +* @author theBGuy +* @desc Scripts for the AutoRush system +* +*/ + +(function (module) { + const { + AutoRush, + RushModes, + RushConfig, + } = require("./RushConfig"); + + const log = function (msg = "", sayMsg = true) { + console.log(msg); + sayMsg && say(msg); + }; + + const playerIn = function (area, nick) { + !area && (area = me.area); + + let party = getParty(); + + if (party) { + do { + if (party.name !== me.name + && (!nick || String.isEqual(party.name, nick)) + && party.area === area) { + return true; + } + } while (party.getNext()); + } + + return false; + }; + + const bumperCheck = function (nick) { + let bumperLevelReq = [20, 40, 60][me.diff]; + return nick + ? Misc.findPlayer(nick).level >= bumperLevelReq + : Misc.checkPartyLevel(bumperLevelReq); + }; + + const playersInAct = function (act) { + !act && (act = me.act); + + let area = sdk.areas.townOfAct(act); + let party = getParty(); + + if (party) { + do { + if (party.name !== me.name && party.area !== area) { + return false; + } + } while (party.getNext()); + } + + return true; + }; + const cain = function () { + log("starting cain"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.DarkWood, true) && Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + throw new Error("Failed to move to Tree of Inifuss"); + } + + let tree = Game.getObject(sdk.quest.chest.InifussTree); + !!tree && tree.distance > 5 && Pather.moveToUnit(tree); + Attack.securePosition(me.x, me.y, 40, 3000, true); + !!tree && tree.distance > 5 && Pather.moveToUnit(tree); + Pather.makePortal(); + log(AutoRush.playersIn); + let tick = getTickCount(); + + while (getTickCount() - tick < Time.minutes(2)) { + if (tree.mode) { + break; + } + Attack.securePosition(me.x, me.y, 20, 1000); + } + + Pather.usePortal(1) || Town.goToTown(); + Pather.useWaypoint(sdk.areas.StonyField, true); + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); + Attack.securePosition(me.x, me.y, 40, 3000, true); + Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); + Pather.makePortal(); + log(AutoRush.playersIn); + + tick = getTickCount(); + + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.usePortal(sdk.areas.Tristram)) { + break; + } + Attack.securePosition(me.x, me.y, 35, 1000); + } + + if (me.inArea(sdk.areas.Tristram)) { + Pather.moveTo(me.x, me.y + 6); + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + + if (gibbet && !gibbet.mode) { + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.CainsJail, 0, 0, true, true)) { + throw new Error("Failed to move to Cain's Jail"); + } + + Attack.securePosition(gibbet.x, gibbet.y, 20, 3000); + Pather.makePortal(); + log(AutoRush.playersIn); + + tick = getTickCount(); + + while (getTickCount() - tick < Time.minutes(2)) { + if (gibbet.mode) { + break; + } + Attack.securePosition(me.x, me.y, 10, 1000); + } + } + } + + return true; + }; + /** @param {string} [nick] */ + const andariel = function (nick) { + log("starting andariel"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.CatacombsLvl2, true) && Precast.doPrecast(true); + + if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true) + || !Pather.moveTo(22582, 9612)) { + throw new Error("andy failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 40, 3000, true); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + if (playerIn(me.area, nick)) { + return true; + } + Pather.moveTo(22582, 9612); + return false; + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Attack.kill(sdk.monsters.Andariel); + log(AutoRush.playersOut); + Pather.moveTo(22582, 9612); + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(250); + } + + Pather.usePortal(null, me.name); + log("a2"); + Town.goToTown(2); + + while (!playersInAct(2)) { + delay(250); + } + } + + return true; + }; + /** @param {string} [nick] */ + const radament = function (nick) { + log("starting radament"); + + const moveIntoPos = function (unit, range) { + let coords = []; + let 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, 105, -105, 120, -120, + 135, -135, 150, -150, 180 + ]; + + for (let i = 0; i < angles.length; i += 1) { + let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); + let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); + + try { + if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { + coords.push({ + x: coordx, + y: coordy + }); + } + } catch (e) { + continue; + } + } + + if (coords.length > 0) { + coords.sort(Sort.units); + + return Pather.moveToUnit(coords[0]); + } + + return false; + }; + + Pather.useWaypoint(sdk.areas.A2SewersLvl2, true) && Precast.doPrecast(false); + Pather.moveToExit(sdk.areas.A2SewersLvl3, true); + + const radaPreset = Game.getPresetObject(sdk.areas.A2SewersLvl3, sdk.quest.chest.HoradricScrollChest); + const radaCoords = radaPreset.realCoords(); + + moveIntoPos(radaCoords, 50); + const rada = Misc.poll(function () { + return Game.getMonster(sdk.monsters.Radament); + }, 1500, 500); + + rada ? moveIntoPos(rada, 60) : console.log("radament unit not found"); + Attack.securePosition(me.x, me.y, 35, 3000); + Pather.makePortal(); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Attack.kill(sdk.monsters.Radament); + + let returnSpot = { + x: me.x, + y: me.y + }; + + log(AutoRush.playersOut); + Pickit.pickItems(); + Attack.securePosition(me.x, me.y, 30, 3000); + + if (!Misc.poll(function () { + return !playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Pather.moveToUnit(returnSpot); + Pather.makePortal(); + log(AutoRush.allIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + Misc.poll(function () { + return !Game.getItem(sdk.quest.item.BookofSkill); + }, 30000, 1000); + + while (playerIn(me.area, nick)) { + delay(200); + } + } + + Pather.usePortal(null, null); + + return true; + }; + /** @param {string} [nick] */ + const cube = function (nick) { + if (me.normal) { + log("starting cube"); + Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); + Precast.doPrecast(true); + + if (!Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { + throw new Error("cube failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 30, 3000, true); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + Pather.usePortal(null, me.name); + } + + return true; + }; + /** @param {string} [nick] */ + const amulet = function (nick) { + const exits = [sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2]; + log("starting amulet"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.LostCity, true) && Precast.doPrecast(true); + + if (!Pather.moveToExit(exits, true) + || !Pather.moveTo(15044, 14045)) { + throw new Error("amulet failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); + + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + Pather.usePortal(null, me.name); + + return true; + }; + /** @param {string} [nick] */ + const staff = function (nick) { + log("starting staff"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FarOasis, true) && Precast.doPrecast(true); + + if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { + throw new Error("staff failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 30, 3000, true); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + Pather.usePortal(null, me.name); + + return true; + }; + /** @param {string} [nick] */ + const summoner = function (nick) { + // right up 25449 5081 (25431, 5011) + // left up 25081 5446 (25011, 5446) + // right down 25830 5447 (25866, 5431) + // left down 25447 5822 (25431, 5861) + + log("starting summoner"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArcaneSanctuary, true) && Precast.doPrecast(true); + + let preset = Game.getPresetObject(sdk.areas.ArcaneSanctuary, sdk.quest.chest.Journal); + let spot = {}; + + switch (preset.roomx * 5 + preset.x) { + case 25011: + spot = { x: 25081, y: 5446 }; + break; + case 25866: + spot = { x: 25830, y: 5447 }; + break; + case 25431: + switch (preset.roomy * 5 + preset.y) { + case 5011: + spot = { x: 25449, y: 5081 }; + break; + case 5861: + spot = { x: 25447, y: 5822 }; + break; + } + + break; + } + + if (!Pather.moveToUnit(spot)) { + throw new Error("summoner failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 25, 3000); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + if (playerIn(me.area, nick)) { + return true; + } + Pather.moveToUnit(spot); + Attack.securePosition(me.x, me.y, 25, 500); + return false; + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); + Attack.kill(sdk.monsters.Summoner); + log(AutoRush.playersOut); + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + Pickit.pickItems(); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); + + let redPortal = Game.getObject(sdk.objects.RedPortal); + + if (!redPortal || !this.usePortal(null, null, redPortal)) { + if (!Misc.poll(() => { + let journal = Game.getObject(sdk.quest.chest.Journal); + + if (journal && journal.interact()) { + delay(1000); + me.cancel(); + } + + redPortal = Pather.getPortal(sdk.areas.CanyonofMagic); + + return (redPortal && Pather.usePortal(null, null, redPortal)); + })) throw new Error("summoner failed"); + } + + return true; + }; + /** @param {string} [nick] */ + const duriel = function (nick) { + log("starting duriel"); + + if (me.inTown) { + Town.doChores(); + Pather.useWaypoint(sdk.areas.CanyonofMagic, true); + } else { + giveWP(); + } + + Precast.doPrecast(true); + + if (!Pather.moveToExit(getRoom().correcttomb, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder)) { + throw new Error("duriel failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (!Misc.poll(function () { + return !playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (!Misc.poll(function () { + return Game.getObject(sdk.objects.PortaltoDurielsLair); + }, AutoRush.playerWaitTimeout, 1000)) { + log("Duriel portal not found"); + return false; + } + + Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); + Attack.kill(sdk.monsters.Duriel); + Pickit.pickItems(); + + Pather.teleport = false; + + Pather.moveTo(22579, 15706); + + Pather.teleport = true; + + Pather.moveTo(22577, 15649, 10); + Pather.moveTo(22577, 15609, 10); + Pather.makePortal(); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (!Pather.usePortal(null, me.name)) { + Town.goToTown(); + } + + Pather.useWaypoint(sdk.areas.PalaceCellarLvl1); + Pather.moveToExit([sdk.areas.HaremLvl2, sdk.areas.HaremLvl1], true); + Pather.moveTo(10022, 5047); + + if (AutoRush.rushMode !== RushModes.chanter) { + log("a3"); + Town.goToTown(3); + Town.doChores(); + + while (!playersInAct(3)) { + delay(250); + } + } + + return true; + }; + /** @param {string} [nick] */ + const lamesen = function (nick) { + log("starting lamesen"); + + if (!Town.goToTown() || !Pather.useWaypoint(sdk.areas.KurastBazaar, true)) { + throw new Error("Lam Essen quest failed"); + } + + Precast.doPrecast(false); + + if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { + throw new Error("Lam Essen quest failed"); + } + + Attack.securePosition(me.x, me.y, 30, 2000); + Pather.makePortal(); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + if (!Misc.poll(function () { + return !playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + } + + Pather.usePortal(null, null); + + return true; + }; + /** @param {string} [nick] */ + const brain = function (nick) { + const exits = [ + sdk.areas.FlayerDungeonLvl1, + sdk.areas.FlayerDungeonLvl2, + sdk.areas.FlayerDungeonLvl3 + ]; + log("starting brain"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.FlayerJungle, true) && Precast.doPrecast(true); + + if (!Pather.moveToExit(exits, true) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsBrainChest)) { + throw new Error("brain failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); + + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + Pather.usePortal(null, me.name); + + return true; + }; + /** @param {string} [nick] */ + const eye = function (nick) { + log("starting eye"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.SpiderForest, true) && Precast.doPrecast(true); + + if (!Pather.moveToExit(sdk.areas.SpiderCavern, true) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsEyeChest)) { + throw new Error("eye failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); + + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + Pather.usePortal(null, me.name); + + return true; + }; + /** @param {string} [nick] */ + const heart = function (nick) { + log("starting heart"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.KurastBazaar, true) && Precast.doPrecast(true); + + if (!Pather.journeyTo(sdk.areas.A3SewersLvl2, true) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsHeartChest)) { + throw new Error("heart failed"); + } + + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); + + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + Pather.usePortal(null, me.name); + + return true; + }; + // re-write to prevent fail to complete quest due to killing council from to far away + /** @param {string} [nick] */ + const travincal = function (nick) { + log("starting travincal"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.Travincal, true) && Precast.doPrecast(true); + + let coords = [me.x, me.y]; + + Pather.moveTo(coords[0] + 23, coords[1] - 102); + Pather.makePortal(); + Attack.securePosition(me.x, me.y, 40, 3000); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Pather.moveTo(coords[0] + 30, coords[1] - 134); + Pather.moveTo(coords[0] + 86, coords[1] - 130); + Pather.moveTo(coords[0] + 71, coords[1] - 94); + Attack.securePosition(me.x, me.y, 40, 3000); + + Pather.moveTo(coords[0] + 23, coords[1] - 102); + Pather.makePortal(); + log(AutoRush.playersOut); + Pather.usePortal(null, me.name); + + return true; + }; + /** @param {string} [nick] */ + const mephisto = function (nick) { + log("starting mephisto"); + + Town.doChores(); + Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true) && Pather.moveTo(17692, 8023) && Pather.makePortal(); + delay(2000); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Pather.moveTo(17591, 8070); + Attack.kill(sdk.monsters.Mephisto); + Pickit.pickItems(); + Pather.moveTo(17692, 8023) && Pather.makePortal(); + log(AutoRush.playersOut); + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(250); + } + + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 40, 3000); + + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); + + if (hydra) { + do { + while (!hydra.dead && hydra.hp > 0) { + delay(500); + } + } while (hydra.getNext()); + } + + Pather.makePortal(); + Pather.moveTo(17581, 8070); + log(AutoRush.playersIn); + + while (!playerIn(me.area, nick)) { + delay(250); + } + + log("a4"); + + while (!playersInAct(4)) { + delay(250); + } + + delay(2000); + Pather.usePortal(null); + } + + return true; + }; + /** @param {string} [nick] */ + const izual = function (nick) { + log("starting izual"); + + const moveIntoPos = function (unit, range) { + let coords = []; + let 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, 105, -105, 120, -120, + 135, -135, 150, -150, 180 + ]; + + for (let i = 0; i < angles.length; i += 1) { + let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); + let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); + + try { + if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { + coords.push({ + x: coordx, + y: coordy + }); + } + } catch (e) { + continue; + } + } + + if (coords.length > 0) { + coords.sort(Sort.units); + + return Pather.moveToUnit(coords[0]); + } + + return false; + }; + + Pather.useWaypoint(sdk.areas.CityoftheDamned, true) && Precast.doPrecast(false); + Pather.moveToExit(sdk.areas.PlainsofDespair, true); + + let izualPreset = Game.getPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual); + let izualCoords = { + area: sdk.areas.PlainsofDespair, + x: izualPreset.roomx * 5 + izualPreset.x, + y: izualPreset.roomy * 5 + izualPreset.y + }; + + moveIntoPos(izualCoords, 50); + let izual = Misc.poll(() => Game.getMonster(sdk.monsters.Izual), 1500, 500); + + izual ? moveIntoPos(izual, 60) : console.log("izual unit not found"); + + let returnSpot = { + x: me.x, + y: me.y + }; + + Attack.securePosition(me.x, me.y, 30, 3000); + Pather.makePortal(); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Attack.kill(sdk.monsters.Izual); + Pickit.pickItems(); + log(AutoRush.playersOut); + Pather.moveToUnit(returnSpot); + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(200); + } + } + + Pather.usePortal(null, null); + + return true; + }; + /** @param {string} [nick] */ + const diablo = function (nick) { + include("core/Common/Diablo.js"); + log("starting diablo"); + + function inviteIn () { + Pather.moveTo(7763, 5267) && Pather.makePortal(); + Pather.moveTo(7727, 5267); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + return true; + } + + Town.doChores(); + Pather.useWaypoint(sdk.areas.RiverofFlame); + Precast.doPrecast(true); + if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) { + throw new Error("Failed to move to Chaos Sanctuary"); + } + + Common.Diablo.initLayout(); + Config.Diablo.Fast = true; + Config.Diablo.SealLeader = false; + + try { + Common.Diablo.runSeals(Config.Diablo.SealOrder); + console.log("Attempting to find Diablo"); + inviteIn() && Common.Diablo.diabloPrep(); + } catch (error) { + console.log("Diablo wasn't found. Checking seals."); + Common.Diablo.runSeals(Config.Diablo.SealOrder); + inviteIn() && Common.Diablo.diabloPrep(); + } + + Attack.kill(sdk.monsters.Diablo); + log(AutoRush.playersOut); + + if (me.expansion && AutoRush.rushMode !== RushModes.chanter) { + log("a5"); + + while (!playersInAct(5)) { + delay(250); + } + } + + Pickit.pickItems(); + !Pather.usePortal(null, me.name) && Town.goToTown(); + + return true; + }; + /** @param {string} [nick] */ + const shenk = function (nick) { + log("starting shenk"); + + Pather.useWaypoint(sdk.areas.FrigidHighlands, true) && Precast.doPrecast(false); + Pather.moveTo(3846, 5120); + Attack.securePosition(me.x, me.y, 30, 3000); + Pather.makePortal(); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + Pickit.pickItems(); + Pather.moveTo(3846, 5120); + log(AutoRush.playersOut); + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(200); + } + } + + Pather.usePortal(null, null); + + return true; + }; + /** @param {string} [nick] */ + const anya = function (nick) { + !me.inTown && Town.goToTown(); + + log("starting anya"); + + if (!Pather.useWaypoint(sdk.areas.CrystalizedPassage, true)) { + throw new Error("Anya quest failed"); + } + + Precast.doPrecast(false); + + if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { + throw new Error("Anya quest failed"); + } + + Attack.securePosition(me.x, me.y, 30, 2000); + + let anya = Game.getObject(sdk.objects.FrozenAnya); + + if (anya) { + Pather.moveToUnit(anya); + // Rusher should be able to interact so quester can get the potion without entering + Packet.entityInteract(anya); + delay(1000 + me.ping); + me.cancel(); + } + + Pather.makePortal(); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Misc.poll(() => !Game.getObject(sdk.objects.FrozenAnya), 30000, 1000); + + log(AutoRush.playersOut); // Mainly for non-questers to know when to get the scroll of resistance + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(200); + } + } + + Pather.usePortal(null, null); + + return true; + }; + /** @param {string} [nick] */ + const ancients = function (nick) { + if (AutoRush.rushMode !== RushModes.chanter) { + if (!RushConfig[me.profile].config.Ancients[sdk.difficulty.nameOf(me.diff)]) { + if (!RushConfig[me.profile].config.Wps) { + log("Hell rush complete~"); + delay(500); + quit(); + } + return false; + } + } + + if (!bumperCheck(nick)) { + if (AutoRush.rushMode === RushModes.chanter) { + log(nick + " you are not eligible for ancients."); + + return false; + } + if (!RushConfig[me.profile].config.Wps) { + log("No eligible bumpers detected. Rush complete~"); + delay(500); + quit(); + } + + return false; + } + + include("core/Common/Ancients.js"); + log("starting ancients"); + + Town.doChores(); + Pather.useWaypoint(sdk.areas.AncientsWay, true) && Precast.doPrecast(true); + + if (!Pather.moveToExit(sdk.areas.ArreatSummit, true)) { + throw new Error("Failed to go to Ancients way."); + } + + Pather.moveTo(10089, 12622); + Pather.makePortal(); + log(AutoRush.allIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Pather.moveTo(10048, 12628); + Common.Ancients.touchAltar(); + Common.Ancients.startAncients(); + + Pather.moveTo(10089, 12622); + me.cancel(); + Pather.makePortal(); + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + + !Pather.usePortal(null, me.name) && Town.goToTown(); + + return true; + }; + /** @param {string} [nick] */ + const baal = function (nick) { + if (me.hell) { + if (!RushConfig[me.profile].config.Wps) { + log("Baal not done in Hell ~Hell rush complete~"); + delay(500); + quit(); + } + wpsToGive.remove(sdk.areas.WorldstoneLvl2); + + return false; + } + + if (!bumperCheck(nick)) { + if (!RushConfig[me.profile].config.Wps) { + log("No eligible bumpers detected. ~Rush complete~"); + delay(500); + quit(); + } + wpsToGive.remove(sdk.areas.WorldstoneLvl2); + + return false; + } + + include("core/Common/Baal.js"); + log("starting baal"); + + if (me.inTown) { + Town.doChores(); + Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { + throw new Error("Failed to move to Throne of Destruction."); + } + } + + Pather.moveTo(15113, 5040); + Attack.clear(15); + Common.Baal.clearThrone(); + + if (AutoRush.rushMode !== RushModes.rusher) { + Pather.moveTo(15118, 5045); + Pather.makePortal(); + say(AutoRush.playersIn); + } + + if (!Common.Baal.clearWaves()) { + throw new Error("Couldn't clear baal waves"); + } + + Common.Baal.clearThrone(); + + if (AutoRush.rushMode !== RushModes.chanter) { + me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done + Pather.moveTo(15090, 5008); + delay(5000); + Precast.doPrecast(true); + Misc.poll(() => !Game.getMonster(sdk.monsters.ThroneBaal), Time.minutes(3), 1000); + + let portal = Game.getObject(sdk.objects.WorldstonePortal); + + if (portal) { + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); + } + + Pather.moveTo(15213, 5908); + Pather.makePortal(); + Pather.moveTo(15170, 5950); + delay(1000); + log(AutoRush.allIn); + + while (!playerIn(me.area, nick)) { + delay(250); + } + + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); + } + + return true; + }; + + module.exports = { + log: log, + playerIn: playerIn, + playersInAct: playersInAct, + bumperCheck: bumperCheck, + andariel: andariel, + cube: cube, + amulet: amulet, + staff: staff, + summoner: summoner, + duriel: duriel, + eye: eye, + brain: brain, + heart: heart, + travincal: travincal, + mephisto: mephisto, + diablo: diablo, + ancients: ancients, + baal: baal, + cain: cain, + radament: radament, + lamesen: lamesen, + izual: izual, + shenk: shenk, + anya: anya + }; +})(module); diff --git a/d2bs/kolbot/libs/systems/autorush/RushConfig.js b/d2bs/kolbot/libs/systems/autorush/RushConfig.js new file mode 100644 index 000000000..a3b3935f0 --- /dev/null +++ b/d2bs/kolbot/libs/systems/autorush/RushConfig.js @@ -0,0 +1,166 @@ +/** +* @filename RushConfig.js +* @author theBGuy +* @desc Configuration file for AutoRush system +* +*/ + +(function (module) { + /** @enum */ + const RushModes = { + /** The rushee that does the quests */ + quester: 0, + /** The rushee that follows */ + follower: 1, + /** The rushee that bumps the quester */ + bumper: 2, + /** Autorush mode */ + rusher: 3, + /** ControlBot/Chant scripts mode */ + chanter: 4, + /** Manual follow mode - disables some of the bot <-> bot communication we need with auto */ + manual: 5, + }; + + const AutoRush = { + /** Command by rusher to tell players to enter a portal */ + playersIn: "1", + /** Command by rusher to tell players to go back to town */ + playersOut: "2", + allIn: "3", + rushMode: RushModes.rusher, + /** How long to wait for a player to leave/enter an area before ending quest script with failed */ + playerWaitTimeout: Time.minutes(1), + /** controls the order */ + sequences: [ + "cain", + "andariel", + "radament", + "cube", + "amulet", + "staff", + "summoner", + "duriel", + "lamesen", + "travincal", + "mephisto", + "izual", + "diablo", + "shenk", + "anya", + "ancients", + "baal", + "givewps", + ], + }; + + /** @type {Object.} */ + const RushConfig = { + "example-quester": { + type: RushModes.quester, + startProfiles: ["example-bumper"], + /** Optional - Fill this out to create a account/character if it doesn't exist already */ + create: { + account: "testacc", + password: "password", + charName: "quester", + charInfo: "scl-sorc", + } + }, + "example-follower": { + type: RushModes.follower, + leader: "example-quester", + create: { + charName: "follower", + charInfo: "scl-zon", + } + }, + "example-bumper": { + type: RushModes.bumper, + leader: "example-quester", + }, + "example-rusher": { + type: RushModes.rusher, + leader: "example-quester", + config: { + WaitPlayerCount: 1, + Cain: true, + Radament: true, + LamEsen: true, + Izual: true, + Shenk: true, + Anya: true, + Ancients: { + Normal: true, + Nightmare: true, + Hell: false, + }, + Wps: false, + LastRun: "", + }, + }, + }; + + const _defaultConfig = { + /** @type {RushModes} */ + type: RushModes.quester, + /** @type {string[]} */ + startProfiles: [], + /** @type {string} */ + leader: "", + /** @type {Object} */ + create: { + /** @type {string} */ + account: "", + /** @type {string} */ + password: "", + /** @type {string} */ + charName: "", + /** + * @type {string} + * @desc Format: "scl-sorc" - "scl" = softcore ladder, "sorc" = sorceress + */ + charInfo: "", + }, + /** @type {Object} */ + config: { + /** @type {number} */ + WaitPlayerCount: 1, + /** @type {boolean} */ + Cain: true, + /** @type {boolean} */ + Radament: true, + /** @type {boolean} */ + LamEsen: true, + /** @type {boolean} */ + Izual: true, + /** @type {boolean} */ + Shenk: true, + /** @type {boolean} */ + Anya: true, + /** @type {Object} */ + Ancients: { + /** @type {boolean} */ + Normal: true, + /** @type {boolean} */ + Nightmare: true, + /** @type {boolean} */ + Hell: false, + }, + /** @type {boolean} */ + Wps: false, + /** @type {string} */ + LastRun: "", + }, + }; + + for (let key in RushConfig) { + RushConfig[key] = Object.assign({}, _defaultConfig, RushConfig[key]); + } + + module.exports = { + AutoRush: AutoRush, + RushModes: RushModes, + RushConfig: RushConfig, + }; +})(module); diff --git a/d2bs/kolbot/libs/systems/autorush/index.d.ts b/d2bs/kolbot/libs/systems/autorush/index.d.ts new file mode 100644 index 000000000..1bbfc7b12 --- /dev/null +++ b/d2bs/kolbot/libs/systems/autorush/index.d.ts @@ -0,0 +1,44 @@ +declare global { + enum RushModes { + quester = 0, + follower = 1, + bumper = 2, + rusher = 3, + chanter = 4, + manual = 5, + } + type DefaultConfig = { + type: RushModes; + startProfiles: string[]; + leader: string; + create: { + account: string; + password: string; + charName: string; + /** + * @desc Format: "scl-sorc" - "scl" = softcore ladder, "sorc" = sorceress + */ + charInfo: string; + }; + config: { + WaitPlayerCount: number; + Cain: boolean; + Radament: boolean; + LamEsen: boolean; + Izual: boolean; + Shenk: boolean; + Anya: boolean; + Ancients: { + Normal: boolean; + Nightmare: boolean; + Hell: boolean; + }; + Wps: boolean; + LastRun: string; + }; + }; + type RushConfig = { + [key: string]: DefaultConfig; + }; +} +export {}; From 2a9adb36b59ea192c35559a49d966f39dc440f3b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 6 Sep 2023 18:40:48 -0400 Subject: [PATCH 300/758] Update ControlBot - added rush commands - changed to queue based processing with tracking current command being executed and for whom - use whisper messages to not blow up chat all the time - use worker to process the messages and limit to 1 message every ~1.5 seconds - keep track of who is in game and inform users where there command is in the message queue if it isn't the next to be processed - announce 30 seconds left in game --- d2bs/kolbot/libs/config/Amazon.js | 1453 +++++++-------- d2bs/kolbot/libs/config/Assassin.js | 1465 ++++++++-------- d2bs/kolbot/libs/config/Barbarian.js | 1443 +++++++-------- d2bs/kolbot/libs/config/Druid.js | 1451 +++++++-------- d2bs/kolbot/libs/config/Necromancer.js | 1493 ++++++++-------- d2bs/kolbot/libs/config/Paladin.js | 1451 +++++++-------- d2bs/kolbot/libs/config/Sorceress.js | 1455 +++++++-------- d2bs/kolbot/libs/config/_BaseConfigFile.js | 1845 ++++++++++---------- d2bs/kolbot/libs/core/Config.js | 21 + d2bs/kolbot/libs/scripts/ControlBot.js | 575 ++++-- 10 files changed, 6596 insertions(+), 6056 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index eb1f44d91..64503be56 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -15,726 +15,745 @@ * Javascript statements need to end with a semi-colon; Good: Scripts.Corpsefire = false; Bad: Scripts.Corpsefire = false */ -function LoadConfig() { - /* Sequence config - * Set to true if you want to run it, set to false if not. - * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. - */ - - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - // ## Team MF - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal - Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed - Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto - Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - /* - * Unless stated otherwise, leader's character name isn't needed on order to run. - * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - */ - - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - - // ##### MFHelper ##### // - // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 - // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited - Scripts.MFHelper = false; - - // ###################### // - /* ##### Pure Leech ##### */ - // ###################### // - - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - - // ########################## // - /* ##### Helper SCRIPTS ##### */ - // ########################## // - - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, +function LoadConfig () { + /* Sequence config + * Set to true if you want to run it, set to false if not. + * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. + */ + + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + // ## Team MF + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal + Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed + Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto + Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + /* + * Unless stated otherwise, leader's character name isn't needed on order to run. + * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + */ + + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + + // ##### MFHelper ##### // + // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 + // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited + Scripts.MFHelper = false; + + // ###################### // + /* ##### Pure Leech ##### */ + // ###################### // + + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + + // ########################## // + /* ##### Helper SCRIPTS ##### */ + // ########################## // + + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /* Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 35; - * GOOD: Config.AttackSkill[1] = sdk.skills.LightningFury; - * BAD: Config.AttackSkill[1] = -35; - * BAD: Config.AttackSkill[1] = "LightningFury"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. - Config.UseInnerSight = true; // Use inner sight as a precast - Config.UseSlowMissiles = true; // Use slow missiles as a precast - Config.UseDecoy = true; // Use decoy with merc stomp - Config.SummonValkyrie = true; // Summon Valkyrie - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces - - // The gems not used by other recipes will be used for magic item rerolling. - - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling - - //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); - - //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /* Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + //Config.PickitFiles.push("kolton.nip"); + //Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 35; + * GOOD: Config.AttackSkill[1] = sdk.skills.LightningFury; + * BAD: Config.AttackSkill[1] = -35; + * BAD: Config.AttackSkill[1] = "LightningFury"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. + Config.UseInnerSight = true; // Use inner sight as a precast + Config.UseSlowMissiles = true; // Use slow missiles as a precast + Config.UseDecoy = true; // Use decoy with merc stomp + Config.SummonValkyrie = true; // Summon Valkyrie + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet + //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces + + // The gems not used by other recipes will be used for magic item rerolling. + + //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + + //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling + + //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); + + //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index d7448e455..4e91d7801 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -15,732 +15,751 @@ * Javascript statements need to end with a semi-colon; Good: Scripts.Corpsefire = false; Bad: Scripts.Corpsefire = false */ -function LoadConfig() { - /* Sequence config - * Set to true if you want to run it, set to false if not. - * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. - */ - - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - // ## Team MF - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal - Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed - Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto - Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - /* - * Unless stated otherwise, leader's character name isn't needed on order to run. - * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - */ - - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - - // ##### MFHelper ##### // - // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 - // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited - Scripts.MFHelper = false; - - // ###################### // - /* ##### Pure Leech ##### */ - // ###################### // - - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - - // ########################## // - /* ##### Helper SCRIPTS ##### */ - // ########################## // - - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, +function LoadConfig () { + /* Sequence config + * Set to true if you want to run it, set to false if not. + * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. + */ + + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + // ## Team MF + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal + Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed + Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto + Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + /* + * Unless stated otherwise, leader's character name isn't needed on order to run. + * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + */ + + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + + // ##### MFHelper ##### // + // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 + // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited + Scripts.MFHelper = false; + + // ###################### // + /* ##### Pure Leech ##### */ + // ###################### // + + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + + // ########################## // + /* ##### Helper SCRIPTS ##### */ + // ########################## // + + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /* Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 251; - * GOOD: Config.AttackSkill[1] = sdk.skills.FireBlast; - * BAD: Config.AttackSkill[1] = -251; - * BAD: Config.AttackSkill[1] = "FireBlast"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - Config.UseTraps = true; // Set to true to use traps - Config.Traps = [271, 271, 271, 276, 276]; // Skill IDs for traps to be cast on all mosters except act bosses. - Config.BossTraps = [271, 271, 271, 271, 271]; // Skill IDs for traps to be cast on act bosses. - - Config.SummonShadow = "Master"; // 0 = don't summon, 1 or "Warrior" = summon Shadow Warrior, 2 or "Master" = summon Shadow Master - Config.UseFade = true; // Set to true to use Fade prebuff. - Config.UseBoS = false; // Set to true to use Burst of Speed prebuff. TODO: Casting in town + UseFade compatibility - Config.UseVenom = false; // Set to true to use Venom prebuff. Set to false if you don't have the skill and have Arachnid Mesh - it will cause connection drop otherwise. - Config.UseBladeShield = false; // Set to true to use blade shield armor - Config.UseCloakofShadows = true; // Set to true to use Cloak of Shadows while fighting. Useful for blinding regular monsters/minions. - Config.AggressiveCloak = false; // Move into Cloak range or cast if already close - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces - - // The gems not used by other recipes will be used for magic item rerolling. - - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling - - //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); - - //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /* Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + //Config.PickitFiles.push("kolton.nip"); + //Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 251; + * GOOD: Config.AttackSkill[1] = sdk.skills.FireBlast; + * BAD: Config.AttackSkill[1] = -251; + * BAD: Config.AttackSkill[1] = "FireBlast"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + Config.UseTraps = true; // Set to true to use traps + Config.Traps = [271, 271, 271, 276, 276]; // Skill IDs for traps to be cast on all mosters except act bosses. + Config.BossTraps = [271, 271, 271, 271, 271]; // Skill IDs for traps to be cast on act bosses. + + Config.SummonShadow = "Master"; // 0 = don't summon, 1 or "Warrior" = summon Shadow Warrior, 2 or "Master" = summon Shadow Master + Config.UseFade = true; // Set to true to use Fade prebuff. + Config.UseBoS = false; // Set to true to use Burst of Speed prebuff. TODO: Casting in town + UseFade compatibility + Config.UseVenom = false; // Set to true to use Venom prebuff. Set to false if you don't have the skill and have Arachnid Mesh - it will cause connection drop otherwise. + Config.UseBladeShield = false; // Set to true to use blade shield armor + Config.UseCloakofShadows = true; // Set to true to use Cloak of Shadows while fighting. Useful for blinding regular monsters/minions. + Config.AggressiveCloak = false; // Move into Cloak range or cast if already close + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet + //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces + + // The gems not used by other recipes will be used for magic item rerolling. + + //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + + //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling + + //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); + + //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 16ed4c110..526af7c49 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -15,721 +15,740 @@ * Javascript statements need to end with a semi-colon; Good: Scripts.Corpsefire = false; Bad: Scripts.Corpsefire = false */ -function LoadConfig() { - /* Sequence config - * Set to true if you want to run it, set to false if not. - * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. - */ - - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - // ## Team MF - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal - Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed - Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto - Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - /* - * Unless stated otherwise, leader's character name isn't needed on order to run. - * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - */ - - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - - // ##### MFHelper ##### // - // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 - // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited - Scripts.MFHelper = false; - - // ###################### // - /* ##### Pure Leech ##### */ - // ###################### // - - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - - // ########################## // - /* ##### Helper SCRIPTS ##### */ - // ########################## // - - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, +function LoadConfig () { + /* Sequence config + * Set to true if you want to run it, set to false if not. + * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. + */ + + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + // ## Team MF + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal + Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed + Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto + Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + /* + * Unless stated otherwise, leader's character name isn't needed on order to run. + * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + */ + + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + + // ##### MFHelper ##### // + // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 + // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited + Scripts.MFHelper = false; + + // ###################### // + /* ##### Pure Leech ##### */ + // ###################### // + + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + + // ########################## // + /* ##### Helper SCRIPTS ##### */ + // ########################## // + + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /* Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 151; - * GOOD: Config.AttackSkill[1] = sdk.skills.Whirlwind; - * BAD: Config.AttackSkill[1] = -151; - * BAD: Config.AttackSkill[1] = "Whirlwind"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill for bosses. - Config.AttackSkill[2] = -1; // Backup/Immune skill for bosses. - Config.AttackSkill[3] = -1; // Primary skill for others. - Config.AttackSkill[4] = -1; // Backup/Immune skill for others. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - Config.FindItem = false; // Use Find Item skill on corpses after clearing. - Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills - Config.UseWarcries = true; // use battle orders, battle command, and shout if we have them - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces - - // The gems not used by other recipes will be used for magic item rerolling. - - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling - - //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); - - //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /* Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + //Config.PickitFiles.push("kolton.nip"); + //Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 151; + * GOOD: Config.AttackSkill[1] = sdk.skills.Whirlwind; + * BAD: Config.AttackSkill[1] = -151; + * BAD: Config.AttackSkill[1] = "Whirlwind"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill for bosses. + Config.AttackSkill[2] = -1; // Backup/Immune skill for bosses. + Config.AttackSkill[3] = -1; // Primary skill for others. + Config.AttackSkill[4] = -1; // Backup/Immune skill for others. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + Config.FindItem = false; // Use Find Item skill on corpses after clearing. + Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills + Config.UseWarcries = true; // use battle orders, battle command, and shout if we have them + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet + //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces + + // The gems not used by other recipes will be used for magic item rerolling. + + //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + + //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling + + //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); + + //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 7317ac3b1..6427ba5ce 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -15,725 +15,744 @@ * Javascript statements need to end with a semi-colon; Good: Scripts.Corpsefire = false; Bad: Scripts.Corpsefire = false */ -function LoadConfig() { - /* Sequence config - * Set to true if you want to run it, set to false if not. - * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. - */ - - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - // ## Team MF - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal - Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed - Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto - Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - /* - * Unless stated otherwise, leader's character name isn't needed on order to run. - * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - */ - - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - - // ##### MFHelper ##### // - // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 - // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited - Scripts.MFHelper = false; - - // ###################### // - /* ##### Pure Leech ##### */ - // ###################### // - - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - - // ########################## // - /* ##### Helper SCRIPTS ##### */ - // ########################## // - - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, +function LoadConfig () { + /* Sequence config + * Set to true if you want to run it, set to false if not. + * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. + */ + + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + // ## Team MF + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal + Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed + Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto + Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + /* + * Unless stated otherwise, leader's character name isn't needed on order to run. + * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + */ + + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + + // ##### MFHelper ##### // + // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 + // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited + Scripts.MFHelper = false; + + // ###################### // + /* ##### Pure Leech ##### */ + // ###################### // + + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + + // ########################## // + /* ##### Helper SCRIPTS ##### */ + // ########################## // + + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /* Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 245; - * GOOD: Config.AttackSkill[1] = sdk.skills.Tornado; - * BAD: Config.AttackSkill[1] = -245; - * BAD: Config.AttackSkill[1] = "Tornado"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - Config.SummonRaven = false; - Config.SummonAnimal = "Grizzly"; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly - Config.SummonSpirit = "Oak Sage"; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" - Config.SummonVine = "Poison Creeper"; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces - - // The gems not used by other recipes will be used for magic item rerolling. - - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling - - //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); - - //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /* Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + //Config.PickitFiles.push("kolton.nip"); + //Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 245; + * GOOD: Config.AttackSkill[1] = sdk.skills.Tornado; + * BAD: Config.AttackSkill[1] = -245; + * BAD: Config.AttackSkill[1] = "Tornado"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + Config.SummonRaven = false; + Config.SummonAnimal = "Grizzly"; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly + Config.SummonSpirit = "Oak Sage"; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" + Config.SummonVine = "Poison Creeper"; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet + //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces + + // The gems not used by other recipes will be used for magic item rerolling. + + //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + + //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling + + //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); + + //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 296d04c99..e1220543a 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -15,746 +15,765 @@ * Javascript statements need to end with a semi-colon; Good: Scripts.Corpsefire = false; Bad: Scripts.Corpsefire = false */ -function LoadConfig() { - /* Sequence config - * Set to true if you want to run it, set to false if not. - * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. - */ - - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - // ## Team MF - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal - Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed - Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto - Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - /* - * Unless stated otherwise, leader's character name isn't needed on order to run. - * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - */ - - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - - // ##### MFHelper ##### // - // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 - // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited - Scripts.MFHelper = false; - - // ###################### // - /* ##### Pure Leech ##### */ - // ###################### // - - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - - // ########################## // - /* ##### Helper SCRIPTS ##### */ - // ########################## // - - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, +function LoadConfig () { + /* Sequence config + * Set to true if you want to run it, set to false if not. + * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. + */ + + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + // ## Team MF + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal + Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed + Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto + Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + /* + * Unless stated otherwise, leader's character name isn't needed on order to run. + * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + */ + + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + + // ##### MFHelper ##### // + // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 + // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited + Scripts.MFHelper = false; + + // ###################### // + /* ##### Pure Leech ##### */ + // ###################### // + + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + + // ########################## // + /* ##### Helper SCRIPTS ##### */ + // ########################## // + + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /* Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 84; - * GOOD: Config.AttackSkill[1] = sdk.skills.BoneSpear; - * BAD: Config.AttackSkill[1] = -84; - * BAD: Config.AttackSkill[1] = "BoneSpear"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - Config.Curse[0] = 0; // Boss curse. Use skill number or set to 0 to disable. - Config.Curse[1] = 0; // Other monsters curse. Use skill number or set to 0 to disable. - - /* Custom curses for monster - * Can use monster name or classid - * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; - * Optional 3rd parameter for spectype, leave blank to use on all - 0x00 Normal Monster - 0x01 Super Unique - 0x02 Champion - 0x04 Boss - 0x08 Minion - Example: Config.CustomCurse = [["HellBovine", 60], [571, 87], ["SkeletonArcher", 71, 0x00]]; - */ - Config.CustomCurse = []; - - Config.ExplodeCorpses = 0; // Explode corpses. Use skill number or 0 to disable. 74 = Corpse Explosion, 83 = Poison Explosion - Config.Golem = "None"; // Golem. 0 or "None" = don't summon, 1 or "Clay" = Clay Golem, 2 or "Blood" = Blood Golem, 3 or "Fire" = Fire Golem - Config.Skeletons = 0; // Number of skeletons to raise. Set to "max" to auto detect, set to 0 to disable. - Config.SkeletonMages = 0; // Number of skeleton mages to raise. Set to "max" to auto detect, set to 0 to disable. - Config.Revives = 0; // Number of revives to raise. Set to "max" to auto detect, set to 0 to disable. - Config.PoisonNovaDelay = 2; // Delay between two Poison Novas in seconds. - Config.ActiveSummon = false; // Raise dead between each attack. If false, it will raise after clearing a spot. - Config.ReviveUnstackable = true; // Revive monsters that can move freely after you teleport. - Config.IronGolemChicken = 30; // Exit game if Iron Golem's life is less or equal to designated percent. - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces - - // The gems not used by other recipes will be used for magic item rerolling. - - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling - - //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); - - //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /* Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + //Config.PickitFiles.push("kolton.nip"); + //Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 84; + * GOOD: Config.AttackSkill[1] = sdk.skills.BoneSpear; + * BAD: Config.AttackSkill[1] = -84; + * BAD: Config.AttackSkill[1] = "BoneSpear"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + Config.Curse[0] = 0; // Boss curse. Use skill number or set to 0 to disable. + Config.Curse[1] = 0; // Other monsters curse. Use skill number or set to 0 to disable. + + /* Custom curses for monster + * Can use monster name or classid + * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; + * Optional 3rd parameter for spectype, leave blank to use on all + 0x00 Normal Monster + 0x01 Super Unique + 0x02 Champion + 0x04 Boss + 0x08 Minion + Example: Config.CustomCurse = [["HellBovine", 60], [571, 87], ["SkeletonArcher", 71, 0x00]]; + */ + Config.CustomCurse = []; + + Config.ExplodeCorpses = 0; // Explode corpses. Use skill number or 0 to disable. 74 = Corpse Explosion, 83 = Poison Explosion + Config.Golem = "None"; // Golem. 0 or "None" = don't summon, 1 or "Clay" = Clay Golem, 2 or "Blood" = Blood Golem, 3 or "Fire" = Fire Golem + Config.Skeletons = 0; // Number of skeletons to raise. Set to "max" to auto detect, set to 0 to disable. + Config.SkeletonMages = 0; // Number of skeleton mages to raise. Set to "max" to auto detect, set to 0 to disable. + Config.Revives = 0; // Number of revives to raise. Set to "max" to auto detect, set to 0 to disable. + Config.PoisonNovaDelay = 2; // Delay between two Poison Novas in seconds. + Config.ActiveSummon = false; // Raise dead between each attack. If false, it will raise after clearing a spot. + Config.ReviveUnstackable = true; // Revive monsters that can move freely after you teleport. + Config.IronGolemChicken = 30; // Exit game if Iron Golem's life is less or equal to designated percent. + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet + //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces + + // The gems not used by other recipes will be used for magic item rerolling. + + //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + + //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling + + //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); + + //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index bf80342d3..724c99dbf 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -15,725 +15,744 @@ * Javascript statements need to end with a semi-colon; Good: Scripts.Corpsefire = false; Bad: Scripts.Corpsefire = false */ -function LoadConfig() { - /* Sequence config - * Set to true if you want to run it, set to false if not. - * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. - */ - - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - // ## Team MF - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal - Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed - Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto - Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - /* - * Unless stated otherwise, leader's character name isn't needed on order to run. - * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - */ - - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - - // ##### MFHelper ##### // - // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 - // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited - Scripts.MFHelper = false; - - // ###################### // - /* ##### Pure Leech ##### */ - // ###################### // - - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - - // ########################## // - /* ##### Helper SCRIPTS ##### */ - // ########################## // - - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, +function LoadConfig () { + /* Sequence config + * Set to true if you want to run it, set to false if not. + * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. + */ + + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + // ## Team MF + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal + Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed + Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto + Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + /* + * Unless stated otherwise, leader's character name isn't needed on order to run. + * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + */ + + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + + // ##### MFHelper ##### // + // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 + // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited + Scripts.MFHelper = false; + + // ###################### // + /* ##### Pure Leech ##### */ + // ###################### // + + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + + // ########################## // + /* ##### Helper SCRIPTS ##### */ + // ########################## // + + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /* Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 97; - * GOOD: Config.AttackSkill[1] = sdk.skills.Smite; - * BAD: Config.AttackSkill[1] = -97; - * BAD: Config.AttackSkill[1] = "smite"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary aura to bosses - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary aura to others. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary aura. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Low mana skill. - Config.LowManaSkill[1] = -1; // Low mana aura. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. - Config.Vigor = true; // Swith to Vigor when running - Config.Charge = true; // Use Charge when running - Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces - - // The gems not used by other recipes will be used for magic item rerolling. - - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling - - //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); - - //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /* Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + //Config.PickitFiles.push("kolton.nip"); + //Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 97; + * GOOD: Config.AttackSkill[1] = sdk.skills.Smite; + * BAD: Config.AttackSkill[1] = -97; + * BAD: Config.AttackSkill[1] = "smite"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary aura to bosses + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary aura to others. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary aura. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Low mana skill. + Config.LowManaSkill[1] = -1; // Low mana aura. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. + Config.Vigor = true; // Swith to Vigor when running + Config.Charge = true; // Use Charge when running + Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet + //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces + + // The gems not used by other recipes will be used for magic item rerolling. + + //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + + //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling + + //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); + + //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index d45461a85..e0cfd0adb 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -15,727 +15,746 @@ * Javascript statements need to end with a semi-colon; Good: Scripts.Corpsefire = false; Bad: Scripts.Corpsefire = false */ -function LoadConfig() { - /* Sequence config - * Set to true if you want to run it, set to false if not. - * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. - */ - - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - // ## Team MF - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal - Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed - Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto - Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - /* - * Unless stated otherwise, leader's character name isn't needed on order to run. - * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - */ - - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - - // ##### MFHelper ##### // - // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 - // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited - Scripts.MFHelper = false; - - // ###################### // - /* ##### Pure Leech ##### */ - // ###################### // - - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - - // ########################## // - /* ##### Helper SCRIPTS ##### */ - // ########################## // - - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, +function LoadConfig () { + /* Sequence config + * Set to true if you want to run it, set to false if not. + * If you want to change the order of the scripts, just change the order of their lines by using cut and paste. + */ + + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = true; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters (for example BO barb + sorc) + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + // ## Team MF + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + Config.Cows.DontMakePortal = false; // if set to true, will go to act 1 stash and wait for 3 minutes for someone to make the cow portal + Config.Cows.JustMakePortal = false; // if set to true just opens cow portal but doesn't clear - useful to ensure maker never gets king killed + Config.Cows.KillKing = false; // MAKE SURE YOUR MAKER DOESN"T HAVE THIS SET TO TRUE!!!! + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto + Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + /* + * Unless stated otherwise, leader's character name isn't needed on order to run. + * Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + */ + + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + + // ##### MFHelper ##### // + // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true and Config.PublicMode > 0 + // NOTE: MFHelper ends when Config.Leader starts Diablo or Baal. Use one of the specific helper scripts as they are better suited + Scripts.MFHelper = false; + + // ###################### // + /* ##### Pure Leech ##### */ + // ###################### // + + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + + // ########################## // + /* ##### Helper SCRIPTS ##### */ + // ########################## // + + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{Ping: 0, Duration: 0}]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /* Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /* Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - //Config.PickitFiles.push("kolton.nip"); - //Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // Uncomment the following line to always attempt to kill these bosses despite immunities and mods - //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /* Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 56; - * GOOD: Config.AttackSkill[1] = sdk.skills.Meteor; - * BAD: Config.AttackSkill[1] = -56; - * BAD: Config.AttackSkill[1] = "meteor"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // Config.ClearPath = { - // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - // Range: 30, // Range to clear while traveling - // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - // }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - Config.CastStatic = 60; // Cast static until the target is at designated life percent. 100 = disabled. - Config.StaticList = []; // List of monster NAMES or CLASSIDS to static. Example: Config.StaticList = ["Andariel", 243]; - Config.UseTelekinesis = true; // Use telekinesis on units that allow it. Example: Shrines, Waypoints, Chests, and Portals - Config.UseEnergyShield = false; // set to true to use energy shield if its available - Config.UseColdArmor = true; // use armor skills, uses skill ids or set to true to let the bot decide based on skill level or false to disable completely - // (40 / sdk.skills.FrozenArmor)(50 / sdk.skills.ShiverArmor)(60 / sdk.skills.ChillingArmor) - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet - //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces - - // The gems not used by other recipes will be used for magic item rerolling. - - //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem - //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - - //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling - - //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); - - //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /* Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /* Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + //Config.PickitFiles.push("kolton.nip"); + //Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // Uncomment the following line to always attempt to kill these bosses despite immunities and mods + //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /* Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 56; + * GOOD: Config.AttackSkill[1] = sdk.skills.Meteor; + * BAD: Config.AttackSkill[1] = -56; + * BAD: Config.AttackSkill[1] = "meteor"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + // Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // Config.ClearPath = { + // Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + // Range: 30, // Range to clear while traveling + // Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + // }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + Config.CastStatic = 60; // Cast static until the target is at designated life percent. 100 = disabled. + Config.StaticList = []; // List of monster NAMES or CLASSIDS to static. Example: Config.StaticList = ["Andariel", 243]; + Config.UseTelekinesis = true; // Use telekinesis on units that allow it. Example: Shrines, Waypoints, Chests, and Portals + Config.UseEnergyShield = false; // set to true to use energy shield if its available + Config.UseColdArmor = true; // use armor skills, uses skill ids or set to true to let the bot decide based on skill level or false to disable completely + // (40 / sdk.skills.FrozenArmor)(50 / sdk.skills.ShiverArmor)(60 / sdk.skills.ChillingArmor) + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + //Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + //Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + //Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + //Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Armet + //Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Vambraces + + // The gems not used by other recipes will be used for magic item rerolling. + + //Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem + //Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + + //Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + /* Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + //Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + //Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + //Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + //Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + //Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = false; // Set to true to enable runeword making/rerolling + + //Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + //Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + //Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); + + //Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + //Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + //Config.KeepRunewords.push("[type] == shield || [type] == auricshields # [fcr] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /* AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; } diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index a6164f291..6c6d961d7 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -4,926 +4,945 @@ * - copy the desired script/config section and paste it to your character configuration file, named Class.CharName.js */ - // User addon script. Read the description in libs/scripts/UserAddon.js - Scripts.UserAddon = false; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! - - // Battle orders script - Use this for 2+ characters - Scripts.BattleOrders = false; - Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO - Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. - Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. - Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails - Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot - Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) - - Scripts.BoBarbHelper = false; // specific HC script with BoBarb on the Bo area during whole game | set it only in barbarian config - Config.BoBarbHelper.Mode = -1; // 0 = give BO, -1 = disabled - Config.BoBarbHelper.Wp = 35; // 35 = Catacombs level 2 - - // ## Team MF system - Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. - Scripts.MFHelper = false; // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true - Config.BreakClearLevel = false; // Stop clearing the current area if the leader goes to another area - - // ############################# // - /* ##### LEECHING SETTINGS ##### */ - // ############################# // - - // leader's character name isn't needed on order to run. Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) - Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) - Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; - Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). - Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. - - // ############################# // - /* ##### BOSS/AREA SCRIPTS ##### */ - // ############################# // - - // *** act 1 *** - Scripts.Corpsefire = false; - Config.Corpsefire.ClearDen = false; - Scripts.Bishibosh = false; - Scripts.Mausoleum = false; - Config.Mausoleum.KillBishibosh = false; - Config.Mausoleum.KillBloodRaven = false; - Config.Mausoleum.ClearCrypt = false; - Scripts.Rakanishu = false; - Config.Rakanishu.KillGriswold = true; - Scripts.UndergroundPassage = false; - Scripts.Coldcrow = false; - Scripts.Tristram = false; - Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Pit = false; - Config.Pit.ClearPit1 = true; - Scripts.Treehead = false; - Scripts.Smith = false; - Scripts.BoneAsh = false; - Scripts.Countess = false; - Config.Countess.KillGhosts = false; - Scripts.Andariel = false; - Scripts.Cows = false; - - // *** act 2 *** - Scripts.Radament = false; - Scripts.CreepingFeature = false; - Scripts.Coldworm = false; - Config.Coldworm.KillBeetleburst = false; - Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels - Scripts.AncientTunnels = false; - Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City - Config.AncientTunnels.KillDarkElder = false; - Scripts.Summoner = false; - Config.Summoner.FireEye = false; - Scripts.Tombs = false; - Config.Tombs.KillDuriel = false; - Scripts.Duriel = false; - - // *** act 3 *** - Scripts.Stormtree = false; - Scripts.BattlemaidSarina = false; - Scripts.KurastTemples = false; - Scripts.Icehawk = false; - Scripts.Endugu = false; - Scripts.Travincal = false; - Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. - Scripts.Mephisto = false; - Config.Mephisto.MoatTrick = false; - Config.Mephisto.KillCouncil = false; - Config.Mephisto.TakeRedPortal = true; - - // *** act 4 *** - Scripts.OuterSteppes = false; - Scripts.Izual = false; - Scripts.Hephasto = false; - Scripts.Diablo = false; - Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals - Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers - Config.Diablo.Entrance = true; // Start from entrance - Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. - Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. - Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path - Config.Diablo.SealWarning = "Leave the seals alone!"; - Config.Diablo.EntranceTP = "Entrance TP up"; - Config.Diablo.StarTP = "Star TP up"; - Config.Diablo.DiabloMsg = "Diablo"; - Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - - // *** act 5 *** - Scripts.Pindleskin = false; - Config.Pindleskin.UseWaypoint = false; - Config.Pindleskin.KillNihlathak = true; - Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. - Scripts.Nihlathak = false; - Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. - Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal - Scripts.Eldritch = false; - Config.Eldritch.OpenChest = true; - Config.Eldritch.KillShenk = true; - Config.Eldritch.KillDacFarren = true; - Scripts.Eyeback = false; - Scripts.SharpTooth = false; - Scripts.ThreshSocket = false; - Scripts.Abaddon = false; - Scripts.Frozenstein = false; - Config.Frozenstein.ClearFrozenRiver = true; - Scripts.Bonesaw = false; - Config.Bonesaw.ClearDrifterCavern = false; - Scripts.Snapchip = false; - Config.Snapchip.ClearIcyCellar = true; - Scripts.Worldstone = false; - Scripts.Baal = false; - Config.Baal.HotTPMessage = "Hot TP!"; - Config.Baal.SafeTPMessage = "Safe TP!"; - Config.Baal.BaalMessage = "Baal!"; - Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. - Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. - - // ############################ // - /* ##### LEECHING SCRIPTS ##### */ - // ############################ // - - Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. - Config.TristramLeech.Helper = false; // If set to true the character will help attack. - Scripts.TravincalLeech = false; // Enters portal at back of Travincal. - Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. - Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader - Config.Wakka.Wait = 1; // Minutes to wait for leader - Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached - Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next - Config.SkipIfBaal = true; // end script it leader is in throne of destruction - Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. - Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. - Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. - Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals - Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. - Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. - Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. - Config.DiabloHelper.OpenSeals = false; // Open seals as the helper - Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast - Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear - Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear - Scripts.AutoBaal = false; // Baal leecher with auto leader assignment - Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found - Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot - Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot - Scripts.BaalHelper = false; - Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne - Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. - Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. - Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. - - // Baal Assistant by YourGreatestMember - Scripts.BaalAssistant = false; // Used to leech or help in baal runs. - Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... - Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne - Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne - Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. - Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. - Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage - Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. - Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) - Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) - Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) - Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. - Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. - Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. - Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. - Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList - - // AutoChaos by noah- https://gist.github.com/noah-/2685fbeccc72fd595bbe89116aea272e, an anonymous Team CS script without explicit in-game communication. It requires at least 1 Sorceress, 1 Barbarian, and 1 Paladin (intended for Classic CS) - Scripts.AutoChaos = false; - Config.AutoChaos.Taxi = false; - Config.AutoChaos.FindShrine = false; // set true to search for shrine only - Config.AutoChaos.Glitcher = false; // set true for low level EXP glitcher (unimplemented) - Config.AutoChaos.SealOrder = [1, 2, 3]; // order in which the taxi will go through cs, 1: vizier, 2: seis, 3: infector - Config.AutoChaos.PreAttack = [0, 0, 0]; // preattack count at each seal, useful for clearing tp's for safer entry, enter values in the following order: [/vizier/, /seis/, /infector/] - Config.AutoChaos.Diablo = 0; // -1 = go to town during diablo, 0 = kill to death, x > 0 = kill to x% - Config.AutoChaos.UseShrine = false; // true = get shrine from act 1 (requires another character running FindShrine) - Config.AutoChaos.Leech = false; // true = hide during diablo, false = stay at star - Config.AutoChaos.Ranged = false; // true = ranged character, false = melee character - Config.AutoChaos.BO = false; // true = don't enter seals after boing at river, false = normal character that fights - Config.AutoChaos.SealPrecast = false; // true = does precast sequence at every seal, false = does not precast at seal - Config.AutoChaos.SealDelay = 0; // number of seconds to wait before entering hot tp - - // ########################### // - /* ##### SPECIAL SCRIPTS ##### */ - // ########################### // - - // ##### ONCE SCRIPTS ##### // - Scripts.WPGetter = false; // Get missing waypoints - Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) - Config.Questing.StopProfile = false; // set to true to shut down profile after completion - - // ##### CONTROL SCRIPTS ##### // - Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js - Scripts.ControlBot = false; - Config.ControlBot.Bo = true; // Bo player at waypoint - Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can - Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. - Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command - Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions - Config.ControlBot.Wps.GiveWps = true; // Give wps on command - Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal - Config.ControlBot.EndMessage = ""; // Message before quitting - Config.ControlBot.GameLength = 20; // Game length in minutes - - // ##### ORG/TORCH ##### // - Scripts.GetKeys = false; // Hunt for T/H/D keys - Scripts.OrgTorch = false; - Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches - Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info - Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on - Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) - Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. - Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area - Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes - Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area - Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes - - // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare - - // ##### MANUAL RUSH ##### // - Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key - - // ##### MISC SCRIPTS ##### // - Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js - Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js - Scripts.IPHunter = false; - Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] - Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found - Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. - // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] - // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. - Config.ShopBot.ShopNPC = NPC.Anya; - // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt - Config.ShopBot.ScanIDs = []; - Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. - Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. - - // ##### EXTRA SCRIPTS ##### // - Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) - Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - // List of act 1 areas to open chests in - Config.ChestMania.Act1 = [ - sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, + // User addon script. Read the description in libs/scripts/UserAddon.js + Scripts.UserAddon = false; // !!!YOU MUST SET THIS TO FALSE IF YOU WANT TO RUN BOSS/AREA SCRIPTS!!! + + // Battle orders script - Use this for 2+ characters + Scripts.BattleOrders = false; + Config.BattleOrders.Mode = 0; // 0 = give BO, 1 = get BO + Config.BattleOrders.Idle = false; // Idle until the player that received BO leaves. + Config.BattleOrders.Getters = []; // List of players to wait for before casting Battle Orders (mode 0). All players must be in the same area as the BOer. + Config.BattleOrders.QuitOnFailure = false; // Quit the game if BO fails + Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot + Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + + Scripts.BoBarbHelper = false; // specific HC script with BoBarb on the Bo area during whole game | set it only in barbarian config + Config.BoBarbHelper.Mode = -1; // 0 = give BO, -1 = disabled + Config.BoBarbHelper.Wp = 35; // 35 = Catacombs level 2 + + // ## Team MF system + Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. + Scripts.MFHelper = false; // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true + Config.BreakClearLevel = false; // Stop clearing the current area if the leader goes to another area + + // ############################# // + /* ##### LEECHING SETTINGS ##### */ + // ############################# // + + // leader's character name isn't needed on order to run. Don't use more scripts of the same type! (Run AutoBaal OR BaalHelper, not both) + Config.Leader = ""; // Leader's ingame character name. Leave blank to try auto-detection (works in AutoBaal, Wakka, MFHelper) + Config.QuitList = [""]; // List of character names to quit with. Example: Config.QuitList = ["MySorc", "MyDin"]; + Config.QuitListMode = 0; // 0 = use character names; 1 = use profile names (all profiles must run on the same computer). + Config.QuitListDelay = []; // Quit the game with random delay in case of using Config.QuitList. Example: Config.QuitListDelay = [1, 10]; will exit with random delay between 1 and 10 seconds. + + // ############################# // + /* ##### BOSS/AREA SCRIPTS ##### */ + // ############################# // + + // *** act 1 *** + Scripts.Corpsefire = false; + Config.Corpsefire.ClearDen = false; + Scripts.Bishibosh = false; + Scripts.Mausoleum = false; + Config.Mausoleum.KillBishibosh = false; + Config.Mausoleum.KillBloodRaven = false; + Config.Mausoleum.ClearCrypt = false; + Scripts.Rakanishu = false; + Config.Rakanishu.KillGriswold = true; + Scripts.UndergroundPassage = false; + Scripts.Coldcrow = false; + Scripts.Tristram = false; + Config.Tristram.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Tristram.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Pit = false; + Config.Pit.ClearPit1 = true; + Scripts.Treehead = false; + Scripts.Smith = false; + Scripts.BoneAsh = false; + Scripts.Countess = false; + Config.Countess.KillGhosts = false; + Scripts.Andariel = false; + Scripts.Cows = false; + + // *** act 2 *** + Scripts.Radament = false; + Scripts.CreepingFeature = false; + Scripts.Coldworm = false; + Config.Coldworm.KillBeetleburst = false; + Config.Coldworm.ClearMaggotLair = false; // Clear all 3 levels + Scripts.AncientTunnels = false; + Config.AncientTunnels.OpenChest = false; // Open special chest in Lost City + Config.AncientTunnels.KillDarkElder = false; + Scripts.Summoner = false; + Config.Summoner.FireEye = false; + Scripts.Tombs = false; + Config.Tombs.KillDuriel = false; + Scripts.Duriel = false; + + // *** act 3 *** + Scripts.Stormtree = false; + Scripts.BattlemaidSarina = false; + Scripts.KurastTemples = false; + Scripts.Icehawk = false; + Scripts.Endugu = false; + Scripts.Travincal = false; + Config.Travincal.PortalLeech = false; // Set to true to open a portal for leechers. + Scripts.Mephisto = false; + Config.Mephisto.MoatTrick = false; + Config.Mephisto.KillCouncil = false; + Config.Mephisto.TakeRedPortal = true; + + // *** act 4 *** + Scripts.OuterSteppes = false; + Scripts.Izual = false; + Scripts.Hephasto = false; + Scripts.Diablo = false; + Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals + Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers + Config.Diablo.Entrance = true; // Start from entrance + Config.Diablo.JustViz = false; // Intended for classic sorc, kills Vizier only. + Config.Diablo.SealLeader = false; // Clear a safe spot around seals and invite leechers in. Leechers should run SealLeecher script. + Config.Diablo.Fast = false; // Runs diablo fast, focuses on clearing seal bosses rather than clearing path + Config.Diablo.SealWarning = "Leave the seals alone!"; + Config.Diablo.EntranceTP = "Entrance TP up"; + Config.Diablo.StarTP = "Star TP up"; + Config.Diablo.DiabloMsg = "Diablo"; + Config.Diablo.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + + // *** act 5 *** + Scripts.Pindleskin = false; + Config.Pindleskin.UseWaypoint = false; + Config.Pindleskin.KillNihlathak = true; + Config.Pindleskin.ViperQuit = false; // End script if Tomb Vipers are found. + Scripts.Nihlathak = false; + Config.Nihlathak.ViperQuit = false; // End script if Tomb Vipers are found. + Config.Nihlathak.UseWaypoint = false; // Use waypoint to Nith, if false uses anya portal + Scripts.Eldritch = false; + Config.Eldritch.OpenChest = true; + Config.Eldritch.KillShenk = true; + Config.Eldritch.KillDacFarren = true; + Scripts.Eyeback = false; + Scripts.SharpTooth = false; + Scripts.ThreshSocket = false; + Scripts.Abaddon = false; + Scripts.Frozenstein = false; + Config.Frozenstein.ClearFrozenRiver = true; + Scripts.Bonesaw = false; + Config.Bonesaw.ClearDrifterCavern = false; + Scripts.Snapchip = false; + Config.Snapchip.ClearIcyCellar = true; + Scripts.Worldstone = false; + Scripts.Baal = false; + Config.Baal.HotTPMessage = "Hot TP!"; + Config.Baal.SafeTPMessage = "Safe TP!"; + Config.Baal.BaalMessage = "Baal!"; + Config.Baal.SoulQuit = false; // End script if Souls (Burning Souls) are found. + Config.Baal.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.Baal.KillBaal = true; // Kill Baal. Leaves game after wave 5 if false. + + // ############################ // + /* ##### LEECHING SCRIPTS ##### */ + // ############################ // + + Scripts.TristramLeech = false; // Enters Tristram, attempts to stay close to the leader and will try and help kill. + Config.TristramLeech.Helper = false; // If set to true the character will help attack. + Scripts.TravincalLeech = false; // Enters portal at back of Travincal. + Config.TravincalLeech.Helper = true; // If set to true the character will teleport to the stairs and help attack. + Scripts.Wakka = false; // Walking chaos leecher with auto leader assignment, stays at safe distance from the leader + Config.Wakka.Wait = 1; // Minutes to wait for leader + Config.Wakka.StopAtLevel = 99; // Stop wakka when this level is reached + Config.Wakka.StopProfile = false; // when StopAtLevel is reached, set to true to stop the profile, false to end script and move on to next + Config.SkipIfBaal = true; // end script it leader is in throne of destruction + Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. + Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. + Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals + Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. + Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. + Config.DiabloHelper.SkipIfBaal = false; // End script if there are party members in a Baal run. + Config.DiabloHelper.OpenSeals = false; // Open seals as the helper + Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast + Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear + Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Scripts.AutoBaal = false; // Baal leecher with auto leader assignment + Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found + Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot + Config.AutoBaal.LongRangeSupport = false; // Cast long distance skills from a safe spot + Scripts.BaalHelper = false; + Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne + Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. + Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. + + // Baal Assistant by YourGreatestMember + Scripts.BaalAssistant = false; // Used to leech or help in baal runs. + Config.BaalAssistant.Wait = 120; // Seconds to wait for a runner to be in the throne / portal wait / safe TP wait / hot TP wait... + Config.BaalAssistant.KillNihlathak = false; // Kill Nihlathak before going to Throne + Config.BaalAssistant.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalAssistant.Helper = true; // Set to true to help attack, set false to to leech. + Config.BaalAssistant.GetShrine = false; // Set to true to get a experience shrine at the start of the run. + Config.BaalAssistant.GetShrineWaitForHotTP = false; // Set to true to get a experience shrine after leader shouts the hot tp message as defined in Config.BaalAssistant.HotTPMessage + Config.BaalAssistant.SkipTP = false; // Set to true to enable the helper to skip the TP and teleport down to the throne room. + Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) + Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) + Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. + Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. + Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. + Config.BaalAssistant.BaalMessage = ["Baal"]; // Configure baal messages, this is a precautionary measure. + Config.BaalAssistant.NextGameMessage = ["Next Game", "Next", "New Game"]; // Next Game message, this is a precautionary quit command, Reccomended setting up: Config.QuitList + + // AutoChaos by noah- https://gist.github.com/noah-/2685fbeccc72fd595bbe89116aea272e, an anonymous Team CS script without explicit in-game communication. It requires at least 1 Sorceress, 1 Barbarian, and 1 Paladin (intended for Classic CS) + Scripts.AutoChaos = false; + Config.AutoChaos.Taxi = false; + Config.AutoChaos.FindShrine = false; // set true to search for shrine only + Config.AutoChaos.Glitcher = false; // set true for low level EXP glitcher (unimplemented) + Config.AutoChaos.SealOrder = [1, 2, 3]; // order in which the taxi will go through cs, 1: vizier, 2: seis, 3: infector + Config.AutoChaos.PreAttack = [0, 0, 0]; // preattack count at each seal, useful for clearing tp's for safer entry, enter values in the following order: [/vizier/, /seis/, /infector/] + Config.AutoChaos.Diablo = 0; // -1 = go to town during diablo, 0 = kill to death, x > 0 = kill to x% + Config.AutoChaos.UseShrine = false; // true = get shrine from act 1 (requires another character running FindShrine) + Config.AutoChaos.Leech = false; // true = hide during diablo, false = stay at star + Config.AutoChaos.Ranged = false; // true = ranged character, false = melee character + Config.AutoChaos.BO = false; // true = don't enter seals after boing at river, false = normal character that fights + Config.AutoChaos.SealPrecast = false; // true = does precast sequence at every seal, false = does not precast at seal + Config.AutoChaos.SealDelay = 0; // number of seconds to wait before entering hot tp + + // ########################### // + /* ##### SPECIAL SCRIPTS ##### */ + // ########################### // + + // ##### ONCE SCRIPTS ##### // + Scripts.WPGetter = false; // Get missing waypoints + Scripts.Questing = false; // Finish missing quests (skill/stat+shenk+ancients) + Config.Questing.StopProfile = false; // set to true to shut down profile after completion + + // ##### CONTROL SCRIPTS ##### // + Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js + Scripts.ControlBot = false; + Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can + Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. + Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command + Config.ControlBot.Chant.AutoEnchant = true; // Automatically enchant nearby players and their minions + Config.ControlBot.Wps.GiveWps = true; // Give wps on command + Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal + Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Cube = true; // Get cube on command + Config.ControlBot.Rush.Radament = true; // Kill Radament on command + Config.ControlBot.Rush.Staff = true; // Get staff on command + Config.ControlBot.Rush.Amulet = true; // Get amulet on command + Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command + Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command + Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command + Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command + Config.ControlBot.Rush.Brain = true; // Get Khalim's brain on command + Config.ControlBot.Rush.Travincal = true; // Kill Travincal on command + Config.ControlBot.Rush.Mephisto = true; // Kill Mephisto on command + Config.ControlBot.Rush.Izual = true; // Kill Izual on command + Config.ControlBot.Rush.Diablo = true; // Kill Diablo on command + Config.ControlBot.Rush.Shenk = true; // Kill Shenk on command + Config.ControlBot.Rush.Anya = true; // Rescue Anya on command + Config.ControlBot.Rush.Ancients = true; // Kill Ancients on command + Config.ControlBot.Rush.Baal = true; // Kill Baal on command + Config.ControlBot.EndMessage = ""; // Message before quitting + Config.ControlBot.GameLength = 20; // Game length in minutes + + // ##### ORG/TORCH ##### // + Scripts.GetKeys = false; // Hunt for T/H/D keys + Scripts.OrgTorch = false; + Config.OrgTorch.MakeTorch = true; // Convert organ sets to torches + Config.OrgTorch.WaitForKeys = true; // Enable Torch System to get keys from other profiles. See libs/TorchSystem.js for more info + Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on + Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) + Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area + Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes + Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area + Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + // ##### AUTO-RUSH ##### // + // RUSHER USES FOLLOWER ENTRY SCRIPT + Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js + Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). + Config.Rusher.Cain = false; // Do cain quest. + Config.Rusher.Radament = false; // Do Radament quest. + Config.Rusher.LamEsen = false; // Do Lam Esen quest. + Config.Rusher.Izual = false; // Do Izual quest. + Config.Rusher.Shenk = false; // Do Shenk quest. + Config.Rusher.Anya = false; // Do Anya quest. + Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) + Config.Rusher.GiveWps = false; // Give all Wps + Config.Rusher.LastRun = ""; // End rush after this run. + // RUSHEE USES LEADER ENTRY SCRIPT + Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader + Config.Rushee.Quester = false; // Enter portals and get quest items. + Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + + // ##### MANUAL RUSH ##### // + Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key + + // ##### MISC SCRIPTS ##### // + Scripts.Gamble = false; // Gambling system, other characters will mule gold into your game so you can gamble infinitely. See Gambling.js + Scripts.Crafting = false; // Crafting system, other characters will mule crafting ingredients. See CraftingSystem.js + Scripts.IPHunter = false; + Config.IPHunter.IPList = []; // List of IPs to look for. example: [165, 201, 64] + Config.IPHunter.GameLength = 3; // Number of minutes to stay in game if ip wasn't found + Scripts.ShopBot = false; // Shopbot script. Automatically uses shopbot.nip and ignores other pickits. + // Supported NPCs: Akara, Charsi, Gheed, Elzix, Fara, Drognan, Ormus, Asheara, Hratli, Jamella, Halbu, Anya. Multiple NPCs are also supported, example: [NPC.Elzix, NPC.Fara] + // Use common sense when combining NPCs. Shopping in different acts will probably lead to bugs. + Config.ShopBot.ShopNPC = NPC.Anya; + // Put item classid numbers or names to scan (remember to put quotes around names). Leave blank to scan ALL items. See libs/config/templates/ShopBot.txt + Config.ShopBot.ScanIDs = []; + Config.ShopBot.CycleDelay = 0; // Delay between shopping cycles in milliseconds, might help with crashes. + Config.ShopBot.QuitOnMatch = false; // Leave game as soon as an item is shopped. + + // ##### EXTRA SCRIPTS ##### // + Scripts.GhostBusters = false; // Kill ghosts in most areas that contain them (rune hunting) + Scripts.ChestMania = false; // Open chests in configured areas. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + // List of act 1 areas to open chests in + Config.ChestMania.Act1 = [ + sdk.areas.CaveLvl2, sdk.areas.UndergroundPassageLvl2, sdk.areas.HoleLvl2, sdk.areas.PitLvl2, sdk.areas.Crypt, sdk.areas.Mausoleum - ]; - // List of act 2 areas to open chests in - Config.ChestMania.Act2 = [ - sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, + ]; + // List of act 2 areas to open chests in + Config.ChestMania.Act2 = [ + sdk.areas.StonyTombLvl1, sdk.areas.StonyTombLvl2, sdk.areas.AncientTunnels, sdk.areas.TalRashasTomb1, sdk.areas.TalRashasTomb2, sdk.areas.TalRashasTomb3, - sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 - ]; - // List of act 3 areas to open chests in - Config.ChestMania.Act3 = [ - sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.TalRashasTomb4, sdk.areas.TalRashasTomb5, sdk.areas.TalRashasTomb6, sdk.areas.TalRashasTomb7 + ]; + // List of act 3 areas to open chests in + Config.ChestMania.Act3 = [ + sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2, - sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 - ]; - // List of act 4 areas to open chests in - Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; - // List of act 5 areas to open chests in - Config.ChestMania.Act5 = [ - sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, + sdk.areas.SpiderCave, sdk.areas.SpiderCavern, sdk.areas.SwampyPitLvl3 + ]; + // List of act 4 areas to open chests in + Config.ChestMania.Act4 = [sdk.areas.RiverofFlame]; + // List of act 5 areas to open chests in + Config.ChestMania.Act5 = [ + sdk.areas.GlacialTrail, sdk.areas.DrifterCavern, sdk.areas.IcyCellar, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit - ]; - Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. - Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - - Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked - // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js - Config.GemHunter.AreaList = [ - sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, - sdk.areas.BlackMarsh, sdk.areas.TamoeHighland - ]; - // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types - Config.GemHunter.GemList = [ - sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, + ]; + Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. + Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt + + Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked + // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js + Config.GemHunter.AreaList = [ + sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, + sdk.areas.BlackMarsh, sdk.areas.TamoeHighland + ]; + // Priority List for Gems to keep in inventory. highest priority first. see \kolbot\libs\modules\sdk.js for gem types + Config.GemHunter.GemList = [ + sdk.items.gems.Flawless.Ruby, sdk.items.gems.Flawless.Amethyst, sdk.items.gems.Flawless.Sapphire, sdk.items.gems.Flawless.Topaz, - sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull - ]; - - // ############################ // - /* #### CHARACTER SETTINGS #### */ - // ############################ // - - // If Config.Leader is set, the bot will only accept invites from leader. - // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. - // If set on true, it simply parties. - Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. - - // General config - Config.AutoMap = false; // Set to true to open automap at the beginning of the game. - Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact - Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. - Config.LogExperience = false; // Print experience statistics in the manager. - - // Chicken settings - Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. - Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. - Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. - Config.TownHP = 0; // Go to town if life is under designated percent. - Config.TownMP = 0; // Go to town if mana is under designated percent. - Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. - - // Town settings - Config.HealHP = 50; // Go to a healer if under designated percent of life. - Config.HealMP = 0; // Go to a healer if under designated percent of mana. - Config.HealStatus = false; // Go to a healer if poisoned or cursed - Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. - Config.MercWatch = false; // Instant merc revive during battle. - Config.TownCheck = false; // Go to town if out of potions - Config.StashGold = 100000; // Minimum amount of gold to stash. - Config.MiniShopBot = true; // Scan items in NPC shops. - Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. - Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. - Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. - - // Item identification settings - Config.CainID.Enable = false; // Identify items at Cain - Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. - Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. - Config.FieldID.Enabled = false; // Identify items while in the field - Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) - 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs - Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; - - // Potion settings - Config.UseHP = 75; // Drink a healing potion if life is under designated percent. - Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. - Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. - Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. - Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. - Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. - Config.HPBuffer = 0; // Number of healing potions to keep in inventory. - Config.MPBuffer = 0; // Number of mana potions to keep in inventory. - Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. - - /** - * Potion types for belt columns from left to right. - * Rejuvenation potions must always be rightmost. - * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") - */ - Config.BeltColumn = ["hp", "hp", "mp", "rv"]; - - /** - * Minimum amount of potions from left to right. - * If we have less, go to vendor to purchase more. - * Set rejuvenation columns to 0, because they can't be bought. - */ - Config.MinColumn = [3, 3, 3, 0]; - - // ############################ // - /* #### INVENTORY SETTINGS #### */ - // ############################ // - /* - * Inventory lock configuration. !!!READ CAREFULLY!!! - * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. - * Put 0s where your torch, annihilus and everything else you want to KEEP is. - * 1 = item is unlocked and will be dropped, stashed or sold. - * If you don't change the default values, the bot won't stash items. - */ - Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - // ########################### // - /* ##### PICKIT SETTINGS ##### */ - // ########################### // - // Default folder is kolbot/pickit. - // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js - - // Config.PickitFiles.push("kolton.nip"); - // Config.PickitFiles.push("LLD.nip"); - Config.PickRange = 40; // Pick radius - Config.FastPick = false; // Check and pick items between attacks - Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. - Config.OpenChests.Enabled = false; // Open chests. Controls key buying. - Config.OpenChests.Range = 15; // radius to scan for chests while pathing - Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names - - // ########################### // - /* ##### PUBLIC SETTINGS ##### */ - // ########################### // - - // ##### CHAT SETTINGS ##### // - Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script - - // LocalChat messages will only be visible on clients running on the same PC - // Highly recommened for online play - // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET - Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET - Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 - Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) - - // Anti-hostile config - Config.AntiHostile = false; // Enable anti-hostile - Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile - Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 - Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. - Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted - - // Party message settings. Each setting represents an array of messages that will be randomly chosen. - // $name, $level, $class and $killer are replaced by the player's name, level, class and killer - Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] - Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] - Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] - Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. - Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. - Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) - - // Shrine Scanner - scan for shrines while moving. - // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt - Config.ScanShrines = []; - - // DClone config - Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth - Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled - Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. - Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled - - // Monster skip config - // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". - // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" - Config.SkipImmune = []; - // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". - // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" - Config.SkipEnchant = []; - // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. - Config.SkipAura = []; - // always attempt to kill these bosses despite immunities and mods - Config.SkipException = []; - // vizier, de seis, infector - // Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; - - // ########################### // - /* ##### ATTACK SETTINGS ##### */ - // ########################### // - - /** - * Attack config - * To disable an attack, set it to -1 - * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js - * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. - * GOOD: Config.AttackSkill[1] = 151; - * GOOD: Config.AttackSkill[1] = sdk.skills.Whirlwind; - * BAD: Config.AttackSkill[1] = -151; - * BAD: Config.AttackSkill[1] = "Whirlwind"; - */ - // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. - Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear - - Config.AttackSkill[0] = -1; // Preattack skill. - Config.AttackSkill[1] = -1; // Primary skill to bosses. - Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. - Config.AttackSkill[3] = -1; // Primary skill to others. - Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. - Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. - Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. - - // Low mana skills - these will be used if main skills can't be cast. - Config.LowManaSkill[0] = -1; // Timed low mana skill. - Config.LowManaSkill[1] = -1; // Untimed low mana skill. + sdk.items.gems.Flawless.Emerald, sdk.items.gems.Flawless.Diamond, sdk.items.gems.Flawless.Skull + ]; + + // ############################ // + /* #### CHARACTER SETTINGS #### */ + // ############################ // + + // If Config.Leader is set, the bot will only accept invites from leader. + // If Config.PublicMode is not 0, Baal and Diablo script will open Town Portals. + // If set on true, it simply parties. + Config.PublicMode = 0; // 1 = invite and accept, 2 = accept only, 3 = invite only, 0 = disable. + + // General config + Config.AutoMap = false; // Set to true to open automap at the beginning of the game. + Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact + Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. + Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.LogExperience = false; // Print experience statistics in the manager. + + // Chicken settings + Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. + Config.ManaChicken = 0; // Exit game if mana is less or equal to designated percent. + Config.MercChicken = 0; // Exit game if merc's life is less or equal to designated percent. + Config.TownHP = 0; // Go to town if life is under designated percent. + Config.TownMP = 0; // Go to town if mana is under designated percent. + Config.PingQuit = [{ Ping: 0, Duration: 0 }]; // Quit if ping is over the given value for over the given time period in seconds. + + // Town settings + Config.HealHP = 50; // Go to a healer if under designated percent of life. + Config.HealMP = 0; // Go to a healer if under designated percent of mana. + Config.HealStatus = false; // Go to a healer if poisoned or cursed + Config.UseMerc = true; // Use merc. This is ignored and always false in d2classic. + Config.MercWatch = false; // Instant merc revive during battle. + Config.TownCheck = false; // Go to town if out of potions + Config.StashGold = 100000; // Minimum amount of gold to stash. + Config.MiniShopBot = true; // Scan items in NPC shops. + Config.PacketShopping = false; // Use packets to shop. Improves shopping speed. + Config.CubeRepair = false; // Repair weapons with Ort and armor with Ral rune. Don't use it if you don't understand the risk of losing items. + Config.RepairPercent = 40; // Durability percent of any equipped item that will trigger repairs. + + // Item identification settings + Config.CainID.Enable = false; // Identify items at Cain + Config.CainID.MinGold = 2500000; // Minimum gold (stash + character) to have in order to use Cain. + Config.CainID.MinUnids = 3; // Minimum number of unid items in order to use Cain. + Config.FieldID.Enabled = false; // Identify items while in the field + Config.FieldID.PacketID = true; // use packets to speed up id process (recommended to use this) + 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.DroppedItemsAnnounce.Enable = false; // Announce Dropped Items to in-game newbs + Config.DroppedItemsAnnounce.Quality = []; // Quality of item to announce. See core/GameData/NTItemAlias.js for values. Example: Config.DroppedItemsAnnounce.Quality = [6, 7, 8]; + + // Potion settings + Config.UseHP = 75; // Drink a healing potion if life is under designated percent. + Config.UseRejuvHP = 40; // Drink a rejuvenation potion if life is under designated percent. + Config.UseMP = 30; // Drink a mana potion if mana is under designated percent. + Config.UseRejuvMP = 0; // Drink a rejuvenation potion if mana is under designated percent. + Config.UseMercHP = 75; // Give a healing potion to your merc if his/her life is under designated percent. + Config.UseMercRejuv = 0; // Give a rejuvenation potion to your merc if his/her life is under designated percent. + Config.HPBuffer = 0; // Number of healing potions to keep in inventory. + Config.MPBuffer = 0; // Number of mana potions to keep in inventory. + Config.RejuvBuffer = 0; // Number of rejuvenation potions to keep in inventory. + + /** + * Potion types for belt columns from left to right. + * Rejuvenation potions must always be rightmost. + * Supported potions - Healing ("hp"), Mana ("mp") and Rejuvenation ("rv") + */ + Config.BeltColumn = ["hp", "hp", "mp", "rv"]; + + /** + * Minimum amount of potions from left to right. + * If we have less, go to vendor to purchase more. + * Set rejuvenation columns to 0, because they can't be bought. + */ + Config.MinColumn = [3, 3, 3, 0]; + + // ############################ // + /* #### INVENTORY SETTINGS #### */ + // ############################ // + /* + * Inventory lock configuration. !!!READ CAREFULLY!!! + * 0 = item is locked and won't be moved. If item occupies more than one slot, ALL of those slots must be set to 0 to lock it in place. + * Put 0s where your torch, annihilus and everything else you want to KEEP is. + * 1 = item is unlocked and will be dropped, stashed or sold. + * If you don't change the default values, the bot won't stash items. + */ + Config.Inventory[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[2] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Config.Inventory[3] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // ########################### // + /* ##### PICKIT SETTINGS ##### */ + // ########################### // + // Default folder is kolbot/pickit. + // Item name and classids located in core/GameData/NTItemAlias.js or modules/sdk.js + + // Config.PickitFiles.push("kolton.nip"); + // Config.PickitFiles.push("LLD.nip"); + Config.PickRange = 40; // Pick radius + Config.FastPick = false; // Check and pick items between attacks + Config.ManualPlayPick = false; // If set to true and D2BotMap entry script is used, will enable picking in manual play. + Config.OpenChests.Enabled = false; // Open chests. Controls key buying. + Config.OpenChests.Range = 15; // radius to scan for chests while pathing + Config.OpenChests.Types = ["chest", "chest3", "armorstand", "weaponrack"]; // which chests to open, use "all" to open all chests. See sdk/txt/chests.txt for full list of chest names + + // ########################### // + /* ##### PUBLIC SETTINGS ##### */ + // ########################### // + + // ##### CHAT SETTINGS ##### // + Config.Silence = false; // Make the bot not say a word. Do not use in combination with LocalChat or MFLeader or any team script + + // LocalChat messages will only be visible on clients running on the same PC + // Highly recommened for online play + // To allow 'say' to use BNET, use 'say("msg", true)', the 2nd parameter will force BNET + Config.LocalChat.Enabled = false; // use LocalChat system - sends chat locally instead of through BNET + Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 + Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + + // Anti-hostile config + Config.AntiHostile = false; // Enable anti-hostile + Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile + Config.TownOnHostile = false; // Go to town instead of quitting when HostileAction is 0 or 1 + Config.RandomPrecast = false; // Anti-PK measure, only supported in Baal and BaalHelper and BaalAssisstant at the moment. + Config.ViperCheck = false; // Quit if revived Tomb Vipers are sighted + + // Party message settings. Each setting represents an array of messages that will be randomly chosen. + // $name, $level, $class and $killer are replaced by the player's name, level, class and killer + Config.Greetings = []; // Example: ["Hello, $name (level $level $class)"] + Config.DeathMessages = []; // Example: ["Watch out for that $killer, $name!"] + Config.Congratulations = []; // Example: ["Congrats on level $level, $name!"] + Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. + Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. + Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + + // Shrine Scanner - scan for shrines while moving. + // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt + Config.ScanShrines = []; + + // DClone config + Config.StopOnDClone = true; // Go to town and idle as soon as Diablo walks the Earth + Config.SoJWaitTime = 5; // Time in minutes to wait for another SoJ sale before leaving game. 0 = disabled + Config.KillDclone = false; // Go to Palace Cellar 3 and try to kill Diablo Clone. Pointless if you already have Annihilus. + Config.DCloneQuit = false; // 1 = quit when Diablo walks, 2 = quit on soj sales, 0 = disabled + + // Monster skip config + // Skip immune monsters. Possible options: "fire", "cold", "lightning", "poison", "physical", "magic". + // You can combine multiple resists with "and", for example - "fire and cold", "physical and cold and poison" + Config.SkipImmune = []; + // Skip enchanted monsters. Possible options: "extra strong", "extra fast", "cursed", "magic resistant", "fire enchanted", "lightning enchanted", "cold enchanted", "mana burn", "teleportation", "spectral hit", "stone skin", "multiple shots". + // You can combine multiple enchantments with "and", for example - "cursed and extra fast", "mana burn and extra strong and lightning enchanted" + Config.SkipEnchant = []; + // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. + Config.SkipAura = []; + // always attempt to kill these bosses despite immunities and mods + Config.SkipException = []; + // vizier, de seis, infector + // Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; + + // ########################### // + /* ##### ATTACK SETTINGS ##### */ + // ########################### // + + /** + * Attack config + * To disable an attack, set it to -1 + * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt or use sdk.skills.SkillName see -> \kolbot\libs\modules\sdk.js + * DO NOT LEAVE THE NEGATIVE SIGN IN FRONT OF THE SKILLID. + * GOOD: Config.AttackSkill[1] = 151; + * GOOD: Config.AttackSkill[1] = sdk.skills.Whirlwind; + * BAD: Config.AttackSkill[1] = -151; + * BAD: Config.AttackSkill[1] = "Whirlwind"; + */ + // Wereform setup. Make sure you read Templates/Attacks.txt for attack skill format. + Config.Wereform = false; // 0 / false - don't shapeshift, 1 / "Werewolf" - change to werewolf, 2 / "Werebear" - change to werebear + + Config.AttackSkill[0] = -1; // Preattack skill. + Config.AttackSkill[1] = -1; // Primary skill to bosses. + Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. + Config.AttackSkill[3] = -1; // Primary skill to others. + Config.AttackSkill[4] = -1; // Primary untimed skill to others. Keep at -1 if Config.AttackSkill[3] is untimed skill. + Config.AttackSkill[5] = -1; // Secondary skill if monster is immune to primary. + Config.AttackSkill[6] = -1; // Secondary untimed skill if monster is immune to primary untimed. + + // Low mana skills - these will be used if main skills can't be cast. + Config.LowManaSkill[0] = -1; // Timed low mana skill. + Config.LowManaSkill[1] = -1; // Untimed low mana skill. Config.ChargeCast = { skill: sdk.skills.LowerResist, spectype: 0x7, }; - - Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas - */ - Config.CustomAttack = { - //"Monster Name": [-1, -1] - }; - - // Weapon slot settings - Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II - Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. - Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. - - Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars - Config.Dodge = false; // 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 = 100; // 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 - - // ############################ // - /* ###### CLEAR SETTINGS ###### */ - // ############################ // - - Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing - - // Clear while traveling during bot scripts - // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 - // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, - // all areas will be cleared using the specified range and spectype - Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - Config.ClearPath = { - Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas - Range: 30, // Range to clear while traveling - Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all - }; - - // ############################ // - /* ###### CLASS SETTINGS ###### */ - // ############################ // - - /* ### AMAZON ### */ - Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. - Config.UseInnerSight = true; // Use inner sight as a precast - Config.UseSlowMissiles = true; // Use slow missiles as a precast - Config.UseDecoy = true; // Use decoy with merc stomp - Config.SummonValkyrie = true; // Summon Valkyrie - - /* ### ASSASSIN ### */ - Config.UseTraps = true; // Set to true to use traps - Config.Traps = [271, 271, 271, 276, 276]; // Skill IDs for traps to be cast on all mosters except act bosses. - Config.BossTraps = [271, 271, 271, 271, 271]; // Skill IDs for traps to be cast on act bosses. - Config.SummonShadow = "Master"; // 0 = don't summon, 1 or "Warrior" = summon Shadow Warrior, 2 or "Master" = summon Shadow Master - Config.UseFade = true; // Set to true to use Fade prebuff. - Config.UseBoS = false; // Set to true to use Burst of Speed prebuff. TODO: Casting in town + UseFade compatibility - Config.UseVenom = false; // Set to true to use Venom prebuff. Set to false if you don't have the skill and have Arachnid Mesh - it will cause connection drop otherwise. - Config.UseBladeShield = false; // Set to true to use blade shield armor - Config.UseCloakofShadows = true; // Set to true to use Cloak of Shadows while fighting. Useful for blinding regular monsters/minions. - Config.AggressiveCloak = false; // Move into Cloak range or cast if already close - - /* ### BARBARIAN ### */ - Config.FindItem = false; // Use Find Item skill on corpses after clearing. - Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills - Config.UseWarcries = true; // use battle orders, battle command, and shout if we have them - - /* ### DRUID ### */ - Config.SummonRaven = false; - Config.SummonAnimal = "Grizzly"; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly - Config.SummonSpirit = "Oak Sage"; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" - Config.SummonVine = "Poison Creeper"; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" - - /* ### NECROMANCER ### */ - Config.Curse[0] = 0; // Boss curse. Use skill number or set to 0 to disable. - Config.Curse[1] = 0; // Other monsters curse. Use skill number or set to 0 to disable. - - /** - * Custom curses for monster - * Can use monster name or classid - * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; - * Optional 3rd parameter for spectype, leave blank to use on all - 0x00 Normal Monster - 0x01 Super Unique - 0x02 Champion - 0x04 Boss - 0x08 Minion - Example: Config.CustomCurse = [["HellBovine", 60], [571, 87], ["SkeletonArcher", 71, 0x00]]; - */ - Config.CustomCurse = []; - - Config.ExplodeCorpses = 0; // Explode corpses. Use skill number or 0 to disable. 74 = Corpse Explosion, 83 = Poison Explosion - Config.Golem = "None"; // Golem. 0 or "None" = don't summon, 1 or "Clay" = Clay Golem, 2 or "Blood" = Blood Golem, 3 or "Fire" = Fire Golem - Config.Skeletons = 0; // Number of skeletons to raise. Set to "max" to auto detect, set to 0 to disable. - Config.SkeletonMages = 0; // Number of skeleton mages to raise. Set to "max" to auto detect, set to 0 to disable. - Config.Revives = 0; // Number of revives to raise. Set to "max" to auto detect, set to 0 to disable. - Config.PoisonNovaDelay = 2; // Delay between two Poison Novas in seconds. - Config.ActiveSummon = false; // Raise dead between each attack. If false, it will raise after clearing a spot. - Config.ReviveUnstackable = true; // Revive monsters that can move freely after you teleport. - Config.IronGolemChicken = 30; // Exit game if Iron Golem's life is less or equal to designated percent. - - /* ### PALADIN ### */ - Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. - Config.Vigor = true; // Swith to Vigor when running - Config.Charge = true; // Use Charge when running - Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] - - /* ### SORCERESS ### */ - Config.CastStatic = 60; // Cast static until the target is at designated life percent. 100 = disabled. - Config.StaticList = []; // List of monster NAMES or CLASSIDS to static. Example: Config.StaticList = ["Andariel", 243]; - Config.UseTelekinesis = true; // Use telekinesis on units that allow it. Example: Shrines, Waypoints, Chests, and Portals - Config.UseEnergyShield = false; // set to true to use energy shield if its available - Config.UseColdArmor = true; // use armor skills, uses skill ids or set to true to let the bot decide based on skill level or false to disable completely - // (40 / sdk.skills.FrozenArmor)(50 / sdk.skills.ShiverArmor)(60 / sdk.skills.ChillingArmor) - - // ########################### // - /* ##### Gamble SETTINGS ##### */ - // ########################### // - Config.Gamble = false; - Config.GambleGoldStart = 1000000; - Config.GambleGoldStop = 500000; - - // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. - Config.GambleItems.push("Amulet"); - Config.GambleItems.push("Ring"); - Config.GambleItems.push("Circlet"); - Config.GambleItems.push("Coronet"); - - // ########################### // - /* ##### CUBING SETTINGS ##### */ - // ########################### // - /* - * All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js - * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). - * Etherealness is optional and only applies to some recipes. - */ - Config.Cubing = false; // Set to true to enable cubing. - Config.ShowCubingInfo = true; // Show cubing messages on console - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Gem, "Flawed Amethyst"]); // make Flawed Amethyst - // Config.Recipes.push([Recipe.Gem, "Flawed Topaz"]); // make Flawed Topaz - // Config.Recipes.push([Recipe.Gem, "Flawed Sapphire"]); // make Flawed Sapphire - // Config.Recipes.push([Recipe.Gem, "Flawed Emerald"]); // make Flawed Emerald - // Config.Recipes.push([Recipe.Gem, "Flawed Ruby"]); // make Flawed Ruby - // Config.Recipes.push([Recipe.Gem, "Flawed Diamond"]); // make Flawed Diamond - // Config.Recipes.push([Recipe.Gem, "Flawed Skull"]); // make Flawed Skull - - // Config.Recipes.push([Recipe.Gem, "Amethyst"]); // make Amethyst - // Config.Recipes.push([Recipe.Gem, "Topaz"]); // make Topaz - // Config.Recipes.push([Recipe.Gem, "Sapphire"]); // make Sapphire - // Config.Recipes.push([Recipe.Gem, "Emerald"]); // make Emerald - // Config.Recipes.push([Recipe.Gem, "Ruby"]); // make Ruby - // Config.Recipes.push([Recipe.Gem, "Diamond"]); // make Diamond - // Config.Recipes.push([Recipe.Gem, "Skull"]); // make Skull - - // Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // make Flawless Amethyst - // Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // make Flawless Topaz - // Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // make Flawless Sapphire - // Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // make Flawless Emerald - // Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // make Flawless Ruby - // Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // make Flawless Diamond - // Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // make Flawless Skull - - // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst - // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz - // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire - // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald - // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby - // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond - // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull - - // Config.Recipes.push([Recipe.Token]); // Make Token of Absolution - - // Config.Recipes.push([Recipe.Rejuv]); // Make Rejuv - // Config.Recipes.push([Recipe.FullRejuv]); // Make Full Rejuv - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Rune, "Eld Rune"]); // Upgrade El to Eld - // Config.Recipes.push([Recipe.Rune, "Tir Rune"]); // Upgrade Eld to Tir - // Config.Recipes.push([Recipe.Rune, "Nef Rune"]); // Upgrade Tir to Nef - // Config.Recipes.push([Recipe.Rune, "Eth Rune"]); // Upgrade Nef to Eth - // Config.Recipes.push([Recipe.Rune, "Ith Rune"]); // Upgrade Eth to Ith - // Config.Recipes.push([Recipe.Rune, "Tal Rune"]); // Upgrade Ith to Tal - // Config.Recipes.push([Recipe.Rune, "Ral Rune"]); // Upgrade Tal to Ral - // Config.Recipes.push([Recipe.Rune, "Ort Rune"]); // Upgrade Ral to Ort - - // Config.Recipes.push([Recipe.Rune, "Thul Rune"]); // Upgrade Ort to Thul - // Config.Recipes.push([Recipe.Rune, "Amn Rune"]); // Upgrade Thul to Amn - // Config.Recipes.push([Recipe.Rune, "Sol Rune"]); // Upgrade Amn to Sol - // Config.Recipes.push([Recipe.Rune, "Shael Rune"]); // Upgrade Sol to Shael - // Config.Recipes.push([Recipe.Rune, "Dol Rune"]); // Upgrade Shael to Dol - // Config.Recipes.push([Recipe.Rune, "Hel Rune"]); // Upgrade Dol to Hel - // Config.Recipes.push([Recipe.Rune, "Io Rune"]); // Upgrade Hel to Io - // Config.Recipes.push([Recipe.Rune, "Lum Rune"]); // Upgrade Io to Lum - // Config.Recipes.push([Recipe.Rune, "Ko Rune"]); // Upgrade Lum to Ko - // Config.Recipes.push([Recipe.Rune, "Fal Rune"]); // Upgrade Ko to Fal - // Config.Recipes.push([Recipe.Rune, "Lem Rune"]); // Upgrade Fal to Lem - - // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul - // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um - // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal - // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist - // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul - // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex - - // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js - - // Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Helm - // Config.Recipes.push([Recipe.Blood.Boots, "Mirrored Boots"]); // Craft Blood Boots - // Config.Recipes.push([Recipe.Blood.Gloves, "Vampirebone Gloves"]); // Craft Blood Gloves - // Config.Recipes.push([Recipe.Blood.Belt, "Mithril Coil"]); // Craft Blood Belt - // Config.Recipes.push([Recipe.Blood.Shield, "Blade Barrier"]); // Craft Blood Shield - // Config.Recipes.push([Recipe.Blood.Body, "Hellforge Plate"]); // Craft Blood Armor - // Config.Recipes.push([Recipe.Blood.Amulet]); // Craft Blood Amulet - // Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring - // Config.Recipes.push([Recipe.Blood.Weapon, "Berserker Axe"]); // Craft Blood Weapon - - // Config.Recipes.push([Recipe.Caster.Helm, "Demonhead Mask"]); // Craft Caster Helm - // Config.Recipes.push([Recipe.Caster.Boots, "Wyrmhide Boots"]); // Craft Caster Boots - // Config.Recipes.push([Recipe.Caster.Gloves, "Bramble Mitts"]); // Craft Caster Gloves - // Config.Recipes.push([Recipe.Caster.Belt, "Vampirefang Belt"]); // Craft Caster Belt - // Config.Recipes.push([Recipe.Caster.Shield, "Luna"]); // Craft Caster Shield - // Config.Recipes.push([Recipe.Caster.Body, "Archon Plate"]); // Craft Caster Armor - // Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet - // Config.Recipes.push([Recipe.Caster.Ring]); // Craft Caster Ring - // Config.Recipes.push([Recipe.Caster.Weapon, "Seraph Rod"]); // Craft Caster Weapon - - // Config.Recipes.push([Recipe.HitPower.Helm, "Giant Conch"]); // Craft Hit Power Helm - // Config.Recipes.push([Recipe.HitPower.Boots, "Boneweave Boots"]); // Craft Hit Power Boots - // Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Gloves - // Config.Recipes.push([Recipe.HitPower.Belt, "Troll Belt"]); // Craft Hit Power Belt - // Config.Recipes.push([Recipe.HitPower.Shield, "Ward"]); // Craft Hit Power Shield - // Config.Recipes.push([Recipe.HitPower.Body, "Kraken Shell"]); // Craft Hit Power Armor - // Config.Recipes.push([Recipe.HitPower.Amulet]); // Craft Hit Power Amulet - // Config.Recipes.push([Recipe.HitPower.Ring]); // Craft Hit Power Ring - // Config.Recipes.push([Recipe.HitPower.Weapon, "Scourge"]); // Craft Hit Power Weapon | "Blunt" = All maces, rods (+50% Undead), excepting orbs - - // Config.Recipes.push([Recipe.Safety.Helm, "Corona"]); // Craft Safety Helm - // Config.Recipes.push([Recipe.Safety.Boots, "Myrmidon Boots"]); // Craft Safety Boots - // Config.Recipes.push([Recipe.Safety.Gloves, "Ogre Gauntlets"]); // Craft Safety Gloves - // Config.Recipes.push([Recipe.Safety.Belt, "Spiderweb Sash"]); // Craft Safety Belt - // Config.Recipes.push([Recipe.Safety.Shield, "Monarch"]); // Craft Safety Shield - // Config.Recipes.push([Recipe.Safety.Body, "Great Hauberk"]); // Craft Safety Armor - // Config.Recipes.push([Recipe.Safety.Amulet]); // Craft Safety Amulet - // Config.Recipes.push([Recipe.Safety.Ring]); // Craft Safety Ring - // Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Javelin"]); // Craft Safety Weapon - // Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Spear"]); // Craft Safety Weapon - - // The gems not used by other recipes will be used for magic item rerolling. - - // Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem (ilvl 91+) - // Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) - // Config.Recipes.push([Recipe.Reroll.Charm.Small]); // Reroll magic Small Charm (ilvl 94+) - // Config.Recipes.push([Recipe.Reroll.Charm.Large]); // Reroll magic Large Charm (ilvl 76+) - // Config.Recipes.push([Recipe.Reroll.Charm.Grand]); // Reroll magic Grand Charm (ilvl 77+) - - // the cubing formula: 6 Perfect Skulls + 1 Rare Item = 1 random low quality rare item of the same type - // Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem - - // the cubing formula: 1 Perfect Skull + 1 Rare Item + Stone of Jordan = 1 high quality new rare item of the same type - // Config.Recipes.push([Recipe.Reroll.HighRare, "Diadem"]); // Reroll high rare Diadem - - /* - * Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. - * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. - */ - // Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher - // Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe - // Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor - // Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate - - // Config.Recipes.push([Recipe.Socket.Magic.LowWeapon, "Bone Wand"]); // Socket magic Bone Wand (ilvl < 30) - // Config.Recipes.push([Recipe.Socket.Magic.HighWeapon, "Swirling Crystal"]); // Socket magic Swirling Crystal (ilvl >= 30) - - // Config.Recipes.push([Recipe.Socket.Rare, "Diadem"]); // Socket rare Diadem - - // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional - // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional - // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite - // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite - // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite - - // ########################### // - /* #### RUNEWORD SETTINGS #### */ - // ########################### // - /* - * All recipes are available in Templates/Runewords.txt - * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them - */ - Config.MakeRunewords = true; // Set to true to enable runeword making/rerolling - - // Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher - // Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe - // Config.Runewords.push([Runeword.Insight, "Great Poleaxe"]); // Make Insight Great Poleaxe - // Config.Runewords.push([Runeword.Insight, "Giant Thresher"]); // Make Insight Giant Thresher - // Config.Runewords.push([Runeword.Insight, "Colossus Voulge"]); // Make Insight Colossus Voulge - // Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); // medium Insight - // Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17 && [enhanceddamage] >= 260 && [attackrate] >= 250"); // perfect Insight - - // Config.Runewords.push([Runeword.Grief, "Phase Blade"]); // Make Grief Phase Blade - // Config.Runewords.push([Runeword.Grief, "Berserker Axe"]); // Make Grief Berserker Axe - // Config.KeepRunewords.push("([type] == sword || [type] == axe) # [plusmaxdamage] >= 390"); // medium Grief - // Config.KeepRunewords.push("([type] == sword || [type] == axe) # [itemfasterattackrate] >= 40 && [plusmaxdamage] >= 400"); // perfect Grief and *optional [itempiercepois] >= 25 - - // Config.Runewords.push([Runeword.CallToArms, "Crystal Sword"]); // Make CTA Crystal Sword - // Config.Runewords.push([Runeword.CallToArms, "Phase Blade"]); // Make CTA Phase Blade - // Config.Runewords.push([Runeword.CallToArms, "Flail"]); // Make CTA Flail - // Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 3 && [plusskillbattleorders] >=3"); - // Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 6 && [plusskillbattleorders] >=6 && [plusskillbattlecry] >= 4"); // perfect CTA and *optional [enhanceddamage] = 290% - - // Config.Runewords.push([Runeword.Spirit, "Crystal Sword"]); // Make Spirit Crystal Sword - // Config.Runewords.push([Runeword.Spirit, "Broad Sword"]); // Make Spirit Broad Sword - // Config.Runewords.push([Runeword.Spirit, "Battle Sword"]); // Make Spirit Battle Sword - // Config.Runewords.push([Runeword.Spirit, "Phase Blade"]); // Make Spirit Phase Blade - // Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch - // Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe - // Config.Runewords.push([Runeword.Spirit, "Kurast Shield"]); // Make Spirit Kurast Shield - // Config.Runewords.push([Runeword.Spirit, "Vortex Shield"]); // Make Spirit Vortex Shield - // Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35"); // middle spirit - // Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35 && [maxmana] >= 112 && [itemabsorbmagic] >=8"); // perfect spirit - - // Config.Runewords.push([Runeword.Prudence, "Sacred Armor", Roll.Eth]); // Make ethereal Prudence Sacred Armor - // Config.KeepRunewords.push("[type] == Armor # [enhanceddefense] == 170 && [fireresist] == 35"); - - // #################################### // - /* #### ADVANCED AUTOMULE SETTINGS #### */ - // #################################### // - /* - * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. - * Force - Items listed here will be muled even if they are ingredients for cubing. - * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. - * - * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. - * Example : - * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. - * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. - * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. - */ - Config.AutoMule.Trigger = []; - Config.AutoMule.Force = []; - Config.AutoMule.Exclude = []; - - // ############################### // - /* #### ITEM LOGGING SETTINGS #### */ - // ############################### // - // Additional item info log settings. All info goes to \logs\ItemLog.txt - Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. - Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; - - // Manager Item Log Screen - Config.LogKeys = false; // Log keys on item viewer - Config.LogOrgans = true; // Log organs on item viewer - Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer - Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer - Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer - Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer - Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer - Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. - - // ######################################## // - /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ - // ######################################## // - /* - * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. - * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. - * - * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; - * skill - skill id number (see /sdk/txt/skills.txt) - * count - maximum number of skill points to allocate for that skill - * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. - * - * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. - */ - Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system - Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved - Config.AutoSkill.Build = []; - - /** - * AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. - * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. - * - * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; - * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 - * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). - * You can also set stat to string value "all", and it will spend all the remaining points. - * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). - * - * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. - */ - Config.AutoStat.Enabled = false; // Enable or disable AutoStat system - Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. - Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. - Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. - Config.AutoStat.Build = []; - - // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) - Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system - - // The name of the build associated with an existing - // template filename located in libs/config/Builds/ - Config.AutoBuild.Template = "BuildName"; - // Allows script to print messages in console - Config.AutoBuild.Verbose = true; - // Debug mode prints a little more information to console and - // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log - // It automatically enables Config.AutoBuild.Verbose - Config.AutoBuild.DebugMode = true; + + Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) + + /* Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas + */ + Config.CustomAttack = { + //"Monster Name": [-1, -1] + }; + + // Weapon slot settings + Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II + Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. + Config.TeleSwitch = false; // Switch to secondary (non-primary) slot when teleporting more than 5 nodes. + + Config.NoTele = false; // Restrict char from teleporting. Useful for low level/low mana chars + Config.Dodge = false; // 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 = 100; // 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 + + // ############################ // + /* ###### CLEAR SETTINGS ###### */ + // ############################ // + + Config.ClearType = 0xF; // Monster spectype to kill in level clear scripts (ie. Mausoleum). 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.BossPriority = false; // Set to true to attack Unique/SuperUnique monsters first when clearing + + // Clear while traveling during bot scripts + // You have two methods to configure clearing. First is simply a spectype to always clear, in any area, with a default range of 30 + // The second method allows you to specify the areas in which to clear while traveling, a range, and a spectype. If area is excluded from this method, + // all areas will be cleared using the specified range and spectype + Config.ClearPath = 0; // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + Config.ClearPath = { + Areas: [74], // Specific areas to clear while traveling in. Comment out to clear in all areas + Range: 30, // Range to clear while traveling + Spectype: 0, // Monster spectype to kill while traveling. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + }; + + // ############################ // + /* ###### CLASS SETTINGS ###### */ + // ############################ // + + /* ### AMAZON ### */ + Config.LightningFuryDelay = 10; // Lightning fury interval in seconds. LF is treated as timed skill. + Config.UseInnerSight = true; // Use inner sight as a precast + Config.UseSlowMissiles = true; // Use slow missiles as a precast + Config.UseDecoy = true; // Use decoy with merc stomp + Config.SummonValkyrie = true; // Summon Valkyrie + + /* ### ASSASSIN ### */ + Config.UseTraps = true; // Set to true to use traps + Config.Traps = [271, 271, 271, 276, 276]; // Skill IDs for traps to be cast on all mosters except act bosses. + Config.BossTraps = [271, 271, 271, 271, 271]; // Skill IDs for traps to be cast on act bosses. + Config.SummonShadow = "Master"; // 0 = don't summon, 1 or "Warrior" = summon Shadow Warrior, 2 or "Master" = summon Shadow Master + Config.UseFade = true; // Set to true to use Fade prebuff. + Config.UseBoS = false; // Set to true to use Burst of Speed prebuff. TODO: Casting in town + UseFade compatibility + Config.UseVenom = false; // Set to true to use Venom prebuff. Set to false if you don't have the skill and have Arachnid Mesh - it will cause connection drop otherwise. + Config.UseBladeShield = false; // Set to true to use blade shield armor + Config.UseCloakofShadows = true; // Set to true to use Cloak of Shadows while fighting. Useful for blinding regular monsters/minions. + Config.AggressiveCloak = false; // Move into Cloak range or cast if already close + + /* ### BARBARIAN ### */ + Config.FindItem = false; // Use Find Item skill on corpses after clearing. + Config.FindItemSwitch = false; // Switch to non-primary slot when using Find Item skills + Config.UseWarcries = true; // use battle orders, battle command, and shout if we have them + + /* ### DRUID ### */ + Config.SummonRaven = false; + Config.SummonAnimal = "Grizzly"; // 0 = disabled, 1 or "Spirit Wolf" = summon spirit wolf, 2 or "Dire Wolf" = summon dire wolf, 3 or "Grizzly" = summon grizzly + Config.SummonSpirit = "Oak Sage"; // 0 = disabled, 1 / "Oak Sage", 2 / "Heart of Wolverine", 3 / "Spirit of Barbs" + Config.SummonVine = "Poison Creeper"; // 0 = disabled, 1 / "Poison Creeper", 2 / "Carrion Vine", 3 / "Solar Creeper" + + /* ### NECROMANCER ### */ + Config.Curse[0] = 0; // Boss curse. Use skill number or set to 0 to disable. + Config.Curse[1] = 0; // Other monsters curse. Use skill number or set to 0 to disable. + + /** + * Custom curses for monster + * Can use monster name or classid + * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; + * Optional 3rd parameter for spectype, leave blank to use on all + 0x00 Normal Monster + 0x01 Super Unique + 0x02 Champion + 0x04 Boss + 0x08 Minion + Example: Config.CustomCurse = [["HellBovine", 60], [571, 87], ["SkeletonArcher", 71, 0x00]]; + */ + Config.CustomCurse = []; + + Config.ExplodeCorpses = 0; // Explode corpses. Use skill number or 0 to disable. 74 = Corpse Explosion, 83 = Poison Explosion + Config.Golem = "None"; // Golem. 0 or "None" = don't summon, 1 or "Clay" = Clay Golem, 2 or "Blood" = Blood Golem, 3 or "Fire" = Fire Golem + Config.Skeletons = 0; // Number of skeletons to raise. Set to "max" to auto detect, set to 0 to disable. + Config.SkeletonMages = 0; // Number of skeleton mages to raise. Set to "max" to auto detect, set to 0 to disable. + Config.Revives = 0; // Number of revives to raise. Set to "max" to auto detect, set to 0 to disable. + Config.PoisonNovaDelay = 2; // Delay between two Poison Novas in seconds. + Config.ActiveSummon = false; // Raise dead between each attack. If false, it will raise after clearing a spot. + Config.ReviveUnstackable = true; // Revive monsters that can move freely after you teleport. + Config.IronGolemChicken = 30; // Exit game if Iron Golem's life is less or equal to designated percent. + + /* ### PALADIN ### */ + Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. + Config.Vigor = true; // Swith to Vigor when running + Config.Charge = true; // Use Charge when running + Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] + + /* ### SORCERESS ### */ + Config.CastStatic = 60; // Cast static until the target is at designated life percent. 100 = disabled. + Config.StaticList = []; // List of monster NAMES or CLASSIDS to static. Example: Config.StaticList = ["Andariel", 243]; + Config.UseTelekinesis = true; // Use telekinesis on units that allow it. Example: Shrines, Waypoints, Chests, and Portals + Config.UseEnergyShield = false; // set to true to use energy shield if its available + Config.UseColdArmor = true; // use armor skills, uses skill ids or set to true to let the bot decide based on skill level or false to disable completely + // (40 / sdk.skills.FrozenArmor)(50 / sdk.skills.ShiverArmor)(60 / sdk.skills.ChillingArmor) + + // ########################### // + /* ##### Gamble SETTINGS ##### */ + // ########################### // + Config.Gamble = false; + Config.GambleGoldStart = 1000000; + Config.GambleGoldStop = 500000; + + // List of item names or classids for gambling. Check libs/core/GameData/NTItemAlias.js file for other item classids. + Config.GambleItems.push("Amulet"); + Config.GambleItems.push("Ring"); + Config.GambleItems.push("Circlet"); + Config.GambleItems.push("Coronet"); + + // ########################### // + /* ##### CUBING SETTINGS ##### */ + // ########################### // + /* + * All recipe names are available in Templates/Cubing.txt. For item names/classids check core/GameData/NTItemAlias.js + * The format is Config.Recipes.push([recipe_name, item_name_or_classid, etherealness]). + * Etherealness is optional and only applies to some recipes. + */ + Config.Cubing = false; // Set to true to enable cubing. + Config.ShowCubingInfo = true; // Show cubing messages on console + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Gem, "Flawed Amethyst"]); // make Flawed Amethyst + // Config.Recipes.push([Recipe.Gem, "Flawed Topaz"]); // make Flawed Topaz + // Config.Recipes.push([Recipe.Gem, "Flawed Sapphire"]); // make Flawed Sapphire + // Config.Recipes.push([Recipe.Gem, "Flawed Emerald"]); // make Flawed Emerald + // Config.Recipes.push([Recipe.Gem, "Flawed Ruby"]); // make Flawed Ruby + // Config.Recipes.push([Recipe.Gem, "Flawed Diamond"]); // make Flawed Diamond + // Config.Recipes.push([Recipe.Gem, "Flawed Skull"]); // make Flawed Skull + + // Config.Recipes.push([Recipe.Gem, "Amethyst"]); // make Amethyst + // Config.Recipes.push([Recipe.Gem, "Topaz"]); // make Topaz + // Config.Recipes.push([Recipe.Gem, "Sapphire"]); // make Sapphire + // Config.Recipes.push([Recipe.Gem, "Emerald"]); // make Emerald + // Config.Recipes.push([Recipe.Gem, "Ruby"]); // make Ruby + // Config.Recipes.push([Recipe.Gem, "Diamond"]); // make Diamond + // Config.Recipes.push([Recipe.Gem, "Skull"]); // make Skull + + // Config.Recipes.push([Recipe.Gem, "Flawless Amethyst"]); // make Flawless Amethyst + // Config.Recipes.push([Recipe.Gem, "Flawless Topaz"]); // make Flawless Topaz + // Config.Recipes.push([Recipe.Gem, "Flawless Sapphire"]); // make Flawless Sapphire + // Config.Recipes.push([Recipe.Gem, "Flawless Emerald"]); // make Flawless Emerald + // Config.Recipes.push([Recipe.Gem, "Flawless Ruby"]); // make Flawless Ruby + // Config.Recipes.push([Recipe.Gem, "Flawless Diamond"]); // make Flawless Diamond + // Config.Recipes.push([Recipe.Gem, "Flawless Skull"]); // make Flawless Skull + + // Config.Recipes.push([Recipe.Gem, "Perfect Amethyst"]); // Make Perfect Amethyst + // Config.Recipes.push([Recipe.Gem, "Perfect Topaz"]); // Make Perfect Topaz + // Config.Recipes.push([Recipe.Gem, "Perfect Sapphire"]); // Make Perfect Sapphire + // Config.Recipes.push([Recipe.Gem, "Perfect Emerald"]); // Make Perfect Emerald + // Config.Recipes.push([Recipe.Gem, "Perfect Ruby"]); // Make Perfect Ruby + // Config.Recipes.push([Recipe.Gem, "Perfect Diamond"]); // Make Perfect Diamond + // Config.Recipes.push([Recipe.Gem, "Perfect Skull"]); // Make Perfect Skull + + // Config.Recipes.push([Recipe.Token]); // Make Token of Absolution + + // Config.Recipes.push([Recipe.Rejuv]); // Make Rejuv + // Config.Recipes.push([Recipe.FullRejuv]); // Make Full Rejuv + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Rune, "Eld Rune"]); // Upgrade El to Eld + // Config.Recipes.push([Recipe.Rune, "Tir Rune"]); // Upgrade Eld to Tir + // Config.Recipes.push([Recipe.Rune, "Nef Rune"]); // Upgrade Tir to Nef + // Config.Recipes.push([Recipe.Rune, "Eth Rune"]); // Upgrade Nef to Eth + // Config.Recipes.push([Recipe.Rune, "Ith Rune"]); // Upgrade Eth to Ith + // Config.Recipes.push([Recipe.Rune, "Tal Rune"]); // Upgrade Ith to Tal + // Config.Recipes.push([Recipe.Rune, "Ral Rune"]); // Upgrade Tal to Ral + // Config.Recipes.push([Recipe.Rune, "Ort Rune"]); // Upgrade Ral to Ort + + // Config.Recipes.push([Recipe.Rune, "Thul Rune"]); // Upgrade Ort to Thul + // Config.Recipes.push([Recipe.Rune, "Amn Rune"]); // Upgrade Thul to Amn + // Config.Recipes.push([Recipe.Rune, "Sol Rune"]); // Upgrade Amn to Sol + // Config.Recipes.push([Recipe.Rune, "Shael Rune"]); // Upgrade Sol to Shael + // Config.Recipes.push([Recipe.Rune, "Dol Rune"]); // Upgrade Shael to Dol + // Config.Recipes.push([Recipe.Rune, "Hel Rune"]); // Upgrade Dol to Hel + // Config.Recipes.push([Recipe.Rune, "Io Rune"]); // Upgrade Hel to Io + // Config.Recipes.push([Recipe.Rune, "Lum Rune"]); // Upgrade Io to Lum + // Config.Recipes.push([Recipe.Rune, "Ko Rune"]); // Upgrade Lum to Ko + // Config.Recipes.push([Recipe.Rune, "Fal Rune"]); // Upgrade Ko to Fal + // Config.Recipes.push([Recipe.Rune, "Lem Rune"]); // Upgrade Fal to Lem + + // Config.Recipes.push([Recipe.Rune, "Pul Rune"]); // Upgrade Lem to Pul + // Config.Recipes.push([Recipe.Rune, "Um Rune"]); // Upgrade Pul to Um + // Config.Recipes.push([Recipe.Rune, "Mal Rune"]); // Upgrade Um to Mal + // Config.Recipes.push([Recipe.Rune, "Ist Rune"]); // Upgrade Mal to Ist + // Config.Recipes.push([Recipe.Rune, "Gul Rune"]); // Upgrade Ist to Gul + // Config.Recipes.push([Recipe.Rune, "Vex Rune"]); // Upgrade Gul to Vex + + // Ingredients for the following recipes will be auto-picked, for classids check libs/core/GameData/NTItemAlias.js + + // Config.Recipes.push([Recipe.Blood.Helm, "Armet"]); // Craft Blood Helm + // Config.Recipes.push([Recipe.Blood.Boots, "Mirrored Boots"]); // Craft Blood Boots + // Config.Recipes.push([Recipe.Blood.Gloves, "Vampirebone Gloves"]); // Craft Blood Gloves + // Config.Recipes.push([Recipe.Blood.Belt, "Mithril Coil"]); // Craft Blood Belt + // Config.Recipes.push([Recipe.Blood.Shield, "Blade Barrier"]); // Craft Blood Shield + // Config.Recipes.push([Recipe.Blood.Body, "Hellforge Plate"]); // Craft Blood Armor + // Config.Recipes.push([Recipe.Blood.Amulet]); // Craft Blood Amulet + // Config.Recipes.push([Recipe.Blood.Ring]); // Craft Blood Ring + // Config.Recipes.push([Recipe.Blood.Weapon, "Berserker Axe"]); // Craft Blood Weapon + + // Config.Recipes.push([Recipe.Caster.Helm, "Demonhead Mask"]); // Craft Caster Helm + // Config.Recipes.push([Recipe.Caster.Boots, "Wyrmhide Boots"]); // Craft Caster Boots + // Config.Recipes.push([Recipe.Caster.Gloves, "Bramble Mitts"]); // Craft Caster Gloves + // Config.Recipes.push([Recipe.Caster.Belt, "Vampirefang Belt"]); // Craft Caster Belt + // Config.Recipes.push([Recipe.Caster.Shield, "Luna"]); // Craft Caster Shield + // Config.Recipes.push([Recipe.Caster.Body, "Archon Plate"]); // Craft Caster Armor + // Config.Recipes.push([Recipe.Caster.Amulet]); // Craft Caster Amulet + // Config.Recipes.push([Recipe.Caster.Ring]); // Craft Caster Ring + // Config.Recipes.push([Recipe.Caster.Weapon, "Seraph Rod"]); // Craft Caster Weapon + + // Config.Recipes.push([Recipe.HitPower.Helm, "Giant Conch"]); // Craft Hit Power Helm + // Config.Recipes.push([Recipe.HitPower.Boots, "Boneweave Boots"]); // Craft Hit Power Boots + // Config.Recipes.push([Recipe.HitPower.Gloves, "Vambraces"]); // Craft Hit Power Gloves + // Config.Recipes.push([Recipe.HitPower.Belt, "Troll Belt"]); // Craft Hit Power Belt + // Config.Recipes.push([Recipe.HitPower.Shield, "Ward"]); // Craft Hit Power Shield + // Config.Recipes.push([Recipe.HitPower.Body, "Kraken Shell"]); // Craft Hit Power Armor + // Config.Recipes.push([Recipe.HitPower.Amulet]); // Craft Hit Power Amulet + // Config.Recipes.push([Recipe.HitPower.Ring]); // Craft Hit Power Ring + // Config.Recipes.push([Recipe.HitPower.Weapon, "Scourge"]); // Craft Hit Power Weapon | "Blunt" = All maces, rods (+50% Undead), excepting orbs + + // Config.Recipes.push([Recipe.Safety.Helm, "Corona"]); // Craft Safety Helm + // Config.Recipes.push([Recipe.Safety.Boots, "Myrmidon Boots"]); // Craft Safety Boots + // Config.Recipes.push([Recipe.Safety.Gloves, "Ogre Gauntlets"]); // Craft Safety Gloves + // Config.Recipes.push([Recipe.Safety.Belt, "Spiderweb Sash"]); // Craft Safety Belt + // Config.Recipes.push([Recipe.Safety.Shield, "Monarch"]); // Craft Safety Shield + // Config.Recipes.push([Recipe.Safety.Body, "Great Hauberk"]); // Craft Safety Armor + // Config.Recipes.push([Recipe.Safety.Amulet]); // Craft Safety Amulet + // Config.Recipes.push([Recipe.Safety.Ring]); // Craft Safety Ring + // Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Javelin"]); // Craft Safety Weapon + // Config.Recipes.push([Recipe.Safety.Weapon, "Matriarchal Spear"]); // Craft Safety Weapon + + // The gems not used by other recipes will be used for magic item rerolling. + + // Config.Recipes.push([Recipe.Reroll.Magic, "Diadem"]); // Reroll magic Diadem (ilvl 91+) + // Config.Recipes.push([Recipe.Reroll.Magic, "Grand Charm"]); // Reroll magic Grand Charm (ilvl 91+) + // Config.Recipes.push([Recipe.Reroll.Charm.Small]); // Reroll magic Small Charm (ilvl 94+) + // Config.Recipes.push([Recipe.Reroll.Charm.Large]); // Reroll magic Large Charm (ilvl 76+) + // Config.Recipes.push([Recipe.Reroll.Charm.Grand]); // Reroll magic Grand Charm (ilvl 77+) + + // the cubing formula: 6 Perfect Skulls + 1 Rare Item = 1 random low quality rare item of the same type + // Config.Recipes.push([Recipe.Reroll.Rare, "Diadem"]); // Reroll rare Diadem + + // the cubing formula: 1 Perfect Skull + 1 Rare Item + Stone of Jordan = 1 high quality new rare item of the same type + // Config.Recipes.push([Recipe.Reroll.HighRare, "Diadem"]); // Reroll high rare Diadem + + /* + * Base item for the following recipes must be in pickit. The rest of the ingredients will be auto-picked. + * Use Roll.Eth, Roll.NonEth or Roll.All to determine what kind of base item to roll - ethereal, non-ethereal or all. + */ + // Config.Recipes.push([Recipe.Socket.Weapon, "Thresher", Roll.Eth]); // Socket ethereal Thresher + // Config.Recipes.push([Recipe.Socket.Weapon, "Cryptic Axe", Roll.Eth]); // Socket ethereal Cryptic Axe + // Config.Recipes.push([Recipe.Socket.Armor, "Sacred Armor", Roll.Eth]); // Socket ethereal Sacred Armor + // Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", Roll.Eth]); // Socket ethereal Archon Plate + + // Config.Recipes.push([Recipe.Socket.Magic.LowWeapon, "Bone Wand"]); // Socket magic Bone Wand (ilvl < 30) + // Config.Recipes.push([Recipe.Socket.Magic.HighWeapon, "Swirling Crystal"]); // Socket magic Swirling Crystal (ilvl >= 30) + + // Config.Recipes.push([Recipe.Socket.Rare, "Diadem"]); // Socket rare Diadem + + // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Heavy Gloves", Roll.NonEth]); // Upgrade Bloodfist to Exceptional + // Config.Recipes.push([Recipe.Unique.Armor.ToExceptional, "Light Gauntlets", Roll.NonEth]); // Upgrade Magefist to Exceptional + // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Sharkskin Gloves", Roll.NonEth]); // Upgrade Bloodfist or Grave Palm to Elite + // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "Battle Gauntlets", Roll.NonEth]); // Upgrade Magefist or Lavagout to Elite + // Config.Recipes.push([Recipe.Unique.Armor.ToElite, "War Boots", Roll.NonEth]); // Upgrade Gore Rider to Elite + + // ########################### // + /* #### RUNEWORD SETTINGS #### */ + // ########################### // + /* + * All recipes are available in Templates/Runewords.txt + * Keep lines follow pickit format and any given runeword is tested vs ALL lines so you don't need to repeat them + */ + Config.MakeRunewords = true; // Set to true to enable runeword making/rerolling + + // Config.Runewords.push([Runeword.Insight, "Thresher", Roll.Eth]); // Make ethereal Insight Thresher + // Config.Runewords.push([Runeword.Insight, "Cryptic Axe", Roll.Eth]); // Make ethereal Insight Cryptic Axe + // Config.Runewords.push([Runeword.Insight, "Great Poleaxe"]); // Make Insight Great Poleaxe + // Config.Runewords.push([Runeword.Insight, "Giant Thresher"]); // Make Insight Giant Thresher + // Config.Runewords.push([Runeword.Insight, "Colossus Voulge"]); // Make Insight Colossus Voulge + // Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17"); // medium Insight + // Config.KeepRunewords.push("[type] == polearm # [meditationaura] == 17 && [enhanceddamage] >= 260 && [attackrate] >= 250"); // perfect Insight + + // Config.Runewords.push([Runeword.Grief, "Phase Blade"]); // Make Grief Phase Blade + // Config.Runewords.push([Runeword.Grief, "Berserker Axe"]); // Make Grief Berserker Axe + // Config.KeepRunewords.push("([type] == sword || [type] == axe) # [plusmaxdamage] >= 390"); // medium Grief + // Config.KeepRunewords.push("([type] == sword || [type] == axe) # [itemfasterattackrate] >= 40 && [plusmaxdamage] >= 400"); // perfect Grief and *optional [itempiercepois] >= 25 + + // Config.Runewords.push([Runeword.CallToArms, "Crystal Sword"]); // Make CTA Crystal Sword + // Config.Runewords.push([Runeword.CallToArms, "Phase Blade"]); // Make CTA Phase Blade + // Config.Runewords.push([Runeword.CallToArms, "Flail"]); // Make CTA Flail + // Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 3 && [plusskillbattleorders] >=3"); + // Config.KeepRunewords.push("[name] == crystalsword || [name] == phaseblade || [name] == flail # [plusskillbattlecommand] >= 6 && [plusskillbattleorders] >=6 && [plusskillbattlecry] >= 4"); // perfect CTA and *optional [enhanceddamage] = 290% + + // Config.Runewords.push([Runeword.Spirit, "Crystal Sword"]); // Make Spirit Crystal Sword + // Config.Runewords.push([Runeword.Spirit, "Broad Sword"]); // Make Spirit Broad Sword + // Config.Runewords.push([Runeword.Spirit, "Battle Sword"]); // Make Spirit Battle Sword + // Config.Runewords.push([Runeword.Spirit, "Phase Blade"]); // Make Spirit Phase Blade + // Config.Runewords.push([Runeword.Spirit, "Monarch", Roll.NonEth]); // Make Spirit Monarch + // Config.Runewords.push([Runeword.Spirit, "Sacred Targe", Roll.NonEth]); // Make Spirit Sacred Targe + // Config.Runewords.push([Runeword.Spirit, "Kurast Shield"]); // Make Spirit Kurast Shield + // Config.Runewords.push([Runeword.Spirit, "Vortex Shield"]); // Make Spirit Vortex Shield + // Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35"); // middle spirit + // Config.KeepRunewords.push("[type] == sword || [type] == shield || [type] == auricshields # [fcr] == 35 && [maxmana] >= 112 && [itemabsorbmagic] >=8"); // perfect spirit + + // Config.Runewords.push([Runeword.Prudence, "Sacred Armor", Roll.Eth]); // Make ethereal Prudence Sacred Armor + // Config.KeepRunewords.push("[type] == Armor # [enhanceddefense] == 170 && [fireresist] == 35"); + + // #################################### // + /* #### ADVANCED AUTOMULE SETTINGS #### */ + // #################################### // + /* + * Trigger - Having an item that is on the list will initiate muling. Useful if you want to mule something immediately upon finding. + * Force - Items listed here will be muled even if they are ingredients for cubing. + * Exclude - Items listed here will be ignored and will not be muled. Items on Trigger or Force lists are prioritized over this list. + * + * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. + * Example : + * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; + * This will initiate muling when your character finds Ber, Jah, or SOJ. + * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; + * This will mule perfect gems/skull during muling. + * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; + * This will exclude muling of runes from tal through sol, and any essences. + */ + Config.AutoMule.Trigger = []; + Config.AutoMule.Force = []; + Config.AutoMule.Exclude = []; + + // ############################### // + /* #### ITEM LOGGING SETTINGS #### */ + // ############################### // + // Additional item info log settings. All info goes to \logs\ItemLog.txt + Config.ItemInfo = false; // Log stashed, skipped (due to no space) or sold items. + Config.ItemInfoQuality = []; // The quality of sold items to log. See core/GameData/NTItemAlias.js for values. Example: Config.ItemInfoQuality = [6, 7, 8]; + + // Manager Item Log Screen + Config.LogKeys = false; // Log keys on item viewer + Config.LogOrgans = true; // Log organs on item viewer + Config.LogLowRunes = false; // Log low runes (El - Dol) on item viewer + Config.LogMiddleRunes = false; // Log middle runes (Hel - Mal) on item viewer + Config.LogHighRunes = true; // Log high runes (Ist - Zod) on item viewer + Config.LogLowGems = false; // Log low gems (chipped, flawed, normal) on item viewer + Config.LogHighGems = false; // Log high gems (flawless, perfect) on item viewer + Config.SkipLogging = []; // Custom log skip list. Set as three digit item code or classid. Example: ["tes", "ceh", 656, 657] will ignore logging of essences. + + // ######################################## // + /* #### AUTO BUILD/SKILL/STAT SETTINGS #### */ + // ######################################## // + /* + * AutoSkill builds character based on array defined by the user and it replaces AutoBuild's skill system. + * AutoSkill will automatically spend skill points and it can also allocate any prerequisite skills as required. + * + * Format: Config.AutoSkill.Build = [[skillID, count, satisfy], [skillID, count, satisfy], ... [skillID, count, satisfy]]; + * skill - skill id number (see /sdk/txt/skills.txt) + * count - maximum number of skill points to allocate for that skill + * satisfy - boolean value to stop(true) or continue(false) further allocation until count is met. Defaults to true if not specified. + * + * See libs/config/Templates/AutoSkillExampleBuilds.txt for Config.AutoSkill.Build examples. + */ + Config.AutoSkill.Enabled = false; // Enable or disable AutoSkill system + Config.AutoSkill.Save = 0; // Number of skill points that will not be spent and saved + Config.AutoSkill.Build = []; + + /** + * AutoStat builds character based on array defined by the user and this will replace AutoBuild's stat system. + * AutoStat will stat Build array order. You may want to stat strength or dexterity first to meet item requirements. + * + * Format: Config.AutoStat.Build = [[statType, stat], [statType, stat], ... [statType, stat]]; + * statType - defined as string, or as corresponding stat integer. "strength" or 0, "dexterity" or 2, "vitality" or 3, "energy" or 1 + * stat - set to an integer value, and it will spend stat points until it reaches desired *hard stat value (*+stats from items are ignored). + * You can also set stat to string value "all", and it will spend all the remaining points. + * Dexterity can be set to "block" and it will stat dexterity up the the desired block value specified in arguemnt (ignored in classic). + * + * See libs/config/Templates/AutoStatExampleBuilds.txt for Config.AutoStat.Build examples. + */ + Config.AutoStat.Enabled = false; // Enable or disable AutoStat system + Config.AutoStat.Save = 0; // Number stat points that will not be spent and saved. + Config.AutoStat.BlockChance = 0; // An integer value set to desired block chance. This is ignored in classic. + Config.AutoStat.UseBulk = true; // Set true to spend multiple stat points at once (up to 100), or false to spend singe point at a time. + Config.AutoStat.Build = []; + + // AutoBuild System ( See /d2bs/kolbot/libs/config/Builds/README.txt for instructions ) + Config.AutoBuild.Enabled = false; // This will enable or disable the AutoBuild system + + // The name of the build associated with an existing + // template filename located in libs/config/Builds/ + Config.AutoBuild.Template = "BuildName"; + // Allows script to print messages in console + Config.AutoBuild.Verbose = true; + // Debug mode prints a little more information to console and + // logs activity to /logs/AutoBuild.CharacterName._MM_DD_YYYY.log + // It automatically enables Config.AutoBuild.Verbose + Config.AutoBuild.DebugMode = true; diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 63e5ca6a7..0f09095b4 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -578,6 +578,27 @@ let Config = { GiveWps: false, SecurePortal: false, }, + Rush: { + Andy: false, + Cube: false, + Radament: false, + Amulet: false, + Staff: false, + Summoner: false, + Duriel: false, + LamEsen: false, + Eye: false, + Heart: false, + Brain: false, + Travincal: false, + Mephisto: false, + Izual: false, + Diablo: false, + Shenk: false, + Anya: false, + Ancients: false, + Baal: false, + }, EndMessage: "", GameLength: 20 }, diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 951d1bf6d..c128bfca8 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1,14 +1,99 @@ /** * @filename ControlBot.js * @author theBGuy -* @credits kolton +* @credits kolton (for the original Enchant.js), +* magace (for the inspiration to add rush commands) * @desc Chat controlled bot for other players. Can open cow portal, give waypoints on command, bo, or enchant * */ function ControlBot () { + // Quests + const { + andariel, + cube, + radament, + amulet, + staff, + summoner, + duriel, + lamesen, + brain, + heart, + eye, + travincal, + mephisto, + izual, + diablo, + shenk, + anya, + ancients, + baal, + } = require("../systems/autorush/AutoRush"); + const { + AutoRush, + RushModes, + } = require("../systems/autorush/RushConfig"); + const Worker = require("../modules/Worker"); + + AutoRush.rushMode = RushModes.chanter; + AutoRush.playersIn = "in"; + AutoRush.playersOut = "out"; + AutoRush.allIn = "all in"; + + const MAX_CHAT_LENGTH = 180; const startTime = getTickCount(); + const maxTime = Time.minutes(Config.ControlBot.GameLength); const chantDuration = Skill.getDuration(sdk.skills.Enchant); + /** @type {Map} */ + const players = new Map(); + + const Chat = { + /** @type {string[]} */ + queue: [], + + /** + * Send a message in chat + * @param {string} msg + */ + say: function (msg) { + Chat.queue.push(msg); + }, + + /** + * Whisper a chat to a user + * @param {string} nick + * @param {string} msg + */ + whisper: function (nick, msg) { + if (!players.has(nick) && !Misc.findPlayer(nick)) { + console.debug("Player not found: " + nick); + return; + } + let who = players.get(nick) || nick; + Chat.queue.push("/w " + who + " " + msg); + }, + }; + + Worker.runInBackground.chat = (function () { + let tick = getTickCount(); + + return function () { + if (!Chat.queue.length) return true; + // should check if next msg is going to be a whisper and if so + // check if the player is in the game and if not, don't send the whisper + if (getTickCount() - tick < 0) return true; + // allow say messages every ~1.5 seconds + tick = getTickCount() + Time.seconds(1) + rand(250, 750); + console.debug("(" + Chat.queue[0] + ")"); + if (Chat.queue[0].length > MAX_CHAT_LENGTH) { + console.debug("Message too long, splitting."); + Chat.queue[0] = Chat.queue[0].substring(0, MAX_CHAT_LENGTH); + } + say(Chat.queue.shift()); + return true; + }; + })(); /** @constructor */ function PlayerTracker () { @@ -40,10 +125,25 @@ function ControlBot () { this.lastChant = getTickCount(); }; - /** @type {Object.} */ - const cmdNicks = {}; - /** @type {Object.} */ - const wpNicks = {}; + /** @constructor */ + function WpTracker () { + this.timer = getTickCount(); + this.requests = 0; + } + + WpTracker.prototype.update = function () { + this.timer = getTickCount(); + this.requests++; + }; + + WpTracker.prototype.timeSinceLastRequest = function () { + return getTickCount() - this.timer; + }; + + /** @type {Map} */ + const cmdNicks = new Map(); + /** @type {Map} */ + const wpNicks = new Map(); /** @type {Set} */ const shitList = new Set(); /** @type {Array} */ @@ -86,7 +186,12 @@ function ControlBot () { ] ]); - let command, nick; + /** @type {[string, string][]} */ + const queue = []; + const running = { + nick: "", + command: "", + }; /** * @param {string} nick @@ -95,23 +200,23 @@ function ControlBot () { const enchant = function (nick) { try { if (!Misc.inMyParty(nick)) { - throw new Error("Accept party invite, noob."); + throw new ScriptError("Accept party invite, noob."); } let unit = Game.getPlayer(nick); if (unit && unit.distance > 35) { - throw new Error("Get closer."); + throw new ScriptError("Get closer."); } if (!unit) { let partyUnit = getParty(nick); if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { - throw new Error("You need to be in one of the towns."); + throw new ScriptError("You need to be in one of the towns."); } // wait until party area is readable? - say("Wait for me at waypoint."); + Chat.say("Wait for me at waypoint."); Town.goToTown(sdk.areas.actOf(partyUnit.area)); unit = Game.getPlayer(nick); @@ -122,7 +227,7 @@ function ControlBot () { // player is alive if (!unit.dead) { if (unit.distance >= 35) { - throw new Error("You went too far away."); + throw new ScriptError("You went too far away."); } Packet.enchant(unit); if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { @@ -133,7 +238,7 @@ function ControlBot () { } } while (unit.getNext()); } else { - say("I don't see you"); + Chat.say("I don't see you"); } unit = Game.getMonster(); @@ -150,7 +255,12 @@ function ControlBot () { return true; } catch (e) { - say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } return false; } @@ -165,24 +275,36 @@ function ControlBot () { try { if (!Misc.inMyParty(nick)) { - throw new Error("Accept party invite, noob."); + throw new ScriptError("Accept party invite, noob."); } let partyUnit = getParty(nick); // wait until party area is readable? if (!Misc.poll(() => Pather.wpAreas.includes(partyUnit.area), 500, 50)) { - throw new Error("Can't find you or you're not somewhere with a waypoint"); + throw new ScriptError("Can't find you or you're not somewhere with a waypoint"); + } + if (partyUnit.inTown) { + let a1Wp = Object.values(sdk.areas) + .filter(function (area) { + if (area < sdk.areas.ColdPlains || area > sdk.areas.CatacombsLvl2) return false; + return Pather.wpAreas.includes(area) && me.haveWaypoint(area); + }).random(); + Chat.whisper(nick, "Go to act 1 waypoint " + getAreaName(a1Wp) + " and wait for me."); + Pather.useWaypoint(a1Wp); + } else { + Pather.useWaypoint(partyUnit.area); } - Pather.useWaypoint(partyUnit.area); - let unit = Game.getPlayer(nick); + let unit = Misc.poll(function () { + return Game.getPlayer(nick); + }, Time.minutes(1), 1000); if (unit && unit.distance > 15) { - say("Get closer."); + Chat.say("Get closer."); if (!Misc.poll(() => unit.distance <= 15, Time.seconds(30), 50)) { - throw new Error("You took to long. Going back to town"); + throw new ScriptError("You took to long. Going back to town"); } } @@ -193,12 +315,17 @@ function ControlBot () { }, 5000, 1000); Pather.useWaypoint(sdk.areas.RogueEncampment); } else { - throw new Error("I don't see you"); + throw new ScriptError("I don't see you"); } return true; } catch (e) { - say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } return false; } @@ -272,13 +399,13 @@ function ControlBot () { } while (leg.getNext()); } - say("Bring the leg " + (wrongLeg ? "from this difficulty" : "") + " close to me."); + Chat.say("Bring the leg " + (wrongLeg ? "from this difficulty" : "") + " close to me."); return false; } if (!Pather.journeyTo(sdk.areas.Tristram)) { - say("Failed to enter Tristram :("); + Chat.say("Failed to enter Tristram :("); Town.goToTown(); return false; @@ -305,7 +432,7 @@ function ControlBot () { } Town.goToTown(); - say("Failed to get the leg :("); + Chat.say("Failed to get the leg :("); return false; }; @@ -352,16 +479,21 @@ function ControlBot () { const openPortal = function (nick) { if (!Config.ControlBot.Cows.MakeCows) return false; try { - if (!Misc.inMyParty(nick)) throw new Error("Accept party invite, noob."); - if (Pather.getPortal(sdk.areas.MooMooFarm)) throw new Error("Cow portal already open."); + if (!Misc.inMyParty(nick)) throw new ScriptError("Accept party invite, noob."); + if (Pather.getPortal(sdk.areas.MooMooFarm)) throw new ScriptError("Cow portal already open."); // king dead or cain not saved - if (me.cows) throw new Error("Can't open the portal because I killed Cow King."); + if (me.cows) throw new ScriptError("Can't open the portal because I killed Cow King."); if (Config.ControlBot.Cows.GetLeg && !me.tristram && !!Config.Leader && !getParty(Config.Leader)) { - throw new Error("Can't get leg because I don't have Cain quest."); + throw new ScriptError("Can't get leg because I don't have Cain quest."); } - if (!me.diffCompleted) throw new Error("Final quest incomplete."); + if (!me.diffCompleted) throw new ScriptError("Final quest incomplete."); } catch (e) { - say(e.message ? e.message : e); + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } return false; } @@ -390,39 +522,11 @@ function ControlBot () { delay(200); } - say("Failed to open cow portal."); - - return false; - }; - - /** - * @param {string} nick - * @returns {string | boolean} - */ - const getWpNick = function (nick) { - if (wpNicks.hasOwnProperty(nick)) { - if (wpNicks[nick].requests > 4) { - return "maxrequests"; - } - - if (getTickCount() - wpNicks[nick].timer < 60000) { - return "mintime"; - } - - return true; - } + Chat.say("Failed to open cow portal."); return false; }; - /** - * @param {string} nick - * @returns {void} - */ - const addWpNick = function (nick) { - wpNicks[nick] = { timer: getTickCount(), requests: 0 }; - }; - /** * @param {string} nick * @returns {boolean} @@ -438,18 +542,23 @@ function ControlBot () { try { if (!Misc.inMyParty(nick)) { - throw new Error("Accept party invite, noob."); + throw new ScriptError("Accept party invite, noob."); } - let reqCheck = getWpNick(nick); - if (reqCheck) { - let _eMsg = reqCheck === "maxrequests" - ? ", you have spent all your waypoint requests for this game." - : ", you may request waypoints every 60 seconds."; - throw new Error(nick + _eMsg); + if (!wpNicks.has(nick)) { + wpNicks.set(nick, new WpTracker()); } - addWpNick(nick); + let check = wpNicks.get(nick); + if (check.requests > 4) { + throw new ScriptError("You have spent all your waypoint requests for this game."); + } else if (check.requests > 1 && check.timeSinceLastRequest() < 60000) { + throw new ScriptError( + "You may request wp again in " + + Math.max(0, (60 - Math.floor(check.timeSinceLastRequest() / 1000))) + + " seconds." + ); + } let act = Misc.getPlayerAct(nick); if (!wps.has(act)) return false; @@ -470,10 +579,10 @@ function ControlBot () { Attack.securePosition(me.x, me.y, 20, 1000); } Pather.makePortal(); - say(getAreaName(me.area) + " TP up"); + Chat.say(getAreaName(me.area) + " TP up"); if (!Misc.poll(() => (Game.getPlayer(nick) || next), Time.seconds(30), Time.seconds(1))) { - say("Aborting wp giving."); + Chat.say("Aborting wp giving."); break; } @@ -489,12 +598,16 @@ function ControlBot () { Town.goToTown(1); Town.move("portalspot"); - wpNicks[nick].requests += 1; - wpNicks[nick].timer = getTickCount(); + check.update(); return true; } catch (e) { - say(e.message ? e.message : e); + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } return false; } finally { @@ -534,29 +647,29 @@ function ControlBot () { // ignore messages not related to our commands if (!actions.has(cmd.toLowerCase())) return false; - if (!cmdNicks.hasOwnProperty(nick)) { - cmdNicks[nick] = new PlayerTracker(); + if (!cmdNicks.has(nick)) { + cmdNicks.set(nick, new PlayerTracker()); } + const player = cmdNicks.get(nick); - if (cmdNicks[nick].ignored) { - if (getTickCount() - cmdNicks[nick].ignored < Time.minutes(1)) { + if (player.ignored) { + if (getTickCount() - player.ignored < Time.minutes(1)) { return true; // ignore flooder } // unignore flooder - cmdNicks[nick].unIgnore(); + player.unIgnore(); } - cmdNicks[nick].commands += 1; + player.commands += 1; - if (getTickCount() - cmdNicks[nick].firstCmd < Time.seconds(10)) { - if (cmdNicks[nick].commands > 5) { - cmdNicks[nick].ignored = getTickCount(); - - say(nick + ", you are being ignored for 60 seconds because of flooding."); + if (getTickCount() - player.firstCmd < Time.seconds(10)) { + if (player.commands > 5) { + player.ignored = getTickCount(); + Chat.whisper(nick, "You are being ignored for 60 seconds because of flooding."); } } else { - cmdNicks[nick].resetCmds(); + player.resetCmds(); } return false; @@ -568,48 +681,113 @@ function ControlBot () { * @returns {boolean} */ function chatEvent (nick, msg) { + if (!nick || !msg) return; + if (nick === me.name) return; + msg = msg.toLowerCase(); + if (msg.match(/^rush /gi)) { + msg = msg.split(" ")[1]; + } + if (!actions.has(msg)) return; if (shitList.has(nick)) { - say("No commands for the shitlisted."); + Chat.say("No commands for the shitlisted."); } else { - command = [msg, nick]; + if (running.nick === nick && running.command === msg) { + console.debug("Command already running."); + return; + } + let index = queue.findIndex(function (cmd) { + return cmd[0] === msg && cmd[1] === nick; + }); + if (index > -1) { + Chat.whisper(nick, "You already requested this command. Queue position: " + (index + 1)); + } else { + queue.push([msg, nick]); + console.log(queue); + if (queue.length > 1 || running.nick !== "") { + Chat.whisper(nick, msg + " has been added to the queue. Queue position: " + (queue.length + 1)); + } + } } } // eslint-disable-next-line no-unused-vars function gameEvent (mode, param1, param2, name1, name2) { switch (mode) { - case 0x02: + case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." // idle in town me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); + if (name2) { + players.set(name1, "*" + name2); + } + + break; + 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." + players.delete(name1); break; } } - /** @type {Map { + if (!value.desc.length) return; + if (value.complete) return; + if (value.desc.includes("Rush")) return; + let desc = (key + " (" + value.desc + "), "); + if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { + msg.push(str); + str = ""; + } + str += desc; + }); + str.length && msg.push(str); + str = "Rush commands (example: rush andy): "; _actions.forEach((value, key) => { - str += (key + " (" + value.desc + "), "); + if (!value.desc.length) return; + if (value.complete) return; + if (!value.desc.includes("Rush")) return; + let desc = (key + ", "); + if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { + msg.push(str); + str = ""; + } + str += desc; + }); + str.length && msg.push(str); + msg.forEach(function (m) { + Chat.whisper(nick, m); }); - say("Commands: " + str); } }); _actions.set("timeleft", { - desc: "Remaining time left for this game", + desc: "Remaining time for this game", hostileCheck: false, run: function () { let tick = Time.minutes(Config.ControlBot.GameLength) - getTickCount() + startTime; let m = Math.floor(tick / 60000); let s = Math.floor((tick / 1000) % 60); - say( + Chat.say( "Time left: " + (m ? m + " minute" + (m > 1 ? "s" : "") + ", " : "") + s + " second" + (s > 1 ? "s." : ".") @@ -625,6 +803,9 @@ function ControlBot () { hostileCheck: false, run: enchant })); + _actions.get("enchant").desc = ""; + } else { + Config.ControlBot.Chant.AutoEnchant = false; } if (Config.ControlBot.Cows.MakeCows && !me.cows) { @@ -637,7 +818,7 @@ function ControlBot () { if (Config.ControlBot.Wps.GiveWps) { _actions.set("wps", { - desc: "Give waypoints in act", + desc: "Give wps in act", hostileCheck: true, run: giveWps }); @@ -646,30 +827,198 @@ function ControlBot () { if (Config.ControlBot.Bo && (Skill.canUse(sdk.skills.BattleOrders) || Precast.haveCTA > 0)) { _actions.set("bo", { - desc: "Bo at waypoint", + desc: "Bo at wp", hostileCheck: true, run: bo }); } + if (Config.ControlBot.Rush) { + if (Config.ControlBot.Rush.Andy) { + _actions.set("andy", { + desc: "Rush Andariel", + hostileCheck: true, + complete: false, + run: andariel + }); + } + if (Config.ControlBot.Rush.Cube) { + _actions.set("cube", { + desc: "Rush Cube", + hostileCheck: true, + complete: false, + run: cube + }); + } + if (Config.ControlBot.Rush.Radament) { + _actions.set("rada", { + desc: "Rush Radament", + hostileCheck: true, + complete: false, + run: radament + }); + } + if (Config.ControlBot.Rush.Staff) { + _actions.set("staff", { + desc: "Rush Staff", + hostileCheck: true, + complete: false, + run: staff + }); + } + if (Config.ControlBot.Rush.Amulet) { + _actions.set("amu", { + desc: "Rush Amulet", + hostileCheck: true, + complete: false, + run: amulet + }); + } + if (Config.ControlBot.Rush.Summoner) { + _actions.set("summoner", { + desc: "Rush Summoner", + hostileCheck: true, + complete: false, + run: summoner + }); + } + if (Config.ControlBot.Rush.Duriel) { + _actions.set("duri", { + desc: "Rush Duriel", + hostileCheck: true, + complete: false, + run: duriel + }); + } + if (Config.ControlBot.Rush.LamEsen) { + _actions.set("lamesen", { + desc: "Rush Lamesen", + hostileCheck: true, + complete: false, + run: lamesen + }); + } + if (Config.ControlBot.Rush.Eye) { + _actions.set("eye", { + desc: "Rush eye", + hostileCheck: true, + complete: false, + run: eye + }); + } + if (Config.ControlBot.Rush.Brain) { + _actions.set("brain", { + desc: "Rush brain", + hostileCheck: true, + complete: false, + run: brain + }); + } + if (Config.ControlBot.Rush.Heart) { + _actions.set("heart", { + desc: "Rush heart", + hostileCheck: true, + complete: false, + run: heart + }); + } + if (Config.ControlBot.Rush.Travincal) { + _actions.set("trav", { + desc: "Rush Travincal", + hostileCheck: true, + complete: false, + run: travincal + }); + } + if (Config.ControlBot.Rush.Mephisto) { + _actions.set("meph", { + desc: "Rush Mephisto", + hostileCheck: true, + complete: false, + run: mephisto + }); + } + if (Config.ControlBot.Rush.Izual) { + _actions.set("izzy", { + desc: "Rush Izual", + hostileCheck: true, + complete: false, + run: izual + }); + } + if (Config.ControlBot.Rush.Diablo) { + _actions.set("diablo", { + desc: "Rush Diablo", + hostileCheck: true, + complete: false, + run: diablo + }); + } + if (Config.ControlBot.Rush.Shenk) { + _actions.set("shenk", { + desc: "Rush Shenk", + hostileCheck: true, + complete: false, + run: shenk + }); + } + if (Config.ControlBot.Rush.Anya) { + _actions.set("anya", { + desc: "Rush Anya", + hostileCheck: true, + complete: false, + run: anya + }); + } + if (Config.ControlBot.Rush.Ancients) { + _actions.set("ancients", { + desc: "Rush Ancients", + hostileCheck: true, + complete: false, + run: ancients + }); + } + if (Config.ControlBot.Rush.Baal) { + _actions.set("baal", { + desc: "Rush Baal", + hostileCheck: true, + complete: false, + run: baal + }); + } + } + return _actions; })(); + /** @param {[string, string]} command */ const runAction = function (command) { if (!command || command.length < 2) return false; + console.debug("Checking command: " + command); let [cmd, nick] = command; + if (cmd.match(/^rush /gi)) { + cmd = cmd.split(" ")[1]; + } if (!actions.has(cmd.toLowerCase())) return false; let action = actions.get(cmd.toLowerCase()); + if (action.desc.includes("Rush") && action.complete) { + Chat.whisper(nick, cmd + " disabled because it's already completed."); + return false; + } if (action.hostileCheck && checkHostiles()) { - say("Command disabled because of hostiles."); + Chat.say("Command disabled because of hostiles."); return false; } + running.nick = nick; + running.command = cmd; + console.debug(running); return action.run(nick); }; // START + let gameEndWarningAnnounced = false; include("oog/ShitList.js"); Config.ShitList && shitList.add(ShitList.read()); @@ -682,29 +1031,47 @@ function ControlBot () { while (true) { while (greet.length > 0) { - nick = greet.shift(); + let nick = greet.shift(); if (!shitList.has(nick)) { - say("Welcome, " + nick + "! For a list of commands say 'help'"); + Chat.say("Welcome, " + nick + "! For a list of commands say 'help'"); } } Town.getDistance("portalspot") > 5 && Town.move("portalspot"); - if (command && !floodCheck(command)) { - runAction(command); + if (queue.length > 0) { + try { + let command = queue.shift(); + if (command && !floodCheck(command)) { + if (runAction(command)) { + // check if command was for rush, if so we need to remove that as an option since its now completed + if (actions.get(running.command).desc.includes("Rush")) { + console.log("Disabling " + running.command + " from actions"); + actions.get(running.command).complete = true; + } + } + } + } catch (e) { + Misc.errorReport(e); + } + running.nick = ""; + running.command = ""; } - command = ""; - me.act > 1 && Town.goToTown(1); Config.ControlBot.Chant.AutoEnchant && autoChant(); - if (getTickCount() - startTime >= Time.minutes(Config.ControlBot.GameLength)) { - say((Config.ControlBot.EndMessage ? Config.ControlBot.EndMessage : "Bye")); + if (getTickCount() - startTime >= maxTime) { + if (Config.ControlBot.EndMessage) { + Chat.say(Config.ControlBot.EndMessage); + } delay(1000); break; + } else if (!gameEndWarningAnnounced && getTickCount() - startTime >= maxTime - Time.seconds(30)) { + Chat.say("Next game in 30 seconds."); + gameEndWarningAnnounced = true; } delay(200); From 8602384278a10fa3e9b0711ec1fb35f1288f4eaa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:25:59 -0400 Subject: [PATCH 301/758] Change `ConfigMaxGameTime` to use minutes - why was it ever using seconds is the real question --- d2bs/kolbot/default.dbj | 2 +- d2bs/kolbot/libs/config/Amazon.js | 2 +- d2bs/kolbot/libs/config/Assassin.js | 2 +- d2bs/kolbot/libs/config/Barbarian.js | 2 +- d2bs/kolbot/libs/config/Druid.js | 2 +- d2bs/kolbot/libs/config/Necromancer.js | 4 ++-- d2bs/kolbot/libs/config/Paladin.js | 2 +- d2bs/kolbot/libs/config/Sorceress.js | 2 +- d2bs/kolbot/libs/config/_BaseConfigFile.js | 2 +- d2bs/kolbot/libs/oog/Locations.js | 6 +++++- 10 files changed, 15 insertions(+), 11 deletions(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 6d1566790..f763c6778 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -140,7 +140,7 @@ function main () { return true; } - me.maxgametime = Config.MaxGameTime * 1000; + me.maxgametime = Time.minutes(Config.MaxGameTime); let stats = DataFile.getStats(); // Check for experience decrease -> log death. Skip report if life chicken is disabled. diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 64503be56..2df0072ee 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -374,7 +374,7 @@ function LoadConfig () { Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 4e91d7801..a6bc9e5bb 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -374,7 +374,7 @@ function LoadConfig () { Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 526af7c49..ed45c92ba 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -374,7 +374,7 @@ function LoadConfig () { Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 6427ba5ce..59b825634 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -374,7 +374,7 @@ function LoadConfig () { Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index e1220543a..a83ede667 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -374,7 +374,7 @@ function LoadConfig () { Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings @@ -589,7 +589,7 @@ function LoadConfig () { * Can use monster name or classid * Format: Config.CustomCurse = [["monstername", skillid], [156, skillid]]; * Optional 3rd parameter for spectype, leave blank to use on all - 0x00 Normal Monster + 0x00 Normal Monster 0x01 Super Unique 0x02 Champion 0x04 Boss diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 724c99dbf..34b092e49 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -374,7 +374,7 @@ function LoadConfig () { Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index e0cfd0adb..62d9831d5 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -374,7 +374,7 @@ function LoadConfig () { Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 6c6d961d7..8fba8b4ec 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -355,7 +355,7 @@ Config.AutoMap = false; // Set to true to open automap at the beginning of the game. Config.WaypointMenu = true; // open waypoint menu, if set to false will use packets to interact Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. - Config.MaxGameTime = 0; // Maximum game time in seconds. Quit game when limit is reached. + Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. // Chicken settings diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index 10324af83..1df678076 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -217,9 +217,13 @@ if (Starter.inGame || Starter.gameInfo.error) { !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); + Starter.isUp = "no"; DataFile.updateStats("currentGame", ""); if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { - ControlAction.timeoutDelay("Min game time wait", Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount()); + ControlAction.timeoutDelay( + "Min game time wait", + Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount() + ); } } From 4cbc0a5e8aa9d63538d8a696a0910528fade9527 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 7 Sep 2023 02:38:23 -0400 Subject: [PATCH 302/758] Update ControlBot.js - handle low invo room preventing us from making cow portal --- d2bs/kolbot/libs/scripts/ControlBot.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index c128bfca8..1ede07e01 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -441,6 +441,11 @@ function ControlBot () { let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); if (tpTome.length < 2) { + if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 2 })) { + if (tpTome.length === 1) { + return tpTome.first(); + } + } let npc = Town.initNPC("Shop", "buyTpTome"); if (!getInteractedNPC()) throw new Error("Failed to find npc"); @@ -499,6 +504,11 @@ function ControlBot () { let leg = getLeg(); if (!leg) return false; + if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 2 })) { + // we don't have any space, put the leg in the stash to make room in invo + Storage.Stash.MoveTo(leg); + me.cancelUIFlags(); + } let tome = getTome(); if (!tome) return false; From dc45dda2218a44dd1d5ca63e9157d70d77f7a00d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:32:03 -0400 Subject: [PATCH 303/758] Update Loader.js - allow passing in an object to override config values for duration of `Loader.runScript` --- d2bs/kolbot/libs/core/Loader.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 6b673eb9b..50e0e82a2 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -130,12 +130,12 @@ const Loader = { } if (this.skipTown.includes(script) || Town.goToTown()) { - print("ÿc2Starting script: ÿc9" + script); + console.log("ÿc2Starting script: ÿc9" + script); Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); reconfiguration = typeof Scripts[script] === "object"; if (reconfiguration) { - print("ÿc2Copying Config properties from " + script + " object."); + console.log("ÿc2Copying Config properties from " + script + " object."); this.copy(Scripts[script], Config); } @@ -184,7 +184,7 @@ const Loader = { } if (reconfiguration) { - print("ÿc2Reverting back unmodified config properties."); + console.log("ÿc2Reverting back unmodified config properties."); this.copy(unmodifiedConfig, Config); } } @@ -226,19 +226,22 @@ const Loader = { if (this.skipTown.includes(script) || Town.goToTown()) { let mainScriptStr = (mainScript !== script ? buildScriptMsg() : ""); this.tempList.push(script); - print(mainScriptStr + "ÿc2Starting script: ÿc9" + script); + console.log(mainScriptStr + "ÿc2Starting script: ÿc9" + script); Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); reconfiguration = typeof Scripts[script] === "object"; if (reconfiguration) { - print("ÿc2Copying Config properties from " + script + " object."); + console.log("ÿc2Copying Config properties from " + script + " object."); this.copy(Scripts[script], Config); } if (typeof configOverride === "function") { reconfiguration = true; configOverride(); + } else if (typeof configOverride === "object") { + reconfiguration = true; + this.copy(configOverride, Config); } let tick = getTickCount(); @@ -278,7 +281,7 @@ const Loader = { this.tempList.pop(); if (reconfiguration) { - print("ÿc2Reverting back unmodified config properties."); + console.log("ÿc2Reverting back unmodified config properties."); this.copy(unmodifiedConfig, Config); } } @@ -287,6 +290,11 @@ const Loader = { return !failed; }, + /** + * Get script name by index + * @param {number} [offset] + * @returns {string} + */ scriptName: function (offset = 0) { let index = this.scriptIndex + offset; @@ -294,6 +302,6 @@ const Loader = { return this.scriptList[index]; } - return null; + return ""; } }; From 1035bb69e2a50a7c8d7137bff74018585c3947b0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 10 Sep 2023 00:29:54 -0400 Subject: [PATCH 304/758] Add exclude game option to channel joiner - allow excluding game names/patterns --- d2bs/kolbot/D2BotChannel.dbj | 30 +++++++++++++++++-- .../libs/systems/channel/ChannelConfig.js | 15 ++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 29c6f69cf..f2cfbc27f 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -80,6 +80,31 @@ const locationAction = (function () { return messageQueue.length > preLen; }; + /** @param {string} game */ + const exclude = function (game) { + // No filters + if (!ChannelConfig.excludeFilter.length) return false; + + for (let filterSet of ChannelConfig.excludeFilter) { + let conditionsMatched = true; + + for (let condition of filterSet) { + // Break the inner loop if an element didn't match or if an element is invalid + if (!condition || !game.match(condition, "gi")) { + conditionsMatched = false; + break; + } + } + + // All elements matched + if (conditionsMatched) { + return true; + } + } + + return false; + }; + let lastText; let channelTick = getTickCount(); let fListTick = 0; @@ -226,6 +251,7 @@ const locationAction = (function () { // check if this is a game we are looking for - what about just following the friend in all games that are not private? for (let gInfo of ChannelConfig.Games) { if (gameName.match(gInfo.game, "gi") + && !exclude(gameName) && !String.isEqual(gameName, joinInfo.oldGame) && !badGames.has(gameName)) { // check if the game is private @@ -278,7 +304,7 @@ const locationAction = (function () { let [, gameName, gamePass] = match; // double check that this is a valid game name - if (gameName.length > 15 || badGames.has(gameName)) continue; + if (gameName.length > 15 || badGames.has(gameName) || exclude(gameName)) continue; // handle following player names if (ChannelConfig.Follow.length > 0) { @@ -310,7 +336,7 @@ const locationAction = (function () { if (game === "") continue; if (!gameName.toLowerCase().includes(game.toLowerCase())) continue; // alright so this gamename is one we were looking for, lets confirm it is not a bad game - if (badGames.has(gameName)) continue; + if (badGames.has(gameName) || exclude(gameName)) continue; // alright so this is a game we want to join, lets set the game name and password console.log("Joining game: " + gameName + "//" + gamePass); // wait for the player to leave the channel diff --git a/d2bs/kolbot/libs/systems/channel/ChannelConfig.js b/d2bs/kolbot/libs/systems/channel/ChannelConfig.js index 5c68fa231..eeaf04e62 100644 --- a/d2bs/kolbot/libs/systems/channel/ChannelConfig.js +++ b/d2bs/kolbot/libs/systems/channel/ChannelConfig.js @@ -27,6 +27,21 @@ Games: [ { game: "", password: "" }, ], + /** + * @description excludeFilter format + * @example Multiple entries in the same array mean AND + * // ignores games that contain "baal" and "-" + * const includeFilter = ["baal", "-"]; + * + * @example Multiple entries in different arrays mean OR + * // will ignore games with either "baal" or "diablo" in their name + * const includeFilter = [ + * ["baal"], + * ["diablo"] + * ]; + * @type {Array>} + */ + excludeFilter: [], /** * Leaders in game character name, only use this if the leader is using announce in the chat. * Can be an array or names ["somename", "somename2"] From 4b0e09253b61ff4cb7a2d2ebca28981a8e2ff0c2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:17:49 -0400 Subject: [PATCH 305/758] Update Barbarian.js - remove debug printing, failure to hork doesn't need to be logged d2bot --- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index d596d60af..bb26f2351 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -254,9 +254,9 @@ const ClassAttack = { } if (attempted && !invalidated && corpse && !corpse.getState(sdk.states.CorpseNoSelect)) { - if (!me.inArea(sdk.areas.ThroneofDestruction)) { - D2Bot.printToConsole("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); - } + // if (!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)); } } From d43a34761e73a514b347678a848911bf6fbe8707 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:28:16 -0400 Subject: [PATCH 306/758] Update Prototypes.js - add `Unit.prototype.isCharm` --- d2bs/kolbot/libs/core/Prototypes.js | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 1255cd99f..469ed73e9 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -449,12 +449,15 @@ Object.defineProperties(Unit.prototype, { } }, isEquipped: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.location === sdk.storage.Equipped; } }, isEquippedCharm: { + // todo - fix this for storage checks + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return (this.location === sdk.storage.Inventory @@ -462,24 +465,28 @@ Object.defineProperties(Unit.prototype, { } }, isInInventory: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.location === sdk.storage.Inventory && this.mode === sdk.items.mode.inStorage; } }, isInStash: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.location === sdk.storage.Stash && this.mode === sdk.items.mode.inStorage; } }, isInCube: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.location === sdk.storage.Cube && this.mode === sdk.items.mode.inStorage; } }, isInStorage: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.mode === sdk.items.mode.inStorage @@ -487,6 +494,7 @@ Object.defineProperties(Unit.prototype, { } }, isInBelt: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.location === sdk.storage.Belt && this.mode === sdk.items.mode.inBelt; @@ -519,6 +527,7 @@ Object.defineProperties(Unit.prototype, { } }, identified: { + /** @this {ItemUnit} */ get: function () { // Can't tell, as it isn't an item if (this.type !== sdk.unittype.Item) return undefined; @@ -527,6 +536,7 @@ Object.defineProperties(Unit.prototype, { } }, ethereal: { + /** @this {ItemUnit} */ get: function () { // Can't tell, as it isn't an item if (this.type !== sdk.unittype.Item) return undefined; @@ -534,29 +544,34 @@ Object.defineProperties(Unit.prototype, { } }, twoHanded: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return getBaseStat("items", this.classid, "2handed") === 1; } }, oneOrTwoHanded: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return getBaseStat("items", this.classid, "1or2handed") === 1; } }, strictlyTwoHanded: { + /** @this {ItemUnit} */ get: function () { return this.twoHanded && !this.oneOrTwoHanded; } }, runeword: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return !!this.getFlag(sdk.items.flags.Runeword); } }, questItem: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return (this.itemType === sdk.items.type.Quest @@ -570,6 +585,7 @@ Object.defineProperties(Unit.prototype, { } }, sellable: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; if (this.getItemCost(sdk.items.cost.ToSell) <= 1) return false; @@ -585,84 +601,105 @@ Object.defineProperties(Unit.prototype, { } }, lowquality: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.LowQuality; }, }, normal: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.Normal; }, }, superior: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.Superior; }, }, magic: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.Magic; }, }, set: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.Set; }, }, rare: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.Rare; }, }, unique: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.Unique; }, }, crafted: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.quality === sdk.items.quality.Crafted; }, }, sockets: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.getStat(sdk.stats.NumSockets); }, }, onGroundOrDropping: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return (this.mode === sdk.items.mode.onGround || this.mode === sdk.items.mode.Dropping); }, }, isShield: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return [sdk.items.type.Shield, sdk.items.type.AuricShields, sdk.items.type.VoodooHeads].includes(this.itemType); }, }, + 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); + } + }, isAnni: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.unique && this.itemType === sdk.items.type.SmallCharm; }, }, isTorch: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.unique && this.itemType === sdk.items.type.LargeCharm; }, }, isGheeds: { + /** @this {ItemUnit} */ get: function () { if (this.type !== sdk.unittype.Item) return false; return this.unique && this.itemType === sdk.items.type.GrandCharm; From fa2105ff3181ca1399e8bd82f9365e1d4c0ace5f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:31:44 -0400 Subject: [PATCH 307/758] some refactoring in manualplay - mostly cleaning up the hook methods and making some of the variables private to their namespace - todo: cache locations and speed up the crud operations on hooks --- d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js | 2 +- .../kolbot/libs/manualplay/hooks/ItemHooks.js | 789 +++++++++--------- .../libs/manualplay/hooks/MonsterHooks.js | 155 ++-- .../libs/manualplay/hooks/VectorHooks.js | 2 +- 4 files changed, 478 insertions(+), 470 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js b/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js index 7285f1c19..eac34086c 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js +++ b/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js @@ -152,7 +152,7 @@ Worker.runInBackground.helpAction = function () { !!str && me.overhead(str); } catch (e) { - print(e); + console.error(e); me.overhead(cmd); } diff --git a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js index 8d8a3385c..a8afee658 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js @@ -7,442 +7,443 @@ */ // todo - clean up all the map stuff -const ItemHooks = { - enabled: true, - pickitEnabled: false, - modifier: 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename)), - hooks: [], - ignoreItemTypes: [ - sdk.items.type.Gold, sdk.items.type.BowQuiver, sdk.items.type.CrossbowQuiver, sdk.items.type.Book, sdk.items.type.Gem, sdk.items.type.Scroll, - sdk.items.type.MissilePotion, sdk.items.type.Key, sdk.items.type.Boots, sdk.items.type.Gloves, sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, - 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.ChippedGem, sdk.items.type.FlawedGem, sdk.items.type.StandardGem, sdk.items.type.FlawlessGem, sdk.items.type.PerfectgGem, - sdk.items.type.Amethyst, sdk.items.type.Diamond, sdk.items.type.Emerald, sdk.items.type.Ruby, sdk.items.type.Sapphire, sdk.items.type.Topaz, sdk.items.type.Skull - ], - codeById: new Map(), - codeByIdAndQuality: new Map(), - itemColorCode: [], - +const ItemHooks = (function () { + const modifier = ( + 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename)) + ); + const ignoreItemTypes = [ + sdk.items.type.Gold, sdk.items.type.BowQuiver, + sdk.items.type.CrossbowQuiver, sdk.items.type.Book, + sdk.items.type.Gem, sdk.items.type.Scroll, + sdk.items.type.MissilePotion, sdk.items.type.Key, + sdk.items.type.Boots, sdk.items.type.Gloves, + sdk.items.type.ThrowingKnife, sdk.items.type.ThrowingAxe, + 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.ChippedGem, sdk.items.type.FlawedGem, + sdk.items.type.StandardGem, sdk.items.type.FlawlessGem, + sdk.items.type.PerfectgGem, sdk.items.type.Amethyst, + sdk.items.type.Diamond, sdk.items.type.Emerald, + sdk.items.type.Ruby, sdk.items.type.Sapphire, + sdk.items.type.Topaz, sdk.items.type.Skull + ]; /** - * @param {number} id - classID of item - * @param {string} setName - * @param {string} uniqueName + * Unique Items */ - addToCodeByClassIdAndQuality: function (id, setName = "", uniqueName = "") { - if (!id || (!setName && !uniqueName)) return; - // create our map structure - if (!this.codeByIdAndQuality.get(id)) { - let temp = []; - setName && temp.push([sdk.items.quality.Set, setName]); - uniqueName && temp.push([sdk.items.quality.Unique, uniqueName]); - this.codeByIdAndQuality.set(id, new Map(temp)); - } - }, + const codeById = new Map([ + [sdk.items.BattleAxe, "The Chieftain"], + [sdk.items.Falchion, "Gleamscythe"], + [sdk.items.BurntWand, "Suicide Branch"], + [sdk.items.PetrifiedWand, "Carin Shard"], + [sdk.items.TombWand, "King Leoric's Arm"], + [sdk.items.Quarterstaff, "Ribcracker"], + [sdk.items.EdgeBow, "Skystrike"], + [sdk.items.GreaterTalons, "Bartuc's"], + [sdk.items.WristSword, "Jade Talon"], + [sdk.items.BattleCestus, "Shadow Killer"], + [sdk.items.FeralClaws, "Firelizard's"], + [sdk.items.EttinAxe, "Rune Master"], + [sdk.items.LichWand, "Boneshade"], + [sdk.items.UnearthedWand, "Death's Web"], + [sdk.items.FlyingAxe, "Gimmershred"], + [sdk.items.WingedKnife, "Warshrike"], + [sdk.items.WingedAxe, "Lacerator"], + [sdk.items.Thresher, "Reaper's Toll"], + [sdk.items.CrypticAxe, "Tomb Reaver"], + [sdk.items.GiantThresher, "Stormspire"], + [sdk.items.ArchonStaff, "Mang Song's"], + [sdk.items.CrusaderBow, "Eaglehorn"], + [sdk.items.WardBow, "Ward Bow"], + [sdk.items.HydraBow, "Windforce"], + [sdk.items.CeremonialBow, "Lycander's Aim"], + [sdk.items.CeremonialPike, "Lycander's Pike"], + [sdk.items.CeremonialJavelin, "Titan's Revenge"], + [sdk.items.EldritchOrb, "Eschuta's"], + [sdk.items.DimensionalShard, "Death's Fathom"], + [sdk.items.MatriarchalBow, "Bloodraven's"], + [sdk.items.MatriarchalSpear, "Stoneraven"], + [sdk.items.MatriarchalJavelin, "Thunder Stroke"], + [sdk.items.LightPlatedBoots, "Goblin Toe"], + [sdk.items.Sallet, "Rockstopper"], + [sdk.items.GhostArmor, "Spirit Shroud"], + [sdk.items.SerpentskinArmor, "Vipermagi's"], + [sdk.items.MeshArmor, "Shaftstop"], + [sdk.items.RussetArmor, "Skullder's"], + [sdk.items.MagePlate, "Que-Hegan's"], + [sdk.items.SharkskinBoots, "Waterwalk"], + [sdk.items.DemonHead, "Andariel's Vis"], + [sdk.items.Tiara, "Kira's"], + [sdk.items.Shako, "Harlequin Crest"], + [sdk.items.WireFleece, "Gladiator's Bane"], + [sdk.items.ScarabshellBoots, "Sandstorm Trek's"], + [sdk.items.BoneweaveBoots, "Marrowwalk"], + [sdk.items.MyrmidonGreaves, "Shadow Dancer"], + [sdk.items.TotemicMask, "Jalal's"], + [sdk.items.SlayerGuard, "Arreat's Face"], + [sdk.items.GildedShield, "HoZ"], + [sdk.items.HierophantTrophy, "Homunculus"], + [sdk.items.BloodSpirit, "Cerebus"], + [sdk.items.EarthSpirit, "Spirit Keeper"], + [sdk.items.FuryVisor, "Wolfhowl"], + [sdk.items.DestroyerHelm, "Demonhorn's"], + [sdk.items.ConquerorCrown, "Halaberd's"], + [sdk.items.SacredRondache, "Alma Negra"], + [sdk.items.ZakarumShield, "Dragonscale"], + [sdk.items.BloodlordSkull, "Darkforce"], + [sdk.items.SuccubusSkull, "Boneflame"], + [sdk.items.SmallCharm, "Annihilus"], + [sdk.items.LargeCharm, "Hellfire Torch"], + [sdk.items.GrandCharm, "Gheed's"], + [sdk.items.Jewel, "Facet"], + /** Misc Items */ + [sdk.items.quest.TokenofAbsolution, "ÿc8Token"], + [sdk.items.quest.TwistedEssenceofSuffering, "ÿc3Ess-Of-Suffering"], + [sdk.items.quest.ChargedEssenceofHatred, "ÿc7Ess-Of-Hatred"], + [sdk.items.quest.BurningEssenceofTerror, "ÿc1Ess-Of-Terror"], + [sdk.items.quest.FesteringEssenceofDestruction, "ÿc3Ess-Of-Destruction"], + ]); + /** + * @param {string} setName + * @param {string} uniqueName + * @returns {Map} + */ + const buildClassIdAndQuality = function (id, setName = "", uniqueName = "") { + let temp = new Map(); + setName && temp.set(sdk.items.quality.Set, setName); + uniqueName && temp.set(sdk.items.quality.Unique, uniqueName); + return temp; + }; + /** + * Set/Unique Items + */ + const codeByIdAndQuality = new Map([ + [sdk.items.JaggedStar, buildClassIdAndQuality("Aldur's Wep", "Moonfall")], + [sdk.items.HuntersGuise, buildClassIdAndQuality("Aldur's Helm")], + [sdk.items.ShadowPlate, buildClassIdAndQuality("Aldur's Armor", "Steel Carapace")], + [sdk.items.BattleBoots, buildClassIdAndQuality("Aldur's Boots", "War Trav's")], + [sdk.items.Caduceus, buildClassIdAndQuality("Griswold's Wep", "Astreon's")], + [sdk.items.Crown, buildClassIdAndQuality("Griswold's Helm", "Crown of Ages")], + [sdk.items.OrnatePlate, buildClassIdAndQuality("Griswold's Armor", "Corpsemourn")], + [sdk.items.VortexShield, buildClassIdAndQuality("Griswold's Shield")], + [sdk.items.OgreMaul, buildClassIdAndQuality("IK Maul", "Windhammer")], + [sdk.items.AvengerGuard, buildClassIdAndQuality("IK Helm")], + [sdk.items.SacredArmor, buildClassIdAndQuality("IK Armor")], + [sdk.items.WarGauntlets, buildClassIdAndQuality("IK Gloves", "HellMouth")], + [sdk.items.WarBoots, buildClassIdAndQuality("IK Boots", "Gore Rider")], + [sdk.items.GrandMatronBow, buildClassIdAndQuality("Mavina's Bow")], + [sdk.items.KrakenShell, buildClassIdAndQuality("Mavina's Armor", "Leviathan")], + [sdk.items.Diadem, buildClassIdAndQuality("Mavina's Helm", "Griffon's Eye")], + [sdk.items.SharkskinBelt, buildClassIdAndQuality("Mavina's Belt", "Razortail")], + [sdk.items.BattleGauntlets, buildClassIdAndQuality("Mavina's Gloves", "Lava Gout")], + [sdk.items.ScissorsKatar, buildClassIdAndQuality("Natalya's Wep")], + [sdk.items.LoricatedMail, buildClassIdAndQuality("Natalya's Armor")], + [sdk.items.GrimHelm, buildClassIdAndQuality("Natalya's Helm", "Vamp Gaze")], + [sdk.items.MeshBoots, buildClassIdAndQuality("Natalya's Boots", "Silkweave")], + [sdk.items.SwirlingCrystal, buildClassIdAndQuality("Tal Orb", "Occulus")], + [sdk.items.LacqueredPlate, buildClassIdAndQuality("Tal Armor")], + [sdk.items.DeathMask, buildClassIdAndQuality("Tal Helm", "Blackhorn's")], + [sdk.items.MeshBelt, buildClassIdAndQuality("Tal Belt", "Gloom's Trap")], + [sdk.items.BoneVisage, buildClassIdAndQuality("Trang Helm", "Giant Skull")], + [sdk.items.ChaosArmor, buildClassIdAndQuality("Trang Armor", "Black Hades")], + [sdk.items.TrollBelt, buildClassIdAndQuality("Trang Belt")], + [sdk.items.HeavyBracers, buildClassIdAndQuality("Trang Gloves", "Ghoulhide")], + [sdk.items.CantorTrophy, buildClassIdAndQuality("Trang Shield")], + [sdk.items.ColossusBlade, buildClassIdAndQuality("Bul-Kathos Blade", "Grandfather")], + [sdk.items.MythicalSword, buildClassIdAndQuality("Bul-Kathos Sword")], + [sdk.items.WarHat, buildClassIdAndQuality("Cow King's Helm", "Peasant Crown")], + [sdk.items.StuddedLeather, buildClassIdAndQuality("Cow King's Armor", "Twitchthroe")], + [sdk.items.HeavyBoots, buildClassIdAndQuality(null, "Gorefoot")], + [sdk.items.Mace, buildClassIdAndQuality("Heavens's Wep", "Crushflange")], + [sdk.items.SpiredHelm, buildClassIdAndQuality("Heavens's Helm", "Nightwing's")], + [sdk.items.Cuirass, buildClassIdAndQuality("Heavens's Armor", "Duriel's Shell")], + [sdk.items.Ward, buildClassIdAndQuality("Heavens's Shield")], + [sdk.items.Bill, buildClassIdAndQuality("Hwanin's Bill", "Blackleach")], + [sdk.items.TigulatedMail, buildClassIdAndQuality("Hwanin's Armor", "Crow Caw")], + [sdk.items.GrandCrown, buildClassIdAndQuality("Hwanin's Helm", "Crown of Thieves")], + [sdk.items.Belt, buildClassIdAndQuality(null, "Nightsmoke")], + [sdk.items.HellforgePlate, buildClassIdAndQuality("Naj's Armor")], + [sdk.items.ElderStaff, buildClassIdAndQuality("Naj's Staff", "Ondal's Wisdom")], + [sdk.items.Circlet, buildClassIdAndQuality("Naj's Helm")], + [sdk.items.SmallShield, buildClassIdAndQuality(null, "Umbral Disk")], + [sdk.items.WingedHelm, buildClassIdAndQuality("G-Face", "Valk Wing")], + [sdk.items.HeavyBelt, buildClassIdAndQuality("Orphan's Belt")], + [sdk.items.HeavyGloves, buildClassIdAndQuality(null, "Bloodfist")], + [sdk.items.CrypticSword, buildClassIdAndQuality("Sazabi's Wep", "Frostwind")], + [sdk.items.BalrogSkin, buildClassIdAndQuality("Sazabi's Armor", "Arkaine's")], + [sdk.items.BreastPlate, buildClassIdAndQuality(null, "Venom Ward")], + [sdk.items.GothicShield, buildClassIdAndQuality(null, "The Ward")], + [sdk.items.DuskShroud, buildClassIdAndQuality("Disciple's Armor", "Ormus Robe's")], + [sdk.items.MithrilCoil, buildClassIdAndQuality("Disciple's Belt", "Verdungo's")], + [sdk.items.BrambleMitts, buildClassIdAndQuality("Disciple's Gloves")], + [sdk.items.DemonhideBoots, buildClassIdAndQuality("Disciple's Boots", "Infernostride")], + [sdk.items.RingMail, buildClassIdAndQuality("Angelic's Armor", "Darkglow")], + [sdk.items.Sabre, buildClassIdAndQuality("Angelic's Wep", "Krintiz")], + [sdk.items.SkullCap, buildClassIdAndQuality("Arcanna's Helm", "Tarnhelm")], + [sdk.items.LightPlate, buildClassIdAndQuality("Arcanna's Armor", "Heavenly Garb")], + [sdk.items.WarStaff, buildClassIdAndQuality("Arcanna's Staff", "Iron Jang Bong")], + [sdk.items.LightGauntlets, buildClassIdAndQuality("Artic's Gloves", "Magefist")], + [sdk.items.LightBelt, buildClassIdAndQuality("Artic's Belt", "Snakecord")], + [sdk.items.QuiltedArmor, buildClassIdAndQuality("Artic's Armor", "Greyform")], + [sdk.items.ShortWarBow, buildClassIdAndQuality("Artic's Bow", "Hellclap")], + /** Berserker's */ + [sdk.items.DoubleAxe, buildClassIdAndQuality("Beserker's Axe", "Bladebone")], + [sdk.items.SplintMail, buildClassIdAndQuality("Beserker's Armor", "Iceblink")], + [sdk.items.Helm, buildClassIdAndQuality("Beserker's Helm", "Coif of Glory")], + /** Tancred's */ + [sdk.items.BoneHelm, buildClassIdAndQuality("Tancred's Skull", "Wormskull")], + [sdk.items.FullPlateMail, buildClassIdAndQuality("Tancred's Spine", "Goldskin")], + [sdk.items.MilitaryPick, buildClassIdAndQuality("Tancred's Crowbill", "Skull Splitter")], + [sdk.items.Boots, buildClassIdAndQuality("Tancred's Hobnails", "Hotspur")], + ]); + const itemColorCode = {}; + itemColorCode[sdk.items.quality.Magic] = { color: 0x97, code: "ÿc3" }; + itemColorCode[sdk.items.quality.Set] = { color: 0x84, code: "ÿc2" }; + itemColorCode[sdk.items.quality.Rare] = { color: 0x6F, code: "ÿc9" }; + itemColorCode[sdk.items.quality.Unique] = { color: 0xA8, code: "ÿc4" }; + + + return { + enabled: true, + pickitEnabled: false, + hooks: [], + + check: function () { + if (!this.enabled) { + this.flush(); - check: function () { - if (!this.enabled) { - this.flush(); + return; + } - return; - } + for (let i = 0; i < this.hooks.length; i++) { + if (!copyUnit(this.hooks[i].item).x) { + for (let j = 0; j < this.hooks[i].hook.length; j++) { + this.hooks[i].hook[j].remove(); + } - for (let i = 0; i < this.hooks.length; i++) { - if (!copyUnit(this.hooks[i].item).x) { - for (let j = 0; j < this.hooks[i].hook.length; j++) { - this.hooks[i].hook[j].remove(); + this.hooks[i].name[0] && this.hooks[i].name[0].remove(); + this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); + this.hooks.splice(i, 1); + i -= 1; + this.flush(); } - - this.hooks[i].name[0] && this.hooks[i].name[0].remove(); - this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); - this.hooks.splice(i, 1); - i -= 1; - this.flush(); } - } - - let item = Game.getItem(); - if (item) { - try { - do { - if (item.area === ActionHooks.currArea && item.onGroundOrDropping - && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && !this.ignoreItemTypes.includes(item.itemType)))) { - if (this.pickitEnabled) { - if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(Pickit.checkItem(item).result) === -1) { + let item = Game.getItem(); + + if (item) { + try { + do { + if (item.area === ActionHooks.currArea && item.onGroundOrDropping + && (item.quality >= sdk.items.quality.Magic || ((item.normal || item.superior) && !ignoreItemTypes.includes(item.itemType)))) { + if (this.pickitEnabled) { + if ([Pickit.Result.UNWANTED, Pickit.Result.TRASH].indexOf(Pickit.checkItem(item).result) === -1) { + !this.getHook(item) && this.add(item); + } + } else { !this.getHook(item) && this.add(item); } + + this.getHook(item) && this.update(); } else { - !this.getHook(item) && this.add(item); + this.remove(item); } - - this.getHook(item) && this.update(); - } else { - this.remove(item); - } - } while (item.getNext()); - } catch (e) { - console.error(e); - this.flush(); + } while (item.getNext()); + } catch (e) { + console.error(e); + this.flush(); + } } - } - }, + }, - update: function () { - for (let i = 0; i < this.hooks.length; i++) { - this.hooks[i].vector[0].x = me.x; - this.hooks[i].vector[0].y = me.y; - } - }, - - /** - * @param {ItemUnit} item - * @returns {string} - */ - getName: function (item) { - let abbr = item.name.split(" "); - let abbrName = ""; - - if (abbr[1]) { - abbrName += abbr[0] + "-"; - - for (let i = 1; i < abbr.length; i++) { - abbrName += abbr[i].substring(0, 1); + update: function () { + for (let i = 0; i < this.hooks.length; i++) { + this.hooks[i].vector[0].x = me.x; + this.hooks[i].vector[0].y = me.y; } - } + }, - return !!abbrName ? abbrName : item.name; - }, + /** + * @param {ItemUnit} item + * @returns {string} + */ + getName: function (item) { + let abbr = item.name.split(" "); + let abbrName = ""; - /** - * @description Create a new hook for a item with custom color and code based on type/quality/classid - * @param {ItemUnit} item - * @todo maybe make class wrappers for hooks and turn the hook array into a map? - */ - newHook: function (item) { - let color = 0, code = "", arr = [], name = [], vector = []; - let eth = (item.ethereal ? "Eth: " : ""); - - switch (item.quality) { - case sdk.items.quality.Normal: - case sdk.items.quality.Superior: - switch (item.itemType) { - case sdk.items.type.Quest: - color = 0x9A; - code += (this.codeById.get(item.classid) || "ÿc8" + item.fname); + if (abbr[1]) { + abbrName += abbr[0] + "-"; - break; - case sdk.items.type.Rune: - if (item.classid >= sdk.items.runes.Vex) { - [color, code] = [0x9B, "ÿc;" + item.fname]; - } else if (item.classid >= sdk.items.runes.Lum) { - [color, code] = [0x9A, "ÿc8" + item.fname]; - } else { - [color, code] = [0xA1, item.fname]; + for (let i = 1; i < abbr.length; i++) { + abbrName += abbr[i].substring(0, 1); } + } - break; - default: - if (item.name && item.sockets !== 1) { - color = 0x20; - - if (item.runeword) { - code = item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""); + return !!abbrName ? abbrName : item.name; + }, + + /** + * @description Create a new hook for a item with custom color and code based on type/quality/classid + * @param {ItemUnit} item + * @todo maybe make class wrappers for hooks and turn the hook array into a map? + */ + newHook: function (item) { + let color = 0, code = "", arr = [], name = [], vector = []; + let eth = (item.ethereal ? "Eth: " : ""); + + switch (item.quality) { + case sdk.items.quality.Normal: + case sdk.items.quality.Superior: + switch (item.itemType) { + case sdk.items.type.Quest: + color = 0x9A; + code += (codeById.get(item.classid) || "ÿc8" + item.fname); + + break; + case sdk.items.type.Rune: + if (item.classid >= sdk.items.runes.Vex) { + [color, code] = [0x9B, "ÿc;" + item.fname]; + } else if (item.classid >= sdk.items.runes.Lum) { + [color, code] = [0x9A, "ÿc8" + item.fname]; } else { - code = "ÿc0" + (item.sockets > 0 ? "[" + item.sockets + "]" : ""); - code += this.getName(item); - item.itemType === sdk.items.type.AuricShields && (code += "[R: " + item.getStat(sdk.stats.FireResist) + "]"); - code += "(" + item.ilvl + ")"; + [color, code] = [0xA1, item.fname]; + } + + break; + default: + if (item.name && item.sockets !== 1) { + color = 0x20; + + if (item.runeword) { + code = item.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, ""); + } else { + code = "ÿc0" + (item.sockets > 0 ? "[" + item.sockets + "]" : ""); + code += this.getName(item); + item.itemType === sdk.items.type.AuricShields && (code += "[R: " + item.getStat(sdk.stats.FireResist) + "]"); + code += "(" + item.ilvl + ")"; + } } + + break; } break; - } + case sdk.items.quality.Set: + case sdk.items.quality.Unique: + ({ color, code } = this.itemColorCode[item.quality]); - break; - case sdk.items.quality.Set: - case sdk.items.quality.Unique: - ({ color, code } = this.itemColorCode[item.quality]); + if (codeById.has(item.classid)) { + code += codeById.get(item.classid); + } - if (this.codeById.has(item.classid)) { - code += this.codeById.get(item.classid); - } + switch (item.classid) { + case sdk.items.Ring: + case sdk.items.Amulet: + code += item.name + "(" + item.ilvl + ")"; + + break; + default: + { + let check = codeByIdAndQuality.get(item.classid); + code += ((check && check.get(item.quality)) || item.name); + } + + break; + } - switch (item.classid) { - case sdk.items.Ring: - case sdk.items.Amulet: - code += item.name + "(" + item.ilvl + ")"; - break; - default: - { - let check = this.codeByIdAndQuality.get(item.classid); - code += ((check && check.get(item.quality)) || item.name); + case sdk.items.quality.Magic: + case sdk.items.quality.Rare: + if (item.name) { + ({ color, code } = this.itemColorCode[item.quality]); + + code += (item.sockets > 0 ? "[" + item.sockets + "]" : ""); + code += this.getName(item); + code += "(" + item.ilvl + ")"; } break; } - break; - case sdk.items.quality.Magic: - case sdk.items.quality.Rare: - if (item.name) { - ({ color, code } = this.itemColorCode[item.quality]); - - code += (item.sockets > 0 ? "[" + item.sockets + "]" : ""); - code += this.getName(item); - code += "(" + item.ilvl + ")"; + !!code && name.push(new Text(eth + code, 665 + Hooks.resfix.x, 104 + modifier + (this.hooks.length * 14), color, 0, 0)); + vector.push(new Line(me.x, me.y, item.x, item.y, color, true)); + arr.push(new Line(item.x - 3, item.y, item.x + 3, item.y, color, true)); + arr.push(new Line(item.x, item.y - 3, item.x, item.y + 3, color, true)); + + return { + itemLoc: arr, + itemName: name, + vector: vector, + }; + }, + + /** + * Add new item hook to our hook array + * @param {ItemUnit} item + */ + add: function (item) { + if (item === undefined || !item.classid) { + return; } - - break; - } - - !!code && name.push(new Text(eth + code, 665 + Hooks.resfix.x, 104 + this.modifier + (this.hooks.length * 14), color, 0, 0)); - vector.push(new Line(me.x, me.y, item.x, item.y, color, true)); - arr.push(new Line(item.x - 3, item.y, item.x + 3, item.y, color, true)); - arr.push(new Line(item.x, item.y - 3, item.x, item.y + 3, color, true)); - - return { - itemLoc: arr, - itemName: name, - vector: vector, - }; - }, - /** - * Add new item hook to our hook array - * @param {ItemUnit} item - */ - add: function (item) { - if (item === undefined || !item.classid) { - return; - } - - let temp = this.newHook(item); - - this.hooks.push({ - item: copyUnit(item), - area: item.area, - hook: temp.itemLoc, - name: temp.itemName, - vector: temp.vector, - }); - }, - - /** - * Get item hook if it exists based on item parameters gid - * @param {ItemUnit} item - * @returns {{ item: ItemUnit, area: number, hook: Line, name: Text, vector: Line} | false} - */ - getHook: function (item) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].item.gid === item.gid) { - return this.hooks[i].hook; + let temp = this.newHook(item); + + this.hooks.push({ + item: copyUnit(item), + area: item.area, + hook: temp.itemLoc, + name: temp.itemName, + vector: temp.vector, + }); + }, + + /** + * Get item hook if it exists based on item parameters gid + * @param {ItemUnit} item + * @returns {{ item: ItemUnit, area: number, hook: Line, name: Text, vector: Line} | false} + */ + getHook: function (item) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].item.gid === item.gid) { + return this.hooks[i].hook; + } } - } - return false; - }, + return false; + }, + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + remove: function (item) { + for (let i = 0; i < this.hooks.length; i++) { + if (this.hooks[i].item.gid === item.gid) { + for (let j = 0; j < this.hooks[i].hook.length; j++) { + this.hooks[i].hook[j].remove(); + } + + this.hooks[i].name[0] && this.hooks[i].name[0].remove(); + this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); + this.hooks.splice(i, 1); - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - remove: function (item) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].item.gid === item.gid) { - for (let j = 0; j < this.hooks[i].hook.length; j++) { - this.hooks[i].hook[j].remove(); + return true; } - - this.hooks[i].name[0] && this.hooks[i].name[0].remove(); - this.hooks[i].vector[0] && this.hooks[i].vector[0].remove(); - this.hooks.splice(i, 1); - - return true; } - } - return false; - }, + return false; + }, - flush: function () { - while (this.hooks.length) { - for (let j = 0; j < this.hooks[0].hook.length; j++) { - this.hooks[0].hook[j].remove(); - } + flush: function () { + while (this.hooks.length) { + for (let j = 0; j < this.hooks[0].hook.length; j++) { + this.hooks[0].hook[j].remove(); + } - this.hooks[0].name[0] && this.hooks[0].name[0].remove(); - this.hooks[0].vector[0] && this.hooks[0].vector[0].remove(); - this.hooks.shift(); + this.hooks[0].name[0] && this.hooks[0].name[0].remove(); + this.hooks[0].vector[0] && this.hooks[0].vector[0].remove(); + this.hooks.shift(); + } } - } -}; - -/** - * Unique Items - */ -ItemHooks.codeById.set(sdk.items.BattleAxe, "The Chieftain"); -ItemHooks.codeById.set(sdk.items.Falchion, "Gleamscythe"); -ItemHooks.codeById.set(sdk.items.BurntWand, "Suicide Branch"); -ItemHooks.codeById.set(sdk.items.PetrifiedWand, "Carin Shard"); -ItemHooks.codeById.set(sdk.items.TombWand, "King Leoric's Arm"); -ItemHooks.codeById.set(sdk.items.Quarterstaff, "Ribcracker"); -ItemHooks.codeById.set(sdk.items.EdgeBow, "Skystrike"); -ItemHooks.codeById.set(sdk.items.GreaterTalons, "Bartuc's"); -ItemHooks.codeById.set(sdk.items.WristSword, "Jade Talon"); -ItemHooks.codeById.set(sdk.items.BattleCestus, "Shadow Killer"); -ItemHooks.codeById.set(sdk.items.FeralClaws, "Firelizard's"); -ItemHooks.codeById.set(sdk.items.EttinAxe, "Rune Master"); -ItemHooks.codeById.set(sdk.items.LichWand, "Boneshade"); -ItemHooks.codeById.set(sdk.items.UnearthedWand, "Death's Web"); -ItemHooks.codeById.set(sdk.items.FlyingAxe, "Gimmershred"); -ItemHooks.codeById.set(sdk.items.WingedKnife, "Warshrike"); -ItemHooks.codeById.set(sdk.items.WingedAxe, "Lacerator"); -ItemHooks.codeById.set(sdk.items.Thresher, "Reaper's Toll"); -ItemHooks.codeById.set(sdk.items.CrypticAxe, "Tomb Reaver"); -ItemHooks.codeById.set(sdk.items.GiantThresher, "Stormspire"); -ItemHooks.codeById.set(sdk.items.ArchonStaff, "Mang Song's"); -ItemHooks.codeById.set(sdk.items.CrusaderBow, "Eaglehorn"); -ItemHooks.codeById.set(sdk.items.WardBow, "Ward Bow"); -ItemHooks.codeById.set(sdk.items.HydraBow, "Windforce"); -ItemHooks.codeById.set(sdk.items.CeremonialBow, "Lycander's Aim"); -ItemHooks.codeById.set(sdk.items.CeremonialPike, "Lycander's Pike"); -ItemHooks.codeById.set(sdk.items.CeremonialJavelin, "Titan's Revenge"); -ItemHooks.codeById.set(sdk.items.EldritchOrb, "Eschuta's"); -ItemHooks.codeById.set(sdk.items.DimensionalShard, "Death's Fathom"); -ItemHooks.codeById.set(sdk.items.MatriarchalBow, "Bloodraven's"); -ItemHooks.codeById.set(sdk.items.MatriarchalSpear, "Stoneraven"); -ItemHooks.codeById.set(sdk.items.MatriarchalJavelin, "Thunder Stroke"); -ItemHooks.codeById.set(sdk.items.LightPlatedBoots, "Goblin Toe"); -ItemHooks.codeById.set(sdk.items.Sallet, "Rockstopper"); -ItemHooks.codeById.set(sdk.items.GhostArmor, "Spirit Shroud"); -ItemHooks.codeById.set(sdk.items.SerpentskinArmor, "Vipermagi's"); -ItemHooks.codeById.set(sdk.items.MeshArmor, "Shaftstop"); -ItemHooks.codeById.set(sdk.items.RussetArmor, "Skullder's"); -ItemHooks.codeById.set(sdk.items.MagePlate, "Que-Hegan's"); -ItemHooks.codeById.set(sdk.items.SharkskinBoots, "Waterwalk"); -ItemHooks.codeById.set(sdk.items.DemonHead, "Andariel's Vis"); -ItemHooks.codeById.set(sdk.items.Tiara, "Kira's"); -ItemHooks.codeById.set(sdk.items.Shako, "Harlequin Crest"); -ItemHooks.codeById.set(sdk.items.WireFleece, "Gladiator's Bane"); -ItemHooks.codeById.set(sdk.items.ScarabshellBoots, "Sandstorm Trek's"); -ItemHooks.codeById.set(sdk.items.BoneweaveBoots, "Marrowwalk"); -ItemHooks.codeById.set(sdk.items.MyrmidonGreaves, "Shadow Dancer"); -ItemHooks.codeById.set(sdk.items.TotemicMask, "Jalal's"); -ItemHooks.codeById.set(sdk.items.SlayerGuard, "Arreat's Face"); -ItemHooks.codeById.set(sdk.items.GildedShield, "HoZ"); -ItemHooks.codeById.set(sdk.items.HierophantTrophy, "Homunculus"); -ItemHooks.codeById.set(sdk.items.BloodSpirit, "Cerebus"); -ItemHooks.codeById.set(sdk.items.EarthSpirit, "Spirit Keeper"); -ItemHooks.codeById.set(sdk.items.FuryVisor, "Wolfhowl"); -ItemHooks.codeById.set(sdk.items.DestroyerHelm, "Demonhorn's"); -ItemHooks.codeById.set(sdk.items.ConquerorCrown, "Halaberd's"); -ItemHooks.codeById.set(sdk.items.SacredRondache, "Alma Negra"); -ItemHooks.codeById.set(sdk.items.ZakarumShield, "Dragonscale"); -ItemHooks.codeById.set(sdk.items.BloodlordSkull, "Darkforce"); -ItemHooks.codeById.set(sdk.items.SuccubusSkull, "Boneflame"); -ItemHooks.codeById.set(sdk.items.SmallCharm, "Annihilus"); -ItemHooks.codeById.set(sdk.items.LargeCharm, "Hellfire Torch"); -ItemHooks.codeById.set(sdk.items.GrandCharm, "Gheed's"); -ItemHooks.codeById.set(sdk.items.Jewel, "Facet"); - -/** - * Misc Items - */ -ItemHooks.codeById.set(sdk.items.quest.TokenofAbsolution, "ÿc8Token"); -ItemHooks.codeById.set(sdk.items.quest.TwistedEssenceofSuffering, "ÿc3Ess-Of-Suffering"); -ItemHooks.codeById.set(sdk.items.quest.ChargedEssenceofHatred, "ÿc7Ess-Of-Hatred"); -ItemHooks.codeById.set(sdk.items.quest.BurningEssenceofTerror, "ÿc1Ess-Of-Terror"); -ItemHooks.codeById.set(sdk.items.quest.FesteringEssenceofDestruction, "ÿc3Ess-Of-Destruction"); - -/** - * Set/Unique Items - */ -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.JaggedStar, "Aldur's Wep", "Moonfall"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HuntersGuise, "Aldur's Helm"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.ShadowPlate, "Aldur's Armor", "Steel Carapace"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BattleBoots, "Aldur's Boots", "War Trav's"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Caduceus, "Griswold's Wep", "Moonfall"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Corona, "Griswold's Helm", "Crown of Ages"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.OrnatePlate, "Griswold's Armor", "Corpsemourn"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.VortexShield, "Griswold's Shield"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.OgreMaul, "IK Maul", "Windhammer"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.AvengerGuard, "IK Helm"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SacredArmor, "IK Armor"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.WarGauntlets, "IK Gloves", "HellMouth"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.WarBoots, "IK Boots", "Gore Rider"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.GrandMatronBow, "Mavina's Bow"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.KrakenShell, "Mavina's Armor", "Leviathan"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Diadem, "Mavina's Helm", "Griffon's Eye"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SharkskinBelt, "Mavina's Belt", "Razortail"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BattleGauntlets, "Mavina's Gloves", "Lava Gout"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.ScissorsKatar, "Natalya's Wep"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.LoricatedMail, "Natalya's Armor"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.GrimHelm, "Natalya's Helm", "Vamp Gaze"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.MeshBoots, "Natalya's Boots", "Silkweave"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SwirlingCrystal, "Tal Orb", "Occulus"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.LacqueredPlate, "Tal Armor"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.DeathMask, "Tal Helm", "Blackhorn's"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.MeshBelt, "Tal Belt", "Gloom's Trap"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BoneVisage, "Trang Helm", "Giant Skull"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.ChaosArmor, "Trang Armor", "Black Hades"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.TrollBelt, "Trang Belt"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HeavyBracers, "Trang Gloves", "Ghoulhide"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.CantorTrophy, "Trang Shield"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.ColossusBlade, "Bul-Kathos Blade", "Grandfather"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.MythicalSword, "Bul-Kathos Sword"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.WarHat, "Cow King's Helm", "Peasant Crown"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.StuddedLeather, "Cow King's Armor", "Twitchthroe"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HeavyBoots, null, "Gorefoot"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Mace, "Heavens's Wep", "Crushflange"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SpiredHelm, "Heavens's Helm", "Nightwing's"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Cuirass, "Heavens's Armor", "Duriel's Shell"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Ward, "Heavens's Shield"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Bill, "Hwanin's Bill", "Blackleach"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.TigulatedMail, "Hwanin's Armor", "Crow Caw"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.GrandCrown, "Hwanin's Helm", "Crown of Thieves"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Belt, null, "Nightsmoke"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HellforgePlate, "Naj's Armor"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.ElderStaff, "Naj's Staff", "Ondal's Wisdom"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Circlet, "Naj's Helm", "Moonfall"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SmallShield, null, "Umbral Disk"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.WingedHelm, "G-Face", "Valk Wing"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HeavyBelt, "Orphan's Belt"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.HeavyGloves, null, "Bloodfist"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.CrypticSword, "Sazabi's Wep", "Frostwind"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BalrogSkin, "Sazabi's Armor", "Arkaine's"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BreastPlate, null, "Venom Ward"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.GothicShield, null, "The Ward"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.DuskShroud, "Disciple's Armor", "Ormus Robe's"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.MithrilCoil, "Disciple's Belt", "Verdungo's"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BrambleMitts, "Disciple's Gloves"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.DemonhideBoots, "Disciple's Boots", "Infernostride"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.RingMail, "Angelic's Armor", "Darkglow"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Sabre, "Angelic's Wep", "Krintiz"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SkullCap, "Arcanna's Helm", "Tarnhelm"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.LightPlate, "Arcanna's Armor", "Heavenly Garb"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.WarStaff, "Arcanna's Staff", "Iron Jang Bong"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.LightGauntlets, "Artic's Gloves", "Magefist"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.LightBelt, "Artic's Belt", "Snakecord"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.QuiltedArmor, "Artic's Armor", "Greyform"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.ShortWarBow, "Artic's Bow", "Hellclap"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.DoubleAxe, "Beserker's Axe", "Bladebone"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.SplintMail, "Beserker's Armor", "Iceblink"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Helm, "Beserker's Helm", "Coif of Glory"); - -/** - * Tancred's - */ -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.BoneHelm, "Tancred's Skull", "Wormskull"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.FullPlateMail, "Tancred's Spine", "Goldskin"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.MilitaryPick, "Tancred's Crowbill", "Skull Splitter"); -ItemHooks.addToCodeByClassIdAndQuality(sdk.items.Boots, "Tancred's Hobnails", "Hotspur"); - -/** - * @todo the rest of the sets/uniques - */ - -ItemHooks.itemColorCode[sdk.items.quality.Magic] = { color: 0x97, code: "ÿc3" }; -ItemHooks.itemColorCode[sdk.items.quality.Set] = { color: 0x84, code: "ÿc2" }; -ItemHooks.itemColorCode[sdk.items.quality.Rare] = { color: 0x6F, code: "ÿc9" }; -ItemHooks.itemColorCode[sdk.items.quality.Unique] = { color: 0xA8, code: "ÿc4" }; + }; +})(); diff --git a/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js b/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js index 195a3193e..9721c415e 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/MonsterHooks.js @@ -6,41 +6,14 @@ * */ -const MonsterHooks = { - hooks: [], - enabled: true, - - check: function () { - if (!this.enabled || me.inTown) { - this.flush(); - - return; - } - - for (let i = 0; i < this.hooks.length; i += 1) { - if (!copyUnit(this.hooks[i].unit).x) { - this.hooks[i].hook.remove(); - this.hooks.splice(i, 1); - - i -= 1; - } - } - - let unit = Game.getMonster(); - - if (unit) { - do { - if (unit.attackable) { - !this.getHook(unit) ? this.add(unit) : this.updateCoords(unit); - } else { - this.remove(unit); - } - } while (unit.getNext()); - } - }, - - // credit DetectiveSquirrel from his maphack https://github.com/DetectiveSquirrel/Kolbot-MapThread/blob/9c721a72a934518cfca1d1a05211b5e03b5b624f/kolbot/threads/MapThread.js#L2353 - specTypeColor: function (unit) { +const MonsterHooks = (function () { + /** + * @author DetectiveSquirrel from his maphack + * https://github.com/DetectiveSquirrel/Kolbot-MapThread/blob/9c721a72a934518cfca1d1a05211b5e03b5b624f/kolbot/threads/MapThread.js#L2353 + * @param {Monster} unit + * @returns {number} + */ + function specTypeColor (unit) { switch (unit.spectype) { case sdk.monsters.spectype.Minion: return 3; @@ -53,55 +26,89 @@ const MonsterHooks = { default: return 8; } - }, - - add: function (unit) { - this.hooks.push({ - unit: copyUnit(unit), - hook: new Text((unit.spectype & 0xF ? "O" : "X"), unit.x, unit.y, this.specTypeColor(unit), 1, null, true) - }); - }, - - updateCoords: function (unit) { - let hook = this.getHook(unit); + } + + /** + * @constructor + * @param {Monster} unit + */ + function MonsterHook (unit) { + this.unit = copyUnit(unit); + this.hook = new Text((unit.spectype & 0xF ? "O" : "X"), unit.x, unit.y, specTypeColor(unit), 1, null, true); + } - if (!hook) { - return false; + MonsterHook.prototype.update = function () { + if (!this.unit || !this.unit.x || !this.unit.attackable) { + this.hook.remove(); + return; } + this.hook.x = this.unit.x; + this.hook.y = this.unit.y; + }; + + return { + /** @type {Object.} */ + hooks: {}, + enabled: true, + + check: function () { + if (!this.enabled || me.inTown) { + this.flush(); + + return; + } - hook.x = unit.x; - hook.y = unit.y; - - return true; - }, + for (let m in this.hooks) { + if (!this.hooks.hasOwnProperty(m)) { + continue; + } - getHook: function (unit) { - for (let i = 0; i < this.hooks.length; i += 1) { - if (this.hooks[i].unit.gid === unit.gid) { - return this.hooks[i].hook; + if (!copyUnit(this.hooks[m].unit).x) { + this.hooks[m].hook.remove(); + delete this.hooks[m]; + } } - } - return false; - }, + let unit = Game.getMonster(); + + if (unit) { + do { + if (unit.attackable) { + if (!this.hooks[unit.gid]) { + this.hooks[unit.gid] = new MonsterHook(unit); + } else { + this.hooks[unit.gid].update(); + } + } else { + if (this.hooks[unit.gid]) { + this.hooks[unit.gid].hook.remove(); + delete this.hooks[unit.gid]; + } + } + } while (unit.getNext()); + } + }, - remove: function (unit) { - for (let i = 0; i < this.hooks.length; i += 1) { - if (this.hooks[i].unit.gid === unit.gid) { - this.hooks[i].hook.remove(); - this.hooks.splice(i, 1); + /** @param {Monster} unit */ + remove: function (unit) { + if (this.hooks.hasOwnProperty(unit.gid)) { + this.hooks[unit.gid].hook.remove(); + delete this.hooks[unit.gid]; return true; } - } - return false; - }, + return false; + }, - flush: function () { - while (this.hooks.length) { - this.hooks[0].hook.remove(); - this.hooks.shift(); + flush: function () { + for (let m in this.hooks) { + if (!this.hooks.hasOwnProperty(m)) { + continue; + } + this.hooks[m].hook.remove(); + delete this.hooks[m]; + } } - } -}; + }; +})(); diff --git a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js index a293a6a4e..e3ce724ad 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js @@ -11,7 +11,7 @@ const VectorHooks = { lastLoc: { x: 0, y: 0 }, names: [], hooks: [], - nextAreas: (function() { + nextAreas: (function () { let nextAreas = []; // Specific area override From 94b365b4b474ce052d0b00a1fb87dc702da529da Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 Sep 2023 00:58:10 -0400 Subject: [PATCH 308/758] Update ItemHooks.js - missed deleting a this reference --- d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js index a8afee658..4e2f125c5 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js @@ -330,7 +330,7 @@ const ItemHooks = (function () { break; case sdk.items.quality.Set: case sdk.items.quality.Unique: - ({ color, code } = this.itemColorCode[item.quality]); + ({ color, code } = itemColorCode[item.quality]); if (codeById.has(item.classid)) { code += codeById.get(item.classid); @@ -355,7 +355,7 @@ const ItemHooks = (function () { case sdk.items.quality.Magic: case sdk.items.quality.Rare: if (item.name) { - ({ color, code } = this.itemColorCode[item.quality]); + ({ color, code } = itemColorCode[item.quality]); code += (item.sockets > 0 ? "[" + item.sockets + "]" : ""); code += this.getName(item); From f41921ccb2601c76c7f710f0c63e769f93511cd5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:31:44 -0400 Subject: [PATCH 309/758] Update ControlBot.js - define custom meph function instead of importing it from autorush, to many spots are different so this is cleaner --- d2bs/kolbot/libs/scripts/ControlBot.js | 46 +++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 1ede07e01..e034ddc93 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -10,6 +10,8 @@ function ControlBot () { // Quests const { + log, + playerIn, andariel, cube, radament, @@ -22,7 +24,7 @@ function ControlBot () { heart, eye, travincal, - mephisto, + // mephisto, izual, diablo, shenk, @@ -36,6 +38,48 @@ function ControlBot () { } = require("../systems/autorush/RushConfig"); const Worker = require("../modules/Worker"); + /** @param {string} [nick] */ + const mephisto = function (nick) { + log("starting mephisto"); + + Town.doChores(); + Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); + if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true)) { + throw new Error("Failed to move to durance 3"); + } + Pather.moveTo(17617, 8069); + Attack.securePosition(me.x, me.y, 30, 3000); + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 20, 3000); + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); + + if (hydra) { + do { + while (!hydra.dead && hydra.hp > 0) { + delay(500); + } + } while (hydra.getNext()); + } + Pather.makePortal(); + Pather.moveTo(17581, 8070); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Pather.moveTo(17591, 8070); + Attack.kill(sdk.monsters.Mephisto); + Pickit.pickItems(); + log("meph dead"); + log(AutoRush.playersOut); + Pather.usePortal(null); + + return true; + }; + AutoRush.rushMode = RushModes.chanter; AutoRush.playersIn = "in"; AutoRush.playersOut = "out"; From 84063912a0668e0390aebab2f5da5c2347d0b552 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:33:41 -0400 Subject: [PATCH 310/758] Update AutoRush.js - small bits of cleanup: - - Cain: use stone alpha as our position to clear instead of our me x/y coordinates, and return to stone alpha before opening portal - - Meph: removed cutout portions for chant mode --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index d562562ea..9faf60799 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -85,9 +85,12 @@ Pather.usePortal(1) || Town.goToTown(); Pather.useWaypoint(sdk.areas.StonyField, true); Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); - Attack.securePosition(me.x, me.y, 40, 3000, true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); + Pather.moveToPresetMonster(sdk.areas.StonyField, sdk.monsters.preset.Rakanishu, { + offX: 10, offY: 10, pop: true + }); + const StoneAlpha = Game.getObject(sdk.quest.chest.StoneAlpha); + Attack.securePosition(StoneAlpha.x, StoneAlpha.y, 40, 3000, true); + StoneAlpha.distance > 5 && Pather.moveToUnit(StoneAlpha); Pather.makePortal(); log(AutoRush.playersIn); @@ -97,7 +100,7 @@ if (Pather.usePortal(sdk.areas.Tristram)) { break; } - Attack.securePosition(me.x, me.y, 35, 1000); + Attack.securePosition(StoneAlpha.x, StoneAlpha.y, 35, 1000); } if (me.inArea(sdk.areas.Tristram)) { @@ -109,7 +112,7 @@ throw new Error("Failed to move to Cain's Jail"); } - Attack.securePosition(gibbet.x, gibbet.y, 20, 3000); + Attack.securePosition(gibbet.x, gibbet.y, 25, 3000); Pather.makePortal(); log(AutoRush.playersIn); @@ -119,7 +122,7 @@ if (gibbet.mode) { break; } - Attack.securePosition(me.x, me.y, 10, 1000); + Attack.securePosition(me.x, me.y, 15, 1000); } } } @@ -729,7 +732,9 @@ Town.doChores(); Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true) && Pather.moveTo(17692, 8023) && Pather.makePortal(); + if (Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true) && Pather.moveTo(17692, 8023)) { + Pather.makePortal(); + } delay(2000); log(AutoRush.playersIn); @@ -746,41 +751,39 @@ Pather.moveTo(17692, 8023) && Pather.makePortal(); log(AutoRush.playersOut); - if (AutoRush.rushMode !== RushModes.chanter) { - while (playerIn(me.area, nick)) { - delay(250); - } - - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 40, 3000); + while (playerIn(me.area, nick)) { + delay(250); + } - let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 40, 3000); - if (hydra) { - do { - while (!hydra.dead && hydra.hp > 0) { - delay(500); - } - } while (hydra.getNext()); - } + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - Pather.makePortal(); - Pather.moveTo(17581, 8070); - log(AutoRush.playersIn); + if (hydra) { + do { + while (!hydra.dead && hydra.hp > 0) { + delay(500); + } + } while (hydra.getNext()); + } - while (!playerIn(me.area, nick)) { - delay(250); - } + Pather.makePortal(); + Pather.moveTo(17581, 8070); + log(AutoRush.playersIn); - log("a4"); + while (!playerIn(me.area, nick)) { + delay(250); + } - while (!playersInAct(4)) { - delay(250); - } + log("a4"); - delay(2000); - Pather.usePortal(null); + while (!playersInAct(4)) { + delay(250); } + delay(2000); + Pather.usePortal(null); + return true; }; /** @param {string} [nick] */ From d4377a9c421390c625c6a91292b6d8337d28a4c0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:42:49 -0400 Subject: [PATCH 311/758] Update Util.js - fix stack tracing for the wrapper methods --- d2bs/kolbot/libs/core/Util.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 0f574c7bb..9fba57dc3 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -345,6 +345,22 @@ return (areaNames[area] || "undefined"); }; + /** + * Utility function to fix error tracing for getPresetUnit(s) + * @param {Error} err + * @param {number} area + * @param {number} id + */ + const rewriteStack = function (err, area, id) { + let stack = err.stack.match(/[^\r\n]+/g); + let fileNameAndLine = stack[1].substring(stack[1].lastIndexOf("\\") + 1); + let [fileName, line] = fileNameAndLine.split(":"); + err.message += " Area: " + area + " Id: " + id; + err.fileName = fileName; + err.lineNumber = line; + err.stack = err.stack.split("\n").slice(1).join("\n"); + }; + const Game = { getDistance: function (...args) { switch (args.length) { @@ -463,7 +479,7 @@ try { return getPresetUnit(area, sdk.unittype.Monster, id); } catch (e) { - e.message += " Area: " + area + " Id: " + id; + rewriteStack(e, area, id); throw e; } }, @@ -477,7 +493,7 @@ try { return getPresetUnits(area, sdk.unittype.Monster, id); } catch (e) { - e.message += " Area: " + area + " Id: " + id; + rewriteStack(e, area, id); throw e; } }, @@ -491,7 +507,7 @@ try { return getPresetUnit(area, sdk.unittype.Object, id); } catch (e) { - e.message += " Area: " + area + " Id: " + id; + rewriteStack(e, area, id); throw e; } }, @@ -505,7 +521,7 @@ try { return getPresetUnits(area, sdk.unittype.Object, id); } catch (e) { - e.message += " Area: " + area + " Id: " + id; + rewriteStack(e, area, id); throw e; } }, @@ -519,7 +535,7 @@ try { return getPresetUnit(area, sdk.unittype.Stairs, id); } catch (e) { - e.message += " Area: " + area + " Id: " + id; + rewriteStack(e, area, id); throw e; } }, @@ -533,7 +549,7 @@ try { return getPresetUnits(area, sdk.unittype.Stairs, id); } catch (e) { - e.message += " Area: " + area + " Id: " + id; + rewriteStack(e, area, id); throw e; } }, @@ -600,7 +616,7 @@ getResponse && addEventListener("copydata", copyDataEvent); if (!sendCopyData(null, profileName, mode, JSON.stringify({ message: message, sender: me.profile }))) { - //print("sendToProfile: failed to get response from " + profileName); + //console.log("sendToProfile: failed to get response from " + profileName); getResponse && removeEventListener("copydata", copyDataEvent); return false; From c08ad10e144ad8d4ebdfb260cd39ee8c29ae11f6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 Sep 2023 19:29:47 -0400 Subject: [PATCH 312/758] Update Town.js - filter non-stashables from item list before iteration --- d2bs/kolbot/libs/core/Town.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index fe513924b..e69880065 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -127,7 +127,7 @@ const Town = { !me.inTown && Town.goToTown(); if (!NPC.getAct(npcName).includes(me.act)) { - Town.goToTown(NPC.getAct(npcName)[0]); + Town.goToTown(NPC.getAct(npcName).first()); } me.cancelUIFlags(); @@ -1532,9 +1532,13 @@ const Town = { me.cancelUIFlags(); - let items = Storage.Inventory.Compare(Config.Inventory); + /** @type {ItemUnit[]} */ + let items = (Storage.Inventory.Compare(Config.Inventory) || []) + .filter(function (item) { + return !Town.ignoreType(item.itemType); + }); - if (items) { + if (items && items.length) { Config.SortSettings.SortStash && Storage.Stash.SortItems(); for (let item of items) { From fd668869a54bb52b8daefa7c7bd97cc5a680c5d5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:18:10 -0400 Subject: [PATCH 313/758] Update AutoRush.js - changed action ordering, always secure position before opening portal - add some more info for chanter mode - increased clear range for eye/brain/heart - fix typo using `this` instead of `Pather` from portion copied from journeyTo function --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 76 +++++++++++-------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 9faf60799..80eca656c 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -35,11 +35,13 @@ return false; }; + const bumperLvlReq = function () { + return [20, 40, 60][me.diff]; + }; const bumperCheck = function (nick) { - let bumperLevelReq = [20, 40, 60][me.diff]; return nick - ? Misc.findPlayer(nick).level >= bumperLevelReq - : Misc.checkPartyLevel(bumperLevelReq); + ? Misc.findPlayer(nick).level >= bumperLvlReq() + : Misc.checkPartyLevel(bumperLvlReq()); }; const playersInAct = function (act) { @@ -140,8 +142,8 @@ throw new Error("andy failed"); } + Attack.securePosition(22582, 9612, 40, 3000, true); Pather.makePortal(); - Attack.securePosition(me.x, me.y, 40, 3000, true); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -290,9 +292,8 @@ || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { throw new Error("cube failed"); } - - Pather.makePortal(); Attack.securePosition(me.x, me.y, 30, 3000, true); + Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -325,8 +326,8 @@ throw new Error("amulet failed"); } + Attack.securePosition(15044, 14045, 25, 3000, me.hell, me.hell); Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); log(AutoRush.playersIn); @@ -358,8 +359,8 @@ throw new Error("staff failed"); } - Pather.makePortal(); Attack.securePosition(me.x, me.y, 30, 3000, true); + Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -390,10 +391,11 @@ Town.doChores(); Pather.useWaypoint(sdk.areas.ArcaneSanctuary, true) && Precast.doPrecast(true); - let preset = Game.getPresetObject(sdk.areas.ArcaneSanctuary, sdk.quest.chest.Journal); + const preset = Game.getPresetObject(sdk.areas.ArcaneSanctuary, sdk.quest.chest.Journal).realCoords(); + /** @type {PathNode} */ let spot = {}; - switch (preset.roomx * 5 + preset.x) { + switch (preset.x) { case 25011: spot = { x: 25081, y: 5446 }; break; @@ -401,7 +403,7 @@ spot = { x: 25830, y: 5447 }; break; case 25431: - switch (preset.roomy * 5 + preset.y) { + switch (preset.y) { case 5011: spot = { x: 25449, y: 5081 }; break; @@ -417,8 +419,8 @@ throw new Error("summoner failed"); } + Attack.securePosition(spot.x, spot.y, 25, 3000); Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -448,8 +450,8 @@ let redPortal = Game.getObject(sdk.objects.RedPortal); - if (!redPortal || !this.usePortal(null, null, redPortal)) { - if (!Misc.poll(() => { + if (!redPortal || !Pather.usePortal(null, null, redPortal)) { + if (!Misc.poll(function () { let journal = Game.getObject(sdk.quest.chest.Journal); if (journal && journal.interact()) { @@ -462,6 +464,7 @@ return (redPortal && Pather.usePortal(null, null, redPortal)); })) throw new Error("summoner failed"); } + Pather.useWaypoint(sdk.areas.LutGholein); return true; }; @@ -483,8 +486,8 @@ throw new Error("duriel failed"); } - Pather.makePortal(); Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); + Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -494,6 +497,10 @@ return false; } + AutoRush.rushMode !== RushModes.chanter + ? log(AutoRush.playersOut) + : log("Place staff in orifice then wait in town"); + if (!Misc.poll(function () { return !playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { @@ -605,8 +612,8 @@ throw new Error("brain failed"); } + Attack.securePosition(me.x, me.y, 30, 3000, me.hell, me.hell); Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); log(AutoRush.playersIn); @@ -638,8 +645,8 @@ throw new Error("eye failed"); } + Attack.securePosition(me.x, me.y, 30, 3000, me.hell, me.hell); Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); log(AutoRush.playersIn); @@ -671,8 +678,8 @@ throw new Error("heart failed"); } + Attack.securePosition(me.x, me.y, 30, 3000, me.hell, me.hell); Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); log(AutoRush.playersIn); @@ -703,8 +710,8 @@ let coords = [me.x, me.y]; Pather.moveTo(coords[0] + 23, coords[1] - 102); - Pather.makePortal(); Attack.securePosition(me.x, me.y, 40, 3000); + Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -827,15 +834,12 @@ Pather.useWaypoint(sdk.areas.CityoftheDamned, true) && Precast.doPrecast(false); Pather.moveToExit(sdk.areas.PlainsofDespair, true); - let izualPreset = Game.getPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual); - let izualCoords = { - area: sdk.areas.PlainsofDespair, - x: izualPreset.roomx * 5 + izualPreset.x, - y: izualPreset.roomy * 5 + izualPreset.y - }; + const izualPreset = Game.getPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual).realCoords(); - moveIntoPos(izualCoords, 50); - let izual = Misc.poll(() => Game.getMonster(sdk.monsters.Izual), 1500, 500); + moveIntoPos(izualPreset, 50); + let izual = Misc.poll(function () { + return Game.getMonster(sdk.monsters.Izual); + }, 1500, 500); izual ? moveIntoPos(izual, 60) : console.log("izual unit not found"); @@ -877,6 +881,7 @@ function inviteIn () { Pather.moveTo(7763, 5267) && Pather.makePortal(); + // change this spot so we don't bring diablo closer to rushees Pather.moveTo(7727, 5267); log(AutoRush.playersIn); @@ -988,8 +993,13 @@ me.cancel(); } + if (AutoRush.rushMode === RushModes.chanter) { + log("Talk to Malah to get potion then come in"); + } Pather.makePortal(); - log(AutoRush.playersIn); + if (AutoRush.rushMode !== RushModes.chanter) { + log(AutoRush.playersIn); + } if (!Misc.poll(function () { return playerIn(me.area, nick); @@ -998,11 +1008,11 @@ return false; } - Misc.poll(() => !Game.getObject(sdk.objects.FrozenAnya), 30000, 1000); - - log(AutoRush.playersOut); // Mainly for non-questers to know when to get the scroll of resistance - if (AutoRush.rushMode !== RushModes.chanter) { + Misc.poll(function () { + return !Game.getObject(sdk.objects.FrozenAnya); + }, 30000, 1000); + log(AutoRush.playersOut); // Mainly for non-questers to know when to get the scroll of resistance while (playerIn(me.area, nick)) { delay(200); } @@ -1027,7 +1037,7 @@ if (!bumperCheck(nick)) { if (AutoRush.rushMode === RushModes.chanter) { - log(nick + " you are not eligible for ancients."); + log(nick + " you are not eligible for ancients. You need to be at least level " + bumperLvlReq()); return false; } From e0682bb52230932e38861ace2778000729d69dbc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 15 Sep 2023 19:33:16 -0400 Subject: [PATCH 314/758] Update ControlBot.js - refactored rush actions - handle checking for command aliases, we know if someone says `rush ammy` then they want us to rush the amulet quest we don't need to ignore it --- d2bs/kolbot/libs/scripts/ControlBot.js | 185 +++++++++---------------- 1 file changed, 63 insertions(+), 122 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index e034ddc93..9853c8c7f 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -741,6 +741,9 @@ function ControlBot () { if (msg.match(/^rush /gi)) { msg = msg.split(" ")[1]; } + if (commandAliases.has(msg)) { + msg = commandAliases.get(msg); + } if (!actions.has(msg)) return; if (shitList.has(nick)) { Chat.say("No commands for the shitlisted."); @@ -789,10 +792,25 @@ function ControlBot () { * @property {string} desc * @property {boolean} hostileCheck * @property {boolean} [complete] + * @property {function(): void} [markAsComplete] * @property {function(): boolean | void} run */ /** @type {Map boolean} run + */ + function RushAction (desc, run) { + this.desc = desc; + this.hostileCheck = true; + this.complete = false; + this.run = run; + } + RushAction.prototype.markAsComplete = function () { + this.complete = true; + }; /** @type {Map _actions.set(key, { - desc: "Give enchant", - hostileCheck: false, - run: enchant - })); - _actions.get("enchant").desc = ""; + _actions.set("chant", { + desc: "Give enchant", + hostileCheck: false, + run: enchant + }); } else { Config.ControlBot.Chant.AutoEnchant = false; } @@ -889,162 +905,84 @@ function ControlBot () { if (Config.ControlBot.Rush) { if (Config.ControlBot.Rush.Andy) { - _actions.set("andy", { - desc: "Rush Andariel", - hostileCheck: true, - complete: false, - run: andariel - }); + _actions.set("andy", new RushAction("Rush Andariel", andariel)); } if (Config.ControlBot.Rush.Cube) { - _actions.set("cube", { - desc: "Rush Cube", - hostileCheck: true, - complete: false, - run: cube - }); + _actions.set("cube", new RushAction("Rush Cube", cube)); } if (Config.ControlBot.Rush.Radament) { - _actions.set("rada", { - desc: "Rush Radament", - hostileCheck: true, - complete: false, - run: radament - }); + _actions.set("rada", new RushAction("Rush Radament", radament)); } if (Config.ControlBot.Rush.Staff) { - _actions.set("staff", { - desc: "Rush Staff", - hostileCheck: true, - complete: false, - run: staff - }); + _actions.set("staff", new RushAction("Rush Staff", staff)); } if (Config.ControlBot.Rush.Amulet) { - _actions.set("amu", { - desc: "Rush Amulet", - hostileCheck: true, - complete: false, - run: amulet - }); + _actions.set("amu", new RushAction("Rush Amulet", amulet)); } if (Config.ControlBot.Rush.Summoner) { - _actions.set("summoner", { - desc: "Rush Summoner", - hostileCheck: true, - complete: false, - run: summoner - }); + _actions.set("summoner", new RushAction("Rush Summoner", summoner)); } if (Config.ControlBot.Rush.Duriel) { - _actions.set("duri", { - desc: "Rush Duriel", - hostileCheck: true, - complete: false, - run: duriel - }); + _actions.set("duri", new RushAction("Rush Duriel", duriel)); } if (Config.ControlBot.Rush.LamEsen) { - _actions.set("lamesen", { - desc: "Rush Lamesen", - hostileCheck: true, - complete: false, - run: lamesen - }); + _actions.set("lamesen", new RushAction("Rush Lamesen", lamesen)); } if (Config.ControlBot.Rush.Eye) { - _actions.set("eye", { - desc: "Rush eye", - hostileCheck: true, - complete: false, - run: eye - }); + _actions.set("eye", new RushAction("Rush Eye", eye)); } if (Config.ControlBot.Rush.Brain) { - _actions.set("brain", { - desc: "Rush brain", - hostileCheck: true, - complete: false, - run: brain - }); + _actions.set("brain", new RushAction("Rush Brain", brain)); } if (Config.ControlBot.Rush.Heart) { - _actions.set("heart", { - desc: "Rush heart", - hostileCheck: true, - complete: false, - run: heart - }); + _actions.set("heart", new RushAction("Rush Heart", heart)); } if (Config.ControlBot.Rush.Travincal) { - _actions.set("trav", { - desc: "Rush Travincal", - hostileCheck: true, - complete: false, - run: travincal - }); + _actions.set("trav", new RushAction("Rush Travincal", travincal)); } if (Config.ControlBot.Rush.Mephisto) { - _actions.set("meph", { - desc: "Rush Mephisto", - hostileCheck: true, - complete: false, - run: mephisto - }); + _actions.set("meph", new RushAction("Rush Mephisto", mephisto)); } if (Config.ControlBot.Rush.Izual) { - _actions.set("izzy", { - desc: "Rush Izual", - hostileCheck: true, - complete: false, - run: izual - }); + _actions.set("izzy", new RushAction("Rush Izual", izual)); } if (Config.ControlBot.Rush.Diablo) { - _actions.set("diablo", { - desc: "Rush Diablo", - hostileCheck: true, - complete: false, - run: diablo - }); + _actions.set("diablo", new RushAction("Rush Diablo", diablo)); } if (Config.ControlBot.Rush.Shenk) { - _actions.set("shenk", { - desc: "Rush Shenk", - hostileCheck: true, - complete: false, - run: shenk - }); + _actions.set("shenk", new RushAction("Rush Shenk", shenk)); } if (Config.ControlBot.Rush.Anya) { - _actions.set("anya", { - desc: "Rush Anya", - hostileCheck: true, - complete: false, - run: anya - }); + _actions.set("anya", new RushAction("Rush Anya", anya)); } if (Config.ControlBot.Rush.Ancients) { - _actions.set("ancients", { - desc: "Rush Ancients", - hostileCheck: true, - complete: false, - run: ancients - }); + _actions.set("ancients", new RushAction("Rush Ancients", ancients)); } if (Config.ControlBot.Rush.Baal) { - _actions.set("baal", { - desc: "Rush Baal", - hostileCheck: true, - complete: false, - run: baal - }); + _actions.set("baal", new RushAction("Rush Baal", baal)); } } return _actions; })(); + /** @type {Map} */ + const commandAliases = new Map([ + ["andariel", "andy"], + ["radament", "rada"], + ["amulet", "amu"], + ["ammy", "amu"], + ["duriel", "duri"], + ["dury", "duri"], + ["tome", "lamesen"], + ["travincal", "trav"], + ["mephisto", "meph"], + ["izual", "izzy"], + ["bome", "bo"], + ["time", "timeleft"], + ["enchant", "chant"], + ]); + /** @param {[string, string]} command */ const runAction = function (command) { if (!command || command.length < 2) return false; @@ -1053,6 +991,9 @@ function ControlBot () { if (cmd.match(/^rush /gi)) { cmd = cmd.split(" ")[1]; } + if (commandAliases.has(cmd.toLowerCase())) { + cmd = commandAliases.get(cmd.toLowerCase()); + } if (!actions.has(cmd.toLowerCase())) return false; let action = actions.get(cmd.toLowerCase()); @@ -1102,7 +1043,7 @@ function ControlBot () { // check if command was for rush, if so we need to remove that as an option since its now completed if (actions.get(running.command).desc.includes("Rush")) { console.log("Disabling " + running.command + " from actions"); - actions.get(running.command).complete = true; + lastAction.markAsComplete(); } } } From 551ddf8985407d6d81b21ab579c899434ace1584 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 16 Sep 2023 18:33:05 -0400 Subject: [PATCH 315/758] Update AutoRush.js - ensure we actually kill Ismail --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 80eca656c..0bf84d9f1 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -240,14 +240,15 @@ Attack.kill(sdk.monsters.Radament); - let returnSpot = { - x: me.x, - y: me.y + let book = Game.getItem(sdk.quest.item.BookofSkill); + const returnSpot = { + x: book.x || me.x, + y: book.y || me.y }; log(AutoRush.playersOut); Pickit.pickItems(); - Attack.securePosition(me.x, me.y, 30, 3000); + Attack.securePosition(returnSpot.x, returnSpot.y, 30, 3000); if (!Misc.poll(function () { return !playerIn(me.area, nick); @@ -707,10 +708,18 @@ Town.doChores(); Pather.useWaypoint(sdk.areas.Travincal, true) && Precast.doPrecast(true); - let coords = [me.x, me.y]; + /** @type {PathNode} */ + const wpCoords = { + x: me.x, + y: me.y + }; + const portalSpot = { + x: wpCoords.x + 23, + y: wpCoords.y - 102 + }; - Pather.moveTo(coords[0] + 23, coords[1] - 102); - Attack.securePosition(me.x, me.y, 40, 3000); + Pather.move(portalSpot); + Attack.securePosition(portalSpot.x, portalSpot.y, 40, 4000); Pather.makePortal(); log(AutoRush.playersIn); @@ -721,12 +730,13 @@ return false; } - Pather.moveTo(coords[0] + 30, coords[1] - 134); - Pather.moveTo(coords[0] + 86, coords[1] - 130); - Pather.moveTo(coords[0] + 71, coords[1] - 94); - Attack.securePosition(me.x, me.y, 40, 3000); + Pather.moveTo(wpCoords.x + 30, wpCoords.y - 134); + Pather.moveTo(wpCoords.x + 86, wpCoords.y - 130); + Pather.moveTo(wpCoords.x + 71, wpCoords.y - 94); + Attack.securePosition(me.x, me.y, 40, 4000); + Attack.kill(sdk.locale.monsters.IsmailVilehand); - Pather.moveTo(coords[0] + 23, coords[1] - 102); + Pather.move(portalSpot); Pather.makePortal(); log(AutoRush.playersOut); Pather.usePortal(null, me.name); From db20bda8aafc1b56cf7f63e2f61b72dda8aa17ae Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 17 Sep 2023 13:50:55 -0400 Subject: [PATCH 316/758] Update AutoBaal.js - little bit of cleanup in chatevent - add handler if leader is muted or isn't saying the safe/baal messages. --- d2bs/kolbot/libs/scripts/AutoBaal.js | 80 +++++++++++++++++----------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index 2b1006e87..a477047b7 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -18,42 +18,55 @@ function AutoBaal () { // internal variables let baalCheck, throneCheck, hotCheck, leader; // internal variables + let hotTick = 0; const safeMsg = ["safe", "throne clear", "leechers can come", "tp is up", "1 clear"]; // safe message - casing doesn't matter const baalMsg = ["baal"]; // baal message - casing doesn't matter const hotMsg = ["hot", "warm", "dangerous", "lethal"]; // used for shrine hunt - // chat event handler function, listen to what leader says + [safeMsg, baalMsg, hotMsg].forEach((function (arr) { + for (let i = 0; i < arr.length; i++) { + arr[i] = arr[i].toLowerCase(); + } + })); + + /** + * chat event handler function, listen to what leader says + * @param {string} nick + * @param {string} msg + */ const chatEvent = function (nick, msg) { // filter leader messages - if (nick === leader) { - // loop through all predefined messages to find a match - for (let i = 0; i < hotMsg.length; i += 1) { - // leader says a hot tp message - if (msg.toLowerCase().includes(hotMsg[i].toLowerCase()) && Config.AutoBaal.FindShrine === 1) { - hotCheck = true; // safe to enter baal chamber - - break; - } + if (!nick || !msg || nick !== leader) return; + msg = msg.toLowerCase(); + + // loop through all predefined messages to find a match + for (let str of hotMsg) { + // leader says a hot tp message + if (msg.includes(str)) { + hotCheck = true; // not safe to enter baal chamber + hotTick = getTickCount(); + + return; } + } - // loop through all predefined messages to find a match - for (let i = 0; i < safeMsg.length; i += 1) { - // leader says a safe tp message - if (msg.toLowerCase().includes(safeMsg[i].toLowerCase())) { - throneCheck = true; // safe to enter throne + // loop through all predefined messages to find a match + for (let str of safeMsg) { + // leader says a safe tp message + if (msg.includes(str)) { + throneCheck = true; // safe to enter throne - break; - } + return; } + } - // loop through all predefined messages to find a match - for (let i = 0; i < baalMsg.length; i += 1) { - // leader says a baal message - if (msg.toLowerCase().includes(baalMsg[i].toLowerCase())) { - baalCheck = true; // safe to enter baal chamber + // loop through all predefined messages to find a match + for (let str of baalMsg) { + // leader says a baal message + if (msg.includes(str)) { + baalCheck = true; // safe to enter baal chamber - break; - } + return; } } }; @@ -190,17 +203,22 @@ function AutoBaal () { } } } + Town.goToTown(5); + Town.move("portalspot"); + + hotCheck = false; + } else if (getTickCount() - hotTick > Time.seconds(30)) { + // maybe we missed the message, go ahead and enter throne + if (!throneCheck && !baalCheck) { + throneCheck = true; + hotCheck = false; + } } - - Town.goToTown(5); - Town.move("portalspot"); - - hotCheck = false; } // wait for throne signal - leader's safe message if ((throneCheck || baalCheck) && me.inArea(sdk.areas.Harrogath)) { - print("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); + console.log("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); Pather.usePortal(sdk.areas.ThroneofDestruction, null); // move to a safe spot Pather.moveTo(Config.AutoBaal.LeechSpot[0], Config.AutoBaal.LeechSpot[1]); @@ -225,7 +243,7 @@ function AutoBaal () { let portal = Game.getObject(sdk.objects.WorldstonePortal); delay(2000); // wait for others to enter first - helps with curses and tentacles from spawning around you - print("ÿc4AutoBaal: ÿc0Entering chamber."); + console.log("ÿc4AutoBaal: ÿc0Entering chamber."); Pather.usePortal(null, null, portal) && Pather.moveTo(15166, 5903); // go to a safe position Town.getCorpse(); } From 679ff4384ac00df1dd35d73d441882562cb9a7a8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 17 Sep 2023 13:52:26 -0400 Subject: [PATCH 317/758] Update MFHelper.js - fix occasional bug at the start of mfhelper if last script was dia/baal where leader hadn't left chaos/throne yet and caused is to prematurely exit --- d2bs/kolbot/libs/scripts/MFHelper.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index f8bd00409..3ed405b9b 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -5,7 +5,8 @@ * */ -function MFHelper() { +function MFHelper () { + const startTime = getTickCount(); /** * @todo We should be able to handle Diablo scripts then resume MFHelper, not sure how yet but doesn't make sense to have * helper just idle if leader does any of the a5 scripts before baal. I guess could re-order them in the configs but having @@ -92,8 +93,8 @@ function MFHelper() { Town.move("portalspot"); } - // Finish MFHelper script if leader is running Diablo or Baal - if ([ + // Finish MFHelper script if leader is running Diablo or Baal AND we've been running longer than 30s + if ((getTickCount() - startTime > Time.seconds(30)) && [ sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber ].includes(player.area)) { break; From 82f360971ffab62a716595d4fa21a46cd43d19ab Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 20 Sep 2023 00:55:42 -0400 Subject: [PATCH 318/758] Update Tools.js - remove old reference to townchicken thread - remove try-finally in exit method, think it was causing some crashes - add gameready check for getting/drinking potions --- d2bs/kolbot/libs/core/Common/Tools.js | 46 +++++++++++++-------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index 07f7b0d50..33de53473 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -87,11 +87,7 @@ if (script) { if (script.running) { curr === "default.dbj" && console.log("ÿc1Pausing."); - - // don't pause townchicken during clone walk - if (curr !== "threads/townchicken.js" || !this.cloneWalked) { - script.pause(); - } + script.pause(); } else { if (curr === "default.dbj") { console.log("ÿc2Resuming."); @@ -108,7 +104,12 @@ for (let curr of this.stopScripts) { try { let script = getScript(curr); - !!script && script.running && script.stop(); + if (!!script && script.running) { + script.stop(); + while (script.running) { + delay(3); + } + } } catch (e) { console.error(e); } @@ -118,19 +119,12 @@ }; this.exit = function (chickenExit = false) { - try { - chickenExit && D2Bot.updateChickens(); - Config.LogExperience && Experience.log(); - console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); - this.stopDefault(); - quit(); - } finally { - while (me.ingame) { - delay(3); - } - } - - return true; + chickenExit && D2Bot.updateChickens(); + Config.LogExperience && Experience.log(); + // clearAllEvents(); + console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); + this.stopDefault(); + quit(); }; /** @@ -140,6 +134,7 @@ */ this.getPotion = function (pottype, type) { if (!pottype) return false; + if (!me.gameReady) return false; let items = me.getItemsEx() .filter(function (item) { @@ -174,6 +169,7 @@ */ this.drinkPotion = function (type) { if (type === undefined) return false; + if (!me.gameReady) return false; let tNow = getTickCount(); switch (type) { @@ -285,13 +281,15 @@ this.getNearestPreset = function () { let id; - let unit = getPresetUnits(me.area); + /** @type {Array} */ + let presets = getPresetUnits(me.area); let dist = 99; - for (let i = 0; i < unit.length; i += 1) { - if (getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y) < dist) { - dist = getDistance(me, unit[i].roomx * 5 + unit[i].x, unit[i].roomy * 5 + unit[i].y); - id = unit[i].type + " " + unit[i].id; + for (let unit of presets) { + let coords = unit.realCoords(); + if (getDistance(me, coords.x, coords.y) < dist) { + dist = getDistance(me, coords.x, coords.y); + id = unit.type + " " + unit.id; } } From 213db330e903a140641f7135083a347a2327f9d4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 20 Sep 2023 00:57:41 -0400 Subject: [PATCH 319/758] Update Me.js - add inShop check before attempting to drink or move potions during clearBelt method - `Town.checkScrolls` -> `me.checkScrolls`, makes more sense as a me method - add `me.maxgold` returns the maximum amount of gold we can carry - add `me.checkQuest` method, to replace `Misc.checkQuest` --- d2bs/kolbot/libs/core/Me.js | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 61d11ed8e..b7200d371 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -220,6 +220,11 @@ me.clearBelt = function () { } } while (item.getNext()); + if (clearList.length > 0 && me.inShop) { + console.debug("Currently inShop, canceling UI flags to clear belt"); + me.cancelUIFlags(); + } + while (clearList.length > 0) { let pot = clearList.shift(); (Storage.Inventory.CanFit(pot) && Storage.Inventory.MoveTo(pot)) || pot.interact(); @@ -699,6 +704,27 @@ me.getItemsForRepair = function (repairPercent, chargedItems) { return itemList; }; +/** + * @param {number} id + * @returns {number} quantity of scrolls in tome + */ +me.checkScrolls = function (id) { + let tome = me.getTome(id); + + if (!tome) { + switch (id) { + case sdk.items.TomeofIdentify: + case "ibk": + return Config.FieldID.Enabled ? 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); +}; + me.checkKeys = function () { if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { @@ -781,6 +807,12 @@ Object.defineProperties(me, { }, configurable: true }, + maxgold: { + /** max capacity (cLvl * 10000) */ + get: function () { + return me.getStat(sdk.stats.Level) * 10000; + }, + }, inShop: { get: function () { if (getUIFlag(sdk.uiflags.Shop)) return true; @@ -1041,6 +1073,12 @@ Object.defineProperties(me, { return getWaypoint(areaIndex); }; + me.checkQuest = function (questId, state) { + const quest = QuestData.get(questId); + if (!quest) return false; + return quest.checkState(state); + }; + Object.defineProperties(me, { highestAct: { get: function () { From 2787fb780b2d3e6704c64383fdedf6e1abff4c9c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 20 Sep 2023 00:59:58 -0400 Subject: [PATCH 320/758] Update Pickit.js - cleanup of the `Pickit.canPick` method - changed usage of copyUnit to use custom copyItem method --- d2bs/kolbot/libs/core/Pickit.js | 152 +++++++++++++++++--------------- 1 file changed, 81 insertions(+), 71 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index e62c524a1..7df90649d 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -111,47 +111,43 @@ const Pickit = { if (!unit) return false; if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) return false; - let tome, potion, needPots, buffers, pottype, myKey, key; - switch (unit.itemType) { case sdk.items.type.Gold: // Check current gold vs max capacity (cLvl*10000) - if (me.getStat(sdk.stats.Gold) === me.getStat(sdk.stats.Level) * 10000) { + if (me.getStat(sdk.stats.Gold) === me.maxgold) { return false; // Skip gold if full } - - break; + return true; case sdk.items.type.Scroll: - // 518 - Tome of Town Portal or 519 - Tome of Identify - tome = me.getItem(unit.classid - 11, sdk.items.mode.inStorage); - - if (tome) { + { + // 518 - Tome of Town Portal or 519 - Tome of Identify + let tome = me.getItem(unit.classid - 11, sdk.items.mode.inStorage); + // Don't pick scrolls if there's no tome + if (!tome) return false; do { - // In inventory, contains 20 scrolls - if (tome.isInInventory && tome.getStat(sdk.stats.Quantity) === 20) { - return false; // Skip a scroll if its tome is full + if (tome.isInInventory && tome.getStat(sdk.stats.Quantity) < 20) { + return true; } } while (tome.getNext()); - } else { - return false; // Don't pick scrolls if there's no tome } - - break; + // Couldn't find a tome that wasn't full. Skipping scroll + return false; case sdk.items.type.Key: - // Assassins don't ever need keys - if (me.assassin) return false; + { + // 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 + let myKey = me.getItem(sdk.items.Key, sdk.items.mode.inStorage); + let 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()); + 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: @@ -171,18 +167,28 @@ const Pickit = { case sdk.items.type.HealingPotion: case sdk.items.type.ManaPotion: case sdk.items.type.RejuvPotion: - needPots = 0; - - for (let i = 0; i < 4; i += 1) { - if (typeof unit.code === "string" && unit.code.includes(Config.BeltColumn[i])) { - needPots += this.beltSize; + { + 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 += Pickit.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; } @@ -190,37 +196,27 @@ const Pickit = { } if (needPots < 1 && this.checkBelt()) { - buffers = ["HPBuffer", "MPBuffer", "RejuvBuffer"]; - - for (let 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()); + 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) continue; + 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()); } } } @@ -230,7 +226,8 @@ const Pickit = { 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; @@ -243,6 +240,7 @@ const Pickit = { } return (needPots > 0); + } case undefined: // Yes, it does happen console.warn("undefined item (!?)"); @@ -263,11 +261,13 @@ const Pickit = { * 4 : Pickup to sell (triggered when low on gold) */ checkItem: function (unit) { - let rval = NTIP.CheckItem(unit, false, true); - const resultObj = (result, line = null) => ({ - result: result, - line: line - }); + const rval = NTIP.CheckItem(unit, false, true); + const resultObj = function (result, line = null) { + return { + result: result, + line: line + }; + }; // make sure we have essentials - no pickit files loaded if (rval.result === Pickit.Result.UNWANTED && Config.PickitFiles.length === 0 @@ -557,6 +557,16 @@ const Pickit = { 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]; + /** @param {ItemUnit} item */ + const copyItem = function (item) { + return { + gid: item.gid, + x: item.x, + y: item.y, + classid: item.classid, + itemType: item.itemType, + }; + }; // why wait for idle? while (!me.idle) { @@ -573,7 +583,7 @@ const Pickit = { } if (Pickit.pickList.some(el => el.gid === item.gid)) continue; if (item.onGroundOrDropping && item.distance <= range) { - Pickit.pickList.push(copyUnit(item)); + Pickit.pickList.push(copyItem(item)); } } while (item.getNext()); } @@ -596,7 +606,7 @@ const Pickit = { } // get the real item - const _item = Game.getItem(-1, -1, currItem.gid); + const _item = Game.getItem(currItem.classid, -1, currItem.gid); if (!_item || copyUnit(_item).x === undefined) { Pickit.pickList.shift(); From 49f6f7f5496cfae95b610abba328a812d37dd71b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 20 Sep 2023 01:02:00 -0400 Subject: [PATCH 321/758] more replacing `print` with `console.log` --- d2bs/kolbot/libs/core/Attack.js | 4 ++-- d2bs/kolbot/libs/core/Attacks/Amazon.js | 2 +- d2bs/kolbot/libs/core/Attacks/Assassin.js | 2 +- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 2 +- d2bs/kolbot/libs/core/Attacks/Druid.js | 2 +- d2bs/kolbot/libs/core/Attacks/Necromancer.js | 4 ++-- d2bs/kolbot/libs/core/Attacks/Paladin.js | 2 +- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 2 +- d2bs/kolbot/libs/core/Auto/AutoSkill.js | 8 ++++---- d2bs/kolbot/libs/core/Auto/AutoStat.js | 6 +++--- d2bs/kolbot/libs/core/CollMap.js | 4 ++-- d2bs/kolbot/libs/core/Common/Diablo.js | 8 ++++---- d2bs/kolbot/libs/core/Cubing.js | 4 ++-- d2bs/kolbot/libs/core/Prototypes.js | 5 +++-- 14 files changed, 28 insertions(+), 27 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 10c535580..4334b99c9 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -572,7 +572,7 @@ const Attack = { // Skip non-unique monsters after 15 attacks, except in Throne of Destruction if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { - print("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + console.log("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); monsterList.shift(); } @@ -1757,7 +1757,7 @@ const Attack = { } } - !!name && print("ÿc4Attackÿc0: No valid positions for: " + name); + !!name && console.log("ÿc4Attackÿc0: No valid positions for: " + name); return false; }, diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index ffd05759f..402b4fdbc 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -62,7 +62,7 @@ const ClassAttack = { let needRepair = me.needRepair(); if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { - print("towncheck"); + console.log("towncheck"); if (Town.visitTown(!!needRepair.length)) { // lost reference to the mob we were attacking diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 5d61739e3..5b14f163a 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -20,7 +20,7 @@ const ClassAttack = { let gid = unit.gid; if (Config.MercWatch && me.needMerc()) { - print("mercwatch"); + console.log("mercwatch"); if (Town.visitTown()) { if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index bb26f2351..e44b84690 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -23,7 +23,7 @@ const ClassAttack = { let needRepair = me.needRepair(); if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { - print("towncheck"); + console.log("towncheck"); if (Town.visitTown(!!needRepair.length)) { if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index da5c84e6f..04ac75f14 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -12,7 +12,7 @@ const ClassAttack = { let gid = unit.gid; if (Config.MercWatch && me.needMerc()) { - print("mercwatch"); + console.log("mercwatch"); if (Town.visitTown()) { if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { diff --git a/d2bs/kolbot/libs/core/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js index fc5ab57f9..669c5ba74 100644 --- a/d2bs/kolbot/libs/core/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -138,7 +138,7 @@ const ClassAttack = { const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; if (Config.MercWatch && me.needMerc()) { - print("mercwatch"); + console.log("mercwatch"); if (Town.visitTown()) { if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { @@ -445,7 +445,7 @@ const ClassAttack = { } } else if (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives) { if (this.checkCorpse(corpse, true)) { - print("Reviving " + corpse.name); + console.log("Reviving " + corpse.name); if (!Skill.cast(sdk.skills.Revive, sdk.skills.hand.Right, corpse)) { return false; diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index ff6faf755..9b875d01e 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -19,7 +19,7 @@ const ClassAttack = { let gid = unit.gid; if (Config.MercWatch && me.needMerc()) { - print("mercwatch"); + console.log("mercwatch"); if (Town.visitTown()) { // lost reference to the mob we were attacking diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 2591619e3..7c00bfe8a 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -64,7 +64,7 @@ const ClassAttack = { if (Config.MercWatch && me.needMerc()) { if (Town.visitTown()) { - print("mercwatch"); + console.log("mercwatch"); if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { console.debug("Lost reference to unit"); diff --git a/d2bs/kolbot/libs/core/Auto/AutoSkill.js b/d2bs/kolbot/libs/core/Auto/AutoSkill.js index 663912251..005bf6df9 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoSkill.js +++ b/d2bs/kolbot/libs/core/Auto/AutoSkill.js @@ -56,7 +56,7 @@ const AutoSkill = new function () { for (let i = 0; i < inputArray.length; i += 1) { // limit maximum allocation count to 20 if (inputArray[i][1] > 20) { - print( + console.log( "AutoSkill: Skill build index " + i + " has allocation count of " + inputArray[i][1] + " and it will be limited to 20" ); @@ -108,7 +108,7 @@ const AutoSkill = new function () { let addTo = this.skillToAdd(this.skillBuildOrder); if (addTo) { - print("AutoSkill: Using skill point in Skill: " + getSkillById(addTo) + " ID: " + addTo); + console.log("AutoSkill: Using skill point in Skill: " + getSkillById(addTo) + " ID: " + addTo); delay(100); useSkillPoint(addTo, 1); } @@ -133,7 +133,7 @@ const AutoSkill = new function () { this.save = save; if (!this.skillBuildOrder || !this.skillBuildOrder.length) { - print("AutoSkill: No build array specified"); + console.log("AutoSkill: No build array specified"); return false; } @@ -152,7 +152,7 @@ const AutoSkill = new function () { } } - print("AutoSkill: Finished allocating skill points"); + console.log("AutoSkill: Finished allocating skill points"); return true; }; diff --git a/d2bs/kolbot/libs/core/Auto/AutoStat.js b/d2bs/kolbot/libs/core/Auto/AutoStat.js index 5e4a8cdea..ae4ea2e80 100644 --- a/d2bs/kolbot/libs/core/Auto/AutoStat.js +++ b/d2bs/kolbot/libs/core/Auto/AutoStat.js @@ -599,7 +599,7 @@ const AutoStat = new function () { while (getTickCount() - tick < 3000) { if (currStat > me.getStat(sdk.stats.StatPts)) { - print( + console.log( "AutoStat: Using " + (currStat - me.getStat(sdk.stats.StatPts)) + " stat points in " + statIDToString[type] ); @@ -723,7 +723,7 @@ const AutoStat = new function () { this.bulkStat = bulkStat; if (!this.statBuildOrder || !this.statBuildOrder.length) { - print("AutoStat: No build array specified"); + console.log("AutoStat: No build array specified"); return false; } @@ -742,7 +742,7 @@ const AutoStat = new function () { } } - print("AutoStat: Finished allocating stat points"); + console.log("AutoStat: Finished allocating stat points"); return true; }; diff --git a/d2bs/kolbot/libs/core/CollMap.js b/d2bs/kolbot/libs/core/CollMap.js index deafbdc90..07addd40c 100644 --- a/d2bs/kolbot/libs/core/CollMap.js +++ b/d2bs/kolbot/libs/core/CollMap.js @@ -163,7 +163,7 @@ const CollMap = new function () { do { if (retry > 30) { - print("failed to get valid coordinate"); + console.log("failed to get valid coordinate"); coordX = cX; coordY = cY; @@ -181,7 +181,7 @@ const CollMap = new function () { retry++; } while (getCollision(me.area, coordX, coordY) & 1); - // print("Move " + retry + " from (" + cX + ", " + cY + ") to (" + coordX + ", " + coordY + ")"); + // console.log("Move " + retry + " from (" + cX + ", " + cY + ") to (" + coordX + ", " + coordY + ")"); return { x: coordX, y: coordY }; }; }; diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 4d5626be7..569aedf07 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -183,7 +183,7 @@ * @param {boolean} openSeals */ runSeals: function (sealOrder, openSeals = true, recheck = false) { - print("seal order: " + sealOrder); + console.log("seal order: " + sealOrder); Common.Diablo.sealOrder = sealOrder; let seals = { 1: () => this.vizierSeal(openSeals), @@ -316,7 +316,7 @@ * @returns {boolean} */ vizierSeal: function (openSeal = true) { - print("Viz layout " + Common.Diablo.vizLayout); + console.log("Viz layout " + Common.Diablo.vizLayout); let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); let distCheck = path.last(); @@ -360,7 +360,7 @@ * @returns {boolean} */ seisSeal: function (openSeal = true) { - print("Seis layout " + Common.Diablo.seisLayout); + console.log("Seis layout " + Common.Diablo.seisLayout); let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); let distCheck = path.last(); @@ -407,7 +407,7 @@ */ infectorSeal: function (openSeal = true) { Precast.doPrecast(true); - print("Inf layout " + Common.Diablo.infLayout); + console.log("Inf layout " + Common.Diablo.infLayout); let path = (Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); let distCheck = path.last(); diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 6e2886468..7a25f32bd 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -357,7 +357,7 @@ const Cubing = { init: function () { if (!Config.Cubing) return; - // print("We have " + Config.Recipes.length + " cubing recipe(s)."); + // console.log("We have " + Config.Recipes.length + " cubing recipe(s)."); for (let i = 0; i < Config.Recipes.length; i += 1) { if (Config.Recipes[i].length > 1 && isNaN(Config.Recipes[i][1])) { @@ -1078,7 +1078,7 @@ const Cubing = { transmute(); delay(700 + me.ping); - print("ÿc4Cubing: " + string); + console.log("ÿc4Cubing: " + string); Config.ShowCubingInfo && D2Bot.printToConsole(string, sdk.colors.D2Bot.Green); this.update(); diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 469ed73e9..e29749bbc 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -2083,7 +2083,7 @@ Unit.prototype.equip = function (destLocation) { currentEquiped.forEach(function (item, index) { // Last item, so swap instead of putting off first if (index === (currentEquiped.length - 1)) { - print("swap " + _self.name + " for " + item.name); + console.log("swap " + _self.name + " for " + item.name); let oldLoc = { x: _self.x, y: _self.y, location: _self.location }; clickItemAndWait(sdk.clicktypes.click.item.Left, _self); // Pick up current item clickItemAndWait(sdk.clicktypes.click.item.Left, destLocation.first()); // the swap of items @@ -2101,7 +2101,7 @@ Unit.prototype.equip = function (destLocation) { return; } - print("Unequip item first " + item.name); + console.log("Unequip item first " + item.name); // Incase multiple items are equipped let spot = findspot(item); // Find a spot for the current item @@ -2422,6 +2422,7 @@ Object.defineProperty(Object.prototype, "has", { PresetUnit.prototype.realCoords = function () { return { + id: this.id, area: this.level, // for some reason, preset units names the area "level" x: this.roomx * 5 + this.x, y: this.roomy * 5 + this.y, From cb1c30860282591e2dcd903d929b7c5f28e59966 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 20 Sep 2023 01:02:37 -0400 Subject: [PATCH 322/758] Update Town.js - mark `Town.checkScrolls` as depreciated --- d2bs/kolbot/libs/core/Town.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index e69880065..9fdea0ec3 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -52,8 +52,6 @@ const Town = { * @param {boolean} repair */ doChores: function (repair = false) { - delay(250); - console.info(true, null, "doChores"); /** @@ -642,24 +640,12 @@ const Town = { }, /** + * @deprecated use `me.checkScrolls` instead * @param {number} id * @returns {number} quantity of scrolls in tome */ checkScrolls: function (id) { - let tome = me.getTome(id); - - if (!tome) { - switch (id) { - case sdk.items.TomeofIdentify: - case "ibk": - return Config.FieldID.Enabled ? 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); + return me.checkScrolls(id); }, identify: function () { From 9137624e6c56f89ea21bae414f8f836a3bbd3220 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 20 Sep 2023 01:03:19 -0400 Subject: [PATCH 323/758] Update Skill.js - add check that the charge is not null/undefined, d2bs bugs a bit with charges --- d2bs/kolbot/libs/core/Skill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index bf93db111..69e753b84 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -93,7 +93,7 @@ if (!item) return; let charges = item.getStat(-2)[sdk.stats.ChargedSkill]; if (!(charges instanceof Array)) charges = [charges]; - let charge = charges.find(c => c.skill === this.skill); + let charge = charges.find(c => !!c && c.skill === this.skill); if (charge) { this.level = charge.level; this.charges = charge.charges; From c77c1854325428239f95455dc3b8e88d84be05b8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 20 Sep 2023 01:05:40 -0400 Subject: [PATCH 324/758] more replacing print statments --- d2bs/kolbot/threads/AntiHostile.js | 12 ++++++------ d2bs/kolbot/threads/ToolsThread.js | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/threads/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js index 1e4b7aadd..ae14c6ce9 100644 --- a/d2bs/kolbot/threads/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -71,7 +71,7 @@ function main () { let script = getScript("default.dbj"); if (script && script.running) { - print("ÿc1Pausing."); + console.log("ÿc1Pausing."); script.pause(); } }; @@ -81,7 +81,7 @@ function main () { let script = getScript("default.dbj"); if (script && !script.running) { - print("ÿc2Resuming."); + console.log("ÿc2Resuming."); script.resume(); } }; @@ -171,7 +171,7 @@ function main () { }; addEventListener("scriptmsg", this.scriptEvent); - print("ÿc2Anti-Hostile thread loaded."); + console.log("ÿc2Anti-Hostile thread loaded."); // Main Loop while (true) { @@ -181,7 +181,7 @@ function main () { if (hostiles.length > 0 && (Config.HostileAction === 0 || (Config.HostileAction === 1 && me.inTown))) { if (Config.TownOnHostile) { - print("ÿc1Hostility detected, going to town."); + console.log("ÿc1Hostility detected, going to town."); this.pause(); if (!me.inTown) { @@ -293,7 +293,7 @@ function main () { // Mode 1 - Quit if hostile player is nearby if (Config.HostileAction === 1) { if (Config.TownOnHostile) { - print("ÿc1Hostile player nearby, going to town."); + console.log("ÿc1Hostile player nearby, going to town."); this.pause(); if (!me.inTown) { @@ -303,7 +303,7 @@ function main () { try { Town.goToTown(); } catch (e) { - print(e + " Failed to go to town. Quitting."); + console.log(e + " Failed to go to town. Quitting."); scriptBroadcast("quit"); // quit if failed to go to town } diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index fd9925e5a..b6ad58570 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -103,7 +103,7 @@ function main () { case sdk.keys.Numpad5: // force automule check if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo")) { if (AutoMule.getMuleItems().length > 0) { - print("ÿc2Mule triggered"); + console.log("ÿc2Mule triggered"); scriptBroadcast("mule"); Common.Toolsthread.exit(); } else { @@ -266,7 +266,7 @@ function main () { Common.Toolsthread.togglePause(); Town.goToTown(); showConsole(); - print("ÿc4Diablo Walks the Earth"); + console.log("ÿc4Diablo Walks the Earth"); me.maxgametime = 0; @@ -287,19 +287,20 @@ function main () { case "toggleQuitlist": canQuit = !canQuit; - break; + return; case "quit": console.debug("Quiting"); quitFlag = true; + Common.Toolsthread.stopDefault(); - break; + return; case "reload": console.log("ÿc8ToolsThread :: " + sdk.colors.Red + "Stopping threads and waiting 5 seconds to restart"); Common.Toolsthread.stopDefault() && delay(Time.seconds(5)); console.log("Starting default.dbj"); load("default.dbj"); - break; + return; case "datadump": console.log("ÿc8Systems Data Dump: ÿc2Start"); console.log("ÿc8Cubing"); @@ -310,7 +311,7 @@ function main () { console.log("ÿc9Runeword Needed Itemsÿc0", Runewords.needList); console.log("ÿc8Systems Data Dump: ÿc1****************Info End****************"); - break; + return; // ignore common scriptBroadcast messages that aren't relevent to this thread case "mule": case "muleTorch": @@ -320,7 +321,7 @@ function main () { case "getMuleMode": case "pingquit": case "townCheck": - break; + return; default: let obj; @@ -336,8 +337,7 @@ function main () { DataFile.updateStats("debugInfo", JSON.stringify(debugInfo)); } - - break; + return; } } }; @@ -352,6 +352,7 @@ function main () { Config.QuitListMode > 0 && Common.Toolsthread.initQuitList(); !Array.isArray(Config.QuitList) && (Config.QuitList = [Config.QuitList]); // make it an array for simpler checks + // console.debug("QuitList", Config.QuitList); // Start while (true) { @@ -448,7 +449,7 @@ function main () { idleTick += rand(1200, 1500) * 1000; let timeStr = Time.format(idleTick - getTickCount()); me.overhead("Diablo Walks the Earth! - Next packet in: (" + timeStr + ")"); - print("Sent anti-idle packet, next refresh in: (" + timeStr + ")"); + console.log("Sent anti-idle packet, next refresh in: (" + timeStr + ")"); } } } From dfab27256a1ecef0283abab4bace69fcebb7033d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 21 Sep 2023 17:27:14 -0400 Subject: [PATCH 325/758] fix rushthread starting prematurely - only accept script commands signed by rusher --- d2bs/kolbot/libs/scripts/Rusher.js | 90 +++++++++++++++++++----------- d2bs/kolbot/threads/RushThread.js | 3 +- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Rusher.js b/d2bs/kolbot/libs/scripts/Rusher.js index c1c5e985d..20ba4817b 100644 --- a/d2bs/kolbot/libs/scripts/Rusher.js +++ b/d2bs/kolbot/libs/scripts/Rusher.js @@ -24,18 +24,52 @@ function Rusher () { "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" ]; - let rushThread = getScript("threads/rushthread.js"); - - const reloadThread = function () { - rushThread = getScript("threads/rushthread.js"); - rushThread && rushThread.stop(); - - delay(500); - load("threads/rushthread.js"); - - rushThread = getScript("threads/rushthread.js"); - - delay(500); + const RushThread = { + /** @type {Script} */ + _thread: null, + path: "threads/rushthread.js", + + get: function () { + if (!this._thread) { + this._thread = getScript(this.path); + } + return this._thread; + }, + /** @param {String} msg */ + send: function (msg) { + // sign the msg so we can ignore other threads' messages + this.get().send("rush-" + msg); + }, + pause: function () { + say("Pausing"); + console.log("Pausing rush thread"); + this.get().pause(); + }, + resume: function () { + say("Resuming"); + console.log("Resuming rush thread"); + this.get().resume(); + }, + start: function () { + load(this.path); + delay(500); + + while (!this.get()) { + delay(500); + } + }, + stop: function () { + this.get().stop(); + }, + reload: function () { + this.stop(); + + while (this.get().running) { + delay(3); + } + this._thread = null; + this.start(); + }, }; const getPartyAct = function () { @@ -122,22 +156,22 @@ function Rusher () { switch (getPartyAct()) { case 2: say("Party is in act 2, starting from act 2"); - rushThread.send("skiptoact 2"); + RushThread.send("skiptoact 2"); break; case 3: say("Party is in act 3, starting from act 3"); - rushThread.send("skiptoact 3"); + RushThread.send("skiptoact 3"); break; case 4: say("Party is in act 4, starting from act 4"); - rushThread.send("skiptoact 4"); + RushThread.send("skiptoact 4"); break; case 5: say("Party is in act 5, starting from act 5"); - rushThread.send("skiptoact 5"); + RushThread.send("skiptoact 5"); break; } @@ -161,12 +195,12 @@ function Rusher () { if (command) { commandSplit0 = command.split(" ")[1]; if (!!commandSplit0 && sequence.some(el => el.toLowerCase() === commandSplit0)) { - rushThread.send(command.toLowerCase()); + RushThread.send(command.toLowerCase()); } } delay(200); - rushThread.send("go"); + RushThread.send("go"); while (true) { if (commands.length > 0) { @@ -174,19 +208,11 @@ function Rusher () { switch (command) { case "pause": - if (rushThread.running) { - say("Pausing"); - - rushThread.pause(); - } + RushThread.pause(); break; case "resume": - if (!rushThread.running) { - say("Resuming"); - - rushThread.resume(); - } + RushThread.resume(); break; default: @@ -200,8 +226,8 @@ function Rusher () { if (commandSplit0.toLowerCase() === "do") { for (i = 0; i < sequence.length; i += 1) { if (command.split(" ")[1] && sequence[i].match(command.split(" ")[1], "gi")) { - this.reloadThread(); - rushThread.send(command.split(" ")[1]); + RushThread.reload(); + RushThread.send(command.split(" ")[1]); break; } @@ -212,8 +238,8 @@ function Rusher () { if (!isNaN(parseInt(command.split(" ")[1], 10)) && parseInt(command.split(" ")[1], 10) > 0 && parseInt(command.split(" ")[1], 10) <= 132) { - reloadThread(); - rushThread.send(command); + RushThread.reload(); + RushThread.send(command); } else { say("Invalid area"); } diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index 8762d7bf4..e43341172 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -1055,7 +1055,8 @@ function main () { this.scriptEvent = function (msg) { if (typeof msg === "string") { - command = msg; + if (!msg.startsWith("rush-")) return; + command = msg.substring(5); } }; From bd9aa30eb9089725da1d35cca7a2c2d37e46c67a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 22 Sep 2023 02:14:11 -0400 Subject: [PATCH 326/758] add `Storage.Inventory.IsPossibleToFit` - fix issue where we keep attempting to pick an item that isn't possible given our current inventory setup and triggering automule --- d2bs/kolbot/libs/core/Pickit.js | 3 ++ d2bs/kolbot/libs/core/Storage.js | 47 +++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 7df90649d..91e372da4 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -245,6 +245,9 @@ const Pickit = { console.warn("undefined item (!?)"); return false; + default: + // don't attempt items we are simply unable to pick up + return Storage.Inventory.IsPossibleToFit(unit); } return true; diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index 1ce2dc03b..9735672e9 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -139,6 +139,33 @@ return true; }; + /** + * @param {ItemUnit} item + */ + Container.prototype.IsPossibleToFit = function (item) { + if (!item) return false; + // only for the inventory as this has to deal with locked spots + if (this.name !== "Inventory") return true; + console.debug("Columns: " + Config.Inventory[0].length + " Rows: " + Config.Inventory.length); + for (let y = 0; y < this.width - (item.sizex - 1); y++) { + Loop: + for (let x = 0; x < this.height - (item.sizey - 1); x++) { + // If spot is locked move on + if (Config.Inventory[x][y] === 0) continue; + + // Loop the item size to make sure we can fit it in non locked spots. + for (let nx = 0; nx < item.sizey; nx++) { + for (let ny = 0; ny < item.sizex; ny++) { + if (Config.Inventory[x + nx][y + ny] === 0) continue Loop; + } + } + + return true; + } + } + return false; + }; + /** * @param {ItemUnit} item */ @@ -246,6 +273,14 @@ // Make sure it's a valid item if (!item) return false; + if (item.sizex && item.sizey && !(item instanceof Unit)) { + // 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; + } + /** * @todo review this to see why it sometimes fails when there is actually enough room */ @@ -258,16 +293,6 @@ let endX = this.width - (item.sizex - 1); let endY = this.height - (item.sizey - 1); - Storage.Reload(); - - if (item.sizex && item.sizey && !(item instanceof Unit)) { - // 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; - } - if (reverseX) { // right-to-left startX = endX - 1; endX = -1; // stops at 0 @@ -280,6 +305,8 @@ yDir = -1; } + Storage.Reload(); + //Loop buffer looking for spot to place item. for (y = startX; y !== endX; y += xDir) { Loop: From 72815c4782b47066e723e4749ed4351654a67141 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 22 Sep 2023 02:14:36 -0400 Subject: [PATCH 327/758] typedef updates --- d2bs/kolbot/sdk/globals.d.ts | 108 ++++++++++++++++++++++------- d2bs/kolbot/sdk/types/Misc.d.ts | 7 +- d2bs/kolbot/sdk/types/NPC.d.ts | 34 +++++++++ d2bs/kolbot/sdk/types/Storage.d.ts | 6 ++ d2bs/kolbot/sdk/types/Town.d.ts | 52 +++----------- 5 files changed, 136 insertions(+), 71 deletions(-) create mode 100644 d2bs/kolbot/sdk/types/NPC.d.ts diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 0411bfda6..e8804ea9d 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -527,6 +527,7 @@ declare global { readonly isAnni: boolean; readonly isTorch: boolean; readonly isGheeds: boolean; + readonly durabilityPercent: number; getColor(): number; getBodyLoc(): number[]; @@ -672,17 +673,57 @@ declare global { readonly IAS: number; readonly shapeshifted: boolean; readonly attacking: boolean; + /** + * @description max gold capacity (cLvl * 10000) + */ + readonly maxgold: number; - haveWaypoint(area: number): boolean; - accessToAct(act: number): boolean; + // d2bs functions overhead(msg: string): void; repair(): boolean; revive(): void; move(x: number, y: number): boolean; setSkill(): boolean; cancel(number?: number): boolean; - inArea(area: number): boolean; + getRepairCost(): number; + + // additions from kolbot + // #setters + walk(): void; + run(): void; switchToPrimary(): boolean; + switchWeapons(slot: 0 | 1): boolean; + + // #getters + getPingDelay(): number; + getTpTool(): ItemUnit | null; + getIdTool(): ItemUnit | null; + getTome(id: number): ItemUnit | null; + getUnids(): ItemUnit[]; + getWeaponQuantity(weaponLoc: number): number; + getItemsForRepair(repairPercent: number, chargedItems: boolean): ItemUnit[]; + castingFrames(skillId: number, fcr?: number, charClass?: number): number; + castingDuration(skillId: number, fcr?: number, charClass?: number): number; + + // #checkers? + needBeltPots(): boolean; + needBufferPots(): boolean; + needPotions(): boolean; + needHealing(): boolean; + needKeys(): boolean; + needRepair(): string[]; + needMerc(): boolean; + needStash(): boolean; + needHealing(): boolean; + // checkScrolls(id: number): number; + checkKeys(): number; + canTpToTown(): boolean; + haveWaypoint(area: number): boolean; + accessToAct(act: number): boolean; + inArea(area: number): boolean; + haveSome(arg0: { name: number; equipped: boolean; }[]): any; + findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; + findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; checkItem(itemInfo: { classid?: number; itemtype?: number; @@ -692,33 +733,17 @@ declare global { name?: string | number; equipped?: boolean | number; }): {have: boolean; item: ItemUnit | null}; - haveSome(arg0: { name: number; equipped: boolean; }[]): any; - equip(destination: number | undefined, item: ItemUnit); - getRepairCost(): number; - findItems(param: number, number?: number, number2?: number): ItemUnit[]; usingShield(): boolean; - walk(): void; - run(): void; - getPingDelay(): number; - findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; - findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; + checkQuest(questId: number, state: number): boolean; + + // #actions + cleanUpInvoPotions(beltSize?: number): boolean; + equip(destination: number | undefined, item: ItemUnit); cancelUIFlags(): boolean; - switchWeapons(slot: 0 | 1): boolean; - castingFrames(skillId: number, fcr?: number, charClass?: number): number; - castingDuration(skillId: number, fcr?: number, charClass?: number): number; - getWeaponQuantity(weaponLoc: number): number; - needPotions(): boolean; - getTpTool(): ItemUnit | null; - getIdTool(): ItemUnit | null; - canTpToTown(): boolean; - needHealing(): boolean; - getTome(id: number): ItemUnit | null; - getUnids(): ItemUnit[]; fieldID(): boolean; - switchToPrimary(): boolean; - haveWaypoint(area: number): boolean; castChargedSkill(skillId: number, target?: Unit): boolean; castChargedSkill(skillId: number, x: number, y: number): boolean; + clearBelt(): boolean; } const me: MeType @@ -821,7 +846,7 @@ declare global { readonly distance: number; getNext(): PresetUnit | false; - realCoords(): { area: number, x: number, y: number }; + realCoords(): { id: number, area: number, x: number, y: number }; } type PresetObject = { @@ -1091,6 +1116,37 @@ declare global { } const console: Console; + class File { + public readonly readable: boolean; + public readonly writeable: boolean; + public readonly seekable: boolean; + public readonly mode: number; + public readonly binaryMode: boolean; + public readonly length: number; + public readonly path: string; + public position: number; + public readonly eof: boolean; + public readonly accessed: number; + public readonly created: number; + public readonly modified: number; + public autoflush: boolean; + + public static open(filePath: string, mode?: number): File; + public static read(count: number): string; + public static read(count: number): Uint8Array; + public close(): File; + public reopen(): File; + public readLine(): string; + public readAllLines(): string[]; + public readAll(): string; + public write(...args: any[]): File; + public seek(n: number): File; + public seek(n: number, isLines: boolean, fromStart: boolean): File; + public flush(): File; + public reset(): File; + public end(): File; + } + function includeIfNotIncluded(file?: string): boolean; function includeCoreLibs(obj: { exclude: string[] }): boolean; function includeSystemLibs(): boolean; diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index eb5ea2fba..fcb623ac5 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -20,15 +20,14 @@ declare global { function scanShrines(range: any): void; function getShrine(unit: any): void; function getShrinesInArea(area: any, type: any, use: any): void; - function skipItem(id: any): void; - function shapeShift(mode: any): void; - function unShift(): void; + /** @deprecated */ function townCheck(boolean?: boolean): void; function spy(name: any): void; function errorReport(error: Error | string, script?: string): void; function debugLog(msg: any): void; function useMenu(id: number): void; function poll(check: () => T, timeout?: number, sleep?: number): T; - function getUIFlags(excluded?: []): void; + function getUIFlags(excluded?: []): number[] | null; + function getQuestStates(questId: number): number[]; } } diff --git a/d2bs/kolbot/sdk/types/NPC.d.ts b/d2bs/kolbot/sdk/types/NPC.d.ts new file mode 100644 index 000000000..1ad58603e --- /dev/null +++ b/d2bs/kolbot/sdk/types/NPC.d.ts @@ -0,0 +1,34 @@ +//@ts-nocheck +declare global { + type NPC = string; + namespace NPC { + function getAct(name: string): number[]; + const Akara: string; + const Gheed: string; + const Charsi: string; + const Kashya: string; + const Warriv: string; + const Fara: string; + const Drognan: string; + const Elzix: string; + const Greiz: string; + const Lysander: string; + const Jerhyn: string; + const Meshif: string; + const Atma: string; + const Ormus: string; + const Alkor: string; + const Hratli: string; + const Asheara: string; + const Jamella: string; + const Halbu: string; + const Tyrael: string; + const Malah: string; + const Anya: string; + const Larzuk: string; + const Qual_Kehk: string; + const Nihlathak: string; + const Cain: string; + } +} +export {}; \ No newline at end of file diff --git a/d2bs/kolbot/sdk/types/Storage.d.ts b/d2bs/kolbot/sdk/types/Storage.d.ts index ac82b3b9d..676e49e47 100644 --- a/d2bs/kolbot/sdk/types/Storage.d.ts +++ b/d2bs/kolbot/sdk/types/Storage.d.ts @@ -36,6 +36,12 @@ declare global { * A function that resets the container's buffer and item list. */ Reset(): void + + /** + * Checks whether it is possible to fit an item in inventory given available non-locked space. + * @param item + */ + IsPossibleToFit(item: ItemUnit): boolean /** * A function that checks if an item can fit in the container. diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts index 4606f79e2..3bb0508eb 100644 --- a/d2bs/kolbot/sdk/types/Town.d.ts +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -1,35 +1,5 @@ // @ts-nocheck declare global { - type NPC = string; - namespace NPC { - function getAct(name: string): number[]; - const Akara: string; - const Gheed: string; - const Charsi: string; - const Kashya: string; - const Warriv: string; - const Fara: string; - const Drognan: string; - const Elzix: string; - const Greiz: string; - const Lysander: string; - const Jerhyn: string; - const Meshif: string; - const Atma: string; - const Ormus: string; - const Alkor: string; - const Hratli: string; - const Asheara: string; - const Jamella: string; - const Halbu: string; - const Tyrael: string; - const Malah: string; - const Anya: string; - const Larzuk: string; - const Qual_Kehk: string; - const Nihlathak: string; - const Cain: string; - } namespace Town { let telekinesis: boolean; let sellTimer: number; @@ -54,7 +24,7 @@ declare global { function canTpToTown(): boolean; function initNPC(task?: string, reason?: string): boolean | NPCUnit; function heal(): boolean; - function needHealing(): boolean; + // function needHealing(): boolean; function buyPotions(): boolean; function shiftCheck(col: number, beltSize: 0 | 2 | 1 | 4 | 3): boolean; function checkColumns(beltSize: 0 | 2 | 1 | 4 | 3): [number, number, number, number]; @@ -63,8 +33,8 @@ declare global { function checkScrolls(id: number): number; function identify(): boolean; function cainID(): boolean; - function fieldID(): boolean; - function getUnids(): false | ItemUnit[]; + // function fieldID(): boolean; + // function getUnids(): false | ItemUnit[]; function identifyItem(unit: ItemUnit, tome: ItemUnit, packetID?: boolean): boolean; function shopItems(): boolean; const gambleIds: any[]; @@ -77,24 +47,24 @@ declare global { quantity: number; }; function buyKeys(): boolean; - function checkKeys(): number; - function needKeys(): boolean; - function wantKeys(): boolean; + // function checkKeys(): number; + // function needKeys(): boolean; + // function wantKeys(): boolean; function repairIngredientCheck(item: ItemUnit): boolean; function cubeRepair(): boolean; function cubeRepairItem(item: ItemUnit): boolean; function repair(force?: boolean): boolean; - function needRepair(): string[]; - function getItemsForRepair(repairPercent: number, chargedItems: boolean): ItemUnit[]; + // function needRepair(): string[]; + // function getItemsForRepair(repairPercent: number, chargedItems: boolean): ItemUnit[]; function reviveMerc(): boolean; - function needMerc(): boolean; + // function needMerc(): boolean; function canStash(item: ItemUnit): boolean; function stash(stashGold?: boolean): boolean; - function needStash(): boolean; + // function needStash(): boolean; function openStash(): boolean; function getCorpse(): boolean; function checkShard(): boolean; - function clearBelt(): boolean; + // function clearBelt(): boolean; function clearScrolls(): boolean; function clearInventory(): boolean; const act: {}[]; From 19bbba91072be286b065937bffd52072b5ad6ead Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 22 Sep 2023 02:15:25 -0400 Subject: [PATCH 328/758] Update AutoRush.js - fix case where book is undefined --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 0bf84d9f1..4e7878a7c 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -242,8 +242,8 @@ let book = Game.getItem(sdk.quest.item.BookofSkill); const returnSpot = { - x: book.x || me.x, - y: book.y || me.y + x: (book ? book.x : me.x), + y: (book ? book.y : me.y) }; log(AutoRush.playersOut); From f152c38898f27b2c97b8f0d7f534f08f9c7ca622 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:25:58 -0400 Subject: [PATCH 329/758] Update Storage.js - remove debug line --- d2bs/kolbot/libs/core/Storage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index 9735672e9..12051d28e 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -146,7 +146,6 @@ if (!item) return false; // only for the inventory as this has to deal with locked spots if (this.name !== "Inventory") return true; - console.debug("Columns: " + Config.Inventory[0].length + " Rows: " + Config.Inventory.length); for (let y = 0; y < this.width - (item.sizex - 1); y++) { Loop: for (let x = 0; x < this.height - (item.sizey - 1); x++) { From cf32cfbd6869fba5ded7155458e3a78bf560c1a3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 22 Sep 2023 18:56:18 -0400 Subject: [PATCH 330/758] Update AutoMule.js - fix dropCharm for dropping torch --- d2bs/kolbot/libs/systems/automule/AutoMule.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index b40e239ad..1ed022818 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -325,12 +325,12 @@ const AutoMule = { Town.move("stash"); if (muleObj.continuousMule) { - print("ÿc4AutoMuleÿc0: Looking for valid mule"); + console.log("ÿc4AutoMuleÿc0: Looking for valid mule"); tick = getTickCount(); while (getTickCount() - tick < timeout) { if (this.verifyMulePrefix(muleObj.charPrefix)) { - print("ÿc4AutoMuleÿc0: Found valid mule"); + console.log("ÿc4AutoMuleÿc0: Found valid mule"); begin = true; break; @@ -352,7 +352,7 @@ const AutoMule = { : this.torchAnniCheck === 1 ? " torch" : ""; - print("ÿc4AutoMuleÿc0: In" + gameType + " mule game."); + console.log("ÿc4AutoMuleÿc0: In" + gameType + " mule game."); D2Bot.updateStatus("AutoMule: In" + gameType + " mule game."); if (this.torchAnniCheck === 2) { @@ -406,7 +406,7 @@ const AutoMule = { removeEventListener("scriptmsg", muleModeEvent); removeEventListener("copydata", dropStatusEvent); - if (!muleObj.continuousMule) { + if (muleObj && !muleObj.continuousMule) { D2Bot.stop(muleObj.muleProfile, true); delay(1000); muleObj.stopProfile && D2Bot.start(muleObj.stopProfile); @@ -625,7 +625,7 @@ const AutoMule = { }, /** - * Drop Anni or Gheeds + * Drop Anni or Torch * @param {boolean} dropAnni * @returns {boolean} */ @@ -648,11 +648,11 @@ const AutoMule = { D2Bot.printToConsole("AutoMule: Transfering Anni.", sdk.colors.D2Bot.DarkGold); } else { item = items.find(function (item) { - return item.isGheeds; + return item.isTorch; }); if (!item) return false; - D2Bot.printToConsole("AutoMule: Transfering Gheeds.", sdk.colors.D2Bot.DarkGold); + D2Bot.printToConsole("AutoMule: Transfering Torch.", sdk.colors.D2Bot.DarkGold); } item.drop(); From 28b290163271d2c06ceeb842a59bd75e2830de5b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 23 Sep 2023 14:20:35 -0400 Subject: [PATCH 331/758] Add `GetFade` script - Handle letting bot go get fade before starting boss/area scripts --- d2bs/kolbot/libs/config/Amazon.js | 2 + d2bs/kolbot/libs/config/Assassin.js | 2 + d2bs/kolbot/libs/config/Barbarian.js | 2 + d2bs/kolbot/libs/config/Druid.js | 2 + d2bs/kolbot/libs/config/Necromancer.js | 2 + d2bs/kolbot/libs/config/Paladin.js | 2 + d2bs/kolbot/libs/config/Sorceress.js | 2 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 2 + d2bs/kolbot/libs/scripts/GetFade.js | 47 ++++++++++++++++++++++ 9 files changed, 63 insertions(+) create mode 100644 d2bs/kolbot/libs/scripts/GetFade.js diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 2df0072ee..7e640549e 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -33,6 +33,8 @@ function LoadConfig () { Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index a6bc9e5bb..dee2b6e56 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -33,6 +33,8 @@ function LoadConfig () { Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index ed45c92ba..6808887fc 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -33,6 +33,8 @@ function LoadConfig () { Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 59b825634..a6816908a 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -33,6 +33,8 @@ function LoadConfig () { Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index a83ede667..ce7427399 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -33,6 +33,8 @@ function LoadConfig () { Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 34b092e49..3db22bb20 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -33,6 +33,8 @@ function LoadConfig () { Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 62d9831d5..a5f116002 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -33,6 +33,8 @@ function LoadConfig () { Config.BattleOrders.SkipIfTardy = true; // Proceed with scripts if other players already moved on from BO spot Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 8fba8b4ec..8b019483d 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -20,6 +20,8 @@ Config.BoBarbHelper.Mode = -1; // 0 = give BO, -1 = disabled Config.BoBarbHelper.Wp = 35; // 35 = Catacombs level 2 + Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + // ## Team MF system Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. Scripts.MFHelper = false; // Run the same MF run as the MFLeader. Leader must have Config.MFLeader = true diff --git a/d2bs/kolbot/libs/scripts/GetFade.js b/d2bs/kolbot/libs/scripts/GetFade.js new file mode 100644 index 000000000..448d3ba8f --- /dev/null +++ b/d2bs/kolbot/libs/scripts/GetFade.js @@ -0,0 +1,47 @@ +/** +* @filename GetFade.js +* @author theBGuy +* @desc Get fade in River of Flames - only works if we are wearing an item with ctc Fade +* +*/ + +function GetFade () { + // Can't get use river if we can't access the act - TODO: use another area if we can't access river + if (!me.accessToAct(4)) return false; + // already have fade + if (me.getState(sdk.states.Fade)) return true; + + /** @type {{ have: boolean, item: ItemUnit }} */ + const fadeItem = me.findFirst([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ]); + if (!fadeItem) throw new Error("No item with ctc Fade equipped"); + + console.log("Getting fade"); + me.overhead("Getting fade"); + + Pather.useWaypoint(sdk.areas.RiverofFlame, true); + Precast.doPrecast(true); + + // check if item is on switch + let mainSlot; + + Pather.moveTo(7811, 5872); + + if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { + mainSlot = me.weaponswitch; + me.switchWeapons(sdk.player.slot.Secondary); + } + + Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + + while (!me.getState(sdk.states.Fade)) { + delay(3); + } + + mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); + + return me.getState(sdk.states.Fade); +} From 2934dc7be0f8e777a27a80360be3a10b4e8458a9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:56:09 -0400 Subject: [PATCH 332/758] Update ControlBot.js - give actual time remaining instead of static response. --- d2bs/kolbot/libs/scripts/ControlBot.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 9853c8c7f..d1040f041 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1065,7 +1065,8 @@ function ControlBot () { break; } else if (!gameEndWarningAnnounced && getTickCount() - startTime >= maxTime - Time.seconds(30)) { - Chat.say("Next game in 30 seconds."); + let remaining = Math.round((maxTime - (getTickCount() - startTime)) / 1000); + Chat.say("Next game in " + (Math.max(0, remaining)) + " seconds."); gameEndWarningAnnounced = true; } From d35cbf8e5216d525a69792d6e1d7ad1d65862f44 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 26 Oct 2023 00:54:34 -0400 Subject: [PATCH 333/758] Update AutoRush.js - fix missing check for rushmode --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 4e7878a7c..e7ac4dd03 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -1101,7 +1101,7 @@ }; /** @param {string} [nick] */ const baal = function (nick) { - if (me.hell) { + if (me.hell && AutoRush.rushMode !== RushModes.chanter) { if (!RushConfig[me.profile].config.Wps) { log("Baal not done in Hell ~Hell rush complete~"); delay(500); @@ -1112,7 +1112,7 @@ return false; } - if (!bumperCheck(nick)) { + if (AutoRush.rushMode !== RushModes.chanter && !bumperCheck(nick)) { if (!RushConfig[me.profile].config.Wps) { log("No eligible bumpers detected. ~Rush complete~"); delay(500); From a666d1ae1850e7e3b380d0d2a296e3a83afa90cf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 26 Oct 2023 00:59:14 -0400 Subject: [PATCH 334/758] Update TownChicken.js - add back missing publicmode tp check --- d2bs/kolbot/libs/modules/workers/TownChicken.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/workers/TownChicken.js b/d2bs/kolbot/libs/modules/workers/TownChicken.js index 0c702aae7..4f96ff056 100644 --- a/d2bs/kolbot/libs/modules/workers/TownChicken.js +++ b/d2bs/kolbot/libs/modules/workers/TownChicken.js @@ -305,7 +305,7 @@ throw new Error("Town.visitTown: Failed to go back from town"); } } - + Config.PublicMode && Pather.makePortal(); console.log("ÿc8End ÿc0:: ÿc8visitTown - currentArea: " + getAreaName(me.area)); return me.area === preArea; From 5781fd73f2fe1353fb253707c5eb5749784e18f3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:41:11 -0400 Subject: [PATCH 335/758] Update Skill.js - handle if `Config.UseColdArmor` is set to -1 or false for disabled --- d2bs/kolbot/libs/core/Skill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 69e753b84..3b98af390 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -161,7 +161,7 @@ if (Precast.coldArmor > 0) { Precast.skills.get(Precast.coldArmor).duration = this.getDuration(Precast.coldArmor); } - } else { + } else if (Precast.skills.has(Config.UseColdArmor)) { Precast.skills.get(Config.UseColdArmor).duration = this.getDuration(Config.UseColdArmor); } From 180d0299d3afecd4cc4a9fbba2e993835bfe3585 Mon Sep 17 00:00:00 2001 From: Georg R <39526093+icommitdesnet@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:41:36 +0100 Subject: [PATCH 336/758] Bloodraven smith quest (#392) * Add smith and bloodraven to Controlbot * fix smith and bloodraven * Add level check for smith quest * Update `Config.js` - Add the new rush props --------- Co-authored-by: theBGuy <60308670+theBGuy@users.noreply.github.com> --- d2bs/kolbot/libs/config/Amazon.js | 2 + d2bs/kolbot/libs/config/Assassin.js | 2 + d2bs/kolbot/libs/config/Barbarian.js | 2 + d2bs/kolbot/libs/config/Druid.js | 2 + d2bs/kolbot/libs/config/Necromancer.js | 2 + d2bs/kolbot/libs/config/Paladin.js | 2 + d2bs/kolbot/libs/config/Sorceress.js | 2 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 2 + d2bs/kolbot/libs/core/Config.js | 2 + d2bs/kolbot/libs/scripts/ControlBot.js | 11 ++- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 75 +++++++++++++++++++ d2bs/kolbot/sdk/types/Config.d.ts | 56 +++++++++----- 12 files changed, 142 insertions(+), 18 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 7e640549e..d7c50edef 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -250,6 +250,8 @@ function LoadConfig () { Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index dee2b6e56..cd3048bdc 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -250,6 +250,8 @@ function LoadConfig () { Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 6808887fc..af49f0dd1 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -250,6 +250,8 @@ function LoadConfig () { Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index a6816908a..62537bce1 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -250,6 +250,8 @@ function LoadConfig () { Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index ce7427399..4ee1e3146 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -250,6 +250,8 @@ function LoadConfig () { Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 3db22bb20..ec951f670 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -250,6 +250,8 @@ function LoadConfig () { Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index a5f116002..415b5968c 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -250,6 +250,8 @@ function LoadConfig () { Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 8b019483d..e137df345 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -231,6 +231,8 @@ Config.ControlBot.Wps.GiveWps = true; // Give wps on command Config.ControlBot.Wps.SecurePortal = true; // Secure wp before making portal Config.ControlBot.Rush.Andy = true; // Kill Andy on command + Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command + Config.ControlBot.Rush.Smith = true; // Kill Smith on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 0f09095b4..9489c0cfc 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -579,6 +579,8 @@ let Config = { SecurePortal: false, }, Rush: { + Bloodraven: false, + Smith: false, Andy: false, Cube: false, Radament: false, diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index d1040f041..288de487b 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -13,6 +13,8 @@ function ControlBot () { log, playerIn, andariel, + bloodraven, + smith, cube, radament, amulet, @@ -907,6 +909,12 @@ function ControlBot () { if (Config.ControlBot.Rush.Andy) { _actions.set("andy", new RushAction("Rush Andariel", andariel)); } + if (Config.ControlBot.Rush.Bloodraven) { + _actions.set("raven", new RushAction("Rush Bloodraven", bloodraven)); + } + if (Config.ControlBot.Rush.Smith) { + _actions.set("smith", new RushAction("Rush Smith", smith)); + } if (Config.ControlBot.Rush.Cube) { _actions.set("cube", new RushAction("Rush Cube", cube)); } @@ -969,6 +977,7 @@ function ControlBot () { /** @type {Map} */ const commandAliases = new Map([ ["andariel", "andy"], + ["bloodraven", "raven"], ["radament", "rada"], ["amulet", "amu"], ["ammy", "amu"], @@ -1043,7 +1052,7 @@ function ControlBot () { // check if command was for rush, if so we need to remove that as an option since its now completed if (actions.get(running.command).desc.includes("Rush")) { console.log("Disabling " + running.command + " from actions"); - lastAction.markAsComplete(); + actions.get(running.command).markAsComplete(); } } } diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index e7ac4dd03..97a91a710 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -177,6 +177,79 @@ return true; }; + + /** @param {string} [nick] */ + const bloodraven = function (nick) { + log("starting bloodraven"); + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains, true) && Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven, 30, 30)) { + throw new Error("bloodraven failed"); + } + + Attack.securePosition(me.x, me.y, 10, 1000); + Pather.makePortal(); + log(AutoRush.playersIn); + + if (!Misc.poll(function () { + if (playerIn(me.area, nick)) { + return true; + } + Pather.moveTo(22582, 9612); + return false; + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + + Attack.kill(sdk.monsters.BloodRaven); + log(AutoRush.playersOut); + Pather.moveTo(22582, 9612); + + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(250); + } + + Pather.usePortal(null, me.name); + Town.goToTown(2); + } + + return true; + }; + + /** @param {string} [nick] */ + const smith = function (nick) { + log("starting smith"); + if (Misc.findPlayer(nick).level < 8) { + log(nick + " you are not eligible for smith. You need to be at least level 8"); + + return false; + } + + Town.doChores(); + Pather.useWaypoint(sdk.areas.OuterCloister, true) && Precast.doPrecast(true); + if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { + throw new Error("smith failed"); + } + Attack.securePosition(me.x, me.y, 30, 3000, true); + Pather.makePortal(); + log(AutoRush.playersIn); + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + Pather.usePortal(null, me.name); + return true; + }; /** @param {string} [nick] */ const radament = function (nick) { log("starting radament"); @@ -1190,6 +1263,8 @@ playersInAct: playersInAct, bumperCheck: bumperCheck, andariel: andariel, + bloodraven: bloodraven, + smith: smith, cube: cube, amulet: amulet, staff: staff, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 46dc3582c..0d47c2ba6 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -399,23 +399,45 @@ declare global { export { Mode_2 as Mode }; export const Wp: number; } - namespace ControlBot { - export const Bo: boolean; - export namespace Cows_1 { - const MakeCows: boolean; - const GetLeg: boolean; - } - export { Cows_1 as Cows }; - export namespace Chant { - const Enchant: boolean; - const AutoEnchant: boolean; - } - export namespace Wps { - const GiveWps: boolean; - const SecurePortal: boolean; - } - export const EndMessage: string; - export const GameLength: number; + interface ControlBot { + Bo: boolean; + Cows: { + MakeCows: boolean; + GetLeg: boolean; + }; + Chant: { + Enchant: boolean; + AutoEnchant: boolean; + }; + Wps: { + GiveWps: boolean; + SecurePortal: boolean; + }; + Rush: { + Bloodraven: boolean; + Smith: boolean; + Andy: boolean; + Cube: boolean; + Radament: boolean; + Amulet: boolean; + Staff: boolean; + Summoner: boolean; + Duriel: boolean; + LamEsen: boolean; + Eye: boolean; + Heart: boolean; + Brain: boolean; + Travincal: boolean; + Mephisto: boolean; + Izual: boolean; + Diablo: boolean; + Shenk: boolean; + Anya: boolean; + Ancients: boolean; + Baal: boolean; + }; + EndMessage: string; + GameLength: number; } namespace IPHunter { export const IPList: any[]; From bb004ab6d877c5f7ade61c62014bc79963673642 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:18:11 -0500 Subject: [PATCH 337/758] Add missing controls for account settings - Add change password controls - Add get new password controls - Add change email controls --- d2bs/kolbot/libs/modules/Control.js | 32 ++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index 9d3f37b38..ab99ce93d 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -15,7 +15,7 @@ * @param {number} xsize * @param {number} ysize */ - function Control(type, x, y, xsize, ysize) { + function Control (type, x, y, xsize, ysize) { /** * @private * @type {number} @@ -119,6 +119,36 @@ Control.UnableToConnectOk = new Control(sdk.controls.Button, 335, 450, 128, 35); } + // Account Settings Menu Controls + { + Control.AccountSettingsLabel = new Control(sdk.controls.LabelBox, 0, 310, 800, 50); + Control.ChangePassword = new Control(sdk.controls.Button, 264, 335, 272, 35); + Control.GetNewPassword = new Control(sdk.controls.Button, 264, 420, 272, 35); + Control.ChangeEmail = new Control(sdk.controls.Button, 264, 505, 272, 35); + } + + // Change Password + { + Control.ChangePasswordAccount = new Control(sdk.controls.TextBox, 322, 342, 162, 19); + Control.ChangePasswordCurrent = new Control(sdk.controls.TextBox, 322, 396, 162, 19); + Control.ChangePasswordNew = new Control(sdk.controls.TextBox, 322, 450, 162, 19); + Control.ChangePasswordConfirm = new Control(sdk.controls.TextBox, 322, 504, 162, 19); + } + + // Get New Password + { + Control.GetNewPasswordAccount = new Control(sdk.controls.TextBox, 251, 422, 293, 19); + Control.GetNewPasswordEmail = new Control(sdk.controls.TextBox, 251, 472, 293, 19); + } + + // Change Email + { + Control.ChangeEmailAccount = new Control(sdk.controls.TextBox, 251, 397, 293, 19); + Control.ChangeEmailCurrent = new Control(sdk.controls.TextBox, 251, 447, 293, 19); + Control.ChangeEmailNew = new Control(sdk.controls.TextBox, 251, 497, 293, 19); + Control.ChangeEmailConfirm = new Control(sdk.controls.TextBox, 251, 547, 293, 19); + } + // Other Multiplayer Menu Controls { Control.OpenBattleNet = new Control(-1, 264, 310, 272, 35); From 436743bcfd5c23a45b5fa5b835328b69690ae3db Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 Dec 2023 23:26:05 -0500 Subject: [PATCH 338/758] Update D2BotMule.dbj - Fix making account on failure to login --- d2bs/kolbot/D2BotMule.dbj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index f79e67358..8896f8cf1 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -92,9 +92,7 @@ function main () { } MuleLogger.save(md5(info.realm.toLowerCase() + info.account.toLowerCase()), info.password); - if (!ControlAction.loginAccount(info)) { - Starter.makeAccount = true; - } + ControlAction.loginAccount(info); } ); locations.set(sdk.game.locations.CreateNewAccount, From f178b0b43a7cc094ddf63a9da9be1b0fc2162f99 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:38:18 -0500 Subject: [PATCH 339/758] refactored `Town.cubeRepairItem` to `Cubing.repairItem` - Add `Item.repairIngred` so the switch statement wasn't repeated - Add `Cubing.repairIngredientCheck` - Add `Cubing.doRepairs` - Makes more sense for the cube repair to be part of the Cubing namespace. It doesn't rely on being in town as someone can use their cube from their inventory --- d2bs/kolbot/libs/core/Cubing.js | 134 +++++++++++++++++++++++++++++++ d2bs/kolbot/libs/core/Item.js | 19 +++++ d2bs/kolbot/libs/core/Storage.js | 8 ++ d2bs/kolbot/libs/core/Town.js | 116 +++++--------------------- 4 files changed, 182 insertions(+), 95 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 7a25f32bd..2794e3b0c 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -1329,4 +1329,138 @@ const Cubing = { me.cancel(); me.cancel(); }, + + /** + * @todo Add chipped/flawed gems for recharging a item + * @param {ItemUnit} item - Rune + */ + repairIngredientCheck: function (item) { + if (!Config.CubeRepair) return false; + if (item.classid !== sdk.items.runes.Ral && item.classid !== sdk.items.runes.Ort) { + return false; + } + + let [have, needRal, needOrt] = [0, 0, 0]; + let items = me.getItemsForRepair(Config.RepairPercent, false); + + if (items.length) { + while (items.length > 0) { + let runeNeeded = Item.getRepairIngred(items.shift()); + + if (runeNeeded === sdk.items.runes.Ral) { + needRal += 1; + } else if (runeNeeded === sdk.items.runes.Ort) { + needOrt += 1; + } + } + } + + switch (item.classid) { + case sdk.items.runes.Ral: + needRal && (have = me.findItems(sdk.items.runes.Ral).length); + + return (!have || have < needRal); + case sdk.items.runes.Ort: + needOrt && (have = me.findItems(sdk.items.runes.Ort).length); + + return (!have || have < needOrt); + default: + return false; + } + }, + + /** + * @todo Allow cube-repairing items from stash/invo + * @todo Repair & Recharge + * @param {ItemUnit} item + * @returns {boolean} + */ + repairItem: function (item) { + if (!item || !item.isEquipped) return false; + + const neededRune = Item.repairIngred(item); + const rune = me.getItem(neededRune); + const bodyLoc = item.bodylocation; + + if (!rune || !Cubing.emptyCube()) return false; + + for (let i = 0; i < 5; i++) { + if (!rune.isInCube) { + console.log("Moving rune to cube..."); + if (!Storage.Cube.MoveTo(rune)) continue; + } + if (!item.isInCube) { + console.log("Moving item to cube..."); + Storage.Cube.MoveTo(item); + } + if (rune.isInCube && item.isInCube && Cubing.openCube()) break; + } + + if (!rune.isInCube || !item.isInCube) { + console.log("Failed to move rune or item to cube."); + // If item was equipped try reequipping it + if (bodyLoc && !item.isEquipped) { + item.isInCube && Cubing.openCube(); + item.equip(bodyLoc); + delay(me.ping * 2 + 500); + me.cancelUIFlags(); + } + return false; + } + + for (let i = 0; i < 100; i += 1) { + let cubeItems = me.findItems(-1, -1, sdk.storage.Cube); + + if (!me.itemoncursor && cubeItems.length === 2) { + console.log("Transmuting..." + i); + transmute(); + delay(1000 + me.ping); + + cubeItems = me.findItems(-1, -1, sdk.storage.Cube); + + // We expect only one item in cube + console.log("Cube contents: " + cubeItems.map(i => i.name).join(", ")); + cubeItems.length === 1 && cubeItems[0].toCursor(); + } + + if (me.itemoncursor) { + const cubeItem = Game.getCursorUnit(); + for (let i = 0; i < 3; i++) { + clickItem(sdk.clicktypes.click.item.Left, bodyLoc); + delay(me.ping * 2 + 500); + + if (cubeItem.bodylocation === bodyLoc) { + console.log(cubeItem.prettyPrint + " successfully repaired and equipped."); + D2Bot.printToConsole(cubeItem.prettyPrint + " successfully repaired and equipped.", sdk.colors.D2Bot.Green); + me.cancelUIFlags(); + + return true; + } + } + } + + delay(200); + } + + // error report is good but do we really need to stop? + Misc.errorReport("Failed to put repaired item back on."); + D2Bot.stop(); + + return false; + }, + + doRepairs: function () { + if (!Config.CubeRepair || !me.cube) return false; + + let items = me.getItemsForRepair(Config.RepairPercent, false) + .sort(function (a, b) { + return a.durabilityPercent - b.durabilityPercent; + }); + + while (items.length > 0) { + Cubing.repairItem(items.shift()); + } + + return true; + }, }; diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index 0e4b92403..cef390e22 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -55,6 +55,25 @@ const Item = { return "ÿc0"; }, + /** @param {ItemUnit} item */ + repairIngred: function (item) { + switch (item.itemType) { + case sdk.items.type.Shield: + case sdk.items.type.Armor: + case sdk.items.type.Boots: + case sdk.items.type.Gloves: + case sdk.items.type.Belt: + case sdk.items.type.VoodooHeads: + case sdk.items.type.AuricShields: + case sdk.items.type.PrimalHelm: + case sdk.items.type.Pelt: + case sdk.items.type.Circlet: + return sdk.items.runes.Ral; + default: + return sdk.items.runes.Ort; + } + }, + /** @param {ItemUnit} item */ hasTier: function (item) { return Config.AutoEquip && NTIP.GetTier(item) > 0; diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index 12051d28e..0a39309f5 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -452,6 +452,14 @@ let cItem, cube; // handle opening 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()) { + return false; + } + } + if (item.location === sdk.storage.Cube/* && this.location === sdk.storage.Stash && !Storage.Inventory.MoveTo(item) */) { if (!getUIFlag(sdk.uiflags.Cube) && !Cubing.openCube()) return false; // Cube -> Stash, must place item in inventory first diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 9fdea0ec3..a14f061b9 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -106,6 +106,7 @@ const Town = { }, /** + * @todo Only use names from the NPC object * @param {string} name * @param {boolean} cancel * @returns {boolean | Unit} @@ -401,18 +402,18 @@ const Town = { 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); - }).forEach(function (p) { - switch (p.itemType) { - case sdk.items.type.HealingPotion: - return (buffer.hp++); - case sdk.items.type.ManaPotion: - return (buffer.mp++); - } - return false; - }); + me.getItemsEx() + .filter(function (p) { + if (!p.isInInventory) return false; + return (p.itemType === sdk.items.type.HealingPotion || p.itemType === sdk.items.type.ManaPotion); + }) + .forEach(function (p) { + if (p.itemType === sdk.items.type.HealingPotion) { + buffer.hp++; + } else { + buffer.mp++; + } + }); }; // HP/MP Buffer @@ -599,13 +600,11 @@ const Town = { */ fillTome: function (classid) { if (me.gold < 450) return false; - if (Town.checkScrolls(classid) >= 13) return true; + if (me.checkScrolls(classid) >= 13) return true; let npc = Town.initNPC("Shop", "fillTome"); if (!npc) return false; - delay(500); - if (classid === sdk.items.TomeofTownPortal && !me.getTome(sdk.items.TomeofTownPortal)) { let tome = npc.getItem(sdk.items.TomeofTownPortal); @@ -701,7 +700,6 @@ const Town = { if (tpTome) { tpTome.sell(); - delay(500); } } @@ -1191,24 +1189,12 @@ const Town = { if (items.length) { while (items.length > 0) { - switch (items.shift().itemType) { - case sdk.items.type.Shield: - case sdk.items.type.Armor: - case sdk.items.type.Boots: - case sdk.items.type.Gloves: - case sdk.items.type.Belt: - case sdk.items.type.VoodooHeads: - case sdk.items.type.AuricShields: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Pelt: - case sdk.items.type.Circlet: - needRal += 1; + let runeNeeded = Item.getRepairIngred(items.shift()); - break; - default: + if (runeNeeded === sdk.items.runes.Ral) { + needRal += 1; + } else if (runeNeeded === sdk.items.runes.Ort) { needOrt += 1; - - break; } } } @@ -1236,79 +1222,19 @@ const Town = { }); while (items.length > 0) { - Town.cubeRepairItem(items.shift()); + Cubing.repairItem(items.shift()); } return true; }, /** + * @deprecated Use `Cubing.repairItem` instead * @param {ItemUnit} item * @returns {boolean} */ cubeRepairItem: function (item) { - if (!item.isInStorage) return false; - - let rune, cubeItems; - const bodyLoc = item.bodylocation; - - switch (item.itemType) { - case sdk.items.type.Shield: - case sdk.items.type.Armor: - case sdk.items.type.Boots: - case sdk.items.type.Gloves: - case sdk.items.type.Belt: - case sdk.items.type.VoodooHeads: - case sdk.items.type.AuricShields: - case sdk.items.type.PrimalHelm: - case sdk.items.type.Pelt: - case sdk.items.type.Circlet: - rune = me.getItem(sdk.items.runes.Ral); - - break; - default: - rune = me.getItem(sdk.items.runes.Ort); - - break; - } - - if (rune && Town.openStash() && Cubing.openCube() && Cubing.emptyCube()) { - for (let i = 0; i < 100; i += 1) { - if (!me.itemoncursor) { - if (Storage.Cube.MoveTo(item) && Storage.Cube.MoveTo(rune)) { - transmute(); - delay(1000 + me.ping); - } - - cubeItems = me.findItems(-1, -1, sdk.storage.Cube); // Get cube contents - - // We expect only one item in cube - cubeItems.length === 1 && cubeItems[0].toCursor(); - } - - if (me.itemoncursor) { - for (let i = 0; i < 3; i += 1) { - clickItem(sdk.clicktypes.click.item.Left, bodyLoc); - delay(me.ping * 2 + 500); - - if (cubeItems[0].bodylocation === bodyLoc) { - let cubeItem = cubeItems[0].fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<;.*]/, "").trim(); - console.log(cubeItem + " successfully repaired and equipped."); - D2Bot.console.logToConsole(cubeItem + " successfully repaired and equipped.", sdk.colors.D2Bot.Green); - - return true; - } - } - } - - delay(200); - } - - Misc.errorReport("Failed to put repaired item back on."); - D2Bot.stop(); - } - - return false; + return Cubing.repairItem(item); }, /** From a8c27cf5b80effeebac4294bb2a74eb4d911a4b8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:59:29 -0500 Subject: [PATCH 340/758] Update town methods - Change over the town cube repair methods to use the new Cubing ones - Move `Town.checkShard` -> `me.checkShard`, not entirely sure the point of this method --- d2bs/kolbot/libs/core/Me.js | 49 ++++++++++++++++++ d2bs/kolbot/libs/core/Town.js | 96 +++++------------------------------ 2 files changed, 62 insertions(+), 83 deletions(-) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index b7200d371..8e5c42910 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -740,6 +740,55 @@ me.checkKeys = function () { }, 0); }; +/** + * @todo Whats the point of this? + * @returns {boolean} + */ +me.checkShard = function () { + let shard; + let check = { left: false, right: false }; + let item = me.getItem("bld", sdk.items.mode.inStorage); + + if (item) { + do { + if (item.isInInventory && item.unique) { + shard = copyUnit(item); + + break; + } + } while (item.getNext()); + } + + if (!shard) return true; + + item = me.getItem(-1, sdk.items.mode.Equipped); + + if (item) { + do { + item.bodylocation === sdk.body.RightArm && (check.right = true); + item.bodylocation === sdk.body.LeftArm && (check.left = true); + } while (item.getNext()); + } + + if (!check.right) { + shard.toCursor(); + + while (me.itemoncursor) { + clickItem(sdk.clicktypes.click.item.Left, sdk.body.RightArm); + delay(500); + } + } else if (!check.left) { + shard.toCursor(); + + while (me.itemoncursor) { + clickItem(sdk.clicktypes.click.item.Left, sdk.body.LeftArm); + delay(500); + } + } + + return true; +}; + // Identify items while in the field if we have a id tome me.fieldID = function () { let list = me.getUnids(); diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index a14f061b9..45110fba9 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1179,53 +1179,19 @@ const Town = { }, /** + * @deprecated Use `Cubing.repairIngredientCheck` instead * @param {ItemUnit} item - Rune */ repairIngredientCheck: function (item) { - if (!Config.CubeRepair) return false; - - let [have, needRal, needOrt] = [0, 0, 0]; - let items = me.getItemsForRepair(Config.RepairPercent, false); - - if (items.length) { - while (items.length > 0) { - let runeNeeded = Item.getRepairIngred(items.shift()); - - if (runeNeeded === sdk.items.runes.Ral) { - needRal += 1; - } else if (runeNeeded === sdk.items.runes.Ort) { - needOrt += 1; - } - } - } - - switch (item.classid) { - case sdk.items.runes.Ral: - needRal && (have = me.findItems(sdk.items.runes.Ral).length); - - return (!have || have < needRal); - case sdk.items.runes.Ort: - needOrt && (have = me.findItems(sdk.items.runes.Ort).length); - - return (!have || have < needOrt); - } - - return false; + return Cubing.repairIngredientCheck(item); }, + /** + * @deprecated Use `Cubing.doRepairs` instead + * @returns {boolean} + */ cubeRepair: function () { - if (!Config.CubeRepair || !me.cube) return false; - - let items = me.getItemsForRepair(Config.RepairPercent, false) - .sort(function (a, b) { - return a.durabilityPercent - b.durabilityPercent; - }); - - while (items.length > 0) { - Cubing.repairItem(items.shift()); - } - - return true; + return Cubing.doRepairs(); }, /** @@ -1586,49 +1552,13 @@ const Town = { return true; }, + /** + * @todo Whats the point of this? + * @deprecated Use `me.checkShard` instead + * @returns {boolean} + */ checkShard: function () { - let shard; - let check = { left: false, right: false }; - let item = me.getItem("bld", sdk.items.mode.inStorage); - - if (item) { - do { - if (item.isInInventory && item.unique) { - shard = copyUnit(item); - - break; - } - } while (item.getNext()); - } - - if (!shard) return true; - - item = me.getItem(-1, sdk.items.mode.Equipped); - - if (item) { - do { - item.bodylocation === sdk.body.RightArm && (check.right = true); - item.bodylocation === sdk.body.LeftArm && (check.left = true); - } while (item.getNext()); - } - - if (!check.right) { - shard.toCursor(); - - while (me.itemoncursor) { - clickItem(sdk.clicktypes.click.item.Left, sdk.body.RightArm); - delay(500); - } - } else if (!check.left) { - shard.toCursor(); - - while (me.itemoncursor) { - clickItem(sdk.clicktypes.click.item.Left, sdk.body.LeftArm); - delay(500); - } - } - - return true; + return me.checkShard(); }, /** @deprecated Use `me.clearBelt` */ From 7849278a3697a6d86d2007e1f1f0731006562c9e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:06:53 -0500 Subject: [PATCH 341/758] Add LadderMenu controls --- d2bs/kolbot/libs/modules/Control.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index ab99ce93d..1bd19132b 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -230,6 +230,30 @@ Control.LobbyQuit = new Control(sdk.controls.Button, 693, 490, 80, 20); } + // Ladder menu controls + { + Control.StandardLadder = new Control(sdk.controls.Button, 463, 188, 272, 32); + Control.HardcoreLadder = new Control(sdk.controls.Button, 463, 238, 272, 32); + Control.ExpansionLadder = new Control(sdk.controls.Button, 463, 288, 272, 32); + Control.ExpansionHardcoreLadder = new Control(sdk.controls.Button, 463, 338, 272, 32); + Control.LadderTab = new Control(sdk.controls.LabelBox, 421, 136, 350, 50); + Control.LadderOverall = new Control(sdk.controls.LabelBox, 427, 157, 85, 29); + Control.LadderAmazon = new Control(sdk.controls.LabelBox, 513, 157, 36, 29); + Control.LadderSorceress = new Control(sdk.controls.LabelBox, 550, 157, 36, 29); + Control.LadderNecromancer = new Control(sdk.controls.LabelBox, 587, 157, 36, 29); + Control.LadderPaladin = new Control(sdk.controls.LabelBox, 624, 157, 36, 29); + Control.LadderBarbarian = new Control(sdk.controls.LabelBox, 661, 157, 36, 29); + Control.LadderDruid = new Control(sdk.controls.LabelBox, 698, 157, 36, 29); + Control.LadderAssassin = new Control(sdk.controls.LabelBox, 735, 157, 36, 29); + Control.LadderRank = new Control(sdk.controls.LabelBox, 434, 162, 217, 12); + Control.LadderName = new Control(sdk.controls.LabelBox, 468, 162, 217, 12); + Control.LadderClass = new Control(sdk.controls.LabelBox, 596, 162, 217, 12); + Control.LadderLevel = new Control(sdk.controls.LabelBox, 640, 162, 217, 12); + Control.LadderExperience = new Control(sdk.controls.LabelBox, 703, 162, 217, 12); + Control.LadderList = new Control(sdk.controls.LabelBox, 434, 391, 313, 218); + Control.LadderScrollDown = new Control(sdk.controls.ScrollBar, 756, 391, 10, 238); + } + // Join Game Menu Controls { Control.JoinGameWindow = new Control(sdk.controls.Button, 652, 469, 120, 20); From 33b5b39f4884f39312e507eacd342b00b81d714c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 15 Dec 2023 22:17:39 -0500 Subject: [PATCH 342/758] Update Pather.js - Fix getting stuck in tavern with atma (She doesn't seem to understand we want to leave) so we gotta sneak out the side door --- d2bs/kolbot/libs/core/Pather.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index cf81dd79e..2a1c9510f 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -465,6 +465,18 @@ const Pather = { } } } + } 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 9bc14090dcc86a72abca78ffd32b7b50af28fd5b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 18 Dec 2023 01:21:26 -0500 Subject: [PATCH 343/758] Update Prototypes.js - speed up opening menu when there is dialog we need to click out of --- d2bs/kolbot/libs/core/Prototypes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index e29749bbc..eede32576 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -755,6 +755,7 @@ Unit.prototype.openMenu = function (addDelay) { if ((getTickCount() - tick > 1000 && getInteractedNPC()) || (getTickCount() - tick > 500 && getIsTalkingNPC())) { me.cancel(); + break; } delay(100); From 778de1d259657ec6650250784fc3e43fac35458a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:30:11 -0500 Subject: [PATCH 344/758] Update Cubing.js - small cleanups --- d2bs/kolbot/libs/core/Cubing.js | 153 +++++++++++++++++--------------- 1 file changed, 82 insertions(+), 71 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 2794e3b0c..144323e75 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -1047,80 +1047,80 @@ const Cubing = { for (let i = 0; i < tempArray.length; i += 1) { let string = "Transmuting: "; let items = this.checkRecipe(tempArray[i]); + if (!Array.isArray(items) || !items.length) continue; - if (Array.isArray(items) && items.length) { - // 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 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; - let itemsToCubeCount = items.length; + let itemsToCubeCount = items.length; - while (items.length) { - string += (items[0].name.trim() + (items.length > 1 ? " + " : "")); - Storage.Cube.MoveTo(items[0]); - items.shift(); - } + while (items.length) { + string += (items[0].name.trim() + (items.length > 1 ? " + " : "")); + Storage.Cube.MoveTo(items[0]); + items.shift(); + } - let itemsInCube = me.getItemsEx().filter(el => el.isInCube); - if (itemsInCube.length !== itemsToCubeCount) { - console.warn("Failed to move all necesary items to cube"); - itemsInCube.forEach(item => { - if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) return; - if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return; - }); - return false; - } + const itemsInCube = me.getItemsEx().filter(function (el) { + return el.isInCube; + }); + if (itemsInCube.length !== itemsToCubeCount) { + console.warn("Failed to move all necesary items to cube"); + itemsInCube.forEach(function (item) { + if (Storage.Inventory.CanFit(item) && Storage.Inventory.MoveTo(item)) return; + if (Storage.Stash.CanFit(item) && Storage.Stash.MoveTo(item)) return; + }); + return false; + } - 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); - this.update(); - - let cubeItems = me.findItems(-1, -1, sdk.storage.Cube); - - if (items) { - for (let j = 0; j < cubeItems.length; j += 1) { - let cubeItem = cubeItems[j]; - let result = Pickit.checkItem(cubeItem); - - /** - * @todo - * - build better method of updating cubelist so if a item we cube is wanted by cubing we - * can update our list without clearing and rebuilding the whole thing - */ - - switch (result.result) { - case Pickit.Result.UNWANTED: - Item.logger("Dropped", cubeItem, "doCubing"); - cubeItem.drop(); - - break; - case Pickit.Result.WANTED: - Item.logger("Cubing Kept", cubeItem); - Item.logItem("Cubing Kept", cubeItem, result.line); - - break; - case Pickit.Result.RUNEWORD: - Runewords.update(cubeItem.classid, cubeItem.gid); - - break; - case Pickit.Result.CRAFTING: - CraftingSystem.update(cubeItem); - - break; - } + transmute(); + delay(700 + me.ping); + console.log("ÿc4Cubing: " + string); + Config.ShowCubingInfo && D2Bot.printToConsole(string, sdk.colors.D2Bot.Green); + this.update(); + + let cubeItems = me.findItems(-1, -1, sdk.storage.Cube); + + if (items) { + for (let cubeItem of cubeItems) { + let result = Pickit.checkItem(cubeItem); + + /** + * @todo + * - build better method of updating cubelist so if a item we cube is wanted by cubing we + * can update our list without clearing and rebuilding the whole thing + */ + + switch (result.result) { + case Pickit.Result.UNWANTED: + Item.logger("Dropped", cubeItem, "doCubing"); + cubeItem.drop(); + + break; + case Pickit.Result.WANTED: + Item.logger("Cubing Kept", cubeItem); + Item.logItem("Cubing Kept", cubeItem, result.line); + + break; + case Pickit.Result.RUNEWORD: + Runewords.update(cubeItem.classid, cubeItem.gid); + + break; + case Pickit.Result.CRAFTING: + CraftingSystem.update(cubeItem); + + break; } } + } - if (!this.emptyCube()) { - break; - } + if (!this.emptyCube()) { + break; } } @@ -1130,7 +1130,7 @@ const Cubing = { */ Cubing.update(); let checkList = this.recipes.slice().shuffle(); - if (checkList.some(r => Cubing.checkRecipe(r))) { + if (checkList.some(Cubing.checkRecipe)) { // we can still cube so recursive call to doCubing return Cubing.doCubing(); } @@ -1174,34 +1174,40 @@ const Cubing = { if (!cube) return false; if (cube.isInStash && !Town.openStash()) return false; + const cubeOpened = function () { + return getUIFlag(sdk.uiflags.Cube); + }; - for (let i = 0; i < 5 && !getUIFlag(sdk.uiflags.Cube); i++) { + for (let i = 0; i < 5 && !cubeOpened(); i++) { cube.interact(); - if (Misc.poll(() => getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { + if (Misc.poll(cubeOpened, (Time.seconds(1) * (i + 1)), 100)) { delay(100 + me.ping * 2); // allow UI to initialize return true; } } - return getUIFlag(sdk.uiflags.Cube); + return cubeOpened(); }, closeCube: function () { if (!getUIFlag(sdk.uiflags.Cube)) return true; + const cubeClosed = function () { + return !getUIFlag(sdk.uiflags.Cube); + }; - for (let i = 0; i < 5 && getUIFlag(sdk.uiflags.Cube); i++) { + for (let i = 0; i < 5 && !cubeClosed(); i++) { me.cancel(); - if (Misc.poll(() => !getUIFlag(sdk.uiflags.Cube), (Time.seconds(1) * (i + 1)), 100)) { + if (Misc.poll(cubeClosed, (Time.seconds(1) * (i + 1)), 100)) { delay(250 + me.ping * 2); // allow UI to initialize return true; } } - return !getUIFlag(sdk.uiflags.Cube); + return cubeClosed(); }, emptyCube: function () { @@ -1211,6 +1217,11 @@ const Cubing = { let items = me.findItems(-1, -1, sdk.storage.Cube); if (!items) return true; + /** @param {ItemUnit} item */ + const prettyPrint = function (item) { + return item && item.prettyPrint; + }; + let sorted = false; while (items.length) { @@ -1223,7 +1234,7 @@ const Cubing = { continue; } - console.warn("Failed to empty cube. Items still in cube :: ", items.map(i => i && i.prettyPrint)); + console.warn("Failed to empty cube. Items still in cube :: ", items.map(prettyPrint)); return false; } From fa3ee623a6fc74b8a62c9a12eb4dfc15722cbb63 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:50:44 -0500 Subject: [PATCH 345/758] Update Cubing.js - Small cleanup, fix `this` reference being undefined when used as a callback --- d2bs/kolbot/libs/core/Cubing.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 144323e75..052eab2ad 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -812,22 +812,22 @@ const Cubing = { let matchList = []; for (let i = 0; i < recipe.Ingredients.length; i += 1) { - for (let j = 0; j < this.validIngredients.length; j += 1) { - if (usedGids.indexOf(this.validIngredients[j].gid) === -1 && ( - this.validIngredients[j].classid === recipe.Ingredients[i] - || (recipe.Ingredients[i] === "pgem" && this.gemList.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "fgem" && this.gems.flawless.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "gem" && this.gems.normal.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "cgem" && this.gems.chipped.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "hpot" && this.pots.healing.includes(this.validIngredients[j].classid)) - || (recipe.Ingredients[i] === "mpot" && this.pots.mana.includes(this.validIngredients[j].classid)) + for (let ingredient of Cubing.validIngredients) { + if (usedGids.indexOf(ingredient.gid) === -1 && ( + ingredient.classid === recipe.Ingredients[i] + || (recipe.Ingredients[i] === "pgem" && Cubing.gemList.includes(ingredient.classid)) + || (recipe.Ingredients[i] === "fgem" && Cubing.gems.flawless.includes(ingredient.classid)) + || (recipe.Ingredients[i] === "gem" && Cubing.gems.normal.includes(ingredient.classid)) + || (recipe.Ingredients[i] === "cgem" && Cubing.gems.chipped.includes(ingredient.classid)) + || (recipe.Ingredients[i] === "hpot" && Cubing.pots.healing.includes(ingredient.classid)) + || (recipe.Ingredients[i] === "mpot" && Cubing.pots.mana.includes(ingredient.classid)) )) { - let item = me.getItem(this.validIngredients[j].classid, -1, this.validIngredients[j].gid); + let item = me.getItem(ingredient.classid, -1, ingredient.gid); // 26.11.2012. check if the item actually belongs to the given recipe - if (item && this.validItem(item, recipe)) { + if (item && Cubing.validItem(item, recipe)) { // don't repeat the same item - usedGids.push(this.validIngredients[j].gid); + usedGids.push(ingredient.gid); // push the item into the match list matchList.push(copyUnit(item)); From e287cbe5ad99ca43f06e9f8d713448427dd7a916 Mon Sep 17 00:00:00 2001 From: magace Date: Thu, 28 Dec 2023 15:52:55 -0600 Subject: [PATCH 346/758] GetEssences.js (#393) * Create GetEssences.js * GetEssences Used to get essences without running every boss each game. * GetEssences Added Config.GetEssences.MoatMeph Config.GetEssences.FastDiablo * Little bit of cleanup - Removed option of MoatMeph from non-sorc config files. It's a sorc only addition --------- Co-authored-by: theBGuy <60308670+theBGuy@users.noreply.github.com> --- d2bs/kolbot/libs/config/Amazon.js | 3 +- d2bs/kolbot/libs/config/Assassin.js | 3 +- d2bs/kolbot/libs/config/Barbarian.js | 3 +- d2bs/kolbot/libs/config/Druid.js | 3 +- d2bs/kolbot/libs/config/Necromancer.js | 3 +- d2bs/kolbot/libs/config/Paladin.js | 3 +- d2bs/kolbot/libs/config/Sorceress.js | 4 +- d2bs/kolbot/libs/config/_BaseConfigFile.js | 4 +- d2bs/kolbot/libs/core/Config.js | 4 ++ d2bs/kolbot/libs/scripts/GetEssences.js | 55 ++++++++++++++++++++++ d2bs/kolbot/sdk/types/Config.d.ts | 4 ++ 11 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 d2bs/kolbot/libs/scripts/GetEssences.js diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index d7c50edef..bea4ada9a 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -351,7 +351,8 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index cd3048bdc..aa14863ff 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -351,7 +351,8 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index af49f0dd1..1251a91d6 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -351,7 +351,8 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 62537bce1..e68eb1a7f 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -351,7 +351,8 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 4ee1e3146..9edc1c46d 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -351,7 +351,8 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index ec951f670..770386fd6 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -351,7 +351,8 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 415b5968c..e9b2a02b7 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -351,7 +351,9 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index e137df345..85eb49951 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -332,7 +332,9 @@ ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 9489c0cfc..8c3527ee1 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -704,6 +704,10 @@ let Config = { Questing: { StopProfile: false }, + GetEssences: { + MoatMeph: false, + FastDiablo: false, + }, GemHunter: { AreaList: [], GemList: [] diff --git a/d2bs/kolbot/libs/scripts/GetEssences.js b/d2bs/kolbot/libs/scripts/GetEssences.js new file mode 100644 index 000000000..80d8bbecc --- /dev/null +++ b/d2bs/kolbot/libs/scripts/GetEssences.js @@ -0,0 +1,55 @@ +/** +* @filename GetEssences.js +* @author magace +* @credits kolton for the original GetKeys +* @desc get essences for Token of Absolution +* +*/ + +function GetEssences () { + Town.doChores(); + + /** + * @param {number} essence + * @returns {number} + */ + const count = function (essence) { + return me.getItemsEx(essence, sdk.items.mode.inStorage).length; + }; + + if (count(sdk.quest.item.TwistedEssenceofSuffering) < 1) { + try { + Loader.runScript("Andariel"); + } catch (e) { + console.error("ÿc1Andariel failed :: ", e); + } + } + + if (count(sdk.quest.item.ChargedEssenceofHatred) < 1) { + try { + Config.Mephisto.MoatTrick = Config.GetEssences.MoatMeph; + Loader.runScript("Mephisto"); + } catch (e) { + console.error("ÿc1Mephisto failed :: ", e); + } + } + + if (count(sdk.quest.item.BurningEssenceofTerror) < 1) { + try { + Config.Diablo.Fast = Config.GetEssences.FastDiablo; + Loader.runScript("Diablo"); + } catch (e) { + console.error("ÿc1Diablo failed :: ", e); + } + } + + if (count(sdk.quest.item.FesteringEssenceofDestruction) < 1) { + try { + Loader.runScript("Baal"); + } catch (e) { + console.error("ÿc1Baal failed :: ", e); + } + } + + return true; +} diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 0d47c2ba6..d6887ea7f 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -555,6 +555,10 @@ declare global { const StopProfile_1: boolean; export { StopProfile_1 as StopProfile }; } + interface GetEssences { + MoatMeph: boolean; + FastDiablo: boolean; + } namespace AutoSkill { const Enabled_3: boolean; export { Enabled_3 as Enabled }; From 5fefff0871bf7d06fee5f1313e43c425331093b2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:52:15 -0800 Subject: [PATCH 347/758] add `closeToStash` option to `Cubing.closeCube` - Using `sendClick` to hit the close button instead of `me.cancel` which will drop any item we have on cursor. We can skip putting items in our invo to move to stash using this. --- d2bs/kolbot/libs/core/Cubing.js | 10 +++++++--- d2bs/kolbot/sdk/types/Cubing.d.ts | 11 ++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 052eab2ad..17a00a11e 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -1191,15 +1191,19 @@ const Cubing = { return cubeOpened(); }, - closeCube: function () { + closeCube: function (closeToStash = false) { if (!getUIFlag(sdk.uiflags.Cube)) return true; const cubeClosed = function () { return !getUIFlag(sdk.uiflags.Cube); }; - for (let i = 0; i < 5 && !cubeClosed(); i++) { - me.cancel(); + const closeBtn = me.screensize + ? { x: 373, y: 469 } + : { x: 285, y: 394 }; + for (let i = 0; i < 5 && !cubeClosed(); i++) { + closeToStash ? sendClick(closeBtn.x, closeBtn.y) : me.cancel(); + if (Misc.poll(cubeClosed, (Time.seconds(1) * (i + 1)), 100)) { delay(250 + me.ping * 2); // allow UI to initialize diff --git a/d2bs/kolbot/sdk/types/Cubing.d.ts b/d2bs/kolbot/sdk/types/Cubing.d.ts index 30cc3782a..30ee1556e 100644 --- a/d2bs/kolbot/sdk/types/Cubing.d.ts +++ b/d2bs/kolbot/sdk/types/Cubing.d.ts @@ -13,11 +13,12 @@ declare global { function checkItem(unit: any): boolean; function keepItem(unit: any): boolean; function validItem(unit: any, recipe: any): void; - function doCubing(): void; - function cursorCheck(): void; - function openCube(): void; - function closeCube(): void; - function emptyCube(): void; + function doCubing(): boolean; + function cursorCheck(): boolean; + function openCube(): boolean; + function closeCube(closeToStash: boolean): boolean; + function emptyCube(): boolean; function makeRevPots(): void; + function repairItem(item: ItemUnit): boolean; } } From 0f1aefd495d76456419dbf67fff30b6fdb7ddd78 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:57:13 -0800 Subject: [PATCH 348/758] Update Town.js - If FastPick is being used, remove it for the duration of town chores. This helps fix it bugging out with shopping - Wrap chores in try/finally so FastPick is added back if it is being used and allowBroadcast is reset - Add a cursorCheck before `me.cancel` when we go to open our stash in case we closed out the stash while moving items around --- d2bs/kolbot/libs/core/Town.js | 325 +++++++++++++++++----------------- 1 file changed, 167 insertions(+), 158 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 45110fba9..d6d503f6f 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -10,6 +10,94 @@ const Town = { sellTimer: getTickCount(), // shop speedup test lastChores: 0, + act: { + 1: { + spot: (function () { + const _spot = {}; + _spot.stash = [0, 0]; + _spot[NPC.Warriv] = [0, 0]; + _spot[NPC.Cain] = [0, 0]; + _spot[NPC.Kashya] = [0, 0]; + _spot[NPC.Akara] = [0, 0]; + _spot[NPC.Charsi] = [0, 0]; + _spot[NPC.Gheed] = [0, 0]; + _spot.portalspot = [0, 0]; + _spot.waypoint = [0, 0]; + _spot.initialized = false; + return _spot; + })(), + }, + 2: { + spot: (function () { + const _spot = {}; + _spot[NPC.Fara] = [5124, 5082]; + _spot[NPC.Cain] = [5124, 5082]; + _spot[NPC.Lysander] = [5118, 5104]; + _spot[NPC.Greiz] = [5033, 5053]; + _spot[NPC.Elzix] = [5032, 5102]; + _spot[NPC.Jerhyn] = [5088, 5153]; + _spot[NPC.Meshif] = [5205, 5058]; + _spot[NPC.Drognan] = [5097, 5035]; + _spot[NPC.Atma] = [5137, 5060]; + _spot[NPC.Warriv] = [5152, 5201]; + _spot.palace = [5088, 5153]; + _spot.sewers = [5221, 5181]; + _spot.portalspot = [5168, 5060]; + _spot.stash = [5124, 5076]; + _spot.waypoint = [5070, 5083]; + _spot.initialized = true; + return _spot; + })(), + }, + 3: { + spot: (function () { + const _spot = {}; + _spot[NPC.Meshif] = [5118, 5168]; + _spot[NPC.Hratli] = [5223, 5048, 5127, 5172]; + _spot[NPC.Ormus] = [5129, 5093]; + _spot[NPC.Asheara] = [5043, 5093]; + _spot[NPC.Alkor] = [5083, 5016]; + _spot[NPC.Cain] = [5148, 5066]; + _spot.stash = [5144, 5059]; + _spot.portalspot = [5150, 5063]; + _spot.waypoint = [5158, 5050]; + _spot.initialized = true; + return _spot; + })(), + }, + 4: { + spot: (function () { + const _spot = {}; + _spot[NPC.Cain] = [5027, 5027]; + _spot[NPC.Halbu] = [5089, 5031]; + _spot[NPC.Tyrael] = [5027, 5027]; + _spot[NPC.Jamella] = [5088, 5054]; + _spot.stash = [5022, 5040]; + _spot.portalspot = [5045, 5042]; + _spot.waypoint = [5043, 5018]; + _spot.initialized = true; + return _spot; + })(), + }, + 5: { + spot: (function () { + const _spot = {}; + _spot[NPC.Larzuk] = [5141, 5045]; + _spot[NPC.Malah] = [5078, 5029]; + _spot[NPC.Cain] = [5119, 5061]; + _spot[NPC.Qual_Kehk] = [5066, 5083]; + _spot[NPC.Anya] = [5112, 5120]; + _spot[NPC.Nihlathak] = [5071, 5111]; + _spot.stash = [5129, 5061]; + _spot.portalspot = [5098, 5019]; + _spot.portal = [5118, 5120]; + _spot.waypoint = [5113, 5068]; + _spot.initialized = true; + return _spot; + })(), + } + }, + tasks: (function () { /** * @param {string} heal @@ -66,43 +154,54 @@ const Town = { throw new Error("Failed to go to town for chores"); } - const preAct = me.act; - Pather.allowBroadcast = false; - - // 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); - } - - me.switchToPrimary(); - - Town.heal(); - Town.identify(); - Town.clearInventory(); - Town.fillTome(sdk.items.TomeofTownPortal); - Town.buyPotions(); - Config.FieldID.Enabled && Town.fillTome(sdk.items.TomeofIdentify); - Town.shopItems(); - Town.buyKeys(); - Town.repair(repair); - Town.gamble(); - Town.reviveMerc(); - Cubing.doCubing(); - Runewords.makeRunewords(); - Town.stash(true); - Town.checkQuestItems(); - !!me.getItem(sdk.items.TomeofTownPortal) && Town.clearScrolls(); - - me.act !== preAct && Town.goToTown(preAct); - me.cancelUIFlags(); - !me.barbarian && Precast.haveCTA === -1 && Precast.doPrecast(false); - - delay(250); - console.info(false, null, "doChores"); - Pather.allowBroadcast = true; - Town.lastChores = getTickCount(); + try { + Pather.allowBroadcast = false; + if (Config.FastPick && new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { + // shopping causes this to bug out sometimes so remove it for duration of chores + removeEventListener("itemaction", Pickit.itemEvent); + } + + const preAct = me.act; + // 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); + } + + me.switchToPrimary(); + + Town.heal(); + Town.identify(); + Town.clearInventory(); + Town.fillTome(sdk.items.TomeofTownPortal); + Town.buyPotions(); + Config.FieldID.Enabled && Town.fillTome(sdk.items.TomeofIdentify); + Town.shopItems(); + Town.buyKeys(); + Town.repair(repair); + Town.gamble(); + Town.reviveMerc(); + Cubing.doCubing(); + Runewords.makeRunewords(); + Town.stash(true); + Town.checkQuestItems(); + !!me.getItem(sdk.items.TomeofTownPortal) && Town.clearScrolls(); + + me.act !== preAct && Town.goToTown(preAct); + me.cancelUIFlags(); + !me.barbarian && Precast.haveCTA === -1 && Precast.doPrecast(false); + + delay(250); + console.info(false, null, "doChores"); - return true; + return true; + } finally { + if (Config.FastPick && new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { + addEventListener("itemaction", Pickit.itemEvent); + } + + Pather.allowBroadcast = true; + Town.lastChores = getTickCount(); + } }, /** @@ -581,14 +680,13 @@ const Town = { */ getPotion: function (npc, type, highestPot = 5) { if (!type) return false; + if (type !== "hp" && type !== "mp") return false; - if (type === "hp" || type === "mp") { - for (let i = highestPot; i > 0; i -= 1) { - let result = npc.getItem(type + i); + for (let i = highestPot; i > 0; i -= 1) { + let result = npc.getItem(type + i); - if (result) { - return result; - } + if (result) { + return result; } } @@ -771,40 +869,38 @@ const Town = { me.cancel(); Town.stash(false); - let unids = me.getUnids(); + const unids = me.getUnids(); + if (!unids.length) return true; - if (unids) { - // Check if we may use Cain - number of unid items - if (unids.length < Config.CainID.MinUnids) return false; + // Check if we may use Cain - number of unid items + if (unids.length < Config.CainID.MinUnids) return false; - // Check if we may use Cain - kept unid items - for (let item of unids) { - if (Pickit.checkItem(item).result > 0) return false; - } + // Check if we may use Cain - kept unid items + for (let item of unids) { + if (Pickit.checkItem(item).result > 0) return false; + } - let cain = Town.initNPC("CainID", "cainID"); - if (!cain) return false; + let cain = Town.initNPC("CainID", "cainID"); + if (!cain) return false; - for (let item of unids) { - let result = Pickit.checkItem(item); + for (let item of unids) { + let result = Pickit.checkItem(item); - switch (result.result) { - case Pickit.Result.UNWANTED: - Item.logger("Dropped", item, "cainID"); - item.drop(); + switch (result.result) { + case Pickit.Result.UNWANTED: + Item.logger("Dropped", item, "cainID"); + item.drop(); - break; - case Pickit.Result.WANTED: - Item.logger("Kept", item); - Item.logItem("Kept", item, result.line); + break; + case Pickit.Result.WANTED: + Item.logger("Kept", item); + Item.logItem("Kept", item, result.line); - break; - default: - break; - } + break; + default: + break; } } - return true; }, @@ -1207,7 +1303,7 @@ const Town = { * @param {boolean} [force=false] */ repair: function (force = false) { - if (Town.cubeRepair()) return true; + if (Cubing.doRepairs()) return true; let npc; let repairAction = me.needRepair(); @@ -1463,13 +1559,14 @@ const Town = { }, openStash: function () { - if (getUIFlag(sdk.uiflags.Cube) && !Cubing.closeCube()) return false; + if (getUIFlag(sdk.uiflags.Cube) && !Cubing.closeCube(true)) return false; if (getUIFlag(sdk.uiflags.Stash)) return true; const stashOpened = function () { return getUIFlag(sdk.uiflags.Stash); }; - for (let i = 0; i < 5; i += 1) { + for (let i = 0; i < 5 && !stashOpened(); i += 1) { + me.itemoncursor && Cubing.cursorCheck(); me.cancel(); if (Town.move("stash")) { @@ -1812,94 +1909,6 @@ const Town = { return true; }, - - act: { - 1: { - spot: (function () { - const _spot = {}; - _spot.stash = [0, 0]; - _spot[NPC.Warriv] = [0, 0]; - _spot[NPC.Cain] = [0, 0]; - _spot[NPC.Kashya] = [0, 0]; - _spot[NPC.Akara] = [0, 0]; - _spot[NPC.Charsi] = [0, 0]; - _spot[NPC.Gheed] = [0, 0]; - _spot.portalspot = [0, 0]; - _spot.waypoint = [0, 0]; - _spot.initialized = false; - return _spot; - })(), - }, - 2: { - spot: (function () { - const _spot = {}; - _spot[NPC.Fara] = [5124, 5082]; - _spot[NPC.Cain] = [5124, 5082]; - _spot[NPC.Lysander] = [5118, 5104]; - _spot[NPC.Greiz] = [5033, 5053]; - _spot[NPC.Elzix] = [5032, 5102]; - _spot[NPC.Jerhyn] = [5088, 5153]; - _spot[NPC.Meshif] = [5205, 5058]; - _spot[NPC.Drognan] = [5097, 5035]; - _spot[NPC.Atma] = [5137, 5060]; - _spot[NPC.Warriv] = [5152, 5201]; - _spot.palace = [5088, 5153]; - _spot.sewers = [5221, 5181]; - _spot.portalspot = [5168, 5060]; - _spot.stash = [5124, 5076]; - _spot.waypoint = [5070, 5083]; - _spot.initialized = true; - return _spot; - })(), - }, - 3: { - spot: (function () { - const _spot = {}; - _spot[NPC.Meshif] = [5118, 5168]; - _spot[NPC.Hratli] = [5223, 5048, 5127, 5172]; - _spot[NPC.Ormus] = [5129, 5093]; - _spot[NPC.Asheara] = [5043, 5093]; - _spot[NPC.Alkor] = [5083, 5016]; - _spot[NPC.Cain] = [5148, 5066]; - _spot.stash = [5144, 5059]; - _spot.portalspot = [5150, 5063]; - _spot.waypoint = [5158, 5050]; - _spot.initialized = true; - return _spot; - })(), - }, - 4: { - spot: (function () { - const _spot = {}; - _spot[NPC.Cain] = [5027, 5027]; - _spot[NPC.Halbu] = [5089, 5031]; - _spot[NPC.Tyrael] = [5027, 5027]; - _spot[NPC.Jamella] = [5088, 5054]; - _spot.stash = [5022, 5040]; - _spot.portalspot = [5045, 5042]; - _spot.waypoint = [5043, 5018]; - _spot.initialized = true; - return _spot; - })(), - }, - 5: { - spot: (function () { - const _spot = {}; - _spot[NPC.Larzuk] = [5141, 5045]; - _spot[NPC.Malah] = [5078, 5029]; - _spot[NPC.Cain] = [5119, 5061]; - _spot[NPC.Qual_Kehk] = [5066, 5083]; - _spot[NPC.Anya] = [5112, 5120]; - _spot[NPC.Nihlathak] = [5071, 5111]; - _spot.stash = [5129, 5061]; - _spot.portalspot = [5098, 5019]; - _spot.portal = [5118, 5120]; - _spot.waypoint = [5113, 5068]; - _spot.initialized = true; - return _spot; - })(), - } - }, initialize: function () { // console.log("Initialize town " + me.act); From d4a6b2834a7f543d1b03e587c196a9036b4796b5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:39:40 -0500 Subject: [PATCH 349/758] Add more sdk.locale.items values - Added Windforce and GinthersRift - Added BoneWall collision --- d2bs/kolbot/libs/modules/sdk.js | 5 ++++- d2bs/kolbot/sdk/types/sdk.d.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index ec92fb454..88a58a632 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -121,8 +121,9 @@ MonsterObject: 0xFFFF, BlockMissile: 0x80E, WallOrRanged: 0x5, - BlockWalk: 0x1805, + BlockWalk: 0x1905, FriendlyRanged: 0x2004, + BoneWall: 4352, }, areas: { @@ -4102,6 +4103,7 @@ JalalsMane: 21750, HeraldofZakarum: 21758, BloodRavensCharge: 21508, + Windforce: 21635, Gimmershred: 21637, MedusasGaze: 21516, Rockstopper: 21519, @@ -4296,6 +4298,7 @@ Thunderstroke: 21445, DemonsArch: 21447, DjinnSlayer: 21450, + GinthersRift: 21829, // Runewords AncientsPledge: 20507, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index dcb2de81d..3f4841c1f 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -91,6 +91,7 @@ declare global { const WallOrRanged: 0x5; const BlockWalk: 0x1805; const FriendlyRanged: 0x2004; + const BoneWall: 4352; } export namespace areas { @@ -3251,11 +3252,11 @@ declare global { const Soul: 540; const Scalp: 541; const Spleen: 542; - const Key: 543; const Ear: 556; const Herb: 602; const anevilforce: 609; // Potions, tomes/scrolls, gold + export const Key: 543; export const TomeofTownPortal: 518; export const TomeofIdentify: 519; export const ScrollofTownPortal: 529; @@ -4221,6 +4222,8 @@ declare global { const Thunderstroke: 21445; const DemonsArch: 21447; const DjinnSlayer: 21450; + const Windforce: 21635; + const GinthersRift: 21829; // Runewords const AncientsPledge: 20507; From 11ac863d59c3c0b2bc479137d53153bedad3bf93 Mon Sep 17 00:00:00 2001 From: Georg R <39526093+icommitdesnet@users.noreply.github.com> Date: Sat, 27 Jan 2024 19:12:25 +0100 Subject: [PATCH 350/758] Manualplay (#395) * Manualplay add * Gambling * Diablo fast Script * Clearlevel * Remove diablo and Clearlevel Gambling Config back to Original Removed Whitespace --- d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js | 2 ++ d2bs/kolbot/libs/manualplay/main.js | 4 ++++ d2bs/kolbot/libs/manualplay/threads/MapHelper.js | 9 +++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js b/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js index eac34086c..c2a5e805f 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js +++ b/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js @@ -23,6 +23,7 @@ const HelpMenu = new function () { "hide": "Hide this menu", "make": "create config file with current characters name", "stash": "Stash items/gold from inventory", + "gamble": "Start gambling", "filltps": "Fill tp tome", "cowportal": "Make cow portal as long as bot already has leg", "uberportal": "Make uber portal(s) as long as bot already has key", @@ -70,6 +71,7 @@ const HelpMenu = new function () { "hide", "make", "stash", + "gamble", "filltps", "cowportal", "uberportal", diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index 66cc4b911..25e42bf1b 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -251,6 +251,10 @@ function main () { case "stash": me.inTown && (qolObj.action = "stashItems"); + break; + case "gamble": + me.inTown && (qolObj.action = "gamble"); + break; case "pick": case "cowportal": diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 6fd469fbf..2a8a284ac 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -279,6 +279,10 @@ function main () { case "stashItems": Town.stash(true, true); + break; + case "gamble": + Config.Gamble ? Town.gamble() : me.overhead("Check your Config. Gambling is disabled."); + break; case "makePortal": Pather.makePortal(); @@ -288,9 +292,10 @@ function main () { Town.goToTown(); break; + case "clear": Attack.clear(10); - + break; case "cowportal": Misc.openRedPortal(sdk.areas.MooMooFarm); @@ -396,7 +401,7 @@ function main () { switch (obj.action) { case "thawing": Town.buyPots(10, "Thawing", true, true); - + break; case "antidote": Town.buyPots(10, "Antidote", true, true); From 75ca53442f15b0a9fa5a8ba7e47fb2eb3e243407 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 Jan 2024 01:56:28 -0500 Subject: [PATCH 351/758] Add `Packet.initNPC` - Util method, send init packet --- d2bs/kolbot/libs/core/Packet.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 09ce2ff69..39d3425a1 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -99,8 +99,8 @@ const Packet = { * @returns {boolean} */ buyItem: function (unit, shiftBuy, gamble) { - let oldGold = me.gold; - let itemCount = me.itemcount; + const oldGold = me.gold; + const itemCount = me.itemcount; let npc = getInteractedNPC(); try { @@ -392,6 +392,20 @@ const Packet = { return true; }, + /** + * @param {NPCUnit} who + * @returns {boolean} + */ + initNPC: function (who) { + if (!who || !copyUnit(who).x) return false; + new PacketBuilder() + .byte(sdk.packets.send.NPCInit) + .dword(1) // action type + .dword(who.gid) + .send(); + return true; + }, + /** * @param {NPCUnit} who * @returns {boolean} From 69b48b27bb6b4b437edf7d3873a41cbbca09f6e7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 Jan 2024 02:26:28 -0500 Subject: [PATCH 352/758] Update ShopBot.js - Cleanup mostly. - Removed messy switch case in favor of predefining the shoppableNpcs with a map. - Predefined the wpPresets and outOfTownWps --- d2bs/kolbot/libs/scripts/ShopBot.js | 123 +++++++++++++--------------- 1 file changed, 55 insertions(+), 68 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js index 226f7cd2b..b2281ab8f 100644 --- a/d2bs/kolbot/libs/scripts/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -5,12 +5,12 @@ * */ -function ShopBot() { +function ShopBot () { const overlayText = { title: new Text("kolbot shopbot", 50, 245, 2, 1), - cycles: new Text("Cycles in last minute:", 50, 260, 2, 1), - frequency: new Text("Valid item frequency:", 50, 275, 2, 1), - totalCycles: new Text("Total cycles:", 50, 290, 2, 1), + cycles: new Text("Cycles in last minute: 0", 50, 260, 2, 1), + frequency: new Text("Valid item frequency: 0", 50, 275, 2, 1), + totalCycles: new Text("Total cycles: 0", 50, 290, 2, 1), }; let tickCount; @@ -18,9 +18,46 @@ function ShopBot() { let validItems = 0; let totalCycles = 0; - Pather.teleport = false; + /** @type {Array<[(item: ItemUnit) => boolean, (item: ItemUnit) => boolean, (item: ItemUnit) => boolean]>} */ const pickEntries = []; + /** @type {Object} */ const npcs = {}; + const wpPresets = { + 1: sdk.objects.A1Waypoint, + 2: sdk.objects.A2Waypoint, + 3: sdk.objects.A3Waypoint, + 4: sdk.objects.A4Waypoint, + 5: sdk.objects.A5Waypoint + }; + const outOfTownWps = { + 1: sdk.areas.CatacombsLvl2, + 2: sdk.areas.A2SewersLvl2, + 3: sdk.areas.DuranceofHateLvl2, + 4: sdk.areas.RiverofFlame, + 5: sdk.areas.CrystalizedPassage + }; + const shopableNPCS = new Map([ + // Act 1 + [NPC.Charsi, { town: sdk.areas.RogueEncampment, menuId: "Repair" }], + [NPC.Akara, { town: sdk.areas.RogueEncampment, menuId: "Shop" }], + [NPC.Gheed, { town: sdk.areas.RogueEncampment, menuId: "Shop" }], + // Act 2 + [NPC.Fara, { town: sdk.areas.LutGholein, menuId: "Repair" }], + [NPC.Elzix, { town: sdk.areas.LutGholein, menuId: "Shop" }], + [NPC.Drognan, { town: sdk.areas.LutGholein, menuId: "Shop" }], + // Act 3 + [NPC.Hratli, { town: sdk.areas.KurastDocktown, menuId: "Repair" }], + [NPC.Asheara, { town: sdk.areas.KurastDocktown, menuId: "Shop" }], + [NPC.Ormus, { town: sdk.areas.KurastDocktown, menuId: "Shop" }], + // Act 4 + [NPC.Halbu, { town: sdk.areas.PandemoniumFortress, menuId: "Repair" }], + [NPC.Jamella, { town: sdk.areas.PandemoniumFortress, menuId: "Shop" }], + // Act 5 + [NPC.Larzuk, { town: sdk.areas.Harrogath, menuId: "Repair" }], + [NPC.Malah, { town: sdk.areas.Harrogath, menuId: "Shop" }], + [NPC.Anya, { town: sdk.areas.Harrogath, menuId: "Shop" }], + [NPC.Nihlathak, { town: sdk.areas.Harrogath, menuId: "Shop" }] + ]); const buildPickList = function () { let nipfile, filepath = "pickit/shopbot.nip"; @@ -78,7 +115,7 @@ function ShopBot() { if (!getUIFlag(sdk.uiflags.NPCMenu)) { Packet.entityInteract(npc); - sendPacket(1, sdk.packets.send.NPCInit, 4, 1, 4, npc.gid); + Packet.initNPC(npc); } let tick = getTickCount(); @@ -103,8 +140,6 @@ function ShopBot() { * @returns {boolean} */ const shopItems = function (npc, menuId) { - let bought; - if (!Storage.Inventory.CanFit({ sizex: 2, sizey: 4 }) && AutoMule.getMuleItems().length > 0) { D2Bot.printToConsole("Mule triggered"); scriptBroadcast("mule"); @@ -127,11 +162,11 @@ function ShopBot() { let items = npc.getItemsEx().filter(function (item) { return (Config.ShopBot.ScanIDs.includes(item.classid) || Config.ShopBot.ScanIDs.length === 0); }); - if (!items.length) return false; me.overhead(npc.itemcount + " items, " + items.length + " valid"); + let bought; validItems += items.length; overlayText.frequency.text = "Valid base items / cycle: " + ((validItems / totalCycles).toFixed(2).toString()); @@ -167,53 +202,13 @@ function ShopBot() { * @returns {boolean} */ const shopAtNPC = function (name) { - let wp, menuId = "Shop"; - - switch (name) { - case NPC.Charsi: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Akara: - case NPC.Gheed: - wp = sdk.areas.RogueEncampment; - - break; - case NPC.Fara: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Elzix: - case NPC.Drognan: - wp = sdk.areas.LutGholein; - - break; - case NPC.Hratli: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Asheara: - case NPC.Ormus: - wp = sdk.areas.KurastDocktown; - - break; - case NPC.Halbu: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Jamella: - wp = sdk.areas.PandemoniumFortress; - - break; - case NPC.Larzuk: - menuId = "Repair"; - // eslint-disable-next-line no-fallthrough - case NPC.Malah: - case NPC.Anya: - wp = sdk.areas.Harrogath; - - break; - default: + if (!shopableNPCS.has(name)) { throw new Error("Invalid NPC"); } - if (!me.inArea(wp) && !Pather.useWaypoint(wp)) return false; + const { town, menuId } = shopableNPCS.get(name); + + if (!me.inArea(town) && !Pather.useWaypoint(town)) return false; let npc = npcs[name] || Game.getNPC(name); @@ -252,9 +247,10 @@ function ShopBot() { if (Config.ShopBot.MinGold && me.gold < Config.ShopBot.MinGold) return true; buildPickList(); - print("Shopbot: Pickit entries: " + pickEntries.length); + console.log("Shopbot: Pickit entries: " + pickEntries.length); Town.doChores(); + Pather.teleport = false; tickCount = getTickCount(); while (!Config.ShopBot.Cycles || totalCycles < Config.ShopBot.Cycles) { @@ -271,13 +267,8 @@ function ShopBot() { if (me.inTown) { let area = getArea(); - let wp = Game.getPresetObject(me.area, [ - sdk.objects.A1Waypoint, sdk.objects.A2Waypoint, - sdk.objects.A3Waypoint, sdk.objects.A4Waypoint, sdk.objects.A5Waypoint - ][me.act - 1]); - let wpX = wp.roomx * 5 + wp.x; - let wpY = wp.roomy * 5 + wp.y; - let redPortal = (getUnits(sdk.unittype.Object, sdk.objects.RedPortal) + const wp = Game.getPresetObject(me.area, wpPresets[me.act]).realCoords(); + const redPortal = (getUnits(sdk.unittype.Object, sdk.objects.RedPortal) .sort((a, b) => a.distance - b.distance)) .first(); let exit = area.exits[0]; @@ -288,8 +279,7 @@ function ShopBot() { } } - if ([sdk.areas.RogueEncampment, sdk.areas.Harrogath].includes(me.area) && !!redPortal && redPortal.distance < 20 - && Pather.usePortal(null, null, redPortal)) { + if (!!redPortal && redPortal.distance < 20 && Pather.usePortal(null, null, redPortal)) { delay(3000); Pather.usePortal(sdk.areas.townOf(me.area)); @@ -298,14 +288,11 @@ function ShopBot() { } delay(1500); - } else if (getDistance(me, exit) < (getDistance(me, wpX, wpY) + 6)) { + } else if (getDistance(me, exit) < (getDistance(me, wp.x, wp.y) + 6)) { Pather.moveToExit(me.area + 1, true); Pather.moveToExit(me.area - 1, true); } else { - Pather.useWaypoint([ - sdk.areas.CatacombsLvl2, sdk.areas.A2SewersLvl2, - sdk.areas.DuranceofHateLvl2, sdk.areas.RiverofFlame, sdk.areas.CrystalizedPassage - ][me.act - 1]); + Pather.useWaypoint(outOfTownWps[me.act]); } } From 768e319b448a2be3eaf2002a8cf8ae040a016cab Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Jan 2024 03:09:49 -0500 Subject: [PATCH 353/758] [Feat] Add ngvote to ControlBot.js - Allow users to vote on starting the next game. The vote must pass by majority and lasts for two minutes. The vote can only happen after at least three minutes in game to prevent users from abusing it and getting the chanter temp restricted. --- d2bs/kolbot/libs/scripts/ControlBot.js | 73 +++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 288de487b..d2a2d7fa4 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -87,6 +87,38 @@ function ControlBot () { AutoRush.playersOut = "out"; AutoRush.allIn = "all in"; + const ngVote = new function () { + /** @type {Set} */ + this.votes = new Set(); + this.watch = false; + this.tick = 0; + this.nextGame = false; + + this.votesNeeded = function () { + return Math.max(1, (Misc.getPlayerCount() - 2) / 2); + }; + this.reset = function () { + this.votes.clear(); + this.tick = 0; + this.watch = false; + }; + this.begin = function () { + this.watch = true; + this.votes.clear(); + this.tick = getTickCount(); + }; + this.checkCount = function () { + return this.votes.size >= this.votesNeeded; + }; + this.vote = function (nick) { + if (this.watch) { + this.votes.add(nick); + } + }; + this.elapsed = function () { + return getTickCount() - this.tick; + }; + }; const MAX_CHAT_LENGTH = 180; const startTime = getTickCount(); const maxTime = Time.minutes(Config.ControlBot.GameLength); @@ -754,6 +786,10 @@ function ControlBot () { console.debug("Command already running."); return; } + if (!floodCheck([msg, nick]) && (msg === "help" || msg === "timeleft" || msg === "ngyes")) { + actions.get(msg).run(nick); + return; + } let index = queue.findIndex(function (cmd) { return cmd[0] === msg && cmd[1] === nick; }); @@ -784,6 +820,9 @@ function ControlBot () { case 0x01: // "%Name1(%Name2) dropped due to errors." case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." players.delete(name1); + if (ngVote.watch) { + ngVote.votes.delete(name1); + } break; } @@ -868,6 +907,33 @@ function ControlBot () { ); } }); + _actions.set("ngvote", { + desc: "Vote for next game", + hostileCheck: false, + run: function (nick) { + if (getTickCount() - startTime < Time.minutes(3)) { + Chat.say( + "Can't vote for next game yet. Must be in game for at least 3 minutes. Remaining: " + + Math.round((Time.minutes(3) - (getTickCount() - startTime)) / 1000) + " seconds." + ); + return; + } + ngVote.begin(); + Chat.say(nick + " voted for next game. Votes Needed: " + ngVote.votesNeeded + ". Type ngyes to vote."); + } + }); + _actions.set("ngyes", { + desc: "", + hostileCheck: false, + run: function (nick) { + if (!ngVote.watch) return; + ngVote.vote(nick); + if (ngVote.checkCount()) { + Chat.say("Enough votes to start next game."); + ngVote.nextGame = true; + } + } + }); if (Config.ControlBot.Chant.Enchant && Skill.canUse(sdk.skills.Enchant)) { @@ -1066,7 +1132,12 @@ function ControlBot () { me.act > 1 && Town.goToTown(1); Config.ControlBot.Chant.AutoEnchant && autoChant(); - if (getTickCount() - startTime >= maxTime) { + if (ngVote.watch && ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { + Chat.say("Not enough votes to start next game."); + ngVote.reset(); + } + + if (getTickCount() - startTime >= maxTime || ngVote.nextGame) { if (Config.ControlBot.EndMessage) { Chat.say(Config.ControlBot.EndMessage); } From 4c1e7960b816ac813109e5409c5595c75187098f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:46:24 -0500 Subject: [PATCH 354/758] [Feat] Low gold watch for ControlBot - ControlBot breaks on low gold so if we get below 500k gold advertise for donations. We will pick gold piles on ground that our <= 20 yards from us and we return to our starting spot afterwards to ensure users don't try to abuse this and lure us out of town - Added overhead method to the Chat namespace. --- d2bs/kolbot/libs/scripts/ControlBot.js | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index d2a2d7fa4..04b942db5 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -138,6 +138,18 @@ function ControlBot () { Chat.queue.push(msg); }, + /** + * Display a message overhead + * @param {string} msg + * @param {boolean} [force] + */ + overhead: function (msg, force = false) { + if (!force && getTickCount() - Chat.overheadTick < 0) return; + // allow overhead messages every ~3-4 seconds + Chat.overheadTick = getTickCount() + Time.seconds(3) + rand(250, 1500); + say("!" + msg); + }, + /** * Whisper a chat to a user * @param {string} nick @@ -1087,6 +1099,23 @@ function ControlBot () { return action.run(nick); }; + const pickGoldPiles = function () { + /** @type {PathNode} */ + const startPos = { x: me.x, y: me.y }; + let gold = Game.getItem(sdk.items.Gold); + + if (gold) { + do { + if (gold.onGroundOrDropping && gold.distance <= 20 && Pickit.canPick(gold)) { + Pickit.pickItem(gold) && Chat.overhead("Thank you!", true); + if (startPos.distance > 5) { + Pather.move(startPos); + } + } + } while (gold.getNext()); + } + }; + // START let gameEndWarningAnnounced = false; include("oog/ShitList.js"); @@ -1132,6 +1161,11 @@ function ControlBot () { me.act > 1 && Town.goToTown(1); Config.ControlBot.Chant.AutoEnchant && autoChant(); + if (me.gold < 500000) { + Chat.overhead("I am low on gold, to keep this service up please donate by dropping gold near me."); + } + pickGoldPiles(); + if (ngVote.watch && ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { Chat.say("Not enough votes to start next game."); ngVote.reset(); From 14cc99e02898bf90662c03cbb7025dda30f9a216 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:20:39 -0500 Subject: [PATCH 355/758] [Formatting] gambling\TeamsConfig.js - Not sure how I missed this but convert tabs -> spaces --- .../libs/systems/gambling/TeamsConfig.js | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js index 02846546e..b3fc90cee 100644 --- a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js +++ b/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js @@ -6,32 +6,32 @@ */ (function (module) { - module.exports = { - /** - Setting up: + module.exports = { + /** + Setting up: - "Gamble Team 1": { // Put a unique team name here. + "Gamble Team 1": { // Put a unique team name here. - goldFinders: ["GF Profile 1", "GF Profile 2"], // List of gold finder PROFILE names. They will join gamble games to drop gold + goldFinders: ["GF Profile 1", "GF Profile 2"], // List of gold finder PROFILE names. They will join gamble games to drop gold - gamblers: ["Gambler 1", "Gambler 2"], // List of gambler PROFILE names. They will keep gambling and picking up gold from gold finders. + gamblers: ["Gambler 1", "Gambler 2"], // List of gambler PROFILE names. They will keep gambling and picking up gold from gold finders. - gambleGames: ["Gambling-", "HeyIGamble-"], // Games that gold finders will join, don't use numbers. + gambleGames: ["Gambling-", "HeyIGamble-"], // Games that gold finders will join, don't use numbers. - goldTrigger: 2500000, // Minimum amount of gold before giving it to gamblers. + goldTrigger: 2500000, // Minimum amount of gold before giving it to gamblers. - goldReserve: 200000 // Amount of gold to keep after dropping. - } + goldReserve: 200000 // Amount of gold to keep after dropping. + } - Once set up properly, the gold finders will run their own games and join gamblers' games when they're out of gold. - */ - "Gamble Team 1": { - goldFinders: [""], - gamblers: [""], - gambleGames: [""], + Once set up properly, the gold finders will run their own games and join gamblers' games when they're out of gold. + */ + "Gamble Team 1": { + goldFinders: [""], + gamblers: [""], + gambleGames: [""], - goldTrigger: 2500000, - goldReserve: 200000 - }, - }; + goldTrigger: 2500000, + goldReserve: 200000 + }, + }; })(module); From 5ee69e351bfa5a7f289ecbe5f9169866dcd30b7f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:26:41 -0500 Subject: [PATCH 356/758] [Revert] BlockWalk collision change - `0x1905` -> `0x1805`, the change was breaking hammerdins --- d2bs/kolbot/libs/modules/sdk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 88a58a632..9fdf82a49 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -121,7 +121,7 @@ MonsterObject: 0xFFFF, BlockMissile: 0x80E, WallOrRanged: 0x5, - BlockWalk: 0x1905, + BlockWalk: 0x1805, FriendlyRanged: 0x2004, BoneWall: 4352, }, From 696302efc380ee727bd0364a7981bd94351a4242 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Jan 2024 23:54:45 -0500 Subject: [PATCH 357/758] [Feat] Add dropgold command to ControlBot - Configurable option to allow party members to request 5k gold once per player per game. - Set `MIN_GOLD` constant of 500k - Only display overhead messages requesting gold donations if there are other players besides us and the idler in game. We don't know the name of the idler so it's just assuming more than 2 players in game. --- d2bs/kolbot/libs/config/Amazon.js | 1 + d2bs/kolbot/libs/config/Assassin.js | 1 + d2bs/kolbot/libs/config/Barbarian.js | 1 + d2bs/kolbot/libs/config/Druid.js | 1 + d2bs/kolbot/libs/config/Necromancer.js | 1 + d2bs/kolbot/libs/config/Paladin.js | 1 + d2bs/kolbot/libs/config/Sorceress.js | 1 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/libs/scripts/ControlBot.js | 142 ++++++++++++++++++--- 10 files changed, 132 insertions(+), 19 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index bea4ada9a..b1c3a49ae 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -243,6 +243,7 @@ function LoadConfig () { Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index aa14863ff..a8cc624eb 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -243,6 +243,7 @@ function LoadConfig () { Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 1251a91d6..9d3ff7be3 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -243,6 +243,7 @@ function LoadConfig () { Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index e68eb1a7f..cb6e7d05d 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -243,6 +243,7 @@ function LoadConfig () { Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 9edc1c46d..a29b19da5 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -243,6 +243,7 @@ function LoadConfig () { Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 770386fd6..93499873a 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -243,6 +243,7 @@ function LoadConfig () { Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index e9b2a02b7..2dfa8a8e1 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -243,6 +243,7 @@ function LoadConfig () { Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 85eb49951..0de6cf102 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -224,6 +224,7 @@ Scripts.Follower = false; // Script that follows a manually played leader around like a merc. For a list of commands, see Follower.js Scripts.ControlBot = false; Config.ControlBot.Bo = true; // Bo player at waypoint + Config.ControlBot.DropGold = true; // Drop 5k gold on command once per player per game Config.ControlBot.Cows.MakeCows = true; // allow making cows if we can Config.ControlBot.Cows.GetLeg = true; // Get Wirt's Leg from Tristram. If set to false, it will check for the leg in town. Config.ControlBot.Chant.Enchant = true; // enchant player and their minions on command diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 8c3527ee1..e4b9d2952 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -566,6 +566,7 @@ let Config = { }, ControlBot: { Bo: false, + DropGold: false, Cows: { MakeCows: false, GetLeg: false, diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 04b942db5..35e07ac21 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -120,11 +120,14 @@ function ControlBot () { }; }; const MAX_CHAT_LENGTH = 180; + const MIN_GOLD = 500000; const startTime = getTickCount(); const maxTime = Time.minutes(Config.ControlBot.GameLength); const chantDuration = Skill.getDuration(sdk.skills.Enchant); /** @type {Map} */ const players = new Map(); + /** @type {Set} */ + const givenGold = new Set(); const Chat = { /** @type {string[]} */ @@ -775,6 +778,101 @@ function ControlBot () { return false; }; + const pickGoldPiles = function () { + /** @type {PathNode} */ + const startPos = { x: me.x, y: me.y }; + let gold = Game.getItem(sdk.items.Gold); + + if (gold) { + do { + if (gold.onGroundOrDropping && gold.distance <= 20 && Pickit.canPick(gold)) { + Pickit.pickItem(gold) && Chat.overhead("Thank you!", true); + if (startPos.distance > 5) { + Pather.move(startPos); + } + } + } while (gold.getNext()); + } + }; + + const dropGold = function (nick) { + try { + if (me.gold < MIN_GOLD) { + throw new ScriptError("Not enough gold to drop."); + } + if (givenGold.has(nick)) { + throw new ScriptError("Already dropped gold this game for you. Don't be greedy."); + } + + let unit = Game.getPlayer(nick); + + if (unit && unit.distance > 15) { + throw new ScriptError("Get closer."); + } + + if (!unit) { + let partyUnit = getParty(nick); + + if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { + throw new ScriptError("You need to be in one of the towns."); + } + // wait until party area is readable? + Chat.say("Wait for me at waypoint."); + Town.goToTown(sdk.areas.actOf(partyUnit.area)); + + unit = Game.getPlayer(nick); + } + + if (unit) { + if (me.getStat(sdk.stats.Gold) < 5000) { + Town.openStash() && gold(5000, 4); + me.cancelUIFlags(); + } + + // drop the gold + gold(5000); + /** @type {ItemUnit} */ + let droppedGold = Misc.poll(function () { + let _gold = Game.getItem(sdk.items.Gold); + if (_gold && _gold.onGroundOrDropping && _gold.getStat(sdk.stats.Gold) === 5000) { + return _gold; + } + return false; + }, Time.seconds(30), 1000); + + if (!droppedGold) { + throw new ScriptError("Failed to drop gold."); + } + + // watch for the gold dissapearing + let picked = false; + Misc.poll(function () { + let _gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround, droppedGold.gid); + if (_gold) return false; + picked = true; + return !_gold; + }, Time.seconds(30), 1000); + + if (!picked) { + Pickit.pickItem(droppedGold); + throw new ScriptError("Failed to pick gold."); + } else { + givenGold.add(nick); + Chat.say("yw " + nick); + } + } else { + throw new ScriptError("I don't see you"); + } + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } + } + }; + /** * @param {string} nick * @param {string} msg @@ -825,6 +923,8 @@ function ControlBot () { me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); if (name2) { players.set(name1, "*" + name2); + } else { + players.set(name1, ""); } break; @@ -947,6 +1047,14 @@ function ControlBot () { } }); + if (Config.ControlBot.DropGold) { + _actions.set("dropgold", { + desc: "Drop 5k gold", + hostileCheck: false, + run: dropGold + }); + } + if (Config.ControlBot.Chant.Enchant && Skill.canUse(sdk.skills.Enchant)) { _actions.set("chant", { @@ -1099,23 +1207,6 @@ function ControlBot () { return action.run(nick); }; - const pickGoldPiles = function () { - /** @type {PathNode} */ - const startPos = { x: me.x, y: me.y }; - let gold = Game.getItem(sdk.items.Gold); - - if (gold) { - do { - if (gold.onGroundOrDropping && gold.distance <= 20 && Pickit.canPick(gold)) { - Pickit.pickItem(gold) && Chat.overhead("Thank you!", true); - if (startPos.distance > 5) { - Pather.move(startPos); - } - } - } while (gold.getNext()); - } - }; - // START let gameEndWarningAnnounced = false; include("oog/ShitList.js"); @@ -1128,6 +1219,16 @@ function ControlBot () { Town.goToTown(1); Town.move("portalspot"); + // check who is in game in cased we missed the gameevent or this was a restart + let party = getParty(); + if (party) { + do { + if (party.name !== me.name && !players.has(party.name)) { + players.set(party.name, ""); + } + } while (party.getNext()); + } + while (true) { while (greet.length > 0) { let nick = greet.shift(); @@ -1161,8 +1262,11 @@ function ControlBot () { me.act > 1 && Town.goToTown(1); Config.ControlBot.Chant.AutoEnchant && autoChant(); - if (me.gold < 500000) { - Chat.overhead("I am low on gold, to keep this service up please donate by dropping gold near me."); + if (me.gold < MIN_GOLD && players.size > 1) { + Chat.overhead( + "I am low on gold, to keep this service up please donate by dropping gold near me." + + " I need at least " + (MIN_GOLD - me.gold) + " gold." + ); } pickGoldPiles(); From f6d3600bd1d746d6d102f2b67da94c10027ac8a1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 3 Feb 2024 13:57:05 -0500 Subject: [PATCH 358/758] Create extensions.json - Add eslint and vsnip-check to recommendd extensions --- .vscode/extensions.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..b9e9b5e6a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "thebguy.vsnip-check" + ] +} \ No newline at end of file From 730d204a978dbf437634ed1092cbe084f232a1c7 Mon Sep 17 00:00:00 2001 From: Sam L Date: Fri, 15 Mar 2024 14:24:09 -0400 Subject: [PATCH 359/758] Update `.gitignore` file (#405) * Initial commit, issue-404 * Do not ignore .vscode project directory --- .gitignore | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8cff0af1f..d7126c15d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,16 @@ -data/ -images/ -logs/ +# Do not track hidden files and directories by default +.* + +# Track these hidden files +!.eslintrc.js +!.gitignore +!.gitmodules + +# Track these hidden directories +!.github/ +!.vscode/ + +# Do not track user generated data d2bs/kolbot/data/secure/*.txt d2bs/kolbot/data/*.json d2bs/kolbot/logs/*.json @@ -12,3 +22,9 @@ d2bs/kolbot/libs/manualplay/config/*.*.js d2bs/kolbot/libs/soloplay/** d2bs/kolbot/libs/config/*.*.js d2bs/kolbot/D2BotSoloPlay.dbj +data/ +images/ +logs/ + +# Do not track install packages +node_modules/ From 7b8e08366c5f1ee0885f2b9e9f17a5eed21a3dba Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 17 Mar 2024 01:37:38 -0400 Subject: [PATCH 360/758] Add `editorconfig` fix vscode exclusions - Only track the two vscode specific files --- .editorconfig | 8 ++++++++ .gitignore | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..3274ec36d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/.gitignore b/.gitignore index d7126c15d..4ebc561d9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,14 @@ !.eslintrc.js !.gitignore !.gitmodules +!.editorconfig # Track these hidden directories !.github/ -!.vscode/ + +# Track these files from vscodes +!.vscode/settings.json +!.vscode/extensions.json # Do not track user generated data d2bs/kolbot/data/secure/*.txt From a1746f302391d0da5d57f204724931cc69de6f06 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 25 Mar 2024 01:03:48 -0400 Subject: [PATCH 361/758] Revert recursive gem cubing - It needs work in order to not use up so much space, revert back to old method that just cubes flawless when needed --- d2bs/kolbot/libs/core/Cubing.js | 148 +++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 30 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 17a00a11e..3dddb3d9a 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -361,8 +361,9 @@ const Cubing = { for (let i = 0; i < Config.Recipes.length; i += 1) { if (Config.Recipes[i].length > 1 && isNaN(Config.Recipes[i][1])) { - if (NTIPAliasClassID.hasOwnProperty(Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase())) { - Config.Recipes[i][1] = NTIPAliasClassID[Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase()]; + const formattedName = Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase(); + if (NTIPAliasClassID.hasOwnProperty(formattedName)) { + Config.Recipes[i][1] = NTIPAliasClassID[formattedName]; } else { Misc.errorReport("ÿc1Invalid cubing entry:ÿc0 " + Config.Recipes[i][1]); Config.Recipes.splice(i, 1); @@ -404,6 +405,7 @@ const Cubing = { * @property {number} [Ethereal] * @property {boolean} [Enabled] * @property {boolean} [AlwaysEnabled] + * @property {number} [MainRecipe] * * * @todo @@ -708,6 +710,7 @@ const Cubing = { } }, + /** @type {ItemUnit[]} */ validIngredients: [], // What we have neededIngredients: [], // What we need subRecipes: [], @@ -725,14 +728,15 @@ const Cubing = { IngredientLoop: for (let j = 0; j < this.recipes[i].Ingredients.length; j += 1) { + const currIngred = this.recipes[i].Ingredients[j]; 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" && this.gems.flawless.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "gem" && this.gems.normal.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "cgem" && this.gems.chipped.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "hpot" && this.pots.healing.includes(items[k].classid)) - || (this.recipes[i].Ingredients[j] === "mpot" && this.pots.mana.includes(items[k].classid)) - || items[k].classid === this.recipes[i].Ingredients[j]) && this.validItem(items[k], this.recipes[i])) { + if (((currIngred === "pgem" && this.gemList.includes(items[k].classid)) + || (currIngred === "fgem" && this.gems.flawless.includes(items[k].classid)) + || (currIngred === "gem" && this.gems.normal.includes(items[k].classid)) + || (currIngred === "cgem" && this.gems.chipped.includes(items[k].classid)) + || (currIngred === "hpot" && this.pots.healing.includes(items[k].classid)) + || (currIngred === "mpot" && this.pots.mana.includes(items[k].classid)) + || items[k].classid === currIngred) && 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, gid: items[k].gid }); @@ -753,33 +757,117 @@ const Cubing = { } // 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: currIngred, 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 gem recipes (if needed) - if (!this.recipes[i].hasOwnProperty("MainRecipe")) { - // make sure we don't add a subrecipe to a subrecipe - for (let gType of Object.values(Cubing.gems)) { - // skip over cgems - can't cube them - if (gType.includes(sdk.items.gems.Chipped.Amethyst)) continue; - for (let gem of gType) { - if (this.subRecipes.indexOf(gem) === -1 - && (this.recipes[i].Ingredients[j] === gem - || (this.recipes[i].Ingredients[j] === "pgem" && Cubing.gemList.includes(gem)))) { - this.recipes.push({ - Ingredients: [gem - 1, gem - 1, gem - 1], - Index: Recipe.Gem, - AlwaysEnabled: true, - MainRecipe: this.recipes[i].Index - }); - this.subRecipes.push(gem); - } - } - } + // if the recipe is enabled (we have the main item), add gem recipes (if needed) - TODO: make this work + // if (!this.recipes[i].hasOwnProperty("MainRecipe")) { + // // make sure we don't add a subrecipe to a subrecipe + // for (let gType of Object.values(Cubing.gems)) { + // // skip over cgems - can't cube them + // if (gType.includes(sdk.items.gems.Chipped.Amethyst)) continue; + // for (let gem of gType) { + // if (this.subRecipes.indexOf(gem) === -1 + // && (this.recipes[i].Ingredients[j] === gem + // || (this.recipes[i].Ingredients[j] === "pgem" && Cubing.gemList.includes(gem)))) { + // this.recipes.push({ + // Ingredients: [gem - 1, gem - 1, gem - 1], + // Index: Recipe.Gem, + // AlwaysEnabled: true, + // MainRecipe: this.recipes[i].Index + // }); + // this.subRecipes.push(gem); + // } + // } + // } + // } + + // If the recipe is enabled (we have the main item), add flawless gem recipes (if needed) - old method + /** + * @param {number} gemId + * @param {number} mainRecipe + * @returns {recipeObj} + */ + const gemRecipe = function (gemId, mainRecipe) { + return { + Ingredients: [gemId, gemId, gemId], + Index: Recipe.Gem, + AlwaysEnabled: true, + MainRecipe: mainRecipe + }; + }; + // Make perf amethyst + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Amethyst) === -1 + && ( + currIngred === sdk.items.gems.Perfect.Amethyst + || (currIngred === "pgem" && this.gemList.includes(sdk.items.gems.Perfect.Amethyst)) + )) { + this.recipes.push(gemRecipe(sdk.items.gems.Flawless.Amethyst, this.recipes[i].Index)); + this.subRecipes.push(sdk.items.gems.Perfect.Amethyst); + } + + // Make perf topaz + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Topaz) === -1 + && ( + currIngred === sdk.items.gems.Perfect.Topaz + || (currIngred === "pgem" && this.gemList.includes(sdk.items.gems.Perfect.Topaz)) + )) { + this.recipes.push(gemRecipe(sdk.items.gems.Flawless.Topaz, this.recipes[i].Index)); + this.subRecipes.push(sdk.items.gems.Perfect.Topaz); + } + + // Make perf sapphire + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Sapphire) === -1 + && ( + currIngred === sdk.items.gems.Perfect.Sapphire + || (currIngred === "pgem" && this.gemList.includes(sdk.items.gems.Perfect.Sapphire)) + )) { + this.recipes.push(gemRecipe(sdk.items.gems.Flawless.Sapphire, this.recipes[i].Index)); + this.subRecipes.push(sdk.items.gems.Perfect.Sapphire); + } + + // Make perf emerald + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Emerald) === -1 + && ( + currIngred === sdk.items.gems.Perfect.Emerald + || (currIngred === "pgem" && this.gemList.includes(sdk.items.gems.Perfect.Emerald)) + )) { + this.recipes.push(gemRecipe(sdk.items.gems.Flawless.Emerald, this.recipes[i].Index)); + this.subRecipes.push(sdk.items.gems.Perfect.Emerald); + } + + // Make perf ruby + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Ruby) === -1 + && ( + currIngred === sdk.items.gems.Perfect.Ruby + || (currIngred === "pgem" && this.gemList.includes(sdk.items.gems.Perfect.Ruby)) + )) { + this.recipes.push(gemRecipe(sdk.items.gems.Flawless.Ruby, this.recipes[i].Index)); + this.subRecipes.push(sdk.items.gems.Perfect.Ruby); + } + + // Make perf diamond + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Diamond) === -1 + && ( + currIngred === sdk.items.gems.Perfect.Diamond + || (currIngred === "pgem" && this.gemList.includes(sdk.items.gems.Perfect.Diamond)) + )) { + this.recipes.push(gemRecipe(sdk.items.gems.Flawless.Diamond, this.recipes[i].Index)); + this.subRecipes.push(sdk.items.gems.Perfect.Diamond); + } + + // Make perf skull + if (this.subRecipes.indexOf(sdk.items.gems.Perfect.Skull) === -1 + && ( + currIngred === sdk.items.gems.Perfect.Skull + || (currIngred === "pgem" && this.gemList.includes(sdk.items.gems.Perfect.Skull)) + )) { + this.recipes.push(gemRecipe(sdk.items.gems.Flawless.Skull, this.recipes[i].Index)); + this.subRecipes.push(sdk.items.gems.Perfect.Skull); } } } From 7ea308b95525816ff05788ce63d6db070ac9c508 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 25 Mar 2024 01:22:05 -0400 Subject: [PATCH 362/758] [BugFix] Continous Torch/Anni mules not moving to next char - Add a check for torch/anni mules to make next char once they've finished picking items and they successfully picked their charm --- d2bs/kolbot/libs/systems/automule/Mule.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/d2bs/kolbot/libs/systems/automule/Mule.js b/d2bs/kolbot/libs/systems/automule/Mule.js index 3bdcac8ea..419089e1f 100644 --- a/d2bs/kolbot/libs/systems/automule/Mule.js +++ b/d2bs/kolbot/libs/systems/automule/Mule.js @@ -239,6 +239,7 @@ const Mule = { let waitTick = getTickCount(); let rval = "ready"; let override = false; + let pickedUniqueCharm = false; if (!Mule.clearedJunk) { me.getItemsEx() @@ -320,6 +321,12 @@ const Mule = { canFit ? Pickit.pickItem(item) : (rval = "next"); + + // torch and anni handling + if (Mule.mode > 0 && Mule.continuous && (item.isAnni || item.isTorch) && item.isInStorage) { + // we picked up a torch or anni so move to next mule once we are done + pickedUniqueCharm = true; + } } if (rval === "next") { @@ -343,6 +350,10 @@ const Mule = { delay(500); } + if (pickedUniqueCharm) { + return "next"; + } + return rval; }, From 6dedb61cafea0f07bf8206606455a1b0a21837e7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:57:16 -0400 Subject: [PATCH 363/758] Add switch casting functionality to `Skill.cast` - Allow specifying a weaponSlot to cast with and switch back when done --- d2bs/kolbot/libs/core/Skill.js | 166 +++++++++++++++++---------------- 1 file changed, 88 insertions(+), 78 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 3b98af390..79d8ada77 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -562,99 +562,109 @@ }, // Cast a skill on self, Unit or coords - cast: function (skillId, hand, x, y, item) { + cast: function (skillId, hand, x, y, item, weaponSlot = -1) { if (skillId === undefined) throw new Error("Unit.cast: Must supply a skill ID"); - switch (true) { - case me.inTown && !this.townSkill(skillId): - case !item && (this.getManaCost(skillId) > me.mp || !this.canUse(skillId)): - case !this.wereFormCheck(skillId): - return false; - } - - 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)) { - delay(300); + const switchWeapons = weaponSlot > -1; + try { + if (switchWeapons && me.weaponswitch !== weaponSlot) { + me.switchWeapons(weaponSlot); + } + switch (true) { + case me.inTown && !this.townSkill(skillId): + case !item && (this.getManaCost(skillId) > me.mp || !this.canUse(skillId)): + case !this.wereFormCheck(skillId): + return false; } - return false; - } + 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)) { + delay(300); + } - if (skillId === sdk.skills.Teleport) { - if (typeof x === "number") { - const orgDist = [x, y].distance; - if (Packet.teleport(x, y)) { - return Misc.poll(function () { - return [x, y].distance < orgDist; - }, 300, 25); + return false; + } + + if (skillId === sdk.skills.Teleport) { + if (typeof x === "number") { + const orgDist = [x, y].distance; + if (Packet.teleport(x, y)) { + return Misc.poll(function () { + return [x, y].distance < orgDist; + }, 300, 25); + } } } - } - if (!this.setSkill(skillId, hand, item)) return false; + if (!this.setSkill(skillId, hand, item)) return false; - if (Config.PacketCasting > 1) { - if (typeof x === "number") { - Packet.castSkill(hand, x, y); - } else if (typeof x === "object") { - Packet.unitCast(hand, x); - } - delay(250); - } else { - let [clickType, shift] = (function () { - 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]; + if (Config.PacketCasting > 1) { + if (typeof x === "number") { + Packet.castSkill(hand, x, y); + } else if (typeof x === "object") { + Packet.unitCast(hand, x); + } + delay(250); + } else { + let [clickType, shift] = (function () { + 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]; + } + })(); + + 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); + + if (Misc.poll(function () { + return me.attacking; + }, 200, 20)) { + break; + } } - })(); - - 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); - - if (Misc.poll(function () { - return me.attacking; - }, 200, 20)) { - break; + + while (me.attacking) { + delay(10); } } - while (me.attacking) { - delay(10); + // account for lag, state 121 doesn't kick in immediately + if (this.isTimed(skillId)) { + Misc.poll(function () { + return ( + me.skillDelay + || me.mode === sdk.player.mode.GettingHit + || me.mode === sdk.player.mode.Blocking + ); + }, 100, 10); } - } - // account for lag, state 121 doesn't kick in immediately - if (this.isTimed(skillId)) { - Misc.poll(function () { - return ( - me.skillDelay - || me.mode === sdk.player.mode.GettingHit - || me.mode === sdk.player.mode.Blocking - ); - }, 100, 10); + return true; + } finally { + if (switchWeapons) { + me.switchWeapons(Attack.getPrimarySlot()); + } } - - return true; }, /** From cff3e30ee9a57ea99c667a4819d50b77ce8a49aa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 26 Mar 2024 00:29:05 -0400 Subject: [PATCH 364/758] Add `Config.CustomPreAttack` - CustomPreAttack is a record of key type number | string with tuple [number, number] - Add `Attack.doPreAttack(unit: Monster)` - Add `Attack.getCustomPreAttack(unit: Monster)` --- d2bs/kolbot/libs/config/Amazon.js | 23 +++++-- d2bs/kolbot/libs/config/Assassin.js | 23 +++++-- d2bs/kolbot/libs/config/Barbarian.js | 23 +++++-- d2bs/kolbot/libs/config/Druid.js | 23 +++++-- d2bs/kolbot/libs/config/Necromancer.js | 23 +++++-- d2bs/kolbot/libs/config/Paladin.js | 23 +++++-- d2bs/kolbot/libs/config/Sorceress.js | 23 +++++-- d2bs/kolbot/libs/config/_BaseConfigFile.js | 23 +++++-- d2bs/kolbot/libs/core/Attack.js | 67 +++++++++++++++++++- d2bs/kolbot/libs/core/Attacks/Amazon.js | 16 ++--- d2bs/kolbot/libs/core/Attacks/Assassin.js | 15 ++--- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 15 ++--- d2bs/kolbot/libs/core/Attacks/Druid.js | 15 ++--- d2bs/kolbot/libs/core/Attacks/Necromancer.js | 15 ++--- d2bs/kolbot/libs/core/Attacks/Paladin.js | 15 ++--- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 24 ++++--- d2bs/kolbot/libs/core/Config.js | 4 +- 17 files changed, 248 insertions(+), 122 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index b1c3a49ae..2ea5345c2 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -546,15 +546,28 @@ function LoadConfig () { Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] }; + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] + }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; + // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index a8cc624eb..de003441d 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -546,15 +546,28 @@ function LoadConfig () { Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] }; + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] + }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; + // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 9d3ff7be3..96c519d9f 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -543,15 +543,28 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Low mana skill. - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] }; + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] + }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; + // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index cb6e7d05d..5eb44e4f8 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -546,15 +546,28 @@ function LoadConfig () { Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] }; + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] + }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; + // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index a29b19da5..40aa17043 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -546,15 +546,28 @@ function LoadConfig () { Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] }; + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] + }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; + // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 93499873a..8dfb95327 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -546,15 +546,28 @@ function LoadConfig () { Config.LowManaSkill[0] = -1; // Low mana skill. Config.LowManaSkill[1] = -1; // Low mana aura. - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] }; + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] + }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; + // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 2dfa8a8e1..23dd7e686 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -547,15 +547,28 @@ function LoadConfig () { Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] }; + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] + }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; + // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II Config.MFSwitchPercent = 0; // Boss life % to switch to non-primary weapon slot. Set to 0 to disable. diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 0de6cf102..f0b2f0ef6 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -541,14 +541,27 @@ Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) - /* Advanced Attack config. Allows custom skills to be used on custom monsters. - * Format: "Monster Name": [timed skill id, untimed skill id] - * Example: "Baal": [38, -1] to use charged bolt on Baal - * Multiple entries are separated by commas + /** + * Advanced Attack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [timed skill id, untimed skill id] + * Example: "Baal": [38, -1] to use charged bolt on Baal + * Multiple entries are separated by commas */ Config.CustomAttack = { - //"Monster Name": [-1, -1] + // "Monster Name": [-1, -1] + }; + + /** + * Advanced PreAttack config. Allows custom skills to be used on custom monsters. + * Format: "Monster Name": [skill id, weapon slot] + * Example: "Baal": [146, 1] to use battle cry on Baal with weapon slot 1 (switches if necessary) + * Multiple entries are separated by commas + */ + Config.CustomPreAttack = { + // "Monster Name": [-1, -1] }; + // Alternatively, you can use the sdk.monsters.MonsterName and sdk.skills.SkillName enums to avoid typos + // Config.CustomPreAttack[sdk.monsters.Baal] = [sdk.skills.BattleCry, sdk.player.slot.Secondary]; // Weapon slot settings Config.PrimarySlot = -1; // primary weapon slot: -1 = disabled (will try to determine primary slot by using non-cta slot that's not empty), 0 = slot I, 1 = slot II diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 4334b99c9..90f37c99e 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -118,7 +118,6 @@ const Attack = { }, /** - * * @param {Monster} unit * @returns {[number, number] | boolean} * @todo add checking for other options than just name/classid @@ -155,6 +154,43 @@ const Attack = { return false; }, + /** + * @param {Monster} unit + * @returns {[number, number] | boolean} + * @todo add checking for other options than just name/classid + * - option for based on spectype + * - option for based on enchant/aura + */ + getCustomPreAttack: function (unit) { + // Check if unit got invalidated + if (!unit || !unit.name || !copyUnit(unit).x) return false; + + for (let i in Config.CustomPreAttack) { + if (Config.CustomPreAttack.hasOwnProperty(i)) { + // if it contains numbers but is a string, convert to an int + if (typeof i === "string" && i.match(/\d+/g)) { + // @ts-ignore + i = parseInt(i, 10); + } + + switch (typeof i) { + case "string": + if (unit.name.toLowerCase() === i.toLowerCase()) { + return Config.CustomPreAttack[i]; + } + + break; + case "number": + if (unit.classid === i) { + return Config.CustomPreAttack[i]; + } + } + } + } + + return false; + }, + /** * @description Check if player or his merc are using Infinity, and adjust resistance checks based on that * @returns {boolean} @@ -1854,5 +1890,32 @@ const Attack = { return (Attack.validSpot(unit.x, unit.y) && Skill.cast(sdk.skills.Whirlwind, Skill.getHand(sdk.skills.Whirlwind), me.x, me.y)); - } + }, + + /** + * @param {Monster} unit + * @returns {AttackResult} + */ + doPreAttack: function (unit) { + const preAttackInfo = Attack.getCustomPreAttack(unit) + ? Attack.getCustomPreAttack(unit) + : [Config.AttackSkill[0], Attack.getPrimarySlot()]; + preAttackInfo.length < 2 && preAttackInfo.push(Attack.getPrimarySlot()); + const [skill, slot] = preAttackInfo; + + if (skill > 0 + && Attack.checkResist(unit, skill) + && (!me.skillDelay || !Skill.isTimed(skill))) { + if (unit.distance > Skill.getRange(skill) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(skill), sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(skill, Skill.getHand(skill), unit, null, null, slot); + + return Attack.Result.SUCCESS; + } + return Attack.Result.NOOP; + }, }; diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 402b4fdbc..4e8196d64 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -9,6 +9,7 @@ const ClassAttack = { bowCheck: false, lightFuryTick: 0, + /** @param {Monster} unit */ decideSkill: function (unit) { let skills = { timed: -1, untimed: -1 }; if (!unit) return skills; @@ -82,18 +83,11 @@ const ClassAttack = { } } - 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; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; } if (Skill.canUse(sdk.skills.InnerSight)) { diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 5b14f163a..2e396366e 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -39,18 +39,11 @@ const ClassAttack = { } } - 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; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; } let mercRevive = 0; diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index e44b84690..0500ed587 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -42,18 +42,11 @@ const ClassAttack = { } } - if (preattack && Config.AttackSkill[0] > 0 - && Attack.checkResist(unit, Attack.getSkillElement(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; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; } let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index 04ac75f14..59b13658b 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -40,18 +40,11 @@ const ClassAttack = { } } - 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; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; } let mercRevive = 0; diff --git a/d2bs/kolbot/libs/core/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js index 669c5ba74..6503bd420 100644 --- a/d2bs/kolbot/libs/core/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -147,18 +147,11 @@ const ClassAttack = { } } - 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; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; } // only continue if we can actually curse the unit otherwise its a waste of time diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 9b875d01e..a05ceea64 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -39,18 +39,11 @@ const ClassAttack = { } } - 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; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; } let mercRevive = 0; diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 7c00bfe8a..c558c68e7 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -6,6 +6,7 @@ */ const ClassAttack = { + /** @param {Monster} unit */ decideSkill: function (unit) { let skills = { timed: -1, untimed: -1 }; if (!unit || !unit.attackable) return skills; @@ -14,7 +15,9 @@ const ClassAttack = { let 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)) { skills.timed = checkSkill; @@ -25,7 +28,9 @@ const ClassAttack = { } // 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, classid)) { skills.untimed = checkSkill; @@ -93,18 +98,11 @@ const ClassAttack = { } } - 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; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } - - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - - return Attack.Result.SUCCESS; } let useStatic = (Config.StaticList.length > 0 diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index e4b9d2952..204d81005 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -85,7 +85,6 @@ let Config = { Config.Loaded = true; } catch (e2) { if (notify) { - // console.log("ÿc8Error in " + e2.fileName.substring(e2.fileName.lastIndexOf("\\") + 1, e2.fileName.length) + "(line " + e2.lineNumber + "): " + e2.message); console.error(e2); throw new Error("Config.init: Error in character config."); @@ -339,7 +338,10 @@ let Config = { DodgeHP: 100, AttackSkill: [], LowManaSkill: [], + /** @type {Record} */ CustomAttack: {}, + /** @type {Record} */ + CustomPreAttack: {}, TeleStomp: false, NoTele: false, ClearType: false, From ed58e2a16b397700f41e3d3bfc457956e09a8d08 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 26 Mar 2024 01:40:38 -0400 Subject: [PATCH 365/758] Update Baal.js - Increase area used for preattack casting meteor/blizzard/firewall - Track wave number - Use closer position (between right side pillars) if main attack skills is frozen orb as it doesn't have the distance that blizzard/meteor have - Switch back to default position for lister when using frozen orb for safety as the pack can quickly overrun sorc from secondary closer position --- d2bs/kolbot/libs/core/Common/Baal.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 56dce164c..9d051b392 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -1,5 +1,5 @@ /** -* @filename Cows.js +* @filename Baal.js * @author theBGuy * @desc Handle Baal functions * @@ -133,7 +133,7 @@ if (me.getState(sdk.states.SkillDelay)) { delay(50); } else { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-1, 1), 5024); + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-2, 2), 5024 + rand(-2, 2)); } } @@ -178,12 +178,14 @@ let tick = getTickCount(); let totalTick = getTickCount(); + let lastWave = 0; MainLoop: while (true) { if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + const wave = this.checkThrone() || 0; - switch (this.checkThrone()) { + switch (wave) { case 1: Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); @@ -232,12 +234,20 @@ break; } + if (wave > 0 && wave > lastWave) { + lastWave = wave; + } + 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 (Config.AttackSkill[3] === sdk.skills.FrozenOrb && (lastWave < 4)) { + [15106, 5040].distance > 3 && Pather.moveTo(15106, 5040); + } else { + [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); + } break; case sdk.player.class.Paladin: From a0b15846e1c9dbfb52f1cebc7f8a3b7f65e834c0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 26 Mar 2024 03:21:03 -0400 Subject: [PATCH 366/758] Add ChargeCast config info to base class configs - Added it awhile back to _BaseconfigFile.js but never added it to each classes config --- d2bs/kolbot/libs/config/Amazon.js | 9 +++++++++ d2bs/kolbot/libs/config/Assassin.js | 9 +++++++++ d2bs/kolbot/libs/config/Barbarian.js | 9 +++++++++ d2bs/kolbot/libs/config/Druid.js | 9 +++++++++ d2bs/kolbot/libs/config/Necromancer.js | 9 +++++++++ d2bs/kolbot/libs/config/Paladin.js | 9 +++++++++ d2bs/kolbot/libs/config/Sorceress.js | 11 ++++++++++- d2bs/kolbot/libs/config/_BaseConfigFile.js | 17 +++++++++++++---- 8 files changed, 77 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 2ea5345c2..218c068f1 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -545,6 +545,15 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all /** * Advanced Attack config. Allows custom skills to be used on custom monsters. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index de003441d..6e2701cc2 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -545,6 +545,15 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all /** * Advanced Attack config. Allows custom skills to be used on custom monsters. diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 96c519d9f..68b475e07 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -542,6 +542,15 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Low mana skill. + + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all /** * Advanced Attack config. Allows custom skills to be used on custom monsters. diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 5eb44e4f8..985a9beb8 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -545,6 +545,15 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all /** * Advanced Attack config. Allows custom skills to be used on custom monsters. diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 40aa17043..3da2cc53c 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -545,6 +545,15 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. + + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all /** * Advanced Attack config. Allows custom skills to be used on custom monsters. diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 8dfb95327..f777c0fbc 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -545,6 +545,15 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Low mana skill. Config.LowManaSkill[1] = -1; // Low mana aura. + + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all /** * Advanced Attack config. Allows custom skills to be used on custom monsters. diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 23dd7e686..66f6f95de 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -546,7 +546,16 @@ function LoadConfig () { // Low mana skills - these will be used if main skills can't be cast. Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. - + + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + /** * Advanced Attack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [timed skill id, untimed skill id] diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index f0b2f0ef6..a9241fcfc 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -534,10 +534,19 @@ Config.LowManaSkill[0] = -1; // Timed low mana skill. Config.LowManaSkill[1] = -1; // Untimed low mana skill. - Config.ChargeCast = { - skill: sdk.skills.LowerResist, - spectype: 0x7, - }; + /** + * ChargeCast config. + * Allows use of charged skills (experimental) + * Summons are unsupported. + * Switchcasting is supported. + */ + Config.ChargeCast.skill = -1; // Skill to use + Config.ChargeCast.spectype = 0x7; // Monster spectype to use skill on. 0xF = skip normal, 0x7 = champions/bosses, 0 = all + + // Config.ChargeCast = { + // skill: sdk.skills.LowerResist, + // spectype: 0x7, + // }; Config.PacketCasting = 0; // 0 = disable, 1 = packet teleport, 2 = full packet casting. (disables casting animation for increased d2bs stability) From 0e794e04a4e8d088fb7e2be69f4d3390a3da67e3 Mon Sep 17 00:00:00 2001 From: Antiwarden <125026215+Antiwarden@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:46:57 +0100 Subject: [PATCH 367/758] Fix typo in ToolsThread.js (#408) `Irom Golem` -> `Iron Golem` --- d2bs/kolbot/threads/ToolsThread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index b6ad58570..d5a5f89e4 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -402,7 +402,7 @@ function main () { if (ironGolem) { // ironGolem.hpmax is bugged with BO if (ironGolem.hp <= Math.floor(128 * Config.IronGolemChicken / 100)) { - D2Bot.printToConsole("Irom Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); + D2Bot.printToConsole("Iron Golem Chicken in " + getAreaName(me.area), sdk.colors.D2Bot.Red); break; } From d81fbf8de8858d147c63781049da28c1c7f7ff0a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 27 Mar 2024 03:25:40 -0400 Subject: [PATCH 368/758] Simple advertise worker - In case people want to advertise their runs this makes it simple --- d2bs/kolbot/default.dbj | 5 +++ d2bs/kolbot/libs/core/Config.js | 6 +++ d2bs/kolbot/libs/modules/workers/Advertise.js | 40 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 d2bs/kolbot/libs/modules/workers/Advertise.js diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index f763c6778..45ba18403 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -174,6 +174,11 @@ function main () { Config.AntiHostile && load("threads/AntiHostile.js"); + // Advertise + if (Config.Advertise.Enabled) { + require("libs/modules/workers/Advertise"); + } + if (Config.FastPick) { print("ÿc2Fast pickit active."); addEventListener("itemaction", Pickit.itemEvent); diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 204d81005..2d0f3378d 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -417,6 +417,12 @@ let Config = { UseOwnItemFilter: false, }, + Advertise: { + Enabled: false, + Message: "", + Interval: [0, 0], + }, + // Script specific MFLeader: false, Mausoleum: { diff --git a/d2bs/kolbot/libs/modules/workers/Advertise.js b/d2bs/kolbot/libs/modules/workers/Advertise.js new file mode 100644 index 000000000..ef237688a --- /dev/null +++ b/d2bs/kolbot/libs/modules/workers/Advertise.js @@ -0,0 +1,40 @@ +/** +* @filename Advertise.js +* @author theBGuy +* @desc Worker script for advertising in chat +* +*/ + +(function (module, require, Worker) { + // Only load this in global scope + if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { + // handle invalid interval input + if (!Array.isArray(Config.Advertise.Interval)) { + if (typeof Config.Advertise.Interval === "number") { + Config.Advertise.Interval = [Config.Advertise.Interval, Config.Advertise.Interval]; + } else { + Config.Advertise.Interval = [30, 60]; + } + } else if (Config.Advertise.Interval.length < 2) { + if (typeof Config.Advertise.Interval[0] === "number") { + Config.Advertise.Interval.push(Config.Advertise.Interval[0] + rand(0, 30)); + } else { + Config.Advertise.Interval = [30, 60]; + } + } + const [min, max] = Config.Advertise.Interval; + let waitTick = getTickCount() + Time.seconds(rand(min, max)); + + // Start + Worker.runInBackground.Advertise = function () { + if (getTickCount() - waitTick < 0) return true; + waitTick += Time.seconds(rand(min, max)); + + say("!" + Config.Advertise.Message, true); + + return true; + }; + + console.log("ÿc2Kolbotÿc0 :: Advertise running"); + } +})(module, require, typeof Worker === "object" && Worker || require("../Worker")); From 78281e98fed9bccc13cf2ede5f43bac9dfb3c968 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:33:12 -0400 Subject: [PATCH 369/758] Update BattleOrders.js - Add utility method to log any names from the getters array that didn't show up to be bo'ed - Add chat event, instead of being fully reliant on silent tracking - Change totelBoed to a Set --- d2bs/kolbot/libs/scripts/BattleOrders.js | 188 ++++++++++++++--------- 1 file changed, 112 insertions(+), 76 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/BattleOrders.js b/d2bs/kolbot/libs/scripts/BattleOrders.js index e9b484425..acbc3bda2 100644 --- a/d2bs/kolbot/libs/scripts/BattleOrders.js +++ b/d2bs/kolbot/libs/scripts/BattleOrders.js @@ -10,7 +10,10 @@ function BattleOrders () { this.gaveBo = false; - this.totalBoed = []; + /** @type {Set} */ + const totalBoed = new Set(); + /** @type {Set} */ + const boGetters = new Set(Config.BattleOrders.Getters.map(name => name.toLowerCase())); const boMode = { Give: 0, @@ -41,7 +44,7 @@ function BattleOrders () { } catch (e) { if (Config.BattleOrders.Wait) { let counter = 0; - print("Waiting " + Config.BattleOrders.Wait + " seconds for other players..."); + console.log("Waiting " + Config.BattleOrders.Wait + " seconds for other players..."); Misc.poll(() => { counter++; @@ -79,7 +82,10 @@ function BattleOrders () { if (party) { do { if ([ - sdk.areas.MooMooFarm, sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber + sdk.areas.MooMooFarm, + sdk.areas.ChaosSanctuary, + sdk.areas.ThroneofDestruction, + sdk.areas.WorldstoneChamber ].includes(party.area)) { log("ÿc1I'm late to BOs. Moving on..."); @@ -100,7 +106,7 @@ function BattleOrders () { // if we haven't already given a bo, lets wait to see if more players show up if (!BattleOrders.gaveBo) { nearPlayers = Misc.getNearbyPlayerCount(); - while (nearPlayers !== Config.BattleOrders.Getters.length) { + while (nearPlayers !== boGetters.size) { if (getTickCount() - tick >= Time.seconds(30)) { log("Begin"); @@ -117,8 +123,8 @@ function BattleOrders () { } let boed = false; - let playersToBo = getUnits(sdk.unittype.Player) - .filter(p => Config.BattleOrders.Getters.includes(p.name.toLowerCase()) && p.distance < 20); + const playersToBo = getUnits(sdk.unittype.Player) + .filter(p => boGetters.has(p.name.toLowerCase()) && p.distance < 20); playersToBo.forEach(p => { tick = getTickCount(); @@ -138,7 +144,7 @@ function BattleOrders () { delay(1000); } - this.totalBoed.indexOf(p.name.toLowerCase()) === -1 && this.totalBoed.push(p.name.toLowerCase()); + totalBoed.add(p.name.toLowerCase()); console.debug("Bo-ed " + p.name); boed = true; } @@ -177,97 +183,127 @@ function BattleOrders () { // Ready Precast.enabled = true; - MainLoop: - while (true) { - if (Config.BattleOrders.SkipIfTardy && tardy()) { - break; + /** + * @param {string} name + * @param {string} msg + */ + function chatEvent (name, msg) { + if (!msg | !name) return; + if (!boGetters.has(name.toLowerCase())) return; + if (msg === "got-bo") { + console.log(name + " got bo"); + totalBoed.add(name.toLowerCase()); } + } - switch (Config.BattleOrders.Mode) { - case boMode.Give: - // check if anyone is near us - nearPlayer = Game.getPlayer(); + /** @returns {string[]} */ + function getFailedToBO () { + return Config.BattleOrders.Getters.filter(name => !totalBoed.has(name.toLowerCase())); + } - if (nearPlayer) { - do { - if (nearPlayer.name !== me.name) { - let nearPlayerName = nearPlayer.name.toLowerCase(); - // there is a player near us and they are in the list of players to bo and in my party - if (Config.BattleOrders.Getters.includes(nearPlayerName) - && !this.totalBoed.includes(nearPlayerName) && Misc.inMyParty(nearPlayerName)) { - let result = giveBO(); - if (result.success) { - if (result.count === Config.BattleOrders.Getters.length - || this.totalBoed.length === Config.BattleOrders.Getters.length) { - // we bo-ed everyone we are set to, don't wait around any longer - break MainLoop; + try { + if (Config.BattleOrders.Mode === boMode.Give) { + addEventListener("chatmsg", chatEvent); + } + + MainLoop: + while (true) { + if (Config.BattleOrders.SkipIfTardy && tardy()) { + break; + } + + switch (Config.BattleOrders.Mode) { + case boMode.Give: + // check if anyone is near us + nearPlayer = Game.getPlayer(); + + if (nearPlayer) { + do { + if (nearPlayer.name !== me.name) { + let nearPlayerName = nearPlayer.name.toLowerCase(); + // there is a player near us and they are in the list of players to bo and in my party + if (boGetters.has(nearPlayerName) + && !totalBoed.has(nearPlayerName) + && Misc.inMyParty(nearPlayerName)) { + let result = giveBO(); + if (result.success) { + if (result.count === boGetters.size + || totalBoed.size === boGetters.size) { + // we bo-ed everyone we are set to, don't wait around any longer + break MainLoop; + } + // reset fail tick + tick = getTickCount(); + // shorten waiting time since we've already started giving out bo's + BattleOrders.gaveBo = true; } - // reset fail tick - tick = getTickCount(); - // shorten waiting time since we've already started giving out bo's - BattleOrders.gaveBo = true; + } + } else { + me.overhead( + "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + + " Seconds for other players" + ); + + if (getTickCount() - tick >= failTimer) { + log("ÿc1Give BO timeout fail."); + log("Failed to bo: " + getFailedToBO().join(", ")); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + + break MainLoop; } } - } else { - me.overhead( - "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) - + " Seconds for other players" - ); + } while (nearPlayer.getNext()); + } else { + me.overhead( + "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + + " Seconds for other players" + ); - if (getTickCount() - tick >= failTimer) { - log("ÿc1Give BO timeout fail."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + if (getTickCount() - tick >= failTimer) { + log("ÿc1Give BO timeout fail."); + log("Failed to bo: " + getFailedToBO().join(", ")); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - break MainLoop; - } + break MainLoop; } - } while (nearPlayer.getNext()); - } else { - me.overhead( - "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) - + " Seconds for other players" - ); + } - if (getTickCount() - tick >= failTimer) { - log("ÿc1Give BO timeout fail."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + break; + case boMode.Receive: + if (me.getState(sdk.states.BattleOrders)) { + log("Got bo-ed"); + say("got-bo"); + delay(1000); break MainLoop; } - } - - break; - case boMode.Receive: - if (me.getState(sdk.states.BattleOrders)) { - log("Got bo-ed"); - delay(1000); - break MainLoop; - } + if (getTickCount() - tick >= failTimer) { + log("ÿc1BO timeout fail."); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - if (getTickCount() - tick >= failTimer) { - log("ÿc1BO timeout fail."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + break MainLoop; + } - break MainLoop; + break; } - break; + delay(500); } - delay(500); - } + (Pather.useWaypoint(sdk.areas.RogueEncampment) || Town.goToTown()); - (Pather.useWaypoint(sdk.areas.RogueEncampment) || Town.goToTown()); - - // what's the point of this? - if (Config.BattleOrders.Mode === boMode.Give && Config.BattleOrders.Idle) { - for (let i = 0; i < Config.BattleOrders.Getters.length; i += 1) { - while (Misc.inMyParty(Config.BattleOrders.Getters[i])) { - delay(1000); + // what's the point of this? + if (Config.BattleOrders.Mode === boMode.Give && Config.BattleOrders.Idle) { + for (let i = 0; i < Config.BattleOrders.Getters.length; i += 1) { + while (Misc.inMyParty(Config.BattleOrders.Getters[i])) { + delay(1000); + } } } - } - return true; + return true; + } finally { + removeEventListener("chatmsg", chatEvent); + } } From ff531e5c7207d8be84723e11e4e0fdb568fa2f31 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:17:44 -0400 Subject: [PATCH 370/758] Update Pather.js - Fix tele-switch to swap back to primary once done #390 @samlitowitz --- d2bs/kolbot/libs/core/Pather.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 2a1c9510f..fc9e1f6dd 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -333,6 +333,7 @@ const Pather = { const leaped = new PathAction(); const whirled = new PathAction(); const cleared = new PathAction(); + const primarySlot = Attack.getPrimarySlot(); // for tele-switch for (let i = 0; i < this.cancelFlags.length; i += 1) { getUIFlag(this.cancelFlags[i]) && me.cancel(); @@ -341,7 +342,9 @@ const Pather = { if (typeof target.x !== "number" || typeof target.y !== "number") { throw new Error("move: Coords must be numbers"); } - if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, sdk.collision.BlockMissile, 5)) return true; + if (getDistance(me, target) < 2 && !CollMap.checkColl(me, target, sdk.collision.BlockMissile, 5)) { + return true; + } let useTeleport = settings.allowTeleport && this.useTeleport(); const tpMana = useTeleport ? Skill.getManaCost(sdk.skills.Teleport) : Infinity; @@ -364,7 +367,7 @@ const Pather = { 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.switchWeapons(primarySlot ^ 1); while (path.length > 0) { // Abort if dead @@ -380,7 +383,7 @@ const Pather = { if (typeof settings.callback === "function" && settings.callback()) { console.debug("Callback function passed. Ending path."); - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + useTeleport && Config.TeleSwitch && me.switchWeapons(primarySlot); PathDebug.removeHooks(); return true; } @@ -414,8 +417,6 @@ const Pather = { Pather.recursion = true; } } - - // settings.allowTown && Misc.townCheck(); } } else { if (!me.inTown) { @@ -509,14 +510,13 @@ const Pather = { delay(5); } - useTeleport && Config.TeleSwitch && me.switchWeapons(Attack.getPrimarySlot() ^ 1); + useTeleport && Config.TeleSwitch && me.switchWeapons(primarySlot); PathDebug.removeHooks(); return getDistance(me, node.x, node.y) < 5; }, /** - * * @param {number} x * @param {number} y * @param {number} minDist From 445760f86a58858fa371dcfaf14412251af9db03 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:07:21 -0400 Subject: [PATCH 371/758] [BugFix] Add missing bonearmor state to skilldata --- d2bs/kolbot/libs/core/GameData/SkillData.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 30d8ce1ea..e953ac8f2 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -385,6 +385,7 @@ }); skillMap.set(sdk.skills.BoneArmor, { hand: sdk.skills.hand.Right, + state: sdk.states.BoneArmor, range: 1, }); skillMap.set(sdk.skills.SkeletonMastery, { From 82e17be9f5a56ba016e0c8407f392ecfa8ca5e3c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 30 Mar 2024 00:49:24 -0400 Subject: [PATCH 372/758] [BugFix] MFSwitchPercent - Update to correctly switch to non-primary slot - Wrap in try-finally in order to ensure we switch back when done Fixes #396 --- d2bs/kolbot/libs/core/Attack.js | 104 ++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 90f37c99e..1cf832ee5 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -268,6 +268,11 @@ const Attack = { return Attack.clear(10); } + /** + * @param {number} gid + * @param {PathNode} loc + * @returns {Unit | boolean} + */ const findTarget = function (gid, loc) { let path = getPath(me.area, me.x, me.y, loc.x, loc.y, 1, 5); if (!path) return false; @@ -284,6 +289,8 @@ const Attack = { const who = (!!target.name ? target.name : classId); const gid = target.gid; + const primarySlot = Attack.getPrimarySlot(); // for mfswitch + const currentScript = Loader.scriptName(0).toLowerCase(); let retry = 0; let errorInfo = ""; @@ -292,70 +299,80 @@ const Attack = { let lastLoc = { x: me.x, y: me.y }; let tick = getTickCount(); console.log("ÿc7Kill ÿc0:: " + who); + if (Config.MFLeader // mfhelper is disabled for these scripts so announcing is pointless - && !Loader.scriptName(0).toLowerCase().includes("diablo") - && !Loader.scriptName(0).toLowerCase().includes("baal") + && !currentScript.includes("diablo") + && !currentScript.includes("baal") && Pather.makePortal()) { say("kill " + classId); } - 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)); + try { + 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; + } + } - if (!target) { - console.warn("ÿc1Failed to kill " + who + " (couldn't relocate unit)"); - break; + // todo - dodge boss missiles + Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); + if (Config.MFSwitchPercent && target.hpPercent < Config.MFSwitchPercent) { + me.switchWeapons(primarySlot ^ 1); } - } - // todo - dodge boss missiles - Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); - Config.MFSwitchPercent && target.hpPercent < Config.MFSwitchPercent && me.switchToPrimary(); + if (attackCount > 0 && attackCount % 15 === 0 && Skill.getRange(Config.AttackSkill[1]) < 4) { + Packet.flash(me.gid); + } - if (attackCount > 0 && attackCount % 15 === 0 && Skill.getRange(Config.AttackSkill[1]) < 4) { - Packet.flash(me.gid); - } + let result = ClassAttack.doAttack(target, attackCount % 15 === 0); - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + if (result === this.Result.FAILED) { + if (retry++ > 3) { + errorInfo = " (doAttack failed)"; - 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; } - 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++; } - lastLoc = { x: me.x, y: me.y }; - attackCount++; - } + attackCount === Config.MaxAttackCount && (errorInfo = " (attackCount exceeded: " + attackCount + ")"); + Config.MFSwitchPercent && me.switchWeapons(primarySlot); + ClassAttack.afterAttack(); + Pickit.pickItems(); - attackCount === Config.MaxAttackCount && (errorInfo = " (attackCount exceeded: " + attackCount + ")"); - Config.MFSwitchPercent && me.switchWeapons(this.getPrimarySlot()); - 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)); + } - 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)); + return (!target || !copyUnit(target).x || target.dead || !target.attackable); + } finally { + // make sure we switch back to primary weapon + if (Config.MFSwitchPercent) { + me.switchWeapons(primarySlot); + } } - - return (!target || !copyUnit(target).x || target.dead || !target.attackable); }, /** @@ -534,7 +551,6 @@ const Attack = { boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); monsterList.sort(sortfunc); - // target = copyUnit(monsterList[0]); target = Game.getMonster(-1, -1, monsterList[0].gid); if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range From 3433e389b97cd0ccb416ce4d86c45705f5d5cc43 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 30 Mar 2024 04:01:44 -0400 Subject: [PATCH 373/758] [Feat] Difficulty + mode announcement in channel - Leader: Modify our game announcement message to include the difficulty we are making a game for and the mode (sc, hc) + ladder or non-ladder - ChannelFollower: Parse new info from game announcement and skip games we'd be unable to join --- d2bs/kolbot/D2BotChannel.dbj | 31 ++++++++++++++++++++++++-- d2bs/kolbot/libs/OOG.js | 37 +++++++++++++++++++++++++++++-- d2bs/kolbot/libs/oog/Locations.js | 20 ++++++++++++----- 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index f2cfbc27f..223717d5a 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -297,15 +297,42 @@ const locationAction = (function () { if (!chat.name || chat.name.split("*")[0] === me.charname) continue; // is this a game announcement? if (!chat.msg.toLowerCase().includes("next game is")) continue; + // eslint-disable-next-line max-len + const gameRegex = new RegExp(/Next game is\s+([a-zA-Z0-9_-]+)(?:\/\/(\w+))?(?:\s+in\s+(\w+))?(?:\s+on\s+(\w+))?/gm); // capture the game name and password if there is one - let match = new RegExp(/^Next game is\s+([a-zA-Z0-9_-]+)(?:\/\/(\w+))?$/gm).exec(chat.msg); + let match = gameRegex.exec(chat.msg); if (!match) continue; // extract capture groups from regex - let [, gameName, gamePass] = match; + let [, gameName, gamePass, diff, mode] = match; // double check that this is a valid game name if (gameName.length > 15 || badGames.has(gameName) || exclude(gameName)) continue; + // TODO: handle difficulty + if (diff) { + // + } + + // handle mode - TODO: handle classic vs expansion (gametype doesn't seem to be set yet in lobby) + if (mode) { + mode = mode.toLowerCase(); + // can't join nl games if we are ladder + if (mode.includes("nl") && me.ladder) { + // console.debug("Skipping NL game: " + gameName + " " + gamePass + " " + diff + " " + mode); + continue; + } + // can't join hardcore games if we are softcore + if (mode.includes("hc") && me.softcore) { + // console.debug("Skipping HC game: " + gameName + " " + gamePass + " " + diff + " " + mode); + continue; + } + // can't join softcore games if we are hardcore + if (mode.includes("sc") && me.hardcore) { + // console.debug("Skipping SC game: " + gameName + " " + gamePass + " " + diff + " " + mode); + continue; + } + } + // handle following player names if (ChannelConfig.Follow.length > 0) { for (let follow of ChannelConfig.Follow) { diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 7aec7cf3c..8d46f973c 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -27,6 +27,29 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }([].filter.constructor("return this")(), function () { const Controls = require("./modules/Control"); + Object.defineProperties(me, { + classic: { + get: function () { + return me.gametype === sdk.game.gametype.Classic; + } + }, + expansion: { + get: function () { + return me.gametype === sdk.game.gametype.Expansion; + } + }, + softcore: { + get: function () { + return me.playertype === false; + } + }, + hardcore: { + get: function () { + return me.playertype === true; + } + }, + }); + const ControlAction = { mutedKey: false, realms: { @@ -808,13 +831,16 @@ includeIfNotIncluded("oog/D2Bot.js"); // required break; case "Highest": if (Controls.Hell.disabled !== 4 && Controls.Hell.click()) { + diff = "Hell"; break; } if (Controls.Nightmare.disabled !== 4 && Controls.Nightmare.click()) { + diff = "Nightmare"; break; } + diff = "Normal"; Controls.Normal.click(); break; @@ -827,12 +853,19 @@ includeIfNotIncluded("oog/D2Bot.js"); // required !!delay && this.timeoutDelay("Make Game Delay", delay); if (Starter.chanInfo.announce) { - Starter.sayMsg("Next game is " + name + (pass === "" ? "" : "//" + pass)); + const pType = me.hardcore ? "hc" : "sc"; + const ladder = me.ladder ? "l" : "nl"; + Starter.sayMsg( + "Next game is " + name + + (pass === "" ? "" : "//" + pass) + + " in " + diff + + " on " + (pType + ladder) + ); } me.blockMouse = true; - print("Creating Game: " + name); + console.log("Creating Game: " + name); Controls.CreateGame.click(); me.blockMouse = false; diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index 1df678076..4ee5c974b 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -208,7 +208,7 @@ Starter.pingQuit = false; } - if (Starter.Config.JoinChannel !== "") { + if (Starter.Config.JoinChannel !== "" || Starter.Config.AnnounceGames) { Controls.LobbyEnterChat.click(); return; @@ -261,7 +261,10 @@ !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()); + ControlAction.timeoutDelay( + "Min game time wait", + Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount() + ); } } @@ -313,8 +316,12 @@ Starter.chanInfo.firstMsg = Starter.Config.FirstJoinMessage; if (Starter.chanInfo.joinChannel) { - typeof Starter.chanInfo.joinChannel === "string" && (Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]); - typeof Starter.chanInfo.firstMsg === "string" && (Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]); + if (typeof Starter.chanInfo.joinChannel === "string") { + Starter.chanInfo.joinChannel = [Starter.chanInfo.joinChannel]; + } + if (typeof Starter.chanInfo.firstMsg === "string") { + Starter.chanInfo.firstMsg = [Starter.chanInfo.firstMsg]; + } for (let i = 0; i < Starter.chanInfo.joinChannel.length; i += 1) { ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); @@ -322,7 +329,7 @@ if (ControlAction.joinChannel(Starter.chanInfo.joinChannel[i])) { Starter.useChat = true; } else { - print("ÿc1Unable to join channel, disabling chat messages."); + console.warn("ÿc1Unable to join channel, disabling chat messages."); Starter.useChat = false; } @@ -331,6 +338,9 @@ delay(500); } } + } else if (Starter.Config.AnnounceGames) { + // announcing in public channel + Starter.useChat = true; } } From 0eef1ce771792f36834df41f744c9265da0d55af Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:15:20 -0400 Subject: [PATCH 374/758] Update Misc.js - Prep for using shrinedata module. - Added jsdoc comments for methods that were missing them --- d2bs/kolbot/libs/core/Misc.js | 1430 +++++++++++++++++---------------- 1 file changed, 745 insertions(+), 685 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index b5fbcab5c..7b0c1ca9b 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -6,830 +6,890 @@ * */ -const Misc = { - /** - * Click something - * @param {number} button - * @param {number} shift - * @param {number | Unit} [x] - * @param {number} [y] - * @returns {boolean} - */ - click: function (button, shift, x, y) { - if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); - - while (!me.gameReady) { - delay(100); - } - - switch (arguments.length) { - case 2: - me.blockMouse = true; - clickMap(button, shift, me.x, me.y); - delay(20); - clickMap(button + 2, shift, me.x, me.y); - me.blockMouse = false; - - break; - case 3: - if (typeof (x) !== "object") throw new Error("Misc.click: Third arg must be a Unit."); - - me.blockMouse = true; - clickMap(button, shift, x); - delay(20); - clickMap(button + 2, shift, x); - me.blockMouse = false; - - break; - case 4: - me.blockMouse = true; - clickMap(button, shift, x, y); - delay(20); - clickMap(button + 2, shift, x, y); - me.blockMouse = false; - - break; - } - - return true; - }, +const Misc = (function () { + return { + /** + * Click something + * @param {number} button + * @param {number} shift + * @param {number | Unit} [x] + * @param {number} [y] + * @returns {boolean} + */ + click: function (button, shift, x, y) { + if (arguments.length < 2) throw new Error("Misc.click: Needs at least 2 arguments."); + + while (!me.gameReady) { + delay(100); + } - /** - * Check if a player is in your party - * @param {string} name - * @returns {boolean} - */ - inMyParty: function (name) { - if (me.name === name) return true; + switch (arguments.length) { + case 2: + me.blockMouse = true; + clickMap(button, shift, me.x, me.y); + delay(20); + clickMap(button + 2, shift, me.x, me.y); + me.blockMouse = false; + + break; + case 3: + if (typeof (x) !== "object") throw new Error("Misc.click: Third arg must be a Unit."); + + me.blockMouse = true; + clickMap(button, shift, x); + delay(20); + clickMap(button + 2, shift, x); + me.blockMouse = false; + + break; + case 4: + me.blockMouse = true; + clickMap(button, shift, x, y); + delay(20); + clickMap(button + 2, shift, x, y); + me.blockMouse = false; + + break; + } - while (!me.gameReady) { - delay(100); - } + return true; + }, - let player, myPartyId; + /** + * Check if a player is in your party + * @param {string} name + * @returns {boolean} + */ + inMyParty: function (name) { + if (me.name === name) return true; - try { - player = getParty(); - if (!player) return false; + while (!me.gameReady) { + delay(100); + } - myPartyId = player.partyid; - player = getParty(name); // May throw an error + let player, myPartyId; - if (player && player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { - return true; - } - } catch (e) { - player = getParty(); + try { + player = getParty(); + if (!player) return false; - if (player) { myPartyId = player.partyid; + player = getParty(name); // May throw an error - while (player.getNext()) { - if (player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { - return true; - } + if (player && player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { + return true; } - } - } - - return false; - }, + } catch (e) { + player = getParty(); - // Find a player - findPlayer: function (name) { - let player = getParty(); + if (player) { + myPartyId = player.partyid; - if (player) { - do { - if (player.name !== me.name && player.name === name) { - return player; + while (player.getNext()) { + if (player.partyid !== sdk.party.NoParty && player.partyid === myPartyId) { + return true; + } + } } - } while (player.getNext()); - } - - return false; - }, + } - // Get player unit - getPlayerUnit: function (name) { - let player = Game.getPlayer(name); + return false; + }, - if (player) { - do { - if (!player.dead) { - return player; - } - } while (player.getNext()); - } + /** + * Find a player + * @param {string} name + * @returns {Party | false} + */ + findPlayer: function (name) { + let player = getParty(); - return false; - }, + if (player) { + do { + if (player.name !== me.name && player.name === name) { + return player; + } + } while (player.getNext()); + } - // Get the player act, accepts party unit or name - getPlayerAct: function (player) { - if (!player) return false; + return false; + }, - let unit = (typeof player === "object" ? player : this.findPlayer(player)); + /** + * Get player unit + * @param {string} name + * @returns {Player | false} + */ + getPlayerUnit: function (name) { + let player = Game.getPlayer(name); - return unit ? sdk.areas.actOf(unit.area) : false; - }, + if (player) { + do { + if (!player.dead) { + return player; + } + } while (player.getNext()); + } - // Get number of players within getUnit distance - getNearbyPlayerCount: function () { - let count = 0; - let player = Game.getPlayer(); + return false; + }, + + /** + * Get the player act, accepts party unit or name + * @param {Party | string} player + * @returns {number | false} + */ + getPlayerAct: function (player) { + if (!player) return false; - if (player) { - do { - if (player.name !== me.name && !player.dead) { - count += 1; - } - } while (player.getNext()); - } + let unit = (typeof player === "object" ? player : this.findPlayer(player)); - return count; - }, + return unit ? sdk.areas.actOf(unit.area) : false; + }, - // Get total number of players in game - getPlayerCount: function () { - let count = 0; - let party = getParty(); + /** + * Get number of players within getUnit distance + * @returns {number} + */ + getNearbyPlayerCount: function () { + let count = 0; + let player = Game.getPlayer(); - if (party) { - do { - count += 1; - } while (party.getNext()); - } + if (player) { + do { + if (player.name !== me.name && !player.dead) { + count += 1; + } + } while (player.getNext()); + } - return count; - }, + return count; + }, - // Get total number of players in game and in my party - getPartyCount: function () { - let count = 0; - let party = getParty(); + /** + * Get total number of players in game + * @returns {number} + */ + getPlayerCount: function () { + let count = 0; + let party = getParty(); - if (party) { - let myPartyId = party.partyid; - - do { - if (party.partyid !== sdk.party.NoParty - && party.partyid === myPartyId - && party.name !== me.name) { - print(party.name); + if (party) { + do { count += 1; - } - } while (party.getNext()); - } - - return count; - }, - - // check if any member of our party meets a certain level req - checkPartyLevel: function (levelCheck = 1, exclude = []) { - !Array.isArray(exclude) && (exclude = [exclude]); - let party = getParty(); - - if (party) { - let myPartyId = party.partyid; + } while (party.getNext()); + } - do { - if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId - && party.name !== me.name && !exclude.includes(party.name)) { - if (party.level >= levelCheck) { - return true; + return count; + }, + + /** + * Get total number of players in game and in my party + * @returns {number} + */ + getPartyCount: function () { + let count = 0; + let party = getParty(); + + if (party) { + let myPartyId = party.partyid; + + do { + if (party.partyid !== sdk.party.NoParty + && party.partyid === myPartyId + && party.name !== me.name) { + console.log(party.name); + count += 1; } - } - } while (party.getNext()); - } - - return false; - }, - - getPlayerArea: function (player) { - if (!player) return false; + } while (party.getNext()); + } - let unit = (typeof player === "object" ? player : this.findPlayer(player)); + return count; + }, + + /** + * Check if any member of our party meets a certain level req + * @param {number} levelCheck + * @param {string | string[]} exclude + * @returns {boolean} + */ + checkPartyLevel: function (levelCheck = 1, exclude = []) { + !Array.isArray(exclude) && (exclude = [exclude]); + let party = getParty(); + + if (party) { + let myPartyId = party.partyid; + + do { + if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId + && party.name !== me.name && !exclude.includes(party.name)) { + if (party.level >= levelCheck) { + return true; + } + } + } while (party.getNext()); + } - return !!unit ? unit.area : 0; - }, + return false; + }, - /** - * autoleader by Ethic - refactored by theBGuy - * Autodetect leader for leech scripts by looking to see who first enters a certain area - * @param {{ destination: number | number[], quitIf?: Function, timeout?: number }} givenSettings - * @returns - */ - autoLeaderDetect: function (givenSettings = {}) { - const settings = Object.assign({}, { - destination: -1, - quitIf: false, - timeout: Infinity - }, givenSettings); + /** + * @param {Player | string} player + * @returns {number | false} + */ + getPlayerArea: function (player) { + if (!player) return false; - // make destination an array so it's easier to handle both cases - !Array.isArray(settings.destination) && (settings.destination = [settings.destination]); + let unit = (typeof player === "object" ? player : this.findPlayer(player)); + + return !!unit ? unit.area : 0; + }, + + /** + * autoleader by Ethic - refactored by theBGuy + * Autodetect leader for leech scripts by looking to see who first enters a certain area + * @param {{ destination: number | number[], quitIf?: Function, timeout?: number }} givenSettings + * @returns + */ + autoLeaderDetect: function (givenSettings = {}) { + const settings = Object.assign({}, { + destination: -1, + quitIf: false, + timeout: Infinity + }, givenSettings); + + // make destination an array so it's easier to handle both cases + !Array.isArray(settings.destination) && (settings.destination = [settings.destination]); + + let leader; + let startTick = getTickCount(); + let check = typeof settings.quitIf === "function"; + do { + let solofail = 0; + let suspect = getParty(); // get party object (players in game) - let leader; - let startTick = getTickCount(); - let check = typeof settings.quitIf === "function"; - do { - let solofail = 0; - let suspect = getParty(); // get party object (players in game) + do { + // player isn't alone + suspect.name !== me.name && (solofail += 1); - do { - // player isn't alone - suspect.name !== me.name && (solofail += 1); + if (check && settings.quitIf(suspect.area)) return false; - if (check && settings.quitIf(suspect.area)) return false; + // first player not hostile found in destination area... + if (settings.destination.includes(suspect.area) && !getPlayerFlag(me.gid, suspect.gid, 8)) { + leader = suspect.name; // ... is our leader + console.log("ÿc4Autodetected " + leader); - // first player not hostile found in destination area... - if (settings.destination.includes(suspect.area) && !getPlayerFlag(me.gid, suspect.gid, 8)) { - leader = suspect.name; // ... is our leader - console.log("ÿc4Autodetected " + leader); + return leader; + } + } while (suspect.getNext()); - return leader; + // empty game, nothing left to do. Or we exceeded our wait time + if (solofail === 0 || (getTickCount() - startTick > settings.timeout)) { + return false; } - } while (suspect.getNext()); - // empty game, nothing left to do. Or we exceeded our wait time - if (solofail === 0 || (getTickCount() - startTick > settings.timeout)) { + delay(500); + } while (!leader); // repeat until leader is found (or until game is empty) + + return false; + }, + + /** + * @description Open a chest Unit (takes chestID or unit) + * @param {Unit | number} unit + * @returns {boolean} If we opened the chest + */ + 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; } - delay(500); - } while (!leader); // repeat until leader is found (or until game is empty) - - return false; - }, - - /** - * @description Open a chest Unit (takes chestID or unit) - * @param {Unit | number} unit - * @returns {boolean} If we opened the chest - */ - 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); - 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); + if (useTK) { + unit.distance > 13 && Attack.getIntoPosition(unit, 13, sdk.collision.WallOrRanged); + if (!Packet.telekinesis(unit)) { + console.debug("Failed to tk: attempt: " + i); + continue; + } + } else { + [(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); + } - 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); - if (useTK) { - unit.distance > 13 && Attack.getIntoPosition(unit, 13, sdk.collision.WallOrRanged); - if (!Packet.telekinesis(unit)) { - console.debug("Failed to tk: attempt: " + i); - continue; + if (Misc.poll(() => unit.mode, 1000, 50)) { + return true; } - } else { - [(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); + Packet.flash(me.gid); } - if (Misc.poll(() => unit.mode, 1000, 50)) { - return true; + // Click to stop walking in case we got stuck + !me.idle && Misc.click(0, 0, me.x, me.y); + + return false; + }, + + /** + * Open all chests that have preset units in an area + * @param {number} area + * @param {number[]} chestIds + * @returns {boolean} + */ + openChestsInArea: function (area, chestIds = []) { + !area && (area = me.area); + area !== me.area && Pather.journeyTo(area); + + let presetUnits = Game.getPresetObjects(area); + if (!presetUnits) return false; + + if (!chestIds.length) { + chestIds = [ + 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 + ]; } - 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; - }, - - // Open all chests that have preset units in an area - openChestsInArea: function (area, chestIds = []) { - !area && (area = me.area); - area !== me.area && Pather.journeyTo(area); - - let presetUnits = Game.getPresetObjects(area); - if (!presetUnits) return false; - - if (!chestIds.length) { - chestIds = [ - 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 - ]; - } + let coords = []; - 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 + }); + } - 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(); } - presetUnits.shift(); - } + while (coords.length) { + coords.sort(Sort.units); + Pather.moveToUnit(coords[0], 1, 2); + this.openChests(20); - while (coords.length) { - coords.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(); + 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; - }, - - openChests: function (range = 15) { - if (!Config.OpenChests.Enabled) return true; - - let unitList = []; - let containers = []; - - // Testing all container code - if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { - containers = [ - "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", - "casket", "armorstand", "weaponrack", "barrel", "holeanim", "tomb2", - "tomb3", "roguecorpse", "ratnest", "corpse", "goo pile", "largeurn", - "urn", "chest3", "jug", "skeleton", "guardcorpse", "sarcophagus", "object2", - "cocoon", "basket", "stash", "hollow log", "hungskeleton", "pillar", - "skullpile", "skull pile", "jar3", "jar2", "jar1", "bonechest", "woodchestl", - "woodchestr", "barrel wilderness", "burialchestr", "burialchestl", "explodingchest", - "chestl", "chestr", "groundtomb", "icecavejar1", "icecavejar2", - "icecavejar3", "icecavejar4", "deadperson", "deadperson2", "evilurn", "tomb1l", "tomb3l", "groundtombl" - ]; - } else { - containers = Config.OpenChests.Types; - } + return true; + }, + + /** + * @param {number} range + * @returns {boolean} + */ + openChests: function (range = 15) { + if (!Config.OpenChests.Enabled) return true; + + let unitList = []; + let containers = []; + + // Testing all container code + if (Config.OpenChests.Types.some((el) => el.toLowerCase() === "all")) { + containers = [ + "chest", "loose rock", "hidden stash", "loose boulder", "corpseonstick", + "casket", "armorstand", "weaponrack", "barrel", "holeanim", "tomb2", + "tomb3", "roguecorpse", "ratnest", "corpse", "goo pile", "largeurn", + "urn", "chest3", "jug", "skeleton", "guardcorpse", "sarcophagus", "object2", + "cocoon", "basket", "stash", "hollow log", "hungskeleton", "pillar", + "skullpile", "skull pile", "jar3", "jar2", "jar1", "bonechest", "woodchestl", + "woodchestr", "barrel wilderness", "burialchestr", "burialchestl", "explodingchest", + "chestl", "chestr", "groundtomb", "icecavejar1", "icecavejar2", + "icecavejar3", "icecavejar4", "deadperson", "deadperson2", "evilurn", "tomb1l", "tomb3l", "groundtombl" + ]; + } else { + containers = Config.OpenChests.Types; + } - let unit = Game.getObject(); + let unit = Game.getObject(); - if (unit) { - do { - if (unit.name && unit.mode === sdk.objects.mode.Inactive - && getDistance(me.x, me.y, unit.x, unit.y) <= range - && containers.includes(unit.name.toLowerCase())) { - unitList.push(copyUnit(unit)); - } - } while (unit.getNext()); - } + if (unit) { + do { + if (unit.name && unit.mode === sdk.objects.mode.Inactive + && getDistance(me.x, me.y, unit.x, unit.y) <= range + && containers.includes(unit.name.toLowerCase())) { + unitList.push(copyUnit(unit)); + } + } while (unit.getNext()); + } - while (unitList.length > 0) { - unitList.sort(Sort.units); - unit = unitList.shift(); + while (unitList.length > 0) { + unitList.sort(Sort.units); + unit = unitList.shift(); - if (unit) { - const chest = Game.getObject(-1, -1, unit.gid); - if (chest && (Pather.useTeleport() || !checkCollision(me, chest, sdk.collision.WallOrRanged)) - && this.openChest(chest)) { - Pickit.pickItems(); + if (unit) { + const chest = Game.getObject(-1, -1, unit.gid); + if (chest && (Pather.useTeleport() || !checkCollision(me, chest, sdk.collision.WallOrRanged)) + && this.openChest(chest)) { + Pickit.pickItems(); + } } } - } - return true; - }, - - shrineStates: false, - - scanShrines: function (range, ignore = []) { - if (!Config.ScanShrines.length) return false; - - !range && (range = Pather.useTeleport() ? 25 : 15); - !Array.isArray(ignore) && (ignore = [ignore]); - - let shrineList = []; - - // Initiate shrine states - if (!this.shrineStates) { - Misc.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; + return true; + }, + + /** @type {number[] | null} */ + shrineStates: null, + + /** + * @param {number} range + * @param {number[]} ignore + * @returns {boolean} + */ + scanShrines: function (range, ignore = []) { + if (!Config.ScanShrines.length) return false; + + !range && (range = Pather.useTeleport() ? 25 : 15); + !Array.isArray(ignore) && (ignore = [ignore]); + + let shrineList = []; + + // Initiate shrine states + if (!this.shrineStates) { + Misc.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 shrine = Game.getObject("shrine"); + let shrine = Game.getObject("shrine"); - if (shrine) { - let index = -1; - // Build a list of nearby shrines - do { - if (shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) - && getDistance(me.x, me.y, shrine.x, shrine.y) <= range) { - shrineList.push(copyUnit(shrine)); - } - } while (shrine.getNext()); + if (shrine) { + let index = -1; + // Build a list of nearby shrines + do { + if (shrine.mode === sdk.objects.mode.Inactive + && !ignore.includes(shrine.objtype) + && getDistance(me.x, me.y, shrine.x, shrine.y) <= range) { + shrineList.push(copyUnit(shrine)); + } + } while (shrine.getNext()); - // 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; + // 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; + break; + } } - } - for (let i = 0; i < Config.ScanShrines.length; i += 1) { - for (let j = 0; j < shrineList.length; j += 1) { - // 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]); - - // Gem shrine - pick gem - if (Config.ScanShrines[i] === sdk.shrines.Gem) { - Pickit.pickItems(); + for (let i = 0; i < Config.ScanShrines.length; i += 1) { + for (let j = 0; j < shrineList.length; j += 1) { + // 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]); + + // Gem shrine - pick gem + if (Config.ScanShrines[i] === sdk.shrines.Gem) { + Pickit.pickItems(); + } } } } } } - } - - return true; - }, - // Use a shrine Unit - getShrine: function (unit) { - if (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); - if (!Packet.telekinesis(unit)) { - Attack.getIntoPosition(unit, 20, sdk.collision.WallOrRanged); + return true; + }, + + /** + * Use a shrine Unit + * @param {ObjectUnit} unit + * @returns {boolean} + */ + getShrine: function (unit) { + if (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); + 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); + } } - } else { - if (getDistance(me, unit) < 4 || Pather.moveToUnit(unit, 3, 0)) { - Misc.click(0, 0, unit); + + if (Misc.poll(() => unit.mode, 1000, 40)) { + return true; } } - if (Misc.poll(() => unit.mode, 1000, 40)) { - return true; - } - } + return false; + }, + + /** + * 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 + * - Sometimes it seems like calling getPresetObjects to quickly after taking an exit causes a crash, only anecdotal evidence though. Test delays + * - Add the rest of the preset shrine id's to look for + */ + getShrinesInArea: function (area, type, use) { + let shrineLocs = []; + let shrineIds = [2, 81, 83]; + let unit = Game.getPresetObjects(area); + let result = false; - return false; - }, - - /** - * 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 - * - Sometimes it seems like calling getPresetObjects to quickly after taking an exit causes a crash, only anecdotal evidence though. Test delays - * - Add the rest of the preset shrine id's to look for - */ - getShrinesInArea: function (area, type, use) { - let shrineLocs = []; - let shrineIds = [2, 81, 83]; - let unit = Game.getPresetObjects(area); - let result = false; - - 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 (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]); + } } } - } - try { - NodeAction.shrinesToIgnore.push(type); + try { + NodeAction.shrinesToIgnore.push(type); - while (shrineLocs.length > 0) { - shrineLocs.sort(Sort.points); - let coords = shrineLocs.shift(); + while (shrineLocs.length > 0) { + 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"); - return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; - } }); + // 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"); + return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; + } }); - let shrine = Game.getObject("shrine"); + 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 (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 (!use || this.getShrine(shrine)) { + result = true; - if (type === sdk.shrines.Gem) { - Pickit.pickItems(); + if (type === sdk.shrines.Gem) { + Pickit.pickItems(); + } + return true; } - return true; } - } - } while (shrine.getNext()); + } while (shrine.getNext()); + } } + } finally { + NodeAction.shrinesToIgnore.remove(type); } - } finally { - NodeAction.shrinesToIgnore.remove(type); - } - - return result; - }, - // Go to town when low on hp/mp or when out of potions. can be upgraded to check for curses etc. - townCheck: function () { - if (!me.canTpToTown()) return false; + return result; + }, - let tTick = getTickCount(); - let check = false; + /** + * Go to town when low on hp/mp or when out of potions. can be upgraded to check for curses etc. + * @deprecated - will be removed in future versions + * @returns {boolean} + */ + townCheck: function () { + if (!me.canTpToTown()) return false; - if (Config.TownCheck && !me.inTown) { - try { - if (me.needPotions() || (Config.OpenChests.Enabled && me.needKeys())) { - check = true; - } - } catch (e) { - return false; - } + let tTick = getTickCount(); + let check = false; - if (check) { - // check that townchicken is running - so we don't spam needing potions if it isn't - let townChick = getScript("threads/TownChicken.js"); - if (!townChick || townChick && !townChick.running) { + if (Config.TownCheck && !me.inTown) { + try { + if (me.needPotions() || (Config.OpenChests.Enabled && me.needKeys())) { + check = true; + } + } catch (e) { return false; } - townChick.send("townCheck"); - console.log("townCheck check Duration: " + (getTickCount() - tTick)); - - return true; - } - } + if (check) { + // check that townchicken is running - so we don't spam needing potions if it isn't + let townChick = getScript("threads/TownChicken.js"); + if (!townChick || townChick && !townChick.running) { + return false; + } - return false; - }, + townChick.send("townCheck"); + console.log("townCheck check Duration: " + (getTickCount() - tTick)); - // Log someone's gear - spy: function (name) { - let unit = getUnit(-1, name); + return true; + } + } - if (!unit) { - console.warn("player not found"); return false; - } + }, + + /** + * Log someone's gear + * @param {string} name + * @returns {boolean} + */ + spy: function (name) { + let unit = getUnit(-1, name); + + if (!unit) { + console.warn("player not found"); + return false; + } - let item = unit.getItem(); + let item = unit.getItem(); - if (item) { - do { - this.logItem(unit.name, item); - } while (item.getNext()); - } + if (item) { + do { + this.logItem(unit.name, item); + } while (item.getNext()); + } - return true; - }, - - errorConsolePrint: true, - screenshotErrors: true, - - /** - * Report script errors to logs/ScriptErrorLog.txt - * @param {Error | string} error - * @param {string} [script] - */ - errorReport: function (error, script) { - let msg, oogmsg, filemsg, source, stack; - let stackLog = ""; - - let date = new Date(); - const 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: " + 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(); - } + return true; + }, + + errorConsolePrint: true, + screenshotErrors: true, + + /** + * Report script errors to logs/ScriptErrorLog.txt + * @param {Error | string} error + * @param {string} [script] + */ + errorReport: function (error, script) { + let msg, oogmsg, filemsg, source, stack; + let stackLog = ""; + + let date = new Date(); + const 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: " + 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 - ); + 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 (i < stack.length - 1) { + stackLog += ", "; + } } } } } - } - stackLog && (filemsg += "Stack: " + stackLog + "\n"); - } + stackLog && (filemsg += "Stack: " + stackLog + "\n"); + } - this.errorConsolePrint && D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); - showConsole(); - console.log(msg); - FileAction.append("logs/ScriptErrorLog.txt", filemsg); + this.errorConsolePrint && D2Bot.printToConsole(oogmsg, sdk.colors.D2Bot.Gray); + showConsole(); + console.log(msg); + FileAction.append("logs/ScriptErrorLog.txt", filemsg); - if (this.screenshotErrors) { - takeScreenshot(); - delay(500); - } - }, + if (this.screenshotErrors) { + takeScreenshot(); + delay(500); + } + }, + + /** + * @param {string} msg + * @returns {void} + */ + debugLog: function (msg) { + if (!Config.Debug) return; + debugLog(me.profile + ": " + msg); + }, + + /** + * Use a NPC menu. Experimental function, subject to change + * @param {number} id - string number (with exception of Ressurect merc). + * @returns {boolean} + */ + useMenu: function (id) { + //print("useMenu " + getLocaleString(id)); + + let npc; + + switch (id) { + case sdk.menu.RessurectMerc: // (non-English dialog) + case sdk.menu.Trade: // (crash dialog) + npc = getInteractedNPC(); + + if (npc) { + npc.useMenu(id); + delay(750); + + return true; + } - /** - * @param {string} msg - * @returns {void} - */ - debugLog: function (msg) { - if (!Config.Debug) return; - debugLog(me.profile + ": " + msg); - }, + break; + } - /** - * Use a NPC menu. Experimental function, subject to change - * @param {number} id - string number (with exception of Ressurect merc). - * @returns {boolean} - */ - useMenu: function (id) { - //print("useMenu " + getLocaleString(id)); + let lines = getDialogLines(); + if (!lines) return false; - let npc; + for (let i = 0; i < lines.length; i += 1) { + if (lines[i].selectable && lines[i].text.includes(getLocaleString(id))) { + getDialogLines()[i].handler(); + delay(750); - switch (id) { - case sdk.menu.RessurectMerc: // (non-English dialog) - case sdk.menu.Trade: // (crash dialog) - npc = getInteractedNPC(); + return true; + } + } - if (npc) { - npc.useMenu(id); - delay(750); + return false; + }, + + /** + * @template T + * @param {function(): T} check + * @param {number} [timeout=6000] + * @param {number} [sleep=40] + * @returns {T | false} + */ + poll: function (check, timeout = 6000, sleep = 40) { + let ret, start = getTickCount(); + + while (getTickCount() - start <= timeout) { + if ((ret = check())) { + return ret; + } - return true; + delay(sleep); } - break; - } + return false; + }, - let lines = getDialogLines(); - if (!lines) return false; + /** + * @param {number[]} excluded + * @returns {number[] | null} array of UI flags that are set, or null if none are set + */ + getUIFlags: function (excluded = []) { + if (!me.gameReady) return null; - for (let i = 0; i < lines.length; i += 1) { - if (lines[i].selectable && lines[i].text.includes(getLocaleString(id))) { - getDialogLines()[i].handler(); - delay(750); + const MAX_FLAG = 37; // anything over 37 crashes + let flags = []; - return true; + if (typeof excluded !== "object" || excluded.length === undefined) { + // not an array-like object, make it an array + excluded = [excluded]; } - } - - return false; - }, - /** - * @template T - * @param {function(): T} check - * @param {number} [timeout=6000] - * @param {number} [sleep=40] - * @returns {T | false} - */ - poll: function (check, timeout = 6000, sleep = 40) { - let ret, start = getTickCount(); - - while (getTickCount() - start <= timeout) { - if ((ret = check())) { - return ret; + for (let c = 1; c <= MAX_FLAG; c++) { + // 0x23 is always set in-game + if (c !== 0x23 && excluded.indexOf(c) === -1 && getUIFlag(c)) { + flags.push(c); + } } - delay(sleep); - } - - return false; - }, - - /** - * @param {number[]} excluded - * @returns {number[] | null} array of UI flags that are set, or null if none are set - */ - getUIFlags: function (excluded = []) { - if (!me.gameReady) return null; + return flags.length ? flags : null; + }, - const MAX_FLAG = 37; // anything over 37 crashes - let flags = []; + /** + * @param {number} id + * @param {number} state + * @returns {0 | 1} + */ + checkQuest: function (id, state) { + Packet.questRefresh(); + delay(500); + return me.getQuest(id, state); + }, + + /** + * @param {number} questID + * @returns {number[]} List of set quest states + */ + getQuestStates: function (questID) { + if (!me.gameReady) return []; + Packet.questRefresh(); + delay(500); + const MAX_STATE = 16; + let questStates = []; - if (typeof excluded !== "object" || excluded.length === undefined) { - // not an array-like object, make it an array - excluded = [excluded]; - } + for (let i = 0; i < MAX_STATE; i++) { + if (me.getQuest(questID, i)) { + questStates.push(i); + } - for (let c = 1; c <= MAX_FLAG; c++) { - // 0x23 is always set in-game - if (c !== 0x23 && excluded.indexOf(c) === -1 && getUIFlag(c)) { - flags.push(c); + delay(50); } - } - return flags.length ? flags : null; - }, - - /** - * @param {number} id - * @param {number} state - * @returns {0 | 1} - */ - checkQuest: function (id, state) { - Packet.questRefresh(); - delay(500); - return me.getQuest(id, state); - }, - - /** - * @param {number} questID - * @returns {number[]} List of set quest states - */ - getQuestStates: function (questID) { - if (!me.gameReady) return []; - Packet.questRefresh(); - delay(500); - const MAX_STATE = 16; - let questStates = []; - - for (let i = 0; i < MAX_STATE; i++) { - if (me.getQuest(questID, i)) { - questStates.push(i); - } - - delay(50); + return questStates; } - - return questStates; - } -}; + }; +})(); From 5667e9e385ae06c9a66be2e0eeb162647fe9c717 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:28:35 -0400 Subject: [PATCH 375/758] [BugFix] Using a3/a5 shrines - A3/A5 shrines have different names so using `Game.getObject("shrine");` caused them to be ignored --- d2bs/kolbot/libs/core/GameData/ShrineData.js | 11 +++++ d2bs/kolbot/libs/core/Misc.js | 47 ++++++-------------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/ShrineData.js b/d2bs/kolbot/libs/core/GameData/ShrineData.js index db13ee49d..bb42aeeda 100644 --- a/d2bs/kolbot/libs/core/GameData/ShrineData.js +++ b/d2bs/kolbot/libs/core/GameData/ShrineData.js @@ -7,6 +7,12 @@ (function (module) { const ShrineData = (function () { + /** + * @constructor + * @param {number} state + * @param {number} duration + * @param {number} regen + */ function Shrine (state, duration, regen) { this.state = state || 0; this.duration = duration || 0; @@ -38,22 +44,27 @@ ]); return { + /** @param {number} shrineType */ get: function (shrineType) { return _shrines.get(shrineType); }, + /** @param {number} shrineType */ has: function (shrineType) { return _shrines.has(shrineType); }, + /** @param {number} shrineType */ getState: function (shrineType) { return _shrines.get(shrineType).state || 0; }, + /** @param {number} shrineType */ getDuration: function (shrineType) { return _shrines.get(shrineType).duration || 0; }, + /** @param {number} shrineType */ getRegenTime: function (shrineType) { return _shrines.get(shrineType).regenTime || Infinity; }, diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 7b0c1ca9b..c3619ac59 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -7,6 +7,8 @@ */ const Misc = (function () { + const ShrineData = require("./GameData/ShrineData"); + return { /** * Click something @@ -456,6 +458,7 @@ const Misc = (function () { !range && (range = Pather.useTeleport() ? 25 : 15); !Array.isArray(ignore) && (ignore = [ignore]); + /** @type {ObjectUnit[]} */ let shrineList = []; // Initiate shrine states @@ -463,53 +466,29 @@ const Misc = (function () { Misc.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; - } + this.shrineStates[i] = ShrineData.getState(Config.ScanShrines[i]); } } - let shrine = Game.getObject("shrine"); + /** + * Fix for a3/a5 shrines + */ + let shrine = Game.getObject(); if (shrine) { let index = -1; + // Build a list of nearby shrines do { - if (shrine.mode === sdk.objects.mode.Inactive + 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) <= range) { 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) { From 0aa12241faf210099b71bf3858ee376879fb1d3a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 31 Mar 2024 18:47:41 -0400 Subject: [PATCH 376/758] Update ShrineData.js - state is undefined error fix, unfortunately no optional chaining operator --- d2bs/kolbot/libs/core/GameData/ShrineData.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/GameData/ShrineData.js b/d2bs/kolbot/libs/core/GameData/ShrineData.js index bb42aeeda..551be62dc 100644 --- a/d2bs/kolbot/libs/core/GameData/ShrineData.js +++ b/d2bs/kolbot/libs/core/GameData/ShrineData.js @@ -56,6 +56,7 @@ /** @param {number} shrineType */ getState: function (shrineType) { + if (!_shrines.has(shrineType)) return 0; return _shrines.get(shrineType).state || 0; }, From fe6099e25328cf17fb89b531be55b04770249626 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 31 Mar 2024 18:53:12 -0400 Subject: [PATCH 377/758] [Feat] Add usage of wells to scanshrines - Enable using wells by adding "wells" to `Config.ScanShrines` - Use `Confg.UseWells` options to configure what health effects dictate using one --- d2bs/kolbot/libs/core/Config.js | 7 +++++++ d2bs/kolbot/libs/core/Misc.js | 31 +++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 2d0f3378d..6dd7d2375 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -254,7 +254,14 @@ let Config = { SkipImmune: [], SkipAura: [], SkipException: [], + /** @type {number[]} */ ScanShrines: [], + UseWells: { + HpPercent: 0, + MpPercent: 0, + StaminaPercent: 0, + StatusEffects: false, + }, Debug: false, AutoMule: { diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index c3619ac59..eaa503b14 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -470,6 +470,23 @@ const Misc = (function () { } } + const needWell = function () { + if (me.hpPercent < Config.UseWells.HpPercent) return true; + if (me.mpPercent < Config.UseWells.MpPercent) return true; + if (me.staminaPercent < Config.UseWells.StaminaPercent) return true; + if (Config.UseWells.StatusEffects) { + return [ + sdk.states.Frozen, + sdk.states.Poison, + sdk.states.AmplifyDamage, + sdk.states.Decrepify + ].some(function (state) { + return me.getState(state); + }); + } + return false; + }; + /** * Fix for a3/a5 shrines */ @@ -480,8 +497,8 @@ const Misc = (function () { // Build a list of nearby shrines do { - if (shrine.name.toLowerCase().includes("shrine") - && ShrineData.has(shrine.objtype) + let _name = shrine.name.toLowerCase(); + if ((_name.includes("shrine") && ShrineData.has(shrine.objtype) || (_name.includes("well"))) && shrine.mode === sdk.objects.mode.Inactive && !ignore.includes(shrine.objtype) && getDistance(me.x, me.y, shrine.x, shrine.y) <= range) { @@ -500,15 +517,17 @@ const Misc = (function () { } 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] + || (Config.ScanShrines[i] === "well" && shrine.name.toLowerCase().includes("well") && needWell()) + ) && (Pather.useTeleport() || !checkCollision(me, shrine, sdk.collision.WallOrRanged))) { + this.getShrine(shrine); // Gem shrine - pick gem if (Config.ScanShrines[i] === sdk.shrines.Gem) { From 685533160ed59bb74458cc7b20445dcb30580b81 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 10 Apr 2024 14:17:28 -0400 Subject: [PATCH 378/758] Update tsconfig.json - fix typo --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 98c065da9..23d371087 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,7 @@ // "outDir": "./d2bs/kolbot/sdk/types/", "outFile": "./d2bs/kolbot/data/out.js", "typeRoots": [ - "./d2bs/kolbot/sdk/global.d.ts", + "./d2bs/kolbot/sdk/globals.d.ts", "./d2bs/kolbot/sdk/types/", "./d2bs/kolbot/libs/SoloPlay/index.d.ts", ], From 76abd6d8df7dfb2d616ce3467c13dc2b12585502 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 10 Apr 2024 14:19:56 -0400 Subject: [PATCH 379/758] Add `Pather.randMove` - Basically just a wrapper around repeating getRandCoordinate and move --- d2bs/kolbot/libs/core/Attack.js | 3 +-- d2bs/kolbot/libs/core/Pather.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 1cf832ee5..14a81a3ae 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -607,8 +607,7 @@ const Attack = { 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); + Pather.randMove(-1, 1, -1, 1, 5); } break; diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index fc9e1f6dd..dd19a7ffc 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -2128,6 +2128,24 @@ const Pather = { return true; }, + + /** + * @param {number} xMin + * @param {number} xMax + * @param {number} yMin + * @param {number} yMax + * @param {number} factor + */ + randMove: function (xMin, xMax, yMin, yMax, factor) { + xMin === undefined && (xMin = -4); + xMax === undefined && (xMax = 4); + yMin === undefined && (yMin = -4); + yMax === undefined && (yMax = 4); + factor === undefined && (factor = 1); + /** @type {PathNode} */ + const coord = CollMap.getRandCoordinate(me.x, -4, 4, me.y, -4, 4, factor); + return Pather.move(coord, { retry: 3, allowClearing: false }); + }, }; Pather.nextAreas[sdk.areas.RogueEncampment] = sdk.areas.BloodMoor; From 3cecdfd0180165568fee22206bd2e8587a80be3a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 10 Apr 2024 14:23:37 -0400 Subject: [PATCH 380/758] Update Loader.js - Create `Runnable` class for the global scripts, this allows us to start exporting some of the data for each script and will make it easier to extend later. - If the first script has a `startArea` return to that town at the end of the run - If we are in the `startArea` for the current script then don't waste time going to town --- d2bs/kolbot/libs/core/Loader.js | 85 ++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 50e0e82a2..a36ca307a 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -5,11 +5,31 @@ * */ +/** @typedef {function(): boolean} GlobalScript */ +// TODO: preaction/postaction +/** + * @constructor + * @param {function(): boolean} action + * @param {number} [startArea] + */ +function Runnable (action, startArea) { + this.action = action; + this.startArea = startArea; +} + const Loader = { + /** @type {string[]} */ fileList: [], + /** @type {string[]} */ scriptList: [], scriptIndex: -1, skipTown: ["Test", "Follower"], + firstScriptAct: -1, + /** @type {GlobalScript | Runnable | null} */ + currentScript: null, + /** @type {Runnable | null} */ + nextScript: null, + /** @type {Set} */ doneScripts: new Set(), init: function () { @@ -18,16 +38,27 @@ const Loader = { }, getScripts: function () { + /** @type {string[]} */ let fileList = dopen("libs/scripts/").getFiles(); for (let i = 0; i < fileList.length; i += 1) { - if (fileList[i].indexOf(".js") > -1) { + if (fileList[i].endsWith(".js")) { this.fileList.push(fileList[i].substring(0, fileList[i].indexOf(".js"))); } } }, - // see http://stackoverflow.com/questions/728360/copying-an-object-in-javascript#answer-728694 + _runCurrent: function () { + return this.currentScript instanceof Runnable + ? this.currentScript.action() + : this.currentScript(); + }, + + /** + * @see http://stackoverflow.com/questions/728360/copying-an-object-in-javascript#answer-728694 + * @param {Date | Array | Object} obj + * @returns + */ clone: function (obj) { let copy; @@ -123,9 +154,29 @@ const Loader = { continue; } + Loader.currentScript = global[script]; + + // Preload the next script + if (Loader.scriptIndex < Loader.scriptList.length - 1) { + let nextScript = this.scriptList[Loader.scriptIndex + 1]; + if (include("scripts/" + nextScript + ".js")) { + if (global[nextScript] instanceof Runnable && global[nextScript].startArea) { + Loader.nextScript = global[nextScript]; + } + } + } + if (isIncluded("scripts/" + script + ".js")) { try { - if (typeof (global[script]) !== "function") { + if (Loader.currentScript instanceof Runnable) { + if (Loader.currentScript.startArea && Loader.scriptIndex === 0) { + Loader.firstScriptAct = sdk.areas.actOf(Loader.currentScript.startArea); + } + + if (Loader.currentScript.startArea && me.inArea(Loader.currentScript.startArea)) { + this.skipTown.push(script); + } + } else if (typeof (Loader.currentScript) !== "function") { throw new Error("Invalid script function name"); } @@ -159,7 +210,7 @@ const Loader = { say("nextup " + script); } - if (global[script]()) { + if (Loader._runCurrent()) { let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); let duration = Time.elapsed(tick); console.log( @@ -181,6 +232,8 @@ const Loader = { if (this.scriptIndex < this.scriptList.length) { // remove script function from global scope, so it can be cleared by GC delete global[script]; + Loader.currentScript = null; + Loader.nextScript = null; } if (reconfiguration) { @@ -190,10 +243,21 @@ const Loader = { } } } + + // return to first script town + if (Loader.firstScriptAct > -1) { + Town.goToTown(Loader.firstScriptAct); + } }, + /** @type {string[]} */ tempList: [], + /** + * @param {string} script + * @param {Object | function(): any} configOverride + * @returns {boolean} + */ runScript: function (script, configOverride) { let reconfiguration, unmodifiedConfig = {}; let failed = false; @@ -217,9 +281,15 @@ const Loader = { return false; } + Loader.currentScript = global[script]; + if (isIncluded("scripts/" + script + ".js")) { try { - if (typeof (global[script]) !== "function") { + if (Loader.currentScript instanceof Runnable) { + if (Loader.currentScript.startArea && me.inArea(Loader.currentScript.startArea)) { + this.skipTown.push(script); + } + } else if (typeof (Loader.currentScript) !== "function") { throw new Error("Invalid script function name"); } @@ -247,7 +317,7 @@ const Loader = { let tick = getTickCount(); let exp = me.getStat(sdk.stats.Experience); - if (global[script]()) { + if (Loader._runCurrent()) { console.log( mainScriptStr + "ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick)) @@ -278,7 +348,8 @@ const Loader = { delete global[script]; } - this.tempList.pop(); + Loader.currentScript = null; + Loader.tempList.pop(); if (reconfiguration) { console.log("ÿc2Reverting back unmodified config properties."); From 1af938411513287e3b0c3dbffd3df5f2b8e9ee68 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:06:43 -0400 Subject: [PATCH 381/758] Switch to using new `Runnable` class --- d2bs/kolbot/libs/scripts/Abaddon.js | 25 +- d2bs/kolbot/libs/scripts/AncientTunnels.js | 45 +- d2bs/kolbot/libs/scripts/Andariel.js | 51 +- d2bs/kolbot/libs/scripts/AutoBaal.js | 415 +-- d2bs/kolbot/libs/scripts/Baal.js | 159 +- d2bs/kolbot/libs/scripts/BaalAssistant.js | 767 +++--- d2bs/kolbot/libs/scripts/BaalHelper.js | 215 +- d2bs/kolbot/libs/scripts/BattleOrders.js | 483 ++-- d2bs/kolbot/libs/scripts/BattlemaidSarina.js | 27 +- d2bs/kolbot/libs/scripts/Bishibosh.js | 21 +- d2bs/kolbot/libs/scripts/BoBarbHelper.js | 167 +- d2bs/kolbot/libs/scripts/BoneAsh.js | 21 +- d2bs/kolbot/libs/scripts/Bonesaw.js | 29 +- d2bs/kolbot/libs/scripts/ChestMania.js | 57 +- .../libs/scripts/ClassicChaosAssistant.js | 217 +- d2bs/kolbot/libs/scripts/ClearAnyArea.js | 28 +- d2bs/kolbot/libs/scripts/Coldcrow.js | 25 +- d2bs/kolbot/libs/scripts/Coldworm.js | 53 +- d2bs/kolbot/libs/scripts/ControlBot.js | 2221 +++++++++-------- d2bs/kolbot/libs/scripts/Corpsefire.js | 27 +- d2bs/kolbot/libs/scripts/Countess.js | 47 +- d2bs/kolbot/libs/scripts/Cows.js | 227 +- d2bs/kolbot/libs/scripts/Crafting.js | 169 +- d2bs/kolbot/libs/scripts/CreepingFeature.js | 23 +- d2bs/kolbot/libs/scripts/CrushTele.js | 88 +- d2bs/kolbot/libs/scripts/DeveloperMode.js | 418 ++-- d2bs/kolbot/libs/scripts/Diablo.js | 136 +- d2bs/kolbot/libs/scripts/DiabloHelper.js | 293 +-- d2bs/kolbot/libs/scripts/Duriel.js | 119 +- d2bs/kolbot/libs/scripts/Eldritch.js | 69 +- d2bs/kolbot/libs/scripts/Endugu.js | 31 +- d2bs/kolbot/libs/scripts/Eyeback.js | 23 +- d2bs/kolbot/libs/scripts/Fangskin.js | 33 +- d2bs/kolbot/libs/scripts/Follower.js | 1716 ++++++------- d2bs/kolbot/libs/scripts/Frozenstein.js | 29 +- d2bs/kolbot/libs/scripts/Gamble.js | 80 +- d2bs/kolbot/libs/scripts/GemHunter.js | 44 +- d2bs/kolbot/libs/scripts/GetCube.js | 45 +- d2bs/kolbot/libs/scripts/GetEssences.js | 80 +- d2bs/kolbot/libs/scripts/GetFade.js | 95 +- d2bs/kolbot/libs/scripts/GetKeys.js | 46 +- d2bs/kolbot/libs/scripts/GhostBusters.js | 195 +- d2bs/kolbot/libs/scripts/Hephasto.js | 35 +- d2bs/kolbot/libs/scripts/IPHunter.js | 82 +- d2bs/kolbot/libs/scripts/Icehawk.js | 23 +- d2bs/kolbot/libs/scripts/Idle.js | 89 +- d2bs/kolbot/libs/scripts/Izual.js | 25 +- d2bs/kolbot/libs/scripts/KillDclone.js | 29 +- d2bs/kolbot/libs/scripts/KurastTemples.js | 69 +- d2bs/kolbot/libs/scripts/MFHelper.js | 460 ++-- d2bs/kolbot/libs/scripts/Mausoleum.js | 77 +- d2bs/kolbot/libs/scripts/Mephisto.js | 271 +- d2bs/kolbot/libs/scripts/Nihlathak.js | 55 +- d2bs/kolbot/libs/scripts/OrgTorch.js | 959 +++---- d2bs/kolbot/libs/scripts/OuterSteppes.js | 21 +- d2bs/kolbot/libs/scripts/Pindleskin.js | 75 +- d2bs/kolbot/libs/scripts/Pit.js | 31 +- d2bs/kolbot/libs/scripts/Questing.js | 704 +++--- d2bs/kolbot/libs/scripts/Radament.js | 29 +- d2bs/kolbot/libs/scripts/Rakanishu.js | 33 +- d2bs/kolbot/libs/scripts/SealLeecher.js | 141 +- d2bs/kolbot/libs/scripts/SharpTooth.js | 29 +- d2bs/kolbot/libs/scripts/ShopBot.js | 492 ++-- d2bs/kolbot/libs/scripts/Smith.js | 25 +- d2bs/kolbot/libs/scripts/Snapchip.js | 27 +- d2bs/kolbot/libs/scripts/Stormtree.js | 23 +- d2bs/kolbot/libs/scripts/Summoner.js | 93 +- d2bs/kolbot/libs/scripts/ThreshSocket.js | 25 +- d2bs/kolbot/libs/scripts/Tombs.js | 45 +- d2bs/kolbot/libs/scripts/Travincal.js | 103 +- d2bs/kolbot/libs/scripts/TravincalLeech.js | 113 +- d2bs/kolbot/libs/scripts/Treehead.js | 23 +- d2bs/kolbot/libs/scripts/Tristram.js | 101 +- d2bs/kolbot/libs/scripts/TristramLeech.js | 193 +- .../kolbot/libs/scripts/UndergroundPassage.js | 23 +- d2bs/kolbot/libs/scripts/UserAddon.js | 162 +- d2bs/kolbot/libs/scripts/WPGetter.js | 36 +- d2bs/kolbot/libs/scripts/Wakka.js | 685 ++--- d2bs/kolbot/libs/scripts/Worldstone.js | 61 +- 79 files changed, 7399 insertions(+), 7127 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Abaddon.js b/d2bs/kolbot/libs/scripts/Abaddon.js index 69d2995a4..5c21a1b6a 100644 --- a/d2bs/kolbot/libs/scripts/Abaddon.js +++ b/d2bs/kolbot/libs/scripts/Abaddon.js @@ -5,17 +5,20 @@ * */ -function Abaddon () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); +const Abaddon = new Runnable( + function Abaddon () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); - if (!Pather.moveToPresetObject(sdk.areas.FrigidHighlands, sdk.objects.RedPortal) - || !Pather.usePortal(sdk.areas.Abaddon)) { - throw new Error("Failed to move to Abaddon"); - } + if (!Pather.moveToPresetObject(sdk.areas.FrigidHighlands, sdk.objects.RedPortal) + || !Pather.usePortal(sdk.areas.Abaddon)) { + throw new Error("Failed to move to Abaddon"); + } - Attack.clearLevel(Config.ClearType); + Attack.clearLevel(Config.ClearType); - return true; -} + return true; + }, + sdk.areas.FrigidHighlands +); diff --git a/d2bs/kolbot/libs/scripts/AncientTunnels.js b/d2bs/kolbot/libs/scripts/AncientTunnels.js index 1d9d0576f..73458419b 100644 --- a/d2bs/kolbot/libs/scripts/AncientTunnels.js +++ b/d2bs/kolbot/libs/scripts/AncientTunnels.js @@ -5,30 +5,33 @@ * */ -function AncientTunnels () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.LostCity); - Precast.doPrecast(true); +const AncientTunnels = new Runnable( + function AncientTunnels () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.LostCity); + Precast.doPrecast(true); - try { - if (Config.AncientTunnels.OpenChest && Pather.moveToPresetObject(me.area, sdk.objects.SuperChest)) { - Misc.openChests(5) && Pickit.pickItems(); + try { + if (Config.AncientTunnels.OpenChest && Pather.moveToPresetObject(me.area, sdk.objects.SuperChest)) { + Misc.openChests(5) && Pickit.pickItems(); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); - } - try { - if (Config.AncientTunnels.KillDarkElder - && Pather.moveToPresetMonster(me.area, sdk.monsters.preset.DarkElder)) { - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); + try { + if (Config.AncientTunnels.KillDarkElder + && Pather.moveToPresetMonster(me.area, sdk.monsters.preset.DarkElder)) { + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); - } - if (!Pather.moveToExit(sdk.areas.AncientTunnels, true)) throw new Error("Failed to move to Ancient Tunnels"); - Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit(sdk.areas.AncientTunnels, true)) throw new Error("Failed to move to Ancient Tunnels"); + Attack.clearLevel(Config.ClearType); - return true; -} + return true; + }, + sdk.areas.LostCity +); diff --git a/d2bs/kolbot/libs/scripts/Andariel.js b/d2bs/kolbot/libs/scripts/Andariel.js index 62db9fd33..bcba77562 100644 --- a/d2bs/kolbot/libs/scripts/Andariel.js +++ b/d2bs/kolbot/libs/scripts/Andariel.js @@ -1,38 +1,41 @@ /** * @filename Andariel.js -* @author kolton +* @author kolton, theBGuy * @desc kill Andariel * */ -function Andariel () { - const killAndariel = function () { - let target = Game.getMonster(sdk.monsters.Andariel); - if (!target) throw new Error("Andariel not found."); +const Andariel = new Runnable( + function Andariel () { + const killAndariel = function () { + let target = Game.getMonster(sdk.monsters.Andariel); + if (!target) throw new Error("Andariel not found."); - Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Andariel); + Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Andariel); - for (let i = 0; i < 300 && target.attackable; i += 1) { - ClassAttack.doAttack(target); - target.distance <= 10 && Pather.moveTo(me.x > 22548 ? 22535 : 22560, 9520); - } + for (let i = 0; i < 300 && target.attackable; i += 1) { + ClassAttack.doAttack(target); + target.distance <= 10 && Pather.moveTo(me.x > 22548 ? 22535 : 22560, 9520); + } - return target.dead; - }; + return target.dead; + }; - Town.doChores(); - Pather.useWaypoint(sdk.areas.CatacombsLvl2); - Precast.doPrecast(true); + Town.doChores(); + Pather.useWaypoint(sdk.areas.CatacombsLvl2); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true)) { - throw new Error("Failed to move to Catacombs Level 4"); - } + if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true)) { + throw new Error("Failed to move to Catacombs Level 4"); + } - Pather.moveTo(22549, 9520); - me.sorceress && me.classic ? killAndariel() : Attack.kill(sdk.monsters.Andariel); + Pather.moveTo(22549, 9520); + me.sorceress && me.classic ? killAndariel() : Attack.kill(sdk.monsters.Andariel); - delay(2000); // Wait for minions to die. - Pickit.pickItems(); + delay(2000); // Wait for minions to die. + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.CatacombsLvl2 +); diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index a477047b7..76845d5fa 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -15,259 +15,262 @@ * - should this listen for baal death packet? */ -function AutoBaal () { - // internal variables - let baalCheck, throneCheck, hotCheck, leader; // internal variables - let hotTick = 0; - const safeMsg = ["safe", "throne clear", "leechers can come", "tp is up", "1 clear"]; // safe message - casing doesn't matter - const baalMsg = ["baal"]; // baal message - casing doesn't matter - const hotMsg = ["hot", "warm", "dangerous", "lethal"]; // used for shrine hunt - - [safeMsg, baalMsg, hotMsg].forEach((function (arr) { - for (let i = 0; i < arr.length; i++) { - arr[i] = arr[i].toLowerCase(); - } - })); - - /** - * chat event handler function, listen to what leader says - * @param {string} nick - * @param {string} msg - */ - const chatEvent = function (nick, msg) { - // filter leader messages - if (!nick || !msg || nick !== leader) return; - msg = msg.toLowerCase(); - - // loop through all predefined messages to find a match - for (let str of hotMsg) { - // leader says a hot tp message - if (msg.includes(str)) { - hotCheck = true; // not safe to enter baal chamber - hotTick = getTickCount(); - - return; +const AutoBaal = new Runnable( + function AutoBaal () { + // internal variables + let baalCheck, throneCheck, hotCheck, leader; // internal variables + let hotTick = 0; + const safeMsg = ["safe", "throne clear", "leechers can come", "tp is up", "1 clear"]; // safe message - casing doesn't matter + const baalMsg = ["baal"]; // baal message - casing doesn't matter + const hotMsg = ["hot", "warm", "dangerous", "lethal"]; // used for shrine hunt + + [safeMsg, baalMsg, hotMsg].forEach((function (arr) { + for (let i = 0; i < arr.length; i++) { + arr[i] = arr[i].toLowerCase(); + } + })); + + /** + * chat event handler function, listen to what leader says + * @param {string} nick + * @param {string} msg + */ + const chatEvent = function (nick, msg) { + // filter leader messages + if (!nick || !msg || nick !== leader) return; + msg = msg.toLowerCase(); + + // loop through all predefined messages to find a match + for (let str of hotMsg) { + // leader says a hot tp message + if (msg.includes(str)) { + hotCheck = true; // not safe to enter baal chamber + hotTick = getTickCount(); + + return; + } } - } - // loop through all predefined messages to find a match - for (let str of safeMsg) { - // leader says a safe tp message - if (msg.includes(str)) { - throneCheck = true; // safe to enter throne + // loop through all predefined messages to find a match + for (let str of safeMsg) { + // leader says a safe tp message + if (msg.includes(str)) { + throneCheck = true; // safe to enter throne - return; + return; + } } - } - // loop through all predefined messages to find a match - for (let str of baalMsg) { - // leader says a baal message - if (msg.includes(str)) { - baalCheck = true; // safe to enter baal chamber + // loop through all predefined messages to find a match + for (let str of baalMsg) { + // leader says a baal message + if (msg.includes(str)) { + baalCheck = true; // safe to enter baal chamber - return; - } - } - }; - - /** - * @todo maybe factor this out and make it useable for other leecher scripts? - */ - const longRangeSupport = function () { - switch (me.classid) { - case sdk.player.class.Necromancer: - ClassAttack.raiseArmy(50); - - if (Config.Curse[1] > 0) { - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged) - && monster.curseable && !monster.isSpecial && ClassAttack.canCurse(monster, Config.Curse[1])) { - Skill.cast(Config.Curse[1], sdk.skills.hand.Right, monster); - } - } while (monster.getNext()); + return; } } + }; + + /** + * @todo maybe factor this out and make it useable for other leecher scripts? + */ + const longRangeSupport = function () { + switch (me.classid) { + case sdk.player.class.Necromancer: + ClassAttack.raiseArmy(50); + + if (Config.Curse[1] > 0) { + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged) + && monster.curseable && !monster.isSpecial && ClassAttack.canCurse(monster, Config.Curse[1])) { + Skill.cast(Config.Curse[1], sdk.skills.hand.Right, monster); + } + } while (monster.getNext()); + } + } - break; - case sdk.player.class.Assassin: - if (Config.UseTraps && ClassAttack.checkTraps({ x: 15095, y: 5037 })) { - ClassAttack.placeTraps({ x: 15095, y: 5037 }, 5); - } + break; + case sdk.player.class.Assassin: + if (Config.UseTraps && ClassAttack.checkTraps({ x: 15095, y: 5037 })) { + ClassAttack.placeTraps({ x: 15095, y: 5037 }, 5); + } - break; - default: - break; - } + break; + default: + break; + } - let skills = [ - sdk.skills.ChargedStrike, sdk.skills.Lightning, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, - sdk.skills.BoneSpear, sdk.skills.BoneSpirit, sdk.skills.DoubleThrow, sdk.skills.Volcano - ]; + let skills = [ + sdk.skills.ChargedStrike, sdk.skills.Lightning, sdk.skills.FireWall, sdk.skills.Meteor, sdk.skills.Blizzard, + sdk.skills.BoneSpear, sdk.skills.BoneSpirit, sdk.skills.DoubleThrow, sdk.skills.Volcano + ]; - if (!skills.some(skill => Config.AttackSkill[1] === skill || Config.AttackSkill[3] === skill)) { - return false; - } + if (!skills.some(skill => Config.AttackSkill[1] === skill || Config.AttackSkill[3] === skill)) { + return false; + } - let monster = Game.getMonster(); - let monList = []; + let monster = Game.getMonster(); + let monList = []; - if (monster) { - do { - if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged)) { - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } + if (monster) { + do { + if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged)) { + monList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } - if (me.inArea(sdk.areas.ThroneofDestruction)) { - [15116, 5026].distance > 10 && Pather.moveTo(15116, 5026); - } + if (me.inArea(sdk.areas.ThroneofDestruction)) { + [15116, 5026].distance > 10 && Pather.moveTo(15116, 5026); + } - let oldVal = Skill.usePvpRange; - Skill.usePvpRange = true; + let oldVal = Skill.usePvpRange; + Skill.usePvpRange = true; - try { - while (monList.length) { - monList.sort(Sort.units); - monster = copyUnit(monList[0]); + try { + while (monList.length) { + monList.sort(Sort.units); + monster = copyUnit(monList[0]); - if (monster && monster.attackable) { - let index = monster.isSpecial ? 1 : 3; + if (monster && monster.attackable) { + let index = monster.isSpecial ? 1 : 3; - if (Config.AttackSkill[index] > -1 - && Attack.checkResist(monster, Attack.getSkillElement(Config.AttackSkill[index]))) { - ClassAttack.doCast(monster, Config.AttackSkill[index], Config.AttackSkill[index + 1]); + if (Config.AttackSkill[index] > -1 + && Attack.checkResist(monster, Attack.getSkillElement(Config.AttackSkill[index]))) { + ClassAttack.doCast(monster, Config.AttackSkill[index], Config.AttackSkill[index + 1]); + } else { + monList.shift(); + } } else { monList.shift(); } - } else { - monList.shift(); - } - delay(5); + delay(5); + } + } finally { + Skill.usePvpRange = oldVal; } - } finally { - Skill.usePvpRange = oldVal; - } - return true; - }; + return true; + }; - // critical error - can't reach harrogath - if (!Town.goToTown(5)) throw new Error("Town.goToTown failed."); + // critical error - can't reach harrogath + if (!Town.goToTown(5)) throw new Error("Town.goToTown failed."); - if (Config.Leader) { - leader = Config.Leader; - if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), Time.seconds(1))) { - throw new Error("AutoBaal: Leader not partied"); + if (Config.Leader) { + leader = Config.Leader; + if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), Time.seconds(1))) { + throw new Error("AutoBaal: Leader not partied"); + } } - } - - try { - addEventListener("chatmsg", chatEvent); - Config.AutoBaal.FindShrine === 2 && (hotCheck = true); - - Town.doChores(); - Town.move("portalspot"); - - // find the first player in throne of destruction - if (leader || (leader = Misc.autoLeaderDetect({ - destination: sdk.areas.ThroneofDestruction, - quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) - }))) { - // do our stuff while partied - while (Misc.inMyParty(leader)) { - if (hotCheck) { - if (Config.AutoBaal.FindShrine) { - let i; - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - for (i = sdk.areas.StonyField; i > 1; i--) { - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; + try { + addEventListener("chatmsg", chatEvent); + Config.AutoBaal.FindShrine === 2 && (hotCheck = true); + + Town.doChores(); + Town.move("portalspot"); + + // find the first player in throne of destruction + if (leader || (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ThroneofDestruction, + quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) + }))) { + // do our stuff while partied + while (Misc.inMyParty(leader)) { + if (hotCheck) { + if (Config.AutoBaal.FindShrine) { + let i; + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + + for (i = sdk.areas.StonyField; i > 1; i--) { + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } } - } - if (i === 1) { - Town.goToTown(); - Pather.useWaypoint(sdk.areas.DarkWood); + if (i === 1) { + Town.goToTown(); + Pather.useWaypoint(sdk.areas.DarkWood); - for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i++) { - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; + for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i++) { + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } } } - } - Town.goToTown(5); - Town.move("portalspot"); - - hotCheck = false; - } else if (getTickCount() - hotTick > Time.seconds(30)) { - // maybe we missed the message, go ahead and enter throne - if (!throneCheck && !baalCheck) { - throneCheck = true; + Town.goToTown(5); + Town.move("portalspot"); + hotCheck = false; + } else if (getTickCount() - hotTick > Time.seconds(30)) { + // maybe we missed the message, go ahead and enter throne + if (!throneCheck && !baalCheck) { + throneCheck = true; + hotCheck = false; + } } } - } - // wait for throne signal - leader's safe message - if ((throneCheck || baalCheck) && me.inArea(sdk.areas.Harrogath)) { - console.log("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); - // move to a safe spot - Pather.moveTo(Config.AutoBaal.LeechSpot[0], Config.AutoBaal.LeechSpot[1]); - Precast.doPrecast(true); - Town.getCorpse(); - } + // wait for throne signal - leader's safe message + if ((throneCheck || baalCheck) && me.inArea(sdk.areas.Harrogath)) { + console.log("ÿc4AutoBaal: ÿc0Trying to take TP to throne."); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + // move to a safe spot + Pather.moveTo(Config.AutoBaal.LeechSpot[0], Config.AutoBaal.LeechSpot[1]); + Precast.doPrecast(true); + Town.getCorpse(); + } - if (!baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport) { - longRangeSupport(); - } - // wait for baal signal - leader's baal message - if (baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { - // move closer to chamber portal - Pather.moveTo(15092, 5010); - Precast.doPrecast(false); - - // wait for baal to go through the portal - while (Game.getMonster(sdk.monsters.ThroneBaal)) { - delay(500); + if (!baalCheck && me.inArea(sdk.areas.ThroneofDestruction) && Config.AutoBaal.LongRangeSupport) { + longRangeSupport(); } + // wait for baal signal - leader's baal message + if (baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { + // move closer to chamber portal + Pather.moveTo(15092, 5010); + Precast.doPrecast(false); + + // wait for baal to go through the portal + while (Game.getMonster(sdk.monsters.ThroneBaal)) { + delay(500); + } - let portal = Game.getObject(sdk.objects.WorldstonePortal); + let portal = Game.getObject(sdk.objects.WorldstonePortal); - delay(2000); // wait for others to enter first - helps with curses and tentacles from spawning around you - console.log("ÿc4AutoBaal: ÿc0Entering chamber."); - Pather.usePortal(null, null, portal) && Pather.moveTo(15166, 5903); // go to a safe position - Town.getCorpse(); - } + delay(2000); // wait for others to enter first - helps with curses and tentacles from spawning around you + console.log("ÿc4AutoBaal: ÿc0Entering chamber."); + Pather.usePortal(null, null, portal) && Pather.moveTo(15166, 5903); // go to a safe position + Town.getCorpse(); + } - let baal = Game.getMonster(sdk.monsters.Baal); + let baal = Game.getMonster(sdk.monsters.Baal); - if (baal) { - if (baal.dead) { - break; - } + if (baal) { + if (baal.dead) { + break; + } - longRangeSupport(); - } + longRangeSupport(); + } - me.mode === sdk.player.mode.Dead && me.revive(); + me.mode === sdk.player.mode.Dead && me.revive(); - delay(500); + delay(500); + } + } else { + throw new Error("Empty game."); } - } else { - throw new Error("Empty game."); + } finally { + removeEventListener("chatmsg", chatEvent); } - } finally { - removeEventListener("chatmsg", chatEvent); - } - return true; -} + return true; + }, + sdk.areas.Harrogath +); diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index c0b22a435..b2728435a 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -5,91 +5,100 @@ * */ -function Baal () { - include("core/Common/Baal.js"); - const announce = function () { - let count, string, souls, dolls; - let monster = Game.getMonster(); - - if (monster) { - count = 0; - - do { - if (monster.attackable && monster.y < 5094) { - monster.distance <= 40 && (count += 1); - !souls && monster.classid === sdk.monsters.BurningSoul1 && (souls = true); - !dolls && monster.classid === sdk.monsters.SoulKiller && (dolls = true); - } - } while (monster.getNext()); +const Baal = new Runnable( + function Baal () { + include("core/Common/Baal.js"); + const announce = function () { + let count, string, souls, dolls; + let monster = Game.getMonster(); + + if (monster) { + count = 0; + + do { + if (monster.attackable && monster.y < 5094) { + monster.distance <= 40 && (count += 1); + !souls && monster.classid === sdk.monsters.BurningSoul1 && (souls = true); + !dolls && monster.classid === sdk.monsters.SoulKiller && (dolls = true); + } + } while (monster.getNext()); + } + + if (count > 30) { + string = "DEADLY!!!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else if (count > 20) { + string = "Lethal!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else if (count > 10) { + string = "Dangerous!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else if (count > 0) { + string = "Warm" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; + } else { + string = "Cool TP. No immediate monsters."; + } + + if (souls) { + string += " Souls "; + dolls && (string += "and Dolls "); + string += "in area."; + } else if (dolls) { + string += " Dolls in area."; + } + + say(string); + }; + + // We can skip chores if it's been less than a minute since we last did them + if (getTickCount() - Town.lastChores > Time.minutes(1)) { + Town.doChores(); } - if (count > 30) { - string = "DEADLY!!!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else if (count > 20) { - string = "Lethal!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else if (count > 10) { - string = "Dangerous!" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else if (count > 0) { - string = "Warm" + " " + count + " monster" + (count > 1 ? "s " : " ") + "nearby."; - } else { - string = "Cool TP. No immediate monsters."; + if (!me.inArea(sdk.areas.WorldstoneLvl2)) { + Config.RandomPrecast + ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) + : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); } - if (souls) { - string += " Souls "; - dolls && (string += "and Dolls "); - string += "in area."; - } else if (dolls) { - string += " Dolls in area."; + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { + throw new Error("Failed to move to Throne of Destruction."); } - say(string); - }; - - Town.doChores(); - Config.RandomPrecast - ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) - : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); - !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + Pather.moveToEx(15095, 5029, { callback: () => { + if (Config.Baal.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + say("Dolls found! NG."); + throw new ScriptError("Dolls found! NG."); + } + + if (Config.Baal.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + say("Souls found! NG."); + throw new ScriptError("Souls found! NG."); + } + } }); + + if (Config.PublicMode) { + announce(); + Pather.moveTo(15118, 5002); + Pather.makePortal(); + say(Config.Baal.HotTPMessage); + Attack.clear(15); + } - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { - throw new Error("Failed to move to Throne of Destruction."); - } + Common.Baal.clearThrone(); - Pather.moveToEx(15095, 5029, { callback: () => { - if (Config.Baal.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - say("Dolls found! NG."); - throw new ScriptError("Dolls found! NG."); + if (Config.PublicMode) { + Pather.moveTo(15118, 5045); + Pather.makePortal(); + say(Config.Baal.SafeTPMessage); + Precast.doPrecast(true); } - if (Config.Baal.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { - say("Souls found! NG."); - throw new ScriptError("Souls found! NG."); + if (!Common.Baal.clearWaves()) { + throw new Error("Couldn't clear baal waves"); } - } }); - - if (Config.PublicMode) { - announce(); - Pather.moveTo(15118, 5002); - Pather.makePortal(); - say(Config.Baal.HotTPMessage); - Attack.clear(15); - } - - Common.Baal.clearThrone(); - - if (Config.PublicMode) { - Pather.moveTo(15118, 5045); - Pather.makePortal(); - say(Config.Baal.SafeTPMessage); - Precast.doPrecast(true); - } - - if (!Common.Baal.clearWaves()) { - throw new Error("Couldn't clear baal waves"); - } - Config.Baal.KillBaal && Common.Baal.killBaal(); + Config.Baal.KillBaal && Common.Baal.killBaal(); - return true; -} + return true; + }, + sdk.areas.WorldstoneLvl2 +); diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index 7dadd969f..030a84f19 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -12,491 +12,494 @@ * - override Misc.getShrinesInArea to end when we recieve safeCheck message */ -function BaalAssistant () { - include("core/Common/Baal.js"); - let Leader = Config.Leader; - let Helper = Config.BaalAssistant.Helper; - let firstAttempt = true; - let quitFlag = false; - let [hotCheck, safeCheck, baalCheck, ngCheck, baalIsDead] = [false, false, false, false, false]; - let [ShrineStatus, secondAttempt, throneStatus, killTracker] = [false, false, false, false]; - - // convert all messages to lowercase - Config.BaalAssistant.HotTPMessage.forEach((msg, i) => { - Config.BaalAssistant.HotTPMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.SafeTPMessage.forEach((msg, i) => { - Config.BaalAssistant.SafeTPMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.BaalMessage.forEach((msg, i) => { - Config.BaalAssistant.BaalMessage[i] = msg.toLowerCase(); - }); - - Config.BaalAssistant.NextGameMessage.forEach((msg, i) => { - Config.BaalAssistant.NextGameMessage[i] = msg.toLowerCase(); - }); - - const chatEvent = function (nick, msg) { - if (nick === Leader) { - if ((Config.BaalAssistant.DollQuit && msg === "Dolls found! NG.") - || (Config.BaalAssistant.SoulQuit && msg === "Souls found! NG.")) { - quitFlag = true; - - return; - } +const BaalAssisstant = new Runnable( + function BaalAssistant () { + include("core/Common/Baal.js"); + let Leader = Config.Leader; + let Helper = Config.BaalAssistant.Helper; + let firstAttempt = true; + let quitFlag = false; + let [hotCheck, safeCheck, baalCheck, ngCheck, baalIsDead] = [false, false, false, false, false]; + let [ShrineStatus, secondAttempt, throneStatus, killTracker] = [false, false, false, false]; + + // convert all messages to lowercase + Config.BaalAssistant.HotTPMessage.forEach((msg, i) => { + Config.BaalAssistant.HotTPMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.SafeTPMessage.forEach((msg, i) => { + Config.BaalAssistant.SafeTPMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.BaalMessage.forEach((msg, i) => { + Config.BaalAssistant.BaalMessage[i] = msg.toLowerCase(); + }); + + Config.BaalAssistant.NextGameMessage.forEach((msg, i) => { + Config.BaalAssistant.NextGameMessage[i] = msg.toLowerCase(); + }); + + const chatEvent = function (nick, msg) { + if (nick === Leader) { + if ((Config.BaalAssistant.DollQuit && msg === "Dolls found! NG.") + || (Config.BaalAssistant.SoulQuit && msg === "Souls found! NG.")) { + quitFlag = true; + + return; + } - msg = msg.toLowerCase(); + msg = msg.toLowerCase(); - for (let i = 0; i < Config.BaalAssistant.HotTPMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.HotTPMessage[i])) { - hotCheck = true; - break; + for (let i = 0; i < Config.BaalAssistant.HotTPMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.HotTPMessage[i])) { + hotCheck = true; + break; + } } - } - for (let i = 0; i < Config.BaalAssistant.SafeTPMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.SafeTPMessage[i])) { - safeCheck = true; - break; + for (let i = 0; i < Config.BaalAssistant.SafeTPMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.SafeTPMessage[i])) { + safeCheck = true; + break; + } } - } - for (let i = 0; i < Config.BaalAssistant.BaalMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.BaalMessage[i])) { - baalCheck = true; - break; + for (let i = 0; i < Config.BaalAssistant.BaalMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.BaalMessage[i])) { + baalCheck = true; + break; + } } - } - for (let i = 0; i < Config.BaalAssistant.NextGameMessage.length; i += 1) { - if (msg.includes(Config.BaalAssistant.NextGameMessage[i])) { - ngCheck = true; - killTracker = true; - break; + for (let i = 0; i < Config.BaalAssistant.NextGameMessage.length; i += 1) { + if (msg.includes(Config.BaalAssistant.NextGameMessage[i])) { + ngCheck = true; + killTracker = true; + break; + } } } - } - }; + }; - const baalDeathEvent = function (bytes = []) { - if (!bytes.length || bytes.length !== 2) return; + const baalDeathEvent = function (bytes = []) { + if (!bytes.length || bytes.length !== 2) return; - if (bytes[0] === sdk.packets.recv.UniqueEvents && bytes[1] === 0x13) { - baalIsDead = true; - } - }; - - const checkParty = function () { - for (let i = 0; i < Config.BaalAssistant.Wait; i += 1) { - let partycheck = getParty(); - if (partycheck) { - do { - if (partycheck.area === sdk.areas.ThroneofDestruction) return false; - if (partycheck.area === sdk.areas.RiverofFlame || partycheck.area === sdk.areas.ChaosSanctuary) return true; - } while (partycheck.getNext()); + if (bytes[0] === sdk.packets.recv.UniqueEvents && bytes[1] === 0x13) { + baalIsDead = true; } + }; - delay(1000); - } - - return false; - }; - - // Start - const Worker = require("../modules/Worker"); - - if (Leader) { - if (!Misc.poll(() => Misc.inMyParty(Leader), Time.seconds(30), 1000)) { - throw new Error("BaalAssistant: Leader not partied"); - } - } - - try { - addEventListener("chatmsg", chatEvent); + const checkParty = function () { + for (let i = 0; i < Config.BaalAssistant.Wait; i += 1) { + let partycheck = getParty(); + if (partycheck) { + do { + if (partycheck.area === sdk.areas.ThroneofDestruction) return false; + if (partycheck.area === sdk.areas.RiverofFlame || partycheck.area === sdk.areas.ChaosSanctuary) return true; + } while (partycheck.getNext()); + } - let killLeaderTracker = false; - if (!Leader && (Config.BaalAssistant.KillNihlathak || Config.BaalAssistant.FastChaos)) { - // run background auto detect so we don't miss messages while running add ons - let leadTick = getTickCount(); + delay(1000); + } - Worker.runInBackground.leaderTracker = function () { - if (killLeaderTracker || killTracker) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); + return false; + }; - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) return false; + // Start + const Worker = require("../modules/Worker"); - let party = getParty(); + if (Leader) { + if (!Misc.poll(() => Misc.inMyParty(Leader), Time.seconds(30), 1000)) { + throw new Error("BaalAssistant: Leader not partied"); + } + } - if (party) { - do { - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { - Leader = party.name; - console.log(sdk.colors.DarkGold + "Autodected " + Leader); - return false; - } - } while (party.getNext()); - } + try { + addEventListener("chatmsg", chatEvent); - return true; - }; - } + let killLeaderTracker = false; + if (!Leader && (Config.BaalAssistant.KillNihlathak || Config.BaalAssistant.FastChaos)) { + // run background auto detect so we don't miss messages while running add ons + let leadTick = getTickCount(); - Config.BaalAssistant.KillNihlathak && Loader.runScript("Nihlathak"); - Config.BaalAssistant.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); - - Town.goToTown(5); - Town.doChores(); - - if (Leader - || (Leader = Misc.autoLeaderDetect({ - destination: sdk.areas.WorldstoneLvl3, - quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area) - })) - || (Leader = Misc.autoLeaderDetect({ - destination: sdk.areas.ThroneofDestruction, - quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) - }))) { - print("ÿc Config.Diablo.Fast = true); - Worker.runInBackground.deathTracker = function () { + Town.goToTown(5); + Town.doChores(); + + if (Leader + || (Leader = Misc.autoLeaderDetect({ + destination: sdk.areas.WorldstoneLvl3, + quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area) + })) + || (Leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ThroneofDestruction, + quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) + }))) { + print("ÿc hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); + if (Config.LifeChicken <= 0) { + let deadTick = getTickCount(); - if (!hotCheck) { - print("ÿc1Leader didn't tell me to start hunting for an experience shrine."); - ShrineStatus = true; - } - } + Worker.runInBackground.deathTracker = function () { + if (killTracker) return false; + // check every 3 seconds + if (getTickCount() - deadTick < 3000) return true; + deadTick = getTickCount(); - // don't waste time looking for seal if party is already killing baal, he'll be dead by the time we find one - if (!ShrineStatus && !baalCheck) { - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - let i; + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + + if (me.dead) { + console.log("I died"); + me.revive(); + delay(500); - for (i = sdk.areas.StonyField; i > sdk.areas.RogueEncampment; i -= 1) { - if (safeCheck) { - break; - } - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; + if (me.inTown) { + Town.move("portalspot"); + baalCheck + ? Pather.usePortal(sdk.areas.WorldstoneChamber, null) + : Pather.usePortal(sdk.areas.ThroneofDestruction, null); } } - if (!safeCheck) { - if (i === sdk.areas.RogueEncampment) { - Town.goToTown(); - Pather.useWaypoint(sdk.areas.DarkWood); - Precast.doPrecast(true); + return true; + }; + } - for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i += 1) { - if (safeCheck) { - break; - } - if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { - break; - } - } + while (Misc.inMyParty(Leader)) { + if (!secondAttempt && !safeCheck && !baalCheck + && !ShrineStatus && !!Config.BaalAssistant.GetShrine + && me.inArea(sdk.areas.Harrogath)) { + if (!!Config.BaalAssistant.GetShrineWaitForHotTP) { + Misc.poll(() => hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); + + if (!hotCheck) { + print("ÿc1Leader didn't tell me to start hunting for an experience shrine."); + ShrineStatus = true; } } - } - - Town.goToTown(5); - ShrineStatus = true; - } - if (firstAttempt && !secondAttempt && !safeCheck - && !baalCheck && !me.inArea(sdk.areas.ThroneofDestruction) - && !me.inArea(sdk.areas.WorldstoneChamber)) { - !!Config.RandomPrecast - ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) - : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); - } + // don't waste time looking for seal if party is already killing baal, he'll be dead by the time we find one + if (!ShrineStatus && !baalCheck) { + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + let i; - if (!me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { - if (Config.BaalAssistant.SkipTP) { - if (firstAttempt && !secondAttempt) { - !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { - throw new Error("Failed to move to WSK3."); + for (i = sdk.areas.StonyField; i > sdk.areas.RogueEncampment; i -= 1) { + if (safeCheck) { + break; + } + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } } - checkParty(); - let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); - if (entrance) { - let [x, y] = [ - entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, - entrance.y > me.y ? entrance.y - 5 : entrance.y + 5 - ]; - Pather.moveTo(x, y); + if (!safeCheck) { + if (i === sdk.areas.RogueEncampment) { + Town.goToTown(); + Pather.useWaypoint(sdk.areas.DarkWood); + Precast.doPrecast(true); + + for (i = sdk.areas.DarkWood; i < sdk.areas.DenofEvil; i += 1) { + if (safeCheck) { + break; + } + if (Misc.getShrinesInArea(i, sdk.shrines.Experience, true)) { + break; + } + } + } } + } - if (!Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) || !Pather.moveTo(15118, 5002)) { - throw new Error("Failed to move to Throne of Destruction."); - } + Town.goToTown(5); + ShrineStatus = true; + } + + if (firstAttempt && !secondAttempt && !safeCheck + && !baalCheck && !me.inArea(sdk.areas.ThroneofDestruction) + && !me.inArea(sdk.areas.WorldstoneChamber)) { + !!Config.RandomPrecast + ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) + : Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + } - Pather.moveToEx(15095, 5029, { callback: () => { - if (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - console.log("Undead Soul Killers found, ending script."); - throw new ScriptError("Undead Soul Killers found, ending script."); + if (!me.inArea(sdk.areas.ThroneofDestruction) && !me.inArea(sdk.areas.WorldstoneChamber)) { + if (Config.BaalAssistant.SkipTP) { + if (firstAttempt && !secondAttempt) { + !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { + throw new Error("Failed to move to WSK3."); } - if (Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { - console.log("Burning Souls found, ending script."); - throw new ScriptError("Burning Souls found, ending script."); + checkParty(); + let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); + if (entrance) { + let [x, y] = [ + entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, + entrance.y > me.y ? entrance.y - 5 : entrance.y + 5 + ]; + Pather.moveTo(x, y); } - } }); - Pather.moveTo(15118, 5002); - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + if (!Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) || !Pather.moveTo(15118, 5002)) { + throw new Error("Failed to move to Throne of Destruction."); + } - secondAttempt = true; - safeCheck = true; - } else { - if (me.inTown) { - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); + Pather.moveToEx(15095, 5029, { callback: () => { + if (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + console.log("Undead Soul Killers found, ending script."); + throw new ScriptError("Undead Soul Killers found, ending script."); + } + + if (Config.BaalAssistant.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + console.log("Burning Souls found, ending script."); + throw new ScriptError("Burning Souls found, ending script."); + } + } }); + + Pather.moveTo(15118, 5002); Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - } - } - } else { - if (firstAttempt && !secondAttempt) { - !me.inArea(sdk.areas.Harrogath) && Pather.useWaypoint(sdk.areas.Harrogath); - Town.move("portalspot"); - if (Config.BaalAssistant.WaitForSafeTP - && !Misc.poll(() => safeCheck, Time.seconds(Config.BaalAssistant.Wait), 1000)) { - throw new Error("No safe TP message."); + secondAttempt = true; + safeCheck = true; + } else { + if (me.inTown) { + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + } } + } else { + if (firstAttempt && !secondAttempt) { + !me.inArea(sdk.areas.Harrogath) && Pather.useWaypoint(sdk.areas.Harrogath); + Town.move("portalspot"); - if ((Config.BaalAssistant.SoulQuit || Config.BaalAssistant.DollQuit) && quitFlag) { - throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); - } + if (Config.BaalAssistant.WaitForSafeTP + && !Misc.poll(() => safeCheck, Time.seconds(Config.BaalAssistant.Wait), 1000)) { + throw new Error("No safe TP message."); + } - if (!Misc.poll( - () => Pather.usePortal(sdk.areas.ThroneofDestruction, null), - Time.seconds(Config.BaalAssistant.Wait), - 1000 - )) { - throw new Error("No portals to Throne."); - } + if ((Config.BaalAssistant.SoulQuit || Config.BaalAssistant.DollQuit) && quitFlag) { + throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); + } - if ((Config.BaalAssistant.SoulQuit - && Game.getMonster(sdk.monsters.BurningSoul1)) - || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { - throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); - } + if (!Misc.poll( + () => Pather.usePortal(sdk.areas.ThroneofDestruction, null), + Time.seconds(Config.BaalAssistant.Wait), + 1000 + )) { + throw new Error("No portals to Throne."); + } + + if ((Config.BaalAssistant.SoulQuit + && Game.getMonster(sdk.monsters.BurningSoul1)) + || (Config.BaalAssistant.DollQuit && Game.getMonster(sdk.monsters.SoulKiller))) { + throw new ScriptError("Burning Souls or Undead Soul Killers found, ending script."); + } - Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); - secondAttempt = true; - safeCheck = true; - } else { - if (me.inTown) { - Town.move("portalspot"); - Pather.usePortal(sdk.areas.ThroneofDestruction, null); Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + secondAttempt = true; + safeCheck = true; + } else { + if (me.inTown) { + Town.move("portalspot"); + Pather.usePortal(sdk.areas.ThroneofDestruction, null); + Helper ? Attack.clear(15) && Pather.moveTo(15118, 5002) : Pather.moveTo(15117, 5045); + } } } } - } - - if (safeCheck && !baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { - if (!baalCheck && !throneStatus) { - if (Helper) { - Attack.clear(15); - Common.Baal.clearThrone(); - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - Precast.doPrecast(true); - } - - let tick = getTickCount(); - MainLoop: while (true) { + if (safeCheck && !baalCheck && me.inArea(sdk.areas.ThroneofDestruction)) { + if (!baalCheck && !throneStatus) { if (Helper) { - if (getDistance(me, 15094, me.paladin ? 5029 : 5038) > 3) { - Pather.moveTo(15094, me.paladin ? 5029 : 5038); - } - } - - if (!Game.getMonster(sdk.monsters.ThroneBaal)) { - break; + Attack.clear(15); + Common.Baal.clearThrone(); + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + Precast.doPrecast(true); } - switch (Common.Baal.checkThrone(Helper)) { - case 1: - Helper && Attack.clear(40); - tick = getTickCount(); + let tick = getTickCount(); - break; - case 2: - Helper && Attack.clear(40); - tick = getTickCount(); + MainLoop: while (true) { + if (Helper) { + if (getDistance(me, 15094, me.paladin ? 5029 : 5038) > 3) { + Pather.moveTo(15094, me.paladin ? 5029 : 5038); + } + } - break; - case 4: - Helper && Attack.clear(40); - tick = getTickCount(); + if (!Game.getMonster(sdk.monsters.ThroneBaal)) { + break; + } - break; - case 3: - Helper && Attack.clear(40) && Common.Baal.checkHydra(); - tick = getTickCount(); + switch (Common.Baal.checkThrone(Helper)) { + case 1: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 2: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 4: + Helper && Attack.clear(40); + tick = getTickCount(); + + break; + case 3: + Helper && Attack.clear(40) && Common.Baal.checkHydra(); + tick = getTickCount(); + + break; + case 5: + if (Helper) { + Attack.clear(40); + } else { + while ([sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2] + .map((unitId) => Game.getMonster(unitId)) + .filter(Boolean).some((unit) => unit.attackable)) { + delay(1000); + } - break; - case 5: - if (Helper) { - Attack.clear(40); - } else { - while ([sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2] - .map((unitId) => Game.getMonster(unitId)) - .filter(Boolean).some((unit) => unit.attackable)) { delay(1000); } - delay(1000); - } + break MainLoop; + default: + if (getTickCount() - tick < 7e3) { + if (me.paladin && me.getState(sdk.states.Poison) + && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + break; + } + } - break MainLoop; - default: - if (getTickCount() - tick < 7e3) { - if (me.paladin && me.getState(sdk.states.Poison) - && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { - break; + if (Helper && !Common.Baal.preattack()) { + delay(100); } - } - if (Helper && !Common.Baal.preattack()) { - delay(100); + break; } - - break; + delay(10); } - delay(10); + throneStatus = true; + baalCheck = true; } - throneStatus = true; - baalCheck = true; } - } - if ((throneStatus || baalCheck) - && Config.BaalAssistant.KillBaal - && me.inArea(sdk.areas.ThroneofDestruction)) { - Helper ? Pather.moveTo(15090, 5008) && delay(2000) : Pather.moveTo(15090, 5010); - Precast.doPrecast(true); - !Helper && addEventListener("gamepacket", baalDeathEvent); + if ((throneStatus || baalCheck) + && Config.BaalAssistant.KillBaal + && me.inArea(sdk.areas.ThroneofDestruction)) { + Helper ? Pather.moveTo(15090, 5008) && delay(2000) : Pather.moveTo(15090, 5010); + Precast.doPrecast(true); + !Helper && addEventListener("gamepacket", baalDeathEvent); - while (Game.getMonster(sdk.monsters.ThroneBaal)) { - delay(500); - } + while (Game.getMonster(sdk.monsters.ThroneBaal)) { + delay(500); + } - let portal = Game.getObject(sdk.objects.WorldstonePortal); + let portal = Game.getObject(sdk.objects.WorldstonePortal); - if (portal) { - delay((Helper ? 1000 : 4000)); - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } + if (portal) { + delay((Helper ? 1000 : 4000)); + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); + } - if (Helper) { - delay(1000); - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); - } else { - Pather.moveTo(15177, 5952); - let baal = Game.getMonster(sdk.monsters.Baal); - - while (!!baal && baal.attackable && !baalIsDead) { + if (Helper) { delay(1000); + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); + } else { + Pather.moveTo(15177, 5952); + let baal = Game.getMonster(sdk.monsters.Baal); + + while (!!baal && baal.attackable && !baalIsDead) { + delay(1000); + } } - } - } else { - // how to accurately know when to end script in the instance of no ngCheck - // listen for baal death packet maybe? - while (!ngCheck && !baalIsDead) { - delay(500); + } else { + // how to accurately know when to end script in the instance of no ngCheck + // listen for baal death packet maybe? + while (!ngCheck && !baalIsDead) { + delay(500); + } } - } - delay(500); + delay(500); + } + } catch (e) { + console.error(e); + } finally { + killTracker = true; } - } catch (e) { - console.error(e); - } finally { - killTracker = true; + } else { + throw new Error("Empty game."); } - } else { - throw new Error("Empty game."); + } finally { + removeEventListener("chatmsg", chatEvent); + removeEventListener("gamepacket", baalDeathEvent); } - } finally { - removeEventListener("chatmsg", chatEvent); - removeEventListener("gamepacket", baalDeathEvent); - } - return true; -} + return true; + }, + sdk.areas.Harrogath +); diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index cbfaae86f..8ae89ca9c 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -5,128 +5,145 @@ * */ -function BaalHelper () { - include("core/Common/Baal.js"); - Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); - Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); - - Town.goToTown(5); - Town.doChores(); - Config.RandomPrecast && Precast.needOutOfTownCast() - ? Precast.doRandomPrecast(true, sdk.areas.Harrogath) - : Precast.doPrecast(true); - - if (Config.BaalHelper.SkipTP) { - !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { - throw new Error("Failed to move to WSK3."); - } - if (!Misc.poll(() => { - let party = getParty(); - if (party) { - do { - if ((!Config.Leader || party.name === Config.Leader) && party.area === sdk.areas.ThroneofDestruction) { - return true; - } - } while (party.getNext()); - } +const BaalHelper = new Runnable( + function BaalHelper () { + include("core/Common/Baal.js"); + Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); + Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); - return false; - }, Time.minutes(Config.BaalHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Throne)"); - } + Town.goToTown(5); - let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); - if (entrance) { - let [x, y] = [ - entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, - entrance.y > me.y ? entrance.y - 5 : entrance.y + 5 - ]; - Pather.moveTo(x, y); + if (getTickCount() - Town.lastChores > Time.minutes(1)) { + Town.doChores(); } + Config.RandomPrecast && Precast.needOutOfTownCast() + ? Precast.doRandomPrecast(true, sdk.areas.Harrogath) + : Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { - throw new Error("Failed to move to WSK3."); - } - if (!Pather.moveToExit(sdk.areas.ThroneofDestruction, true)) { - throw new Error("Failed to move to Throne of Destruction."); - } - Pather.moveToEx(15113, 5040, { callback: () => { - if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - console.log("Undead Soul Killers found, ending script."); - throw new ScriptError("Undead Soul Killers found, ending script."); + if (Config.BaalHelper.SkipTP) { + !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); + + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { + throw new Error("Failed to move to WSK3."); } + if (!Misc.poll(() => { + let party = getParty(); + + if (party) { + do { + if ((!Config.Leader || party.name === Config.Leader) && party.area === sdk.areas.ThroneofDestruction) { + return true; + } + } while (party.getNext()); + } - if (Config.BaalHelper.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { - console.log("Burning Souls found, ending script."); - throw new ScriptError("Burning Souls found, ending script."); + return false; + }, Time.minutes(Config.BaalHelper.Wait), 1000)) { + throw new Error( + "Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Throne)" + ); } - } }); - } else { - Town.goToTown(5); - Town.move("portalspot"); - let quitFlag = false; + let entrance = Misc.poll(() => Game.getStairs(sdk.exits.preset.NextAreaWorldstone), 1000, 200); + if (entrance) { + let [x, y] = [ + entrance.x > me.x ? entrance.x - 5 : entrance.x + 5, + entrance.y > me.y ? entrance.y - 5 : entrance.y + 5 + ]; + Pather.moveTo(x, y); + } - const chatEvent = function (nick, msg) { - if (nick === Config.Leader) { - if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") - || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { - quitFlag = true; - } + if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], false)) { + throw new Error("Failed to move to WSK3."); } - }; + if (!Pather.moveToExit(sdk.areas.ThroneofDestruction, true)) { + throw new Error("Failed to move to Throne of Destruction."); + } + Pather.moveToEx(15113, 5040, { callback: () => { + if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + console.log("Undead Soul Killers found, ending script."); + throw new ScriptError("Undead Soul Killers found, ending script."); + } - if (Config.BaalHelper.DollQuit || Config.BaalHelper.SoulQuit) { - addEventListener("chatmsg", chatEvent); - } + if (Config.BaalHelper.SoulQuit && Game.getMonster(sdk.monsters.BurningSoul1)) { + console.log("Burning Souls found, ending script."); + throw new ScriptError("Burning Souls found, ending script."); + } + } }); + } else { + Town.goToTown(5); + Town.move("portalspot"); + + let quitFlag = false; + + const chatEvent = function (nick, msg) { + if (nick === Config.Leader) { + if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") + || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { + quitFlag = true; + } + } + }; - try { - if (!Misc.poll(() => { - if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { - if (quitFlag) throw new ScriptError("Burning Souls or Dolls found, ending script."); - if (Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { - return true; + if (Config.BaalHelper.DollQuit || Config.BaalHelper.SoulQuit) { + addEventListener("chatmsg", chatEvent); + } + + try { + if (!Misc.poll(() => { + if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + if (quitFlag) throw new ScriptError("Burning Souls or Dolls found, ending script."); + if (Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + return true; + } } + + return false; + }, Time.minutes(Config.BaalHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); } + } catch (e) { + console.log(e.message); - return false; - }, Time.minutes(Config.BaalHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + return true; + } finally { + removeEventListener("chatmsg", chatEvent); } - } catch (e) { - console.log(e.message); + } + + if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { + print("Undead Soul Killers found."); return true; - } finally { - removeEventListener("chatmsg", chatEvent); } - } - if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - print("Undead Soul Killers found."); + Precast.doPrecast(false); + Attack.clear(15); + Common.Baal.clearThrone(); + + if (!Common.Baal.clearWaves()) { + throw new Error("Couldn't clear baal waves"); + } - return true; - } + if (Config.BaalHelper.KillBaal) { + Common.Baal.killBaal(); + } else { + Town.goToTown(); + while (true) { + delay(500); + } + } - Precast.doPrecast(false); - Attack.clear(15); - Common.Baal.clearThrone(); - - if (!Common.Baal.clearWaves()) { - throw new Error("Couldn't clear baal waves"); + return true; } +); - if (Config.BaalHelper.KillBaal) { - Common.Baal.killBaal(); - } else { - Town.goToTown(); - while (true) { - delay(500); +Object.defineProperty(BaalHelper, "startArea", { + get: function() { + if (Config.BaalHelper.KillNihlathak || !Config.BaalHelper.FastChaos) { + return sdk.areas.Harrogath; } - } - - return true; -} + return sdk.areas.RiverofFlame; + }, +}); diff --git a/d2bs/kolbot/libs/scripts/BattleOrders.js b/d2bs/kolbot/libs/scripts/BattleOrders.js index acbc3bda2..a8683828d 100644 --- a/d2bs/kolbot/libs/scripts/BattleOrders.js +++ b/d2bs/kolbot/libs/scripts/BattleOrders.js @@ -7,303 +7,304 @@ // todo - define bo-er name, so bots who are getting bo know who is supposed to give it // todo - use profile <-> profile communication so we don't need to set char names, Maybe shout global? +const BattleOrders = new Runnable( + function BattleOrders () { + this.gaveBo = false; + /** @type {Set} */ + const totalBoed = new Set(); + /** @type {Set} */ + const boGetters = new Set(Config.BattleOrders.Getters.map(name => name.toLowerCase())); + + const boMode = { + Give: 0, + Receive: 1 + }; -function BattleOrders () { - this.gaveBo = false; - /** @type {Set} */ - const totalBoed = new Set(); - /** @type {Set} */ - const boGetters = new Set(Config.BattleOrders.Getters.map(name => name.toLowerCase())); - - const boMode = { - Give: 0, - Receive: 1 - }; - - // convert all names in getter to lowercase - Config.BattleOrders.Getters.forEach((name, index) => { - Config.BattleOrders.Getters[index] = name.toLowerCase(); - }); - - function checkForPlayers () { - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - } - - function log (msg = "") { - console.log(msg); - me.overhead(msg); - } - - function tardy () { - let party; - - AreaInfoLoop: - while (true) { - try { - checkForPlayers(); - } catch (e) { - if (Config.BattleOrders.Wait) { - let counter = 0; - console.log("Waiting " + Config.BattleOrders.Wait + " seconds for other players..."); - - Misc.poll(() => { - counter++; - me.overhead( - "Waiting " + Math.round(((tick + Time.seconds(Config.BattleOrders.Wait)) - getTickCount()) / 1000) - + " Seconds for other players" - ); - if (counter % 5 === 0) { - return checkForPlayers(); + function checkForPlayers () { + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + } + + function log (msg = "") { + console.log(msg); + me.overhead(msg); + } + + function tardy () { + let party; + + AreaInfoLoop: + while (true) { + try { + checkForPlayers(); + } catch (e) { + if (Config.BattleOrders.Wait) { + let counter = 0; + console.log("Waiting " + Config.BattleOrders.Wait + " seconds for other players..."); + + Misc.poll(() => { + counter++; + me.overhead( + "Waiting " + Math.round(((tick + Time.seconds(Config.BattleOrders.Wait)) - getTickCount()) / 1000) + + " Seconds for other players" + ); + if (counter % 5 === 0) { + return checkForPlayers(); + } + return false; + }, Time.seconds(Config.BattleOrders.Wait), Time.seconds(1)); + + continue; + } else { + console.error(e); + // emptry game, don't wait + return true; + } + } + + party = getParty(); + + if (party) { + do { + if (party.name !== me.name && party.area) { + break AreaInfoLoop; // Can read player area } - return false; - }, Time.seconds(Config.BattleOrders.Wait), Time.seconds(1)); - - continue; - } else { - console.error(e); - // emptry game, don't wait - return true; + } while (party.getNext()); } - } - party = getParty(); + delay(500); + } if (party) { do { - if (party.name !== me.name && party.area) { - break AreaInfoLoop; // Can read player area + if ([ + sdk.areas.MooMooFarm, + sdk.areas.ChaosSanctuary, + sdk.areas.ThroneofDestruction, + sdk.areas.WorldstoneChamber + ].includes(party.area)) { + log("ÿc1I'm late to BOs. Moving on..."); + + return true; } } while (party.getNext()); } - delay(500); + return false; // Not late; wait. } - if (party) { - do { - if ([ - sdk.areas.MooMooFarm, - sdk.areas.ChaosSanctuary, - sdk.areas.ThroneofDestruction, - sdk.areas.WorldstoneChamber - ].includes(party.area)) { - log("ÿc1I'm late to BOs. Moving on..."); - - return true; - } - } while (party.getNext()); - } - - return false; // Not late; wait. - } + // bo is AoE, lets build a list of all players near us so we can know who we boed + function giveBO () { + // more players might be showing up, give a moment and lets wait until the nearby player count is static + let nearPlayers = 0; + let tick = getTickCount(); + + // if we haven't already given a bo, lets wait to see if more players show up + if (!BattleOrders.gaveBo) { + nearPlayers = Misc.getNearbyPlayerCount(); + while (nearPlayers !== boGetters.size) { + if (getTickCount() - tick >= Time.seconds(30)) { + log("Begin"); - // bo is AoE, lets build a list of all players near us so we can know who we boed - function giveBO () { - // more players might be showing up, give a moment and lets wait until the nearby player count is static - let nearPlayers = 0; - let tick = getTickCount(); - - // if we haven't already given a bo, lets wait to see if more players show up - if (!BattleOrders.gaveBo) { - nearPlayers = Misc.getNearbyPlayerCount(); - while (nearPlayers !== boGetters.size) { - if (getTickCount() - tick >= Time.seconds(30)) { - log("Begin"); + break; + } - break; + me.overhead( + "Waiting " + Math.round(((tick + Time.seconds(30)) - getTickCount()) / 1000) + + " for all players to show up" + ); + nearPlayers = Misc.getNearbyPlayerCount(); + delay(1000); } - - me.overhead( - "Waiting " + Math.round(((tick + Time.seconds(30)) - getTickCount()) / 1000) - + " for all players to show up" - ); - nearPlayers = Misc.getNearbyPlayerCount(); - delay(1000); } - } - let boed = false; - const playersToBo = getUnits(sdk.unittype.Player) - .filter(p => boGetters.has(p.name.toLowerCase()) && p.distance < 20); - playersToBo.forEach(p => { - tick = getTickCount(); + let boed = false; + const playersToBo = getUnits(sdk.unittype.Player) + .filter(p => boGetters.has(p.name.toLowerCase()) && p.distance < 20); + playersToBo.forEach(p => { + tick = getTickCount(); - if (copyUnit(p).x) { - while (!p.getState(sdk.states.BattleOrders) && copyUnit(p).x) { - if (getTickCount() - tick >= Time.minutes(1)) { - log("ÿc1BO timeout fail."); + if (copyUnit(p).x) { + while (!p.getState(sdk.states.BattleOrders) && copyUnit(p).x) { + if (getTickCount() - tick >= Time.minutes(1)) { + log("ÿc1BO timeout fail."); - if (Config.BattleOrders.QuitOnFailure) { - quit(); + if (Config.BattleOrders.QuitOnFailure) { + quit(); + } + + break; } - break; + Precast.doPrecast(true); + delay(1000); } - Precast.doPrecast(true); - delay(1000); + totalBoed.add(p.name.toLowerCase()); + console.debug("Bo-ed " + p.name); + boed = true; } + }); - totalBoed.add(p.name.toLowerCase()); - console.debug("Bo-ed " + p.name); - boed = true; + if (boed) { + delay(5000); } - }); - if (boed) { - delay(5000); + return { + success: boed, + count: playersToBo.length + }; } - return { - success: boed, - count: playersToBo.length - }; - } - - // START - Town.doChores(); - - try { - Pather.useWaypoint(sdk.areas.CatacombsLvl2, true); - } catch (wperror) { - log("ÿc1Failed to take waypoint."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - - return false; - } - - // don't bo until we are ready to do so - Precast.enabled = false; - Pather.moveTo(me.x + 6, me.y + 6); - - let tick = getTickCount(); - let failTimer = Time.minutes(2); - let nearPlayer; - - // Ready - Precast.enabled = true; - - /** - * @param {string} name - * @param {string} msg - */ - function chatEvent (name, msg) { - if (!msg | !name) return; - if (!boGetters.has(name.toLowerCase())) return; - if (msg === "got-bo") { - console.log(name + " got bo"); - totalBoed.add(name.toLowerCase()); + // START + Town.doChores(); + + try { + Pather.useWaypoint(sdk.areas.CatacombsLvl2, true); + } catch (wperror) { + log("ÿc1Failed to take waypoint."); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + + return false; } - } - /** @returns {string[]} */ - function getFailedToBO () { - return Config.BattleOrders.Getters.filter(name => !totalBoed.has(name.toLowerCase())); - } + // don't bo until we are ready to do so + Precast.enabled = false; + Pather.moveTo(me.x + 6, me.y + 6); + + let tick = getTickCount(); + let failTimer = Time.minutes(2); + let nearPlayer; + + // Ready + Precast.enabled = true; + + /** + * @param {string} name + * @param {string} msg + */ + function chatEvent (name, msg) { + if (!msg | !name) return; + if (!boGetters.has(name.toLowerCase())) return; + if (msg === "got-bo") { + console.log(name + " got bo"); + totalBoed.add(name.toLowerCase()); + } + } - try { - if (Config.BattleOrders.Mode === boMode.Give) { - addEventListener("chatmsg", chatEvent); + /** @returns {string[]} */ + function getFailedToBO () { + return Config.BattleOrders.Getters.filter(name => !totalBoed.has(name.toLowerCase())); } - MainLoop: - while (true) { - if (Config.BattleOrders.SkipIfTardy && tardy()) { - break; + try { + if (Config.BattleOrders.Mode === boMode.Give) { + addEventListener("chatmsg", chatEvent); } - switch (Config.BattleOrders.Mode) { - case boMode.Give: - // check if anyone is near us - nearPlayer = Game.getPlayer(); + MainLoop: + while (true) { + if (Config.BattleOrders.SkipIfTardy && tardy()) { + break; + } - if (nearPlayer) { - do { - if (nearPlayer.name !== me.name) { - let nearPlayerName = nearPlayer.name.toLowerCase(); - // there is a player near us and they are in the list of players to bo and in my party - if (boGetters.has(nearPlayerName) - && !totalBoed.has(nearPlayerName) - && Misc.inMyParty(nearPlayerName)) { - let result = giveBO(); - if (result.success) { - if (result.count === boGetters.size - || totalBoed.size === boGetters.size) { - // we bo-ed everyone we are set to, don't wait around any longer - break MainLoop; + switch (Config.BattleOrders.Mode) { + case boMode.Give: + // check if anyone is near us + nearPlayer = Game.getPlayer(); + + if (nearPlayer) { + do { + if (nearPlayer.name !== me.name) { + let nearPlayerName = nearPlayer.name.toLowerCase(); + // there is a player near us and they are in the list of players to bo and in my party + if (boGetters.has(nearPlayerName) + && !totalBoed.has(nearPlayerName) + && Misc.inMyParty(nearPlayerName)) { + let result = giveBO(); + if (result.success) { + if (result.count === boGetters.size + || totalBoed.size === boGetters.size) { + // we bo-ed everyone we are set to, don't wait around any longer + break MainLoop; + } + // reset fail tick + tick = getTickCount(); + // shorten waiting time since we've already started giving out bo's + BattleOrders.gaveBo = true; } - // reset fail tick - tick = getTickCount(); - // shorten waiting time since we've already started giving out bo's - BattleOrders.gaveBo = true; + } + } else { + me.overhead( + "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + + " Seconds for other players" + ); + + if (getTickCount() - tick >= failTimer) { + log("ÿc1Give BO timeout fail."); + log("Failed to bo: " + getFailedToBO().join(", ")); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + + break MainLoop; } } - } else { - me.overhead( - "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) - + " Seconds for other players" - ); + } while (nearPlayer.getNext()); + } else { + me.overhead( + "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) + + " Seconds for other players" + ); - if (getTickCount() - tick >= failTimer) { - log("ÿc1Give BO timeout fail."); - log("Failed to bo: " + getFailedToBO().join(", ")); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + if (getTickCount() - tick >= failTimer) { + log("ÿc1Give BO timeout fail."); + log("Failed to bo: " + getFailedToBO().join(", ")); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - break MainLoop; - } + break MainLoop; } - } while (nearPlayer.getNext()); - } else { - me.overhead( - "Waiting " + Math.round(((tick + failTimer) - getTickCount()) / 1000) - + " Seconds for other players" - ); + } - if (getTickCount() - tick >= failTimer) { - log("ÿc1Give BO timeout fail."); - log("Failed to bo: " + getFailedToBO().join(", ")); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + break; + case boMode.Receive: + if (me.getState(sdk.states.BattleOrders)) { + log("Got bo-ed"); + say("got-bo"); + delay(1000); break MainLoop; } - } - - break; - case boMode.Receive: - if (me.getState(sdk.states.BattleOrders)) { - log("Got bo-ed"); - say("got-bo"); - delay(1000); - break MainLoop; - } + if (getTickCount() - tick >= failTimer) { + log("ÿc1BO timeout fail."); + Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); - if (getTickCount() - tick >= failTimer) { - log("ÿc1BO timeout fail."); - Config.BattleOrders.QuitOnFailure && scriptBroadcast("quit"); + break MainLoop; + } - break MainLoop; + break; } - break; + delay(500); } - delay(500); - } - - (Pather.useWaypoint(sdk.areas.RogueEncampment) || Town.goToTown()); + if (Loader.nextScript && Loader.nextScript.startArea) { + Pather.useWaypoint(Loader.nextScript.startArea); + } else { + (Pather.useWaypoint(sdk.areas.RogueEncampment) || Town.goToTown()); + } - // what's the point of this? - if (Config.BattleOrders.Mode === boMode.Give && Config.BattleOrders.Idle) { - for (let i = 0; i < Config.BattleOrders.Getters.length; i += 1) { - while (Misc.inMyParty(Config.BattleOrders.Getters[i])) { - delay(1000); + // what's the point of this? + if (Config.BattleOrders.Mode === boMode.Give && Config.BattleOrders.Idle) { + for (let i = 0; i < Config.BattleOrders.Getters.length; i += 1) { + while (Misc.inMyParty(Config.BattleOrders.Getters[i])) { + delay(1000); + } } } - } - return true; - } finally { - removeEventListener("chatmsg", chatEvent); - } -} + return true; + } finally { + removeEventListener("chatmsg", chatEvent); + } + }, + sdk.areas.CatacombsLvl2 +); diff --git a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js index c8bb7ca7a..2d4112071 100644 --- a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js +++ b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js @@ -5,18 +5,21 @@ * */ -function BattlemaidSarina () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.KurastBazaar); - Precast.doPrecast(true); +const BattlemaidSarina = new Runnable( + function BattlemaidSarina () { + Town.doChores(); + Pather.useWaypoint(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 near Sarina"); - } + if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder)) { + throw new Error("Failed to move near Sarina"); + } - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.BattlemaidSarina)); - Pickit.pickItems(); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.BattlemaidSarina)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.KurastBazaar +); diff --git a/d2bs/kolbot/libs/scripts/Bishibosh.js b/d2bs/kolbot/libs/scripts/Bishibosh.js index a370b2414..5b9ad7b9c 100644 --- a/d2bs/kolbot/libs/scripts/Bishibosh.js +++ b/d2bs/kolbot/libs/scripts/Bishibosh.js @@ -5,14 +5,17 @@ * */ -function Bishibosh () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); +const Bishibosh = new Runnable( + function Bishibosh () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); - Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Bishibosh)); - Pickit.pickItems(); + Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Bishibosh)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.ColdPlains +); diff --git a/d2bs/kolbot/libs/scripts/BoBarbHelper.js b/d2bs/kolbot/libs/scripts/BoBarbHelper.js index a0af3ae64..d04e85d93 100644 --- a/d2bs/kolbot/libs/scripts/BoBarbHelper.js +++ b/d2bs/kolbot/libs/scripts/BoBarbHelper.js @@ -6,99 +6,102 @@ * */ -function BoBarbHelper () { - if (!me.barbarian && Config.BoBarbHelper.Mode !== 0) return true; - - const townNearbyMonster = true; // go to town if monsters nearby - const townLowMana = 20; // go refill mana if mana drops below this percent - const shouldHealMana = amount => me.mp < Math.floor(me.mpmax * amount / 100); - - const healMana = () => { - Pather.useWaypoint(sdk.areas.RogueEncampment); - Town.initNPC("Heal", "heal"); - Pather.useWaypoint(Config.BoBarbHelper.Wp); - }; - - const shouldBuff = unit => ( - Misc.inMyParty(unit) && - getDistance(me, unit) < 10 && - unit.name !== me.name && - !unit.dead && - !unit.inTown - ); - - const giveBuff = () => { - const unit = Game.getPlayer(); - - do { - if (shouldBuff(unit)) { - Precast.doPrecast(true); - delay(50); - } - } while (unit.getNext()); - }; - - const monsterNear = () => { - const unit = Game.getMonster(); +const BoBarbHelper = new Runnable( + function BoBarbHelper () { + if (!me.barbarian && Config.BoBarbHelper.Mode !== 0) return true; + + const townNearbyMonster = true; // go to town if monsters nearby + const townLowMana = 20; // go refill mana if mana drops below this percent + const shouldHealMana = amount => me.mp < Math.floor(me.mpmax * amount / 100); + + const healMana = () => { + Pather.useWaypoint(sdk.areas.RogueEncampment); + Town.initNPC("Heal", "heal"); + Pather.useWaypoint(Config.BoBarbHelper.Wp); + }; + + const shouldBuff = unit => ( + Misc.inMyParty(unit) && + getDistance(me, unit) < 10 && + unit.name !== me.name && + !unit.dead && + !unit.inTown + ); + + const giveBuff = () => { + const unit = Game.getPlayer(); - if (unit) { do { - if (unit.attackable && getDistance(me, unit) < 20) { - return true; + if (shouldBuff(unit)) { + Precast.doPrecast(true); + delay(50); } } while (unit.getNext()); + }; + + const monsterNear = () => { + const unit = Game.getMonster(); + + if (unit) { + do { + if (unit.attackable && getDistance(me, unit) < 20) { + return true; + } + } while (unit.getNext()); + } + + return false; + }; + + if (!Config.QuitList) { + showConsole(); + print("set Config.QuitList in character settings"); + print("if you don't I will idle indefinitely"); } - return false; - }; - - if (!Config.QuitList) { - showConsole(); - print("set Config.QuitList in character settings"); - print("if you don't I will idle indefinitely"); - } - - if (me.hardcore && Config.LifeChicken <= 0) { - showConsole(); - print("on HARDCORE"); - print("you should set Config.LifeChicken"); - print("monsters can find their way to wps ..."); - delay(2000); - hideConsole(); - me.overhead("set LifeChiken to 40"); - Config.LifeChicken = 40; - } - - shouldHealMana(townLowMana) && Town.initNPC("Heal", "heal"); - Town.heal(); // in case our life is low as well - - try { - Pather.useWaypoint(Config.BoBarbHelper.Wp); - } catch (e) { - showConsole(); - print("Failed to move to BO WP"); - print("make sure I have " + getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); - delay(20000); + if (me.hardcore && Config.LifeChicken <= 0) { + showConsole(); + print("on HARDCORE"); + print("you should set Config.LifeChicken"); + print("monsters can find their way to wps ..."); + delay(2000); + hideConsole(); + me.overhead("set LifeChiken to 40"); + Config.LifeChicken = 40; + } - return true; - } + shouldHealMana(townLowMana) && Town.initNPC("Heal", "heal"); + Town.heal(); // in case our life is low as well + + try { + Pather.useWaypoint(Config.BoBarbHelper.Wp); + } catch (e) { + showConsole(); + print("Failed to move to BO WP"); + print("make sure I have " + getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); + delay(20000); + + return true; + } - Pather.moveTo(me.x + 4, me.y + 4); + Pather.moveTo(me.x + 4, me.y + 4); - while (true) { - giveBuff(); + while (true) { + giveBuff(); - if (townNearbyMonster && monsterNear()) { - if (!Pather.useWaypoint(sdk.areas.RogueEncampment)) { - break; + if (townNearbyMonster && monsterNear()) { + if (!Pather.useWaypoint(sdk.areas.RogueEncampment)) { + break; + } } - } - shouldHealMana(townLowMana) && healMana(); - delay(25); - } + shouldHealMana(townLowMana) && healMana(); + delay(25); + } - Town.goToTown(); + Town.goToTown(); - return true; -} + return true; + }, + Config.BoBarbHelper.Wp +); diff --git a/d2bs/kolbot/libs/scripts/BoneAsh.js b/d2bs/kolbot/libs/scripts/BoneAsh.js index c0600e315..534482bf6 100644 --- a/d2bs/kolbot/libs/scripts/BoneAsh.js +++ b/d2bs/kolbot/libs/scripts/BoneAsh.js @@ -5,15 +5,18 @@ * */ -function BoneAsh () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); +const BoneAsh = new Runnable( + function BoneAsh () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); - if (!Pather.moveTo(20047, 4898)) throw new Error("Failed to move to Bone Ash"); + if (!Pather.moveTo(20047, 4898)) throw new Error("Failed to move to Bone Ash"); - Attack.kill(getLocaleString(sdk.locale.monsters.BoneAsh)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.BoneAsh)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.InnerCloister +); diff --git a/d2bs/kolbot/libs/scripts/Bonesaw.js b/d2bs/kolbot/libs/scripts/Bonesaw.js index f0286a766..d3a56aba0 100644 --- a/d2bs/kolbot/libs/scripts/Bonesaw.js +++ b/d2bs/kolbot/libs/scripts/Bonesaw.js @@ -5,18 +5,21 @@ * */ -function Bonesaw () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.GlacialTrail); - Precast.doPrecast(true); +const Bonesaw = new Runnable( + function Bonesaw () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.GlacialTrail); + Precast.doPrecast(true); - if (!Pather.moveToPresetObject(sdk.areas.GlacialTrail, sdk.objects.LargeSparklyChest, { offX: 15, offY: 15 })) { - throw new Error("Failed to move to Bonesaw"); - } + if (!Pather.moveToPresetObject(sdk.areas.GlacialTrail, sdk.objects.LargeSparklyChest, { offX: 15, offY: 15 })) { + throw new Error("Failed to move to Bonesaw"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.BonesawBreaker)); - if (Config.Bonesaw.ClearDrifterCavern && Pather.moveToExit(sdk.areas.DrifterCavern, true)) { - Attack.clearLevel(Config.ClearType); - } - return true; -} + Attack.kill(getLocaleString(sdk.locale.monsters.BonesawBreaker)); + if (Config.Bonesaw.ClearDrifterCavern && Pather.moveToExit(sdk.areas.DrifterCavern, true)) { + Attack.clearLevel(Config.ClearType); + } + return true; + }, + sdk.areas.GlacialTrail +); diff --git a/d2bs/kolbot/libs/scripts/ChestMania.js b/d2bs/kolbot/libs/scripts/ChestMania.js index db7352bf1..cb9d29dab 100644 --- a/d2bs/kolbot/libs/scripts/ChestMania.js +++ b/d2bs/kolbot/libs/scripts/ChestMania.js @@ -7,34 +7,37 @@ // todo - if we have run ghostsbusters before this then some of these areas don't need to be re-run -function ChestMania () { - Town.doChores(); - const nextToTown = [ - sdk.areas.BloodMoor, - sdk.areas.RockyWaste, - sdk.areas.SpiderForest, - sdk.areas.OuterSteppes, - sdk.areas.BloodyFoothills - ]; +const ChestMania = new Runnable( + function ChestMania () { + Town.doChores(); + const nextToTown = [ + sdk.areas.BloodMoor, + sdk.areas.RockyWaste, + sdk.areas.SpiderForest, + sdk.areas.OuterSteppes, + sdk.areas.BloodyFoothills + ]; - Object.values(Config.ChestMania) - .forEach(function (act) { - for (let area of act) { - if (nextToTown.includes(area)) { - // if we precast as soon as we step out of town it sometimes crashes - so do precast somewhere else first - Precast.doRandomPrecast(false); + Object.values(Config.ChestMania) + .forEach(function (act) { + for (let area of act) { + if (nextToTown.includes(area)) { + // if we precast as soon as we step out of town it sometimes crashes - so do precast somewhere else first + Precast.doRandomPrecast(false); + } + try { + Pather.journeyTo(area); + Precast.doPrecast(false); + Misc.openChestsInArea(area); + } catch (e) { + console.error(e); + } } - try { - Pather.journeyTo(area); - Precast.doPrecast(false); - Misc.openChestsInArea(area); - } catch (e) { - console.error(e); - } - } - Town.doChores(); - }); + Town.doChores(); + }); - return true; -} + return true; + }, + Object.values(Config.ChestMania).find((act) => act.length > 0)[0][0] +); diff --git a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js index b5966a8e5..d93dc20c0 100644 --- a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js +++ b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js @@ -7,124 +7,127 @@ // redo this, maybe different keys or chat commands instead? -function ClassicChaosAssistant () { - include("core/Common/Diablo.js"); - let stargo, infgo, seisgo, vizgo, infseal, seisseal, vizseal, diablopickup, normalpickup = false; +const ClassicChaosAssistant = new Runnable( + function ClassicChaosAssistant () { + include("core/Common/Diablo.js"); + let stargo, infgo, seisgo, vizgo, infseal, seisseal, vizseal, diablopickup, normalpickup = false; - addEventListener("keyup", - function (key) { - switch (key) { - case sdk.keys.Numpad1: - stargo = true; - - break; - case sdk.keys.Numpad2: - infgo = true; - - break; - case sdk.keys.Numpad3: - infseal = true; - - break; - case sdk.keys.Numpad4: - seisgo = true; - - break; - case sdk.keys.Numpad5: - seisseal = true; - - break; - case sdk.keys.Numpad6: - vizgo = true; - - break; - case sdk.keys.Numpad7: - vizseal = true; - - break; - case sdk.keys.Numpad8: // (Open last seal, teleport to star and pickup for 30 seconds) - diablopickup = true; - - break; - case sdk.keys.Numpad9: // (Pickup at current location) - normalpickup = true; - - break; - default: - break; - } - }); + addEventListener("keyup", + function (key) { + switch (key) { + case sdk.keys.Numpad1: + stargo = true; + + break; + case sdk.keys.Numpad2: + infgo = true; + + break; + case sdk.keys.Numpad3: + infseal = true; + + break; + case sdk.keys.Numpad4: + seisgo = true; + + break; + case sdk.keys.Numpad5: + seisseal = true; + + break; + case sdk.keys.Numpad6: + vizgo = true; + + break; + case sdk.keys.Numpad7: + vizseal = true; + + break; + case sdk.keys.Numpad8: // (Open last seal, teleport to star and pickup for 30 seconds) + diablopickup = true; + + break; + case sdk.keys.Numpad9: // (Pickup at current location) + normalpickup = true; + + break; + default: + break; + } + }); - while (true) { - switch (me.area) { - case sdk.areas.ChaosSanctuary: - if (infgo) { - Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); - Pather.makePortal() && say("Infector of Souls TP Up!"); - infgo = false; - } + while (true) { + switch (me.area) { + case sdk.areas.ChaosSanctuary: + if (infgo) { + Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); + Pather.makePortal() && say("Infector of Souls TP Up!"); + infgo = false; + } - if (seisgo) { - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); - Pather.makePortal() && say("Lord De Seis TP Up!"); - seisgo = false; - } + if (seisgo) { + Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); + Pather.makePortal() && say("Lord De Seis TP Up!"); + seisgo = false; + } - if (vizgo) { - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); - Pather.makePortal() && say("Grand Vizier of Chaos TP Up!"); - vizgo = false; - } + if (vizgo) { + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); + Pather.makePortal() && say("Grand Vizier of Chaos TP Up!"); + vizgo = false; + } - if (infseal) { - Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2); - Common.Diablo.openSeal(sdk.objects.DiabloSealInfector) && say("Infector of Souls spawned!"); - Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); - infseal = false; - } + if (infseal) { + Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2); + Common.Diablo.openSeal(sdk.objects.DiabloSealInfector) && say("Infector of Souls spawned!"); + Common.Diablo.infLayout === 1 ? Pather.moveTo(7893, 5306) : Pather.moveTo(7929, 5294); + infseal = false; + } - if (seisseal) { - Common.Diablo.openSeal(sdk.objects.DiabloSealSeis) && say("Lord De Seis spawned!"); - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); - seisseal = false; - } + if (seisseal) { + Common.Diablo.openSeal(sdk.objects.DiabloSealSeis) && say("Lord De Seis spawned!"); + Common.Diablo.seisLayout === 1 ? Pather.moveTo(7773, 5191) : Pather.moveTo(7794, 5189); + seisseal = false; + } - if (vizseal) { - Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2) && say("Grand Vizier of Chaos spawned!"); - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); - vizseal = false; - } + if (vizseal) { + Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2) && say("Grand Vizier of Chaos spawned!"); + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7681, 5302) : Pather.moveTo(7675, 5305); + vizseal = false; + } - if (diablopickup) { - Common.Diablo.openSeal(sdk.objects.DiabloSealVizier); - Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); - for (let i = 0; i < 300; i += 1) { - Pickit.pickItems(); - delay(100); + if (diablopickup) { + Common.Diablo.openSeal(sdk.objects.DiabloSealVizier); + Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); + for (let i = 0; i < 300; i += 1) { + Pickit.pickItems(); + delay(100); + } + diablopickup = false; } - diablopickup = false; - } - if (normalpickup) { - Pickit.pickItems(); - normalpickup = false; - } + if (normalpickup) { + Pickit.pickItems(); + normalpickup = false; + } - break; - default: - if (stargo) { - if (me.inArea(sdk.areas.RiverofFlame)) { - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); - Common.Diablo.initLayout(); - break; + break; + default: + if (stargo) { + if (me.inArea(sdk.areas.RiverofFlame)) { + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, 255); + Common.Diablo.initLayout(); + break; + } + stargo = false; } - stargo = false; - } - break; + break; + } + + delay(10); } - - delay(10); - } -} + }, + sdk.areas.RiverofFlame +); diff --git a/d2bs/kolbot/libs/scripts/ClearAnyArea.js b/d2bs/kolbot/libs/scripts/ClearAnyArea.js index b78638545..1245607ba 100644 --- a/d2bs/kolbot/libs/scripts/ClearAnyArea.js +++ b/d2bs/kolbot/libs/scripts/ClearAnyArea.js @@ -5,18 +5,26 @@ * */ -function ClearAnyArea () { - Town.doChores(); +const ClearAnyArea = new Runnable( + function ClearAnyArea () { + Town.doChores(); - for (let area of Config.ClearAnyArea.AreaList) { - try { - if (Pather.journeyTo(area)) { - Attack.clearLevel(Config.ClearType); + for (let area of Config.ClearAnyArea.AreaList) { + try { + if (Pather.journeyTo(area)) { + Attack.clearLevel(Config.ClearType); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); } + + return true; } +); - return true; -} +Object.defineProperty(ClearAnyArea, "startArea", { + get: function () { + return Config.ClearAnyArea.AreaList[0]; + } +}); diff --git a/d2bs/kolbot/libs/scripts/Coldcrow.js b/d2bs/kolbot/libs/scripts/Coldcrow.js index 0b4fa642f..d470a41e2 100644 --- a/d2bs/kolbot/libs/scripts/Coldcrow.js +++ b/d2bs/kolbot/libs/scripts/Coldcrow.js @@ -5,17 +5,20 @@ * */ -function Coldcrow () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); +const Coldcrow = new Runnable( + function Coldcrow () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.CaveLvl1, true, false)) throw new Error("Failed to move to Cave"); - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Coldcrow, 0, 0, false)) { - throw new Error("Failed to move to Coldcrow"); - } + if (!Pather.moveToExit(sdk.areas.CaveLvl1, true, false)) throw new Error("Failed to move to Cave"); + if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Coldcrow, 0, 0, false)) { + throw new Error("Failed to move to Coldcrow"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.Coldcrow)); + Attack.kill(getLocaleString(sdk.locale.monsters.Coldcrow)); - return true; -} + return true; + }, + sdk.areas.ColdPlains +); diff --git a/d2bs/kolbot/libs/scripts/Coldworm.js b/d2bs/kolbot/libs/scripts/Coldworm.js index d95ff5ea3..62092b3f6 100644 --- a/d2bs/kolbot/libs/scripts/Coldworm.js +++ b/d2bs/kolbot/libs/scripts/Coldworm.js @@ -5,35 +5,38 @@ * */ -function Coldworm () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FarOasis); - Precast.doPrecast(true); +const Coldworm = new Runnable( + function Coldworm () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.FarOasis); + Precast.doPrecast(true); - // Beetleburst, added by 13ack.Stab - if (Config.Coldworm.KillBeetleburst) { - try { - if (!Pather.moveToPresetMonster(me.area, sdk.monsters.preset.Beetleburst)) { - throw new Error("Failed to move to Beetleburst"); + // Beetleburst, added by 13ack.Stab + if (Config.Coldworm.KillBeetleburst) { + try { + if (!Pather.moveToPresetMonster(me.area, sdk.monsters.preset.Beetleburst)) { + throw new Error("Failed to move to Beetleburst"); + } + Attack.kill(getLocaleString(sdk.locale.monsters.Beetleburst)); + } catch (e) { + console.error(e); // not the main part of this script so simply log and move on } - Attack.kill(getLocaleString(sdk.locale.monsters.Beetleburst)); - } catch (e) { - console.error(e); // not the main part of this script so simply log and move on } - } - if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true)) { - throw new Error("Failed to move to Coldworm"); - } - - if (Config.Coldworm.ClearMaggotLair) { - Attack.clearLevel(Config.ClearType); - } else { - if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { + if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true)) { throw new Error("Failed to move to Coldworm"); } - Attack.kill(sdk.monsters.ColdwormtheBurrower); - } - return true; -} + if (Config.Coldworm.ClearMaggotLair) { + Attack.clearLevel(Config.ClearType); + } else { + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { + throw new Error("Failed to move to Coldworm"); + } + Attack.kill(sdk.monsters.ColdwormtheBurrower); + } + + return true; + }, + sdk.areas.FarOasis +); diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 35e07ac21..13083fd27 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -7,1293 +7,1296 @@ * */ -function ControlBot () { - // Quests - const { - log, - playerIn, - andariel, - bloodraven, - smith, - cube, - radament, - amulet, - staff, - summoner, - duriel, - lamesen, - brain, - heart, - eye, - travincal, - // mephisto, - izual, - diablo, - shenk, - anya, - ancients, - baal, - } = require("../systems/autorush/AutoRush"); - const { - AutoRush, - RushModes, - } = require("../systems/autorush/RushConfig"); - const Worker = require("../modules/Worker"); - - /** @param {string} [nick] */ - const mephisto = function (nick) { - log("starting mephisto"); - - Town.doChores(); - Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true)) { - throw new Error("Failed to move to durance 3"); - } - Pather.moveTo(17617, 8069); - Attack.securePosition(me.x, me.y, 30, 3000); - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 20, 3000); - let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - - if (hydra) { - do { - while (!hydra.dead && hydra.hp > 0) { - delay(500); - } - } while (hydra.getNext()); - } - Pather.makePortal(); - Pather.moveTo(17581, 8070); - log(AutoRush.playersIn); - - if (!Misc.poll(function () { - return playerIn(me.area, nick); - }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); - return false; - } +const ControlBot = new Runnable( + function ControlBot () { + // Quests + const { + log, + playerIn, + andariel, + bloodraven, + smith, + cube, + radament, + amulet, + staff, + summoner, + duriel, + lamesen, + brain, + heart, + eye, + travincal, + // mephisto, + izual, + diablo, + shenk, + anya, + ancients, + baal, + } = require("../systems/autorush/AutoRush"); + const { + AutoRush, + RushModes, + } = require("../systems/autorush/RushConfig"); + const Worker = require("../modules/Worker"); + + /** @param {string} [nick] */ + const mephisto = function (nick) { + log("starting mephisto"); - Pather.moveTo(17591, 8070); - Attack.kill(sdk.monsters.Mephisto); - Pickit.pickItems(); - log("meph dead"); - log(AutoRush.playersOut); - Pather.usePortal(null); + Town.doChores(); + Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); + if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true)) { + throw new Error("Failed to move to durance 3"); + } + Pather.moveTo(17617, 8069); + Attack.securePosition(me.x, me.y, 30, 3000); + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 20, 3000); + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - return true; - }; + if (hydra) { + do { + while (!hydra.dead && hydra.hp > 0) { + delay(500); + } + } while (hydra.getNext()); + } + Pather.makePortal(); + Pather.moveTo(17581, 8070); + log(AutoRush.playersIn); - AutoRush.rushMode = RushModes.chanter; - AutoRush.playersIn = "in"; - AutoRush.playersOut = "out"; - AutoRush.allIn = "all in"; + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } - const ngVote = new function () { - /** @type {Set} */ - this.votes = new Set(); - this.watch = false; - this.tick = 0; - this.nextGame = false; + Pather.moveTo(17591, 8070); + Attack.kill(sdk.monsters.Mephisto); + Pickit.pickItems(); + log("meph dead"); + log(AutoRush.playersOut); + Pather.usePortal(null); - this.votesNeeded = function () { - return Math.max(1, (Misc.getPlayerCount() - 2) / 2); + return true; }; - this.reset = function () { - this.votes.clear(); - this.tick = 0; + + AutoRush.rushMode = RushModes.chanter; + AutoRush.playersIn = "in"; + AutoRush.playersOut = "out"; + AutoRush.allIn = "all in"; + + const ngVote = new function () { + /** @type {Set} */ + this.votes = new Set(); this.watch = false; + this.tick = 0; + this.nextGame = false; + + this.votesNeeded = function () { + return Math.max(1, (Misc.getPlayerCount() - 2) / 2); + }; + this.reset = function () { + this.votes.clear(); + this.tick = 0; + this.watch = false; + }; + this.begin = function () { + this.watch = true; + this.votes.clear(); + this.tick = getTickCount(); + }; + this.checkCount = function () { + return this.votes.size >= this.votesNeeded; + }; + this.vote = function (nick) { + if (this.watch) { + this.votes.add(nick); + } + }; + this.elapsed = function () { + return getTickCount() - this.tick; + }; }; - this.begin = function () { - this.watch = true; - this.votes.clear(); - this.tick = getTickCount(); + const MAX_CHAT_LENGTH = 180; + const MIN_GOLD = 500000; + const startTime = getTickCount(); + const maxTime = Time.minutes(Config.ControlBot.GameLength); + const chantDuration = Skill.getDuration(sdk.skills.Enchant); + /** @type {Map} */ + const players = new Map(); + /** @type {Set} */ + const givenGold = new Set(); + + const Chat = { + /** @type {string[]} */ + queue: [], + + /** + * Send a message in chat + * @param {string} msg + */ + say: function (msg) { + Chat.queue.push(msg); + }, + + /** + * Display a message overhead + * @param {string} msg + * @param {boolean} [force] + */ + overhead: function (msg, force = false) { + if (!force && getTickCount() - Chat.overheadTick < 0) return; + // allow overhead messages every ~3-4 seconds + Chat.overheadTick = getTickCount() + Time.seconds(3) + rand(250, 1500); + say("!" + msg); + }, + + /** + * Whisper a chat to a user + * @param {string} nick + * @param {string} msg + */ + whisper: function (nick, msg) { + if (!players.has(nick) && !Misc.findPlayer(nick)) { + console.debug("Player not found: " + nick); + return; + } + let who = players.get(nick) || nick; + Chat.queue.push("/w " + who + " " + msg); + }, }; - this.checkCount = function () { - return this.votes.size >= this.votesNeeded; + + Worker.runInBackground.chat = (function () { + let tick = getTickCount(); + + return function () { + if (!Chat.queue.length) return true; + // should check if next msg is going to be a whisper and if so + // check if the player is in the game and if not, don't send the whisper + if (getTickCount() - tick < 0) return true; + // allow say messages every ~1.5 seconds + tick = getTickCount() + Time.seconds(1) + rand(250, 750); + console.debug("(" + Chat.queue[0] + ")"); + if (Chat.queue[0].length > MAX_CHAT_LENGTH) { + console.debug("Message too long, splitting."); + Chat.queue[0] = Chat.queue[0].substring(0, MAX_CHAT_LENGTH); + } + say(Chat.queue.shift()); + return true; + }; + })(); + + /** @constructor */ + function PlayerTracker () { + this.firstCmd = getTickCount(); + this.commands = 0; + this.ignored = false; + } + + PlayerTracker.prototype.resetCmds = function () { + this.firstCmd = getTickCount(); + this.commands = 0; }; - this.vote = function (nick) { - if (this.watch) { - this.votes.add(nick); - } + + PlayerTracker.prototype.unIgnore = function () { + this.ignored = false; + this.commands = 0; }; - this.elapsed = function () { - return getTickCount() - this.tick; + + /** @constructor */ + function ChantTracker () { + this.lastChant = getTickCount(); + } + + ChantTracker.prototype.reChant = function () { + return getTickCount() - this.lastChant >= chantDuration - Time.minutes(1); }; - }; - const MAX_CHAT_LENGTH = 180; - const MIN_GOLD = 500000; - const startTime = getTickCount(); - const maxTime = Time.minutes(Config.ControlBot.GameLength); - const chantDuration = Skill.getDuration(sdk.skills.Enchant); - /** @type {Map} */ - const players = new Map(); - /** @type {Set} */ - const givenGold = new Set(); - - const Chat = { - /** @type {string[]} */ - queue: [], - /** - * Send a message in chat - * @param {string} msg - */ - say: function (msg) { - Chat.queue.push(msg); - }, + ChantTracker.prototype.update = function () { + this.lastChant = getTickCount(); + }; - /** - * Display a message overhead - * @param {string} msg - * @param {boolean} [force] - */ - overhead: function (msg, force = false) { - if (!force && getTickCount() - Chat.overheadTick < 0) return; - // allow overhead messages every ~3-4 seconds - Chat.overheadTick = getTickCount() + Time.seconds(3) + rand(250, 1500); - say("!" + msg); - }, + /** @constructor */ + function WpTracker () { + this.timer = getTickCount(); + this.requests = 0; + } - /** - * Whisper a chat to a user - * @param {string} nick - * @param {string} msg - */ - whisper: function (nick, msg) { - if (!players.has(nick) && !Misc.findPlayer(nick)) { - console.debug("Player not found: " + nick); - return; - } - let who = players.get(nick) || nick; - Chat.queue.push("/w " + who + " " + msg); - }, - }; - - Worker.runInBackground.chat = (function () { - let tick = getTickCount(); - - return function () { - if (!Chat.queue.length) return true; - // should check if next msg is going to be a whisper and if so - // check if the player is in the game and if not, don't send the whisper - if (getTickCount() - tick < 0) return true; - // allow say messages every ~1.5 seconds - tick = getTickCount() + Time.seconds(1) + rand(250, 750); - console.debug("(" + Chat.queue[0] + ")"); - if (Chat.queue[0].length > MAX_CHAT_LENGTH) { - console.debug("Message too long, splitting."); - Chat.queue[0] = Chat.queue[0].substring(0, MAX_CHAT_LENGTH); - } - say(Chat.queue.shift()); - return true; + WpTracker.prototype.update = function () { + this.timer = getTickCount(); + this.requests++; }; - })(); - - /** @constructor */ - function PlayerTracker () { - this.firstCmd = getTickCount(); - this.commands = 0; - this.ignored = false; - } - - PlayerTracker.prototype.resetCmds = function () { - this.firstCmd = getTickCount(); - this.commands = 0; - }; - - PlayerTracker.prototype.unIgnore = function () { - this.ignored = false; - this.commands = 0; - }; - - /** @constructor */ - function ChantTracker () { - this.lastChant = getTickCount(); - } - - ChantTracker.prototype.reChant = function () { - return getTickCount() - this.lastChant >= chantDuration - Time.minutes(1); - }; - - ChantTracker.prototype.update = function () { - this.lastChant = getTickCount(); - }; - - /** @constructor */ - function WpTracker () { - this.timer = getTickCount(); - this.requests = 0; - } - - WpTracker.prototype.update = function () { - this.timer = getTickCount(); - this.requests++; - }; - - WpTracker.prototype.timeSinceLastRequest = function () { - return getTickCount() - this.timer; - }; - - /** @type {Map} */ - const cmdNicks = new Map(); - /** @type {Map} */ - const wpNicks = new Map(); - /** @type {Set} */ - const shitList = new Set(); - /** @type {Array} */ - const greet = []; - /** @type {Map} */ - const wps = new Map([ - [1, [ - sdk.areas.ColdPlains, sdk.areas.StonyField, - sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.OuterCloister, sdk.areas.JailLvl1, - sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2 - ] - ], - [2, [ - sdk.areas.A2SewersLvl2, sdk.areas.DryHills, - sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, - sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, - sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic - ] - ], - [3, [ - sdk.areas.SpiderForest, sdk.areas.GreatMarsh, - sdk.areas.FlayerJungle, sdk.areas.LowerKurast, - sdk.areas.KurastBazaar, sdk.areas.UpperKurast, - sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2 - ] - ], - [4, [ - sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame - ] - ], - [5, [ - sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, - sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, - sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 - ] - ] - ]); - - /** @type {[string, string][]} */ - const queue = []; - const running = { - nick: "", - command: "", - }; - - /** - * @param {string} nick - * @returns {boolean} - */ - const enchant = function (nick) { - try { - if (!Misc.inMyParty(nick)) { - throw new ScriptError("Accept party invite, noob."); - } - let unit = Game.getPlayer(nick); + WpTracker.prototype.timeSinceLastRequest = function () { + return getTickCount() - this.timer; + }; + + /** @type {Map} */ + const cmdNicks = new Map(); + /** @type {Map} */ + const wpNicks = new Map(); + /** @type {Set} */ + const shitList = new Set(); + /** @type {Array} */ + const greet = []; + /** @type {Map} */ + const wps = new Map([ + [1, [ + sdk.areas.ColdPlains, sdk.areas.StonyField, + sdk.areas.DarkWood, sdk.areas.BlackMarsh, + sdk.areas.OuterCloister, sdk.areas.JailLvl1, + sdk.areas.InnerCloister, sdk.areas.CatacombsLvl2 + ] + ], + [2, [ + sdk.areas.A2SewersLvl2, sdk.areas.DryHills, + sdk.areas.HallsoftheDeadLvl2, sdk.areas.FarOasis, + sdk.areas.LostCity, sdk.areas.PalaceCellarLvl1, + sdk.areas.ArcaneSanctuary, sdk.areas.CanyonofMagic + ] + ], + [3, [ + sdk.areas.SpiderForest, sdk.areas.GreatMarsh, + sdk.areas.FlayerJungle, sdk.areas.LowerKurast, + sdk.areas.KurastBazaar, sdk.areas.UpperKurast, + sdk.areas.Travincal, sdk.areas.DuranceofHateLvl2 + ] + ], + [4, [ + sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame + ] + ], + [5, [ + sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, + sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, + sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.WorldstoneLvl2 + ] + ] + ]); + + /** @type {[string, string][]} */ + const queue = []; + const running = { + nick: "", + command: "", + }; + + /** + * @param {string} nick + * @returns {boolean} + */ + const enchant = function (nick) { + try { + if (!Misc.inMyParty(nick)) { + throw new ScriptError("Accept party invite, noob."); + } + + let unit = Game.getPlayer(nick); - if (unit && unit.distance > 35) { - throw new ScriptError("Get closer."); + if (unit && unit.distance > 35) { + throw new ScriptError("Get closer."); + } + + if (!unit) { + let partyUnit = getParty(nick); + + if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { + throw new ScriptError("You need to be in one of the towns."); + } + // wait until party area is readable? + Chat.say("Wait for me at waypoint."); + Town.goToTown(sdk.areas.actOf(partyUnit.area)); + + unit = Game.getPlayer(nick); + } + + if (unit) { + do { + // player is alive + if (!unit.dead) { + if (unit.distance >= 35) { + throw new ScriptError("You went too far away."); + } + Packet.enchant(unit); + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chantList.has(unit.name) + ? chantList.get(unit.name).update() + : chantList.set(unit.name, new ChantTracker()); + } + } + } while (unit.getNext()); + } else { + Chat.say("I don't see you"); + } + + unit = Game.getMonster(); + + if (unit) { + do { + // merc or any other owned unit + if (unit.getParent() && unit.getParent().name === nick) { + Packet.enchant(unit); + delay(500); + } + } while (unit.getNext()); + } + + return true; + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } + + return false; } + }; + + /** + * @param {string} nick + * @returns {boolean} + */ + const bo = function (nick) { + if (!Config.ControlBot.Bo) return false; + + try { + if (!Misc.inMyParty(nick)) { + throw new ScriptError("Accept party invite, noob."); + } - if (!unit) { let partyUnit = getParty(nick); - if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { - throw new ScriptError("You need to be in one of the towns."); - } // wait until party area is readable? - Chat.say("Wait for me at waypoint."); - Town.goToTown(sdk.areas.actOf(partyUnit.area)); + if (!Misc.poll(() => Pather.wpAreas.includes(partyUnit.area), 500, 50)) { + throw new ScriptError("Can't find you or you're not somewhere with a waypoint"); + } + if (partyUnit.inTown) { + let a1Wp = Object.values(sdk.areas) + .filter(function (area) { + if (area < sdk.areas.ColdPlains || area > sdk.areas.CatacombsLvl2) return false; + return Pather.wpAreas.includes(area) && me.haveWaypoint(area); + }).random(); + Chat.whisper(nick, "Go to act 1 waypoint " + getAreaName(a1Wp) + " and wait for me."); + Pather.useWaypoint(a1Wp); + } else { + Pather.useWaypoint(partyUnit.area); + } + + let unit = Misc.poll(function () { + return Game.getPlayer(nick); + }, Time.minutes(1), 1000); + + if (unit && unit.distance > 15) { + Chat.say("Get closer."); + + if (!Misc.poll(() => unit.distance <= 15, Time.seconds(30), 50)) { + throw new ScriptError("You took to long. Going back to town"); + } + } - unit = Game.getPlayer(nick); + if (unit && unit.distance <= 15 && !unit.dead) { + Misc.poll(function () { + Precast.doPrecast(true); + return unit.getState(sdk.states.BattleOrders); + }, 5000, 1000); + Pather.useWaypoint(sdk.areas.RogueEncampment); + } else { + throw new ScriptError("I don't see you"); + } + + return true; + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } + + return false; } + }; + + const autoChant = function () { + if (!Config.ControlBot.Chant.Enchant) return false; + + let chanted = []; + let unit = Game.getPlayer(); if (unit) { do { - // player is alive - if (!unit.dead) { - if (unit.distance >= 35) { - throw new ScriptError("You went too far away."); - } + if (unit === me.name || unit.dead) continue; + if (shitList.has(unit.name)) continue; + if (!Misc.inMyParty(unit.name) || unit.distance > 40) continue; + // allow rechanting someone if it's going to run out soon for them + if (!unit.getState(sdk.states.Enchant) + || (chantList.has(unit.name) && chantList.get(unit.name).reChant())) { Packet.enchant(unit); if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); chantList.has(unit.name) ? chantList.get(unit.name).update() : chantList.set(unit.name, new ChantTracker()); } } } while (unit.getNext()); - } else { - Chat.say("I don't see you"); } unit = Game.getMonster(); if (unit) { do { - // merc or any other owned unit - if (unit.getParent() && unit.getParent().name === nick) { + if (unit.getParent() + && chanted.includes(unit.getParent().name) + && !unit.getState(sdk.states.Enchant) + && unit.distance <= 40) { Packet.enchant(unit); - delay(500); + // not going to re-enchant the minions for now though, will think on how best to handle that later + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + } } } while (unit.getNext()); } return true; - } catch (e) { - if (e instanceof ScriptError) { - Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); - } else { - console.error(e); - Chat.say("Internal Error"); - } - - return false; - } - }; - - /** - * @param {string} nick - * @returns {boolean} - */ - const bo = function (nick) { - if (!Config.ControlBot.Bo) return false; + }; - try { - if (!Misc.inMyParty(nick)) { - throw new ScriptError("Accept party invite, noob."); + const getLeg = function () { + if (me.getItem(sdk.quest.item.WirtsLeg)) { + return me.getItem(sdk.quest.item.WirtsLeg); } - let partyUnit = getParty(nick); + let leg, gid, wrongLeg; - // wait until party area is readable? - if (!Misc.poll(() => Pather.wpAreas.includes(partyUnit.area), 500, 50)) { - throw new ScriptError("Can't find you or you're not somewhere with a waypoint"); - } - if (partyUnit.inTown) { - let a1Wp = Object.values(sdk.areas) - .filter(function (area) { - if (area < sdk.areas.ColdPlains || area > sdk.areas.CatacombsLvl2) return false; - return Pather.wpAreas.includes(area) && me.haveWaypoint(area); - }).random(); - Chat.whisper(nick, "Go to act 1 waypoint " + getAreaName(a1Wp) + " and wait for me."); - Pather.useWaypoint(a1Wp); - } else { - Pather.useWaypoint(partyUnit.area); - } + if (!Config.ControlBot.Cows.GetLeg) { + leg = Game.getItem(sdk.items.quest.WirtsLeg); - let unit = Misc.poll(function () { - return Game.getPlayer(nick); - }, Time.minutes(1), 1000); + if (leg) { + do { + if (leg.name.includes("ÿc1")) { + wrongLeg = true; + } else if (leg.distance <= 15) { + gid = leg.gid; + Pickit.pickItem(leg); - if (unit && unit.distance > 15) { - Chat.say("Get closer."); - - if (!Misc.poll(() => unit.distance <= 15, Time.seconds(30), 50)) { - throw new ScriptError("You took to long. Going back to town"); + return me.getItem(-1, -1, gid); + } + } while (leg.getNext()); } - } - if (unit && unit.distance <= 15 && !unit.dead) { - Misc.poll(function () { - Precast.doPrecast(true); - return unit.getState(sdk.states.BattleOrders); - }, 5000, 1000); - Pather.useWaypoint(sdk.areas.RogueEncampment); - } else { - throw new ScriptError("I don't see you"); - } + Chat.say("Bring the leg " + (wrongLeg ? "from this difficulty" : "") + " close to me."); - return true; - } catch (e) { - if (e instanceof ScriptError) { - Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); - } else { - console.error(e); - Chat.say("Internal Error"); + return false; } - - return false; - } - }; - - const autoChant = function () { - if (!Config.ControlBot.Chant.Enchant) return false; - - let chanted = []; - let unit = Game.getPlayer(); - - if (unit) { - do { - if (unit === me.name || unit.dead) continue; - if (shitList.has(unit.name)) continue; - if (!Misc.inMyParty(unit.name) || unit.distance > 40) continue; - // allow rechanting someone if it's going to run out soon for them - if (!unit.getState(sdk.states.Enchant) - || (chantList.has(unit.name) && chantList.get(unit.name).reChant())) { - Packet.enchant(unit); - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); - chantList.has(unit.name) - ? chantList.get(unit.name).update() - : chantList.set(unit.name, new ChantTracker()); - } - } - } while (unit.getNext()); - } - unit = Game.getMonster(); - - if (unit) { - do { - if (unit.getParent() - && chanted.includes(unit.getParent().name) - && !unit.getState(sdk.states.Enchant) - && unit.distance <= 40) { - Packet.enchant(unit); - // not going to re-enchant the minions for now though, will think on how best to handle that later - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); - } - } - } while (unit.getNext()); - } + if (!Pather.journeyTo(sdk.areas.Tristram)) { + Chat.say("Failed to enter Tristram :("); + Town.goToTown(); - return true; - }; + return false; + } - const getLeg = function () { - if (me.getItem(sdk.quest.item.WirtsLeg)) { - return me.getItem(sdk.quest.item.WirtsLeg); - } + Pather.moveTo(25048, 5177); - let leg, gid, wrongLeg; + let wirt = Game.getObject(sdk.quest.chest.Wirt); - if (!Config.ControlBot.Cows.GetLeg) { - leg = Game.getItem(sdk.items.quest.WirtsLeg); + for (let i = 0; i < 8; i += 1) { + wirt.interact(); + delay(500); - if (leg) { - do { - if (leg.name.includes("ÿc1")) { - wrongLeg = true; - } else if (leg.distance <= 15) { - gid = leg.gid; - Pickit.pickItem(leg); + leg = Game.getItem(sdk.quest.item.WirtsLeg); - return me.getItem(-1, -1, gid); - } - } while (leg.getNext()); - } + if (leg) { + gid = leg.gid; - Chat.say("Bring the leg " + (wrongLeg ? "from this difficulty" : "") + " close to me."); + Pickit.pickItem(leg); + Town.goToTown(); - return false; - } + return me.getItem(-1, -1, gid); + } + } - if (!Pather.journeyTo(sdk.areas.Tristram)) { - Chat.say("Failed to enter Tristram :("); Town.goToTown(); + Chat.say("Failed to get the leg :("); return false; - } - - Pather.moveTo(25048, 5177); - - let wirt = Game.getObject(sdk.quest.chest.Wirt); - - for (let i = 0; i < 8; i += 1) { - wirt.interact(); - delay(500); + }; - leg = Game.getItem(sdk.quest.item.WirtsLeg); + const getTome = function () { + let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); - if (leg) { - gid = leg.gid; + if (tpTome.length < 2) { + if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 2 })) { + if (tpTome.length === 1) { + return tpTome.first(); + } + } + let npc = Town.initNPC("Shop", "buyTpTome"); + if (!getInteractedNPC()) throw new Error("Failed to find npc"); - Pickit.pickItem(leg); - Town.goToTown(); + let tome = npc.getItem(sdk.items.TomeofTownPortal); - return me.getItem(-1, -1, gid); + 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); + tpTome.forEach(function (book) { + if (book.isInInventory) { + let scroll = npc.getItem(sdk.items.ScrollofTownPortal); + + while (book.getStat(sdk.stats.Quantity) < 20) { + if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { + scroll.buy(true); + } else { + break; + } + + delay(20); + } + } + }); + } else { + throw new Error("Failed to buy tome"); + } } - } - - Town.goToTown(); - Chat.say("Failed to get the leg :("); - - return false; - }; - const getTome = function () { - let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + return tpTome.last(); + }; - if (tpTome.length < 2) { - if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 2 })) { - if (tpTome.length === 1) { - return tpTome.first(); + /** + * @param {string} nick + * @returns {boolean} + */ + const openPortal = function (nick) { + if (!Config.ControlBot.Cows.MakeCows) return false; + try { + if (!Misc.inMyParty(nick)) throw new ScriptError("Accept party invite, noob."); + if (Pather.getPortal(sdk.areas.MooMooFarm)) throw new ScriptError("Cow portal already open."); + // king dead or cain not saved + if (me.cows) throw new ScriptError("Can't open the portal because I killed Cow King."); + if (Config.ControlBot.Cows.GetLeg && !me.tristram && !!Config.Leader && !getParty(Config.Leader)) { + throw new ScriptError("Can't get leg because I don't have Cain quest."); } + if (!me.diffCompleted) throw new ScriptError("Final quest incomplete."); + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } + return false; } - let npc = Town.initNPC("Shop", "buyTpTome"); - if (!getInteractedNPC()) throw new Error("Failed to find npc"); - let tome = npc.getItem(sdk.items.TomeofTownPortal); + let leg = getLeg(); + if (!leg) return false; + if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 2 })) { + // we don't have any space, put the leg in the stash to make room in invo + Storage.Stash.MoveTo(leg); + me.cancelUIFlags(); + } - 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); - tpTome.forEach(function (book) { - if (book.isInInventory) { - let scroll = npc.getItem(sdk.items.ScrollofTownPortal); - - while (book.getStat(sdk.stats.Quantity) < 20) { - if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { - scroll.buy(true); - } else { - break; - } + let tome = getTome(); + if (!tome) return false; - delay(20); - } - } - }); - } else { - throw new Error("Failed to buy tome"); + if (!Town.openStash() + || !Cubing.emptyCube() + || !Storage.Cube.MoveTo(leg) + || !Storage.Cube.MoveTo(tome) + || !Cubing.openCube()) { + return false; } - } - return tpTome.last(); - }; + transmute(); + delay(500); - /** - * @param {string} nick - * @returns {boolean} - */ - const openPortal = function (nick) { - if (!Config.ControlBot.Cows.MakeCows) return false; - try { - if (!Misc.inMyParty(nick)) throw new ScriptError("Accept party invite, noob."); - if (Pather.getPortal(sdk.areas.MooMooFarm)) throw new ScriptError("Cow portal already open."); - // king dead or cain not saved - if (me.cows) throw new ScriptError("Can't open the portal because I killed Cow King."); - if (Config.ControlBot.Cows.GetLeg && !me.tristram && !!Config.Leader && !getParty(Config.Leader)) { - throw new ScriptError("Can't get leg because I don't have Cain quest."); - } - if (!me.diffCompleted) throw new ScriptError("Final quest incomplete."); - } catch (e) { - if (e instanceof ScriptError) { - Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); - } else { - console.error(e); - Chat.say("Internal Error"); - } - return false; - } + for (let i = 0; i < 10; i += 1) { + if (Pather.getPortal(sdk.areas.MooMooFarm)) { + return true; + } - let leg = getLeg(); - if (!leg) return false; - if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 2 })) { - // we don't have any space, put the leg in the stash to make room in invo - Storage.Stash.MoveTo(leg); - me.cancelUIFlags(); - } + delay(200); + } - let tome = getTome(); - if (!tome) return false; + Chat.say("Failed to open cow portal."); - if (!Town.openStash() - || !Cubing.emptyCube() - || !Storage.Cube.MoveTo(leg) - || !Storage.Cube.MoveTo(tome) - || !Cubing.openCube()) { return false; - } - - transmute(); - delay(500); + }; - for (let i = 0; i < 10; i += 1) { - if (Pather.getPortal(sdk.areas.MooMooFarm)) { - return true; - } + /** + * @param {string} nick + * @returns {boolean} + */ + const giveWps = function (nick) { + let next = false; + const nextWatcher = function (who, msg) { + if (who !== nick) return; + if (msg === "next") { + next = true; + } + }; - delay(200); - } + try { + if (!Misc.inMyParty(nick)) { + throw new ScriptError("Accept party invite, noob."); + } - Chat.say("Failed to open cow portal."); - - return false; - }; - - /** - * @param {string} nick - * @returns {boolean} - */ - const giveWps = function (nick) { - let next = false; - const nextWatcher = function (who, msg) { - if (who !== nick) return; - if (msg === "next") { - next = true; - } - }; + if (!wpNicks.has(nick)) { + wpNicks.set(nick, new WpTracker()); + } - try { - if (!Misc.inMyParty(nick)) { - throw new ScriptError("Accept party invite, noob."); - } + let check = wpNicks.get(nick); + if (check.requests > 4) { + throw new ScriptError("You have spent all your waypoint requests for this game."); + } else if (check.requests > 1 && check.timeSinceLastRequest() < 60000) { + throw new ScriptError( + "You may request wp again in " + + Math.max(0, (60 - Math.floor(check.timeSinceLastRequest() / 1000))) + + " seconds." + ); + } - if (!wpNicks.has(nick)) { - wpNicks.set(nick, new WpTracker()); - } + let act = Misc.getPlayerAct(nick); + if (!wps.has(act)) return false; + addEventListener("chatmsg", nextWatcher); - let check = wpNicks.get(nick); - if (check.requests > 4) { - throw new ScriptError("You have spent all your waypoint requests for this game."); - } else if (check.requests > 1 && check.timeSinceLastRequest() < 60000) { - throw new ScriptError( - "You may request wp again in " - + Math.max(0, (60 - Math.floor(check.timeSinceLastRequest() / 1000))) - + " seconds." - ); - } + for (let wp of wps.get(act)) { + if (checkHostiles()) { + break; + } - let act = Misc.getPlayerAct(nick); - if (!wps.has(act)) return false; - addEventListener("chatmsg", nextWatcher); + try { + if (next) { + next = false; + continue; + } + Pather.useWaypoint(wp, true); + if (Config.ControlBot.Wps.SecurePortal) { + Attack.securePosition(me.x, me.y, 20, 1000); + } + Pather.makePortal(); + Chat.say(getAreaName(me.area) + " TP up"); - for (let wp of wps.get(act)) { - if (checkHostiles()) { - break; - } + if (!Misc.poll(() => (Game.getPlayer(nick) || next), Time.seconds(30), Time.seconds(1))) { + Chat.say("Aborting wp giving."); - try { - if (next) { + break; + } next = false; + + delay(5000); + } catch (error) { continue; } - Pather.useWaypoint(wp, true); - if (Config.ControlBot.Wps.SecurePortal) { - Attack.securePosition(me.x, me.y, 20, 1000); - } - Pather.makePortal(); - Chat.say(getAreaName(me.area) + " TP up"); + } - if (!Misc.poll(() => (Game.getPlayer(nick) || next), Time.seconds(30), Time.seconds(1))) { - Chat.say("Aborting wp giving."); + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); - break; - } - next = false; + check.update(); - delay(5000); - } catch (error) { - continue; + return true; + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); } + + return false; + } finally { + removeEventListener("chatmsg", nextWatcher); } + }; - Town.doChores(); - Town.goToTown(1); - Town.move("portalspot"); + const checkHostiles = function () { + let rval = false; + let party = getParty(); - check.update(); + if (party) { + do { + if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8)) { + rval = true; - return true; - } catch (e) { - if (e instanceof ScriptError) { - Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); - } else { - console.error(e); - Chat.say("Internal Error"); + if (Config.ShitList && !shitList.has(party.name)) { + shitList.add(party.name); + } + } + } while (party.getNext()); } - - return false; - } finally { - removeEventListener("chatmsg", nextWatcher); - } - }; - const checkHostiles = function () { - let rval = false; - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8)) { - rval = true; - - if (Config.ShitList && !shitList.has(party.name)) { - shitList.add(party.name); - } - } - } while (party.getNext()); - } + return rval; + }; - return rval; - }; + /** + * @param {string} command + * @returns {boolean} + */ + const floodCheck = function (command) { + if (!command || command.length < 2) return false; + let [cmd, nick] = command; + + // ignore overhead messages + if (!nick) return true; + // ignore messages not related to our commands + if (!actions.has(cmd.toLowerCase())) return false; - /** - * @param {string} command - * @returns {boolean} - */ - const floodCheck = function (command) { - if (!command || command.length < 2) return false; - let [cmd, nick] = command; - - // ignore overhead messages - if (!nick) return true; - // ignore messages not related to our commands - if (!actions.has(cmd.toLowerCase())) return false; + if (!cmdNicks.has(nick)) { + cmdNicks.set(nick, new PlayerTracker()); + } + const player = cmdNicks.get(nick); - if (!cmdNicks.has(nick)) { - cmdNicks.set(nick, new PlayerTracker()); - } - const player = cmdNicks.get(nick); + if (player.ignored) { + if (getTickCount() - player.ignored < Time.minutes(1)) { + return true; // ignore flooder + } - if (player.ignored) { - if (getTickCount() - player.ignored < Time.minutes(1)) { - return true; // ignore flooder + // unignore flooder + player.unIgnore(); } - // unignore flooder - player.unIgnore(); - } + player.commands += 1; - player.commands += 1; - - if (getTickCount() - player.firstCmd < Time.seconds(10)) { - if (player.commands > 5) { - player.ignored = getTickCount(); - Chat.whisper(nick, "You are being ignored for 60 seconds because of flooding."); + if (getTickCount() - player.firstCmd < Time.seconds(10)) { + if (player.commands > 5) { + player.ignored = getTickCount(); + Chat.whisper(nick, "You are being ignored for 60 seconds because of flooding."); + } + } else { + player.resetCmds(); } - } else { - player.resetCmds(); - } - return false; - }; + return false; + }; - const pickGoldPiles = function () { - /** @type {PathNode} */ - const startPos = { x: me.x, y: me.y }; - let gold = Game.getItem(sdk.items.Gold); + const pickGoldPiles = function () { + /** @type {PathNode} */ + const startPos = { x: me.x, y: me.y }; + let gold = Game.getItem(sdk.items.Gold); - if (gold) { - do { - if (gold.onGroundOrDropping && gold.distance <= 20 && Pickit.canPick(gold)) { - Pickit.pickItem(gold) && Chat.overhead("Thank you!", true); - if (startPos.distance > 5) { - Pather.move(startPos); + if (gold) { + do { + if (gold.onGroundOrDropping && gold.distance <= 20 && Pickit.canPick(gold)) { + Pickit.pickItem(gold) && Chat.overhead("Thank you!", true); + if (startPos.distance > 5) { + Pather.move(startPos); + } } - } - } while (gold.getNext()); - } - }; - - const dropGold = function (nick) { - try { - if (me.gold < MIN_GOLD) { - throw new ScriptError("Not enough gold to drop."); - } - if (givenGold.has(nick)) { - throw new ScriptError("Already dropped gold this game for you. Don't be greedy."); + } while (gold.getNext()); } + }; - let unit = Game.getPlayer(nick); - - if (unit && unit.distance > 15) { - throw new ScriptError("Get closer."); - } + const dropGold = function (nick) { + try { + if (me.gold < MIN_GOLD) { + throw new ScriptError("Not enough gold to drop."); + } + if (givenGold.has(nick)) { + throw new ScriptError("Already dropped gold this game for you. Don't be greedy."); + } - if (!unit) { - let partyUnit = getParty(nick); + let unit = Game.getPlayer(nick); - if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { - throw new ScriptError("You need to be in one of the towns."); + if (unit && unit.distance > 15) { + throw new ScriptError("Get closer."); } - // wait until party area is readable? - Chat.say("Wait for me at waypoint."); - Town.goToTown(sdk.areas.actOf(partyUnit.area)); - unit = Game.getPlayer(nick); - } + if (!unit) { + let partyUnit = getParty(nick); - if (unit) { - if (me.getStat(sdk.stats.Gold) < 5000) { - Town.openStash() && gold(5000, 4); - me.cancelUIFlags(); - } - - // drop the gold - gold(5000); - /** @type {ItemUnit} */ - let droppedGold = Misc.poll(function () { - let _gold = Game.getItem(sdk.items.Gold); - if (_gold && _gold.onGroundOrDropping && _gold.getStat(sdk.stats.Gold) === 5000) { - return _gold; + if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { + throw new ScriptError("You need to be in one of the towns."); } - return false; - }, Time.seconds(30), 1000); + // wait until party area is readable? + Chat.say("Wait for me at waypoint."); + Town.goToTown(sdk.areas.actOf(partyUnit.area)); - if (!droppedGold) { - throw new ScriptError("Failed to drop gold."); + unit = Game.getPlayer(nick); } - // watch for the gold dissapearing - let picked = false; - Misc.poll(function () { - let _gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround, droppedGold.gid); - if (_gold) return false; - picked = true; - return !_gold; - }, Time.seconds(30), 1000); + if (unit) { + if (me.getStat(sdk.stats.Gold) < 5000) { + Town.openStash() && gold(5000, 4); + me.cancelUIFlags(); + } + + // drop the gold + gold(5000); + /** @type {ItemUnit} */ + let droppedGold = Misc.poll(function () { + let _gold = Game.getItem(sdk.items.Gold); + if (_gold && _gold.onGroundOrDropping && _gold.getStat(sdk.stats.Gold) === 5000) { + return _gold; + } + return false; + }, Time.seconds(30), 1000); - if (!picked) { - Pickit.pickItem(droppedGold); - throw new ScriptError("Failed to pick gold."); + if (!droppedGold) { + throw new ScriptError("Failed to drop gold."); + } + + // watch for the gold dissapearing + let picked = false; + Misc.poll(function () { + let _gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround, droppedGold.gid); + if (_gold) return false; + picked = true; + return !_gold; + }, Time.seconds(30), 1000); + + if (!picked) { + Pickit.pickItem(droppedGold); + throw new ScriptError("Failed to pick gold."); + } else { + givenGold.add(nick); + Chat.say("yw " + nick); + } } else { - givenGold.add(nick); - Chat.say("yw " + nick); + throw new ScriptError("I don't see you"); } - } else { - throw new ScriptError("I don't see you"); - } - } catch (e) { - if (e instanceof ScriptError) { - Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); - } else { - console.error(e); - Chat.say("Internal Error"); - } - } - }; - - /** - * @param {string} nick - * @param {string} msg - * @returns {boolean} - */ - function chatEvent (nick, msg) { - if (!nick || !msg) return; - if (nick === me.name) return; - msg = msg.toLowerCase(); - if (msg.match(/^rush /gi)) { - msg = msg.split(" ")[1]; - } - if (commandAliases.has(msg)) { - msg = commandAliases.get(msg); - } - if (!actions.has(msg)) return; - if (shitList.has(nick)) { - Chat.say("No commands for the shitlisted."); - } else { - if (running.nick === nick && running.command === msg) { - console.debug("Command already running."); - return; - } - if (!floodCheck([msg, nick]) && (msg === "help" || msg === "timeleft" || msg === "ngyes")) { - actions.get(msg).run(nick); - return; - } - let index = queue.findIndex(function (cmd) { - return cmd[0] === msg && cmd[1] === nick; - }); - if (index > -1) { - Chat.whisper(nick, "You already requested this command. Queue position: " + (index + 1)); - } else { - queue.push([msg, nick]); - console.log(queue); - if (queue.length > 1 || running.nick !== "") { - Chat.whisper(nick, msg + " has been added to the queue. Queue position: " + (queue.length + 1)); + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); } } - } - } - - // eslint-disable-next-line no-unused-vars - function gameEvent (mode, param1, param2, name1, name2) { - switch (mode) { - case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." - // idle in town - me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); - if (name2) { - players.set(name1, "*" + name2); - } else { - players.set(name1, ""); - } - - break; - 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." - players.delete(name1); - if (ngVote.watch) { - ngVote.votes.delete(name1); - } + }; - break; - } - } - - /** - * @typedef {Object} Action - * @property {string} desc - * @property {boolean} hostileCheck - * @property {boolean} [complete] - * @property {function(): void} [markAsComplete] - * @property {function(): boolean | void} run - */ - /** @type {Map boolean} run - */ - function RushAction (desc, run) { - this.desc = desc; - this.hostileCheck = true; - this.complete = false; - this.run = run; - } - RushAction.prototype.markAsComplete = function () { - this.complete = true; - }; - /** @type {Map { - if (!value.desc.length) return; - if (value.complete) return; - if (value.desc.includes("Rush")) return; - let desc = (key + " (" + value.desc + "), "); - if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { - msg.push(str); - str = ""; - } - str += desc; + * @param {string} nick + * @param {string} msg + * @returns {boolean} + */ + function chatEvent (nick, msg) { + if (!nick || !msg) return; + if (nick === me.name) return; + msg = msg.toLowerCase(); + if (msg.match(/^rush /gi)) { + msg = msg.split(" ")[1]; + } + if (commandAliases.has(msg)) { + msg = commandAliases.get(msg); + } + if (!actions.has(msg)) return; + if (shitList.has(nick)) { + Chat.say("No commands for the shitlisted."); + } else { + if (running.nick === nick && running.command === msg) { + console.debug("Command already running."); + return; + } + if (!floodCheck([msg, nick]) && (msg === "help" || msg === "timeleft" || msg === "ngyes")) { + actions.get(msg).run(nick); + return; + } + let index = queue.findIndex(function (cmd) { + return cmd[0] === msg && cmd[1] === nick; }); - str.length && msg.push(str); - str = "Rush commands (example: rush andy): "; - _actions.forEach((value, key) => { - if (!value.desc.length) return; - if (value.complete) return; - if (!value.desc.includes("Rush")) return; - let desc = (key + ", "); - if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { - msg.push(str); - str = ""; + if (index > -1) { + Chat.whisper(nick, "You already requested this command. Queue position: " + (index + 1)); + } else { + queue.push([msg, nick]); + console.log(queue); + if (queue.length > 1 || running.nick !== "") { + Chat.whisper(nick, msg + " has been added to the queue. Queue position: " + (queue.length + 1)); } - str += desc; - }); - str.length && msg.push(str); - msg.forEach(function (m) { - Chat.whisper(nick, m); - }); - } - }); - _actions.set("timeleft", { - desc: "Remaining time for this game", - hostileCheck: false, - run: function () { - let tick = Time.minutes(Config.ControlBot.GameLength) - getTickCount() + startTime; - let m = Math.floor(tick / 60000); - let s = Math.floor((tick / 1000) % 60); - - Chat.say( - "Time left: " - + (m ? m + " minute" + (m > 1 ? "s" : "") - + ", " : "") + s + " second" + (s > 1 ? "s." : ".") - ); - } - }); - _actions.set("ngvote", { - desc: "Vote for next game", - hostileCheck: false, - run: function (nick) { - if (getTickCount() - startTime < Time.minutes(3)) { - Chat.say( - "Can't vote for next game yet. Must be in game for at least 3 minutes. Remaining: " - + Math.round((Time.minutes(3) - (getTickCount() - startTime)) / 1000) + " seconds." - ); - return; } - ngVote.begin(); - Chat.say(nick + " voted for next game. Votes Needed: " + ngVote.votesNeeded + ". Type ngyes to vote."); } - }); - _actions.set("ngyes", { - desc: "", - hostileCheck: false, - run: function (nick) { - if (!ngVote.watch) return; - ngVote.vote(nick); - if (ngVote.checkCount()) { - Chat.say("Enough votes to start next game."); - ngVote.nextGame = true; + } + + // eslint-disable-next-line no-unused-vars + function gameEvent (mode, param1, param2, name1, name2) { + switch (mode) { + case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." + // idle in town + me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); + if (name2) { + players.set(name1, "*" + name2); + } else { + players.set(name1, ""); } - } - }); - if (Config.ControlBot.DropGold) { - _actions.set("dropgold", { - desc: "Drop 5k gold", - hostileCheck: false, - run: dropGold - }); + break; + 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." + players.delete(name1); + if (ngVote.watch) { + ngVote.votes.delete(name1); + } + + break; + } } - if (Config.ControlBot.Chant.Enchant - && Skill.canUse(sdk.skills.Enchant)) { - _actions.set("chant", { - desc: "Give enchant", + /** + * @typedef {Object} Action + * @property {string} desc + * @property {boolean} hostileCheck + * @property {boolean} [complete] + * @property {function(): void} [markAsComplete] + * @property {function(): boolean | void} run + */ + /** @type {Map boolean} run + */ + function RushAction (desc, run) { + this.desc = desc; + this.hostileCheck = true; + this.complete = false; + this.run = run; + } + RushAction.prototype.markAsComplete = function () { + this.complete = true; + }; + /** @type {Map { + if (!value.desc.length) return; + if (value.complete) return; + if (value.desc.includes("Rush")) return; + let desc = (key + " (" + value.desc + "), "); + if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { + msg.push(str); + str = ""; + } + str += desc; + }); + str.length && msg.push(str); + str = "Rush commands (example: rush andy): "; + _actions.forEach((value, key) => { + if (!value.desc.length) return; + if (value.complete) return; + if (!value.desc.includes("Rush")) return; + let desc = (key + ", "); + if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { + msg.push(str); + str = ""; + } + str += desc; + }); + str.length && msg.push(str); + msg.forEach(function (m) { + Chat.whisper(nick, m); + }); + } }); - } + _actions.set("timeleft", { + desc: "Remaining time for this game", + hostileCheck: false, + run: function () { + let tick = Time.minutes(Config.ControlBot.GameLength) - getTickCount() + startTime; + let m = Math.floor(tick / 60000); + let s = Math.floor((tick / 1000) % 60); - if (Config.ControlBot.Wps.GiveWps) { - _actions.set("wps", { - desc: "Give wps in act", - hostileCheck: true, - run: giveWps + Chat.say( + "Time left: " + + (m ? m + " minute" + (m > 1 ? "s" : "") + + ", " : "") + s + " second" + (s > 1 ? "s." : ".") + ); + } }); - } - - if (Config.ControlBot.Bo - && (Skill.canUse(sdk.skills.BattleOrders) || Precast.haveCTA > 0)) { - _actions.set("bo", { - desc: "Bo at wp", - hostileCheck: true, - run: bo + _actions.set("ngvote", { + desc: "Vote for next game", + hostileCheck: false, + run: function (nick) { + if (getTickCount() - startTime < Time.minutes(3)) { + Chat.say( + "Can't vote for next game yet. Must be in game for at least 3 minutes. Remaining: " + + Math.round((Time.minutes(3) - (getTickCount() - startTime)) / 1000) + " seconds." + ); + return; + } + ngVote.begin(); + Chat.say(nick + " voted for next game. Votes Needed: " + ngVote.votesNeeded + ". Type ngyes to vote."); + } + }); + _actions.set("ngyes", { + desc: "", + hostileCheck: false, + run: function (nick) { + if (!ngVote.watch) return; + ngVote.vote(nick); + if (ngVote.checkCount()) { + Chat.say("Enough votes to start next game."); + ngVote.nextGame = true; + } + } }); - } - if (Config.ControlBot.Rush) { - if (Config.ControlBot.Rush.Andy) { - _actions.set("andy", new RushAction("Rush Andariel", andariel)); - } - if (Config.ControlBot.Rush.Bloodraven) { - _actions.set("raven", new RushAction("Rush Bloodraven", bloodraven)); - } - if (Config.ControlBot.Rush.Smith) { - _actions.set("smith", new RushAction("Rush Smith", smith)); - } - if (Config.ControlBot.Rush.Cube) { - _actions.set("cube", new RushAction("Rush Cube", cube)); - } - if (Config.ControlBot.Rush.Radament) { - _actions.set("rada", new RushAction("Rush Radament", radament)); - } - if (Config.ControlBot.Rush.Staff) { - _actions.set("staff", new RushAction("Rush Staff", staff)); - } - if (Config.ControlBot.Rush.Amulet) { - _actions.set("amu", new RushAction("Rush Amulet", amulet)); - } - if (Config.ControlBot.Rush.Summoner) { - _actions.set("summoner", new RushAction("Rush Summoner", summoner)); - } - if (Config.ControlBot.Rush.Duriel) { - _actions.set("duri", new RushAction("Rush Duriel", duriel)); - } - if (Config.ControlBot.Rush.LamEsen) { - _actions.set("lamesen", new RushAction("Rush Lamesen", lamesen)); - } - if (Config.ControlBot.Rush.Eye) { - _actions.set("eye", new RushAction("Rush Eye", eye)); - } - if (Config.ControlBot.Rush.Brain) { - _actions.set("brain", new RushAction("Rush Brain", brain)); - } - if (Config.ControlBot.Rush.Heart) { - _actions.set("heart", new RushAction("Rush Heart", heart)); - } - if (Config.ControlBot.Rush.Travincal) { - _actions.set("trav", new RushAction("Rush Travincal", travincal)); - } - if (Config.ControlBot.Rush.Mephisto) { - _actions.set("meph", new RushAction("Rush Mephisto", mephisto)); - } - if (Config.ControlBot.Rush.Izual) { - _actions.set("izzy", new RushAction("Rush Izual", izual)); + if (Config.ControlBot.DropGold) { + _actions.set("dropgold", { + desc: "Drop 5k gold", + hostileCheck: false, + run: dropGold + }); } - if (Config.ControlBot.Rush.Diablo) { - _actions.set("diablo", new RushAction("Rush Diablo", diablo)); + + if (Config.ControlBot.Chant.Enchant + && Skill.canUse(sdk.skills.Enchant)) { + _actions.set("chant", { + desc: "Give enchant", + hostileCheck: false, + run: enchant + }); + } else { + Config.ControlBot.Chant.AutoEnchant = false; } - if (Config.ControlBot.Rush.Shenk) { - _actions.set("shenk", new RushAction("Rush Shenk", shenk)); + + if (Config.ControlBot.Cows.MakeCows && !me.cows) { + _actions.set("cows", { + desc: "Open cow level", + hostileCheck: true, + run: openPortal + }); } - if (Config.ControlBot.Rush.Anya) { - _actions.set("anya", new RushAction("Rush Anya", anya)); + + if (Config.ControlBot.Wps.GiveWps) { + _actions.set("wps", { + desc: "Give wps in act", + hostileCheck: true, + run: giveWps + }); } - if (Config.ControlBot.Rush.Ancients) { - _actions.set("ancients", new RushAction("Rush Ancients", ancients)); + + if (Config.ControlBot.Bo + && (Skill.canUse(sdk.skills.BattleOrders) || Precast.haveCTA > 0)) { + _actions.set("bo", { + desc: "Bo at wp", + hostileCheck: true, + run: bo + }); } - if (Config.ControlBot.Rush.Baal) { - _actions.set("baal", new RushAction("Rush Baal", baal)); + + if (Config.ControlBot.Rush) { + if (Config.ControlBot.Rush.Andy) { + _actions.set("andy", new RushAction("Rush Andariel", andariel)); + } + if (Config.ControlBot.Rush.Bloodraven) { + _actions.set("raven", new RushAction("Rush Bloodraven", bloodraven)); + } + if (Config.ControlBot.Rush.Smith) { + _actions.set("smith", new RushAction("Rush Smith", smith)); + } + if (Config.ControlBot.Rush.Cube) { + _actions.set("cube", new RushAction("Rush Cube", cube)); + } + if (Config.ControlBot.Rush.Radament) { + _actions.set("rada", new RushAction("Rush Radament", radament)); + } + if (Config.ControlBot.Rush.Staff) { + _actions.set("staff", new RushAction("Rush Staff", staff)); + } + if (Config.ControlBot.Rush.Amulet) { + _actions.set("amu", new RushAction("Rush Amulet", amulet)); + } + if (Config.ControlBot.Rush.Summoner) { + _actions.set("summoner", new RushAction("Rush Summoner", summoner)); + } + if (Config.ControlBot.Rush.Duriel) { + _actions.set("duri", new RushAction("Rush Duriel", duriel)); + } + if (Config.ControlBot.Rush.LamEsen) { + _actions.set("lamesen", new RushAction("Rush Lamesen", lamesen)); + } + if (Config.ControlBot.Rush.Eye) { + _actions.set("eye", new RushAction("Rush Eye", eye)); + } + if (Config.ControlBot.Rush.Brain) { + _actions.set("brain", new RushAction("Rush Brain", brain)); + } + if (Config.ControlBot.Rush.Heart) { + _actions.set("heart", new RushAction("Rush Heart", heart)); + } + if (Config.ControlBot.Rush.Travincal) { + _actions.set("trav", new RushAction("Rush Travincal", travincal)); + } + if (Config.ControlBot.Rush.Mephisto) { + _actions.set("meph", new RushAction("Rush Mephisto", mephisto)); + } + if (Config.ControlBot.Rush.Izual) { + _actions.set("izzy", new RushAction("Rush Izual", izual)); + } + if (Config.ControlBot.Rush.Diablo) { + _actions.set("diablo", new RushAction("Rush Diablo", diablo)); + } + if (Config.ControlBot.Rush.Shenk) { + _actions.set("shenk", new RushAction("Rush Shenk", shenk)); + } + if (Config.ControlBot.Rush.Anya) { + _actions.set("anya", new RushAction("Rush Anya", anya)); + } + if (Config.ControlBot.Rush.Ancients) { + _actions.set("ancients", new RushAction("Rush Ancients", ancients)); + } + if (Config.ControlBot.Rush.Baal) { + _actions.set("baal", new RushAction("Rush Baal", baal)); + } } - } - return _actions; - })(); - - /** @type {Map} */ - const commandAliases = new Map([ - ["andariel", "andy"], - ["bloodraven", "raven"], - ["radament", "rada"], - ["amulet", "amu"], - ["ammy", "amu"], - ["duriel", "duri"], - ["dury", "duri"], - ["tome", "lamesen"], - ["travincal", "trav"], - ["mephisto", "meph"], - ["izual", "izzy"], - ["bome", "bo"], - ["time", "timeleft"], - ["enchant", "chant"], - ]); - - /** @param {[string, string]} command */ - const runAction = function (command) { - if (!command || command.length < 2) return false; - console.debug("Checking command: " + command); - let [cmd, nick] = command; - if (cmd.match(/^rush /gi)) { - cmd = cmd.split(" ")[1]; - } - if (commandAliases.has(cmd.toLowerCase())) { - cmd = commandAliases.get(cmd.toLowerCase()); - } + return _actions; + })(); + + /** @type {Map} */ + const commandAliases = new Map([ + ["andariel", "andy"], + ["bloodraven", "raven"], + ["radament", "rada"], + ["amulet", "amu"], + ["ammy", "amu"], + ["duriel", "duri"], + ["dury", "duri"], + ["tome", "lamesen"], + ["travincal", "trav"], + ["mephisto", "meph"], + ["izual", "izzy"], + ["bome", "bo"], + ["time", "timeleft"], + ["enchant", "chant"], + ]); + + /** @param {[string, string]} command */ + const runAction = function (command) { + if (!command || command.length < 2) return false; + console.debug("Checking command: " + command); + let [cmd, nick] = command; + if (cmd.match(/^rush /gi)) { + cmd = cmd.split(" ")[1]; + } + if (commandAliases.has(cmd.toLowerCase())) { + cmd = commandAliases.get(cmd.toLowerCase()); + } + + if (!actions.has(cmd.toLowerCase())) return false; + let action = actions.get(cmd.toLowerCase()); + if (action.desc.includes("Rush") && action.complete) { + Chat.whisper(nick, cmd + " disabled because it's already completed."); + return false; + } + if (action.hostileCheck && checkHostiles()) { + Chat.say("Command disabled because of hostiles."); + return false; + } + running.nick = nick; + running.command = cmd; + console.debug(running); + + return action.run(nick); + }; - if (!actions.has(cmd.toLowerCase())) return false; - let action = actions.get(cmd.toLowerCase()); - if (action.desc.includes("Rush") && action.complete) { - Chat.whisper(nick, cmd + " disabled because it's already completed."); - return false; - } - if (action.hostileCheck && checkHostiles()) { - Chat.say("Command disabled because of hostiles."); - return false; - } - running.nick = nick; - running.command = cmd; - console.debug(running); - - return action.run(nick); - }; - - // START - let gameEndWarningAnnounced = false; - include("oog/ShitList.js"); - Config.ShitList && shitList.add(ShitList.read()); - - try { - addEventListener("chatmsg", chatEvent); - addEventListener("gameevent", gameEvent); - Town.doChores(); - Town.goToTown(1); - Town.move("portalspot"); - - // check who is in game in cased we missed the gameevent or this was a restart - let party = getParty(); - if (party) { - do { - if (party.name !== me.name && !players.has(party.name)) { - players.set(party.name, ""); - } - } while (party.getNext()); - } + // START + let gameEndWarningAnnounced = false; + include("oog/ShitList.js"); + Config.ShitList && shitList.add(ShitList.read()); - while (true) { - while (greet.length > 0) { - let nick = greet.shift(); + try { + addEventListener("chatmsg", chatEvent); + addEventListener("gameevent", gameEvent); + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); - if (!shitList.has(nick)) { - Chat.say("Welcome, " + nick + "! For a list of commands say 'help'"); - } + // check who is in game in cased we missed the gameevent or this was a restart + let party = getParty(); + if (party) { + do { + if (party.name !== me.name && !players.has(party.name)) { + players.set(party.name, ""); + } + } while (party.getNext()); } - Town.getDistance("portalspot") > 5 && Town.move("portalspot"); - - if (queue.length > 0) { - try { - let command = queue.shift(); - if (command && !floodCheck(command)) { - if (runAction(command)) { - // check if command was for rush, if so we need to remove that as an option since its now completed - if (actions.get(running.command).desc.includes("Rush")) { - console.log("Disabling " + running.command + " from actions"); - actions.get(running.command).markAsComplete(); + while (true) { + while (greet.length > 0) { + let nick = greet.shift(); + + if (!shitList.has(nick)) { + Chat.say("Welcome, " + nick + "! For a list of commands say 'help'"); + } + } + + Town.getDistance("portalspot") > 5 && Town.move("portalspot"); + + if (queue.length > 0) { + try { + let command = queue.shift(); + if (command && !floodCheck(command)) { + if (runAction(command)) { + // check if command was for rush, if so we need to remove that as an option since its now completed + if (actions.get(running.command).desc.includes("Rush")) { + console.log("Disabling " + running.command + " from actions"); + actions.get(running.command).markAsComplete(); + } } } + } catch (e) { + Misc.errorReport(e); } - } catch (e) { - Misc.errorReport(e); + running.nick = ""; + running.command = ""; } - running.nick = ""; - running.command = ""; - } - me.act > 1 && Town.goToTown(1); - Config.ControlBot.Chant.AutoEnchant && autoChant(); + me.act > 1 && Town.goToTown(1); + Config.ControlBot.Chant.AutoEnchant && autoChant(); - if (me.gold < MIN_GOLD && players.size > 1) { - Chat.overhead( - "I am low on gold, to keep this service up please donate by dropping gold near me." - + " I need at least " + (MIN_GOLD - me.gold) + " gold." - ); - } - pickGoldPiles(); + if (me.gold < MIN_GOLD && players.size > 1) { + Chat.overhead( + "I am low on gold, to keep this service up please donate by dropping gold near me." + + " I need at least " + (MIN_GOLD - me.gold) + " gold." + ); + } + pickGoldPiles(); - if (ngVote.watch && ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { - Chat.say("Not enough votes to start next game."); - ngVote.reset(); - } + if (ngVote.watch && ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { + Chat.say("Not enough votes to start next game."); + ngVote.reset(); + } - if (getTickCount() - startTime >= maxTime || ngVote.nextGame) { - if (Config.ControlBot.EndMessage) { - Chat.say(Config.ControlBot.EndMessage); + if (getTickCount() - startTime >= maxTime || ngVote.nextGame) { + if (Config.ControlBot.EndMessage) { + Chat.say(Config.ControlBot.EndMessage); + } + delay(1000); + + break; + } else if (!gameEndWarningAnnounced && getTickCount() - startTime >= maxTime - Time.seconds(30)) { + let remaining = Math.round((maxTime - (getTickCount() - startTime)) / 1000); + Chat.say("Next game in " + (Math.max(0, remaining)) + " seconds."); + gameEndWarningAnnounced = true; } - delay(1000); - break; - } else if (!gameEndWarningAnnounced && getTickCount() - startTime >= maxTime - Time.seconds(30)) { - let remaining = Math.round((maxTime - (getTickCount() - startTime)) / 1000); - Chat.say("Next game in " + (Math.max(0, remaining)) + " seconds."); - gameEndWarningAnnounced = true; + delay(200); } - - delay(200); + } finally { + removeEventListener("chatmsg", chatEvent); + removeEventListener("gameevent", gameEvent); } - } finally { - removeEventListener("chatmsg", chatEvent); - removeEventListener("gameevent", gameEvent); - } - return true; -} + return true; + }, + sdk.areas.RogueEncampment +); diff --git a/d2bs/kolbot/libs/scripts/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js index 78bfae95a..2be2eca26 100644 --- a/d2bs/kolbot/libs/scripts/Corpsefire.js +++ b/d2bs/kolbot/libs/scripts/Corpsefire.js @@ -5,18 +5,21 @@ * */ -function Corpsefire () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); +const Corpsefire = new Runnable( + function Corpsefire () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true)) { - throw new Error("Failed to move to Corpsefire"); - } + if (!Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true) + || !Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true)) { + throw new Error("Failed to move to Corpsefire"); + } - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Corpsefire)); - Config.Corpsefire.ClearDen && Attack.clearLevel(); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Corpsefire)); + Config.Corpsefire.ClearDen && Attack.clearLevel(); - return true; -} + return true; + }, + sdk.areas.ColdPlains +); diff --git a/d2bs/kolbot/libs/scripts/Countess.js b/d2bs/kolbot/libs/scripts/Countess.js index 47014a720..e9c525875 100644 --- a/d2bs/kolbot/libs/scripts/Countess.js +++ b/d2bs/kolbot/libs/scripts/Countess.js @@ -5,31 +5,34 @@ * */ -function Countess () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.BlackMarsh); - Precast.doPrecast(true); +const Countess = new Runnable( + function Countess () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.BlackMarsh); + Precast.doPrecast(true); - if (!Pather.moveToExit([ - sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, - sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5 - ], true)) throw new Error("Failed to move to Countess"); + if (!Pather.moveToExit([ + sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, + sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TowerCellarLvl5 + ], true)) throw new Error("Failed to move to Countess"); - let poi = Game.getPresetObject(me.area, sdk.objects.SuperChest); + let poi = Game.getPresetObject(me.area, sdk.objects.SuperChest); - if (!poi) throw new Error("Failed to move to Countess (preset not found)"); + if (!poi) throw new Error("Failed to move to Countess (preset not found)"); - switch (poi.roomx * 5 + poi.x) { - case 12565: - Pather.moveTo(12578, 11043); - break; - case 12526: - Pather.moveTo(12548, 11083); - break; - } + switch (poi.roomx * 5 + poi.x) { + case 12565: + Pather.moveTo(12578, 11043); + break; + case 12526: + Pather.moveTo(12548, 11083); + break; + } - Attack.clear(20, 0, getLocaleString(sdk.locale.monsters.TheCountess)); - Config.OpenChests.Enabled && Misc.openChestsInArea(); + Attack.clear(20, 0, getLocaleString(sdk.locale.monsters.TheCountess)); + Config.OpenChests.Enabled && Misc.openChestsInArea(); - return true; -} + return true; + }, + sdk.areas.BlackMarsh +); diff --git a/d2bs/kolbot/libs/scripts/Cows.js b/d2bs/kolbot/libs/scripts/Cows.js index 9975a40a8..aaa1eb4cf 100644 --- a/d2bs/kolbot/libs/scripts/Cows.js +++ b/d2bs/kolbot/libs/scripts/Cows.js @@ -5,151 +5,154 @@ * */ -function Cows () { - include("core/Common/Cows.js"); - - const getLeg = function () { - if (me.wirtsleg) return me.wirtsleg; - - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); - - if (!Misc.poll(() => { - let p = Pather.getPortal(sdk.areas.Tristram); - return (p && Pather.usePortal(sdk.areas.Tristram, null, p)); - }, Time.minutes(1), 1000)) { - throw new Error("Tristram portal not found"); - } +const Cows = new Runnable( + function Cows () { + include("core/Common/Cows.js"); + + const getLeg = function () { + if (me.wirtsleg) return me.wirtsleg; + + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); + + if (!Misc.poll(() => { + let p = Pather.getPortal(sdk.areas.Tristram); + return (p && Pather.usePortal(sdk.areas.Tristram, null, p)); + }, Time.minutes(1), 1000)) { + throw new Error("Tristram portal not found"); + } - Pather.moveTo(25048, 5177); + Pather.moveTo(25048, 5177); - let wirt = Game.getObject(sdk.quest.chest.Wirt); + let wirt = Game.getObject(sdk.quest.chest.Wirt); - for (let i = 0; i < 8; i += 1) { - wirt.interact(); - delay(500); + for (let i = 0; i < 8; i += 1) { + wirt.interact(); + delay(500); - let leg = Game.getItem(sdk.quest.item.WirtsLeg); + let leg = Game.getItem(sdk.quest.item.WirtsLeg); - if (leg) { - let gid = leg.gid; + if (leg) { + let gid = leg.gid; - Pickit.pickItem(leg); - Town.goToTown(); + Pickit.pickItem(leg); + Town.goToTown(); - return me.getItem(-1, -1, gid); + return me.getItem(-1, -1, gid); + } } - } - throw new Error("Failed to get the leg"); - }; + throw new Error("Failed to get the leg"); + }; - const getTome = function () { - let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); + const getTome = function () { + let tpTome = me.findItems(sdk.items.TomeofTownPortal, sdk.items.mode.inStorage, sdk.storage.Inventory); - if (tpTome.length < 2) { - let npc = Town.initNPC("Shop", "buyTpTome"); + if (tpTome.length < 2) { + let npc = Town.initNPC("Shop", "buyTpTome"); - if (!getInteractedNPC()) { - throw new Error("Failed to find npc"); - } + if (!getInteractedNPC()) { + throw new Error("Failed to find npc"); + } - let tome = npc.getItem(sdk.items.TomeofTownPortal); + let 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); - tpTome.forEach(function (book) { - if (book.isInInventory) { - let scroll = npc.getItem(sdk.items.ScrollofTownPortal); - while (book.getStat(sdk.stats.Quantity) < 20) { - if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { - scroll.buy(true); - } else { - break; - } + 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); + tpTome.forEach(function (book) { + if (book.isInInventory) { + let scroll = npc.getItem(sdk.items.ScrollofTownPortal); + while (book.getStat(sdk.stats.Quantity) < 20) { + if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { + scroll.buy(true); + } else { + break; + } - delay(20); + delay(20); + } } - } - }); - } else { - throw new Error("Failed to buy tome"); + }); + } else { + throw new Error("Failed to buy tome"); + } } - } - - return tpTome.last(); - }; - - const openPortal = function (leg, tome) { - if (!Town.openStash()) throw new Error("Failed to open stash"); - if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); - if (!Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome) || !Cubing.openCube()) { - throw new Error("Failed to cube leg and tome"); - } - transmute(); - delay(1000); - me.cancelUIFlags(); + return tpTome.last(); + }; - for (let i = 0; i < 10; i += 1) { - if (Pather.getPortal(sdk.areas.MooMooFarm)) { - return true; + const openPortal = function (leg, tome) { + if (!Town.openStash()) throw new Error("Failed to open stash"); + if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); + if (!Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome) || !Cubing.openCube()) { + throw new Error("Failed to cube leg and tome"); } - delay(200); - } + transmute(); + delay(1000); + me.cancelUIFlags(); - throw new Error("Portal not found"); - }; + for (let i = 0; i < 10; i += 1) { + if (Pather.getPortal(sdk.areas.MooMooFarm)) { + return true; + } + delay(200); + } - // we can begin now - try { - if (!me.diffCompleted) throw new Error("Final quest incomplete."); + throw new Error("Portal not found"); + }; - Town.goToTown(1); - Town.doChores(); - Town.move("stash"); - // Check to see if portal is already open, if not get the ingredients - if (!Pather.getPortal(sdk.areas.MooMooFarm)) { - if (Config.Cows.DontMakePortal) throw new Error("NOT PORTAL MAKER"); - if (!me.tristram) throw new Error("Cain quest incomplete"); - if (me.cows) throw new Error("Already killed the Cow King."); - - let leg = getLeg(); - let tome = getTome(); - openPortal(leg, tome); - } - } catch (e) { - typeof e === "object" && e.message && e.message !== "NOT PORTAL MAKER" && console.error(e); + // we can begin now + try { + if (!me.diffCompleted) throw new Error("Final quest incomplete."); - if (Misc.getPlayerCount() > 1) { Town.goToTown(1); + Town.doChores(); Town.move("stash"); - console.log("ÿc9(Cows) :: ÿc0Waiting 1 minute to see if anyone else opens the cow portal"); - if (!Misc.poll(() => Pather.getPortal(sdk.areas.MooMooFarm), Time.minutes(3), 2000)) { - throw new Error("No cow portal"); + // Check to see if portal is already open, if not get the ingredients + if (!Pather.getPortal(sdk.areas.MooMooFarm)) { + if (Config.Cows.DontMakePortal) throw new Error("NOT PORTAL MAKER"); + if (!me.tristram) throw new Error("Cain quest incomplete"); + if (me.cows) throw new Error("Already killed the Cow King."); + + let leg = getLeg(); + let tome = getTome(); + openPortal(leg, tome); + } + } catch (e) { + typeof e === "object" && e.message && e.message !== "NOT PORTAL MAKER" && console.error(e); + + if (Misc.getPlayerCount() > 1) { + Town.goToTown(1); + Town.move("stash"); + console.log("ÿc9(Cows) :: ÿc0Waiting 1 minute to see if anyone else opens the cow portal"); + + if (!Misc.poll(() => Pather.getPortal(sdk.areas.MooMooFarm), Time.minutes(3), 2000)) { + throw new Error("No cow portal"); + } + } else { + return false; } - } else { - return false; } - } - if (Config.Cows.JustMakePortal) { - if (Pather.getPortal(sdk.areas.MooMooFarm)) { - return true; - } else { - throw new Error("I failed to make cow portal"); + if (Config.Cows.JustMakePortal) { + if (Pather.getPortal(sdk.areas.MooMooFarm)) { + return true; + } else { + throw new Error("I failed to make cow portal"); + } } - } - Pather.usePortal(sdk.areas.MooMooFarm); - Precast.doPrecast(false); - Config.Cows.KillKing ? Attack.clearLevel() : Common.Cows.clearCowLevel(); + Pather.usePortal(sdk.areas.MooMooFarm); + Precast.doPrecast(false); + Config.Cows.KillKing ? Attack.clearLevel() : Common.Cows.clearCowLevel(); - return true; -} + return true; + }, + sdk.areas.RogueEncampment +); diff --git a/d2bs/kolbot/libs/scripts/Crafting.js b/d2bs/kolbot/libs/scripts/Crafting.js index 987856504..1891e9880 100644 --- a/d2bs/kolbot/libs/scripts/Crafting.js +++ b/d2bs/kolbot/libs/scripts/Crafting.js @@ -8,116 +8,119 @@ let info; let gameRequest = false; -function Crafting () { - info = CraftingSystem.getInfo(); +const Crafting = new Runnable( + function Crafting () { + info = CraftingSystem.getInfo(); - if (!info || !info.worker) throw new Error("Bad Crafting System config."); + if (!info || !info.worker) throw new Error("Bad Crafting System config."); - me.maxgametime = 0; - Town.goToTown(1); - Town.doChores(); - Town.move("stash"); - updateInfo(); - pickItems(); - - addEventListener("copydata", - function (mode, msg) { - let obj, rval; - - if (mode === 0) { - try { - obj = JSON.parse(msg); - } catch (e) { - return false; - } - - if (obj) { - switch (obj.name) { - case "GetGame": - if (info.Collectors.includes(obj.profile)) { - print("GetGame: " + obj.profile); - sendCopyData(null, obj.profile, 4, me.gamename + "/" + me.gamepassword); + me.maxgametime = 0; + Town.goToTown(1); + Town.doChores(); + Town.move("stash"); + updateInfo(); + pickItems(); - gameRequest = true; - } + addEventListener("copydata", + function (mode, msg) { + let obj, rval; - break; - case "GetSetInfo": - if (info.Collectors.includes(obj.profile)) { - print("GetSetInfo: " + obj.profile); + if (mode === 0) { + try { + obj = JSON.parse(msg); + } catch (e) { + return false; + } - rval = []; + if (obj) { + switch (obj.name) { + case "GetGame": + if (info.Collectors.includes(obj.profile)) { + print("GetGame: " + obj.profile); + sendCopyData(null, obj.profile, 4, me.gamename + "/" + me.gamepassword); - for (let i = 0; i < info.Sets.length; i += 1) { - rval.push(info.Sets[i].Enabled ? 1 : 0); + gameRequest = true; } - print(rval); - - sendCopyData(null, obj.profile, 4, JSON.stringify({ name: "SetInfo", value: rval })); - } - - break; - } - } - } + break; + case "GetSetInfo": + if (info.Collectors.includes(obj.profile)) { + print("GetSetInfo: " + obj.profile); - return true; - }); + rval = []; - for (let i = 0; i < Cubing.recipes.length; i += 1) { - Cubing.recipes[i].Level = 0; - } + for (let i = 0; i < info.Sets.length; i += 1) { + rval.push(info.Sets[i].Enabled ? 1 : 0); + } - while (true) { - for (let i = 0; i < info.Sets.length; i += 1) { - switch (info.Sets[i].Type) { - case "crafting": - let num = 0; - let npcName = getNPCName(info.Sets[i].BaseItems); + print(rval); - if (npcName) { - num = countItems(info.Sets[i].BaseItems, 4); + sendCopyData(null, obj.profile, 4, JSON.stringify({ name: "SetInfo", value: rval })); + } - if (num < info.Sets[i].SetAmount) { - shopStuff(npcName, info.Sets[i].BaseItems, info.Sets[i].SetAmount); + break; + } } } - break; - case "cubing": // Nothing to do currently - break; - case "runewords": // Nothing to do currently - break; - } + return true; + }); + + for (let i = 0; i < Cubing.recipes.length; i += 1) { + Cubing.recipes[i].Level = 0; } - me.act !== 1 && Town.goToTown(1) && Town.move("stash"); + while (true) { + for (let i = 0; i < info.Sets.length; i += 1) { + switch (info.Sets[i].Type) { + case "crafting": + let num = 0; + let npcName = getNPCName(info.Sets[i].BaseItems); + + if (npcName) { + num = countItems(info.Sets[i].BaseItems, 4); - if (gameRequest) { - for (let i = 0; i < 10; i += 1) { - if (Misc.getPlayerCount() > 1) { - while (Misc.getPlayerCount() > 1) { - delay(200); + if (num < info.Sets[i].SetAmount) { + shopStuff(npcName, info.Sets[i].BaseItems, info.Sets[i].SetAmount); + } } break; - } else { + case "cubing": // Nothing to do currently + break; + case "runewords": // Nothing to do currently break; } } - gameRequest = false; - } + me.act !== 1 && Town.goToTown(1) && Town.move("stash"); - pickItems(); - Cubing.update(); - Runewords.buildLists(); - Cubing.doCubing(); - Runewords.makeRunewords(); - delay(2000); - } -} + if (gameRequest) { + for (let i = 0; i < 10; i += 1) { + if (Misc.getPlayerCount() > 1) { + while (Misc.getPlayerCount() > 1) { + delay(200); + } + + break; + } else { + break; + } + } + + gameRequest = false; + } + + pickItems(); + Cubing.update(); + Runewords.buildLists(); + Cubing.doCubing(); + Runewords.makeRunewords(); + delay(2000); + } + }, + sdk.areas.RogueEncampment +); function getNPCName (idList) { for (let i = 0; i < idList.length; i += 1) { diff --git a/d2bs/kolbot/libs/scripts/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js index 5cec063f4..2ac136fee 100644 --- a/d2bs/kolbot/libs/scripts/CreepingFeature.js +++ b/d2bs/kolbot/libs/scripts/CreepingFeature.js @@ -5,14 +5,17 @@ * */ -function CreepingFeature () { - Town.doChores(); - Town.goToTown(2); - - Pather.journeyTo(sdk.areas.StonyTombLvl2); - Pather.moveToPresetMonster(sdk.areas.StonyTombLvl2, sdk.monsters.preset.CreepingFeature); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.CreepingFeature)); - Pickit.pickItems(); +const CreepingFeature = new Runnable( + function CreepingFeature () { + Town.doChores(); + Town.goToTown(2); + + Pather.journeyTo(sdk.areas.StonyTombLvl2); + Pather.moveToPresetMonster(sdk.areas.StonyTombLvl2, sdk.monsters.preset.CreepingFeature); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.CreepingFeature)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.LutGholein +); diff --git a/d2bs/kolbot/libs/scripts/CrushTele.js b/d2bs/kolbot/libs/scripts/CrushTele.js index b09b718d7..eed5a7632 100644 --- a/d2bs/kolbot/libs/scripts/CrushTele.js +++ b/d2bs/kolbot/libs/scripts/CrushTele.js @@ -5,52 +5,54 @@ * */ -function CrushTele () { - let go = false; +const CrushTele = new Runnable( + function CrushTele () { + let go = false; - addEventListener("keyup", - function (key) { - key === sdk.keys.NumpadDash && (go = true); - } - ); + addEventListener("keyup", + function (key) { + key === sdk.keys.NumpadDash && (go = true); + } + ); - while (true) { - if (go) { - switch (me.area) { - case sdk.areas.CatacombsLvl2: - Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); - break; - case sdk.areas.HallsoftheDeadLvl2: - Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest); - break; - case sdk.areas.FarOasis: - Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); - break; - case sdk.areas.LostCity: - Pather.moveToExit([ - sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2 - ], true); - break; - case sdk.areas.CanyonofMagic: - Pather.moveToExit(getRoom().correcttomb, true); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder); - break; - case sdk.areas.ArcaneSanctuary: - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, 0, 0, false, true); - break; - case sdk.areas.DuranceofHateLvl2: - Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true); - break; - case sdk.areas.RiverofFlame: - Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, sdk.objects.DiabloStar); - break; + while (true) { + if (go) { + switch (me.area) { + case sdk.areas.CatacombsLvl2: + Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true); + break; + case sdk.areas.HallsoftheDeadLvl2: + Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest); + break; + case sdk.areas.FarOasis: + Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); + break; + case sdk.areas.LostCity: + Pather.moveToExit([ + sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2 + ], true); + break; + case sdk.areas.CanyonofMagic: + Pather.moveToExit(getRoom().correcttomb, true); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder); + break; + case sdk.areas.ArcaneSanctuary: + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal, 0, 0, false, true); + break; + case sdk.areas.DuranceofHateLvl2: + Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true); + break; + case sdk.areas.RiverofFlame: + Pather.moveToPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, sdk.objects.DiabloStar); + break; + } + + go = false; } - go = false; + delay(10); } - - delay(10); } -} +); diff --git a/d2bs/kolbot/libs/scripts/DeveloperMode.js b/d2bs/kolbot/libs/scripts/DeveloperMode.js index 1f6d354c3..ef9f8c165 100644 --- a/d2bs/kolbot/libs/scripts/DeveloperMode.js +++ b/d2bs/kolbot/libs/scripts/DeveloperMode.js @@ -5,278 +5,280 @@ * */ -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": - print("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); - me.overhead("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) { - print("ÿc1Missing arguments"); - } else { - action = msgList[1]; - } - - break; - case "done": - done = true; +const DeveloperMode = new Runnable( + 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; - break; - case "testing": - test = true; + let cmd = msg.split(" ")[0].split(".")[1]; + let msgList = msg.split(" "); - break; - case "command": - if (msgList.length < 2) { - print("ÿc1Missing arguments"); - } else { - command = msgList.splice(1).join(" "); - } + switch (cmd.toLowerCase()) { + case "me": + print("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); + me.overhead("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); - break; - case "watch": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); break; - } + case "useraddon": + userAddon = !userAddon; + me.overhead("userAddon set to " + userAddon); - switch (msgList[1].toLowerCase()) { - case "sent": - if (msgList[2] === "list") { - print("Watching sent packets : ÿc8" + watchSent.join(", ")); - break; - } - - watchSent.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); break; - - case "recv": - if (msgList[2] === "list") { - print("Watching received packets : ÿc8" + watchRecv.join(", ")); - break; + case "run": + if (msgList.length < 2) { + print("ÿc1Missing arguments"); + } else { + action = msgList[1]; } - watchRecv.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); break; + case "done": + done = true; - default: - print("ÿc1Invalid argument : " + msgList[1]); break; - } + case "testing": + test = true; - break; - - case "!watch": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); break; - } + case "command": + if (msgList.length < 2) { + print("ÿc1Missing arguments"); + } else { + command = msgList.splice(1).join(" "); + } - switch (msgList[1].toLowerCase()) { - case "sent": - if (watchSent.indexOf(msgList[2]) > -1) watchSent.splice(watchSent.indexOf(msgList[2]), 1); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from watch list"); break; + case "watch": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); + break; + } - case "recv": - if (watchRecv.indexOf(msgList[2]) > -1) watchRecv.splice(watchRecv.indexOf(msgList[2]), 1); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); - break; + switch (msgList[1].toLowerCase()) { + case "sent": + if (msgList[2] === "list") { + print("Watching sent packets : ÿc8" + watchSent.join(", ")); + break; + } - default: - print("ÿc1Invalid argument : " + msgList[1]); - break; - } + watchSent.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); + break; - break; + case "recv": + if (msgList[2] === "list") { + print("Watching received packets : ÿc8" + watchRecv.join(", ")); + break; + } - case "block": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); - break; - } + watchRecv.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); + break; - switch (msgList[1].toLowerCase()) { - case "sent": - if (msgList[2] === "list") { - print("Blocking sent packets : ÿc8" + blockSent.join(", ")); + default: + print("ÿc1Invalid argument : " + msgList[1]); break; } - blockSent.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); break; - case "recv": - if (msgList[2] === "list") { - print("Blocking received packets : ÿc8" + blockRecv.join(", ")); + case "!watch": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); break; } - blockRecv.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); - break; + switch (msgList[1].toLowerCase()) { + case "sent": + if (watchSent.indexOf(msgList[2]) > -1) watchSent.splice(watchSent.indexOf(msgList[2]), 1); + print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from watch list"); + break; - default: - print("ÿc1Invalid argument : " + msgList[1]); - break; - } + case "recv": + if (watchRecv.indexOf(msgList[2]) > -1) watchRecv.splice(watchRecv.indexOf(msgList[2]), 1); + print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); + break; - break; + default: + print("ÿc1Invalid argument : " + msgList[1]); + break; + } - case "!block": - if (msgList.length < 3) { - print("ÿc1Missing arguments"); break; - } - switch (msgList[1].toLowerCase()) { - case "sent": - if (blockSent.indexOf(msgList[2]) > -1) blockSent.splice(blockSent.indexOf(msgList[2]), 1); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from block list"); - break; + case "block": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); + break; + } - case "recv": - if (blockRecv.indexOf(msgList[2]) > -1) blockRecv.splice(blockRecv.indexOf(msgList[2]), 1); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); - break; + switch (msgList[1].toLowerCase()) { + case "sent": + if (msgList[2] === "list") { + print("Blocking sent packets : ÿc8" + blockSent.join(", ")); + break; + } - default: - print("ÿc1Invalid argument : " + msgList[1]); - break; - } + blockSent.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); + break; - break; - } - }; + case "recv": + if (msgList[2] === "list") { + print("Blocking received packets : ÿc8" + blockRecv.join(", ")); + break; + } - // Received packet handler - const packetReceived = function (pBytes) { - let ID = pBytes[0].toString(16); + blockRecv.push(msgList[2]); + print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); + break; - // Block received packets from list - if (blockRecv.includes(ID)) return true; + default: + print("ÿc1Invalid argument : " + msgList[1]); + break; + } - if (watchRecv.includes(ID)) { - let size = pBytes.length; - let array = [].slice.call(pBytes); - array.shift(); - console.log("ÿc2S ÿc8" + ID + "ÿc0 " + array.join(" ") + " ÿc5(" + size + " Bytes)"); - } + break; - return false; - }; + case "!block": + if (msgList.length < 3) { + print("ÿc1Missing arguments"); + break; + } - // Sent packet handler - const packetSent = function (pBytes) { - let ID = pBytes[0].toString(16); + switch (msgList[1].toLowerCase()) { + case "sent": + if (blockSent.indexOf(msgList[2]) > -1) blockSent.splice(blockSent.indexOf(msgList[2]), 1); + print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from block list"); + break; - // Block all commands or irc chat from being sent to server - if (ID === "15") { - if (pBytes[3] === 46) { - let str = ""; + case "recv": + if (blockRecv.indexOf(msgList[2]) > -1) blockRecv.splice(blockRecv.indexOf(msgList[2]), 1); + print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); + break; - for (let b = 3; b < pBytes.length - 3; b++) { - str += String.fromCharCode(pBytes[b]); + default: + print("ÿc1Invalid argument : " + msgList[1]); + break; } - if (pBytes[3] === 46) { - runCommand(str); - return true; - } + break; } - } + }; - // Block sent packets from list - if (blockSent.includes(ID)) return true; + // Received packet handler + const packetReceived = function (pBytes) { + let ID = pBytes[0].toString(16); - 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)"); - } + // Block received packets from list + if (blockRecv.includes(ID)) return true; - return false; - }; + if (watchRecv.includes(ID)) { + let size = pBytes.length; + let array = [].slice.call(pBytes); + array.shift(); + console.log("ÿc2S ÿc8" + ID + "ÿc0 " + array.join(" ") + " ÿc5(" + size + " Bytes)"); + } - const copiedConfig = copyObj(Config); - const UnitInfo = new (require("../modules/UnitInfo")); + return false; + }; - try { - console.log("starting developermode"); - me.overhead("Started developer mode"); - addEventListener("gamepacketsent", packetSent); - addEventListener("gamepacket", packetReceived); - Config.Silence = false; + // Sent packet handler + const packetSent = function (pBytes) { + let ID = pBytes[0].toString(16); - while (!done) { - if (action) { - includeIfNotIncluded("scripts/" + action + ".js"); + // Block all commands or irc chat from being sent to server + if (ID === "15") { + if (pBytes[3] === 46) { + let str = ""; - UnitInfo.check(); + for (let b = 3; b < pBytes.length - 3; b++) { + str += String.fromCharCode(pBytes[b]); + } - if (isIncluded("scripts/" + action + ".js")) { - try { - Loader.runScript(action); - } catch (e) { - console.error(e); + if (pBytes[3] === 46) { + runCommand(str); + return true; } - } else { - console.warn("Failed to include: " + action); } + } - me.overhead("Done with action"); - action = false; + // 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)"); } - if (command) { - UnitInfo.check(); + return false; + }; + + const copiedConfig = copyObj(Config); + const UnitInfo = new (require("../modules/UnitInfo")); + + try { + console.log("starting developermode"); + me.overhead("Started developer mode"); + addEventListener("gamepacketsent", packetSent); + addEventListener("gamepacket", packetReceived); + Config.Silence = false; + + while (!done) { + if (action) { + includeIfNotIncluded("scripts/" + action + ".js"); + + UnitInfo.check(); + + if (isIncluded("scripts/" + action + ".js")) { + try { + Loader.runScript(action); + } catch (e) { + console.error(e); + } + } else { + console.warn("Failed to include: " + action); + } - try { - eval(command); - } catch (e) { - console.error(e); + me.overhead("Done with action"); + action = false; } - me.overhead("Done with action"); - command = false; - } + if (command) { + UnitInfo.check(); - if (userAddon) { - UnitInfo.createInfo(Game.getSelectedUnit()); - } + try { + eval(command); + } catch (e) { + console.error(e); + } - if (test) { - me.overhead("done"); - test = false; - } + me.overhead("Done with action"); + command = false; + } - delay(100); + if (userAddon) { + UnitInfo.createInfo(Game.getSelectedUnit()); + } + + if (test) { + me.overhead("done"); + test = false; + } + + delay(100); + } + } finally { + removeEventListener("gamepacketsent", packetSent); + removeEventListener("gamepacket", packetReceived); + Config = copiedConfig; + UnitInfo.remove(); } - } finally { - removeEventListener("gamepacketsent", packetSent); - removeEventListener("gamepacket", packetReceived); - Config = copiedConfig; - UnitInfo.remove(); + + return true; } - - return true; -} +); diff --git a/d2bs/kolbot/libs/scripts/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js index fa00eb9e4..416d30550 100644 --- a/d2bs/kolbot/libs/scripts/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -9,82 +9,92 @@ * */ -function Diablo () { - include("core/Common/Diablo.js"); - Pather._teleport = Pather.teleport; - Common.Diablo.clearRadius = Config.Diablo.ClearRadius; +const Diablo = new Runnable( + function Diablo () { + include("core/Common/Diablo.js"); + Pather._teleport = Pather.teleport; + Common.Diablo.clearRadius = Config.Diablo.ClearRadius; - // START - Town.doChores(); - !!Config.RandomPrecast - ? Precast.doRandomPrecast(true, sdk.areas.RiverofFlame) - : Pather.useWaypoint(sdk.areas.RiverofFlame) && Precast.doPrecast(true); - !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); + // START - if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) { - throw new Error("Failed to move to Chaos Sanctuary"); - } + // We can skip chores if it's been less than a minute since we last did them + if (getTickCount() - Town.lastChores > Time.minutes(1)) { + Town.doChores(); + } - Common.Diablo.initLayout(); + if (!me.inArea(sdk.areas.RiverofFlame)) { + !!Config.RandomPrecast + ? Precast.doRandomPrecast(true, sdk.areas.RiverofFlame) + : Pather.useWaypoint(sdk.areas.RiverofFlame) && Precast.doPrecast(true); + !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); + } - if (Config.Diablo.JustViz) { - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); - Config.PublicMode && Pather.makePortal(); - Common.Diablo.vizierSeal(true); + if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) { + throw new Error("Failed to move to Chaos Sanctuary"); + } - return true; - } + Common.Diablo.initLayout(); - try { - if (Config.Diablo.Entrance && !Config.Diablo.Fast) { - Attack.clear(30, 0, false, Common.Diablo.sort); - Pather.moveTo(7790, 5544); + if (Config.Diablo.JustViz) { + Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); + Config.PublicMode && Pather.makePortal(); + Common.Diablo.vizierSeal(true); - if (Config.PublicMode && Pather.makePortal()) { - say(Config.Diablo.EntranceTP); - if (Config.Diablo.WalkClear) { - Pather.teleport = false; + return true; + } + + try { + if (Config.Diablo.Entrance && !Config.Diablo.Fast) { + Attack.clear(30, 0, false, Common.Diablo.sort); + Pather.moveTo(7790, 5544); + + if (Config.PublicMode && Pather.makePortal()) { + say(Config.Diablo.EntranceTP); + if (Config.Diablo.WalkClear) { + Pather.teleport = false; + } } - } - Pather.moveTo(7790, 5544); - Precast.doPrecast(true); - Attack.clear(30, 0, false, Common.Diablo.sort); - Common.Diablo.followPath(Common.Diablo.entranceToStar); - } else { - Pather.moveTo(7774, 5305); - Attack.clear(15, 0, false, Common.Diablo.sort); - } + Pather.moveTo(7790, 5544); + Precast.doPrecast(true); + Attack.clear(30, 0, false, Common.Diablo.sort); + Common.Diablo.followPath(Common.Diablo.entranceToStar); + } else { + Pather.moveTo(7774, 5305); + Attack.clear(15, 0, false, Common.Diablo.sort); + } - Pather.moveTo(7791, 5293); + Pather.moveTo(7791, 5293); - if (Config.PublicMode && Pather.makePortal()) { - say(Config.Diablo.StarTP); - Pather.teleport = !Config.Diablo.WalkClear && Pather._teleport; - } + if (Config.PublicMode && Pather.makePortal()) { + say(Config.Diablo.StarTP); + Pather.teleport = !Config.Diablo.WalkClear && Pather._teleport; + } - Attack.clear(30, 0, false, Common.Diablo.sort); + Attack.clear(30, 0, false, Common.Diablo.sort); - try { - Common.Diablo.runSeals(Config.Diablo.SealOrder); - // maybe instead of throwing error if we fail to open seal, add it to an array to re-check before diabloPrep then if that fails throw and error - Config.PublicMode && say(Config.Diablo.DiabloMsg); - console.log("Attempting to find Diablo"); - Common.Diablo.diabloPrep(); - } catch (error) { - console.warn("Diablo wasn't found. Checking seals."); - Common.Diablo.runSeals(Config.Diablo.SealOrder, true, true); - Common.Diablo.diabloPrep(); - } + try { + Common.Diablo.runSeals(Config.Diablo.SealOrder); + // maybe instead of throwing error if we fail to open seal, add it to an array to re-check before diabloPrep then if that fails throw and error + Config.PublicMode && say(Config.Diablo.DiabloMsg); + console.log("Attempting to find Diablo"); + Common.Diablo.diabloPrep(); + } catch (error) { + console.warn("Diablo wasn't found. Checking seals."); + Common.Diablo.runSeals(Config.Diablo.SealOrder, true, true); + Common.Diablo.diabloPrep(); + } - Attack.kill(sdk.monsters.Diablo); - Pickit.pickItems(); - Config.Diablo.SealLeader && say("done"); - } finally { - if (Pather.teleport !== Pather._teleport) { - Pather.teleport = Pather._teleport; + Attack.kill(sdk.monsters.Diablo); + Pickit.pickItems(); + Config.Diablo.SealLeader && say("done"); + } finally { + if (Pather.teleport !== Pather._teleport) { + Pather.teleport = Pather._teleport; + } } - } - return true; -} + return true; + }, + sdk.areas.RiverofFlame +); diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index d4a6c3cde..a84f41094 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -5,175 +5,178 @@ * */ -function DiabloHelper () { - include("core/Common/Diablo.js"); - this.Leader = Config.Leader; - Common.Diablo.waitForGlow = true; - Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; - Town.doChores(); - const Worker = require("../modules/Worker"); - - try { - addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - - if (Config.DiabloHelper.SkipIfBaal) { - let leadTick = getTickCount(); - - Worker.runInBackground.leaderTracker = function () { - if (Common.Diablo.done) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - let party = getParty(); - - if (party) { - do { - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { - if (Loader.scriptName() === "DiabloHelper") { - throw new Error("Party leader is running baal"); - } else { - // kill process - return false; +const DiabloHelper = new Runnable( + function DiabloHelper () { + include("core/Common/Diablo.js"); + this.Leader = Config.Leader; + Common.Diablo.waitForGlow = true; + Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; + Town.doChores(); + const Worker = require("../modules/Worker"); + + try { + addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + + if (Config.DiabloHelper.SkipIfBaal) { + let leadTick = getTickCount(); + + Worker.runInBackground.leaderTracker = function () { + if (Common.Diablo.done) return false; + // check every 3 seconds + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + let party = getParty(); + + if (party) { + do { + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { + if (Loader.scriptName() === "DiabloHelper") { + throw new Error("Party leader is running baal"); + } else { + // kill process + return false; + } } - } - } while (party.getNext()); - } + } while (party.getNext()); + } - return true; - }; - } + return true; + }; + } - Config.DiabloHelper.SafePrecast && Precast.needOutOfTownCast() - ? Precast.doRandomPrecast(true, sdk.areas.PandemoniumFortress) - : Precast.doPrecast(true); + Config.DiabloHelper.SafePrecast && Precast.needOutOfTownCast() + ? Precast.doRandomPrecast(true, sdk.areas.PandemoniumFortress) + : Precast.doPrecast(true); - if (Config.DiabloHelper.SkipTP) { - !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); + if (Config.DiabloHelper.SkipTP) { + !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); - if (!Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); - !Config.DiabloHelper.Entrance && Pather.moveTo(7774, 5305); + if (!Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); + !Config.DiabloHelper.Entrance && Pather.moveTo(7774, 5305); - if (!Misc.poll(() => { - let party = getParty(); + if (!Misc.poll(() => { + let party = getParty(); - if (party) { - do { - if ((!DiabloHelper.Leader || party.name === DiabloHelper.Leader) - && party.area === sdk.areas.ChaosSanctuary) { - return true; - } - } while (party.getNext()); - } + if (party) { + do { + if ((!DiabloHelper.Leader || party.name === DiabloHelper.Leader) + && party.area === sdk.areas.ChaosSanctuary) { + return true; + } + } while (party.getNext()); + } - Attack.clear(30, 0, false, Common.Diablo.sort); + Attack.clear(30, 0, false, Common.Diablo.sort); - return false; - }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Chaos)"); - } - } else { - Town.goToTown(4); - Town.move("portalspot"); - if (!DiabloHelper.Leader) { - DiabloHelper.Leader = Misc.autoLeaderDetect({ - destination: sdk.areas.ChaosSanctuary, - quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), - timeout: Time.minutes(2) - }); - } - - if (!Misc.poll(() => { - if (Pather.getPortal(sdk.areas.ChaosSanctuary, Config.Leader || null) - && Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader || null)) { - return true; + return false; + }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Chaos)"); + } + } else { + Town.goToTown(4); + Town.move("portalspot"); + if (!DiabloHelper.Leader) { + DiabloHelper.Leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ChaosSanctuary, + quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), + timeout: Time.minutes(2) + }); } - return false; - }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + if (!Misc.poll(() => { + if (Pather.getPortal(sdk.areas.ChaosSanctuary, Config.Leader || null) + && Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader || null)) { + return true; + } + + return false; + }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + } } - } - Common.Diablo.initLayout(); + Common.Diablo.initLayout(); - let diaTick = getTickCount(); + let diaTick = getTickCount(); - Worker.runInBackground.diaSpawned = function () { - if (Common.Diablo.done) return false; - // check every 1/4 second - if (getTickCount() - diaTick < 250) return true; - diaTick = getTickCount(); + Worker.runInBackground.diaSpawned = function () { + if (Common.Diablo.done) return false; + // check every 1/4 second + if (getTickCount() - diaTick < 250) return true; + diaTick = getTickCount(); - if (Common.Diablo.diabloSpawned) throw new Error("Diablo spawned"); + if (Common.Diablo.diabloSpawned) throw new Error("Diablo spawned"); - return true; - }; + return true; + }; + + try { + if (Config.DiabloHelper.Entrance && Common.Diablo.starCoords.distance > Common.Diablo.entranceCoords.distance) { + Attack.clear(35, 0, false, Common.Diablo.sort); + Common.Diablo.followPath(Common.Diablo.entranceToStar); + } else { + Pather.moveTo(7774, 5305); + Attack.clear(35, 0, false, Common.Diablo.sort); + } - try { - if (Config.DiabloHelper.Entrance && Common.Diablo.starCoords.distance > Common.Diablo.entranceCoords.distance) { - Attack.clear(35, 0, false, Common.Diablo.sort); - Common.Diablo.followPath(Common.Diablo.entranceToStar); - } else { Pather.moveTo(7774, 5305); Attack.clear(35, 0, false, Common.Diablo.sort); - } - - Pather.moveTo(7774, 5305); - Attack.clear(35, 0, false, Common.Diablo.sort); - Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); - Common.Diablo.moveToStar(); - Misc.poll(() => { - if (Common.Diablo.diabloSpawned) return true; - if (Game.getMonster(sdk.monsters.Diablo)) return true; - if ([ - sdk.areas.WorldstoneLvl3, - sdk.areas.ThroneofDestruction, - sdk.areas.WorldstoneChamber - ].includes(Misc.getPlayerArea(DiabloHelper.Leader))) { - throw new Error("END"); + Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); + Common.Diablo.moveToStar(); + Misc.poll(() => { + if (Common.Diablo.diabloSpawned) return true; + if (Game.getMonster(sdk.monsters.Diablo)) return true; + if ([ + sdk.areas.WorldstoneLvl3, + sdk.areas.ThroneofDestruction, + sdk.areas.WorldstoneChamber + ].includes(Misc.getPlayerArea(DiabloHelper.Leader))) { + throw new Error("END"); + } + return false; + }, Time.minutes(2), 500); + } catch (e) { + let eMsg = e.message ? e.message : e; + console.log(eMsg); + + if (eMsg === "END") { + return true; } - return false; - }, Time.minutes(2), 500); - } catch (e) { - let eMsg = e.message ? e.message : e; - console.log(eMsg); - - if (eMsg === "END") { - return true; } - } - try { - !Common.Diablo.diabloSpawned && (Common.Diablo.diaWaitTime += Time.minutes(1)); - console.log("Attempting to find Diablo"); - Common.Diablo.diabloPrep(); - } catch (error) { - console.log("Diablo wasn't found"); - if (Config.DiabloHelper.RecheckSeals) { - try { - console.log("Rechecking seals"); - Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); - Misc.poll(() => Common.Diablo.diabloSpawned, Time.minutes(2), 500); - Common.Diablo.diabloPrep(); - } catch (e2) { - // + try { + !Common.Diablo.diabloSpawned && (Common.Diablo.diaWaitTime += Time.minutes(1)); + console.log("Attempting to find Diablo"); + Common.Diablo.diabloPrep(); + } catch (error) { + console.log("Diablo wasn't found"); + if (Config.DiabloHelper.RecheckSeals) { + try { + console.log("Rechecking seals"); + Common.Diablo.runSeals(Config.DiabloHelper.SealOrder, Config.DiabloHelper.OpenSeals); + Misc.poll(() => Common.Diablo.diabloSpawned, Time.minutes(2), 500); + Common.Diablo.diabloPrep(); + } catch (e2) { + // + } } } + + Attack.kill(sdk.monsters.Diablo); + Pickit.pickItems(); + } catch (e) { + console.error(e); + } finally { + Common.Diablo.done = true; + removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); } - Attack.kill(sdk.monsters.Diablo); - Pickit.pickItems(); - } catch (e) { - console.error(e); - } finally { - Common.Diablo.done = true; - removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - } - - return true; -} + return true; + }, + sdk.areas.PandemoniumFortress +); diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index 284c37925..733d5fe4c 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -5,74 +5,77 @@ * */ -function Duriel () { - const killDuriel = function () { - let target = Misc.poll(function () { - return Game.getMonster(sdk.monsters.Duriel); - }, 1000, 200); - if (!target) throw new Error("Duriel not found."); +const Duriel = new Runnable( + function Duriel () { + const killDuriel = function () { + let target = Misc.poll(function () { + return Game.getMonster(sdk.monsters.Duriel); + }, 1000, 200); + if (!target) throw new Error("Duriel not found."); - if (Config.MFLeader && Pather.makePortal()) { - say("kill " + sdk.monsters.Duriel); - } + if (Config.MFLeader && Pather.makePortal()) { + say("kill " + sdk.monsters.Duriel); + } - for (let i = 0; i < 300 && target.attackable; i += 1) { - ClassAttack.doAttack(target); - target.distance <= 10 && Pather.moveTo(22638, me.y < target.y ? 15722 : 15693); - } + for (let i = 0; i < 300 && target.attackable; i += 1) { + ClassAttack.doAttack(target); + target.distance <= 10 && Pather.moveTo(22638, me.y < target.y ? 15722 : 15693); + } - return target.dead; - }; + return target.dead; + }; - if (!me.inArea(sdk.areas.CanyonofMagic)) { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CanyonofMagic); - } + if (!me.inArea(sdk.areas.CanyonofMagic)) { + Town.doChores(); + Pather.useWaypoint(sdk.areas.CanyonofMagic); + } - Precast.doPrecast(true); + Precast.doPrecast(true); - if (!Pather.moveToExit(getRoom().correcttomb, true)) { - throw new Error("Failed to move to Tal Rasha's Tomb"); - } - /** @type {ObjectUnit} */ - let lairEntrance = null; - if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricStaffHolder, - { offX: -11, offY: 3, callback: function () { - lairEntrance = Game.getObject(sdk.objects.PortaltoDurielsLair); - return lairEntrance && lairEntrance.distance < 20; - } })) { - throw new Error("Failed to move to Orifice"); - } + if (!Pather.moveToExit(getRoom().correcttomb, true)) { + throw new Error("Failed to move to Tal Rasha's Tomb"); + } + /** @type {ObjectUnit} */ + let lairEntrance = null; + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricStaffHolder, + { offX: -11, offY: 3, callback: function () { + lairEntrance = Game.getObject(sdk.objects.PortaltoDurielsLair); + return lairEntrance && lairEntrance.distance < 20; + } })) { + throw new Error("Failed to move to Orifice"); + } - // me.hardcore && !me.sorceress && Attack.clear(5); - if (lairEntrance && Skill.useTK(lairEntrance)) { - if (lairEntrance.distance > 20) { - Attack.getIntoPosition(lairEntrance, 20, sdk.collision.LineOfSight); + // me.hardcore && !me.sorceress && Attack.clear(5); + if (lairEntrance && Skill.useTK(lairEntrance)) { + if (lairEntrance.distance > 20) { + Attack.getIntoPosition(lairEntrance, 20, sdk.collision.LineOfSight); + } + Misc.poll(function () { + Packet.telekinesis(lairEntrance) && delay(100); + return me.inArea(sdk.areas.DurielsLair); + }, 1000, 200); } - Misc.poll(function () { - Packet.telekinesis(lairEntrance) && delay(100); - return me.inArea(sdk.areas.DurielsLair); - }, 1000, 200); - } - let [type, id, target] = [ - sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair - ]; + let [type, id, target] = [ + sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair + ]; - if (!me.inArea(sdk.areas.DurielsLair) - && !Pather.useUnit(type, id, target)) { - Attack.clear(10); - Pather.useUnit(type, id, target); - } + if (!me.inArea(sdk.areas.DurielsLair) + && !Pather.useUnit(type, id, target)) { + Attack.clear(10); + Pather.useUnit(type, id, target); + } - if (!me.inArea(sdk.areas.DurielsLair)) { - throw new Error("Failed to move to Duriel"); - } + if (!me.inArea(sdk.areas.DurielsLair)) { + throw new Error("Failed to move to Duriel"); + } - me.sorceress && me.classic - ? killDuriel() - : Attack.kill(sdk.monsters.Duriel); - Pickit.pickItems(); + me.sorceress && me.classic + ? killDuriel() + : Attack.kill(sdk.monsters.Duriel); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.CanyonofMagic +); diff --git a/d2bs/kolbot/libs/scripts/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js index 015aebce8..4458b75d3 100644 --- a/d2bs/kolbot/libs/scripts/Eldritch.js +++ b/d2bs/kolbot/libs/scripts/Eldritch.js @@ -5,42 +5,45 @@ * */ -function Eldritch () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); - let { x, y } = me; - Pather.moveTo(3745, 5084); - Attack.kill(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); - - try { - // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); - // Could this be causing crashes here? - if (Config.Eldritch.OpenChest - && Pather.moveNearPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 10)) { - Misc.openChest(sdk.objects.FrigidHighlandsChest) && Pickit.pickItems(); - // check distance from current location to shenk and if far tp to town and use wp instead - if ([x, y].distance > 120 || !Pather.canTeleport()) { - Town.goToTown() && Pather.useWaypoint(sdk.areas.FrigidHighlands); +const Eldritch = new Runnable( + function Eldritch () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); + let { x, y } = me; + Pather.moveTo(3745, 5084); + Attack.kill(getLocaleString(sdk.locale.monsters.EldritchtheRectifier)); + + try { + // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); + // Could this be causing crashes here? + if (Config.Eldritch.OpenChest + && Pather.moveNearPreset(sdk.areas.FrigidHighlands, sdk.unittype.Object, sdk.objects.LargeSparklyChest, 10)) { + Misc.openChest(sdk.objects.FrigidHighlandsChest) && Pickit.pickItems(); + // check distance from current location to shenk and if far tp to town and use wp instead + if ([x, y].distance > 120 || !Pather.canTeleport()) { + Town.goToTown() && Pather.useWaypoint(sdk.areas.FrigidHighlands); + } } + } catch (e) { + console.warn("(Eldritch) :: Failed to open chest. " + e); } - } catch (e) { - console.warn("(Eldritch) :: Failed to open chest. " + e); - } - try { - if (Config.Eldritch.KillShenk && Pather.moveToExit(sdk.areas.BloodyFoothills, false) && Pather.moveTo(3876, 5130)) { - Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + try { + if (Config.Eldritch.KillShenk && Pather.moveToExit(sdk.areas.BloodyFoothills, false) && Pather.moveTo(3876, 5130)) { + Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + } + } catch (e) { + console.warn("(Eldritch) :: Failed to Kill Shenk. " + e); } - } catch (e) { - console.warn("(Eldritch) :: Failed to Kill Shenk. " + e); - } - if (Config.Eldritch.KillDacFarren - && Pather.moveNearPreset(sdk.areas.BloodyFoothills, sdk.unittype.Monster, sdk.monsters.preset.DacFarren, 10) - && Pather.moveTo(4478, 5108)) { - Attack.kill(getLocaleString(sdk.locale.monsters.DacFarren)); - } + if (Config.Eldritch.KillDacFarren + && Pather.moveNearPreset(sdk.areas.BloodyFoothills, sdk.unittype.Monster, sdk.monsters.preset.DacFarren, 10) + && Pather.moveTo(4478, 5108)) { + Attack.kill(getLocaleString(sdk.locale.monsters.DacFarren)); + } - return true; -} + return true; + }, + sdk.areas.FrigidHighlands +); diff --git a/d2bs/kolbot/libs/scripts/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js index b4f136180..3f94929af 100644 --- a/d2bs/kolbot/libs/scripts/Endugu.js +++ b/d2bs/kolbot/libs/scripts/Endugu.js @@ -5,17 +5,26 @@ * */ -function Endugu () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FlayerJungle); - Precast.doPrecast(true); +const Endugu = new Runnable( + function Endugu () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.FlayerJungle); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3], true) - || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsBrainChest)) { - throw new Error("Failed to move to Endugu"); - } + const exits = [ + sdk.areas.FlayerDungeonLvl1, + sdk.areas.FlayerDungeonLvl2, + sdk.areas.FlayerDungeonLvl3 + ]; - Attack.kill(getLocaleString(sdk.locale.monsters.WitchDoctorEndugu)); + if (!Pather.moveToExit(exits, true) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.KhalimsBrainChest)) { + throw new Error("Failed to move to Endugu"); + } - return true; -} + Attack.kill(getLocaleString(sdk.locale.monsters.WitchDoctorEndugu)); + + return true; + }, + sdk.areas.FlayerJungle +); diff --git a/d2bs/kolbot/libs/scripts/Eyeback.js b/d2bs/kolbot/libs/scripts/Eyeback.js index 915de3fcf..4bb800610 100644 --- a/d2bs/kolbot/libs/scripts/Eyeback.js +++ b/d2bs/kolbot/libs/scripts/Eyeback.js @@ -5,16 +5,19 @@ * */ -function Eyeback () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArreatPlateau); - Precast.doPrecast(true); +const Eyeback = new Runnable( + 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"); - } + if (!Pather.moveToPresetMonster(sdk.areas.FrigidHighlands, sdk.monsters.preset.EyebacktheUnleashed)) { + throw new Error("Failed to move to Eyeback the Unleashed"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.EyebacktheUnleashed)); + Attack.kill(getLocaleString(sdk.locale.monsters.EyebacktheUnleashed)); - return true; -} + return true; + }, + sdk.areas.ArreatPlateau +); diff --git a/d2bs/kolbot/libs/scripts/Fangskin.js b/d2bs/kolbot/libs/scripts/Fangskin.js index b91960d39..1a9cc1087 100644 --- a/d2bs/kolbot/libs/scripts/Fangskin.js +++ b/d2bs/kolbot/libs/scripts/Fangskin.js @@ -5,22 +5,25 @@ * */ -function Fangskin () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.LostCity); - Precast.doPrecast(true); +const Fangskin = new Runnable( + function Fangskin () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.LostCity); + Precast.doPrecast(true); - if (!Pather.moveToExit([ - sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2 - ], true)) { - throw new Error("Failed to move to Fangskin"); - } + if (!Pather.moveToExit([ + sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2 + ], true)) { + throw new Error("Failed to move to Fangskin"); + } - // casters can kill fangskin from the altar spot for better safety - Pather.canTeleport() && Skill.getRange(Config.AttackSkill[1] > 10) && Pather.moveTo(15044, 14045); + // casters can kill fangskin from the altar spot for better safety + Pather.canTeleport() && Skill.getRange(Config.AttackSkill[1] > 10) && Pather.moveTo(15044, 14045); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Fangskin)); - Pickit.pickItems(); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Fangskin)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.LostCity +); diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 72c3654da..d3b428e1e 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -65,965 +65,967 @@ * */ -function Follower () { - const QuestData = require("../core/GameData/QuestData"); - /** @type {Map} */ - const areas = new Map(); - /** @type {Set} */ - const _players = new Set(); - /** @type {Array} */ - const actions = []; - /** @type {Set} */ - const commanders = new Set(); - Config.Leader && commanders.add(Config.Leader); - let [allowSay, attack, openContainers, stop] = [true, true, true, false]; - - /** @type {Map _actions.set(key, () => { - Pather.teleport = !Pather.teleport; - announce("Teleport " + (Pather.teleport ? "on" : "off")); - })); - ["tele off", (me.name + " tele off")] - .forEach(key => _actions.set(key, () => { - Pather.teleport = false; - announce("Teleport off."); - })); - ["tele on", (me.name + " tele on")] - .forEach(key => _actions.set(key, () => { - Pather.teleport = true; - announce("Teleport on."); - })); - ["a", (me.name + " a")] - .forEach(key => _actions.set(key, () => { - attack = !attack; - announce("Attack " + (attack ? "on" : "off")); - })); - ["aon", (me.name + " aon")] - .forEach(key => _actions.set(key, () => { - attack = true; - announce("Attack on."); - })); - ["aoff", (me.name + " aoff")] - .forEach(key => _actions.set(key, () => { - attack = false; - announce("Attack off."); - })); - ["s", (me.name + " s")] - .forEach(key => _actions.set(key, () => { - stop = !stop; - announce((stop ? "Stopping." : "Resuming.")); - })); - ["quiet", (me.name + " quiet")] - .forEach(key => _actions.set(key, () => { - allowSay = !allowSay; - console.log("Allow say: " + allowSay); - })); - - return _actions; - })(); - - /** @type {Map _actions.set(key, () => { - Packet.flash(me.gid, me.getPingDelay()); - })); - ["quit", (me.name + " quit")] - .forEach(key => _actions.set(key, () => { - quit(); - })); - ["r", (me.name + " r")] - .forEach(key => _actions.set(key, () => { - revive(); - })); - ["move", (me.name + " move")] - .forEach(key => _actions.set(key, () => { - let coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); - Pather.moveTo(coord.x, coord.y); - })); - ["pre", (me.name + " pre")] - .forEach(key => _actions.set(key, () => { - Precast.doPrecast(true); - })); - ["bo", (me.name + " bo")] - .forEach(key => _actions.set(key, () => { - !me.inTown && Precast.doPrecast(true); - })); - ["h", (me.name + " h")] - .forEach(key => _actions.set(key, () => { - !me.inTown && Skill.cast(sdk.skills.Howl); - })); - - return _actions; - })(); - - /** @type {Map _actions.set(key, () => { - if (me.inArea(sdk.areas.MooMooFarm)) return; - Town.goToTown(1); - Town.move("portalspot"); - if (!Pather.usePortal(sdk.areas.MooMooFarm)) { - announce("Failed to enter red cow portal."); - } - })); - ["wp", (me.name + " wp")] - .forEach(key => _actions.set(key, () => { - if (me.inTown) return; - if (getWaypoint(Pather.wpAreas.indexOf(me.area))) return; - if (!Pather.wpAreas.includes(me.area)) return; - if (Pather.getWP(me.area)) { - announce("Got Wp in " + getAreaName(me.area)); - } - })); - ["c", (me.name + " c")] - .forEach(key => _actions.set(key, () => { - !me.inTown && Town.getCorpse(); - })); - ["p", (me.name + " p")] - .forEach(key => _actions.set(key, () => { - announce("!Picking items."); - Pickit.pickItems(); - openContainers && Misc.openChests(20); - announce("!Done picking."); - })); - ["1", (me.name + " 1")] - .forEach(key => _actions.set(key, () => { - if (me.inTown && Leader.partyUnit.inTown && Misc.getPlayerAct(Config.Leader) !== me.act) { - announce("Going to leader's town."); - Town.goToTown(Misc.getPlayerAct(Config.Leader)); - Town.move("portalspot"); - } else if (me.inTown) { - announce("Going outside."); - Town.goToTown(Misc.getPlayerAct(Config.Leader)); +const Follower = new Runnable( + function Follower () { + const QuestData = require("../core/GameData/QuestData"); + /** @type {Map} */ + const areas = new Map(); + /** @type {Set} */ + const _players = new Set(); + /** @type {Array} */ + const actions = []; + /** @type {Set} */ + const commanders = new Set(); + Config.Leader && commanders.add(Config.Leader); + let [allowSay, attack, openContainers, stop] = [true, true, true, false]; + + /** @type {Map _actions.set(key, () => { + Pather.teleport = !Pather.teleport; + announce("Teleport " + (Pather.teleport ? "on" : "off")); + })); + ["tele off", (me.name + " tele off")] + .forEach(key => _actions.set(key, () => { + Pather.teleport = false; + announce("Teleport off."); + })); + ["tele on", (me.name + " tele on")] + .forEach(key => _actions.set(key, () => { + Pather.teleport = true; + announce("Teleport on."); + })); + ["a", (me.name + " a")] + .forEach(key => _actions.set(key, () => { + attack = !attack; + announce("Attack " + (attack ? "on" : "off")); + })); + ["aon", (me.name + " aon")] + .forEach(key => _actions.set(key, () => { + attack = true; + announce("Attack on."); + })); + ["aoff", (me.name + " aoff")] + .forEach(key => _actions.set(key, () => { + attack = false; + announce("Attack off."); + })); + ["s", (me.name + " s")] + .forEach(key => _actions.set(key, () => { + stop = !stop; + announce((stop ? "Stopping." : "Resuming.")); + })); + ["quiet", (me.name + " quiet")] + .forEach(key => _actions.set(key, () => { + allowSay = !allowSay; + console.log("Allow say: " + allowSay); + })); + + return _actions; + })(); + + /** @type {Map _actions.set(key, () => { + Packet.flash(me.gid, me.getPingDelay()); + })); + ["quit", (me.name + " quit")] + .forEach(key => _actions.set(key, () => { + quit(); + })); + ["r", (me.name + " r")] + .forEach(key => _actions.set(key, () => { + revive(); + })); + ["move", (me.name + " move")] + .forEach(key => _actions.set(key, () => { + let coord = CollMap.getRandCoordinate(me.x, -5, 5, me.y, -5, 5); + Pather.moveTo(coord.x, coord.y); + })); + ["pre", (me.name + " pre")] + .forEach(key => _actions.set(key, () => { + Precast.doPrecast(true); + })); + ["bo", (me.name + " bo")] + .forEach(key => _actions.set(key, () => { + !me.inTown && Precast.doPrecast(true); + })); + ["h", (me.name + " h")] + .forEach(key => _actions.set(key, () => { + !me.inTown && Skill.cast(sdk.skills.Howl); + })); + + return _actions; + })(); + + /** @type {Map _actions.set(key, () => { + if (me.inArea(sdk.areas.MooMooFarm)) return; + Town.goToTown(1); Town.move("portalspot"); + if (!Pather.usePortal(sdk.areas.MooMooFarm)) { + announce("Failed to enter red cow portal."); + } + })); + ["wp", (me.name + " wp")] + .forEach(key => _actions.set(key, () => { + if (me.inTown) return; + if (getWaypoint(Pather.wpAreas.indexOf(me.area))) return; + if (!Pather.wpAreas.includes(me.area)) return; + if (Pather.getWP(me.area)) { + announce("Got Wp in " + getAreaName(me.area)); + } + })); + ["c", (me.name + " c")] + .forEach(key => _actions.set(key, () => { + !me.inTown && Town.getCorpse(); + })); + ["p", (me.name + " p")] + .forEach(key => _actions.set(key, () => { + announce("!Picking items."); + Pickit.pickItems(); + openContainers && Misc.openChests(20); + announce("!Done picking."); + })); + ["1", (me.name + " 1")] + .forEach(key => _actions.set(key, () => { + if (me.inTown && Leader.partyUnit.inTown && Misc.getPlayerAct(Config.Leader) !== me.act) { + announce("Going to leader's town."); + Town.goToTown(Misc.getPlayerAct(Config.Leader)); + Town.move("portalspot"); + } else if (me.inTown) { + announce("Going outside."); + Town.goToTown(Misc.getPlayerAct(Config.Leader)); + Town.move("portalspot"); - if (!Pather.usePortal(null, Leader.partyUnit.name)) { - if (!checkExit(Leader.partyUnit, Leader.partyUnit.area)) { - return; + if (!Pather.usePortal(null, Leader.partyUnit.name)) { + if (!checkExit(Leader.partyUnit, Leader.partyUnit.area)) { + return; + } } - } - let _timeout = getTickCount() + Time.minutes(2); - while (!Misc.getPlayerUnit(Config.Leader) && !me.dead) { - if (getTickCount() > _timeout) { - announce("Leader not found."); - Town.goToTown(); - break; + let _timeout = getTickCount() + Time.minutes(2); + while (!Misc.getPlayerUnit(Config.Leader) && !me.dead) { + if (getTickCount() > _timeout) { + announce("Leader not found."); + Town.goToTown(); + break; + } + Attack.clear(10); + delay(200); } - Attack.clear(10); - delay(200); } + })); + ["2", (me.name + " 2")] + .forEach(key => _actions.set(key, () => { + if (!me.inTown) { + delay(150); + announce("Going to town."); + getUnits(sdk.unittype.Object) + .filter(unit => unit.classid === sdk.objects.BluePortal + && unit.area === me.area && [Leader.partyUnit.name, me.name].includes(unit.getParent())) + .sort((a, b) => a.distance - b.distance) + .some(portal => Pather.usePortal(null, null, portal)); + // Pather.usePortal(null, Leader.unit.name) || Pather.usePortal(sdk.areas.townOf(me.area)); + } + })); + ["3", (me.name + " 3")] + .forEach(key => _actions.set(key, () => { + if (!me.inTown) return; + announce("Running town chores"); + Town.doChores(); + Town.move("portalspot"); + announce("Ready"); + })); + ["a2", "a3", "a4", "a5"] + .forEach((key, index) => { + _actions.set(key, () => { changeAct(index + 2); }); + _actions.set(me.name + " " + key, () => { changeAct(index + 2); }); + }); + _actions.set(me.name + " tp", () => { + if (!Pather.makePortal()) { + announce("No TP scrolls or tomes."); } - })); - ["2", (me.name + " 2")] - .forEach(key => _actions.set(key, () => { - if (!me.inTown) { - delay(150); - announce("Going to town."); - getUnits(sdk.unittype.Object) - .filter(unit => unit.classid === sdk.objects.BluePortal - && unit.area === me.area && [Leader.partyUnit.name, me.name].includes(unit.getParent())) - .sort((a, b) => a.distance - b.distance) - .some(portal => Pather.usePortal(null, null, portal)); - // Pather.usePortal(null, Leader.unit.name) || Pather.usePortal(sdk.areas.townOf(me.area)); - } - })); - ["3", (me.name + " 3")] - .forEach(key => _actions.set(key, () => { - if (!me.inTown) return; - announce("Running town chores"); - Town.doChores(); - Town.move("portalspot"); - announce("Ready"); - })); - ["a2", "a3", "a4", "a5"] - .forEach((key, index) => { - _actions.set(key, () => { changeAct(index + 2); }); - _actions.set(me.name + " " + key, () => { changeAct(index + 2); }); }); - _actions.set(me.name + " tp", () => { - if (!Pather.makePortal()) { - announce("No TP scrolls or tomes."); - } - }); - return _actions; - })(); + return _actions; + })(); - /** @type {Map { - Pather.journeyTo(sdk.areas.DenofEvil); - } - ], - [ - "bloodraven", () => { - Pather.journeyTo(sdk.areas.BurialGrounds); - Pather.moveNearPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven, 15); - } - ], - [ - "tree", () => { - Pather.journeyTo(sdk.areas.DarkWood); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5); - } - ], - [ - "trist", () => { - Pather.journeyTo(sdk.areas.Tristram); - } - ], - [ - "pit", () => { - Pather.journeyTo(sdk.areas.PitLvl1); - } - ], - [ - "countess", () => { - Pather.journeyTo(sdk.areas.TowerCellarLvl5); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); - } - ], - [ - "andy", () => { - Pather.journeyTo(sdk.areas.CatacombsLvl4); - } - ], - [ - "rad", () => { - Pather.journeyTo(sdk.areas.A2SewersLvl3); - Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricScrollChest, 5); - } - ], - [ - "cube", () => { - Pather.journeyTo(sdk.areas.HallsoftheDeadLvl3); - Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricCubeChest, 15); - } - ], - [ - "amulet", () => { - Pather.journeyTo(sdk.areas.ClawViperTempleLvl2); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ViperAmuletChest); - } - ], - [ - "staff", () => { - Pather.journeyTo(sdk.areas.HallsoftheDeadLvl3); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); - } - ], - [ - "summoner", () => { - Pather.journeyTo(sdk.areas.ArcaneSanctuary); - Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.Journal, 15); - } - ], - [ - "staff-altar", () => { - Pather.journeyTo(sdk.areas.CanyonofMagic); - Pather.moveToExit(getRoom().correcttomb, true); - Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricStaffHolder, 5); - } - ], - [ - "duriel", () => { - Pather.journeyTo(sdk.areas.DurielsLair); - } - ], - [ - "eye", () => { - Pather.journeyTo(sdk.areas.SpiderCavern); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsEyeChest); - } - ], - [ - "brain", () => { - Pather.journeyTo(sdk.areas.FlayerDungeonLvl3); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest); - } - ], - [ - "heart", () => { - Pather.journeyTo(sdk.areas.A3SewersLvl2); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsHeartChest); - } - ], - [ - "council", () => { - Pather.journeyTo(sdk.areas.Travincal); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.CompellingOrb); - } - ], - [ - "meph", () => { - Pather.journeyTo(sdk.areas.DuranceofHateLvl3); - Pather.moveTo(17590, 8068); - } - ], - [ - "izual", () => { - Pather.journeyTo(sdk.areas.PlainsofDespair); - Pather.moveNearPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Izual, 20); - } - ], - [ - "forge", () => { - Pather.journeyTo(sdk.areas.RiverofFlame); - Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge, 5); - } - ], - [ - "chaos", () => { - Pather.journeyTo(sdk.areas.ChaosSanctuary); - } - ], - [ - "viz", () => { - Pather.journeyTo(sdk.areas.ChaosSanctuary); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealVizier); - } - ], - [ - "seis", () => { - Pather.journeyTo(sdk.areas.ChaosSanctuary); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealSeis); - } - ], - [ - "infector", () => { - Pather.journeyTo(sdk.areas.ChaosSanctuary); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealInfector); - } - ], - [ - "anya", () => { - Pather.journeyTo(sdk.areas.FrozenRiver); - Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, 5); - } - ], - [ - "ancients", () => { - Pather.journeyTo(sdk.areas.ArreatSummit); - } - ], - [ - "nith", () => { - Pather.journeyTo(sdk.areas.HallsofVaught); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform); - } - ], - [ - "throne", () => { - Pather.journeyTo(sdk.areas.ThroneofDestruction); - } - ], - ]); - - /** - * @todo allow user to use skill name and try to match it to skill id - */ - const skillsMap = (function () { - const _skills = new Map(); - - for (let value of Object.values(sdk.skills)) { - if (typeof value === "number") { - _skills.set(getSkillById(value), value); - } - } + /** @type {Map { + Pather.journeyTo(sdk.areas.DenofEvil); + } + ], + [ + "bloodraven", () => { + Pather.journeyTo(sdk.areas.BurialGrounds); + Pather.moveNearPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.BloodRaven, 15); + } + ], + [ + "tree", () => { + Pather.journeyTo(sdk.areas.DarkWood); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5); + } + ], + [ + "trist", () => { + Pather.journeyTo(sdk.areas.Tristram); + } + ], + [ + "pit", () => { + Pather.journeyTo(sdk.areas.PitLvl1); + } + ], + [ + "countess", () => { + Pather.journeyTo(sdk.areas.TowerCellarLvl5); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.SuperChest); + } + ], + [ + "andy", () => { + Pather.journeyTo(sdk.areas.CatacombsLvl4); + } + ], + [ + "rad", () => { + Pather.journeyTo(sdk.areas.A2SewersLvl3); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricScrollChest, 5); + } + ], + [ + "cube", () => { + Pather.journeyTo(sdk.areas.HallsoftheDeadLvl3); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricCubeChest, 15); + } + ], + [ + "amulet", () => { + Pather.journeyTo(sdk.areas.ClawViperTempleLvl2); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ViperAmuletChest); + } + ], + [ + "staff", () => { + Pather.journeyTo(sdk.areas.HallsoftheDeadLvl3); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); + } + ], + [ + "summoner", () => { + Pather.journeyTo(sdk.areas.ArcaneSanctuary); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.Journal, 15); + } + ], + [ + "staff-altar", () => { + Pather.journeyTo(sdk.areas.CanyonofMagic); + Pather.moveToExit(getRoom().correcttomb, true); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.HoradricStaffHolder, 5); + } + ], + [ + "duriel", () => { + Pather.journeyTo(sdk.areas.DurielsLair); + } + ], + [ + "eye", () => { + Pather.journeyTo(sdk.areas.SpiderCavern); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsEyeChest); + } + ], + [ + "brain", () => { + Pather.journeyTo(sdk.areas.FlayerDungeonLvl3); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsBrainChest); + } + ], + [ + "heart", () => { + Pather.journeyTo(sdk.areas.A3SewersLvl2); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.KhalimsHeartChest); + } + ], + [ + "council", () => { + Pather.journeyTo(sdk.areas.Travincal); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.CompellingOrb); + } + ], + [ + "meph", () => { + Pather.journeyTo(sdk.areas.DuranceofHateLvl3); + Pather.moveTo(17590, 8068); + } + ], + [ + "izual", () => { + Pather.journeyTo(sdk.areas.PlainsofDespair); + Pather.moveNearPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Izual, 20); + } + ], + [ + "forge", () => { + Pather.journeyTo(sdk.areas.RiverofFlame); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HellForge, 5); + } + ], + [ + "chaos", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + } + ], + [ + "viz", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealVizier); + } + ], + [ + "seis", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealSeis); + } + ], + [ + "infector", () => { + Pather.journeyTo(sdk.areas.ChaosSanctuary); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.DiabloSealInfector); + } + ], + [ + "anya", () => { + Pather.journeyTo(sdk.areas.FrozenRiver); + Pather.moveNearPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, 5); + } + ], + [ + "ancients", () => { + Pather.journeyTo(sdk.areas.ArreatSummit); + } + ], + [ + "nith", () => { + Pather.journeyTo(sdk.areas.HallsofVaught); + Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.NihlathaksPlatform); + } + ], + [ + "throne", () => { + Pather.journeyTo(sdk.areas.ThroneofDestruction); + } + ], + ]); - return _skills; - })(); + /** + * @todo allow user to use skill name and try to match it to skill id + */ + const skillsMap = (function () { + const _skills = new Map(); - const announce = function (msg = "") { - if (!allowSay) return; - say(msg); - }; + for (let value of Object.values(sdk.skills)) { + if (typeof value === "number") { + _skills.set(getSkillById(value), value); + } + } - const revive = function () { - while (!me.inTown) { - me.revive(); - delay(1000); - } + return _skills; + })(); - Town.move("portalspot"); - announce("I'm alive!"); - }; + const announce = function (msg = "") { + if (!allowSay) return; + say(msg); + }; - const playerInGame = function (name = "") { - if (!name) return false; - if (_players.has(name.toLowerCase())) return true; - let player = getParty(); + const revive = function () { + while (!me.inTown) { + me.revive(); + delay(1000); + } - if (player) { - do { - _players.add(player.name.toLowerCase()); - } while (player.getNext()); - } - return _players.has(name.toLowerCase()); - }; - - /** - * Change areas to where leader is - * @param {Party} unit - * @param {number} area - * @returns {boolean} - */ - const checkExit = function (unit, area) { - if (unit.inTown && me.inTown) return false; - - /** @type {Exit[]} */ - let exits = []; - if (areas.has(me.area)) { - exits = areas.get(me.area).exits; - } else { - let _area = getArea(me.area); - if (_area) { - exits = _area.exits; - areas.set(me.area, _area); + Town.move("portalspot"); + announce("I'm alive!"); + }; + + const playerInGame = function (name = "") { + if (!name) return false; + if (_players.has(name.toLowerCase())) return true; + let player = getParty(); + + if (player) { + do { + _players.add(player.name.toLowerCase()); + } while (player.getNext()); } - } - - for (let exit of exits) { - if (exit.target === area) { - announce("Taking exit to " + getAreaName(exit.target)); - return Pather.moveToExit(area, true); + return _players.has(name.toLowerCase()); + }; + + /** + * Change areas to where leader is + * @param {Party} unit + * @param {number} area + * @returns {boolean} + */ + const checkExit = function (unit, area) { + if (unit.inTown && me.inTown) return false; + + /** @type {Exit[]} */ + let exits = []; + if (areas.has(me.area)) { + exits = areas.get(me.area).exits; + } else { + let _area = getArea(me.area); + if (_area) { + exits = _area.exits; + areas.set(me.area, _area); + } } - } - - if (unit.inTown) { - let wp = Game.getObject("waypoint"); - - if (wp && wp.distance < 30) { - announce("Taking waypoint to " + getAreaName(area)); - return Pather.useWaypoint(area, true); + + for (let exit of exits) { + if (exit.target === area) { + announce("Taking exit to " + getAreaName(exit.target)); + return Pather.moveToExit(area, true); + } } - } - let target = Game.getObject("portal"); + if (unit.inTown) { + let wp = Game.getObject("waypoint"); - if (target) { - do { - if (target.objtype === area) { - announce("Taking portal to " + getAreaName(area)); - return Pather.usePortal(null, null, target); + if (wp && wp.distance < 30) { + announce("Taking waypoint to " + getAreaName(area)); + return Pather.useWaypoint(area, true); } - } while (target.getNext()); - } + } - // Arcane<->Cellar portal - if ((me.inArea(sdk.areas.ArcaneSanctuary) && area === sdk.areas.PalaceCellarLvl3) - || (me.inArea(sdk.areas.PalaceCellarLvl3) && area === sdk.areas.ArcaneSanctuary)) { - announce("Special transit to " + getAreaName(area)); - return Pather.usePortal(null); - } + let target = Game.getObject("portal"); - // Tal-Rasha's tomb->Duriel's lair - if (me.area >= sdk.areas.TalRashasTomb1 - && me.area <= sdk.areas.TalRashasTomb7 - && area === sdk.areas.DurielsLair) { - announce("Special transit to " + getAreaName(area)); - return Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, area); - } - - // durance 3 -> pandemonium fortress - if (me.inArea(sdk.areas.DuranceofHateLvl3) && area === sdk.areas.PandemoniumFortress) { - announce("Special transit to " + getAreaName(area)); - return Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); - } + if (target) { + do { + if (target.objtype === area) { + announce("Taking portal to " + getAreaName(area)); + return Pather.usePortal(null, null, target); + } + } while (target.getNext()); + } - // Throne->Chamber - if (me.inArea(sdk.areas.ThroneofDestruction) && area === sdk.areas.WorldstoneChamber) { - let wsp = Game.getObject(sdk.objects.WorldstonePortal); + // Arcane<->Cellar portal + if ((me.inArea(sdk.areas.ArcaneSanctuary) && area === sdk.areas.PalaceCellarLvl3) + || (me.inArea(sdk.areas.PalaceCellarLvl3) && area === sdk.areas.ArcaneSanctuary)) { + announce("Special transit to " + getAreaName(area)); + return Pather.usePortal(null); + } - if (wsp) { + // Tal-Rasha's tomb->Duriel's lair + if (me.area >= sdk.areas.TalRashasTomb1 + && me.area <= sdk.areas.TalRashasTomb7 + && area === sdk.areas.DurielsLair) { announce("Special transit to " + getAreaName(area)); - return Pather.usePortal(null, null, wsp); + return Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, area); } - } - return false; - }; + // durance 3 -> pandemonium fortress + if (me.inArea(sdk.areas.DuranceofHateLvl3) && area === sdk.areas.PandemoniumFortress) { + announce("Special transit to " + getAreaName(area)); + return Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); + } - /** - * Talk to a NPC - * @param {string} name - * @returns {boolean} - */ - const talk = function (name) { - try { - if (!me.inTown) throw new Error("I'm not in town!"); - if (typeof name !== "string") throw new Error("No NPC name given."); - Town.npcInteract(name); + // Throne->Chamber + if (me.inArea(sdk.areas.ThroneofDestruction) && area === sdk.areas.WorldstoneChamber) { + let wsp = Game.getObject(sdk.objects.WorldstonePortal); - return true; - } catch (e) { - console.error(e); - announce( - (typeof e === "object" && e.message - ? e.message - : typeof e === "string" - ? e - : "Failed to talk to " + name) - ); + if (wsp) { + announce("Special transit to " + getAreaName(area)); + return Pather.usePortal(null, null, wsp); + } + } return false; - } finally { - Town.move("portalspot"); - } - }; - - /** - * Change act after completing last act quest - * @param {number} act - * @returns {boolean} - */ - const changeAct = function (act) { - let preArea = me.area; - - if (me.area >= sdk.areas.townOfAct(act)) { - announce("My current act is higher than " + act); - return false; - } + }; + + /** + * Talk to a NPC + * @param {string} name + * @returns {boolean} + */ + const talk = function (name) { + try { + if (!me.inTown) throw new Error("I'm not in town!"); + if (typeof name !== "string") throw new Error("No NPC name given."); + Town.npcInteract(name); - const npcTravel = new Map([ - [1, ["Warriv", sdk.areas.RogueEncampment]], - [2, [(me.act === 1 ? "Warriv" : "Meshif"), sdk.areas.LutGholein]], - [3, ["Meshif", sdk.areas.KurastDocktown]], - [4, ["", sdk.areas.PandemoniumFortress]], - [5, ["Tyrael", sdk.areas.Harrogath]], - ]); + return true; + } catch (e) { + console.error(e); + announce( + (typeof e === "object" && e.message + ? e.message + : typeof e === "string" + ? e + : "Failed to talk to " + name) + ); + + return false; + } finally { + Town.move("portalspot"); + } + }; + + /** + * Change act after completing last act quest + * @param {number} act + * @returns {boolean} + */ + const changeAct = function (act) { + let preArea = me.area; + + if (me.area >= sdk.areas.townOfAct(act)) { + announce("My current act is higher than " + act); + return false; + } - const preCheck = new Map([ - [ - 2, - () => QuestData.get(sdk.quest.id.SistersToTheSlaughter).complete(true) - ], - [ - 3, - () => { - if (QuestData.get(sdk.quest.id.TheSevenTombs).complete()) return true; - if (!QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/)) { - Town.npcInteract("Jerhyn"); - if (me.getTpTool()) { - Pather.moveToExit(sdk.areas.HaremLvl1, true); - Pather.usePortal(null) || Pather.makePortal(true); + const npcTravel = new Map([ + [1, ["Warriv", sdk.areas.RogueEncampment]], + [2, [(me.act === 1 ? "Warriv" : "Meshif"), sdk.areas.LutGholein]], + [3, ["Meshif", sdk.areas.KurastDocktown]], + [4, ["", sdk.areas.PandemoniumFortress]], + [5, ["Tyrael", sdk.areas.Harrogath]], + ]); + + const preCheck = new Map([ + [ + 2, + () => QuestData.get(sdk.quest.id.SistersToTheSlaughter).complete(true) + ], + [ + 3, + () => { + if (QuestData.get(sdk.quest.id.TheSevenTombs).complete()) return true; + if (!QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/)) { + Town.npcInteract("Jerhyn"); + if (me.getTpTool()) { + Pather.moveToExit(sdk.areas.HaremLvl1, true); + Pather.usePortal(null) || Pather.makePortal(true); + } } + return QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/); } - return QuestData.get(sdk.quest.id.TheSevenTombs).checkState(4/*talked to jerhyn*/); - } - ], - [ - 4, - () => { - if (me.inTown) { - if (!QuestData.get(sdk.quest.id.TheBlackenedTemple).complete()) { - Town.npcInteract("Cain"); + ], + [ + 4, + () => { + if (me.inTown) { + if (!QuestData.get(sdk.quest.id.TheBlackenedTemple).complete()) { + Town.npcInteract("Cain"); + } + Town.move("portalspot"); + Pather.usePortal(sdk.areas.DuranceofHateLvl3, null); } - Town.move("portalspot"); - Pather.usePortal(sdk.areas.DuranceofHateLvl3, null); + return me.inArea(sdk.areas.DuranceofHateLvl3); } - return me.inArea(sdk.areas.DuranceofHateLvl3); - } - ], - [ - 5, - () => { - if (!QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/)) { - Town.npcInteract("Tyrael"); + ], + [ + 5, + () => { + if (!QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/)) { + Town.npcInteract("Tyrael"); + } + return QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/); } - return QuestData.get(sdk.quest.id.TerrorsEnd).checkState(9/*talked to tyrael*/); - } - ] - ]); + ] + ]); - if (!preCheck.get(act)()) { - announce("Failed act " + act + " precheck"); - return false; - } + if (!preCheck.get(act)()) { + announce("Failed act " + act + " precheck"); + return false; + } - if (act !== 4) { - let [npc, loc] = npcTravel.get(act); - 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 (act !== 4) { + let [npc, loc] = npcTravel.get(act); + 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++) { - new PacketBuilder() - .byte(sdk.packets.send.EntityAction) - .dword(0) - .dword(npcUnit.gid) - .dword(loc) - .send(); - delay(1000); - - if (me.act === act) { - break; + if (npcUnit) { + for (let i = 0; i < 5; i++) { + new PacketBuilder() + .byte(sdk.packets.send.EntityAction) + .dword(0) + .dword(npcUnit.gid) + .dword(loc) + .send(); + delay(1000); + + if (me.act === act) { + break; + } } } - } - } else { - if (me.inArea(sdk.areas.DuranceofHateLvl3)) { - let target = Game.getObject(sdk.objects.RedPortalToAct4); - target && Pather.moveTo(target.x - 3, target.y - 1); + } else { + if (me.inArea(sdk.areas.DuranceofHateLvl3)) { + let target = Game.getObject(sdk.objects.RedPortalToAct4); + target && Pather.moveTo(target.x - 3, target.y - 1); - Pather.usePortal(null); + Pather.usePortal(null); + } } - } - while (!me.gameReady) { - delay(100); - } + while (!me.gameReady) { + delay(100); + } - if (me.area === preArea) { - me.cancel(); - Town.move("portalspot"); - announce("Act change failed."); + if (me.area === preArea) { + me.cancel(); + Town.move("portalspot"); + announce("Act change failed."); - return false; - } + return false; + } - Town.move("portalspot"); - announce("Act change successful."); - act === 2 && announce("Don't forget to talk to Drognan after getting the Viper Amulet!"); + Town.move("portalspot"); + announce("Act change successful."); + act === 2 && announce("Don't forget to talk to Drognan after getting the Viper Amulet!"); - return true; - }; + return true; + }; - const pickPotions = function (range = 5) { - if (me.dead) return false; + const pickPotions = function (range = 5) { + if (me.dead) return false; - me.clearBelt(); + me.clearBelt(); - while (!me.idle) { - delay(40); - } + while (!me.idle) { + delay(40); + } - let pickList = []; - let item = Game.getItem(); + let pickList = []; + let item = Game.getItem(); - if (item) { - do { - if (item.onGroundOrDropping && item.itemType >= sdk.items.type.HealingPotion - && item.itemType <= sdk.items.type.RejuvPotion && item.distance <= range) { - pickList.push(copyUnit(item)); - } - } while (item.getNext()); - } + if (item) { + do { + if (item.onGroundOrDropping && item.itemType >= sdk.items.type.HealingPotion + && item.itemType <= sdk.items.type.RejuvPotion && item.distance <= range) { + pickList.push(copyUnit(item)); + } + } while (item.getNext()); + } - pickList.sort(Pickit.sortItems); + pickList.sort(Pickit.sortItems); - while (pickList.length > 0) { - item = pickList.shift(); + while (pickList.length > 0) { + item = pickList.shift(); - if (item && copyUnit(item).x) { - let status = Pickit.checkItem(item).result; + if (item && copyUnit(item).x) { + let status = Pickit.checkItem(item).result; - if (status && Pickit.canPick(item)) { - Pickit.pickItem(item, status); + if (status && Pickit.canPick(item)) { + Pickit.pickItem(item, status); + } } } - } - return true; - }; - - /** - * @param {string} nick - * @param {string} msg - */ - const chatEvent = function (nick, msg) { - if (msg && nick === Config.Leader) { - if (toggleActions.has(msg)) { - toggleActions.get(msg)(); - - return; - } else if (quickActions.has(msg)) { - quickActions.get(msg)(); - - return; - } else if (specialActions.has(msg)) { - actions.push(msg); - - return; - } - let piecewise = msg.split(" "); - let who = piecewise.length > 1 && piecewise.first() || ""; - const isForMe = (charClass.includes(who) || who === me.name || who === "all"); + return true; + }; + + /** + * @param {string} nick + * @param {string} msg + */ + const chatEvent = function (nick, msg) { + if (msg && nick === Config.Leader) { + if (toggleActions.has(msg)) { + toggleActions.get(msg)(); + + return; + } else if (quickActions.has(msg)) { + quickActions.get(msg)(); + + return; + } else if (specialActions.has(msg)) { + actions.push(msg); + + return; + } + let piecewise = msg.split(" "); + let who = piecewise.length > 1 && piecewise.first() || ""; + const isForMe = (charClass.includes(who) || who === me.name || who === "all"); - if (me.paladin && isForMe && msg.includes("aura ")) { - let aura = parseInt(msg.split(" ")[2], 10); + if (me.paladin && isForMe && msg.includes("aura ")) { + let aura = parseInt(msg.split(" ")[2], 10); - if (me.getSkill(aura, sdk.skills.subindex.SoftPoints)) { - announce("Active aura is: " + aura); + if (me.getSkill(aura, sdk.skills.subindex.SoftPoints)) { + announce("Active aura is: " + aura); - Config.AttackSkill[2] = aura; - Config.AttackSkill[4] = aura; + Config.AttackSkill[2] = aura; + Config.AttackSkill[4] = aura; - Skill.setSkill(aura, sdk.skills.hand.Right); - } else { - announce("I don't have that aura."); - } - } else if (isForMe && msg.includes("skill ")) { - let skill = parseInt(msg.split(" ")[2], 10); + Skill.setSkill(aura, sdk.skills.hand.Right); + } else { + announce("I don't have that aura."); + } + } else if (isForMe && msg.includes("skill ")) { + let skill = parseInt(msg.split(" ")[2], 10); - if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { - announce("Attack skill is: " + skill); + if (me.getSkill(skill, sdk.skills.subindex.SoftPoints)) { + announce("Attack skill is: " + skill); - Config.AttackSkill[1] = skill; - Config.AttackSkill[3] = skill; + Config.AttackSkill[1] = skill; + Config.AttackSkill[3] = skill; + } else { + announce("I don't have that skill."); + } } else { - announce("I don't have that skill."); - } - } else { - if (who) { - if (isForMe) { - msg = msg.replace(who, "").trim(); - } else if (playerInGame(who)) { - return; + if (who) { + if (isForMe) { + msg = msg.replace(who, "").trim(); + } else if (playerInGame(who)) { + return; + } } + actions.push(msg); } - actions.push(msg); - } - } else if (msg && msg.split(" ")[0] === "leader" && (commanders.has(nick) || !commanders.size)) { - let piece = msg.split(" ")[1]; + } else if (msg && msg.split(" ")[0] === "leader" && (commanders.has(nick) || !commanders.size)) { + let piece = msg.split(" ")[1]; - if (typeof piece === "string") { - commanders.add(piece); - announce("Switching leader to " + piece); + if (typeof piece === "string") { + commanders.add(piece); + announce("Switching leader to " + piece); - Config.Leader = piece; - Leader.partyUnit = Misc.findPlayer(Config.Leader); - Leader.unit = Misc.getPlayerUnit(Config.Leader); + Config.Leader = piece; + Leader.partyUnit = Misc.findPlayer(Config.Leader); + Leader.unit = Misc.getPlayerUnit(Config.Leader); + } } - } - }; - - const gameEvent = function (mode, param1, param2, name1, name2) { - console.log("gameevent", mode, param1, param2, name1, name2); - if (name1 === Config.Leader - && mode === 0x07 - && param1 === 0x02 - && param2 === 0x09) { - recheck = true; - } - }; - - // START - let recheck = false; - addEventListener("chatmsg", chatEvent); - addEventListener("gameevent", gameEvent); - openContainers && Config.OpenChests.enabled && Config.OpenChests.Types.push("all"); - - // Override config values that use TP - Config.TownCheck = false; - Config.TownHP = 0; - Config.TownMP = 0; - const charClass = sdk.player.class.nameOf(me.classid).toLowerCase(); - const Leader = new function () { - /** @type {Party} */ - this.partyUnit = null; - /** @type {Player} */ - this.unit = null; - }; - - Leader.partyUnit = Misc.poll( - () => Misc.findPlayer(Config.Leader), - Time.minutes(3), - Time.seconds(1) - ); - - if (!Leader.partyUnit) { - announce("Leader not found."); - scriptBroadcast("quit"); - - return true; - } + }; + + const gameEvent = function (mode, param1, param2, name1, name2) { + console.log("gameevent", mode, param1, param2, name1, name2); + if (name1 === Config.Leader + && mode === 0x07 + && param1 === 0x02 + && param2 === 0x09) { + recheck = true; + } + }; - announce("Leader found :: " + Leader.partyUnit.name); + // START + let recheck = false; + addEventListener("chatmsg", chatEvent); + addEventListener("gameevent", gameEvent); + openContainers && Config.OpenChests.enabled && Config.OpenChests.Types.push("all"); + + // Override config values that use TP + Config.TownCheck = false; + Config.TownHP = 0; + Config.TownMP = 0; + const charClass = sdk.player.class.nameOf(me.classid).toLowerCase(); + const Leader = new function () { + /** @type {Party} */ + this.partyUnit = null; + /** @type {Player} */ + this.unit = null; + }; + + Leader.partyUnit = Misc.poll( + () => Misc.findPlayer(Config.Leader), + Time.minutes(3), + Time.seconds(1) + ); + + if (!Leader.partyUnit) { + announce("Leader not found."); + scriptBroadcast("quit"); - while (!Misc.inMyParty(Config.Leader)) { - delay(500); - } + return true; + } - announce("Partied."); + announce("Leader found :: " + Leader.partyUnit.name); - if (Leader.partyUnit.inTown && !me.inTown) { - announce("Going to leader's town."); - Town.goToTown(sdk.areas.actOf(Leader.partyUnit.area)); - } - me.inTown && Town.move("portalspot"); - Leader.unit = Misc.getPlayerUnit(Config.Leader); - - // Main Loop - while (true) { - if (recheck) { - if (!Misc.poll(() => Misc.inMyParty(Config.Leader), Time.minutes(1), Time.seconds(1))) { - announce("Leader left party."); - - break; - } - recheck = false; - } - if (me.mode === sdk.player.mode.Dead) { - revive(); + while (!Misc.inMyParty(Config.Leader)) { + delay(500); } - while (stop) { - delay(500); + announce("Partied."); + + if (Leader.partyUnit.inTown && !me.inTown) { + announce("Going to leader's town."); + Town.goToTown(sdk.areas.actOf(Leader.partyUnit.area)); } + me.inTown && Town.move("portalspot"); + Leader.unit = Misc.getPlayerUnit(Config.Leader); + + // Main Loop + while (true) { + if (recheck) { + if (!Misc.poll(() => Misc.inMyParty(Config.Leader), Time.minutes(1), Time.seconds(1))) { + announce("Leader left party."); + + break; + } + recheck = false; + } + if (me.mode === sdk.player.mode.Dead) { + revive(); + } - if (!me.inTown) { - try { - if (!Leader.unit) throw new Error("Leader not found."); - if (!Leader.partyUnit) throw new Error("party unit not found."); - if (me.inArea(Leader.partyUnit.area) && copyUnit(Leader.unit).x) { - if (Leader.unit.distance > 10) { - // Pather.moveToUnit(Leader.unit); - Pather.moveToEx( - Leader.unit.x, Leader.unit.y, { callback: () => ( - Leader.unit && Leader.unit.distance < 10 - ) } - ); - } - } else { - if (!me.inArea(Leader.partyUnit.area) && !me.inTown) { - while (Leader.partyUnit.area === 0) delay(100); - checkExit(Leader.partyUnit, Leader.partyUnit.area); + while (stop) { + delay(500); + } - while (me.area === 0) { - delay(100); + if (!me.inTown) { + try { + if (!Leader.unit) throw new Error("Leader not found."); + if (!Leader.partyUnit) throw new Error("party unit not found."); + if (me.inArea(Leader.partyUnit.area) && copyUnit(Leader.unit).x) { + if (Leader.unit.distance > 10) { + // Pather.moveToUnit(Leader.unit); + Pather.moveToEx( + Leader.unit.x, Leader.unit.y, { callback: () => ( + Leader.unit && Leader.unit.distance < 10 + ) } + ); + } + } else { + if (!me.inArea(Leader.partyUnit.area) && !me.inTown) { + while (Leader.partyUnit.area === 0) delay(100); + checkExit(Leader.partyUnit, Leader.partyUnit.area); + + while (me.area === 0) { + delay(100); + } } } - } - } catch (e) { - console.error(e); - if (Leader.partyUnit) { - if (me.inArea(Leader.partyUnit.area)) { - Pather.moveToEx( - Leader.partyUnit.x, Leader.partyUnit.y, { callback: () => { - Leader.unit = Misc.getPlayerUnit(Config.Leader); - return Leader.unit && Leader.unit.distance < 10; - } } - ); - } else if (Leader.partyUnit.inTown) { - // go back to town if there are there for awhile - if (!Misc.poll( - () => (Leader.unit = Misc.getPlayerUnit(Config.Leader)), - Time.seconds(45), - Time.seconds(1)) - ) { - Town.goToTown(sdk.areas.actOf(Leader.partyUnit.area)); - Town.move("portalspot"); + } catch (e) { + console.error(e); + if (Leader.partyUnit) { + if (me.inArea(Leader.partyUnit.area)) { + Pather.moveToEx( + Leader.partyUnit.x, Leader.partyUnit.y, { callback: () => { + Leader.unit = Misc.getPlayerUnit(Config.Leader); + return Leader.unit && Leader.unit.distance < 10; + } } + ); + } else if (Leader.partyUnit.inTown) { + // go back to town if there are there for awhile + if (!Misc.poll( + () => (Leader.unit = Misc.getPlayerUnit(Config.Leader)), + Time.seconds(45), + Time.seconds(1)) + ) { + Town.goToTown(sdk.areas.actOf(Leader.partyUnit.area)); + Town.move("portalspot"); + } } + } else { + Leader.partyUnit = Misc.findPlayer(Config.Leader); } - } else { - Leader.partyUnit = Misc.findPlayer(Config.Leader); } - } - if (attack) { - // custom attack needs to be done so we can keep track of leader while we attack in break - // early if leader moves on - Attack.clear(20, false, false, false, true); - pickPotions(20); - } + if (attack) { + // custom attack needs to be done so we can keep track of leader while we attack in break + // early if leader moves on + Attack.clear(20, false, false, false, true); + pickPotions(20); + } - if (me.paladin && Config.AttackSkill[2] > 0) { - Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); - } - } else if (!actions.length && getTickCount() - Town.lastChores > Time.minutes(3)) { - // no actions currently, lets do some town chores - if (me.gold > 1000 - && (me.needPotions() || Town.checkScrolls(sdk.items.TomeofTownPortal) < 5)) { - Town.doChores(); + if (me.paladin && Config.AttackSkill[2] > 0) { + Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + } + } else if (!actions.length && getTickCount() - Town.lastChores > Time.minutes(3)) { + // no actions currently, lets do some town chores + if (me.gold > 1000 + && (me.needPotions() || Town.checkScrolls(sdk.items.TomeofTownPortal) < 5)) { + Town.doChores(); + } + Town.getDistance("portalspot") > 5 && Town.move("portalspot"); } - Town.getDistance("portalspot") > 5 && Town.move("portalspot"); - } - if (actions.length) { - let action = actions.shift(); + if (actions.length) { + let action = actions.shift(); - if (specialActions.has(action)) { - specialActions.get(action)(); - } else { - switch (action) { - case "reload": - case me.name + "reload": - scriptBroadcast("reload"); - - return true; - default: - console.log(action); - if (action.includes("talk")) { - talk(action.split(" ")[1]); - } else if (action.includes("chug")) { - let temp = action.toLowerCase().split(" "); - let [, type, amount] = temp; - amount === undefined && (amount = 10); - typeof amount === "string" && (amount = parseInt(amount, 10)); - - if (type === "a" || type === "antidote") { - Town.buyPots(amount, sdk.items.AntidotePotion, true, true); - } else if (type === "t" || type === "thawing") { - Town.buyPots(amount, sdk.items.ThawingPotion, true, true); - } else if (type === "s" || type === "stamina") { - Town.buyPots(amount, sdk.items.StaminaPotion, true, true); - } - } else if (action.includes("taxi")) { - let [, where] = action.split(" "); - console.log(where); - if (where) { - try { - if (taxiMap.has(where)) { - taxiMap.get(where)(); - } else { - let _areaId = parseInt(where, 10); - Pather.journeyTo(_areaId); + if (specialActions.has(action)) { + specialActions.get(action)(); + } else { + switch (action) { + case "reload": + case me.name + "reload": + scriptBroadcast("reload"); + + return true; + default: + console.log(action); + if (action.includes("talk")) { + talk(action.split(" ")[1]); + } else if (action.includes("chug")) { + let temp = action.toLowerCase().split(" "); + let [, type, amount] = temp; + amount === undefined && (amount = 10); + typeof amount === "string" && (amount = parseInt(amount, 10)); + + if (type === "a" || type === "antidote") { + Town.buyPots(amount, sdk.items.AntidotePotion, true, true); + } else if (type === "t" || type === "thawing") { + Town.buyPots(amount, sdk.items.ThawingPotion, true, true); + } else if (type === "s" || type === "stamina") { + Town.buyPots(amount, sdk.items.StaminaPotion, true, true); + } + } else if (action.includes("taxi")) { + let [, where] = action.split(" "); + console.log(where); + if (where) { + try { + if (taxiMap.has(where)) { + taxiMap.get(where)(); + } else { + let _areaId = parseInt(where, 10); + Pather.journeyTo(_areaId); + } + Pather.makePortal(); + } catch (e) { + console.error(e); + announce("Failed to taxi to " + where); + Town.goToTown(); } - Pather.makePortal(); - } catch (e) { - console.error(e); - announce("Failed to taxi to " + where); - Town.goToTown(); + } else if (action === "taxi list") { + announce("Taxi destinations: " + Array.from(taxiMap.keys()).join(", ")); } - } else if (action === "taxi list") { - announce("Taxi destinations: " + Array.from(taxiMap.keys()).join(", ")); } } } } - } - delay(100); - } + delay(100); + } - removeEventListener("chatmsg", chatEvent); - removeEventListener("gameevent", gameEvent); + removeEventListener("chatmsg", chatEvent); + removeEventListener("gameevent", gameEvent); - return true; -} + return true; + } +); diff --git a/d2bs/kolbot/libs/scripts/Frozenstein.js b/d2bs/kolbot/libs/scripts/Frozenstein.js index d56fc06b4..9c9de0b59 100644 --- a/d2bs/kolbot/libs/scripts/Frozenstein.js +++ b/d2bs/kolbot/libs/scripts/Frozenstein.js @@ -1,22 +1,25 @@ /** * @filename Frozenstein.js * @author kolton -* @desc kill Frozensteinand optionally clear Frozen River +* @desc kill Frozenstein and optionally clear Frozen River * */ -function Frozenstein () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CrystalizedPassage); - Precast.doPrecast(true); +const Frozenstein = new Runnable( + function Frozenstein () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.CrystalizedPassage); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, -5, -5)) { - throw new Error("Failed to move to Frozenstein"); - } + if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, -5, -5)) { + throw new Error("Failed to move to Frozenstein"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.Frozenstein)); - Config.Frozenstein.ClearFrozenRiver && Attack.clearLevel(Config.ClearType); + Attack.kill(getLocaleString(sdk.locale.monsters.Frozenstein)); + Config.Frozenstein.ClearFrozenRiver && Attack.clearLevel(Config.ClearType); - return true; -} + return true; + }, + sdk.areas.CrystalizedPassage +); diff --git a/d2bs/kolbot/libs/scripts/Gamble.js b/d2bs/kolbot/libs/scripts/Gamble.js index ae27c6c5a..e29280f58 100644 --- a/d2bs/kolbot/libs/scripts/Gamble.js +++ b/d2bs/kolbot/libs/scripts/Gamble.js @@ -5,57 +5,59 @@ * */ -function Gamble () { - let idleTick = 0; - let info = Gambling.getInfo(); - let needGold = false; +const Gamble = new Runnable( + function Gamble () { + let info = Gambling.getInfo(); + if (!info) throw new Error("Bad Gambling System config."); + + let idleTick = 0; + let needGold = false; + + me.maxgametime = 0; + Town.goToTown(1); + + addEventListener("copydata", + function (mode, msg) { + if (needGold && mode === 0 && info.goldFinders.indexOf(msg) > -1) { + print("Got game request from " + msg); + sendCopyData(null, msg, 4, me.gamename + "/" + me.gamepassword); + } + }); - if (!info) throw new Error("Bad Gambling System config."); + while (true) { + Town.needGamble() ? Town.gamble() : (needGold = true) && (idleTick = 0); + Town.move("stash"); - me.maxgametime = 0; - Town.goToTown(1); + while (needGold) { + // should there be a player count check before getting into this loop? + // Or maybe gamevent for player join/leave, or itemevent for gold dropping? + while (true) { + Town.needGamble() && (needGold = false); + Town.stash(); - addEventListener("copydata", - function (mode, msg) { - if (needGold && mode === 0 && info.goldFinders.indexOf(msg) > -1) { - print("Got game request from " + msg); - sendCopyData(null, msg, 4, me.gamename + "/" + me.gamepassword); - } - }); + let gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround); - while (true) { - Town.needGamble() ? Town.gamble() : (needGold = true) && (idleTick = 0); - Town.move("stash"); + if (!gold || !Pickit.canPick(gold)) { + break; + } - while (needGold) { - // should there be a player count check before getting into this loop? - // Or maybe gamevent for player join/leave, or itemevent for gold dropping? - while (true) { - Town.needGamble() && (needGold = false); - Town.stash(); + Pickit.pickItem(gold); + delay(500); - let gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround); + } - if (!gold || !Pickit.canPick(gold)) { - break; + if (needGold && getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += rand(1200, 1500) * 1000; } - Pickit.pickItem(gold); delay(500); - } - if (needGold && getTickCount() - idleTick > 0) { - Packet.questRefresh(); - idleTick += rand(1200, 1500) * 1000; - } - - delay(500); + delay(1000); } - delay(1000); + // eslint-disable-next-line no-unreachable + return true; } - - // eslint-disable-next-line no-unreachable - return true; -} +); diff --git a/d2bs/kolbot/libs/scripts/GemHunter.js b/d2bs/kolbot/libs/scripts/GemHunter.js index 452e8feca..141530107 100644 --- a/d2bs/kolbot/libs/scripts/GemHunter.js +++ b/d2bs/kolbot/libs/scripts/GemHunter.js @@ -12,29 +12,31 @@ * - We should also then keep track of where the shrine was, (I don't remember if gem shrines regen, so check this) * - Take into account the next area and sort the shrines to bring us to the exit if its connected */ -function GemHunter () { - Town.doChores(); - Town.getGem(); - if (Town.getGemsInInv().length === 0) { - print("ÿc4GemHunterÿc0: no gems in inventory - aborting."); - return false; - } +const GemHunter = new Runnable( + function GemHunter () { + Town.doChores(); + Town.getGem(); + if (Town.getGemsInInv().length === 0) { + print("ÿc4GemHunterÿc0: no gems in inventory - aborting."); + return false; + } - for (let area of Config.GemHunter.AreaList) { - if (Town.getGemsInInv().length > 0) { - print("ÿc4GemHunterÿc0: Moving to " + getAreaName(area)); - Pather.journeyTo(area); - if (i % 2 === 0) Precast.doPrecast(true); - if (Misc.getShrinesInArea(area, sdk.shrines.Gem, true)) { - Pickit.pickItems(); - print("ÿc4GemHunterÿc0: found a gem Shrine"); - if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { - print("ÿc4GemHunterÿc0: Getting a new Gem in Town."); - Town.visitTown(); // Go to Town and do chores. Will throw an error if it fails to return from Town. - Town.getGem(); + for (let area of Config.GemHunter.AreaList) { + if (Town.getGemsInInv().length > 0) { + print("ÿc4GemHunterÿc0: Moving to " + getAreaName(area)); + Pather.journeyTo(area); + if (i % 2 === 0) Precast.doPrecast(true); + if (Misc.getShrinesInArea(area, sdk.shrines.Gem, true)) { + Pickit.pickItems(); + print("ÿc4GemHunterÿc0: found a gem Shrine"); + if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { + print("ÿc4GemHunterÿc0: Getting a new Gem in Town."); + Town.visitTown(); // Go to Town and do chores. Will throw an error if it fails to return from Town. + Town.getGem(); + } } } } + return true; } - return true; -} +); diff --git a/d2bs/kolbot/libs/scripts/GetCube.js b/d2bs/kolbot/libs/scripts/GetCube.js index 0b6c2afcc..7c4ec7ed6 100644 --- a/d2bs/kolbot/libs/scripts/GetCube.js +++ b/d2bs/kolbot/libs/scripts/GetCube.js @@ -5,31 +5,34 @@ * */ -function GetCube () { - // Can't get the cube if we can't access the act - if (!me.accessToAct(2)) return false; +const GetCube = new Runnable( + function GetCube () { + // Can't get the cube if we can't access the act + if (!me.accessToAct(2)) return false; - console.log("Getting cube"); - me.overhead("Getting cube"); + console.log("Getting cube"); + me.overhead("Getting cube"); - Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); - Precast.doPrecast(true); + Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); + Precast.doPrecast(true); - if (Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) - && Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricCubeChest)) { - let chest = Game.getObject(sdk.quest.chest.HoradricCubeChest); + if (Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) + && Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricCubeChest)) { + let chest = Game.getObject(sdk.quest.chest.HoradricCubeChest); - if (chest) { - Misc.openChest(chest); - Misc.poll(function () { - let cube = Game.getItem(sdk.quest.item.Cube); - return !!cube && Pickit.pickItem(cube); - }, 1000, 2000); + if (chest) { + Misc.openChest(chest); + Misc.poll(function () { + let cube = Game.getItem(sdk.quest.item.Cube); + return !!cube && Pickit.pickItem(cube); + }, 1000, 2000); + } } - } - Town.goToTown(); - let cube = me.getItem(sdk.quest.item.Cube); + Town.goToTown(); + let cube = me.getItem(sdk.quest.item.Cube); - return (!!cube && Storage.Stash.MoveTo(cube)); -} + return (!!cube && Storage.Stash.MoveTo(cube)); + }, + sdk.areas.HallsoftheDeadLvl2 +); diff --git a/d2bs/kolbot/libs/scripts/GetEssences.js b/d2bs/kolbot/libs/scripts/GetEssences.js index 80d8bbecc..61aabbdb4 100644 --- a/d2bs/kolbot/libs/scripts/GetEssences.js +++ b/d2bs/kolbot/libs/scripts/GetEssences.js @@ -6,50 +6,52 @@ * */ -function GetEssences () { - Town.doChores(); - - /** - * @param {number} essence - * @returns {number} - */ - const count = function (essence) { - return me.getItemsEx(essence, sdk.items.mode.inStorage).length; - }; +const GetEssences = new Runnable( + function GetEssences () { + Town.doChores(); + + /** + * @param {number} essence + * @returns {number} + */ + const count = function (essence) { + return me.getItemsEx(essence, sdk.items.mode.inStorage).length; + }; - if (count(sdk.quest.item.TwistedEssenceofSuffering) < 1) { - try { - Loader.runScript("Andariel"); - } catch (e) { - console.error("ÿc1Andariel failed :: ", e); + if (count(sdk.quest.item.TwistedEssenceofSuffering) < 1) { + try { + Loader.runScript("Andariel"); + } catch (e) { + console.error("ÿc1Andariel failed :: ", e); + } } - } - if (count(sdk.quest.item.ChargedEssenceofHatred) < 1) { - try { - Config.Mephisto.MoatTrick = Config.GetEssences.MoatMeph; - Loader.runScript("Mephisto"); - } catch (e) { - console.error("ÿc1Mephisto failed :: ", e); + if (count(sdk.quest.item.ChargedEssenceofHatred) < 1) { + try { + Config.Mephisto.MoatTrick = Config.GetEssences.MoatMeph; + Loader.runScript("Mephisto"); + } catch (e) { + console.error("ÿc1Mephisto failed :: ", e); + } } - } - - if (count(sdk.quest.item.BurningEssenceofTerror) < 1) { - try { - Config.Diablo.Fast = Config.GetEssences.FastDiablo; - Loader.runScript("Diablo"); - } catch (e) { - console.error("ÿc1Diablo failed :: ", e); + + if (count(sdk.quest.item.BurningEssenceofTerror) < 1) { + try { + Config.Diablo.Fast = Config.GetEssences.FastDiablo; + Loader.runScript("Diablo"); + } catch (e) { + console.error("ÿc1Diablo failed :: ", e); + } } - } - if (count(sdk.quest.item.FesteringEssenceofDestruction) < 1) { - try { - Loader.runScript("Baal"); - } catch (e) { - console.error("ÿc1Baal failed :: ", e); + if (count(sdk.quest.item.FesteringEssenceofDestruction) < 1) { + try { + Loader.runScript("Baal"); + } catch (e) { + console.error("ÿc1Baal failed :: ", e); + } } - } - return true; -} + return true; + } +); diff --git a/d2bs/kolbot/libs/scripts/GetFade.js b/d2bs/kolbot/libs/scripts/GetFade.js index 448d3ba8f..7d2d8805a 100644 --- a/d2bs/kolbot/libs/scripts/GetFade.js +++ b/d2bs/kolbot/libs/scripts/GetFade.js @@ -5,43 +5,58 @@ * */ -function GetFade () { - // Can't get use river if we can't access the act - TODO: use another area if we can't access river - if (!me.accessToAct(4)) return false; - // already have fade - if (me.getState(sdk.states.Fade)) return true; - - /** @type {{ have: boolean, item: ItemUnit }} */ - const fadeItem = me.findFirst([ - { name: sdk.locale.items.Treachery, equipped: true }, - { name: sdk.locale.items.LastWish, equipped: true }, - { name: sdk.locale.items.SpiritWard, equipped: true } - ]); - if (!fadeItem) throw new Error("No item with ctc Fade equipped"); - - console.log("Getting fade"); - me.overhead("Getting fade"); - - Pather.useWaypoint(sdk.areas.RiverofFlame, true); - Precast.doPrecast(true); - - // check if item is on switch - let mainSlot; - - Pather.moveTo(7811, 5872); - - if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { - mainSlot = me.weaponswitch; - me.switchWeapons(sdk.player.slot.Secondary); - } - - Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - - while (!me.getState(sdk.states.Fade)) { - delay(3); - } - - mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); - - return me.getState(sdk.states.Fade); -} +const GetFade = new Runnable( + function GetFade () { + // Can't get use river if we can't access the act - TODO: use another area if we can't access river + if (!me.accessToAct(4)) return false; + // already have fade + if (me.getState(sdk.states.Fade)) return true; + + /** @type {{ have: boolean, item: ItemUnit }} */ + const fadeItem = me.findFirst([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ]); + if (!fadeItem) throw new Error("No item with ctc Fade equipped"); + + console.log("Getting fade"); + me.overhead("Getting fade"); + + Pather.useWaypoint(sdk.areas.RiverofFlame, true); + Precast.doPrecast(true); + + // check if item is on switch + let mainSlot; + + Pather.moveTo(7811, 5872); + + if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { + mainSlot = me.weaponswitch; + me.switchWeapons(sdk.player.slot.Secondary); + } + + Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + + try { + let retry = 0; + let timeout = getTickCount() + Time.minutes(1); + while (!me.getState(sdk.states.Fade)) { + if (getTickCount() > timeout) { + retry++; + if (retry > 5) { + throw new Error("Failed to get fade"); + } + Pather.randMove(); + Pather.moveTo(7811, 5872); + timeout = getTickCount() + Time.minutes(1); + } + delay(3); + } + return me.getState(sdk.states.Fade); + } finally { + mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); + } + }, + sdk.areas.RiverofFlame +); diff --git a/d2bs/kolbot/libs/scripts/GetKeys.js b/d2bs/kolbot/libs/scripts/GetKeys.js index 523c5907f..8d7572413 100644 --- a/d2bs/kolbot/libs/scripts/GetKeys.js +++ b/d2bs/kolbot/libs/scripts/GetKeys.js @@ -5,32 +5,34 @@ * */ -function GetKeys () { - Town.doChores(); +const GetKeys = new Runnable( + function GetKeys () { + Town.doChores(); - if (me.getItemsEx(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length < 3) { - try { - Loader.runScript("Countess"); - } catch (e) { - console.error("ÿc1Countess failed :: ", e); + if (me.getItemsEx(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length < 3) { + try { + Loader.runScript("Countess"); + } catch (e) { + console.error("ÿc1Countess failed :: ", e); + } } - } - if (me.getItemsEx(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length < 3) { - try { - Loader.runScript("Summoner", () => Config.Summoner.FireEye = false); - } catch (e) { - console.error("ÿc1Summoner failed :: ", e); + if (me.getItemsEx(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length < 3) { + try { + Loader.runScript("Summoner", () => Config.Summoner.FireEye = false); + } catch (e) { + console.error("ÿc1Summoner failed :: ", e); + } } - } - if (me.getItemsEx(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length < 3) { - try { - Loader.runScript("Nihlathak"); - } catch (e) { - console.error("ÿc1Nihlathak failed :: ", e); + if (me.getItemsEx(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length < 3) { + try { + Loader.runScript("Nihlathak"); + } catch (e) { + console.error("ÿc1Nihlathak failed :: ", e); + } } - } - return true; -} + return true; + } +); diff --git a/d2bs/kolbot/libs/scripts/GhostBusters.js b/d2bs/kolbot/libs/scripts/GhostBusters.js index 334710bea..5165594b7 100644 --- a/d2bs/kolbot/libs/scripts/GhostBusters.js +++ b/d2bs/kolbot/libs/scripts/GhostBusters.js @@ -5,111 +5,114 @@ * */ -function GhostBusters () { - const clearGhosts = function () { - let room = getRoom(); - if (!room) return false; - - const rooms = []; - /** @param {Monster} monster */ - const check = function (monster) { - return monster.isGhost && monster.distance <= 30; - }; +const GhostBusters = new Runnable( + function GhostBusters () { + const clearGhosts = function () { + let room = getRoom(); + if (!room) return false; + + const rooms = []; + /** @param {Monster} monster */ + const check = function (monster) { + return monster.isGhost && monster.distance <= 30; + }; - 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 = Attack.buildMonsterList(check); - if (!monList.length) continue; + let monList = Attack.buildMonsterList(check); + if (!monList.length) continue; - if (!Attack.clearList(monList)) { - return false; + if (!Attack.clearList(monList)) { + return false; + } } } - } - return true; - }; + return true; + }; - const tasks = new Map([ - ["cellar", () => { - Pather.useWaypoint(sdk.areas.BlackMarsh); - Precast.doPrecast(true); + const tasks = new Map([ + ["cellar", () => { + Pather.useWaypoint(sdk.areas.BlackMarsh); + Precast.doPrecast(true); - for (let i = sdk.areas.ForgottenTower; i <= sdk.areas.TowerCellarLvl5; i += 1) { - Pather.moveToExit(i, true) && clearGhosts(); - } - }], - ["jail", () => { - // gonna use inner cloister wp and travel backwards - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); - - for (let i = sdk.areas.JailLvl3; i >= sdk.areas.JailLvl1; i -= 1) { - Pather.moveToExit(i, true) && clearGhosts(); - } - }], - ["cathedral", () => { - Pather.useWaypoint(sdk.areas.InnerCloister); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.Cathedral, true); - clearGhosts(); - }], - ["tombs", () => { - Pather.useWaypoint(sdk.areas.CanyonofMagic); - Precast.doPrecast(true); - - for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i += 1) { - Pather.moveToExit(i, true) && clearGhosts(); - Pather.moveToExit(sdk.areas.CanyonofMagic, true); + for (let i = sdk.areas.ForgottenTower; i <= sdk.areas.TowerCellarLvl5; i += 1) { + Pather.moveToExit(i, true) && clearGhosts(); + } + }], + ["jail", () => { + // gonna use inner cloister wp and travel backwards + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); + + for (let i = sdk.areas.JailLvl3; i >= sdk.areas.JailLvl1; i -= 1) { + Pather.moveToExit(i, true) && clearGhosts(); + } + }], + ["cathedral", () => { + Pather.useWaypoint(sdk.areas.InnerCloister); + Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.Cathedral, true); + clearGhosts(); + }], + ["tombs", () => { + Pather.useWaypoint(sdk.areas.CanyonofMagic); + Precast.doPrecast(true); + + for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i += 1) { + Pather.moveToExit(i, true) && clearGhosts(); + Pather.moveToExit(sdk.areas.CanyonofMagic, true); + } + }], + ["flayerDungeon", () => { + Pather.useWaypoint(sdk.areas.FlayerJungle); + Precast.doPrecast(true); + + [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3].forEach(area => { + Pather.moveToExit(area, true) && clearGhosts(); + }); + }], + ["crystalinePassage", () => { + Pather.useWaypoint(sdk.areas.CrystalizedPassage); + Precast.doPrecast(true); + clearGhosts(); + Pather.moveToExit(sdk.areas.FrozenRiver, true) && clearGhosts(); + }], + ["glacialTrail", () => { + Pather.useWaypoint(sdk.areas.GlacialTrail); + Precast.doPrecast(true); + clearGhosts(); + Pather.moveToExit(sdk.areas.DrifterCavern, true) && clearGhosts(); + }], + ["icyCellar", () => { + Pather.useWaypoint(sdk.areas.AncientsWay); + Precast.doPrecast(true); + Pather.moveToExit(sdk.areas.IcyCellar, true) && clearGhosts(); + }] + ]); + + tasks.forEach(task => { + Town.doChores(); + + try { + task(); + } finally { + Town.goToTown(); } - }], - ["flayerDungeon", () => { - Pather.useWaypoint(sdk.areas.FlayerJungle); - Precast.doPrecast(true); - - [sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, sdk.areas.FlayerDungeonLvl3].forEach(area => { - Pather.moveToExit(area, true) && clearGhosts(); - }); - }], - ["crystalinePassage", () => { - Pather.useWaypoint(sdk.areas.CrystalizedPassage); - Precast.doPrecast(true); - clearGhosts(); - Pather.moveToExit(sdk.areas.FrozenRiver, true) && clearGhosts(); - }], - ["glacialTrail", () => { - Pather.useWaypoint(sdk.areas.GlacialTrail); - Precast.doPrecast(true); - clearGhosts(); - Pather.moveToExit(sdk.areas.DrifterCavern, true) && clearGhosts(); - }], - ["icyCellar", () => { - Pather.useWaypoint(sdk.areas.AncientsWay); - Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.IcyCellar, true) && clearGhosts(); - }] - ]); - - tasks.forEach(task => { - Town.doChores(); - - try { - task(); - } finally { - Town.goToTown(); - } - }); - - return true; -} + }); + + return true; + }, + sdk.areas.BlackMarsh +); diff --git a/d2bs/kolbot/libs/scripts/Hephasto.js b/d2bs/kolbot/libs/scripts/Hephasto.js index e862fdecf..c8c81ae53 100644 --- a/d2bs/kolbot/libs/scripts/Hephasto.js +++ b/d2bs/kolbot/libs/scripts/Hephasto.js @@ -5,23 +5,26 @@ * */ -function Hephasto () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.RiverofFlame); - Precast.doPrecast(true); +const Hephasto = new Runnable( + function Hephasto () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.RiverofFlame); + Precast.doPrecast(true); - if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge)) { - throw new Error("Failed to move to Hephasto"); - } + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge)) { + throw new Error("Failed to move to Hephasto"); + } - try { - Attack.kill(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); - } catch (e) { - print("Heph not found. Carry on"); - } + try { + Attack.kill(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); + } catch (e) { + print("Heph not found. Carry on"); + } - Pickit.pickItems(); - Config.Hephasto.ClearRiver && Attack.clearLevel(Config.Hephasto.ClearType); + Pickit.pickItems(); + Config.Hephasto.ClearRiver && Attack.clearLevel(Config.Hephasto.ClearType); - return true; -} + return true; + }, + sdk.areas.RiverofFlame +); diff --git a/d2bs/kolbot/libs/scripts/IPHunter.js b/d2bs/kolbot/libs/scripts/IPHunter.js index 0782c8d67..1302d48a0 100644 --- a/d2bs/kolbot/libs/scripts/IPHunter.js +++ b/d2bs/kolbot/libs/scripts/IPHunter.js @@ -7,53 +7,55 @@ * */ -function IPHunter () { - const ip = Number(me.gameserverip.split(".")[3]); - - if (Config.IPHunter.IPList.includes(ip)) { - load("threads/antiidle.js"); - const foundMsg = "IPHunter: IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword; - D2Bot.printToConsole(foundMsg, sdk.colors.D2Bot.DarkGold); - console.log(foundMsg); - me.overhead(":D IP found! - [" + ip + "]"); - me.maxgametime = 0; - - for (let i = 12; i > 0; i -= 1) { - me.overhead(":D IP found! - [" + ip + "]" + (i - 1) + " beep left"); - beep(); // works if windows sounds are enabled - delay(250); - } +const IPHunter = new Runnable( + function IPHunter () { + const ip = Number(me.gameserverip.split(".")[3]); + + if (Config.IPHunter.IPList.includes(ip)) { + load("threads/antiidle.js"); + const foundMsg = "IPHunter: IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword; + D2Bot.printToConsole(foundMsg, sdk.colors.D2Bot.DarkGold); + console.log(foundMsg); + me.overhead(":D IP found! - [" + ip + "]"); + me.maxgametime = 0; - while (true) { - /* // remove comment if you want beeps at every movement - for (let i = 12; i != 0; i -= 1) { - me.overhead(":D IP found! - [" + ip + "]" + (i-1) + " beep left"); + for (let i = 12; i > 0; i -= 1) { + me.overhead(":D IP found! - [" + ip + "]" + (i - 1) + " beep left"); beep(); // works if windows sounds are enabled delay(250); } - */ - - me.overhead(":D IP found! - [" + ip + "]"); - try { - Town.move("waypoint"); - Town.move("stash"); - } catch (e) { - // ensure it doesnt leave game by failing to walk due to desyncing. - } - for (let i = (12 * 60); i > 0; i -= 1) { - me.overhead(":D IP found! - [" + ip + "] Next movement in: " + i + " sec."); - delay(1000); + while (true) { + /* // remove comment if you want beeps at every movement + for (let i = 12; i != 0; i -= 1) { + me.overhead(":D IP found! - [" + ip + "]" + (i-1) + " beep left"); + beep(); // works if windows sounds are enabled + delay(250); + } + */ + + me.overhead(":D IP found! - [" + ip + "]"); + try { + Town.move("waypoint"); + Town.move("stash"); + } catch (e) { + // ensure it doesnt leave game by failing to walk due to desyncing. + } + + for (let i = (12 * 60); i > 0; i -= 1) { + me.overhead(":D IP found! - [" + ip + "] Next movement in: " + i + " sec."); + delay(1000); + } } } - } - for (let i = (Config.IPHunter.GameLength * 60); i > 0; i -= 1) { - me.overhead(":( IP : [" + (ip) + "] NG: " + i + " sec"); - delay(1000); - } + for (let i = (Config.IPHunter.GameLength * 60); i > 0; i -= 1) { + me.overhead(":( IP : [" + (ip) + "] NG: " + i + " sec"); + delay(1000); + } - D2Bot.printToConsole("IPHunter: IP was [" + ip + "]", sdk.colors.D2Bot.Gray); + D2Bot.printToConsole("IPHunter: IP was [" + ip + "]", sdk.colors.D2Bot.Gray); - return true; -} + return true; + } +); diff --git a/d2bs/kolbot/libs/scripts/Icehawk.js b/d2bs/kolbot/libs/scripts/Icehawk.js index 186281ed0..ed4aa5222 100644 --- a/d2bs/kolbot/libs/scripts/Icehawk.js +++ b/d2bs/kolbot/libs/scripts/Icehawk.js @@ -5,16 +5,19 @@ * */ -function Icehawk () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.KurastBazaar); - Precast.doPrecast(true); +const Icehawk = new Runnable( + function Icehawk () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.KurastBazaar); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2], false)) { - throw new Error("Failed to move to Icehawk"); - } + if (!Pather.moveToExit([sdk.areas.A3SewersLvl1, sdk.areas.A3SewersLvl2], false)) { + throw new Error("Failed to move to Icehawk"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.IcehawkRiftwing)); + Attack.kill(getLocaleString(sdk.locale.monsters.IcehawkRiftwing)); - return true; -} + return true; + }, + sdk.areas.KurastBazaar +); diff --git a/d2bs/kolbot/libs/scripts/Idle.js b/d2bs/kolbot/libs/scripts/Idle.js index 2d937f8ab..f3496e971 100644 --- a/d2bs/kolbot/libs/scripts/Idle.js +++ b/d2bs/kolbot/libs/scripts/Idle.js @@ -5,54 +5,57 @@ * */ -function Idle () { - const greet = []; - // eslint-disable-next-line no-unused-vars - function gameEvent (mode, param1, param2, name1, name2) { - switch (mode) { - case 0x02: - // idle in town - me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); +const Idle = new Runnable( + function Idle () { + const greet = []; + // eslint-disable-next-line no-unused-vars + function gameEvent (mode, param1, param2, name1, name2) { + switch (mode) { + case 0x02: + // idle in town + me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); - break; - } - } - - try { - const startTime = getTickCount(); - let idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); - if (Config.Idle.Advertise) { - addEventListener("gameevent", gameEvent); - } - - while (true) { - if (!me.inArea(sdk.areas.RogueEncampment)) { - Town.goToTown(1); - } else if (Town.getDistance("stash") > 10) { - Town.move("stash"); - } - if (Config.Idle.MaxGameLength > 0 - && getTickCount() - startTime > Time.minutes(Config.Idle.MaxGameLength)) { break; } + } + + try { + const startTime = getTickCount(); + let idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); if (Config.Idle.Advertise) { - while (greet.length) { - let name = greet.shift(); - say("!Welcome " + name + ". " + Config.Idle.AdvertiseMessage); - } + addEventListener("gameevent", gameEvent); } - if (getTickCount() - idleTick > 0) { - Packet.questRefresh(); - idleTick += Time.seconds(rand(1200, 1500)); - console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + + while (true) { + if (!me.inArea(sdk.areas.RogueEncampment)) { + Town.goToTown(1); + } else if (Town.getDistance("stash") > 10) { + Town.move("stash"); + } + if (Config.Idle.MaxGameLength > 0 + && getTickCount() - startTime > Time.minutes(Config.Idle.MaxGameLength)) { + break; + } + if (Config.Idle.Advertise) { + while (greet.length) { + let name = greet.shift(); + say("!Welcome " + name + ". " + Config.Idle.AdvertiseMessage); + } + } + if (getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + + delay(1000); } - delay(1000); + return true; + } finally { + // cleanup + removeEventListener("gameevent", gameEvent); } - - return true; - } finally { - // cleanup - removeEventListener("gameevent", gameEvent); - } -} + }, + sdk.areas.RogueEncampment +); diff --git a/d2bs/kolbot/libs/scripts/Izual.js b/d2bs/kolbot/libs/scripts/Izual.js index 010e232f0..569147d04 100644 --- a/d2bs/kolbot/libs/scripts/Izual.js +++ b/d2bs/kolbot/libs/scripts/Izual.js @@ -5,17 +5,20 @@ * */ -function Izual () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CityoftheDamned); - Precast.doPrecast(true); +const Izual = new Runnable( + function Izual () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.CityoftheDamned); + Precast.doPrecast(true); - if (!Pather.moveToPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual)) { - throw new Error("Failed to move to Izual."); - } + if (!Pather.moveToPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual)) { + throw new Error("Failed to move to Izual."); + } - Attack.kill(sdk.monsters.Izual); - Pickit.pickItems(); + Attack.kill(sdk.monsters.Izual); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.CityoftheDamned +); diff --git a/d2bs/kolbot/libs/scripts/KillDclone.js b/d2bs/kolbot/libs/scripts/KillDclone.js index 3a8642879..d461da534 100644 --- a/d2bs/kolbot/libs/scripts/KillDclone.js +++ b/d2bs/kolbot/libs/scripts/KillDclone.js @@ -5,20 +5,23 @@ * */ -function KillDclone () { - Pather.useWaypoint(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); +const KillDclone = new Runnable( + function KillDclone () { + Pather.useWaypoint(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); - if (!Pather.usePortal(null)) { - throw new Error("Failed to move to Palace Cellar"); - } + if (!Pather.usePortal(null)) { + throw new Error("Failed to move to Palace Cellar"); + } - Attack.kill(sdk.monsters.DiabloClone); - Pickit.pickItems(); + Attack.kill(sdk.monsters.DiabloClone); + Pickit.pickItems(); - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleAnni"); - } + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleAnni"); + } - return true; -} + return true; + }, + sdk.areas.ArcaneSanctuary +); diff --git a/d2bs/kolbot/libs/scripts/KurastTemples.js b/d2bs/kolbot/libs/scripts/KurastTemples.js index fbbe85119..8745f728f 100644 --- a/d2bs/kolbot/libs/scripts/KurastTemples.js +++ b/d2bs/kolbot/libs/scripts/KurastTemples.js @@ -5,39 +5,42 @@ * */ -function KurastTemples () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.KurastBazaar); +const KurastTemples = new Runnable( + function KurastTemples () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.KurastBazaar); - [ - { 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(area => { - try { - if (!me.inArea(area.base)) { - // maybe journeyTo instead? - if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to change area"); - } - let precastTimeout = getTickCount() + Time.minutes(2); - /** @type {Map area.temples.some(temple => temple === exit.target)) - .forEach(exit => _temples.set(exit.target, { x: exit.x, y: exit.y })); - area.temples - .sort((a, b) => _temples.get(a).distance - _temples.get(b).distance) - .forEach(temple => { - if (!Pather.moveToExit(temple, true)) throw new Error("Failed to move to the temple"); - Attack.clearLevel(Config.ClearType); - if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to move out of the temple"); - Precast.doPrecast((getTickCount() > precastTimeout)); - }); + [ + { 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(area => { + try { + if (!me.inArea(area.base)) { + // maybe journeyTo instead? + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to change area"); + } + let precastTimeout = getTickCount() + Time.minutes(2); + /** @type {Map area.temples.some(temple => temple === exit.target)) + .forEach(exit => _temples.set(exit.target, { x: exit.x, y: exit.y })); + area.temples + .sort((a, b) => _temples.get(a).distance - _temples.get(b).distance) + .forEach(temple => { + if (!Pather.moveToExit(temple, true)) throw new Error("Failed to move to the temple"); + Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit(area.base, true)) throw new Error("Failed to move out of the temple"); + Precast.doPrecast((getTickCount() > precastTimeout)); + }); - } catch (e) { - console.error(e); - } - }); + } catch (e) { + console.error(e); + } + }); - return true; -} + return true; + }, + sdk.areas.KurastBazaar +); diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index 3ed405b9b..78d048578 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -5,286 +5,288 @@ * */ -function MFHelper () { - const startTime = getTickCount(); - /** - * @todo We should be able to handle Diablo scripts then resume MFHelper, not sure how yet but doesn't make sense to have - * helper just idle if leader does any of the a5 scripts before baal. I guess could re-order them in the configs but having - * it broken up by act flows better - */ - let player, playerAct, split; - let lastPrecast; - - /** @type {{ task: string, msg: string, at: number, area: number }[]} */ - const taskList = []; - const tasks = ["kill", "clearlevel", "clear", "quit", "cows", "council", "goto", "nextup"]; - - /** - * @param {string} name - * @param {string} msg - */ - function chatEvent (name, msg) { - if (!msg) return; - let msgShort = msg && msg.length ? msg.split(" ")[0] : ""; - if (!tasks.includes(msgShort)) return; - - if (!player) { - // anything else we need to consider here? - player = Misc.findPlayer(name); - } +const MFHelper = new Runnable( + function MFHelper () { + const startTime = getTickCount(); + /** + * @todo We should be able to handle Diablo scripts then resume MFHelper, not sure how yet but doesn't make sense to have + * helper just idle if leader does any of the a5 scripts before baal. I guess could re-order them in the configs but having + * it broken up by act flows better + */ + let player, playerAct, split; + let lastPrecast; + + /** @type {{ task: string, msg: string, at: number, area: number }[]} */ + const taskList = []; + const tasks = ["kill", "clearlevel", "clear", "quit", "cows", "council", "goto", "nextup"]; + + /** + * @param {string} name + * @param {string} msg + */ + function chatEvent (name, msg) { + if (!msg) return; + let msgShort = msg && msg.length ? msg.split(" ")[0] : ""; + if (!tasks.includes(msgShort)) return; + + if (!player) { + // anything else we need to consider here? + player = Misc.findPlayer(name); + } - if (player && name === player.name) { - taskList.push({ task: msgShort, msg: msg, at: getTickCount(), area: player.area }); + if (player && name === player.name) { + taskList.push({ task: msgShort, msg: msg, at: getTickCount(), area: player.area }); + } } - } - - addEventListener("chatmsg", chatEvent); - Town.doChores(); - Town.move("portalspot"); - if (Config.Leader) { - if (!Misc.poll(() => Misc.inMyParty(Config.Leader), 30e4, 1000)) { - throw new Error("MFHelper: Leader not partied"); - } + addEventListener("chatmsg", chatEvent); + Town.doChores(); + Town.move("portalspot"); - player = Misc.findPlayer(Config.Leader); - } + if (Config.Leader) { + if (!Misc.poll(() => Misc.inMyParty(Config.Leader), 30e4, 1000)) { + throw new Error("MFHelper: Leader not partied"); + } - if (player) { - if (!Misc.poll(() => player.area, 120 * 60, 100 + me.ping)) { - throw new Error("Failed to wait for player area"); + player = Misc.findPlayer(Config.Leader); } - playerAct = Misc.getPlayerAct(Config.Leader); + if (player) { + if (!Misc.poll(() => player.area, 120 * 60, 100 + me.ping)) { + throw new Error("Failed to wait for player area"); + } - if (playerAct && playerAct !== me.act) { - Town.goToTown(playerAct); - Town.move("portalspot"); - } - } + playerAct = Misc.getPlayerAct(Config.Leader); - // START - while (true) { - if (me.dead) { - while (me.mode === sdk.player.mode.Death) { - delay(3); + if (playerAct && playerAct !== me.act) { + Town.goToTown(playerAct); + Town.move("portalspot"); } + } - if (me.hardcore) { - D2Bot.printToConsole( - "(MFHelper) :: " + me.charname + " has died at level " - + me.charlvl + ". Shutting down profile...", - sdk.colors.D2Bot.Red - ); - D2Bot.stop(); - } + // START + while (true) { + if (me.dead) { + while (me.mode === sdk.player.mode.Death) { + delay(3); + } - while (!me.inTown) { - me.revive(); - delay(1000); - } + if (me.hardcore) { + D2Bot.printToConsole( + "(MFHelper) :: " + me.charname + " has died at level " + + me.charlvl + ". Shutting down profile...", + sdk.colors.D2Bot.Red + ); + D2Bot.stop(); + } - Town.move("portalspot"); - console.log("revived!"); - } + while (!me.inTown) { + me.revive(); + delay(1000); + } - if (player) { - if (me.needHealing() && Town.heal()) { Town.move("portalspot"); + console.log("revived!"); } - // Finish MFHelper script if leader is running Diablo or Baal AND we've been running longer than 30s - if ((getTickCount() - startTime > Time.seconds(30)) && [ - sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber - ].includes(player.area)) { - break; - } + if (player) { + if (me.needHealing() && Town.heal()) { + Town.move("portalspot"); + } - if (taskList.length) { - console.debug("Leader area :: " + player.area); - - if (taskList[0].task === "quit") return true; - // check if any message is telling us to quit - if (taskList.find(el => el.task === "quit")) return true; - - // check if any message is telling us that nextup is diablo/baal - if (taskList.some(el => { - if (el.task === "nextup") { - let script = el.msg.split("nextup ")[1]; - - if (script && ["Diablo", "Baal"].includes(script)) { - console.log("ÿc4MFHelperÿc0: Ending script"); - return true; - } - } + // Finish MFHelper script if leader is running Diablo or Baal AND we've been running longer than 30s + if ((getTickCount() - startTime > Time.seconds(30)) && [ + sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber + ].includes(player.area)) { + break; + } - return false; - })) return true; - - // handled pre-reqs, now perform normal checks - let { task, msg, at, area } = taskList.shift(); - - switch (task) { - case "goto": - try { - // lets see if the task list contains any other goto messages, in case we were late - { - let gt = taskList.findIndex(el => el.task === "goto"); - if (gt > -1) { - // alright there is another so lets see where we should actually be going - for (let i = 0; i < gt - 1; i++) { - // feels hacky but this should remove all elements up to the next goto message, while preserving the order of list - ({ task, msg, at, area } = taskList.shift()); - } + if (taskList.length) { + console.debug("Leader area :: " + player.area); + + if (taskList[0].task === "quit") return true; + // check if any message is telling us to quit + if (taskList.find(el => el.task === "quit")) return true; + + // check if any message is telling us that nextup is diablo/baal + if (taskList.some(el => { + if (el.task === "nextup") { + let script = el.msg.split("nextup ")[1]; + + if (script && ["Diablo", "Baal"].includes(script)) { + console.log("ÿc4MFHelperÿc0: Ending script"); + return true; } } - split = msg.substr(6); - console.log("ÿc4MFHelperÿc0: Goto " + split); - - if (!!parseInt(split, 10)) { - split = parseInt(split, 10); - } - - Town.goToTown(split, true); - Town.move("portalspot"); - } catch (townerror) { - console.log(townerror); - } + return false; + })) return true; - break; - case "nextup": - split = msg.split("nextup ")[1]; - console.log("ÿc4MFHelperÿc0: NextUp " + split); + // handled pre-reqs, now perform normal checks + let { task, msg, at, area } = taskList.shift(); - break; - case "cows": - console.log("ÿc4MFHelperÿc0: Clear Cows"); - - if (Misc.poll(() => { - Town.goToTown(1) && Pather.usePortal(sdk.areas.MooMooFarm); - return me.inArea(sdk.areas.MooMooFarm); - }, Time.minutes(1), 500 + me.ping)) { - include("core/Common/Cows.js"); - Precast.doPrecast(false); - Common.Cows.clearCowLevel(); - delay(1000); - } else { - console.warn("Failed to use portal. Currently in area: " + me.area); - } + switch (task) { + case "goto": + try { + // lets see if the task list contains any other goto messages, in case we were late + { + let gt = taskList.findIndex(el => el.task === "goto"); + if (gt > -1) { + // alright there is another so lets see where we should actually be going + for (let i = 0; i < gt - 1; i++) { + // feels hacky but this should remove all elements up to the next goto message, while preserving the order of list + ({ task, msg, at, area } = taskList.shift()); + } + } + } - break; - case "council": - if (!me.inArea(sdk.areas.Travincal) && Town.goToTown(3)) { - Town.move("portalspot"); - Misc.poll(() => Pather.usePortal(sdk.areas.Travincal, player.name), Time.seconds(15), 500 + me.ping); - } - console.log("ÿc4MFHelperÿc0: Kill Council"); - Attack.clearClassids(sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3); + split = msg.substr(6); + console.log("ÿc4MFHelperÿc0: Goto " + split); + + if (!!parseInt(split, 10)) { + split = parseInt(split, 10); + } - break; - default: - // alright first lets check how long its been since the command was given - // this probably needs to be adjusted but for now 3 minutes on any of theses tasks is probably too long - if (getTickCount() - at > Time.minutes(3)) continue; - - /** - * @todo still think this section needs to be done better, we are using a snapshot of the player's area at the time - * of the message but sometimes the area hasn't been updated yet, causing us to do dumb things like attempt to kill - * while still in town. We can't just use the players area though because of towncheck/chicken. Feel like best solution - * would be adding area into leaders message and just always parsing it from there - */ - try { - split = msg.split(task + " ")[1]; - if (parseInt(split, 10)) { - split = parseInt(split, 10); + Town.goToTown(split, true); + Town.move("portalspot"); + } catch (townerror) { + console.log(townerror); } - } catch (e) { - console.warn(e.message || "Failed to get id from message split"); + break; - } + case "nextup": + split = msg.split("nextup ")[1]; + console.log("ÿc4MFHelperÿc0: NextUp " + split); - if (me.area !== area) { - !me.inTown && Town.goToTown(); + break; + case "cows": + console.log("ÿc4MFHelperÿc0: Clear Cows"); + + if (Misc.poll(() => { + Town.goToTown(1) && Pather.usePortal(sdk.areas.MooMooFarm); + return me.inArea(sdk.areas.MooMooFarm); + }, Time.minutes(1), 500 + me.ping)) { + include("core/Common/Cows.js"); + Precast.doPrecast(false); + Common.Cows.clearCowLevel(); + delay(1000); + } else { + console.warn("Failed to use portal. Currently in area: " + me.area); + } - if (me.act !== sdk.areas.actOf(area)) { - Town.goToTown(sdk.areas.actOf(area)); + break; + case "council": + if (!me.inArea(sdk.areas.Travincal) && Town.goToTown(3)) { Town.move("portalspot"); + Misc.poll(() => Pather.usePortal(sdk.areas.Travincal, player.name), Time.seconds(15), 500 + me.ping); } + console.log("ÿc4MFHelperÿc0: Kill Council"); + Attack.clearClassids(sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3); - String.isEqual(task, "clearlevel") - ? (area = split) - : (player.area !== area && !player.inTown) && (area = player.area); - + break; + default: + // alright first lets check how long its been since the command was given + // this probably needs to be adjusted but for now 3 minutes on any of theses tasks is probably too long + if (getTickCount() - at > Time.minutes(3)) continue; + + /** + * @todo still think this section needs to be done better, we are using a snapshot of the player's area at the time + * of the message but sometimes the area hasn't been updated yet, causing us to do dumb things like attempt to kill + * while still in town. We can't just use the players area though because of towncheck/chicken. Feel like best solution + * would be adding area into leaders message and just always parsing it from there + */ try { - Misc.poll(() => Pather.usePortal(null, player.name), Time.seconds(15), 500 + me.ping); + split = msg.split(task + " ")[1]; + if (parseInt(split, 10)) { + split = parseInt(split, 10); + } } catch (e) { - console.warn(e.message || "Failed to take leader portal"); - continue; + console.warn(e.message || "Failed to get id from message split"); + break; } - } - if (!me.inTown && me.area === area) { - let forceCast = false; - if (!lastPrecast || getTickCount() - lastPrecast > Time.minutes(2)) { - (forceCast = true) && (lastPrecast = getTickCount()); - } - Precast.doPrecast(forceCast); - } else if (!me.inTown && !me.inArea(player.area)) { - Town.goToTown(sdk.areas.actOf(player.area)); - continue; - } + if (me.area !== area) { + !me.inTown && Town.goToTown(); - switch (task) { - case "kill": - console.log("ÿc4MFHelperÿc0: Kill " + split); + if (me.act !== sdk.areas.actOf(area)) { + Town.goToTown(sdk.areas.actOf(area)); + Town.move("portalspot"); + } - try { - Attack.kill(split); - Pickit.pickItems(); - } catch (killerror) { - console.error(killerror); + String.isEqual(task, "clearlevel") + ? (area = split) + : (player.area !== area && !player.inTown) && (area = player.area); + + try { + Misc.poll(() => Pather.usePortal(null, player.name), Time.seconds(15), 500 + me.ping); + } catch (e) { + console.warn(e.message || "Failed to take leader portal"); + continue; + } } - break; - case "clearlevel": - try { - console.log("ÿc4MFHelperÿc0: Clear Level " + getAreaName(split)); + if (!me.inTown && me.area === area) { + let forceCast = false; + if (!lastPrecast || getTickCount() - lastPrecast > Time.minutes(2)) { + (forceCast = true) && (lastPrecast = getTickCount()); + } + Precast.doPrecast(forceCast); + } else if (!me.inTown && !me.inArea(player.area)) { + Town.goToTown(sdk.areas.actOf(player.area)); + continue; + } - if (me.area !== split) { - Town.goToTown(sdk.areas.actOf(split)); - Town.move("portalspot"); - if (!Misc.poll(() => Pather.usePortal(split, player.name), Time.seconds(15), 500 + me.ping)) { - throw new Error("Failed to move to clearlevel area"); + switch (task) { + case "kill": + console.log("ÿc4MFHelperÿc0: Kill " + split); + + try { + Attack.kill(split); + Pickit.pickItems(); + } catch (killerror) { + console.error(killerror); + } + + break; + case "clearlevel": + try { + console.log("ÿc4MFHelperÿc0: Clear Level " + getAreaName(split)); + + if (me.area !== split) { + Town.goToTown(sdk.areas.actOf(split)); + Town.move("portalspot"); + if (!Misc.poll(() => Pather.usePortal(split, player.name), Time.seconds(15), 500 + me.ping)) { + throw new Error("Failed to move to clearlevel area"); + } } + Attack.clearLevel(Config.ClearType); + } catch (killerror2) { + console.error(killerror2); } - Attack.clearLevel(Config.ClearType); - } catch (killerror2) { - console.error(killerror2); - } - break; - case "clear": - console.log("ÿc4MFHelperÿc0: Clear " + split); + break; + case "clear": + console.log("ÿc4MFHelperÿc0: Clear " + split); - try { - Attack.clear(15, 0, split); - } catch (killerror2) { - console.error(killerror2); - } + try { + Attack.clear(15, 0, split); + } catch (killerror2) { + console.error(killerror2); + } - break; - } + break; + } - if (!Pather.getPortal(sdk.areas.townOf(me.act)) || !Pather.usePortal(sdk.areas.townOf(me.act))) { - Town.goToTown(); + if (!Pather.getPortal(sdk.areas.townOf(me.act)) || !Pather.usePortal(sdk.areas.townOf(me.act))) { + Town.goToTown(); + } } } } + + delay(100); } - delay(100); + return true; } - - return true; -} +); diff --git a/d2bs/kolbot/libs/scripts/Mausoleum.js b/d2bs/kolbot/libs/scripts/Mausoleum.js index d41330d68..29e4f8331 100644 --- a/d2bs/kolbot/libs/scripts/Mausoleum.js +++ b/d2bs/kolbot/libs/scripts/Mausoleum.js @@ -5,43 +5,46 @@ * */ -function Mausoleum () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ColdPlains); - Precast.doPrecast(true); - - if (Config.Mausoleum.KillBishibosh) { - Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh); - Attack.kill(getLocaleString(sdk.locale.monsters.Bishibosh)); - Pickit.pickItems(); - } - - if (!Pather.moveToExit(sdk.areas.BurialGrounds, true)) throw new Error("Failed to move to Burial Grounds"); - - if (Config.Mausoleum.KillBloodRaven) { - Pather.moveToPresetMonster(sdk.areas.BurialGrounds, sdk.monsters.preset.BloodRaven, { - minDist: me.sorceress && Pather.canTeleport() ? 30 : 5 - }); - Attack.kill(getLocaleString(sdk.locale.monsters.BloodRaven)); - Pickit.pickItems(); - } - - try { - Pather.moveToExit(sdk.areas.Mausoleum, true) && Attack.clearLevel(Config.ClearType); - } catch (e) { - console.error(e); - } - - if (Config.Mausoleum.ClearCrypt) { - // Crypt exit is... awkward - if (!(Pather.moveToExit(sdk.areas.BurialGrounds, true) - && Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Stairs, sdk.exits.preset.Crypt) - && Pather.moveToExit(sdk.areas.Crypt, true))) { - throw new Error("Failed to move to Crypt"); +const Mausoleum = new Runnable( + function Mausoleum () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ColdPlains); + Precast.doPrecast(true); + + if (Config.Mausoleum.KillBishibosh) { + Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh); + Attack.kill(getLocaleString(sdk.locale.monsters.Bishibosh)); + Pickit.pickItems(); } - Attack.clearLevel(Config.ClearType); - } + if (!Pather.moveToExit(sdk.areas.BurialGrounds, true)) throw new Error("Failed to move to Burial Grounds"); - return true; -} + if (Config.Mausoleum.KillBloodRaven) { + Pather.moveToPresetMonster(sdk.areas.BurialGrounds, sdk.monsters.preset.BloodRaven, { + minDist: me.sorceress && Pather.canTeleport() ? 30 : 5 + }); + Attack.kill(getLocaleString(sdk.locale.monsters.BloodRaven)); + Pickit.pickItems(); + } + + try { + Pather.moveToExit(sdk.areas.Mausoleum, true) && Attack.clearLevel(Config.ClearType); + } catch (e) { + console.error(e); + } + + if (Config.Mausoleum.ClearCrypt) { + // Crypt exit is... awkward + if (!(Pather.moveToExit(sdk.areas.BurialGrounds, true) + && Pather.moveToPreset(sdk.areas.BurialGrounds, sdk.unittype.Stairs, sdk.exits.preset.Crypt) + && Pather.moveToExit(sdk.areas.Crypt, true))) { + throw new Error("Failed to move to Crypt"); + } + + Attack.clearLevel(Config.ClearType); + } + + return true; + }, + sdk.areas.ColdPlains +); diff --git a/d2bs/kolbot/libs/scripts/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js index 7890875cb..7e5a4186b 100644 --- a/d2bs/kolbot/libs/scripts/Mephisto.js +++ b/d2bs/kolbot/libs/scripts/Mephisto.js @@ -5,158 +5,161 @@ * */ -function Mephisto () { - // eslint-disable-next-line no-unused-vars - const killMephisto = function () { - let pos = {}; - let attackCount = 0; - let meph = Game.getMonster(sdk.monsters.Mephisto); - if (!meph) throw new Error("Mephisto not found!"); - - Config.MFLeader && Pather.makePortal() && say("kill " + meph.classid); - - while (attackCount < 300 && meph.attackable(meph)) { - if (meph.mode === sdk.monsters.mode.Attacking2) { - let angle = Math.round(Math.atan2(me.y - meph.y, me.x - meph.x) * 180 / Math.PI); - let angles = me.y > meph.y ? [-30, -60, -90] : [30, 60, 90]; - - for (let i = 0; i < angles.length; i += 1) { - pos.dist = 18; - pos.x = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.x); - pos.y = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.y); - - if (Attack.validSpot(pos.x, pos.y)) { - me.overhead("move, bitch!"); - Pather.moveTo(pos.x, pos.y); - - break; +const Mephisto = new Runnable( + function Mephisto () { + // eslint-disable-next-line no-unused-vars + const killMephisto = function () { + let pos = {}; + let attackCount = 0; + let meph = Game.getMonster(sdk.monsters.Mephisto); + if (!meph) throw new Error("Mephisto not found!"); + + Config.MFLeader && Pather.makePortal() && say("kill " + meph.classid); + + while (attackCount < 300 && meph.attackable(meph)) { + if (meph.mode === sdk.monsters.mode.Attacking2) { + let angle = Math.round(Math.atan2(me.y - meph.y, me.x - meph.x) * 180 / Math.PI); + let angles = me.y > meph.y ? [-30, -60, -90] : [30, 60, 90]; + + for (let i = 0; i < angles.length; i += 1) { + pos.dist = 18; + pos.x = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.x); + pos.y = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * pos.dist + meph.y); + + if (Attack.validSpot(pos.x, pos.y)) { + me.overhead("move, bitch!"); + Pather.moveTo(pos.x, pos.y); + + break; + } } } - } - if (ClassAttack.doAttack(meph) < 2) { - break; + if (ClassAttack.doAttack(meph) < 2) { + break; + } + + attackCount += 1; } - attackCount += 1; - } + return meph.dead; + }; + + const moat = function () { + /** + * @param {number} x + * @param {number} y + * @param {number} duration + * @returns {{ x: number, y: number, duration: number }} + */ + const _posObj = (x, y, duration) => ({ x: x, y: y, duration: duration }); + + delay(350); + Pather.moveTo(17563, 8072); + + let mephisto = Game.getMonster(sdk.monsters.Mephisto); + if (!mephisto) throw new Error("Mephisto not found."); + + delay(350); + [ + _posObj(17575, 8086, 350), _posObj(17584, 8091, 1200), + _posObj(17600, 8095, 550), _posObj(17610, 8094, 2500) + ].forEach(pos => Pather.moveTo(pos.x, pos.y) && delay(pos.duration)); - return meph.dead; - }; - - const moat = function () { - /** - * @param {number} x - * @param {number} y - * @param {number} duration - * @returns {{ x: number, y: number, duration: number }} - */ - const _posObj = (x, y, duration) => ({ x: x, y: y, duration: duration }); - - delay(350); - Pather.moveTo(17563, 8072); - - let mephisto = Game.getMonster(sdk.monsters.Mephisto); - if (!mephisto) throw new Error("Mephisto not found."); - - delay(350); - [ - _posObj(17575, 8086, 350), _posObj(17584, 8091, 1200), - _posObj(17600, 8095, 550), _posObj(17610, 8094, 2500) - ].forEach(pos => Pather.moveTo(pos.x, pos.y) && delay(pos.duration)); - - Attack.clear(10); - Pather.moveTo(17610, 8094); - - const _lurePositions = [ - _posObj(17600, 8095, 150), _posObj(17584, 8091, 150), - _posObj(17575, 8086, 150), _posObj(17563, 8072, 350), - _posObj(17575, 8086, 350), _posObj(17584, 8091, 1200), - _posObj(17600, 8095, 550), _posObj(17610, 8094, 2500) - ]; - let count = 0; - let distance = getDistance(me, mephisto); - - while (distance > 27) { - count += 1; - - _lurePositions - .forEach(pos => Pather.moveTo(pos.x, pos.y) && delay(pos.duration)); Attack.clear(10); Pather.moveTo(17610, 8094); - distance = getDistance(me, mephisto); + const _lurePositions = [ + _posObj(17600, 8095, 150), _posObj(17584, 8091, 150), + _posObj(17575, 8086, 150), _posObj(17563, 8072, 350), + _posObj(17575, 8086, 350), _posObj(17584, 8091, 1200), + _posObj(17600, 8095, 550), _posObj(17610, 8094, 2500) + ]; + let count = 0; + let distance = getDistance(me, mephisto); + + while (distance > 27) { + count += 1; + + _lurePositions + .forEach(pos => Pather.moveTo(pos.x, pos.y) && delay(pos.duration)); + Attack.clear(10); + Pather.moveTo(17610, 8094); + + distance = getDistance(me, mephisto); + + if (count >= 5) { + throw new Error("Failed to lure Mephisto."); + } + } + + return true; + }; + + const killCouncil = function () { + const councilMembers = [sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3]; + const coords = [[17600, 8125], [17600, 8015], [17643, 8068]]; - if (count >= 5) { - throw new Error("Failed to lure Mephisto."); + for (let [x, y] of coords) { + Pather.moveTo(x, y); + Attack.clearList(Attack.getMob(councilMembers, 0, 40)); } + + return true; + }; + + Town.doChores(); + Pather.useWaypoint(sdk.areas.DuranceofHateLvl2); + Precast.doPrecast(true); + + if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true)) { + throw new Error("Failed to move to Durance Level 3"); } - return true; - }; + Config.Mephisto.KillCouncil && killCouncil(); - const killCouncil = function () { - const councilMembers = [sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3]; - const coords = [[17600, 8125], [17600, 8015], [17643, 8068]]; + if (Config.Mephisto.TakeRedPortal) { + Pather.moveTo(17590, 8068); + delay(400); // Activate the bridge tile + } else { + Pather.moveTo(17566, 8069); + } - for (let [x, y] of coords) { - Pather.moveTo(x, y); - Attack.clearList(Attack.getMob(councilMembers, 0, 40)); + if (me.sorceress && Config.Mephisto.MoatTrick && Pather.canTeleport()) { + moat(); + Skill.usePvpRange = true; + Attack.kill(sdk.monsters.Mephisto); + Skill.usePvpRange = false; + } else { + Attack.kill(sdk.monsters.Mephisto); } - return true; - }; - - Town.doChores(); - Pather.useWaypoint(sdk.areas.DuranceofHateLvl2); - Precast.doPrecast(true); - - if (!Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true)) { - throw new Error("Failed to move to Durance Level 3"); - } - - Config.Mephisto.KillCouncil && killCouncil(); - - if (Config.Mephisto.TakeRedPortal) { - Pather.moveTo(17590, 8068); - delay(400); // Activate the bridge tile - } else { - Pather.moveTo(17566, 8069); - } - - if (me.sorceress && Config.Mephisto.MoatTrick && Pather.canTeleport()) { - moat(); - Skill.usePvpRange = true; - Attack.kill(sdk.monsters.Mephisto); - Skill.usePvpRange = false; - } else { - Attack.kill(sdk.monsters.Mephisto); - } - - Pickit.pickItems(); - - if (Config.OpenChests.Enabled) { - Pather.moveTo(17572, 8011) && Attack.openChests(5); - Pather.moveTo(17572, 8125) && Attack.openChests(5); - Pather.moveTo(17515, 8061) && Attack.openChests(5); - } - - if (Config.Mephisto.TakeRedPortal) { - Pather.moveTo(17590, 8068); - let tick = getTickCount(), time = 0; - - // Wait until bridge is there - while (getCollision(me.area, 17601, 8070, 17590, 8068) !== 0 - && (time = getTickCount() - tick) < 2000) { - Pather.moveTo(17590, 8068); // Activate it - delay(3); + Pickit.pickItems(); + + if (Config.OpenChests.Enabled) { + Pather.moveTo(17572, 8011) && Attack.openChests(5); + Pather.moveTo(17572, 8125) && Attack.openChests(5); + Pather.moveTo(17515, 8061) && Attack.openChests(5); } - // If bridge is there, and we can move to the location - if (time < 2000 && Pather.moveTo(17601, 8070)) { - Pather.usePortal(null); + if (Config.Mephisto.TakeRedPortal) { + Pather.moveTo(17590, 8068); + let tick = getTickCount(), time = 0; + + // Wait until bridge is there + while (getCollision(me.area, 17601, 8070, 17590, 8068) !== 0 + && (time = getTickCount() - tick) < 2000) { + Pather.moveTo(17590, 8068); // Activate it + delay(3); + } + + // If bridge is there, and we can move to the location + if (time < 2000 && Pather.moveTo(17601, 8070)) { + Pather.usePortal(null); + } } - } - return true; -} + return true; + }, + sdk.areas.DuranceofHateLvl2 +); diff --git a/d2bs/kolbot/libs/scripts/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js index 21a5b8f0d..98c0176e0 100644 --- a/d2bs/kolbot/libs/scripts/Nihlathak.js +++ b/d2bs/kolbot/libs/scripts/Nihlathak.js @@ -5,35 +5,38 @@ * */ -function Nihlathak () { - Town.goToTown(5); - Town.doChores(); - - !Pather.initialized && Pather.useWaypoint(null, true); - - // UseWaypoint if set to or if we already have it - if (Config.Nihlathak.UseWaypoint || getWaypoint(Pather.wpAreas.indexOf(sdk.areas.HallsofPain))) { - Pather.useWaypoint(sdk.areas.HallsofPain); - } else { - if (Pather.journeyTo(sdk.areas.NihlathaksTemple)) { - Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain], true); +const Nihlathak = new Runnable( + function Nihlathak () { + Town.goToTown(5); + Town.doChores(); + + !Pather.initialized && Pather.useWaypoint(null, true); + + // UseWaypoint if set to or if we already have it + if (Config.Nihlathak.UseWaypoint || getWaypoint(Pather.wpAreas.indexOf(sdk.areas.HallsofPain))) { + Pather.useWaypoint(sdk.areas.HallsofPain); + } else { + if (Pather.journeyTo(sdk.areas.NihlathaksTemple)) { + Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain], true); + } } - } - Precast.doPrecast(false); + Precast.doPrecast(false); - if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) throw new Error("Failed to go to Nihlathak"); + if (!Pather.moveToExit(sdk.areas.HallsofVaught, true)) throw new Error("Failed to go to Nihlathak"); - // faster detection of TombVipers - Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { callback: () => { - if (Config.Nihlathak.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { - console.log("Tomb Vipers found."); - throw new ScriptError("Tomb Vipers found."); - } - } }); + // faster detection of TombVipers + Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { callback: () => { + if (Config.Nihlathak.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); + throw new ScriptError("Tomb Vipers found."); + } + } }); - Attack.kill(sdk.monsters.Nihlathak); - Pickit.pickItems(); + Attack.kill(sdk.monsters.Nihlathak); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.Harrogath +); diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index 7db9b72db..bd784ef4b 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -17,575 +17,578 @@ * - bo barb or war cry barb would make killing main boss easier with all the surrounding mobs being stunned */ -function OrgTorch () { - let currentGameInfo = null; +const OrgTorch = new Runnable( + function OrgTorch () { + let currentGameInfo = null; - const portalMode = { - MiniUbers: 0, - UberTristram: 1 - }; + const portalMode = { + MiniUbers: 0, + UberTristram: 1 + }; - const OrgTorchData = { - filePath: "logs/OrgTorch-" + me.profile + ".json", - _default: { gamename: me.gamename, doneAreas: [] }, + const OrgTorchData = { + filePath: "logs/OrgTorch-" + me.profile + ".json", + _default: { gamename: me.gamename, doneAreas: [] }, - create: function () { - FileTools.writeText(this.filePath, JSON.stringify(this._default)); - return this._default; - }, - - read: function () { - let obj = {}; - try { - let string = FileTools.readText(this.filePath); - obj = JSON.parse(string); - } catch (e) { + create: function () { + FileTools.writeText(this.filePath, JSON.stringify(this._default)); return this._default; - } + }, + + read: function () { + let obj = {}; + try { + let string = FileTools.readText(this.filePath); + obj = JSON.parse(string); + } catch (e) { + return this._default; + } - return obj; - }, + return obj; + }, - update: function (newData) { - let data = this.read(); - Object.assign(data, newData); - FileTools.writeText(this.filePath, JSON.stringify(data)); - }, + update: function (newData) { + let data = this.read(); + Object.assign(data, newData); + FileTools.writeText(this.filePath, JSON.stringify(data)); + }, - remove: function () { - return FileTools.remove(this.filePath); - } - }; - - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - const getQuestItem = function (item) { - if (item) { - let id = item.classid; - let canFit = Storage.Inventory.CanFit(item); - if (!canFit && Pickit.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); - Town.visitTown(); - !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); + remove: function () { + return FileTools.remove(this.filePath); } - } - return Pickit.pickItem(item); - }; - - // Identify & mule - const checkTorch = function () { - if (me.inArea(sdk.areas.UberTristram)) { - Pather.moveTo(25105, 5140); - Pather.usePortal(sdk.areas.Harrogath); - } + }; + + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + const getQuestItem = function (item) { + if (item) { + let id = item.classid; + let canFit = Storage.Inventory.CanFit(item); + if (!canFit && Pickit.canMakeRoom()) { + console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); + Town.visitTown(); + !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); + } + } + return Pickit.pickItem(item); + }; + + // Identify & mule + const checkTorch = function () { + if (me.inArea(sdk.areas.UberTristram)) { + Pather.moveTo(25105, 5140); + Pather.usePortal(sdk.areas.Harrogath); + } + + Town.doChores(); - Town.doChores(); + if (!Config.OrgTorch.MakeTorch) return false; - if (!Config.OrgTorch.MakeTorch) return false; + let torch = me.checkItem({ classid: sdk.items.LargeCharm, quality: sdk.items.quality.Unique }); - let torch = me.checkItem({ classid: sdk.items.LargeCharm, quality: sdk.items.quality.Unique }); + if (torch.have && Pickit.checkItem(torch.item).result === Pickit.Result.WANTED) { + if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { + scriptBroadcast("muleTorch"); + scriptBroadcast("quit"); + } - if (torch.have && Pickit.checkItem(torch.item).result === Pickit.Result.WANTED) { - if (AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("torchMuleInfo")) { - scriptBroadcast("muleTorch"); - scriptBroadcast("quit"); + return true; } - return true; - } + return false; + }; + + /** + * Try to lure a monster - wait until it's close enough + * @param {number} bossId + * @returns {boolean} + * @todo redo this + * - should, lure boss AWAY from the others and to us + * - create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses + */ + const lure = function (bossId) { + let unit = Game.getMonster(bossId); + + if (unit) { + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (unit.distance <= 10) { + return true; + } - return false; - }; - - /** - * Try to lure a monster - wait until it's close enough - * @param {number} bossId - * @returns {boolean} - * @todo redo this - * - should, lure boss AWAY from the others and to us - * - create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses - */ - const lure = function (bossId) { - let unit = Game.getMonster(bossId); - - if (unit) { - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (unit.distance <= 10) { - return true; + delay(50); } + } + + return false; + }; + + /** + * Check if we have complete sets of organs + * @returns {boolean} + */ + const completeSetCheck = function () { + let [horns, brains, eyes] = [0, 0, 0]; + me.getItemsEx() + .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) + .forEach(i => { + switch (i.classid) { + case sdk.items.quest.DiablosHorn: + return (horns++); + case sdk.items.quest.MephistosBrain: + return (brains++); + case sdk.items.quest.BaalsEye: + return (eyes++); + default: return 0; + } + }); - delay(50); + if (!horns || !brains || !eyes) { + return false; } - } - return false; - }; + // We just need one set to make a torch + if (Config.OrgTorch.MakeTorch) { + return horns > 0 && brains > 0 && eyes > 0; + } - /** - * Check if we have complete sets of organs - * @returns {boolean} - */ - const completeSetCheck = function () { - let [horns, brains, eyes] = [0, 0, 0]; - me.getItemsEx() - .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) - .forEach(i => { - switch (i.classid) { - case sdk.items.quest.DiablosHorn: - return (horns++); - case sdk.items.quest.MephistosBrain: - return (brains++); - case sdk.items.quest.BaalsEye: - return (eyes++); - default: return 0; + return horns === brains && horns === eyes && brains === eyes; + }; + + /** + * Get fade in River of Flames - only works if we are wearing an item with ctc Fade + * @returns {boolean} + * @todo equipping an item from storage if we have it + */ + const getFade = function () { + if (Config.OrgTorch.GetFade && !me.getState(sdk.states.Fade) + && me.haveSome([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ])) { + console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); + // lets figure out what fade item we have before we leave town + let fadeItem = me.findFirst([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ]); + + Pather.useWaypoint(sdk.areas.RiverofFlame); + Precast.doPrecast(true); + // check if item is on switch + let mainSlot; + + Pather.moveTo(7811, 5872); + + if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { + mainSlot = me.weaponswitch; + me.switchWeapons(sdk.player.slot.Secondary); } - }); - if (!horns || !brains || !eyes) { - return false; - } + Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - // We just need one set to make a torch - if (Config.OrgTorch.MakeTorch) { - return horns > 0 && brains > 0 && eyes > 0; - } + while (!me.getState(sdk.states.Fade)) { + delay(100); + } - return horns === brains && horns === eyes && brains === eyes; - }; - - /** - * Get fade in River of Flames - only works if we are wearing an item with ctc Fade - * @returns {boolean} - * @todo equipping an item from storage if we have it - */ - const getFade = function () { - if (Config.OrgTorch.GetFade && !me.getState(sdk.states.Fade) - && me.haveSome([ - { name: sdk.locale.items.Treachery, equipped: true }, - { name: sdk.locale.items.LastWish, equipped: true }, - { name: sdk.locale.items.SpiritWard, equipped: true } - ])) { - console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); - // lets figure out what fade item we have before we leave town - let fadeItem = me.findFirst([ - { name: sdk.locale.items.Treachery, equipped: true }, - { name: sdk.locale.items.LastWish, equipped: true }, - { name: sdk.locale.items.SpiritWard, equipped: true } - ]); - - Pather.useWaypoint(sdk.areas.RiverofFlame); - Precast.doPrecast(true); - // check if item is on switch - let mainSlot; - - Pather.moveTo(7811, 5872); - - if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { - mainSlot = me.weaponswitch; - me.switchWeapons(sdk.player.slot.Secondary); + mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); + + console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.Green + "Fade Achieved"); } - Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + return true; + }; + + /** + * Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram + * @param {number} mode + * @returns {ObjectUnit | false} + */ + const openPortal = function (mode) { + let item1 = mode === portalMode.MiniUbers + ? me.findItem("pk1", sdk.items.mode.inStorage) + : me.findItem("dhn", sdk.items.mode.inStorage); + let item2 = mode === portalMode.MiniUbers + ? me.findItem("pk2", sdk.items.mode.inStorage) + : me.findItem("bey", sdk.items.mode.inStorage); + let item3 = mode === portalMode.MiniUbers + ? me.findItem("pk3", sdk.items.mode.inStorage) + : me.findItem("mbr", 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); + + let portal = Game.getObject(sdk.objects.RedPortal); - while (!me.getState(sdk.states.Fade)) { - delay(100); + if (portal) { + do { + switch (mode) { + case portalMode.MiniUbers: + if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype) + && currentGameInfo.doneAreas.indexOf(portal.objtype) === -1) { + return copyUnit(portal); + } + + break; + case portalMode.UberTristram: + if (portal.objtype === sdk.areas.UberTristram) { + return copyUnit(portal); + } + + break; + } + } while (portal.getNext()); + } } - mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); + return false; + }; - console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.Green + "Fade Achieved"); - } + const matronsDen = function () { + let dHorns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length; - return true; - }; - - /** - * Open a red portal. Mode 0 = mini ubers, mode 1 = Tristram - * @param {number} mode - * @returns {ObjectUnit | false} - */ - const openPortal = function (mode) { - let item1 = mode === portalMode.MiniUbers - ? me.findItem("pk1", sdk.items.mode.inStorage) - : me.findItem("dhn", sdk.items.mode.inStorage); - let item2 = mode === portalMode.MiniUbers - ? me.findItem("pk2", sdk.items.mode.inStorage) - : me.findItem("bey", sdk.items.mode.inStorage); - let item3 = mode === portalMode.MiniUbers - ? me.findItem("pk3", sdk.items.mode.inStorage) - : me.findItem("mbr", sdk.items.mode.inStorage); + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.MatronsDen, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); + Attack.kill(sdk.monsters.Lilith); + Pickit.pickItems(); + getQuestItem(Game.getItem(sdk.items.quest.DiablosHorn)); + Town.goToTown(); - Town.goToTown(5); - Town.doChores(); + // we sucessfully picked up the horn + return (me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length > dHorns); + }; - if (Town.openStash() && Cubing.emptyCube()) { - if (!Storage.Cube.MoveTo(item1) - || !Storage.Cube.MoveTo(item2) - || !Storage.Cube.MoveTo(item3) - || !Cubing.openCube()) { - return false; - } + const forgottenSands = function () { + let bEyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length; - transmute(); - delay(1000); + Precast.doPrecast(true); - let portal = Game.getObject(sdk.objects.RedPortal); + let nodes = [ + { x: 20196, y: 8694 }, + { x: 20308, y: 8588 }, + { x: 20187, y: 8639 }, + { x: 20100, y: 8550 }, + { x: 20103, y: 8688 }, + { x: 20144, y: 8709 }, + { x: 20263, y: 8811 }, + { x: 20247, y: 8665 }, + ]; - if (portal) { - do { - switch (mode) { - case portalMode.MiniUbers: - if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype) - && currentGameInfo.doneAreas.indexOf(portal.objtype) === -1) { - return copyUnit(portal); - } + try { + for (let i = 0; i < nodes.length; i++) { + Pather.moveTo(nodes[i].x, nodes[i].y); + delay(500); + if (Game.getMonster(sdk.monsters.UberDuriel)) { break; - case portalMode.UberTristram: - if (portal.objtype === sdk.areas.UberTristram) { - return copyUnit(portal); - } + } - break; + let eye = Game.getItem(sdk.items.quest.BaalsEye, sdk.items.mode.onGround); + + if (eye && Pickit.pickItem(eye)) { + throw new Error("Found an picked wanted organ"); } - } while (portal.getNext()); + } + + Attack.kill(sdk.monsters.UberDuriel); + Pickit.pickItems(); + getQuestItem(Game.getItem(sdk.items.quest.BaalsEye)); + Town.goToTown(); + } catch (e) { + // } - } - return false; - }; + // we sucessfully picked up the eye + return (me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length > bEyes); + }; - const matronsDen = function () { - let dHorns = me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length; + const furnance = function () { + let mBrain = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length; - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.MatronsDen, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); - Attack.kill(sdk.monsters.Lilith); - Pickit.pickItems(); - getQuestItem(Game.getItem(sdk.items.quest.DiablosHorn)); - Town.goToTown(); + Precast.doPrecast(true); + Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); + Attack.kill(sdk.monsters.UberIzual); + Pickit.pickItems(); + getQuestItem(Game.getItem(sdk.items.quest.MephistosBrain)); + Town.goToTown(); - // we sucessfully picked up the horn - return (me.findItems(sdk.items.quest.DiablosHorn, sdk.items.mode.inStorage).length > dHorns); - }; + // we sucessfully picked up the brain + return (me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length > mBrain); + }; - const forgottenSands = function () { - let bEyes = me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length; + /** + * @todo re-write this, lure doesn't always work and other classes can do ubers + */ + const uberTrist = function () { + let skillBackup; + let useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); - Precast.doPrecast(true); + Pather.moveTo(25068, 5078); + Precast.doPrecast(true); - let nodes = [ - { x: 20196, y: 8694 }, - { x: 20308, y: 8588 }, - { x: 20187, y: 8639 }, - { x: 20100, y: 8550 }, - { x: 20103, y: 8688 }, - { x: 20144, y: 8709 }, - { x: 20263, y: 8811 }, - { x: 20247, y: 8665 }, - ]; + let nodes = [ + { x: 25040, y: 5101 }, + { x: 25040, y: 5166 }, + { x: 25122, y: 5170 }, + ]; - try { for (let i = 0; i < nodes.length; i++) { Pather.moveTo(nodes[i].x, nodes[i].y); - delay(500); - - if (Game.getMonster(sdk.monsters.UberDuriel)) { - break; - } + } - let eye = Game.getItem(sdk.items.quest.BaalsEye, sdk.items.mode.onGround); + useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + lure(sdk.monsters.UberMephisto); + Pather.moveTo(25129, 5198); + useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + lure(sdk.monsters.UberMephisto); - if (eye && Pickit.pickItem(eye)) { - throw new Error("Found an picked wanted organ"); - } + if (!Game.getMonster(sdk.monsters.UberMephisto)) { + Pather.moveTo(25122, 5170); } - Attack.kill(sdk.monsters.UberDuriel); - Pickit.pickItems(); - getQuestItem(Game.getItem(sdk.items.quest.BaalsEye)); - Town.goToTown(); - } catch (e) { - // - } + if (useSalvation) { + skillBackup = Config.AttackSkill[2]; + Config.AttackSkill[2] = sdk.skills.Salvation; - // we sucessfully picked up the eye - return (me.findItems(sdk.items.quest.BaalsEye, sdk.items.mode.inStorage).length > bEyes); - }; - - const furnance = function () { - let mBrain = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length; - - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); - Attack.kill(sdk.monsters.UberIzual); - Pickit.pickItems(); - getQuestItem(Game.getItem(sdk.items.quest.MephistosBrain)); - Town.goToTown(); - - // we sucessfully picked up the brain - return (me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length > mBrain); - }; - - /** - * @todo re-write this, lure doesn't always work and other classes can do ubers - */ - const uberTrist = function () { - let skillBackup; - let useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); - - Pather.moveTo(25068, 5078); - Precast.doPrecast(true); - - let nodes = [ - { x: 25040, y: 5101 }, - { x: 25040, y: 5166 }, - { x: 25122, y: 5170 }, - ]; - - for (let i = 0; i < nodes.length; i++) { - Pather.moveTo(nodes[i].x, nodes[i].y); - } + Attack.init(); + } - useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - lure(sdk.monsters.UberMephisto); - Pather.moveTo(25129, 5198); - useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - lure(sdk.monsters.UberMephisto); + Attack.kill(sdk.monsters.UberMephisto); - if (!Game.getMonster(sdk.monsters.UberMephisto)) { - Pather.moveTo(25122, 5170); - } + if (skillBackup && useSalvation) { + Config.AttackSkill[2] = skillBackup; - if (useSalvation) { - skillBackup = Config.AttackSkill[2]; - Config.AttackSkill[2] = sdk.skills.Salvation; + Attack.init(); + } - Attack.init(); - } + Pather.moveTo(25162, 5141); + delay(3250); - Attack.kill(sdk.monsters.UberMephisto); + if (!Game.getMonster(sdk.monsters.UberDiablo)) { + Pather.moveTo(25122, 5170); + } - if (skillBackup && useSalvation) { - Config.AttackSkill[2] = skillBackup; + Attack.kill(sdk.monsters.UberDiablo); - Attack.init(); - } + if (!Game.getMonster(sdk.monsters.UberBaal)) { + Pather.moveTo(25122, 5170); + } - Pather.moveTo(25162, 5141); - delay(3250); + Attack.kill(sdk.monsters.UberBaal); + Pickit.pickItems(); + currentGameInfo.doneAreas.push(sdk.areas.UberTristram) && OrgTorchData.update(currentGameInfo); + checkTorch(); + }; + + /** + * Do mini ubers or Tristram based on area we're already in + * @param {number} portalId + */ + const pandemoniumRun = function (portalId) { + switch (me.area) { + case sdk.areas.MatronsDen: + if (matronsDen()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + } - if (!Game.getMonster(sdk.monsters.UberDiablo)) { - Pather.moveTo(25122, 5170); - } + break; + case sdk.areas.ForgottenSands: + if (forgottenSands()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + } - Attack.kill(sdk.monsters.UberDiablo); + break; + case sdk.areas.FurnaceofPain: + if (furnance()) { + currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + } - if (!Game.getMonster(sdk.monsters.UberBaal)) { - Pather.moveTo(25122, 5170); - } + break; + case sdk.areas.UberTristram: + uberTrist(); - Attack.kill(sdk.monsters.UberBaal); - Pickit.pickItems(); - currentGameInfo.doneAreas.push(sdk.areas.UberTristram) && OrgTorchData.update(currentGameInfo); - checkTorch(); - }; - - /** - * Do mini ubers or Tristram based on area we're already in - * @param {number} portalId - */ - const pandemoniumRun = function (portalId) { - switch (me.area) { - case sdk.areas.MatronsDen: - if (matronsDen()) { - currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + break; } + }; - break; - case sdk.areas.ForgottenSands: - if (forgottenSands()) { - currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); + /** + * @param {ObjectUnit} portal + */ + const runEvent = function (portal) { + if (portal) { + if (Config.OrgTorch.PreGame.Antidote.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Antidote.Drink > 0) { + Town.buyPots(Config.OrgTorch.PreGame.Antidote.Drink, "Antidote", true, true); + } + if (Config.OrgTorch.PreGame.Thawing.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Thawing.Drink > 0) { + Town.buyPots(Config.OrgTorch.PreGame.Thawing.Drink, "Thawing", true, true); + } + Town.move("stash"); + console.log("taking portal: " + portal.objtype); + Pather.usePortal(null, null, portal); + pandemoniumRun(portal.objtype); } + }; - break; - case sdk.areas.FurnaceofPain: - if (furnance()) { - currentGameInfo.doneAreas.push(portalId) && OrgTorchData.update(currentGameInfo); - } + // ################# // + /* ##### START ##### */ + // ################# // - break; - case sdk.areas.UberTristram: - uberTrist(); + // make sure we are picking the organs + Config.PickitFiles.length === 0 && NTIP.OpenFile("pickit/keyorg.nip", true); - break; - } - }; - - /** - * @param {ObjectUnit} portal - */ - const runEvent = function (portal) { - if (portal) { - if (Config.OrgTorch.PreGame.Antidote.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Antidote.Drink > 0) { - Town.buyPots(Config.OrgTorch.PreGame.Antidote.Drink, "Antidote", true, true); - } - if (Config.OrgTorch.PreGame.Thawing.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Thawing.Drink > 0) { - Town.buyPots(Config.OrgTorch.PreGame.Thawing.Drink, "Thawing", true, true); - } - Town.move("stash"); - console.log("taking portal: " + portal.objtype); - Pather.usePortal(null, null, portal); - pandemoniumRun(portal.objtype); + FileTools.exists(OrgTorchData.filePath) && (currentGameInfo = OrgTorchData.read()); + + if (!currentGameInfo || currentGameInfo.gamename !== me.gamename) { + currentGameInfo = OrgTorchData.create(); } - }; - - // ################# // - /* ##### START ##### */ - // ################# // - - // make sure we are picking the organs - Config.PickitFiles.length === 0 && NTIP.OpenFile("pickit/keyorg.nip", true); - - FileTools.exists(OrgTorchData.filePath) && (currentGameInfo = OrgTorchData.read()); - - if (!currentGameInfo || currentGameInfo.gamename !== me.gamename) { - currentGameInfo = OrgTorchData.create(); - } - - let portal; - let [tkeys, hkeys, dkeys] = [0, 0, 0]; - let [brains, eyes, horns] = [0, 0, 0]; - - me.getItemsEx() - .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) - .forEach(i => { - switch (i.classid) { - case sdk.items.quest.KeyofTerror: - return (tkeys++); - case sdk.items.quest.KeyofHate: - return (hkeys++); - case sdk.items.quest.KeyofDestruction: - return (dkeys++); - case sdk.items.quest.DiablosHorn: - return (horns++); - case sdk.items.quest.MephistosBrain: - return (brains++); - case sdk.items.quest.BaalsEye: - return (eyes++); - default: return 0; - } - }); - - // Do town chores and quit if MakeTorch is true and we have a torch. - checkTorch(); - - // Wait for other bots to drop off their keys. This works only if TorchSystem.js is configured properly. - Config.OrgTorch.WaitForKeys && TorchSystem.waitForKeys(); - - Town.goToTown(5); - Town.move("stash"); - - let redPortals = getUnits(sdk.unittype.Object, sdk.objects.RedPortal) - .filter(el => [ - sdk.areas.MatronsDen, sdk.areas.ForgottenSands, - sdk.areas.FurnaceofPain, sdk.areas.UberTristram - ].includes(el.objtype)); - let miniPortals = 0; - let keySetsReq = 3; - let tristOpen = false; - - if (redPortals.length > 0) { - redPortals.forEach(function (portal) { - if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype)) { - miniPortals++; - keySetsReq--; - } else if (portal.objtype === sdk.areas.UberTristram) { - tristOpen = true; - } - }); - } else { - // possible same game name but different day and data file never got deleted - currentGameInfo.doneAreas.length > 0 && (currentGameInfo = OrgTorchData.create()); - } - - // End the script if we don't have enough keys nor organs - if ((tkeys < keySetsReq || hkeys < keySetsReq || dkeys < keySetsReq) - && (brains < 1 || eyes < 1 || horns < 1) && !tristOpen) { - console.log("Not enough keys or organs."); - OrgTorchData.remove(); - return true; - } + let portal; + let [tkeys, hkeys, dkeys] = [0, 0, 0]; + let [brains, eyes, horns] = [0, 0, 0]; + + me.getItemsEx() + .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) + .forEach(i => { + switch (i.classid) { + case sdk.items.quest.KeyofTerror: + return (tkeys++); + case sdk.items.quest.KeyofHate: + return (hkeys++); + case sdk.items.quest.KeyofDestruction: + return (dkeys++); + case sdk.items.quest.DiablosHorn: + return (horns++); + case sdk.items.quest.MephistosBrain: + return (brains++); + case sdk.items.quest.BaalsEye: + return (eyes++); + default: return 0; + } + }); - Config.UseMerc = false; + // Do town chores and quit if MakeTorch is true and we have a torch. + checkTorch(); + + // Wait for other bots to drop off their keys. This works only if TorchSystem.js is configured properly. + Config.OrgTorch.WaitForKeys && TorchSystem.waitForKeys(); - // We have enough keys, do mini ubers - if (tkeys >= keySetsReq && hkeys >= keySetsReq && dkeys >= keySetsReq) { - getFade(); Town.goToTown(5); - console.log("Making organs."); - D2Bot.printToConsole("OrgTorch: Making organs.", sdk.colors.D2Bot.DarkGold); Town.move("stash"); - // there are already open portals lets check our info on them - if (miniPortals > 0) { - for (let i = 0; i < miniPortals; i++) { - // mini-portal is up but its not in our done areas, probably chickend during it, lets try again - if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(redPortals[i].objtype) - && !currentGameInfo.doneAreas.includes(redPortals[i].objtype)) { - portal = redPortals[i]; - runEvent(portal); + let redPortals = getUnits(sdk.unittype.Object, sdk.objects.RedPortal) + .filter(el => [ + sdk.areas.MatronsDen, sdk.areas.ForgottenSands, + sdk.areas.FurnaceofPain, sdk.areas.UberTristram + ].includes(el.objtype)); + let miniPortals = 0; + let keySetsReq = 3; + let tristOpen = false; + + if (redPortals.length > 0) { + redPortals.forEach(function (portal) { + if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(portal.objtype)) { + miniPortals++; + keySetsReq--; + } else if (portal.objtype === sdk.areas.UberTristram) { + tristOpen = true; } - } + }); + } else { + // possible same game name but different day and data file never got deleted + currentGameInfo.doneAreas.length > 0 && (currentGameInfo = OrgTorchData.create()); } - for (let i = 0; i < keySetsReq; i += 1) { - // Abort if we have a complete set of organs - // If Config.OrgTorch.MakeTorch is false, check after at least one portal is made - if ((Config.OrgTorch.MakeTorch || i > 0) && completeSetCheck()) { - break; - } + // End the script if we don't have enough keys nor organs + if ((tkeys < keySetsReq || hkeys < keySetsReq || dkeys < keySetsReq) + && (brains < 1 || eyes < 1 || horns < 1) && !tristOpen) { + console.log("Not enough keys or organs."); + OrgTorchData.remove(); - portal = openPortal(portalMode.MiniUbers); - runEvent(portal); + return true; } - } - // Don't make torches if not configured to OR if the char already has one - if (!Config.OrgTorch.MakeTorch || checkTorch()) { - OrgTorchData.remove(); + Config.UseMerc = false; - return true; - } + // We have enough keys, do mini ubers + if (tkeys >= keySetsReq && hkeys >= keySetsReq && dkeys >= keySetsReq) { + getFade(); + Town.goToTown(5); + console.log("Making organs."); + D2Bot.printToConsole("OrgTorch: Making organs.", sdk.colors.D2Bot.DarkGold); + Town.move("stash"); - // Count organs - brains = me.findItems("mbr", sdk.items.mode.inStorage).length || 0; - eyes = me.findItems("bey", sdk.items.mode.inStorage).length || 0; - horns = me.findItems("dhn", sdk.items.mode.inStorage).length || 0; + // there are already open portals lets check our info on them + if (miniPortals > 0) { + for (let i = 0; i < miniPortals; i++) { + // mini-portal is up but its not in our done areas, probably chickend during it, lets try again + if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(redPortals[i].objtype) + && !currentGameInfo.doneAreas.includes(redPortals[i].objtype)) { + portal = redPortals[i]; + runEvent(portal); + } + } + } - // We have enough organs, do Tristram - or trist is open we may have chickened and came back so check it - // if trist was already open when we joined should we run that first? - if ((brains && eyes && horns) || tristOpen) { - getFade(); - Town.goToTown(5); - Town.move("stash"); + for (let i = 0; i < keySetsReq; i += 1) { + // Abort if we have a complete set of organs + // If Config.OrgTorch.MakeTorch is false, check after at least one portal is made + if ((Config.OrgTorch.MakeTorch || i > 0) && completeSetCheck()) { + break; + } - if (!tristOpen) { - console.log("Making torch"); - D2Bot.printToConsole("OrgTorch: Making torch.", sdk.colors.D2Bot.DarkGold); - portal = openPortal(portalMode.UberTristram); - } else { - portal = Pather.getPortal(sdk.areas.UberTristram); + portal = openPortal(portalMode.MiniUbers); + runEvent(portal); + } } - runEvent(portal); - OrgTorchData.remove(); - } + // Don't make torches if not configured to OR if the char already has one + if (!Config.OrgTorch.MakeTorch || checkTorch()) { + OrgTorchData.remove(); + + return true; + } - return true; -} + // Count organs + brains = me.findItems("mbr", sdk.items.mode.inStorage).length || 0; + eyes = me.findItems("bey", sdk.items.mode.inStorage).length || 0; + horns = me.findItems("dhn", sdk.items.mode.inStorage).length || 0; + + // We have enough organs, do Tristram - or trist is open we may have chickened and came back so check it + // if trist was already open when we joined should we run that first? + if ((brains && eyes && horns) || tristOpen) { + getFade(); + Town.goToTown(5); + Town.move("stash"); + + if (!tristOpen) { + console.log("Making torch"); + D2Bot.printToConsole("OrgTorch: Making torch.", sdk.colors.D2Bot.DarkGold); + portal = openPortal(portalMode.UberTristram); + } else { + portal = Pather.getPortal(sdk.areas.UberTristram); + } + + runEvent(portal); + OrgTorchData.remove(); + } + + return true; + }, + sdk.areas.Harrogath +); diff --git a/d2bs/kolbot/libs/scripts/OuterSteppes.js b/d2bs/kolbot/libs/scripts/OuterSteppes.js index 2b5b39463..225e4f43a 100644 --- a/d2bs/kolbot/libs/scripts/OuterSteppes.js +++ b/d2bs/kolbot/libs/scripts/OuterSteppes.js @@ -5,14 +5,17 @@ * */ -function OuterSteppes() { - if (!Town.goToTown(4)) throw new Error("Failed to go to act 4"); - Town.doChores(); - // force random precast because currently bugs if we precast as soon as we go from inTown to out of town - Precast.doRandomPrecast(true); - if (!Pather.journeyTo(sdk.areas.OuterSteppes)) throw new Error("Failed to move to Outer Steppes"); +const OuterSteppes = new Runnable( + function OuterSteppes() { + if (!Town.goToTown(4)) throw new Error("Failed to go to act 4"); + Town.doChores(); + // force random precast because currently bugs if we precast as soon as we go from inTown to out of town + Precast.doRandomPrecast(true); + if (!Pather.journeyTo(sdk.areas.OuterSteppes)) throw new Error("Failed to move to Outer Steppes"); - Attack.clearLevel(Config.ClearType); + Attack.clearLevel(Config.ClearType); - return true; -} + return true; + }, + sdk.areas.PandemoniumFortress +); diff --git a/d2bs/kolbot/libs/scripts/Pindleskin.js b/d2bs/kolbot/libs/scripts/Pindleskin.js index ff4aca090..a5e775405 100644 --- a/d2bs/kolbot/libs/scripts/Pindleskin.js +++ b/d2bs/kolbot/libs/scripts/Pindleskin.js @@ -5,48 +5,51 @@ * */ -function Pindleskin () { - Town.goToTown((Config.Pindleskin.UseWaypoint ? undefined : 5)); - Town.doChores(); +const Pindleskin = new Runnable( + function Pindleskin () { + Town.goToTown((Config.Pindleskin.UseWaypoint ? undefined : 5)); + Town.doChores(); - if (Config.Pindleskin.UseWaypoint) { - Pather.useWaypoint(sdk.areas.HallsofPain); - Precast.doPrecast(true); + if (Config.Pindleskin.UseWaypoint) { + Pather.useWaypoint(sdk.areas.HallsofPain); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.NihlathaksTemple], true)) { - throw new Error("Failed to move to Nihlahak's Temple"); + if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.NihlathaksTemple], true)) { + throw new Error("Failed to move to Nihlahak's Temple"); + } + } else { + if (!Pather.journeyTo(sdk.areas.NihlathaksTemple)) throw new Error("Failed to use portal."); + Precast.doPrecast(true); } - } else { - if (!Pather.journeyTo(sdk.areas.NihlathaksTemple)) throw new Error("Failed to use portal."); - Precast.doPrecast(true); - } - - Pather.moveTo(10058, 13234); - - try { - Attack.kill(getLocaleString(sdk.locale.monsters.Pindleskin)); - } catch (e) { - console.error(e); - } - - if (Config.Pindleskin.KillNihlathak) { - if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.HallsofVaught], true)) { - throw new Error("Failed to move to Halls of Vaught"); + + Pather.moveTo(10058, 13234); + + try { + Attack.kill(getLocaleString(sdk.locale.monsters.Pindleskin)); + } catch (e) { + console.error(e); } - // faster detection of TombVipers - Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { offX: 10, offY: 10, callback: () => { - if (Config.Pindleskin.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { - console.log("Tomb Vipers found."); - throw new ScriptError("Tomb Vipers found."); + if (Config.Pindleskin.KillNihlathak) { + if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.HallsofVaught], true)) { + throw new Error("Failed to move to Halls of Vaught"); } - } }); - Config.Pindleskin.ClearVipers && Attack.clearList(Attack.getMob(sdk.monsters.TombViper2, 0, 20)); + // faster detection of TombVipers + Pather.moveToPresetObject(me.area, sdk.objects.NihlathaksPlatform, { offX: 10, offY: 10, callback: () => { + if (Config.Pindleskin.ViperQuit && Game.getMonster(sdk.monsters.TombViper2)) { + console.log("Tomb Vipers found."); + throw new ScriptError("Tomb Vipers found."); + } + } }); - Attack.kill(sdk.monsters.Nihlathak); - Pickit.pickItems(); - } + Config.Pindleskin.ClearVipers && Attack.clearList(Attack.getMob(sdk.monsters.TombViper2, 0, 20)); + + Attack.kill(sdk.monsters.Nihlathak); + Pickit.pickItems(); + } - return true; -} + return true; + }, + sdk.areas.Harrogath +); diff --git a/d2bs/kolbot/libs/scripts/Pit.js b/d2bs/kolbot/libs/scripts/Pit.js index 300d81476..8079db5ea 100644 --- a/d2bs/kolbot/libs/scripts/Pit.js +++ b/d2bs/kolbot/libs/scripts/Pit.js @@ -5,20 +5,23 @@ * */ -function Pit () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.BlackMarsh); - Precast.doPrecast(true); +const Pit = new Runnable( + function Pit () { + Town.doChores(); + Pather.useWaypoint(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"); - } - Config.Pit.ClearPit1 && Attack.clearLevel(Config.ClearType); + if (!Pather.moveToExit([sdk.areas.TamoeHighland, sdk.areas.PitLvl1], true)) { + throw new Error("Failed to move to Pit level 1"); + } + Config.Pit.ClearPit1 && Attack.clearLevel(Config.ClearType); - if (!Pather.moveToExit(sdk.areas.PitLvl2, true, Config.Pit.ClearPath)) { - throw new Error("Failed to move to Pit level 2"); - } - Attack.clearLevel(); + if (!Pather.moveToExit(sdk.areas.PitLvl2, true, Config.Pit.ClearPath)) { + throw new Error("Failed to move to Pit level 2"); + } + Attack.clearLevel(); - return true; -} + return true; + }, + sdk.areas.BlackMarsh +); diff --git a/d2bs/kolbot/libs/scripts/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js index 9790cc2eb..553ae9a6d 100644 --- a/d2bs/kolbot/libs/scripts/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -7,412 +7,414 @@ // @notes: can't do duriel or meph because all the extra tasks. this is not meant to be autoplay or self rush -function Questing () { - const log = (msg = "", errorMsg = false) => { - me.overhead(msg); - console.log("ÿc9(Questing) :: " + (errorMsg ? "ÿc1" : "ÿc0") + msg); - }; - - /** - * @param {ItemUnit} item - * @returns {boolean} - */ - const getQuestItem = (item) => { - if (item) { - let id = item.classid; - let canFit = Storage.Inventory.CanFit(item); - if (!canFit && Pickit.canMakeRoom()) { - console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); - Town.visitTown(); - !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); - } - } - return Pickit.pickItem(item); - }; - - const den = function () { - log("starting den"); - - Town.doChores(); - if (!Pather.journeyTo(sdk.areas.DenofEvil)) throw new Error("den failed"); - Precast.doPrecast(true); - Attack.clearLevel(); - Town.goToTown() && Town.npcInteract("Akara"); - }; - - const smith = function () { - log("starting smith"); - include("core/Common/Smith.js"); - Common.Smith(); - }; - - const cain = function () { - include("core/Common/Cain.js"); - log("starting cain"); - - Town.doChores(); - Common.Cain.run(); - }; - - const andy = function () { - log("starting andy"); - - Town.doChores(); - if (!Pather.journeyTo(sdk.areas.CatacombsLvl4)) throw new Error("andy failed"); - Pather.moveTo(22582, 9612); - - 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); +const Questing = new Runnable( + function Questing () { + const log = (msg = "", errorMsg = false) => { + me.overhead(msg); + console.log("ÿc9(Questing) :: " + (errorMsg ? "ÿc1" : "ÿc0") + msg); + }; - if (andy && andy.distance < 15) { - break; + /** + * @param {ItemUnit} item + * @returns {boolean} + */ + const getQuestItem = (item) => { + if (item) { + let id = item.classid; + let canFit = Storage.Inventory.CanFit(item); + if (!canFit && Pickit.canMakeRoom()) { + console.log("ÿc7Trying to make room for " + Item.color(item) + item.name); + Town.visitTown(); + !copyUnit(item).x && (item = Misc.poll(() => Game.getItem(id))); } - - Pather.moveToUnit(coords[0]); - Attack.clearClassids(sdk.monsters.DarkShaman1); - coords.shift(); } - } + return Pickit.pickItem(item); + }; - Attack.kill(sdk.monsters.Andariel); - Town.goToTown(); - Town.npcInteract("Warriv", false); - Misc.useMenu(sdk.menu.GoEast); - }; + const den = function () { + log("starting den"); - const radament = function () { - log("starting radament"); + Town.doChores(); + if (!Pather.journeyTo(sdk.areas.DenofEvil)) throw new Error("den failed"); + Precast.doPrecast(true); + Attack.clearLevel(); + Town.goToTown() && Town.npcInteract("Akara"); + }; + + const smith = function () { + log("starting smith"); + include("core/Common/Smith.js"); + Common.Smith(); + }; + + const cain = function () { + include("core/Common/Cain.js"); + log("starting cain"); + + Town.doChores(); + Common.Cain.run(); + }; + + const andy = function () { + log("starting andy"); + + Town.doChores(); + if (!Pather.journeyTo(sdk.areas.CatacombsLvl4)) throw new Error("andy failed"); + Pather.moveTo(22582, 9612); + + 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; + } - if (!Pather.journeyTo(sdk.areas.A2SewersLvl3)) { - throw new Error("radament failed"); - } + Pather.moveToUnit(coords[0]); + Attack.clearClassids(sdk.monsters.DarkShaman1); + coords.shift(); + } + } - Precast.doPrecast(true); + Attack.kill(sdk.monsters.Andariel); + Town.goToTown(); + Town.npcInteract("Warriv", false); + Misc.useMenu(sdk.menu.GoEast); + }; - if (!Pather.moveToPreset(sdk.areas.A2SewersLvl3, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest)) { - throw new Error("radament failed"); - } + const radament = function () { + log("starting radament"); - Attack.kill(sdk.monsters.Radament); + if (!Pather.journeyTo(sdk.areas.A2SewersLvl3)) { + throw new Error("radament failed"); + } - let book = Game.getItem(sdk.quest.item.BookofSkill); - getQuestItem(book); + Precast.doPrecast(true); - Town.goToTown(); - Town.npcInteract("Atma"); - }; + if (!Pather.moveToPreset(sdk.areas.A2SewersLvl3, sdk.unittype.Object, sdk.quest.chest.HoradricScrollChest)) { + throw new Error("radament failed"); + } - const lamEssen = function () { - log("starting lam essen"); + Attack.kill(sdk.monsters.Radament); - if (!Pather.journeyTo(sdk.areas.RuinedTemple)) { - throw new Error("Lam Essen quest failed"); - } + let book = Game.getItem(sdk.quest.item.BookofSkill); + getQuestItem(book); - Precast.doPrecast(true); + Town.goToTown(); + Town.npcInteract("Atma"); + }; - if (!Pather.moveToPreset(sdk.areas.RuinedTemple, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { - throw new Error("Lam Essen quest failed"); - } + const lamEssen = function () { + log("starting lam essen"); - Misc.openChest(sdk.quest.chest.LamEsensTomeHolder); - let book = Misc.poll(() => Game.getItem(sdk.quest.item.LamEsensTome), 1000, 100); - getQuestItem(book); - Town.goToTown(); - Town.npcInteract("Alkor"); - }; - - const izual = function () { - log("starting izual"); - if (!Loader.runScript("Izual")) throw new Error("izual failed"); - Town.goToTown(); - Town.npcInteract("Tyrael"); - }; - - const diablo = function () { - log("starting diablo"); - if (!Loader.runScript("Diablo")) throw new Error(); - Town.goToTown(4); - - Game.getObject(sdk.objects.RedPortalToAct5) - ? Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath) - : Town.npcInteract("Tyrael", false) && Misc.useMenu(sdk.menu.TravelToHarrogath); - }; - - const shenk = function () { - log("starting shenk"); - - if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { - throw new Error("shenk failed"); - } + if (!Pather.journeyTo(sdk.areas.RuinedTemple)) { + throw new Error("Lam Essen quest failed"); + } - Precast.doPrecast(true); - Pather.moveTo(3883, 5113); - Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); - Town.goToTown(); - Town.npcInteract("Larzuk"); - }; + Precast.doPrecast(true); - const barbs = function () { - log("starting barb rescue"); + if (!Pather.moveToPreset(sdk.areas.RuinedTemple, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { + throw new Error("Lam Essen quest failed"); + } - if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { - throw new Error("barbs failed"); - } - Precast.doPrecast(true); + Misc.openChest(sdk.quest.chest.LamEsensTomeHolder); + let book = Misc.poll(() => Game.getItem(sdk.quest.item.LamEsensTome), 1000, 100); + getQuestItem(book); + Town.goToTown(); + Town.npcInteract("Alkor"); + }; + + const izual = function () { + log("starting izual"); + if (!Loader.runScript("Izual")) throw new Error("izual failed"); + Town.goToTown(); + Town.npcInteract("Tyrael"); + }; + + const diablo = function () { + log("starting diablo"); + if (!Loader.runScript("Diablo")) throw new Error(); + Town.goToTown(4); + + Game.getObject(sdk.objects.RedPortalToAct5) + ? Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath) + : Town.npcInteract("Tyrael", false) && Misc.useMenu(sdk.menu.TravelToHarrogath); + }; + + const shenk = function () { + log("starting shenk"); + + if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { + throw new Error("shenk failed"); + } - let barbs = (Game.getPresetObjects(me.area, sdk.quest.chest.BarbCage) || []); - if (!barbs.length) throw new Error("Couldn't find the barbs"); + Precast.doPrecast(true); + Pather.moveTo(3883, 5113); + Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); + Town.goToTown(); + Town.npcInteract("Larzuk"); + }; - let coords = []; + const barbs = function () { + log("starting barb rescue"); - // Dark-f: x-3 - for (let cage = 0; cage < barbs.length; cage += 1) { - coords.push({ - x: barbs[cage].roomx * 5 + barbs[cage].x - 3, - y: barbs[cage].roomy * 5 + barbs[cage].y - }); - } + if (!Pather.useWaypoint(sdk.areas.FrigidHighlands, true)) { + throw new Error("barbs failed"); + } + Precast.doPrecast(true); - for (let i = 0; i < coords.length; i += 1) { - log((i + 1) + "/" + coords.length); - Pather.moveToUnit(coords[i], 2, 0); - let door = Game.getMonster(sdk.monsters.PrisonDoor); + let barbs = (Game.getPresetObjects(me.area, sdk.quest.chest.BarbCage) || []); + if (!barbs.length) throw new Error("Couldn't find the barbs"); - if (door) { - Pather.moveToUnit(door, 1, 0); - Attack.kill(door); - } + let coords = []; - delay(1500 + me.ping); - } + // Dark-f: x-3 + for (let cage = 0; cage < barbs.length; cage += 1) { + coords.push({ + x: barbs[cage].roomx * 5 + barbs[cage].x - 3, + y: barbs[cage].roomy * 5 + barbs[cage].y + }); + } - Town.npcInteract("qual_kehk"); - }; + for (let i = 0; i < coords.length; i += 1) { + log((i + 1) + "/" + coords.length); + Pather.moveToUnit(coords[i], 2, 0); + let door = Game.getMonster(sdk.monsters.PrisonDoor); - const anya = function () { - log("starting anya"); + if (door) { + Pather.moveToUnit(door, 1, 0); + Attack.kill(door); + } - if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { - if (!Pather.journeyTo(sdk.areas.FrozenRiver)) { - throw new Error("anya failed"); + delay(1500 + me.ping); } - Precast.doPrecast(true); + Town.npcInteract("qual_kehk"); + }; - if (!Pather.moveToPreset(sdk.areas.FrozenRiver, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { - throw new Error("Anya quest failed"); - } + const anya = function () { + log("starting anya"); - delay(1000); + if (!Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */)) { + if (!Pather.journeyTo(sdk.areas.FrozenRiver)) { + throw new Error("anya failed"); + } - let frozenanya = Game.getObject(sdk.objects.FrozenAnya); + Precast.doPrecast(true); - /** - * 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); + if (!Pather.moveToPreset(sdk.areas.FrozenRiver, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { + throw new Error("Anya quest failed"); } - Misc.poll(() => getIsTalkingNPC() || frozenanya.mode, 2000, 50); - me.cancel() && me.cancel(); - } - Town.npcInteract("malah"); + delay(1000); - /** - * 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 - + let 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) { - for (let i = 0; i < 3; i++) { - frozenanya.distance > 5 && Pather.moveToUnit(frozenanya, 1, 2); + 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); - 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(); - } } + Misc.poll(() => getIsTalkingNPC() || frozenanya.mode, 2000, 50); + me.cancel() && me.cancel(); } - } - } - /** - * 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"); + + /** + * 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(); + } + } + } + } } /** - * 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"); + * 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 */ + let scroll = me.scrollofresistance; + !!scroll && scroll.use(); } + }; + + // @theBGuy + const ancients = function () { + include("core/Common/Ancients.js"); + log("starting ancients"); + Town.doChores(); + + if (!Pather.journeyTo(sdk.areas.ArreatSummit)) throw new Error("ancients failed"); + + // ancients prep + Town.doChores(); + [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion] + .forEach(p => Town.buyPots(10, p, true)); + + let tempConfig = copyObj(Config); // save and update config settings + let townChicken = getScript("threads/townchicken.js"); + townChicken && townChicken.running && townChicken.stop(); + + Config.TownCheck = false; + Config.MercWatch = false; + Config.TownHP = 0; + Config.TownMP = 0; + Config.HPBuffer = 15; + Config.MPBuffer = 15; + Config.LifeChicken = 10; + + log("updated settings"); + + Town.buyPotions(); + // re-enter Arreat Summit + if (!Pather.usePortal(sdk.areas.ArreatSummit, me.name)) { + log("Failed to take portal back to Arreat Summit", true); + Pather.journeyTo(sdk.areas.ArreatSummit); + } + + Precast.doPrecast(true); - /** Handles using the scroll, no need to repeat the same code here */ - let scroll = me.scrollofresistance; - !!scroll && scroll.use(); - } - }; - - // @theBGuy - const ancients = function () { - include("core/Common/Ancients.js"); - log("starting ancients"); - Town.doChores(); - - if (!Pather.journeyTo(sdk.areas.ArreatSummit)) throw new Error("ancients failed"); - - // ancients prep - Town.doChores(); - [sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion] - .forEach(p => Town.buyPots(10, p, true)); - - let tempConfig = copyObj(Config); // save and update config settings - let townChicken = getScript("threads/townchicken.js"); - townChicken && townChicken.running && townChicken.stop(); - - Config.TownCheck = false; - Config.MercWatch = false; - Config.TownHP = 0; - Config.TownMP = 0; - Config.HPBuffer = 15; - Config.MPBuffer = 15; - Config.LifeChicken = 10; - - log("updated settings"); - - Town.buyPotions(); - // re-enter Arreat Summit - if (!Pather.usePortal(sdk.areas.ArreatSummit, me.name)) { - log("Failed to take portal back to Arreat Summit", true); - Pather.journeyTo(sdk.areas.ArreatSummit); - } - - Precast.doPrecast(true); + // move to altar + if (!Pather.moveToPreset(sdk.areas.ArreatSummit, sdk.unittype.Object, sdk.quest.chest.AncientsAltar)) { + log("Failed to move to ancients' altar", true); + } - // move to altar - if (!Pather.moveToPreset(sdk.areas.ArreatSummit, sdk.unittype.Object, sdk.quest.chest.AncientsAltar)) { - log("Failed to move to ancients' altar", true); - } + Common.Ancients.touchAltar(); + Common.Ancients.startAncients(true); + + me.cancel(); + Config = tempConfig; + log("restored settings"); + Precast.doPrecast(true); - Common.Ancients.touchAltar(); - Common.Ancients.startAncients(true); - - me.cancel(); - Config = tempConfig; - log("restored settings"); - Precast.doPrecast(true); - - // reload town chicken in case we are doing others scripts after this one finishes - let townChick = getScript("threads/TownChicken.js"); - if ((Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick)) { - load("threads/TownChicken.js"); - } + // reload town chicken in case we are doing others scripts after this one finishes + let townChick = getScript("threads/TownChicken.js"); + if ((Config.TownHP > 0 || Config.TownMP > 0) && (townChick && !townChick.running || !townChick)) { + load("threads/TownChicken.js"); + } - try { - if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { - Pather.moveToExit([sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2], true); - Pather.getWP(sdk.areas.WorldstoneLvl2); + try { + if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { + Pather.moveToExit([sdk.areas.WorldstoneLvl1, sdk.areas.WorldstoneLvl2], true); + Pather.getWP(sdk.areas.WorldstoneLvl2); + } + } catch (err) { + log("Cleared Ancients. Failed to get WSK Waypoint", true); } - } catch (err) { - log("Cleared Ancients. Failed to get WSK Waypoint", true); - } - }; + }; - const baal = function () { - log("starting baal"); - // just run baal script? I mean why re-invent the wheel here - Loader.runScript("Baal"); - Town.goToTown(5); - }; + const baal = function () { + log("starting baal"); + // just run baal script? I mean why re-invent the wheel here + Loader.runScript("Baal"); + Town.goToTown(5); + }; - const tasks = (function () { - /** - * @constructor - * @param {function(): void} task - * @param {() => boolean} preReq - * @param {() => boolean} complete - */ - function Task (task, preReq, complete) { - this.run = task; - this.preReq = (preReq || (() => true)); - this.complete = (complete || (() => false)); - } - return [ - new Task(den, () => true, () => me.den), - new Task(smith, () => me.charlvl > 9, () => me.smith || me.imbue), - new Task(cain, () => true, () => me.cain), - new Task(andy, () => true, () => me.andariel), - new Task(radament, () => me.accessToAct(2), () => me.radament), - new Task(lamEssen, () => me.accessToAct(3), () => me.lamessen), - new Task(izual, () => me.accessToAct(4), () => me.izual), - new Task(diablo, () => me.accessToAct(4), () => me.diablo), - new Task(shenk, () => me.accessToAct(5), () => me.shenk || me.larzuk), - new Task(barbs, () => me.accessToAct(5), () => me.barbrescue), - new Task(anya, () => me.accessToAct(5), () => me.anya), - new Task(ancients, () => me.accessToAct(5) && me.charlvl > [20, 40, 60][me.diff], () => me.ancients), - new Task(baal, () => me.accessToAct(5) && me.ancients, () => me.baal), - ]; - })(); - - !me.inTown && Town.doChores(); - - for (let task of tasks) { - if (task.preReq() && !task.complete()) { - try { - task.run(); - } catch (e) { - console.error(e); + const tasks = (function () { + /** + * @constructor + * @param {function(): void} task + * @param {() => boolean} preReq + * @param {() => boolean} complete + */ + function Task (task, preReq, complete) { + this.run = task; + this.preReq = (preReq || (() => true)); + this.complete = (complete || (() => false)); + } + return [ + new Task(den, () => true, () => me.den), + new Task(smith, () => me.charlvl > 9, () => me.smith || me.imbue), + new Task(cain, () => true, () => me.cain), + new Task(andy, () => true, () => me.andariel), + new Task(radament, () => me.accessToAct(2), () => me.radament), + new Task(lamEssen, () => me.accessToAct(3), () => me.lamessen), + new Task(izual, () => me.accessToAct(4), () => me.izual), + new Task(diablo, () => me.accessToAct(4), () => me.diablo), + new Task(shenk, () => me.accessToAct(5), () => me.shenk || me.larzuk), + new Task(barbs, () => me.accessToAct(5), () => me.barbrescue), + new Task(anya, () => me.accessToAct(5), () => me.anya), + new Task(ancients, () => me.accessToAct(5) && me.charlvl > [20, 40, 60][me.diff], () => me.ancients), + new Task(baal, () => me.accessToAct(5) && me.ancients, () => me.baal), + ]; + })(); + + !me.inTown && Town.doChores(); + + for (let task of tasks) { + if (task.preReq() && !task.complete()) { + try { + task.run(); + } catch (e) { + console.error(e); + } } } - } - if (Config.Questing.StopProfile || Loader.scriptList.length === 1) { - D2Bot.printToConsole("All quests done. Stopping profile.", sdk.colors.D2Bot.Green); - D2Bot.stop(); - } else { - log("ÿc9(Questing) :: ÿc2Complete"); - } + if (Config.Questing.StopProfile || Loader.scriptList.length === 1) { + D2Bot.printToConsole("All quests done. Stopping profile.", sdk.colors.D2Bot.Green); + D2Bot.stop(); + } else { + log("ÿc9(Questing) :: ÿc2Complete"); + } - return true; -} + return true; + } +); diff --git a/d2bs/kolbot/libs/scripts/Radament.js b/d2bs/kolbot/libs/scripts/Radament.js index 5a9d175ed..abcb5367d 100644 --- a/d2bs/kolbot/libs/scripts/Radament.js +++ b/d2bs/kolbot/libs/scripts/Radament.js @@ -5,19 +5,22 @@ * */ -function Radament () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.A2SewersLvl2); - Precast.doPrecast(true); +const Radament = new Runnable( + function Radament () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.A2SewersLvl2); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.A2SewersLvl3, true) - || !Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricScrollChest)) { - throw new Error("Failed to move to Radament"); - } + if (!Pather.moveToExit(sdk.areas.A2SewersLvl3, true) + || !Pather.moveToPresetObject(me.area, sdk.quest.chest.HoradricScrollChest)) { + throw new Error("Failed to move to Radament"); + } - Attack.kill(sdk.monsters.Radament); - Pickit.pickItems(); - Attack.openChests(20); + Attack.kill(sdk.monsters.Radament); + Pickit.pickItems(); + Attack.openChests(20); - return true; -} + return true; + }, + sdk.areas.A2SewersLvl2 +); diff --git a/d2bs/kolbot/libs/scripts/Rakanishu.js b/d2bs/kolbot/libs/scripts/Rakanishu.js index 1544cfacb..97e378822 100644 --- a/d2bs/kolbot/libs/scripts/Rakanishu.js +++ b/d2bs/kolbot/libs/scripts/Rakanishu.js @@ -5,22 +5,25 @@ * */ -function Rakanishu () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); +const Rakanishu = new Runnable( + function Rakanishu () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) { - throw new Error("Failed to move to Rakanishu"); - } - Attack.kill(getLocaleString(sdk.locale.monsters.Rakanishu)); + if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) { + throw new Error("Failed to move to Rakanishu"); + } + Attack.kill(getLocaleString(sdk.locale.monsters.Rakanishu)); - if (Config.Rakanishu.KillGriswold && Pather.getPortal(sdk.areas.Tristram)) { - if (!Pather.usePortal(sdk.areas.Tristram)) throw new Error("Failed to move to Tristram"); + if (Config.Rakanishu.KillGriswold && Pather.getPortal(sdk.areas.Tristram)) { + if (!Pather.usePortal(sdk.areas.Tristram)) throw new Error("Failed to move to Tristram"); - Pather.moveTo(25149, 5180); - Attack.clear(20, 0xF, sdk.monsters.Griswold); - } + Pather.moveTo(25149, 5180); + Attack.clear(20, 0xF, sdk.monsters.Griswold); + } - return true; -} + return true; + }, + sdk.areas.StonyField +); diff --git a/d2bs/kolbot/libs/scripts/SealLeecher.js b/d2bs/kolbot/libs/scripts/SealLeecher.js index 6529afbaf..d9cf53b6a 100644 --- a/d2bs/kolbot/libs/scripts/SealLeecher.js +++ b/d2bs/kolbot/libs/scripts/SealLeecher.js @@ -5,95 +5,98 @@ * */ -function SealLeecher() { - let commands = []; +const SealLeecher = new Runnable( + function SealLeecher() { + let commands = []; - Town.goToTown(4); - Town.doChores(); - Town.move("portalspot"); + Town.goToTown(4); + Town.doChores(); + Town.move("portalspot"); - if (!Config.Leader) { - D2Bot.printToConsole("You have to set Config.Leader"); - D2Bot.stop(); + if (!Config.Leader) { + D2Bot.printToConsole("You have to set Config.Leader"); + D2Bot.stop(); - return false; - } - - let chatEvent = function (nick, msg) { - if (nick === Config.Leader) { - commands.push(msg); + return false; } - }; - try { - addEventListener("chatmsg", chatEvent); + let chatEvent = function (nick, msg) { + if (nick === Config.Leader) { + commands.push(msg); + } + }; - // Wait until leader is partied - while (!Misc.inMyParty(Config.Leader)) { - delay(1000); - } + try { + addEventListener("chatmsg", chatEvent); - while (Misc.inMyParty(Config.Leader)) { - if (commands.length > 0) { - let command = commands.shift(); + // Wait until leader is partied + while (!Misc.inMyParty(Config.Leader)) { + delay(1000); + } - switch (command) { - case "in": - if (me.inTown) { - Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); - delay(250); - } + while (Misc.inMyParty(Config.Leader)) { + if (commands.length > 0) { + let command = commands.shift(); - if (getDistance(me, 7761, 5267) < 10) { - Pather.walkTo(7761, 5267, 2); - } + switch (command) { + case "in": + if (me.inTown) { + Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); + delay(250); + } - break; - case "out": - if (!me.inTown) { - Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); - } + if (getDistance(me, 7761, 5267) < 10) { + Pather.walkTo(7761, 5267, 2); + } - break; - case "done": - if (!me.inTown) { - Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); - } + break; + case "out": + if (!me.inTown) { + Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); + } - return true; // End script - } - } + break; + case "done": + if (!me.inTown) { + Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); + } - if (me.dead) { - while (me.mode === sdk.player.mode.Death) { - delay(40); + return true; // End script + } } - me.revive(); + if (me.dead) { + while (me.mode === sdk.player.mode.Death) { + delay(40); + } + + me.revive(); - while (!me.inTown) { - delay(40); + while (!me.inTown) { + delay(40); + } } - } - if (!me.inTown) { - let monster = Game.getMonster(); + if (!me.inTown) { + let monster = Game.getMonster(); - if (monster) { - do { - if (monster.attackable && monster.distance < 20) { - me.overhead("HOT"); - Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); - } - } while (monster.getNext()); + if (monster) { + do { + if (monster.attackable && monster.distance < 20) { + me.overhead("HOT"); + Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader); + } + } while (monster.getNext()); + } } - } - delay(100); + delay(100); + } + } finally { + removeEventListener("chatmsg", chatEvent); } - } finally { - removeEventListener("chatmsg", chatEvent); - } - return true; -} + return true; + }, + sdk.areas.PandemoniumFortress +); diff --git a/d2bs/kolbot/libs/scripts/SharpTooth.js b/d2bs/kolbot/libs/scripts/SharpTooth.js index 9c66a67fa..a5467d65d 100644 --- a/d2bs/kolbot/libs/scripts/SharpTooth.js +++ b/d2bs/kolbot/libs/scripts/SharpTooth.js @@ -5,19 +5,22 @@ * */ -function SharpTooth () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.FrigidHighlands); - Precast.doPrecast(true); +const SharpTooth = new Runnable( + function SharpTooth () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.FrigidHighlands); + Precast.doPrecast(true); - // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); - // Could this be causing crashes here? - if (!Pather.moveToPresetMonster(sdk.areas.FrigidHighlands, sdk.monsters.preset.SharpToothSayer)) { - throw new Error("Failed to move to Sharptooth Slayer"); - } + // FrigidHighlands returns invalid size with getBaseStat('leveldefs', 111, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); + // Could this be causing crashes here? + if (!Pather.moveToPresetMonster(sdk.areas.FrigidHighlands, sdk.monsters.preset.SharpToothSayer)) { + throw new Error("Failed to move to Sharptooth Slayer"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.SharpToothSayer)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.SharpToothSayer)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.FrigidHighlands +); diff --git a/d2bs/kolbot/libs/scripts/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js index b2281ab8f..5ddabb717 100644 --- a/d2bs/kolbot/libs/scripts/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -5,300 +5,302 @@ * */ -function ShopBot () { - const overlayText = { - title: new Text("kolbot shopbot", 50, 245, 2, 1), - cycles: new Text("Cycles in last minute: 0", 50, 260, 2, 1), - frequency: new Text("Valid item frequency: 0", 50, 275, 2, 1), - totalCycles: new Text("Total cycles: 0", 50, 290, 2, 1), - }; - - let tickCount; - let cycles = 0; - let validItems = 0; - let totalCycles = 0; - - /** @type {Array<[(item: ItemUnit) => boolean, (item: ItemUnit) => boolean, (item: ItemUnit) => boolean]>} */ - const pickEntries = []; - /** @type {Object} */ - const npcs = {}; - const wpPresets = { - 1: sdk.objects.A1Waypoint, - 2: sdk.objects.A2Waypoint, - 3: sdk.objects.A3Waypoint, - 4: sdk.objects.A4Waypoint, - 5: sdk.objects.A5Waypoint - }; - const outOfTownWps = { - 1: sdk.areas.CatacombsLvl2, - 2: sdk.areas.A2SewersLvl2, - 3: sdk.areas.DuranceofHateLvl2, - 4: sdk.areas.RiverofFlame, - 5: sdk.areas.CrystalizedPassage - }; - const shopableNPCS = new Map([ - // Act 1 - [NPC.Charsi, { town: sdk.areas.RogueEncampment, menuId: "Repair" }], - [NPC.Akara, { town: sdk.areas.RogueEncampment, menuId: "Shop" }], - [NPC.Gheed, { town: sdk.areas.RogueEncampment, menuId: "Shop" }], - // Act 2 - [NPC.Fara, { town: sdk.areas.LutGholein, menuId: "Repair" }], - [NPC.Elzix, { town: sdk.areas.LutGholein, menuId: "Shop" }], - [NPC.Drognan, { town: sdk.areas.LutGholein, menuId: "Shop" }], - // Act 3 - [NPC.Hratli, { town: sdk.areas.KurastDocktown, menuId: "Repair" }], - [NPC.Asheara, { town: sdk.areas.KurastDocktown, menuId: "Shop" }], - [NPC.Ormus, { town: sdk.areas.KurastDocktown, menuId: "Shop" }], - // Act 4 - [NPC.Halbu, { town: sdk.areas.PandemoniumFortress, menuId: "Repair" }], - [NPC.Jamella, { town: sdk.areas.PandemoniumFortress, menuId: "Shop" }], - // Act 5 - [NPC.Larzuk, { town: sdk.areas.Harrogath, menuId: "Repair" }], - [NPC.Malah, { town: sdk.areas.Harrogath, menuId: "Shop" }], - [NPC.Anya, { town: sdk.areas.Harrogath, menuId: "Shop" }], - [NPC.Nihlathak, { town: sdk.areas.Harrogath, menuId: "Shop" }] - ]); - - const buildPickList = function () { - let nipfile, filepath = "pickit/shopbot.nip"; - let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); - - if (!FileTools.exists(filepath)) { - Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); - return false; - } +const ShopBot = new Runnable( + function ShopBot () { + const overlayText = { + title: new Text("kolbot shopbot", 50, 245, 2, 1), + cycles: new Text("Cycles in last minute: 0", 50, 260, 2, 1), + frequency: new Text("Valid item frequency: 0", 50, 275, 2, 1), + totalCycles: new Text("Total cycles: 0", 50, 290, 2, 1), + }; + + let tickCount; + let cycles = 0; + let validItems = 0; + let totalCycles = 0; + + /** @type {Array<[(item: ItemUnit) => boolean, (item: ItemUnit) => boolean, (item: ItemUnit) => boolean]>} */ + const pickEntries = []; + /** @type {Object} */ + const npcs = {}; + const wpPresets = { + 1: sdk.objects.A1Waypoint, + 2: sdk.objects.A2Waypoint, + 3: sdk.objects.A3Waypoint, + 4: sdk.objects.A4Waypoint, + 5: sdk.objects.A5Waypoint + }; + const outOfTownWps = { + 1: sdk.areas.CatacombsLvl2, + 2: sdk.areas.A2SewersLvl2, + 3: sdk.areas.DuranceofHateLvl2, + 4: sdk.areas.RiverofFlame, + 5: sdk.areas.CrystalizedPassage + }; + const shopableNPCS = new Map([ + // Act 1 + [NPC.Charsi, { town: sdk.areas.RogueEncampment, menuId: "Repair" }], + [NPC.Akara, { town: sdk.areas.RogueEncampment, menuId: "Shop" }], + [NPC.Gheed, { town: sdk.areas.RogueEncampment, menuId: "Shop" }], + // Act 2 + [NPC.Fara, { town: sdk.areas.LutGholein, menuId: "Repair" }], + [NPC.Elzix, { town: sdk.areas.LutGholein, menuId: "Shop" }], + [NPC.Drognan, { town: sdk.areas.LutGholein, menuId: "Shop" }], + // Act 3 + [NPC.Hratli, { town: sdk.areas.KurastDocktown, menuId: "Repair" }], + [NPC.Asheara, { town: sdk.areas.KurastDocktown, menuId: "Shop" }], + [NPC.Ormus, { town: sdk.areas.KurastDocktown, menuId: "Shop" }], + // Act 4 + [NPC.Halbu, { town: sdk.areas.PandemoniumFortress, menuId: "Repair" }], + [NPC.Jamella, { town: sdk.areas.PandemoniumFortress, menuId: "Shop" }], + // Act 5 + [NPC.Larzuk, { town: sdk.areas.Harrogath, menuId: "Repair" }], + [NPC.Malah, { town: sdk.areas.Harrogath, menuId: "Shop" }], + [NPC.Anya, { town: sdk.areas.Harrogath, menuId: "Shop" }], + [NPC.Nihlathak, { town: sdk.areas.Harrogath, menuId: "Shop" }] + ]); + + const buildPickList = function () { + let nipfile, filepath = "pickit/shopbot.nip"; + let filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length); + + if (!FileTools.exists(filepath)) { + Misc.errorReport("ÿc1NIP file doesn't exist: ÿc0" + filepath); + return false; + } - try { - nipfile = File.open(filepath, 0); - } catch (fileError) { - Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); - } + try { + nipfile = File.open(filepath, 0); + } catch (fileError) { + 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) { - let info = { - line: i + 1, - file: filename, - string: lines[i] - }; + for (let i = 0; i < lines.length; i += 1) { + let info = { + line: i + 1, + file: filename, + string: lines[i] + }; - let line = NTIP.ParseLineInt(lines[i], info); - line && pickEntries.push(line); - } + let line = NTIP.ParseLineInt(lines[i], info); + line && pickEntries.push(line); + } - return true; - }; + return true; + }; - /** - * Interact and open the menu of an NPC unit - * @param {NPCUnit} npc - * @returns {boolean} - */ - const openMenu = function (npc) { - if (!npc || npc.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); + /** + * Interact and open the menu of an NPC unit + * @param {NPCUnit} npc + * @returns {boolean} + */ + const openMenu = function (npc) { + if (!npc || npc.type !== sdk.unittype.NPC) throw new Error("Unit.openMenu: Must be used on NPCs."); - let interactedNPC = getInteractedNPC(); + let interactedNPC = getInteractedNPC(); - if (interactedNPC && interactedNPC.name !== npc.name) { - Packet.cancelNPC(interactedNPC); - me.cancel(); - } + if (interactedNPC && interactedNPC.name !== npc.name) { + Packet.cancelNPC(interactedNPC); + me.cancel(); + } - if (getUIFlag(sdk.uiflags.NPCMenu)) return true; + if (getUIFlag(sdk.uiflags.NPCMenu)) return true; - for (let i = 0; i < 10; i += 1) { - npc.distance > 5 && Pather.walkTo(npc.x, npc.y); + for (let i = 0; i < 10; i += 1) { + npc.distance > 5 && Pather.walkTo(npc.x, npc.y); - if (!getUIFlag(sdk.uiflags.NPCMenu)) { - Packet.entityInteract(npc); - Packet.initNPC(npc); - } + if (!getUIFlag(sdk.uiflags.NPCMenu)) { + Packet.entityInteract(npc); + Packet.initNPC(npc); + } - let tick = getTickCount(); + let tick = getTickCount(); - while (getTickCount() - tick < Math.max(Math.round((i + 1) * 250 / (i / 3 + 1)), me.ping + 1)) { - if (getUIFlag(sdk.uiflags.NPCMenu)) { - return true; - } + while (getTickCount() - tick < Math.max(Math.round((i + 1) * 250 / (i / 3 + 1)), me.ping + 1)) { + if (getUIFlag(sdk.uiflags.NPCMenu)) { + return true; + } - delay(10); + delay(10); + } } - } - me.cancel(); - - return false; - }; - - /** - * @param {NPCUnit} npc - * @param {number} menuId - * @returns {boolean} - */ - const shopItems = function (npc, menuId) { - if (!Storage.Inventory.CanFit({ sizex: 2, sizey: 4 }) && AutoMule.getMuleItems().length > 0) { - D2Bot.printToConsole("Mule triggered"); - scriptBroadcast("mule"); - scriptBroadcast("quit"); - return true; - } + me.cancel(); - if (!npc) return false; + return false; + }; + + /** + * @param {NPCUnit} npc + * @param {number} menuId + * @returns {boolean} + */ + const shopItems = function (npc, menuId) { + if (!Storage.Inventory.CanFit({ sizex: 2, sizey: 4 }) && AutoMule.getMuleItems().length > 0) { + D2Bot.printToConsole("Mule triggered"); + scriptBroadcast("mule"); + scriptBroadcast("quit"); + return true; + } - for (let i = 0; i < 10; i += 1) { - delay(150); + if (!npc) return false; - i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, 1, 4, npc.gid, 4, 0); + for (let i = 0; i < 10; i += 1) { + delay(150); - if (npc.itemcount > 0) { - break; - } - } + i % 2 === 0 && sendPacket(1, sdk.packets.send.EntityAction, 4, 1, 4, npc.gid, 4, 0); - let items = npc.getItemsEx().filter(function (item) { - return (Config.ShopBot.ScanIDs.includes(item.classid) || Config.ShopBot.ScanIDs.length === 0); - }); - if (!items.length) return false; - - me.overhead(npc.itemcount + " items, " + items.length + " valid"); - - let bought; - validItems += items.length; - overlayText.frequency.text = "Valid base items / cycle: " + ((validItems / totalCycles).toFixed(2).toString()); - - for (let i = 0; i < items.length; i += 1) { - if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && - me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && - NTIP.CheckItem(items[i], pickEntries) - ) { - beep(); - D2Bot.printToConsole("Match found!", sdk.colors.D2Bot.DarkGold); - delay(1000); - - if (npc.startTrade(menuId)) { - Item.logItem("Shopped", items[i]); - items[i].buy(); - bought = true; + if (npc.itemcount > 0) { + break; } + } - Config.ShopBot.QuitOnMatch && scriptBroadcast("quit"); + let items = npc.getItemsEx().filter(function (item) { + return (Config.ShopBot.ScanIDs.includes(item.classid) || Config.ShopBot.ScanIDs.length === 0); + }); + if (!items.length) return false; + + me.overhead(npc.itemcount + " items, " + items.length + " valid"); + + let bought; + validItems += items.length; + overlayText.frequency.text = "Valid base items / cycle: " + ((validItems / totalCycles).toFixed(2).toString()); + + for (let i = 0; i < items.length; i += 1) { + if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && + me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && + NTIP.CheckItem(items[i], pickEntries) + ) { + beep(); + D2Bot.printToConsole("Match found!", sdk.colors.D2Bot.DarkGold); + delay(1000); + + if (npc.startTrade(menuId)) { + Item.logItem("Shopped", items[i]); + items[i].buy(); + bought = true; + } + + Config.ShopBot.QuitOnMatch && scriptBroadcast("quit"); + } } - } - if (bought) { - me.cancelUIFlags(); - Town.stash(); - } + if (bought) { + me.cancelUIFlags(); + Town.stash(); + } - return true; - }; - - /** - * @param {string} name - * @returns {boolean} - */ - const shopAtNPC = function (name) { - if (!shopableNPCS.has(name)) { - throw new Error("Invalid NPC"); - } + return true; + }; + + /** + * @param {string} name + * @returns {boolean} + */ + const shopAtNPC = function (name) { + if (!shopableNPCS.has(name)) { + throw new Error("Invalid NPC"); + } - const { town, menuId } = shopableNPCS.get(name); + const { town, menuId } = shopableNPCS.get(name); - if (!me.inArea(town) && !Pather.useWaypoint(town)) return false; + if (!me.inArea(town) && !Pather.useWaypoint(town)) return false; - let npc = npcs[name] || Game.getNPC(name); + let npc = npcs[name] || Game.getNPC(name); - if (!npc || npc.type !== sdk.unittype.NPC || npc.distance > 5) { - npc = Town.npcInteract(name); - } + if (!npc || npc.type !== sdk.unittype.NPC || npc.distance > 5) { + npc = Town.npcInteract(name); + } - if (!npc) return false; + if (!npc) return false; - !npcs[name] && (npcs[name] = copyUnit(npc)); - Config.ShopBot.CycleDelay && delay(Config.ShopBot.CycleDelay); - openMenu(npc) && shopItems(npc, menuId); + !npcs[name] && (npcs[name] = copyUnit(npc)); + Config.ShopBot.CycleDelay && delay(Config.ShopBot.CycleDelay); + openMenu(npc) && shopItems(npc, menuId); - return true; - }; - - // START - for (let i = 0; i < Config.ShopBot.ScanIDs.length; i += 1) { - if (isNaN(Config.ShopBot.ScanIDs[i])) { - if (NTIPAliasClassID.hasOwnProperty(Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase())) { - Config.ShopBot.ScanIDs[i] = NTIPAliasClassID[Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase()]; - } else { - Misc.errorReport("ÿc1Invalid ShopBot entry:ÿc0 " + Config.ShopBot.ScanIDs[i]); - Config.ShopBot.ScanIDs.splice(i, 1); - i -= 1; + return true; + }; + + // START + for (let i = 0; i < Config.ShopBot.ScanIDs.length; i += 1) { + if (isNaN(Config.ShopBot.ScanIDs[i])) { + if (NTIPAliasClassID.hasOwnProperty(Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase())) { + Config.ShopBot.ScanIDs[i] = NTIPAliasClassID[Config.ShopBot.ScanIDs[i].replace(/\s+/g, "").toLowerCase()]; + } else { + Misc.errorReport("ÿc1Invalid ShopBot entry:ÿc0 " + Config.ShopBot.ScanIDs[i]); + Config.ShopBot.ScanIDs.splice(i, 1); + i -= 1; + } } } - } - typeof Config.ShopBot.ShopNPC === "string" && (Config.ShopBot.ShopNPC = [Config.ShopBot.ShopNPC]); + typeof Config.ShopBot.ShopNPC === "string" && (Config.ShopBot.ShopNPC = [Config.ShopBot.ShopNPC]); - for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { - Config.ShopBot.ShopNPC[i] = Config.ShopBot.ShopNPC[i].toLowerCase(); - } + for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { + Config.ShopBot.ShopNPC[i] = Config.ShopBot.ShopNPC[i].toLowerCase(); + } - if (Config.ShopBot.MinGold && me.gold < Config.ShopBot.MinGold) return true; + if (Config.ShopBot.MinGold && me.gold < Config.ShopBot.MinGold) return true; - buildPickList(); - console.log("Shopbot: Pickit entries: " + pickEntries.length); - Town.doChores(); + buildPickList(); + console.log("Shopbot: Pickit entries: " + pickEntries.length); + Town.doChores(); - Pather.teleport = false; - tickCount = getTickCount(); + Pather.teleport = false; + tickCount = getTickCount(); - while (!Config.ShopBot.Cycles || totalCycles < Config.ShopBot.Cycles) { - if (getTickCount() - tickCount >= 60 * 1000) { - overlayText.cycles.text = "Cycles in last minute: " + cycles.toString(); - overlayText.totalCycles.text = "Total cycles: " + totalCycles.toString(); - cycles = 0; - tickCount = getTickCount(); - } + while (!Config.ShopBot.Cycles || totalCycles < Config.ShopBot.Cycles) { + if (getTickCount() - tickCount >= 60 * 1000) { + overlayText.cycles.text = "Cycles in last minute: " + cycles.toString(); + overlayText.totalCycles.text = "Total cycles: " + totalCycles.toString(); + cycles = 0; + tickCount = getTickCount(); + } - for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { - shopAtNPC(Config.ShopBot.ShopNPC[i]); - } + for (let i = 0; i < Config.ShopBot.ShopNPC.length; i += 1) { + shopAtNPC(Config.ShopBot.ShopNPC[i]); + } - if (me.inTown) { - let area = getArea(); - const wp = Game.getPresetObject(me.area, wpPresets[me.act]).realCoords(); - const redPortal = (getUnits(sdk.unittype.Object, sdk.objects.RedPortal) - .sort((a, b) => a.distance - b.distance)) - .first(); - let exit = area.exits[0]; - - for (let i = 1; i < area.exits.length; i++) { - if (getDistance(me, exit) > getDistance(me, area.exits[i])) { - exit = area.exits[i]; + if (me.inTown) { + let area = getArea(); + const wp = Game.getPresetObject(me.area, wpPresets[me.act]).realCoords(); + const redPortal = (getUnits(sdk.unittype.Object, sdk.objects.RedPortal) + .sort((a, b) => a.distance - b.distance)) + .first(); + let exit = area.exits[0]; + + for (let i = 1; i < area.exits.length; i++) { + if (getDistance(me, exit) > getDistance(me, area.exits[i])) { + exit = area.exits[i]; + } } - } - if (!!redPortal && redPortal.distance < 20 && Pather.usePortal(null, null, redPortal)) { - delay(3000); - Pather.usePortal(sdk.areas.townOf(me.area)); + if (!!redPortal && redPortal.distance < 20 && Pather.usePortal(null, null, redPortal)) { + delay(3000); + Pather.usePortal(sdk.areas.townOf(me.area)); - if (totalCycles === 0) { - delay(10000); - } + if (totalCycles === 0) { + delay(10000); + } - delay(1500); - } else if (getDistance(me, exit) < (getDistance(me, wp.x, wp.y) + 6)) { - Pather.moveToExit(me.area + 1, true); - Pather.moveToExit(me.area - 1, true); - } else { - Pather.useWaypoint(outOfTownWps[me.act]); + delay(1500); + } else if (getDistance(me, exit) < (getDistance(me, wp.x, wp.y) + 6)) { + Pather.moveToExit(me.area + 1, true); + Pather.moveToExit(me.area - 1, true); + } else { + Pather.useWaypoint(outOfTownWps[me.act]); + } } + + cycles += 1; + totalCycles += 1; } - cycles += 1; - totalCycles += 1; + return true; } - - return true; -} +); diff --git a/d2bs/kolbot/libs/scripts/Smith.js b/d2bs/kolbot/libs/scripts/Smith.js index 00799d314..73a12baba 100644 --- a/d2bs/kolbot/libs/scripts/Smith.js +++ b/d2bs/kolbot/libs/scripts/Smith.js @@ -5,17 +5,20 @@ * */ -function Smith () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.OuterCloister); - Precast.doPrecast(true); +const Smith = new Runnable( + function Smith () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.OuterCloister); + Precast.doPrecast(true); - if (!Pather.moveToPresetObject(sdk.areas.Barracks, sdk.quest.chest.MalusHolder)) { - throw new Error("Failed to move to the Smith"); - } + if (!Pather.moveToPresetObject(sdk.areas.Barracks, sdk.quest.chest.MalusHolder)) { + throw new Error("Failed to move to the Smith"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.OuterCloister +); diff --git a/d2bs/kolbot/libs/scripts/Snapchip.js b/d2bs/kolbot/libs/scripts/Snapchip.js index b116164d9..173633107 100644 --- a/d2bs/kolbot/libs/scripts/Snapchip.js +++ b/d2bs/kolbot/libs/scripts/Snapchip.js @@ -5,18 +5,21 @@ * */ -function Snapchip () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.AncientsWay); - Precast.doPrecast(true); +const Snapchip = new Runnable( + function Snapchip () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.AncientsWay); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.IcyCellar, true) - || !Pather.moveToPresetObject(me.area, sdk.objects.SmallSparklyChest)) { - throw new Error("Failed to move to Snapchip Shatter"); - } + if (!Pather.moveToExit(sdk.areas.IcyCellar, true) + || !Pather.moveToPresetObject(me.area, sdk.objects.SmallSparklyChest)) { + throw new Error("Failed to move to Snapchip Shatter"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.SnapchipShatter)); - Config.Snapchip.ClearIcyCellar && Attack.clearLevel(Config.ClearType); + Attack.kill(getLocaleString(sdk.locale.monsters.SnapchipShatter)); + Config.Snapchip.ClearIcyCellar && Attack.clearLevel(Config.ClearType); - return true; -} + return true; + }, + sdk.areas.AncientsWay +); diff --git a/d2bs/kolbot/libs/scripts/Stormtree.js b/d2bs/kolbot/libs/scripts/Stormtree.js index 17a7fe9ce..125dca98e 100644 --- a/d2bs/kolbot/libs/scripts/Stormtree.js +++ b/d2bs/kolbot/libs/scripts/Stormtree.js @@ -5,16 +5,19 @@ * */ -function Stormtree () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.LowerKurast); - Precast.doPrecast(true); +const Stormtree = new Runnable( + function Stormtree () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.LowerKurast); + Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.FlayerJungle, true)) { - throw new Error("Failed to move to Stormtree"); - } + if (!Pather.moveToExit(sdk.areas.FlayerJungle, true)) { + throw new Error("Failed to move to Stormtree"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.Stormtree)); + Attack.kill(getLocaleString(sdk.locale.monsters.Stormtree)); - return true; -} + return true; + }, + sdk.areas.LowerKurast +); diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index ff3a3f32e..9e739f5fc 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -5,61 +5,64 @@ * */ -function Summoner () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArcaneSanctuary); - Precast.doPrecast(true); +const Summoner = new Runnable( + function Summoner () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArcaneSanctuary); + Precast.doPrecast(true); - if (Config.Summoner.FireEye) { - try { - if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); - } catch (e) { - console.error(e); + if (Config.Summoner.FireEye) { + try { + if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); + } catch (e) { + console.error(e); + } } - } - if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) { - throw new Error("Failed to move back to arcane"); - } - if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.Journal, { offX: -3, offY: -3 })) { - throw new Error("Failed to move to Summoner"); - } + if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) { + throw new Error("Failed to move back to arcane"); + } + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.Journal, { offX: -3, offY: -3 })) { + throw new Error("Failed to move to Summoner"); + } - Attack.clear(15, 0, sdk.monsters.TheSummoner); + Attack.clear(15, 0, sdk.monsters.TheSummoner); - // always take portal, faster access to wp - // first check if portal is already up - let portal = Game.getObject(sdk.objects.RedPortal); + // always take portal, faster access to wp + // first check if portal is already up + let portal = Game.getObject(sdk.objects.RedPortal); - if (!portal || !Pather.usePortal(null, null, portal)) { - for (let i = 0; i < 5; i++) { - // couldn't find portal, attempt to interact with journal - let journal = Game.getObject(sdk.objects.Journal); + if (!portal || !Pather.usePortal(null, null, portal)) { + for (let i = 0; i < 5; i++) { + // couldn't find portal, attempt to interact with journal + let journal = Game.getObject(sdk.objects.Journal); - // couldnt find journal? Move to it's preset - if (!journal) { - Pather.moveToPresetObject(me.area, sdk.objects.Journal); - continue; - } else if (journal && journal.distance > (18 - i)) { - Pather.moveNearUnit(journal, 13); - } + // couldnt find journal? Move to it's preset + if (!journal) { + Pather.moveToPresetObject(me.area, sdk.objects.Journal); + continue; + } else if (journal && journal.distance > (18 - i)) { + Pather.moveNearUnit(journal, 13); + } - Packet.entityInteract(journal); - Misc.poll(() => getIsTalkingNPC() || Game.getObject(sdk.objects.RedPortal), 2000, 200); - me.cancel() && me.cancel(); + Packet.entityInteract(journal); + Misc.poll(() => getIsTalkingNPC() || Game.getObject(sdk.objects.RedPortal), 2000, 200); + me.cancel() && me.cancel(); - if (Pather.usePortal(sdk.areas.CanyonofMagic)) { - break; + if (Pather.usePortal(sdk.areas.CanyonofMagic)) { + break; + } } } - } - if (me.inArea(sdk.areas.CanyonofMagic)) { - Loader.scriptName(1) === "Duriel" - ? Loader.skipTown.push("Duriel") - : Pather.useWaypoint(sdk.areas.LutGholein); - } + if (me.inArea(sdk.areas.CanyonofMagic)) { + Loader.scriptName(1) === "Duriel" + ? Loader.skipTown.push("Duriel") + : Pather.useWaypoint(sdk.areas.LutGholein); + } - return true; -} + return true; + }, + sdk.areas.ArcaneSanctuary +); diff --git a/d2bs/kolbot/libs/scripts/ThreshSocket.js b/d2bs/kolbot/libs/scripts/ThreshSocket.js index 4159b0008..2a7ec75a9 100644 --- a/d2bs/kolbot/libs/scripts/ThreshSocket.js +++ b/d2bs/kolbot/libs/scripts/ThreshSocket.js @@ -5,17 +5,20 @@ * */ -function ThreshSocket () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArreatPlateau); - Precast.doPrecast(true); +const ThreshSocket = new Runnable( + function ThreshSocket () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.ArreatPlateau); + Precast.doPrecast(true); - // ArreatPlateau returns invalid size with getBaseStat('leveldefs', 112, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); - // Could this be causing crashes here? Would it be better to go from crystal to Arreat instead? - if (!Pather.moveToExit(sdk.areas.CrystalizedPassage, false)) throw new Error("Failed to move to Thresh Socket"); + // ArreatPlateau returns invalid size with getBaseStat('leveldefs', 112, ['SizeX', 'SizeX(N)', 'SizeX(H)'][me.diff]); + // Could this be causing crashes here? Would it be better to go from crystal to Arreat instead? + if (!Pather.moveToExit(sdk.areas.CrystalizedPassage, false)) throw new Error("Failed to move to Thresh Socket"); - Attack.kill(getLocaleString(sdk.locale.monsters.ThreshSocket)); - Pickit.pickItems(); + Attack.kill(getLocaleString(sdk.locale.monsters.ThreshSocket)); + Pickit.pickItems(); - return true; -} + return true; + }, + sdk.areas.ArreatPlateau +); diff --git a/d2bs/kolbot/libs/scripts/Tombs.js b/d2bs/kolbot/libs/scripts/Tombs.js index 54bd5a741..2c06ef2a6 100644 --- a/d2bs/kolbot/libs/scripts/Tombs.js +++ b/d2bs/kolbot/libs/scripts/Tombs.js @@ -5,28 +5,31 @@ * */ -function Tombs() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CanyonofMagic); - Precast.doPrecast(true); - const correctTomb = getRoom().correcttomb; +const Tombs = new Runnable( + function Tombs() { + Town.doChores(); + Pather.useWaypoint(sdk.areas.CanyonofMagic); + Precast.doPrecast(true); + const correctTomb = getRoom().correcttomb; - for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i++) { - try { - if (!Pather.journeyTo(i, true)) throw new Error("Failed to move to tomb"); - - Attack.clearLevel(Config.ClearType); - - if (Config.Tombs.KillDuriel && me.inArea(correctTomb)) { - Pather.journeyTo(sdk.areas.DurielsLair) && Attack.kill(sdk.monsters.Duriel); - Pather.journeyTo(sdk.areas.CanyonofMagic); + for (let i = sdk.areas.TalRashasTomb1; i <= sdk.areas.TalRashasTomb7; i++) { + try { + if (!Pather.journeyTo(i, true)) throw new Error("Failed to move to tomb"); + + Attack.clearLevel(Config.ClearType); + + if (Config.Tombs.KillDuriel && me.inArea(correctTomb)) { + Pather.journeyTo(sdk.areas.DurielsLair) && Attack.kill(sdk.monsters.Duriel); + Pather.journeyTo(sdk.areas.CanyonofMagic); + } + + if (!Pather.moveToExit(sdk.areas.CanyonofMagic, true)) throw new Error("Failed to move to Canyon"); + } catch (e) { + console.error(e); } - - if (!Pather.moveToExit(sdk.areas.CanyonofMagic, true)) throw new Error("Failed to move to Canyon"); - } catch (e) { - console.error(e); } - } - return true; -} + return true; + }, + sdk.areas.CanyonofMagic +); diff --git a/d2bs/kolbot/libs/scripts/Travincal.js b/d2bs/kolbot/libs/scripts/Travincal.js index e48cd3bbd..13450e9a4 100644 --- a/d2bs/kolbot/libs/scripts/Travincal.js +++ b/d2bs/kolbot/libs/scripts/Travincal.js @@ -5,66 +5,69 @@ * */ -function Travincal () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.Travincal); - Precast.doPrecast(true); +const Travincal = new Runnable( + function Travincal () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.Travincal); + Precast.doPrecast(true); - const [orgX, orgY] = [me.x, me.y]; + const [orgX, orgY] = [me.x, me.y]; - /** @param {Monster} mon */ - const councilMember = (mon) => ( - [sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].includes(mon.classid) - ); + /** @param {Monster} mon */ + const councilMember = (mon) => ( + [sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].includes(mon.classid) + ); - if (Config.Travincal.PortalLeech) { - Pather.moveTo(orgX + 85, orgY - 139); - Attack.securePosition(orgX + 70, orgY - 139, 25, 2000); - Attack.securePosition(orgX + 100, orgY - 139, 25, 2000); - Attack.securePosition(orgX + 85, orgY - 139, 25, 5000); - Pather.moveTo(orgX + 85, orgY - 139); - Pather.makePortal(); - delay(1000); - Precast.doPrecast(true); - } + if (Config.Travincal.PortalLeech) { + Pather.moveTo(orgX + 85, orgY - 139); + Attack.securePosition(orgX + 70, orgY - 139, 25, 2000); + Attack.securePosition(orgX + 100, orgY - 139, 25, 2000); + Attack.securePosition(orgX + 85, orgY - 139, 25, 5000); + Pather.moveTo(orgX + 85, orgY - 139); + Pather.makePortal(); + delay(1000); + Precast.doPrecast(true); + } - if (Skill.canUse(sdk.skills.LeapAttack) && !Pather.canTeleport()) { - const coords = [[60, -53], [64, -72], [78, -72], [74, -88]]; + if (Skill.canUse(sdk.skills.LeapAttack) && !Pather.canTeleport()) { + const coords = [[60, -53], [64, -72], [78, -72], [74, -88]]; - for (let i = 0; i < coords.length; i++) { - let [x, y] = coords[i]; + for (let i = 0; i < coords.length; i++) { + let [x, y] = coords[i]; - if (i % 2 === 0) { - Pather.moveTo(orgX + x, orgY + y); - } else { - Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, orgX + x, orgY + y); - Attack.clearList( - Attack.buildMonsterList( - /** @param {Monster} mon */ - (mon) => councilMember(mon) && !checkCollision(me, mon, sdk.collision.BlockWall) - ) - ); + if (i % 2 === 0) { + Pather.moveTo(orgX + x, orgY + y); + } else { + Skill.cast(sdk.skills.LeapAttack, sdk.skills.hand.Right, orgX + x, orgY + y); + Attack.clearList( + Attack.buildMonsterList( + /** @param {Monster} mon */ + (mon) => councilMember(mon) && !checkCollision(me, mon, sdk.collision.BlockWall) + ) + ); + } } - } - Attack.clearList(Attack.buildMonsterList(councilMember)); - } else { - Pather.moveTo(orgX + 101, orgY - 56); + Attack.clearList(Attack.buildMonsterList(councilMember)); + } else { + Pather.moveTo(orgX + 101, orgY - 56); - // Stack Merc - if (me.barbarian && !Pather.canTeleport() && me.expansion) { - Pather.moveToExit([sdk.areas.DuranceofHateLvl1, sdk.areas.Travincal], true); - } + // Stack Merc + if (me.barbarian && !Pather.canTeleport() && me.expansion) { + Pather.moveToExit([sdk.areas.DuranceofHateLvl1, sdk.areas.Travincal], true); + } - if (Config.MFLeader) { - Pather.makePortal(); - say("council " + me.area); - } + if (Config.MFLeader) { + Pather.makePortal(); + say("council " + me.area); + } - Attack.clearList(Attack.buildMonsterList(councilMember)); - } + Attack.clearList(Attack.buildMonsterList(councilMember)); + } - Config.MFLeader && Config.PublicMode && say("travdone"); + Config.MFLeader && Config.PublicMode && say("travdone"); - return true; -} + return true; + }, + sdk.areas.Travincal +); diff --git a/d2bs/kolbot/libs/scripts/TravincalLeech.js b/d2bs/kolbot/libs/scripts/TravincalLeech.js index 4e8649f88..f0951ce5d 100644 --- a/d2bs/kolbot/libs/scripts/TravincalLeech.js +++ b/d2bs/kolbot/libs/scripts/TravincalLeech.js @@ -13,73 +13,76 @@ * - add dodge if position is too hot (hydras can kill a low level quickly) */ -function TravincalLeech () { - include("core/Common/Leecher.js"); - let leader; - let done = false; +const TravincalLeech = new Runnable( + function TravincalLeech () { + include("core/Common/Leecher.js"); + let leader; + let done = false; - const chatEvent = function (nick, msg) { - if (nick === leader && msg.toLowerCase() === "travdone") { - done = true; - } - }; + const chatEvent = function (nick, msg) { + if (nick === leader && msg.toLowerCase() === "travdone") { + done = true; + } + }; - Town.goToTown(3); - Town.doChores(); - Town.move("portalspot"); + Town.goToTown(3); + Town.doChores(); + Town.move("portalspot"); - if (Config.Leader) { - leader = Config.Leader; - if (!Misc.poll(() => Misc.inMyParty(leader), Time.minutes(2), 1000)) { - throw new Error("TristramLeech: Leader not partied"); + if (Config.Leader) { + leader = Config.Leader; + if (!Misc.poll(() => Misc.inMyParty(leader), Time.minutes(2), 1000)) { + throw new Error("TristramLeech: Leader not partied"); + } } - } - !leader && (leader = Misc.autoLeaderDetect({ - destination: sdk.areas.Travincal, - quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), - timeout: Time.minutes(5) - })); + !leader && (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.Travincal, + quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), + timeout: Time.minutes(5) + })); - if (leader) { - try { - const Worker = require("../modules/Worker"); - addEventListener("chatmsg", chatEvent); + if (leader) { + try { + const Worker = require("../modules/Worker"); + addEventListener("chatmsg", chatEvent); - Common.Leecher.killLeaderTracker = false; - Common.Leecher.leader = leader; - Common.Leecher.currentScript = Loader.scriptName(); - Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; - - while (Misc.inMyParty(Common.Leecher.leader)) { - if (done) return true; - - if (me.inTown && Pather.getPortal(sdk.areas.Travincal, Common.Leecher.leader)) { - Pather.usePortal(sdk.areas.Travincal, Common.Leecher.leader); - Town.getCorpse(); - } + Common.Leecher.killLeaderTracker = false; + Common.Leecher.leader = leader; + Common.Leecher.currentScript = Loader.scriptName(); + Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; - if (me.mode === sdk.player.mode.Dead) { - me.revive(); + while (Misc.inMyParty(Common.Leecher.leader)) { + if (done) return true; - while (!me.inTown) { - delay(100); + if (me.inTown && Pather.getPortal(sdk.areas.Travincal, Common.Leecher.leader)) { + Pather.usePortal(sdk.areas.Travincal, Common.Leecher.leader); + Town.getCorpse(); } + + if (me.mode === sdk.player.mode.Dead) { + me.revive(); - Town.move("portalspot"); - } + while (!me.inTown) { + delay(100); + } + + Town.move("portalspot"); + } - delay(100); + delay(100); + } + } catch (e) { + console.error(e); + } finally { + removeEventListener("chatmsg", chatEvent); + Common.Leecher.killLeaderTracker = true; } - } catch (e) { - console.error(e); - } finally { - removeEventListener("chatmsg", chatEvent); - Common.Leecher.killLeaderTracker = true; + } else { + console.warn("No leader found"); } - } else { - console.warn("No leader found"); - } - return true; -} + return true; + }, + sdk.areas.KurastDocktown +); diff --git a/d2bs/kolbot/libs/scripts/Treehead.js b/d2bs/kolbot/libs/scripts/Treehead.js index 31c31e4b9..e687e8b59 100644 --- a/d2bs/kolbot/libs/scripts/Treehead.js +++ b/d2bs/kolbot/libs/scripts/Treehead.js @@ -5,16 +5,19 @@ * */ -function Treehead () { - Town.doChores(); - Pather.useWaypoint(sdk.areas.DarkWood); - Precast.doPrecast(true); +const Treehead = new Runnable( + function Treehead () { + Town.doChores(); + Pather.useWaypoint(sdk.areas.DarkWood); + Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - throw new Error("Failed to move to Treehead"); - } + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + throw new Error("Failed to move to Treehead"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.TreeheadWoodFist)); + Attack.kill(getLocaleString(sdk.locale.monsters.TreeheadWoodFist)); - return true; -} + return true; + }, + sdk.areas.DarkWood +); diff --git a/d2bs/kolbot/libs/scripts/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js index f1c6c6a1b..1e0958ef5 100644 --- a/d2bs/kolbot/libs/scripts/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -5,64 +5,67 @@ * */ -function Tristram () { - Pather._teleport = Pather.teleport; +const Tristram = new Runnable( + function Tristram () { + Pather._teleport = Pather.teleport; - // complete quest if its not complete - if (!me.getQuest(sdk.quest.id.TheSearchForCain, 4) - && !me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { - include("core/Common/Cain.js"); - Common.Cain.run(); - } + // complete quest if its not complete + if (!me.getQuest(sdk.quest.id.TheSearchForCain, 4) + && !me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { + include("core/Common/Cain.js"); + Common.Cain.run(); + } - MainLoop: - while (true) { - switch (true) { - case me.inTown: - Town.doChores(); - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); + MainLoop: + while (true) { + switch (true) { + case me.inTown: + Town.doChores(); + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); - break; - case me.inArea(sdk.areas.StonyField): - if (!Pather.moveToPreset( - sdk.areas.StonyField, - sdk.unittype.Monster, - sdk.monsters.preset.Rakanishu, - 0, 0, - false, - true) - ) { - throw new Error("Failed to move to Rakanishu"); - } + break; + case me.inArea(sdk.areas.StonyField): + if (!Pather.moveToPreset( + sdk.areas.StonyField, + sdk.unittype.Monster, + sdk.monsters.preset.Rakanishu, + 0, 0, + false, + true) + ) { + throw new Error("Failed to move to Rakanishu"); + } - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Rakanishu)); + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Rakanishu)); - while (!Pather.usePortal(sdk.areas.Tristram)) { - Attack.securePosition(me.x, me.y, 10, 1000); - } + while (!Pather.usePortal(sdk.areas.Tristram)) { + Attack.securePosition(me.x, me.y, 10, 1000); + } - break; - case me.inArea(sdk.areas.Tristram): - let redPortal = Game.getObject(sdk.objects.RedPortal); - !!redPortal && Pather.moveTo(redPortal.x, redPortal.y + 6); + break; + case me.inArea(sdk.areas.Tristram): + let redPortal = Game.getObject(sdk.objects.RedPortal); + !!redPortal && Pather.moveTo(redPortal.x, redPortal.y + 6); - if (Config.Tristram.PortalLeech) { - Pather.makePortal(); - delay(1000); - Pather.teleport = !Config.Tristram.WalkClear && Pather._teleport; - } + if (Config.Tristram.PortalLeech) { + Pather.makePortal(); + delay(1000); + Pather.teleport = !Config.Tristram.WalkClear && Pather._teleport; + } - Config.Tristram.PortalLeech ? Attack.clearLevel(0) : Attack.clearLevel(Config.ClearType); + Config.Tristram.PortalLeech ? Attack.clearLevel(0) : Attack.clearLevel(Config.ClearType); - break MainLoop; - default: - break MainLoop; + break MainLoop; + default: + break MainLoop; + } } - } - Config.MFLeader && Config.PublicMode && say("tristdone"); - Pather.teleport = Pather._teleport; + Config.MFLeader && Config.PublicMode && say("tristdone"); + Pather.teleport = Pather._teleport; - return true; -} + return true; + }, + sdk.areas.StonyField +); diff --git a/d2bs/kolbot/libs/scripts/TristramLeech.js b/d2bs/kolbot/libs/scripts/TristramLeech.js index b071289ab..2e144cc6b 100644 --- a/d2bs/kolbot/libs/scripts/TristramLeech.js +++ b/d2bs/kolbot/libs/scripts/TristramLeech.js @@ -5,121 +5,124 @@ * */ -function TristramLeech () { - include("core/Common/Leecher.js"); - let done = false; - let whereisleader, leader; - - const chatEvent = function (nick, msg) { - if (nick === leader && msg.toLowerCase() === "tristdone") { - done = true; - } - }; - - Town.doChores(); - Town.goToTown(1); - Town.move("portalspot"); - - if (Config.Leader) { - leader = (Config.Leader || Config.TristramLeech.Leader); - if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), 1000)) { - throw new Error("TristramLeech: Leader not partied"); - } - } - - !leader && (leader = Misc.autoLeaderDetect({ - destination: sdk.areas.Tristram, - quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), - timeout: Time.minutes(5) - })); - - if (leader) { - try { - const Worker = require("../modules/Worker"); - addEventListener("chatmsg", chatEvent); - - Common.Leecher.leader = leader; - Common.Leecher.currentScript = Loader.scriptName(); - Common.Leecher.killLeaderTracker = false; - Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; - - if (!Misc.poll(() => { - if (done) return true; - if (Pather.getPortal(sdk.areas.Tristram, Config.Leader || null) - && Pather.usePortal(sdk.areas.Tristram, Config.Leader || null)) { - return true; - } - - return false; - }, Time.minutes(5), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); +const TristramLeech = new Runnable( + function TristramLeech () { + include("core/Common/Leecher.js"); + let done = false; + let whereisleader, leader; + + const chatEvent = function (nick, msg) { + if (nick === leader && msg.toLowerCase() === "tristdone") { + done = true; } + }; + + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); + + if (Config.Leader) { + leader = (Config.Leader || Config.TristramLeech.Leader); + if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), 1000)) { + throw new Error("TristramLeech: Leader not partied"); + } + } - Precast.doPrecast(true); - delay(3000); - - whereisleader = Misc.poll(() => { - let lead = getParty(leader); + !leader && (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.Tristram, + quitIf: (area) => Common.Leecher.nextScriptAreas.includes(area), + timeout: Time.minutes(5) + })); + + if (leader) { + try { + const Worker = require("../modules/Worker"); + addEventListener("chatmsg", chatEvent); + + Common.Leecher.leader = leader; + Common.Leecher.currentScript = Loader.scriptName(); + Common.Leecher.killLeaderTracker = false; + Worker.runInBackground.leaderTracker = Common.Leecher.leaderTracker; + + if (!Misc.poll(() => { + if (done) return true; + if (Pather.getPortal(sdk.areas.Tristram, Config.Leader || null) + && Pather.usePortal(sdk.areas.Tristram, Config.Leader || null)) { + return true; + } - if (lead.area === sdk.areas.Tristram) { - return lead; + return false; + }, Time.minutes(5), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); } - return false; - }, Time.minutes(3), 1000); - - while (true) { - if (done) return true; - - whereisleader = getParty(leader); - let leaderUnit = Misc.getPlayerUnit(leader); + Precast.doPrecast(true); + delay(3000); - if (whereisleader.area !== sdk.areas.Tristram && !Misc.poll(() => { + whereisleader = Misc.poll(() => { let lead = getParty(leader); if (lead.area === sdk.areas.Tristram) { - return true; + return lead; } return false; - }, Time.minutes(3), 1000)) { - console.log("Leader wasn't in tristram for longer than 3 minutes, End script"); + }, Time.minutes(3), 1000); + + while (true) { + if (done) return true; - break; - } + whereisleader = getParty(leader); + let leaderUnit = Misc.getPlayerUnit(leader); + + if (whereisleader.area !== sdk.areas.Tristram && !Misc.poll(() => { + let lead = getParty(leader); + + if (lead.area === sdk.areas.Tristram) { + return true; + } + + return false; + }, Time.minutes(3), 1000)) { + console.log("Leader wasn't in tristram for longer than 3 minutes, End script"); - if (whereisleader.area === me.area) { - try { - if (copyUnit(leaderUnit).x) { - if (Config.TristramLeech.Helper && leaderUnit.distance > 4) { - Pather.moveToUnit(leaderUnit) && Attack.clear(10); + break; + } + + if (whereisleader.area === me.area) { + try { + if (copyUnit(leaderUnit).x) { + if (Config.TristramLeech.Helper && leaderUnit.distance > 4) { + Pather.moveToUnit(leaderUnit) && Attack.clear(10); + } + !Config.TristramLeech.Helper && leaderUnit.distance > 20 && Pather.moveNearUnit(leaderUnit, 15); + } else { + if (Config.TristramLeech.Helper) { + Pather.moveTo(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y) && Attack.clear(10); + } + !Config.TristramLeech.Helper && Pather.moveNear(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y, 15); } - !Config.TristramLeech.Helper && leaderUnit.distance > 20 && Pather.moveNearUnit(leaderUnit, 15); - } else { - if (Config.TristramLeech.Helper) { - Pather.moveTo(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y) && Attack.clear(10); + } catch (err) { + if (whereisleader.area === me.area) { + Config.TristramLeech.Helper && Pather.moveTo(whereisleader.x, whereisleader.y) && Attack.clear(10); + !Config.TristramLeech.Helper && Pather.moveNear(whereisleader.x, whereisleader.y, 15); } - !Config.TristramLeech.Helper && Pather.moveNear(copyUnit(leaderUnit).x, copyUnit(leaderUnit).y, 15); - } - } catch (err) { - if (whereisleader.area === me.area) { - Config.TristramLeech.Helper && Pather.moveTo(whereisleader.x, whereisleader.y) && Attack.clear(10); - !Config.TristramLeech.Helper && Pather.moveNear(whereisleader.x, whereisleader.y, 15); } } - } - delay(100); + delay(100); + } + } catch (e) { + console.error(e); + } finally { + removeEventListener("chatmsg", chatEvent); + Common.Leecher.killLeaderTracker = true; } - } catch (e) { - console.error(e); - } finally { - removeEventListener("chatmsg", chatEvent); - Common.Leecher.killLeaderTracker = true; } - } - if (!me.inTown && Town.goToTown()) throw new Error("Failed to get back to town"); + if (!me.inTown && Town.goToTown()) throw new Error("Failed to get back to town"); - return true; -} + return true; + }, + sdk.areas.RogueEncampment +); diff --git a/d2bs/kolbot/libs/scripts/UndergroundPassage.js b/d2bs/kolbot/libs/scripts/UndergroundPassage.js index 6e3dde7ad..b266c6cd5 100644 --- a/d2bs/kolbot/libs/scripts/UndergroundPassage.js +++ b/d2bs/kolbot/libs/scripts/UndergroundPassage.js @@ -5,16 +5,19 @@ * */ -function UndergroundPassage() { - Town.doChores(); - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); +const UndergroundPassage = new Runnable( + function UndergroundPassage() { + Town.doChores(); + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.UndergroundPassageLvl1, sdk.areas.UndergroundPassageLvl2], true)) { - throw new Error("Failed to move to Underground passage level 2"); - } + if (!Pather.moveToExit([sdk.areas.UndergroundPassageLvl1, sdk.areas.UndergroundPassageLvl2], true)) { + throw new Error("Failed to move to Underground passage level 2"); + } - Attack.clearLevel(); + Attack.clearLevel(); - return true; -} + return true; + }, + sdk.areas.StonyField +); diff --git a/d2bs/kolbot/libs/scripts/UserAddon.js b/d2bs/kolbot/libs/scripts/UserAddon.js index 55b4da4bb..c22d10cfe 100644 --- a/d2bs/kolbot/libs/scripts/UserAddon.js +++ b/d2bs/kolbot/libs/scripts/UserAddon.js @@ -8,102 +8,104 @@ * */ -function UserAddon () { - let i, title, dummy, command = ""; - let showInfo = true; - const UnitInfo = new (require("../modules/UnitInfo")); - const className = sdk.player.class.nameOf(me.classid); - const flags = [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, - sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, - sdk.uiflags.Quest, sdk.uiflags.Msgs, sdk.uiflags.Stash, - sdk.uiflags.Shop, sdk.uiflags.EscMenu, sdk.uiflags.Cube - ]; +const UserAddon = new Runnable( + function UserAddon () { + let i, title, dummy, command = ""; + let showInfo = true; + const UnitInfo = new (require("../modules/UnitInfo")); + const className = sdk.player.class.nameOf(me.classid); + const flags = [ + sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, + sdk.uiflags.QuickSkill, sdk.uiflags.SkillWindow, sdk.uiflags.ChatBox, + sdk.uiflags.Quest, sdk.uiflags.Msgs, sdk.uiflags.Stash, + sdk.uiflags.Shop, sdk.uiflags.EscMenu, sdk.uiflags.Cube + ]; - const keyEvent = function (key) { - switch (key) { - case sdk.keys.Spacebar: - FileTools.copy("libs/config/" + className + ".js", "libs/config/" + className + "." + me.name + ".js"); - D2Bot.printToConsole("libs/config/" + className + "." + me.name + ".js has been created."); - D2Bot.printToConsole("Please configure your bot and start it again."); - D2Bot.stop(); + const keyEvent = function (key) { + switch (key) { + case sdk.keys.Spacebar: + FileTools.copy("libs/config/" + className + ".js", "libs/config/" + className + "." + me.name + ".js"); + D2Bot.printToConsole("libs/config/" + className + "." + me.name + ".js has been created."); + D2Bot.printToConsole("Please configure your bot and start it again."); + D2Bot.stop(); - break; - } - }; + break; + } + }; - /** - * @param {string} speaker - * @param {string} msg - * @returns {boolean} - */ - const onChatInput = function (speaker, msg) { - if (msg.length && msg[0] === ".") { - command = msg.split(" ")[0].split(".")[1]; - return true; - } + /** + * @param {string} speaker + * @param {string} msg + * @returns {boolean} + */ + const onChatInput = function (speaker, msg) { + if (msg.length && msg[0] === ".") { + command = msg.split(" ")[0].split(".")[1]; + return true; + } - return false; - }; + return false; + }; - try { - // Make sure the item event is loaded - why though? - !Config.FastPick && addEventListener("itemaction", Pickit.itemEvent); - addEventListener("chatinputblocker", onChatInput); + try { + // Make sure the item event is loaded - why though? + !Config.FastPick && addEventListener("itemaction", Pickit.itemEvent); + addEventListener("chatinputblocker", onChatInput); - if (!FileTools.exists("libs/config/" + className + "." + me.name + ".js")) { - console.log("ÿc4UserAddonÿc0: Press HOME and then press SPACE if you want to create character config."); - addEventListener("keyup", keyEvent); - showConsole(); - } + if (!FileTools.exists("libs/config/" + className + "." + me.name + ".js")) { + console.log("ÿc4UserAddonÿc0: Press HOME and then press SPACE if you want to create character config."); + addEventListener("keyup", keyEvent); + showConsole(); + } + + while (true) { + for (i = 0; i < flags.length; i += 1) { + if (getUIFlag(flags[i])) { + if (title) { + title.remove(); + dummy.remove(); - while (true) { - for (i = 0; i < flags.length; i += 1) { - if (getUIFlag(flags[i])) { - if (title) { - title.remove(); - dummy.remove(); + title = false; + dummy = false; + } - title = false; - dummy = false; + break; } + } - break; + if (i === flags.length && !title) { + title = new Text(":: kolbot user addon ::", 400, 525, 4, 0, 2); + dummy = new Text("`", 1, 1); // Prevents crash } - } - if (i === flags.length && !title) { - title = new Text(":: kolbot user addon ::", 400, 525, 4, 0, 2); - dummy = new Text("`", 1, 1); // Prevents crash - } + UnitInfo.check(); - UnitInfo.check(); + if (command) { + console.debug(command); + if (command.toLowerCase() === "done") { + return true; + } else if (command.toLowerCase() === "info") { + showInfo = !showInfo; + } + command = ""; + } - if (command) { - console.debug(command); - if (command.toLowerCase() === "done") { - return true; - } else if (command.toLowerCase() === "info") { - showInfo = !showInfo; + Pickit.fastPick(); + if (showInfo) { + UnitInfo.createInfo(Game.getSelectedUnit()); } - command = ""; - } - Pickit.fastPick(); - if (showInfo) { - UnitInfo.createInfo(Game.getSelectedUnit()); + delay(20); } - - delay(20); + } finally { + console.log("ÿc4UserAddon ÿc1ended"); + removeEventListener("keyup", keyEvent); + removeEventListener("itemaction", Pickit.itemEvent); + removeEventListener("chatinputblocker", onChatInput); + // ensure hooks are properly disposed of + !!title && title.remove(); + dummy && dummy.remove(); + UnitInfo.remove(); } - } finally { - console.log("ÿc4UserAddon ÿc1ended"); - removeEventListener("keyup", keyEvent); - removeEventListener("itemaction", Pickit.itemEvent); - removeEventListener("chatinputblocker", onChatInput); - // ensure hooks are properly disposed of - !!title && title.remove(); - dummy && dummy.remove(); - UnitInfo.remove(); } -} +); diff --git a/d2bs/kolbot/libs/scripts/WPGetter.js b/d2bs/kolbot/libs/scripts/WPGetter.js index be363c003..e0625d143 100644 --- a/d2bs/kolbot/libs/scripts/WPGetter.js +++ b/d2bs/kolbot/libs/scripts/WPGetter.js @@ -5,25 +5,27 @@ * */ -function WPGetter () { - Town.doChores(); - Town.goToTown(); - Pather.getWP(me.area); +const WPGetter = new Runnable( + function WPGetter () { + Town.doChores(); + Town.goToTown(); + Pather.getWP(me.area); - let highestAct = me.highestAct; - let lastWP = sdk.areas.townOfAct((highestAct === 5 ? highestAct : highestAct + 1)); - lastWP === sdk.areas.Harrogath && (lastWP = me.baal ? sdk.areas.WorldstoneLvl2 : sdk.areas.AncientsWay); - let wpsToGet = Pather.nonTownWpAreas - .filter((wp) => wp < lastWP && wp !== sdk.areas.HallsofPain && !getWaypoint(Pather.wpAreas.indexOf(wp))); + const highestAct = me.highestAct; + let lastWP = sdk.areas.townOfAct((highestAct === 5 ? highestAct : highestAct + 1)); + lastWP === sdk.areas.Harrogath && (lastWP = me.baal ? sdk.areas.WorldstoneLvl2 : sdk.areas.AncientsWay); + let wpsToGet = Pather.nonTownWpAreas + .filter((wp) => wp < lastWP && wp !== sdk.areas.HallsofPain && !getWaypoint(Pather.wpAreas.indexOf(wp))); - console.debug(wpsToGet); + console.debug(wpsToGet); - for (let wp of wpsToGet) { - Pather.getWP(wp); - if (Town.checkScrolls(sdk.items.TomeofTownPortal) < 10) { - Town.doChores(); + for (let wp of wpsToGet) { + Pather.getWP(wp); + if (me.checkScrolls(sdk.items.TomeofTownPortal) < 10) { + Town.doChores(); + } } - } - return true; -} + return true; + } +); diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index d67d46ac0..8e45bbb1c 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -5,253 +5,282 @@ * */ -function Wakka () { - include("core/Common/Diablo.js"); - const timeout = Config.Wakka.Wait; - const [minDist, maxDist] = [50, 80]; - const internals = { - died: false, - safeTP: false, - coordsInit: false, - vizCoords: [], - seisCoords: [], - infCoords: [], - vizClear: false, - seisClear: false, - infClear: false, - }; - - let tick; - let leader = ""; - let [leaderUnit, leaderPartyUnit] = [null, null]; - - const checkMonsters = function (range = 15, dodge = false) { - let monList = []; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.y < 5565 && monster.attackable && monster.distance <= range) { - if (!dodge) return true; - monList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - - if (!monList.length) return false; +const Wakka = new Runnable( + function Wakka () { + include("core/Common/Diablo.js"); + const timeout = Config.Wakka.Wait; + const [minDist, maxDist] = [50, 80]; + const internals = { + died: false, + safeTP: false, + coordsInit: false, + vizCoords: [], + seisCoords: [], + infCoords: [], + vizClear: false, + seisClear: false, + infClear: false, + }; - monList.sort(Sort.units); + let tick; + let leader = ""; + let [leaderUnit, leaderPartyUnit] = [null, null]; - if (monList[0].distance < 25 && !checkCollision(me, monList[0], sdk.collision.Ranged)) { - Attack.deploy(monList[0], 25, 5, 15); - } + const checkMonsters = function (range = 15, dodge = false) { + let monList = []; + let monster = Game.getMonster(); - return true; - }; - - const getCoords = function () { - if (!internals.coordsInit) { - Common.Diablo.initLayout(); - internals.vizCoords = Common.Diablo.vizLayout === 1 - ? [7707, 5274] - : [7708, 5298]; - internals.seisCoords = Common.Diablo.seisLayout === 1 - ? [7812, 5223] - : [7809, 5193]; - internals.infCoords = Common.Diablo.infLayout === 1 - ? [7868, 5294] - : [7882, 5306]; - internals.coordsInit = true; - } - }; + if (monster) { + do { + if (monster.y < 5565 && monster.attackable && monster.distance <= range) { + if (!dodge) return true; + monList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } - /** @param {string} name */ - const checkBoss = function (name) { - let glow = Game.getObject(sdk.objects.SealGlow); - if (!glow) return false; + if (!monList.length) return false; - for (let i = 0; i < 10; i += 1) { - let boss = Game.getMonster(name); + monList.sort(Sort.units); - if (boss) { - while (!boss.dead) { - delay(500); - } - - return true; + if (monList[0].distance < 25 && !checkCollision(me, monList[0], sdk.collision.Ranged)) { + Attack.deploy(monList[0], 25, 5, 15); } - delay(500); - } - - return true; - }; + return true; + }; - const revive = function () { - if (me.mode === sdk.player.mode.Death) { - while (me.mode !== sdk.player.mode.Dead) { - delay(3); + const getCoords = function () { + if (!internals.coordsInit) { + Common.Diablo.initLayout(); + internals.vizCoords = Common.Diablo.vizLayout === 1 + ? [7707, 5274] + : [7708, 5298]; + internals.seisCoords = Common.Diablo.seisLayout === 1 + ? [7812, 5223] + : [7809, 5193]; + internals.infCoords = Common.Diablo.infLayout === 1 + ? [7868, 5294] + : [7882, 5306]; + internals.coordsInit = true; } - } else if (me.dead) { - if (Config.LifeChicken <= 0) { - console.log("I Died...reviving"); - internals.died = true; - me.revive(); - } else { - scriptBroadcast("quit"); - } - } - }; - - const getCorpse = function () { - revive(); + }; - let rval = false; - let corpse = Game.getPlayer(me.name, sdk.player.mode.Dead); + /** @param {string} name */ + const checkBoss = function (name) { + let glow = Game.getObject(sdk.objects.SealGlow); + if (!glow) return false; - if (corpse) { - do { - if (corpse.distance <= 15) { - Pather.moveToUnit(corpse); - corpse.interact(); - delay(500); + for (let i = 0; i < 10; i += 1) { + let boss = Game.getMonster(name); - rval = true; + if (boss) { + while (!boss.dead) { + delay(500); + } + + return true; } - } while (corpse.getNext()); - } - return rval; - }; + delay(500); + } - /** @param {[number, number]} dest */ - const followPath = function (dest) { - let path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 10); - if (!path) throw new Error("Failed go get path"); + return true; + }; - while (path.length > 0) { - if (me.mode === sdk.player.mode.Dead || me.inTown) return false; - if (!leaderUnit || !copyUnit(leaderUnit).x) { - leaderUnit = Game.getPlayer(leader); + const revive = function () { + if (me.mode === sdk.player.mode.Death) { + while (me.mode !== sdk.player.mode.Dead) { + delay(3); + } + } else if (me.dead) { + if (Config.LifeChicken <= 0) { + console.log("I Died...reviving"); + internals.died = true; + me.revive(); + } else { + scriptBroadcast("quit"); + } } + }; - if (leaderUnit) { - // monsters nearby - don't move - if (checkMonsters(45, true) && leaderUnit.distance <= maxDist) { - path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); - delay(200); + const getCorpse = function () { + revive(); - continue; - } + let rval = false; + let corpse = Game.getPlayer(me.name, sdk.player.mode.Dead); - // leader within minDist range - don't move - if (leaderUnit.distance <= minDist) { - delay(200); + if (corpse) { + do { + if (corpse.distance <= 15) { + Pather.moveToUnit(corpse); + corpse.interact(); + delay(500); - continue; - } + rval = true; + } + } while (corpse.getNext()); + } - // make sure distance to next node isn't too hot - if ([path[0].x, path[0].y].mobCount({ range: 15 }) !== 0) { - console.log("Mobs at next node"); - // mobs, stay where we are - delay(200); + return rval; + }; - continue; + /** @param {[number, number]} dest */ + const followPath = function (dest) { + let path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 10); + if (!path) throw new Error("Failed go get path"); + + while (path.length > 0) { + if (me.mode === sdk.player.mode.Dead || me.inTown) return false; + if (!leaderUnit || !copyUnit(leaderUnit).x) { + leaderUnit = Game.getPlayer(leader); } - } else { - // leaderUnit out of getUnit range but leader is still within reasonable distance - check party unit's coords! - leaderPartyUnit = getParty(leader); - if (leaderPartyUnit) { - // leader went to town - don't move - if (leaderPartyUnit.area !== me.area) { + if (leaderUnit) { + // monsters nearby - don't move + if (checkMonsters(45, true) && leaderUnit.distance <= maxDist) { + path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); delay(200); continue; } - // if there's monsters between the leecher and leader, wait until monsters are dead or leader is out of maxDist range - if (checkMonsters(45, true) && getDistance(me, leaderPartyUnit.x, leaderPartyUnit.y) <= maxDist) { - path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); + // leader within minDist range - don't move + if (leaderUnit.distance <= minDist) { + delay(200); + continue; + } + + // make sure distance to next node isn't too hot + if ([path[0].x, path[0].y].mobCount({ range: 15 }) !== 0) { + console.log("Mobs at next node"); + // mobs, stay where we are delay(200); continue; } + } else { + // leaderUnit out of getUnit range but leader is still within reasonable distance - check party unit's coords! + leaderPartyUnit = getParty(leader); + + if (leaderPartyUnit) { + // leader went to town - don't move + if (leaderPartyUnit.area !== me.area) { + delay(200); + + continue; + } + + // if there's monsters between the leecher and leader, wait until monsters are dead or leader is out of maxDist range + if (checkMonsters(45, true) && getDistance(me, leaderPartyUnit.x, leaderPartyUnit.y) <= maxDist) { + path = getPath(me.area, me.x, me.y, dest[0], dest[1], 0, 15); + + delay(200); + + continue; + } + } } + + Pather.moveTo(path[0].x, path[0].y) && path.shift(); + // no mobs around us, so it's safe to pick + !me.checkForMobs({ + range: 10, + coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor) + }) && Pickit.pickItems(5); + getCorpse(); } - Pather.moveTo(path[0].x, path[0].y) && path.shift(); - // no mobs around us, so it's safe to pick - !me.checkForMobs({ - range: 10, - coll: (sdk.collision.BlockWall | sdk.collision.Objects | sdk.collision.ClosedDoor) - }) && Pickit.pickItems(5); - getCorpse(); - } + return true; + }; - return true; - }; + /** @returns {number} */ + const getLeaderUnitArea = function () { + if (!leaderUnit || !copyUnit(leaderUnit).x) { + leaderUnit = Game.getPlayer(leader); + } + return !!leaderUnit + ? leaderUnit.area + : getParty(leader).area; + }; - /** @returns {number} */ - const getLeaderUnitArea = function () { - if (!leaderUnit || !copyUnit(leaderUnit).x) { - leaderUnit = Game.getPlayer(leader); - } - return !!leaderUnit - ? leaderUnit.area - : getParty(leader).area; - }; - - const log = function (msg = "") { - me.overhead(msg); - console.log(msg); - }; - - // START - Town.goToTown(4); - Town.move("portalspot"); - - if (Config.Leader) { - leader = Config.Leader; - if (!Misc.poll(() => Misc.inMyParty(leader), 30e3, 1000)) { - console.warn("Wakka: Leader not partied. Using autodetect"); - leader = ""; + const log = function (msg = "") { + me.overhead(msg); + console.log(msg); + }; + + // START + Town.goToTown(4); + Town.move("portalspot"); + + if (Config.Leader) { + leader = Config.Leader; + if (!Misc.poll(() => Misc.inMyParty(leader), 30e3, 1000)) { + console.warn("Wakka: Leader not partied. Using autodetect"); + leader = ""; + } } - } - - !leader && (leader = Misc.autoLeaderDetect({ - destination: sdk.areas.ChaosSanctuary, - quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), - timeout: timeout * 60e3 - })); - Town.doChores(); - if (!leader) throw new Error("Wakka: Leader not found"); - - addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - const Worker = require("../modules/Worker"); - - try { - if (Config.Wakka.SkipIfBaal) { - let leadTick = getTickCount(); - let killLeaderTracker = false; - - Worker.runInBackground.leaderTracker = function () { - if (Common.Diablo.done || killLeaderTracker) return false; + + !leader && (leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ChaosSanctuary, + quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), + timeout: timeout * 60e3 + })); + Town.doChores(); + if (!leader) throw new Error("Wakka: Leader not found"); + + addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + const Worker = require("../modules/Worker"); + + try { + if (Config.Wakka.SkipIfBaal) { + let leadTick = getTickCount(); + let killLeaderTracker = false; + + Worker.runInBackground.leaderTracker = function () { + if (Common.Diablo.done || killLeaderTracker) return false; + // check every 3 seconds + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { + if (Loader.scriptName() === "Wakka") { + killLeaderTracker = true; + throw new Error("Party leader is running baal"); + } else { + // kill process + return false; + } + } + + return true; + }; + } + + let levelTick = getTickCount(); + let killLevelTracker = false; + + Worker.runInBackground.levelTracker = function () { + if (Common.Diablo.done || killLevelTracker) return false; // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); + if (getTickCount() - levelTick < 3000) return true; + levelTick = getTickCount(); // check again in another 3 seconds if game wasn't ready if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { + if (me.charlvl >= Config.Wakka.StopAtLevel) { + Config.Wakka.StopProfile && D2Bot.stop(); + if (Loader.scriptName() === "Wakka") { - killLeaderTracker = true; - throw new Error("Party leader is running baal"); + killLevelTracker = true; + throw new Error("Reached wanted level"); } else { // kill process return false; @@ -260,183 +289,157 @@ function Wakka () { return true; }; - } - - let levelTick = getTickCount(); - let killLevelTracker = false; - - Worker.runInBackground.levelTracker = function () { - if (Common.Diablo.done || killLevelTracker) return false; - // check every 3 seconds - if (getTickCount() - levelTick < 3000) return true; - levelTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (me.charlvl >= Config.Wakka.StopAtLevel) { - Config.Wakka.StopProfile && D2Bot.stop(); + let diaTick = getTickCount(); - if (Loader.scriptName() === "Wakka") { - killLevelTracker = true; - throw new Error("Reached wanted level"); - } else { - // kill process + Worker.runInBackground.diaSpawned = function () { + if (Common.Diablo.done || (internals.vizClear && internals.seisClear && internals.infClear)) { return false; } - } - - return true; - }; - - let diaTick = getTickCount(); - - Worker.runInBackground.diaSpawned = function () { - if (Common.Diablo.done || (internals.vizClear && internals.seisClear && internals.infClear)) { - return false; - } - // check every 1/4 second - if (getTickCount() - diaTick < 250) return true; - diaTick = getTickCount(); - - if (Common.Diablo.diabloSpawned) { - internals.vizClear = true; - internals.seisClear = true; - internals.infClear = true; - throw new Error("Diablo spawned"); - } + // check every 1/4 second + if (getTickCount() - diaTick < 250) return true; + diaTick = getTickCount(); + + if (Common.Diablo.diabloSpawned) { + internals.vizClear = true; + internals.seisClear = true; + internals.infClear = true; + throw new Error("Diablo spawned"); + } - return true; - }; + return true; + }; - while (Misc.inMyParty(leader)) { - try { - if (me.inArea(sdk.areas.PandemoniumFortress)) { - let portal = Pather.getPortal(sdk.areas.ChaosSanctuary, null); + while (Misc.inMyParty(leader)) { + try { + if (me.inArea(sdk.areas.PandemoniumFortress)) { + let portal = Pather.getPortal(sdk.areas.ChaosSanctuary, null); - if (portal) { - !internals.safeTP && delay(5000); - Pather.usePortal(sdk.areas.ChaosSanctuary, null); - Precast.doPrecast(true); - } - } else if (me.inArea(sdk.areas.ChaosSanctuary)) { - try { - if (!internals.safeTP) { - if (checkMonsters(25, false)) { - log("hot tp"); - Pather.usePortal(sdk.areas.PandemoniumFortress, null); - - continue; - } else { - getCoords(); - internals.safeTP = true; - } + if (portal) { + !internals.safeTP && delay(5000); + Pather.usePortal(sdk.areas.ChaosSanctuary, null); + Precast.doPrecast(true); } + } else if (me.inArea(sdk.areas.ChaosSanctuary)) { + try { + if (!internals.safeTP) { + if (checkMonsters(25, false)) { + log("hot tp"); + Pather.usePortal(sdk.areas.PandemoniumFortress, null); + + continue; + } else { + getCoords(); + internals.safeTP = true; + } + } - if (!internals.vizClear) { - if (followPath(internals.vizCoords)) { - if (checkBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { - log("vizier dead"); - internals.vizClear = true; - Precast.doPrecast(true); - tick = getTickCount(); - - while (getTickCount() - tick >= 5000) { - delay(100); + if (!internals.vizClear) { + if (followPath(internals.vizCoords)) { + if (checkBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { + log("vizier dead"); + internals.vizClear = true; + Precast.doPrecast(true); + tick = getTickCount(); + + while (getTickCount() - tick >= 5000) { + delay(100); + } } + } else { + console.debug("Failed to move to viz"); } - } else { - console.debug("Failed to move to viz"); } - } - - if (internals.vizClear && !internals.seisClear) { - if (followPath(internals.seisCoords)) { - if (checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { - log("seis dead"); - internals.seisClear = true; - Precast.doPrecast(true); - tick = getTickCount(); - while (getTickCount() - tick >= 7000) { - delay(100); + if (internals.vizClear && !internals.seisClear) { + if (followPath(internals.seisCoords)) { + if (checkBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { + log("seis dead"); + internals.seisClear = true; + Precast.doPrecast(true); + tick = getTickCount(); + + while (getTickCount() - tick >= 7000) { + delay(100); + } } + } else { + console.debug("Failed to move to seis"); } - } else { - console.debug("Failed to move to seis"); } - } - if (internals.vizClear && internals.seisClear && !internals.infClear) { - if (followPath(internals.infCoords)) { - if (checkBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { - log("infector dead"); - internals.infClear = true; - Precast.doPrecast(true); - tick = getTickCount(); - - while (getTickCount() - tick >= 2000) { - delay(100); + if (internals.vizClear && internals.seisClear && !internals.infClear) { + if (followPath(internals.infCoords)) { + if (checkBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { + log("infector dead"); + internals.infClear = true; + Precast.doPrecast(true); + tick = getTickCount(); + + while (getTickCount() - tick >= 2000) { + delay(100); + } } + } else { + console.debug("Failed to move to infector"); } - } else { - console.debug("Failed to move to infector"); } + + if (internals.vizClear && internals.seisClear && internals.infClear) { + Pather.moveTo(7767, 5263); + Misc.poll(function () { + if (Common.Diablo.diabloSpawned) return true; + if (Game.getMonster(sdk.monsters.Diablo)) return true; + return false; + }, Time.minutes(2), 500); + } + } catch (e) { + console.error((e.message ? e.message : e)); } if (internals.vizClear && internals.seisClear && internals.infClear) { Pather.moveTo(7767, 5263); - Misc.poll(function () { - if (Common.Diablo.diabloSpawned) return true; - if (Game.getMonster(sdk.monsters.Diablo)) return true; - return false; - }, Time.minutes(2), 500); - } - } catch (e) { - console.error((e.message ? e.message : e)); - } - if (internals.vizClear && internals.seisClear && internals.infClear) { - Pather.moveTo(7767, 5263); + let diablo = Misc.poll(function () { + return Game.getMonster(sdk.monsters.Diablo); + }, Time.minutes(3), 500); - let diablo = Misc.poll(function () { - return Game.getMonster(sdk.monsters.Diablo); - }, Time.minutes(3), 500); + if (diablo) { + while (!diablo.dead) { + delay(100); + } + log("Diablo is dead"); - if (diablo) { - while (!diablo.dead) { - delay(100); - } - log("Diablo is dead"); + if (!me.canTpToTown() || !Town.goToTown()) { + Pather.usePortal(sdk.areas.PandemoniumFortress); + } - if (!me.canTpToTown() || !Town.goToTown()) { - Pather.usePortal(sdk.areas.PandemoniumFortress); + return true; + } else { + log("Couldn't find diablo"); } - - return true; - } else { - log("Couldn't find diablo"); } } - } - revive(); + revive(); - delay(200); - } catch (e) { - console.error(e); + delay(200); + } catch (e) { + console.error(e); - return true; + return true; + } } + } catch (e) { + // console.error(e); + } finally { + Common.Diablo.done; + removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); } - } catch (e) { - // console.error(e); - } finally { - Common.Diablo.done; - removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - } - log("Wakka complete"); + log("Wakka complete"); - return true; -} + return true; + }, + sdk.areas.PandemoniumFortress +); diff --git a/d2bs/kolbot/libs/scripts/Worldstone.js b/d2bs/kolbot/libs/scripts/Worldstone.js index 7d453d251..c47831728 100644 --- a/d2bs/kolbot/libs/scripts/Worldstone.js +++ b/d2bs/kolbot/libs/scripts/Worldstone.js @@ -5,35 +5,38 @@ * */ -function Worldstone() { - Town.doChores(); - 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(); +const Worldstone = new Runnable( + function Worldstone() { + Town.doChores(); Pather.useWaypoint(sdk.areas.WorldstoneLvl2); - } else { - Pather.moveToExit(sdk.areas.WorldstoneLvl2, true); - } + 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); + Pather.moveToExit(sdk.areas.WorldstoneLvl3, true) && Attack.clearLevel(Config.ClearType); - return true; -} + return true; + }, + sdk.areas.WorldstoneLvl2 +); From 2bf6dfdaeb4a33c67fc16c6ab3d58c177c1bed68 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:07:45 -0400 Subject: [PATCH 382/758] typedef udates --- d2bs/kolbot/sdk/globals.d.ts | 4 +++- d2bs/kolbot/sdk/types/Loader.d.ts | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index e8804ea9d..6629621c0 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -528,6 +528,7 @@ declare global { readonly isTorch: boolean; readonly isGheeds: boolean; readonly durabilityPercent: number; + readonly isCharm: boolean; getColor(): number; getBodyLoc(): number[]; @@ -715,8 +716,9 @@ declare global { needMerc(): boolean; needStash(): boolean; needHealing(): boolean; - // checkScrolls(id: number): number; + checkScrolls(id: number): number; checkKeys(): number; + checkShard(): boolean; canTpToTown(): boolean; haveWaypoint(area: number): boolean; accessToAct(act: number): boolean; diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index a11f4b6af..fd3a9a740 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -1,17 +1,30 @@ export {}; declare global { + type GlobalScript = () => boolean; + interface Runnable { + action: () => boolean; + startArea: number | undefined; + } + namespace Loader { - const fileList: string[] + const fileList: string[]; const scriptList: string[]; const scriptIndex: number; const skipTown: string[]; + const firstScriptAct: number; + const currentScript: GlobalScript | Runnable | null; + const nextScript: GlobalScript | Runnable | null; + const doneScripts: Set; + const tempList: string[]; function init(): void; function getScripts(): void; + function _runCurrent(): boolean; function clone(obj: any): void; function copy(from: any, to: any): void; function loadScripts(): void; - function scriptName(offset?: number): void; + function runScript(name: string, configOverride: Object | (() => any)): boolean; + function scriptName(offset?: number): string; } type Scripts = { From 074e8ae6bede66a5713da5b64b409b6794f861e7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 11 Apr 2024 15:08:44 -0400 Subject: [PATCH 383/758] [BugFix] Typo `BaalAssisstant` -> `BaalAssistant` --- d2bs/kolbot/libs/scripts/BaalAssistant.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index 030a84f19..dc8547fdc 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -12,7 +12,7 @@ * - override Misc.getShrinesInArea to end when we recieve safeCheck message */ -const BaalAssisstant = new Runnable( +const BaalAssistant = new Runnable( function BaalAssistant () { include("core/Common/Baal.js"); let Leader = Config.Leader; From fc9b0d06bfbf7da79b27493771d749101d7a1987 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 14 Apr 2024 01:13:47 -0400 Subject: [PATCH 384/758] Update OOG.js - Fix redefining props on me --- d2bs/kolbot/libs/OOG.js | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 8d46f973c..6cf30fcac 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -8,6 +8,7 @@ !isIncluded("Polyfill.js") && include("Polyfill.js"); includeIfNotIncluded("oog/D2Bot.js"); // required +includeIfNotIncluded("core/Me.js"); /** * ControlAction and Starter are very closely related, how should this be handled? @@ -27,29 +28,6 @@ includeIfNotIncluded("oog/D2Bot.js"); // required }([].filter.constructor("return this")(), function () { const Controls = require("./modules/Control"); - Object.defineProperties(me, { - classic: { - get: function () { - return me.gametype === sdk.game.gametype.Classic; - } - }, - expansion: { - get: function () { - return me.gametype === sdk.game.gametype.Expansion; - } - }, - softcore: { - get: function () { - return me.playertype === false; - } - }, - hardcore: { - get: function () { - return me.playertype === true; - } - }, - }); - const ControlAction = { mutedKey: false, realms: { From 6124fddb6e8a2eeed211f68e67eb4bb258a904c4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 14 Apr 2024 01:23:56 -0400 Subject: [PATCH 385/758] Fix find/getCharacters for singleplayer - Singleplayer isn't bound by the 24 character limit cap so need to be able to keep scrolling in case character is far down the list --- d2bs/kolbot/libs/OOG.js | 48 ++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 6cf30fcac..d98a3319f 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -240,8 +240,12 @@ includeIfNotIncluded("core/Me.js"); * @returns {Control | false} */ findCharacter: function (info, startFromTop = true) { + const singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); + // offline doesn't have a character limit cap + const cap = singlePlayer ? 999 : 24; let count = 0; let tick = getTickCount(); + let firstCheck; while (getLocation() !== sdk.game.locations.CharSelect) { if (getTickCount() - tick >= 5000) { @@ -258,6 +262,7 @@ includeIfNotIncluded("core/Me.js"); let control = Controls.CharSelectCharInfo0.control; if (control) { + firstCheck = control.getText(); do { let text = control.getText(); @@ -270,12 +275,23 @@ includeIfNotIncluded("core/Me.js"); return control; } } - } while (count < 24 && 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 (String.isEqual(firstCheck[1], nameCheck[1])) { + return false; + } + } + } } else { // no further check necessary break; @@ -286,16 +302,21 @@ includeIfNotIncluded("core/Me.js"); }, getCharacters: function () { + const singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); + // offline doesn't have a character limit cap + const cap = singlePlayer ? 999 : 24; let count = 0; let list = []; + let firstCheck; // start from beginning of the char list sendKey(sdk.keys.code.Home); - while (getLocation() === sdk.game.locations.CharSelect && count < 24) { + while (getLocation() === sdk.game.locations.CharSelect && count < cap) { let control = Controls.CharSelectCharInfo0.control; if (control) { + firstCheck = control.getText(); do { let text = control.getText(); @@ -306,12 +327,23 @@ includeIfNotIncluded("core/Me.js"); list.push(text[1]); } } - } while (count < 24 && control.getNext()); + } while (count < cap && control.getNext()); } // check for additional characters up to 24 - if (count === 8 || count === 16) { - Controls.CharSelectChar6.click() && this.scrollDown(); + 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; From 332078d962b87ba132fc1ed4b137f3a75e36f85d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 14 Apr 2024 01:45:26 -0400 Subject: [PATCH 386/758] Add lowgold case for dropping below our repaircosts - Sine rw's can be expensive to repair --- d2bs/kolbot/libs/core/Pickit.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 91e372da4..f5287558a 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -279,7 +279,7 @@ const Pickit = { } if ((unit.classid === sdk.items.runes.Ort || unit.classid === sdk.items.runes.Ral) - && Town.repairIngredientCheck(unit)) { + && Cubing.repairIngredientCheck(unit)) { return resultObj(Pickit.Result.UTILITY); } @@ -303,10 +303,16 @@ const Pickit = { } } - if (rval.result === Pickit.Result.UNWANTED && !Town.ignoreType(unit.itemType) && !unit.questItem - && ((unit.isInInventory && (me.inTown || !Config.FieldID.Enabled)) - || (me.gold < Config.LowGold || (me.gold < 500000 && Config.PickitFiles.length === 0)))) { - // Gold doesn't take up room, just pick it up + if (rval.result === Pickit.Result.UNWANTED + && !Town.ignoreType(unit.itemType) + && !unit.questItem + && ( + (unit.isInInventory && (me.inTown || !Config.FieldID.Enabled)) + || me.gold < Config.LowGold + || (me.gold < 500000 && Config.PickitFiles.length === 0) + || (me.gold < me.getRepairCost()) + )) { + // Gold doesn't ta=ke up room, just pick it up if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.TRASH); if (!this.invoLocked) { From 040fbe5389e6e368bb998b120f6fa1e8f9e2723e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:40:42 -0400 Subject: [PATCH 387/758] [BugFix] Don't touch cube if it's in locked invo spot --- d2bs/kolbot/libs/core/Storage.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index 0a39309f5..a483c06cf 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -127,6 +127,11 @@ 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) { @@ -196,11 +201,12 @@ } item = this.itemList[this.buffer[x][y] - 1]; - + if (item.classid === sdk.quest.item.Cube - && item.isInStash - && item.x === 0 - && item.y === 0) { + && ( + (item.isInInventory && Storage.Inventory.IsLocked(item, Config.Inventory)) + || (item.isInStash && item.x === 0 && item.y === 0) + )) { continue; // dont touch the cube } From 988fdffd650797e8fb44da1c9b7206f424c89454 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 30 Apr 2024 00:48:21 -0400 Subject: [PATCH 388/758] Update ConfigOverrides.js - Small cleanup --- .../libs/manualplay/libs/ConfigOverrides.js | 161 +++++++++--------- 1 file changed, 78 insertions(+), 83 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js b/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js index c85fc542e..c8a7ed3fc 100644 --- a/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/ConfigOverrides.js @@ -8,107 +8,102 @@ includeIfNotIncluded("core/Config.js"); -let original = Config.init; - -Config.init = function (notify) { - let configFilename = ""; - let classes = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; - - for (let i = 0; i < 5; i += 1) { - switch (i) { - case 0: // Custom config - includeIfNotIncluded("config/_customconfig.js"); - - for (let n in CustomConfig) { - if (CustomConfig.hasOwnProperty(n)) { - if (CustomConfig[n].indexOf(me.profile) > -1) { - if (notify) { - print("ÿc2Loading custom config: ÿc9" + n + ".js"); - } - +(function (Config, original) { + Config.init = function (notify) { + const className = sdk.player.class.nameOf(me.classid); + 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", + }))(className, me.profile, me.charname, me.realm); + let configFilename = ""; + + for (let i = 0; i < 5; i++) { + switch (i) { + case 0: // Custom config + includeIfNotIncluded("config/_customconfig.js"); + + for (let n in CustomConfig) { + if (CustomConfig.hasOwnProperty(n) && CustomConfig[n].includes(me.profile)) { + notify && console.log("ÿc2Loading custom config: ÿc9" + n + ".js"); configFilename = n + ".js"; break; } } - } - - break; - case 1:// Class.Profile.js - configFilename = classes[me.classid] + "." + me.profile + ".js"; - break; - case 2: // Realm.Class.Charname.js - configFilename = me.realm + "." + classes[me.classid] + "." + me.charname + ".js"; + break; + default: + configFilename = formats[i]; - break; - case 3: // Class.Charname.js - configFilename = classes[me.classid] + "." + me.charname + ".js"; - - break; - case 4: // Profile.js - configFilename = me.profile + ".js"; + break; + } - break; + if (configFilename && FileTools.exists("libs/manualplay/config/" + configFilename)) { + break; + } } - if (configFilename && FileTools.exists("libs/manualplay/config/" + configFilename)) { - break; - } - } + if (FileTools.exists("libs/manualplay/config/" + configFilename)) { + try { + if (!include("manualplay/config/" + configFilename)) { + throw new Error(); + } + } catch (e1) { + throw new Error("Failed to load character config."); + } + } else { + if (notify) { + console.log("ÿc1" + className + "." + me.charname + ".js not found!"); // Use the primary format + console.log("ÿc1Loading default config."); + } - if (FileTools.exists("libs/manualplay/config/" + configFilename)) { - try { - if (!include("manualplay/config/" + configFilename)) { - throw new Error(); + try { + // Try to find default config + if (!FileTools.exists("libs/manualplay/config/" + className + ".js")) { + D2Bot.printToConsole("Not going well? Read the guides: https://github.com/blizzhackers/documentation"); + throw new Error("ÿc1Default config not found. \nÿc9 Try reading the kolbot guides."); + } + + if (!include("manualplay/config/" + className + ".js")) { + throw new Error("ÿc1Failed to load default config."); + } + } catch (e) { + console.log(e); + original.apply(this, arguments); } - } catch (e1) { - throw new Error("Failed to load character config."); - } - } else { - if (notify) { - print("ÿc1" + classes[me.classid] + "." + me.charname + ".js not found!"); // Use the primary format - print("ÿc1Loading default config."); } try { - // Try to find default config - if (!FileTools.exists("libs/manualplay/config/" + classes[me.classid] + ".js")) { - D2Bot.printToConsole("Not going well? Read the guides: https://github.com/blizzhackers/documentation"); - throw new Error("ÿc1Default config not found. \nÿc9 Try reading the kolbot guides."); + LoadConfig.call(); + } catch (e2) { + if (notify) { + console.error(e2); + + throw new Error("Config.init: Error in character config."); } - - if (!include("manualplay/config/" + classes[me.classid] + ".js")) { - throw new Error("ÿc1Failed to load default config."); - } - } catch (e) { - print(e); - original(); } - } - - try { - LoadConfig.call(); - } catch (e2) { - if (notify) { - print("ÿ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; + global.say = (what) => console.log("Tryed to say: " + what); } - } - 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) => print("Tryed to say: " + what); - } - - try { - if (Config.AutoBuild.Enabled === true && !isIncluded("core/Auto/AutoBuild.js") && include("core/Auto/AutoBuild.js")) { - AutoBuild.initialize(); + try { + if (Config.AutoBuild.Enabled === true && includeIfNotIncluded("core/Auto/AutoBuild.js")) { + AutoBuild.initialize(); + } + } catch (e3) { + console.log("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); + console.error(e3); } - } catch (e3) { - print("ÿc8Error in libs/core/AutoBuild.js (AutoBuild system is not active!)"); - print(e3.toSource()); - } -}; + }; +})(Config, Config.init); From 83cd4d00d57c9daf9bab6bb2a5223398029196b3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 2 May 2024 02:09:42 -0400 Subject: [PATCH 389/758] Update Item.js - add date stamp when printing to the item log. Was kinda annoying not knowing what day an item was kept --- d2bs/kolbot/libs/core/Item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index cef390e22..f93d52465 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -554,7 +554,7 @@ const Item = { desc += "$" + (unit.ethereal ? ":eth" : ""); const itemObj = { - title: action + " " + name, + title: (new Date().dateStamp() + " ") + action + " " + name, description: desc, image: code, textColor: unit.quality, From 2166f43df4f3e153d7d3c2212a2a31df5c5a0c63 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 10 May 2024 23:53:20 -0400 Subject: [PATCH 390/758] Cleanup warnings from depreciated methods --- d2bs/kolbot/libs/core/Pickit.js | 2 +- d2bs/kolbot/libs/core/Town.js | 2 +- d2bs/kolbot/libs/scripts/Follower.js | 2 +- d2bs/kolbot/libs/systems/torch/TorchSystem.js | 2 +- d2bs/kolbot/threads/RushThread.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index f5287558a..d934ca62c 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -463,7 +463,7 @@ const Pickit = { case sdk.items.ScrollofIdentify: console.log( "ÿc7Picked up " + stats.color + stats.name - + " ÿc7(" + Town.checkScrolls(stats.classid === sdk.items.ScrollofTownPortal ? "tbk" : "ibk") + "/20)" + + " ÿc7(" + me.checkScrolls(stats.classid === sdk.items.ScrollofTownPortal ? "tbk" : "ibk") + "/20)" ); return true; } diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index d6d503f6f..d5b67d67e 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1642,7 +1642,7 @@ const Town = { !Game.getPlayer(-1, -1, gid) && corpseList.shift(); } - me.classic && Town.checkShard(); + me.classic && me.checkShard(); // re-init skills since we started off without our body Skill.init(); diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index d3b428e1e..30e377ef7 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -960,7 +960,7 @@ const Follower = new Runnable( } else if (!actions.length && getTickCount() - Town.lastChores > Time.minutes(3)) { // no actions currently, lets do some town chores if (me.gold > 1000 - && (me.needPotions() || Town.checkScrolls(sdk.items.TomeofTownPortal) < 5)) { + && (me.needPotions() || me.checkScrolls(sdk.items.TomeofTownPortal) < 5)) { Town.doChores(); } Town.getDistance("portalspot") > 5 && Town.move("portalspot"); diff --git a/d2bs/kolbot/libs/systems/torch/TorchSystem.js b/d2bs/kolbot/libs/systems/torch/TorchSystem.js index 0ca63d695..92589b84f 100644 --- a/d2bs/kolbot/libs/systems/torch/TorchSystem.js +++ b/d2bs/kolbot/libs/systems/torch/TorchSystem.js @@ -322,7 +322,7 @@ const TorchSystem = { } // Free up inventory - Town.needStash() && Town.stash(); + me.needStash() && Town.stash(); // Get the number keys tkeys = me.findItems("pk1", sdk.items.mode.inStorage).length || 0; diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index e43341172..3d3d01a55 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -1035,7 +1035,7 @@ function main () { console.log(JSON.stringify(wpsLeft)); wpsLeft.forEach(function (wp) { - Town.checkScrolls(sdk.items.TomeofTownPortal) <= 5 && (Pather.useWaypoint(sdk.areas.townOf(me.area)) || Town.goToTown()) && Town.doChores(); + me.checkScrolls(sdk.items.TomeofTownPortal) <= 5 && (Pather.useWaypoint(sdk.areas.townOf(me.area)) || Town.goToTown()) && Town.doChores(); Pather.useWaypoint(wp); }); From f89293ba3c2425ded4c8a25332f407f135b17bf8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 11 May 2024 00:16:02 -0400 Subject: [PATCH 391/758] Update Attack.js --- d2bs/kolbot/libs/core/Attack.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 14a81a3ae..bb179ccfe 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1,3 +1,4 @@ +/// /** * @filename Attack.js * @author kolton, theBGuy From 3e59165a11a7949940a1dc683ba79693133e1ea7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 12 May 2024 23:35:18 -0400 Subject: [PATCH 392/758] [BugFix] Cubing - Fix ladder check for single player --- d2bs/kolbot/libs/core/Cubing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 3dddb3d9a..dfa67b894 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -689,7 +689,7 @@ const Cubing = { case sdk.items.runes.Jah: case sdk.items.runes.Cham: case sdk.items.runes.Zod: - if (me.ladder) { + if (me.ladder || !me.realm) { this.recipes.push({ Ingredients: ingredients, Index: index }); } From 1b99000d16930b10c6c7de7e113f0f1ad3f6e450 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 13 May 2024 13:25:32 -0400 Subject: [PATCH 393/758] Small leech script tweaks - Track start time for autobaal in case there is no throne/baal announcement. Track leader area for the same reason. If they enter worldstone then we've missed the baal announcement so set it ourselves - End our loop if diablo has been killed in wakka. Saw it was getting stuck sometimes --- d2bs/kolbot/libs/scripts/AutoBaal.js | 12 ++++++++++++ d2bs/kolbot/libs/scripts/Wakka.js | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index 76845d5fa..5d05b601f 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -180,8 +180,20 @@ const AutoBaal = new Runnable( destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) }))) { + const start = getTickCount(); // do our stuff while partied while (Misc.inMyParty(leader)) { + if (!throneCheck && !baalCheck && getTickCount() - start > Time.minutes(2)) { + // no signal? Lets set it ourselves and check things out + console.log("ÿc4AutoBaal: ÿc0No signal from leader, setting throne signal."); + throneCheck = true; + } + + if (!baalCheck && Misc.getPlayerArea(leader) === sdk.areas.WorldstoneChamber) { + console.log("ÿc4AutoBaal: ÿc0Leader is in Baal chamber, setting baal signal."); + baalCheck = true; + } + if (hotCheck) { if (Config.AutoBaal.FindShrine) { let i; diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 8e45bbb1c..74593b77e 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -252,6 +252,7 @@ const Wakka = new Runnable( if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { if (Loader.scriptName() === "Wakka") { killLeaderTracker = true; + Common.Diablo.done = true; throw new Error("Party leader is running baal"); } else { // kill process @@ -312,6 +313,11 @@ const Wakka = new Runnable( while (Misc.inMyParty(leader)) { try { + if (Common.Diablo.done) { + console.log("Diablo is done"); + break; + } + if (me.inArea(sdk.areas.PandemoniumFortress)) { let portal = Pather.getPortal(sdk.areas.ChaosSanctuary, null); From 88e509e199aebd60b55a13b9127e4efdf4d4c596 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 13 May 2024 13:32:48 -0400 Subject: [PATCH 394/758] Adding `Config.RunningAura`, allow setting specific aura while pathing - By default the paladin uses either vigor or the main attack aura but by setting `Config.RunningAura` you can do something like using Salvation or holy freeze while moving --- d2bs/kolbot/libs/config/Paladin.js | 1 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + d2bs/kolbot/libs/core/Config.js | 2 ++ d2bs/kolbot/libs/core/Pather.js | 6 +++++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index f777c0fbc..3e0e69818 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -612,6 +612,7 @@ function LoadConfig () { // ############################ // Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. Config.Vigor = true; // Swith to Vigor when running + Config.RunningAura = -1; // Aura to use when running, DO NOT use in conjunction with Config.Vigor it will be ignored Config.Charge = true; // Use Charge when running Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index a9241fcfc..e223ea124 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -666,6 +666,7 @@ /* ### PALADIN ### */ Config.AvoidDolls = false; // Try to attack dolls from a greater distance with hammerdins. Config.Vigor = true; // Swith to Vigor when running + Config.RunningAura = sdk.skills.Salvation; // Aura to use when running, DO NOT use in conjunction with Config.Vigor it will be ignored Config.Charge = true; // Use Charge when running Config.Redemption = [50, 50]; // Switch to Redemption after clearing an area if under designated life or mana. Format: [lifepercent, manapercent] diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 6dd7d2375..62edf40a5 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -5,6 +5,7 @@ * */ +/** @type {Record} */ const Scripts = {}; let Config = { @@ -390,6 +391,7 @@ let Config = { Redemption: [0, 0], Charge: false, Vigor: false, + RunningAura: -1, AvoidDolls: false, // Barbarian specific diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index dd19a7ffc..2464b4047 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -618,6 +618,8 @@ const Pather = { && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { if (Skill.canUse(sdk.skills.Vigor)) { Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); + } else if (Config.RunningAura > -1 && Skill.canUse(Config.RunningAura)) { + Skill.setSkill(Config.RunningAura, sdk.skills.hand.Right); } else if (!Config.Vigor && !Attack.auradin && Skill.canUse(sdk.skills.HolyFreeze)) { // Useful in classic to keep mobs cold while you rush them Skill.setSkill(sdk.skills.HolyFreeze, sdk.skills.hand.Right); @@ -643,7 +645,9 @@ const Pather = { if (me.paladin && !me.inTown) { Skill.canUse(sdk.skills.Vigor) ? Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right) - : Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + : Config.RunningAura > -1 + ? Skill.setSkill(Config.RunningAura, sdk.skills.hand.Right) + : Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); } if (this.openDoors(x, y) && getDistance(me.x, me.y, x, y) <= minDist) { From 0f16d1ef4bd2b4c8ab8ef0b356ad1326ea4335b4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 13 May 2024 15:30:47 -0400 Subject: [PATCH 395/758] Add `aura` to SkillData, create `Skill.isAura` - Add `Skill.isAura` and `Skill.canUse` check for `Config.RunningAura` usage - update type definitions --- d2bs/kolbot/libs/core/GameData/SkillData.js | 2 ++ d2bs/kolbot/libs/core/Pather.js | 4 ++-- d2bs/kolbot/libs/core/Skill.js | 9 +++++++++ d2bs/kolbot/sdk/types/Misc.d.ts | 22 +++++++++++++++------ d2bs/kolbot/sdk/types/Skill.d.ts | 2 ++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index e953ac8f2..f591427f8 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -1349,6 +1349,8 @@ this.timed = (_skillData.timed || getBaseStat("skills", skillId, "delay") > 0); /** @type {boolean} */ this.missleSkill = (_skillData.missile || false); + /** @type {boolean} */ + this.aura = getBaseStat("skills", skillId, "aura") === 1; /** @type {number} */ this.charClass = getBaseStat("skills", skillId, "charClass"); /** @type {number} */ diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 2464b4047..4e1a38eed 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -618,7 +618,7 @@ const Pather = { && Skill.setSkill(sdk.skills.Charge, sdk.skills.hand.Left)) { if (Skill.canUse(sdk.skills.Vigor)) { Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right); - } else if (Config.RunningAura > -1 && Skill.canUse(Config.RunningAura)) { + } else if (Skill.isAura(Config.RunningAura) && Skill.canUse(Config.RunningAura)) { Skill.setSkill(Config.RunningAura, sdk.skills.hand.Right); } else if (!Config.Vigor && !Attack.auradin && Skill.canUse(sdk.skills.HolyFreeze)) { // Useful in classic to keep mobs cold while you rush them @@ -645,7 +645,7 @@ const Pather = { if (me.paladin && !me.inTown) { Skill.canUse(sdk.skills.Vigor) ? Skill.setSkill(sdk.skills.Vigor, sdk.skills.hand.Right) - : Config.RunningAura > -1 + : Skill.isAura(Config.RunningAura) && Skill.canUse(Config.RunningAura) ? Skill.setSkill(Config.RunningAura, sdk.skills.hand.Right) : Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); } diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 79d8ada77..dfb1bfbf7 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -419,6 +419,15 @@ return _SkillData.get(skillId).missleSkill; }, + /** + * @param {number} skillId + * @returns {boolean} + */ + isAura: function (skillId = -1) { + if (!_SkillData.has(skillId)) return false; + return _SkillData.get(skillId).aura; + }, + /** * Wereform skill check * @param {number} skillId diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index fcb623ac5..43fdeb993 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -8,12 +8,22 @@ declare global { function click(button: number, shift: number, unit: Unit): void; function click(button: number, shift: number, x: Unit, y: undefined): void; - function inMyParty(name: any): void; - function findPlayer(name: any): void; - function getPlayerUnit(name: any): void; - function getPlayerAct(player: any): void; - function getNearbyPlayerCount(): void; - function getPlayerCount(): void; + function inMyParty(name: string): boolean; + function findPlayer(name: string): Party | false; + function getPlayerUnit(name: string): Player | false; + function getPlayerAct(player: Party | string): number | false; + function getNearbyPlayerCount(): number; + function getPlayerCount(): number; + function getPartyCount(): number; + function checkPartyLevel(levelCheck: number, exclude: string | string[]): boolean; + function getPlayerArea(player: Party | string): number | false; + + type AutoLeaderDetectSettings = { + destination: number | number[], + quitIf: (area: number) => boolean, + timeout: number, + }; + function autoLeaderDetect(givenSettings: AutoLeaderDetectSettings): string | false; function openChest(unit: any): boolean; function openChestsInArea(area?: any, chestIds?: any): void; function openChests(range: any): void; diff --git a/d2bs/kolbot/sdk/types/Skill.d.ts b/d2bs/kolbot/sdk/types/Skill.d.ts index 2e5996d62..7a3d66a0a 100644 --- a/d2bs/kolbot/sdk/types/Skill.d.ts +++ b/d2bs/kolbot/sdk/types/Skill.d.ts @@ -10,6 +10,7 @@ declare global { townSkill: boolean; timed: boolean; missleSkill: boolean; + aura: boolean; charClass: number; reqLevel: number; preReqs: number[]; @@ -80,6 +81,7 @@ declare global { function isTimed(skillId: number): boolean; function townSkill(skillId: number): boolean; function missileSkill(skillId: number): boolean; + function isAura(skillId: number): boolean; function wereFormCheck(skillId: number): boolean; function setSkill(skillId: number, hand?: number, item?: any): boolean; function shapeShift(mode: number | string): boolean; From c80a408a32bf85e9f5e2fbbc7a0fce336e1b43c1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 14 May 2024 01:12:34 -0400 Subject: [PATCH 396/758] Default start acts 2,3 to use Act 1 due to long distance from wp - Acts 2 and 3 both spawn us far away from the wp so use act 1 as our start location instead. TODO: If we are a leech script say mfhelper and the leader starts from a2 we should move to a3 so we can use meshif to get to the portalarea faster --- d2bs/kolbot/libs/core/Loader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index a36ca307a..bc4fc8927 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -246,7 +246,8 @@ const Loader = { // return to first script town if (Loader.firstScriptAct > -1) { - Town.goToTown(Loader.firstScriptAct); + let _act = [2, 3].includes(Loader.firstScriptAct) ? 1 : Loader.firstScriptAct; + Town.goToTown(_act); } }, From 59cd542e08d95c580f8ff1556400b05a6bcc8e63 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 14 May 2024 01:26:44 -0400 Subject: [PATCH 397/758] Update autoLeaderDetect - Instead of always selecting the first player in the right area, build a list so we can perform better checks. Start by sorting the players by level, this fixes coming in late to a game and selecting a follower to be our leader. Next leaders will have portals up (if we've come in late) so look to see if any of the portals belong to one of our suspected leaders --- d2bs/kolbot/libs/core/Misc.js | 39 ++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index eaa503b14..4ac28527c 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -265,8 +265,10 @@ const Misc = (function () { let leader; let startTick = getTickCount(); - let check = typeof settings.quitIf === "function"; + const check = typeof settings.quitIf === "function"; do { + /** @type {Party[]} */ + let suspects = []; let solofail = 0; let suspect = getParty(); // get party object (players in game) @@ -276,15 +278,38 @@ const Misc = (function () { if (check && settings.quitIf(suspect.area)) return false; - // first player not hostile found in destination area... - if (settings.destination.includes(suspect.area) && !getPlayerFlag(me.gid, suspect.gid, 8)) { - leader = suspect.name; // ... is our leader - console.log("ÿc4Autodetected " + leader); - - return leader; + // players not hostile found in destination area... + if (settings.destination.includes(suspect.area) + && !getPlayerFlag(me.gid, suspect.gid, sdk.player.flag.Hostile)) { + suspects.push(copyObj(suspect)); + console.log("ÿc4Autodetected ÿc0" + suspect.name + " (level " + suspect.level + ")"); } } while (suspect.getNext()); + if (suspects.length > 1) { + // if we have more than one suspect, sort by level and they are generally the leaders + suspects.sort((a, b) => b.level - a.level); + + // look for tps from the suspect to the destination area. Sometimes we come in late, happens a lot with pubjoin + for (let suspect of suspects) { + let portal = Pather.getPortal(null, suspect.name); + if (!portal) continue; + + if (portal && settings.destination.includes(portal.objtype)) { + leader = suspect.name; + console.log("ÿc4Autodetect Selecting: ÿc0" + leader + " (Portal found)"); + return leader; + } + } + } + + if (suspects.length) { + leader = suspects[0].name; + console.log("ÿc4Autodetect Selecting: ÿc0" + leader); + + return leader; + } + // empty game, nothing left to do. Or we exceeded our wait time if (solofail === 0 || (getTickCount() - startTick > settings.timeout)) { return false; From 1d36e79f75bbd42fbfd612453ff82c1a9da65015 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 May 2024 14:28:07 -0400 Subject: [PATCH 398/758] Update Wakka hot-tp check - Handle case where tp disappears so we aren't stuck --- d2bs/kolbot/libs/scripts/Wakka.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 74593b77e..2f4698d0d 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -228,7 +228,7 @@ const Wakka = new Runnable( timeout: timeout * 60e3 })); Town.doChores(); - if (!leader) throw new Error("Wakka: Leader not found"); + if (!leader) throw new ScriptError("Wakka: Leader not found"); addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); const Worker = require("../modules/Worker"); @@ -331,9 +331,17 @@ const Wakka = new Runnable( if (!internals.safeTP) { if (checkMonsters(25, false)) { log("hot tp"); - Pather.usePortal(sdk.areas.PandemoniumFortress, null); - - continue; + // go back through portal if it's still there + if (Pather.usePortal(sdk.areas.PandemoniumFortress, null)) { + continue; + } + // if the portal isn't there try to make our own + if (me.canTpToTown() && Town.goToTown()) { + continue; + } + // dodge monsters otherwise - find closest monster + let _closeMon = Attack.getNearestMonster(25); + Attack.deploy(_closeMon, 25, 5, 15); } else { getCoords(); internals.safeTP = true; From 605f3f2405a44c2e91b2f5e5117d5082989d2e45 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 May 2024 19:25:07 -0400 Subject: [PATCH 399/758] [BugFix] Fix uploading to itemlog and saving screenshot - Slashes in datestring were causing it fail, replace slashes with dashes --- d2bs/kolbot/libs/core/Item.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index f93d52465..3ce80fbb1 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -552,9 +552,10 @@ const Item = { keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); desc += "$" + (unit.ethereal ? ":eth" : ""); + const formattedDate = new Date().dateStamp().replace(/\//g, "-"); const itemObj = { - title: (new Date().dateStamp() + " ") + action + " " + name, + title: formattedDate + " " + action + " " + name, description: desc, image: code, textColor: unit.quality, From 9a802e3eddae4948bceefc7c60622bece02b748a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 May 2024 17:11:50 -0400 Subject: [PATCH 400/758] Allow passing in callback function for clearLevel to stop early - Define `breakClearLevel` method in MFHelper to end early if we've been given a new task --- d2bs/kolbot/libs/core/Attack.js | 15 ++++----------- d2bs/kolbot/libs/scripts/MFHelper.js | 13 ++++++++++++- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index bb179ccfe..fc0d79fb9 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1020,9 +1020,10 @@ const Attack = { /** * @description Clear an entire area based on monster spectype * @param {number} spectype + * @param {() => boolean} [cb] callback to end clearing early * @returns {boolean} */ - clearLevel: function (spectype = 0) { + clearLevel: function (spectype = 0, cb = null) { function RoomSort (a, b) { return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); } @@ -1036,8 +1037,6 @@ const Attack = { let myRoom, previousArea; let rooms = []; const currentArea = getArea().id; - const breakClearLevelCheck = !!(Loader.scriptName() === "MFHelper" - && Config.MFHelper.BreakClearLevel && Config.Leader !== ""); do { rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); @@ -1053,14 +1052,8 @@ const Attack = { // get the first room + initialize myRoom var !myRoom && (room = getRoom(me.x, me.y)); - if (breakClearLevelCheck) { - let leader = Misc.findPlayer(Config.Leader); - - if (leader && leader.area !== me.area && !leader.inTown) { - me.overhead("break the clearing in " + getArea().name); - - return true; - } + if (typeof cb === "function" && cb()) { + break; } if (room) { diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index 78d048578..068e4e492 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -39,6 +39,17 @@ const MFHelper = new Runnable( } } + function breakClearLevel () { + if (!Config.MFHelper.BreakClearLevel) return false; + if (taskList.length) { + console.debug("Recieved new task, breaking clearLevel"); + + return true; + } + + return false; + } + addEventListener("chatmsg", chatEvent); Town.doChores(); Town.move("portalspot"); @@ -259,7 +270,7 @@ const MFHelper = new Runnable( throw new Error("Failed to move to clearlevel area"); } } - Attack.clearLevel(Config.ClearType); + Attack.clearLevel(Config.ClearType, breakClearLevel); } catch (killerror2) { console.error(killerror2); } From 77b7944ba5f50e2aeeb9655fc0865b3ee5ac3b9f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 00:30:45 -0400 Subject: [PATCH 401/758] Track bosses killed and add `PathNode` class - Sometimes due to clearing while pathing we kill the boss so keep track of the ids that way when can stop early. Seen this happen most with walking bots moving to get into position for Andariel - Add `PathNode` class, so can stop having to do `{ x: number, y: number }` for nodes --- d2bs/kolbot/libs/core/Attack.js | 15 +++++++++++++++ d2bs/kolbot/libs/core/Pather.js | 10 ++++++++++ d2bs/kolbot/libs/scripts/Andariel.js | 8 ++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index fc0d79fb9..9146810ec 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -29,6 +29,11 @@ const Attack = { NEEDMANA: 3, NOOP: 4, // used for clearing, if we didn't find any monsters to clear it's not exactly a success or fail }, + /** + * Track bosses killed + * @type {Set} + */ + _killed: new Set(), // Initialize attacks init: function () { @@ -265,6 +270,10 @@ const Attack = { : Misc.poll(() => Game.getMonster(classId), 2000, 100)); if (!target) { + if (Attack._killed.has(classId)) { + console.log("ÿc7Killed ÿc0:: " + classId); + return true; + } console.warn("Attack.kill: Target not found"); return Attack.clear(10); } @@ -364,6 +373,9 @@ const Attack = { if (!!target && target.attackable) { console.warn("ÿc1Failed to kill ÿc0" + who + errorInfo); } else { + if (target.dead && target.isBoss) { + Attack._killed.add(target.classid); + } console.log("ÿc7Killed ÿc0:: " + who + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); } @@ -632,6 +644,9 @@ const Attack = { * @todo allow for more aggressive horking here */ if (target.dead || Config.FastPick || Config.FastFindItem) { + if (target.isBoss) { + Attack._killed.add(target.classid); + } if (boss && boss.gid === target.gid && target.dead) { killedBoss = true; console.log( diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 4e1a38eed..0a740ab05 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -5,6 +5,16 @@ * */ +/** + * @constructor + * @param {number} x + * @param {number} y + */ +function PathNode (x, y) { + this.x = x; + this.y = y; +} + /** * Perform certain actions after moving to each node * @todo this needs to be re-worked diff --git a/d2bs/kolbot/libs/scripts/Andariel.js b/d2bs/kolbot/libs/scripts/Andariel.js index bcba77562..21417d17f 100644 --- a/d2bs/kolbot/libs/scripts/Andariel.js +++ b/d2bs/kolbot/libs/scripts/Andariel.js @@ -29,8 +29,12 @@ const Andariel = new Runnable( throw new Error("Failed to move to Catacombs Level 4"); } - Pather.moveTo(22549, 9520); - me.sorceress && me.classic ? killAndariel() : Attack.kill(sdk.monsters.Andariel); + Pather.move(new PathNode(22549, 9520), { callback: function () { + return Attack._killed.has(sdk.monsters.Andariel); + } }); + me.sorceress && me.classic + ? killAndariel() + : Attack.kill(sdk.monsters.Andariel); delay(2000); // Wait for minions to die. Pickit.pickItems(); From d029e556edc89885c95eeff3955c48d65283d341 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 00:52:19 -0400 Subject: [PATCH 402/758] Add `NTIP.addLine` method - Brought over from soloplay, makes sense to be able to easily add a single line --- d2bs/kolbot/libs/core/NTItemParser.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index b717632fb..4f8266730 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -32,6 +32,29 @@ const NTIP_CheckList = []; const NTIP_CheckListNoTier = []; let stringArray = []; +NTIP.addLine = function (itemString, filename = "kolbot") { + const tierdItem = itemString.toLowerCase().includes("tier"); + const info = { + line: NTIP_CheckList.length + 1, + file: filename, + string: itemString + }; + + const line = NTIP.ParseLineInt(itemString, info); + + if (line) { + NTIP_CheckList.push(line); + + if (!tierdItem) { + NTIP_CheckListNoTier.push(line); + } + + stringArray.push(info); + } + + return true; +}; + NTIP.OpenFile = function (filepath, notify) { if (!FileTools.exists(filepath)) { if (notify) { @@ -538,6 +561,7 @@ NTIP.ParseLineInt = function (input, info) { let keyword = p_keyword.toLowerCase(); switch (keyword) { + case "mq": case "maxquantity": value = Number(p_section[i].split("==")[1].match(/\d+/g)); From 402022260073092d50b19b842d038dac4a2eebb3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 00:55:47 -0400 Subject: [PATCH 403/758] Add `Config.FastPickRange` and init `Config.PickitLines` - Add any pickitlines defined from our config during pickit initialization - Update `Unit.prototype.gold` for items - Check usage of `Config.FastPickRange`, defaults to normal `PickRange` if it's not been set --- d2bs/kolbot/libs/core/Config.js | 7 +++++++ d2bs/kolbot/libs/core/Pickit.js | 31 +++++++++++++++++++---------- d2bs/kolbot/libs/core/Prototypes.js | 4 ++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 62edf40a5..c170ea1d2 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -241,12 +241,19 @@ let Config = { MakeRoom: true, ClearInvOnStart: false, FastPick: false, + FastPickRange: 0, ManualPlayPick: false, OpenChests: { Enabled: false, Range: 15, Types: ["chest", "chest3", "armorstand", "weaponrack"] }, + /** + * Each entry should be a tuple of [nipline, filename] + * @example [["[name] == ThulRune # # [maxquantity] == 1", "HeartOfTheOak"]] + * @type {[string, string][]} + */ + PickitLines: [], PickitFiles: [], BeltColumn: [], MinColumn: [], diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index d934ca62c..773c0c17c 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -47,6 +47,14 @@ const Pickit = { */ init: function (notify) { Config.PickitFiles.forEach((file) => NTIP.OpenFile("pickit/" + file, notify)); + Config.PickitLines.forEach(function (line) { + if (Array.isArray(line)) { + let [str, file] = line; + NTIP.addLine(str, file); + } else { + NTIP.addLine(line); + } + }); // check if we can pick up items, only do this is our inventory slots aren't completly locked Pickit.invoLocked = !Config.Inventory.some(row => row.some(el => el > 0)); @@ -275,17 +283,17 @@ const Pickit = { // make sure we have essentials - no pickit files loaded if (rval.result === Pickit.Result.UNWANTED && Config.PickitFiles.length === 0 && Pickit.essentials.includes(unit.itemType) && this.canPick(unit)) { - return resultObj(Pickit.Result.WANTED); + return resultObj(Pickit.Result.WANTED, "Essentials"); } if ((unit.classid === sdk.items.runes.Ort || unit.classid === sdk.items.runes.Ral) && Cubing.repairIngredientCheck(unit)) { - return resultObj(Pickit.Result.UTILITY); + return resultObj(Pickit.Result.UTILITY, "Cubing Repair Ingredients"); } - 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 (CraftingSystem.checkItem(unit)) return resultObj(Pickit.Result.CRAFTING, "Crafting System"); + if (Cubing.checkItem(unit)) return resultObj(Pickit.Result.CUBING, "Cubing"); + if (Runewords.checkItem(unit)) return resultObj(Pickit.Result.RUNEWORD, "Runewords"); // if Gemhunting, pick Item for Cubing, if no other system needs it if (Scripts.GemHunter && rval.result === Pickit.Result.UNWANTED) { @@ -313,7 +321,7 @@ const Pickit = { || (me.gold < me.getRepairCost()) )) { // Gold doesn't ta=ke up room, just pick it up - if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.TRASH); + if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.WANTED, "LowGold"); if (!this.invoLocked) { const itemValue = unit.getItemCost(sdk.items.cost.ToSell); @@ -443,11 +451,13 @@ const Pickit = { item = copyUnit(item); if (stats.classid === sdk.items.Gold) { - if (!item.getStat(sdk.stats.Gold) || item.getStat(sdk.stats.Gold) < stats.gold) { + let _gold = item.gold; + if (!_gold || _gold < stats.gold) { console.log( "ÿc7Picked up " + stats.color - + (item.getStat(sdk.stats.Gold) ? (item.getStat(sdk.stats.Gold) - stats.gold) : stats.gold) + + (_gold ? (_gold - stats.gold) : stats.gold) + " " + stats.name + + (keptLine ? " ÿc0(" + keptLine + ")" : "") ); return true; } @@ -712,6 +722,7 @@ const Pickit = { let item; const _removeList = []; const itemList = []; + const range = Config.FastPickRange || Config.PickRange; for (let gid of this.gidList) { _removeList.push(gid); @@ -720,7 +731,7 @@ const Pickit = { && (!Town.ignoreType(item.itemType) || (item.itemType >= sdk.items.type.HealingPotion && item.itemType <= sdk.items.type.RejuvPotion)) && item.itemType !== sdk.items.type.Gold - && getDistance(me, item) <= Config.PickRange) { + && getDistance(me, item) <= range) { itemList.push(copyUnit(item)); } } @@ -741,7 +752,7 @@ const Pickit = { if (status.result && this.canPick(item) && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType))) { - this.pickItem(item, status.result, status.line, retry); + this.pickItem(item, status.result, status.line + " / (fastpick)", retry); } } } diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index eede32576..139f142b0 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -365,7 +365,11 @@ Object.defineProperties(Unit.prototype, { } }, gold: { + /** @this {Unit} */ get: function () { + if (this.type === sdk.unittype.Item) { + return this.getStat(sdk.stats.Gold); + } return this.getStat(sdk.stats.Gold) + this.getStat(sdk.stats.GoldBank); } }, From 37dac64a465cfdf874edefff1055e31a09560901 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 11:56:54 -0400 Subject: [PATCH 404/758] Add `options` parameter to `Runnable` class, add `bossid` to boss scripts - Add pre/post action, forceTown, and bossid to our runnable class - Skip a boss script if we've already killed the boss --- d2bs/kolbot/libs/core/Attack.js | 10 +++++- d2bs/kolbot/libs/core/Loader.js | 37 +++++++++++++++++--- d2bs/kolbot/libs/scripts/Andariel.js | 5 ++- d2bs/kolbot/libs/scripts/Baal.js | 5 ++- d2bs/kolbot/libs/scripts/BattlemaidSarina.js | 5 ++- d2bs/kolbot/libs/scripts/Bishibosh.js | 5 ++- d2bs/kolbot/libs/scripts/BoneAsh.js | 5 ++- d2bs/kolbot/libs/scripts/Bonesaw.js | 5 ++- d2bs/kolbot/libs/scripts/Coldcrow.js | 5 ++- d2bs/kolbot/libs/scripts/Coldworm.js | 5 ++- d2bs/kolbot/libs/scripts/Corpsefire.js | 5 ++- d2bs/kolbot/libs/scripts/Countess.js | 5 ++- d2bs/kolbot/libs/scripts/CreepingFeature.js | 5 ++- d2bs/kolbot/libs/scripts/Diablo.js | 5 ++- d2bs/kolbot/libs/scripts/Duriel.js | 5 ++- d2bs/kolbot/libs/scripts/Endugu.js | 5 ++- d2bs/kolbot/libs/scripts/Eyeback.js | 5 ++- d2bs/kolbot/libs/scripts/Fangskin.js | 5 ++- d2bs/kolbot/libs/scripts/Icehawk.js | 5 ++- d2bs/kolbot/libs/scripts/Izual.js | 5 ++- d2bs/kolbot/libs/scripts/Mephisto.js | 5 ++- d2bs/kolbot/libs/scripts/Nihlathak.js | 7 ++-- d2bs/kolbot/libs/scripts/Radament.js | 5 ++- d2bs/kolbot/libs/scripts/SharpTooth.js | 5 ++- d2bs/kolbot/libs/scripts/Smith.js | 5 ++- d2bs/kolbot/libs/scripts/Stormtree.js | 5 ++- d2bs/kolbot/libs/scripts/ThreshSocket.js | 5 ++- d2bs/kolbot/libs/scripts/Treehead.js | 5 ++- d2bs/kolbot/sdk/globals.d.ts | 21 +++++------ d2bs/kolbot/sdk/types/Attack.d.ts | 10 ++++++ 30 files changed, 167 insertions(+), 43 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 9146810ec..a56b92ce3 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -31,10 +31,18 @@ const Attack = { }, /** * Track bosses killed - * @type {Set} + * @type {Set} */ _killed: new Set(), + /** + * @param {number | string} id + * @returns {boolean} + */ + haveKilled: function (id) { + return this._killed.has(id); + }, + // Initialize attacks init: function () { if (Config.Wereform) { diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index bc4fc8927..ef3cf9c50 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -7,14 +7,27 @@ /** @typedef {function(): boolean} GlobalScript */ // TODO: preaction/postaction +/** + * @typedef {Object} RunnableOptions + * @property {function(): boolean} preAction + * @property {function(): boolean} postAction + * @property {boolean} forceTown + * @property {number} bossid + */ + /** * @constructor * @param {function(): boolean} action * @param {number} [startArea] + * @param {RunnableOptions} [options] */ -function Runnable (action, startArea) { +function Runnable (action, startArea, options = {}) { this.action = action; this.startArea = startArea; + this.preAction = options.hasOwnProperty("preAction") ? options.preAction : null; + this.postAction = options.hasOwnProperty("postAction") ? options.postAction : null; + this.forceTown = options.hasOwnProperty("forceTown") ? options.forceTown : false; + this.bossid = options.hasOwnProperty("bossid") ? options.bossid : null; } const Loader = { @@ -169,17 +182,29 @@ const Loader = { if (isIncluded("scripts/" + script + ".js")) { try { if (Loader.currentScript instanceof Runnable) { - if (Loader.currentScript.startArea && Loader.scriptIndex === 0) { - Loader.firstScriptAct = sdk.areas.actOf(Loader.currentScript.startArea); + const { startArea, bossid } = Loader.currentScript; + + if (startArea && Loader.scriptIndex === 0) { + Loader.firstScriptAct = sdk.areas.actOf(startArea); } - if (Loader.currentScript.startArea && me.inArea(Loader.currentScript.startArea)) { + if (startArea && me.inArea(startArea)) { this.skipTown.push(script); } + + if (bossid && Attack.haveKilled(bossid)) { + console.log("ÿc2Skipping script: ÿc9" + script + " ÿc2- Boss already killed."); + continue; + } } else if (typeof (Loader.currentScript) !== "function") { - throw new Error("Invalid script function name"); + throw new Error( + "Invalid script function name. " + + "Typeof: " + typeof (Loader.currentScript) + + " Name: " + script + ); } + if (this.skipTown.includes(script) || Town.goToTown()) { console.log("ÿc2Starting script: ÿc9" + script); Messaging.sendToScript("threads/toolsthread.js", JSON.stringify({ currScript: script })); @@ -226,6 +251,8 @@ const Loader = { } catch (error) { if (!(error instanceof ScriptError)) { Misc.errorReport(error, script); + } else { + console.error(error); } } finally { // Dont run for last script as that will clear everything anyway diff --git a/d2bs/kolbot/libs/scripts/Andariel.js b/d2bs/kolbot/libs/scripts/Andariel.js index 21417d17f..adc9d8a50 100644 --- a/d2bs/kolbot/libs/scripts/Andariel.js +++ b/d2bs/kolbot/libs/scripts/Andariel.js @@ -41,5 +41,8 @@ const Andariel = new Runnable( return true; }, - sdk.areas.CatacombsLvl2 + sdk.areas.CatacombsLvl2, + { + bossid: sdk.monsters.Andariel, + } ); diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index b2728435a..5512c8019 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -100,5 +100,8 @@ const Baal = new Runnable( return true; }, - sdk.areas.WorldstoneLvl2 + sdk.areas.WorldstoneLvl2, + { + bossid: sdk.monsters.Baal, + } ); diff --git a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js index 2d4112071..97fe41386 100644 --- a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js +++ b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js @@ -21,5 +21,8 @@ const BattlemaidSarina = new Runnable( return true; }, - sdk.areas.KurastBazaar + sdk.areas.KurastBazaar, + { + bossid: getLocaleString(sdk.locale.monsters.BattlemaidSarina), + } ); diff --git a/d2bs/kolbot/libs/scripts/Bishibosh.js b/d2bs/kolbot/libs/scripts/Bishibosh.js index 5b9ad7b9c..466069424 100644 --- a/d2bs/kolbot/libs/scripts/Bishibosh.js +++ b/d2bs/kolbot/libs/scripts/Bishibosh.js @@ -17,5 +17,8 @@ const Bishibosh = new Runnable( return true; }, - sdk.areas.ColdPlains + sdk.areas.ColdPlains, + { + bossid: getLocaleString(sdk.locale.monsters.Bishibosh), + } ); diff --git a/d2bs/kolbot/libs/scripts/BoneAsh.js b/d2bs/kolbot/libs/scripts/BoneAsh.js index 534482bf6..1df4c8187 100644 --- a/d2bs/kolbot/libs/scripts/BoneAsh.js +++ b/d2bs/kolbot/libs/scripts/BoneAsh.js @@ -18,5 +18,8 @@ const BoneAsh = new Runnable( return true; }, - sdk.areas.InnerCloister + sdk.areas.InnerCloister, + { + bossid: getLocaleString(sdk.locale.monsters.BoneAsh), + } ); diff --git a/d2bs/kolbot/libs/scripts/Bonesaw.js b/d2bs/kolbot/libs/scripts/Bonesaw.js index d3a56aba0..1c77f7193 100644 --- a/d2bs/kolbot/libs/scripts/Bonesaw.js +++ b/d2bs/kolbot/libs/scripts/Bonesaw.js @@ -21,5 +21,8 @@ const Bonesaw = new Runnable( } return true; }, - sdk.areas.GlacialTrail + sdk.areas.GlacialTrail, + { + bossid: getLocaleString(sdk.locale.monsters.BonesawBreaker), + } ); diff --git a/d2bs/kolbot/libs/scripts/Coldcrow.js b/d2bs/kolbot/libs/scripts/Coldcrow.js index d470a41e2..23ddf5a28 100644 --- a/d2bs/kolbot/libs/scripts/Coldcrow.js +++ b/d2bs/kolbot/libs/scripts/Coldcrow.js @@ -20,5 +20,8 @@ const Coldcrow = new Runnable( return true; }, - sdk.areas.ColdPlains + sdk.areas.ColdPlains, + { + bossid: getLocaleString(sdk.locale.monsters.Coldcrow), + } ); diff --git a/d2bs/kolbot/libs/scripts/Coldworm.js b/d2bs/kolbot/libs/scripts/Coldworm.js index 62092b3f6..bf4ee161c 100644 --- a/d2bs/kolbot/libs/scripts/Coldworm.js +++ b/d2bs/kolbot/libs/scripts/Coldworm.js @@ -38,5 +38,8 @@ const Coldworm = new Runnable( return true; }, - sdk.areas.FarOasis + sdk.areas.FarOasis, + { + bossid: sdk.monsters.ColdwormtheBurrower, + } ); diff --git a/d2bs/kolbot/libs/scripts/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js index 2be2eca26..00669072a 100644 --- a/d2bs/kolbot/libs/scripts/Corpsefire.js +++ b/d2bs/kolbot/libs/scripts/Corpsefire.js @@ -21,5 +21,8 @@ const Corpsefire = new Runnable( return true; }, - sdk.areas.ColdPlains + sdk.areas.ColdPlains, + { + bossid: getLocaleString(sdk.locale.monsters.Corpsefire), + } ); diff --git a/d2bs/kolbot/libs/scripts/Countess.js b/d2bs/kolbot/libs/scripts/Countess.js index e9c525875..f029b0bcf 100644 --- a/d2bs/kolbot/libs/scripts/Countess.js +++ b/d2bs/kolbot/libs/scripts/Countess.js @@ -34,5 +34,8 @@ const Countess = new Runnable( return true; }, - sdk.areas.BlackMarsh + sdk.areas.BlackMarsh, + { + bossid: getLocaleString(sdk.locale.monsters.TheCountess), + } ); diff --git a/d2bs/kolbot/libs/scripts/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js index 2ac136fee..aac03c928 100644 --- a/d2bs/kolbot/libs/scripts/CreepingFeature.js +++ b/d2bs/kolbot/libs/scripts/CreepingFeature.js @@ -17,5 +17,8 @@ const CreepingFeature = new Runnable( return true; }, - sdk.areas.LutGholein + sdk.areas.LutGholein, + { + bossid: getLocaleString(sdk.locale.monsters.CreepingFeature), + } ); diff --git a/d2bs/kolbot/libs/scripts/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js index 416d30550..f1b6c5438 100644 --- a/d2bs/kolbot/libs/scripts/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -96,5 +96,8 @@ const Diablo = new Runnable( return true; }, - sdk.areas.RiverofFlame + sdk.areas.RiverofFlame, + { + bossid: sdk.monsters.Diablo, + } ); diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index 733d5fe4c..cd0d0e2f9 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -77,5 +77,8 @@ const Duriel = new Runnable( return true; }, - sdk.areas.CanyonofMagic + sdk.areas.CanyonofMagic, + { + bossid: sdk.monsters.Duriel, + } ); diff --git a/d2bs/kolbot/libs/scripts/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js index 3f94929af..703a91344 100644 --- a/d2bs/kolbot/libs/scripts/Endugu.js +++ b/d2bs/kolbot/libs/scripts/Endugu.js @@ -26,5 +26,8 @@ const Endugu = new Runnable( return true; }, - sdk.areas.FlayerJungle + sdk.areas.FlayerJungle, + { + bossid: getLocaleString(sdk.locale.monsters.WitchDoctorEndugu), + } ); diff --git a/d2bs/kolbot/libs/scripts/Eyeback.js b/d2bs/kolbot/libs/scripts/Eyeback.js index 4bb800610..a9278e1e8 100644 --- a/d2bs/kolbot/libs/scripts/Eyeback.js +++ b/d2bs/kolbot/libs/scripts/Eyeback.js @@ -19,5 +19,8 @@ const Eyeback = new Runnable( return true; }, - sdk.areas.ArreatPlateau + sdk.areas.ArreatPlateau, + { + bossid: getLocaleString(sdk.locale.monsters.EyebacktheUnleashed), + } ); diff --git a/d2bs/kolbot/libs/scripts/Fangskin.js b/d2bs/kolbot/libs/scripts/Fangskin.js index 1a9cc1087..ad4cabcb2 100644 --- a/d2bs/kolbot/libs/scripts/Fangskin.js +++ b/d2bs/kolbot/libs/scripts/Fangskin.js @@ -25,5 +25,8 @@ const Fangskin = new Runnable( return true; }, - sdk.areas.LostCity + sdk.areas.LostCity, + { + bossid: getLocaleString(sdk.locale.monsters.Fangskin), + } ); diff --git a/d2bs/kolbot/libs/scripts/Icehawk.js b/d2bs/kolbot/libs/scripts/Icehawk.js index ed4aa5222..66cada79b 100644 --- a/d2bs/kolbot/libs/scripts/Icehawk.js +++ b/d2bs/kolbot/libs/scripts/Icehawk.js @@ -19,5 +19,8 @@ const Icehawk = new Runnable( return true; }, - sdk.areas.KurastBazaar + sdk.areas.KurastBazaar, + { + bossid: getLocaleString(sdk.locale.monsters.IcehawkRiftwing), + } ); diff --git a/d2bs/kolbot/libs/scripts/Izual.js b/d2bs/kolbot/libs/scripts/Izual.js index 569147d04..ad275fb24 100644 --- a/d2bs/kolbot/libs/scripts/Izual.js +++ b/d2bs/kolbot/libs/scripts/Izual.js @@ -20,5 +20,8 @@ const Izual = new Runnable( return true; }, - sdk.areas.CityoftheDamned + sdk.areas.CityoftheDamned, + { + bossid: sdk.monsters.Izual, + } ); diff --git a/d2bs/kolbot/libs/scripts/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js index 7e5a4186b..34ab22b47 100644 --- a/d2bs/kolbot/libs/scripts/Mephisto.js +++ b/d2bs/kolbot/libs/scripts/Mephisto.js @@ -161,5 +161,8 @@ const Mephisto = new Runnable( return true; }, - sdk.areas.DuranceofHateLvl2 + sdk.areas.DuranceofHateLvl2, + { + bossid: sdk.monsters.Mephisto, + } ); diff --git a/d2bs/kolbot/libs/scripts/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js index 98c0176e0..59aec6cc6 100644 --- a/d2bs/kolbot/libs/scripts/Nihlathak.js +++ b/d2bs/kolbot/libs/scripts/Nihlathak.js @@ -13,7 +13,7 @@ const Nihlathak = new Runnable( !Pather.initialized && Pather.useWaypoint(null, true); // UseWaypoint if set to or if we already have it - if (Config.Nihlathak.UseWaypoint || getWaypoint(Pather.wpAreas.indexOf(sdk.areas.HallsofPain))) { + if (Config.Nihlathak.UseWaypoint || me.haveWaypoint(sdk.areas.HallsofPain)) { Pather.useWaypoint(sdk.areas.HallsofPain); } else { if (Pather.journeyTo(sdk.areas.NihlathaksTemple)) { @@ -38,5 +38,8 @@ const Nihlathak = new Runnable( return true; }, - sdk.areas.Harrogath + sdk.areas.Harrogath, + { + bossid: sdk.monsters.Nihlathak, + } ); diff --git a/d2bs/kolbot/libs/scripts/Radament.js b/d2bs/kolbot/libs/scripts/Radament.js index abcb5367d..388ff58e9 100644 --- a/d2bs/kolbot/libs/scripts/Radament.js +++ b/d2bs/kolbot/libs/scripts/Radament.js @@ -22,5 +22,8 @@ const Radament = new Runnable( return true; }, - sdk.areas.A2SewersLvl2 + sdk.areas.A2SewersLvl2, + { + bossid: sdk.monsters.Radament, + } ); diff --git a/d2bs/kolbot/libs/scripts/SharpTooth.js b/d2bs/kolbot/libs/scripts/SharpTooth.js index a5467d65d..f99ab88d0 100644 --- a/d2bs/kolbot/libs/scripts/SharpTooth.js +++ b/d2bs/kolbot/libs/scripts/SharpTooth.js @@ -22,5 +22,8 @@ const SharpTooth = new Runnable( return true; }, - sdk.areas.FrigidHighlands + sdk.areas.FrigidHighlands, + { + bossid: getLocaleString(sdk.locale.monsters.SharpToothSayer), + } ); diff --git a/d2bs/kolbot/libs/scripts/Smith.js b/d2bs/kolbot/libs/scripts/Smith.js index 73a12baba..d63ea64df 100644 --- a/d2bs/kolbot/libs/scripts/Smith.js +++ b/d2bs/kolbot/libs/scripts/Smith.js @@ -20,5 +20,8 @@ const Smith = new Runnable( return true; }, - sdk.areas.OuterCloister + sdk.areas.OuterCloister, + { + bossid: getLocaleString(sdk.locale.monsters.TheSmith), + } ); diff --git a/d2bs/kolbot/libs/scripts/Stormtree.js b/d2bs/kolbot/libs/scripts/Stormtree.js index 125dca98e..7cac460da 100644 --- a/d2bs/kolbot/libs/scripts/Stormtree.js +++ b/d2bs/kolbot/libs/scripts/Stormtree.js @@ -19,5 +19,8 @@ const Stormtree = new Runnable( return true; }, - sdk.areas.LowerKurast + sdk.areas.LowerKurast, + { + bossid: getLocaleString(sdk.locale.monsters.Stormtree), + } ); diff --git a/d2bs/kolbot/libs/scripts/ThreshSocket.js b/d2bs/kolbot/libs/scripts/ThreshSocket.js index 2a7ec75a9..692005f8c 100644 --- a/d2bs/kolbot/libs/scripts/ThreshSocket.js +++ b/d2bs/kolbot/libs/scripts/ThreshSocket.js @@ -20,5 +20,8 @@ const ThreshSocket = new Runnable( return true; }, - sdk.areas.ArreatPlateau + sdk.areas.ArreatPlateau, + { + bossid: getLocaleString(sdk.locale.monsters.ThreshSocket), + } ); diff --git a/d2bs/kolbot/libs/scripts/Treehead.js b/d2bs/kolbot/libs/scripts/Treehead.js index e687e8b59..0c48bac58 100644 --- a/d2bs/kolbot/libs/scripts/Treehead.js +++ b/d2bs/kolbot/libs/scripts/Treehead.js @@ -19,5 +19,8 @@ const Treehead = new Runnable( return true; }, - sdk.areas.DarkWood + sdk.areas.DarkWood, + { + bossid: getLocaleString(sdk.locale.monsters.TreeheadWoodFist), + } ); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 6629621c0..3162e02be 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -122,6 +122,10 @@ declare global { symmetricDifference(other: Set): Set; } + interface Date { + dateStamp(): string; + } + class ScriptError extends Error { } @@ -232,15 +236,6 @@ declare global { class Frame extends Box { } - interface ClassAttack { - doAttack(unit: Monster, preattack?: boolean): number - afterAttack(any?: any): void - doCast(unit: Monster, timedSkill: number, untimedSkill: number): number - - // Self defined - decideSkill(unit: Monster, skipSkill?: number[]): [number, number] - } - /** * @todo Figure out what each of these actually returns to properly document them */ @@ -429,6 +424,11 @@ declare global { readonly curseable: boolean; readonly scareable: boolean; readonly attacking: boolean; + readonly fireRes: number; + readonly coldRes: number; + readonly lightRes: number; + readonly poisonRes: number; + resPenalty: number; getEnchant(type: number): boolean; hasEnchant(...enchants: number): boolean @@ -529,6 +529,7 @@ declare global { readonly isGheeds: boolean; readonly durabilityPercent: number; readonly isCharm: boolean; + readonly gold: number; getColor(): number; getBodyLoc(): number[]; @@ -1075,7 +1076,7 @@ declare global { function playSound(num: number): void function quit(): never function quitGame(): never - function say(what: string): void + function say(what: string, force?: boolean): void function clickParty(player: Party, type: 0 | 1 | 2 | 3 | 4) function weaponSwitch(): void function transmute(): void diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 8e6246f8d..1068c7353 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -6,11 +6,21 @@ declare global { CANTATTACK: 2, // need to fix the ambiguity between this result and Failed NEEDMANA: 3 } + interface ClassAttack { + doAttack(unit: Monster, preattack?: boolean): AttackResult + afterAttack(any?: any): void + doCast(unit: Monster, timedSkill: number, untimedSkill: number): AttackResult + + // Self defined + decideSkill(unit: Monster, skipSkill?: number[]): { timed: number, untimed: number } + } namespace Attack { const infinity: boolean; const auradin: boolean; const monsterObjects: number[]; const Result: AttackResult; + const _killed: Set; + function haveKilled(id: number | string): boolean; function init(): void; function checkSlot(slot?: 0 | 1): boolean; function getPrimarySlot(): 0 | 1; From 7dcf86faee398252ca97bd08604a45890ce72e4f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 13:17:57 -0400 Subject: [PATCH 405/758] Fix `Attack.clear` adding to _killed set before target is dead --- d2bs/kolbot/libs/core/Attack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index a56b92ce3..4c5547ebe 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -652,7 +652,7 @@ const Attack = { * @todo allow for more aggressive horking here */ if (target.dead || Config.FastPick || Config.FastFindItem) { - if (target.isBoss) { + if (target.isBoss && target.dead) { Attack._killed.add(target.classid); } if (boss && boss.gid === target.gid && target.dead) { From 1212c8232a0486b8331ed13e797817a990cf486f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 18:02:55 -0400 Subject: [PATCH 406/758] Handle adding superuniques to killed set --- d2bs/kolbot/libs/core/Attack.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 4c5547ebe..911aaa447 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -652,8 +652,9 @@ const Attack = { * @todo allow for more aggressive horking here */ if (target.dead || Config.FastPick || Config.FastFindItem) { - if (target.isBoss && target.dead) { - Attack._killed.add(target.classid); + if ((target.isBoss || target.uniqueid > 0) && target.dead) { + // TODO: add uniqueids to sdk + Attack._killed.add(target.isBoss ? target.classid : target.name); } if (boss && boss.gid === target.gid && target.dead) { killedBoss = true; From 30ed7a18e483e630dbe1a025db9d900fa47d03e4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 18:18:52 -0400 Subject: [PATCH 407/758] Small tweaks - `GetFade`: Use both fires, sometimes we just fail to get into position so moving to the alternate one can fix it - `Wakka`: Fix `getParty(leader).area is undefined, sometimes getParty fails - `AutoBaal`: Change thrown error to ScriptError and lower the waittime before setting throneCheck ourselves --- d2bs/kolbot/libs/scripts/AutoBaal.js | 6 +++--- d2bs/kolbot/libs/scripts/GetFade.js | 13 ++++++++++--- d2bs/kolbot/libs/scripts/Wakka.js | 7 ++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index 5d05b601f..473610953 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -159,12 +159,12 @@ const AutoBaal = new Runnable( }; // critical error - can't reach harrogath - if (!Town.goToTown(5)) throw new Error("Town.goToTown failed."); + if (!Town.goToTown(5)) throw new ScriptError("Town.goToTown failed."); if (Config.Leader) { leader = Config.Leader; if (!Misc.poll(() => Misc.inMyParty(leader), Time.seconds(30), Time.seconds(1))) { - throw new Error("AutoBaal: Leader not partied"); + throw new ScriptError("AutoBaal: Leader not partied"); } } @@ -183,7 +183,7 @@ const AutoBaal = new Runnable( const start = getTickCount(); // do our stuff while partied while (Misc.inMyParty(leader)) { - if (!throneCheck && !baalCheck && getTickCount() - start > Time.minutes(2)) { + if (!throneCheck && !baalCheck && getTickCount() - start > Time.seconds(90)) { // no signal? Lets set it ourselves and check things out console.log("ÿc4AutoBaal: ÿc0No signal from leader, setting throne signal."); throneCheck = true; diff --git a/d2bs/kolbot/libs/scripts/GetFade.js b/d2bs/kolbot/libs/scripts/GetFade.js index 7d2d8805a..e2f84cdf1 100644 --- a/d2bs/kolbot/libs/scripts/GetFade.js +++ b/d2bs/kolbot/libs/scripts/GetFade.js @@ -26,10 +26,15 @@ const GetFade = new Runnable( Pather.useWaypoint(sdk.areas.RiverofFlame, true); Precast.doPrecast(true); + /** @type {PathSettings} */ + const pathSettings = { minDist: 2 }; + const leftFire = new PathNode(7787, 5873); + const rightFire = new PathNode(7811, 5872); + // check if item is on switch let mainSlot; - Pather.moveTo(7811, 5872); + Pather.move(rightFire, pathSettings); if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { mainSlot = me.weaponswitch; @@ -47,8 +52,10 @@ const GetFade = new Runnable( if (retry > 5) { throw new Error("Failed to get fade"); } - Pather.randMove(); - Pather.moveTo(7811, 5872); + Pather.randMove(-1, 1, -1, 1, 3); + retry % 2 === 0 + ? Pather.move(leftFire, pathSettings) + : Pather.move(rightFire, pathSettings); timeout = getTickCount() + Time.minutes(1); } delay(3); diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 2f4698d0d..491154ac5 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -200,9 +200,10 @@ const Wakka = new Runnable( if (!leaderUnit || !copyUnit(leaderUnit).x) { leaderUnit = Game.getPlayer(leader); } - return !!leaderUnit - ? leaderUnit.area - : getParty(leader).area; + if (leaderUnit && leaderUnit.area !== 0) return leaderUnit.area; + let pLeader = getParty(leader); + if (pLeader && pLeader.area !== 0) return pLeader.area; + return 0; }; const log = function (msg = "") { From 26ced2002e44d392ab27d812dbe97a313f62b6a4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 18:38:45 -0400 Subject: [PATCH 408/758] Fix `Attack.kill` for adding correct info to `_killed` set --- d2bs/kolbot/libs/core/Attack.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 911aaa447..44ea4ed55 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -381,8 +381,10 @@ const Attack = { if (!!target && target.attackable) { console.warn("ÿc1Failed to kill ÿc0" + who + errorInfo); } else { - if (target.dead && target.isBoss) { - Attack._killed.add(target.classid); + if (target.dead && (target.isBoss || target.uniqueid > -1)) { + // a little obnoxious, but we need to track bosses killed and this handles if we are attempting to check by id or name + target.isBoss && Attack._killed.add(target.classid); + target.uniqueid > -1 && Attack._killed.add(target.name); } console.log("ÿc7Killed ÿc0:: " + who + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick)); } @@ -654,7 +656,8 @@ const Attack = { if (target.dead || Config.FastPick || Config.FastFindItem) { if ((target.isBoss || target.uniqueid > 0) && target.dead) { // TODO: add uniqueids to sdk - Attack._killed.add(target.isBoss ? target.classid : target.name); + target.isBoss && Attack._killed.add(target.classid); + target.uniqueid > -1 && Attack._killed.add(target.name); } if (boss && boss.gid === target.gid && target.dead) { killedBoss = true; From 4387113f14d596ab6723dc9ef1011c2142ad64ad Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 19:16:49 -0400 Subject: [PATCH 409/758] Add bossid check to `Loader.runScript` --- d2bs/kolbot/libs/core/Loader.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index ef3cf9c50..72deae1c6 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -314,8 +314,15 @@ const Loader = { if (isIncluded("scripts/" + script + ".js")) { try { if (Loader.currentScript instanceof Runnable) { - if (Loader.currentScript.startArea && me.inArea(Loader.currentScript.startArea)) { - this.skipTown.push(script); + const { startArea, bossid } = Loader.currentScript; + + if (startArea && me.inArea(startArea)) { + Loader.skipTown.push(script); + } + + if (bossid && Attack.haveKilled(bossid)) { + console.log("ÿc2Skipping script: ÿc9" + script + " ÿc2- Boss already killed."); + return true; } } else if (typeof (Loader.currentScript) !== "function") { throw new Error("Invalid script function name"); From 1643707ca8345ce0bb67288916a7405148efa392 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 May 2024 19:19:35 -0400 Subject: [PATCH 410/758] Add `Attack.haveKilled` checks for scripts that handle more than one boss or action - Some of the scripts have sub-actions so we can't just use the single bossid check to know if we need to skip them but we still don't want to waste time moving to a boss we know is already dead --- d2bs/kolbot/libs/scripts/AncientTunnels.js | 1 + d2bs/kolbot/libs/scripts/Coldworm.js | 2 +- d2bs/kolbot/libs/scripts/Eldritch.js | 7 ++++- d2bs/kolbot/libs/scripts/Follower.js | 2 +- d2bs/kolbot/libs/scripts/Hephasto.js | 21 ++++++++------- d2bs/kolbot/libs/scripts/Mausoleum.js | 4 +-- d2bs/kolbot/libs/scripts/Pindleskin.js | 30 ++++++++++++---------- d2bs/kolbot/libs/scripts/Rakanishu.js | 14 ++++++---- d2bs/kolbot/libs/scripts/Summoner.js | 8 +++++- d2bs/kolbot/libs/scripts/Tristram.js | 4 ++- 10 files changed, 58 insertions(+), 35 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/AncientTunnels.js b/d2bs/kolbot/libs/scripts/AncientTunnels.js index 73458419b..85ac30908 100644 --- a/d2bs/kolbot/libs/scripts/AncientTunnels.js +++ b/d2bs/kolbot/libs/scripts/AncientTunnels.js @@ -21,6 +21,7 @@ const AncientTunnels = new Runnable( try { if (Config.AncientTunnels.KillDarkElder + && !Attack.haveKilled(getLocaleString(sdk.locale.monsters.DarkElder)) && Pather.moveToPresetMonster(me.area, sdk.monsters.preset.DarkElder)) { Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.DarkElder)); } diff --git a/d2bs/kolbot/libs/scripts/Coldworm.js b/d2bs/kolbot/libs/scripts/Coldworm.js index bf4ee161c..db50afe2a 100644 --- a/d2bs/kolbot/libs/scripts/Coldworm.js +++ b/d2bs/kolbot/libs/scripts/Coldworm.js @@ -12,7 +12,7 @@ const Coldworm = new Runnable( Precast.doPrecast(true); // Beetleburst, added by 13ack.Stab - if (Config.Coldworm.KillBeetleburst) { + if (Config.Coldworm.KillBeetleburst && !Attack.haveKilled(getLocaleString(sdk.locale.monsters.Beetleburst))) { try { if (!Pather.moveToPresetMonster(me.area, sdk.monsters.preset.Beetleburst)) { throw new Error("Failed to move to Beetleburst"); diff --git a/d2bs/kolbot/libs/scripts/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js index 4458b75d3..98ad2a24c 100644 --- a/d2bs/kolbot/libs/scripts/Eldritch.js +++ b/d2bs/kolbot/libs/scripts/Eldritch.js @@ -30,7 +30,11 @@ const Eldritch = new Runnable( } try { - if (Config.Eldritch.KillShenk && Pather.moveToExit(sdk.areas.BloodyFoothills, false) && Pather.moveTo(3876, 5130)) { + + if (Config.Eldritch.KillShenk + && !Attack.haveKilled(getLocaleString(sdk.locale.monsters.ShenktheOverseer)) + && Pather.moveToExit(sdk.areas.BloodyFoothills, false) + && Pather.moveTo(3876, 5130)) { Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); } } catch (e) { @@ -38,6 +42,7 @@ const Eldritch = new Runnable( } if (Config.Eldritch.KillDacFarren + && !Attack.haveKilled(getLocaleString(sdk.locale.monsters.DacFarren)) && Pather.moveNearPreset(sdk.areas.BloodyFoothills, sdk.unittype.Monster, sdk.monsters.preset.DacFarren, 10) && Pather.moveTo(4478, 5108)) { Attack.kill(getLocaleString(sdk.locale.monsters.DacFarren)); diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 30e377ef7..2a1dbdf9c 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -180,7 +180,7 @@ const Follower = new Runnable( ["wp", (me.name + " wp")] .forEach(key => _actions.set(key, () => { if (me.inTown) return; - if (getWaypoint(Pather.wpAreas.indexOf(me.area))) return; + if (me.haveWaypoint(me.area)) return; if (!Pather.wpAreas.includes(me.area)) return; if (Pather.getWP(me.area)) { announce("Got Wp in " + getAreaName(me.area)); diff --git a/d2bs/kolbot/libs/scripts/Hephasto.js b/d2bs/kolbot/libs/scripts/Hephasto.js index c8c81ae53..73bc4fc53 100644 --- a/d2bs/kolbot/libs/scripts/Hephasto.js +++ b/d2bs/kolbot/libs/scripts/Hephasto.js @@ -11,17 +11,20 @@ const Hephasto = new Runnable( Pather.useWaypoint(sdk.areas.RiverofFlame); Precast.doPrecast(true); - if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge)) { - throw new Error("Failed to move to Hephasto"); - } + if (!Attack.haveKilled(sdk.monsters.Hephasto)) { + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.HellForge)) { + throw new Error("Failed to move to Hephasto"); + } - try { - Attack.kill(getLocaleString(sdk.locale.monsters.HephastoTheArmorer)); - } catch (e) { - print("Heph not found. Carry on"); - } + try { + Attack.kill(sdk.monsters.Hephasto); + } catch (e) { + console.log("Heph not found. Carry on"); + } - Pickit.pickItems(); + Pickit.pickItems(); + } + Config.Hephasto.ClearRiver && Attack.clearLevel(Config.Hephasto.ClearType); return true; diff --git a/d2bs/kolbot/libs/scripts/Mausoleum.js b/d2bs/kolbot/libs/scripts/Mausoleum.js index 29e4f8331..9c2ff187d 100644 --- a/d2bs/kolbot/libs/scripts/Mausoleum.js +++ b/d2bs/kolbot/libs/scripts/Mausoleum.js @@ -11,7 +11,7 @@ const Mausoleum = new Runnable( Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); - if (Config.Mausoleum.KillBishibosh) { + if (Config.Mausoleum.KillBishibosh && !Attack.haveKilled(getLocaleString(sdk.locale.monsters.Bishibosh))) { Pather.moveToPresetMonster(sdk.areas.ColdPlains, sdk.monsters.preset.Bishibosh); Attack.kill(getLocaleString(sdk.locale.monsters.Bishibosh)); Pickit.pickItems(); @@ -19,7 +19,7 @@ const Mausoleum = new Runnable( if (!Pather.moveToExit(sdk.areas.BurialGrounds, true)) throw new Error("Failed to move to Burial Grounds"); - if (Config.Mausoleum.KillBloodRaven) { + if (Config.Mausoleum.KillBloodRaven && !Attack.haveKilled(sdk.monsters.BloodRaven)) { Pather.moveToPresetMonster(sdk.areas.BurialGrounds, sdk.monsters.preset.BloodRaven, { minDist: me.sorceress && Pather.canTeleport() ? 30 : 5 }); diff --git a/d2bs/kolbot/libs/scripts/Pindleskin.js b/d2bs/kolbot/libs/scripts/Pindleskin.js index a5e775405..8550ff181 100644 --- a/d2bs/kolbot/libs/scripts/Pindleskin.js +++ b/d2bs/kolbot/libs/scripts/Pindleskin.js @@ -10,24 +10,26 @@ const Pindleskin = new Runnable( Town.goToTown((Config.Pindleskin.UseWaypoint ? undefined : 5)); Town.doChores(); - if (Config.Pindleskin.UseWaypoint) { - Pather.useWaypoint(sdk.areas.HallsofPain); - Precast.doPrecast(true); + if (!Attack.haveKilled(getLocaleString(sdk.locale.monsters.Pindleskin))) { + if (Config.Pindleskin.UseWaypoint) { + Pather.useWaypoint(sdk.areas.HallsofPain); + Precast.doPrecast(true); - if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.NihlathaksTemple], true)) { - throw new Error("Failed to move to Nihlahak's Temple"); + if (!Pather.moveToExit([sdk.areas.HallsofAnguish, sdk.areas.NihlathaksTemple], true)) { + throw new Error("Failed to move to Nihlahak's Temple"); + } + } else { + if (!Pather.journeyTo(sdk.areas.NihlathaksTemple)) throw new Error("Failed to use portal."); + Precast.doPrecast(true); } - } else { - if (!Pather.journeyTo(sdk.areas.NihlathaksTemple)) throw new Error("Failed to use portal."); - Precast.doPrecast(true); - } - Pather.moveTo(10058, 13234); + Pather.moveTo(10058, 13234); - try { - Attack.kill(getLocaleString(sdk.locale.monsters.Pindleskin)); - } catch (e) { - console.error(e); + try { + Attack.kill(getLocaleString(sdk.locale.monsters.Pindleskin)); + } catch (e) { + console.error(e); + } } if (Config.Pindleskin.KillNihlathak) { diff --git a/d2bs/kolbot/libs/scripts/Rakanishu.js b/d2bs/kolbot/libs/scripts/Rakanishu.js index 97e378822..3aa437e97 100644 --- a/d2bs/kolbot/libs/scripts/Rakanishu.js +++ b/d2bs/kolbot/libs/scripts/Rakanishu.js @@ -1,6 +1,6 @@ /** * @filename Rakanishu.js -* @author kolton +* @author kolton, theBGuy * @desc kill Rakanishu and optionally Griswold * */ @@ -11,12 +11,16 @@ const Rakanishu = new Runnable( Pather.useWaypoint(sdk.areas.StonyField); Precast.doPrecast(true); - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) { - throw new Error("Failed to move to Rakanishu"); + if (!Attack.haveKilled(getLocaleString(sdk.locale.monsters.Rakanishu))) { + if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) { + throw new Error("Failed to move to Rakanishu"); + } + Attack.kill(getLocaleString(sdk.locale.monsters.Rakanishu)); } - Attack.kill(getLocaleString(sdk.locale.monsters.Rakanishu)); - if (Config.Rakanishu.KillGriswold && Pather.getPortal(sdk.areas.Tristram)) { + if (Config.Rakanishu.KillGriswold + && !Attack.haveKilled(sdk.monsters.Griswold) + && Pather.getPortal(sdk.areas.Tristram)) { if (!Pather.usePortal(sdk.areas.Tristram)) throw new Error("Failed to move to Tristram"); Pather.moveTo(25149, 5180); diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index 9e739f5fc..3a8328075 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -11,7 +11,7 @@ const Summoner = new Runnable( Pather.useWaypoint(sdk.areas.ArcaneSanctuary); Precast.doPrecast(true); - if (Config.Summoner.FireEye) { + if (Config.Summoner.FireEye && !Attack.haveKilled(getLocaleString(sdk.locale.monsters.FireEye))) { try { if (!Pather.usePortal(null)) throw new Error("Failed to move to Fire Eye"); Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.FireEye)); @@ -23,6 +23,12 @@ const Summoner = new Runnable( if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) { throw new Error("Failed to move back to arcane"); } + + if (Attack.haveKilled(sdk.monsters.TheSummoner)) { + console.log("Summoner already dead"); + return true; + } + if (!Pather.moveToPresetObject(me.area, sdk.quest.chest.Journal, { offX: -3, offY: -3 })) { throw new Error("Failed to move to Summoner"); } diff --git a/d2bs/kolbot/libs/scripts/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js index 1e0958ef5..c0b06b501 100644 --- a/d2bs/kolbot/libs/scripts/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -37,7 +37,9 @@ const Tristram = new Runnable( throw new Error("Failed to move to Rakanishu"); } - Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Rakanishu)); + if (!Attack.haveKilled(getLocaleString(sdk.locale.monsters.Rakanishu))) { + Attack.clear(15, 0, getLocaleString(sdk.locale.monsters.Rakanishu)); + } while (!Pather.usePortal(sdk.areas.Tristram)) { Attack.securePosition(me.x, me.y, 10, 1000); From 3dc0ac5f00171415f39d604dc234b7c924a7acce Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 May 2024 00:49:10 -0400 Subject: [PATCH 411/758] Move `startArea` into RunnableOptions parameter - Add a default `preaction` for scripts to only run townchores if it's been more than a minute since our last chores. --- d2bs/kolbot/libs/core/Loader.js | 33 +- d2bs/kolbot/libs/scripts/Abaddon.js | 5 +- d2bs/kolbot/libs/scripts/AncientTunnels.js | 5 +- d2bs/kolbot/libs/scripts/Andariel.js | 3 +- d2bs/kolbot/libs/scripts/AutoBaal.js | 5 +- d2bs/kolbot/libs/scripts/Baal.js | 7 +- d2bs/kolbot/libs/scripts/BaalAssistant.js | 5 +- d2bs/kolbot/libs/scripts/BaalHelper.js | 16 +- d2bs/kolbot/libs/scripts/BattleOrders.js | 6 +- d2bs/kolbot/libs/scripts/BattlemaidSarina.js | 3 +- d2bs/kolbot/libs/scripts/Bishibosh.js | 3 +- d2bs/kolbot/libs/scripts/BoBarbHelper.js | 18 +- d2bs/kolbot/libs/scripts/BoneAsh.js | 5 +- d2bs/kolbot/libs/scripts/Bonesaw.js | 3 +- d2bs/kolbot/libs/scripts/ChestMania.js | 5 +- .../libs/scripts/ClassicChaosAssistant.js | 4 +- d2bs/kolbot/libs/scripts/ClearAnyArea.js | 2 - d2bs/kolbot/libs/scripts/Coldcrow.js | 3 +- d2bs/kolbot/libs/scripts/Coldworm.js | 3 +- d2bs/kolbot/libs/scripts/ControlBot.js | 5 +- d2bs/kolbot/libs/scripts/Corpsefire.js | 3 +- d2bs/kolbot/libs/scripts/Countess.js | 4 +- d2bs/kolbot/libs/scripts/Cows.js | 5 +- d2bs/kolbot/libs/scripts/Crafting.js | 5 +- d2bs/kolbot/libs/scripts/CreepingFeature.js | 3 +- d2bs/kolbot/libs/scripts/DeveloperMode.js | 3 + d2bs/kolbot/libs/scripts/Diablo.js | 8 +- d2bs/kolbot/libs/scripts/DiabloHelper.js | 5 +- d2bs/kolbot/libs/scripts/Duriel.js | 3 +- d2bs/kolbot/libs/scripts/Eldritch.js | 5 +- d2bs/kolbot/libs/scripts/Endugu.js | 5 +- d2bs/kolbot/libs/scripts/Eyeback.js | 5 +- d2bs/kolbot/libs/scripts/Fangskin.js | 3 +- d2bs/kolbot/libs/scripts/Frozenstein.js | 23 +- d2bs/kolbot/libs/scripts/Gamble.js | 3 + d2bs/kolbot/libs/scripts/GemHunter.js | 11 +- d2bs/kolbot/libs/scripts/GetCube.js | 5 +- d2bs/kolbot/libs/scripts/GetEssences.js | 8 +- d2bs/kolbot/libs/scripts/GetFade.js | 5 +- d2bs/kolbot/libs/scripts/GetKeys.js | 16 +- d2bs/kolbot/libs/scripts/GhostBusters.js | 5 +- d2bs/kolbot/libs/scripts/Hephasto.js | 5 +- d2bs/kolbot/libs/scripts/IPHunter.js | 3 + d2bs/kolbot/libs/scripts/Icehawk.js | 5 +- d2bs/kolbot/libs/scripts/Idle.js | 5 +- d2bs/kolbot/libs/scripts/Izual.js | 3 +- d2bs/kolbot/libs/scripts/KillDclone.js | 5 +- d2bs/kolbot/libs/scripts/KurastTemples.js | 5 +- d2bs/kolbot/libs/scripts/MFHelper.js | 3 + d2bs/kolbot/libs/scripts/Mausoleum.js | 5 +- d2bs/kolbot/libs/scripts/Mephisto.js | 3 +- d2bs/kolbot/libs/scripts/Nihlathak.js | 3 +- d2bs/kolbot/libs/scripts/OrgTorch.js | 4 +- d2bs/kolbot/libs/scripts/OuterSteppes.js | 5 +- d2bs/kolbot/libs/scripts/Pindleskin.js | 5 +- d2bs/kolbot/libs/scripts/Pit.js | 5 +- d2bs/kolbot/libs/scripts/Radament.js | 3 +- d2bs/kolbot/libs/scripts/Rakanishu.js | 5 +- d2bs/kolbot/libs/scripts/Rushee.js | 1308 ++++++------ d2bs/kolbot/libs/scripts/Rusher.js | 113 +- d2bs/kolbot/libs/scripts/SealLeecher.js | 6 +- d2bs/kolbot/libs/scripts/SharpTooth.js | 3 +- d2bs/kolbot/libs/scripts/ShopBot.js | 3 + d2bs/kolbot/libs/scripts/Smith.js | 3 +- d2bs/kolbot/libs/scripts/Snapchip.js | 5 +- d2bs/kolbot/libs/scripts/Stormtree.js | 3 +- d2bs/kolbot/libs/scripts/Summoner.js | 5 +- d2bs/kolbot/libs/scripts/ThreshSocket.js | 3 +- d2bs/kolbot/libs/scripts/Tombs.js | 5 +- d2bs/kolbot/libs/scripts/Travincal.js | 5 +- d2bs/kolbot/libs/scripts/TravincalLeech.js | 5 +- d2bs/kolbot/libs/scripts/Treehead.js | 3 +- d2bs/kolbot/libs/scripts/Tristram.js | 4 +- d2bs/kolbot/libs/scripts/TristramLeech.js | 7 +- .../kolbot/libs/scripts/UndergroundPassage.js | 5 +- d2bs/kolbot/libs/scripts/UserAddon.js | 3 + d2bs/kolbot/libs/scripts/WPGetter.js | 3 +- d2bs/kolbot/libs/scripts/Wakka.js | 5 +- d2bs/kolbot/libs/scripts/Worldstone.js | 5 +- d2bs/kolbot/libs/scripts/rungamex.js | 1781 +++++++++++++++++ d2bs/kolbot/libs/scripts/rungamex.txt | 1781 +++++++++++++++++ 81 files changed, 4512 insertions(+), 896 deletions(-) create mode 100644 d2bs/kolbot/libs/scripts/rungamex.js create mode 100644 d2bs/kolbot/libs/scripts/rungamex.txt diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 72deae1c6..6520b4edf 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -9,22 +9,29 @@ // TODO: preaction/postaction /** * @typedef {Object} RunnableOptions - * @property {function(): boolean} preAction + * @property {function(): any} preAction * @property {function(): boolean} postAction * @property {boolean} forceTown * @property {number} bossid + * @property {number} startArea */ /** * @constructor - * @param {function(): boolean} action - * @param {number} [startArea] + * @param {function(): boolean} action * @param {RunnableOptions} [options] */ -function Runnable (action, startArea, options = {}) { +function Runnable (action, options = {}) { this.action = action; - this.startArea = startArea; - this.preAction = options.hasOwnProperty("preAction") ? options.preAction : null; + this.startArea = options.hasOwnProperty("startArea") ? options.startArea : null; + this.preAction = options.hasOwnProperty("preAction") + ? options.preAction + : function chores () { + // TODO: We need to do a dry-run of chores to actually determine if we need it or not + if (getTickCount() - Town.lastChores > Time.minutes(1)) { + Town.doChores(); + } + }; this.postAction = options.hasOwnProperty("postAction") ? options.postAction : null; this.forceTown = options.hasOwnProperty("forceTown") ? options.forceTown : false; this.bossid = options.hasOwnProperty("bossid") ? options.bossid : null; @@ -182,20 +189,24 @@ const Loader = { if (isIncluded("scripts/" + script + ".js")) { try { if (Loader.currentScript instanceof Runnable) { - const { startArea, bossid } = Loader.currentScript; + const { startArea, bossid, preAction } = Loader.currentScript; if (startArea && Loader.scriptIndex === 0) { Loader.firstScriptAct = sdk.areas.actOf(startArea); } - if (startArea && me.inArea(startArea)) { - this.skipTown.push(script); - } - if (bossid && Attack.haveKilled(bossid)) { console.log("ÿc2Skipping script: ÿc9" + script + " ÿc2- Boss already killed."); continue; } + + if (preAction && typeof preAction === "function") { + preAction(); + } + + if (startArea && me.inArea(startArea)) { + this.skipTown.push(script); + } } else if (typeof (Loader.currentScript) !== "function") { throw new Error( "Invalid script function name. " diff --git a/d2bs/kolbot/libs/scripts/Abaddon.js b/d2bs/kolbot/libs/scripts/Abaddon.js index 5c21a1b6a..6735b6870 100644 --- a/d2bs/kolbot/libs/scripts/Abaddon.js +++ b/d2bs/kolbot/libs/scripts/Abaddon.js @@ -7,7 +7,6 @@ const Abaddon = new Runnable( function Abaddon () { - Town.doChores(); Pather.useWaypoint(sdk.areas.FrigidHighlands); Precast.doPrecast(true); @@ -20,5 +19,7 @@ const Abaddon = new Runnable( return true; }, - sdk.areas.FrigidHighlands + { + startArea: sdk.areas.FrigidHighlands + } ); diff --git a/d2bs/kolbot/libs/scripts/AncientTunnels.js b/d2bs/kolbot/libs/scripts/AncientTunnels.js index 85ac30908..0e4d53ba6 100644 --- a/d2bs/kolbot/libs/scripts/AncientTunnels.js +++ b/d2bs/kolbot/libs/scripts/AncientTunnels.js @@ -7,7 +7,6 @@ const AncientTunnels = new Runnable( function AncientTunnels () { - Town.doChores(); Pather.useWaypoint(sdk.areas.LostCity); Precast.doPrecast(true); @@ -34,5 +33,7 @@ const AncientTunnels = new Runnable( return true; }, - sdk.areas.LostCity + { + startArea: sdk.areas.LostCity + } ); diff --git a/d2bs/kolbot/libs/scripts/Andariel.js b/d2bs/kolbot/libs/scripts/Andariel.js index adc9d8a50..bcc729d47 100644 --- a/d2bs/kolbot/libs/scripts/Andariel.js +++ b/d2bs/kolbot/libs/scripts/Andariel.js @@ -21,7 +21,6 @@ const Andariel = new Runnable( return target.dead; }; - Town.doChores(); Pather.useWaypoint(sdk.areas.CatacombsLvl2); Precast.doPrecast(true); @@ -41,8 +40,8 @@ const Andariel = new Runnable( return true; }, - sdk.areas.CatacombsLvl2, { + startArea: sdk.areas.CatacombsLvl2, bossid: sdk.monsters.Andariel, } ); diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index 473610953..13f6707de 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -284,5 +284,8 @@ const AutoBaal = new Runnable( return true; }, - sdk.areas.Harrogath + { + startArea: sdk.areas.Harrogath, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index 5512c8019..91351edce 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -47,11 +47,6 @@ const Baal = new Runnable( say(string); }; - // We can skip chores if it's been less than a minute since we last did them - if (getTickCount() - Town.lastChores > Time.minutes(1)) { - Town.doChores(); - } - if (!me.inArea(sdk.areas.WorldstoneLvl2)) { Config.RandomPrecast ? Precast.doRandomPrecast(true, sdk.areas.WorldstoneLvl2) @@ -100,8 +95,8 @@ const Baal = new Runnable( return true; }, - sdk.areas.WorldstoneLvl2, { + startArea: sdk.areas.WorldstoneLvl2, bossid: sdk.monsters.Baal, } ); diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index dc8547fdc..1891e3e42 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -501,5 +501,8 @@ const BaalAssistant = new Runnable( return true; }, - sdk.areas.Harrogath + { + startArea: sdk.areas.Harrogath, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 8ae89ca9c..89e908b99 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -9,14 +9,8 @@ const BaalHelper = new Runnable( function BaalHelper () { include("core/Common/Baal.js"); - Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); - Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); Town.goToTown(5); - - if (getTickCount() - Town.lastChores > Time.minutes(1)) { - Town.doChores(); - } Config.RandomPrecast && Precast.needOutOfTownCast() ? Precast.doRandomPrecast(true, sdk.areas.Harrogath) : Precast.doPrecast(true); @@ -136,6 +130,16 @@ const BaalHelper = new Runnable( } return true; + }, + { + preAction: function () { + Config.BaalHelper.KillNihlathak && Loader.runScript("Nihlathak"); + Config.BaalHelper.FastChaos && Loader.runScript("Diablo", () => Config.Diablo.Fast = true); + + if (getTickCount() - Town.lastChores > Time.minutes(1)) { + Town.doChores(); + } + } } ); diff --git a/d2bs/kolbot/libs/scripts/BattleOrders.js b/d2bs/kolbot/libs/scripts/BattleOrders.js index a8683828d..763cb4cf6 100644 --- a/d2bs/kolbot/libs/scripts/BattleOrders.js +++ b/d2bs/kolbot/libs/scripts/BattleOrders.js @@ -156,8 +156,6 @@ const BattleOrders = new Runnable( } // START - Town.doChores(); - try { Pather.useWaypoint(sdk.areas.CatacombsLvl2, true); } catch (wperror) { @@ -306,5 +304,7 @@ const BattleOrders = new Runnable( removeEventListener("chatmsg", chatEvent); } }, - sdk.areas.CatacombsLvl2 + { + startArea: sdk.areas.CatacombsLvl2 + } ); diff --git a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js index 97fe41386..39c379d85 100644 --- a/d2bs/kolbot/libs/scripts/BattlemaidSarina.js +++ b/d2bs/kolbot/libs/scripts/BattlemaidSarina.js @@ -7,7 +7,6 @@ const BattlemaidSarina = new Runnable( function BattlemaidSarina () { - Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar); Precast.doPrecast(true); @@ -21,8 +20,8 @@ const BattlemaidSarina = new Runnable( return true; }, - sdk.areas.KurastBazaar, { + startArea: sdk.areas.KurastBazaar, bossid: getLocaleString(sdk.locale.monsters.BattlemaidSarina), } ); diff --git a/d2bs/kolbot/libs/scripts/Bishibosh.js b/d2bs/kolbot/libs/scripts/Bishibosh.js index 466069424..feb723307 100644 --- a/d2bs/kolbot/libs/scripts/Bishibosh.js +++ b/d2bs/kolbot/libs/scripts/Bishibosh.js @@ -7,7 +7,6 @@ const Bishibosh = new Runnable( function Bishibosh () { - Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); @@ -17,8 +16,8 @@ const Bishibosh = new Runnable( return true; }, - sdk.areas.ColdPlains, { + startArea: sdk.areas.ColdPlains, bossid: getLocaleString(sdk.locale.monsters.Bishibosh), } ); diff --git a/d2bs/kolbot/libs/scripts/BoBarbHelper.js b/d2bs/kolbot/libs/scripts/BoBarbHelper.js index d04e85d93..f7f0c0d95 100644 --- a/d2bs/kolbot/libs/scripts/BoBarbHelper.js +++ b/d2bs/kolbot/libs/scripts/BoBarbHelper.js @@ -55,15 +55,15 @@ const BoBarbHelper = new Runnable( if (!Config.QuitList) { showConsole(); - print("set Config.QuitList in character settings"); - print("if you don't I will idle indefinitely"); + console.log("set Config.QuitList in character settings"); + console.log("if you don't I will idle indefinitely"); } if (me.hardcore && Config.LifeChicken <= 0) { showConsole(); - print("on HARDCORE"); - print("you should set Config.LifeChicken"); - print("monsters can find their way to wps ..."); + console.log("on HARDCORE"); + console.log("you should set Config.LifeChicken"); + console.log("monsters can find their way to wps ..."); delay(2000); hideConsole(); me.overhead("set LifeChiken to 40"); @@ -77,8 +77,8 @@ const BoBarbHelper = new Runnable( Pather.useWaypoint(Config.BoBarbHelper.Wp); } catch (e) { showConsole(); - print("Failed to move to BO WP"); - print("make sure I have " + getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); + console.log("Failed to move to BO WP"); + console.log("make sure I have " + getAreaName(Config.BoBarbHelper.Wp) + " waypoint"); delay(20000); return true; @@ -103,5 +103,7 @@ const BoBarbHelper = new Runnable( return true; }, - Config.BoBarbHelper.Wp + { + startArea: Config.BoBarbHelper.Wp + } ); diff --git a/d2bs/kolbot/libs/scripts/BoneAsh.js b/d2bs/kolbot/libs/scripts/BoneAsh.js index 1df4c8187..46f635fc3 100644 --- a/d2bs/kolbot/libs/scripts/BoneAsh.js +++ b/d2bs/kolbot/libs/scripts/BoneAsh.js @@ -1,13 +1,12 @@ /** * @filename BoneAsh.js -* @author kolton +* @author kolton, theBGuy * @desc kill Bone Ash * */ const BoneAsh = new Runnable( function BoneAsh () { - Town.doChores(); Pather.useWaypoint(sdk.areas.InnerCloister); Precast.doPrecast(true); @@ -18,8 +17,8 @@ const BoneAsh = new Runnable( return true; }, - sdk.areas.InnerCloister, { + startArea: sdk.areas.InnerCloister, bossid: getLocaleString(sdk.locale.monsters.BoneAsh), } ); diff --git a/d2bs/kolbot/libs/scripts/Bonesaw.js b/d2bs/kolbot/libs/scripts/Bonesaw.js index 1c77f7193..352b76802 100644 --- a/d2bs/kolbot/libs/scripts/Bonesaw.js +++ b/d2bs/kolbot/libs/scripts/Bonesaw.js @@ -7,7 +7,6 @@ const Bonesaw = new Runnable( function Bonesaw () { - Town.doChores(); Pather.useWaypoint(sdk.areas.GlacialTrail); Precast.doPrecast(true); @@ -21,8 +20,8 @@ const Bonesaw = new Runnable( } return true; }, - sdk.areas.GlacialTrail, { + startArea: sdk.areas.GlacialTrail, bossid: getLocaleString(sdk.locale.monsters.BonesawBreaker), } ); diff --git a/d2bs/kolbot/libs/scripts/ChestMania.js b/d2bs/kolbot/libs/scripts/ChestMania.js index cb9d29dab..0541dc8f2 100644 --- a/d2bs/kolbot/libs/scripts/ChestMania.js +++ b/d2bs/kolbot/libs/scripts/ChestMania.js @@ -9,7 +9,6 @@ const ChestMania = new Runnable( function ChestMania () { - Town.doChores(); const nextToTown = [ sdk.areas.BloodMoor, sdk.areas.RockyWaste, @@ -39,5 +38,7 @@ const ChestMania = new Runnable( return true; }, - Object.values(Config.ChestMania).find((act) => act.length > 0)[0][0] + { + startArea: Object.values(Config.ChestMania).find((act) => act.length > 0)[0][0] + } ); diff --git a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js index d93dc20c0..2f4bdd839 100644 --- a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js +++ b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js @@ -129,5 +129,7 @@ const ClassicChaosAssistant = new Runnable( delay(10); } }, - sdk.areas.RiverofFlame + { + startArea: sdk.areas.RiverofFlame + } ); diff --git a/d2bs/kolbot/libs/scripts/ClearAnyArea.js b/d2bs/kolbot/libs/scripts/ClearAnyArea.js index 1245607ba..1840740ef 100644 --- a/d2bs/kolbot/libs/scripts/ClearAnyArea.js +++ b/d2bs/kolbot/libs/scripts/ClearAnyArea.js @@ -7,8 +7,6 @@ const ClearAnyArea = new Runnable( function ClearAnyArea () { - Town.doChores(); - for (let area of Config.ClearAnyArea.AreaList) { try { if (Pather.journeyTo(area)) { diff --git a/d2bs/kolbot/libs/scripts/Coldcrow.js b/d2bs/kolbot/libs/scripts/Coldcrow.js index 23ddf5a28..496e46054 100644 --- a/d2bs/kolbot/libs/scripts/Coldcrow.js +++ b/d2bs/kolbot/libs/scripts/Coldcrow.js @@ -7,7 +7,6 @@ const Coldcrow = new Runnable( function Coldcrow () { - Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); @@ -20,8 +19,8 @@ const Coldcrow = new Runnable( return true; }, - sdk.areas.ColdPlains, { + startArea: sdk.areas.ColdPlains, bossid: getLocaleString(sdk.locale.monsters.Coldcrow), } ); diff --git a/d2bs/kolbot/libs/scripts/Coldworm.js b/d2bs/kolbot/libs/scripts/Coldworm.js index db50afe2a..e23cb2531 100644 --- a/d2bs/kolbot/libs/scripts/Coldworm.js +++ b/d2bs/kolbot/libs/scripts/Coldworm.js @@ -7,7 +7,6 @@ const Coldworm = new Runnable( function Coldworm () { - Town.doChores(); Pather.useWaypoint(sdk.areas.FarOasis); Precast.doPrecast(true); @@ -38,8 +37,8 @@ const Coldworm = new Runnable( return true; }, - sdk.areas.FarOasis, { + startArea: sdk.areas.FarOasis, bossid: sdk.monsters.ColdwormtheBurrower, } ); diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 13083fd27..1d85ac9de 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1298,5 +1298,8 @@ const ControlBot = new Runnable( return true; }, - sdk.areas.RogueEncampment + { + startArea: sdk.areas.RogueEncampment, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js index 00669072a..83bd8a449 100644 --- a/d2bs/kolbot/libs/scripts/Corpsefire.js +++ b/d2bs/kolbot/libs/scripts/Corpsefire.js @@ -7,7 +7,6 @@ const Corpsefire = new Runnable( function Corpsefire () { - Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); @@ -21,8 +20,8 @@ const Corpsefire = new Runnable( return true; }, - sdk.areas.ColdPlains, { + startArea: sdk.areas.ColdPlains, bossid: getLocaleString(sdk.locale.monsters.Corpsefire), } ); diff --git a/d2bs/kolbot/libs/scripts/Countess.js b/d2bs/kolbot/libs/scripts/Countess.js index f029b0bcf..4c2e6a8b1 100644 --- a/d2bs/kolbot/libs/scripts/Countess.js +++ b/d2bs/kolbot/libs/scripts/Countess.js @@ -7,7 +7,6 @@ const Countess = new Runnable( function Countess () { - Town.doChores(); Pather.useWaypoint(sdk.areas.BlackMarsh); Precast.doPrecast(true); @@ -17,7 +16,6 @@ const Countess = new Runnable( ], true)) throw new Error("Failed to move to Countess"); let poi = Game.getPresetObject(me.area, sdk.objects.SuperChest); - if (!poi) throw new Error("Failed to move to Countess (preset not found)"); switch (poi.roomx * 5 + poi.x) { @@ -34,8 +32,8 @@ const Countess = new Runnable( return true; }, - sdk.areas.BlackMarsh, { + startArea: sdk.areas.BlackMarsh, bossid: getLocaleString(sdk.locale.monsters.TheCountess), } ); diff --git a/d2bs/kolbot/libs/scripts/Cows.js b/d2bs/kolbot/libs/scripts/Cows.js index aaa1eb4cf..e67af2e67 100644 --- a/d2bs/kolbot/libs/scripts/Cows.js +++ b/d2bs/kolbot/libs/scripts/Cows.js @@ -154,5 +154,8 @@ const Cows = new Runnable( return true; }, - sdk.areas.RogueEncampment + { + startArea: sdk.areas.RogueEncampment, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/Crafting.js b/d2bs/kolbot/libs/scripts/Crafting.js index 1891e9880..1af6bb943 100644 --- a/d2bs/kolbot/libs/scripts/Crafting.js +++ b/d2bs/kolbot/libs/scripts/Crafting.js @@ -119,7 +119,10 @@ const Crafting = new Runnable( delay(2000); } }, - sdk.areas.RogueEncampment + { + startArea: sdk.areas.RogueEncampment, + preAction: null + } ); function getNPCName (idList) { diff --git a/d2bs/kolbot/libs/scripts/CreepingFeature.js b/d2bs/kolbot/libs/scripts/CreepingFeature.js index aac03c928..043824f62 100644 --- a/d2bs/kolbot/libs/scripts/CreepingFeature.js +++ b/d2bs/kolbot/libs/scripts/CreepingFeature.js @@ -7,7 +7,6 @@ const CreepingFeature = new Runnable( function CreepingFeature () { - Town.doChores(); Town.goToTown(2); Pather.journeyTo(sdk.areas.StonyTombLvl2); @@ -17,8 +16,8 @@ const CreepingFeature = new Runnable( return true; }, - sdk.areas.LutGholein, { + startArea: sdk.areas.LutGholein, bossid: getLocaleString(sdk.locale.monsters.CreepingFeature), } ); diff --git a/d2bs/kolbot/libs/scripts/DeveloperMode.js b/d2bs/kolbot/libs/scripts/DeveloperMode.js index ef9f8c165..9bd7fbc8d 100644 --- a/d2bs/kolbot/libs/scripts/DeveloperMode.js +++ b/d2bs/kolbot/libs/scripts/DeveloperMode.js @@ -280,5 +280,8 @@ const DeveloperMode = new Runnable( } return true; + }, + { + preAction: null } ); diff --git a/d2bs/kolbot/libs/scripts/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js index f1b6c5438..2f9ad97a8 100644 --- a/d2bs/kolbot/libs/scripts/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -16,12 +16,6 @@ const Diablo = new Runnable( Common.Diablo.clearRadius = Config.Diablo.ClearRadius; // START - - // We can skip chores if it's been less than a minute since we last did them - if (getTickCount() - Town.lastChores > Time.minutes(1)) { - Town.doChores(); - } - if (!me.inArea(sdk.areas.RiverofFlame)) { !!Config.RandomPrecast ? Precast.doRandomPrecast(true, sdk.areas.RiverofFlame) @@ -96,8 +90,8 @@ const Diablo = new Runnable( return true; }, - sdk.areas.RiverofFlame, { + startArea: sdk.areas.RiverofFlame, bossid: sdk.monsters.Diablo, } ); diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index a84f41094..f42c09981 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -11,7 +11,6 @@ const DiabloHelper = new Runnable( this.Leader = Config.Leader; Common.Diablo.waitForGlow = true; Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; - Town.doChores(); const Worker = require("../modules/Worker"); try { @@ -178,5 +177,7 @@ const DiabloHelper = new Runnable( return true; }, - sdk.areas.PandemoniumFortress + { + startArea: sdk.areas.PandemoniumFortress + } ); diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index cd0d0e2f9..c8690e4cc 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -77,8 +77,9 @@ const Duriel = new Runnable( return true; }, - sdk.areas.CanyonofMagic, { + startArea: sdk.areas.CanyonofMagic, bossid: sdk.monsters.Duriel, + preAction: null, } ); diff --git a/d2bs/kolbot/libs/scripts/Eldritch.js b/d2bs/kolbot/libs/scripts/Eldritch.js index 98ad2a24c..25451b411 100644 --- a/d2bs/kolbot/libs/scripts/Eldritch.js +++ b/d2bs/kolbot/libs/scripts/Eldritch.js @@ -7,7 +7,6 @@ const Eldritch = new Runnable( function Eldritch () { - Town.doChores(); Pather.useWaypoint(sdk.areas.FrigidHighlands); Precast.doPrecast(true); let { x, y } = me; @@ -50,5 +49,7 @@ const Eldritch = new Runnable( return true; }, - sdk.areas.FrigidHighlands + { + startArea: sdk.areas.FrigidHighlands + } ); diff --git a/d2bs/kolbot/libs/scripts/Endugu.js b/d2bs/kolbot/libs/scripts/Endugu.js index 703a91344..e80d2ebfb 100644 --- a/d2bs/kolbot/libs/scripts/Endugu.js +++ b/d2bs/kolbot/libs/scripts/Endugu.js @@ -1,13 +1,12 @@ /** * @filename Endugu.js -* @author kolton +* @author kolton, theBGuy * @desc kill Witch Doctor Endugu * */ const Endugu = new Runnable( function Endugu () { - Town.doChores(); Pather.useWaypoint(sdk.areas.FlayerJungle); Precast.doPrecast(true); @@ -26,8 +25,8 @@ const Endugu = new Runnable( return true; }, - sdk.areas.FlayerJungle, { + startArea: sdk.areas.FlayerJungle, bossid: getLocaleString(sdk.locale.monsters.WitchDoctorEndugu), } ); diff --git a/d2bs/kolbot/libs/scripts/Eyeback.js b/d2bs/kolbot/libs/scripts/Eyeback.js index a9278e1e8..0c0247daf 100644 --- a/d2bs/kolbot/libs/scripts/Eyeback.js +++ b/d2bs/kolbot/libs/scripts/Eyeback.js @@ -1,13 +1,12 @@ /** * @filename Eyeback.js -* @author kolton +* @author kolton, theBGuy * @desc kill Eyeback the Unleashed * */ const Eyeback = new Runnable( function Eyeback () { - Town.doChores(); Pather.useWaypoint(sdk.areas.ArreatPlateau); Precast.doPrecast(true); @@ -19,8 +18,8 @@ const Eyeback = new Runnable( return true; }, - sdk.areas.ArreatPlateau, { + startArea: sdk.areas.ArreatPlateau, bossid: getLocaleString(sdk.locale.monsters.EyebacktheUnleashed), } ); diff --git a/d2bs/kolbot/libs/scripts/Fangskin.js b/d2bs/kolbot/libs/scripts/Fangskin.js index ad4cabcb2..15ed5f984 100644 --- a/d2bs/kolbot/libs/scripts/Fangskin.js +++ b/d2bs/kolbot/libs/scripts/Fangskin.js @@ -7,7 +7,6 @@ const Fangskin = new Runnable( function Fangskin () { - Town.doChores(); Pather.useWaypoint(sdk.areas.LostCity); Precast.doPrecast(true); @@ -25,8 +24,8 @@ const Fangskin = new Runnable( return true; }, - sdk.areas.LostCity, { + startArea: sdk.areas.LostCity, bossid: getLocaleString(sdk.locale.monsters.Fangskin), } ); diff --git a/d2bs/kolbot/libs/scripts/Frozenstein.js b/d2bs/kolbot/libs/scripts/Frozenstein.js index 9c9de0b59..0934e4d1a 100644 --- a/d2bs/kolbot/libs/scripts/Frozenstein.js +++ b/d2bs/kolbot/libs/scripts/Frozenstein.js @@ -1,25 +1,34 @@ /** * @filename Frozenstein.js -* @author kolton +* @author kolton, theBGuy * @desc kill Frozenstein and optionally clear Frozen River * */ const Frozenstein = new Runnable( function Frozenstein () { - Town.doChores(); Pather.useWaypoint(sdk.areas.CrystalizedPassage); Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, -5, -5)) { - throw new Error("Failed to move to Frozenstein"); + if (!Pather.moveToExit(sdk.areas.FrozenRiver, true)) { + throw new Error("Failed to move to frozen river"); } - Attack.kill(getLocaleString(sdk.locale.monsters.Frozenstein)); + if (!Attack.haveKilled(getLocaleString(sdk.locale.monsters.Frozenstein))) { + if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) + || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform, -5, -5)) { + throw new Error("Failed to move to Frozenstein"); + } + + Attack.kill(getLocaleString(sdk.locale.monsters.Frozenstein)); + } else { + console.log("Frozenstein already dead"); + } Config.Frozenstein.ClearFrozenRiver && Attack.clearLevel(Config.ClearType); return true; }, - sdk.areas.CrystalizedPassage + { + startArea: sdk.areas.CrystalizedPassage + } ); diff --git a/d2bs/kolbot/libs/scripts/Gamble.js b/d2bs/kolbot/libs/scripts/Gamble.js index e29280f58..5bf462515 100644 --- a/d2bs/kolbot/libs/scripts/Gamble.js +++ b/d2bs/kolbot/libs/scripts/Gamble.js @@ -59,5 +59,8 @@ const Gamble = new Runnable( // eslint-disable-next-line no-unreachable return true; + }, + { + preAction: null } ); diff --git a/d2bs/kolbot/libs/scripts/GemHunter.js b/d2bs/kolbot/libs/scripts/GemHunter.js index 141530107..7bdaf09b4 100644 --- a/d2bs/kolbot/libs/scripts/GemHunter.js +++ b/d2bs/kolbot/libs/scripts/GemHunter.js @@ -17,20 +17,20 @@ const GemHunter = new Runnable( Town.doChores(); Town.getGem(); if (Town.getGemsInInv().length === 0) { - print("ÿc4GemHunterÿc0: no gems in inventory - aborting."); + console.log("ÿc4GemHunterÿc0: no gems in inventory - aborting."); return false; } for (let area of Config.GemHunter.AreaList) { if (Town.getGemsInInv().length > 0) { - print("ÿc4GemHunterÿc0: Moving to " + getAreaName(area)); + console.log("ÿc4GemHunterÿc0: Moving to " + getAreaName(area)); Pather.journeyTo(area); if (i % 2 === 0) Precast.doPrecast(true); if (Misc.getShrinesInArea(area, sdk.shrines.Gem, true)) { Pickit.pickItems(); - print("ÿc4GemHunterÿc0: found a gem Shrine"); + console.log("ÿc4GemHunterÿc0: found a gem Shrine"); if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { - print("ÿc4GemHunterÿc0: Getting a new Gem in Town."); + console.log("ÿc4GemHunterÿc0: Getting a new Gem in Town."); Town.visitTown(); // Go to Town and do chores. Will throw an error if it fails to return from Town. Town.getGem(); } @@ -38,5 +38,8 @@ const GemHunter = new Runnable( } } return true; + }, + { + preAction: null } ); diff --git a/d2bs/kolbot/libs/scripts/GetCube.js b/d2bs/kolbot/libs/scripts/GetCube.js index 7c4ec7ed6..481efc24f 100644 --- a/d2bs/kolbot/libs/scripts/GetCube.js +++ b/d2bs/kolbot/libs/scripts/GetCube.js @@ -34,5 +34,8 @@ const GetCube = new Runnable( return (!!cube && Storage.Stash.MoveTo(cube)); }, - sdk.areas.HallsoftheDeadLvl2 + { + startArea: sdk.areas.HallsoftheDeadLvl2, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/GetEssences.js b/d2bs/kolbot/libs/scripts/GetEssences.js index 61aabbdb4..343742e8b 100644 --- a/d2bs/kolbot/libs/scripts/GetEssences.js +++ b/d2bs/kolbot/libs/scripts/GetEssences.js @@ -8,12 +8,10 @@ const GetEssences = new Runnable( function GetEssences () { - Town.doChores(); - /** - * @param {number} essence - * @returns {number} - */ + * @param {number} essence + * @returns {number} + */ const count = function (essence) { return me.getItemsEx(essence, sdk.items.mode.inStorage).length; }; diff --git a/d2bs/kolbot/libs/scripts/GetFade.js b/d2bs/kolbot/libs/scripts/GetFade.js index e2f84cdf1..13f329993 100644 --- a/d2bs/kolbot/libs/scripts/GetFade.js +++ b/d2bs/kolbot/libs/scripts/GetFade.js @@ -65,5 +65,8 @@ const GetFade = new Runnable( mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); } }, - sdk.areas.RiverofFlame + { + startArea: sdk.areas.RiverofFlame, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/GetKeys.js b/d2bs/kolbot/libs/scripts/GetKeys.js index 8d7572413..219282cc2 100644 --- a/d2bs/kolbot/libs/scripts/GetKeys.js +++ b/d2bs/kolbot/libs/scripts/GetKeys.js @@ -1,15 +1,21 @@ /** * @filename GetKeys.js -* @author kolton +* @author kolton, theBGuy * @desc get them keys * */ const GetKeys = new Runnable( function GetKeys () { - Town.doChores(); + /** + * @param {number} id + * @returns {number} + */ + const count = function (id) { + return me.getItemsEx(id, sdk.items.mode.inStorage).length; + }; - if (me.getItemsEx(sdk.items.quest.KeyofTerror, sdk.items.mode.inStorage).length < 3) { + if (count(sdk.items.quest.KeyofTerror) < 3) { try { Loader.runScript("Countess"); } catch (e) { @@ -17,7 +23,7 @@ const GetKeys = new Runnable( } } - if (me.getItemsEx(sdk.items.quest.KeyofHate, sdk.items.mode.inStorage).length < 3) { + if (count(sdk.items.quest.KeyofHate) < 3) { try { Loader.runScript("Summoner", () => Config.Summoner.FireEye = false); } catch (e) { @@ -25,7 +31,7 @@ const GetKeys = new Runnable( } } - if (me.getItemsEx(sdk.items.quest.KeyofDestruction, sdk.items.mode.inStorage).length < 3) { + if (count(sdk.items.quest.KeyofDestruction) < 3) { try { Loader.runScript("Nihlathak"); } catch (e) { diff --git a/d2bs/kolbot/libs/scripts/GhostBusters.js b/d2bs/kolbot/libs/scripts/GhostBusters.js index 5165594b7..38d74b1fd 100644 --- a/d2bs/kolbot/libs/scripts/GhostBusters.js +++ b/d2bs/kolbot/libs/scripts/GhostBusters.js @@ -114,5 +114,8 @@ const GhostBusters = new Runnable( return true; }, - sdk.areas.BlackMarsh + { + startArea: sdk.areas.BlackMarsh, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/Hephasto.js b/d2bs/kolbot/libs/scripts/Hephasto.js index 73bc4fc53..1858ecdd2 100644 --- a/d2bs/kolbot/libs/scripts/Hephasto.js +++ b/d2bs/kolbot/libs/scripts/Hephasto.js @@ -7,7 +7,6 @@ const Hephasto = new Runnable( function Hephasto () { - Town.doChores(); Pather.useWaypoint(sdk.areas.RiverofFlame); Precast.doPrecast(true); @@ -29,5 +28,7 @@ const Hephasto = new Runnable( return true; }, - sdk.areas.RiverofFlame + { + startArea: sdk.areas.RiverofFlame + } ); diff --git a/d2bs/kolbot/libs/scripts/IPHunter.js b/d2bs/kolbot/libs/scripts/IPHunter.js index 1302d48a0..28dee9ee5 100644 --- a/d2bs/kolbot/libs/scripts/IPHunter.js +++ b/d2bs/kolbot/libs/scripts/IPHunter.js @@ -57,5 +57,8 @@ const IPHunter = new Runnable( D2Bot.printToConsole("IPHunter: IP was [" + ip + "]", sdk.colors.D2Bot.Gray); return true; + }, + { + preAction: null } ); diff --git a/d2bs/kolbot/libs/scripts/Icehawk.js b/d2bs/kolbot/libs/scripts/Icehawk.js index 66cada79b..022d6d995 100644 --- a/d2bs/kolbot/libs/scripts/Icehawk.js +++ b/d2bs/kolbot/libs/scripts/Icehawk.js @@ -1,13 +1,12 @@ /** * @filename Icehawk.js -* @author kolton +* @author kolton, theBGuy * @desc kill Icehawk Riftwing * */ const Icehawk = new Runnable( function Icehawk () { - Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar); Precast.doPrecast(true); @@ -19,8 +18,8 @@ const Icehawk = new Runnable( return true; }, - sdk.areas.KurastBazaar, { + startArea: sdk.areas.KurastBazaar, bossid: getLocaleString(sdk.locale.monsters.IcehawkRiftwing), } ); diff --git a/d2bs/kolbot/libs/scripts/Idle.js b/d2bs/kolbot/libs/scripts/Idle.js index f3496e971..463d7c60d 100644 --- a/d2bs/kolbot/libs/scripts/Idle.js +++ b/d2bs/kolbot/libs/scripts/Idle.js @@ -57,5 +57,8 @@ const Idle = new Runnable( removeEventListener("gameevent", gameEvent); } }, - sdk.areas.RogueEncampment + { + startArea: sdk.areas.RogueEncampment, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/Izual.js b/d2bs/kolbot/libs/scripts/Izual.js index ad275fb24..94a642132 100644 --- a/d2bs/kolbot/libs/scripts/Izual.js +++ b/d2bs/kolbot/libs/scripts/Izual.js @@ -7,7 +7,6 @@ const Izual = new Runnable( function Izual () { - Town.doChores(); Pather.useWaypoint(sdk.areas.CityoftheDamned); Precast.doPrecast(true); @@ -20,8 +19,8 @@ const Izual = new Runnable( return true; }, - sdk.areas.CityoftheDamned, { + startArea: sdk.areas.CityoftheDamned, bossid: sdk.monsters.Izual, } ); diff --git a/d2bs/kolbot/libs/scripts/KillDclone.js b/d2bs/kolbot/libs/scripts/KillDclone.js index d461da534..f8a80b2db 100644 --- a/d2bs/kolbot/libs/scripts/KillDclone.js +++ b/d2bs/kolbot/libs/scripts/KillDclone.js @@ -23,5 +23,8 @@ const KillDclone = new Runnable( return true; }, - sdk.areas.ArcaneSanctuary + { + startArea: sdk.areas.ArcaneSanctuary, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/KurastTemples.js b/d2bs/kolbot/libs/scripts/KurastTemples.js index 8745f728f..3be1a0227 100644 --- a/d2bs/kolbot/libs/scripts/KurastTemples.js +++ b/d2bs/kolbot/libs/scripts/KurastTemples.js @@ -7,7 +7,6 @@ const KurastTemples = new Runnable( function KurastTemples () { - Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar); [ @@ -42,5 +41,7 @@ const KurastTemples = new Runnable( return true; }, - sdk.areas.KurastBazaar + { + startArea: sdk.areas.KurastBazaar + } ); diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index 068e4e492..b88f89a7f 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -299,5 +299,8 @@ const MFHelper = new Runnable( } return true; + }, + { + preAction: null } ); diff --git a/d2bs/kolbot/libs/scripts/Mausoleum.js b/d2bs/kolbot/libs/scripts/Mausoleum.js index 9c2ff187d..c56703a7f 100644 --- a/d2bs/kolbot/libs/scripts/Mausoleum.js +++ b/d2bs/kolbot/libs/scripts/Mausoleum.js @@ -7,7 +7,6 @@ const Mausoleum = new Runnable( function Mausoleum () { - Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains); Precast.doPrecast(true); @@ -46,5 +45,7 @@ const Mausoleum = new Runnable( return true; }, - sdk.areas.ColdPlains + { + startArea: sdk.areas.ColdPlains + } ); diff --git a/d2bs/kolbot/libs/scripts/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js index 34ab22b47..8f4d22ca1 100644 --- a/d2bs/kolbot/libs/scripts/Mephisto.js +++ b/d2bs/kolbot/libs/scripts/Mephisto.js @@ -108,7 +108,6 @@ const Mephisto = new Runnable( return true; }; - Town.doChores(); Pather.useWaypoint(sdk.areas.DuranceofHateLvl2); Precast.doPrecast(true); @@ -161,8 +160,8 @@ const Mephisto = new Runnable( return true; }, - sdk.areas.DuranceofHateLvl2, { + startArea: sdk.areas.DuranceofHateLvl2, bossid: sdk.monsters.Mephisto, } ); diff --git a/d2bs/kolbot/libs/scripts/Nihlathak.js b/d2bs/kolbot/libs/scripts/Nihlathak.js index 59aec6cc6..c0a3e3a61 100644 --- a/d2bs/kolbot/libs/scripts/Nihlathak.js +++ b/d2bs/kolbot/libs/scripts/Nihlathak.js @@ -8,7 +8,6 @@ const Nihlathak = new Runnable( function Nihlathak () { Town.goToTown(5); - Town.doChores(); !Pather.initialized && Pather.useWaypoint(null, true); @@ -38,8 +37,8 @@ const Nihlathak = new Runnable( return true; }, - sdk.areas.Harrogath, { + startArea: sdk.areas.Harrogath, bossid: sdk.monsters.Nihlathak, } ); diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index bd784ef4b..0ced53cc8 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -590,5 +590,7 @@ const OrgTorch = new Runnable( return true; }, - sdk.areas.Harrogath + { + startArea: sdk.areas.Harrogath + } ); diff --git a/d2bs/kolbot/libs/scripts/OuterSteppes.js b/d2bs/kolbot/libs/scripts/OuterSteppes.js index 225e4f43a..e29d55541 100644 --- a/d2bs/kolbot/libs/scripts/OuterSteppes.js +++ b/d2bs/kolbot/libs/scripts/OuterSteppes.js @@ -8,7 +8,6 @@ const OuterSteppes = new Runnable( function OuterSteppes() { if (!Town.goToTown(4)) throw new Error("Failed to go to act 4"); - Town.doChores(); // force random precast because currently bugs if we precast as soon as we go from inTown to out of town Precast.doRandomPrecast(true); if (!Pather.journeyTo(sdk.areas.OuterSteppes)) throw new Error("Failed to move to Outer Steppes"); @@ -17,5 +16,7 @@ const OuterSteppes = new Runnable( return true; }, - sdk.areas.PandemoniumFortress + { + startArea: sdk.areas.PandemoniumFortress + } ); diff --git a/d2bs/kolbot/libs/scripts/Pindleskin.js b/d2bs/kolbot/libs/scripts/Pindleskin.js index 8550ff181..147913f64 100644 --- a/d2bs/kolbot/libs/scripts/Pindleskin.js +++ b/d2bs/kolbot/libs/scripts/Pindleskin.js @@ -8,7 +8,6 @@ const Pindleskin = new Runnable( function Pindleskin () { Town.goToTown((Config.Pindleskin.UseWaypoint ? undefined : 5)); - Town.doChores(); if (!Attack.haveKilled(getLocaleString(sdk.locale.monsters.Pindleskin))) { if (Config.Pindleskin.UseWaypoint) { @@ -53,5 +52,7 @@ const Pindleskin = new Runnable( return true; }, - sdk.areas.Harrogath + { + startArea: sdk.areas.Harrogath + } ); diff --git a/d2bs/kolbot/libs/scripts/Pit.js b/d2bs/kolbot/libs/scripts/Pit.js index 8079db5ea..1140b8219 100644 --- a/d2bs/kolbot/libs/scripts/Pit.js +++ b/d2bs/kolbot/libs/scripts/Pit.js @@ -7,7 +7,6 @@ const Pit = new Runnable( function Pit () { - Town.doChores(); Pather.useWaypoint(sdk.areas.BlackMarsh); Precast.doPrecast(true); @@ -23,5 +22,7 @@ const Pit = new Runnable( return true; }, - sdk.areas.BlackMarsh + { + startArea: sdk.areas.BlackMarsh + } ); diff --git a/d2bs/kolbot/libs/scripts/Radament.js b/d2bs/kolbot/libs/scripts/Radament.js index 388ff58e9..987330b86 100644 --- a/d2bs/kolbot/libs/scripts/Radament.js +++ b/d2bs/kolbot/libs/scripts/Radament.js @@ -7,7 +7,6 @@ const Radament = new Runnable( function Radament () { - Town.doChores(); Pather.useWaypoint(sdk.areas.A2SewersLvl2); Precast.doPrecast(true); @@ -22,8 +21,8 @@ const Radament = new Runnable( return true; }, - sdk.areas.A2SewersLvl2, { + startArea: sdk.areas.A2SewersLvl2, bossid: sdk.monsters.Radament, } ); diff --git a/d2bs/kolbot/libs/scripts/Rakanishu.js b/d2bs/kolbot/libs/scripts/Rakanishu.js index 3aa437e97..36203d964 100644 --- a/d2bs/kolbot/libs/scripts/Rakanishu.js +++ b/d2bs/kolbot/libs/scripts/Rakanishu.js @@ -7,7 +7,6 @@ const Rakanishu = new Runnable( function Rakanishu () { - Town.doChores(); Pather.useWaypoint(sdk.areas.StonyField); Precast.doPrecast(true); @@ -29,5 +28,7 @@ const Rakanishu = new Runnable( return true; }, - sdk.areas.StonyField + { + startArea: sdk.areas.StonyField + } ); diff --git a/d2bs/kolbot/libs/scripts/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js index cf371e53c..b253fde10 100644 --- a/d2bs/kolbot/libs/scripts/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -6,88 +6,86 @@ * */ -let Overrides = require("../modules/Override"); -new Overrides.Override(Town, Town.goToTown, function(orignal, act, wpmenu) { - try { - orignal(act, wpmenu); +function Rushee () { + const Overrides = require("../modules/Override"); - return true; - } catch (e) { - print(e); - - return Pather.useWaypoint(sdk.areas.townOf(me.area)); - } -}).apply(); + new Overrides.Override(Town, Town.goToTown, function (orignal, act, wpmenu) { + try { + orignal(act, wpmenu); + + return true; + } catch (e) { + console.log(e); + + return Pather.useWaypoint(sdk.areas.townOf(me.area)); + } + }).apply(); -new Overrides.Override(Pather, Pather.getWP, function(orignal, area, clearPath) { - if (area !== me.area) return false; + new Overrides.Override(Pather, Pather.getWP, function (orignal, area, clearPath) { + if (area !== me.area) return false; - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - if (preset) { - let x = (preset.roomx * 5 + preset.x); - let y = (preset.roomy * 5 + preset.y); - if (!me.inTown && [x, y].distance > 15) return false; + if (preset) { + let x = (preset.roomx * 5 + preset.x); + let y = (preset.roomy * 5 + preset.y); + if (!me.inTown && [x, y].distance > 15) return false; - Skill.haveTK - ? this.moveNearUnit(preset, 20, { clearSettings: { clearPath: clearPath } }) - : this.moveToUnit(preset, 0, 0, clearPath); + Skill.haveTK + ? this.moveNearUnit(preset, 20, { clearSettings: { clearPath: clearPath } }) + : this.moveToUnit(preset, 0, 0, clearPath); - let wp = Game.getObject("waypoint"); + let wp = Game.getObject("waypoint"); - if (wp) { - for (let j = 0; j < 10; j++) { - if (!getUIFlag(sdk.uiflags.Waypoint)) { - if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { - wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Packet.telekinesis(wp); - } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { - this.moveToUnit(wp) && Misc.click(0, 0, wp); + if (wp) { + for (let j = 0; j < 10; j++) { + if (!getUIFlag(sdk.uiflags.Waypoint)) { + if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { + wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + Packet.telekinesis(wp); + } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { + this.moveToUnit(wp) && Misc.click(0, 0, wp); + } } - } - if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { - delay(500); - me.cancelUIFlags(); + if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { + delay(500); + me.cancelUIFlags(); - return true; - } + return true; + } - // handle getUnit bug - if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { - Town.getDistance("waypoint") > 5 && Town.move("waypoint"); - Misc.click(0, 0, wp); - } + // handle getUnit bug + if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { + Town.getDistance("waypoint") > 5 && Town.move("waypoint"); + Misc.click(0, 0, wp); + } - delay(500); + delay(500); + } } } } - } - - return false; -}).apply(); - -function Rushee() { - let act, leader, target, done = false; - let actions = []; - - this.log = function (msg = "", sayMsg = false) { - print(msg); - sayMsg && say(msg); - }; - this.useScrollOfRes = function () { + return false; + }).apply(); + const { log } = require("../systems/autorush/AutoRush"); + const { + AutoRush, + // RushModes, + } = require("../systems/autorush/RushConfig"); + + const useScrollOfRes = function () { let scroll = me.scrollofresistance; if (scroll) { clickItem(sdk.clicktypes.click.item.Right, scroll); - print("Using scroll of resistance"); + console.log("Using scroll of resistance"); } }; - this.revive = function () { + const revive = function () { while (me.mode === sdk.player.mode.Death) { delay(40); } @@ -96,17 +94,17 @@ function Rushee() { me.revive(); while (!me.inTown) { - delay(40); + delay(3); } } }; // todo - map the chest to classid so we only need to pass in one value - this.getQuestItem = function (classid, chestid) { + const getQuestItem = function (classid, chestid) { let tick = getTickCount(); if (me.getItem(classid)) { - this.log("Already have: " + classid); + log("Already have: " + classid); return true; } @@ -115,7 +113,7 @@ function Rushee() { let chest = Game.getObject(chestid); if (!chest) { - this.log("Couldn't find: " + chestid); + log("Couldn't find: " + chestid); return false; } @@ -123,7 +121,7 @@ function Rushee() { if (Misc.openChest(chest)) { break; } - this.log("Failed to open chest: Attempt[" + (i + 1) + "]"); + log("Failed to open chest: Attempt[" + (i + 1) + "]"); let coord = CollMap.getRandCoordinate(chest.x, -4, 4, chest.y, -4, 4); coord && Pather.moveTo(coord.x, coord.y); } @@ -141,7 +139,7 @@ function Rushee() { return Pickit.pickItem(item) && delay(1000); }; - this.checkQuestMonster = function (classid) { + const checkQuestMonster = function (classid) { let monster = Game.getMonster(classid); if (monster) { @@ -155,9 +153,13 @@ function Rushee() { return false; }; - this.tyraelTalk = function () { + const tyraelTalk = function () { + if (me.inArea(sdk.areas.DurielsLair) && [22577, 15609].distance > 10) { + Pather.move({ x: 22577, y: 15609 }, { callback: function () { + return Game.getNPC(NPC.Tyrael); + } }); + } let npc = Game.getNPC(NPC.Tyrael); - if (!npc) return false; for (let i = 0; i < 3; i += 1) { @@ -176,30 +178,49 @@ function Rushee() { return Pather.usePortal(null) || Pather.usePortal(null, Config.Leader); }; - this.cubeStaff = function () { - let shaft = me.shaft; - let amulet = me.amulet; - - if (!shaft || !amulet) return false; - - Storage.Cube.MoveTo(amulet); - Storage.Cube.MoveTo(shaft); - Cubing.openCube(); - print("making staff"); - transmute(); - delay(750 + me.ping); - - let staff = me.completestaff; - - if (!staff) return false; - - Storage.Inventory.MoveTo(staff); - me.cancel(); - - return true; - }; + const cube = (function () { + const staff = { + ingreds: [sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.item.ViperAmulet], + outcome: sdk.quest.item.HoradricStaff, + }; + const flail = { + ingreds: [ + sdk.quest.item.KhalimsFlail, sdk.quest.item.KhalimsEye, + sdk.quest.item.KhalimsBrain, sdk.quest.item.KhalimsHeart + ], + outcome: sdk.quest.item.KhalimsWill, + }; + /** @param {{ ingreds: number[], outcome: number }} item */ + const make = function (item) { + if (me.getItem(item.outcome)) return true; + let ingreds = item.ingreds.map(id => me.getItem(id)); + if (!ingreds.every(i => i)) return false; + ingreds.forEach(i => Storage.Cube.MoveTo(i)); + Cubing.openCube(); + transmute(); + delay(750 + me.ping); + + let outcome = me.getItem(item.outcome); + if (!outcome) return false; + + Storage.Inventory.MoveTo(outcome); + me.cancel(); - this.placeStaff = function () { + return true; + }; + return { + Staff: function () { + log("Making staff", Config.LocalChat.Enabled); + return make(staff); + }, + Flail: function () { + log("Making flail", Config.LocalChat.Enabled); + return make(flail); + }, + }; + })(); + + const placeStaff = function () { let tick = getTickCount(); let orifice = Game.getObject(sdk.quest.chest.HoradricStaffHolder); if (!orifice) return false; @@ -230,7 +251,7 @@ function Rushee() { return true; }; - this.changeAct = function (act) { + const changeAct = function (act) { let preArea = me.area; if (me.mode === sdk.player.mode.Dead) { @@ -311,12 +332,18 @@ function Rushee() { if (me.area === preArea) { me.cancel(); Town.move("portalspot"); - this.log("Act change failed.", Config.LocalChat.Enabled); + log("Act change failed.", Config.LocalChat.Enabled); return false; } - this.log("Act change done.", Config.LocalChat.Enabled); + if (me.act === 2 && Game.getNPC(NPC.Jerhyn)) { + Town.npcInteract("Jerhyn"); + } else if (me.act === 3) { + Town.npcInteract("Hratli"); + } + + log("Act change done.", Config.LocalChat.Enabled); } catch (e) { return false; } @@ -324,7 +351,7 @@ function Rushee() { return true; }; - this.getQuestInfo = function (id) { + const getQuestInfo = function (id) { // note: end bosses double printed to match able to go to act flag let quests = [ ["cain", sdk.quest.id.TheSearchForCain], @@ -350,16 +377,23 @@ function Rushee() { ]; let quest = quests.find(element => element[1] === id); + console.debug("Quest: " + quest + " ID: " + id); return (!!quest ? quest[0] : ""); }; - this.nonQuesterNPCTalk = false; + let nonQuesterNPCTalk = false; + let act, target, done = false; + const commands = []; addEventListener("chatmsg", function (who, msg) { + if (!Config.Leader && msg.includes("questinfo")) { + Config.Leader = who; + console.debug("Assigned Leader: " + Config.Leader); + } if (who === Config.Leader) { - actions.push(msg); + commands.push(msg); } }); @@ -367,16 +401,22 @@ function Rushee() { Town.goToTown(me.highestAct); me.inTown && Town.move("portalspot"); + if (me.inArea(sdk.areas.RogueEncampment) + && !me.getQuest(sdk.quest.id.SpokeToWarriv, sdk.quest.states.Completed)) { + Town.npcInteract("Warriv"); + Town.move("portalspot"); + } + // if we can't find our leader after 5 minutes, I'm thinking they aren't showing up. Lets not wait around forever - leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.minutes(5), 1000); + const leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.minutes(5), 1000); if (!leader) throw new Error("Failed to find my rusher"); Config.Rushee.Quester - ? this.log("Leader found", Config.LocalChat.Enabled) - : console.log("Leader Found: " + Config.Leader); + ? log("(Quester) Leader Found: " + Config.Leader, Config.LocalChat.Enabled) + : console.log("(NonQuester) Leader Found: " + Config.Leader); // lets figure out if we either are the bumper or have a bumper so we know if we need to stop at the end of the rush - let bumperLevelReq = [20, 40, 60][me.diff]; + const bumperLevelReq = [20, 40, 60][me.diff]; // ensure we are the right level to go to next difficulty if not on classic let nextGame = (Config.Rushee.Bumper && (me.classic || me.charlvl >= bumperLevelReq)); if (!nextGame) { @@ -389,677 +429,627 @@ function Rushee() { } console.debug("Is this our last run? " + (nextGame ? "No" : "Yes")); - while (true) { - // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces - try { - if (actions.length > 0) { - switch (actions[0]) { - case "all in": - switch (leader.area) { - case sdk.areas.A2SewersLvl3: - // Pick Book of Skill, use Book of Skill - Town.move("portalspot"); - Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); - delay(500); - - while (true) { - target = Game.getItem(sdk.quest.item.BookofSkill); - - if (!target) { - break; - } - - Pickit.pickItem(target); - delay(250); - target = me.getItem(sdk.quest.item.BookofSkill); - - if (target) { - print("Using book of skill"); - clickItem(sdk.clicktypes.click.item.Right, target); - - break; - } - } - - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - actions.shift(); - - break; - } - - actions.shift(); + const actions = new Map([ + [AutoRush.allIn, function () { + switch (leader.area) { + case sdk.areas.A2SewersLvl3: + // Pick Book of Skill, use Book of Skill + Town.move("portalspot"); + Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); + delay(500); - break; - case "questinfo": - if (!Config.Rushee.Quester) { - actions.shift(); + while (true) { + target = Game.getItem(sdk.quest.item.BookofSkill); + if (!target) { break; } - say("highestquest " + this.getQuestInfo(me.highestQuestDone)); - actions.shift(); + Pickit.pickItem(target); + delay(250); + target = me.getItem(sdk.quest.item.BookofSkill); - break; - case "wpinfo": - if (!Config.Rushee.Quester) { - actions.shift(); + if (target) { + console.log("Using book of skill"); + clickItem(sdk.clicktypes.click.item.Right, target); break; } + } - // go activate wp if we don't know our wps yet - !getWaypoint(1) && Pather.getWP(me.area); - - let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { - if (area === sdk.areas.HallsofPain) return false; - if (me.classic && area >= sdk.areas.Harrogath) return false; - if (getWaypoint(Pather.wpAreas.indexOf(area))) return false; - return true; - }); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - say("mywps " + JSON.stringify(myWps)); - actions.shift(); + return true; + default: + if (!Config.Rushee.Bumper) return true; - break; - case "wp": - if (!me.inTown && !Town.goToTown()) { - this.log("I can't get to town :(", Config.LocalChat.Enabled); + while (!leader.area) { + delay(500); + } - break; - } + act = Misc.getPlayerAct(leader); - act = Misc.getPlayerAct(leader); + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); + switch (leader.area) { + case sdk.areas.ArreatSummit: + if (!Pather.usePortal(sdk.areas.ArreatSummit, Config.Leader)) { + return false; } - // make sure we talk to cain to access durance - leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); - - // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas - (!Config.Rushee.Quester && !this.nonQuesterNPCTalk) && (this.nonQuesterNPCTalk = true); - - Town.getDistance("portalspot") > 10 && Town.move("portalspot"); - if (Pather.usePortal(null, Config.Leader) && Pather.getWP(me.area) && Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) && Town.move("portalspot")) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); - } else { - // check for bugged portal - let p = Game.getObject("portal"); - let preArea = me.area; - if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) - && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); - } else { - this.log("Failed to get wp", Config.LocalChat.Enabled); - !me.inTown && Town.goToTown(); - } + // Wait until portal is gone + while (Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { + delay(500); } - actions.shift(); - - break; - case "1": - while (!leader.area) { + // Wait until portal is up again + while (!Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { delay(500); } - act = Misc.getPlayerAct(leader); - - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); + if (!Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { + return false; } - // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester - if (this.nonQuesterNPCTalk) { - console.debug("Leader Area: " + getAreaName(leader.area)); - - switch (leader.area) { - case sdk.areas.ClawViperTempleLvl2: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Drognan")) { - actions.shift(); - console.debug("drognan done"); - } - - break; - case sdk.areas.ArcaneSanctuary: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Atma")) { - actions.shift(); - console.debug("atma done"); - } + return true; + case sdk.areas.WorldstoneChamber: + if (!Pather.usePortal(sdk.areas.WorldstoneChamber, Config.Leader)) { + return false; + } - break; - case sdk.areas.Travincal: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) || Misc.checkQuest(sdk.quest.id.TheGuardian, 8), Time.seconds(20), 1000)); - if (Town.npcInteract("Cain")) { - actions.shift(); - console.debug("cain done"); - } + return true; + } + } + return true; + }], + [AutoRush.playersIn, function () { + while (!leader.area) { + delay(500); + } - break; - case sdk.areas.ArreatSummit: - Misc.poll(() => (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Malah")) { - actions.shift(); - console.debug("malah done"); - } + act = Misc.getPlayerAct(leader); - break; - } + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } - me.inTown && Town.move("portalspot"); + // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester + if (nonQuesterNPCTalk) { + console.debug("Leader Area: " + getAreaName(leader.area)); + + switch (leader.area) { + case sdk.areas.ClawViperTempleLvl2: + Misc.poll(function () { + return !!(Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) + || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete)); + }, Time.seconds(20), 1000); + if (Town.npcInteract("Drognan")) { + console.debug("drognan done"); + return true; } - if (!Config.Rushee.Quester) { - actions.shift(); + return false; + case sdk.areas.ArcaneSanctuary: + Misc.poll(function () { + return !!(Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) + || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete)); + }, Time.seconds(20), 1000); + if (Town.npcInteract("Atma")) { + console.debug("atma done"); + return true; + } + return false; + case sdk.areas.Travincal: + Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) || Misc.checkQuest(sdk.quest.id.TheGuardian, 8), Time.seconds(20), 1000)); + if (Town.npcInteract("Cain")) { + console.debug("cain done"); + return true; + } - break; + return false; + case sdk.areas.ArreatSummit: + Misc.poll(() => (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); + if (Town.npcInteract("Malah")) { + console.debug("malah done"); + return true; } - switch (leader.area) { - case sdk.areas.StonyField: - if (!Pather.usePortal(sdk.areas.StonyField, Config.Leader)) { - this.log("Failed to us portal to stony field", Config.LocalChat.Enabled); - break; - } + return false; + } - let stones = [ - Game.getObject(sdk.quest.chest.StoneAlpha), - Game.getObject(sdk.quest.chest.StoneBeta), - Game.getObject(sdk.quest.chest.StoneGamma), - Game.getObject(sdk.quest.chest.StoneDelta), - Game.getObject(sdk.quest.chest.StoneLambda) - ]; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (Misc.openChest(stone)) { - stones.splice(i, 1); - i--; - } - delay(10); - } - } + me.inTown && Town.move("portalspot"); + } - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram)) { - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - - break; - } - } - Town.move("portalspot"); - actions.shift(); + if (!Config.Rushee.Quester) { + return true; + } - break; - case sdk.areas.DarkWood: - if (!Pather.usePortal(sdk.areas.DarkWood, Config.Leader)) { - this.log("Failed to use portal to dark wood", Config.LocalChat.Enabled); - break; + switch (leader.area) { + case sdk.areas.StonyField: + if (!Pather.usePortal(sdk.areas.StonyField, Config.Leader)) { + log("Failed to use portal to stony field", Config.LocalChat.Enabled); + return false; + } + + let stones = [ + Game.getObject(sdk.quest.chest.StoneAlpha), + Game.getObject(sdk.quest.chest.StoneBeta), + Game.getObject(sdk.quest.chest.StoneGamma), + Game.getObject(sdk.quest.chest.StoneDelta), + Game.getObject(sdk.quest.chest.StoneLambda) + ]; + + while (stones.some((stone) => !stone.mode)) { + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; + + if (Misc.openChest(stone)) { + stones.splice(i, 1); + i--; } + delay(10); + } + } - this.getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); - delay(500); + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.getPortal(sdk.areas.Tristram)) { Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - if (Town.npcInteract("Akara")) { - this.log("Akara done", Config.LocalChat.Enabled); - } - - Town.move("portalspot"); - actions.shift(); - break; - case sdk.areas.Tristram: - if (!Pather.usePortal(sdk.areas.Tristram, Config.Leader)) { - this.log("Failed to use portal to Tristram", Config.LocalChat.Enabled); - break; - } - - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - Town.npcInteract("Akara") && this.log("Akara done", Config.LocalChat.Enabled); - } - } - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.CatacombsLvl4: - if (!Pather.usePortal(sdk.areas.CatacombsLvl4, Config.Leader)) { - this.log("Failed to use portal to catacombs", Config.LocalChat.Enabled); - break; - } - - target = Pather.getPortal(null, Config.Leader); - target && Pather.walkTo(target.x, target.y); - - actions.shift(); - - break; - case sdk.areas.A2SewersLvl3: - Town.move("portalspot"); - - if (Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader)) { - actions.shift(); - } - - break; - case sdk.areas.HallsoftheDeadLvl3: - Pather.usePortal(sdk.areas.HallsoftheDeadLvl3, Config.Leader); - this.getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + } + } + Town.move("portalspot"); - actions.shift(); + return true; + case sdk.areas.DarkWood: + if (!Pather.usePortal(sdk.areas.DarkWood, Config.Leader)) { + log("Failed to use portal to dark wood", Config.LocalChat.Enabled); + return false; + } - break; - case sdk.areas.ClawViperTempleLvl2: - Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); - this.getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - - if (Town.npcInteract("Drognan")) { - actions.shift(); - say("drognan done", Config.LocalChat.Enabled); - } + getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); + delay(500); + Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + + if (Town.npcInteract("Akara")) { + log("Akara done", Config.LocalChat.Enabled); + } - Town.move("portalspot"); + Town.move("portalspot"); - break; - case sdk.areas.MaggotLairLvl3: - Pather.usePortal(sdk.areas.MaggotLairLvl3, Config.Leader); - this.getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); - delay(500); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - this.cubeStaff(); + return true; + case sdk.areas.Tristram: + if (!Pather.usePortal(sdk.areas.Tristram, Config.Leader)) { + log("Failed to use portal to Tristram", Config.LocalChat.Enabled); + break; + } - actions.shift(); + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - break; - case sdk.areas.ArcaneSanctuary: - if (!Pather.usePortal(sdk.areas.ArcaneSanctuary, Config.Leader)) { - break; - } - - actions.shift(); + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { + Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + Town.npcInteract("Akara") && log("Akara done", Config.LocalChat.Enabled); + } + } + Town.move("portalspot"); + commands.shift(); - break; - case sdk.areas.TalRashasTomb1: - case sdk.areas.TalRashasTomb2: - case sdk.areas.TalRashasTomb3: - case sdk.areas.TalRashasTomb4: - case sdk.areas.TalRashasTomb5: - case sdk.areas.TalRashasTomb6: - case sdk.areas.TalRashasTomb7: - Pather.usePortal(null, Config.Leader); - this.placeStaff(); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - actions.shift(); + break; + case sdk.areas.CatacombsLvl4: + if (!Pather.usePortal(sdk.areas.CatacombsLvl4, Config.Leader)) { + log("Failed to use portal to catacombs", Config.LocalChat.Enabled); + return false; + } - break; - case sdk.areas.DurielsLair: - Pather.usePortal(sdk.areas.DurielsLair, Config.Leader); - this.tyraelTalk(); + target = Pather.getPortal(null, Config.Leader); + target && Pather.walkTo(target.x, target.y); - actions.shift(); + return true; + case sdk.areas.A2SewersLvl3: + Town.move("portalspot"); - break; - case sdk.areas.Travincal: - if (!Pather.usePortal(sdk.areas.Travincal, Config.Leader)) { - me.cancel(); + return Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); + case sdk.areas.HallsoftheDeadLvl3: + Pather.usePortal(sdk.areas.HallsoftheDeadLvl3, Config.Leader); + getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); + return Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + case sdk.areas.ClawViperTempleLvl2: + Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); + getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + + if (Town.npcInteract("Drognan")) { + say("drognan done", Config.LocalChat.Enabled); + Town.move("portalspot"); + return true; + } - break; - } + return false; + case sdk.areas.MaggotLairLvl3: + Pather.usePortal(sdk.areas.MaggotLairLvl3, Config.Leader); + getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); + delay(500); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + return cube.Staff(); + case sdk.areas.ArcaneSanctuary: + return Pather.usePortal(sdk.areas.ArcaneSanctuary, Config.Leader); + case sdk.areas.TalRashasTomb1: + case sdk.areas.TalRashasTomb2: + case sdk.areas.TalRashasTomb3: + case sdk.areas.TalRashasTomb4: + case sdk.areas.TalRashasTomb5: + case sdk.areas.TalRashasTomb6: + case sdk.areas.TalRashasTomb7: + Pather.usePortal(null, Config.Leader); + placeStaff(); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + return true; + case sdk.areas.DurielsLair: + Pather.usePortal(sdk.areas.DurielsLair, Config.Leader); + tyraelTalk(); + + return true; + case sdk.areas.Travincal: + if (!Pather.usePortal(sdk.areas.Travincal, Config.Leader)) { + me.cancel(); - actions.shift(); + return false; + } - break; - case sdk.areas.RuinedTemple: - if (!Pather.usePortal(sdk.areas.RuinedTemple, Config.Leader)) { - me.cancel(); + return true; + case sdk.areas.RuinedTemple: + if (!Pather.usePortal(sdk.areas.RuinedTemple, Config.Leader)) { + me.cancel(); - break; - } + return false; + } - this.getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); - Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); - Town.npcInteract("Alkor"); - Town.move("portalspot"); - actions.shift(); + getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); + Town.npcInteract("Alkor"); + Town.move("portalspot"); + return true; + case sdk.areas.DuranceofHateLvl3: + if (!Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader)) { + me.cancel(); + return false; + } - break; - case sdk.areas.DuranceofHateLvl3: - if (!Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader)) { - me.cancel(); + return true; + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + return Pather.usePortal(null, Config.Leader); + case sdk.areas.ChaosSanctuary: + Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); + Pather.moveTo(7762, 5268); + Packet.flash(me.gid); + delay(500); + Pather.walkTo(7763, 5267, 2); - break; - } + while (!Game.getMonster(sdk.monsters.Diablo)) { + delay(500); + } - actions.shift(); + Pather.moveTo(7763, 5267); + return true; + case sdk.areas.BloodyFoothills: + return Pather.usePortal(sdk.areas.BloodyFoothills, Config.Leader); + case sdk.areas.FrozenRiver: + Town.npcInteract("Malah"); - break; - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - if (Pather.usePortal(null, Config.Leader)) { - actions.shift(); - } + Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader); + delay(500); - break; - case sdk.areas.ChaosSanctuary: - Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); - Pather.moveTo(7762, 5268); - Packet.flash(me.gid); - delay(500); - Pather.walkTo(7763, 5267, 2); + target = Game.getObject(sdk.objects.FrozenAnya); - while (!Game.getMonster(sdk.monsters.Diablo)) { - delay(500); - } + if (target) { + Pather.moveToUnit(target); + Misc.poll(() => { + Packet.entityInteract(target); + delay(100); + return !Game.getObject(sdk.objects.FrozenAnya); + }, 1000, 200); + delay(1000); + me.cancel(); + } - Pather.moveTo(7763, 5267); - actions.shift(); + return true; + default: // unsupported area + return true; + } + return true; + }], + [AutoRush.playersOut, function () { + if (!Config.Rushee.Quester) { + // Non-questers can piggyback off quester out messages + switch (leader.area) { + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete) && Town.npcInteract("Tyrael"); - break; - case sdk.areas.BloodyFoothills: - Pather.usePortal(sdk.areas.BloodyFoothills, Config.Leader); - actions.shift(); + break; + case sdk.areas.BloodyFoothills: + me.act === 5 && Town.npcInteract("Larzuk"); - break; - case sdk.areas.FrozenRiver: + break; + case sdk.areas.FrozenRiver: + if (me.act === 5) { Town.npcInteract("Malah"); - - Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader); - delay(500); - - target = Game.getObject(sdk.objects.FrozenAnya); - - if (target) { - Pather.moveToUnit(target); - Misc.poll(() => { - Packet.entityInteract(target); - delay(100); - return !Game.getObject(sdk.objects.FrozenAnya); - }, 1000, 200); - delay(1000); - me.cancel(); - } - - actions.shift(); - - break; - default: // unsupported area - actions.shift(); - - break; + useScrollOfRes(); } break; - case "2": // Go back to town and check quest - if (!Config.Rushee.Quester) { - // Non-questers can piggyback off quester out messages - switch (leader.area) { - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete) && Town.npcInteract("Tyrael"); - - break; - case sdk.areas.BloodyFoothills: - me.act === 5 && Town.npcInteract("Larzuk"); - - break; - case sdk.areas.FrozenRiver: - if (me.act === 5) { - Town.npcInteract("Malah"); - this.useScrollOfRes(); - } - - break; - } - - actions.shift(); - - break; - } - - this.revive(); - - switch (me.area) { - case sdk.areas.CatacombsLvl4: - // Go to town if not there, break if procedure fails - if (!me.inTown && !Pather.usePortal(sdk.areas.RogueEncampment)) { - break; - } - - if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { - D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); - quit(); - } - - actions.shift(); - - break; - case sdk.areas.A2SewersLvl3: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - break; - } - - actions.shift(); - - break; - case sdk.areas.ArcaneSanctuary: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - break; - } - - Town.npcInteract("Atma"); - - if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); - quit(); - } - - Town.move("portalspot"); - actions.shift(); - - break; - case sdk.areas.Travincal: - if (!me.inTown && !Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { - break; - } + } - Town.npcInteract("Cain"); + commands.shift(); - if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); - quit(); - } + return true; + } - Town.move("portalspot"); - actions.shift(); + revive(); - break; - case sdk.areas.DuranceofHateLvl3: - if (!Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { - break; - } + switch (me.area) { + case sdk.areas.CatacombsLvl4: + // Go to town if not there, break if procedure fails + if (!me.inTown && !Pather.usePortal(sdk.areas.RogueEncampment)) { + return false; + } - actions.shift(); + if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { + D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); + quit(); + } - break; - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - break; - } + return true; + case sdk.areas.A2SewersLvl3: + if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { + return false; + } - if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { - Town.npcInteract("Tyrael"); - Town.move("portalspot"); - } + return true; + case sdk.areas.ArcaneSanctuary: + if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { + return false; + } - actions.shift(); + Town.npcInteract("Atma"); - break; - case sdk.areas.ChaosSanctuary: - me.classic && D2Bot.restart(); + if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); + quit(); + } - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - break; - } + Town.move("portalspot"); + return true; + case sdk.areas.Travincal: + if (!me.inTown && !Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { + return false; + } - actions.shift(); + Town.npcInteract("Cain"); - break; - case sdk.areas.BloodyFoothills: - if (!me.inTown && !Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - break; - } + if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); + quit(); + } - Town.npcInteract("Larzuk"); - Town.move("portalspot"); - actions.shift(); + Town.move("portalspot"); + return true; + case sdk.areas.DuranceofHateLvl3: + return Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { + return false; + } - break; - case sdk.areas.FrozenRiver: - if (!me.inTown && !Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader)) { - break; - } + if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { + Town.npcInteract("Tyrael"); + Town.move("portalspot"); + } - Town.npcInteract("Malah"); - this.useScrollOfRes(); - Town.move("portalspot"); + return true; + case sdk.areas.ChaosSanctuary: + me.classic && D2Bot.restart(); - actions.shift(); + if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { + return false; + } - break; - default: - Town.move("portalspot"); - actions.shift(); + return true; + case sdk.areas.BloodyFoothills: + if (!me.inTown && !Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { + return false; + } - break; - } + Town.npcInteract("Larzuk"); + Town.move("portalspot"); + return true; + case sdk.areas.FrozenRiver: + if (!me.inTown && !Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader)) { + return false; + } - break; - case "3": // Bumper - if (!Config.Rushee.Bumper) { - actions.shift(); + Town.npcInteract("Malah"); + useScrollOfRes(); + Town.move("portalspot"); - break; - } + return true; + default: + Town.move("portalspot"); + return true; + } + }], + ["flail", function () { + if (!Config.Rushee.Quester) { + return true; + } + + }], + ["questinfo", function () { + if (!Config.Rushee.Quester) { + return true; + } + say("highestquest " + getQuestInfo(me.highestQuestDone)); + return true; + }], + ["wpinfo", function () { + if (!Config.Rushee.Quester) { + return true; + } - while (!leader.area) { - delay(500); - } + // go activate wp if we don't know our wps yet + !me.haveWaypoint(1) && Pather.getWP(me.area); - act = Misc.getPlayerAct(leader); + let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { + if (area === sdk.areas.HallsofPain) return false; + if (me.classic && area >= sdk.areas.Harrogath) return false; + if (me.haveWaypoint(area)) return false; + return true; + }); - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } + say("mywps " + JSON.stringify(myWps)); + return true; + }], + ["wp", function () { + if (!me.inTown && !Town.goToTown()) { + log("I can't get to town :(", Config.LocalChat.Enabled); + return false; + } - switch (leader.area) { - case sdk.areas.ArreatSummit: - if (!Pather.usePortal(sdk.areas.ArreatSummit, Config.Leader)) { - break; - } + act = Misc.getPlayerAct(leader); - // Wait until portal is gone - while (Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { - delay(500); - } + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } - // Wait until portal is up again - while (!Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { - delay(500); - } + // make sure we talk to cain to access durance + leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); + + // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas + (!Config.Rushee.Quester && !nonQuesterNPCTalk) && (nonQuesterNPCTalk = true); + + Town.getDistance("portalspot") > 10 && Town.move("portalspot"); + if (Pather.usePortal(null, Config.Leader) && Pather.getWP(me.area) && Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) && Town.move("portalspot")) { + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + } else { + // check for bugged portal + let p = Game.getObject("portal"); + let preArea = me.area; + if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) + && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + } else { + log("Failed to get wp", Config.LocalChat.Enabled); + !me.inTown && Town.goToTown(); + } + } - if (!Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - break; - } + return true; + }], + ["a2", function () { + if (!changeAct(2)) { + return false; + } - actions.shift(); + Town.move("portalspot"); + return true; + }], + ["a3", function () { + if (!changeAct(3)) { + return false; + } - break; - case sdk.areas.WorldstoneChamber: - if (!Pather.usePortal(sdk.areas.WorldstoneChamber, Config.Leader)) { - break; - } + Town.move("portalspot"); + return true; + }], + ["a4", function () { + if (!changeAct(4)) { + return false; + } - actions.shift(); + Town.move("portalspot"); + return true; + }], + ["a5", function () { + if (!changeAct(5)) { + return false; + } - break; - } + Town.move("portalspot"); + return true; + }], + ["quit", function () { + done = true; + return true; + }], + ["exit", function () { + if (!nextGame) { + D2Bot.printToConsole("Rush Complete"); + D2Bot.stop(); + } else { + D2Bot.restart(); + } + return true; + }], + ["leader", function () { + console.log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); + Config.LocalChat.Enabled && say(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); + return true; + }], + [me.name + " quest", function () { + say("I am quester."); + Config.Rushee.Quester = true; - break; - case "quit": - done = true; + return true; + }] + ]); + const curr = { + cmd: "", + retry: 0 + }; - break; - case "exit": - case "bye ~": - if (!nextGame) { - D2Bot.printToConsole("Rush Complete"); - D2Bot.stop(); + while (true) { + // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces + try { + if (commands.length > 0) { + let command = commands[0].toLowerCase(); + + if (actions.has(command)) { + curr.cmd = command; + if (actions.get(command)()) { + commands.shift(); + curr.retry = 0; } else { - D2Bot.restart(); - } - - break; - case "a2": - case "a3": - case "a4": - case "a5": - act = actions[0].toString()[1]; - !!act && (act = (parseInt(act, 10) || me.act + 1)); - - if (!this.changeAct(act)) { - break; + console.debug("Command retry: " + command); + curr.retry++; + if (curr.retry > 3) { + log("Failed to do " + command, Config.LocalChat.Enabled); + commands.shift(); + } } - - Town.move("portalspot"); - actions.shift(); - - break; - case me.name + " quest": - say("I am quester."); - Config.Rushee.Quester = true; - - actions.shift(); - - break; - case "leader": - console.log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - Config.LocalChat.Enabled && say(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - actions.shift(); - - break; - default: // Invalid command - actions.shift(); - - break; + } else { + commands.shift(); } } } catch (e) { + console.error(e); + commands.shift(); if (me.mode === sdk.player.mode.Dead) { me.revive(); while (!me.inTown) { - delay(500); + delay(3); } } } diff --git a/d2bs/kolbot/libs/scripts/Rusher.js b/d2bs/kolbot/libs/scripts/Rusher.js index 20ba4817b..66f79ab15 100644 --- a/d2bs/kolbot/libs/scripts/Rusher.js +++ b/d2bs/kolbot/libs/scripts/Rusher.js @@ -18,12 +18,18 @@ function Rusher () { load("threads/rushthread.js"); delay(500); - let i, command, master, commandSplit0; - let commands = []; - let sequence = [ - "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", - "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" - ]; + const { + AutoRush, + RushModes, + RushConfig, + } = require("../systems/autorush/RushConfig"); + + const commands = []; + let command = ""; + let master = ""; + let commandSplit0; + let done = false; + const RushThread = { /** @type {Script} */ _thread: null, @@ -118,6 +124,7 @@ function Rusher () { break; case "quit": if (nick === master) { + done = true; say("bye ~"); scriptBroadcast("quit"); } else { @@ -147,62 +154,46 @@ function Rusher () { addEventListener("chatmsg", chatEvent); - while (Misc.getPartyCount() < Math.min(8, Config.Rusher.WaitPlayerCount)) { + while (Misc.getPartyCount() < Math.min(8, RushConfig[me.profile].config.WaitPlayerCount)) { me.overhead("Waiting for players to join"); delay(500); } // Skip to a higher act if all party members are there - switch (getPartyAct()) { - case 2: - say("Party is in act 2, starting from act 2"); - RushThread.send("skiptoact 2"); - - break; - case 3: - say("Party is in act 3, starting from act 3"); - RushThread.send("skiptoact 3"); - - break; - case 4: - say("Party is in act 4, starting from act 4"); - RushThread.send("skiptoact 4"); - - break; - case 5: - say("Party is in act 5, starting from act 5"); - RushThread.send("skiptoact 5"); - - break; + let partyAct = getPartyAct(); + if (partyAct > 1) { + say("Party is in act " + partyAct + ", skipping to act " + partyAct); + RushThread.send("skiptoact " + partyAct); } // get info from master - let tick = getTickCount(); - let askAgain = 1; - say("questinfo"); - while (!command) { - // wait up to 3 minutes - if (getTickCount() - tick > Time.minutes(3)) { - break; - } + if (RushConfig[me.profile].type === RushModes.rusher) { + let tick = getTickCount(); + let askAgain = 1; + say("questinfo"); + while (!command) { + // wait up to 3 minutes + if (getTickCount() - tick > Time.minutes(3)) { + break; + } - if (getTickCount() - tick > Time.minutes(askAgain)) { - say("questinfo"); - askAgain++; + if (getTickCount() - tick > Time.minutes(askAgain)) { + say("questinfo"); + askAgain++; + } } - } - - if (command) { - commandSplit0 = command.split(" ")[1]; - if (!!commandSplit0 && sequence.some(el => el.toLowerCase() === commandSplit0)) { - RushThread.send(command.toLowerCase()); + if (command) { + commandSplit0 = command.toLowerCase().split(" ")[1]; + if (!!commandSplit0 && AutoRush.sequences.includes(commandSplit0)) { + RushThread.send(command.toLowerCase()); + } } } - + delay(200); RushThread.send("go"); - while (true) { + while (!done) { if (commands.length > 0) { command = commands.shift(); @@ -224,25 +215,23 @@ function Rusher () { } if (commandSplit0.toLowerCase() === "do") { - for (i = 0; i < sequence.length; i += 1) { - if (command.split(" ")[1] && sequence[i].match(command.split(" ")[1], "gi")) { - RushThread.reload(); - RushThread.send(command.split(" ")[1]); - - break; - } + let script = command.split(" ")[1]; + if (!script || !AutoRush.sequences.some(el => el.match(script, "gi"))) { + say("Invalid sequence"); + break; } - - i === sequence.length && say("Invalid sequence"); + RushThread.reload(); + RushThread.send(script); } else if (commandSplit0.toLowerCase() === "clear") { - if (!isNaN(parseInt(command.split(" ")[1], 10)) - && parseInt(command.split(" ")[1], 10) > 0 - && parseInt(command.split(" ")[1], 10) <= 132) { - RushThread.reload(); - RushThread.send(command); - } else { + let area = command.split(" ")[1]; + if (!area) break; + let areaId = parseInt(area, 10); + if (isNaN(areaId) || areaId < 1 || areaId > 132) { say("Invalid area"); + break; } + RushThread.reload(); + RushThread.send(command); } } diff --git a/d2bs/kolbot/libs/scripts/SealLeecher.js b/d2bs/kolbot/libs/scripts/SealLeecher.js index d9cf53b6a..13e19a7cc 100644 --- a/d2bs/kolbot/libs/scripts/SealLeecher.js +++ b/d2bs/kolbot/libs/scripts/SealLeecher.js @@ -8,9 +8,7 @@ const SealLeecher = new Runnable( function SealLeecher() { let commands = []; - Town.goToTown(4); - Town.doChores(); Town.move("portalspot"); if (!Config.Leader) { @@ -98,5 +96,7 @@ const SealLeecher = new Runnable( return true; }, - sdk.areas.PandemoniumFortress + { + startArea: sdk.areas.PandemoniumFortress + } ); diff --git a/d2bs/kolbot/libs/scripts/SharpTooth.js b/d2bs/kolbot/libs/scripts/SharpTooth.js index f99ab88d0..25863d621 100644 --- a/d2bs/kolbot/libs/scripts/SharpTooth.js +++ b/d2bs/kolbot/libs/scripts/SharpTooth.js @@ -7,7 +7,6 @@ const SharpTooth = new Runnable( function SharpTooth () { - Town.doChores(); Pather.useWaypoint(sdk.areas.FrigidHighlands); Precast.doPrecast(true); @@ -22,8 +21,8 @@ const SharpTooth = new Runnable( return true; }, - sdk.areas.FrigidHighlands, { + startArea: sdk.areas.FrigidHighlands, bossid: getLocaleString(sdk.locale.monsters.SharpToothSayer), } ); diff --git a/d2bs/kolbot/libs/scripts/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js index 5ddabb717..0bcdd6801 100644 --- a/d2bs/kolbot/libs/scripts/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -302,5 +302,8 @@ const ShopBot = new Runnable( } return true; + }, + { + preAction: null } ); diff --git a/d2bs/kolbot/libs/scripts/Smith.js b/d2bs/kolbot/libs/scripts/Smith.js index d63ea64df..98d36ffe7 100644 --- a/d2bs/kolbot/libs/scripts/Smith.js +++ b/d2bs/kolbot/libs/scripts/Smith.js @@ -7,7 +7,6 @@ const Smith = new Runnable( function Smith () { - Town.doChores(); Pather.useWaypoint(sdk.areas.OuterCloister); Precast.doPrecast(true); @@ -20,8 +19,8 @@ const Smith = new Runnable( return true; }, - sdk.areas.OuterCloister, { + startArea: sdk.areas.OuterCloister, bossid: getLocaleString(sdk.locale.monsters.TheSmith), } ); diff --git a/d2bs/kolbot/libs/scripts/Snapchip.js b/d2bs/kolbot/libs/scripts/Snapchip.js index 173633107..764016126 100644 --- a/d2bs/kolbot/libs/scripts/Snapchip.js +++ b/d2bs/kolbot/libs/scripts/Snapchip.js @@ -7,7 +7,6 @@ const Snapchip = new Runnable( function Snapchip () { - Town.doChores(); Pather.useWaypoint(sdk.areas.AncientsWay); Precast.doPrecast(true); @@ -21,5 +20,7 @@ const Snapchip = new Runnable( return true; }, - sdk.areas.AncientsWay + { + startArea: sdk.areas.AncientsWay + } ); diff --git a/d2bs/kolbot/libs/scripts/Stormtree.js b/d2bs/kolbot/libs/scripts/Stormtree.js index 7cac460da..44be33c37 100644 --- a/d2bs/kolbot/libs/scripts/Stormtree.js +++ b/d2bs/kolbot/libs/scripts/Stormtree.js @@ -7,7 +7,6 @@ const Stormtree = new Runnable( function Stormtree () { - Town.doChores(); Pather.useWaypoint(sdk.areas.LowerKurast); Precast.doPrecast(true); @@ -19,8 +18,8 @@ const Stormtree = new Runnable( return true; }, - sdk.areas.LowerKurast, { + startArea: sdk.areas.LowerKurast, bossid: getLocaleString(sdk.locale.monsters.Stormtree), } ); diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index 3a8328075..bb7c4856f 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -7,7 +7,6 @@ const Summoner = new Runnable( function Summoner () { - Town.doChores(); Pather.useWaypoint(sdk.areas.ArcaneSanctuary); Precast.doPrecast(true); @@ -70,5 +69,7 @@ const Summoner = new Runnable( return true; }, - sdk.areas.ArcaneSanctuary + { + startArea: sdk.areas.ArcaneSanctuary + } ); diff --git a/d2bs/kolbot/libs/scripts/ThreshSocket.js b/d2bs/kolbot/libs/scripts/ThreshSocket.js index 692005f8c..65d10c465 100644 --- a/d2bs/kolbot/libs/scripts/ThreshSocket.js +++ b/d2bs/kolbot/libs/scripts/ThreshSocket.js @@ -7,7 +7,6 @@ const ThreshSocket = new Runnable( function ThreshSocket () { - Town.doChores(); Pather.useWaypoint(sdk.areas.ArreatPlateau); Precast.doPrecast(true); @@ -20,8 +19,8 @@ const ThreshSocket = new Runnable( return true; }, - sdk.areas.ArreatPlateau, { + startArea: sdk.areas.ArreatPlateau, bossid: getLocaleString(sdk.locale.monsters.ThreshSocket), } ); diff --git a/d2bs/kolbot/libs/scripts/Tombs.js b/d2bs/kolbot/libs/scripts/Tombs.js index 2c06ef2a6..7cde54ed6 100644 --- a/d2bs/kolbot/libs/scripts/Tombs.js +++ b/d2bs/kolbot/libs/scripts/Tombs.js @@ -7,7 +7,6 @@ const Tombs = new Runnable( function Tombs() { - Town.doChores(); Pather.useWaypoint(sdk.areas.CanyonofMagic); Precast.doPrecast(true); const correctTomb = getRoom().correcttomb; @@ -31,5 +30,7 @@ const Tombs = new Runnable( return true; }, - sdk.areas.CanyonofMagic + { + startArea: sdk.areas.CanyonofMagic + } ); diff --git a/d2bs/kolbot/libs/scripts/Travincal.js b/d2bs/kolbot/libs/scripts/Travincal.js index 13450e9a4..3388bca06 100644 --- a/d2bs/kolbot/libs/scripts/Travincal.js +++ b/d2bs/kolbot/libs/scripts/Travincal.js @@ -7,7 +7,6 @@ const Travincal = new Runnable( function Travincal () { - Town.doChores(); Pather.useWaypoint(sdk.areas.Travincal); Precast.doPrecast(true); @@ -69,5 +68,7 @@ const Travincal = new Runnable( return true; }, - sdk.areas.Travincal + { + startArea: sdk.areas.Travincal + } ); diff --git a/d2bs/kolbot/libs/scripts/TravincalLeech.js b/d2bs/kolbot/libs/scripts/TravincalLeech.js index f0951ce5d..e0c5b706c 100644 --- a/d2bs/kolbot/libs/scripts/TravincalLeech.js +++ b/d2bs/kolbot/libs/scripts/TravincalLeech.js @@ -84,5 +84,8 @@ const TravincalLeech = new Runnable( return true; }, - sdk.areas.KurastDocktown + { + startArea: sdk.areas.KurastDocktown, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/Treehead.js b/d2bs/kolbot/libs/scripts/Treehead.js index 0c48bac58..04992cb4a 100644 --- a/d2bs/kolbot/libs/scripts/Treehead.js +++ b/d2bs/kolbot/libs/scripts/Treehead.js @@ -7,7 +7,6 @@ const Treehead = new Runnable( function Treehead () { - Town.doChores(); Pather.useWaypoint(sdk.areas.DarkWood); Precast.doPrecast(true); @@ -19,8 +18,8 @@ const Treehead = new Runnable( return true; }, - sdk.areas.DarkWood, { + startArea: sdk.areas.DarkWood, bossid: getLocaleString(sdk.locale.monsters.TreeheadWoodFist), } ); diff --git a/d2bs/kolbot/libs/scripts/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js index c0b06b501..d3b986c3a 100644 --- a/d2bs/kolbot/libs/scripts/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -69,5 +69,7 @@ const Tristram = new Runnable( return true; }, - sdk.areas.StonyField + { + startArea: sdk.areas.StonyField + } ); diff --git a/d2bs/kolbot/libs/scripts/TristramLeech.js b/d2bs/kolbot/libs/scripts/TristramLeech.js index 2e144cc6b..c46c6fbbd 100644 --- a/d2bs/kolbot/libs/scripts/TristramLeech.js +++ b/d2bs/kolbot/libs/scripts/TristramLeech.js @@ -16,8 +16,7 @@ const TristramLeech = new Runnable( done = true; } }; - - Town.doChores(); + Town.goToTown(1); Town.move("portalspot"); @@ -124,5 +123,7 @@ const TristramLeech = new Runnable( return true; }, - sdk.areas.RogueEncampment + { + startArea: sdk.areas.RogueEncampment + } ); diff --git a/d2bs/kolbot/libs/scripts/UndergroundPassage.js b/d2bs/kolbot/libs/scripts/UndergroundPassage.js index b266c6cd5..3b471227e 100644 --- a/d2bs/kolbot/libs/scripts/UndergroundPassage.js +++ b/d2bs/kolbot/libs/scripts/UndergroundPassage.js @@ -7,7 +7,6 @@ const UndergroundPassage = new Runnable( function UndergroundPassage() { - Town.doChores(); Pather.useWaypoint(sdk.areas.StonyField); Precast.doPrecast(true); @@ -19,5 +18,7 @@ const UndergroundPassage = new Runnable( return true; }, - sdk.areas.StonyField + { + startArea: sdk.areas.StonyField + } ); diff --git a/d2bs/kolbot/libs/scripts/UserAddon.js b/d2bs/kolbot/libs/scripts/UserAddon.js index c22d10cfe..20fd05e4b 100644 --- a/d2bs/kolbot/libs/scripts/UserAddon.js +++ b/d2bs/kolbot/libs/scripts/UserAddon.js @@ -107,5 +107,8 @@ const UserAddon = new Runnable( dummy && dummy.remove(); UnitInfo.remove(); } + }, + { + preAction: null } ); diff --git a/d2bs/kolbot/libs/scripts/WPGetter.js b/d2bs/kolbot/libs/scripts/WPGetter.js index e0625d143..65c9a0360 100644 --- a/d2bs/kolbot/libs/scripts/WPGetter.js +++ b/d2bs/kolbot/libs/scripts/WPGetter.js @@ -7,7 +7,6 @@ const WPGetter = new Runnable( function WPGetter () { - Town.doChores(); Town.goToTown(); Pather.getWP(me.area); @@ -15,7 +14,7 @@ const WPGetter = new Runnable( let lastWP = sdk.areas.townOfAct((highestAct === 5 ? highestAct : highestAct + 1)); lastWP === sdk.areas.Harrogath && (lastWP = me.baal ? sdk.areas.WorldstoneLvl2 : sdk.areas.AncientsWay); let wpsToGet = Pather.nonTownWpAreas - .filter((wp) => wp < lastWP && wp !== sdk.areas.HallsofPain && !getWaypoint(Pather.wpAreas.indexOf(wp))); + .filter((wp) => wp < lastWP && wp !== sdk.areas.HallsofPain && !me.haveWaypoint(wp)); console.debug(wpsToGet); diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 491154ac5..95c138e6c 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -456,5 +456,8 @@ const Wakka = new Runnable( return true; }, - sdk.areas.PandemoniumFortress + { + startArea: sdk.areas.PandemoniumFortress, + preAction: null + } ); diff --git a/d2bs/kolbot/libs/scripts/Worldstone.js b/d2bs/kolbot/libs/scripts/Worldstone.js index c47831728..14be2b358 100644 --- a/d2bs/kolbot/libs/scripts/Worldstone.js +++ b/d2bs/kolbot/libs/scripts/Worldstone.js @@ -7,7 +7,6 @@ const Worldstone = new Runnable( function Worldstone() { - Town.doChores(); Pather.useWaypoint(sdk.areas.WorldstoneLvl2); Precast.doPrecast(true); /** @@ -38,5 +37,7 @@ const Worldstone = new Runnable( return true; }, - sdk.areas.WorldstoneLvl2 + { + startArea: sdk.areas.WorldstoneLvl2 + } ); diff --git a/d2bs/kolbot/libs/scripts/rungamex.js b/d2bs/kolbot/libs/scripts/rungamex.js new file mode 100644 index 000000000..bf4cedfd7 --- /dev/null +++ b/d2bs/kolbot/libs/scripts/rungamex.js @@ -0,0 +1,1781 @@ +/** +* @filename rungame2.js +* @author darjkeel@d2jsp +* @desc one script to run them all, and in the dankness bind them +* @readme rungame2 readme.txt +**/ + +function rungamex() { + let boWP = 83, //Default bo wp + chars = [ //Define chars and list their bosses/tasks + "Sorceress", + [ + "chores", + "getbo 83", + "Sszark the Burning", + "goto 102", + "areadelay in 75 Parzivol PLA", + "taxi on", + "areadelay in 75 Parzivol PLA", + "Wyand Voidbringer", + "areadelay in 75 Parzivol PLA", + "Bremm Sparkfist", + "areadelay in 75 Parzivol PLA", + "Maffer Dragonhand", + "goto 105", + "areadelay in 103 Parzivol PLA", + "Izual", + "Hephasto the Armorer", + "taxi off", + "chaos", + "areadelay in 108 Parzivol PLA", + "getleg", + "makecows", + "chests on", + "Witch Doctor Endugu", + "Icehawk Riftwing", + "chests off", + "findshrine", + "getbo 108", + "Urdars", + "chores" + ], + "QuestMF", + [ + "chores", + "getbo 83", + "Andariel", + "chests on", + "Mephisto", + "chests off", + "Travincal", + "Fire Eye", + "The Summoner", + "Duriel", + "The Cow King", + "Beetleburst", + "Dark Elder", + "Tunnels", + "chores" + ], + "PLA", + [ + "chores", + "getbo 83", + "Travincal", + "chores", + "areadelay in 102 CCA", + "ride CCA", + "chaos", + "getbo 108", + "ride CCA", + "chores" + ], + "Parzivol", + [ + "chores", + "givebo 83 QuestMF PLA CCA", + "Travincal", + "goto 75", + "areadelay in 102 CCA", + "ride CCA", + "chaos", + "givebo 108 PLA CCA", + "ride CCA", + "chores" + ] + ], + + //Chaos options, only apply to walky cs "chaos" + tele = "star", //Where to tp when running chaos. "gate" or "star" + teleMsg = [""], //Message(s) after tp to cs. If multiple, picks one at random + openSeals = [3], //Tele to and open seals after tp. 1 = infector main, 2 = infector other, 3 = seis, 4 = vizier other, 5 = vizier main + sealMsg = [], //Message(s) after pre-popping seals. If multiple, picks one at random + clearChars = ["PLA", "Parzivol"], //Name of chars who will clear chaos + clearMode = 0, //Clear mode = 0 = all, 0xF = skip normal, 0x7 = just champion/uniques (no minions) + clearDirection = 1, //0 for clockwise Vizi->Seis->Fect. 1 for counterclockwise Fect->Seis->Vizi + //Diablo options, apply to either walk cs "chaos" or taxi cs "Infector of Souls","Lord de Seis","Grand Vizier of Chaos","Diablo" + killDiablo = ["PLA"], //Names of chars who will kill Diablo. Leave out your Baron(ess) char(s) for best drops + leechDiablo = ["Parzivol"], //Names of chars who will leech d exp but not kill + weakenDiablo = [], //Names of chars who will leave party and cast weakening spells (static/curse/aura) but not kill + shrineDiablo = "Parzivol", //Name a char who will use experience shrine at diablo + //Delay options for good happy timings + boDelay = 180, //Max time to wait giving or getting bo + boQuit = false, //Quits if no bo given or received in time + taxiDelay = 120, //Max time to wait for a taxi tp + taxiQuit = false, //Quit if no taxi tp in time + chaosDelay = 180, //Max time to wait for a chaos tp + chaosQuit = false, //Quit if no cs tp found in time + shrineDelay = 40, //Max time to wait for a tp to shrine in useshrine + finderDelay = 0, //Make shrine finder wait for user to take + areaDelay = 30, //Max time to wait for other chars in areadelay + //END OF USER CONFIGS BEWARE NO TOUCHY BELOW + Bosses = [ + "The Cow King", + "Corpsefire", + "Bishibosh", + "Blood Raven", + "Bonebreaker", + "Coldcrow", + "Rakanishu", + "Griswold", + "Treehead Woodfist", + "The Countess", + "The Smith", + "Pitspawn Fouldog", + "Bone Ash", + "Andariel", + "Radament", + "Creeping Feature", + "Bloodwitch the Wild", + "Beetleburst", + "Coldworm the Burrower", + "Dark Elder", + "Fangskin", + "Fire Eye", + "The Summoner", + "Ancient Kaa the Soulless", + "Duriel", + "Sszark the Burning", + "Witch Doctor Endugu", + "Stormtree", + "Battlemaid Sarina", + "Icehawk Riftwing", + "Travincal", + "Bremm Sparkfist", + "Wyand Voidbringer", + "Maffer Dragonhand", + "Mephisto", + "Izual", + "Hephasto the Armorer", + "Infector of Souls", + "Lord de Seis", + "Grand Vizier of Chaos", + "Diablo" + ], + Tasks = [ + "chores", + "givebo", + "getbo", + "taxi", + "ride", + "chaos", + "getleg", + "makecows", + "clearcows", + "findshrine", + "useshrine", + "chests", + "goto", + "areadelay", + "Shopbot", + "Urdars", + "Tunnels" + ], + wps = [ + 1, 3, 3, + 3, 3, 3, + 4, 4, 5, + 6, 27, 29, + 32, 35, 48, + 42, 57, 43, + 43, 44, 44, + 74, 74, 46, + 46, 76, 78, + 79, 80, 81, + 83, 101, 101, + 101, 101, 106, + 106, 107, 107, + 107, 107, 106 + ], + exits = [ + [0], + [2, 8], + [0], + [17], + [17, 18], + [9], + [38], + [0], + [0], + [20, 21, 22, 23, 24, 25], + [28], + [30], + [33], + [36, 37], + [49], + [41, 55, 59], + [60], + [0], + [62, 63, 64], + [0], + [45, 58, 61], + [0], + [0], + [0], + [0], + [85], + [88, 89, 91], + [78], + [94], + [92], + [0], + [102], + [102], + [102], + [102], + [105], + [107], + [108], + [108], + [108], + [108] + ], + presets = [ + 773, + 774, + 734, + 805, + 735, + 736, + 737, + 739, + 738, + 740, + 754, + 741, + 743, + 156, + 744, + 748, + 745, + 747, + 749, + 751, + 746, + 750, + 250, + 753, + 211, + 755, + 756, + false, + 758, + 759, + false, + 762, + 764, + 765, + 242, + 256, + 775, + false, + false, + false, + false + ], + vizLayout, + seisLayout, + infLayout, + vizx, + vizy, + seisx, + seisy, + infx, + infy, + diax, + diay, + cmd, + last, + taxiChar = false, + chests = false, + runString, + runDone; + + /** + * @param {string | number} name + * @returns {boolean | number} + */ + this.find = function (name) { + let unit = Game.getPlayer(name); + if (unit) return unit.area; + + let party = getParty(); + if (!party) return false; + + do { + if (party.name === name) { + return (party.area > 0) ? party.area : true; + } + } while (party.getNext()); + return false; + }; + + /** + * @param {string | number} name + * @returns {Act} + */ + this.getAct = function(name) { + let _area = this.find(name); + return _area === true + ? me.act + : sdk.areas.actOf(_area); + }; + + /** + * @param {number} id + * @returns {boolean} + */ + this.openSeal = function (id) { + let seal = Game.getObject(id); + if (seal && seal.mode) return false; + Pather.moveToPreset(108, 2, id, 4, 4); + seal = Game.getObject(id); + if (!seal || (seal && seal.mode)) return false; + let retryCount = 0; + + while (!seal.mode) { + if (!Skill.cast(43, 0, seal) || retryCount > 50) seal.interact(); + delay(100); + if (seal.mode === 0) { + if (retryCount % 5 === 0) { + Pather.moveTo(seal.x + rand(-5, 5), seal.y + rand(-5, 5)); + } + delay(400); + retryCount++; + } + if (retryCount > 100) { + D2Bot.printToConsole("OPEN SEAL " + id + " FAILED in game: " + me.gamename, 9); + return false; + } + if (cmd === "Diablo") { + break; + } + } + return true; + }; + + this.getLayout = function(seal, value) { + let sealPreset = getPresetUnit(108, 2, seal); + if (!seal) { + throw new Error("Seal preset not found."); + } + if (sealPreset.roomy * 5 + sealPreset.y === value || sealPreset.roomx * 5 + sealPreset.x === value) { + return 1; + } + return 2; + }; + + this.initLayout = function() { + vizLayout = getLayout(396, 5275); // 1 = "Y", 2 = "L" + if (vizLayout === 1) vizx = 7680, vizy = 5295; // Y (7680,5295) L (7685,5320) + if (vizLayout === 2) vizx = 7685, vizy = 5320; + seisLayout = getLayout(394, 7773); // 1 = "2", 2 = "5" + if (seisLayout === 1) seisx = 7775, seisy = 5200; // 2 (7775,5220) 5 (7780,5185) + if (seisLayout === 2) seisx = 7780, seisy = 5175; + infLayout = getLayout(392, 7893); // 1 = "I", 2 = "J" + if (infLayout === 1) infx = 7910, infy = 5295; // I (7910,5295) J (7295,5280) + if (infLayout === 2) infx = 7925, infy = 5280; + diax = 7795, diay = 5295; + }; + + /** + * @param {string[]} char_str + */ + this.bo = function(char_str) { + let unit; + if (char_str) { + Precast.doPrecast(true); + for (let i = 0; i < char_str.length; i++) { + unit = getUnit(0, char_str[i]); + if (unit + && unit.name !== me.name + && !unit.dead + && Misc.inMyParty(unit.name) + && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) + && getDistance(me, unit) <= 20) { + Precast.doPrecast(true); + } + } + } else { + unit = getUnit(0); + if (unit) { + do { + if (unit.name !== me.name + && !unit.dead + && Misc.inMyParty(unit.name) + && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) + && getDistance(me, unit) <= 20) { + Precast.doPrecast(true); + } + } while (unit.getNext()); + } + } + }; + + this.chant = function(char_str) { + let unit; + if (char_str) { + for (let i = 0; i < char_str.length; i++) { + unit = getUnit(0, char_str[i]); + if (unit && !unit.getState(16) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(250); + + } + unit = getUnit(1); + if (unit) { + do { + if (unit.getParent() && char_str.indexOf(unit.getParent().name) > -1 && !unit.getState(16) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(500); + } + } while (unit.getNext()); + } + } + } else { + unit = getUnit(0); + if (unit) { + do { + if (!unit.getState(16) && Misc.inMyParty(unit.name) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(250); + } + } while (unit.getNext()); + + } + unit = getUnit(1); + if (unit) { + do { + if (unit.getParent() && !unit.getState(16) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(500); + } + } while (unit.getNext()); + } + } + }; + + this.pop = function() { + let monsterList = [], + monster = getUnit(1); + if (monster) { + do { + if ([345, 346, 347].indexOf(monster.classid) > -1 || (monster.spectype !== 0 && monster.spectype !== 8)) { + if (getDistance(me, monster) <= 30) monsterList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + for (let i = 0; i < monsterList.length; i++) { + if ((monsterList[i].mode === 0 || monsterList[i].mode === 12) && !monsterList[i].getState(1) && !monsterList[i].getState(96) && !monsterList[i].getState(99) && !monsterList[i].getState(104) && !monsterList[i].getState(107) && !monsterList[i].getState(118)) { + console.log("popping " + monsterList[i].name + " spectype: " + monsterList[i].spectype + " classid: " + monsterList[i].classid); + Pather.moveTo(monsterList[i].x, monsterList[i].y, 3); + ClassAttack.findItem(10); + } + } + }; + + this.chests = function() { + let preset = getPresetUnits(me.area, 2), + coords = []; + while (preset.length > 0) { + if ([5, 6, 87, 88, 139, 147, 148, 177, 329, 330, 333, 371, 387, 389, 397, 424, 425, 454, 455, 580, 581].indexOf(preset[0].id) > -1) { + coords.push({ + x: preset[0].roomx * 5 + preset[0].x, + y: preset[0].roomy * 5 + preset[0].y + }); + } + preset.shift(); + } + while (coords.length) { + coords.sort(Sort.units); + Pather.moveToUnit(coords[0], 1, 2); + Misc.openChests(5); + coords.shift(); + } + }; + + this.delays = function(mode, delay_, char_str, mode2, aid) { + let tTimer = getTickCount(); + switch (mode) { + case "givebo": + for (let i = 0; i < char_str.length; i++) { + if (getTickCount() >= tTimer + delay_ * 1e3 - 5000 && i < char_str.length) { + D2Bot.printToConsole("give bo fail", 9); + if (boQuit) quit(); + break; + } + if (getUnit(0, char_str[i])) i++; + delay(100); + } + this.bo(char_str); + break; + case "getbo": + while (!Misc.inMyParty(char_str) || !getUnit(0, char_str, 10)) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("get bo fail 1", 9); + if (boQuit) quit(); + break; + } + delay(100); + } + while (getTickCount() <= tTimer + delay_ * 1e3 && (!me.getState(26) || !me.getState(32) || !me.getState(51))) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("get bo fail 2", 9); + if (boQuit) quit(); + break; + } + delay(100); + } + if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.chant(); + else { + for (let i = 0; i < 10; i++) { + let brk = true; + for (let j = 0; j < 5; j++) { + delay(100); + if (getUnit(0, char_str, 10)) brk = false; + } + if (brk) break; + } + } + break; + case "givechant": + { + let i = 0; + while (getTickCount() <= tTimer + delay_ * 1e3) { + if (getTickCount() >= tTimer + delay_ * 1e3) break; + if (char_str && char_str.length > 0 && getUnit(0, char_str[i])) { + this.chant(char_str[i]); + i++; + } else this.chant(); + delay(100); + } + } + break; + case "ride": + while (!Misc.inMyParty(char_str) || !taxiChar) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("wait for taxi fail", 9); + if (taxiQuit) quit(); + break; + } + delay(100); + } + break; + case "chaos": + if (getUnit(2, "waypoint")) Pather.useWaypoint(103, true); + else Town.goToTown(4); + Town.move("portalspot"); + while (me.area !== 108 && !Pather.usePortal(108, char_str)) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("wait for chaos fail", 9); + if (taxiQuit) quit(); + break; + } + delay(100); + } + break; + case "shrine": + Town.goToTown(1); + Town.move("portalspot"); + while (!me.getState(137)) { + if (char_str != null && !Misc.inMyParty(char_str)) break; + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("wait for shrine fail", 9); + if (taxiQuit) quit(); + break; + } + + if (Pather.usePortal(null, char_str)) { + let shrine = getUnit(2, "shrine"); + if (shrine) { + do { + if (shrine.objtype === 15 && shrine.mode === 0) { + Pather.moveTo(shrine.x - 2, shrine.y - 2); + Misc.getShrine(shrine); + Pather.makePortal(); + if (me.getState(137)) break; + } + } while (shrine.getNext()); + } + if (!char_str || !Pather.usePortal(1, char_str)) Town.goToTown(1); + break; + } + delay(100); + } + break; + case "finder": + loop1: + while (getTickCount() <= tTimer + delay_ * 1e3) { + let unit = getUnit(0); + if (unit) { + do { + if (unit.getState(137)) break loop1; + } while (unit.getNext()); + } + delay(100); + } + break; + case "area": + let chars_ = []; + let pp = getParty(); //big pp variable name come at me bro + if (pp) { + do { + if (!char_str) { + if (pp.name !== me.name) chars_.push(pp.name); + } else if (char_str) { + if (char_str.indexOf(pp.name) > -1 && pp.name !== me.name) chars_.push(pp.name); + } + } while (pp.getNext()); + } + loop2: + while (getTickCount() <= tTimer + delay_ * 1e3) { + delay(100); + if (mode2 === "in") { + for (let i = 0; i < chars_.length; i++) { + if (!char_str && this.find(chars_[i]) === aid) break loop2; + else if (char_str && this.find(chars_[i]) !== aid) break; + else if (char_str && i === chars_.length - 1) break loop2; + } + } else if (mode2 === "out") { + for (let i = 0; i < chars_.length; i++) { + if (this.find(chars_[i]) === aid) break; + else if (i === chars_.length - 1) break loop2; + } + } + } + break; + } + }; + + this.killBoss = function(name) { + if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); + if (me.name === taxiChar + && name !== "Grand Vizier of Chaos" + && name !== "Lord de Seis" + && name !== "Infector of Souls" + && name !== "Diablo") { + if (getUnit(1, name) && getDistance(getUnit(1, name)) > 10 && me.area !== 108) Pather.moveTo(getUnit(1, name).x + rand(-5, 5), getUnit(1, name).y + rand(-5, 5), 0); + Pather.makePortal(); + say(name); + } + if (name === "Grand Vizier of Chaos" + || name === "Lord de Seis" + || name === "Infector of Souls" + || name === "Diablo") { + if (!diax) this.initLayout(); + let sealTimer = getTickCount(); + + for (let i = 0; getTickCount() <= sealTimer + 20 * 1e3; i++) { + if (!getUnit(1, name)) { + if (!this.precast()) Attack.clear(10); + if (name === "Grand Vizier of Chaos") Pather.moveTo(vizx, vizy); + if (name === "Infector of Souls") Pather.moveTo(infx, infy); + if (name === "Lord de Seis") Pather.moveTo(seisx, seisy); + if (name === "Diablo") Pather.moveTo(diax, diay); + delay(10); + } else if (i >= 2000) break; + else break; + } + } + if (getUnit(1, name) && getUnit(1, name).hp > 0) { + if (me.name === taxiChar) { + for (let i = 0; i < 30; i++) { + let mon = getUnit(1, name); + if (getUnit(1, name)) Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); + if (!mon || mon.hp === 0) break; + delay(100); + } + } else if (getUnit(1, name)) { + if (getUnit(1, name).spectype === 0) { + Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0, name); + } else { + Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); + } + } + } + if (name === "Travincal") { + let monsterList = []; + let monster = Game.getMonster(); + if (monster) { + do { + if ([345, 346, 347].includes(monster.classid) && monster.attackable) { + monsterList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + Attack.clearList(monsterList); + } + if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); + if (me.classid === 4 && Config.FindItem) this.pop(); + if (name === "Andariel") delay(2000); + Pickit.pickItems(); + return true; + }; + + this.runBoss = function (name, wp, exit, preset) { + console.log(name); + if (me.area !== wp && exit.indexOf(me.area) === -1) { + let wpunit = getUnit(2, "waypoint"); + if (!wpunit) Town.goToTown(); + Pather.useWaypoint(wp, true); + Precast.doPrecast(true); + } + if (!this.bossSpecial(name, 1)) return false; + if (exit.indexOf(me.area) > 0 && me.area !== exit[exit.length - 1]) { + let i = []; + for (let j = exit.indexOf(me.area); j < exit.length; j++) i.push(exit[j]); + exit = i; + } + if (exit[0] !== 0 && me.area !== exit[exit.length - 1]) Pather.moveToExit(exit, true); + if (!this.bossSpecial(name, 2)) return false; + if (preset && getPresetUnit(me.area, 1, preset)) Pather.moveToPreset(me.area, 1, preset); + if (this.bossSpecial(name, 3)) this.killBoss(name); + if (!this.bossSpecial(name, 4)) return false; + return true; + }; + + this.bossSpecial = function (name, t) { + if (t === 1) { + if (name === "The Cow King") { + Town.move("stash"); + for (let i = 0; i < 30; i++) { + if (Pather.usePortal(39)) break; + delay(100); + } + if (!Pather.usePortal(39)) return false; + else return true; + } + if (chests && ["Stormtree", "Battlemaid Sarina", "Icehawk Riftwing"].indexOf(name) > -1) { + this.chests(); + Pickit.pickItems(); + return true; + } + return true; + } + if (t === 2) { + if (chests && name === "Icehawk Riftwing") { + this.chests(); + } + if (name === "Griswold") { + Pather.moveToPreset(me.area, 1, 737); + while (!getUnit(2, 60)) delay(10); + Pather.usePortal(38); + return true; + } + if (name === "Fire Eye") { + Pather.usePortal(null); + return true; + } + if (name === "Ancient Kaa the Soulless") { + let j, k, pre, kaa = false; + for (j = 66; kaa === false; j++) { + if (me.area !== 46) Pather.moveToExit(46, true); + Pather.moveToExit(j, true); + pre = getPresetUnits(me.area, 1); + for (k = 0; k < pre.length; k++) { + if (pre[k].id === 753) kaa = true; + } + } + return true; + } + if (name === "Duriel") { + Pather.moveToExit(getRoom().correcttomb, true); + Pather.moveToPreset(me.area, 2, 152, -3, -3); + while (!getUnit(2, 100)) delay(10); + if (me.classid === 1 && me.getSkill(43, 1)) { + for (let i = 0; i < 10; i += 1) { + if (me.area === 73) { + break; + } + Skill.cast(43, 0, getUnit(2, 100)); + } + } else Pather.useUnit(2, 100, 73); + return true; + } + if (name === "Travincal") { + Pather.moveTo(me.x + 101, me.y - 71); + return true; + } + if (name === "Grand Vizier of Chaos") { + if (!vizx) this.initLayout(); + if (me.name === taxiChar) { + Pather.moveTo(vizx, vizy, 3); + Pather.makePortal(); + say(name); + } + this.openSeal(396); + Pather.moveTo(vizx, vizy, 3); + return true; + } + if (name === "Lord de Seis") { + if (!seisx) this.initLayout(); + if (me.name === taxiChar) { + Pather.moveTo(seisx, seisy, 3); + Pather.makePortal(); + say(name); + } + this.openSeal(394); + Pather.moveTo(seisx, seisy, 3); + return true; + } + if (name === "Infector of Souls") { + if (!infx) this.initLayout(); + if (me.name === taxiChar) { + Pather.moveTo(infx, infy, 3); + Pather.makePortal(); + say(name); + } + this.openSeal(392); + Pather.moveTo(infx, infy, 3); + return true; + } + if (name === "Diablo") { + if (!diax) this.initLayout(); + Pather.moveTo(diax, diay, 3); + return true; + } + return true; + } + if (t === 3) { + if (name === "Diablo") { + if (me.name === taxiChar) { + //cmd = "Diablo"; + say("Diablo"); + this.diablo(); + return false; + } else return true; + } + return true; + } + if (t === 4) { + if (name === "Fire Eye") { + Pather.moveTo(10073, 8668); + Pather.usePortal(null); + return true; + } + if (name === "The Summoner") { + let journal = getUnit(2, 357); + if (journal) { + Pather.moveTo(journal.x, journal.y); + for (let i = 0; i < 5; i += 1) { + journal.interact(); + delay(me.ping + 500); + me.cancel(); + delay(me.ping + 500); + if (Pather.getPortal(46)) break; + } + Pather.usePortal(46); + } + return true; + } + if (chests && ["Bonebreaker", "Coldcrow", "Countess", "Creeping Feature", "Coldworm the Burrower", "Dark Elder", "Icehawk Riftwing", "Mephisto"].indexOf(name) > -1) { + if (name === "Coldcrow") Pather.moveToExit(13, true); + if (name === "Dark Elder") Pather.moveToExit(65, true); + if (name === "Icehawk Riftwing") Pather.moveToExit(93, true); + this.chests(); + Pickit.pickItems(); + return true; + } + if (name === "Grand Vizier of Chaos") { + this.openSeal(395); + return true; + } + if (name === "Infector of Souls") { + this.openSeal(393); + return true; + } + return true; + } + return false; + }; + + this.runList = function (list) { + runString = []; + for (let i = 0; i < list.length; i++) { + if (Bosses.indexOf(list[i]) > -1) { + if (runString.indexOf(list[i]) === -1) runString.push(list[i]); + else D2Bot.printToConsole("boss '" + list[i] + "' listed more than once", 9); + } else { + for (let j = 0; j < Tasks.length; j++) { + if (list[i].indexOf(Tasks[j]) === 0) runString.push(list[i]); + } + if (runString.indexOf(list[i]) === -1) D2Bot.printToConsole("boss/task string '" + list[i] + "' not recognized and ignored", 9); + } + } + runDone = []; + for (let i = 0; runDone.length < runString.length; i++) { + if (Bosses.indexOf(runString[i]) > -1) { + let loc = Bosses.indexOf(runString[i]); + if (runDone.indexOf(runString[i]) === -1) { + this.runBoss(Bosses[loc], wps[loc], exits[loc], presets[loc]); + runDone.push(runString[i]); + } + } else { + for (let j = 0; j < Tasks.length; j++) { + if (runString[i].indexOf(Tasks[j]) === 0) { + this.runTask(runString[i]); + runDone.push(runString[i]); + } + } + } + } + return true; + }; + + this.runTask = function (task) { + console.log(task); + let wp; + if (task.indexOf("givebo") === 0) { + let boChars = []; + if (task === "givebo") { + wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf(" ") === -1) { + wp = Number(task); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + wp = Number(task.slice(0, task.indexOf(" "))); + for (let i = 0; i < chars.length; i += 2) { + if (task.indexOf(chars[i]) > -1) boChars.push(chars[i]); + } + } + if (isNaN(wp)) wp = boWP; + } + if (!wp || boChars.length === 0) { + D2Bot.printToConsole("givebo string error", 9); + return false; + } + Town.goToTown(); + Pather.useWaypoint(wp, true); + this.delays("givebo", boDelay, boChars); + } else if (task.indexOf("getbo") === 0) { + let boBarb; + if (task === "getbo") { + wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; + } + } + } else if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf(" ") === -1) { + wp = Number(task); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; + } + } + } + if (task.indexOf(" ") > -1) { + wp = Number(task.slice(0, task.indexOf(" "))); + boBarb = task.slice(task.indexOf(" ") + 1); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + if (task.indexOf(chars[i]) > -1) boBarb = chars[i - 1]; + } + } + } + if (!wp || !boBarb) { + D2Bot.printToConsole("getbo string error", 9); + return false; + } + Town.goToTown(); + Pather.useWaypoint(wp, true); + Pather.moveTo(me.x - 5, me.y - 5, 3); + this.delays("getbo", boDelay, boBarb); + } else if (task.indexOf("chant") === 0 || task.indexOf("givechant") === 0) { + let chantChars = []; + if (task === "chant" || task === "givechant") { + wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf(" ") === -1) { + wp = Number(task); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + wp = Number(task.slice(0, task.indexOf(" "))); + for (let i = 0; i < chars.length; i += 2) { + if (task.indexOf(chars[i]) > -1) chantChars.push(chars[i]); + } + } + if (isNaN(wp)) wp = boWP; + } + if (!wp || chantChars.length === 0) { + D2Bot.printToConsole("givechant string error", 9); + return false; + } + if (me.area !== wp) Pather.useWaypoint(wp, true); + this.delays("givechant", chantDelay, chantChars); + } else if (task.indexOf("taxi") === 0) { + if (task === "taxi") taxiChar = me.name; + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task === "on") { + taxiChar = me.name; + say("taxi"); + } + if (task === "off") { + taxiChar = false; + say("ixat"); + } + } + } else if (task.indexOf("ride") === 0) { + if (task === "ride") { + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("taxi") > -1) taxiChar = chars[i - 1]; + } + } + } else if (task.indexOf(" ") > -1) taxiChar = task.slice(task.indexOf(" ") + 1); + if (!taxiChar) { + D2Bot.printToConsole("ride string error", 9); + return false; + } + this.delays("ride", taxiDelay, taxiChar); + this.rideTaxi(); + } else if (task.indexOf("chests") === 0) { + if (task === "chests") { + chests = true; + } + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task === "on") chests = true; + if (task === "off") chests = false; + } + } else if (task === "chaos") { + if (me.name === taxiChar || (!taxiChar && me.classid === 1 && me.area !== 108)) { + if (me.area !== 107 && me.area !== 108) { + Town.goToTown(); + Pather.useWaypoint(107, true); + } + let teleTo; + if (tele === "gate") { + teleTo = [7795, 5555]; + } else if (tele === "star") { + teleTo = [7792, 5291]; + } + Pather.moveTo(teleTo[0], teleTo[1], 3); + Pather.makePortal(); + if (teleMsg.length > 0) say(teleMsg[rand(0, teleMsg.length - 1)]); + if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); + if (openSeals.length > 0) { + for (let k = 0; k < openSeals.length; k++) { + this.openSeal(391 + openSeals[k]); + } + Pather.moveTo(teleTo[0], teleTo[1], 3); + if (sealMsg.length > 0) say(sealMsg[rand(0, sealMsg.length - 1)]); + if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); + } + if (clearChars.indexOf(me.name) > -1) { + Pather.makePortal(); + this.clearChaos(); + } else { + Town.goToTown(); + //Town.doChores(); + //Town.move("portalspot"); + } + } else { + this.delays("chaos", chaosDelay, null); + if (me.area === 108 && clearChars.indexOf(me.name) > -1) { + this.clearChaos(); + } + } + } else if (task === "getleg") { + this.getLeg(); + } else if (task === "makecows") { + this.openPortal(); + } else if (task === "Shopbot") { + this.Shopbot(); + } else if (task === "Tunnels") { + Pather.journeyTo(65); + Attack.clearLevel(Config.ClearType); + } else if (task === "Urdars") { + Town.goToTown(); + Pather.useWaypoint(106); + Precast.doPrecast(true); + Pather.journeyTo(107); + this.Urdars(); + } else if (task === "findshrine") { + let shrineAreas = [2, 3, 4, 10, 5, 6, 7/*,26,27,28,29,30,31,32,33,34,35,41,42,43,44,76,77,78,79,80,81,82,104,105,106,107,108*/]; + Town.goToTown(); + Pather.useWaypoint(3, true); + for (let i = 0; i < shrineAreas.length; i++) { + Pather.journeyTo(shrineAreas[i]); + if (Misc.getShrinesInArea(shrineAreas[i], 15, false)) { + if (chars[chars.indexOf(me.name) + 1].indexOf("useshrine") === chars[chars.indexOf(me.name) + 1].indexOf("findshrine") + 1) { + let shrine = getUnit(2, "shrine"); + if (shrine) { + do { + if (shrine.objtype === 15 && shrine.mode === 0) { + Pather.moveTo(shrine.x - 3, shrine.y - 3); + Misc.getShrine(shrine); + if (me.getState(137)) break; + } + } while (shrine.getNext()); + } + } + break; + } + } + Town.goToTown(); + if (finderDelay && finderDelay > 0) this.delays("finder", finderDelay); + } else if (task === "useshrine") { + if (me.getState(137)) return true; + let finder; + for (let i = 1; i <= chars.length; i += 2) { + if (chars[i].indexOf("findshrine") > -1) finder = chars[i - 1]; + } + if (!finder) finder = null; + if (finder === me.name || (finder == null && me.classid === 1)) this.runTask("findshrine"); + this.delays("shrine", shrineDelay, finder); + } else if (task === "clearcows") { + if (me.area !== 39) { + Town.goToTown(1); + Town.move("stash"); + for (let i = 0; i < 30; i++) { + if (Pather.usePortal(39)) break; + delay(100); + } + } + if (me.area === 39) Attack.clearLevel(Config.ClearType); + } else if (task === "chores") { + Town.goToTown(); + Town.doChores(); + } else if (task.indexOf("areadelay") === 0) { + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf("in") === 0) { + if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "in", task); + else if (task.indexOf(" ") > -1) { + let aid = Number(task.slice(0, task.indexOf(" "))); + task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(aid)) this.delays("area", areaDelay, task, "in", aid); + else D2Bot.printToConsole("areadelay string error1", 9); + } else D2Bot.printToConsole("areadelay string error2", 9); + } else if (task.indexOf("out") === 0) { + if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "out", task); + else if (task.indexOf(" ") > -1) { + let aid = Number(task.slice(0, task.indexOf(" "))); + task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(aid)) this.delays("area", areaDelay, task, "out", aid); + else D2Bot.printToConsole("areadelay string error3", 9); + } else D2Bot.printToConsole("areadelay string error4", 9); + } else D2Bot.printToConsole("areadelay string error5", 9); + } else D2Bot.printToConsole("areadelay string error6", 9); + } else if (task.indexOf("goto") === 0) { + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + let aid = Number(task); + if (!isNaN(aid)) Pather.journeyTo(aid); + else D2Bot.printToConsole("goto string value error", 9); + } + return true; + } + return false; + }; + + this.clearChaos = function() { + this.clearAct = function(act, x, y) { + if (act === "move") { + let xPath = (clearMode === 0) ? true : false; + Pather.moveTo(x, y, 3, xPath); + if (tele === "gate" && x === 7794 && y === 5315) Pather.makePortal(); + if (tele === "star" && clearDirection === 0 && x === 7765 && y === 5294) Pather.makePortal(); + if (tele === "star" && clearDirection === 1 && x === 7825 && y === 5294) Pather.makePortal(); + Attack.clear(25, clearMode); + } else if (act === "seal") { + if (openSeal(x) === true) { + if (x === 392 || x === 396) { + for (let i = 0; i < 30; i++) { + let boss = (x === 392) ? getUnit(1, "Infector of Souls") : getUnit(1, "Grand Vizier of Chaos"); + if (boss) break; + delay(100); + } + } + } + if (((x === 393 && clearDirection === 0) || (x === 395 && clearDirection === 1)) && cmd !== "Diablo") { + cmd = "Diablo"; + say("Diablo"); + } + } else if (act === "Infector of Souls" || act === "Lord de Seis" || act === "Grand Vizier of Chaos") { + let boss = getUnit(1, act); + if (!boss) { + Pather.moveTo(x, y, 3, xPath); + Attack.clear(25, clearMode); + } + boss = getUnit(1, act); + if (boss) { + if (boss.hp <= 0 || boss.mode === 0 || boss.mode === 12) { + // + } else { + Attack.clear(25, clearMode, act); + } + } + } else if (act === "Diablo") { + cmd = "Diablo"; + } else { + throw new Error("Invalid clearAct"); + } + if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); + Precast.doPrecast(false); + }; + Precast.doPrecast(false); + Attack.clear(10); + initLayout(); + let Gate = ["move", 7794, 5545, "move", 7794, 5525, "move", 7794, 5505, "move", 7794, 5480, "move", 7794, 5440, "move", 7794, 5405, "move", 7794, 5365, "move", 7794, 5335, "move", 7794, 5315]; + let preFect = ["move", 7825, 5294, "move", 7843, 5294, "move", 7861, 5294]; + let Fect = (infLayout === 1) + ? ["move", 7890, 5295, "move", 7915, 5290, "seal", 392, 1, "Infector of Souls", 7890, 5295, "Infector of Souls", 7915, 5290, "seal", 393, 0] + : ["move", 7900, 5275, "move", 7930, 5280, "move", 7930, 5310, "seal", 392, 1, "move", 7930, 5310, "Infector of Souls", 7930, 5280, "Infector of Souls", 7900, 5275, "seal", 393, 0]; + let preSeis = ["move", 7794, 5265, "move", 7794, 5245, "move", 7794, 5225]; + let Seis2 = (openSeals.indexOf(3) > -1) + ? ["move", 7775, 5210, "move", 7775, 5195, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210] + : ["move", 7775, 5210, "move", 7775, 5195, "move", 7810, 5190, "move", 7810, 5155, "move", 7785, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210]; + let Seis5 = (openSeals.indexOf(3) > -1) ? ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195] : ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "move", 7805, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195]; + let Seis = (seisLayout === 1) ? Seis2 : Seis5; + let preVizi = ["move", 7765, 5294, "move", 7745, 5294, "move", 7715, 5294]; + let Vizi = (vizLayout === 1) + ? ["move", 7680, 5290, "move", 7665, 5275, "seal", 396, 2, "Grand Vizier of Chaos", 7665, 5275, "Grand Vizier of Chaos", 7680, 5290, "seal", 395, 0] + : ["move", 7700, 5315, "move", 7670, 5315, "seal", 396, 2, "Grand Vizier of Chaos", 7670, 5315, "Grand Vizier of Chaos", 7700, 5315, "seal", 395, 0]; + let Diablo = (clearDirection === 1) + ? ["move", 7765, 5295, "move", 7796, 5296, "Diablo", 666, 666] + : ["move", 7825, 5294, "move", 7795, 5295, "Diablo", 666, 666]; + let clearActQueue = []; + if (tele === "gate" && me.x >= 5520) clearActQueue = clearActQueue.concat(Gate); + if (clearDirection === 0) clearActQueue = clearActQueue.concat(preVizi, Vizi, preSeis, Seis, preFect, Fect, Diablo); + if (clearDirection === 1) clearActQueue = clearActQueue.concat(preFect, Fect, preSeis, Seis, preVizi, Vizi, Diablo); + for (let j = 1; clearActQueue.length > j; j += 3) { + if (cmd === "Diablo") { + if (last !== "Diablo") this.diablo(); + break; + } + this.clearAct(clearActQueue[j - 1], clearActQueue[j], clearActQueue[j + 1]); + if (cmd === "Diablo") { + if (last !== "Diablo") this.diablo(); + break; + } + delay(100); + } + return true; + }; + + this.diablo = function () { + cmd !== "Diablo" && (cmd = "Diablo"); + if (last === "Diablo") return true; + + if (taxiChar === me.name) { + if (me.area === 108) { + Pather.moveTo(diax, diay, 3); + if ((killDiablo.includes(me.name) + || leechDiablo.includes(me.name) + || weakenDiablo.includes(me.name)) + && me.name !== shrineDiablo) { + Pather.makePortal(); + } else { + Pather.makePortal(true); + } + } + if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { + this.delays("area", 30, shrineDiablo, "in", 108); + } else if (killDiablo.length > 0) { + this.delays("area", 30, killDiablo, "in", 108); + } + } + + if (shrineDiablo === me.name) { + let finder; + for (let i = 1; i <= chars.length; i += 2) { + if (chars[i].includes("findshrine")) { + finder = chars[i - 1]; + } + } + + !finder && (finder = null); + if (finder === me.name || (finder === null && me.sorceress)) { + this.runTask("findshrine"); + } + console.log(finder); + this.delays("shrine", shrineDelay, finder); + } + if (killDiablo.indexOf(me.name) > -1) { + if (me.area !== 108) { + Town.goToTown(4); + Town.move("portalspot"); + if (!taxiChar || !Pather.usePortal(108, taxiChar)) { + let portal = getUnit(2, "portal"); + if (portal) { + do { + if (portal.objtype === 108 + && portal.getParent() !== me.name + && Misc.inMyParty(portal.getParent()) + && chars.indexOf(portal.getParent()) > -1) { + if (Pather.usePortal(null, null, portal)) break; + } + } while (portal.getNext()); + } + } + } + if (me.area !== 108) Pather.usePortal(108, null); + if (me.area === 108) { + Pather.moveTo(diax, diay, 3); + Pather.makePortal(); + if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { + this.delays("area", 60, shrineDiablo, "in", 108); //60 was 30 + } + this.killBoss("Diablo"); + } + } else if (leechDiablo.indexOf(me.name) > -1) { + if (me.area !== 108) { + Town.goToTown(4); + Town.move("portalspot"); + if (taxiChar) { + if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); + } else Pather.usePortal(108, null); + } + if (me.area === 108) { + (leechDiablo.indexOf(me.name) % 2 === 0) + ? Pather.moveTo(7792, 5291, 3) + : Pather.moveTo(7792, 5291, 3); + for (let i = 0; i < 200; i++) { + if (!getUnit(1, 243)) { + delay(100); + } + } + while (getUnit(1, "Diablo") && getUnit(1, "Diablo").hp > 0) delay(100); + Pickit.pickItems(); + } + } else if (weakenDiablo.indexOf(me.name) > -1) { + if (me.area !== 108) { + Town.goToTown(4); + Town.move("portalspot"); + if (taxiChar) { + if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); + Pather.makePortal(); + } else Pather.usePortal(108, null); + } + if (me.area === 108) { + if (!diax) this.initLayout(); + Pather.moveTo(diax, diay, 3); + let waitfor = killDiablo.concat(leechDiablo); + console.log(waitfor); + loop: + for (let i = 0; i < 300; i++) { + for (let j = 0; j < waitfor.length; j++) { + delay(100); + if (Misc.inMyParty(waitfor[j]) && !getUnit(0, waitfor[j])) break; + else if ((!Misc.inMyParty(waitfor[j]) || getUnit(0, waitfor[j])) && j === waitfor.length - 1) break loop; + } + } + Config.PublicMode = 0; + delay(2 * me.ping + 100); + let player = getParty(); + clickParty(player, 3); + for (let i = 0; i < 200; i++) { + if (!getUnit(1, 243)) { + delay(100); + } + } + while (getUnit(1, 243) && getUnit(1, 243).hp > 0) { + switch (me.classid) { + case 0: //Amazon + break; + case 1: //Sorceress + if (Skill.cast(42, 0)) Skill.cast(42, 0); //static + break; + case 2: //Necromancer + if (Skill.cast(Config.Curse[0], 0)) Skill.cast(Config.Curse[0], 0); //spam boss curse + //else if other curses + break; + case 3: //Paladin + if (Skill.setSkill(123, 0)) Skill.setSkill(123, 0); //conviction aura + break; + case 4: //Barbarian + break; + } + delay(100); + } + Pickit.pickItems(); + } + } else { + if (me.area === 108) { + if (clearDirection === 0) Pather.moveTo(7825, 5294, 3); + if (clearDirection === 1) Pather.moveTo(7765, 5294, 3); + } + Town.goToTown(); + } + taxiChar = false; + last = "Diablo"; + return true; + }; + + this.getLeg = function () { + if (Pather.getPortal(sdk.areas.MooMooFarm)) return false; + /** @type {ItemUnit} */ + let leg; + let gid; + let wrongLeg; // this isn't doing anything + + while (!leg) { + if (me.getItem(sdk.quest.item.WirtsLeg)) { + leg = me.getItem(sdk.quest.item.WirtsLeg); + return me.getItem(-1, -1, leg.gid); + } + + leg = Game.getItem(sdk.quest.item.WirtsLeg); + if (leg) { + do { + if (leg.name.indexOf("ÿc1") > -1) { + wrongLeg = true; + + return false; + } else { + gid = leg.gid; + Pickit.pickItem(leg); + + return me.getItem(-1, -1, gid); + } + } while (leg.getNext()); + } + + if (chars[chars.indexOf(me.name) + 1].indexOf("getleg") > -1) { + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); + + /** @type {ObjectUnit} */ + let portal; + for (let i = 0; i < 6; i += 1) { + portal = Pather.getPortal(sdk.areas.Tristram); + if (portal) { + Pather.usePortal(null, null, portal); + break; + } + delay(500); + } + + if (!portal) { + Town.goToTown(); + return false; + } + + Pather.moveTo(25048, 5177); + let wirt = Game.getObject(sdk.quest.chest.Wirt); + for (let i = 0; i < 8; i += 1) { + !!wirt && wirt.interact(); + delay(500); + leg = Game.getItem(sdk.quest.item.WirtsLeg); + + if (leg) { + gid = leg.gid; + Pickit.pickItem(leg); + Town.goToTown(1); + + if (chars[chars.indexOf(me.name) + 1].indexOf("makecows") === -1) { + Town.move("stash"); + leg.drop(); + return true; + } else { + return me.getItem(-1, -1, gid); + } + } + } + Town.goToTown(); + } else { + Town.move("stash"); + } + delay(100); + } + return false; + }; + + this.getTome = function () { + let myTome = me.findItem("tbk", 0, 3); + let tome = me.getItem("tbk"); + if (tome) { + do { + if (!myTome || tome.gid !== myTome.gid) { + return copyUnit(tome); + } + } while (tome.getNext()); + } + + Town.move(NPC.Akara); + let akara = Town.initNPC("Shop"); + if (!akara) return false; + + tome = akara.getItem("tbk"); + if (tome.buy()) { + tome = me.getItem("tbk"); + if (tome) { + do { + if (!myTome || tome.gid !== myTome.gid) { + return copyUnit(tome); + } + } while (tome.getNext()); + } + } + return false; + }; + + this.openPortal = function () { + Town.goToTown(1); + let leg = this.getLeg(); + if (!leg) return false; + + let tome = this.getTome(); + if (!tome) return false; + + if (!Town.openStash() + || !Cubing.emptyCube() + || !Storage.Cube.MoveTo(leg) + || !Storage.Cube.MoveTo(tome) + || !Cubing.openCube()) { + return false; + } + transmute(); + delay(500); + for (let i = 0; i < 10; i += 1) { + if (Pather.getPortal(39)) { + return true; + } + delay(200); + } + me.cancel(); + return false; + }; + + this.rideTaxi = function() { + let tTimer = getTickCount(); + while (true) { + if (!taxiChar || !Misc.inMyParty(taxiChar)) { + break; + } + + if (this.getAct(taxiChar) > 0) { + Town.goToTown(this.getAct(taxiChar)); + Town.move("portalspot"); + } + + if (cmd && cmd !== last) { + let currCmd = cmd; + if (currCmd === "Diablo") { + this.diablo(); + } else if (teleMsg.includes(currCmd)) { + if (clearChars.includes(me.name)) { + this.delays("chaos", chaosDelay, taxiChar); + this.clearChaos(); + } + taxiChar = false; + + break; + } else if (Pather.getPortal(null, taxiChar)) { + if (Pather.usePortal(null, taxiChar)) { + if (currCmd === cmd) { + this.killBoss(currCmd); + } + Town.goToTown(); + } + } + last = currCmd; + tTimer = getTickCount(); + } + + if (getTickCount > tTimer + taxiDelay * 1e3) { + taxiChar = false; + break; + } + Precast.doPrecast(false); + delay(100); + } + }; + + this.Shopbot = function () { + Loader.runScript("ShopBot"); + }; + + /** + * @param {string | number} name + * @returns {boolean} + */ + this.Urdars = function (name) { + let room = getRoom(); + if (!room) return false; + + const urdars = [189, 309, 361, 311, 675, 300, 692]; + const rooms = []; + /** @param {Monster} monster */ + const check = function (monster) { + return ( + urdars.includes(monster.classid) + && monster.attackable + && monster.spectype !== sdk.monsters.spectype.All + && monster.spectype !== sdk.monsters.spectype.Minion + && 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 = []; + let monster = Game.getMonster(name); + + if (monster) { + do { + if (check(monster)) { + monList.push(copyUnit(monster)); + + if (me.name === taxiChar) { + Pather.moveTo(monster.x + rand(-5, 5), monster.y + rand(-5, 5), 0); + Pather.makePortal(); + say(monster.name); + } + + if (monster && monster.hp > 0) { + if (me.name === taxiChar) { + for (let i = 0; i < 30; i++) { + if (monster) Attack.clear(getDistance(me, monster) + 5, 0xF, monster); + if (!monster || monster.hp === 0) break; + delay(100); + } + } else if (monster) { + if (monster.spectype === sdk.monsters.spectype.All) { + Attack.clear(getDistance(me, monster) + 5, 0, monster); + } else { + Attack.clear(getDistance(me, monster) + 5, 0xF, monster); + } + } + } + } + } while (monster.getNext()); + } + + if (!Attack.clearList(monList)) { + return false; + } + } + } + + return true; + }; + + this.precast = function () { + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Barbarian: + return false; + case sdk.player.class.Sorceress: //Sorceress + return ( + Skill.cast(sdk.skills.Blizzard) + || Skill.cast(sdk.skills.Meteor) + || Skill.cast(sdk.skills.StaticField) + || Skill.cast(sdk.skills.FrostNova) + || Skill.cast(sdk.skills.Hydra) + || Skill.cast(sdk.skills.Nova) + ); + case sdk.player.class.Necromancer: + return Skill.cast(Config.Curse[1], 0); + case sdk.player.class.Paladin: + return ( + Skill.setSkill(sdk.skills.Concentration, 0) + && Skill.cast(sdk.skills.BlessedHammer) + ); + } + return false; + }; + + /** + * @param {string} nick + * @param {string} msg + */ + function ChatEvent(nick, msg) { + if (!taxiChar && msg === "taxi" && chars.includes(nick)) { + taxiChar = nick; + } else if (nick === taxiChar && msg === "ixat" && cmd !== "Diablo") { + taxiChar = false; + } else if (nick === taxiChar && nick !== me.name) { + cmd = msg; + } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick) && clearChars.indexOf(me.name)) { + cmd = "Diablo"; + } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick)) { + if (leechDiablo.includes(me.name)) { + taxiChar = nick; + } + this.diablo(); + } + } + + // START + let listen = false; + for (let i = 0; i < chars[chars.indexOf(me.name) + 1].length; i++) { + if (chars[chars.indexOf(me.name) + 1][i].indexOf("ride") > -1) { + listen = true; + } + } + if (!listen && ( + killDiablo.includes(me.name) + || leechDiablo.includes(me.name) + || weakenDiablo.includes(me.name) + || clearChars.includes(me.name) + )) { + listen = true; + } + if (listen) { + addEventListener("chatmsg", ChatEvent); + } + Pickit.pickItems(); + Town.heal(); + this.runList(chars[chars.indexOf(me.name) + 1]); + if (killDiablo.indexOf(me.name) === -1) { + for (let i = 0; i < killDiablo.length; i++) { + while (Misc.inMyParty(killDiablo[i])) { + if (me.sorceress && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) { + this.delays("givechant", 1); + } else { + delay(1000); + } + } + } + } + + return true; +} diff --git a/d2bs/kolbot/libs/scripts/rungamex.txt b/d2bs/kolbot/libs/scripts/rungamex.txt new file mode 100644 index 000000000..bf4cedfd7 --- /dev/null +++ b/d2bs/kolbot/libs/scripts/rungamex.txt @@ -0,0 +1,1781 @@ +/** +* @filename rungame2.js +* @author darjkeel@d2jsp +* @desc one script to run them all, and in the dankness bind them +* @readme rungame2 readme.txt +**/ + +function rungamex() { + let boWP = 83, //Default bo wp + chars = [ //Define chars and list their bosses/tasks + "Sorceress", + [ + "chores", + "getbo 83", + "Sszark the Burning", + "goto 102", + "areadelay in 75 Parzivol PLA", + "taxi on", + "areadelay in 75 Parzivol PLA", + "Wyand Voidbringer", + "areadelay in 75 Parzivol PLA", + "Bremm Sparkfist", + "areadelay in 75 Parzivol PLA", + "Maffer Dragonhand", + "goto 105", + "areadelay in 103 Parzivol PLA", + "Izual", + "Hephasto the Armorer", + "taxi off", + "chaos", + "areadelay in 108 Parzivol PLA", + "getleg", + "makecows", + "chests on", + "Witch Doctor Endugu", + "Icehawk Riftwing", + "chests off", + "findshrine", + "getbo 108", + "Urdars", + "chores" + ], + "QuestMF", + [ + "chores", + "getbo 83", + "Andariel", + "chests on", + "Mephisto", + "chests off", + "Travincal", + "Fire Eye", + "The Summoner", + "Duriel", + "The Cow King", + "Beetleburst", + "Dark Elder", + "Tunnels", + "chores" + ], + "PLA", + [ + "chores", + "getbo 83", + "Travincal", + "chores", + "areadelay in 102 CCA", + "ride CCA", + "chaos", + "getbo 108", + "ride CCA", + "chores" + ], + "Parzivol", + [ + "chores", + "givebo 83 QuestMF PLA CCA", + "Travincal", + "goto 75", + "areadelay in 102 CCA", + "ride CCA", + "chaos", + "givebo 108 PLA CCA", + "ride CCA", + "chores" + ] + ], + + //Chaos options, only apply to walky cs "chaos" + tele = "star", //Where to tp when running chaos. "gate" or "star" + teleMsg = [""], //Message(s) after tp to cs. If multiple, picks one at random + openSeals = [3], //Tele to and open seals after tp. 1 = infector main, 2 = infector other, 3 = seis, 4 = vizier other, 5 = vizier main + sealMsg = [], //Message(s) after pre-popping seals. If multiple, picks one at random + clearChars = ["PLA", "Parzivol"], //Name of chars who will clear chaos + clearMode = 0, //Clear mode = 0 = all, 0xF = skip normal, 0x7 = just champion/uniques (no minions) + clearDirection = 1, //0 for clockwise Vizi->Seis->Fect. 1 for counterclockwise Fect->Seis->Vizi + //Diablo options, apply to either walk cs "chaos" or taxi cs "Infector of Souls","Lord de Seis","Grand Vizier of Chaos","Diablo" + killDiablo = ["PLA"], //Names of chars who will kill Diablo. Leave out your Baron(ess) char(s) for best drops + leechDiablo = ["Parzivol"], //Names of chars who will leech d exp but not kill + weakenDiablo = [], //Names of chars who will leave party and cast weakening spells (static/curse/aura) but not kill + shrineDiablo = "Parzivol", //Name a char who will use experience shrine at diablo + //Delay options for good happy timings + boDelay = 180, //Max time to wait giving or getting bo + boQuit = false, //Quits if no bo given or received in time + taxiDelay = 120, //Max time to wait for a taxi tp + taxiQuit = false, //Quit if no taxi tp in time + chaosDelay = 180, //Max time to wait for a chaos tp + chaosQuit = false, //Quit if no cs tp found in time + shrineDelay = 40, //Max time to wait for a tp to shrine in useshrine + finderDelay = 0, //Make shrine finder wait for user to take + areaDelay = 30, //Max time to wait for other chars in areadelay + //END OF USER CONFIGS BEWARE NO TOUCHY BELOW + Bosses = [ + "The Cow King", + "Corpsefire", + "Bishibosh", + "Blood Raven", + "Bonebreaker", + "Coldcrow", + "Rakanishu", + "Griswold", + "Treehead Woodfist", + "The Countess", + "The Smith", + "Pitspawn Fouldog", + "Bone Ash", + "Andariel", + "Radament", + "Creeping Feature", + "Bloodwitch the Wild", + "Beetleburst", + "Coldworm the Burrower", + "Dark Elder", + "Fangskin", + "Fire Eye", + "The Summoner", + "Ancient Kaa the Soulless", + "Duriel", + "Sszark the Burning", + "Witch Doctor Endugu", + "Stormtree", + "Battlemaid Sarina", + "Icehawk Riftwing", + "Travincal", + "Bremm Sparkfist", + "Wyand Voidbringer", + "Maffer Dragonhand", + "Mephisto", + "Izual", + "Hephasto the Armorer", + "Infector of Souls", + "Lord de Seis", + "Grand Vizier of Chaos", + "Diablo" + ], + Tasks = [ + "chores", + "givebo", + "getbo", + "taxi", + "ride", + "chaos", + "getleg", + "makecows", + "clearcows", + "findshrine", + "useshrine", + "chests", + "goto", + "areadelay", + "Shopbot", + "Urdars", + "Tunnels" + ], + wps = [ + 1, 3, 3, + 3, 3, 3, + 4, 4, 5, + 6, 27, 29, + 32, 35, 48, + 42, 57, 43, + 43, 44, 44, + 74, 74, 46, + 46, 76, 78, + 79, 80, 81, + 83, 101, 101, + 101, 101, 106, + 106, 107, 107, + 107, 107, 106 + ], + exits = [ + [0], + [2, 8], + [0], + [17], + [17, 18], + [9], + [38], + [0], + [0], + [20, 21, 22, 23, 24, 25], + [28], + [30], + [33], + [36, 37], + [49], + [41, 55, 59], + [60], + [0], + [62, 63, 64], + [0], + [45, 58, 61], + [0], + [0], + [0], + [0], + [85], + [88, 89, 91], + [78], + [94], + [92], + [0], + [102], + [102], + [102], + [102], + [105], + [107], + [108], + [108], + [108], + [108] + ], + presets = [ + 773, + 774, + 734, + 805, + 735, + 736, + 737, + 739, + 738, + 740, + 754, + 741, + 743, + 156, + 744, + 748, + 745, + 747, + 749, + 751, + 746, + 750, + 250, + 753, + 211, + 755, + 756, + false, + 758, + 759, + false, + 762, + 764, + 765, + 242, + 256, + 775, + false, + false, + false, + false + ], + vizLayout, + seisLayout, + infLayout, + vizx, + vizy, + seisx, + seisy, + infx, + infy, + diax, + diay, + cmd, + last, + taxiChar = false, + chests = false, + runString, + runDone; + + /** + * @param {string | number} name + * @returns {boolean | number} + */ + this.find = function (name) { + let unit = Game.getPlayer(name); + if (unit) return unit.area; + + let party = getParty(); + if (!party) return false; + + do { + if (party.name === name) { + return (party.area > 0) ? party.area : true; + } + } while (party.getNext()); + return false; + }; + + /** + * @param {string | number} name + * @returns {Act} + */ + this.getAct = function(name) { + let _area = this.find(name); + return _area === true + ? me.act + : sdk.areas.actOf(_area); + }; + + /** + * @param {number} id + * @returns {boolean} + */ + this.openSeal = function (id) { + let seal = Game.getObject(id); + if (seal && seal.mode) return false; + Pather.moveToPreset(108, 2, id, 4, 4); + seal = Game.getObject(id); + if (!seal || (seal && seal.mode)) return false; + let retryCount = 0; + + while (!seal.mode) { + if (!Skill.cast(43, 0, seal) || retryCount > 50) seal.interact(); + delay(100); + if (seal.mode === 0) { + if (retryCount % 5 === 0) { + Pather.moveTo(seal.x + rand(-5, 5), seal.y + rand(-5, 5)); + } + delay(400); + retryCount++; + } + if (retryCount > 100) { + D2Bot.printToConsole("OPEN SEAL " + id + " FAILED in game: " + me.gamename, 9); + return false; + } + if (cmd === "Diablo") { + break; + } + } + return true; + }; + + this.getLayout = function(seal, value) { + let sealPreset = getPresetUnit(108, 2, seal); + if (!seal) { + throw new Error("Seal preset not found."); + } + if (sealPreset.roomy * 5 + sealPreset.y === value || sealPreset.roomx * 5 + sealPreset.x === value) { + return 1; + } + return 2; + }; + + this.initLayout = function() { + vizLayout = getLayout(396, 5275); // 1 = "Y", 2 = "L" + if (vizLayout === 1) vizx = 7680, vizy = 5295; // Y (7680,5295) L (7685,5320) + if (vizLayout === 2) vizx = 7685, vizy = 5320; + seisLayout = getLayout(394, 7773); // 1 = "2", 2 = "5" + if (seisLayout === 1) seisx = 7775, seisy = 5200; // 2 (7775,5220) 5 (7780,5185) + if (seisLayout === 2) seisx = 7780, seisy = 5175; + infLayout = getLayout(392, 7893); // 1 = "I", 2 = "J" + if (infLayout === 1) infx = 7910, infy = 5295; // I (7910,5295) J (7295,5280) + if (infLayout === 2) infx = 7925, infy = 5280; + diax = 7795, diay = 5295; + }; + + /** + * @param {string[]} char_str + */ + this.bo = function(char_str) { + let unit; + if (char_str) { + Precast.doPrecast(true); + for (let i = 0; i < char_str.length; i++) { + unit = getUnit(0, char_str[i]); + if (unit + && unit.name !== me.name + && !unit.dead + && Misc.inMyParty(unit.name) + && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) + && getDistance(me, unit) <= 20) { + Precast.doPrecast(true); + } + } + } else { + unit = getUnit(0); + if (unit) { + do { + if (unit.name !== me.name + && !unit.dead + && Misc.inMyParty(unit.name) + && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) + && getDistance(me, unit) <= 20) { + Precast.doPrecast(true); + } + } while (unit.getNext()); + } + } + }; + + this.chant = function(char_str) { + let unit; + if (char_str) { + for (let i = 0; i < char_str.length; i++) { + unit = getUnit(0, char_str[i]); + if (unit && !unit.getState(16) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(250); + + } + unit = getUnit(1); + if (unit) { + do { + if (unit.getParent() && char_str.indexOf(unit.getParent().name) > -1 && !unit.getState(16) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(500); + } + } while (unit.getNext()); + } + } + } else { + unit = getUnit(0); + if (unit) { + do { + if (!unit.getState(16) && Misc.inMyParty(unit.name) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(250); + } + } while (unit.getNext()); + + } + unit = getUnit(1); + if (unit) { + do { + if (unit.getParent() && !unit.getState(16) && getDistance(me, unit) <= 40) { + Skill.setSkill(52, 0); + sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); + delay(500); + } + } while (unit.getNext()); + } + } + }; + + this.pop = function() { + let monsterList = [], + monster = getUnit(1); + if (monster) { + do { + if ([345, 346, 347].indexOf(monster.classid) > -1 || (monster.spectype !== 0 && monster.spectype !== 8)) { + if (getDistance(me, monster) <= 30) monsterList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + for (let i = 0; i < monsterList.length; i++) { + if ((monsterList[i].mode === 0 || monsterList[i].mode === 12) && !monsterList[i].getState(1) && !monsterList[i].getState(96) && !monsterList[i].getState(99) && !monsterList[i].getState(104) && !monsterList[i].getState(107) && !monsterList[i].getState(118)) { + console.log("popping " + monsterList[i].name + " spectype: " + monsterList[i].spectype + " classid: " + monsterList[i].classid); + Pather.moveTo(monsterList[i].x, monsterList[i].y, 3); + ClassAttack.findItem(10); + } + } + }; + + this.chests = function() { + let preset = getPresetUnits(me.area, 2), + coords = []; + while (preset.length > 0) { + if ([5, 6, 87, 88, 139, 147, 148, 177, 329, 330, 333, 371, 387, 389, 397, 424, 425, 454, 455, 580, 581].indexOf(preset[0].id) > -1) { + coords.push({ + x: preset[0].roomx * 5 + preset[0].x, + y: preset[0].roomy * 5 + preset[0].y + }); + } + preset.shift(); + } + while (coords.length) { + coords.sort(Sort.units); + Pather.moveToUnit(coords[0], 1, 2); + Misc.openChests(5); + coords.shift(); + } + }; + + this.delays = function(mode, delay_, char_str, mode2, aid) { + let tTimer = getTickCount(); + switch (mode) { + case "givebo": + for (let i = 0; i < char_str.length; i++) { + if (getTickCount() >= tTimer + delay_ * 1e3 - 5000 && i < char_str.length) { + D2Bot.printToConsole("give bo fail", 9); + if (boQuit) quit(); + break; + } + if (getUnit(0, char_str[i])) i++; + delay(100); + } + this.bo(char_str); + break; + case "getbo": + while (!Misc.inMyParty(char_str) || !getUnit(0, char_str, 10)) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("get bo fail 1", 9); + if (boQuit) quit(); + break; + } + delay(100); + } + while (getTickCount() <= tTimer + delay_ * 1e3 && (!me.getState(26) || !me.getState(32) || !me.getState(51))) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("get bo fail 2", 9); + if (boQuit) quit(); + break; + } + delay(100); + } + if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.chant(); + else { + for (let i = 0; i < 10; i++) { + let brk = true; + for (let j = 0; j < 5; j++) { + delay(100); + if (getUnit(0, char_str, 10)) brk = false; + } + if (brk) break; + } + } + break; + case "givechant": + { + let i = 0; + while (getTickCount() <= tTimer + delay_ * 1e3) { + if (getTickCount() >= tTimer + delay_ * 1e3) break; + if (char_str && char_str.length > 0 && getUnit(0, char_str[i])) { + this.chant(char_str[i]); + i++; + } else this.chant(); + delay(100); + } + } + break; + case "ride": + while (!Misc.inMyParty(char_str) || !taxiChar) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("wait for taxi fail", 9); + if (taxiQuit) quit(); + break; + } + delay(100); + } + break; + case "chaos": + if (getUnit(2, "waypoint")) Pather.useWaypoint(103, true); + else Town.goToTown(4); + Town.move("portalspot"); + while (me.area !== 108 && !Pather.usePortal(108, char_str)) { + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("wait for chaos fail", 9); + if (taxiQuit) quit(); + break; + } + delay(100); + } + break; + case "shrine": + Town.goToTown(1); + Town.move("portalspot"); + while (!me.getState(137)) { + if (char_str != null && !Misc.inMyParty(char_str)) break; + if (getTickCount() >= tTimer + delay_ * 1e3) { + D2Bot.printToConsole("wait for shrine fail", 9); + if (taxiQuit) quit(); + break; + } + + if (Pather.usePortal(null, char_str)) { + let shrine = getUnit(2, "shrine"); + if (shrine) { + do { + if (shrine.objtype === 15 && shrine.mode === 0) { + Pather.moveTo(shrine.x - 2, shrine.y - 2); + Misc.getShrine(shrine); + Pather.makePortal(); + if (me.getState(137)) break; + } + } while (shrine.getNext()); + } + if (!char_str || !Pather.usePortal(1, char_str)) Town.goToTown(1); + break; + } + delay(100); + } + break; + case "finder": + loop1: + while (getTickCount() <= tTimer + delay_ * 1e3) { + let unit = getUnit(0); + if (unit) { + do { + if (unit.getState(137)) break loop1; + } while (unit.getNext()); + } + delay(100); + } + break; + case "area": + let chars_ = []; + let pp = getParty(); //big pp variable name come at me bro + if (pp) { + do { + if (!char_str) { + if (pp.name !== me.name) chars_.push(pp.name); + } else if (char_str) { + if (char_str.indexOf(pp.name) > -1 && pp.name !== me.name) chars_.push(pp.name); + } + } while (pp.getNext()); + } + loop2: + while (getTickCount() <= tTimer + delay_ * 1e3) { + delay(100); + if (mode2 === "in") { + for (let i = 0; i < chars_.length; i++) { + if (!char_str && this.find(chars_[i]) === aid) break loop2; + else if (char_str && this.find(chars_[i]) !== aid) break; + else if (char_str && i === chars_.length - 1) break loop2; + } + } else if (mode2 === "out") { + for (let i = 0; i < chars_.length; i++) { + if (this.find(chars_[i]) === aid) break; + else if (i === chars_.length - 1) break loop2; + } + } + } + break; + } + }; + + this.killBoss = function(name) { + if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); + if (me.name === taxiChar + && name !== "Grand Vizier of Chaos" + && name !== "Lord de Seis" + && name !== "Infector of Souls" + && name !== "Diablo") { + if (getUnit(1, name) && getDistance(getUnit(1, name)) > 10 && me.area !== 108) Pather.moveTo(getUnit(1, name).x + rand(-5, 5), getUnit(1, name).y + rand(-5, 5), 0); + Pather.makePortal(); + say(name); + } + if (name === "Grand Vizier of Chaos" + || name === "Lord de Seis" + || name === "Infector of Souls" + || name === "Diablo") { + if (!diax) this.initLayout(); + let sealTimer = getTickCount(); + + for (let i = 0; getTickCount() <= sealTimer + 20 * 1e3; i++) { + if (!getUnit(1, name)) { + if (!this.precast()) Attack.clear(10); + if (name === "Grand Vizier of Chaos") Pather.moveTo(vizx, vizy); + if (name === "Infector of Souls") Pather.moveTo(infx, infy); + if (name === "Lord de Seis") Pather.moveTo(seisx, seisy); + if (name === "Diablo") Pather.moveTo(diax, diay); + delay(10); + } else if (i >= 2000) break; + else break; + } + } + if (getUnit(1, name) && getUnit(1, name).hp > 0) { + if (me.name === taxiChar) { + for (let i = 0; i < 30; i++) { + let mon = getUnit(1, name); + if (getUnit(1, name)) Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); + if (!mon || mon.hp === 0) break; + delay(100); + } + } else if (getUnit(1, name)) { + if (getUnit(1, name).spectype === 0) { + Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0, name); + } else { + Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); + } + } + } + if (name === "Travincal") { + let monsterList = []; + let monster = Game.getMonster(); + if (monster) { + do { + if ([345, 346, 347].includes(monster.classid) && monster.attackable) { + monsterList.push(copyUnit(monster)); + } + } while (monster.getNext()); + } + Attack.clearList(monsterList); + } + if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); + if (me.classid === 4 && Config.FindItem) this.pop(); + if (name === "Andariel") delay(2000); + Pickit.pickItems(); + return true; + }; + + this.runBoss = function (name, wp, exit, preset) { + console.log(name); + if (me.area !== wp && exit.indexOf(me.area) === -1) { + let wpunit = getUnit(2, "waypoint"); + if (!wpunit) Town.goToTown(); + Pather.useWaypoint(wp, true); + Precast.doPrecast(true); + } + if (!this.bossSpecial(name, 1)) return false; + if (exit.indexOf(me.area) > 0 && me.area !== exit[exit.length - 1]) { + let i = []; + for (let j = exit.indexOf(me.area); j < exit.length; j++) i.push(exit[j]); + exit = i; + } + if (exit[0] !== 0 && me.area !== exit[exit.length - 1]) Pather.moveToExit(exit, true); + if (!this.bossSpecial(name, 2)) return false; + if (preset && getPresetUnit(me.area, 1, preset)) Pather.moveToPreset(me.area, 1, preset); + if (this.bossSpecial(name, 3)) this.killBoss(name); + if (!this.bossSpecial(name, 4)) return false; + return true; + }; + + this.bossSpecial = function (name, t) { + if (t === 1) { + if (name === "The Cow King") { + Town.move("stash"); + for (let i = 0; i < 30; i++) { + if (Pather.usePortal(39)) break; + delay(100); + } + if (!Pather.usePortal(39)) return false; + else return true; + } + if (chests && ["Stormtree", "Battlemaid Sarina", "Icehawk Riftwing"].indexOf(name) > -1) { + this.chests(); + Pickit.pickItems(); + return true; + } + return true; + } + if (t === 2) { + if (chests && name === "Icehawk Riftwing") { + this.chests(); + } + if (name === "Griswold") { + Pather.moveToPreset(me.area, 1, 737); + while (!getUnit(2, 60)) delay(10); + Pather.usePortal(38); + return true; + } + if (name === "Fire Eye") { + Pather.usePortal(null); + return true; + } + if (name === "Ancient Kaa the Soulless") { + let j, k, pre, kaa = false; + for (j = 66; kaa === false; j++) { + if (me.area !== 46) Pather.moveToExit(46, true); + Pather.moveToExit(j, true); + pre = getPresetUnits(me.area, 1); + for (k = 0; k < pre.length; k++) { + if (pre[k].id === 753) kaa = true; + } + } + return true; + } + if (name === "Duriel") { + Pather.moveToExit(getRoom().correcttomb, true); + Pather.moveToPreset(me.area, 2, 152, -3, -3); + while (!getUnit(2, 100)) delay(10); + if (me.classid === 1 && me.getSkill(43, 1)) { + for (let i = 0; i < 10; i += 1) { + if (me.area === 73) { + break; + } + Skill.cast(43, 0, getUnit(2, 100)); + } + } else Pather.useUnit(2, 100, 73); + return true; + } + if (name === "Travincal") { + Pather.moveTo(me.x + 101, me.y - 71); + return true; + } + if (name === "Grand Vizier of Chaos") { + if (!vizx) this.initLayout(); + if (me.name === taxiChar) { + Pather.moveTo(vizx, vizy, 3); + Pather.makePortal(); + say(name); + } + this.openSeal(396); + Pather.moveTo(vizx, vizy, 3); + return true; + } + if (name === "Lord de Seis") { + if (!seisx) this.initLayout(); + if (me.name === taxiChar) { + Pather.moveTo(seisx, seisy, 3); + Pather.makePortal(); + say(name); + } + this.openSeal(394); + Pather.moveTo(seisx, seisy, 3); + return true; + } + if (name === "Infector of Souls") { + if (!infx) this.initLayout(); + if (me.name === taxiChar) { + Pather.moveTo(infx, infy, 3); + Pather.makePortal(); + say(name); + } + this.openSeal(392); + Pather.moveTo(infx, infy, 3); + return true; + } + if (name === "Diablo") { + if (!diax) this.initLayout(); + Pather.moveTo(diax, diay, 3); + return true; + } + return true; + } + if (t === 3) { + if (name === "Diablo") { + if (me.name === taxiChar) { + //cmd = "Diablo"; + say("Diablo"); + this.diablo(); + return false; + } else return true; + } + return true; + } + if (t === 4) { + if (name === "Fire Eye") { + Pather.moveTo(10073, 8668); + Pather.usePortal(null); + return true; + } + if (name === "The Summoner") { + let journal = getUnit(2, 357); + if (journal) { + Pather.moveTo(journal.x, journal.y); + for (let i = 0; i < 5; i += 1) { + journal.interact(); + delay(me.ping + 500); + me.cancel(); + delay(me.ping + 500); + if (Pather.getPortal(46)) break; + } + Pather.usePortal(46); + } + return true; + } + if (chests && ["Bonebreaker", "Coldcrow", "Countess", "Creeping Feature", "Coldworm the Burrower", "Dark Elder", "Icehawk Riftwing", "Mephisto"].indexOf(name) > -1) { + if (name === "Coldcrow") Pather.moveToExit(13, true); + if (name === "Dark Elder") Pather.moveToExit(65, true); + if (name === "Icehawk Riftwing") Pather.moveToExit(93, true); + this.chests(); + Pickit.pickItems(); + return true; + } + if (name === "Grand Vizier of Chaos") { + this.openSeal(395); + return true; + } + if (name === "Infector of Souls") { + this.openSeal(393); + return true; + } + return true; + } + return false; + }; + + this.runList = function (list) { + runString = []; + for (let i = 0; i < list.length; i++) { + if (Bosses.indexOf(list[i]) > -1) { + if (runString.indexOf(list[i]) === -1) runString.push(list[i]); + else D2Bot.printToConsole("boss '" + list[i] + "' listed more than once", 9); + } else { + for (let j = 0; j < Tasks.length; j++) { + if (list[i].indexOf(Tasks[j]) === 0) runString.push(list[i]); + } + if (runString.indexOf(list[i]) === -1) D2Bot.printToConsole("boss/task string '" + list[i] + "' not recognized and ignored", 9); + } + } + runDone = []; + for (let i = 0; runDone.length < runString.length; i++) { + if (Bosses.indexOf(runString[i]) > -1) { + let loc = Bosses.indexOf(runString[i]); + if (runDone.indexOf(runString[i]) === -1) { + this.runBoss(Bosses[loc], wps[loc], exits[loc], presets[loc]); + runDone.push(runString[i]); + } + } else { + for (let j = 0; j < Tasks.length; j++) { + if (runString[i].indexOf(Tasks[j]) === 0) { + this.runTask(runString[i]); + runDone.push(runString[i]); + } + } + } + } + return true; + }; + + this.runTask = function (task) { + console.log(task); + let wp; + if (task.indexOf("givebo") === 0) { + let boChars = []; + if (task === "givebo") { + wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf(" ") === -1) { + wp = Number(task); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + wp = Number(task.slice(0, task.indexOf(" "))); + for (let i = 0; i < chars.length; i += 2) { + if (task.indexOf(chars[i]) > -1) boChars.push(chars[i]); + } + } + if (isNaN(wp)) wp = boWP; + } + if (!wp || boChars.length === 0) { + D2Bot.printToConsole("givebo string error", 9); + return false; + } + Town.goToTown(); + Pather.useWaypoint(wp, true); + this.delays("givebo", boDelay, boChars); + } else if (task.indexOf("getbo") === 0) { + let boBarb; + if (task === "getbo") { + wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; + } + } + } else if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf(" ") === -1) { + wp = Number(task); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; + } + } + } + if (task.indexOf(" ") > -1) { + wp = Number(task.slice(0, task.indexOf(" "))); + boBarb = task.slice(task.indexOf(" ") + 1); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + if (task.indexOf(chars[i]) > -1) boBarb = chars[i - 1]; + } + } + } + if (!wp || !boBarb) { + D2Bot.printToConsole("getbo string error", 9); + return false; + } + Town.goToTown(); + Pather.useWaypoint(wp, true); + Pather.moveTo(me.x - 5, me.y - 5, 3); + this.delays("getbo", boDelay, boBarb); + } else if (task.indexOf("chant") === 0 || task.indexOf("givechant") === 0) { + let chantChars = []; + if (task === "chant" || task === "givechant") { + wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf(" ") === -1) { + wp = Number(task); + if (isNaN(wp)) wp = boWP; + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); + } + } + } else if (task.indexOf(" ") > -1) { + wp = Number(task.slice(0, task.indexOf(" "))); + for (let i = 0; i < chars.length; i += 2) { + if (task.indexOf(chars[i]) > -1) chantChars.push(chars[i]); + } + } + if (isNaN(wp)) wp = boWP; + } + if (!wp || chantChars.length === 0) { + D2Bot.printToConsole("givechant string error", 9); + return false; + } + if (me.area !== wp) Pather.useWaypoint(wp, true); + this.delays("givechant", chantDelay, chantChars); + } else if (task.indexOf("taxi") === 0) { + if (task === "taxi") taxiChar = me.name; + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task === "on") { + taxiChar = me.name; + say("taxi"); + } + if (task === "off") { + taxiChar = false; + say("ixat"); + } + } + } else if (task.indexOf("ride") === 0) { + if (task === "ride") { + for (let i = 1; i <= chars.length; i += 2) { + for (let j = 0; j < chars[i].length; j++) { + if (chars[i][j].indexOf("taxi") > -1) taxiChar = chars[i - 1]; + } + } + } else if (task.indexOf(" ") > -1) taxiChar = task.slice(task.indexOf(" ") + 1); + if (!taxiChar) { + D2Bot.printToConsole("ride string error", 9); + return false; + } + this.delays("ride", taxiDelay, taxiChar); + this.rideTaxi(); + } else if (task.indexOf("chests") === 0) { + if (task === "chests") { + chests = true; + } + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task === "on") chests = true; + if (task === "off") chests = false; + } + } else if (task === "chaos") { + if (me.name === taxiChar || (!taxiChar && me.classid === 1 && me.area !== 108)) { + if (me.area !== 107 && me.area !== 108) { + Town.goToTown(); + Pather.useWaypoint(107, true); + } + let teleTo; + if (tele === "gate") { + teleTo = [7795, 5555]; + } else if (tele === "star") { + teleTo = [7792, 5291]; + } + Pather.moveTo(teleTo[0], teleTo[1], 3); + Pather.makePortal(); + if (teleMsg.length > 0) say(teleMsg[rand(0, teleMsg.length - 1)]); + if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); + if (openSeals.length > 0) { + for (let k = 0; k < openSeals.length; k++) { + this.openSeal(391 + openSeals[k]); + } + Pather.moveTo(teleTo[0], teleTo[1], 3); + if (sealMsg.length > 0) say(sealMsg[rand(0, sealMsg.length - 1)]); + if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); + } + if (clearChars.indexOf(me.name) > -1) { + Pather.makePortal(); + this.clearChaos(); + } else { + Town.goToTown(); + //Town.doChores(); + //Town.move("portalspot"); + } + } else { + this.delays("chaos", chaosDelay, null); + if (me.area === 108 && clearChars.indexOf(me.name) > -1) { + this.clearChaos(); + } + } + } else if (task === "getleg") { + this.getLeg(); + } else if (task === "makecows") { + this.openPortal(); + } else if (task === "Shopbot") { + this.Shopbot(); + } else if (task === "Tunnels") { + Pather.journeyTo(65); + Attack.clearLevel(Config.ClearType); + } else if (task === "Urdars") { + Town.goToTown(); + Pather.useWaypoint(106); + Precast.doPrecast(true); + Pather.journeyTo(107); + this.Urdars(); + } else if (task === "findshrine") { + let shrineAreas = [2, 3, 4, 10, 5, 6, 7/*,26,27,28,29,30,31,32,33,34,35,41,42,43,44,76,77,78,79,80,81,82,104,105,106,107,108*/]; + Town.goToTown(); + Pather.useWaypoint(3, true); + for (let i = 0; i < shrineAreas.length; i++) { + Pather.journeyTo(shrineAreas[i]); + if (Misc.getShrinesInArea(shrineAreas[i], 15, false)) { + if (chars[chars.indexOf(me.name) + 1].indexOf("useshrine") === chars[chars.indexOf(me.name) + 1].indexOf("findshrine") + 1) { + let shrine = getUnit(2, "shrine"); + if (shrine) { + do { + if (shrine.objtype === 15 && shrine.mode === 0) { + Pather.moveTo(shrine.x - 3, shrine.y - 3); + Misc.getShrine(shrine); + if (me.getState(137)) break; + } + } while (shrine.getNext()); + } + } + break; + } + } + Town.goToTown(); + if (finderDelay && finderDelay > 0) this.delays("finder", finderDelay); + } else if (task === "useshrine") { + if (me.getState(137)) return true; + let finder; + for (let i = 1; i <= chars.length; i += 2) { + if (chars[i].indexOf("findshrine") > -1) finder = chars[i - 1]; + } + if (!finder) finder = null; + if (finder === me.name || (finder == null && me.classid === 1)) this.runTask("findshrine"); + this.delays("shrine", shrineDelay, finder); + } else if (task === "clearcows") { + if (me.area !== 39) { + Town.goToTown(1); + Town.move("stash"); + for (let i = 0; i < 30; i++) { + if (Pather.usePortal(39)) break; + delay(100); + } + } + if (me.area === 39) Attack.clearLevel(Config.ClearType); + } else if (task === "chores") { + Town.goToTown(); + Town.doChores(); + } else if (task.indexOf("areadelay") === 0) { + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + if (task.indexOf("in") === 0) { + if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "in", task); + else if (task.indexOf(" ") > -1) { + let aid = Number(task.slice(0, task.indexOf(" "))); + task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(aid)) this.delays("area", areaDelay, task, "in", aid); + else D2Bot.printToConsole("areadelay string error1", 9); + } else D2Bot.printToConsole("areadelay string error2", 9); + } else if (task.indexOf("out") === 0) { + if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "out", task); + else if (task.indexOf(" ") > -1) { + let aid = Number(task.slice(0, task.indexOf(" "))); + task = task.slice(task.indexOf(" ") + 1); + if (!isNaN(aid)) this.delays("area", areaDelay, task, "out", aid); + else D2Bot.printToConsole("areadelay string error3", 9); + } else D2Bot.printToConsole("areadelay string error4", 9); + } else D2Bot.printToConsole("areadelay string error5", 9); + } else D2Bot.printToConsole("areadelay string error6", 9); + } else if (task.indexOf("goto") === 0) { + if (task.indexOf(" ") > -1) { + task = task.slice(task.indexOf(" ") + 1); + let aid = Number(task); + if (!isNaN(aid)) Pather.journeyTo(aid); + else D2Bot.printToConsole("goto string value error", 9); + } + return true; + } + return false; + }; + + this.clearChaos = function() { + this.clearAct = function(act, x, y) { + if (act === "move") { + let xPath = (clearMode === 0) ? true : false; + Pather.moveTo(x, y, 3, xPath); + if (tele === "gate" && x === 7794 && y === 5315) Pather.makePortal(); + if (tele === "star" && clearDirection === 0 && x === 7765 && y === 5294) Pather.makePortal(); + if (tele === "star" && clearDirection === 1 && x === 7825 && y === 5294) Pather.makePortal(); + Attack.clear(25, clearMode); + } else if (act === "seal") { + if (openSeal(x) === true) { + if (x === 392 || x === 396) { + for (let i = 0; i < 30; i++) { + let boss = (x === 392) ? getUnit(1, "Infector of Souls") : getUnit(1, "Grand Vizier of Chaos"); + if (boss) break; + delay(100); + } + } + } + if (((x === 393 && clearDirection === 0) || (x === 395 && clearDirection === 1)) && cmd !== "Diablo") { + cmd = "Diablo"; + say("Diablo"); + } + } else if (act === "Infector of Souls" || act === "Lord de Seis" || act === "Grand Vizier of Chaos") { + let boss = getUnit(1, act); + if (!boss) { + Pather.moveTo(x, y, 3, xPath); + Attack.clear(25, clearMode); + } + boss = getUnit(1, act); + if (boss) { + if (boss.hp <= 0 || boss.mode === 0 || boss.mode === 12) { + // + } else { + Attack.clear(25, clearMode, act); + } + } + } else if (act === "Diablo") { + cmd = "Diablo"; + } else { + throw new Error("Invalid clearAct"); + } + if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); + Precast.doPrecast(false); + }; + Precast.doPrecast(false); + Attack.clear(10); + initLayout(); + let Gate = ["move", 7794, 5545, "move", 7794, 5525, "move", 7794, 5505, "move", 7794, 5480, "move", 7794, 5440, "move", 7794, 5405, "move", 7794, 5365, "move", 7794, 5335, "move", 7794, 5315]; + let preFect = ["move", 7825, 5294, "move", 7843, 5294, "move", 7861, 5294]; + let Fect = (infLayout === 1) + ? ["move", 7890, 5295, "move", 7915, 5290, "seal", 392, 1, "Infector of Souls", 7890, 5295, "Infector of Souls", 7915, 5290, "seal", 393, 0] + : ["move", 7900, 5275, "move", 7930, 5280, "move", 7930, 5310, "seal", 392, 1, "move", 7930, 5310, "Infector of Souls", 7930, 5280, "Infector of Souls", 7900, 5275, "seal", 393, 0]; + let preSeis = ["move", 7794, 5265, "move", 7794, 5245, "move", 7794, 5225]; + let Seis2 = (openSeals.indexOf(3) > -1) + ? ["move", 7775, 5210, "move", 7775, 5195, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210] + : ["move", 7775, 5210, "move", 7775, 5195, "move", 7810, 5190, "move", 7810, 5155, "move", 7785, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210]; + let Seis5 = (openSeals.indexOf(3) > -1) ? ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195] : ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "move", 7805, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195]; + let Seis = (seisLayout === 1) ? Seis2 : Seis5; + let preVizi = ["move", 7765, 5294, "move", 7745, 5294, "move", 7715, 5294]; + let Vizi = (vizLayout === 1) + ? ["move", 7680, 5290, "move", 7665, 5275, "seal", 396, 2, "Grand Vizier of Chaos", 7665, 5275, "Grand Vizier of Chaos", 7680, 5290, "seal", 395, 0] + : ["move", 7700, 5315, "move", 7670, 5315, "seal", 396, 2, "Grand Vizier of Chaos", 7670, 5315, "Grand Vizier of Chaos", 7700, 5315, "seal", 395, 0]; + let Diablo = (clearDirection === 1) + ? ["move", 7765, 5295, "move", 7796, 5296, "Diablo", 666, 666] + : ["move", 7825, 5294, "move", 7795, 5295, "Diablo", 666, 666]; + let clearActQueue = []; + if (tele === "gate" && me.x >= 5520) clearActQueue = clearActQueue.concat(Gate); + if (clearDirection === 0) clearActQueue = clearActQueue.concat(preVizi, Vizi, preSeis, Seis, preFect, Fect, Diablo); + if (clearDirection === 1) clearActQueue = clearActQueue.concat(preFect, Fect, preSeis, Seis, preVizi, Vizi, Diablo); + for (let j = 1; clearActQueue.length > j; j += 3) { + if (cmd === "Diablo") { + if (last !== "Diablo") this.diablo(); + break; + } + this.clearAct(clearActQueue[j - 1], clearActQueue[j], clearActQueue[j + 1]); + if (cmd === "Diablo") { + if (last !== "Diablo") this.diablo(); + break; + } + delay(100); + } + return true; + }; + + this.diablo = function () { + cmd !== "Diablo" && (cmd = "Diablo"); + if (last === "Diablo") return true; + + if (taxiChar === me.name) { + if (me.area === 108) { + Pather.moveTo(diax, diay, 3); + if ((killDiablo.includes(me.name) + || leechDiablo.includes(me.name) + || weakenDiablo.includes(me.name)) + && me.name !== shrineDiablo) { + Pather.makePortal(); + } else { + Pather.makePortal(true); + } + } + if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { + this.delays("area", 30, shrineDiablo, "in", 108); + } else if (killDiablo.length > 0) { + this.delays("area", 30, killDiablo, "in", 108); + } + } + + if (shrineDiablo === me.name) { + let finder; + for (let i = 1; i <= chars.length; i += 2) { + if (chars[i].includes("findshrine")) { + finder = chars[i - 1]; + } + } + + !finder && (finder = null); + if (finder === me.name || (finder === null && me.sorceress)) { + this.runTask("findshrine"); + } + console.log(finder); + this.delays("shrine", shrineDelay, finder); + } + if (killDiablo.indexOf(me.name) > -1) { + if (me.area !== 108) { + Town.goToTown(4); + Town.move("portalspot"); + if (!taxiChar || !Pather.usePortal(108, taxiChar)) { + let portal = getUnit(2, "portal"); + if (portal) { + do { + if (portal.objtype === 108 + && portal.getParent() !== me.name + && Misc.inMyParty(portal.getParent()) + && chars.indexOf(portal.getParent()) > -1) { + if (Pather.usePortal(null, null, portal)) break; + } + } while (portal.getNext()); + } + } + } + if (me.area !== 108) Pather.usePortal(108, null); + if (me.area === 108) { + Pather.moveTo(diax, diay, 3); + Pather.makePortal(); + if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { + this.delays("area", 60, shrineDiablo, "in", 108); //60 was 30 + } + this.killBoss("Diablo"); + } + } else if (leechDiablo.indexOf(me.name) > -1) { + if (me.area !== 108) { + Town.goToTown(4); + Town.move("portalspot"); + if (taxiChar) { + if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); + } else Pather.usePortal(108, null); + } + if (me.area === 108) { + (leechDiablo.indexOf(me.name) % 2 === 0) + ? Pather.moveTo(7792, 5291, 3) + : Pather.moveTo(7792, 5291, 3); + for (let i = 0; i < 200; i++) { + if (!getUnit(1, 243)) { + delay(100); + } + } + while (getUnit(1, "Diablo") && getUnit(1, "Diablo").hp > 0) delay(100); + Pickit.pickItems(); + } + } else if (weakenDiablo.indexOf(me.name) > -1) { + if (me.area !== 108) { + Town.goToTown(4); + Town.move("portalspot"); + if (taxiChar) { + if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); + Pather.makePortal(); + } else Pather.usePortal(108, null); + } + if (me.area === 108) { + if (!diax) this.initLayout(); + Pather.moveTo(diax, diay, 3); + let waitfor = killDiablo.concat(leechDiablo); + console.log(waitfor); + loop: + for (let i = 0; i < 300; i++) { + for (let j = 0; j < waitfor.length; j++) { + delay(100); + if (Misc.inMyParty(waitfor[j]) && !getUnit(0, waitfor[j])) break; + else if ((!Misc.inMyParty(waitfor[j]) || getUnit(0, waitfor[j])) && j === waitfor.length - 1) break loop; + } + } + Config.PublicMode = 0; + delay(2 * me.ping + 100); + let player = getParty(); + clickParty(player, 3); + for (let i = 0; i < 200; i++) { + if (!getUnit(1, 243)) { + delay(100); + } + } + while (getUnit(1, 243) && getUnit(1, 243).hp > 0) { + switch (me.classid) { + case 0: //Amazon + break; + case 1: //Sorceress + if (Skill.cast(42, 0)) Skill.cast(42, 0); //static + break; + case 2: //Necromancer + if (Skill.cast(Config.Curse[0], 0)) Skill.cast(Config.Curse[0], 0); //spam boss curse + //else if other curses + break; + case 3: //Paladin + if (Skill.setSkill(123, 0)) Skill.setSkill(123, 0); //conviction aura + break; + case 4: //Barbarian + break; + } + delay(100); + } + Pickit.pickItems(); + } + } else { + if (me.area === 108) { + if (clearDirection === 0) Pather.moveTo(7825, 5294, 3); + if (clearDirection === 1) Pather.moveTo(7765, 5294, 3); + } + Town.goToTown(); + } + taxiChar = false; + last = "Diablo"; + return true; + }; + + this.getLeg = function () { + if (Pather.getPortal(sdk.areas.MooMooFarm)) return false; + /** @type {ItemUnit} */ + let leg; + let gid; + let wrongLeg; // this isn't doing anything + + while (!leg) { + if (me.getItem(sdk.quest.item.WirtsLeg)) { + leg = me.getItem(sdk.quest.item.WirtsLeg); + return me.getItem(-1, -1, leg.gid); + } + + leg = Game.getItem(sdk.quest.item.WirtsLeg); + if (leg) { + do { + if (leg.name.indexOf("ÿc1") > -1) { + wrongLeg = true; + + return false; + } else { + gid = leg.gid; + Pickit.pickItem(leg); + + return me.getItem(-1, -1, gid); + } + } while (leg.getNext()); + } + + if (chars[chars.indexOf(me.name) + 1].indexOf("getleg") > -1) { + Pather.useWaypoint(sdk.areas.StonyField); + Precast.doPrecast(true); + Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); + + /** @type {ObjectUnit} */ + let portal; + for (let i = 0; i < 6; i += 1) { + portal = Pather.getPortal(sdk.areas.Tristram); + if (portal) { + Pather.usePortal(null, null, portal); + break; + } + delay(500); + } + + if (!portal) { + Town.goToTown(); + return false; + } + + Pather.moveTo(25048, 5177); + let wirt = Game.getObject(sdk.quest.chest.Wirt); + for (let i = 0; i < 8; i += 1) { + !!wirt && wirt.interact(); + delay(500); + leg = Game.getItem(sdk.quest.item.WirtsLeg); + + if (leg) { + gid = leg.gid; + Pickit.pickItem(leg); + Town.goToTown(1); + + if (chars[chars.indexOf(me.name) + 1].indexOf("makecows") === -1) { + Town.move("stash"); + leg.drop(); + return true; + } else { + return me.getItem(-1, -1, gid); + } + } + } + Town.goToTown(); + } else { + Town.move("stash"); + } + delay(100); + } + return false; + }; + + this.getTome = function () { + let myTome = me.findItem("tbk", 0, 3); + let tome = me.getItem("tbk"); + if (tome) { + do { + if (!myTome || tome.gid !== myTome.gid) { + return copyUnit(tome); + } + } while (tome.getNext()); + } + + Town.move(NPC.Akara); + let akara = Town.initNPC("Shop"); + if (!akara) return false; + + tome = akara.getItem("tbk"); + if (tome.buy()) { + tome = me.getItem("tbk"); + if (tome) { + do { + if (!myTome || tome.gid !== myTome.gid) { + return copyUnit(tome); + } + } while (tome.getNext()); + } + } + return false; + }; + + this.openPortal = function () { + Town.goToTown(1); + let leg = this.getLeg(); + if (!leg) return false; + + let tome = this.getTome(); + if (!tome) return false; + + if (!Town.openStash() + || !Cubing.emptyCube() + || !Storage.Cube.MoveTo(leg) + || !Storage.Cube.MoveTo(tome) + || !Cubing.openCube()) { + return false; + } + transmute(); + delay(500); + for (let i = 0; i < 10; i += 1) { + if (Pather.getPortal(39)) { + return true; + } + delay(200); + } + me.cancel(); + return false; + }; + + this.rideTaxi = function() { + let tTimer = getTickCount(); + while (true) { + if (!taxiChar || !Misc.inMyParty(taxiChar)) { + break; + } + + if (this.getAct(taxiChar) > 0) { + Town.goToTown(this.getAct(taxiChar)); + Town.move("portalspot"); + } + + if (cmd && cmd !== last) { + let currCmd = cmd; + if (currCmd === "Diablo") { + this.diablo(); + } else if (teleMsg.includes(currCmd)) { + if (clearChars.includes(me.name)) { + this.delays("chaos", chaosDelay, taxiChar); + this.clearChaos(); + } + taxiChar = false; + + break; + } else if (Pather.getPortal(null, taxiChar)) { + if (Pather.usePortal(null, taxiChar)) { + if (currCmd === cmd) { + this.killBoss(currCmd); + } + Town.goToTown(); + } + } + last = currCmd; + tTimer = getTickCount(); + } + + if (getTickCount > tTimer + taxiDelay * 1e3) { + taxiChar = false; + break; + } + Precast.doPrecast(false); + delay(100); + } + }; + + this.Shopbot = function () { + Loader.runScript("ShopBot"); + }; + + /** + * @param {string | number} name + * @returns {boolean} + */ + this.Urdars = function (name) { + let room = getRoom(); + if (!room) return false; + + const urdars = [189, 309, 361, 311, 675, 300, 692]; + const rooms = []; + /** @param {Monster} monster */ + const check = function (monster) { + return ( + urdars.includes(monster.classid) + && monster.attackable + && monster.spectype !== sdk.monsters.spectype.All + && monster.spectype !== sdk.monsters.spectype.Minion + && 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 = []; + let monster = Game.getMonster(name); + + if (monster) { + do { + if (check(monster)) { + monList.push(copyUnit(monster)); + + if (me.name === taxiChar) { + Pather.moveTo(monster.x + rand(-5, 5), monster.y + rand(-5, 5), 0); + Pather.makePortal(); + say(monster.name); + } + + if (monster && monster.hp > 0) { + if (me.name === taxiChar) { + for (let i = 0; i < 30; i++) { + if (monster) Attack.clear(getDistance(me, monster) + 5, 0xF, monster); + if (!monster || monster.hp === 0) break; + delay(100); + } + } else if (monster) { + if (monster.spectype === sdk.monsters.spectype.All) { + Attack.clear(getDistance(me, monster) + 5, 0, monster); + } else { + Attack.clear(getDistance(me, monster) + 5, 0xF, monster); + } + } + } + } + } while (monster.getNext()); + } + + if (!Attack.clearList(monList)) { + return false; + } + } + } + + return true; + }; + + this.precast = function () { + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Barbarian: + return false; + case sdk.player.class.Sorceress: //Sorceress + return ( + Skill.cast(sdk.skills.Blizzard) + || Skill.cast(sdk.skills.Meteor) + || Skill.cast(sdk.skills.StaticField) + || Skill.cast(sdk.skills.FrostNova) + || Skill.cast(sdk.skills.Hydra) + || Skill.cast(sdk.skills.Nova) + ); + case sdk.player.class.Necromancer: + return Skill.cast(Config.Curse[1], 0); + case sdk.player.class.Paladin: + return ( + Skill.setSkill(sdk.skills.Concentration, 0) + && Skill.cast(sdk.skills.BlessedHammer) + ); + } + return false; + }; + + /** + * @param {string} nick + * @param {string} msg + */ + function ChatEvent(nick, msg) { + if (!taxiChar && msg === "taxi" && chars.includes(nick)) { + taxiChar = nick; + } else if (nick === taxiChar && msg === "ixat" && cmd !== "Diablo") { + taxiChar = false; + } else if (nick === taxiChar && nick !== me.name) { + cmd = msg; + } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick) && clearChars.indexOf(me.name)) { + cmd = "Diablo"; + } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick)) { + if (leechDiablo.includes(me.name)) { + taxiChar = nick; + } + this.diablo(); + } + } + + // START + let listen = false; + for (let i = 0; i < chars[chars.indexOf(me.name) + 1].length; i++) { + if (chars[chars.indexOf(me.name) + 1][i].indexOf("ride") > -1) { + listen = true; + } + } + if (!listen && ( + killDiablo.includes(me.name) + || leechDiablo.includes(me.name) + || weakenDiablo.includes(me.name) + || clearChars.includes(me.name) + )) { + listen = true; + } + if (listen) { + addEventListener("chatmsg", ChatEvent); + } + Pickit.pickItems(); + Town.heal(); + this.runList(chars[chars.indexOf(me.name) + 1]); + if (killDiablo.indexOf(me.name) === -1) { + for (let i = 0; i < killDiablo.length; i++) { + while (Misc.inMyParty(killDiablo[i])) { + if (me.sorceress && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) { + this.delays("givechant", 1); + } else { + delay(1000); + } + } + } + } + + return true; +} From db4113a0387baeb15df46a8e87c90da5ba5edd4b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 May 2024 00:57:54 -0400 Subject: [PATCH 412/758] Add `Pather.getWalkDistance` + use to fix `getIntoPosition` - Walking bots will sometimes pick locations on other sides of walls while attempting to find a spot to cast from because it's "close" distance. Check the actual walking distance to this spot --- d2bs/kolbot/libs/core/Attack.js | 10 ++++++++++ d2bs/kolbot/libs/core/Pather.js | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 44ea4ed55..13790890d 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1803,6 +1803,16 @@ const Attack = { for (let i = 0; i < coords.length; i += 1) { // Valid position found if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { + if (!Pather.canTeleport() && Pather.getWalkDistance(coords[i].x, coords[i].y) > unit.distance) { + if (Config.DebugMode.Path) { + console.debug( + "Skipping position due to walk distance being too far." + + "\n - DistanceToMonster: " + unit.distance + + "\n - DistanceToPosition: " + Pather.getWalkDistance(coords[i].x, coords[i].y) + ); + } + continue; + } if ((() => { switch (walk) { case 1: diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 0a740ab05..6e7bf3e5a 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -2160,6 +2160,32 @@ const Pather = { const coord = CollMap.getRandCoordinate(me.x, -4, 4, me.y, -4, 4, factor); return Pather.move(coord, { retry: 3, allowClearing: false }); }, + + /** + * @param {number} x + * @param {number} y + * @param {number} [area] + * @param {number} [xx] + * @param {number} [yy] + * @param {number} [reductionType] + * @param {number} [radius] + * @returns {number} + */ + 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) { + return i && getDistance(s[i - 1], e) || 0; + }) + .reduce(function (acc, cur) { + return acc + cur; + }, 0) || Infinity; + }, }; Pather.nextAreas[sdk.areas.RogueEncampment] = sdk.areas.BloodMoor; From 34a533e5c6e39ddd583976b17ab1168cd10d9fb6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 23 May 2024 01:07:43 -0400 Subject: [PATCH 413/758] Add experimental autoshriner - Automatically determine what shrines we want based on states, distances, need. Still in development. To enable set `Config.AutoShriner = true` --- d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/libs/core/Misc.js | 267 ++++++++++++++++++++++++++++++-- d2bs/kolbot/libs/core/Pather.js | 6 +- 3 files changed, 260 insertions(+), 14 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index c170ea1d2..1dbea5ea1 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -264,6 +264,7 @@ let Config = { SkipException: [], /** @type {number[]} */ ScanShrines: [], + AutoShriner: false, UseWells: { HpPercent: 0, MpPercent: 0, diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 4ac28527c..39779dfa8 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -10,6 +10,7 @@ const Misc = (function () { const ShrineData = require("./GameData/ShrineData"); return { + _diabloSpawned: false, /** * Click something * @param {number} button @@ -421,7 +422,6 @@ const Misc = (function () { openChests: function (range = 15) { if (!Config.OpenChests.Enabled) return true; - let unitList = []; let containers = []; // Testing all container code @@ -441,21 +441,36 @@ const Misc = (function () { containers = Config.OpenChests.Types; } - let unit = Game.getObject(); + /** @type {Set} */ + const seenGids = new Set(); - if (unit) { - do { - if (unit.name && unit.mode === sdk.objects.mode.Inactive - && getDistance(me.x, me.y, unit.x, unit.y) <= range - && containers.includes(unit.name.toLowerCase())) { - unitList.push(copyUnit(unit)); - } - } while (unit.getNext()); - } + /** + * @param {number} range + * @returns {ObjectUnit[]} + */ + const buildChestList = function (range) { + let unitList = []; + let unit = Game.getObject(); + + if (unit) { + do { + if (unit.name && unit.mode === sdk.objects.mode.Inactive + && !seenGids.has(unit.gid) && seenGids.add(unit.gid) + && getDistance(me.x, me.y, unit.x, unit.y) <= range + && containers.includes(unit.name.toLowerCase())) { + unitList.push(copyUnit(unit)); + } + } while (unit.getNext()); + } + return unitList; + }; + + const startPos = new PathNode(me.x, me.y); + let unitList = buildChestList(range); while (unitList.length > 0) { unitList.sort(Sort.units); - unit = unitList.shift(); + let unit = unitList.shift(); if (unit) { const chest = Game.getObject(-1, -1, unit.gid); @@ -464,6 +479,13 @@ const Misc = (function () { Pickit.pickItems(); } } + + if (startPos.distance > 5) { + // rebuid chest list every 5 chests in case we've moved and add any new chests to our list + let _unitList = buildChestList(range / 2); + console.debug("Rescanning for chests: " + _unitList.length + " chests found."); + unitList = unitList.concat(_unitList); + } } return true; @@ -472,12 +494,230 @@ const Misc = (function () { /** @type {number[] | null} */ shrineStates: null, + lastShrine: new function () { + this.tick = 0; + this.duration = 0; + this.type = -1; + this.state = 0; + + /** @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); + }; + }, + + /** @type {Set} */ + _shrinerIgnore: new Set(), + + shriner: function () { + if (!Config.AutoShriner) return false; + + let shrineList = []; + let shrine = Game.getObject(); + + /** + * TODO: Handle stateful shrines + * TODO: Track last shrine used - should tier based on shrine type + * @param {ObjectUnit} shrine + * @returns {boolean} + */ + const wantShrine = function (shrine) { + if (ShrineData.getState(shrine.objtype) + && Misc.lastShrine.type === shrine.objtype + && Misc.lastShrine.isMyCurrentState() + && Misc.lastShrine.remaining() > Time.seconds(30)) { + return false; + } + switch (shrine.objtype) { + case sdk.shrines.Health: + // we only want if its dire or its close to us if we can't teleport + if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 10) { + return me.hpPercent <= 50; + } + return me.hpPercent < 80; + case sdk.shrines.Mana: + // we only want if its dire or its close to us if we can't teleport + if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 10) { + return me.mpPercent <= 50; + } + return me.mpPercent < 80; + case sdk.shrines.Refilling: + // we only want if its dire or its close to us if we can't teleport + if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 10) { + return me.hpPercent <= 50 || me.mpPercent <= 50; + } + return me.hpPercent < 85 || me.mpPercent < 85; + case sdk.shrines.Experience: + return me.charlvl < 99; + case sdk.shrines.Skill: + return !me.getState(sdk.states.ShrineExperience); + case sdk.shrines.ManaRecharge: + case sdk.shrines.Stamina: + // we only want if its close to us if we can't teleport + if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + return false; + } + // for now, only grab if we have nothing else active + return !Misc.lastShrine.state || !me.getState(Misc.lastShrine.state); + case sdk.shrines.ResistFire: + case sdk.shrines.ResistCold: + case sdk.shrines.ResistLightning: + case sdk.shrines.ResistPoison: + { + /** @type {Record} */ + let resistances = {}; + resistances[sdk.shrines.ResistFire] = me.fireRes; + resistances[sdk.shrines.ResistCold] = me.coldRes; + resistances[sdk.shrines.ResistLightning] = me.lightRes; + resistances[sdk.shrines.ResistPoison] = me.poisonRes; + + // we only want if its dire or its close to us if we can't teleport + if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + return resistances[shrine.objtype] <= 0; + } + + if (!Misc.lastShrine.state || !me.getState(Misc.lastShrine.state)) { + return true; + } + + // first check if the lasts shrine was a resist shrine + if (resistances[Misc.lastShrine.type] === undefined) { + // evaluate whether we should overwrite the last shrine + if (Misc.lastShrine.type === sdk.shrines.Experience) { + // never overwrite experience shrine + return false; + } + + if (Misc.lastShrine.type === sdk.shrines.Skill) { + if (resistances[shrine.objtype] <= 25) { + // makes sense if we have a low resistance + return true; + } + + return false; + } + } + + // check that the current shrine benefits our lowest resistance better than the last shrine + if (resistances[shrine.objtype] < resistances[Misc.lastShrine.type]) { + // how much better? If it's at least a 5% difference, we should take it + // otherwise only do it if the distance is convenient + + return true; + } + break; + } + // TODO: handle armor and combat shrines + case sdk.shrines.Armor: + case sdk.shrines.Combat: + // we only want if its close to us if we can't teleport + if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + return false; + } + + if (!Misc.lastShrine.state || !me.getState(Misc.lastShrine.state)) { + return true; + } + return false; + case sdk.shrines.Monster: + // we only want if its close to us if we can't teleport + if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + return false; + } + + return true; // why not? + case sdk.shrines.Gem: + // for now we ignore if we are gem hunting later on + // TODO: add gem hunting logic, get gem from stash if we have one + return !Scripts.GemHunter; + } + return false; + }; + + const wantWell = function () { + if (me.hpPercent < 75) return true; + if (me.mpPercent < 75) return true; + if (me.staminaPercent < 50) return true; + return [ + sdk.states.Frozen, + sdk.states.Poison, + sdk.states.AmplifyDamage, + sdk.states.Decrepify + ].some(function (state) { + return me.getState(state); + }); + }; + + if (shrine) { + do { + let _name = shrine.name.toLowerCase(); + if ((_name.includes("shrine") && ShrineData.has(shrine.objtype) || (_name.includes("well"))) + && ShrineData.has(shrine.objtype) + && shrine.mode === sdk.objects.mode.Inactive) { + shrineList.push(copyUnit(shrine)); + } + } while (shrine.getNext()); + } + + while (shrineList.length > 0) { + shrineList.sort(Sort.units); + shrine = shrineList.shift(); + + if (shrine) { + if (me.inArea(sdk.areas.ChaosSanctuary)) { + // stateful shrines are pointless in CS unless we are running wakka or diablo has spawned so no more oblivion knights + if (!this._diabloSpawned && Loader.scriptName() !== "Wakka" && ShrineData.getState(shrine.objtype)) { + continue; + } + } + + if (ShrineData.has(shrine.objtype) ? wantShrine(shrine) : wantWell()) { + // need to take distance into account. + // How far away is this shrine? + // Can we teleport to it? + // Is it closer to our path at a later point? + // Is it a really good shrine or just a meh one? So we know if we should go out of our way for it. + if (shrine.distance > Skill.haveTK ? 20 : 10) { + if (Pather.currentWalkingPath.some((point) => getDistance(point.x, point.y, shrine.x, shrine.y) < 10)) { + if (Config.DebugMode.Path) { + new Line(shrine.x - 3, shrine.y, shrine.x + 3, shrine.y, 0x9B, true); + new Line(shrine.x, shrine.y - 3, shrine.x, shrine.y + 3, 0x9B, true); + } + continue; + } + } + this.getShrine(shrine); + } + } + } + + return true; + }, + /** * @param {number} range * @param {number[]} ignore * @returns {boolean} */ - scanShrines: function (range, ignore = []) { + scanShrines: function (range, ignore) { + if (Config.AutoShriner) { + return this.shriner(); + } if (!Config.ScanShrines.length) return false; !range && (range = Pather.useTeleport() ? 25 : 15); @@ -588,6 +828,7 @@ const Misc = (function () { } if (Misc.poll(() => unit.mode, 1000, 40)) { + Misc.lastShrine.update(unit); return true; } } diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 6e7bf3e5a..7c2b5bf17 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -97,7 +97,11 @@ const NodeAction = { * Scan shrines while pathing */ getShrines: function () { - Config.ScanShrines.length > 0 && Misc.scanShrines(null, this.shrinesToIgnore); + if (Config.AutoShriner) { + Misc.shriner(); + } else if (Config.ScanShrines.length > 0) { + Misc.scanShrines(null, this.shrinesToIgnore); + } } }; From 1f41f4a692d87b157a34a99b28e252c2aae3418f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 May 2024 18:44:05 -0400 Subject: [PATCH 414/758] [Feat] New nip syntax `in` and `notin` - Use `in` or `notin` with a comma separated list so we don't have to type out long chains of `[name] == id || [name == id2...` now can do `[name] in (id1, id2)` --- d2bs/kolbot/libs/core/NTItemParser.js | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 4f8266730..d45916ee6 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -439,10 +439,52 @@ NTIP.ParseLineInt = function (input, info) { ["stat", NTIPAliasStat], ]); + const parseAliasIn = { + in: "\[([^\]]+)\]in\(", + notin: "\[([^\]]+)\]notin\(", + /** + * @param {string} input + * @returns {boolean} + */ + test: function (input) { + return new RegExp(/\[([^\]]+)\](in|notin)\(/gi).test(input); + }, + /** + * @param {string} input + * @returns {string} + */ + convert: function (input) { + const regex = new RegExp(/\[([^\]]+)\](in|notin)\(([^)]+)\)/g); + let match; + let result = input; + while ((match = regex.exec(input)) !== null) { + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + const [_full, property, type, values] = match; + if (!property || !values) throw new Error("Invalid syntax"); + const alias = "(" + values.split(",") + .filter(function (el) { + return el.trim().length > 0; + }) + .map(function (el) { + return "[" + property + "]" + (type === "in" ? "==" : "!=") + el.trim(); + }) + .join(type === "in" ? "||" : "&&") + + ")"; + result = result.replace(match[0], alias); + } + return result; + } + }; + p_result = input.split("#"); try { if (p_result[0] && p_result[0].length > 4) { + if (parseAliasIn.test(p_result[0])) { + p_result[0] = parseAliasIn.convert(p_result[0]); + } p_section = p_result[0].split("["); p_result[0] = p_section[0]; From 4c49571d697c15e2e5f292ff06a71223c82aec71 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 May 2024 11:28:46 -0400 Subject: [PATCH 415/758] Update NTItemParser.js - Small tweak so we don't have to re-initialize the RegExp for every line --- d2bs/kolbot/libs/core/NTItemParser.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index d45916ee6..f576c895c 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -442,12 +442,15 @@ NTIP.ParseLineInt = function (input, info) { const parseAliasIn = { in: "\[([^\]]+)\]in\(", notin: "\[([^\]]+)\]notin\(", + /** @private */ + _regex: new RegExp(/\[([^\]]+)\](in|notin)\(/gi), /** * @param {string} input * @returns {boolean} */ test: function (input) { - return new RegExp(/\[([^\]]+)\](in|notin)\(/gi).test(input); + this._regex.lastIndex = 0; + return this._regex.test(input); }, /** * @param {string} input From 3c5aa6b2ff540c76e292b16166c56127aced3551 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 May 2024 11:42:57 -0400 Subject: [PATCH 416/758] Moved `parseAliasIn` and maps outside of `ParseLineInt`'s function body - Need to be able to access is from outside the function --- d2bs/kolbot/libs/core/NTItemParser.js | 211 +++++++++++++------------- 1 file changed, 105 insertions(+), 106 deletions(-) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index f576c895c..8234bfd03 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -372,6 +372,100 @@ NTIP.IsSyntaxInt = function (ch) { ); }; +NTIP.parseAliasIn = { + in: "\[([^\]]+)\]in\(", + notin: "\[([^\]]+)\]notin\(", + /** @private */ + _regex: new RegExp(/\[([^\]]+)\](in|notin)\(/gi), + /** + * @param {string} input + * @returns {boolean} + */ + test: function (input) { + this._regex.lastIndex = 0; + return this._regex.test(input); + }, + /** + * @param {string} input + * @returns {string} + */ + convert: function (input) { + const regex = new RegExp(/\[([^\]]+)\](in|notin)\(([^)]+)\)/g); + let match; + let result = input; + while ((match = regex.exec(input)) !== null) { + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + const [_full, property, type, values] = match; + if (!property || !values) throw new Error("Invalid syntax"); + const alias = "(" + values.split(",") + .filter(function (el) { + return el.trim().length > 0; + }) + .map(function (el) { + return "[" + property + "]" + (type === "in" ? "==" : "!=") + el.trim(); + }) + .join(type === "in" ? "||" : "&&") + + ")"; + result = result.replace(match[0], alias); + } + return result; + } +}; + +NTIP._props = new Map([ + ["classid", "item.classid"], + ["name", "item.classid"], + ["type", "item.itemType"], + ["class", "item.itemclass"], + ["quality", "item.quality"], + ["charlvl", "me.charlvl"], + ["level", "item.ilvl"], + ["flag", "item.getFlag("], + ["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()"], + ["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)"], + ["prefix", "item.getPrefix("], + ["suffix", "item.getSuffix("] +]); + +NTIP._aliases = new Map([ + ["n", "name"], + ["id", "classid"], + ["t", "type"], + ["q", "quality"], + ["lvl", "level"], + ["ilvl", "level"], + ["f", "flag"], + ["hc", "hardcore"], + ["cl", "classic"], + ["clvl", "charlvl"], +]); + +NTIP._lists = new Map([ + ["color", NTIPAliasColor], + ["type", NTIPAliasType], + ["name", NTIPAliasClassID], + ["classid", NTIPAliasClassID], + ["class", NTIPAliasClass], + ["quality", NTIPAliasQuality], + ["flag", NTIPAliasFlag], + ["stat", NTIPAliasStat], +]); + NTIP.ParseLineInt = function (input, info) { let i, property, p_start, p_end, p_section, p_keyword, p_result, value; @@ -387,117 +481,22 @@ NTIP.ParseLineInt = function (input, info) { return null; } - const _props = new Map([ - ["classid", "item.classid"], - ["name", "item.classid"], - ["type", "item.itemType"], - ["class", "item.itemclass"], - ["quality", "item.quality"], - ["charlvl", "me.charlvl"], - ["level", "item.ilvl"], - ["flag", "item.getFlag("], - ["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()"], - ["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)"], - ["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"], - ]); - - const _lists = new Map([ - ["color", NTIPAliasColor], - ["type", NTIPAliasType], - ["name", NTIPAliasClassID], - ["classid", NTIPAliasClassID], - ["class", NTIPAliasClass], - ["quality", NTIPAliasQuality], - ["flag", NTIPAliasFlag], - ["stat", NTIPAliasStat], - ]); - - const parseAliasIn = { - in: "\[([^\]]+)\]in\(", - notin: "\[([^\]]+)\]notin\(", - /** @private */ - _regex: new RegExp(/\[([^\]]+)\](in|notin)\(/gi), - /** - * @param {string} input - * @returns {boolean} - */ - test: function (input) { - this._regex.lastIndex = 0; - return this._regex.test(input); - }, - /** - * @param {string} input - * @returns {string} - */ - convert: function (input) { - const regex = new RegExp(/\[([^\]]+)\](in|notin)\(([^)]+)\)/g); - let match; - let result = input; - while ((match = regex.exec(input)) !== null) { - if (match.index === regex.lastIndex) { - regex.lastIndex++; - } - const [_full, property, type, values] = match; - if (!property || !values) throw new Error("Invalid syntax"); - const alias = "(" + values.split(",") - .filter(function (el) { - return el.trim().length > 0; - }) - .map(function (el) { - return "[" + property + "]" + (type === "in" ? "==" : "!=") + el.trim(); - }) - .join(type === "in" ? "||" : "&&") - + ")"; - result = result.replace(match[0], alias); - } - return result; - } - }; - p_result = input.split("#"); try { if (p_result[0] && p_result[0].length > 4) { - if (parseAliasIn.test(p_result[0])) { - p_result[0] = parseAliasIn.convert(p_result[0]); + 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]; 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); - if (_aliases.has(property)) { - property = _aliases.get(property); + if (NTIP._aliases.has(property)) { + property = NTIP._aliases.get(property); } switch (property) { @@ -505,19 +504,19 @@ 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)) { throw new Error("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); } - 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) { @@ -548,12 +547,12 @@ NTIP.ParseLineInt = function (input, info) { break; default: - if (!_lists.has(property)) { + if (!NTIP._lists.has(property)) { throw new Error("Unknown property: " + property + " File: " + info.file + " Line: " + info.line); - } else if (_lists.get(property)[p_keyword] === undefined) { + } 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] += _lists.get(property)[p_keyword]; + p_result[0] += NTIP._lists.get(property)[p_keyword]; property === "flag" && (p_result[0] += ")"); break; From f02a29f02397771462cd12c617b0ccfba5fe14f9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 May 2024 12:00:55 -0400 Subject: [PATCH 417/758] Update NTIP.d.ts --- d2bs/kolbot/sdk/types/NTIP.d.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/sdk/types/NTIP.d.ts b/d2bs/kolbot/sdk/types/NTIP.d.ts index b722921f2..611271195 100644 --- a/d2bs/kolbot/sdk/types/NTIP.d.ts +++ b/d2bs/kolbot/sdk/types/NTIP.d.ts @@ -1,7 +1,25 @@ export {}; declare global { namespace NTIP { - function OpenFile(filepath: string, notify: boolean): void; - function CheckItem(unit: Unit, entryList?: [] | false, verbose?: boolean): void; + function addLine(itemString: string, fileName: string): boolean; + function OpenFile(filepath: string, notify: boolean): boolean; + function CheckQuantityOwned(item_type: (item: ItemUnit) => boolean, item_stats: (item: ItemUnit) => boolean): number; + function Clear(): void; + function generateTierFunc(tierType: string): (item: ItemUnit) => number; + function GetTier(item: ItemUnit): number; + function GetMercTier(item: ItemUnit): number; + function IsSyntaxInt(ch: string): boolean; + const parseAliasIn: { + in: string; + notin: string; + _regex: RegExp; + test: (input: string) => boolean; + convert: (input: string) => string; + }; + const _props: Map; + const _aliases: Map; + const _lists: Map>; + function ParseLineInt(input: string, info: any): boolean; + function CheckItem(item: ItemUnit, entryList?: [] | false, verbose?: boolean): number | { line: string, result: number }; } } From a90ca85825c868b0b06256366def6929ac2867a7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 May 2024 12:10:17 -0400 Subject: [PATCH 418/758] Update NTIP.d.ts --- d2bs/kolbot/sdk/types/NTIP.d.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/d2bs/kolbot/sdk/types/NTIP.d.ts b/d2bs/kolbot/sdk/types/NTIP.d.ts index 611271195..59e60ac7d 100644 --- a/d2bs/kolbot/sdk/types/NTIP.d.ts +++ b/d2bs/kolbot/sdk/types/NTIP.d.ts @@ -1,5 +1,13 @@ export {}; declare global { + const NTIPAliasType: Record; + const NTIPAliasClassID: Record; + const NTIPAliasClass: Record; + const NTIPAliasQuality: Record; + const NTIPAliasFlag: Record; + const NTIPAliasColor: Record; + const NTIPAliasStat: Record; + namespace NTIP { function addLine(itemString: string, fileName: string): boolean; function OpenFile(filepath: string, notify: boolean): boolean; From e75c00f04a37bfa0bf5b02f26f94d8ab3037aeaf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 29 May 2024 00:40:46 -0400 Subject: [PATCH 419/758] Minor maintenance - Updated eslint `no-ununsed-vars` rule to ignore underscore pattern - Added eslint to devDependancies, using older version for compatibility with the current rulesets and rc file --- .eslintrc.js | 2 +- package-lock.json | 2177 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 2179 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index bdae662b9..e571a42da 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -127,7 +127,7 @@ module.exports = { "no-constant-condition": ["error", { "checkLoops": false }], "no-extra-label": "error", //"no-labels": ["error", {"allowLoop": true}], // in the future no loops ;) - "no-unused-vars": ["warn", { "vars": "local" }], + "no-unused-vars": ["warn", { "vars": "local", "varsIgnorePattern": "^_" }], "no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted" }], "no-undef": ["off", "always"], "no-extra-boolean-cast": ["off", "always"], diff --git a/package-lock.json b/package-lock.json index 92390d9be..467e21e66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,1196 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { + "eslint": "^7.32.0", "typescript": "^4.9.3" } }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.6", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "4.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", @@ -24,14 +1211,1004 @@ "engines": { "node": ">=4.2.0" } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true } }, "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.24.6", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, "typescript": { "version": "4.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true } } } diff --git a/package.json b/package.json index 353e38901..466eef1b3 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "devDependencies": { + "eslint": "^7.32.0", "typescript": "^4.9.3" } } From 94e304ec02ab43f97e847c5b175f85bc2c38f686 Mon Sep 17 00:00:00 2001 From: Jan Stoots <31186222+jaenster@users.noreply.github.com> Date: Wed, 29 May 2024 18:35:39 +0200 Subject: [PATCH 420/758] Feature/wp cache (#410) * Add support for OOG script to cache the waypoint data * Removed debug print statements * Re-worked waypoint caching - Added WpWatcher worker for our main ingame thread, the out of game version running from our entry script was causing crashes. - Handle requesting our wp-cache inside the pather.init method - Add parameter to optionally use orignal getWaypoint method - If we come across a mismatch between our cache and our current wplist, update the cache - Create `me.waypoints` for storing our waypoint data * Update D2BotLead.dbj --------- Co-authored-by: theBGuy <60308670+theBGuy@users.noreply.github.com> --- d2bs/kolbot/D2BotFollow.dbj | 2 +- d2bs/kolbot/D2BotLead.dbj | 1 - d2bs/kolbot/default.dbj | 3 + d2bs/kolbot/libs/OOG.js | 36 +++++ d2bs/kolbot/libs/core/Me.js | 25 +++ d2bs/kolbot/libs/core/Pather.js | 152 ++++++++++++------ d2bs/kolbot/libs/modules/workers/WpWatcher.js | 40 +++++ d2bs/kolbot/sdk/globals.d.ts | 3 +- 8 files changed, 209 insertions(+), 53 deletions(-) create mode 100644 d2bs/kolbot/libs/modules/workers/WpWatcher.js diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 1fd0a78b5..805e79b39 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -58,7 +58,7 @@ const locationAction = (function () { locations, addLocations } = require("./libs/oog/Locations"); - + /** @param {string} leader */ const joinCheck = function (leader) { D2Bot.requestGame(leader); diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 07fa375dc..17ea78a17 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -112,7 +112,6 @@ function main () { } DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); - while (true) { // returns true before actually in game so we can't only use this check while (me.ingame) { diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 45ba18403..4364c2d84 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -184,6 +184,9 @@ function main () { addEventListener("itemaction", Pickit.itemEvent); } + // waypoint cacher + require("libs/modules/workers/WpWatcher"); + // 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 diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index d98a3319f..acc4a9d9a 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -502,6 +502,8 @@ includeIfNotIncluded("core/Me.js"); delay(500); Controls.PopupYes.click(); delay(500); + // delete wp cache if it exists + delete Starter.waypointCache[info.charName]; return true; } catch (e) { @@ -1080,7 +1082,25 @@ includeIfNotIncluded("core/Me.js"); Controls.BottomLeftExit.click(); }, + waypointCache: {}, + scriptMsgEvent: function (msg) { + if (typeof msg === "object" + && msg.hasOwnProperty("type") + && msg.type === "cache-waypoints" + && msg.hasOwnProperty("data") + && Array.isArray(msg.data)) { + + // Upsert array so it exists + let arr = typeof Starter.waypointCache[me.charname] === "object" + ? Starter.waypointCache[me.charname] + // 3 elements of nothing + : Starter.waypointCache[me.charname] = [undefined, undefined, undefined]; + arr[me.diff] = msg.data; + + return; + } + if (msg && typeof msg !== "string") return; switch (msg) { case "mule": @@ -1116,6 +1136,22 @@ includeIfNotIncluded("core/Me.js"); case "pingquit": Starter.pingQuit = true; + break; + + case "get-cached-waypoints": + if (!me.ingame) { + break; + } + + if (typeof Starter.waypointCache[me.charname] === "object" + && Starter.waypointCache[me.charname].length === 3) { + let arr = Starter.waypointCache[me.charname]; + const cache = arr[me.diff]; + if (cache) { + scriptBroadcast({ type: "wp-cache", data: cache }); + } + } + break; } }, diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 8e5c42910..acf5bcaaa 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -1102,6 +1102,31 @@ Object.defineProperties(me, { (function () { const QuestData = require("./GameData/QuestData"); + const AMOUNT_OF_WAYPOINTS = [ + sdk.waypoints.Act1, + sdk.waypoints.Act2, + sdk.waypoints.Act3, + sdk.waypoints.Act4, + sdk.waypoints.Act5 + ].flat().length; + /** @type {boolean[]} */ + const _cachedWaypoints = new Array(AMOUNT_OF_WAYPOINTS).fill(false); + + Object.defineProperty(me, "waypoints", { + get: function () { + return _cachedWaypoints; + }, + /** @param {boolean[]} value */ + set: function (value) { + if (!Array.isArray(value)) return; + value.forEach(function (val, index) { + if (index < AMOUNT_OF_WAYPOINTS) { + _cachedWaypoints[index] = val; + } + }); + } + }); + /** * @param {number} act * @returns {boolean} diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 7c2b5bf17..77232e5ae 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -26,7 +26,7 @@ const NodeAction = { /** * Run all the functions within NodeAction (except for itself) - * @param {clearSettings} arg + * @param {clearSettings} arg */ go: function (arg) { if (!this.enabled) return; @@ -39,7 +39,7 @@ const NodeAction = { /** * Kill monsters while pathing - * @param {clearSettings} arg + * @param {clearSettings} arg * @returns {void} */ killMonsters: function (arg = {}) { @@ -112,7 +112,7 @@ const PathDebug = { /** * Draw our path on the screen - * @param {PathNode[]} path + * @param {PathNode[]} path * @returns {void} */ drawPath: function (path) { @@ -137,9 +137,9 @@ const PathDebug = { /** * Check if a set of coords are a set path - * @param {PathNode[]} path - * @param {number} x - * @param {number} y + * @param {PathNode[]} path + * @param {number} x + * @param {number} y * @returns {boolean} */ coordsInPath: function (path, x, y) { @@ -200,14 +200,36 @@ const Pather = { ], nextAreas: {}, + /** @param {{ type: string, data: number[] }} msg */ + cacheListener: function (msg) { + if (typeof msg !== "object" || !msg /*null*/) return; + if (typeof msg.type === "undefined") return; + if (msg.type !== "wp-cache") return; + if (typeof msg.data !== "object") return; + if (!Array.isArray(msg.data)) return; + if (msg.data.length !== Pather.wpAreas.length) return; + + me.waypoints = msg.data; + + // Waypoint data is set + Pather.initialized = true; + }, + init: function () { if (!this.initialized) { + addEventListener("scriptmsg", Pather.cacheListener); me.classic && (Pather.nonTownWpAreas = this.nonTownWpAreas.filter((wp) => wp < sdk.areas.Harrogath)); - if (!Config.WaypointMenu) { + + scriptBroadcast("get-cached-waypoints"); + delay(500); + + if (!Config.WaypointMenu && !Pather.initialized) { !getWaypoint(1) && this.getWP(me.area); me.cancelUIFlags(); Pather.initialized = true; } + + removeEventListener("scriptmsg", Pather.cacheListener); } }, @@ -229,11 +251,11 @@ const Pather = { * @property {number} [area] * @property {number} [reductionType] * @property {number} [coll] - * @property {boolean} [returnSpotOnError] + * @property {boolean} [returnSpotOnError] * - * @param {PathNode} spot - * @param {number} distance - * @param {spotOnDistanceSettings} givenSettings + * @param {PathNode} spot + * @param {number} distance + * @param {spotOnDistanceSettings} givenSettings * @returns {PathNode} */ spotOnDistance: function (spot, distance, givenSettings = {}) { @@ -245,7 +267,7 @@ const Pather = { }, givenSettings); let nodes = (getPath(spotSettings.area, me.x, me.y, spot.x, spot.y, spotSettings.reductionType, 4) || []); - + if (!nodes.length) { if (spotSettings.reductionType === 2) { // try again with walking reduction @@ -272,15 +294,15 @@ const Pather = { * @property {boolean} [returnSpotOnError] * @property {Function} [callback] * @property {clearSettings} [clearSettings] - * + * * @typedef {object} clearSettings * @property {boolean} [clearSettings.clearPath] * @property {number} [clearSettings.range] * @property {number} [clearSettings.specType] * @property {Function} [clearSettings.sort] * - * @param {PathNode | Unit | PresetUnit} target - * @param {pathSettings} givenSettings + * @param {PathNode | Unit | PresetUnit} target + * @param {pathSettings} givenSettings * @returns {boolean} */ move: function (target, givenSettings = {}) { @@ -531,10 +553,10 @@ const Pather = { }, /** - * @param {number} x - * @param {number} y - * @param {number} minDist - * @param {pathSettings} givenSettings + * @param {number} x + * @param {number} y + * @param {number} minDist + * @param {pathSettings} givenSettings * @returns {boolean} */ moveNear: function (x, y, minDist, givenSettings = {}) { @@ -554,11 +576,11 @@ const Pather = { }, /** - * - * @param {number} x - * @param {number} y - * @param {pathSettings} givenSettings - * @returns + * + * @param {number} x + * @param {number} y + * @param {pathSettings} givenSettings + * @returns */ moveToEx: function (x, y, givenSettings = {}) { return Pather.move({ x: x, y: y }, givenSettings); @@ -794,7 +816,7 @@ const Pather = { } let monstawall = Game.getMonster("barricade"); - + if (monstawall) { do { if (monstawall.hp > 0 && (getDistance(monstawall, x, y) < 4 @@ -987,12 +1009,12 @@ const Pather = { * @todo * moveTo/NearPresetTile */ - + /** - * - * @param {number} area - * @param {number} unitId - * @param {pathSettings} givenSettings + * + * @param {number} area + * @param {number} unitId + * @param {pathSettings} givenSettings */ moveToPresetObject: function (area, unitId, givenSettings = {}) { if (area === undefined || unitId === undefined) { @@ -1020,10 +1042,10 @@ const Pather = { }, /** - * - * @param {number} area - * @param {number} unitId - * @param {pathSettings} givenSettings + * + * @param {number} area + * @param {number} unitId + * @param {pathSettings} givenSettings */ moveToPresetMonster: function (area, unitId, givenSettings = {}) { if (area === undefined || unitId === undefined) { @@ -1069,10 +1091,10 @@ const Pather = { 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; @@ -1140,8 +1162,8 @@ const Pather = { }, /** - * @param {number} area - * @param {number} exit + * @param {number} area + * @param {number} exit * @returns {number} */ getDistanceToExit: function (area, exit) { @@ -1157,8 +1179,8 @@ const Pather = { }, /** - * @param {number} area - * @param {number} exit + * @param {number} area + * @param {number} exit * @returns {PathNode | false} */ getExitCoords: function (area, exit) { @@ -1264,7 +1286,7 @@ const Pather = { + (!!targetArea ? " TargetArea: " + getAreaName(targetArea) : "") ); } - + return unit.useUnit(targetArea); }, @@ -1501,7 +1523,7 @@ const Pather = { .first(); !!oldPortal && (oldGid = oldPortal.gid); - + if (tpTool.use() || Game.getObject("portal")) { let tick = getTickCount(); @@ -1595,7 +1617,7 @@ const Pather = { } else { let timeTillNextPortal = Math.max(3, Math.round(2500 - (getTickCount() - this.lastPortalTick))); delay(timeTillNextPortal); - + continue; } } @@ -1677,7 +1699,7 @@ const Pather = { * @param {number} range - maximum allowed range from the starting coords * @param {number} step - distance between each checked dot on the grid * @param {number} coll - collision flag to avoid - * @param {number} size + * @param {number} size * @returns {[number, number] | false} */ getNearestWalkable: function (x, y, range, step, coll, size) { @@ -1720,7 +1742,7 @@ const Pather = { * @param {number} y - the y coord to check * @param {number} coll - collision flag to search for * @param {boolean} cacheOnly - use only cached room data - * @param {number} size + * @param {number} size * @returns {boolean} */ checkSpot: function (x, y, coll, cacheOnly, size) { @@ -1753,7 +1775,7 @@ const Pather = { /** * @param {number} area - the id of area to get the waypoint in - * @param {boolean} [clearPath] + * @param {boolean} [clearPath] * @returns {boolean} */ getWP: function (area, clearPath) { @@ -1857,7 +1879,7 @@ const Pather = { if (!me.inTown) { Precast.doPrecast(false); - + if (this.wpAreas.includes(currArea) && !getWaypoint(this.wpAreas.indexOf(currArea))) { this.getWP(currArea); @@ -1866,7 +1888,7 @@ const Pather = { if (me.inTown && this.nextAreas[currArea] !== targetArea && this.wpAreas.includes(targetArea) && getWaypoint(this.wpAreas.indexOf(targetArea))) { - this.useWaypoint(targetArea, !this.plotCourse_openedWpMenu); + this.useWaypoint(targetArea, !Pather.initialized); Precast.doPrecast(false); } else if (currArea === sdk.areas.StonyField && targetArea === sdk.areas.Tristram) { // Stony Field -> Tristram @@ -2068,8 +2090,11 @@ const Pather = { !src && (src = me.area); - if (!this.plotCourse_openedWpMenu && me.inTown && this.nextAreas[me.area] !== dest && Pather.useWaypoint(null)) { - Pather.plotCourse_openedWpMenu = true; + if (!Pather.initialized + && me.inTown + && Pather.nextAreas[me.area] !== dest + && Pather.useWaypoint(null)) { + Pather.initialized = true; } while (toVisitNodes.length > 0) { @@ -2197,3 +2222,30 @@ Pather.nextAreas[sdk.areas.LutGholein] = sdk.areas.RockyWaste; Pather.nextAreas[sdk.areas.KurastDocktown] = sdk.areas.SpiderForest; Pather.nextAreas[sdk.areas.PandemoniumFortress] = sdk.areas.OuterSteppes; Pather.nextAreas[sdk.areas.Harrogath] = sdk.areas.BloodyFoothills; + +/** + * Trick to let the OOG script cache the getWaypoint + * @param {Object} globalThis + * @param {(id: number) => boolean} original + */ +(function (globalThis, original) { + globalThis._getWaypoint = original; + + globalThis.getWaypoint = function (id, noCache = false) { + if (noCache) { + return original(id); + } + // You got it + if (me.waypoints[id]) { + return true; + } + // You cant lose a wp, you can gain one. Store the result + const result = original(id); + if (result !== me.waypoints[id]) { + // we've got a mismatch, update the cache + me.waypoints[id] = result; + scriptBroadcast({ type: "cache-waypoints", data: me.waypoints }); + } + return result; + }; +})([].filter.constructor("return this")(), getWaypoint); diff --git a/d2bs/kolbot/libs/modules/workers/WpWatcher.js b/d2bs/kolbot/libs/modules/workers/WpWatcher.js new file mode 100644 index 000000000..686bc9920 --- /dev/null +++ b/d2bs/kolbot/libs/modules/workers/WpWatcher.js @@ -0,0 +1,40 @@ +/** +* @filename WpWatcher.js +* @author theBGuy +* @desc Worker script for cacheing and watching waypoints +* +*/ + +(function (module, require, Worker) { + // Only load this in global scope + if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { + let waitTick = getTickCount(); + let done = false; + + // Start + Worker.runInBackground.WpWatcher = function () { + if (done) return true; + if (getTickCount() - waitTick < 100) return true; + waitTick = getTickCount(); + if (!me.gameReady) return true; + + // Waypoint is open, so lets cache it + if (!getUIFlag(sdk.uiflags.Waypoint)) { + return true; + } + + // Cache the waypoints + const waypoints = Pather.wpAreas.map(function (area, index) { + return getWaypoint(index, true); + }); + me.waypoints = waypoints; + Pather.initialized = true; + scriptBroadcast({ type: "cache-waypoints", data: waypoints }); + done = true; + + return true; + }; + + console.log("ÿc2Kolbotÿc0 :: Waypoint Watcher running"); + } +})(module, require, typeof Worker === "object" && Worker || require("../Worker")); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 3162e02be..83dadc424 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -679,6 +679,7 @@ declare global { * @description max gold capacity (cLvl * 10000) */ readonly maxgold: number; + waypoints: boolean[]; // d2bs functions overhead(msg: string): void; @@ -782,7 +783,7 @@ declare global { function getThreadPriority(): number function getUIFlag(flag: number): boolean function getTradeInfo(mode: 0 | 1 | 2): boolean - function getWaypoint(id: number): boolean + function getWaypoint(id: number, noCache?: boolean): boolean class Script { running: boolean; From 7cc739248511f95cbdb6d72f80f5043d6abbc6de Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 29 May 2024 12:38:38 -0400 Subject: [PATCH 421/758] Add room drawing hooks to collmap - For debugging it's helpful to be able to see the rooms, add missing jsdoc comments as well --- d2bs/kolbot/libs/core/CollMap.js | 107 +++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/CollMap.js b/d2bs/kolbot/libs/core/CollMap.js index 07addd40c..88f1af755 100644 --- a/d2bs/kolbot/libs/core/CollMap.js +++ b/d2bs/kolbot/libs/core/CollMap.js @@ -8,7 +8,61 @@ const CollMap = new function () { this.rooms = []; this.maps = []; + /** @type {Line[]} */ + this.hooks = []; + this.colors = { + green: 0x84, + red: 0x0a, + black: 0x00, + white: 0xff, + purple: 0x9b, + blue: 0x97, + }; + + /** + * @param {Room} room + * @param {('green' | 'red' | 'black' | 'white' | 'purple' | 'blue' | number)} [color='green'] + * @param {boolean} [update=false] + * @returns {void} + */ + this.drawRoom = function (room, color = "green", update = false) { + let idx = this.hooks.findIndex(h => h.room.x === room.x && h.room.y === room.y); + if (idx >= 0) { + if (!update) return; + this.hooks[idx].lines.forEach(l => l.remove()); + this.hooks.splice(idx, 1); + } + const lineColor = typeof color === "string" + ? (color in this.colors) ? this.colors[color] : this.colors.green + : color; + let lines = [ + new Line(room.x * 5, room.y * 5, room.x * 5 + room.xsize, room.y * 5, lineColor, true), + new Line(room.x * 5 + room.xsize, room.y * 5, room.x * 5 + room.xsize, room.y * 5 + room.ysize, lineColor, true), + new Line(room.x * 5 + room.xsize, room.y * 5 + room.ysize, room.x * 5, room.y * 5 + room.ysize, lineColor, true), + new Line(room.x * 5, room.y * 5 + room.ysize, room.x * 5, room.y * 5, lineColor, true), + ]; + this.hooks.push({ room: room, lines: lines }); + }; + + /** @param {Room} room */ + this.removeHookForRoom = function (room) { + let index = this.hooks.findIndex(h => h.room.x === room.x && h.room.y === room.y); + if (index !== -1) { + this.hooks[index].lines.forEach(l => l.remove()); + this.hooks.splice(index, 1); + } + }; + + this.removeHooks = function () { + this.hooks.forEach(hook => hook.lines.forEach(l => l.remove())); + this.hooks = []; + }; + /** + * @param {number} x + * @param {number} y + * @returns {boolean} + */ this.getNearbyRooms = function (x, y) { let room = getRoom(x, y); if (!room) return false; @@ -26,6 +80,11 @@ const CollMap = new function () { return true; }; + /** + * @param {number | Room} x + * @param {number} [y] + * @returns {boolean} + */ this.addRoom = function (x, y) { let room = x instanceof Room ? x : getRoom(x, y); @@ -46,6 +105,12 @@ const CollMap = new function () { return false; }; + /** + * @param {number} x + * @param {number} y + * @param {boolean} [cacheOnly] + * @returns {boolean} + */ this.getColl = function (x, y, cacheOnly) { let index = this.getRoomIndex(x, y, cacheOnly); @@ -63,6 +128,12 @@ const CollMap = new function () { return 5; }; + /** + * @param {number} x + * @param {number} y + * @param {boolean} [cacheOnly] + * @returns {number | undefined} + */ this.getRoomIndex = function (x, y, cacheOnly) { this.rooms.length > 25 && this.reset(); @@ -81,6 +152,12 @@ const CollMap = new function () { return undefined; }; + /** + * @param {number} x + * @param {number} y + * @param {Room} room + * @returns {boolean} + */ this.coordsInRoom = function (x, y, room) { if (room && x >= room.x * 5 && x < room.x * 5 + room.xsize && y >= room.y * 5 && y < room.y * 5 + room.ysize) { return true; @@ -94,8 +171,15 @@ const CollMap = new function () { this.maps = []; }; - // Check collision between unitA and unitB. true = collision present, false = collision not present - // If checking for blocking collisions (0x1, 0x4), true means blocked, false means not blocked + /** + * Check collision between unitA and unitB. true = collision present, false = collision not present + * If checking for blocking collisions (0x1, 0x4), true means blocked, false means not blocked + * @param {Unit | PathNode} unitA + * @param {Unit | PathNode} unitB + * @param {number} coll + * @param {number} thickness + * @returns {boolean} + */ this.checkColl = function (unitA, unitB, coll, thickness) { thickness === undefined && (thickness = 1); @@ -120,13 +204,18 @@ const CollMap = new function () { return false; }; + /** + * @param {Room} room + * @returns {PathNode} + */ this.getTelePoint = function (room) { // returns {x, y, distance} of a valid point with lowest distance from room center // distance is from room center, handy for keeping bot from trying to teleport on walls if (!room) throw new Error("Invalid room passed to getTelePoint"); - let roomx = room.x * 5, roomy = room.y * 5; + let roomx = room.x * 5; + let roomy = room.y * 5; if (getCollision(room.area, roomx, roomy) & 1) { let collision = room.getCollision(), validTiles = []; @@ -156,6 +245,16 @@ const CollMap = new function () { return { x: roomx, y: roomy, distance: 0 }; }; + /** + * @param {number} cX + * @param {number} xmin + * @param {number} xmax + * @param {number} cY + * @param {number} ymin + * @param {number} ymax + * @param {number} factor + * @returns {PathNode} + */ this.getRandCoordinate = function (cX, xmin, xmax, cY, ymin, ymax, factor = 1) { // returns randomized {x, y} object with valid coordinates let coordX, coordY; @@ -182,6 +281,6 @@ const CollMap = new function () { } while (getCollision(me.area, coordX, coordY) & 1); // console.log("Move " + retry + " from (" + cX + ", " + cY + ") to (" + coordX + ", " + coordY + ")"); - return { x: coordX, y: coordY }; + return new PathNode(coordX, coordY); }; }; From 6d956bf722890e42be2c2bc3577e0bbd8ae829ce Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 29 May 2024 12:43:29 -0400 Subject: [PATCH 422/758] Create Graph.js - Add ryan's Graph module to start working on improving clearlevel pathing - Implemented caching to speed up sorting algo's - Discard non-walkable rooms during initialization - Modified nearestNeighborSearch to check for nearbyVertices so we can elimate corner rooms faster instead of having to run back to them later --- d2bs/kolbot/libs/modules/Graph.js | 283 ++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 d2bs/kolbot/libs/modules/Graph.js diff --git a/d2bs/kolbot/libs/modules/Graph.js b/d2bs/kolbot/libs/modules/Graph.js new file mode 100644 index 000000000..e354f5a6a --- /dev/null +++ b/d2bs/kolbot/libs/modules/Graph.js @@ -0,0 +1,283 @@ +/** + * @author ryancrunchi, theBGuy + * @description Graph algorithms implementation for rooms exploration. + */ + +(function (module) { + /** + * Wrapper class for room as vertex + * @constructor + * @param {Room} room + */ + function Vertex(room) { + this.id = Vertex._id++; + this.centerX = room.x * 5 + room.xsize / 2; + this.centerY = room.y * 5 + room.ysize / 2; + this.x = room.x; + this.y = room.y; + this.xsize = room.xsize; + this.ysize = room.ysize; + this.seen = false; + this.walkableX = this.centerX; + this.walkableY = this.centerY; + this.area = room.level; + let adjusted = Pather.getNearestWalkable(this.centerX, this.centerY, 20, 10); + if (!adjusted) { + throw new Error("Vertex is not walkable"); + } + this.walkableX = adjusted[0]; + this.walkableY = adjusted[1]; + + this.cache = {}; + this.clearCache = function() { + this.cache = {}; + }; + + this.walkablePath = function() { + if (this.cache.walkablePath) { + return this.cache.walkablePath; + } + let path = getPath(this.area, me.x, me.y, this.walkableX, this.walkableY, 0, Pather.walkDistance); + this.cache.walkablePath = path; + return path; + }; + + this.walkablePathDistance = function() { + if (this.cache.walkablePathDistance) { + return this.cache.walkablePathDistance; + } + let path = this.walkablePath(); + if (!path.length) { + return Infinity; + } + let distance = 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); + this.cache.walkablePathDistance = distance; + return distance; + }; + + /** + * @this {Vertex} + * @param {Vertex} other + * @returns {Array<{x: number, y: number}>} + */ + this.walkablePathTo = function(other) { + const { area, walkableX, walkableY } = this; + if (this.cache.walkablePathTo && this.cache.walkablePathTo[other.id]) { + return this.cache.walkablePathTo[other.id]; + } + let path = getPath(area, walkableX, walkableY, other.walkableX, other.walkableY, 0, Pather.walkDistance); + if (!this.cache.walkablePathTo) { + this.cache.walkablePathTo = {}; + } + this.cache.walkablePathTo[other.id] = path; + return path; + }; + + /** + * @param {Vertex} other + * @returns {number} + */ + this.walkablePathDistanceTo = function(other) { + if (this.cache.walkablePathDistanceTo && this.cache.walkablePathDistanceTo[other.id]) { + return this.cache.walkablePathDistanceTo[other.id]; + } + let path = this.walkablePathTo(other); + if (!path.length) { + return Infinity; + } + let distance = 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); + if (!this.cache.walkablePathDistanceTo) { + this.cache.walkablePathDistanceTo = {}; + } + this.cache.walkablePathDistanceTo[other.id] = distance; + return distance; + }; + } + + Vertex._id = 0; + + /** + * @description Graph class to handle vertices and search algorithms + * @constructor + */ + function Graph() { + CollMap.removeHooks(); + /** @type {Vertex[]} */ + this.vertices = []; + + // TODO: We should eliminate rooms that are over 80% unwalkable + let room = getRoom(); + if (room) { + do { + try { + let vertex = new Vertex(room); + this.vertices.push(vertex); + CollMap.drawRoom(copyObj(room), "blue"); + } catch (e) { + CollMap.drawRoom(copyObj(room), "red"); + } + } while (room.getNext()); + } + + this.vertices.sort(function (a, b) { + return getDistance(me.x, me.y, a.walkableX, a.walkableY) - getDistance(me.x, me.y, b.walkableX, b.walkableY); + }); + + /** + * get the graph vertex from room object + * @param {Room} room + */ + this.vertexForRoom = function (room) { + return this.vertices.find(function (v) { + return v.x === room.x && v.y === room.y; + }); + }; + + /** + * get the room the vertex is in + * @param {Vertex} vertex + * @returns {Room} + */ + this.roomForVertex = function (vertex) { + return getRoom(vertex.centerX, vertex.centerY); + }; + + /** + * get nearby vertices from vertex (child) by getting neaby rooms. + * @param {Vertex} vertex + * @returns + */ + this.nearbyVertices = function (vertex) { + let room = this.roomForVertex(vertex); + if (!room) { + return []; + } + const self = this; + return room.getNearby() + .compactMap(function (r) { + return self.vertexForRoom(r); + }); + //.sort((a, b) => a.adjustedPathDistance - b.adjustedPathDistance); + }; + } + + // eslint-disable-next-line no-unused-vars + Graph.customSearch = function(graph, explore) { + + }; + + /** @param {Vertex} v */ + const filterSeen = function (v) { + return !v.seen; + }; + + /** + * @param {Graph} graph + * @param {(vertex: Vertex) => any} explore + */ + Graph.nearestNeighbourSearch = function(graph, explore) { + let currentVertex = graph.vertices.filter(filterSeen).first(); + while (currentVertex) { + CollMap.drawRoom(graph.roomForVertex(currentVertex), "green", true); + + explore(currentVertex); + // currentVertex.seen = true; // not working when it comes from neabies array, it should be referenced from graph.vertices array + graph.vertices.find(function (v) { return v === currentVertex; }).seen = true; + CollMap.drawRoom(graph.roomForVertex(currentVertex), "purple", true); + let nearbies = graph.nearbyVertices(currentVertex) + .filter(filterSeen) + .sort(function (a, b) { + // First sort by number of neighbors (ascending) + let diff = graph.nearbyVertices(a).length - graph.nearbyVertices(b).length; + if (diff !== 0) return diff; + // If number of neighbors is the same, sort by walkable path distance (ascending) + return currentVertex.walkablePathDistanceTo(a) - currentVertex.walkablePathDistanceTo(b); + }); + nearbies.forEach(function (n) { + CollMap.drawRoom(graph.roomForVertex(n), "white", true); + }); + currentVertex = nearbies.first() || + // if no neihbors is found, get next nearest vertex in graph + graph.vertices + .filter(filterSeen) + .sort(function (a, b) { + return a.walkablePathDistance() - b.walkablePathDistance(); + }) + .first(); + for (let vertice of graph.vertices) { + vertice.clearCache(); + } + } + + //TODO: sometimes, the bot leaves a small group of vertices alone, and continues to the biggest part of the graph + // this leads the bot to go to this small group at the end and it is not optimal. It should have gone to this small group before finishing all the rest + // we need to construct get disconnected parts of graph and go to the nearest smallest part before continuing + }; + + /** + * DFS implementation + * exploreFunction is a function called for every explored vertex in the graph that takes a vertex as parameter + * @param {Graph} graph + * @param {(vertex: Vertex) => any} exploreFunction + */ + Graph.depthFirstSearch = function(graph, exploreFunction) { + /** @type {Vertex[]} */ + let stack = []; + let startVertex = graph.vertices.first(); + stack.push(startVertex); + + while (stack.length) { + let vertex = stack.pop(); + if (vertex.seen) continue; + exploreFunction(vertex); + vertex.seen = true; + + CollMap.drawRoom(vertex, "green", true); + let neighbors = graph.nearbyVertices(vertex).filter(filterSeen); + for (let i = 0; i < neighbors.length; i++) { + stack.push(neighbors[i]); + CollMap.drawRoom(neighbors[i], "purple", true); + } + console.time("sort"); + stack.sort(function (a, b) { + return b.walkablePathDistance() - a.walkablePathDistance(); + }); + console.timeEnd("sort"); + // clear cache for all vertices + for (let vertice of graph.vertices) { + vertice.clearCache(); + } + } + }; + + /** + * BFS implementation + * exploreFunction is a function called for every explored vertex in the graph that takes a vertex as parameter + * @param {Graph} graph + * @param {(vertex: Vertex) => any} exploreFunction + */ + Graph.breadthFirstSearch = function(graph, exploreFunction) { + let queue = []; + let startVertex = graph.vertices.first(); + queue.push(startVertex); + while (queue.length) { + let vertex = queue.shift(); + let neighbors = graph.nearbyVertices(vertex).filter(filterSeen); + for (let i = 0; i < neighbors.length; i++) { + queue.push(neighbors[i]); + neighbors[i].seen = true; + } + exploreFunction(vertex); + vertex.seen = true; + CollMap.drawRoom(vertex, "green", true); + } + }; + + module.exports = Graph; +})(module, require); From 604bef04323ff2006b681246d4ed341025eff23b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 29 May 2024 19:23:48 -0400 Subject: [PATCH 423/758] Cleanup + refactor Graph - Modified the methods so we can handle walking or teleport exploration - Fixed over valuing nearbyVertices in `nearestNeighborSearch`, so far this method seems most efficient in cave exploration --- d2bs/kolbot/libs/modules/Graph.js | 195 ++++++++++++++++++------------ 1 file changed, 117 insertions(+), 78 deletions(-) diff --git a/d2bs/kolbot/libs/modules/Graph.js b/d2bs/kolbot/libs/modules/Graph.js index e354f5a6a..b0450a3ca 100644 --- a/d2bs/kolbot/libs/modules/Graph.js +++ b/d2bs/kolbot/libs/modules/Graph.js @@ -21,6 +21,7 @@ this.walkableX = this.centerX; this.walkableY = this.centerY; this.area = room.level; + // Should the step be lowered? let adjusted = Pather.getNearestWalkable(this.centerX, this.centerY, 20, 10); if (!adjusted) { throw new Error("Vertex is not walkable"); @@ -28,79 +29,108 @@ this.walkableX = adjusted[0]; this.walkableY = adjusted[1]; + /** @type {Record} */ this.cache = {}; - this.clearCache = function() { - this.cache = {}; - }; - - this.walkablePath = function() { - if (this.cache.walkablePath) { - return this.cache.walkablePath; - } - let path = getPath(this.area, me.x, me.y, this.walkableX, this.walkableY, 0, Pather.walkDistance); - this.cache.walkablePath = path; - return path; - }; + } - this.walkablePathDistance = function() { - if (this.cache.walkablePathDistance) { - return this.cache.walkablePathDistance; - } - let path = this.walkablePath(); - if (!path.length) { - return Infinity; - } - let distance = 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); - this.cache.walkablePathDistance = distance; - return distance; - }; + /** @static */ + Vertex._id = 0; - /** - * @this {Vertex} - * @param {Vertex} other - * @returns {Array<{x: number, y: number}>} - */ - this.walkablePathTo = function(other) { - const { area, walkableX, walkableY } = this; - if (this.cache.walkablePathTo && this.cache.walkablePathTo[other.id]) { - return this.cache.walkablePathTo[other.id]; - } - let path = getPath(area, walkableX, walkableY, other.walkableX, other.walkableY, 0, Pather.walkDistance); - if (!this.cache.walkablePathTo) { - this.cache.walkablePathTo = {}; - } - this.cache.walkablePathTo[other.id] = path; - return path; - }; + /** @this {Vertex} */ + Vertex.prototype.clearCache = function() { + this.cache = {}; + }; - /** - * @param {Vertex} other - * @returns {number} - */ - this.walkablePathDistanceTo = function(other) { - if (this.cache.walkablePathDistanceTo && this.cache.walkablePathDistanceTo[other.id]) { - return this.cache.walkablePathDistanceTo[other.id]; - } - let path = this.walkablePathTo(other); - if (!path.length) { - return Infinity; - } - let distance = 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); - if (!this.cache.walkablePathDistanceTo) { - this.cache.walkablePathDistanceTo = {}; - } - this.cache.walkablePathDistanceTo[other.id] = distance; - return distance; - }; - } + /** + * @param {"walk" | "teleport"} mode + * @returns {PathNode[]} + */ + Vertex.prototype.path = function(mode = "walk") { + const key = mode + "Path"; + if (this.cache[key]) { + return this.cache[key]; + } + const x = mode === "walk" ? this.walkableX : this.centerX; + const y = mode === "walk" ? this.walkableY : this.centerY; + const rType = mode === "walk" ? 0 : 1; + const nDist = mode === "walk" ? Pather.walkDistance : Pather.teleDistance; + let path = getPath(this.area, me.x, me.y, x, y, rType, nDist); + this.cache[key] = path; + return path; + }; - Vertex._id = 0; + /** + * @param {"walk" | "teleport"} mode + * @returns {number} + */ + Vertex.prototype.pathDistance = function(mode = "walk") { + const key = mode + "PathDistance"; + if (this.cache[key]) { + return this.cache[key]; + } + let path = this.path(mode); + if (!path.length) { + return Infinity; + } + let distance = 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); + this.cache[key] = distance; + return distance; + }; + + /** + * @this {Vertex} + * @param {Vertex} other + * @param {"walk" | "teleport"} mode + * @returns {Array<{x: number, y: number}>} + */ + Vertex.prototype.pathTo = function(other, mode = "walk") { + const key = mode + "PathTo"; + if (this.cache[key] && this.cache[key][other.id]) { + return this.cache[key][other.id]; + } + const area = this.area; + const rType = mode === "walk" ? 0 : 1; + const nDist = mode === "walk" ? Pather.walkDistance : Pather.teleDistance; + const x = mode === "walk" ? this.walkableX : this.centerX; + const y = mode === "walk" ? this.walkableY : this.centerY; + const otherX = mode === "walk" ? other.walkableX : other.centerX; + const otherY = mode === "walk" ? other.walkableY : other.centerY; + + let path = getPath(area, x, y, otherX, otherY, rType, nDist); + if (!this.cache[key]) { + this.cache[key] = {}; + } + this.cache[key][other.id] = path; + return path; + }; + + /** + * @param {Vertex} other + * @param {"walk" | "teleport"} mode + * @returns {number} + */ + Vertex.prototype.pathDistanceTo = function(other, mode = "walk") { + const key = mode + "PathDistanceTo"; + if (this.cache[key] && this.cache[key][other.id]) { + return this.cache[key][other.id]; + } + let path = this.pathTo(other, mode); + if (!path.length) { + return Infinity; + } + let distance = 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); + if (!this.cache[key]) { + this.cache[key] = {}; + } + this.cache[key][other.id] = distance; + return distance; + }; /** * @description Graph class to handle vertices and search algorithms @@ -180,8 +210,9 @@ /** * @param {Graph} graph * @param {(vertex: Vertex) => any} explore + * @param {"walk" | "teleport"} mode */ - Graph.nearestNeighbourSearch = function(graph, explore) { + Graph.nearestNeighbourSearch = function(graph, explore, mode = "walk") { let currentVertex = graph.vertices.filter(filterSeen).first(); while (currentVertex) { CollMap.drawRoom(graph.roomForVertex(currentVertex), "green", true); @@ -193,11 +224,18 @@ let nearbies = graph.nearbyVertices(currentVertex) .filter(filterSeen) .sort(function (a, b) { - // First sort by number of neighbors (ascending) - let diff = graph.nearbyVertices(a).length - graph.nearbyVertices(b).length; - if (diff !== 0) return diff; + let distanceToA = currentVertex.pathDistanceTo(a, mode); + let distanceToB = currentVertex.pathDistanceTo(b, mode); + let distDiff = Math.abs(distanceToA - distanceToB); + + // If the difference is less than 5% of the distance to a, sort by number of neighbors + if (distDiff / distanceToA < 0.05) { + // First sort by number of neighbors (ascending) + let diff = graph.nearbyVertices(a).length - graph.nearbyVertices(b).length; + if (diff !== 0) return diff; + } // If number of neighbors is the same, sort by walkable path distance (ascending) - return currentVertex.walkablePathDistanceTo(a) - currentVertex.walkablePathDistanceTo(b); + return distanceToA - distanceToB; }); nearbies.forEach(function (n) { CollMap.drawRoom(graph.roomForVertex(n), "white", true); @@ -207,7 +245,7 @@ graph.vertices .filter(filterSeen) .sort(function (a, b) { - return a.walkablePathDistance() - b.walkablePathDistance(); + return a.pathDistance(mode) - b.pathDistance(mode); }) .first(); for (let vertice of graph.vertices) { @@ -225,8 +263,9 @@ * exploreFunction is a function called for every explored vertex in the graph that takes a vertex as parameter * @param {Graph} graph * @param {(vertex: Vertex) => any} exploreFunction + * @param {"walk" | "teleport"} mode */ - Graph.depthFirstSearch = function(graph, exploreFunction) { + Graph.depthFirstSearch = function(graph, exploreFunction, mode = "walk") { /** @type {Vertex[]} */ let stack = []; let startVertex = graph.vertices.first(); @@ -244,11 +283,11 @@ stack.push(neighbors[i]); CollMap.drawRoom(neighbors[i], "purple", true); } - console.time("sort"); + // console.time("sort"); stack.sort(function (a, b) { - return b.walkablePathDistance() - a.walkablePathDistance(); + return b.pathDistance(mode) - a.pathDistance(mode); }); - console.timeEnd("sort"); + // console.timeEnd("sort"); // clear cache for all vertices for (let vertice of graph.vertices) { vertice.clearCache(); From f07f5882b1f54eb628a4d3ddc8ffbccc45643214 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:01:45 -0400 Subject: [PATCH 424/758] Update Graph.js - Add coordsInRoom method to vertex class - Add check after exportation method as we can move out of the room we started in --- d2bs/kolbot/libs/modules/Graph.js | 55 ++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/modules/Graph.js b/d2bs/kolbot/libs/modules/Graph.js index b0450a3ca..fb1622663 100644 --- a/d2bs/kolbot/libs/modules/Graph.js +++ b/d2bs/kolbot/libs/modules/Graph.js @@ -41,6 +41,21 @@ this.cache = {}; }; + Vertex.prototype.markAsSeen = function() { + this.seen = true; + }; + + /** + * @param {number} x + * @param {number} y + * @returns {boolean} + */ + Vertex.prototype.coordsInRoom = function(x, y) { + return (x >= this.x * 5 && x < this.x * 5 + this.xsize + && y >= this.y * 5 && y < this.y * 5 + this.ysize + ); + }; + /** * @param {"walk" | "teleport"} mode * @returns {PathNode[]} @@ -91,11 +106,12 @@ if (this.cache[key] && this.cache[key][other.id]) { return this.cache[key][other.id]; } + const inRoom = CollMap.coordsInRoom(me.x, me.y, this); const area = this.area; const rType = mode === "walk" ? 0 : 1; const nDist = mode === "walk" ? Pather.walkDistance : Pather.teleDistance; - const x = mode === "walk" ? this.walkableX : this.centerX; - const y = mode === "walk" ? this.walkableY : this.centerY; + const x = inRoom ? me.x : mode === "walk" ? this.walkableX : this.centerX; + const y = inRoom ? me.y : mode === "walk" ? this.walkableY : this.centerY; const otherX = mode === "walk" ? other.walkableX : other.centerX; const otherY = mode === "walk" ? other.walkableY : other.centerY; @@ -215,12 +231,23 @@ Graph.nearestNeighbourSearch = function(graph, explore, mode = "walk") { let currentVertex = graph.vertices.filter(filterSeen).first(); while (currentVertex) { - CollMap.drawRoom(graph.roomForVertex(currentVertex), "green", true); + CollMap.drawRoom(currentVertex, "green", true); explore(currentVertex); - // currentVertex.seen = true; // not working when it comes from neabies array, it should be referenced from graph.vertices array - graph.vertices.find(function (v) { return v === currentVertex; }).seen = true; - CollMap.drawRoom(graph.roomForVertex(currentVertex), "purple", true); + currentVertex.markAsSeen(); + CollMap.drawRoom(currentVertex, "purple", true); + + // our explore method could move us to a different room, so we need to get the vertex again + if (!currentVertex.coordsInRoom(me.x, me.y)) { + console.debug("Moved to a different room, getting new vertex"); + let _newVertex = graph.vertexForRoom(getRoom(me.x, me.y)); + if (_newVertex) { + currentVertex = _newVertex; + } else { + console.warn("Could not find vertex for my room?"); + } + } + let nearbies = graph.nearbyVertices(currentVertex) .filter(filterSeen) .sort(function (a, b) { @@ -230,7 +257,7 @@ // If the difference is less than 5% of the distance to a, sort by number of neighbors if (distDiff / distanceToA < 0.05) { - // First sort by number of neighbors (ascending) + // sort by number of neighbors (ascending) let diff = graph.nearbyVertices(a).length - graph.nearbyVertices(b).length; if (diff !== 0) return diff; } @@ -245,7 +272,19 @@ graph.vertices .filter(filterSeen) .sort(function (a, b) { - return a.pathDistance(mode) - b.pathDistance(mode); + let aDist = a.pathDistance(mode); + let bDist = b.pathDistance(mode); + let distDiff = Math.abs(aDist - bDist); + + // If the difference is less than 5% of the distance to a, sort by number of neighbors + if (distDiff / aDist < 0.05) { + // sort by number of neighbors (ascending) + let diff = graph.nearbyVertices(a).length - graph.nearbyVertices(b).length; + if (diff !== 0) return diff; + } + + // return a.pathDistance(mode) - b.pathDistance(mode); + return aDist - bDist; }) .first(); for (let vertice of graph.vertices) { From 0e02d117dead438d9d076a9204653fb4e1170cf2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:09:33 -0400 Subject: [PATCH 425/758] Update Attack.js - Add `Attack.clearEx`, will be replacing use of `Attack.clear` it's second param is an opts object which allows cleaner usage. - Add `Attack.clearLevelWalk`, experimental but uses the nearestNeighborSearch algo to explore level. Best usage has been in dungeon's/caves --- d2bs/kolbot/libs/core/Attack.js | 271 ++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 13790890d..a90650f28 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -491,6 +491,242 @@ const Attack = { return scariness; }, + /** + * @typedef {Object} ClearOptions + * @property {number} spectype + * @property {number | Unit} bossId + * @property {(a: T, b: T) => number} sortfunc + * @property {boolean} pickit + * @property {(unit: Monster) => boolean} filter + * @property {() => any} callback + */ + + /** + * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster + * @param {number} range + * @param {Partial} opts + * @returns {boolean} + */ + clearEx: function (range, opts = {}) { + while (!me.gameReady) { + delay(40); + } + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) return false; + + range === undefined && (range = 25); + if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); + + const settings = Object.assign({ + spectype: 0, + bossId: false, + sortfunc: Attack.sortMonsters, + pickit: true, + filter: false, + callback: false, + }, opts); + const { spectype, bossId, sortfunc, pickit, filter, callback } = settings; + + /** @type {Map 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 }); + if (Config.MFLeader + && !!bossId + // mfhelper is disabled for these scripts so announcing is pointless + && !Loader.scriptName(0).toLowerCase().includes("diablo") + && !Loader.scriptName(0).toLowerCase().includes("baal") + && Pather.makePortal()) { + say("clear " + (["number", "string"].includes(typeof bossId) ? bossId : bossId.name)); + } + } else { + ({ orgx, orgy } = { orgx: me.x, orgy: me.y }); + } + + let monsterList = []; + let target = Game.getMonster(); + + if (target) { + do { + if (typeof filter === "function" && !filter(target)) continue; + 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 + && (Pather.canTeleport() || !checkCollision(me, target, sdk.collision.WallOrRanged))) { + start = true; + } + + monsterList.push(copyUnit(target)); + } + } while (target.getNext()); + } + + while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { + if (me.dead) return false; + if (typeof callback === "function") callback(); + + boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); + monsterList.sort(sortfunc); + target = Game.getMonster(-1, -1, monsterList[0].gid); + + if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range + || (this.getScarinessLevel(target) > 7 && target.distance <= 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)); + } + // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); + + let _currMon = attacks.get(target.gid); + const checkAttackSkill = (!!_currMon && _currMon.attacks % 15 === 0); + const result = ClassAttack.doAttack(target, checkAttackSkill); + // let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + + 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; + const isSpecial = target.isSpecial; + const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + const checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; + const 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) { + Pather.randMove(-1, 1, -1, 1, 5); + } + + break; + default: + // Flash with melee skills + if (_currMon.attacks > 0 + && _currMon.attacks % (isSpecial ? 15 : 5) === 0 + && Skill.getRange(skillCheck) < 4) { + Packet.flash(me.gid); + // It'd be helpful to get a position in the opposite direction of the monster move there and then move back + // Pather.moveTo(me.x + (me.x - target.x), me.y + (me.y - target.y)); + console.debug("ÿc1Flashing " + target.name + " " + target.gid + " " + _currMon.attacks); + // Pather.randMove(-1, 1, -1, 1, 3); + + } + + break; + } + + // Skip non-unique monsters after 15 attacks, except in Throne of Destruction + if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && _currMon.attacks > 15) { + console.log("ÿc1Skipping " + target.name + " " + target.gid + " " + _currMon.attacks); + monsterList.shift(); + } + + /** + * @todo allow for more aggressive horking here + */ + if (target.dead || Config.FastPick || Config.FastFindItem) { + if ((target.isBoss || target.uniqueid > 0) && target.dead) { + // TODO: add uniqueids to sdk + target.isBoss && Attack._killed.add(target.classid); + target.uniqueid > -1 && Attack._killed.add(target.name); + } + if (boss && boss.gid === target.gid && target.dead) { + killedBoss = true; + console.log( + "ÿc7Cleared ÿc0:: " + (!!target.name ? target.name : bossId) + + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) + ); + } + Config.FastFindItem && pickit && ClassAttack.findItem(); + Pickit.fastPick(); + } + } else { + if (me.inArea(sdk.areas.ChaosSanctuary) && target.classid === sdk.monsters.StormCaster1) { + // probably behind the wall - skip them + monsterList.shift(); + retry = 0; + } + if (retry++ > 3) { + monsterList.shift(); + retry = 0; + } + + Packet.flash(me.gid); + } + } else { + monsterList.shift(); + } + } + + if (attackCount > 0) { + ClassAttack.afterAttack(pickit); + this.openChests(range, orgx, orgy); + pickit && 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 + } + + if (boss && !killedBoss) { + // check if boss corpse is around + 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 true; + }, + /** * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster * @param {number} [range=25] @@ -1044,6 +1280,41 @@ const Attack = { Attack.ignoredGids = []; }, + /** + * @description Clear an entire area based on monster spectype using nearestNeighbourSearch + * @param {number} spectype + * @param {() => boolean} [cb] callback to end clearing early + * @returns {boolean} + */ + clearLevelWalk: function (spectype, cb = null) { + const Graph = require("../modules/Graph"); + + try { + console.info(true, getAreaName(me.area), "clearLevelWalk-nearestNeighbourSearch"); + let graph = new Graph(); + Graph.nearestNeighbourSearch(graph, function (room) { + if (typeof cb === "function" && cb()) { + throw new ScriptError("Clearing stopped by callback"); + } + const roomNode = new PathNode(room.walkableX, room.walkableY); + Pather.move(roomNode, { callback: cb, clearSettings: { clearPath: true } }); + Attack.clearEx(room.xsize, { + spectype: spectype || 0, + filter: function (unit) { + return unit && room.coordsInRoom(unit.x, unit.y); + } + }); + }, "walk"); + } catch (e) { + if (!(e instanceof ScriptError)) { + console.error(e); + } + } finally { + CollMap.removeHooks(); + console.info(false, getAreaName(me.area), "clearLevelWalk-nearestNeighbourSearch"); + } + }, + /** * @description Clear an entire area based on monster spectype * @param {number} spectype From 5f888d766dc1f41fff82c538eb7eb6489a1570c1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:43:14 -0400 Subject: [PATCH 426/758] [BugFix] `seenGids.add` returns void caused failure to build chest list --- d2bs/kolbot/libs/core/Misc.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 39779dfa8..17d22fe2c 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -455,9 +455,10 @@ const Misc = (function () { if (unit) { do { if (unit.name && unit.mode === sdk.objects.mode.Inactive - && !seenGids.has(unit.gid) && seenGids.add(unit.gid) + && !seenGids.has(unit.gid) && getDistance(me.x, me.y, unit.x, unit.y) <= range && containers.includes(unit.name.toLowerCase())) { + seenGids.add(unit.gid); unitList.push(copyUnit(unit)); } } while (unit.getNext()); @@ -482,7 +483,7 @@ const Misc = (function () { if (startPos.distance > 5) { // rebuid chest list every 5 chests in case we've moved and add any new chests to our list - let _unitList = buildChestList(range / 2); + let _unitList = buildChestList(Math.round(range / 2)); console.debug("Rescanning for chests: " + _unitList.length + " chests found."); unitList = unitList.concat(_unitList); } From eb374d102a9ae9a012111b4ca5aafd63b8bd3b1a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:28:52 -0400 Subject: [PATCH 427/758] accidental upload - revert changes to rusher/rushee --- d2bs/kolbot/libs/scripts/Rushee.js | 1308 +++++++++--------- d2bs/kolbot/libs/scripts/Rusher.js | 113 +- d2bs/kolbot/libs/scripts/rungamex.js | 1781 ------------------------- d2bs/kolbot/libs/scripts/rungamex.txt | 1781 ------------------------- 4 files changed, 721 insertions(+), 4262 deletions(-) delete mode 100644 d2bs/kolbot/libs/scripts/rungamex.js delete mode 100644 d2bs/kolbot/libs/scripts/rungamex.txt diff --git a/d2bs/kolbot/libs/scripts/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js index b253fde10..cf371e53c 100644 --- a/d2bs/kolbot/libs/scripts/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -6,86 +6,88 @@ * */ +let Overrides = require("../modules/Override"); -function Rushee () { - const Overrides = require("../modules/Override"); +new Overrides.Override(Town, Town.goToTown, function(orignal, act, wpmenu) { + try { + orignal(act, wpmenu); - new Overrides.Override(Town, Town.goToTown, function (orignal, act, wpmenu) { - try { - orignal(act, wpmenu); - - return true; - } catch (e) { - console.log(e); - - return Pather.useWaypoint(sdk.areas.townOf(me.area)); - } - }).apply(); + return true; + } catch (e) { + print(e); + + return Pather.useWaypoint(sdk.areas.townOf(me.area)); + } +}).apply(); - new Overrides.Override(Pather, Pather.getWP, function (orignal, area, clearPath) { - if (area !== me.area) return false; +new Overrides.Override(Pather, Pather.getWP, function(orignal, area, clearPath) { + if (area !== me.area) return false; - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - if (preset) { - let x = (preset.roomx * 5 + preset.x); - let y = (preset.roomy * 5 + preset.y); - if (!me.inTown && [x, y].distance > 15) return false; + if (preset) { + let x = (preset.roomx * 5 + preset.x); + let y = (preset.roomy * 5 + preset.y); + if (!me.inTown && [x, y].distance > 15) return false; - Skill.haveTK - ? this.moveNearUnit(preset, 20, { clearSettings: { clearPath: clearPath } }) - : this.moveToUnit(preset, 0, 0, clearPath); + Skill.haveTK + ? this.moveNearUnit(preset, 20, { clearSettings: { clearPath: clearPath } }) + : this.moveToUnit(preset, 0, 0, clearPath); - let wp = Game.getObject("waypoint"); + let wp = Game.getObject("waypoint"); - if (wp) { - for (let j = 0; j < 10; j++) { - if (!getUIFlag(sdk.uiflags.Waypoint)) { - if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { - wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Packet.telekinesis(wp); - } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { - this.moveToUnit(wp) && Misc.click(0, 0, wp); - } + if (wp) { + for (let j = 0; j < 10; j++) { + if (!getUIFlag(sdk.uiflags.Waypoint)) { + if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { + wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + Packet.telekinesis(wp); + } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { + this.moveToUnit(wp) && Misc.click(0, 0, wp); } + } - if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { - delay(500); - me.cancelUIFlags(); - - return true; - } + if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { + delay(500); + me.cancelUIFlags(); - // handle getUnit bug - if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { - Town.getDistance("waypoint") > 5 && Town.move("waypoint"); - Misc.click(0, 0, wp); - } + return true; + } - delay(500); + // handle getUnit bug + if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { + Town.getDistance("waypoint") > 5 && Town.move("waypoint"); + Misc.click(0, 0, wp); } + + delay(500); } } } + } - return false; - }).apply(); - const { log } = require("../systems/autorush/AutoRush"); - const { - AutoRush, - // RushModes, - } = require("../systems/autorush/RushConfig"); - - const useScrollOfRes = function () { + return false; +}).apply(); + +function Rushee() { + let act, leader, target, done = false; + let actions = []; + + this.log = function (msg = "", sayMsg = false) { + print(msg); + sayMsg && say(msg); + }; + + this.useScrollOfRes = function () { let scroll = me.scrollofresistance; if (scroll) { clickItem(sdk.clicktypes.click.item.Right, scroll); - console.log("Using scroll of resistance"); + print("Using scroll of resistance"); } }; - const revive = function () { + this.revive = function () { while (me.mode === sdk.player.mode.Death) { delay(40); } @@ -94,17 +96,17 @@ function Rushee () { me.revive(); while (!me.inTown) { - delay(3); + delay(40); } } }; // todo - map the chest to classid so we only need to pass in one value - const getQuestItem = function (classid, chestid) { + this.getQuestItem = function (classid, chestid) { let tick = getTickCount(); if (me.getItem(classid)) { - log("Already have: " + classid); + this.log("Already have: " + classid); return true; } @@ -113,7 +115,7 @@ function Rushee () { let chest = Game.getObject(chestid); if (!chest) { - log("Couldn't find: " + chestid); + this.log("Couldn't find: " + chestid); return false; } @@ -121,7 +123,7 @@ function Rushee () { if (Misc.openChest(chest)) { break; } - log("Failed to open chest: Attempt[" + (i + 1) + "]"); + this.log("Failed to open chest: Attempt[" + (i + 1) + "]"); let coord = CollMap.getRandCoordinate(chest.x, -4, 4, chest.y, -4, 4); coord && Pather.moveTo(coord.x, coord.y); } @@ -139,7 +141,7 @@ function Rushee () { return Pickit.pickItem(item) && delay(1000); }; - const checkQuestMonster = function (classid) { + this.checkQuestMonster = function (classid) { let monster = Game.getMonster(classid); if (monster) { @@ -153,13 +155,9 @@ function Rushee () { return false; }; - const tyraelTalk = function () { - if (me.inArea(sdk.areas.DurielsLair) && [22577, 15609].distance > 10) { - Pather.move({ x: 22577, y: 15609 }, { callback: function () { - return Game.getNPC(NPC.Tyrael); - } }); - } + this.tyraelTalk = function () { let npc = Game.getNPC(NPC.Tyrael); + if (!npc) return false; for (let i = 0; i < 3; i += 1) { @@ -178,49 +176,30 @@ function Rushee () { return Pather.usePortal(null) || Pather.usePortal(null, Config.Leader); }; - const cube = (function () { - const staff = { - ingreds: [sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.item.ViperAmulet], - outcome: sdk.quest.item.HoradricStaff, - }; - const flail = { - ingreds: [ - sdk.quest.item.KhalimsFlail, sdk.quest.item.KhalimsEye, - sdk.quest.item.KhalimsBrain, sdk.quest.item.KhalimsHeart - ], - outcome: sdk.quest.item.KhalimsWill, - }; - /** @param {{ ingreds: number[], outcome: number }} item */ - const make = function (item) { - if (me.getItem(item.outcome)) return true; - let ingreds = item.ingreds.map(id => me.getItem(id)); - if (!ingreds.every(i => i)) return false; - ingreds.forEach(i => Storage.Cube.MoveTo(i)); - Cubing.openCube(); - transmute(); - delay(750 + me.ping); - - let outcome = me.getItem(item.outcome); - if (!outcome) return false; - - Storage.Inventory.MoveTo(outcome); - me.cancel(); + this.cubeStaff = function () { + let shaft = me.shaft; + let amulet = me.amulet; - return true; - }; - return { - Staff: function () { - log("Making staff", Config.LocalChat.Enabled); - return make(staff); - }, - Flail: function () { - log("Making flail", Config.LocalChat.Enabled); - return make(flail); - }, - }; - })(); - - const placeStaff = function () { + if (!shaft || !amulet) return false; + + Storage.Cube.MoveTo(amulet); + Storage.Cube.MoveTo(shaft); + Cubing.openCube(); + print("making staff"); + transmute(); + delay(750 + me.ping); + + let staff = me.completestaff; + + if (!staff) return false; + + Storage.Inventory.MoveTo(staff); + me.cancel(); + + return true; + }; + + this.placeStaff = function () { let tick = getTickCount(); let orifice = Game.getObject(sdk.quest.chest.HoradricStaffHolder); if (!orifice) return false; @@ -251,7 +230,7 @@ function Rushee () { return true; }; - const changeAct = function (act) { + this.changeAct = function (act) { let preArea = me.area; if (me.mode === sdk.player.mode.Dead) { @@ -332,18 +311,12 @@ function Rushee () { if (me.area === preArea) { me.cancel(); Town.move("portalspot"); - log("Act change failed.", Config.LocalChat.Enabled); + this.log("Act change failed.", Config.LocalChat.Enabled); return false; } - if (me.act === 2 && Game.getNPC(NPC.Jerhyn)) { - Town.npcInteract("Jerhyn"); - } else if (me.act === 3) { - Town.npcInteract("Hratli"); - } - - log("Act change done.", Config.LocalChat.Enabled); + this.log("Act change done.", Config.LocalChat.Enabled); } catch (e) { return false; } @@ -351,7 +324,7 @@ function Rushee () { return true; }; - const getQuestInfo = function (id) { + this.getQuestInfo = function (id) { // note: end bosses double printed to match able to go to act flag let quests = [ ["cain", sdk.quest.id.TheSearchForCain], @@ -377,23 +350,16 @@ function Rushee () { ]; let quest = quests.find(element => element[1] === id); - console.debug("Quest: " + quest + " ID: " + id); return (!!quest ? quest[0] : ""); }; - let nonQuesterNPCTalk = false; - let act, target, done = false; - const commands = []; + this.nonQuesterNPCTalk = false; addEventListener("chatmsg", function (who, msg) { - if (!Config.Leader && msg.includes("questinfo")) { - Config.Leader = who; - console.debug("Assigned Leader: " + Config.Leader); - } if (who === Config.Leader) { - commands.push(msg); + actions.push(msg); } }); @@ -401,22 +367,16 @@ function Rushee () { Town.goToTown(me.highestAct); me.inTown && Town.move("portalspot"); - if (me.inArea(sdk.areas.RogueEncampment) - && !me.getQuest(sdk.quest.id.SpokeToWarriv, sdk.quest.states.Completed)) { - Town.npcInteract("Warriv"); - Town.move("portalspot"); - } - // if we can't find our leader after 5 minutes, I'm thinking they aren't showing up. Lets not wait around forever - const leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.minutes(5), 1000); + leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.minutes(5), 1000); if (!leader) throw new Error("Failed to find my rusher"); Config.Rushee.Quester - ? log("(Quester) Leader Found: " + Config.Leader, Config.LocalChat.Enabled) - : console.log("(NonQuester) Leader Found: " + Config.Leader); + ? this.log("Leader found", Config.LocalChat.Enabled) + : console.log("Leader Found: " + Config.Leader); // lets figure out if we either are the bumper or have a bumper so we know if we need to stop at the end of the rush - const bumperLevelReq = [20, 40, 60][me.diff]; + let bumperLevelReq = [20, 40, 60][me.diff]; // ensure we are the right level to go to next difficulty if not on classic let nextGame = (Config.Rushee.Bumper && (me.classic || me.charlvl >= bumperLevelReq)); if (!nextGame) { @@ -429,627 +389,677 @@ function Rushee () { } console.debug("Is this our last run? " + (nextGame ? "No" : "Yes")); - const actions = new Map([ - [AutoRush.allIn, function () { - switch (leader.area) { - case sdk.areas.A2SewersLvl3: - // Pick Book of Skill, use Book of Skill - Town.move("portalspot"); - Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); - delay(500); + while (true) { + // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces + try { + if (actions.length > 0) { + switch (actions[0]) { + case "all in": + switch (leader.area) { + case sdk.areas.A2SewersLvl3: + // Pick Book of Skill, use Book of Skill + Town.move("portalspot"); + Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); + delay(500); + + while (true) { + target = Game.getItem(sdk.quest.item.BookofSkill); + + if (!target) { + break; + } + + Pickit.pickItem(target); + delay(250); + target = me.getItem(sdk.quest.item.BookofSkill); + + if (target) { + print("Using book of skill"); + clickItem(sdk.clicktypes.click.item.Right, target); + + break; + } + } - while (true) { - target = Game.getItem(sdk.quest.item.BookofSkill); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + actions.shift(); - if (!target) { break; } - Pickit.pickItem(target); - delay(250); - target = me.getItem(sdk.quest.item.BookofSkill); + actions.shift(); - if (target) { - console.log("Using book of skill"); - clickItem(sdk.clicktypes.click.item.Right, target); + break; + case "questinfo": + if (!Config.Rushee.Quester) { + actions.shift(); break; } - } - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + say("highestquest " + this.getQuestInfo(me.highestQuestDone)); + actions.shift(); - return true; - default: - if (!Config.Rushee.Bumper) return true; + break; + case "wpinfo": + if (!Config.Rushee.Quester) { + actions.shift(); - while (!leader.area) { - delay(500); - } + break; + } - act = Misc.getPlayerAct(leader); + // go activate wp if we don't know our wps yet + !getWaypoint(1) && Pather.getWP(me.area); - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } + let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { + if (area === sdk.areas.HallsofPain) return false; + if (me.classic && area >= sdk.areas.Harrogath) return false; + if (getWaypoint(Pather.wpAreas.indexOf(area))) return false; + return true; + }); - switch (leader.area) { - case sdk.areas.ArreatSummit: - if (!Pather.usePortal(sdk.areas.ArreatSummit, Config.Leader)) { - return false; + say("mywps " + JSON.stringify(myWps)); + actions.shift(); + + break; + case "wp": + if (!me.inTown && !Town.goToTown()) { + this.log("I can't get to town :(", Config.LocalChat.Enabled); + + break; } - // Wait until portal is gone - while (Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { - delay(500); + act = Misc.getPlayerAct(leader); + + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); } - // Wait until portal is up again - while (!Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { - delay(500); + // make sure we talk to cain to access durance + leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); + + // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas + (!Config.Rushee.Quester && !this.nonQuesterNPCTalk) && (this.nonQuesterNPCTalk = true); + + Town.getDistance("portalspot") > 10 && Town.move("portalspot"); + if (Pather.usePortal(null, Config.Leader) && Pather.getWP(me.area) && Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) && Town.move("portalspot")) { + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + } else { + // check for bugged portal + let p = Game.getObject("portal"); + let preArea = me.area; + if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) + && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + } else { + this.log("Failed to get wp", Config.LocalChat.Enabled); + !me.inTown && Town.goToTown(); + } } - if (!Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - return false; + actions.shift(); + + break; + case "1": + while (!leader.area) { + delay(500); } - return true; - case sdk.areas.WorldstoneChamber: - if (!Pather.usePortal(sdk.areas.WorldstoneChamber, Config.Leader)) { - return false; + act = Misc.getPlayerAct(leader); + + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); } - return true; - } - } - return true; - }], - [AutoRush.playersIn, function () { - while (!leader.area) { - delay(500); - } + // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester + if (this.nonQuesterNPCTalk) { + console.debug("Leader Area: " + getAreaName(leader.area)); - act = Misc.getPlayerAct(leader); + switch (leader.area) { + case sdk.areas.ClawViperTempleLvl2: + Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); + if (Town.npcInteract("Drognan")) { + actions.shift(); + console.debug("drognan done"); + } - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } + break; + case sdk.areas.ArcaneSanctuary: + Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); + if (Town.npcInteract("Atma")) { + actions.shift(); + console.debug("atma done"); + } - // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester - if (nonQuesterNPCTalk) { - console.debug("Leader Area: " + getAreaName(leader.area)); - - switch (leader.area) { - case sdk.areas.ClawViperTempleLvl2: - Misc.poll(function () { - return !!(Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) - || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete)); - }, Time.seconds(20), 1000); - if (Town.npcInteract("Drognan")) { - console.debug("drognan done"); - return true; - } + break; + case sdk.areas.Travincal: + Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) || Misc.checkQuest(sdk.quest.id.TheGuardian, 8), Time.seconds(20), 1000)); + if (Town.npcInteract("Cain")) { + actions.shift(); + console.debug("cain done"); + } - return false; - case sdk.areas.ArcaneSanctuary: - Misc.poll(function () { - return !!(Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) - || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete)); - }, Time.seconds(20), 1000); - if (Town.npcInteract("Atma")) { - console.debug("atma done"); - return true; - } - return false; - case sdk.areas.Travincal: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) || Misc.checkQuest(sdk.quest.id.TheGuardian, 8), Time.seconds(20), 1000)); - if (Town.npcInteract("Cain")) { - console.debug("cain done"); - return true; - } + break; + case sdk.areas.ArreatSummit: + Misc.poll(() => (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); + if (Town.npcInteract("Malah")) { + actions.shift(); + console.debug("malah done"); + } - return false; - case sdk.areas.ArreatSummit: - Misc.poll(() => (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Malah")) { - console.debug("malah done"); - return true; + break; + } + + me.inTown && Town.move("portalspot"); } - return false; - } + if (!Config.Rushee.Quester) { + actions.shift(); - me.inTown && Town.move("portalspot"); - } + break; + } - if (!Config.Rushee.Quester) { - return true; - } + switch (leader.area) { + case sdk.areas.StonyField: + if (!Pather.usePortal(sdk.areas.StonyField, Config.Leader)) { + this.log("Failed to us portal to stony field", Config.LocalChat.Enabled); + break; + } - switch (leader.area) { - case sdk.areas.StonyField: - if (!Pather.usePortal(sdk.areas.StonyField, Config.Leader)) { - log("Failed to use portal to stony field", Config.LocalChat.Enabled); - return false; - } + let stones = [ + Game.getObject(sdk.quest.chest.StoneAlpha), + Game.getObject(sdk.quest.chest.StoneBeta), + Game.getObject(sdk.quest.chest.StoneGamma), + Game.getObject(sdk.quest.chest.StoneDelta), + Game.getObject(sdk.quest.chest.StoneLambda) + ]; + + while (stones.some((stone) => !stone.mode)) { + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; + + if (Misc.openChest(stone)) { + stones.splice(i, 1); + i--; + } + delay(10); + } + } - let stones = [ - Game.getObject(sdk.quest.chest.StoneAlpha), - Game.getObject(sdk.quest.chest.StoneBeta), - Game.getObject(sdk.quest.chest.StoneGamma), - Game.getObject(sdk.quest.chest.StoneDelta), - Game.getObject(sdk.quest.chest.StoneLambda) - ]; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (Misc.openChest(stone)) { - stones.splice(i, 1); - i--; + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.getPortal(sdk.areas.Tristram)) { + Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + + break; + } + } + Town.move("portalspot"); + actions.shift(); + + break; + case sdk.areas.DarkWood: + if (!Pather.usePortal(sdk.areas.DarkWood, Config.Leader)) { + this.log("Failed to use portal to dark wood", Config.LocalChat.Enabled); + break; } - delay(10); - } - } - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram)) { + this.getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); + delay(500); Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + if (Town.npcInteract("Akara")) { + this.log("Akara done", Config.LocalChat.Enabled); + } + + Town.move("portalspot"); + actions.shift(); + break; - } - } - Town.move("portalspot"); + case sdk.areas.Tristram: + if (!Pather.usePortal(sdk.areas.Tristram, Config.Leader)) { + this.log("Failed to use portal to Tristram", Config.LocalChat.Enabled); + break; + } - return true; - case sdk.areas.DarkWood: - if (!Pather.usePortal(sdk.areas.DarkWood, Config.Leader)) { - log("Failed to use portal to dark wood", Config.LocalChat.Enabled); - return false; - } + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); - delay(500); - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - - if (Town.npcInteract("Akara")) { - log("Akara done", Config.LocalChat.Enabled); - } + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { + Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); + Town.npcInteract("Akara") && this.log("Akara done", Config.LocalChat.Enabled); + } + } + Town.move("portalspot"); + actions.shift(); - Town.move("portalspot"); + break; + case sdk.areas.CatacombsLvl4: + if (!Pather.usePortal(sdk.areas.CatacombsLvl4, Config.Leader)) { + this.log("Failed to use portal to catacombs", Config.LocalChat.Enabled); + break; + } - return true; - case sdk.areas.Tristram: - if (!Pather.usePortal(sdk.areas.Tristram, Config.Leader)) { - log("Failed to use portal to Tristram", Config.LocalChat.Enabled); - break; - } + target = Pather.getPortal(null, Config.Leader); + target && Pather.walkTo(target.x, target.y); - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + actions.shift(); - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - Town.npcInteract("Akara") && log("Akara done", Config.LocalChat.Enabled); - } - } - Town.move("portalspot"); - commands.shift(); + break; + case sdk.areas.A2SewersLvl3: + Town.move("portalspot"); - break; - case sdk.areas.CatacombsLvl4: - if (!Pather.usePortal(sdk.areas.CatacombsLvl4, Config.Leader)) { - log("Failed to use portal to catacombs", Config.LocalChat.Enabled); - return false; - } + if (Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader)) { + actions.shift(); + } - target = Pather.getPortal(null, Config.Leader); - target && Pather.walkTo(target.x, target.y); + break; + case sdk.areas.HallsoftheDeadLvl3: + Pather.usePortal(sdk.areas.HallsoftheDeadLvl3, Config.Leader); + this.getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - return true; - case sdk.areas.A2SewersLvl3: - Town.move("portalspot"); + actions.shift(); - return Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); - case sdk.areas.HallsoftheDeadLvl3: - Pather.usePortal(sdk.areas.HallsoftheDeadLvl3, Config.Leader); - getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); - return Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - case sdk.areas.ClawViperTempleLvl2: - Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); - getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - - if (Town.npcInteract("Drognan")) { - say("drognan done", Config.LocalChat.Enabled); - Town.move("portalspot"); - return true; - } + break; + case sdk.areas.ClawViperTempleLvl2: + Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); + this.getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + + if (Town.npcInteract("Drognan")) { + actions.shift(); + say("drognan done", Config.LocalChat.Enabled); + } - return false; - case sdk.areas.MaggotLairLvl3: - Pather.usePortal(sdk.areas.MaggotLairLvl3, Config.Leader); - getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); - delay(500); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - return cube.Staff(); - case sdk.areas.ArcaneSanctuary: - return Pather.usePortal(sdk.areas.ArcaneSanctuary, Config.Leader); - case sdk.areas.TalRashasTomb1: - case sdk.areas.TalRashasTomb2: - case sdk.areas.TalRashasTomb3: - case sdk.areas.TalRashasTomb4: - case sdk.areas.TalRashasTomb5: - case sdk.areas.TalRashasTomb6: - case sdk.areas.TalRashasTomb7: - Pather.usePortal(null, Config.Leader); - placeStaff(); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - return true; - case sdk.areas.DurielsLair: - Pather.usePortal(sdk.areas.DurielsLair, Config.Leader); - tyraelTalk(); - - return true; - case sdk.areas.Travincal: - if (!Pather.usePortal(sdk.areas.Travincal, Config.Leader)) { - me.cancel(); + Town.move("portalspot"); - return false; - } + break; + case sdk.areas.MaggotLairLvl3: + Pather.usePortal(sdk.areas.MaggotLairLvl3, Config.Leader); + this.getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); + delay(500); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + this.cubeStaff(); - return true; - case sdk.areas.RuinedTemple: - if (!Pather.usePortal(sdk.areas.RuinedTemple, Config.Leader)) { - me.cancel(); + actions.shift(); - return false; - } + break; + case sdk.areas.ArcaneSanctuary: + if (!Pather.usePortal(sdk.areas.ArcaneSanctuary, Config.Leader)) { + break; + } - getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); - Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); - Town.npcInteract("Alkor"); - Town.move("portalspot"); - return true; - case sdk.areas.DuranceofHateLvl3: - if (!Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader)) { - me.cancel(); + actions.shift(); - return false; - } + break; + case sdk.areas.TalRashasTomb1: + case sdk.areas.TalRashasTomb2: + case sdk.areas.TalRashasTomb3: + case sdk.areas.TalRashasTomb4: + case sdk.areas.TalRashasTomb5: + case sdk.areas.TalRashasTomb6: + case sdk.areas.TalRashasTomb7: + Pather.usePortal(null, Config.Leader); + this.placeStaff(); + Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + actions.shift(); - return true; - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - return Pather.usePortal(null, Config.Leader); - case sdk.areas.ChaosSanctuary: - Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); - Pather.moveTo(7762, 5268); - Packet.flash(me.gid); - delay(500); - Pather.walkTo(7763, 5267, 2); + break; + case sdk.areas.DurielsLair: + Pather.usePortal(sdk.areas.DurielsLair, Config.Leader); + this.tyraelTalk(); - while (!Game.getMonster(sdk.monsters.Diablo)) { - delay(500); - } + actions.shift(); - Pather.moveTo(7763, 5267); - return true; - case sdk.areas.BloodyFoothills: - return Pather.usePortal(sdk.areas.BloodyFoothills, Config.Leader); - case sdk.areas.FrozenRiver: - Town.npcInteract("Malah"); + break; + case sdk.areas.Travincal: + if (!Pather.usePortal(sdk.areas.Travincal, Config.Leader)) { + me.cancel(); - Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader); - delay(500); + break; + } - target = Game.getObject(sdk.objects.FrozenAnya); + actions.shift(); - if (target) { - Pather.moveToUnit(target); - Misc.poll(() => { - Packet.entityInteract(target); - delay(100); - return !Game.getObject(sdk.objects.FrozenAnya); - }, 1000, 200); - delay(1000); - me.cancel(); - } + break; + case sdk.areas.RuinedTemple: + if (!Pather.usePortal(sdk.areas.RuinedTemple, Config.Leader)) { + me.cancel(); - return true; - default: // unsupported area - return true; - } - return true; - }], - [AutoRush.playersOut, function () { - if (!Config.Rushee.Quester) { - // Non-questers can piggyback off quester out messages - switch (leader.area) { - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete) && Town.npcInteract("Tyrael"); + break; + } - break; - case sdk.areas.BloodyFoothills: - me.act === 5 && Town.npcInteract("Larzuk"); + this.getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); + Town.npcInteract("Alkor"); + Town.move("portalspot"); + actions.shift(); - break; - case sdk.areas.FrozenRiver: - if (me.act === 5) { + + break; + case sdk.areas.DuranceofHateLvl3: + if (!Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader)) { + me.cancel(); + + break; + } + + actions.shift(); + + break; + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + if (Pather.usePortal(null, Config.Leader)) { + actions.shift(); + } + + break; + case sdk.areas.ChaosSanctuary: + Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); + Pather.moveTo(7762, 5268); + Packet.flash(me.gid); + delay(500); + Pather.walkTo(7763, 5267, 2); + + while (!Game.getMonster(sdk.monsters.Diablo)) { + delay(500); + } + + Pather.moveTo(7763, 5267); + actions.shift(); + + break; + case sdk.areas.BloodyFoothills: + Pather.usePortal(sdk.areas.BloodyFoothills, Config.Leader); + actions.shift(); + + break; + case sdk.areas.FrozenRiver: Town.npcInteract("Malah"); - useScrollOfRes(); + + Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader); + delay(500); + + target = Game.getObject(sdk.objects.FrozenAnya); + + if (target) { + Pather.moveToUnit(target); + Misc.poll(() => { + Packet.entityInteract(target); + delay(100); + return !Game.getObject(sdk.objects.FrozenAnya); + }, 1000, 200); + delay(1000); + me.cancel(); + } + + actions.shift(); + + break; + default: // unsupported area + actions.shift(); + + break; } break; - } + case "2": // Go back to town and check quest + if (!Config.Rushee.Quester) { + // Non-questers can piggyback off quester out messages + switch (leader.area) { + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete) && Town.npcInteract("Tyrael"); + + break; + case sdk.areas.BloodyFoothills: + me.act === 5 && Town.npcInteract("Larzuk"); + + break; + case sdk.areas.FrozenRiver: + if (me.act === 5) { + Town.npcInteract("Malah"); + this.useScrollOfRes(); + } - commands.shift(); + break; + } - return true; - } + actions.shift(); - revive(); + break; + } - switch (me.area) { - case sdk.areas.CatacombsLvl4: - // Go to town if not there, break if procedure fails - if (!me.inTown && !Pather.usePortal(sdk.areas.RogueEncampment)) { - return false; - } + this.revive(); - if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { - D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); - quit(); - } + switch (me.area) { + case sdk.areas.CatacombsLvl4: + // Go to town if not there, break if procedure fails + if (!me.inTown && !Pather.usePortal(sdk.areas.RogueEncampment)) { + break; + } - return true; - case sdk.areas.A2SewersLvl3: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - return false; - } + if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { + D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); + quit(); + } - return true; - case sdk.areas.ArcaneSanctuary: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - return false; - } + actions.shift(); - Town.npcInteract("Atma"); + break; + case sdk.areas.A2SewersLvl3: + if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { + break; + } - if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); - quit(); - } + actions.shift(); - Town.move("portalspot"); - return true; - case sdk.areas.Travincal: - if (!me.inTown && !Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { - return false; - } + break; + case sdk.areas.ArcaneSanctuary: + if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { + break; + } - Town.npcInteract("Cain"); + Town.npcInteract("Atma"); - if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); - quit(); - } + if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); + quit(); + } - Town.move("portalspot"); - return true; - case sdk.areas.DuranceofHateLvl3: - return Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - return false; - } + Town.move("portalspot"); + actions.shift(); - if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { - Town.npcInteract("Tyrael"); - Town.move("portalspot"); - } + break; + case sdk.areas.Travincal: + if (!me.inTown && !Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { + break; + } - return true; - case sdk.areas.ChaosSanctuary: - me.classic && D2Bot.restart(); + Town.npcInteract("Cain"); - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - return false; - } + if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); + quit(); + } - return true; - case sdk.areas.BloodyFoothills: - if (!me.inTown && !Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - return false; - } + Town.move("portalspot"); + actions.shift(); - Town.npcInteract("Larzuk"); - Town.move("portalspot"); - return true; - case sdk.areas.FrozenRiver: - if (!me.inTown && !Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader)) { - return false; - } + break; + case sdk.areas.DuranceofHateLvl3: + if (!Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { + break; + } - Town.npcInteract("Malah"); - useScrollOfRes(); - Town.move("portalspot"); + actions.shift(); - return true; - default: - Town.move("portalspot"); - return true; - } - }], - ["flail", function () { - if (!Config.Rushee.Quester) { - return true; - } - - }], - ["questinfo", function () { - if (!Config.Rushee.Quester) { - return true; - } - say("highestquest " + getQuestInfo(me.highestQuestDone)); - return true; - }], - ["wpinfo", function () { - if (!Config.Rushee.Quester) { - return true; - } + break; + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { + break; + } - // go activate wp if we don't know our wps yet - !me.haveWaypoint(1) && Pather.getWP(me.area); + if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { + Town.npcInteract("Tyrael"); + Town.move("portalspot"); + } - let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { - if (area === sdk.areas.HallsofPain) return false; - if (me.classic && area >= sdk.areas.Harrogath) return false; - if (me.haveWaypoint(area)) return false; - return true; - }); + actions.shift(); - say("mywps " + JSON.stringify(myWps)); - return true; - }], - ["wp", function () { - if (!me.inTown && !Town.goToTown()) { - log("I can't get to town :(", Config.LocalChat.Enabled); - return false; - } + break; + case sdk.areas.ChaosSanctuary: + me.classic && D2Bot.restart(); - act = Misc.getPlayerAct(leader); + if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { + break; + } - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } + actions.shift(); - // make sure we talk to cain to access durance - leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); - - // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas - (!Config.Rushee.Quester && !nonQuesterNPCTalk) && (nonQuesterNPCTalk = true); - - Town.getDistance("portalspot") > 10 && Town.move("portalspot"); - if (Pather.usePortal(null, Config.Leader) && Pather.getWP(me.area) && Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) && Town.move("portalspot")) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); - } else { - // check for bugged portal - let p = Game.getObject("portal"); - let preArea = me.area; - if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) - && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); - } else { - log("Failed to get wp", Config.LocalChat.Enabled); - !me.inTown && Town.goToTown(); - } - } + break; + case sdk.areas.BloodyFoothills: + if (!me.inTown && !Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { + break; + } - return true; - }], - ["a2", function () { - if (!changeAct(2)) { - return false; - } + Town.npcInteract("Larzuk"); + Town.move("portalspot"); + actions.shift(); - Town.move("portalspot"); - return true; - }], - ["a3", function () { - if (!changeAct(3)) { - return false; - } + break; + case sdk.areas.FrozenRiver: + if (!me.inTown && !Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader)) { + break; + } - Town.move("portalspot"); - return true; - }], - ["a4", function () { - if (!changeAct(4)) { - return false; - } + Town.npcInteract("Malah"); + this.useScrollOfRes(); + Town.move("portalspot"); - Town.move("portalspot"); - return true; - }], - ["a5", function () { - if (!changeAct(5)) { - return false; - } + actions.shift(); - Town.move("portalspot"); - return true; - }], - ["quit", function () { - done = true; - return true; - }], - ["exit", function () { - if (!nextGame) { - D2Bot.printToConsole("Rush Complete"); - D2Bot.stop(); - } else { - D2Bot.restart(); - } - return true; - }], - ["leader", function () { - console.log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - Config.LocalChat.Enabled && say(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - return true; - }], - [me.name + " quest", function () { - say("I am quester."); - Config.Rushee.Quester = true; + break; + default: + Town.move("portalspot"); + actions.shift(); - return true; - }] - ]); - const curr = { - cmd: "", - retry: 0 - }; + break; + } - while (true) { - // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces - try { - if (commands.length > 0) { - let command = commands[0].toLowerCase(); - - if (actions.has(command)) { - curr.cmd = command; - if (actions.get(command)()) { - commands.shift(); - curr.retry = 0; - } else { - console.debug("Command retry: " + command); - curr.retry++; - if (curr.retry > 3) { - log("Failed to do " + command, Config.LocalChat.Enabled); - commands.shift(); + break; + case "3": // Bumper + if (!Config.Rushee.Bumper) { + actions.shift(); + + break; + } + + while (!leader.area) { + delay(500); + } + + act = Misc.getPlayerAct(leader); + + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } + + switch (leader.area) { + case sdk.areas.ArreatSummit: + if (!Pather.usePortal(sdk.areas.ArreatSummit, Config.Leader)) { + break; + } + + // Wait until portal is gone + while (Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { + delay(500); } + + // Wait until portal is up again + while (!Pather.getPortal(sdk.areas.Harrogath, Config.Leader)) { + delay(500); + } + + if (!Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { + break; + } + + actions.shift(); + + break; + case sdk.areas.WorldstoneChamber: + if (!Pather.usePortal(sdk.areas.WorldstoneChamber, Config.Leader)) { + break; + } + + actions.shift(); + + break; } - } else { - commands.shift(); + + break; + case "quit": + done = true; + + break; + case "exit": + case "bye ~": + if (!nextGame) { + D2Bot.printToConsole("Rush Complete"); + D2Bot.stop(); + } else { + D2Bot.restart(); + } + + break; + case "a2": + case "a3": + case "a4": + case "a5": + act = actions[0].toString()[1]; + !!act && (act = (parseInt(act, 10) || me.act + 1)); + + if (!this.changeAct(act)) { + break; + } + + Town.move("portalspot"); + actions.shift(); + + break; + case me.name + " quest": + say("I am quester."); + Config.Rushee.Quester = true; + + actions.shift(); + + break; + case "leader": + console.log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); + Config.LocalChat.Enabled && say(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); + actions.shift(); + + break; + default: // Invalid command + actions.shift(); + + break; } } } catch (e) { - console.error(e); - commands.shift(); if (me.mode === sdk.player.mode.Dead) { me.revive(); while (!me.inTown) { - delay(3); + delay(500); } } } diff --git a/d2bs/kolbot/libs/scripts/Rusher.js b/d2bs/kolbot/libs/scripts/Rusher.js index 66f79ab15..20ba4817b 100644 --- a/d2bs/kolbot/libs/scripts/Rusher.js +++ b/d2bs/kolbot/libs/scripts/Rusher.js @@ -18,18 +18,12 @@ function Rusher () { load("threads/rushthread.js"); delay(500); - const { - AutoRush, - RushModes, - RushConfig, - } = require("../systems/autorush/RushConfig"); - - const commands = []; - let command = ""; - let master = ""; - let commandSplit0; - let done = false; - + let i, command, master, commandSplit0; + let commands = []; + let sequence = [ + "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", + "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" + ]; const RushThread = { /** @type {Script} */ _thread: null, @@ -124,7 +118,6 @@ function Rusher () { break; case "quit": if (nick === master) { - done = true; say("bye ~"); scriptBroadcast("quit"); } else { @@ -154,46 +147,62 @@ function Rusher () { addEventListener("chatmsg", chatEvent); - while (Misc.getPartyCount() < Math.min(8, RushConfig[me.profile].config.WaitPlayerCount)) { + while (Misc.getPartyCount() < Math.min(8, Config.Rusher.WaitPlayerCount)) { me.overhead("Waiting for players to join"); delay(500); } // Skip to a higher act if all party members are there - let partyAct = getPartyAct(); - if (partyAct > 1) { - say("Party is in act " + partyAct + ", skipping to act " + partyAct); - RushThread.send("skiptoact " + partyAct); + switch (getPartyAct()) { + case 2: + say("Party is in act 2, starting from act 2"); + RushThread.send("skiptoact 2"); + + break; + case 3: + say("Party is in act 3, starting from act 3"); + RushThread.send("skiptoact 3"); + + break; + case 4: + say("Party is in act 4, starting from act 4"); + RushThread.send("skiptoact 4"); + + break; + case 5: + say("Party is in act 5, starting from act 5"); + RushThread.send("skiptoact 5"); + + break; } // get info from master - if (RushConfig[me.profile].type === RushModes.rusher) { - let tick = getTickCount(); - let askAgain = 1; - say("questinfo"); - while (!command) { - // wait up to 3 minutes - if (getTickCount() - tick > Time.minutes(3)) { - break; - } + let tick = getTickCount(); + let askAgain = 1; + say("questinfo"); + while (!command) { + // wait up to 3 minutes + if (getTickCount() - tick > Time.minutes(3)) { + break; + } - if (getTickCount() - tick > Time.minutes(askAgain)) { - say("questinfo"); - askAgain++; - } + if (getTickCount() - tick > Time.minutes(askAgain)) { + say("questinfo"); + askAgain++; } - if (command) { - commandSplit0 = command.toLowerCase().split(" ")[1]; - if (!!commandSplit0 && AutoRush.sequences.includes(commandSplit0)) { - RushThread.send(command.toLowerCase()); - } + } + + if (command) { + commandSplit0 = command.split(" ")[1]; + if (!!commandSplit0 && sequence.some(el => el.toLowerCase() === commandSplit0)) { + RushThread.send(command.toLowerCase()); } } - + delay(200); RushThread.send("go"); - while (!done) { + while (true) { if (commands.length > 0) { command = commands.shift(); @@ -215,23 +224,25 @@ function Rusher () { } if (commandSplit0.toLowerCase() === "do") { - let script = command.split(" ")[1]; - if (!script || !AutoRush.sequences.some(el => el.match(script, "gi"))) { - say("Invalid sequence"); - break; + for (i = 0; i < sequence.length; i += 1) { + if (command.split(" ")[1] && sequence[i].match(command.split(" ")[1], "gi")) { + RushThread.reload(); + RushThread.send(command.split(" ")[1]); + + break; + } } - RushThread.reload(); - RushThread.send(script); + + i === sequence.length && say("Invalid sequence"); } else if (commandSplit0.toLowerCase() === "clear") { - let area = command.split(" ")[1]; - if (!area) break; - let areaId = parseInt(area, 10); - if (isNaN(areaId) || areaId < 1 || areaId > 132) { + if (!isNaN(parseInt(command.split(" ")[1], 10)) + && parseInt(command.split(" ")[1], 10) > 0 + && parseInt(command.split(" ")[1], 10) <= 132) { + RushThread.reload(); + RushThread.send(command); + } else { say("Invalid area"); - break; } - RushThread.reload(); - RushThread.send(command); } } diff --git a/d2bs/kolbot/libs/scripts/rungamex.js b/d2bs/kolbot/libs/scripts/rungamex.js deleted file mode 100644 index bf4cedfd7..000000000 --- a/d2bs/kolbot/libs/scripts/rungamex.js +++ /dev/null @@ -1,1781 +0,0 @@ -/** -* @filename rungame2.js -* @author darjkeel@d2jsp -* @desc one script to run them all, and in the dankness bind them -* @readme rungame2 readme.txt -**/ - -function rungamex() { - let boWP = 83, //Default bo wp - chars = [ //Define chars and list their bosses/tasks - "Sorceress", - [ - "chores", - "getbo 83", - "Sszark the Burning", - "goto 102", - "areadelay in 75 Parzivol PLA", - "taxi on", - "areadelay in 75 Parzivol PLA", - "Wyand Voidbringer", - "areadelay in 75 Parzivol PLA", - "Bremm Sparkfist", - "areadelay in 75 Parzivol PLA", - "Maffer Dragonhand", - "goto 105", - "areadelay in 103 Parzivol PLA", - "Izual", - "Hephasto the Armorer", - "taxi off", - "chaos", - "areadelay in 108 Parzivol PLA", - "getleg", - "makecows", - "chests on", - "Witch Doctor Endugu", - "Icehawk Riftwing", - "chests off", - "findshrine", - "getbo 108", - "Urdars", - "chores" - ], - "QuestMF", - [ - "chores", - "getbo 83", - "Andariel", - "chests on", - "Mephisto", - "chests off", - "Travincal", - "Fire Eye", - "The Summoner", - "Duriel", - "The Cow King", - "Beetleburst", - "Dark Elder", - "Tunnels", - "chores" - ], - "PLA", - [ - "chores", - "getbo 83", - "Travincal", - "chores", - "areadelay in 102 CCA", - "ride CCA", - "chaos", - "getbo 108", - "ride CCA", - "chores" - ], - "Parzivol", - [ - "chores", - "givebo 83 QuestMF PLA CCA", - "Travincal", - "goto 75", - "areadelay in 102 CCA", - "ride CCA", - "chaos", - "givebo 108 PLA CCA", - "ride CCA", - "chores" - ] - ], - - //Chaos options, only apply to walky cs "chaos" - tele = "star", //Where to tp when running chaos. "gate" or "star" - teleMsg = [""], //Message(s) after tp to cs. If multiple, picks one at random - openSeals = [3], //Tele to and open seals after tp. 1 = infector main, 2 = infector other, 3 = seis, 4 = vizier other, 5 = vizier main - sealMsg = [], //Message(s) after pre-popping seals. If multiple, picks one at random - clearChars = ["PLA", "Parzivol"], //Name of chars who will clear chaos - clearMode = 0, //Clear mode = 0 = all, 0xF = skip normal, 0x7 = just champion/uniques (no minions) - clearDirection = 1, //0 for clockwise Vizi->Seis->Fect. 1 for counterclockwise Fect->Seis->Vizi - //Diablo options, apply to either walk cs "chaos" or taxi cs "Infector of Souls","Lord de Seis","Grand Vizier of Chaos","Diablo" - killDiablo = ["PLA"], //Names of chars who will kill Diablo. Leave out your Baron(ess) char(s) for best drops - leechDiablo = ["Parzivol"], //Names of chars who will leech d exp but not kill - weakenDiablo = [], //Names of chars who will leave party and cast weakening spells (static/curse/aura) but not kill - shrineDiablo = "Parzivol", //Name a char who will use experience shrine at diablo - //Delay options for good happy timings - boDelay = 180, //Max time to wait giving or getting bo - boQuit = false, //Quits if no bo given or received in time - taxiDelay = 120, //Max time to wait for a taxi tp - taxiQuit = false, //Quit if no taxi tp in time - chaosDelay = 180, //Max time to wait for a chaos tp - chaosQuit = false, //Quit if no cs tp found in time - shrineDelay = 40, //Max time to wait for a tp to shrine in useshrine - finderDelay = 0, //Make shrine finder wait for user to take - areaDelay = 30, //Max time to wait for other chars in areadelay - //END OF USER CONFIGS BEWARE NO TOUCHY BELOW - Bosses = [ - "The Cow King", - "Corpsefire", - "Bishibosh", - "Blood Raven", - "Bonebreaker", - "Coldcrow", - "Rakanishu", - "Griswold", - "Treehead Woodfist", - "The Countess", - "The Smith", - "Pitspawn Fouldog", - "Bone Ash", - "Andariel", - "Radament", - "Creeping Feature", - "Bloodwitch the Wild", - "Beetleburst", - "Coldworm the Burrower", - "Dark Elder", - "Fangskin", - "Fire Eye", - "The Summoner", - "Ancient Kaa the Soulless", - "Duriel", - "Sszark the Burning", - "Witch Doctor Endugu", - "Stormtree", - "Battlemaid Sarina", - "Icehawk Riftwing", - "Travincal", - "Bremm Sparkfist", - "Wyand Voidbringer", - "Maffer Dragonhand", - "Mephisto", - "Izual", - "Hephasto the Armorer", - "Infector of Souls", - "Lord de Seis", - "Grand Vizier of Chaos", - "Diablo" - ], - Tasks = [ - "chores", - "givebo", - "getbo", - "taxi", - "ride", - "chaos", - "getleg", - "makecows", - "clearcows", - "findshrine", - "useshrine", - "chests", - "goto", - "areadelay", - "Shopbot", - "Urdars", - "Tunnels" - ], - wps = [ - 1, 3, 3, - 3, 3, 3, - 4, 4, 5, - 6, 27, 29, - 32, 35, 48, - 42, 57, 43, - 43, 44, 44, - 74, 74, 46, - 46, 76, 78, - 79, 80, 81, - 83, 101, 101, - 101, 101, 106, - 106, 107, 107, - 107, 107, 106 - ], - exits = [ - [0], - [2, 8], - [0], - [17], - [17, 18], - [9], - [38], - [0], - [0], - [20, 21, 22, 23, 24, 25], - [28], - [30], - [33], - [36, 37], - [49], - [41, 55, 59], - [60], - [0], - [62, 63, 64], - [0], - [45, 58, 61], - [0], - [0], - [0], - [0], - [85], - [88, 89, 91], - [78], - [94], - [92], - [0], - [102], - [102], - [102], - [102], - [105], - [107], - [108], - [108], - [108], - [108] - ], - presets = [ - 773, - 774, - 734, - 805, - 735, - 736, - 737, - 739, - 738, - 740, - 754, - 741, - 743, - 156, - 744, - 748, - 745, - 747, - 749, - 751, - 746, - 750, - 250, - 753, - 211, - 755, - 756, - false, - 758, - 759, - false, - 762, - 764, - 765, - 242, - 256, - 775, - false, - false, - false, - false - ], - vizLayout, - seisLayout, - infLayout, - vizx, - vizy, - seisx, - seisy, - infx, - infy, - diax, - diay, - cmd, - last, - taxiChar = false, - chests = false, - runString, - runDone; - - /** - * @param {string | number} name - * @returns {boolean | number} - */ - this.find = function (name) { - let unit = Game.getPlayer(name); - if (unit) return unit.area; - - let party = getParty(); - if (!party) return false; - - do { - if (party.name === name) { - return (party.area > 0) ? party.area : true; - } - } while (party.getNext()); - return false; - }; - - /** - * @param {string | number} name - * @returns {Act} - */ - this.getAct = function(name) { - let _area = this.find(name); - return _area === true - ? me.act - : sdk.areas.actOf(_area); - }; - - /** - * @param {number} id - * @returns {boolean} - */ - this.openSeal = function (id) { - let seal = Game.getObject(id); - if (seal && seal.mode) return false; - Pather.moveToPreset(108, 2, id, 4, 4); - seal = Game.getObject(id); - if (!seal || (seal && seal.mode)) return false; - let retryCount = 0; - - while (!seal.mode) { - if (!Skill.cast(43, 0, seal) || retryCount > 50) seal.interact(); - delay(100); - if (seal.mode === 0) { - if (retryCount % 5 === 0) { - Pather.moveTo(seal.x + rand(-5, 5), seal.y + rand(-5, 5)); - } - delay(400); - retryCount++; - } - if (retryCount > 100) { - D2Bot.printToConsole("OPEN SEAL " + id + " FAILED in game: " + me.gamename, 9); - return false; - } - if (cmd === "Diablo") { - break; - } - } - return true; - }; - - this.getLayout = function(seal, value) { - let sealPreset = getPresetUnit(108, 2, seal); - if (!seal) { - throw new Error("Seal preset not found."); - } - if (sealPreset.roomy * 5 + sealPreset.y === value || sealPreset.roomx * 5 + sealPreset.x === value) { - return 1; - } - return 2; - }; - - this.initLayout = function() { - vizLayout = getLayout(396, 5275); // 1 = "Y", 2 = "L" - if (vizLayout === 1) vizx = 7680, vizy = 5295; // Y (7680,5295) L (7685,5320) - if (vizLayout === 2) vizx = 7685, vizy = 5320; - seisLayout = getLayout(394, 7773); // 1 = "2", 2 = "5" - if (seisLayout === 1) seisx = 7775, seisy = 5200; // 2 (7775,5220) 5 (7780,5185) - if (seisLayout === 2) seisx = 7780, seisy = 5175; - infLayout = getLayout(392, 7893); // 1 = "I", 2 = "J" - if (infLayout === 1) infx = 7910, infy = 5295; // I (7910,5295) J (7295,5280) - if (infLayout === 2) infx = 7925, infy = 5280; - diax = 7795, diay = 5295; - }; - - /** - * @param {string[]} char_str - */ - this.bo = function(char_str) { - let unit; - if (char_str) { - Precast.doPrecast(true); - for (let i = 0; i < char_str.length; i++) { - unit = getUnit(0, char_str[i]); - if (unit - && unit.name !== me.name - && !unit.dead - && Misc.inMyParty(unit.name) - && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) - && getDistance(me, unit) <= 20) { - Precast.doPrecast(true); - } - } - } else { - unit = getUnit(0); - if (unit) { - do { - if (unit.name !== me.name - && !unit.dead - && Misc.inMyParty(unit.name) - && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) - && getDistance(me, unit) <= 20) { - Precast.doPrecast(true); - } - } while (unit.getNext()); - } - } - }; - - this.chant = function(char_str) { - let unit; - if (char_str) { - for (let i = 0; i < char_str.length; i++) { - unit = getUnit(0, char_str[i]); - if (unit && !unit.getState(16) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(250); - - } - unit = getUnit(1); - if (unit) { - do { - if (unit.getParent() && char_str.indexOf(unit.getParent().name) > -1 && !unit.getState(16) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(500); - } - } while (unit.getNext()); - } - } - } else { - unit = getUnit(0); - if (unit) { - do { - if (!unit.getState(16) && Misc.inMyParty(unit.name) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(250); - } - } while (unit.getNext()); - - } - unit = getUnit(1); - if (unit) { - do { - if (unit.getParent() && !unit.getState(16) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(500); - } - } while (unit.getNext()); - } - } - }; - - this.pop = function() { - let monsterList = [], - monster = getUnit(1); - if (monster) { - do { - if ([345, 346, 347].indexOf(monster.classid) > -1 || (monster.spectype !== 0 && monster.spectype !== 8)) { - if (getDistance(me, monster) <= 30) monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - for (let i = 0; i < monsterList.length; i++) { - if ((monsterList[i].mode === 0 || monsterList[i].mode === 12) && !monsterList[i].getState(1) && !monsterList[i].getState(96) && !monsterList[i].getState(99) && !monsterList[i].getState(104) && !monsterList[i].getState(107) && !monsterList[i].getState(118)) { - console.log("popping " + monsterList[i].name + " spectype: " + monsterList[i].spectype + " classid: " + monsterList[i].classid); - Pather.moveTo(monsterList[i].x, monsterList[i].y, 3); - ClassAttack.findItem(10); - } - } - }; - - this.chests = function() { - let preset = getPresetUnits(me.area, 2), - coords = []; - while (preset.length > 0) { - if ([5, 6, 87, 88, 139, 147, 148, 177, 329, 330, 333, 371, 387, 389, 397, 424, 425, 454, 455, 580, 581].indexOf(preset[0].id) > -1) { - coords.push({ - x: preset[0].roomx * 5 + preset[0].x, - y: preset[0].roomy * 5 + preset[0].y - }); - } - preset.shift(); - } - while (coords.length) { - coords.sort(Sort.units); - Pather.moveToUnit(coords[0], 1, 2); - Misc.openChests(5); - coords.shift(); - } - }; - - this.delays = function(mode, delay_, char_str, mode2, aid) { - let tTimer = getTickCount(); - switch (mode) { - case "givebo": - for (let i = 0; i < char_str.length; i++) { - if (getTickCount() >= tTimer + delay_ * 1e3 - 5000 && i < char_str.length) { - D2Bot.printToConsole("give bo fail", 9); - if (boQuit) quit(); - break; - } - if (getUnit(0, char_str[i])) i++; - delay(100); - } - this.bo(char_str); - break; - case "getbo": - while (!Misc.inMyParty(char_str) || !getUnit(0, char_str, 10)) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("get bo fail 1", 9); - if (boQuit) quit(); - break; - } - delay(100); - } - while (getTickCount() <= tTimer + delay_ * 1e3 && (!me.getState(26) || !me.getState(32) || !me.getState(51))) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("get bo fail 2", 9); - if (boQuit) quit(); - break; - } - delay(100); - } - if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.chant(); - else { - for (let i = 0; i < 10; i++) { - let brk = true; - for (let j = 0; j < 5; j++) { - delay(100); - if (getUnit(0, char_str, 10)) brk = false; - } - if (brk) break; - } - } - break; - case "givechant": - { - let i = 0; - while (getTickCount() <= tTimer + delay_ * 1e3) { - if (getTickCount() >= tTimer + delay_ * 1e3) break; - if (char_str && char_str.length > 0 && getUnit(0, char_str[i])) { - this.chant(char_str[i]); - i++; - } else this.chant(); - delay(100); - } - } - break; - case "ride": - while (!Misc.inMyParty(char_str) || !taxiChar) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("wait for taxi fail", 9); - if (taxiQuit) quit(); - break; - } - delay(100); - } - break; - case "chaos": - if (getUnit(2, "waypoint")) Pather.useWaypoint(103, true); - else Town.goToTown(4); - Town.move("portalspot"); - while (me.area !== 108 && !Pather.usePortal(108, char_str)) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("wait for chaos fail", 9); - if (taxiQuit) quit(); - break; - } - delay(100); - } - break; - case "shrine": - Town.goToTown(1); - Town.move("portalspot"); - while (!me.getState(137)) { - if (char_str != null && !Misc.inMyParty(char_str)) break; - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("wait for shrine fail", 9); - if (taxiQuit) quit(); - break; - } - - if (Pather.usePortal(null, char_str)) { - let shrine = getUnit(2, "shrine"); - if (shrine) { - do { - if (shrine.objtype === 15 && shrine.mode === 0) { - Pather.moveTo(shrine.x - 2, shrine.y - 2); - Misc.getShrine(shrine); - Pather.makePortal(); - if (me.getState(137)) break; - } - } while (shrine.getNext()); - } - if (!char_str || !Pather.usePortal(1, char_str)) Town.goToTown(1); - break; - } - delay(100); - } - break; - case "finder": - loop1: - while (getTickCount() <= tTimer + delay_ * 1e3) { - let unit = getUnit(0); - if (unit) { - do { - if (unit.getState(137)) break loop1; - } while (unit.getNext()); - } - delay(100); - } - break; - case "area": - let chars_ = []; - let pp = getParty(); //big pp variable name come at me bro - if (pp) { - do { - if (!char_str) { - if (pp.name !== me.name) chars_.push(pp.name); - } else if (char_str) { - if (char_str.indexOf(pp.name) > -1 && pp.name !== me.name) chars_.push(pp.name); - } - } while (pp.getNext()); - } - loop2: - while (getTickCount() <= tTimer + delay_ * 1e3) { - delay(100); - if (mode2 === "in") { - for (let i = 0; i < chars_.length; i++) { - if (!char_str && this.find(chars_[i]) === aid) break loop2; - else if (char_str && this.find(chars_[i]) !== aid) break; - else if (char_str && i === chars_.length - 1) break loop2; - } - } else if (mode2 === "out") { - for (let i = 0; i < chars_.length; i++) { - if (this.find(chars_[i]) === aid) break; - else if (i === chars_.length - 1) break loop2; - } - } - } - break; - } - }; - - this.killBoss = function(name) { - if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); - if (me.name === taxiChar - && name !== "Grand Vizier of Chaos" - && name !== "Lord de Seis" - && name !== "Infector of Souls" - && name !== "Diablo") { - if (getUnit(1, name) && getDistance(getUnit(1, name)) > 10 && me.area !== 108) Pather.moveTo(getUnit(1, name).x + rand(-5, 5), getUnit(1, name).y + rand(-5, 5), 0); - Pather.makePortal(); - say(name); - } - if (name === "Grand Vizier of Chaos" - || name === "Lord de Seis" - || name === "Infector of Souls" - || name === "Diablo") { - if (!diax) this.initLayout(); - let sealTimer = getTickCount(); - - for (let i = 0; getTickCount() <= sealTimer + 20 * 1e3; i++) { - if (!getUnit(1, name)) { - if (!this.precast()) Attack.clear(10); - if (name === "Grand Vizier of Chaos") Pather.moveTo(vizx, vizy); - if (name === "Infector of Souls") Pather.moveTo(infx, infy); - if (name === "Lord de Seis") Pather.moveTo(seisx, seisy); - if (name === "Diablo") Pather.moveTo(diax, diay); - delay(10); - } else if (i >= 2000) break; - else break; - } - } - if (getUnit(1, name) && getUnit(1, name).hp > 0) { - if (me.name === taxiChar) { - for (let i = 0; i < 30; i++) { - let mon = getUnit(1, name); - if (getUnit(1, name)) Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); - if (!mon || mon.hp === 0) break; - delay(100); - } - } else if (getUnit(1, name)) { - if (getUnit(1, name).spectype === 0) { - Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0, name); - } else { - Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); - } - } - } - if (name === "Travincal") { - let monsterList = []; - let monster = Game.getMonster(); - if (monster) { - do { - if ([345, 346, 347].includes(monster.classid) && monster.attackable) { - monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - Attack.clearList(monsterList); - } - if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); - if (me.classid === 4 && Config.FindItem) this.pop(); - if (name === "Andariel") delay(2000); - Pickit.pickItems(); - return true; - }; - - this.runBoss = function (name, wp, exit, preset) { - console.log(name); - if (me.area !== wp && exit.indexOf(me.area) === -1) { - let wpunit = getUnit(2, "waypoint"); - if (!wpunit) Town.goToTown(); - Pather.useWaypoint(wp, true); - Precast.doPrecast(true); - } - if (!this.bossSpecial(name, 1)) return false; - if (exit.indexOf(me.area) > 0 && me.area !== exit[exit.length - 1]) { - let i = []; - for (let j = exit.indexOf(me.area); j < exit.length; j++) i.push(exit[j]); - exit = i; - } - if (exit[0] !== 0 && me.area !== exit[exit.length - 1]) Pather.moveToExit(exit, true); - if (!this.bossSpecial(name, 2)) return false; - if (preset && getPresetUnit(me.area, 1, preset)) Pather.moveToPreset(me.area, 1, preset); - if (this.bossSpecial(name, 3)) this.killBoss(name); - if (!this.bossSpecial(name, 4)) return false; - return true; - }; - - this.bossSpecial = function (name, t) { - if (t === 1) { - if (name === "The Cow King") { - Town.move("stash"); - for (let i = 0; i < 30; i++) { - if (Pather.usePortal(39)) break; - delay(100); - } - if (!Pather.usePortal(39)) return false; - else return true; - } - if (chests && ["Stormtree", "Battlemaid Sarina", "Icehawk Riftwing"].indexOf(name) > -1) { - this.chests(); - Pickit.pickItems(); - return true; - } - return true; - } - if (t === 2) { - if (chests && name === "Icehawk Riftwing") { - this.chests(); - } - if (name === "Griswold") { - Pather.moveToPreset(me.area, 1, 737); - while (!getUnit(2, 60)) delay(10); - Pather.usePortal(38); - return true; - } - if (name === "Fire Eye") { - Pather.usePortal(null); - return true; - } - if (name === "Ancient Kaa the Soulless") { - let j, k, pre, kaa = false; - for (j = 66; kaa === false; j++) { - if (me.area !== 46) Pather.moveToExit(46, true); - Pather.moveToExit(j, true); - pre = getPresetUnits(me.area, 1); - for (k = 0; k < pre.length; k++) { - if (pre[k].id === 753) kaa = true; - } - } - return true; - } - if (name === "Duriel") { - Pather.moveToExit(getRoom().correcttomb, true); - Pather.moveToPreset(me.area, 2, 152, -3, -3); - while (!getUnit(2, 100)) delay(10); - if (me.classid === 1 && me.getSkill(43, 1)) { - for (let i = 0; i < 10; i += 1) { - if (me.area === 73) { - break; - } - Skill.cast(43, 0, getUnit(2, 100)); - } - } else Pather.useUnit(2, 100, 73); - return true; - } - if (name === "Travincal") { - Pather.moveTo(me.x + 101, me.y - 71); - return true; - } - if (name === "Grand Vizier of Chaos") { - if (!vizx) this.initLayout(); - if (me.name === taxiChar) { - Pather.moveTo(vizx, vizy, 3); - Pather.makePortal(); - say(name); - } - this.openSeal(396); - Pather.moveTo(vizx, vizy, 3); - return true; - } - if (name === "Lord de Seis") { - if (!seisx) this.initLayout(); - if (me.name === taxiChar) { - Pather.moveTo(seisx, seisy, 3); - Pather.makePortal(); - say(name); - } - this.openSeal(394); - Pather.moveTo(seisx, seisy, 3); - return true; - } - if (name === "Infector of Souls") { - if (!infx) this.initLayout(); - if (me.name === taxiChar) { - Pather.moveTo(infx, infy, 3); - Pather.makePortal(); - say(name); - } - this.openSeal(392); - Pather.moveTo(infx, infy, 3); - return true; - } - if (name === "Diablo") { - if (!diax) this.initLayout(); - Pather.moveTo(diax, diay, 3); - return true; - } - return true; - } - if (t === 3) { - if (name === "Diablo") { - if (me.name === taxiChar) { - //cmd = "Diablo"; - say("Diablo"); - this.diablo(); - return false; - } else return true; - } - return true; - } - if (t === 4) { - if (name === "Fire Eye") { - Pather.moveTo(10073, 8668); - Pather.usePortal(null); - return true; - } - if (name === "The Summoner") { - let journal = getUnit(2, 357); - if (journal) { - Pather.moveTo(journal.x, journal.y); - for (let i = 0; i < 5; i += 1) { - journal.interact(); - delay(me.ping + 500); - me.cancel(); - delay(me.ping + 500); - if (Pather.getPortal(46)) break; - } - Pather.usePortal(46); - } - return true; - } - if (chests && ["Bonebreaker", "Coldcrow", "Countess", "Creeping Feature", "Coldworm the Burrower", "Dark Elder", "Icehawk Riftwing", "Mephisto"].indexOf(name) > -1) { - if (name === "Coldcrow") Pather.moveToExit(13, true); - if (name === "Dark Elder") Pather.moveToExit(65, true); - if (name === "Icehawk Riftwing") Pather.moveToExit(93, true); - this.chests(); - Pickit.pickItems(); - return true; - } - if (name === "Grand Vizier of Chaos") { - this.openSeal(395); - return true; - } - if (name === "Infector of Souls") { - this.openSeal(393); - return true; - } - return true; - } - return false; - }; - - this.runList = function (list) { - runString = []; - for (let i = 0; i < list.length; i++) { - if (Bosses.indexOf(list[i]) > -1) { - if (runString.indexOf(list[i]) === -1) runString.push(list[i]); - else D2Bot.printToConsole("boss '" + list[i] + "' listed more than once", 9); - } else { - for (let j = 0; j < Tasks.length; j++) { - if (list[i].indexOf(Tasks[j]) === 0) runString.push(list[i]); - } - if (runString.indexOf(list[i]) === -1) D2Bot.printToConsole("boss/task string '" + list[i] + "' not recognized and ignored", 9); - } - } - runDone = []; - for (let i = 0; runDone.length < runString.length; i++) { - if (Bosses.indexOf(runString[i]) > -1) { - let loc = Bosses.indexOf(runString[i]); - if (runDone.indexOf(runString[i]) === -1) { - this.runBoss(Bosses[loc], wps[loc], exits[loc], presets[loc]); - runDone.push(runString[i]); - } - } else { - for (let j = 0; j < Tasks.length; j++) { - if (runString[i].indexOf(Tasks[j]) === 0) { - this.runTask(runString[i]); - runDone.push(runString[i]); - } - } - } - } - return true; - }; - - this.runTask = function (task) { - console.log(task); - let wp; - if (task.indexOf("givebo") === 0) { - let boChars = []; - if (task === "givebo") { - wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf(" ") === -1) { - wp = Number(task); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - wp = Number(task.slice(0, task.indexOf(" "))); - for (let i = 0; i < chars.length; i += 2) { - if (task.indexOf(chars[i]) > -1) boChars.push(chars[i]); - } - } - if (isNaN(wp)) wp = boWP; - } - if (!wp || boChars.length === 0) { - D2Bot.printToConsole("givebo string error", 9); - return false; - } - Town.goToTown(); - Pather.useWaypoint(wp, true); - this.delays("givebo", boDelay, boChars); - } else if (task.indexOf("getbo") === 0) { - let boBarb; - if (task === "getbo") { - wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; - } - } - } else if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf(" ") === -1) { - wp = Number(task); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; - } - } - } - if (task.indexOf(" ") > -1) { - wp = Number(task.slice(0, task.indexOf(" "))); - boBarb = task.slice(task.indexOf(" ") + 1); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - if (task.indexOf(chars[i]) > -1) boBarb = chars[i - 1]; - } - } - } - if (!wp || !boBarb) { - D2Bot.printToConsole("getbo string error", 9); - return false; - } - Town.goToTown(); - Pather.useWaypoint(wp, true); - Pather.moveTo(me.x - 5, me.y - 5, 3); - this.delays("getbo", boDelay, boBarb); - } else if (task.indexOf("chant") === 0 || task.indexOf("givechant") === 0) { - let chantChars = []; - if (task === "chant" || task === "givechant") { - wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf(" ") === -1) { - wp = Number(task); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - wp = Number(task.slice(0, task.indexOf(" "))); - for (let i = 0; i < chars.length; i += 2) { - if (task.indexOf(chars[i]) > -1) chantChars.push(chars[i]); - } - } - if (isNaN(wp)) wp = boWP; - } - if (!wp || chantChars.length === 0) { - D2Bot.printToConsole("givechant string error", 9); - return false; - } - if (me.area !== wp) Pather.useWaypoint(wp, true); - this.delays("givechant", chantDelay, chantChars); - } else if (task.indexOf("taxi") === 0) { - if (task === "taxi") taxiChar = me.name; - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task === "on") { - taxiChar = me.name; - say("taxi"); - } - if (task === "off") { - taxiChar = false; - say("ixat"); - } - } - } else if (task.indexOf("ride") === 0) { - if (task === "ride") { - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("taxi") > -1) taxiChar = chars[i - 1]; - } - } - } else if (task.indexOf(" ") > -1) taxiChar = task.slice(task.indexOf(" ") + 1); - if (!taxiChar) { - D2Bot.printToConsole("ride string error", 9); - return false; - } - this.delays("ride", taxiDelay, taxiChar); - this.rideTaxi(); - } else if (task.indexOf("chests") === 0) { - if (task === "chests") { - chests = true; - } - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task === "on") chests = true; - if (task === "off") chests = false; - } - } else if (task === "chaos") { - if (me.name === taxiChar || (!taxiChar && me.classid === 1 && me.area !== 108)) { - if (me.area !== 107 && me.area !== 108) { - Town.goToTown(); - Pather.useWaypoint(107, true); - } - let teleTo; - if (tele === "gate") { - teleTo = [7795, 5555]; - } else if (tele === "star") { - teleTo = [7792, 5291]; - } - Pather.moveTo(teleTo[0], teleTo[1], 3); - Pather.makePortal(); - if (teleMsg.length > 0) say(teleMsg[rand(0, teleMsg.length - 1)]); - if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); - if (openSeals.length > 0) { - for (let k = 0; k < openSeals.length; k++) { - this.openSeal(391 + openSeals[k]); - } - Pather.moveTo(teleTo[0], teleTo[1], 3); - if (sealMsg.length > 0) say(sealMsg[rand(0, sealMsg.length - 1)]); - if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); - } - if (clearChars.indexOf(me.name) > -1) { - Pather.makePortal(); - this.clearChaos(); - } else { - Town.goToTown(); - //Town.doChores(); - //Town.move("portalspot"); - } - } else { - this.delays("chaos", chaosDelay, null); - if (me.area === 108 && clearChars.indexOf(me.name) > -1) { - this.clearChaos(); - } - } - } else if (task === "getleg") { - this.getLeg(); - } else if (task === "makecows") { - this.openPortal(); - } else if (task === "Shopbot") { - this.Shopbot(); - } else if (task === "Tunnels") { - Pather.journeyTo(65); - Attack.clearLevel(Config.ClearType); - } else if (task === "Urdars") { - Town.goToTown(); - Pather.useWaypoint(106); - Precast.doPrecast(true); - Pather.journeyTo(107); - this.Urdars(); - } else if (task === "findshrine") { - let shrineAreas = [2, 3, 4, 10, 5, 6, 7/*,26,27,28,29,30,31,32,33,34,35,41,42,43,44,76,77,78,79,80,81,82,104,105,106,107,108*/]; - Town.goToTown(); - Pather.useWaypoint(3, true); - for (let i = 0; i < shrineAreas.length; i++) { - Pather.journeyTo(shrineAreas[i]); - if (Misc.getShrinesInArea(shrineAreas[i], 15, false)) { - if (chars[chars.indexOf(me.name) + 1].indexOf("useshrine") === chars[chars.indexOf(me.name) + 1].indexOf("findshrine") + 1) { - let shrine = getUnit(2, "shrine"); - if (shrine) { - do { - if (shrine.objtype === 15 && shrine.mode === 0) { - Pather.moveTo(shrine.x - 3, shrine.y - 3); - Misc.getShrine(shrine); - if (me.getState(137)) break; - } - } while (shrine.getNext()); - } - } - break; - } - } - Town.goToTown(); - if (finderDelay && finderDelay > 0) this.delays("finder", finderDelay); - } else if (task === "useshrine") { - if (me.getState(137)) return true; - let finder; - for (let i = 1; i <= chars.length; i += 2) { - if (chars[i].indexOf("findshrine") > -1) finder = chars[i - 1]; - } - if (!finder) finder = null; - if (finder === me.name || (finder == null && me.classid === 1)) this.runTask("findshrine"); - this.delays("shrine", shrineDelay, finder); - } else if (task === "clearcows") { - if (me.area !== 39) { - Town.goToTown(1); - Town.move("stash"); - for (let i = 0; i < 30; i++) { - if (Pather.usePortal(39)) break; - delay(100); - } - } - if (me.area === 39) Attack.clearLevel(Config.ClearType); - } else if (task === "chores") { - Town.goToTown(); - Town.doChores(); - } else if (task.indexOf("areadelay") === 0) { - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf("in") === 0) { - if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "in", task); - else if (task.indexOf(" ") > -1) { - let aid = Number(task.slice(0, task.indexOf(" "))); - task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(aid)) this.delays("area", areaDelay, task, "in", aid); - else D2Bot.printToConsole("areadelay string error1", 9); - } else D2Bot.printToConsole("areadelay string error2", 9); - } else if (task.indexOf("out") === 0) { - if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "out", task); - else if (task.indexOf(" ") > -1) { - let aid = Number(task.slice(0, task.indexOf(" "))); - task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(aid)) this.delays("area", areaDelay, task, "out", aid); - else D2Bot.printToConsole("areadelay string error3", 9); - } else D2Bot.printToConsole("areadelay string error4", 9); - } else D2Bot.printToConsole("areadelay string error5", 9); - } else D2Bot.printToConsole("areadelay string error6", 9); - } else if (task.indexOf("goto") === 0) { - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - let aid = Number(task); - if (!isNaN(aid)) Pather.journeyTo(aid); - else D2Bot.printToConsole("goto string value error", 9); - } - return true; - } - return false; - }; - - this.clearChaos = function() { - this.clearAct = function(act, x, y) { - if (act === "move") { - let xPath = (clearMode === 0) ? true : false; - Pather.moveTo(x, y, 3, xPath); - if (tele === "gate" && x === 7794 && y === 5315) Pather.makePortal(); - if (tele === "star" && clearDirection === 0 && x === 7765 && y === 5294) Pather.makePortal(); - if (tele === "star" && clearDirection === 1 && x === 7825 && y === 5294) Pather.makePortal(); - Attack.clear(25, clearMode); - } else if (act === "seal") { - if (openSeal(x) === true) { - if (x === 392 || x === 396) { - for (let i = 0; i < 30; i++) { - let boss = (x === 392) ? getUnit(1, "Infector of Souls") : getUnit(1, "Grand Vizier of Chaos"); - if (boss) break; - delay(100); - } - } - } - if (((x === 393 && clearDirection === 0) || (x === 395 && clearDirection === 1)) && cmd !== "Diablo") { - cmd = "Diablo"; - say("Diablo"); - } - } else if (act === "Infector of Souls" || act === "Lord de Seis" || act === "Grand Vizier of Chaos") { - let boss = getUnit(1, act); - if (!boss) { - Pather.moveTo(x, y, 3, xPath); - Attack.clear(25, clearMode); - } - boss = getUnit(1, act); - if (boss) { - if (boss.hp <= 0 || boss.mode === 0 || boss.mode === 12) { - // - } else { - Attack.clear(25, clearMode, act); - } - } - } else if (act === "Diablo") { - cmd = "Diablo"; - } else { - throw new Error("Invalid clearAct"); - } - if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); - Precast.doPrecast(false); - }; - Precast.doPrecast(false); - Attack.clear(10); - initLayout(); - let Gate = ["move", 7794, 5545, "move", 7794, 5525, "move", 7794, 5505, "move", 7794, 5480, "move", 7794, 5440, "move", 7794, 5405, "move", 7794, 5365, "move", 7794, 5335, "move", 7794, 5315]; - let preFect = ["move", 7825, 5294, "move", 7843, 5294, "move", 7861, 5294]; - let Fect = (infLayout === 1) - ? ["move", 7890, 5295, "move", 7915, 5290, "seal", 392, 1, "Infector of Souls", 7890, 5295, "Infector of Souls", 7915, 5290, "seal", 393, 0] - : ["move", 7900, 5275, "move", 7930, 5280, "move", 7930, 5310, "seal", 392, 1, "move", 7930, 5310, "Infector of Souls", 7930, 5280, "Infector of Souls", 7900, 5275, "seal", 393, 0]; - let preSeis = ["move", 7794, 5265, "move", 7794, 5245, "move", 7794, 5225]; - let Seis2 = (openSeals.indexOf(3) > -1) - ? ["move", 7775, 5210, "move", 7775, 5195, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210] - : ["move", 7775, 5210, "move", 7775, 5195, "move", 7810, 5190, "move", 7810, 5155, "move", 7785, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210]; - let Seis5 = (openSeals.indexOf(3) > -1) ? ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195] : ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "move", 7805, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195]; - let Seis = (seisLayout === 1) ? Seis2 : Seis5; - let preVizi = ["move", 7765, 5294, "move", 7745, 5294, "move", 7715, 5294]; - let Vizi = (vizLayout === 1) - ? ["move", 7680, 5290, "move", 7665, 5275, "seal", 396, 2, "Grand Vizier of Chaos", 7665, 5275, "Grand Vizier of Chaos", 7680, 5290, "seal", 395, 0] - : ["move", 7700, 5315, "move", 7670, 5315, "seal", 396, 2, "Grand Vizier of Chaos", 7670, 5315, "Grand Vizier of Chaos", 7700, 5315, "seal", 395, 0]; - let Diablo = (clearDirection === 1) - ? ["move", 7765, 5295, "move", 7796, 5296, "Diablo", 666, 666] - : ["move", 7825, 5294, "move", 7795, 5295, "Diablo", 666, 666]; - let clearActQueue = []; - if (tele === "gate" && me.x >= 5520) clearActQueue = clearActQueue.concat(Gate); - if (clearDirection === 0) clearActQueue = clearActQueue.concat(preVizi, Vizi, preSeis, Seis, preFect, Fect, Diablo); - if (clearDirection === 1) clearActQueue = clearActQueue.concat(preFect, Fect, preSeis, Seis, preVizi, Vizi, Diablo); - for (let j = 1; clearActQueue.length > j; j += 3) { - if (cmd === "Diablo") { - if (last !== "Diablo") this.diablo(); - break; - } - this.clearAct(clearActQueue[j - 1], clearActQueue[j], clearActQueue[j + 1]); - if (cmd === "Diablo") { - if (last !== "Diablo") this.diablo(); - break; - } - delay(100); - } - return true; - }; - - this.diablo = function () { - cmd !== "Diablo" && (cmd = "Diablo"); - if (last === "Diablo") return true; - - if (taxiChar === me.name) { - if (me.area === 108) { - Pather.moveTo(diax, diay, 3); - if ((killDiablo.includes(me.name) - || leechDiablo.includes(me.name) - || weakenDiablo.includes(me.name)) - && me.name !== shrineDiablo) { - Pather.makePortal(); - } else { - Pather.makePortal(true); - } - } - if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { - this.delays("area", 30, shrineDiablo, "in", 108); - } else if (killDiablo.length > 0) { - this.delays("area", 30, killDiablo, "in", 108); - } - } - - if (shrineDiablo === me.name) { - let finder; - for (let i = 1; i <= chars.length; i += 2) { - if (chars[i].includes("findshrine")) { - finder = chars[i - 1]; - } - } - - !finder && (finder = null); - if (finder === me.name || (finder === null && me.sorceress)) { - this.runTask("findshrine"); - } - console.log(finder); - this.delays("shrine", shrineDelay, finder); - } - if (killDiablo.indexOf(me.name) > -1) { - if (me.area !== 108) { - Town.goToTown(4); - Town.move("portalspot"); - if (!taxiChar || !Pather.usePortal(108, taxiChar)) { - let portal = getUnit(2, "portal"); - if (portal) { - do { - if (portal.objtype === 108 - && portal.getParent() !== me.name - && Misc.inMyParty(portal.getParent()) - && chars.indexOf(portal.getParent()) > -1) { - if (Pather.usePortal(null, null, portal)) break; - } - } while (portal.getNext()); - } - } - } - if (me.area !== 108) Pather.usePortal(108, null); - if (me.area === 108) { - Pather.moveTo(diax, diay, 3); - Pather.makePortal(); - if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { - this.delays("area", 60, shrineDiablo, "in", 108); //60 was 30 - } - this.killBoss("Diablo"); - } - } else if (leechDiablo.indexOf(me.name) > -1) { - if (me.area !== 108) { - Town.goToTown(4); - Town.move("portalspot"); - if (taxiChar) { - if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); - } else Pather.usePortal(108, null); - } - if (me.area === 108) { - (leechDiablo.indexOf(me.name) % 2 === 0) - ? Pather.moveTo(7792, 5291, 3) - : Pather.moveTo(7792, 5291, 3); - for (let i = 0; i < 200; i++) { - if (!getUnit(1, 243)) { - delay(100); - } - } - while (getUnit(1, "Diablo") && getUnit(1, "Diablo").hp > 0) delay(100); - Pickit.pickItems(); - } - } else if (weakenDiablo.indexOf(me.name) > -1) { - if (me.area !== 108) { - Town.goToTown(4); - Town.move("portalspot"); - if (taxiChar) { - if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); - Pather.makePortal(); - } else Pather.usePortal(108, null); - } - if (me.area === 108) { - if (!diax) this.initLayout(); - Pather.moveTo(diax, diay, 3); - let waitfor = killDiablo.concat(leechDiablo); - console.log(waitfor); - loop: - for (let i = 0; i < 300; i++) { - for (let j = 0; j < waitfor.length; j++) { - delay(100); - if (Misc.inMyParty(waitfor[j]) && !getUnit(0, waitfor[j])) break; - else if ((!Misc.inMyParty(waitfor[j]) || getUnit(0, waitfor[j])) && j === waitfor.length - 1) break loop; - } - } - Config.PublicMode = 0; - delay(2 * me.ping + 100); - let player = getParty(); - clickParty(player, 3); - for (let i = 0; i < 200; i++) { - if (!getUnit(1, 243)) { - delay(100); - } - } - while (getUnit(1, 243) && getUnit(1, 243).hp > 0) { - switch (me.classid) { - case 0: //Amazon - break; - case 1: //Sorceress - if (Skill.cast(42, 0)) Skill.cast(42, 0); //static - break; - case 2: //Necromancer - if (Skill.cast(Config.Curse[0], 0)) Skill.cast(Config.Curse[0], 0); //spam boss curse - //else if other curses - break; - case 3: //Paladin - if (Skill.setSkill(123, 0)) Skill.setSkill(123, 0); //conviction aura - break; - case 4: //Barbarian - break; - } - delay(100); - } - Pickit.pickItems(); - } - } else { - if (me.area === 108) { - if (clearDirection === 0) Pather.moveTo(7825, 5294, 3); - if (clearDirection === 1) Pather.moveTo(7765, 5294, 3); - } - Town.goToTown(); - } - taxiChar = false; - last = "Diablo"; - return true; - }; - - this.getLeg = function () { - if (Pather.getPortal(sdk.areas.MooMooFarm)) return false; - /** @type {ItemUnit} */ - let leg; - let gid; - let wrongLeg; // this isn't doing anything - - while (!leg) { - if (me.getItem(sdk.quest.item.WirtsLeg)) { - leg = me.getItem(sdk.quest.item.WirtsLeg); - return me.getItem(-1, -1, leg.gid); - } - - leg = Game.getItem(sdk.quest.item.WirtsLeg); - if (leg) { - do { - if (leg.name.indexOf("ÿc1") > -1) { - wrongLeg = true; - - return false; - } else { - gid = leg.gid; - Pickit.pickItem(leg); - - return me.getItem(-1, -1, gid); - } - } while (leg.getNext()); - } - - if (chars[chars.indexOf(me.name) + 1].indexOf("getleg") > -1) { - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); - - /** @type {ObjectUnit} */ - let portal; - for (let i = 0; i < 6; i += 1) { - portal = Pather.getPortal(sdk.areas.Tristram); - if (portal) { - Pather.usePortal(null, null, portal); - break; - } - delay(500); - } - - if (!portal) { - Town.goToTown(); - return false; - } - - Pather.moveTo(25048, 5177); - let wirt = Game.getObject(sdk.quest.chest.Wirt); - for (let i = 0; i < 8; i += 1) { - !!wirt && wirt.interact(); - delay(500); - leg = Game.getItem(sdk.quest.item.WirtsLeg); - - if (leg) { - gid = leg.gid; - Pickit.pickItem(leg); - Town.goToTown(1); - - if (chars[chars.indexOf(me.name) + 1].indexOf("makecows") === -1) { - Town.move("stash"); - leg.drop(); - return true; - } else { - return me.getItem(-1, -1, gid); - } - } - } - Town.goToTown(); - } else { - Town.move("stash"); - } - delay(100); - } - return false; - }; - - this.getTome = function () { - let myTome = me.findItem("tbk", 0, 3); - let tome = me.getItem("tbk"); - if (tome) { - do { - if (!myTome || tome.gid !== myTome.gid) { - return copyUnit(tome); - } - } while (tome.getNext()); - } - - Town.move(NPC.Akara); - let akara = Town.initNPC("Shop"); - if (!akara) return false; - - tome = akara.getItem("tbk"); - if (tome.buy()) { - tome = me.getItem("tbk"); - if (tome) { - do { - if (!myTome || tome.gid !== myTome.gid) { - return copyUnit(tome); - } - } while (tome.getNext()); - } - } - return false; - }; - - this.openPortal = function () { - Town.goToTown(1); - let leg = this.getLeg(); - if (!leg) return false; - - let tome = this.getTome(); - if (!tome) return false; - - if (!Town.openStash() - || !Cubing.emptyCube() - || !Storage.Cube.MoveTo(leg) - || !Storage.Cube.MoveTo(tome) - || !Cubing.openCube()) { - return false; - } - transmute(); - delay(500); - for (let i = 0; i < 10; i += 1) { - if (Pather.getPortal(39)) { - return true; - } - delay(200); - } - me.cancel(); - return false; - }; - - this.rideTaxi = function() { - let tTimer = getTickCount(); - while (true) { - if (!taxiChar || !Misc.inMyParty(taxiChar)) { - break; - } - - if (this.getAct(taxiChar) > 0) { - Town.goToTown(this.getAct(taxiChar)); - Town.move("portalspot"); - } - - if (cmd && cmd !== last) { - let currCmd = cmd; - if (currCmd === "Diablo") { - this.diablo(); - } else if (teleMsg.includes(currCmd)) { - if (clearChars.includes(me.name)) { - this.delays("chaos", chaosDelay, taxiChar); - this.clearChaos(); - } - taxiChar = false; - - break; - } else if (Pather.getPortal(null, taxiChar)) { - if (Pather.usePortal(null, taxiChar)) { - if (currCmd === cmd) { - this.killBoss(currCmd); - } - Town.goToTown(); - } - } - last = currCmd; - tTimer = getTickCount(); - } - - if (getTickCount > tTimer + taxiDelay * 1e3) { - taxiChar = false; - break; - } - Precast.doPrecast(false); - delay(100); - } - }; - - this.Shopbot = function () { - Loader.runScript("ShopBot"); - }; - - /** - * @param {string | number} name - * @returns {boolean} - */ - this.Urdars = function (name) { - let room = getRoom(); - if (!room) return false; - - const urdars = [189, 309, 361, 311, 675, 300, 692]; - const rooms = []; - /** @param {Monster} monster */ - const check = function (monster) { - return ( - urdars.includes(monster.classid) - && monster.attackable - && monster.spectype !== sdk.monsters.spectype.All - && monster.spectype !== sdk.monsters.spectype.Minion - && 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 = []; - let monster = Game.getMonster(name); - - if (monster) { - do { - if (check(monster)) { - monList.push(copyUnit(monster)); - - if (me.name === taxiChar) { - Pather.moveTo(monster.x + rand(-5, 5), monster.y + rand(-5, 5), 0); - Pather.makePortal(); - say(monster.name); - } - - if (monster && monster.hp > 0) { - if (me.name === taxiChar) { - for (let i = 0; i < 30; i++) { - if (monster) Attack.clear(getDistance(me, monster) + 5, 0xF, monster); - if (!monster || monster.hp === 0) break; - delay(100); - } - } else if (monster) { - if (monster.spectype === sdk.monsters.spectype.All) { - Attack.clear(getDistance(me, monster) + 5, 0, monster); - } else { - Attack.clear(getDistance(me, monster) + 5, 0xF, monster); - } - } - } - } - } while (monster.getNext()); - } - - if (!Attack.clearList(monList)) { - return false; - } - } - } - - return true; - }; - - this.precast = function () { - switch (me.classid) { - case sdk.player.class.Amazon: - case sdk.player.class.Barbarian: - return false; - case sdk.player.class.Sorceress: //Sorceress - return ( - Skill.cast(sdk.skills.Blizzard) - || Skill.cast(sdk.skills.Meteor) - || Skill.cast(sdk.skills.StaticField) - || Skill.cast(sdk.skills.FrostNova) - || Skill.cast(sdk.skills.Hydra) - || Skill.cast(sdk.skills.Nova) - ); - case sdk.player.class.Necromancer: - return Skill.cast(Config.Curse[1], 0); - case sdk.player.class.Paladin: - return ( - Skill.setSkill(sdk.skills.Concentration, 0) - && Skill.cast(sdk.skills.BlessedHammer) - ); - } - return false; - }; - - /** - * @param {string} nick - * @param {string} msg - */ - function ChatEvent(nick, msg) { - if (!taxiChar && msg === "taxi" && chars.includes(nick)) { - taxiChar = nick; - } else if (nick === taxiChar && msg === "ixat" && cmd !== "Diablo") { - taxiChar = false; - } else if (nick === taxiChar && nick !== me.name) { - cmd = msg; - } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick) && clearChars.indexOf(me.name)) { - cmd = "Diablo"; - } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick)) { - if (leechDiablo.includes(me.name)) { - taxiChar = nick; - } - this.diablo(); - } - } - - // START - let listen = false; - for (let i = 0; i < chars[chars.indexOf(me.name) + 1].length; i++) { - if (chars[chars.indexOf(me.name) + 1][i].indexOf("ride") > -1) { - listen = true; - } - } - if (!listen && ( - killDiablo.includes(me.name) - || leechDiablo.includes(me.name) - || weakenDiablo.includes(me.name) - || clearChars.includes(me.name) - )) { - listen = true; - } - if (listen) { - addEventListener("chatmsg", ChatEvent); - } - Pickit.pickItems(); - Town.heal(); - this.runList(chars[chars.indexOf(me.name) + 1]); - if (killDiablo.indexOf(me.name) === -1) { - for (let i = 0; i < killDiablo.length; i++) { - while (Misc.inMyParty(killDiablo[i])) { - if (me.sorceress && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) { - this.delays("givechant", 1); - } else { - delay(1000); - } - } - } - } - - return true; -} diff --git a/d2bs/kolbot/libs/scripts/rungamex.txt b/d2bs/kolbot/libs/scripts/rungamex.txt deleted file mode 100644 index bf4cedfd7..000000000 --- a/d2bs/kolbot/libs/scripts/rungamex.txt +++ /dev/null @@ -1,1781 +0,0 @@ -/** -* @filename rungame2.js -* @author darjkeel@d2jsp -* @desc one script to run them all, and in the dankness bind them -* @readme rungame2 readme.txt -**/ - -function rungamex() { - let boWP = 83, //Default bo wp - chars = [ //Define chars and list their bosses/tasks - "Sorceress", - [ - "chores", - "getbo 83", - "Sszark the Burning", - "goto 102", - "areadelay in 75 Parzivol PLA", - "taxi on", - "areadelay in 75 Parzivol PLA", - "Wyand Voidbringer", - "areadelay in 75 Parzivol PLA", - "Bremm Sparkfist", - "areadelay in 75 Parzivol PLA", - "Maffer Dragonhand", - "goto 105", - "areadelay in 103 Parzivol PLA", - "Izual", - "Hephasto the Armorer", - "taxi off", - "chaos", - "areadelay in 108 Parzivol PLA", - "getleg", - "makecows", - "chests on", - "Witch Doctor Endugu", - "Icehawk Riftwing", - "chests off", - "findshrine", - "getbo 108", - "Urdars", - "chores" - ], - "QuestMF", - [ - "chores", - "getbo 83", - "Andariel", - "chests on", - "Mephisto", - "chests off", - "Travincal", - "Fire Eye", - "The Summoner", - "Duriel", - "The Cow King", - "Beetleburst", - "Dark Elder", - "Tunnels", - "chores" - ], - "PLA", - [ - "chores", - "getbo 83", - "Travincal", - "chores", - "areadelay in 102 CCA", - "ride CCA", - "chaos", - "getbo 108", - "ride CCA", - "chores" - ], - "Parzivol", - [ - "chores", - "givebo 83 QuestMF PLA CCA", - "Travincal", - "goto 75", - "areadelay in 102 CCA", - "ride CCA", - "chaos", - "givebo 108 PLA CCA", - "ride CCA", - "chores" - ] - ], - - //Chaos options, only apply to walky cs "chaos" - tele = "star", //Where to tp when running chaos. "gate" or "star" - teleMsg = [""], //Message(s) after tp to cs. If multiple, picks one at random - openSeals = [3], //Tele to and open seals after tp. 1 = infector main, 2 = infector other, 3 = seis, 4 = vizier other, 5 = vizier main - sealMsg = [], //Message(s) after pre-popping seals. If multiple, picks one at random - clearChars = ["PLA", "Parzivol"], //Name of chars who will clear chaos - clearMode = 0, //Clear mode = 0 = all, 0xF = skip normal, 0x7 = just champion/uniques (no minions) - clearDirection = 1, //0 for clockwise Vizi->Seis->Fect. 1 for counterclockwise Fect->Seis->Vizi - //Diablo options, apply to either walk cs "chaos" or taxi cs "Infector of Souls","Lord de Seis","Grand Vizier of Chaos","Diablo" - killDiablo = ["PLA"], //Names of chars who will kill Diablo. Leave out your Baron(ess) char(s) for best drops - leechDiablo = ["Parzivol"], //Names of chars who will leech d exp but not kill - weakenDiablo = [], //Names of chars who will leave party and cast weakening spells (static/curse/aura) but not kill - shrineDiablo = "Parzivol", //Name a char who will use experience shrine at diablo - //Delay options for good happy timings - boDelay = 180, //Max time to wait giving or getting bo - boQuit = false, //Quits if no bo given or received in time - taxiDelay = 120, //Max time to wait for a taxi tp - taxiQuit = false, //Quit if no taxi tp in time - chaosDelay = 180, //Max time to wait for a chaos tp - chaosQuit = false, //Quit if no cs tp found in time - shrineDelay = 40, //Max time to wait for a tp to shrine in useshrine - finderDelay = 0, //Make shrine finder wait for user to take - areaDelay = 30, //Max time to wait for other chars in areadelay - //END OF USER CONFIGS BEWARE NO TOUCHY BELOW - Bosses = [ - "The Cow King", - "Corpsefire", - "Bishibosh", - "Blood Raven", - "Bonebreaker", - "Coldcrow", - "Rakanishu", - "Griswold", - "Treehead Woodfist", - "The Countess", - "The Smith", - "Pitspawn Fouldog", - "Bone Ash", - "Andariel", - "Radament", - "Creeping Feature", - "Bloodwitch the Wild", - "Beetleburst", - "Coldworm the Burrower", - "Dark Elder", - "Fangskin", - "Fire Eye", - "The Summoner", - "Ancient Kaa the Soulless", - "Duriel", - "Sszark the Burning", - "Witch Doctor Endugu", - "Stormtree", - "Battlemaid Sarina", - "Icehawk Riftwing", - "Travincal", - "Bremm Sparkfist", - "Wyand Voidbringer", - "Maffer Dragonhand", - "Mephisto", - "Izual", - "Hephasto the Armorer", - "Infector of Souls", - "Lord de Seis", - "Grand Vizier of Chaos", - "Diablo" - ], - Tasks = [ - "chores", - "givebo", - "getbo", - "taxi", - "ride", - "chaos", - "getleg", - "makecows", - "clearcows", - "findshrine", - "useshrine", - "chests", - "goto", - "areadelay", - "Shopbot", - "Urdars", - "Tunnels" - ], - wps = [ - 1, 3, 3, - 3, 3, 3, - 4, 4, 5, - 6, 27, 29, - 32, 35, 48, - 42, 57, 43, - 43, 44, 44, - 74, 74, 46, - 46, 76, 78, - 79, 80, 81, - 83, 101, 101, - 101, 101, 106, - 106, 107, 107, - 107, 107, 106 - ], - exits = [ - [0], - [2, 8], - [0], - [17], - [17, 18], - [9], - [38], - [0], - [0], - [20, 21, 22, 23, 24, 25], - [28], - [30], - [33], - [36, 37], - [49], - [41, 55, 59], - [60], - [0], - [62, 63, 64], - [0], - [45, 58, 61], - [0], - [0], - [0], - [0], - [85], - [88, 89, 91], - [78], - [94], - [92], - [0], - [102], - [102], - [102], - [102], - [105], - [107], - [108], - [108], - [108], - [108] - ], - presets = [ - 773, - 774, - 734, - 805, - 735, - 736, - 737, - 739, - 738, - 740, - 754, - 741, - 743, - 156, - 744, - 748, - 745, - 747, - 749, - 751, - 746, - 750, - 250, - 753, - 211, - 755, - 756, - false, - 758, - 759, - false, - 762, - 764, - 765, - 242, - 256, - 775, - false, - false, - false, - false - ], - vizLayout, - seisLayout, - infLayout, - vizx, - vizy, - seisx, - seisy, - infx, - infy, - diax, - diay, - cmd, - last, - taxiChar = false, - chests = false, - runString, - runDone; - - /** - * @param {string | number} name - * @returns {boolean | number} - */ - this.find = function (name) { - let unit = Game.getPlayer(name); - if (unit) return unit.area; - - let party = getParty(); - if (!party) return false; - - do { - if (party.name === name) { - return (party.area > 0) ? party.area : true; - } - } while (party.getNext()); - return false; - }; - - /** - * @param {string | number} name - * @returns {Act} - */ - this.getAct = function(name) { - let _area = this.find(name); - return _area === true - ? me.act - : sdk.areas.actOf(_area); - }; - - /** - * @param {number} id - * @returns {boolean} - */ - this.openSeal = function (id) { - let seal = Game.getObject(id); - if (seal && seal.mode) return false; - Pather.moveToPreset(108, 2, id, 4, 4); - seal = Game.getObject(id); - if (!seal || (seal && seal.mode)) return false; - let retryCount = 0; - - while (!seal.mode) { - if (!Skill.cast(43, 0, seal) || retryCount > 50) seal.interact(); - delay(100); - if (seal.mode === 0) { - if (retryCount % 5 === 0) { - Pather.moveTo(seal.x + rand(-5, 5), seal.y + rand(-5, 5)); - } - delay(400); - retryCount++; - } - if (retryCount > 100) { - D2Bot.printToConsole("OPEN SEAL " + id + " FAILED in game: " + me.gamename, 9); - return false; - } - if (cmd === "Diablo") { - break; - } - } - return true; - }; - - this.getLayout = function(seal, value) { - let sealPreset = getPresetUnit(108, 2, seal); - if (!seal) { - throw new Error("Seal preset not found."); - } - if (sealPreset.roomy * 5 + sealPreset.y === value || sealPreset.roomx * 5 + sealPreset.x === value) { - return 1; - } - return 2; - }; - - this.initLayout = function() { - vizLayout = getLayout(396, 5275); // 1 = "Y", 2 = "L" - if (vizLayout === 1) vizx = 7680, vizy = 5295; // Y (7680,5295) L (7685,5320) - if (vizLayout === 2) vizx = 7685, vizy = 5320; - seisLayout = getLayout(394, 7773); // 1 = "2", 2 = "5" - if (seisLayout === 1) seisx = 7775, seisy = 5200; // 2 (7775,5220) 5 (7780,5185) - if (seisLayout === 2) seisx = 7780, seisy = 5175; - infLayout = getLayout(392, 7893); // 1 = "I", 2 = "J" - if (infLayout === 1) infx = 7910, infy = 5295; // I (7910,5295) J (7295,5280) - if (infLayout === 2) infx = 7925, infy = 5280; - diax = 7795, diay = 5295; - }; - - /** - * @param {string[]} char_str - */ - this.bo = function(char_str) { - let unit; - if (char_str) { - Precast.doPrecast(true); - for (let i = 0; i < char_str.length; i++) { - unit = getUnit(0, char_str[i]); - if (unit - && unit.name !== me.name - && !unit.dead - && Misc.inMyParty(unit.name) - && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) - && getDistance(me, unit) <= 20) { - Precast.doPrecast(true); - } - } - } else { - unit = getUnit(0); - if (unit) { - do { - if (unit.name !== me.name - && !unit.dead - && Misc.inMyParty(unit.name) - && (!unit.getState(26) || !unit.getState(32) || !unit.getState(51)) - && getDistance(me, unit) <= 20) { - Precast.doPrecast(true); - } - } while (unit.getNext()); - } - } - }; - - this.chant = function(char_str) { - let unit; - if (char_str) { - for (let i = 0; i < char_str.length; i++) { - unit = getUnit(0, char_str[i]); - if (unit && !unit.getState(16) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(250); - - } - unit = getUnit(1); - if (unit) { - do { - if (unit.getParent() && char_str.indexOf(unit.getParent().name) > -1 && !unit.getState(16) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(500); - } - } while (unit.getNext()); - } - } - } else { - unit = getUnit(0); - if (unit) { - do { - if (!unit.getState(16) && Misc.inMyParty(unit.name) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(250); - } - } while (unit.getNext()); - - } - unit = getUnit(1); - if (unit) { - do { - if (unit.getParent() && !unit.getState(16) && getDistance(me, unit) <= 40) { - Skill.setSkill(52, 0); - sendPacket(1, 0x11, 4, unit.type, 4, unit.gid); - delay(500); - } - } while (unit.getNext()); - } - } - }; - - this.pop = function() { - let monsterList = [], - monster = getUnit(1); - if (monster) { - do { - if ([345, 346, 347].indexOf(monster.classid) > -1 || (monster.spectype !== 0 && monster.spectype !== 8)) { - if (getDistance(me, monster) <= 30) monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - for (let i = 0; i < monsterList.length; i++) { - if ((monsterList[i].mode === 0 || monsterList[i].mode === 12) && !monsterList[i].getState(1) && !monsterList[i].getState(96) && !monsterList[i].getState(99) && !monsterList[i].getState(104) && !monsterList[i].getState(107) && !monsterList[i].getState(118)) { - console.log("popping " + monsterList[i].name + " spectype: " + monsterList[i].spectype + " classid: " + monsterList[i].classid); - Pather.moveTo(monsterList[i].x, monsterList[i].y, 3); - ClassAttack.findItem(10); - } - } - }; - - this.chests = function() { - let preset = getPresetUnits(me.area, 2), - coords = []; - while (preset.length > 0) { - if ([5, 6, 87, 88, 139, 147, 148, 177, 329, 330, 333, 371, 387, 389, 397, 424, 425, 454, 455, 580, 581].indexOf(preset[0].id) > -1) { - coords.push({ - x: preset[0].roomx * 5 + preset[0].x, - y: preset[0].roomy * 5 + preset[0].y - }); - } - preset.shift(); - } - while (coords.length) { - coords.sort(Sort.units); - Pather.moveToUnit(coords[0], 1, 2); - Misc.openChests(5); - coords.shift(); - } - }; - - this.delays = function(mode, delay_, char_str, mode2, aid) { - let tTimer = getTickCount(); - switch (mode) { - case "givebo": - for (let i = 0; i < char_str.length; i++) { - if (getTickCount() >= tTimer + delay_ * 1e3 - 5000 && i < char_str.length) { - D2Bot.printToConsole("give bo fail", 9); - if (boQuit) quit(); - break; - } - if (getUnit(0, char_str[i])) i++; - delay(100); - } - this.bo(char_str); - break; - case "getbo": - while (!Misc.inMyParty(char_str) || !getUnit(0, char_str, 10)) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("get bo fail 1", 9); - if (boQuit) quit(); - break; - } - delay(100); - } - while (getTickCount() <= tTimer + delay_ * 1e3 && (!me.getState(26) || !me.getState(32) || !me.getState(51))) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("get bo fail 2", 9); - if (boQuit) quit(); - break; - } - delay(100); - } - if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.chant(); - else { - for (let i = 0; i < 10; i++) { - let brk = true; - for (let j = 0; j < 5; j++) { - delay(100); - if (getUnit(0, char_str, 10)) brk = false; - } - if (brk) break; - } - } - break; - case "givechant": - { - let i = 0; - while (getTickCount() <= tTimer + delay_ * 1e3) { - if (getTickCount() >= tTimer + delay_ * 1e3) break; - if (char_str && char_str.length > 0 && getUnit(0, char_str[i])) { - this.chant(char_str[i]); - i++; - } else this.chant(); - delay(100); - } - } - break; - case "ride": - while (!Misc.inMyParty(char_str) || !taxiChar) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("wait for taxi fail", 9); - if (taxiQuit) quit(); - break; - } - delay(100); - } - break; - case "chaos": - if (getUnit(2, "waypoint")) Pather.useWaypoint(103, true); - else Town.goToTown(4); - Town.move("portalspot"); - while (me.area !== 108 && !Pather.usePortal(108, char_str)) { - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("wait for chaos fail", 9); - if (taxiQuit) quit(); - break; - } - delay(100); - } - break; - case "shrine": - Town.goToTown(1); - Town.move("portalspot"); - while (!me.getState(137)) { - if (char_str != null && !Misc.inMyParty(char_str)) break; - if (getTickCount() >= tTimer + delay_ * 1e3) { - D2Bot.printToConsole("wait for shrine fail", 9); - if (taxiQuit) quit(); - break; - } - - if (Pather.usePortal(null, char_str)) { - let shrine = getUnit(2, "shrine"); - if (shrine) { - do { - if (shrine.objtype === 15 && shrine.mode === 0) { - Pather.moveTo(shrine.x - 2, shrine.y - 2); - Misc.getShrine(shrine); - Pather.makePortal(); - if (me.getState(137)) break; - } - } while (shrine.getNext()); - } - if (!char_str || !Pather.usePortal(1, char_str)) Town.goToTown(1); - break; - } - delay(100); - } - break; - case "finder": - loop1: - while (getTickCount() <= tTimer + delay_ * 1e3) { - let unit = getUnit(0); - if (unit) { - do { - if (unit.getState(137)) break loop1; - } while (unit.getNext()); - } - delay(100); - } - break; - case "area": - let chars_ = []; - let pp = getParty(); //big pp variable name come at me bro - if (pp) { - do { - if (!char_str) { - if (pp.name !== me.name) chars_.push(pp.name); - } else if (char_str) { - if (char_str.indexOf(pp.name) > -1 && pp.name !== me.name) chars_.push(pp.name); - } - } while (pp.getNext()); - } - loop2: - while (getTickCount() <= tTimer + delay_ * 1e3) { - delay(100); - if (mode2 === "in") { - for (let i = 0; i < chars_.length; i++) { - if (!char_str && this.find(chars_[i]) === aid) break loop2; - else if (char_str && this.find(chars_[i]) !== aid) break; - else if (char_str && i === chars_.length - 1) break loop2; - } - } else if (mode2 === "out") { - for (let i = 0; i < chars_.length; i++) { - if (this.find(chars_[i]) === aid) break; - else if (i === chars_.length - 1) break loop2; - } - } - } - break; - } - }; - - this.killBoss = function(name) { - if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); - if (me.name === taxiChar - && name !== "Grand Vizier of Chaos" - && name !== "Lord de Seis" - && name !== "Infector of Souls" - && name !== "Diablo") { - if (getUnit(1, name) && getDistance(getUnit(1, name)) > 10 && me.area !== 108) Pather.moveTo(getUnit(1, name).x + rand(-5, 5), getUnit(1, name).y + rand(-5, 5), 0); - Pather.makePortal(); - say(name); - } - if (name === "Grand Vizier of Chaos" - || name === "Lord de Seis" - || name === "Infector of Souls" - || name === "Diablo") { - if (!diax) this.initLayout(); - let sealTimer = getTickCount(); - - for (let i = 0; getTickCount() <= sealTimer + 20 * 1e3; i++) { - if (!getUnit(1, name)) { - if (!this.precast()) Attack.clear(10); - if (name === "Grand Vizier of Chaos") Pather.moveTo(vizx, vizy); - if (name === "Infector of Souls") Pather.moveTo(infx, infy); - if (name === "Lord de Seis") Pather.moveTo(seisx, seisy); - if (name === "Diablo") Pather.moveTo(diax, diay); - delay(10); - } else if (i >= 2000) break; - else break; - } - } - if (getUnit(1, name) && getUnit(1, name).hp > 0) { - if (me.name === taxiChar) { - for (let i = 0; i < 30; i++) { - let mon = getUnit(1, name); - if (getUnit(1, name)) Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); - if (!mon || mon.hp === 0) break; - delay(100); - } - } else if (getUnit(1, name)) { - if (getUnit(1, name).spectype === 0) { - Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0, name); - } else { - Attack.clear(getDistance(me, getUnit(1, name)) + 5, 0xF, name); - } - } - } - if (name === "Travincal") { - let monsterList = []; - let monster = Game.getMonster(); - if (monster) { - do { - if ([345, 346, 347].includes(monster.classid) && monster.attackable) { - monsterList.push(copyUnit(monster)); - } - } while (monster.getNext()); - } - Attack.clearList(monsterList); - } - if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); - if (me.classid === 4 && Config.FindItem) this.pop(); - if (name === "Andariel") delay(2000); - Pickit.pickItems(); - return true; - }; - - this.runBoss = function (name, wp, exit, preset) { - console.log(name); - if (me.area !== wp && exit.indexOf(me.area) === -1) { - let wpunit = getUnit(2, "waypoint"); - if (!wpunit) Town.goToTown(); - Pather.useWaypoint(wp, true); - Precast.doPrecast(true); - } - if (!this.bossSpecial(name, 1)) return false; - if (exit.indexOf(me.area) > 0 && me.area !== exit[exit.length - 1]) { - let i = []; - for (let j = exit.indexOf(me.area); j < exit.length; j++) i.push(exit[j]); - exit = i; - } - if (exit[0] !== 0 && me.area !== exit[exit.length - 1]) Pather.moveToExit(exit, true); - if (!this.bossSpecial(name, 2)) return false; - if (preset && getPresetUnit(me.area, 1, preset)) Pather.moveToPreset(me.area, 1, preset); - if (this.bossSpecial(name, 3)) this.killBoss(name); - if (!this.bossSpecial(name, 4)) return false; - return true; - }; - - this.bossSpecial = function (name, t) { - if (t === 1) { - if (name === "The Cow King") { - Town.move("stash"); - for (let i = 0; i < 30; i++) { - if (Pather.usePortal(39)) break; - delay(100); - } - if (!Pather.usePortal(39)) return false; - else return true; - } - if (chests && ["Stormtree", "Battlemaid Sarina", "Icehawk Riftwing"].indexOf(name) > -1) { - this.chests(); - Pickit.pickItems(); - return true; - } - return true; - } - if (t === 2) { - if (chests && name === "Icehawk Riftwing") { - this.chests(); - } - if (name === "Griswold") { - Pather.moveToPreset(me.area, 1, 737); - while (!getUnit(2, 60)) delay(10); - Pather.usePortal(38); - return true; - } - if (name === "Fire Eye") { - Pather.usePortal(null); - return true; - } - if (name === "Ancient Kaa the Soulless") { - let j, k, pre, kaa = false; - for (j = 66; kaa === false; j++) { - if (me.area !== 46) Pather.moveToExit(46, true); - Pather.moveToExit(j, true); - pre = getPresetUnits(me.area, 1); - for (k = 0; k < pre.length; k++) { - if (pre[k].id === 753) kaa = true; - } - } - return true; - } - if (name === "Duriel") { - Pather.moveToExit(getRoom().correcttomb, true); - Pather.moveToPreset(me.area, 2, 152, -3, -3); - while (!getUnit(2, 100)) delay(10); - if (me.classid === 1 && me.getSkill(43, 1)) { - for (let i = 0; i < 10; i += 1) { - if (me.area === 73) { - break; - } - Skill.cast(43, 0, getUnit(2, 100)); - } - } else Pather.useUnit(2, 100, 73); - return true; - } - if (name === "Travincal") { - Pather.moveTo(me.x + 101, me.y - 71); - return true; - } - if (name === "Grand Vizier of Chaos") { - if (!vizx) this.initLayout(); - if (me.name === taxiChar) { - Pather.moveTo(vizx, vizy, 3); - Pather.makePortal(); - say(name); - } - this.openSeal(396); - Pather.moveTo(vizx, vizy, 3); - return true; - } - if (name === "Lord de Seis") { - if (!seisx) this.initLayout(); - if (me.name === taxiChar) { - Pather.moveTo(seisx, seisy, 3); - Pather.makePortal(); - say(name); - } - this.openSeal(394); - Pather.moveTo(seisx, seisy, 3); - return true; - } - if (name === "Infector of Souls") { - if (!infx) this.initLayout(); - if (me.name === taxiChar) { - Pather.moveTo(infx, infy, 3); - Pather.makePortal(); - say(name); - } - this.openSeal(392); - Pather.moveTo(infx, infy, 3); - return true; - } - if (name === "Diablo") { - if (!diax) this.initLayout(); - Pather.moveTo(diax, diay, 3); - return true; - } - return true; - } - if (t === 3) { - if (name === "Diablo") { - if (me.name === taxiChar) { - //cmd = "Diablo"; - say("Diablo"); - this.diablo(); - return false; - } else return true; - } - return true; - } - if (t === 4) { - if (name === "Fire Eye") { - Pather.moveTo(10073, 8668); - Pather.usePortal(null); - return true; - } - if (name === "The Summoner") { - let journal = getUnit(2, 357); - if (journal) { - Pather.moveTo(journal.x, journal.y); - for (let i = 0; i < 5; i += 1) { - journal.interact(); - delay(me.ping + 500); - me.cancel(); - delay(me.ping + 500); - if (Pather.getPortal(46)) break; - } - Pather.usePortal(46); - } - return true; - } - if (chests && ["Bonebreaker", "Coldcrow", "Countess", "Creeping Feature", "Coldworm the Burrower", "Dark Elder", "Icehawk Riftwing", "Mephisto"].indexOf(name) > -1) { - if (name === "Coldcrow") Pather.moveToExit(13, true); - if (name === "Dark Elder") Pather.moveToExit(65, true); - if (name === "Icehawk Riftwing") Pather.moveToExit(93, true); - this.chests(); - Pickit.pickItems(); - return true; - } - if (name === "Grand Vizier of Chaos") { - this.openSeal(395); - return true; - } - if (name === "Infector of Souls") { - this.openSeal(393); - return true; - } - return true; - } - return false; - }; - - this.runList = function (list) { - runString = []; - for (let i = 0; i < list.length; i++) { - if (Bosses.indexOf(list[i]) > -1) { - if (runString.indexOf(list[i]) === -1) runString.push(list[i]); - else D2Bot.printToConsole("boss '" + list[i] + "' listed more than once", 9); - } else { - for (let j = 0; j < Tasks.length; j++) { - if (list[i].indexOf(Tasks[j]) === 0) runString.push(list[i]); - } - if (runString.indexOf(list[i]) === -1) D2Bot.printToConsole("boss/task string '" + list[i] + "' not recognized and ignored", 9); - } - } - runDone = []; - for (let i = 0; runDone.length < runString.length; i++) { - if (Bosses.indexOf(runString[i]) > -1) { - let loc = Bosses.indexOf(runString[i]); - if (runDone.indexOf(runString[i]) === -1) { - this.runBoss(Bosses[loc], wps[loc], exits[loc], presets[loc]); - runDone.push(runString[i]); - } - } else { - for (let j = 0; j < Tasks.length; j++) { - if (runString[i].indexOf(Tasks[j]) === 0) { - this.runTask(runString[i]); - runDone.push(runString[i]); - } - } - } - } - return true; - }; - - this.runTask = function (task) { - console.log(task); - let wp; - if (task.indexOf("givebo") === 0) { - let boChars = []; - if (task === "givebo") { - wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf(" ") === -1) { - wp = Number(task); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getbo") > -1) boChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - wp = Number(task.slice(0, task.indexOf(" "))); - for (let i = 0; i < chars.length; i += 2) { - if (task.indexOf(chars[i]) > -1) boChars.push(chars[i]); - } - } - if (isNaN(wp)) wp = boWP; - } - if (!wp || boChars.length === 0) { - D2Bot.printToConsole("givebo string error", 9); - return false; - } - Town.goToTown(); - Pather.useWaypoint(wp, true); - this.delays("givebo", boDelay, boChars); - } else if (task.indexOf("getbo") === 0) { - let boBarb; - if (task === "getbo") { - wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; - } - } - } else if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf(" ") === -1) { - wp = Number(task); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("givebo") > -1) boBarb = chars[i - 1]; - } - } - } - if (task.indexOf(" ") > -1) { - wp = Number(task.slice(0, task.indexOf(" "))); - boBarb = task.slice(task.indexOf(" ") + 1); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - if (task.indexOf(chars[i]) > -1) boBarb = chars[i - 1]; - } - } - } - if (!wp || !boBarb) { - D2Bot.printToConsole("getbo string error", 9); - return false; - } - Town.goToTown(); - Pather.useWaypoint(wp, true); - Pather.moveTo(me.x - 5, me.y - 5, 3); - this.delays("getbo", boDelay, boBarb); - } else if (task.indexOf("chant") === 0 || task.indexOf("givechant") === 0) { - let chantChars = []; - if (task === "chant" || task === "givechant") { - wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf(" ") === -1) { - wp = Number(task); - if (isNaN(wp)) wp = boWP; - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("getchant") > -1) chantChars.push(chars[i - 1]); - } - } - } else if (task.indexOf(" ") > -1) { - wp = Number(task.slice(0, task.indexOf(" "))); - for (let i = 0; i < chars.length; i += 2) { - if (task.indexOf(chars[i]) > -1) chantChars.push(chars[i]); - } - } - if (isNaN(wp)) wp = boWP; - } - if (!wp || chantChars.length === 0) { - D2Bot.printToConsole("givechant string error", 9); - return false; - } - if (me.area !== wp) Pather.useWaypoint(wp, true); - this.delays("givechant", chantDelay, chantChars); - } else if (task.indexOf("taxi") === 0) { - if (task === "taxi") taxiChar = me.name; - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task === "on") { - taxiChar = me.name; - say("taxi"); - } - if (task === "off") { - taxiChar = false; - say("ixat"); - } - } - } else if (task.indexOf("ride") === 0) { - if (task === "ride") { - for (let i = 1; i <= chars.length; i += 2) { - for (let j = 0; j < chars[i].length; j++) { - if (chars[i][j].indexOf("taxi") > -1) taxiChar = chars[i - 1]; - } - } - } else if (task.indexOf(" ") > -1) taxiChar = task.slice(task.indexOf(" ") + 1); - if (!taxiChar) { - D2Bot.printToConsole("ride string error", 9); - return false; - } - this.delays("ride", taxiDelay, taxiChar); - this.rideTaxi(); - } else if (task.indexOf("chests") === 0) { - if (task === "chests") { - chests = true; - } - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task === "on") chests = true; - if (task === "off") chests = false; - } - } else if (task === "chaos") { - if (me.name === taxiChar || (!taxiChar && me.classid === 1 && me.area !== 108)) { - if (me.area !== 107 && me.area !== 108) { - Town.goToTown(); - Pather.useWaypoint(107, true); - } - let teleTo; - if (tele === "gate") { - teleTo = [7795, 5555]; - } else if (tele === "star") { - teleTo = [7792, 5291]; - } - Pather.moveTo(teleTo[0], teleTo[1], 3); - Pather.makePortal(); - if (teleMsg.length > 0) say(teleMsg[rand(0, teleMsg.length - 1)]); - if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); - if (openSeals.length > 0) { - for (let k = 0; k < openSeals.length; k++) { - this.openSeal(391 + openSeals[k]); - } - Pather.moveTo(teleTo[0], teleTo[1], 3); - if (sealMsg.length > 0) say(sealMsg[rand(0, sealMsg.length - 1)]); - if (me.classid === 1 && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) this.delays("givechant", 5); - } - if (clearChars.indexOf(me.name) > -1) { - Pather.makePortal(); - this.clearChaos(); - } else { - Town.goToTown(); - //Town.doChores(); - //Town.move("portalspot"); - } - } else { - this.delays("chaos", chaosDelay, null); - if (me.area === 108 && clearChars.indexOf(me.name) > -1) { - this.clearChaos(); - } - } - } else if (task === "getleg") { - this.getLeg(); - } else if (task === "makecows") { - this.openPortal(); - } else if (task === "Shopbot") { - this.Shopbot(); - } else if (task === "Tunnels") { - Pather.journeyTo(65); - Attack.clearLevel(Config.ClearType); - } else if (task === "Urdars") { - Town.goToTown(); - Pather.useWaypoint(106); - Precast.doPrecast(true); - Pather.journeyTo(107); - this.Urdars(); - } else if (task === "findshrine") { - let shrineAreas = [2, 3, 4, 10, 5, 6, 7/*,26,27,28,29,30,31,32,33,34,35,41,42,43,44,76,77,78,79,80,81,82,104,105,106,107,108*/]; - Town.goToTown(); - Pather.useWaypoint(3, true); - for (let i = 0; i < shrineAreas.length; i++) { - Pather.journeyTo(shrineAreas[i]); - if (Misc.getShrinesInArea(shrineAreas[i], 15, false)) { - if (chars[chars.indexOf(me.name) + 1].indexOf("useshrine") === chars[chars.indexOf(me.name) + 1].indexOf("findshrine") + 1) { - let shrine = getUnit(2, "shrine"); - if (shrine) { - do { - if (shrine.objtype === 15 && shrine.mode === 0) { - Pather.moveTo(shrine.x - 3, shrine.y - 3); - Misc.getShrine(shrine); - if (me.getState(137)) break; - } - } while (shrine.getNext()); - } - } - break; - } - } - Town.goToTown(); - if (finderDelay && finderDelay > 0) this.delays("finder", finderDelay); - } else if (task === "useshrine") { - if (me.getState(137)) return true; - let finder; - for (let i = 1; i <= chars.length; i += 2) { - if (chars[i].indexOf("findshrine") > -1) finder = chars[i - 1]; - } - if (!finder) finder = null; - if (finder === me.name || (finder == null && me.classid === 1)) this.runTask("findshrine"); - this.delays("shrine", shrineDelay, finder); - } else if (task === "clearcows") { - if (me.area !== 39) { - Town.goToTown(1); - Town.move("stash"); - for (let i = 0; i < 30; i++) { - if (Pather.usePortal(39)) break; - delay(100); - } - } - if (me.area === 39) Attack.clearLevel(Config.ClearType); - } else if (task === "chores") { - Town.goToTown(); - Town.doChores(); - } else if (task.indexOf("areadelay") === 0) { - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - if (task.indexOf("in") === 0) { - if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "in", task); - else if (task.indexOf(" ") > -1) { - let aid = Number(task.slice(0, task.indexOf(" "))); - task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(aid)) this.delays("area", areaDelay, task, "in", aid); - else D2Bot.printToConsole("areadelay string error1", 9); - } else D2Bot.printToConsole("areadelay string error2", 9); - } else if (task.indexOf("out") === 0) { - if (task.indexOf(" ") > -1) task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(Number(task))) this.delays("area", areaDelay, false, "out", task); - else if (task.indexOf(" ") > -1) { - let aid = Number(task.slice(0, task.indexOf(" "))); - task = task.slice(task.indexOf(" ") + 1); - if (!isNaN(aid)) this.delays("area", areaDelay, task, "out", aid); - else D2Bot.printToConsole("areadelay string error3", 9); - } else D2Bot.printToConsole("areadelay string error4", 9); - } else D2Bot.printToConsole("areadelay string error5", 9); - } else D2Bot.printToConsole("areadelay string error6", 9); - } else if (task.indexOf("goto") === 0) { - if (task.indexOf(" ") > -1) { - task = task.slice(task.indexOf(" ") + 1); - let aid = Number(task); - if (!isNaN(aid)) Pather.journeyTo(aid); - else D2Bot.printToConsole("goto string value error", 9); - } - return true; - } - return false; - }; - - this.clearChaos = function() { - this.clearAct = function(act, x, y) { - if (act === "move") { - let xPath = (clearMode === 0) ? true : false; - Pather.moveTo(x, y, 3, xPath); - if (tele === "gate" && x === 7794 && y === 5315) Pather.makePortal(); - if (tele === "star" && clearDirection === 0 && x === 7765 && y === 5294) Pather.makePortal(); - if (tele === "star" && clearDirection === 1 && x === 7825 && y === 5294) Pather.makePortal(); - Attack.clear(25, clearMode); - } else if (act === "seal") { - if (openSeal(x) === true) { - if (x === 392 || x === 396) { - for (let i = 0; i < 30; i++) { - let boss = (x === 392) ? getUnit(1, "Infector of Souls") : getUnit(1, "Grand Vizier of Chaos"); - if (boss) break; - delay(100); - } - } - } - if (((x === 393 && clearDirection === 0) || (x === 395 && clearDirection === 1)) && cmd !== "Diablo") { - cmd = "Diablo"; - say("Diablo"); - } - } else if (act === "Infector of Souls" || act === "Lord de Seis" || act === "Grand Vizier of Chaos") { - let boss = getUnit(1, act); - if (!boss) { - Pather.moveTo(x, y, 3, xPath); - Attack.clear(25, clearMode); - } - boss = getUnit(1, act); - if (boss) { - if (boss.hp <= 0 || boss.mode === 0 || boss.mode === 12) { - // - } else { - Attack.clear(25, clearMode, act); - } - } - } else if (act === "Diablo") { - cmd = "Diablo"; - } else { - throw new Error("Invalid clearAct"); - } - if (chars[chars.indexOf(me.name) + 1].indexOf("givebo") > -1) this.bo(); - Precast.doPrecast(false); - }; - Precast.doPrecast(false); - Attack.clear(10); - initLayout(); - let Gate = ["move", 7794, 5545, "move", 7794, 5525, "move", 7794, 5505, "move", 7794, 5480, "move", 7794, 5440, "move", 7794, 5405, "move", 7794, 5365, "move", 7794, 5335, "move", 7794, 5315]; - let preFect = ["move", 7825, 5294, "move", 7843, 5294, "move", 7861, 5294]; - let Fect = (infLayout === 1) - ? ["move", 7890, 5295, "move", 7915, 5290, "seal", 392, 1, "Infector of Souls", 7890, 5295, "Infector of Souls", 7915, 5290, "seal", 393, 0] - : ["move", 7900, 5275, "move", 7930, 5280, "move", 7930, 5310, "seal", 392, 1, "move", 7930, 5310, "Infector of Souls", 7930, 5280, "Infector of Souls", 7900, 5275, "seal", 393, 0]; - let preSeis = ["move", 7794, 5265, "move", 7794, 5245, "move", 7794, 5225]; - let Seis2 = (openSeals.indexOf(3) > -1) - ? ["move", 7775, 5210, "move", 7775, 5195, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210] - : ["move", 7775, 5210, "move", 7775, 5195, "move", 7810, 5190, "move", 7810, 5155, "move", 7785, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5195, "Lord de Seis", 7775, 5210]; - let Seis5 = (openSeals.indexOf(3) > -1) ? ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195] : ["move", 7810, 5195, "move", 7780, 5190, "move", 7775, 5155, "move", 7805, 5155, "seal", 394, 1, "Lord de Seis", 7775, 5155, "Lord de Seis", 7780, 5190, "move", 7780, 5190, "move", 7810, 5195]; - let Seis = (seisLayout === 1) ? Seis2 : Seis5; - let preVizi = ["move", 7765, 5294, "move", 7745, 5294, "move", 7715, 5294]; - let Vizi = (vizLayout === 1) - ? ["move", 7680, 5290, "move", 7665, 5275, "seal", 396, 2, "Grand Vizier of Chaos", 7665, 5275, "Grand Vizier of Chaos", 7680, 5290, "seal", 395, 0] - : ["move", 7700, 5315, "move", 7670, 5315, "seal", 396, 2, "Grand Vizier of Chaos", 7670, 5315, "Grand Vizier of Chaos", 7700, 5315, "seal", 395, 0]; - let Diablo = (clearDirection === 1) - ? ["move", 7765, 5295, "move", 7796, 5296, "Diablo", 666, 666] - : ["move", 7825, 5294, "move", 7795, 5295, "Diablo", 666, 666]; - let clearActQueue = []; - if (tele === "gate" && me.x >= 5520) clearActQueue = clearActQueue.concat(Gate); - if (clearDirection === 0) clearActQueue = clearActQueue.concat(preVizi, Vizi, preSeis, Seis, preFect, Fect, Diablo); - if (clearDirection === 1) clearActQueue = clearActQueue.concat(preFect, Fect, preSeis, Seis, preVizi, Vizi, Diablo); - for (let j = 1; clearActQueue.length > j; j += 3) { - if (cmd === "Diablo") { - if (last !== "Diablo") this.diablo(); - break; - } - this.clearAct(clearActQueue[j - 1], clearActQueue[j], clearActQueue[j + 1]); - if (cmd === "Diablo") { - if (last !== "Diablo") this.diablo(); - break; - } - delay(100); - } - return true; - }; - - this.diablo = function () { - cmd !== "Diablo" && (cmd = "Diablo"); - if (last === "Diablo") return true; - - if (taxiChar === me.name) { - if (me.area === 108) { - Pather.moveTo(diax, diay, 3); - if ((killDiablo.includes(me.name) - || leechDiablo.includes(me.name) - || weakenDiablo.includes(me.name)) - && me.name !== shrineDiablo) { - Pather.makePortal(); - } else { - Pather.makePortal(true); - } - } - if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { - this.delays("area", 30, shrineDiablo, "in", 108); - } else if (killDiablo.length > 0) { - this.delays("area", 30, killDiablo, "in", 108); - } - } - - if (shrineDiablo === me.name) { - let finder; - for (let i = 1; i <= chars.length; i += 2) { - if (chars[i].includes("findshrine")) { - finder = chars[i - 1]; - } - } - - !finder && (finder = null); - if (finder === me.name || (finder === null && me.sorceress)) { - this.runTask("findshrine"); - } - console.log(finder); - this.delays("shrine", shrineDelay, finder); - } - if (killDiablo.indexOf(me.name) > -1) { - if (me.area !== 108) { - Town.goToTown(4); - Town.move("portalspot"); - if (!taxiChar || !Pather.usePortal(108, taxiChar)) { - let portal = getUnit(2, "portal"); - if (portal) { - do { - if (portal.objtype === 108 - && portal.getParent() !== me.name - && Misc.inMyParty(portal.getParent()) - && chars.indexOf(portal.getParent()) > -1) { - if (Pather.usePortal(null, null, portal)) break; - } - } while (portal.getNext()); - } - } - } - if (me.area !== 108) Pather.usePortal(108, null); - if (me.area === 108) { - Pather.moveTo(diax, diay, 3); - Pather.makePortal(); - if (Misc.inMyParty(shrineDiablo) && me.name !== shrineDiablo) { - this.delays("area", 60, shrineDiablo, "in", 108); //60 was 30 - } - this.killBoss("Diablo"); - } - } else if (leechDiablo.indexOf(me.name) > -1) { - if (me.area !== 108) { - Town.goToTown(4); - Town.move("portalspot"); - if (taxiChar) { - if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); - } else Pather.usePortal(108, null); - } - if (me.area === 108) { - (leechDiablo.indexOf(me.name) % 2 === 0) - ? Pather.moveTo(7792, 5291, 3) - : Pather.moveTo(7792, 5291, 3); - for (let i = 0; i < 200; i++) { - if (!getUnit(1, 243)) { - delay(100); - } - } - while (getUnit(1, "Diablo") && getUnit(1, "Diablo").hp > 0) delay(100); - Pickit.pickItems(); - } - } else if (weakenDiablo.indexOf(me.name) > -1) { - if (me.area !== 108) { - Town.goToTown(4); - Town.move("portalspot"); - if (taxiChar) { - if (!Pather.usePortal(108, taxiChar)) Pather.usePortal(108, null); - Pather.makePortal(); - } else Pather.usePortal(108, null); - } - if (me.area === 108) { - if (!diax) this.initLayout(); - Pather.moveTo(diax, diay, 3); - let waitfor = killDiablo.concat(leechDiablo); - console.log(waitfor); - loop: - for (let i = 0; i < 300; i++) { - for (let j = 0; j < waitfor.length; j++) { - delay(100); - if (Misc.inMyParty(waitfor[j]) && !getUnit(0, waitfor[j])) break; - else if ((!Misc.inMyParty(waitfor[j]) || getUnit(0, waitfor[j])) && j === waitfor.length - 1) break loop; - } - } - Config.PublicMode = 0; - delay(2 * me.ping + 100); - let player = getParty(); - clickParty(player, 3); - for (let i = 0; i < 200; i++) { - if (!getUnit(1, 243)) { - delay(100); - } - } - while (getUnit(1, 243) && getUnit(1, 243).hp > 0) { - switch (me.classid) { - case 0: //Amazon - break; - case 1: //Sorceress - if (Skill.cast(42, 0)) Skill.cast(42, 0); //static - break; - case 2: //Necromancer - if (Skill.cast(Config.Curse[0], 0)) Skill.cast(Config.Curse[0], 0); //spam boss curse - //else if other curses - break; - case 3: //Paladin - if (Skill.setSkill(123, 0)) Skill.setSkill(123, 0); //conviction aura - break; - case 4: //Barbarian - break; - } - delay(100); - } - Pickit.pickItems(); - } - } else { - if (me.area === 108) { - if (clearDirection === 0) Pather.moveTo(7825, 5294, 3); - if (clearDirection === 1) Pather.moveTo(7765, 5294, 3); - } - Town.goToTown(); - } - taxiChar = false; - last = "Diablo"; - return true; - }; - - this.getLeg = function () { - if (Pather.getPortal(sdk.areas.MooMooFarm)) return false; - /** @type {ItemUnit} */ - let leg; - let gid; - let wrongLeg; // this isn't doing anything - - while (!leg) { - if (me.getItem(sdk.quest.item.WirtsLeg)) { - leg = me.getItem(sdk.quest.item.WirtsLeg); - return me.getItem(-1, -1, leg.gid); - } - - leg = Game.getItem(sdk.quest.item.WirtsLeg); - if (leg) { - do { - if (leg.name.indexOf("ÿc1") > -1) { - wrongLeg = true; - - return false; - } else { - gid = leg.gid; - Pickit.pickItem(leg); - - return me.getItem(-1, -1, gid); - } - } while (leg.getNext()); - } - - if (chars[chars.indexOf(me.name) + 1].indexOf("getleg") > -1) { - Pather.useWaypoint(sdk.areas.StonyField); - Precast.doPrecast(true); - Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 8, 8); - - /** @type {ObjectUnit} */ - let portal; - for (let i = 0; i < 6; i += 1) { - portal = Pather.getPortal(sdk.areas.Tristram); - if (portal) { - Pather.usePortal(null, null, portal); - break; - } - delay(500); - } - - if (!portal) { - Town.goToTown(); - return false; - } - - Pather.moveTo(25048, 5177); - let wirt = Game.getObject(sdk.quest.chest.Wirt); - for (let i = 0; i < 8; i += 1) { - !!wirt && wirt.interact(); - delay(500); - leg = Game.getItem(sdk.quest.item.WirtsLeg); - - if (leg) { - gid = leg.gid; - Pickit.pickItem(leg); - Town.goToTown(1); - - if (chars[chars.indexOf(me.name) + 1].indexOf("makecows") === -1) { - Town.move("stash"); - leg.drop(); - return true; - } else { - return me.getItem(-1, -1, gid); - } - } - } - Town.goToTown(); - } else { - Town.move("stash"); - } - delay(100); - } - return false; - }; - - this.getTome = function () { - let myTome = me.findItem("tbk", 0, 3); - let tome = me.getItem("tbk"); - if (tome) { - do { - if (!myTome || tome.gid !== myTome.gid) { - return copyUnit(tome); - } - } while (tome.getNext()); - } - - Town.move(NPC.Akara); - let akara = Town.initNPC("Shop"); - if (!akara) return false; - - tome = akara.getItem("tbk"); - if (tome.buy()) { - tome = me.getItem("tbk"); - if (tome) { - do { - if (!myTome || tome.gid !== myTome.gid) { - return copyUnit(tome); - } - } while (tome.getNext()); - } - } - return false; - }; - - this.openPortal = function () { - Town.goToTown(1); - let leg = this.getLeg(); - if (!leg) return false; - - let tome = this.getTome(); - if (!tome) return false; - - if (!Town.openStash() - || !Cubing.emptyCube() - || !Storage.Cube.MoveTo(leg) - || !Storage.Cube.MoveTo(tome) - || !Cubing.openCube()) { - return false; - } - transmute(); - delay(500); - for (let i = 0; i < 10; i += 1) { - if (Pather.getPortal(39)) { - return true; - } - delay(200); - } - me.cancel(); - return false; - }; - - this.rideTaxi = function() { - let tTimer = getTickCount(); - while (true) { - if (!taxiChar || !Misc.inMyParty(taxiChar)) { - break; - } - - if (this.getAct(taxiChar) > 0) { - Town.goToTown(this.getAct(taxiChar)); - Town.move("portalspot"); - } - - if (cmd && cmd !== last) { - let currCmd = cmd; - if (currCmd === "Diablo") { - this.diablo(); - } else if (teleMsg.includes(currCmd)) { - if (clearChars.includes(me.name)) { - this.delays("chaos", chaosDelay, taxiChar); - this.clearChaos(); - } - taxiChar = false; - - break; - } else if (Pather.getPortal(null, taxiChar)) { - if (Pather.usePortal(null, taxiChar)) { - if (currCmd === cmd) { - this.killBoss(currCmd); - } - Town.goToTown(); - } - } - last = currCmd; - tTimer = getTickCount(); - } - - if (getTickCount > tTimer + taxiDelay * 1e3) { - taxiChar = false; - break; - } - Precast.doPrecast(false); - delay(100); - } - }; - - this.Shopbot = function () { - Loader.runScript("ShopBot"); - }; - - /** - * @param {string | number} name - * @returns {boolean} - */ - this.Urdars = function (name) { - let room = getRoom(); - if (!room) return false; - - const urdars = [189, 309, 361, 311, 675, 300, 692]; - const rooms = []; - /** @param {Monster} monster */ - const check = function (monster) { - return ( - urdars.includes(monster.classid) - && monster.attackable - && monster.spectype !== sdk.monsters.spectype.All - && monster.spectype !== sdk.monsters.spectype.Minion - && 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 = []; - let monster = Game.getMonster(name); - - if (monster) { - do { - if (check(monster)) { - monList.push(copyUnit(monster)); - - if (me.name === taxiChar) { - Pather.moveTo(monster.x + rand(-5, 5), monster.y + rand(-5, 5), 0); - Pather.makePortal(); - say(monster.name); - } - - if (monster && monster.hp > 0) { - if (me.name === taxiChar) { - for (let i = 0; i < 30; i++) { - if (monster) Attack.clear(getDistance(me, monster) + 5, 0xF, monster); - if (!monster || monster.hp === 0) break; - delay(100); - } - } else if (monster) { - if (monster.spectype === sdk.monsters.spectype.All) { - Attack.clear(getDistance(me, monster) + 5, 0, monster); - } else { - Attack.clear(getDistance(me, monster) + 5, 0xF, monster); - } - } - } - } - } while (monster.getNext()); - } - - if (!Attack.clearList(monList)) { - return false; - } - } - } - - return true; - }; - - this.precast = function () { - switch (me.classid) { - case sdk.player.class.Amazon: - case sdk.player.class.Barbarian: - return false; - case sdk.player.class.Sorceress: //Sorceress - return ( - Skill.cast(sdk.skills.Blizzard) - || Skill.cast(sdk.skills.Meteor) - || Skill.cast(sdk.skills.StaticField) - || Skill.cast(sdk.skills.FrostNova) - || Skill.cast(sdk.skills.Hydra) - || Skill.cast(sdk.skills.Nova) - ); - case sdk.player.class.Necromancer: - return Skill.cast(Config.Curse[1], 0); - case sdk.player.class.Paladin: - return ( - Skill.setSkill(sdk.skills.Concentration, 0) - && Skill.cast(sdk.skills.BlessedHammer) - ); - } - return false; - }; - - /** - * @param {string} nick - * @param {string} msg - */ - function ChatEvent(nick, msg) { - if (!taxiChar && msg === "taxi" && chars.includes(nick)) { - taxiChar = nick; - } else if (nick === taxiChar && msg === "ixat" && cmd !== "Diablo") { - taxiChar = false; - } else if (nick === taxiChar && nick !== me.name) { - cmd = msg; - } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick) && clearChars.indexOf(me.name)) { - cmd = "Diablo"; - } else if (msg === "Diablo" && cmd !== msg && chars.includes(nick)) { - if (leechDiablo.includes(me.name)) { - taxiChar = nick; - } - this.diablo(); - } - } - - // START - let listen = false; - for (let i = 0; i < chars[chars.indexOf(me.name) + 1].length; i++) { - if (chars[chars.indexOf(me.name) + 1][i].indexOf("ride") > -1) { - listen = true; - } - } - if (!listen && ( - killDiablo.includes(me.name) - || leechDiablo.includes(me.name) - || weakenDiablo.includes(me.name) - || clearChars.includes(me.name) - )) { - listen = true; - } - if (listen) { - addEventListener("chatmsg", ChatEvent); - } - Pickit.pickItems(); - Town.heal(); - this.runList(chars[chars.indexOf(me.name) + 1]); - if (killDiablo.indexOf(me.name) === -1) { - for (let i = 0; i < killDiablo.length; i++) { - while (Misc.inMyParty(killDiablo[i])) { - if (me.sorceress && Skill.setSkill(52, 0) && me.getSkill(52, 0) >= 10) { - this.delays("givechant", 1); - } else { - delay(1000); - } - } - } - } - - return true; -} From b77e06209c53859eae9994563b76b1e6193cbba0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 19 Jun 2024 19:40:29 -0400 Subject: [PATCH 428/758] Update Misc.js - comment out debug statement and check if shrine name is defined when building shrineList --- d2bs/kolbot/libs/core/Misc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 17d22fe2c..d8495309b 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -484,7 +484,7 @@ const Misc = (function () { if (startPos.distance > 5) { // rebuid chest list every 5 chests in case we've moved and add any new chests to our list let _unitList = buildChestList(Math.round(range / 2)); - console.debug("Rescanning for chests: " + _unitList.length + " chests found."); + // console.debug("Rescanning for chests: " + _unitList.length + " chests found."); unitList = unitList.concat(_unitList); } } @@ -763,6 +763,7 @@ const Misc = (function () { // Build a list of nearby shrines do { + if (!shrine.name) continue; let _name = shrine.name.toLowerCase(); if ((_name.includes("shrine") && ShrineData.has(shrine.objtype) || (_name.includes("well"))) && shrine.mode === sdk.objects.mode.Inactive From ac6dac8bfb577be981496f8023cf3b77a7192761 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:12:53 -0400 Subject: [PATCH 429/758] Add support for ChargeCasting to restrict to certain classids - Refactor charge casting section into it's own method `doChargeCast` - `Config.ChargeCast.classids` accepts number ids or string names --- d2bs/kolbot/libs/core/Attack.js | 32 ++++++++++++++++++++++ d2bs/kolbot/libs/core/Attacks/Amazon.js | 21 ++++++++------ d2bs/kolbot/libs/core/Attacks/Assassin.js | 8 +----- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 8 +----- d2bs/kolbot/libs/core/Attacks/Druid.js | 8 +----- d2bs/kolbot/libs/core/Attacks/Paladin.js | 11 +++----- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 16 +++++------ d2bs/kolbot/libs/core/Config.js | 5 +++- d2bs/kolbot/sdk/types/Attack.d.ts | 2 ++ 9 files changed, 66 insertions(+), 45 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index a90650f28..83f821771 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -2235,4 +2235,36 @@ const Attack = { } return Attack.Result.NOOP; }, + + /** + * @param {Monster} unit + * @returns {boolean} + */ + doChargeCast: function (unit) { + const { skill, spectype, classids } = Config.ChargeCast; + const cRange = Skill.getRange(skill); + const cState = Skill.getState(skill); + + if (classids.length) { + /** + * @param {string | number} id + * @returns {boolean} + */ + const validId = function (id) { + return typeof id === "number" + ? unit.classid === id + : unit.name.toLowerCase().includes(id); + }; + if (!classids.some(validId)) { + return false; + } + } + + if ((!spectype || (unit.spectype & spectype)) + && (!cState || !unit.getState(cState)) + && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { + return Skill.castCharges(skill, unit); + } + return false; + }, }; diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index 4e8196d64..a2389b1ee 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -5,6 +5,7 @@ * */ +/** @implements {ClassAttack} */ const ClassAttack = { bowCheck: false, lightFuryTick: 0, @@ -56,6 +57,11 @@ const ClassAttack = { return skills; }, + /** + * @param {Monster | Player} unit + * @param {boolean} [preattack] + * @returns {AttackResult} + */ doAttack: function (unit, preattack) { if (!unit) return Attack.Result.SUCCESS; Config.TeleSwitch && me.switchToPrimary(); @@ -74,13 +80,7 @@ const ClassAttack = { } if (Config.ChargeCast.skill > -1) { - let cRange = Skill.getRange(Config.ChargeCast.skill); - let cState = Skill.getState(Config.ChargeCast.skill); - if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) - && (!cState || !unit.getState(cState)) - && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { - Skill.castCharges(Config.ChargeCast.skill, unit); - } + Attack.doChargeCast(unit); } if (preattack) { @@ -172,7 +172,12 @@ const ClassAttack = { this.lightFuryTick = 0; }, - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + /** + * @param {Monster | Player} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} 0 - fail, 1 - success, 2 - no valid attack skills + */ doCast: function (unit, timedSkill = -1, untimedSkill = -1) { // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index 2e396366e..e1725cfa1 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -30,13 +30,7 @@ const ClassAttack = { } if (Config.ChargeCast.skill > -1) { - let cRange = Skill.getRange(Config.ChargeCast.skill); - let cState = Skill.getState(Config.ChargeCast.skill); - if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) - && (!cState || !unit.getState(cState)) - && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { - Skill.castCharges(Config.ChargeCast.skill, unit); - } + Attack.doChargeCast(unit); } if (preattack) { diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index 0500ed587..f6af603d6 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -33,13 +33,7 @@ const ClassAttack = { } if (Config.ChargeCast.skill > -1) { - let cRange = Skill.getRange(Config.ChargeCast.skill); - let cState = Skill.getState(Config.ChargeCast.skill); - if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) - && (!cState || !unit.getState(cState)) - && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { - Skill.castCharges(Config.ChargeCast.skill, unit); - } + Attack.doChargeCast(unit); } if (preattack) { diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index 59b13658b..f735f74dd 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -31,13 +31,7 @@ const ClassAttack = { } if (Config.ChargeCast.skill > -1) { - let cRange = Skill.getRange(Config.ChargeCast.skill); - let cState = Skill.getState(Config.ChargeCast.skill); - if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) - && (!cState || !unit.getState(cState)) - && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { - Skill.castCharges(Config.ChargeCast.skill, unit); - } + Attack.doChargeCast(unit); } if (preattack) { diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index a05ceea64..457aca530 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -30,13 +30,7 @@ const ClassAttack = { } if (Config.ChargeCast.skill > -1) { - let cRange = Skill.getRange(Config.ChargeCast.skill); - let cState = Skill.getState(Config.ChargeCast.skill); - if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) - && (!cState || !unit.getState(cState)) - && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { - Skill.castCharges(Config.ChargeCast.skill, unit); - } + Attack.doChargeCast(unit); } if (preattack) { @@ -85,6 +79,9 @@ const ClassAttack = { if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5])) { attackSkill = Config.AttackSkill[5]; aura = Config.AttackSkill[6]; + } else if (Config.AttackSkill[7] > -1 && Attack.checkResist(unit, Config.AttackSkill[7])) { + attackSkill = Config.AttackSkill[7]; + aura = Config.AttackSkill[8]; } } } diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index c558c68e7..d8b07c403 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -5,6 +5,7 @@ * */ +/** @implements {ClassAttack} */ const ClassAttack = { /** @param {Monster} unit */ decideSkill: function (unit) { @@ -89,13 +90,7 @@ const ClassAttack = { } if (Config.ChargeCast.skill > -1) { - let cRange = Skill.getRange(Config.ChargeCast.skill); - let cState = Skill.getState(Config.ChargeCast.skill); - if ((!Config.ChargeCast.spectype || (unit.spectype & Config.ChargeCast.spectype)) - && (!cState || !unit.getState(cState)) - && (unit.distance < cRange || !checkCollision(me, unit, sdk.collision.LineOfSight))) { - Skill.castCharges(Config.ChargeCast.skill, unit); - } + Attack.doChargeCast(unit); } if (preattack) { @@ -201,7 +196,12 @@ const ClassAttack = { Precast.doPrecast(false); }, - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills + /** + * @param {Monster | Player} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} 0 - fail, 1 - success, 2 - no valid attack skills + */ doCast: function (unit, timedSkill = -1, untimedSkill = -1) { // No valid skills can be found if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 1dbea5ea1..1133ad24e 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -92,9 +92,10 @@ let Config = { } } + // Always set the orginal say function + global._say = global.say; 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); } @@ -367,6 +368,8 @@ let Config = { ChargeCast: { skill: -1, spectype: 0x7, + /** @type {(number|string)[]} */ + classids: [], }, // Amazon specific diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 1068c7353..9918695e1 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -72,5 +72,7 @@ declare global { function checkCorpse(unit: Monster): boolean; function checkNearCorpses(unit: Monster, range?: number): any; function whirlwind(unit: Monster | Player): boolean; + function doPreAttack(unit: Monster): AttackResult; + function doChargeCast(unit: Monster): boolean; } } From b8f926358e54897384df9867ad58a0e5789e05b8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:45:36 -0400 Subject: [PATCH 430/758] Enable use of ChargedSkills with CustomPreAttack - experimental --- d2bs/kolbot/libs/core/Attack.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 83f821771..0e820d4dd 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -2229,7 +2229,12 @@ const Attack = { } } - Skill.cast(skill, Skill.getHand(skill), unit, null, null, slot); + // Check if we need to charge cast - TODO: better check for charge vs not + if (Skill.charges.find(c => c.skill === skill)) { + Skill.castCharges(skill, unit); + } else { + Skill.cast(skill, Skill.getHand(skill), unit, null, null, slot); + } return Attack.Result.SUCCESS; } From cd27f2512dd2bc0fef392b19be181d7ee12232c2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:12:19 -0400 Subject: [PATCH 431/758] Update `Time.format` method - Fixes being restricted to 24hours --- d2bs/kolbot/libs/Polyfill.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index f57fad670..f25c4e3f7 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -1358,7 +1358,17 @@ const Time = { return (minutes * 60000); }, format: function (ms = 0) { - return (new Date(ms).toISOString().slice(11, -5)); + 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 = function (num) { + return (num < 10 ? "0" + num : num); + }; + + return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); + // return (new Date(ms).toISOString().slice(11, -5)); }, toSeconds: function (ms = 0) { return (ms / 1000); From 4f86647ae2c1d892cc307a6aa1c37195506c0053 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 4 Jul 2024 01:22:16 -0400 Subject: [PATCH 432/758] Fix `ControlAction.getCharacters` - Was returning false instead of breaking the loop and returning the list when we detected no more characters --- d2bs/kolbot/libs/OOG.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index acc4a9d9a..aee5e7b6c 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -340,7 +340,7 @@ includeIfNotIncluded("core/Me.js"); let nameCheck = check.getText(); if (String.isEqual(firstCheck[1], nameCheck[1])) { - return false; + break; } } } From 0d60f6a7e39ae12611c20fd597479ff378e4475e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 7 Jul 2024 01:28:39 -0400 Subject: [PATCH 433/758] Fix typo in `Skill.castCharges`. Fix error `filter is not a method` in `castChargedSkill` - Had duplicated RightArmSecondary instead of having left and right, prevented using charges from switch shield - Sometimes getStat is returning an object instead of an array, turn it into an array so it doesn't break our filter check --- d2bs/kolbot/libs/core/Prototypes.js | 15 +++++++++------ d2bs/kolbot/libs/core/Skill.js | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 139f142b0..8ce168b2d 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -1984,18 +1984,21 @@ Unit.prototype.castChargedSkill = function (...args) { }).first().unit; return chargedItem.castChargedSkill.apply(chargedItem, args); } else if (this.type === sdk.unittype.Item) { - charge = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates - - if (!charge) throw Error("No charged skill on this item"); + /** @type {Charge[]} */ + let charges = this.getStat(-2)[sdk.stats.ChargedSkill]; // WARNING. Somehow this gives duplicates + if (!charges) throw Error("No charged skill on this item"); + if (!Array.isArray(charges)) { + charges = [charges]; + } if (skillId) { // Filter out all other charged skills - charge = charge.filter(item => (skillId && item.skill === skillId) && !!item.charges); - } else if (charge.length > 1) { + charges = charges.filter(item => (skillId && item.skill === skillId) && !!item.charges); + } else if (charges.length > 1) { throw new Error("multiple charges on this item without a given skillId"); } - charge = charge.first(); + charge = charges.first(); if (charge) { // Setting skill on hand diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index dfb1bfbf7..f33cf6a25 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -698,7 +698,7 @@ if (!item) return false; if (!unit) unit = me; const weaponSwitch = me.weaponswitch; - if ([sdk.body.RightArmSecondary, sdk.body.RightArmSecondary].includes(item.bodylocation)) { + if ([sdk.body.RightArmSecondary, sdk.body.LeftArmSecondary].includes(item.bodylocation)) { me.switchWeapons(weaponSwitch ^ 1); } try { From ba2db0dc77f1e933f010ffa1eda8b4fd278dc501 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 7 Jul 2024 02:50:48 -0400 Subject: [PATCH 434/758] Add `Skill.useableOn` method to fix casting stateful spells on monsters that aren't affected - Some spells won't work on certain monsters, i.e. Slow Missiles won't work on prime evils. For now just doing this check with charge casting --- d2bs/kolbot/libs/core/Skill.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index f33cf6a25..17029a7f4 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -459,6 +459,35 @@ return true; }, + /** + * Check whether this skills is even usable on the target + * @param {number} skillId + * @param {Monster} unit + */ + usableOn: function (skillId, unit) { + if (!unit || !unit.type) return false; + + switch (skillId) { + case sdk.skills.SlowMissiles: + return !unit.isPrimeEvil; + case sdk.skills.Confuse: + case sdk.skills.Attract: + return unit.scareable; + case sdk.skills.DimVision: + if (unit.isSpecial) return false; + if ([ + sdk.monsters.OblivionKnight1, + sdk.monsters.OblivionKnight2, + sdk.monsters.OblivionKnight3 + ].includes(unit.classid)) { + return false; + } + return true; + default: + return true; + } + }, + // Put a skill on desired slot setSkill: function (skillId, hand, item) { const checkHand = (hand === sdk.skills.hand.Right @@ -684,6 +713,10 @@ */ castCharges: function (skillId, unit) { if (!Skill.charges.length) return false; + // TODO: better validity check - for now preventing spamming slow missiles on bosses where it does't work + if (unit && unit.hasOwnProperty("classid") && !Skill.usableOn(skillId, unit)) { + return false; + } const charge = Skill.charges .filter(function (c) { return c.skill === skillId && c.charges > 0; From 56a8759e0abd7c6c6945d6ca940a7f0924c5df06 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 7 Jul 2024 02:55:51 -0400 Subject: [PATCH 435/758] Update `Attack.clear` for charge usage - Iterate our monster list to find anyone we might need to cast on --- d2bs/kolbot/libs/core/Attack.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 0e820d4dd..94fe87652 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -824,7 +824,27 @@ const Attack = { } // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + // custom handling here, we want to find a valid monster to use our skill on + // if we wait until they are the current target, it may too late to be useful + if (Config.ChargeCast.skill > -1 + && Config.ChargeCast.spectype + && !(target.spectype & Config.ChargeCast.spectype)) { + let cRange = Skill.getRange(Config.ChargeCast.skill); + let cState = Skill.getState(Config.ChargeCast.skill); + let chargeTarget = monsterList.find(function (mon) { + return ( + (mon.spectype & Config.ChargeCast.spectype) + && (mon.distance <= cRange) + && (!cState || !mon.getState(cState)) + && !checkCollision(me, mon, sdk.collision.LineOfSight) + ); + }); + if (chargeTarget && chargeTarget.gid !== target.gid) { + Attack.doChargeCast(chargeTarget); + } + } + + const result = ClassAttack.doAttack(target, attackCount % 15 === 0); if (result) { retry = 0; From 1f50af0e2d08c363e3db4337dbb7c5c561d65ea2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 11 Aug 2024 01:14:09 -0400 Subject: [PATCH 436/758] [hotfix[ Update shriner to handle objects with no name prop - Fixes `shrine.name is undefined` #416 --- d2bs/kolbot/libs/core/Misc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index d8495309b..c53d74dea 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -666,6 +666,7 @@ const Misc = (function () { if (shrine) { do { + if (!shrine.name) continue; let _name = shrine.name.toLowerCase(); if ((_name.includes("shrine") && ShrineData.has(shrine.objtype) || (_name.includes("well"))) && ShrineData.has(shrine.objtype) From 65e5fcc6e3074586c170a0be3c1d8b1f6a68373d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:10:04 -0400 Subject: [PATCH 437/758] Update Loader.js - Add cleanup method for Runnable class --- d2bs/kolbot/libs/core/Loader.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 6520b4edf..6cc3299c7 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -11,6 +11,7 @@ * @typedef {Object} RunnableOptions * @property {function(): any} preAction * @property {function(): boolean} postAction + * @property {function(): any} cleanup * @property {boolean} forceTown * @property {number} bossid * @property {number} startArea @@ -33,6 +34,7 @@ function Runnable (action, options = {}) { } }; this.postAction = options.hasOwnProperty("postAction") ? options.postAction : null; + this.cleanup = options.hasOwnProperty("cleanup") ? options.cleanup : null; this.forceTown = options.hasOwnProperty("forceTown") ? options.forceTown : false; this.bossid = options.hasOwnProperty("bossid") ? options.bossid : null; } @@ -268,6 +270,12 @@ const Loader = { } finally { // Dont run for last script as that will clear everything anyway if (this.scriptIndex < this.scriptList.length) { + // run cleanup if applicable + if (Loader.currentScript instanceof Runnable) { + if (Loader.currentScript.cleanup && typeof Loader.currentScript.cleanup === "function") { + Loader.currentScript.cleanup(); + } + } // remove script function from global scope, so it can be cleared by GC delete global[script]; Loader.currentScript = null; @@ -394,6 +402,13 @@ const Loader = { delete global[script]; } + // run cleanup if applicable + if (Loader.currentScript instanceof Runnable) { + if (Loader.currentScript.cleanup && typeof Loader.currentScript.cleanup === "function") { + Loader.currentScript.cleanup(); + } + } + Loader.currentScript = null; Loader.tempList.pop(); From 0e2bc770e0472d8009abe21e23336c4c0881c21e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:11:08 -0400 Subject: [PATCH 438/758] Update ChestMania.js - Ensure OpenChests is enabled for this script, cleanup afterwards --- d2bs/kolbot/libs/scripts/ChestMania.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ChestMania.js b/d2bs/kolbot/libs/scripts/ChestMania.js index 0541dc8f2..edd362fea 100644 --- a/d2bs/kolbot/libs/scripts/ChestMania.js +++ b/d2bs/kolbot/libs/scripts/ChestMania.js @@ -9,6 +9,8 @@ const ChestMania = new Runnable( function ChestMania () { + Config.OpenChests._enabled = Config.OpenChests.Enabled; + Config.OpenChests.Enabled = true; const nextToTown = [ sdk.areas.BloodMoor, sdk.areas.RockyWaste, @@ -39,6 +41,10 @@ const ChestMania = new Runnable( return true; }, { - startArea: Object.values(Config.ChestMania).find((act) => act.length > 0)[0][0] + startArea: Object.values(Config.ChestMania).find((act) => act.length > 0)[0][0], + cleanup: function () { + Config.OpenChests.Enabled = Config.OpenChests._enabled; + delete Config.OpenChests._enabled; + } } ); From b18db4476b8136a6cf88593de48e2b7ee1e56819 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:29:58 -0400 Subject: [PATCH 439/758] Update Loader.d.ts --- d2bs/kolbot/sdk/types/Loader.d.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index fd3a9a740..bf17e124a 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -2,8 +2,13 @@ export {}; declare global { type GlobalScript = () => boolean; interface Runnable { + startArea: number | null; + forceTown: boolean; + bossid: number | null; + preAction: () => any; action: () => boolean; - startArea: number | undefined; + postAction: () => any; + cleanup: () => any; } namespace Loader { From 9544d2bb6785ca772e74c904b2d68a8e303b5dc9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:54:44 -0400 Subject: [PATCH 440/758] Update ActionHooks.js - Removed sellitem ctrl action --- .../kolbot/libs/manualplay/hooks/ActionHooks.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js index 892c4e001..8ce173d23 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js @@ -50,8 +50,19 @@ const ActionHooks = { 3: "sellItem" }, blockKeyEvent: () => [ - sdk.uiflags.Inventory, sdk.uiflags.StatsWindow, sdk.uiflags.ChatBox, sdk.uiflags.EscMenu, sdk.uiflags.Shop, sdk.uiflags.Quest, sdk.uiflags.Waypoint, - sdk.uiflags.TradePrompt, sdk.uiflags.Msgs, sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Help, sdk.uiflags.MercScreen + sdk.uiflags.Inventory, + sdk.uiflags.StatsWindow, + sdk.uiflags.ChatBox, + sdk.uiflags.EscMenu, + sdk.uiflags.Shop, + sdk.uiflags.Quest, + sdk.uiflags.Waypoint, + sdk.uiflags.TradePrompt, + sdk.uiflags.Msgs, + sdk.uiflags.Stash, + sdk.uiflags.Cube, + sdk.uiflags.Help, + sdk.uiflags.MercScreen ].some((flag) => getUIFlag(flag)), /** @@ -235,7 +246,7 @@ const ActionHooks = { break; case 3: // Shop - qolObj.action = "sellItem"; + // qolObj.action = "sellItem"; break; default: From 25fd72e403d7b3ef1a150d1fee20b697243010b9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 26 Sep 2024 00:56:34 -0400 Subject: [PATCH 441/758] Fix PickThread for manulaplay - The location of pickthread was moved awhile back and needed the path updated from `threads/pickthread` -> `libs/manualplay/threads/pickthread` - `Pickit.gidList` was changed from and array to a Set, needed to update to use size instead of length Co-Authored-By: tricky-eli <178055463+tricky-eli@users.noreply.github.com> --- d2bs/kolbot/libs/manualplay/threads/MapHelper.js | 2 +- d2bs/kolbot/libs/manualplay/threads/PickThread.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 2a8a284ac..7030fb07c 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -60,7 +60,7 @@ function main () { const togglePickThread = function () { if (!Config.ManualPlayPick) return; - const pickThread = getScript("threads/pickthread.js"); + const pickThread = getScript("libs/manualplay/threads/pickthread.js"); if (pickThread) { if (pickThread.running) { diff --git a/d2bs/kolbot/libs/manualplay/threads/PickThread.js b/d2bs/kolbot/libs/manualplay/threads/PickThread.js index 8cb06d210..e265a76bc 100644 --- a/d2bs/kolbot/libs/manualplay/threads/PickThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/PickThread.js @@ -51,7 +51,7 @@ function main () { } } - if (!me.inTown && !noPick && !me.itemoncursor && Pickit.gidList.length > 0) { + if (!me.inTown && !noPick && !me.itemoncursor && Pickit.gidList.size > 0) { Pickit.fastPick(1); } From f92c0961c075598413780ec15c40f977915286ec Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 26 Sep 2024 01:00:10 -0400 Subject: [PATCH 442/758] Update Config.d.ts --- d2bs/kolbot/sdk/types/Config.d.ts | 1024 ++++++++++++++--------------- 1 file changed, 491 insertions(+), 533 deletions(-) diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index d6887ea7f..ebe808f45 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -7,399 +7,376 @@ declare global { // interface Scripts { [data: string]: Partial | boolean } - namespace Config { - function init(notify: any): void; - const Loaded: boolean; - const DebugMode: boolean; - const StartDelay: number; - const PickDelay: number; - const AreaDelay: number; - const MinGameTime: number; - const MaxGameTime: number; - const LifeChicken: number; - const ManaChicken: number; - const UseHP: number; - const UseMP: number; - const UseRejuvHP: number; - const UseRejuvMP: number; - const UseMercHP: number; - const UseMercRejuv: number; - const MercChicken: number; - const IronGolemChicken: number; - const HealHP: number; - const HealMP: number; - const HealStatus: boolean; - const TownHP: number; - const TownMP: number; - namespace StackThawingPots { - const enabled: boolean; - const quantity: number; - } - namespace StackAntidotePots { - const enabled_1: boolean; - export { enabled_1 as enabled }; - const quantity_1: number; - export { quantity_1 as quantity }; - } - namespace StackStaminaPots { - const enabled_2: boolean; - export { enabled_2 as enabled }; - const quantity_2: number; - export { quantity_2 as quantity }; - } - const AutoMap: boolean; - const LastMessage: string; - const UseMerc: boolean; - const MercWatch: boolean; - const LowGold: number; - const StashGold: number; - namespace FieldID { - const Enabled: boolean; - const PacketID: boolean; - const UsedSpace: number; - } - namespace DroppedItemsAnnounce { - const Enable: boolean; - const Quality: any[]; - const LogToOOG: boolean; - const OOGQuality: any[]; - } - namespace CainID { - const Enable_1: boolean; - export { Enable_1 as Enable }; - export const MinGold: number; - export const MinUnids: number; - } - const Inventory: number[][]; - namespace LocalChat { - const Enabled_1: boolean; - export { Enabled_1 as Enabled }; - export const Toggle: boolean; - export const Mode: number; - } - const Silence: boolean; - const PublicMode: boolean; - const PartyAfterScript: boolean; - const Greetings: any[]; - const DeathMessages: any[]; - const Congratulations: any[]; - const ShitList: boolean; - const UnpartyShitlisted: boolean; - const Leader: string; - const QuitList: any[]; - const QuitListMode: number; - const QuitListDelay: any[]; - const HPBuffer: number; - const MPBuffer: number; - const RejuvBuffer: number; - const PickRange: number; - const MakeRoom: boolean; - const ClearInvOnStart: boolean; - const FastPick: boolean; - const ManualPlayPick: boolean; - namespace OpenChests { - const Enabled_2: boolean; - export { Enabled_2 as Enabled }; - export const Range: number; - export const Types: string[]; - } - const PickitFiles: any[]; - const BeltColumn: any[]; - const MinColumn: any[]; - const SkipId: any[]; - const SkipEnchant: any[]; - const SkipImmune: any[]; - const SkipAura: any[]; - const SkipException: any[]; - const ScanShrines: any[]; - const Debug: boolean; - namespace AutoMule { - const Trigger: any[]; - const Force: any[]; - const Exclude: any[]; - } - const ItemInfo: boolean; - const ItemInfoQuality: any[]; - const LogKeys: boolean; - const LogOrgans: boolean; - const LogLowRunes: boolean; - const LogMiddleRunes: boolean; - const LogHighRunes: boolean; - const LogLowGems: boolean; - const LogHighGems: boolean; - const SkipLogging: any[]; - const ShowCubingInfo: boolean; - const Cubing: boolean; - const CubeRepair: boolean; - const RepairPercent: number; - const Recipes: any[]; - const MakeRunewords: boolean; - const Runewords: any[][]; - const KeepRunewords: any[]; - const Gamble: boolean; - const GambleItems: any[]; - const GambleGoldStart: number; - const GambleGoldStop: number; - const MiniShopBot: boolean; - const TeleSwitch: boolean; - const MFSwitchPercent: number; - const PrimarySlot: number; - const LogExperience: boolean; - const TownCheck: boolean; - const PingQuit: { + interface Config { + init(notify: any): void; + Loaded: boolean; + DebugMode: { + Path: boolean, + Stack: boolean, + Memory: boolean, + Skill: boolean, + Town: boolean, + }; + StartDelay: number; + PickDelay: number; + AreaDelay: number; + MinGameTime: number; + MaxGameTime: number; + LifeChicken: number; + ManaChicken: number; + UseHP: number; + UseMP: number; + UseRejuvHP: number; + UseRejuvMP: number; + UseMercHP: number; + UseMercRejuv: number; + MercChicken: number; + IronGolemChicken: number; + HealHP: number; + HealMP: number; + HealStatus: boolean; + TownHP: number; + TownMP: number; + StackThawingPots: { + enabled: boolean; + quantity: number; + }; + StackAntidotePots: { + enabled: boolean; + quantity: number; + }; + StackStaminaPots: { + enabled: boolean; + quantity: number; + }; + AutoMap: boolean; + LastMessage: string; + UseMerc: boolean; + MercWatch: boolean; + LowGold: number; + StashGold: number; + FieldID: { + Enabled: boolean; + PacketID: boolean; + UsedSpace: number; + }; + DroppedItemsAnnounce: { + Enable: boolean; + Quality: any[]; + LogToOOG: boolean; + OOGQuality: any[]; + }; + CainID: { + Enable: boolean; + MinGold: number; + MinUnids: number; + }; + Inventory: number[][]; + LocalChat: { + Enabled: boolean; + Toggle: boolean; + Mode: number; + }; + Silence: boolean; + PublicMode: boolean; + PartyAfterScript: boolean; + Greetings: any[]; + DeathMessages: any[]; + Congratulations: any[]; + ShitList: boolean; + UnpartyShitlisted: boolean; + Leader: string; + QuitList: any[]; + QuitListMode: number; + QuitListDelay: any[]; + HPBuffer: number; + MPBuffer: number; + RejuvBuffer: number; + PickRange: number; + MakeRoom: boolean; + ClearInvOnStart: boolean; + FastPick: boolean; + ManualPlayPick: boolean; + OpenChests: { + Enabled: boolean; + Range: number; + Types: string[]; + }; + PickitLines: [string, string][]; + PickitFiles: string[]; + BeltColumn: any[]; + MinColumn: any[]; + SkipId: any[]; + SkipEnchant: any[]; + SkipImmune: any[]; + SkipAura: any[]; + SkipException: any[]; + ScanShrines: any[]; + Debug: boolean; + AutoMule: { + Trigger: any[]; + Force: any[]; + Exclude: any[]; + }; + ItemInfo: boolean; + ItemInfoQuality: any[]; + LogKeys: boolean; + LogOrgans: boolean; + LogLowRunes: boolean; + LogMiddleRunes: boolean; + LogHighRunes: boolean; + LogLowGems: boolean; + LogHighGems: boolean; + SkipLogging: any[]; + ShowCubingInfo: boolean; + Cubing: boolean; + CubeRepair: boolean; + RepairPercent: number; + Recipes: any[]; + MakeRunewords: boolean; + Runewords: any[][]; + KeepRunewords: any[]; + Gamble: boolean; + GambleItems: any[]; + GambleGoldStart: number; + GambleGoldStop: number; + MiniShopBot: boolean; + TeleSwitch: boolean; + MFSwitchPercent: number; + PrimarySlot: number; + LogExperience: boolean; + TownCheck: boolean; + PingQuit: { Ping: number; Duration: number; }[]; - const PacketShopping: boolean; - const FCR: number; - const FHR: number; - const FBR: number; - const IAS: number; - const PacketCasting: number; - const WaypointMenu: boolean; - const AntiHostile: boolean; - const RandomPrecast: boolean; - const HostileAction: number; - const TownOnHostile: boolean; - const ViperCheck: boolean; - const StopOnDClone: boolean; - const SoJWaitTime: number; - const KillDclone: boolean; - const DCloneQuit: boolean; - const DCloneWaitTime: number; - const FastParty: boolean; - const AutoEquip: boolean; - const ChampionBias: number; - const UseCta: boolean; - const Dodge: boolean; - const DodgeRange: number; - const DodgeHP: number; - const AttackSkill: any[]; - const LowManaSkill: any[]; - const CustomAttack: {}; - const TeleStomp: boolean; - const NoTele: boolean; - const ClearType: boolean; - const ClearPath: boolean; - const BossPriority: boolean; - const MaxAttackCount: number; - const LightningFuryDelay: number; - const UseInnerSight: boolean; - const UseSlowMissiles: boolean; - const UseDecoy: boolean; - const SummonValkyrie: boolean; - const UseTelekinesis: boolean; - const CastStatic: boolean; - const StaticList: any[]; - const UseEnergyShield: boolean; - const UseColdArmor: boolean; - const Golem: number; - const ActiveSummon: boolean; - const Skeletons: number; - const SkeletonMages: number; - const Revives: number; - const ReviveUnstackable: boolean; - const PoisonNovaDelay: number; - const Curse: any[]; - const CustomCurse: any[]; - const ExplodeCorpses: number; - const Redemption: number[]; - const Charge: boolean; - const Vigor: boolean; - const AvoidDolls: boolean; - const FindItem: boolean; - const FindItemSwitch: boolean; - const UseWarcries: boolean; - const Wereform: number; - const SummonRaven: number; - const SummonAnimal: number; - const SummonVine: number; - const SummonSpirit: number; - const UseTraps: boolean; - const Traps: any[]; - const BossTraps: any[]; - const UseFade: boolean; - const UseBoS: boolean; - const UseVenom: boolean; - const UseBladeShield: boolean; - const UseCloakofShadows: boolean; - const AggressiveCloak: boolean; - const SummonShadow: boolean; - const CustomClassAttack: string; - namespace MapMode { - const UseOwnItemFilter: boolean; - } - const MFLeader: boolean; - namespace Mausoleum { - const KillBishibosh: boolean; - const KillBloodRaven: boolean; - const ClearCrypt: boolean; - } - namespace Cows { - const DontMakePortal: boolean; - const JustMakePortal: boolean; - const KillKing: boolean; - } - namespace Tombs { - const KillDuriel: boolean; - } - namespace Eldritch { - const OpenChest: boolean; - const KillSharptooth: boolean; - const KillShenk: boolean; - const KillDacFarren: boolean; - } - namespace Pindleskin { - const UseWaypoint: boolean; - const KillNihlathak: boolean; - const ViperQuit: boolean; - } - namespace Nihlathak { - const ViperQuit_1: boolean; - export { ViperQuit_1 as ViperQuit }; - const UseWaypoint_1: boolean; - export { UseWaypoint_1 as UseWaypoint }; - } - namespace Pit { - const ClearPath_1: boolean; - export { ClearPath_1 as ClearPath }; - export const ClearPit1: boolean; - } - namespace Snapchip { - const ClearIcyCellar: boolean; - } - namespace Frozenstein { - const ClearFrozenRiver: boolean; - } - namespace Rakanishu { - const KillGriswold: boolean; - } - namespace AutoBaal { - const Leader_1: string; - export { Leader_1 as Leader }; - export const FindShrine: boolean; - export const LeechSpot: number[]; - export const LongRangeSupport: boolean; - } - namespace KurastChests { - const LowerKurast: boolean; - const Bazaar: boolean; - const Sewers1: boolean; - const Sewers2: boolean; - } - namespace Countess { - const KillGhosts: boolean; - } - namespace Baal { - const DollQuit: boolean; - const SoulQuit: boolean; - const KillBaal: boolean; - const HotTPMessage: string; - const SafeTPMessage: string; - const BaalMessage: string; - } - namespace BaalAssistant { - const KillNihlathak_1: boolean; - export { KillNihlathak_1 as KillNihlathak }; - export const FastChaos: boolean; - export const Wait: number; - export const Helper: boolean; - export const GetShrine: boolean; - export const GetShrineWaitForHotTP: boolean; - const DollQuit_1: boolean; - export { DollQuit_1 as DollQuit }; - const SoulQuit_1: boolean; - export { SoulQuit_1 as SoulQuit }; - export const SkipTP: boolean; - export const WaitForSafeTP: boolean; - const KillBaal_1: boolean; - export { KillBaal_1 as KillBaal }; - const HotTPMessage_1: any[]; - export { HotTPMessage_1 as HotTPMessage }; - const SafeTPMessage_1: any[]; - export { SafeTPMessage_1 as SafeTPMessage }; - const BaalMessage_1: any[]; - export { BaalMessage_1 as BaalMessage }; - export const NextGameMessage: any[]; - } - namespace BaalHelper { - const Wait_1: number; - export { Wait_1 as Wait }; - const KillNihlathak_2: boolean; - export { KillNihlathak_2 as KillNihlathak }; - const FastChaos_1: boolean; - export { FastChaos_1 as FastChaos }; - const DollQuit_2: boolean; - export { DollQuit_2 as DollQuit }; - const KillBaal_2: boolean; - export { KillBaal_2 as KillBaal }; - const SkipTP_1: boolean; - export { SkipTP_1 as SkipTP }; - } - namespace Corpsefire { - const ClearDen: boolean; - } - namespace Hephasto { - export const ClearRiver: boolean; - const ClearType_1: boolean; - export { ClearType_1 as ClearType }; - } - namespace Diablo { - const WalkClear: boolean; - const Entrance: boolean; - const JustViz: boolean; - const SealLeader: boolean; - const Fast: boolean; - const SealWarning: string; - const EntranceTP: string; - const StarTP: string; - const DiabloMsg: string; - const ClearRadius: number; - const SealOrder: string[]; - } - namespace DiabloHelper { - const Wait_2: number; - export { Wait_2 as Wait }; - const Entrance_1: boolean; - export { Entrance_1 as Entrance }; - export const SkipIfBaal: boolean; - const SkipTP_2: boolean; - export { SkipTP_2 as SkipTP }; - export const OpenSeals: boolean; - export const SafePrecast: boolean; - const ClearRadius_1: number; - export { ClearRadius_1 as ClearRadius }; - const SealOrder_1: string[]; - export { SealOrder_1 as SealOrder }; - export const RecheckSeals: boolean; - } - namespace MFHelper { - const BreakClearLevel: boolean; - } - namespace Wakka { - const Wait_3: number; - export { Wait_3 as Wait }; - export const StopAtLevel: number; - export const StopProfile: boolean; - const SkipIfBaal_1: boolean; - export { SkipIfBaal_1 as SkipIfBaal }; - } - namespace BattleOrders { - const Mode_1: number; - export { Mode_1 as Mode }; - export const Getters: any[]; - export const Idle: boolean; - export const QuitOnFailure: boolean; - export const SkipIfTardy: boolean; - const Wait_4: number; - export { Wait_4 as Wait }; - } - namespace BoBarbHelper { - const Mode_2: number; - export { Mode_2 as Mode }; - export const Wp: number; - } - interface ControlBot { + PacketShopping: boolean; + FCR: number; + FHR: number; + FBR: number; + IAS: number; + PacketCasting: number; + WaypointMenu: boolean; + AntiHostile: boolean; + RandomPrecast: boolean; + HostileAction: number; + TownOnHostile: boolean; + ViperCheck: boolean; + StopOnDClone: boolean; + SoJWaitTime: number; + KillDclone: boolean; + DCloneQuit: boolean; + DCloneWaitTime: number; + FastParty: boolean; + AutoEquip: boolean; + ChampionBias: number; + UseCta: boolean; + Dodge: boolean; + DodgeRange: number; + DodgeHP: number; + AttackSkill: any[]; + LowManaSkill: any[]; + CustomAttack: {}; + TeleStomp: boolean; + NoTele: boolean; + ClearType: boolean; + ClearPath: boolean; + BossPriority: boolean; + MaxAttackCount: number; + LightningFuryDelay: number; + UseInnerSight: boolean; + UseSlowMissiles: boolean; + UseDecoy: boolean; + SummonValkyrie: boolean; + UseTelekinesis: boolean; + CastStatic: boolean; + StaticList: any[]; + UseEnergyShield: boolean; + UseColdArmor: boolean; + Golem: number; + ActiveSummon: boolean; + Skeletons: number; + SkeletonMages: number; + Revives: number; + ReviveUnstackable: boolean; + PoisonNovaDelay: number; + Curse: any[]; + CustomCurse: any[]; + ExplodeCorpses: number; + Redemption: number[]; + Charge: boolean; + Vigor: boolean; + AvoidDolls: boolean; + FindItem: boolean; + FindItemSwitch: boolean; + UseWarcries: boolean; + Wereform: number; + SummonRaven: number; + SummonAnimal: number; + SummonVine: number; + SummonSpirit: number; + UseTraps: boolean; + Traps: any[]; + BossTraps: any[]; + UseFade: boolean; + UseBoS: boolean; + UseVenom: boolean; + UseBladeShield: boolean; + UseCloakofShadows: boolean; + AggressiveCloak: boolean; + SummonShadow: boolean; + ChargeCast: { + skill: number; + spectype: number; + classids: (number | string)[]; + }; + CustomClassAttack: string; + MapMode: { + UseOwnItemFilter: boolean; + }; + MFLeader: boolean; + Mausoleum: { + KillBishibosh: boolean; + KillBloodRaven: boolean; + ClearCrypt: boolean; + }; + Cows: { + DontMakePortal: boolean; + JustMakePortal: boolean; + KillKing: boolean; + }; + Tombs: { + KillDuriel: boolean; + }; + Eldritch: { + OpenChest: boolean; + KillSharptooth: boolean; + KillShenk: boolean; + KillDacFarren: boolean; + }; + Pindleskin: { + UseWaypoint: boolean; + KillNihlathak: boolean; + ViperQuit: boolean; + }; + Nihlathak: { + ViperQuit: boolean; + UseWaypoint: boolean; + }; + Pit: { + ClearPath: boolean; + ClearPit1: boolean; + }; + Snapchip: { + ClearIcyCellar: boolean; + }; + Frozenstein: { + ClearFrozenRiver: boolean; + }; + Rakanishu: { + KillGriswold: boolean; + }; + AutoBaal: { + Leader: string; + FindShrine: boolean; + LeechSpot: number[]; + LongRangeSupport: boolean; + }; + KurastChests: { + LowerKurast: boolean; + Bazaar: boolean; + Sewers1: boolean; + Sewers2: boolean; + }; + Countess: { + KillGhosts: boolean; + }; + Baal: { + DollQuit: boolean; + SoulQuit: boolean; + KillBaal: boolean; + HotTPMessage: string; + SafeTPMessage: string; + BaalMessage: string; + }; + BaalAssistant: { + KillNihlathak: boolean; + FastChaos: boolean; + Wait: number; + Helper: boolean; + GetShrine: boolean; + GetShrineWaitForHotTP: boolean; + DollQuit: boolean; + SoulQuit: boolean; + SkipTP: boolean; + WaitForSafeTP: boolean; + KillBaal: boolean; + HotTPMessage: any[]; + SafeTPMessage: any[]; + BaalMessage: any[]; + NextGameMessage: any[]; + }; + BaalHelper: { + Wait: number; + KillNihlathak: boolean; + FastChaos: boolean; + DollQuit: boolean; + KillBaal: boolean; + SkipTP: boolean; + }; + Corpsefire: { + ClearDen: boolean; + }; + Hephasto: { + ClearRiver: boolean; + ClearType: boolean; + }; + Diablo: { + WalkClear: boolean; + Entrance: boolean; + JustViz: boolean; + SealLeader: boolean; + Fast: boolean; + SealWarning: string; + EntranceTP: string; + StarTP: string; + DiabloMsg: string; + ClearRadius: number; + SealOrder: string[]; + }; + DiabloHelper: { + Wait: number; + Entrance: boolean; + SkipIfBaal: boolean; + SkipTP: boolean; + OpenSeals: boolean; + SafePrecast: boolean; + ClearRadius: number; + SealOrder: string[]; + RecheckSeals: boolean; + }; + MFHelper: { + BreakClearLevel: boolean; + }; + Wakka: { + Wait: number; + StopAtLevel: number; + StopProfile: boolean; + SkipIfBaal: boolean; + }; + BattleOrders: { + Mode: number; + Getters: any[]; + Idle: boolean; + QuitOnFailure: boolean; + SkipIfTardy: boolean; + Wait: number; + }; + BoBarbHelper: { + Mode: number; + Wp: number; + }; + ControlBot: { Bo: boolean; Cows: { MakeCows: boolean; @@ -438,151 +415,132 @@ declare global { }; EndMessage: string; GameLength: number; - } - namespace IPHunter { - export const IPList: any[]; - const GameLength_1: number; - export { GameLength_1 as GameLength }; - } - namespace Follower { - const Leader_2: string; - export { Leader_2 as Leader }; - } - namespace Mephisto { - const MoatTrick: boolean; - const KillCouncil: boolean; - const TakeRedPortal: boolean; - } - namespace ShopBot { - const ScanIDs: any[]; - const ShopNPC: string; - const CycleDelay: number; - const QuitOnMatch: boolean; - } - namespace Coldworm { - const KillBeetleburst: boolean; - const ClearMaggotLair: boolean; - } - namespace Summoner { - const FireEye: boolean; - } - namespace AncientTunnels { - const OpenChest_1: boolean; - export { OpenChest_1 as OpenChest }; - export const KillDarkElder: boolean; - } - namespace OrgTorch { - const WaitForKeys: boolean; - const WaitTimeout: boolean; - const UseSalvation: boolean; - const GetFade: boolean; - const MakeTorch: boolean; - namespace PreGame { - namespace Thawing { - const Drink: number; - const At: any[]; - } - namespace Antidote { - const Drink_1: number; - export { Drink_1 as Drink }; - const At_1: any[]; - export { At_1 as At }; - } - } - } - namespace Synch { - const WaitFor: any[]; - } - namespace TristramLeech { - const Leader_3: string; - export { Leader_3 as Leader }; - const Helper_1: boolean; - export { Helper_1 as Helper }; - const Wait_5: number; - export { Wait_5 as Wait }; - } - namespace TravincalLeech { - const Leader_4: string; - export { Leader_4 as Leader }; - const Helper_2: boolean; - export { Helper_2 as Helper }; - const Wait_6: number; - export { Wait_6 as Wait }; - } - namespace Tristram { - export const PortalLeech: boolean; - const WalkClear_1: boolean; - export { WalkClear_1 as WalkClear }; - } - namespace Travincal { - const PortalLeech_1: boolean; - export { PortalLeech_1 as PortalLeech }; - } - namespace SkillStat { - const Skills: any[]; - } - namespace Bonesaw { - const ClearDrifterCavern: boolean; - } - namespace ChestMania { - const Act1: any[]; - const Act2: any[]; - const Act3: any[]; - const Act4: any[]; - const Act5: any[]; - } - namespace ClearAnyArea { - const AreaList: any[]; - } - namespace Rusher { - export const WaitPlayerCount: number; - export const Cain: boolean; - export const Radament: boolean; - export const LamEsen: boolean; - export const Izual: boolean; - export const Shenk: boolean; - export const Anya: boolean; - export const HellAncients: boolean; - const GiveWps_1: boolean; - export { GiveWps_1 as GiveWps }; - export const LastRun: string; - } - namespace Rushee { - const Quester: boolean; - const Bumper: boolean; - } - namespace Questing { - const StopProfile_1: boolean; - export { StopProfile_1 as StopProfile }; - } - interface GetEssences { + }; + IPHunter: { + IPList: any[]; + GameLength: number; + }; + Follower: { + Leader: string; + }; + Mephisto: { + MoatTrick: boolean; + KillCouncil: boolean; + TakeRedPortal: boolean; + }; + ShopBot: { + ScanIDs: any[]; + ShopNPC: string; + CycleDelay: number; + QuitOnMatch: boolean; + }; + Coldworm: { + KillBeetleburst: boolean; + ClearMaggotLair: boolean; + }; + Summoner: { + FireEye: boolean; + }; + AncientTunnels: { + OpenChest: boolean; + KillDarkElder: boolean; + }; + OrgTorch: { + WaitForKeys: boolean; + WaitTimeout: boolean; + UseSalvation: boolean; + GetFade: boolean; + MakeTorch: boolean; + PreGame: { + Thawing: { + Drink: number; + At: any[]; + }; + Antidote: { + Drink: number; + At: any[]; + }; + }; + }; + Synch: { + WaitFor: any[]; + }; + TristramLeech: { + Leader: string; + Helper: boolean; + Wait: number; + }; + TravincalLeech: { + Leader: string; + Helper: boolean; + Wait: number; + }; + Tristram: { + PortalLeech: boolean; + WalkClear: boolean; + }; + Travincal: { + PortalLeech: boolean; + }; + SkillStat: { + Skills: any[]; + }; + Bonesaw: { + ClearDrifterCavern: boolean; + }; + ChestMania: { + Act1: any[]; + Act2: any[]; + Act3: any[]; + Act4: any[]; + Act5: any[]; + }; + ClearAnyArea: { + AreaList: any[]; + }; + Rusher: { + WaitPlayerCount: number; + Cain: boolean; + Radament: boolean; + LamEsen: boolean; + Izual: boolean; + Shenk: boolean; + Anya: boolean; + HellAncients: boolean; + GiveWps: boolean; + LastRun: string; + }; + Rushee: { + Quester: boolean; + Bumper: boolean; + Protector: boolean; + }; + Questing: { + StopProfile: boolean; + }; + GetEssences: { MoatMeph: boolean; FastDiablo: boolean; - } - namespace AutoSkill { - const Enabled_3: boolean; - export { Enabled_3 as Enabled }; - export const Build: any[]; - export const Save: number; - } - namespace AutoStat { - const Enabled_4: boolean; - export { Enabled_4 as Enabled }; - const Build_1: any[]; - export { Build_1 as Build }; - const Save_1: number; - export { Save_1 as Save }; - export const BlockChance: number; - export const UseBulk: boolean; - } - namespace AutoBuild { - const Enabled_5: boolean; - export { Enabled_5 as Enabled }; - export const Template: string; - export const Verbose: boolean; - const DebugMode_1: boolean; - export { DebugMode_1 as DebugMode }; - } + }; + AutoSkill: { + Enabled: boolean; + Build: any[]; + Save: number; + }; + AutoStat: { + Enabled: boolean; + Build: any[]; + Save: number; + BlockChance: number; + UseBulk: boolean; + }; + AutoBuild: { + Enabled: boolean; + Template: string; + Verbose: boolean; + DebugMode: boolean; + }; } + const Config: Config; } export {}; From b677ecc2246b6020134ae79aa772e85f4875b634 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 12 Oct 2024 04:39:49 -0400 Subject: [PATCH 443/758] [Chore] Cleanup + maintenance - Added github workflow to run eslint on PRs - Updated vscode settings.json to enable eslint fixes on save - Removed the "no-useless-escape" rule and added ignoring strings to the "max-len" rule - Added lint script to package.json - Linted and fixed all reported issues --- .eslintrc.js | 3 +- .github/workflows/eslint.yml | 23 + .vscode/settings.json | 5 +- d2bs/kolbot/D2BotFollow.dbj | 2 +- d2bs/kolbot/libs/config/Builds/Class.Build.js | 1793 ++++++++--------- d2bs/kolbot/libs/modules/HTTP.js | 18 +- d2bs/kolbot/libs/modules/Override.js | 1 + d2bs/kolbot/libs/scripts/Follower.js | 2 +- d2bs/kolbot/libs/scripts/OrgTorch.js | 9 +- d2bs/kolbot/threads/Party.js | 4 +- package.json | 1 + 11 files changed, 952 insertions(+), 909 deletions(-) create mode 100644 .github/workflows/eslint.yml diff --git a/.eslintrc.js b/.eslintrc.js index e571a42da..cd2c3d2b8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -131,6 +131,7 @@ module.exports = { "no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted" }], "no-undef": ["off", "always"], "no-extra-boolean-cast": ["off", "always"], - "max-len": ["warn", { "code": 120, "ignoreComments": true, "ignoreUrls": true }], + "no-useless-escape": ["off", "always"], + "max-len": ["warn", { "code": 120, "ignoreComments": true, "ignoreUrls": true, "ignoreStrings": true }], } }; diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 000000000..c833727d3 --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,23 @@ +name: ESLint Check + +on: + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18.20.4' + + - name: Install dependencies + run: npm install + + - name: Run ESLint + run: npm run lint \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 68e0b66f9..b128d3e28 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,8 @@ 120 ], "editor.tabSize": 2, - "editor.detectIndentation": false + "editor.detectIndentation": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, } \ No newline at end of file diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 805e79b39..59364cfe6 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -178,7 +178,7 @@ const locationAction = (function () { JoinLoop2: for (let i = 0; i < 5; i += 1) { - for (let [leader, check] of tracker) { + for (let [leader, _check] of tracker) { Starter.joinInfo = {}; D2Bot.requestGame(leader); delay(100); diff --git a/d2bs/kolbot/libs/config/Builds/Class.Build.js b/d2bs/kolbot/libs/config/Builds/Class.Build.js index c076defcd..519d07360 100644 --- a/d2bs/kolbot/libs/config/Builds/Class.Build.js +++ b/d2bs/kolbot/libs/config/Builds/Class.Build.js @@ -16,901 +16,900 @@ */ js_strict(true); -if (!isIncluded("core/Cubing.js")) { include("core/Cubing.js"); }; -if (!isIncluded("core/Prototypes.js")) { include("core/Prototypes.js"); }; -if (!isIncluded("core/Runewords.js")) { include("core/Runewords.js"); }; -if (!isIncluded("core/Town.js")) { include("core/Town.js"); }; - -var AutoBuildTemplate = { - - 1: { - //SkillPoints: [-1], // This doesn't matter. We don't have skill points to spend at lvl 1 - //StatPoints: [-1,-1,-1,-1,-1], // This doesn't matter. We don't have stat points to spend at lvl 1 - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 2: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 3: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 4: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 5: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 6: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 7: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 8: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 9: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 10: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 11: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 12: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 13: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 14: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 15: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 16: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 17: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 18: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 19: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 20: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 21: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 22: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 23: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 24: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 25: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 26: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 27: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 28: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 29: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 30: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 31: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 32: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 33: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 34: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 35: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 36: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 37: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 38: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 39: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 40: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 41: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 42: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 43: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 44: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 45: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 46: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 47: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 48: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 49: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 50: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 51: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 52: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 53: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 54: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 55: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 56: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 57: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 58: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 59: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 60: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 61: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 62: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 63: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 64: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 65: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 66: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 67: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 68: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 69: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 70: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 71: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 72: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 73: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 74: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 75: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 76: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 77: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 78: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 79: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 80: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 81: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 82: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 83: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 84: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 85: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 86: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 87: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 88: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 89: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 90: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 91: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 92: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 93: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 94: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 95: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 96: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 97: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 98: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - }, - - 99: { - SkillPoints: [-1], - StatPoints: [-1,-1,-1,-1,-1], - Update: function () { - Config.AttackSkill = [-1,-1,-1,-1,-1,-1,-1]; - Config.LowManaSkill = [-1,-1]; - } - } +if (!isIncluded("core/Cubing.js")) { include("core/Cubing.js"); } +if (!isIncluded("core/Prototypes.js")) { include("core/Prototypes.js"); } +if (!isIncluded("core/Runewords.js")) { include("core/Runewords.js"); } +if (!isIncluded("core/Town.js")) { include("core/Town.js"); } + +const AutoBuildTemplate = { + 1: { + //SkillPoints: [-1], // This doesn't matter. We don't have skill points to spend at lvl 1 + //StatPoints: [-1,-1,-1,-1,-1], // This doesn't matter. We don't have stat points to spend at lvl 1 + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 2: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 3: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 4: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 5: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 6: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 7: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 8: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 9: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 10: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 11: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 12: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 13: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 14: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 15: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 16: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 17: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 18: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 19: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 20: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 21: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 22: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 23: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 24: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 25: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 26: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 27: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 28: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 29: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 30: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 31: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 32: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 33: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 34: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 35: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 36: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 37: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 38: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 39: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 40: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 41: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 42: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 43: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 44: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 45: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 46: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 47: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 48: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 49: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 50: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 51: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 52: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 53: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 54: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 55: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 56: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 57: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 58: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 59: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 60: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 61: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 62: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 63: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 64: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 65: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 66: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 67: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 68: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 69: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 70: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 71: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 72: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 73: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 74: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 75: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 76: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 77: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 78: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 79: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 80: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 81: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 82: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 83: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 84: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 85: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 86: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 87: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 88: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 89: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 90: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 91: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 92: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 93: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 94: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 95: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 96: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 97: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 98: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + }, + + 99: { + SkillPoints: [-1], + StatPoints: [-1, -1, -1, -1, -1], + Update: function () { + Config.AttackSkill = [-1, -1, -1, -1, -1, -1, -1]; + Config.LowManaSkill = [-1, -1]; + } + } }; diff --git a/d2bs/kolbot/libs/modules/HTTP.js b/d2bs/kolbot/libs/modules/HTTP.js index e5d3cae4a..584d1f7da 100644 --- a/d2bs/kolbot/libs/modules/HTTP.js +++ b/d2bs/kolbot/libs/modules/HTTP.js @@ -8,6 +8,17 @@ const Socket = require("Socket"); const Promise = require("Promise"); + /** + * @typedef {Object} HTTPConfig + * @property {string} url - The URL to send the request to. + * @property {Object} headers - The headers to include in the request. + * @property {string} headers.User-Agent - The User-Agent header value. + * @property {string} headers.Accept - The Accept header value. + * @property {number} port - The port to use for the request. + * @property {string} method - The HTTP method to use for the request. + * @property {string} data - The data to send with the request (for POST requests). + */ + const defaultOptions = { url: "", headers: { @@ -19,12 +30,15 @@ data: "", // Fill with content for post }; + /** + * @param {HTTPConfig} config + */ const HTTP = function (config = {}) { config = Object.assign(defaultOptions, config); if (!config.url) { throw new Error("Must give a url to connect to"); } - const [fullUrl, protocol, hostname, uri] = config.url.match(/^(.*:)\/\/([A-Za-z0-9\-\.]+)?(.*)/); + const [_fullUrl, _protocol, hostname, uri] = config.url.match(/^(.*:)\/\/([A-Za-z0-9\-\.]+)?(.*)/); const socket = new Socket(hostname, config.port); socket.connect(); @@ -35,7 +49,7 @@ if (config.data.length) { // in case we send data config.headers["Content-Length"] = config.data.length; } - config.headers["Host"] = hostname; // required for HTTP/1.1 + config.headers.Host = hostname; // required for HTTP/1.1 const data = [config.method + " " + uri + " " + "HTTP/1.1"]; Object.keys(config.headers).forEach((key) => data.push(key + ": " + config.headers[key])); diff --git a/d2bs/kolbot/libs/modules/Override.js b/d2bs/kolbot/libs/modules/Override.js index a7b496177..4eac2330f 100644 --- a/d2bs/kolbot/libs/modules/Override.js +++ b/d2bs/kolbot/libs/modules/Override.js @@ -1,3 +1,4 @@ +/* eslint-disable */ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index 2a1dbdf9c..d979103a7 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -435,7 +435,7 @@ const Follower = new Runnable( /** * @todo allow user to use skill name and try to match it to skill id */ - const skillsMap = (function () { + const _skillsMap = (function () { const _skills = new Map(); for (let value of Object.values(sdk.skills)) { diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index 0ced53cc8..0811a25b5 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -432,11 +432,12 @@ const OrgTorch = new Runnable( */ const runEvent = function (portal) { if (portal) { - if (Config.OrgTorch.PreGame.Antidote.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Antidote.Drink > 0) { - Town.buyPots(Config.OrgTorch.PreGame.Antidote.Drink, "Antidote", true, true); + const { Antidote, Thawing } = Config.OrgTorch.PreGame; + if (Antidote.At.includes(portal.objtype) && Antidote.Drink > 0) { + Town.buyPots(Antidote.Drink, "Antidote", true, true); } - if (Config.OrgTorch.PreGame.Thawing.At.includes(portal.objtype) && Config.OrgTorch.PreGame.Thawing.Drink > 0) { - Town.buyPots(Config.OrgTorch.PreGame.Thawing.Drink, "Thawing", true, true); + if (Thawing.At.includes(portal.objtype) && Thawing.Drink > 0) { + Town.buyPots(Thawing.Drink, "Thawing", true, true); } Town.move("stash"); console.log("taking portal: " + portal.objtype); diff --git a/d2bs/kolbot/threads/Party.js b/d2bs/kolbot/threads/Party.js index 6b585ed02..3cb328b28 100644 --- a/d2bs/kolbot/threads/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -85,13 +85,13 @@ function main () { } let quitting = false; - let partyCheck = false; + // let partyCheck = false; const scriptEvent = function (msg) { if (!!msg && typeof msg === "string") { switch (msg) { case "hostileCheck": - partyCheck = true; + // partyCheck = true; break; case "quit": diff --git a/package.json b/package.json index 466eef1b3..7d19e904c 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "", "typings": "./d2bs/kolbot/**/global.d.ts", "scripts": { + "lint": "eslint --fix-dry-run --ext .js --ext .dbj d2bs/kolbot/", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], From 1bf6c105b8efc753d4703e94573cc9c4980ca05f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 12 Oct 2024 06:16:47 -0400 Subject: [PATCH 444/758] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d19e904c..e2aefb83b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "", "typings": "./d2bs/kolbot/**/global.d.ts", "scripts": { - "lint": "eslint --fix-dry-run --ext .js --ext .dbj d2bs/kolbot/", + "lint": "eslint --fix --ext .js --ext .dbj d2bs/kolbot/", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], From c59b21660bfb32a0f910f31df09d7f63bb251a54 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 12 Oct 2024 17:33:34 -0400 Subject: [PATCH 445/758] Update DeveloperMode.js - Added option to create a character config if one wasn't found - Added utility method `log` - Added jsdoc - Fixed thrown errors from incorrect script names breaking the event handler and handle case sensitivity as it's annoyoing knowing we are trying to run `Baal` but typing `baal` and it failing --- d2bs/kolbot/libs/scripts/DeveloperMode.js | 121 ++++++++++++++++------ 1 file changed, 87 insertions(+), 34 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/DeveloperMode.js b/d2bs/kolbot/libs/scripts/DeveloperMode.js index 9bd7fbc8d..dbee98448 100644 --- a/d2bs/kolbot/libs/scripts/DeveloperMode.js +++ b/d2bs/kolbot/libs/scripts/DeveloperMode.js @@ -7,8 +7,31 @@ const DeveloperMode = new Runnable( function DeveloperMode () { + const className = sdk.player.class.nameOf(me.classid); + + /** + * @param {string} str + * @param {boolean} [toConsole=false] + * @param {number | string} [color=0] + */ + const log = function (str = "", toConsole = false, color = 0) { + console.log("ÿc8Dev Modeÿ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("Dev Mode :: " + str, color); + }; + let [done, action, command, userAddon, test] = [false, false, false, false, false]; let [watchSent, watchRecv, blockSent, blockRecv] = [[], [], [], []]; + + /** + * @param {string} msg + * @returns {void} + */ const runCommand = function (msg) { if (msg.length <= 1) return; @@ -17,8 +40,7 @@ const DeveloperMode = new Runnable( switch (cmd.toLowerCase()) { case "me": - print("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); - me.overhead("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); + log("Character Level: " + me.charlvl + " | Area: " + me.area + " | x: " + me.x + ", y: " + me.y); break; case "useraddon": @@ -28,7 +50,7 @@ const DeveloperMode = new Runnable( break; case "run": if (msgList.length < 2) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); } else { action = msgList[1]; } @@ -44,7 +66,7 @@ const DeveloperMode = new Runnable( break; case "command": if (msgList.length < 2) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); } else { command = msgList.splice(1).join(" "); } @@ -52,33 +74,33 @@ const DeveloperMode = new Runnable( break; case "watch": if (msgList.length < 3) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); break; } switch (msgList[1].toLowerCase()) { case "sent": if (msgList[2] === "list") { - print("Watching sent packets : ÿc8" + watchSent.join(", ")); + console.log("Watching sent packets : ÿc8" + watchSent.join(", ")); break; } watchSent.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to watch list"); break; case "recv": if (msgList[2] === "list") { - print("Watching received packets : ÿc8" + watchRecv.join(", ")); + console.log("Watching received packets : ÿc8" + watchRecv.join(", ")); break; } watchRecv.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to watch list"); break; default: - print("ÿc1Invalid argument : " + msgList[1]); + console.log("ÿc1Invalid argument : " + msgList[1]); break; } @@ -86,23 +108,23 @@ const DeveloperMode = new Runnable( case "!watch": if (msgList.length < 3) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); break; } switch (msgList[1].toLowerCase()) { case "sent": if (watchSent.indexOf(msgList[2]) > -1) watchSent.splice(watchSent.indexOf(msgList[2]), 1); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from watch list"); + 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); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); + console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from watch list"); break; default: - print("ÿc1Invalid argument : " + msgList[1]); + console.log("ÿc1Invalid argument : " + msgList[1]); break; } @@ -110,33 +132,33 @@ const DeveloperMode = new Runnable( case "block": if (msgList.length < 3) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); break; } switch (msgList[1].toLowerCase()) { case "sent": if (msgList[2] === "list") { - print("Blocking sent packets : ÿc8" + blockSent.join(", ")); + console.log("Blocking sent packets : ÿc8" + blockSent.join(", ")); break; } blockSent.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (sent) to block list"); break; case "recv": if (msgList[2] === "list") { - print("Blocking received packets : ÿc8" + blockRecv.join(", ")); + console.log("Blocking received packets : ÿc8" + blockRecv.join(", ")); break; } blockRecv.push(msgList[2]); - print("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); + console.log("Added ÿc80x" + msgList[2] + "ÿc0 (recv) to block list"); break; default: - print("ÿc1Invalid argument : " + msgList[1]); + console.log("ÿc1Invalid argument : " + msgList[1]); break; } @@ -144,23 +166,23 @@ const DeveloperMode = new Runnable( case "!block": if (msgList.length < 3) { - print("ÿc1Missing arguments"); + console.log("ÿc1Missing arguments"); break; } switch (msgList[1].toLowerCase()) { case "sent": if (blockSent.indexOf(msgList[2]) > -1) blockSent.splice(blockSent.indexOf(msgList[2]), 1); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (sent) from block list"); + 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); - print("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); + console.log("Removed packet ÿc80x" + msgList[2] + "ÿc0 (recv) from block list"); break; default: - print("ÿc1Invalid argument : " + msgList[1]); + console.log("ÿc1Invalid argument : " + msgList[1]); break; } @@ -218,6 +240,20 @@ const DeveloperMode = new Runnable( return false; }; + /** + * @param {number} key + */ + const keyEvent = function (key) { + switch (key) { + case sdk.keys.Spacebar: + FileTools.copy("libs/config/" + className + ".js", "libs/config/" + className + "." + me.name + ".js"); + log("libs/config/" + className + "." + me.name + ".js has been created.", true); + log("Please configure your bot and reload or restart", true); + + break; + } + }; + const copiedConfig = copyObj(Config); const UnitInfo = new (require("../modules/UnitInfo")); @@ -226,22 +262,38 @@ const DeveloperMode = new Runnable( me.overhead("Started developer mode"); addEventListener("gamepacketsent", packetSent); addEventListener("gamepacket", packetReceived); + + if (!FileTools.exists("libs/config/" + className + "." + me.name + ".js")) { + console.log("ÿc4UserAddonÿc0: Press HOME and then press SPACE if you want to create character config."); + addEventListener("keyup", keyEvent); + } Config.Silence = false; while (!done) { if (action) { - includeIfNotIncluded("scripts/" + action + ".js"); - - UnitInfo.check(); + try { + let script = Loader.fileList.find(function (el) { + return String.isEqual(el, action); + }); - if (isIncluded("scripts/" + action + ".js")) { - try { - Loader.runScript(action); - } catch (e) { - console.error(e); + if (!script) { + throw new Error("Failed to find script: " + action); + } + includeIfNotIncluded("scripts/" + script + ".js"); + + UnitInfo.check(); + + if (isIncluded("scripts/" + script + ".js")) { + try { + Loader.runScript(script); + } catch (e) { + console.error(e); + } + } else { + console.warn("Failed to include: " + script); } - } else { - console.warn("Failed to include: " + action); + } catch (e) { + console.error(e); } me.overhead("Done with action"); @@ -273,6 +325,7 @@ const DeveloperMode = new Runnable( delay(100); } } finally { + removeEventListener("keyup", keyEvent); removeEventListener("gamepacketsent", packetSent); removeEventListener("gamepacket", packetReceived); Config = copiedConfig; From de723b00bbb8e966ed6fa60067df59b6ce826a09 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 12 Oct 2024 17:42:55 -0400 Subject: [PATCH 446/758] Update string prototype `isEqual` and add `isType` utility method - Add optional third parameter on `isEqual` for toggling case sensitivity, it defaults to false meaning both strings with be downcased for comparison - Add `isType` method to replace repeat instances of `typeof val !== type` checks - Updated some missing type info --- d2bs/kolbot/libs/Polyfill.js | 90 ++++++++++++++++++++++++++-- d2bs/kolbot/sdk/globals.d.ts | 112 ++++++++++++++++++++++++++++++++--- 2 files changed, 190 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index f25c4e3f7..22f213217 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -68,7 +68,7 @@ String.prototype.diffCount = function (stringB) { return (Math.max(graph.a.length, graph.b.length) - graph.graph[graph.a.length - 1][graph.b.length - 1]); } catch (err) { - print(err.stack); + console.log(err.stack); } return Infinity; @@ -184,10 +184,14 @@ if (!String.isEqual) { * @static * @param {string} str1 * @param {string} str2 + * @param {boolean} caseSensitive * @returns {boolean} */ - String.isEqual = function (str1, str2) { - if (typeof str1 !== "string" || typeof str2 !== "string") return false; + String.isEqual = function (str1, str2, caseSensitive = false) { + if (!isType(str1, "string") || !isType(str2, "string")) return false; + if (caseSensitive) { + return str1 === str2; + } return str1.toLowerCase() === str2.toLowerCase(); }; } @@ -1348,15 +1352,44 @@ if (!global.hasOwnProperty("copyObj")) { }); } +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Misc Utils ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Time - Namespace of Helper methods for dealing with time + * - isType - Method to peform simple type checks + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + const Time = { + /** + * Converts seconds to milliseconds. + * + * @param {number} [seconds=0] - The number of seconds to convert. + * @returns {number} - The equivalent time in milliseconds. + */ seconds: function (seconds = 0) { - if (typeof seconds !== "number") return 0; + if (!isType(seconds, "number")) return 0; return (seconds * 1000); }, + + /** + * Converts minutes to milliseconds. + * + * @param {number} [minutes=0] - The number of minutes to convert. + * @returns {number} - The equivalent time in milliseconds. + */ minutes: function (minutes = 0) { - if (typeof minutes !== "number") return 0; + if (!isType(minutes, "number")) return 0; return (minutes * 60000); }, + + /** + * Formats milliseconds into a "HH:MM:SS" string. + * + * @param {number} [ms=0] - The time in milliseconds to format. + * @returns {string} - The formatted time string. + */ format: function (ms = 0) { const hours = Math.floor(ms / 3600000); const minutes = Math.floor((ms % 3600000) / 60000); @@ -1370,19 +1403,66 @@ const Time = { return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); // return (new Date(ms).toISOString().slice(11, -5)); }, + + /** + * Converts milliseconds to seconds. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in seconds. + */ toSeconds: function (ms = 0) { return (ms / 1000); }, + + /** + * Converts milliseconds to minutes. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in minutes. + */ toMinutes: function (ms = 0) { return (ms / 60000); }, + + /** + * Converts milliseconds to hours. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in hours. + */ toHours: function (ms = 0) { return (ms / 3600000); }, + + /** + * Converts milliseconds to days. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in days. + */ toDays: function (ms = 0) { return (ms / 86400000); }, + + /** + * Calculates the elapsed time from a given timestamp. + * + * @param {number} [ms=0] - The starting time in milliseconds. + * @returns {number} - The elapsed time in milliseconds. + */ elapsed: function (ms = 0) { return (getTickCount() - ms); } }; + +/** + * @param {any} val + * @param {PrimitiveType} type + * @returns {boolean} + */ +const isType = function (val, type) { + if (type === "array") { + return Array.isArray(val); + } + return typeof val === type; +}; diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 83dadc424..23de5bbf1 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -16,6 +16,7 @@ /// /// /// +/// declare global { interface Error { @@ -95,7 +96,7 @@ declare global { } interface StringConstructor { - static isEqual(str1: string, str2: string): boolean; + static isEqual(str1: string, str2: string, caseSensitive?: boolean): boolean; } interface ObjectConstructor { @@ -751,15 +752,28 @@ declare global { } const me: MeType - - // type PathNode = { - // x: number, - // y: number - // }; interface PathNode { x: number; y: number; + /** + * Distance from 'me' to this node + */ readonly distance: number; + /** + * Distance from 'unit' to this node + * @param unit + */ + distanceTo(unit: Unit): number; + /** + * Walk Distance from 'me' to this node + */ + getWalkDistance(): number; + /** + * Walk Distance from 'node' to this node + * @param node + */ + getWalkDistanceTo(node: PathNode, area?: number): number; + mobCount(givenSettings: { range?: number, coll?: number, type?: number, ignoreClassids?: number[] }): number; } function getUnit(type: 4, name?: string, mode?: number, unitId?: number): ItemUnit @@ -805,6 +819,8 @@ declare global { class Room { area: number; + level: number; + number: number; correcttomb: number; x: number; y: number; @@ -1078,6 +1094,11 @@ declare global { function quit(): never function quitGame(): never function say(what: string, force?: boolean): void + /** + * Use when you want to force something to be said in chat and don't know if LocalChat is being used + * @param what + */ + function _say(what: string): void function clickParty(player: Party, type: 0 | 1 | 2 | 3 | 4) function weaponSwitch(): void function transmute(): void @@ -1191,6 +1212,15 @@ declare global { GameDoesNotExistTimeout: number, // Seconds to wait before cancelling the 'Game does not exist.' screen } + interface ProfileInfo { + profile: string, + account: string, + charName: string, + difficulty: string, + tag: string, + realm: string, + } + interface StarterInterface { Config: StarterConfig, useChat: boolean, @@ -1218,6 +1248,9 @@ declare global { }, gameInfo: { error: string, + gameName?: string, + gamePass?: string, + difficulty?: string, crashInfo: { currScript: number, area: number, @@ -1225,7 +1258,7 @@ declare global { switchKeys: boolean, }, joinInfo: {}, - profileInfo: {}, + profileInfo: ProfileInfo, sayMsg(string: string): void, timer(tick: number): string, @@ -1241,14 +1274,79 @@ declare global { const Starter: StarterInterface; namespace Time { + /** + * Converts seconds to milliseconds. + * + * @param {number} [seconds=0] - The number of seconds to convert. + * @returns {number} - The equivalent time in milliseconds. + */ function seconds(seconds: number): number; + + /** + * Converts minutes to milliseconds. + * + * @param {number} [minutes=0] - The number of minutes to convert. + * @returns {number} - The equivalent time in milliseconds. + */ function minutes(minutes: number): number; + + /** + * Formats milliseconds into a "HH:MM:SS" string. + * + * @param {number} [ms=0] - The time in milliseconds to format. + * @returns {string} - The formatted time string. + */ function format(ms: number): string; + + /** + * Converts milliseconds to seconds. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in seconds. + */ function toSeconds(ms: number): number; + + /** + * Converts milliseconds to minutes. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in minutes. + */ function toMinutes(ms: number): number; + + /** + * Converts milliseconds to hours. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in hours. + */ function toHours(ms: number): number; + + /** + * Converts milliseconds to days. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in days. + */ function toDays(ms: number): number; + + /** + * Calculates the elapsed time from a given timestamp. + * + * @param {number} [ms=0] - The starting time in milliseconds. + * @returns {number} - The elapsed time in milliseconds. + */ function elapsed(start: number): number; } + + type PrimitiveType = "undefined" | "object" | "boolean" | "number" | "bigint" | "string" | "symbol" | "function" | "array"; + /** + * Checks if the value matches the expected type. + * + * @param val - The value to be checked. + * @param type - The expected type as a string. + * @returns {boolean} - Returns true if the value matches the expected type, otherwise false. + */ + function isType(val: any, type: PrimitiveType): boolean; } export {}; From 226c33d3d9ad1287430409ede4cfd894ad606ec8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 12 Oct 2024 20:13:34 -0400 Subject: [PATCH 447/758] Ensure only unique recipes in cubing list + add low grand charm recipe - The unique recipe constraint mostly affects soloplay, improves cubing resource allocation without having multiple identical recipes all trying to get ingredients - The lowgrand recipe is for rerolling grand charms for plain skillers --- d2bs/kolbot/libs/core/Cubing.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index dfa67b894..8acef6f11 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -99,6 +99,7 @@ const Recipe = { Small: 56, Large: 57, Grand: 58, + LowGrand: 64 }, }, Rune: 52, @@ -240,6 +241,7 @@ Object.defineProperty(Recipe, "ingredients", { return [sdk.items.SmallCharm, "pgem", "pgem", "pgem"]; case Recipe.Reroll.Charm.Large: return [sdk.items.LargeCharm, "pgem", "pgem", "pgem"]; + case Recipe.Reroll.Charm.LowGrand: case Recipe.Reroll.Charm.Grand: return [sdk.items.GrandCharm, "pgem", "pgem", "pgem"]; case Recipe.Reroll.Magic: // Hacky solution ftw @@ -356,9 +358,11 @@ const Cubing = { init: function () { if (!Config.Cubing) return; - // console.log("We have " + Config.Recipes.length + " cubing recipe(s)."); + /** @type {Set} */ + const uniqueRecipes = new Set(); + for (let i = 0; i < Config.Recipes.length; i += 1) { if (Config.Recipes[i].length > 1 && isNaN(Config.Recipes[i][1])) { const formattedName = Config.Recipes[i][1].replace(/\s+/g, "").toLowerCase(); @@ -371,6 +375,14 @@ const Cubing = { i -= 1; } } + + let stringifiedRecipe = JSON.stringify(Config.Recipes[i]); + if (uniqueRecipes.has(stringifiedRecipe)) { + Config.Recipes.splice(i, 1); + i -= 1; + } else { + uniqueRecipes.add(stringifiedRecipe); + } } this.buildRecipes(); @@ -620,6 +632,7 @@ const Cubing = { break; case Recipe.Reroll.Charm.Small: case Recipe.Reroll.Charm.Large: + case Recipe.Reroll.Charm.LowGrand: case Recipe.Reroll.Charm.Grand: case Recipe.Reroll.Magic: // Hacky solution ftw /** @@ -629,6 +642,8 @@ const Cubing = { this.recipes.push({ Ingredients: ingredients, Level: 94, Index: index }); } else if (index === Recipe.Reroll.Charm.Large) { this.recipes.push({ Ingredients: ingredients, Level: 76, Index: index }); + } else if (index === Recipe.Reroll.Charm.LowGrand) { + this.recipes.push({ Ingredients: ingredients, Level: 50, Index: index }); } else if (index === Recipe.Reroll.Charm.Grand) { this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); } else { From ebfe1407be53e34ce92dcabecf59aea4b9e76bda Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:18:54 -0400 Subject: [PATCH 448/758] Created `globals.js` to clean up the misuse of Pollyfill.js - The global decorators weren't exactly pollyfills, just things like sdk and global methods to make like easier so move them to their own file. --- d2bs/kolbot/libs/Polyfill.js | 276 ------------------------------- d2bs/kolbot/libs/core/Util.js | 18 -- d2bs/kolbot/libs/critical.js | 1 + d2bs/kolbot/libs/globals.js | 299 ++++++++++++++++++++++++++++++++++ 4 files changed, 300 insertions(+), 294 deletions(-) create mode 100644 d2bs/kolbot/libs/globals.js diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 22f213217..3b48b5cfb 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -1190,279 +1190,3 @@ if (!Date.prototype.hasOwnProperty("dateStamp")) { } }); } - -/** - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - * ~~~~~~~~~~~~~~~~~~~~~~ global d2bs Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - * - sdk - sdk object @see libs/modules/sdk.js - * - includeIfNotIncluded - include file if not already included - * - includeCoreLibs - include all core libs - * - includeSystemLibs - include all system driver files - * - clone - clone object - * - copyObj - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - */ - - -if (!global.hasOwnProperty("sdk") && typeof require !== "undefined") { - Object.defineProperty(global, "sdk", { - value: require("../modules/sdk"), - enumerable: true, - }); -} - -if (!global.hasOwnProperty("includeIfNotIncluded")) { - Object.defineProperty(global, "includeIfNotIncluded", { - /** - * @param {string} file - */ - value: function (file = "") { - if (!isIncluded(file)) { - if (!include(file)) { - console.error("Failed to include " + file); - console.trace(); - return false; - } - } - return true; - }, - }); -} - -if (!global.hasOwnProperty("includeCoreLibs")) { - Object.defineProperty(global, "includeCoreLibs", { - /** - * @description includes all files from libs/core/ folder - * @param {string[]} ignoreFiles - */ - value: function (obj = { exclude: [] }) { - /** @type {string[]} */ - let files = dopen("libs/core/").getFiles(); - if (!files.length) throw new Error("Failed to find my files"); - if (!files.includes("Pather.js")) { - console.warn("Incorrect Files?", files); - // something went wrong? - while (!files.includes("Pather.js")) { - files = dopen("libs/core/").getFiles(); - delay(50); - } - } - // always include util first - includeIfNotIncluded("core/Util.js"); - files - .filter(function (file) { - return file.endsWith(".js") - && !obj.exclude.includes(file) - && !file.match("util.js", "gi"); - }) - .forEach(function (x) { - if (!includeIfNotIncluded("core/" + x)) { - throw new Error("Failed to include core/" + x); - } - }); - return true; - }, - }); -} - -if (!global.hasOwnProperty("includeSystemLibs")) { - Object.defineProperty(global, "includeSystemLibs", { - /** - * @description includes system driver files from libs/systems/ folder - */ - value: function () { - include("systems/automule/automule.js"); - include("systems/crafting/CraftingSystem.js"); - include("systems/gambling/Gambling.js"); - include("systems/torch/TorchSystem.js"); - return true; - }, - }); -} - -if (!global.hasOwnProperty("clone")) { - Object.defineProperty(global, "clone", { - /** - * @param {Date | any[] | object} obj - * @returns {ThisParameterType} deep copy of parameter - */ - value: function (obj) { - let copy; - - // Handle the 3 simple types, and null or undefined - if (null === obj || "object" !== typeof obj) { - return obj; - } - - // Handle Date - if (obj instanceof Date) { - copy = new Date(); - copy.setTime(obj.getTime()); - - return copy; - } - - // Handle Array - if (obj instanceof Array) { - copy = []; - - for (let i = 0; i < obj.length; i += 1) { - copy[i] = clone(obj[i]); - } - - return copy; - } - - // Handle Object - if (obj instanceof Object) { - copy = {}; - - for (let attr in obj) { - if (obj.hasOwnProperty(attr)) { - copy[attr] = clone(obj[attr]); - } - } - - return copy; - } - - throw new Error("Unable to copy obj! Its type isn't supported."); - }, - }); -} - -if (!global.hasOwnProperty("copyObj")) { - Object.defineProperty(global, "copyObj", { - /** - * @param {object} from - * @returns {object} deep copy - */ - value: function (from) { - let obj = {}; - - for (let i in from) { - if (from.hasOwnProperty(i)) { - obj[i] = clone(from[i]); - } - } - - return obj; - }, - }); -} - -/** - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Misc Utils ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - * - Time - Namespace of Helper methods for dealing with time - * - isType - Method to peform simple type checks - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - */ - -const Time = { - /** - * Converts seconds to milliseconds. - * - * @param {number} [seconds=0] - The number of seconds to convert. - * @returns {number} - The equivalent time in milliseconds. - */ - seconds: function (seconds = 0) { - if (!isType(seconds, "number")) return 0; - return (seconds * 1000); - }, - - /** - * Converts minutes to milliseconds. - * - * @param {number} [minutes=0] - The number of minutes to convert. - * @returns {number} - The equivalent time in milliseconds. - */ - minutes: function (minutes = 0) { - if (!isType(minutes, "number")) return 0; - return (minutes * 60000); - }, - - /** - * Formats milliseconds into a "HH:MM:SS" string. - * - * @param {number} [ms=0] - The time in milliseconds to format. - * @returns {string} - The formatted time string. - */ - 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 = function (num) { - return (num < 10 ? "0" + num : num); - }; - - return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); - // return (new Date(ms).toISOString().slice(11, -5)); - }, - - /** - * Converts milliseconds to seconds. - * - * @param {number} [ms=0] - The time in milliseconds to convert. - * @returns {number} - The equivalent time in seconds. - */ - toSeconds: function (ms = 0) { - return (ms / 1000); - }, - - /** - * Converts milliseconds to minutes. - * - * @param {number} [ms=0] - The time in milliseconds to convert. - * @returns {number} - The equivalent time in minutes. - */ - toMinutes: function (ms = 0) { - return (ms / 60000); - }, - - /** - * Converts milliseconds to hours. - * - * @param {number} [ms=0] - The time in milliseconds to convert. - * @returns {number} - The equivalent time in hours. - */ - toHours: function (ms = 0) { - return (ms / 3600000); - }, - - /** - * Converts milliseconds to days. - * - * @param {number} [ms=0] - The time in milliseconds to convert. - * @returns {number} - The equivalent time in days. - */ - toDays: function (ms = 0) { - return (ms / 86400000); - }, - - /** - * Calculates the elapsed time from a given timestamp. - * - * @param {number} [ms=0] - The starting time in milliseconds. - * @returns {number} - The elapsed time in milliseconds. - */ - elapsed: function (ms = 0) { - return (getTickCount() - ms); - } -}; - -/** - * @param {any} val - * @param {PrimitiveType} type - * @returns {boolean} - */ -const isType = function (val, type) { - if (type === "array") { - return Array.isArray(val); - } - return typeof val === type; -}; diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 9fba57dc3..30f48b059 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -31,23 +31,6 @@ ScriptError.prototype = Object.create(Error.prototype); ScriptError.prototype.constructor = ScriptError; - /** - * get all running threads and return them as an array - * @returns {Script[]} - */ - const getThreads = function () { - let threads = []; - let script = getScript(); - - if (script) { - do { - threads.push(copyObj(script)); - } while (script.getNext()); - } - - return threads; - }; - /** * @param {...args} * @returns {Unit[]} @@ -638,7 +621,6 @@ }; return { - getThreads: getThreads, getUnits: getUnits, clickItemAndWait: clickItemAndWait, clickUnitAndWait: clickUnitAndWait, diff --git a/d2bs/kolbot/libs/critical.js b/d2bs/kolbot/libs/critical.js index ba2a999f3..86599aa3c 100644 --- a/d2bs/kolbot/libs/critical.js +++ b/d2bs/kolbot/libs/critical.js @@ -7,4 +7,5 @@ include("json2.js"); // I don't know if this one is actually critical but including it include("polyfill.js"); +include("globals.js"); me.ingame ? include("oog/D2Bot.js") : include("OOG.js"); diff --git a/d2bs/kolbot/libs/globals.js b/d2bs/kolbot/libs/globals.js new file mode 100644 index 000000000..60cc3b846 --- /dev/null +++ b/d2bs/kolbot/libs/globals.js @@ -0,0 +1,299 @@ +/** +* @filename globals.js +* @author theBGuy +* @desc Globals that aren't polyfills just helpful utils that need to be accessable both in-game and out +* +*/ + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~ global d2bs helpers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - sdk - sdk object @see libs/modules/sdk.js + * - includeIfNotIncluded - include file if not already included + * - includeCoreLibs - include all core libs + * - includeSystemLibs - include all system driver files + * - clone - clone object + * - copyObj + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + + +if (!global.hasOwnProperty("sdk") && typeof require !== "undefined") { + Object.defineProperty(global, "sdk", { + value: require("../modules/sdk"), + enumerable: true, + }); +} + +if (!global.hasOwnProperty("includeIfNotIncluded")) { + Object.defineProperty(global, "includeIfNotIncluded", { + /** + * @param {string} file + */ + value: function (file = "") { + if (!isIncluded(file)) { + if (!include(file)) { + console.error("Failed to include " + file); + console.trace(); + return false; + } + } + return true; + }, + }); +} + +if (!global.hasOwnProperty("includeCoreLibs")) { + Object.defineProperty(global, "includeCoreLibs", { + /** + * @description includes all files from libs/core/ folder + * @param {string[]} ignoreFiles + */ + value: function (obj = { exclude: [] }) { + /** @type {string[]} */ + let files = dopen("libs/core/").getFiles(); + if (!files.length) throw new Error("Failed to find my files"); + if (!files.includes("Pather.js")) { + console.warn("Incorrect Files?", files); + // something went wrong? + while (!files.includes("Pather.js")) { + files = dopen("libs/core/").getFiles(); + delay(50); + } + } + // always include util first + includeIfNotIncluded("core/Util.js"); + files + .filter(function (file) { + return file.endsWith(".js") + && !obj.exclude.includes(file) + && !file.match("util.js", "gi"); + }) + .forEach(function (x) { + if (!includeIfNotIncluded("core/" + x)) { + throw new Error("Failed to include core/" + x); + } + }); + return true; + }, + }); +} + +if (!global.hasOwnProperty("includeSystemLibs")) { + Object.defineProperty(global, "includeSystemLibs", { + /** + * @description includes system driver files from libs/systems/ folder + */ + value: function () { + include("systems/automule/automule.js"); + include("systems/crafting/CraftingSystem.js"); + include("systems/gambling/Gambling.js"); + include("systems/torch/TorchSystem.js"); + return true; + }, + }); +} + +if (!global.hasOwnProperty("clone")) { + Object.defineProperty(global, "clone", { + /** + * @param {Date | any[] | object} obj + * @returns {ThisParameterType} deep copy of parameter + */ + value: function (obj) { + let copy; + + // Handle the 3 simple types, and null or undefined + if (null === obj || "object" !== typeof obj) { + return obj; + } + + // Handle Date + if (obj instanceof Date) { + copy = new Date(); + copy.setTime(obj.getTime()); + + return copy; + } + + // Handle Array + if (obj instanceof Array) { + copy = []; + + for (let i = 0; i < obj.length; i += 1) { + copy[i] = clone(obj[i]); + } + + return copy; + } + + // Handle Object + if (obj instanceof Object) { + copy = {}; + + for (let attr in obj) { + if (obj.hasOwnProperty(attr)) { + copy[attr] = clone(obj[attr]); + } + } + + return copy; + } + + throw new Error("Unable to copy obj! Its type isn't supported."); + }, + }); +} + +if (!global.hasOwnProperty("copyObj")) { + Object.defineProperty(global, "copyObj", { + /** + * @param {object} from + * @returns {object} deep copy + */ + value: function (from) { + let obj = {}; + + for (let i in from) { + if (from.hasOwnProperty(i)) { + obj[i] = clone(from[i]); + } + } + + return obj; + }, + }); +} + +/** + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Misc Utils ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + * - Time - Namespace of Helper methods for dealing with time + * - isType - Method to peform simple type checks + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + */ + +const Time = { + /** + * Converts seconds to milliseconds. + * + * @param {number} [seconds=0] - The number of seconds to convert. + * @returns {number} - The equivalent time in milliseconds. + */ + seconds: function (seconds = 0) { + if (!isType(seconds, "number")) return 0; + return (seconds * 1000); + }, + + /** + * Converts minutes to milliseconds. + * + * @param {number} [minutes=0] - The number of minutes to convert. + * @returns {number} - The equivalent time in milliseconds. + */ + minutes: function (minutes = 0) { + if (!isType(minutes, "number")) return 0; + return (minutes * 60000); + }, + + /** + * Formats milliseconds into a "HH:MM:SS" string. + * + * @param {number} [ms=0] - The time in milliseconds to format. + * @returns {string} - The formatted time string. + */ + 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 = function (num) { + return (num < 10 ? "0" + num : num); + }; + + return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); + // return (new Date(ms).toISOString().slice(11, -5)); + }, + + /** + * Converts milliseconds to seconds. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in seconds. + */ + toSeconds: function (ms = 0) { + return (ms / 1000); + }, + + /** + * Converts milliseconds to minutes. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in minutes. + */ + toMinutes: function (ms = 0) { + return (ms / 60000); + }, + + /** + * Converts milliseconds to hours. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in hours. + */ + toHours: function (ms = 0) { + return (ms / 3600000); + }, + + /** + * Converts milliseconds to days. + * + * @param {number} [ms=0] - The time in milliseconds to convert. + * @returns {number} - The equivalent time in days. + */ + toDays: function (ms = 0) { + return (ms / 86400000); + }, + + /** + * Calculates the elapsed time from a given timestamp. + * + * @param {number} [ms=0] - The starting time in milliseconds. + * @returns {number} - The elapsed time in milliseconds. + */ + elapsed: function (ms = 0) { + return (getTickCount() - ms); + } +}; + +/** + * @param {any} val + * @param {PrimitiveType} type + * @returns {boolean} + */ +const isType = function (val, type) { + if (type === "array") { + return Array.isArray(val); + } + return typeof val === type; +}; + +/** + * get all running threads and return them as an array + * @returns {Script[]} + */ +const getThreads = function () { + let threads = []; + let script = getScript(); + + if (script) { + do { + threads.push(copyObj(script)); + } while (script.getNext()); + } + + return threads; +}; From d3d640cafbdf8e5f56a7896f2e78a57553d040df Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:22:24 -0400 Subject: [PATCH 449/758] Track what our entry script is --- d2bs/kolbot/libs/core/Misc.js | 5 ++--- d2bs/kolbot/libs/oog/D2Bot.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index c53d74dea..eb1902749 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -151,7 +151,7 @@ const Misc = (function () { }, /** - * Get number of players within getUnit distance + * Get number of players within getUnit distance - excludes self * @returns {number} */ getNearbyPlayerCount: function () { @@ -201,7 +201,6 @@ const Misc = (function () { if (party.partyid !== sdk.party.NoParty && party.partyid === myPartyId && party.name !== me.name) { - console.log(party.name); count += 1; } } while (party.getNext()); @@ -1046,7 +1045,7 @@ const Misc = (function () { * @returns {boolean} */ useMenu: function (id) { - //print("useMenu " + getLocaleString(id)); + //console.log("useMenu " + getLocaleString(id)); let npc; diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index f2b2dbfa0..7413be83c 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -22,12 +22,19 @@ includeIfNotIncluded("oog/DataFile.js"); const D2Bot = { handle: 0, + _entry: "", init: function () { let handle = DataFile.getStats().handle; if (handle) { D2Bot.handle = handle; + + let tmp = getThreads().find((thread) => thread.name.endsWith(".dbj")); + if (tmp) { + // Remove .dbj + D2Bot._entry = tmp.name.split(".")[0]; + } } return D2Bot.handle; @@ -38,8 +45,8 @@ includeIfNotIncluded("oog/DataFile.js"); }, printToConsole: function (msg, color, tooltip, trigger) { - let printObj = { - msg: ((new Date().dateStamp() + " ") + msg), + const printObj = { + msg: (("(" + D2Bot._entry + ") ") + (new Date().dateStamp() + " ") + msg), color: color || 0, tooltip: tooltip || "", trigger: trigger || "" From 82b1fff59a509d231ce745e6249e3298dcb3a657 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Oct 2024 18:23:51 -0400 Subject: [PATCH 450/758] Remove password check from friend list channel join - Added lobby news and warning controls --- d2bs/kolbot/D2BotChannel.dbj | 4 +++- d2bs/kolbot/libs/modules/Control.js | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 223717d5a..40ecfabf1 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -257,7 +257,9 @@ const locationAction = (function () { // check if the game is private if (_private && gInfo.password) { // we can't join if the game is private and we don't have a password - if (!gInfo.password) continue; + // ugh why would bnet not actually use (private) correctly + /** @todo Figure out how we can check if game is private or not */ + // if (!gInfo.password) continue; joinInfo.gamePass = gInfo.password; } joinInfo.gameName = gameName; diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index 1bd19132b..3755c4002 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -228,6 +228,8 @@ Control.LobbyLadder = new Control(sdk.controls.Button, 614, 490, 80, 20); Control.LobbyHelp = new Control(sdk.controls.Button, 146, 480, 120, 20); Control.LobbyQuit = new Control(sdk.controls.Button, 693, 490, 80, 20); + Control.LobbyNews = new Control(sdk.controls.LabelBox, 28, 410, 354, 298); + Control.LobbyWarning = new Control(sdk.controls.LabelBox, 447, 398, 290, 269); } // Ladder menu controls From b3d67fc3f84bd3e99eb8e0616c09682cd547c11b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Oct 2024 18:30:26 -0400 Subject: [PATCH 451/758] Update AutoRush.js - Minor cleanups, skip checking `playersIn` if they aren't in our party --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 64 ++++++++++++++----- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 97a91a710..6d961b6ad 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -12,11 +12,20 @@ RushConfig, } = require("./RushConfig"); + /** + * @param {string} msg + * @param {boolean} sayMsg + */ const log = function (msg = "", sayMsg = true) { console.log(msg); sayMsg && say(msg); }; + /** + * @param {number} area + * @param {string} [nick] + * @returns {boolean} + */ const playerIn = function (area, nick) { !area && (area = me.area); @@ -38,12 +47,18 @@ const bumperLvlReq = function () { return [20, 40, 60][me.diff]; }; + + /** + * @param {string} nick + * @returns {boolean} + */ const bumperCheck = function (nick) { return nick ? Misc.findPlayer(nick).level >= bumperLvlReq() : Misc.checkPartyLevel(bumperLvlReq()); }; + /** @param {Act} act */ const playersInAct = function (act) { !act && (act = me.act); @@ -51,7 +66,9 @@ let party = getParty(); if (party) { + const myPartyId = party.partyid; do { + if (party.partyid !== myPartyId) continue; if (party.name !== me.name && party.area !== area) { return false; } @@ -60,6 +77,7 @@ return true; }; + const cain = function () { log("starting cain"); Town.doChores(); @@ -81,7 +99,7 @@ if (tree.mode) { break; } - Attack.securePosition(me.x, me.y, 20, 1000); + Attack.securePosition(me.x, me.y, 25, 1000); } Pather.usePortal(1) || Town.goToTown(); @@ -90,7 +108,8 @@ Pather.moveToPresetMonster(sdk.areas.StonyField, sdk.monsters.preset.Rakanishu, { offX: 10, offY: 10, pop: true }); - const StoneAlpha = Game.getObject(sdk.quest.chest.StoneAlpha); + const alphaPreset = Game.getPresetObject(sdk.areas.StonyField, sdk.quest.chest.StoneAlpha); + const StoneAlpha = alphaPreset.realCoords(); Attack.securePosition(StoneAlpha.x, StoneAlpha.y, 40, 3000, true); StoneAlpha.distance > 5 && Pather.moveToUnit(StoneAlpha); Pather.makePortal(); @@ -99,7 +118,7 @@ tick = getTickCount(); while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.usePortal(sdk.areas.Tristram)) { + if (Pather.getPortal(sdk.areas.Tristram) && Pather.usePortal(sdk.areas.Tristram)) { break; } Attack.securePosition(StoneAlpha.x, StoneAlpha.y, 35, 1000); @@ -131,18 +150,20 @@ return true; }; + /** @param {string} [nick] */ const andariel = function (nick) { log("starting andariel"); Town.doChores(); Pather.useWaypoint(sdk.areas.CatacombsLvl2, true) && Precast.doPrecast(true); + const safeNode = new PathNode(22582, 9612); if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true) - || !Pather.moveTo(22582, 9612)) { + || !Pather.move(safeNode)) { throw new Error("andy failed"); } - Attack.securePosition(22582, 9612, 40, 3000, true); + Attack.securePosition(safeNode.x, safeNode.y, 40, 3000, true); Pather.makePortal(); log(AutoRush.playersIn); @@ -150,7 +171,7 @@ if (playerIn(me.area, nick)) { return true; } - Pather.moveTo(22582, 9612); + Pather.move(safeNode); return false; }, AutoRush.playerWaitTimeout, 1000)) { log("timed out"); @@ -159,7 +180,7 @@ Attack.kill(sdk.monsters.Andariel); log(AutoRush.playersOut); - Pather.moveTo(22582, 9612); + Pather.move(safeNode); if (AutoRush.rushMode !== RushModes.chanter) { while (playerIn(me.area, nick)) { @@ -226,7 +247,7 @@ log(nick + " you are not eligible for smith. You need to be at least level 8"); return false; - } + } Town.doChores(); Pather.useWaypoint(sdk.areas.OuterCloister, true) && Precast.doPrecast(true); @@ -250,6 +271,7 @@ Pather.usePortal(null, me.name); return true; }; + /** @param {string} [nick] */ const radament = function (nick) { log("starting radament"); @@ -781,17 +803,21 @@ Town.doChores(); Pather.useWaypoint(sdk.areas.Travincal, true) && Precast.doPrecast(true); - /** @type {PathNode} */ - const wpCoords = { - x: me.x, - y: me.y - }; - const portalSpot = { - x: wpCoords.x + 23, - y: wpCoords.y - 102 - }; + const wpCoords = new PathNode(me.x, me.y); + const portalSpot = new PathNode(wpCoords.x + 23, wpCoords.y - 102); + + [ + new PathNode(4742, 2179), + new PathNode(4738, 2133), + new PathNode(4768, 2150), + new PathNode(4762, 2106), + ].forEach((node) => { + Pather.move(node); + Attack.securePosition(node.x, node.y, 25, 2500); + }); Pather.move(portalSpot); + console.debug("Mob Count? " + me.mobCount({ range: 40 })); Attack.securePosition(portalSpot.x, portalSpot.y, 40, 4000); Pather.makePortal(); log(AutoRush.playersIn); @@ -1252,6 +1278,10 @@ Pather.moveTo(15134, 5923); Attack.kill(sdk.monsters.Baal); Pickit.pickItems(); + // Move back to rushee + Pather.moveTo(15213, 5908); + Pather.makePortal(); + log(AutoRush.playersOut); } return true; From 82f999354d67539c587e5d3a8a72016631e8460c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 28 Oct 2024 01:48:43 -0400 Subject: [PATCH 452/758] Update AutoRush.js - while loops with potential to go one forever are bad, replaced with polls for players changing act --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 6d961b6ad..73049a6a3 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -188,11 +188,17 @@ } Pather.usePortal(null, me.name); - log("a2"); Town.goToTown(2); + for (let i = 0; i < 3; i++) { + log("a2"); + + let playersMoved = Misc.poll(function () { + return !playersInAct(2); + }, Time.seconds(30), Time.seconds(1)); - while (!playersInAct(2)) { - delay(250); + if (playersMoved) { + break; + } } } @@ -642,13 +648,19 @@ Pather.moveTo(10022, 5047); if (AutoRush.rushMode !== RushModes.chanter) { - log("a3"); - Town.goToTown(3); - Town.doChores(); + for (let i = 0; i < 3; i++) { + log("a3"); + + let playersMoved = Misc.poll(function () { + return !playersInAct(3); + }, Time.seconds(30), Time.seconds(1)); - while (!playersInAct(3)) { - delay(250); + if (playersMoved) { + break; + } } + Town.goToTown(3); + Town.doChores(); } return true; @@ -891,10 +903,19 @@ delay(250); } - log("a4"); + if (AutoRush.rushMode !== RushModes.chanter) { + // allow 3 attempts + for (let i = 0; i < 3; i++) { + log("a4"); + + let playersMoved = Misc.poll(function () { + return !playersInAct(4); + }, Time.seconds(30), Time.seconds(1)); - while (!playersInAct(4)) { - delay(250); + if (playersMoved) { + break; + } + } } delay(2000); @@ -1029,10 +1050,17 @@ log(AutoRush.playersOut); if (me.expansion && AutoRush.rushMode !== RushModes.chanter) { - log("a5"); + // allow 3 attempts + for (let i = 0; i < 3; i++) { + log("a5"); + + let playersMoved = Misc.poll(function () { + return !playersInAct(5); + }, Time.seconds(30), Time.seconds(1)); - while (!playersInAct(5)) { - delay(250); + if (playersMoved) { + break; + } } } @@ -1206,7 +1234,6 @@ delay(500); quit(); } - wpsToGive.remove(sdk.areas.WorldstoneLvl2); return false; } @@ -1217,7 +1244,6 @@ delay(500); quit(); } - wpsToGive.remove(sdk.areas.WorldstoneLvl2); return false; } From 2bd964cf6ca89e15a8d43c7e75c73288c3711edc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 29 Oct 2024 02:18:53 -0400 Subject: [PATCH 453/758] Update README.md --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index fb209736e..16ac31560 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,12 @@ * D2Bot# - manager (C#) * kolbot - script library (JS) -* use the mainline (trunk) branch +If you want to contribute to kolbot code, make sure you run `npm run lint` for final polish. -If you want to contribute to kolbot code, make sure you use [ESLint options for kolbot code](https://gist.githubusercontent.com/Nishimura-Katsuo/2d6866666c7acf10047c486a15a7fe60/raw/99ef9c2995929c492ef856772ff346e0f19709cd/.eslintrc.js) or [JSLint options for kolbot code](https://gist.githubusercontent.com/noah-/d917342e52281d54c404e0b2c18b0c6e/raw/fbade95e38b103d2654b90d85ef62a51c4295153/jslint.config) for final polish. If you want to contribute to d2bs/d2bot#, come to irc.synirc.net/d2bs and ask around. +[**Live Docs**](https://bhdocs.github.io/) + [**Documentation Repo**](https://github.com/blizzhackers/documentation#diablo-2-botting-system-d2bs) ## Install dependencies - do this first! @@ -31,17 +32,17 @@ If you want to contribute to d2bs/d2bot#, come to irc.synirc.net/d2bs and ask ar - [Microsoft .NET Framework 4.0 (or higher)](https://dotnet.microsoft.com/download/dotnet-framework) ## Getting Started -- [download kolbot](https://github.com/blizzhackers/documentation/blob/master/d2bot/Download.md#download) -- [d2bot manager setup](https://github.com/blizzhackers/documentation/blob/master/d2bot/ManagerSetup.md/#manager-setup) -- [IDE-Setup](IDES.md/#code-editors-ides): How to set up your IDE for syntax highlighting -- [FAQ](https://github.com/blizzhackers/documentation/blob/master/kolbot/FAQ.md/#faq) +- [download kolbot](https://github.com/blizzhackers/documentation/blob/restructure/d2bot/Download.md#download) +- [d2bot manager setup](https://github.com/blizzhackers/documentation/blob/restructure/d2bot/ManagerSetup.md/#manager-setup) +- [IDE-Setup](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/IDES.md/#code-editors-ides): +- [FAQ](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/FAQ.md/#faq) ## Guides -- [manual playing](https://github.com/blizzhackers/documentation/blob/master/kolbot/ManualPlay.md/#manual-playing) -- [multi botting](https://github.com/blizzhackers/documentation/blob/master/kolbot/MultiBotting.md/#multi-botting) +- [manual playing](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/ManualPlay.md/#manual-playing) +- [multi botting](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/MultiBotting.md/#multi-botting) - [kolbot-SoloPlay](https://github.com/blizzhackers/kolbot-SoloPlay) -- [character config](https://github.com/blizzhackers/documentation/blob/master/kolbot/CharacterConfig.md/#character-configuration) -- [TCP/IP Games](https://github.com/blizzhackers/documentation/blob/master/kolbot/TCP-IP%20games.md#tcpip-games) +- [character config](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/CharacterConfig.md/#character-configuration) +- [TCP/IP Games](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/TCP-IP%20games.md#tcpip-games) ## LimeDrop web based item manager and dropper From 925f513522a2fd2d448839ee0f176b3b7450a127 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 Nov 2024 20:23:02 -0500 Subject: [PATCH 454/758] Add `Config.GetEssences.RunDuriel` option - Allows running Duriel for extra chance at EoS --- d2bs/kolbot/libs/config/Amazon.js | 6 ++++-- d2bs/kolbot/libs/config/Assassin.js | 6 ++++-- d2bs/kolbot/libs/config/Barbarian.js | 6 ++++-- d2bs/kolbot/libs/config/Druid.js | 6 ++++-- d2bs/kolbot/libs/config/Necromancer.js | 6 ++++-- d2bs/kolbot/libs/config/Paladin.js | 6 ++++-- d2bs/kolbot/libs/config/_BaseConfigFile.js | 5 +++-- d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/libs/scripts/GetEssences.js | 8 ++++++++ d2bs/kolbot/sdk/types/Config.d.ts | 1 + 10 files changed, 37 insertions(+), 14 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 218c068f1..e772e71a4 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -352,8 +352,10 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. - Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.RunDuriel = false; // Run duriel for extra chance at TwistedEssenceofSuffering + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 6e2701cc2..958b5cf0d 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -352,8 +352,10 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. - Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.RunDuriel = false; // Run duriel for extra chance at TwistedEssenceofSuffering + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 68b475e07..62de3fe8b 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -352,8 +352,10 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. - Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.RunDuriel = false; // Run duriel for extra chance at TwistedEssenceofSuffering + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 985a9beb8..47eeeff64 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -352,8 +352,10 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. - Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.RunDuriel = false; // Run duriel for extra chance at TwistedEssenceofSuffering + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 3da2cc53c..45749566e 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -352,8 +352,10 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. - Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.RunDuriel = false; // Run duriel for extra chance at TwistedEssenceofSuffering + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 3e0e69818..8885ecbc1 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -352,8 +352,10 @@ function LoadConfig () { ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. - Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.RunDuriel = false; // Run duriel for extra chance at TwistedEssenceofSuffering + Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index e223ea124..3b80c5e2e 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -333,9 +333,10 @@ ]; Scripts.ClearAnyArea = false; // Clear any area. Uses Config.ClearType to determine which type of monsters to kill. Config.ClearAnyArea.AreaList = []; // List of area ids to clear. See sdk/txt/areas.txt - Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Scripts.GetEssences = false; // Hunt for Essences. Useful for cubing tokens without running all the bosses. + Config.GetEssences.RunDuriel = false; // Run duriel for extra chance at TwistedEssenceofSuffering Config.GetEssences.MoatMeph = true; // Lure Meph and attempt killing from other side of moat - Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path + Config.GetEssences.FastDiablo = true; // Runs diablo seals without clearing path Scripts.GemHunter = false; // Hunt for Gem Shrines. add the upgraded gems to your pickit. Upgraded version of gems will be auto-picked // List of are ids to hunt in. See sdk/txt/areas.txt or use sdk.areas.AreaName see -> \kolbot\libs\modules\sdk.js Config.GemHunter.AreaList = [ diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 1133ad24e..961353269 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -736,6 +736,7 @@ let Config = { GetEssences: { MoatMeph: false, FastDiablo: false, + RunDuriel: false, }, GemHunter: { AreaList: [], diff --git a/d2bs/kolbot/libs/scripts/GetEssences.js b/d2bs/kolbot/libs/scripts/GetEssences.js index 343742e8b..002670f7f 100644 --- a/d2bs/kolbot/libs/scripts/GetEssences.js +++ b/d2bs/kolbot/libs/scripts/GetEssences.js @@ -24,6 +24,14 @@ const GetEssences = new Runnable( } } + if (Config.GetEssences.RunDuriel && count(sdk.quest.item.TwistedEssenceofSuffering) < 1) { + try { + Loader.runScript("Duriel"); + } catch (e) { + console.error("ÿc1Duriel failed :: ", e); + } + } + if (count(sdk.quest.item.ChargedEssenceofHatred) < 1) { try { Config.Mephisto.MoatTrick = Config.GetEssences.MoatMeph; diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index ebe808f45..a9fbd50fc 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -521,6 +521,7 @@ declare global { GetEssences: { MoatMeph: boolean; FastDiablo: boolean; + RunDuriel: boolean; }; AutoSkill: { Enabled: boolean; From d65929b6850dbacae7c738874f6e3c750c9e8665 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 19 Dec 2024 20:05:41 -0500 Subject: [PATCH 455/758] Update ControlBot.js - Use chantlist to check for parent during autochant --- d2bs/kolbot/libs/scripts/ControlBot.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 1d85ac9de..a1da3c739 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -340,7 +340,9 @@ const ControlBot = new Runnable( if (unit) { do { // merc or any other owned unit - if (unit.getParent() && unit.getParent().name === nick) { + let parent = unit.getParent(); + if (!parent) continue; + if (parent.name === nick) { Packet.enchant(unit); delay(500); } @@ -455,7 +457,7 @@ const ControlBot = new Runnable( if (unit) { do { if (unit.getParent() - && chanted.includes(unit.getParent().name) + && chantList.has(unit.getParent().name) && !unit.getState(sdk.states.Enchant) && unit.distance <= 40) { Packet.enchant(unit); From 2eba73f5be2ba997df790e0897843ee261318d8c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:20:56 -0500 Subject: [PATCH 456/758] Fix D2BotPubJoin utilizing `AttemptNextGame` - Always add the next game to the doneList when we get in game --- d2bs/kolbot/D2BotPubJoin.dbj | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index beec61eed..7a89f5091 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -147,7 +147,7 @@ const locationAction = (function () { return; } - print("updating runs"); + console.log("updating runs"); D2Bot.updateRuns(); lastGameTick = getTickCount(); @@ -198,7 +198,7 @@ const locationAction = (function () { try { joinGame(gameToJoin, ""); } catch (joinErr) { - print(joinErr); + console.log(joinErr); } retry++; @@ -209,6 +209,8 @@ const locationAction = (function () { if (getLocation() === sdk.game.locations.GameDoesNotExist) { Starter.LocationEvents.openJoinGameWindow(); + } else { + return; } } } @@ -257,7 +259,7 @@ const locationAction = (function () { try { joinGame(gameToJoin, ""); } catch (joinErr) { - print(joinErr); + console.log(joinErr); } me.blockMouse = false; @@ -345,7 +347,7 @@ function main () { while (!Object.keys(Starter.profileInfo).length) { D2Bot.getProfile(); - print("Getting Profile"); + console.log("Getting Profile"); delay(500); } @@ -359,7 +361,7 @@ function main () { if (!Starter.inGame) { Starter.gameStart = getTickCount(); - print("Updating Status"); + console.log("Updating Status"); Starter.lastGameStatus = "ingame"; Starter.inGame = true; @@ -369,6 +371,18 @@ function main () { DataFile.updateStats("runs", Starter.gameCount); DataFile.updateStats("ingameTick"); Starter.setNextGame(me.gamename); + + /** @type {string[]} */ + let doneGames = []; + if (FileTools.exists("logs/doneGames.json")) { + doneGames = JSON.parse(FileAction.read("logs/doneGames.json")); + } + + doneGames.length >= 20 && doneGames.shift(); + if (!doneGames.includes(me.gamename)) { + doneGames.push(me.gamename); + } + FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); } D2Bot.updateStatus( From 4bdde93e4bd6e584324bc525180cc1b2f2eef6b2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:24:27 -0500 Subject: [PATCH 457/758] Update Pickit.js --- d2bs/kolbot/libs/core/Pickit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 773c0c17c..f24b07017 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -187,7 +187,7 @@ const Pickit = { ]); for (let column of Config.BeltColumn) { - if (unit.code.includes(column)) { + if (unit.code && unit.code.includes(column)) { needPots += Pickit.beltSize; } } From 3a896abaa79113b5d87be263283d51481f9d0c43 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 21 Dec 2024 19:12:55 -0500 Subject: [PATCH 458/758] Fix D2BotMuleLog not updating `currAcc` property in generated `MuleLog.json` file - Update currAcc on successful log in --- d2bs/kolbot/D2BotMuleLog.dbj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 80a056fed..61d19001c 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -20,6 +20,7 @@ Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/ba // only libs we should need here as te rest of the actions are performed from default.dbj thread include("systems/dropper/DropperSetup.js"); include("systems/mulelogger/MuleLogger.js"); +include("systems/automule/AutoMule.js"); if (!FileTools.exists("data/" + me.profile + ".json")) { DataFile.create(); @@ -40,7 +41,9 @@ const parseInfo = function () { }; const locationAction = (function () { let currAcc; + /** @type {string[]} */ let charList = []; + /** @type {{ currAcc: string, currChar: string }} */ let obj = {}; let ftjRetry = 0; @@ -141,6 +144,7 @@ const locationAction = (function () { } if (FileTools.exists("logs/MuleLog.json")) { + /** @type {{ currAcc: string, currChar: string }} */ obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); if (obj.currAcc) { @@ -165,6 +169,7 @@ const locationAction = (function () { MuleLogger.save(md5(currAcc[2].toLowerCase() + currAcc[0].toLowerCase()), currAcc[1]); if (ControlAction.loginAccount({ account: currAcc[0], password: currAcc[1], realm: currAcc[2] })) { + FileTools.writeText("logs/MuleLog.json", JSON.stringify(obj)); accounts.shift(); // remove current account from the list } } @@ -181,10 +186,12 @@ const locationAction = (function () { if (!charList.length && Controls.BottomLeftExit.click()) { return; } - + charList[0] === "all" && (charList = ControlAction.getCharacters()); - + charList[0] === "first" && (charList = ControlAction.getCharacters().slice(0, 1)); + if (FileTools.exists("logs/MuleLog.json")) { + /** @type {{ currAcc: string, currChar: string }} */ obj = JSON.parse(FileTools.readText("logs/MuleLog.json")); if (obj.currChar) { From cdc164fac6780b913120890130c136d421bd447a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 25 Dec 2024 17:51:13 -0500 Subject: [PATCH 459/758] Update ControlBot.js - Add stop command to halt wp giving --- d2bs/kolbot/libs/scripts/ControlBot.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index a1da3c739..cd86f866a 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -639,10 +639,17 @@ const ControlBot = new Runnable( */ const giveWps = function (nick) { let next = false; + let stop = false; + /** + * @param {string} who + * @param {string} msg + */ const nextWatcher = function (who, msg) { if (who !== nick) return; if (msg === "next") { next = true; + } else if (msg === "stop") { + stop = true; } }; @@ -671,7 +678,7 @@ const ControlBot = new Runnable( addEventListener("chatmsg", nextWatcher); for (let wp of wps.get(act)) { - if (checkHostiles()) { + if (stop || checkHostiles()) { break; } From 3bb49e47757150b50e5453ac20ba8934e4cf3f5a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 00:14:50 -0500 Subject: [PATCH 460/758] Fix typo in `SimpleParty.js` --- d2bs/kolbot/libs/modules/workers/SimpleParty.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index 5fa99d36f..6b1c373db 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -145,7 +145,7 @@ ShitList.add(party.name); } - if (player.partyflag === sdk.party.flag.Cancel) { + if (party.partyflag === sdk.party.flag.Cancel) { clickParty(party, BUTTON_INVITE_ACCEPT_CANCEL); // cancel invitation } From d6e3bde52c072b9e66f4ec554b7a1dc72627e707 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 00:17:55 -0500 Subject: [PATCH 461/758] Update RuneData.js - Fixed ladderRws list --- d2bs/kolbot/libs/core/GameData/RuneData.js | 26 +++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/RuneData.js b/d2bs/kolbot/libs/core/GameData/RuneData.js index fdda86ca8..503c9714d 100644 --- a/d2bs/kolbot/libs/core/GameData/RuneData.js +++ b/d2bs/kolbot/libs/core/GameData/RuneData.js @@ -20,9 +20,29 @@ sdk.items.type.Polearm, sdk.items.type.Scepter, sdk.items.type.HandtoHand ]; const ladderRws = [ - "Brand", "Death", "Destruction", "Dragon", "Edge", "Fortitude", "Grief", - "Ice", "Infinity", "Insight", "LastWish", "Lawbringer", "Oath", "Obedience", - "Phoenix", "Pride", "Rift", "Spirit", "VoiceofReason", "White", + "Brand", + "Death", + "Destruction", + "Dream", + "Dragon", + "Edge", + "Faith", + "Fortitude", + "Grief", + "Harmony", + "Ice", + "Infinity", + "Insight", + "LastWish", + "Lawbringer", + "Oath", + "Obedience", + "Phoenix", + "Pride", + "Rift", + "Spirit", + "VoiceofReason", + "Wrath", ]; function getItemType (iType) { From 3c80e1203d5232f8ab98a3a00f1629f991dcec17 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 00:40:23 -0500 Subject: [PATCH 462/758] Update SimpleParty.js - Missed another typo --- d2bs/kolbot/libs/modules/workers/SimpleParty.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index 6b1c373db..a5dd967bd 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -195,10 +195,10 @@ // Add new hostile players to temp shitlist, leader should have Config.ShitList set to true to update the permanent list. if (getPlayerFlag(me.gid, party.gid, sdk.player.flag.Hostile) && shitList.includes(party.name)) { - shitList.push(player.name); + shitList.push(party.name); } - if (shitList.includes(player.name) + if (shitList.includes(party.name) && myPartyId !== NO_PARTY && party.partyid === myPartyId) { console.log("Unpartying shitlisted player: " + party.name); From 30168fd100f6d5ea6ce9d527ded5866a5ba35ab5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:24:41 -0500 Subject: [PATCH 463/758] Formatted + updated AntiHostile to fix infinite wait and usage with SimpleParty - Changed + cleaned up the structure, removed usage of `this` instances - Added jsdoc comments - Added a broadcast event for updated default since it's paused during the duration of the hostileEvent - Added a check in findHostiles to remove players who were once hostile but no longer are but are still in game - Add a call into the town wait while loop to findHostiles in case the players aren't hostile anymore but are still in game so we don't wait around forever --- d2bs/kolbot/threads/AntiHostile.js | 167 +++++++++++++++++++---------- 1 file changed, 109 insertions(+), 58 deletions(-) diff --git a/d2bs/kolbot/threads/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js index ae14c6ce9..136c3e863 100644 --- a/d2bs/kolbot/threads/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -1,12 +1,11 @@ -/* eslint-disable max-len */ /** -* @filename AntiHostile.js -* @author kolton -* @desc handle hostile threats -* -*/ + * @filename AntiHostile.js + * @author kolton + * @desc handle hostile threats + * + */ js_strict(true); -include("critical.js"); // required +include("critical.js"); // required // globals needed for core gameplay includeCoreLibs(); @@ -18,43 +17,53 @@ include("systems/gameaction/GameAction.js"); include("oog/ShitList.js"); -function main () { +function main() { // Variables and functions let player, attackCount, prevPos, check, missile, outside; - let charClass = ["Amazon", "Sorceress", "Necromancer", "Paladin", "Barbarian", "Druid", "Assassin"]; let hostiles = []; - // AntiHostile gets game event info from ToolsThread - this.scriptEvent = function (msg) { + /** + * Handles game events for AntiHostile. + * @const + * @param {string} msg - The message received from the game event. + */ + const scriptEvent = function (msg) { if (!msg || typeof msg !== "string") return; - + switch (msg.split(" ")[0]) { case "remove": // Remove a hostile player that left the game if (hostiles.indexOf(msg.split(" ")[1]) > -1) { hostiles.splice(hostiles.indexOf(msg.split(" ")[1]), 1); } - break; case "mugshot": // Take a screenshot and log the kill D2Bot.printToConsole(msg.split(" ")[1] + " has been neutralized.", sdk.colors.D2Bot.Blue); hideConsole(); delay(500); takeScreenshot(); - break; } }; - // Find all hostile players and add their names to the 'hostiles' list - this.findHostiles = function () { + /** + * Find all hostile players and add their names to the 'hostiles' list + * @returns {boolean} + */ + const findHostiles = function () { let party = getParty(); if (party) { do { - if (party.name !== me.name - && getPlayerFlag(me.gid, party.gid, 8) - && hostiles.indexOf(party.name) === -1) { - D2Bot.printToConsole(party.name + " (Level " + party.level + " " + charClass[party.classid] + ")" + " has declared hostility.", sdk.colors.D2Bot.Orange); + if (hostiles.includes(party.name) && !getPlayerFlag(me.gid, party.gid, 8)) { + hostiles.splice(hostiles.indexOf(party.name), 1); + + continue; + } + if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8) && hostiles.indexOf(party.name) === -1) { + D2Bot.printToConsole( + party.name + " (Level " + party.level + " " + sdk.player.class.nameOf(party.classid) + ")" + " has declared hostility.", + sdk.colors.D2Bot.Orange + ); hostiles.push(party.name); if (Config.ShitList) { ShitList.add(party.name); @@ -66,8 +75,10 @@ function main () { return true; }; - // Pause default so actions don't conflict - this.pause = function () { + /** + * Pause default so actions don't conflict + */ + const pause = function () { let script = getScript("default.dbj"); if (script && script.running) { @@ -76,18 +87,24 @@ function main () { } }; - // Resume default - this.resume = function () { + /** + * Resume default + */ + const resume = function () { let script = getScript("default.dbj"); if (script && !script.running) { console.log("ÿc2Resuming."); script.resume(); + scriptBroadcast("hostileEventEnded"); } }; - // Find hostile player Units - this.findPlayer = function () { + /** + * Find hostile player Units + * @returns {Player | boolean} + */ + const findPlayer = function () { for (let i = 0; i < hostiles.length; i += 1) { let player = Game.getPlayer(hostiles[i]); @@ -103,8 +120,14 @@ function main () { return false; }; - // Find a missile type - this.findMissile = function (owner, id, range) { + /** + * Find a missile type + * @param {Player} owner + * @param {number} id + * @param {number} range + * @returns {Missile | boolean} + */ + const findMissile = function (owner, id, range) { range === undefined && (range = 999); let missile = Game.getMissile(id); @@ -119,7 +142,11 @@ function main () { return false; }; - this.checkSummons = function (player) { + /** + * @param {Player} player + * @returns {boolean} + */ + const checkSummons = function (player) { if (!player) return false; let name = player.name; let unit = Game.getMonster(); @@ -127,7 +154,11 @@ function main () { if (unit) { do { // Revives and spirit wolves - if (unit.getParent() && unit.getParent().name === name && (unit.getState(sdk.states.Revive) || unit.classid === sdk.monsters.Wolf2)) { + if ( + unit.getParent() + && unit.getParent().name === name + && (unit.getState(sdk.states.Revive) || unit.classid === sdk.monsters.Wolf2) + ) { return true; } } while (unit.getNext()); @@ -146,21 +177,28 @@ function main () { Skill.usePvpRange = true; // Attack sequence adjustments - this only affects the AntiHostile thread - if (Skill.canUse(sdk.skills.MindBlast) - && [sdk.skills.FireBlast, sdk.skills.ShockWeb].includes(Config.AttackSkill[1])) { + if ( + Skill.canUse(sdk.skills.MindBlast) + && [sdk.skills.FireBlast, sdk.skills.ShockWeb].includes(Config.AttackSkill[1]) + ) { Config.AttackSkill[1] = sdk.skills.MindBlast; ClassAttack.trapRange = 40; } - // A simple but fast player dodge function - this.moveAway = function (unit, range) { - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); + /** + * A simple but fast player dodge function + * @param {Player | Monster} unit + * @param {number} range + * @returns {boolean} + */ + const moveAway = function (unit, range) { + let angle = Math.round((Math.atan2(me.y - unit.y, me.x - unit.x) * 180) / Math.PI); let angles = [0, 45, -45, 90, -90, 135, -135, 180]; for (let i = 0; i < angles.length; i += 1) { // Avoid the position where the player actually tries to move to - let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); // unit.targetx - let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); // unit.targety + let coordx = Math.round(Math.cos(((angle + angles[i]) * Math.PI) / 180) * range + unit.x); // unit.targetx + let coordy = Math.round(Math.sin(((angle + angles[i]) * Math.PI) / 180) * range + unit.y); // unit.targety if (Attack.validSpot(coordx, coordy)) { return Pather.moveTo(coordx, coordy); @@ -170,19 +208,19 @@ function main () { return false; }; - addEventListener("scriptmsg", this.scriptEvent); + addEventListener("scriptmsg", scriptEvent); console.log("ÿc2Anti-Hostile thread loaded."); // Main Loop while (true) { if (me.gameReady) { // Scan for hostiles - this.findHostiles(); + findHostiles(); if (hostiles.length > 0 && (Config.HostileAction === 0 || (Config.HostileAction === 1 && me.inTown))) { if (Config.TownOnHostile) { console.log("ÿc1Hostility detected, going to town."); - this.pause(); + pause(); if (!me.inTown) { outside = true; @@ -196,6 +234,7 @@ function main () { } while (hostiles.length > 0) { + findHostiles(); delay(500); } @@ -204,7 +243,7 @@ function main () { Pather.usePortal(null, me.name); } - this.resume(); + resume(); } else { scriptBroadcast("quit"); } @@ -219,10 +258,10 @@ function main () { switch (me.classid) { case sdk.player.class.Sorceress: prevPos = { x: me.x, y: me.y }; - this.pause(); + pause(); Pather.moveTo(15103, 5247); - while (!this.findPlayer() && hostiles.length > 0) { + while (!findPlayer() && hostiles.length > 0) { if (!me.skillDelay) { Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099, 5237); } else { @@ -244,10 +283,10 @@ function main () { } prevPos = { x: me.x, y: me.y }; - this.pause(); + pause(); Pather.moveTo(15103, 5247); - while (!this.findPlayer() && hostiles.length > 0) { + while (!findPlayer() && hostiles.length > 0) { // Tornado path is a function of target x. Slight randomization will make sure it can't always miss Skill.cast(Config.AttackSkill[1], Skill.getHand(Config.AttackSkill[1]), 15099 + rand(-2, 2), 5237); } @@ -255,10 +294,10 @@ function main () { break; case sdk.player.class.Assassin: prevPos = { x: me.x, y: me.y }; - this.pause(); + pause(); Pather.moveTo(15103, 5247); - while (!this.findPlayer() && hostiles.length > 0) { + while (!findPlayer() && hostiles.length > 0) { if (Config.UseTraps) { check = ClassAttack.checkTraps({ x: 15099, y: 5242, classid: 544 }); @@ -281,20 +320,20 @@ function main () { // Player left, return to old position if (!hostiles.length && prevPos) { Pather.moveTo(prevPos.x, prevPos.y); - this.resume(); + resume(); // Reset position prevPos = false; } - player = this.findPlayer(); + player = findPlayer(); if (player) { // Mode 1 - Quit if hostile player is nearby if (Config.HostileAction === 1) { if (Config.TownOnHostile) { console.log("ÿc1Hostile player nearby, going to town."); - this.pause(); + pause(); if (!me.inTown) { outside = true; @@ -316,7 +355,7 @@ function main () { Pather.usePortal(null, me.name); } - this.resume(); + resume(); } else { scriptBroadcast("quit"); } @@ -331,7 +370,7 @@ function main () { prevPos = { x: me.x, y: me.y }; } - this.pause(); + pause(); Config.UseMerc = false; // Don't go revive the merc mid-fight attackCount = 0; @@ -353,8 +392,12 @@ function main () { if (missile) { do { - if (getPlayerFlag(me.gid, missile.owner, 8) && (getDistance(me, missile) < 15 || (missile.targetx && getDistance(me, missile.targetx, missile.targety) < 15))) { - this.moveAway(missile, Skill.getRange(Config.AttackSkill[1])); + if ( + getPlayerFlag(me.gid, missile.owner, 8) + && (getDistance(me, missile) < 15 + || (missile.targetx && getDistance(me, missile.targetx, missile.targety) < 15)) + ) { + moveAway(missile, Skill.getRange(Config.AttackSkill[1])); break; } @@ -362,15 +405,23 @@ function main () { } // Move away if the player is too close or if he tries to move too close (telestomp) - if (Skill.getRange(Config.AttackSkill[1]) > 20 && (getDistance(me, player) < 30 || (player.targetx && getDistance(me, player.targetx, player.targety) < 15))) { - this.moveAway(player, Skill.getRange(Config.AttackSkill[1])); + if ( + Skill.getRange(Config.AttackSkill[1]) > 20 + && (getDistance(me, player) < 30 + || (player.targetx && getDistance(me, player.targetx, player.targety) < 15)) + ) { + moveAway(player, Skill.getRange(Config.AttackSkill[1])); } break; case sdk.player.class.Paladin: // Smite summoners if (Config.AttackSkill[1] === sdk.skills.BlessedHammer && Skill.canUse(sdk.skills.Smite)) { - if ([sdk.player.class.Necromancer, sdk.player.class.Druid].includes(player.classid) && getDistance(me, player) < 4 && this.checkSummons(player)) { + if ( + [sdk.player.class.Necromancer, sdk.player.class.Druid].includes(player.classid) + && getDistance(me, player) < 4 + && checkSummons(player) + ) { Skill.cast(sdk.skills.Smite, sdk.skills.hand.Left, player); } } @@ -386,7 +437,7 @@ function main () { } Pather.moveTo(prevPos.x, prevPos.y); - this.resume(); + resume(); } } From e4ff8c1507ea40be0e0724312ae6be416ecbe59d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:26:35 -0500 Subject: [PATCH 464/758] Update `SimpleParty` to have a scriptmsg listener for updating shitlist since we are paused during that time --- d2bs/kolbot/libs/modules/workers/SimpleParty.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index a5dd967bd..7c5072d9f 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -90,6 +90,20 @@ SimpleParty.timer = 0; if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { + addEventListener("scriptmsg", function (msg) { + if (!isType(msg, "string")) return; + if (msg === "hostileEventEnded" && Config.ShitList) { + let updatedList = ShitList.read(); + if (updatedList.length) { + for (let name of updatedList) { + if (!shitList.includes(name)) { + shitList.push(name); + } + } + } + } + }); + // For now, we gonna do this in game with a single party (Worker.runInBackground.party = (function () { console.log("ÿc2Kolbotÿc0 :: Simple party running"); From 835bbe013074bf403e9eae16fe1d836579b52991 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:27:44 -0500 Subject: [PATCH 465/758] Update globals.d.ts --- d2bs/kolbot/sdk/globals.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 23de5bbf1..52552c5f0 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1347,6 +1347,6 @@ declare global { * @param type - The expected type as a string. * @returns {boolean} - Returns true if the value matches the expected type, otherwise false. */ - function isType(val: any, type: PrimitiveType): boolean; + function isType(val: any, type: T): val is PrimitiveTypeMap[T]; } export {}; From 1c9b0b09b4e885fae71e947857d99e30acc99af7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 02:03:05 -0500 Subject: [PATCH 466/758] Add `me.shitList` property so it's easier to share data across workers on the same thread - Keep a global set of the names in the shitList, this makes it simple for actions that share it like the `SimpleParty` thread and `ControlBot` --- d2bs/kolbot/libs/core/Me.js | 58 ++++++++++++++----- .../libs/modules/workers/SimpleParty.js | 40 +++++-------- d2bs/kolbot/libs/oog/ShitList.js | 2 + d2bs/kolbot/libs/scripts/ControlBot.js | 41 +++++++------ d2bs/kolbot/sdk/globals.d.ts | 6 ++ 5 files changed, 91 insertions(+), 56 deletions(-) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index acf5bcaaa..352c49eab 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -7,6 +7,8 @@ // Ensure these are in polyfill.js !isIncluded("Polyfill.js") && include("Polyfill.js"); +/** @global */ +const Me = me; /** * @desciption Set me.runwalk to 0 (walk) @@ -726,8 +728,13 @@ me.checkScrolls = function (id) { }; me.checkKeys = function () { - if (!Config.OpenChests.Enabled || me.assassin || me.gold < 540 - || (!me.getItem("key") && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 }))) { + if (!Config.OpenChests.Enabled) return 12; + // sins don't need keys + if (me.assassin) return 12; + // cam't afford key + if (me.gold < 540) return 12; + // no room for key + if (!me.getItem(sdk.items.Key) && !Storage.Inventory.CanFit({ sizex: 1, sizey: 1 })) { return 12; } @@ -947,6 +954,27 @@ Object.defineProperties(me, { return me.getState(sdk.states.SkillDelay); } }, + _shitList: { + value: new Set(), + writable: true, + enumerable: true, + configurable: true + }, + shitList: { + get: function () { + return this._shitList; + }, + /** @param {Set} value */ + set: function (value) { + if (value instanceof Set) { + this._shitList = value; + } else { + throw new TypeError("shitList must be a Set"); + } + }, + enumerable: true, + configurable: true + } }); /** @@ -1097,11 +1125,21 @@ Object.defineProperties(me, { }); /** - * Quests + * Quests + AreaData */ (function () { const QuestData = require("./GameData/QuestData"); + /** + * @param {number} act + * @returns {boolean} + */ + me.accessToAct = function (act) { + if (act === 1) return true; + return me.highestAct >= act; + }; + + // const AMOUNT_OF_WAYPOINTS = 39; const AMOUNT_OF_WAYPOINTS = [ sdk.waypoints.Act1, sdk.waypoints.Act2, @@ -1126,15 +1164,6 @@ Object.defineProperties(me, { }); } }); - - /** - * @param {number} act - * @returns {boolean} - */ - me.accessToAct = function (act) { - if (act === 1) return true; - return me.highestAct >= act; - }; /** * Easier way to check if you have a waypoint @@ -1142,7 +1171,7 @@ Object.defineProperties(me, { * @returns {boolean} */ me.haveWaypoint = function (area) { - let areaIndex = Pather.wpAreas.indexOf(area); + const areaIndex = Pather.wpAreas.indexOf(area); if (areaIndex === -1) return false; return getWaypoint(areaIndex); }; @@ -1177,7 +1206,8 @@ Object.defineProperties(me, { if ([ sdk.quest.id.RescueonMountArreat, sdk.quest.id.SiegeOnHarrogath, - sdk.quest.id.ToolsoftheTrade + sdk.quest.id.ToolsoftheTrade, + sdk.quest.id.PrisonofIce, ].includes(i) && QuestData.get(i).complete(true)) { return i; } diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index 7c5072d9f..113b0e182 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -1,9 +1,9 @@ (function (module, require) { // party thread specific include("oog/ShitList.js"); - const shitList = (Config.ShitList || Config.UnpartyShitlisted) - ? ShitList.read() - : []; + if ((Config.ShitList || Config.UnpartyShitlisted)) { + ShitList.read().forEach((name) => me.shitList.add(name)); + } const Worker = require("../Worker"); const NO_PARTY = 65535; const PARTY_MEMBER = 1; @@ -92,18 +92,11 @@ if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { addEventListener("scriptmsg", function (msg) { if (!isType(msg, "string")) return; - if (msg === "hostileEventEnded" && Config.ShitList) { - let updatedList = ShitList.read(); - if (updatedList.length) { - for (let name of updatedList) { - if (!shitList.includes(name)) { - shitList.push(name); - } - } - } + if (msg === "hostileEventEnded" && (Config.ShitList || Config.UnpartyShitlisted)) { + ShitList.read().forEach((name) => me.shitList.add(name)); } }); - + // For now, we gonna do this in game with a single party (Worker.runInBackground.party = (function () { console.log("ÿc2Kolbotÿc0 :: Simple party running"); @@ -142,10 +135,10 @@ // Deal with inviting if ( // If no party is formed, or im member of the biggest party - party.partyflag !== INVITED && // Already invited - party.partyflag !== ACCEPTABLE && // Need to accept invite, so cant invite - party.partyflag !== PARTY_MEMBER && // cant party again with soemone - party.partyid === NO_PARTY // Can only invite someone that isnt in a party + party.partyflag !== INVITED // Already invited + && party.partyflag !== ACCEPTABLE // Need to accept invite, so cant invite + && party.partyflag !== PARTY_MEMBER // cant party again with soemone + && party.partyid === NO_PARTY // Can only invite someone that isnt in a party && ( // If im not in a party, only if there is no party myPartyId === NO_PARTY && biggestPartyId === NO_PARTY // OR, if im part of the biggest party @@ -153,9 +146,8 @@ ) ) { if (getPlayerFlag(me.gid, party.gid, sdk.player.flag.Hostile)) { - if (shitList.includes(party.name)) { + if (me.shitList.has(party.name)) { say(party.name + " has been shitlisted."); - shitList.push(party.name); ShitList.add(party.name); } @@ -164,7 +156,7 @@ } continue; - } else if (shitList.includes(party.name)) { + } else if (me.shitList.has(party.name)) { continue; } @@ -188,7 +180,7 @@ if (acceptFirst !== party.name) { continue; // Ignore party acceptation } - if (shitList.includes(party.name)) { + if (me.shitList.has(party.name)) { continue; } } @@ -208,11 +200,11 @@ if (Config.UnpartyShitlisted) { // Add new hostile players to temp shitlist, leader should have Config.ShitList set to true to update the permanent list. if (getPlayerFlag(me.gid, party.gid, sdk.player.flag.Hostile) - && shitList.includes(party.name)) { - shitList.push(party.name); + && !me.shitList.has(party.name)) { + me.shitList.add(party.name); } - if (shitList.includes(party.name) + if (me.shitList.has(party.name) && myPartyId !== NO_PARTY && party.partyid === myPartyId) { console.log("Unpartying shitlisted player: " + party.name); diff --git a/d2bs/kolbot/libs/oog/ShitList.js b/d2bs/kolbot/libs/oog/ShitList.js index a77e3767d..687bfd170 100644 --- a/d2bs/kolbot/libs/oog/ShitList.js +++ b/d2bs/kolbot/libs/oog/ShitList.js @@ -72,6 +72,7 @@ const ShitList = { /** @param {string} name */ add: function (name) { + me.shitList.add(name); let obj = this.getObj(); if (obj.shitlist.includes(name)) return; obj.shitlist.push(name); @@ -84,6 +85,7 @@ const ShitList = { /** @param {string} name */ remove: function (name) { + me.shitList.delete(name); let obj = this.getObj(); let index = obj.shitlist.indexOf(name); if (index === -1) return false; diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index cd86f866a..6b57b9f62 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -40,7 +40,7 @@ const ControlBot = new Runnable( RushModes, } = require("../systems/autorush/RushConfig"); const Worker = require("../modules/Worker"); - + /** @param {string} [nick] */ const mephisto = function (nick) { log("starting mephisto"); @@ -131,6 +131,7 @@ const ControlBot = new Runnable( const givenGold = new Set(); const Chat = { + overheadTick: 0, /** @type {string[]} */ queue: [], @@ -177,8 +178,8 @@ const ControlBot = new Runnable( // should check if next msg is going to be a whisper and if so // check if the player is in the game and if not, don't send the whisper if (getTickCount() - tick < 0) return true; - // allow say messages every ~1.5 seconds - tick = getTickCount() + Time.seconds(1) + rand(250, 750); + // allow say messages every ~1.7 seconds + tick = getTickCount() + Time.seconds(1) + rand(500, 950); console.debug("(" + Chat.queue[0] + ")"); if (Chat.queue[0].length > MAX_CHAT_LENGTH) { console.debug("Message too long, splitting."); @@ -238,8 +239,6 @@ const ControlBot = new Runnable( const cmdNicks = new Map(); /** @type {Map} */ const wpNicks = new Map(); - /** @type {Set} */ - const shitList = new Set(); /** @type {Array} */ const greet = []; /** @type {Map 40) continue; // allow rechanting someone if it's going to run out soon for them if (!unit.getState(sdk.states.Enchant) @@ -687,6 +686,7 @@ const ControlBot = new Runnable( next = false; continue; } + Pather.useWaypoint(wp, true); if (Config.ControlBot.Wps.SecurePortal) { Attack.securePosition(me.x, me.y, 20, 1000); @@ -737,8 +737,8 @@ const ControlBot = new Runnable( if (party.name !== me.name && getPlayerFlag(me.gid, party.gid, 8)) { rval = true; - if (Config.ShitList && !shitList.has(party.name)) { - shitList.add(party.name); + if (Config.ShitList && !me.shitList.has(party.name)) { + me.shitList.add(party.name); } } } while (party.getNext()); @@ -898,8 +898,10 @@ const ControlBot = new Runnable( if (commandAliases.has(msg)) { msg = commandAliases.get(msg); } - if (!actions.has(msg)) return; - if (shitList.has(nick)) { + if (!actions.has(msg)) { + return; + } + if (me.shitList.has(nick)) { Chat.say("No commands for the shitlisted."); } else { if (running.nick === nick && running.command === msg) { @@ -978,7 +980,8 @@ const ControlBot = new Runnable( const _actions = new Map(); _actions.set("help", { - desc: "Display commands", + // desc: "Display commands", + desc: "", hostileCheck: false, /** @param {string} nick */ run: function (nick) { @@ -988,7 +991,8 @@ const ControlBot = new Runnable( if (!value.desc.length) return; if (value.complete) return; if (value.desc.includes("Rush")) return; - let desc = (key + " (" + value.desc + "), "); + // let desc = (key + " (" + value.desc + "), "); + let desc = (key + ", "); if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { msg.push(str); str = ""; @@ -996,7 +1000,7 @@ const ControlBot = new Runnable( str += desc; }); str.length && msg.push(str); - str = "Rush commands (example: rush andy): "; + str = "Rush cmds (ex: rush andy): "; _actions.forEach((value, key) => { if (!value.desc.length) return; if (value.complete) return; @@ -1041,7 +1045,7 @@ const ControlBot = new Runnable( return; } ngVote.begin(); - Chat.say(nick + " voted for next game. Votes Needed: " + ngVote.votesNeeded + ". Type ngyes to vote."); + Chat.say(nick + " voted for next game. Votes Needed: " + ngVote.votesNeeded() + ". Type ngyes to vote."); } }); _actions.set("ngyes", { @@ -1220,7 +1224,7 @@ const ControlBot = new Runnable( // START let gameEndWarningAnnounced = false; include("oog/ShitList.js"); - Config.ShitList && shitList.add(ShitList.read()); + Config.ShitList && ShitList.read().forEach((name) => me.shitList.add(name)); try { addEventListener("chatmsg", chatEvent); @@ -1243,8 +1247,9 @@ const ControlBot = new Runnable( while (greet.length > 0) { let nick = greet.shift(); - if (!shitList.has(nick)) { - Chat.say("Welcome, " + nick + "! For a list of commands say 'help'"); + if (!me.shitList.has(nick)) { + // Chat.say("Welcome, " + nick + "! For a list of commands say 'help'"); + Chat.overhead("Welcome, " + nick + "! For a list of commands say 'help'"); } } @@ -1309,6 +1314,6 @@ const ControlBot = new Runnable( }, { startArea: sdk.areas.RogueEncampment, - preAction: null + preAction: null, } ); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 52552c5f0..c68f3b281 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -681,6 +681,12 @@ declare global { */ readonly maxgold: number; waypoints: boolean[]; + /** + * @private + * Don't use directly, use `me.shitList` + */ + _shitList: Set; + shitList: Set; // d2bs functions overhead(msg: string): void; From e2b09269a2fd5d19c0eb14748633f3bc206939b9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Dec 2024 11:00:22 -0500 Subject: [PATCH 467/758] Update Mule.js --- d2bs/kolbot/libs/systems/automule/Mule.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/systems/automule/Mule.js b/d2bs/kolbot/libs/systems/automule/Mule.js index 419089e1f..d58897480 100644 --- a/d2bs/kolbot/libs/systems/automule/Mule.js +++ b/d2bs/kolbot/libs/systems/automule/Mule.js @@ -49,6 +49,7 @@ const MuleData = { // set next account - increase account number in mule datafile nextAccount: function () { + Starter.makeAccount = true; let obj = MuleData.read(); obj.accNum += 1; From 69f48ce26756c720163bcaa3b4e4f31e76054a41 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 3 Jan 2025 00:24:35 -0500 Subject: [PATCH 468/758] Fix `ngvote` for ControlBot + little refactoring - Change `ngVote.watch` -> `ngVote.active` - Fix invalid `votesNeeded` count - Fix typo in `ngVote.checkCount`. `votesNeeded` is a method - Changed the help message back to in game chat vs whisper, noticed getting muted a lot when using whipser - Add handler so users don't cause the bot to spam the help message, if they've asked once this game then the help message exists in the logs they can check that --- d2bs/kolbot/libs/scripts/ControlBot.js | 81 +++++++++++++++++++------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 6b57b9f62..43b9ae345 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -9,6 +9,8 @@ const ControlBot = new Runnable( function ControlBot () { + /** @type {string[]} */ + const shitlistedAccounts = []; // Quests const { log, @@ -91,28 +93,34 @@ const ControlBot = new Runnable( const ngVote = new function () { /** @type {Set} */ this.votes = new Set(); - this.watch = false; + this.active = false; this.tick = 0; this.nextGame = false; this.votesNeeded = function () { - return Math.max(1, (Misc.getPlayerCount() - 2) / 2); + return Math.max(1, Math.floor((Misc.getPlayerCount() - 2) / 2)); }; this.reset = function () { this.votes.clear(); this.tick = 0; - this.watch = false; + this.active = false; }; this.begin = function () { - this.watch = true; + this.active = true; this.votes.clear(); this.tick = getTickCount(); }; this.checkCount = function () { - return this.votes.size >= this.votesNeeded; + const reqMet = this.votes.size >= this.votesNeeded(); + if (reqMet) { + Chat.say("Enough votes to start next game."); + ngVote.nextGame = true; + this.reset(); + } + return reqMet; }; this.vote = function (nick) { - if (this.watch) { + if (this.active) { this.votes.add(nick); } }; @@ -168,6 +176,15 @@ const ControlBot = new Runnable( let who = players.get(nick) || nick; Chat.queue.push("/w " + who + " " + msg); }, + + /** + * Private message a chat to a user + * @param {string} nick + * @param {string} msg + */ + message: function (nick, msg) { + Chat.queue.push("/m " + nick + " " + msg); + }, }; Worker.runInBackground.chat = (function () { @@ -175,9 +192,11 @@ const ControlBot = new Runnable( return function () { if (!Chat.queue.length) return true; - // should check if next msg is going to be a whisper and if so - // check if the player is in the game and if not, don't send the whisper if (getTickCount() - tick < 0) return true; + // check if next msg is going to be a whisper + if (Chat.queue[0].startsWith("/w")) { + // check if the player is in the game and if not, don't send the whisper + } // allow say messages every ~1.7 seconds tick = getTickCount() + Time.seconds(1) + rand(500, 950); console.debug("(" + Chat.queue[0] + ")"); @@ -195,6 +214,7 @@ const ControlBot = new Runnable( this.firstCmd = getTickCount(); this.commands = 0; this.ignored = false; + this.seenHelpMsg = false; } PlayerTracker.prototype.resetCmds = function () { @@ -934,6 +954,9 @@ const ControlBot = new Runnable( // idle in town me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); if (name2) { + if (shitlistedAccounts.includes(name2) && !me.shitList.has(name1)) { + ShitList.add(name1); + } players.set(name1, "*" + name2); } else { players.set(name1, ""); @@ -944,7 +967,7 @@ const ControlBot = new Runnable( case 0x01: // "%Name1(%Name2) dropped due to errors." case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." players.delete(name1); - if (ngVote.watch) { + if (ngVote.active) { ngVote.votes.delete(name1); } @@ -1000,7 +1023,7 @@ const ControlBot = new Runnable( str += desc; }); str.length && msg.push(str); - str = "Rush cmds (ex: rush andy): "; + str = "Rush cmds: "; _actions.forEach((value, key) => { if (!value.desc.length) return; if (value.complete) return; @@ -1013,9 +1036,17 @@ const ControlBot = new Runnable( str += desc; }); str.length && msg.push(str); - msg.forEach(function (m) { - Chat.whisper(nick, m); - }); + + !cmdNicks.has(nick) && cmdNicks.set(nick, new PlayerTracker()); + if (cmdNicks.has(nick) && cmdNicks.get(nick).seenHelpMsg) { + Chat.message(nick, "You have seen the help menu before this game please refer to message log"); + } else { + msg.forEach(function (m) { + // Chat.whisper(nick, m); + Chat.say(m); + }); + } + cmdNicks.get(nick).seenHelpMsg = true; } }); _actions.set("timeleft", { @@ -1037,14 +1068,19 @@ const ControlBot = new Runnable( desc: "Vote for next game", hostileCheck: false, run: function (nick) { + if (ngVote.active) { + Chat.say("NGVote is already active. Current count: " + ngVote.votes.size); + return; + } if (getTickCount() - startTime < Time.minutes(3)) { Chat.say( - "Can't vote for next game yet. Must be in game for at least 3 minutes. Remaining: " + "Can't vote for ng yet. Must be in game for at least 3 minutes. Remaining: " + Math.round((Time.minutes(3) - (getTickCount() - startTime)) / 1000) + " seconds." ); return; } ngVote.begin(); + ngVote.vote(nick); Chat.say(nick + " voted for next game. Votes Needed: " + ngVote.votesNeeded() + ". Type ngyes to vote."); } }); @@ -1052,12 +1088,9 @@ const ControlBot = new Runnable( desc: "", hostileCheck: false, run: function (nick) { - if (!ngVote.watch) return; + if (!ngVote.active) return; ngVote.vote(nick); - if (ngVote.checkCount()) { - Chat.say("Enough votes to start next game."); - ngVote.nextGame = true; - } + ngVote.checkCount(); } }); @@ -1285,9 +1318,13 @@ const ControlBot = new Runnable( } pickGoldPiles(); - if (ngVote.watch && ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { - Chat.say("Not enough votes to start next game."); - ngVote.reset(); + if (ngVote.active) { + if (ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { + Chat.say("Not enough votes to start next game. Votes gathered " + ngVote.votes.size); + ngVote.reset(); + } else { + ngVote.checkCount(); + } } if (getTickCount() - startTime >= maxTime || ngVote.nextGame) { From 2c3c975d30cad4c658f2c0c43d7bce11f8d42333 Mon Sep 17 00:00:00 2001 From: Georg R <39526093+icommitdesnet@users.noreply.github.com> Date: Fri, 3 Jan 2025 21:04:22 +0100 Subject: [PATCH 469/758] Manualplay fixes (#425) * add extra character to abbreviated name to prevent collision. small charm and small crescent now differ grand charm and grand crown now differ * there is no id building the itemquality>name map fix item * remove "debugging" code --- d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js index 4e2f125c5..30e79a74d 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ItemHooks.js @@ -108,7 +108,7 @@ const ItemHooks = (function () { * @param {string} uniqueName * @returns {Map} */ - const buildClassIdAndQuality = function (id, setName = "", uniqueName = "") { + const buildClassIdAndQuality = function (setName = "", uniqueName = "") { let temp = new Map(); setName && temp.set(sdk.items.quality.Set, setName); uniqueName && temp.set(sdk.items.quality.Unique, uniqueName); @@ -136,7 +136,7 @@ const ItemHooks = (function () { [sdk.items.Diadem, buildClassIdAndQuality("Mavina's Helm", "Griffon's Eye")], [sdk.items.SharkskinBelt, buildClassIdAndQuality("Mavina's Belt", "Razortail")], [sdk.items.BattleGauntlets, buildClassIdAndQuality("Mavina's Gloves", "Lava Gout")], - [sdk.items.ScissorsKatar, buildClassIdAndQuality("Natalya's Wep")], + [sdk.items.ScissorsSuwayyah, buildClassIdAndQuality("Natalya's Wep")], [sdk.items.LoricatedMail, buildClassIdAndQuality("Natalya's Armor")], [sdk.items.GrimHelm, buildClassIdAndQuality("Natalya's Helm", "Vamp Gaze")], [sdk.items.MeshBoots, buildClassIdAndQuality("Natalya's Boots", "Silkweave")], @@ -275,7 +275,7 @@ const ItemHooks = (function () { abbrName += abbr[0] + "-"; for (let i = 1; i < abbr.length; i++) { - abbrName += abbr[i].substring(0, 1); + abbrName += abbr[i].substring(0, 2); } } From 6839b8121c5937f6a8137cd850c18d718d14cee5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:51:24 -0500 Subject: [PATCH 470/758] Fix strict warning for secondary alternative attack skill setup --- d2bs/kolbot/libs/core/Attacks/Paladin.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index 457aca530..ae0e096e4 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -79,7 +79,11 @@ const ClassAttack = { if (Config.AttackSkill[5] > -1 && Attack.checkResist(unit, Config.AttackSkill[5])) { attackSkill = Config.AttackSkill[5]; aura = Config.AttackSkill[6]; - } else if (Config.AttackSkill[7] > -1 && Attack.checkResist(unit, Config.AttackSkill[7])) { + } else if ( + Config.AttackSkill.length === 9 + && Config.AttackSkill[7] > -1 + && Attack.checkResist(unit, Config.AttackSkill[7]) + ) { attackSkill = Config.AttackSkill[7]; aura = Config.AttackSkill[8]; } From 68bca5f5f838cf5e7d3beb3875b238e83a742f7b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 14 Jan 2025 01:27:07 -0500 Subject: [PATCH 471/758] Update globals.d.ts --- d2bs/kolbot/sdk/globals.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index c68f3b281..4cfaab56c 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -278,7 +278,8 @@ declare global { readText(filename: string) writeText(filename: string, data: string) appendText(filename: string, data: string) - exists(filename: string): Boolean; + exists(filename: string): boolean; + remove(filename: string): boolean; } function getCollision(area: number, x: number, y: number, x2: number, y2: number) From 24c54dde431410733a6992b798ed4aa120a87ddf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 14 Jan 2025 01:29:51 -0500 Subject: [PATCH 472/758] Add handler for pre-existing mule files leftover from ladder reset - Add check for non-ladder characters to see if there is an existing ladder file for the character and if so go ahead and remove it so we don't have duplicate data. Mostly affects droppers --- .../libs/systems/mulelogger/MuleLogger.js | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 28b14cf96..f89756981 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -250,17 +250,37 @@ const MuleLogger = { // hcl = hardcore class ladder // sen = softcore expan nonladder - FileTools.writeText( + /** + * @param {string} account + * @param {string} charName + * @param {boolean} playerType + * @param {number} gameType + * @param {boolean} ladder + * @returns {string} + */ + const buildFileName = (account, charName, playerType, gameType, ladder) => ( "mules/" + realm + "/" - + me.account + "/" - + me.name + "." - + ( me.playertype ? "h" : "s" ) - + (me.gametype ? "e" : "c" ) - + ( me.ladder > 0 ? "l" : "n" ) - + ".txt", + + account + "/" + + charName + "." + + (playerType ? "h" : "s" ) + + (gameType ? "e" : "c" ) + + (ladder ? "l" : "n" ) + + ".txt" + ); + FileTools.writeText( + buildFileName(me.account, me.name, me.playertype, me.gametype, me.ladder > 0), finalString ); - print("Item logging done."); + + if (!me.ladder) { + let ladderVersion = buildFileName(me.account, me.name, me.playertype, me.gametype, true); + if (FileTools.exists(ladderVersion)) { + // this character is probably being relogged after ladder reset, log that we are deleting and remove the old file + console.log("Found ladder version of this char, removing the old file as assuming this is leftover from before ladder reset."); + FileTools.remove(ladderVersion); + } + } + console.log("Item logging done."); } }; From 2ec0e4b8f2fa2dab58c80a185fae19365517f4f4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:53:55 -0500 Subject: [PATCH 473/758] Add `RealmGoingDownInXMinutes` locale string --- d2bs/kolbot/libs/modules/sdk.js | 3 +++ d2bs/kolbot/sdk/types/sdk.d.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 9fdf82a49..1870d538b 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -1694,6 +1694,8 @@ BluePortal: 59, RedPortal: 60, Smoke: 401, + Well: 132, + Wells: [130, 132, 322], }, exits: { @@ -4498,6 +4500,7 @@ Identify: 3350, Repair2: 3351, EnhancedDefense: 3520, + RealmGoingDownInXMinutes: 3651, Strength: 4060, Dexterity: 4062, Vitality: 4066, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 3f4841c1f..d9d87653f 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4423,6 +4423,7 @@ declare global { const Identify: 3350; const Repair2: 3351; const EnhancedDefense: 3520; + const RealmGoingDownInXMinutes: 3651; const Strength: 4060; const Dexterity: 4062; const Vitality: 4066; From 952a8c708fa99c10a0b58b6e2f256fe6018f818e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:02:13 -0500 Subject: [PATCH 474/758] Update ToolsThread.js - Log the `realm going down in x minutes` gameevent --- d2bs/kolbot/threads/ToolsThread.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index d5a5f89e4..bef539a2b 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -277,6 +277,12 @@ function main () { } } + 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 bb656ceaa348991afac50eb0d6a7bcd543d5bb0e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:58:58 -0500 Subject: [PATCH 475/758] Update AutoRush.js - Add chanter handler to run directly back to rouge encampment using exit -> wp instead of taking portal and having to run back from further away --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 73049a6a3..3c48518f1 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -661,6 +661,9 @@ } Town.goToTown(3); Town.doChores(); + } else if (AutoRush.rushMode === RushModes.chanter) { + Pather.moveToExit([sdk.areas.HaremLvl1, sdk.areas.LutGholein], true); + Pather.useWaypoint(sdk.areas.RogueEncampment); } return true; From cff01fedd63215791bdd8aba4da697585a55acec Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:10:52 -0500 Subject: [PATCH 476/758] Update ControlBot - Add `givewp ` command + Fix ngvoting - Add experimental support for giving a single wp by name - Fix abuse of the ngvote system, give players time to vote yes/no --- d2bs/kolbot/libs/scripts/ControlBot.js | 182 +++++++++++++++++++++---- 1 file changed, 157 insertions(+), 25 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 43b9ae345..72aa018fc 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -9,8 +9,6 @@ const ControlBot = new Runnable( function ControlBot () { - /** @type {string[]} */ - const shitlistedAccounts = []; // Quests const { log, @@ -42,6 +40,7 @@ const ControlBot = new Runnable( RushModes, } = require("../systems/autorush/RushConfig"); const Worker = require("../modules/Worker"); + const AreaData = require("../core/GameData/AreaData"); /** @param {string} [nick] */ const mephisto = function (nick) { @@ -92,7 +91,9 @@ const ControlBot = new Runnable( const ngVote = new function () { /** @type {Set} */ - this.votes = new Set(); + this.votesYes = new Set(); + /** @type {Set} */ + this.votesNo = new Set(); this.active = false; this.tick = 0; this.nextGame = false; @@ -101,27 +102,52 @@ const ControlBot = new Runnable( return Math.max(1, Math.floor((Misc.getPlayerCount() - 2) / 2)); }; this.reset = function () { - this.votes.clear(); + this.votesYes.clear(); + this.votesNo.clear(); this.tick = 0; this.active = false; }; this.begin = function () { this.active = true; - this.votes.clear(); + this.votesYes.clear(); + this.votesNo.clear(); this.tick = getTickCount(); }; this.checkCount = function () { - const reqMet = this.votes.size >= this.votesNeeded(); + // ensure we've counted everyones votes when checking for a draw + if (Misc.getPartyCount() === this.votesYes.size + this.votesNo.size) { + if (this.votesYes.size === this.votesNo.size) { + Chat.say("Not enough votes to start ng we have a draw."); + this.reset(); + return false; + } + } + const votesNeeded = this.votesNeeded(); + if (this.votesNo.size >= votesNeeded) { + Chat.say("ng rejected by majority."); + this.reset(); + return false; + } + const reqMet = this.votesYes.size >= votesNeeded; if (reqMet) { - Chat.say("Enough votes to start next game."); + Chat.say("ng approved by majority."); ngVote.nextGame = true; this.reset(); } return reqMet; }; - this.vote = function (nick) { - if (this.active) { - this.votes.add(nick); + /** + * @param {string} nick + * @param {"yes" | "no"} type + */ + this.vote = function (nick, type) { + if (!this.active) return; + if (type === "yes") { + this.votesNo.delete(nick); + this.votesYes.add(nick); + } else if (type === "no") { + this.votesYes.delete(nick); + this.votesNo.add(nick); } }; this.elapsed = function () { @@ -244,6 +270,7 @@ const ControlBot = new Runnable( function WpTracker () { this.timer = getTickCount(); this.requests = 0; + this.singleWpRequests = 0; } WpTracker.prototype.update = function () { @@ -251,6 +278,11 @@ const ControlBot = new Runnable( this.requests++; }; + WpTracker.prototype.updateSingle = function () { + this.timer = getTickCount(); + this.singleWpRequests++; + }; + WpTracker.prototype.timeSinceLastRequest = function () { return getTickCount() - this.timer; }; @@ -652,6 +684,67 @@ const ControlBot = new Runnable( return false; }; + /** + * @param {string} nick + * @param {number} areaId + * @returns {boolean} + */ + const giveWp = function (nick, areaId) { + try { + if (!Misc.inMyParty(nick)) { + throw new ScriptError("Accept party invite, noob."); + } + + Chat.say("Giving wp " + getAreaName(areaId)); + + if (!wpNicks.has(nick)) { + wpNicks.set(nick, new WpTracker()); + } + + const check = wpNicks.get(nick); + if (check.singleWpRequests > 12) { + throw new ScriptError("You have spent all your waypoint requests for this game."); + } else if (check.requests > 1 && check.timeSinceLastRequest() < 60000) { + throw new ScriptError( + "You may request wp again in " + + Math.max(0, (60 - Math.floor(check.timeSinceLastRequest() / 1000))) + + " seconds." + ); + } + + let act = Misc.getPlayerAct(nick); + if (!wps.has(act)) return false; + + Pather.useWaypoint(areaId, true); + if (Config.ControlBot.Wps.SecurePortal) { + Attack.securePosition(me.x, me.y, 20, 1000); + } + Pather.makePortal(); + Chat.say(getAreaName(me.area) + " TP up"); + + if (!Misc.poll(() => (Game.getPlayer(nick)), Time.seconds(30), Time.seconds(1))) { + Chat.say("Aborting wp giving."); + } + + Town.doChores(); + Town.goToTown(1); + Town.move("portalspot"); + + check.updateSingle(); + + return true; + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } + + return false; + } + }; + /** * @param {string} nick * @returns {boolean} @@ -912,8 +1005,11 @@ const ControlBot = new Runnable( if (!nick || !msg) return; if (nick === me.name) return; msg = msg.toLowerCase(); + const full = msg.replace(/[\'\<\>\[\]\{\}\(\)\!\@\#\$\%\^\&\*\_\+\=\|\~\`\;\:\"\?\,\.\/\\]/g, ""); if (msg.match(/^rush /gi)) { msg = msg.split(" ")[1]; + } else if (msg.match(/^givewp /gi)) { + msg = msg.slice(0, 6).trim(); } if (commandAliases.has(msg)) { msg = commandAliases.get(msg); @@ -928,9 +1024,11 @@ const ControlBot = new Runnable( console.debug("Command already running."); return; } - if (!floodCheck([msg, nick]) && (msg === "help" || msg === "timeleft" || msg === "ngyes")) { - actions.get(msg).run(nick); - return; + if (!floodCheck([msg, nick])) { + if (["help", "timeleft", "ngyes", "ngno"].includes(msg)) { + actions.get(msg).run(nick); + return; + } } let index = queue.findIndex(function (cmd) { return cmd[0] === msg && cmd[1] === nick; @@ -938,7 +1036,7 @@ const ControlBot = new Runnable( if (index > -1) { Chat.whisper(nick, "You already requested this command. Queue position: " + (index + 1)); } else { - queue.push([msg, nick]); + queue.push([msg, nick, full]); console.log(queue); if (queue.length > 1 || running.nick !== "") { Chat.whisper(nick, msg + " has been added to the queue. Queue position: " + (queue.length + 1)); @@ -954,9 +1052,6 @@ const ControlBot = new Runnable( // idle in town me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); if (name2) { - if (shitlistedAccounts.includes(name2) && !me.shitList.has(name1)) { - ShitList.add(name1); - } players.set(name1, "*" + name2); } else { players.set(name1, ""); @@ -968,7 +1063,8 @@ const ControlBot = new Runnable( case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." players.delete(name1); if (ngVote.active) { - ngVote.votes.delete(name1); + ngVote.votesYes.delete(name1); + ngVote.votesNo.delete(name1); } break; @@ -1015,7 +1111,7 @@ const ControlBot = new Runnable( if (value.complete) return; if (value.desc.includes("Rush")) return; // let desc = (key + " (" + value.desc + "), "); - let desc = (key + ", "); + let desc = value.desc.includes("experimental") ? ("(" + value.desc + "), ") : (key + ", "); if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { msg.push(str); str = ""; @@ -1080,8 +1176,9 @@ const ControlBot = new Runnable( return; } ngVote.begin(); - ngVote.vote(nick); - Chat.say(nick + " voted for next game. Votes Needed: " + ngVote.votesNeeded() + ". Type ngyes to vote."); + ngVote.vote(nick, "yes"); + const votesNeeded = ngVote.votesNeeded(); + Chat.say(nick + " voted for next game. Votes Needed: " + votesNeeded + ". Type ngyes/ngno"); } }); _actions.set("ngyes", { @@ -1089,7 +1186,16 @@ const ControlBot = new Runnable( hostileCheck: false, run: function (nick) { if (!ngVote.active) return; - ngVote.vote(nick); + ngVote.vote(nick, "yes"); + ngVote.checkCount(); + } + }); + _actions.set("ngno", { + desc: "", + hostileCheck: false, + run: function (nick) { + if (!ngVote.active) return; + ngVote.vote(nick, "no"); ngVote.checkCount(); } }); @@ -1127,6 +1233,12 @@ const ControlBot = new Runnable( hostileCheck: true, run: giveWps }); + + _actions.set("givewp", { + desc: "givewp - experimental", + hostileCheck: true, + run: giveWp + }); } if (Config.ControlBot.Bo @@ -1225,11 +1337,11 @@ const ControlBot = new Runnable( ["enchant", "chant"], ]); - /** @param {[string, string]} command */ + /** @param {[string, string, string]} command */ const runAction = function (command) { if (!command || command.length < 2) return false; console.debug("Checking command: " + command); - let [cmd, nick] = command; + let [cmd, nick, full] = command; if (cmd.match(/^rush /gi)) { cmd = cmd.split(" ")[1]; } @@ -1247,6 +1359,26 @@ const ControlBot = new Runnable( Chat.say("Command disabled because of hostiles."); return false; } + + if (full.match(/^givewp /gi)) { + let [, areaName] = full.split("givewp "); + if (areaName) { + /** @type {AreaDataObj} */ + let area = AreaData.findByName(areaName); + if (area.Waypoint === 255) { + Chat.say(area.LocaleString + " isn't a valid wp area to ask for"); + + return false; + } + + running.nick = nick; + running.command = cmd; + console.debug(running); + + return action.run(nick, area.Index); + } + } + running.nick = nick; running.command = cmd; console.debug(running); @@ -1322,7 +1454,7 @@ const ControlBot = new Runnable( if (ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { Chat.say("Not enough votes to start next game. Votes gathered " + ngVote.votes.size); ngVote.reset(); - } else { + } else if (ngVote.elapsed() > Time.seconds(30) && !ngVote.nextGame) { ngVote.checkCount(); } } From 899d14c507553114025a15d84dd37ef520dcc714 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 17 Jan 2025 00:47:58 -0500 Subject: [PATCH 477/758] Update ControlBot.js - Fix leftover `votes` reference --- d2bs/kolbot/libs/scripts/ControlBot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 72aa018fc..5a0af9fd0 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1165,7 +1165,7 @@ const ControlBot = new Runnable( hostileCheck: false, run: function (nick) { if (ngVote.active) { - Chat.say("NGVote is already active. Current count: " + ngVote.votes.size); + Chat.say("NGVote is already active. Current count: " + ngVote.votesYes.size); return; } if (getTickCount() - startTime < Time.minutes(3)) { From bcf15f46c36249c65df7de747dc9d518afda510e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 17 Jan 2025 01:29:33 -0500 Subject: [PATCH 478/758] Add handler for guards blocking exit from act 2 palace --- d2bs/kolbot/libs/core/Pather.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 77232e5ae..00487788b 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -502,9 +502,9 @@ const Pather = { } } } - } else if (fail > 0 && me.inArea(sdk.areas.LutGholein) && me.x > 5122 && me.y <= 5049) { + } else if (fail > 0 && me.inArea(sdk.areas.LutGholein)) { // 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) { + if (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)) { @@ -513,6 +513,10 @@ const Pather = { Pather.walkTo(node[0], node[1]); }); } + } else if (me.x >= 5051 && me.x <= 5068 && me.y <= 5145) { + // we might not be able to get past the guards - rare but can happen to rushers + Pather.moveToExit(sdk.areas.HaremLvl1, true); + Pather.makePortal(true); } } From 70b30f3b2aaa936f7d2a047c6d53e5b3091f0a79 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 17 Jan 2025 01:52:20 -0500 Subject: [PATCH 479/758] Add gidbinn to controlbot rush commands --- d2bs/kolbot/libs/config/Amazon.js | 1 + d2bs/kolbot/libs/config/Assassin.js | 1 + d2bs/kolbot/libs/config/Barbarian.js | 1 + d2bs/kolbot/libs/config/Druid.js | 1 + d2bs/kolbot/libs/config/Necromancer.js | 1 + d2bs/kolbot/libs/config/Paladin.js | 1 + d2bs/kolbot/libs/config/Sorceress.js | 1 + d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/libs/scripts/ControlBot.js | 4 +++ d2bs/kolbot/libs/systems/autorush/AutoRush.js | 35 +++++++++++++++++++ d2bs/kolbot/sdk/types/Config.d.ts | 1 + 11 files changed, 48 insertions(+) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index e772e71a4..36db5705b 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -259,6 +259,7 @@ function LoadConfig () { Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 958b5cf0d..9740e4168 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -259,6 +259,7 @@ function LoadConfig () { Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 62de3fe8b..5e3ba1b0e 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -259,6 +259,7 @@ function LoadConfig () { Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 47eeeff64..0f40f20ae 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -259,6 +259,7 @@ function LoadConfig () { Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 45749566e..e16545f8f 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -259,6 +259,7 @@ function LoadConfig () { Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 8885ecbc1..26cc1ebca 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -259,6 +259,7 @@ function LoadConfig () { Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 66f6f95de..286a14243 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -259,6 +259,7 @@ function LoadConfig () { Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 961353269..84950edfa 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -617,6 +617,7 @@ let Config = { Staff: false, Summoner: false, Duriel: false, + Gidbinn: false, LamEsen: false, Eye: false, Heart: false, diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 5a0af9fd0..ea8d5afa9 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -22,6 +22,7 @@ const ControlBot = new Runnable( staff, summoner, duriel, + gidbinn, lamesen, brain, heart, @@ -1278,6 +1279,9 @@ const ControlBot = new Runnable( if (Config.ControlBot.Rush.Duriel) { _actions.set("duri", new RushAction("Rush Duriel", duriel)); } + if (Config.ControlBot.Rush.Gidbinn) { + _actions.set("gidbinn", new RushAction("Rush Gidbinn", gidbinn)); + } if (Config.ControlBot.Rush.LamEsen) { _actions.set("lamesen", new RushAction("Rush Lamesen", lamesen)); } diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 3c48518f1..5fc0cef1c 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -669,6 +669,40 @@ return true; }; /** @param {string} [nick] */ + const gidbinn = function (nick) { + log("starting gidbinn"); + + Town.doChores(); + Pather.useWaypoint(sdk.areas.FlayerJungle, true) && Precast.doPrecast(true); + if (!Pather.moveToPreset(sdk.areas.FlayerJungle, sdk.unittype.Object, sdk.quest.chest.GidbinnAltar)) { + throw new Error("gidbinn failed"); + } + const altar = Game.getObject(sdk.quest.chest.GidbinnAltar); + if (!altar) { + throw new Error("gidbinn failed - couldn't find altar"); + } + Misc.poll(function () { + altar.interact(); + return altar.mode === sdk.objects.mode.Active; + }, Time.minutes(1), Time.seconds(1)); + Attack.securePosition(me.x, me.y, 30, 3000, true); + Pather.makePortal(); + log(AutoRush.playersIn); + if (!Misc.poll(function () { + return playerIn(me.area, nick); + }, AutoRush.playerWaitTimeout, 1000)) { + log("timed out"); + return false; + } + if (AutoRush.rushMode !== RushModes.chanter) { + while (playerIn(me.area, nick)) { + delay(100); + } + } + Pather.usePortal(null, me.name); + return true; + }; + /** @param {string} [nick] */ const lamesen = function (nick) { log("starting lamesen"); @@ -1339,6 +1373,7 @@ baal: baal, cain: cain, radament: radament, + gidbinn: gidbinn, lamesen: lamesen, izual: izual, shenk: shenk, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index a9fbd50fc..9ee3818b8 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -400,6 +400,7 @@ declare global { Staff: boolean; Summoner: boolean; Duriel: boolean; + Gidbinn: boolean; LamEsen: boolean; Eye: boolean; Heart: boolean; From f4986d9d97dbb09a1c6364ef02ebec82391c6991 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 17 Jan 2025 01:53:14 -0500 Subject: [PATCH 480/758] Fix commands from non-partied members --- d2bs/kolbot/libs/scripts/ControlBot.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index ea8d5afa9..60f2331aa 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1346,6 +1346,10 @@ const ControlBot = new Runnable( if (!command || command.length < 2) return false; console.debug("Checking command: " + command); let [cmd, nick, full] = command; + if (!Misc.inMyParty(nick)) { + Chat.say("Accept party invite, noob. Cmds only allowed for party members."); + return false; + } if (cmd.match(/^rush /gi)) { cmd = cmd.split(" ")[1]; } From e9bf25753378e706b2fe18c7cae9037efcf5b514 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 19 Jan 2025 01:49:01 -0500 Subject: [PATCH 481/758] fix: shriner attempting to get shrines in different area - Check that the shrine we are checking is in the same area as we are --- d2bs/kolbot/libs/core/Misc.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index eb1902749..42232117f 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -666,6 +666,9 @@ const Misc = (function () { if (shrine) { do { if (!shrine.name) continue; + // don't leave our area to grab shrines + // TODO: better fix for this as it'd be okay for small detours but orginally found this at halls of vaught stairs attempting to get shrine that was on next level + if (!me.inArea(shrine.area)) continue; let _name = shrine.name.toLowerCase(); if ((_name.includes("shrine") && ShrineData.has(shrine.objtype) || (_name.includes("well"))) && ShrineData.has(shrine.objtype) From dfdcbcc2307c3e91fe331ef56ad73b8aa34ea12b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:28:31 -0500 Subject: [PATCH 482/758] Update ControlBot missed changing ref to old `ngVote.votes` prop --- d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + d2bs/kolbot/libs/scripts/ControlBot.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 3b80c5e2e..156be5e40 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -240,6 +240,7 @@ Config.ControlBot.Rush.Amulet = true; // Get amulet on command Config.ControlBot.Rush.Summoner = true; // Kill Summoner on command Config.ControlBot.Rush.Duriel = true; // Kill Duriel on command + Config.ControlBot.Rush.Gidbinn = true; // Clear Gidbinn altar on command Config.ControlBot.Rush.LamEsen = true; // Get LamEsen's tome on command Config.ControlBot.Rush.Eye = true; // Get Khalim's eye on command Config.ControlBot.Rush.Heart = true; // Get Khalim's heart on command diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 60f2331aa..958bc9f32 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1460,7 +1460,7 @@ const ControlBot = new Runnable( if (ngVote.active) { if (ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { - Chat.say("Not enough votes to start next game. Votes gathered " + ngVote.votes.size); + Chat.say("Not enough votes to start next game. Votes gathered " + ngVote.votesYes.size); ngVote.reset(); } else if (ngVote.elapsed() > Time.seconds(30) && !ngVote.nextGame) { ngVote.checkCount(); From 4c1e6855ac1753cf53a376fa3daaa3ea5b029c24 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 21 Jan 2025 01:55:36 -0500 Subject: [PATCH 483/758] Add `Config.AnnounceGameTimeRemaining` - Let bot announce how much time is left in game if the MinGameTime hasn't been met yet. Useful for followers of public runs to have an idea of when the next game is going to be up --- d2bs/kolbot/default.dbj | 8 ++++++++ d2bs/kolbot/libs/config/Amazon.js | 1 + d2bs/kolbot/libs/config/Assassin.js | 1 + d2bs/kolbot/libs/config/Barbarian.js | 1 + d2bs/kolbot/libs/config/Druid.js | 1 + d2bs/kolbot/libs/config/Necromancer.js | 1 + d2bs/kolbot/libs/config/Paladin.js | 1 + d2bs/kolbot/libs/config/Sorceress.js | 1 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/sdk/types/Config.d.ts | 1 + 11 files changed, 18 insertions(+) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 4364c2d84..ba977987a 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -237,6 +237,14 @@ function main () { try { Town.goToTown(); + if (Config.AnnounceGameTimeRemaing) { + say( + "Next game in " + + Math.round(((startTime + Config.MinGameTime) - getTickCount()) / 1000) + + " seconds." + ); + } + while (getTickCount() - startTime < Config.MinGameTime * 1000) { me.overhead( "Stalling for " diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 36db5705b..a7dbcd8e7 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -498,6 +498,7 @@ function LoadConfig () { Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 9740e4168..e333058a2 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -498,6 +498,7 @@ function LoadConfig () { Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 5e3ba1b0e..dc7d744b3 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -498,6 +498,7 @@ function LoadConfig () { Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 0f40f20ae..5900d056d 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -498,6 +498,7 @@ function LoadConfig () { Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index e16545f8f..d03b2ead2 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -498,6 +498,7 @@ function LoadConfig () { Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 26cc1ebca..721c30513 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -498,6 +498,7 @@ function LoadConfig () { Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 286a14243..9dc295474 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -497,6 +497,7 @@ function LoadConfig () { Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 156be5e40..1e5344f03 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -482,6 +482,7 @@ Config.ShitList = false; // Blacklist hostile players so they don't get invited to party. Config.UnpartyShitlisted = false; // Leave party if someone invited a blacklisted player. Config.LastMessage = ""; // Message or array of messages to say at the end of the run. Use $nextgame to say next game - "Next game: $nextgame" (works with lead entry point) + Config.AnnounceGameTimeRemaing = false; // Announce time remaing in game if MinGameTime is set and hasn't been reached // Shrine Scanner - scan for shrines while moving. // Put the shrine types in order of priority (from highest to lowest). For a list of types, see sdk/txt/shrines.txt diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 84950edfa..b49ef2f3c 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -220,6 +220,7 @@ let Config = { Silence: false, PublicMode: false, PartyAfterScript: false, + AnnounceGameTimeRemaing: false, /** @type {string[]} */ Greetings: [], diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 9ee3818b8..9beba3a06 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -83,6 +83,7 @@ declare global { Greetings: any[]; DeathMessages: any[]; Congratulations: any[]; + AnnounceGameTimeRemaing: boolean; ShitList: boolean; UnpartyShitlisted: boolean; Leader: string; From 4a777e30965591a62f02626cb35b79ea093bf98d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:39:44 -0500 Subject: [PATCH 484/758] Add polyfill for `Array.prototype.with()` --- d2bs/kolbot/libs/Polyfill.js | 23 +++++++++++++++++++++++ d2bs/kolbot/sdk/globals.d.ts | 9 +++++++++ 2 files changed, 32 insertions(+) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 3b48b5cfb..58f23aae8 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -676,6 +676,29 @@ if (!Array.prototype.toSpliced) { }; } +/** + * @description The with() method of Array instances is the copying version of using the bracket notation to change the value of a given index. + * It returns a new array with the element at the given index replaced with the given value. + * @param {number} index - Zero-based index at which to change the array, converted to an integer. + * @param {*} value - Any value to be assigned to the given index. + * @returns {Array} A new array with the element at index replaced with value. + * @throws {RangeError} If index >= array.length or index < -array.length. + */ +if (!Array.prototype.with) { + Array.prototype.with = function (index, value) { + const len = this.length; + const relativeIndex = index < 0 ? len + index : index; + + if (relativeIndex < 0 || relativeIndex >= len) { + throw new RangeError("Index out of range"); + } + + const newArray = this.slice(); + newArray[relativeIndex] = value; + return newArray; + }; +} + /** * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Object Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 4cfaab56c..a2fb6536d 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -81,6 +81,15 @@ declare global { * @returns {Array} A new array with the removed elements and optionally added elements. */ toSpliced(start: number, deleteCount?: number, ...items: T[]): T[]; + /** + * @description The with() method of Array instances is the copying version of using the bracket notation to change the value of a given index. + * It returns a new array with the element at the given index replaced with the given value. + * @param {number} index - Zero-based index at which to change the array, converted to an integer. + * @param {*} value - Any value to be assigned to the given index. + * @returns {Array} A new array with the element at index replaced with value. + * @throws {RangeError} If index >= array.length or index < -array.length. + */ + with(index: number, value: T): T[]; } interface String { From 1159245af32be6d01a362eefddf5ea9b7fa56bc9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 24 Jan 2025 17:16:49 -0500 Subject: [PATCH 485/758] feat: Create `Config.AdvancedCustomAttack` - `AdvancedCustomAttack` allows for more fine grain control over attacks than just checking classid/name --- d2bs/kolbot/libs/config/Amazon.js | 10 ++++++++ d2bs/kolbot/libs/config/Assassin.js | 10 ++++++++ d2bs/kolbot/libs/config/Barbarian.js | 10 ++++++++ d2bs/kolbot/libs/config/Druid.js | 10 ++++++++ d2bs/kolbot/libs/config/Necromancer.js | 10 ++++++++ d2bs/kolbot/libs/config/Paladin.js | 10 ++++++++ d2bs/kolbot/libs/config/Sorceress.js | 10 ++++++++ d2bs/kolbot/libs/config/_BaseConfigFile.js | 28 ++++++++++++++++++++++ d2bs/kolbot/libs/core/Attack.js | 8 +++++++ d2bs/kolbot/libs/core/Config.js | 2 ++ d2bs/kolbot/sdk/types/Config.d.ts | 4 +++- 11 files changed, 111 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index a7dbcd8e7..ec7cce355 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -569,6 +569,16 @@ function LoadConfig () { // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = []; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index e333058a2..fc39912dc 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -569,6 +569,16 @@ function LoadConfig () { // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = []; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index dc7d744b3..3d3f942a8 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -566,6 +566,16 @@ function LoadConfig () { // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = []; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 5900d056d..77274e647 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -569,6 +569,16 @@ function LoadConfig () { // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = []; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index d03b2ead2..f652ccc5e 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -569,6 +569,16 @@ function LoadConfig () { // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = []; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 721c30513..0af908f99 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -569,6 +569,16 @@ function LoadConfig () { // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = []; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 9dc295474..4525a9427 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -568,6 +568,16 @@ function LoadConfig () { // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = []; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 1e5344f03..ecc2115d7 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -563,6 +563,34 @@ // "Monster Name": [-1, -1] }; + /** + * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * Advanced Attack config. Allows custom skills to be used on custom conditions. + * Each entry in the array should be an object with a `check` function and an `attack` array. + * The `check` function determines whether the custom attack should be used on a given monster. + * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * + * Example: + * [ + * { + * check: function (unit) { + * return unit.getEnchant(sdk.enchant.LightningEnchanted); + * }, + * attack: [sdk.skills.Zeal, sdk.skills.Salvation] + * }, + * ] + * + * Multiple entries are separated by commas. + */ + Config.AdvancedCustomAttack = [ + // { + // check: function (unit) { + // return unit.getEnchant(sdk.enchant.LightningEnchanted); + // }, + // attack: [sdk.skills.Zeal, sdk.skills.Salvation] + // }, + ]; + /** * Advanced PreAttack config. Allows custom skills to be used on custom monsters. * Format: "Monster Name": [skill id, weapon slot] diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 94fe87652..8fb487db3 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -142,6 +142,14 @@ const Attack = { // Check if unit got invalidated if (!unit || !unit.name || !copyUnit(unit).x) return false; + for (let el of Config.AdvancedCustomAttack) { + if (el.hasOwnProperty("check") && el.hasOwnProperty("attack")) { + if (typeof el.check === "function" && el.check(unit)) { + return el.attack; + } + } + } + for (let i in Config.CustomAttack) { if (Config.CustomAttack.hasOwnProperty(i)) { // if it contains numbers but is a string, convert to an int diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index b49ef2f3c..025ee987b 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -360,6 +360,8 @@ let Config = { CustomAttack: {}, /** @type {Record} */ CustomPreAttack: {}, + /** @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} */ + AdvancedCustomAttack: [], TeleStomp: false, NoTele: false, ClearType: false, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 9beba3a06..3b771e467 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -177,7 +177,9 @@ declare global { DodgeHP: number; AttackSkill: any[]; LowManaSkill: any[]; - CustomAttack: {}; + CustomAttack: Record; + CustomPreAttack: Record, + AdvancedCustomAttack: { check: (unit: Monster) => boolean, attack: [number, number] }[], TeleStomp: boolean; NoTele: boolean; ClearType: boolean; From aedd0d76485c291196275b7b9eff63725a1d1a79 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Jan 2025 21:03:53 -0500 Subject: [PATCH 486/758] feat: Add shared context object to Runnable class - Introduced a shared `ctx` object to the `Runnable` class. - The `ctx` object allows for better state management and data sharing between different parts of the script. - Updated existing methods to utilize the shared `ctx` object for consistent state handling. --- d2bs/kolbot/libs/core/Loader.js | 52 ++++++++++++++----------------- d2bs/kolbot/sdk/types/Loader.d.ts | 28 ++++++++++++----- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 6cc3299c7..cd774b236 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -5,17 +5,6 @@ * */ -/** @typedef {function(): boolean} GlobalScript */ -// TODO: preaction/postaction -/** - * @typedef {Object} RunnableOptions - * @property {function(): any} preAction - * @property {function(): boolean} postAction - * @property {function(): any} cleanup - * @property {boolean} forceTown - * @property {number} bossid - * @property {number} startArea - */ /** * @constructor @@ -25,14 +14,15 @@ function Runnable (action, options = {}) { this.action = action; this.startArea = options.hasOwnProperty("startArea") ? options.startArea : null; + this.setup = options.hasOwnProperty("setup") ? options.setup : null; this.preAction = options.hasOwnProperty("preAction") ? options.preAction : function chores () { - // TODO: We need to do a dry-run of chores to actually determine if we need it or not - if (getTickCount() - Town.lastChores > Time.minutes(1)) { - Town.doChores(); - } - }; + // TODO: We need to do a dry-run of chores to actually determine if we need it or not + if (getTickCount() - Town.lastChores > Time.minutes(1)) { + Town.doChores(); + } + }; this.postAction = options.hasOwnProperty("postAction") ? options.postAction : null; this.cleanup = options.hasOwnProperty("cleanup") ? options.cleanup : null; this.forceTown = options.hasOwnProperty("forceTown") ? options.forceTown : false; @@ -70,10 +60,11 @@ const Loader = { } }, - _runCurrent: function () { + /** @param {ScriptContext} ctx */ + _runCurrent: function (ctx) { return this.currentScript instanceof Runnable - ? this.currentScript.action() - : this.currentScript(); + ? this.currentScript.action(ctx) + : this.currentScript(ctx); }, /** @@ -156,7 +147,8 @@ const Loader = { } for (Loader.scriptIndex = 0; Loader.scriptIndex < Loader.scriptList.length; Loader.scriptIndex++) { - let script = this.scriptList[this.scriptIndex]; + const ctx = {}; + const script = this.scriptList[this.scriptIndex]; if (this.fileList.indexOf(script) === -1) { if (FileTools.exists("scripts/" + script + ".js")) { @@ -203,7 +195,7 @@ const Loader = { } if (preAction && typeof preAction === "function") { - preAction(); + preAction(ctx); } if (startArea && me.inArea(startArea)) { @@ -248,7 +240,7 @@ const Loader = { say("nextup " + script); } - if (Loader._runCurrent()) { + if (Loader._runCurrent(ctx)) { let gain = Math.max(me.getStat(sdk.stats.Experience) - exp, 0); let duration = Time.elapsed(tick); console.log( @@ -273,7 +265,7 @@ const Loader = { // run cleanup if applicable if (Loader.currentScript instanceof Runnable) { if (Loader.currentScript.cleanup && typeof Loader.currentScript.cleanup === "function") { - Loader.currentScript.cleanup(); + Loader.currentScript.cleanup(ctx); } } // remove script function from global scope, so it can be cleared by GC @@ -302,7 +294,7 @@ const Loader = { /** * @param {string} script - * @param {Object | function(): any} configOverride + * @param {Partial | function(): any} configOverride * @returns {boolean} */ runScript: function (script, configOverride) { @@ -331,9 +323,10 @@ const Loader = { Loader.currentScript = global[script]; if (isIncluded("scripts/" + script + ".js")) { + const ctx = {}; try { if (Loader.currentScript instanceof Runnable) { - const { startArea, bossid } = Loader.currentScript; + const { startArea, bossid, preAction } = Loader.currentScript; if (startArea && me.inArea(startArea)) { Loader.skipTown.push(script); @@ -343,6 +336,10 @@ const Loader = { console.log("ÿc2Skipping script: ÿc9" + script + " ÿc2- Boss already killed."); return true; } + + if (preAction && typeof preAction === "function") { + preAction(ctx); + } } else if (typeof (Loader.currentScript) !== "function") { throw new Error("Invalid script function name"); } @@ -371,7 +368,7 @@ const Loader = { let tick = getTickCount(); let exp = me.getStat(sdk.stats.Experience); - if (Loader._runCurrent()) { + if (Loader._runCurrent(ctx)) { console.log( mainScriptStr + "ÿc7" + script + " :: ÿc0Complete ÿc0- ÿc7Duration: ÿc0" + (Time.format(getTickCount() - tick)) @@ -405,10 +402,9 @@ const Loader = { // run cleanup if applicable if (Loader.currentScript instanceof Runnable) { if (Loader.currentScript.cleanup && typeof Loader.currentScript.cleanup === "function") { - Loader.currentScript.cleanup(); + Loader.currentScript.cleanup(ctx); } } - Loader.currentScript = null; Loader.tempList.pop(); diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index bf17e124a..2bbe902a7 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -1,14 +1,28 @@ export {}; declare global { type GlobalScript = () => boolean; - interface Runnable { + type ScriptContext = { [key: string]: any }; + interface RunnableOptions { + setup?: (ctx: ScriptContext) => any; + preAction?: (ctx: ScriptContext) => any; + postAction?: (ctx: ScriptContext) => any; + cleanup?: (ctx: ScriptContext) => any; + forceTown?: boolean; + bossid?: number; + startArea?: number; + } + + class Runnable { + constructor(action: () => boolean, options: Partial); + + action: (ctx: ScriptContext) => boolean; startArea: number | null; + setup: ((ctx: ScriptContext) => any) | null; + preAction: (ctx: ScriptContext) => any; + postAction: ((ctx: ScriptContext) => any) | null; + cleanup: ((ctx: ScriptContext) => any) | null; forceTown: boolean; bossid: number | null; - preAction: () => any; - action: () => boolean; - postAction: () => any; - cleanup: () => any; } namespace Loader { @@ -24,11 +38,11 @@ declare global { function init(): void; function getScripts(): void; - function _runCurrent(): boolean; + function _runCurrent(ctx: ScriptContext): boolean; function clone(obj: any): void; function copy(from: any, to: any): void; function loadScripts(): void; - function runScript(name: string, configOverride: Object | (() => any)): boolean; + function runScript(name: string, configOverride: Partial | (() => any)): boolean; function scriptName(offset?: number): string; } From db1a452280df5e4c7dd0c7a60ffb5f5a028c7119 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Jan 2025 21:12:31 -0500 Subject: [PATCH 487/758] patch: Open stash if not opened when moving from stash -> invo --- d2bs/kolbot/libs/core/Storage.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index a483c06cf..a644c5cb8 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -479,6 +479,9 @@ // Make sure stash is open if (this.location === sdk.storage.Stash && !Town.openStash()) return false; + if (this.location === sdk.storage.Inventory && item.location === sdk.storage.Stash && !Town.openStash()) { + return false; + } const [orgX, orgY, orgLoc] = [item.x, item.y, item.location]; const moveItem = (x, y, location) => { From 7b9eb3ce30988598716e5ebf057e213d903fd772 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 Jan 2025 15:48:54 -0500 Subject: [PATCH 488/758] feat: Add `me.getOwned` for building item list of given criteria more declaratively --- d2bs/kolbot/libs/core/Me.js | 37 ++++++++++++++++++++++++++++++++++++ d2bs/kolbot/sdk/globals.d.ts | 12 ++++++++++++ 2 files changed, 49 insertions(+) diff --git a/d2bs/kolbot/libs/core/Me.js b/d2bs/kolbot/libs/core/Me.js index 352c49eab..71c04ae57 100644 --- a/d2bs/kolbot/libs/core/Me.js +++ b/d2bs/kolbot/libs/core/Me.js @@ -853,6 +853,43 @@ me.switchToPrimary = function () { return me.switchWeapons(Attack.getPrimarySlot()); }; +/** + * Get a list of items that match the given criteria. + * @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[]} + */ +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.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()); + } + + return itemList; +}; + /** * Misc functions, stats/modes/states/ etc */ diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index a2fb6536d..4f0787428 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -563,6 +563,17 @@ declare global { useUnit(targetArea?: number): boolean; } + type GetOwnedSettings = { + itemType?: number, + classid?: number, + mode?: number, + quality?: number, + sockets?: number, + location?: number, + ethereal?: boolean, + cb?: (item: ItemUnit) => boolean, + }; + interface MeType extends Unit { public type: PlayerType; readonly account: string; @@ -724,6 +735,7 @@ declare global { getItemsForRepair(repairPercent: number, chargedItems: boolean): ItemUnit[]; castingFrames(skillId: number, fcr?: number, charClass?: number): number; castingDuration(skillId: number, fcr?: number, charClass?: number): number; + getOwned(itemInfo: ItemUnit | GetOwnedSettings): ItemUnit[]; // #checkers? needBeltPots(): boolean; From ea0b5c72bbaa2bb80eaf0418e6f84178a3318b15 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 Jan 2025 16:27:44 -0500 Subject: [PATCH 489/758] feat: Extend `Config.AutoMule.Trigger` to accept callbacks - Enhanced the `matchItem` function to support processing callback functions within the list parameter. - Callbacks in the list can now be used to perform custom checks on items, providing greater flexibility and customization. - This allows users to define more complex and dynamic item matching logic. --- d2bs/kolbot/libs/config/_BaseConfigFile.js | 16 +++++++++++--- d2bs/kolbot/libs/systems/automule/AutoMule.js | 21 ++++++++++++------- d2bs/kolbot/sdk/types/Config.d.ts | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index ecc2115d7..fb34dfa24 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -932,11 +932,21 @@ * List can either be set as string in pickit format and/or as number referring to item classids. Each entries are separated by commas. * Example : * Config.AutoMule.Trigger = [639, 640, "[type] == ring && [quality] == unique # [maxmana] == 20"]; - * This will initiate muling when your character finds Ber, Jah, or SOJ. + * - This will initiate muling when your character finds Ber, Jah, or SOJ. + * ADVANCED USAGE OF TRIGGER: + * Config.AutoMule.Trigger = [ + * function (item) { + * return ( + * item.classid === sdk.items.quest.KeyofTerror + * && me.getOwned({ classid: sdk.items.quest.KeyofTerror }).length === 3 + * ); + * }, + * ]; + * - This will initiate muling if the item being checked is the Key of Terror and we own 3 of them * Config.AutoMule.Force = [561, 566, 571, 576, 581, 586, 601]; - * This will mule perfect gems/skull during muling. + * - This will mule perfect gems/skull during muling. * Config.AutoMule.Exclude = ["[name] >= talrune && [name] <= solrune", "[name] >= 654 && [name] <= 657"]; - * This will exclude muling of runes from tal through sol, and any essences. + * - This will exclude muling of runes from tal through sol, and any essences. */ Config.AutoMule.Trigger = []; Config.AutoMule.Force = []; diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index 1ed022818..6108aa460 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -492,25 +492,30 @@ const AutoMule = { /** * @param {ItemUnit} item - * @param {string[] | number[]} list + * @param {(number | string | ((item: ItemUnit) => boolean))[]} list * @returns {boolean} */ matchItem: function (item, list) { - let parsedPickit = [], classIDs = []; + const parsedPickit = []; + const classIDs = []; - for (let i = 0; i < list.length; i += 1) { + for (let check of list) { let info = { file: "Character Config", - line: list[i] + line: check }; // classids - if (typeof list[i] === "number") { - classIDs.push(list[i]); - } else if (typeof list[i] === "string") { + if (typeof check === "number") { + classIDs.push(check); + } else if (typeof check === "string") { // pickit entries - let parsedLine = NTIP.ParseLineInt(list[i], info); + let parsedLine = NTIP.ParseLineInt(check, info); parsedLine && parsedPickit.push(parsedLine); + } else if (typeof check === "function") { + if (check(item)) { + return true; + } } } diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 3b771e467..50d296526 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -115,7 +115,7 @@ declare global { ScanShrines: any[]; Debug: boolean; AutoMule: { - Trigger: any[]; + Trigger: (number | string | ((item: ItemUnit) => boolean))[]; Force: any[]; Exclude: any[]; }; From f600e1cb1cb3a62196803d83518732030f45bd5a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 26 Jan 2025 16:43:16 -0500 Subject: [PATCH 490/758] patch: Add missing hook calls for `setup` & `postAction` in Loader --- d2bs/kolbot/libs/core/Loader.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index cd774b236..d268a2275 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -183,7 +183,7 @@ const Loader = { if (isIncluded("scripts/" + script + ".js")) { try { if (Loader.currentScript instanceof Runnable) { - const { startArea, bossid, preAction } = Loader.currentScript; + const { startArea, bossid, preAction, setup } = Loader.currentScript; if (startArea && Loader.scriptIndex === 0) { Loader.firstScriptAct = sdk.areas.actOf(startArea); @@ -194,6 +194,10 @@ const Loader = { continue; } + if (setup && typeof setup === "function") { + setup(ctx); + } + if (preAction && typeof preAction === "function") { preAction(ctx); } @@ -251,6 +255,14 @@ const Loader = { + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) ); this.doneScripts.add(script); + + if (Loader.currentScript instanceof Runnable) { + const { postAction } = Loader.currentScript; + + if (postAction && typeof postAction === "function") { + postAction(ctx); + } + } } } } catch (error) { @@ -337,6 +349,10 @@ const Loader = { return true; } + if (setup && typeof setup === "function") { + setup(ctx); + } + if (preAction && typeof preAction === "function") { preAction(ctx); } @@ -383,6 +399,14 @@ const Loader = { + "ÿc7 - Exp/minute: ÿc0" + (gain / (duration / 60000)).toFixed(2) ); this.doneScripts.add(script); + + if (Loader.currentScript instanceof Runnable) { + const { postAction } = Loader.currentScript; + + if (postAction && typeof postAction === "function") { + postAction(ctx); + } + } } } } catch (error) { From d6af2cb512753a4425a20ec0fc5683fe67cbe1f3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 27 Jan 2025 20:45:44 -0500 Subject: [PATCH 491/758] feat: Add `Object.hasOwn` polyfill --- d2bs/kolbot/libs/Polyfill.js | 6 ++++++ d2bs/kolbot/sdk/globals.d.ts | 1 + 2 files changed, 7 insertions(+) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 58f23aae8..8072b41fb 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -759,6 +759,12 @@ if (!Object.entries) { }; } +if (!Object.hasOwn) { + Object.hasOwn = function (obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + }; +} + // eslint-disable-next-line no-var if (typeof global === "undefined") var global = [].filter.constructor("return this")(); // eslint-disable-next-line dot-notation diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 4f0787428..72793729a 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -116,6 +116,7 @@ declare global { values(source: object): any[]; entries(source: object): any[][]; is(o1: any, o2: any): boolean; + hasOwn(obj: object, prop: string): boolean; } interface Object { From 497b82d1d763ce573c99b926612f0fc255f7e6e0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:46:25 -0500 Subject: [PATCH 492/758] bugfix: Keep track of parent script when running `Loader.runScript` via ctx so it can be restored --- d2bs/kolbot/libs/core/Loader.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index d268a2275..2e137d126 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -332,10 +332,13 @@ const Loader = { return false; } - Loader.currentScript = global[script]; if (isIncluded("scripts/" + script + ".js")) { - const ctx = {}; + const ctx = { + _parent: Loader.currentScript + }; + Loader.currentScript = global[script]; + try { if (Loader.currentScript instanceof Runnable) { const { startArea, bossid, preAction } = Loader.currentScript; @@ -429,7 +432,7 @@ const Loader = { Loader.currentScript.cleanup(ctx); } } - Loader.currentScript = null; + Loader.currentScript = ctx._parent; Loader.tempList.pop(); if (reconfiguration) { From 4208f410295d73f505554cfb44704b5887fc60f3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:23:13 -0500 Subject: [PATCH 493/758] Update Loader.js - Missed destructuring setup --- d2bs/kolbot/libs/core/Loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index 2e137d126..b5b19f1ad 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -341,7 +341,7 @@ const Loader = { try { if (Loader.currentScript instanceof Runnable) { - const { startArea, bossid, preAction } = Loader.currentScript; + const { startArea, bossid, preAction, setup } = Loader.currentScript; if (startArea && me.inArea(startArea)) { Loader.skipTown.push(script); From 5e05606616049e636f68a3d34bb31eed538da117 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 26 Feb 2025 18:46:35 -0500 Subject: [PATCH 494/758] Update ngVoting for ControlBot to reduce wait period if only character in game - Fix endless chanting poppy summon - Fix 5 second wait during wp giving --- d2bs/kolbot/libs/scripts/ControlBot.js | 153 ++++++++++++++++--------- 1 file changed, 98 insertions(+), 55 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 958bc9f32..79dba4ea4 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -90,70 +90,97 @@ const ControlBot = new Runnable( AutoRush.playersOut = "out"; AutoRush.allIn = "all in"; - const ngVote = new function () { - /** @type {Set} */ - this.votesYes = new Set(); - /** @type {Set} */ - this.votesNo = new Set(); - this.active = false; - this.tick = 0; - this.nextGame = false; - - this.votesNeeded = function () { + const ngVote = { + /** @type {Map} */ + votes: new Map(), + active: false, + tick: 0, + nextGame: false, + + /** + * Calculate the number of votes needed for a decision + * @returns {number} + */ + votesNeeded: function () { return Math.max(1, Math.floor((Misc.getPlayerCount() - 2) / 2)); - }; - this.reset = function () { - this.votesYes.clear(); - this.votesNo.clear(); + }, + + /** + * Reset the voting state + */ + reset: function () { + this.votes.clear(); this.tick = 0; this.active = false; - }; - this.begin = function () { + }, + + /** + * Begin a new voting session + */ + begin: function() { this.active = true; - this.votesYes.clear(); - this.votesNo.clear(); + this.votes.clear(); this.tick = getTickCount(); - }; - this.checkCount = function () { - // ensure we've counted everyones votes when checking for a draw - if (Misc.getPartyCount() === this.votesYes.size + this.votesNo.size) { - if (this.votesYes.size === this.votesNo.size) { - Chat.say("Not enough votes to start ng we have a draw."); - this.reset(); - return false; - } - } + }, + + /** + * Check current count + * @param {"yes" | "no"} type + */ + count: function (type) { + return type === "yes" + ? Array.from(this.votes.values()).filter(vote => vote === "yes").length + : Array.from(this.votes.values()).filter(vote => vote === "no").length; + }, + + /** + * Check the current vote count and determine the outcome + * @returns {boolean} + */ + checkCount: function() { const votesNeeded = this.votesNeeded(); - if (this.votesNo.size >= votesNeeded) { + const yesVotes = Array.from(this.votes.values()).filter(vote => vote === "yes").length; + const noVotes = Array.from(this.votes.values()).filter(vote => vote === "no").length; + + if (Misc.getPartyCount() === yesVotes + noVotes && yesVotes === noVotes) { + Chat.say("Not enough votes to start ng we have a draw."); + this.reset(); + return false; + } + + if (noVotes >= votesNeeded) { Chat.say("ng rejected by majority."); this.reset(); return false; } - const reqMet = this.votesYes.size >= votesNeeded; - if (reqMet) { + + if (yesVotes >= votesNeeded) { Chat.say("ng approved by majority."); - ngVote.nextGame = true; + this.nextGame = true; this.reset(); + return true; } - return reqMet; - }; + + return false; + }, + /** + * Register a vote * @param {string} nick * @param {"yes" | "no"} type */ - this.vote = function (nick, type) { + vote: function(nick, type) { if (!this.active) return; - if (type === "yes") { - this.votesNo.delete(nick); - this.votesYes.add(nick); - } else if (type === "no") { - this.votesYes.delete(nick); - this.votesNo.add(nick); - } - }; - this.elapsed = function () { + this.votes.set(nick, type); + }, + + /** + * Get the elapsed time since the vote started + * @returns {number} + */ + elapsed: function() { return getTickCount() - this.tick; - }; + } }; const MAX_CHAT_LENGTH = 180; const MIN_GOLD = 500000; @@ -391,6 +418,10 @@ const ControlBot = new Runnable( if (unit) { do { + if (unit.classid === sdk.summons.Poppy) { + console.log(unit); + continue; + } // merc or any other owned unit let parent = unit.getParent(); if (!parent) continue; @@ -724,7 +755,7 @@ const ControlBot = new Runnable( Chat.say(getAreaName(me.area) + " TP up"); if (!Misc.poll(() => (Game.getPlayer(nick)), Time.seconds(30), Time.seconds(1))) { - Chat.say("Aborting wp giving."); + Chat.say(nick + " didn't show up. Aborting wp giving."); } Town.doChores(); @@ -761,7 +792,7 @@ const ControlBot = new Runnable( if (who !== nick) return; if (msg === "next") { next = true; - } else if (msg === "stop") { + } else if (msg === "stop" || msg === "abort") { stop = true; } }; @@ -788,6 +819,8 @@ const ControlBot = new Runnable( let act = Misc.getPlayerAct(nick); if (!wps.has(act)) return false; + Chat.say("Giving wps for act " + act); + addEventListener("chatmsg", nextWatcher); for (let wp of wps.get(act)) { @@ -808,14 +841,14 @@ const ControlBot = new Runnable( Pather.makePortal(); Chat.say(getAreaName(me.area) + " TP up"); - if (!Misc.poll(() => (Game.getPlayer(nick) || next), Time.seconds(30), Time.seconds(1))) { - Chat.say("Aborting wp giving."); + if (!Misc.poll(() => (Game.getPlayer(nick) || next || stop), Time.seconds(30), Time.seconds(1))) { + Chat.say(nick + " didn't show up. Aborting wp giving."); break; } next = false; - delay(5000); + Misc.poll(() => next || stop, Time.seconds(5), 500); } catch (error) { continue; } @@ -1064,8 +1097,7 @@ const ControlBot = new Runnable( case 0x03: // "%Name1(%Name2) left our world. Diablo's minions weaken." players.delete(name1); if (ngVote.active) { - ngVote.votesYes.delete(name1); - ngVote.votesNo.delete(name1); + ngVote.votes.delete(name1); } break; @@ -1166,7 +1198,7 @@ const ControlBot = new Runnable( hostileCheck: false, run: function (nick) { if (ngVote.active) { - Chat.say("NGVote is already active. Current count: " + ngVote.votesYes.size); + Chat.say("NGVote is already active. Current count: " + ngVote.count("yes")); return; } if (getTickCount() - startTime < Time.minutes(3)) { @@ -1178,8 +1210,15 @@ const ControlBot = new Runnable( } ngVote.begin(); ngVote.vote(nick, "yes"); + const partyCount = Misc.getPartyCount(); const votesNeeded = ngVote.votesNeeded(); - Chat.say(nick + " voted for next game. Votes Needed: " + votesNeeded + ". Type ngyes/ngno"); + + if (partyCount === 1) { + Chat.say(nick + " since you're the only player in party, skipping wait period. NG"); + ngVote.nextGame = true; + } else { + Chat.say(nick + " voted for next game. Votes Needed: " + votesNeeded + ". Type ngyes/ngno"); + } } }); _actions.set("ngyes", { @@ -1346,6 +1385,10 @@ const ControlBot = new Runnable( if (!command || command.length < 2) return false; console.debug("Checking command: " + command); let [cmd, nick, full] = command; + if (!Misc.findPlayer(nick)) { + Chat.say("Seems " + nick + " left? Skipping " + cmd); + return false; + } if (!Misc.inMyParty(nick)) { Chat.say("Accept party invite, noob. Cmds only allowed for party members."); return false; @@ -1460,7 +1503,7 @@ const ControlBot = new Runnable( if (ngVote.active) { if (ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { - Chat.say("Not enough votes to start next game. Votes gathered " + ngVote.votesYes.size); + Chat.say("Not enough votes to start next game. Votes gathered " + ngVote.count("yes")); ngVote.reset(); } else if (ngVote.elapsed() > Time.seconds(30) && !ngVote.nextGame) { ngVote.checkCount(); From 463406e249be805c2e8261246aea839e4f431ab4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 1 Mar 2025 01:25:21 -0500 Subject: [PATCH 495/758] [Patch] Fix travincal portal clearing for AutoRush - The coordinates aren't static so need to base the nodes to clear off the wp --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 60 +++++++++++++------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 5fc0cef1c..ac2ef5582 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -78,7 +78,8 @@ return true; }; - const cain = function () { + /** @param {string} [nick] */ + const cain = function (nick) { log("starting cain"); Town.doChores(); Pather.useWaypoint(sdk.areas.DarkWood, true) && Precast.doPrecast(true); @@ -118,8 +119,17 @@ tick = getTickCount(); while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram) && Pather.usePortal(sdk.areas.Tristram)) { - break; + if (Pather.getPortal(sdk.areas.Tristram)) { + let playersleftStony = Misc.poll(function () { + if (playerIn(me.area, nick)) { + return true; + } + return false; + }, AutoRush.playerWaitTimeout, 1000); + + if (playersleftStony && Pather.usePortal(sdk.areas.Tristram)) { + break; + } } Attack.securePosition(StoneAlpha.x, StoneAlpha.y, 35, 1000); } @@ -145,6 +155,19 @@ } Attack.securePosition(me.x, me.y, 15, 1000); } + + const playersLeftTrist = Misc.poll(function () { + if (playerIn(me.area, nick)) { + return true; + } + Pather.move(gibbet); + return false; + }, AutoRush.playerWaitTimeout, 1000); + + if (!playersLeftTrist) { + log("timed out"); + return false; + } } } @@ -188,7 +211,6 @@ } Pather.usePortal(null, me.name); - Town.goToTown(2); for (let i = 0; i < 3; i++) { log("a2"); @@ -200,6 +222,7 @@ break; } } + Town.goToTown(2); } return true; @@ -577,8 +600,6 @@ if (me.inTown) { Town.doChores(); Pather.useWaypoint(sdk.areas.CanyonofMagic, true); - } else { - giveWP(); } Precast.doPrecast(true); @@ -621,12 +642,7 @@ Attack.kill(sdk.monsters.Duriel); Pickit.pickItems(); - Pather.teleport = false; - - Pather.moveTo(22579, 15706); - - Pather.teleport = true; - + Pather.moveToEx(22579, 15706, { allowTeleport: false }); Pather.moveTo(22577, 15649, 10); Pather.moveTo(22577, 15609, 10); Pather.makePortal(); @@ -662,6 +678,7 @@ Town.goToTown(3); Town.doChores(); } else if (AutoRush.rushMode === RushModes.chanter) { + Pather.makePortal(); Pather.moveToExit([sdk.areas.HaremLvl1, sdk.areas.LutGholein], true); Pather.useWaypoint(sdk.areas.RogueEncampment); } @@ -856,22 +873,21 @@ const portalSpot = new PathNode(wpCoords.x + 23, wpCoords.y - 102); [ - new PathNode(4742, 2179), - new PathNode(4738, 2133), - new PathNode(4768, 2150), - new PathNode(4762, 2106), + new PathNode(wpCoords.x + 4, wpCoords.y - 47), + new PathNode(wpCoords.x - 4, wpCoords.y - 92), + new PathNode(wpCoords.x + 25, wpCoords.y - 70), + new PathNode(wpCoords.x + 20, wpCoords.y - 123), ].forEach((node) => { - Pather.move(node); - Attack.securePosition(node.x, node.y, 25, 2500); + Pather.move(node) && Attack.securePosition(node.x, node.y, 25, 2500); }); Pather.move(portalSpot); - console.debug("Mob Count? " + me.mobCount({ range: 40 })); Attack.securePosition(portalSpot.x, portalSpot.y, 40, 4000); Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { + Attack.securePosition(portalSpot.x, portalSpot.y, 25, 1000); return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { log("timed out"); @@ -1285,6 +1301,12 @@ return false; } + if (AutoRush.rushMode === RushModes.chanter && !bumperCheck(nick)) { + log(nick + " you are not eligible for baal. You need to be at least level " + bumperLvlReq()); + + return false; + } + include("core/Common/Baal.js"); log("starting baal"); From 53ad11021a88637afc575235015e3b465f0afa4a Mon Sep 17 00:00:00 2001 From: Georg R <39526093+icommitdesnet@users.noreply.github.com> Date: Mon, 3 Mar 2025 22:20:57 +0100 Subject: [PATCH 496/758] add ubers command to enter uber tristram (#434) * add ubers command to enter uber tristram * move following into checkExit --- d2bs/kolbot/libs/scripts/Follower.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index d979103a7..ff23e0bc9 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -553,7 +553,18 @@ const Follower = new Runnable( return Pather.usePortal(null, null, wsp); } } - + // Uber portals + const uberPortals = [ + sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain, sdk.areas.UberTristram + ]; + if (me.inArea(sdk.areas.Harrogath) && uberPortals.includes(area)) { + Town.moveToSpot("stash"); + let uberPortal = Pather.getPortal(area); + if (uberPortal) { + announce("Special transit to " + getAreaName(area)); + return Pather.usePortal(null, null, uberPortal); + } + } return false; }; From ca2fd0ba78841eec48df354d8383b5f5205cbed0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 12 Mar 2025 12:28:35 -0400 Subject: [PATCH 497/758] feat: Add handler for unpartying at the end of a run for MinGameTime wait --- d2bs/kolbot/default.dbj | 4 ++++ d2bs/kolbot/libs/config/Amazon.js | 1 + d2bs/kolbot/libs/config/Assassin.js | 1 + d2bs/kolbot/libs/config/Barbarian.js | 1 + d2bs/kolbot/libs/config/Druid.js | 1 + d2bs/kolbot/libs/config/Necromancer.js | 1 + d2bs/kolbot/libs/config/Paladin.js | 1 + d2bs/kolbot/libs/config/Sorceress.js | 1 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/libs/modules/workers/SimpleParty.js | 4 ++++ d2bs/kolbot/sdk/types/Config.d.ts | 1 + d2bs/kolbot/threads/Party.js | 5 +++++ 13 files changed, 23 insertions(+) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index ba977987a..8d4e6073d 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -244,6 +244,10 @@ function main () { + " seconds." ); } + + if (Config.UnpartyForMinGameTimeWait) { + scriptBroadcast("unparty"); + } while (getTickCount() - startTime < Config.MinGameTime * 1000) { me.overhead( diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index ec7cce355..953fe99c6 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -385,6 +385,7 @@ function LoadConfig () { Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index fc39912dc..c35aecf49 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -385,6 +385,7 @@ function LoadConfig () { Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 3d3f942a8..f299682fa 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -385,6 +385,7 @@ function LoadConfig () { Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 77274e647..d28993c13 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -385,6 +385,7 @@ function LoadConfig () { Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index f652ccc5e..2289fb728 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -385,6 +385,7 @@ function LoadConfig () { Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 0af908f99..c4d9d7bed 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -385,6 +385,7 @@ function LoadConfig () { Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 4525a9427..1b9b8b104 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -384,6 +384,7 @@ function LoadConfig () { Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index fb34dfa24..b18c7a394 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -366,6 +366,7 @@ Config.MinGameTime = 60; // Min game time in seconds. Bot will TP to town and stay in game if the run is completed before. Config.MaxGameTime = 0; // Maximum game time in minutes. Quit game when limit is reached. Config.LogExperience = false; // Print experience statistics in the manager. + Config.UnpartyForMinGameTimeWait = false; // Unparty for MinGameTime wait - can prevent players from completing q's in your game you don't want completed // Chicken settings Config.LifeChicken = 30; // Exit game if life is less or equal to designated percent. diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 025ee987b..1c463c15f 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -126,6 +126,7 @@ let Config = { AreaDelay: 0, MinGameTime: 0, MaxGameTime: 0, + UnpartyForMinGameTimeWait: false, // Healing and chicken LifeChicken: 0, diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index 113b0e182..c751f18d5 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -88,12 +88,16 @@ }; SimpleParty.timer = 0; + SimpleParty.enabled = true; if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { addEventListener("scriptmsg", function (msg) { if (!isType(msg, "string")) return; if (msg === "hostileEventEnded" && (Config.ShitList || Config.UnpartyShitlisted)) { ShitList.read().forEach((name) => me.shitList.add(name)); + } else if (msg === "unparty") { + SimpleParty.enabled = false; + clickParty(getParty(), BUTTON_LEAVE_PARTY); } }); diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 50d296526..4c67d3c31 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -21,6 +21,7 @@ declare global { PickDelay: number; AreaDelay: number; MinGameTime: number; + UnpartyForMinGameTimeWait: boolean; MaxGameTime: number; LifeChicken: number; ManaChicken: number; diff --git a/d2bs/kolbot/threads/Party.js b/d2bs/kolbot/threads/Party.js index 3cb328b28..789ade34e 100644 --- a/d2bs/kolbot/threads/Party.js +++ b/d2bs/kolbot/threads/Party.js @@ -98,6 +98,11 @@ function main () { console.debug("Quiting"); quitting = true; + break; + case "unparty": + clickParty(getParty(), sdk.party.controls.Leave); + quitting = true; + break; default: let obj; From cd2700e90845b8e7c84bb640a8cb04d3ad557e53 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 12 Mar 2025 12:36:18 -0400 Subject: [PATCH 498/758] patch: Reworked ngVote for ControlBot again + add cancel - Reworked the voting system to track yes, no, and undecided to ensure everyone gets the chance to vote. The voting period now lasts for 2 minutes and will poll the undecided voters every 30 seconds to cast their vote - Fixed the givewp command to exclude halls --- d2bs/kolbot/libs/core/Misc.js | 24 +++ d2bs/kolbot/libs/scripts/ControlBot.js | 194 +++++++++++++++++++++++-- d2bs/kolbot/sdk/types/Misc.d.ts | 1 + 3 files changed, 210 insertions(+), 9 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 42232117f..576a99761 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -209,6 +209,30 @@ const Misc = (function () { return count; }, + /** + * Get players in game and in my party + * @returns {Party[]} + */ + getPartyMembers: function () { + /** @type {Party[]} */ + const members = []; + let party = getParty(); + + if (party) { + let myPartyId = party.partyid; + + do { + if (party.partyid !== sdk.party.NoParty + && party.partyid === myPartyId + && party.name !== me.name) { + members.push(copyObj(party)); + } + } while (party.getNext()); + } + + return members; + }, + /** * Check if any member of our party meets a certain level req * @param {number} levelCheck diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 79dba4ea4..2811cc9d5 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -90,12 +90,14 @@ const ControlBot = new Runnable( AutoRush.playersOut = "out"; AutoRush.allIn = "all in"; + /** @typedef {"yes" | "no" | "undecided"} NgVote */ const ngVote = { - /** @type {Map} */ + /** @type {Map} */ votes: new Map(), active: false, tick: 0, nextGame: false, + undecidedAskTick: 0, /** * Calculate the number of votes needed for a decision @@ -120,24 +122,66 @@ const ControlBot = new Runnable( begin: function() { this.active = true; this.votes.clear(); + for (let player of Misc.getPartyMembers()) { + this.votes.set(player.name, "undecided"); + } this.tick = getTickCount(); }, /** * Check current count - * @param {"yes" | "no"} type + * @param {NgVote} type */ count: function (type) { + if (type === "undecided") { + return Array.from(this.votes.values()).filter(vote => vote === "undecided").length; + } return type === "yes" ? Array.from(this.votes.values()).filter(vote => vote === "yes").length : Array.from(this.votes.values()).filter(vote => vote === "no").length; }, + stats: function () { + let [yes, no, undecided] = [0, 0, 0]; + + for (let [_name, vote] of this.votes) { + if (vote === "yes") { + yes++; + } else if (vote === "no") { + no++; + } else if (vote === "undecided") { + undecided++; + } + } + + return ("yes: " + yes + " no: " + no + " undecided: " + undecided); + }, + /** * Check the current vote count and determine the outcome + * @param {boolean} skipUndecided * @returns {boolean} */ - checkCount: function() { + checkCount: function(skipUndecided = false) { + let undecided = []; + + if (!skipUndecided) { + for (let [playerName, vote] of this.votes) { + if (vote === "undecided") { + undecided.push(playerName); + } + } + + if (undecided.length) { + if (getTickCount() - ngVote.undecidedAskTick > Time.seconds(30)) { + let votingPeriodRemaining = Math.round(Time.toSeconds(Time.minutes(2) - ngVote.elapsed())); + Chat.say(undecided.join(", ") + " please cast your vote. Vote ends in " + votingPeriodRemaining + "s"); + ngVote.undecidedAskTick = getTickCount(); + } + return false; + } + } + const votesNeeded = this.votesNeeded(); const yesVotes = Array.from(this.votes.values()).filter(vote => vote === "yes").length; const noVotes = Array.from(this.votes.values()).filter(vote => vote === "no").length; @@ -1030,6 +1074,81 @@ const ControlBot = new Runnable( } }; + const dropTrollGold = function (nick) { + try { + if (givenGold.has(nick)) { + throw new ScriptError("Already gifted you this game. Don't be greedy."); + } + + let unit = Game.getPlayer(nick); + + if (unit && unit.distance > 5) { + throw new ScriptError("Get closer."); + } + + if (!unit) { + let partyUnit = getParty(nick); + + if (!Misc.poll(() => partyUnit.inTown, 500, 50)) { + throw new ScriptError("You need to be in one of the towns."); + } + // wait until party area is readable? + Chat.say("Wait for me at waypoint."); + Town.goToTown(sdk.areas.actOf(partyUnit.area)); + + unit = Game.getPlayer(nick); + } + + if (unit) { + if (me.getStat(sdk.stats.Gold) < 5000) { + Town.openStash() && gold(5000, 4); + me.cancelUIFlags(); + } + + // drop the gold + gold(1); + /** @type {ItemUnit} */ + let droppedGold = Misc.poll(function () { + let _gold = Game.getItem(sdk.items.Gold); + if (_gold && _gold.onGroundOrDropping && _gold.getStat(sdk.stats.Gold) === 1) { + return _gold; + } + return false; + }, Time.seconds(30), 1000); + + if (!droppedGold) { + throw new ScriptError("Failed to drop gold."); + } + + // watch for the gold dissapearing + let picked = false; + Misc.poll(function () { + let _gold = Game.getItem(sdk.items.Gold, sdk.items.mode.onGround, droppedGold.gid); + if (_gold) return false; + picked = true; + return !_gold; + }, Time.seconds(30), 1000); + + if (!picked) { + Pickit.pickItem(droppedGold); + throw new ScriptError("Failed to pick gold."); + } else { + givenGold.add(nick); + Chat.say("yw " + nick); + } + } else { + throw new ScriptError("I don't see you"); + } + } catch (e) { + if (e instanceof ScriptError) { + Chat.say((typeof e === "object" && e.message ? e.message : typeof e === "string" && e)); + } else { + console.error(e); + Chat.say("Internal Error"); + } + } + }; + /** * @param {string} nick * @param {string} msg @@ -1044,10 +1163,32 @@ const ControlBot = new Runnable( msg = msg.split(" ")[1]; } else if (msg.match(/^givewp /gi)) { msg = msg.slice(0, 6).trim(); + } else if (msg.match(/^cancel /gi)) { + msg = msg.slice("cancel ".length); + + const cmdIndex = queue.findIndex(function (item) { + const [cmd, commander] = item; + if (!String.isEqual(nick, commander)) { + return false; + } + + return String.isEqual(msg, cmd); + }); + + if (cmdIndex !== 1) { + Chat.say("Removing " + msg + " from the queue"); + queue.splice(cmdIndex, 1); + + return; + } } + if (commandAliases.has(msg)) { msg = commandAliases.get(msg); } + if (msg.match(/^drop /gi) && !Config.ControlBot.DropGold) { + msg = "troll"; + } if (!actions.has(msg)) { return; } @@ -1080,11 +1221,21 @@ const ControlBot = new Runnable( } // eslint-disable-next-line no-unused-vars + /** + * @param {number} mode + * @param {string} param1 + * @param {string} param2 + * @param {string} name1 + * @param {string} name2 + */ function gameEvent (mode, param1, param2, name1, name2) { switch (mode) { case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." // idle in town me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); + if (name1 && ngVote.active) { + ngVote.votes.set(name1, "undecided"); + } if (name2) { players.set(name1, "*" + name2); } else { @@ -1144,7 +1295,11 @@ const ControlBot = new Runnable( if (value.complete) return; if (value.desc.includes("Rush")) return; // let desc = (key + " (" + value.desc + "), "); - let desc = value.desc.includes("experimental") ? ("(" + value.desc + "), ") : (key + ", "); + let desc = value.desc.includes("experimental") + ? ("(" + value.desc + "), ") + : (key === "cancel" || key === "givewp") + ? value.desc + ", " + : (key + ", "); if (str.length + desc.length > MAX_CHAT_LENGTH - (nick.length + 2)) { msg.push(str); str = ""; @@ -1178,6 +1333,11 @@ const ControlBot = new Runnable( cmdNicks.get(nick).seenHelpMsg = true; } }); + _actions.set("cancel", { + desc: "cancel ", + hostileCheck: false, + run: () => {} + }); _actions.set("timeleft", { desc: "Remaining time for this game", hostileCheck: false, @@ -1198,7 +1358,7 @@ const ControlBot = new Runnable( hostileCheck: false, run: function (nick) { if (ngVote.active) { - Chat.say("NGVote is already active. Current count: " + ngVote.count("yes")); + Chat.say("NGVote is already active. Current count: " + ngVote.stats()); return; } if (getTickCount() - startTime < Time.minutes(3)) { @@ -1246,6 +1406,12 @@ const ControlBot = new Runnable( hostileCheck: false, run: dropGold }); + } else { + _actions.set("troll", { + desc: "", + hostileCheck: false, + run: dropTrollGold + }); } if (Config.ControlBot.Chant.Enchant @@ -1275,7 +1441,7 @@ const ControlBot = new Runnable( }); _actions.set("givewp", { - desc: "givewp - experimental", + desc: "givewp ", hostileCheck: true, run: giveWp }); @@ -1419,6 +1585,8 @@ const ControlBot = new Runnable( if (area.Waypoint === 255) { Chat.say(area.LocaleString + " isn't a valid wp area to ask for"); + return false; + } else if (area.Index === sdk.areas.HallsofPain) { return false; } @@ -1502,9 +1670,17 @@ const ControlBot = new Runnable( pickGoldPiles(); if (ngVote.active) { - if (ngVote.elapsed() > Time.minutes(2) && !ngVote.nextGame) { - Chat.say("Not enough votes to start next game. Votes gathered " + ngVote.count("yes")); - ngVote.reset(); + if (ngVote.elapsed() > Time.minutes(2)) { + ngVote.checkCount(true); + if (!ngVote.nextGame) { + Chat.say( + "Not enough votes to start ng." + + " yes: " + ngVote.count("yes") + + " no: " + ngVote.count("no") + + " undecided: " + ngVote.count("undecided") + ); + ngVote.reset(); + } } else if (ngVote.elapsed() > Time.seconds(30) && !ngVote.nextGame) { ngVote.checkCount(); } diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index 43fdeb993..15bb0d20e 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -15,6 +15,7 @@ declare global { function getNearbyPlayerCount(): number; function getPlayerCount(): number; function getPartyCount(): number; + function getPartyMembers(): Party[]; function checkPartyLevel(levelCheck: number, exclude: string | string[]): boolean; function getPlayerArea(player: Party | string): number | false; From 7cc52c22b39d3853014b75d22f649120b3255ee9 Mon Sep 17 00:00:00 2001 From: Georg R <39526093+icommitdesnet@users.noreply.github.com> Date: Sat, 29 Mar 2025 21:33:25 +0100 Subject: [PATCH 499/758] Gemhandling update (#424) * Gemhandling update let pickit decide, if we want an upgrade check for gem upgrade before picking a gemshrine (autoshriner and scanshrine) no collecting of chipped gems vith gem shrines * set to strictly equal * add prepareForGemShrine to type fix linting warnings * Cleanup nested class declaration + Fix unnecessary attempt at portaling on start + `dontStashGids` Set - Moved the `GemUnit` class out from the body of `isUpgradePossible` - Faster check using `some` instead of `filter` for checking clean invo - Add `Town.dontStashGids` which is a Set of gids we are not allowed to stash populated during gamplay - Only attempt taking portal if we moved, this was causing us to take any portal that was ours even if we hadn't gone anywhere * Add `Pickit.systemKeep` so we only keep one gem for GemHunter and don't sell it * Update GemHunter.js - Clean up GemHunter, use custom `findGemShrines` rather than relying on `Misc.getShrinesInArea` method. `findGemShrines` handles preparing for the next shrine if we've found one and ending early if we have no more gems to use * Update types --------- Co-authored-by: theBGuy <60308670+theBGuy@users.noreply.github.com> --- d2bs/kolbot/libs/core/Misc.js | 9 +- d2bs/kolbot/libs/core/Pickit.js | 30 ++-- d2bs/kolbot/libs/core/Town.js | 206 ++++++++++++++++++-------- d2bs/kolbot/libs/scripts/GemHunter.js | 88 ++++++++--- d2bs/kolbot/sdk/types/Pickit.d.ts | 1 + d2bs/kolbot/sdk/types/Town.d.ts | 2 + 6 files changed, 244 insertions(+), 92 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 576a99761..84191fc05 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -668,7 +668,8 @@ const Misc = (function () { case sdk.shrines.Gem: // for now we ignore if we are gem hunting later on // TODO: add gem hunting logic, get gem from stash if we have one - return !Scripts.GemHunter; + console.debug("shriner: gem shrine. try my best."); + return Town.prepareForGemShrine(); } return false; }; @@ -730,6 +731,7 @@ const Misc = (function () { } } this.getShrine(shrine); + Pickit.pickItems(); } } } @@ -821,6 +823,11 @@ const Misc = (function () { shrine.objtype === Config.ScanShrines[i] || (Config.ScanShrines[i] === "well" && shrine.name.toLowerCase().includes("well") && needWell()) ) && (Pather.useTeleport() || !checkCollision(me, shrine, sdk.collision.WallOrRanged))) { + // Gem shrine - prepare and pick anyways + if (Config.ScanShrines[i] === sdk.shrines.Gem) { + console.debug("scanshrines: gem shine. try my best."); + Town.prepareForGemShrine(); + } this.getShrine(shrine); // Gem shrine - pick gem diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index f24b07017..e9bb2a11c 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -41,6 +41,8 @@ const Pickit = { sdk.items.type.Gold, sdk.items.type.Scroll, sdk.items.type.HealingPotion, sdk.items.type.ManaPotion, sdk.items.type.RejuvPotion ], + /** @type {{ reason: string, gid: number }[]} */ + systemKeep: [], /** * @param {boolean} notify @@ -299,15 +301,25 @@ const Pickit = { if (Scripts.GemHunter && rval.result === Pickit.Result.UNWANTED) { // gemhunter active if (Config.GemHunter.GemList.some((p) => [unit.classid - 1, unit.classid].includes(p))) { - // base and upgraded gem will be kept - let _items = me.getItemsEx(unit.classid, sdk.items.mode.inStorage) - .filter(function (i) { - return i.gid !== unit.gid - && !CraftingSystem.checkItem(i) - && !Cubing.checkItem(i) - && !Runewords.checkItem(i); - }); - if (_items.length === 0) return resultObj(Pickit.Result.WANTED, "GemHunter"); + let existingGem = Pickit.systemKeep.find((el) => el.reason === "GemHunter"); + if (existingGem && existingGem.gid === unit.gid) { + return resultObj(Pickit.Result.WANTED, "GemHunter"); + } + if (!existingGem || !me.getItem(-1, -1, existingGem.gid)) { + existingGem && Pickit.systemKeep.findAndRemove((el) => el.reason === "GemHunter"); + // base and upgraded gem will be kept + let _items = me.getItemsEx(unit.classid, sdk.items.mode.inStorage) + .filter(function (i) { + return i.gid !== unit.gid + && !CraftingSystem.checkItem(i) + && !Cubing.checkItem(i) + && !Runewords.checkItem(i); + }); + if (_items.length === 0) { + Pickit.systemKeep.push({ reason: "GemHunter", gid: unit.gid }); + return resultObj(Pickit.Result.WANTED, "GemHunter"); + } + } } } diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index d5b67d67e..f797eda90 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -9,6 +9,8 @@ const Town = { telekinesis: true, sellTimer: getTickCount(), // shop speedup test lastChores: 0, + /** @type {Set} */ + dontStashGids: new Set(), act: { 1: { @@ -1424,9 +1426,13 @@ const Town = { * @param {ItemUnit} item */ canStash: function (item) { - if (Town.ignoreType(item.itemType) - || [sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid) - || !Town.canStashGem(item)) { + if (Town.dontStashGids.has(item.gid)) { + return false; + } + if (Town.ignoreType(item.itemType)) { + return false; + } + if ([sdk.items.quest.HoradricStaff, sdk.items.quest.KhalimsWill].includes(item.classid)) { return false; } /** @@ -1435,68 +1441,6 @@ const Town = { return Storage.Stash.CanFit(item); }, - /** - * get ordered list of gems in inventory to use with Gem Hunter Script - * @returns {ItemUnit[]} ordered list of relevant gems - */ - getGemsInInv: function () { - let GemList = Config.GemHunter.GemList; - return me.getItemsEx() - .filter((p) => p.isInInventory && GemList.includes(p.classid)) - .sort((a, b) => GemList.indexOf(a.classid) - GemList.indexOf(b.classid)); - }, - - /** - * get ordered list of gems in stash to use with Gem Hunter Script - * @returns {ItemUnit[]} ordered list of relevant gems - */ - getGemsInStash: function () { - let GemList = Config.GemHunter.GemList; - return me.getItemsEx() - .filter((p) => p.isInStash && GemList.includes(p.classid)) - .sort((a, b) => GemList.indexOf(a.classid) - GemList.indexOf(b.classid)); - }, - - /** - * gem check for use with Gem Hunter Script - * @param {ItemUnit} item - * @returns {boolean} if we should stash this gem - */ - canStashGem: function (item) { - // we aren't using the gem hunter script or we aren't scanning for the gem shrines while moving - // for now we are only going to keep a gem in our invo while the script is active - if (Loader.scriptName() !== "GemHunter") return true; - // not in our list - if (Config.GemHunter.GemList.indexOf(item.classid) === -1) return true; - - let GemList = Config.GemHunter.GemList; - let gemsInStash = Town.getGemsInStash(); - let bestGeminStash = gemsInStash.length > 0 ? gemsInStash.first().classid : -1; - let gemsInInvo = Town.getGemsInInv(); - let bestGeminInv = gemsInInvo.length > 0 ? gemsInInvo.first().classid : -1; - - return ( - (GemList.indexOf(bestGeminStash) < GemList.indexOf(bestGeminInv)) // better one in stash - || (GemList.indexOf(bestGeminInv) < GemList.indexOf(item.classid)) // better one in inv - || (gemsInInvo.filter((p) => p.classid === item.classid).length > 1)); // another one in inv - }, - - /** - * move best gem from stash to inventory, if none in inventrory - * to use with Gem Hunter Script - * @returns {boolean} if any gem has been moved - */ - getGem: function () { - if (Loader.scriptName() === "GemHunter") { - if (Town.getGemsInInv().length === 0 && Town.getGemsInStash().length > 0) { - let gem = Town.getGemsInStash().first(); - Storage.Inventory.MoveTo(gem) && Item.logger("Inventoried", gem); - return true; - } - } - return false; - }, - /** * @param {boolean} [stashGold=true] * @returns {boolean} @@ -2218,5 +2162,137 @@ const Town = { console.info(false, "CurrentArea: " + getAreaName(me.area)); return true; + }, + + /** + * @description get a gem worthy upgrading to inventory. go town if needed + * @returns {boolean} If a gem to upgrade is in inventory + */ + prepareForGemShrine: function () { + /** + * @param {ItemUnit} unit + * @returns {boolean} + */ + function isUpgradePossible(unit) { + return ( + unit.itemType >= sdk.items.type.Amethyst + && unit.itemType <= sdk.items.type.Skull + && !Object.values(sdk.items.gems.Perfect).includes(unit.classid) + ); + } + + /** + * @class + * @param {ItemUnit} gem + */ + function GemUnit(gem) { + this.itemType = gem.itemType; + this.classid = gem.classid; + this.type = gem.type; + this.mode = gem.mode; + this.gid = gem.gid; + } + // eslint-disable-next-line no-unused-vars + GemUnit.prototype.getFlag = function (flag) { + return true; + }; + + /** + * @param {ItemUnit} unit + * @returns {boolean} + */ + function pickitWantsUpgrade(unit) { + // Stub Object to let Pickit Check + let gem = new GemUnit(unit); + // lets upgrade the gem + gem.classid += 1; + return [ + Pickit.Result.WANTED, Pickit.Result.CUBING, + Pickit.Result.RUNEWORD, Pickit.Result.CRAFTING + ].includes(Pickit.checkItem(gem).result); + } + + // do i have any gem worth upgrading + const haveUpGems = me.getItemsEx() + .some(function (p) { + return isUpgradePossible(p) && pickitWantsUpgrade(p); + }); + + if (!haveUpGems) { + console.debug("I dont have any gems worth upgrading"); + return false; + } + // we have gems that must not be upgraded in inventory + const isInventoryClean = !me.getItemsEx() + .some(function (p) { + return p.isInInventory && isUpgradePossible(p) && !pickitWantsUpgrade(p); + }); + + // we have gems that can be upgraded in inventory + const validGem = me.getItemsEx() + .find(function (p) { + return p.isInInventory && isUpgradePossible(p) && pickitWantsUpgrade(p); + }); + + // we already got good gems in inv and no bad games -> take shrine + if (isInventoryClean && validGem) { + console.debug("Inventory looks good. lets take the shrine."); + Town.dontStashGids.add(validGem.gid); + return true; + } + + // go to town + const preArea = me.area; + const preAct = me.act; + if (!me.inTown) { + // not an essential function -> handle thrown errors + try { + Town.goToTown(); + } catch (e) { + return false; + } + } + + // stash the bad gems first + !isInventoryClean && Town.stash(); + + // get any good gem. flawless first (by lvlreq) + const goodGem = me.getItemsEx() + .filter(function (p) { + return isUpgradePossible(p) && pickitWantsUpgrade(p); + }) + .sort (function(a, b) { + return b.lvlreq - a.lvlreq; + }) + .first(); + + let sucess = false; + + if (goodGem) { + console.debug("get Gem: " + goodGem.fname); + Town.stash(false); + if (Storage.Inventory.MoveTo(goodGem)) { + sucess = true; + Town.dontStashGids.add(goodGem.gid); + Item.logger("Inventoried", goodGem); + } + } + + if (me.area !== preArea) { + // leave town + me.act !== preAct && Town.goToTown(preAct); + Town.move("portalspot"); + + if (!Pather.usePortal(null, me.name)) { + try { + Pather.usePortal(preArea, me.name); + } catch (e) { + throw new Error("Town.visitTown: Failed to go back from town"); + } + } + + Config.PublicMode && Pather.makePortal(); + } + return sucess; } }; diff --git a/d2bs/kolbot/libs/scripts/GemHunter.js b/d2bs/kolbot/libs/scripts/GemHunter.js index 7bdaf09b4..b0c86314c 100644 --- a/d2bs/kolbot/libs/scripts/GemHunter.js +++ b/d2bs/kolbot/libs/scripts/GemHunter.js @@ -1,6 +1,6 @@ /** * @filename GemHunter.js -* @author icommitdesnet +* @author icommitdesnet, theBGuy * @desc hunt gem shrines * */ @@ -14,32 +14,86 @@ */ const GemHunter = new Runnable( function GemHunter () { - Town.doChores(); - Town.getGem(); - if (Town.getGemsInInv().length === 0) { + if (!Town.prepareForGemShrine()) { console.log("ÿc4GemHunterÿc0: no gems in inventory - aborting."); return false; } - for (let area of Config.GemHunter.AreaList) { - if (Town.getGemsInInv().length > 0) { - console.log("ÿc4GemHunterÿc0: Moving to " + getAreaName(area)); - Pather.journeyTo(area); - if (i % 2 === 0) Precast.doPrecast(true); - if (Misc.getShrinesInArea(area, sdk.shrines.Gem, true)) { - Pickit.pickItems(); - console.log("ÿc4GemHunterÿc0: found a gem Shrine"); - if ((Town.getGemsInInv().length === 0) && (Town.getGemsInStash().length > 0)) { - console.log("ÿc4GemHunterÿc0: Getting a new Gem in Town."); - Town.visitTown(); // Go to Town and do chores. Will throw an error if it fails to return from Town. - Town.getGem(); + /** @param {number} area */ + const findGemShrines = function (area) { + const shrineLocs = []; + const units = Game.getPresetObjects(area) + .filter(function (preset) { + return sdk.shrines.Presets.includes(preset.id); + }); + + if (units.length) { + for (let shrine of units) { + shrineLocs.push(shrine.realCoords()); + } + } + + try { + NodeAction.shrinesToIgnore.push(sdk.shrines.Gem); + + while (shrineLocs.length > 0) { + shrineLocs.sort(Sort.units); + let coords = shrineLocs.shift(); + + Pather.move(coords, { minDist: Skill.haveTK ? 20 : 5, callback: () => { + let shrine = Game.getObject("shrine"); + return !!shrine && shrine.x === coords.x && shrine.y === coords.y; + } }); + + let shrine = Game.getObject("shrine"); + + if (shrine) { + do { + if (shrine.objtype === sdk.shrines.Gem && shrine.mode === sdk.objects.mode.Inactive) { + (!Skill.haveTK || !use) && Pather.moveTo(shrine.x - 2, shrine.y - 2); + + console.log("ÿc4GemHunterÿc0: found a gem Shrine"); + if (Misc.getShrine(shrine)) { + Pickit.pickItems(); + + if (!Town.prepareForGemShrine()) { + console.error("ÿc4GemHunterÿc0: failed to prepare for next shrine - aborting."); + return false; + } + } + } + } while (shrine.getNext()); } } + + return true; + } finally { + NodeAction.shrinesToIgnore.remove(sdk.shrines.Gem); + } + }; + + for (let area of Config.GemHunter.AreaList) { + console.log("ÿc4GemHunterÿc0: Moving to " + getAreaName(area)); + Pather.journeyTo(area); + Precast.doPrecast(false); + + if (!findGemShrines(area)) { + console.debug("ending early"); + break; } } return true; }, { - preAction: null + preAction: function (ctx) { + ctx.preGidList = new Set(Town.dontStashGids); + }, + cleanup: function (ctx) { + for (let gid of Town.dontStashGids) { + if (!ctx.preGidList.has(gid)) { + Town.dontStashGids.delete(gid); + } + } + } } ); diff --git a/d2bs/kolbot/sdk/types/Pickit.d.ts b/d2bs/kolbot/sdk/types/Pickit.d.ts index a8df9e378..fe16a900f 100644 --- a/d2bs/kolbot/sdk/types/Pickit.d.ts +++ b/d2bs/kolbot/sdk/types/Pickit.d.ts @@ -18,6 +18,7 @@ declare global { const Result: PickitResult; const tkable: number[]; const essentials: number[]; + const systemKeep: { reason: string, gid: number }[]; function init(notify: any): void; function itemEvent(gid?: number, mode?: number, code?: number, global?: number): void; diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts index 3bb0508eb..62271f455 100644 --- a/d2bs/kolbot/sdk/types/Town.d.ts +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -4,6 +4,7 @@ declare global { let telekinesis: boolean; let sellTimer: number; let lastChores: number; + const dontStashGids: Set; const tasks: Map Date: Sat, 29 Mar 2025 16:40:50 -0400 Subject: [PATCH 500/758] [hotfix] Cancel trade prompt if bot clicks on another player while casting enchant --- d2bs/kolbot/libs/core/Precast.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index be571a2e0..f918c2504 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -371,11 +371,21 @@ const Precast = (function () { chantList.has(unit.name) ? chantList.get(unit.name).update() : chantList.set(unit.name, new ChantTracker()); + + // not sure why this happens but sometimes clicks on other characters while casting + if (getUIFlag(sdk.uiflags.TradePrompt)) { + me.cancel(); + } } } } while (unit.getNext()); } + // not sure why this happens but sometimes clicks on other characters while casting + if (getUIFlag(sdk.uiflags.TradePrompt)) { + me.cancel(); + } + // Minion unit = Game.getMonster(); From 1b729dede3c01a886c1229646a6b4b229f44252e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 2 Apr 2025 04:26:53 -0400 Subject: [PATCH 501/758] [feat] Create global `env` object for secrets - Add a global env object that lazy loads data from `d2bs/kolbot/.env.json` if it exists. We can store any secrets here and then use them by accessing `env.` --- .gitignore | 1 + d2bs/kolbot/libs/Polyfill.js | 72 ++++++++++++++++++++++++++++++++++++ d2bs/kolbot/sdk/globals.d.ts | 24 ++++++++++++ 3 files changed, 97 insertions(+) diff --git a/.gitignore b/.gitignore index 4ebc561d9..53ef9d25c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ logs/ # Do not track install packages node_modules/ +d2bs/kolbot/env.json diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 8072b41fb..1ae5ed2d9 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -784,6 +784,78 @@ if (!global.hasOwnProperty("require")) { }); } +if (!global.hasOwnProperty("env")) { + /** + * @typedef {Object} EnvStore + * @property {function(Record): EnvStore} update - Updates environment settings + * @property {Object.} [customSettings] - Any additional custom settings + */ + + /** @type {EnvStore} */ + const envStore = {}; + let initialized = false; + + Object.defineProperty(global, "env", { + value: new Proxy({}, { + get: function (target, prop) { + if (!initialized) { + /** @param {Record} settings */ + envStore.update = function(settings) { + Object.assign(this, settings); + return this; + }; + + if (FileTools.exists(".env.json")) { + try { + let loadedEnv = FileAction.parse(".env.json"); + envStore.update(loadedEnv); + } catch (err) { + console.error(err); + } + } + + initialized = true; + } + + if (prop in envStore) { + return typeof envStore[prop] === "function" + ? envStore[prop].bind(envStore) + : envStore[prop]; + } + + return undefined; + }, + + set: function (target, prop, value) { + if (!initialized) { + this.get(target, "version"); + } + + envStore[prop] = value; + return true; + }, + + has: function (target, prop) { + if (!initialized) { + this.get(target, "version"); + } + + return prop in envStore; + }, + + ownKeys: function (target) { + if (!initialized) { + this.get(target, "version"); + } + + return Object.keys(envStore); + } + }), + writable: false, + configurable: false + }); +} + /** * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Math Polyfills ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 72793729a..2e154b162 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -137,6 +137,30 @@ declare global { dateStamp(): string; } + /** + * Environment configuration + * @namespace + */ + interface Env { + /** + * Updates environment settings with provided values + * @param settings Object containing settings to update + * @returns The updated env object for chaining + */ + update(settings: Record): Env; + + /** + * Any additional custom properties + */ + [key: string]: any; + } + + /** + * Global environment object + * This object is lazily loaded when first accessed. + */ + const env: Env; + class ScriptError extends Error { } From 137ab999ef079e7f0ab63e3ebdf9daa6cb8542ac Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Apr 2025 12:58:19 -0400 Subject: [PATCH 502/758] Clean up DataFile initialization --- d2bs/kolbot/D2BotBlank.dbj | 4 +-- d2bs/kolbot/D2BotChannel.dbj | 2 +- d2bs/kolbot/D2BotCleaner.dbj | 2 +- d2bs/kolbot/D2BotFollow.dbj | 2 +- d2bs/kolbot/D2BotGameAction.dbj | 2 +- d2bs/kolbot/D2BotLead.dbj | 2 +- d2bs/kolbot/D2BotMap.dbj | 6 ++--- d2bs/kolbot/D2BotPubJoin.dbj | 2 +- d2bs/kolbot/libs/oog/DataFile.js | 18 ++++++++++--- d2bs/kolbot/sdk/types/OOG.d.ts | 46 +++++++++++++++++++++++++++----- 10 files changed, 64 insertions(+), 22 deletions(-) diff --git a/d2bs/kolbot/D2BotBlank.dbj b/d2bs/kolbot/D2BotBlank.dbj index 446965655..4c804d9dd 100644 --- a/d2bs/kolbot/D2BotBlank.dbj +++ b/d2bs/kolbot/D2BotBlank.dbj @@ -9,8 +9,8 @@ function main() { include("critical.js"); // required - if (!FileTools.exists("data/" + me.profile + ".json")) { - DataFile.create(); + if (DataFile.init()) { + Starter.firstRun = true; } addEventListener("copydata", Starter.receiveCopyData); diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 40ecfabf1..082809e9b 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -44,7 +44,7 @@ const joinInfo = { joinChannel: Starter.Config.JoinChannel, }; -if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { +if (DataFile.init()) { Starter.firstRun = true; } diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index 57f174dd3..99d185e86 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -24,7 +24,7 @@ const { let Controls = require("./libs/modules/Control"); -if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { +if (DataFile.init()) { Starter.firstRun = true; } diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 59364cfe6..56fa71213 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -27,7 +27,7 @@ if (typeof Starter.AdvancedConfig[me.profile] === "object") { } delete Starter.AdvancedConfig; -if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { +if (DataFile.init()) { Starter.firstRun = true; } diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index 88b65d683..260e50320 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -25,7 +25,7 @@ include("systems/gameaction/GameAction.js"); const Overrides = require("./modules/Override"); -if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { +if (DataFile.init()) { Starter.firstRun = true; } diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 17ea78a17..fa38dfed9 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -23,7 +23,7 @@ if (typeof Starter.AdvancedConfig[me.profile] === "object") { } delete Starter.AdvancedConfig; -if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { +if (DataFile.init()) { Starter.firstRun = true; } diff --git a/d2bs/kolbot/D2BotMap.dbj b/d2bs/kolbot/D2BotMap.dbj index 58efe4d53..21944f3e7 100644 --- a/d2bs/kolbot/D2BotMap.dbj +++ b/d2bs/kolbot/D2BotMap.dbj @@ -8,9 +8,9 @@ function main () { include("critical.js"); // required - if (!FileTools.exists("data/" + me.profile + ".json")) { - DataFile.create(); - } + if (DataFile.init()) { + Starter.firstRun = true; +} addEventListener("copydata", Starter.receiveCopyData); diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index 7a89f5091..53e23d6a2 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -46,7 +46,7 @@ new Overrides.Override(Starter, Starter.setNextGame, function (orignal, gameName DataFile.updateStats("nextGame", nextGame); }).apply(); -if (!FileTools.exists("data/" + me.profile + ".json") && DataFile.create()) { +if (DataFile.init()) { Starter.firstRun = true; } diff --git a/d2bs/kolbot/libs/oog/DataFile.js b/d2bs/kolbot/libs/oog/DataFile.js index 9cc0dc041..874993d4e 100644 --- a/d2bs/kolbot/libs/oog/DataFile.js +++ b/d2bs/kolbot/libs/oog/DataFile.js @@ -19,6 +19,7 @@ includeIfNotIncluded("oog/FileAction.js"); } }(this, function () { const DataFile = { + _path: "data/" + me.profile + ".json", _default: { handle: 0, name: "", @@ -34,8 +35,17 @@ includeIfNotIncluded("oog/FileAction.js"); nextGame: "" }, + init: function () { + if (!FileTools.exists(this._path)) { + this.create(); + + return true; + } + return false; + }, + create: function () { - FileAction.write("data/" + me.profile + ".json", JSON.stringify(this._default, null, 2)); + FileAction.write(this._path, JSON.stringify(this._default, null, 2)); return this._default; }, @@ -60,10 +70,10 @@ includeIfNotIncluded("oog/FileAction.js"); }, getObj: function () { - !FileTools.exists("data/" + me.profile + ".json") && DataFile.create(); + !FileTools.exists(this._path) && DataFile.create(); let obj; - let string = FileAction.read("data/" + me.profile + ".json"); + let string = FileAction.read(this._path); try { obj = JSON.parse(string); @@ -150,7 +160,7 @@ includeIfNotIncluded("oog/FileAction.js"); let string = JSON.stringify(obj, null, 2); - FileAction.write("data/" + me.profile + ".json", string); + FileAction.write(this._path, string); } }; diff --git a/d2bs/kolbot/sdk/types/OOG.d.ts b/d2bs/kolbot/sdk/types/OOG.d.ts index 791de0f70..0a2e50c15 100644 --- a/d2bs/kolbot/sdk/types/OOG.d.ts +++ b/d2bs/kolbot/sdk/types/OOG.d.ts @@ -1,10 +1,28 @@ // @ts-nocheck declare global { - namespace DataFile { - function create(): void - function getObj(): void - function getStats(): any - function updateStats(arg: any, value?: any): void + namespace DataFile { + const _path: string; + const _default: { + handle: number; + name: string; + level: number; + experience: number; + gold: number; + deaths: number; + runs: number; + lastArea: string; + ingameTick: number; + gameName: string; + currentGame: string; + nextGame: string; + }; + + function init(): boolean; + function create(): typeof _default; + function read(profile: string): typeof _default | null; + function getObj(): typeof _default; + function getStats(): typeof _default; + function updateStats(arg: string | string[], value?: any): void; } namespace FileAction { @@ -14,6 +32,18 @@ declare global { function parse(path: string): any; } + interface D2BotProfileInfo { + Name: string; + Status: string; + Account: string; + Character: string; + Difficulty: string; + Realm: string; + Game: string; + Entry: string; + Tag: string; + } + export const D2Bot: { handle: number, init(): void @@ -46,7 +76,7 @@ declare global { ingame(): void joinMe(profile: any, gameName: any, gameCount: any, gamePass: any, isUp: any): void requestGame(profile: any): void - getProfile(): void + getProfile(): D2BotProfileInfo setProfile(account: any, password: any, character: any, difficulty: any, realm: any, infoTag: any, gamePath: any): void setTag(tag: any): void store(info: any): void @@ -133,6 +163,8 @@ declare global { ysize: number ): string; + type Difficulty = 'Normal' | 'Nightmare' | 'Hell' | 'Highest'; + function scrollDown(): void; function clickRealm(realm: realms): boolean; function findCharacter(info: CharacterInfo): Control | false; @@ -147,7 +179,7 @@ declare global { function makeAccount(info: AccountInfo): boolean; function loginAccount(info: AccountInfo): boolean; function joinChannel(channel: string): boolean; - function createGame(name: string, pass: string, diff: string, delay: number): void; + function createGame(name: string, pass: string, diff: Difficulty, delay: number): void; function getGameList(): { gameName: string, players: number }[] | false; function getQueueTime(): number; function loginOtherMultiplayer(): boolean; From 81c6383b5d899b817d305fcc50c127664a914206 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Apr 2025 13:05:20 -0400 Subject: [PATCH 503/758] [feat] Extended options for cubing recipes - This feature enables more control over when we want to enable/disable recipes dynamically. Users can now choose to enable a recipe by the current quantity they have of the result, i.e only cubing a Ber if they don't already have n amount of them, for further control users can pass a condition callback to be checked if for instance they want to only roll a recipe if they have a certain amount of space in their stash like ```js Config.Recipes.push([Recipe.Socket.Armor, "Archon Plate", { Ethereal: Roll.Eth, condition: function () { return Storage.Stash.UsedSpacePercent() < 85; }, }]); ``` --- d2bs/kolbot/libs/core/Cubing.js | 512 ++++++++++++++------------------ 1 file changed, 217 insertions(+), 295 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 8acef6f11..82f598312 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -110,21 +110,15 @@ const Recipe = { }, Rejuv: 62, FullRejuv: 63, -}; -/** - * @memberof Recipe - * @function ingredients - * @returns {number[]} - */ -Object.defineProperty(Recipe, "ingredients", { + // TODO: refactor to one method that returns the baseRecipeObj /** * Get list of ingredients needed for certain recipe * @param {number} index - Index of recipe to check * @param {number} [keyItem] - Key item in cubing recipe * @returns {number[]} */ - value: function (index, keyItem) { + ingredients: function (index, keyItem) { switch (index) { case Recipe.Gem: return [keyItem - 1, keyItem - 1, keyItem - 1]; @@ -331,6 +325,126 @@ Object.defineProperty(Recipe, "ingredients", { } return []; }, + + /** + * @param {number} index + */ + itemLevel: function (index) { + switch (index) { + case Recipe.HitPower.Helm: + return 84; + case Recipe.HitPower.Boots: + return 71; + case Recipe.HitPower.Gloves: + return 79; + case Recipe.HitPower.Belt: + return 71; + case Recipe.HitPower.Shield: + return 82; + case Recipe.HitPower.Body: + return 85; + case Recipe.HitPower.Amulet: + return 90; + case Recipe.HitPower.Ring: + return 77; + case Recipe.HitPower.Weapon: + return 85; + case Recipe.Blood.Helm: + return 84; + case Recipe.Blood.Boots: + return 71; + case Recipe.Blood.Gloves: + return 79; + case Recipe.Blood.Belt: + return 71; + case Recipe.Blood.Shield: + return 82; + case Recipe.Blood.Body: + return 85; + case Recipe.Blood.Amulet: + return 90; + case Recipe.Blood.Ring: + return 77; + case Recipe.Blood.Weapon: + return 85; + case Recipe.Caster.Helm: + return 84; + case Recipe.Caster.Boots: + return 71; + case Recipe.Caster.Gloves: + return 79; + case Recipe.Caster.Belt: + return 71; + case Recipe.Caster.Shield: + return 82; + case Recipe.Caster.Body: + return 85; + case Recipe.Caster.Amulet: + return 90; + case Recipe.Caster.Ring: + return 77; + case Recipe.Caster.Weapon: + return 85; + case Recipe.Safety.Helm: + return 84; + case Recipe.Safety.Boots: + return 71; + case Recipe.Safety.Gloves: + return 79; + case Recipe.Safety.Belt: + return 71; + case Recipe.Safety.Shield: + return 82; + case Recipe.Safety.Body: + return 85; + case Recipe.Safety.Amulet: + return 90; + case Recipe.Safety.Ring: + return 77; + case Recipe.Safety.Weapon: + return 85; + // Upgrading Recipes------------------------------------------------------------------------// + case Recipe.Socket.Magic.LowWeapon: + case Recipe.Socket.Magic.HighWeapon: + // ilvl < 30 + // ilvl >= 30 + return 30; + case Recipe.Reroll.Charm.Small: + case Recipe.Reroll.Charm.Large: + case Recipe.Reroll.Charm.LowGrand: + case Recipe.Reroll.Charm.Grand: + case Recipe.Reroll.Magic: // Hacky solution ftw + /** + * Charm ilvls based on https://diablo2.diablowiki.net/Guide:Charms_v1.10,_by_Kronos + */ + if (index === Recipe.Reroll.Charm.Small) { + return 94; + } else if (index === Recipe.Reroll.Charm.Large) { + return 76; + } else if (index === Recipe.Reroll.Charm.LowGrand) { + return 50; + } else if (index === Recipe.Reroll.Charm.Grand) { + return 77; + } + return 91; + } + return undefined; + }, +}; + +/** + * @memberof Recipe + * @function ingredients + */ +Object.defineProperty(Recipe, "ingredients", { + enumerable: false, +}); + +/** + * @memberof Recipe + * @function itemLevel + */ +Object.defineProperty(Recipe, "itemLevel", { enumerable: false, }); @@ -413,11 +527,15 @@ const Cubing = { * @typedef recipeObj * @property {number[] | string[]} Ingredients * @property {number} Index + * @property {number} KeyItem * @property {number} [Level] * @property {number} [Ethereal] * @property {boolean} [Enabled] * @property {boolean} [AlwaysEnabled] * @property {number} [MainRecipe] + * @property {number} [MaxQuantity] + * @property {() => boolean} [condition] + * @property {string} [pickLine] * * * @todo @@ -427,301 +545,72 @@ const Cubing = { Cubing.recipes = []; for (let i = 0; i < Config.Recipes.length; i += 1) { - if (typeof Config.Recipes[i] !== "object" - || (Config.Recipes[i].length > 2 && typeof Config.Recipes[i][2] !== "number") - || Config.Recipes[i].length < 1) { + if ( + !isType(Config.Recipes[i], "array") + || Config.Recipes[i].length < 1 + || ( + Config.Recipes[i].length > 2 + && typeof Config.Recipes[i][2] !== "number" + && typeof Config.Recipes[i][2] !== "object" + ) + ) { throw new Error("Cubing.buildRecipes: Invalid recipe format."); } /** @type {number[]} */ - let [index, keyItem] = Config.Recipes[i]; + const [index, keyItem, opts] = Config.Recipes[i]; const ingredients = Recipe.ingredients(index, keyItem); + const itemLevel = Recipe.itemLevel(index); + /** @type {Partial} */ + const extendedOpts = { + Ethereal: undefined, + MaxQuantity: undefined, + condition: undefined + }; + + if (isType(opts, "number")) { + extendedOpts.Ethereal = opts; + } else if (isType(opts, "object")) { + Object.assign(extendedOpts, opts); + } - switch (index) { - case Recipe.Gem: - this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); - - break; - // Crafting Recipes--------------------------------------------------------------// - case Recipe.HitPower.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.HitPower.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.HitPower.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.HitPower.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.HitPower.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.HitPower.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.HitPower.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.HitPower.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - - break; - case Recipe.HitPower.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Blood.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.Blood.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Blood.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.Blood.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Blood.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.Blood.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Blood.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.Blood.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - - break; - case Recipe.Blood.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Caster.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.Caster.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Caster.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.Caster.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Caster.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.Caster.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Caster.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.Caster.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - - break; - case Recipe.Caster.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Safety.Helm: - this.recipes.push({ Ingredients: ingredients, Level: 84, Index: index }); - - break; - case Recipe.Safety.Boots: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Safety.Gloves: - this.recipes.push({ Ingredients: ingredients, Level: 79, Index: index }); - - break; - case Recipe.Safety.Belt: - this.recipes.push({ Ingredients: ingredients, Level: 71, Index: index }); - - break; - case Recipe.Safety.Shield: - this.recipes.push({ Ingredients: ingredients, Level: 82, Index: index }); - - break; - case Recipe.Safety.Body: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); - - break; - case Recipe.Safety.Amulet: - this.recipes.push({ Ingredients: ingredients, Level: 90, Index: index }); - - break; - case Recipe.Safety.Ring: - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); + /** @type {recipeObj} */ + const baseRecipeObj = { + Index: index, + KeyItem: keyItem, + Ingredients: ingredients + }; - break; - case Recipe.Safety.Weapon: - this.recipes.push({ Ingredients: ingredients, Level: 85, Index: index }); + if (itemLevel) { + baseRecipeObj.Level = itemLevel; + } + const recipeObj = Object.assign(baseRecipeObj, extendedOpts); - break; - // Upgrading Recipes------------------------------------------------------------------------// - case Recipe.Unique.Weapon.ToExceptional: - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); + switch (index) { + case Recipe.Gem: + case Recipe.Token: + recipeObj.AlwaysEnabled = true; break; case Recipe.Unique.Weapon.ToElite: // Ladder only - if (me.ladder) { - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - } - - break; - case Recipe.Unique.Armor.ToExceptional: - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; case Recipe.Unique.Armor.ToElite: // Ladder only - if (me.ladder) { - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - } - - break; - case Recipe.Rare.Weapon.ToExceptional: - case Recipe.Rare.Weapon.ToElite: - case Recipe.Rare.Armor.ToExceptional: - case Recipe.Rare.Armor.ToElite: - case Recipe.Socket.Shield: - case Recipe.Socket.Weapon: - case Recipe.Socket.Armor: - case Recipe.Socket.Helm: - case Recipe.Socket.Rare: - this.recipes.push({ Ingredients: ingredients, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.Magic.LowWeapon: - // ilvl < 30 - this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Socket.Magic.HighWeapon: - // ilvl >= 30 - this.recipes.push({ Ingredients: ingredients, Level: 30, Index: index, Ethereal: Config.Recipes[i][2] }); - - break; - case Recipe.Reroll.Charm.Small: - case Recipe.Reroll.Charm.Large: - case Recipe.Reroll.Charm.LowGrand: - case Recipe.Reroll.Charm.Grand: - case Recipe.Reroll.Magic: // Hacky solution ftw - /** - * Charm ilvls based on https://diablo2.diablowiki.net/Guide:Charms_v1.10,_by_Kronos - */ - if (index === Recipe.Reroll.Charm.Small) { - this.recipes.push({ Ingredients: ingredients, Level: 94, Index: index }); - } else if (index === Recipe.Reroll.Charm.Large) { - this.recipes.push({ Ingredients: ingredients, Level: 76, Index: index }); - } else if (index === Recipe.Reroll.Charm.LowGrand) { - this.recipes.push({ Ingredients: ingredients, Level: 50, Index: index }); - } else if (index === Recipe.Reroll.Charm.Grand) { - this.recipes.push({ Ingredients: ingredients, Level: 77, Index: index }); - } else { - this.recipes.push({ Ingredients: ingredients, Level: 91, Index: index }); - } - - break; - case Recipe.Reroll.Rare: - this.recipes.push({ Ingredients: ingredients, Index: index }); - + if (!me.ladder) continue; break; case Recipe.Reroll.HighRare: - this.recipes.push({ Ingredients: ingredients, Index: index, Enabled: false }); - - break; - case Recipe.LowToNorm.Weapon: - case Recipe.LowToNorm.Armor: - this.recipes.push({ Ingredients: ingredients, Index: index }); + recipeObj.Enabled = false; break; case Recipe.Rune: - switch (Config.Recipes[i][1]) { - 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: ingredients, Index: index, AlwaysEnabled: true }); - - break; - case sdk.items.runes.Thul: - case sdk.items.runes.Amn: - case sdk.items.runes.Sol: - case sdk.items.runes.Shael: - case sdk.items.runes.Dol: - this.recipes.push({ Ingredients: ingredients, Index: index }); - - break; - case sdk.items.runes.Hel: - case sdk.items.runes.Io: - case sdk.items.runes.Lum: - case sdk.items.runes.Ko: - case sdk.items.runes.Fal: - case sdk.items.runes.Lem: - case sdk.items.runes.Pul: - case sdk.items.runes.Um: - case sdk.items.runes.Mal: - case sdk.items.runes.Ist: - case sdk.items.runes.Gul: - case sdk.items.runes.Vex: - case sdk.items.runes.Ohm: - case sdk.items.runes.Lo: - case sdk.items.runes.Sur: - case sdk.items.runes.Ber: - case sdk.items.runes.Jah: - case sdk.items.runes.Cham: - case sdk.items.runes.Zod: - if (me.ladder || !me.realm) { - this.recipes.push({ Ingredients: ingredients, Index: index }); - } - - break; + if (keyItem >= sdk.items.runes.Eld && keyItem <= sdk.items.runes.Ort) { + recipeObj.AlwaysEnabled = true; + } else { + // not ladder and online - we can do these recipes on single player + if (!me.ladder && me.realm) continue; } - break; - case Recipe.Token: - this.recipes.push({ Ingredients: ingredients, Index: index, AlwaysEnabled: true }); - - break; - case Recipe.Rejuv: - case Recipe.FullRejuv: - this.recipes.push({ Ingredients: ingredients, Index: index }); - break; } + this.recipes.push(recipeObj); } }, @@ -738,6 +627,31 @@ const Cubing = { let items = me.findItems(-1, sdk.items.mode.inStorage); for (let i = 0; i < this.recipes.length; i += 1) { + const recipe = this.recipes[i]; + + if (recipe.hasOwnProperty("condition") && typeof recipe.condition === "function") { + if (!recipe.condition()) { + console.debug("Skipping recipe due to condition cb"); + continue; + } + } + + if (recipe.hasOwnProperty("MaxQuantity") && typeof recipe.MaxQuantity === "number") { + let itemClassid = recipe.KeyItem; + let itemCount = me.getItemsEx(itemClassid).filter(function (item) { + return item.isInStorage; + }).length; + + if (itemCount >= recipe.MaxQuantity) { + console.debug( + "Skipping recipe due to item count exceeding MaxQuantity." + + " Have: " + itemCount + + ", Wanted: " + recipe.MaxQuantity + ); + continue; + } + } + // Set default Enabled property - true if recipe is always enabled, false otherwise this.recipes[i].Enabled = this.recipes[i].hasOwnProperty("AlwaysEnabled"); @@ -745,16 +659,20 @@ const Cubing = { for (let j = 0; j < this.recipes[i].Ingredients.length; j += 1) { const currIngred = this.recipes[i].Ingredients[j]; for (let k = 0; k < items.length; k += 1) { - if (((currIngred === "pgem" && this.gemList.includes(items[k].classid)) - || (currIngred === "fgem" && this.gems.flawless.includes(items[k].classid)) - || (currIngred === "gem" && this.gems.normal.includes(items[k].classid)) - || (currIngred === "cgem" && this.gems.chipped.includes(items[k].classid)) - || (currIngred === "hpot" && this.pots.healing.includes(items[k].classid)) - || (currIngred === "mpot" && this.pots.mana.includes(items[k].classid)) - || items[k].classid === currIngred) && this.validItem(items[k], this.recipes[i])) { + const item = items[k]; + const { classid, gid } = item; + if (( + (currIngred === "pgem" && this.gemList.includes(classid)) + || (currIngred === "fgem" && this.gems.flawless.includes(classid)) + || (currIngred === "gem" && this.gems.normal.includes(classid)) + || (currIngred === "cgem" && this.gems.chipped.includes(classid)) + || (currIngred === "hpot" && this.pots.healing.includes(classid)) + || (currIngred === "mpot" && this.pots.mana.includes(classid)) + || classid === currIngred) && this.validItem(item, 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, gid: items[k].gid }); + this.validIngredients.push({ classid: classid, gid: gid }); // Remove from item list to prevent counting the same item more than once items.splice(k, 1); @@ -762,7 +680,8 @@ const Cubing = { // Enable recipes for gem/jewel pickup if (this.recipes[i].Index !== Recipe.Rune - || ([Recipe.Rune, Recipe.Rejuv, Recipe.FullRejuv].includes(this.recipes[i].Index) && j >= 1)) { + || ([Recipe.Rune, Recipe.Rejuv, Recipe.FullRejuv].includes(this.recipes[i].Index) && j >= 1) + ) { // Enable rune recipe after 2 bases are found this.recipes[i].Enabled = true; } @@ -1145,16 +1064,19 @@ const Cubing = { this.update(); // Randomize the recipe array to prevent recipe blocking (multiple caster items etc.) - let tempArray = this.recipes.slice().shuffle(); + const tempArray = this.recipes.slice().shuffle(); for (let i = 0; i < tempArray.length; i += 1) { + const recipe = tempArray[i]; + let string = "Transmuting: "; - let items = this.checkRecipe(tempArray[i]); + let items = this.checkRecipe(recipe); if (!Array.isArray(items) || !items.length) continue; // 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 ((!getUIFlag(sdk.uiflags.Cube) && !Town.openStash()) || !this.emptyCube()) { + return false; + } this.cursorCheck(); i = -1; From fc16fd418dda0838f0ae176bf77b5148a5f39af6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Apr 2025 13:12:43 -0400 Subject: [PATCH 504/758] [feat] Enable params to be passed with an actionevent sent to MapHelper - Allow passing a range or quantity param for the pick and stack actions --- .../libs/manualplay/libs/PickitOverrides.js | 8 ++- d2bs/kolbot/libs/manualplay/main.js | 10 +++- .../libs/manualplay/threads/MapHelper.js | 53 ++++++++++++++----- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js index 98c9bd702..e922b8bd6 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PickitOverrides.js @@ -7,13 +7,17 @@ includeIfNotIncluded("core/Pickit.js"); -Pickit.basicPickItems = function () { +/** + * @param {number} range + * @returns {boolean} + */ +Pickit.basicPickItems = function (range) { let itemList = []; let item = Game.getItem(); if (item) { do { - if (item.onGroundOrDropping) { + if (item.onGroundOrDropping && item.distance <= range) { itemList.push(copyUnit(item)); } } while (item.getNext()); diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index 25e42bf1b..6be43a2a6 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -236,7 +236,7 @@ function main () { msg = msg.toLowerCase(); let cmd = msg.split(" ")[0].split(".")[1]; let msgList = msg.split(" "); - let qolObj = { type: "qol", dest: false, action: false }; + let qolObj = { type: "qol", dest: false, action: false, params: [] }; switch (cmd) { case "useraddon": @@ -262,6 +262,10 @@ function main () { case "filltps": qolObj.action = cmd; + if (msgList.length > 1) { + qolObj.params.push(msgList.at(1)); + } + break; case "drop": if (msgList.length < 2) { @@ -282,6 +286,10 @@ function main () { qolObj.type = "stack"; qolObj.action = msgList[1]; + if (msgList.length > 2) { + qolObj.params.push(msgList.at(2)); + } + break; case "help": if (HelpMenu.cleared) { diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 7030fb07c..78f1a3d6f 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -20,13 +20,32 @@ include("systems/gameaction/GameAction.js"); include("manualplay/MapMode.js"); MapMode.include(); +/** + * @typedef {Object} UnitAction + * @property {string} do - The action to perform ('openChest', 'usePortal') + * @property {number} [id] - The ID associated with the action + */ + +/** + * @typedef {'area' | 'unit' | 'wp' | 'actChange' | 'portal' | 'qol' | 'drop' |'stack'} EventType + */ + +/** + * @typedef {Object} ActionEvent + * @property {EventType} type - The type of action to perform + * @property {number|string|null} [dest] - The destination area ID, unit ID, or act number + * @property {UnitAction|string} [action] - Action details or action string + * @property {string[]} params + */ + function main () { // getUnit test getUnit(-1) === null && console.warn("getUnit bug detected"); console.log("ÿc9MapHelper loaded"); - let obj = { type: false, dest: false, action: false }; + /** @type {ActionEvent} */ + const obj = { type: null, dest: null, action: null, params: [] }; let action, fail = 0, x, y; const mapThread = getScript("libs/manualplay/main.js"); @@ -366,7 +385,12 @@ function main () { break; case "pick": - Config.ManualPlayPick ? Pickit.pickItems() : Pickit.basicPickItems(); + { + let range = obj.params.length > 0 && !isNaN(Number(obj.params[0])) + ? Number(obj.params[0]) + : Config.PickRange; + Config.ManualPlayPick ? Pickit.pickItems(range) : Pickit.basicPickItems(range); + } break; case "sellItem": @@ -398,19 +422,24 @@ function main () { break; case "stack": - switch (obj.action) { - case "thawing": - Town.buyPots(10, "Thawing", true, true); + { + let quantity = obj.params.length > 0 && !isNaN(Number(obj.params[0])) + ? Number(obj.params[0]) + : 10; + switch (obj.action) { + case "thawing": + Town.buyPots(quantity, "Thawing", true, true); - break; - case "antidote": - Town.buyPots(10, "Antidote", true, true); + break; + case "antidote": + Town.buyPots(quantity, "Antidote", true, true); - break; - case "stamina": - Town.buyPots(10, "Stamina", true, true); + break; + case "stamina": + Town.buyPots(quantity, "Stamina", true, true); - break; + break; + } } break; From a09e874777e6f45145ebb6a844335a590a7eb89f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Apr 2025 13:21:35 -0400 Subject: [PATCH 505/758] [feat] Enable multi profile pubjoining & move doneGames out of `logs/` --- d2bs/kolbot/D2BotPubJoin.dbj | 71 +++++++++++++++---- .../libs/systems/pubjoin/PubJoinConfig.js | 10 +++ 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index 53e23d6a2..f2d591cba 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -4,6 +4,9 @@ * @desc Entry script for following public games * * @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/torch/TorchSystem")} +* @typedef {import("./libs/systems/crafting/CraftingSystem")} +* @typedef {import("./libs/systems/gambling/Gambling")} */ include("critical.js"); // required @@ -17,7 +20,8 @@ Starter.Config.AttemptNextGameRetrys = 5; Starter.Config.MinPlayers = 1; // Minimum players in game to join const { includeFilter, - excludeFilter + excludeFilter, + profileOverides, } = require("./libs/systems/pubjoin/PubJoinConfig"); // Override default values for StarterConfig under here by following format @@ -35,6 +39,22 @@ if (typeof Starter.AdvancedConfig[me.profile] === "object") { } delete Starter.AdvancedConfig; +if (typeof profileOverides[me.profile] === "object") { + if (profileOverides[me.profile].hasOwnProperty("includeFilter")) { + includeFilter.length = 0; // reset + for (let filter of profileOverides[me.profile].includeFilter) { + includeFilter.push(filter); + } + } + + if (profileOverides[me.profile].hasOwnProperty("excludeFilter")) { + excludeFilter.length = 0; // reset + for (let filter of profileOverides[me.profile].excludeFilter) { + excludeFilter.push(filter); + } + } +} + new Overrides.Override(Starter, Starter.setNextGame, function (orignal, gameName) { function incrementString (text) { return text.replace(/(\d*)$/, (_, t) => (+t + 1).toString().padStart(t.length, 0)); @@ -53,8 +73,39 @@ if (DataFile.init()) { let lastGameTick, retry = 0; let retryTick = 0; +const GameTracker = { + _path: "data/" + me.profile + "/pubjoin.json", + + init: function () { + if (!FileTools.exists("data/" + me.profile)) { + let folder = dopen("data"); + folder.create(me.profile); + } + + if (!FileTools.exists(this._path)) { + FileAction.write(this._path, JSON.stringify([])); + } + }, + + /** + * @returns {string[]} + */ + read: function () { + return FileAction.parse(this._path); + }, + + /** @param {string[]} doneGames */ + update: function (doneGames) { + return FileAction.write(this._path, JSON.stringify(doneGames)); + }, +}; + const locationAction = (function () { - let gameToJoin, doneGames, gameList; + let gameToJoin; + /** @type {{ gameName: string; players: number }[] } */ + let gameList; + /** @type {string[]} */ + let doneGames = []; const Controls = require("./libs/modules/Control"); const { @@ -226,11 +277,8 @@ const locationAction = (function () { } if (gameList) { - doneGames = []; + doneGames = GameTracker.read(); gameToJoin = false; - if (FileTools.exists("logs/doneGames.json")) { - doneGames = JSON.parse(FileAction.read("logs/doneGames.json")); - } gameList .sort(function (a, b) { @@ -252,7 +300,7 @@ const locationAction = (function () { if (gameToJoin) { doneGames.length >= 20 && doneGames.shift(); doneGames.push(gameToJoin); - FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); + GameTracker.update(doneGames); me.blockMouse = true; @@ -351,6 +399,8 @@ function main () { delay(500); } + GameTracker.init(); + while (true) { // returns true before actually in game so we can't only use this check while (me.ingame) { @@ -373,16 +423,13 @@ function main () { Starter.setNextGame(me.gamename); /** @type {string[]} */ - let doneGames = []; - if (FileTools.exists("logs/doneGames.json")) { - doneGames = JSON.parse(FileAction.read("logs/doneGames.json")); - } + let doneGames = GameTracker.read(); doneGames.length >= 20 && doneGames.shift(); if (!doneGames.includes(me.gamename)) { doneGames.push(me.gamename); } - FileAction.write("logs/doneGames.json", JSON.stringify(doneGames)); + GameTracker.update(doneGames); } D2Bot.updateStatus( diff --git a/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js b/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js index 7c6dba24e..56c8291b8 100644 --- a/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js +++ b/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js @@ -42,8 +42,18 @@ [""] ]; + /** + * @type {Record>, excludeFilter?: Array>}>} + */ + const profileOverides = { + // "test": { + // includeFilter: [["trist"], ["tombs"]] + // } + }; + module.exports = { includeFilter: includeFilter, excludeFilter: excludeFilter, + profileOverides: profileOverides, }; })(module); From a43c8026f2fea33b1224b276cf494289cb41ca27 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 7 Apr 2025 02:32:13 -0400 Subject: [PATCH 506/758] Update AreaData.findByName for better partial match support & custom filtering --- d2bs/kolbot/libs/core/GameData/AreaData.js | 141 +++++++++++++++++---- 1 file changed, 119 insertions(+), 22 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/AreaData.js b/d2bs/kolbot/libs/core/GameData/AreaData.js index 230b78d98..20305132e 100644 --- a/d2bs/kolbot/libs/core/GameData/AreaData.js +++ b/d2bs/kolbot/libs/core/GameData/AreaData.js @@ -40,20 +40,51 @@ 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 + * @typedef {Object} AreaDataObj + * @property {number} Super - Number of super uniques present in this area + * @property {number} Index - Area ID + * @property {number} Act - Act this area is in [0-4] + * @property {number} MonsterDensity - Value used to determine monster population density + * @property {Object} ChampionPacks - Champion packs information + * @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 {Object} Size - Size of the area + * @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 {string} LocaleString - Locale string index for getLocaleString + * @property {string} InternalName - Internal name + * @property {function(number): boolean} hasMonsterType - Check if this area has a monster of a certain type + * @property {function(function(any, number): void): void} forEachMonster - Iterate through each monster in this area and apply a callback function + * @property {function(function(any, number, any): void): void} forEachMonsterAndMinion - Iterate through each monster and minion in this area and apply a callback function + * @property {function(): AreaDataObj} townArea - Check if area is a town area + * @property {function(): boolean} haveWaypoint - Check if the area has a waypoint + * @property {function(): (AreaDataObj|undefined)} nearestWaypointArea - Find nearest waypoint in area + * @property {function(): (PresetUnit|undefined)} waypointPreset - Get the waypoint preset unit + */ + + /** + * @typedef {Object} Dungeons + * @property {number[]} DenOfEvil + * @property {number[]} Hole + * @property {number[]} Pit + * @property {number[]} Cave + * @property {number[]} UndergroundPassage + * @property {number[]} Cellar + * @property {number[]} A2Sewers + * @property {number[]} StonyTomb + * @property {number[]} HallsOfDead + * @property {number[]} ClawViperTemple + * @property {number[]} MaggotLair + * @property {number[]} Tombs + * @property {number[]} Swamp + * @property {number[]} FlayerDungeon + * @property {number[]} A3Sewers + * @property {number[]} HighLevelForgottenTemples + * @property {number[]} LowLevelForgottenTemples + * @property {number[]} RedPortalPits */ /** @type {AreaDataObj[]} */ @@ -152,7 +183,7 @@ 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)); + return me.haveWaypoint((wpArea || this.townArea().Index)); }, /** * Find nearest waypoint in area @@ -183,18 +214,84 @@ } /** - * @property {function} AreaData.findByName - * @param {string} whatToFind - * @returns + * @function + * @static + * @name AreaData.findByName + * @param {string} whatToFind + * @param {function(AreaDataObj): boolean} [filter] - Optional filter function to restrict which areas are considered + * @returns {AreaDataObj} */ - 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]); + AreaData.findByName = function (whatToFind, filter) { + if (!whatToFind || typeof whatToFind !== "string") { + return AreaData[1]; + } + + const searchTerm = whatToFind.toLowerCase().trim(); + + const areaPool = typeof filter === "function" + ? AreaData.filter(area => area && area.LocaleString && filter(area)) + : AreaData.filter(area => area && area.LocaleString); + if (areaPool.length === 0) { + return AreaData[1]; + } + + // First attempt: Look for exact matches or contains + const exactMatches = areaPool.filter(function (area) { + if (!area || !area.LocaleString) return false; + + const localeLower = area.LocaleString.toLowerCase(); + const internalLower = area.InternalName ? area.InternalName.toLowerCase() : ""; + + // Exact match gets highest priority + if (localeLower === searchTerm || internalLower === searchTerm) { + return true; + } + + // Contains match gets second priority (handles partial names like "worldstone") + if (localeLower.includes(searchTerm) || internalLower.includes(searchTerm)) { + return true; + } + + return false; + }); + + // If we found exact or contains matches, sort them by length (shorter names first) + // This helps prioritize more specific matches when partial names are given + if (exactMatches.length > 0) { + exactMatches.sort(function (a, b) { + const aLocale = a.LocaleString.toLowerCase(); + const bLocale = b.LocaleString.toLowerCase(); + + // If one contains the term exactly at the start, prioritize it + const aStartsWith = aLocale.startsWith(searchTerm); + const bStartsWith = bLocale.startsWith(searchTerm); + + if (aStartsWith && !bStartsWith) return -1; + if (!aStartsWith && bStartsWith) return 1; + + // Otherwise sort by shortest containing name + return aLocale.length - bLocale.length; + }); + + return exactMatches[0]; + } + + // Fallback: Use the diffCount approach for fuzzy matching + let matches = areaPool + .map(area => { + const localeDiff = area.LocaleString ? searchTerm.diffCount(area.LocaleString.toLowerCase()) : Infinity; + const internalDiff = area.InternalName ? searchTerm.diffCount(area.InternalName.toLowerCase()) : Infinity; + return [Math.min(localeDiff, internalDiff), area]; + }) + .sort((a, b) => a[0] - b[0]); + return matches[0][1]; }; + /** + * @type {Dungeons} + */ AreaData.dungeons = { DenOfEvil: [sdk.areas.DenofEvil], From 80ba2acf92e3bd93cc5ca2112b0e6fb25ef216bb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 7 Apr 2025 02:39:05 -0400 Subject: [PATCH 507/758] Update ControlBot `givewp ` for area sanitization and filtering for areas with wps --- d2bs/kolbot/libs/scripts/ControlBot.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 2811cc9d5..f732f1141 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1580,8 +1580,13 @@ const ControlBot = new Runnable( if (full.match(/^givewp /gi)) { let [, areaName] = full.split("givewp "); if (areaName) { + let cleanedAreaName = areaName.replace(/[<>\[\]{}()]/g, "").trim(); + /** @param {AreaDataObj} area */ + const areaFilter = function (area) { + return area.Waypoint !== 255 && area.Index !== sdk.areas.HallsofPain; + }; /** @type {AreaDataObj} */ - let area = AreaData.findByName(areaName); + let area = AreaData.findByName(cleanedAreaName, areaFilter); if (area.Waypoint === 255) { Chat.say(area.LocaleString + " isn't a valid wp area to ask for"); From ddb9002e71023d196b3bb3b7d9522706f8d5e7b3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 7 Apr 2025 15:25:06 -0400 Subject: [PATCH 508/758] Update PacketBuilder class to use es5 function syntax over arrow methods - This version of spidermonkey doesn't support arrow methods well --- d2bs/kolbot/libs/core/Util.js | 73 +++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index 30f48b059..fb2ec0ab5 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -117,14 +117,13 @@ /** * @class * @description new PacketBuilder() - create new packet object + * @param {number} [initialByte] - Optional initial byte to add to the packet * @example (Spoof 'reassign player' packet to client): * new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer).byte(0).dword(me.gid).word(x).word(y).byte(1).get(); * @example (Spoof 'player move' packet to server): - * new PacketBuilder().byte(sdk.packets.send.RunToLocation).word(x).word(y).send(); - * @todo pass the inital byte into the constructor so we don't always have to do `new PacketBuilder().byte(sdk.packets.recv.ReassignPlayer)...` - * it would just be `new PacketBuilder(sdk.packets.recv.ReassignPlayer)...` + * new PacketBuilder(sdk.packets.send.RunToLocation).word(x).word(y).send(); */ - function PacketBuilder () { + function PacketBuilder (initialByte) { // globals DataView ArrayBuffer if (this.__proto__.constructor !== PacketBuilder) { throw new Error("PacketBuilder must be called with 'new' operator!"); @@ -133,20 +132,35 @@ let queue = []; let count = 0; - // accepts any number of arguments - let enqueue = (type, size) => (...args) => { - args.forEach(arg => { - if (type === "String") { - arg = stringToEUC(arg); - size = arg.length + 1; - } + // If initial byte was provided, add it + if (typeof initialByte === "number") { + queue.push({ type: "Uint8", size: 1, data: initialByte }); + count += 1; + } - queue.push({ type: type, size: size, data: arg }); - count += size; - }); + /** + * Internal function to add data to the packet queue + * @param {string} type - The data type + * @param {number} size - The byte size + * @returns {function} - A function that accepts data and adds it to the queue + */ + function enqueue(type, size) { + return function() { + for (let i = 0; i < arguments.length; i++) { + let arg = arguments[i]; + + if (type === "String") { + arg = stringToEUC(arg); + size = arg.length + 1; + } - return this; - }; + queue.push({ type: type, size: size, data: arg }); + count += size; + } + + return this; + }; + } /** @description size = 4 */ this.float = enqueue("Float32", 4); @@ -158,9 +172,13 @@ this.byte = enqueue("Uint8", 1); this.string = enqueue("String"); - this.buildDataView = () => { + /** + * Builds a DataView from the packet data + * @returns {DataView} - The built packet as a DataView + */ + this.buildDataView = function () { let dv = new DataView(new ArrayBuffer(count)), i = 0; - queue.forEach(field => { + queue.forEach(function (field) { if (field.type === "String") { for (let l = 0; l < field.data.length; l++) { dv.setUint8(i++, field.data.charCodeAt(l), true); @@ -176,8 +194,23 @@ return dv; }; - this.send = () => (sendPacket(this.buildDataView().buffer), this); - this.spoof = () => (getPacket(this.buildDataView().buffer), this); + /** + * Sends the packet to the server + * @returns {PacketBuilder} - Returns this for method chaining + */ + this.send = function() { + sendPacket(this.buildDataView().buffer); + return this; + }; + + /** + * Spoofs the packet to the client + * @returns {PacketBuilder} - Returns this for method chaining + */ + this.spoof = function() { + getPacket(this.buildDataView().buffer); + return this; + }; this.get = this.spoof; // same thing but spoof has clearer intent than get } From 09ce5b6e99d83d1d70b99b9f3d8a811d24e8e7ae Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Apr 2025 18:35:59 -0400 Subject: [PATCH 509/758] ControlBot updates - Add cain, add squlching flooders, add ngvote cooldown - Add cain to available rush commands - Give better timed out message including the players name in the message - Track voting period with a configurable cooldown period to stop users from spamming it when they lose the vote - Auto squlch shitlisted players so they don't cause the bot to be muted by having it repeatedly say, "no commands for the shitlisted" - Squelch flooders instead of just ignoring their messages, this prevents us from even having to process it for the 60 seconds which lets d2bs work better - Catch errors and move on during autochanting, sometimes players left or summons would dissapear causing an error to be thrown. Just log it and move on as this isn't fatal - Add `stopWatcher` to the givewp command so players can end the wait early if they requested something they didn't mean too (still see lots of users get this command wrong idk how) - Clean the input message before attempting to process it - Add messages for den and forge, as users often request these over and over even after seeing the help message that lists available commands - Add message for when users try to cancel the active command - Add message for when users call cancel with no action - Add message for when users request the command that is already running - Make NGVoting configurable like other commands - Change a1 location from portalspot to stash - Add a message after users vote so they know their vote was counted - Add a message for when users vote and their vote has already been counted --- d2bs/kolbot/libs/config/Amazon.js | 4 + d2bs/kolbot/libs/config/Assassin.js | 4 + d2bs/kolbot/libs/config/Barbarian.js | 4 + d2bs/kolbot/libs/config/Druid.js | 4 + d2bs/kolbot/libs/config/Necromancer.js | 4 + d2bs/kolbot/libs/config/Paladin.js | 4 + d2bs/kolbot/libs/config/Sorceress.js | 4 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 4 + d2bs/kolbot/libs/core/Config.js | 23 +- d2bs/kolbot/libs/scripts/ControlBot.js | 425 +++++++++++++----- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 84 ++-- d2bs/kolbot/sdk/types/Config.d.ts | 42 +- 12 files changed, 460 insertions(+), 146 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 953fe99c6..76a0b1a2d 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -253,6 +253,7 @@ function LoadConfig () { Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -274,6 +275,9 @@ function LoadConfig () { Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index c35aecf49..bd1f71136 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -253,6 +253,7 @@ function LoadConfig () { Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -274,6 +275,9 @@ function LoadConfig () { Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index f299682fa..df5565e49 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -253,6 +253,7 @@ function LoadConfig () { Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -274,6 +275,9 @@ function LoadConfig () { Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index d28993c13..b2cbadf06 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -253,6 +253,7 @@ function LoadConfig () { Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -274,6 +275,9 @@ function LoadConfig () { Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 2289fb728..100b0389d 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -253,6 +253,7 @@ function LoadConfig () { Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -274,6 +275,9 @@ function LoadConfig () { Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index c4d9d7bed..c0138136e 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -253,6 +253,7 @@ function LoadConfig () { Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -274,6 +275,9 @@ function LoadConfig () { Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 1b9b8b104..0574f1d27 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -253,6 +253,7 @@ function LoadConfig () { Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -274,6 +275,9 @@ function LoadConfig () { Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index b18c7a394..813bbf46b 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -234,6 +234,7 @@ Config.ControlBot.Rush.Andy = true; // Kill Andy on command Config.ControlBot.Rush.Bloodraven = true; // Kill Bloodraven on command Config.ControlBot.Rush.Smith = true; // Kill Smith on command + Config.ControlBot.Rush.Cain = true; // Rescue cain on command Config.ControlBot.Rush.Cube = true; // Get cube on command Config.ControlBot.Rush.Radament = true; // Kill Radament on command Config.ControlBot.Rush.Staff = true; // Get staff on command @@ -255,6 +256,9 @@ Config.ControlBot.Rush.Baal = true; // Kill Baal on command Config.ControlBot.EndMessage = ""; // Message before quitting Config.ControlBot.GameLength = 20; // Game length in minutes + Config.ControlBot.NGVoting = true; // Allow players to vote on new game + Config.ControlBot.NGVoteCooldown = 3; // Time in minutes after a vote period a players has to wait to start a new vote + Config.ControlBot.MinGameLength = 3; // Minimum time in minutes before a ng vote can be called // ##### ORG/TORCH ##### // Scripts.GetKeys = false; // Hunt for T/H/D keys diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 1c463c15f..fb44843db 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -120,13 +120,17 @@ let Config = { Town: false, }, + // Experimental + FastParty: false, + AutoEquip: false, + UseExperimentalAvoid: false, + // Time StartDelay: 0, PickDelay: 0, AreaDelay: 0, MinGameTime: 0, MaxGameTime: 0, - UnpartyForMinGameTimeWait: false, // Healing and chicken LifeChicken: 0, @@ -341,10 +345,6 @@ let Config = { DCloneQuit: false, DCloneWaitTime: 30, - // Experimental - FastParty: false, - AutoEquip: false, - // GameData ChampionBias: 60, @@ -406,6 +406,7 @@ let Config = { Redemption: [0, 0], Charge: false, Vigor: false, + UseVigorOnLowStam: false, RunningAura: -1, AvoidDolls: false, @@ -571,7 +572,8 @@ let Config = { RecheckSeals: false }, MFHelper: { - BreakClearLevel: false + BreakClearLevel: false, + BreakOnDiaBaal: true, }, Wakka: { Wait: 1, @@ -636,7 +638,10 @@ let Config = { Baal: false, }, EndMessage: "", - GameLength: 20 + GameLength: 20, + MinGameLength: 3, + NGVoting: false, + NGVoteCooldown: 3, }, IPHunter: { IPList: [], @@ -733,7 +738,7 @@ let Config = { }, Rushee: { Quester: false, - Bumper: false + Bumper: false, }, Questing: { StopProfile: false @@ -764,5 +769,5 @@ let Config = { Template: "", Verbose: false, DebugMode: false - } + }, }; diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index f732f1141..439e0ef77 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -35,6 +35,7 @@ const ControlBot = new Runnable( anya, ancients, baal, + timedOut, } = require("../systems/autorush/AutoRush"); const { AutoRush, @@ -43,6 +44,53 @@ const ControlBot = new Runnable( const Worker = require("../modules/Worker"); const AreaData = require("../core/GameData/AreaData"); + /** @param {string} [nick] */ + const cain = function (nick) { + log("starting cain"); + + if (Game.getNPC(NPC.Cain)) { + log("Cain has already been rescued"); + + return true; + } + + Town.doChores(); + Pather.useWaypoint(sdk.areas.StonyField, true); + Precast.doPrecast(true); + if (!Pather.journeyTo(sdk.areas.Tristram)) { + log("Can't get to tristram"); + + return true; + } + + if (me.inArea(sdk.areas.Tristram)) { + Pather.moveTo(me.x, me.y + 6); + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + + if (gibbet && !gibbet.mode) { + if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.CainsJail, 0, 0, true, true)) { + throw new Error("Failed to move to Cain's Jail"); + } + + Attack.securePosition(gibbet.x, gibbet.y, 25, 3000); + Pather.makePortal(); + log(AutoRush.playersIn); + + const cainRescued = Misc.poll(function () { + Attack.securePosition(me.x, me.y, 15, 1000); + return gibbet.mode; + }, Time.minutes(2)); + + if (!cainRescued) { + log(timedOut(nick)); + return false; + } + } + } + + return true; + }; + /** @param {string} [nick] */ const mephisto = function (nick) { log("starting mephisto"); @@ -71,7 +119,7 @@ const ControlBot = new Runnable( if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + timedOut(nick); return false; } @@ -98,6 +146,10 @@ const ControlBot = new Runnable( tick: 0, nextGame: false, undecidedAskTick: 0, + lastVotePeriod: { + startedBy: "", + endedAt: 0, + }, /** * Calculate the number of votes needed for a decision @@ -107,6 +159,14 @@ const ControlBot = new Runnable( return Math.max(1, Math.floor((Misc.getPlayerCount() - 2) / 2)); }, + /** + * Get the time since the last vote period ended + * @returns {number} + */ + timeSinceLastVote: function () { + return getTickCount() - this.lastVotePeriod.endedAt; + }, + /** * Reset the voting state */ @@ -114,18 +174,21 @@ const ControlBot = new Runnable( this.votes.clear(); this.tick = 0; this.active = false; + this.lastVotePeriod.endedAt = getTickCount(); }, /** * Begin a new voting session + * @param {string} nick */ - begin: function() { + begin: function(nick) { this.active = true; this.votes.clear(); for (let player of Misc.getPartyMembers()) { this.votes.set(player.name, "undecided"); } this.tick = getTickCount(); + this.lastVotePeriod.startedBy = nick; }, /** @@ -306,12 +369,39 @@ const ControlBot = new Runnable( return true; }; })(); + + Worker.runInBackground.flooders = (function () { + let tick = getTickCount(); + + return function () { + if (getTickCount() - tick < 0) return true; + // check every 1 second + tick = getTickCount() + Time.seconds(1) + rand(500, 950); + + for (let [key, player] of cmdNicks) { + if (getTickCount() - player.ignoredAt > Time.minutes(1)) { + if (!player.ignored) continue; + let party = getParty(key); + if (!party || !getPlayerFlag(me.gid, party.gid, sdk.player.flag.Squelch)) { + continue; + } + + clickParty(party, sdk.party.controls.Squelch); + player.unIgnore(); + } + } + + return true; + }; + })(); /** @constructor */ function PlayerTracker () { this.firstCmd = getTickCount(); this.commands = 0; this.ignored = false; + this.ignoredAt = 0; + this.toldToChill = false; this.seenHelpMsg = false; } @@ -320,8 +410,22 @@ const ControlBot = new Runnable( this.commands = 0; }; + /** @param {string} nick */ + PlayerTracker.prototype.ignore = function (nick) { + let party = getParty(nick); + if (!party || getPlayerFlag(me.gid, party.gid, sdk.player.flag.Squelch)) { + return; + } + + clickParty(party, sdk.party.controls.Squelch); + + this.ignored = true; + this.ignoredAt = getTickCount(); + }; + PlayerTracker.prototype.unIgnore = function () { this.ignored = false; + this.ignoredAt = 0; this.commands = 0; }; @@ -562,19 +666,23 @@ const ControlBot = new Runnable( if (unit) { do { - if (unit === me.name || unit.dead) continue; - if (me.shitList.has(unit.name)) continue; - if (!Misc.inMyParty(unit.name) || unit.distance > 40) continue; - // allow rechanting someone if it's going to run out soon for them - if (!unit.getState(sdk.states.Enchant) - || (chantList.has(unit.name) && chantList.get(unit.name).reChant())) { - Packet.enchant(unit); - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); - chantList.has(unit.name) - ? chantList.get(unit.name).update() - : chantList.set(unit.name, new ChantTracker()); + try { + if (unit === me.name || unit.dead) continue; + if (me.shitList.has(unit.name)) continue; + if (!Misc.inMyParty(unit.name) || unit.distance > 40) continue; + // allow rechanting someone if it's going to run out soon for them + if (!unit.getState(sdk.states.Enchant) + || (chantList.has(unit.name) && chantList.get(unit.name).reChant())) { + Packet.enchant(unit); + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + chantList.has(unit.name) + ? chantList.get(unit.name).update() + : chantList.set(unit.name, new ChantTracker()); + } } + } catch (err) { + console.error(err); } } while (unit.getNext()); } @@ -583,15 +691,19 @@ const ControlBot = new Runnable( if (unit) { do { - if (unit.getParent() - && chantList.has(unit.getParent().name) - && !unit.getState(sdk.states.Enchant) - && unit.distance <= 40) { - Packet.enchant(unit); - // not going to re-enchant the minions for now though, will think on how best to handle that later - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); + try { + if (unit.getParent() + && chantList.has(unit.getParent().name) + && !unit.getState(sdk.states.Enchant) + && unit.distance <= 40) { + Packet.enchant(unit); + // not going to re-enchant the minions for now though, will think on how best to handle that later + if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(unit.name); + } } + } catch (err) { + console.error(err); } } while (unit.getNext()); } @@ -766,6 +878,18 @@ const ControlBot = new Runnable( * @returns {boolean} */ const giveWp = function (nick, areaId) { + let stop = false; + /** + * @param {string} who + * @param {string} msg + */ + const stopWatcher = function (who, msg) { + if (who !== nick) return; + if (msg === "stop" || msg === "abort") { + stop = true; + } + }; + try { if (!Misc.inMyParty(nick)) { throw new ScriptError("Accept party invite, noob."); @@ -791,6 +915,7 @@ const ControlBot = new Runnable( let act = Misc.getPlayerAct(nick); if (!wps.has(act)) return false; + addEventListener("chatmsg", stopWatcher); Pather.useWaypoint(areaId, true); if (Config.ControlBot.Wps.SecurePortal) { Attack.securePosition(me.x, me.y, 20, 1000); @@ -798,7 +923,7 @@ const ControlBot = new Runnable( Pather.makePortal(); Chat.say(getAreaName(me.area) + " TP up"); - if (!Misc.poll(() => (Game.getPlayer(nick)), Time.seconds(30), Time.seconds(1))) { + if (!Misc.poll(() => (stop || Game.getPlayer(nick)), Time.seconds(30), Time.seconds(1))) { Chat.say(nick + " didn't show up. Aborting wp giving."); } @@ -818,6 +943,8 @@ const ControlBot = new Runnable( } return false; + } finally { + removeEventListener("chatmsg", stopWatcher); } }; @@ -956,21 +1083,18 @@ const ControlBot = new Runnable( } const player = cmdNicks.get(nick); + // with new flooder worker we shouldn't get here unless it failed but handle it anyway if (player.ignored) { - if (getTickCount() - player.ignored < Time.minutes(1)) { - return true; // ignore flooder - } - - // unignore flooder - player.unIgnore(); + return true; } player.commands += 1; if (getTickCount() - player.firstCmd < Time.seconds(10)) { if (player.commands > 5) { - player.ignored = getTickCount(); - Chat.whisper(nick, "You are being ignored for 60 seconds because of flooding."); + Chat.say("spamming gets you nonwhere but a timeout, enjoy a minute of being ignored"); + player.ignore(nick); + return true; } } else { player.resetCmds(); @@ -987,7 +1111,7 @@ const ControlBot = new Runnable( if (gold) { do { if (gold.onGroundOrDropping && gold.distance <= 20 && Pickit.canPick(gold)) { - Pickit.pickItem(gold) && Chat.overhead("Thank you!", true); + Pickit.pickItem(gold) && me.inTown && Chat.overhead("Thank you!", true); if (startPos.distance > 5) { Pather.move(startPos); } @@ -1157,64 +1281,115 @@ const ControlBot = new Runnable( function chatEvent (nick, msg) { if (!nick || !msg) return; if (nick === me.name) return; - msg = msg.toLowerCase(); - const full = msg.replace(/[\'\<\>\[\]\{\}\(\)\!\@\#\$\%\^\&\*\_\+\=\|\~\`\;\:\"\?\,\.\/\\]/g, ""); - if (msg.match(/^rush /gi)) { - msg = msg.split(" ")[1]; - } else if (msg.match(/^givewp /gi)) { - msg = msg.slice(0, 6).trim(); - } else if (msg.match(/^cancel /gi)) { - msg = msg.slice("cancel ".length); + /** + * @param {string} input + */ + const cleanMsg = function (input) { + return input + .replace(/[\'\<\>\[\]\{\}\(\)\!\@\#\$\%\^\&\*\_\+\=\|\~\`\;\:\"\?\,\.\/\\]|plz|please/g, "") + .toLowerCase() + .trim(); + }; + const full = cleanMsg(msg); + const denRegex = /^\b(den|den ?of ?evil)\b/; + const forgeRegex = /^\b(forge|hell ?forge)\b/; + let chatCmd = full; + + if (chatCmd.match(/^rush /gi)) { + chatCmd = chatCmd.split(" ")[1]; + } else if (chatCmd.match(/^givewp /gi)) { + chatCmd = chatCmd.slice(0, 6).trim(); + } else if (chatCmd.match(/^cancel /gi)) { + chatCmd = chatCmd.slice("cancel ".length); + + if (chatCmd === running.command && String.isEqual(nick, running.nick)) { + Chat.say("Can't cancel the active action"); + return; + } + const cmdIndex = queue.findIndex(function (item) { const [cmd, commander] = item; if (!String.isEqual(nick, commander)) { return false; } - return String.isEqual(msg, cmd); + return String.isEqual(chatCmd, cmd); }); if (cmdIndex !== 1) { - Chat.say("Removing " + msg + " from the queue"); + Chat.say("Removing " + chatCmd + " from the queue"); queue.splice(cmdIndex, 1); return; } + return; + } + + if (denRegex.test(chatCmd) || forgeRegex.test(chatCmd)) { + Chat.say(chatCmd + " is not one of the commands"); + + return; } - if (commandAliases.has(msg)) { - msg = commandAliases.get(msg); + if (chatCmd === "cancel") { + Chat.say("Cancel must be used with a command, i.e cancel andy"); + + return; } - if (msg.match(/^drop /gi) && !Config.ControlBot.DropGold) { - msg = "troll"; + + if (commandAliases.has(chatCmd)) { + chatCmd = commandAliases.get(chatCmd); + } + + if (chatCmd.match(/^drop /gi) && !Config.ControlBot.DropGold) { + chatCmd = "troll"; } - if (!actions.has(msg)) { + + if (!actions.has(chatCmd)) { return; } + if (me.shitList.has(nick)) { Chat.say("No commands for the shitlisted."); } else { - if (running.nick === nick && running.command === msg) { - console.debug("Command already running."); + if (running.nick === nick && running.command === chatCmd) { + console.debug("Command already running. active ", running); + if (cmdNicks.get(nick).toldToChill) return; + if (running.command === "wps") { + Chat.whisper(nick, "chill I'm already running wps if you want me to stop type stop"); + } else { + Chat.whisper(nick, "chill I've already started. spamming doesn't make this go faster"); + } + cmdNicks.get(nick).toldToChill = true; return; } - if (!floodCheck([msg, nick])) { - if (["help", "timeleft", "ngyes", "ngno"].includes(msg)) { - actions.get(msg).run(nick); + if (actions.get(chatCmd).desc.toLowerCase().includes("rush")) { + if (running.command === chatCmd) { + Chat.whisper(nick, "I'm already runnning that for " + running.nick); + + return; + } + } + if (!floodCheck([chatCmd, nick])) { + if (["help", "timeleft", "ngyes", "ngno"].includes(chatCmd)) { + actions.get(chatCmd).run(nick); return; } } let index = queue.findIndex(function (cmd) { - return cmd[0] === msg && cmd[1] === nick; + return cmd[0] === chatCmd && cmd[1] === nick; }); if (index > -1) { Chat.whisper(nick, "You already requested this command. Queue position: " + (index + 1)); } else { - queue.push([msg, nick, full]); + queue.push([chatCmd, nick, full]); console.log(queue); if (queue.length > 1 || running.nick !== "") { - Chat.whisper(nick, msg + " has been added to the queue. Queue position: " + (queue.length + 1)); + Chat.whisper(nick, chatCmd + " has been added to the queue. position: " + (queue.length + 1)); + if (running.command) { + Chat.say("currently running " + running.command + " for " + running.nick); + } } } } @@ -1242,6 +1417,15 @@ const ControlBot = new Runnable( players.set(name1, ""); } + try { + // autosqelch shitlisted players + if (me.shitList.has(name1)) { + clickParty(getParty(name1), sdk.party.controls.Squelch); + } + } catch (err) { + console.error(err); + } + break; case 0x00: // "%Name1(%Name2) dropped due to time out." case 0x01: // "%Name1(%Name2) dropped due to errors." @@ -1262,6 +1446,7 @@ const ControlBot = new Runnable( * @property {boolean} [complete] * @property {function(): void} [markAsComplete] * @property {function(): boolean | void} run + * @property {string} type */ /** @type {Map 5 && Town.move("portalspot"); + Town.getDistance("stash") > 8 && Town.move("stash"); if (queue.length > 0) { try { @@ -1650,10 +1868,11 @@ const ControlBot = new Runnable( if (command && !floodCheck(command)) { if (runAction(command)) { // check if command was for rush, if so we need to remove that as an option since its now completed - if (actions.get(running.command).desc.includes("Rush")) { + if (actions.get(running.command.split(" ")[0]).desc.includes("Rush")) { console.log("Disabling " + running.command + " from actions"); - actions.get(running.command).markAsComplete(); + actions.get(running.command).markAsComplete(running.nick); } + cmdNicks.get(running.nick).toldToChill = false; } } } catch (e) { diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index ac2ef5582..1dbcec6f0 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -21,6 +21,10 @@ sayMsg && say(msg); }; + const timedOut = function (nick = "") { + return nick ? "timed out waiting for " + nick : "timed out"; + }; + /** * @param {number} area * @param {string} [nick] @@ -165,7 +169,7 @@ }, AutoRush.playerWaitTimeout, 1000); if (!playersLeftTrist) { - log("timed out"); + log(timedOut(nick)); return false; } } @@ -197,7 +201,7 @@ Pather.move(safeNode); return false; }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -249,7 +253,7 @@ Pather.moveTo(22582, 9612); return false; }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -289,7 +293,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } if (AutoRush.rushMode !== RushModes.chanter) { @@ -305,6 +309,11 @@ const radament = function (nick) { log("starting radament"); + /** + * @param {Monster} unit + * @param {number} range + * @returns {boolean} + */ const moveIntoPos = function (unit, range) { let coords = []; let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); @@ -358,7 +367,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -377,7 +386,7 @@ if (!Misc.poll(function () { return !playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -386,9 +395,13 @@ log(AutoRush.allIn); if (!Misc.poll(function () { + // often happens with chanter mode where users don't listen to the commands + if (!Game.getItem(sdk.quest.item.BookofSkill)) { + return true; + } return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -424,7 +437,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -459,7 +472,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -491,7 +504,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -556,7 +569,7 @@ Attack.securePosition(me.x, me.y, 25, 500); return false; }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -616,7 +629,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -627,7 +640,7 @@ if (!Misc.poll(function () { return !playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -651,7 +664,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -665,7 +678,7 @@ if (AutoRush.rushMode !== RushModes.chanter) { for (let i = 0; i < 3; i++) { - log("a3"); + log("changeact 3"); let playersMoved = Misc.poll(function () { return !playersInAct(3); @@ -708,7 +721,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } if (AutoRush.rushMode !== RushModes.chanter) { @@ -741,7 +754,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -749,7 +762,7 @@ if (!Misc.poll(function () { return !playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } } @@ -782,7 +795,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -815,7 +828,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -848,7 +861,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -877,7 +890,7 @@ new PathNode(wpCoords.x - 4, wpCoords.y - 92), new PathNode(wpCoords.x + 25, wpCoords.y - 70), new PathNode(wpCoords.x + 20, wpCoords.y - 123), - ].forEach((node) => { + ].forEach(function (node) { Pather.move(node) && Attack.securePosition(node.x, node.y, 25, 2500); }); @@ -890,7 +903,7 @@ Attack.securePosition(portalSpot.x, portalSpot.y, 25, 1000); return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -922,7 +935,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -980,6 +993,11 @@ const izual = function (nick) { log("starting izual"); + /** + * @param {Monster} unit + * @param {number} range + * @returns {boolean} + */ const moveIntoPos = function (unit, range) { let coords = []; let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); @@ -1038,7 +1056,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -1071,7 +1089,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -1135,7 +1153,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -1184,8 +1202,13 @@ } if (AutoRush.rushMode === RushModes.chanter) { + let malahspotion = me.getItem(sdk.quest.item.MalahsPotion); + if (malahspotion) { + malahspotion.drop(); + } log("Talk to Malah to get potion then come in"); } + Pather.makePortal(); if (AutoRush.rushMode !== RushModes.chanter) { log(AutoRush.playersIn); @@ -1194,7 +1217,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -1257,7 +1280,7 @@ if (!Misc.poll(function () { return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { - log("timed out"); + log(timedOut(nick)); return false; } @@ -1287,6 +1310,7 @@ delay(500); quit(); } + scriptBroadcast("rush-removewp " + sdk.areas.WorldstoneLvl2); return false; } @@ -1297,6 +1321,7 @@ delay(500); quit(); } + scriptBroadcast("rush-removewp " + sdk.areas.WorldstoneLvl2); return false; } @@ -1374,6 +1399,7 @@ module.exports = { log: log, + timedOut: timedOut, playerIn: playerIn, playersInAct: playersInAct, bumperCheck: bumperCheck, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 4c67d3c31..f3628824a 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -7,6 +7,12 @@ declare global { // interface Scripts { [data: string]: Partial | boolean } + type ExtendedCubingOpts = { Ethereal: number, MaxQuantity: number, condition: () => boolean }; + type CubingRecipe = + | [number, string] + | [number, string, number] + | [number, string, ExtendedCubingOpts]; + interface Config { init(notify: any): void; Loaded: boolean; @@ -17,12 +23,21 @@ declare global { Skill: boolean, Town: boolean, }; + + // Experimental + FastParty: boolean; + AutoEquip: boolean; + UseExperimentalAvoid: boolean; + + Experimental: { + } + StartDelay: number; PickDelay: number; AreaDelay: number; MinGameTime: number; - UnpartyForMinGameTimeWait: boolean; MaxGameTime: number; + UnpartyForMinGameTimeWait: boolean; LifeChicken: number; ManaChicken: number; UseHP: number; @@ -73,6 +88,16 @@ declare global { MinUnids: number; }; Inventory: number[][]; + SortSettings: { + SortInventory: boolean, + SortStash: boolean, + PlugYStash: boolean, + ItemsSortedFromLeft: number[], + ItemsSortedFromRight: number[], + PrioritySorting: boolean, + ItemsSortedFromLeftPriority: number[], + ItemsSortedFromRightPriority: number[], + }, LocalChat: { Enabled: boolean; Toggle: boolean; @@ -134,7 +159,7 @@ declare global { Cubing: boolean; CubeRepair: boolean; RepairPercent: number; - Recipes: any[]; + Recipes: CubingRecipe[]; MakeRunewords: boolean; Runewords: any[][]; KeepRunewords: any[]; @@ -169,8 +194,6 @@ declare global { KillDclone: boolean; DCloneQuit: boolean; DCloneWaitTime: number; - FastParty: boolean; - AutoEquip: boolean; ChampionBias: number; UseCta: boolean; Dodge: boolean; @@ -325,6 +348,7 @@ declare global { KillNihlathak: boolean; FastChaos: boolean; DollQuit: boolean; + SoulQuit: boolean; KillBaal: boolean; SkipTP: boolean; }; @@ -361,6 +385,7 @@ declare global { }; MFHelper: { BreakClearLevel: boolean; + BreakOnDiaBaal: boolean; }; Wakka: { Wait: number; @@ -380,6 +405,11 @@ declare global { Mode: number; Wp: number; }; + Idle: { + Advertise: boolean; + AdvertiseMessage: string; + MaxGameLength: number; + }; ControlBot: { Bo: boolean; Cows: { @@ -404,7 +434,6 @@ declare global { Staff: boolean; Summoner: boolean; Duriel: boolean; - Gidbinn: boolean; LamEsen: boolean; Eye: boolean; Heart: boolean; @@ -420,6 +449,9 @@ declare global { }; EndMessage: string; GameLength: number; + MinGameLength: number; + NGVoting: boolean; + NGVoteCooldown: number; }; IPHunter: { IPList: any[]; From 6f4b557bfa82702f5f715f7d323e7ec26b425925 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Apr 2025 03:56:49 -0400 Subject: [PATCH 510/758] Update manualplay HelpMenu, changed to module and added close icon --- d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js | 165 ------------- .../kolbot/libs/manualplay/hooks/TextHooks.js | 195 ++++++++++++---- d2bs/kolbot/libs/manualplay/main.js | 26 ++- .../libs/manualplay/modules/HelpMenu.js | 217 ++++++++++++++++++ 4 files changed, 382 insertions(+), 221 deletions(-) delete mode 100644 d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js create mode 100644 d2bs/kolbot/libs/manualplay/modules/HelpMenu.js diff --git a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js b/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js deleted file mode 100644 index c2a5e805f..000000000 --- a/d2bs/kolbot/libs/manualplay/hooks/HelpMenu.js +++ /dev/null @@ -1,165 +0,0 @@ -/* eslint-disable max-len */ -/** -* @filename HelpMenu.js -* @author theBGuy -* @desc Help Menu for MapThread -* -*/ - -const HelpMenu = new function () { - this.hooks = []; - this.box = []; - this.cleared = true; - this.helpBoxX = 715 + (me.screensize ? 0 : -160); - this.helpBoxY = 88 + 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)); - this.helpBoxTextX = 647 + (me.screensize ? 0 : -160); - this.helpBoxTextY = 88 + 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)) + 15; - this.action = []; - this.actionY = -1; - this.tick = 0; - this.chatCommands = { - "me": "Displays Character level, Area, and x/y coordinates", - "pick": "Pick items from the ground to inventory", - "hide": "Hide this menu", - "make": "create config file with current characters name", - "stash": "Stash items/gold from inventory", - "gamble": "Start gambling", - "filltps": "Fill tp tome", - "cowportal": "Make cow portal as long as bot already has leg", - "uberportal": "Make uber portal(s) as long as bot already has key", - "ubertrist": "Make uber tristam portal as long as bot already has organs", - "useraddon": "Toggles useraddon mode", - "Ctrl": "Hover over an item then press Ctrl to move that item from one area to the next. In example: Stash to Inventory, Cube to Inventory, Inventory to TradeScreen, or Inventory to Shop (sellItem)", - "drop": { - "invo": "Drop all items in the inventory", - "stash": "Drop all items in the stash excluding the cube" - }, - "stack": { - "antidote": "Buy and Stack 10 antidote potions for 5 minutes of boosted poison resistance", - "thawing": "Buy and Stack 10 thawing potions for 5 minutes of boosted cold resistance", - "stamina": "Buy and Stack 10 stamina potions for 5 minutes of boosted stamina", - }, - "Num": { - "9:": "Stops current pathing action", - }, - }; - - this.hookHandler = function (click, x, y) { - // Left click - if (click === 0) { - // give a small timeout between clicks - if (getTickCount() - HelpMenu.tick > 1000) { - HelpMenu.action.push([x, y]); - } - // Block click - return true; - } - - return false; - }; - - this.addHook = function (text) { - this.hooks.push(new Text("ÿc4." + text, this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); - }; - - this.showMenu = function () { - this.cleared = false; - - let chatCommands = [ - "me", - "pick", - "hide", - "make", - "stash", - "gamble", - "filltps", - "cowportal", - "uberportal", - "ubertrist", - "useraddon", - "drop invo", - "drop stash", - "stack antidote", - "stack thawing", - "stack stamina", - ]; - - this.hooks.push(new Text("ÿc2Chat Commands:", this.helpBoxTextX, this.helpBoxTextY, 0, 0, 0)); - - for (let i = 0; i < chatCommands.length; i++) { - this.addHook(chatCommands[i]); - } - - this.hooks.push(new Text("ÿc2Key Commands:", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0)); - this.hooks.push(new Text("ÿc4 Ctrl : ÿc0Move Item", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); - this.hooks.push(new Text("ÿc4 Pause : ÿc1Pause Map", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Delete: ÿc1Quick Exit", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 End : ÿc1Stop Profile", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num 9: ÿc1Stop Action", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); - this.hooks.push(new Text("ÿc4 Num / : ÿc1Reload", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num + : ÿc0Show Stats", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num * : ÿc0Precast", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - this.hooks.push(new Text("ÿc4 Num . : ÿc0Log Character", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); - - this.box.push(new Box(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 0x0, 1, 2)); - this.box.push(new Frame(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 2)); - this.box[this.box.length - 2].zorder = 0; - }; - - this.hideMenu = function () { - this.cleared = true; - - while (this.hooks.length > 0) { - this.hooks.shift().remove(); - } - - while (this.box.length > 0) { - this.box.shift().remove(); - } - - return; - }; - - this.sortHooks = function (h1, h2) { - return Math.abs(h1.y - HelpMenu.actionY) - Math.abs(h2.y - HelpMenu.actionY); - }; -}; - -let Worker = require("../../modules/Worker"); - -Worker.runInBackground.helpAction = function () { - while (HelpMenu.action.length > 0) { - HelpMenu.tick = getTickCount(); - HelpMenu.actionY = HelpMenu.action.shift()[1]; - // Sort hooks - HelpMenu.hooks.sort(HelpMenu.sortHooks); - - let cmd = HelpMenu.hooks[0].text.split(" ")[0].split(".")[1]; - let msgList = HelpMenu.hooks[0].text.split(" "); - - if (!HelpMenu.hooks[0].text.includes(".")) { - cmd = HelpMenu.hooks[0].text.split(" ")[1]; - } - - try { - let str = ""; - - if (msgList.length === 2 && typeof HelpMenu.chatCommands[cmd] === "object") { - str = HelpMenu.chatCommands[cmd][msgList[1]]; - } else if (msgList.length > 2 && typeof HelpMenu.chatCommands[cmd] === "object") { - str = HelpMenu.chatCommands[cmd][msgList[2]]; - } else { - str = HelpMenu.chatCommands[cmd]; - } - - !!str && me.overhead(str); - } catch (e) { - console.error(e); - me.overhead(cmd); - } - - delay(150); - } - - return true; -}; diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js index e95b3e064..bb97536d2 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js @@ -1,10 +1,10 @@ /* eslint-disable max-len */ /** -* @filename TextHooks.js -* @author theBGuy -* @desc Text hooks for MapThread -* -*/ + * @filename TextHooks.js + * @author theBGuy + * @desc Text hooks for MapThread + * + */ const TextHooks = { enabled: true, @@ -24,13 +24,21 @@ const TextHooks = { statusHooks: [], dashBoard: [], qolHooks: [], + /** @type {{ name: string, hook: Text }[]} */ hooks: [], yLocMapScale: { 1: 40, 2: 30, 3: 20, 4: 10, 6: -10, 9: -40 }, - modifier: 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) + Number(!!me.gameserverip && !me.realm)), + ip: (me.gameserverip.length > 0 ? me.gameserverip.split(".")[3] : "0"), + modifier: + 16 + * (Number(!!me.diff) + + Number(!!me.gamepassword) + + Number(!!me.gametype) + + Number(!!me.gamename) + + Number(!!me.gameserverip && !me.realm)), getScale: function (hkLen) { if (!!TextHooks.yLocMapScale[hkLen]) { - TextHooks.frameYSizeScale = (-1 * this.yLocMapScale[hkLen]); + TextHooks.frameYSizeScale = -1 * this.yLocMapScale[hkLen]; TextHooks.frameYLocScale = this.yLocMapScale[hkLen]; } else { TextHooks.frameYSizeScale = 0; @@ -54,26 +62,33 @@ const TextHooks = { TextHooks.frameworkDisplayed = true; } - this.displaySettings ? !this.getHook("showSettings", this.statusHooks) && this.add("showSettings") : !this.getHook("hideSettings", this.statusHooks) && this.add("hideSettings"); + this.displaySettings + ? !this.getHook("showSettings", this.statusHooks) && this.add("showSettings") + : !this.getHook("hideSettings", this.statusHooks) && this.add("hideSettings"); this.displayTitle && !this.getHook("title", this.hooks) && this.add("title"); - !this.getHook("ping", this.hooks) ? this.add("ping") : (this.getHook("ping", this.hooks).hook.text = "Ping: " + me.ping); + !this.getHook("ping", this.hooks) + ? this.add("ping") + : (this.getHook("ping", this.hooks).hook.text = "Ping: " + me.ping); !this.getHook("time", this.hooks) ? this.add("time") : (this.getHook("time", this.hooks).hook.text = this.timer()); }, update: function (hkLen = 0) { - let updateSettingsDisplay = (this.displaySettings && this.settingsModifer < Math.max(0, hkLen - 3)); + let updateSettingsDisplay = this.displaySettings && this.settingsModifer < Math.max(0, hkLen - 3); this.getScale(hkLen); this.add("dashboard"); updateSettingsDisplay && this.add("showSettings"); - (this.lastAct !== me.act || this.wasInTown !== me.inTown || !this.getHook("qolBoard", this.qolHooks)) && this.add("qolBoard"); + + if ((this.lastAct !== me.act || this.wasInTown !== me.inTown || !this.getHook("qolBoard", this.qolHooks))) { + this.add("qolBoard"); + } }, hookHandler: function (click, x, y) { function sortHooks(h1, h2) { return Math.abs(h1.hook.y - y) - Math.abs(h2.hook.y - y); } - + if (click === 0) { TextHooks.statusHooks.sort(sortHooks); @@ -89,41 +104,49 @@ const TextHooks = { add: function (name, hookArr = []) { let orginalLen = hookArr.length; - + switch (name) { case "credits": this.hooks.push({ name: "credits", - hook: new Text("MM by theBGuy", 0, 600 + Hooks.resfix.y, 4, 0, 0) + hook: new Text("MM by theBGuy", 0, 600 + Hooks.resfix.y, 4, 0, 0), }); break; case "title": this.hooks.push({ name: "title", - hook: new Text(":: Running Map-Mode, enter .help in chat to see more commands ::", 0, 13, 4, 0, 0) + hook: new Text(":: Running Map-Mode, enter .help in chat to see more commands ::", 0, 13, 4, 0, 0), }); break; case "ping": this.hooks.push({ name: "ping", - hook: new Text("Ping: " + me.ping, 785 + Hooks.resfix.x, 56 + this.modifier, 4, 1, 1) + hook: new Text("Ping: " + me.ping, 785 + Hooks.resfix.x, 56 + this.modifier, 4, 1, 1), }); break; case "time": this.hooks.push({ name: "time", - hook: new Text(this.timer(), 785 + Hooks.resfix.x, 72 + this.modifier, 4, 1, 1) + hook: new Text(this.timer(), 785 + Hooks.resfix.x, 72 + this.modifier, 4, 1, 1), }); break; case "ip": this.hooks.push({ name: "ip", - hook: new Text("IP: " + (me.gameserverip.length > 0 ? me.gameserverip.split(".")[3] : "0"), 785 + Hooks.resfix.x, 88 + this.modifier, 4, 1, 1) + hook: new Text( + "IP: " + TextHooks.ip, + 785 + Hooks.resfix.x, + 88 + this.modifier, + 4, + 1, + 1 + ), }); + this.hooks[this.hooks.length - 1].hook.zorder = 0; break; case "dashboard": @@ -133,12 +156,26 @@ const TextHooks = { this.dashBoard.push({ name: "dashboard", - hook: new Box(Hooks.dashBoard.x, Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, 225, 60 + this.frameYSizeScale, 0x0, 1, 0) + hook: new Box( + Hooks.dashBoard.x, + Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, + 225, + 60 + this.frameYSizeScale, + 0x0, + 1, + 0 + ), }); this.dashBoard.push({ name: "dashboardframe", - hook: new Frame(Hooks.dashBoard.x, Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, 225, 60 + this.frameYSizeScale, 0) + hook: new Frame( + Hooks.dashBoard.x, + Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, + 225, + 60 + this.frameYSizeScale, + 0 + ), }); this.getHook("dashboard", this.dashBoard).hook.zorder = 0; @@ -147,33 +184,57 @@ const TextHooks = { case "key5": this.qolHooks.push({ name: "key5", - hook: new Text("Key 5: " + (me.inTown ? "Heal" : "Make Portal"), Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) + hook: new Text( + "Key 5: " + (me.inTown ? "Heal" : "Make Portal"), + Hooks.qolBoard.x + 5 + Hooks.resfix.x, + 545 - this.qolHooks.length * 10 + Hooks.resfix.y, + 4 + ), }); break; case "key6": this.qolHooks.push({ name: "key6", - hook: new Text("Key 6: " + (me.inTown ? "Open Stash" : "Go To Town"), Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) + hook: new Text( + "Key 6: " + (me.inTown ? "Open Stash" : "Go To Town"), + Hooks.qolBoard.x + 5 + Hooks.resfix.x, + 545 - this.qolHooks.length * 10 + Hooks.resfix.y, + 4 + ), }); break; case "nextAct": - me.inTown && me.accessToAct(me.act + 1) && this.qolHooks.push({ - name: "Next Act", - dest: me.act + 1, - type: "actChange", - hook: new Text("Shift > : Next Act", Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) - }); + me.inTown + && me.accessToAct(me.act + 1) + && this.qolHooks.push({ + name: "Next Act", + dest: me.act + 1, + type: "actChange", + hook: new Text( + "Shift > : Next Act", + Hooks.qolBoard.x + 5 + Hooks.resfix.x, + 545 - this.qolHooks.length * 10 + Hooks.resfix.y, + 4 + ), + }); break; case "previousAct": - me.inTown && me.act > 1 && this.qolHooks.push({ - name: "Previous Act", - dest: me.act - 1, - type: "actChange", - hook: new Text("Shift < : Previous Act", Hooks.qolBoard.x + 5 + Hooks.resfix.x, 545 - (this.qolHooks.length * 10) + Hooks.resfix.y, 4) - }); + me.inTown + && me.act > 1 + && this.qolHooks.push({ + name: "Previous Act", + dest: me.act - 1, + type: "actChange", + hook: new Text( + "Shift < : Previous Act", + Hooks.qolBoard.x + 5 + Hooks.resfix.x, + 545 - this.qolHooks.length * 10 + Hooks.resfix.y, + 4 + ), + }); break; case "qolBoard": @@ -191,12 +252,26 @@ const TextHooks = { this.qolHooks.push({ name: "qolBoard", - hook: new Box(Hooks.qolBoard.x + Hooks.resfix.x, Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, 140, 60 + (-1 * this.qolFrameYSize), 0x0, 1, 0) + hook: new Box( + Hooks.qolBoard.x + Hooks.resfix.x, + Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, + 140, + 60 + -1 * this.qolFrameYSize, + 0x0, + 1, + 0 + ), }); this.qolHooks.push({ name: "qolFrame", - hook: new Frame(Hooks.qolBoard.x + Hooks.resfix.x, Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, 140, 60 + (-1 * this.qolFrameYSize), 0) + hook: new Frame( + Hooks.qolBoard.x + Hooks.resfix.x, + Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, + 140, + 60 + -1 * this.qolFrameYSize, + 0 + ), }); this.qolHooks[this.qolHooks.length - 2].hook.zorder = 0; @@ -205,28 +280,44 @@ const TextHooks = { case "pickitStatus": this.statusHooks.push({ name: "pickitStatus", - hook: new Text("ÿc4N-Pad - ÿc0: " + (ItemHooks.pickitEnabled ? "ÿc 1000) { + HelpMenu.action.push([x, y]); + } + // Block click + return true; + } + + return false; + }, + + /** + * @param {string} text + */ + addHook: function (text) { + this.hooks.push(new Text("ÿc4." + text, this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); + }, + + showMenu: function () { + this.cleared = false; + + this.hooks.push(new Text("ÿc2Chat Commands:", this.helpBoxTextX, this.helpBoxTextY, 0, 0, 0)); + this.hooks[this.hooks.length - 1].zorder = 2; + + const addCommands = (commands, prefix = "") => { + const cmdKeys = Object.keys(commands); + + for (let i = 0; i < cmdKeys.length; i++) { + const key = cmdKeys[i]; + + if (typeof commands[key] === "object") { + this.addHook(prefix + key); + + const subPrefix = prefix + key + " "; + Object.keys(commands[key]).forEach(subKey => { + this.addHook(" " + subPrefix + subKey); + }); + } else { + this.addHook(prefix + key); + } + } + }; + + addCommands(this.chatCommands); + + this.hooks.push(new Text("ÿc2Key Commands:", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0)); + this.hooks.push(new Text("ÿc4 Ctrl : ÿc0Move Item", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); + this.hooks.push(new Text("ÿc4 Pause : ÿc1Pause Map", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Delete: ÿc1Quick Exit", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 End : ÿc1Stop Profile", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num 9: ÿc1Stop Action", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false, this.hookHandler)); + this.hooks.push(new Text("ÿc4 Num / : ÿc1Reload", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num + : ÿc0Show Stats", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num * : ÿc0Precast", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + this.hooks.push(new Text("ÿc4 Num . : ÿc0Log Character", this.helpBoxTextX, this.helpBoxTextY + 13 * this.hooks.length, 0, 0, 0, false)); + + this.box.push(new Box(this.helpBoxX, this.helpBoxY, 150, 18, 0x0, 4, 2)); + this.box[this.box.length - 1].zorder = 1; + this.box.push(new Box(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 0x0, 1, 2)); + this.box.push(new Frame(this.helpBoxX, this.helpBoxY, 150, 8 + (this.hooks.length * 13), 2)); + this.box[this.box.length - 2].zorder = 0; + + this.hooks.push(new Text("ÿc1X", this.helpBoxTextX + 125, this.helpBoxTextY, 0, 0, 0, false, this.hookHandler)); + this.hooks[this.hooks.length - 1].zorder = 1; + }, + + hideMenu: function () { + this.cleared = true; + + while (this.hooks.length > 0) { + this.hooks.shift().remove(); + } + + while (this.box.length > 0) { + this.box.shift().remove(); + } + + return; + }, + + sortHooks: function (h1, h2) { + return Math.abs(h1.y - HelpMenu.actionY) - Math.abs(h2.y - HelpMenu.actionY); + } + }; + + Worker.runInBackground.helpAction = function () { + while (HelpMenu.action.length > 0) { + HelpMenu.tick = getTickCount(); + HelpMenu.actionY = HelpMenu.action.shift()[1]; + const actionHooks = HelpMenu.hooks.filter(function (hook) { + return typeof hook.click === "function"; + }).sort(HelpMenu.sortHooks); + + if (actionHooks[0].text === "ÿc1X") { + HelpMenu.hideMenu(); + + return true; + } + + const msgList = actionHooks[0].text.split(" ").filterNull(); + let cmd = msgList[0].split(".")[1]; + + if (!actionHooks[0].text.includes(".")) { + cmd = msgList[1]; + } + + try { + let str = ""; + + if (msgList[0] === "ÿc4." && msgList.length >= 2) { + const parentCmd = msgList[1]; + const subCmd = msgList[2]; + const subWithParam = msgList.slice(2).join(" "); + + if (HelpMenu.chatCommands[parentCmd]) { + if (HelpMenu.chatCommands[parentCmd][subCmd]) { + str = HelpMenu.chatCommands[parentCmd][subCmd]; + } else if (HelpMenu.chatCommands[parentCmd][subWithParam]) { + str = HelpMenu.chatCommands[parentCmd][subWithParam]; + } + } + } else if (typeof HelpMenu.chatCommands[cmd] === "object") { + const subCommands = Object.keys(HelpMenu.chatCommands[cmd]); + str = "Available options: " + subCommands.join(", "); + } else { + str = HelpMenu.chatCommands[cmd]; + } + + !!str && me.overhead(str); + } catch (e) { + console.error(e); + me.overhead(cmd); + } + + delay(150); + } + + return true; + }; + + return HelpMenu; +})); From 468fbd344d36abecb02589b4980e8cf962ccd2a5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Apr 2025 02:27:54 -0400 Subject: [PATCH 511/758] Refactor manualplay hooks, separated out a lot of the logic for clarity - Broke up a lot of the logic into smaller pieces and removed most of the large switch cases in favor of maps --- .../libs/manualplay/hooks/ActionHooks.js | 1665 ++++++++++------- .../libs/manualplay/hooks/ShrineHooks.js | 61 +- .../libs/manualplay/hooks/TextHooks.d.ts | 70 + .../kolbot/libs/manualplay/hooks/TextHooks.js | 712 +++---- .../libs/manualplay/hooks/VectorHooks.js | 901 +++++---- .../libs/manualplay/libs/AttackOverrides.js | 4 +- d2bs/kolbot/libs/manualplay/libs/Hooks.js | 89 + .../libs/manualplay/libs/PatherOverrides.js | 2 +- d2bs/kolbot/libs/manualplay/main.js | 135 +- .../libs/manualplay/modules/HookFactory.js | 130 ++ .../libs/manualplay/threads/MapHelper.js | 12 +- 11 files changed, 2230 insertions(+), 1551 deletions(-) create mode 100644 d2bs/kolbot/libs/manualplay/hooks/TextHooks.d.ts create mode 100644 d2bs/kolbot/libs/manualplay/libs/Hooks.js create mode 100644 d2bs/kolbot/libs/manualplay/modules/HookFactory.js diff --git a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js index 8ce173d23..24c56a786 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js @@ -1,796 +1,1063 @@ -/* eslint-disable max-len */ /** -* @filename ActionHooks.js -* @author theBGuy -* @desc Action hooks for MapThread -* -*/ - -const ActionHooks = { - hooks: [], - portals: [], - frame: [], - action: null, - currArea: 0, - enabled: true, - prevAreas: [ - sdk.areas.None, sdk.areas.None, sdk.areas.RogueEncampment, sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.UndergroundPassageLvl1, sdk.areas.DarkWood, sdk.areas.BlackMarsh, - sdk.areas.BloodMoor, sdk.areas.ColdPlains, sdk.areas.StonyField, sdk.areas.BlackMarsh, sdk.areas.TamoeHighland, sdk.areas.CaveLvl1, sdk.areas.UndergroundPassageLvl1, sdk.areas.HoleLvl1, - sdk.areas.PitLvl1, sdk.areas.ColdPlains, sdk.areas.BurialGrounds, sdk.areas.BurialGrounds, sdk.areas.BlackMarsh, sdk.areas.ForgottenTower, sdk.areas.TowerCellarLvl1, sdk.areas.TowerCellarLvl2, - sdk.areas.TowerCellarLvl3, sdk.areas.TowerCellarLvl4, sdk.areas.TamoeHighland, sdk.areas.MonasteryGate, sdk.areas.OuterCloister, sdk.areas.Barracks, sdk.areas.JailLvl1, sdk.areas.JailLvl2, - sdk.areas.JailLvl3, sdk.areas.InnerCloister, sdk.areas.Cathedral, sdk.areas.CatacombsLvl1, sdk.areas.CatacombsLvl2, sdk.areas.CatacombsLvl3, sdk.areas.StonyField, sdk.areas.RogueEncampment, - sdk.areas.RogueEncampment, sdk.areas.LutGholein, sdk.areas.RockyWaste, sdk.areas.DryHills, sdk.areas.FarOasis, sdk.areas.LostCity, sdk.areas.ArcaneSanctuary, sdk.areas.LutGholein, - sdk.areas.A2SewersLvl1, sdk.areas.A2SewersLvl2, sdk.areas.LutGholein, sdk.areas.HaremLvl1, sdk.areas.HaremLvl2, sdk.areas.PalaceCellarLvl1, sdk.areas.PalaceCellarLvl2, sdk.areas.RockyWaste, - sdk.areas.DryHills, sdk.areas.HallsoftheDeadLvl1, sdk.areas.ValleyofSnakes, sdk.areas.StonyTombLvl1, sdk.areas.HallsoftheDeadLvl2, sdk.areas.ClawViperTempleLvl1, sdk.areas.FarOasis, - sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.LostCity, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, - sdk.areas.CanyonofMagic, sdk.areas.CanyonofMagic, sdk.areas.RogueEncampment, sdk.areas.PalaceCellarLvl3, sdk.areas.RogueEncampment, sdk.areas.KurastDocktown, sdk.areas.SpiderForest, - sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.LowerKurast, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, - sdk.areas.SpiderForest, sdk.areas.SpiderForest, sdk.areas.FlayerJungle, sdk.areas.SwampyPitLvl1, sdk.areas.FlayerJungle, sdk.areas.FlayerDungeonLvl1, sdk.areas.SwampyPitLvl2, sdk.areas.FlayerDungeonLvl2, - sdk.areas.UpperKurast, sdk.areas.A3SewersLvl1, sdk.areas.KurastBazaar, sdk.areas.KurastBazaar, sdk.areas.UpperKurast, sdk.areas.UpperKurast, sdk.areas.KurastCauseway, sdk.areas.KurastCauseway, - sdk.areas.Travincal, sdk.areas.DuranceofHateLvl1, sdk.areas.DuranceofHateLvl2, sdk.areas.DuranceofHateLvl3, sdk.areas.PandemoniumFortress, sdk.areas.OuterSteppes, sdk.areas.PlainsofDespair, - sdk.areas.CityoftheDamned, sdk.areas.RiverofFlame, sdk.areas.PandemoniumFortress, sdk.areas.Harrogath, sdk.areas.BloodyFoothills, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, - sdk.areas.CrystalizedPassage, sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail, sdk.areas.GlacialTrail, sdk.areas.FrozenTundra, sdk.areas.AncientsWay, sdk.areas.AncientsWay, sdk.areas.Harrogath, - sdk.areas.NihlathaksTemple, sdk.areas.HallsofAnguish, sdk.areas.HallsofPain, sdk.areas.FrigidHighlands, sdk.areas.ArreatPlateau, sdk.areas.FrozenTundra, sdk.areas.ArreatSummit, sdk.areas.WorldstoneLvl1, - sdk.areas.WorldstoneLvl2, sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath, sdk.areas.Harrogath - ], - areaInfo: {}, - ctrlObj: { - 0: { - 3: "moveItemFromInvoToTrade", - 5: "moveItemFromTradeToInvo" - }, - 1: { - 3: "moveItemFromInvoToStash", - 7: "moveItemFromStashToInvo" - }, - 2: { - 3: "moveItemFromInvoToCube", - 6: "moveItemFromCubeToInvo" - }, - 3: "sellItem" - }, - blockKeyEvent: () => [ - sdk.uiflags.Inventory, - sdk.uiflags.StatsWindow, - sdk.uiflags.ChatBox, - sdk.uiflags.EscMenu, - sdk.uiflags.Shop, - sdk.uiflags.Quest, - sdk.uiflags.Waypoint, - sdk.uiflags.TradePrompt, - sdk.uiflags.Msgs, - sdk.uiflags.Stash, - sdk.uiflags.Cube, - sdk.uiflags.Help, - sdk.uiflags.MercScreen - ].some((flag) => getUIFlag(flag)), + * @filename ActionHooks.js + * @author theBGuy + * @desc Action hooks for MapThread + * + */ +const ActionHooks = (function () { /** - * Set action based on key input - * @param {number} keycode - * @returns {void} - * @todo this would probably be better as pushing to an action stack and implementing a timeout to prevent spamming the same action + * @typedef {Object} ActionHookEntry + * @property {string} name - The name of the hook + * @property {string} type - The type of the hook (unit, area, portal) + * @property {string} action - The action to perform (openChest, openPortal, etc.) + * @property {Object} dest - The destination coordinates (x, y) or area ID + * @property {Hook} hook */ - event: function (keycode) { - if ([sdk.keys.Shift, sdk.keys.Alt].some(k => k === keycode)) { - return; - } - - ActionHooks.action = keycode; - }, - - getOnScreenLocation: function () { - let possibleLocs = [sdk.uiflags.TradePrompt, sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.Shop]; - - for (let i = 0; i < possibleLocs.length; i++) { - if (getUIFlag(possibleLocs[i])) { - return possibleLocs.indexOf(possibleLocs[i]); - } + + /** @param {ActionHookEntry[]} hooks */ + const clearHooks = function (hooks) { + while (hooks.length) { + hooks.pop().hook.remove(); } + }; - return -1; - }, - - checkAction: function () { - let hook; - let unit, screenLoc; - let obj = { type: false, dest: false, action: false }; - let qolObj = { type: "qol", dest: false, action: false }; - - if (this.action) { - try { - // quick ones first - ends checkAction if one of these was true - if ([sdk.keys.Seven, sdk.keys.Eight, sdk.keys.Nine, sdk.keys.NumpadDash].includes(this.action)) { - if (this.blockKeyEvent()) return; - switch (this.action) { - case sdk.keys.Seven: - if (TextHooks.displaySettings) { - TextHooks.getHook("itemStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 7ÿc0: " + (ItemHooks.enabled ? "Enable" : "Disable") + " Item Filter"; - } - ItemHooks.enabled = !ItemHooks.enabled; - - break; - case sdk.keys.Eight: - if (TextHooks.displaySettings) { - TextHooks.getHook("monsterStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 8ÿc0: " + (MonsterHooks.enabled ? "Enable" : "Disable") + " Monsters"; - } - MonsterHooks.enabled = !MonsterHooks.enabled; - - break; - case sdk.keys.Nine: - if (TextHooks.displaySettings) { - TextHooks.getHook("vectorStatus", TextHooks.statusHooks).hook.text = "ÿc4Key 9ÿc0: " + (VectorHooks.enabled ? "Enable" : "Disable") + " Vectors"; - } - VectorHooks.enabled = !VectorHooks.enabled; - - break; - case sdk.keys.NumpadDash: - if (ItemHooks.pickitEnabled) { - ItemHooks.pickitEnabled = false; - } else { - ItemHooks.pickitEnabled = true; - ItemHooks.flush(); - - if (!Hooks.saidMessage) { - showConsole(); - print("ÿc - hook = TextHooks.getHook("Next Act", TextHooks.qolHooks); - - break; - case sdk.keys.Ctrl: - unit = Game.getSelectedUnit(); - - if (!!unit) { - screenLoc = this.getOnScreenLocation(); - - switch (screenLoc) { - case 0: // Trade screen - case 1: // Stash - case 2: // Cube - qolObj.action = this.ctrlObj[screenLoc][unit.location]; - - break; - case 3: // Shop - // qolObj.action = "sellItem"; - - break; - default: - break; - } - } - - break; - case sdk.keys.Five: - if (!me.inTown) { - me.getTpTool() && (qolObj.action = "makePortal"); - } else if (me.inTown) { - if (!getUIFlag(sdk.uiflags.Stash) && !getUIFlag(sdk.uiflags.TradePrompt) && !getUIFlag(sdk.uiflags.Inventory)) { - qolObj.action = "heal"; - } - } - - break; - case sdk.keys.Six: - if (!me.inTown) { - me.getTpTool() && (qolObj.action = "takePortal"); - } else if (me.inTown) { - if (!getUIFlag(sdk.uiflags.Stash) && !getUIFlag(sdk.uiflags.TradePrompt) && !getUIFlag(sdk.uiflags.Inventory)) { - qolObj.action = "openStash"; - } - } - - break; - case sdk.keys.Insert: - if (me.inTown) { - break; - } - - qolObj.action = "clear"; - - break; - } - } - - if (hook) { - Object.assign(obj, hook); - Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(obj)); - } else if (qolObj.action) { - Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(qolObj)); + /** + * @param {string} name - Hook name + * @param {string} type - Hook type (area, unit, portal, wp) + * @param {number|PathNode} dest - Destination area ID or coordinates + * @param {string} [text] - Custom text (defaults to standard by hook type) + * @param {{ do: string, id: number }} [action] - Optional action object + * @returns {ActionHookEntry} + */ + const createActionHook = function (name, type, dest, text, action = false) { + if (!text) { + text = (function () { + switch (name) { + case "Next Area": + return "Num 0: " + getAreaName(dest).replace("Level", "Lvl"); + case "Previous Area": + return "ÿc1Num 1: " + getAreaName(dest).replace("Level", "Lvl"); + case "Waypoint": + return "ÿc9Num 2: WP"; + case "POI": + return "ÿc { - switch (name) { - case "Next Area": - return "Num 0: "; - case "Previous Area": - return "ÿc1Num 1: "; - case "Side Area": - return "ÿc3Num 4: "; - case "POI2": - return "ÿc} */ + const hookObj = { name: name, type: type, - dest: dest, - hook: new Text(hookTxt + getAreaName(dest), Hooks.dashBoard.x + 5, this.yHookLoc()) - }); + dest: dest + }; + + if (action) { + hookObj.action = action; + } + + hookObj.hook = new Text(text, Hooks.dashBoard.x + 5, ActionHooks.yHookLoc()); + return hookObj; - }, + }; /** - * Creates new action hook based on our current area - * @param {number} area + * @type {Map} */ - add: function (area) { - let i, exits, wp, poi, nextCheck, infSeal, seisSeal, vizSeal, bossX; - let nextAreas = []; - - // Specific area override - nextAreas[sdk.areas.TamoeHighland] = sdk.areas.MonasteryGate; - nextAreas[sdk.areas.SpiderForest] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.GreatMarsh] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.CrystalizedPassage] = sdk.areas.GlacialTrail; - nextAreas[sdk.areas.GlacialTrail] = sdk.areas.FrozenTundra; - nextAreas[sdk.areas.AncientsWay] = sdk.areas.ArreatSummit; - me.inArea(sdk.areas.CanyonofMagic) && (nextAreas[sdk.areas.CanyonofMagic] = getRoom().correcttomb); - - switch (me.area) { - case sdk.areas.Tristram: - this.hooks.push({ + const specialAreaConfigs = new Map([ + [sdk.areas.BloodMoor, [ + { name: "Side Area", type: "area", dest: sdk.areas.DenofEvil }, + ]], + [sdk.areas.ColdPlains, [ + { name: "Side Area", type: "area", dest: sdk.areas.BurialGrounds }, + ]], + [sdk.areas.BurialGrounds, [ + { name: "Side Area", type: "area", dest: sdk.areas.Mausoleum }, + ]], + [sdk.areas.Tristram, [ + { name: "POI2", type: "unit", action: { do: "openChest", id: sdk.quest.chest.Wirt }, dest: { x: 25048, y: 5177 }, - hook: new Text("ÿc} + */ + const areaConfigs = new Map([ + [sdk.areas.Tristram, [ + { name: "Previous Area", type: "portal", dest: sdk.areas.StonyField }, + ]], + + [sdk.areas.MooMooFarm, [ + { name: "Previous Area", type: "portal", dest: sdk.areas.RogueEncampment }, + ]], + + [sdk.areas.PalaceCellarLvl3, [ + { name: "Previous Area", type: "area", dest: sdk.areas.PalaceCellarLvl2 }, + { name: "Next Area", type: "portal", dest: sdk.areas.ArcaneSanctuary }, + ]], + + [sdk.areas.ArcaneSanctuary, [ + { name: "Previous Area", type: "area", dest: sdk.areas.PalaceCellarLvl3 }, + { name: "Next Area", type: "area", dest: sdk.areas.CanyonofMagic }, + ]], + + [sdk.areas.CanyonofMagic, [ + { name: "Previous Area", type: "portal", dest: sdk.areas.ArcaneSanctuary }, + ]], + + [sdk.areas.DuranceofHateLvl3, [ + { name: "Previous Area", type: "area", dest: sdk.areas.DuranceofHateLvl2 }, + { name: "Next Area", type: "area", dest: sdk.areas.PandemoniumFortress }, + ]], + + [sdk.areas.NihlathaksTemple, [ { - let correctTomb = getRoom().correcttomb; - let currExits = getArea().exits - .filter((ex) => ex.target !== correctTomb) - .sort(function(a, b) { - return a.target - b.target; - }).reverse(); - - let curr; - for (let i = 8; i > 4; i--) { - curr = currExits.shift(); - this.hooks.push({ - name: "POI" + (i - 3), - type: "area", - dest: curr.target, - hook: new Text("ÿc ex.target !== correctTomb) + .sort(function (a, b) { + return a.target - b.target; + }) + .reverse(); + + let curr; + + for (let i = 8; i > 4; i--) { + curr = currExits.shift(); + ActionHooks.hooks.push(createActionHook( + "POI" + (i - 3), + "area", + curr.target + )); + } - curr = currExits.shift(); - this.hooks.push({ - name: "POI", - type: "area", - dest: curr.target, - hook: new Text("ÿc uberPortalIds.includes(portal.objtype))) { + return; + } + + TextHooks.displaySettings = false; + ActionHooks.frame.push({ + name: "portalbox", + hook: new Box(Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0x0, 1, 0), + }); + + ActionHooks.frame.push({ + name: "portalframe", + hook: new Frame(Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0), + }); + + const portalConfig = [ + { area: sdk.areas.MatronsDen, name: "Matron's Den", key: 5, yOffset: 0 }, + { area: sdk.areas.ForgottenSands, name: "Forgotten Sands", key: 6, yOffset: 15 }, + { area: sdk.areas.FurnaceofPain, name: "Furnace of Pain", key: 7, yOffset: 30 }, + { area: sdk.areas.UberTristram, name: "Uber Tristam", key: 8, yOffset: 45 } + ]; + + portalConfig.forEach(function (config) { + if (Pather.getPortal(config.area)) { + ActionHooks.portals.push({ + name: config.name, + type: "portal", + dest: config.area, + hook: new Text( + "ÿc1Num " + config.key + ": " + config.name, + Hooks.portalBoard.x, + Hooks.portalBoard.y + Hooks.resfix.y + config.yOffset + ), }); } + }); + }; - break; - case sdk.areas.SpiderForest: - this.hooks.push(this.newHook("POI3", "area", sdk.areas.GreatMarsh)); - this.hooks.push(this.newHook("POI2", "area", sdk.areas.SpiderCave)); - - break; - case sdk.areas.FlayerJungle: - this.hooks.push(this.newHook("POI2", "area", sdk.areas.SwampyPitLvl1)); - - break; - case sdk.areas.KurastBazaar: - this.hooks.push(this.newHook("POI2", "area", sdk.areas.DisusedFane)); - - break; - case sdk.areas.UpperKurast: - this.hooks.push(this.newHook("POI3", "area", sdk.areas.ForgottenTemple)); - this.hooks.push(this.newHook("POI2", "area", sdk.areas.ForgottenReliquary)); + const addChaosSeals = function () { + if (!me.inArea(sdk.areas.ChaosSanctuary)) { + return; + } + const sealIds = [sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealSeis, sdk.objects.DiabloSealVizier]; + const seals = Game.getPresetObjects(sdk.areas.ChaosSanctuary).filter(function (seal) { + return sealIds.includes(seal.id); + }).map(function (seal) { + return seal.realCoords(); + }); - break; - case sdk.areas.KurastCauseway: - this.hooks.push(this.newHook("POI3", "area", sdk.areas.RuinedFane)); - this.hooks.push(this.newHook("POI2", "area", sdk.areas.DisusedReliquary)); + const infSeal = seals.find(function () { + return this.id === sdk.objects.DiabloSealInfector; + }); + const seisSeal = seals.find(function () { + return this.id === sdk.objects.DiabloSealSeis; + }); + const vizSeal = seals.find(function () { + return this.id === sdk.objects.DiabloSealVizier; + }); + + if (infSeal) { + ActionHooks.hooks.push(createActionHook( + "POI4", + "unit", + { x: infSeal.x, y: infSeal.y }, + "ÿc k === keycode)) { + return; + } - break; - case sdk.areas.FarOasis: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.MaggotLairLvl1)); + ActionHooks.action = keycode; + }, - break; - case sdk.areas.LostCity: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.AncientTunnels)); + checkAction: function () { + if (!ActionHooks.action) { + return; + } + + const obj = { type: false, dest: false, action: false }; - break; - case sdk.areas.SpiderForest: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.SpiderCavern)); + try { + // quick ones first - ends checkAction if one of these was true + if ([sdk.keys.Seven, sdk.keys.Eight, sdk.keys.Nine, sdk.keys.NumpadDash].includes(this.action)) { + processToggleKeys(); - break; - case sdk.areas.FlayerJungle: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.FlayerDungeonLvl1)); + return; + } - break; - case sdk.areas.KurastBazaar: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.RuinedTemple)); + const qolObj = processActionKey(); + if (qolObj && qolObj.action) { + Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(qolObj)); - break; - case sdk.areas.UpperKurast: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.A3SewersLvl1)); + return; + } + + const ctrlObj = processCtrlKey(); + if (ctrlObj && ctrlObj.action) { + Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(ctrlObj)); - break; - case sdk.areas.KurastCauseway: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.DisusedReliquary)); + return; + } - break; - case sdk.areas.A3SewersLvl1: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.KurastBazaar)); + let hook = processPOIKey(); + if (hook) { + Object.assign(obj, hook); + Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(obj)); - break; - case sdk.areas.CrystalizedPassage: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.FrozenRiver)); + return; + } + + hook = ((action) => { + switch (action) { + case sdk.keys.Numpad0: + return ActionHooks.getHook("Next Area"); + case sdk.keys.Numpad1: + return ActionHooks.getHook("Previous Area"); + case sdk.keys.Numpad2: + return ActionHooks.getHook("Waypoint"); + case sdk.keys.Numpad3: + return ActionHooks.getHook("POI"); + case sdk.keys.Numpad4: + return ActionHooks.getHook("Side Area"); + case 188: // shift < + return TextHooks.getHook("Previous Act", TextHooks.qolHooks); + case 190: // shift > + return TextHooks.getHook("Next Act", TextHooks.qolHooks); + default: + return null; + } + })(this.action); - break; - case sdk.areas.GlacialTrail: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.DrifterCavern)); + if (hook) { + Object.assign(obj, hook); + Messaging.sendToScript(MapMode.mapHelperFilePath, JSON.stringify(obj)); + } + } catch (e) { + console.error(e); + } finally { + ActionHooks.action = null; + } + }, - break; - case sdk.areas.AncientsWay: - this.hooks.push(this.newHook("Side Area", "area", sdk.areas.IcyCellar)); + check: function () { + if (!this.enabled) return; - break; - } + ActionHooks.checkAction(); - poi = VectorHooks.getPOI(); + if (me.area !== this.currArea) { + this.flush(); - if (poi) { - this.hooks.push({ - name: "POI", - type: "unit", - action: poi.action || false, - dest: { x: poi.x, y: poi.y }, - hook: new Text("ÿc [sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain, sdk.areas.UberTristram].includes(portal.objtype))) { - TextHooks.displaySettings = false; - this.frame.push({ - name: "portalbox", - hook: new Box (Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0x0, 1, 0) - }); - - this.frame.push({ - name: "portalframe", - hook: new Frame(Hooks.portalBoard.x - 8, Hooks.portalBoard.y + Hooks.resfix.y - 17, 190, 70, 0) - }); - - Pather.getPortal(sdk.areas.MatronsDen) && this.portals.push({ - name: "Matron's Den", - type: "portal", - dest: sdk.areas.MatronsDen, - hook: new Text("ÿc1Num 5: Matron's Den", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y) - }); - - Pather.getPortal(sdk.areas.ForgottenSands) && this.portals.push({ - name: "Sands", - type: "portal", - dest: sdk.areas.ForgottenSands, - hook: new Text("ÿc1Num 6: Forgotten Sands", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 15) - }); - - Pather.getPortal(sdk.areas.FurnaceofPain) && this.portals.push({ - name: "Furnace", - type: "portal", - dest: sdk.areas.FurnaceofPain, - hook: new Text("ÿc1Num 7: Furnace of Pain", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 30) - }); - - Pather.getPortal(sdk.areas.UberTristram) && this.portals.push({ - name: "Uber Tristam", - type: "portal", - dest: sdk.areas.UberTristram, - hook: new Text("ÿc1Num 8: Uber Tristam", Hooks.portalBoard.x, Hooks.portalBoard.y + Hooks.resfix.y + 45) - }); - } + /** + * Creates new action hook based on our current area + * @param {number} area + */ + add: function (area) { + let bossX; - let entrance = { x: 0, y: 0 }; + // Specific area override + if (me.inArea(sdk.areas.CanyonofMagic) && !nextAreas.has(sdk.areas.CanyonofMagic)) { + nextAreas.set(sdk.areas.CanyonofMagic, getRoom().correcttomb); + } - switch (me.area) { - case sdk.areas.Tristram: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.StonyField)); + if (specialAreaConfigs.has(area)) { + for (let config of specialAreaConfigs.get(area)) { + this.hooks.push(createActionHook( + config.name, + config.type, + config.dest, + Object.hasOwn(config, "text") ? config.text : "", + Object.hasOwn(config, "action") ? config.action : false + )); + } + } - break; - case sdk.areas.MooMooFarm: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.RogueEncampment)); + addTombs(); + addChaosSeals(); + addCowPortal(); - break; - case sdk.areas.CanyonofMagic: - this.hooks.push(this.newHook("Previous Area", "portal", sdk.areas.ArcaneSanctuary)); + const poi = VectorHooks.getPOI(); - break; - case sdk.areas.ArcaneSanctuary: - this.hooks.push(this.newHook("Previous Area", "area", sdk.areas.PalaceCellarLvl3)); - this.hooks.push(this.newHook("Next Area", "area", sdk.areas.CanyonofMagic)); + if (poi) { + this.hooks.push(createActionHook( + "POI", + "unit", + { x: poi.x, y: poi.y }, + "ÿc 0 ? me.gameserverip.split(".")[3] : "0"), - modifier: - 16 +const TextHooks = (function () { + const Events = new (require("../../modules/Events")); + const HookFactory = require("../modules/HookFactory"); + + const Y_POS_MODIFIER = 16 * (Number(!!me.diff) + Number(!!me.gamepassword) + Number(!!me.gametype) + Number(!!me.gamename) - + Number(!!me.gameserverip && !me.realm)), - - getScale: function (hkLen) { - if (!!TextHooks.yLocMapScale[hkLen]) { - TextHooks.frameYSizeScale = -1 * this.yLocMapScale[hkLen]; - TextHooks.frameYLocScale = this.yLocMapScale[hkLen]; - } else { - TextHooks.frameYSizeScale = 0; - TextHooks.frameYLocScale = 0; - } - - TextHooks.settingsModifer = Math.max(0, hkLen - 3); - }, - - check: function () { - if (!this.enabled) { - this.flush(); - - return; - } - - if (!this.frameworkDisplayed) { - !this.getHook("credits", this.hooks) && this.add("credits"); - !!me.gameserverip && !this.getHook("ip", this.hooks) && this.add("ip"); - TextHooks.lastAct = 0; // sorta hacky solution, but works this will cause qolBoard to update after being flushed from a uiflag - TextHooks.frameworkDisplayed = true; - } - - this.displaySettings - ? !this.getHook("showSettings", this.statusHooks) && this.add("showSettings") - : !this.getHook("hideSettings", this.statusHooks) && this.add("hideSettings"); - this.displayTitle && !this.getHook("title", this.hooks) && this.add("title"); - !this.getHook("ping", this.hooks) - ? this.add("ping") - : (this.getHook("ping", this.hooks).hook.text = "Ping: " + me.ping); - !this.getHook("time", this.hooks) ? this.add("time") : (this.getHook("time", this.hooks).hook.text = this.timer()); - }, - - update: function (hkLen = 0) { - let updateSettingsDisplay = this.displaySettings && this.settingsModifer < Math.max(0, hkLen - 3); - - this.getScale(hkLen); - this.add("dashboard"); - updateSettingsDisplay && this.add("showSettings"); - - if ((this.lastAct !== me.act || this.wasInTown !== me.inTown || !this.getHook("qolBoard", this.qolHooks))) { - this.add("qolBoard"); - } - }, - - hookHandler: function (click, x, y) { - function sortHooks(h1, h2) { + + Number(!!me.gameserverip && !me.realm)); + const IP = (me.gameserverip.length > 0 ? me.gameserverip.split(".")[3] : ""); + const Y_LOC_MAP_SCALE = { 1: 40, 2: 30, 3: 20, 4: 10, 6: -10, 9: -40 }; + + /** @typedef {import("./TextHooks").HookEntry} HookEntry */ + /** @typedef {import("../libs/Hooks")} */ + + /** + * @param {number} click + * @param {number} x + * @param {number} y + * @returns {boolean} + */ + const onClick = function (click, x, y) { + /** + * @param {HookEntry} h1 + * @param {HookEntry} h2 + */ + const sortHooks = function (h1, h2) { return Math.abs(h1.hook.y - y) - Math.abs(h2.hook.y - y); - } + }; if (click === 0) { - TextHooks.statusHooks.sort(sortHooks); + const actionHooks = TextHooks.statusHooks.filter(function ({ hook }) { + return typeof hook.click === "function"; + }).sort(sortHooks); - if (TextHooks.statusHooks[0].name === "hideSettings" || TextHooks.statusHooks[0].name === "showSettings") { + if (!actionHooks.length) { + console.log("No action hooks found.", actionHooks); + return false; + } + + if (actionHooks[0].name === "hideSettings" || actionHooks[0].name === "showSettings") { TextHooks.displaySettings = !TextHooks.displaySettings; return true; @@ -100,320 +54,378 @@ const TextHooks = { } return false; - }, + }; + + const getScale = function (hkLen) { + if (!!Y_LOC_MAP_SCALE[hkLen]) { + TextHooks.frameYSizeScale = -1 * Y_LOC_MAP_SCALE[hkLen]; + TextHooks.frameYLocScale = Y_LOC_MAP_SCALE[hkLen]; + } else { + TextHooks.frameYSizeScale = 0; + TextHooks.frameYLocScale = 0; + } + + TextHooks.settingsModifer = Math.max(0, hkLen - 3); + }; - add: function (name, hookArr = []) { - let orginalLen = hookArr.length; + /** @param {HookEntry[]} hooks */ + const clearHooks = (hooks) => { + while (hooks.length) { + hooks.pop().hook.remove(); + } + }; + + const timer = function () { + return " (" + new Date(getTickCount() - me.gamestarttime).toISOString().slice(11, -5) + ")"; + }; - switch (name) { - case "credits": - this.hooks.push({ + const textHooks = { + credits: function () { + return HookFactory.createHooks.text({ name: "credits", - hook: new Text("MM by theBGuy", 0, 600 + Hooks.resfix.y, 4, 0, 0), + text: "MM by theBGuy", + x: 0, + y: 600 + Hooks.resfix.y, }); - - break; - case "title": - this.hooks.push({ + }, + title: function () { + return HookFactory.createHooks.text({ name: "title", - hook: new Text(":: Running Map-Mode, enter .help in chat to see more commands ::", 0, 13, 4, 0, 0), + text: ":: Running Map-Mode, enter .help in chat to see more commands ::", + x: 0, + y: 13, }); - - break; - case "ping": - this.hooks.push({ + }, + ping: function () { + return HookFactory.createHooks.text({ name: "ping", - hook: new Text("Ping: " + me.ping, 785 + Hooks.resfix.x, 56 + this.modifier, 4, 1, 1), + text: "Ping: " + me.ping, + x: 785 + Hooks.resfix.x, + y: 56 + Y_POS_MODIFIER, + color: 4, + font: 1, + align: 1, }); - - break; - case "time": - this.hooks.push({ + }, + time: function () { + return HookFactory.createHooks.text({ name: "time", - hook: new Text(this.timer(), 785 + Hooks.resfix.x, 72 + this.modifier, 4, 1, 1), + text: timer(), + x: 785 + Hooks.resfix.x, + y: 72 + Y_POS_MODIFIER, + color: 4, + font: 1, + align: 1, }); - - break; - case "ip": - this.hooks.push({ + }, + ip: function () { + const hook = HookFactory.createHooks.text({ name: "ip", - hook: new Text( - "IP: " + TextHooks.ip, - 785 + Hooks.resfix.x, - 88 + this.modifier, - 4, - 1, - 1 - ), - }); - this.hooks[this.hooks.length - 1].hook.zorder = 0; - - break; - case "dashboard": - while (this.dashBoard.length) { - this.dashBoard.shift().hook.remove(); - } - - this.dashBoard.push({ - name: "dashboard", - hook: new Box( - Hooks.dashBoard.x, - Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, - 225, - 60 + this.frameYSizeScale, - 0x0, - 1, - 0 - ), - }); - - this.dashBoard.push({ - name: "dashboardframe", - hook: new Frame( - Hooks.dashBoard.x, - Hooks.dashBoard.y + Hooks.resfix.y + this.frameYLocScale, - 225, - 60 + this.frameYSizeScale, - 0 - ), + text: "IP: " + IP, + x: 785 + Hooks.resfix.x, + y: 88 + Y_POS_MODIFIER, + color: 4, + font: 1, + align: 1, }); - - this.getHook("dashboard", this.dashBoard).hook.zorder = 0; - - break; - case "key5": - this.qolHooks.push({ + hook.hook.zorder = 0; + return hook; + }, + key5: function () { + return HookFactory.createHooks.text({ name: "key5", - hook: new Text( - "Key 5: " + (me.inTown ? "Heal" : "Make Portal"), - Hooks.qolBoard.x + 5 + Hooks.resfix.x, - 545 - this.qolHooks.length * 10 + Hooks.resfix.y, - 4 - ), + text: "Key 5: " + (me.inTown ? "Heal" : "Make Portal"), + x: Hooks.qolBoard.x + 5 + Hooks.resfix.x, + y: 545 - TextHooks.qolHooks.length * 10 + Hooks.resfix.y, }); - - break; - case "key6": - this.qolHooks.push({ + }, + key6: function () { + return HookFactory.createHooks.text({ name: "key6", - hook: new Text( - "Key 6: " + (me.inTown ? "Open Stash" : "Go To Town"), - Hooks.qolBoard.x + 5 + Hooks.resfix.x, - 545 - this.qolHooks.length * 10 + Hooks.resfix.y, - 4 - ), + text: "Key 6: " + (me.inTown ? "Open Stash" : "Go To Town"), + x: Hooks.qolBoard.x + 5 + Hooks.resfix.x, + y: 545 - TextHooks.qolHooks.length * 10 + Hooks.resfix.y, }); - - break; - case "nextAct": - me.inTown - && me.accessToAct(me.act + 1) - && this.qolHooks.push({ - name: "Next Act", - dest: me.act + 1, - type: "actChange", - hook: new Text( - "Shift > : Next Act", - Hooks.qolBoard.x + 5 + Hooks.resfix.x, - 545 - this.qolHooks.length * 10 + Hooks.resfix.y, - 4 - ), - }); - - break; - case "previousAct": - me.inTown - && me.act > 1 - && this.qolHooks.push({ - name: "Previous Act", - dest: me.act - 1, - type: "actChange", - hook: new Text( - "Shift < : Previous Act", - Hooks.qolBoard.x + 5 + Hooks.resfix.x, - 545 - this.qolHooks.length * 10 + Hooks.resfix.y, - 4 - ), - }); - - break; - case "qolBoard": - TextHooks.qolFrameYSize = 50; - TextHooks.lastAct = me.act; - TextHooks.wasInTown = me.inTown; - - while (this.qolHooks.length) { - this.qolHooks.shift().hook.remove(); + }, + nextAct: function () { + if (me.inTown && me.accessToAct(me.act + 1)) { + return Object.assign({ + dest: me.act + 1, + type: "actChange", + }, HookFactory.createHooks.text({ + name: "Next Act", + text: "Shift > : Next Act", + x: Hooks.qolBoard.x + 5 + Hooks.resfix.x, + y: 545 - TextHooks.qolHooks.length * 10 + Hooks.resfix.y, + })); } - - this.qols.forEach(function (hook) { - TextHooks.add(hook, TextHooks.qolHooks) && (TextHooks.qolFrameYSize -= 10); - }); - - this.qolHooks.push({ - name: "qolBoard", - hook: new Box( - Hooks.qolBoard.x + Hooks.resfix.x, - Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, - 140, - 60 + -1 * this.qolFrameYSize, - 0x0, - 1, - 0 - ), - }); - - this.qolHooks.push({ - name: "qolFrame", - hook: new Frame( - Hooks.qolBoard.x + Hooks.resfix.x, - Hooks.qolBoard.y + this.qolFrameYSize + Hooks.resfix.y, - 140, - 60 + -1 * this.qolFrameYSize, - 0 - ), - }); - - this.qolHooks[this.qolHooks.length - 2].hook.zorder = 0; - - break; - case "pickitStatus": - this.statusHooks.push({ + return null; + }, + previousAct: function () { + if (me.inTown && me.act > 1) { + return Object.assign({ + dest: me.act - 1, + type: "actChange", + }, HookFactory.createHooks.text({ + name: "Previous Act", + text: "Shift < : Previous Act", + x: Hooks.qolBoard.x + 5 + Hooks.resfix.x, + y: 545 - TextHooks.qolHooks.length * 10 + Hooks.resfix.y, + })); + } + return null; + }, + pickitStatus: function () { + return HookFactory.createHooks.text({ name: "pickitStatus", - hook: new Text( - "ÿc4N-Pad - ÿc0: " + (ItemHooks.pickitEnabled ? "ÿc orginalLen; - }, + Events.on("areachange", function () { + console.log("Area change detected, clearing hooks."); + + clearHooks(TextHooks.dashBoard); + clearHooks(TextHooks.qolHooks); + + getScale(ActionHooks.hooks.length); + TextHooks.add("dashboard", TextHooks.dashBoard); + TextHooks.add("qolBoard", TextHooks.qolHooks); - getHook: function (name, hookArr = []) { - for (let i = 0; i < hookArr.length; i++) { - if (hookArr[i].name === name) { - return hookArr[i]; - } + if (TextHooks.displaySettings) { + clearHooks(TextHooks.statusHooks); + TextHooks.add("showSettings", TextHooks.statusHooks); } + }); + + return { + events: Events, + enabled: true, + displayTitle: true, + displaySettings: true, + frameworkDisplayed: false, + frameYSizeScale: 0, + frameYLocScale: 0, + settingsModifer: 0, + dashBoardWidthScale: 0, + statusFrameYSize: 0, + qolFrameYSize: 0, + /** @type {HookEntry[]} */ + statusHooks: [], + /** @type {HookEntry[]} */ + dashBoard: [], + /** @type {HookEntry[]} */ + qolHooks: [], + /** @type {HookEntry[]} */ + hooks: [], + + check: function () { + if (!TextHooks.enabled) { + TextHooks.flush(); + + return; + } - return false; - }, - - timer: function () { - return " (" + new Date(getTickCount() - me.gamestarttime).toISOString().slice(11, -5) + ")"; - }, + if (!TextHooks.frameworkDisplayed) { + TextHooks.add("credits", TextHooks.hooks); + !!IP && TextHooks.add("ip", TextHooks.hooks); + TextHooks.frameworkDisplayed = true; + } - flush: function () { - if (!Hooks.enabled) { - while (this.hooks.length) { - this.hooks.shift().hook.remove(); + TextHooks.displaySettings + ? TextHooks.add("showSettings", TextHooks.statusHooks) + : TextHooks.add("hideSettings", TextHooks.statusHooks); + if (TextHooks.displayTitle) { + TextHooks.add("title", TextHooks.hooks); + } + TextHooks.updateHook("ping", TextHooks.hooks, "Ping: " + me.ping); + TextHooks.updateHook("time", TextHooks.hooks, timer()); + }, + + /** + * @param {string} name + * @param {HookEntry[]} hookArr + * @param {string} text + */ + updateHook: function (name = "", hookArr = [], text = "") { + const entry = TextHooks.getHook(name, hookArr); + + if (entry && text) { + entry.hook.text = text; + } else { + TextHooks.add(name, hookArr); + } + }, + + /** + * @param {string} name + * @param {HookEntry[]} hookArr + * @returns {boolean} + */ + add: function (name, hookArr = []) { + const orginalLen = hookArr.length; + + if (!name || !hookArr || TextHooks.getHook(name, hookArr)) { + return false; + } + + if (textHooks[name]) { + let hook = textHooks[name](); + if (hook) { + hookArr.push(hook); + } + } else if (specialCases[name]) { + specialCases[name](); } - TextHooks.frameworkDisplayed = false; - } + return hookArr.length > orginalLen; + }, + + /** + * @param {string} name + * @param {HookEntry[]} hookArr + * @returns {HookEntry|boolean} + */ + getHook: function (name, hookArr = []) { + for (let i = 0; i < hookArr.length; i++) { + if (hookArr[i].name === name) { + return hookArr[i]; + } + } - while (this.statusHooks.length) { - this.statusHooks.shift().hook.remove(); - } + return false; + }, - while (this.dashBoard.length) { - this.dashBoard.shift().hook.remove(); - } + flush: function () { + if (!Hooks.enabled) { + clearHooks(this.hooks); + TextHooks.frameworkDisplayed = false; + } - while (this.qolHooks.length) { - this.qolHooks.shift().hook.remove(); - } - }, -}; + clearHooks(this.statusHooks); + clearHooks(this.dashBoard); + clearHooks(this.qolHooks); + }, + }; +})(); diff --git a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js index e3ce724ad..9ea3a2cae 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/VectorHooks.js @@ -5,393 +5,564 @@ * */ -const VectorHooks = { - enabled: true, - currArea: 0, - lastLoc: { x: 0, y: 0 }, - names: [], - hooks: [], - nextAreas: (function () { - let nextAreas = []; - - // Specific area override - nextAreas[sdk.areas.TamoeHighland] = sdk.areas.MonasteryGate; - nextAreas[sdk.areas.SpiderForest] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.GreatMarsh] = sdk.areas.FlayerJungle; - nextAreas[sdk.areas.CrystalizedPassage] = sdk.areas.GlacialTrail; - nextAreas[sdk.areas.GlacialTrail] = sdk.areas.FrozenTundra; - nextAreas[sdk.areas.AncientsWay] = sdk.areas.ArreatSummit; - nextAreas[sdk.areas.ThroneofDestruction] = sdk.areas.WorldstoneChamber; - - return nextAreas; - })(), - - check: function () { - if (!this.enabled) { - this.flush(); - - return; +const VectorHooks = (function () { + const nextAreas = new Map([ + [sdk.areas.TamoeHighland, sdk.areas.MonasteryGate], + [sdk.areas.SpiderForest, sdk.areas.FlayerJungle], + [sdk.areas.GreatMarsh, sdk.areas.FlayerJungle], + [sdk.areas.CrystalizedPassage, sdk.areas.GlacialTrail], + [sdk.areas.GlacialTrail, sdk.areas.FrozenTundra], + [sdk.areas.AncientsWay, sdk.areas.ArreatSummit], + [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber], + ]); + + /** + * Areas that contain super chests + */ + const superChestAreas = new Set([ + sdk.areas.CaveLvl2, + sdk.areas.HoleLvl2, + sdk.areas.PitLvl2, + sdk.areas.UndergroundPassageLvl2, + sdk.areas.Crypt, + sdk.areas.Mausoleum, + sdk.areas.StonyTombLvl2, + sdk.areas.AncientTunnels, + sdk.areas.GreatMarsh, + sdk.areas.SpiderCave, + sdk.areas.SwampyPitLvl3, + sdk.areas.DisusedFane, + sdk.areas.ForgottenReliquary, + sdk.areas.ForgottenTemple, + sdk.areas.DisusedReliquary, + sdk.areas.DrifterCavern, + sdk.areas.IcyCellar, + sdk.areas.Abaddon, + sdk.areas.PitofAcheron, + sdk.areas.InfernalPit + ]); + + /** + * Areas that contain large sparkly chests + */ + const largeSparklyChestAreas = new Set([ + sdk.areas.GlacialTrail, + sdk.areas.HallsofAnguish, + sdk.areas.HallsofPain + ]); + + const hellEntranceAreas = new Set([ + sdk.areas.FrigidHighlands, + sdk.areas.ArreatPlateau, + sdk.areas.FrozenTundra + ]); + + /** + * Helper function to create a POI from a preset object + * @param {number} objectId - The ID of the preset object to find + * @param {string} name - Name to display + * @param {Object} [action] - Optional action + * @return {Object|false} POI information or false if not found + */ + function createPOIFromPreset(objectId, name, action = null) { + const unit = Game.getPresetObject(me.area, objectId); + if (!unit) return false; + + const coords = unit.realCoords(); + const poi = { + name: name, + x: coords.x, + y: coords.y + }; + + if (action) { + poi.action = action; } - - if (me.area !== this.currArea) { - this.flush(); - - if (!me.area || !me.gameReady) return; - - try { - let exits = getArea().exits; - VectorHooks.currArea = me.area; - - if (exits) { - for (let i = 0; i < exits.length; i++) { - if (me.inArea(sdk.areas.CanyonofMagic)) { - this.add(exits[i].x, exits[i].y, exits[i].target === getRoom().correcttomb ? 0x69 : 0x99); - } else if (exits[i].target === this.nextAreas[me.area] && this.nextAreas[me.area]) { - this.add(exits[i].x, exits[i].y, 0x1F); - } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area) && this.nextAreas[me.area]) { - this.add(exits[i].x, exits[i].y, 0x99); - } else if (exits[i].target === ActionHooks.prevAreas.indexOf(me.area)) { - this.add(exits[i].x, exits[i].y, 0x1F); - } else if (exits[i].target === ActionHooks.prevAreas[me.area]) { - this.add(exits[i].x, exits[i].y, 0x0A); - } else { - this.add(exits[i].x, exits[i].y, 0x99); - } - - this.addNames(exits[i]); - } - } - - let wp = this.getWP(); - wp && this.add(wp.x, wp.y, 0xA8); - let poi = this.getPOI(); - poi && this.add(poi.x, poi.y, 0x7D); - } catch (e) { - console.error(e); - } - } else if (me.x !== this.lastLoc.x || me.y !== this.lastLoc.y) { - this.update(); + + return poi; + } + + /** + * Helper to create POI from preset monster + * @param {number} monsterId - The ID of the preset monster to find + * @param {string} name - Name to display + * @param {Object} [action] - Optional action + * @return {Object|false} POI information or false if not found + */ + function createPOIFromMonster(monsterId, name, action = null) { + const unit = Game.getPresetMonster(me.area, monsterId); + if (!unit) return false; + + const poi = { + name: name, + x: unit.x, + y: unit.y + }; + + if (action) { + poi.action = action; } - }, - - add: function (x, y, color) { - this.hooks.push(new Line(me.x, me.y, x, y, color, true)); - }, - - addNames: function (area) { - this.names.push(new Text(getAreaName(area.target), area.x, area.y, 0, 6, 2, true)); - }, - - update: function () { - VectorHooks.lastLoc = { x: me.x, y: me.y }; - - for (let i = 0; i < this.hooks.length; i++) { - this.hooks[i].x = me.x; - this.hooks[i].y = me.y; + + return poi; + } + + /** + * Helper to create POI from preset stairs + * @param {number} stairId - The ID of the preset stair to find + * @param {string} name - Name to display + * @param {Object} [action] - Optional action + * @return {Object|false} POI information or false if not found + */ + function createPOIFromStair(stairId, name, action = null) { + const unit = Game.getPresetStair(me.area, stairId); + if (!unit) return false; + + const poi = { + name: name, + x: unit.x, + y: unit.y + }; + + if (action) { + poi.action = action; } - }, - - flush: function () { - while (this.hooks.length) { - this.hooks.shift().remove(); + + return poi; + } + + /** + * Helper to create a POI from fixed coordinates + * @param {number} x - X coordinate + * @param {number} y - Y coordinate + * @param {string} name - Name to display + * @param {Object} [action] - Optional action + * @return {Object} POI information + */ + function createFixedPOI(x, y, name, action = null) { + const poi = { + name: name, + x: x, + y: y + }; + + if (action) { + poi.action = action; } - - while (this.names.length) { - this.names.shift().remove(); + + return poi; + } + + /** + * Helper to create a POI from a live monster + * @param {number} monsterId - Monster ID to look for + * @param {number} x - Default X coordinate if monster not found + * @param {number} y - Default Y coordinate if monster not found + * @param {string} name - Name to display + * @param {Object} [action] - Optional action + * @return {Object} POI information + */ + function createMonsterPOI(monsterId, x, y, name, action = null) { + const monster = Game.getMonster(monsterId); + const poi = { + name: name, + x: monster ? monster.x : x, + y: monster ? monster.y : y + }; + + if (action) { + poi.action = action; } + + return poi; + } - VectorHooks.currArea = 0; - }, - - getWP: function () { - if (Pather.wpAreas.indexOf(me.area) === -1) return false; - - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - - if (preset) { - return preset.realCoords(); - } + /** + * Handlers for specific locations + */ + const poiHandlers = { + superChest: function() { + return createPOIFromPreset( + sdk.objects.SmallSparklyChest, + "SuperChest", + { do: "openChest", id: sdk.objects.SmallSparklyChest } + ); + }, + + largeSparklyChest: function() { + return createPOIFromPreset( + sdk.objects.LargeSparklyChest, + "SuperChest", + { do: "openChest", id: sdk.objects.LargeSparklyChest } + ); + }, + + talRashaTomb: function() { + let poi = createPOIFromPreset(sdk.quest.chest.HoradricStaffHolder, "Orifice"); + if (poi) return poi; + + return createPOIFromPreset(sdk.objects.SmallSparklyChest, "SuperChest"); + }, + + getHellEntrance: function () { + return createPOIFromPreset( + sdk.objects.RedPortal, + "Hell Entrance", + { do: "usePortal" } + ); + }, + }; + + poiHandlers[sdk.areas.ColdPlains] = function() { + return createPOIFromStair(sdk.exits.preset.AreaEntrance, "Cave Level 1"); + }; + + poiHandlers[sdk.areas.StonyField] = function() { + return createPOIFromMonster( + sdk.monsters.preset.Rakanishu, + "Cairn Stones", + { do: "usePortal", id: sdk.areas.Tristram } + ); + }; + + poiHandlers[sdk.areas.DarkWood] = function() { + return createPOIFromPreset(sdk.quest.chest.InifussTree, "Tree"); + }; + + poiHandlers[sdk.areas.BlackMarsh] = function() { + return createPOIFromStair(sdk.exits.preset.AreaEntrance, "Hole Level 1"); + }; + + poiHandlers[sdk.areas.DenofEvil] = function() { + return createPOIFromMonster(sdk.monsters.preset.Corpsefire, "Corpsefire"); + }; + + poiHandlers[sdk.areas.BurialGrounds] = function() { + return createPOIFromMonster(sdk.monsters.preset.BloodRaven, "Bloodraven"); + }; + + poiHandlers[sdk.areas.TowerCellarLvl5] = function() { + return createPOIFromPreset(sdk.objects.SuperChest, "Countess"); + }; + + poiHandlers[sdk.areas.Barracks] = function() { + return createPOIFromPreset(sdk.quest.chest.MalusHolder, "Smith"); + }; + + poiHandlers[sdk.areas.Cathedral] = function() { + return createFixedPOI(20047, 4898, "BoneAsh"); + }; + + poiHandlers[sdk.areas.CatacombsLvl4] = function() { + return createFixedPOI(22549, 9520, "Andariel"); + }; + + poiHandlers[sdk.areas.Tristram] = function() { + return createMonsterPOI(sdk.monsters.Griswold, 25163, 5170, "Griswold"); + }; + + poiHandlers[sdk.areas.MooMooFarm] = function() { + const monster = Game.getMonster(sdk.monsters.TheCowKing); + if (monster) { + return createFixedPOI(monster.x, monster.y, "Cow King"); + } + const preset = Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing); + if (preset) { + return createFixedPOI(preset.x, preset.y, "Cow King"); } - return false; - }, - - getPOI: function () { - let unit, name; - let poi = {}; - - switch (me.area) { - case sdk.areas.CaveLvl2: - case sdk.areas.HoleLvl2: - case sdk.areas.PitLvl2: - case sdk.areas.Crypt: - case sdk.areas.Mausoleum: - case sdk.areas.StonyTombLvl2: - case sdk.areas.AncientTunnels: - case sdk.areas.GreatMarsh: - case sdk.areas.SpiderCave: - case sdk.areas.SwampyPitLvl3: - case sdk.areas.DisusedFane: - case sdk.areas.ForgottenReliquary: - case sdk.areas.ForgottenTemple: - case sdk.areas.DisusedReliquary: - case sdk.areas.DrifterCavern: - case sdk.areas.IcyCellar: - case sdk.areas.Abaddon: - case sdk.areas.PitofAcheron: - case sdk.areas.InfernalPit: - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - poi = { name: "SuperChest", action: { do: "openChest", id: sdk.objects.SmallSparklyChest } }; - - break; - case sdk.areas.GlacialTrail: - case sdk.areas.HallsofAnguish: - case sdk.areas.HallsofPain: - unit = Game.getPresetObject(me.area, sdk.objects.LargeSparklyChest); - poi = { name: "SuperChest", action: { do: "openChest", id: sdk.objects.LargeSparklyChest } }; - - break; - case sdk.areas.ColdPlains: - unit = Game.getPresetStair(me.area, sdk.exits.preset.AreaEntrance); - name = "Cave Level 1"; - - break; - case sdk.areas.StonyField: - unit = Game.getPresetMonster(me.area, sdk.monsters.preset.Rakanishu); - poi = { name: "Cairn Stones", action: { do: "usePortal", id: sdk.areas.Tristram } }; - - break; - case sdk.areas.DarkWood: - unit = Game.getPresetObject(me.area, sdk.quest.chest.InifussTree); - name = "Tree"; - - break; - case sdk.areas.BlackMarsh: - unit = Game.getPresetStair(me.area, sdk.exits.preset.AreaEntrance); - name = "Hole Level 1"; - - break; - case sdk.areas.DenofEvil: - unit = Game.getPresetMonster(me.area, sdk.monsters.preset.Corpsefire); - name = "Corpsefire"; - - break; - case sdk.areas.BurialGrounds: - unit = Game.getPresetMonster(me.area, sdk.monsters.preset.BloodRaven); - name = "Bloodraven"; - - break; - case sdk.areas.TowerCellarLvl5: - unit = Game.getPresetObject(me.area, sdk.objects.SuperChest); - name = "Countess"; - - break; - case sdk.areas.Barracks: - unit = Game.getPresetObject(me.area, sdk.quest.chest.MalusHolder); - name = "Smith"; - - break; - case sdk.areas.Cathedral: - unit = { x: 20047, y: 4898 }; - name = "BoneAsh"; - - break; - case sdk.areas.CatacombsLvl4: - unit = { x: 22549, y: 9520 }; - name = "Andariel"; - - break; - case sdk.areas.Tristram: - unit = Game.getMonster(sdk.monsters.Griswold) ? Game.getMonster(sdk.monsters.Griswold) : { x: 25163, y: 5170 }; - name = "Griswold"; - - break; - case sdk.areas.MooMooFarm: - unit = Game.getMonster(sdk.monsters.TheCowKing) - ? Game.getMonster(sdk.monsters.TheCowKing) - : Game.getPresetMonster(me.area, sdk.monsters.preset.TheCowKing); - name = "Cow King"; - - break; - case sdk.areas.LutGholein: - unit = Game.getPresetStair(me.area, sdk.exits.preset.A2EnterSewersDoor); - name = "Sewer's Level 1"; - - break; - case sdk.areas.A2SewersLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricScrollChest); - name = "Radament"; - - break; - case sdk.areas.PalaceCellarLvl3: - unit = { x: 10073, y: 8670 }; - poi = { name: "Arcane Sanctuary", action: { do: "usePortal" } }; - - break; - case sdk.areas.HallsoftheDeadLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricCubeChest); - poi = { name: "Cube", action: { do: "openChest", id: sdk.quest.chest.HoradricCubeChest } }; - - break; - case sdk.areas.ClawViperTempleLvl2: - unit = Game.getPresetObject(me.area, sdk.quest.chest.ViperAmuletChest); - poi = { name: "Amulet", action: { do: "openChest", id: sdk.quest.chest.ViperAmuletChest } }; - - break; - case sdk.areas.MaggotLairLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.ShaftoftheHoradricStaffChest); - poi = { name: "Staff", action: { do: "openChest", id: sdk.quest.chest.ShaftoftheHoradricStaffChest } }; - - break; - case sdk.areas.ArcaneSanctuary: - unit = Game.getPresetObject(me.area, sdk.quest.chest.Journal); - name = "Summoner"; - - break; - case sdk.areas.TalRashasTomb1: - case sdk.areas.TalRashasTomb2: - case sdk.areas.TalRashasTomb3: - case sdk.areas.TalRashasTomb4: - case sdk.areas.TalRashasTomb5: - case sdk.areas.TalRashasTomb6: - case sdk.areas.TalRashasTomb7: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HoradricStaffHolder); - name = "Orifice"; + }; + + poiHandlers[sdk.areas.LutGholein] = function() { + return createPOIFromStair(sdk.exits.preset.A2EnterSewersDoor, "Sewer's Level 1"); + }; + + poiHandlers[sdk.areas.A2SewersLvl3] = function() { + return createPOIFromPreset(sdk.quest.chest.HoradricScrollChest, "Radament"); + }; + + poiHandlers[sdk.areas.HallsoftheDeadLvl3] = function() { + return createPOIFromPreset( + sdk.quest.chest.HoradricCubeChest, + "Cube", + { do: "openChest", id: sdk.quest.chest.HoradricCubeChest } + ); + }; + + poiHandlers[sdk.areas.ClawViperTempleLvl2] = function() { + return createPOIFromPreset( + sdk.quest.chest.ViperAmuletChest, + "Amulet", + { do: "openChest", id: sdk.quest.chest.ViperAmuletChest } + ); + }; + + poiHandlers[sdk.areas.MaggotLairLvl3] = function() { + return createPOIFromPreset( + sdk.quest.chest.ShaftoftheHoradricStaffChest, + "Staff", + { do: "openChest", id: sdk.quest.chest.ShaftoftheHoradricStaffChest } + ); + }; + + poiHandlers[sdk.areas.ArcaneSanctuary] = function() { + return createPOIFromPreset(sdk.quest.chest.Journal, "Summoner"); + }; + + poiHandlers[sdk.areas.DurielsLair] = function() { + return createFixedPOI(22577, 15609, "Tyrael"); + }; + + poiHandlers[sdk.areas.FlayerJungle] = function() { + return createPOIFromPreset(sdk.quest.chest.GidbinnAltar, "Gidbinn"); + }; + + poiHandlers[sdk.areas.KurastBazaar] = function() { + return createPOIFromStair(sdk.exits.preset.A3EnterSewers, "Sewer's Level 1"); + }; + + poiHandlers[sdk.areas.SpiderCavern] = function() { + return createPOIFromPreset( + sdk.quest.chest.KhalimsEyeChest, + "Eye", + { do: "openChest", id: sdk.quest.chest.KhalimsEyeChest } + ); + }; + + poiHandlers[sdk.areas.FlayerDungeonLvl3] = function() { + return createPOIFromPreset( + sdk.quest.chest.KhalimsBrainChest, + "Brain", + { do: "openChest", id: sdk.quest.chest.KhalimsBrainChest } + ); + }; + + poiHandlers[sdk.areas.A3SewersLvl2] = function() { + return createPOIFromPreset( + sdk.quest.chest.KhalimsHeartChest, + "Heart", + { do: "openChest", id: sdk.quest.chest.KhalimsHeartChest } + ); + }; + + poiHandlers[sdk.areas.RuinedTemple] = function() { + return createPOIFromPreset( + sdk.quest.chest.LamEsensTomeHolder, + "Lam Esen", + { do: "openChest", id: sdk.quest.chest.LamEsensTomeHolder } + ); + }; + + poiHandlers[sdk.areas.Travincal] = function() { + return createPOIFromPreset(sdk.objects.CompellingOrb, "Orb"); + }; + + poiHandlers[sdk.areas.DuranceofHateLvl3] = function() { + return createFixedPOI(17588, 8069, "Mephisto"); + }; + + poiHandlers[sdk.areas.PlainsofDespair] = function() { + return createPOIFromMonster(sdk.monsters.Izual, "Izual"); + }; + + poiHandlers[sdk.areas.RiverofFlame] = function() { + return createPOIFromPreset(sdk.quest.chest.HellForge, "Hephasto"); + }; + + poiHandlers[sdk.areas.ChaosSanctuary] = function() { + return createPOIFromPreset(sdk.objects.DiabloStar, "Star"); + }; + + poiHandlers[sdk.areas.Harrogath] = function() { + return createFixedPOI( + 5112, 5120, + "Anya Portal", + { do: "usePortal", id: sdk.areas.NihlathaksTemple } + ); + }; + + poiHandlers[sdk.areas.BloodyFoothills] = function() { + return createFixedPOI(3899, 5113, "Shenk"); + }; + + poiHandlers[sdk.areas.FrozenRiver] = function() { + return createPOIFromPreset(sdk.objects.FrozenAnyasPlatform, "Frozen Anya"); + }; + + poiHandlers[sdk.areas.NihlathaksTemple] = function() { + return createFixedPOI(10058, 13234, "Pindle"); + }; + + poiHandlers[sdk.areas.HallsofVaught] = function() { + return createPOIFromPreset(sdk.objects.NihlathaksPlatform, "Nihlathak"); + }; + + poiHandlers[sdk.areas.ThroneofDestruction] = function() { + return createFixedPOI(15118, 5002, "Throne Room"); + }; + + poiHandlers[sdk.areas.WorldstoneChamber] = function() { + const baal = Game.getMonster(sdk.monsters.Baal); + return createFixedPOI( + baal ? baal.x : 15134, + baal ? baal.y : 5923, + "Baal" + ); + }; + + poiHandlers[sdk.areas.MatronsDen] = function() { + return createPOIFromPreset(sdk.objects.SmallSparklyChest, "Lilith"); + }; + + poiHandlers[sdk.areas.ForgottenSands] = function() { + const duriel = Game.getMonster(sdk.monsters.UberDuriel); + if (!duriel) return false; + return createFixedPOI(duriel.x, duriel.y, "Duriel"); + }; + + poiHandlers[sdk.areas.FurnaceofPain] = function() { + return createPOIFromPreset(sdk.objects.SmallSparklyChest, "Izual"); + }; + + /** + * Add a vector line from player to the specified coordinates + * @param {number} x - destination x coordinate + * @param {number} y - destination y coordinate + * @param {number} color - line color + */ + function addVector(x, y, color) { + VectorHooks.hooks.push(new Line(me.x, me.y, x, y, color, true)); + } - if (!unit) { - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - name = "SuperChest"; + /** + * Add area name text at specified coordinates + * @param {Object} area - area information + */ + function addAreaName(area) { + VectorHooks.names.push(new Text(getAreaName(area.target), area.x, area.y, 0, 6, 2, true)); + } + + return { + enabled: true, + currArea: 0, + lastLoc: { x: 0, y: 0 }, + /** @type {Text[]} */ + names: [], + /** @type {Line[]} */ + hooks: [], + + check: function () { + if (!this.enabled) { + this.flush(); + + return; } - break; - case sdk.areas.DurielsLair: - unit = { x: 22577, y: 15609 }; - name = "Tyrael"; - - break; - case sdk.areas.FlayerJungle: - unit = Game.getPresetObject(me.area, sdk.quest.chest.GidbinnAltar); - name = "Gidbinn"; - - break; - case sdk.areas.KurastBazaar: - unit = Game.getPresetStair(me.area, sdk.exits.preset.A3EnterSewers); - name = "Sewer's Level 1"; - - break; - case sdk.areas.SpiderCavern: - unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsEyeChest); - poi = { name: "Eye", action: { do: "openChest", id: sdk.quest.chest.KhalimsEyeChest } }; - - break; - case sdk.areas.FlayerDungeonLvl3: - unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsBrainChest); - poi = { name: "Brain", action: { do: "openChest", id: sdk.quest.chest.KhalimsBrainChest } }; - - break; - case sdk.areas.A3SewersLvl2: - unit = Game.getPresetObject(me.area, sdk.quest.chest.KhalimsHeartChest); - poi = { name: "Heart", action: { do: "openChest", id: sdk.quest.chest.KhalimsHeartChest } }; - - break; - case sdk.areas.RuinedTemple: - unit = Game.getPresetObject(me.area, sdk.quest.chest.LamEsensTomeHolder); - poi = { name: "Lam Esen", action: { do: "openChest", id: sdk.quest.chest.LamEsensTomeHolder } }; - - break; - case sdk.areas.Travincal: - unit = Game.getPresetObject(me.area, sdk.objects.CompellingOrb); - name = "Orb"; - - break; - case sdk.areas.DuranceofHateLvl3: - unit = { x: 17588, y: 8069 }; - name = "Mephisto"; - - break; - case sdk.areas.PlainsofDespair: - unit = Game.getPresetMonster(me.area, sdk.monsters.Izual); - name = "Izual"; - - break; - case sdk.areas.RiverofFlame: - unit = Game.getPresetObject(me.area, sdk.quest.chest.HellForge); - name = "Hephasto"; - - break; - case sdk.areas.ChaosSanctuary: - unit = Game.getPresetObject(me.area, sdk.objects.DiabloStar); - name = "Star"; - - break; - case sdk.areas.Harrogath: - unit = { x: 5112, y: 5120 }; - poi = { name: "Anya Portal", action: { do: "usePortal", id: sdk.areas.NihlathaksTemple } }; + if (me.area !== this.currArea) { + this.flush(); + + if (!me.area || !me.gameReady) return; + + try { + /** @type {Area["exits"]} */ + const exits = getArea().exits; + VectorHooks.currArea = me.area; + + if (exits) { + for (let exit of exits) { + if (me.inArea(sdk.areas.CanyonofMagic)) { + addVector(exit.x, exit.y, exit.target === getRoom().correcttomb ? 0x69 : 0x99); + } else if (nextAreas.has(me.area) && exit.target === nextAreas.get(me.area)) { + addVector(exit.x, exit.y, 0x1F); + } else if (exit.target === ActionHooks.prevAreas.indexOf(me.area) && nextAreas.get(me.area)) { + addVector(exit.x, exit.y, 0x99); + } else if (exit.target === ActionHooks.prevAreas.indexOf(me.area)) { + addVector(exit.x, exit.y, 0x1F); + } else if (exit.target === ActionHooks.prevAreas[me.area]) { + addVector(exit.x, exit.y, 0x0A); + } else { + addVector(exit.x, exit.y, 0x99); + } + + addAreaName(exit); + } + } - break; - case sdk.areas.BloodyFoothills: - unit = { x: 3899, y: 5113 }; - name = "Shenk"; + let wp = this.getWP(); + wp && addVector(wp.x, wp.y, 0xA8); + let poi = this.getPOI(); + poi && addVector(poi.x, poi.y, 0x7D); + } catch (e) { + console.error(e); + } + } else if (me.x !== this.lastLoc.x || me.y !== this.lastLoc.y) { + VectorHooks.update(); + } + }, - break; - case sdk.areas.FrigidHighlands: - case sdk.areas.ArreatPlateau: - case sdk.areas.FrozenTundra: - unit = Game.getPresetObject(me.area, sdk.objects.RedPortal); - poi = { name: "Hell Entrance", action: { do: "usePortal" } }; + update: function () { + VectorHooks.lastLoc = { x: me.x, y: me.y }; - break; - case sdk.areas.FrozenRiver: - unit = Game.getPresetObject(me.area, sdk.objects.FrozenAnyasPlatform); - name = "Frozen Anya"; + for (let i = 0; i < this.hooks.length; i++) { + this.hooks[i].x = me.x; + this.hooks[i].y = me.y; + } + }, - break; - case sdk.areas.NihlathaksTemple: - unit = { x: 10058, y: 13234 }; - name = "Pindle"; + flush: function () { + while (this.hooks.length) { + this.hooks.pop().remove(); + } - break; - case sdk.areas.HallsofVaught: - unit = Game.getPresetObject(me.area, sdk.objects.NihlathaksPlatform); - name = "Nihlathak"; + while (this.names.length) { + this.names.pop().remove(); + } - break; - case sdk.areas.ThroneofDestruction: - unit = { x: 15118, y: 5002 }; - name = "Throne Room"; + VectorHooks.currArea = 0; + }, - break; - case sdk.areas.WorldstoneChamber: - unit = Game.getMonster(sdk.monsters.Baal) ? Game.getMonster(sdk.monsters.Baal) : { x: 15134, y: 5923 }; - name = "Baal"; + getWP: function () { + if (Pather.wpAreas.indexOf(me.area) === -1) return false; - break; - case sdk.areas.MatronsDen: - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - name = "Lilith"; + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); - break; - case sdk.areas.ForgottenSands: - unit = Game.getMonster(sdk.monsters.UberDuriel); - name = "Duriel"; + if (preset) { + return preset.realCoords(); + } + } - break; - case sdk.areas.FurnaceofPain: - unit = Game.getPresetObject(me.area, sdk.objects.SmallSparklyChest); - name = "Izual"; + return false; + }, - break; - } + getPOI: function () { + if (superChestAreas.has(me.area)) { + let result = poiHandlers.superChest(); + if (result) return result; + } + + if (largeSparklyChestAreas.has(me.area)) { + let result = poiHandlers.largeSparklyChest(); + if (result) return result; + } - if (unit) { - name && !poi.name && (poi.name = name); - (unit instanceof PresetUnit) && (unit = unit.realCoords()); - [poi.x, poi.y] = [unit.x, unit.y]; + if (hellEntranceAreas.has(me.area)) { + let result = poiHandlers.getHellEntrance(); + if (result) return result; + } + + if (me.area >= sdk.areas.TalRashasTomb1 && me.area <= sdk.areas.TalRashasTomb7) { + return poiHandlers.talRashaTomb(); + } + + if (poiHandlers[me.area]) { + try { + return poiHandlers[me.area](); + } catch (e) { + console.error("Error in POI handler for area " + me.area + ": " + e); + } + } - return poi; + return false; } - - return false; - } -}; + }; +})(); diff --git a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js index d6677ebab..ecf212f1b 100644 --- a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js @@ -11,14 +11,14 @@ Attack.init = function (notify = false) { if (Config.Wereform) { include("core/Attacks/wereform.js"); } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { - print("Loading custom attack file"); + console.log("Loading custom attack file"); include("core/Attacks/" + Config.CustomClassAttack + ".js"); } else { include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); } if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { - notify && print("ÿc1Bad attack config. Don't expect your bot to attack."); + notify && console.log("ÿc1Bad attack config. Don't expect your bot to attack."); } this.getPrimarySlot(); diff --git a/d2bs/kolbot/libs/manualplay/libs/Hooks.js b/d2bs/kolbot/libs/manualplay/libs/Hooks.js new file mode 100644 index 000000000..b73ffd2c4 --- /dev/null +++ b/d2bs/kolbot/libs/manualplay/libs/Hooks.js @@ -0,0 +1,89 @@ +/** + * @typedef {import('./hooks/TextHooks')} + */ + +const Hooks = { + dashBoard: { x: 113, y: 490 }, + portalBoard: { x: 12, y: 432 }, + qolBoard: { x: 545, y: 490 }, + resfix: { x: (me.screensize ? 0 : -160), y: (me.screensize ? 0 : -120) }, + saidMessage: false, + userAddon: false, + enabled: true, + flushed: false, + + init: function () { + let files = dopen("libs/manualplay/hooks/").getFiles(); + + Array.isArray(files) && files + .filter(file => file.endsWith(".js")) + .forEach(function (x) { + if (!isIncluded("manualplay/hooks/" + x)) { + if (!include("manualplay/hooks/" + x)) { + throw new Error("Failed to include " + "manualplay/hooks/" + x); + } + } + }); + }, + + update: function () { + while (!me.gameReady) { + delay(100); + } + + if (!this.enabled) { + Hooks.enabled = getUIFlag(sdk.uiflags.AutoMap); + + return; + } + + ActionHooks.check(); + VectorHooks.check(); + MonsterHooks.check(); + ShrineHooks.check(); + ItemHooks.check(); + TextHooks.check(); + Hooks.flushed = false; + }, + + flush: function (flag) { + if (Hooks.flushed === flag) return true; + + const invoFlagCheck = function () { + return [sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.TradePrompt].every(function (el) { + return !getUIFlag(el); + }); + }; + + if (flag === true) { + Hooks.enabled = false; + + MonsterHooks.flush(); + ShrineHooks.flush(); + TextHooks.flush(); + VectorHooks.flush(); + ActionHooks.flush(); + ItemHooks.flush(); + } else { + if (sdk.uiflags.Waypoint === flag) { + VectorHooks.flush(); + TextHooks.displaySettings = false; + TextHooks.check(); + } else if (sdk.uiflags.Inventory === flag && invoFlagCheck()) { + ItemHooks.flush(); + TextHooks.check(); + } else { + MonsterHooks.flush(); + ShrineHooks.flush(); + TextHooks.flush(); + VectorHooks.flush(); + ActionHooks.flush(); + ItemHooks.flush(); + } + } + + Hooks.flushed = flag; + + return true; + } +}; diff --git a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js index 9f6f52c83..d879f713e 100644 --- a/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/PatherOverrides.js @@ -85,7 +85,7 @@ Pather.changeAct = function (act) { } else if (useWp) { Town.goToTown(act); } else { - print("Failed to move to " + npc); + console.log("Failed to move to " + npc); me.overhead("Failed to move to " + npc); } diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index 9204d0655..08c96330a 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /** * @filename main.js * @author theBGuy @@ -24,85 +23,9 @@ const LocalChat = require("../modules/LocalChat"); include("manualplay/MapMode.js"); MapMode.include(); -const Hooks = { - dashBoard: { x: 113, y: 490 }, - portalBoard: { x: 12, y: 432 }, - qolBoard: { x: 545, y: 490 }, - resfix: { x: (me.screensize ? 0 : -160), y: (me.screensize ? 0 : -120) }, - saidMessage: false, - userAddon: false, - enabled: true, - flushed: false, - - init: function () { - let files = dopen("libs/manualplay/hooks/").getFiles(); - - Array.isArray(files) && files - .filter(file => file.endsWith(".js")) - .forEach(function (x) { - if (!isIncluded("manualplay/hooks/" + x)) { - if (!include("manualplay/hooks/" + x)) { - throw new Error("Failed to include " + "manualplay/hooks/" + x); - } - } - }); - }, - - update: function () { - while (!me.gameReady) { - delay(100); - } - - if (!this.enabled) { - Hooks.enabled = getUIFlag(sdk.uiflags.AutoMap); - - return; - } - - ActionHooks.check(); - VectorHooks.check(); - MonsterHooks.check(); - ShrineHooks.check(); - ItemHooks.check(); - TextHooks.check(); - Hooks.flushed = false; - }, - - flush: function (flag) { - if (Hooks.flushed === flag) return true; - - if (flag === true) { - Hooks.enabled = false; - - MonsterHooks.flush(); - ShrineHooks.flush(); - TextHooks.flush(); - VectorHooks.flush(); - ActionHooks.flush(); - ItemHooks.flush(); - } else { - if (sdk.uiflags.Waypoint === flag) { - VectorHooks.flush(); - TextHooks.displaySettings = false; - TextHooks.check(); - } else if (sdk.uiflags.Inventory === flag && [sdk.uiflags.Stash, sdk.uiflags.Cube, sdk.uiflags.TradePrompt].every((el) => !getUIFlag(el))) { - ItemHooks.flush(); - TextHooks.check(); - } else { - MonsterHooks.flush(); - ShrineHooks.flush(); - TextHooks.flush(); - VectorHooks.flush(); - ActionHooks.flush(); - ItemHooks.flush(); - } - } - - Hooks.flushed = flag; - - return true; - } -}; +/** + * @typedef {import('./hooks/TextHooks')} + */ function main () { D2Bot.init(); // Get D2Bot# handle @@ -351,38 +274,42 @@ function main () { addEventListener("keyup", ActionHooks.event); // addEventListener("itemaction", Pickit.itemEvent); - while (true) { - while (!me.area || !me.gameReady) { - delay(100); - } + try { + while (true) { + while (!me.area || !me.gameReady) { + delay(100); + } - let hideFlagFound = false; + let hideFlagFound = false; - revealArea(me.area); - - for (let i = 0; i < hideFlags.length; i++) { - if (getUIFlag(hideFlags[i])) { - Hooks.flush(hideFlags[i]); - ActionHooks.checkAction(); - hideFlagFound = true; - delay(100); + revealArea(me.area); + + for (let i = 0; i < hideFlags.length; i++) { + if (getUIFlag(hideFlags[i])) { + Hooks.flush(hideFlags[i]); + ActionHooks.checkAction(); + hideFlagFound = true; + delay(100); - break; + break; + } } - } - if (hideFlagFound) { - continue; - } + if (hideFlagFound) { + continue; + } - getUIFlag(sdk.uiflags.AutoMap) - ? Hooks.update() - : Hooks.flush(true) && (!HelpMenu.cleared && HelpMenu.hideMenu()); + getUIFlag(sdk.uiflags.AutoMap) + ? Hooks.update() + : Hooks.flush(true) && (!HelpMenu.cleared && HelpMenu.hideMenu()); - delay(20); + delay(20); - while (getUIFlag(sdk.uiflags.ShowItem)) { - ItemHooks.flush(); + while (getUIFlag(sdk.uiflags.ShowItem)) { + ItemHooks.flush(); + } } + } catch (e) { + Misc.errorReport(e, "main.js", "main()"); } } diff --git a/d2bs/kolbot/libs/manualplay/modules/HookFactory.js b/d2bs/kolbot/libs/manualplay/modules/HookFactory.js new file mode 100644 index 000000000..85b23dd16 --- /dev/null +++ b/d2bs/kolbot/libs/manualplay/modules/HookFactory.js @@ -0,0 +1,130 @@ +/** + * @filename HookFactory.js + * @author theBGuy + * @desc UMD module HookFactory for MapThread + * + */ + +(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.HookFactory = factory(); + } +}([].filter.constructor("return this")(), function() { + /** + * @typedef {Object} HookEntry + * @property {string} name - The identifier for this hook entry + * @property {Hook} hook - The actual hook object (Text, Box, Frame, etc.) + * @property {number} [dest] - Optional destination (for act change hooks) + * @property {string} [type] - Optional type information (e.g., "actChange") + */ + + const HookFactory = { + createHooks: { + // text: string, x: number, y: number, color: number, font: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function + /** + * Creates a text hook + * @param {Partial} options + * @returns {HookEntry} The created hook entry + */ + text: function(options = {}) { + const { name, text, x, y, color, font, align, automap, handler } = Object.assign({ + name: "", + text: "", + x: 0, + y: 0, + color: 4, + font: 0, + align: 0, + automap: false, + handler: null + }, options); + return { + name: name, + hook: new Text(text, x, y, color, font, align, automap, handler), + }; + }, + + // x: number, y: number, xsize: number, ysize: number, color: number, opacity: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function + /** + * Creates a box hook + * @param {Partial} options + * @returns {HookEntry} The created hook entry + */ + box: function(options = {}) { + const { name, x, y, width, height, color, transparency, click } = Object.assign({ + name: "", + x: 0, + y: 0, + width: 0, + height: 0, + color: 0x0, + transparency: 1, + click: null + }, options); + return { + name: name, + hook: new Box(x, y, width, height, color, transparency, click) + }; + }, + + // rame(x: number, y: number, xsize: number, ysize: number, color: number, opacity: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function) + /** + * Creates a frame hook + * @param {Partial} options + */ + frame: function(options = {}) { + const { name, x, y, width, height, style } = Object.assign({ + name: "", + x: 0, + y: 0, + width: 0, + height: 0, + style: 0 + }, options); + return { + name: name, + hook: new Frame(x, y, width, height, style) + }; + } + }, + + /** + * Creates a container with a box and frame + * @param {string} boxName - Name for the box element + * @param {string} frameName - Name for the frame element + * @param {number} x - X coordinate position + * @param {number} y - Y coordinate position + * @param {number} width - Width of the container + * @param {number} height - Height of the container + * @returns {Array} Array containing the box and frame hooks + */ + createContainer: function(boxName, frameName, x, y, width, height) { + const container = []; + + container.push(this.createHooks.box({ + name: boxName, + x: x, + y: y, + width: width, + height: height + })); + container[container.length - 1].hook.zorder = 0; + container.push(this.createHooks.frame({ + name: frameName, + x: x, + y: y, + width: width, + height: height + })); + + return container; + } + }; + + return HookFactory; +})); diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 78f1a3d6f..51fd5ebe2 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -64,6 +64,7 @@ function main () { }; portalMap[sdk.areas.InfernalPit] = { 14: [12638, 9373], + 15: [12638, 9063], 20: [12708, 9063], 25: [12948, 9128], }; @@ -242,9 +243,14 @@ function main () { break; case sdk.areas.ArcaneSanctuary: - Pather.moveTo(12692, 5195); - redPortal = Pather.getPortal(obj.dest); - !redPortal && Pather.useWaypoint(obj.dest); + if (me.inArea(sdk.areas.CanyonofMagic)) { + Pather.moveTo(12692, 5195); + redPortal = Pather.getPortal(obj.dest); + !redPortal && Pather.useWaypoint(obj.dest); + } else if (me.inArea(sdk.areas.PalaceCellarLvl3)) { + Pather.moveTo(10073, 8670); + Pather.usePortal(null); + } break; case sdk.areas.Harrogath: From 3dc2a628bdb7d309166a905796ae71af24af8926 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Apr 2025 12:42:40 -0400 Subject: [PATCH 512/758] Update ShrineHooks.js - Removed redundant usage of array when we were always accessing the first/and only element --- d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js index 964a89e71..6b28f37ac 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ShrineHooks.js @@ -43,7 +43,7 @@ const ShrineHooks = { for (let i = 0; i < this.hooks.length; i++) { if (!copyUnit(this.hooks[i].shrine).objtype) { - this.hooks[i].hook[0].remove(); + this.hooks[i].hook.remove(); this.hooks.splice(i, 1); i -= 1; @@ -72,8 +72,8 @@ const ShrineHooks = { /** @param {ObjectUnit} shrine */ newHook: function (shrine) { - let typeName = this.shrines.get(shrine.objtype); - return typeName ? [new Text(typeName, shrine.x, shrine.y, 4, 6, 2, true)] : []; + let typeName = ShrineHooks.shrines.get(shrine.objtype); + return typeName ? new Text(typeName, shrine.x, shrine.y, 4, 6, 2, true) : null; }, /** @param {ObjectUnit} shrine */ @@ -88,9 +88,9 @@ const ShrineHooks = { /** @param {ObjectUnit} shrine */ getHook: function (shrine) { - for (let i = 0; i < this.hooks.length; i++) { - if (this.hooks[i].shrine.gid === shrine.gid) { - return this.hooks[i].hook; + for (let entry of ShrineHooks.hooks) { + if (entry.shrine.gid === shrine.gid) { + return entry.hook; } } @@ -101,7 +101,7 @@ const ShrineHooks = { remove: function (shrine) { for (let i = 0; i < this.hooks.length; i++) { if (this.hooks[i].shrine.gid === shrine.gid) { - this.hooks[i].hook[0].remove(); + this.hooks[i].hook.remove(); this.hooks.splice(i, 1); return true; From a91491f0cf684abb40e3261d4eef08d952d2ce89 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:37:57 -0400 Subject: [PATCH 513/758] Update Config.js - Add back `UnpartyForMinGameTimeWait: false,` it got removed in 09ce5b6e99d83d1d70b99b9f3d8a811d24e8e7ae --- d2bs/kolbot/libs/core/Config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index fb44843db..a794c2b26 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -226,6 +226,7 @@ let Config = { PublicMode: false, PartyAfterScript: false, AnnounceGameTimeRemaing: false, + UnpartyForMinGameTimeWait: false, /** @type {string[]} */ Greetings: [], From 6902c7c44c3cdd38ac6d755268302ec19a807502 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 25 Apr 2025 01:17:44 -0400 Subject: [PATCH 514/758] [hotfix] Update ActionHooks for handling Throne of Destruction exits ordering - Needed to define the Worldstone chamber exit manually which resulted in the wsk3 exit being out of order so add to special cases and return early from add like we do for durance and palace --- d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js index 24c56a786..c47e8b6b4 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js @@ -210,6 +210,7 @@ const ActionHooks = (function () { ]], [sdk.areas.ThroneofDestruction, [ + { name: "Previous Area", type: "area", dest: sdk.areas.WorldstoneLvl3 }, { name: "Next Area", type: "area", dest: sdk.areas.WorldstoneChamber }, ]], ]); @@ -960,7 +961,11 @@ const ActionHooks = (function () { } } - if (me.inArea(sdk.areas.DuranceofHateLvl3) || me.inArea(sdk.areas.PalaceCellarLvl3)) { + if ( + me.inArea(sdk.areas.DuranceofHateLvl3) + || me.inArea(sdk.areas.PalaceCellarLvl3) + || me.inArea(sdk.areas.ThroneofDestruction) + ) { // hacky but this is the only way to get the correct ordering return; } From 9fcb17fba45d2df27d1bfe6f0f1217b9e6f21da5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:34:48 -0400 Subject: [PATCH 515/758] Add opacity param to HookFactory.createContainer & Make TextHook setting opaque - Allow configuring the Box opacity with createContainer - Made the settings box opaque so the life text doesn't make it hard to read --- d2bs/kolbot/libs/manualplay/hooks/TextHooks.js | 3 ++- d2bs/kolbot/libs/manualplay/modules/HookFactory.js | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js index 2043f4518..302dccbe6 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js @@ -266,7 +266,8 @@ const TextHooks = (function () { 5, 503 - TextHooks.settingsModifer * 10 - statusHookNames.length * 12 + Hooks.resfix.y, 170, - TextHooks.statusFrameYSize + TextHooks.statusFrameYSize, + 4 ); containers.forEach(function (container) { diff --git a/d2bs/kolbot/libs/manualplay/modules/HookFactory.js b/d2bs/kolbot/libs/manualplay/modules/HookFactory.js index 85b23dd16..3e176bbca 100644 --- a/d2bs/kolbot/libs/manualplay/modules/HookFactory.js +++ b/d2bs/kolbot/libs/manualplay/modules/HookFactory.js @@ -56,19 +56,19 @@ * @returns {HookEntry} The created hook entry */ box: function(options = {}) { - const { name, x, y, width, height, color, transparency, click } = Object.assign({ + const { name, x, y, width, height, color, opacity, click } = Object.assign({ name: "", x: 0, y: 0, width: 0, height: 0, color: 0x0, - transparency: 1, + opacity: 1, click: null }, options); return { name: name, - hook: new Box(x, y, width, height, color, transparency, click) + hook: new Box(x, y, width, height, color, opacity, click) }; }, @@ -101,9 +101,10 @@ * @param {number} y - Y coordinate position * @param {number} width - Width of the container * @param {number} height - Height of the container + * @param {number} opcacity - Opacity of the box * @returns {Array} Array containing the box and frame hooks */ - createContainer: function(boxName, frameName, x, y, width, height) { + createContainer: function(boxName, frameName, x, y, width, height, opcacity) { const container = []; container.push(this.createHooks.box({ @@ -111,7 +112,8 @@ x: x, y: y, width: width, - height: height + height: height, + opacity: opcacity, })); container[container.length - 1].hook.zorder = 0; container.push(this.createHooks.frame({ From 6bde85dbb195d64adf253eb656b408e346d7dd55 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 26 Apr 2025 01:51:02 -0400 Subject: [PATCH 516/758] [hotfix] Fix reference to this for `addChaosSeals` in ActionHooks.js --- d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js index c47e8b6b4..2774cb89d 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/ActionHooks.js @@ -480,14 +480,14 @@ const ActionHooks = (function () { return seal.realCoords(); }); - const infSeal = seals.find(function () { - return this.id === sdk.objects.DiabloSealInfector; + const infSeal = seals.find(function (seal) { + return seal.id === sdk.objects.DiabloSealInfector; }); - const seisSeal = seals.find(function () { - return this.id === sdk.objects.DiabloSealSeis; + const seisSeal = seals.find(function (seal) { + return seal.id === sdk.objects.DiabloSealSeis; }); - const vizSeal = seals.find(function () { - return this.id === sdk.objects.DiabloSealVizier; + const vizSeal = seals.find(function (seal) { + return seal.id === sdk.objects.DiabloSealVizier; }); if (infSeal) { From cbf3c674c4024cba394f207eb80bd963fef98858 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Apr 2025 17:13:33 -0400 Subject: [PATCH 517/758] [fix] Remove double check from heal check as Town.heal does its own - This was causing chickens occasionally if the first script didn't heal before starting. Was redundant anyway since Town.heal checks the config values --- d2bs/kolbot/default.dbj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 8d4e6073d..85256fb74 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -199,7 +199,7 @@ function main () { Config.ClearInvOnStart && Town.clearInventory(); [x, y].distance > 3 && Pather.moveTo(x, y); Pickit.pickItems(); - me.hpPercent <= 10 && Town.heal() && me.cancelUIFlags(); + Town.heal() && me.cancelUIFlags(); if (Config.DebugMode.Memory) { delay(2000); From 36184223068765ef4aefd118bd27f279e0960768 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Apr 2025 17:21:35 -0400 Subject: [PATCH 518/758] [feat] Add `Config.ImmunityException` override - Mostly useful for characters using physical damage attacks like zeal or frenzy with weapons or other gear that does elemental damage allowing us to kill physical immunes without breaking the immunity itself. --- d2bs/kolbot/libs/core/Attack.js | 8 ++++++-- d2bs/kolbot/libs/core/Config.js | 2 ++ d2bs/kolbot/sdk/types/Attack.d.ts | 2 ++ d2bs/kolbot/sdk/types/Config.d.ts | 11 ++++++----- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 8fb487db3..df4915ae4 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1839,7 +1839,7 @@ const Attack = { /** * @description Get element by skill number * @param {number} skillId - * @returns {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none" | false} + * @returns {DamageType | "none" | false} */ getSkillElement: function (skillId) { let elements = ["physical", "fire", "lightning", "magic", "cold", "poison", "none"]; @@ -1871,7 +1871,7 @@ const Attack = { /** * @description Get a monster's resistance to specified element * @param {Unit | Monster} unit - * @param {"physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"} type + * @param {DamageType | "none"} type * @returns {number} */ getResist: function (unit, type) { @@ -1935,6 +1935,10 @@ const Attack = { return unit.hpPercent > Config.CastStatic; } + if (Config.ImmunityException.includes(damageType)) { + return true; + } + // 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) { diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index a794c2b26..7767c2259 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -270,6 +270,8 @@ let Config = { SkipImmune: [], SkipAura: [], SkipException: [], + /** @type {DamageType[]} */ + ImmunityException: [], /** @type {number[]} */ ScanShrines: [], AutoShriner: false, diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 9918695e1..132f7e558 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -1,5 +1,7 @@ export {}; declare global { + type DamageType = "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt"; + interface AttackResult { FAILED: 0, SUCCESS: 1, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index f3628824a..dc2f5f24e 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -133,11 +133,12 @@ declare global { PickitFiles: string[]; BeltColumn: any[]; MinColumn: any[]; - SkipId: any[]; - SkipEnchant: any[]; - SkipImmune: any[]; - SkipAura: any[]; - SkipException: any[]; + SkipId: number[]; + SkipEnchant: string[]; + SkipImmune: string[]; + SkipAura: string[]; + SkipException: (number | string)[]; + ImmunityException: DamageType[]; ScanShrines: any[]; Debug: boolean; AutoMule: { From b91b2668fc5c3989522e2c06f00328a9591cf78e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Apr 2025 17:24:30 -0400 Subject: [PATCH 519/758] [feat] Enable utilizing `Config.AdvancedCustomAttack` for preAttacks as well --- d2bs/kolbot/libs/core/Attack.js | 8 ++++++++ d2bs/kolbot/libs/core/Config.js | 2 +- d2bs/kolbot/sdk/types/Config.d.ts | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index df4915ae4..ff4c88065 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -186,6 +186,14 @@ const Attack = { getCustomPreAttack: function (unit) { // Check if unit got invalidated if (!unit || !unit.name || !copyUnit(unit).x) return false; + + for (let el of Config.AdvancedCustomAttack) { + if (el.hasOwnProperty("check") && el.hasOwnProperty("preAttack")) { + if (typeof el.check === "function" && el.check(unit)) { + return el.preAttack; + } + } + } for (let i in Config.CustomPreAttack) { if (Config.CustomPreAttack.hasOwnProperty(i)) { diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 7767c2259..8aa748f22 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -364,7 +364,7 @@ let Config = { CustomAttack: {}, /** @type {Record} */ CustomPreAttack: {}, - /** @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} */ + /** @type {{ check: (unit: Monster) => boolean, attack: [number, number], preAttack: number }[]} */ AdvancedCustomAttack: [], TeleStomp: false, NoTele: false, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index dc2f5f24e..d6eba3548 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -204,7 +204,7 @@ declare global { LowManaSkill: any[]; CustomAttack: Record; CustomPreAttack: Record, - AdvancedCustomAttack: { check: (unit: Monster) => boolean, attack: [number, number] }[], + AdvancedCustomAttack: { check: (unit: Monster) => boolean, attack: [number, number], preAttack: number }[], TeleStomp: boolean; NoTele: boolean; ClearType: boolean; From f153270e55f918882495be2a3bd11a597eb93f00 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:17:20 -0400 Subject: [PATCH 520/758] [feat] Add control for CharCreateStatusText popup - This is the popup that shows `name already taken` error state and `please wait` info state --- d2bs/kolbot/libs/modules/Control.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/modules/Control.js b/d2bs/kolbot/libs/modules/Control.js index 3755c4002..c096bf183 100644 --- a/d2bs/kolbot/libs/modules/Control.js +++ b/d2bs/kolbot/libs/modules/Control.js @@ -217,6 +217,7 @@ // these two are the same as popup yes/no, should they be kept seperate or merged? Control.CharCreateHCWarningOk = new Control(sdk.controls.Button, 421, 337, 96, 32); Control.CharCreateHCWarningCancel = new Control(sdk.controls.Button, 281, 337, 96, 32); + Control.CharCreateStatusText = new Control(sdk.controls.LabelBox, 268, 320, 264, 120); } // Lobby Menu Controls From 4d3b193c7d818be09e736992fb8874514b83e63d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 4 May 2025 20:59:10 -0400 Subject: [PATCH 521/758] feat: Experimental OrgTorchHelper - Currently tested with Taxi and Helper modes, allowing users to have a helper that can teleport to mini ubers and also help clear. --- d2bs/kolbot/libs/config/Amazon.js | 7 + d2bs/kolbot/libs/config/Assassin.js | 7 + d2bs/kolbot/libs/config/Barbarian.js | 7 + d2bs/kolbot/libs/config/Druid.js | 7 + d2bs/kolbot/libs/config/Necromancer.js | 7 + d2bs/kolbot/libs/config/Paladin.js | 7 + d2bs/kolbot/libs/config/Sorceress.js | 7 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 7 + d2bs/kolbot/libs/core/Config.js | 7 + d2bs/kolbot/libs/scripts/OrgTorch.js | 311 +++++++----- d2bs/kolbot/libs/scripts/OrgTorchHelper.js | 444 ++++++++++++++++++ .../kolbot/libs/systems/torch/OrgTorchData.js | 68 +++ .../libs/systems/torch/TorchSystem.d.ts | 23 + d2bs/kolbot/sdk/types/Config.d.ts | 7 + 14 files changed, 797 insertions(+), 119 deletions(-) create mode 100644 d2bs/kolbot/libs/scripts/OrgTorchHelper.js create mode 100644 d2bs/kolbot/libs/systems/torch/OrgTorchData.js create mode 100644 d2bs/kolbot/libs/systems/torch/TorchSystem.d.ts diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 76a0b1a2d..3fd65785f 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -292,6 +292,13 @@ function LoadConfig () { Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index bd1f71136..832ff4808 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -292,6 +292,13 @@ function LoadConfig () { Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index df5565e49..4e57c9ace 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -292,6 +292,13 @@ function LoadConfig () { Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index b2cbadf06..db60274fb 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -292,6 +292,13 @@ function LoadConfig () { Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 100b0389d..d308b7dbc 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -292,6 +292,13 @@ function LoadConfig () { Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index c0138136e..27d9a8a97 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -292,6 +292,13 @@ function LoadConfig () { Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 0574f1d27..28d09bdb2 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -292,6 +292,13 @@ function LoadConfig () { Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 813bbf46b..81302de3d 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -272,6 +272,13 @@ Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area Config.OrgTorch.PreGame.Thawing.Drink = 10; // Chug x thawing pots. Each thawing pot gives +50 cold res and +10 max cold for 30 seconds. The duration stacks. 10 potions == 5 minutes + + Scripts.OrgTorchHelper = false; + Config.OrgTorchHelper.Taxi = false; // Taxi the killer to the area + Config.OrgTorchHelper.Helper = true; // Set to true to help attack, set false to wait in town. + Config.OrgTorchHelper.UseWalkPath = false; // Use walk path to get to the area - helpful if leader is a walker and you have tele + Config.OrgTorchHelper.SkipTp = false; // Skip and go through the red portal + Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // // RUSHER USES FOLLOWER ENTRY SCRIPT diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 8aa748f22..996f1eafc 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -686,6 +686,13 @@ let Config = { Antidote: { Drink: 0, At: [] }, } }, + OrgTorchHelper: { + Taxi: false, + Helper: false, + SkipTp: false, + GetFade: false, + UseWalkPath: false, + }, Synch: { WaitFor: [] }, diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index 0811a25b5..ff40820fc 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -19,44 +19,31 @@ const OrgTorch = new Runnable( function OrgTorch () { + Config.MFLeader = true; let currentGameInfo = null; + /** @type {Player | null} */ + let taxiPlayer = null; + let taxiUp = false; + const OrgTorchData = require("../systems/torch/OrgTorchData"); const portalMode = { MiniUbers: 0, UberTristram: 1 }; - const OrgTorchData = { - filePath: "logs/OrgTorch-" + me.profile + ".json", - _default: { gamename: me.gamename, doneAreas: [] }, - - create: function () { - FileTools.writeText(this.filePath, JSON.stringify(this._default)); - return this._default; - }, - - read: function () { - let obj = {}; - try { - let string = FileTools.readText(this.filePath); - obj = JSON.parse(string); - } catch (e) { - return this._default; - } - - return obj; - }, + function chatEvent (nick, msg) { + if (!nick || !msg) return; + if (msg !== "up") return; - update: function (newData) { - let data = this.read(); - Object.assign(data, newData); - FileTools.writeText(this.filePath, JSON.stringify(data)); - }, + if (!taxiPlayer && String.isEqual(Config.OrgTorch.TaxiChar, nick)) { + taxiPlayer = Misc.findPlayer(nick); + } - remove: function () { - return FileTools.remove(this.filePath); + if (taxiPlayer && taxiPlayer.name === nick) { + taxiUp = true; + console.log("ÿc7OrgTorch :: Taxi is up"); } - }; + } /** * @param {ItemUnit} item @@ -111,7 +98,10 @@ const OrgTorch = new Runnable( const lure = function (bossId) { let unit = Game.getMonster(bossId); - if (unit) { + // 25121/5157 - bottom left corner of top left building + // 25141/5175 - top left corner of bottom left building + // 25131/5187 - area we want to lure meph to + if (unit && !unit.dead) { let tick = getTickCount(); while (getTickCount() - tick < 2000) { @@ -131,21 +121,29 @@ const OrgTorch = new Runnable( * @returns {boolean} */ const completeSetCheck = function () { - let [horns, brains, eyes] = [0, 0, 0]; + const organs = new Map([ + [sdk.items.quest.DiablosHorn, 0], + [sdk.items.quest.MephistosBrain, 0], + [sdk.items.quest.BaalsEye, 0] + ]); + /** @param {ItemUnit} item */ + const filterInvo = function (item) { + return item.isInStorage && organs.has(item.classid) && item.normal; + }; + /** @param {ItemUnit} item */ + const countOrgans = function (item) { + if (organs.has(item.classid)) { + organs.set(item.classid, organs.get(item.classid) + 1); + } + }; me.getItemsEx() - .filter(i => i.isInInventory && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) - .forEach(i => { - switch (i.classid) { - case sdk.items.quest.DiablosHorn: - return (horns++); - case sdk.items.quest.MephistosBrain: - return (brains++); - case sdk.items.quest.BaalsEye: - return (eyes++); - default: return 0; - } - }); + .filter(filterInvo) + .forEach(countOrgans); + const horns = organs.get(sdk.items.quest.DiablosHorn); + const brains = organs.get(sdk.items.quest.MephistosBrain); + const eyes = organs.get(sdk.items.quest.BaalsEye); + if (!horns || !brains || !eyes) { return false; } @@ -164,19 +162,18 @@ const OrgTorch = new Runnable( * @todo equipping an item from storage if we have it */ const getFade = function () { - if (Config.OrgTorch.GetFade && !me.getState(sdk.states.Fade) - && me.haveSome([ - { name: sdk.locale.items.Treachery, equipped: true }, - { name: sdk.locale.items.LastWish, equipped: true }, - { name: sdk.locale.items.SpiritWard, equipped: true } - ])) { + if (!Config.OrgTorch.GetFade) return true; + if (me.getState(sdk.states.Fade)) return true; + + // lets figure out what fade item we have before we leave town + const fadeItem = me.findFirst([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ]); + + if (fadeItem.have) { console.log(sdk.colors.Orange + "OrgTorch :: " + sdk.colors.White + "Getting Fade"); - // lets figure out what fade item we have before we leave town - let fadeItem = me.findFirst([ - { name: sdk.locale.items.Treachery, equipped: true }, - { name: sdk.locale.items.LastWish, equipped: true }, - { name: sdk.locale.items.SpiritWard, equipped: true } - ]); Pather.useWaypoint(sdk.areas.RiverofFlame); Precast.doPrecast(true); @@ -185,7 +182,7 @@ const OrgTorch = new Runnable( Pather.moveTo(7811, 5872); - if (fadeItem.have && fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { + if (fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { mainSlot = me.weaponswitch; me.switchWeapons(sdk.player.slot.Secondary); } @@ -279,20 +276,24 @@ const OrgTorch = new Runnable( Precast.doPrecast(true); - let nodes = [ - { x: 20196, y: 8694 }, - { x: 20308, y: 8588 }, - { x: 20187, y: 8639 }, - { x: 20100, y: 8550 }, - { x: 20103, y: 8688 }, - { x: 20144, y: 8709 }, - { x: 20263, y: 8811 }, - { x: 20247, y: 8665 }, + const nodes = [ + new PathNode(20196, 8694), + new PathNode(20308, 8588), + new PathNode(20187, 8639), + new PathNode(20100, 8550), + new PathNode(20103, 8688), + new PathNode(20144, 8709), + new PathNode(20263, 8811), + new PathNode(20247, 8665), ]; + const foundDuriel = function () { + return !!Game.getMonster(sdk.monsters.UberDuriel); + }; + try { - for (let i = 0; i < nodes.length; i++) { - Pather.moveTo(nodes[i].x, nodes[i].y); + for (let node of nodes) { + Pather.move(node, { callback: foundDuriel }); delay(500); if (Game.getMonster(sdk.monsters.UberDuriel)) { @@ -321,8 +322,15 @@ const OrgTorch = new Runnable( const furnance = function () { let mBrain = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length; + const foundIzual = function () { + return !!Game.getMonster(sdk.monsters.UberIzual); + }; Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.FurnaceofPain, sdk.unittype.Object, sdk.objects.SmallSparklyChest, 2, 2); + Pather.moveToPresetObject( + sdk.areas.FurnaceofPain, + sdk.objects.SmallSparklyChest, + { offX: 2, offY: 2, callback: foundIzual } + ); Attack.kill(sdk.monsters.UberIzual); Pickit.pickItems(); getQuestItem(Game.getItem(sdk.items.quest.MephistosBrain)); @@ -337,19 +345,29 @@ const OrgTorch = new Runnable( */ const uberTrist = function () { let skillBackup; - let useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); + const useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); Pather.moveTo(25068, 5078); Precast.doPrecast(true); - let nodes = [ - { x: 25040, y: 5101 }, - { x: 25040, y: 5166 }, - { x: 25122, y: 5170 }, + const nodes = [ + new PathNode(25040, 5101), + new PathNode(25040, 5166), + new PathNode(25131, 5187), + new PathNode(25122, 5170), ]; - for (let i = 0; i < nodes.length; i++) { - Pather.moveTo(nodes[i].x, nodes[i].y); + for (let node of nodes) { + Pather.move(node, { callback: function () { + let meph = Game.getMonster(sdk.monsters.UberMephisto); + let diablo = Game.getMonster(sdk.monsters.UberDiablo); + let baal = Game.getMonster(sdk.monsters.UberBaal); + return ( + (meph && meph.distance < 10) + || (diablo && diablo.distance < 10) + || (baal && baal.distance < 10) + ); + } }); } useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); @@ -431,19 +449,40 @@ const OrgTorch = new Runnable( * @param {ObjectUnit} portal */ const runEvent = function (portal) { - if (portal) { - const { Antidote, Thawing } = Config.OrgTorch.PreGame; - if (Antidote.At.includes(portal.objtype) && Antidote.Drink > 0) { - Town.buyPots(Antidote.Drink, "Antidote", true, true); - } - if (Thawing.At.includes(portal.objtype) && Thawing.Drink > 0) { - Town.buyPots(Thawing.Drink, "Thawing", true, true); + if (!portal) return; + const portalArea = portal.objtype; + const { Antidote, Thawing } = Config.OrgTorch.PreGame; + if (Antidote.At.includes(portal.objtype) && Antidote.Drink > 0) { + Town.buyPots(Antidote.Drink, "Antidote", true, true); + } + if (Thawing.At.includes(portal.objtype) && Thawing.Drink > 0) { + Town.buyPots(Thawing.Drink, "Thawing", true, true); + } + say("Starting " + portal.objtype); + + if (Config.OrgTorch.TaxiChar) { + Town.move("portalspot"); + + const taxiReady = function () { + return taxiUp && Pather.usePortal(portalArea, taxiPlayer.name); + }; + + if (Misc.poll(taxiReady, Time.minutes(1), 1000)) { + taxiUp = false; + console.log("taking portal: " + portalArea); + pandemoniumRun(portalArea); + + return; + } else { + console.log("OrgTorch :: Taxi not ready, taking red portal."); + Town.move("stash"); } + } else { Town.move("stash"); - console.log("taking portal: " + portal.objtype); - Pather.usePortal(null, null, portal); - pandemoniumRun(portal.objtype); } + console.log("taking portal: " + portal.objtype); + Pather.usePortal(null, null, portal); + pandemoniumRun(portal.objtype); }; // ################# // @@ -453,35 +492,42 @@ const OrgTorch = new Runnable( // make sure we are picking the organs Config.PickitFiles.length === 0 && NTIP.OpenFile("pickit/keyorg.nip", true); - FileTools.exists(OrgTorchData.filePath) && (currentGameInfo = OrgTorchData.read()); + OrgTorchData.exists() && (currentGameInfo = OrgTorchData.read()); if (!currentGameInfo || currentGameInfo.gamename !== me.gamename) { currentGameInfo = OrgTorchData.create(); } let portal; - let [tkeys, hkeys, dkeys] = [0, 0, 0]; - let [brains, eyes, horns] = [0, 0, 0]; - - me.getItemsEx() - .filter(i => i.isInStorage && !Town.ignoreType(i.itemType) && i.quality === sdk.items.quality.Normal) - .forEach(i => { - switch (i.classid) { - case sdk.items.quest.KeyofTerror: - return (tkeys++); - case sdk.items.quest.KeyofHate: - return (hkeys++); - case sdk.items.quest.KeyofDestruction: - return (dkeys++); - case sdk.items.quest.DiablosHorn: - return (horns++); - case sdk.items.quest.MephistosBrain: - return (brains++); - case sdk.items.quest.BaalsEye: - return (eyes++); - default: return 0; + + const ingredients = new Map([ + [sdk.items.quest.KeyofTerror, 0], + [sdk.items.quest.KeyofHate, 0], + [sdk.items.quest.KeyofDestruction, 0], + [sdk.items.quest.DiablosHorn, 0], + [sdk.items.quest.MephistosBrain, 0], + [sdk.items.quest.BaalsEye, 0] + ]); + + const checkIngredients = function () { + /** @param {ItemUnit} item */ + const filterIngredients = function (item) { + return item.isInStorage && ingredients.has(item.classid) && item.normal; + }; + /** @param {ItemUnit} item */ + const countIngredients = function (item) { + if (ingredients.has(item.classid)) { + ingredients.set(item.classid, ingredients.get(item.classid) + 1); } - }); + }; + + me.getItemsEx() + .filter(filterIngredients) + .forEach(countIngredients); + }; + + // Initialize our ingredients map + checkIngredients(); // Do town chores and quit if MakeTorch is true and we have a torch. checkTorch(); @@ -492,11 +538,16 @@ const OrgTorch = new Runnable( Town.goToTown(5); Town.move("stash"); - let redPortals = getUnits(sdk.unittype.Object, sdk.objects.RedPortal) - .filter(el => [ - sdk.areas.MatronsDen, sdk.areas.ForgottenSands, - sdk.areas.FurnaceofPain, sdk.areas.UberTristram - ].includes(el.objtype)); + const uberPortals = [ + sdk.areas.MatronsDen, + sdk.areas.ForgottenSands, + sdk.areas.FurnaceofPain, + sdk.areas.UberTristram + ]; + const redPortals = getUnits(sdk.unittype.Object, sdk.objects.RedPortal) + .filter(function (el) { + return uberPortals.includes(el.objtype); + }); let miniPortals = 0; let keySetsReq = 3; let tristOpen = false; @@ -515,9 +566,24 @@ const OrgTorch = new Runnable( currentGameInfo.doneAreas.length > 0 && (currentGameInfo = OrgTorchData.create()); } + /** + * @param {number} req + * @returns {boolean} + */ + const validKeyCount = function (req) { + return ingredients.get(sdk.items.quest.KeyofTerror) >= req + && ingredients.get(sdk.items.quest.KeyofHate) >= req + && ingredients.get(sdk.items.quest.KeyofDestruction) >= req; + }; + + const validOrganCount = function () { + return ingredients.get(sdk.items.quest.DiablosHorn) >= 1 + && ingredients.get(sdk.items.quest.MephistosBrain) >= 1 + && ingredients.get(sdk.items.quest.BaalsEye) >= 1; + }; + // End the script if we don't have enough keys nor organs - if ((tkeys < keySetsReq || hkeys < keySetsReq || dkeys < keySetsReq) - && (brains < 1 || eyes < 1 || horns < 1) && !tristOpen) { + if (!validKeyCount(keySetsReq) && !validOrganCount() && !tristOpen) { console.log("Not enough keys or organs."); OrgTorchData.remove(); @@ -527,19 +593,28 @@ const OrgTorch = new Runnable( Config.UseMerc = false; // We have enough keys, do mini ubers - if (tkeys >= keySetsReq && hkeys >= keySetsReq && dkeys >= keySetsReq) { + if (validKeyCount(keySetsReq)) { getFade(); Town.goToTown(5); console.log("Making organs."); D2Bot.printToConsole("OrgTorch: Making organs.", sdk.colors.D2Bot.DarkGold); Town.move("stash"); + if (Config.OrgTorch.TaxiChar) { + addEventListener("chatmsg", chatEvent); + + if (!taxiPlayer) { + taxiPlayer = Misc.findPlayer(Config.OrgTorch.TaxiChar); + } + } + // there are already open portals lets check our info on them if (miniPortals > 0) { for (let i = 0; i < miniPortals; i++) { // mini-portal is up but its not in our done areas, probably chickend during it, lets try again if ([sdk.areas.MatronsDen, sdk.areas.ForgottenSands, sdk.areas.FurnaceofPain].includes(redPortals[i].objtype) - && !currentGameInfo.doneAreas.includes(redPortals[i].objtype)) { + && !currentGameInfo.doneAreas.includes(redPortals[i].objtype) + ) { portal = redPortals[i]; runEvent(portal); } @@ -566,13 +641,11 @@ const OrgTorch = new Runnable( } // Count organs - brains = me.findItems("mbr", sdk.items.mode.inStorage).length || 0; - eyes = me.findItems("bey", sdk.items.mode.inStorage).length || 0; - horns = me.findItems("dhn", sdk.items.mode.inStorage).length || 0; + checkIngredients(); // We have enough organs, do Tristram - or trist is open we may have chickened and came back so check it // if trist was already open when we joined should we run that first? - if ((brains && eyes && horns) || tristOpen) { + if (validOrganCount() || tristOpen) { getFade(); Town.goToTown(5); Town.move("stash"); diff --git a/d2bs/kolbot/libs/scripts/OrgTorchHelper.js b/d2bs/kolbot/libs/scripts/OrgTorchHelper.js new file mode 100644 index 000000000..806aa13ca --- /dev/null +++ b/d2bs/kolbot/libs/scripts/OrgTorchHelper.js @@ -0,0 +1,444 @@ +/** +* @filename OrgTorchHelper.js +* @author theBGuy +* @desc Run alongside OrgTorch to help with Uber Tristram and Uber bosses +* +*/ + +const OrgTorchHelper = new Runnable( + function OrgTorchHelper () { + // TODO: Temp remove organs from nip so this doesn't interfere with OrgTorch + Config.FindItem = false; + + let quitting = false; + /** @type {Party} */ + let player = null; + /** @type {number} */ + let lastPrecast; + + /** @type {{ task: string, id?: number | string, at: number, area: number }[]} */ + const taskList = []; + const tasks = ["kill", "clear", "quit", "starting"]; + + /** + * @param {string} name + * @param {string} msg + */ + function chatEvent (name, msg) { + if (!msg) return; + const cmd = msg.toLowerCase().split(" "); + const task = cmd.length ? cmd.shift() : ""; + if (!tasks.includes(task)) { + return; + } + const idSplit = cmd.join(" "); + const id = (function () { + try { + return parseInt(idSplit, 10) || idSplit; + } catch (e) { + console.warn(e.message || "Failed to parse id from message split"); + return; + } + })(); + + if (!player) { + // anything else we need to consider here? + player = Misc.findPlayer(name); + } + + if (player && name === player.name) { + if (Config.OrgTorchHelper.SkipTp && me.inArea(player.area)) { + return; + } + if (Config.OrgTorchHelper.Taxi && task !== "starting") { + return; + } + if (Config.OrgTorchHelper.Taxi && id === sdk.areas.UberTristram) { + return; + } + taskList.push({ task: task, id: id, at: getTickCount(), area: player.area }); + } + } + + function handleDeath () { + while (me.mode === sdk.player.mode.Death) { + delay(3); + } + + if (me.hardcore) { + D2Bot.printToConsole( + "(OrgTorchHelper) :: " + me.charname + " has died at level " + + me.charlvl + ". Shutting down profile...", + sdk.colors.D2Bot.Red + ); + D2Bot.stop(); + } + + while (!me.inTown) { + me.revive(); + delay(1000); + } + + Town.move("portalspot"); + console.log("revived!"); + } + + /** + * Get fade in River of Flames - only works if we are wearing an item with ctc Fade + * @returns {boolean} + * @todo equipping an item from storage if we have it + */ + const getFade = function () { + if (!Config.OrgTorchHelper.GetFade) return true; + if (me.getState(sdk.states.Fade)) return true; + + // lets figure out what fade item we have before we leave town + const fadeItem = me.findFirst([ + { name: sdk.locale.items.Treachery, equipped: true }, + { name: sdk.locale.items.LastWish, equipped: true }, + { name: sdk.locale.items.SpiritWard, equipped: true } + ]); + + if (fadeItem.have) { + console.log(sdk.colors.Orange + "OrgTorchHelper :: " + sdk.colors.White + "Getting Fade"); + + Pather.useWaypoint(sdk.areas.RiverofFlame); + Precast.doPrecast(true); + // check if item is on switch + let mainSlot; + + Pather.moveTo(7811, 5872); + + if (fadeItem.item.isOnSwap && me.weaponswitch !== sdk.player.slot.Secondary) { + mainSlot = me.weaponswitch; + me.switchWeapons(sdk.player.slot.Secondary); + } + + Skill.canUse(sdk.skills.Salvation) && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); + + while (!me.getState(sdk.states.Fade)) { + delay(100); + } + + mainSlot !== undefined && me.weaponswitch !== mainSlot && me.switchWeapons(mainSlot); + + console.log(sdk.colors.Orange + "OrgTorchHelper :: " + sdk.colors.Green + "Fade Achieved"); + } + + return true; + }; + + const portalUp = function () { + return Config.OrgTorchHelper.Helper ? Pather.makePortal() : Town.goToTown(); + }; + + const matronsDen = function () { + Precast.doPrecast(true); + Pather.moveToPresetObject( + sdk.areas.MatronsDen, + sdk.objects.SmallSparklyChest, + { offX: 2, offY: 2, useWalkPath: Config.OrgTorchHelper.UseWalkPath, } + ); + + if (Config.OrgTorchHelper.Taxi) { + portalUp() && say("up"); + + if (!Config.OrgTorchHelper.Helper) { + return; + } + } + // TODO: allow callback to end clearing - in this case stop if lilith is dead + Attack.clear(25, sdk.monsters.spectype.All, sdk.monsters.Lilith); + Pickit.pickItems(); + Town.goToTown(); + }; + + const forgottenSands = function () { + Precast.doPrecast(true); + + const nodes = [ + new PathNode(20196, 8694), + new PathNode(20308, 8588), + new PathNode(20187, 8639), + new PathNode(20100, 8550), + new PathNode(20103, 8688), + new PathNode(20144, 8709), + new PathNode(20263, 8811), + new PathNode(20247, 8665), + ]; + + /** @type {Monster | null} */ + let dury = null; + const foundDuriel = function () { + dury = Game.getMonster(sdk.monsters.UberDuriel); + return !!dury; + }; + + try { + for (let node of nodes) { + Pather.move(node, { useWalkPath: Config.OrgTorchHelper.UseWalkPath, callback: foundDuriel }); + delay(500); + + if (foundDuriel()) { + break; + } + } + + if (Config.OrgTorchHelper.Taxi) { + dury && Pather.move(dury); + portalUp() && say("up"); + + if (!Config.OrgTorchHelper.Helper) { + return; + } + } + Attack.clear(25, sdk.monsters.spectype.All, sdk.monsters.UberDuriel); + Pickit.pickItems(); + Town.goToTown(); + } catch (e) { + // + } + }; + + const furnance = function () { + Precast.doPrecast(true); + Pather.moveToPresetObject( + sdk.areas.FurnaceofPain, + sdk.objects.SmallSparklyChest, + { offX: 2, offY: 2, useWalkPath: Config.OrgTorchHelper.UseWalkPath, } + ); + + if (Config.OrgTorchHelper.Taxi) { + portalUp() && say("up"); + + if (!Config.OrgTorchHelper.Helper) { + return; + } + } + Attack.clear(25, sdk.monsters.spectype.All, sdk.monsters.UberIzual); + Pickit.pickItems(); + Town.goToTown(); + }; + + /** + * Try to lure a monster - wait until it's close enough + * @param {number} bossId + * @returns {boolean} + * @todo redo this + * - should, lure boss AWAY from the others and to us + * - create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses + */ + const lure = function (bossId) { + let unit = Game.getMonster(bossId); + + if (unit) { + let tick = getTickCount(); + + while (getTickCount() - tick < 2000) { + if (unit.distance <= 10) { + return true; + } + + delay(50); + } + } + + return false; + }; + + const uberTrist = function () { + Config.MercWatch = false; + + Pather.moveTo(25068, 5078); + Precast.doPrecast(true); + + let nodes = [ + new PathNode(25040, 5101), + new PathNode(25040, 5166), + new PathNode(25122, 5170), + ]; + + for (let node of nodes) { + Pather.move(node); + } + + lure(sdk.monsters.UberMephisto); + Pather.moveTo(25129, 5198); + lure(sdk.monsters.UberMephisto); + + if (!Game.getMonster(sdk.monsters.UberMephisto)) { + Pather.moveTo(25122, 5170); + } + + Attack.clear(15, sdk.monsters.spectype.All, sdk.monsters.UberMephisto); + + Pather.moveTo(25162, 5141); + delay(3250); + + if (!Game.getMonster(sdk.monsters.UberDiablo)) { + Pather.moveTo(25122, 5170); + } + + Attack.clear(15, sdk.monsters.spectype.All, sdk.monsters.UberDiablo); + + if (!Game.getMonster(sdk.monsters.UberBaal)) { + Pather.moveTo(25122, 5170); + } + + Attack.clear(15, sdk.monsters.spectype.All, sdk.monsters.UberBaal); + Pather.moveTo(25105, 5140); + Pather.usePortal(sdk.areas.Harrogath); + }; + + const useLeaderPortal = function () { + return Pather.usePortal(null, player.name); + }; + + addEventListener("chatmsg", chatEvent); + + // ################# // + /* ##### START ##### */ + // ################# // + + getFade(); + Town.doChores(); + Town.goToTown(5); + Town.move("portalspot"); + + if (Config.Leader) { + if (!Misc.poll(() => Misc.inMyParty(Config.Leader), 30e4, 1000)) { + throw new Error("MFHelper: Leader not partied"); + } + + player = Misc.findPlayer(Config.Leader); + } + + // START + while (!quitting) { + if (me.dead) { + handleDeath(); + } + + if (player) { + if (me.needHealing() && Town.heal()) { + Town.move("portalspot"); + } + + if (taskList.length) { + console.debug("Leader area :: " + player.area); + + if (taskList[0].task === "quit") return true; + // check if any message is telling us to quit + if (taskList.find(el => el.task === "quit")) return true; + + // handled pre-reqs, now perform normal checks + let { task, id, at, area } = taskList.shift(); + + if (!id) { + console.warn("OrgTorchHelper :: No id found in taskList, skipping task " + task); + continue; + } + + if ((task === "kill" || task === "clear" && Attack._killed.has(id))) { + continue; + } + + // alright first lets check how long its been since the command was given + // this probably needs to be adjusted but for now 3 minutes on any of theses tasks is probably too long + if (getTickCount() - at > Time.minutes(3)) continue; + + /** + * @todo still think this section needs to be done better, we are using a snapshot of the player's area at the time + * of the message but sometimes the area hasn't been updated yet, causing us to do dumb things like attempt to kill + * while still in town. We can't just use the players area though because of towncheck/chicken. Feel like best solution + * would be adding area into leaders message and just always parsing it from there + */ + if (me.area !== area) { + !me.inTown && Town.goToTown(); + + if (me.act !== sdk.areas.actOf(area)) { + Town.goToTown(sdk.areas.actOf(area)); + Town.move("portalspot"); + } + + if (player.area !== area && !player.inTown) { + area = player.area; + } + + try { + Misc.poll(useLeaderPortal, Time.seconds(15), 500 + me.ping); + } catch (e) { + console.warn(e.message || "Failed to take leader portal"); + continue; + } + } + + if (!me.inTown && me.area === area) { + let forceCast = false; + if (!lastPrecast || getTickCount() - lastPrecast > Time.minutes(2)) { + (forceCast = true) && (lastPrecast = getTickCount()); + } + Precast.doPrecast(forceCast); + } else if (!me.inTown && !me.inArea(player.area)) { + Town.goToTown(5); + continue; + } + + switch (task) { + case "starting": + console.log("ÿc4OrgTorchHelperÿc0: Starting " + id); + + if (!Config.OrgTorchHelper.SkipTp && id === sdk.areas.UberTristram) { + continue; + } + + Pather.usePortal(id); + + if (me.inArea(sdk.areas.MatronsDen)) { + matronsDen(); + } else if (me.inArea(sdk.areas.FurnaceofPain)) { + furnance(); + } else if (me.inArea(sdk.areas.ForgottenSands)) { + forgottenSands(); + } else if (me.inArea(sdk.areas.UberTristram)) { + uberTrist(); + } + + break; + case "kill": + console.log("ÿc4OrgTorchHelperÿc0: Kill " + id); + + try { + Attack.kill(id); + } catch (err) { + console.error(err); + } + + break; + case "clear": + console.log("ÿc4OrgTorchHelperÿc0: Clear " + id); + + try { + Attack.clear(15, 0, id); + } catch (err) { + console.error(err); + } + + break; + } + + if (!Pather.getPortal(sdk.areas.townOf(me.act)) || !Pather.usePortal(sdk.areas.townOf(me.act))) { + Town.goToTown(); + } + } + } + + delay(100); + } + + return true; + }, + { + startArea: sdk.areas.Harrogath + } +); diff --git a/d2bs/kolbot/libs/systems/torch/OrgTorchData.js b/d2bs/kolbot/libs/systems/torch/OrgTorchData.js new file mode 100644 index 000000000..b24a89781 --- /dev/null +++ b/d2bs/kolbot/libs/systems/torch/OrgTorchData.js @@ -0,0 +1,68 @@ +/** +* @filename OrgTorchData.js +* @author theBGuy +* @desc Data file handling for OrgTorch.js +* +*/ + +(function (module) { + /** + * @typedef {Object} OrgTorchDataObject + * @property {string} gamename - The name of the game. + * @property {string} gamepassword - The password for the game. + * @property {number} active - The index of the active area. + * @property {Array} doneAreas - An array of completed areas. + */ + + const OrgTorchData = { + _path: "data/" + me.profile + "/orgtorch.json", + /** @type {OrgTorchDataObject} */ + _default: { gamename: "", gamepassword: "", active: -1, doneAreas: [] }, + + exists: function () { + return FileTools.exists(this._path); + }, + + create: function () { + if (!FileTools.exists("data/" + me.profile)) { + let folder = dopen("data"); + folder.create(me.profile); + } + + const obj = Object.assign({}, this._default); + + if (me.gamename) { + obj.gamename = me.gamename; + } + + if (me.gamepassword) { + obj.gamepassword = me.gamepassword; + } + + FileAction.write(this._path, JSON.stringify(obj)); + return obj; + }, + + /** @returns {OrgTorchDataObject} */ + read: function () { + try { + return FileAction.parse(this._path); + } catch (e) { + return this._default; + } + }, + + /** @param {Partial} newData */ + update: function (newData) { + let data = this.read(); + Object.assign(data, newData); + FileTools.writeText(this._path, JSON.stringify(data)); + }, + + remove: function () { + return FileTools.remove(this._path); + } + }; + + module.exports = OrgTorchData; +})(module); diff --git a/d2bs/kolbot/libs/systems/torch/TorchSystem.d.ts b/d2bs/kolbot/libs/systems/torch/TorchSystem.d.ts new file mode 100644 index 000000000..decb82181 --- /dev/null +++ b/d2bs/kolbot/libs/systems/torch/TorchSystem.d.ts @@ -0,0 +1,23 @@ +/// + +declare global { + namespace TorchSystem { + interface FarmerProfile { + KeyFinderProfiles: string[]; + FarmGame: string; + profile?: string; + } + + let FarmerProfiles: { [key: string]: FarmerProfile }; + let inGame: boolean; + let check: boolean; + + function getFarmers(): FarmerProfile[] | false; + function isFarmer(): FarmerProfile | false; + function inGameCheck(): boolean; + function keyCheck(): number[]; + function outOfGameCheck(): boolean; + function waitForKeys(): void; + } +} +export {}; \ No newline at end of file diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index d6eba3548..3632b8b74 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -500,6 +500,13 @@ declare global { }; }; }; + OrgTorchHelper: { + Taxi: boolean; + Helper: boolean; + SkipTp: boolean; + GetFade: boolean; + UseWalkPath: boolean; + }; Synch: { WaitFor: any[]; }; From a6a4539025a82d71bedfeeab1de7711c4e54046f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 5 May 2025 03:24:58 -0400 Subject: [PATCH 522/758] patch: Fix ControlAction.getPermedStatus false positives - Characters who need refreshing also have the expiry text but they are already permed. Handle this by extracting out the actual days, this still has an edge case for characters who need refreshing and get below the 11 day count (default value for new unpermed characters) --- d2bs/kolbot/libs/OOG.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index aee5e7b6c..550e5bd29 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -368,9 +368,20 @@ includeIfNotIncluded("core/Me.js"); if (!control) return false; let text = control.getText(); - if (!Array.isArray(text) || typeof text[1] !== "string") return false; + if (!Array.isArray(text) || typeof text[1] !== "string") { + return false; + } + + let expireText = text.find(el => el.includes(expireStr)); + if (!expireText) { + return true; + } + + let daysMatch = /\d+/.exec(expireText); + let days = daysMatch ? parseInt(daysMatch[0], 10) : 0; - return !text.some(el => el.includes(expireStr)); + // > 11 days means this char has been permed before + return days > 11; }, /** From c1cf9ed002b9949c52a065547613aa40e860c86a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 8 May 2025 16:14:19 -0400 Subject: [PATCH 523/758] Add missing `Config.OrgTorch.TaxiChar` in default configs --- d2bs/kolbot/libs/config/Amazon.js | 1 + d2bs/kolbot/libs/config/Assassin.js | 1 + d2bs/kolbot/libs/config/Barbarian.js | 1 + d2bs/kolbot/libs/config/Druid.js | 1 + d2bs/kolbot/libs/config/Necromancer.js | 1 + d2bs/kolbot/libs/config/Paladin.js | 1 + d2bs/kolbot/libs/config/Sorceress.js | 1 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + 8 files changed, 8 insertions(+) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 3fd65785f..edcea5189 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -287,6 +287,7 @@ function LoadConfig () { Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 832ff4808..aa3960e31 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -287,6 +287,7 @@ function LoadConfig () { Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 4e57c9ace..73159bddb 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -287,6 +287,7 @@ function LoadConfig () { Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index db60274fb..31a42aada 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -287,6 +287,7 @@ function LoadConfig () { Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index d308b7dbc..98afb32c8 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -287,6 +287,7 @@ function LoadConfig () { Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 27d9a8a97..56d7985de 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -287,6 +287,7 @@ function LoadConfig () { Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 28d09bdb2..ca2da77d9 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -287,6 +287,7 @@ function LoadConfig () { Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 81302de3d..ef13d2f65 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -268,6 +268,7 @@ Config.OrgTorch.WaitTimeout = 15; // Time in minutes to wait for keys before moving on Config.OrgTorch.UseSalvation = true; // Use Salvation aura on Mephisto (if possible) Config.OrgTorch.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. + Config.OrgTorch.TaxiChar = ""; // Name of the taxi character running OrgTorchHelper. Config.OrgTorch.PreGame.Antidote.At = [sdk.areas.MatronsDen, sdk.areas.UberTristram]; // Chug x antidotes before each area Config.OrgTorch.PreGame.Antidote.Drink = 10; // Chug x antidotes. Each antidote gives +50 poison res and +10 max poison for 30 seconds. The duration stacks. 10 potions == 5 minutes Config.OrgTorch.PreGame.Thawing.At = [sdk.areas.FurnaceofPain, sdk.areas.UberTristram]; // Chug x thawing pots before each area From b3eb443c842866dd94d31094140f4bc767418c35 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 May 2025 18:44:54 -0400 Subject: [PATCH 524/758] Fix OrgTorch waiting for taxi for Tristram + Cleanup --- d2bs/kolbot/libs/scripts/OrgTorch.js | 79 ++++++++++++++-------- d2bs/kolbot/libs/scripts/OrgTorchHelper.js | 62 +++++++---------- 2 files changed, 73 insertions(+), 68 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/OrgTorch.js b/d2bs/kolbot/libs/scripts/OrgTorch.js index ff40820fc..cbf62dcf1 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorch.js +++ b/d2bs/kolbot/libs/scripts/OrgTorch.js @@ -19,6 +19,18 @@ const OrgTorch = new Runnable( function OrgTorch () { + if (Config.OrgTorch.UseSalvation) { + Config.AdvancedCustomAttack.push({ + check: function (unit) { + return ( + unit.classid === sdk.monsters.UberMephisto + || (me.inArea(sdk.areas.UberTristram) && Game.getMonster(sdk.monsters.UberMephisto)) + ); + }, + attack: [Config.AttackSkill[1], sdk.skills.Salvation] + }); + } + Config.MFLeader = true; let currentGameInfo = null; /** @type {Player | null} */ @@ -31,6 +43,10 @@ const OrgTorch = new Runnable( UberTristram: 1 }; + /** + * @param {string} nick + * @param {string} msg + */ function chatEvent (nick, msg) { if (!nick || !msg) return; if (msg !== "up") return; @@ -71,8 +87,10 @@ const OrgTorch = new Runnable( Town.doChores(); - if (!Config.OrgTorch.MakeTorch) return false; - + if (!Config.OrgTorch.MakeTorch) { + return false; + } + let torch = me.checkItem({ classid: sdk.items.LargeCharm, quality: sdk.items.quality.Unique }); if (torch.have && Pickit.checkItem(torch.item).result === Pickit.Result.WANTED) { @@ -323,7 +341,7 @@ const OrgTorch = new Runnable( let mBrain = me.findItems(sdk.items.quest.MephistosBrain, sdk.items.mode.inStorage).length; const foundIzual = function () { - return !!Game.getMonster(sdk.monsters.UberIzual); + return !!Game.getMonster(sdk.monsters.UberIzual) || Attack.haveKilled(sdk.monsters.UberIzual); }; Precast.doPrecast(true); Pather.moveToPresetObject( @@ -344,17 +362,19 @@ const OrgTorch = new Runnable( * @todo re-write this, lure doesn't always work and other classes can do ubers */ const uberTrist = function () { - let skillBackup; - const useSalvation = Config.OrgTorch.UseSalvation && Skill.canUse(sdk.skills.Salvation); - Pather.moveTo(25068, 5078); + + if (Precast.checkCTA() && !me.getState(sdk.states.BattleOrders) && Misc.getNearbyPlayerCount() === 0) { + Config.UseCta = true; // override + } Precast.doPrecast(true); const nodes = [ new PathNode(25040, 5101), new PathNode(25040, 5166), new PathNode(25131, 5187), - new PathNode(25122, 5170), + // new PathNode(25122, 5170), + new PathNode(25131, 5187), ]; for (let node of nodes) { @@ -370,42 +390,41 @@ const OrgTorch = new Runnable( } }); } - useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - lure(sdk.monsters.UberMephisto); - Pather.moveTo(25129, 5198); - useSalvation && Skill.setSkill(sdk.skills.Salvation, sdk.skills.hand.Right); - lure(sdk.monsters.UberMephisto); + lure(sdk.monsters.UberMephisto, 25131, 5187); if (!Game.getMonster(sdk.monsters.UberMephisto)) { Pather.moveTo(25122, 5170); } - if (useSalvation) { - skillBackup = Config.AttackSkill[2]; - Config.AttackSkill[2] = sdk.skills.Salvation; - - Attack.init(); - } - Attack.kill(sdk.monsters.UberMephisto); - if (skillBackup && useSalvation) { - Config.AttackSkill[2] = skillBackup; - - Attack.init(); - } - - Pather.moveTo(25162, 5141); - delay(3250); + Pather.move(new PathNode(25162, 5141), { callback: function () { + return Attack.haveKilled(sdk.monsters.UberDiablo); + } }); + + const uberDiableCB = function () { + if (Attack.haveKilled(sdk.monsters.UberDiablo)) { + return true; + } + let diablo = Game.getMonster(sdk.monsters.UberDiablo); + return (diablo && diablo.distance < 10) || diablo.dead; + }; + Misc.poll(uberDiableCB, 3250, 50); if (!Game.getMonster(sdk.monsters.UberDiablo)) { - Pather.moveTo(25122, 5170); + Pather.move(new PathNode(25122, 5170), { callback: uberDiableCB }); } Attack.kill(sdk.monsters.UberDiablo); if (!Game.getMonster(sdk.monsters.UberBaal)) { - Pather.moveTo(25122, 5170); + Pather.move(new PathNode(25122, 5170), { callback: function () { + if (Attack.haveKilled(sdk.monsters.UberBaal)) { + return true; + } + let baal = Game.getMonster(sdk.monsters.UberBaal); + return (baal && baal.distance < 10) || baal.dead; + } }); } Attack.kill(sdk.monsters.UberBaal); @@ -460,7 +479,7 @@ const OrgTorch = new Runnable( } say("Starting " + portal.objtype); - if (Config.OrgTorch.TaxiChar) { + if (Config.OrgTorch.TaxiChar && portalArea !== sdk.areas.UberTristram) { Town.move("portalspot"); const taxiReady = function () { diff --git a/d2bs/kolbot/libs/scripts/OrgTorchHelper.js b/d2bs/kolbot/libs/scripts/OrgTorchHelper.js index 806aa13ca..5c1f1d933 100644 --- a/d2bs/kolbot/libs/scripts/OrgTorchHelper.js +++ b/d2bs/kolbot/libs/scripts/OrgTorchHelper.js @@ -5,6 +5,7 @@ * */ + const OrgTorchHelper = new Runnable( function OrgTorchHelper () { // TODO: Temp remove organs from nip so this doesn't interfere with OrgTorch @@ -21,9 +22,9 @@ const OrgTorchHelper = new Runnable( const tasks = ["kill", "clear", "quit", "starting"]; /** - * @param {string} name - * @param {string} msg - */ + * @param {string} name + * @param {string} msg + */ function chatEvent (name, msg) { if (!msg) return; const cmd = msg.toLowerCase().split(" "); @@ -84,10 +85,10 @@ const OrgTorchHelper = new Runnable( } /** - * Get fade in River of Flames - only works if we are wearing an item with ctc Fade - * @returns {boolean} - * @todo equipping an item from storage if we have it - */ + * Get fade in River of Flames - only works if we are wearing an item with ctc Fade + * @returns {boolean} + * @todo equipping an item from storage if we have it + */ const getFade = function () { if (!Config.OrgTorchHelper.GetFade) return true; if (me.getState(sdk.states.Fade)) return true; @@ -219,32 +220,6 @@ const OrgTorchHelper = new Runnable( Pickit.pickItems(); Town.goToTown(); }; - - /** - * Try to lure a monster - wait until it's close enough - * @param {number} bossId - * @returns {boolean} - * @todo redo this - * - should, lure boss AWAY from the others and to us - * - create path to boss, move some -> wait to see if aggroed -> if yes - move back and make sure it follows until its safely away from other bosses - */ - const lure = function (bossId) { - let unit = Game.getMonster(bossId); - - if (unit) { - let tick = getTickCount(); - - while (getTickCount() - tick < 2000) { - if (unit.distance <= 10) { - return true; - } - - delay(50); - } - } - - return false; - }; const uberTrist = function () { Config.MercWatch = false; @@ -252,6 +227,18 @@ const OrgTorchHelper = new Runnable( Pather.moveTo(25068, 5078); Precast.doPrecast(true); + const validIds = [ + sdk.monsters.UberDiablo, + sdk.monsters.UberMephisto, + sdk.monsters.UberBaal + ]; + + // poll for the killer to have started so we don't interfere with lure + Misc.poll(function () { + let killCmd = taskList.find(el => el.task === "kill" && validIds.includes(el.id)); + return killCmd && killCmd.at < getTickCount() - Time.minutes(3); + }, Time.seconds(15), 50); + let nodes = [ new PathNode(25040, 5101), new PathNode(25040, 5166), @@ -262,10 +249,6 @@ const OrgTorchHelper = new Runnable( Pather.move(node); } - lure(sdk.monsters.UberMephisto); - Pather.moveTo(25129, 5198); - lure(sdk.monsters.UberMephisto); - if (!Game.getMonster(sdk.monsters.UberMephisto)) { Pather.moveTo(25122, 5170); } @@ -273,7 +256,10 @@ const OrgTorchHelper = new Runnable( Attack.clear(15, sdk.monsters.spectype.All, sdk.monsters.UberMephisto); Pather.moveTo(25162, 5141); - delay(3250); + Misc.poll(function () { + let diablo = Game.getMonster(sdk.monsters.UberDiablo); + return diablo && (diablo.distance < 10 || diablo.dead); + }, 3250, 50); if (!Game.getMonster(sdk.monsters.UberDiablo)) { Pather.moveTo(25122, 5170); From cef0d3fe085c7a9794ab753ec4766a668d389335 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 May 2025 18:50:00 -0400 Subject: [PATCH 525/758] Fix countdown timer + cleanup GameAction --- d2bs/kolbot/D2BotGameAction.dbj | 8 +- .../libs/systems/gameaction/GameAction.js | 107 +++++++++++------- 2 files changed, 72 insertions(+), 43 deletions(-) diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index 260e50320..aa555eb05 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -156,7 +156,9 @@ const locationAction = (function () { // last char in list if (!charList || !charList.length) { - GameAction.update("done", "GameAction has completed task"); + const loginInfo = GameAction.getLogin(); + const accountInfo = loginInfo.account ? "for account " + loginInfo.account : ""; + GameAction.update("done", "GameAction has completed task " + accountInfo); D2Bot.stop(me.profile, true); delay(5000); return; @@ -180,7 +182,9 @@ const locationAction = (function () { // last char in list if (!charList || !charList.length) { - GameAction.update("done", "GameAction has completed task"); + const loginInfo = GameAction.getLogin(); + const accountInfo = loginInfo.account ? "for account " + loginInfo.account : ""; + GameAction.update("done", "GameAction has completed task " + accountInfo); D2Bot.stop(me.profile, true); delay(5000); return; diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js index 3d762d107..9b7ee030f 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -20,21 +20,33 @@ const GameAction = { task: null, // don't edit init: function (task) { - GameAction.task = JSON.parse(task); + try { + GameAction.task = JSON.parse(task); - if (this.task["data"] && typeof this.task.data === "string") { - this.task.data = JSON.parse(this.task.data); - } + if (this.task["data"] && typeof this.task.data === "string") { + this.task.data = JSON.parse(this.task.data); + } - MuleLogger.LogNames = this.LogNames; - MuleLogger.LogItemLevel = this.LogItemLevel; - MuleLogger.LogEquipped = this.LogEquipped; - MuleLogger.LogMerc = this.LogMerc; - MuleLogger.SaveScreenShot = this.SaveScreenShot; + MuleLogger.LogNames = this.LogNames; + MuleLogger.LogItemLevel = this.LogItemLevel; + MuleLogger.LogEquipped = this.LogEquipped; + MuleLogger.LogMerc = this.LogMerc; + MuleLogger.SaveScreenShot = this.SaveScreenShot; - return true; + return true; + } catch (err) { + console.log("ÿc4GameActionÿc0: Error in init: " + err); + this.update("done", "Error in init: " + err); + D2Bot.stop(); + + return false; + } }, + /** + * @param {string} action + * @param {string | Object} data + */ update: function (action, data) { if (typeof action !== "string") throw new Error("Action must be a string!"); @@ -42,7 +54,14 @@ const GameAction = { D2Bot.printToConsole(data); - let tag = JSON.parse(JSON.stringify(this.task)); // deep copy + let tag = (function () { + try { + return JSON.parse(JSON.stringify(this.task)); // deep copy + } catch (err) { + console.log("ÿc4GameActionÿc0: Error in update: " + err); + return {}; + } + })(); tag.action = action; tag.data = data; D2Bot.setTag(tag); @@ -113,42 +132,47 @@ const GameAction = { }, inGameCheck: function () { - if (getScript("D2BotGameAction.dbj")) { - while (!this["task"]) { - D2Bot.getProfile(); - delay(500); - } + if (!getScript("D2BotGameAction.dbj")) { + return false; + } + + while (!this["task"]) { + D2Bot.getProfile(); + delay(500); + } - switch (this.task.action) { - case "doMule": - MuleLogger.logChar(); + switch (this.task.action) { + case "doMule": + MuleLogger.logChar(); - break; - case "doDrop": - this.dropItems(this.task.data.items); - MuleLogger.logChar(); + break; + case "doDrop": + this.dropItems(this.task.data.items); + MuleLogger.logChar(); - break; - default: - break; - } + break; + default: + break; + } - while ((getTickCount() - me.gamestarttime) < this.IngameTime * 1000) { - delay(1000); - } + while ((getTickCount() - me.gamestarttime) < this.IngameTime * 1000) { + const elapsedMs = getTickCount() - me.gamestarttime; + const totalMs = this.IngameTime * 1000; + const remainingSeconds = Math.round((totalMs - elapsedMs) / 1000); + + me.overhead("Stalling for " + remainingSeconds + " Seconds"); + delay(1000); + } - try { - quit(); - } finally { - while (me.ingame) { - delay(100); - } + try { + quit(); + } finally { + while (me.ingame) { + delay(100); } - - return true; } - return false; + return true; }, load: function (hash) { @@ -185,7 +209,8 @@ const GameAction = { continue; } - let info = droplist[i].itemid.split(":"); //":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y; + //unit.gid ":" + unit.classid + ":" + unit.location + ":" + unit.x + ":" + unit.y; + let info = droplist[i].itemid.split(":"); let classid = info[1]; let loc = info[2]; @@ -193,7 +218,7 @@ const GameAction = { let unitY = info[4]; // for debug purposes - print("classid: " + classid + " location: " + loc + " X: " + unitX + " Y: " + unitY); + console.log("classid: " + classid + " location: " + loc + " X: " + unitX + " Y: " + unitY); for (let j = 0; j < items.length; j += 1) { if (items[j].classid.toString() === classid From d0eefd1edadff67f13b4e0a29266551b4bc161a4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 17 May 2025 18:53:43 -0400 Subject: [PATCH 526/758] Update Attack.clearEx for more granular control lifecycle callbacks - Add `onLoop`, `onCleared`, and `earlyExit` callbacks - Check if bossid is in our already killed set - Fix attempting to make portal in Uber Tristram --- d2bs/kolbot/libs/core/Attack.js | 80 +++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index ff4c88065..8cc8f4088 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -514,7 +514,9 @@ const Attack = { * @property {(a: T, b: T) => number} sortfunc * @property {boolean} pickit * @property {(unit: Monster) => boolean} filter - * @property {() => any} callback + * @property {() => any} onLoop - Called on each iteration of the main loop + * @property {() => boolean} earlyExit - If returns true, exit the clearing loop. Called on each iteration of the main loop + * @property {() => void} onCleared - Called after all clearing is complete */ /** @@ -524,24 +526,39 @@ const Attack = { * @returns {boolean} */ clearEx: function (range, opts = {}) { + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { + return false; + } + + if (typeof (range) !== "number") { + throw new Error("Attack.clear: range must be a number."); + } + while (!me.gameReady) { delay(40); } - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) return false; range === undefined && (range = 25); - if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); - const settings = Object.assign({ - spectype: 0, - bossId: false, + const { spectype, bossId, sortfunc, filter, onLoop, pickit, earlyExit, onCleared } = Object.assign({ + spectype: sdk.monsters.spectype.All, + bossId: undefined, sortfunc: Attack.sortMonsters, pickit: true, - filter: false, - callback: false, + filter: undefined, + onLoop: undefined, + earlyExit: undefined, + onCleared: undefined, }, opts); - const { spectype, bossId, sortfunc, pickit, filter, callback } = settings; + /** + * @param {unknown} unit + * @returns {boolean} + */ + const isMonsterUnit = function (unit) { + return unit && typeof unit === "object" && unit.type === sdk.unittype.Monster; + }; + /** @type {Map mon.gid === boss.gid)) { + console.log("Adding boss to monsterList"); + monsterList.push(copyUnit(boss)); + } + while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { if (me.dead) return false; - if (typeof callback === "function") callback(); + if (typeof onLoop === "function") { + onLoop(); + } + + if (typeof earlyExit === "function" && earlyExit()) { + console.log("ÿc7Cleared ÿc0:: Early exit condition met"); + break; + } boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); monsterList.sort(sortfunc); target = Game.getMonster(-1, -1, monsterList[0].gid); - if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range - || (this.getScarinessLevel(target) > 7 && target.distance <= range)) - && target.attackable) { + if ( + (target && target.x !== undefined) + && ( + getDistance(target, orgx, orgy) <= range + || (this.getScarinessLevel(target) > 7 && target.distance <= range) + ) + && target.attackable + ) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); tick = getTickCount(); @@ -615,12 +656,10 @@ const Attack = { logged = true; console.log("ÿc7Clear ÿc0:: " + (!!target.name ? target.name : bossId)); } - // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); let _currMon = attacks.get(target.gid); const checkAttackSkill = (!!_currMon && _currMon.attacks % 15 === 0); const result = ClassAttack.doAttack(target, checkAttackSkill); - // let result = ClassAttack.doAttack(target, attackCount % 15 === 0); if (result) { retry = 0; @@ -722,7 +761,7 @@ const Attack = { if (attackCount > 0) { ClassAttack.afterAttack(pickit); - this.openChests(range, orgx, orgy); + Attack.openChests(range, orgx, orgy); pickit && 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 @@ -740,6 +779,10 @@ const Attack = { } } + if (typeof onCleared === "function") { + onCleared(); + } + return true; }, @@ -797,7 +840,8 @@ const Attack = { // mfhelper is disabled for these scripts so announcing is pointless && !Loader.scriptName(0).toLowerCase().includes("diablo") && !Loader.scriptName(0).toLowerCase().includes("baal") - && Pather.makePortal()) { + // bypass UberTristram check, we can't make a portal there + && (me.inArea(sdk.areas.UberTristram) || Pather.makePortal())) { say("clear " + (["number", "string"].includes(typeof bossId) ? bossId : bossId.name)); } } else { From 4c22bf82de912c20c465d259644e62e4c261e102 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 23 May 2025 17:39:57 -0400 Subject: [PATCH 527/758] Update ControlBot ngvote, playertracking, msg randomization - Added randomization to the messages to reduce temp mutes - Consolidated chanttracker into playertracker - Fixed attempting to chant minions that are not part of our party - Fixed failing to find wirt - Moved ngVote into background worker - Don't accept new commands after announcing nextGame - Add `malus` as a alias to `smith` - Increased the final delay before breaking loop to give time for final messages to be processed --- d2bs/kolbot/libs/scripts/ControlBot.js | 221 ++++++++++++++++++------- 1 file changed, 162 insertions(+), 59 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 439e0ef77..84bf106f4 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -9,6 +9,64 @@ const ControlBot = new Runnable( function ControlBot () { + const thankYouMessages = [ + "Ty {name}. Current count: {stats}", + "Got your vote, {name}! The tally is now {stats}", + "Vote recorded, {name}. Current standings: {stats}", + "Thanks {name}, I've counted your vote. Current count: {stats}", + "{name}'s vote has been tallied. Updated count: {stats}", + "Roger that {name}, vote recorded. Current status: {stats}" + ]; + + const voteCompletionMessages = [ + "Thank you {name}, that settles it. Time to tally the votes.", + "And {name} makes it unanimous! Tallying votes now.", + "That's everyone! Thanks {name} for the final vote. Let's count them up.", + "With {name}'s vote, we're all accounted for. Time for the results.", + "Last vote in from {name}! Let's see where we stand.", + "Decision time! {name} has cast the final vote. Counting now." + ]; + + const alreadyCountedMessages = [ + "Your vote has already been counted", + "I've already recorded your vote", + "You've voted already, no need to vote again", + "One vote per person, yours is already counted", + "Vote already registered, thanks!", + "I remember your vote, no need to repeat" + ]; + + const voteRequestMessages = [ + "{players} please cast your vote. Voting ends in {time}s", + "Still waiting on votes from {players}. {time} seconds remaining", + "Don't forget to vote {players}! Time remaining: {time}s", + "{players}, we need your vote! {time} seconds left to decide", + "Hey {players}, make your voice heard! {time}s left to vote", + "{time} seconds left and we're still waiting on {players} to vote" + ]; + + const queuePositionMessages = [ + "{command} has been added to the queue. Position: {position}", + "Added {command} to queue. You're #{position} in line", + "Request for {command} queued. Current position: {position}", + "I'll get to your {command} request soon. Queue position: {position}", + "You're #{position} in the queue for {command}", + "{command} added. There are {position} requests ahead of you" + ]; + + const currentlyRunningMessages = [ + "Currently running {command} for {nick}", + "Right now I'm helping {nick} with {command}", + "Busy with {command} for {nick} at the moment", + "Working on {command} with {nick} now", + "{nick}'s {command} request is in progress" + ]; + + const stillRunningMessages = [ + "Still processing your {command} request right now", + // come up with others + ]; + // Quests const { log, @@ -138,6 +196,9 @@ const ControlBot = new Runnable( AutoRush.playersOut = "out"; AutoRush.allIn = "all in"; + // TODO: Handle multi's abusing the vote by having multiple accounts in the game + // most multi's use similar names so we can check for that then need to update votes needed based on that + // since their vote should only count as 1 /** @typedef {"yes" | "no" | "undecided"} NgVote */ const ngVote = { /** @type {Map} */ @@ -225,7 +286,7 @@ const ControlBot = new Runnable( * @param {boolean} skipUndecided * @returns {boolean} */ - checkCount: function(skipUndecided = false) { + checkCount: function (skipUndecided = false) { let undecided = []; if (!skipUndecided) { @@ -238,7 +299,10 @@ const ControlBot = new Runnable( if (undecided.length) { if (getTickCount() - ngVote.undecidedAskTick > Time.seconds(30)) { let votingPeriodRemaining = Math.round(Time.toSeconds(Time.minutes(2) - ngVote.elapsed())); - Chat.say(undecided.join(", ") + " please cast your vote. Vote ends in " + votingPeriodRemaining + "s"); + let message = voteRequestMessages.random() + .replace("{players}", undecided.join(", ")) + .replace("{time}", votingPeriodRemaining); + Chat.say(message); ngVote.undecidedAskTick = getTickCount(); } return false; @@ -378,7 +442,7 @@ const ControlBot = new Runnable( // check every 1 second tick = getTickCount() + Time.seconds(1) + rand(500, 950); - for (let [key, player] of cmdNicks) { + for (let [key, player] of playerTracker) { if (getTickCount() - player.ignoredAt > Time.minutes(1)) { if (!player.ignored) continue; let party = getParty(key); @@ -394,6 +458,29 @@ const ControlBot = new Runnable( return true; }; })(); + + Worker.runInBackground.ngVote = (function () { + let tick = getTickCount(); + + return function () { + if (getTickCount() - tick < 0) return true; + // check every 1 second + tick = getTickCount() + Time.seconds(1); + if (!ngVote.active) return true; + + if (ngVote.elapsed() > Time.minutes(2)) { + ngVote.checkCount(true); + if (!ngVote.nextGame) { + Chat.say("Not enough votes to start ng." + ngVote.stats()); + ngVote.reset(); + } + } else if (ngVote.elapsed() > Time.seconds(30) && !ngVote.nextGame) { + ngVote.checkCount(); + } + + return true; + }; + })(); /** @constructor */ function PlayerTracker () { @@ -403,6 +490,7 @@ const ControlBot = new Runnable( this.ignoredAt = 0; this.toldToChill = false; this.seenHelpMsg = false; + this.lastChant = 0; } PlayerTracker.prototype.resetCmds = function () { @@ -429,16 +517,11 @@ const ControlBot = new Runnable( this.commands = 0; }; - /** @constructor */ - function ChantTracker () { - this.lastChant = getTickCount(); - } - - ChantTracker.prototype.reChant = function () { + PlayerTracker.prototype.reChant = function () { return getTickCount() - this.lastChant >= chantDuration - Time.minutes(1); }; - ChantTracker.prototype.update = function () { + PlayerTracker.prototype.updateChantTracker = function () { this.lastChant = getTickCount(); }; @@ -464,13 +547,11 @@ const ControlBot = new Runnable( }; /** @type {Map} */ - const cmdNicks = new Map(); + const playerTracker = new Map(); /** @type {Map} */ const wpNicks = new Map(); /** @type {Array} */ const greet = []; - /** @type {Map} */ const wps = new Map([ @@ -552,9 +633,10 @@ const ControlBot = new Runnable( } Packet.enchant(unit); if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chantList.has(unit.name) - ? chantList.get(unit.name).update() - : chantList.set(unit.name, new ChantTracker()); + if (!playerTracker.has(unit.name)) { + playerTracker.set(unit.name, new PlayerTracker()); + } + playerTracker.get(unit.name).updateChantTracker(); } } } while (unit.getNext()); @@ -672,13 +754,15 @@ const ControlBot = new Runnable( if (!Misc.inMyParty(unit.name) || unit.distance > 40) continue; // allow rechanting someone if it's going to run out soon for them if (!unit.getState(sdk.states.Enchant) - || (chantList.has(unit.name) && chantList.get(unit.name).reChant())) { + || (playerTracker.has(unit.name) && playerTracker.get(unit.name).reChant()) + ) { Packet.enchant(unit); if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { chanted.push(unit.name); - chantList.has(unit.name) - ? chantList.get(unit.name).update() - : chantList.set(unit.name, new ChantTracker()); + if (!playerTracker.has(unit.name)) { + playerTracker.set(unit.name, new PlayerTracker()); + } + playerTracker.get(unit.name).updateChantTracker(); } } } catch (err) { @@ -693,7 +777,8 @@ const ControlBot = new Runnable( do { try { if (unit.getParent() - && chantList.has(unit.getParent().name) + && Misc.inMyParty(unit.getParent().name) + && playerTracker.has(unit.getParent().name) && !unit.getState(sdk.states.Enchant) && unit.distance <= 40) { Packet.enchant(unit); @@ -750,9 +835,11 @@ const ControlBot = new Runnable( let wirt = Game.getObject(sdk.quest.chest.Wirt); - for (let i = 0; i < 8; i += 1) { - wirt.interact(); - delay(500); + for (let i = 0; i < 8; i++) { + if (wirt) { + wirt.interact(); + delay(500); + } leg = Game.getItem(sdk.quest.item.WirtsLeg); @@ -1078,12 +1165,12 @@ const ControlBot = new Runnable( // ignore messages not related to our commands if (!actions.has(cmd.toLowerCase())) return false; - if (!cmdNicks.has(nick)) { - cmdNicks.set(nick, new PlayerTracker()); + if (!playerTracker.has(nick)) { + playerTracker.set(nick, new PlayerTracker()); } - const player = cmdNicks.get(nick); + const player = playerTracker.get(nick); - // with new flooder worker we shouldn't get here unless it failed but handle it anyway + // with new flooder worker we shouldn't get here unless it failed if (player.ignored) { return true; } @@ -1355,13 +1442,13 @@ const ControlBot = new Runnable( } else { if (running.nick === nick && running.command === chatCmd) { console.debug("Command already running. active ", running); - if (cmdNicks.get(nick).toldToChill) return; + if (playerTracker.get(nick).toldToChill) return; if (running.command === "wps") { Chat.whisper(nick, "chill I'm already running wps if you want me to stop type stop"); } else { Chat.whisper(nick, "chill I've already started. spamming doesn't make this go faster"); } - cmdNicks.get(nick).toldToChill = true; + playerTracker.get(nick).toldToChill = true; return; } if (actions.get(chatCmd).desc.toLowerCase().includes("rush")) { @@ -1377,6 +1464,12 @@ const ControlBot = new Runnable( return; } } + + if (ngVote.nextGame && running.command) { + Chat.say("Not accepting new commands, ngvote passed. ng will be made after I finish " + running.command); + + return; + } let index = queue.findIndex(function (cmd) { return cmd[0] === chatCmd && cmd[1] === nick; }); @@ -1384,11 +1477,16 @@ const ControlBot = new Runnable( Chat.whisper(nick, "You already requested this command. Queue position: " + (index + 1)); } else { queue.push([chatCmd, nick, full]); - console.log(queue); if (queue.length > 1 || running.nick !== "") { - Chat.whisper(nick, chatCmd + " has been added to the queue. position: " + (queue.length + 1)); + let queueMessage = queuePositionMessages.random() + .replace(/{command}/g, chatCmd) + .replace(/{position}/g, queue.length + 1); + Chat.whisper(nick, queueMessage); if (running.command) { - Chat.say("currently running " + running.command + " for " + running.nick); + let runningMessage = (nick === running.nick ? stillRunningMessages : currentlyRunningMessages).random() + .replace(/{command}/g, running.command) + .replace(/{nick}/g, running.nick); + Chat.say(runningMessage); } } } @@ -1411,6 +1509,9 @@ const ControlBot = new Runnable( if (name1 && ngVote.active) { ngVote.votes.set(name1, "undecided"); } + if (name1 && !playerTracker.has(name1)) { + playerTracker.set(name1, new PlayerTracker()); + } if (name2) { players.set(name1, "*" + name2); } else { @@ -1510,8 +1611,8 @@ const ControlBot = new Runnable( }); str.length && msg.push(str); - !cmdNicks.has(nick) && cmdNicks.set(nick, new PlayerTracker()); - if (cmdNicks.has(nick) && cmdNicks.get(nick).seenHelpMsg) { + !playerTracker.has(nick) && playerTracker.set(nick, new PlayerTracker()); + if (playerTracker.has(nick) && playerTracker.get(nick).seenHelpMsg) { Chat.message(nick, "You have seen the help menu before this game please refer to message log"); } else { msg.forEach(function (m) { @@ -1519,7 +1620,7 @@ const ControlBot = new Runnable( Chat.say(m); }); } - cmdNicks.get(nick).seenHelpMsg = true; + playerTracker.get(nick).seenHelpMsg = true; } }); _actions.set("cancel", { @@ -1586,11 +1687,20 @@ const ControlBot = new Runnable( run: function (nick) { if (!ngVote.active) return; if (ngVote.votes.get(nick) === "yes") { - Chat.say("Your vote has already been counted"); + Chat.say(alreadyCountedMessages.random()); return; } ngVote.vote(nick, "yes"); - Chat.say("ty " + nick + ". New count " + ngVote.stats()); + let undecided = ngVote.count("undecided"); + if (undecided > 0) { + let message = thankYouMessages.random() + .replace("{name}", nick) + .replace("{stats}", ngVote.stats()); + Chat.say(message); + } else { + let message = voteCompletionMessages.random().replace("{name}", nick); + Chat.say(message); + } ngVote.checkCount(); } }); @@ -1600,11 +1710,20 @@ const ControlBot = new Runnable( run: function (nick) { if (!ngVote.active) return; if (ngVote.votes.get(nick) === "no") { - Chat.say("Your vote has already been counted"); + Chat.say(alreadyCountedMessages.random()); return; } ngVote.vote(nick, "no"); - Chat.say("ty " + nick + ". New count " + ngVote.stats()); + let undecided = ngVote.count("undecided"); + if (undecided > 0) { + let message = thankYouMessages.random() + .replace("{name}", nick) + .replace("{stats}", ngVote.stats()); + Chat.say(message); + } else { + let message = voteCompletionMessages.random().replace("{name}", nick); + Chat.say(message); + } ngVote.checkCount(); } }); @@ -1745,6 +1864,7 @@ const ControlBot = new Runnable( const commandAliases = new Map([ ["andariel", "andy"], ["bloodraven", "raven"], + ["malus", "smith"], ["radament", "rada"], ["amulet", "amu"], ["ammy", "amu"], @@ -1872,7 +1992,7 @@ const ControlBot = new Runnable( console.log("Disabling " + running.command + " from actions"); actions.get(running.command).markAsComplete(running.nick); } - cmdNicks.get(running.nick).toldToChill = false; + playerTracker.get(running.nick).toldToChill = false; } } } catch (e) { @@ -1893,28 +2013,11 @@ const ControlBot = new Runnable( } pickGoldPiles(); - if (ngVote.active) { - if (ngVote.elapsed() > Time.minutes(2)) { - ngVote.checkCount(true); - if (!ngVote.nextGame) { - Chat.say( - "Not enough votes to start ng." - + " yes: " + ngVote.count("yes") - + " no: " + ngVote.count("no") - + " undecided: " + ngVote.count("undecided") - ); - ngVote.reset(); - } - } else if (ngVote.elapsed() > Time.seconds(30) && !ngVote.nextGame) { - ngVote.checkCount(); - } - } - if (getTickCount() - startTime >= maxTime || ngVote.nextGame) { if (Config.ControlBot.EndMessage) { Chat.say(Config.ControlBot.EndMessage); } - delay(1000); + delay(2000); break; } else if (!gameEndWarningAnnounced && getTickCount() - startTime >= maxTime - Time.seconds(30)) { From ea58ef768a3dbca6716a207a6d21379c4311e215 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 23 May 2025 19:26:07 -0400 Subject: [PATCH 528/758] Add additional info to MuleLogger.logItem description string - Add etherealness, runeword flag, itemType, quality, and class so we can use it for limedrop --- d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index f89756981..82a3ee902 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -4,6 +4,7 @@ * @author kolton, theBGuy * @desc Log items and perm accounts/characters, for setup @see LoggerConfig.js * +* @typedef {import("../../../sdk/globals")} */ const MuleLogger = { @@ -20,7 +21,7 @@ const MuleLogger = { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // inGameCheck: function () { if (getScript("D2BotMuleLog.dbj") && this.LogGame[0] && me.gamename.match(this.LogGame[0], "i")) { - print("ÿc4MuleLoggerÿc0: Logging items on " + me.account + " - " + me.name + "."); + console.log("ÿc4MuleLoggerÿc0: Logging items on " + me.account + " - " + me.name + "."); D2Bot.printToConsole("MuleLogger: Logging items on " + me.account + " - " + me.name + ".", sdk.colors.D2Bot.DarkGold); this.logChar(); let stayInGame = this.IngameTime; @@ -134,8 +135,12 @@ const MuleLogger = { + unit.classid + ":" + unit.location + ":" + unit.x + ":" - + unit.y - + (unit.getFlag(sdk.items.flags.Ethereal) ? ":eth" : "") + + unit.y + ":" + + (unit.getFlag(sdk.items.flags.Ethereal) ? "1" : "0") + ":" + + (unit.getFlag(sdk.items.flags.Runeword) ? "1" : "0") + ":" + + unit.itemType + ":" + + unit.quality + ":" + + unit.itemclass + ":" ); let color = unit.getColor(); let code = Item.getItemCode(unit); @@ -174,6 +179,7 @@ const MuleLogger = { // Dropper handler, todo figure out another way to do this if (isIncluded("systems/dropper/ItemDB.js") || include("systems/dropper/ItemDB.js")) { /** @typedef {import("../dropper/ItemDB")} */ + // @ts-ignore while (!ItemDB.init(false)) { delay(1000); } @@ -182,7 +188,8 @@ const MuleLogger = { let items = me.getItemsEx(); if (!items.length) return; - let folder, realm = me.realm || "Single Player"; + const realm = me.realm || "Single Player"; + let folder; let finalString = ""; if (!FileTools.exists("mules/" + realm)) { From 6ecd61f3773b618f3a6871b83190ccbfe9ffd7a0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 23 May 2025 19:26:12 -0400 Subject: [PATCH 529/758] Update globals.d.ts --- d2bs/kolbot/sdk/globals.d.ts | 78 ++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 2e154b162..3c77693fd 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -49,10 +49,12 @@ declare global { symmetricDifference(other: T[]): T[]; flat(depth?: number): T[]; compactMap(callback: (value: T, index: number, obj: T[]) => any, thisArg?: any): any[]; + filterNull(): T[]; filterHighDistance(step: number): any[] isEqual(t: T[]): boolean remove(val: T): T[] random(): T; + shuffle(): T[]; /** * Creates a new array by sorting the elements of the original array. * @@ -90,6 +92,12 @@ declare global { * @throws {RangeError} If index >= array.length or index < -array.length. */ with(index: number, value: T): T[]; + /** + * Finds the first element that matches the predicate and removes it from the array. + * @param predicate A function to test each element of the array. + * @returns The modified array. + */ + findAndRemove(predicate: (value: T, index: number, array: T[]) => boolean): this; } interface String { @@ -196,6 +204,8 @@ declare global { automap: boolean; remove(): void; + click(): void; + hover(): void; } class Line extends Hook { @@ -412,6 +422,7 @@ declare global { type PlayerType = 0; class Player extends Unit { public type: PlayerType; + readonly size: number; } type MonsterType = 1; @@ -465,6 +476,7 @@ declare global { readonly lightRes: number; readonly poisonRes: number; resPenalty: number; + readonly size: number; getEnchant(type: number): boolean; hasEnchant(...enchants: number): boolean @@ -566,6 +578,7 @@ declare global { readonly durabilityPercent: number; readonly isCharm: boolean; readonly gold: number; + readonly itemclass: number; getColor(): number; getBodyLoc(): number[]; @@ -599,6 +612,17 @@ declare global { cb?: (item: ItemUnit) => boolean, }; + interface ItemInfo { + classid?: number; + itemtype?: number; + quality?: number; + runeword?: boolean; + ethereal?: boolean; + equipped?: boolean | number; + basetype?: boolean; + name?: string | number; + } + interface MeType extends Unit { public type: PlayerType; readonly account: string; @@ -733,6 +757,12 @@ declare global { */ _shitList: Set; shitList: Set; + /** + * @private + * Don't use directly, use `me.qutting` + */ + _quitting: boolean; + quitting: boolean; // d2bs functions overhead(msg: string): void; @@ -791,6 +821,7 @@ declare global { name?: string | number; equipped?: boolean | number; }): {have: boolean; item: ItemUnit | null}; + findFirst(itemInfo: ItemInfo): { have: boolean; item: ItemUnit | null }; usingShield(): boolean; checkQuest(questId: number, state: number): boolean; @@ -1077,14 +1108,46 @@ declare global { function sendDDE() function keystate() - type eventName = 'gamepacket' | 'scriptmsg' | 'copydata' | 'keyup' | 'keydown' | 'itemaction' | 'chatmsg'; - + type eventName = + | 'itemaction' + | 'gameevent' + | 'copydata' + | 'chatmsg' + | 'chatinput' + | 'whispermsg' + | 'chatmsgblocker' + | 'chatinputblocker' + | 'whispermsgblocker' + | 'mousemove' + | 'ScreenHookHover' + | 'mouseclick' + | 'keyup' + | 'keydownblocker' + | 'keydown' + | 'memana' + | 'melife' + | 'playerassign' + | 'ScreenHookClick' + | 'Command' + | 'scriptmsg' + | 'gamepacket' + | 'gamepacketsent' + | 'realmpacket'; + + function addEventListener(eventType: 'realmpacket', callback: ((bytes: ArrayBufferLike) => boolean)): void function addEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void function addEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void function addEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void function addEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void - function addEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number|string) => void)): void - function addEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void + function addEventListener(eventType: 'keyup' | 'keydown' | 'keydownblocker', callback: ((key: number|string) => void)): void + function addEventListener(eventType: 'chatmsg' | 'chatinput' | 'whispermsg', callback: ((nick: string, msg: string) => void)): void + function addEventListener(eventType: 'chatmsgblocker' | 'chatinputblocker' | 'whispermsgblocker', callback: ((arg1: string, arg2: string) => void)): void + function addEventListener(eventType: 'mousemove', callback: ((arg1: string, arg2: string) => void)): void + function addEventListener(eventType: 'ScreenHookHover', callback: ((arg1: string, arg2: string) => void)): void + function addEventListener(eventType: 'mouseclick', callback: ((arg1: string, arg2: string, arg3: string, arg4: string) => void)): void + function addEventListener(eventType: 'memana' | 'melife', callback: ((arg1: string, arg2: string) => void)): void + function addEventListener(eventType: 'playerassign', callback: ((arg1: string, arg4: string) => void)): void + function addEventListener(eventType: 'ScreenHookClick', callback: ((arg1: any, arg2: any, arg3: any, arg4: any) => void)): void function addEventListener(eventType: eventName, callback: ((...args: any) => void)): void function removeEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void @@ -1312,6 +1375,7 @@ declare global { }, joinInfo: {}, profileInfo: ProfileInfo, + ftjCount: number, sayMsg(string: string): void, timer(tick: number): string, @@ -1401,5 +1465,11 @@ declare global { * @returns {boolean} - Returns true if the value matches the expected type, otherwise false. */ function isType(val: any, type: T): val is PrimitiveTypeMap[T]; + + /** + * This method sleeps the caller thread for the duration in ms passed to it + * @note Use sparingly, this method stops the background workers on the callers thread + */ + function threadSleep(ms: number); } export {}; From de51bc78df67eeeeef999b7b16a5879b55cf0136 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 23 May 2025 19:40:09 -0400 Subject: [PATCH 530/758] feat: Add `doConvertNL` action to GameAction - This action handles converting ladder mule files to non-ladder without having to delete and relog them so limedrop is able to have an accurate list --- d2bs/kolbot/D2BotGameAction.dbj | 6 +++ .../libs/systems/gameaction/GameAction.d.ts | 1 + .../libs/systems/gameaction/GameAction.js | 50 ++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index aa555eb05..254378709 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -329,6 +329,12 @@ function main () { GameAction.init(tag); + if (GameAction.task.action === "doConvertNL") { + GameAction.convertLadderFiles(); + + return; + } + if (Starter.gameInfo.error) { if (!!DataFile.getStats().debugInfo) { Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts index 749de673b..28ab397f7 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts @@ -18,6 +18,7 @@ declare global { function load(hash: string): string; function save(hash: string, data: string): void; function dropItems(dropList: string[]): void; + function convertLadderFiles(): void; } } export {}; \ No newline at end of file diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js index 9b7ee030f..8f0f62c2b 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -17,12 +17,14 @@ const GameAction = { SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) IngameTime: 60, // Time to wait before leaving game + /** @type {{ action: string, data: any } | null} */ task: null, // don't edit init: function (task) { try { GameAction.task = JSON.parse(task); - + console.log("ÿc4GameActionÿc0: Task: ", GameAction.task); + if (this.task["data"] && typeof this.task.data === "string") { this.task.data = JSON.parse(this.task.data); } @@ -56,7 +58,7 @@ const GameAction = { let tag = (function () { try { - return JSON.parse(JSON.stringify(this.task)); // deep copy + return JSON.parse(JSON.stringify(GameAction.task)); // deep copy } catch (err) { console.log("ÿc4GameActionÿc0: Error in update: " + err); return {}; @@ -230,4 +232,48 @@ const GameAction = { } } }, + + convertLadderFiles: function () { + console.log("ÿc4GameActionÿc0: Converting ladder files to non-ladder..."); + if (!this.task || !this.task.data || this.task.action !== "doConvertNL") { + return; + } + + /** @type {{ realm: string, account: string, character: string}[]} */ + const data = this.task.data; + let converted = 0; + + if (!data || !Array.isArray(data)) { + this.update("done", "Invalid data for conversion!"); + D2Bot.stop(); + delay(5000); + } + + for (let i = 0; i < data.length; i++) { + const { realm, account, character } = data[i]; + + if (!realm || !account || !character) { + continue; + } + + const fileName = "mules/" + realm + "/" + account + "/" + character + ".txt"; + if (!FileTools.exists(fileName)) { + continue; + } + + const fileContent = FileTools.readText(fileName); + if (!fileContent) { + continue; + } + const [charName, ext] = character.split("."); + const newFileName = "mules/" + realm + "/" + account + "/" + charName + "." + ext.replace("l", "n") + ".txt"; + FileTools.writeText(newFileName, fileContent); + FileTools.remove(fileName); + console.log("Converted " + fileName + " to " + newFileName); + converted++; + } + + this.update("done", "Conversion complete! Converted " + converted + " files."); + D2Bot.stop(me.profile, true); + }, }; From 3ad1b0a844e53c4c27ab230672dea9893a3b3b95 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 25 May 2025 12:39:05 -0400 Subject: [PATCH 531/758] Update MuleLogger.js - Include color, socket count, and gfx in description --- d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 82a3ee902..f5baf5dd9 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -120,7 +120,7 @@ const MuleLogger = { includeIfNotIncluded("core/misc.js"); let header = ""; - let name = ( + const name = ( unit.itemType + "_" + unit.fname .split("\n") @@ -129,6 +129,9 @@ const MuleLogger = { .replace(/(y|ÿ)c[0-9!"+<:;.*]|\/|\\/g, "") .trim() ); + const color = unit.getColor(); + const code = Item.getItemCode(unit); + const sock = unit.getItemsEx(); let desc = ( Item.getItemDesc(unit, logIlvl) + "$" + unit.gid + ":" @@ -141,10 +144,10 @@ const MuleLogger = { + unit.itemType + ":" + unit.quality + ":" + unit.itemclass + ":" + + sock.length + ":" + + unit.gfx + ":" + + color + ":" ); - let color = unit.getColor(); - let code = Item.getItemCode(unit); - let sock = unit.getItemsEx(); if (sock.length) { for (let i = 0; i < sock.length; i += 1) { From 1a01b3a01e825c8e70bbf615ff874746a561c4e9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 26 May 2025 17:15:09 -0400 Subject: [PATCH 532/758] feat: Add command suggestion feature to ControlBot - Handle if users make a typo, i.e. some person who keeps typing `reven` instead of `raven` --- d2bs/kolbot/libs/scripts/ControlBot.js | 58 ++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 84bf106f4..bc8cbc7a4 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1360,6 +1360,56 @@ const ControlBot = new Runnable( } }; + /** + * Finds commands that closely match the input + * @param {string} input - User entered command + * @returns {string[]} - Array of matching command suggestions + */ + function findSimilarCommands(input) { + if (!input || input.length < 2) return []; + + let matches = []; + + for (let [key, value] of actions) { + if (!value.desc) continue; + + if (key.startsWith(input)) { + matches.push(key); + } + } + + if (matches.length > 0) { + return matches; + } + + // check for typos (commands with at most 1 character different) + if (input.length >= 3) { + for (let [key, value] of actions) { + if (!value.desc) continue; + + // Check for similar commands with at most 1 character different + if (Math.abs(key.length - input.length) <= 1) { + let diffCount = 0; + + for (let j = 0; j < Math.min(key.length, input.length); j++) { + if (key[j] !== input[j]) diffCount += 1; + if (diffCount > 1) break; + } + + // Account for length difference as well + diffCount += Math.abs(key.length - input.length); + + // Match with at most 1 character different + if (diffCount <= 1) { + matches.push(key); + } + } + } + } + + return matches; + } + /** * @param {string} nick * @param {string} msg @@ -1434,6 +1484,14 @@ const ControlBot = new Runnable( } if (!actions.has(chatCmd)) { + let similarCommands = findSimilarCommands(chatCmd); + + if (similarCommands.length === 1) { + Chat.whisper(nick, "Did you mean '" + similarCommands[0] + "'?"); + } else if (similarCommands.length > 1 && similarCommands.length <= 5) { + Chat.whisper(nick, "Did you mean one of these: " + similarCommands.join(", ") + "?"); + } + return; } From 6388bd32c027fa5cab912f67ef816703643e7862 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 29 May 2025 20:34:04 -0400 Subject: [PATCH 533/758] Fix attempting to cancel active ngvote in ControlBot.js --- d2bs/kolbot/libs/scripts/ControlBot.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index bc8cbc7a4..7768b887d 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1444,6 +1444,11 @@ const ControlBot = new Runnable( return; } + + if (chatCmd === "ngvote" && ngVote.active) { + Chat.say("Can't cancel ngvote, it is already active. Cast your vote instead with ngyes/ngno"); + return; + } const cmdIndex = queue.findIndex(function (item) { const [cmd, commander] = item; From 93144dea567f48e5e01f1b5ee4a60732e21e94e1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 30 May 2025 13:29:58 -0400 Subject: [PATCH 534/758] Fix ControlBot givewp command when users don't give an area --- d2bs/kolbot/libs/scripts/ControlBot.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 7768b887d..c28135738 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1480,6 +1480,12 @@ const ControlBot = new Runnable( return; } + if (chatCmd === "givewp") { + Chat.say("givewp must be used with an area, i.e givewp cold plains"); + + return; + } + if (commandAliases.has(chatCmd)) { chatCmd = commandAliases.get(chatCmd); } From f8cfc47351296684c893f6ee9ca98a9bbd2bf5a0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 3 Jun 2025 01:39:30 -0400 Subject: [PATCH 535/758] Add druid vine summon classid's to sdk --- d2bs/kolbot/libs/modules/sdk.js | 4 ++++ d2bs/kolbot/sdk/types/sdk.d.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 1870d538b..5b8de8e78 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -1489,6 +1489,7 @@ player: { flag: { Ignore: 2, + Squelch: 4, Hostile: 8, }, slot: { @@ -1954,6 +1955,9 @@ DeathMauler: 57, PutridDefiler: 58, }, + PoisonCreeper: 425, + CarrionVine: 426, + SolarCreeper: 427, DiablosBoneCage: 340, DiablosBoneCage2: 342, Dummy1: 149, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index d9d87653f..3554181d6 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -1416,6 +1416,7 @@ declare global { export namespace player { export namespace flag { const Ignore: 2; + const Squelch: 4; const Hostile: 8; } export namespace slot { @@ -1876,6 +1877,9 @@ declare global { const DeathMauler: 57; const PutridDefiler: 58; } + const PoisonCreeper: 425; + const CarrionVine: 426; + const SolarCreeper: 427; const DiablosBoneCage: 340; const DiablosBoneCage2: 342; const Dummy1: 149; From 8f8668fc336363469e7836a2dd84c1cfe8cd12a3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 3 Jun 2025 01:44:29 -0400 Subject: [PATCH 536/758] feat: Add isDruidVine property and size to Unit and update globals.d.ts --- d2bs/kolbot/libs/core/Prototypes.js | 40 +++++++++++++++++++++++------ d2bs/kolbot/sdk/globals.d.ts | 1 + 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 8ce168b2d..acedf6d4f 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -19,17 +19,19 @@ } // Stupid reference thing - // eslint-disable-next-line no-unused-vars - const test = original(-1); - - let [first] = args, second = args.length >= 2 ? args[1] : undefined; + const _test = original(-1); + const [first, second] = args; const ret = original.apply(this, args); // deal with bug - if (first === 1 && typeof second === "string" && ret - && ((me.act === 1 && ret.classid === sdk.monsters.Dummy1) - || me.act === 2 && ret.classid === sdk.monsters.Dummy2)) { + if ( + (first === sdk.unittype.Monster && typeof second === "string" && ret) + && ( + (me.act === 1 && ret.classid === sdk.monsters.Dummy1) + || (me.act === 2 && ret.classid === sdk.monsters.Dummy2) + ) + ) { return null; } @@ -187,6 +189,13 @@ Object.defineProperties(Unit.prototype, { return getBaseStat("monstats", this.classid, "MonType") === sdk.monsters.type.Scarab; }, }, + isDruidVine: { + get: function () { + return [ + sdk.monsters.PoisonCreeper, sdk.monsters.CarrionVine, sdk.monsters.SolarCreeper, + ].includes(this.classid); + } + }, isWalking: { get: function () { return (this.mode === sdk.monsters.mode.Walking && (this.targetx !== this.x || this.targety !== this.y)); @@ -390,6 +399,19 @@ Object.defineProperties(Unit.prototype, { if (this.type > sdk.unittype.Player) throw new Error("Unit.inTown: Must be used with player units."); return sdk.areas.Towns.includes(this.area); } + }, + size: { + /** @this {Monster | Player} */ + get: function () { + if (this.type > sdk.unittype.Monster) { + throw new Error("Unit.size: Must be used with monster or player units."); + } + const baseId = getBaseStat("monstats", this.classid, "baseid"); + const size = getBaseStat("monstats2", baseId, "sizex"); + + // in case base stat returns something outrageous + return (typeof size !== "number" || size < 1 || size > 3) ? 3 : size; + } } }); @@ -1121,7 +1143,7 @@ Unit.prototype.checkItem = function (itemInfo) { /** * @description Returns first item given by itemInfo * @param {ItemInfo} itemInfo - * @returns {ItemUnit[]} + * @returns {{ have: boolean, item: ItemUnit }} */ Unit.prototype.findFirst = function (itemInfo = []) { if (this === undefined || this.type > 1) return { have: false, item: null }; @@ -2026,6 +2048,8 @@ Unit.prototype.castChargedSkill = function (...args) { // The result of "successfully" casted is different, so we cant wait for it here. We have to assume it worked return true; + } else { + throw new Error("No valid charged skills found on this item"); } } diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 3c77693fd..b76dc2214 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -454,6 +454,7 @@ declare global { readonly isUnraveler: boolean; readonly isFallen: boolean; readonly isBeetle: boolean; + readonly isDruidVine: boolean; readonly extraStrong: boolean; readonly extraFast: boolean; readonly cursed: boolean; From 44ee44d9d03c8bf160e5847d66f735c22d83dc02 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 3 Jun 2025 01:54:16 -0400 Subject: [PATCH 537/758] feat: Enhance ControlBot chat message handling with burst protection and message validation - Add queue hogging handling, limit users to three commands - Update `ngvote is active` message to let users know what to type to vote since it is apparently not obvious - Fix chanting attempting to chant vines - Overide global say so all messages go through queue --- d2bs/kolbot/libs/scripts/ControlBot.js | 136 +++++++++++++++++++------ 1 file changed, 107 insertions(+), 29 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index c28135738..ee1360e08 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -362,10 +362,32 @@ const ControlBot = new Runnable( const players = new Map(); /** @type {Set} */ const givenGold = new Set(); + + const sendChatMessage = say; + + /** @param {string} msg */ + global.say = function (msg) { + if (typeof msg !== "string") { + throw new TypeError("Message must be a string"); + } + Chat.say(msg); + }; + + /** + * @constructor + * @param {string} msg + */ + function Message (msg) { + if (typeof msg !== "string") { + throw new TypeError("Message must be a string"); + } + this.msg = msg; + this.createdAt = getTickCount(); + } const Chat = { overheadTick: 0, - /** @type {string[]} */ + /** @type {Message[]} */ queue: [], /** @@ -373,7 +395,7 @@ const ControlBot = new Runnable( * @param {string} msg */ say: function (msg) { - Chat.queue.push(msg); + Chat.queue.push(new Message(msg)); }, /** @@ -385,7 +407,7 @@ const ControlBot = new Runnable( if (!force && getTickCount() - Chat.overheadTick < 0) return; // allow overhead messages every ~3-4 seconds Chat.overheadTick = getTickCount() + Time.seconds(3) + rand(250, 1500); - say("!" + msg); + sendChatMessage("!" + msg); }, /** @@ -399,7 +421,7 @@ const ControlBot = new Runnable( return; } let who = players.get(nick) || nick; - Chat.queue.push("/w " + who + " " + msg); + Chat.queue.push(new Message("/w " + who + " " + msg)); }, /** @@ -408,28 +430,64 @@ const ControlBot = new Runnable( * @param {string} msg */ message: function (nick, msg) { - Chat.queue.push("/m " + nick + " " + msg); + Chat.queue.push(new Message("/m " + nick + " " + msg)); }, }; Worker.runInBackground.chat = (function () { let tick = getTickCount(); + let burstCount = 0; + let burstStartTime = 0; + const BURST_LIMIT = 4; + const BURST_WINDOW = Time.seconds(16); + const BURST_COOLDOWN = Time.seconds(10); return function () { if (!Chat.queue.length) return true; if (getTickCount() - tick < 0) return true; // check if next msg is going to be a whisper - if (Chat.queue[0].startsWith("/w")) { + if (Chat.queue[0].msg.startsWith("/w")) { // check if the player is in the game and if not, don't send the whisper } + + // don't immediately respond, seems to trigger temp mutes more often + if (getTickCount() - Chat.queue[0].createdAt < 500) { + // don't send messages that are too new + return true; + } + + // Burst protection (prevent too many messages in short time) + let currentTime = getTickCount(); + + // Reset burst count if window has passed + if (currentTime - burstStartTime > BURST_WINDOW) { + burstCount = 0; + burstStartTime = currentTime; + } + + // If we've hit burst limit, enforce cooldown + if (burstCount >= BURST_LIMIT) { + if (currentTime - burstStartTime < BURST_COOLDOWN) { + return true; // Still in cooldown period + } + burstCount = 0; + burstStartTime = currentTime; + } + + if (burstCount === 0) { + burstStartTime = currentTime; + } + // allow say messages every ~1.7 seconds tick = getTickCount() + Time.seconds(1) + rand(500, 950); - console.debug("(" + Chat.queue[0] + ")"); - if (Chat.queue[0].length > MAX_CHAT_LENGTH) { + burstCount += 1; + + console.debug("(" + Chat.queue[0].msg + ") [Burst: " + burstCount + "/" + BURST_LIMIT + "]"); + if (Chat.queue[0].msg.length > MAX_CHAT_LENGTH) { console.debug("Message too long, splitting."); - Chat.queue[0] = Chat.queue[0].substring(0, MAX_CHAT_LENGTH); + Chat.queue[0].msg = Chat.queue[0].msg.substring(0, MAX_CHAT_LENGTH); } - say(Chat.queue.shift()); + sendChatMessage(Chat.queue.shift().msg); return true; }; })(); @@ -644,22 +702,21 @@ const ControlBot = new Runnable( Chat.say("I don't see you"); } - unit = Game.getMonster(); + let monster = Game.getMonster(); - if (unit) { + if (monster) { do { - if (unit.classid === sdk.summons.Poppy) { - console.log(unit); + if (monster.isDruidVine) { continue; } // merc or any other owned unit - let parent = unit.getParent(); + let parent = monster.getParent(); if (!parent) continue; if (parent.name === nick) { - Packet.enchant(unit); + Packet.enchant(monster); delay(500); } - } while (unit.getNext()); + } while (monster.getNext()); } return true; @@ -771,26 +828,27 @@ const ControlBot = new Runnable( } while (unit.getNext()); } - unit = Game.getMonster(); + let monster = Game.getMonster(); - if (unit) { + if (monster) { do { try { - if (unit.getParent() - && Misc.inMyParty(unit.getParent().name) - && playerTracker.has(unit.getParent().name) - && !unit.getState(sdk.states.Enchant) - && unit.distance <= 40) { - Packet.enchant(unit); + if (monster.getParent() + && !monster.isDruidVine + && Misc.inMyParty(monster.getParent().name) + && playerTracker.has(monster.getParent().name) + && !monster.getState(sdk.states.Enchant) + && monster.distance <= 40) { + Packet.enchant(monster); // not going to re-enchant the minions for now though, will think on how best to handle that later - if (Misc.poll(() => unit.getState(sdk.states.Enchant), 500, 50)) { - chanted.push(unit.name); + if (Misc.poll(() => monster.getState(sdk.states.Enchant), 500, 50)) { + chanted.push(monster.name); } } } catch (err) { console.error(err); } - } while (unit.getNext()); + } while (monster.getNext()); } return true; @@ -1545,6 +1603,26 @@ const ControlBot = new Runnable( if (index > -1) { Chat.whisper(nick, "You already requested this command. Queue position: " + (index + 1)); } else { + if (queue.length > 1) { + let commandsToCheck = []; + + if (running.command && running.nick) { + commandsToCheck.push([running.command, running.nick]); + } + + commandsToCheck = commandsToCheck.concat(queue.slice(0, 2)); + commandsToCheck.push([chatCmd, nick]); + + const isUserHoggingQueue = commandsToCheck.every(function (item) { + return item[1] === nick; + }); + + if (isUserHoggingQueue && commandsToCheck.length >= 4) { + Chat.whisper(nick, "You are hogging the queue. Max 3 commands per user at a time."); + return; + } + } + queue.push([chatCmd, nick, full]); if (queue.length > 1 || running.nick !== "") { let queueMessage = queuePositionMessages.random() @@ -1719,7 +1797,7 @@ const ControlBot = new Runnable( hostileCheck: false, run: function (nick) { if (ngVote.active) { - Chat.say("NGVote is already active. Current count: " + ngVote.stats()); + Chat.say("NGVote is already active. Type ngyes/ngno to vote. Current count: " + ngVote.stats()); return; } const { MinGameLength, NGVoteCooldown } = Config.ControlBot; From 0b1166d2191a5973d8b99e103b98805faec78ebf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:46:43 -0400 Subject: [PATCH 538/758] Fix AutoRush gidbinn sequence, rusher can't interact with altar --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index 1dbcec6f0..e156ce4fb 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -711,10 +711,7 @@ if (!altar) { throw new Error("gidbinn failed - couldn't find altar"); } - Misc.poll(function () { - altar.interact(); - return altar.mode === sdk.objects.mode.Active; - }, Time.minutes(1), Time.seconds(1)); + Attack.securePosition(me.x, me.y, 30, 3000, true); Pather.makePortal(); log(AutoRush.playersIn); @@ -724,6 +721,25 @@ log(timedOut(nick)); return false; } + + Misc.poll(function () { + Attack.clear(15); + altar.distance > 5 && Pather.moveToUnit(altar); + return altar.mode === sdk.objects.mode.Active; + }, Time.minutes(1), Time.seconds(1)); + + Misc.poll(function () { + const gidbinn = Game.getItem(sdk.quest.item.TheGidbinn); + if (gidbinn) { + Attack.securePosition(gidbinn.x, gidbinn.y, 30, 3000, true); + Pather.moveToUnit(gidbinn); + return true; + } + Attack.clear(25); + altar.distance > 5 && Pather.moveToUnit(altar); + return false; + }, Time.minutes(1), Time.seconds(1)); + if (AutoRush.rushMode !== RushModes.chanter) { while (playerIn(me.area, nick)) { delay(100); From 3ba2b86433563b5f50e44bafc2d7da95091a4463 Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Sun, 15 Jun 2025 18:50:21 +0100 Subject: [PATCH 539/758] fix: Avoid non-standard prototype (#450) --- d2bs/kolbot/threads/AutoBuildThread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/threads/AutoBuildThread.js b/d2bs/kolbot/threads/AutoBuildThread.js index 7623387f9..4258f6e1f 100644 --- a/d2bs/kolbot/threads/AutoBuildThread.js +++ b/d2bs/kolbot/threads/AutoBuildThread.js @@ -142,7 +142,7 @@ function getRequiredSkills (id) { 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 skillInValidRange = (sdk.skills.Attack < skill && skill <= sdk.skills.PhoenixStrike) && (![sdk.skills.IdentifyScroll, sdk.skills.BookofIdentify, sdk.skills.TownPortalScroll, sdk.skills.BookofTownPortal].includes(skill)); let hardPointsInSkill = me.getSkill(skill, sdk.skills.subindex.HardPoints); if (skillInValidRange && !hardPointsInSkill) { From fdb29c5c9b0218e6168dc9c5ea08fc5b0f37bd0f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:01:43 -0400 Subject: [PATCH 540/758] Add druid raven summon classid --- d2bs/kolbot/libs/modules/sdk.js | 1 + d2bs/kolbot/sdk/types/sdk.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 5b8de8e78..e8ed72e95 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -1955,6 +1955,7 @@ DeathMauler: 57, PutridDefiler: 58, }, + Raven: 419, PoisonCreeper: 425, CarrionVine: 426, SolarCreeper: 427, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 3554181d6..15674bbd5 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -1877,6 +1877,7 @@ declare global { const DeathMauler: 57; const PutridDefiler: 58; } + const Raven: 419; const PoisonCreeper: 425; const CarrionVine: 426; const SolarCreeper: 427; From bf016c8c8f92f0b77fd356126369d34eb36bd6c3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:11:13 -0400 Subject: [PATCH 541/758] feat: Add isEnchantable property to Monster and update Unit prototype --- d2bs/kolbot/libs/core/Prototypes.js | 10 ++++++++++ d2bs/kolbot/sdk/globals.d.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index acedf6d4f..81b49cc14 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -190,12 +190,22 @@ Object.defineProperties(Unit.prototype, { }, }, isDruidVine: { + /** @this {Unit} */ get: function () { return [ sdk.monsters.PoisonCreeper, sdk.monsters.CarrionVine, sdk.monsters.SolarCreeper, ].includes(this.classid); } }, + isEnchantable: { + /** @this {Monster} */ + get: function () { + if (this.type > sdk.unittype.Monster) { + throw new Error("Unit.isEnchantable: Must be used with monster units."); + } + return (this.isMonster && !this.isNPC && !this.isDruidVine && this.classid !== sdk.monsters.Raven); + }, + }, isWalking: { get: function () { return (this.mode === sdk.monsters.mode.Walking && (this.targetx !== this.x || this.targety !== this.y)); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index b76dc2214..9e7834710 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -478,6 +478,7 @@ declare global { readonly poisonRes: number; resPenalty: number; readonly size: number; + readonly isEnchantable: boolean; getEnchant(type: number): boolean; hasEnchant(...enchants: number): boolean From 0bb1d23ef523a7bf3b6e3e8b189f601816d8ee60 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:13:33 -0400 Subject: [PATCH 542/758] fix: Update ControlBot enchanting to check for isEnchantable property instead of isDruidVine - Ravens are also not enchantable, and there are probably others to add later --- d2bs/kolbot/libs/scripts/ControlBot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index ee1360e08..0a492260d 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -706,7 +706,7 @@ const ControlBot = new Runnable( if (monster) { do { - if (monster.isDruidVine) { + if (!monster.isEnchantable) { continue; } // merc or any other owned unit @@ -834,7 +834,7 @@ const ControlBot = new Runnable( do { try { if (monster.getParent() - && !monster.isDruidVine + && monster.isEnchantable && Misc.inMyParty(monster.getParent().name) && playerTracker.has(monster.getParent().name) && !monster.getState(sdk.states.Enchant) From 51cda9e2ec63cc2be605db32556afbc0cd17914b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:20:21 -0400 Subject: [PATCH 543/758] fix: ControlBot `givewp` check always failing due to being checked after splicing msg - Update voterequest messages to specify its for ng because users weren't correlating that otherwise --- d2bs/kolbot/libs/scripts/ControlBot.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 0a492260d..966c64fdc 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -37,12 +37,12 @@ const ControlBot = new Runnable( ]; const voteRequestMessages = [ - "{players} please cast your vote. Voting ends in {time}s", - "Still waiting on votes from {players}. {time} seconds remaining", - "Don't forget to vote {players}! Time remaining: {time}s", - "{players}, we need your vote! {time} seconds left to decide", - "Hey {players}, make your voice heard! {time}s left to vote", - "{time} seconds left and we're still waiting on {players} to vote" + "{players} please cast your vote for ng. Voting ends in {time}s", + "Still waiting on ng votes from {players}. {time} seconds remaining", + "Don't forget to vote for ng {players}! Time remaining: {time}s", + "{players}, we need your vote for ng! {time} seconds left to decide", + "Hey {players}, make your voice heard! {time}s left to vote for ng", + "{time} seconds left and we're still waiting on {players} to vote for ng" ]; const queuePositionMessages = [ @@ -1489,6 +1489,12 @@ const ControlBot = new Runnable( const denRegex = /^\b(den|den ?of ?evil)\b/; const forgeRegex = /^\b(forge|hell ?forge)\b/; let chatCmd = full; + + if (chatCmd === "givewp") { + Chat.say("givewp must be used with an area, i.e givewp cold plains"); + + return; + } if (chatCmd.match(/^rush /gi)) { chatCmd = chatCmd.split(" ")[1]; @@ -1538,17 +1544,11 @@ const ControlBot = new Runnable( return; } - if (chatCmd === "givewp") { - Chat.say("givewp must be used with an area, i.e givewp cold plains"); - - return; - } - if (commandAliases.has(chatCmd)) { chatCmd = commandAliases.get(chatCmd); } - if (chatCmd.match(/^drop /gi) && !Config.ControlBot.DropGold) { + if ((chatCmd.match(/^drop/gi) || chatCmd.match(/^give ?gold$/)) && !Config.ControlBot.DropGold) { chatCmd = "troll"; } From 4802875c24066d495f49a32d9d25dd15e864ea1f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 16 Jun 2025 01:17:17 -0400 Subject: [PATCH 544/758] hotfix: Update Unit prototype isNPC getter to exclude mercenary class IDs --- d2bs/kolbot/libs/core/Prototypes.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 81b49cc14..82533951c 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -81,8 +81,14 @@ Object.defineProperties(Unit.prototype, { }, }, isNPC: { + /** @this {Monster | NPCUnit} */ get: function () { - return this.type === sdk.unittype.Monster && this.getStat(sdk.stats.Alignment) === 2; + const mercClassids = [sdk.mercs.Rogue, sdk.mercs.Guard, sdk.mercs.IronWolf, sdk.mercs.A5Barb]; + return ( + this.type === sdk.unittype.Monster + && !mercClassids.includes(this.classid) + && this.getStat(sdk.stats.Alignment) === 2 + ); }, }, // todo - monster types From d51e86df1df25418d23b2da32a602d981aeae1a9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 18 Jun 2025 18:54:34 -0400 Subject: [PATCH 545/758] fix: Improve error handling in ControlBot stash and cube operations - Break up the check into pieces, we should only attempt emptying the cube if there are other things in it besides the tome or leg. This was causing failures occasionally if the bot didn't have enough room in the invo to take the leg or tome out of the cube. It was also redundant to empty then put it back. This also provides better error messages --- d2bs/kolbot/libs/scripts/ControlBot.js | 30 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 966c64fdc..cb7449a4d 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -993,11 +993,31 @@ const ControlBot = new Runnable( let tome = getTome(); if (!tome) return false; - if (!Town.openStash() - || !Cubing.emptyCube() - || !Storage.Cube.MoveTo(leg) - || !Storage.Cube.MoveTo(tome) - || !Cubing.openCube()) { + if (!Town.openStash()) { + log("Failed to open stash"); + return false; + } + + const cubeItems = me.getItemsEx(-1, sdk.items.mode.inStorage).filter(function (item) { + return ( + item.isInCube + && item.classid !== sdk.items.quest.WirtsLeg + && item.classid !== sdk.items.TomeofTownPortal + ); + }); + + if (cubeItems.length > 0 && !Cubing.emptyCube()) { + log("Failed to empty cube"); + return false; + } + + if (!Storage.Cube.MoveTo(leg) || !Storage.Cube.MoveTo(tome)) { + log("Failed to move items to cube"); + return false; + } + + if (!Cubing.openCube()) { + log("Failed to open cube"); return false; } From 2bb3c14a998d7bf6147e6f77ec420fd9a086b5e9 Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Fri, 20 Jun 2025 11:46:34 +0100 Subject: [PATCH 546/758] fix: Comply with what is being asked when using Proxy (#452) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/set#return_value Can throw a type error when used in strict mode otherwise --- d2bs/kolbot/libs/modules/Worker.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index 51f431324..c704981cc 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -82,6 +82,7 @@ } }; self.pushLowPrio(proxyCallback); + return true; }, deleteProperty: function (target, name) { if (!target.processes.hasOwnProperty(name)) { From 574c75d6af2b0b258565fb5036bcda02c2b711ec Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Fri, 20 Jun 2025 15:41:12 +0100 Subject: [PATCH 547/758] Check for String.contains (#451) --- d2bs/kolbot/libs/Polyfill.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/libs/Polyfill.js b/d2bs/kolbot/libs/Polyfill.js index 1ae5ed2d9..7e63f6d9a 100644 --- a/d2bs/kolbot/libs/Polyfill.js +++ b/d2bs/kolbot/libs/Polyfill.js @@ -89,6 +89,10 @@ if (!String.prototype.includes) { }; } +if (!String.prototype.contains) { + String.prototype.contains = String.prototype.includes; +} + String.prototype.capitalize = function (downcase = false) { return this.charAt(0).toUpperCase() + (downcase ? this.slice(1).toLowerCase() : this.slice(1)); }; From 687e60f413b49e992b1e8276026b2ec3173ad25b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:15:41 -0400 Subject: [PATCH 548/758] fix: ControlBot ngvote noVotes counting, restore global say on cleanup, better ngvote messages - Add logging for opening cow portal, users were confused if it had started running yet - Add retries for opening stash, sometimes fails if people get in the way - Add `cancel` to commands that will stop wp giving - Handle people responding yes/no during ngvoting because for some reason it's hard to understand to type ngyes or ngno - Fix `cancel ` giving false positives for removing something from the queue, was kinda dumb should check gt 1 not just not equal to -1 duh - Remove idle in town restriction for greeting players, just push to or chat queue and handle it - Add `talrasha` as an alias to duriel - Add cleanup that restores the global say method since we override it during for the scripts lifetime --- d2bs/kolbot/libs/scripts/ControlBot.js | 70 +++++++++++++++++++------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index cb7449a4d..bd90468da 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -7,8 +7,15 @@ * */ +/** + * @typedef {ScriptContext & { cleanup: () => void }} ControlBotContext + */ + const ControlBot = new Runnable( - function ControlBot () { + /** + * @param {ControlBotContext} ctx + */ + function ControlBot (ctx) { const thankYouMessages = [ "Ty {name}. Current count: {stats}", "Got your vote, {name}! The tally is now {stats}", @@ -319,7 +326,7 @@ const ControlBot = new Runnable( return false; } - if (noVotes >= votesNeeded) { + if (noVotes >= votesNeeded && noVotes > yesVotes) { Chat.say("ng rejected by majority."); this.reset(); return false; @@ -372,6 +379,11 @@ const ControlBot = new Runnable( } Chat.say(msg); }; + + ctx.cleanup = function () { + // restore the original say function + global.say = sendChatMessage; + }; /** * @constructor @@ -982,8 +994,11 @@ const ControlBot = new Runnable( return false; } + log("starting cows"); + let leg = getLeg(); if (!leg) return false; + if (!Storage.Inventory.CanFit({ sizex: 1, sizey: 2 })) { // we don't have any space, put the leg in the stash to make room in invo Storage.Stash.MoveTo(leg); @@ -991,9 +1006,22 @@ const ControlBot = new Runnable( } let tome = getTome(); - if (!tome) return false; + if (!tome) { + log("Failed to get tome"); + return false; + } + + let openedStash = false; + + for (let i = 0; i < 3; i++) { + if (Town.openStash()) { + openedStash = true; + + break; + } + } - if (!Town.openStash()) { + if (!openedStash) { log("Failed to open stash"); return false; } @@ -1050,7 +1078,7 @@ const ControlBot = new Runnable( */ const stopWatcher = function (who, msg) { if (who !== nick) return; - if (msg === "stop" || msg === "abort") { + if (msg === "stop" || msg === "abort" || msg === "cancel") { stop = true; } }; @@ -1128,7 +1156,7 @@ const ControlBot = new Runnable( if (who !== nick) return; if (msg === "next") { next = true; - } else if (msg === "stop" || msg === "abort") { + } else if (msg === "stop" || msg === "abort" || msg === "cancel") { stop = true; } }; @@ -1515,6 +1543,13 @@ const ControlBot = new Runnable( return; } + + if (ngVote.active) { + if (chatCmd === "yes" || chatCmd === "no") { + Chat.say("Were you trying to vote? Type ngyes or ngno instead."); + return; + } + } if (chatCmd.match(/^rush /gi)) { chatCmd = chatCmd.split(" ")[1]; @@ -1543,7 +1578,7 @@ const ControlBot = new Runnable( return String.isEqual(chatCmd, cmd); }); - if (cmdIndex !== 1) { + if (cmdIndex > 1) { Chat.say("Removing " + chatCmd + " from the queue"); queue.splice(cmdIndex, 1); @@ -1671,8 +1706,6 @@ const ControlBot = new Runnable( function gameEvent (mode, param1, param2, name1, name2) { switch (mode) { case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." - // idle in town - me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); if (name1 && ngVote.active) { ngVote.votes.set(name1, "undecided"); } @@ -1689,6 +1722,9 @@ const ControlBot = new Runnable( // autosqelch shitlisted players if (me.shitList.has(name1)) { clickParty(getParty(name1), sdk.party.controls.Squelch); + } else { + // Handle greeting new players + Chat.say("Welcome, " + name1 + "! For a list of commands say 'help' or visit https://d2bggames.netlify.app/"); } } catch (err) { console.error(err); @@ -2037,6 +2073,7 @@ const ControlBot = new Runnable( ["ammy", "amu"], ["duriel", "duri"], ["dury", "duri"], + ["talrasha", "duri"], ["tome", "lamesen"], ["travincal", "trav"], ["mephisto", "meph"], @@ -2138,15 +2175,6 @@ const ControlBot = new Runnable( } while (true) { - while (greet.length > 0) { - let nick = greet.shift(); - - if (!me.shitList.has(nick)) { - // Chat.say("Welcome, " + nick + "! For a list of commands say 'help'"); - Chat.overhead("Welcome, " + nick + "! For a list of commands say 'help'"); - } - } - Town.getDistance("stash") > 8 && Town.move("stash"); if (queue.length > 0) { @@ -2205,5 +2233,11 @@ const ControlBot = new Runnable( { startArea: sdk.areas.RogueEncampment, preAction: null, + /** + * @param {ControlBotContext} ctx + */ + cleanup: function (ctx) { + ctx.cleanup(); + } } ); From 2ecc536be8b22591be5e986ef3a5a438d74ffac6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:19:16 -0400 Subject: [PATCH 549/758] feat: Update Idle script to move every 10-12 in combination with the refresh packet - Noticed occasionally the idler's were still being disconnected so this is enough to stop that --- d2bs/kolbot/libs/scripts/Idle.js | 108 ++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 36 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Idle.js b/d2bs/kolbot/libs/scripts/Idle.js index 463d7c60d..e6842f6e8 100644 --- a/d2bs/kolbot/libs/scripts/Idle.js +++ b/d2bs/kolbot/libs/scripts/Idle.js @@ -5,60 +5,96 @@ * */ +/** + * @typedef {ScriptContext & { cleanup: () => void }} IdleContext + */ + const Idle = new Runnable( - function Idle () { + /** + * @param {IdleContext} ctx + */ + function Idle (ctx) { const greet = []; - // eslint-disable-next-line no-unused-vars - function gameEvent (mode, param1, param2, name1, name2) { + + /** + * @param {number} mode + * @param {number} param1 + * @param {number} param2 + * @param {string} name1 + * @param {string} name2 + */ + function gameEvent (mode, param1, param2, name1, _name2) { switch (mode) { - case 0x02: + case 0x02: // "%Name1(%Name2) joined our world. Diablo's minions grow stronger." // idle in town - me.inTown && me.mode === sdk.player.mode.StandingInTown && greet.push(name1); - + if (me.inTown && me.mode === sdk.player.mode.StandingInTown) { + greet.push(name1); + } break; } } - try { - const startTime = getTickCount(); - let idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); + ctx.cleanup = function () { + removeEventListener("gameevent", gameEvent); + }; + + const startTime = getTickCount(); + // send anti-idle packet every ~20 minutes + let idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); + // move to waypoint/stash every 10-12 minutes + let moveTick = getTickCount() + Time.seconds(rand(600, 720)); + + if (Config.Idle.Advertise) { + addEventListener("gameevent", gameEvent); + } + + while (true) { + if (!me.inArea(sdk.areas.RogueEncampment)) { + Town.goToTown(1); + } else if (Town.getDistance("stash") > 10) { + Town.move("stash"); + } + + if (Config.Idle.MaxGameLength > 0 + && getTickCount() - startTime > Time.minutes(Config.Idle.MaxGameLength)) { + break; + } + if (Config.Idle.Advertise) { - addEventListener("gameevent", gameEvent); + while (greet.length) { + let name = greet.shift(); + say("!Welcome " + name + ". " + Config.Idle.AdvertiseMessage); + } } - while (true) { - if (!me.inArea(sdk.areas.RogueEncampment)) { - Town.goToTown(1); - } else if (Town.getDistance("stash") > 10) { + if (getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + + if (getTickCount() - moveTick > 0) { + if (me.inTown && me.mode === sdk.player.mode.StandingInTown) { + Town.move("waypoint"); Town.move("stash"); } - if (Config.Idle.MaxGameLength > 0 - && getTickCount() - startTime > Time.minutes(Config.Idle.MaxGameLength)) { - break; - } - if (Config.Idle.Advertise) { - while (greet.length) { - let name = greet.shift(); - say("!Welcome " + name + ". " + Config.Idle.AdvertiseMessage); - } - } - if (getTickCount() - idleTick > 0) { - Packet.questRefresh(); - idleTick += Time.seconds(rand(1200, 1500)); - console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); - } - - delay(1000); + moveTick += Time.seconds(rand(600, 720)); + console.log("Moved to waypoint/stash, next move in: (" + Time.format(moveTick - getTickCount()) + ")"); } - return true; - } finally { - // cleanup - removeEventListener("gameevent", gameEvent); + delay(1000); } + + return true; }, { startArea: sdk.areas.RogueEncampment, - preAction: null + preAction: null, + /** + * @param {IdleContext} ctx + */ + cleanup: function (ctx) { + ctx.cleanup(); + } } ); From b3ec392d1cebf5c88039db60239ce73468b69ba8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:21:32 -0400 Subject: [PATCH 550/758] Update ControlBot.js - remove external link --- d2bs/kolbot/libs/scripts/ControlBot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index bd90468da..1cb141cd3 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1724,7 +1724,7 @@ const ControlBot = new Runnable( clickParty(getParty(name1), sdk.party.controls.Squelch); } else { // Handle greeting new players - Chat.say("Welcome, " + name1 + "! For a list of commands say 'help' or visit https://d2bggames.netlify.app/"); + Chat.say("Welcome, " + name1 + "! For a list of commands say help"); } } catch (err) { console.error(err); From 5a7b5d3123b42107a5ae263640fd18672ef14a0d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 24 Jun 2025 01:08:04 -0400 Subject: [PATCH 551/758] Fix ControlBot cancel command for aliases --- d2bs/kolbot/libs/scripts/ControlBot.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 1cb141cd3..2e1cac79f 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -1568,6 +1568,10 @@ const ControlBot = new Runnable( Chat.say("Can't cancel ngvote, it is already active. Cast your vote instead with ngyes/ngno"); return; } + + if (commandAliases.has(chatCmd)) { + chatCmd = commandAliases.get(chatCmd); + } const cmdIndex = queue.findIndex(function (item) { const [cmd, commander] = item; @@ -1578,7 +1582,7 @@ const ControlBot = new Runnable( return String.isEqual(chatCmd, cmd); }); - if (cmdIndex > 1) { + if (cmdIndex !== -1) { Chat.say("Removing " + chatCmd + " from the queue"); queue.splice(cmdIndex, 1); From 62a2072254bb8322de5b3aefdbd9f81c48061540 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 24 Jun 2025 01:35:58 -0400 Subject: [PATCH 552/758] Fix ControlBot ngvote counting non-partied players & refactor count method --- d2bs/kolbot/libs/scripts/ControlBot.js | 41 +++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 2e1cac79f..998728837 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -224,7 +224,7 @@ const ControlBot = new Runnable( * @returns {number} */ votesNeeded: function () { - return Math.max(1, Math.floor((Misc.getPlayerCount() - 2) / 2)); + return Math.max(1, Math.floor((Misc.getPartyCount()) / 2)); }, /** @@ -261,21 +261,15 @@ const ControlBot = new Runnable( /** * Check current count - * @param {NgVote} type */ - count: function (type) { - if (type === "undecided") { - return Array.from(this.votes.values()).filter(vote => vote === "undecided").length; - } - return type === "yes" - ? Array.from(this.votes.values()).filter(vote => vote === "yes").length - : Array.from(this.votes.values()).filter(vote => vote === "no").length; - }, - - stats: function () { + count: function () { let [yes, no, undecided] = [0, 0, 0]; - for (let [_name, vote] of this.votes) { + for (let [name, vote] of this.votes) { + if (!Misc.inMyParty(name)) { + continue; + } + if (vote === "yes") { yes++; } else if (vote === "no") { @@ -285,6 +279,16 @@ const ControlBot = new Runnable( } } + return { + yes: yes, + no: no, + undecided: undecided + }; + }, + + stats: function () { + const { yes, no, undecided } = this.count(); + return ("yes: " + yes + " no: " + no + " undecided: " + undecided); }, @@ -298,7 +302,7 @@ const ControlBot = new Runnable( if (!skipUndecided) { for (let [playerName, vote] of this.votes) { - if (vote === "undecided") { + if (vote === "undecided" && Misc.inMyParty(playerName)) { undecided.push(playerName); } } @@ -317,8 +321,7 @@ const ControlBot = new Runnable( } const votesNeeded = this.votesNeeded(); - const yesVotes = Array.from(this.votes.values()).filter(vote => vote === "yes").length; - const noVotes = Array.from(this.votes.values()).filter(vote => vote === "no").length; + const { yes: yesVotes, no: noVotes } = this.count(); if (Misc.getPartyCount() === yesVotes + noVotes && yesVotes === noVotes) { Chat.say("Not enough votes to start ng we have a draw."); @@ -620,8 +623,6 @@ const ControlBot = new Runnable( const playerTracker = new Map(); /** @type {Map} */ const wpNicks = new Map(); - /** @type {Array} */ - const greet = []; /** @type {Map} */ const wps = new Map([ @@ -1898,7 +1899,7 @@ const ControlBot = new Runnable( return; } ngVote.vote(nick, "yes"); - let undecided = ngVote.count("undecided"); + let { undecided } = ngVote.count(); if (undecided > 0) { let message = thankYouMessages.random() .replace("{name}", nick) @@ -1921,7 +1922,7 @@ const ControlBot = new Runnable( return; } ngVote.vote(nick, "no"); - let undecided = ngVote.count("undecided"); + let { undecided } = ngVote.count(); if (undecided > 0) { let message = thankYouMessages.random() .replace("{name}", nick) From 175d96e7c34b92f6cf2a55c5005b5f7f90ae886d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Jun 2025 01:21:55 -0400 Subject: [PATCH 553/758] feat: Add locale ladder string & use for findCharacter check --- d2bs/kolbot/libs/OOG.js | 7 ++++++- d2bs/kolbot/libs/modules/sdk.js | 1 + d2bs/kolbot/sdk/types/sdk.d.ts | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 550e5bd29..89393572e 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -240,6 +240,11 @@ includeIfNotIncluded("core/Me.js"); * @returns {Control | false} */ findCharacter: function (info, startFromTop = true) { + const ladderString = getLocaleString(sdk.locale.text.Ladder); + /** @param {string} text */ + const matchLadderString = function (text) { + return text.includes(ladderString); + }; const singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); // offline doesn't have a character limit cap const cap = singlePlayer ? 999 : 24; @@ -270,7 +275,7 @@ includeIfNotIncluded("core/Me.js"); count++; if (String.isEqual(text[1], info.charName)) { - if (info.ladder && !text.some(el => el.includes("LADDER"))) continue; + if (info.ladder && !text.some(matchLadderString)) continue; // how to check hardcore? return control; } diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index e8ed72e95..2a32f6aa6 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -4479,6 +4479,7 @@ }, text: { + Ladder: 719, RepairCost: 3330, SellValue: 3331, IdentifyCost: 3332, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 15674bbd5..301fd8892 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4402,6 +4402,7 @@ declare global { } namespace text { + const Ladder: 719; const RepairCost: 3330; const SellValue: 3331; const IdentifyCost: 3332; From a667ac71a01e066744d5e84024cc669defcc0029 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 3 Jul 2025 01:11:08 -0400 Subject: [PATCH 554/758] Update D2BotCleaner.dbj for SoloPlay data paths to .soloplay directory - Changed all file and folder paths from 'libs/SoloPlay/Data' to 'libs/SoloPlay/.soloplay' in D2BotCleaner.dbj to reflect new storage location for SoloPlay data files --- d2bs/kolbot/D2BotCleaner.dbj | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index 99d185e86..aa9990c4a 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -80,10 +80,10 @@ function dataCleaner () { // Filepaths const dataFP = "data/" + profile + ".json"; - const gameTimeFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-GameTime" + ".json"; - const charDataFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-CharData" + ".json"; - const lvlPerfFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-LevelingPerformance" + ".csv"; - const scrPerfFP = "libs/SoloPlay/Data/" + profile + "/" + profile + "-ScriptPerformance" + ".csv"; + const gameTimeFP = "libs/SoloPlay/.soloplay/" + profile + "/" + profile + "-GameTime" + ".json"; + const charDataFP = "libs/SoloPlay/.soloplay/" + profile + "/" + profile + "-CharData" + ".json"; + const lvlPerfFP = "libs/SoloPlay/.soloplay/" + profile + "/" + profile + "-LevelingPerformance" + ".csv"; + const scrPerfFP = "libs/SoloPlay/.soloplay/" + profile + "/" + profile + "-ScriptPerformance" + ".csv"; let savePath = "logs/"; // default value in case something goes wrong with assigning actual savePath if (CleanerConfig.SaveFiles && soloplayProfile) { @@ -93,31 +93,31 @@ function dataCleaner () { || FileTools.exists(lvlPerfFP) || FileTools.exists(scrPerfFP)) { // Create folder to copy files to - if (!FileTools.exists("libs/SoloPlay/Data/" + charType)) { - folder = dopen("libs/SoloPlay/Data"); + if (!FileTools.exists("libs/SoloPlay/.soloplay/" + charType)) { + folder = dopen("libs/SoloPlay/.soloplay"); folder.create(charType); } - if (!FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass)) { - folder = dopen("libs/SoloPlay/Data/" + charType); + if (!FileTools.exists("libs/SoloPlay/.soloplay/" + charType + "/" + charClass)) { + folder = dopen("libs/SoloPlay/.soloplay/" + charType); folder.create(charClass); } - let files = dopen("libs/SoloPlay/Data/" + charType + "/" + charClass + "/").getFolders(); + let files = dopen("libs/SoloPlay/.soloplay/" + charType + "/" + charClass + "/").getFolders(); j = files.length + 1; // make sure folder doesn't already exist. - while (FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString())) { + while (FileTools.exists("libs/SoloPlay/.soloplay/" + charType + "/" + charClass + "/" + j.toString())) { j++; delay(100); } - if (!FileTools.exists("libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString())) { - folder = dopen("libs/SoloPlay/Data/" + charType + "/" + charClass); + if (!FileTools.exists("libs/SoloPlay/.soloplay/" + charType + "/" + charClass + "/" + j.toString())) { + folder = dopen("libs/SoloPlay/.soloplay/" + charType + "/" + charClass); folder.create(j.toString()); } - savePath = "libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j.toString() + "/" + profile; + savePath = "libs/SoloPlay/.soloplay/" + charType + "/" + charClass + "/" + j.toString() + "/" + profile; profileExists = true; } } @@ -149,7 +149,7 @@ function dataCleaner () { } if (CleanerConfig.SaveFiles && profileExists && soloplayProfile) { - D2Bot.printToConsole("D2BotCleaner: Files saved to -> libs/SoloPlay/Data/" + charType + "/" + charClass + "/" + j, sdk.colors.D2Bot.Gold); + D2Bot.printToConsole("D2BotCleaner: Files saved to -> libs/SoloPlay/.soloplay/" + charType + "/" + charClass + "/" + j, sdk.colors.D2Bot.Gold); } if (profileExists) { From 1ac88471d52eaf9794ac26b6f831369225130d67 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 3 Jul 2025 01:52:45 -0400 Subject: [PATCH 555/758] feat: Add setup scripts, config files, and update .gitignore - Stop tracking data files that could potentially contain sensitive information. - Added setup and update batch scripts for repository initialization and updates. Introduced initial configuration and log files in the +setup directory, and included .gitkeep files for data and logs directories. --- {logs => +setup}/Console.rtf | 0 {data => +setup}/cdkeys.json | 0 {d2bs => +setup}/d2bs.ini | Bin {logs => +setup}/exceptions.log | 0 {logs => +setup}/keyinfo.log | 0 {data => +setup}/patch.json | 0 {data => +setup}/profile.json | 0 {data => +setup}/schedules.json | 0 {data => +setup}/server.json | 0 .gitignore | 12 ++-- README.md | 12 ++++ data/.gitkeep | 0 logs/.gitkeep | 0 setup.bat | 45 +++++++++++++++ update.bat | 94 ++++++++++++++++++++++++++++++++ 15 files changed, 159 insertions(+), 4 deletions(-) rename {logs => +setup}/Console.rtf (100%) rename {data => +setup}/cdkeys.json (100%) rename {d2bs => +setup}/d2bs.ini (100%) rename {logs => +setup}/exceptions.log (100%) rename {logs => +setup}/keyinfo.log (100%) rename {data => +setup}/patch.json (100%) rename {data => +setup}/profile.json (100%) rename {data => +setup}/schedules.json (100%) rename {data => +setup}/server.json (100%) create mode 100644 data/.gitkeep create mode 100644 logs/.gitkeep create mode 100644 setup.bat create mode 100644 update.bat diff --git a/logs/Console.rtf b/+setup/Console.rtf similarity index 100% rename from logs/Console.rtf rename to +setup/Console.rtf diff --git a/data/cdkeys.json b/+setup/cdkeys.json similarity index 100% rename from data/cdkeys.json rename to +setup/cdkeys.json diff --git a/d2bs/d2bs.ini b/+setup/d2bs.ini similarity index 100% rename from d2bs/d2bs.ini rename to +setup/d2bs.ini diff --git a/logs/exceptions.log b/+setup/exceptions.log similarity index 100% rename from logs/exceptions.log rename to +setup/exceptions.log diff --git a/logs/keyinfo.log b/+setup/keyinfo.log similarity index 100% rename from logs/keyinfo.log rename to +setup/keyinfo.log diff --git a/data/patch.json b/+setup/patch.json similarity index 100% rename from data/patch.json rename to +setup/patch.json diff --git a/data/profile.json b/+setup/profile.json similarity index 100% rename from data/profile.json rename to +setup/profile.json diff --git a/data/schedules.json b/+setup/schedules.json similarity index 100% rename from data/schedules.json rename to +setup/schedules.json diff --git a/data/server.json b/+setup/server.json similarity index 100% rename from data/server.json rename to +setup/server.json diff --git a/.gitignore b/.gitignore index 53ef9d25c..e50fedb1e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ !.gitignore !.gitmodules !.editorconfig +!.gitkeep # Track these hidden directories !.github/ @@ -15,6 +16,11 @@ !.vscode/extensions.json # Do not track user generated data +data/*.json +images/ +logs/*.rtf +logs/*.log +d2bs/d2bs.ini d2bs/kolbot/data/secure/*.txt d2bs/kolbot/data/*.json d2bs/kolbot/logs/*.json @@ -22,14 +28,12 @@ d2bs/kolbot/logs/*.txt d2bs/kolbot/logs/**/** d2bs/kolbot/mules/**/*.txt d2bs/logs/*.log +d2bs/kolbot/env.json d2bs/kolbot/libs/manualplay/config/*.*.js d2bs/kolbot/libs/soloplay/** d2bs/kolbot/libs/config/*.*.js d2bs/kolbot/D2BotSoloPlay.dbj -data/ -images/ -logs/ # Do not track install packages node_modules/ -d2bs/kolbot/env.json +d2bs/kolbot/data/web/limedrop.json diff --git a/README.md b/README.md index 16ac31560..a0c24786c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,18 @@ If you want to contribute to d2bs/d2bot#, come to irc.synirc.net/d2bs and ask ar - [Microsoft Visual C++ 2010 Redistributable Package (x86)](https://www.microsoft.com/en-us/download/details.aspx?id=26999) - [Microsoft .NET Framework 4.0 (or higher)](https://dotnet.microsoft.com/download/dotnet-framework) +## Set up + +**IMPORTANT: Run the setup script first!** + +After downloading kolbot, you must run the setup script to configure the necessary files: + +1. Run `setup.bat` to copy configuration files to their correct locations +2. The script will also initialize Git submodules if Git is available +3. This step is required for kolbot to function properly + +To keep your installation updated, you can run `update.bat` at any time to pull the latest changes from the repository and update all submodules. + ## Getting Started - [download kolbot](https://github.com/blizzhackers/documentation/blob/restructure/d2bot/Download.md#download) - [d2bot manager setup](https://github.com/blizzhackers/documentation/blob/restructure/d2bot/ManagerSetup.md/#manager-setup) diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/setup.bat b/setup.bat new file mode 100644 index 000000000..87549451c --- /dev/null +++ b/setup.bat @@ -0,0 +1,45 @@ +@echo off +echo Copying setup files to their respective directories... + +REM Check if git is available +git --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo WARNING: Git is not installed or not available in PATH + echo Please install Git and ensure it's in your PATH to use submodules + echo Continuing with file copying only... + echo. +) else ( + echo Git detected, initializing and updating submodules... + git submodule update --init --recursive + if %errorlevel% neq 0 ( + echo WARNING: Failed to update submodules + echo Please check your Git configuration and try again + echo. + ) else ( + echo Submodules updated successfully! + echo. + ) +) + +REM Create directories if they don't exist +if not exist "data\" mkdir "data\" +if not exist "logs\" mkdir "logs\" +if not exist "d2bs\" mkdir "d2bs\" + +REM Copy JSON files to data directory +if exist "+setup\cdkeys.json" copy "+setup\cdkeys.json" "data\" >nul +if exist "+setup\patch.json" copy "+setup\patch.json" "data\" >nul +if exist "+setup\profile.json" copy "+setup\profile.json" "data\" >nul +if exist "+setup\schedules.json" copy "+setup\schedules.json" "data\" >nul +if exist "+setup\server.json" copy "+setup\server.json" "data\" >nul + +REM Copy log files to logs directory +if exist "+setup\Console.rtf" copy "+setup\Console.rtf" "logs\" >nul +if exist "+setup\exceptions.log" copy "+setup\exceptions.log" "logs\" >nul +if exist "+setup\keyinfo.log" copy "+setup\keyinfo.log" "logs\" >nul + +REM Copy ini file to d2bs directory +if exist "+setup\d2bs.ini" copy "+setup\d2bs.ini" "d2bs\" >nul + +echo Setup files copied successfully! +pause \ No newline at end of file diff --git a/update.bat b/update.bat new file mode 100644 index 000000000..b02edd911 --- /dev/null +++ b/update.bat @@ -0,0 +1,94 @@ +@echo off +echo Updating repository and submodules... + +REM Check if git is available +git --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo ERROR: Git is not installed or not available in PATH + echo Please install Git and ensure it's in your PATH before running this script + echo. + pause + exit /b 1 +) + +echo Git detected, checking for local changes... + +REM Check if there are any local changes +git diff --quiet +set has_changes=%errorlevel% + +git diff --cached --quiet +set has_staged_changes=%errorlevel% + +if %has_changes% neq 0 ( + echo Local changes detected, stashing them... + git stash push -m "Auto-stash before update" + if %errorlevel% neq 0 ( + echo ERROR: Failed to stash local changes + echo Please manually resolve any conflicts and try again + echo. + pause + exit /b 1 + ) + set stashed=1 +) else if %has_staged_changes% neq 0 ( + echo Staged changes detected, stashing them... + git stash push -m "Auto-stash before update" + if %errorlevel% neq 0 ( + echo ERROR: Failed to stash staged changes + echo Please manually resolve any conflicts and try again + echo. + pause + exit /b 1 + ) + set stashed=1 +) else ( + echo No local changes detected + set stashed=0 +) + +echo. +echo Updating repository... + +REM Pull latest changes from remote +git pull +if %errorlevel% neq 0 ( + echo ERROR: Failed to pull updates from remote repository + echo Please check your Git configuration and network connection + echo. + pause + exit /b 1 +) + +echo Repository updated successfully! +echo. + +REM Update all submodules to latest commits +echo Updating submodules... +git submodule update --init --remote --recursive +if %errorlevel% neq 0 ( + echo WARNING: Failed to update submodules + echo Please check your Git configuration and try again + echo. +) else ( + echo Submodules updated successfully! + echo. +) + +REM Restore stashed changes if any were stashed +if %stashed% equ 1 ( + echo Restoring your local changes... + git stash pop + if %errorlevel% neq 0 ( + echo WARNING: Failed to restore stashed changes automatically + echo Your changes are saved in the stash. You can restore them manually with: + echo git stash pop + echo. + ) else ( + echo Local changes restored successfully! + echo. + ) +) + +echo Update complete! +pause \ No newline at end of file From a7ffc1ffc3b0bbab06023cb4d26a459e1642e067 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 3 Jul 2025 17:59:58 -0400 Subject: [PATCH 556/758] Add D2BotSoloPlay.dbj entry script and update .gitignore --- .gitignore | 1 - d2bs/kolbot/D2BotSoloPlay.dbj | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 d2bs/kolbot/D2BotSoloPlay.dbj diff --git a/.gitignore b/.gitignore index e50fedb1e..67108f1ab 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ d2bs/kolbot/env.json d2bs/kolbot/libs/manualplay/config/*.*.js d2bs/kolbot/libs/soloplay/** d2bs/kolbot/libs/config/*.*.js -d2bs/kolbot/D2BotSoloPlay.dbj # Do not track install packages node_modules/ diff --git a/d2bs/kolbot/D2BotSoloPlay.dbj b/d2bs/kolbot/D2BotSoloPlay.dbj new file mode 100644 index 000000000..28f4fde8f --- /dev/null +++ b/d2bs/kolbot/D2BotSoloPlay.dbj @@ -0,0 +1,14 @@ +/** +* @filename D2BotSoloPlay.dbj +* @author theBGuy +* @desc Entry script for SoloPlay leveling system. Actual logic is in SoloEntry.js +* +* +* @typedef {import("./sdk/globals")} +*/ + +// No touchy! +if (!include("SoloPlay/OOG/SoloEntry.js")) { + D2Bot.print("SoloPlay: Failed to include SoloEntry.js"); + D2Bot.stop(); +} From daf8be7475e740ce2c590ba5fdc318acaa80fcb5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:07:43 -0400 Subject: [PATCH 557/758] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 67108f1ab..5c20c6ff1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,7 @@ logs/*.rtf logs/*.log d2bs/d2bs.ini d2bs/kolbot/data/secure/*.txt -d2bs/kolbot/data/*.json +d2bs/kolbot/data/**/*.json d2bs/kolbot/logs/*.json d2bs/kolbot/logs/*.txt d2bs/kolbot/logs/**/** From b960ece53a9862f38f8d05620977ee03a0952e35 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:39:24 -0400 Subject: [PATCH 558/758] Add SoloPlay submodule to .gitmodules - Added the SoloPlay submodule under d2bs/kolbot/libs/SoloPlay. This allows the project to include and manage the SoloPlay library as a dependency. --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index 4735c30a4..86c074cea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "limedrop"] path = limedrop url = https://github.com/noah-/limedrop +[submodule "SoloPlay"] + path = d2bs/kolbot/libs/SoloPlay + url = https://github.com/blizzhackers/kolbot-SoloPlay From 673dd78c96ad9c637ae9e6a7eed9505944073456 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 3 Jul 2025 19:58:10 -0400 Subject: [PATCH 559/758] feat: Update .gitmodules for SoloPlay submodule and adjust .gitignore --- .gitignore | 1 - .gitmodules | 2 +- d2bs/kolbot/libs/SoloPlay | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) create mode 160000 d2bs/kolbot/libs/SoloPlay diff --git a/.gitignore b/.gitignore index 5c20c6ff1..b47d25ed9 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,6 @@ d2bs/kolbot/mules/**/*.txt d2bs/logs/*.log d2bs/kolbot/env.json d2bs/kolbot/libs/manualplay/config/*.*.js -d2bs/kolbot/libs/soloplay/** d2bs/kolbot/libs/config/*.*.js # Do not track install packages diff --git a/.gitmodules b/.gitmodules index 86c074cea..320499516 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "limedrop"] path = limedrop url = https://github.com/noah-/limedrop -[submodule "SoloPlay"] +[submodule "d2bs/kolbot/libs/SoloPlay"] path = d2bs/kolbot/libs/SoloPlay url = https://github.com/blizzhackers/kolbot-SoloPlay diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay new file mode 160000 index 000000000..b4a4e19dc --- /dev/null +++ b/d2bs/kolbot/libs/SoloPlay @@ -0,0 +1 @@ +Subproject commit b4a4e19dc8d7c3429e2ac68ec6af1d82258b8f5b From 0cffc1b8a735de63b5c129ee1d9570f20767ddcd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 3 Jul 2025 21:12:31 -0400 Subject: [PATCH 560/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index b4a4e19dc..53c606f78 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit b4a4e19dc8d7c3429e2ac68ec6af1d82258b8f5b +Subproject commit 53c606f78b82798096eaddd72a60bfa38ac06545 From 4175ecfa03c3508433f3f8cbdd0e2d6c867e5758 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 02:11:16 -0400 Subject: [PATCH 561/758] Refactor config and setup structure, add GameActionConfig - Moved various configuration files from d2bs/kolbot/libs to +setup subdirectories for better organization. Should be all the user config except for pickit files. - Added a new GameActionConfig.js file and updated GameAction.js to load its configuration from this file. - Enhanced setup.bat to use variables, modular copy logic, and support the new directory structure. - Updated .gitignore to exclude old config paths. --- .../config => +setup/automule}/MuleConfig.js | 0 .../automule}/TorchAnniMules.js | 0 .../channel/ChannelConfig.js | 0 .../cleaner/CleanerConfig.js | 0 .../libs => +setup}/config/_CustomConfig.js | 0 .../crafting/TeamsConfig.js | 0 +setup/{ => data}/cdkeys.json | 0 +setup/{ => data}/patch.json | 0 +setup/{ => data}/profile.json | 0 +setup/{ => data}/schedules.json | 0 +setup/{ => data}/server.json | 0 .../systems => +setup}/follow/FollowConfig.js | 0 .../gambling/TeamsConfig.js | 0 +setup/gameaction/GameActionConfig.js | 17 ++++ +setup/{ => logs}/Console.rtf | 0 +setup/{ => logs}/exceptions.log | 0 +setup/{ => logs}/keyinfo.log | 0 .../mulelogger/LoggerConfig.js | 0 .../pubjoin/PubJoinConfig.js | 0 .../libs => +setup}/starter/AdvancedConfig.js | 0 .../libs => +setup}/starter/StarterConfig.js | 0 .../systems => +setup}/torch/FarmerConfig.js | 0 .gitignore | 14 ++++ .../libs/systems/gameaction/GameAction.js | 20 ++--- setup.bat | 79 ++++++++++++++++--- 25 files changed, 108 insertions(+), 22 deletions(-) rename {d2bs/kolbot/libs/systems/automule/config => +setup/automule}/MuleConfig.js (100%) rename {d2bs/kolbot/libs/systems/automule/config => +setup/automule}/TorchAnniMules.js (100%) rename {d2bs/kolbot/libs/systems => +setup}/channel/ChannelConfig.js (100%) rename {d2bs/kolbot/libs/systems => +setup}/cleaner/CleanerConfig.js (100%) rename {d2bs/kolbot/libs => +setup}/config/_CustomConfig.js (100%) rename {d2bs/kolbot/libs/systems => +setup}/crafting/TeamsConfig.js (100%) rename +setup/{ => data}/cdkeys.json (100%) rename +setup/{ => data}/patch.json (100%) rename +setup/{ => data}/profile.json (100%) rename +setup/{ => data}/schedules.json (100%) rename +setup/{ => data}/server.json (100%) rename {d2bs/kolbot/libs/systems => +setup}/follow/FollowConfig.js (100%) rename {d2bs/kolbot/libs/systems => +setup}/gambling/TeamsConfig.js (100%) create mode 100644 +setup/gameaction/GameActionConfig.js rename +setup/{ => logs}/Console.rtf (100%) rename +setup/{ => logs}/exceptions.log (100%) rename +setup/{ => logs}/keyinfo.log (100%) rename {d2bs/kolbot/libs/systems => +setup}/mulelogger/LoggerConfig.js (100%) rename {d2bs/kolbot/libs/systems => +setup}/pubjoin/PubJoinConfig.js (100%) rename {d2bs/kolbot/libs => +setup}/starter/AdvancedConfig.js (100%) rename {d2bs/kolbot/libs => +setup}/starter/StarterConfig.js (100%) rename {d2bs/kolbot/libs/systems => +setup}/torch/FarmerConfig.js (100%) diff --git a/d2bs/kolbot/libs/systems/automule/config/MuleConfig.js b/+setup/automule/MuleConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/automule/config/MuleConfig.js rename to +setup/automule/MuleConfig.js diff --git a/d2bs/kolbot/libs/systems/automule/config/TorchAnniMules.js b/+setup/automule/TorchAnniMules.js similarity index 100% rename from d2bs/kolbot/libs/systems/automule/config/TorchAnniMules.js rename to +setup/automule/TorchAnniMules.js diff --git a/d2bs/kolbot/libs/systems/channel/ChannelConfig.js b/+setup/channel/ChannelConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/channel/ChannelConfig.js rename to +setup/channel/ChannelConfig.js diff --git a/d2bs/kolbot/libs/systems/cleaner/CleanerConfig.js b/+setup/cleaner/CleanerConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/cleaner/CleanerConfig.js rename to +setup/cleaner/CleanerConfig.js diff --git a/d2bs/kolbot/libs/config/_CustomConfig.js b/+setup/config/_CustomConfig.js similarity index 100% rename from d2bs/kolbot/libs/config/_CustomConfig.js rename to +setup/config/_CustomConfig.js diff --git a/d2bs/kolbot/libs/systems/crafting/TeamsConfig.js b/+setup/crafting/TeamsConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/crafting/TeamsConfig.js rename to +setup/crafting/TeamsConfig.js diff --git a/+setup/cdkeys.json b/+setup/data/cdkeys.json similarity index 100% rename from +setup/cdkeys.json rename to +setup/data/cdkeys.json diff --git a/+setup/patch.json b/+setup/data/patch.json similarity index 100% rename from +setup/patch.json rename to +setup/data/patch.json diff --git a/+setup/profile.json b/+setup/data/profile.json similarity index 100% rename from +setup/profile.json rename to +setup/data/profile.json diff --git a/+setup/schedules.json b/+setup/data/schedules.json similarity index 100% rename from +setup/schedules.json rename to +setup/data/schedules.json diff --git a/+setup/server.json b/+setup/data/server.json similarity index 100% rename from +setup/server.json rename to +setup/data/server.json diff --git a/d2bs/kolbot/libs/systems/follow/FollowConfig.js b/+setup/follow/FollowConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/follow/FollowConfig.js rename to +setup/follow/FollowConfig.js diff --git a/d2bs/kolbot/libs/systems/gambling/TeamsConfig.js b/+setup/gambling/TeamsConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/gambling/TeamsConfig.js rename to +setup/gambling/TeamsConfig.js diff --git a/+setup/gameaction/GameActionConfig.js b/+setup/gameaction/GameActionConfig.js new file mode 100644 index 000000000..ad2834b33 --- /dev/null +++ b/+setup/gameaction/GameActionConfig.js @@ -0,0 +1,17 @@ +/** +* @filename GameActionConfig.js +* @author theBGuy +* @desc Configuration file for GameAction system +* +*/ + +(function (module) { + module.exports = { + LogNames: true, // Put account/character name on the picture + LogItemLevel: true, // Add item level to the picture + LogEquipped: false, // include equipped items + LogMerc: false, // include items merc has equipped (if alive) + SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) + IngameTime: 60, // Time to wait before leaving game + }; +})(module); diff --git a/+setup/Console.rtf b/+setup/logs/Console.rtf similarity index 100% rename from +setup/Console.rtf rename to +setup/logs/Console.rtf diff --git a/+setup/exceptions.log b/+setup/logs/exceptions.log similarity index 100% rename from +setup/exceptions.log rename to +setup/logs/exceptions.log diff --git a/+setup/keyinfo.log b/+setup/logs/keyinfo.log similarity index 100% rename from +setup/keyinfo.log rename to +setup/logs/keyinfo.log diff --git a/d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js b/+setup/mulelogger/LoggerConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js rename to +setup/mulelogger/LoggerConfig.js diff --git a/d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js b/+setup/pubjoin/PubJoinConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js rename to +setup/pubjoin/PubJoinConfig.js diff --git a/d2bs/kolbot/libs/starter/AdvancedConfig.js b/+setup/starter/AdvancedConfig.js similarity index 100% rename from d2bs/kolbot/libs/starter/AdvancedConfig.js rename to +setup/starter/AdvancedConfig.js diff --git a/d2bs/kolbot/libs/starter/StarterConfig.js b/+setup/starter/StarterConfig.js similarity index 100% rename from d2bs/kolbot/libs/starter/StarterConfig.js rename to +setup/starter/StarterConfig.js diff --git a/d2bs/kolbot/libs/systems/torch/FarmerConfig.js b/+setup/torch/FarmerConfig.js similarity index 100% rename from d2bs/kolbot/libs/systems/torch/FarmerConfig.js rename to +setup/torch/FarmerConfig.js diff --git a/.gitignore b/.gitignore index b47d25ed9..6e63f9487 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,17 @@ d2bs/kolbot/libs/config/*.*.js # Do not track install packages node_modules/ d2bs/kolbot/data/web/limedrop.json +d2bs/kolbot/libs/systems/automule/config/MuleConfig.js +d2bs/kolbot/libs/systems/channel/ChannelConfig.js +d2bs/kolbot/libs/systems/cleaner/CleanerConfig.js +d2bs/kolbot/libs/systems/torch/FarmerConfig.js +d2bs/kolbot/libs/systems/pubjoin/PubJoinConfig.js +d2bs/kolbot/libs/systems/mulelogger/LoggerConfig.js +d2bs/kolbot/libs/systems/gambling/TeamsConfig.js +d2bs/kolbot/libs/systems/follow/FollowConfig.js +d2bs/kolbot/libs/systems/crafting/TeamsConfig.js +d2bs/kolbot/libs/systems/automule/config/TorchAnniMules.js +d2bs/kolbot/libs/config/_CustomConfig.js +d2bs/kolbot/libs/starter/AdvancedConfig.js +d2bs/kolbot/libs/starter/StarterConfig.js +d2bs/kolbot/libs/systems/gameaction/GameActionConfig.js diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js index 8f0f62c2b..3e33fd2b2 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -8,18 +8,13 @@ include("systems/mulelogger/MuleLogger.js"); const GameAction = { - // keeping with the general structure changes this section should probably be in its own config file - // but its not a lot so does it really need to be? - LogNames: true, // Put account/character name on the picture - LogItemLevel: true, // Add item level to the picture - LogEquipped: false, // include equipped items - LogMerc: false, // include items merc has equipped (if alive) - SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) - IngameTime: 60, // Time to wait before leaving game - /** @type {{ action: string, data: any } | null} */ task: null, - // don't edit + + /** + * @param {string} task - JSON string containing task information + * @returns {boolean} + */ init: function (task) { try { GameAction.task = JSON.parse(task); @@ -277,3 +272,8 @@ const GameAction = { D2Bot.stop(me.profile, true); }, }; + +// load configuration file and apply settings to GameAction, has to be after the namespace is created +(function () { + Object.assign(GameAction, require("./GameActionConfig", null, false)); +})(); diff --git a/setup.bat b/setup.bat index 87549451c..c091e66e1 100644 --- a/setup.bat +++ b/setup.bat @@ -21,25 +21,80 @@ if %errorlevel% neq 0 ( ) ) +REM Set base directories +set "SETUP_DIR=+setup" +set "DATA_DIR=data" +set "LOGS_DIR=logs" +set "D2BS_DIR=d2bs" +set "STARTER_DIR=d2bs\kolbot\libs\starter" +set "SYSTEMS_DIR=d2bs\kolbot\libs\systems" +set "CONFIG_DIR=d2bs\kolbot\libs\config" + REM Create directories if they don't exist -if not exist "data\" mkdir "data\" -if not exist "logs\" mkdir "logs\" -if not exist "d2bs\" mkdir "d2bs\" +if not exist "%DATA_DIR%\" mkdir "%DATA_DIR%" +if not exist "%LOGS_DIR%\" mkdir "%LOGS_DIR%" +if not exist "%D2BS_DIR%\" mkdir "%D2BS_DIR%" +if not exist "%STARTER_DIR%\" mkdir "%STARTER_DIR%" +if not exist "%SYSTEMS_DIR%\automule\config\" mkdir "%SYSTEMS_DIR%\automule\config" +if not exist "%SYSTEMS_DIR%\channel\" mkdir "%SYSTEMS_DIR%\channel" +if not exist "%SYSTEMS_DIR%\cleaner\" mkdir "%SYSTEMS_DIR%\cleaner" +if not exist "%SYSTEMS_DIR%\crafting\" mkdir "%SYSTEMS_DIR%\crafting" +if not exist "%SYSTEMS_DIR%\follow\" mkdir "%SYSTEMS_DIR%\follow" +if not exist "%SYSTEMS_DIR%\gambling\" mkdir "%SYSTEMS_DIR%\gambling" +if not exist "%SYSTEMS_DIR%\mulelogger\" mkdir "%SYSTEMS_DIR%\mulelogger" +if not exist "%SYSTEMS_DIR%\pubjoin\" mkdir "%SYSTEMS_DIR%\pubjoin" +if not exist "%SYSTEMS_DIR%\torch\" mkdir "%SYSTEMS_DIR%\torch" + +REM Function to copy file if it doesn't exist +REM Usage: call :CopyIfNotExists "source" "destination" "description" +goto :main +:CopyIfNotExists +if exist "%~1" ( + if not exist "%~2" ( + copy "%~1" "%~2" >nul + echo Copied %~3 + ) else ( + echo %~3 already exists - skipping + ) +) +goto :eof + +:main REM Copy JSON files to data directory -if exist "+setup\cdkeys.json" copy "+setup\cdkeys.json" "data\" >nul -if exist "+setup\patch.json" copy "+setup\patch.json" "data\" >nul -if exist "+setup\profile.json" copy "+setup\profile.json" "data\" >nul -if exist "+setup\schedules.json" copy "+setup\schedules.json" "data\" >nul -if exist "+setup\server.json" copy "+setup\server.json" "data\" >nul +call :CopyIfNotExists "%SETUP_DIR%\data\cdkeys.json" "%DATA_DIR%\cdkeys.json" "cdkeys.json to data directory" +call :CopyIfNotExists "%SETUP_DIR%\data\patch.json" "%DATA_DIR%\patch.json" "patch.json to data directory" +call :CopyIfNotExists "%SETUP_DIR%\data\profile.json" "%DATA_DIR%\profile.json" "profile.json to data directory" +call :CopyIfNotExists "%SETUP_DIR%\data\schedules.json" "%DATA_DIR%\schedules.json" "schedules.json to data directory" +call :CopyIfNotExists "%SETUP_DIR%\data\server.json" "%DATA_DIR%\server.json" "server.json to data directory" REM Copy log files to logs directory -if exist "+setup\Console.rtf" copy "+setup\Console.rtf" "logs\" >nul -if exist "+setup\exceptions.log" copy "+setup\exceptions.log" "logs\" >nul -if exist "+setup\keyinfo.log" copy "+setup\keyinfo.log" "logs\" >nul +call :CopyIfNotExists "%SETUP_DIR%\logs\Console.rtf" "%LOGS_DIR%\Console.rtf" "Console.rtf to logs directory" +call :CopyIfNotExists "%SETUP_DIR%\logs\exceptions.log" "%LOGS_DIR%\exceptions.log" "exceptions.log to logs directory" +call :CopyIfNotExists "%SETUP_DIR%\logs\keyinfo.log" "%LOGS_DIR%\keyinfo.log" "keyinfo.log to logs directory" REM Copy ini file to d2bs directory -if exist "+setup\d2bs.ini" copy "+setup\d2bs.ini" "d2bs\" >nul +call :CopyIfNotExists "%SETUP_DIR%\d2bs.ini" "%D2BS_DIR%\d2bs.ini" "d2bs.ini to d2bs directory" + +REM Copy system files to their respective directories +call :CopyIfNotExists "%SETUP_DIR%\automule\MuleConfig.js" "%SYSTEMS_DIR%\automule\config\MuleConfig.js" "MuleConfig.js to automule config directory" +call :CopyIfNotExists "%SETUP_DIR%\automule\TorchAnniMules.js" "%SYSTEMS_DIR%\automule\config\TorchAnniMules.js" "TorchAnniMules.js to automule config directory" +call :CopyIfNotExists "%SETUP_DIR%\channel\ChannelConfig.js" "%SYSTEMS_DIR%\channel\ChannelConfig.js" "ChannelConfig.js to channel directory" +call :CopyIfNotExists "%SETUP_DIR%\cleaner\CleanerConfig.js" "%SYSTEMS_DIR%\cleaner\CleanerConfig.js" "CleanerConfig.js to cleaner directory" +call :CopyIfNotExists "%SETUP_DIR%\crafting\TeamsConfig.js" "%SYSTEMS_DIR%\crafting\TeamsConfig.js" "TeamsConfig.js to crafting directory" +call :CopyIfNotExists "%SETUP_DIR%\follow\FollowConfig.js" "%SYSTEMS_DIR%\follow\FollowConfig.js" "FollowConfig.js to follow directory" +call :CopyIfNotExists "%SETUP_DIR%\gambling\TeamsConfig.js" "%SYSTEMS_DIR%\gambling\TeamsConfig.js" "TeamsConfig.js to gambling directory" +call :CopyIfNotExists "%SETUP_DIR%\gameaction\GameActionConfig.js" "%SYSTEMS_DIR%\gameaction\GameActionConfig.js" "GameActionConfig.js to gameaction directory" +call :CopyIfNotExists "%SETUP_DIR%\mulelogger\LoggerConfig.js" "%SYSTEMS_DIR%\mulelogger\LoggerConfig.js" "LoggerConfig.js to mulelogger directory" +call :CopyIfNotExists "%SETUP_DIR%\pubjoin\PubJoinConfig.js" "%SYSTEMS_DIR%\pubjoin\PubJoinConfig.js" "PubJoinConfig.js to pubjoin directory" +call :CopyIfNotExists "%SETUP_DIR%\torch\FarmerConfig.js" "%SYSTEMS_DIR%\torch\FarmerConfig.js" "FarmerConfig.js to torch directory" + +REM Copy custom config files to their respective directories +call :CopyIfNotExists "%SETUP_DIR%\config\_CustomConfig.js" "%CONFIG_DIR%\_CustomConfig.js" "_CustomConfig.js to config directory" + +REM Copy starter config files to their respective directories +call :CopyIfNotExists "%SETUP_DIR%\starter\AdvancedConfig.js" "%STARTER_DIR%\AdvancedConfig.js" "AdvancedConfig.js to starter directory" +call :CopyIfNotExists "%SETUP_DIR%\starter\StarterConfig.js" "%STARTER_DIR%\StarterConfig.js" "StarterConfig.js to starter directory" echo Setup files copied successfully! pause \ No newline at end of file From 0a38ae40c1b7f2b72825addac153c989d91e5af8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 02:11:47 -0400 Subject: [PATCH 562/758] Add @types/node to devDependencies - Added the @types/node package at version ^24.0.10 to devDependencies in package.json to provide Node.js type definitions for TypeScript development. --- package-lock.json | 33 +++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 34 insertions(+) diff --git a/package-lock.json b/package-lock.json index 467e21e66..579fe6f78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { + "@types/node": "^24.0.10", "eslint": "^7.32.0", "typescript": "^4.9.3" } @@ -157,6 +158,16 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@types/node": { + "version": "24.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz", + "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -1212,6 +1223,13 @@ "node": ">=4.2.0" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1378,6 +1396,15 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@types/node": { + "version": "24.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz", + "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", + "dev": true, + "requires": { + "undici-types": "~7.8.0" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -2174,6 +2201,12 @@ "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "dev": true }, + "undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index e2aefb83b..511606e89 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "author": "", "license": "ISC", "devDependencies": { + "@types/node": "^24.0.10", "eslint": "^7.32.0", "typescript": "^4.9.3" } From 8ed584a5f1c348470ff5da3011ceb287cd99bfe7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 02:29:07 -0400 Subject: [PATCH 563/758] Add IncludePath type and update include function signatures - Introduced a new IncludePath type in sdk/types/include-types.d.ts to provide strict typing for include functions. - Updated the isIncluded and include function signatures in globals.d.ts to use the new IncludePath type, improving type safety for file inclusion operations. --- d2bs/kolbot/sdk/globals.d.ts | 6 ++++-- d2bs/kolbot/sdk/types/include-types.d.ts | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 d2bs/kolbot/sdk/types/include-types.d.ts diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 9e7834710..3306d320a 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -19,6 +19,8 @@ /// declare global { + type IncludePath = import('./types/include-types').IncludePath; + interface Error { fileName: string; lineNumber: number; @@ -1098,8 +1100,8 @@ declare global { function utf8ToEuc(arg: any): [] function delay(ms: number): void function load(file: string): boolean - function isIncluded(file: string): boolean - function include(file: string): boolean + function isIncluded(file: IncludePath): boolean + function include(file: IncludePath): boolean function stacktrace(): true function rand(from: number, to: number): number function copy(what: string): void diff --git a/d2bs/kolbot/sdk/types/include-types.d.ts b/d2bs/kolbot/sdk/types/include-types.d.ts new file mode 100644 index 000000000..a737f1f63 --- /dev/null +++ b/d2bs/kolbot/sdk/types/include-types.d.ts @@ -0,0 +1,2 @@ +// Auto-generated include types for kolbot libs +export type IncludePath = "OOG.js" | "Polyfill.js" | "SoloPlay/BuildFiles/Runewords/AncientsPledge.js" | "SoloPlay/BuildFiles/Runewords/Bone.js" | "SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js" | "SoloPlay/BuildFiles/Runewords/CallToArms.js" | "SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js" | "SoloPlay/BuildFiles/Runewords/Chaos.js" | "SoloPlay/BuildFiles/Runewords/CrescentMoon.js" | "SoloPlay/BuildFiles/Runewords/DragonArmor.js" | "SoloPlay/BuildFiles/Runewords/DreamHelm.js" | "SoloPlay/BuildFiles/Runewords/DreamShield.js" | "SoloPlay/BuildFiles/Runewords/Duress.js" | "SoloPlay/BuildFiles/Runewords/Enigma.js" | "SoloPlay/BuildFiles/Runewords/Exile.js" | "SoloPlay/BuildFiles/Runewords/Faith.js" | "SoloPlay/BuildFiles/Runewords/Fortitude.js" | "SoloPlay/BuildFiles/Runewords/Fury.js" | "SoloPlay/BuildFiles/Runewords/Grief.js" | "SoloPlay/BuildFiles/Runewords/HandOfJustice.js" | "SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js" | "SoloPlay/BuildFiles/Runewords/Honor.js" | "SoloPlay/BuildFiles/Runewords/Ice.js" | "SoloPlay/BuildFiles/Runewords/KingsGrace.js" | "SoloPlay/BuildFiles/Runewords/LastWish.js" | "SoloPlay/BuildFiles/Runewords/Lawbringer.js" | "SoloPlay/BuildFiles/Runewords/Lore.js" | "SoloPlay/BuildFiles/Runewords/Malice.js" | "SoloPlay/BuildFiles/Runewords/MercDoom.js" | "SoloPlay/BuildFiles/Runewords/MercFortitude.js" | "SoloPlay/BuildFiles/Runewords/MercInfinity.js" | "SoloPlay/BuildFiles/Runewords/MercInsight.js" | "SoloPlay/BuildFiles/Runewords/MercPride.js" | "SoloPlay/BuildFiles/Runewords/MercTreachery.js" | "SoloPlay/BuildFiles/Runewords/Myth.js" | "SoloPlay/BuildFiles/Runewords/PDiamondShield.js" | "SoloPlay/BuildFiles/Runewords/PhoenixShield.js" | "SoloPlay/BuildFiles/Runewords/Rhyme.js" | "SoloPlay/BuildFiles/Runewords/Sanctuary.js" | "SoloPlay/BuildFiles/Runewords/Silence.js" | "SoloPlay/BuildFiles/Runewords/Smoke.js" | "SoloPlay/BuildFiles/Runewords/SpiritShield.js" | "SoloPlay/BuildFiles/Runewords/SpiritSword.js" | "SoloPlay/BuildFiles/Runewords/Stealth.js" | "SoloPlay/BuildFiles/Runewords/Steel.js" | "SoloPlay/BuildFiles/Runewords/Treachery.js" | "SoloPlay/BuildFiles/Runewords/VoiceOfReason.js" | "SoloPlay/BuildFiles/Runewords/White.js" | "SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.StartBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.js" | "SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.StartBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.js" | "SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.js" | "SoloPlay/BuildFiles/druid/druid.ElementalBuild.js" | "SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.LevelingBuild.js" | "SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.StartBuild.js" | "SoloPlay/BuildFiles/druid/druid.StormbearBuild.js" | "SoloPlay/BuildFiles/druid/druid.WindBuild.js" | "SoloPlay/BuildFiles/druid/druid.WolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.js" | "SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.js" | "SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.StartBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.js" | "SoloPlay/Config/Amazon.js" | "SoloPlay/Config/Assassin.js" | "SoloPlay/Config/Barbarian.js" | "SoloPlay/Config/Druid.js" | "SoloPlay/Config/Necromancer.js" | "SoloPlay/Config/Paladin.js" | "SoloPlay/Config/Sorceress.js" | "SoloPlay/Core/AttackOverrides.js" | "SoloPlay/Core/AutoBuild.js" | "SoloPlay/Core/AutoMuleOverrides.js" | "SoloPlay/Core/AutoStatOverrides.js" | "SoloPlay/Core/CharmEquip.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks-WIP.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/AssassinAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/BarbarianAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/DruidAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/NecromancerAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/PaladinAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/SorceressAttacks.js" | "SoloPlay/Core/ConfigOverrides.js" | "SoloPlay/Core/CubingOverrides.js" | "SoloPlay/Core/DynamicTiers.js" | "SoloPlay/Core/Globals.js" | "SoloPlay/Core/ItemOverrides.js" | "SoloPlay/Core/ItemPrototypes.js" | "SoloPlay/Core/ItemUtilities.js" | "SoloPlay/Core/LoaderOverrides.js" | "SoloPlay/Core/Me.js" | "SoloPlay/Core/Mercenary.js" | "SoloPlay/Core/MiscOverrides.js" | "SoloPlay/Core/MuleloggerOverrides.js" | "SoloPlay/Core/NPCAction.js" | "SoloPlay/Core/NTIPOverrides.js" | "SoloPlay/Core/PatherOverrides.js" | "SoloPlay/Core/PickitOverrides.js" | "SoloPlay/Core/Polyfills.js" | "SoloPlay/Core/PrecastOverrides.js" | "SoloPlay/Core/PrototypeOverrides.js" | "SoloPlay/Core/Quest.js" | "SoloPlay/Core/RunewordsOverrides.js" | "SoloPlay/Core/SkillOverrides.js" | "SoloPlay/Core/SoloEvents.js" | "SoloPlay/Core/SoloWants.js" | "SoloPlay/Core/StorageOverrides.js" | "SoloPlay/Core/TownOverrides.js" | "SoloPlay/Modules/Clear.js" | "SoloPlay/Modules/Coords.js" | "SoloPlay/Modules/Events.js" | "SoloPlay/Modules/GameData/AreaData.js" | "SoloPlay/Modules/GameData/GameData.js" | "SoloPlay/Modules/GameData/LocaleStringID.js" | "SoloPlay/Modules/GameData/MissileData.js" | "SoloPlay/Modules/GameData/MonsterData.js" | "SoloPlay/Modules/GameData/PotData.js" | "SoloPlay/Modules/General.js" | "SoloPlay/Modules/Guard.js" | "SoloPlay/Modules/MercLib.js" | "SoloPlay/Modules/Mock.js" | "SoloPlay/Modules/MockItem.js" | "SoloPlay/Modules/MoveTo.js" | "SoloPlay/Modules/NameGen.js" | "SoloPlay/Modules/Vector.js" | "SoloPlay/Modules/bigInt.js" | "SoloPlay/Modules/utilities.js" | "SoloPlay/OOG/OOGOverrides.js" | "SoloPlay/OOG/SoloEntry.js" | "SoloPlay/Scripts/a1chests.js" | "SoloPlay/Scripts/a5chests.js" | "SoloPlay/Scripts/amulet.js" | "SoloPlay/Scripts/ancients.js" | "SoloPlay/Scripts/ancienttunnels.js" | "SoloPlay/Scripts/andariel.js" | "SoloPlay/Scripts/anya.js" | "SoloPlay/Scripts/baal.js" | "SoloPlay/Scripts/beetleburst.js" | "SoloPlay/Scripts/bishibosh.js" | "SoloPlay/Scripts/bloodraven.js" | "SoloPlay/Scripts/boneash.js" | "SoloPlay/Scripts/brain.js" | "SoloPlay/Scripts/cave.js" | "SoloPlay/Scripts/corpsefire.js" | "SoloPlay/Scripts/countess.js" | "SoloPlay/Scripts/cows.js" | "SoloPlay/Scripts/creepingfeature.js" | "SoloPlay/Scripts/cube.js" | "SoloPlay/Scripts/den.js" | "SoloPlay/Scripts/developermode.js" | "SoloPlay/Scripts/diablo.js" | "SoloPlay/Scripts/duriel.js" | "SoloPlay/Scripts/eye.js" | "SoloPlay/Scripts/eyeback.js" | "SoloPlay/Scripts/fireeye.js" | "SoloPlay/Scripts/getkeys.js" | "SoloPlay/Scripts/heart.js" | "SoloPlay/Scripts/hellforge.js" | "SoloPlay/Scripts/hephasto.js" | "SoloPlay/Scripts/izual.js" | "SoloPlay/Scripts/jail.js" | "SoloPlay/Scripts/lamessen.js" | "SoloPlay/Scripts/lowerkurast.js" | "SoloPlay/Scripts/maggotlair.js" | "SoloPlay/Scripts/mausoleum.js" | "SoloPlay/Scripts/mephisto.js" | "SoloPlay/Scripts/nith.js" | "SoloPlay/Scripts/orgtorch.js" | "SoloPlay/Scripts/pindle.js" | "SoloPlay/Scripts/pits.js" | "SoloPlay/Scripts/radament.js" | "SoloPlay/Scripts/river.js" | "SoloPlay/Scripts/savebarby.js" | "SoloPlay/Scripts/shenk.js" | "SoloPlay/Scripts/smith.js" | "SoloPlay/Scripts/staff.js" | "SoloPlay/Scripts/summoner.js" | "SoloPlay/Scripts/templeruns.js" | "SoloPlay/Scripts/tombs.js" | "SoloPlay/Scripts/travincal.js" | "SoloPlay/Scripts/treehead.js" | "SoloPlay/Scripts/tristram.js" | "SoloPlay/Scripts/worldstone.js" | "SoloPlay/SoloPlay.js" | "SoloPlay/Threads/Reload.js" | "SoloPlay/Threads/ToolsThread.js" | "SoloPlay/Tools/CharData.js" | "SoloPlay/Tools/Developer.js" | "SoloPlay/Tools/Overlay.js" | "SoloPlay/Tools/SoloIndex.js" | "SoloPlay/Tools/Tracker.js" | "SoloPlay/Workers/EventEmitter.js" | "SoloPlay/Workers/EventHandler.js" | "SoloPlay/Workers/TownChicken.js" | "config/Amazon.js" | "config/Assassin.js" | "config/Barbarian.js" | "config/Builds/Class.Build.js" | "config/Builds/Sorceress.ExampleBuild.js" | "config/Druid.js" | "config/Necromancer.js" | "config/Paladin.js" | "config/Sorceress.js" | "config/_BaseConfigFile.js" | "config/_CustomConfig.js" | "core/Attack.js" | "core/Attacks/Amazon.js" | "core/Attacks/Assassin.js" | "core/Attacks/Barbarian.js" | "core/Attacks/Druid.js" | "core/Attacks/Necromancer.js" | "core/Attacks/Paladin.js" | "core/Attacks/Sorceress.js" | "core/Attacks/Wereform.js" | "core/Auto/AutoBuild.js" | "core/Auto/AutoSkill.js" | "core/Auto/AutoStat.js" | "core/CollMap.js" | "core/Common.js" | "core/Common/Ancients.js" | "core/Common/Baal.js" | "core/Common/Cain.js" | "core/Common/Cows.js" | "core/Common/Diablo.js" | "core/Common/Leecher.js" | "core/Common/Smith.js" | "core/Common/Tools.js" | "core/Config.js" | "core/Cubing.js" | "core/Experience.js" | "core/GameData/AreaData.js" | "core/GameData/GameData.js" | "core/GameData/LocaleStringID.js" | "core/GameData/MonsterData.js" | "core/GameData/NTItemAlias.js" | "core/GameData/QuestData.js" | "core/GameData/RuneData.js" | "core/GameData/ShrineData.js" | "core/GameData/SkillData.js" | "core/Item.js" | "core/Loader.js" | "core/Me.js" | "core/Misc.js" | "core/NPC.js" | "core/NTItemParser.js" | "core/Packet.js" | "core/Pather.js" | "core/Pickit.js" | "core/Precast.js" | "core/Prototypes.js" | "core/Runewords.js" | "core/Skill.js" | "core/Storage.js" | "core/Town.js" | "core/Util.js" | "critical.js" | "globals.js" | "json2.js" | "manualplay/MapMode.js" | "manualplay/config/Amazon.js" | "manualplay/config/Assassin.js" | "manualplay/config/Barbarian.js" | "manualplay/config/Druid.js" | "manualplay/config/Necromancer.js" | "manualplay/config/Paladin.js" | "manualplay/config/Sorceress.js" | "manualplay/hooks/ActionHooks.js" | "manualplay/hooks/ItemHooks.js" | "manualplay/hooks/MonsterHooks.js" | "manualplay/hooks/ShrineHooks.js" | "manualplay/hooks/TextHooks.js" | "manualplay/hooks/VectorHooks.js" | "manualplay/libs/AttackOverrides.js" | "manualplay/libs/ConfigOverrides.js" | "manualplay/libs/Hooks.js" | "manualplay/libs/MiscOverrides.js" | "manualplay/libs/PatherOverrides.js" | "manualplay/libs/PickitOverrides.js" | "manualplay/libs/TownOverrides.js" | "manualplay/main.js" | "manualplay/modules/HelpMenu.js" | "manualplay/modules/HookFactory.js" | "manualplay/threads/MapHelper.js" | "manualplay/threads/MapToolsThread.js" | "manualplay/threads/PickThread.js" | "modules/Control.js" | "modules/CopyData.js" | "modules/Deltas.js" | "modules/Events.js" | "modules/Graph.js" | "modules/HTTP.js" | "modules/LocalChat.js" | "modules/Messaging.js" | "modules/Override.js" | "modules/Promise.js" | "modules/Socket.js" | "modules/Team.js" | "modules/UnitInfo.js" | "modules/Worker.js" | "modules/sdk.js" | "modules/workers/Advertise.js" | "modules/workers/Guard.js" | "modules/workers/SimpleParty.js" | "modules/workers/TownChicken.js" | "modules/workers/WpWatcher.js" | "oog/D2Bot.js" | "oog/DataFile.js" | "oog/FileAction.js" | "oog/Locations.js" | "oog/ShitList.js" | "require.js" | "scripts/Abaddon.js" | "scripts/AncientTunnels.js" | "scripts/Andariel.js" | "scripts/AutoBaal.js" | "scripts/Baal.js" | "scripts/BaalAssistant.js" | "scripts/BaalHelper.js" | "scripts/BattleOrders.js" | "scripts/BattlemaidSarina.js" | "scripts/Bishibosh.js" | "scripts/BoBarbHelper.js" | "scripts/BoneAsh.js" | "scripts/Bonesaw.js" | "scripts/ChestMania.js" | "scripts/ClassicChaosAssistant.js" | "scripts/ClearAnyArea.js" | "scripts/Coldcrow.js" | "scripts/Coldworm.js" | "scripts/ControlBot.js" | "scripts/Corpsefire.js" | "scripts/Countess.js" | "scripts/Cows.js" | "scripts/Crafting.js" | "scripts/CreepingFeature.js" | "scripts/CrushTele.js" | "scripts/DeveloperMode.js" | "scripts/Diablo.js" | "scripts/DiabloHelper.js" | "scripts/Duriel.js" | "scripts/Eldritch.js" | "scripts/Endugu.js" | "scripts/Eyeback.js" | "scripts/Fangskin.js" | "scripts/Follower.js" | "scripts/Frozenstein.js" | "scripts/Gamble.js" | "scripts/GemHunter.js" | "scripts/GetCube.js" | "scripts/GetEssences.js" | "scripts/GetFade.js" | "scripts/GetKeys.js" | "scripts/GhostBusters.js" | "scripts/Hephasto.js" | "scripts/IPHunter.js" | "scripts/Icehawk.js" | "scripts/Idle.js" | "scripts/Izual.js" | "scripts/KillDclone.js" | "scripts/KurastTemples.js" | "scripts/MFHelper.js" | "scripts/Mausoleum.js" | "scripts/Mephisto.js" | "scripts/Nihlathak.js" | "scripts/OrgTorch.js" | "scripts/OrgTorchHelper.js" | "scripts/OuterSteppes.js" | "scripts/Pindleskin.js" | "scripts/Pit.js" | "scripts/Questing.js" | "scripts/Radament.js" | "scripts/Rakanishu.js" | "scripts/Rushee.js" | "scripts/Rusher.js" | "scripts/SealLeecher.js" | "scripts/SharpTooth.js" | "scripts/ShopBot.js" | "scripts/Smith.js" | "scripts/Snapchip.js" | "scripts/Stormtree.js" | "scripts/Summoner.js" | "scripts/Synch.js" | "scripts/Synch2.js" | "scripts/Test.js" | "scripts/ThreshSocket.js" | "scripts/Tombs.js" | "scripts/Travincal.js" | "scripts/TravincalLeech.js" | "scripts/Treehead.js" | "scripts/Tristram.js" | "scripts/TristramLeech.js" | "scripts/UndergroundPassage.js" | "scripts/UserAddon.js" | "scripts/WPGetter.js" | "scripts/Wakka.js" | "scripts/Worldstone.js" | "starter/AdvancedConfig.js" | "starter/StarterConfig.js" | "systems/automule/AutoMule.js" | "systems/automule/Mule.js" | "systems/automule/config/MuleConfig.js" | "systems/automule/config/TorchAnniMules.js" | "systems/automule/main.js" | "systems/autorush/AutoRush.js" | "systems/autorush/RushConfig.js" | "systems/channel/ChannelConfig.js" | "systems/cleaner/CleanerConfig.js" | "systems/crafting/CraftingSystem.js" | "systems/crafting/TeamsConfig.js" | "systems/follow/FollowConfig.js" | "systems/gambling/Gambling.js" | "systems/gambling/TeamsConfig.js" | "systems/gameaction/GameAction.js" | "systems/gameaction/GameActionConfig.js" | "systems/mulelogger/LoggerConfig.js" | "systems/mulelogger/MuleLogger.js" | "systems/pubjoin/PubJoinConfig.js" | "systems/torch/FarmerConfig.js" | "systems/torch/OrgTorchData.js" | "systems/torch/TorchSystem.js"; From 1068bca87116a84d0aeeb6aea994f5c5bd6ab002 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 02:43:50 -0400 Subject: [PATCH 564/758] Refactor script and include path typings - Replaces include-types.d.ts with include-paths.d.ts and introduces kolbot-scripts.d.ts for explicit KolbotScript typing. Updates references in globals.d.ts, Loader.d.ts, and Config.js to use the new KolbotScript and IncludePath types, improving type safety and maintainability. --- d2bs/kolbot/libs/core/Config.js | 2 +- d2bs/kolbot/sdk/globals.d.ts | 5 +++-- d2bs/kolbot/sdk/types/Loader.d.ts | 2 +- d2bs/kolbot/sdk/types/include-paths.d.ts | 2 ++ d2bs/kolbot/sdk/types/include-types.d.ts | 2 -- d2bs/kolbot/sdk/types/kolbot-scripts.d.ts | 2 ++ 6 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 d2bs/kolbot/sdk/types/include-paths.d.ts delete mode 100644 d2bs/kolbot/sdk/types/include-types.d.ts create mode 100644 d2bs/kolbot/sdk/types/kolbot-scripts.d.ts diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 996f1eafc..7147ed518 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -5,7 +5,7 @@ * */ -/** @type {Record} */ +/** @type {Record} */ const Scripts = {}; let Config = { diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 3306d320a..b719ba732 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -19,8 +19,9 @@ /// declare global { - type IncludePath = import('./types/include-types').IncludePath; - + type IncludePath = import('./types/include-paths').IncludePath; + type KolbotScript = import('./types/kolbot-scripts').KolbotScript; + interface Error { fileName: string; lineNumber: number; diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index 2bbe902a7..bc4bef2e0 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -47,7 +47,7 @@ declare global { } type Scripts = { - [key: string]: boolean; + [key in KolbotScript]: boolean; }; const Scripts: Scripts; diff --git a/d2bs/kolbot/sdk/types/include-paths.d.ts b/d2bs/kolbot/sdk/types/include-paths.d.ts new file mode 100644 index 000000000..95e6677de --- /dev/null +++ b/d2bs/kolbot/sdk/types/include-paths.d.ts @@ -0,0 +1,2 @@ +// Auto-generated include types for kolbot libs +export type IncludePath = "OOG.js" | "json2.js" | "core/Me.js" | "globals.js" | "require.js" | "core/NPC.js" | "critical.js" | "Polyfill.js" | "core/Item.js" | "core/Misc.js" | "core/Town.js" | "core/Util.js" | "oog/D2Bot.js" | "core/Skill.js" | "core/Attack.js" | "core/Common.js" | "core/Config.js" | "core/Cubing.js" | "core/Loader.js" | "core/Packet.js" | "core/Pather.js" | "core/Pickit.js" | "modules/sdk.js" | "scripts/Pit.js" | "config/Druid.js" | "core/CollMap.js" | "core/Precast.js" | "core/Storage.js" | "modules/HTTP.js" | "modules/Team.js" | "oog/DataFile.js" | "oog/ShitList.js" | "scripts/Baal.js" | "scripts/Cows.js" | "scripts/Idle.js" | "scripts/Test.js" | "config/Amazon.js" | "modules/Graph.js" | "oog/Locations.js" | "scripts/Izual.js" | "scripts/Smith.js" | "scripts/Synch.js" | "scripts/Tombs.js" | "scripts/Wakka.js" | "config/Paladin.js" | "core/Runewords.js" | "modules/Deltas.js" | "modules/Events.js" | "modules/Socket.js" | "modules/Worker.js" | "oog/FileAction.js" | "scripts/Diablo.js" | "scripts/Duriel.js" | "scripts/Endugu.js" | "scripts/Gamble.js" | "scripts/Rushee.js" | "scripts/Rusher.js" | "scripts/Synch2.js" | "config/Assassin.js" | "core/Experience.js" | "core/Prototypes.js" | "manualplay/main.js" | "modules/Control.js" | "modules/Promise.js" | "scripts/Abaddon.js" | "scripts/BoneAsh.js" | "scripts/Bonesaw.js" | "scripts/Eyeback.js" | "scripts/GetCube.js" | "scripts/GetFade.js" | "scripts/GetKeys.js" | "scripts/Icehawk.js" | "scripts/ShopBot.js" | "config/Barbarian.js" | "config/Sorceress.js" | "core/Common/Baal.js" | "core/Common/Cain.js" | "core/Common/Cows.js" | "modules/CopyData.js" | "modules/Override.js" | "modules/UnitInfo.js" | "scripts/Andariel.js" | "scripts/AutoBaal.js" | "scripts/Coldcrow.js" | "scripts/Coldworm.js" | "scripts/Countess.js" | "scripts/Crafting.js" | "scripts/Eldritch.js" | "scripts/Fangskin.js" | "scripts/Follower.js" | "scripts/Hephasto.js" | "scripts/IPHunter.js" | "scripts/Mephisto.js" | "scripts/MFHelper.js" | "scripts/OrgTorch.js" | "scripts/Questing.js" | "scripts/Radament.js" | "scripts/Snapchip.js" | "scripts/Summoner.js" | "scripts/Treehead.js" | "scripts/Tristram.js" | "scripts/WPGetter.js" | "SoloPlay/Core/Me.js" | "core/Common/Smith.js" | "core/Common/Tools.js" | "core/NTItemParser.js" | "modules/LocalChat.js" | "modules/Messaging.js" | "scripts/Bishibosh.js" | "scripts/CrushTele.js" | "scripts/GemHunter.js" | "scripts/Mausoleum.js" | "scripts/Nihlathak.js" | "scripts/Rakanishu.js" | "scripts/Stormtree.js" | "scripts/Travincal.js" | "scripts/UserAddon.js" | "SoloPlay/SoloPlay.js" | "config/Necromancer.js" | "core/Attacks/Druid.js" | "core/Auto/AutoStat.js" | "core/Common/Diablo.js" | "manualplay/MapMode.js" | "scripts/BaalHelper.js" | "scripts/ChestMania.js" | "scripts/ControlBot.js" | "scripts/Corpsefire.js" | "scripts/KillDclone.js" | "scripts/Pindleskin.js" | "scripts/SharpTooth.js" | "scripts/Worldstone.js" | "core/Attacks/Amazon.js" | "core/Auto/AutoBuild.js" | "core/Auto/AutoSkill.js" | "core/Common/Leecher.js" | "scripts/Frozenstein.js" | "scripts/GetEssences.js" | "scripts/SealLeecher.js" | "SoloPlay/Core/Quest.js" | "config/_CustomConfig.js" | "core/Attacks/Paladin.js" | "core/Common/Ancients.js" | "scripts/BattleOrders.js" | "scripts/BoBarbHelper.js" | "scripts/ClearAnyArea.js" | "scripts/DiabloHelper.js" | "scripts/GhostBusters.js" | "scripts/OuterSteppes.js" | "scripts/ThreshSocket.js" | "SoloPlay/Scripts/den.js" | "SoloPlay/Scripts/eye.js" | "core/Attacks/Assassin.js" | "core/Attacks/Wereform.js" | "manualplay/libs/Hooks.js" | "modules/workers/Guard.js" | "scripts/BaalAssistant.js" | "scripts/DeveloperMode.js" | "scripts/KurastTemples.js" | "scripts/TristramLeech.js" | "SoloPlay/Config/Druid.js" | "SoloPlay/Core/Globals.js" | "SoloPlay/Modules/Mock.js" | "SoloPlay/Scripts/anya.js" | "SoloPlay/Scripts/baal.js" | "SoloPlay/Scripts/cave.js" | "SoloPlay/Scripts/cows.js" | "SoloPlay/Scripts/cube.js" | "SoloPlay/Scripts/jail.js" | "SoloPlay/Scripts/nith.js" | "SoloPlay/Scripts/pits.js" | "starter/StarterConfig.js" | "systems/automule/main.js" | "systems/automule/Mule.js" | "config/_BaseConfigFile.js" | "core/Attacks/Barbarian.js" | "core/Attacks/Sorceress.js" | "core/GameData/AreaData.js" | "core/GameData/GameData.js" | "core/GameData/RuneData.js" | "scripts/AncientTunnels.js" | "scripts/OrgTorchHelper.js" | "scripts/TravincalLeech.js" | "SoloPlay/Config/Amazon.js" | "SoloPlay/Modules/Clear.js" | "SoloPlay/Modules/Guard.js" | "SoloPlay/OOG/SoloEntry.js" | "SoloPlay/Scripts/brain.js" | "SoloPlay/Scripts/heart.js" | "SoloPlay/Scripts/izual.js" | "SoloPlay/Scripts/river.js" | "SoloPlay/Scripts/shenk.js" | "SoloPlay/Scripts/smith.js" | "SoloPlay/Scripts/staff.js" | "SoloPlay/Scripts/tombs.js" | "SoloPlay/Tools/Overlay.js" | "SoloPlay/Tools/Tracker.js" | "starter/AdvancedConfig.js" | "core/GameData/QuestData.js" | "core/GameData/SkillData.js" | "manualplay/config/Druid.js" | "scripts/CreepingFeature.js" | "SoloPlay/Config/Paladin.js" | "SoloPlay/Core/AutoBuild.js" | "SoloPlay/Core/Mercenary.js" | "SoloPlay/Core/NPCAction.js" | "SoloPlay/Core/Polyfills.js" | "SoloPlay/Core/SoloWants.js" | "SoloPlay/Modules/bigInt.js" | "SoloPlay/Modules/Coords.js" | "SoloPlay/Modules/Events.js" | "SoloPlay/Modules/MoveTo.js" | "SoloPlay/Modules/Vector.js" | "SoloPlay/Scripts/amulet.js" | "SoloPlay/Scripts/diablo.js" | "SoloPlay/Scripts/duriel.js" | "SoloPlay/Scripts/pindle.js" | "SoloPlay/Threads/Reload.js" | "SoloPlay/Tools/CharData.js" | "core/Attacks/Necromancer.js" | "core/GameData/ShrineData.js" | "manualplay/config/Amazon.js" | "scripts/BattlemaidSarina.js" | "SoloPlay/Config/Assassin.js" | "SoloPlay/Core/CharmEquip.js" | "SoloPlay/Core/SoloEvents.js" | "SoloPlay/Modules/General.js" | "SoloPlay/Modules/MercLib.js" | "SoloPlay/Modules/NameGen.js" | "SoloPlay/Scripts/boneash.js" | "SoloPlay/Scripts/eyeback.js" | "SoloPlay/Scripts/fireeye.js" | "SoloPlay/Scripts/getkeys.js" | "SoloPlay/Tools/Developer.js" | "SoloPlay/Tools/SoloIndex.js" | "config/Builds/Class.Build.js" | "core/GameData/MonsterData.js" | "core/GameData/NTItemAlias.js" | "manualplay/config/Paladin.js" | "modules/workers/Advertise.js" | "modules/workers/WpWatcher.js" | "SoloPlay/Config/Barbarian.js" | "SoloPlay/Config/Sorceress.js" | "SoloPlay/Modules/MockItem.js" | "SoloPlay/OOG/OOGOverrides.js" | "SoloPlay/Scripts/a1chests.js" | "SoloPlay/Scripts/a5chests.js" | "SoloPlay/Scripts/ancients.js" | "SoloPlay/Scripts/andariel.js" | "SoloPlay/Scripts/countess.js" | "SoloPlay/Scripts/hephasto.js" | "SoloPlay/Scripts/lamessen.js" | "SoloPlay/Scripts/mephisto.js" | "SoloPlay/Scripts/orgtorch.js" | "SoloPlay/Scripts/radament.js" | "SoloPlay/Scripts/summoner.js" | "SoloPlay/Scripts/treehead.js" | "SoloPlay/Scripts/tristram.js" | "systems/automule/AutoMule.js" | "systems/autorush/AutoRush.js" | "systems/gambling/Gambling.js" | "systems/torch/TorchSystem.js" | "manualplay/config/Assassin.js" | "manualplay/hooks/ItemHooks.js" | "manualplay/hooks/TextHooks.js" | "scripts/UndergroundPassage.js" | "SoloPlay/Core/DynamicTiers.js" | "SoloPlay/Modules/utilities.js" | "SoloPlay/Scripts/bishibosh.js" | "SoloPlay/Scripts/hellforge.js" | "SoloPlay/Scripts/mausoleum.js" | "SoloPlay/Scripts/savebarby.js" | "SoloPlay/Scripts/travincal.js" | "systems/torch/FarmerConfig.js" | "systems/torch/OrgTorchData.js" | "manualplay/config/Barbarian.js" | "manualplay/config/Sorceress.js" | "manualplay/modules/HelpMenu.js" | "modules/workers/SimpleParty.js" | "modules/workers/TownChicken.js" | "SoloPlay/Config/Necromancer.js" | "SoloPlay/Core/ItemOverrides.js" | "SoloPlay/Core/ItemUtilities.js" | "SoloPlay/Core/MiscOverrides.js" | "SoloPlay/Core/NTIPOverrides.js" | "SoloPlay/Core/TownOverrides.js" | "SoloPlay/Scripts/bloodraven.js" | "SoloPlay/Scripts/corpsefire.js" | "SoloPlay/Scripts/maggotlair.js" | "SoloPlay/Scripts/templeruns.js" | "SoloPlay/Scripts/worldstone.js" | "systems/autorush/RushConfig.js" | "systems/follow/FollowConfig.js" | "core/GameData/LocaleStringID.js" | "manualplay/hooks/ActionHooks.js" | "manualplay/hooks/ShrineHooks.js" | "manualplay/hooks/VectorHooks.js" | "manualplay/threads/MapHelper.js" | "SoloPlay/Core/ItemPrototypes.js" | "SoloPlay/Core/SkillOverrides.js" | "SoloPlay/Scripts/beetleburst.js" | "SoloPlay/Scripts/lowerkurast.js" | "SoloPlay/Threads/ToolsThread.js" | "SoloPlay/Workers/TownChicken.js" | "systems/crafting/TeamsConfig.js" | "systems/gambling/TeamsConfig.js" | "manualplay/config/Necromancer.js" | "manualplay/hooks/MonsterHooks.js" | "manualplay/libs/MiscOverrides.js" | "manualplay/libs/TownOverrides.js" | "manualplay/threads/PickThread.js" | "scripts/ClassicChaosAssistant.js" | "SoloPlay/Core/AttackOverrides.js" | "SoloPlay/Core/ConfigOverrides.js" | "SoloPlay/Core/CubingOverrides.js" | "SoloPlay/Core/LoaderOverrides.js" | "SoloPlay/Core/PatherOverrides.js" | "SoloPlay/Core/PickitOverrides.js" | "SoloPlay/Workers/EventEmitter.js" | "SoloPlay/Workers/EventHandler.js" | "systems/channel/ChannelConfig.js" | "systems/cleaner/CleanerConfig.js" | "systems/gameaction/GameAction.js" | "systems/mulelogger/MuleLogger.js" | "systems/pubjoin/PubJoinConfig.js" | "manualplay/modules/HookFactory.js" | "SoloPlay/Core/PrecastOverrides.js" | "SoloPlay/Core/StorageOverrides.js" | "SoloPlay/Scripts/developermode.js" | "manualplay/libs/AttackOverrides.js" | "manualplay/libs/ConfigOverrides.js" | "manualplay/libs/PatherOverrides.js" | "manualplay/libs/PickitOverrides.js" | "SoloPlay/BuildFiles/druid/druid.js" | "SoloPlay/Core/AutoMuleOverrides.js" | "SoloPlay/Core/AutoStatOverrides.js" | "SoloPlay/Scripts/ancienttunnels.js" | "systems/crafting/CraftingSystem.js" | "systems/mulelogger/LoggerConfig.js" | "SoloPlay/Core/PrototypeOverrides.js" | "SoloPlay/Core/RunewordsOverrides.js" | "SoloPlay/Scripts/creepingfeature.js" | "manualplay/threads/MapToolsThread.js" | "SoloPlay/BuildFiles/amazon/amazon.js" | "SoloPlay/BuildFiles/Runewords/Ice.js" | "SoloPlay/Core/MuleloggerOverrides.js" | "SoloPlay/Modules/GameData/PotData.js" | "SoloPlay/BuildFiles/Runewords/Bone.js" | "SoloPlay/BuildFiles/Runewords/Fury.js" | "SoloPlay/BuildFiles/Runewords/Lore.js" | "SoloPlay/BuildFiles/Runewords/Myth.js" | "SoloPlay/Modules/GameData/AreaData.js" | "SoloPlay/Modules/GameData/GameData.js" | "systems/automule/config/MuleConfig.js" | "SoloPlay/BuildFiles/paladin/paladin.js" | "SoloPlay/BuildFiles/Runewords/Chaos.js" | "SoloPlay/BuildFiles/Runewords/Exile.js" | "SoloPlay/BuildFiles/Runewords/Faith.js" | "SoloPlay/BuildFiles/Runewords/Grief.js" | "SoloPlay/BuildFiles/Runewords/Honor.js" | "SoloPlay/BuildFiles/Runewords/Rhyme.js" | "SoloPlay/BuildFiles/Runewords/Smoke.js" | "SoloPlay/BuildFiles/Runewords/Steel.js" | "SoloPlay/BuildFiles/Runewords/White.js" | "systems/gameaction/GameActionConfig.js" | "config/Builds/Sorceress.ExampleBuild.js" | "SoloPlay/BuildFiles/Runewords/Duress.js" | "SoloPlay/BuildFiles/Runewords/Enigma.js" | "SoloPlay/BuildFiles/Runewords/Malice.js" | "SoloPlay/BuildFiles/assassin/assassin.js" | "SoloPlay/BuildFiles/Runewords/Silence.js" | "SoloPlay/BuildFiles/Runewords/Stealth.js" | "SoloPlay/Modules/GameData/MissileData.js" | "SoloPlay/Modules/GameData/MonsterData.js" | "SoloPlay/BuildFiles/Runewords/LastWish.js" | "SoloPlay/BuildFiles/Runewords/MercDoom.js" | "systems/automule/config/TorchAnniMules.js" | "SoloPlay/BuildFiles/barbarian/barbarian.js" | "SoloPlay/BuildFiles/Runewords/DreamHelm.js" | "SoloPlay/BuildFiles/Runewords/Fortitude.js" | "SoloPlay/BuildFiles/Runewords/MercPride.js" | "SoloPlay/BuildFiles/Runewords/Sanctuary.js" | "SoloPlay/BuildFiles/Runewords/Treachery.js" | "SoloPlay/BuildFiles/sorceress/sorceress.js" | "SoloPlay/BuildFiles/Runewords/CallToArms.js" | "SoloPlay/BuildFiles/Runewords/KingsGrace.js" | "SoloPlay/BuildFiles/Runewords/Lawbringer.js" | "SoloPlay/Modules/GameData/LocaleStringID.js" | "SoloPlay/BuildFiles/druid/druid.WindBuild.js" | "SoloPlay/BuildFiles/druid/druid.WolfBuild.js" | "SoloPlay/BuildFiles/Runewords/DragonArmor.js" | "SoloPlay/BuildFiles/Runewords/DreamShield.js" | "SoloPlay/BuildFiles/Runewords/MercInsight.js" | "SoloPlay/BuildFiles/Runewords/SpiritSword.js" | "SoloPlay/BuildFiles/druid/druid.StartBuild.js" | "SoloPlay/BuildFiles/Runewords/CrescentMoon.js" | "SoloPlay/BuildFiles/Runewords/MercInfinity.js" | "SoloPlay/BuildFiles/Runewords/SpiritShield.js" | "SoloPlay/BuildFiles/necromancer/necromancer.js" | "SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js" | "SoloPlay/BuildFiles/Runewords/HandOfJustice.js" | "SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js" | "SoloPlay/BuildFiles/Runewords/MercFortitude.js" | "SoloPlay/BuildFiles/Runewords/MercTreachery.js" | "SoloPlay/BuildFiles/Runewords/PhoenixShield.js" | "SoloPlay/BuildFiles/Runewords/VoiceOfReason.js" | "SoloPlay/BuildFiles/amazon/amazon.StartBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js" | "SoloPlay/BuildFiles/Runewords/AncientsPledge.js" | "SoloPlay/BuildFiles/Runewords/PDiamondShield.js" | "SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js" | "SoloPlay/BuildFiles/druid/druid.ElementalBuild.js" | "SoloPlay/BuildFiles/druid/druid.StormbearBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.StartBuild.js" | "SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js" | "SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js" | "SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js" | "SoloPlay/Core/ClassAttackOverrides/DruidAttacks.js" | "SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.StartBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks.js" | "SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js" | "SoloPlay/Core/ClassAttackOverrides/PaladinAttacks.js" | "SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AssassinAttacks.js" | "SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js" | "SoloPlay/Core/ClassAttackOverrides/BarbarianAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/SorceressAttacks.js" | "SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks-WIP.js" | "SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js" | "SoloPlay/Core/ClassAttackOverrides/NecromancerAttacks.js" | "SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js"; diff --git a/d2bs/kolbot/sdk/types/include-types.d.ts b/d2bs/kolbot/sdk/types/include-types.d.ts deleted file mode 100644 index a737f1f63..000000000 --- a/d2bs/kolbot/sdk/types/include-types.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Auto-generated include types for kolbot libs -export type IncludePath = "OOG.js" | "Polyfill.js" | "SoloPlay/BuildFiles/Runewords/AncientsPledge.js" | "SoloPlay/BuildFiles/Runewords/Bone.js" | "SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js" | "SoloPlay/BuildFiles/Runewords/CallToArms.js" | "SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js" | "SoloPlay/BuildFiles/Runewords/Chaos.js" | "SoloPlay/BuildFiles/Runewords/CrescentMoon.js" | "SoloPlay/BuildFiles/Runewords/DragonArmor.js" | "SoloPlay/BuildFiles/Runewords/DreamHelm.js" | "SoloPlay/BuildFiles/Runewords/DreamShield.js" | "SoloPlay/BuildFiles/Runewords/Duress.js" | "SoloPlay/BuildFiles/Runewords/Enigma.js" | "SoloPlay/BuildFiles/Runewords/Exile.js" | "SoloPlay/BuildFiles/Runewords/Faith.js" | "SoloPlay/BuildFiles/Runewords/Fortitude.js" | "SoloPlay/BuildFiles/Runewords/Fury.js" | "SoloPlay/BuildFiles/Runewords/Grief.js" | "SoloPlay/BuildFiles/Runewords/HandOfJustice.js" | "SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js" | "SoloPlay/BuildFiles/Runewords/Honor.js" | "SoloPlay/BuildFiles/Runewords/Ice.js" | "SoloPlay/BuildFiles/Runewords/KingsGrace.js" | "SoloPlay/BuildFiles/Runewords/LastWish.js" | "SoloPlay/BuildFiles/Runewords/Lawbringer.js" | "SoloPlay/BuildFiles/Runewords/Lore.js" | "SoloPlay/BuildFiles/Runewords/Malice.js" | "SoloPlay/BuildFiles/Runewords/MercDoom.js" | "SoloPlay/BuildFiles/Runewords/MercFortitude.js" | "SoloPlay/BuildFiles/Runewords/MercInfinity.js" | "SoloPlay/BuildFiles/Runewords/MercInsight.js" | "SoloPlay/BuildFiles/Runewords/MercPride.js" | "SoloPlay/BuildFiles/Runewords/MercTreachery.js" | "SoloPlay/BuildFiles/Runewords/Myth.js" | "SoloPlay/BuildFiles/Runewords/PDiamondShield.js" | "SoloPlay/BuildFiles/Runewords/PhoenixShield.js" | "SoloPlay/BuildFiles/Runewords/Rhyme.js" | "SoloPlay/BuildFiles/Runewords/Sanctuary.js" | "SoloPlay/BuildFiles/Runewords/Silence.js" | "SoloPlay/BuildFiles/Runewords/Smoke.js" | "SoloPlay/BuildFiles/Runewords/SpiritShield.js" | "SoloPlay/BuildFiles/Runewords/SpiritSword.js" | "SoloPlay/BuildFiles/Runewords/Stealth.js" | "SoloPlay/BuildFiles/Runewords/Steel.js" | "SoloPlay/BuildFiles/Runewords/Treachery.js" | "SoloPlay/BuildFiles/Runewords/VoiceOfReason.js" | "SoloPlay/BuildFiles/Runewords/White.js" | "SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.StartBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.js" | "SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.StartBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.js" | "SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.js" | "SoloPlay/BuildFiles/druid/druid.ElementalBuild.js" | "SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.LevelingBuild.js" | "SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.StartBuild.js" | "SoloPlay/BuildFiles/druid/druid.StormbearBuild.js" | "SoloPlay/BuildFiles/druid/druid.WindBuild.js" | "SoloPlay/BuildFiles/druid/druid.WolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.js" | "SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.js" | "SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.StartBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.js" | "SoloPlay/Config/Amazon.js" | "SoloPlay/Config/Assassin.js" | "SoloPlay/Config/Barbarian.js" | "SoloPlay/Config/Druid.js" | "SoloPlay/Config/Necromancer.js" | "SoloPlay/Config/Paladin.js" | "SoloPlay/Config/Sorceress.js" | "SoloPlay/Core/AttackOverrides.js" | "SoloPlay/Core/AutoBuild.js" | "SoloPlay/Core/AutoMuleOverrides.js" | "SoloPlay/Core/AutoStatOverrides.js" | "SoloPlay/Core/CharmEquip.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks-WIP.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/AssassinAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/BarbarianAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/DruidAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/NecromancerAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/PaladinAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/SorceressAttacks.js" | "SoloPlay/Core/ConfigOverrides.js" | "SoloPlay/Core/CubingOverrides.js" | "SoloPlay/Core/DynamicTiers.js" | "SoloPlay/Core/Globals.js" | "SoloPlay/Core/ItemOverrides.js" | "SoloPlay/Core/ItemPrototypes.js" | "SoloPlay/Core/ItemUtilities.js" | "SoloPlay/Core/LoaderOverrides.js" | "SoloPlay/Core/Me.js" | "SoloPlay/Core/Mercenary.js" | "SoloPlay/Core/MiscOverrides.js" | "SoloPlay/Core/MuleloggerOverrides.js" | "SoloPlay/Core/NPCAction.js" | "SoloPlay/Core/NTIPOverrides.js" | "SoloPlay/Core/PatherOverrides.js" | "SoloPlay/Core/PickitOverrides.js" | "SoloPlay/Core/Polyfills.js" | "SoloPlay/Core/PrecastOverrides.js" | "SoloPlay/Core/PrototypeOverrides.js" | "SoloPlay/Core/Quest.js" | "SoloPlay/Core/RunewordsOverrides.js" | "SoloPlay/Core/SkillOverrides.js" | "SoloPlay/Core/SoloEvents.js" | "SoloPlay/Core/SoloWants.js" | "SoloPlay/Core/StorageOverrides.js" | "SoloPlay/Core/TownOverrides.js" | "SoloPlay/Modules/Clear.js" | "SoloPlay/Modules/Coords.js" | "SoloPlay/Modules/Events.js" | "SoloPlay/Modules/GameData/AreaData.js" | "SoloPlay/Modules/GameData/GameData.js" | "SoloPlay/Modules/GameData/LocaleStringID.js" | "SoloPlay/Modules/GameData/MissileData.js" | "SoloPlay/Modules/GameData/MonsterData.js" | "SoloPlay/Modules/GameData/PotData.js" | "SoloPlay/Modules/General.js" | "SoloPlay/Modules/Guard.js" | "SoloPlay/Modules/MercLib.js" | "SoloPlay/Modules/Mock.js" | "SoloPlay/Modules/MockItem.js" | "SoloPlay/Modules/MoveTo.js" | "SoloPlay/Modules/NameGen.js" | "SoloPlay/Modules/Vector.js" | "SoloPlay/Modules/bigInt.js" | "SoloPlay/Modules/utilities.js" | "SoloPlay/OOG/OOGOverrides.js" | "SoloPlay/OOG/SoloEntry.js" | "SoloPlay/Scripts/a1chests.js" | "SoloPlay/Scripts/a5chests.js" | "SoloPlay/Scripts/amulet.js" | "SoloPlay/Scripts/ancients.js" | "SoloPlay/Scripts/ancienttunnels.js" | "SoloPlay/Scripts/andariel.js" | "SoloPlay/Scripts/anya.js" | "SoloPlay/Scripts/baal.js" | "SoloPlay/Scripts/beetleburst.js" | "SoloPlay/Scripts/bishibosh.js" | "SoloPlay/Scripts/bloodraven.js" | "SoloPlay/Scripts/boneash.js" | "SoloPlay/Scripts/brain.js" | "SoloPlay/Scripts/cave.js" | "SoloPlay/Scripts/corpsefire.js" | "SoloPlay/Scripts/countess.js" | "SoloPlay/Scripts/cows.js" | "SoloPlay/Scripts/creepingfeature.js" | "SoloPlay/Scripts/cube.js" | "SoloPlay/Scripts/den.js" | "SoloPlay/Scripts/developermode.js" | "SoloPlay/Scripts/diablo.js" | "SoloPlay/Scripts/duriel.js" | "SoloPlay/Scripts/eye.js" | "SoloPlay/Scripts/eyeback.js" | "SoloPlay/Scripts/fireeye.js" | "SoloPlay/Scripts/getkeys.js" | "SoloPlay/Scripts/heart.js" | "SoloPlay/Scripts/hellforge.js" | "SoloPlay/Scripts/hephasto.js" | "SoloPlay/Scripts/izual.js" | "SoloPlay/Scripts/jail.js" | "SoloPlay/Scripts/lamessen.js" | "SoloPlay/Scripts/lowerkurast.js" | "SoloPlay/Scripts/maggotlair.js" | "SoloPlay/Scripts/mausoleum.js" | "SoloPlay/Scripts/mephisto.js" | "SoloPlay/Scripts/nith.js" | "SoloPlay/Scripts/orgtorch.js" | "SoloPlay/Scripts/pindle.js" | "SoloPlay/Scripts/pits.js" | "SoloPlay/Scripts/radament.js" | "SoloPlay/Scripts/river.js" | "SoloPlay/Scripts/savebarby.js" | "SoloPlay/Scripts/shenk.js" | "SoloPlay/Scripts/smith.js" | "SoloPlay/Scripts/staff.js" | "SoloPlay/Scripts/summoner.js" | "SoloPlay/Scripts/templeruns.js" | "SoloPlay/Scripts/tombs.js" | "SoloPlay/Scripts/travincal.js" | "SoloPlay/Scripts/treehead.js" | "SoloPlay/Scripts/tristram.js" | "SoloPlay/Scripts/worldstone.js" | "SoloPlay/SoloPlay.js" | "SoloPlay/Threads/Reload.js" | "SoloPlay/Threads/ToolsThread.js" | "SoloPlay/Tools/CharData.js" | "SoloPlay/Tools/Developer.js" | "SoloPlay/Tools/Overlay.js" | "SoloPlay/Tools/SoloIndex.js" | "SoloPlay/Tools/Tracker.js" | "SoloPlay/Workers/EventEmitter.js" | "SoloPlay/Workers/EventHandler.js" | "SoloPlay/Workers/TownChicken.js" | "config/Amazon.js" | "config/Assassin.js" | "config/Barbarian.js" | "config/Builds/Class.Build.js" | "config/Builds/Sorceress.ExampleBuild.js" | "config/Druid.js" | "config/Necromancer.js" | "config/Paladin.js" | "config/Sorceress.js" | "config/_BaseConfigFile.js" | "config/_CustomConfig.js" | "core/Attack.js" | "core/Attacks/Amazon.js" | "core/Attacks/Assassin.js" | "core/Attacks/Barbarian.js" | "core/Attacks/Druid.js" | "core/Attacks/Necromancer.js" | "core/Attacks/Paladin.js" | "core/Attacks/Sorceress.js" | "core/Attacks/Wereform.js" | "core/Auto/AutoBuild.js" | "core/Auto/AutoSkill.js" | "core/Auto/AutoStat.js" | "core/CollMap.js" | "core/Common.js" | "core/Common/Ancients.js" | "core/Common/Baal.js" | "core/Common/Cain.js" | "core/Common/Cows.js" | "core/Common/Diablo.js" | "core/Common/Leecher.js" | "core/Common/Smith.js" | "core/Common/Tools.js" | "core/Config.js" | "core/Cubing.js" | "core/Experience.js" | "core/GameData/AreaData.js" | "core/GameData/GameData.js" | "core/GameData/LocaleStringID.js" | "core/GameData/MonsterData.js" | "core/GameData/NTItemAlias.js" | "core/GameData/QuestData.js" | "core/GameData/RuneData.js" | "core/GameData/ShrineData.js" | "core/GameData/SkillData.js" | "core/Item.js" | "core/Loader.js" | "core/Me.js" | "core/Misc.js" | "core/NPC.js" | "core/NTItemParser.js" | "core/Packet.js" | "core/Pather.js" | "core/Pickit.js" | "core/Precast.js" | "core/Prototypes.js" | "core/Runewords.js" | "core/Skill.js" | "core/Storage.js" | "core/Town.js" | "core/Util.js" | "critical.js" | "globals.js" | "json2.js" | "manualplay/MapMode.js" | "manualplay/config/Amazon.js" | "manualplay/config/Assassin.js" | "manualplay/config/Barbarian.js" | "manualplay/config/Druid.js" | "manualplay/config/Necromancer.js" | "manualplay/config/Paladin.js" | "manualplay/config/Sorceress.js" | "manualplay/hooks/ActionHooks.js" | "manualplay/hooks/ItemHooks.js" | "manualplay/hooks/MonsterHooks.js" | "manualplay/hooks/ShrineHooks.js" | "manualplay/hooks/TextHooks.js" | "manualplay/hooks/VectorHooks.js" | "manualplay/libs/AttackOverrides.js" | "manualplay/libs/ConfigOverrides.js" | "manualplay/libs/Hooks.js" | "manualplay/libs/MiscOverrides.js" | "manualplay/libs/PatherOverrides.js" | "manualplay/libs/PickitOverrides.js" | "manualplay/libs/TownOverrides.js" | "manualplay/main.js" | "manualplay/modules/HelpMenu.js" | "manualplay/modules/HookFactory.js" | "manualplay/threads/MapHelper.js" | "manualplay/threads/MapToolsThread.js" | "manualplay/threads/PickThread.js" | "modules/Control.js" | "modules/CopyData.js" | "modules/Deltas.js" | "modules/Events.js" | "modules/Graph.js" | "modules/HTTP.js" | "modules/LocalChat.js" | "modules/Messaging.js" | "modules/Override.js" | "modules/Promise.js" | "modules/Socket.js" | "modules/Team.js" | "modules/UnitInfo.js" | "modules/Worker.js" | "modules/sdk.js" | "modules/workers/Advertise.js" | "modules/workers/Guard.js" | "modules/workers/SimpleParty.js" | "modules/workers/TownChicken.js" | "modules/workers/WpWatcher.js" | "oog/D2Bot.js" | "oog/DataFile.js" | "oog/FileAction.js" | "oog/Locations.js" | "oog/ShitList.js" | "require.js" | "scripts/Abaddon.js" | "scripts/AncientTunnels.js" | "scripts/Andariel.js" | "scripts/AutoBaal.js" | "scripts/Baal.js" | "scripts/BaalAssistant.js" | "scripts/BaalHelper.js" | "scripts/BattleOrders.js" | "scripts/BattlemaidSarina.js" | "scripts/Bishibosh.js" | "scripts/BoBarbHelper.js" | "scripts/BoneAsh.js" | "scripts/Bonesaw.js" | "scripts/ChestMania.js" | "scripts/ClassicChaosAssistant.js" | "scripts/ClearAnyArea.js" | "scripts/Coldcrow.js" | "scripts/Coldworm.js" | "scripts/ControlBot.js" | "scripts/Corpsefire.js" | "scripts/Countess.js" | "scripts/Cows.js" | "scripts/Crafting.js" | "scripts/CreepingFeature.js" | "scripts/CrushTele.js" | "scripts/DeveloperMode.js" | "scripts/Diablo.js" | "scripts/DiabloHelper.js" | "scripts/Duriel.js" | "scripts/Eldritch.js" | "scripts/Endugu.js" | "scripts/Eyeback.js" | "scripts/Fangskin.js" | "scripts/Follower.js" | "scripts/Frozenstein.js" | "scripts/Gamble.js" | "scripts/GemHunter.js" | "scripts/GetCube.js" | "scripts/GetEssences.js" | "scripts/GetFade.js" | "scripts/GetKeys.js" | "scripts/GhostBusters.js" | "scripts/Hephasto.js" | "scripts/IPHunter.js" | "scripts/Icehawk.js" | "scripts/Idle.js" | "scripts/Izual.js" | "scripts/KillDclone.js" | "scripts/KurastTemples.js" | "scripts/MFHelper.js" | "scripts/Mausoleum.js" | "scripts/Mephisto.js" | "scripts/Nihlathak.js" | "scripts/OrgTorch.js" | "scripts/OrgTorchHelper.js" | "scripts/OuterSteppes.js" | "scripts/Pindleskin.js" | "scripts/Pit.js" | "scripts/Questing.js" | "scripts/Radament.js" | "scripts/Rakanishu.js" | "scripts/Rushee.js" | "scripts/Rusher.js" | "scripts/SealLeecher.js" | "scripts/SharpTooth.js" | "scripts/ShopBot.js" | "scripts/Smith.js" | "scripts/Snapchip.js" | "scripts/Stormtree.js" | "scripts/Summoner.js" | "scripts/Synch.js" | "scripts/Synch2.js" | "scripts/Test.js" | "scripts/ThreshSocket.js" | "scripts/Tombs.js" | "scripts/Travincal.js" | "scripts/TravincalLeech.js" | "scripts/Treehead.js" | "scripts/Tristram.js" | "scripts/TristramLeech.js" | "scripts/UndergroundPassage.js" | "scripts/UserAddon.js" | "scripts/WPGetter.js" | "scripts/Wakka.js" | "scripts/Worldstone.js" | "starter/AdvancedConfig.js" | "starter/StarterConfig.js" | "systems/automule/AutoMule.js" | "systems/automule/Mule.js" | "systems/automule/config/MuleConfig.js" | "systems/automule/config/TorchAnniMules.js" | "systems/automule/main.js" | "systems/autorush/AutoRush.js" | "systems/autorush/RushConfig.js" | "systems/channel/ChannelConfig.js" | "systems/cleaner/CleanerConfig.js" | "systems/crafting/CraftingSystem.js" | "systems/crafting/TeamsConfig.js" | "systems/follow/FollowConfig.js" | "systems/gambling/Gambling.js" | "systems/gambling/TeamsConfig.js" | "systems/gameaction/GameAction.js" | "systems/gameaction/GameActionConfig.js" | "systems/mulelogger/LoggerConfig.js" | "systems/mulelogger/MuleLogger.js" | "systems/pubjoin/PubJoinConfig.js" | "systems/torch/FarmerConfig.js" | "systems/torch/OrgTorchData.js" | "systems/torch/TorchSystem.js"; diff --git a/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts b/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts new file mode 100644 index 000000000..b22fae9fb --- /dev/null +++ b/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts @@ -0,0 +1,2 @@ +// Auto-generated script types +export type KolbotScript = "Abaddon" | "AncientTunnels" | "Andariel" | "AutoBaal" | "Baal" | "BaalAssistant" | "BaalHelper" | "BattleOrders" | "BattlemaidSarina" | "Bishibosh" | "BoBarbHelper" | "BoneAsh" | "Bonesaw" | "ChestMania" | "ClassicChaosAssistant" | "ClearAnyArea" | "Coldcrow" | "Coldworm" | "ControlBot" | "Corpsefire" | "Countess" | "Cows" | "Crafting" | "CreepingFeature" | "CrushTele" | "DeveloperMode" | "Diablo" | "DiabloHelper" | "Duriel" | "Eldritch" | "Endugu" | "Eyeback" | "Fangskin" | "Follower" | "Frozenstein" | "Gamble" | "GemHunter" | "GetCube" | "GetEssences" | "GetFade" | "GetKeys" | "GhostBusters" | "Hephasto" | "IPHunter" | "Icehawk" | "Idle" | "Izual" | "KillDclone" | "KurastTemples" | "MFHelper" | "Mausoleum" | "Mephisto" | "Nihlathak" | "OrgTorch" | "OrgTorchHelper" | "OuterSteppes" | "Pindleskin" | "Pit" | "Questing" | "Radament" | "Rakanishu" | "Rushee" | "Rusher" | "SealLeecher" | "SharpTooth" | "ShopBot" | "Smith" | "Snapchip" | "Stormtree" | "Summoner" | "Synch" | "Synch2" | "Test" | "ThreshSocket" | "Tombs" | "Travincal" | "TravincalLeech" | "Treehead" | "Tristram" | "TristramLeech" | "UndergroundPassage" | "UserAddon" | "WPGetter" | "Wakka" | "Worldstone"; From 83c26b177f7c5e55c84eaf638125263001523c73 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 03:12:04 -0400 Subject: [PATCH 565/758] Refactor StarterConfig usage for modular system configs - Moved system-specific StarterConfig settings from .dbj files into their respective config modules. - Updated .dbj files to import and apply these settings using Object.assign, improving maintainability and separation of concerns. - Added new StarterConfig.js for automule and updated .gitignore accordingly. --- +setup/automule/StarterConfig.js | 23 +++++++++++++++++++++++ +setup/follow/FollowConfig.js | 6 ++++++ +setup/gameaction/GameActionConfig.js | 8 ++++++++ +setup/mulelogger/LoggerConfig.js | 7 +++++++ +setup/pubjoin/PubJoinConfig.js | 11 +++++++++++ .gitignore | 1 + d2bs/kolbot/D2BotChannel.dbj | 6 ------ d2bs/kolbot/D2BotCleaner.dbj | 2 -- d2bs/kolbot/D2BotFollow.dbj | 9 +++------ d2bs/kolbot/D2BotGameAction.dbj | 6 ++---- d2bs/kolbot/D2BotMap.dbj | 4 ++-- d2bs/kolbot/D2BotMule.dbj | 13 +++---------- d2bs/kolbot/D2BotMuleLog.dbj | 13 ++++--------- d2bs/kolbot/D2BotPubJoin.dbj | 12 ++---------- setup.bat | 1 + 15 files changed, 73 insertions(+), 49 deletions(-) create mode 100644 +setup/automule/StarterConfig.js diff --git a/+setup/automule/StarterConfig.js b/+setup/automule/StarterConfig.js new file mode 100644 index 000000000..b549b4234 --- /dev/null +++ b/+setup/automule/StarterConfig.js @@ -0,0 +1,23 @@ +/** +* @filename StarterConfig.js +* @author theBGuy +* @desc Starter Configuration file for D2BotAutoMule system +* +*/ + +(function (module) { + // D2BotMule specific settings - for global settings see libs/starter/StarterConfig.js + const StarterConfig = { + MinGameTime: 30, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby + MaxGameTime: 60, // Maximum game length in minutes, only for continuous muling + CreateGameDelay: 5, // Seconds to wait before creating a new game + SwitchKeyDelay: 0, // Seconds to wait before switching a used/banned key or after realm down + ExitToMenu: false, // Set to true to wait out restriction in main menu or false to wait in lobby. + VersionErrorDelay: rand(15, 30), // Seconds to wait after 'unable to identify version' message + MakeAccountOnFailure: true + }; + + module.exports = { + StarterConfig: StarterConfig + }; +})(module); diff --git a/+setup/follow/FollowConfig.js b/+setup/follow/FollowConfig.js index f0960dfce..5374f1266 100644 --- a/+setup/follow/FollowConfig.js +++ b/+setup/follow/FollowConfig.js @@ -6,6 +6,11 @@ */ (function (module) { + // D2BotFollow specific settings - for global settings see libs/starter/StarterConfig.js + const StarterConfig = { + JoinRetryDelay: 5, // Time in seconds to wait before next join attempt + }; + /** * @description Join game settings * - Format: "leader's profile": ["leecher 1 profile", "leecher 2 profile", ...] @@ -24,5 +29,6 @@ module.exports = { JoinSettings: JoinSettings, + StarterConfig: StarterConfig, }; })(module); diff --git a/+setup/gameaction/GameActionConfig.js b/+setup/gameaction/GameActionConfig.js index ad2834b33..0aaa18792 100644 --- a/+setup/gameaction/GameActionConfig.js +++ b/+setup/gameaction/GameActionConfig.js @@ -13,5 +13,13 @@ LogMerc: false, // include items merc has equipped (if alive) SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) IngameTime: 60, // Time to wait before leaving game + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + // D2BotGameAction specific settings - for global settings see libs/starter/StarterConfig.js + StarterConfig: { + MinGameTime: 0, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby + CreateGameDelay: 5, // Seconds to wait before creating a new game + SwitchKeyDelay: 0, // Seconds to wait before switching a used/banned key or after realm down + }, }; })(module); diff --git a/+setup/mulelogger/LoggerConfig.js b/+setup/mulelogger/LoggerConfig.js index 3bb3603c4..77aa3eca1 100644 --- a/+setup/mulelogger/LoggerConfig.js +++ b/+setup/mulelogger/LoggerConfig.js @@ -32,5 +32,12 @@ "exampleAcc/pa33word3/realm": ["all"], }, // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + + // D2BotMuleLog specific settings - for global settings see libs/starter/StarterConfig.js + StarterConfig: { + MinGameTime: rand(150, 180), // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby + CreateGameDelay: 5, // Seconds to wait before creating a new game + SwitchKeyDelay: 0, // Seconds to wait before switching a used/banned key or after realm down + } }; })(module); diff --git a/+setup/pubjoin/PubJoinConfig.js b/+setup/pubjoin/PubJoinConfig.js index 56c8291b8..89919102f 100644 --- a/+setup/pubjoin/PubJoinConfig.js +++ b/+setup/pubjoin/PubJoinConfig.js @@ -6,6 +6,16 @@ */ (function (module) { + // D2BotPubJoin specific settings - for global settings see libs/starter/StarterConfig.js + const StarterConfig = { + MinGameTime: 0, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby + ResetCount: 0, // Reset game count back to 1 every X games. + JoinDelay: 10, // Seconds to wait between join attempts + AttemptNextGame: true, // after joining a game, attempt incrementing game count and joining next game rather than looking for it in game list + AttemptNextGameRetrys: 5, + MinPlayers: 1, // Minimum players in game to join + }; + /** * @description includeFilter format * @example Multiple entries in the same array mean AND @@ -55,5 +65,6 @@ includeFilter: includeFilter, excludeFilter: excludeFilter, profileOverides: profileOverides, + StarterConfig: StarterConfig }; })(module); diff --git a/.gitignore b/.gitignore index 6e63f9487..0fa69c429 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ d2bs/kolbot/libs/config/_CustomConfig.js d2bs/kolbot/libs/starter/AdvancedConfig.js d2bs/kolbot/libs/starter/StarterConfig.js d2bs/kolbot/libs/systems/gameaction/GameActionConfig.js +d2bs/kolbot/libs/systems/automule/config/StarterConfig.js diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 082809e9b..8433b08a8 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -8,15 +8,9 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// D2BotChannel specific settings - for global settings see libs/starter/StarterConfig.js const { ChannelConfig, } = require("./libs/systems/channel/ChannelConfig"); -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Override default values for StarterConfig under here by following format -// Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -// Starter.Config.JoinChannel = ""; // Default channel. -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // the only things we really need from these are their oog checks includeSystemLibs(); diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index aa9990c4a..6173a7c20 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -19,8 +19,6 @@ const { AdvancedCleanerConfig, } = require("./libs/systems/cleaner/CleanerConfig"); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Override default values for StarterConfig under here by following format -// Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds let Controls = require("./libs/modules/Control"); diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 56fa71213..3949d8954 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -8,14 +8,11 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// D2BotFollow specific settings - for global settings see libs/starter/StarterConfig.js -Starter.Config.JoinRetryDelay = 5; // Time in seconds to wait before next join attempt const { - JoinSettings + JoinSettings, + StarterConfig } = require("./libs/systems/follow/FollowConfig"); - -// Override default values for StarterConfig under here by following format -// Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds +Object.assign(Starter.Config, StarterConfig); // the only things we really need from these are their oog checks includeSystemLibs(); diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index 254378709..de8fc8ab2 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -10,10 +10,8 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// D2BotGameAction specific settings - for global settings see libs/starter/StarterConfig.js -Starter.Config.MinGameTime = 0; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby -Starter.Config.CreateGameDelay = 5; // Seconds to wait before creating a new game -Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/banned key or after realm down +const { StarterConfig } = require("./libs/systems/gameaction/GameActionConfig"); +Object.assign(Starter.Config, StarterConfig); // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds diff --git a/d2bs/kolbot/D2BotMap.dbj b/d2bs/kolbot/D2BotMap.dbj index 21944f3e7..f6b959a7d 100644 --- a/d2bs/kolbot/D2BotMap.dbj +++ b/d2bs/kolbot/D2BotMap.dbj @@ -9,8 +9,8 @@ function main () { include("critical.js"); // required if (DataFile.init()) { - Starter.firstRun = true; -} + Starter.firstRun = true; + } addEventListener("copydata", Starter.receiveCopyData); diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 8896f8cf1..6859e0ae7 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -9,14 +9,8 @@ js_strict(true); include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// D2BotMule specific settings - for global settings see libs/starter/StarterConfig.js -Starter.Config.MinGameTime = 30; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby -Starter.Config.MaxGameTime = 60; // Maximum game length in minutes, only for continuous muling -Starter.Config.CreateGameDelay = 5; // Seconds to wait before creating a new game -Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/banned key or after realm down -Starter.Config.ExitToMenu = false; // Set to true to wait out restriction in main menu or false to wait in lobby. -Starter.Config.VersionErrorDelay = rand(15, 30); // Seconds to wait after 'unable to identify version' message -Starter.Config.MakeAccountOnFailure = true; +const { StarterConfig } = require("./libs/systems/automule/config/StarterConfig"); +Object.assign(Starter.Config, StarterConfig); // Override default values for StarterConfig under here by following format // Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds @@ -27,8 +21,7 @@ include("systems/automule/Mule.js"); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -if (!FileTools.exists("data/" + me.profile + ".json") - && DataFile.create()) { +if (DataFile.init()) { Starter.firstRun = true; } diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 61d19001c..97c93d17d 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -9,21 +9,16 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// D2BotMuleLog specific settings - for global settings see libs/starter/StarterConfig.js -Starter.Config.MinGameTime = rand(150, 180); // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby -Starter.Config.CreateGameDelay = 5; // Seconds to wait before creating a new game -Starter.Config.SwitchKeyDelay = 0; // Seconds to wait before switching a used/banned key or after realm down - -// Override default values for StarterConfig under here by following format -// Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds +const { StarterConfig } = require("./libs/systems/mulelogger/LoggerConfig"); +Object.assign(Starter.Config, StarterConfig); // only libs we should need here as te rest of the actions are performed from default.dbj thread include("systems/dropper/DropperSetup.js"); include("systems/mulelogger/MuleLogger.js"); include("systems/automule/AutoMule.js"); -if (!FileTools.exists("data/" + me.profile + ".json")) { - DataFile.create(); +if (DataFile.init()) { + Starter.firstRun = true; } const usingDropper = isIncluded("systems/dropper/DropperSetup.js"); diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index f2d591cba..a203d3dc6 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -11,23 +11,15 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// D2BotPubJoin specific settings - for global settings see libs/starter/StarterConfig.js -Starter.Config.MinGameTime = 0; // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby -Starter.Config.ResetCount = 0; // Reset game count back to 1 every X games. -Starter.Config.JoinDelay = 10; // Seconds to wait between join attempts -Starter.Config.AttemptNextGame = true; // after joining a game, attempt incrementing game count and joining next game rather than looking for it in game list -Starter.Config.AttemptNextGameRetrys = 5; -Starter.Config.MinPlayers = 1; // Minimum players in game to join const { includeFilter, excludeFilter, profileOverides, + StarterConfig, } = require("./libs/systems/pubjoin/PubJoinConfig"); -// Override default values for StarterConfig under here by following format -// Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds - // ############################################################################### +Object.assign(Starter.Config, StarterConfig); // the only things we really need from these are their oog checks includeSystemLibs(); diff --git a/setup.bat b/setup.bat index c091e66e1..1dca16d51 100644 --- a/setup.bat +++ b/setup.bat @@ -79,6 +79,7 @@ call :CopyIfNotExists "%SETUP_DIR%\d2bs.ini" "%D2BS_DIR%\d2bs.ini" "d2bs.ini to REM Copy system files to their respective directories call :CopyIfNotExists "%SETUP_DIR%\automule\MuleConfig.js" "%SYSTEMS_DIR%\automule\config\MuleConfig.js" "MuleConfig.js to automule config directory" call :CopyIfNotExists "%SETUP_DIR%\automule\TorchAnniMules.js" "%SYSTEMS_DIR%\automule\config\TorchAnniMules.js" "TorchAnniMules.js to automule config directory" +call :CopyIfNotExists "%SETUP_DIR%\automule\StarterConfig.js" "%SYSTEMS_DIR%\automule\config\StarterConfig.js" "StarterConfig.js to automule config directory" call :CopyIfNotExists "%SETUP_DIR%\channel\ChannelConfig.js" "%SYSTEMS_DIR%\channel\ChannelConfig.js" "ChannelConfig.js to channel directory" call :CopyIfNotExists "%SETUP_DIR%\cleaner\CleanerConfig.js" "%SYSTEMS_DIR%\cleaner\CleanerConfig.js" "CleanerConfig.js to cleaner directory" call :CopyIfNotExists "%SETUP_DIR%\crafting\TeamsConfig.js" "%SYSTEMS_DIR%\crafting\TeamsConfig.js" "TeamsConfig.js to crafting directory" From 83e1a813c1615bab5a5937f77bae567627209951 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 03:18:39 -0400 Subject: [PATCH 566/758] Clean up comments --- d2bs/kolbot/D2BotLead.dbj | 2 -- d2bs/kolbot/D2BotMule.dbj | 3 --- 2 files changed, 5 deletions(-) diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index fa38dfed9..102e1cca9 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -10,8 +10,6 @@ include("critical.js"); // required // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // - for global settings @see libs/starter/StarterConfig.js -// Override default values for StarterConfig under here by following format -// Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds // the only things we really need from these are their oog checks includeSystemLibs(); diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 6859e0ae7..4d0ad8b2d 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -12,9 +12,6 @@ include("critical.js"); // required const { StarterConfig } = require("./libs/systems/automule/config/StarterConfig"); Object.assign(Starter.Config, StarterConfig); -// Override default values for StarterConfig under here by following format -// Starter.Config.ValueToChange = value; // Example: Starter.Config.MinGameTime = 500; // changes MinGameTime to 500 seconds -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // system libs includeSystemLibs(); include("systems/automule/Mule.js"); From 482dbbe21fc970f1e0c561fcd99954bdc4e6ed83 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 15:10:02 -0400 Subject: [PATCH 567/758] Refactor Attack.securePosition to use options object - Updated the Attack.securePosition function to accept a single options object instead of multiple positional parameters. Refactored all usages across the codebase to use the new options object, improving readability and maintainability. - Updated type definitions and added JSDoc for the new options interface. --- d2bs/kolbot/libs/SoloPlay | 2 +- d2bs/kolbot/libs/core/Attack.js | 36 ++++++++--- d2bs/kolbot/libs/core/Common/Cain.js | 2 +- d2bs/kolbot/libs/core/Common/Diablo.js | 6 +- d2bs/kolbot/libs/scripts/ControlBot.js | 12 ++-- d2bs/kolbot/libs/scripts/Travincal.js | 6 +- d2bs/kolbot/libs/scripts/Tristram.js | 2 +- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 62 +++++++++---------- d2bs/kolbot/sdk/types/Attack.d.ts | 10 ++- d2bs/kolbot/threads/RushThread.js | 46 +++++++------- 10 files changed, 105 insertions(+), 79 deletions(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 53c606f78..0ec31b75e 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 53c606f78b82798096eaddd72a60bfa38ac06545 +Subproject commit 0ec31b75e4ecd27649784e81b3c723fe85288f8c diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 8cc8f4088..3f0908878 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1242,30 +1242,48 @@ const Attack = { return true; }, + /** + * @typedef {Object} SecurePositionOptions + * @property {number} [range=15] - Range to check for monsters + * @property {number} [timer=3000] - Time to wait for a safe position + * @property {boolean} [skipBlocked=true] - Skip monsters that block the path + * @property {boolean} [useRedemption=false] - Redemption for Paladin + * @property {number[]} [skipIds=[]] - Skip monsters with these classids + */ + /** * @param {number} x * @param {number} y - * @param {number} [range=15] - * @param {number} [timer=3000] - time in ms - * @param {boolean} [skipBlocked=true] - * @param {boolean} [special=false] + * @param {SecurePositionOptions} [options] * @returns {void} */ - securePosition: function (x, y, range = 15, timer = 3000, skipBlocked = true, special = false) { + securePosition: function (x, y, options = {}) { let tick; (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); - skipBlocked === true && (skipBlocked = sdk.collision.Ranged); + const node = new PathNode(x, y); + /** @type {Required} */ + const clearOptions = Object.assign({ + range: 15, + timer: 3000, + skipBlocked: true, + useRedemption: false, + skipIds: [], + }, options); + clearOptions.skipBlocked === true && (clearOptions.skipBlocked = sdk.collision.Ranged); + + const { range, timer, skipBlocked, useRedemption, skipIds } = clearOptions; while (true) { - [x, y].distance > 5 && Pather.moveTo(x, y); + node.distance > 5 && Pather.moveTo(node.x, node.y); let monster = Game.getMonster(); let monList = []; if (monster) { do { - if (getDistance(monster, x, y) <= range && monster.attackable && this.canAttack(monster) + if (skipIds.includes(monster.classid)) continue; + if (getDistance(monster, node.x, node.y) <= range && monster.attackable && this.canAttack(monster) && (!skipBlocked || !checkCollision(me, monster, skipBlocked)) && (Pather.canTeleport() || !checkCollision(me, monster, sdk.collision.BlockWall))) { monList.push(copyUnit(monster)); @@ -1287,7 +1305,7 @@ const Attack = { tick && (tick = false); } - if (special) { + if (useRedemption) { if (me.paladin && Skill.canUse(sdk.skills.Redemption) && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { delay(1000); diff --git a/d2bs/kolbot/libs/core/Common/Cain.js b/d2bs/kolbot/libs/core/Common/Cain.js index a6fe32ebf..4dd70678a 100644 --- a/d2bs/kolbot/libs/core/Common/Cain.js +++ b/d2bs/kolbot/libs/core/Common/Cain.js @@ -76,7 +76,7 @@ sdk.monsters.preset.Rakanishu, { offX: 10, offY: 10, pop: true } ); - Attack.securePosition(me.x, me.y, 40, 3000, true); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); Pather.moveToPresetObject( sdk.areas.StonyField, sdk.quest.chest.StoneAlpha, diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 569aedf07..1e17e4a5a 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -322,7 +322,7 @@ if (Config.Diablo.SealLeader || Config.Diablo.Fast) { Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); Config.Diablo.SealLeader && Pather.makePortal() && say("in"); } @@ -366,7 +366,7 @@ if (Config.Diablo.SealLeader || Config.Diablo.Fast) { Common.Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); Config.Diablo.SealLeader && Pather.makePortal() && say("in"); } @@ -413,7 +413,7 @@ if (Config.Diablo.SealLeader || Config.Diablo.Fast) { Common.Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, 35, 3000, true); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); Config.Diablo.SealLeader && Pather.makePortal() && say("in"); } diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index 998728837..dad9125ce 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -137,12 +137,12 @@ const ControlBot = new Runnable( throw new Error("Failed to move to Cain's Jail"); } - Attack.securePosition(gibbet.x, gibbet.y, 25, 3000); + Attack.securePosition(gibbet.x, gibbet.y, { range: 25, duration: 3000 }); Pather.makePortal(); log(AutoRush.playersIn); const cainRescued = Misc.poll(function () { - Attack.securePosition(me.x, me.y, 15, 1000); + Attack.securePosition(me.x, me.y, { range: 15, duration: 1000 }); return gibbet.mode; }, Time.minutes(2)); @@ -166,8 +166,8 @@ const ControlBot = new Runnable( throw new Error("Failed to move to durance 3"); } Pather.moveTo(17617, 8069); - Attack.securePosition(me.x, me.y, 30, 3000); - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 20, 3000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, { range: 20, duration: 3000 }); let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); if (hydra) { @@ -1112,7 +1112,7 @@ const ControlBot = new Runnable( addEventListener("chatmsg", stopWatcher); Pather.useWaypoint(areaId, true); if (Config.ControlBot.Wps.SecurePortal) { - Attack.securePosition(me.x, me.y, 20, 1000); + Attack.securePosition(me.x, me.y, { range: 20, duration: 1000 }); } Pather.makePortal(); Chat.say(getAreaName(me.area) + " TP up"); @@ -1201,7 +1201,7 @@ const ControlBot = new Runnable( Pather.useWaypoint(wp, true); if (Config.ControlBot.Wps.SecurePortal) { - Attack.securePosition(me.x, me.y, 20, 1000); + Attack.securePosition(me.x, me.y, { range: 20, duration: 1000 }); } Pather.makePortal(); Chat.say(getAreaName(me.area) + " TP up"); diff --git a/d2bs/kolbot/libs/scripts/Travincal.js b/d2bs/kolbot/libs/scripts/Travincal.js index 3388bca06..a710684ab 100644 --- a/d2bs/kolbot/libs/scripts/Travincal.js +++ b/d2bs/kolbot/libs/scripts/Travincal.js @@ -19,9 +19,9 @@ const Travincal = new Runnable( if (Config.Travincal.PortalLeech) { Pather.moveTo(orgX + 85, orgY - 139); - Attack.securePosition(orgX + 70, orgY - 139, 25, 2000); - Attack.securePosition(orgX + 100, orgY - 139, 25, 2000); - Attack.securePosition(orgX + 85, orgY - 139, 25, 5000); + Attack.securePosition(orgX + 70, orgY - 139, { range: 25, duration: 2000 }); + Attack.securePosition(orgX + 100, orgY - 139, { range: 25, duration: 2000 }); + Attack.securePosition(orgX + 85, orgY - 139, { range: 25, duration: 5000 }); Pather.moveTo(orgX + 85, orgY - 139); Pather.makePortal(); delay(1000); diff --git a/d2bs/kolbot/libs/scripts/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js index d3b986c3a..faae22f7f 100644 --- a/d2bs/kolbot/libs/scripts/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -42,7 +42,7 @@ const Tristram = new Runnable( } while (!Pather.usePortal(sdk.areas.Tristram)) { - Attack.securePosition(me.x, me.y, 10, 1000); + Attack.securePosition(me.x, me.y, { range: 10, duration: 1000 }); } break; diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index e156ce4fb..eb89f2a45 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -94,7 +94,7 @@ let tree = Game.getObject(sdk.quest.chest.InifussTree); !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Attack.securePosition(me.x, me.y, 40, 3000, true); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); !!tree && tree.distance > 5 && Pather.moveToUnit(tree); Pather.makePortal(); log(AutoRush.playersIn); @@ -104,7 +104,7 @@ if (tree.mode) { break; } - Attack.securePosition(me.x, me.y, 25, 1000); + Attack.securePosition(me.x, me.y, { range: 25, duration: 1000 }); } Pather.usePortal(1) || Town.goToTown(); @@ -115,7 +115,7 @@ }); const alphaPreset = Game.getPresetObject(sdk.areas.StonyField, sdk.quest.chest.StoneAlpha); const StoneAlpha = alphaPreset.realCoords(); - Attack.securePosition(StoneAlpha.x, StoneAlpha.y, 40, 3000, true); + Attack.securePosition(StoneAlpha.x, StoneAlpha.y, { range: 40, duration: 3000, skipBlocked: true }); StoneAlpha.distance > 5 && Pather.moveToUnit(StoneAlpha); Pather.makePortal(); log(AutoRush.playersIn); @@ -135,7 +135,7 @@ break; } } - Attack.securePosition(StoneAlpha.x, StoneAlpha.y, 35, 1000); + Attack.securePosition(StoneAlpha.x, StoneAlpha.y, { range: 35, duration: 1000 }); } if (me.inArea(sdk.areas.Tristram)) { @@ -147,7 +147,7 @@ throw new Error("Failed to move to Cain's Jail"); } - Attack.securePosition(gibbet.x, gibbet.y, 25, 3000); + Attack.securePosition(gibbet.x, gibbet.y, { range: 25, duration: 3000 }); Pather.makePortal(); log(AutoRush.playersIn); @@ -157,7 +157,7 @@ if (gibbet.mode) { break; } - Attack.securePosition(me.x, me.y, 15, 1000); + Attack.securePosition(me.x, me.y, { range: 15, duration: 1000 }); } const playersLeftTrist = Misc.poll(function () { @@ -190,7 +190,7 @@ throw new Error("andy failed"); } - Attack.securePosition(safeNode.x, safeNode.y, 40, 3000, true); + Attack.securePosition(safeNode.x, safeNode.y, { range: 40, duration: 3000, skipBlocked: true }); Pather.makePortal(); log(AutoRush.playersIn); @@ -242,7 +242,7 @@ throw new Error("bloodraven failed"); } - Attack.securePosition(me.x, me.y, 10, 1000); + Attack.securePosition(me.x, me.y, { range: 10, duration: 1000 }); Pather.makePortal(); log(AutoRush.playersIn); @@ -287,7 +287,7 @@ if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { throw new Error("smith failed"); } - Attack.securePosition(me.x, me.y, 30, 3000, true); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -360,7 +360,7 @@ }, 1500, 500); rada ? moveIntoPos(rada, 60) : console.log("radament unit not found"); - Attack.securePosition(me.x, me.y, 35, 3000); + Attack.securePosition(me.x, me.y, { range: 35, duration: 3000 }); Pather.makePortal(); log(AutoRush.playersIn); @@ -381,7 +381,7 @@ log(AutoRush.playersOut); Pickit.pickItems(); - Attack.securePosition(returnSpot.x, returnSpot.y, 30, 3000); + Attack.securePosition(returnSpot.x, returnSpot.y, { range: 30, duration: 3000 }); if (!Misc.poll(function () { return !playerIn(me.area, nick); @@ -430,7 +430,7 @@ || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { throw new Error("cube failed"); } - Attack.securePosition(me.x, me.y, 30, 3000, true); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); Pather.makePortal(); log(AutoRush.playersIn); @@ -464,7 +464,7 @@ throw new Error("amulet failed"); } - Attack.securePosition(15044, 14045, 25, 3000, me.hell, me.hell); + Attack.securePosition(15044, 14045, { range: 25, duration: 3000, skipBlocked: me.hell, useRedemption: me.hell }); Pather.makePortal(); log(AutoRush.playersIn); @@ -497,7 +497,7 @@ throw new Error("staff failed"); } - Attack.securePosition(me.x, me.y, 30, 3000, true); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); Pather.makePortal(); log(AutoRush.playersIn); @@ -557,7 +557,7 @@ throw new Error("summoner failed"); } - Attack.securePosition(spot.x, spot.y, 25, 3000); + Attack.securePosition(spot.x, spot.y, { range: 25, duration: 3000 }); Pather.makePortal(); log(AutoRush.playersIn); @@ -566,7 +566,7 @@ return true; } Pather.moveToUnit(spot); - Attack.securePosition(me.x, me.y, 25, 500); + Attack.securePosition(me.x, me.y, { range: 25, duration: 500 }); return false; }, AutoRush.playerWaitTimeout, 1000)) { log(timedOut(nick)); @@ -622,7 +622,7 @@ throw new Error("duriel failed"); } - Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true, useRedemption: me.hell }); Pather.makePortal(); log(AutoRush.playersIn); @@ -712,7 +712,7 @@ throw new Error("gidbinn failed - couldn't find altar"); } - Attack.securePosition(me.x, me.y, 30, 3000, true); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { @@ -731,7 +731,7 @@ Misc.poll(function () { const gidbinn = Game.getItem(sdk.quest.item.TheGidbinn); if (gidbinn) { - Attack.securePosition(gidbinn.x, gidbinn.y, 30, 3000, true); + Attack.securePosition(gidbinn.x, gidbinn.y, { range: 30, duration: 3000, skipBlocked: true }); Pather.moveToUnit(gidbinn); return true; } @@ -763,7 +763,7 @@ throw new Error("Lam Essen quest failed"); } - Attack.securePosition(me.x, me.y, 30, 2000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 2000 }); Pather.makePortal(); log(AutoRush.playersIn); @@ -803,7 +803,7 @@ throw new Error("brain failed"); } - Attack.securePosition(me.x, me.y, 30, 3000, me.hell, me.hell); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: me.hell, useRedemption: me.hell }); Pather.makePortal(); log(AutoRush.playersIn); @@ -836,7 +836,7 @@ throw new Error("eye failed"); } - Attack.securePosition(me.x, me.y, 30, 3000, me.hell, me.hell); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: me.hell, useRedemption: me.hell }); Pather.makePortal(); log(AutoRush.playersIn); @@ -869,7 +869,7 @@ throw new Error("heart failed"); } - Attack.securePosition(me.x, me.y, 30, 3000, me.hell, me.hell); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: me.hell, useRedemption: me.hell }); Pather.makePortal(); log(AutoRush.playersIn); @@ -907,16 +907,16 @@ new PathNode(wpCoords.x + 25, wpCoords.y - 70), new PathNode(wpCoords.x + 20, wpCoords.y - 123), ].forEach(function (node) { - Pather.move(node) && Attack.securePosition(node.x, node.y, 25, 2500); + Pather.move(node) && Attack.securePosition(node.x, node.y, { range: 25, duration: 2500 }); }); Pather.move(portalSpot); - Attack.securePosition(portalSpot.x, portalSpot.y, 40, 4000); + Attack.securePosition(portalSpot.x, portalSpot.y, { range: 40, duration: 4000 }); Pather.makePortal(); log(AutoRush.playersIn); if (!Misc.poll(function () { - Attack.securePosition(portalSpot.x, portalSpot.y, 25, 1000); + Attack.securePosition(portalSpot.x, portalSpot.y, { range: 25, duration: 1000 }); return playerIn(me.area, nick); }, AutoRush.playerWaitTimeout, 1000)) { log(timedOut(nick)); @@ -926,7 +926,7 @@ Pather.moveTo(wpCoords.x + 30, wpCoords.y - 134); Pather.moveTo(wpCoords.x + 86, wpCoords.y - 130); Pather.moveTo(wpCoords.x + 71, wpCoords.y - 94); - Attack.securePosition(me.x, me.y, 40, 4000); + Attack.securePosition(me.x, me.y, { range: 40, duration: 4000 }); Attack.kill(sdk.locale.monsters.IsmailVilehand); Pather.move(portalSpot); @@ -965,7 +965,7 @@ delay(250); } - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 40, 3000); + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, { range: 40, duration: 3000 }); let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); @@ -1065,7 +1065,7 @@ y: me.y }; - Attack.securePosition(me.x, me.y, 30, 3000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); Pather.makePortal(); log(AutoRush.playersIn); @@ -1162,7 +1162,7 @@ Pather.useWaypoint(sdk.areas.FrigidHighlands, true) && Precast.doPrecast(false); Pather.moveTo(3846, 5120); - Attack.securePosition(me.x, me.y, 30, 3000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); Pather.makePortal(); log(AutoRush.playersIn); @@ -1205,7 +1205,7 @@ throw new Error("Anya quest failed"); } - Attack.securePosition(me.x, me.y, 30, 2000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 2000 }); let anya = Game.getObject(sdk.objects.FrozenAnya); diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 132f7e558..16a525e44 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -41,7 +41,15 @@ declare global { y: number; }): Monster[]; function clearList(mainArg: Function | Unit[], sortFunc?: Function, refresh?: boolean): boolean; - function securePosition(x: number, y: number, range?: number, timer?: number, skipBlocked?: boolean, special?: boolean): void; + + interface SecurePositionOptions { + range?: number; + timer?: number; + skipBlocked?: boolean; + useRedemption?: boolean; + skipIds?: number[]; + } + function securePosition(x: number, y: number, options: SecurePositionOptions): void; function markRoom(room: Room, color: number): void; function countUniques(): void; function storeStatistics(area: number): void; diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index 3d3d01a55..4511e6480 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -41,7 +41,7 @@ function giveWP () { addEventListener("chatmsg", wpEvent); let playerCount = Misc.getPartyCount(); let mobCount = getUnits(sdk.unittype.Monster).filter(mon => mon.distance <= 15 && mon.attackable).length; - mobCount > 0 && Attack.securePosition(me.x, me.y, 15, Time.seconds(30), true); + mobCount > 0 && Attack.securePosition(me.x, me.y, { range: 15, duration: Time.seconds(30), skipBlocked: true }); wp.distance > 5 && Pather.moveToUnit(wp); Pather.makePortal(); say("wp"); @@ -143,7 +143,7 @@ function main () { } Pather.makePortal(); - Attack.securePosition(me.x, me.y, 40, 3000, true); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); this.log("1"); while (!this.playerIn()) { @@ -182,7 +182,7 @@ function main () { } Pather.makePortal(); - Attack.securePosition(me.x, me.y, 30, 3000, true); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); this.log("1"); while (!this.playerIn()) { @@ -210,7 +210,7 @@ function main () { } Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000, me.hell, me.hell); + Attack.securePosition(me.x, me.y, { range: 25, duration: 3000, skipBlocked: me.hell, useRedemption: me.hell }); this.log("1"); @@ -238,7 +238,7 @@ function main () { } Pather.makePortal(); - Attack.securePosition(me.x, me.y, 30, 3000, true); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); this.log("1"); while (!this.playerIn()) { @@ -292,12 +292,12 @@ function main () { } Pather.makePortal(); - Attack.securePosition(me.x, me.y, 25, 3000); + Attack.securePosition(me.x, me.y, { range: 25, duration: 3000 }); this.log("1"); while (!this.playerIn()) { Pather.moveToUnit(spot); - Attack.securePosition(me.x, me.y, 25, 500); + Attack.securePosition(me.x, me.y, { range: 25, duration: 500 }); delay(250); } @@ -350,7 +350,7 @@ function main () { } Pather.makePortal(); - Attack.securePosition(me.x, me.y, 30, 3000, true, me.hell); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true, useRedemption: me.hell }); this.log("1"); while (!this.playerIn()) { @@ -412,7 +412,7 @@ function main () { Pather.moveTo(coords[0] + 23, coords[1] - 102); Pather.makePortal(); - Attack.securePosition(me.x, me.y, 40, 3000); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000 }); this.log("1"); while (!this.playerIn()) { @@ -422,7 +422,7 @@ function main () { Pather.moveTo(coords[0] + 30, coords[1] - 134); Pather.moveTo(coords[0] + 86, coords[1] - 130); Pather.moveTo(coords[0] + 71, coords[1] - 94); - Attack.securePosition(me.x, me.y, 40, 3000); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000 }); Pather.moveTo(coords[0] + 23, coords[1] - 102); Pather.makePortal(); @@ -455,7 +455,7 @@ function main () { delay(250); } - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, 40, 3000); + Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, { range: 40, duration: 3000 }); let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); @@ -690,7 +690,7 @@ function main () { let tree = Game.getObject(sdk.quest.chest.InifussTree); !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Attack.securePosition(me.x, me.y, 40, 3000, true); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); !!tree && tree.distance > 5 && Pather.moveToUnit(tree); Pather.makePortal(); this.log("1"); @@ -700,14 +700,14 @@ function main () { if (tree.mode) { break; } - Attack.securePosition(me.x, me.y, 20, 1000); + Attack.securePosition(me.x, me.y, { range: 20, duration: 1000 }); } Pather.usePortal(1) || Town.goToTown(); Pather.useWaypoint(sdk.areas.StonyField, true); Precast.doPrecast(true); Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); - Attack.securePosition(me.x, me.y, 40, 3000, true); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); Pather.makePortal(); this.log("1"); @@ -718,7 +718,7 @@ function main () { if (Pather.usePortal(sdk.areas.Tristram)) { break; } - Attack.securePosition(me.x, me.y, 35, 1000); + Attack.securePosition(me.x, me.y, { range: 35, duration: 1000 }); } if (me.inArea(sdk.areas.Tristram)) { @@ -730,7 +730,7 @@ function main () { throw new Error("Failed to move to Cain's Jail"); } - Attack.securePosition(gibbet.x, gibbet.y, 20, 3000); + Attack.securePosition(gibbet.x, gibbet.y, { range: 20, duration: 3000 }); Pather.makePortal(); this.log("1"); @@ -740,7 +740,7 @@ function main () { if (gibbet.mode) { break; } - Attack.securePosition(me.x, me.y, 10, 1000); + Attack.securePosition(me.x, me.y, { range: 10, duration: 1000 }); } } } @@ -797,7 +797,7 @@ function main () { let rada = Misc.poll(() => Game.getMonster(sdk.monsters.Radament), 1500, 500); rada ? moveIntoPos(rada, 60) : print("radament unit not found"); - Attack.securePosition(me.x, me.y, 35, 3000); + Attack.securePosition(me.x, me.y, { range: 35, duration: 3000 }); Pather.makePortal(); this.log("1"); @@ -814,7 +814,7 @@ function main () { this.log("2"); Pickit.pickItems(); - Attack.securePosition(me.x, me.y, 30, 3000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); while (this.playerIn()) { delay(200); @@ -855,7 +855,7 @@ function main () { throw new Error("Lam Essen quest failed"); } - Attack.securePosition(me.x, me.y, 30, 2000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 2000 }); Pather.makePortal(); this.log("1"); @@ -927,7 +927,7 @@ function main () { y: me.y }; - Attack.securePosition(me.x, me.y, 30, 3000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); Pather.makePortal(); this.log("1"); @@ -956,7 +956,7 @@ function main () { Pather.useWaypoint(sdk.areas.FrigidHighlands, true) && Precast.doPrecast(false); Pather.moveTo(3846, 5120); - Attack.securePosition(me.x, me.y, 30, 3000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); Pather.makePortal(); this.log("1"); @@ -996,7 +996,7 @@ function main () { throw new Error("Anya quest failed"); } - Attack.securePosition(me.x, me.y, 30, 2000); + Attack.securePosition(me.x, me.y, { range: 30, duration: 2000 }); let anya = Game.getObject(sdk.objects.FrozenAnya); From 81060385288c73a72196547dcb712f3dd5ba40d2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 15:13:08 -0400 Subject: [PATCH 568/758] Update AutoRush summoner sequence to include summoner id in skipIds for securePosition - Ensure we don't kill the summoner by mistake --- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index eb89f2a45..cde91482e 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -530,34 +530,28 @@ Pather.useWaypoint(sdk.areas.ArcaneSanctuary, true) && Precast.doPrecast(true); const preset = Game.getPresetObject(sdk.areas.ArcaneSanctuary, sdk.quest.chest.Journal).realCoords(); - /** @type {PathNode} */ - let spot = {}; - - switch (preset.x) { - case 25011: - spot = { x: 25081, y: 5446 }; - break; - case 25866: - spot = { x: 25830, y: 5447 }; - break; - case 25431: - switch (preset.y) { - case 5011: - spot = { x: 25449, y: 5081 }; - break; - case 5861: - spot = { x: 25447, y: 5822 }; - break; + const spot = (function () { + switch (preset.x) { + case 25011: + return new PathNode(25081, 5446); + case 25866: + return new PathNode(25830, 5447); + case 25431: + switch (preset.y) { + case 5011: + return new PathNode(25449, 5081); + case 5861: + return new PathNode(25447, 5822); + } } + return null; + })(); - break; - } - - if (!Pather.moveToUnit(spot)) { + if (!spot || !Pather.moveToUnit(spot)) { throw new Error("summoner failed"); } - Attack.securePosition(spot.x, spot.y, { range: 25, duration: 3000 }); + Attack.securePosition(spot.x, spot.y, { range: 25, duration: 3000, skipIds: [sdk.monsters.Summoner] }); Pather.makePortal(); log(AutoRush.playersIn); From 9c75d2d88626cc1cc3bd8df503e7e49fac3b4384 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 16:13:57 -0400 Subject: [PATCH 569/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 0ec31b75e..46afefe33 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 0ec31b75e4ecd27649784e81b3c723fe85288f8c +Subproject commit 46afefe33b8d10bd84d3a1f8f7bdb252129f0587 From f1e8307fb7676a162bfc1fbba78a0fff22dc1e91 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 16:56:54 -0400 Subject: [PATCH 570/758] Refactor GameAction and MuleLogger type definitions - Updated GameAction.d.ts and MuleLogger.d.ts to use explicit TypeScript interfaces instead of global namespaces, improving type safety and clarity. Updated corresponding JS files to use the new type annotations. --- .../libs/systems/gameaction/GameAction.d.ts | 43 ++++--- .../libs/systems/gameaction/GameAction.js | 1 + .../libs/systems/mulelogger/MuleLogger.d.ts | 116 +++++++++--------- .../libs/systems/mulelogger/MuleLogger.js | 12 +- 4 files changed, 84 insertions(+), 88 deletions(-) diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts index 28ab397f7..3e3a1459e 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts @@ -1,24 +1,27 @@ // @ts-nocheck -declare global { - namespace GameAction { - let LogNames: boolean; - let LogItemLevel: boolean; - let LogEquipped: boolean; - let LogMerc: boolean; - let SaveScreenShot: boolean; - let IngameTime: number; - let task: any; // Update the type of `task` as needed +export interface GameActionType { + LogNames: boolean; + LogItemLevel: boolean; + LogEquipped: boolean; + LogMerc: boolean; + SaveScreenShot: boolean; + IngameTime: number; + task: { action: string, data: any } | null; + + // Methods + init(task: string): void; + update(action: string, data: string | Object): void; + gameInfo(): { gameName: string, gamePass: string }; + getLogin(): { realm: string, account: string, password: string }; + getCharacters(): string[]; + inGameCheck(): boolean; + load(hash: string): string; + save(hash: string, data: string): void; + dropItems(dropList: string[]): void; + convertLadderFiles(): void; +} - function init(task: any): void; - function update(action: string, data: any): void; - function gameInfo(): { gameName: string, gamePass: string }; - function getLogin(): { realm: string, account: string, password: string }; - function getCharacters(): string[]; - function inGameCheck(): boolean; - function load(hash: string): string; - function save(hash: string, data: string): void; - function dropItems(dropList: string[]): void; - function convertLadderFiles(): void; - } +declare global { + const GameAction: GameActionType; } export {}; \ No newline at end of file diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js index 3e33fd2b2..c0ced3e89 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -7,6 +7,7 @@ */ include("systems/mulelogger/MuleLogger.js"); +/** @type {import("./GameAction").GameActionType} */ const GameAction = { /** @type {{ action: string, data: any } | null} */ task: null, diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts index 8b96d0ad9..3d8a80b33 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts @@ -1,66 +1,68 @@ -declare global { - namespace MuleLogger { - const LogGame: [string, string]; - let LogNames: boolean; - let LogItemLevel: boolean; - let LogEquipped: boolean; - let LogMerc: boolean; - let SaveScreenShot: boolean; - let AutoPerm: boolean; - let IngameTime: number; - const LogAccounts: { [account: string]: string[] }; - - function inGameCheck(): boolean; - /** - * Save perm status to logs/MuleLogPermInfo.json. - * @param charPermInfo - The character's permanent status information. - */ - function savePermedStatus(charPermInfo?: { charname: string; perm: boolean }): void; +export interface MuleLoggerType { + LogGame: [string, string]; + LogNames: boolean; + LogItemLevel: boolean; + LogEquipped: boolean; + LogMerc: boolean; + SaveScreenShot: boolean; + AutoPerm: boolean; + IngameTime: number; + LogAccounts: { [account: string]: string[] }; + + // Methods + inGameCheck(): boolean; + + /** + * Save perm status to logs/MuleLogPermInfo.json. + * @param charPermInfo - The character's permanent status information. + */ + savePermedStatus(charPermInfo?: { charname: string; perm: boolean }): void; - /** - * Load perm status from logs/MuleLogPermInfo.json. - * @returns The character's permanent status information. - */ - function loadPermedStatus(): { charname: string; perm: boolean }; + /** + * Load perm status from logs/MuleLogPermInfo.json. + * @returns The character's permanent status information. + */ + loadPermedStatus(): { charname: string; perm: boolean }; - /** - * @param hash - The hash value. - * @returns The loaded data. - */ - function load(hash: string): string; + /** + * @param hash - The hash value. + * @returns The loaded data. + */ + load(hash: string): string; - /** - * @param hash - The hash value. - * @param data - The data to save. - */ - function save(hash: string, data: string): void; + /** + * @param hash - The hash value. + * @param data - The data to save. + */ + save(hash: string, data: string): void; - function remove(): void; + remove(): void; - /** - * Log kept item stats in the manager. - * @param unit - The item unit. - * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. - * @returns The logged item information. - */ - function logItem(unit: ItemUnit, logIlvl?: boolean): { - itemColor: string; - image: string; - title: string; - description: string; - header: string; - sockets: any; // Update the type of `sockets` as needed - }; + /** + * Log kept item stats in the manager. + * @param unit - The item unit. + * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. + * @returns The logged item information. + */ + logItem(unit: ItemUnit, logIlvl?: boolean): { + itemColor: number; + image: string; + title: string; + description: string; + header: string; + sockets: any; + }; - /** - * Log character to D2Bot# itemviewer. - * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. - * @param logName - Log the character's name. Default: `LogNames` value. - * @param saveImg - Save the item image. Default: `SaveScreenShot` value. - */ - function logChar(logIlvl?: boolean, logName?: boolean, saveImg?: boolean): void; + /** + * Log character to D2Bot# itemviewer. + * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. + * @param logName - Log the character's name. Default: `LogNames` value. + * @param saveImg - Save the item image. Default: `SaveScreenShot` value. + */ + logChar(logIlvl?: boolean, logName?: boolean, saveImg?: boolean): void; +} - // Add more functions and properties as needed - } +declare global { + const MuleLogger: MuleLoggerType; } export {}; \ No newline at end of file diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index f5baf5dd9..731b92b07 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -7,18 +7,8 @@ * @typedef {import("../../../sdk/globals")} */ +/** @type {import("./MuleLogger").MuleLoggerType} */ const MuleLogger = { - // ~~~ DON'T TOUCH, configuration file loaded at bottom. Use LoggerConfig.js ~~~ // - LogGame: ["", ""], // ["gamename", "password"] - LogNames: true, // Put account/character name on the picture - LogItemLevel: true, // Add item level to the picture - LogEquipped: true, // include equipped items - LogMerc: true, // include items merc has equipped (if alive) - SaveScreenShot: false, // Save pictures in jpg format (saved in 'Images' folder) - AutoPerm: true, // override InGameTime to perm character - IngameTime: 0, // (180, 210) to avoid RD, increase it to (7230, 7290) for mule perming - LogAccounts: {}, - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // inGameCheck: function () { if (getScript("D2BotMuleLog.dbj") && this.LogGame[0] && me.gamename.match(this.LogGame[0], "i")) { console.log("ÿc4MuleLoggerÿc0: Logging items on " + me.account + " - " + me.name + "."); From 8703c20b77dd78e5770bf3f39e79fd5b75e6369f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:16:01 -0400 Subject: [PATCH 571/758] Add type annotations to starter config modules - Added JSDoc type annotations to AdvancedConfig.js and StarterConfig.js for improved type safety and editor support. Also added a @ts-ignore comment in Loader.d.ts to suppress TypeScript errors for the Runnable class. --- +setup/starter/AdvancedConfig.js | 1 + +setup/starter/StarterConfig.js | 1 + d2bs/kolbot/sdk/types/Loader.d.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/+setup/starter/AdvancedConfig.js b/+setup/starter/AdvancedConfig.js index 8e8c23a19..02683e6da 100644 --- a/+setup/starter/AdvancedConfig.js +++ b/+setup/starter/AdvancedConfig.js @@ -7,6 +7,7 @@ */ (function (module) { + /** @type {Record>} */ module.exports = { /* Features: Override channel for each profile, Override join delay for each profile diff --git a/+setup/starter/StarterConfig.js b/+setup/starter/StarterConfig.js index 02081b3be..5cb1ec735 100644 --- a/+setup/starter/StarterConfig.js +++ b/+setup/starter/StarterConfig.js @@ -7,6 +7,7 @@ */ (function (module) { + /** @type {Partial} */ module.exports = { MinGameTime: 360, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby PingQuitDelay: 30, // Time in seconds to wait in lobby after quitting due to high ping diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index bc4bef2e0..fc2554819 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -12,6 +12,7 @@ declare global { startArea?: number; } + // @ts-ignore class Runnable { constructor(action: () => boolean, options: Partial); From 9e48982dbd75cdabd2d45a793496fe33ae8322e1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 18:29:09 -0400 Subject: [PATCH 572/758] Fix typo in D2BotSoloPlay, had print instead of printToConsole --- d2bs/kolbot/D2BotSoloPlay.dbj | 3 ++- d2bs/kolbot/libs/SoloPlay | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/D2BotSoloPlay.dbj b/d2bs/kolbot/D2BotSoloPlay.dbj index 28f4fde8f..5f43583e4 100644 --- a/d2bs/kolbot/D2BotSoloPlay.dbj +++ b/d2bs/kolbot/D2BotSoloPlay.dbj @@ -8,7 +8,8 @@ */ // No touchy! +include("critical.js"); // required if (!include("SoloPlay/OOG/SoloEntry.js")) { - D2Bot.print("SoloPlay: Failed to include SoloEntry.js"); + D2Bot.printToConsole("SoloPlay: Failed to include SoloEntry.js"); D2Bot.stop(); } diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 46afefe33..c3f963403 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 46afefe33b8d10bd84d3a1f8f7bdb252129f0587 +Subproject commit c3f96340372ed4b6a2e6a3c1bc94031cce9e9435 From 01df32c4d80beca5f97803d345c7acafa8207f29 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 5 Jul 2025 19:47:04 -0400 Subject: [PATCH 573/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index c3f963403..ac816f15d 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit c3f96340372ed4b6a2e6a3c1bc94031cce9e9435 +Subproject commit ac816f15d790026539d89430d6a69be30caebde9 From 1c516f98b7df37cda150d1bdd911d4054c51d64e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 00:54:20 -0400 Subject: [PATCH 574/758] Add debug path visualization for clearLevel - Introduces debug hooks to visualize pathing during room clearing in Attack.js and Cows.js. When Config.DebugMode.Path is enabled, rooms are highlighted and labeled with incrementing numbers. - Hooks are properly removed after execution to prevent lingering debug artifacts. --- d2bs/kolbot/libs/core/Attack.js | 13 +++++++++++++ d2bs/kolbot/libs/core/Common/Cows.js | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 3f0908878..c7921d48e 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1433,6 +1433,13 @@ const Attack = { let myRoom, previousArea; let rooms = []; const currentArea = getArea().id; + /** @type {Text[]} */ + const hooks = []; + + /** @param {Text} hook */ + const clearHook = function (hook) { + hook && hook.remove(); + }; do { rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); @@ -1468,6 +1475,10 @@ const Attack = { let result = Pather.getNearestWalkable(room[0], room[1], 18, 3); if (result) { + if (Config.DebugMode.Path) { + CollMap.drawRoom(room[2], "green"); + hooks.push(new Text((++count).toString(), room[0], room[1], 2, 1, null, true)); + } Pather.moveToEx( result[0], result[1], { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } } @@ -1487,6 +1498,8 @@ const Attack = { } //this.storeStatistics(getAreaName(me.area)); + CollMap.removeHooks(); + hooks.forEach(clearHook); console.info(false, getAreaName(currentArea), "clearLevel"); return true; diff --git a/d2bs/kolbot/libs/core/Common/Cows.js b/d2bs/kolbot/libs/core/Common/Cows.js index 7a73cadcb..6842a2d06 100644 --- a/d2bs/kolbot/libs/core/Common/Cows.js +++ b/d2bs/kolbot/libs/core/Common/Cows.js @@ -45,7 +45,14 @@ Config.MFLeader && Pather.makePortal() && say("cows"); let myRoom; - let rooms = this.buildCowRooms(); + const rooms = this.buildCowRooms(); + /** @type {Text[]} */ + const hooks = []; + + /** @param {Text} hook */ + const clearHook = function (hook) { + hook && hook.remove(); + }; while (rooms.length > 0) { // get the first room + initialize myRoom var @@ -66,11 +73,18 @@ let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); if (result) { + if (Config.DebugMode.Path) { + CollMap.drawRoom(room[2], "green"); + hooks.push(new Text((++count).toString(), room[0], room[1], 2, 1, null, true)); + } Pather.moveTo(result[0], result[1], 3); if (!Attack.clear(30)) return false; } } + CollMap.removeHooks(); + hooks.forEach(clearHook); + return true; }, }, From 9cd78a6d35d414e5fb34b8be20e9c1971b09c1ae Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 01:42:47 -0400 Subject: [PATCH 575/758] Added Attack.clearRoom, removed Attack.markRoom - markRoom was unused and CollMap.drawRoom should be used instead - Allow clearing a single room with Attack.clearRoom - Fix jsdoc typings for CollMap.hooks --- d2bs/kolbot/libs/core/Attack.js | 68 +++++++++++++++++++++++-------- d2bs/kolbot/libs/core/CollMap.js | 17 ++++++-- d2bs/kolbot/sdk/types/Attack.d.ts | 2 +- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index c7921d48e..df77dcfe8 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1316,21 +1316,6 @@ const Attack = { } }, - /** - * @description Draw lines around a room on minimap - * @param {Room} room - * @param {number} color - */ - markRoom: function (room, color) { - let arr = []; - const [rX, rY] = [room.x * 5, room.y * 5]; - - arr.push(new Line(rX, rY, rX, rY + room.ysize, color, true)); - arr.push(new Line(rX, rY, rX + room.xsize, rY, color, true)); - arr.push(new Line(rX + room.xsize, rY, rX + room.xsize, rY + room.ysize, color, true)); - arr.push(new Line(rX, rY + room.ysize, rX + room.xsize, rY + room.ysize, color, true)); - }, - /** * @description Count uniques in current area within getUnit range */ @@ -1378,6 +1363,57 @@ const Attack = { Attack.ignoredGids = []; }, + /** + * @description Clear a single room based on monster spectype + * @param {Room} room - The room to clear + * @param {number} spectype - The monster spectype to clear + * @returns {boolean} + */ + clearRoom: function (room, spectype = 0) { + function getCenter(room) { + let centerX = room.x * 5 + room.xsize / 2; + let centerY = room.y * 5 + room.ysize / 2; + + let adjusted = Pather.getNearestWalkable(centerX, centerY, 18, 3); + return adjusted ? [adjusted[0], adjusted[1]] : [centerX, centerY]; + } + + const currentArea = getArea().id; + + const myRoom = getCenter(room); + const result = Pather.getNearestWalkable(myRoom[0], myRoom[1], 18, 3); + /** @param {Monster} unit */ + const shouldAttack = function (unit) { + return CollMap.coordsInRoom(unit.x, unit.y, room); + }; + + if (result) { + if (Config.DebugMode.Path) { + CollMap.drawRoom(room, "green", true); + } + let node = new PathNode(result[0], result[1]); + + Pather.move( + node, + { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } } + ); + + if (!this.clear(60, spectype, undefined, undefined, undefined, shouldAttack)) { + return false; + } + } else if (currentArea !== getArea().id) { + // Make sure bot does not get stuck in different area. + Pather.moveToEx( + myRoom[0], myRoom[1], + { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } } + ); + } + + CollMap.removeHookForRoom(room); + + return true; + }, + /** * @description Clear an entire area based on monster spectype using nearestNeighbourSearch * @param {number} spectype @@ -1431,7 +1467,7 @@ const Attack = { console.info(true, getAreaName(me.area)); let myRoom, previousArea; - let rooms = []; + const rooms = []; const currentArea = getArea().id; /** @type {Text[]} */ const hooks = []; diff --git a/d2bs/kolbot/libs/core/CollMap.js b/d2bs/kolbot/libs/core/CollMap.js index 88f1af755..e5778bddf 100644 --- a/d2bs/kolbot/libs/core/CollMap.js +++ b/d2bs/kolbot/libs/core/CollMap.js @@ -8,7 +8,7 @@ const CollMap = new function () { this.rooms = []; this.maps = []; - /** @type {Line[]} */ + /** @type {{ room: Room, lines: Line[]}[]} */ this.hooks = []; this.colors = { green: 0x84, @@ -44,17 +44,26 @@ const CollMap = new function () { this.hooks.push({ room: room, lines: lines }); }; - /** @param {Room} room */ + /** @param {Hook} hook */ + const clearHook = function (hook) { + hook && hook.remove(); + }; + + /** + * @this {CollMap} + * @param {Room} room + */ this.removeHookForRoom = function (room) { let index = this.hooks.findIndex(h => h.room.x === room.x && h.room.y === room.y); if (index !== -1) { - this.hooks[index].lines.forEach(l => l.remove()); + this.hooks[index].lines.forEach(clearHook); this.hooks.splice(index, 1); } }; + /** @this {CollMap} */ this.removeHooks = function () { - this.hooks.forEach(hook => hook.lines.forEach(l => l.remove())); + this.hooks.forEach(hook => hook.lines.forEach(clearHook)); this.hooks = []; }; diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 16a525e44..96cd0448c 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -50,9 +50,9 @@ declare global { skipIds?: number[]; } function securePosition(x: number, y: number, options: SecurePositionOptions): void; - function markRoom(room: Room, color: number): void; function countUniques(): void; function storeStatistics(area: number): void; + function clearRoom(room: Room, spectype?: number): boolean; function clearLevel(spectype?: number): boolean; function sortMonsters(unitA: Unit, unitB: Unit): boolean; function validSpot(x: number, y: number, skill?: number, unitid?: number): boolean; From 61b82be6f0fc8cb8da433e079c38ad9686a2100c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 11:05:19 -0400 Subject: [PATCH 576/758] Swap to using lazy module loading for Common modules - Turn each Common/ file into module that can be lazy loaded when needed --- d2bs/kolbot/libs/core/Common.js | 141 +- d2bs/kolbot/libs/core/Common/Ancients.js | 268 ++-- d2bs/kolbot/libs/core/Common/Baal.js | 539 ++++---- d2bs/kolbot/libs/core/Common/Cain.js | 226 ++-- d2bs/kolbot/libs/core/Common/Cows.js | 210 ++- d2bs/kolbot/libs/core/Common/Diablo.js | 1172 +++++++++-------- d2bs/kolbot/libs/core/Common/Leecher.js | 72 +- d2bs/kolbot/libs/core/Common/Smith.js | 38 +- d2bs/kolbot/libs/core/Common/Tools.js | 590 +++++---- .../libs/manualplay/threads/MapToolsThread.js | 1 - d2bs/kolbot/libs/scripts/Baal.js | 1 - d2bs/kolbot/libs/scripts/BaalAssistant.js | 1 - d2bs/kolbot/libs/scripts/BaalHelper.js | 2 - .../libs/scripts/ClassicChaosAssistant.js | 1 - d2bs/kolbot/libs/scripts/Cows.js | 2 - d2bs/kolbot/libs/scripts/Diablo.js | 1 - d2bs/kolbot/libs/scripts/DiabloHelper.js | 1 - d2bs/kolbot/libs/scripts/MFHelper.js | 1 - d2bs/kolbot/libs/scripts/Questing.js | 3 - d2bs/kolbot/libs/scripts/TravincalLeech.js | 1 - d2bs/kolbot/libs/scripts/Tristram.js | 1 - d2bs/kolbot/libs/scripts/TristramLeech.js | 1 - d2bs/kolbot/libs/scripts/Wakka.js | 1 - d2bs/kolbot/libs/systems/autorush/AutoRush.js | 3 - d2bs/kolbot/sdk/globals.d.ts | 1 + d2bs/kolbot/sdk/types/Common.d.ts | 16 + d2bs/kolbot/sdk/types/include-paths.d.ts | 2 +- d2bs/kolbot/threads/RushThread.js | 3 - d2bs/kolbot/threads/ToolsThread.js | 1 - 29 files changed, 1760 insertions(+), 1540 deletions(-) create mode 100644 d2bs/kolbot/sdk/types/Common.d.ts diff --git a/d2bs/kolbot/libs/core/Common.js b/d2bs/kolbot/libs/core/Common.js index 92c61ccec..9cd4e82fa 100644 --- a/d2bs/kolbot/libs/core/Common.js +++ b/d2bs/kolbot/libs/core/Common.js @@ -5,7 +5,140 @@ * */ -const Common = { - // each common functionality is loaded into this object when it's needed - // for the actual function files @see core/Common/ -}; +// each common functionality is loaded into this object when it's needed +// for the actual function files @see core/Common/ +const Common = (function () { + /** + * @type {Map} + */ + const moduleCache = new Map(); + + /** + * @type {Map} + */ + const modulePathMap = new Map([ + ["Ancients", "./Common/Ancients"], + ["Baal", "./Common/Baal"], + ["Cain", "./Common/Cain"], + ["Cows", "./Common/Cows"], + ["Diablo", "./Common/Diablo"], + ["Leecher", "./Common/Leecher"], + ["Smith", "./Common/Smith"], + ["Toolsthread", "./Common/Tools"], + ]); + + /** + * Lazy load a Common module + * @param {string} moduleName - The name of the module to load + * @returns {any} The loaded module + */ + function loadModule(moduleName) { + if (moduleCache.has(moduleName)) { + return moduleCache.get(moduleName); + } + + let modulePath = modulePathMap.get(moduleName); + if (!modulePath) { + throw new Error("Unknown module: " + moduleName); + } + + try { + console.debug("Loading Common module: " + moduleName + " from " + modulePath); + let module = require(modulePath); + moduleCache.set(moduleName, module); + return module; + } catch (error) { + throw new Error("Failed to load module " + moduleName + ": " + error.message); + } + } + + return new Proxy({}, { + /** + * @param {Object} target + * @param {string} property + * @returns {any} + */ + get: function (target, property) { + if (typeof property === "string" && modulePathMap.has(property)) { + return loadModule(property); + } + return target[property]; + }, + + /** + * @param {Object} target + * @param {string} property + * @param {any} value + * @returns {boolean} + */ + set: function (target, property, value) { + target[property] = value; + return true; + }, + + /** + * @param {Object} target + * @param {string} property + * @returns {boolean} + */ + has: function (target, property) { + return typeof property === "string" && ( + modulePathMap.has(property) + || target.hasOwnProperty(property) + ); + }, + + /** + * @param {Object} target + * @returns {string[]} + */ + ownKeys: function (target) { + return Array.from(modulePathMap.keys()).concat(Object.keys(target)); + }, + + /** + * @param {Object} target + * @param {string} property + * @returns {PropertyDescriptor | undefined} + */ + getOwnPropertyDescriptor: function (target, property) { + if (typeof property === "string" && modulePathMap.has(property)) { + return { + enumerable: true, + configurable: true, + get: function () { + return loadModule(property); + } + }; + } + return Object.getOwnPropertyDescriptor(target, property); + }, + + /** + * @param {Object} target + * @param {string} property + * @returns {boolean} + */ + deleteProperty: function (target, property) { + if (typeof property === "string" && modulePathMap.has(property)) { + // Remove from cache if it exists + if (moduleCache.has(property)) { + moduleCache.delete(property); + } + // Remove from target if it exists + if (target.hasOwnProperty(property)) { + delete target[property]; + } + return true; + } + + // Handle regular properties + if (target.hasOwnProperty(property)) { + delete target[property]; + return true; + } + + return false; + } + }); +})(); diff --git a/d2bs/kolbot/libs/core/Common/Ancients.js b/d2bs/kolbot/libs/core/Common/Ancients.js index 15ddd8b0e..f99a56f2c 100644 --- a/d2bs/kolbot/libs/core/Common/Ancients.js +++ b/d2bs/kolbot/libs/core/Common/Ancients.js @@ -5,151 +5,147 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Ancients", { - value: new function () { - this.altarSpot = { x: 10047, y: 12622 }; - this.archway = { x: 10050, y: 12637 }; - this.talicStatue = { x: 10037, y: 12617 }; - this.madawcStatue = { x: 10048, y: 12607 }; - this.korlicStatue = { x: 10058, y: 12617 }; - this.lastPrep = 0; - - this.canAttack = function () { - let ancient = Game.getMonster(); - - if (ancient) { - do { - if (!ancient.getParent() && !Attack.canAttack(ancient)) { - console.log("Can't attack ancients"); - return false; - } - } while (ancient.getNext()); - } - - return true; - }; - - this.touchAltar = function () { - let altar = Misc.poll(() => Game.getObject(sdk.objects.AncientsAltar), 5000, 100); - - if (altar) { - while (altar.mode !== sdk.objects.mode.Active) { - if (Skill.haveTK) { - (this.archway.distance > 1 || altar.distance > 20) && Pather.moveToUnit(this.archway); - Packet.telekinesis(altar); - } else { - Pather.moveToUnit(altar); - altar.interact(); - } - delay(200 + me.ping); - me.cancel(); +(function (module) { + module.exports = new function () { + this.altarSpot = { x: 10047, y: 12622 }; + this.archway = { x: 10050, y: 12637 }; + this.talicStatue = { x: 10037, y: 12617 }; + this.madawcStatue = { x: 10048, y: 12607 }; + this.korlicStatue = { x: 10058, y: 12617 }; + this.lastPrep = 0; + + this.canAttack = function () { + let ancient = Game.getMonster(); + + if (ancient) { + do { + if (!ancient.getParent() && !Attack.canAttack(ancient)) { + console.log("Can't attack ancients"); + return false; } - - // wait for ancients to spawn - while (!Game.getMonster(sdk.monsters.TalictheDefender)) { - delay(250 + me.ping); + } while (ancient.getNext()); + } + + return true; + }; + + this.touchAltar = function () { + let altar = Misc.poll(() => Game.getObject(sdk.objects.AncientsAltar), 5000, 100); + + if (altar) { + while (altar.mode !== sdk.objects.mode.Active) { + if (Skill.haveTK) { + (this.archway.distance > 1 || altar.distance > 20) && Pather.moveToUnit(this.archway); + Packet.telekinesis(altar); + } else { + Pather.moveToUnit(altar); + altar.interact(); } - - return true; - } else { - Pather.moveNearUnit(this.altarSpot, (Skill.haveTK ? 19 : 5)); + delay(200 + me.ping); + me.cancel(); } - return false; - }; - - this.checkStatues = function () { - let statues = getUnits(sdk.unittype.Object) - .filter(u => [ - sdk.objects.KorlictheProtectorStatue, - sdk.objects.TalictheDefenderStatue, - sdk.objects.MadawctheGuardianStatue].includes(u.classid) - && u.mode === sdk.objects.mode.Active); - return statues.length === 3; - }; - - this.checkCorners = function () { - let pos = [ - { x: 10036, y: 12592 }, { x: 10066, y: 12589 }, - { x: 10065, y: 12623 }, { x: 10058, y: 12648 }, - { x: 10040, y: 12660 }, { x: 10036, y: 12630 }, - { x: 10038, y: 12611 } - ]; - Pather.moveToUnit(this.altarSpot); - if (!this.checkStatues()) { - return pos.forEach((node) => { - // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { - return; - } - Pather.moveTo(node.x, node.y); - Attack.clear(30); - }); + // wait for ancients to spawn + while (!Game.getMonster(sdk.monsters.TalictheDefender)) { + delay(250 + me.ping); } return true; - }; - - this.killAncients = function (checkQuest = false) { - let retry = 0; - let attackRange = Skill.getRange(Config.AttackSkill[1]); - Pather.moveNearUnit(this.altarSpot, attackRange); - - while (!this.checkStatues()) { - if (retry > 5) { - console.log("Failed to kill anicents."); - - break; + } else { + Pather.moveNearUnit(this.altarSpot, (Skill.haveTK ? 19 : 5)); + } + + return false; + }; + + this.checkStatues = function () { + let statues = getUnits(sdk.unittype.Object) + .filter(u => [ + sdk.objects.KorlictheProtectorStatue, + sdk.objects.TalictheDefenderStatue, + sdk.objects.MadawctheGuardianStatue].includes(u.classid) + && u.mode === sdk.objects.mode.Active); + return statues.length === 3; + }; + + this.checkCorners = function () { + let pos = [ + { x: 10036, y: 12592 }, { x: 10066, y: 12589 }, + { x: 10065, y: 12623 }, { x: 10058, y: 12648 }, + { x: 10040, y: 12660 }, { x: 10036, y: 12630 }, + { x: 10038, y: 12611 } + ]; + Pather.moveToUnit(this.altarSpot); + if (!this.checkStatues()) { + return pos.forEach((node) => { + // no mobs at that next, skip it + if ([node.x, node.y].distance < 35 && [node.x, node.y].mobCount({ range: 30 }) === 0) { + return; } - /** - * @todo - far cast pwning the ancients - */ - Attack.clearClassids( - sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian - ); - delay(1000); - - if (checkQuest) { - if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { - break; - } - console.log("Failed to kill anicents. Attempt: " + retry); + Pather.moveTo(node.x, node.y); + Attack.clear(30); + }); + } + + return true; + }; + + this.killAncients = function (checkQuest = false) { + let retry = 0; + let attackRange = Skill.getRange(Config.AttackSkill[1]); + Pather.moveNearUnit(this.altarSpot, attackRange); + + while (!this.checkStatues()) { + if (retry > 5) { + console.log("Failed to kill anicents."); + + break; + } + /** + * @todo - far cast pwning the ancients + */ + Attack.clearClassids( + sdk.monsters.KorlictheProtector, sdk.monsters.TalictheDefender, sdk.monsters.MadawctheGuardian + ); + delay(1000); + + if (checkQuest) { + if (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.Completed)) { + break; } - - this.checkCorners(); - retry++; + console.log("Failed to kill anicents. Attempt: " + retry); } - }; - - this.ancientsPrep = function () { - Town.goToTown(); - Town.fillTome(sdk.items.TomeofTownPortal); - [ - sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion - ].forEach(p => Town.buyPots(10, p, true)); - Town.buyPotions(); - Pather.usePortal(sdk.areas.ArreatSummit, me.name); - Common.Ancients.lastPrep = getTickCount(); - }; - - this.startAncients = function (preTasks = false, checkQuest = false) { - let retry = 0; - this.touchAltar(); - while (!this.canAttack()) { - if (retry > 10) throw new Error("I think I'm unable to complete ancients, I've rolled them 10 times"); - preTasks && getTickCount() - this.lastPrep > Time.minutes(1) - ? this.ancientsPrep() - : Pather.makePortal(); - this.touchAltar(); - retry++; - } + this.checkCorners(); + retry++; + } + }; + + this.ancientsPrep = function () { + Town.goToTown(); + Town.fillTome(sdk.items.TomeofTownPortal); + [ + sdk.items.StaminaPotion, sdk.items.AntidotePotion, sdk.items.ThawingPotion + ].forEach(p => Town.buyPots(10, p, true)); + Town.buyPotions(); + Pather.usePortal(sdk.areas.ArreatSummit, me.name); + Common.Ancients.lastPrep = getTickCount(); + }; + + this.startAncients = function (preTasks = false, checkQuest = false) { + let retry = 0; + this.touchAltar(); + + while (!this.canAttack()) { + if (retry > 10) throw new Error("I think I'm unable to complete ancients, I've rolled them 10 times"); + preTasks && getTickCount() - this.lastPrep > Time.minutes(1) + ? this.ancientsPrep() + : Pather.makePortal(); + this.touchAltar(); + retry++; + } - this.killAncients(checkQuest); - }; - }, - configurable: true, - }); -})(Common); + this.killAncients(checkQuest); + }; + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 9d051b392..419ab9458 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -5,327 +5,334 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Baal", { - value: new function Baal () { - this.throneCoords = { - bottomLeft: { x: 15072, y: 5073 }, - bottomRight: { x: 15118, y: 5073 }, - bottomCenter: { x: 15093, y: 5073 }, - entraceArchway: { x: 15097, y: 5099 }, - topLeft: { x: 15072, y: 5002 }, - topRight: { x: 15118, y: 5002 }, - baal: { x: 15090, y: 5014 }, - }; - - this.checkHydra = function () { - let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - if (hydra) { - do { - if (hydra.mode !== sdk.monsters.mode.Dead - && hydra.getStat(sdk.stats.Alignment) !== 2) { - let _pos = [ - this.throneCoords.bottomLeft, this.throneCoords.bottomRight, - this.throneCoords.topRight, this.throneCoords.topLeft, - ].sort(function (a, b) { - return getDistance(me, a) - getDistance(me, b); - }).first(); - Pather.moveTo(_pos.x, _pos.y); - while (hydra.mode !== sdk.monsters.mode.Dead) { - delay(500); - if (!copyUnit(hydra).x) { - break; - } +(function (module) { + module.exports = new function Baal () { + this.throneCoords = { + bottomLeft: { x: 15072, y: 5073 }, + bottomRight: { x: 15118, y: 5073 }, + bottomCenter: { x: 15093, y: 5073 }, + entraceArchway: { x: 15097, y: 5099 }, + topLeft: { x: 15072, y: 5002 }, + topRight: { x: 15118, y: 5002 }, + baal: { x: 15090, y: 5014 }, + }; + + this.checkHydra = function () { + let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); + if (hydra) { + do { + if (hydra.mode !== sdk.monsters.mode.Dead + && hydra.getStat(sdk.stats.Alignment) !== 2) { + let _pos = [ + this.throneCoords.bottomLeft, this.throneCoords.bottomRight, + this.throneCoords.topRight, this.throneCoords.topLeft, + ].sort(function (a, b) { + return getDistance(me, a) - getDistance(me, b); + }).first(); + Pather.moveTo(_pos.x, _pos.y); + while (hydra.mode !== sdk.monsters.mode.Dead) { + delay(500); + if (!copyUnit(hydra).x) { + break; } - - break; } - } while (hydra.getNext()); - } - - return true; - }; - this.checkThrone = function (clear = true) { - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable - && monster.y < 5080 - && (monster.x > 15072 && monster.x < 15118)) { - switch (monster.classid) { - case sdk.monsters.WarpedFallen: - case sdk.monsters.WarpedShaman: - return 1; - case sdk.monsters.BaalSubjectMummy: - case sdk.monsters.BaalColdMage: - return 2; - case sdk.monsters.Council4: - return 3; - case sdk.monsters.VenomLord2: - return 4; - case sdk.monsters.ListerTheTormenter: - return 5; - default: - if (clear) { - Attack.getIntoPosition(monster, 10, sdk.collision.Ranged); - Attack.clear(15); - } - - return false; + break; + } + } while (hydra.getNext()); + } + + return true; + }; + + this.checkThrone = function (clear = true) { + let monster = Game.getMonster(); + + if (monster) { + do { + if (monster.attackable + && monster.y < 5080 + && (monster.x > 15072 && monster.x < 15118)) { + switch (monster.classid) { + case sdk.monsters.WarpedFallen: + case sdk.monsters.WarpedShaman: + return 1; + case sdk.monsters.BaalSubjectMummy: + case sdk.monsters.BaalColdMage: + return 2; + case sdk.monsters.Council4: + return 3; + case sdk.monsters.VenomLord2: + return 4; + case sdk.monsters.ListerTheTormenter: + return 5; + default: + if (clear) { + Attack.getIntoPosition(monster, 10, sdk.collision.Ranged); + Attack.clear(15); } - } - } while (monster.getNext()); - } - return false; - }; + return false; + } + } + } while (monster.getNext()); + } - this.clearThrone = function () { - if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + return false; + }; - let monList = []; + this.clearThrone = function () { + if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - if (Config.AvoidDolls) { - let mon = Game.getMonster(sdk.monsters.SoulKiller); + let monList = []; - if (mon) { - do { - // exclude dolls from the list - if (!mon.isDoll && mon.x >= 15072 && mon.x <= 15118 - && mon.y >= 5002 && mon.y <= 5079 - && mon.attackable && !Attack.skipCheck(mon)) { - monList.push(copyUnit(mon)); - } - } while (mon.getNext()); - } + if (Config.AvoidDolls) { + let mon = Game.getMonster(sdk.monsters.SoulKiller); - if (monList.length > 0) { - return Attack.clearList(monList); - } + if (mon) { + do { + // exclude dolls from the list + if (!mon.isDoll && mon.x >= 15072 && mon.x <= 15118 + && mon.y >= 5002 && mon.y <= 5079 + && mon.attackable && !Attack.skipCheck(mon)) { + monList.push(copyUnit(mon)); + } + } while (mon.getNext()); } - let pos = [ - { x: 15097, y: 5054 }, { x: 15079, y: 5014 }, - { x: 15085, y: 5053 }, { x: 15085, y: 5040 }, - { x: 15098, y: 5040 }, { x: 15099, y: 5022 }, - { x: 15086, y: 5024 }, { x: 15079, y: 5014 } - ]; - return pos.forEach(function (node) { - // no mobs at that next, skip it - if ([node.x, node.y].distance < 35 - && [node.x, node.y].mobCount({ range: 30 }) === 0) { - return; - } - Pather.moveTo(node.x, node.y); - Attack.clear(30); - }); - }; + if (monList.length > 0) { + return Attack.clearList(monList); + } + } + + + const nodes = [ + new PathNode(15097, 5054), + new PathNode(15079, 5014), + new PathNode(15085, 5053), + new PathNode(15085, 5040), + new PathNode(15098, 5040), + new PathNode(15099, 5022), + new PathNode(15086, 5024), + new PathNode(15079, 5014) + ].sort(Sort.units); + + while (nodes.length > 0) { + const node = nodes.shift(); + + // no mobs at that next, skip it + if (node.distance < 35 && node.mobCount({ range: 30 }) === 0) { + continue; + } - this.preattack = function () { - switch (me.classid) { - case sdk.player.class.Sorceress: - if ([ - sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall - ].includes(Config.AttackSkill[1])) { - if (me.getState(sdk.states.SkillDelay)) { - delay(50); - } else { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-2, 2), 5024 + rand(-2, 2)); - } + Pather.move(node); + Attack.clear(30); + + nodes.sort(Sort.units); + } + return true; + }; + + this.preattack = function () { + switch (me.classid) { + case sdk.player.class.Sorceress: + if ([ + sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall + ].includes(Config.AttackSkill[1])) { + if (me.getState(sdk.states.SkillDelay)) { + delay(50); + } else { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 15094 + rand(-2, 2), 5024 + rand(-2, 2)); } + } - break; - case sdk.player.class.Paladin: - if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { - Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); - - return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); - } + break; + case sdk.player.class.Paladin: + if (Config.AttackSkill[3] === sdk.skills.BlessedHammer) { + Config.AttackSkill[4] > 0 && Skill.setSkill(Config.AttackSkill[4], sdk.skills.hand.Right); - break; - 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, 15094 + rand(-1, 1), 5029); + return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Left); + } - return true; - } + break; + 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, 15094 + rand(-1, 1), 5029); - break; - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({ x: 15094, y: 5028 }); + return true; + } - if (check) { - return ClassAttack.placeTraps({ x: 15094, y: 5028 }, 5); - } - } + break; + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let check = ClassAttack.checkTraps({ x: 15094, y: 5028 }); - if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { - return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); + if (check) { + return ClassAttack.placeTraps({ x: 15094, y: 5028 }, 5); } - - break; } - return false; - }; + if (Config.AttackSkill[3] === sdk.skills.ShockWeb) { + return Skill.cast(Config.AttackSkill[3], sdk.skills.hand.Right, 15094, 5028); + } - this.clearWaves = function () { - Pather.moveTo(15094, me.paladin ? 5029 : 5038); + break; + } - let tick = getTickCount(); - let totalTick = getTickCount(); - let lastWave = 0; + return false; + }; - MainLoop: - while (true) { - if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; - const wave = this.checkThrone() || 0; + this.clearWaves = function () { + Pather.moveTo(15094, me.paladin ? 5029 : 5038); - switch (wave) { - case 1: - Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); + let tick = getTickCount(); + let totalTick = getTickCount(); + let lastWave = 0; - break; - case 2: - Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); + MainLoop: + while (true) { + if (!Game.getMonster(sdk.monsters.ThroneBaal)) return true; + const wave = this.checkThrone() || 0; - break; - case 3: - Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); - this.checkHydra() && (tick = getTickCount()); + switch (wave) { + case 1: + Attack.clearClassids(sdk.monsters.WarpedFallen, sdk.monsters.WarpedShaman) && (tick = getTickCount()); - break; - case 4: - Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); + break; + case 2: + Attack.clearClassids(sdk.monsters.BaalSubjectMummy, sdk.monsters.BaalColdMage) && (tick = getTickCount()); - break; - case 5: - if (Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2)) { - tick = getTickCount(); - } + break; + case 3: + Attack.clearClassids(sdk.monsters.Council4) && (tick = getTickCount()); + this.checkHydra() && (tick = getTickCount()); - 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(function () { - 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); - } - } + break; + case 4: + Attack.clearClassids(sdk.monsters.VenomLord2) && (tick = getTickCount()); - if (getTickCount() - tick > Time.seconds(20)) { - this.clearThrone(); - tick = getTickCount(); - } + break; + case 5: + if (Attack.clearClassids(sdk.monsters.ListerTheTormenter, sdk.monsters.Minion1, sdk.monsters.Minion2)) { + tick = getTickCount(); + } - if (!this.preattack()) { - delay(100); + 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(function () { + 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); } + } - break; + if (getTickCount() - tick > Time.seconds(20)) { + this.clearThrone(); + tick = getTickCount(); } - if (wave > 0 && wave > lastWave) { - lastWave = wave; + if (!this.preattack()) { + delay(100); } - switch (me.classid) { - case sdk.player.class.Amazon: - case sdk.player.class.Sorceress: - case sdk.player.class.Necromancer: - case sdk.player.class.Assassin: - if (Config.AttackSkill[3] === sdk.skills.FrozenOrb && (lastWave < 4)) { - [15106, 5040].distance > 3 && Pather.moveTo(15106, 5040); - } else { - [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); - } + break; + } - 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); + if (wave > 0 && wave > lastWave) { + lastWave = wave; + } - break; - } + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Sorceress: + case sdk.player.class.Necromancer: + case sdk.player.class.Assassin: + if (Config.AttackSkill[3] === sdk.skills.FrozenOrb && (lastWave < 4)) { + [15106, 5040].distance > 3 && Pather.moveTo(15106, 5040); + } else { + [15116, 5026].distance > 3 && Pather.moveTo(15116, 5026); + } - 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; + 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 we've been in the throne for 30 minutes that's way too long - if (getTickCount() - totalTick > Time.minutes(30)) { - return false; + 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); - delay(10); + break; } - this.clearThrone(); + // If we've been in the throne for 30 minutes that's way too long + if (getTickCount() - totalTick > Time.minutes(30)) { + return false; + } - return true; - }; - - this.killBaal = function () { - if (me.inArea(sdk.areas.ThroneofDestruction)) { - Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); - me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done - Pather.moveTo(15090, 5008); - Misc.poll(function () { - return !Game.getMonster(sdk.monsters.ThroneBaal); - }, Time.seconds(5), 100); - Precast.doPrecast(true); - Misc.poll(function () { - if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({ range: 15 })) { - Common.Baal.clearThrone(); - Pather.moveTo(15090, 5008); - } - return !Game.getMonster(sdk.monsters.ThroneBaal); - }, Time.minutes(3), 1000); + delay(10); + } + + this.clearThrone(); + + return true; + }; + + this.killBaal = function () { + if (me.inArea(sdk.areas.ThroneofDestruction)) { + Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); + me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done + Pather.moveTo(15090, 5008); + Misc.poll(function () { + return !Game.getMonster(sdk.monsters.ThroneBaal); + }, Time.seconds(5), 100); + Precast.doPrecast(true); + Misc.poll(function () { + if (me.mode === sdk.player.mode.GettingHit || me.checkForMobs({ range: 15 })) { + Common.Baal.clearThrone(); + Pather.moveTo(15090, 5008); + } + return !Game.getMonster(sdk.monsters.ThroneBaal); + }, Time.minutes(3), 1000); - let portal = Game.getObject(sdk.objects.WorldstonePortal); + let portal = Game.getObject(sdk.objects.WorldstonePortal); - if (portal) { - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } + if (portal) { + Pather.usePortal(null, null, portal); + } else { + throw new Error("Couldn't find portal."); } + } - if (me.inArea(sdk.areas.WorldstoneChamber)) { - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); + if (me.inArea(sdk.areas.WorldstoneChamber)) { + Pather.moveTo(15134, 5923); + Attack.kill(sdk.monsters.Baal); + Pickit.pickItems(); - return true; - } + return true; + } - return false; - }; - }, - configurable: true, - }); -})(Common); + return false; + }; + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Common/Cain.js b/d2bs/kolbot/libs/core/Common/Cain.js index 4dd70678a..c19a202ad 100644 --- a/d2bs/kolbot/libs/core/Common/Cain.js +++ b/d2bs/kolbot/libs/core/Common/Cain.js @@ -5,135 +5,131 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Cain", { - value: { - activateStone: function (stone) { - for (let i = 0; i < 3; i++) { - // don't use tk if we are right next to it - let useTK = (stone.distance > 5 && Skill.useTK(stone) && i === 0); - if (useTK) { - stone.distance > 13 && Attack.getIntoPosition(stone, 13, sdk.collision.Ranged); - if (!Packet.telekinesis(stone)) { - console.debug("Failed to tk: attempt: " + i); - continue; - } - } else { - [(stone.x + 1), (stone.y + 2)].distance > 5 && Pather.moveTo(stone.x + 1, stone.y + 2, 3); - Misc.click(0, 0, stone); - } - - if (Misc.poll(() => stone.mode, 1000, 50)) { - return true; +(function (module) { + module.exports = { + activateStone: function (stone) { + for (let i = 0; i < 3; i++) { + // don't use tk if we are right next to it + let useTK = (stone.distance > 5 && Skill.useTK(stone) && i === 0); + if (useTK) { + stone.distance > 13 && Attack.getIntoPosition(stone, 13, sdk.collision.Ranged); + if (!Packet.telekinesis(stone)) { + console.debug("Failed to tk: attempt: " + i); + continue; } - Packet.flash(me.gid); + } else { + [(stone.x + 1), (stone.y + 2)].distance > 5 && Pather.moveTo(stone.x + 1, stone.y + 2, 3); + Misc.click(0, 0, stone); } - // Click to stop walking in case we got stuck - !me.idle && Misc.click(0, 0, me.x, me.y); + if (Misc.poll(() => stone.mode, 1000, 50)) { + return true; + } + Packet.flash(me.gid); + } - return false; - }, + // Click to stop walking in case we got stuck + !me.idle && Misc.click(0, 0, me.x, me.y); - run: function () { - MainLoop: - while (true) { - switch (true) { - case !Game.getItem(sdk.quest.item.ScrollofInifuss) - && !Game.getItem(sdk.quest.item.KeytotheCairnStones) - && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4): - Pather.useWaypoint(sdk.areas.DarkWood, true); - Precast.doPrecast(true); + return false; + }, - if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - throw new Error("Failed to move to Tree of Inifuss"); - } + run: function () { + MainLoop: + while (true) { + switch (true) { + case !Game.getItem(sdk.quest.item.ScrollofInifuss) + && !Game.getItem(sdk.quest.item.KeytotheCairnStones) + && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, 4): + Pather.useWaypoint(sdk.areas.DarkWood, true); + Precast.doPrecast(true); + + if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { + throw new Error("Failed to move to Tree of Inifuss"); + } - let tree = Game.getObject(sdk.quest.chest.InifussTree); - !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Misc.openChest(tree); - let scroll = Misc.poll(() => Game.getItem(sdk.quest.item.ScrollofInifuss), 1000, 100); - - Pickit.pickItem(scroll); - Town.goToTown(); - Town.npcInteract("Akara"); - - break; - case Game.getItem(sdk.quest.item.ScrollofInifuss): - Town.goToTown(1); - Town.npcInteract("Akara"); - - break; - case Game.getItem(sdk.quest.item.KeytotheCairnStones) && !me.inArea(sdk.areas.StonyField): - Pather.journeyTo(sdk.areas.StonyField); - Precast.doPrecast(true); - - break; - case Game.getItem(sdk.quest.item.KeytotheCairnStones) && me.inArea(sdk.areas.StonyField): - Pather.moveToPresetMonster( - sdk.areas.StonyField, - sdk.monsters.preset.Rakanishu, - { offX: 10, offY: 10, pop: true } - ); - Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); - Pather.moveToPresetObject( - sdk.areas.StonyField, - sdk.quest.chest.StoneAlpha, - { clearSettings: { clearPath: true } } - ); - let stones = [ - Game.getObject(sdk.quest.chest.StoneAlpha), - Game.getObject(sdk.quest.chest.StoneBeta), - Game.getObject(sdk.quest.chest.StoneGamma), - Game.getObject(sdk.quest.chest.StoneDelta), - Game.getObject(sdk.quest.chest.StoneLambda) - ]; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (this.activateStone(stone)) { - stones.splice(i, 1); - i--; - } - delay(10); + let tree = Game.getObject(sdk.quest.chest.InifussTree); + !!tree && tree.distance > 5 && Pather.moveToUnit(tree); + Misc.openChest(tree); + let scroll = Misc.poll(() => Game.getItem(sdk.quest.item.ScrollofInifuss), 1000, 100); + + Pickit.pickItem(scroll); + Town.goToTown(); + Town.npcInteract("Akara"); + + break; + case Game.getItem(sdk.quest.item.ScrollofInifuss): + Town.goToTown(1); + Town.npcInteract("Akara"); + + break; + case Game.getItem(sdk.quest.item.KeytotheCairnStones) && !me.inArea(sdk.areas.StonyField): + Pather.journeyTo(sdk.areas.StonyField); + Precast.doPrecast(true); + + break; + case Game.getItem(sdk.quest.item.KeytotheCairnStones) && me.inArea(sdk.areas.StonyField): + Pather.moveToPresetMonster( + sdk.areas.StonyField, + sdk.monsters.preset.Rakanishu, + { offX: 10, offY: 10, pop: true } + ); + Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); + Pather.moveToPresetObject( + sdk.areas.StonyField, + sdk.quest.chest.StoneAlpha, + { clearSettings: { clearPath: true } } + ); + let stones = [ + Game.getObject(sdk.quest.chest.StoneAlpha), + Game.getObject(sdk.quest.chest.StoneBeta), + Game.getObject(sdk.quest.chest.StoneGamma), + Game.getObject(sdk.quest.chest.StoneDelta), + Game.getObject(sdk.quest.chest.StoneLambda) + ]; + + while (stones.some((stone) => !stone.mode)) { + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; + + if (this.activateStone(stone)) { + stones.splice(i, 1); + i--; } + delay(10); } + } - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram)) { - Pather.usePortal(sdk.areas.Tristram); - - break; - } + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.getPortal(sdk.areas.Tristram)) { + Pather.usePortal(sdk.areas.Tristram); + + break; } + } - break; - case me.inArea(sdk.areas.Tristram) - && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed): - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + break; + case me.inArea(sdk.areas.Tristram) + && !Misc.checkQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed): + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { - Town.goToTown(1); - Town.npcInteract("Akara") && console.log("Akara done"); - } + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { + Town.goToTown(1); + Town.npcInteract("Akara") && console.log("Akara done"); } - - break; - default: - break MainLoop; } - } - return true; + break; + default: + break MainLoop; + } } - }, - configurable: true, - }); -})(Common); + + return true; + } + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Common/Cows.js b/d2bs/kolbot/libs/core/Common/Cows.js index 6842a2d06..f0ed26e4f 100644 --- a/d2bs/kolbot/libs/core/Common/Cows.js +++ b/d2bs/kolbot/libs/core/Common/Cows.js @@ -5,89 +5,155 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Cows", { - value: { - buildCowRooms: function () { - let finalRooms = []; - let indexes = []; - - let kingPreset = Game.getPresetMonster(sdk.areas.MooMooFarm, sdk.monsters.preset.TheCowKing); - let badRooms = getRoom(kingPreset.roomx * 5 + kingPreset.x, kingPreset.roomy * 5 + kingPreset.y).getNearby(); - - for (let i = 0; i < badRooms.length; i += 1) { - let badRooms2 = badRooms[i].getNearby(); - - for (let j = 0; j < badRooms2.length; j += 1) { - if (indexes.indexOf(badRooms2[j].x + "" + badRooms2[j].y) === -1) { - indexes.push(badRooms2[j].x + "" + badRooms2[j].y); - } +(function (module) { + module.exports = new function Cows () { + /** @type {Room[]} */ + this._badRooms = []; + /** @type {{ id: number, area: number, x: number, y: number } | null} */ + this._kingCoords = null; + + this.buildCowRooms = function () { + /** @type {Partial[]} */ + const finalRooms = []; + /** @type {Set} */ + const indexes = new Set(); + + const kingPreset = Game.getPresetMonster(sdk.areas.MooMooFarm, sdk.monsters.preset.TheCowKing); + this._kingCoords = kingPreset.realCoords(); + /** @type {Room[]} */ + const badRooms = getRoom(this._kingCoords.x, this._kingCoords.y).getNearby(); + + for (let i = 0; i < badRooms.length; i += 1) { + this._badRooms.push(badRooms[i]); + CollMap.drawRoom(badRooms[i], "red"); + let badRooms2 = badRooms[i].getNearby(); + + for (let j = 0; j < badRooms2.length; j += 1) { + if (!indexes.has(badRooms2[j].x + "" + badRooms2[j].y)) { + CollMap.drawRoom(badRooms2[j], "white"); + indexes.add(badRooms2[j].x + "" + badRooms2[j].y); } } + } - let room = getRoom(); + let room = getRoom(); - do { - if (indexes.indexOf(room.x + "" + room.y) === -1) { - finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); - } - } while (room.getNext()); + do { + if (!indexes.has(room.x + "" + room.y)) { + finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + } + } while (room.getNext()); - return finalRooms; - }, + return finalRooms; + }; - clearCowLevel: function () { - function roomSort(a, b) { - return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); - } + // add soloplays kingTracker? + this.clearCowLevel = function () { + function roomSort(a, b) { + const aSeen = seen.has(JSON.stringify(a)); + const bSeen = seen.has(JSON.stringify(b)); - Config.MFLeader && Pather.makePortal() && say("cows"); - - let myRoom; - const rooms = this.buildCowRooms(); - /** @type {Text[]} */ - const hooks = []; - - /** @param {Text} hook */ - const clearHook = function (hook) { - hook && hook.remove(); - }; - - 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]; + if (aSeen && !bSeen) { + return 1; // a is seen, b is not seen, so b should come first + } else if (!aSeen && bSeen) { + return -1; // b is seen, a is not seen, so a should come first + } + + return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); + } + + Config.MFLeader && Pather.makePortal() && say("cows"); + + let myRoom; + /** @type {Set} */ + const seen = new Set(); + let rooms = this.buildCowRooms(); + + // Check if the starting room is in badRooms + if (!Pather.useTeleport()) { + let startRoom = getRoom(me.x, me.y); + let startRoomCoords = [startRoom.x * 5 + startRoom.xsize / 2, startRoom.y * 5 + startRoom.ysize / 2]; + let startRoomInBadRooms = this._badRooms.some(function (badRoom) { + return CollMap.coordsInRoom(startRoomCoords[0], startRoomCoords[1], badRoom); + }); + + if (startRoomInBadRooms) { + console.warn("Starting room is in badRooms, finding the closest safe room..."); + rooms.sort(function (a, b) { + // eslint-disable-next-line max-len + return getDistance(startRoomCoords[0], startRoomCoords[1], a[0], a[1]) - getDistance(startRoomCoords[0], startRoomCoords[1], b[0], b[1]); + }); + + for (let room of rooms) { + let safeRoom = !this._badRooms.some(function (badRoom) { + return CollMap.coordsInRoom(room[0], room[1], badRoom); + }); + + if (safeRoom) { + let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); + if (result) { + Pather.moveTo(result[0], result[1], 3); + myRoom = [result[0], result[1]]; + break; + } } } + } + } + + RoomLoop: + 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); - let room = rooms.shift(); - let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); - - if (result) { - if (Config.DebugMode.Path) { - CollMap.drawRoom(room[2], "green"); - hooks.push(new Text((++count).toString(), room[0], room[1], 2, 1, null, true)); + rooms.sort(roomSort); + let room = rooms.shift(); + seen.add(JSON.stringify(room)); + let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); + + if (result) { + if (!Pather.useTeleport()) { + // lets avoid running through the badrooms + let path = getPath( + me.area, + result[0], result[1], + me.x, me.y, + 0, + Pather.walkDistance + ); + if (path) { + for (let node of path) { + let pathInBadRoom = this._badRooms.some(function (badRoom) { + return CollMap.coordsInRoom(node.x, node.y, badRoom); + }); + if (pathInBadRoom) { + if (!seen.has(JSON.stringify(room))) { + seen.add(JSON.stringify(room)); + rooms.push(room); + } else { + console.warn("Seen this room before and the path still takes us through the badrooms, skip it"); + } + continue RoomLoop; + } + } } - Pather.moveTo(result[0], result[1], 3); - if (!Attack.clear(30)) return false; } + Pather.moveTo(result[0], result[1], 3); + if (!Attack.clear(30)) return false; } + } - CollMap.removeHooks(); - hooks.forEach(clearHook); - - return true; - }, - }, - configurable: true, - }); -})(Common); + return true; + }; + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 1e17e4a5a..644b28f5e 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -5,666 +5,704 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - /** - * @todo - * - keep track of seals opened and bosses killed to - * - improve targetting when using getBoss, sometimes we run to the location we want to attack from while running past the boss - * I see this mostly with viz, also have seen seis fail due to not being close enough, even though he spawned - */ - Object.defineProperty(Common, "Diablo", { - /** - * @namespace Common - * - * @typedef Diablo - * @property {boolean} diabloSpawned - * - */ - value: { - diabloSpawned: false, - diaWaitTime: Time.seconds(30), - clearRadius: 30, - done: false, - waitForGlow: false, - sealOrder: [], - openedSeals: [], - vizLayout: -1, - seisLayout: -1, - infLayout: -1, - entranceCoords: { x: 7790, y: 5544 }, - starCoords: { x: 7791, y: 5293 }, - // path coordinates - entranceToStar: [ - [7794, 5517], [7791, 5491], [7768, 5459], - [7775, 5424], [7817, 5458], [7777, 5408], - [7769, 5379], [7777, 5357], [7809, 5359], - [7805, 5330], [7780, 5317], [7791, 5293]], - starToVizA: [ - [7759, 5295], [7734, 5295], [7716, 5295], [7718, 5276], - [7697, 5292], [7678, 5293], [7665, 5276], [7662, 5314] - ], - starToVizB: [ - [7759, 5295], [7734, 5295], [7716, 5295], - [7701, 5315], [7666, 5313], [7653, 5284] - ], - starToSeisA: [ - [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], - [7775, 5205], [7804, 5193], [7814, 5169], [7788, 5153] - ], - starToSeisB: [ - [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], - [7811, 5218], [7807, 5194], [7779, 5193], [7774, 5160], [7803, 5154] - ], - starToInfA: [ - [7809, 5268], [7834, 5306], [7852, 5280], - [7852, 5310], [7869, 5294], [7895, 5295], [7919, 5290] - ], - starToInfB: [ - [7809, 5268], [7834, 5306], [7852, 5280], [7852, 5310], - [7869, 5294], [7895, 5274], [7927, 5275], [7932, 5297], [7923, 5313] - ], - // check for strays array - cleared: [], - - diabloLightsEvent: function (bytes = []) { - if (me.inArea(sdk.areas.ChaosSanctuary) - && bytes && bytes.length === 2 && bytes[0] === 0x89 && bytes[1] === 0x0C) { - Common.Diablo.diabloSpawned = true; - } - }, - - sort: function (a, b) { - if (Config.BossPriority) { - if ((a.isSuperUnique) && (b.isSuperUnique)) return getDistance(me, a) - getDistance(me, b); - if (a.isSuperUnique) return -1; - if (b.isSuperUnique) return 1; - } +(function (module) { + const _Diablo = { + diabloSpawned: false, + diaWaitTime: Time.seconds(30), + clearRadius: 30, + done: false, + waitForGlow: false, + sealOrder: [], + openedSeals: [], + vizLayout: -1, + seisLayout: -1, + infLayout: -1, + entranceCoords: new PathNode(7790, 5544), + starCoords: new PathNode(7791, 5293), + // path coordinates + entranceToStar: [ + [7794, 5517], [7791, 5491], [7768, 5459], + [7775, 5424], [7817, 5458], [7777, 5408], + [7769, 5379], [7777, 5357], [7809, 5359], + [7805, 5330], [7780, 5317], [7791, 5293]], + starToVizA: [ + [7759, 5295], [7734, 5295], [7716, 5295], [7718, 5276], + [7697, 5292], [7678, 5293], [7665, 5276], [7662, 5314] + ], + starToVizB: [ + [7759, 5295], [7734, 5295], [7716, 5295], + [7701, 5315], [7666, 5313], [7653, 5284] + ], + starToSeisA: [ + [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], + [7775, 5205], [7804, 5193], [7814, 5169], [7788, 5153] + ], + starToSeisB: [ + [7781, 5259], [7805, 5258], [7802, 5237], [7776, 5228], + [7811, 5218], [7807, 5194], [7779, 5193], [7774, 5160], [7803, 5154] + ], + starToInfA: [ + [7809, 5268], [7834, 5306], [7852, 5280], + [7852, 5310], [7869, 5294], [7895, 5295], [7919, 5290] + ], + starToInfB: [ + [7809, 5268], [7834, 5306], [7852, 5280], [7852, 5310], + [7869, 5294], [7895, 5274], [7927, 5275], [7932, 5297], [7923, 5313] + ], + // check for strays array + cleared: [], + + /** @param {number[]} bytes */ + diabloLightsEvent: function (bytes = []) { + if (me.inArea(sdk.areas.ChaosSanctuary) + && bytes + && bytes.length === 2 + && bytes[0] === 0x89 + && bytes[1] === 0x0C) { + _Diablo.diabloSpawned = true; + Misc._diabloSpawned = true; + } + }, - // Entrance to Star / De Seis - if (me.y > 5325 || me.y < 5260) return (a.y > b.y ? -1 : 1); - // Vizier - if (me.x < 7765) return (a.x > b.x ? -1 : 1); - // Infector - if (me.x > 7825) return (!checkCollision(me, a, sdk.collision.BlockWall) && a.x < b.x ? -1 : 1); + /** + * @param {Monster} a + * @param {Monster} b + * @returns {number} + */ + sort: function (a, b) { + if (Config.BossPriority) { + if ((a.isSuperUnique) && (b.isSuperUnique)) return getDistance(me, a) - getDistance(me, b); + if (a.isSuperUnique) return -1; + if (b.isSuperUnique) return 1; + } + + // Entrance to Star / De Seis + if (me.y > 5325 || me.y < 5260) return (a.y > b.y ? -1 : 1); + // Vizier + if (me.x < 7765) return (a.x > b.x ? -1 : 1); + // Infector + if (me.x > 7825) return (!checkCollision(me, a, sdk.collision.BlockWall) && a.x < b.x ? -1 : 1); + + return getDistance(me, a) - getDistance(me, b); + }, - return getDistance(me, a) - getDistance(me, b); - }, + /** + * @param {number} seal + * @param {number} value + * @returns {number} + */ + getLayout: function (seal, value) { + let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); + if (!seal) throw new Error("Seal preset not found. Can't continue."); + let _seal = sealPreset.realCoords(); + + if (_seal.y === value || _seal.x === value) { + return 1; + } + + return 2; + }, - getLayout: function (seal, value) { - let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); - if (!seal) throw new Error("Seal preset not found. Can't continue."); + /** + * - VizLayout - 1 = "Y", 2 = "L" + * - SeisLayout - 1 = "2", 2 = "5" + * - InfLayout - 1 = "I", 2 = "J" + */ + initLayout: function () { + // 1 = "Y", 2 = "L" + _Diablo.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); + // 1 = "2", 2 = "5" + _Diablo.seisLayout = this.getLayout(sdk.objects.DiabloSealSeis, 7773); + // 1 = "I", 2 = "J" + _Diablo.infLayout = this.getLayout(sdk.objects.DiabloSealInfector, 7893); + }, - if (sealPreset.roomy * 5 + sealPreset.y === value - || sealPreset.roomx * 5 + sealPreset.x === value) { - return 1; + /** + * Follow static path + * @param {number[][]} path + * @returns {void} + */ + followPath: function (path) { + if (Config.Diablo.Fast) { + let last = path.last(); + let lastNode = new PathNode(last[0], last[1]); + Pather.moveToUnit(lastNode); + return; + } + + const canTele = Pather.canTeleport(); + + for (let i = 0; i < path.length; i++) { + this.cleared.length > 0 && this.clearStrays(); + + // no monsters at the next node, skip it + let next = i + 1 !== path.length ? path[i + 1] : null; + if (next && next.distance < 40 && next.mobCount({ range: 35 }) === 0) { + continue; } - return 2; - }, + let node = new PathNode(path[i][0], path[i][1]); + Pather.moveTo(node.x, node.y, 3, node.distance > 50); + Attack.clear(this.clearRadius, 0, false, _Diablo.sort); - /** - * - VizLayout - 1 = "Y", 2 = "L" - * - SeisLayout - 1 = "2", 2 = "5" - * - InfLayout - 1 = "I", 2 = "J" - */ - initLayout: function () { - // 1 = "Y", 2 = "L" - Common.Diablo.vizLayout = this.getLayout(sdk.objects.DiabloSealVizier, 5275); - // 1 = "2", 2 = "5" - Common.Diablo.seisLayout = this.getLayout(sdk.objects.DiabloSealSeis, 7773); - // 1 = "I", 2 = "J" - Common.Diablo.infLayout = this.getLayout(sdk.objects.DiabloSealInfector, 7893); - }, + // Push cleared positions so they can be checked for strays + this.cleared.push(path[i]); - /** - * Follow static path - * @param {number[][]} path - * @returns {void} - */ - followPath: function (path) { - if (Config.Diablo.Fast) { - let last = path.last(); - let lastNode = { x: last[0], y: last[1] }; - Pather.moveToUnit(lastNode); - return; + // After 5 nodes go back 2 nodes to check for monsters - only think we should do this with tele chars + if (canTele && i === 5 && path.length > 8) { + path = path.slice(3); + i = 0; } - - for (let i = 0; i < path.length; i++) { - this.cleared.length > 0 && this.clearStrays(); - - // no monsters at the next node, skip it - let next = i + 1 !== path.length ? path[i + 1] : null; - if (next && next.distance < 40 && next.mobCount({ range: 35 }) === 0) { - continue; - } + } + }, - Pather.moveTo(path[i][0], path[i][1], 3, getDistance(me, path[i][0], path[i][1]) > 50); - Attack.clear(this.clearRadius, 0, false, Common.Diablo.sort); + clearStrays: function () { + const startPos = new PathNode(me.x, me.y); + let monster = Game.getMonster(); - // Push cleared positions so they can be checked for strays - this.cleared.push(path[i]); + if (monster) { + do { + if (!monster || !monster.attackable) continue; + for (let i = 0; i < this.cleared.length; i += 1) { + let node = new PathNode(this.cleared[i][0], this.cleared[i][1]); + if (node.distanceTo(monster) < 30 + && Attack.validSpot(monster.x, monster.y)) { + Pather.moveToUnit(monster); + Attack.clear(15, 0, false, _Diablo.sort); - // After 5 nodes go back 2 nodes to check for monsters - if (i === 5 && path.length > 8) { - path = path.slice(3); - i = 0; - } - } - }, - - clearStrays: function () { - let oldPos = { x: me.x, y: me.y }; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.attackable) { - for (let i = 0; i < this.cleared.length; i += 1) { - if (getDistance(monster, this.cleared[i][0], this.cleared[i][1]) < 30 - && Attack.validSpot(monster.x, monster.y)) { - Pather.moveToUnit(monster); - Attack.clear(15, 0, false, Common.Diablo.sort); - - break; - } - } + break; } - } while (monster.getNext()); - } + } + } while (monster.getNext()); + } - getDistance(me, oldPos.x, oldPos.y) > 5 && Pather.moveTo(oldPos.x, oldPos.y); + startPos.distance > 5 && Pather.move(startPos); - return true; - }, + return true; + }, - /** - * @param {number[] | string[]} sealOrder - * @param {boolean} openSeals - */ - runSeals: function (sealOrder, openSeals = true, recheck = false) { - console.log("seal order: " + sealOrder); - Common.Diablo.sealOrder = sealOrder; - let seals = { - 1: () => this.vizierSeal(openSeals), - 2: () => this.seisSeal(openSeals), - 3: () => this.infectorSeal(openSeals), - "vizier": () => this.vizierSeal(openSeals), - "seis": () => this.seisSeal(openSeals), - "infector": () => this.infectorSeal(openSeals), - }; - try { - recheck && addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - sealOrder.forEach(seal => { - if (recheck && Common.Diablo.diabloSpawned) throw new ScriptError("Diablo spawned"); - seals[seal](); - }); - } catch (e) { - if (!(e instanceof ScriptError)) { - throw e; // it wasn't the custom error so throw it to the next handler - } - } finally { - recheck && removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + /** + * @param {number[] | string[]} sealOrder + * @param {boolean} openSeals + */ + runSeals: function (sealOrder, openSeals = true, recheck = false) { + console.log("seal order: " + sealOrder); + _Diablo.sealOrder = sealOrder; + let seals = { + 1: () => this.vizierSeal(openSeals), + 2: () => this.seisSeal(openSeals), + 3: () => this.infectorSeal(openSeals), + "vizier": () => this.vizierSeal(openSeals), + "seis": () => this.seisSeal(openSeals), + "infector": () => this.infectorSeal(openSeals), + }; + try { + addEventListener("gamepacket", _Diablo.diabloLightsEvent); + sealOrder.forEach(seal => { + if (recheck && _Diablo.diabloSpawned) throw new ScriptError("Diablo spawned"); + seals[seal](); + }); + } catch (e) { + if (!(e instanceof ScriptError)) { + throw e; // it wasn't the custom error so throw it to the next handler } - }, + } finally { + removeEventListener("gamepacket", _Diablo.diabloLightsEvent); + } + }, - /** - * Attempt casting telekinesis on seal to activate it - * @param {Unit} seal - * @returns {boolean} - */ - tkSeal: function (seal) { - if (!Skill.useTK(seal)) return false; - - for (let i = 0; i < 5; i++) { - seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); - - if (Packet.telekinesis(seal) - && Misc.poll(() => seal.mode, 1000, 100)) { - break; - } + /** + * Attempt casting telekinesis on seal to activate it + * @param {Unit} seal + * @returns {boolean} + */ + tkSeal: function (seal) { + if (!Skill.useTK(seal)) return false; + + for (let i = 0; i < 5; i++) { + seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); + + if (Packet.telekinesis(seal) + && Misc.poll(() => seal.mode, 1000, 100)) { + break; } + } - return !!seal.mode; - }, - - /** - * Open one of diablos seals - * @param {number} classid - * @returns {boolean} - */ - openSeal: function (classid) { - let seal; - const mainSeal = [ - sdk.objects.DiabloSealVizier, sdk.objects.DiabloSealSeis, sdk.objects.DiabloSealInfector - ].includes(classid); - const warn = Config.PublicMode && mainSeal && Loader.scriptName() === "Diablo"; - const seisSeal = classid === sdk.objects.DiabloSealSeis; - const infSeal = [sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealInfector2].includes(classid); - let usetk = (Skill.haveTK && (!seisSeal || this.seisLayout !== 1)); - - for (let i = 0; i < 5; i++) { - if (!seal) { - usetk - ? Pather.moveNearPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, 15) - : Pather.moveToPresetObject( - sdk.areas.ChaosSanctuary, - classid, - { offX: seisSeal ? 5 : 2, offY: seisSeal ? 5 : 0 } - ); - seal = Misc.poll(() => Game.getObject(classid), 1000, 100); - } + return !!seal.mode; + }, - if (!seal) { - console.debug("Couldn't find seal: " + classid); - return false; - } + /** + * Open one of diablos seals + * @param {number} classid + * @returns {boolean} + */ + openSeal: function (classid) { + let seal; + const mainSeal = [ + sdk.objects.DiabloSealVizier, + sdk.objects.DiabloSealSeis, + sdk.objects.DiabloSealInfector + ].includes(classid); + const warn = Config.PublicMode && mainSeal && Loader.scriptName() === "Diablo"; + const seisSeal = classid === sdk.objects.DiabloSealSeis; + const infSeal = [sdk.objects.DiabloSealInfector, sdk.objects.DiabloSealInfector2].includes(classid); + let usetk = (Skill.haveTK && (!seisSeal || this.seisLayout !== 1)); + + for (let i = 0; i < 5; i++) { + if (!seal) { + usetk + ? Pather.moveNearPreset(sdk.areas.ChaosSanctuary, sdk.unittype.Object, classid, 15) + : Pather.moveToPresetObject( + sdk.areas.ChaosSanctuary, + classid, + { offX: seisSeal ? 5 : 2, offY: seisSeal ? 5 : 0 } + ); + seal = Misc.poll(() => Game.getObject(classid), 1000, 100); + } - if (seal.mode) { - warn && say(Config.Diablo.SealWarning); - return true; - } + if (!seal) { + console.debug("Couldn't find seal: " + classid); + return false; + } - // Clear around Infector seal, Any leftover abyss knights casting decrep is bad news with Infector - if ((infSeal || i > 1) && me.getMobCount() > 1) { - Attack.clear(15); - // Move back to seal - usetk ? Pather.moveNearUnit(seal, 15) : Pather.moveToUnit(seal, seisSeal ? 5 : 2, seisSeal ? 5 : 0); - } + if (seal.mode) { + warn && say(Config.Diablo.SealWarning); + return true; + } - if (usetk && this.tkSeal(seal)) { - return seal.mode; - } else { - usetk && (usetk = false); + // Clear around Infector seal, Any leftover abyss knights casting decrep is bad news with Infector + if ((infSeal || i > 1) && me.getMobCount() > 1) { + Attack.clear(15); + // Move back to seal + usetk ? Pather.moveNearUnit(seal, 15) : Pather.moveToUnit(seal, seisSeal ? 5 : 2, seisSeal ? 5 : 0); + } - if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({ x: 7899, y: 5293 }); - check && ClassAttack.placeTraps({ x: 7899, y: 5293 }, check); - } - } + if (usetk && this.tkSeal(seal)) { + return seal.mode; + } else { + usetk && (usetk = false); - seisSeal ? Misc.poll(function () { - // stupid diablo shit, walk around the de-seis seal clicking it until we find "the spot"...sigh - if (!seal.mode) { - Pather.walkTo(seal.x + (rand(-1, 1)), seal.y + (rand(-1, 1))); - clickUnitAndWait(0, 0, seal) || seal.interact(); - } - return !!seal.mode; - }, 3000, 60) : seal.interact(); - - // de seis optimization - if (seisSeal && Attack.validSpot(seal.x + 15, seal.y)) { - Pather.walkTo(seal.x + 15, seal.y); - } else { - Pather.walkTo(seal.x - 5, seal.y - 5); + if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { + if (Config.UseTraps) { + let check = ClassAttack.checkTraps({ x: 7899, y: 5293 }); + check && ClassAttack.placeTraps({ x: 7899, y: 5293 }, check); } } - delay(seisSeal ? 1000 + me.ping : 500 + me.ping); + seisSeal ? Misc.poll(function () { + // stupid diablo shit, walk around the de-seis seal clicking it until we find "the spot"...sigh + if (!seal.mode) { + Pather.walkTo(seal.x + (rand(-1, 1)), seal.y + (rand(-1, 1))); + clickUnitAndWait(0, 0, seal) || seal.interact(); + } + return !!seal.mode; + }, 3000, 60) : seal.interact(); - if (seal.mode) { - break; + // de seis optimization + if (seisSeal && Attack.validSpot(seal.x + 15, seal.y)) { + Pather.walkTo(seal.x + 15, seal.y); + } else { + Pather.walkTo(seal.x - 5, seal.y - 5); } } - return (!!seal && seal.mode); - }, - - /** - * @param {boolean} openSeal - * @returns {boolean} - */ - vizierSeal: function (openSeal = true) { - console.log("Viz layout " + Common.Diablo.vizLayout); - let path = (Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); - let distCheck = path.last(); - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } - - distCheck.distance > 30 && this.followPath(Common.Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + delay(seisSeal ? 1000 + me.ping : 500 + me.ping); - if (openSeal - && ![sdk.objects.DiabloSealVizier2, sdk.objects.DiabloSealVizier].every(s => Common.Diablo.openSeal(s))) { - throw new Error("Failed to open Vizier seals."); - } - - delay(1 + me.ping); - let cb = () => { - let viz = Game.getMonster(getLocaleString(sdk.locale.monsters.GrandVizierofChaos)); - return viz && (viz.distance < Skill.getRange(Config.AttackSkill[1]) || viz.dead); - }; - /** - * @todo better coords or maybe a delay, viz appears in different locations and sometimes its right where we are moving to - * which is okay for hammerdins or melee chars but not for soft chars like sorcs - */ - Common.Diablo.vizLayout === 1 - ? Pather.moveToEx(7691, 5292, { callback: cb }) - : Pather.moveToEx(7695, 5316, { callback: cb }); - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.GrandVizierofChaos))) { - throw new Error("Failed to kill Vizier"); + if (seal.mode) { + break; } + } - Config.Diablo.SealLeader && say("out"); + return (!!seal && seal.mode); + }, + /** + * @param {boolean} openSeal + * @returns {boolean} + */ + vizierSeal: function (openSeal = true) { + const vizier = getLocaleString(sdk.locale.monsters.GrandVizierofChaos); + if (Attack.haveKilled(vizier)) { return true; - }, - + } + console.log("Viz layout " + _Diablo.vizLayout); + let path = (_Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + _Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + distCheck.distance > 30 && this.followPath(_Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + + if (openSeal + && ![sdk.objects.DiabloSealVizier2, sdk.objects.DiabloSealVizier].every(s => _Diablo.openSeal(s))) { + throw new Error("Failed to open Vizier seals."); + } + + delay(1 + me.ping); + let cb = () => { + let viz = Game.getMonster(vizier); + return viz && (viz.distance < Skill.getRange(Config.AttackSkill[1]) || viz.dead); + }; /** - * @param {boolean} openSeal - * @returns {boolean} - */ - seisSeal: function (openSeal = true) { - console.log("Seis layout " + Common.Diablo.seisLayout); - let path = (Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); - let distCheck = path.last(); - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); - } + * @todo better coords or maybe a delay, viz appears in different locations and sometimes its right where we are moving to + * which is okay for hammerdins or melee chars but not for soft chars like sorcs + */ + _Diablo.vizLayout === 1 + ? Pather.moveToEx(7691, 5292, { callback: cb }) + : Pather.moveToEx(7695, 5316, { callback: cb }); - if (distCheck.distance > 30) { - this.followPath(Common.Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); - } + if (!_Diablo.getBoss(vizier)) { + throw new Error("Failed to kill Vizier"); + } - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealSeis)) { - throw new Error("Failed to open de Seis seal."); - } - let cb = () => { - let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); - return seis && (seis.distance < Skill.getRange(Config.AttackSkill[1]) || seis.dead); - }; - Common.Diablo.seisLayout === 1 - ? Pather.moveToEx(7798, 5194, { callback: cb }) - : Pather.moveToEx(7796, 5155, { callback: cb }); - try { - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.LordDeSeis))) { - throw new Error("Failed to kill de Seis"); - } - } catch (e) { - // sometimes we fail just because we aren't in range, - Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { - let seis = Game.getMonster(getLocaleString(sdk.locale.monsters.LordDeSeis)); - return seis && (seis.distance < 30 || seis.dead); - } }); - } + Config.Diablo.SealLeader && say("out"); - Config.Diablo.SealLeader && say("out"); + return true; + }, + /** + * @param {boolean} openSeal + * @returns {boolean} + */ + seisSeal: function (openSeal = true) { + const deSeis = getLocaleString(sdk.locale.monsters.LordDeSeis); + if (Attack.haveKilled(deSeis)) { return true; - }, - - /** - * @param {boolean} openSeal - * @returns {boolean} - */ - infectorSeal: function (openSeal = true) { - Precast.doPrecast(true); - console.log("Inf layout " + Common.Diablo.infLayout); - let path = (Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - let distCheck = path.last(); - - if (Config.Diablo.SealLeader || Config.Diablo.Fast) { - Common.Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); - Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); - Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + console.log("Seis layout " + _Diablo.seisLayout); + let path = (_Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + _Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + if (distCheck.distance > 30) { + this.followPath(_Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + } + + if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealSeis)) { + throw new Error("Failed to open de Seis seal."); + } + let cb = () => { + let seis = Game.getMonster(deSeis); + return seis && (seis.distance < Skill.getRange(Config.AttackSkill[1]) || seis.dead); + }; + _Diablo.seisLayout === 1 + ? Pather.moveToEx(7798, 5194, { callback: cb }) + : Pather.moveToEx(7796, 5155, { callback: cb }); + try { + if (!_Diablo.getBoss(deSeis)) { + throw new Error("Failed to kill de Seis"); } + } catch (e) { + // sometimes we fail just because we aren't in range, + Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { + let seis = Game.getMonster(deSeis); + return seis && (seis.distance < 30 || seis.dead); + } }); + } - distCheck.distance > 70 && this.followPath(Common.Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - - let cb = () => { - let inf = Game.getMonster(getLocaleString(sdk.locale.monsters.InfectorofSouls)); - return inf && (inf.distance < Skill.getRange(Config.AttackSkill[1]) || inf.dead); - }; - - let moveToLoc = () => { - if (Common.Diablo.infLayout === 1) { - (me.sorceress || me.assassin) && Pather.moveToEx(7876, 5296, { callback: cb }); - delay(1 + me.ping); - } else { - delay(1 + me.ping); - Pather.moveToEx(7928, 5295, { callback: cb }); - } - }; - - if (Config.Diablo.Fast) { - if (openSeal - && ![ - sdk.objects.DiabloSealInfector2, sdk.objects.DiabloSealInfector - ].every(s => Common.Diablo.openSeal(s))) { - throw new Error("Failed to open Infector seals."); - } - moveToLoc(); - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { - throw new Error("Failed to kill Infector"); - } - } else { - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector)) { - throw new Error("Failed to open Infector seals."); - } - moveToLoc(); - - if (!Common.Diablo.getBoss(getLocaleString(sdk.locale.monsters.InfectorofSouls))) { - throw new Error("Failed to kill Infector"); - } - if (openSeal && !Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2)) { - throw new Error("Failed to open Infector seals."); - } - // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss - !openSeal && [3, "infector"].includes(Common.Diablo.sealOrder.last()) && Misc.poll(() => { - if (Common.Diablo.diabloSpawned) return true; + Config.Diablo.SealLeader && say("out"); - let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); - if (lastSeal && lastSeal.mode) { - return true; - } - return false; - }, Time.minutes(3), 1000); - } - - Config.Diablo.SealLeader && say("out"); + return true; + }, + /** + * @param {boolean} openSeal + * @returns {boolean} + */ + infectorSeal: function (openSeal = true) { + const infector = getLocaleString(sdk.locale.monsters.InfectorofSouls); + if (Attack.haveKilled(infector)) { return true; - }, - - hammerdinPreAttack: function (name, amount = 5) { - if (me.paladin && Config.AttackSkill[1] === sdk.skills.BlessedHammer) { - let target = Game.getMonster(name); - - if (!target || !target.attackable) return true; - - let positions = [[6, 11], [0, 8], [8, -1], [-9, 2], [0, -11], [8, -8]]; - - for (let i = 0; i < positions.length; i += 1) { - // check if we can move there - if (Attack.validSpot(target.x + positions[i][0], target.y + positions[i][1])) { - Pather.moveTo(target.x + positions[i][0], target.y + positions[i][1]); - Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + } + Precast.doPrecast(true); + console.log("Inf layout " + _Diablo.infLayout); + let path = (_Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + let distCheck = path.last(); + + if (Config.Diablo.SealLeader || Config.Diablo.Fast) { + _Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); + Config.Diablo.SealLeader && Attack.securePosition(me.x, me.y, { range: 35, duration: 3000, skipBlocked: true }); + Config.Diablo.SealLeader && Pather.makePortal() && say("in"); + } + + distCheck.distance > 70 && this.followPath(_Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + + let cb = () => { + let inf = Game.getMonster(infector); + return inf && (inf.distance < Skill.getRange(Config.AttackSkill[1]) || inf.dead); + }; + + let moveToLoc = () => { + if (_Diablo.infLayout === 1) { + (me.sorceress || me.assassin) && Pather.moveToEx(7876, 5296, { callback: cb }); + delay(1 + me.ping); + } else { + delay(1 + me.ping); + Pather.moveToEx(7928, 5295, { callback: cb }); + } + }; - for (let n = 0; n < amount; n += 1) { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); - } + if (Config.Diablo.Fast) { + if (openSeal + && ![ + sdk.objects.DiabloSealInfector2, sdk.objects.DiabloSealInfector + ].every(s => _Diablo.openSeal(s))) { + throw new Error("Failed to open Infector seals."); + } + moveToLoc(); - return true; - } - } + if (!_Diablo.getBoss(infector)) { + throw new Error("Failed to kill Infector"); } - - return false; - }, - - preattack: function (id) { - let coords = (() => { - switch (id) { - case getLocaleString(sdk.locale.monsters.GrandVizierofChaos): - return Common.Diablo.vizLayout === 1 ? [7676, 5295] : [7684, 5318]; - case getLocaleString(sdk.locale.monsters.LordDeSeis): - return Common.Diablo.seisLayout === 1 ? [7778, 5216] : [7775, 5208]; - case getLocaleString(sdk.locale.monsters.InfectorofSouls): - return Common.Diablo.infLayout === 1 ? [7913, 5292] : [7915, 5280]; - default: - return []; - } - })(); - if (!coords.length) return false; + } else { + if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealInfector)) { + throw new Error("Failed to open Infector seals."); + } + moveToLoc(); - switch (me.classid) { - case sdk.player.class.Sorceress: - if ([ - sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall - ].includes(Config.AttackSkill[1])) { - me.skillDelay && delay(500); - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, coords[0], coords[1]); + if (!_Diablo.getBoss(infector)) { + throw new Error("Failed to kill Infector"); + } + if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealInfector2)) { + throw new Error("Failed to open Infector seals."); + } + // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss + !openSeal && [3, "infector"].includes(_Diablo.sealOrder.last()) && Misc.poll(() => { + if (_Diablo.diabloSpawned) return true; + let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); + if (lastSeal && lastSeal.mode) { return true; } + return false; + }, Time.minutes(3), 1000); + } - break; - case sdk.player.class.Paladin: - return this.hammerdinPreAttack(id, 8); - case sdk.player.class.Assassin: - if (Config.UseTraps) { - let trapCheck = ClassAttack.checkTraps({ x: coords[0], y: coords[1] }); + Config.Diablo.SealLeader && say("out"); - if (trapCheck) { - ClassAttack.placeTraps({ x: coords[0], y: coords[1] }, 5); + return true; + }, - return true; - } + /** + * @param {string | number} name + * @param {number} [amount=5] + * @returns {boolean} + */ + hammerdinPreAttack: function (name, amount = 5) { + if (!me.paladin || !me.getSkill(sdk.skills.BlessedHammer)) return false; + + let target = Game.getMonster(name); + if (!target || !target.attackable) return true; + + let positions = [[6, 11], [0, 8], [8, -1], [-9, 2], [0, -11], [8, -8]]; + + for (let pos of positions) { + const [offX, offY] = pos; + // check if we can move there + if (Attack.validSpot(target.x + offX, target.y + offY)) { + Pather.moveTo(target.x + offX, target.y + offY); + Skill.setSkill(Config.AttackSkill[2], sdk.skills.hand.Right); + + for (let n = 0; n < amount; n += 1) { + Skill.cast(sdk.skills.BlessedHammer, sdk.skills.hand.Left); + target = Game.getMonster(name); + if (!target || !target.attackable) return true; } - break; + return true; } + } + + return false; + }, - return false; - }, - - getBoss: function (name) { - let glow = Game.getObject(sdk.objects.SealGlow); + /** + * @param {string} id + * @returns {boolean} + */ + preattack: function (id) { + const coords = (() => { + switch (id) { + case getLocaleString(sdk.locale.monsters.GrandVizierofChaos): + return _Diablo.vizLayout === 1 ? [7676, 5295] : [7684, 5318]; + case getLocaleString(sdk.locale.monsters.LordDeSeis): + return _Diablo.seisLayout === 1 ? [7778, 5216] : [7775, 5208]; + case getLocaleString(sdk.locale.monsters.InfectorofSouls): + return _Diablo.infLayout === 1 ? [7913, 5292] : [7915, 5280]; + default: + return []; + } + })(); + if (!coords.length) return false; + + let boss = Game.getMonster(id); + if (boss && boss.dead) return true; + + switch (me.classid) { + case sdk.player.class.Sorceress: + if ([ + sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall + ].includes(Config.AttackSkill[1])) { + me.skillDelay && delay(500); + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, coords[0], coords[1]); + + return true; + } - if (this.waitForGlow) { - while (true) { - if (!this.preattack(name)) { - delay(500); - } + break; + case sdk.player.class.Paladin: + return this.hammerdinPreAttack(id, 8); + case sdk.player.class.Assassin: + if (Config.UseTraps) { + let trapCheck = ClassAttack.checkTraps({ x: coords[0], y: coords[1] }); - glow = Game.getObject(sdk.objects.SealGlow); + if (trapCheck) { + ClassAttack.placeTraps({ x: coords[0], y: coords[1] }, 5); - if (glow) { - break; - } + return true; } } - for (let i = 0; i < 16; i += 1) { - let boss = Game.getMonster(name); + break; + } + + return false; + }, - if (boss) { - Common.Diablo.hammerdinPreAttack(name, 8); - return (Config.Diablo.Fast ? Attack.kill(boss) : Attack.clear(40, 0, boss, this.sort)); + /** + * @param {string | number} name + * @returns {boolean} + */ + getBoss: function (name) { + let glow = Game.getObject(sdk.objects.SealGlow); + + if (this.waitForGlow) { + while (true) { + if (!this.preattack(name)) { + delay(500); } - delay(250); - } + glow = Game.getObject(sdk.objects.SealGlow); - return !!glow; - }, - - moveToStar: function () { - switch (me.classid) { - case sdk.player.class.Amazon: - case sdk.player.class.Sorceress: - case sdk.player.class.Necromancer: - case sdk.player.class.Assassin: - return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), { returnSpotOnError: true }); - case sdk.player.class.Paladin: - case sdk.player.class.Druid: - case sdk.player.class.Barbarian: - return Pather.moveTo(7788, 5292); + if (glow) { + break; + } } + } - return false; - }, + for (let i = 0; i < 16; i += 1) { + let boss = Game.getMonster(name); - diabloPrep: function () { - if (Config.Diablo.SealLeader) { - Pather.moveTo(7763, 5267); - Pather.makePortal() && say("in"); - Pather.moveTo(7788, 5292); + if (boss) { + _Diablo.hammerdinPreAttack(name, 8); + if (!Config.Diablo.Fast && boss.distance > 40) { + // + } + return (Config.Diablo.Fast ? Attack.kill(boss) : Attack.clear(40, 0, boss, this.sort)); } - this.moveToStar(); - - let tick = getTickCount(); + delay(250); + } - while (getTickCount() - tick < this.diaWaitTime) { - if (getTickCount() - tick >= Time.seconds(8)) { - switch (me.classid) { - 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); - } + return !!glow; + }, - delay(500); + moveToStar: function () { + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Sorceress: + case sdk.player.class.Necromancer: + case sdk.player.class.Assassin: + return Pather.moveNear(7791, 5293, (me.sorceress ? 35 : 25), { returnSpotOnError: true }); + case sdk.player.class.Paladin: + case sdk.player.class.Druid: + case sdk.player.class.Barbarian: + return Pather.moveTo(7788, 5292); + } + + return false; + }, - break; - case sdk.player.class.Paladin: - Skill.setSkill(Config.AttackSkill[2]); - if (Config.AttackSkill[1] === sdk.skills.BlessedHammer) { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); - } + diabloPrep: function () { + if (Config.Diablo.SealLeader) { + Pather.moveTo(7763, 5267); + Pather.makePortal() && say("in"); + Pather.moveTo(7788, 5292); + } + + this.moveToStar(); + + let tick = getTickCount(); + + while (getTickCount() - tick < this.diaWaitTime) { + if (getTickCount() - tick >= Time.seconds(8)) { + switch (me.classid) { + 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); + } - 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); + delay(500); - break; - } + break; + case sdk.player.class.Paladin: + Skill.setSkill(Config.AttackSkill[2]); + if (Config.AttackSkill[1] === sdk.skills.BlessedHammer) { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Left); + } - delay(500); + 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; - 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); - } - - if (Config.AttackSkill[1] === sdk.skills.ShockWeb) { - Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793, 5293); - } + } - delay(500); + delay(500); - break; - default: - 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); + } - break; + if (Config.AttackSkill[1] === sdk.skills.ShockWeb) { + Skill.cast(Config.AttackSkill[1], sdk.skills.hand.Right, 7793, 5293); } - } else { + delay(500); - } - if (Game.getMonster(sdk.monsters.Diablo)) { - return true; + break; + default: + delay(500); + + break; } + } else { + delay(500); + } + + if (Game.getMonster(sdk.monsters.Diablo)) { + return true; } + } - throw new Error("Diablo not found"); - }, + throw new Error("Diablo not found"); }, - configurable: true, - }); -})(Common); + }; + + module.exports = _Diablo; +})(module); diff --git a/d2bs/kolbot/libs/core/Common/Leecher.js b/d2bs/kolbot/libs/core/Common/Leecher.js index f7a65262e..4174d71c1 100644 --- a/d2bs/kolbot/libs/core/Common/Leecher.js +++ b/d2bs/kolbot/libs/core/Common/Leecher.js @@ -5,47 +5,45 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Leecher", { - value: { - leadTick: 0, - leader: null, - killLeaderTracker: false, - currentScript: "", - nextScriptAreas: [sdk.areas.TowerCellarLvl5, sdk.areas.PitLvl1, sdk.areas.PitLvl2, sdk.areas.BurialGrounds, - sdk.areas.CatacombsLvl4, sdk.areas.MooMooFarm, sdk.areas.DuranceofHateLvl3, - sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber - ], +(function (module) { + const Leecher = { + leadTick: 0, + leader: null, + killLeaderTracker: false, + currentScript: "", + nextScriptAreas: [sdk.areas.TowerCellarLvl5, sdk.areas.PitLvl1, sdk.areas.PitLvl2, sdk.areas.BurialGrounds, + sdk.areas.CatacombsLvl4, sdk.areas.MooMooFarm, sdk.areas.DuranceofHateLvl3, + sdk.areas.ChaosSanctuary, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber + ], - leaderTracker: function () { - if (Common.Leecher.killLeaderTracker) return false; - // check every 3 seconds - if (getTickCount() - Common.Leecher.leadTick < 3000) return true; - Common.Leecher.leadTick = getTickCount(); + leaderTracker: function () { + if (Leecher.killLeaderTracker) return false; + // check every 3 seconds + if (getTickCount() - Leecher.leadTick < 3000) return true; + Leecher.leadTick = getTickCount(); - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - let party = getParty(Common.Leecher.leader); + let party = getParty(Leecher.leader); - if (party) { - // Player has moved on to another script - if (Common.Leecher.nextScriptAreas.includes(party.area)) { - if (Loader.scriptName() === Common.Leecher.currentScript) { - Common.Leecher.killLeaderTracker = true; - throw new Error("Party leader is running a new script"); - } else { - // kill process - return false; - } + if (party) { + // Player has moved on to another script + if (Leecher.nextScriptAreas.includes(party.area)) { + if (Loader.scriptName() === Leecher.currentScript) { + Leecher.killLeaderTracker = true; + throw new Error("Party leader is running a new script"); + } else { + // kill process + return false; } } - - return true; } - }, - configurable: true, - }); -})(Common); + + return true; + } + }; + + module.exports = Leecher; +})(module); diff --git a/d2bs/kolbot/libs/core/Common/Smith.js b/d2bs/kolbot/libs/core/Common/Smith.js index f709cebb8..4ecb156ba 100644 --- a/d2bs/kolbot/libs/core/Common/Smith.js +++ b/d2bs/kolbot/libs/core/Common/Smith.js @@ -5,25 +5,21 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Smith", { - value: function () { - if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { - throw new Error("Failed to move to the Smith"); - } +(function (module) { + module.exports = function () { + if (!Pather.moveToPreset(sdk.areas.Barracks, sdk.unittype.Object, sdk.quest.chest.MalusHolder)) { + throw new Error("Failed to move to the Smith"); + } - Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); - let malusChest = Game.getObject(sdk.quest.chest.MalusHolder); - !!malusChest && malusChest.distance > 5 && Pather.moveToUnit(malusChest); - Misc.openChest(malusChest); - let malus = Misc.poll(() => Game.getItem(sdk.quest.item.HoradricMalus), 1000, 100); - Pickit.pickItem(malus); - Town.goToTown(); - Town.npcInteract("Charsi"); - - return !!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete); - }, - configurable: true, - }); -})(Common); + Attack.kill(getLocaleString(sdk.locale.monsters.TheSmith)); + let malusChest = Game.getObject(sdk.quest.chest.MalusHolder); + !!malusChest && malusChest.distance > 5 && Pather.moveToUnit(malusChest); + Misc.openChest(malusChest); + let malus = Misc.poll(() => Game.getItem(sdk.quest.item.HoradricMalus), 1000, 100); + Pickit.pickItem(malus); + Town.goToTown(); + Town.npcInteract("Charsi"); + + return !!Misc.checkQuest(sdk.quest.id.ToolsoftheTrade, sdk.quest.states.ReqComplete); + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index 33de53473..8ab1007d1 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -5,357 +5,355 @@ * */ -(function (Common) { - typeof Common !== "object" && (Common = {}); - Object.defineProperty(Common, "Toolsthread", { - value: new function Toolsthread () { - this.pots = { - Health: 0, - Mana: 1, - Rejuv: 2, - MercHealth: 3, - MercRejuv: 4 - }; - this.pingTimer = []; - this.pauseScripts = []; - this.stopScripts = []; - this.timerLastDrink = []; - this.cloneWalked = false; - - /** - * @param {boolean} print - * @returns {boolean} - */ - this.checkPing = function (print = true) { - // Quit after at least 5 seconds in game - if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; - - for (let i = 0; i < Config.PingQuit.length; i += 1) { - if (Config.PingQuit[i].Ping > 0) { - if (me.ping >= Config.PingQuit[i].Ping) { - me.overhead("High Ping"); - - if (this.pingTimer[i] === undefined || this.pingTimer[i] === 0) { - this.pingTimer[i] = getTickCount(); - } +(function (module) { + const Toolsthread = { + pots: { + Health: 0, + Mana: 1, + Rejuv: 2, + MercHealth: 3, + MercRejuv: 4 + }, + pingTimer: [], + pauseScripts: [], + stopScripts: [], + timerLastDrink: [], + cloneWalked: false, + + /** + * @param {boolean} print + * @returns {boolean} + */ + checkPing: function (print = true) { + // Quit after at least 5 seconds in game + if (getTickCount() - me.gamestarttime < 5000 || !me.gameReady) return false; + + for (let i = 0; i < Config.PingQuit.length; i += 1) { + if (Config.PingQuit[i].Ping > 0) { + if (me.ping >= Config.PingQuit[i].Ping) { + me.overhead("High Ping"); + + if (this.pingTimer[i] === undefined || this.pingTimer[i] === 0) { + this.pingTimer[i] = getTickCount(); + } - if (getTickCount() - this.pingTimer[i] >= Config.PingQuit[i].Duration * 1000) { - if (print) { - D2Bot.printToConsole( - "High ping (" + me.ping + "/" + Config.PingQuit[i].Ping + ") - leaving game.", - sdk.colors.D2Bot.Red - ); - } - scriptBroadcast("pingquit"); - scriptBroadcast("quit"); - - return true; + if (getTickCount() - this.pingTimer[i] >= Config.PingQuit[i].Duration * 1000) { + if (print) { + D2Bot.printToConsole( + "High ping (" + me.ping + "/" + Config.PingQuit[i].Ping + ") - leaving game.", + sdk.colors.D2Bot.Red + ); } - } else { - this.pingTimer[i] = 0; + scriptBroadcast("pingquit"); + scriptBroadcast("quit"); + + return true; } + } else { + this.pingTimer[i] = 0; } } + } - return false; - }, + return false; + }, - this.initQuitList = function () { - let temp = []; + initQuitList: function () { + let temp = []; - for (let profile of Config.QuitList) { - if (FileTools.exists("data/" + profile + ".json")) { - let string = FileAction.read("data/" + profile + ".json"); + for (let profile of Config.QuitList) { + if (FileTools.exists("data/" + profile + ".json")) { + let string = FileAction.read("data/" + profile + ".json"); - if (string) { - let obj = JSON.parse(string); + if (string) { + let obj = JSON.parse(string); - if (obj && obj.hasOwnProperty("name")) { - temp.push(obj.name); - } + if (obj && obj.hasOwnProperty("name")) { + temp.push(obj.name); } } } + } - Config.QuitList = temp.slice(0); - }; - - this.togglePause = function () { - for (let curr of this.pauseScripts) { - let script = getScript(curr); + Config.QuitList = temp.slice(0); + }, - if (script) { - if (script.running) { - curr === "default.dbj" && console.log("ÿc1Pausing."); - script.pause(); - } else { - if (curr === "default.dbj") { - console.log("ÿc2Resuming."); - } - script.resume(); + togglePause: function () { + for (let curr of this.pauseScripts) { + let script = getScript(curr); + + if (script) { + if (script.running) { + curr === "default.dbj" && console.log("ÿc1Pausing."); + script.pause(); + } else { + if (curr === "default.dbj") { + console.log("ÿc2Resuming."); } + script.resume(); } } + } - return true; - }, - - this.stopDefault = function () { - for (let curr of this.stopScripts) { - try { - let script = getScript(curr); - if (!!script && script.running) { - script.stop(); - while (script.running) { - delay(3); - } + return true; + }, + + stopDefault: function () { + for (let curr of this.stopScripts) { + try { + let script = getScript(curr); + if (!!script && script.running) { + script.stop(); + while (script.running) { + delay(3); } - } catch (e) { - console.error(e); } + } catch (e) { + console.error(e); } + } - return true; - }; - - this.exit = function (chickenExit = false) { - chickenExit && D2Bot.updateChickens(); - Config.LogExperience && Experience.log(); - // clearAllEvents(); - console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); - this.stopDefault(); - quit(); - }; - - /** - * @param {number} pottype - * @param {number} type - * @returns {ItemUnit | false} - */ - this.getPotion = function (pottype, type) { - if (!pottype) return false; - if (!me.gameReady) return false; - - let items = me.getItemsEx() - .filter(function (item) { - return item.itemType === pottype; - }); - if (items.length === 0) return false; - - // Get highest id = highest potion first - items.sort(function (a, b) { - return b.classid - a.classid; + return true; + }, + + exit: function (chickenExit = false) { + chickenExit && D2Bot.updateChickens(); + Config.LogExperience && Experience.log(); + // clearAllEvents(); + console.log("ÿc8Run duration ÿc2" + (Time.format(getTickCount() - me.gamestarttime))); + this.stopDefault(); + quit(); + }, + + /** + * @param {number} pottype + * @param {number} type + * @returns {ItemUnit | false} + */ + getPotion: function (pottype, type) { + if (!pottype) return false; + if (!me.gameReady) return false; + + let items = me.getItemsEx() + .filter(function (item) { + return item.itemType === pottype; }); + if (items.length === 0) return false; - for (let item of items) { - if (type < this.pots.MercHealth && item.isInInventory && item.itemType === pottype) { - console.log("ÿc2Drinking potion from inventory."); - return item; - } + // Get highest id = highest potion first + items.sort(function (a, b) { + return b.classid - a.classid; + }); - if (item.isInBelt && item.itemType === pottype) { - console.log("ÿc2" + (type > 2 ? "Giving Merc" : "Drinking") + " potion from belt."); - return item; - } + for (let item of items) { + if (type < this.pots.MercHealth && item.isInInventory && item.itemType === pottype) { + console.log("ÿc2Drinking potion from inventory."); + return item; } - return false; - }; - - /** - * @param {number} type - * @returns {boolean} - * @todo add stamina/thawing/antidote pot drinking here - */ - this.drinkPotion = function (type) { - if (type === undefined) return false; - if (!me.gameReady) return false; - let tNow = getTickCount(); + if (item.isInBelt && item.itemType === pottype) { + console.log("ÿc2" + (type > 2 ? "Giving Merc" : "Drinking") + " potion from belt."); + return item; + } + } - switch (type) { - case this.pots.Health: - case this.pots.Mana: - if ((this.timerLastDrink[type] - && (tNow - this.timerLastDrink[type] < 1000)) - || me.getState(type === this.pots.Health ? sdk.states.HealthPot : sdk.states.ManaPot)) { - return false; - } + return false; + }, - break; - case this.pots.Rejuv: - // small delay for juvs just to prevent using more at once - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 300)) { - return false; - } + /** + * @param {number} type + * @returns {boolean} + * @todo add stamina/thawing/antidote pot drinking here + */ + drinkPotion: function (type) { + if (type === undefined) return false; + if (!me.gameReady) return false; + let tNow = getTickCount(); + + switch (type) { + case this.pots.Health: + case this.pots.Mana: + if ((this.timerLastDrink[type] + && (tNow - this.timerLastDrink[type] < 1000)) + || me.getState(type === this.pots.Health ? sdk.states.HealthPot : sdk.states.ManaPot)) { + return false; + } - break; - case this.pots.MercRejuv: - // larger delay for juvs just to prevent using more at once, considering merc update rate - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 2000)) { - return false; - } + break; + case this.pots.Rejuv: + // small delay for juvs just to prevent using more at once + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 300)) { + return false; + } - break; - default: - if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 8000)) { - return false; - } + break; + case this.pots.MercRejuv: + // larger delay for juvs just to prevent using more at once, considering merc update rate + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 2000)) { + return false; + } - break; + break; + default: + if (this.timerLastDrink[type] && (tNow - this.timerLastDrink[type] < 8000)) { + return false; } - let pottype = (() => { - switch (type) { - case this.pots.Health: - case this.pots.MercHealth: - return sdk.items.type.HealingPotion; - case this.pots.Mana: - return sdk.items.type.ManaPotion; - default: - return sdk.items.type.RejuvPotion; - } - })(); + break; + } - let potion = this.getPotion(pottype, type); + let pottype = (() => { + switch (type) { + case this.pots.Health: + case this.pots.MercHealth: + return sdk.items.type.HealingPotion; + case this.pots.Mana: + return sdk.items.type.ManaPotion; + default: + return sdk.items.type.RejuvPotion; + } + })(); - if (potion) { - if (me.dead) return false; + let potion = this.getPotion(pottype, type); - if (me.mode === sdk.player.mode.SkillActionSequence) { - while (me.mode === sdk.player.mode.SkillActionSequence) { - delay (25); - } - } + if (potion) { + if (me.dead) return false; - try { - type < this.pots.MercHealth - ? potion.interact() - : Packet.useBeltItemForMerc(potion); - } catch (e) { - console.error(e); + if (me.mode === sdk.player.mode.SkillActionSequence) { + while (me.mode === sdk.player.mode.SkillActionSequence) { + delay (25); } - - this.timerLastDrink[type] = getTickCount(); - - return true; } - return false; - }; - - this.checkVipers = function () { - let monster = Game.getMonster(sdk.monsters.TombViper2); + try { + type < this.pots.MercHealth + ? potion.interact() + : Packet.useBeltItemForMerc(potion); + } catch (e) { + console.error(e); + } - if (monster) { - do { - if (monster.getState(sdk.states.Revive)) { - let owner = monster.getParent(); + this.timerLastDrink[type] = getTickCount(); - if (owner && owner.name !== me.name) { - D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); + return true; + } - return true; - } - } - } while (monster.getNext()); - } + return false; + }, - return false; - }; + checkVipers: function () { + let monster = Game.getMonster(sdk.monsters.TombViper2); - this.getIronGolem = function () { - let golem = Game.getMonster(sdk.summons.IronGolem); + if (monster) { + do { + if (monster.getState(sdk.states.Revive)) { + let owner = monster.getParent(); - if (golem) { - do { - let owner = golem.getParent(); + if (owner && owner.name !== me.name) { + D2Bot.printToConsole("Revived Tomb Vipers found. Leaving game.", sdk.colors.D2Bot.Red); - if (owner && owner.name === me.name) { - return copyUnit(golem); + return true; } - } while (golem.getNext()); - } + } + } while (monster.getNext()); + } + + return false; + }, - return false; - }; + getIronGolem: function () { + let golem = Game.getMonster(sdk.summons.IronGolem); - this.getNearestPreset = function () { - let id; - /** @type {Array} */ - let presets = getPresetUnits(me.area); - let dist = 99; + if (golem) { + do { + let owner = golem.getParent(); - for (let unit of presets) { - let coords = unit.realCoords(); - if (getDistance(me, coords.x, coords.y) < dist) { - dist = getDistance(me, coords.x, coords.y); - id = unit.type + " " + unit.id; + if (owner && owner.name === me.name) { + return copyUnit(golem); } - } + } while (golem.getNext()); + } - return id || ""; - }, - - /** - * @param {MeType | MercUnit} unit - * @returns {string} - */ - this.getStatsString = function (unit) { - let realFCR = unit.getStat(sdk.stats.FCR); - let realIAS = unit.getStat(sdk.stats.IAS); - let realFBR = unit.getStat(sdk.stats.FBR); - let realFHR = unit.getStat(sdk.stats.FHR); - // me.getStat(sdk.stats.FasterCastRate) will return real FCR from gear + Config.FCR from char cfg - - if (unit === me) { - realFCR -= Config.FCR; - realIAS -= Config.IAS; - realFBR -= Config.FBR; - realFHR -= Config.FHR; + return false; + }, + + getNearestPreset: function () { + let id; + /** @type {Array} */ + let presets = getPresetUnits(me.area); + let dist = 99; + + for (let unit of presets) { + let coords = unit.realCoords(); + if (getDistance(me, coords.x, coords.y) < dist) { + dist = getDistance(me, coords.x, coords.y); + id = unit.type + " " + unit.id; } + } - let maxHellFireRes = 75 + unit.getStat(sdk.stats.MaxFireResist); - let hellFireRes = unit.getRes(sdk.stats.FireResist, sdk.difficulty.Hell); - hellFireRes > maxHellFireRes && (hellFireRes = maxHellFireRes); - - let maxHellColdRes = 75 + unit.getStat(sdk.stats.MaxColdResist); - let hellColdRes = unit.getRes(sdk.stats.ColdResist, sdk.difficulty.Hell); - hellColdRes > maxHellColdRes && (hellColdRes = maxHellColdRes); - - let maxHellLightRes = 75 + unit.getStat(sdk.stats.MaxLightResist); - let hellLightRes = unit.getRes(sdk.stats.LightResist, sdk.difficulty.Hell); - hellLightRes > maxHellLightRes && (hellLightRes = maxHellLightRes); - - let maxHellPoisonRes = 75 + unit.getStat(sdk.stats.MaxPoisonResist); - let hellPoisonRes = unit.getRes(sdk.stats.PoisonResist, sdk.difficulty.Hell); - hellPoisonRes > maxHellPoisonRes && (hellPoisonRes = maxHellPoisonRes); - - let str = - "ÿc4Character Level: ÿc0" + unit.charlvl - + (unit === me ? " ÿc4Difficulty: ÿc0" + sdk.difficulty.nameOf(me.diff) - + " ÿc4HighestActAvailable: ÿc0" + me.highestAct : "") + "\n" - + "ÿc1FR: ÿc0" + unit.getStat(sdk.stats.FireResist) + "ÿc1 Applied FR: ÿc0" + unit.fireRes - + "/ÿc3 CR: ÿc0" + unit.getStat(sdk.stats.ColdResist) + "ÿc3 Applied CR: ÿc0" + unit.coldRes - + "/ÿc9 LR: ÿc0" + unit.getStat(sdk.stats.LightResist) + "ÿc9 Applied LR: ÿc0" + unit.lightRes - + "/ÿc2 PR: ÿc0" + unit.getStat(sdk.stats.PoisonResist) + "ÿc2 Applied PR: ÿc0" + unit.poisonRes + "\n" - + (!me.hell - ? "Hell res: ÿc1" + hellFireRes - + "ÿc0/ÿc3" + hellColdRes + "ÿc0/ÿc9" + hellLightRes + "ÿc0/ÿc2" + hellPoisonRes + "ÿc0\n" : "") - + "ÿc4MF: ÿc0" + unit.getStat(sdk.stats.MagicBonus) + "ÿc4 GF: ÿc0" + unit.getStat(sdk.stats.GoldBonus) - + " ÿc4FCR: ÿc0" + realFCR + " ÿc4IAS: ÿc0" + realIAS + " ÿc4FBR: ÿc0" + realFBR - + " ÿc4FHR: ÿc0" + realFHR + " ÿc4FRW: ÿc0" + unit.getStat(sdk.stats.FRW) + "\n" - + "ÿc4CB: ÿc0" + unit.getStat(sdk.stats.CrushingBlow) + " ÿc4DS: ÿc0" + unit.getStat(sdk.stats.DeadlyStrike) - + " ÿc4OW: ÿc0" + unit.getStat(sdk.stats.OpenWounds) - + " ÿc1LL: ÿc0" + unit.getStat(sdk.stats.LifeLeech) + " ÿc3ML: ÿc0" + unit.getStat(sdk.stats.ManaLeech) - + " ÿc8DR: ÿc0" + unit.getStat(sdk.stats.DamageResist) - + "% + " + unit.getStat(sdk.stats.NormalDamageReduction) - + " ÿc8MDR: ÿc0" + unit.getStat(sdk.stats.MagicResist) - + "% + " + unit.getStat(sdk.stats.MagicDamageReduction) + "\n" - + (unit.getStat(sdk.stats.CannotbeFrozen) > 0 ? "ÿc3Cannot be Frozenÿc1\n" : "\n"); - - return str; - }; + return id || ""; }, - configurable: true, - }); -})(Common); + + /** + * @param {MeType | MercUnit} unit + * @returns {string} + */ + getStatsString: function (unit) { + let realFCR = unit.getStat(sdk.stats.FCR); + let realIAS = unit.getStat(sdk.stats.IAS); + let realFBR = unit.getStat(sdk.stats.FBR); + let realFHR = unit.getStat(sdk.stats.FHR); + // me.getStat(sdk.stats.FasterCastRate) will return real FCR from gear + Config.FCR from char cfg + + if (unit === me) { + realFCR -= Config.FCR; + realIAS -= Config.IAS; + realFBR -= Config.FBR; + realFHR -= Config.FHR; + } + + let maxHellFireRes = 75 + unit.getStat(sdk.stats.MaxFireResist); + let hellFireRes = unit.getRes(sdk.stats.FireResist, sdk.difficulty.Hell); + hellFireRes > maxHellFireRes && (hellFireRes = maxHellFireRes); + + let maxHellColdRes = 75 + unit.getStat(sdk.stats.MaxColdResist); + let hellColdRes = unit.getRes(sdk.stats.ColdResist, sdk.difficulty.Hell); + hellColdRes > maxHellColdRes && (hellColdRes = maxHellColdRes); + + let maxHellLightRes = 75 + unit.getStat(sdk.stats.MaxLightResist); + let hellLightRes = unit.getRes(sdk.stats.LightResist, sdk.difficulty.Hell); + hellLightRes > maxHellLightRes && (hellLightRes = maxHellLightRes); + + let maxHellPoisonRes = 75 + unit.getStat(sdk.stats.MaxPoisonResist); + let hellPoisonRes = unit.getRes(sdk.stats.PoisonResist, sdk.difficulty.Hell); + hellPoisonRes > maxHellPoisonRes && (hellPoisonRes = maxHellPoisonRes); + + let str + = "ÿc4Character Level: ÿc0" + unit.charlvl + + (unit === me ? " ÿc4Difficulty: ÿc0" + sdk.difficulty.nameOf(me.diff) + + " ÿc4HighestActAvailable: ÿc0" + me.highestAct : "") + "\n" + + "ÿc1FR: ÿc0" + unit.getStat(sdk.stats.FireResist) + "ÿc1 Applied FR: ÿc0" + unit.fireRes + + "/ÿc3 CR: ÿc0" + unit.getStat(sdk.stats.ColdResist) + "ÿc3 Applied CR: ÿc0" + unit.coldRes + + "/ÿc9 LR: ÿc0" + unit.getStat(sdk.stats.LightResist) + "ÿc9 Applied LR: ÿc0" + unit.lightRes + + "/ÿc2 PR: ÿc0" + unit.getStat(sdk.stats.PoisonResist) + "ÿc2 Applied PR: ÿc0" + unit.poisonRes + "\n" + + (!me.hell + ? "Hell res: ÿc1" + hellFireRes + + "ÿc0/ÿc3" + hellColdRes + "ÿc0/ÿc9" + hellLightRes + "ÿc0/ÿc2" + hellPoisonRes + "ÿc0\n" : "") + + "ÿc4MF: ÿc0" + unit.getStat(sdk.stats.MagicBonus) + "ÿc4 GF: ÿc0" + unit.getStat(sdk.stats.GoldBonus) + + " ÿc4FCR: ÿc0" + realFCR + " ÿc4IAS: ÿc0" + realIAS + " ÿc4FBR: ÿc0" + realFBR + + " ÿc4FHR: ÿc0" + realFHR + " ÿc4FRW: ÿc0" + unit.getStat(sdk.stats.FRW) + "\n" + + "ÿc4CB: ÿc0" + unit.getStat(sdk.stats.CrushingBlow) + " ÿc4DS: ÿc0" + unit.getStat(sdk.stats.DeadlyStrike) + + " ÿc4OW: ÿc0" + unit.getStat(sdk.stats.OpenWounds) + + " ÿc1LL: ÿc0" + unit.getStat(sdk.stats.LifeLeech) + " ÿc3ML: ÿc0" + unit.getStat(sdk.stats.ManaLeech) + + " ÿc8DR: ÿc0" + unit.getStat(sdk.stats.DamageResist) + + "% + " + unit.getStat(sdk.stats.NormalDamageReduction) + + " ÿc8MDR: ÿc0" + unit.getStat(sdk.stats.MagicResist) + + "% + " + unit.getStat(sdk.stats.MagicDamageReduction) + "\n" + + (unit.getStat(sdk.stats.CannotbeFrozen) > 0 ? "ÿc3Cannot be Frozenÿc1\n" : "\n"); + + return str; + } + }; + + module.exports = Toolsthread; +})(module); diff --git a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js index e37703df0..9c242d3e8 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js @@ -11,7 +11,6 @@ include("critical.js"); // required // globals needed for core gameplay includeCoreLibs(); -include("core/Common/Tools.js"); // system libs includeSystemLibs(); diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index 91351edce..d6b4aa16a 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -7,7 +7,6 @@ const Baal = new Runnable( function Baal () { - include("core/Common/Baal.js"); const announce = function () { let count, string, souls, dolls; let monster = Game.getMonster(); diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index 1891e3e42..2685b3579 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -14,7 +14,6 @@ const BaalAssistant = new Runnable( function BaalAssistant () { - include("core/Common/Baal.js"); let Leader = Config.Leader; let Helper = Config.BaalAssistant.Helper; let firstAttempt = true; diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 89e908b99..7d2249d4f 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -8,8 +8,6 @@ const BaalHelper = new Runnable( function BaalHelper () { - include("core/Common/Baal.js"); - Town.goToTown(5); Config.RandomPrecast && Precast.needOutOfTownCast() ? Precast.doRandomPrecast(true, sdk.areas.Harrogath) diff --git a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js index 2f4bdd839..655fa4f6f 100644 --- a/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js +++ b/d2bs/kolbot/libs/scripts/ClassicChaosAssistant.js @@ -9,7 +9,6 @@ const ClassicChaosAssistant = new Runnable( function ClassicChaosAssistant () { - include("core/Common/Diablo.js"); let stargo, infgo, seisgo, vizgo, infseal, seisseal, vizseal, diablopickup, normalpickup = false; addEventListener("keyup", diff --git a/d2bs/kolbot/libs/scripts/Cows.js b/d2bs/kolbot/libs/scripts/Cows.js index e67af2e67..f432265eb 100644 --- a/d2bs/kolbot/libs/scripts/Cows.js +++ b/d2bs/kolbot/libs/scripts/Cows.js @@ -7,8 +7,6 @@ const Cows = new Runnable( function Cows () { - include("core/Common/Cows.js"); - const getLeg = function () { if (me.wirtsleg) return me.wirtsleg; diff --git a/d2bs/kolbot/libs/scripts/Diablo.js b/d2bs/kolbot/libs/scripts/Diablo.js index 2f9ad97a8..21e3280ab 100644 --- a/d2bs/kolbot/libs/scripts/Diablo.js +++ b/d2bs/kolbot/libs/scripts/Diablo.js @@ -11,7 +11,6 @@ const Diablo = new Runnable( function Diablo () { - include("core/Common/Diablo.js"); Pather._teleport = Pather.teleport; Common.Diablo.clearRadius = Config.Diablo.ClearRadius; diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index f42c09981..00915b1c6 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -7,7 +7,6 @@ const DiabloHelper = new Runnable( function DiabloHelper () { - include("core/Common/Diablo.js"); this.Leader = Config.Leader; Common.Diablo.waitForGlow = true; Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; diff --git a/d2bs/kolbot/libs/scripts/MFHelper.js b/d2bs/kolbot/libs/scripts/MFHelper.js index b88f89a7f..7ccc20dd7 100644 --- a/d2bs/kolbot/libs/scripts/MFHelper.js +++ b/d2bs/kolbot/libs/scripts/MFHelper.js @@ -177,7 +177,6 @@ const MFHelper = new Runnable( Town.goToTown(1) && Pather.usePortal(sdk.areas.MooMooFarm); return me.inArea(sdk.areas.MooMooFarm); }, Time.minutes(1), 500 + me.ping)) { - include("core/Common/Cows.js"); Precast.doPrecast(false); Common.Cows.clearCowLevel(); delay(1000); diff --git a/d2bs/kolbot/libs/scripts/Questing.js b/d2bs/kolbot/libs/scripts/Questing.js index 553ae9a6d..81318e939 100644 --- a/d2bs/kolbot/libs/scripts/Questing.js +++ b/d2bs/kolbot/libs/scripts/Questing.js @@ -43,12 +43,10 @@ const Questing = new Runnable( const smith = function () { log("starting smith"); - include("core/Common/Smith.js"); Common.Smith(); }; const cain = function () { - include("core/Common/Cain.js"); log("starting cain"); Town.doChores(); @@ -297,7 +295,6 @@ const Questing = new Runnable( // @theBGuy const ancients = function () { - include("core/Common/Ancients.js"); log("starting ancients"); Town.doChores(); diff --git a/d2bs/kolbot/libs/scripts/TravincalLeech.js b/d2bs/kolbot/libs/scripts/TravincalLeech.js index e0c5b706c..df24603f8 100644 --- a/d2bs/kolbot/libs/scripts/TravincalLeech.js +++ b/d2bs/kolbot/libs/scripts/TravincalLeech.js @@ -15,7 +15,6 @@ const TravincalLeech = new Runnable( function TravincalLeech () { - include("core/Common/Leecher.js"); let leader; let done = false; diff --git a/d2bs/kolbot/libs/scripts/Tristram.js b/d2bs/kolbot/libs/scripts/Tristram.js index faae22f7f..b6ae8b845 100644 --- a/d2bs/kolbot/libs/scripts/Tristram.js +++ b/d2bs/kolbot/libs/scripts/Tristram.js @@ -12,7 +12,6 @@ const Tristram = new Runnable( // complete quest if its not complete if (!me.getQuest(sdk.quest.id.TheSearchForCain, 4) && !me.getQuest(sdk.quest.id.TheSearchForCain, sdk.quest.states.Completed)) { - include("core/Common/Cain.js"); Common.Cain.run(); } diff --git a/d2bs/kolbot/libs/scripts/TristramLeech.js b/d2bs/kolbot/libs/scripts/TristramLeech.js index c46c6fbbd..5a24ac258 100644 --- a/d2bs/kolbot/libs/scripts/TristramLeech.js +++ b/d2bs/kolbot/libs/scripts/TristramLeech.js @@ -7,7 +7,6 @@ const TristramLeech = new Runnable( function TristramLeech () { - include("core/Common/Leecher.js"); let done = false; let whereisleader, leader; diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 95c138e6c..83958e47a 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -7,7 +7,6 @@ const Wakka = new Runnable( function Wakka () { - include("core/Common/Diablo.js"); const timeout = Config.Wakka.Wait; const [minDist, maxDist] = [50, 80]; const internals = { diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index cde91482e..b681f2c44 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -1087,7 +1087,6 @@ }; /** @param {string} [nick] */ const diablo = function (nick) { - include("core/Common/Diablo.js"); log("starting diablo"); function inviteIn () { @@ -1273,7 +1272,6 @@ return false; } - include("core/Common/Ancients.js"); log("starting ancients"); Town.doChores(); @@ -1342,7 +1340,6 @@ return false; } - include("core/Common/Baal.js"); log("starting baal"); if (me.inTown) { diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index b719ba732..3ca93bb2f 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -17,6 +17,7 @@ /// /// /// +/// declare global { type IncludePath = import('./types/include-paths').IncludePath; diff --git a/d2bs/kolbot/sdk/types/Common.d.ts b/d2bs/kolbot/sdk/types/Common.d.ts new file mode 100644 index 000000000..4a00f5a70 --- /dev/null +++ b/d2bs/kolbot/sdk/types/Common.d.ts @@ -0,0 +1,16 @@ +export {}; +declare global { + // namespace Common { + // namespace Diablo {} + // } + const Common: { + Ancients: typeof import("../../libs/core/Common/Ancients"); + Baal: typeof import("../../libs/core/Common/Baal"); + Cain: typeof import("../../libs/core/Common/Cain"); + Cows: typeof import("../../libs/core/Common/Cows"); + Diablo: typeof import("../../libs/core/Common/Diablo"); + Leecher: typeof import("../../libs/core/Common/Leecher"); + Smith: typeof import("../../libs/core/Common/Smith"); + Toolsthread: typeof import("../../libs/core/Common/Tools"); + }; +} diff --git a/d2bs/kolbot/sdk/types/include-paths.d.ts b/d2bs/kolbot/sdk/types/include-paths.d.ts index 95e6677de..22dacab02 100644 --- a/d2bs/kolbot/sdk/types/include-paths.d.ts +++ b/d2bs/kolbot/sdk/types/include-paths.d.ts @@ -1,2 +1,2 @@ // Auto-generated include types for kolbot libs -export type IncludePath = "OOG.js" | "json2.js" | "core/Me.js" | "globals.js" | "require.js" | "core/NPC.js" | "critical.js" | "Polyfill.js" | "core/Item.js" | "core/Misc.js" | "core/Town.js" | "core/Util.js" | "oog/D2Bot.js" | "core/Skill.js" | "core/Attack.js" | "core/Common.js" | "core/Config.js" | "core/Cubing.js" | "core/Loader.js" | "core/Packet.js" | "core/Pather.js" | "core/Pickit.js" | "modules/sdk.js" | "scripts/Pit.js" | "config/Druid.js" | "core/CollMap.js" | "core/Precast.js" | "core/Storage.js" | "modules/HTTP.js" | "modules/Team.js" | "oog/DataFile.js" | "oog/ShitList.js" | "scripts/Baal.js" | "scripts/Cows.js" | "scripts/Idle.js" | "scripts/Test.js" | "config/Amazon.js" | "modules/Graph.js" | "oog/Locations.js" | "scripts/Izual.js" | "scripts/Smith.js" | "scripts/Synch.js" | "scripts/Tombs.js" | "scripts/Wakka.js" | "config/Paladin.js" | "core/Runewords.js" | "modules/Deltas.js" | "modules/Events.js" | "modules/Socket.js" | "modules/Worker.js" | "oog/FileAction.js" | "scripts/Diablo.js" | "scripts/Duriel.js" | "scripts/Endugu.js" | "scripts/Gamble.js" | "scripts/Rushee.js" | "scripts/Rusher.js" | "scripts/Synch2.js" | "config/Assassin.js" | "core/Experience.js" | "core/Prototypes.js" | "manualplay/main.js" | "modules/Control.js" | "modules/Promise.js" | "scripts/Abaddon.js" | "scripts/BoneAsh.js" | "scripts/Bonesaw.js" | "scripts/Eyeback.js" | "scripts/GetCube.js" | "scripts/GetFade.js" | "scripts/GetKeys.js" | "scripts/Icehawk.js" | "scripts/ShopBot.js" | "config/Barbarian.js" | "config/Sorceress.js" | "core/Common/Baal.js" | "core/Common/Cain.js" | "core/Common/Cows.js" | "modules/CopyData.js" | "modules/Override.js" | "modules/UnitInfo.js" | "scripts/Andariel.js" | "scripts/AutoBaal.js" | "scripts/Coldcrow.js" | "scripts/Coldworm.js" | "scripts/Countess.js" | "scripts/Crafting.js" | "scripts/Eldritch.js" | "scripts/Fangskin.js" | "scripts/Follower.js" | "scripts/Hephasto.js" | "scripts/IPHunter.js" | "scripts/Mephisto.js" | "scripts/MFHelper.js" | "scripts/OrgTorch.js" | "scripts/Questing.js" | "scripts/Radament.js" | "scripts/Snapchip.js" | "scripts/Summoner.js" | "scripts/Treehead.js" | "scripts/Tristram.js" | "scripts/WPGetter.js" | "SoloPlay/Core/Me.js" | "core/Common/Smith.js" | "core/Common/Tools.js" | "core/NTItemParser.js" | "modules/LocalChat.js" | "modules/Messaging.js" | "scripts/Bishibosh.js" | "scripts/CrushTele.js" | "scripts/GemHunter.js" | "scripts/Mausoleum.js" | "scripts/Nihlathak.js" | "scripts/Rakanishu.js" | "scripts/Stormtree.js" | "scripts/Travincal.js" | "scripts/UserAddon.js" | "SoloPlay/SoloPlay.js" | "config/Necromancer.js" | "core/Attacks/Druid.js" | "core/Auto/AutoStat.js" | "core/Common/Diablo.js" | "manualplay/MapMode.js" | "scripts/BaalHelper.js" | "scripts/ChestMania.js" | "scripts/ControlBot.js" | "scripts/Corpsefire.js" | "scripts/KillDclone.js" | "scripts/Pindleskin.js" | "scripts/SharpTooth.js" | "scripts/Worldstone.js" | "core/Attacks/Amazon.js" | "core/Auto/AutoBuild.js" | "core/Auto/AutoSkill.js" | "core/Common/Leecher.js" | "scripts/Frozenstein.js" | "scripts/GetEssences.js" | "scripts/SealLeecher.js" | "SoloPlay/Core/Quest.js" | "config/_CustomConfig.js" | "core/Attacks/Paladin.js" | "core/Common/Ancients.js" | "scripts/BattleOrders.js" | "scripts/BoBarbHelper.js" | "scripts/ClearAnyArea.js" | "scripts/DiabloHelper.js" | "scripts/GhostBusters.js" | "scripts/OuterSteppes.js" | "scripts/ThreshSocket.js" | "SoloPlay/Scripts/den.js" | "SoloPlay/Scripts/eye.js" | "core/Attacks/Assassin.js" | "core/Attacks/Wereform.js" | "manualplay/libs/Hooks.js" | "modules/workers/Guard.js" | "scripts/BaalAssistant.js" | "scripts/DeveloperMode.js" | "scripts/KurastTemples.js" | "scripts/TristramLeech.js" | "SoloPlay/Config/Druid.js" | "SoloPlay/Core/Globals.js" | "SoloPlay/Modules/Mock.js" | "SoloPlay/Scripts/anya.js" | "SoloPlay/Scripts/baal.js" | "SoloPlay/Scripts/cave.js" | "SoloPlay/Scripts/cows.js" | "SoloPlay/Scripts/cube.js" | "SoloPlay/Scripts/jail.js" | "SoloPlay/Scripts/nith.js" | "SoloPlay/Scripts/pits.js" | "starter/StarterConfig.js" | "systems/automule/main.js" | "systems/automule/Mule.js" | "config/_BaseConfigFile.js" | "core/Attacks/Barbarian.js" | "core/Attacks/Sorceress.js" | "core/GameData/AreaData.js" | "core/GameData/GameData.js" | "core/GameData/RuneData.js" | "scripts/AncientTunnels.js" | "scripts/OrgTorchHelper.js" | "scripts/TravincalLeech.js" | "SoloPlay/Config/Amazon.js" | "SoloPlay/Modules/Clear.js" | "SoloPlay/Modules/Guard.js" | "SoloPlay/OOG/SoloEntry.js" | "SoloPlay/Scripts/brain.js" | "SoloPlay/Scripts/heart.js" | "SoloPlay/Scripts/izual.js" | "SoloPlay/Scripts/river.js" | "SoloPlay/Scripts/shenk.js" | "SoloPlay/Scripts/smith.js" | "SoloPlay/Scripts/staff.js" | "SoloPlay/Scripts/tombs.js" | "SoloPlay/Tools/Overlay.js" | "SoloPlay/Tools/Tracker.js" | "starter/AdvancedConfig.js" | "core/GameData/QuestData.js" | "core/GameData/SkillData.js" | "manualplay/config/Druid.js" | "scripts/CreepingFeature.js" | "SoloPlay/Config/Paladin.js" | "SoloPlay/Core/AutoBuild.js" | "SoloPlay/Core/Mercenary.js" | "SoloPlay/Core/NPCAction.js" | "SoloPlay/Core/Polyfills.js" | "SoloPlay/Core/SoloWants.js" | "SoloPlay/Modules/bigInt.js" | "SoloPlay/Modules/Coords.js" | "SoloPlay/Modules/Events.js" | "SoloPlay/Modules/MoveTo.js" | "SoloPlay/Modules/Vector.js" | "SoloPlay/Scripts/amulet.js" | "SoloPlay/Scripts/diablo.js" | "SoloPlay/Scripts/duriel.js" | "SoloPlay/Scripts/pindle.js" | "SoloPlay/Threads/Reload.js" | "SoloPlay/Tools/CharData.js" | "core/Attacks/Necromancer.js" | "core/GameData/ShrineData.js" | "manualplay/config/Amazon.js" | "scripts/BattlemaidSarina.js" | "SoloPlay/Config/Assassin.js" | "SoloPlay/Core/CharmEquip.js" | "SoloPlay/Core/SoloEvents.js" | "SoloPlay/Modules/General.js" | "SoloPlay/Modules/MercLib.js" | "SoloPlay/Modules/NameGen.js" | "SoloPlay/Scripts/boneash.js" | "SoloPlay/Scripts/eyeback.js" | "SoloPlay/Scripts/fireeye.js" | "SoloPlay/Scripts/getkeys.js" | "SoloPlay/Tools/Developer.js" | "SoloPlay/Tools/SoloIndex.js" | "config/Builds/Class.Build.js" | "core/GameData/MonsterData.js" | "core/GameData/NTItemAlias.js" | "manualplay/config/Paladin.js" | "modules/workers/Advertise.js" | "modules/workers/WpWatcher.js" | "SoloPlay/Config/Barbarian.js" | "SoloPlay/Config/Sorceress.js" | "SoloPlay/Modules/MockItem.js" | "SoloPlay/OOG/OOGOverrides.js" | "SoloPlay/Scripts/a1chests.js" | "SoloPlay/Scripts/a5chests.js" | "SoloPlay/Scripts/ancients.js" | "SoloPlay/Scripts/andariel.js" | "SoloPlay/Scripts/countess.js" | "SoloPlay/Scripts/hephasto.js" | "SoloPlay/Scripts/lamessen.js" | "SoloPlay/Scripts/mephisto.js" | "SoloPlay/Scripts/orgtorch.js" | "SoloPlay/Scripts/radament.js" | "SoloPlay/Scripts/summoner.js" | "SoloPlay/Scripts/treehead.js" | "SoloPlay/Scripts/tristram.js" | "systems/automule/AutoMule.js" | "systems/autorush/AutoRush.js" | "systems/gambling/Gambling.js" | "systems/torch/TorchSystem.js" | "manualplay/config/Assassin.js" | "manualplay/hooks/ItemHooks.js" | "manualplay/hooks/TextHooks.js" | "scripts/UndergroundPassage.js" | "SoloPlay/Core/DynamicTiers.js" | "SoloPlay/Modules/utilities.js" | "SoloPlay/Scripts/bishibosh.js" | "SoloPlay/Scripts/hellforge.js" | "SoloPlay/Scripts/mausoleum.js" | "SoloPlay/Scripts/savebarby.js" | "SoloPlay/Scripts/travincal.js" | "systems/torch/FarmerConfig.js" | "systems/torch/OrgTorchData.js" | "manualplay/config/Barbarian.js" | "manualplay/config/Sorceress.js" | "manualplay/modules/HelpMenu.js" | "modules/workers/SimpleParty.js" | "modules/workers/TownChicken.js" | "SoloPlay/Config/Necromancer.js" | "SoloPlay/Core/ItemOverrides.js" | "SoloPlay/Core/ItemUtilities.js" | "SoloPlay/Core/MiscOverrides.js" | "SoloPlay/Core/NTIPOverrides.js" | "SoloPlay/Core/TownOverrides.js" | "SoloPlay/Scripts/bloodraven.js" | "SoloPlay/Scripts/corpsefire.js" | "SoloPlay/Scripts/maggotlair.js" | "SoloPlay/Scripts/templeruns.js" | "SoloPlay/Scripts/worldstone.js" | "systems/autorush/RushConfig.js" | "systems/follow/FollowConfig.js" | "core/GameData/LocaleStringID.js" | "manualplay/hooks/ActionHooks.js" | "manualplay/hooks/ShrineHooks.js" | "manualplay/hooks/VectorHooks.js" | "manualplay/threads/MapHelper.js" | "SoloPlay/Core/ItemPrototypes.js" | "SoloPlay/Core/SkillOverrides.js" | "SoloPlay/Scripts/beetleburst.js" | "SoloPlay/Scripts/lowerkurast.js" | "SoloPlay/Threads/ToolsThread.js" | "SoloPlay/Workers/TownChicken.js" | "systems/crafting/TeamsConfig.js" | "systems/gambling/TeamsConfig.js" | "manualplay/config/Necromancer.js" | "manualplay/hooks/MonsterHooks.js" | "manualplay/libs/MiscOverrides.js" | "manualplay/libs/TownOverrides.js" | "manualplay/threads/PickThread.js" | "scripts/ClassicChaosAssistant.js" | "SoloPlay/Core/AttackOverrides.js" | "SoloPlay/Core/ConfigOverrides.js" | "SoloPlay/Core/CubingOverrides.js" | "SoloPlay/Core/LoaderOverrides.js" | "SoloPlay/Core/PatherOverrides.js" | "SoloPlay/Core/PickitOverrides.js" | "SoloPlay/Workers/EventEmitter.js" | "SoloPlay/Workers/EventHandler.js" | "systems/channel/ChannelConfig.js" | "systems/cleaner/CleanerConfig.js" | "systems/gameaction/GameAction.js" | "systems/mulelogger/MuleLogger.js" | "systems/pubjoin/PubJoinConfig.js" | "manualplay/modules/HookFactory.js" | "SoloPlay/Core/PrecastOverrides.js" | "SoloPlay/Core/StorageOverrides.js" | "SoloPlay/Scripts/developermode.js" | "manualplay/libs/AttackOverrides.js" | "manualplay/libs/ConfigOverrides.js" | "manualplay/libs/PatherOverrides.js" | "manualplay/libs/PickitOverrides.js" | "SoloPlay/BuildFiles/druid/druid.js" | "SoloPlay/Core/AutoMuleOverrides.js" | "SoloPlay/Core/AutoStatOverrides.js" | "SoloPlay/Scripts/ancienttunnels.js" | "systems/crafting/CraftingSystem.js" | "systems/mulelogger/LoggerConfig.js" | "SoloPlay/Core/PrototypeOverrides.js" | "SoloPlay/Core/RunewordsOverrides.js" | "SoloPlay/Scripts/creepingfeature.js" | "manualplay/threads/MapToolsThread.js" | "SoloPlay/BuildFiles/amazon/amazon.js" | "SoloPlay/BuildFiles/Runewords/Ice.js" | "SoloPlay/Core/MuleloggerOverrides.js" | "SoloPlay/Modules/GameData/PotData.js" | "SoloPlay/BuildFiles/Runewords/Bone.js" | "SoloPlay/BuildFiles/Runewords/Fury.js" | "SoloPlay/BuildFiles/Runewords/Lore.js" | "SoloPlay/BuildFiles/Runewords/Myth.js" | "SoloPlay/Modules/GameData/AreaData.js" | "SoloPlay/Modules/GameData/GameData.js" | "systems/automule/config/MuleConfig.js" | "SoloPlay/BuildFiles/paladin/paladin.js" | "SoloPlay/BuildFiles/Runewords/Chaos.js" | "SoloPlay/BuildFiles/Runewords/Exile.js" | "SoloPlay/BuildFiles/Runewords/Faith.js" | "SoloPlay/BuildFiles/Runewords/Grief.js" | "SoloPlay/BuildFiles/Runewords/Honor.js" | "SoloPlay/BuildFiles/Runewords/Rhyme.js" | "SoloPlay/BuildFiles/Runewords/Smoke.js" | "SoloPlay/BuildFiles/Runewords/Steel.js" | "SoloPlay/BuildFiles/Runewords/White.js" | "systems/gameaction/GameActionConfig.js" | "config/Builds/Sorceress.ExampleBuild.js" | "SoloPlay/BuildFiles/Runewords/Duress.js" | "SoloPlay/BuildFiles/Runewords/Enigma.js" | "SoloPlay/BuildFiles/Runewords/Malice.js" | "SoloPlay/BuildFiles/assassin/assassin.js" | "SoloPlay/BuildFiles/Runewords/Silence.js" | "SoloPlay/BuildFiles/Runewords/Stealth.js" | "SoloPlay/Modules/GameData/MissileData.js" | "SoloPlay/Modules/GameData/MonsterData.js" | "SoloPlay/BuildFiles/Runewords/LastWish.js" | "SoloPlay/BuildFiles/Runewords/MercDoom.js" | "systems/automule/config/TorchAnniMules.js" | "SoloPlay/BuildFiles/barbarian/barbarian.js" | "SoloPlay/BuildFiles/Runewords/DreamHelm.js" | "SoloPlay/BuildFiles/Runewords/Fortitude.js" | "SoloPlay/BuildFiles/Runewords/MercPride.js" | "SoloPlay/BuildFiles/Runewords/Sanctuary.js" | "SoloPlay/BuildFiles/Runewords/Treachery.js" | "SoloPlay/BuildFiles/sorceress/sorceress.js" | "SoloPlay/BuildFiles/Runewords/CallToArms.js" | "SoloPlay/BuildFiles/Runewords/KingsGrace.js" | "SoloPlay/BuildFiles/Runewords/Lawbringer.js" | "SoloPlay/Modules/GameData/LocaleStringID.js" | "SoloPlay/BuildFiles/druid/druid.WindBuild.js" | "SoloPlay/BuildFiles/druid/druid.WolfBuild.js" | "SoloPlay/BuildFiles/Runewords/DragonArmor.js" | "SoloPlay/BuildFiles/Runewords/DreamShield.js" | "SoloPlay/BuildFiles/Runewords/MercInsight.js" | "SoloPlay/BuildFiles/Runewords/SpiritSword.js" | "SoloPlay/BuildFiles/druid/druid.StartBuild.js" | "SoloPlay/BuildFiles/Runewords/CrescentMoon.js" | "SoloPlay/BuildFiles/Runewords/MercInfinity.js" | "SoloPlay/BuildFiles/Runewords/SpiritShield.js" | "SoloPlay/BuildFiles/necromancer/necromancer.js" | "SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js" | "SoloPlay/BuildFiles/Runewords/HandOfJustice.js" | "SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js" | "SoloPlay/BuildFiles/Runewords/MercFortitude.js" | "SoloPlay/BuildFiles/Runewords/MercTreachery.js" | "SoloPlay/BuildFiles/Runewords/PhoenixShield.js" | "SoloPlay/BuildFiles/Runewords/VoiceOfReason.js" | "SoloPlay/BuildFiles/amazon/amazon.StartBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js" | "SoloPlay/BuildFiles/Runewords/AncientsPledge.js" | "SoloPlay/BuildFiles/Runewords/PDiamondShield.js" | "SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js" | "SoloPlay/BuildFiles/druid/druid.ElementalBuild.js" | "SoloPlay/BuildFiles/druid/druid.StormbearBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.StartBuild.js" | "SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js" | "SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js" | "SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js" | "SoloPlay/Core/ClassAttackOverrides/DruidAttacks.js" | "SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.StartBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks.js" | "SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js" | "SoloPlay/Core/ClassAttackOverrides/PaladinAttacks.js" | "SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AssassinAttacks.js" | "SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js" | "SoloPlay/Core/ClassAttackOverrides/BarbarianAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/SorceressAttacks.js" | "SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks-WIP.js" | "SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js" | "SoloPlay/Core/ClassAttackOverrides/NecromancerAttacks.js" | "SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js"; +export type IncludePath = "OOG.js" | "json2.js" | "core/Me.js" | "globals.js" | "require.js" | "core/NPC.js" | "critical.js" | "Polyfill.js" | "core/Item.js" | "core/Misc.js" | "core/Town.js" | "core/Util.js" | "oog/D2Bot.js" | "core/Skill.js" | "core/Attack.js" | "core/Common.js" | "core/Config.js" | "core/Cubing.js" | "core/Loader.js" | "core/Packet.js" | "core/Pather.js" | "core/Pickit.js" | "scripts/Pit.js" | "config/Druid.js" | "core/CollMap.js" | "core/Precast.js" | "core/Storage.js" | "oog/DataFile.js" | "oog/ShitList.js" | "scripts/Baal.js" | "scripts/Cows.js" | "scripts/Idle.js" | "scripts/Test.js" | "config/Amazon.js" | "oog/Locations.js" | "scripts/Izual.js" | "scripts/Smith.js" | "scripts/Synch.js" | "scripts/Tombs.js" | "scripts/Wakka.js" | "config/Paladin.js" | "core/Runewords.js" | "oog/FileAction.js" | "scripts/Diablo.js" | "scripts/Duriel.js" | "scripts/Endugu.js" | "scripts/Gamble.js" | "scripts/Rushee.js" | "scripts/Rusher.js" | "scripts/Synch2.js" | "config/Assassin.js" | "core/Experience.js" | "core/Prototypes.js" | "manualplay/main.js" | "scripts/Abaddon.js" | "scripts/BoneAsh.js" | "scripts/Bonesaw.js" | "scripts/Eyeback.js" | "scripts/GetCube.js" | "scripts/GetFade.js" | "scripts/GetKeys.js" | "scripts/Icehawk.js" | "scripts/ShopBot.js" | "config/Barbarian.js" | "config/Sorceress.js" | "scripts/Andariel.js" | "scripts/AutoBaal.js" | "scripts/Coldcrow.js" | "scripts/Coldworm.js" | "scripts/Countess.js" | "scripts/Crafting.js" | "scripts/Eldritch.js" | "scripts/Fangskin.js" | "scripts/Follower.js" | "scripts/Hephasto.js" | "scripts/IPHunter.js" | "scripts/Mephisto.js" | "scripts/MFHelper.js" | "scripts/OrgTorch.js" | "scripts/Questing.js" | "scripts/Radament.js" | "scripts/Snapchip.js" | "scripts/Summoner.js" | "scripts/Treehead.js" | "scripts/Tristram.js" | "scripts/WPGetter.js" | "SoloPlay/Core/Me.js" | "core/NTItemParser.js" | "scripts/Bishibosh.js" | "scripts/CrushTele.js" | "scripts/GemHunter.js" | "scripts/Mausoleum.js" | "scripts/Nihlathak.js" | "scripts/Rakanishu.js" | "scripts/Stormtree.js" | "scripts/Travincal.js" | "scripts/UserAddon.js" | "SoloPlay/SoloPlay.js" | "config/Necromancer.js" | "core/Attacks/Druid.js" | "core/Auto/AutoStat.js" | "manualplay/MapMode.js" | "scripts/BaalHelper.js" | "scripts/ChestMania.js" | "scripts/ControlBot.js" | "scripts/Corpsefire.js" | "scripts/KillDclone.js" | "scripts/Pindleskin.js" | "scripts/SharpTooth.js" | "scripts/Worldstone.js" | "core/Attacks/Amazon.js" | "core/Auto/AutoBuild.js" | "core/Auto/AutoSkill.js" | "scripts/Frozenstein.js" | "scripts/GetEssences.js" | "scripts/SealLeecher.js" | "SoloPlay/Core/Quest.js" | "config/_CustomConfig.js" | "core/Attacks/Paladin.js" | "scripts/BattleOrders.js" | "scripts/BoBarbHelper.js" | "scripts/ClearAnyArea.js" | "scripts/DiabloHelper.js" | "scripts/GhostBusters.js" | "scripts/OuterSteppes.js" | "scripts/ThreshSocket.js" | "SoloPlay/Scripts/den.js" | "SoloPlay/Scripts/eye.js" | "core/Attacks/Assassin.js" | "core/Attacks/Wereform.js" | "manualplay/libs/Hooks.js" | "scripts/_Abaddon copy.js" | "scripts/BaalAssistant.js" | "scripts/DeveloperMode.js" | "scripts/KurastTemples.js" | "scripts/TristramLeech.js" | "SoloPlay/Config/Druid.js" | "SoloPlay/Core/Globals.js" | "SoloPlay/Scripts/anya.js" | "SoloPlay/Scripts/baal.js" | "SoloPlay/Scripts/cave.js" | "SoloPlay/Scripts/cows.js" | "SoloPlay/Scripts/cube.js" | "SoloPlay/Scripts/jail.js" | "SoloPlay/Scripts/nith.js" | "SoloPlay/Scripts/pits.js" | "starter/StarterConfig.js" | "systems/automule/main.js" | "systems/automule/Mule.js" | "config/_BaseConfigFile.js" | "core/Attacks/Barbarian.js" | "core/Attacks/Sorceress.js" | "core/GameData/AreaData.js" | "core/GameData/GameData.js" | "core/GameData/RuneData.js" | "scripts/AncientTunnels.js" | "scripts/OrgTorchHelper.js" | "scripts/TravincalLeech.js" | "SoloPlay/Config/Amazon.js" | "SoloPlay/OOG/SoloEntry.js" | "SoloPlay/Scripts/brain.js" | "SoloPlay/Scripts/heart.js" | "SoloPlay/Scripts/izual.js" | "SoloPlay/Scripts/river.js" | "SoloPlay/Scripts/shenk.js" | "SoloPlay/Scripts/smith.js" | "SoloPlay/Scripts/staff.js" | "SoloPlay/Scripts/tombs.js" | "SoloPlay/Tools/Overlay.js" | "SoloPlay/Tools/Tracker.js" | "starter/AdvancedConfig.js" | "core/GameData/QuestData.js" | "core/GameData/SkillData.js" | "manualplay/config/Druid.js" | "scripts/CreepingFeature.js" | "SoloPlay/Config/Paladin.js" | "SoloPlay/Core/AutoBuild.js" | "SoloPlay/Core/Mercenary.js" | "SoloPlay/Core/NPCAction.js" | "SoloPlay/Core/Polyfills.js" | "SoloPlay/Core/SoloWants.js" | "SoloPlay/Scripts/amulet.js" | "SoloPlay/Scripts/diablo.js" | "SoloPlay/Scripts/duriel.js" | "SoloPlay/Scripts/pindle.js" | "SoloPlay/Tools/CharData.js" | "core/Attacks/Necromancer.js" | "core/GameData/ShrineData.js" | "manualplay/config/Amazon.js" | "scripts/BattlemaidSarina.js" | "SoloPlay/Config/Assassin.js" | "SoloPlay/Core/CharmEquip.js" | "SoloPlay/Core/SoloEvents.js" | "SoloPlay/Scripts/boneash.js" | "SoloPlay/Scripts/eyeback.js" | "SoloPlay/Scripts/fireeye.js" | "SoloPlay/Scripts/getkeys.js" | "SoloPlay/Tools/Developer.js" | "SoloPlay/Tools/SoloIndex.js" | "config/Builds/Class.Build.js" | "core/GameData/MonsterData.js" | "core/GameData/NTItemAlias.js" | "manualplay/config/Paladin.js" | "SoloPlay/Config/Barbarian.js" | "SoloPlay/Config/Sorceress.js" | "SoloPlay/OOG/OOGOverrides.js" | "SoloPlay/Scripts/a1chests.js" | "SoloPlay/Scripts/a5chests.js" | "SoloPlay/Scripts/ancients.js" | "SoloPlay/Scripts/andariel.js" | "SoloPlay/Scripts/countess.js" | "SoloPlay/Scripts/hephasto.js" | "SoloPlay/Scripts/lamessen.js" | "SoloPlay/Scripts/mephisto.js" | "SoloPlay/Scripts/orgtorch.js" | "SoloPlay/Scripts/radament.js" | "SoloPlay/Scripts/summoner.js" | "SoloPlay/Scripts/treehead.js" | "SoloPlay/Scripts/tristram.js" | "systems/automule/AutoMule.js" | "systems/autorush/AutoRush.js" | "systems/gambling/Gambling.js" | "systems/torch/TorchSystem.js" | "manualplay/config/Assassin.js" | "manualplay/hooks/ItemHooks.js" | "manualplay/hooks/TextHooks.js" | "scripts/UndergroundPassage.js" | "SoloPlay/Core/DynamicTiers.js" | "SoloPlay/OOG/StarterConfig.js" | "SoloPlay/Scripts/bishibosh.js" | "SoloPlay/Scripts/hellforge.js" | "SoloPlay/Scripts/mausoleum.js" | "SoloPlay/Scripts/savebarby.js" | "SoloPlay/Scripts/travincal.js" | "systems/torch/FarmerConfig.js" | "systems/torch/OrgTorchData.js" | "manualplay/config/Barbarian.js" | "manualplay/config/Sorceress.js" | "SoloPlay/Config/Necromancer.js" | "SoloPlay/Core/ItemOverrides.js" | "SoloPlay/Core/ItemUtilities.js" | "SoloPlay/Core/MiscOverrides.js" | "SoloPlay/Core/NTIPOverrides.js" | "SoloPlay/Core/TownOverrides.js" | "SoloPlay/Scripts/bloodraven.js" | "SoloPlay/Scripts/corpsefire.js" | "SoloPlay/Scripts/maggotlair.js" | "SoloPlay/Scripts/templeruns.js" | "SoloPlay/Scripts/worldstone.js" | "systems/autorush/RushConfig.js" | "systems/follow/FollowConfig.js" | "core/GameData/LocaleStringID.js" | "manualplay/hooks/ActionHooks.js" | "manualplay/hooks/ShrineHooks.js" | "manualplay/hooks/VectorHooks.js" | "manualplay/threads/MapHelper.js" | "SoloPlay/Core/ItemPrototypes.js" | "SoloPlay/Core/SkillOverrides.js" | "SoloPlay/Scripts/beetleburst.js" | "SoloPlay/Scripts/lowerkurast.js" | "systems/crafting/TeamsConfig.js" | "systems/gambling/TeamsConfig.js" | "manualplay/config/Necromancer.js" | "manualplay/hooks/MonsterHooks.js" | "manualplay/libs/MiscOverrides.js" | "manualplay/libs/TownOverrides.js" | "manualplay/threads/PickThread.js" | "scripts/ClassicChaosAssistant.js" | "SoloPlay/Core/AttackOverrides.js" | "SoloPlay/Core/ConfigOverrides.js" | "SoloPlay/Core/CubingOverrides.js" | "SoloPlay/Core/LoaderOverrides.js" | "SoloPlay/Core/PatherOverrides.js" | "SoloPlay/Core/PickitOverrides.js" | "systems/channel/ChannelConfig.js" | "systems/cleaner/CleanerConfig.js" | "systems/gameaction/GameAction.js" | "systems/mulelogger/MuleLogger.js" | "systems/pubjoin/PubJoinConfig.js" | "SoloPlay/Core/PrecastOverrides.js" | "SoloPlay/Core/StorageOverrides.js" | "SoloPlay/Scripts/developermode.js" | "manualplay/libs/AttackOverrides.js" | "manualplay/libs/ConfigOverrides.js" | "manualplay/libs/PatherOverrides.js" | "manualplay/libs/PickitOverrides.js" | "SoloPlay/BuildFiles/druid/druid.js" | "SoloPlay/Core/AutoMuleOverrides.js" | "SoloPlay/Core/AutoStatOverrides.js" | "SoloPlay/Scripts/ancienttunnels.js" | "systems/crafting/CraftingSystem.js" | "systems/mulelogger/LoggerConfig.js" | "SoloPlay/Core/PrototypeOverrides.js" | "SoloPlay/Core/RunewordsOverrides.js" | "SoloPlay/Scripts/creepingfeature.js" | "manualplay/threads/MapToolsThread.js" | "SoloPlay/BuildFiles/amazon/amazon.js" | "SoloPlay/BuildFiles/Runewords/Ice.js" | "SoloPlay/Core/MuleloggerOverrides.js" | "SoloPlay/BuildFiles/Runewords/Bone.js" | "SoloPlay/BuildFiles/Runewords/Fury.js" | "SoloPlay/BuildFiles/Runewords/Lore.js" | "SoloPlay/BuildFiles/Runewords/Myth.js" | "systems/automule/config/MuleConfig.js" | "SoloPlay/BuildFiles/paladin/paladin.js" | "SoloPlay/BuildFiles/Runewords/Chaos.js" | "SoloPlay/BuildFiles/Runewords/Exile.js" | "SoloPlay/BuildFiles/Runewords/Faith.js" | "SoloPlay/BuildFiles/Runewords/Grief.js" | "SoloPlay/BuildFiles/Runewords/Honor.js" | "SoloPlay/BuildFiles/Runewords/Rhyme.js" | "SoloPlay/BuildFiles/Runewords/Smoke.js" | "SoloPlay/BuildFiles/Runewords/Steel.js" | "SoloPlay/BuildFiles/Runewords/White.js" | "systems/gameaction/GameActionConfig.js" | "config/Builds/Sorceress.ExampleBuild.js" | "SoloPlay/BuildFiles/Runewords/Duress.js" | "SoloPlay/BuildFiles/Runewords/Enigma.js" | "SoloPlay/BuildFiles/Runewords/Malice.js" | "SoloPlay/BuildFiles/assassin/assassin.js" | "SoloPlay/BuildFiles/Runewords/Silence.js" | "SoloPlay/BuildFiles/Runewords/Stealth.js" | "systems/automule/config/StarterConfig.js" | "SoloPlay/BuildFiles/Runewords/LastWish.js" | "SoloPlay/BuildFiles/Runewords/MercDoom.js" | "systems/automule/config/TorchAnniMules.js" | "SoloPlay/BuildFiles/barbarian/barbarian.js" | "SoloPlay/BuildFiles/Runewords/DreamHelm.js" | "SoloPlay/BuildFiles/Runewords/Fortitude.js" | "SoloPlay/BuildFiles/Runewords/MercPride.js" | "SoloPlay/BuildFiles/Runewords/Sanctuary.js" | "SoloPlay/BuildFiles/Runewords/Treachery.js" | "SoloPlay/BuildFiles/sorceress/sorceress.js" | "SoloPlay/BuildFiles/Runewords/CallToArms.js" | "SoloPlay/BuildFiles/Runewords/KingsGrace.js" | "SoloPlay/BuildFiles/Runewords/Lawbringer.js" | "SoloPlay/BuildFiles/druid/druid.WindBuild.js" | "SoloPlay/BuildFiles/druid/druid.WolfBuild.js" | "SoloPlay/BuildFiles/Runewords/DragonArmor.js" | "SoloPlay/BuildFiles/Runewords/DreamShield.js" | "SoloPlay/BuildFiles/Runewords/MercInsight.js" | "SoloPlay/BuildFiles/Runewords/SpiritSword.js" | "SoloPlay/BuildFiles/druid/druid.StartBuild.js" | "SoloPlay/BuildFiles/Runewords/CrescentMoon.js" | "SoloPlay/BuildFiles/Runewords/MercInfinity.js" | "SoloPlay/BuildFiles/Runewords/SpiritShield.js" | "SoloPlay/BuildFiles/necromancer/necromancer.js" | "SoloPlay/BuildFiles/Runewords/ChainsOfHonor.js" | "SoloPlay/BuildFiles/Runewords/HandOfJustice.js" | "SoloPlay/BuildFiles/Runewords/HeartOfTheOak.js" | "SoloPlay/BuildFiles/Runewords/MercFortitude.js" | "SoloPlay/BuildFiles/Runewords/MercTreachery.js" | "SoloPlay/BuildFiles/Runewords/PhoenixShield.js" | "SoloPlay/BuildFiles/Runewords/VoiceOfReason.js" | "SoloPlay/BuildFiles/amazon/amazon.StartBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.WfzonBuild.js" | "SoloPlay/BuildFiles/Runewords/AncientsPledge.js" | "SoloPlay/BuildFiles/Runewords/PDiamondShield.js" | "SoloPlay/BuildFiles/druid/druid.FirewolfBuild.js" | "SoloPlay/BuildFiles/druid/druid.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.JavazonBuild.js" | "SoloPlay/BuildFiles/druid/druid.ElementalBuild.js" | "SoloPlay/BuildFiles/druid/druid.StormbearBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.StartBuild.js" | "SoloPlay/BuildFiles/Runewords/BreathOfTheDying.js" | "SoloPlay/BuildFiles/amazon/amazon.LevelingBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.SteppingBuild.js" | "SoloPlay/BuildFiles/druid/druid.PlaguewolfBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SmiterBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ZealerBuild.js" | "SoloPlay/Core/ClassAttackOverrides/DruidAttacks.js" | "SoloPlay/BuildFiles/amazon/amazon.WitchyzonBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.StartBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.AuradinBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks.js" | "SoloPlay/BuildFiles/paladin/paladin.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.ColdBuild.js" | "SoloPlay/Core/ClassAttackOverrides/PaladinAttacks.js" | "SoloPlay/BuildFiles/amazon/amazon.FaithbowzonBuild.js" | "SoloPlay/BuildFiles/amazon/amazon.FrostmaidenBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.TrapsinBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.StartBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ThrowBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.HammerdinBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.TorchadinBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlovaBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.EsorbBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.StartBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AssassinAttacks.js" | "SoloPlay/BuildFiles/assassin/assassin.LevelingBuild.js" | "SoloPlay/BuildFiles/assassin/assassin.WhirlsinBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.FrenzyBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SingerBuild.js" | "SoloPlay/Core/ClassAttackOverrides/BarbarianAttacks.js" | "SoloPlay/Core/ClassAttackOverrides/SorceressAttacks.js" | "SoloPlay/BuildFiles/paladin/paladin.HammershockBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.SancdreamerBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.MeteorbBuild.js" | "SoloPlay/Core/ClassAttackOverrides/AmazonAttacks-WIP.js" | "SoloPlay/BuildFiles/barbarian/barbarian.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.SteppingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.UberconcBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.BoneBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LevelingBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.SteppingBuild.js" | "SoloPlay/Core/ClassAttackOverrides/NecromancerAttacks.js" | "SoloPlay/BuildFiles/barbarian/barbarian.WhirlwindBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.StartBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.LightningBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.PoisonBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.SummonBuild.js" | "SoloPlay/BuildFiles/paladin/paladin.ClassicauradinBuild.js" | "SoloPlay/BuildFiles/sorceress/sorceress.BlizzballerBuild.js" | "SoloPlay/BuildFiles/necromancer/necromancer.LevelingBuild.js" | "SoloPlay/BuildFiles/barbarian/barbarian.ImmortalwhirlBuild.js"; diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index 4511e6480..a34271c9a 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -488,7 +488,6 @@ function main () { }; this.diablo = function () { - include("core/Common/Diablo.js"); this.log("starting diablo"); function inviteIn () { @@ -560,7 +559,6 @@ function main () { return false; } - include("core/Common/Ancients.js"); this.log("starting ancients"); Town.doChores(); @@ -618,7 +616,6 @@ function main () { return false; } - include("core/Common/Baal.js"); this.log("starting baal"); if (me.inTown) { diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index bef539a2b..ed1fccf62 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -10,7 +10,6 @@ include("critical.js"); // required // globals needed for core gameplay includeCoreLibs(); -include("core/Common/Tools.js"); // system libs includeSystemLibs(); From 5409733f93274c446718c254923aefb17b8f2bac Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 11:06:17 -0400 Subject: [PATCH 577/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index ac816f15d..5af5157d0 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit ac816f15d790026539d89430d6a69be30caebde9 +Subproject commit 5af5157d0d0726d53e8c7511389213e19466fa83 From 0340265a35e304666d8eae743ecd9aa0601e55b8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 11:23:22 -0400 Subject: [PATCH 578/758] Update .gitignore --- .gitignore | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0fa69c429..b8538d3a9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,12 @@ !.vscode/settings.json !.vscode/extensions.json +# Do not track install packages +node_modules/ + +# Do not track files denoted as private +**/__* + # Do not track user generated data data/*.json images/ @@ -31,9 +37,6 @@ d2bs/logs/*.log d2bs/kolbot/env.json d2bs/kolbot/libs/manualplay/config/*.*.js d2bs/kolbot/libs/config/*.*.js - -# Do not track install packages -node_modules/ d2bs/kolbot/data/web/limedrop.json d2bs/kolbot/libs/systems/automule/config/MuleConfig.js d2bs/kolbot/libs/systems/channel/ChannelConfig.js From 39519cb0e1a35537de1eae263ec5164d91c5d10f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 13:19:31 -0400 Subject: [PATCH 579/758] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index a0c24786c..fdc6e43ba 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,15 @@ [**Join the Slack Channel!**](https://join.slack.com/t/blizzhackers/shared_invite/zt-qahq0w11-uzETJNgKmS9DdApJSRQqaw) +## Table of Contents + +- [General](#general) +- [Install Dependencies](#install-dependencies---do-this-first) +- [Set Up](#set-up) +- [Getting Started](#getting-started) +- [Guides](#guides) +- [LimeDrop](#limedrop-web-based-item-manager-and-dropper) + ## General 1. D2BS, D2Bot and kolbot # are educational tools with an open source developer community. These tools are meant to be used offline or on private servers that explicitly allow them. These tools are not meant to be abused on battle.net (a Blizzard Entertainment entity). From 0fde57dad79177044bec91f63d6f92aba93f3670 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 19:46:13 -0400 Subject: [PATCH 580/758] Add missing tracker to Attack.clearList - Always keep track of uniques/bosses killed --- d2bs/kolbot/libs/core/Attack.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index df77dcfe8..6c2d06e47 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -787,6 +787,7 @@ const Attack = { }, /** + * @todo Refactor so this can accept prebuilt monsterlist, we have repeat logic with this and clearList * @description Clear monsters in a section based on range and spectype or clear monsters around a boss monster * @param {number} [range=25] * @param {number} [spectype=0] @@ -1215,7 +1216,13 @@ const Attack = { attackCount += 1; - if (target.dead || Config.FastPick) { + if (target.dead || Config.FastPick || Config.FastFindItem) { + if ((target.isBoss || target.uniqueid > 0) && target.dead) { + // TODO: add uniqueids to sdk + target.isBoss && Attack._killed.add(target.classid); + target.uniqueid > -1 && Attack._killed.add(target.name); + } + Config.FastFindItem && pickit && ClassAttack.findItem(); Pickit.fastPick(); } } else { From a18b166b89df4a129e863a7146a37677ccc85c3a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 19:53:53 -0400 Subject: [PATCH 581/758] feat: Add timeout to securePosition props, prevent infinite wait if unable to securePosition - Seen a monster stuck in a place we couldn't attack but not blocked keep the bot waiting indefinitely, this fixes that by setting a default of 5 minutes --- d2bs/kolbot/libs/core/Attack.js | 26 ++++++++++++-------------- d2bs/kolbot/sdk/types/Attack.d.ts | 7 ++++++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 6c2d06e47..dae205571 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1249,37 +1249,30 @@ const Attack = { return true; }, - /** - * @typedef {Object} SecurePositionOptions - * @property {number} [range=15] - Range to check for monsters - * @property {number} [timer=3000] - Time to wait for a safe position - * @property {boolean} [skipBlocked=true] - Skip monsters that block the path - * @property {boolean} [useRedemption=false] - Redemption for Paladin - * @property {number[]} [skipIds=[]] - Skip monsters with these classids - */ - /** * @param {number} x * @param {number} y - * @param {SecurePositionOptions} [options] - * @returns {void} + * @param {Attack.SecurePositionOptions} [options] + * @returns {boolean} */ securePosition: function (x, y, options = {}) { let tick; (typeof x !== "number" || typeof y !== "number") && ({ x, y } = me); const node = new PathNode(x, y); - /** @type {Required} */ + /** @type {Required} */ const clearOptions = Object.assign({ range: 15, timer: 3000, skipBlocked: true, useRedemption: false, skipIds: [], + timeout: Time.minutes(5), }, options); clearOptions.skipBlocked === true && (clearOptions.skipBlocked = sdk.collision.Ranged); - const { range, timer, skipBlocked, useRedemption, skipIds } = clearOptions; + const startTime = getTickCount(); + const { range, timer, skipBlocked, useRedemption, skipIds, timeout } = clearOptions; while (true) { node.distance > 5 && Pather.moveTo(node.x, node.y); @@ -1303,7 +1296,7 @@ const Attack = { // only return if it's been safe long enough if (getTickCount() - tick >= timer) { - return; + return true; } } else { this.clearList(monList); @@ -1319,6 +1312,11 @@ const Attack = { } } + if (timeout && getTickCount() - startTime >= timeout) { + console.warn("ÿc1Attack.securePosition: Timeout reached, giving up."); + return false; + } + delay(100); } }, diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 96cd0448c..791b44592 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -48,8 +48,13 @@ declare global { skipBlocked?: boolean; useRedemption?: boolean; skipIds?: number[]; + /** + * @default 300000 (5 minutes) + * @description Timeout in milliseconds for attempting to secure area. + */ + timeout?: number; } - function securePosition(x: number, y: number, options: SecurePositionOptions): void; + function securePosition(x: number, y: number, options: SecurePositionOptions): boolean; function countUniques(): void; function storeStatistics(area: number): void; function clearRoom(room: Room, spectype?: number): boolean; From 5934905c30b5c8ca731a29cc81b5e6f35a7e4fec Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 6 Jul 2025 19:57:53 -0400 Subject: [PATCH 582/758] Add skipIds for rush commands that come close to boss - This helps for if the boss gets agroed while we are securing area, doesn't fix if clearPath is being used though --- d2bs/kolbot/libs/scripts/ControlBot.js | 4 ++-- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ControlBot.js b/d2bs/kolbot/libs/scripts/ControlBot.js index dad9125ce..c1ea012cd 100644 --- a/d2bs/kolbot/libs/scripts/ControlBot.js +++ b/d2bs/kolbot/libs/scripts/ControlBot.js @@ -166,8 +166,8 @@ const ControlBot = new Runnable( throw new Error("Failed to move to durance 3"); } Pather.moveTo(17617, 8069); - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, { range: 20, duration: 3000 }); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipIds: [sdk.monsters.Mephisto] }); + Attack.securePosition(17591, 8070, { range: 20, duration: 3000, skipIds: [sdk.monsters.Mephisto] }); let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); if (hydra) { diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index b681f2c44..e811857c0 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -1059,7 +1059,7 @@ y: me.y }; - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); + Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipIds: [sdk.monsters.Izual] }); Pather.makePortal(); log(AutoRush.playersIn); From b9572c7ff40086134de859860d842028f6b7465b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 7 Jul 2025 01:40:25 -0400 Subject: [PATCH 583/758] Add `distanceTo` prototype on PathNode --- d2bs/kolbot/libs/core/Pather.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 00487788b..1683f5a0b 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -15,6 +15,25 @@ function PathNode (x, y) { this.y = y; } +/** + * Distance from unit to node + * @param {Unit} unit + * @returns {number} + */ +PathNode.prototype.distanceTo = function (unit) { + return !me.gameReady ? NaN : (getDistance.apply(null, [unit, this])); +}; + +PathNode.prototype.getWalkDistance = function () { + return (getPath(me.area, me.x, me.y, this.x, this.y, 0, Pather.walkDistance) || []) + .map(function (e, i, s) { + return i && getDistance(s[i - 1], e) || 0; + }) + .reduce(function (acc, cur) { + return acc + cur; + }, 0) || Infinity; +}; + /** * Perform certain actions after moving to each node * @todo this needs to be re-worked From 9ca8d3cf2e276423b68ebbd84a17188654f99bf4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 7 Jul 2025 17:32:48 -0400 Subject: [PATCH 584/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 5af5157d0..a5e971fb8 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 5af5157d0d0726d53e8c7511389213e19466fa83 +Subproject commit a5e971fb8ddae919e469536dface06dde7d24350 From 31063e93da1e903e393e84977829dd867b9d71ef Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 7 Jul 2025 18:20:38 -0400 Subject: [PATCH 585/758] Remove Baal constructor name from export - Not entirely sure why this was causing the lazy loading to fail, name conflict maybe? --- d2bs/kolbot/libs/core/Common/Baal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 419ab9458..e18d5de0f 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -6,7 +6,7 @@ */ (function (module) { - module.exports = new function Baal () { + module.exports = new function () { this.throneCoords = { bottomLeft: { x: 15072, y: 5073 }, bottomRight: { x: 15118, y: 5073 }, From 8bd601ecb641a016bbb9174651bdf358ccd2e728 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 8 Jul 2025 02:23:05 -0400 Subject: [PATCH 586/758] Add deepMerge utility for deep object merging - Introduced a deepMerge function in globals.js to perform deep merging of objects, including nested properties. - Updated globals.d.ts to include type definitions for the new deepMerge function. --- d2bs/kolbot/libs/globals.js | 28 ++++++++++++++++++++++++++++ d2bs/kolbot/sdk/globals.d.ts | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/d2bs/kolbot/libs/globals.js b/d2bs/kolbot/libs/globals.js index 60cc3b846..e520b0c03 100644 --- a/d2bs/kolbot/libs/globals.js +++ b/d2bs/kolbot/libs/globals.js @@ -297,3 +297,31 @@ const getThreads = function () { return threads; }; + +/** + * Deep merge objects, handling nested properties properly + * @param {Object} target - Target object to merge into + * @param {Object} source - Source object to merge from + * @returns {Object} Merged object + */ +function deepMerge(target, source) { + if (!source || typeof source !== "object") { + return target; + } + + let result = Object.assign({}, target); + + for (let key in source) { + if (source.hasOwnProperty(key)) { + if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) { + // Recursively merge nested objects + result[key] = deepMerge(result[key] || {}, source[key]); + } else { + // Direct assignment for primitives and arrays + result[key] = source[key]; + } + } + } + + return result; +} diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 3ca93bb2f..9142a6935 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1477,5 +1477,13 @@ declare global { * @note Use sparingly, this method stops the background workers on the callers thread */ function threadSleep(ms: number); + + /** + * Deep merge objects, handling nested properties properly + * @param target - Target object to merge into + * @param source - Source object to merge from + * @returns Merged object + */ + function deepMerge(target: object, source: object): object } export {}; From db1d6b1cd8ec79207b4efb529989f35e905fdf80 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 8 Jul 2025 03:14:42 -0400 Subject: [PATCH 587/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index a5e971fb8..89fd26e7c 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit a5e971fb8ddae919e469536dface06dde7d24350 +Subproject commit 89fd26e7c2fbaad0fe0b882c4e5a45a8d37dd6c0 From c410d3795b56708f50ec53610dd1ba0c279961cf Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 8 Jul 2025 03:16:03 -0400 Subject: [PATCH 588/758] Update setup.bat - Handle soloplays configuration files --- setup.bat | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/setup.bat b/setup.bat index 1dca16d51..d72b006a7 100644 --- a/setup.bat +++ b/setup.bat @@ -29,6 +29,10 @@ set "D2BS_DIR=d2bs" set "STARTER_DIR=d2bs\kolbot\libs\starter" set "SYSTEMS_DIR=d2bs\kolbot\libs\systems" set "CONFIG_DIR=d2bs\kolbot\libs\config" +set "SOLOPLAY_DIR=d2bs\kolbot\libs\SoloPlay" +set "SOLOPLAY_SETUP_DIR=%SOLOPLAY_DIR%\+setup" +set "SOLOPLAY_SETTINGS_DIR=%SOLOPLAY_DIR%\Settings" +set "SOLOPLAY_OOG_DIR=%SOLOPLAY_DIR%\OOG" REM Create directories if they don't exist if not exist "%DATA_DIR%\" mkdir "%DATA_DIR%" @@ -97,5 +101,10 @@ REM Copy starter config files to their respective directories call :CopyIfNotExists "%SETUP_DIR%\starter\AdvancedConfig.js" "%STARTER_DIR%\AdvancedConfig.js" "AdvancedConfig.js to starter directory" call :CopyIfNotExists "%SETUP_DIR%\starter\StarterConfig.js" "%STARTER_DIR%\StarterConfig.js" "StarterConfig.js to starter directory" +REM Copy SoloPlay setup files to their respective directories +call :CopyIfNotExists "%SOLOPLAY_SETUP_DIR%\Settings.js" "%SOLOPLAY_SETTINGS_DIR%\Settings.js" "Settings.js to SoloPlay Settings directory" +call :CopyIfNotExists "%SOLOPLAY_SETUP_DIR%\AdvancedSettings.js" "%SOLOPLAY_SETTINGS_DIR%\AdvancedSettings.js" "AdvancedSettings.js to SoloPlay Settings directory" +call :CopyIfNotExists "%SOLOPLAY_SETUP_DIR%\StarterConfig.js" "%SOLOPLAY_OOG_DIR%\StarterConfig.js" "StarterConfig.js to SoloPlay OOG directory" + echo Setup files copied successfully! pause \ No newline at end of file From 78db714fa0704b5a100a6eb252c4c23103e09f62 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:04:06 -0400 Subject: [PATCH 589/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 89fd26e7c..43222c137 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 89fd26e7c2fbaad0fe0b882c4e5a45a8d37dd6c0 +Subproject commit 43222c1379e9104b090e2da93b25ed6f0d4445bd From 81cf5b33c7dc5abce0f2e16feec98dac7bb7798e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:48:32 -0400 Subject: [PATCH 590/758] Add missing `SoulQuit` config option for BaalHelper --- d2bs/kolbot/libs/config/Amazon.js | 1 + d2bs/kolbot/libs/config/Assassin.js | 1 + d2bs/kolbot/libs/config/Barbarian.js | 1 + d2bs/kolbot/libs/config/Druid.js | 1 + d2bs/kolbot/libs/config/Necromancer.js | 1 + d2bs/kolbot/libs/config/Paladin.js | 1 + d2bs/kolbot/libs/config/Sorceress.js | 1 + d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + d2bs/kolbot/libs/core/Config.js | 1 + 9 files changed, 9 insertions(+) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index edcea5189..2a7c2ae7d 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -208,6 +208,7 @@ function LoadConfig () { Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index aa3960e31..83bf3716f 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -208,6 +208,7 @@ function LoadConfig () { Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 73159bddb..7b83c215a 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -208,6 +208,7 @@ function LoadConfig () { Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 31a42aada..567f568da 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -208,6 +208,7 @@ function LoadConfig () { Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 98afb32c8..d9f1ffadc 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -208,6 +208,7 @@ function LoadConfig () { Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 56d7985de..dae6d62e0 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -208,6 +208,7 @@ function LoadConfig () { Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index ca2da77d9..8bb582122 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -208,6 +208,7 @@ function LoadConfig () { Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index ef13d2f65..0f1036500 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -174,6 +174,7 @@ Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne + Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 7147ed518..0c53b48c9 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -540,6 +540,7 @@ let Config = { KillNihlathak: false, FastChaos: false, DollQuit: false, + SoulQuit: false, KillBaal: false, SkipTP: false }, From 49d4c4877ac038a246ca92eaea570c12a3892660 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:51:03 -0400 Subject: [PATCH 591/758] Replace usage of global print with console.log - I thought I had gotten all of these already --- d2bs/kolbot/libs/OOG.js | 8 ++++---- d2bs/kolbot/libs/core/NTItemParser.js | 4 ++-- d2bs/kolbot/libs/modules/LocalChat.js | 2 +- d2bs/kolbot/libs/modules/Promise.js | 2 +- d2bs/kolbot/libs/modules/Team.js | 4 ++-- d2bs/kolbot/libs/modules/Worker.js | 2 +- d2bs/kolbot/libs/require.js | 4 ++-- d2bs/kolbot/libs/scripts/BaalAssistant.js | 4 ++-- d2bs/kolbot/libs/scripts/BaalHelper.js | 2 +- d2bs/kolbot/libs/scripts/Crafting.js | 14 +++++++------- d2bs/kolbot/libs/scripts/Gamble.js | 2 +- d2bs/kolbot/libs/scripts/Rushee.js | 10 +++++----- d2bs/kolbot/libs/scripts/Test.js | 10 +++++----- d2bs/kolbot/libs/systems/torch/TorchSystem.js | 6 +++--- d2bs/kolbot/threads/RushThread.js | 10 +++++----- 15 files changed, 42 insertions(+), 42 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 89393572e..7eb9223bc 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -773,7 +773,7 @@ includeIfNotIncluded("core/Me.js"); break MainLoop; // break if we're sure we're on empty char screen default: - print(getLocation()); + console.log(getLocation()); me.blockMouse = false; @@ -962,7 +962,7 @@ includeIfNotIncluded("core/Me.js"); try { login(me.profile); } catch (e) { - print(e); + console.log(e); } break; @@ -1570,7 +1570,7 @@ includeIfNotIncluded("core/Me.js"); // If stuck here for too long, game creation likely failed. Exit to char selection and try again. if (queue < 10) { if (!Starter.locationTimeout(Starter.Config.WaitInLineTimeout * 1e3, currentLoc)) { - print("Failed to create game"); + console.log("Failed to create game"); Controls.CancelCreateGame.click(); Controls.LobbyQuit.click(); delay(1000); @@ -1582,7 +1582,7 @@ includeIfNotIncluded("core/Me.js"); if (Starter.Config.WaitOutQueueRestriction) { D2Bot.updateStatus("Waiting out Queue restriction: " + queue); } else { - print("Restricted... Queue: " + queue); + console.log("Restricted... Queue: " + queue); D2Bot.printToConsole("Restricted... Queue: " + queue, sdk.colors.D2Bot.Red); Controls.CancelCreateGame.click(); diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 8234bfd03..74ce96568 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -122,7 +122,7 @@ NTIP.CheckQuantityOwned = function (item_type, item_stats) { let items = me.getItemsEx(); if (!items.length) { - print("I can't find my items!"); + console.log("I can't find my items!"); return 0; } @@ -145,7 +145,7 @@ NTIP.CheckQuantityOwned = function (item_type, item_stats) { } } - //print("I have "+num+" of these."); + //console.log("I have "+num+" of these."); return num; }; diff --git a/d2bs/kolbot/libs/modules/LocalChat.js b/d2bs/kolbot/libs/modules/LocalChat.js index 298e8d1e4..1ccf9df25 100644 --- a/d2bs/kolbot/libs/modules/LocalChat.js +++ b/d2bs/kolbot/libs/modules/LocalChat.js @@ -52,7 +52,7 @@ if (!Config.LocalChat.Enabled) return; Config.LocalChat.Mode = (Config.LocalChat.Mode + cycle) % 3; - print("ÿc2LocalChat enabled. Mode: " + Config.LocalChat.Mode); + console.log("ÿc2LocalChat enabled. Mode: " + Config.LocalChat.Mode); switch (Config.LocalChat.Mode) { case 2: diff --git a/d2bs/kolbot/libs/modules/Promise.js b/d2bs/kolbot/libs/modules/Promise.js index 71a17a141..c24217b9f 100644 --- a/d2bs/kolbot/libs/modules/Promise.js +++ b/d2bs/kolbot/libs/modules/Promise.js @@ -44,7 +44,7 @@ if (this.__proto__.constructor !== Promise) { - print((new Error).stack); + console.log((new Error).stack); throw new Error("Promise must be called with 'new' operator!"); } diff --git a/d2bs/kolbot/libs/modules/Team.js b/d2bs/kolbot/libs/modules/Team.js index 773df9db5..f8d69099a 100644 --- a/d2bs/kolbot/libs/modules/Team.js +++ b/d2bs/kolbot/libs/modules/Team.js @@ -39,7 +39,7 @@ }; if (threadType === "thread") { - print("ÿc2Kolbotÿc0 :: Team thread started"); + console.log("ÿc2Kolbotÿc0 :: Team thread started"); Messaging.on("Team", data => ( typeof data === "object" && data @@ -61,7 +61,7 @@ newContent = FileTools.readText("data/" + filename); if (!newContent) return; // no content } catch (e) { - print("Can't read: `" + "data/" + filename + "`"); + console.log("Can't read: `" + "data/" + filename + "`"); } diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index c704981cc..505485c90 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -43,7 +43,7 @@ throw error; } // keep on throwing - print("[ÿc9Warningÿc0] Too much recursion"); + console.log("[ÿc9Warningÿc0] Too much recursion"); } }; diff --git a/d2bs/kolbot/libs/require.js b/d2bs/kolbot/libs/require.js index 3fc8ba311..2ee91f833 100644 --- a/d2bs/kolbot/libs/require.js +++ b/d2bs/kolbot/libs/require.js @@ -78,8 +78,8 @@ global.require = (function (include, isIncluded, print, notify) { if (!isIncluded(fullpath + ".js") && !modules.hasOwnProperty(moduleNameShort)) { if (debug) { - depth && notify && print("ÿc2Kolbotÿc0 :: - loading dependency of " + filename + ": " + moduleNameShort); - !depth && notify && print("ÿc2Kolbotÿc0 :: Loading module: " + moduleNameShort); + depth && notify && console.log("ÿc2Kolbotÿc0 :: - loading dependency of " + filename + ": " + moduleNameShort); + !depth && notify && console.log("ÿc2Kolbotÿc0 :: Loading module: " + moduleNameShort); } let oldModule = Object.create(global["module"]); diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index 2685b3579..34d121d66 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -163,7 +163,7 @@ const BaalAssistant = new Runnable( destination: sdk.areas.ThroneofDestruction, quitIf: (area) => [sdk.areas.WorldstoneChamber].includes(area) }))) { - print("ÿc hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); if (!hotCheck) { - print("ÿc1Leader didn't tell me to start hunting for an experience shrine."); + console.log("ÿc1Leader didn't tell me to start hunting for an experience shrine."); ShrineStatus = true; } } diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 7d2249d4f..7c8027a08 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -105,7 +105,7 @@ const BaalHelper = new Runnable( } if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { - print("Undead Soul Killers found."); + console.log("Undead Soul Killers found."); return true; } diff --git a/d2bs/kolbot/libs/scripts/Crafting.js b/d2bs/kolbot/libs/scripts/Crafting.js index 1af6bb943..2bd243c0d 100644 --- a/d2bs/kolbot/libs/scripts/Crafting.js +++ b/d2bs/kolbot/libs/scripts/Crafting.js @@ -36,7 +36,7 @@ const Crafting = new Runnable( switch (obj.name) { case "GetGame": if (info.Collectors.includes(obj.profile)) { - print("GetGame: " + obj.profile); + console.log("GetGame: " + obj.profile); sendCopyData(null, obj.profile, 4, me.gamename + "/" + me.gamepassword); gameRequest = true; @@ -45,7 +45,7 @@ const Crafting = new Runnable( break; case "GetSetInfo": if (info.Collectors.includes(obj.profile)) { - print("GetSetInfo: " + obj.profile); + console.log("GetSetInfo: " + obj.profile); rval = []; @@ -53,7 +53,7 @@ const Crafting = new Runnable( rval.push(info.Sets[i].Enabled ? 1 : 0); } - print(rval); + console.log(rval); sendCopyData(null, obj.profile, 4, JSON.stringify({ name: "SetInfo", value: rval })); } @@ -180,7 +180,7 @@ function updateInfo () { for (let j = 0; j < items.length; j += 1) { if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list && AutoMule.cubingIngredient(items[j])) { // Item is a valid Cubing ingredient - print("Base found: " + items[j].classid); + console.log("Base found: " + items[j].classid); info.Sets[i].Enabled = true; @@ -201,7 +201,7 @@ function updateInfo () { for (let j = 0; j < items.length; j += 1) { if (info.Sets[i].BaseItems.includes(items[j].classid) // Item is on the bases list && runewordIngredient(items[j])) { // Item is a valid Runeword ingredient - print("Base found: " + items[j].classid); + console.log("Base found: " + items[j].classid); info.Sets[i].Enabled = true; @@ -292,7 +292,7 @@ function checkItem (item) { } function shopStuff (npcId, classids, amount) { - print("shopStuff: " + npcId + " " + amount); + console.log("shopStuff: " + npcId + " " + amount); let wpArea, town, path, menuId, npc; let leadTimeout = 30; @@ -360,7 +360,7 @@ function shopStuff (npcId, classids, amount) { && me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && classids.includes(items[i].classid)) { - //print("Bought " + items[i].name); + //console.log("Bought " + items[i].name); items[i].buy(); let num = countItems(classids, sdk.items.quality.Magic); diff --git a/d2bs/kolbot/libs/scripts/Gamble.js b/d2bs/kolbot/libs/scripts/Gamble.js index 5bf462515..639b4323e 100644 --- a/d2bs/kolbot/libs/scripts/Gamble.js +++ b/d2bs/kolbot/libs/scripts/Gamble.js @@ -19,7 +19,7 @@ const Gamble = new Runnable( addEventListener("copydata", function (mode, msg) { if (needGold && mode === 0 && info.goldFinders.indexOf(msg) > -1) { - print("Got game request from " + msg); + console.log("Got game request from " + msg); sendCopyData(null, msg, 4, me.gamename + "/" + me.gamepassword); } }); diff --git a/d2bs/kolbot/libs/scripts/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js index cf371e53c..64803bd14 100644 --- a/d2bs/kolbot/libs/scripts/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -14,7 +14,7 @@ new Overrides.Override(Town, Town.goToTown, function(orignal, act, wpmenu) { return true; } catch (e) { - print(e); + console.log(e); return Pather.useWaypoint(sdk.areas.townOf(me.area)); } @@ -75,7 +75,7 @@ function Rushee() { let actions = []; this.log = function (msg = "", sayMsg = false) { - print(msg); + console.log(msg); sayMsg && say(msg); }; @@ -83,7 +83,7 @@ function Rushee() { let scroll = me.scrollofresistance; if (scroll) { clickItem(sdk.clicktypes.click.item.Right, scroll); - print("Using scroll of resistance"); + console.log("Using scroll of resistance"); } }; @@ -185,7 +185,7 @@ function Rushee() { Storage.Cube.MoveTo(amulet); Storage.Cube.MoveTo(shaft); Cubing.openCube(); - print("making staff"); + console.log("making staff"); transmute(); delay(750 + me.ping); @@ -414,7 +414,7 @@ function Rushee() { target = me.getItem(sdk.quest.item.BookofSkill); if (target) { - print("Using book of skill"); + console.log("Using book of skill"); clickItem(sdk.clicktypes.click.item.Right, target); break; diff --git a/d2bs/kolbot/libs/scripts/Test.js b/d2bs/kolbot/libs/scripts/Test.js index daa6dde31..325046736 100644 --- a/d2bs/kolbot/libs/scripts/Test.js +++ b/d2bs/kolbot/libs/scripts/Test.js @@ -6,7 +6,7 @@ */ function Test() { - print("ÿc8TESTING"); + console.log("ÿc8TESTING"); let c; @@ -21,8 +21,8 @@ function Test() { try { doTest(); } catch (qq) { - print("failed"); - print(qq + " " + qq.fileName + " " + qq.lineNumber); + console.log("failed"); + console.log(qq + " " + qq.fileName + " " + qq.lineNumber); } c = false; @@ -33,6 +33,6 @@ function Test() { } function doTest() { - print("test"); - print("done"); + console.log("test"); + console.log("done"); } diff --git a/d2bs/kolbot/libs/systems/torch/TorchSystem.js b/d2bs/kolbot/libs/systems/torch/TorchSystem.js index 92589b84f..21623ddf9 100644 --- a/d2bs/kolbot/libs/systems/torch/TorchSystem.js +++ b/d2bs/kolbot/libs/systems/torch/TorchSystem.js @@ -48,7 +48,7 @@ const TorchSystem = { for (let i = 0; i < farmers.length; i += 1) { if (farmers[i].FarmGame.length > 0 && me.gamename.toLowerCase().match(farmers[i].FarmGame.toLowerCase())) { - print("ÿc4Torch Systemÿc0: In Farm game."); + console.log("ÿc4Torch Systemÿc0: In Farm game."); D2Bot.printToConsole("Torch System: Transfering keys.", sdk.colors.D2Bot.DarkGold); D2Bot.updateStatus("Torch System: In game."); Town.goToTown(1); @@ -282,7 +282,7 @@ const TorchSystem = { } if (farmer.KeyFinderProfiles.includes(obj.profile)) { - print("Got game request from: " + obj.profile); + console.log("Got game request from: " + obj.profile); sendCopyData( null, obj.profile, 6, JSON.stringify({ name: "gameName", value: { gameName: me.gamename, password: me.gamepassword } }) @@ -295,7 +295,7 @@ const TorchSystem = { break; case "keyCheck": if (farmer.KeyFinderProfiles.includes(obj.profile)) { - print("Got key count request from: " + obj.profile); + console.log("Got key count request from: " + obj.profile); // Get the number of needed keys neededItems = { pk1: 3 - tkeys, pk2: 3 - hkeys, pk3: 3 - dkeys, rv: juvCheck() }; diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index a34271c9a..7acd95457 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -80,7 +80,7 @@ new Overrides.Override(Pather, Pather.useWaypoint, function(orignal, targetArea, if (orignal(targetArea, check)) { return (Config.Rusher.GiveWps && giveWP()) || true; } else { - print("failed"); + console.log("failed"); return false; } @@ -513,10 +513,10 @@ function main () { try { Common.Diablo.runSeals(Config.Diablo.SealOrder); - print("Attempting to find Diablo"); + console.log("Attempting to find Diablo"); inviteIn() && Common.Diablo.diabloPrep(); } catch (error) { - print("Diablo wasn't found. Checking seals."); + console.log("Diablo wasn't found. Checking seals."); Common.Diablo.runSeals(Config.Diablo.SealOrder); inviteIn() && Common.Diablo.diabloPrep(); } @@ -793,7 +793,7 @@ function main () { moveIntoPos(radaCoords, 50); let rada = Misc.poll(() => Game.getMonster(sdk.monsters.Radament), 1500, 500); - rada ? moveIntoPos(rada, 60) : print("radament unit not found"); + rada ? moveIntoPos(rada, 60) : console.log("radament unit not found"); Attack.securePosition(me.x, me.y, { range: 35, duration: 3000 }); Pather.makePortal(); this.log("1"); @@ -917,7 +917,7 @@ function main () { moveIntoPos(izualCoords, 50); let izual = Misc.poll(() => Game.getMonster(sdk.monsters.Izual), 1500, 500); - izual ? moveIntoPos(izual, 60) : print("izual unit not found"); + izual ? moveIntoPos(izual, 60) : console.log("izual unit not found"); let returnSpot = { x: me.x, From 5831ad1c1c9faba38ff0ffa70e730af6935dedef Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 10 Jul 2025 13:19:44 -0400 Subject: [PATCH 592/758] Added fallback to moveToPresetObject in Rakanishu.js - Updated both scripts to use Pather.moveToPresetMonster for improved reliability when locating bosses. - Added fallback to moveToPresetObject in Rakanishu.js for cases where the boss cannot be found due to map seed issues --- d2bs/kolbot/libs/scripts/Corpsefire.js | 5 +++-- d2bs/kolbot/libs/scripts/Rakanishu.js | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Corpsefire.js b/d2bs/kolbot/libs/scripts/Corpsefire.js index 83bd8a449..2101d5597 100644 --- a/d2bs/kolbot/libs/scripts/Corpsefire.js +++ b/d2bs/kolbot/libs/scripts/Corpsefire.js @@ -1,6 +1,6 @@ /** * @filename Corpsefire.js -* @author kolton +* @author kolton, theBGuy * @desc kill Corpsefire and optionally clear Den of Evil * */ @@ -11,7 +11,8 @@ const Corpsefire = new Runnable( Precast.doPrecast(true); if (!Pather.moveToExit([sdk.areas.BloodMoor, sdk.areas.DenofEvil], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Corpsefire, 0, 0, false, true)) { + || !Pather.moveToPresetMonster(sdk.areas.DenofEvil, sdk.monsters.preset.Corpsefire, { pop: true }) + ) { throw new Error("Failed to move to Corpsefire"); } diff --git a/d2bs/kolbot/libs/scripts/Rakanishu.js b/d2bs/kolbot/libs/scripts/Rakanishu.js index 36203d964..e01a0323a 100644 --- a/d2bs/kolbot/libs/scripts/Rakanishu.js +++ b/d2bs/kolbot/libs/scripts/Rakanishu.js @@ -11,8 +11,11 @@ const Rakanishu = new Runnable( Precast.doPrecast(true); if (!Attack.haveKilled(getLocaleString(sdk.locale.monsters.Rakanishu))) { - if (!Pather.moveToPreset(me.area, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 0, 0, false, true)) { - throw new Error("Failed to move to Rakanishu"); + if (!Pather.moveToPresetMonster(sdk.areas.StonyField, sdk.monsters.preset.Rakanishu, { pop: true })) { + // sometime bad map seed will not allow us to find Rakanishu to use stone preset + if (!Pather.moveToPresetObject(sdk.areas.StonyField, sdk.quest.chest.StoneAlpha)) { + throw new Error("Failed to move to Rakanishu"); + } } Attack.kill(getLocaleString(sdk.locale.monsters.Rakanishu)); } From e62145cd75d93260b637adb95d6e71a93c782698 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 10 Jul 2025 16:17:02 -0400 Subject: [PATCH 593/758] Update jsdoc for `Config.AdvancedCustomAttack` for `preAttack` addition --- d2bs/kolbot/libs/config/Amazon.js | 3 ++- d2bs/kolbot/libs/config/Assassin.js | 3 ++- d2bs/kolbot/libs/config/Barbarian.js | 3 ++- d2bs/kolbot/libs/config/Druid.js | 3 ++- d2bs/kolbot/libs/config/Necromancer.js | 3 ++- d2bs/kolbot/libs/config/Paladin.js | 3 ++- d2bs/kolbot/libs/config/Sorceress.js | 3 ++- d2bs/kolbot/libs/config/_BaseConfigFile.js | 19 +++++++++++-------- 8 files changed, 25 insertions(+), 15 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 2a7c2ae7d..f91d87967 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -584,11 +584,12 @@ function LoadConfig () { }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = []; diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 83bf3716f..0a536958a 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -584,11 +584,12 @@ function LoadConfig () { }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = []; diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 7b83c215a..245db2182 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -581,11 +581,12 @@ function LoadConfig () { }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = []; diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 567f568da..c7ba24b35 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -584,11 +584,12 @@ function LoadConfig () { }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = []; diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index d9f1ffadc..2a2f9e952 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -584,11 +584,12 @@ function LoadConfig () { }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = []; diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index dae6d62e0..37074a7b8 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -584,11 +584,12 @@ function LoadConfig () { }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = []; diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 8bb582122..137c04645 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -583,11 +583,12 @@ function LoadConfig () { }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = []; diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 0f1036500..1c1bff9d8 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -578,11 +578,12 @@ }; /** - * @type {{ check: (unit: Monster) => boolean, attack: [number, number] }[]} + * @type {{ check: (unit: Monster) => boolean, attack?: [number, number], preAttack?: number }[]} * Advanced Attack config. Allows custom skills to be used on custom conditions. * Each entry in the array should be an object with a `check` function and an `attack` array. * The `check` function determines whether the custom attack should be used on a given monster. * The `attack` array specifies the skills to use: [timed skill id, untimed skill id]. + * The `preAttack` property can be used to specify a skill to cast before the main attack. * * Example: * [ @@ -590,19 +591,21 @@ * check: function (unit) { * return unit.getEnchant(sdk.enchant.LightningEnchanted); * }, - * attack: [sdk.skills.Zeal, sdk.skills.Salvation] + * attack: [sdk.skills.Zeal, sdk.skills.Salvation], + * preAttack: sdk.skills.SlowMissiles * }, * ] * * Multiple entries are separated by commas. */ Config.AdvancedCustomAttack = [ - // { - // check: function (unit) { - // return unit.getEnchant(sdk.enchant.LightningEnchanted); - // }, - // attack: [sdk.skills.Zeal, sdk.skills.Salvation] - // }, + { + // check: function (unit) { + // return unit.getEnchant(sdk.enchant.LightningEnchanted); + // }, + // attack: [sdk.skills.Zeal, sdk.skills.Salvation], + // preAttack: sdk.skills.SlowMissiles + }, ]; /** From d39258767a2961b4e3f316647ef3e791fbc66fc8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 12 Jul 2025 13:17:23 -0400 Subject: [PATCH 594/758] Update README.md --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fdc6e43ba..ba3ce86d4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ - [General](#general) - [Install Dependencies](#install-dependencies---do-this-first) -- [Set Up](#set-up) +- [Required after Download Setup](#required-after-download-setup) - [Getting Started](#getting-started) - [Guides](#guides) - [LimeDrop](#limedrop-web-based-item-manager-and-dropper) @@ -40,11 +40,11 @@ If you want to contribute to d2bs/d2bot#, come to irc.synirc.net/d2bs and ask ar - [Microsoft Visual C++ 2010 Redistributable Package (x86)](https://www.microsoft.com/en-us/download/details.aspx?id=26999) - [Microsoft .NET Framework 4.0 (or higher)](https://dotnet.microsoft.com/download/dotnet-framework) -## Set up +## Required after download setup -**IMPORTANT: Run the setup script first!** +**kolbot will NOT work without running the setup script first!** -After downloading kolbot, you must run the setup script to configure the necessary files: +After downloading kolbot, you **MUST** run the setup script before attempting to use the bot: 1. Run `setup.bat` to copy configuration files to their correct locations 2. The script will also initialize Git submodules if Git is available @@ -52,10 +52,14 @@ After downloading kolbot, you must run the setup script to configure the necessa To keep your installation updated, you can run `update.bat` at any time to pull the latest changes from the repository and update all submodules. +### What happens if you skip setup: +- Missing configuration files +- You'll see this error: `Please view your exceptions.log file. D2Bot# will close now :(` + ## Getting Started - [download kolbot](https://github.com/blizzhackers/documentation/blob/restructure/d2bot/Download.md#download) - [d2bot manager setup](https://github.com/blizzhackers/documentation/blob/restructure/d2bot/ManagerSetup.md/#manager-setup) -- [IDE-Setup](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/IDES.md/#code-editors-ides): +- [IDE-Setup](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/IDES.md/#code-editors-ides) - [FAQ](https://github.com/blizzhackers/documentation/blob/restructure/kolbot/FAQ.md/#faq) ## Guides From 00defaf66512732672725f90437105e83e621b75 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 14 Jul 2025 19:16:03 -0400 Subject: [PATCH 595/758] Replace batch setup with PowerShell script - Added a new PowerShell script (+setup/setup.ps1) to handle setup tasks, including directory creation, file copying, and submodule initialization. The setup.bat script is now simplified to invoke the PowerShell script, consolidating logic and improving maintainability. - The execution flow of the batch script with the function call was failing occasionally so this simplifes it --- +setup/setup.ps1 | 136 +++++++++++++++++++++++++++++++++++++++++++++++ setup.bat | 109 +------------------------------------ 2 files changed, 137 insertions(+), 108 deletions(-) create mode 100644 +setup/setup.ps1 diff --git a/+setup/setup.ps1 b/+setup/setup.ps1 new file mode 100644 index 000000000..836c7fe33 --- /dev/null +++ b/+setup/setup.ps1 @@ -0,0 +1,136 @@ +function Copy-IfNotExists { + param ( + [string]$Source, + [string]$Destination, + [string]$Description + ) + if (Test-Path $Source) { + if (-not (Test-Path $Destination)) { + Copy-Item $Source $Destination -Force + Write-Host "Copied $Description" + } else { + Write-Host "$Description already exists - skipping" + } + } else { + Write-Host "WARNING: Source file $Source not found - skipping $Description" + } +} + +Write-Host "Copying setup files to their respective directories..." +Write-Host "Current directory: $PWD" +Write-Host "" + +# Check if git is available +if (-not (Get-Command git -ErrorAction SilentlyContinue)) { + Write-Host "WARNING: Git is not installed or not available in PATH" + Write-Host "Please install Git and ensure it's in your PATH to use submodules" + Write-Host "Continuing with file copying only..." + Write-Host "" +} else { + Write-Host "Git detected, initializing and updating submodules..." + git submodule update --init --recursive + if ($LASTEXITCODE -ne 0) { + Write-Host "WARNING: Failed to update submodules" + Write-Host "Please check your Git configuration and try again" + Write-Host "" + } else { + Write-Host "Submodules updated successfully!" + Write-Host "" + } +} + +# Set base directories +$SETUP_DIR = "+setup" +$DATA_DIR = "data" +$LOGS_DIR = "logs" +$D2BS_DIR = "d2bs" +$STARTER_DIR = "d2bs\kolbot\libs\starter" +$SYSTEMS_DIR = "d2bs\kolbot\libs\systems" +$CONFIG_DIR = "d2bs\kolbot\libs\config" +$SOLOPLAY_DIR = "d2bs\kolbot\libs\SoloPlay" +$SOLOPLAY_SETUP_DIR = "$SOLOPLAY_DIR\+setup" +$SOLOPLAY_SETTINGS_DIR = "$SOLOPLAY_DIR\Settings" +$SOLOPLAY_OOG_DIR = "$SOLOPLAY_DIR\OOG" + +# Create directories if they don't exist +$dirs = @( + $DATA_DIR, + $LOGS_DIR, + $D2BS_DIR, + $STARTER_DIR, + "$SYSTEMS_DIR\automule\config", + "$SYSTEMS_DIR\channel", + "$SYSTEMS_DIR\cleaner", + "$SYSTEMS_DIR\crafting", + "$SYSTEMS_DIR\follow", + "$SYSTEMS_DIR\gambling", + "$SYSTEMS_DIR\gameaction", + "$SYSTEMS_DIR\mulelogger", + "$SYSTEMS_DIR\pubjoin", + "$SYSTEMS_DIR\torch" +) +foreach ($dir in $dirs) { + if (-not (Test-Path $dir)) { + New-Item -ItemType Directory -Path $dir | Out-Null + } +} + +# Copy JSON files to data directory +Write-Host "Copying JSON files to data directory..." +Copy-IfNotExists "$SETUP_DIR\data\cdkeys.json" "$DATA_DIR\cdkeys.json" "cdkeys.json to data directory" +Copy-IfNotExists "$SETUP_DIR\data\patch.json" "$DATA_DIR\patch.json" "patch.json to data directory" +Copy-IfNotExists "$SETUP_DIR\data\profile.json" "$DATA_DIR\profile.json" "profile.json to data directory" +Copy-IfNotExists "$SETUP_DIR\data\schedules.json" "$DATA_DIR\schedules.json" "schedules.json to data directory" +Copy-IfNotExists "$SETUP_DIR\data\server.json" "$DATA_DIR\server.json" "server.json to data directory" +Write-Host "JSON files processed successfully!" +Write-Host "" + +# Copy log files to logs directory +Write-Host "Copying log files to logs directory..." +Copy-IfNotExists "$SETUP_DIR\logs\Console.rtf" "$LOGS_DIR\Console.rtf" "Console.rtf to logs directory" +Copy-IfNotExists "$SETUP_DIR\logs\exceptions.log" "$LOGS_DIR\exceptions.log" "exceptions.log to logs directory" +Copy-IfNotExists "$SETUP_DIR\logs\keyinfo.log" "$LOGS_DIR\keyinfo.log" "keyinfo.log to logs directory" +Write-Host "Log files processed successfully!" +Write-Host "" + +# Copy ini file to d2bs directory +Write-Host "Copying d2bs.ini to d2bs directory..." +Copy-IfNotExists "$SETUP_DIR\d2bs.ini" "$D2BS_DIR\d2bs.ini" "d2bs.ini to d2bs directory" +Write-Host "" + +# Copy system files to their respective directories +Write-Host "Copying system configuration files..." +Copy-IfNotExists "$SETUP_DIR\automule\MuleConfig.js" "$SYSTEMS_DIR\automule\config\MuleConfig.js" "MuleConfig.js to automule config directory" +Copy-IfNotExists "$SETUP_DIR\automule\TorchAnniMules.js" "$SYSTEMS_DIR\automule\config\TorchAnniMules.js" "TorchAnniMules.js to automule config directory" +Copy-IfNotExists "$SETUP_DIR\automule\StarterConfig.js" "$SYSTEMS_DIR\automule\config\StarterConfig.js" "StarterConfig.js to automule config directory" +Copy-IfNotExists "$SETUP_DIR\channel\ChannelConfig.js" "$SYSTEMS_DIR\channel\ChannelConfig.js" "ChannelConfig.js to channel directory" +Copy-IfNotExists "$SETUP_DIR\cleaner\CleanerConfig.js" "$SYSTEMS_DIR\cleaner\CleanerConfig.js" "CleanerConfig.js to cleaner directory" +Copy-IfNotExists "$SETUP_DIR\crafting\TeamsConfig.js" "$SYSTEMS_DIR\crafting\TeamsConfig.js" "TeamsConfig.js to crafting directory" +Copy-IfNotExists "$SETUP_DIR\follow\FollowConfig.js" "$SYSTEMS_DIR\follow\FollowConfig.js" "FollowConfig.js to follow directory" +Copy-IfNotExists "$SETUP_DIR\gambling\TeamsConfig.js" "$SYSTEMS_DIR\gambling\TeamsConfig.js" "TeamsConfig.js to gambling directory" +Copy-IfNotExists "$SETUP_DIR\gameaction\GameActionConfig.js" "$SYSTEMS_DIR\gameaction\GameActionConfig.js" "GameActionConfig.js to gameaction directory" +Copy-IfNotExists "$SETUP_DIR\mulelogger\LoggerConfig.js" "$SYSTEMS_DIR\mulelogger\LoggerConfig.js" "LoggerConfig.js to mulelogger directory" +Copy-IfNotExists "$SETUP_DIR\pubjoin\PubJoinConfig.js" "$SYSTEMS_DIR\pubjoin\PubJoinConfig.js" "PubJoinConfig.js to pubjoin directory" +Copy-IfNotExists "$SETUP_DIR\torch\FarmerConfig.js" "$SYSTEMS_DIR\torch\FarmerConfig.js" "FarmerConfig.js to torch directory" +Write-Host "System configuration files processed successfully!" +Write-Host "" + +# Copy custom config files to their respective directories +Write-Host "Copying custom configuration files..." +Copy-IfNotExists "$SETUP_DIR\config\_CustomConfig.js" "$CONFIG_DIR\_CustomConfig.js" "_CustomConfig.js to config directory" +Write-Host "" + +# Copy starter config files to their respective directories +Write-Host "Copying starter configuration files..." +Copy-IfNotExists "$SETUP_DIR\starter\AdvancedConfig.js" "$STARTER_DIR\AdvancedConfig.js" "AdvancedConfig.js to starter directory" +Copy-IfNotExists "$SETUP_DIR\starter\StarterConfig.js" "$STARTER_DIR\StarterConfig.js" "StarterConfig.js to starter directory" +Write-Host "" + +# Copy SoloPlay setup files to their respective directories +Write-Host "Copying SoloPlay configuration files..." +Copy-IfNotExists "$SOLOPLAY_SETUP_DIR\Settings.js" "$SOLOPLAY_SETTINGS_DIR\Settings.js" "Settings.js to SoloPlay Settings directory" +Copy-IfNotExists "$SOLOPLAY_SETUP_DIR\AdvancedSettings.js" "$SOLOPLAY_SETTINGS_DIR\AdvancedSettings.js" "AdvancedSettings.js to SoloPlay Settings directory" +Copy-IfNotExists "$SOLOPLAY_SETUP_DIR\StarterConfig.js" "$SOLOPLAY_OOG_DIR\StarterConfig.js" "StarterConfig.js to SoloPlay OOG directory" +Write-Host "" + +Write-Host "Setup files copied" \ No newline at end of file diff --git a/setup.bat b/setup.bat index d72b006a7..3c9fc3fcf 100644 --- a/setup.bat +++ b/setup.bat @@ -1,110 +1,3 @@ @echo off -echo Copying setup files to their respective directories... - -REM Check if git is available -git --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo WARNING: Git is not installed or not available in PATH - echo Please install Git and ensure it's in your PATH to use submodules - echo Continuing with file copying only... - echo. -) else ( - echo Git detected, initializing and updating submodules... - git submodule update --init --recursive - if %errorlevel% neq 0 ( - echo WARNING: Failed to update submodules - echo Please check your Git configuration and try again - echo. - ) else ( - echo Submodules updated successfully! - echo. - ) -) - -REM Set base directories -set "SETUP_DIR=+setup" -set "DATA_DIR=data" -set "LOGS_DIR=logs" -set "D2BS_DIR=d2bs" -set "STARTER_DIR=d2bs\kolbot\libs\starter" -set "SYSTEMS_DIR=d2bs\kolbot\libs\systems" -set "CONFIG_DIR=d2bs\kolbot\libs\config" -set "SOLOPLAY_DIR=d2bs\kolbot\libs\SoloPlay" -set "SOLOPLAY_SETUP_DIR=%SOLOPLAY_DIR%\+setup" -set "SOLOPLAY_SETTINGS_DIR=%SOLOPLAY_DIR%\Settings" -set "SOLOPLAY_OOG_DIR=%SOLOPLAY_DIR%\OOG" - -REM Create directories if they don't exist -if not exist "%DATA_DIR%\" mkdir "%DATA_DIR%" -if not exist "%LOGS_DIR%\" mkdir "%LOGS_DIR%" -if not exist "%D2BS_DIR%\" mkdir "%D2BS_DIR%" -if not exist "%STARTER_DIR%\" mkdir "%STARTER_DIR%" -if not exist "%SYSTEMS_DIR%\automule\config\" mkdir "%SYSTEMS_DIR%\automule\config" -if not exist "%SYSTEMS_DIR%\channel\" mkdir "%SYSTEMS_DIR%\channel" -if not exist "%SYSTEMS_DIR%\cleaner\" mkdir "%SYSTEMS_DIR%\cleaner" -if not exist "%SYSTEMS_DIR%\crafting\" mkdir "%SYSTEMS_DIR%\crafting" -if not exist "%SYSTEMS_DIR%\follow\" mkdir "%SYSTEMS_DIR%\follow" -if not exist "%SYSTEMS_DIR%\gambling\" mkdir "%SYSTEMS_DIR%\gambling" -if not exist "%SYSTEMS_DIR%\mulelogger\" mkdir "%SYSTEMS_DIR%\mulelogger" -if not exist "%SYSTEMS_DIR%\pubjoin\" mkdir "%SYSTEMS_DIR%\pubjoin" -if not exist "%SYSTEMS_DIR%\torch\" mkdir "%SYSTEMS_DIR%\torch" - -REM Function to copy file if it doesn't exist -REM Usage: call :CopyIfNotExists "source" "destination" "description" -goto :main - -:CopyIfNotExists -if exist "%~1" ( - if not exist "%~2" ( - copy "%~1" "%~2" >nul - echo Copied %~3 - ) else ( - echo %~3 already exists - skipping - ) -) -goto :eof - -:main -REM Copy JSON files to data directory -call :CopyIfNotExists "%SETUP_DIR%\data\cdkeys.json" "%DATA_DIR%\cdkeys.json" "cdkeys.json to data directory" -call :CopyIfNotExists "%SETUP_DIR%\data\patch.json" "%DATA_DIR%\patch.json" "patch.json to data directory" -call :CopyIfNotExists "%SETUP_DIR%\data\profile.json" "%DATA_DIR%\profile.json" "profile.json to data directory" -call :CopyIfNotExists "%SETUP_DIR%\data\schedules.json" "%DATA_DIR%\schedules.json" "schedules.json to data directory" -call :CopyIfNotExists "%SETUP_DIR%\data\server.json" "%DATA_DIR%\server.json" "server.json to data directory" - -REM Copy log files to logs directory -call :CopyIfNotExists "%SETUP_DIR%\logs\Console.rtf" "%LOGS_DIR%\Console.rtf" "Console.rtf to logs directory" -call :CopyIfNotExists "%SETUP_DIR%\logs\exceptions.log" "%LOGS_DIR%\exceptions.log" "exceptions.log to logs directory" -call :CopyIfNotExists "%SETUP_DIR%\logs\keyinfo.log" "%LOGS_DIR%\keyinfo.log" "keyinfo.log to logs directory" - -REM Copy ini file to d2bs directory -call :CopyIfNotExists "%SETUP_DIR%\d2bs.ini" "%D2BS_DIR%\d2bs.ini" "d2bs.ini to d2bs directory" - -REM Copy system files to their respective directories -call :CopyIfNotExists "%SETUP_DIR%\automule\MuleConfig.js" "%SYSTEMS_DIR%\automule\config\MuleConfig.js" "MuleConfig.js to automule config directory" -call :CopyIfNotExists "%SETUP_DIR%\automule\TorchAnniMules.js" "%SYSTEMS_DIR%\automule\config\TorchAnniMules.js" "TorchAnniMules.js to automule config directory" -call :CopyIfNotExists "%SETUP_DIR%\automule\StarterConfig.js" "%SYSTEMS_DIR%\automule\config\StarterConfig.js" "StarterConfig.js to automule config directory" -call :CopyIfNotExists "%SETUP_DIR%\channel\ChannelConfig.js" "%SYSTEMS_DIR%\channel\ChannelConfig.js" "ChannelConfig.js to channel directory" -call :CopyIfNotExists "%SETUP_DIR%\cleaner\CleanerConfig.js" "%SYSTEMS_DIR%\cleaner\CleanerConfig.js" "CleanerConfig.js to cleaner directory" -call :CopyIfNotExists "%SETUP_DIR%\crafting\TeamsConfig.js" "%SYSTEMS_DIR%\crafting\TeamsConfig.js" "TeamsConfig.js to crafting directory" -call :CopyIfNotExists "%SETUP_DIR%\follow\FollowConfig.js" "%SYSTEMS_DIR%\follow\FollowConfig.js" "FollowConfig.js to follow directory" -call :CopyIfNotExists "%SETUP_DIR%\gambling\TeamsConfig.js" "%SYSTEMS_DIR%\gambling\TeamsConfig.js" "TeamsConfig.js to gambling directory" -call :CopyIfNotExists "%SETUP_DIR%\gameaction\GameActionConfig.js" "%SYSTEMS_DIR%\gameaction\GameActionConfig.js" "GameActionConfig.js to gameaction directory" -call :CopyIfNotExists "%SETUP_DIR%\mulelogger\LoggerConfig.js" "%SYSTEMS_DIR%\mulelogger\LoggerConfig.js" "LoggerConfig.js to mulelogger directory" -call :CopyIfNotExists "%SETUP_DIR%\pubjoin\PubJoinConfig.js" "%SYSTEMS_DIR%\pubjoin\PubJoinConfig.js" "PubJoinConfig.js to pubjoin directory" -call :CopyIfNotExists "%SETUP_DIR%\torch\FarmerConfig.js" "%SYSTEMS_DIR%\torch\FarmerConfig.js" "FarmerConfig.js to torch directory" - -REM Copy custom config files to their respective directories -call :CopyIfNotExists "%SETUP_DIR%\config\_CustomConfig.js" "%CONFIG_DIR%\_CustomConfig.js" "_CustomConfig.js to config directory" - -REM Copy starter config files to their respective directories -call :CopyIfNotExists "%SETUP_DIR%\starter\AdvancedConfig.js" "%STARTER_DIR%\AdvancedConfig.js" "AdvancedConfig.js to starter directory" -call :CopyIfNotExists "%SETUP_DIR%\starter\StarterConfig.js" "%STARTER_DIR%\StarterConfig.js" "StarterConfig.js to starter directory" - -REM Copy SoloPlay setup files to their respective directories -call :CopyIfNotExists "%SOLOPLAY_SETUP_DIR%\Settings.js" "%SOLOPLAY_SETTINGS_DIR%\Settings.js" "Settings.js to SoloPlay Settings directory" -call :CopyIfNotExists "%SOLOPLAY_SETUP_DIR%\AdvancedSettings.js" "%SOLOPLAY_SETTINGS_DIR%\AdvancedSettings.js" "AdvancedSettings.js to SoloPlay Settings directory" -call :CopyIfNotExists "%SOLOPLAY_SETUP_DIR%\StarterConfig.js" "%SOLOPLAY_OOG_DIR%\StarterConfig.js" "StarterConfig.js to SoloPlay OOG directory" - -echo Setup files copied successfully! +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0/+setup/setup.ps1" pause \ No newline at end of file From 494f6df3c045f1a233d21a0e7170903f5bd4d726 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:23:30 -0400 Subject: [PATCH 596/758] Add cache timeout constant to QuestData - Introduces a CACHE_TIME constant for quest data refresh intervals, replacing the hardcoded value. - Increases the timeout from 500ms to 5minutes --- d2bs/kolbot/libs/SoloPlay | 2 +- d2bs/kolbot/libs/core/GameData/QuestData.js | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 43222c137..257428298 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 43222c1379e9104b090e2da93b25ed6f0d4445bd +Subproject commit 257428298ec8b184f20770d03125ef8e081b2eac diff --git a/d2bs/kolbot/libs/core/GameData/QuestData.js b/d2bs/kolbot/libs/core/GameData/QuestData.js index ccb709f36..203a097b3 100644 --- a/d2bs/kolbot/libs/core/GameData/QuestData.js +++ b/d2bs/kolbot/libs/core/GameData/QuestData.js @@ -10,6 +10,7 @@ * @todo Fill out more, items for quests, npcs, etc */ const QuestData = (function () { + const CACHE_TIME = Time.minutes(5); let _lastRefresh = 0; /** @type {Set} */ @@ -19,10 +20,12 @@ sdk.quest.id.SpokeToJerhyn, sdk.quest.id.AbleToGotoActIII, sdk.quest.id.SpokeToHratli, sdk.quest.id.AbleToGotoActIV, sdk.quest.id.SpokeToTyrael, sdk.quest.id.AbleToGotoActV, - ].forEach(questId => _specials.add(questId)); + ].forEach(function (questId) { + _specials.add(questId); + }); const refresh = function () { - if (getTickCount() - _lastRefresh > 500) { + if (getTickCount() - _lastRefresh > CACHE_TIME) { Packet.questRefresh(); _lastRefresh = getTickCount(); } @@ -136,7 +139,7 @@ sdk.quest.id.RescueonMountArreat, sdk.quest.id.PrisonofIce, sdk.quest.id.BetrayalofHarrogath, sdk.quest.id.RiteofPassage, sdk.quest.id.EyeofDestruction, ] - ].forEach((questIds, act) => { + ].forEach(function (questIds, act) { for (let questId of questIds) { questMap.set(questId, new Quest(questId, act + 1)); } From b11f7890045a368af66901a71a2709d8efa394c4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:30:56 -0400 Subject: [PATCH 597/758] Add mainScript param to Common\Tools#togglePause and update usage - Modified Common.Tools.togglePause to accept a mainScript parameter, defaulting to 'default.dbj'. - Updated MapToolsThread.js to call togglePause with the correct script path. --- d2bs/kolbot/libs/core/Common/Tools.js | 10 +++++++--- d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index 8ab1007d1..c1f4550f5 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -78,16 +78,20 @@ Config.QuitList = temp.slice(0); }, - togglePause: function () { + /** + * @param {string} mainScript + * @returns {boolean} + */ + togglePause: function (mainScript = "default.dbj") { for (let curr of this.pauseScripts) { let script = getScript(curr); if (script) { if (script.running) { - curr === "default.dbj" && console.log("ÿc1Pausing."); + curr === mainScript && console.log("ÿc1Pausing."); script.pause(); } else { - if (curr === "default.dbj") { + if (curr === mainScript) { console.log("ÿc2Resuming."); } script.resume(); diff --git a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js index 9c242d3e8..d7628dec2 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapToolsThread.js @@ -69,7 +69,7 @@ function main () { const keyEvent = function (key) { switch (key) { case sdk.keys.PauseBreak: // pause main.dbj - Common.Toolsthread.togglePause(); + Common.Toolsthread.togglePause("libs/manualplay/main.js"); break; case sdk.keys.Delete: // quit current game From 9ca8e25f178c464318b14130242531f6aaf02645 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:26:02 -0400 Subject: [PATCH 598/758] Handle singleplayer check for cubing ladder recipes --- d2bs/kolbot/libs/core/Cubing.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 82f598312..1c8693d4a 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -594,7 +594,8 @@ const Cubing = { break; case Recipe.Unique.Weapon.ToElite: // Ladder only case Recipe.Unique.Armor.ToElite: // Ladder only - if (!me.ladder) continue; + // not ladder and online - we can do these recipes on single player + if (!me.ladder && me.realm) continue; break; case Recipe.Reroll.HighRare: recipeObj.Enabled = false; From f3633a12767ea9663d5797025ee9fea5f2f264a6 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 01:09:51 -0400 Subject: [PATCH 599/758] Create copilot-instructions.md --- .github/copilot-instructions.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..1e8f7b10c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,9 @@ +# Project general coding standards for JavaScript +- Prefer function syntax over arrow syntax +- No template literals +- Use `let` instead of `const` in for...of and for...in declarations +- No use of computed property names in object initialzer +- No setTimeout or setInterval +- Always use JSDOC +- No var declarations, always use let or const +- No optional chaining \ No newline at end of file From d0fd5cd1bb440e364e9e88884cd42062c397c314 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 01:10:28 -0400 Subject: [PATCH 600/758] Fix: Add missing Cubing ingredients case for `Thul` --- d2bs/kolbot/libs/core/Cubing.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 1c8693d4a..3c8c9a89d 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -263,6 +263,7 @@ const Recipe = { case sdk.items.runes.Tal: case sdk.items.runes.Ral: case sdk.items.runes.Ort: + case sdk.items.runes.Thul: return [keyItem - 1, keyItem - 1, keyItem - 1]; case sdk.items.runes.Amn: // thul->amn return [sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.runes.Thul, sdk.items.gems.Chipped.Topaz]; From f555b9ccc8eb2b02e8933bfd76a985b02bb9ebc8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 01:50:25 -0400 Subject: [PATCH 601/758] Fix typo in LadderOverride config option - Corrected the spelling of 'LadderOveride' to 'LadderOverride' in Config.js and updated all references and type definitions to match. --- d2bs/kolbot/libs/core/Config.js | 2 +- d2bs/kolbot/libs/core/GameData/RuneData.js | 2 +- d2bs/kolbot/sdk/types/Config.d.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 0c53b48c9..9621cbb55 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -312,7 +312,7 @@ let Config = { */ Runewords: [], KeepRunewords: [], - LadderOveride: false, + LadderOverride: false, Gamble: false, GambleItems: [], GambleGoldStart: 0, diff --git a/d2bs/kolbot/libs/core/GameData/RuneData.js b/d2bs/kolbot/libs/core/GameData/RuneData.js index 503c9714d..ef3de3a65 100644 --- a/d2bs/kolbot/libs/core/GameData/RuneData.js +++ b/d2bs/kolbot/libs/core/GameData/RuneData.js @@ -84,7 +84,7 @@ // not ladder restricted or we are on ladder if (!this._ladder || ladder) return false; // ladder restricted and we have enabled ladder override - if (Config.LadderOveride) return false; + if (Config.LadderOverride) return false; // ladder restricted return true; }; diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 3632b8b74..798a24c84 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -164,6 +164,7 @@ declare global { MakeRunewords: boolean; Runewords: any[][]; KeepRunewords: any[]; + LadderOverride: boolean; Gamble: boolean; GambleItems: any[]; GambleGoldStart: number; From dc374342d38489242ef12d1e6f1e4b1e6d14cf68 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 02:15:09 -0400 Subject: [PATCH 602/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 257428298..fc06debc8 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 257428298ec8b184f20770d03125ef8e081b2eac +Subproject commit fc06debc8e45a3d832e789d6ed63c7020158a3d1 From c130ff9b983b93d7cf656bf93aed675458cb0946 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 03:27:46 -0400 Subject: [PATCH 603/758] Refactor deepMerge to perform mutable merge - Changed the deepMerge function to modify the target object directly instead of returning a new merged object. The previous immutable merge logic is now commented out, and the function now returns the mutated target. --- d2bs/kolbot/libs/globals.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/globals.js b/d2bs/kolbot/libs/globals.js index e520b0c03..00da4f2b4 100644 --- a/d2bs/kolbot/libs/globals.js +++ b/d2bs/kolbot/libs/globals.js @@ -309,19 +309,36 @@ function deepMerge(target, source) { return target; } - let result = Object.assign({}, target); - + // Immutable merge + // let result = Object.assign({}, target); + + // for (let key in source) { + // if (source.hasOwnProperty(key)) { + // if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) { + // // Recursively merge nested objects + // result[key] = deepMerge(result[key] || {}, source[key]); + // } else { + // // Direct assignment for primitives and arrays + // result[key] = source[key]; + // } + // } + // } + + // return result; + + // Mutable merge - this modifies the target object for (let key in source) { if (source.hasOwnProperty(key)) { if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) { - // Recursively merge nested objects - result[key] = deepMerge(result[key] || {}, source[key]); + if (!target[key] || typeof target[key] !== "object" || Array.isArray(target[key])) { + target[key] = {}; + } + deepMerge(target[key], source[key]); } else { - // Direct assignment for primitives and arrays - result[key] = source[key]; + target[key] = source[key]; } } } - return result; + return target; } From 2293be3c24db9f8e2571bd3714534a4f9f44bd9e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 03:29:34 -0400 Subject: [PATCH 604/758] Refactor `Skill.haveTK` definition to use getter declaration --- d2bs/kolbot/libs/core/Skill.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 17029a7f4..c12cb868a 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -750,15 +750,11 @@ } } }, - }; - Object.defineProperties(Skill, { - haveTK: { - get: function () { - return Skill.canUse(sdk.skills.Telekinesis); - }, - }, - }); + get haveTK () { + return Skill.canUse(sdk.skills.Telekinesis); + } + }; // export to the global scope global.Skill = Skill; From 0540c0f37ee3477177d4861c4d53c1d819002c92 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 12:06:49 -0400 Subject: [PATCH 605/758] Update globals.d.ts --- d2bs/kolbot/libs/OOG.js | 8 ++- d2bs/kolbot/sdk/globals.d.ts | 97 ++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 7eb9223bc..0e5137bfa 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1291,14 +1291,18 @@ includeIfNotIncluded("core/Me.js"); return rval; }, + /** + * @param {number} [len] + * @returns {string} + */ randomNumberString: function (len) { !len && (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; diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 9142a6935..d9e26b26c 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1392,6 +1392,103 @@ declare global { receiveCopyData(mode: number, msg: string | object): void, randomString(len?: number, useNumbers?: boolean): string, randomNumberString(len?: number): string, + /** + * Cache for waypoints by character name and difficulty + */ + waypointCache: { [charName: string]: any[] }; + + /** + * Handles script message events (object version) + * @param msg - The message object + */ + scriptMsgEvent(msg: object): void; + + /** + * Handles copy data event + * @param mode - The mode + * @param msg - The message (object or string) + */ + receiveCopyData(mode: number, msg: object | string): void; + + /** + * Returns a random string of given length + * @param len - Length of string + * @param useNumbers - Whether to use numbers + */ + randomString(len?: number, useNumbers?: boolean): string; + + /** + * Returns a random number string of given length + * @param len - Length of string + */ + randomNumberString(len?: number): string; + + /** + * Formats a timer string from a tick value + * @param tick - The tick value + */ + timer(tick: number): string; + + /** + * Waits for a location to change or timeout + * @param time - Timeout in ms + * @param location - Location to wait for + */ + locationTimeout(time: number, location: number): boolean; + + /** + * Sets the next game name in the stats + * @param gameInfo - Game info object + */ + setNextGame(gameInfo?: { gameName?: string }): void; + + /** + * Updates the game count and performs relog actions + */ + updateCount(): void; + + /** + * Handles script message events (string version) + * @param msg - The message string + */ + scriptMsgEvent(msg: string): void; + + /** + * Handles copy data event + * @param mode - The mode + * @param msg - The message (object or string) + */ + receiveCopyData(mode: number, msg: object | string): void; + + /** + * Returns a random string of given length + * @param len - Length of string + * @param useNumbers - Whether to use numbers + */ + randomString(len?: number, useNumbers?: boolean): string; + + /** + * Returns a random number string of given length + * @param len - Length of string + */ + randomNumberString(len?: number): string; + + /** + * Location event handlers + */ + LocationEvents: { + selectDifficultySP(): boolean; + loginError(): void; + charSelectError(): boolean; + realmDown(): void; + waitingInLine(): void; + gameDoesNotExist(): void; + unableToConnect(): void; + openCreateGameWindow(): boolean; + openJoinGameWindow(): void; + login(otherMultiCheck?: boolean): boolean; + otherMultiplayerSelect(): void; + }; } const Starter: StarterInterface; From 342f77a1e1cbfc97b1a54831bc74cafe250c62fc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 17:53:58 -0400 Subject: [PATCH 606/758] Add Biome config and TypeScript standards, update vscode settings - Added biome.json for Biome formatting and linting, and .github/instructions/typescript.instructions.md for TypeScript coding standards. - Updated .vscode/settings.json to use Biome for TypeScript files and improved code actions. - Enhanced d2bs/kolbot/sdk/globals.d.ts with consistent formatting and type definitions. - Updated dependencies to include @biomejs/biome. --- .github/copilot-instructions.md | 4 + .../instructions/typescript.instructions.md | 6 + .vscode/settings.json | 18 +- biome.json | 39 + d2bs/kolbot/sdk/globals.d.ts | 774 ++++++++++-------- package-lock.json | 236 ++++++ package.json | 1 + 7 files changed, 714 insertions(+), 364 deletions(-) create mode 100644 .github/instructions/typescript.instructions.md create mode 100644 biome.json diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1e8f7b10c..1b122be24 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,3 +1,7 @@ +--- +applyTo: "**/*.js" +--- + # Project general coding standards for JavaScript - Prefer function syntax over arrow syntax - No template literals diff --git a/.github/instructions/typescript.instructions.md b/.github/instructions/typescript.instructions.md new file mode 100644 index 000000000..0251b0367 --- /dev/null +++ b/.github/instructions/typescript.instructions.md @@ -0,0 +1,6 @@ +--- +applyTo: "**/*.ts" +--- + +# Project general coding standards for TypeScript +- Prefer for...of over forEach \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b128d3e28..11f167797 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,21 @@ ], "editor.tabSize": 2, "editor.detectIndentation": false, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" + "[javascript]": { + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + } }, + "[typescript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "source.removeUnusedImports": "always", + "quickfix.biome": "explicit", + "source.fixAll.ts": "explicit", + "source.fixAll.eslint": "never", + "source.fixAll.biome": "explicit", + } + } } \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..7a75d5b0f --- /dev/null +++ b/biome.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.1.2/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": true, + "includes": ["**/*.ts"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "warn" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "lineWidth": 120 + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index d9e26b26c..757661a09 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -20,8 +20,8 @@ /// declare global { - type IncludePath = import('./types/include-paths').IncludePath; - type KolbotScript = import('./types/kolbot-scripts').KolbotScript; + type IncludePath = import("./types/include-paths").IncludePath; + type KolbotScript = import("./types/kolbot-scripts").KolbotScript; interface Error { fileName: string; @@ -54,9 +54,9 @@ declare global { flat(depth?: number): T[]; compactMap(callback: (value: T, index: number, obj: T[]) => any, thisArg?: any): any[]; filterNull(): T[]; - filterHighDistance(step: number): any[] - isEqual(t: T[]): boolean - remove(val: T): T[] + filterHighDistance(step: number): any[]; + isEqual(t: T[]): boolean; + remove(val: T): T[]; random(): T; shuffle(): T[]; /** @@ -105,11 +105,11 @@ declare global { } interface String { - lcsGraph(compareToThis: string): { a: string, b: string, graph: Uint16Array[]} - diffCount(a:string): number; + lcsGraph(compareToThis: string): { a: string; b: string; graph: Uint16Array[] }; + diffCount(a: string): number; startsWith(a: string): boolean; capitalize(downCase: boolean): string; - format(...pairs: Array): string; + format(...pairs: Array): string; padStart(targetLength: number, padString: string): string; padEnd(targetLength: number, padString: string): string; at(index: number): string | undefined; @@ -117,7 +117,7 @@ declare global { } interface StringConstructor { - static isEqual(str1: string, str2: string, caseSensitive?: boolean): boolean; + isEqual(str1: string, str2: string, caseSensitive?: boolean): boolean; } interface ObjectConstructor { @@ -160,25 +160,24 @@ declare global { * @returns The updated env object for chaining */ update(settings: Record): Env; - + /** * Any additional custom properties */ [key: string]: any; } - + /** * Global environment object * This object is lazily loaded when first accessed. */ const env: Env; - class ScriptError extends Error { - } + class ScriptError extends Error {} type Act = 1 | 2 | 3 | 4 | 5; - type actType = { initialized: boolean, spot: { [data: string]: [number, number] } }; - type potType = 'hp' | 'mp' | 'rv'; + type actType = { initialized: boolean; spot: { [data: string]: [number, number] } }; + type potType = "hp" | "mp" | "rv"; class Hook { color: number; @@ -196,7 +195,7 @@ declare global { * The z-order of the Hook (what it covers up and is covered by). */ zorder: number; - + /** * How much of the controls underneath the Hook should show through. */ @@ -213,7 +212,17 @@ declare global { } class Line extends Hook { - constructor(x: number, y: number, x2: number, y2: number, color: number, visible: boolean, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); + constructor( + x: number, + y: number, + x2: number, + y2: number, + color: number, + visible: boolean, + automap: boolean, + ClickHandler?: Function, + HoverHandler?: Function, + ); /** * The first x coordinate of the Line. */ @@ -245,7 +254,7 @@ declare global { align: number, automap: boolean, ClickHandler?: Function, - HoverHandler?: Function + HoverHandler?: Function, ); text: string; /** @@ -260,7 +269,18 @@ declare global { } class Box extends Hook { - constructor(x: number, y: number, xsize: number, ysize: number, color: number, opacity: number, align: number, automap: boolean, ClickHandler?: Function, HoverHandler?: Function); + constructor( + x: number, + y: number, + xsize: number, + ysize: number, + color: number, + opacity: number, + align: number, + automap: boolean, + ClickHandler?: Function, + HoverHandler?: Function, + ); /** * The x coordinate (left) of the Box. */ @@ -282,8 +302,7 @@ declare global { ysize: number; } - class Frame extends Box { - } + class Frame extends Box {} /** * @todo Figure out what each of these actually returns to properly document them @@ -323,23 +342,23 @@ declare global { const FILE_APPEND: 2; const FileTools: { - readText(filename: string) - writeText(filename: string, data: string) - appendText(filename: string, data: string) + readText(filename: string); + writeText(filename: string, data: string); + appendText(filename: string, data: string); exists(filename: string): boolean; remove(filename: string): boolean; - } + }; - function getCollision(area: number, x: number, y: number, x2: number, y2: number) + function getCollision(area: number, x: number, y: number, x2: number, y2: number); function getDistance(unit: PathNode, other: PathNode): number; function getDistance(unit: PathNode, x: number, y: number): number; /************************************* - * Unit description * - * Needs expansion * - *************************************/ - + * Unit description * + * Needs expansion * + *************************************/ + type UnitType = 0 | 1 | 2 | 3 | 4 | 5; interface Unit { readonly type: UnitType; @@ -384,7 +403,7 @@ declare global { readonly poisonRes: number; readonly hpPercent: number; readonly prettyPrint: string; - + // D2BS built in getNext(): Unit | false; interact(): boolean; @@ -406,21 +425,16 @@ declare global { getSkill(skillId: number, type: 0 | 1, item?: ItemUnit): number; getStat(index: number, subid?: number, extra?: number): number; getState(index: number, subid?: number): boolean; - getQuest(quest: number, subid: number): number + getQuest(quest: number, subid: number): number; getParent(): Unit | string; getMinionCount(): number; - // additions from kolbot + // additions from kolbot getStatEx(one: number, sub?: number): number; getItemsEx(classId?: number, mode?: number, unitId?: number): ItemUnit[]; getItemsEx(name?: string, mode?: number, unitId?: number): ItemUnit[]; inArea(area: number): boolean; - checkForMobs(givenSettings: { - range?: number; - count?: number; - coll?: number; - spectype: number - }): boolean + checkForMobs(givenSettings: { range?: number; count?: number; coll?: number; spectype: number }): boolean; } type PlayerType = 0; @@ -430,8 +444,7 @@ declare global { } type MonsterType = 1; - interface Monster extends Unit { - } + interface Monster extends Unit {} class Monster extends Unit { public type: MonsterType; @@ -485,7 +498,7 @@ declare global { readonly isEnchantable: boolean; getEnchant(type: number): boolean; - hasEnchant(...enchants: number): boolean + hasEnchant(...enchants: number): boolean; } class NPCUnit extends Unit { @@ -494,15 +507,14 @@ declare global { openMenu(): boolean; useMenu(): boolean; - startTrade: (mode: any) => (any | boolean); + startTrade: (mode: any) => any | boolean; } class MercUnit extends Monster { - equip(destination: number | undefined, item: ItemUnit) + equip(destination: number | undefined, item: ItemUnit); } - interface ObjectUnit extends Unit { - } + interface ObjectUnit extends Unit {} type ObjectType = 2; class ObjectUnit extends Unit { @@ -549,7 +561,7 @@ declare global { // additional, not from d2bs readonly identified: boolean; - readonly isEquipped: boolean + readonly isEquipped: boolean; readonly dexreq: number; readonly strreq: number; readonly charclass: number; @@ -563,7 +575,7 @@ declare global { readonly runeword: boolean; readonly questItem: boolean; readonly ethereal: boolean; - readonly twoHanded: boolean + readonly twoHanded: boolean; readonly oneOrTwoHanded: boolean; readonly strictlyTwoHanded: boolean; readonly sellable: boolean; @@ -596,8 +608,8 @@ declare global { drop(): boolean; equip(slot?: number): boolean; buy(shift?: boolean, gamble?: boolean): boolean; - sellOrDrop():void - toCursor():boolean + sellOrDrop(): void; + toCursor(): boolean; use(): boolean; } @@ -608,14 +620,14 @@ declare global { } type GetOwnedSettings = { - itemType?: number, - classid?: number, - mode?: number, - quality?: number, - sockets?: number, - location?: number, - ethereal?: boolean, - cb?: (item: ItemUnit) => boolean, + itemType?: number; + classid?: number; + mode?: number; + quality?: number; + sockets?: number; + location?: number; + ethereal?: boolean; + cb?: (item: ItemUnit) => boolean; }; interface ItemInfo { @@ -630,7 +642,7 @@ declare global { } interface MeType extends Unit { - public type: PlayerType; + type: PlayerType; readonly account: string; readonly charname: string; readonly diff: 0 | 1 | 2; @@ -642,7 +654,7 @@ declare global { readonly ping: number; readonly fps: number; readonly locale: number; - readonly playertype: 0|1; + readonly playertype: 0 | 1; readonly realm: string; readonly realmshort: string; readonly mercrevivecost: number; @@ -664,7 +676,7 @@ declare global { readonly gameserverip: string; readonly itemcount: number; readonly classid: 0 | 1 | 2 | 3 | 4 | 5 | 6; - readonly weaponswitch: 0|1; + readonly weaponswitch: 0 | 1; readonly gameReady: boolean; blockMouse: boolean; blockKeys: boolean; @@ -815,7 +827,7 @@ declare global { haveWaypoint(area: number): boolean; accessToAct(act: number): boolean; inArea(area: number): boolean; - haveSome(arg0: { name: number; equipped: boolean; }[]): any; + haveSome(arg0: { name: number; equipped: boolean }[]): any; findItem(id?: number | string, mode?: number, location?: number, quality?: number): ItemUnit | boolean; findItems(id?: number | string, mode?: number, location?: number): ItemUnit[]; checkItem(itemInfo: { @@ -826,7 +838,7 @@ declare global { ethereal?: boolean; name?: string | number; equipped?: boolean | number; - }): {have: boolean; item: ItemUnit | null}; + }): { have: boolean; item: ItemUnit | null }; findFirst(itemInfo: ItemInfo): { have: boolean; item: ItemUnit | null }; usingShield(): boolean; checkQuest(questId: number, state: number): boolean; @@ -841,7 +853,7 @@ declare global { clearBelt(): boolean; } - const me: MeType + const me: MeType; interface PathNode { x: number; y: number; @@ -851,7 +863,7 @@ declare global { readonly distance: number; /** * Distance from 'unit' to this node - * @param unit + * @param unit */ distanceTo(unit: Unit): number; /** @@ -860,34 +872,42 @@ declare global { getWalkDistance(): number; /** * Walk Distance from 'node' to this node - * @param node + * @param node */ getWalkDistanceTo(node: PathNode, area?: number): number; - mobCount(givenSettings: { range?: number, coll?: number, type?: number, ignoreClassids?: number[] }): number; + mobCount(givenSettings: { range?: number; coll?: number; type?: number; ignoreClassids?: number[] }): number; } - function getUnit(type: 4, name?: string, mode?: number, unitId?: number): ItemUnit - function getUnit(type: 4, classId?: number, mode?: number, unitId?: number): ItemUnit - function getUnit(type: 1, name?: string, mode?: number, unitId?: number): Monster - function getUnit(type: 1, classId?: number, mode?: number, unitId?: number): Monster - function getUnit(type?: number, name?: string, mode?: number, unitId?: number): Unit - function getUnit(type?: number, classId?: number, mode?: number, unitId?: number): Unit - - function getPath(area: number, fromX: number, fromY: number, toX: number, toY: number, reductionType: 0 | 1 | 2, radius: number): PathNode[] | false - function getCollision(area: number, x: number, y: number) - function getMercHP(): number - function getCursorType(type: 1 | 3 | 6): boolean - function getCursorType(): number - function getSkillByName(name: string): number - function getSkillById(id: number): string - function getLocaleString(id: number): string + function getUnit(type: 4, name?: string, mode?: number, unitId?: number): ItemUnit; + function getUnit(type: 4, classId?: number, mode?: number, unitId?: number): ItemUnit; + function getUnit(type: 1, name?: string, mode?: number, unitId?: number): Monster; + function getUnit(type: 1, classId?: number, mode?: number, unitId?: number): Monster; + function getUnit(type?: number, name?: string, mode?: number, unitId?: number): Unit; + function getUnit(type?: number, classId?: number, mode?: number, unitId?: number): Unit; + + function getPath( + area: number, + fromX: number, + fromY: number, + toX: number, + toY: number, + reductionType: 0 | 1 | 2, + radius: number, + ): PathNode[] | false; + function getCollision(area: number, x: number, y: number); + function getMercHP(): number; + function getCursorType(type: 1 | 3 | 6): boolean; + function getCursorType(): number; + function getSkillByName(name: string): number; + function getSkillById(id: number): string; + function getLocaleString(id: number): string; // Never seen in the wild, not sure about arguments - function getTextSize(name: string, size: number) - function getThreadPriority(): number - function getUIFlag(flag: number): boolean - function getTradeInfo(mode: 0 | 1 | 2): boolean - function getWaypoint(id: number, noCache?: boolean): boolean + function getTextSize(name: string, size: number); + function getThreadPriority(): number; + function getUIFlag(flag: number): boolean; + function getTradeInfo(mode: 0 | 1 | 2): boolean; + function getWaypoint(id: number, noCache?: boolean): boolean; class Script { running: boolean; @@ -904,8 +924,8 @@ declare global { send(): void; } - function getScript(name?: string | boolean): Script | false - function getScripts(): Script | false + function getScript(name?: string | boolean): Script | false; + function getScripts(): Script | false; class Room { area: number; @@ -919,14 +939,14 @@ declare global { getNext(): Room | false; getNearby(): Room[]; - isInRoom(unit: PathNode):boolean - isInRoom(x:number, y:number):boolean + isInRoom(unit: PathNode): boolean; + isInRoom(x: number, y: number): boolean; } - function getRoom(area: number, x: number, y: number): Room | false - function getRoom(x: number, y: number): Room | false - function getRoom(area: number): Room | false - function getRoom(): Room | false + function getRoom(area: number, x: number, y: number): Room | false; + function getRoom(x: number, y: number): Room | false; + function getRoom(area: number): Room | false; + function getRoom(): Room | false; class Party { x: number; @@ -944,7 +964,7 @@ declare global { getNext(): Party | false; } - function getParty(unit?: Unit): Party | false + function getParty(unit?: Unit): Party | false; class PresetUnit { id: number; @@ -956,28 +976,28 @@ declare global { readonly distance: number; getNext(): PresetUnit | false; - realCoords(): { id: number, area: number, x: number, y: number }; + realCoords(): { id: number; area: number; x: number; y: number }; } type PresetObject = { - area: number, - id: number, - type: number, - x: number, - y: number, - } + area: number; + id: number; + type: number; + x: number; + y: number; + }; - function getPresetUnit(area?: number, objType?: number, classid?: number): PresetUnit | false - function getPresetUnit(area?: number, objType?: 2, classid?: number): PresetUnit | false - function getPresetUnits(area?: number, objType?: number, classid?: number): PresetUnit[] | false + function getPresetUnit(area?: number, objType?: number, classid?: number): PresetUnit | false; + function getPresetUnit(area?: number, objType?: 2, classid?: number): PresetUnit | false; + function getPresetUnits(area?: number, objType?: number, classid?: number): PresetUnit[] | false; interface Exit extends Object { - x: number, - y: number, - type: number, - target: number, - tileid: number, - level: number, + x: number; + y: number; + type: number; + target: number; + tileid: number; + level: number; } class Area { @@ -992,9 +1012,9 @@ declare global { getNext(): Area | false; } - function getArea(id?: number): Area | false - function getBaseStat(table: string, row: number, column: string | number): number | string - function getBaseStat(row: number, column: string): number | string + function getArea(id?: number): Area | false; + function getBaseStat(table: string, row: number, column: string | number): number | string; + function getBaseStat(row: number, column: string): number | string; /** * @todo get a better understanding of Control @@ -1064,16 +1084,16 @@ declare global { getText(): string[]; } - type Profile = { - type: number, - ip: number, - username: string, - gateway: string, - character: string, - difficulty: number, - maxloginTime: number, - maxCharacterSelectTime: number, - } + type D2BotProfile = { + type: number; + ip: number; + username: string; + gateway: string; + character: string; + difficulty: number; + maxloginTime: number; + maxCharacterSelectTime: number; + }; function Profile(): Profile; class SQLite { @@ -1090,87 +1110,108 @@ declare global { getColumnValue(index: number): any; } - function getControl(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control | false - function getControls(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control[] - function getPlayerFlag(meGid: number, otherGid: number, type: number): boolean - function getTickCount(): number - function getInteractedNPC(): NPCUnit | false - function getIsTalkingNPC(): boolean - function getDialogLines(): { handler() }[] | false - function print(what: string): void - function stringToEUC(arg: any): [] - function utf8ToEuc(arg: any): [] - function delay(ms: number): void - function load(file: string): boolean - function isIncluded(file: IncludePath): boolean - function include(file: IncludePath): boolean - function stacktrace(): true - function rand(from: number, to: number): number - function copy(what: string): void - function paste(): string + function getControl(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control | false; + function getControls(type?: number, x?: number, y?: number, xsize?: number, ysize?: number): Control[]; + function getPlayerFlag(meGid: number, otherGid: number, type: number): boolean; + function getTickCount(): number; + function getInteractedNPC(): NPCUnit | false; + function getIsTalkingNPC(): boolean; + function getDialogLines(): { handler() }[] | false; + function print(what: string): void; + function stringToEUC(arg: any): []; + function utf8ToEuc(arg: any): []; + function delay(ms: number): void; + function load(file: string): boolean; + function isIncluded(file: IncludePath): boolean; + function include(file: IncludePath): boolean; + function stacktrace(): true; + function rand(from: number, to: number): number; + function copy(what: string): void; + function paste(): string; function sendCopyData(noIdea: null, handle: number | string, mode: number, data: string): void; - function sendDDE() - function keystate() + function sendDDE(); + function keystate(); type eventName = - | 'itemaction' - | 'gameevent' - | 'copydata' - | 'chatmsg' - | 'chatinput' - | 'whispermsg' - | 'chatmsgblocker' - | 'chatinputblocker' - | 'whispermsgblocker' - | 'mousemove' - | 'ScreenHookHover' - | 'mouseclick' - | 'keyup' - | 'keydownblocker' - | 'keydown' - | 'memana' - | 'melife' - | 'playerassign' - | 'ScreenHookClick' - | 'Command' - | 'scriptmsg' - | 'gamepacket' - | 'gamepacketsent' - | 'realmpacket'; - - function addEventListener(eventType: 'realmpacket', callback: ((bytes: ArrayBufferLike) => boolean)): void - function addEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void - function addEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void - function addEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void - function addEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void - function addEventListener(eventType: 'keyup' | 'keydown' | 'keydownblocker', callback: ((key: number|string) => void)): void - function addEventListener(eventType: 'chatmsg' | 'chatinput' | 'whispermsg', callback: ((nick: string, msg: string) => void)): void - function addEventListener(eventType: 'chatmsgblocker' | 'chatinputblocker' | 'whispermsgblocker', callback: ((arg1: string, arg2: string) => void)): void - function addEventListener(eventType: 'mousemove', callback: ((arg1: string, arg2: string) => void)): void - function addEventListener(eventType: 'ScreenHookHover', callback: ((arg1: string, arg2: string) => void)): void - function addEventListener(eventType: 'mouseclick', callback: ((arg1: string, arg2: string, arg3: string, arg4: string) => void)): void - function addEventListener(eventType: 'memana' | 'melife', callback: ((arg1: string, arg2: string) => void)): void - function addEventListener(eventType: 'playerassign', callback: ((arg1: string, arg4: string) => void)): void - function addEventListener(eventType: 'ScreenHookClick', callback: ((arg1: any, arg2: any, arg3: any, arg4: any) => void)): void - function addEventListener(eventType: eventName, callback: ((...args: any) => void)): void - - function removeEventListener(eventType: 'gamepacket', callback: ((bytes: ArrayBufferLike) => boolean)): void - function removeEventListener(eventType: 'scriptmsg', callback: ((data: string | object | number) => void)): void - function removeEventListener(eventType: 'copydata', callback: ((mode: number, msg: string) => void)): void - function removeEventListener(eventType: 'itemaction', callback: ((gid: number, mode?: number, code?: string, global?: true) => void)): void - function removeEventListener(eventType: 'keyup' | 'keydown', callback: ((key: number) => void)): void - function removeEventListener(eventType: 'chatmsg', callback: ((nick: string, msg: string) => void)): void - function removeEventListener(eventType: eventName, callback: ((...args: any) => void)): void - - function clearEvent() - function clearAllEvents() - function js_strict() - function version(): number - function scriptBroadcast(what: string | object): void - function sqlite_version() - function sqlite_memusage() + | "itemaction" + | "gameevent" + | "copydata" + | "chatmsg" + | "chatinput" + | "whispermsg" + | "chatmsgblocker" + | "chatinputblocker" + | "whispermsgblocker" + | "mousemove" + | "ScreenHookHover" + | "mouseclick" + | "keyup" + | "keydownblocker" + | "keydown" + | "memana" + | "melife" + | "playerassign" + | "ScreenHookClick" + | "Command" + | "scriptmsg" + | "gamepacket" + | "gamepacketsent" + | "realmpacket"; + + function addEventListener(eventType: "realmpacket", callback: (bytes: ArrayBufferLike) => boolean): void; + function addEventListener(eventType: "gamepacket", callback: (bytes: ArrayBufferLike) => boolean): void; + function addEventListener(eventType: "scriptmsg", callback: (data: string | object | number) => void): void; + function addEventListener(eventType: "copydata", callback: (mode: number, msg: string) => void): void; + function addEventListener( + eventType: "itemaction", + callback: (gid: number, mode?: number, code?: string, global?: true) => void, + ): void; + function addEventListener( + eventType: "keyup" | "keydown" | "keydownblocker", + callback: (key: number | string) => void, + ): void; + function addEventListener( + eventType: "chatmsg" | "chatinput" | "whispermsg", + callback: (nick: string, msg: string) => void, + ): void; + function addEventListener( + eventType: "chatmsgblocker" | "chatinputblocker" | "whispermsgblocker", + callback: (arg1: string, arg2: string) => void, + ): void; + function addEventListener(eventType: "mousemove", callback: (arg1: string, arg2: string) => void): void; + function addEventListener(eventType: "ScreenHookHover", callback: (arg1: string, arg2: string) => void): void; + function addEventListener( + eventType: "mouseclick", + callback: (arg1: string, arg2: string, arg3: string, arg4: string) => void, + ): void; + function addEventListener(eventType: "memana" | "melife", callback: (arg1: string, arg2: string) => void): void; + function addEventListener(eventType: "playerassign", callback: (arg1: string, arg4: string) => void): void; + function addEventListener( + eventType: "ScreenHookClick", + callback: (arg1: any, arg2: any, arg3: any, arg4: any) => void, + ): void; + function addEventListener(eventType: eventName, callback: (...args: any) => void): void; + + function removeEventListener(eventType: "gamepacket", callback: (bytes: ArrayBufferLike) => boolean): void; + function removeEventListener(eventType: "scriptmsg", callback: (data: string | object | number) => void): void; + function removeEventListener(eventType: "copydata", callback: (mode: number, msg: string) => void): void; + function removeEventListener( + eventType: "itemaction", + callback: (gid: number, mode?: number, code?: string, global?: true) => void, + ): void; + function removeEventListener(eventType: "keyup" | "keydown", callback: (key: number) => void): void; + function removeEventListener(eventType: "chatmsg", callback: (nick: string, msg: string) => void): void; + function removeEventListener(eventType: eventName, callback: (...args: any) => void): void; + + function clearEvent(); + function clearAllEvents(); + function js_strict(); + function version(): number; + function scriptBroadcast(what: string | object): void; + function sqlite_version(); + function sqlite_memusage(); type directory = { getFiles(): string[]; @@ -1178,88 +1219,88 @@ declare global { create(what?: string): boolean; }; function dopen(what?: string): directory | false; - function debugLog(text: string): void - function showConsole(): void - function hideConsole(): void + function debugLog(text: string): void; + function showConsole(): void; + function hideConsole(): void; // out of game functions - function login(name?: string): void - function selectCharacter() - function createGame() - function joinGame() - function addProfile() - function getLocation():number - function loadMpq() + function login(name?: string): void; + function selectCharacter(); + function createGame(); + function joinGame(); + function addProfile(); + function getLocation(): number; + function loadMpq(); // game functions that don't have anything to do with gathering data - function submitItem(): void - function getMouseCoords() - function copyUnit(unit: S): S - function clickMap(type: 0 | 1 | 2 | 3, shift: 0 | 1, x: number, y: number) - function acceptTrade() - function tradeOk() - function beep(id?: number) - - function clickItem(where: 0 | 1 | 2, bodyLocation: number) - function clickItem(where: 0 | 1 | 2, item: ItemUnit) - function clickItem(where: 0 | 1 | 2, x: number, y: number) - function clickItem(where: 0 | 1 | 2, x: number, y: number, location: number) - - function getDistance(a: Unit, b: Unit): number - function getDistance(a: Unit, toX: number, toY: number): number - function getDistance(fromX: number, fromY: number, b: Unit): number - function getDistance(fromX: number, fromY: number, toX: number, toY: number): number - - function gold(amount: number, changeType?: 0 | 1 | 2 | 3 | 4): void - function checkCollision(a: Unit, b: Unit, type: number): boolean - function playSound(num: number): void - function quit(): never - function quitGame(): never - function say(what: string, force?: boolean): void + function submitItem(): void; + function getMouseCoords(); + function copyUnit(unit: S): S; + function clickMap(type: 0 | 1 | 2 | 3, shift: 0 | 1, x: number, y: number); + function acceptTrade(); + function tradeOk(); + function beep(id?: number); + + function clickItem(where: 0 | 1 | 2, bodyLocation: number); + function clickItem(where: 0 | 1 | 2, item: ItemUnit); + function clickItem(where: 0 | 1 | 2, x: number, y: number); + function clickItem(where: 0 | 1 | 2, x: number, y: number, location: number); + + function getDistance(a: Unit, b: Unit): number; + function getDistance(a: Unit, toX: number, toY: number): number; + function getDistance(fromX: number, fromY: number, b: Unit): number; + function getDistance(fromX: number, fromY: number, toX: number, toY: number): number; + + function gold(amount: number, changeType?: 0 | 1 | 2 | 3 | 4): void; + function checkCollision(a: Unit, b: Unit, type: number): boolean; + function playSound(num: number): void; + function quit(): never; + function quitGame(): never; + function say(what: string, force?: boolean): void; /** * Use when you want to force something to be said in chat and don't know if LocalChat is being used - * @param what + * @param what */ - function _say(what: string): void - function clickParty(player: Party, type: 0 | 1 | 2 | 3 | 4) - function weaponSwitch(): void - function transmute(): void - function useStatPoint(type: number): void - function useSkillPoint(type: number): void - function takeScreenshot(): void - function moveNPC(npc: Monster, x: number, y: number): void + function _say(what: string): void; + function clickParty(player: Party, type: 0 | 1 | 2 | 3 | 4); + function weaponSwitch(): void; + function transmute(): void; + function useStatPoint(type: number): void; + function useSkillPoint(type: number): void; + function takeScreenshot(): void; + function moveNPC(npc: Monster, x: number, y: number): void; - function getPacket(buffer: ArrayBuffer): void - function getPacket(...args: { size: number, data: number }[]): void + function getPacket(buffer: ArrayBuffer): void; + function getPacket(...args: { size: number; data: number }[]): void; - function sendPacket(buffer: ArrayBuffer): void - function sendPacket(...number: number[]): void + function sendPacket(buffer: ArrayBuffer): void; + function sendPacket(...number: number[]): void; - function getIP(): string - function sendKey(key: number): void - function revealLevel(unknown: true): void + function getIP(): string; + function sendKey(key: number): void; + function revealLevel(unknown: true): void; // hash functions - function md5(str: string): string - function sha1(str: string): string - function sha256(str: string): string - function sha384(str: string): string - function sha512(str: string): string - function md5_file(str: string): string - function sha1_file(str: string): string - function sha256_file(str: string): string - function sha384_file(str: string): string - function sha512_file(str: string): string + function md5(str: string): string; + function sha1(str: string): string; + function sha256(str: string): string; + function sha384(str: string): string; + function sha512(str: string): string; + function md5_file(str: string): string; + function sha1_file(str: string): string; + function sha256_file(str: string): string; + function sha384_file(str: string): string; + function sha512_file(str: string): string; interface Console { - static log(...whatever: any[]): void - static debug(...whatever: any[]): void - static warn(...whatever: any[]): void - static error(...whatever: any[]): void - static time(name: string): void; - static timeEnd(name: string): void; - static trace(): void; - static info(start: boolean, msg: string, timer: string): void; + log(...whatever: any[]): void; + debug(...whatever: any[]): void; + warn(...whatever: any[]): void; + error(...whatever: any[]): void; + time(name: string): void; + timeEnd(name: string): void; + trace(): void; + info(start: boolean, msg: string, timer: string): void; } const console: Console; @@ -1301,97 +1342,97 @@ declare global { function copyObj(from: object): object; interface StarterConfig { - MinGameTime: number, - MaxGameTime?: number, - PingQuitDelay: number, - CreateGameDelay: number, - ResetCount: number - CharacterDifference: number, - MaxPlayerCount: number, - StopOnDeadHardcore: boolean, - - JoinChannel: string, - FirstJoinMessage: string, - ChatActionsDelay: number, - AnnounceGames: boolean, - AnnounceMessage: string, - AfterGameMessage: string, - - InvalidPasswordDelay: number, // Minutes to wait after getting Invalid Password message - VersionErrorDelay: number, // Seconds to wait after 'unable to identify version' message - SwitchKeyDelay: number, // Seconds to wait before switching a used/banned key or after realm down - CrashDelay: number, // Seconds to wait after a d2 window crash - FTJDelay: number, // Seconds to wait after failing to create a game - RealmDownDelay: number, // Minutes to wait after getting Realm Down message - UnableToConnectDelay: number, // Minutes to wait after Unable To Connect message - TCPIPNoHostDelay: number, // Seconds to wait after Cannot Connect To Server message - CDKeyInUseDelay: number, // Minutes to wait before connecting again if CD-Key is in use. - ConnectingTimeout: number, // Seconds to wait before cancelling the 'Connecting...' screen - PleaseWaitTimeout: number, // Seconds to wait before cancelling the 'Please Wait...' screen - WaitInLineTimeout: number, // Seconds to wait before cancelling the 'Waiting in Line...' screen - WaitOutQueueRestriction: boolean, // Wait out queue if we are restricted, queue time > 10000 - WaitOutQueueExitToMenu: boolean, // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby - GameDoesNotExistTimeout: number, // Seconds to wait before cancelling the 'Game does not exist.' screen + MinGameTime: number; + MaxGameTime?: number; + PingQuitDelay: number; + CreateGameDelay: number; + ResetCount: number; + CharacterDifference: number; + MaxPlayerCount: number; + StopOnDeadHardcore: boolean; + + JoinChannel: string; + FirstJoinMessage: string; + ChatActionsDelay: number; + AnnounceGames: boolean; + AnnounceMessage: string; + AfterGameMessage: string; + + InvalidPasswordDelay: number; // Minutes to wait after getting Invalid Password message + VersionErrorDelay: number; // Seconds to wait after 'unable to identify version' message + SwitchKeyDelay: number; // Seconds to wait before switching a used/banned key or after realm down + CrashDelay: number; // Seconds to wait after a d2 window crash + FTJDelay: number; // Seconds to wait after failing to create a game + RealmDownDelay: number; // Minutes to wait after getting Realm Down message + UnableToConnectDelay: number; // Minutes to wait after Unable To Connect message + TCPIPNoHostDelay: number; // Seconds to wait after Cannot Connect To Server message + CDKeyInUseDelay: number; // Minutes to wait before connecting again if CD-Key is in use. + ConnectingTimeout: number; // Seconds to wait before cancelling the 'Connecting...' screen + PleaseWaitTimeout: number; // Seconds to wait before cancelling the 'Please Wait...' screen + WaitInLineTimeout: number; // Seconds to wait before cancelling the 'Waiting in Line...' screen + WaitOutQueueRestriction: boolean; // Wait out queue if we are restricted, queue time > 10000 + WaitOutQueueExitToMenu: boolean; // Wait out queue restriction at D2 Splash screen if true, else wait out in lobby + GameDoesNotExistTimeout: number; // Seconds to wait before cancelling the 'Game does not exist.' screen } interface ProfileInfo { - profile: string, - account: string, - charName: string, - difficulty: string, - tag: string, - realm: string, + profile: string; + account: string; + charName: string; + difficulty: string; + tag: string; + realm: string; } interface StarterInterface { - Config: StarterConfig, - useChat: boolean, - pingQuit: boolean, - inGame: boolean, - firstLogin: boolean, - firstRun: boolean, - isUp: "yes"|"no", - loginRetry: number, - deadCheck: boolean, - chatActionsDone: boolean, - gameStart: boolean, - gameCount: number, - lastGameStatus: string, - handle: number | null, - connectFail: boolean, - connectFailRetry: number, - makeAccount: false, - channelNotify: boolean, + Config: StarterConfig; + useChat: boolean; + pingQuit: boolean; + inGame: boolean; + firstLogin: boolean; + firstRun: boolean; + isUp: "yes" | "no"; + loginRetry: number; + deadCheck: boolean; + chatActionsDone: boolean; + gameStart: boolean; + gameCount: number; + lastGameStatus: string; + handle: number | null; + connectFail: boolean; + connectFailRetry: number; + makeAccount: false; + channelNotify: boolean; chanInfo: { - joinChannel: string, - firstMsg: string, - afterMsg: string, - announce: boolean, - }, + joinChannel: string; + firstMsg: string; + afterMsg: string; + announce: boolean; + }; gameInfo: { - error: string, - gameName?: string, - gamePass?: string, - difficulty?: string, + error: string; + gameName?: string; + gamePass?: string; + difficulty?: string; crashInfo: { - currScript: number, - area: number, - }, - switchKeys: boolean, - }, - joinInfo: {}, - profileInfo: ProfileInfo, - ftjCount: number, - - sayMsg(string: string): void, - timer(tick: number): string, - locationTimeout(time: number, location: number): boolean, - setNextGame(gameInfo: { gameName: string }): void, - updateCount(): void, - scriptMsgEvent(msg: string): void, - receiveCopyData(mode: number, msg: string | object): void, - randomString(len?: number, useNumbers?: boolean): string, - randomNumberString(len?: number): string, + currScript: number; + area: number; + }; + switchKeys: boolean; + }; + joinInfo: {}; + profileInfo: ProfileInfo; + ftjCount: number; + + sayMsg(string: string): void; + timer(tick: number): string; + locationTimeout(time: number, location: number): boolean; + setNextGame(gameInfo: { gameName: string }): void; + updateCount(): void; + scriptMsgEvent(msg: string): void; + receiveCopyData(mode: number, msg: string | object): void; + randomString(len?: number, useNumbers?: boolean): string; + randomNumberString(len?: number): string; /** * Cache for waypoints by character name and difficulty */ @@ -1496,7 +1537,7 @@ declare global { namespace Time { /** * Converts seconds to milliseconds. - * + * * @param {number} [seconds=0] - The number of seconds to convert. * @returns {number} - The equivalent time in milliseconds. */ @@ -1504,7 +1545,7 @@ declare global { /** * Converts minutes to milliseconds. - * + * * @param {number} [minutes=0] - The number of minutes to convert. * @returns {number} - The equivalent time in milliseconds. */ @@ -1512,7 +1553,7 @@ declare global { /** * Formats milliseconds into a "HH:MM:SS" string. - * + * * @param {number} [ms=0] - The time in milliseconds to format. * @returns {string} - The formatted time string. */ @@ -1520,7 +1561,7 @@ declare global { /** * Converts milliseconds to seconds. - * + * * @param {number} [ms=0] - The time in milliseconds to convert. * @returns {number} - The equivalent time in seconds. */ @@ -1528,7 +1569,7 @@ declare global { /** * Converts milliseconds to minutes. - * + * * @param {number} [ms=0] - The time in milliseconds to convert. * @returns {number} - The equivalent time in minutes. */ @@ -1536,7 +1577,7 @@ declare global { /** * Converts milliseconds to hours. - * + * * @param {number} [ms=0] - The time in milliseconds to convert. * @returns {number} - The equivalent time in hours. */ @@ -1544,7 +1585,7 @@ declare global { /** * Converts milliseconds to days. - * + * * @param {number} [ms=0] - The time in milliseconds to convert. * @returns {number} - The equivalent time in days. */ @@ -1552,17 +1593,26 @@ declare global { /** * Calculates the elapsed time from a given timestamp. - * + * * @param {number} [ms=0] - The starting time in milliseconds. * @returns {number} - The elapsed time in milliseconds. */ function elapsed(start: number): number; } - type PrimitiveType = "undefined" | "object" | "boolean" | "number" | "bigint" | "string" | "symbol" | "function" | "array"; + type PrimitiveType = + | "undefined" + | "object" + | "boolean" + | "number" + | "bigint" + | "string" + | "symbol" + | "function" + | "array"; /** * Checks if the value matches the expected type. - * + * * @param val - The value to be checked. * @param type - The expected type as a string. * @returns {boolean} - Returns true if the value matches the expected type, otherwise false. @@ -1581,6 +1631,6 @@ declare global { * @param source - Source object to merge from * @returns Merged object */ - function deepMerge(target: object, source: object): object + function deepMerge(target: object, source: object): object; } export {}; diff --git a/package-lock.json b/package-lock.json index 579fe6f78..c12957767 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { + "@biomejs/biome": "2.1.2", "@types/node": "^24.0.10", "eslint": "^7.32.0", "typescript": "^4.9.3" @@ -118,6 +119,169 @@ "node": ">=4" } }, + "node_modules/@biomejs/biome": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.2.tgz", + "integrity": "sha512-yq8ZZuKuBVDgAS76LWCfFKHSYIAgqkxVB3mGVVpOe2vSkUTs7xG46zXZeNPRNVjiJuw0SZ3+J2rXiYx0RUpfGg==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.1.2", + "@biomejs/cli-darwin-x64": "2.1.2", + "@biomejs/cli-linux-arm64": "2.1.2", + "@biomejs/cli-linux-arm64-musl": "2.1.2", + "@biomejs/cli-linux-x64": "2.1.2", + "@biomejs/cli-linux-x64-musl": "2.1.2", + "@biomejs/cli-win32-arm64": "2.1.2", + "@biomejs/cli-win32-x64": "2.1.2" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.2.tgz", + "integrity": "sha512-leFAks64PEIjc7MY/cLjE8u5OcfBKkcDB0szxsWUB4aDfemBep1WVKt0qrEyqZBOW8LPHzrFMyDl3FhuuA0E7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.2.tgz", + "integrity": "sha512-Nmmv7wRX5Nj7lGmz0FjnWdflJg4zii8Ivruas6PBKzw5SJX/q+Zh2RfnO+bBnuKLXpj8kiI2x2X12otpH6a32A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.2.tgz", + "integrity": "sha512-NWNy2Diocav61HZiv2enTQykbPP/KrA/baS7JsLSojC7Xxh2nl9IczuvE5UID7+ksRy2e7yH7klm/WkA72G1dw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.2.tgz", + "integrity": "sha512-qgHvafhjH7Oca114FdOScmIKf1DlXT1LqbOrrbR30kQDLFPEOpBG0uzx6MhmsrmhGiCFCr2obDamu+czk+X0HQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.2.tgz", + "integrity": "sha512-Km/UYeVowygTjpX6sGBzlizjakLoMQkxWbruVZSNE6osuSI63i4uCeIL+6q2AJlD3dxoiBJX70dn1enjQnQqwA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.2.tgz", + "integrity": "sha512-xlB3mU14ZUa3wzLtXfmk2IMOGL+S0aHFhSix/nssWS/2XlD27q+S6f0dlQ8WOCbYoXcuz8BCM7rCn2lxdTrlQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.2.tgz", + "integrity": "sha512-G8KWZli5ASOXA3yUQgx+M4pZRv3ND16h77UsdunUL17uYpcL/UC7RkWTdkfvMQvogVsAuz5JUcBDjgZHXxlKoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.2.tgz", + "integrity": "sha512-9zajnk59PMpjBkty3bK2IrjUsUHvqe9HWwyAWQBjGLE7MIBjbX2vwv1XPEhmO2RRuGoTkVx3WCanHrjAytICLA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -1362,6 +1526,78 @@ } } }, + "@biomejs/biome": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.2.tgz", + "integrity": "sha512-yq8ZZuKuBVDgAS76LWCfFKHSYIAgqkxVB3mGVVpOe2vSkUTs7xG46zXZeNPRNVjiJuw0SZ3+J2rXiYx0RUpfGg==", + "dev": true, + "requires": { + "@biomejs/cli-darwin-arm64": "2.1.2", + "@biomejs/cli-darwin-x64": "2.1.2", + "@biomejs/cli-linux-arm64": "2.1.2", + "@biomejs/cli-linux-arm64-musl": "2.1.2", + "@biomejs/cli-linux-x64": "2.1.2", + "@biomejs/cli-linux-x64-musl": "2.1.2", + "@biomejs/cli-win32-arm64": "2.1.2", + "@biomejs/cli-win32-x64": "2.1.2" + } + }, + "@biomejs/cli-darwin-arm64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.2.tgz", + "integrity": "sha512-leFAks64PEIjc7MY/cLjE8u5OcfBKkcDB0szxsWUB4aDfemBep1WVKt0qrEyqZBOW8LPHzrFMyDl3FhuuA0E7g==", + "dev": true, + "optional": true + }, + "@biomejs/cli-darwin-x64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.2.tgz", + "integrity": "sha512-Nmmv7wRX5Nj7lGmz0FjnWdflJg4zii8Ivruas6PBKzw5SJX/q+Zh2RfnO+bBnuKLXpj8kiI2x2X12otpH6a32A==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.2.tgz", + "integrity": "sha512-NWNy2Diocav61HZiv2enTQykbPP/KrA/baS7JsLSojC7Xxh2nl9IczuvE5UID7+ksRy2e7yH7klm/WkA72G1dw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64-musl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.2.tgz", + "integrity": "sha512-qgHvafhjH7Oca114FdOScmIKf1DlXT1LqbOrrbR30kQDLFPEOpBG0uzx6MhmsrmhGiCFCr2obDamu+czk+X0HQ==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.2.tgz", + "integrity": "sha512-Km/UYeVowygTjpX6sGBzlizjakLoMQkxWbruVZSNE6osuSI63i4uCeIL+6q2AJlD3dxoiBJX70dn1enjQnQqwA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64-musl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.2.tgz", + "integrity": "sha512-xlB3mU14ZUa3wzLtXfmk2IMOGL+S0aHFhSix/nssWS/2XlD27q+S6f0dlQ8WOCbYoXcuz8BCM7rCn2lxdTrlQA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-arm64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.2.tgz", + "integrity": "sha512-G8KWZli5ASOXA3yUQgx+M4pZRv3ND16h77UsdunUL17uYpcL/UC7RkWTdkfvMQvogVsAuz5JUcBDjgZHXxlKoA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-x64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.2.tgz", + "integrity": "sha512-9zajnk59PMpjBkty3bK2IrjUsUHvqe9HWwyAWQBjGLE7MIBjbX2vwv1XPEhmO2RRuGoTkVx3WCanHrjAytICLA==", + "dev": true, + "optional": true + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", diff --git a/package.json b/package.json index 511606e89..8c4798342 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "author": "", "license": "ISC", "devDependencies": { + "@biomejs/biome": "2.1.2", "@types/node": "^24.0.10", "eslint": "^7.32.0", "typescript": "^4.9.3" From 5ec8c38ae0210bf1f9d90bc94879769a37e9d6c5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 19 Jul 2025 18:02:40 -0400 Subject: [PATCH 607/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index fc06debc8..66120c1bf 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit fc06debc8e45a3d832e789d6ed63c7020158a3d1 +Subproject commit 66120c1bf809380ec84fac1208e478a1b74a578a From e1d5c44e386ac1c35a624eaac1e842cc55da1c1c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:32:19 -0400 Subject: [PATCH 608/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 66120c1bf..6fcd8a008 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 66120c1bf809380ec84fac1208e478a1b74a578a +Subproject commit 6fcd8a008352a2897094d29cf1d6f3fd84df14bd From 1d962ae87698e65867177e0648660a51ecd06b47 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:34:14 -0400 Subject: [PATCH 609/758] Cleanup + add `PathNode` class type declaration --- d2bs/kolbot/libs/core/Cubing.js | 4 ++-- d2bs/kolbot/libs/core/Prototypes.js | 3 +++ d2bs/kolbot/libs/core/Town.js | 4 +++- d2bs/kolbot/sdk/globals.d.ts | 5 +++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 3c8c9a89d..3c3b58f8f 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -633,7 +633,7 @@ const Cubing = { if (recipe.hasOwnProperty("condition") && typeof recipe.condition === "function") { if (!recipe.condition()) { - console.debug("Skipping recipe due to condition cb"); + console.debug("Skipping recipe " + recipe.Index + " due to condition cb"); continue; } } @@ -646,7 +646,7 @@ const Cubing = { if (itemCount >= recipe.MaxQuantity) { console.debug( - "Skipping recipe due to item count exceeding MaxQuantity." + "Skipping recipe " + recipe.Index + " due to item count exceeding MaxQuantity." + " Have: " + itemCount + ", Wanted: " + recipe.MaxQuantity ); diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 82533951c..551ed721a 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -213,16 +213,19 @@ Object.defineProperties(Unit.prototype, { }, }, isWalking: { + /** @this {Monster} */ get: function () { return (this.mode === sdk.monsters.mode.Walking && (this.targetx !== this.x || this.targety !== this.y)); } }, isRunning: { + /** @this {Monster} */ get: function () { return (this.mode === sdk.monsters.mode.Running && (this.targetx !== this.x || this.targety !== this.y)); } }, isMoving: { + /** @this {Monster} */ get: function () { return (this.isWalking || this.isRunning); }, diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index f797eda90..0e5f01539 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -1219,7 +1219,9 @@ const Town = { let name = ""; let quantity = 0; - let chugs = me.getItemsEx(type).filter(pot => pot.isInInventory); + let chugs = me.getItemsEx(type).filter(function (pot) { + return pot.isInInventory; + }); if (chugs.length > 0) { name = chugs.first().name; diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 757661a09..0ac4db977 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -854,6 +854,7 @@ declare global { } const me: MeType; + interface PathNode { x: number; y: number; @@ -878,6 +879,10 @@ declare global { mobCount(givenSettings: { range?: number; coll?: number; type?: number; ignoreClassids?: number[] }): number; } + class PathNode { + constructor(x: number, y: number); + } + function getUnit(type: 4, name?: string, mode?: number, unitId?: number): ItemUnit; function getUnit(type: 4, classId?: number, mode?: number, unitId?: number): ItemUnit; function getUnit(type: 1, name?: string, mode?: number, unitId?: number): Monster; From 86c4231015c0fd44b81fd64d71573d737e82d242 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 20 Jul 2025 21:10:53 -0400 Subject: [PATCH 610/758] Add CollMap.d.ts --- d2bs/kolbot/sdk/globals.d.ts | 1 + d2bs/kolbot/sdk/types/CollMap.d.ts | 139 +++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 d2bs/kolbot/sdk/types/CollMap.d.ts diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 0ac4db977..cc2bbef54 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -18,6 +18,7 @@ /// /// /// +/// declare global { type IncludePath = import("./types/include-paths").IncludePath; diff --git a/d2bs/kolbot/sdk/types/CollMap.d.ts b/d2bs/kolbot/sdk/types/CollMap.d.ts new file mode 100644 index 000000000..abe09e456 --- /dev/null +++ b/d2bs/kolbot/sdk/types/CollMap.d.ts @@ -0,0 +1,139 @@ +declare global { + interface CollMapRoom { + x: number; + y: number; + xsize: number; + ysize: number; + } + + interface CollMapHook { + room: Room; + lines: Line[]; + } + + interface CollMapColors { + readonly green: 0x84; + readonly red: 0x0a; + readonly black: 0x00; + readonly white: 0xff; + readonly purple: 0x9b; + readonly blue: 0x97; + } + + type CollMapColorName = "green" | "red" | "black" | "white" | "purple" | "blue"; + + interface CollMapInstance { + rooms: CollMapRoom[]; + maps: number[][][]; + hooks: CollMapHook[]; + readonly colors: CollMapColors; + + /** + * Draw room outline on screen + * @param room The room to draw + * @param color Color name or color code + * @param update Whether to update existing drawing + */ + drawRoom(room: Room, color?: CollMapColorName | number, update?: boolean): void; + + /** + * Remove hook for specific room + * @param room The room to remove hook for + */ + removeHookForRoom(room: Room): void; + + /** + * Remove all hooks + */ + removeHooks(): void; + + /** + * Get nearby rooms for given coordinates + * @param x X coordinate + * @param y Y coordinate + * @returns Success status + */ + getNearbyRooms(x: number, y: number): boolean; + + /** + * Add room to collision map + * @param x Room object or X coordinate + * @param y Y coordinate (optional if first param is Room) + * @returns Success status + */ + addRoom(x: Room | number, y?: number): boolean; + + /** + * Get collision value at coordinates + * @param x X coordinate + * @param y Y coordinate + * @param cacheOnly Only check cache + * @returns Collision value + */ + getColl(x: number, y: number, cacheOnly?: boolean): number; + + /** + * Get room index for coordinates + * @param x X coordinate + * @param y Y coordinate + * @param cacheOnly Only check cache + * @returns Room index or undefined + */ + getRoomIndex(x: number, y: number, cacheOnly?: boolean): number | undefined; + + /** + * Check if coordinates are in room + * @param x X coordinate + * @param y Y coordinate + * @param room Room to check + * @returns Whether coordinates are in room + */ + coordsInRoom(x: number, y: number, room: CollMapRoom): boolean; + + /** + * Reset collision map + */ + reset(): void; + + /** + * Check collision between two units + * @param unitA First unit + * @param unitB Second unit + * @param coll Collision flags + * @param thickness Line thickness + * @returns Whether collision exists + */ + checkColl(unitA: Unit | PathNode, unitB: Unit | PathNode, coll: number, thickness?: number): boolean; + + /** + * Get teleport point for room + * @param room Room to get teleport point for + * @returns Teleport point or null + */ + getTelePoint(room: Room): PathNode | null; + + /** + * Get random valid coordinate + * @param cX Center X coordinate + * @param xmin Minimum X offset + * @param xmax Maximum X offset + * @param cY Center Y coordinate + * @param ymin Minimum Y offset + * @param ymax Maximum Y offset + * @param factor Scale factor + * @returns Random valid coordinate + */ + getRandCoordinate( + cX: number, + xmin: number, + xmax: number, + cY: number, + ymin: number, + ymax: number, + factor?: number, + ): PathNode; + } + + const CollMap: CollMapInstance; +} +export {}; From acc436cca94c93c4afced20ac345ed91730d856e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:13:02 -0400 Subject: [PATCH 611/758] Add `DiabloSeal` type for `Config.Diablo.SealOrder` --- d2bs/kolbot/libs/core/Config.js | 2 ++ d2bs/kolbot/sdk/types/Config.d.ts | 59 +++++++++++++------------------ 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 9621cbb55..2e159a043 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -562,6 +562,7 @@ let Config = { StarTP: "Star TP up", DiabloMsg: "Diablo", ClearRadius: 30, + /** @type {import("sdk/types/Config").DiabloSeal[]} */ SealOrder: ["vizier", "seis", "infector"] }, DiabloHelper: { @@ -572,6 +573,7 @@ let Config = { OpenSeals: false, SafePrecast: true, ClearRadius: 30, + /** @type {import("sdk/types/Config").DiabloSeal[]} */ SealOrder: ["vizier", "seis", "infector"], RecheckSeals: false }, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 798a24c84..50f3ddc23 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -1,27 +1,19 @@ -/** -* @filename Config.js -* @author kolton -* @desc config loading and default config values storage -* -*/ +export type DiabloSeal = "vizier" | "seis" | "infector"; declare global { // interface Scripts { [data: string]: Partial | boolean } - type ExtendedCubingOpts = { Ethereal: number, MaxQuantity: number, condition: () => boolean }; - type CubingRecipe = - | [number, string] - | [number, string, number] - | [number, string, ExtendedCubingOpts]; + type ExtendedCubingOpts = { Ethereal: number; MaxQuantity: number; condition: () => boolean }; + type CubingRecipe = [number, string] | [number, string, number] | [number, string, ExtendedCubingOpts]; interface Config { - init(notify: any): void; + init(notify: boolean): void; Loaded: boolean; DebugMode: { - Path: boolean, - Stack: boolean, - Memory: boolean, - Skill: boolean, - Town: boolean, + Path: boolean; + Stack: boolean; + Memory: boolean; + Skill: boolean; + Town: boolean; }; // Experimental @@ -29,8 +21,7 @@ declare global { AutoEquip: boolean; UseExperimentalAvoid: boolean; - Experimental: { - } + Experimental: {}; StartDelay: number; PickDelay: number; @@ -89,15 +80,15 @@ declare global { }; Inventory: number[][]; SortSettings: { - SortInventory: boolean, - SortStash: boolean, - PlugYStash: boolean, - ItemsSortedFromLeft: number[], - ItemsSortedFromRight: number[], - PrioritySorting: boolean, - ItemsSortedFromLeftPriority: number[], - ItemsSortedFromRightPriority: number[], - }, + SortInventory: boolean; + SortStash: boolean; + PlugYStash: boolean; + ItemsSortedFromLeft: number[]; + ItemsSortedFromRight: number[]; + PrioritySorting: boolean; + ItemsSortedFromLeftPriority: number[]; + ItemsSortedFromRightPriority: number[]; + }; LocalChat: { Enabled: boolean; Toggle: boolean; @@ -139,7 +130,8 @@ declare global { SkipAura: string[]; SkipException: (number | string)[]; ImmunityException: DamageType[]; - ScanShrines: any[]; + ScanShrines: number[]; + AutoShriner: boolean; Debug: boolean; AutoMule: { Trigger: (number | string | ((item: ItemUnit) => boolean))[]; @@ -204,8 +196,8 @@ declare global { AttackSkill: any[]; LowManaSkill: any[]; CustomAttack: Record; - CustomPreAttack: Record, - AdvancedCustomAttack: { check: (unit: Monster) => boolean, attack: [number, number], preAttack: number }[], + CustomPreAttack: Record; + AdvancedCustomAttack: { check: (unit: Monster) => boolean; attack: [number, number]; preAttack: number }[]; TeleStomp: boolean; NoTele: boolean; ClearType: boolean; @@ -372,7 +364,7 @@ declare global { StarTP: string; DiabloMsg: string; ClearRadius: number; - SealOrder: string[]; + SealOrder: DiabloSeal[]; }; DiabloHelper: { Wait: number; @@ -382,7 +374,7 @@ declare global { OpenSeals: boolean; SafePrecast: boolean; ClearRadius: number; - SealOrder: string[]; + SealOrder: DiabloSeal[]; RecheckSeals: boolean; }; MFHelper: { @@ -590,4 +582,3 @@ declare global { } const Config: Config; } -export {}; From a001de3a8c711ce4500306c56eafd31723a90da7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:13:14 -0400 Subject: [PATCH 612/758] Update Misc.d.ts & globals.d.ts - removed some duplicates and added missing methods to misc --- d2bs/kolbot/sdk/globals.d.ts | 39 ++------------------ d2bs/kolbot/sdk/types/Misc.d.ts | 63 +++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 63 deletions(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index cc2bbef54..d829b2a3b 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -435,6 +435,7 @@ declare global { getItemsEx(classId?: number, mode?: number, unitId?: number): ItemUnit[]; getItemsEx(name?: string, mode?: number, unitId?: number): ItemUnit[]; inArea(area: number): boolean; + getMobCount(range: number, coll: number, type: number, noSpecialMobs: boolean): number; checkForMobs(givenSettings: { range?: number; count?: number; coll?: number; spectype: number }): boolean; } @@ -1431,24 +1432,16 @@ declare global { ftjCount: number; sayMsg(string: string): void; - timer(tick: number): string; - locationTimeout(time: number, location: number): boolean; - setNextGame(gameInfo: { gameName: string }): void; - updateCount(): void; - scriptMsgEvent(msg: string): void; - receiveCopyData(mode: number, msg: string | object): void; - randomString(len?: number, useNumbers?: boolean): string; - randomNumberString(len?: number): string; /** * Cache for waypoints by character name and difficulty */ - waypointCache: { [charName: string]: any[] }; + waypointCache: { [charName: string]: number[] }; /** * Handles script message events (object version) * @param msg - The message object */ - scriptMsgEvent(msg: object): void; + scriptMsgEvent(msg: string | object): void; /** * Handles copy data event @@ -1494,32 +1487,6 @@ declare global { */ updateCount(): void; - /** - * Handles script message events (string version) - * @param msg - The message string - */ - scriptMsgEvent(msg: string): void; - - /** - * Handles copy data event - * @param mode - The mode - * @param msg - The message (object or string) - */ - receiveCopyData(mode: number, msg: object | string): void; - - /** - * Returns a random string of given length - * @param len - Length of string - * @param useNumbers - Whether to use numbers - */ - randomString(len?: number, useNumbers?: boolean): string; - - /** - * Returns a random number string of given length - * @param len - Length of string - */ - randomNumberString(len?: number): string; - /** * Location event handlers */ diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index 15bb0d20e..634035f59 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -1,13 +1,23 @@ - -export{}; +export {}; declare global { namespace Misc { - const screenshotErrors: any; - const errorConsolePrint: any; + const _diabloSpawned: boolean; + const screenshotErrors: boolean; + const errorConsolePrint: boolean; const useItemLog: boolean; - - function click(button: number, shift: number, unit: Unit): void; - function click(button: number, shift: number, x: Unit, y: undefined): void; + let shrineStates: number[] | null; + const _shrinerIgnore: Set; + const lastShrine: { + tick: number; + duration: number; + type: number; + state: number; + update(unit: ObjectUnit): void; + remaining(): number; + isMyCurrentState(): boolean; + }; + + function click(button: number, shift: number, x?: number | Unit, y?: number): boolean; function inMyParty(name: string): boolean; function findPlayer(name: string): Party | false; function getPlayerUnit(name: string): Player | false; @@ -16,29 +26,28 @@ declare global { function getPlayerCount(): number; function getPartyCount(): number; function getPartyMembers(): Party[]; - function checkPartyLevel(levelCheck: number, exclude: string | string[]): boolean; + function checkPartyLevel(levelCheck?: number, exclude?: string | string[]): boolean; function getPlayerArea(player: Party | string): number | false; - - type AutoLeaderDetectSettings = { - destination: number | number[], - quitIf: (area: number) => boolean, - timeout: number, - }; - function autoLeaderDetect(givenSettings: AutoLeaderDetectSettings): string | false; - function openChest(unit: any): boolean; - function openChestsInArea(area?: any, chestIds?: any): void; - function openChests(range: any): void; - function scanShrines(range: any): void; - function getShrine(unit: any): void; - function getShrinesInArea(area: any, type: any, use: any): void; + function autoLeaderDetect(givenSettings?: { + destination?: number | number[]; + quitIf?: (area: number) => boolean; + timeout?: number; + }): string | false; + function openChest(unit: Unit | number): boolean; + function openChestsInArea(area?: number, chestIds?: number[]): boolean; + function openChests(range?: number): boolean; + function shriner(ignore: number[]): boolean; + function scanShrines(range: number, ignore: number[]): boolean; + function getShrine(unit: ObjectUnit): boolean; + function getShrinesInArea(area: number, type: number, use: boolean): boolean; /** @deprecated */ - function townCheck(boolean?: boolean): void; - function spy(name: any): void; + function townCheck(): boolean; + function spy(name: string): boolean; function errorReport(error: Error | string, script?: string): void; - function debugLog(msg: any): void; - function useMenu(id: number): void; - function poll(check: () => T, timeout?: number, sleep?: number): T; - function getUIFlags(excluded?: []): number[] | null; + function debugLog(msg: string): void; + function useMenu(id: number): boolean; + function poll(check: () => T, timeout?: number, sleep?: number): T | false; + function getUIFlags(excluded?: number[]): number[] | null; function getQuestStates(questId: number): number[]; } } From 7c1bc472af9ab60cab8f9e2b640e7b9c5804c424 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:31:17 -0400 Subject: [PATCH 613/758] Refactor Attack clear logic and add experimental walk clear - Refactored Attack.js to improve monster targeting, attack skill selection, and room clearing logic. - Added Config.UseExperimentalClearLevel to enable a new walk-based clear method for non-teleporting characters in dungeon areas. - Updated type definitions and Config.js to support the new option. Improved code readability and maintainability by restructuring several functions and adding documentation. --- d2bs/kolbot/libs/core/Attack.js | 403 ++++++++++++++++++++---------- d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/sdk/types/Config.d.ts | 6 +- 3 files changed, 274 insertions(+), 136 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index dae205571..06e47e38f 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -186,7 +186,7 @@ const Attack = { getCustomPreAttack: function (unit) { // Check if unit got invalidated if (!unit || !unit.name || !copyUnit(unit).x) return false; - + for (let el of Config.AdvancedCustomAttack) { if (el.hasOwnProperty("check") && el.hasOwnProperty("preAttack")) { if (typeof el.check === "function" && el.check(unit)) { @@ -284,7 +284,7 @@ const Attack = { /** * @description Kill a monster based on its classId, can pass a unit as well - * @param {Unit | number} classId + * @param {Monster | number | string} classId * @returns {boolean} If we managed to kill the unit */ kill: function (classId) { @@ -305,7 +305,7 @@ const Attack = { /** * @param {number} gid * @param {PathNode} loc - * @returns {Unit | boolean} + * @returns {Monster | boolean} */ const findTarget = function (gid, loc) { let path = getPath(me.area, me.x, me.y, loc.x, loc.y, 1, 5); @@ -338,12 +338,13 @@ const Attack = { // mfhelper is disabled for these scripts so announcing is pointless && !currentScript.includes("diablo") && !currentScript.includes("baal") + && !me.inArea(sdk.areas.UberTristram) && Pather.makePortal()) { say("kill " + classId); } try { - while (attackCount < Config.MaxAttackCount && target.attackable && !this.skipCheck(target)) { + while (attackCount < Config.MaxAttackCount && target.attackable && !Attack.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); @@ -794,10 +795,11 @@ const Attack = { * @param {number | Unit} [bossId] * @param {(a: T, b: T) => number} [sortfunc] * @param {boolean} [pickit] + * @param {(unit: Monster) => boolean} [shouldAttackCb] * @returns {boolean} * @todo change to passing an object */ - clear: function (range, spectype, bossId, sortfunc, pickit = true) { + clear: function (range, spectype, bossId, sortfunc, pickit = true, shouldAttackCb = () => true) { while (!me.gameReady) { delay(40); } @@ -812,8 +814,9 @@ const Attack = { if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); - let i, boss, orgx, orgy, start, skillCheck; - let gidAttack = []; + /** @type {Map mon.gid === boss.gid)) { + monsterList.push(copyUnit(boss)); + } + while (start && monsterList.length > 0 && attackCount < Config.MaxAttackCount) { if (me.dead) return false; @@ -873,9 +882,16 @@ const Attack = { monsterList.sort(sortfunc); target = Game.getMonster(-1, -1, monsterList[0].gid); - if (target && target.x !== undefined && (getDistance(target, orgx, orgy) <= range - || (this.getScarinessLevel(target) > 7 && target.distance <= range)) - && target.attackable) { + if ( + target + && target.x !== undefined + && shouldAttackCb(target) + && ( + getDistance(target, orgx, orgy) <= range + || (this.getScarinessLevel(target) > 7 && target.distance <= range) + ) + && target.attackable + ) { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); tick = getTickCount(); @@ -885,11 +901,14 @@ const Attack = { } // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); - // custom handling here, we want to find a valid monster to use our skill on - // if we wait until they are the current target, it may too late to be useful + let _currMon = attacks.get(target.gid); + const checkAttackSkill = (!!_currMon && _currMon.attacks % 15 === 0); + if (Config.ChargeCast.skill > -1 && Config.ChargeCast.spectype && !(target.spectype & Config.ChargeCast.spectype)) { + // custom handling here, we want to find a valid monster to use our skill on + // if we wait until they are the current target, it may be pointless let cRange = Skill.getRange(Config.ChargeCast.skill); let cState = Skill.getState(Config.ChargeCast.skill); let chargeTarget = monsterList.find(function (mon) { @@ -904,8 +923,8 @@ const Attack = { Attack.doChargeCast(chargeTarget); } } - - const result = ClassAttack.doAttack(target, attackCount % 15 === 0); + + const result = ClassAttack.doAttack(target, checkAttackSkill); if (result) { retry = 0; @@ -918,25 +937,21 @@ const Attack = { continue; } - for (i = 0; i < gidAttack.length; i += 1) { - if (gidAttack[i].gid === target.gid) { - break; - } - } - - if (i === gidAttack.length) { - gidAttack.push({ gid: target.gid, attacks: 0, name: target.name }); + if (!_currMon) { + _currMon = { attacks: 0, name: target.name }; + attacks.set(target.gid, _currMon); } - gidAttack[i].attacks += 1; + _currMon.attacks += 1; 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; + const isSpecial = target.isSpecial; + const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; + const checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; + const hammerCheck = me.paladin && checkSkill === sdk.skills.BlessedHammer; - if (Config.AttackSkill[secAttack] > -1 && (!Attack.checkResist(target, checkSkill) - || (hammerCheck && !ClassAttack.getHammerPosition(target)))) { + if (Config.AttackSkill[secAttack] > -1 + && (!Attack.checkResist(target, checkSkill) || (hammerCheck && !ClassAttack.getHammerPosition(target))) + ) { skillCheck = Config.AttackSkill[secAttack]; } else { skillCheck = checkSkill; @@ -946,15 +961,18 @@ const Attack = { 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 && Pather.useTeleport()) { Pather.randMove(-1, 1, -1, 1, 5); } 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 + ) { + // It'd be helpful to get a position in the opposite direction of the monster move there and then move back Packet.flash(me.gid); } @@ -962,8 +980,8 @@ const Attack = { } // Skip non-unique monsters after 15 attacks, except in Throne of Destruction - if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && gidAttack[i].attacks > 15) { - console.log("ÿc1Skipping " + target.name + " " + target.gid + " " + gidAttack[i].attacks); + if (!me.inArea(sdk.areas.ThroneofDestruction) && !isSpecial && _currMon.attacks > 15) { + console.log("ÿc1Skipping " + target.name + " " + target.gid + " " + _currMon.attacks); monsterList.shift(); } @@ -1368,6 +1386,41 @@ const Attack = { Attack.ignoredGids = []; }, + /** + * @description Clear an entire area based on monster spectype using nearestNeighbourSearch + * @param {number} spectype + * @param {() => boolean} [cb] callback to end clearing early + * @returns {boolean} + */ + clearLevelWalk: function (spectype, cb = null) { + const Graph = require("../modules/Graph"); + + try { + console.info(true, getAreaName(me.area), "clearLevelWalk-nearestNeighbourSearch"); + let graph = new Graph(); + Graph.nearestNeighbourSearch(graph, function (room) { + if (typeof cb === "function" && cb()) { + throw new ScriptError("Clearing stopped by callback"); + } + const roomNode = new PathNode(room.walkableX, room.walkableY); + Pather.move(roomNode, { callback: cb, clearSettings: { clearPath: true } }); + Attack.clearEx(room.xsize, { + spectype: spectype || 0, + filter: function (unit) { + return unit && room.coordsInRoom(unit.x, unit.y); + } + }); + }, "walk"); + } catch (e) { + if (!(e instanceof ScriptError)) { + console.error(e); + } + } finally { + CollMap.removeHooks(); + console.info(false, getAreaName(me.area), "clearLevelWalk-nearestNeighbourSearch"); + } + }, + /** * @description Clear a single room based on monster spectype * @param {Room} room - The room to clear @@ -1419,41 +1472,6 @@ const Attack = { return true; }, - /** - * @description Clear an entire area based on monster spectype using nearestNeighbourSearch - * @param {number} spectype - * @param {() => boolean} [cb] callback to end clearing early - * @returns {boolean} - */ - clearLevelWalk: function (spectype, cb = null) { - const Graph = require("../modules/Graph"); - - try { - console.info(true, getAreaName(me.area), "clearLevelWalk-nearestNeighbourSearch"); - let graph = new Graph(); - Graph.nearestNeighbourSearch(graph, function (room) { - if (typeof cb === "function" && cb()) { - throw new ScriptError("Clearing stopped by callback"); - } - const roomNode = new PathNode(room.walkableX, room.walkableY); - Pather.move(roomNode, { callback: cb, clearSettings: { clearPath: true } }); - Attack.clearEx(room.xsize, { - spectype: spectype || 0, - filter: function (unit) { - return unit && room.coordsInRoom(unit.x, unit.y); - } - }); - }, "walk"); - } catch (e) { - if (!(e instanceof ScriptError)) { - console.error(e); - } - } finally { - CollMap.removeHooks(); - console.info(false, getAreaName(me.area), "clearLevelWalk-nearestNeighbourSearch"); - } - }, - /** * @description Clear an entire area based on monster spectype * @param {number} spectype @@ -1465,17 +1483,80 @@ const Attack = { return getDistance(myRoom[0], myRoom[1], a[0], a[1]) - getDistance(myRoom[0], myRoom[1], b[0], b[1]); } + function _walkingRoomSort (a, b) { + let aDist = Pather.getWalkDistance(a[0], a[1], me.area, myRoom[0], myRoom[1]); + let bDist = Pather.getWalkDistance(b[0], b[1], me.area, myRoom[0], myRoom[1]); + return aDist - bDist; + } + + /** + * @param {Room} room + * @returns {[number, number]} + */ + function getCenter (room) { + let centerX = room.x * 5 + room.xsize / 2; + let centerY = room.y * 5 + room.ysize / 2; + + let adjusted = Pather.getNearestWalkable(centerX, centerY, 18, 3); + return adjusted ? [adjusted[0], adjusted[1]] : [centerX, centerY]; + } + let room = getRoom(); if (!room) return false; + const canTele = Pather.canTeleport(); + const currentArea = getArea().id; + const dungeons = [ + sdk.areas.DenofEvil, + sdk.areas.HoleLvl1, + sdk.areas.HoleLvl2, + sdk.areas.PitLvl1, + sdk.areas.PitLvl2, + sdk.areas.CaveLvl1, + sdk.areas.CaveLvl2, + sdk.areas.UndergroundPassageLvl1, + sdk.areas.UndergroundPassageLvl2, + sdk.areas.TowerCellarLvl1, + sdk.areas.TowerCellarLvl2, + sdk.areas.TowerCellarLvl3, + sdk.areas.TowerCellarLvl4, + sdk.areas.TowerCellarLvl5, + sdk.areas.Crypt, + sdk.areas.Mausoleum, + sdk.areas.A2SewersLvl1, + sdk.areas.A2SewersLvl2, + sdk.areas.A2SewersLvl3, + sdk.areas.StonyTombLvl1, + sdk.areas.StonyTombLvl2, + sdk.areas.HallsoftheDeadLvl1, + sdk.areas.HallsoftheDeadLvl2, + sdk.areas.HallsoftheDeadLvl3, + sdk.areas.MaggotLairLvl1, + sdk.areas.MaggotLairLvl2, + sdk.areas.MaggotLairLvl3, + sdk.areas.AncientTunnels, + sdk.areas.ClawViperTempleLvl1, + sdk.areas.ClawViperTempleLvl2, + sdk.areas.TalRashasTomb1, + sdk.areas.TalRashasTomb2, + sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, + sdk.areas.TalRashasTomb5, + sdk.areas.TalRashasTomb6, + sdk.areas.TalRashasTomb7, + ]; + + if (!canTele && dungeons.includes(me.area) && (Config.DebugMode.Path || Config.UseExperimentalClearLevel)) { + return Attack.clearLevelWalk(spectype, cb); + } console.time("clearLevel"); console.info(true, getAreaName(me.area)); let myRoom, previousArea; - const rooms = []; - const currentArea = getArea().id; + let rooms = []; + let count = 0; /** @type {Text[]} */ - const hooks = []; + let hooks = []; /** @param {Text} hook */ const clearHook = function (hook) { @@ -1483,7 +1564,7 @@ const Attack = { }; do { - rooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + rooms.push([...getCenter(room), copyObj(room)]); } while (room.getNext()); if (Config.MFLeader && rooms.length > 0) { @@ -1506,7 +1587,7 @@ const Attack = { 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]; + myRoom = getCenter(room); } } @@ -1520,10 +1601,19 @@ const Attack = { CollMap.drawRoom(room[2], "green"); hooks.push(new Text((++count).toString(), room[0], room[1], 2, 1, null, true)); } - Pather.moveToEx( - result[0], result[1], - { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } } - ); + let node = new PathNode(result[0], result[1]); + + if (node.distance < 20 && !canTele && node.mobCount() === 0) { + if (Config.DebugMode.Path) { + console.debug("ÿc1Skipping room " + room[0] + " " + room[1]); + CollMap.drawRoom(room[2], "red", true); + } + } else { + Pather.move( + node, + { retry: 3, clearSettings: { specType: spectype, clearPath: (!Pather.canTeleport()) } } + ); + } previousArea = result; if (!this.clear(40, spectype)) { @@ -1799,7 +1889,9 @@ const Attack = { }, deploy: function (unit, distance, spread, range) { - 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 safeLoc = this.findSafeSpot(unit, distance, spread, range); @@ -1853,17 +1945,19 @@ const Attack = { /** * @description checks if we should skip a monster - * @param {Unit} unit + * @param {Monster} unit * @returns {Boolean} If we should skip this monster */ skipCheck: function (unit) { if (me.inArea(sdk.areas.ThroneofDestruction)) return false; - if (unit.isSpecial && Config.SkipException && Config.SkipException.includes(unit.name)) { + if (unit.isSpecial && Config.SkipException.length && Config.SkipException.includes(unit.name)) { console.log("ÿc1Skip Exception: " + unit.name); return false; } - if (Config.SkipId.includes(unit.classid)) return true; + if (Config.SkipId.includes(unit.classid)) { + return true; + } let tempArray = []; @@ -2047,7 +2141,13 @@ const Attack = { return 0; }, - // Check if a monster is immune to specified attack type + /** + * Check if a monster is immune to specified attack type + * @param {Monster | Player} unit + * @param {number} val + * @param {number} maxres + * @returns {boolean} + */ checkResist: function (unit, val, maxres = 100) { if (!unit || !unit.type || unit.isPlayer) return true; @@ -2065,7 +2165,7 @@ const Attack = { // 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 (Attack.infinity && ["fire", "lightning", "cold"].includes(damageType) && unit.getState) { if (!unit.getState(sdk.states.Conviction)) { if (addLowerRes && !unit.getState(sdk.states.LowerResist)) { let lowerResPercent = this.getLowerResistPercent(); @@ -2077,8 +2177,12 @@ const Attack = { return this.getResist(unit, damageType) < maxres; } - if (this.auradin && ["physical", "fire", "cold", "lightning"].includes(damageType) - && me.getState(sdk.states.Conviction) && unit.getState) { + if ( + Attack.auradin + && ["physical", "fire", "cold", "lightning"].includes(damageType) + && me.getState(sdk.states.Conviction) + && unit.getState + ) { let valid = false; // our main dps is not physical despite using zeal @@ -2124,26 +2228,38 @@ const Attack = { * @returns {boolean} */ 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]))) { - return true; - } - } else { - if (Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[3])) - || Attack.checkResist(unit, this.getSkillElement(Config.AttackSkill[4]))) { - return true; - } + if (!unit || !unit.type || !unit.isMonster) return false; + const skillElems = Config.AttackSkill.map(function (skill) { + return Attack.getSkillElement(skill); + }); + // Unique/Champion + if (unit.isSpecial) { + if (Attack.checkResist(unit, skillElems[1]) + || Attack.checkResist(unit, skillElems[2])) { + return true; } + } else { + if (Attack.checkResist(unit, skillElems[3]) + || Attack.checkResist(unit, skillElems[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])); + if (skillElems.length === 7) { + if (Attack.checkResist(unit, skillElems[5]) + || Attack.checkResist(unit, skillElems[6])) { + return true; } } + // Secondary if monster is immune to our existing backup skill + // i.e. Hammerdins having holybolt as main backup but using smite here as third backup + if (skillElems.length === 9) { + if (Attack.checkResist(unit, skillElems[7]) + || Attack.checkResist(unit, skillElems[8])) { + return true; + } + } return false; }, @@ -2184,7 +2300,6 @@ const Attack = { if (!unit || !unit.x || !unit.y) return false; walk === true && (walk = 1); - force && console.debug("Forcing new position"); /** @@ -2206,9 +2321,27 @@ const Attack = { 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 canTele = !walk && Pather.useTeleport(); const { x: orgX, y: orgY } = me; + /** @param {PathNode} node */ + const handleMove = function (node) { + switch (walk) { + case 1: + return Pather.walkTo(node.x, node.y, 2); + case 2: + if (node.distance < 6 && !CollMap.checkColl(me, node, sdk.collision.WallOrRanged)) { + return Pather.walkTo(node.x, node.y, 2); + } else { + return Pather.move(node, { retry: 1, allowPicking: !force }); + } + default: + return Pather.move(node, { retry: 1, allowPicking: !force }); + } + }; + for (let n = 0; n < 3; n++) { + /** @type {PathNode[]} */ const coords = []; n > 0 && (distance -= Math.floor(fullDistance / 3 - 1)); @@ -2216,52 +2349,50 @@ const Attack = { 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); + let node = new PathNode(cx, cy); // 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 (force && node.distance < distance) continue; + if (Pather.checkSpot(node.x, node.y, sdk.collision.BlockWall, false)) { + coords.push(node); } } if (!coords.length) continue; coords.sort(Sort.units); - for (let i = 0; i < coords.length; i += 1) { - // Valid position found - if (!CollMap.checkColl({ x: coords[i].x, y: coords[i].y }, unit, coll, 1)) { - if (!Pather.canTeleport() && Pather.getWalkDistance(coords[i].x, coords[i].y) > unit.distance) { + for (let coord of coords) { + // check if position is valid + if (CollMap.checkColl(coord, unit, coll, 1)) { + continue; + } + if (!canTele) { + if (Config.DebugMode.Path) { + console.debug("coord", coord, " dist", coord.distance); + new Line(coord.x - 3, coord.y, coord.x + 3, coord.y, 0x9B, true); + new Line(coord.x, coord.y - 3, coord.x, coord.y + 3, 0x9B, true); + } + + let walkDist = Pather.getWalkDistance(coord.x, coord.y); + if (walkDist > unit.distance) { if (Config.DebugMode.Path) { console.debug( "Skipping position due to walk distance being too far." + "\n - DistanceToMonster: " + unit.distance - + "\n - DistanceToPosition: " + Pather.getWalkDistance(coords[i].x, coords[i].y) + + "\n - DistanceToPosition: " + walkDist ); + continue; } - continue; } - if ((() => { - switch (walk) { - case 1: - return Pather.walkTo(coords[i].x, coords[i].y, 2); - case 2: - if (coords[i].distance < 6 && !CollMap.checkColl(me, coords[i], sdk.collision.WallOrRanged)) { - return Pather.walkTo(coords[i].x, coords[i].y, 2); - } else { - return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); - } - default: - return Pather.moveToEx(coords[i].x, coords[i].y, { retry: 1, allowPicking: !force }); - } - })()) { - if (Config.DebugMode.Path && force) { - console.debug( - "Sucessfully got into position. orginal Loc: " + orgX + "/" + orgY - + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance - ); - } - return true; + } + if (handleMove(coord)) { + if (Config.DebugMode.Path && force) { + console.debug( + "Sucessfully got into position. orginal Loc: " + orgX + "/" + orgY + + " new loc " + me.x + "/" + me.y + " distance: " + [orgX, orgY].distance + ); } + return true; } } } @@ -2319,7 +2450,9 @@ const Attack = { return ([ sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect - ].every(state => !unit.getState(state))); + ].every(function (state) { + return !unit.getState(state); + })); }, /** @@ -2375,9 +2508,11 @@ const Attack = { : [Config.AttackSkill[0], Attack.getPrimarySlot()]; preAttackInfo.length < 2 && preAttackInfo.push(Attack.getPrimarySlot()); const [skill, slot] = preAttackInfo; + const cState = Skill.getState(skill); if (skill > 0 && Attack.checkResist(unit, skill) + && (!cState || !unit.getState(cState)) && (!me.skillDelay || !Skill.isTimed(skill))) { if (unit.distance > Skill.getRange(skill) || checkCollision(me, unit, sdk.collision.Ranged)) { if (!Attack.getIntoPosition(unit, Skill.getRange(skill), sdk.collision.Ranged)) { diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 2e159a043..3d95f75f3 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -124,6 +124,7 @@ let Config = { FastParty: false, AutoEquip: false, UseExperimentalAvoid: false, + UseExperimentalClearLevel: false, // Time StartDelay: 0, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 50f3ddc23..4ea5d606f 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -20,8 +20,10 @@ declare global { FastParty: boolean; AutoEquip: boolean; UseExperimentalAvoid: boolean; - - Experimental: {}; + /** + * Enables experimental walk clear level feature for non-teleporters + */ + UseExperimentalClearLevel: boolean; StartDelay: number; PickDelay: number; From d8792053cf4e534bea6dee3be199d114044d0a2c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:42:23 -0400 Subject: [PATCH 614/758] Update Cain.js --- d2bs/kolbot/libs/core/Common/Cain.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/libs/core/Common/Cain.js b/d2bs/kolbot/libs/core/Common/Cain.js index c19a202ad..8ba7315f5 100644 --- a/d2bs/kolbot/libs/core/Common/Cain.js +++ b/d2bs/kolbot/libs/core/Common/Cain.js @@ -7,6 +7,10 @@ (function (module) { module.exports = { + /** + * @param {ObjectUnit} stone + * @returns {boolean} + */ activateStone: function (stone) { for (let i = 0; i < 3; i++) { // don't use tk if we are right next to it From 8850fa660fcf72d52a5f44db722fbfd451aba016 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:44:09 -0400 Subject: [PATCH 615/758] Enable subpaths with `PathDebug` - Add shrinehooks for shriner debugging --- d2bs/kolbot/libs/core/Pather.js | 72 +++++++++++++++++++++++-------- d2bs/kolbot/sdk/types/Pather.d.ts | 58 +++++++++++++++++++++---- 2 files changed, 104 insertions(+), 26 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 1683f5a0b..cb9683325 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -5,6 +5,8 @@ * */ +includeIfNotIncluded("manualplay/hooks/ShrineHooks.js"); + /** * @constructor * @param {number} x @@ -117,7 +119,7 @@ const NodeAction = { */ getShrines: function () { if (Config.AutoShriner) { - Misc.shriner(); + Misc.shriner(this.shrinesToIgnore); } else if (Config.ScanShrines.length > 0) { Misc.scanShrines(null, this.shrinesToIgnore); } @@ -125,33 +127,40 @@ const NodeAction = { }; const PathDebug = { - /** @type {Line[]} */ - hooks: [], enableHooks: false, + /** @type {Map 5) { + // console.debug(path, "move: Failed to generate path, trying to find a walkable node. Current distance: " + getDistance(me, node.x, node.y), " node: ", node); + // let adjustedNode = Pather.getNearestWalkable(node.x, node.y, 5, 1, sdk.collision.BlockWalk); + // if (!adjustedNode) { + // throw new Error("move: Failed to generate path."); + // } + // let [tmpX, tmpY] = adjustedNode; + // path = getPath( + // me.area, + // tmpX, tmpY, + // me.x, me.y, + // useTeleport ? 1 : 0, + // useTeleport ? (annoyingArea ? 30 : this.teleDistance) : this.walkDistance + // ); + + // if (!path || !path.length) { + // throw new Error("move: Failed to generate path."); + // } + // node.x = tmpX; + // node.y = tmpY; + // } + if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { settings.retry = 10; } path.reverse(); settings.pop && path.pop(); - PathDebug.drawPath(path); + PathDebug.drawPath(PATH_DEBUG_ID, path); useTeleport && Config.TeleSwitch && path.length > 5 && me.switchWeapons(primarySlot ^ 1); while (path.length > 0) { @@ -434,12 +469,14 @@ const Pather = { if (getUIFlag(this.cancelFlags[i])) me.cancel(); } + Config.DebugMode.Path && ShrineHooks.check(); + node = path.shift(); if (typeof settings.callback === "function" && settings.callback()) { console.debug("Callback function passed. Ending path."); useTeleport && Config.TeleSwitch && me.switchWeapons(primarySlot); - PathDebug.removeHooks(); + PathDebug.removeHooks(PATH_DEBUG_ID); return true; } @@ -550,7 +587,7 @@ const Pather = { if (!path) throw new Error("move: Failed to generate path."); path.reverse(); - PathDebug.drawPath(path); + PathDebug.drawPath(PATH_DEBUG_ID, path); settings.pop && path.pop(); if (fail > 0) { @@ -570,7 +607,8 @@ const Pather = { } useTeleport && Config.TeleSwitch && me.switchWeapons(primarySlot); - PathDebug.removeHooks(); + PathDebug.removeHooks(PATH_DEBUG_ID); + Config.DebugMode.Path && ShrineHooks.flush(); return getDistance(me, node.x, node.y) < 5; }, diff --git a/d2bs/kolbot/sdk/types/Pather.d.ts b/d2bs/kolbot/sdk/types/Pather.d.ts index 8b9d70dae..c5c787dcd 100644 --- a/d2bs/kolbot/sdk/types/Pather.d.ts +++ b/d2bs/kolbot/sdk/types/Pather.d.ts @@ -1,5 +1,14 @@ export {}; declare global { + interface PathDebug { + enableHooks: boolean; + paths: Map; + drawPath(id: number, path: PathNode[]): void; + removeHooks(id: number): void; + coordsInPath(path: PathNode[], x: number, y: number): boolean; + } + const PathDebug: PathDebug; + interface PathSettings { allowNodeActions?: boolean; allowTeleport?: boolean; @@ -30,14 +39,42 @@ declare global { let lastPortalTick: 0; let allowBroadcast: boolean; - function getWalkDistance(x: number, y: number, area?: number, xx?: number, yy?: number, reductionType?: 0 | 1 | 2, radius?: number): number; + function getWalkDistance( + x: number, + y: number, + area?: number, + xx?: number, + yy?: number, + reductionType?: 0 | 1 | 2, + radius?: number, + ): number; function useTeleport(): boolean; - function moveTo(x: number, y: number, retry?: number | undefined, clearPath?: boolean | undefined, pop?: boolean | undefined): boolean; + function moveTo( + x: number, + y: number, + retry?: number | undefined, + clearPath?: boolean | undefined, + pop?: boolean | undefined, + ): boolean; function teleportTo(x: any, y: any, maxRange?: any): void; function walkTo(x: any, y: any, minDist?: number | undefined): boolean; function openDoors(x: any, y: any): boolean; - function moveToUnit(unit: PathNode, offX?: undefined, offY?: undefined, clearPath?: undefined, pop?: undefined): boolean; - function moveToPreset(area: any, unitType: any, unitId: any, offX?: any, offY?: any, clearPath?: any, pop?: any): boolean; + function moveToUnit( + unit: PathNode, + offX?: undefined, + offY?: undefined, + clearPath?: undefined, + pop?: undefined, + ): boolean; + function moveToPreset( + area: any, + unitType: any, + unitId: any, + offX?: any, + offY?: any, + clearPath?: any, + pop?: any, + ): boolean; function moveToPresetObject(area: number, unitId: number, givenSettings?: PathSettings): boolean; function moveToPresetMonster(area: number, unitId: number, givenSettings?: PathSettings): boolean; function moveToExit(targetArea: any, use?: any, givenSettings?: PathSettings): boolean; @@ -53,16 +90,19 @@ declare global { function usePortal(targetArea?: number | null, owner?: string | undefined, unit?: undefined): boolean; function getPortal(targetArea: number, owner?: any): ObjectUnit | false; function getNearestWalkable( - x: number, y: number, range: number, step: number, coll: number, size?: number + x: number, + y: number, + range: number, + step: number, + coll: number, + size?: number, ): [number, number] | false; - function checkSpot( - x: number, y: number, coll: number, cacheOnly: boolean, size: number - ): boolean; + function checkSpot(x: number, y: number, coll: number, cacheOnly: boolean, size: number): boolean; /** @deprecated use `me.accessToAct(act)` instead */ function accessToAct(act: number): boolean; function getWP(area: number, clearPath?: boolean): boolean; function journeyTo(area: number): boolean; - function plotCourse(dest: number, src: number): false | { course: number[], useWP: boolean }; + function plotCourse(dest: number, src: number): false | { course: number[]; useWP: boolean }; function areasConnected(src: number, dest: number): void; } } From 04ce78dc0ae376b5b72bb76d3bea4175a454586d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:29:38 -0400 Subject: [PATCH 616/758] Add `Shrines` to DebugMode config --- d2bs/kolbot/libs/core/Config.js | 1 + d2bs/kolbot/libs/core/Pather.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 3d95f75f3..7dc62d39f 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -118,6 +118,7 @@ let Config = { Memory: false, Skill: false, Town: false, + Shrines: false, }, // Experimental diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index cb9683325..346233553 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -469,7 +469,7 @@ const Pather = { if (getUIFlag(this.cancelFlags[i])) me.cancel(); } - Config.DebugMode.Path && ShrineHooks.check(); + Config.DebugMode.Shrines && ShrineHooks.check(); node = path.shift(); @@ -608,7 +608,7 @@ const Pather = { useTeleport && Config.TeleSwitch && me.switchWeapons(primarySlot); PathDebug.removeHooks(PATH_DEBUG_ID); - Config.DebugMode.Path && ShrineHooks.flush(); + Config.DebugMode.Shrines && ShrineHooks.flush(); return getDistance(me, node.x, node.y) < 5; }, From bfe6be951bd617a3a483206893a9ee8f5fca5414 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:32:43 -0400 Subject: [PATCH 617/758] Update shriner to accept range and ignore list - Add Poison & Exploding shrines - Update Stamina shrine to account for current stamina percent and maxstamina --- d2bs/kolbot/libs/core/Misc.js | 53 ++++++++++++++++++++++++------- d2bs/kolbot/sdk/types/Config.d.ts | 1 + 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 84191fc05..35ad5d2e9 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -548,7 +548,12 @@ const Misc = (function () { /** @type {Set} */ _shrinerIgnore: new Set(), - shriner: function () { + /** + * @param {number[]} ignore + * @param {number} range + * @returns {boolean} + */ + shriner: function (ignore = [], range = 50) { if (!Config.AutoShriner) return false; let shrineList = []; @@ -567,22 +572,24 @@ const Misc = (function () { && Misc.lastShrine.remaining() > Time.seconds(30)) { return false; } + const walkDistance = Pather.getWalkDistance(shrine.x, shrine.y); + switch (shrine.objtype) { case sdk.shrines.Health: // we only want if its dire or its close to us if we can't teleport - if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 10) { + if (!Pather.useTeleport() && walkDistance > 10) { return me.hpPercent <= 50; } return me.hpPercent < 80; case sdk.shrines.Mana: // we only want if its dire or its close to us if we can't teleport - if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 10) { + if (!Pather.useTeleport() && walkDistance > 10) { return me.mpPercent <= 50; } return me.mpPercent < 80; case sdk.shrines.Refilling: // we only want if its dire or its close to us if we can't teleport - if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 10) { + if (!Pather.useTeleport() && walkDistance > 10) { return me.hpPercent <= 50 || me.mpPercent <= 50; } return me.hpPercent < 85 || me.mpPercent < 85; @@ -591,9 +598,18 @@ const Misc = (function () { case sdk.shrines.Skill: return !me.getState(sdk.states.ShrineExperience); case sdk.shrines.ManaRecharge: + // we only want if its close to us if we can't teleport + if (!Pather.useTeleport() && walkDistance > 15) { + return false; + } + // for now, only grab if we have nothing else active + return !Misc.lastShrine.state || !me.getState(Misc.lastShrine.state); case sdk.shrines.Stamina: // we only want if its close to us if we can't teleport - if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + if ( + !Pather.useTeleport() + && walkDistance > (me.staminamax < 200 || me.staminaPercent < 30) ? 30 : 15 + ) { return false; } // for now, only grab if we have nothing else active @@ -611,7 +627,7 @@ const Misc = (function () { resistances[sdk.shrines.ResistPoison] = me.poisonRes; // we only want if its dire or its close to us if we can't teleport - if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + if (!Pather.useTeleport() && walkDistance > 15) { return resistances[shrine.objtype] <= 0; } @@ -650,7 +666,7 @@ const Misc = (function () { case sdk.shrines.Armor: case sdk.shrines.Combat: // we only want if its close to us if we can't teleport - if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + if (!Pather.useTeleport() && walkDistance > 15) { return false; } @@ -660,7 +676,7 @@ const Misc = (function () { return false; case sdk.shrines.Monster: // we only want if its close to us if we can't teleport - if (!Pather.useTeleport() && Pather.getWalkDistance(shrine.x, shrine.y) > 15) { + if (!Pather.useTeleport() && walkDistance > 15) { return false; } @@ -670,6 +686,15 @@ const Misc = (function () { // TODO: add gem hunting logic, get gem from stash if we have one console.debug("shriner: gem shrine. try my best."); return Town.prepareForGemShrine(); + case sdk.shrines.Poison: + case sdk.shrines.Exploding: + // if it's close are we are low on gold then why not? + if (!Pather.useTeleport() && walkDistance > 10) { + return false; + } + // ideally we should mock the potion to see if we will actually pick it up + // but for now we just check if we are low on gold + return me.gold < Config.LowGold || me.gold < 500000; } return false; }; @@ -697,7 +722,9 @@ const Misc = (function () { let _name = shrine.name.toLowerCase(); if ((_name.includes("shrine") && ShrineData.has(shrine.objtype) || (_name.includes("well"))) && ShrineData.has(shrine.objtype) - && shrine.mode === sdk.objects.mode.Inactive) { + && !ignore.includes(shrine.objtype) + && shrine.mode === sdk.objects.mode.Inactive + ) { shrineList.push(copyUnit(shrine)); } } while (shrine.getNext()); @@ -708,6 +735,10 @@ const Misc = (function () { shrine = shrineList.shift(); if (shrine) { + if (shrine.distance > range) { + continue; // too far away + } + if (me.inArea(sdk.areas.ChaosSanctuary)) { // stateful shrines are pointless in CS unless we are running wakka or diablo has spawned so no more oblivion knights if (!this._diabloSpawned && Loader.scriptName() !== "Wakka" && ShrineData.getState(shrine.objtype)) { @@ -745,13 +776,13 @@ const Misc = (function () { * @returns {boolean} */ scanShrines: function (range, ignore) { + !Array.isArray(ignore) && (ignore = [ignore]); if (Config.AutoShriner) { - return this.shriner(); + return this.shriner(ignore); } if (!Config.ScanShrines.length) return false; !range && (range = Pather.useTeleport() ? 25 : 15); - !Array.isArray(ignore) && (ignore = [ignore]); /** @type {ObjectUnit[]} */ let shrineList = []; diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 4ea5d606f..63424d506 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -14,6 +14,7 @@ declare global { Memory: boolean; Skill: boolean; Town: boolean; + Shrines: boolean; }; // Experimental From 39b0d7841c8111a73e1e5e147f1ee6b9e1228a11 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:19:29 -0400 Subject: [PATCH 618/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 6fcd8a008..b4d153a66 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 6fcd8a008352a2897094d29cf1d6f3fd84df14bd +Subproject commit b4d153a667fc4716ff409c10902d183a8e9181fa From 962814fd8bf34f54cc6e914ca7b266fae549d7ba Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 17:39:22 -0400 Subject: [PATCH 619/758] Fix typo in ShrineData + add shrine names - Experience shrine was mistyped with the ShrineResCold state --- d2bs/kolbot/libs/core/GameData/ShrineData.js | 42 ++++++++++---------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/ShrineData.js b/d2bs/kolbot/libs/core/GameData/ShrineData.js index 551be62dc..4e3d6cb25 100644 --- a/d2bs/kolbot/libs/core/GameData/ShrineData.js +++ b/d2bs/kolbot/libs/core/GameData/ShrineData.js @@ -9,38 +9,40 @@ const ShrineData = (function () { /** * @constructor + * @param {string} name * @param {number} state * @param {number} duration * @param {number} regen */ - function Shrine (state, duration, regen) { + function Shrine (name, state, duration, regen) { + this.name = name || "Unknown Shrine"; this.state = state || 0; this.duration = duration || 0; this.regenTime = Time.minutes(regen) || Infinity; } const _shrines = new Map([ - [sdk.shrines.Refilling, new Shrine(sdk.shrines.None, 0, 2)], - [sdk.shrines.Health, new Shrine(sdk.shrines.None, 0, 5)], - [sdk.shrines.Mana, new Shrine(sdk.shrines.None, 0, 5)], + [sdk.shrines.Refilling, new Shrine("Refilling", sdk.shrines.None, 0, 2)], + [sdk.shrines.Health, new Shrine("Health", sdk.shrines.None, 0, 5)], + [sdk.shrines.Mana, new Shrine("Mana", sdk.shrines.None, 0, 5)], [sdk.shrines.HealthExchange, new Shrine()], [sdk.shrines.ManaExchange, new Shrine()], - [sdk.shrines.Armor, new Shrine(sdk.states.ShrineArmor, 2400, 5)], - [sdk.shrines.Combat, new Shrine(sdk.states.ShrineCombat, 2400, 5)], - [sdk.shrines.ResistFire, new Shrine(sdk.states.ShrineResFire, 3600, 5)], - [sdk.shrines.ResistCold, new Shrine(sdk.states.ShrineResCold, 3600, 5)], - [sdk.shrines.ResistLightning, new Shrine(sdk.states.ShrineResLighting, 3600, 5)], - [sdk.shrines.ResistPoison, new Shrine(sdk.states.ShrineResPoison, 3600, 5)], - [sdk.shrines.Skill, new Shrine(sdk.states.ShrineSkill, 2400, 5)], - [sdk.shrines.ManaRecharge, new Shrine(sdk.states.ShrineManaRegen, 2400, 5)], - [sdk.shrines.Stamina, new Shrine(sdk.states.ShrineStamina, 4800, 5)], - [sdk.shrines.Experience, new Shrine(sdk.states.ShrineResCold, 3600)], + [sdk.shrines.Armor, new Shrine("Armor", sdk.states.ShrineArmor, 2400, 5)], + [sdk.shrines.Combat, new Shrine("Combat", sdk.states.ShrineCombat, 2400, 5)], + [sdk.shrines.ResistFire, new Shrine("Resist Fire", sdk.states.ShrineResFire, 3600, 5)], + [sdk.shrines.ResistCold, new Shrine("Resist Cold", sdk.states.ShrineResCold, 3600, 5)], + [sdk.shrines.ResistLightning, new Shrine("Resist Lightning", sdk.states.ShrineResLighting, 3600, 5)], + [sdk.shrines.ResistPoison, new Shrine("Resist Poison", sdk.states.ShrineResPoison, 3600, 5)], + [sdk.shrines.Skill, new Shrine("Skill", sdk.states.ShrineSkill, 2400, 5)], + [sdk.shrines.ManaRecharge, new Shrine("Mana Recharge", sdk.states.ShrineManaRegen, 2400, 5)], + [sdk.shrines.Stamina, new Shrine("Stamina", sdk.states.ShrineStamina, 4800, 5)], + [sdk.shrines.Experience, new Shrine("Experience", sdk.states.ShrineExperience, 3600)], [sdk.shrines.Enirhs, new Shrine()], - [sdk.shrines.Portal, new Shrine()], - [sdk.shrines.Gem, new Shrine()], - [sdk.shrines.Fire, new Shrine()], - [sdk.shrines.Monster, new Shrine()], - [sdk.shrines.Exploding, new Shrine()], - [sdk.shrines.Poison, new Shrine()], + [sdk.shrines.Portal, new Shrine("Portal")], + [sdk.shrines.Gem, new Shrine("Gem")], + [sdk.shrines.Fire, new Shrine("Fire")], + [sdk.shrines.Monster, new Shrine("Monster")], + [sdk.shrines.Exploding, new Shrine("Exploding")], + [sdk.shrines.Poison, new Shrine("Poison")], ]); return { From 224343f15692f68118e731798369fde86714b15e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 18:51:39 -0400 Subject: [PATCH 620/758] Add Starter.LocationEvents.charSelectConnecting & Fix type in singleplayer check for findCharacter --- d2bs/kolbot/libs/OOG.js | 33 ++++++++++++++++++++++++++++++++- d2bs/kolbot/sdk/globals.d.ts | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 0e5137bfa..a2b3579d3 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -245,7 +245,7 @@ includeIfNotIncluded("core/Me.js"); const matchLadderString = function (text) { return text.includes(ladderString); }; - const singlePlayer = ![sdk.game.gametype.OpenBattlenet, sdk.game.gametype.BattleNet].includes(Profile().type); + const singlePlayer = ![sdk.game.profiletype.OpenBattlenet, sdk.game.profiletype.Battlenet].includes(Profile().type); // offline doesn't have a character limit cap const cap = singlePlayer ? 999 : 24; let count = 0; @@ -260,6 +260,24 @@ includeIfNotIncluded("core/Me.js"); 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.LocationEvents.charSelectConnecting()) { + D2Bot.printToConsole("Stuck at connecting screen"); + D2Bot.restart(); + } + } + // start from beginning of the char list startFromTop && sendKey(sdk.keys.code.Home); @@ -1807,6 +1825,19 @@ includeIfNotIncluded("core/Me.js"); } else { Controls.OtherMultiplayerCancel.click(); } + }, + + 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; + } } }; })(), diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index d829b2a3b..8695478ca 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1502,6 +1502,7 @@ declare global { openJoinGameWindow(): void; login(otherMultiCheck?: boolean): boolean; otherMultiplayerSelect(): void; + charSelectConnecting(): boolean; }; } From 0276107ee7839e82f8cd31c2ed3f16279eca04ee Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 20:07:12 -0400 Subject: [PATCH 621/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index b4d153a66..8bdb913da 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit b4d153a667fc4716ff409c10902d183a8e9181fa +Subproject commit 8bdb913da5ff39191f5c2473f7237bf3f12cf589 From 3f48e0809d06ced246c3d1347053b35295693e14 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Jul 2025 00:55:35 -0400 Subject: [PATCH 622/758] Fix `findCharacter` for singleplayer & add missing serverdown location - Update type info for global `Profile` method --- d2bs/kolbot/libs/OOG.js | 2 +- d2bs/kolbot/libs/oog/Locations.js | 13 ++++++++++--- d2bs/kolbot/sdk/globals.d.ts | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index a2b3579d3..202cf7bf2 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -293,7 +293,7 @@ includeIfNotIncluded("core/Me.js"); count++; if (String.isEqual(text[1], info.charName)) { - if (info.ladder && !text.some(matchLadderString)) continue; + if (!singlePlayer && info.ladder && !text.some(matchLadderString)) continue; // how to check hardcore? return control; } diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index 4ee5c974b..9ec7c8019 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -55,6 +55,7 @@ [sdk.game.locations.TcpIpEnterIp, function () { Controls.PopupNo.click(); + // Controls.TcpIpCancel.click(); } ], [sdk.game.locations.MainMenu, @@ -350,6 +351,13 @@ Starter.LocationEvents.openCreateGameWindow(); } ], + [ + sdk.game.locations.ServerDown, + function () { + ControlAction.timeoutDelay("Server Down", Time.minutes(5)); + Controls.OkCentered.click(); + } + ], ]); addLocations([sdk.game.locations.PreSplash, sdk.game.locations.SplashScreen], function (location) { @@ -395,9 +403,8 @@ } ); addLocations([sdk.game.locations.Disconnected, sdk.game.locations.LobbyLostConnection], - function () { - D2Bot.updateStatus("Disconnected/LostConnection"); - delay(1000); + function (loc) { + ControlAction.timeoutDelay(loc === sdk.game.locations.Disconnected ? "Disconnected" : "Lost Connection", 3000); Controls.OkCentered.click(); } ); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 8695478ca..70a002fdc 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1101,7 +1101,7 @@ declare global { maxloginTime: number; maxCharacterSelectTime: number; }; - function Profile(): Profile; + function Profile(): D2BotProfile; class SQLite { constructor(database: string, isNew: boolean); From 9e2ee6dca616ce201919e8a24fc7387af4e711e8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 24 Jul 2025 01:20:11 -0400 Subject: [PATCH 623/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 8bdb913da..c351f95b8 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 8bdb913da5ff39191f5c2473f7237bf3f12cf589 +Subproject commit c351f95b813605df4f3f64ec6ae460b13227392e From c61005d0703c09245d9b8bbed8058182ccdc95ed Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Sat, 26 Jul 2025 17:46:43 +0100 Subject: [PATCH 624/758] fix: Fix missing variable declaration (#455) --- d2bs/kolbot/libs/core/Common/Cows.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/core/Common/Cows.js b/d2bs/kolbot/libs/core/Common/Cows.js index f0ed26e4f..5a3dc56a4 100644 --- a/d2bs/kolbot/libs/core/Common/Cows.js +++ b/d2bs/kolbot/libs/core/Common/Cows.js @@ -101,6 +101,7 @@ } } + let room; RoomLoop: while (rooms.length > 0) { // get the first room + initialize myRoom var From 6c3485ea8cf30e17cfc4d6aaeb5bdf768b96ef03 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:45:04 -0400 Subject: [PATCH 625/758] fix: Replace usage of deprecated `Town.clearBelt` & usage of global print --- d2bs/kolbot/D2BotCleaner.dbj | 2 +- d2bs/kolbot/default.dbj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index 6173a7c20..bf4d00073 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -190,7 +190,7 @@ function deleteAllCharacters () { let info = { charName: character }; if (CharactersToExclude.includes(character)) continue; if (!ControlAction.deleteCharacter(info)) { - print("failed to delete character " + character); + console.error("failed to delete character " + character); return false; } delay(500); diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index 85256fb74..b577c74b2 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -180,7 +180,7 @@ function main () { } if (Config.FastPick) { - print("ÿc2Fast pickit active."); + console.log("ÿc2Fast pickit active."); addEventListener("itemaction", Pickit.itemEvent); } @@ -192,7 +192,7 @@ function main () { // main checks Cubing.cursorCheck(); Town.getCorpse(); - Town.clearBelt(); + me.clearBelt(); Pather.init(); // initialize wp data let { x, y } = me; From d7cd18f779cd4cf94d8a994605380b88b53d2441 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:46:15 -0400 Subject: [PATCH 626/758] Update default.dbj - Always use automap when debug path is active --- d2bs/kolbot/default.dbj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/default.dbj b/d2bs/kolbot/default.dbj index b577c74b2..76171e5dc 100644 --- a/d2bs/kolbot/default.dbj +++ b/d2bs/kolbot/default.dbj @@ -213,7 +213,7 @@ function main () { } } - me.automap = Config.AutoMap; + me.automap = Config.AutoMap || Config.DebugMode.Path; // Next game = drop keys TorchSystem.keyCheck() && scriptBroadcast("torch"); From dda0d380ce9217d9af0beda936f9e83680a42198 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:50:04 -0400 Subject: [PATCH 627/758] fix: Refactor buildCowRooms to improve readability - Update JSDOC definitions - Added path path debug settings - Move `room` declaration out of while loop --- d2bs/kolbot/libs/core/Common/Cows.js | 66 +++++++++++++++++++++------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Cows.js b/d2bs/kolbot/libs/core/Common/Cows.js index 5a3dc56a4..953d21ac7 100644 --- a/d2bs/kolbot/libs/core/Common/Cows.js +++ b/d2bs/kolbot/libs/core/Common/Cows.js @@ -13,7 +13,7 @@ this._kingCoords = null; this.buildCowRooms = function () { - /** @type {Partial[]} */ + /** @type {[number, number, Room][]} */ const finalRooms = []; /** @type {Set} */ const indexes = new Set(); @@ -22,16 +22,27 @@ this._kingCoords = kingPreset.realCoords(); /** @type {Room[]} */ const badRooms = getRoom(this._kingCoords.x, this._kingCoords.y).getNearby(); + /** + * @param {Room} room + * @returns {string} + */ + const roomKey = function (room) { + return room.x + ":" + room.y; + }; + + for (let room of badRooms) { + this._badRooms.push(room); + if (Config.DebugMode.Path) { + CollMap.drawRoom(room, "red"); + } + const badRooms2 = room.getNearby(); - for (let i = 0; i < badRooms.length; i += 1) { - this._badRooms.push(badRooms[i]); - CollMap.drawRoom(badRooms[i], "red"); - let badRooms2 = badRooms[i].getNearby(); - - for (let j = 0; j < badRooms2.length; j += 1) { - if (!indexes.has(badRooms2[j].x + "" + badRooms2[j].y)) { - CollMap.drawRoom(badRooms2[j], "white"); - indexes.add(badRooms2[j].x + "" + badRooms2[j].y); + for (let badRoom of badRooms2) { + if (!indexes.has(roomKey(badRoom))) { + if (Config.DebugMode.Path) { + CollMap.drawRoom(badRoom, "white"); + } + indexes.add(roomKey(badRoom)); } } } @@ -39,8 +50,8 @@ let room = getRoom(); do { - if (!indexes.has(room.x + "" + room.y)) { - finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2]); + if (!indexes.has(roomKey(room))) { + finalRooms.push([room.x * 5 + room.xsize / 2, room.y * 5 + room.ysize / 2, copyObj(room)]); } } while (room.getNext()); @@ -49,6 +60,11 @@ // add soloplays kingTracker? this.clearCowLevel = function () { + /** + * @param {Room} a + * @param {Room} b + * @returns {number} + */ function roomSort(a, b) { const aSeen = seen.has(JSON.stringify(a)); const bSeen = seen.has(JSON.stringify(b)); @@ -64,10 +80,22 @@ Config.MFLeader && Pather.makePortal() && say("cows"); + let count = 0; + /** @type {[number, number]} */ let myRoom; + /** @type {Partial} */ + let room; /** @type {Set} */ const seen = new Set(); - let rooms = this.buildCowRooms(); + const rooms = this.buildCowRooms(); + + /** @type {Text[]} */ + const hooks = []; + + /** @param {Text} hook */ + const clearHook = function (hook) { + hook && hook.remove(); + }; // Check if the starting room is in badRooms if (!Pather.useTeleport()) { @@ -101,7 +129,6 @@ } } - let room; RoomLoop: while (rooms.length > 0) { // get the first room + initialize myRoom var @@ -118,9 +145,9 @@ } rooms.sort(roomSort); - let room = rooms.shift(); + room = rooms.shift(); seen.add(JSON.stringify(room)); - let result = Pather.getNearestWalkable(room[0], room[1], 10, 2); + const result = Pather.getNearestWalkable(room[0], room[1], 10, 2); if (result) { if (!Pather.useTeleport()) { @@ -149,11 +176,18 @@ } } } + if (Config.DebugMode.Path) { + CollMap.drawRoom(room[2], "green"); + hooks.push(new Text((++count).toString(), room[0], room[1], 2, 1, null, true)); + } Pather.moveTo(result[0], result[1], 3); if (!Attack.clear(30)) return false; } } + CollMap.removeHooks(); + hooks.forEach(clearHook); + return true; }; }; From 671968b6776eb1a51aa709611dfdd1e8c7c4be31 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:55:48 -0400 Subject: [PATCH 628/758] Update ShopBot.js - Add nip line that triggered shopping --- d2bs/kolbot/libs/scripts/ShopBot.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/ShopBot.js b/d2bs/kolbot/libs/scripts/ShopBot.js index 0bcdd6801..86ffeb6e1 100644 --- a/d2bs/kolbot/libs/scripts/ShopBot.js +++ b/d2bs/kolbot/libs/scripts/ShopBot.js @@ -171,18 +171,21 @@ const ShopBot = new Runnable( validItems += items.length; overlayText.frequency.text = "Valid base items / cycle: " + ((validItems / totalCycles).toFixed(2).toString()); - for (let i = 0; i < items.length; i += 1) { - if (Storage.Inventory.CanFit(items[i]) && Pickit.canPick(items[i]) && - me.gold >= items[i].getItemCost(sdk.items.cost.ToBuy) && - NTIP.CheckItem(items[i], pickEntries) + for (let item of items) { + const rval = NTIP.CheckItem(item, pickEntries, true); + if (!rval.result) continue; + + if (Storage.Inventory.CanFit(item) + && Pickit.canPick(item) + && me.gold >= item.getItemCost(sdk.items.cost.ToBuy) ) { beep(); D2Bot.printToConsole("Match found!", sdk.colors.D2Bot.DarkGold); delay(1000); if (npc.startTrade(menuId)) { - Item.logItem("Shopped", items[i]); - items[i].buy(); + Item.logItem("Shopped", item, rval.line); + item.buy(); bought = true; } From ef03813371e8c1e582b2670c162b4af76262e078 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Jul 2025 17:23:16 -0400 Subject: [PATCH 629/758] Minor cleanups --- d2bs/kolbot/libs/core/Common/Tools.js | 6 +++--- d2bs/kolbot/libs/core/NTItemParser.js | 2 ++ d2bs/kolbot/libs/core/Skill.js | 13 ++++++++++++- d2bs/kolbot/sdk/globals.d.ts | 1 + 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Tools.js b/d2bs/kolbot/libs/core/Common/Tools.js index c1f4550f5..ebd1c48cc 100644 --- a/d2bs/kolbot/libs/core/Common/Tools.js +++ b/d2bs/kolbot/libs/core/Common/Tools.js @@ -206,7 +206,7 @@ break; } - let pottype = (() => { + const pottype = (() => { switch (type) { case this.pots.Health: case this.pots.MercHealth: @@ -218,14 +218,14 @@ } })(); - let potion = this.getPotion(pottype, type); + const potion = this.getPotion(pottype, type); if (potion) { if (me.dead) return false; if (me.mode === sdk.player.mode.SkillActionSequence) { while (me.mode === sdk.player.mode.SkillActionSequence) { - delay (25); + delay(3); } } diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 74ce96568..ab46c3b90 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -227,9 +227,11 @@ NTIP.GetMercTier = NTIP.generateTierFunc("Merctier"); * @param {ItemUnit} item * @param {[(item) => Boolean, (item) => Boolean, (item) => Boolean]} entryList * @param {boolean} verbose + * @returns {number|{line: string, result: number}} */ NTIP.CheckItem = function (item, entryList, verbose) { let i, num; + /** @type {{ line: string, result: number }} */ let rval = {}; let result = 0; diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index c12cb868a..01e2d2d88 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -599,7 +599,18 @@ } }, - // Cast a skill on self, Unit or coords + /** + * Cast a skill on self, Unit or coords + * @param {number} skillId + * @param {number} hand + * @param {number | Unit | PathNode} x + * @param {number} [y] + * @param {ItemUnit} [item] + * @param {number} [weaponSlot] + * @returns {boolean} + * + * @todo Track skills cast so we can determine most used, skills, mana expenditure, etc. + */ cast: function (skillId, hand, x, y, item, weaponSlot = -1) { if (skillId === undefined) throw new Error("Unit.cast: Must supply a skill ID"); const switchWeapons = weaponSlot > -1; diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 70a002fdc..691948d85 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -766,6 +766,7 @@ declare global { readonly IAS: number; readonly shapeshifted: boolean; readonly attacking: boolean; + readonly size: number; /** * @description max gold capacity (cLvl * 10000) */ From 1d57343cc96d6d89f3f9d21cf0ec52371f1fc5fb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 27 Jul 2025 17:27:27 -0400 Subject: [PATCH 630/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index c351f95b8..e6621e010 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit c351f95b813605df4f3f64ec6ae460b13227392e +Subproject commit e6621e010250628db7b1be6a6ec5ce46a6106d8e From c8b067390ceef7df0c7f0863f82c5415b9e37eb8 Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Sat, 23 Aug 2025 13:47:07 +0100 Subject: [PATCH 631/758] fix: Pick correct stairs unit when there are multiple (#459) --- d2bs/kolbot/libs/core/Pather.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 346233553..c504f6f70 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1348,6 +1348,12 @@ const Pather = { ); } + // There might be multiple stairs with the same class id nearby. + // Given we've walked to the stairs, pick the closest one. + if (type === sdk.unittype.Stairs) { + unit = getUnits(type, id).sort(Sort.units).first() || unit; + } + return unit.useUnit(targetArea); }, From 3874ad00d8c5bfed20d9a8707e8c4a979a22fe5d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 7 Sep 2025 01:50:43 -0400 Subject: [PATCH 632/758] Create CharRefresher system - Similar to Mulelogger but just brings character to lobby - Create new system to allow easily refreshing expiry timers on accounts/characters. Brings characters to lobby and waits configurable time --- +setup/charrefresher/RefresherConfig.js | 34 ++ +setup/setup.ps1 | 4 +- .gitignore | 1 + d2bs/kolbot/D2BotCharRefresher.dbj | 318 ++++++++++++++++++ .../systems/charrefresher/CharRefresher.d.ts | 20 ++ .../systems/charrefresher/CharRefresher.js | 36 ++ 6 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 +setup/charrefresher/RefresherConfig.js create mode 100644 d2bs/kolbot/D2BotCharRefresher.dbj create mode 100644 d2bs/kolbot/libs/systems/charrefresher/CharRefresher.d.ts create mode 100644 d2bs/kolbot/libs/systems/charrefresher/CharRefresher.js diff --git a/+setup/charrefresher/RefresherConfig.js b/+setup/charrefresher/RefresherConfig.js new file mode 100644 index 000000000..1c0afdbdb --- /dev/null +++ b/+setup/charrefresher/RefresherConfig.js @@ -0,0 +1,34 @@ +/** +* @filename RefresherConfig.js +* @author theBGuy +* @desc Configuration file for CharRefresher system +* +*/ + +(function (module) { + module.exports = { + LobbyTime: [15, 30], // (30, 60) to if you want to be less likely to get disconnected in the lobby + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + RefreshAccounts: { + /* Format: + "account1/password1/realm": ["charname1", "charname2 etc"], + "account2/password2/realm": ["charnameX", "charnameY etc"], + "account3/password3/realm": ["all"] + + To refresh a full account, put "account/password/realm": ["all"] + + realm = useast, uswest, europe or asia + + Enter Individual entries are separated with a comma below + */ + "exampleAcc/pa33word3/realm": ["all"], + }, + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + + // D2BotCharRefresher specific settings - for global settings see libs/starter/StarterConfig.js + StarterConfig: { + // none needed right now + } + }; +})(module); diff --git a/+setup/setup.ps1 b/+setup/setup.ps1 index 836c7fe33..4fba44ec7 100644 --- a/+setup/setup.ps1 +++ b/+setup/setup.ps1 @@ -67,7 +67,8 @@ $dirs = @( "$SYSTEMS_DIR\gameaction", "$SYSTEMS_DIR\mulelogger", "$SYSTEMS_DIR\pubjoin", - "$SYSTEMS_DIR\torch" + "$SYSTEMS_DIR\torch", + "$SYSTEMS_DIR\charrefresher" ) foreach ($dir in $dirs) { if (-not (Test-Path $dir)) { @@ -112,6 +113,7 @@ Copy-IfNotExists "$SETUP_DIR\gameaction\GameActionConfig.js" "$SYSTEMS_DIR\gamea Copy-IfNotExists "$SETUP_DIR\mulelogger\LoggerConfig.js" "$SYSTEMS_DIR\mulelogger\LoggerConfig.js" "LoggerConfig.js to mulelogger directory" Copy-IfNotExists "$SETUP_DIR\pubjoin\PubJoinConfig.js" "$SYSTEMS_DIR\pubjoin\PubJoinConfig.js" "PubJoinConfig.js to pubjoin directory" Copy-IfNotExists "$SETUP_DIR\torch\FarmerConfig.js" "$SYSTEMS_DIR\torch\FarmerConfig.js" "FarmerConfig.js to torch directory" +Copy-IfNotExists "$SETUP_DIR\charrefresher\RefresherConfig.js" "$SYSTEMS_DIR\charrefresher\RefresherConfig.js" "RefresherConfig.js to charrefresher directory" Write-Host "System configuration files processed successfully!" Write-Host "" diff --git a/.gitignore b/.gitignore index b8538d3a9..d7528dd18 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,4 @@ d2bs/kolbot/libs/starter/AdvancedConfig.js d2bs/kolbot/libs/starter/StarterConfig.js d2bs/kolbot/libs/systems/gameaction/GameActionConfig.js d2bs/kolbot/libs/systems/automule/config/StarterConfig.js +d2bs/kolbot/libs/systems/charrefresher/RefresherConfig.js diff --git a/d2bs/kolbot/D2BotCharRefresher.dbj b/d2bs/kolbot/D2BotCharRefresher.dbj new file mode 100644 index 000000000..c5baee073 --- /dev/null +++ b/d2bs/kolbot/D2BotCharRefresher.dbj @@ -0,0 +1,318 @@ +/** +* @filename D2BotCharRefresher.dbj +* @author kolton, theBGuy +* @desc Entry script for CharRefresher.js +* +* @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/charrefresher/CharRefresher")} +*/ + +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +include("systems/charrefresher/CharRefresher.js"); + +if (DataFile.init()) { + Starter.firstRun = true; +} + +/** @type {string[]} */ +const accounts = []; +/** @type {string[]} */ +const chars = []; + +const parseInfo = function () { + if (!FileTools.exists("libs/systems/charrefresher/RefresherConfig.js")) { + console.error("Could not find RefresherConfig.js, please run setup.bat to get RefresherConfig.js and edit it to your needs."); + D2Bot.printToConsole("Could not find RefresherConfig.js, please run setup.bat to get RefresherConfig.js and edit it to your needs."); + D2Bot.stop(me.profile, true); + + return; + } + + const { RefreshAccounts, StarterConfig } = require("./libs/systems/charrefresher/RefresherConfig"); + Object.assign(Starter.Config, StarterConfig); + + for (let i in RefreshAccounts) { + if (RefreshAccounts.hasOwnProperty(i) && typeof i === "string") { + accounts.push(i); + chars.push(RefreshAccounts[i]); + } + } +}; + +const locationAction = (function () { + let currAcc; + /** @type {string[]} */ + let charList = []; + /** @type {{ currAcc: string, currChar: string }} */ + let obj = {}; + + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations + } = require("./libs/oog/Locations"); + + const getLobbyTime = function() { + if (Array.isArray(CharRefresher.LobbyTime) && CharRefresher.LobbyTime.length === 2) { + return Time.seconds(rand(CharRefresher.LobbyTime[0], CharRefresher.LobbyTime[1])); + } + return Time.seconds(CharRefresher.LobbyTime); + }; + + addLocations([sdk.game.locations.Lobby, sdk.game.locations.LobbyChat, sdk.game.locations.CreateGame], + function () { + D2Bot.updateStatus("Lobby"); + + if (Starter.inGame) { + // Should never hit this as this isn't meant to go in games but just in case + if (getTickCount() - Starter.gameStart < Starter.Config.MinGameTime * 1e3) { + ControlAction.timeoutDelay( + "Min game time wait", + Starter.Config.MinGameTime * 1e3 + Starter.gameStart - getTickCount() + ); + } + + console.log("updating runs"); + D2Bot.updateRuns(); + delay(1000); + + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + Controls.LobbyQuit.click(); + + return; + } + + ControlAction.timeoutDelay( + "Character refresh delay", + getLobbyTime() + ); + + Controls.LobbyQuit.click(); + } + ); + addLocations( + [ + sdk.game.locations.JoinGame, + sdk.game.locations.Ladder, + sdk.game.locations.ChannelList, + ], + function () { + Starter.LocationEvents.openCreateGameWindow(); + } + ); + addLocations( + [ + sdk.game.locations.MainMenu, + sdk.game.locations.Login, + sdk.game.locations.SplashScreen, + ], + function () { + if (!accounts.length) { + CharRefresher.remove(); + D2Bot.printToConsole("Done refreshing chars!"); + D2Bot.stop(me.profile, true); + + return; + } + + if (FileTools.exists("logs/CharRefresher.json")) { + /** @type {{ currAcc: string, currChar: string }} */ + obj = JSON.parse(FileTools.readText("logs/CharRefresher.json")); + + if (obj.currAcc) { + for (let i = 0; i < accounts.length; i += 1) { + if (accounts[i].split("/")[0] === obj.currAcc) { + accounts.splice(0, i); + chars.splice(0, i); + i -= 1; + + break; + } + } + } + } + + currAcc = accounts[0]; + currAcc = currAcc.split("/"); + charList = chars[0]; + obj.currAcc = currAcc[0]; + + console.log("ÿc4CharRefresherÿc2: Login account: " + currAcc[0]); + + if (!(currAcc[2] in ControlAction.realms)) { + console.log("ÿc4CharRefresherÿc2: Invalid realm: " + currAcc[2] + ", skipping account"); + D2Bot.printToConsole("Invalid realm: " + currAcc[2] + ", skipping account", sdk.colors.D2Bot.Gray); + accounts.shift(); + chars.shift(); + + return; + } + + CharRefresher.save(md5(currAcc[2].toLowerCase() + currAcc[0].toLowerCase()), currAcc[1]); + + if (ControlAction.loginAccount({ account: currAcc[0], password: currAcc[1], realm: currAcc[2] })) { + FileTools.writeText("logs/CharRefresher.json", JSON.stringify(obj)); + accounts.shift(); // remove current account from the list + } + } + ); + locations.set(sdk.game.locations.CharSelect, + function () { + // Single Player screen fix + if (getLocation() === sdk.game.locations.CharSelect + && !Controls.CharSelectCurrentRealm.control + && Controls.BottomLeftExit.click()) { + return; + } + + if (!charList.length && Controls.BottomLeftExit.click()) { + return; + } + + charList[0] === "all" && (charList = ControlAction.getCharacters()); + charList[0] === "first" && (charList = ControlAction.getCharacters().slice(0, 1)); + + if (FileTools.exists("logs/CharRefresher.json")) { + /** @type {{ currAcc: string, currChar: string }} */ + obj = JSON.parse(FileTools.readText("logs/CharRefresher.json")); + + if (obj.currChar) { + for (let i = 0; i < charList.length; i += 1) { + if (charList[i] === obj.currChar) { + // Remove the previous currChar as well + charList.splice(0, i + 1); + + break; + } + } + } + } + + // last char in acc = trigger next acc + if (!charList.length) { + console.log("No more characters"); + accounts.shift(); // remove current account from the list + chars.shift(); + + return; + } + + let currChar = charList.shift(); + obj.currChar = currChar; + + console.log("ÿc4CharRefresherÿc2: Login character: " + currChar); + FileTools.writeText("logs/CharRefresher.json", JSON.stringify(obj)); + + ControlAction.timeoutDelay( + "Character select delay", + Time.seconds(rand(1, 5)) + ); + + ControlAction.loginCharacter({ charName: currChar }); + } + ); + addLocations([sdk.game.locations.CharSelectConnecting, sdk.game.locations.CharSelectNoChars], + function () { + if (!Starter.LocationEvents.charSelectError()) { + accounts.shift(); // remove current account from the list + chars.shift(); + } + } + ); + locations.set(sdk.game.locations.OtherMultiplayer, + function () { + Controls.OtherMultiplayerCancel.click(); + } + ); + locations.set(sdk.game.locations.TcpIp, + function () { + Controls.TcpIpCancel.click(); + } + ); + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; +})(); + +function main () { + addEventListener("copydata", Starter.receiveCopyData); + + while (!Starter.handle) { + delay(100); + } + + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); + + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } + + if (Starter.gameInfo.rdBlocker) { + D2Bot.printToConsole("You must disable RD Blocker for CharRefresher to work properly. Stopping."); + D2Bot.stop(me.profile, true); + + return; + } + + parseInfo(); + + if (Starter.gameInfo.error) { + if (DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, + sdk.colors.D2Bot.Gray + ); + } + + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + D2Bot.updateRuns(); + } + + DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); + + 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) { + if (!Starter.inGame) { + console.log("Updating Status"); + Starter.lastGameStatus = "ingame"; + Starter.inGame = true; + Starter.gameStart = getTickCount(); + DataFile.updateStats("runs", Starter.gameCount); + } + + D2Bot.updateStatus("Game: " + me.gamename + Starter.timer(Starter.gameStart)); + } + + delay(1000); + } + + locationAction.run(getLocation()); + delay(1000); + } +} diff --git a/d2bs/kolbot/libs/systems/charrefresher/CharRefresher.d.ts b/d2bs/kolbot/libs/systems/charrefresher/CharRefresher.d.ts new file mode 100644 index 000000000..9aec1c5fc --- /dev/null +++ b/d2bs/kolbot/libs/systems/charrefresher/CharRefresher.d.ts @@ -0,0 +1,20 @@ +export interface CharRefresherType { + LobbyTime: number | number[]; + /** + * @param hash - The hash value. + * @returns The loaded data. + */ + load(hash: string): string; + + /** + * @param hash - The hash value. + * @param data - The data to save. + */ + save(hash: string, data: string): void; + + remove(): void; +} + +declare global { + const CharRefresher: CharRefresherType; +} diff --git a/d2bs/kolbot/libs/systems/charrefresher/CharRefresher.js b/d2bs/kolbot/libs/systems/charrefresher/CharRefresher.js new file mode 100644 index 000000000..318189ed7 --- /dev/null +++ b/d2bs/kolbot/libs/systems/charrefresher/CharRefresher.js @@ -0,0 +1,36 @@ +/** +* @filename CharRefresher.js +* @author theBGuy +* @desc Refresh characters so they don't expire, for setup @see RefresherConfig.js +* +* @typedef {import("../../../sdk/globals")} +*/ + +/** @type {import("./CharRefresher").CharRefresherType} */ +const CharRefresher = { + LobbyTime: [15, 30], + /** + * @param {string} hash + * @returns {string} + */ + load: function (hash) { + let filename = "data/secure/" + hash + ".txt"; + if (!FileTools.exists(filename)) { + throw new Error("File " + filename + " does not exist!"); + } + return FileTools.readText(filename); + }, + + /** + * @param {string} hash + * @param {string} data + */ + save: function (hash, data) { + let filename = "data/secure/" + hash + ".txt"; + FileTools.writeText(filename, data); + }, + + remove: function () { + FileTools.remove("logs/CharRefresher.json"); + }, +}; From 0ad36db9e57db171e9a61de22b4665ae3cca6c3d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 7 Sep 2025 02:00:08 -0400 Subject: [PATCH 633/758] Add Starter.JoinGameDelay to help prevent disconnect opening join window after games --- +setup/starter/StarterConfig.js | 1 + d2bs/kolbot/D2BotFollow.dbj | 7 +++++++ d2bs/kolbot/sdk/globals.d.ts | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/+setup/starter/StarterConfig.js b/+setup/starter/StarterConfig.js index 5cb1ec735..2a03b5cfd 100644 --- a/+setup/starter/StarterConfig.js +++ b/+setup/starter/StarterConfig.js @@ -26,6 +26,7 @@ AnnounceMessage: "", // Message to announce before making game AfterGameMessage: "", // Default message after a finished game. Can be an array of messages + JoinGameDelay: 3, // Seconds to wait before opening the join game window after a game InvalidPasswordDelay: 10, // Minutes to wait after getting Invalid Password message VersionErrorDelay: rand(5, 30), // Seconds to wait after 'unable to identify version' message SwitchKeyDelay: 5, // Seconds to wait before switching a used/banned key or after realm down diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 3949d8954..72da8adb0 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -4,6 +4,9 @@ * @desc Entry script for following bots running on the same pc * * @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/torch/TorchSystem")} +* @typedef {import("./libs/systems/crafting/CraftingSystem")} +* @typedef {import("./libs/systems/gambling/Gambling")} */ include("critical.js"); // required @@ -101,6 +104,10 @@ const locationAction = (function () { Starter.gameCount += 1; Starter.lastGameStatus = "ready"; Starter.inGame = false; + + if (typeof Starter.Config.JoinGameDelay === "number" && Starter.Config.JoinGameDelay > 0) { + ControlAction.timeoutDelay("Join Game Delay", Starter.Config.JoinGameDelay * 1e3); + } } Starter.LocationEvents.openJoinGameWindow(); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 691948d85..051be1cfa 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1366,6 +1366,10 @@ declare global { AnnounceMessage: string; AfterGameMessage: string; + /** + * Seconds to wait before opening the join game window after a game + */ + JoinGameDelay: number; InvalidPasswordDelay: number; // Minutes to wait after getting Invalid Password message VersionErrorDelay: number; // Seconds to wait after 'unable to identify version' message SwitchKeyDelay: number; // Seconds to wait before switching a used/banned key or after realm down From a2bb781b8bdf49cdc1375109685ee66f85977a40 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 20 Sep 2025 14:59:28 -0400 Subject: [PATCH 634/758] Fix includes for PickThread --- d2bs/kolbot/libs/manualplay/threads/PickThread.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/threads/PickThread.js b/d2bs/kolbot/libs/manualplay/threads/PickThread.js index e265a76bc..98203ae3e 100644 --- a/d2bs/kolbot/libs/manualplay/threads/PickThread.js +++ b/d2bs/kolbot/libs/manualplay/threads/PickThread.js @@ -5,18 +5,12 @@ * */ js_strict(true); - -include("json2.js"); // required? -include("polyfill.js"); // required -include("oog/D2Bot.js"); // required +include("critical.js"); // globals needed for core gameplay -// todo - figure out what here is actually needed for mapmode vs what is only required for bot mode -include("core/NTItemParser.js"); -include("core/Util"); includeCoreLibs(); -// system libs - same for here +// system libs includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); From cbf40da6fad7281c79cf91ab05c5d3530471e4e7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:16:04 -0400 Subject: [PATCH 635/758] Fix using sock.length instead of unit.sockets for Mulelogger - Was using the number of items socketed into a item instead of the total number of sockets for an item --- d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 731b92b07..80135397b 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -134,7 +134,7 @@ const MuleLogger = { + unit.itemType + ":" + unit.quality + ":" + unit.itemclass + ":" - + sock.length + ":" + + unit.sockets + ":" + unit.gfx + ":" + color + ":" ); From ea404ebb9c9b5ade00f7487f9b14d750f70f0a20 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:36:33 -0400 Subject: [PATCH 636/758] Move off wp when performing doRandomPrecast - Add Pather method `findSpotAtDistance` which allows finding a valid spot that is a certain distance from a node --- d2bs/kolbot/libs/core/Pather.js | 37 +++++++++++++++++++++++++++++++ d2bs/kolbot/libs/core/Precast.js | 10 ++++++++- d2bs/kolbot/sdk/types/Pather.d.ts | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index c504f6f70..9d6ddbb5e 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -311,6 +311,43 @@ const Pather = { || (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y })); }, + /** + * @param {PathNode} node + * @param {number} distance - desired distance from node + * @param {number} [maxAttempts=16] - number of angles to try + * @returns {PathNode | false} + */ + findSpotAtDistance: function (node, distance, maxAttempts = 16) { + if (!node) return false; + + const angleStep = 360 / maxAttempts; + + for (let i = 0; i < maxAttempts; i++) { + let angle = (i * angleStep) * Math.PI / 180; + let x = Math.round(node.x + Math.cos(angle) * distance); + let y = Math.round(node.y + Math.sin(angle) * distance); + + if (Pather.checkSpot(x, y, sdk.collision.BlockWalk)) { + return new PathNode(x, y); + } + } + + // If no exact distance spot found, try to find nearest walkable spot around the target distance + for (let radius = distance - 2; radius <= distance + 2; radius++) { + for (let i = 0; i < maxAttempts; i++) { + let angle = (i * angleStep) * Math.PI / 180; + let x = Math.round(node.x + Math.cos(angle) * radius); + let y = Math.round(node.y + Math.sin(angle) * radius); + + if (Pather.checkSpot(x, y, sdk.collision.BlockWalk)) { + return new PathNode(x, y); + } + } + } + + return false; + }, + /** * @typedef {object} pathSettings * @property {boolean} [allowNodeActions] diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index f918c2504..b0e5498e4 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -589,7 +589,15 @@ const Precast = (function () { try { // Only do this is you are a barb or actually have a cta. Otherwise its just a waste of time and you can precast in town if (Precast.needOutOfTownCast()) { - Pather.useWaypoint("random") && Precast.doPrecast(force); + if (Pather.useWaypoint("random")) { + let wp = Game.getObject("waypoint"); + let spot = Pather.findSpotAtDistance(wp, 8); + + if (spot) { + Pather.move(spot); + } + Precast.doPrecast(force); + } } else { Precast.doPrecast(force); } diff --git a/d2bs/kolbot/sdk/types/Pather.d.ts b/d2bs/kolbot/sdk/types/Pather.d.ts index c5c787dcd..bade1445b 100644 --- a/d2bs/kolbot/sdk/types/Pather.d.ts +++ b/d2bs/kolbot/sdk/types/Pather.d.ts @@ -39,6 +39,7 @@ declare global { let lastPortalTick: 0; let allowBroadcast: boolean; + function findSpotAtDistance(node: PathNode, distance: number, maxAttempts?: number): PathNode | false; function getWalkDistance( x: number, y: number, From 0f708695e3f0ddd9dcd12780490e02614e9211bd Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Sat, 27 Sep 2025 13:38:44 +0100 Subject: [PATCH 637/758] fix: Account for the fact modules are not in libs rather than lib folder (#461) Co-authored-by: theBGuy <60308670+theBGuy@users.noreply.github.com> --- d2bs/kolbot/libs/require.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/d2bs/kolbot/libs/require.js b/d2bs/kolbot/libs/require.js index 2ee91f833..4ef5146ac 100644 --- a/d2bs/kolbot/libs/require.js +++ b/d2bs/kolbot/libs/require.js @@ -52,6 +52,11 @@ global.require = (function (include, isIncluded, print, notify) { directory = "../" + directory; // Add a extra recursive path, as we start out of the lib directory } + // remove the / from it, in case it was libs/ (rather than lib/) and we now have a leading slash + if (directory.startsWith("/")) { + directory = directory.substr(1); + } + path = path || directory; let fullpath = removeRelativePath((path + field).replace(/\\/, "/")).toLowerCase(); @@ -59,6 +64,12 @@ global.require = (function (include, isIncluded, print, notify) { if (fullpath.startsWith("lib")) { fullpath = fullpath.substr(4); } + + // remove the / from it, in case it was libs/ (rather than lib/) and we now have a leading slash + if (fullpath.startsWith("/")) { + fullpath = fullpath.substr(1); + } + const packageName = fullpath; const asNew = this.__proto__.constructor === require && ((...args) => new (Function.prototype.bind.apply(modules[packageName].exports, args))); From 61cf5ce311412c2b1db2241bc36d14996fd9deed Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Oct 2025 19:17:32 -0400 Subject: [PATCH 638/758] Add base64 encoding/decoding module - Introduces a new base64.js module providing btoa and atob functions for base64 encoding and decoding, adapted for the Kolbot environment. --- d2bs/kolbot/libs/modules/external/base64.js | 62 +++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 d2bs/kolbot/libs/modules/external/base64.js diff --git a/d2bs/kolbot/libs/modules/external/base64.js b/d2bs/kolbot/libs/modules/external/base64.js new file mode 100644 index 000000000..703bb5cc3 --- /dev/null +++ b/d2bs/kolbot/libs/modules/external/base64.js @@ -0,0 +1,62 @@ +/** + * https://github.com/MaxArt2501/base64-js/blob/master/base64.js + * With modifications for Kolbot environment + */ + +(function (module) { + // base64 character set, plus padding character (=) + const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + // Regular expression to check formal correctness of base64 encoded strings + const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/; + + /** + * @param {string} string + */ + const btoa = function(string) { + string = String(string); + let bitmap, a, b, c, + result = "", i = 0, + rest = string.length % 3; // To determine the final padding + + for (; i < string.length;) { + if ((a = string.charCodeAt(i++)) > 255 + || (b = string.charCodeAt(i++)) > 255 + || (c = string.charCodeAt(i++)) > 255) {throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");} + + bitmap = (a << 16) | (b << 8) | c; + result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) + + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63); + } + + // If there's need of padding, replace the last 'A's with equal signs + return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result; + }; + + /** + * @param {string} string + */ + const atob = function(string) { + // atob can work with strings with whitespaces, even inside the encoded part, + // but only \t, \n, \f, \r and ' ', which can be stripped. + string = String(string).replace(/[\t\n\f\r ]+/g, ""); + if (!b64re.test(string)) {throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");} + + // Adding the padding if missing, for semplicity + string += "==".slice(2 - (string.length & 3)); + let bitmap, result = "", r1, r2, i = 0; + for (; i < string.length;) { + bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 + | (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++))); + + result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) + : r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) + : String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255); + } + return result; + }; + + module.exports = { + btoa: btoa, + atob: atob + }; +})(module); From 4926ac1766315e305939e1bd6da662301af53e3b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 25 Oct 2025 19:19:33 -0400 Subject: [PATCH 639/758] feat: Add `MuleLogger.dumpItemStats` and update `MuleLogger.logItem` - Added dumpItemStats method to MuleLogger for extracting detailed item stats. - Updated logItem to include a base64-encoded JSON dump of item properties and stats. - Adjusted type definitions in MuleLogger.d.ts and sdk/globals.d.ts to reflect new and corrected item property fields. --- .../libs/systems/mulelogger/MuleLogger.d.ts | 58 +++++---- .../libs/systems/mulelogger/MuleLogger.js | 120 ++++++++++++++++-- d2bs/kolbot/sdk/globals.d.ts | 4 +- 3 files changed, 146 insertions(+), 36 deletions(-) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts index 3d8a80b33..eb8404bbf 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.d.ts @@ -8,61 +8,65 @@ export interface MuleLoggerType { AutoPerm: boolean; IngameTime: number; LogAccounts: { [account: string]: string[] }; - + // Methods inGameCheck(): boolean; - + /** - * Save perm status to logs/MuleLogPermInfo.json. - * @param charPermInfo - The character's permanent status information. - */ + * Save perm status to logs/MuleLogPermInfo.json. + * @param charPermInfo - The character's permanent status information. + */ savePermedStatus(charPermInfo?: { charname: string; perm: boolean }): void; /** - * Load perm status from logs/MuleLogPermInfo.json. - * @returns The character's permanent status information. - */ + * Load perm status from logs/MuleLogPermInfo.json. + * @returns The character's permanent status information. + */ loadPermedStatus(): { charname: string; perm: boolean }; /** - * @param hash - The hash value. - * @returns The loaded data. - */ + * @param hash - The hash value. + * @returns The loaded data. + */ load(hash: string): string; /** - * @param hash - The hash value. - * @param data - The data to save. - */ + * @param hash - The hash value. + * @param data - The data to save. + */ save(hash: string, data: string): void; remove(): void; + dumpItemStats(item: ItemUnit): Record; + /** - * Log kept item stats in the manager. - * @param unit - The item unit. - * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. - * @returns The logged item information. - */ - logItem(unit: ItemUnit, logIlvl?: boolean): { + * Log kept item stats in the manager. + * @param unit - The item unit. + * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. + * @returns The logged item information. + */ + logItem( + unit: ItemUnit, + logIlvl?: boolean, + ): { itemColor: number; image: string; title: string; description: string; header: string; - sockets: any; + sockets: ItemUnit[]; }; /** - * Log character to D2Bot# itemviewer. - * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. - * @param logName - Log the character's name. Default: `LogNames` value. - * @param saveImg - Save the item image. Default: `SaveScreenShot` value. - */ + * Log character to D2Bot# itemviewer. + * @param logIlvl - Log the item's item level. Default: `LogItemLevel` value. + * @param logName - Log the character's name. Default: `LogNames` value. + * @param saveImg - Save the item image. Default: `SaveScreenShot` value. + */ logChar(logIlvl?: boolean, logName?: boolean, saveImg?: boolean): void; } declare global { const MuleLogger: MuleLoggerType; } -export {}; \ No newline at end of file diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 80135397b..9e4b87a4a 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -101,6 +101,88 @@ const MuleLogger = { FileTools.remove("logs/MuleLogPermInfo.json"); }, + /** + * @param {ItemUnit} item + * @returns {Record} + */ + dumpItemStats: function (item) { + const stats = item.getStat(-2); + const dump = {}; + + for (let i = 0; i < stats.length; i += 1) { + if (stats[i]) { + for (let n in NTIPAliasStat) { + let val; + + if (NTIPAliasStat.hasOwnProperty(n)) { + switch (typeof NTIPAliasStat[n]) { + case "number": + if (NTIPAliasStat[n] === i) { + switch (NTIPAliasStat[n]) { + case sdk.stats.ToBlock: + case sdk.stats.MinDamage: + case sdk.stats.MaxDamage: + case sdk.stats.SecondaryMinDamage: + case sdk.stats.SecondaryMaxDamage: + case sdk.stats.Defense: + case sdk.stats.AddClassSkills: + case sdk.stats.AddSkillTab: + case sdk.stats.ThrowMinDamage: + case sdk.stats.ThrowMaxDamage: + val = item.getStatEx(NTIPAliasStat[n]); + + if (val) { + dump[n] = val; + } + + break; + // poison damage stuff + case sdk.stats.PoisonMinDamage: + case sdk.stats.PoisonMaxDamage: + case sdk.stats.PoisonLength: + case sdk.stats.PoisonCount: + if (!dump.hasOwnProperty("poisondamage")) { + val = item.getStatEx(sdk.stats.PoisonMinDamage, 1); + + if (val) { + dump.poisondamage = val; + } + } + + break; + case sdk.stats.SkillOnAttack: + case sdk.stats.SkillOnStrike: + case sdk.stats.ChargedSkill: + if (stats[i]) { + dump[n] = stats[i].skill; + } + + break; + default: + if (stats[i][0]) { + dump[n] = stats[i][0]; + } + + break; + } + } + + break; + case "object": + val = item.getStatEx(NTIPAliasStat[n][0], NTIPAliasStat[n][1]); + if (val) { + dump[n] = val; + } + break; + } + } + } + } + } + + return dump; + }, + /** * Log kept item stats in the manager. * @param {ItemUnit} unit @@ -122,6 +204,35 @@ const MuleLogger = { const color = unit.getColor(); const code = Item.getItemCode(unit); const sock = unit.getItemsEx(); + const { btoa } = require("../../modules/external/base64"); + const itemInfo = { + id: unit.classid, + code: unit.code, + name: name, + prefix: unit.prefix, + suffix: unit.suffix, + prefixes: unit.prefixes, + suffixes: unit.suffixes, + prefixnum: unit.prefixnum, + suffixnum: unit.suffixnum, + prefixnums: unit.prefixnums, + suffixnums: unit.suffixnums, + itemType: unit.itemType, + itemClass: unit.itemclass, + quality: unit.quality, + sockets: unit.sockets, + gfx: unit.gfx, + color: color, + ilvl: unit.ilvl, + lvlreq: unit.lvlreq, + strreq: unit.strreq, + dexreq: unit.dexreq, + flags: unit.getFlags(), + ethereal: unit.getFlag(sdk.items.flags.Ethereal), + runeword: unit.getFlag(sdk.items.flags.Runeword), + stats: MuleLogger.dumpItemStats(unit) + }; + let desc = ( Item.getItemDesc(unit, logIlvl) + "$" + unit.gid + ":" @@ -129,14 +240,7 @@ const MuleLogger = { + unit.location + ":" + unit.x + ":" + unit.y + ":" - + (unit.getFlag(sdk.items.flags.Ethereal) ? "1" : "0") + ":" - + (unit.getFlag(sdk.items.flags.Runeword) ? "1" : "0") + ":" - + unit.itemType + ":" - + unit.quality + ":" - + unit.itemclass + ":" - + unit.sockets + ":" - + unit.gfx + ":" - + color + ":" + + btoa(JSON.stringify(itemInfo)) + ":" ); if (sock.length) { diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 051be1cfa..46b979dfe 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -542,11 +542,13 @@ declare global { // todo define item modes public readonly type: ItemType; readonly code: string; + readonly prefix?: string; + readonly suffix?: string; readonly prefixes: string[]; readonly suffixes: string[]; readonly prefixnum: number; readonly suffixnum: number; - readonly prefixenums: number[]; + readonly prefixnums: number[]; readonly suffixnums: number[]; readonly fname: string; readonly quality: number; From bf777e060808348de04374d5257a36dc89f9d4c3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 1 Nov 2025 00:56:52 -0400 Subject: [PATCH 640/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index e6621e010..4492d3fa4 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit e6621e010250628db7b1be6a6ec5ce46a6106d8e +Subproject commit 4492d3fa4ac137217d502601c6969b5ed014c41a From 8358128d0a96ad6285f8ff2ce0e0a84226572122 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 1 Nov 2025 20:32:54 -0400 Subject: [PATCH 641/758] fix: Correct the ancients classids --- d2bs/kolbot/libs/modules/sdk.js | 6 +- d2bs/kolbot/sdk/types/sdk.d.ts | 396 +++++++++++++++++++++++--------- 2 files changed, 290 insertions(+), 112 deletions(-) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 2a32f6aa6..27d83384f 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -2505,9 +2505,9 @@ Griswold: 365, Izual: 256, Hephasto: 409, - KorlictheProtector: 540, - TalictheDefender: 541, - MadawctheGuardian: 542, + TalictheDefender: 540, + MadawctheGuardian: 541, + KorlictheProtector: 542, ListerTheTormenter: 571, TheCowKing: 743, // 773? ColdwormtheBurrower: 284, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 301fd8892..e96ce2249 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -19,7 +19,7 @@ declare global { } export namespace party { - const NoParty: 65535 + const NoParty: 65535; namespace flag { const Invite: 0; const InParty: 1; @@ -42,7 +42,7 @@ declare global { const Right: 1; const ShiftLeft: 2; // For belt const MercFromBelt: 3; // For belt - const Mercenary: 4 // Give to merc + const Mercenary: 4; // Give to merc } namespace map { const LeftDown: 0; @@ -55,7 +55,7 @@ declare global { } namespace shift { const NoShift: 0; - const Shift: 1 + const Shift: 1; } } @@ -86,8 +86,8 @@ declare global { const FriendlyNPC: 0x2000; const Unknown2: 0x4000; const DeadBodies: 0x8000; - const MonsterObject: 0xFFFF; - const BlockMissile: 0x80E; + const MonsterObject: 0xffff; + const BlockMissile: 0x80e; const WallOrRanged: 0x5; const BlockWalk: 0x1805; const FriendlyRanged: 0x2004; @@ -250,14 +250,14 @@ declare global { const townOf: (townArea: number) => 1 | 40 | 75 | 103 | 109; const townOfAct: (act: 1 | 2 | 3 | 4 | 5) => 1 | 40 | 75 | 103 | 109; } - + export namespace skills { namespace get { const RightName: 0; const LeftName: 1; const RightId: 2; const LeftId: 3; - const AllSkills: 4 + const AllSkills: 4; } namespace hand { const Right: 0; @@ -267,7 +267,7 @@ declare global { } namespace subindex { const HardPoints: 0; - const SoftPoints: 1 + const SoftPoints: 1; } // General const Attack: 0; @@ -562,7 +562,7 @@ declare global { const MartialArts: 50; } } - export const skillTabs: undefined + export const skillTabs: undefined; export namespace quest { export namespace item { @@ -620,15 +620,35 @@ declare global { } const items: [ // act 1 - 88, 89, 524, 525, + 88, + 89, + 524, + 525, // act 2 - 91, 92, 521, 549, 552, + 91, + 92, + 521, + 549, + 552, // act 3 - 86, 87, 173, 174, 545, 546, 547, 548, 553, 554, 555, + 86, + 87, + 173, + 174, + 545, + 546, + 547, + 548, + 553, + 554, + 555, // act 4 - 90, 551, + 90, + 551, // act 5 - 644, 645, 646, + 644, + 645, + 646, ]; export namespace chest { // act1 @@ -669,15 +689,34 @@ declare global { } const chests: [ // act 1 - 17, 18, 19, 20, 21, 22, 26, 30, 108, + 17, + 18, + 19, + 20, + 21, + 22, + 26, + 30, + 108, // act 2 - 149, 152, 354, 355, 356, 357, + 149, + 152, + 354, + 355, + 356, + 357, // act 3 - 81, 193, 405, 406, 407, + 81, + 193, + 405, + 406, + 407, // act 4 376, // act 5 - 434, 558, 546 + 434, + 558, + 546, ]; export namespace id { const SpokeToWarriv: 0; @@ -737,12 +776,12 @@ declare global { const NPCMenu: 0x08; const EscMenu: 0x09; const KeytotheCairnStonesScreen: 0x10; - const AutoMap: 0x0A; - const ConfigControls: 0x0B; - const Shop: 0x0C; - const ShowItem: 0x0D; - const SubmitItem: 0x0E; - const Quest: 0x0F; + const AutoMap: 0x0a; + const ConfigControls: 0x0b; + const Shop: 0x0c; + const ShowItem: 0x0d; + const SubmitItem: 0x0e; + const Quest: 0x0f; const QuestLog: 0x11; const StatusArea: 0x12; const Waypoint: 0x14; @@ -751,45 +790,125 @@ declare global { const TradePrompt: 0x17; const Msgs: 0x18; const Stash: 0x19; - const Cube: 0x1A; - const ShowBelt: 0x1F; + const Cube: 0x1a; + const ShowBelt: 0x1f; const Help: 0x21; const MercScreen: 0x24; - const ScrollWindow: 0x25 + const ScrollWindow: 0x25; } export namespace menu { - const Respec: 0x2BA0; - const Ok: 0x0D49; - const Talk: 0x0D35; - const Trade: 0x0D44; - const TradeRepair: 0x0D06; - const Imbue: 0x0FB1; - const Gamble: 0x0D46; - const Hire: 0x0D45; - const GoEast: 0x0D36; - const GoWest: 0x0D37; - const IdentifyItems: 0x0FB4; - const SailEast: 0x0D38; - const SailWest: 0x0D39; + const Respec: 0x2ba0; + const Ok: 0x0d49; + const Talk: 0x0d35; + const Trade: 0x0d44; + const TradeRepair: 0x0d06; + const Imbue: 0x0fb1; + const Gamble: 0x0d46; + const Hire: 0x0d45; + const GoEast: 0x0d36; + const GoWest: 0x0d37; + const IdentifyItems: 0x0fb4; + const SailEast: 0x0d38; + const SailWest: 0x0d39; const RessurectMerc: 0x1507; - const AddSockets: 0x58DC; - const Personalize: 0x58DD; - const TravelToHarrogath: 0x58D2; + const AddSockets: 0x58dc; + const Personalize: 0x58dd; + const TravelToHarrogath: 0x58d2; } // shrine types export namespace shrines { const Presets: [2, 81, 83, 170, 344, 197, 202]; const Ids: [ - 2, 77, 81, 83, 84, 85, 93, 96, 97, 109, 116, 123, 124, - 133, 134, 135, 136, 150, 151, 164, 165, 166, 167, 168, - 170, 172, 173, 184, 190, 191, 197, 199, 200, 201, 202, - 206, 226, 231, 232, 236, 249, 260, 262, 263, 264, 265, - 275, 276, 277, 278, 279, 280, 281, 282, 299, 300, 302, - 303, 320, 325, 343, 344, 361, 414, 415, 421, 422, 423, - 427, 428, 464, 465, 472, 479, 483, 484, 488, 491, 492, - 495, 497, 499, 503, 509, 512, 520, 521, 522 + 2, + 77, + 81, + 83, + 84, + 85, + 93, + 96, + 97, + 109, + 116, + 123, + 124, + 133, + 134, + 135, + 136, + 150, + 151, + 164, + 165, + 166, + 167, + 168, + 170, + 172, + 173, + 184, + 190, + 191, + 197, + 199, + 200, + 201, + 202, + 206, + 226, + 231, + 232, + 236, + 249, + 260, + 262, + 263, + 264, + 265, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 299, + 300, + 302, + 303, + 320, + 325, + 343, + 344, + 361, + 414, + 415, + 421, + 422, + 423, + 427, + 428, + 464, + 465, + 472, + 479, + 483, + 484, + 488, + 491, + 492, + 495, + 497, + 499, + 503, + 509, + 512, + 520, + 521, + 522, ]; const None: 0; const Refilling: 1; @@ -813,7 +932,7 @@ declare global { const Fire: 19; const Monster: 20; const Exploding: 21; - const Poison: 22 + const Poison: 22; } // unit states @@ -1421,13 +1540,14 @@ declare global { } export namespace slot { const Main: 0; - const Secondary: 1 + const Secondary: 1; } export namespace move { const Walk: 0; - const Run: 1 + const Run: 1; } - export namespace mode { // sdk.player.mode. + export namespace mode { + // sdk.player.mode. const Death: 0; const StandingOutsideTown: 1; const Walking: 2; @@ -1458,9 +1578,11 @@ declare global { const Druid: 5; const Assassin: 6; - const nameOf: (classid: 0 | 1 | 2 | 3 | 4 | 5 | 6) => "Amazon" | "Sorceress" | "Necromancer" | "Paladin" | "Barbarian" | "Druid" | "Assassin" | false; + const nameOf: ( + classid: 0 | 1 | 2 | 3 | 4 | 5 | 6, + ) => "Amazon" | "Sorceress" | "Necromancer" | "Paladin" | "Barbarian" | "Druid" | "Assassin" | false; } - export { _class as class }; + export type { _class as class }; } export namespace npcs { @@ -1481,7 +1603,7 @@ declare global { const Dead: 12; const KnockedBack: 13; const Spawning: 14; - const Running: 15 + const Running: 15; } const Akara: 148; @@ -1536,9 +1658,65 @@ declare global { } const chestIds: [ - 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, ]; // act1 @@ -1711,7 +1889,7 @@ declare global { const NextAreaWorldstone: 82; } } - + export namespace monsters { namespace preset { // Confirmed @@ -1805,7 +1983,7 @@ declare global { const Dead: 12; const KnockedBack: 13; const Spawning: 14; - const Running: 15 + const Running: 15; } namespace spectype { const All: 0; @@ -2427,9 +2605,9 @@ declare global { const Griswold: 365; const Izual: 256; const Hephasto: 409; - const KorlictheProtector: 540; - const TalictheDefender: 541; - const MadawctheGuardian: 542; + const TalictheDefender: 540; + const MadawctheGuardian: 541; + const KorlictheProtector: 542; const ListerTheTormenter: 571; const TheCowKing: 743; const ColdwormtheBurrower: 284; @@ -2507,7 +2685,7 @@ declare global { const Dead: 12; const KnockedBack: 13; const Spawning: 14; - const Running: 15 + const Running: 15; } const ClayGolem: 289; @@ -2538,7 +2716,7 @@ declare global { const Dead: 12; const KnockedBack: 13; const Spawning: 14; - const Running: 15 + const Running: 15; } const Rogue: 271; @@ -2585,7 +2763,7 @@ declare global { const Feet: 9; const Gloves: 10; const RightArmSecondary: 11; - const LeftArmSecondary: 12 + const LeftArmSecondary: 12; } export namespace items { @@ -2620,7 +2798,7 @@ declare global { const onGround: 3; // Item on ground const onCursor: 4; // Item on cursor const Dropping: 5; // Item being dropped - const Socketed: 6 // Item socketed in item + const Socketed: 6; // Item socketed in item } export namespace quality { const LowQuality: 1; @@ -2637,7 +2815,7 @@ declare global { const Exceptional: 1; const Elite: 2; } - export { _class1 as class }; + export type { _class1 as class }; export namespace type { const Shield: 2; const Armor: 3; @@ -2741,7 +2919,7 @@ declare global { const Topaz: 101; const Skull: 102; } - + // Weapons export const HandAxe: 0; export const Axe: 1; @@ -4398,7 +4576,7 @@ declare global { const Zephyr: 20675; } namespace dialog { - const youDoNotHaveEnoughGoldForThat: 3362 + const youDoNotHaveEnoughGoldForThat: 3362; } namespace text { @@ -4638,7 +4816,7 @@ declare global { const Battlenet: 2; const OpenBattlenet: 3; const TcpIpHost: 4; - const TcpIpJoin: 5 + const TcpIpJoin: 5; } namespace controls { @@ -4699,7 +4877,7 @@ declare global { const TcpIpUnableToConnect: 44; } } - + export namespace colors { const White: "ÿc0"; const Red: "ÿc1"; @@ -4722,7 +4900,7 @@ declare global { const DarkGold: 7; const Orange: 8; const Red: 9; - const Gray: 10 + const Gray: 10; } } @@ -4803,14 +4981,14 @@ declare global { namespace code { const Backspace: 0x08; const Tab: 0x09; - const Clear: 0x0C; - const Enter: 0x0D; + const Clear: 0x0c; + const Enter: 0x0d; const Shift: 0x10; const Ctrl: 0x11; const Alt: 0x12; const PauseBreak: 0x13; const CapsLock: 0x14; - const Esc: 0x1B; + const Esc: 0x1b; const Space: 0x20; const PageUp: 0x21; const PageDown: 0x22; @@ -4821,10 +4999,10 @@ declare global { const RightArrow: 0x27; const DownArrow: 0x28; const Select: 0x29; - const Print: 0x2A; - const PrintScreen: 0x2C; - const Insert: 0x2D; - const Delete: 0x2E; + const Print: 0x2a; + const PrintScreen: 0x2c; + const Insert: 0x2d; + const Delete: 0x2e; } } @@ -4840,7 +5018,7 @@ declare global { const Smack: 9; const ProgressBar: 10; const Popup: 11; - const AccountList: 12 + const AccountList: 12; } export namespace packets { @@ -4854,11 +5032,11 @@ declare global { const LeftSkillOnEntityEx: 0x07; const LeftSkillOnLocationEx: 0x08; const LeftSkillOnEntityEx2: 0x09; - const LeftSkillOnEntityEx3: 0x0A; - const RightSkillOnLocation: 0x0C; - const RightSkillOnEntity: 0x0D; - const RightSkillOnEntityEx: 0x0E; - const RightSkillOnLocationEx: 0x0F; + const LeftSkillOnEntityEx3: 0x0a; + const RightSkillOnLocation: 0x0c; + const RightSkillOnEntity: 0x0d; + const RightSkillOnEntityEx: 0x0e; + const RightSkillOnLocationEx: 0x0f; const RightSkillOnEntityEx2: 0x10; const RightSkillOnEntityEx3: 0x11; const SetInfernoState: 0x12; @@ -4869,12 +5047,12 @@ declare global { const DropItem: 0x17; const ItemToBuffer: 0x18; const PickupBufferItem: 0x19; - const ItemToBody: 0x1A; - const Swap2HandedItem: 0x1B; - const PickupBodyItem: 0x1C; - const SwitchBodyItem: 0x1D; - const Switch1HandWith2Hand: 0x1E; - const SwitchInventoryItem: 0x1F; + const ItemToBody: 0x1a; + const Swap2HandedItem: 0x1b; + const PickupBodyItem: 0x1c; + const SwitchBodyItem: 0x1d; + const Switch1HandWith2Hand: 0x1e; + const SwitchInventoryItem: 0x1f; const UseItem: 0x20; const StackItem: 0x21; const RemoveStackItem: 0x22; @@ -4885,8 +5063,8 @@ declare global { const IndentifyItem: 0x27; const InsertSocketItem: 0x28; const ScrollToMe: 0x29; - const ItemToCube: 0x2A; - const NPCInit: 0x2F; + const ItemToCube: 0x2a; + const NPCInit: 0x2f; const NPCCancel: 0x30; const QuestMessage: 0x31; const NPCBuy: 0x32; @@ -4896,11 +5074,11 @@ declare global { const HireMerc: 0x36; const IndentifyGamble: 0x37; const EntityAction: 0x38; - const AddStat: 0x3A; - const AddSkill: 0x3B; - const SelectSkill: 0x3C; - const ActivateItem: 0x3E; - const CharacterPhrase: 0x3F; + const AddStat: 0x3a; + const AddSkill: 0x3b; + const SelectSkill: 0x3c; + const ActivateItem: 0x3e; + const CharacterPhrase: 0x3f; const UpdateQuests: 0x40; const Resurrect: 0x41; const StaffInOrifice: 0x44; @@ -4908,19 +5086,19 @@ declare global { const MercMove: 0x47; const BusyStateOff: 0x48; const Waypoint: 0x49; - const RequestEntityUpdate: 0x4B; - const Transmorgify: 0x4C; - const PlayNPCMessage: 0x4D; - const ClickButton: 0x4F; + const RequestEntityUpdate: 0x4b; + const Transmorgify: 0x4c; + const PlayNPCMessage: 0x4d; + const ClickButton: 0x4f; const DropGold: 0x50; const BindHotkey: 0x51; const StaminaOn: 0x53; const StaminaOff: 0x54; const QuestCompleted: 0x58; const MakeEntityMove: 0x59; - const SquelchHostile: 0x5D; - const Party: 0x5E; - const UpdatePlayerPos: 0x5F; + const SquelchHostile: 0x5d; + const Party: 0x5e; + const UpdatePlayerPos: 0x5f; const SwapWeapon: 0x60; const MercItem: 0x61; const MercRessurect: 0x62; From 75fac010bf7c3c505c5a1182d9b3a506cea6f19d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 1 Nov 2025 20:33:30 -0400 Subject: [PATCH 642/758] chore: update editorconfig for prettier usage --- .editorconfig | 3 ++- .vscode/settings.json | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index 3274ec36d..d012c1426 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,4 +5,5 @@ indent_style = space indent_size = 2 charset = utf-8 trim_trailing_whitespace = true -insert_final_newline = true \ No newline at end of file +insert_final_newline = true +max_line_length = 120 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 11f167797..7c602b5c4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,8 +6,9 @@ "editor.detectIndentation": false, "[javascript]": { "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - } + "source.fixAll.eslint": "explicit", + }, + "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "[typescript]": { "editor.formatOnSave": true, From 46e70f8524bc019f4936f0b65e47e1a0b0f1a5ba Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 1 Nov 2025 20:34:05 -0400 Subject: [PATCH 643/758] Update globals.d.ts - Update type definition of Object.hasOwn to infer keys from object --- d2bs/kolbot/sdk/globals.d.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 46b979dfe..ba6faba45 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -129,7 +129,9 @@ declare global { values(source: object): any[]; entries(source: object): any[][]; is(o1: any, o2: any): boolean; - hasOwn(obj: object, prop: string): boolean; + // hasOwn(obj: object, prop: string): boolean; + hasOwn(obj: T, prop: keyof T): boolean; + hasOwn(obj: T, prop: K): prop is keyof T; } interface Object { From 63f4f390cffea0f8fd0330a409ef2b0f0143454f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 1 Nov 2025 20:34:46 -0400 Subject: [PATCH 644/758] chore: Update getNearestMonster definition --- d2bs/kolbot/sdk/types/Attack.d.ts | 74 ++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 791b44592..fd6b9a666 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -1,20 +1,20 @@ export {}; declare global { type DamageType = "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt"; - + interface AttackResult { - FAILED: 0, - SUCCESS: 1, - CANTATTACK: 2, // need to fix the ambiguity between this result and Failed - NEEDMANA: 3 + FAILED: 0; + SUCCESS: 1; + CANTATTACK: 2; // need to fix the ambiguity between this result and Failed + NEEDMANA: 3; } interface ClassAttack { - doAttack(unit: Monster, preattack?: boolean): AttackResult - afterAttack(any?: any): void - doCast(unit: Monster, timedSkill: number, untimedSkill: number): AttackResult + doAttack(unit: Monster, preattack?: boolean): AttackResult; + afterAttack(any?: any): void; + doCast(unit: Monster, timedSkill: number, untimedSkill: number): AttackResult; // Self defined - decideSkill(unit: Monster, skipSkill?: number[]): { timed: number, untimed: number } + decideSkill(unit: Monster, skipSkill?: number[]): { timed: number; untimed: number }; } namespace Attack { const infinity: boolean; @@ -34,14 +34,27 @@ declare global { function kill(classId: number | Unit): boolean; function hurt(classId: string | number | Unit, percent: number): boolean; function getScarinessLevel(unit: Unit): number; - function clear(range?: number, spectype?: number, bossId?: number | Unit, sortfunc?: Function, pickit?: boolean): boolean; + function clear( + range?: number, + spectype?: number, + bossId?: number | Unit, + sortfunc?: Function, + pickit?: boolean, + ): boolean; function clearClassids(...ids: number[]): boolean; - function getMob(classid: number, spectype: number, range: number, center: Unit | { - x: number; - y: number; - }): Monster[]; + function getMob( + classid: number, + spectype: number, + range: number, + center: + | Unit + | { + x: number; + y: number; + }, + ): Monster[]; function clearList(mainArg: Function | Unit[], sortFunc?: Function, refresh?: boolean): boolean; - + interface SecurePositionOptions { range?: number; timer?: number; @@ -63,27 +76,48 @@ declare global { function validSpot(x: number, y: number, skill?: number, unitid?: number): boolean; function openChests(range: number, x?: number, y?: number): boolean; function buildMonsterList(): [] | Monster[]; - function findSafeSpot(unit: Unit, distance: number, spread: number, range: number, ...args: any[]): { + function findSafeSpot( + unit: Unit, + distance: number, + spread: number, + range: number, + ...args: any[] + ): { x: number; y: number; }; function deploy(unit: Monster, distance: any, spread: any, range: any, ...args: any[]): boolean; function getMonsterCount(x: any, y: any, range: any, list: any): number; - function buildGrid(xmin: any, xmax: any, ymin: any, ymax: any, spread: any): { + function buildGrid( + xmin: any, + xmax: any, + ymin: any, + ymax: any, + spread: any, + ): { x: any; y: any; coll: number; }[]; function skipCheck(unit: Monster): boolean; - function getSkillElement(skillId: number): false | "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"; - function getResist(unit: Monster, type: "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"): boolean; + function getSkillElement( + skillId: number, + ): false | "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none"; + function getResist( + unit: Monster, + type: "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt" | "none", + ): boolean; function getLowerResistPercent(): number; function getConvictionPercent(): number; function checkResist(unit: Monster, val: any, maxres?: number): boolean; function canAttack(unit: Monster): boolean; function usingBow(): false | "bow" | "crossbow"; function getIntoPosition(unit: Monster, distance: any, coll: any, walk: any): boolean; - function getNearestMonster(givenSettings?: {}): any; + function getNearestMonster(givenSettings?: { + skipBlocked?: boolean; + skipImmune?: boolean; + skipGid?: number; + }): Monster | false; function checkCorpse(unit: Monster): boolean; function checkNearCorpses(unit: Monster, range?: number): any; function whirlwind(unit: Monster | Player): boolean; From 79c31462f56bf3ff03dc8f455c11d2cb37ee4b66 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 15 Nov 2025 01:56:14 -0500 Subject: [PATCH 645/758] patch: Add LobbyChat location to D2BotPubJoin - Allows bot to join channel if specified --- d2bs/kolbot/D2BotPubJoin.dbj | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index a203d3dc6..07cf9543d 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -171,6 +171,12 @@ const locationAction = (function () { Starter.pingQuit = false; } + if (Starter.Config.JoinChannel !== "" || Starter.Config.AnnounceGames) { + Controls.LobbyEnterChat.click(); + + return; + } + if (Starter.inGame || Starter.gameInfo.error) { !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); @@ -207,6 +213,43 @@ const locationAction = (function () { Starter.LocationEvents.openJoinGameWindow(); } ); + locations.set(sdk.game.locations.LobbyChat, + function () { + D2Bot.updateStatus("Lobby Chat"); + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + return; + } + + console.log("updating runs"); + D2Bot.updateRuns(); + + lastGameTick = getTickCount(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + } + + if (!Starter.chatActionsDone) { + Starter.chatActionsDone = true; + + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + say("/j " + Starter.Config.JoinChannel); + delay(1000); + + if (Starter.Config.FirstJoinMessage !== "") { + say(Starter.Config.FirstJoinMessage); + delay(500); + } + } + + Starter.LocationEvents.openJoinGameWindow(); + } + ); addLocations([sdk.game.locations.WaitingInLine, sdk.game.locations.CreateGame], function () { Controls.CancelCreateGame.click(); From e16e99a1668e1d7169de7b940840595b887f2c7b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 16 Nov 2025 16:15:57 -0500 Subject: [PATCH 646/758] Enabled Unit.idle to apply to monsters - Remove the restriction of idle for applying only to players, monsters have an idle state where they are just standing --- d2bs/kolbot/libs/core/Prototypes.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 551ed721a..501e91711 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -386,10 +386,19 @@ Object.defineProperties(Unit.prototype, { }, idle: { get: function () { - if (this.type > sdk.unittype.Player) throw new Error("Unit.idle: Must be used with player units."); - // Dead is pretty idle too - return (this.mode === sdk.player.mode.StandingOutsideTown - || this.mode === sdk.player.mode.StandingInTown || this.mode === sdk.player.mode.Dead); + if (this.type > sdk.unittype.Monster) { + throw new Error("Unit.idle: Must be used with Player or Monster units."); + } + switch (this.type) { + case sdk.unittype.Player: + // Dead is pretty idle too + return (this.mode === sdk.player.mode.StandingOutsideTown + || this.mode === sdk.player.mode.StandingInTown || this.mode === sdk.player.mode.Idle); + case sdk.unittype.Monster: + return (this.mode === sdk.monsters.mode.Standing); + default: + return false; + } } }, gold: { From 7b57525ec936dfb1cef7098a4875437c41d232fc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 17 Nov 2025 00:31:44 -0500 Subject: [PATCH 647/758] Update config.d.ts Config.Advertise + add support for array of advertise messages - Updated Advertise module to support array of messages to randomly select as well as static singular message --- d2bs/kolbot/libs/config/_BaseConfigFile.js | 6 ++++++ d2bs/kolbot/libs/modules/workers/Advertise.js | 6 +++++- d2bs/kolbot/sdk/types/Config.d.ts | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 1c1bff9d8..fde106afb 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -481,6 +481,12 @@ Config.LocalChat.Toggle = false; // optional, set to KEY value to toggle through modes 0, 1, 2 Config.LocalChat.Mode = 1; // 0 = disabled, 1 = chat from 'say' (recommended), 2 = all chat (for manual play) + // Advertise settings + Config.Advertise.Enabled = true; + Config.Advertise.Message = "Kolbot is super cool"; + // Config.Advertise.Message = ["Kolbot is super cool", "Join op kolbot games!", "Kolbot FTW!"]; // Array of messages to randomly choose from + Config.Advertise.Interval = [30, 60]; // Send advert message every X to Y seconds + // Anti-hostile config Config.AntiHostile = false; // Enable anti-hostile Config.HostileAction = 0; // 0 - quit immediately, 1 - quit when hostile player is sighted, 2 - attack hostile diff --git a/d2bs/kolbot/libs/modules/workers/Advertise.js b/d2bs/kolbot/libs/modules/workers/Advertise.js index ef237688a..dd371933d 100644 --- a/d2bs/kolbot/libs/modules/workers/Advertise.js +++ b/d2bs/kolbot/libs/modules/workers/Advertise.js @@ -29,8 +29,12 @@ Worker.runInBackground.Advertise = function () { if (getTickCount() - waitTick < 0) return true; waitTick += Time.seconds(rand(min, max)); + + let message = Array.isArray(Config.Advertise.Message) + ? Config.Advertise.Message.random() + : Config.Advertise.Message; - say("!" + Config.Advertise.Message, true); + say("!" + message, true); return true; }; diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 63424d506..a9d3320f9 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -258,6 +258,13 @@ declare global { MapMode: { UseOwnItemFilter: boolean; }; + + Advertise: { + Enabled: boolean; + Message: string | string[]; + Interval: [number, number] | number; + }; + MFLeader: boolean; Mausoleum: { KillBishibosh: boolean; From 4ff9ed52bbbb1ffa007efead223e21936c01c5e5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:31:02 -0500 Subject: [PATCH 648/758] Refactor sdk namespace to SDK interface - Converted the global 'sdk' namespace and its sub-namespaces to a single 'SDK' interface with nested objects. --- d2bs/kolbot/sdk/types/sdk.d.ts | 9342 ++++++++++++++++---------------- 1 file changed, 4671 insertions(+), 4671 deletions(-) diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index e96ce2249..5c3ff7ff5 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -1,624 +1,624 @@ declare global { - namespace sdk { - export namespace waypoints { - const Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; - const Act1: number[]; - const Act2: number[]; - const Act3: number[]; - const Act4: number[]; - const Act5: number[]; - } + interface SDK { + waypoints: { + Ids: [119, 145, 156, 157, 237, 238, 288, 323, 324, 398, 402, 429, 494, 496, 511, 539]; + Act1: number[]; + Act2: number[]; + Act3: number[]; + Act4: number[]; + Act5: number[]; + }; - export namespace difficulty { - const Normal: 0; - const Nightmare: 1; - const Hell: 2; - const Difficulties: ["Normal", "Nightmare", "Hell"]; + difficulty: { + Normal: 0; + Nightmare: 1; + Hell: 2; + Difficulties: ["Normal", "Nightmare", "Hell"]; - const nameOf: (diff: 0 | 1 | 2) => "Normal" | "Nightmare" | "Hell" | false; - } + nameOf: (diff: 0 | 1 | 2) => "Normal" | "Nightmare" | "Hell" | false; + }; - export namespace party { - const NoParty: 65535; - namespace flag { - const Invite: 0; - const InParty: 1; - const Accept: 2; - const Cancel: 4; - } - namespace controls { - const Hostile: 1; - const InviteOrCancel: 2; - const Leave: 3; - const Ignore: 4; - const Squelch: 5; - } - } + party: { + NoParty: 65535; + flag: { + Invite: 0; + InParty: 1; + Accept: 2; + Cancel: 4; + }; + controls: { + Hostile: 1; + InviteOrCancel: 2; + Leave: 3; + Ignore: 4; + Squelch: 5; + }; + }; - export namespace clicktypes { - namespace click { - namespace item { - const Left: 0; - const Right: 1; - const ShiftLeft: 2; // For belt - const MercFromBelt: 3; // For belt - const Mercenary: 4; // Give to merc - } - namespace map { - const LeftDown: 0; - const LeftHold: 1; - const LeftUp: 2; - const RightDown: 3; - const RightHold: 4; - const RightUp: 5; - } - } - namespace shift { - const NoShift: 0; - const Shift: 1; - } - } + clicktypes: { + click: { + item: { + Left: 0; + Right: 1; + ShiftLeft: 2; // For belt + MercFromBelt: 3; // For belt + Mercenary: 4; // Give to merc + }; + map: { + LeftDown: 0; + LeftHold: 1; + LeftUp: 2; + RightDown: 3; + RightHold: 4; + RightUp: 5; + }; + }; + shift: { + NoShift: 0; + Shift: 1; + }; + }; - export namespace cursortype { - const Empty: 1; - const ItemOnUnitHover: 3; // see notes - const ItemOnCursor: 4; // see notes - const Identify: 6; - const Repair: 7; - } + cursortype: { + Empty: 1; + ItemOnUnitHover: 3; // see notes + ItemOnCursor: 4; // see notes + Identify: 6; + Repair: 7; + }; - export namespace collision { - const BlockWall: 0x01; - const LineOfSight: 0x02; - const Ranged: 0x04; - const PlayerToWalk: 0x08; - const DarkArea: 0x10; - const Casting: 0x20; - const Unknown: 0x40; - const Players: 0x80; - const Monsters: 0x100; - const Items: 0x200; - const Objects: 0x400; - const ClosedDoor: 0x800; - const IsOnFloor: 0x1000; - const MonsterIsOnFloor: 0x1100; - const MonsterIsOnFloorDarkArea: 0x1110; // in doorway - const FriendlyNPC: 0x2000; - const Unknown2: 0x4000; - const DeadBodies: 0x8000; - const MonsterObject: 0xffff; - const BlockMissile: 0x80e; - const WallOrRanged: 0x5; - const BlockWalk: 0x1805; - const FriendlyRanged: 0x2004; - const BoneWall: 4352; - } + collision: { + BlockWall: 0x01; + LineOfSight: 0x02; + Ranged: 0x04; + PlayerToWalk: 0x08; + DarkArea: 0x10; + Casting: 0x20; + Unknown: 0x40; + Players: 0x80; + Monsters: 0x100; + Items: 0x200; + Objects: 0x400; + ClosedDoor: 0x800; + IsOnFloor: 0x1000; + MonsterIsOnFloor: 0x1100; + MonsterIsOnFloorDarkArea: 0x1110; // in doorway + FriendlyNPC: 0x2000; + Unknown2: 0x4000; + DeadBodies: 0x8000; + MonsterObject: 0xffff; + BlockMissile: 0x80e; + WallOrRanged: 0x5; + BlockWalk: 0x1805; + FriendlyRanged: 0x2004; + BoneWall: 4352; + }; - export namespace areas { - const Towns: [1, 40, 75, 103, 109]; - const None: 0; + areas: { + Towns: [1, 40, 75, 103, 109]; + None: 0; // Act 1 - const RogueEncampment: 1; - const BloodMoor: 2; - const ColdPlains: 3; - const StonyField: 4; - const DarkWood: 5; - const BlackMarsh: 6; - const TamoeHighland: 7; - const DenofEvil: 8; - const CaveLvl1: 9; - const UndergroundPassageLvl1: 10; - const HoleLvl1: 11; - const PitLvl1: 12; - const CaveLvl2: 13; - const UndergroundPassageLvl2: 14; - const HoleLvl2: 15; - const PitLvl2: 16; - const BurialGrounds: 17; - const Crypt: 18; - const Mausoleum: 19; - const ForgottenTower: 20; - const TowerCellarLvl1: 21; - const TowerCellarLvl2: 22; - const TowerCellarLvl3: 23; - const TowerCellarLvl4: 24; - const TowerCellarLvl5: 25; - const MonasteryGate: 26; - const OuterCloister: 27; - const Barracks: 28; - const JailLvl1: 29; - const JailLvl2: 30; - const JailLvl3: 31; - const InnerCloister: 32; - const Cathedral: 33; - const CatacombsLvl1: 34; - const CatacombsLvl2: 35; - const CatacombsLvl3: 36; - const CatacombsLvl4: 37; - const Tristram: 38; - const MooMooFarm: 39; + RogueEncampment: 1; + BloodMoor: 2; + ColdPlains: 3; + StonyField: 4; + DarkWood: 5; + BlackMarsh: 6; + TamoeHighland: 7; + DenofEvil: 8; + CaveLvl1: 9; + UndergroundPassageLvl1: 10; + HoleLvl1: 11; + PitLvl1: 12; + CaveLvl2: 13; + UndergroundPassageLvl2: 14; + HoleLvl2: 15; + PitLvl2: 16; + BurialGrounds: 17; + Crypt: 18; + Mausoleum: 19; + ForgottenTower: 20; + TowerCellarLvl1: 21; + TowerCellarLvl2: 22; + TowerCellarLvl3: 23; + TowerCellarLvl4: 24; + TowerCellarLvl5: 25; + MonasteryGate: 26; + OuterCloister: 27; + Barracks: 28; + JailLvl1: 29; + JailLvl2: 30; + JailLvl3: 31; + InnerCloister: 32; + Cathedral: 33; + CatacombsLvl1: 34; + CatacombsLvl2: 35; + CatacombsLvl3: 36; + CatacombsLvl4: 37; + Tristram: 38; + MooMooFarm: 39; // Act 2 - const LutGholein: 40; - const RockyWaste: 41; - const DryHills: 42; - const FarOasis: 43; - const LostCity: 44; - const ValleyofSnakes: 45; - const CanyonofMagic: 46; - const A2SewersLvl1: 47; - const A2SewersLvl2: 48; - const A2SewersLvl3: 49; - const HaremLvl1: 50; - const HaremLvl2: 51; - const PalaceCellarLvl1: 52; - const PalaceCellarLvl2: 53; - const PalaceCellarLvl3: 54; - const StonyTombLvl1: 55; - const HallsoftheDeadLvl1: 56; - const HallsoftheDeadLvl2: 57; - const ClawViperTempleLvl1: 58; - const StonyTombLvl2: 59; - const HallsoftheDeadLvl3: 60; - const ClawViperTempleLvl2: 61; - const MaggotLairLvl1: 62; - const MaggotLairLvl2: 63; - const MaggotLairLvl3: 64; - const AncientTunnels: 65; - const TalRashasTomb1: 66; - const TalRashasTomb2: 67; - const TalRashasTomb3: 68; - const TalRashasTomb4: 69; - const TalRashasTomb5: 70; - const TalRashasTomb6: 71; - const TalRashasTomb7: 72; - const DurielsLair: 73; - const ArcaneSanctuary: 74; + LutGholein: 40; + RockyWaste: 41; + DryHills: 42; + FarOasis: 43; + LostCity: 44; + ValleyofSnakes: 45; + CanyonofMagic: 46; + A2SewersLvl1: 47; + A2SewersLvl2: 48; + A2SewersLvl3: 49; + HaremLvl1: 50; + HaremLvl2: 51; + PalaceCellarLvl1: 52; + PalaceCellarLvl2: 53; + PalaceCellarLvl3: 54; + StonyTombLvl1: 55; + HallsoftheDeadLvl1: 56; + HallsoftheDeadLvl2: 57; + ClawViperTempleLvl1: 58; + StonyTombLvl2: 59; + HallsoftheDeadLvl3: 60; + ClawViperTempleLvl2: 61; + MaggotLairLvl1: 62; + MaggotLairLvl2: 63; + MaggotLairLvl3: 64; + AncientTunnels: 65; + TalRashasTomb1: 66; + TalRashasTomb2: 67; + TalRashasTomb3: 68; + TalRashasTomb4: 69; + TalRashasTomb5: 70; + TalRashasTomb6: 71; + TalRashasTomb7: 72; + DurielsLair: 73; + ArcaneSanctuary: 74; // Act 3 - const KurastDocktown: 75; - const SpiderForest: 76; - const GreatMarsh: 77; - const FlayerJungle: 78; - const LowerKurast: 79; - const KurastBazaar: 80; - const UpperKurast: 81; - const KurastCauseway: 82; - const Travincal: 83; - const SpiderCave: 84; - const SpiderCavern: 85; - const SwampyPitLvl1: 86; - const SwampyPitLvl2: 87; - const FlayerDungeonLvl1: 88; - const FlayerDungeonLvl2: 89; - const SwampyPitLvl3: 90; - const FlayerDungeonLvl3: 91; - const A3SewersLvl1: 92; - const A3SewersLvl2: 93; - const RuinedTemple: 94; - const DisusedFane: 95; - const ForgottenReliquary: 96; - const ForgottenTemple: 97; - const RuinedFane: 98; - const DisusedReliquary: 99; - const DuranceofHateLvl1: 100; - const DuranceofHateLvl2: 101; - const DuranceofHateLvl3: 102; + KurastDocktown: 75; + SpiderForest: 76; + GreatMarsh: 77; + FlayerJungle: 78; + LowerKurast: 79; + KurastBazaar: 80; + UpperKurast: 81; + KurastCauseway: 82; + Travincal: 83; + SpiderCave: 84; + SpiderCavern: 85; + SwampyPitLvl1: 86; + SwampyPitLvl2: 87; + FlayerDungeonLvl1: 88; + FlayerDungeonLvl2: 89; + SwampyPitLvl3: 90; + FlayerDungeonLvl3: 91; + A3SewersLvl1: 92; + A3SewersLvl2: 93; + RuinedTemple: 94; + DisusedFane: 95; + ForgottenReliquary: 96; + ForgottenTemple: 97; + RuinedFane: 98; + DisusedReliquary: 99; + DuranceofHateLvl1: 100; + DuranceofHateLvl2: 101; + DuranceofHateLvl3: 102; // Act 4 - const PandemoniumFortress: 103; - const OuterSteppes: 104; - const PlainsofDespair: 105; - const CityoftheDamned: 106; - const RiverofFlame: 107; - const ChaosSanctuary: 108; + PandemoniumFortress: 103; + OuterSteppes: 104; + PlainsofDespair: 105; + CityoftheDamned: 106; + RiverofFlame: 107; + ChaosSanctuary: 108; // Act 5 - const Harrogath: 109; - const BloodyFoothills: 110; - const FrigidHighlands: 111; - const ArreatPlateau: 112; - const CrystalizedPassage: 113; - const FrozenRiver: 114; - const GlacialTrail: 115; - const DrifterCavern: 116; - const FrozenTundra: 117; - const AncientsWay: 118; - const IcyCellar: 119; - const ArreatSummit: 120; - const NihlathaksTemple: 121; - const HallsofAnguish: 122; - const HallsofPain: 123; - const HallsofVaught: 124; - const Abaddon: 125; - const PitofAcheron: 126; - const InfernalPit: 127; - const WorldstoneLvl1: 128; - const WorldstoneLvl2: 129; - const WorldstoneLvl3: 130; - const ThroneofDestruction: 131; - const WorldstoneChamber: 132; + Harrogath: 109; + BloodyFoothills: 110; + FrigidHighlands: 111; + ArreatPlateau: 112; + CrystalizedPassage: 113; + FrozenRiver: 114; + GlacialTrail: 115; + DrifterCavern: 116; + FrozenTundra: 117; + AncientsWay: 118; + IcyCellar: 119; + ArreatSummit: 120; + NihlathaksTemple: 121; + HallsofAnguish: 122; + HallsofPain: 123; + HallsofVaught: 124; + Abaddon: 125; + PitofAcheron: 126; + InfernalPit: 127; + WorldstoneLvl1: 128; + WorldstoneLvl2: 129; + WorldstoneLvl3: 130; + ThroneofDestruction: 131; + WorldstoneChamber: 132; // Ubers - const MatronsDen: 133; - const ForgottenSands: 134; - const FurnaceofPain: 135; - const UberTristram: 136; + MatronsDen: 133; + ForgottenSands: 134; + FurnaceofPain: 135; + UberTristram: 136; - const actOf: (act: number) => 1 | 2 | 3 | 4 | 5; - const townOf: (townArea: number) => 1 | 40 | 75 | 103 | 109; - const townOfAct: (act: 1 | 2 | 3 | 4 | 5) => 1 | 40 | 75 | 103 | 109; - } + actOf: (act: number) => 1 | 2 | 3 | 4 | 5; + townOf: (townArea: number) => 1 | 40 | 75 | 103 | 109; + townOfAct: (act: 1 | 2 | 3 | 4 | 5) => 1 | 40 | 75 | 103 | 109; + }; - export namespace skills { - namespace get { - const RightName: 0; - const LeftName: 1; - const RightId: 2; - const LeftId: 3; - const AllSkills: 4; - } - namespace hand { - const Right: 0; - const Left: 1; - const LeftNoShift: 2; - const RightShift: 3; - } - namespace subindex { - const HardPoints: 0; - const SoftPoints: 1; - } + skills: { + get: { + RightName: 0; + LeftName: 1; + RightId: 2; + LeftId: 3; + AllSkills: 4; + }; + hand: { + Right: 0; + Left: 1; + LeftNoShift: 2; + RightShift: 3; + }; + subindex: { + HardPoints: 0; + SoftPoints: 1; + }; // General - const Attack: 0; - const Kick: 1; - const Throw: 2; - const Unsummon: 3; - const LeftHandThrow: 4; - const LeftHandSwing: 5; + Attack: 0; + Kick: 1; + Throw: 2; + Unsummon: 3; + LeftHandThrow: 4; + LeftHandSwing: 5; // Amazon - const MagicArrow: 6; - const FireArrow: 7; - const InnerSight: 8; - const CriticalStrike: 9; - const Jab: 10; - const ColdArrow: 11; - const MultipleShot: 12; - const Dodge: 13; - const PowerStrike: 14; - const PoisonJavelin: 15; - const ExplodingArrow: 16; - const SlowMissiles: 17; - const Avoid: 18; - const Impale: 19; - const LightningBolt: 20; - const IceArrow: 21; - const GuidedArrow: 22; - const Penetrate: 23; - const ChargedStrike: 24; - const PlagueJavelin: 25; - const Strafe: 26; - const ImmolationArrow: 27; - const Dopplezon: 28; - const Decoy: 28; - const Evade: 29; - const Fend: 30; - const FreezingArrow: 31; - const Valkyrie: 32; - const Pierce: 33; - const LightningStrike: 34; - const LightningFury: 35; + MagicArrow: 6; + FireArrow: 7; + InnerSight: 8; + CriticalStrike: 9; + Jab: 10; + ColdArrow: 11; + MultipleShot: 12; + Dodge: 13; + PowerStrike: 14; + PoisonJavelin: 15; + ExplodingArrow: 16; + SlowMissiles: 17; + Avoid: 18; + Impale: 19; + LightningBolt: 20; + IceArrow: 21; + GuidedArrow: 22; + Penetrate: 23; + ChargedStrike: 24; + PlagueJavelin: 25; + Strafe: 26; + ImmolationArrow: 27; + Dopplezon: 28; + Decoy: 28; + Evade: 29; + Fend: 30; + FreezingArrow: 31; + Valkyrie: 32; + Pierce: 33; + LightningStrike: 34; + LightningFury: 35; // Sorc - const FireBolt: 36; - const Warmth: 37; - const ChargedBolt: 38; - const IceBolt: 39; - const FrozenArmor: 40; - const Inferno: 41; - const StaticField: 42; - const Telekinesis: 43; - const FrostNova: 44; - const IceBlast: 45; - const Blaze: 46; - const FireBall: 47; - const Nova: 48; - const Lightning: 49; - const ShiverArmor: 50; - const FireWall: 51; - const Enchant: 52; - const ChainLightning: 53; - const Teleport: 54; - const GlacialSpike: 55; - const Meteor: 56; - const ThunderStorm: 57; - const EnergyShield: 58; - const Blizzard: 59; - const ChillingArmor: 60; - const FireMastery: 61; - const Hydra: 62; - const LightningMastery: 63; - const FrozenOrb: 64; - const ColdMastery: 65; + FireBolt: 36; + Warmth: 37; + ChargedBolt: 38; + IceBolt: 39; + FrozenArmor: 40; + Inferno: 41; + StaticField: 42; + Telekinesis: 43; + FrostNova: 44; + IceBlast: 45; + Blaze: 46; + FireBall: 47; + Nova: 48; + Lightning: 49; + ShiverArmor: 50; + FireWall: 51; + Enchant: 52; + ChainLightning: 53; + Teleport: 54; + GlacialSpike: 55; + Meteor: 56; + ThunderStorm: 57; + EnergyShield: 58; + Blizzard: 59; + ChillingArmor: 60; + FireMastery: 61; + Hydra: 62; + LightningMastery: 63; + FrozenOrb: 64; + ColdMastery: 65; // Necro - const AmplifyDamage: 66; - const Teeth: 67; - const BoneArmor: 68; - const SkeletonMastery: 69; - const RaiseSkeleton: 70; - const DimVision: 71; - const Weaken: 72; - const PoisonDagger: 73; - const CorpseExplosion: 74; - const ClayGolem: 75; - const IronMaiden: 76; - const Terror: 77; - const BoneWall: 78; - const GolemMastery: 79; - const RaiseSkeletalMage: 80; - const Confuse: 81; - const LifeTap: 82; - const PoisonExplosion: 83; - const BoneSpear: 84; - const BloodGolem: 85; - const Attract: 86; - const Decrepify: 87; - const BonePrison: 88; - const SummonResist: 89; - const IronGolem: 90; - const LowerResist: 91; - const PoisonNova: 92; - const BoneSpirit: 93; - const FireGolem: 94; - const Revive: 95; + AmplifyDamage: 66; + Teeth: 67; + BoneArmor: 68; + SkeletonMastery: 69; + RaiseSkeleton: 70; + DimVision: 71; + Weaken: 72; + PoisonDagger: 73; + CorpseExplosion: 74; + ClayGolem: 75; + IronMaiden: 76; + Terror: 77; + BoneWall: 78; + GolemMastery: 79; + RaiseSkeletalMage: 80; + Confuse: 81; + LifeTap: 82; + PoisonExplosion: 83; + BoneSpear: 84; + BloodGolem: 85; + Attract: 86; + Decrepify: 87; + BonePrison: 88; + SummonResist: 89; + IronGolem: 90; + LowerResist: 91; + PoisonNova: 92; + BoneSpirit: 93; + FireGolem: 94; + Revive: 95; // Paladin - const Sacrifice: 96; - const Smite: 97; - const Might: 98; - const Prayer: 99; - const ResistFire: 100; - const HolyBolt: 101; - const HolyFire: 102; - const Thorns: 103; - const Defiance: 104; - const ResistCold: 105; - const Zeal: 106; - const Charge: 107; - const BlessedAim: 108; - const Cleansing: 109; - const ResistLightning: 110; - const Vengeance: 111; - const BlessedHammer: 112; - const Concentration: 113; - const HolyFreeze: 114; - const Vigor: 115; - const Conversion: 116; - const HolyShield: 117; - const HolyShock: 118; - const Sanctuary: 119; - const Meditation: 120; - const FistoftheHeavens: 121; - const Fanaticism: 122; - const Conviction: 123; - const Redemption: 124; - const Salvation: 125; + Sacrifice: 96; + Smite: 97; + Might: 98; + Prayer: 99; + ResistFire: 100; + HolyBolt: 101; + HolyFire: 102; + Thorns: 103; + Defiance: 104; + ResistCold: 105; + Zeal: 106; + Charge: 107; + BlessedAim: 108; + Cleansing: 109; + ResistLightning: 110; + Vengeance: 111; + BlessedHammer: 112; + Concentration: 113; + HolyFreeze: 114; + Vigor: 115; + Conversion: 116; + HolyShield: 117; + HolyShock: 118; + Sanctuary: 119; + Meditation: 120; + FistoftheHeavens: 121; + Fanaticism: 122; + Conviction: 123; + Redemption: 124; + Salvation: 125; // Barb - const Bash: 126; - const SwordMastery: 127; - const AxeMastery: 128; - const MaceMastery: 129; - const Howl: 130; - const FindPotion: 131; - const Leap: 132; - const DoubleSwing: 133; - const PoleArmMastery: 134; - const ThrowingMastery: 135; - const SpearMastery: 136; - const Taunt: 137; - const Shout: 138; - const Stun: 139; - const DoubleThrow: 140; - const IncreasedStamina: 141; - const FindItem: 142; - const LeapAttack: 143; - const Concentrate: 144; - const IronSkin: 145; - const BattleCry: 146; - const Frenzy: 147; - const IncreasedSpeed: 148; - const BattleOrders: 149; - const GrimWard: 150; - const Whirlwind: 151; - const Berserk: 152; - const NaturalResistance: 153; - const WarCry: 154; - const BattleCommand: 155; + Bash: 126; + SwordMastery: 127; + AxeMastery: 128; + MaceMastery: 129; + Howl: 130; + FindPotion: 131; + Leap: 132; + DoubleSwing: 133; + PoleArmMastery: 134; + ThrowingMastery: 135; + SpearMastery: 136; + Taunt: 137; + Shout: 138; + Stun: 139; + DoubleThrow: 140; + IncreasedStamina: 141; + FindItem: 142; + LeapAttack: 143; + Concentrate: 144; + IronSkin: 145; + BattleCry: 146; + Frenzy: 147; + IncreasedSpeed: 148; + BattleOrders: 149; + GrimWard: 150; + Whirlwind: 151; + Berserk: 152; + NaturalResistance: 153; + WarCry: 154; + BattleCommand: 155; // General stuff - const IdentifyScroll: 217; - const BookofIdentify: 218; - const TownPortalScroll: 219; - const BookofTownPortal: 220; + IdentifyScroll: 217; + BookofIdentify: 218; + TownPortalScroll: 219; + BookofTownPortal: 220; // Druid - const Raven: 221; - const PoisonCreeper: 222; // External - const PlaguePoppy: 222; // Internal - const Werewolf: 223; // External - const Wearwolf: 223; // Internal - const Lycanthropy: 224; // External - const ShapeShifting: 224; // Internal - const Firestorm: 225; - const OakSage: 226; - const SpiritWolf: 227; // External - const SummonSpiritWolf: 227; // Internal - const Werebear: 228; // External - const Wearbear: 228; // Internal - const MoltenBoulder: 229; - const ArcticBlast: 230; - const CarrionVine: 231; // External - const CycleofLife: 231; // Internal - const FeralRage: 232; - const Maul: 233; - const Fissure: 234; // Internal - const Eruption: 234; // Internal - const CycloneArmor: 235; - const HeartofWolverine: 236; - const SummonDireWolf: 237; // External - const SummonFenris: 237; // Internal - const Rabies: 238; - const FireClaws: 239; - const Twister: 240; - const SolarCreeper: 241; // External - const Vines: 241; // Internal - const Hunger: 242; - const ShockWave: 243; - const Volcano: 244; - const Tornado: 245; - const SpiritofBarbs: 246; - const Grizzly: 247; // External - const SummonGrizzly: 247; // Internal - const Fury: 248; - const Armageddon: 249; - const Hurricane: 250; + Raven: 221; + PoisonCreeper: 222; // External + PlaguePoppy: 222; // Internal + Werewolf: 223; // External + Wearwolf: 223; // Internal + Lycanthropy: 224; // External + ShapeShifting: 224; // Internal + Firestorm: 225; + OakSage: 226; + SpiritWolf: 227; // External + SummonSpiritWolf: 227; // Internal + Werebear: 228; // External + Wearbear: 228; // Internal + MoltenBoulder: 229; + ArcticBlast: 230; + CarrionVine: 231; // External + CycleofLife: 231; // Internal + FeralRage: 232; + Maul: 233; + Fissure: 234; // Internal + Eruption: 234; // Internal + CycloneArmor: 235; + HeartofWolverine: 236; + SummonDireWolf: 237; // External + SummonFenris: 237; // Internal + Rabies: 238; + FireClaws: 239; + Twister: 240; + SolarCreeper: 241; // External + Vines: 241; // Internal + Hunger: 242; + ShockWave: 243; + Volcano: 244; + Tornado: 245; + SpiritofBarbs: 246; + Grizzly: 247; // External + SummonGrizzly: 247; // Internal + Fury: 248; + Armageddon: 249; + Hurricane: 250; // Assa - const FireBlast: 251; // External - const FireTrauma: 251; // Internal - const ClawMastery: 252; - const PsychicHammer: 253; - const TigerStrike: 254; - const DragonTalon: 255; - const ShockWeb: 256; // External - const ShockField: 256; // Internal - const BladeSentinel: 257; - const Quickness: 258; // Internal name - const BurstofSpeed: 258; // Shown name - const FistsofFire: 259; - const DragonClaw: 260; - const ChargedBoltSentry: 261; - const WakeofFire: 262; // External - const WakeofFireSentry: 262; // Internal - const WeaponBlock: 263; - const CloakofShadows: 264; - const CobraStrike: 265; - const BladeFury: 266; - const Fade: 267; - const ShadowWarrior: 268; - const ClawsofThunder: 269; - const DragonTail: 270; - const LightningSentry: 271; - const WakeofInferno: 272; // External - const InfernoSentry: 272; // Internal - const MindBlast: 273; - const BladesofIce: 274; - const DragonFlight: 275; - const DeathSentry: 276; - const BladeShield: 277; - const Venom: 278; - const ShadowMaster: 279; - const PhoenixStrike: 280; // External - const RoyalStrike: 280; // Internal - const WakeofDestructionSentry: 281; // Not used? - const Summoner: 500; // special - namespace tabs { + FireBlast: 251; // External + FireTrauma: 251; // Internal + ClawMastery: 252; + PsychicHammer: 253; + TigerStrike: 254; + DragonTalon: 255; + ShockWeb: 256; // External + ShockField: 256; // Internal + BladeSentinel: 257; + Quickness: 258; // Internal name + BurstofSpeed: 258; // Shown name + FistsofFire: 259; + DragonClaw: 260; + ChargedBoltSentry: 261; + WakeofFire: 262; // External + WakeofFireSentry: 262; // Internal + WeaponBlock: 263; + CloakofShadows: 264; + CobraStrike: 265; + BladeFury: 266; + Fade: 267; + ShadowWarrior: 268; + ClawsofThunder: 269; + DragonTail: 270; + LightningSentry: 271; + WakeofInferno: 272; // External + InfernoSentry: 272; // Internal + MindBlast: 273; + BladesofIce: 274; + DragonFlight: 275; + DeathSentry: 276; + BladeShield: 277; + Venom: 278; + ShadowMaster: 279; + PhoenixStrike: 280; // External + RoyalStrike: 280; // Internal + WakeofDestructionSentry: 281; // Not used? + Summoner: 500; // special + tabs: { // Ama - const BowandCrossbow: 0; - const PassiveandMagic: 1; - const JavelinandSpear: 2; + BowandCrossbow: 0; + PassiveandMagic: 1; + JavelinandSpear: 2; // Sorc - const Fire: 8; - const Lightning: 9; - const Cold: 10; + Fire: 8; + Lightning: 9; + Cold: 10; // Necro - const Curses: 16; - const PoisonandBone: 17; - const NecroSummoning: 18; + Curses: 16; + PoisonandBone: 17; + NecroSummoning: 18; // Pala - const PalaCombat: 24; - const Offensive: 25; - const Defensive: 26; + PalaCombat: 24; + Offensive: 25; + Defensive: 26; // Barb - const BarbCombat: 32; - const Masteries: 33; - const Warcries: 34; + BarbCombat: 32; + Masteries: 33; + Warcries: 34; // Druid - const DruidSummon: 40; - const ShapeShifting: 41; - const Elemental: 42; + DruidSummon: 40; + ShapeShifting: 41; + Elemental: 42; // Assa - const Traps: 48; - const ShadowDisciplines: 49; - const MartialArts: 50; - } - } - export const skillTabs: undefined; + Traps: 48; + ShadowDisciplines: 49; + MartialArts: 50; + }; + }; + skillTabs: undefined; - export namespace quest { - export namespace item { + quest: { + item: { // Act 1 - const WirtsLeg: 88; - const HoradricMalus: 89; - const ScrollofInifuss: 524; - const KeytotheCairnStones: 525; + WirtsLeg: 88; + HoradricMalus: 89; + ScrollofInifuss: 524; + KeytotheCairnStones: 525; // Act 2 - const FinishedStaff: 91; - const HoradricStaff: 91; - const IncompleteStaff: 92; - const ShaftoftheHoradricStaff: 92; - const ViperAmulet: 521; - const TopoftheHoradricStaff: 521; - const Cube: 549; - const BookofSkill: 552; + FinishedStaff: 91; + HoradricStaff: 91; + IncompleteStaff: 92; + ShaftoftheHoradricStaff: 92; + ViperAmulet: 521; + TopoftheHoradricStaff: 521; + Cube: 549; + BookofSkill: 552; // Act 3 - const DecoyGidbinn: 86; - const TheGidbinn: 87; - const KhalimsFlail: 173; - const KhalimsWill: 174; - const PotofLife: 545; - const AJadeFigurine: 546; - const JadeFigurine: 546; - const TheGoldenBird: 547; - const LamEsensTome: 548; - const KhalimsEye: 553; - const KhalimsHeart: 554; - const KhalimsBrain: 555; + DecoyGidbinn: 86; + TheGidbinn: 87; + KhalimsFlail: 173; + KhalimsWill: 174; + PotofLife: 545; + AJadeFigurine: 546; + JadeFigurine: 546; + TheGoldenBird: 547; + LamEsensTome: 548; + KhalimsEye: 553; + KhalimsHeart: 554; + KhalimsBrain: 555; // Act 4 - const HellForgeHammer: 90; - const Soulstone: 551; - const MephistosSoulstone: 551; + HellForgeHammer: 90; + Soulstone: 551; + MephistosSoulstone: 551; // Act 5 - const MalahsPotion: 644; - const ScrollofKnowledge: 645; - const ScrollofResistance: 646; + MalahsPotion: 644; + ScrollofKnowledge: 645; + ScrollofResistance: 646; // Pandemonium Event - const KeyofTerror: 647; - const KeyofHate: 648; - const KeyofDestruction: 649; - const DiablosHorn: 650; - const BaalsEye: 651; - const MephistosBrain: 652; - const StandardofHeroes: 658; + KeyofTerror: 647; + KeyofHate: 648; + KeyofDestruction: 649; + DiablosHorn: 650; + BaalsEye: 651; + MephistosBrain: 652; + StandardofHeroes: 658; // Essences/Token - const TokenofAbsolution: 653; - const TwistedEssenceofSuffering: 654; - const ChargedEssenceofHatred: 655; - const BurningEssenceofTerror: 656; - const FesteringEssenceofDestruction: 657; + TokenofAbsolution: 653; + TwistedEssenceofSuffering: 654; + ChargedEssenceofHatred: 655; + BurningEssenceofTerror: 656; + FesteringEssenceofDestruction: 657; // Misc - const TheBlackTowerKey: 544; - } - const items: [ + TheBlackTowerKey: 544; + }; + items: [ // act 1 88, 89, @@ -650,44 +650,44 @@ declare global { 645, 646, ]; - export namespace chest { + chest: { // act1 - const StoneAlpha: 17; - const StoneBeta: 18; - const StoneGamma: 19; - const StoneDelta: 20; - const StoneLambda: 21; - const StoneTheta: 22; // ? - const CainsJail: 26; - const InifussTree: 30; - const MalusHolder: 108; - const Wirt: 268; + StoneAlpha: 17; + StoneBeta: 18; + StoneGamma: 19; + StoneDelta: 20; + StoneLambda: 21; + StoneTheta: 22; // ? + CainsJail: 26; + InifussTree: 30; + MalusHolder: 108; + Wirt: 268; // act 2 - const ViperAmuletChest: 149; - const HoradricStaffHolder: 152; - const HoradricCubeChest: 354; - const HoradricScrollChest: 355; - const ShaftoftheHoradricStaffChest: 356; - const Journal: 357; + ViperAmuletChest: 149; + HoradricStaffHolder: 152; + HoradricCubeChest: 354; + HoradricScrollChest: 355; + ShaftoftheHoradricStaffChest: 356; + Journal: 357; // act 3 - const ForestAltar: 81; - const LamEsensTomeHolder: 193; - const GidbinnAltar: 252; - const KhalimsHeartChest: 405; - const KhalimsBrainChest: 406; - const KhalimsEyeChest: 407; + ForestAltar: 81; + LamEsensTomeHolder: 193; + GidbinnAltar: 252; + KhalimsHeartChest: 405; + KhalimsBrainChest: 406; + KhalimsEyeChest: 407; // act 4 - const HellForge: 376; + HellForge: 376; // act 5 - const BarbCage: 473; - const FrozenAnya: 558; - const AncientsAltar: 546; - } - const chests: [ + BarbCage: 473; + FrozenAnya: 558; + AncientsAltar: 546; + }; + chests: [ // act 1 17, 18, @@ -718,109 +718,109 @@ declare global { 558, 546, ]; - export namespace id { - const SpokeToWarriv: 0; - const DenofEvil: 1; - const SistersBurialGrounds: 2; - const TheSearchForCain: 4; - const ForgottenTower: 5; - const ToolsoftheTrade: 3; - const SistersToTheSlaughter: 6; - const AbleToGotoActII: 7; - const SpokeToJerhyn: 8; - const RadamentsLair: 9; - const TheHoradricStaff: 10; - const TheTaintedSun: 11; - const TheArcaneSanctuary: 12; - const TheSummoner: 13; - const TheSevenTombs: 14; - const AbleToGotoActIII: 15; - const SpokeToHratli: 16; - const TheGoldenBird: 20; - const BladeoftheOldReligion: 19; - const KhalimsWill: 18; - const LamEsensTome: 17; - const TheBlackenedTemple: 21; - const TheGuardian: 22; - const AbleToGotoActIV: 23; - const SpokeToTyrael: 24; - const TheFallenAngel: 25; - const HellsForge: 27; - const TerrorsEnd: 26; - const AbleToGotoActV: 28; - const SiegeOnHarrogath: 35; - const RescueonMountArreat: 36; - const PrisonofIce: 37; - const BetrayalofHarrogath: 38; - const RiteofPassage: 39; - const EyeofDestruction: 40; - const Respec: 41; - } + id: { + SpokeToWarriv: 0; + DenofEvil: 1; + SistersBurialGrounds: 2; + TheSearchForCain: 4; + ForgottenTower: 5; + ToolsoftheTrade: 3; + SistersToTheSlaughter: 6; + AbleToGotoActII: 7; + SpokeToJerhyn: 8; + RadamentsLair: 9; + TheHoradricStaff: 10; + TheTaintedSun: 11; + TheArcaneSanctuary: 12; + TheSummoner: 13; + TheSevenTombs: 14; + AbleToGotoActIII: 15; + SpokeToHratli: 16; + TheGoldenBird: 20; + BladeoftheOldReligion: 19; + KhalimsWill: 18; + LamEsensTome: 17; + TheBlackenedTemple: 21; + TheGuardian: 22; + AbleToGotoActIV: 23; + SpokeToTyrael: 24; + TheFallenAngel: 25; + HellsForge: 27; + TerrorsEnd: 26; + AbleToGotoActV: 28; + SiegeOnHarrogath: 35; + RescueonMountArreat: 36; + PrisonofIce: 37; + BetrayalofHarrogath: 38; + RiteofPassage: 39; + EyeofDestruction: 40; + Respec: 41; + }; // just common states for now - namespace states { - const Completed: 0; - const ReqComplete: 1; - const GreyedOut: 12; - const PartyMemberComplete: 13; - const CannotComplete: 14; - } - } + states: { + Completed: 0; + ReqComplete: 1; + GreyedOut: 12; + PartyMemberComplete: 13; + CannotComplete: 14; + }; + }; // in game data - export namespace uiflags { - const Inventory: 0x01; - const StatsWindow: 0x02; - const QuickSkill: 0x03; - const SkillWindow: 0x04; - const ChatBox: 0x05; - const NPCMenu: 0x08; - const EscMenu: 0x09; - const KeytotheCairnStonesScreen: 0x10; - const AutoMap: 0x0a; - const ConfigControls: 0x0b; - const Shop: 0x0c; - const ShowItem: 0x0d; - const SubmitItem: 0x0e; - const Quest: 0x0f; - const QuestLog: 0x11; - const StatusArea: 0x12; - const Waypoint: 0x14; - const MiniPanel: 0x15; - const Party: 0x16; - const TradePrompt: 0x17; - const Msgs: 0x18; - const Stash: 0x19; - const Cube: 0x1a; - const ShowBelt: 0x1f; - const Help: 0x21; - const MercScreen: 0x24; - const ScrollWindow: 0x25; - } + uiflags: { + Inventory: 0x01; + StatsWindow: 0x02; + QuickSkill: 0x03; + SkillWindow: 0x04; + ChatBox: 0x05; + NPCMenu: 0x08; + EscMenu: 0x09; + KeytotheCairnStonesScreen: 0x10; + AutoMap: 0x0a; + ConfigControls: 0x0b; + Shop: 0x0c; + ShowItem: 0x0d; + SubmitItem: 0x0e; + Quest: 0x0f; + QuestLog: 0x11; + StatusArea: 0x12; + Waypoint: 0x14; + MiniPanel: 0x15; + Party: 0x16; + TradePrompt: 0x17; + Msgs: 0x18; + Stash: 0x19; + Cube: 0x1a; + ShowBelt: 0x1f; + Help: 0x21; + MercScreen: 0x24; + ScrollWindow: 0x25; + }; - export namespace menu { - const Respec: 0x2ba0; - const Ok: 0x0d49; - const Talk: 0x0d35; - const Trade: 0x0d44; - const TradeRepair: 0x0d06; - const Imbue: 0x0fb1; - const Gamble: 0x0d46; - const Hire: 0x0d45; - const GoEast: 0x0d36; - const GoWest: 0x0d37; - const IdentifyItems: 0x0fb4; - const SailEast: 0x0d38; - const SailWest: 0x0d39; - const RessurectMerc: 0x1507; - const AddSockets: 0x58dc; - const Personalize: 0x58dd; - const TravelToHarrogath: 0x58d2; - } + menu: { + Respec: 0x2ba0; + Ok: 0x0d49; + Talk: 0x0d35; + Trade: 0x0d44; + TradeRepair: 0x0d06; + Imbue: 0x0fb1; + Gamble: 0x0d46; + Hire: 0x0d45; + GoEast: 0x0d36; + GoWest: 0x0d37; + IdentifyItems: 0x0fb4; + SailEast: 0x0d38; + SailWest: 0x0d39; + RessurectMerc: 0x1507; + AddSockets: 0x58dc; + Personalize: 0x58dd; + TravelToHarrogath: 0x58d2; + }; // shrine types - export namespace shrines { - const Presets: [2, 81, 83, 170, 344, 197, 202]; - const Ids: [ + shrines: { + Presets: [2, 81, 83, 170, 344, 197, 202]; + Ids: [ 2, 77, 81, @@ -910,754 +910,753 @@ declare global { 521, 522, ]; - const None: 0; - const Refilling: 1; - const Health: 2; - const Mana: 3; - const HealthExchange: 4; - const ManaExchange: 5; - const Armor: 6; - const Combat: 7; - const ResistFire: 8; - const ResistCold: 9; - const ResistLightning: 10; - const ResistPoison: 11; - const Skill: 12; - const ManaRecharge: 13; - const Stamina: 14; - const Experience: 15; - const Enirhs: 16; - const Portal: 17; - const Gem: 18; - const Fire: 19; - const Monster: 20; - const Exploding: 21; - const Poison: 22; - } + None: 0; + Refilling: 1; + Health: 2; + Mana: 3; + HealthExchange: 4; + ManaExchange: 5; + Armor: 6; + Combat: 7; + ResistFire: 8; + ResistCold: 9; + ResistLightning: 10; + ResistPoison: 11; + Skill: 12; + ManaRecharge: 13; + Stamina: 14; + Experience: 15; + Enirhs: 16; + Portal: 17; + Gem: 18; + Fire: 19; + Monster: 20; + Exploding: 21; + Poison: 22; + }; // unit states - export namespace states { - const None: 0; - const FrozenSolid: 1; - const Poison: 2; - const ResistFire: 3; - const ResistCold: 4; - const ResistLightning: 5; - const ResistMagic: 6; - const PlayerBody: 7; - const ResistAll: 8; - const AmplifyDamage: 9; - const FrozenArmor: 10; - const Frozen: 11; - const Inferno: 12; - const Blaze: 13; - const BoneArmor: 14; - const Concentrate: 15; - const Enchant: 16; - const InnerSight: 17; - const SkillMove: 18; - const Weaken: 19; - const ChillingArmor: 20; - const Stunned: 21; - const SpiderLay: 22; - const DimVision: 23; - const Slowed: 24; - const FetishAura: 25; - const Shout: 26; - const Taunt: 27; - const Conviction: 28; - const Convicted: 29; - const EnergyShield: 30; - const Venom: 31; - const BattleOrders: 32; - const Might: 33; - const Prayer: 34; - const HolyFire: 35; - const Thorns: 36; - const Defiance: 37; - const ThunderStorm: 38; - const LightningBolt: 39; - const BlessedAim: 40; - const Stamina: 41; - const Concentration: 42; - const Holywind: 43; - const HolyFreeze: 43; - const HolywindCold: 44; - const HolyFreezeCold: 44; - const Cleansing: 45; - const HolyShock: 46; - const Sanctuary: 47; - const Meditation: 48; - const Fanaticism: 49; - const Redemption: 50; - const BattleCommand: 51; - const PreventHeal: 52; - const Conversion: 53; - const Uninterruptable: 54; - const IronMaiden: 55; - const Terror: 56; - const Attract: 57; - const LifeTap: 58; - const Confuse: 59; - const Decrepify: 60; - const LowerResist: 61; - const OpenWounds: 62; - const Dopplezon: 63; - const Decoy: 63; - const CriticalStrike: 64; - const Dodge: 65; - const Avoid: 66; - const Penetrate: 67; - const Evade: 68; - const Pierce: 69; - const Warmth: 70; - const FireMastery: 71; - const LightningMastery: 72; - const ColdMastery: 73; - const SwordMastery: 74; - const AxeMastery: 75; - const MaceMastery: 76; - const PoleArmMastery: 77; - const ThrowingMastery: 78; - const SpearMastery: 79; - const IncreasedStamina: 80; - const IronSkin: 81; - const IncreasedSpeed: 82; - const NaturalResistance: 83; - const FingerMageCurse: 84; - const NoManaReg: 85; - const JustHit: 86; - const SlowMissiles: 87; - const ShiverArmor: 88; - const BattleCry: 89; - const Blue: 90; - const Red: 91; - const DeathDelay: 92; - const Valkyrie: 93; - const Frenzy: 94; - const Berserk: 95; - const Revive: 96; - const ItemFullSet: 97; - const SourceUnit: 98; - const Redeemed: 99; - const HealthPot: 100; - const HolyShield: 101; - const JustPortaled: 102; - const MonFrenzy: 103; - const CorpseNoDraw: 104; - const Alignment: 105; - const ManaPot: 106; - const Shatter: 107; - const SyncWarped: 108; - const ConversionSave: 109; - const Pregnat: 110; - const Rabies: 112; - const DefenceCurse: 113; - const BloodMana: 114; - const Burning: 115; - const DragonFlight: 116; - const Maul: 117; - const CorpseNoSelect: 118; - const ShadowWarrior: 119; - const FeralRage: 120; - const SkillDelay: 121; - const ProgressiveDamage: 122; - const ProgressiveSteal: 123; - const ProgressiveOther: 124; - const ProgressiveFire: 125; - const ProgressiveCold: 126; - const ProgressiveLighting: 127; - const ShrineArmor: 128; - const ShrineCombat: 129; - const ShrineResLighting: 130; - const ShrineResFire: 131; - const ShrineResCold: 132; - const ShrineResPoison: 133; - const ShrineSkill: 134; - const ShrineManaRegen: 135; - const ShrineStamina: 136; - const ShrineExperience: 137; - const FenrisRage: 138; - const Wolf: 139; - const Wearwolf: 139; - const Bear: 140; - const Wearbear: 140; - const Bloodlust: 141; - const ChangeClass: 142; - const Attached: 143; - const Hurricane: 144; - const Armageddon: 145; - const Invis: 146; - const Barbs: 147; - const HeartofWolverine: 148; - const OakSage: 149; - const VineBeast: 150; - const CycloneArmor: 151; - const ClawMastery: 152; - const CloakofShadows: 153; - const Recyled: 154; - const WeaponBlock: 155; - const Cloaked: 156; - const Quickness: 157; // Internal name - const BurstofSpeed: 157; // External name - const BladeShield: 158; - const Fade: 159; - const RestInPeace: 172; - const Glowing: 175; - const Delerium: 177; - const Antidote: 178; - const Thawing: 179; - const StaminaPot: 180; - } + states: { + None: 0; + FrozenSolid: 1; + Poison: 2; + ResistFire: 3; + ResistCold: 4; + ResistLightning: 5; + ResistMagic: 6; + PlayerBody: 7; + ResistAll: 8; + AmplifyDamage: 9; + FrozenArmor: 10; + Frozen: 11; + Inferno: 12; + Blaze: 13; + BoneArmor: 14; + Concentrate: 15; + Enchant: 16; + InnerSight: 17; + SkillMove: 18; + Weaken: 19; + ChillingArmor: 20; + Stunned: 21; + SpiderLay: 22; + DimVision: 23; + Slowed: 24; + FetishAura: 25; + Shout: 26; + Taunt: 27; + Conviction: 28; + Convicted: 29; + EnergyShield: 30; + Venom: 31; + BattleOrders: 32; + Might: 33; + Prayer: 34; + HolyFire: 35; + Thorns: 36; + Defiance: 37; + ThunderStorm: 38; + LightningBolt: 39; + BlessedAim: 40; + Stamina: 41; + Concentration: 42; + Holywind: 43; + HolyFreeze: 43; + HolywindCold: 44; + HolyFreezeCold: 44; + Cleansing: 45; + HolyShock: 46; + Sanctuary: 47; + Meditation: 48; + Fanaticism: 49; + Redemption: 50; + BattleCommand: 51; + PreventHeal: 52; + Conversion: 53; + Uninterruptable: 54; + IronMaiden: 55; + Terror: 56; + Attract: 57; + LifeTap: 58; + Confuse: 59; + Decrepify: 60; + LowerResist: 61; + OpenWounds: 62; + Dopplezon: 63; + Decoy: 63; + CriticalStrike: 64; + Dodge: 65; + Avoid: 66; + Penetrate: 67; + Evade: 68; + Pierce: 69; + Warmth: 70; + FireMastery: 71; + LightningMastery: 72; + ColdMastery: 73; + SwordMastery: 74; + AxeMastery: 75; + MaceMastery: 76; + PoleArmMastery: 77; + ThrowingMastery: 78; + SpearMastery: 79; + IncreasedStamina: 80; + IronSkin: 81; + IncreasedSpeed: 82; + NaturalResistance: 83; + FingerMageCurse: 84; + NoManaReg: 85; + JustHit: 86; + SlowMissiles: 87; + ShiverArmor: 88; + BattleCry: 89; + Blue: 90; + Red: 91; + DeathDelay: 92; + Valkyrie: 93; + Frenzy: 94; + Berserk: 95; + Revive: 96; + ItemFullSet: 97; + SourceUnit: 98; + Redeemed: 99; + HealthPot: 100; + HolyShield: 101; + JustPortaled: 102; + MonFrenzy: 103; + CorpseNoDraw: 104; + Alignment: 105; + ManaPot: 106; + Shatter: 107; + SyncWarped: 108; + ConversionSave: 109; + Pregnat: 110; + Rabies: 112; + DefenceCurse: 113; + BloodMana: 114; + Burning: 115; + DragonFlight: 116; + Maul: 117; + CorpseNoSelect: 118; + ShadowWarrior: 119; + FeralRage: 120; + SkillDelay: 121; + ProgressiveDamage: 122; + ProgressiveSteal: 123; + ProgressiveOther: 124; + ProgressiveFire: 125; + ProgressiveCold: 126; + ProgressiveLighting: 127; + ShrineArmor: 128; + ShrineCombat: 129; + ShrineResLighting: 130; + ShrineResFire: 131; + ShrineResCold: 132; + ShrineResPoison: 133; + ShrineSkill: 134; + ShrineManaRegen: 135; + ShrineStamina: 136; + ShrineExperience: 137; + FenrisRage: 138; + Wolf: 139; + Wearwolf: 139; + Bear: 140; + Wearbear: 140; + Bloodlust: 141; + ChangeClass: 142; + Attached: 143; + Hurricane: 144; + Armageddon: 145; + Invis: 146; + Barbs: 147; + HeartofWolverine: 148; + OakSage: 149; + VineBeast: 150; + CycloneArmor: 151; + ClawMastery: 152; + CloakofShadows: 153; + Recyled: 154; + WeaponBlock: 155; + Cloaked: 156; + Quickness: 157; // Internal name + BurstofSpeed: 157; // External name + BladeShield: 158; + Fade: 159; + RestInPeace: 172; + Glowing: 175; + Delerium: 177; + Antidote: 178; + Thawing: 179; + StaminaPot: 180; + }; - export namespace enchant { - const RandName: 1; - const HpMultiply: 2; - const AddLightRadius: 3; - const AddMLvl: 4; - const ExtraStrong: 5; - const ExtraFast: 6; - const Cursed: 7; - const MagicResistant: 8; - const FireEnchanted: 9; - const PoisonDeath: 10; - const InsectDeath: 11; - const ChainLightingDeath: 12; - const IgnoreTargetDefense: 13; - const UnknownMod: 14; - const KillMinionsDeath: 15; - const ChampMods: 16; - const LightningEnchanted: 17; - const ColdEnchanted: 18; - const UnusedMercMod: 19; - const ChargedBoltWhenStruck: 20; - const TempSummoned: 21; - const QuestMod: 22; - const PoisonField: 23; - const Thief: 24; - const ManaBurn: 25; - const Teleportation: 26; - const SpectralHit: 27; - const StoneSkin: 28; - const MultipleShots: 29; - const Aura: 30; - const CorpseExplosion: 31; - const FireExplosionOnDeath: 32; // not sure what the difference is between this and 9 - const FreezeOnDeath: 33; - const SelfResurrect: 34; - const IceShatter: 35; - const ChampStoned: 36; - const ChampStats: 37; - const ChampCurseImmune: 38; - } + enchant: { + RandName: 1; + HpMultiply: 2; + AddLightRadius: 3; + AddMLvl: 4; + ExtraStrong: 5; + ExtraFast: 6; + Cursed: 7; + MagicResistant: 8; + FireEnchanted: 9; + PoisonDeath: 10; + InsectDeath: 11; + ChainLightingDeath: 12; + IgnoreTargetDefense: 13; + UnknownMod: 14; + KillMinionsDeath: 15; + ChampMods: 16; + LightningEnchanted: 17; + ColdEnchanted: 18; + UnusedMercMod: 19; + ChargedBoltWhenStruck: 20; + TempSummoned: 21; + QuestMod: 22; + PoisonField: 23; + Thief: 24; + ManaBurn: 25; + Teleportation: 26; + SpectralHit: 27; + StoneSkin: 28; + MultipleShots: 29; + Aura: 30; + CorpseExplosion: 31; + FireExplosionOnDeath: 32; // not sure what the difference is between this and 9 + FreezeOnDeath: 33; + SelfResurrect: 34; + IceShatter: 35; + ChampStoned: 36; + ChampStats: 37; + ChampCurseImmune: 38; + }; // unit stats - export namespace stats { - const StunLength: 66; - const VelocityPercent: 67; - const OtherAnimrate: 69; - const HpRegen: 74; + stats: { + StunLength: 66; + VelocityPercent: 67; + OtherAnimrate: 69; + HpRegen: 74; - const LastBlockFrame: 95; - const State: 98; - const MonsterPlayerCount: 100; + LastBlockFrame: 95; + State: 98; + MonsterPlayerCount: 100; - const CurseResistance: 109; - const IronMaidenLevel: 129; - const LifeTapLevel: 130; + CurseResistance: 109; + IronMaidenLevel: 129; + LifeTapLevel: 130; - const Alignment: 172; - const Target0: 173; - const Target1: 174; - const GoldLost: 175; - const MinimumRequiredLevel: 176; - const ConversionLevel: 176; - const ConversionMaxHp: 177; - const UnitDooverlay: 178; - const AttackVsMontype: 179; - const DamageVsMontype: 180; + Alignment: 172; + Target0: 173; + Target1: 174; + GoldLost: 175; + MinimumRequiredLevel: 176; + ConversionLevel: 176; + ConversionMaxHp: 177; + UnitDooverlay: 178; + AttackVsMontype: 179; + DamageVsMontype: 180; - const ArmorOverridePercent: 182; - const FireLength: 315; - const BurningMin: 316; - const BurningMax: 317; - const ProgressiveDamage: 318; - const ProgressiveSteal: 319; - const ProgressiveOther: 320; - const ProgressiveFire: 321; - const ProgressiveCold: 322; - const ProgressiveLightning: 323; - const ProgressiveTohit: 325; - const PoisonCount: 326; - const DamageFramerate: 327; - const PierceIdx: 328; + ArmorOverridePercent: 182; + FireLength: 315; + BurningMin: 316; + BurningMax: 317; + ProgressiveDamage: 318; + ProgressiveSteal: 319; + ProgressiveOther: 320; + ProgressiveFire: 321; + ProgressiveCold: 322; + ProgressiveLightning: 323; + ProgressiveTohit: 325; + PoisonCount: 326; + DamageFramerate: 327; + PierceIdx: 328; - const ModifierListSkill: 350; - const ModifierListLevel: 351; + ModifierListSkill: 350; + ModifierListLevel: 351; - const LastSentHpPct: 352; - const SourceUnitType: 353; - const SourceUnitId: 354; + LastSentHpPct: 352; + SourceUnitType: 353; + SourceUnitId: 354; - const SkillThornsPercent: 131; - const SkillBoneArmor: 132; - const SkillCycloneArmor: 132; - const SkillBoneArmorMax: 133; - const SkillCycloneArmorMax: 133; - const SkillFade: 181; - const SkillPoisonOverrideLength: 101; - const SkillBypassUndead: 103; - const SkillBypassDemons: 104; - const SkillBypassBeasts: 106; - const SkillHandofAthena: 161; - const SkillStaminaPercent: 162; - const SkillPassiveStaminaPercent: 163; - const SkillConcentration: 164; - const SkillEnchant: 165; - const SkillPierce: 166; - const SkillConviction: 167; - const SkillChillingArmor: 168; - const SkillFrenzy: 169; - const SkillDecrepify: 170; - const SkillArmorPercent: 171; + SkillThornsPercent: 131; + SkillBoneArmor: 132; + SkillCycloneArmor: 132; + SkillBoneArmorMax: 133; + SkillCycloneArmorMax: 133; + SkillFade: 181; + SkillPoisonOverrideLength: 101; + SkillBypassUndead: 103; + SkillBypassDemons: 104; + SkillBypassBeasts: 106; + SkillHandofAthena: 161; + SkillStaminaPercent: 162; + SkillPassiveStaminaPercent: 163; + SkillConcentration: 164; + SkillEnchant: 165; + SkillPierce: 166; + SkillConviction: 167; + SkillChillingArmor: 168; + SkillFrenzy: 169; + SkillDecrepify: 170; + SkillArmorPercent: 171; - const Strength: 0; - const Energy: 1; - const Dexterity: 2; - const Vitality: 3; - const StatPts: 4; - const NewSkills: 5; - const HitPoints: 6; - const MaxHp: 7; - const Mana: 8; - const MaxMana: 9; - const Stamina: 10; - const MaxStamina: 11; - const Level: 12; - const Experience: 13; - const Gold: 14; - const GoldBank: 15; - const ArmorPercent: 16; - const MaxDamagePercent: 17; - const MinDamagePercent: 18; - const EnhancedDamage: 18; - const ToHit: 19; - const ToBlock: 20; - const MinDamage: 21; - const MaxDamage: 22; - const SecondaryMinDamage: 23; - const SecondaryMaxDamage: 24; - const DamagePercent: 25; - const ManaRecovery: 26; - const ManaRecoveryBonus: 27; - const StaminaRecoveryBonus: 28; - const LastExp: 29; - const NextExp: 30; - const ArmorClass: 31; - const Defense: 31; - const ArmorClassVsMissile: 32; - const ArmorClassVsHth: 33; - const NormalDamageReduction: 34; - const MagicDamageReduction: 35; - const DamageResist: 36; - const MagicResist: 37; - const MaxMagicResist: 38; - const FireResist: 39; - const MaxFireResist: 40; - const LightResist: 41; - const LightningResist: 41; - const MaxLightResist: 42; - const ColdResist: 43; - const MaxColdResist: 44; - const PoisonResist: 45; - const MaxPoisonResist: 46; - const DamageAura: 47; - const FireMinDamage: 48; - const FireMaxDamage: 49; - const LightMinDamage: 50; - const LightMaxDamage: 51; - const MagicMinDamage: 52; - const MagicMaxDamage: 53; - const ColdMinDamage: 54; - const ColdMaxDamage: 55; - const ColdLength: 56; - const PoisonMinDamage: 57; - const PoisonMaxDamage: 58; - const PoisonLength: 59; - const LifeDrainMinDamage: 60; - const LifeLeech: 60; - const LifeDrainMaxDamage: 61; - const ManaDrainMinDamage: 62; - const ManaLeech: 62; - const ManaDrainMaxDamage: 63; - const StaminaDrainMinDamage: 64; - const StaminaDrainMaxDamage: 65; - const AttackRate: 68; - const PreviousSkillRight: 181; - const PreviousSkillMiddle: 182; - const PreviousSkillLeft: 183; - const PassiveFireMastery: 329; - const PassiveLightningMastery: 330; - const PassiveColdMastery: 331; - const PassivePoisonMastery: 332; - const PassiveFirePierce: 333; - const PassiveLightningPierce: 334; - const PassiveColdPierce: 335; - const PassivePoisonPierce: 336; - const PassiveCriticalStrike: 337; - const PassiveDodge: 338; - const PassiveAvoid: 339; - const PassiveEvade: 340; - const PassiveWarmth: 341; - const PassiveMasteryMeleeTh: 342; - const PassiveMasteryMeleeDmg: 343; - const PassiveMasteryMeleeCrit: 344; - const PassiveMasteryThrowTh: 345; - const PassiveMasteryThrowDmg: 346; - const PassiveMasteryThrowCrit: 347; - const PassiveWeaponBlock: 348; - const PassiveSummonResist: 349; - const PassiveMagMastery: 357; - const PassiveMagPierce: 358; - const Quantity: 70; - const Value: 71; - const Durability: 72; - const MaxDurability: 73; - const MaxDurabilityPercent: 75; - const MaxHpPercent: 76; - const MaxManaPercent: 77; - const AttackerTakesDamage: 78; - const GoldBonus: 79; - const MagicBonus: 80; - const Knockback: 81; - const TimeDuration: 82; - const AddClassSkills: 83; - const AddExperience: 85; - const HealAfterKill: 86; - const ReducedPrices: 87; - const DoubleHerbDuration: 88; - const LightRadius: 89; - const LightColor: 90; - const ReqPercent: 91; - const LevelReq: 92; - const FasterAttackRate: 93; - const IAS: 93; - const LevelReqPct: 94; - const FasterMoveVelocity: 96; - const FRW: 96; - const NonClassSkill: 97; - const OSkill: 97; - const FasterGetHitRate: 99; - const FHR: 99; - const FasterBlockRate: 102; - const FBR: 102; - const FasterCastRate: 105; - const FCR: 105; - const SingleSkill: 107; - const RestinPeace: 108; - const PoisonLengthResist: 110; - const NormalDamage: 111; - const Howl: 112; - const Stupidity: 113; - const DamagetoMana: 114; - const IgnoreTargetAc: 115; - const IgnoreTargetDefense: 115; - const FractionalTargetAc: 116; - const PreventHeal: 117; - const HalfFreezeDuration: 118; - const ToHitPercent: 119; - const DamageTargetAc: 120; - const DemonDamagePercent: 121; - const UndeadDamagePercent: 122; - const DemontoHit: 123; - const UndeadtoHit: 124; - const Throwable: 125; - const ElemSkill: 126; - const AllSkills: 127; - const AttackerTakesLightDamage: 128; - const Freeze: 134; - const OpenWounds: 135; - const CrushingBlow: 136; - const KickDamage: 137; - const ManaAfterKill: 138; - const HealAfterDemonKill: 139; - const ExtraBlood: 140; - const DeadlyStrike: 141; - const AbsorbFirePercent: 142; - const AbsorbFire: 143; - const AbsorbLightPercent: 144; - const AbsorbLight: 145; - const AbsorbMagicPercent: 146; - const AbsorbMagic: 147; - const AbsorbColdPercent: 148; - const AbsorbCold: 149; - const AbsorbSlash: 262; - const AbsorbCrush: 263; - const AbsorbThrust: 264; - const AbsorbSlashPercent: 265; - const AbsorbCrushPercent: 266; - const AbsorbThrustPercent: 267; - const Slow: 150; - const Indestructible: 152; - const CannotbeFrozen: 153; - const StaminaDrainPct: 154; - const Reanimate: 155; - const Pierce: 156; - const MagicArrow: 157; - const ExplosiveArrow: 158; - const ThrowMinDamage: 159; - const ThrowMaxDamage: 160; - const AddSkillTab: 188; - const NumSockets: 194; - const SkillOnAura: 151; - const SkillOnAttack: 195; - const SkillOnKill: 196; - const SkillOnDeath: 197; - const SkillOnHit: 198; - const SkillOnStrike: 198; - const SkillOnLevelUp: 199; - const SkillOnGetHit: 201; - const SkillWhenStruck: 201; - const ChargedSkill: 204; - const PerLevelArmor: 214; - const PerLevelArmorPercent: 215; - const PerLevelHp: 216; - const PerLevelMana: 217; - const PerLevelMaxDamage: 218; - const PerLevelMaxDamagePercent: 219; - const PerLevelStrength: 220; - const PerLevelDexterity: 221; - const PerLevelEnergy: 222; - const PerLevelVitality: 223; - const PerLevelTohit: 224; - const PerLevelTohitPercent: 225; - const PerLevelColdDamageMax: 226; - const PerLevelFireDamageMax: 227; - const PerLevelLtngDamageMax: 228; - const PerLevelPoisDamageMax: 229; - const PerLevelResistCold: 230; - const PerLevelResistFire: 231; - const PerLevelResistLtng: 232; - const PerLevelResistPois: 233; - const PerLevelAbsorbCold: 234; - const PerLevelAbsorbFire: 235; - const PerLevelAbsorbLtng: 236; - const PerLevelAbsorbPois: 237; - const PerLevelThorns: 238; - const PerLevelFindGold: 239; - const PerLevelFindMagic: 240; - const PerLevelRegenstamina: 241; - const PerLevelStamina: 242; - const PerLevelDamageDemon: 243; - const PerLevelDamageUndead: 244; - const PerLevelTohitDemon: 245; - const PerLevelTohitUndead: 246; - const PerLevelCrushingblow: 247; - const PerLevelOpenwounds: 248; - const PerLevelKickDamage: 249; - const PerLevelDeadlystrike: 250; - const PerLevelFindGems: 251; - const ReplenishDurability: 252; - const ReplenishQuantity: 253; - const ExtraStack: 254; - const Find: 255; - const SlashDamage: 256; - const SlashDamagePercent: 257; - const CrushDamage: 258; - const CrushDamagePercent: 259; - const ThrustDamage: 260; - const ThrustDamagePercent: 261; - const ArmorByTime: 268; - const ArmorPercentByTime: 269; - const HpByTime: 270; - const ManaByTime: 271; - const MaxDamageByTime: 272; - const MaxDamagePercentByTime: 273; - const StrengthByTime: 274; - const DexterityByTime: 275; - const EnergyByTime: 276; - const VitalityByTime: 277; - const TohitByTime: 278; - const TohitPercentByTime: 279; - const ColdDamageMaxByTime: 280; - const FireDamageMaxByTime: 281; - const LtngDamageMaxByTime: 282; - const PoisDamageMaxByTime: 283; - const ResistColdByTime: 284; - const ResistFireByTime: 285; - const ResistLtngByTime: 286; - const ResistPoisByTime: 287; - const AbsorbColdByTime: 288; - const AbsorbFireByTime: 289; - const AbsorbLtngByTime: 290; - const AbsorbPoisByTime: 291; - const FindGoldByTime: 292; - const FindMagicByTime: 293; - const RegenstaminaByTime: 294; - const StaminaByTime: 295; - const DamageDemonByTime: 296; - const DamageUndeadByTime: 297; - const TohitDemonByTime: 298; - const TohitUndeadByTime: 299; - const CrushingBlowByTime: 300; - const OpenWoundsByTime: 301; - const KickDamageByTime: 302; - const DeadlyStrikeByTime: 303; - const FindGemsByTime: 304; - const PierceCold: 305; - const PierceFire: 306; - const PierceLtng: 307; - const PiercePois: 308; - const DamageVsMonster: 309; - const DamagePercentVsMonster: 310; - const TohitVsMonster: 311; - const TohitPercentVsMonster: 312; - const AcVsMonster: 313; - const AcPercentVsMonster: 314; - const ExtraCharges: 324; - const QuestDifficulty: 356; + Strength: 0; + Energy: 1; + Dexterity: 2; + Vitality: 3; + StatPts: 4; + NewSkills: 5; + HitPoints: 6; + MaxHp: 7; + Mana: 8; + MaxMana: 9; + Stamina: 10; + MaxStamina: 11; + Level: 12; + Experience: 13; + Gold: 14; + GoldBank: 15; + ArmorPercent: 16; + MaxDamagePercent: 17; + MinDamagePercent: 18; + EnhancedDamage: 18; + ToHit: 19; + ToBlock: 20; + MinDamage: 21; + MaxDamage: 22; + SecondaryMinDamage: 23; + SecondaryMaxDamage: 24; + DamagePercent: 25; + ManaRecovery: 26; + ManaRecoveryBonus: 27; + StaminaRecoveryBonus: 28; + LastExp: 29; + NextExp: 30; + ArmorClass: 31; + Defense: 31; + ArmorClassVsMissile: 32; + ArmorClassVsHth: 33; + NormalDamageReduction: 34; + MagicDamageReduction: 35; + DamageResist: 36; + MagicResist: 37; + MaxMagicResist: 38; + FireResist: 39; + MaxFireResist: 40; + LightResist: 41; + LightningResist: 41; + MaxLightResist: 42; + ColdResist: 43; + MaxColdResist: 44; + PoisonResist: 45; + MaxPoisonResist: 46; + DamageAura: 47; + FireMinDamage: 48; + FireMaxDamage: 49; + LightMinDamage: 50; + LightMaxDamage: 51; + MagicMinDamage: 52; + MagicMaxDamage: 53; + ColdMinDamage: 54; + ColdMaxDamage: 55; + ColdLength: 56; + PoisonMinDamage: 57; + PoisonMaxDamage: 58; + PoisonLength: 59; + LifeDrainMinDamage: 60; + LifeLeech: 60; + LifeDrainMaxDamage: 61; + ManaDrainMinDamage: 62; + ManaLeech: 62; + ManaDrainMaxDamage: 63; + StaminaDrainMinDamage: 64; + StaminaDrainMaxDamage: 65; + AttackRate: 68; + PreviousSkillRight: 181; + PreviousSkillMiddle: 182; + PreviousSkillLeft: 183; + PassiveFireMastery: 329; + PassiveLightningMastery: 330; + PassiveColdMastery: 331; + PassivePoisonMastery: 332; + PassiveFirePierce: 333; + PassiveLightningPierce: 334; + PassiveColdPierce: 335; + PassivePoisonPierce: 336; + PassiveCriticalStrike: 337; + PassiveDodge: 338; + PassiveAvoid: 339; + PassiveEvade: 340; + PassiveWarmth: 341; + PassiveMasteryMeleeTh: 342; + PassiveMasteryMeleeDmg: 343; + PassiveMasteryMeleeCrit: 344; + PassiveMasteryThrowTh: 345; + PassiveMasteryThrowDmg: 346; + PassiveMasteryThrowCrit: 347; + PassiveWeaponBlock: 348; + PassiveSummonResist: 349; + PassiveMagMastery: 357; + PassiveMagPierce: 358; + Quantity: 70; + Value: 71; + Durability: 72; + MaxDurability: 73; + MaxDurabilityPercent: 75; + MaxHpPercent: 76; + MaxManaPercent: 77; + AttackerTakesDamage: 78; + GoldBonus: 79; + MagicBonus: 80; + Knockback: 81; + TimeDuration: 82; + AddClassSkills: 83; + AddExperience: 85; + HealAfterKill: 86; + ReducedPrices: 87; + DoubleHerbDuration: 88; + LightRadius: 89; + LightColor: 90; + ReqPercent: 91; + LevelReq: 92; + FasterAttackRate: 93; + IAS: 93; + LevelReqPct: 94; + FasterMoveVelocity: 96; + FRW: 96; + NonClassSkill: 97; + OSkill: 97; + FasterGetHitRate: 99; + FHR: 99; + FasterBlockRate: 102; + FBR: 102; + FasterCastRate: 105; + FCR: 105; + SingleSkill: 107; + RestinPeace: 108; + PoisonLengthResist: 110; + NormalDamage: 111; + Howl: 112; + Stupidity: 113; + DamagetoMana: 114; + IgnoreTargetAc: 115; + IgnoreTargetDefense: 115; + FractionalTargetAc: 116; + PreventHeal: 117; + HalfFreezeDuration: 118; + ToHitPercent: 119; + DamageTargetAc: 120; + DemonDamagePercent: 121; + UndeadDamagePercent: 122; + DemontoHit: 123; + UndeadtoHit: 124; + Throwable: 125; + ElemSkill: 126; + AllSkills: 127; + AttackerTakesLightDamage: 128; + Freeze: 134; + OpenWounds: 135; + CrushingBlow: 136; + KickDamage: 137; + ManaAfterKill: 138; + HealAfterDemonKill: 139; + ExtraBlood: 140; + DeadlyStrike: 141; + AbsorbFirePercent: 142; + AbsorbFire: 143; + AbsorbLightPercent: 144; + AbsorbLight: 145; + AbsorbMagicPercent: 146; + AbsorbMagic: 147; + AbsorbColdPercent: 148; + AbsorbCold: 149; + AbsorbSlash: 262; + AbsorbCrush: 263; + AbsorbThrust: 264; + AbsorbSlashPercent: 265; + AbsorbCrushPercent: 266; + AbsorbThrustPercent: 267; + Slow: 150; + Indestructible: 152; + CannotbeFrozen: 153; + StaminaDrainPct: 154; + Reanimate: 155; + Pierce: 156; + MagicArrow: 157; + ExplosiveArrow: 158; + ThrowMinDamage: 159; + ThrowMaxDamage: 160; + AddSkillTab: 188; + NumSockets: 194; + SkillOnAura: 151; + SkillOnAttack: 195; + SkillOnKill: 196; + SkillOnDeath: 197; + SkillOnHit: 198; + SkillOnStrike: 198; + SkillOnLevelUp: 199; + SkillOnGetHit: 201; + SkillWhenStruck: 201; + ChargedSkill: 204; + PerLevelArmor: 214; + PerLevelArmorPercent: 215; + PerLevelHp: 216; + PerLevelMana: 217; + PerLevelMaxDamage: 218; + PerLevelMaxDamagePercent: 219; + PerLevelStrength: 220; + PerLevelDexterity: 221; + PerLevelEnergy: 222; + PerLevelVitality: 223; + PerLevelTohit: 224; + PerLevelTohitPercent: 225; + PerLevelColdDamageMax: 226; + PerLevelFireDamageMax: 227; + PerLevelLtngDamageMax: 228; + PerLevelPoisDamageMax: 229; + PerLevelResistCold: 230; + PerLevelResistFire: 231; + PerLevelResistLtng: 232; + PerLevelResistPois: 233; + PerLevelAbsorbCold: 234; + PerLevelAbsorbFire: 235; + PerLevelAbsorbLtng: 236; + PerLevelAbsorbPois: 237; + PerLevelThorns: 238; + PerLevelFindGold: 239; + PerLevelFindMagic: 240; + PerLevelRegenstamina: 241; + PerLevelStamina: 242; + PerLevelDamageDemon: 243; + PerLevelDamageUndead: 244; + PerLevelTohitDemon: 245; + PerLevelTohitUndead: 246; + PerLevelCrushingblow: 247; + PerLevelOpenwounds: 248; + PerLevelKickDamage: 249; + PerLevelDeadlystrike: 250; + PerLevelFindGems: 251; + ReplenishDurability: 252; + ReplenishQuantity: 253; + ExtraStack: 254; + Find: 255; + SlashDamage: 256; + SlashDamagePercent: 257; + CrushDamage: 258; + CrushDamagePercent: 259; + ThrustDamage: 260; + ThrustDamagePercent: 261; + ArmorByTime: 268; + ArmorPercentByTime: 269; + HpByTime: 270; + ManaByTime: 271; + MaxDamageByTime: 272; + MaxDamagePercentByTime: 273; + StrengthByTime: 274; + DexterityByTime: 275; + EnergyByTime: 276; + VitalityByTime: 277; + TohitByTime: 278; + TohitPercentByTime: 279; + ColdDamageMaxByTime: 280; + FireDamageMaxByTime: 281; + LtngDamageMaxByTime: 282; + PoisDamageMaxByTime: 283; + ResistColdByTime: 284; + ResistFireByTime: 285; + ResistLtngByTime: 286; + ResistPoisByTime: 287; + AbsorbColdByTime: 288; + AbsorbFireByTime: 289; + AbsorbLtngByTime: 290; + AbsorbPoisByTime: 291; + FindGoldByTime: 292; + FindMagicByTime: 293; + RegenstaminaByTime: 294; + StaminaByTime: 295; + DamageDemonByTime: 296; + DamageUndeadByTime: 297; + TohitDemonByTime: 298; + TohitUndeadByTime: 299; + CrushingBlowByTime: 300; + OpenWoundsByTime: 301; + KickDamageByTime: 302; + DeadlyStrikeByTime: 303; + FindGemsByTime: 304; + PierceCold: 305; + PierceFire: 306; + PierceLtng: 307; + PiercePois: 308; + DamageVsMonster: 309; + DamagePercentVsMonster: 310; + TohitVsMonster: 311; + TohitPercentVsMonster: 312; + AcVsMonster: 313; + AcPercentVsMonster: 314; + ExtraCharges: 324; + QuestDifficulty: 356; // doesn't exist but define for prototypes - const AllRes: 555; - } + AllRes: 555; + }; // unit info - export namespace unittype { - const Player: 0; - const NPC: 1; - const Monster: 1; - const Object: 2; - const Missile: 3; - const Item: 4; - const Stairs: 5; // const ToDo: might be more as stairs - } + unittype: { + Player: 0; + NPC: 1; + Monster: 1; + Object: 2; + Missile: 3; + Item: 4; + Stairs: 5; // ToDo: might be more as stairs + }; - export namespace player { - export namespace flag { - const Ignore: 2; - const Squelch: 4; - const Hostile: 8; - } - export namespace slot { - const Main: 0; - const Secondary: 1; - } - export namespace move { - const Walk: 0; - const Run: 1; - } - export namespace mode { + player: { + flag: { + Ignore: 2; + Squelch: 4; + Hostile: 8; + }; + slot: { + Main: 0; + Secondary: 1; + }; + move: { + Walk: 0; + Run: 1; + }; + mode: { // sdk.player.mode. - const Death: 0; - const StandingOutsideTown: 1; - const Walking: 2; - const Running: 3; - const GettingHit: 4; - const StandingInTown: 5; - const WalkingInTown: 6; - const Attacking1: 7; - const Attacking2: 8; - const Blocking: 9; - const CastingSkill: 10; - const ThrowingItem: 11; - const Kicking: 12; - const UsingSkill1: 13; - const UsingSkill2: 14; - const UsingSkill3: 15; - const UsingSkill4: 16; - const Dead: 17; - const SkillActionSequence: 18; - const KnockedBack: 19; - } - namespace _class { - const Amazon: 0; - const Sorceress: 1; - const Necromancer: 2; - const Paladin: 3; - const Barbarian: 4; - const Druid: 5; - const Assassin: 6; + Death: 0; + StandingOutsideTown: 1; + Walking: 2; + Running: 3; + GettingHit: 4; + StandingInTown: 5; + WalkingInTown: 6; + Attacking1: 7; + Attacking2: 8; + Blocking: 9; + CastingSkill: 10; + ThrowingItem: 11; + Kicking: 12; + UsingSkill1: 13; + UsingSkill2: 14; + UsingSkill3: 15; + UsingSkill4: 16; + Dead: 17; + SkillActionSequence: 18; + KnockedBack: 19; + }; + class: { + Amazon: 0; + Sorceress: 1; + Necromancer: 2; + Paladin: 3; + Barbarian: 4; + Druid: 5; + Assassin: 6; - const nameOf: ( + nameOf: ( classid: 0 | 1 | 2 | 3 | 4 | 5 | 6, ) => "Amazon" | "Sorceress" | "Necromancer" | "Paladin" | "Barbarian" | "Druid" | "Assassin" | false; - } - export type { _class as class }; - } + }; + }; - export namespace npcs { + npcs: { // same as monsters but more clear to use units.npcs.mode - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15; - } + mode: { + Death: 0; + Standing: 1; + Walking: 2; + GettingHit: 3; + Attacking1: 4; + Attacking2: 5; + Blocking: 6; + CastingSkill: 7; + UsingSkill1: 8; + UsingSkill2: 9; + UsingSkill3: 10; + UsingSkill4: 11; + Dead: 12; + KnockedBack: 13; + Spawning: 14; + Running: 15; + }; - const Akara: 148; - const Alkor: 254; - const Asheara: 252; - const WarrivAct1: 155; - const WarrivAct2: 175; - const Atma: 176; - const Tyrael: 367; - const Tyrael2: 251; - const Tyrael3: 521; - const Charsi: 154; - const DeckardCain1: 146; - const DeckardCain2: 244; - const DeckardCain3: 245; - const DeckardCain4: 246; - const DeckardCain5: 265; - const DeckardCain6: 520; - const Drognan: 177; - const Elzix: 199; - const Fara: 178; - const Gheed: 147; - const Greiz: 198; - const Halbu: 257; - const Hratli: 253; - const Jamella: 405; - const Jerhyn: 201; - const Kaelan: 331; - const Kashya: 150; - const Larzuk: 511; - const Lysander: 202; - const Malah: 513; - const Meshif: 210; - const Meshif2: 264; - const Natalya: 297; - const Ormus: 255; - const NihlathakNPC: 526; - const Qualkehk: 515; - const RogueScout: 270; - const TempleGuard1: 52; - const TempleGuard2: 665; - const TempleGuard3: 666; - const Townguard1: 535; - const Townguard2: 536; - } + Akara: 148; + Alkor: 254; + Asheara: 252; + WarrivAct1: 155; + WarrivAct2: 175; + Atma: 176; + Tyrael: 367; + Tyrael2: 251; + Tyrael3: 521; + Charsi: 154; + DeckardCain1: 146; + DeckardCain2: 244; + DeckardCain3: 245; + DeckardCain4: 246; + DeckardCain5: 265; + DeckardCain6: 520; + Drognan: 177; + Elzix: 199; + Fara: 178; + Gheed: 147; + Greiz: 198; + Halbu: 257; + Hratli: 253; + Jamella: 405; + Jerhyn: 201; + Kaelan: 331; + Kashya: 150; + Larzuk: 511; + Lysander: 202; + Malah: 513; + Meshif: 210; + Meshif2: 264; + Natalya: 297; + Ormus: 255; + NihlathakNPC: 526; + Qualkehk: 515; + RogueScout: 270; + TempleGuard1: 52; + TempleGuard2: 665; + TempleGuard3: 666; + Townguard1: 535; + Townguard2: 536; + }; - export namespace objects { - namespace mode { - const Inactive: 0; - const Interacted: 1; - const Active: 2; - } + objects: { + mode: { + Inactive: 0; + Interacted: 1; + Active: 2; + }; - const chestIds: [ + chestIds: [ 5, 6, 87, @@ -1720,3401 +1719,3402 @@ declare global { ]; // act1 - const MoldyTome: 8; - const A1TownFire: 39; - const A1Waypoint: 119; - const StoneAlpha: 17; - const StoneBeta: 18; - const StoneGamma: 19; - const StoneDelta: 20; - const StoneLambda: 21; - const StoneTheta: 22; - const CainsJail: 26; - const InifussTree: 30; - const Malus: 108; + MoldyTome: 8; + A1TownFire: 39; + A1Waypoint: 119; + StoneAlpha: 17; + StoneBeta: 18; + StoneGamma: 19; + StoneDelta: 20; + StoneLambda: 21; + StoneTheta: 22; + CainsJail: 26; + InifussTree: 30; + Malus: 108; // act 2 - const A2Waypoint: 156; - const A2UndergroundUpStairs: 22; - const TrapDoorA2: 74; // ancienttunnel/sewers act 2 - const DoorbyDockAct2: 75; // incorrect ? const TODO: figure out what 75 really corresponds to since the door is obj type 5 with classid 20 - const PortaltoDurielsLair: 100; - const HoradricStaffHolder: 152; - const ArcaneSanctuaryPortal: 298; - const HoradricCubeChest: 354; - const HoradricScrollChest: 355; - const Journal: 357; + A2Waypoint: 156; + A2UndergroundUpStairs: 22; + TrapDoorA2: 74; // ancienttunnel/sewers act 2 + DoorbyDockAct2: 75; // incorrect ? TODO: figure out what 75 really corresponds to since the door is obj type 5 with classid 20 + PortaltoDurielsLair: 100; + HoradricStaffHolder: 152; + ArcaneSanctuaryPortal: 298; + HoradricCubeChest: 354; + HoradricScrollChest: 355; + Journal: 357; // act 3 - const A3Waypoint: 237; - const ForestAltar: 81; - const LamEsensTome: 193; - const SewerStairsA3: 366; - const SewerLever: 367; - const DuranceEntryStairs: 386; - const RedPortalToAct4: 342; - const CompellingOrb: 404; + A3Waypoint: 237; + ForestAltar: 81; + LamEsensTome: 193; + SewerStairsA3: 366; + SewerLever: 367; + DuranceEntryStairs: 386; + RedPortalToAct4: 342; + CompellingOrb: 404; // act 4 - const A4Waypoint: 398; - const SealGlow: 131; - const DiabloStar: 255; - const DiabloSealInfector: 392; - const DiabloSealInfector2: 393; - const DiabloSealSeis: 394; - const DiabloSealVizier: 396; - const DiabloSealVizier2: 395; - const RedPortalToAct5: 566; // The one of tyreal + A4Waypoint: 398; + SealGlow: 131; + DiabloStar: 255; + DiabloSealInfector: 392; + DiabloSealInfector2: 393; + DiabloSealSeis: 394; + DiabloSealVizier: 396; + DiabloSealVizier2: 395; + RedPortalToAct5: 566; // The one of tyreal // act 5 - const A5Waypoint: 429; - const SideCavesA5: 75; // FrozenRiver, DrifterCavern; IcyCellar - const Act5Gate: 449; - const KorlictheProtectorStatue: 474; - const TalictheDefenderStatue: 475; - const MadawctheGuardianStatue: 476; - const AncientsAltar: 546; - const ArreatEnterAncientsWay: 564; - const ArreatEnterWorldstone: 547; - //const AncientsDoor: 547; - const AncientsDoor: 547; // Worldstone keep lvl 1 - const FrozenAnya: 558; - const FrozenAnyasPlatform: 460; - const NihlathaksPlatform: 462; - const WorldstonePortal: 563; + A5Waypoint: 429; + SideCavesA5: 75; // FrozenRiver, DrifterCavern; IcyCellar + Act5Gate: 449; + KorlictheProtectorStatue: 474; + TalictheDefenderStatue: 475; + MadawctheGuardianStatue: 476; + AncientsAltar: 546; + ArreatEnterAncientsWay: 564; + ArreatEnterWorldstone: 547; + //AncientsDoor: 547; + AncientsDoor: 547; // Worldstone keep lvl 1 + FrozenAnya: 558; + FrozenAnyasPlatform: 460; + NihlathaksPlatform: 462; + WorldstonePortal: 563; - const FrigidHighlandsChest: 455; - const IcyCellarChest: 397; + FrigidHighlandsChest: 455; + IcyCellarChest: 397; - const SmallSparklyChest: 397; - const LargeSparklyChest: 455; - const SuperChest: 580; + SmallSparklyChest: 397; + LargeSparklyChest: 455; + SuperChest: 580; // misc - const BubblingPoolofBlood: 82; - const HornShrine: 83; - const Stash: 267; - const BluePortal: 59; - const RedPortal: 60; - const Smoke: 401; - } + BubblingPoolofBlood: 82; + HornShrine: 83; + Stash: 267; + BluePortal: 59; + RedPortal: 60; + Smoke: 401; + }; - export namespace exits { - namespace type { - const WalkThrough: 1; - const Stairs: 2; - const RedPortal: 60; - } - namespace preset { - const AreaEntrance: 0; // special + exits: { + type: { + WalkThrough: 1; + Stairs: 2; + RedPortal: 60; + }; + preset: { + AreaEntrance: 0; // special // act 1 - const CaveHoleUp: 4; - const CaveHoleLvl2: 5; - const Crypt: 6; - const Mausoleum: 7; - const CryptMausExit: 8; - const JailUpStairs: 13; - const JailDownStairs: 14; - const CathedralDownStairs: 15; - const CathedralUpStairs: 16; - const CatacombsUpStairs: 17; - const CatacombsDownStairs: 18; + CaveHoleUp: 4; + CaveHoleLvl2: 5; + Crypt: 6; + Mausoleum: 7; + CryptMausExit: 8; + JailUpStairs: 13; + JailDownStairs: 14; + CathedralDownStairs: 15; + CathedralUpStairs: 16; + CatacombsUpStairs: 17; + CatacombsDownStairs: 18; // act 2 - const A2SewersTrapDoor: 19; - const A2EnterSewersDoor: 20; - const A2ExitSewersDoor: 21; - const A2UndergroundUpStairs: 22; - const A2DownStairs: 23; - const EnterHaremStairs: 24; - const ExitHaremStairs: 25; - const PreviousLevelHaremRight: 26; - const PreviousLevelHaremLeft: 27; - const NextLevelHaremRight: 28; - const NextLevelHaremLeft: 29; - const PreviousPalaceRight: 30; - const PreviousPalaceLeft: 31; - const NextLevelPalace: 32; - const EnterStonyTomb: 33; - const EnterHalls: 36; - const EnterTalTomb1: 38; - const EnterTalTomb2: 39; - const EnterTalTomb3: 40; - const EnterTalTomb4: 41; - const EnterTalTomb5: 42; - const EnterTalTomb6: 43; - const EnterTalTomb7: 44; - const PreviousAreaTomb: 45; - const NextLevelTomb: 46; - const EnterMaggotLair: 47; - const PreviousAreaMaggotLair: 48; - const NextLevelMaggotLair: 49; - const AncientTunnelsTrapDoor: 50; - const EntrancetoDurielsLair: 100; + A2SewersTrapDoor: 19; + A2EnterSewersDoor: 20; + A2ExitSewersDoor: 21; + A2UndergroundUpStairs: 22; + A2DownStairs: 23; + EnterHaremStairs: 24; + ExitHaremStairs: 25; + PreviousLevelHaremRight: 26; + PreviousLevelHaremLeft: 27; + NextLevelHaremRight: 28; + NextLevelHaremLeft: 29; + PreviousPalaceRight: 30; + PreviousPalaceLeft: 31; + NextLevelPalace: 32; + EnterStonyTomb: 33; + EnterHalls: 36; + EnterTalTomb1: 38; + EnterTalTomb2: 39; + EnterTalTomb3: 40; + EnterTalTomb4: 41; + EnterTalTomb5: 42; + EnterTalTomb6: 43; + EnterTalTomb7: 44; + PreviousAreaTomb: 45; + NextLevelTomb: 46; + EnterMaggotLair: 47; + PreviousAreaMaggotLair: 48; + NextLevelMaggotLair: 49; + AncientTunnelsTrapDoor: 50; + EntrancetoDurielsLair: 100; // act 3 - const EnterSpiderHole: 51; - const ExitSpiderHole: 52; - const EnterPit: 53; - const EnterDungeon: 54; - const PreviousAreaDungeon: 55; - const NextLevelDungeon: 56; - const A3EnterSewers: 57; - const A3ExitSewersUpperK: 58; - const A3SewersPreviousArea: 58; - const A3ExitSewers: 59; - const A3NextLevelSewers: 60; - const EnterTemple: 61; - const ExitTemple: 63; - const EnterDurance: 64; - const PreviousLevelDurance: 65; - const NextLevelDurance: 68; - const SewerStairsA3: 366; - const DuranceEntryStairs: 386; + EnterSpiderHole: 51; + ExitSpiderHole: 52; + EnterPit: 53; + EnterDungeon: 54; + PreviousAreaDungeon: 55; + NextLevelDungeon: 56; + A3EnterSewers: 57; + A3ExitSewersUpperK: 58; + A3SewersPreviousArea: 58; + A3ExitSewers: 59; + A3NextLevelSewers: 60; + EnterTemple: 61; + ExitTemple: 63; + EnterDurance: 64; + PreviousLevelDurance: 65; + NextLevelDurance: 68; + SewerStairsA3: 366; + DuranceEntryStairs: 386; // act 4 - const EnterRiverStairs: 69; - const ExitRiverStairs: 70; + EnterRiverStairs: 69; + ExitRiverStairs: 70; // act 5 - const EnterCrystal: 71; - const A5ExitCave: 73; - const A5NextLevelCave: 74; - const EnterSubLevelCave: 75; - const EnterNithsTemple: 76; - const PreviousAreaNithsTemple: 77; - const NextAreaNithsTemple: 78; - const ArreatEnterAncientsWay: 79; - const ArreatEnterWorldstone: 80; - const PreviousAreaWorldstone: 81; - const NextAreaWorldstone: 82; - } - } + EnterCrystal: 71; + A5ExitCave: 73; + A5NextLevelCave: 74; + EnterSubLevelCave: 75; + EnterNithsTemple: 76; + PreviousAreaNithsTemple: 77; + NextAreaNithsTemple: 78; + ArreatEnterAncientsWay: 79; + ArreatEnterWorldstone: 80; + PreviousAreaWorldstone: 81; + NextAreaWorldstone: 82; + }; + }; - export namespace monsters { - namespace preset { + monsters: { + preset: { // Confirmed - const Izual: 256; - const Bishibosh: 734; - const Bonebreak: 735; - const Coldcrow: 736; - const Rakanishu: 737; - const TreeheadWoodFist: 738; - const Griswold: 739; - const TheCountess: 740; - const PitspawnFouldog: 741; - const FlamespiketheCrawler: 742; - const BoneAsh: 743; - const Radament: 744; - const BloodwitchtheWild: 745; - const Fangskin: 746; - const Beetleburst: 747; - const CreepingFeature: 748; - const ColdwormtheBurrower: 749; - const FireEye: 750; - const DarkElder: 751; - const TheSummoner: 752; - const AncientKaatheSoulless: 753; - const TheSmith: 754; - const SszarktheBurning: 755; - const WitchDoctorEndugu: 756; - const Stormtree: 757; - const BattlemaidSarina: 758; - const IcehawkRiftwing: 759; - const IsmailVilehand: 760; - const GelebFlamefinger: 761; - const BremmSparkfist: 762; - const ToorcIcefist: 763; - const WyandVoidfinger: 764; - const MafferDragonhand: 765; - const WingedDeath: 766; - const Taintbreeder: 768; - const RiftwraiththeCannibal: 769; - const InfectorofSouls: 770; - const LordDeSeis: 771; - const GrandVizierofChaos: 772; - const TheCowKing: 773; - const Corpsefire: 774; - const Hephasto: 775; - const ShenktheOverseer: 776; - const TalictheDefender: 777; - const MadawctheGuardian: 778; - const KorlictheProtector: 779; - const AxeDweller: 780; - const BonesawBreaker: 781; - const DacFarren: 782; - const EldritchtheRectifier: 783; - const EyebacktheUnleashed: 784; - const ThreshSocket: 785; - const Pindleskin: 786; - const SnapchipShatter: 787; - const AnodizedElite: 788; - const VinvearMolech: 789; - const SharpToothSayer: 790; - const MagmaTorquer: 791; - const BlazeRipper: 792; - const Frozenstein: 793; - const Nihlathak: 794; - const ColenzotheAnnihilator: 795; - const AchmeltheCursed: 796; - const BartuctheBloody: 797; - const VentartheUnholy: 798; - const ListertheTormentor: 799; - const BloodRaven: 805; + Izual: 256; + Bishibosh: 734; + Bonebreak: 735; + Coldcrow: 736; + Rakanishu: 737; + TreeheadWoodFist: 738; + Griswold: 739; + TheCountess: 740; + PitspawnFouldog: 741; + FlamespiketheCrawler: 742; + BoneAsh: 743; + Radament: 744; + BloodwitchtheWild: 745; + Fangskin: 746; + Beetleburst: 747; + CreepingFeature: 748; + ColdwormtheBurrower: 749; + FireEye: 750; + DarkElder: 751; + TheSummoner: 752; + AncientKaatheSoulless: 753; + TheSmith: 754; + SszarktheBurning: 755; + WitchDoctorEndugu: 756; + Stormtree: 757; + BattlemaidSarina: 758; + IcehawkRiftwing: 759; + IsmailVilehand: 760; + GelebFlamefinger: 761; + BremmSparkfist: 762; + ToorcIcefist: 763; + WyandVoidfinger: 764; + MafferDragonhand: 765; + WingedDeath: 766; + Taintbreeder: 768; + RiftwraiththeCannibal: 769; + InfectorofSouls: 770; + LordDeSeis: 771; + GrandVizierofChaos: 772; + TheCowKing: 773; + Corpsefire: 774; + Hephasto: 775; + ShenktheOverseer: 776; + TalictheDefender: 777; + MadawctheGuardian: 778; + KorlictheProtector: 779; + AxeDweller: 780; + BonesawBreaker: 781; + DacFarren: 782; + EldritchtheRectifier: 783; + EyebacktheUnleashed: 784; + ThreshSocket: 785; + Pindleskin: 786; + SnapchipShatter: 787; + AnodizedElite: 788; + VinvearMolech: 789; + SharpToothSayer: 790; + MagmaTorquer: 791; + BlazeRipper: 792; + Frozenstein: 793; + Nihlathak: 794; + ColenzotheAnnihilator: 795; + AchmeltheCursed: 796; + BartuctheBloody: 797; + VentartheUnholy: 798; + ListertheTormentor: 799; + BloodRaven: 805; // Unconfirmed // Questionable - const GriefGrumble: 741; // JailLvl2 - const UniqueJailLvl3: 273; - const UniqueArcaneSanctuary: 371; - } - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15; - } - namespace spectype { - const All: 0; - const Super: 1; - const Champion: 2; - const Unique: 4; - const SuperUnique: 5; - const Magic: 6; - const Minion: 8; - } + GriefGrumble: 741; // JailLvl2 + UniqueJailLvl3: 273; + UniqueArcaneSanctuary: 371; + }; + mode: { + Death: 0; + Standing: 1; + Walking: 2; + GettingHit: 3; + Attacking1: 4; + Attacking2: 5; + Blocking: 6; + CastingSkill: 7; + UsingSkill1: 8; + UsingSkill2: 9; + UsingSkill3: 10; + UsingSkill4: 11; + Dead: 12; + KnockedBack: 13; + Spawning: 14; + Running: 15; + }; + spectype: { + All: 0; + Super: 1; + Champion: 2; + Unique: 4; + SuperUnique: 5; + Magic: 6; + Minion: 8; + }; // todo - determine what all these correlate to - namespace type { - const Undead: 1; - const Demon: 2; - const Insect: 3; - const Human: 4; - const Construct: 5; - const LowUndead: 6; - const HighUndead: 7; - const Skeleton: 8; - const Zombie: 9; - const BigHead: 10; - const FoulCrow: 11; - const Fallen: 12; - const Brute: 13; - const SandRaider: 14; - const Wraith: 15; - const CorruptRogue: 16; - const Baboon: 17; - const GoatMan: 18; - const QuillRat: 19; - const SandMaggot: 20; - const Viper: 21; - const SandLeaper: 22; - const PantherWoman: 23; - const Swarm: 24; - const Scarab: 25; - const Mummy: 26; - const Unraveler: 27; - const Vulture: 28; - const Mosquito: 29; - const WillowWisp: 30; - const Arach: 31; - const ThornHulk: 32; - const Vampire: 33; - const BatDemon: 34; - const Fetish: 35; - const Blunderbore: 36; - const UndeadFetish: 37; - const Zakarum: 38; - const FrogDemon: 39; - const Tentacle: 40; - const FingerMage: 41; - const Golem: 42; - const Vilekind: 43; - const Regurgitator: 44; - const DoomKnight: 45; - const CouncilMember: 46; - const MegaDemon: 47; - const Bovine: 48; - const SeigeBeast: 49; - const SnowYeti: 50; - const Minion: 51; - const Succubus: 52; - const Overseer: 53; - const Imp: 54; - const FrozenHorror: 55; - const BloodLord: 56; - const DeathMauler: 57; - const PutridDefiler: 58; - } - const Raven: 419; - const PoisonCreeper: 425; - const CarrionVine: 426; - const SolarCreeper: 427; - const DiablosBoneCage: 340; - const DiablosBoneCage2: 342; - const Dummy1: 149; - const Dummy2: 268; - const AbyssKnight: 311; - const Afflicted: 10; - const Afflicted2: 580; - const AlbinoRoach: 95; - const Ancient1: 104; - const Ancient2: 669; - const Ancient3: 670; - const Apparition: 41; - const Arach1: 122; - const Arach2: 685; - const Assailant: 33; - const Assailant2: 603; - const BaalColdMage: 381; - const Balrog1: 360; - const Balrog2: 686; - const Banished: 135; - const Barbs: 422; - const Bear1: 428; - const Bear2: 431; - const Beast: 441; - const BerserkSlayer: 462; - const BlackArcher: 163; - const BlackLancer1: 168; - const BlackLancer2: 617; - const BlackLocusts: 88; - const BlackRaptor1: 17; - const BlackRaptor2: 592; - const BlackRogue: 46; - const BlackSoul1: 121; - const BlackSoul2: 640; - const BlackVultureNest: 208; - const BloodBoss: 482; - const BloodBringer: 443; - const BloodClan1: 55; - const BloodClan2: 588; - const BloodDiver: 139; - const BloodGolem: 290; - const BloodHawk1: 16; - const BloodHawk2: 591; - const BloodHawkNest: 207; - const BloodHook: 116; - const BloodHookNest: 336; - const BloodLord1: 134; - const BloodLord2: 695; - const BloodWing: 117; - const BloodWingNest: 337; - const Blunderbore1: 186; - const Blunderbore2: 618; - const BoneArcher1: 172; - const BoneArcher2: 576; - const BoneMage1: 275; - const BoneMage2: 380; - const BoneMage3: 384; - const BoneMage4: 388; - const BoneMage5: 624; - const BoneWarrior1: 2; - const BoneWarrior2: 648; - const HellBovine: 391; - const BrambleHulk: 128; - const Brute: 24; - const Bunny: 556; - const BurningDead: 3; - const BurningDeadArcher1: 173; - const BurningDeadArcher2: 575; - const BurningDeadArcher3: 577; - const BurningDeadMage1: 276; - const BurningDeadMage2: 385; - const BurningDeadMage3: 389; - const BurningDeadMage4: 621; - const BurningSoul1: 641; - const BurningSoul2: 120; - const Cadaver1: 100; - const Cadaver2: 703; - const Cantor: 239; - const CarrionBird1: 110; - const CarrionBird2: 608; - const Carver1: 642; - const Carver2: 20; - const CarverShaman: 645; - const CarverShaman2: 59; - const CaveLeaper1: 79; - const CaveLeaper2: 629; - const ClawViper1: 74; - const ClawViper2: 594; - const CloudStalker1: 18; - const CloudStalker2: 593; - const CloudStalkerNest: 209; - const Combatant1: 522; - const Combatant2: 523; - const ConsumedFireBoar: 464; - const ConsumedIceBoar: 463; - const CorpseSpitter: 308; - const Corpulent: 307; - const Creature1: 248; - const Creature2: 427; - const Creeper: 413; - const CrushBiest: 442; - const Crusher: 26; - const Damned1: 14; - const Damned2: 584; - const DarkArcher1: 162; - const DarkArcher2: 614; - const DarkFamiliar: 140; - const DarkHunter: 43; - const DarkLancer1: 167; - const DarkLancer2: 616; - const DarkLord1: 133; - const DarkLord2: 697; - const DarkOne1: 22; - const DarkOne2: 644; - const DarkRanger: 160; - const DarkShaman1: 61; - const DarkShaman2: 647; - const DarkShape: 42; - const DarkSpearwoman: 165; - const DarkStalker: 45; - const DeamonSteed: 445; - const DeathClan1: 57; - const DeathClan2: 589; - const Decayed: 97; - const DefiledWarrior: 440; - const Defiler1: 546; - const Defiler2: 547; - const Defiler3: 548; - const Defiler4: 549; - const Defiler5: 550; - const DesertWing: 136; - const Destruction: 410; - const Devilkin: 643; - const Devilkin2: 21; - const DevilkinShaman: 646; - const DevilkinShaman2: 60; - const Devourer: 70; - const DevourerEgg: 192; - const DevourerQueen: 286; - const DevourerYoung: 182; - const Disfigured: 13; - const Disfigured2: 583; - const Dominus1: 474; - const Dominus2: 636; - const DoomApe: 51; - const DoomKnight: 310; - const DoomKnight1: 699; - const DoomKnight2: 700; - const Drehya1: 512; - const Drehya2: 527; - const DriedCorpse: 96; - const DrownedCarcass: 8; - const DuneBeast: 48; - const DungSoldier: 91; - const Dweller: 247; - const Eagle: 429; - const Embalmed: 98; - const Faithful: 236; - const Fallen: 19; - const FallenShaman: 58; - const FanaticMinion: 461; - const Feeder: 115; - const FeederNest: 335; - const Fenris: 421; - const Fetish1: 142; - const BoneFetish2: 213; - const Fetish3: 397; - const FetishShaman: 279; - const Fiend1: 137; - const Fiend2: 651; - const FireBoar: 456; - const FireTower: 372; - const FlameSpider: 125; - const Flayer1: 143; - const BoneFetish3: 214; - const Flayer3: 398; - const Flayer4: 659; - const Flayer5: 656; - const FlayerShaman1: 280; - const FlayerShaman2: 662; - const FleshArcher: 164; - const FleshBeast1: 301; - const FleshBeast2: 678; - const FleshHunter: 47; - const FleshLancer: 169; - const FleshSpawner1: 298; - const FleshSpawner2: 676; - const FlyingScimitar: 234; - const FoulCrow: 15; - const FoulCrow2: 590; - const FoulCrowNest: 206; - const FrenziedHellSpawn: 465; - const FrenziedIceSpawn: 466; - const GargantuanBeast: 28; - const Geglash: 200; - const Ghost1: 38; - const Ghost2: 631; - const Ghoul: 7; - const GhoulLord1: 131; - const GhoulLord2: 696; - const GiantLamprey: 71; - const GiantLampreyEgg: 193; - const GiantLampreyQueen: 287; - const GiantLampreyYoung: 183; - const GiantUrchin: 317; - const Gloam1: 118; - const Gloam2: 639; - const Gloombat1: 138; - const Gloombat2: 650; - const Gorbelly: 187; - const GoreBearer: 444; - const GreaterHellSpawn1: 459; - const GreaterHellSpawn2: 684; - const GreaterIceSpawn: 460; - const Groper: 304; - const Grotesque1: 300; - const Grotesque2: 675; - const GrotesqueWyrm1: 303; - const GrotesqueWyrm2: 677; - const Guardian1: 102; - const Guardian2: 667; - const Hawk: 419; - const Heirophant1: 240; - const Heirophant2: 241; - const Heirophant3: 673; - const Heirophant4: 674; - const HellBuzzard: 112; - const HellCat: 86; - const HellClan1: 56; - const HellClan2: 587; - const HellSlinger: 376; - const HellSpawn1: 457; - const HellSpawn2: 683; - const HellSwarm: 90; - const HellWhip: 483; - const HollowOne: 101; - const Horror: 4; - const Horror1: 501; - const Horror2: 502; - const Horror3: 503; - const Horror4: 504; - const Horror5: 505; - const HorrorArcher1: 174; - const HorrorArcher2: 579; - const HorrorMage1: 277; - const HorrorMage2: 382; - const HorrorMage3: 386; - const HorrorMage4: 390; - const HorrorMage5: 623; - const HorrorMage6: 625; - const HorrorMage7: 626; - const Hs1: 560; - const HungryDead: 6; - const Huntress1: 83; - const Huntress2: 627; - const Hut: 528; - const Hydra1: 351; - const Hydra2: 352; - const Hydra3: 353; - const IceBoar: 455; - const IceSpawn: 458; - const Imp1: 492; - const Imp2: 493; - const Imp3: 494; - const Imp4: 495; - const Imp5: 496; - const Imp6: 688; - const Imp7: 689; - const Infidel1: 32; - const Infidel2: 600; - const InsaneHellSpawn: 467; - const InsaneIceSpawn: 468; - const Invader1: 31; - const Invader2: 602; - const Itchies: 87; - const JungleHunter: 50; - const JungleUrchin: 67; - const Larva: 283; - const Lasher: 480; - const LightningSpire: 371; - const Lord1: 506; - const Lord2: 507; - const Lord3: 508; - const Lord4: 509; - const Lord5: 510; - const Lord6: 652; - const Lord7: 653; - const Maggot: 227; - const Malachai: 408; - const Marauder: 30; - const Marauder2: 599; - const Master: 418; - const Mauler: 188; - const Mauler1: 529; - const Mauler12: 604; - const Mauler2: 530; - const Mauler3: 531; - const Mauler4: 532; - const Mauler5: 533; - const Mauler6: 619; - const MawFiend: 694; - const MawFiend2: 309; - const Council1: 345; - const Council2: 346; - const Council3: 347; - const Council4: 557; - const Minion1: 572; - const Minion2: 573; - const Enslaved: 453; - const MinionSlayerSpawner: 485; - const MinionSpawner: 484; - const Misshapen1: 12; - const Misshapen2: 582; - const MoonClan1: 53; - const MoonClan2: 585; - const BaalSubjectMummy: 105; - const Navi: 266; - const Flavie: 266; - const NightClan1: 54; - const NightClan2: 586; - const NightLord: 132; - const NightMarauder: 295; - const NightSlinger1: 375; - const NightSlinger2: 395; - const NightTiger: 85; - const OblivionKnight1: 312; - const OblivionKnight2: 701; - const OblivionKnight3: 702; - const OverLord: 481; - const OverSeer: 479; - const PitLord1: 361; - const PitLord2: 687; - const PitViper1: 76; - const PitViper2: 595; - const PlagueBearer: 9; - const PlagueBugs: 89; - const PoisonSpinner: 124; - const PreservedDead: 99; - const ProwlingDead: 438; - const QuillBear: 313; - const QuillRat1: 63; - const QuillRat2: 605; - const RatMan1: 141; - const RatMan2: 396; - const BoneFetish1: 212; - const RatMan4: 407; - const RatManShaman: 278; - const RazorBeast: 316; - const RazorPitDemon: 82; - const RazorSpine1: 66; - const RazorSpine2: 607; - const ReanimatedHorde: 437; - const Returned1: 1; - const Returned2: 649; - const ReturnedArcher1: 171; - const ReturnedArcher2: 578; - const ReturnedMage: 274; - const ReturnedMage1: 379; - const ReturnedMage2: 383; - const ReturnedMage3: 387; - const ReturnedMage4: 620; - const ReturnedMage5: 622; - const RiverStalkerHead: 262; - const RiverStalkerLimb: 259; - const RockDweller: 49; - const RockWorm: 69; - const RockWormEgg: 191; - const RockWormQueen: 285; - const RockWormYoung: 181; - const RotWalker: 436; - const SaberCat1: 84; - const SaberCat2: 628; - const Salamander1: 75; - const Salamander2: 596; - const SandFisher: 123; - const SandLeaper: 78; - const SandMaggot: 68; - const SandMaggotEgg: 190; - const SandMaggotYoung: 180; - const SandRaider1: 29; - const SandRaider2: 601; - const DeathBeetle: 92; - const Scarab1: 93; - const Scarab2: 654; - const Sentry1: 411; - const Sentry2: 412; - const Sentry3: 415; - const Sentry4: 416; - const SerpentMagus1: 77; - const SerpentMagus2: 598; - const Sexton: 238; - const Skeleton: 0; - const SkeletonArcher: 170; - const Slayerexp1: 454; - const Slayerexp2: 682; - const Slinger1: 373; - const Slinger2: 610; - const Slinger3: 611; - const Slinger4: 612; - const SnowYeti1: 446; - const SnowYeti2: 447; - const SnowYeti3: 448; - const SnowYeti4: 449; - const SoulKiller: 691; - const SoulKiller1: 399; - const SoulKiller2: 144; - const SoulKiller3: 215; - const SoulKiller4: 658; - const SoulKiller5: 661; - const SoulKillerShaman1: 664; - const SoulKillerShaman2: 281; - const SpearCat: 394; - const SpearCat1: 374; - const Specter1: 40; - const Specter2: 633; - const SpiderMagus: 126; - const SpikeFiend1: 64; - const SpikeFiend2: 606; - const Spikefist: 130; - const SpikeGiant: 314; - const SteelWeevil1: 94; - const SteelWeevil2: 655; - const StormCaster1: 306; - const StormCaster2: 693; - const Strangler1: 305; - const Strangler2: 692; - const StygianDog: 302; - const StygianDoll1: 145; - const StygianDoll2: 216; - const StygianDoll3: 400; - const StygianDoll4: 660; - const StygianDoll5: 657; - const StygianDoll6: 690; - const StygianDollShaman1: 663; - const StygianDollShaman2: 282; - const StygianFury: 476; - const StygianHag: 299; - const StygianHarlot: 471; - const StygianWatcherHead: 263; - const StygianWatcherLimb: 260; - const Succubusexp1: 469; - const Succubusexp2: 634; - const Sucker: 114; - const SuckerNest: 334; - const Summoner: 250; - const SwampGhost: 119; - const Tainted: 11; - const Tainted2: 581; - const Taunt: 545; - const Temptress1: 472; - const Temptress2: 473; - const Temptress3: 635; - const Tentacle1: 562; - const Tentacle2: 563; - const Tentacle3: 564; - const Tentacle4: 565; - const Tentacle5: 566; - const ThornBeast: 65; - const ThornBrute: 315; - const ThornedHulk1: 127; - const ThornedHulk2: 609; - const Thrasher: 129; - const TombCreeper1: 80; - const TombCreeper2: 630; - const TombViper1: 73; - const TombViper2: 597; - const TrappedSoul1: 403; - const TrappedSoul2: 404; - const TreeLurker: 81; - const UndeadScavenger: 111; - const UnholyCorpse1: 439; - const UnholyCorpse2: 698; - const Unraveler1: 103; - const Unraveler2: 668; - const Urdar: 189; - const VenomLord1: 362; - const VenomLord2: 558; - const VileArcher1: 161; - const VileArcher2: 613; - const VileHunter: 44; - const VileLancer1: 166; - const VileLancer2: 615; - const VileTemptress: 470; - const VileWitch1: 475; - const VileWitch2: 638; - const WailingBeast: 27; - const WarpedFallen: 23; - const WarpedShaman: 62; - const Warrior: 417; - const WaterWatcherHead: 261; - const WaterWatcherLimb: 258; - const WingedNightmare: 113; - const Witch1: 637; - const Witch2: 477; - const Witch3: 478; - const Wolf1: 359; - const Wolf2: 420; - const Wolf3: 430; - const WolfRider1: 450; - const WolfRider2: 451; - const WolfRider3: 452; - const WorldKiller1: 679; - const WorldKiller2: 72; - const WorldKillerEgg1: 681; - const WorldKillerEgg2: 194; - const WorldKillerQueen: 288; - const WorldKillerYoung1: 680; - const WorldKillerYoung2: 184; - const Worm1: 551; - const Worm2: 552; - const Worm3: 553; - const Worm4: 554; - const Worm5: 555; - const Wraith1: 39; - const Wraith2: 632; - const Yeti: 25; - const Zakarumite: 235; - const Zealot1: 237; - const Zealot2: 671; - const Zealot3: 672; - const Zombie: 5; + type: { + Undead: 1; + Demon: 2; + Insect: 3; + Human: 4; + Construct: 5; + LowUndead: 6; + HighUndead: 7; + Skeleton: 8; + Zombie: 9; + BigHead: 10; + FoulCrow: 11; + Fallen: 12; + Brute: 13; + SandRaider: 14; + Wraith: 15; + CorruptRogue: 16; + Baboon: 17; + GoatMan: 18; + QuillRat: 19; + SandMaggot: 20; + Viper: 21; + SandLeaper: 22; + PantherWoman: 23; + Swarm: 24; + Scarab: 25; + Mummy: 26; + Unraveler: 27; + Vulture: 28; + Mosquito: 29; + WillowWisp: 30; + Arach: 31; + ThornHulk: 32; + Vampire: 33; + BatDemon: 34; + Fetish: 35; + Blunderbore: 36; + UndeadFetish: 37; + Zakarum: 38; + FrogDemon: 39; + Tentacle: 40; + FingerMage: 41; + Golem: 42; + Vilekind: 43; + Regurgitator: 44; + DoomKnight: 45; + CouncilMember: 46; + MegaDemon: 47; + Bovine: 48; + SeigeBeast: 49; + SnowYeti: 50; + Minion: 51; + Succubus: 52; + Overseer: 53; + Imp: 54; + FrozenHorror: 55; + BloodLord: 56; + DeathMauler: 57; + PutridDefiler: 58; + }; + Raven: 419; + PoisonCreeper: 425; + CarrionVine: 426; + SolarCreeper: 427; + DiablosBoneCage: 340; + DiablosBoneCage2: 342; + Dummy1: 149; + Dummy2: 268; + AbyssKnight: 311; + Afflicted: 10; + Afflicted2: 580; + AlbinoRoach: 95; + Ancient1: 104; + Ancient2: 669; + Ancient3: 670; + Apparition: 41; + Arach1: 122; + Arach2: 685; + Assailant: 33; + Assailant2: 603; + BaalColdMage: 381; + Balrog1: 360; + Balrog2: 686; + Banished: 135; + Barbs: 422; + Bear1: 428; + Bear2: 431; + Beast: 441; + BerserkSlayer: 462; + BlackArcher: 163; + BlackLancer1: 168; + BlackLancer2: 617; + BlackLocusts: 88; + BlackRaptor1: 17; + BlackRaptor2: 592; + BlackRogue: 46; + BlackSoul1: 121; + BlackSoul2: 640; + BlackVultureNest: 208; + BloodBoss: 482; + BloodBringer: 443; + BloodClan1: 55; + BloodClan2: 588; + BloodDiver: 139; + BloodGolem: 290; + BloodHawk1: 16; + BloodHawk2: 591; + BloodHawkNest: 207; + BloodHook: 116; + BloodHookNest: 336; + BloodLord1: 134; + BloodLord2: 695; + BloodWing: 117; + BloodWingNest: 337; + Blunderbore1: 186; + Blunderbore2: 618; + BoneArcher1: 172; + BoneArcher2: 576; + BoneMage1: 275; + BoneMage2: 380; + BoneMage3: 384; + BoneMage4: 388; + BoneMage5: 624; + BoneWarrior1: 2; + BoneWarrior2: 648; + HellBovine: 391; + BrambleHulk: 128; + Brute: 24; + Bunny: 556; + BurningDead: 3; + BurningDeadArcher1: 173; + BurningDeadArcher2: 575; + BurningDeadArcher3: 577; + BurningDeadMage1: 276; + BurningDeadMage2: 385; + BurningDeadMage3: 389; + BurningDeadMage4: 621; + BurningSoul1: 641; + BurningSoul2: 120; + Cadaver1: 100; + Cadaver2: 703; + Cantor: 239; + CarrionBird1: 110; + CarrionBird2: 608; + Carver1: 642; + Carver2: 20; + CarverShaman: 645; + CarverShaman2: 59; + CaveLeaper1: 79; + CaveLeaper2: 629; + ClawViper1: 74; + ClawViper2: 594; + CloudStalker1: 18; + CloudStalker2: 593; + CloudStalkerNest: 209; + Combatant1: 522; + Combatant2: 523; + ConsumedFireBoar: 464; + ConsumedIceBoar: 463; + CorpseSpitter: 308; + Corpulent: 307; + Creature1: 248; + Creature2: 427; + Creeper: 413; + CrushBiest: 442; + Crusher: 26; + Damned1: 14; + Damned2: 584; + DarkArcher1: 162; + DarkArcher2: 614; + DarkFamiliar: 140; + DarkHunter: 43; + DarkLancer1: 167; + DarkLancer2: 616; + DarkLord1: 133; + DarkLord2: 697; + DarkOne1: 22; + DarkOne2: 644; + DarkRanger: 160; + DarkShaman1: 61; + DarkShaman2: 647; + DarkShape: 42; + DarkSpearwoman: 165; + DarkStalker: 45; + DeamonSteed: 445; + DeathClan1: 57; + DeathClan2: 589; + Decayed: 97; + DefiledWarrior: 440; + Defiler1: 546; + Defiler2: 547; + Defiler3: 548; + Defiler4: 549; + Defiler5: 550; + DesertWing: 136; + Destruction: 410; + Devilkin: 643; + Devilkin2: 21; + DevilkinShaman: 646; + DevilkinShaman2: 60; + Devourer: 70; + DevourerEgg: 192; + DevourerQueen: 286; + DevourerYoung: 182; + Disfigured: 13; + Disfigured2: 583; + Dominus1: 474; + Dominus2: 636; + DoomApe: 51; + DoomKnight: 310; + DoomKnight1: 699; + DoomKnight2: 700; + Drehya1: 512; + Drehya2: 527; + DriedCorpse: 96; + DrownedCarcass: 8; + DuneBeast: 48; + DungSoldier: 91; + Dweller: 247; + Eagle: 429; + Embalmed: 98; + Faithful: 236; + Fallen: 19; + FallenShaman: 58; + FanaticMinion: 461; + Feeder: 115; + FeederNest: 335; + Fenris: 421; + Fetish1: 142; + BoneFetish2: 213; + Fetish3: 397; + FetishShaman: 279; + Fiend1: 137; + Fiend2: 651; + FireBoar: 456; + FireTower: 372; + FlameSpider: 125; + Flayer1: 143; + BoneFetish3: 214; + Flayer3: 398; + Flayer4: 659; + Flayer5: 656; + FlayerShaman1: 280; + FlayerShaman2: 662; + FleshArcher: 164; + FleshBeast1: 301; + FleshBeast2: 678; + FleshHunter: 47; + FleshLancer: 169; + FleshSpawner1: 298; + FleshSpawner2: 676; + FlyingScimitar: 234; + FoulCrow: 15; + FoulCrow2: 590; + FoulCrowNest: 206; + FrenziedHellSpawn: 465; + FrenziedIceSpawn: 466; + GargantuanBeast: 28; + Geglash: 200; + Ghost1: 38; + Ghost2: 631; + Ghoul: 7; + GhoulLord1: 131; + GhoulLord2: 696; + GiantLamprey: 71; + GiantLampreyEgg: 193; + GiantLampreyQueen: 287; + GiantLampreyYoung: 183; + GiantUrchin: 317; + Gloam1: 118; + Gloam2: 639; + Gloombat1: 138; + Gloombat2: 650; + Gorbelly: 187; + GoreBearer: 444; + GreaterHellSpawn1: 459; + GreaterHellSpawn2: 684; + GreaterIceSpawn: 460; + Groper: 304; + Grotesque1: 300; + Grotesque2: 675; + GrotesqueWyrm1: 303; + GrotesqueWyrm2: 677; + Guardian1: 102; + Guardian2: 667; + Hawk: 419; + Heirophant1: 240; + Heirophant2: 241; + Heirophant3: 673; + Heirophant4: 674; + HellBuzzard: 112; + HellCat: 86; + HellClan1: 56; + HellClan2: 587; + HellSlinger: 376; + HellSpawn1: 457; + HellSpawn2: 683; + HellSwarm: 90; + HellWhip: 483; + HollowOne: 101; + Horror: 4; + Horror1: 501; + Horror2: 502; + Horror3: 503; + Horror4: 504; + Horror5: 505; + HorrorArcher1: 174; + HorrorArcher2: 579; + HorrorMage1: 277; + HorrorMage2: 382; + HorrorMage3: 386; + HorrorMage4: 390; + HorrorMage5: 623; + HorrorMage6: 625; + HorrorMage7: 626; + Hs1: 560; + HungryDead: 6; + Huntress1: 83; + Huntress2: 627; + Hut: 528; + Hydra1: 351; + Hydra2: 352; + Hydra3: 353; + IceBoar: 455; + IceSpawn: 458; + Imp1: 492; + Imp2: 493; + Imp3: 494; + Imp4: 495; + Imp5: 496; + Imp6: 688; + Imp7: 689; + Infidel1: 32; + Infidel2: 600; + InsaneHellSpawn: 467; + InsaneIceSpawn: 468; + Invader1: 31; + Invader2: 602; + Itchies: 87; + JungleHunter: 50; + JungleUrchin: 67; + Larva: 283; + Lasher: 480; + LightningSpire: 371; + Lord1: 506; + Lord2: 507; + Lord3: 508; + Lord4: 509; + Lord5: 510; + Lord6: 652; + Lord7: 653; + Maggot: 227; + Malachai: 408; + Marauder: 30; + Marauder2: 599; + Master: 418; + Mauler: 188; + Mauler1: 529; + Mauler12: 604; + Mauler2: 530; + Mauler3: 531; + Mauler4: 532; + Mauler5: 533; + Mauler6: 619; + MawFiend: 694; + MawFiend2: 309; + Council1: 345; + Council2: 346; + Council3: 347; + Council4: 557; + Minion1: 572; + Minion2: 573; + Enslaved: 453; + MinionSlayerSpawner: 485; + MinionSpawner: 484; + Misshapen1: 12; + Misshapen2: 582; + MoonClan1: 53; + MoonClan2: 585; + BaalSubjectMummy: 105; + Navi: 266; + Flavie: 266; + NightClan1: 54; + NightClan2: 586; + NightLord: 132; + NightMarauder: 295; + NightSlinger1: 375; + NightSlinger2: 395; + NightTiger: 85; + OblivionKnight1: 312; + OblivionKnight2: 701; + OblivionKnight3: 702; + OverLord: 481; + OverSeer: 479; + PitLord1: 361; + PitLord2: 687; + PitViper1: 76; + PitViper2: 595; + PlagueBearer: 9; + PlagueBugs: 89; + PoisonSpinner: 124; + PreservedDead: 99; + ProwlingDead: 438; + QuillBear: 313; + QuillRat1: 63; + QuillRat2: 605; + RatMan1: 141; + RatMan2: 396; + BoneFetish1: 212; + RatMan4: 407; + RatManShaman: 278; + RazorBeast: 316; + RazorPitDemon: 82; + RazorSpine1: 66; + RazorSpine2: 607; + ReanimatedHorde: 437; + Returned1: 1; + Returned2: 649; + ReturnedArcher1: 171; + ReturnedArcher2: 578; + ReturnedMage: 274; + ReturnedMage1: 379; + ReturnedMage2: 383; + ReturnedMage3: 387; + ReturnedMage4: 620; + ReturnedMage5: 622; + RiverStalkerHead: 262; + RiverStalkerLimb: 259; + RockDweller: 49; + RockWorm: 69; + RockWormEgg: 191; + RockWormQueen: 285; + RockWormYoung: 181; + RotWalker: 436; + SaberCat1: 84; + SaberCat2: 628; + Salamander1: 75; + Salamander2: 596; + SandFisher: 123; + SandLeaper: 78; + SandMaggot: 68; + SandMaggotEgg: 190; + SandMaggotYoung: 180; + SandRaider1: 29; + SandRaider2: 601; + DeathBeetle: 92; + Scarab1: 93; + Scarab2: 654; + Sentry1: 411; + Sentry2: 412; + Sentry3: 415; + Sentry4: 416; + SerpentMagus1: 77; + SerpentMagus2: 598; + Sexton: 238; + Skeleton: 0; + SkeletonArcher: 170; + Slayerexp1: 454; + Slayerexp2: 682; + Slinger1: 373; + Slinger2: 610; + Slinger3: 611; + Slinger4: 612; + SnowYeti1: 446; + SnowYeti2: 447; + SnowYeti3: 448; + SnowYeti4: 449; + SoulKiller: 691; + SoulKiller1: 399; + SoulKiller2: 144; + SoulKiller3: 215; + SoulKiller4: 658; + SoulKiller5: 661; + SoulKillerShaman1: 664; + SoulKillerShaman2: 281; + SpearCat: 394; + SpearCat1: 374; + Specter1: 40; + Specter2: 633; + SpiderMagus: 126; + SpikeFiend1: 64; + SpikeFiend2: 606; + Spikefist: 130; + SpikeGiant: 314; + SteelWeevil1: 94; + SteelWeevil2: 655; + StormCaster1: 306; + StormCaster2: 693; + Strangler1: 305; + Strangler2: 692; + StygianDog: 302; + StygianDoll1: 145; + StygianDoll2: 216; + StygianDoll3: 400; + StygianDoll4: 660; + StygianDoll5: 657; + StygianDoll6: 690; + StygianDollShaman1: 663; + StygianDollShaman2: 282; + StygianFury: 476; + StygianHag: 299; + StygianHarlot: 471; + StygianWatcherHead: 263; + StygianWatcherLimb: 260; + Succubusexp1: 469; + Succubusexp2: 634; + Sucker: 114; + SuckerNest: 334; + Summoner: 250; + SwampGhost: 119; + Tainted: 11; + Tainted2: 581; + Taunt: 545; + Temptress1: 472; + Temptress2: 473; + Temptress3: 635; + Tentacle1: 562; + Tentacle2: 563; + Tentacle3: 564; + Tentacle4: 565; + Tentacle5: 566; + ThornBeast: 65; + ThornBrute: 315; + ThornedHulk1: 127; + ThornedHulk2: 609; + Thrasher: 129; + TombCreeper1: 80; + TombCreeper2: 630; + TombViper1: 73; + TombViper2: 597; + TrappedSoul1: 403; + TrappedSoul2: 404; + TreeLurker: 81; + UndeadScavenger: 111; + UnholyCorpse1: 439; + UnholyCorpse2: 698; + Unraveler1: 103; + Unraveler2: 668; + Urdar: 189; + VenomLord1: 362; + VenomLord2: 558; + VileArcher1: 161; + VileArcher2: 613; + VileHunter: 44; + VileLancer1: 166; + VileLancer2: 615; + VileTemptress: 470; + VileWitch1: 475; + VileWitch2: 638; + WailingBeast: 27; + WarpedFallen: 23; + WarpedShaman: 62; + Warrior: 417; + WaterWatcherHead: 261; + WaterWatcherLimb: 258; + WingedNightmare: 113; + Witch1: 637; + Witch2: 477; + Witch3: 478; + Wolf1: 359; + Wolf2: 420; + Wolf3: 430; + WolfRider1: 450; + WolfRider2: 451; + WolfRider3: 452; + WorldKiller1: 679; + WorldKiller2: 72; + WorldKillerEgg1: 681; + WorldKillerEgg2: 194; + WorldKillerQueen: 288; + WorldKillerYoung1: 680; + WorldKillerYoung2: 184; + Worm1: 551; + Worm2: 552; + Worm3: 553; + Worm4: 554; + Worm5: 555; + Wraith1: 39; + Wraith2: 632; + Yeti: 25; + Zakarumite: 235; + Zealot1: 237; + Zealot2: 671; + Zealot3: 672; + Zombie: 5; // Bosses/Ubers - const Andariel: 156; - const Duriel: 211; - const Mephisto: 242; - const Diablo: 243; - const DiabloClone: 333; - const ThroneBaal: 543; - const Baal: 544; - const BaalClone: 570; - const UberMephisto: 704; - const UberBaal: 705; - const UberIzual: 706; - const Lilith: 707; - const UberDuriel: 708; - const UberDiablo: 709; + Andariel: 156; + Duriel: 211; + Mephisto: 242; + Diablo: 243; + DiabloClone: 333; + ThroneBaal: 543; + Baal: 544; + BaalClone: 570; + UberMephisto: 704; + UberBaal: 705; + UberIzual: 706; + Lilith: 707; + UberDuriel: 708; + UberDiablo: 709; // Mini-Bosses - const TheSmith: 402; - const BloodRaven: 267; - const Radament: 229; - const TheSummoner: 250; - const Griswold: 365; - const Izual: 256; - const Hephasto: 409; - const TalictheDefender: 540; - const MadawctheGuardian: 541; - const KorlictheProtector: 542; - const ListerTheTormenter: 571; - const TheCowKing: 743; - const ColdwormtheBurrower: 284; - const Nihlathak: 526; + TheSmith: 402; + BloodRaven: 267; + Radament: 229; + TheSummoner: 250; + Griswold: 365; + Izual: 256; + Hephasto: 409; + TalictheDefender: 540; + MadawctheGuardian: 541; + KorlictheProtector: 542; + ListerTheTormenter: 571; + TheCowKing: 743; + ColdwormtheBurrower: 284; + Nihlathak: 526; // Objects - const Turret1: 348; - const Turret2: 349; - const Turret3: 350; - const CatapultS: 497; - const CatapultE: 498; - const CatapultSiege: 499; - const CatapultW: 500; - const Compellingorb: 366; - const GargoyleTrap: 273; - const MummyGenerator: 228; - const Stairs: 559; - const BarricadeDoor1: 432; - const BarricadeDoor2: 433; - const PrisonDoor: 434; - const BarricadeTower: 435; - const BarricadeWall1: 524; - const BarricadeWall2: 525; + Turret1: 348; + Turret2: 349; + Turret3: 350; + CatapultS: 497; + CatapultE: 498; + CatapultSiege: 499; + CatapultW: 500; + Compellingorb: 366; + GargoyleTrap: 273; + MummyGenerator: 228; + Stairs: 559; + BarricadeDoor1: 432; + BarricadeDoor2: 433; + PrisonDoor: 434; + BarricadeTower: 435; + BarricadeWall1: 524; + BarricadeWall2: 525; // Misc? - const Youngdiablo: 368; - const Left: 525; - const Life: 426; - const Effect: 574; - const Pet: 414; - const Prince: 249; - const POW: 534; - const Right: 524; - const Sage: 424; - const Town: 514; - const Cow: 179; - } + Youngdiablo: 368; + Left: 525; + Life: 426; + Effect: 574; + Pet: 414; + Prince: 249; + POW: 534; + Right: 524; + Sage: 424; + Town: 514; + Cow: 179; + }; - export namespace summons { - namespace type { - const Valkyrie: 2; - const Golem: 3; - const Skeleton: 4; - const SkeletonMage: 5; - const Revive: 6; - const Mercenary: 7; - const Dopplezon: 8; - const Raven: 10; - const SpiritWolf: 11; - const Fenris: 12; - const DireWolf: 12; - const Totem: 13; - const Spirit: 13; - const Vine: 14; - const Grizzly: 15; - const ShadowWarrior: 16; - const Shadow: 16; - const AssassinTrap: 17; - const Hydra: 19; - } + summons: { + type: { + Valkyrie: 2; + Golem: 3; + Skeleton: 4; + SkeletonMage: 5; + Revive: 6; + Mercenary: 7; + Dopplezon: 8; + Raven: 10; + SpiritWolf: 11; + Fenris: 12; + DireWolf: 12; + Totem: 13; + Spirit: 13; + Vine: 14; + Grizzly: 15; + ShadowWarrior: 16; + Shadow: 16; + AssassinTrap: 17; + Hydra: 19; + }; - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15; - } + mode: { + Death: 0; + Standing: 1; + Walking: 2; + GettingHit: 3; + Attacking1: 4; + Attacking2: 5; + Blocking: 6; + CastingSkill: 7; + UsingSkill1: 8; + UsingSkill2: 9; + UsingSkill3: 10; + UsingSkill4: 11; + Dead: 12; + KnockedBack: 13; + Spawning: 14; + Running: 15; + }; - const ClayGolem: 289; - const Dopplezon: 356; - const Valkyrie: 357; - const FireGolem: 292; - const IronGolem: 291; - const NecroMage: 364; - const NecroSkeleton: 363; - const Poppy: 425; - const Wolverine: 423; - } + ClayGolem: 289; + Dopplezon: 356; + Valkyrie: 357; + FireGolem: 292; + IronGolem: 291; + NecroMage: 364; + NecroSkeleton: 363; + Poppy: 425; + Wolverine: 423; + }; - export namespace mercs { - namespace mode { - const Death: 0; - const Standing: 1; - const Walking: 2; - const GettingHit: 3; - const Attacking1: 4; - const Attacking2: 5; - const Blocking: 6; - const CastingSkill: 7; - const UsingSkill1: 8; - const UsingSkill2: 9; - const UsingSkill3: 10; - const UsingSkill4: 11; - const Dead: 12; - const KnockedBack: 13; - const Spawning: 14; - const Running: 15; - } + mercs: { + mode: { + Death: 0; + Standing: 1; + Walking: 2; + GettingHit: 3; + Attacking1: 4; + Attacking2: 5; + Blocking: 6; + CastingSkill: 7; + UsingSkill1: 8; + UsingSkill2: 9; + UsingSkill3: 10; + UsingSkill4: 11; + Dead: 12; + KnockedBack: 13; + Spawning: 14; + Running: 15; + }; - const Rogue: 271; - const Guard: 338; - const IronWolf: 359; - const A5Barb: 561; - } + Rogue: 271; + Guard: 338; + IronWolf: 359; + A5Barb: 561; + }; - export namespace missiles { - const DiabloLightning: 172; - const FissureCrack1: 462; - const FissureCrack2: 463; - } + missiles: { + DiabloLightning: 172; + FissureCrack1: 462; + FissureCrack2: 463; + }; - export namespace storage { - const Equipped: 1; - const Belt: 2; - const Inventory: 3; - const TradeWindow: 5; - const Cube: 6; - const Stash: 7; - } + storage: { + Equipped: 1; + Belt: 2; + Inventory: 3; + TradeWindow: 5; + Cube: 6; + Stash: 7; + }; - export namespace node { - const NotOnPlayer: 0; - const Storage: 1; - const Belt: 2; - const Equipped: 3; - const Cursor: 4; - } + node: { + NotOnPlayer: 0; + Storage: 1; + Belt: 2; + Equipped: 3; + Cursor: 4; + }; // Same apply's for merc with less things available - export namespace body { - const None: 0; - const Head: 1; - const Neck: 2; - const Torso: 3; - const Armor: 3; - const RightArm: 4; - const LeftArm: 5; - const RingRight: 6; - const RingLeft: 7; - const Belt: 8; - const Feet: 9; - const Gloves: 10; - const RightArmSecondary: 11; - const LeftArmSecondary: 12; - } + body: { + None: 0; + Head: 1; + Neck: 2; + Torso: 3; + Armor: 3; + RightArm: 4; + LeftArm: 5; + RingRight: 6; + RingLeft: 7; + Belt: 8; + Feet: 9; + Gloves: 10; + RightArmSecondary: 11; + LeftArmSecondary: 12; + }; - export namespace items { - export namespace cost { - const ToBuy: 0; - const ToSell: 1; - const ToRepair: 2; - } - export namespace flags { - const Equipped: 0x00000001; - const InSocket: 0x00000008; - const Identified: 0x00000010; - const OnActiveWeaponSlot: 0x00000040; - const OnSwapWeaponSlot: 0x00000080; - const Broken: 0x00000100; - const FullRejuv: 0x00000400; - const Socketed: 0x00000800; - const InTradeGamble: 0x00002000; - const NotInSocket: 0x00004000; - const Ear: 0x00010000; - const StartingItem: 0x00020000; - const RuneQuestPotion: 0x00200000; - const Ethereal: 0x00400000; - const IsAnItem: 0x00800000; - const Personalized: 0x01000000; - const Runeword: 0x04000000; - } - export namespace mode { - const inStorage: 0; //Item inven stash cube store = Item inven stash cube store - const Equipped: 1; // Item equipped self or merc - const inBelt: 2; // Item in belt - const onGround: 3; // Item on ground - const onCursor: 4; // Item on cursor - const Dropping: 5; // Item being dropped - const Socketed: 6; // Item socketed in item - } - export namespace quality { - const LowQuality: 1; - const Normal: 2; - const Superior: 3; - const Magic: 4; - const Set: 5; - const Rare: 6; - const Unique: 7; - const Crafted: 8; - } - export namespace _class1 { - const Normal: 0; - const Exceptional: 1; - const Elite: 2; - } - export type { _class1 as class }; - export namespace type { - const Shield: 2; - const Armor: 3; - const Gold: 4; - const BowQuiver: 5; - const CrossbowQuiver: 6; - const PlayerBodyPart: 7; - const Herb: 8; - const Potion: 9; - const Ring: 10; - const Elixir: 11; - const Amulet: 12; - const Charm: 13; - const notused0: 14; - const Boots: 15; - const Gloves: 16; - const notused1: 17; - const Book: 18; - const Belt: 19; - const Gem: 20; - const Torch: 21; - const Scroll: 22; - const notused2: 23; - const Scepter: 24; - const Wand: 25; - const Staff: 26; - const Bow: 27; - const Axe: 28; - const Club: 29; - const Sword: 30; - const Hammer: 31; - const Knife: 32; - const Spear: 33; - const Polearm: 34; - const Crossbow: 35; - const Mace: 36; - const Helm: 37; - const MissilePotion: 38; - const Quest: 39; - const Bodypart: 40; - const Key: 41; - const ThrowingKnife: 42; - const ThrowingAxe: 43; - const Javelin: 44; - const Weapon: 45; - const MeleeWeapon: 46; - const MissileWeapon: 47; - const ThrownWeapon: 48; - const ComboWeapon: 49; - const AnyArmor: 50; - const AnyShield: 51; - const Miscellaneous: 52; - const SocketFiller: 53; - const Secondhand: 54; - const StavesandRods: 55; - const Missile: 56; - const Blunt: 57; - const Jewel: 58; - const ClassSpecific: 59; - const AmazonItem: 60; - const BarbarianItem: 61; - const NecromancerItem: 62; - const PaladinItem: 63; - const SorceressItem: 64; - const AssassinItem: 65; - const DruidItem: 66; - const HandtoHand: 67; - const Orb: 68; - const VoodooHeads: 69; - const AuricShields: 70; - const PrimalHelm: 71; - const Pelt: 72; - const Cloak: 73; - const Rune: 74; - const Circlet: 75; - const HealingPotion: 76; - const ManaPotion: 77; - const RejuvPotion: 78; - const StaminaPotion: 79; - const AntidotePotion: 80; - const ThawingPotion: 81; - const SmallCharm: 82; - const LargeCharm: 83; - const GrandCharm: 84; - const AmazonBow: 85; - const AmazonSpear: 86; - const AmazonJavelin: 87; - const AssassinClaw: 88; - const MagicBowQuiv: 89; - const MagicxBowQuiv: 90; - const ChippedGem: 91; - const FlawedGem: 92; - const StandardGem: 93; - const FlawlessGem: 94; - const PerfectgGem: 95; - const Amethyst: 96; - const Diamond: 97; - const Emerald: 98; - const Ruby: 99; - const Sapphire: 100; - const Topaz: 101; - const Skull: 102; - } + items: { + cost: { + ToBuy: 0; + ToSell: 1; + ToRepair: 2; + }; + flags: { + Equipped: 0x00000001; + InSocket: 0x00000008; + Identified: 0x00000010; + OnActiveWeaponSlot: 0x00000040; + OnSwapWeaponSlot: 0x00000080; + Broken: 0x00000100; + FullRejuv: 0x00000400; + Socketed: 0x00000800; + InTradeGamble: 0x00002000; + NotInSocket: 0x00004000; + Ear: 0x00010000; + StartingItem: 0x00020000; + RuneQuestPotion: 0x00200000; + Ethereal: 0x00400000; + IsAnItem: 0x00800000; + Personalized: 0x01000000; + Runeword: 0x04000000; + }; + mode: { + inStorage: 0; //Item inven stash cube store = Item inven stash cube store + Equipped: 1; // Item equipped self or merc + inBelt: 2; // Item in belt + onGround: 3; // Item on ground + onCursor: 4; // Item on cursor + Dropping: 5; // Item being dropped + Socketed: 6; // Item socketed in item + }; + quality: { + LowQuality: 1; + Normal: 2; + Superior: 3; + Magic: 4; + Set: 5; + Rare: 6; + Unique: 7; + Crafted: 8; + }; + // @ts-ignore + class: { + Normal: 0; + Exceptional: 1; + Elite: 2; + }; + type: { + Shield: 2; + Armor: 3; + Gold: 4; + BowQuiver: 5; + CrossbowQuiver: 6; + PlayerBodyPart: 7; + Herb: 8; + Potion: 9; + Ring: 10; + Elixir: 11; + Amulet: 12; + Charm: 13; + notused0: 14; + Boots: 15; + Gloves: 16; + notused1: 17; + Book: 18; + Belt: 19; + Gem: 20; + Torch: 21; + Scroll: 22; + notused2: 23; + Scepter: 24; + Wand: 25; + Staff: 26; + Bow: 27; + Axe: 28; + Club: 29; + Sword: 30; + Hammer: 31; + Knife: 32; + Spear: 33; + Polearm: 34; + Crossbow: 35; + Mace: 36; + Helm: 37; + MissilePotion: 38; + Quest: 39; + Bodypart: 40; + Key: 41; + ThrowingKnife: 42; + ThrowingAxe: 43; + Javelin: 44; + Weapon: 45; + MeleeWeapon: 46; + MissileWeapon: 47; + ThrownWeapon: 48; + ComboWeapon: 49; + AnyArmor: 50; + AnyShield: 51; + Miscellaneous: 52; + SocketFiller: 53; + Secondhand: 54; + StavesandRods: 55; + Missile: 56; + Blunt: 57; + Jewel: 58; + ClassSpecific: 59; + AmazonItem: 60; + BarbarianItem: 61; + NecromancerItem: 62; + PaladinItem: 63; + SorceressItem: 64; + AssassinItem: 65; + DruidItem: 66; + HandtoHand: 67; + Orb: 68; + VoodooHeads: 69; + AuricShields: 70; + PrimalHelm: 71; + Pelt: 72; + Cloak: 73; + Rune: 74; + Circlet: 75; + HealingPotion: 76; + ManaPotion: 77; + RejuvPotion: 78; + StaminaPotion: 79; + AntidotePotion: 80; + ThawingPotion: 81; + SmallCharm: 82; + LargeCharm: 83; + GrandCharm: 84; + AmazonBow: 85; + AmazonSpear: 86; + AmazonJavelin: 87; + AssassinClaw: 88; + MagicBowQuiv: 89; + MagicxBowQuiv: 90; + ChippedGem: 91; + FlawedGem: 92; + StandardGem: 93; + FlawlessGem: 94; + PerfectgGem: 95; + Amethyst: 96; + Diamond: 97; + Emerald: 98; + Ruby: 99; + Sapphire: 100; + Topaz: 101; + Skull: 102; + }; // Weapons - export const HandAxe: 0; - export const Axe: 1; - export const DoubleAxe: 2; - export const MilitaryPick: 3; - export const WarAxe: 4; - export const LargeAxe: 5; - export const BroadAxe: 6; - export const BattleAxe: 7; - export const GreatAxe: 8; - export const GiantAxe: 9; - export const Wand: 10; - export const YewWand: 11; - export const BoneWand: 12; - export const GrimWand: 13; - export const Club: 14; - export const Scepter: 15; - export const GrandScepter: 16; - export const WarScepter: 17; - export const SpikedClub: 18; - export const Mace: 19; - export const MorningStar: 20; - export const Flail: 21; - export const WarHammer: 22; - export const Maul: 23; - export const GreatMaul: 24; - export const ShortSword: 25; - export const Scimitar: 26; - export const Sabre: 27; - export const Falchion: 28; - export const CrystalSword: 29; - export const BroadSword: 30; - export const LongSword: 31; - export const WarSword: 32; - export const Two_HandedSword: 33; - export const Claymore: 34; - export const GiantSword: 35; - export const BastardSword: 36; - export const Flamberge: 37; - export const GreatSword: 38; - export const Dagger: 39; - export const Dirk: 40; - export const Kris: 41; - export const Blade: 42; - export const ThrowingKnife: 43; - export const ThrowingAxe: 44; - export const BalancedKnife: 45; - export const BalancedAxe: 46; - export const Javelin: 47; - export const Pilum: 48; - export const ShortSpear: 49; - export const Glaive: 50; - export const ThrowingSpear: 51; - export const Spear: 52; - export const Trident: 53; - export const Brandistock: 54; - export const Spetum: 55; - export const Pike: 56; - export const Bardiche: 57; - export const Voulge: 58; - export const Scythe: 59; - export const Poleaxe: 60; - export const Halberd: 61; - export const WarScythe: 62; - export const ShortStaff: 63; - export const LongStaff: 64; - export const GnarledStaff: 65; - export const BattleStaff: 66; - export const WarStaff: 67; - export const ShortBow: 68; - export const HuntersBow: 69; - export const LongBow: 70; - export const CompositeBow: 71; - export const ShortBattleBow: 72; - export const LongBattleBow: 73; - export const ShortWarBow: 74; - export const LongWarBow: 75; - export const LightCrossbow: 76; - export const Crossbow: 77; - export const HeavyCrossbow: 78; - export const RepeatingCrossbow: 79; - export const Hatchet: 93; - export const Cleaver: 94; - export const TwinAxe: 95; - export const Crowbill: 96; - export const Naga: 97; - export const MilitaryAxe: 98; - export const BeardedAxe: 99; - export const Tabar: 100; - export const GothicAxe: 101; - export const AncientAxe: 102; - export const BurntWand: 103; - export const PetrifiedWand: 104; - export const TombWand: 105; - export const GraveWand: 106; - export const Cudgel: 107; - export const RuneScepter: 108; - export const HolyWaterSprinkler: 109; - export const DivineScepter: 110; - export const BarbedClub: 111; - export const FlangedMace: 112; - export const JaggedStar: 113; - export const Knout: 114; - export const BattleHammer: 115; - export const WarClub: 116; - export const MarteldeFer: 117; - export const Gladius: 118; - export const Cutlass: 119; - export const Shamshir: 120; - export const Tulwar: 121; - export const DimensionalBlade: 122; - export const BattleSword: 123; - export const RuneSword: 124; - export const AncientSword: 125; - export const Espandon: 126; - export const DacianFalx: 127; - export const TuskSword: 128; - export const GothicSword: 129; - export const Zweihander: 130; - export const ExecutionerSword: 131; - export const Poignard: 132; - export const Rondel: 133; - export const Cinquedeas: 134; - export const Stiletto: 135; - export const BattleDart: 136; - export const Francisca: 137; - export const WarDart: 138; - export const Hurlbat: 139; - export const WarJavelin: 140; - export const GreatPilum: 141; - export const Simbilan: 142; - export const Spiculum: 143; - export const Harpoon: 144; - export const WarSpear: 145; - export const Fuscina: 146; - export const WarFork: 147; - export const Yari: 148; - export const Lance: 149; - export const LochaberAxe: 150; - export const Bill: 151; - export const BattleScythe: 152; - export const Partizan: 153; - export const Bec_de_Corbin: 154; - export const GrimScythe: 155; - export const JoStaff: 156; - export const Quarterstaff: 157; - export const CedarStaff: 158; - export const GothicStaff: 159; - export const RuneStaff: 160; - export const EdgeBow: 161; - export const RazorBow: 162; - export const CedarBow: 163; - export const DoubleBow: 164; - export const ShortSiegeBow: 165; - export const LargeSiegeBow: 166; - export const RuneBow: 167; - export const GothicBow: 168; - export const Arbalest: 169; - export const SiegeCrossbow: 170; - export const Ballista: 171; - export const Chu_Ko_Nu: 172; - export const Katar: 175; - export const WristBlade: 176; - export const HatchetHands: 177; - export const Cestus: 178; - export const Claws: 179; - export const BladeTalons: 180; - export const ScissorsKatar: 181; - export const Quhab: 182; - export const WristSpike: 183; - export const Fascia: 184; - export const HandScythe: 185; - export const GreaterClaws: 186; - export const GreaterTalons: 187; - export const ScissorsQuhab: 188; - export const Suwayyah: 189; - export const WristSword: 190; - export const WarFist: 191; - export const BattleCestus: 192; - export const FeralClaws: 193; - export const RunicTalons: 194; - export const ScissorsSuwayyah: 195; - export const Tomahawk: 196; - export const SmallCrescent: 197; - export const EttinAxe: 198; - export const WarSpike: 199; - export const BerserkerAxe: 200; - export const FeralAxe: 201; - export const Silver_edgedAxe: 202; - export const Decapitator: 203; - export const ChampionAxe: 204; - export const GloriousAxe: 205; - export const PolishedWand: 206; - export const GhostWand: 207; - export const LichWand: 208; - export const UnearthedWand: 209; - export const Truncheon: 210; - export const MightyScepter: 211; - export const SeraphRod: 212; - export const Caduceus: 213; - export const TyrantClub: 214; - export const ReinforcedMace: 215; - export const DevilStar: 216; - export const Scourge: 217; - export const LegendaryMallet: 218; - export const OgreMaul: 219; - export const ThunderMaul: 220; - export const Falcata: 221; - export const Ataghan: 222; - export const ElegantBlade: 223; - export const HydraEdge: 224; - export const PhaseBlade: 225; - export const ConquestSword: 226; - export const CrypticSword: 227; - export const MythicalSword: 228; - export const LegendSword: 229; - export const HighlandBlade: 230; - export const BalrogBlade: 231; - export const ChampionSword: 232; - export const ColossusSword: 233; - export const ColossusBlade: 234; - export const BoneKnife: 235; - export const MithrilPoint: 236; - export const FangedKnife: 237; - export const LegendSpike: 238; - export const FlyingKnife: 239; - export const FlyingAxe: 240; - export const WingedKnife: 241; - export const WingedAxe: 242; - export const HyperionJavelin: 243; - export const StygianPilum: 244; - export const BalrogSpear: 245; - export const GhostGlaive: 246; - export const WingedHarpoon: 247; - export const HyperionSpear: 248; - export const StygianPike: 249; - export const Mancatcher: 250; - export const GhostSpear: 251; - export const WarPike: 252; - export const OgreAxe: 253; - export const ColossusVoulge: 254; - export const Thresher: 255; - export const CrypticAxe: 256; - export const GreatPoleaxe: 257; - export const GiantThresher: 258; - export const WalkingStick: 259; - export const Stalagmite: 260; - export const ElderStaff: 261; - export const Shillelagh: 262; - export const ArchonStaff: 263; - export const SpiderBow: 264; - export const BladeBow: 265; - export const ShadowBow: 266; - export const GreatBow: 267; - export const DiamondBow: 268; - export const CrusaderBow: 269; - export const WardBow: 270; - export const HydraBow: 271; - export const PelletBow: 272; - export const GorgonCrossbow: 273; - export const ColossusCrossbow: 274; - export const DemonCrossbow: 275; - export const EagleOrb: 276; - export const SacredGlobe: 277; - export const SmokedSphere: 278; - export const ClaspedOrb: 279; - export const JaredsStone: 280; - export const StagBow: 281; - export const ReflexBow: 282; - export const MaidenSpear: 283; - export const MaidenPike: 284; - export const MaidenJavelin: 285; - export const GlowingOrb: 286; - export const CrystallineGlobe: 287; - export const CloudySphere: 288; - export const SparklingBall: 289; - export const SwirlingCrystal: 290; - export const AshwoodBow: 291; - export const CeremonialBow: 292; - export const CeremonialSpear: 293; - export const CeremonialPike: 294; - export const CeremonialJavelin: 295; - export const HeavenlyStone: 296; - export const EldritchOrb: 297; - export const DemonHeart: 298; - export const VortexOrb: 299; - export const DimensionalShard: 300; - export const MatriarchalBow: 301; - export const GrandMatronBow: 302; - export const MatriarchalSpear: 303; - export const MatriarchalPike: 304; - export const MatriarchalJavelin: 305; - export const Cap: 306; - export const SkullCap: 307; - export const Helm: 308; - export const FullHelm: 309; - export const GreatHelm: 310; - export const Crown: 311; - export const Mask: 312; - export const QuiltedArmor: 313; - export const LeatherArmor: 314; - export const HardLeatherArmor: 315; - export const StuddedLeather: 316; - export const RingMail: 317; - export const ScaleMail: 318; - export const ChainMail: 319; - export const BreastPlate: 320; - export const SplintMail: 321; - export const PlateMail: 322; - export const FieldPlate: 323; - export const GothicPlate: 324; - export const FullPlateMail: 325; - export const AncientArmor: 326; - export const LightPlate: 327; - export const Buckler: 328; - export const SmallShield: 329; - export const LargeShield: 330; - export const KiteShield: 331; - export const TowerShield: 332; - export const GothicShield: 333; - export const LeatherGloves: 334; - export const HeavyGloves: 335; - export const ChainGloves: 336; - export const LightGauntlets: 337; - export const Gauntlets: 338; - export const Boots: 339; - export const HeavyBoots: 340; - export const ChainBoots: 341; - export const LightPlatedBoots: 342; - export const Greaves: 343; - export const Sash: 344; - export const LightBelt: 345; - export const Belt: 346; - export const HeavyBelt: 347; - export const PlatedBelt: 348; - export const BoneHelm: 349; - export const BoneShield: 350; - export const SpikedShield: 351; - export const WarHat: 352; - export const Sallet: 353; - export const Casque: 354; - export const Basinet: 355; - export const WingedHelm: 356; - export const GrandCrown: 357; - export const DeathMask: 358; - export const GhostArmor: 359; - export const SerpentskinArmor: 360; - export const DemonhideArmor: 361; - export const TrellisedArmor: 362; - export const LinkedMail: 363; - export const TigulatedMail: 364; - export const MeshArmor: 365; - export const Cuirass: 366; - export const RussetArmor: 367; - export const TemplarCoat: 368; - export const SharktoothArmor: 369; - export const EmbossedPlate: 370; - export const ChaosArmor: 371; - export const OrnatePlate: 372; - export const MagePlate: 373; - export const Defender: 374; - export const RoundShield: 375; - export const Scutum: 376; - export const DragonShield: 377; - export const Pavise: 378; - export const AncientShield: 379; - export const DemonhideGloves: 380; - export const SharkskinGloves: 381; - export const HeavyBracers: 382; - export const BattleGauntlets: 383; - export const WarGauntlets: 384; - export const DemonhideBoots: 385; - export const SharkskinBoots: 386; - export const MeshBoots: 387; - export const BattleBoots: 388; - export const WarBoots: 389; - export const DemonhideSash: 390; - export const SharkskinBelt: 391; - export const MeshBelt: 392; - export const BattleBelt: 393; - export const WarBelt: 394; - export const GrimHelm: 395; - export const GrimShield: 396; - export const BarbedShield: 397; - export const WolfHead: 398; - export const HawkHelm: 399; - export const Antlers: 400; - export const FalconMask: 401; - export const SpiritMask: 402; - export const JawboneCap: 403; - export const FangedHelm: 404; - export const HornedHelm: 405; - export const AssaultHelmet: 406; - export const AvengerGuard: 407; - export const Targe: 408; - export const Rondache: 409; - export const HeraldicShield: 410; - export const AerinShield: 411; - export const CrownShield: 412; - export const PreservedHead: 413; - export const ZombieHead: 414; - export const UnravellerHead: 415; - export const GargoyleHead: 416; - export const DemonHead: 417; - export const Circlet: 418; - export const Coronet: 419; - export const Tiara: 420; - export const Diadem: 421; - export const Shako: 422; - export const Hydraskull: 423; - export const Armet: 424; - export const GiantConch: 425; - export const SpiredHelm: 426; - export const Corona: 427; - export const Demonhead: 428; - export const DuskShroud: 429; - export const Wyrmhide: 430; - export const ScarabHusk: 431; - export const WireFleece: 432; - export const DiamondMail: 433; - export const LoricatedMail: 434; - export const Boneweave: 435; - export const GreatHauberk: 436; - export const BalrogSkin: 437; - export const HellforgePlate: 438; - export const KrakenShell: 439; - export const LacqueredPlate: 440; - export const ShadowPlate: 441; - export const SacredArmor: 442; - export const ArchonPlate: 443; - export const Heater: 444; - export const Luna: 445; - export const Hyperion: 446; - export const Monarch: 447; - export const Aegis: 448; - export const Ward: 449; - export const BrambleMitts: 450; - export const VampireboneGloves: 451; - export const Vambraces: 452; - export const CrusaderGauntlets: 453; - export const OgreGauntlets: 454; - export const WyrmhideBoots: 455; - export const ScarabshellBoots: 456; - export const BoneweaveBoots: 457; - export const MirroredBoots: 458; - export const MyrmidonGreaves: 459; - export const SpiderwebSash: 460; - export const VampirefangBelt: 461; - export const MithrilCoil: 462; - export const TrollBelt: 463; - export const ColossusGirdle: 464; - export const BoneVisage: 465; - export const TrollNest: 466; - export const BladeBarrier: 467; - export const AlphaHelm: 468; - export const GriffonHeaddress: 469; - export const HuntersGuise: 470; - export const SacredFeathers: 471; - export const TotemicMask: 472; - export const JawboneVisor: 473; - export const LionHelm: 474; - export const RageMask: 475; - export const SavageHelmet: 476; - export const SlayerGuard: 477; - export const AkaranTarge: 478; - export const AkaranRondache: 479; - export const ProtectorShield: 480; - export const GildedShield: 481; - export const RoyalShield: 482; - export const MummifiedTrophy: 483; - export const FetishTrophy: 484; - export const SextonTrophy: 485; - export const CantorTrophy: 486; - export const HierophantTrophy: 487; - export const BloodSpirit: 488; - export const SunSpirit: 489; - export const EarthSpirit: 490; - export const SkySpirit: 491; - export const DreamSpirit: 492; - export const CarnageHelm: 493; - export const FuryVisor: 494; - export const DestroyerHelm: 495; - export const ConquerorCrown: 496; - export const GuardianCrown: 497; - export const SacredTarge: 498; - export const SacredRondache: 499; - export const KurastShield: 500; - export const ZakarumShield: 501; - export const VortexShield: 502; - export const MinionSkull: 503; - export const HellspawnSkull: 504; - export const OverseerSkull: 505; - export const SuccubusSkull: 506; - export const BloodlordSkull: 507; - export const Amulet: 520; - export const Ring: 522; - export const Arrows: 526; - export const Bolts: 528; - export const Jewel: 643; + HandAxe: 0; + Axe: 1; + DoubleAxe: 2; + MilitaryPick: 3; + WarAxe: 4; + LargeAxe: 5; + BroadAxe: 6; + BattleAxe: 7; + GreatAxe: 8; + GiantAxe: 9; + Wand: 10; + YewWand: 11; + BoneWand: 12; + GrimWand: 13; + Club: 14; + Scepter: 15; + GrandScepter: 16; + WarScepter: 17; + SpikedClub: 18; + Mace: 19; + MorningStar: 20; + Flail: 21; + WarHammer: 22; + Maul: 23; + GreatMaul: 24; + ShortSword: 25; + Scimitar: 26; + Sabre: 27; + Falchion: 28; + CrystalSword: 29; + BroadSword: 30; + LongSword: 31; + WarSword: 32; + Two_HandedSword: 33; + Claymore: 34; + GiantSword: 35; + BastardSword: 36; + Flamberge: 37; + GreatSword: 38; + Dagger: 39; + Dirk: 40; + Kris: 41; + Blade: 42; + ThrowingKnife: 43; + ThrowingAxe: 44; + BalancedKnife: 45; + BalancedAxe: 46; + Javelin: 47; + Pilum: 48; + ShortSpear: 49; + Glaive: 50; + ThrowingSpear: 51; + Spear: 52; + Trident: 53; + Brandistock: 54; + Spetum: 55; + Pike: 56; + Bardiche: 57; + Voulge: 58; + Scythe: 59; + Poleaxe: 60; + Halberd: 61; + WarScythe: 62; + ShortStaff: 63; + LongStaff: 64; + GnarledStaff: 65; + BattleStaff: 66; + WarStaff: 67; + ShortBow: 68; + HuntersBow: 69; + LongBow: 70; + CompositeBow: 71; + ShortBattleBow: 72; + LongBattleBow: 73; + ShortWarBow: 74; + LongWarBow: 75; + LightCrossbow: 76; + Crossbow: 77; + HeavyCrossbow: 78; + RepeatingCrossbow: 79; + Hatchet: 93; + Cleaver: 94; + TwinAxe: 95; + Crowbill: 96; + Naga: 97; + MilitaryAxe: 98; + BeardedAxe: 99; + Tabar: 100; + GothicAxe: 101; + AncientAxe: 102; + BurntWand: 103; + PetrifiedWand: 104; + TombWand: 105; + GraveWand: 106; + Cudgel: 107; + RuneScepter: 108; + HolyWaterSprinkler: 109; + DivineScepter: 110; + BarbedClub: 111; + FlangedMace: 112; + JaggedStar: 113; + Knout: 114; + BattleHammer: 115; + WarClub: 116; + MarteldeFer: 117; + Gladius: 118; + Cutlass: 119; + Shamshir: 120; + Tulwar: 121; + DimensionalBlade: 122; + BattleSword: 123; + RuneSword: 124; + AncientSword: 125; + Espandon: 126; + DacianFalx: 127; + TuskSword: 128; + GothicSword: 129; + Zweihander: 130; + ExecutionerSword: 131; + Poignard: 132; + Rondel: 133; + Cinquedeas: 134; + Stiletto: 135; + BattleDart: 136; + Francisca: 137; + WarDart: 138; + Hurlbat: 139; + WarJavelin: 140; + GreatPilum: 141; + Simbilan: 142; + Spiculum: 143; + Harpoon: 144; + WarSpear: 145; + Fuscina: 146; + WarFork: 147; + Yari: 148; + Lance: 149; + LochaberAxe: 150; + Bill: 151; + BattleScythe: 152; + Partizan: 153; + Bec_de_Corbin: 154; + GrimScythe: 155; + JoStaff: 156; + Quarterstaff: 157; + CedarStaff: 158; + GothicStaff: 159; + RuneStaff: 160; + EdgeBow: 161; + RazorBow: 162; + CedarBow: 163; + DoubleBow: 164; + ShortSiegeBow: 165; + LargeSiegeBow: 166; + RuneBow: 167; + GothicBow: 168; + Arbalest: 169; + SiegeCrossbow: 170; + Ballista: 171; + Chu_Ko_Nu: 172; + Katar: 175; + WristBlade: 176; + HatchetHands: 177; + Cestus: 178; + Claws: 179; + BladeTalons: 180; + ScissorsKatar: 181; + Quhab: 182; + WristSpike: 183; + Fascia: 184; + HandScythe: 185; + GreaterClaws: 186; + GreaterTalons: 187; + ScissorsQuhab: 188; + Suwayyah: 189; + WristSword: 190; + WarFist: 191; + BattleCestus: 192; + FeralClaws: 193; + RunicTalons: 194; + ScissorsSuwayyah: 195; + Tomahawk: 196; + SmallCrescent: 197; + EttinAxe: 198; + WarSpike: 199; + BerserkerAxe: 200; + FeralAxe: 201; + Silver_edgedAxe: 202; + Decapitator: 203; + ChampionAxe: 204; + GloriousAxe: 205; + PolishedWand: 206; + GhostWand: 207; + LichWand: 208; + UnearthedWand: 209; + Truncheon: 210; + MightyScepter: 211; + SeraphRod: 212; + Caduceus: 213; + TyrantClub: 214; + ReinforcedMace: 215; + DevilStar: 216; + Scourge: 217; + LegendaryMallet: 218; + OgreMaul: 219; + ThunderMaul: 220; + Falcata: 221; + Ataghan: 222; + ElegantBlade: 223; + HydraEdge: 224; + PhaseBlade: 225; + ConquestSword: 226; + CrypticSword: 227; + MythicalSword: 228; + LegendSword: 229; + HighlandBlade: 230; + BalrogBlade: 231; + ChampionSword: 232; + ColossusSword: 233; + ColossusBlade: 234; + BoneKnife: 235; + MithrilPoint: 236; + FangedKnife: 237; + LegendSpike: 238; + FlyingKnife: 239; + FlyingAxe: 240; + WingedKnife: 241; + WingedAxe: 242; + HyperionJavelin: 243; + StygianPilum: 244; + BalrogSpear: 245; + GhostGlaive: 246; + WingedHarpoon: 247; + HyperionSpear: 248; + StygianPike: 249; + Mancatcher: 250; + GhostSpear: 251; + WarPike: 252; + OgreAxe: 253; + ColossusVoulge: 254; + Thresher: 255; + CrypticAxe: 256; + GreatPoleaxe: 257; + GiantThresher: 258; + WalkingStick: 259; + Stalagmite: 260; + ElderStaff: 261; + Shillelagh: 262; + ArchonStaff: 263; + SpiderBow: 264; + BladeBow: 265; + ShadowBow: 266; + GreatBow: 267; + DiamondBow: 268; + CrusaderBow: 269; + WardBow: 270; + HydraBow: 271; + PelletBow: 272; + GorgonCrossbow: 273; + ColossusCrossbow: 274; + DemonCrossbow: 275; + EagleOrb: 276; + SacredGlobe: 277; + SmokedSphere: 278; + ClaspedOrb: 279; + JaredsStone: 280; + StagBow: 281; + ReflexBow: 282; + MaidenSpear: 283; + MaidenPike: 284; + MaidenJavelin: 285; + GlowingOrb: 286; + CrystallineGlobe: 287; + CloudySphere: 288; + SparklingBall: 289; + SwirlingCrystal: 290; + AshwoodBow: 291; + CeremonialBow: 292; + CeremonialSpear: 293; + CeremonialPike: 294; + CeremonialJavelin: 295; + HeavenlyStone: 296; + EldritchOrb: 297; + DemonHeart: 298; + VortexOrb: 299; + DimensionalShard: 300; + MatriarchalBow: 301; + GrandMatronBow: 302; + MatriarchalSpear: 303; + MatriarchalPike: 304; + MatriarchalJavelin: 305; + Cap: 306; + SkullCap: 307; + Helm: 308; + FullHelm: 309; + GreatHelm: 310; + Crown: 311; + Mask: 312; + QuiltedArmor: 313; + LeatherArmor: 314; + HardLeatherArmor: 315; + StuddedLeather: 316; + RingMail: 317; + ScaleMail: 318; + ChainMail: 319; + BreastPlate: 320; + SplintMail: 321; + PlateMail: 322; + FieldPlate: 323; + GothicPlate: 324; + FullPlateMail: 325; + AncientArmor: 326; + LightPlate: 327; + Buckler: 328; + SmallShield: 329; + LargeShield: 330; + KiteShield: 331; + TowerShield: 332; + GothicShield: 333; + LeatherGloves: 334; + HeavyGloves: 335; + ChainGloves: 336; + LightGauntlets: 337; + Gauntlets: 338; + Boots: 339; + HeavyBoots: 340; + ChainBoots: 341; + LightPlatedBoots: 342; + Greaves: 343; + Sash: 344; + LightBelt: 345; + Belt: 346; + HeavyBelt: 347; + PlatedBelt: 348; + BoneHelm: 349; + BoneShield: 350; + SpikedShield: 351; + WarHat: 352; + Sallet: 353; + Casque: 354; + Basinet: 355; + WingedHelm: 356; + GrandCrown: 357; + DeathMask: 358; + GhostArmor: 359; + SerpentskinArmor: 360; + DemonhideArmor: 361; + TrellisedArmor: 362; + LinkedMail: 363; + TigulatedMail: 364; + MeshArmor: 365; + Cuirass: 366; + RussetArmor: 367; + TemplarCoat: 368; + SharktoothArmor: 369; + EmbossedPlate: 370; + ChaosArmor: 371; + OrnatePlate: 372; + MagePlate: 373; + Defender: 374; + RoundShield: 375; + Scutum: 376; + DragonShield: 377; + Pavise: 378; + AncientShield: 379; + DemonhideGloves: 380; + SharkskinGloves: 381; + HeavyBracers: 382; + BattleGauntlets: 383; + WarGauntlets: 384; + DemonhideBoots: 385; + SharkskinBoots: 386; + MeshBoots: 387; + BattleBoots: 388; + WarBoots: 389; + DemonhideSash: 390; + SharkskinBelt: 391; + MeshBelt: 392; + BattleBelt: 393; + WarBelt: 394; + GrimHelm: 395; + GrimShield: 396; + BarbedShield: 397; + WolfHead: 398; + HawkHelm: 399; + Antlers: 400; + FalconMask: 401; + SpiritMask: 402; + JawboneCap: 403; + FangedHelm: 404; + HornedHelm: 405; + AssaultHelmet: 406; + AvengerGuard: 407; + Targe: 408; + Rondache: 409; + HeraldicShield: 410; + AerinShield: 411; + CrownShield: 412; + PreservedHead: 413; + ZombieHead: 414; + UnravellerHead: 415; + GargoyleHead: 416; + DemonHead: 417; + Circlet: 418; + Coronet: 419; + Tiara: 420; + Diadem: 421; + Shako: 422; + Hydraskull: 423; + Armet: 424; + GiantConch: 425; + SpiredHelm: 426; + Corona: 427; + Demonhead: 428; + DuskShroud: 429; + Wyrmhide: 430; + ScarabHusk: 431; + WireFleece: 432; + DiamondMail: 433; + LoricatedMail: 434; + Boneweave: 435; + GreatHauberk: 436; + BalrogSkin: 437; + HellforgePlate: 438; + KrakenShell: 439; + LacqueredPlate: 440; + ShadowPlate: 441; + SacredArmor: 442; + ArchonPlate: 443; + Heater: 444; + Luna: 445; + Hyperion: 446; + Monarch: 447; + Aegis: 448; + Ward: 449; + BrambleMitts: 450; + VampireboneGloves: 451; + Vambraces: 452; + CrusaderGauntlets: 453; + OgreGauntlets: 454; + WyrmhideBoots: 455; + ScarabshellBoots: 456; + BoneweaveBoots: 457; + MirroredBoots: 458; + MyrmidonGreaves: 459; + SpiderwebSash: 460; + VampirefangBelt: 461; + MithrilCoil: 462; + TrollBelt: 463; + ColossusGirdle: 464; + BoneVisage: 465; + TrollNest: 466; + BladeBarrier: 467; + AlphaHelm: 468; + GriffonHeaddress: 469; + HuntersGuise: 470; + SacredFeathers: 471; + TotemicMask: 472; + JawboneVisor: 473; + LionHelm: 474; + RageMask: 475; + SavageHelmet: 476; + SlayerGuard: 477; + AkaranTarge: 478; + AkaranRondache: 479; + ProtectorShield: 480; + GildedShield: 481; + RoyalShield: 482; + MummifiedTrophy: 483; + FetishTrophy: 484; + SextonTrophy: 485; + CantorTrophy: 486; + HierophantTrophy: 487; + BloodSpirit: 488; + SunSpirit: 489; + EarthSpirit: 490; + SkySpirit: 491; + DreamSpirit: 492; + CarnageHelm: 493; + FuryVisor: 494; + DestroyerHelm: 495; + ConquerorCrown: 496; + GuardianCrown: 497; + SacredTarge: 498; + SacredRondache: 499; + KurastShield: 500; + ZakarumShield: 501; + VortexShield: 502; + MinionSkull: 503; + HellspawnSkull: 504; + OverseerSkull: 505; + SuccubusSkull: 506; + BloodlordSkull: 507; + Amulet: 520; + Ring: 522; + Arrows: 526; + Bolts: 528; + Jewel: 643; // Misc? - const Elixir: 508; - const Torch: 527; - const Heart: 531; - const Brain: 532; - const Jawbone: 533; - const Eye: 534; - const Horn: 535; - const Tail: 536; - const Flag: 537; - const Fang: 538; - const Quill: 539; - const Soul: 540; - const Scalp: 541; - const Spleen: 542; - const Ear: 556; - const Herb: 602; - const anevilforce: 609; + Elixir: 508; + Torch: 527; + Heart: 531; + Brain: 532; + Jawbone: 533; + Eye: 534; + Horn: 535; + Tail: 536; + Flag: 537; + Fang: 538; + Quill: 539; + Soul: 540; + Scalp: 541; + Spleen: 542; + Ear: 556; + Herb: 602; + anevilforce: 609; // Potions, tomes/scrolls, gold - export const Key: 543; - export const TomeofTownPortal: 518; - export const TomeofIdentify: 519; - export const ScrollofTownPortal: 529; - export const ScrollofIdentify: 530; - export const RancidGasPotion: 80; - export const OilPotion: 81; - export const ChokingGasPotion: 82; - export const ExplodingPotion: 83; - export const StranglingGasPotion: 84; - export const FulminatingPotion: 85; - export const StaminaPotion: 513; - export const AntidotePotion: 514; - export const RejuvenationPotion: 515; - export const FullRejuvenationPotion: 516; - export const ThawingPotion: 517; - export const MinorHealingPotion: 587; - export const LightHealingPotion: 588; - export const HealingPotion: 589; - export const GreaterHealingPotion: 590; - export const SuperHealingPotion: 591; - export const MinorManaPotion: 592; - export const LightManaPotion: 593; - export const ManaPotion: 594; - export const GreaterManaPotion: 595; - export const SuperManaPotion: 596; - export const Gold: 523; + Key: 543; + TomeofTownPortal: 518; + TomeofIdentify: 519; + ScrollofTownPortal: 529; + ScrollofIdentify: 530; + RancidGasPotion: 80; + OilPotion: 81; + ChokingGasPotion: 82; + ExplodingPotion: 83; + StranglingGasPotion: 84; + FulminatingPotion: 85; + StaminaPotion: 513; + AntidotePotion: 514; + RejuvenationPotion: 515; + FullRejuvenationPotion: 516; + ThawingPotion: 517; + MinorHealingPotion: 587; + LightHealingPotion: 588; + HealingPotion: 589; + GreaterHealingPotion: 590; + SuperHealingPotion: 591; + MinorManaPotion: 592; + LightManaPotion: 593; + ManaPotion: 594; + GreaterManaPotion: 595; + SuperManaPotion: 596; + Gold: 523; // Charms - export const SmallCharm: 603; - export const LargeCharm: 604; - export const GrandCharm: 605; + SmallCharm: 603; + LargeCharm: 604; + GrandCharm: 605; - export namespace quest { + quest: { // Act 1 - const WirtsLeg: 88; - const HoradricMalus: 89; - const ScrollofInifuss: 524; - const KeytotheCairnStones: 525; + WirtsLeg: 88; + HoradricMalus: 89; + ScrollofInifuss: 524; + KeytotheCairnStones: 525; // Act 2 - const FinishedStaff: 91; - const HoradricStaff: 91; - const IncompleteStaff: 92; - const ShaftoftheHoradricStaff: 92; - const ViperAmulet: 521; - const TopoftheHoradricStaff: 521; - const Cube: 549; - const BookofSkill: 552; + FinishedStaff: 91; + HoradricStaff: 91; + IncompleteStaff: 92; + ShaftoftheHoradricStaff: 92; + ViperAmulet: 521; + TopoftheHoradricStaff: 521; + Cube: 549; + BookofSkill: 552; // Act 3 - const DecoyGidbinn: 86; - const TheGidbinn: 87; - const KhalimsFlail: 173; - const KhalimsWill: 174; - const PotofLife: 545; - const AJadeFigurine: 546; - const JadeFigurine: 546; - const TheGoldenBird: 547; - const LamEsensTome: 548; - const KhalimsEye: 553; - const KhalimsHeart: 554; - const KhalimsBrain: 555; + DecoyGidbinn: 86; + TheGidbinn: 87; + KhalimsFlail: 173; + KhalimsWill: 174; + PotofLife: 545; + AJadeFigurine: 546; + JadeFigurine: 546; + TheGoldenBird: 547; + LamEsensTome: 548; + KhalimsEye: 553; + KhalimsHeart: 554; + KhalimsBrain: 555; // Act 4 - const HellForgeHammer: 90; - const Soulstone: 551; - const MephistosSoulstone: 551; + HellForgeHammer: 90; + Soulstone: 551; + MephistosSoulstone: 551; // Act 5 - const MalahsPotion: 644; - const ScrollofKnowledge: 645; - const ScrollofResistance: 646; + MalahsPotion: 644; + ScrollofKnowledge: 645; + ScrollofResistance: 646; // Pandemonium Event - const KeyofTerror: 647; - const KeyofHate: 648; - const KeyofDestruction: 649; - const DiablosHorn: 650; - const BaalsEye: 651; - const MephistosBrain: 652; - const StandardofHeroes: 658; + KeyofTerror: 647; + KeyofHate: 648; + KeyofDestruction: 649; + DiablosHorn: 650; + BaalsEye: 651; + MephistosBrain: 652; + StandardofHeroes: 658; // Essences/Token - const TokenofAbsolution: 653; - const TwistedEssenceofSuffering: 654; - const ChargedEssenceofHatred: 655; - const BurningEssenceofTerror: 656; - const FesteringEssenceofDestruction: 657; + TokenofAbsolution: 653; + TwistedEssenceofSuffering: 654; + ChargedEssenceofHatred: 655; + BurningEssenceofTerror: 656; + FesteringEssenceofDestruction: 657; // Misc - const TheBlackTowerKey: 544; - } - export namespace runes { - const El: 610; - const Eld: 611; - const Tir: 612; - const Nef: 613; - const Eth: 614; - const Ith: 615; - const Tal: 616; - const Ral: 617; - const Ort: 618; - const Thul: 619; - const Amn: 620; - const Sol: 621; - const Shael: 622; - const Dol: 623; - const Hel: 624; - const Io: 625; - const Lum: 626; - const Ko: 627; - const Fal: 628; - const Lem: 629; - const Pul: 630; - const Um: 631; - const Mal: 632; - const Ist: 633; - const Gul: 634; - const Vex: 635; - const Ohm: 636; - const Lo: 637; - const Sur: 638; - const Ber: 639; - const Jah: 640; - const Cham: 641; - const Zod: 642; - } - export namespace gems { - export namespace Perfect { - const Amethyst: 561; - const Topaz: 566; - const Sapphire: 571; - const Emerald: 576; - const Ruby: 581; - const Diamond: 586; - const Skull: 601; - } - export namespace Flawless { - const Amethyst: 560; - const Topaz: 565; - const Sapphire: 570; - const Emerald: 575; - const Ruby: 580; - const Diamond: 585; - const Skull: 600; - } - export namespace Normal { - const Amethyst: 559; - const Topaz: 564; - const Sapphire: 569; - const Emerald: 574; - const Ruby: 579; - const Diamond: 584; - const Skull: 599; - } - export namespace Flawed { - const Amethyst: 558; - const Topaz: 563; - const Sapphire: 568; - const Emerald: 573; - const Ruby: 578; - const Diamond: 583; - const Skull: 598; - } - export namespace Chipped { - const Amethyst: 557; - const Topaz: 562; - const Sapphire: 567; - const Emerald: 572; - const Ruby: 577; - const Diamond: 582; - const Skull: 597; - } - } - } + TheBlackTowerKey: 544; + }; + runes: { + El: 610; + Eld: 611; + Tir: 612; + Nef: 613; + Eth: 614; + Ith: 615; + Tal: 616; + Ral: 617; + Ort: 618; + Thul: 619; + Amn: 620; + Sol: 621; + Shael: 622; + Dol: 623; + Hel: 624; + Io: 625; + Lum: 626; + Ko: 627; + Fal: 628; + Lem: 629; + Pul: 630; + Um: 631; + Mal: 632; + Ist: 633; + Gul: 634; + Vex: 635; + Ohm: 636; + Lo: 637; + Sur: 638; + Ber: 639; + Jah: 640; + Cham: 641; + Zod: 642; + }; + gems: { + Perfect: { + Amethyst: 561; + Topaz: 566; + Sapphire: 571; + Emerald: 576; + Ruby: 581; + Diamond: 586; + Skull: 601; + }; + Flawless: { + Amethyst: 560; + Topaz: 565; + Sapphire: 570; + Emerald: 575; + Ruby: 580; + Diamond: 585; + Skull: 600; + }; + Normal: { + Amethyst: 559; + Topaz: 564; + Sapphire: 569; + Emerald: 574; + Ruby: 579; + Diamond: 584; + Skull: 599; + }; + Flawed: { + Amethyst: 558; + Topaz: 563; + Sapphire: 568; + Emerald: 573; + Ruby: 578; + Diamond: 583; + Skull: 598; + }; + Chipped: { + Amethyst: 557; + Topaz: 562; + Sapphire: 567; + Emerald: 572; + Ruby: 577; + Diamond: 582; + Skull: 597; + }; + }; + }; // locale strings - export namespace locale { - export namespace monsters { + locale: { + monsters: { // bosses - const Andariel: 3021; - const Duriel: 3054; - const Mephisto: 3062; - const Diablo: 3060; - const Baal: 3061; + Andariel: 3021; + Duriel: 3054; + Mephisto: 3062; + Diablo: 3060; + Baal: 3061; // Mini bosses - const BloodRaven: 3111; - const TreeheadWoodFist: 2873; - const TheCountess: 2875; - const TheSmith: 2889; - const Radament: 2879; - const TheSummoner: 3099; - const HephastoTheArmorer: 1067; - const Izual: 1014; - const ShenktheOverseer: 22435; + BloodRaven: 3111; + TreeheadWoodFist: 2873; + TheCountess: 2875; + TheSmith: 2889; + Radament: 2879; + TheSummoner: 3099; + HephastoTheArmorer: 1067; + Izual: 1014; + ShenktheOverseer: 22435; // Uniques - const Corpsefire: 3319; - const TheCowKing: 2850; - const GrandVizierofChaos: 2851; - const LordDeSeis: 2852; - const InfectorofSouls: 2853; - const RiftwraiththeCannibal: 2854; - const Taintbreeder: 2855; - const TheTormentor: 2856; - const Darkwing: 2857; - const MafferDragonhand: 2858; - const WyandVoidbringer: 2859; - const ToorcIcefist: 2860; - const BremmSparkfist: 2861; - const GelebFlamefinger: 2862; - const IsmailVilehand: 2863; - const IcehawkRiftwing: 2864; - const BattlemaidSarina: 2865; - const Stormtree: 2866; - const WitchDoctorEndugu: 2867; - const SszarkTheBurning: 2868; - const Bishibosh: 2869; - const Bonebreaker: 2870; - const Coldcrow: 2871; - const Rakanishu: 2872; - const Griswold: 2874; - const PitspawnFouldog: 2876; - const FlamespiketheCrawler: 2877; - const BoneAsh: 2878; - const BloodwitchtheWild: 2880; - const Fangskin: 2881; - const Beetleburst: 2882; - const CreepingFeature: 2883; - const ColdwormtheBurrower: 2884; - const FireEye: 2885; - const DarkElder: 2886; - const AncientKaatheSoulless: 2888; - const SharpToothSayer: 22493; - const SnapchipShatter: 22496; - const Pindleskin: 22497; - const ThreshSocket: 22498; - const EyebacktheUnleashed: 22499; - const EldritchtheRectifier: 22500; - const DacFarren: 22501; - const BonesawBreaker: 22502; - const Frozenstein: 22504; - const Rogue: 2897; - const StygianDoll: 2898; - const SoulKiller: 2899; - const Flayer: 2900; - const Fetish: 2901; - const RatMan: 2902; - const UndeadStygianDoll: 2903; - const UndeadSoulKiller: 2904; - const UndeadFlayer: 2905; - const UndeadFetish: 2906; - const UndeadRatMan: 2907; - const DarkFamiliar: 2908; - const BloodDiver: 2909; - const Gloombat: 2910; - const DesertWing: 2911; - const TheBanished: 2912; - const BloodLord: 2913; - const DarkLord: 2914; - const NightLord: 2915; - const GhoulLord: 2916; - const Spikefist: 2917; - const Thrasher: 2918; - const BrambleHulk: 2919; - const ThornedHulk: 2920; - const SpiderMagus: 2921; - const FlameSpider: 2922; - const PoisonSpinner: 2923; - const SandFisher: 2924; - const Arach: 2925; - const BloodWing: 2926; - const BloodHook: 2927; - const Feeder: 2928; - const Sucker: 2929; - const WingedNightmare: 2930; - const HellBuzzard: 2931; - const UndeadScavenger: 2932; - const CarrionBird: 2933; - const Unraveler: 2934; - const Guardian: 2935; - const HollowOne: 2936; - const HoradrimAncient: 2937; - const BoneScarab: 2938; - const SteelScarab: 2939; - const Scarab: 2940; - const DeathBeetle: 2941; - const DungSoldier: 2942; - const HellSwarm: 2943; - const PlagueBugs: 2944; - const BlackLocusts: 2945; - const Itchies: 2946; - const HellCat: 2947; - const NightTiger: 2948; - const SaberCat: 2949; - const Huntress: 2950; - const CliffLurker: 2951; - const TreeLurker: 2952; - const CaveLeaper: 2953; - const TombCreeper: 2954; - const SandLeaper: 2955; - const TombViper: 2956; - const PitViper: 2957; - const Salamander: 2958; - const ClawViper: 2959; - const SerpentMagus: 2960; - const BloodMaggot: 2961; - const GiantLamprey: 2962; - const Devourer: 2963; - const RockWorm: 2964; - const SandMaggot: 2965; - const BushBarb: 2966; - const RazorSpine: 2967; - const ThornBeast: 2968; - const SpikeFiend: 2969; - const QuillRat: 2970; - const HellClan: 2971; - const MoonClan: 2972; - const NightClan: 2973; - const DeathClan: 2974; - const BloodClan: 2975; - const TempleGuard: 2976; - const DoomApe: 2977; - const JungleHunter: 2978; - const RockDweller: 2979; - const DuneBeast: 2980; - const FleshHunter: 2981; - const BlackRogue: 2982; - const DarkStalker: 2983; - const VileHunter: 2984; - const DarkHunter: 2985; - const DarkShape: 2986; - const Apparition: 2987; - const Specter: 2988; - const Wraith: 2989; - const Ghost: 2990; - const Assailant: 2991; - const Infidel: 2992; - const Invader: 2993; - const Marauder: 2994; - const SandRaider: 2995; - const GargantuanBeast: 2996; - const WailingBeast: 2997; - const Yeti: 2998; - const Crusher: 2999; - const Brute: 3000; - const CloudStalker: 3001; - const BlackVulture: 3002; - const BlackRaptor: 3003; - const BloodHawk: 3004; - const FoulCrow: 3005; - const PlagueBearer: 3006; - const Ghoul: 3007; - const DrownedCarcass: 3008; - const HungryDead: 3009; - const Zombie: 3010; - const Horror: 3012; - const Returned: 3013; - const BurningDead: 3014; - const BoneWarrior: 3015; - const Damned: 3016; - const Disfigured: 3017; - const Misshapen: 3018; - const Tainted: 3019; - const Afflicted: 3020; - const Camel: 3033; - const Cadaver: 3034; - const PreservedDead: 3035; - const Embalmed: 3036; - const DriedCorpse: 3037; - const Decayed: 3038; - const Urdar: 3039; - const Mauler: 3040; - const Gorebelly: 3041; - const Blunderbore: 3042; - const BloodMaggotYoung: 3043; - const GiantLampreyYoung: 3044; - const DevourerYoung: 3045; - const RockWormYoung: 3046; - const SandMaggotYoung: 3047; - const BloodMaggotEgg: 3048; - const GiantLampreyEgg: 3049; - const DevourerEgg: 3050; - const RockWormEgg: 3051; - const SandMaggotEgg: 3052; - const Maggot: 3053; - const BloodHawkNest: 3055; - const FlyingScimitar: 3056; - const CloudStalkerNest: 3057; - const BlackRaptorNest: 3058; - const FoulCrowNest: 3059; - const Cantor: 3063; - const Heirophant: 3064; - const Sexton: 3065; - const Zealot: 3066; - const Faithful: 3067; - const Zakarumite: 3068; - const BlackSoul: 3069; - const BurningSoul: 3070; - const SwampGhost: 3071; - const Gloam: 3072; - const WarpedShaman: 3073; - const DarkShaman: 3074; - const DevilkinShaman: 3075; - const CarverShaman: 3076; - const FallenShaman: 3077; - const WarpedOne: 3078; - const DarkOne: 3079; - const Devilkin: 3080; - const Carver: 3081; - const Fallen: 3082; - const ReturnedArcher: 3083; - const HorrorArcher: 3084; - const BurningDeadArcher: 3085; - const BoneArcher: 3086; - const CorpseArcher: 3087; - const SkeletonArcher: 3088; - const FleshLancer: 3089; - const BlackLancer: 3090; - const DarkLancer: 3091; - const VileLancer: 3092; - const DarkSpearwoman: 3093; - const FleshArcher: 3094; - const BlackArcher: 3095; - const DarkRanger: 3096; - const VileArcher: 3097; - const DarkArcher: 3098; - const StygianDollShaman: 3100; - const SoulKillerShaman: 3101; - const FlayerShaman: 3102; - const FetishShaman: 3103; - const RatManShaman: 3104; - const HorrorMage: 3105; - const BurningDeadMage: 3106; - const BoneMage: 3107; - const CorpseMage: 3108; - const ReturnedMage: 3109; - const GargoyleTrap: 3110; - const NightMarauder: 3121; - const FireGolem: 3122; - const IronGolem: 3123; - const BloodGolem: 3124; - const ClayGolem: 3125; - const BloodMaggotQueen: 3126; - const GiantLampreyQueen: 3127; - const DevourerQueen: 3128; - const RockWormQueen: 3129; - const SandMaggotQueen: 3130; - const SlimePrince: 3131; - const BogCreature: 3132; - const SwampDweller: 3133; - const BarbedGiant: 3134; - const RazorBeast: 3135; - const ThornBrute: 3136; - const SpikeGiant: 3137; - const QuillBear: 3138; - const CouncilMember: 3139; - const DarkWanderer: 3141; - const HellSlinger: 3142; - const NightSlinger: 3143; - const SpearCat: 3144; - const Slinger: 3145; - const FireTower: 3146; - const LightningSpire: 3147; - const PitLord: 3148; - const Balrog: 3149; - const VenomLord: 3150; - const IronWolf: 3151; - const InvisoSpawner: 3152; - const OblivionKnight: 3153; - const Mage: 3154; - const AbyssKnight: 3155; - const FighterMage: 3156; - const DoomKnight: 3157; - const Fighter: 3158; - const MawFiend: 3159; - const CorpseSpitter: 3160; - const Corpulent: 3161; - const StormCaster: 3162; - const Strangler: 3163; - const DoomCaster: 3164; - const GrotesqueWyrm: 3165; - const StygianDog: 3166; - const FleshBeast: 3167; - const Grotesque: 3168; - const StygianHag: 3169; - const FleshSpawner: 3170; - const RogueScout: 3171; - const BloodWingNest: 3172; - const BloodHookNest: 3173; - const FeederNest: 3174; - const SuckerNest: 3175; - const Hydra: 3325; - } - namespace npcs { - const Asheara: 1008; - const Hratli: 1009; - const Alkor: 1010; - const Ormus: 1011; - const Natalya: 1012; - const Tyrael: 1013; - const Izual1: 1014; - const Izual2: 1015; - const Jamella: 1016; - const Halbu: 1017; - const Hadriel: 1018; - const Hazade: 1019; - const Alhizeer: 1020; - const Azrael: 1021; - const Ahsab: 1022; - const Chalan: 1023; - const Haseen: 1024; - const Razan: 1025; - const Emilio: 1026; - const Pratham: 1027; - const Fazel: 1028; - const Jemali: 1029; - const Kasim: 1030; - const Gulzar: 1031; - const Mizan: 1032; - const Leharas: 1033; - const Durga: 1034; - const Neeraj: 1035; - const Ilzan: 1036; - const Zanarhi: 1037; - const Waheed: 1038; - const Vikhyat: 1039; - const Jelani: 1040; - const Barani: 1041; - const Jabari: 1042; - const Devak: 1043; - const Raldin: 1044; - const Telash: 1045; - const Ajheed: 1046; - const Narphet: 1047; - const Khaleel: 1048; - const Phaet: 1049; - const Geshef: 1050; - const Vanji: 1051; - const Haphet: 1052; - const Thadar: 1053; - const Yatiraj: 1054; - const Rhadge: 1055; - const Yashied: 1056; - const Lharhad: 1057; - const Flux: 1058; - const Scorch: 1059; - //const Natalya: 3022; both 1012 and 3022 return Natalya? - const DeckardCain: 2890; - const Gheed: 2891; - const Akara: 2892; - const Kashya: 2893; - const Charsi: 2894; - const Warriv: 2895; - const Drognan: 3023; - const Atma: 3024; - const Fara: 3025; - const Lysander: 3026; - const Jerhyn: 3028; - const Geglash: 3029; - const Elzix: 3030; - const Greiz: 3031; - const Flavie: 3112; - const Kaelan: 3113; - const Meshif: 3114; - const Larzuk: 22476; - const Anya: 22477; - const Malah: 22478; - const Nihlathak1: 22479; - const QualKehk: 22480; - const Guard: 22481; - const Combatant: 22482; - const Nihlathak2: 22483; - } - namespace items { - const KhalimsFlail: 1060; - const KhalimsWill1: 1061; - const KhalimsFlail2: 1062; - const KhalimsWill2: 1063; - const KhalimsEye: 1064; - const KhalimsBrain: 1065; - const KhalimsHeart: 1066; - const ScrollofInifuss: 2216; - const KeytotheCairnStones: 2217; - const AJadeFigurine: 2227; - const TheGoldenBird: 2228; - const LamEsensTome1: 2229; - const LamEsensTome2: 2230; - const HoradricCube: 2231; - const HoradricScroll: 2232; - const MephistosSoulstone: 2233; - const Ear: 2235; - const AmuletoftheViper: 2697; - const StaffofKings: 2698; - const HoradricStaff: 2699; + Corpsefire: 3319; + TheCowKing: 2850; + GrandVizierofChaos: 2851; + LordDeSeis: 2852; + InfectorofSouls: 2853; + RiftwraiththeCannibal: 2854; + Taintbreeder: 2855; + TheTormentor: 2856; + Darkwing: 2857; + MafferDragonhand: 2858; + WyandVoidbringer: 2859; + ToorcIcefist: 2860; + BremmSparkfist: 2861; + GelebFlamefinger: 2862; + IsmailVilehand: 2863; + IcehawkRiftwing: 2864; + BattlemaidSarina: 2865; + Stormtree: 2866; + WitchDoctorEndugu: 2867; + SszarkTheBurning: 2868; + Bishibosh: 2869; + Bonebreaker: 2870; + Coldcrow: 2871; + Rakanishu: 2872; + Griswold: 2874; + PitspawnFouldog: 2876; + FlamespiketheCrawler: 2877; + BoneAsh: 2878; + BloodwitchtheWild: 2880; + Fangskin: 2881; + Beetleburst: 2882; + CreepingFeature: 2883; + ColdwormtheBurrower: 2884; + FireEye: 2885; + DarkElder: 2886; + AncientKaatheSoulless: 2888; + SharpToothSayer: 22493; + SnapchipShatter: 22496; + Pindleskin: 22497; + ThreshSocket: 22498; + EyebacktheUnleashed: 22499; + EldritchtheRectifier: 22500; + DacFarren: 22501; + BonesawBreaker: 22502; + Frozenstein: 22504; + Rogue: 2897; + StygianDoll: 2898; + SoulKiller: 2899; + Flayer: 2900; + Fetish: 2901; + RatMan: 2902; + UndeadStygianDoll: 2903; + UndeadSoulKiller: 2904; + UndeadFlayer: 2905; + UndeadFetish: 2906; + UndeadRatMan: 2907; + DarkFamiliar: 2908; + BloodDiver: 2909; + Gloombat: 2910; + DesertWing: 2911; + TheBanished: 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; + HoradrimAncient: 2937; + BoneScarab: 2938; + SteelScarab: 2939; + Scarab: 2940; + DeathBeetle: 2941; + DungSoldier: 2942; + HellSwarm: 2943; + PlagueBugs: 2944; + BlackLocusts: 2945; + Itchies: 2946; + HellCat: 2947; + NightTiger: 2948; + SaberCat: 2949; + Huntress: 2950; + CliffLurker: 2951; + TreeLurker: 2952; + CaveLeaper: 2953; + TombCreeper: 2954; + SandLeaper: 2955; + TombViper: 2956; + PitViper: 2957; + Salamander: 2958; + ClawViper: 2959; + SerpentMagus: 2960; + BloodMaggot: 2961; + GiantLamprey: 2962; + Devourer: 2963; + RockWorm: 2964; + SandMaggot: 2965; + BushBarb: 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; + Wraith: 2989; + 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; + Horror: 3012; + Returned: 3013; + BurningDead: 3014; + BoneWarrior: 3015; + Damned: 3016; + Disfigured: 3017; + Misshapen: 3018; + Tainted: 3019; + Afflicted: 3020; + Camel: 3033; + Cadaver: 3034; + PreservedDead: 3035; + Embalmed: 3036; + DriedCorpse: 3037; + Decayed: 3038; + Urdar: 3039; + Mauler: 3040; + Gorebelly: 3041; + Blunderbore: 3042; + BloodMaggotYoung: 3043; + GiantLampreyYoung: 3044; + DevourerYoung: 3045; + RockWormYoung: 3046; + SandMaggotYoung: 3047; + BloodMaggotEgg: 3048; + GiantLampreyEgg: 3049; + DevourerEgg: 3050; + RockWormEgg: 3051; + SandMaggotEgg: 3052; + Maggot: 3053; + BloodHawkNest: 3055; + FlyingScimitar: 3056; + CloudStalkerNest: 3057; + BlackRaptorNest: 3058; + FoulCrowNest: 3059; + 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; + WarpedOne: 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; + StygianDollShaman: 3100; + SoulKillerShaman: 3101; + FlayerShaman: 3102; + FetishShaman: 3103; + RatManShaman: 3104; + HorrorMage: 3105; + BurningDeadMage: 3106; + BoneMage: 3107; + CorpseMage: 3108; + ReturnedMage: 3109; + GargoyleTrap: 3110; + NightMarauder: 3121; + FireGolem: 3122; + IronGolem: 3123; + BloodGolem: 3124; + ClayGolem: 3125; + BloodMaggotQueen: 3126; + GiantLampreyQueen: 3127; + DevourerQueen: 3128; + RockWormQueen: 3129; + SandMaggotQueen: 3130; + SlimePrince: 3131; + BogCreature: 3132; + SwampDweller: 3133; + BarbedGiant: 3134; + RazorBeast: 3135; + ThornBrute: 3136; + SpikeGiant: 3137; + QuillBear: 3138; + CouncilMember: 3139; + DarkWanderer: 3141; + HellSlinger: 3142; + NightSlinger: 3143; + SpearCat: 3144; + Slinger: 3145; + FireTower: 3146; + LightningSpire: 3147; + PitLord: 3148; + Balrog: 3149; + VenomLord: 3150; + IronWolf: 3151; + InvisoSpawner: 3152; + OblivionKnight: 3153; + Mage: 3154; + AbyssKnight: 3155; + FighterMage: 3156; + DoomKnight: 3157; + Fighter: 3158; + MawFiend: 3159; + CorpseSpitter: 3160; + Corpulent: 3161; + StormCaster: 3162; + Strangler: 3163; + DoomCaster: 3164; + GrotesqueWyrm: 3165; + StygianDog: 3166; + FleshBeast: 3167; + Grotesque: 3168; + StygianHag: 3169; + FleshSpawner: 3170; + RogueScout: 3171; + BloodWingNest: 3172; + BloodHookNest: 3173; + FeederNest: 3174; + SuckerNest: 3175; + Hydra: 3325; + }; + npcs: { + Asheara: 1008; + Hratli: 1009; + Alkor: 1010; + Ormus: 1011; + Natalya: 1012; + Tyrael: 1013; + Izual1: 1014; + Izual2: 1015; + Jamella: 1016; + Halbu: 1017; + Hadriel: 1018; + Hazade: 1019; + Alhizeer: 1020; + Azrael: 1021; + Ahsab: 1022; + Chalan: 1023; + Haseen: 1024; + Razan: 1025; + Emilio: 1026; + Pratham: 1027; + Fazel: 1028; + Jemali: 1029; + Kasim: 1030; + Gulzar: 1031; + Mizan: 1032; + Leharas: 1033; + Durga: 1034; + Neeraj: 1035; + Ilzan: 1036; + Zanarhi: 1037; + Waheed: 1038; + Vikhyat: 1039; + Jelani: 1040; + Barani: 1041; + Jabari: 1042; + Devak: 1043; + Raldin: 1044; + Telash: 1045; + Ajheed: 1046; + Narphet: 1047; + Khaleel: 1048; + Phaet: 1049; + Geshef: 1050; + Vanji: 1051; + Haphet: 1052; + Thadar: 1053; + Yatiraj: 1054; + Rhadge: 1055; + Yashied: 1056; + Lharhad: 1057; + Flux: 1058; + Scorch: 1059; + //Natalya: 3022; both 1012 and 3022 return Natalya? + DeckardCain: 2890; + Gheed: 2891; + Akara: 2892; + Kashya: 2893; + Charsi: 2894; + Warriv: 2895; + Drognan: 3023; + Atma: 3024; + Fara: 3025; + Lysander: 3026; + Jerhyn: 3028; + Geglash: 3029; + Elzix: 3030; + Greiz: 3031; + Flavie: 3112; + Kaelan: 3113; + Meshif: 3114; + Larzuk: 22476; + Anya: 22477; + Malah: 22478; + Nihlathak1: 22479; + QualKehk: 22480; + Guard: 22481; + Combatant: 22482; + Nihlathak2: 22483; + }; + items: { + KhalimsFlail: 1060; + KhalimsWill1: 1061; + KhalimsFlail2: 1062; + KhalimsWill2: 1063; + KhalimsEye: 1064; + KhalimsBrain: 1065; + KhalimsHeart: 1066; + ScrollofInifuss: 2216; + KeytotheCairnStones: 2217; + AJadeFigurine: 2227; + TheGoldenBird: 2228; + LamEsensTome1: 2229; + LamEsensTome2: 2230; + HoradricCube: 2231; + HoradricScroll: 2232; + MephistosSoulstone: 2233; + Ear: 2235; + AmuletoftheViper: 2697; + StaffofKings: 2698; + HoradricStaff: 2699; // Sets // Angelic Rainment - const AngelicsSword: 10172; - const AngelicsArmor: 10173; - const AngelicsRing: 10174; - const AngelicsAmulet: 10175; + AngelicsSword: 10172; + AngelicsArmor: 10173; + AngelicsRing: 10174; + AngelicsAmulet: 10175; // Arcannas Tricks - const ArcannasAmulet: 10180; - const ArcannasStaff: 10181; - const ArcannasHelmet: 10182; - const ArcannasArmor: 10183; + ArcannasAmulet: 10180; + ArcannasStaff: 10181; + ArcannasHelmet: 10182; + ArcannasArmor: 10183; // Artic Gear - const ArticsBow: 10176; - const ArticsArmor: 10177; - const ArticsBelt: 10178; - const ArticsGloves: 10179; + ArticsBow: 10176; + ArticsArmor: 10177; + ArticsBelt: 10178; + ArticsGloves: 10179; // Berserkers Gear - const BerserkersHelmet: 10166; - const BerserkersAxe: 10167; - const BerserkersArmor: 10168; + BerserkersHelmet: 10166; + BerserkersAxe: 10167; + BerserkersArmor: 10168; // Cathans Traps - const CathansRing: 10147; - const CathansAmulet: 10148; - const CathansHelmet: 10149; - const CathansArmor: 10150; - const CathansStaff: 10151; + CathansRing: 10147; + CathansAmulet: 10148; + CathansHelmet: 10149; + CathansArmor: 10150; + CathansStaff: 10151; // Civerbs Gear - const CiverbsShield: 10122; - const CiverbsAmulet: 10123; - const CiverbsScepter: 10124; + CiverbsShield: 10122; + CiverbsAmulet: 10123; + CiverbsScepter: 10124; // Clegaws Brace - const ClegawsSword: 10128; - const ClegawsShield: 10129; - const ClegawsGloves: 10130; + ClegawsSword: 10128; + ClegawsShield: 10129; + ClegawsGloves: 10130; // Deaths Disguise - const DeathsGloves: 10169; - const DeathsBelt: 10170; - const DeathsSword: 10171; + DeathsGloves: 10169; + DeathsBelt: 10170; + DeathsSword: 10171; // Hsarus Defense - const HsarusBoots: 10125; - const HsarusShield: 10126; - const HsarusBelt: 10127; + HsarusBoots: 10125; + HsarusShield: 10126; + HsarusBelt: 10127; // Infernal Tools - const InfernalsHelmet: 10163; - const InfernalsWand: 10164; - const InfernalsBelt: 10165; + InfernalsHelmet: 10163; + InfernalsWand: 10164; + InfernalsBelt: 10165; // Irathas Finery - const IrathasBelt: 10131; - const IrathasHelmet: 10132; - const IrathasGloves: 10133; - const IrathasAmulet: 10134; + IrathasBelt: 10131; + IrathasHelmet: 10132; + IrathasGloves: 10133; + IrathasAmulet: 10134; // Isenharts Armory - const IsenhartsHelmet: 10135; - const IsenhartsArmor: 10136; - const IsenhartsShield: 10137; - const IsenhartsSword: 10138; + IsenhartsHelmet: 10135; + IsenhartsArmor: 10136; + IsenhartsShield: 10137; + IsenhartsSword: 10138; // Milabrega Regalia - const MilabregasArmor: 10143; - const MilabregasHelmet: 10144; - const MilabregasScepter: 10145; - const MilabregasShield: 10146; + MilabregasArmor: 10143; + MilabregasHelmet: 10144; + MilabregasScepter: 10145; + MilabregasShield: 10146; // Sigons - const SigonsHelmet: 10157; - const SigonsArmor: 10158; - const SigonsGloves: 10159; - const SigonsBoots: 10160; - const SigonsBelt: 10161; - const SigonsShield: 10162; + SigonsHelmet: 10157; + SigonsArmor: 10158; + SigonsGloves: 10159; + SigonsBoots: 10160; + SigonsBelt: 10161; + SigonsShield: 10162; // Tancreds - const TancredsPick: 10152; - const TancredsArmor: 10153; - const TancredsBoots: 10154; - const TancredsAmulet: 10155; - const TancredsHelmet: 10156; + TancredsPick: 10152; + TancredsArmor: 10153; + TancredsBoots: 10154; + TancredsAmulet: 10155; + TancredsHelmet: 10156; // Vidalas - const VidalasAmulet: 10139; - const VidalasArmor: 10140; - const VidalasBoots: 10141; - const VidalasBow: 10142; + VidalasAmulet: 10139; + VidalasArmor: 10140; + VidalasBoots: 10141; + VidalasBow: 10142; // LoD Sets // Aldurs's Legacy - const AldursHelmet: 21697; - const AldursArmor: 21698; - const AldursBoots: 21700; - const AldursMace: 21847; + AldursHelmet: 21697; + AldursArmor: 21698; + AldursBoots: 21700; + AldursMace: 21847; // Bul-Kathos's Children - const BulKathosBlade: 21688; - const BulKathoSword: 21689; + BulKathosBlade: 21688; + BulKathoSword: 21689; // Cow Kings's Leathers - const CowKingsHelmet: 21723; - const CowKingsArmor: 21724; - const CowKingsBoots: 21725; + CowKingsHelmet: 21723; + CowKingsArmor: 21724; + CowKingsBoots: 21725; // Disciples - const DisciplesAmulet: 21717; - const DisciplesGloves: 21718; - const DisciplesBoots: 21719; - const DisciplesArmor: 21720; - const DisciplesBelt: 21721; + DisciplesAmulet: 21717; + DisciplesGloves: 21718; + DisciplesBoots: 21719; + DisciplesArmor: 21720; + DisciplesBelt: 21721; // Griswolds's Legacy - const GriswoldsScepter: 21673; - const GriswoldsShield: 21674; - const GriswoldsArmor: 21675; - const GriswoldsHelmet: 21676; + GriswoldsScepter: 21673; + GriswoldsShield: 21674; + GriswoldsArmor: 21675; + GriswoldsHelmet: 21676; // Heaven's Brethren - const HeavensMace: 21823; - const HeavensHelmet: 21824; - const HeavensShield: 21825; - const HeavensArmor: 21826; + HeavensMace: 21823; + HeavensHelmet: 21824; + HeavensShield: 21825; + HeavensArmor: 21826; // Hwanin's - const HwaninsHelmet: 21712; - const HwaninsPolearm: 21713; - const HwaninsArmor: 21714; - const HwaninsBelt: 21821; + HwaninsHelmet: 21712; + HwaninsPolearm: 21713; + HwaninsArmor: 21714; + HwaninsBelt: 21821; // IK - const ImmortalKingsMaul: 21840; - const ImmortalKingsBoots: 21841; - const ImmortalKingsGloves: 21842; - const ImmortalKingsBelt: 21843; - const ImmortalKingsArmor: 21844; - const ImmortalKingsHelmet: 21845; + ImmortalKingsMaul: 21840; + ImmortalKingsBoots: 21841; + ImmortalKingsGloves: 21842; + ImmortalKingsBelt: 21843; + ImmortalKingsArmor: 21844; + ImmortalKingsHelmet: 21845; // M'avina's - const MavinasHelmet: 21702; - const MavinasArmor: 21703; - const MavinasGloves: 21704; - const MavinasBelt: 21705; - const MavinasBow: 21706; + MavinasHelmet: 21702; + MavinasArmor: 21703; + MavinasGloves: 21704; + MavinasBelt: 21705; + MavinasBow: 21706; // Natalya's - const NatalyasHelmet: 21668; - const NatalyasClaw: 21669; - const NatalyasArmor: 21670; - const NatalyasBoots: 21671; + NatalyasHelmet: 21668; + NatalyasClaw: 21669; + NatalyasArmor: 21670; + NatalyasBoots: 21671; // Naj's - const NajsStaff: 21640; - const NajsArmor: 21831; - const NajsHelmet: 21832; + NajsStaff: 21640; + NajsArmor: 21831; + NajsHelmet: 21832; // Orphan's - const OrphansHelmet: 21731; - const OrphansBelt: 21732; - const OrphansGloves: 21733; - const OrphansShield: 21734; + OrphansHelmet: 21731; + OrphansBelt: 21732; + OrphansGloves: 21733; + OrphansShield: 21734; // Sanders's - const SandersGloves: 21876; - const SandersBoots: 21877; - const SandersHelmet: 21878; - const SandersWand: 21879; + SandersGloves: 21876; + SandersBoots: 21877; + SandersHelmet: 21878; + SandersWand: 21879; // Sazabi's - const SazabisSword: 21708; - const SazabisArmor: 21709; - const SazabisHelmet: 21710; + SazabisSword: 21708; + SazabisArmor: 21709; + SazabisHelmet: 21710; // Tal - const TalRashasBelt: 21816; - const TalRashasAmulet: 21817; - const TalRashasArmor: 21818; - const TalRashasOrb: 21819; - const TalRashasHelmet: 21820; + TalRashasBelt: 21816; + TalRashasAmulet: 21817; + TalRashasArmor: 21818; + TalRashasOrb: 21819; + TalRashasHelmet: 21820; // Trang-Ouls - const TrangOulsHelmet: 21661; - const TrangOulsShield: 21662; - const TrangOulsArmor: 21664; - const TrangOulsGloves: 21665; - const TrangOulsBelt: 21666; + TrangOulsHelmet: 21661; + TrangOulsShield: 21662; + TrangOulsArmor: 21664; + TrangOulsGloves: 21665; + TrangOulsBelt: 21666; // Uniques // Quest/Misc - const KeyofTerror: 11146; - const KeyofHate: 11147; - const KeyofDestruction: 11148; - const DiablosHorn: 11149; - const BaalsEye: 11150; - const MephistosBrain: 11151; - const StandardofHeroes: 11152; - const HellfireTorch: 11153; - const Annihilus: 21743; + KeyofTerror: 11146; + KeyofHate: 11147; + KeyofDestruction: 11148; + DiablosHorn: 11149; + BaalsEye: 11150; + MephistosBrain: 11151; + StandardofHeroes: 11152; + HellfireTorch: 11153; + Annihilus: 21743; // Unique Items - const WitchwildString: 10911; - const TitansRevenge: 21735; - const LycandersAim: 21737; - const ArreatsFace: 21744; - const Homunculus: 21755; - const JalalsMane: 21750; - const HeraldofZakarum: 21758; - const BloodRavensCharge: 21508; - const Gimmershred: 21637; - const MedusasGaze: 21516; - const Rockstopper: 21519; - const CrownofThieves: 21522; - const BlackhornsFace: 21523; - const TheSpiritShroud: 21524; - const SkinoftheFlayedOne: 21525; - const IronPelt: 21526; - const SpiritForge: 21527; - const CrowCaw: 21528; - const DurielsShell: 21529; - const SkulldersIre: 21530; - const Toothrow: 21531; - const AtmasWail: 21532; - const BlackHades: 21533; - const Corpsemourn: 21534; - const QueHegans: 21535; - const QueHegansWisdom: 21535; - const Mosers: 21536; - const MosersBlessedCircle: 21536; - const Stormchaser: 21537; - const TiamatsRubuke: 21538; - const GerkesSanctuary: 21539; - const RadamentsSphere: 21540; - const Gravepalm: 21541; - const Ghoulhide: 21542; - const Hellmouth: 21543; - const Infernostride: 21544; - const Waterwalk: 21545; - const Silkweave: 21546; - const WarTraveler: 21547; - const Razortail: 21548; - const GloomsTrap: 21549; - const Snowclash: 21550; - const ThundergodsVigor: 21551; - const LidlessWall: 21552; - const LanceGuard: 21553; - const Boneflame: 21555; - const SteelPillar: 21556; - const NightwingsVeil: 21557; - const CrownofAges: 21559; - const AndarielsVisage: 21560; - const Dragonscale: 21562; - const SteelCarapace: 21563; - const RainbowFacet: 21565; - const Ravenlore: 21566; - const Boneshade: 21567; - const Flamebellow: 21570; - const DeathsFathom: 21571; - const Wolfhowl: 21572; - const SpiritWard: 21573; - const KirasGuardian: 21574; - const OrmusRobe: 21575; - const GheedsFortune: 21576; - const HalberdsReign: 21579; - const DraculsGrasp: 21583; - const Frostwind: 21584; - const TemplarsMight: 21585; - const EschutasTemper: 21586; // also 21620? - const FirelizardsTalons: 21587; - const SandstormTrek: 21588; - const Marrowwalk: 21589; - const HeavensLight: 21590; - const ArachnidMesh: 21592; - const NosferatusCoil: 21593; - const Verdungos: 21595; - const VerdungosHeartyCord: 21595; - const CarrionWind: 21597; - const GiantSkull: 21598; - const AstreonsIronWard: 21599; - const SaracensChance: 21608; - const HighlordsWrath: 21609; - const Ravenfrost: 21610; - const Dwarfstar: 21611; - const AtmasScarab: 21612; - const Maras: 21613; - const MarasKaleidoscope: 21613; - const CrescentMoonAmulet: 21614; - const TheRisingSun: 21615; - const TheCatsEye: 21616; - const BulKathosWeddingBand: 21617; - const Metalgrid: 21619; - const Stormshield: 21621; - const BlackoakShield: 21622; - const ArkainesValor: 21624; - const TheGladiatorsBane: 21625; - const HarlequinsCrest: 21627; - const GuardianAngel: 21632; - const TheGrandfather: 21643; - const Doombringer: 21644; - const TyraelsMight: 21645; - const Lightsabre: 21646; - const TheCraniumBasher: 21647; - const DeathsWeb: 21650; - const TheAtlantean: 21654; - const CarinShard: 21658; - const Coldkill: 21286; - const ButchersCleaver: 21287; - const Islestrike: 21289; - const GuardianNaga: 21291; - const SpellSteel: 21293; - const SuicideBranch: 21297; - const ArmofKingLeoric: 21299; - const BlackhandKey: 21300; - const DarkClanCrusher: 21301; - const TheFetidSprinkler: 21304; - const HandofBlessedLight: 21305; - const Fleshrender: 21306; - const SureshrillFrost: 21307; - const Moonfall: 21308; - const BaezilsVortex: 21309; - const Earthshaker: 21310; - const TheGavelofPain: 21312; - const Bloodletter: 21313; - const ColdstealEye: 21314; - const Hexfire: 21315; - const BladeofAliBaba: 21316; - const Riftslash: 21317; - const Headstriker: 21318; - const PlagueBearer: 21319; - //const TheAtlantean: 21320; - const CrainteVomir: 21321; - const BingSzWang: 21322; - const TheVileHusk: 21323; - const Cloudcrack: 21324; - const TodesfaelleFlamme: 21325; - const Swordguard: 21326; - const Spineripper: 21327; - const HeartCarver: 21328; - const BlackbogsSharp: 21329; - const Stormspike: 21330; - const TheImpaler: 21331; - const HoneSudan: 21334; - const SpireofHonor: 21335; - const TheMeatScraper: 21336; - const BlackleachBlade: 21337; - const AthenasWrath: 21338; - const PierreTombaleCouant: 21339; - const GrimsBurningDead: 21341; - const Ribcracker: 21342; - const ChromaticIre: 21343; - const Warspear: 21344; - const SkullCollector: 21345; - const Skystrike: 21346; - //const WitchwildString: 21349; - const GoldstrikeArch: 21350; - const PusSpitter: 21352; - const VampireGaze: 21354; - const StringofEars: 21355; - const GoreRider: 21356; - const LavaGout: 21357; - const VenomGrip: 21358; - const Visceratuant: 21359; - //const GuardianAngel: 21360; - const Shaftstop: 21361; - const SkinofVipermagi: 21362; - const Blackhorn: 21363; - const ValkyrieWing: 21364; - const PeasantCrown: 21365; - const DemonMachine: 21366; - const Riphook: 21369; - const Razorswitch: 21370; - const OndalsWisdom: 21375; - const Deathbit: 21379; - const Warshrike: 21380; - const DemonLimb: 21387; - const SteelShade: 21388; - const TombReaver: 21389; - //const DeathsWeb: 21390; - const AngelsSong: 21393; - const TheRedeemer: 21394; - const Bonehew: 21398; - const Steelrend: 21399; - const AriocsNeedle: 21402; - const SoulDrainer: 21407; - const RuneMaster: 21408; - const DeathCleaver: 21409; - const ExecutionersJustice: 21410; - const Leviathan: 21412; - const WispProjector: 21417; - const Lacerator: 21419; - const MangSongsLesson: 21420; - const Viperfork: 21421; - const TheReapersToll: 21427; - const SpiritKeeper: 21428; - const Hellrack: 21429; - const AlmaNegra: 21430; - const DarkforceSpawn: 21431; - const Ghostflame: 21438; - const ShadowKiller: 21439; - const GriffonsEye: 21442; - const Thunderstroke: 21445; - const DemonsArch: 21447; - const DjinnSlayer: 21450; - const Windforce: 21635; - const GinthersRift: 21829; + WitchwildString: 10911; + TitansRevenge: 21735; + LycandersAim: 21737; + ArreatsFace: 21744; + Homunculus: 21755; + JalalsMane: 21750; + HeraldofZakarum: 21758; + BloodRavensCharge: 21508; + Gimmershred: 21637; + MedusasGaze: 21516; + Rockstopper: 21519; + CrownofThieves: 21522; + BlackhornsFace: 21523; + TheSpiritShroud: 21524; + SkinoftheFlayedOne: 21525; + IronPelt: 21526; + SpiritForge: 21527; + CrowCaw: 21528; + DurielsShell: 21529; + SkulldersIre: 21530; + Toothrow: 21531; + AtmasWail: 21532; + BlackHades: 21533; + Corpsemourn: 21534; + QueHegans: 21535; + QueHegansWisdom: 21535; + Mosers: 21536; + MosersBlessedCircle: 21536; + Stormchaser: 21537; + TiamatsRubuke: 21538; + GerkesSanctuary: 21539; + RadamentsSphere: 21540; + Gravepalm: 21541; + Ghoulhide: 21542; + Hellmouth: 21543; + Infernostride: 21544; + Waterwalk: 21545; + Silkweave: 21546; + WarTraveler: 21547; + Razortail: 21548; + GloomsTrap: 21549; + Snowclash: 21550; + ThundergodsVigor: 21551; + LidlessWall: 21552; + LanceGuard: 21553; + Boneflame: 21555; + SteelPillar: 21556; + NightwingsVeil: 21557; + CrownofAges: 21559; + AndarielsVisage: 21560; + Dragonscale: 21562; + SteelCarapace: 21563; + RainbowFacet: 21565; + Ravenlore: 21566; + Boneshade: 21567; + Flamebellow: 21570; + DeathsFathom: 21571; + Wolfhowl: 21572; + SpiritWard: 21573; + KirasGuardian: 21574; + OrmusRobe: 21575; + GheedsFortune: 21576; + HalberdsReign: 21579; + DraculsGrasp: 21583; + Frostwind: 21584; + TemplarsMight: 21585; + EschutasTemper: 21586; // also 21620? + FirelizardsTalons: 21587; + SandstormTrek: 21588; + Marrowwalk: 21589; + HeavensLight: 21590; + ArachnidMesh: 21592; + NosferatusCoil: 21593; + Verdungos: 21595; + VerdungosHeartyCord: 21595; + CarrionWind: 21597; + GiantSkull: 21598; + AstreonsIronWard: 21599; + SaracensChance: 21608; + HighlordsWrath: 21609; + Ravenfrost: 21610; + Dwarfstar: 21611; + AtmasScarab: 21612; + Maras: 21613; + MarasKaleidoscope: 21613; + CrescentMoonAmulet: 21614; + TheRisingSun: 21615; + TheCatsEye: 21616; + BulKathosWeddingBand: 21617; + Metalgrid: 21619; + Stormshield: 21621; + BlackoakShield: 21622; + ArkainesValor: 21624; + TheGladiatorsBane: 21625; + HarlequinsCrest: 21627; + GuardianAngel: 21632; + TheGrandfather: 21643; + Doombringer: 21644; + TyraelsMight: 21645; + Lightsabre: 21646; + TheCraniumBasher: 21647; + DeathsWeb: 21650; + TheAtlantean: 21654; + CarinShard: 21658; + Coldkill: 21286; + ButchersCleaver: 21287; + Islestrike: 21289; + GuardianNaga: 21291; + SpellSteel: 21293; + SuicideBranch: 21297; + ArmofKingLeoric: 21299; + BlackhandKey: 21300; + DarkClanCrusher: 21301; + TheFetidSprinkler: 21304; + HandofBlessedLight: 21305; + Fleshrender: 21306; + SureshrillFrost: 21307; + Moonfall: 21308; + BaezilsVortex: 21309; + Earthshaker: 21310; + TheGavelofPain: 21312; + Bloodletter: 21313; + ColdstealEye: 21314; + Hexfire: 21315; + BladeofAliBaba: 21316; + Riftslash: 21317; + Headstriker: 21318; + PlagueBearer: 21319; + //TheAtlantean: 21320; + CrainteVomir: 21321; + BingSzWang: 21322; + TheVileHusk: 21323; + Cloudcrack: 21324; + TodesfaelleFlamme: 21325; + Swordguard: 21326; + Spineripper: 21327; + HeartCarver: 21328; + BlackbogsSharp: 21329; + Stormspike: 21330; + TheImpaler: 21331; + HoneSudan: 21334; + SpireofHonor: 21335; + TheMeatScraper: 21336; + BlackleachBlade: 21337; + AthenasWrath: 21338; + PierreTombaleCouant: 21339; + GrimsBurningDead: 21341; + Ribcracker: 21342; + ChromaticIre: 21343; + Warspear: 21344; + SkullCollector: 21345; + Skystrike: 21346; + //WitchwildString: 21349; + GoldstrikeArch: 21350; + PusSpitter: 21352; + VampireGaze: 21354; + StringofEars: 21355; + GoreRider: 21356; + LavaGout: 21357; + VenomGrip: 21358; + Visceratuant: 21359; + //GuardianAngel: 21360; + Shaftstop: 21361; + SkinofVipermagi: 21362; + Blackhorn: 21363; + ValkyrieWing: 21364; + PeasantCrown: 21365; + DemonMachine: 21366; + Riphook: 21369; + Razorswitch: 21370; + OndalsWisdom: 21375; + Deathbit: 21379; + Warshrike: 21380; + DemonLimb: 21387; + SteelShade: 21388; + TombReaver: 21389; + //DeathsWeb: 21390; + AngelsSong: 21393; + TheRedeemer: 21394; + Bonehew: 21398; + Steelrend: 21399; + AriocsNeedle: 21402; + SoulDrainer: 21407; + RuneMaster: 21408; + DeathCleaver: 21409; + ExecutionersJustice: 21410; + Leviathan: 21412; + WispProjector: 21417; + Lacerator: 21419; + MangSongsLesson: 21420; + Viperfork: 21421; + TheReapersToll: 21427; + SpiritKeeper: 21428; + Hellrack: 21429; + AlmaNegra: 21430; + DarkforceSpawn: 21431; + Ghostflame: 21438; + ShadowKiller: 21439; + GriffonsEye: 21442; + Thunderstroke: 21445; + DemonsArch: 21447; + DjinnSlayer: 21450; + Windforce: 21635; + GinthersRift: 21829; // Runewords - const AncientsPledge: 20507; - const Armageddon: 20508; - const Authority: 20509; - const Beast: 20510; - const Beauty: 20511; - const Black: 20512; - const Blood: 20513; - const Bone: 20514; - const Bramble: 20515; - const Brand: 20516; - const BreathoftheDying: 20517; - const BrokenPromise: 20518; - const CalltoArms: 20519; - const ChainsofHonor: 20520; - const Chance: 20521; - const Chaos: 20522; - const CrescentMoon: 20523; - const Darkness: 20524; - const Daylight: 20525; - const Death: 20526; - const Deception: 20527; - const Delerium: 20528; - const Desire: 20529; - const Despair: 20530; - const Destruction: 20531; - const Doom: 20532; - const Dragon: 20533; - const Dread: 20534; - const Dream: 20535; - const Duress: 20536; - const Edge: 20537; - const Elation: 20538; - const Enigma: 20539; - const Enlightenment: 20540; - const Envy: 20541; - const Eternity: 20542; - const Exile: 20543; - const Faith: 20544; - const Famine: 20545; - const Flame: 20546; - const Fortitude: 20547; - const Fortune: 20548; - const Friendship: 20549; - const Fury: 20550; - const Gloom: 20551; - const Grief: 20553; - const HandofJustice: 20554; - const Harmony: 20555; - const HeartoftheOak: 20557; - const HolyThunder: 20560; - const Honor: 20561; - const Revenge: 20562; - const Humility: 20563; - const Hunger: 20564; - const Ice: 20565; - const Infinity: 20566; - const Innocence: 20567; - const Insight: 20568; - const Jealousy: 20569; - const Judgement: 20570; - const KingsGrace: 20571; - const Kingslayer: 20572; - const KnightsVigil: 20573; - const Knowledge: 20574; - const LastWish: 20575; - const Law: 20576; - const Lawbringer: 20577; - const Leaf: 20578; - const Lightning: 20579; - const Lionheart: 20580; - const Lore: 20581; - const Love: 20582; - const Loyalty: 20583; - const Lust: 20584; - const Madness: 20585; - const Malice: 20586; - const Melody: 20587; - const Memory: 20588; - const Mist: 20589; - const Morning: 20590; - const Mystery: 20591; - const Myth: 20592; - const Nadir: 20593; - const NaturesKingdom: 20594; - const Night: 20595; - const Oath: 20596; - const Obedience: 20597; - const Oblivion: 20598; - const Obsession: 20599; - const Passion: 20600; - const Patience: 20601; - const Patter: 20602; - const Peace: 20603; - const VoiceofReason: 20604; - const Penitence: 20605; - const Peril: 20606; - const Pestilence: 20607; - const Phoenix: 20608; - const Piety: 20609; - const PillarofFaith: 20610; - const Plague: 20611; - const Praise: 20612; - const Prayer: 20613; - const Pride: 20614; - const Principle: 20615; - const ProwessinBattle: 20616; - const Prudence: 20617; - const Punishment: 20618; - const Purity: 20619; - const Question: 20620; - const Radiance: 20621; - const Rain: 20622; - const Reason: 20623; - const Red: 20624; - const Rhyme: 20625; - const Rift: 20626; - const Sanctuary: 20627; - const Serendipity: 20628; - const Shadow: 20629; - const ShadowofDoubt: 20630; - const Silence: 20631; - const SirensSong: 20632; - const Smoke: 20633; - const Sorrow: 20634; - const Spirit: 20635; - const Splendor: 20636; - const Starlight: 20637; - const Stealth: 20638; - const Steel: 20639; - const StillWater: 20640; - const Sting: 20641; - const Stone: 20642; - const Storm: 20643; - const Strength: 20644; - const Tempest: 20645; - const Temptation: 20646; - const Terror: 20647; - const Thirst: 20648; - const Thought: 20649; - const Thunder: 20650; - const Time: 20651; - const Tradition: 20652; - const Treachery: 20653; - const Trust: 20654; - const Truth: 20655; - const UnbendingWill: 20656; - const Valor: 20657; - const Vengeance: 20658; - const Venom: 20659; - const Victory: 20660; - const Voice: 20661; - const Void: 20662; - const War: 20663; - const Water: 20664; - const Wealth: 20665; - const Whisper: 20666; - const White: 20667; - const Wind: 20668; - const WingsofHope: 20669; - const Wisdom: 20670; - const Woe: 20671; - const Wonder: 20672; - const Wrath: 20673; - const Youth: 20674; - const Zephyr: 20675; - } - namespace dialog { - const youDoNotHaveEnoughGoldForThat: 3362; - } + AncientsPledge: 20507; + Armageddon: 20508; + Authority: 20509; + Beast: 20510; + Beauty: 20511; + Black: 20512; + Blood: 20513; + Bone: 20514; + Bramble: 20515; + Brand: 20516; + BreathoftheDying: 20517; + BrokenPromise: 20518; + CalltoArms: 20519; + ChainsofHonor: 20520; + Chance: 20521; + Chaos: 20522; + CrescentMoon: 20523; + Darkness: 20524; + Daylight: 20525; + Death: 20526; + Deception: 20527; + Delerium: 20528; + Desire: 20529; + Despair: 20530; + Destruction: 20531; + Doom: 20532; + Dragon: 20533; + Dread: 20534; + Dream: 20535; + Duress: 20536; + Edge: 20537; + Elation: 20538; + Enigma: 20539; + Enlightenment: 20540; + Envy: 20541; + Eternity: 20542; + Exile: 20543; + Faith: 20544; + Famine: 20545; + Flame: 20546; + Fortitude: 20547; + Fortune: 20548; + Friendship: 20549; + Fury: 20550; + Gloom: 20551; + Grief: 20553; + HandofJustice: 20554; + Harmony: 20555; + HeartoftheOak: 20557; + HolyThunder: 20560; + Honor: 20561; + Revenge: 20562; + Humility: 20563; + Hunger: 20564; + Ice: 20565; + Infinity: 20566; + Innocence: 20567; + Insight: 20568; + Jealousy: 20569; + Judgement: 20570; + KingsGrace: 20571; + Kingslayer: 20572; + KnightsVigil: 20573; + Knowledge: 20574; + LastWish: 20575; + Law: 20576; + Lawbringer: 20577; + Leaf: 20578; + Lightning: 20579; + Lionheart: 20580; + Lore: 20581; + Love: 20582; + Loyalty: 20583; + Lust: 20584; + Madness: 20585; + Malice: 20586; + Melody: 20587; + Memory: 20588; + Mist: 20589; + Morning: 20590; + Mystery: 20591; + Myth: 20592; + Nadir: 20593; + NaturesKingdom: 20594; + Night: 20595; + Oath: 20596; + Obedience: 20597; + Oblivion: 20598; + Obsession: 20599; + Passion: 20600; + Patience: 20601; + Patter: 20602; + Peace: 20603; + VoiceofReason: 20604; + Penitence: 20605; + Peril: 20606; + Pestilence: 20607; + Phoenix: 20608; + Piety: 20609; + PillarofFaith: 20610; + Plague: 20611; + Praise: 20612; + Prayer: 20613; + Pride: 20614; + Principle: 20615; + ProwessinBattle: 20616; + Prudence: 20617; + Punishment: 20618; + Purity: 20619; + Question: 20620; + Radiance: 20621; + Rain: 20622; + Reason: 20623; + Red: 20624; + Rhyme: 20625; + Rift: 20626; + Sanctuary: 20627; + Serendipity: 20628; + Shadow: 20629; + ShadowofDoubt: 20630; + Silence: 20631; + SirensSong: 20632; + Smoke: 20633; + Sorrow: 20634; + Spirit: 20635; + Splendor: 20636; + Starlight: 20637; + Stealth: 20638; + Steel: 20639; + StillWater: 20640; + Sting: 20641; + Stone: 20642; + Storm: 20643; + Strength: 20644; + Tempest: 20645; + Temptation: 20646; + Terror: 20647; + Thirst: 20648; + Thought: 20649; + Thunder: 20650; + Time: 20651; + Tradition: 20652; + Treachery: 20653; + Trust: 20654; + Truth: 20655; + UnbendingWill: 20656; + Valor: 20657; + Vengeance: 20658; + Venom: 20659; + Victory: 20660; + Voice: 20661; + Void: 20662; + War: 20663; + Water: 20664; + Wealth: 20665; + Whisper: 20666; + White: 20667; + Wind: 20668; + WingsofHope: 20669; + Wisdom: 20670; + Woe: 20671; + Wonder: 20672; + Wrath: 20673; + Youth: 20674; + Zephyr: 20675; + }; + dialog: { + youDoNotHaveEnoughGoldForThat: 3362; + }; - namespace text { - const Ladder: 719; - const RepairCost: 3330; - const SellValue: 3331; - const IdentifyCost: 3332; - const ItemCannotBeTradedHere: 3333; - const TradeRepair: 3334; - const Buy: 3335; - const Sell: 3336; - const Heal: 3337; - const Repair: 3338; - const NextPage: 3339; - const PreviousPage: 3340; - const Transmute: 3341; - const YourGold: 3342; - const WhichItemShouldBeImbued: 3343; - const Yes: 3344; - const No: 3345; - const Gold2: 3346; - const Sell2: 3347; - const Buy2: 3358; - const Hire: 3349; - const ToStrength: 3473; - const ToDexterity: 3474; - const Defense: 3481; - const Identify: 3350; - const Repair2: 3351; - const EnhancedDefense: 3520; - const RealmGoingDownInXMinutes: 3651; - const Strength: 4060; - const Dexterity: 4062; - const Vitality: 4066; - const Energy: 4069; - const DoNotMeetLevelReqForThisGame: 5162; - const CdKeyDisabled: 5199; - const CdKeyInUseBy: 5200; - const OnlyOneInstanceAtATime: 5201; - const CdKeyIntendedForAnotherProduct: 5202; - const InvalidPassword: 5207; - const AccountDoesNotExist: 5208; - const AccountIsCorrupted: 5209; - const AccountMustBeAtLeast: 5217; - const AccountCantBeMoreThan: 5218; - const PasswordMustBeAtLeast: 5219; - const PasswordCantBeMoreThan: 5220; - const LoginError: 5224; - const UsernameMustBeAtLeast: 5231; - const UsernameIncludedIllegalChars: 5232; - const UsernameIncludedDisallowedwords: 5233; - const AccountNameAlreadyExist: 5239; - const UnableToCreateAccount: 5249; - const CannotCreateGamesDeadHCChar: 5304; - const Disconnected: 5347; - const UnableToIndentifyVersion: 5245; - const BattlenetNotResponding: 5353; - const BattlenetNotResponding2: 5354; - const HcCannotPlayWithSc: 5361; - const ScCannotPlayWithHc: 5362; - const CannotPlayInHellClassic: 5363; - const CannotPlayInNightmareClassic: 5364; - const EnhancedDamage: 10038; - const ClassicCannotPlayWithXpac: 10101; - const XpacCannotPlayWithClassic: 10102; - const LoDKeyDisabled: 10913; - const LodKeyInUseBy: 10914; - const LoDKeyIntendedForAnotherProduct: 10915; - const NonLadderCannotPlayWithLadder: 10929; - const LadderCannotPlayWithNonLadder: 10930; - const YourPositionInLineIs: 11026; - const Gateway: 11049; - const Ghostly: 11084; - const Fanatic: 11085; - const Possessed: 11086; - const Berserker: 11087; - const ExpiresIn: 11133; - const CdKeyDisabledFromRealm: 11161; - const CannotPlayInHellXpac: 21793; - const CannotPlayInNightmareXpac: 21794; - } + text: { + Ladder: 719; + RepairCost: 3330; + SellValue: 3331; + IdentifyCost: 3332; + ItemCannotBeTradedHere: 3333; + TradeRepair: 3334; + Buy: 3335; + Sell: 3336; + Heal: 3337; + Repair: 3338; + NextPage: 3339; + PreviousPage: 3340; + Transmute: 3341; + YourGold: 3342; + WhichItemShouldBeImbued: 3343; + Yes: 3344; + No: 3345; + Gold2: 3346; + Sell2: 3347; + Buy2: 3358; + Hire: 3349; + ToStrength: 3473; + ToDexterity: 3474; + Defense: 3481; + Identify: 3350; + Repair2: 3351; + EnhancedDefense: 3520; + RealmGoingDownInXMinutes: 3651; + Strength: 4060; + Dexterity: 4062; + Vitality: 4066; + Energy: 4069; + DoNotMeetLevelReqForThisGame: 5162; + CdKeyDisabled: 5199; + CdKeyInUseBy: 5200; + OnlyOneInstanceAtATime: 5201; + CdKeyIntendedForAnotherProduct: 5202; + InvalidPassword: 5207; + AccountDoesNotExist: 5208; + AccountIsCorrupted: 5209; + AccountMustBeAtLeast: 5217; + AccountCantBeMoreThan: 5218; + PasswordMustBeAtLeast: 5219; + PasswordCantBeMoreThan: 5220; + LoginError: 5224; + UsernameMustBeAtLeast: 5231; + UsernameIncludedIllegalChars: 5232; + UsernameIncludedDisallowedwords: 5233; + AccountNameAlreadyExist: 5239; + UnableToCreateAccount: 5249; + CannotCreateGamesDeadHCChar: 5304; + Disconnected: 5347; + UnableToIndentifyVersion: 5245; + BattlenetNotResponding: 5353; + BattlenetNotResponding2: 5354; + HcCannotPlayWithSc: 5361; + ScCannotPlayWithHc: 5362; + CannotPlayInHellClassic: 5363; + CannotPlayInNightmareClassic: 5364; + EnhancedDamage: 10038; + ClassicCannotPlayWithXpac: 10101; + XpacCannotPlayWithClassic: 10102; + LoDKeyDisabled: 10913; + LodKeyInUseBy: 10914; + LoDKeyIntendedForAnotherProduct: 10915; + NonLadderCannotPlayWithLadder: 10929; + LadderCannotPlayWithNonLadder: 10930; + YourPositionInLineIs: 11026; + Gateway: 11049; + Ghostly: 11084; + Fanatic: 11085; + Possessed: 11086; + Berserker: 11087; + ExpiresIn: 11133; + CdKeyDisabledFromRealm: 11161; + CannotPlayInHellXpac: 21793; + CannotPlayInNightmareXpac: 21794; + }; - namespace areas { + areas: { // Act 1 - const RogueEncampment: 5055; - const BloodMoor: 5054; - const ColdPlains: 5053; - const StonyField: 5052; - const DarkWood: 5051; - const BlackMarsh: 5050; - const TamoeHighland: 5049; - const DenofEvil: 5048; - const CaveLvl1: 5047; - const UndergroundPassageLvl1: 5046; - const HoleLvl1: 5045; - const PitLvl1: 5044; - const CaveLvl2: 5043; - const UndergroundPassageLvl2: 5042; - const HoleLvl2: 5041; - const PitLvl2: 5040; - const BurialGrounds: 5039; - const Crypt: 5038; - const Mausoleum: 5037; - const ForgottenTower: 5036; - const TowerCellarLvl1: 5035; - const TowerCellarLvl2: 5034; - const TowerCellarLvl3: 5033; - const TowerCellarLvl4: 5032; - const TowerCellarLvl5: 5031; - const MonasteryGate: 5030; - const OuterCloister: 5029; - const Barracks: 5038; - const JailLvl1: 5027; - const JailLvl2: 5026; - const JailLvl3: 5025; - const InnerCloister: 5024; - const Cathedral: 5023; - const CatacombsLvl1: 5022; - const CatacombsLvl2: 5021; - const CatacombsLvl3: 5020; - const CatacombsLvl4: 5019; - const Tristram: 5018; - const MooMooFarm: 788; + RogueEncampment: 5055; + BloodMoor: 5054; + ColdPlains: 5053; + StonyField: 5052; + DarkWood: 5051; + BlackMarsh: 5050; + TamoeHighland: 5049; + DenofEvil: 5048; + CaveLvl1: 5047; + UndergroundPassageLvl1: 5046; + HoleLvl1: 5045; + PitLvl1: 5044; + CaveLvl2: 5043; + UndergroundPassageLvl2: 5042; + HoleLvl2: 5041; + PitLvl2: 5040; + BurialGrounds: 5039; + Crypt: 5038; + Mausoleum: 5037; + ForgottenTower: 5036; + TowerCellarLvl1: 5035; + TowerCellarLvl2: 5034; + TowerCellarLvl3: 5033; + TowerCellarLvl4: 5032; + TowerCellarLvl5: 5031; + MonasteryGate: 5030; + OuterCloister: 5029; + Barracks: 5038; + JailLvl1: 5027; + JailLvl2: 5026; + JailLvl3: 5025; + InnerCloister: 5024; + Cathedral: 5023; + CatacombsLvl1: 5022; + CatacombsLvl2: 5021; + CatacombsLvl3: 5020; + CatacombsLvl4: 5019; + Tristram: 5018; + MooMooFarm: 788; // Act 2 - const LutGholein: 852; - const RockyWaste: 851; - const DryHills: 850; - const FarOasis: 849; - const LostCity: 848; - const ValleyofSnakes: 847; - const CanyonofMagic: 846; - const A2SewersLvl1: 845; - const A2SewersLvl2: 844; - const A2SewersLvl3: 843; - const HaremLvl1: 842; - const HaremLvl2: 841; - const PalaceCellarLvl1: 840; - const PalaceCellarLvl2: 839; - const PalaceCellarLvl3: 838; - const StonyTombLvl1: 837; - const HallsoftheDeadLvl1: 836; - const HallsoftheDeadLvl2: 835; - const ClawViperTempleLvl1: 834; - const StonyTombLvl2: 833; - const HallsoftheDeadLvl3: 832; - const ClawViperTempleLvl2: 831; - const MaggotLairLvl1: 830; - const MaggotLairLvl2: 829; - const MaggotLairLvl3: 828; - const AncientTunnels: 827; - const TalRashasTomb1: 826; - const TalRashasTomb2: 826; - const TalRashasTomb3: 826; - const TalRashasTomb4: 826; - const TalRashasTomb5: 826; - const TalRashasTomb6: 826; - const TalRashasTomb7: 826; - const DurielsLair: 825; - const ArcaneSanctuary: 824; + LutGholein: 852; + RockyWaste: 851; + DryHills: 850; + FarOasis: 849; + LostCity: 848; + ValleyofSnakes: 847; + CanyonofMagic: 846; + A2SewersLvl1: 845; + A2SewersLvl2: 844; + A2SewersLvl3: 843; + HaremLvl1: 842; + HaremLvl2: 841; + PalaceCellarLvl1: 840; + PalaceCellarLvl2: 839; + PalaceCellarLvl3: 838; + StonyTombLvl1: 837; + HallsoftheDeadLvl1: 836; + HallsoftheDeadLvl2: 835; + ClawViperTempleLvl1: 834; + StonyTombLvl2: 833; + HallsoftheDeadLvl3: 832; + ClawViperTempleLvl2: 831; + MaggotLairLvl1: 830; + MaggotLairLvl2: 829; + MaggotLairLvl3: 828; + AncientTunnels: 827; + TalRashasTomb1: 826; + TalRashasTomb2: 826; + TalRashasTomb3: 826; + TalRashasTomb4: 826; + TalRashasTomb5: 826; + TalRashasTomb6: 826; + TalRashasTomb7: 826; + DurielsLair: 825; + ArcaneSanctuary: 824; // Act 3 - const KurastDocktown: 820; - const SpiderForest: 819; - const GreatMarsh: 818; - const FlayerJungle: 817; - const LowerKurast: 816; - const KurastBazaar: 815; - const UpperKurast: 814; - const KurastCauseway: 813; - const Travincal: 812; - const SpiderCave: 810; - const SpiderCavern: 811; - const SwampyPitLvl1: 809; - const SwampyPitLvl2: 808; - const FlayerDungeonLvl1: 806; - const FlayerDungeonLvl2: 805; - const SwampyPitLvl3: 807; - const FlayerDungeonLvl3: 804; - const A3SewersLvl1: 845; - const A3SewersLvl2: 844; - const RuinedTemple: 803; - const DisusedFane: 802; - const ForgottenReliquary: 801; - const ForgottenTemple: 800; - const RuinedFane: 799; - const DisusedReliquary: 798; - const DuranceofHateLvl1: 797; - const DuranceofHateLvl2: 796; - const DuranceofHateLvl3: 795; + KurastDocktown: 820; + SpiderForest: 819; + GreatMarsh: 818; + FlayerJungle: 817; + LowerKurast: 816; + KurastBazaar: 815; + UpperKurast: 814; + KurastCauseway: 813; + Travincal: 812; + SpiderCave: 810; + SpiderCavern: 811; + SwampyPitLvl1: 809; + SwampyPitLvl2: 808; + FlayerDungeonLvl1: 806; + FlayerDungeonLvl2: 805; + SwampyPitLvl3: 807; + FlayerDungeonLvl3: 804; + A3SewersLvl1: 845; + A3SewersLvl2: 844; + RuinedTemple: 803; + DisusedFane: 802; + ForgottenReliquary: 801; + ForgottenTemple: 800; + RuinedFane: 799; + DisusedReliquary: 798; + DuranceofHateLvl1: 797; + DuranceofHateLvl2: 796; + DuranceofHateLvl3: 795; // Act 4 - const PandemoniumFortress: 790; - const OuterSteppes: 792; - const PlainsofDespair: 793; - const CityoftheDamned: 794; - const RiverofFlame: 791; - const ChaosSanctuary: 789; + PandemoniumFortress: 790; + OuterSteppes: 792; + PlainsofDespair: 793; + CityoftheDamned: 794; + RiverofFlame: 791; + ChaosSanctuary: 789; // Act 5 - const Harrogath: 22646; - const BloodyFoothills: 22647; - const FrigidHighlands: 22648; - const ArreatPlateau: 22649; - const CrystalizedPassage: 22650; - const FrozenRiver: 22651; - const GlacialTrail: 22652; - const DrifterCavern: 22653; - const FrozenTundra: 22654; - const AncientsWay: 22655; - const IcyCellar: 22656; - const ArreatSummit: 22657; - const NihlathaksTemple: 22658; - const HallsofAnguish: 22659; - const HallsofPain: 22660; - const HallsofVaught: 22662; - const Abaddon: 21865; - const PitofAcheron: 21866; - const InfernalPit: 21867; - const WorldstoneLvl1: 22663; - const WorldstoneLvl2: 22664; - const WorldstoneLvl3: 22665; - const ThroneofDestruction: 22667; - const WorldstoneChamber: 22666; + Harrogath: 22646; + BloodyFoothills: 22647; + FrigidHighlands: 22648; + ArreatPlateau: 22649; + CrystalizedPassage: 22650; + FrozenRiver: 22651; + GlacialTrail: 22652; + DrifterCavern: 22653; + FrozenTundra: 22654; + AncientsWay: 22655; + IcyCellar: 22656; + ArreatSummit: 22657; + NihlathaksTemple: 22658; + HallsofAnguish: 22659; + HallsofPain: 22660; + HallsofVaught: 22662; + Abaddon: 21865; + PitofAcheron: 21866; + InfernalPit: 21867; + WorldstoneLvl1: 22663; + WorldstoneLvl2: 22664; + WorldstoneLvl3: 22665; + ThroneofDestruction: 22667; + WorldstoneChamber: 22666; // Ubers - const MatronsDen: 5389; - const ForgottenSands: 5389; - const FurnaceofPain: 5389; - const UberTristram: 5018; - } - } + MatronsDen: 5389; + ForgottenSands: 5389; + FurnaceofPain: 5389; + UberTristram: 5018; + }; + }; - export namespace game { - namespace profiletype { - const SinglePlayer: 1; - const Battlenet: 2; - const OpenBattlenet: 3; - const TcpIpHost: 4; - const TcpIpJoin: 5; - } + game: { + profiletype: { + SinglePlayer: 1; + Battlenet: 2; + OpenBattlenet: 3; + TcpIpHost: 4; + TcpIpJoin: 5; + }; - namespace controls { - const Disabled: 4; - } + controls: { + Disabled: 4; + }; - namespace gametype { - const Classic: 0; - const Expansion: 1; - } + gametype: { + Classic: 0; + Expansion: 1; + }; // out of game locations - namespace locations { - const PreSplash: 0; - const Lobby: 1; - const WaitingInLine: 2; - const LobbyChat: 3; - const CreateGame: 4; - const JoinGame: 5; - const Ladder: 6; - const ChannelList: 7; - const MainMenu: 8; - const Login: 9; - const LoginError: 10; - const LoginUnableToConnect: 11; - const CharSelect: 12; - const RealmDown: 13; - const Disconnected: 14; - const NewCharSelected: 15; - const CharSelectPleaseWait: 16; - const LobbyLostConnection: 17; - const SplashScreen: 18; - const CdKeyInUse: 19; - const SelectDifficultySP: 20; - const MainMenuConnecting: 21; - const InvalidCdKey: 22; - const CharSelectConnecting: 23; - const ServerDown: 24; - const LobbyPleaseWait: 25; - const GameNameExists: 26; - const GatewaySelect: 27; - const GameDoesNotExist: 28; - const CharacterCreate: 29; - const OkCenteredErrorPopUp: 30; - const TermsOfUse: 31; - const CreateNewAccount: 32; - const PleaseRead: 33; - const RegisterEmail: 34; - const Credits: 35; - const Cinematics: 36; - const CharChangeRealm: 37; - const GameIsFull: 38; - const OtherMultiplayer: 39; - const TcpIp: 40; - const TcpIpEnterIp: 41; - const CharSelectNoChars: 42; - const CharSelectChangeRealm: 43; - const TcpIpUnableToConnect: 44; - } - } + locations: { + PreSplash: 0; + Lobby: 1; + WaitingInLine: 2; + LobbyChat: 3; + CreateGame: 4; + JoinGame: 5; + Ladder: 6; + ChannelList: 7; + MainMenu: 8; + Login: 9; + LoginError: 10; + LoginUnableToConnect: 11; + CharSelect: 12; + RealmDown: 13; + Disconnected: 14; + NewCharSelected: 15; + CharSelectPleaseWait: 16; + LobbyLostConnection: 17; + SplashScreen: 18; + CdKeyInUse: 19; + SelectDifficultySP: 20; + MainMenuConnecting: 21; + InvalidCdKey: 22; + CharSelectConnecting: 23; + ServerDown: 24; + LobbyPleaseWait: 25; + GameNameExists: 26; + GatewaySelect: 27; + GameDoesNotExist: 28; + CharacterCreate: 29; + OkCenteredErrorPopUp: 30; + TermsOfUse: 31; + CreateNewAccount: 32; + PleaseRead: 33; + RegisterEmail: 34; + Credits: 35; + Cinematics: 36; + CharChangeRealm: 37; + GameIsFull: 38; + OtherMultiplayer: 39; + TcpIp: 40; + TcpIpEnterIp: 41; + CharSelectNoChars: 42; + CharSelectChangeRealm: 43; + TcpIpUnableToConnect: 44; + }; + }; - export namespace colors { - const White: "ÿc0"; - const Red: "ÿc1"; - const NeonGreen: "ÿc2"; - const Blue: "ÿc3"; - const DarkGold: "ÿc4"; - const Gray: "ÿc5"; - const Black: "ÿc6"; - const LightGold: "ÿc7"; - const Orange: "ÿc8"; - const Yellow: "ÿc9"; - const DarkGreen: "ÿconst c:"; - const Purple: "ÿc;"; - const Green: "ÿc<"; - namespace D2Bot { - const Black: 0; - const Blue: 4; - const Green: 5; - const Gold: 6; - const DarkGold: 7; - const Orange: 8; - const Red: 9; - const Gray: 10; - } - } + colors: { + White: "ÿc0"; + Red: "ÿc1"; + NeonGreen: "ÿc2"; + Blue: "ÿc3"; + DarkGold: "ÿc4"; + Gray: "ÿc5"; + Black: "ÿc6"; + LightGold: "ÿc7"; + Orange: "ÿc8"; + Yellow: "ÿc9"; + DarkGreen: "ÿc:"; + Purple: "ÿc;"; + Green: "ÿc<"; + D2Bot: { + Black: 0; + Blue: 4; + Green: 5; + Gold: 6; + DarkGold: 7; + Orange: 8; + Red: 9; + Gray: 10; + }; + }; - export namespace keys { - const Backspace: 8; - const Tab: 9; - const Enter: 13; - const Shift: 16; - const Ctrl: 17; - const Alt: 18; - const PauseBreak: 19; - const CapsLock: 20; - const Escape: 27; - const Spacebar: 32; - const PageUp: 33; - const PageDown: 34; - const End: 35; - const Home: 36; - const LeftArrow: 37; - const UpArrow: 38; - const RightArrow: 39; - const DownArrow: 40; - const Insert: 45; - const Delete: 46; - const Zero: 48; - const One: 49; - const Two: 50; - const Three: 51; - const Four: 52; - const Five: 53; - const Six: 54; - const Seven: 55; - const Eight: 56; - const Nine: 57; - const LeftWindowKey: 91; - const RightWindowKey: 92; - const SelectKey: 93; - const Numpad0: 96; - const Numpad1: 97; - const Numpad2: 98; - const Numpad3: 99; - const Numpad4: 100; - const Numpad5: 101; - const Numpad6: 102; - const Numpad7: 103; - const Numpad8: 104; - const Numpad9: 105; - const NumpadStar: 106; - const NumpadPlus: 107; - const NumpadDash: 109; - const NumpadDecimal: 110; - const NumpadSlash: 111; - const F1: 112; - const F2: 113; - const F3: 114; - const F4: 115; - const F5: 116; - const F6: 117; - const F7: 118; - const F8: 119; - const F9: 120; - const F10: 121; - const F11: 122; - const F12: 123; - const NumLock: 144; - const ScrollLock: 145; - const SemiColon: 186; - const EqualSign: 187; - const Comma: 188; - const Dash: 189; - const Period: 190; - const ForwardSlash: 191; - const GraveAccent: 192; - const OpenBracket: 219; - const BackSlash: 220; - const CloseBracket: 221; - const SingleQuote: 222; - namespace code { - const Backspace: 0x08; - const Tab: 0x09; - const Clear: 0x0c; - const Enter: 0x0d; - const Shift: 0x10; - const Ctrl: 0x11; - const Alt: 0x12; - const PauseBreak: 0x13; - const CapsLock: 0x14; - const Esc: 0x1b; - const Space: 0x20; - const PageUp: 0x21; - const PageDown: 0x22; - const End: 0x23; - const Home: 0x24; - const LeftArrow: 0x25; - const UpArrow: 0x26; - const RightArrow: 0x27; - const DownArrow: 0x28; - const Select: 0x29; - const Print: 0x2a; - const PrintScreen: 0x2c; - const Insert: 0x2d; - const Delete: 0x2e; - } - } + keys: { + Backspace: 8; + Tab: 9; + Enter: 13; + Shift: 16; + Ctrl: 17; + Alt: 18; + PauseBreak: 19; + CapsLock: 20; + Escape: 27; + Spacebar: 32; + PageUp: 33; + PageDown: 34; + End: 35; + Home: 36; + LeftArrow: 37; + UpArrow: 38; + RightArrow: 39; + DownArrow: 40; + Insert: 45; + Delete: 46; + Zero: 48; + One: 49; + Two: 50; + Three: 51; + Four: 52; + Five: 53; + Six: 54; + Seven: 55; + Eight: 56; + Nine: 57; + LeftWindowKey: 91; + RightWindowKey: 92; + SelectKey: 93; + Numpad0: 96; + Numpad1: 97; + Numpad2: 98; + Numpad3: 99; + Numpad4: 100; + Numpad5: 101; + Numpad6: 102; + Numpad7: 103; + Numpad8: 104; + Numpad9: 105; + NumpadStar: 106; + NumpadPlus: 107; + NumpadDash: 109; + NumpadDecimal: 110; + NumpadSlash: 111; + F1: 112; + F2: 113; + F3: 114; + F4: 115; + F5: 116; + F6: 117; + F7: 118; + F8: 119; + F9: 120; + F10: 121; + F11: 122; + F12: 123; + NumLock: 144; + ScrollLock: 145; + SemiColon: 186; + EqualSign: 187; + Comma: 188; + Dash: 189; + Period: 190; + ForwardSlash: 191; + GraveAccent: 192; + OpenBracket: 219; + BackSlash: 220; + CloseBracket: 221; + SingleQuote: 222; + code: { + Backspace: 0x08; + Tab: 0x09; + Clear: 0x0c; + Enter: 0x0d; + Shift: 0x10; + Ctrl: 0x11; + Alt: 0x12; + PauseBreak: 0x13; + CapsLock: 0x14; + Esc: 0x1b; + Space: 0x20; + PageUp: 0x21; + PageDown: 0x22; + End: 0x23; + Home: 0x24; + LeftArrow: 0x25; + UpArrow: 0x26; + RightArrow: 0x27; + DownArrow: 0x28; + Select: 0x29; + Print: 0x2a; + PrintScreen: 0x2c; + Insert: 0x2d; + Delete: 0x2e; + }; + }; - export namespace controls { - const TextBox: 1; - const Image1: 2; - const Image2: 3; - const LabelBox: 4; - const ScrollBar: 5; - const Button: 6; - const List: 7; - const Timer: 8; - const Smack: 9; - const ProgressBar: 10; - const Popup: 11; - const AccountList: 12; - } + controls: { + TextBox: 1; + Image1: 2; + Image2: 3; + LabelBox: 4; + ScrollBar: 5; + Button: 6; + List: 7; + Timer: 8; + Smack: 9; + ProgressBar: 10; + Popup: 11; + AccountList: 12; + }; - export namespace packets { - namespace send { - const WalkToLocation: 0x01; - const WalkToEntity: 0x02; - const RunToLocation: 0x03; - const RunToEntity: 0x04; - const LeftSkillOnLocation: 0x05; - const LeftSkillOnEntity: 0x06; - const LeftSkillOnEntityEx: 0x07; - const LeftSkillOnLocationEx: 0x08; - const LeftSkillOnEntityEx2: 0x09; - const LeftSkillOnEntityEx3: 0x0a; - const RightSkillOnLocation: 0x0c; - const RightSkillOnEntity: 0x0d; - const RightSkillOnEntityEx: 0x0e; - const RightSkillOnLocationEx: 0x0f; - const RightSkillOnEntityEx2: 0x10; - const RightSkillOnEntityEx3: 0x11; - const SetInfernoState: 0x12; - const InteractWithEntity: 0x13; - const OverheadMessage: 0x14; - const Chat: 0x15; - const PickupItem: 0x16; - const DropItem: 0x17; - const ItemToBuffer: 0x18; - const PickupBufferItem: 0x19; - const ItemToBody: 0x1a; - const Swap2HandedItem: 0x1b; - const PickupBodyItem: 0x1c; - const SwitchBodyItem: 0x1d; - const Switch1HandWith2Hand: 0x1e; - const SwitchInventoryItem: 0x1f; - const UseItem: 0x20; - const StackItem: 0x21; - const RemoveStackItem: 0x22; - const ItemToBelt: 0x23; - const RemoveBeltItem: 0x24; - const SwitchBeltItem: 0x25; - const UseBeltItem: 0x26; - const IndentifyItem: 0x27; - const InsertSocketItem: 0x28; - const ScrollToMe: 0x29; - const ItemToCube: 0x2a; - const NPCInit: 0x2f; - const NPCCancel: 0x30; - const QuestMessage: 0x31; - const NPCBuy: 0x32; - const NPCSell: 0x33; - const NPCIndentifyItems: 0x34; - const Repair: 0x35; - const HireMerc: 0x36; - const IndentifyGamble: 0x37; - const EntityAction: 0x38; - const AddStat: 0x3a; - const AddSkill: 0x3b; - const SelectSkill: 0x3c; - const ActivateItem: 0x3e; - const CharacterPhrase: 0x3f; - const UpdateQuests: 0x40; - const Resurrect: 0x41; - const StaffInOrifice: 0x44; - const MercInteract: 0x46; - const MercMove: 0x47; - const BusyStateOff: 0x48; - const Waypoint: 0x49; - const RequestEntityUpdate: 0x4b; - const Transmorgify: 0x4c; - const PlayNPCMessage: 0x4d; - const ClickButton: 0x4f; - const DropGold: 0x50; - const BindHotkey: 0x51; - const StaminaOn: 0x53; - const StaminaOff: 0x54; - const QuestCompleted: 0x58; - const MakeEntityMove: 0x59; - const SquelchHostile: 0x5d; - const Party: 0x5e; - const UpdatePlayerPos: 0x5f; - const SwapWeapon: 0x60; - const MercItem: 0x61; - const MercRessurect: 0x62; - const LeaveGame: 0x69; - } - namespace recv { - const GameExit: 0x06; - const MapReveal: 0x07; - const MapHide: 0x08; - const ReassignPlayer: 0x15; - const SetSkill: 0x23; - const Chat: 0x26; - const UniqueEvents: 0x89; - const WeaponSwitch: 0x97; - } - } + packets: { + send: { + WalkToLocation: 0x01; + WalkToEntity: 0x02; + RunToLocation: 0x03; + RunToEntity: 0x04; + LeftSkillOnLocation: 0x05; + LeftSkillOnEntity: 0x06; + LeftSkillOnEntityEx: 0x07; + LeftSkillOnLocationEx: 0x08; + LeftSkillOnEntityEx2: 0x09; + LeftSkillOnEntityEx3: 0x0a; + RightSkillOnLocation: 0x0c; + RightSkillOnEntity: 0x0d; + RightSkillOnEntityEx: 0x0e; + RightSkillOnLocationEx: 0x0f; + RightSkillOnEntityEx2: 0x10; + RightSkillOnEntityEx3: 0x11; + SetInfernoState: 0x12; + InteractWithEntity: 0x13; + OverheadMessage: 0x14; + Chat: 0x15; + PickupItem: 0x16; + DropItem: 0x17; + ItemToBuffer: 0x18; + PickupBufferItem: 0x19; + ItemToBody: 0x1a; + Swap2HandedItem: 0x1b; + PickupBodyItem: 0x1c; + SwitchBodyItem: 0x1d; + Switch1HandWith2Hand: 0x1e; + SwitchInventoryItem: 0x1f; + UseItem: 0x20; + StackItem: 0x21; + RemoveStackItem: 0x22; + ItemToBelt: 0x23; + RemoveBeltItem: 0x24; + SwitchBeltItem: 0x25; + UseBeltItem: 0x26; + IndentifyItem: 0x27; + InsertSocketItem: 0x28; + ScrollToMe: 0x29; + ItemToCube: 0x2a; + NPCInit: 0x2f; + NPCCancel: 0x30; + QuestMessage: 0x31; + NPCBuy: 0x32; + NPCSell: 0x33; + NPCIndentifyItems: 0x34; + Repair: 0x35; + HireMerc: 0x36; + IndentifyGamble: 0x37; + EntityAction: 0x38; + AddStat: 0x3a; + AddSkill: 0x3b; + SelectSkill: 0x3c; + ActivateItem: 0x3e; + CharacterPhrase: 0x3f; + UpdateQuests: 0x40; + Resurrect: 0x41; + StaffInOrifice: 0x44; + MercInteract: 0x46; + MercMove: 0x47; + BusyStateOff: 0x48; + Waypoint: 0x49; + RequestEntityUpdate: 0x4b; + Transmorgify: 0x4c; + PlayNPCMessage: 0x4d; + ClickButton: 0x4f; + DropGold: 0x50; + BindHotkey: 0x51; + StaminaOn: 0x53; + StaminaOff: 0x54; + QuestCompleted: 0x58; + MakeEntityMove: 0x59; + SquelchHostile: 0x5d; + Party: 0x5e; + UpdatePlayerPos: 0x5f; + SwapWeapon: 0x60; + MercItem: 0x61; + MercRessurect: 0x62; + LeaveGame: 0x69; + }; + recv: { + GameExit: 0x06; + MapReveal: 0x07; + MapHide: 0x08; + ReassignPlayer: 0x15; + SetSkill: 0x23; + Chat: 0x26; + UniqueEvents: 0x89; + WeaponSwitch: 0x97; + }; + }; } + const sdk: SDK; } export {}; From cd54b860a95eee1756aa32e3817ee153a1cc2842 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:31:36 -0500 Subject: [PATCH 649/758] Refactor Misc namespace to interface in type definitions - Changed the Misc namespace to an interface and updated global declarations to improve type safety and clarity. --- d2bs/kolbot/sdk/types/Misc.d.ts | 73 +++++++++++++++++---------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index 634035f59..237b935ff 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -1,13 +1,14 @@ export {}; + declare global { - namespace Misc { - const _diabloSpawned: boolean; - const screenshotErrors: boolean; - const errorConsolePrint: boolean; - const useItemLog: boolean; - let shrineStates: number[] | null; - const _shrinerIgnore: Set; - const lastShrine: { + interface Misc { + _diabloSpawned: boolean; + screenshotErrors: boolean; + errorConsolePrint: boolean; + useItemLog: boolean; + shrineStates: number[] | null; + _shrinerIgnore: Set; + lastShrine: { tick: number; duration: number; type: number; @@ -17,37 +18,39 @@ declare global { isMyCurrentState(): boolean; }; - function click(button: number, shift: number, x?: number | Unit, y?: number): boolean; - function inMyParty(name: string): boolean; - function findPlayer(name: string): Party | false; - function getPlayerUnit(name: string): Player | false; - function getPlayerAct(player: Party | string): number | false; - function getNearbyPlayerCount(): number; - function getPlayerCount(): number; - function getPartyCount(): number; - function getPartyMembers(): Party[]; - function checkPartyLevel(levelCheck?: number, exclude?: string | string[]): boolean; - function getPlayerArea(player: Party | string): number | false; - function autoLeaderDetect(givenSettings?: { + click(button: number, shift: number, x?: number | Unit, y?: number): boolean; + inMyParty(name: string): boolean; + findPlayer(name: string): Party | false; + getPlayerUnit(name: string): Player | false; + getPlayerAct(player: Party | string): number | false; + getNearbyPlayerCount(): number; + getPlayerCount(): number; + getPartyCount(): number; + getPartyMembers(): Party[]; + checkPartyLevel(levelCheck?: number, exclude?: string | string[]): boolean; + getPlayerArea(player: Party | string): number | false; + autoLeaderDetect(givenSettings?: { destination?: number | number[]; quitIf?: (area: number) => boolean; timeout?: number; }): string | false; - function openChest(unit: Unit | number): boolean; - function openChestsInArea(area?: number, chestIds?: number[]): boolean; - function openChests(range?: number): boolean; - function shriner(ignore: number[]): boolean; - function scanShrines(range: number, ignore: number[]): boolean; - function getShrine(unit: ObjectUnit): boolean; - function getShrinesInArea(area: number, type: number, use: boolean): boolean; + openChest(unit: Unit | number): boolean; + openChestsInArea(area?: number, chestIds?: number[]): boolean; + openChests(range?: number): boolean; + shriner(ignore: number[]): boolean; + scanShrines(range: number, ignore: number[]): boolean; + getShrine(unit: ObjectUnit): boolean; + getShrinesInArea(area: number, type: number, use: boolean): boolean; /** @deprecated */ - function townCheck(): boolean; - function spy(name: string): boolean; - function errorReport(error: Error | string, script?: string): void; - function debugLog(msg: string): void; - function useMenu(id: number): boolean; - function poll(check: () => T, timeout?: number, sleep?: number): T | false; - function getUIFlags(excluded?: number[]): number[] | null; - function getQuestStates(questId: number): number[]; + townCheck(): boolean; + spy(name: string): boolean; + errorReport(error: Error | string, script?: string): void; + debugLog(msg: string): void; + useMenu(id: number): boolean; + poll(check: () => T, timeout?: number, sleep?: number): T | false; + getUIFlags(excluded?: number[]): number[] | null; + getQuestStates(questId: number): number[]; } + + const Misc: Misc; } From 5b6ec1fe05c2f8569c5a1ab8fdee37a0a146403e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:34:36 -0500 Subject: [PATCH 650/758] Refactor event system to use AsyncEvents module + add new Events module - Introduced AsyncEvents.js as a new event system and updated references in TextHooks, Messaging, Socket, and Team modules to use it instead of the previous Events implementation. - The Events.js module was refactored for improved event handling, but AsyncEvents is now the default for asynchronous event management across the codebase. --- .../libs/manualplay/hooks/TextHooks.d.ts | 46 +++--- .../kolbot/libs/manualplay/hooks/TextHooks.js | 2 +- d2bs/kolbot/libs/modules/AsyncEvents.js | 51 +++++++ d2bs/kolbot/libs/modules/Events.js | 142 ++++++++++++------ d2bs/kolbot/libs/modules/Messaging.js | 2 +- d2bs/kolbot/libs/modules/Socket.js | 2 +- d2bs/kolbot/libs/modules/Team.js | 2 +- 7 files changed, 176 insertions(+), 71 deletions(-) create mode 100644 d2bs/kolbot/libs/modules/AsyncEvents.js diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.d.ts b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.d.ts index 5b58a0377..99d37ebea 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.d.ts +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.d.ts @@ -1,5 +1,3 @@ -import Events from "libs/modules/Events"; - /** * An entry in the hook arrays */ @@ -12,8 +10,8 @@ export interface HookEntry { declare global { /** - * The TextHooks module - */ + * The TextHooks module + */ const TextHooks: { events: Events; enabled: boolean; @@ -32,39 +30,37 @@ declare global { hooks: HookEntry[]; /** - * Check and update the hooks - */ + * Check and update the hooks + */ check(): void; /** - * Update a hook's text or add it if it doesn't exist - * @param name - The hook identifier - * @param hookArr - The array containing the hooks - * @param text - The text to update - */ + * Update a hook's text or add it if it doesn't exist + * @param name - The hook identifier + * @param hookArr - The array containing the hooks + * @param text - The text to update + */ updateHook(name: string, hookArr: HookEntry[], text: string): void; /** - * Add a hook to the specified array - * @param name - The hook identifier - * @param hookArr - The array to add the hook to - * @returns Whether the hook was added - */ + * Add a hook to the specified array + * @param name - The hook identifier + * @param hookArr - The array to add the hook to + * @returns Whether the hook was added + */ add(name: string, hookArr: HookEntry[]): boolean; /** - * Find a hook in the specified array - * @param name - The hook identifier - * @param hookArr - The array to search in - * @returns The found hook entry or false if not found - */ + * Find a hook in the specified array + * @param name - The hook identifier + * @param hookArr - The array to search in + * @returns The found hook entry or false if not found + */ getHook(name: string, hookArr: HookEntry[]): HookEntry | boolean; /** - * Remove all hooks - */ + * Remove all hooks + */ flush(): void; }; } - -export {}; \ No newline at end of file diff --git a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js index 302dccbe6..b9ec728a2 100644 --- a/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js +++ b/d2bs/kolbot/libs/manualplay/hooks/TextHooks.js @@ -6,7 +6,7 @@ */ const TextHooks = (function () { - const Events = new (require("../../modules/Events")); + const Events = new (require("../../modules/AsyncEvents")); const HookFactory = require("../modules/HookFactory"); const Y_POS_MODIFIER = 16 diff --git a/d2bs/kolbot/libs/modules/AsyncEvents.js b/d2bs/kolbot/libs/modules/AsyncEvents.js new file mode 100644 index 000000000..5d824f1ff --- /dev/null +++ b/d2bs/kolbot/libs/modules/AsyncEvents.js @@ -0,0 +1,51 @@ +/** + * @author Jaenster + * @description A node like event system + * + */ + +(function (module, require) { + // eslint-disable-next-line no-unused-vars + const Events = module.exports = function () { + const Worker = require("Worker"), self = this; + + this.hooks = []; + + function Hook(name, callback) { + this.name = name; + this.callback = callback; + this.id = self.hooks.push(this) - 1; + this.__callback = callback; // used for once + } + + this.on = function (name, callback) { + if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; + return new Hook(name, callback); + }; + + this.trigger = function (name, ...args) { + return self.hooks.forEach(hook => !hook.name || hook.name === name && Worker.push(function () { + hook.callback.apply(hook, args); + })); + }; + + this.emit = this.trigger; // Alias for trigger + + this.once = function (name, callback) { + if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; + const hook = new Hook(name, function (...args) { + callback.apply(undefined, args); + delete self.hooks[this.id]; + }); + hook.__callback = callback; + }; + + this.off = function (name, callback) { + self.hooks.filter(hook => hook.__callback === callback).forEach(hook => { + delete self.hooks[hook.id]; + }); + }; + + this.removeListener = this.off; // Alias for remove + }; +})(module, require); diff --git a/d2bs/kolbot/libs/modules/Events.js b/d2bs/kolbot/libs/modules/Events.js index 5d824f1ff..9c44453e3 100644 --- a/d2bs/kolbot/libs/modules/Events.js +++ b/d2bs/kolbot/libs/modules/Events.js @@ -1,51 +1,109 @@ /** * @author Jaenster * @description A node like event system - * */ -(function (module, require) { - // eslint-disable-next-line no-unused-vars - const Events = module.exports = function () { - const Worker = require("Worker"), self = this; +(function (module) { + /** + * @class Events + * @constructor + */ + function Events() { + const handlers = new WeakMap(); + const onceHandlers = new WeakMap(); - this.hooks = []; + /** + * Get the event map for an object and map type. + * @param {Object} obj - The object to get the map for. + * @param {WeakMap>} mapType - The WeakMap storing event maps. + * @returns {Map} The event map for the object. + */ + function getMap(obj, mapType) { + if (!mapType.has(obj)) { + mapType.set(obj, new Map()); + } + return mapType.get(obj); + } + + /** + * Register an event handler for the given key. + * @param {string} key - The event name. + * @param {Function} handler - The callback function. + * @param {WeakMap} [handlerType] - Optional handler map (internal use). + * @returns {Events} The instance for chaining. + */ + function on(key, handler, handlerType) { + handlerType = handlerType || handlers; + let map = getMap(this, handlerType); + if (!map.has(key)) { + map.set(key, []); + } + map.get(key).push(handler); + return this; + } + + /** + * Register a one-time event handler for the given key. + * @param {string} key - The event name. + * @param {Function} handler - The callback function. + * @returns {Events} The instance for chaining. + */ + function once(key, handler) { + return on.call(this, key, handler, onceHandlers); + } + + /** + * Remove an event handler for the given key. + * @param {string} key - The event name. + * @param {Function} handler - The callback function to remove. + * @returns {Events} The instance for chaining. + */ + function off(key, handler) { + [handlers, onceHandlers].forEach(function (handlerType) { + let map = getMap(this, handlerType); + if (map.has(key)) { + let arr = map.get(key); + let idx = arr.indexOf(handler); + if (idx > -1) { + arr.splice(idx, 1); + } + } + }, this); + return this; + } + + /** + * Emit an event, calling all handlers for the given key. + * @param {string} key - The event name. + * @param {...*} args - Arguments to pass to the handlers. + * @returns {Events} The instance for chaining. + */ + function emit(key, ...args) { + let callbacks = []; + + let onceMap = getMap(this, onceHandlers); + let restMap = getMap(this, handlers); - function Hook(name, callback) { - this.name = name; - this.callback = callback; - this.id = self.hooks.push(this) - 1; - this.__callback = callback; // used for once + if (onceMap.has(key)) { + callbacks = callbacks.concat(onceMap.get(key).splice(0)); + } + if (restMap.has(key)) { + callbacks = callbacks.concat(restMap.get(key)); + } + + callbacks.forEach(function (cb) { + cb.apply(this, args); + }, this); + + return this; } - this.on = function (name, callback) { - if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; - return new Hook(name, callback); - }; - - this.trigger = function (name, ...args) { - return self.hooks.forEach(hook => !hook.name || hook.name === name && Worker.push(function () { - hook.callback.apply(hook, args); - })); - }; - - this.emit = this.trigger; // Alias for trigger - - this.once = function (name, callback) { - if (callback === undefined && typeof name === "function") [callback, name] = [name, callback]; - const hook = new Hook(name, function (...args) { - callback.apply(undefined, args); - delete self.hooks[this.id]; - }); - hook.__callback = callback; - }; - - this.off = function (name, callback) { - self.hooks.filter(hook => hook.__callback === callback).forEach(hook => { - delete self.hooks[hook.id]; - }); - }; - - this.removeListener = this.off; // Alias for remove - }; -})(module, require); + // Attach methods to the instance + this.on = on; + this.once = once; + this.off = off; + this.emit = emit; + } + + module.exports = Events; +})(module); diff --git a/d2bs/kolbot/libs/modules/Messaging.js b/d2bs/kolbot/libs/modules/Messaging.js index 3b1587b04..80bd73e7a 100644 --- a/d2bs/kolbot/libs/modules/Messaging.js +++ b/d2bs/kolbot/libs/modules/Messaging.js @@ -5,7 +5,7 @@ (function (module, require) { - const myEvents = new (require("Events")); + const myEvents = new (require("./AsyncEvents")); const Worker = require("Worker"); diff --git a/d2bs/kolbot/libs/modules/Socket.js b/d2bs/kolbot/libs/modules/Socket.js index 6c4ea5065..c42457015 100644 --- a/d2bs/kolbot/libs/modules/Socket.js +++ b/d2bs/kolbot/libs/modules/Socket.js @@ -6,7 +6,7 @@ (function (module, require, buildinSock) { const Worker = require("Worker"); - const Events = require("Events"); + const Events = require("./AsyncEvents"); /** @constructor Socket*/ function Socket(hostname, port) { diff --git a/d2bs/kolbot/libs/modules/Team.js b/d2bs/kolbot/libs/modules/Team.js index f8d69099a..84b8907d5 100644 --- a/d2bs/kolbot/libs/modules/Team.js +++ b/d2bs/kolbot/libs/modules/Team.js @@ -8,7 +8,7 @@ (function (threadType, globalThis) { const others = []; - const myEvents = new (require("Events")); + const myEvents = new (require("./AsyncEvents")); const Worker = require("Worker"); const Messaging = require("Messaging"); const defaultCopyDataMode = 0xC0FFFEE; From 2ee30ec60b3d16b6e9807cc14337f50b7ae6cada Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:37:12 -0500 Subject: [PATCH 651/758] Add PathNode.update method and update typings - Introduced an update method to PathNode for updating x and y coordinates. - Updated TypeScript definitions to reflect the new method and added missing references for improved type safety. --- d2bs/kolbot/libs/core/Pather.js | 8 ++++++++ d2bs/kolbot/sdk/globals.d.ts | 3 +++ 2 files changed, 11 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 9d6ddbb5e..331537c42 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -36,6 +36,14 @@ PathNode.prototype.getWalkDistance = function () { }, 0) || Infinity; }; +/** + * @param {{ x?: number, y?: number }} node + */ +PathNode.prototype.update = function (node) { + if (typeof node.x === "number") this.x = node.x; + if (typeof node.y === "number") this.y = node.y; +}; + /** * Perform certain actions after moving to each node * @todo this needs to be re-worked diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index ba6faba45..611b697d3 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -19,10 +19,12 @@ /// /// /// +/// declare global { type IncludePath = import("./types/include-paths").IncludePath; type KolbotScript = import("./types/kolbot-scripts").KolbotScript; + type EventsInstance = InstanceType; interface Error { fileName: string; @@ -884,6 +886,7 @@ declare global { */ getWalkDistanceTo(node: PathNode, area?: number): number; mobCount(givenSettings: { range?: number; coll?: number; type?: number; ignoreClassids?: number[] }): number; + update({ x, y }: { x?: number; y?: number }): void; } class PathNode { From e46fa004fbac5577e9954df4c8cd8cb595bae85d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:38:29 -0500 Subject: [PATCH 652/758] Refactor Common module to use LazyLoader proxy - Replaces the internal lazy-loading logic in Common.js with a new LazyLoader module for improved modularity and maintainability. - Adds LazyLoader.js, updates Common.js to use it, and adjusts Common.d.ts typings to reflect the new proxy interface. --- d2bs/kolbot/libs/core/Common.js | 122 +--------------- d2bs/kolbot/libs/modules/LazyLoader.js | 189 +++++++++++++++++++++++++ d2bs/kolbot/sdk/types/Common.d.ts | 9 +- 3 files changed, 196 insertions(+), 124 deletions(-) create mode 100644 d2bs/kolbot/libs/modules/LazyLoader.js diff --git a/d2bs/kolbot/libs/core/Common.js b/d2bs/kolbot/libs/core/Common.js index 9cd4e82fa..f828d084f 100644 --- a/d2bs/kolbot/libs/core/Common.js +++ b/d2bs/kolbot/libs/core/Common.js @@ -8,10 +8,7 @@ // each common functionality is loaded into this object when it's needed // for the actual function files @see core/Common/ const Common = (function () { - /** - * @type {Map} - */ - const moduleCache = new Map(); + const LazyLoader = require("../modules/LazyLoader"); /** * @type {Map} @@ -26,119 +23,6 @@ const Common = (function () { ["Smith", "./Common/Smith"], ["Toolsthread", "./Common/Tools"], ]); - - /** - * Lazy load a Common module - * @param {string} moduleName - The name of the module to load - * @returns {any} The loaded module - */ - function loadModule(moduleName) { - if (moduleCache.has(moduleName)) { - return moduleCache.get(moduleName); - } - - let modulePath = modulePathMap.get(moduleName); - if (!modulePath) { - throw new Error("Unknown module: " + moduleName); - } - - try { - console.debug("Loading Common module: " + moduleName + " from " + modulePath); - let module = require(modulePath); - moduleCache.set(moduleName, module); - return module; - } catch (error) { - throw new Error("Failed to load module " + moduleName + ": " + error.message); - } - } - - return new Proxy({}, { - /** - * @param {Object} target - * @param {string} property - * @returns {any} - */ - get: function (target, property) { - if (typeof property === "string" && modulePathMap.has(property)) { - return loadModule(property); - } - return target[property]; - }, - - /** - * @param {Object} target - * @param {string} property - * @param {any} value - * @returns {boolean} - */ - set: function (target, property, value) { - target[property] = value; - return true; - }, - - /** - * @param {Object} target - * @param {string} property - * @returns {boolean} - */ - has: function (target, property) { - return typeof property === "string" && ( - modulePathMap.has(property) - || target.hasOwnProperty(property) - ); - }, - - /** - * @param {Object} target - * @returns {string[]} - */ - ownKeys: function (target) { - return Array.from(modulePathMap.keys()).concat(Object.keys(target)); - }, - - /** - * @param {Object} target - * @param {string} property - * @returns {PropertyDescriptor | undefined} - */ - getOwnPropertyDescriptor: function (target, property) { - if (typeof property === "string" && modulePathMap.has(property)) { - return { - enumerable: true, - configurable: true, - get: function () { - return loadModule(property); - } - }; - } - return Object.getOwnPropertyDescriptor(target, property); - }, - - /** - * @param {Object} target - * @param {string} property - * @returns {boolean} - */ - deleteProperty: function (target, property) { - if (typeof property === "string" && modulePathMap.has(property)) { - // Remove from cache if it exists - if (moduleCache.has(property)) { - moduleCache.delete(property); - } - // Remove from target if it exists - if (target.hasOwnProperty(property)) { - delete target[property]; - } - return true; - } - - // Handle regular properties - if (target.hasOwnProperty(property)) { - delete target[property]; - return true; - } - - return false; - } - }); + + return LazyLoader(modulePathMap); })(); diff --git a/d2bs/kolbot/libs/modules/LazyLoader.js b/d2bs/kolbot/libs/modules/LazyLoader.js new file mode 100644 index 000000000..feea5f62a --- /dev/null +++ b/d2bs/kolbot/libs/modules/LazyLoader.js @@ -0,0 +1,189 @@ +/** + * @filename LazyLoader.js + * @author theBGuy + * @desc Provides a factory function to create a proxy object that lazily loads modules on property access. + * Useful for grouping related modules and optimizing resource usage by loading each module only when needed. + * + */ + +(function (module, require) { + function getCallerRelativeDir() { + const stack = new Error().stack.match(/[^\r\n]+/g); + let loaderDir, callerDir; + + for (let i = 1; i < stack.length; i++) { + // Find LazyLoader.js location + if (/lazyloader\.js/i.test(stack[i])) { + let match = stack[i].match(/@([a-zA-Z]:[^\s:]+)\.js:/); + if (match) { + loaderDir = match[1].replace(/\\/g, "/"); + } + } else { + // Find first non-LazyLoader caller + const match = stack[i].match(/@([a-zA-Z]:[^\s:]+)\.js:/); + if (match) { + callerDir = match[1].replace(/\\/g, "/"); + break; + } + } + } + if (!loaderDir || !callerDir) return ""; + // Remove filenames, keep directories + loaderDir = loaderDir.substring(0, loaderDir.lastIndexOf("/")); + callerDir = callerDir.substring(0, callerDir.lastIndexOf("/")); + // Compute relative path from loaderDir to callerDir + const loaderParts = loaderDir.split("/"); + const callerParts = callerDir.split("/"); + // Find common root + let i = 0; + while (i < loaderParts.length && i < callerParts.length && loaderParts[i] === callerParts[i]) { + i++; + } + // Go up from loaderDir + let relPath = ""; + for (let j = i; j < loaderParts.length; j++) relPath += "../"; + // Go down to callerDir + relPath += callerParts.slice(i).join("/"); + return relPath; + } + + /** + * Create a lazy-loading proxy for modules. + * @param {Map} modulePathMap - Map of module names to paths + */ + function createLazyModuleProxy(modulePathMap) { + const moduleCache = new Map(); + const callerRelDir = getCallerRelativeDir(); + + function loadModule(moduleName, customHandler) { + const nameStr = String(moduleName); + + if (customHandler) { + moduleCache.set(nameStr, customHandler); + return customHandler; + } + if (moduleCache.has(nameStr)) { + return moduleCache.get(nameStr); + } + let modulePath = modulePathMap.get(nameStr); + if (!modulePath) { + throw new Error("Unknown module: " + nameStr); + } + try { + if (modulePath.startsWith("./")) { + modulePath = callerRelDir + "/" + modulePath.slice(2); + } + console.debug("Loading module: " + nameStr + " from " + modulePath); + let module = require(modulePath); + moduleCache.set(nameStr, module); + return module; + } catch (error) { + throw new Error("Failed to load module " + nameStr + ": " + error.message); + } + } + + return new Proxy({}, { + /** + * @param {Object} target + * @param {string} property + * @returns {any} + */ + get: function (target, property) { + if (property === "load") { + /** @param {string} module */ + return function (module, customHandler) { + return loadModule(module, customHandler); + }; + } + if (modulePathMap.has(property)) { + return loadModule(property); + } + // Backwards compatibility for ClassAttack: proxy to current class instance + if ( + typeof property === "string" && + !(property in target) && + modulePathMap.has(me.classid) + ) { + const instance = loadModule(me.classid); + if (instance && property in instance) { + const value = instance[property]; + return typeof value === "function" ? value.bind(instance) : value; + } + } + return target[property]; + }, + + /** + * @param {Object} target + * @param {string} property + * @param {any} value + * @returns {boolean} + */ + set: function (target, property, value) { + target[property] = value; + return true; + }, + + /** + * @param {Object} target + * @param {string} property + * @returns {boolean} + */ + has: function (target, property) { + return typeof property === "string" && ( + modulePathMap.has(property) || target.hasOwnProperty(property) + ); + }, + + /** + * @param {Object} target + * @returns {string[]} + */ + ownKeys: function (target) { + return Array.from(modulePathMap.keys()).concat(Object.keys(target)); + }, + + /** + * @param {Object} target + * @param {string} property + * @returns {PropertyDescriptor | undefined} + */ + getOwnPropertyDescriptor: function (target, property) { + if (modulePathMap.has(property)) { + return { + enumerable: true, + configurable: true, + get: function () { + return loadModule(property); + } + }; + } + return Object.getOwnPropertyDescriptor(target, property); + }, + + /** + * @param {Object} target + * @param {string} property + * @returns {boolean} + */ + deleteProperty: function (target, property) { + if (modulePathMap.has(property)) { + if (moduleCache.has(property)) { + moduleCache.delete(property); + } + if (target.hasOwnProperty(property)) { + delete target[property]; + } + return true; + } + if (target.hasOwnProperty(property)) { + delete target[property]; + return true; + } + return false; + } + }); + } + + module.exports = createLazyModuleProxy; +})(module, require); diff --git a/d2bs/kolbot/sdk/types/Common.d.ts b/d2bs/kolbot/sdk/types/Common.d.ts index 4a00f5a70..7ddffdda8 100644 --- a/d2bs/kolbot/sdk/types/Common.d.ts +++ b/d2bs/kolbot/sdk/types/Common.d.ts @@ -1,16 +1,15 @@ -export {}; declare global { - // namespace Common { - // namespace Diablo {} - // } const Common: { + load: (moduleName: string) => unknown; + Ancients: typeof import("../../libs/core/Common/Ancients"); Baal: typeof import("../../libs/core/Common/Baal"); Cain: typeof import("../../libs/core/Common/Cain"); Cows: typeof import("../../libs/core/Common/Cows"); - Diablo: typeof import("../../libs/core/Common/Diablo"); + Diablo: typeof import("../../libs/core/Common/Diablo") & EventsInstance; Leecher: typeof import("../../libs/core/Common/Leecher"); Smith: typeof import("../../libs/core/Common/Smith"); Toolsthread: typeof import("../../libs/core/Common/Tools"); }; } +export {}; From 10634854038b9a68d3598a2df9d9a9fe2812fdcc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:49:02 -0500 Subject: [PATCH 653/758] Refactor class attack handling and modularize logic - Replaces direct includes of class attack files with a new ClassAttack loader, refactors attack logic to use ClassAttack[me.classid] for method calls, and modularizes individual class attack files (Amazon, Assassin, Barbarian) to use CommonJS exports. - Adds new core/ClassAttack.js and sdk/types/ClassAttack.d.ts for type and loader support. Improves maintainability and extensibility of attack logic. --- d2bs/kolbot/libs/core/Attack.js | 60 +- d2bs/kolbot/libs/core/Attacks/Amazon.js | 408 ++++----- d2bs/kolbot/libs/core/Attacks/Assassin.js | 474 +++++----- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 477 +++++----- d2bs/kolbot/libs/core/Attacks/Druid.js | 305 +++---- d2bs/kolbot/libs/core/Attacks/Necromancer.js | 882 ++++++++++--------- d2bs/kolbot/libs/core/Attacks/Paladin.js | 623 ++++++------- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 434 ++++----- d2bs/kolbot/libs/core/Attacks/Wereform.js | 293 +++--- d2bs/kolbot/libs/core/ClassAttack.js | 27 + d2bs/kolbot/libs/core/Common/Baal.js | 4 +- d2bs/kolbot/libs/core/Precast.js | 2 +- d2bs/kolbot/libs/modules/sdk.js | 1 + d2bs/kolbot/libs/scripts/Andariel.js | 2 +- d2bs/kolbot/libs/scripts/AutoBaal.js | 10 +- d2bs/kolbot/libs/scripts/Duriel.js | 2 +- d2bs/kolbot/libs/scripts/Mephisto.js | 2 +- d2bs/kolbot/sdk/types/Attack.d.ts | 9 +- d2bs/kolbot/sdk/types/ClassAttack.d.ts | 27 + d2bs/kolbot/threads/AntiHostile.js | 6 +- 20 files changed, 2081 insertions(+), 1967 deletions(-) create mode 100644 d2bs/kolbot/libs/core/ClassAttack.js create mode 100644 d2bs/kolbot/sdk/types/ClassAttack.d.ts diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 06e47e38f..c00ced041 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -45,13 +45,14 @@ const Attack = { // Initialize attacks init: function () { + // TODO: properly handle loading wereform and custom files so they work with LazyLoader and get the correct types if (Config.Wereform) { - include("core/Attacks/wereform.js"); + ClassAttack.load(me.classid, require("./Attacks/Wereform")); } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { console.log("Loading custom attack file"); - include("core/Attacks/" + Config.CustomClassAttack + ".js"); + ClassAttack.load(me.classid, require("./Attacks/" + Config.CustomClassAttack)); } else { - include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); + ClassAttack.load(me.classid); } if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { @@ -366,7 +367,7 @@ const Attack = { Packet.flash(me.gid); } - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + let result = ClassAttack[me.classid].doAttack(target, attackCount % 15 === 0); if (result === this.Result.FAILED) { if (retry++ > 3) { @@ -392,7 +393,7 @@ const Attack = { attackCount === Config.MaxAttackCount && (errorInfo = " (attackCount exceeded: " + attackCount + ")"); Config.MFSwitchPercent && me.switchWeapons(primarySlot); - ClassAttack.afterAttack(); + ClassAttack[me.classid].afterAttack(); Pickit.pickItems(); if (!!target && target.attackable) { @@ -439,7 +440,7 @@ const Attack = { const who = (!!target.name ? target.name : classId); while (attackCount < Config.MaxAttackCount && target.attackable && !Attack.skipCheck(target)) { - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + let result = ClassAttack[me.classid].doAttack(target, attackCount % 15 === 0); if (result === this.Result.FAILED) { if (retry++ > 3) { @@ -660,7 +661,7 @@ const Attack = { let _currMon = attacks.get(target.gid); const checkAttackSkill = (!!_currMon && _currMon.attacks % 15 === 0); - const result = ClassAttack.doAttack(target, checkAttackSkill); + const result = ClassAttack[me.classid].doAttack(target, checkAttackSkill); if (result) { retry = 0; @@ -683,10 +684,13 @@ const Attack = { const isSpecial = target.isSpecial; const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; const checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; - const hammerCheck = me.paladin && checkSkill === sdk.skills.BlessedHammer; + const hammerCheck = me.classid === sdk.player.class.Paladin && checkSkill === sdk.skills.BlessedHammer; if (Config.AttackSkill[secAttack] > -1 - && (!Attack.checkResist(target, checkSkill) || (hammerCheck && !ClassAttack.getHammerPosition(target)))) { + && ( + !Attack.checkResist(target, checkSkill) + || (hammerCheck && !ClassAttack[me.classid].getHammerPosition(target))) + ) { skillCheck = Config.AttackSkill[secAttack]; } else { skillCheck = checkSkill; @@ -739,7 +743,9 @@ const Attack = { + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) ); } - Config.FastFindItem && pickit && ClassAttack.findItem(); + if (Config.FastFindItem && pickit && me.classid === sdk.player.class.Barbarian) { + ClassAttack[me.classid].findItem(); + } Pickit.fastPick(); } } else { @@ -761,7 +767,7 @@ const Attack = { } if (attackCount > 0) { - ClassAttack.afterAttack(pickit); + ClassAttack[me.classid].afterAttack(pickit); Attack.openChests(range, orgx, orgy); pickit && Pickit.pickItems(); } else { @@ -924,7 +930,7 @@ const Attack = { } } - const result = ClassAttack.doAttack(target, checkAttackSkill); + const result = ClassAttack[me.classid].doAttack(target, checkAttackSkill); if (result) { retry = 0; @@ -947,10 +953,12 @@ const Attack = { const isSpecial = target.isSpecial; const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; const checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; - const hammerCheck = me.paladin && checkSkill === sdk.skills.BlessedHammer; + const hammerCheck = me.classid === sdk.player.class.Paladin && checkSkill === sdk.skills.BlessedHammer; if (Config.AttackSkill[secAttack] > -1 - && (!Attack.checkResist(target, checkSkill) || (hammerCheck && !ClassAttack.getHammerPosition(target))) + && ( + !Attack.checkResist(target, checkSkill) + || (hammerCheck && !ClassAttack[me.classid].getHammerPosition(target))) ) { skillCheck = Config.AttackSkill[secAttack]; } else { @@ -1001,7 +1009,9 @@ const Attack = { + "ÿc0 - ÿc7Duration: ÿc0" + Time.format(getTickCount() - tick) ); } - Config.FastFindItem && pickit && ClassAttack.findItem(); + if (Config.FastFindItem && pickit && me.classid === sdk.player.class.Barbarian) { + ClassAttack[me.classid].findItem(); + } Pickit.fastPick(); } } else { @@ -1023,7 +1033,7 @@ const Attack = { } if (attackCount > 0) { - ClassAttack.afterAttack(pickit); + ClassAttack[me.classid].afterAttack(pickit); this.openChests(range, orgx, orgy); pickit && Pickit.pickItems(); } else { @@ -1180,7 +1190,7 @@ const Attack = { Config.Dodge && me.hpPercent <= Config.DodgeHP && this.deploy(target, Config.DodgeRange, 5, 9); // me.overhead("attacking " + target.name + " spectype " + target.spectype + " id " + target.classid); let i; - let result = ClassAttack.doAttack(target, attackCount % 15 === 0); + let result = ClassAttack[me.classid].doAttack(target, attackCount % 15 === 0); if (result) { retry = 0; @@ -1240,7 +1250,7 @@ const Attack = { target.isBoss && Attack._killed.add(target.classid); target.uniqueid > -1 && Attack._killed.add(target.name); } - Config.FastFindItem && pickit && ClassAttack.findItem(); + Config.FastFindItem && pickit && ClassAttack[me.classid].findItem(); Pickit.fastPick(); } } else { @@ -1257,7 +1267,7 @@ const Attack = { } if (attackCount > 0) { - ClassAttack.afterAttack(true); + ClassAttack[me.classid].afterAttack(true); this.openChests(Config.OpenChests.Range); Pickit.pickItems(); } else { @@ -1854,13 +1864,18 @@ const Attack = { monList = this.buildMonsterList(); monList.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); + const grid = this.buildGrid(unit.x - distance, unit.x + distance, unit.y - distance, unit.y + distance, spread); if (!grid.length) return false; - grid.sort((a, b) => getDistance(b.x, b.y, unit.x, unit.y) - getDistance(a.x, a.y, unit.x, unit.y)); + 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) @@ -1928,6 +1943,7 @@ const Attack = { throw new Error("buildGrid: Bad parameters"); } + /** @type {(PathNode & { coll: number })[]} */ let grid = []; for (let i = xmin; i <= xmax; i += spread) { diff --git a/d2bs/kolbot/libs/core/Attacks/Amazon.js b/d2bs/kolbot/libs/core/Attacks/Amazon.js index a2389b1ee..f9191e597 100644 --- a/d2bs/kolbot/libs/core/Attacks/Amazon.js +++ b/d2bs/kolbot/libs/core/Attacks/Amazon.js @@ -5,269 +5,271 @@ * */ -/** @implements {ClassAttack} */ -const ClassAttack = { - bowCheck: false, - lightFuryTick: 0, - - /** @param {Monster} unit */ - decideSkill: function (unit) { - let skills = { timed: -1, untimed: -1 }; - if (!unit) return skills; - - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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]; - } +(function (module) { + module.exports = { + classid: sdk.player.class.Amazon, + bowCheck: false, + lightFuryTick: 0, + + /** @param {Monster} unit */ + decideSkill: function (unit) { + let skills = { timed: -1, untimed: -1 }; + if (!unit) return skills; + + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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]; + } - // 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; - }, - - /** - * @param {Monster | Player} unit - * @param {boolean} [preattack] - * @returns {AttackResult} - */ - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - let 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; + return skills; + }, + + /** + * @param {Monster | Player} unit + * @param {boolean} [preattack] + * @returns {AttackResult} + */ + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + let 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; + } } } - } - if (Config.ChargeCast.skill > -1) { - Attack.doChargeCast(unit); - } + if (Config.ChargeCast.skill > -1) { + Attack.doChargeCast(unit); + } - if (preattack) { - let preAttackResult = Attack.doPreAttack(unit); - if (preAttackResult !== Attack.Result.NOOP) { - return preAttackResult; + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; + } } - } - if (Skill.canUse(sdk.skills.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); + if (Skill.canUse(sdk.skills.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); + } } - } - if (Skill.canUse(sdk.skills.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 (!unit.getState(sdk.states.InnerSight) && Config.UseInnerSight && Skill.canUse(sdk.skills.InnerSight)) { - Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); + if (Skill.canUse(sdk.skills.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 (!unit.getState(sdk.states.InnerSight) && Config.UseInnerSight && Skill.canUse(sdk.skills.InnerSight)) { + Skill.cast(sdk.skills.InnerSight, sdk.skills.hand.Right, unit); + } + } else { + Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); } - } else { - Skill.cast(sdk.skills.SlowMissiles, sdk.skills.hand.Right, unit); } } } - } - let mercRevive = 0; - let skills = this.decideSkill(unit); - let result = this.doCast(unit, skills.timed, skills.untimed); + let mercRevive = 0; + 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(); + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); - while (unit.attackable) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - if (!unit) return Attack.Result.SUCCESS; + while (unit.attackable) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + } + 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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + + if (!!closeMob) { + let findSkill = this.decideSkill(closeMob); + if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { + (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); + } + } } - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - - if (!!closeMob) { - let findSkill = this.decideSkill(closeMob); - if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { - (Skill.canUse(sdk.skills.Decoy) && Skill.cast(sdk.skills.Decoy, sdk.skills.hand.Right, unit)); - } + return Attack.Result.SUCCESS; + } + + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + + let needRepair = (me.needRepair() || []); + + // Repair check, mainly to restock arrows + needRepair.length > 0 && Town.visitTown(true); + + this.lightFuryTick = 0; + }, + + /** + * @param {Monster | Player} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} 0 - fail, 1 - success, 2 - no valid attack skills + */ + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // No valid skills can be found + if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; + Config.TeleSwitch && me.switchToPrimary(); + + // 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; } } - return Attack.Result.SUCCESS; - } + let walk; - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - - let needRepair = (me.needRepair() || []); - - // Repair check, mainly to restock arrows - needRepair.length > 0 && Town.visitTown(true); - - this.lightFuryTick = 0; - }, - - /** - * @param {Monster | Player} unit - * @param {number} timedSkill - * @param {number} untimedSkill - * @returns {AttackResult} 0 - fail, 1 - success, 2 - no valid attack skills - */ - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // No valid skills can be found - if (timedSkill < 0 && untimedSkill < 0) return Attack.Result.CANTATTACK; - Config.TeleSwitch && me.switchToPrimary(); - - // 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.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)) { + ClassAttack.lightFuryTick = getTickCount(); + } - let walk; + return Attack.Result.SUCCESS; + } + + break; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, unit.classid)) { + return Attack.Result.FAILED; + } - if (timedSkill > -1 && (!me.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)) { + // 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; } } - if (!unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit)) { - ClassAttack.lightFuryTick = getTickCount(); - } + !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); return Attack.Result.SUCCESS; } + } - break; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, unit.classid)) { + if (untimedSkill > -1) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, unit.classid)) { return Attack.Result.FAILED; } - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = (Skill.getRange(timedSkill) < 4 + walk = (Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) ); - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; } } - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); return Attack.Result.SUCCESS; } - } - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, unit.classid)) { - 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) - ); + Misc.poll(() => !me.skillDelay, 1000, 40); - if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } + // Wait for Lightning Fury timeout + while (timedSkill === sdk.skills.LightningFury + && this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { + delay(40); } - !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 (timedSkill === sdk.skills.LightningFury - && this.lightFuryTick && getTickCount() - this.lightFuryTick < Config.LightningFuryDelay * 1000) { - delay(40); - } - - return Attack.Result.SUCCESS; - } -}; + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index e1725cfa1..d7d1cacc0 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -5,284 +5,292 @@ * */ -const ClassAttack = { - lastTrapPos: {}, - trapRange: 20, - - /** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {AttackResult} - */ - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking +(function (module) { + module.exports = { + lastTrapPos: new PathNode(), + trapRange: 20, + + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } } } - } - - if (Config.ChargeCast.skill > -1) { - Attack.doChargeCast(unit); - } - if (preattack) { - let preAttackResult = Attack.doPreAttack(unit); - if (preAttackResult !== Attack.Result.NOOP) { - return preAttackResult; - } - } - - let mercRevive = 0; - let timedSkill = -1; - let untimedSkill = -1; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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 && Skill.canUse(sdk.skills.CloakofShadows) - && !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; + if (Config.ChargeCast.skill > -1) { + Attack.doChargeCast(unit); } - } - let checkTraps = this.checkTraps(unit); + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; + } + } - 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))) { + let mercRevive = 0; + let timedSkill = -1; + let untimedSkill = -1; + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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 && Skill.canUse(sdk.skills.CloakofShadows) + && !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; } } - 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 && Skill.canUse(sdk.skills.CloakofShadows) - && unit.distance < 20 && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { - Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); - } - - // 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)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 - && Attack.checkResist(unit, Config.AttackSkill[5]) - && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { - timedSkill = Config.AttackSkill[5]; - } - - // 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, classid)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 - && Attack.checkResist(unit, Config.AttackSkill[6]) - && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { - untimedSkill = Config.AttackSkill[6]; - } - - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 - && Skill.getManaCost(timedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } - - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 - && Skill.getManaCost(untimedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } - - let result = this.doCast(unit, timedSkill, untimedSkill); - - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); - - while (unit.attackable) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + 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; + } } - if (!unit) return Attack.Result.SUCCESS; - if (me.needMerc()) { - if (Config.MercWatch && mercRevive++ < 1) { - Town.visitTown(); - } else { - return Attack.Result.CANTATTACK; + 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 && Skill.canUse(sdk.skills.CloakofShadows) + && unit.distance < 20 && !me.skillDelay && !me.getState(sdk.states.CloakofShadows)) { + Skill.cast(sdk.skills.CloakofShadows, sdk.skills.hand.Right); + } + + // 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)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + timedSkill = Config.AttackSkill[5]; + } + + // 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, classid)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + untimedSkill = Config.AttackSkill[6]; + } + + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } + + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } + + let result = this.doCast(unit, timedSkill, untimedSkill); + + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + + while (unit.attackable) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); } + if (!unit) return Attack.Result.SUCCESS; - (merc === undefined || !merc) && (merc = me.getMerc()); - } + if (me.needMerc()) { + if (Config.MercWatch && mercRevive++ < 1) { + Town.visitTown(); + } else { + return Attack.Result.CANTATTACK; + } - if (!!merc && getDistance(merc, unit) > 5) { - Pather.moveToUnit(unit); + (merc === undefined || !merc) && (merc = me.getMerc()); + } + + if (!!merc && getDistance(merc, unit) > 5) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); } - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); + return Attack.Result.SUCCESS; } - return Attack.Result.SUCCESS; - } - - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - }, - - /** - * @param {Monster} unit - * @param {number} timedSkill - * @param {number} untimedSkill - * @returns {AttackResult} - */ - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); - - let walk; - let classid = unit.classid; - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.Whirlwind: - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.BlockWall)) { + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + }, + + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); + + let walk; + let classid = unit.classid; + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.Whirlwind: + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.BlockWall)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.BlockWall)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Attack.whirlwind(unit); + + return Attack.Result.SUCCESS; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { return Attack.Result.FAILED; } - } - !unit.dead && Attack.whirlwind(unit); + 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) + ); - return Attack.Result.SUCCESS; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + 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, untimedSkill, classid)) { return Attack.Result.FAILED; } - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = (Skill.getRange(timedSkill) < 4 + walk = (Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) ); - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; } } - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); return Attack.Result.SUCCESS; } - } - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { - return Attack.Result.FAILED; - } + Misc.poll(() => !me.skillDelay, 1000, 40); + + return Attack.Result.SUCCESS; + }, - 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) - ); + checkTraps: function (unit) { + if (!Config.UseTraps || !unit) return false; - if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } + // getDistance crashes when using an object with x, y props, that's why it's unit.x, unit.y and not unit + // is this still a thing ^^? todo: test it + if (me.getMinionCount(sdk.summons.type.AssassinTrap) === 0 || !this.lastTrapPos.hasOwnProperty("x") + || getDistance(unit.x, unit.y, this.lastTrapPos.x, this.lastTrapPos.y) > 15) { + return 5; } - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - - return Attack.Result.SUCCESS; - } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; - }, - - checkTraps: function (unit) { - if (!Config.UseTraps || !unit) return false; - - // getDistance crashes when using an object with x, y props, that's why it's unit.x, unit.y and not unit - // is this still a thing ^^? todo: test it - if (me.getMinionCount(sdk.summons.type.AssassinTrap) === 0 || !this.lastTrapPos.hasOwnProperty("x") - || getDistance(unit.x, unit.y, this.lastTrapPos.x, this.lastTrapPos.y) > 15) { - return 5; - } - - return 5 - me.getMinionCount(sdk.summons.type.AssassinTrap); - }, - - // todo - either import soloplays immune to trap check or add config option for immune to traps - // since this is the base file probably better to leave the option available rather than hard code it - // check if unit is still attackable after each cast? - placeTraps: function (unit, amount = 5) { - 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 - why not andy? - 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; - - Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + return 5 - me.getMinionCount(sdk.summons.type.AssassinTrap); + }, + + // todo - either import soloplays immune to trap check or add config option for immune to traps + // since this is the base file probably better to leave the option available rather than hard code it + // check if unit is still attackable after each cast? + /** + * @param {Monster | Unit} unit + * @param {number} amount + */ + placeTraps: function (unit, amount = 5) { + let traps = 0; + this.lastTrapPos.update({ 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 - why not andy? + 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; + + Skill.cast(Config.Traps[traps], sdk.skills.hand.Right, unit.x + i, unit.y + j); + } + + traps += 1; } - - traps += 1; } } - } - return true; - }, -}; + return true; + }, + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index f6af603d6..34e22d5a8 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -5,286 +5,287 @@ * */ -/** - * @todo - * - Add howl - */ - -const ClassAttack = { +(function (module) { /** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {AttackResult} - */ - doAttack: function (unit, preattack = false) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - let needRepair = me.needRepair(); - - if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { - console.log("towncheck"); - - if (Town.visitTown(!!needRepair.length)) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + * @todo + * - Add howl + */ + module.exports = { + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ + doAttack: function (unit, preattack = false) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + let needRepair = me.needRepair(); + + if ((Config.MercWatch && me.needMerc()) || needRepair.length > 0) { + console.log("towncheck"); + + if (Town.visitTown(!!needRepair.length)) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } } } - } - if (Config.ChargeCast.skill > -1) { - Attack.doChargeCast(unit); - } - - if (preattack) { - let preAttackResult = Attack.doPreAttack(unit); - if (preAttackResult !== Attack.Result.NOOP) { - return preAttackResult; + if (Config.ChargeCast.skill > -1) { + Attack.doChargeCast(unit); } - } - - let 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 && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { - attackSkill = Config.AttackSkill[index + 1]; + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; + } } - } - // Low mana skill - if (Skill.getManaCost(attackSkill) > me.mp - && Config.LowManaSkill[0] > -1 - && Attack.checkResist(unit, Config.LowManaSkill[0])) { - attackSkill = Config.LowManaSkill[0]; - } - - // low weapon-quantity -> use secondary skill if we can - if (attackSkill === sdk.skills.DoubleThrow - && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) - && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { - attackSkill = Config.AttackSkill[index + 1]; - } + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let attackSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - // Telestomp with barb is pointless - return this.doCast(unit, attackSkill); - }, - - /** - * Check if we need to precast, repair items, or perform findItem - * @param {boolean} pickit - determines if we use findItem or not - */ - afterAttack: function (pickit = true) { - Precast.doPrecast(false); + if (!Attack.checkResist(unit, attackSkill)) { + attackSkill = -1; - let needRepair = (me.needRepair() || []); - - // Repair check - needRepair.length > 0 && Town.visitTown(true); - pickit && this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); - }, - - /** - * @param {Monster} unit - * @param {number} attackSkill - * @returns {AttackResult} - */ - doCast: function (unit, attackSkill = -1) { - if (attackSkill < 0) return Attack.Result.CANTATTACK; - // check if unit became invalidated - if (!unit || !unit.attackable) return Attack.Result.SUCCESS; - (Config.TeleSwitch || Config.FindItemSwitch) && me.switchToPrimary(); - - switch (attackSkill) { - case sdk.skills.Whirlwind: - /** - * @todo we sometimes struggle getting into position because of monsters which is dumb since we can - * just whirl through them, so that needs to be fixed - */ - 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 (Config.AttackSkill[index + 1] > -1 && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { + attackSkill = Config.AttackSkill[index + 1]; } } - !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; + // Low mana skill + if (Skill.getManaCost(attackSkill) > me.mp + && Config.LowManaSkill[0] > -1 + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + attackSkill = Config.LowManaSkill[0]; } - - if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - let walk = (Skill.getRange(attackSkill) < 4 - && unit.distance < 10 - && !checkCollision(me, unit, sdk.collision.BlockWall) - ); - - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } + + // low weapon-quantity -> use secondary skill if we can + if (attackSkill === sdk.skills.DoubleThrow + && (me.getWeaponQuantity() <= 3 || me.getWeaponQuantity(sdk.body.LeftArm) <= 3) + && Skill.canUse(Config.AttackSkill[index + 1]) && Attack.checkResist(unit, Config.AttackSkill[index + 1])) { + attackSkill = Config.AttackSkill[index + 1]; } - !unit.dead && Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + // Telestomp with barb is pointless + return this.doCast(unit, attackSkill); + }, + + /** + * Check if we need to precast, repair items, or perform findItem + * @param {boolean} pickit - determines if we use findItem or not + */ + afterAttack: function (pickit = true) { + Precast.doPrecast(false); + + let needRepair = (me.needRepair() || []); + + // Repair check + needRepair.length > 0 && Town.visitTown(true); + pickit && this.findItem(me.inArea(sdk.areas.Travincal) ? 60 : 20); + }, + + /** + * @param {Monster} unit + * @param {number} attackSkill + * @returns {AttackResult} + */ + doCast: function (unit, attackSkill = -1) { + if (attackSkill < 0) return Attack.Result.CANTATTACK; + // check if unit became invalidated + if (!unit || !unit.attackable) return Attack.Result.SUCCESS; + (Config.TeleSwitch || Config.FindItemSwitch) && me.switchToPrimary(); + + switch (attackSkill) { + case sdk.skills.Whirlwind: + /** + * @todo we sometimes struggle getting into position because of monsters which is dumb since we can + * just whirl through them, so that needs to be fixed + */ + 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; + } + } - return Attack.Result.SUCCESS; - } - }, + !unit.dead && Attack.whirlwind(unit); - /** - * Check whether there are any monsters in range that are attackable - * @param {number} range - * @returns {boolean} - */ - checkCloseMonsters: function (range = 10) { - const [mainAttElm, secAttElm] = [ - Attack.getSkillElement(Config.AttackSkill[1]), - Attack.getSkillElement(Config.AttackSkill[3]), - ]; - let monster = Game.getMonster(); - - if (monster) { - do { - if (monster.distance <= range - && monster.attackable - && !checkCollision(me, monster, sdk.collision.Ranged) - && ( - Attack.checkResist(monster, monster.isSpecial ? mainAttElm : secAttElm) - || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, secAttElm)) - ) - ) { - return true; + return Attack.Result.SUCCESS; + default: + if (Skill.getRange(attackSkill) < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { + return Attack.Result.FAILED; } - } while (monster.getNext()); - } - - return false; - }, - /** - * Use findItem skill to hork bodies - * @param {number} range - * @returns {boolean} - */ - findItem: function (range = 10) { - if (!Skill.canUse(sdk.skills.FindItem)) return false; + if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + let walk = (Skill.getRange(attackSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); - let corpseList = []; - const { x: orgX, y: orgY } = me; + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } - MainLoop: - for (let i = 0; i < 3; i += 1) { - let corpse = Game.getMonster(); + !unit.dead && Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); - if (corpse) { + return Attack.Result.SUCCESS; + } + }, + + /** + * Check whether there are any monsters in range that are attackable + * @param {number} range + * @returns {boolean} + */ + checkCloseMonsters: function (range = 10) { + const [mainAttElm, secAttElm] = [ + Attack.getSkillElement(Config.AttackSkill[1]), + Attack.getSkillElement(Config.AttackSkill[3]), + ]; + let monster = Game.getMonster(); + + if (monster) { do { - if (corpse.dead && getDistance(corpse, orgX, orgY) <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); + if (monster.distance <= range + && monster.attackable + && !checkCollision(me, monster, sdk.collision.Ranged) + && ( + Attack.checkResist(monster, monster.isSpecial ? mainAttElm : secAttElm) + || (Config.AttackSkill[3] > -1 && Attack.checkResist(monster, secAttElm)) + ) + ) { + return true; } - } while (corpse.getNext()); + } while (monster.getNext()); } - while (corpseList.length > 0) { - if (this.checkCloseMonsters(5)) { - console.debug("Monsters nearby, clearing"); - Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); - Attack.clear(10, false, false, false, false); - Pather.moveToEx(orgX, orgY, { allowPicking: false }); - - continue MainLoop; + return false; + }, + + /** + * Use findItem skill to hork bodies + * @param {number} range + * @returns {boolean} + */ + findItem: function (range = 10) { + if (!Skill.canUse(sdk.skills.FindItem)) return false; + + let corpseList = []; + const { x: orgX, y: orgY } = me; + + MainLoop: + 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()); } - 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); + while (corpseList.length > 0) { + if (this.checkCloseMonsters(5)) { + console.debug("Monsters nearby, clearing"); + Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); + Attack.clear(10, false, false, false, false); + Pather.moveToEx(orgX, orgY, { allowPicking: false }); - if (this.checkCorpse(corpse)) { - if (corpse.distance > 30 || checkCollision(me, corpse, sdk.collision.BlockWall)) { - Pather.moveNearUnit(corpse, 5); + continue MainLoop; } - 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; - } + + 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 || checkCollision(me, corpse, sdk.collision.BlockWall)) { + Pather.moveNearUnit(corpse, 5); } - // 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; + 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); - delay(10); + break CorpseLoop; + } + + delay(10); + } } } - } - if (attempted && !invalidated && corpse && !corpse.getState(sdk.states.CorpseNoSelect)) { - // if (!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 (attempted && !invalidated && corpse && !corpse.getState(sdk.states.CorpseNoSelect)) { + // if (!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.switchWeapons(Attack.getPrimarySlot()); + Config.FindItemSwitch && me.switchWeapons(Attack.getPrimarySlot()); - return true; - }, + return true; + }, - /** - * Check if corpse is horkable - * @param {Monster} unit - * @returns {boolean} - */ - checkCorpse: function (unit) { - if (!unit || !copyUnit(unit).x || !unit.dead) { - return false; - } - if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 - && unit.spectype === sdk.monsters.spectype.All) { - // why ignore all normal monsters? - return false; - } + /** + * Check if corpse is horkable + * @param {Monster} unit + * @returns {boolean} + */ + checkCorpse: function (unit) { + if (!unit || !copyUnit(unit).x || !unit.dead) { + return false; + } + if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 + && unit.spectype === sdk.monsters.spectype.All) { + // why ignore all normal monsters? + return false; + } - // monstats2 doesn't contain guest monsters info. sigh.. - if (unit.classid <= sdk.monsters.BurningDeadArcher2 - && !getBaseStat("monstats2", unit.classid, "corpseSel")) { - return false; - } + // monstats2 doesn't contain guest monsters info. sigh.. + if (unit.classid <= sdk.monsters.BurningDeadArcher2 + && !getBaseStat("monstats2", unit.classid, "corpseSel")) { + return false; + } + + let states = [ + sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, + sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect + ]; - let states = [ - sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, - sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect - ]; - - return (unit.distance <= 25 - && !checkCollision(me, unit, sdk.collision.Ranged) - && states.every(function (state) { - return !unit.getState(state); - })); - } -}; + return (unit.distance <= 25 + && !checkCollision(me, unit, sdk.collision.Ranged) + && states.every(function (state) { + return !unit.getState(state); + })); + } + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Attacks/Druid.js b/d2bs/kolbot/libs/core/Attacks/Druid.js index f735f74dd..2d4a91bb6 100644 --- a/d2bs/kolbot/libs/core/Attacks/Druid.js +++ b/d2bs/kolbot/libs/core/Attacks/Druid.js @@ -5,197 +5,208 @@ * */ -const ClassAttack = { - doAttack: function (unit, preattack = false) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking +(function (module) { + module.exports = { + /** + * @param {Monster} unit + * @param {boolean} preattack + */ + doAttack: function (unit, preattack = false) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } } } - } - // Rebuff Hurricane - if (Skill.canUse(sdk.skills.Hurricane) && !me.getState(sdk.states.Hurricane)) { - Skill.cast(sdk.skills.Hurricane, sdk.skills.hand.Right); - } - // Rebuff Cyclone Armor - if (Skill.canUse(sdk.skills.CycloneArmor) && !me.getState(sdk.states.CycloneArmor)) { - Skill.cast(sdk.skills.CycloneArmor, sdk.skills.hand.Right); - } + // Rebuff Hurricane + if (Skill.canUse(sdk.skills.Hurricane) && !me.getState(sdk.states.Hurricane)) { + Skill.cast(sdk.skills.Hurricane, sdk.skills.hand.Right); + } + // Rebuff Cyclone Armor + if (Skill.canUse(sdk.skills.CycloneArmor) && !me.getState(sdk.states.CycloneArmor)) { + Skill.cast(sdk.skills.CycloneArmor, sdk.skills.hand.Right); + } - if (Config.ChargeCast.skill > -1) { - Attack.doChargeCast(unit); - } + if (Config.ChargeCast.skill > -1) { + Attack.doChargeCast(unit); + } - if (preattack) { - let preAttackResult = Attack.doPreAttack(unit); - if (preAttackResult !== Attack.Result.NOOP) { - return preAttackResult; + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; + } } - } - let mercRevive = 0; - let timedSkill = -1; - let untimedSkill = -1; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 - && Attack.checkResist(unit, Config.AttackSkill[5]) - && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { - timedSkill = Config.AttackSkill[5]; - } + let mercRevive = 0; + let timedSkill = -1; + let untimedSkill = -1; + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + timedSkill = 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, classid)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 - && Attack.checkResist(unit, Config.AttackSkill[6]) - && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { - untimedSkill = Config.AttackSkill[6]; - } + if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + untimedSkill = Config.AttackSkill[6]; + } - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 - && Skill.getManaCost(timedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 - && Skill.getManaCost(untimedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } - let result = this.doCast(unit, timedSkill, untimedSkill); + let result = this.doCast(unit, timedSkill, untimedSkill); - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); - while (unit.attackable) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - if (!unit) return Attack.Result.SUCCESS; + while (unit.attackable) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + } + 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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); } - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); + return Attack.Result.SUCCESS; } - return Attack.Result.SUCCESS; - } + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + }, + + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} + */ + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); + + let walk; + let classid = unit.classid; + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.Tornado: + 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; + } + } + + // Randomized x coord changes tornado path and prevents constant missing + !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit.x + rand(-2, 2), unit.y); - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - }, - - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); - - let walk; - let classid = unit.classid; - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { - switch (timedSkill) { - case sdk.skills.Tornado: - 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.SUCCESS; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { return Attack.Result.FAILED; } - } - // Randomized x coord changes tornado path and prevents constant missing - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit.x + rand(-2, 2), unit.y); + 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) + ); - return Attack.Result.SUCCESS; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + 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, untimedSkill, classid)) { return Attack.Result.FAILED; } - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - walk = (Skill.getRange(timedSkill) < 4 + walk = (Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) ); - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; } } - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); return Attack.Result.SUCCESS; } - } - - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { - 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); + Misc.poll(() => !me.skillDelay, 1000, 40); return Attack.Result.SUCCESS; } - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; - } -}; + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Attacks/Necromancer.js b/d2bs/kolbot/libs/core/Attacks/Necromancer.js index 6503bd420..483cf3792 100644 --- a/d2bs/kolbot/libs/core/Attacks/Necromancer.js +++ b/d2bs/kolbot/libs/core/Attacks/Necromancer.js @@ -5,570 +5,572 @@ * */ -const ClassAttack = { - novaTick: 0, - maxSkeletons: 0, - maxMages: 0, - maxRevives: 0, - - setArmySize: function () { - ClassAttack.maxSkeletons = Config.Skeletons === "max" - ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeleton) - : Config.Skeletons; - ClassAttack.maxMages = Config.SkeletonMages === "max" - ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeletalMage) - : Config.SkeletonMages; - ClassAttack.maxRevives = Config.Revives === "max" - ? Skill.getMaxSummonCount(sdk.skills.Revive) - : Config.Revives; - }, - - /** - * @returns {boolean} true - doesn't use summons or has all he can summon, false - not full of summons yet - */ - isArmyFull: function () { - // This necro doesn't summon anything so assume he's full - if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { - return true; - } +(function (module) { + module.exports = { + novaTick: 0, + maxSkeletons: 0, + maxMages: 0, + maxRevives: 0, + + setArmySize: function () { + ClassAttack[me.classid].maxSkeletons = Config.Skeletons === "max" + ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeleton) + : Config.Skeletons; + ClassAttack[me.classid].maxMages = Config.SkeletonMages === "max" + ? Skill.getMaxSummonCount(sdk.skills.RaiseSkeletalMage) + : Config.SkeletonMages; + ClassAttack[me.classid].maxRevives = Config.Revives === "max" + ? Skill.getMaxSummonCount(sdk.skills.Revive) + : Config.Revives; + }, + + /** + * @returns {boolean} true - doesn't use summons or has all he can summon, false - not full of summons yet + */ + isArmyFull: function () { + // This necro doesn't summon anything so assume he's full + if (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0) { + return true; + } - // Make sure we have a current count of summons needed - this.setArmySize(); + // Make sure we have a current count of summons needed + this.setArmySize(); - // See if we're at full army count - if ((me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) - && (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) - && (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives)) { - return false; - } + // See if we're at full army count + if ((me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) + && (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) + && (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives)) { + return false; + } - // If we got this far this necro has all the summons he needs - return true; - }, - - /** - * Check if we can use specific curse on monster - * @param {Monster} unit - * @param {number} curseID - * @returns {boolean} - */ - canCurse: function (unit, curseID) { - if (unit === undefined || unit.dead || !Skill.canUse(curseID)) return false; - - let state = (() => { - switch (curseID) { - case sdk.skills.AmplifyDamage: - return sdk.states.AmplifyDamage; - case sdk.skills.DimVision: - // dim doesn't work on oblivion knights - if ([ - sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3 - ].includes(unit.classid)) { + // If we got this far this necro has all the summons he needs + return true; + }, + + /** + * Check if we can use specific curse on monster + * @param {Monster} unit + * @param {number} curseID + * @returns {boolean} + */ + canCurse: function (unit, curseID) { + if (unit === undefined || unit.dead || !Skill.canUse(curseID)) return false; + + let state = (() => { + switch (curseID) { + case sdk.skills.AmplifyDamage: + return sdk.states.AmplifyDamage; + case sdk.skills.DimVision: + // dim doesn't work on oblivion knights + if ([ + sdk.monsters.OblivionKnight1, sdk.monsters.OblivionKnight2, sdk.monsters.OblivionKnight3 + ].includes(unit.classid)) { + return false; + } + return sdk.states.DimVision; + case sdk.skills.Weaken: + return sdk.states.Weaken; + case sdk.skills.IronMaiden: + return sdk.states.IronMaiden; + case sdk.skills.Terror: + return unit.scareable ? sdk.states.Terror : false; + case sdk.skills.Confuse: + // doens't work on specials + return unit.scareable ? sdk.states.Confuse : false; + case sdk.skills.LifeTap: + return sdk.states.LifeTap; + case sdk.skills.Attract: + // doens't work on specials + return unit.scareable ? sdk.states.Attract : false; + case sdk.skills.Decrepify: + return sdk.states.Decrepify; + case sdk.skills.LowerResist: + return sdk.states.LowerResist; + default: + console.warn("(ÿc9canCurse) :: ÿc1Invalid Curse ID: " + curseID); + return false; } - return sdk.states.DimVision; - case sdk.skills.Weaken: - return sdk.states.Weaken; - case sdk.skills.IronMaiden: - return sdk.states.IronMaiden; - case sdk.skills.Terror: - return unit.scareable ? sdk.states.Terror : false; - case sdk.skills.Confuse: - // doens't work on specials - return unit.scareable ? sdk.states.Confuse : false; - case sdk.skills.LifeTap: - return sdk.states.LifeTap; - case sdk.skills.Attract: - // doens't work on specials - return unit.scareable ? sdk.states.Attract : false; - case sdk.skills.Decrepify: - return sdk.states.Decrepify; - case sdk.skills.LowerResist: - return sdk.states.LowerResist; - default: - console.warn("(ÿc9canCurse) :: ÿc1Invalid Curse ID: " + curseID); - - return false; - } - })(); - - return state ? !unit.getState(state) : false; - }, - - /** - * @param {Monster} unit - * @returns {number | boolean} - */ - getCustomCurse: function (unit) { - if (Config.CustomCurse.length <= 0) return false; - - let curse = Config.CustomCurse - .findIndex(function (unitID) { - if ((typeof unitID[0] === "number" && unit.classid && unit.classid === unitID[0]) - || (typeof unitID[0] === "string" && unit.name && unit.name.toLowerCase() === unitID[0].toLowerCase())) { - return true; + })(); + + return state ? !unit.getState(state) : false; + }, + + /** + * @param {Monster} unit + * @returns {number | boolean} + */ + getCustomCurse: function (unit) { + if (Config.CustomCurse.length <= 0) return false; + + let curse = Config.CustomCurse + .findIndex(function (unitID) { + if ((typeof unitID[0] === "number" && unit.classid && unit.classid === unitID[0]) + || (typeof unitID[0] === "string" && unit.name && unit.name.toLowerCase() === unitID[0].toLowerCase())) { + return true; + } + return false; + }); + if (curse > -1) { + // format [id, curse, spectype] + if (Config.CustomCurse[curse].length === 3) { + return ((unit.spectype & Config.CustomCurse[curse][2]) ? Config.CustomCurse[curse][1] : false); + } else { + return Config.CustomCurse[curse][1]; } - return false; - }); - if (curse > -1) { - // format [id, curse, spectype] - if (Config.CustomCurse[curse].length === 3) { - return ((unit.spectype & Config.CustomCurse[curse][2]) ? Config.CustomCurse[curse][1] : false); - } else { - return Config.CustomCurse[curse][1]; } - } - return false; - }, - - /** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills - */ - doAttack: function (unit, preattack = false) { - if (!unit || unit.dead) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - - let mercRevive = 0; - let gid = unit.gid; - let classid = unit.classid; - let [timedSkill, untimedSkill, customCurse] = [-1, -1, -1]; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - - if (Config.MercWatch && me.needMerc()) { - console.log("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + return false; + }, + + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills + */ + doAttack: function (unit, preattack = false) { + if (!unit || unit.dead) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + + let mercRevive = 0; + let gid = unit.gid; + let classid = unit.classid; + let [timedSkill, untimedSkill, customCurse] = [-1, -1, -1]; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + + if (Config.MercWatch && me.needMerc()) { + console.log("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } } } - } - - if (preattack) { - let preAttackResult = Attack.doPreAttack(unit); - if (preAttackResult !== Attack.Result.NOOP) { - return preAttackResult; - } - } - - // only continue if we can actually curse the unit otherwise its a waste of time - if (unit.curseable) { - customCurse = this.getCustomCurse(unit); - if (customCurse && this.canCurse(unit, customCurse)) { - if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { - return Attack.Result.FAILED; - } + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } + } - Skill.cast(customCurse, sdk.skills.hand.Right, unit); + // only continue if we can actually curse the unit otherwise its a waste of time + if (unit.curseable) { + customCurse = this.getCustomCurse(unit); - return Attack.Result.SUCCESS; - } else if (!customCurse) { - if (Config.Curse[0] > 0 && unit.isSpecial && this.canCurse(unit, Config.Curse[0])) { + if (customCurse && this.canCurse(unit, customCurse)) { if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { return Attack.Result.FAILED; } } - Skill.cast(Config.Curse[0], sdk.skills.hand.Right, unit); + Skill.cast(customCurse, sdk.skills.hand.Right, unit); return Attack.Result.SUCCESS; - } - - if (Config.Curse[1] > 0 && !unit.isSpecial && this.canCurse(unit, Config.Curse[1])) { - if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { - return Attack.Result.FAILED; + } else if (!customCurse) { + if (Config.Curse[0] > 0 && unit.isSpecial && this.canCurse(unit, Config.Curse[0])) { + if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } } + + Skill.cast(Config.Curse[0], sdk.skills.hand.Right, unit); + + return Attack.Result.SUCCESS; } - Skill.cast(Config.Curse[1], sdk.skills.hand.Right, unit); + if (Config.Curse[1] > 0 && !unit.isSpecial && this.canCurse(unit, Config.Curse[1])) { + if (unit.distance > 25 || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, 25, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + Skill.cast(Config.Curse[1], sdk.skills.hand.Right, unit); - return Attack.Result.SUCCESS; + return Attack.Result.SUCCESS; + } } } - } - // 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)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 - && Attack.checkResist(unit, Config.AttackSkill[5]) - && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { - timedSkill = Config.AttackSkill[5]; - } + if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[5], classid)) { + timedSkill = 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, classid)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 - && Attack.checkResist(unit, Config.AttackSkill[6]) - && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { - untimedSkill = Config.AttackSkill[6]; - } + if (Attack.checkResist(unit, checkSkill) && Attack.validSpot(unit.x, unit.y, checkSkill, classid)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y, Config.AttackSkill[6], classid)) { + untimedSkill = Config.AttackSkill[6]; + } - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 - && Skill.getManaCost(timedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 - && Skill.getManaCost(untimedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } - let result = this.doCast(unit, timedSkill, untimedSkill); + let result = this.doCast(unit, timedSkill, untimedSkill); - if (result === 1) { - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - } else if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); + if (result === 1) { + 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 || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - if (!unit) return Attack.Result.SUCCESS; + while (unit.attackable) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + } + 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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + Config.ActiveSummon && this.raiseArmy(); + this.explodeCorpses(unit); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); } - Config.ActiveSummon && this.raiseArmy(); - this.explodeCorpses(unit); - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob && this.doCast(closeMob, timedSkill, untimedSkill); + return Attack.Result.SUCCESS; } - return Attack.Result.SUCCESS; - } + return result; + }, + + afterAttack: function () { + Precast.doPrecast(false); + this.raiseArmy(); + this.novaTick = 0; + }, + + /** + * @param {Monster} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills + */ + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); + + let walk; + let classid = unit.classid; + + // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros + this.isArmyFull() && this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { + switch (timedSkill) { + case sdk.skills.PoisonNova: + if (!this.novaTick || getTickCount() - this.novaTick > Config.PoisonNovaDelay * 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.novaTick = getTickCount(); + } + } - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - this.raiseArmy(); - this.novaTick = 0; - }, - - /** - * @param {Monster} unit - * @param {number} timedSkill - * @param {number} untimedSkill - * @returns {number} 0 - fail, 1 - success, 2 - no valid attack skills - */ - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); - - let walk; - let classid = unit.classid; - - // Check for bodies to exploit for CorpseExplosion before committing to an attack for non-summoner type necros - this.isArmyFull() && this.checkCorpseNearMonster(unit) && this.explodeCorpses(unit); - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill))) { - 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 > 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.novaTick = getTickCount(); - } - } + delay(300); - break; - case sdk.skills.Summoner: // Pure Summoner - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged)) { + break; + default: + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { return Attack.Result.FAILED; } - } - delay(300); + if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + // Allow short-distance walking for melee skills + let walk = (Skill.getRange(timedSkill) < 4 + && unit.distance < 10 + && !checkCollision(me, unit, sdk.collision.BlockWall) + ); - break; - default: - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } + } + + !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + + break; + } + } + + if (untimedSkill > -1) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { return Attack.Result.FAILED; } - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + if (unit.distance > Skill.getRange(untimedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { // Allow short-distance walking for melee skills - let walk = (Skill.getRange(timedSkill) < 4 + walk = (Skill.getRange(untimedSkill) < 4 && unit.distance < 10 && !checkCollision(me, unit, sdk.collision.BlockWall) ); - if (!Attack.getIntoPosition(unit, Skill.getRange(timedSkill), sdk.collision.Ranged, walk)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { return Attack.Result.FAILED; } } - !unit.dead && Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - - break; - } - } + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - if (untimedSkill > -1) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { - return Attack.Result.FAILED; + return Attack.Result.SUCCESS; } - 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) - ); + Misc.poll(() => !me.skillDelay, 1000, 40); - if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; - } + // Delay for Poison Nova + while (timedSkill === sdk.skills.PoisonNova + && this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { + delay(40); } - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - return Attack.Result.SUCCESS; - } + }, - Misc.poll(() => !me.skillDelay, 1000, 40); + /** + * @param {number} range + */ + raiseArmy: function (range = 25) { + let tick, count; - // Delay for Poison Nova - while (timedSkill === sdk.skills.PoisonNova - && this.novaTick && getTickCount() - this.novaTick < Config.PoisonNovaDelay * 1000) { - delay(40); - } + this.setArmySize(); - return Attack.Result.SUCCESS; - }, - - /** - * @param {number} range - */ - raiseArmy: function (range = 25) { - let tick, count; - - this.setArmySize(); - - for (let i = 0; i < 3; i += 1) { - let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); - let corpseList = []; + for (let i = 0; i < 3; i += 1) { + let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); + let corpseList = []; - if (corpse) { - do { - // within casting distance - if (corpse.distance <= range && this.checkCorpse(corpse)) { - corpseList.push(copyUnit(corpse)); - } - } while (corpse.getNext()); - } - - while (corpseList.length > 0) { - corpse = corpseList.shift(); - - // should probably have a way to priortize which ones we summon first - if (me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) { - if (!Skill.cast(sdk.skills.RaiseSkeleton, sdk.skills.hand.Right, corpse)) { - return false; - } + if (corpse) { + do { + // within casting distance + if (corpse.distance <= range && this.checkCorpse(corpse)) { + corpseList.push(copyUnit(corpse)); + } + } while (corpse.getNext()); + } - count = me.getMinionCount(sdk.summons.type.Skeleton); - tick = getTickCount(); + while (corpseList.length > 0) { + corpse = corpseList.shift(); - while (getTickCount() - tick < 200) { - if (me.getMinionCount(sdk.summons.type.Skeleton) > count) { - break; + // should probably have a way to priortize which ones we summon first + if (me.getMinionCount(sdk.summons.type.Skeleton) < this.maxSkeletons) { + if (!Skill.cast(sdk.skills.RaiseSkeleton, sdk.skills.hand.Right, corpse)) { + return false; } - delay(10); - } - } else if (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) { - if (!Skill.cast(sdk.skills.RaiseSkeletalMage, sdk.skills.hand.Right, corpse)) { - return false; - } + count = me.getMinionCount(sdk.summons.type.Skeleton); + tick = getTickCount(); - count = me.getMinionCount(sdk.summons.type.SkeletonMage); - tick = getTickCount(); + while (getTickCount() - tick < 200) { + if (me.getMinionCount(sdk.summons.type.Skeleton) > count) { + break; + } - while (getTickCount() - tick < 200) { - if (me.getMinionCount(sdk.summons.type.SkeletonMage) > count) { - break; + delay(10); } - - delay(10); - } - } else if (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives) { - if (this.checkCorpse(corpse, true)) { - console.log("Reviving " + corpse.name); - - if (!Skill.cast(sdk.skills.Revive, sdk.skills.hand.Right, corpse)) { + } else if (me.getMinionCount(sdk.summons.type.SkeletonMage) < this.maxMages) { + if (!Skill.cast(sdk.skills.RaiseSkeletalMage, sdk.skills.hand.Right, corpse)) { return false; } - count = me.getMinionCount(sdk.summons.type.Revive); + count = me.getMinionCount(sdk.summons.type.SkeletonMage); tick = getTickCount(); while (getTickCount() - tick < 200) { - if (me.getMinionCount(sdk.summons.type.Revive) > count) { + if (me.getMinionCount(sdk.summons.type.SkeletonMage) > count) { break; } delay(10); } + } else if (me.getMinionCount(sdk.summons.type.Revive) < this.maxRevives) { + if (this.checkCorpse(corpse, true)) { + console.log("Reviving " + corpse.name); + + if (!Skill.cast(sdk.skills.Revive, sdk.skills.hand.Right, corpse)) { + return false; + } + + count = me.getMinionCount(sdk.summons.type.Revive); + tick = getTickCount(); + + while (getTickCount() - tick < 200) { + if (me.getMinionCount(sdk.summons.type.Revive) > count) { + break; + } + + delay(10); + } + } + } else { + return true; } - } else { - return true; } } - } - return true; - }, + return true; + }, - /** - * @param {Monster} unit - */ - explodeCorpses: function (unit) { - if (Config.ExplodeCorpses === 0 || unit.dead) return false; + /** + * @param {Monster} unit + */ + explodeCorpses: function (unit) { + if (Config.ExplodeCorpses === 0 || unit.dead) return false; - let corpseList = []; - let range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); - let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); + let corpseList = []; + 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()); + 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()); + // 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 (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 (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 (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { - delay(me.ping + 1); + 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(); + } 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 (corpse) { + me.overhead("Exploding: " + corpse.classid + " " + corpse.name); - if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { - delay(200); + if (Skill.cast(Config.ExplodeCorpses, sdk.skills.hand.Right, corpse)) { + delay(200); + } } + } else { + break; } - } else { - break; } } } - } - return true; - }, + return true; + }, - /** - * @param {Monster} monster - * @param {number} range - */ - checkCorpseNearMonster: function (monster, range) { - let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); + /** + * @param {Monster} monster + * @param {number} range + */ + checkCorpseNearMonster: function (monster, range) { + let corpse = Game.getMonster(-1, sdk.monsters.mode.Dead); - // Assume CorpseExplosion if no range specified - if (range === undefined) { - range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); - } + // Assume CorpseExplosion if no range specified + if (range === undefined) { + range = Math.floor((me.getSkill(Config.ExplodeCorpses, sdk.skills.subindex.SoftPoints) + 7) / 3); + } - if (corpse) { - do { - if (getDistance(corpse, monster) <= range) { - return true; - } - } while (corpse.getNext()); - } + if (corpse) { + do { + if (getDistance(corpse, monster) <= range) { + return true; + } + } while (corpse.getNext()); + } - return false; - }, - - /** - * @param {Unit} unit - * @param {boolean} revive - */ - checkCorpse: function (unit, revive = false) { - if (!unit || unit.mode !== sdk.monsters.mode.Dead) return false; - - let baseId = getBaseStat("monstats", unit.classid, "baseid"), badList = [312, 571]; - let states = [ - sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, - sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect - ]; - - if (revive - && (unit.isSpecial || badList.includes(baseId) - || (Config.ReviveUnstackable && getBaseStat("monstats2", baseId, "sizex") === 3))) { return false; - } + }, + + /** + * @param {Unit} unit + * @param {boolean} revive + */ + checkCorpse: function (unit, revive = false) { + if (!unit || unit.mode !== sdk.monsters.mode.Dead) return false; + + let baseId = getBaseStat("monstats", unit.classid, "baseid"), badList = [312, 571]; + let states = [ + sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, + sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect + ]; + + if (revive + && (unit.isSpecial || badList.includes(baseId) + || (Config.ReviveUnstackable && getBaseStat("monstats2", baseId, "sizex") === 3))) { + return false; + } - if (!getBaseStat("monstats2", baseId, revive ? "revive" : "corpseSel")) return false; + if (!getBaseStat("monstats2", baseId, revive ? "revive" : "corpseSel")) return false; - return !!(unit.distance <= 25 - && !checkCollision(me, unit, sdk.collision.Ranged) - && states.every(state => !unit.getState(state))); - } -}; + return !!(unit.distance <= 25 + && !checkCollision(me, unit, sdk.collision.Ranged) + && states.every(state => !unit.getState(state))); + } + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Attacks/Paladin.js b/d2bs/kolbot/libs/core/Attacks/Paladin.js index ae0e096e4..5df622631 100644 --- a/d2bs/kolbot/libs/core/Attacks/Paladin.js +++ b/d2bs/kolbot/libs/core/Attacks/Paladin.js @@ -5,231 +5,222 @@ * */ -const ClassAttack = { - attackAuras: [sdk.skills.HolyFire, sdk.skills.HolyFreeze, sdk.skills.HolyShock], - - /** - * @param {Unit} unit - * @param {boolean} [preattack] - * @returns {AttackResult} - */ - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - 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; +(function (module) { + module.exports = { + attackAuras: [sdk.skills.HolyFire, sdk.skills.HolyFreeze, sdk.skills.HolyShock], + + /** + * @param {Unit} unit + * @param {boolean} [preattack] + * @returns {AttackResult} + */ + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + 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.ChargeCast.skill > -1) { - Attack.doChargeCast(unit); - } + if (Config.ChargeCast.skill > -1) { + Attack.doChargeCast(unit); + } - if (preattack) { - let preAttackResult = Attack.doPreAttack(unit); - if (preAttackResult !== Attack.Result.NOOP) { - return preAttackResult; + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; + } } - } - let mercRevive = 0; - let [attackSkill, aura] = [-1, -1]; - const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let mercRevive = 0; + let [attackSkill, aura] = [-1, -1]; + const index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - if (Attack.getCustomAttack(unit)) { - [attackSkill, aura] = Attack.getCustomAttack(unit); - } else { - attackSkill = Config.AttackSkill[index]; - aura = Config.AttackSkill[index + 1]; - } + 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) { + let _check = (this.attackAuras.includes(Config.AttackSkill[6]) + ? Config.AttackSkill[6] + : Config.AttackSkill[5]); + if (Attack.checkResist(unit, _check)) { + 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]; - // 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) { - let _check = (this.attackAuras.includes(Config.AttackSkill[6]) - ? Config.AttackSkill[6] - : Config.AttackSkill[5]); - if (Attack.checkResist(unit, _check)) { + // 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]; + } else if ( + Config.AttackSkill.length === 9 + && Config.AttackSkill[7] > -1 + && Attack.checkResist(unit, Config.AttackSkill[7]) + ) { + attackSkill = Config.AttackSkill[7]; + aura = Config.AttackSkill[8]; } } } - } 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]; - } else if ( - Config.AttackSkill.length === 9 - && Config.AttackSkill[7] > -1 - && Attack.checkResist(unit, Config.AttackSkill[7]) - ) { - attackSkill = Config.AttackSkill[7]; - aura = Config.AttackSkill[8]; - } + + // Low mana skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(attackSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + [attackSkill, aura] = Config.LowManaSkill; } - } - // Low mana skill - if (Config.LowManaSkill[0] > -1 - && Skill.getManaCost(attackSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[0])) { - [attackSkill, aura] = Config.LowManaSkill; - } + let result = this.doCast(unit, attackSkill, aura); - let result = this.doCast(unit, attackSkill, aura); + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); - if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { - let merc = me.getMerc(); + while (unit.attackable) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + } + if (!unit) return Attack.Result.SUCCESS; - while (unit.attackable) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - 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 && !!spot.x && Pather.walkTo(spot.x, spot.y); + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + !!closeMob && this.doCast(closeMob, attackSkill, aura); } - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - !!closeMob && this.doCast(closeMob, attackSkill, aura); + return Attack.Result.SUCCESS; } - return Attack.Result.SUCCESS; - } - - return result; - }, + return result; + }, - afterAttack: function () { - Precast.doPrecast(false); + afterAttack: function () { + Precast.doPrecast(false); - // only proceed with other checks if we can use redemption and the config values aren't 0 - if (Skill.canUse(sdk.skills.Redemption) && Config.Redemption.some(v => v > 0)) { - if ((me.hpPercent < Config.Redemption[0] || me.mpPercent < Config.Redemption[1]) - && Attack.checkNearCorpses(me) > 2 && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { - delay(1500); + // only proceed with other checks if we can use redemption and the config values aren't 0 + if (Skill.canUse(sdk.skills.Redemption) && Config.Redemption.some(v => v > 0)) { + if ((me.hpPercent < Config.Redemption[0] || me.mpPercent < Config.Redemption[1]) + && Attack.checkNearCorpses(me) > 2 && Skill.setSkill(sdk.skills.Redemption, sdk.skills.hand.Right)) { + delay(1500); + } } - } + + /** + * @todo add config options for these and possibly add to Pather.walkTo + */ + // if (Skill.canUse(sdk.skills.Cleansing) + // && ([sdk.states.AmplifyDamage, sdk.states.Decrepify].some(s => me.getState(s)) || me.hpPercent < 70 && me.getState(sdk.states.Poison)) + // && !me.checkForMobs({range: 12, coll: sdk.collision.BlockWall}) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { + // me.overhead("Delaying for a second to get rid of Poison"); + // Misc.poll(() => (![sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Poison].some(s => me.getState(s)) || 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); + // } + }, /** - * @todo add config options for these and possibly add to Pather.walkTo + * @param {Monster} unit + * @param {number} attackSkill + * @param {number} aura */ - // if (Skill.canUse(sdk.skills.Cleansing) - // && ([sdk.states.AmplifyDamage, sdk.states.Decrepify].some(s => me.getState(s)) || me.hpPercent < 70 && me.getState(sdk.states.Poison)) - // && !me.checkForMobs({range: 12, coll: sdk.collision.BlockWall}) && Skill.setSkill(sdk.skills.Cleansing, sdk.skills.hand.Right)) { - // me.overhead("Delaying for a second to get rid of Poison"); - // Misc.poll(() => (![sdk.states.AmplifyDamage, sdk.states.Decrepify, sdk.states.Poison].some(s => me.getState(s)) || 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); - // } - }, - - 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; - Config.TeleSwitch && me.switchToPrimary(); - - 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, Skill.getHand(attackSkill), unit); - - return Attack.Result.SUCCESS; - } + 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; + Config.TeleSwitch && me.switchToPrimary(); + + 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, Skill.getHand(attackSkill), unit); - // 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.SUCCESS; } - return Attack.Result.FAILED; - } + // 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]); + } - if (unit.distance > 9 || !unit.attackable) return Attack.Result.SUCCESS; + return Attack.Result.FAILED; + } - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + if (unit.distance > 9 || !unit.attackable) return Attack.Result.SUCCESS; - for (let i = 0; i < 3; i += 1) { - Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - if (!unit.attackable || unit.distance > 9 || unit.isPlayer) { - break; - } - } + for (let i = 0; i < 3; i += 1) { + Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); - return Attack.Result.SUCCESS; - case sdk.skills.HolyBolt: - if (unit.distance > Skill.getRange(attackSkill) + 3 || CollMap.checkColl(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged)) { - return Attack.Result.FAILED; + if (!unit.attackable || unit.distance > 9 || unit.isPlayer) { + break; + } } - } - CollMap.reset(); - - if (unit.distance > Skill.getRange(attackSkill) || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.FriendlyRanged, true)) { - return Attack.Result.FAILED; + return Attack.Result.SUCCESS; + case sdk.skills.HolyBolt: + if (unit.distance > Skill.getRange(attackSkill) + 3 || CollMap.checkColl(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } } - } - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); - } + CollMap.reset(); - return Attack.Result.SUCCESS; - case sdk.skills.FistoftheHeavens: - if (!me.skillDelay) { - if (unit.distance > Skill.getRange(attackSkill) - || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { + if (unit.distance > Skill.getRange(attackSkill) || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.FriendlyRanged, true)) { return Attack.Result.FAILED; } @@ -238,161 +229,185 @@ const ClassAttack = { if (!unit.dead) { aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + } - return Attack.Result.SUCCESS; + return Attack.Result.SUCCESS; + case sdk.skills.FistoftheHeavens: + if (!me.skillDelay) { + if (unit.distance > Skill.getRange(attackSkill) + || CollMap.checkColl(me, unit, sdk.collision.FriendlyRanged, 2)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.FriendlyRanged, true)) { + return Attack.Result.FAILED; + } + } + + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, Skill.getHand(attackSkill), 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)) { + 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); - } + 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 (Skill.getRange(attackSkill) < 4 && !Attack.validSpot(unit.x, unit.y, attackSkill, unit.classid)) { - return Attack.Result.FAILED; - } + break; + 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)) { - let walk = (attackSkill !== sdk.skills.Smite - && Skill.getRange(attackSkill) < 4 - && unit.distance < 10 - && !checkCollision(me, unit, sdk.collision.BlockWall) - ); + if (unit.distance > Skill.getRange(attackSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { + let walk = (attackSkill !== sdk.skills.Smite + && Skill.getRange(attackSkill) < 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, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; + // walk short distances instead of tele for melee attacks. teleport if failed to walk + if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } } - } - if (!unit.dead) { - aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); - Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + if (!unit.dead) { + aura > -1 && Skill.setSkill(aura, sdk.skills.hand.Right); + Skill.cast(attackSkill, Skill.getHand(attackSkill), unit); + } + + return Attack.Result.SUCCESS; } + Misc.poll(() => !me.skillDelay, 1000, 40); + return Attack.Result.SUCCESS; - } + }, - Misc.poll(() => !me.skillDelay, 1000, 40); + /** + * @param {Monster} unit + * @returns {boolean} + */ + dollAvoid: function (unit) { + let distance = 14; - return Attack.Result.SUCCESS; - }, + 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); - dollAvoid: function (unit) { - let distance = 14; + 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 } }); + } + } - 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); + return false; + }, - 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 } }); + /** + * @param {Monster | Player} unit + * @returns {boolean} + */ + getHammerPosition: function (unit) { + let x, y, positions; + const canTele = Pather.canTeleport(); + const baseId = getBaseStat("monstats", unit.classid, "baseid"); + let size = getBaseStat("monstats2", baseId, "sizex"); + + // in case base stat returns something outrageous + (typeof size !== "number" || size < 1 || size > 3) && (size = 3); + + switch (unit.type) { + case sdk.unittype.Player: + x = unit.x; + y = unit.y; + 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; } - } - - return false; - }, - - /** - * @param {Monster | Player} unit - * @returns {boolean} - */ - getHammerPosition: function (unit) { - let x, y, positions; - const canTele = Pather.canTeleport(); - const baseId = getBaseStat("monstats", unit.classid, "baseid"); - let size = getBaseStat("monstats2", baseId, "sizex"); - - // in case base stat returns something outrageous - (typeof size !== "number" || size < 1 || size > 3) && (size = 3); - - switch (unit.type) { - case sdk.unittype.Player: - x = unit.x; - y = unit.y; - 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 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.x, check.y].distance < 1) { - return true; - } else if (!canTele && ([check.x, check.y].distance < 1 - && !CollMap.checkColl(unit, check, sdk.collision.BlockWalk, 0)) - || ([check.x, check.y].distance <= 4 && me.getMobCount(6) > 2)) { - return true; + if (canTele && [check.x, check.y].distance < 1) { + return true; + } else if (!canTele && ([check.x, check.y].distance < 1 + && !CollMap.checkColl(unit, check, sdk.collision.BlockWalk, 0)) + || ([check.x, check.y].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] }; + 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, sdk.collision.BlockWalk, 0)) { - if (this.reposition(check.x, check.y)) return true; + if (Attack.validSpot(check.x, check.y) + && !CollMap.checkColl(unit, check, sdk.collision.BlockWalk, 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); + // console.debug("Failed to find a hammer position for " + unit.name + " distance from me: " + unit.distance); - return false; - }, + return false; + }, - reposition: function (x, y) { - if (typeof x !== "number" || typeof y !== "number") return false; - const node = { x: x, y: y }; - if (node.distance > 0) { - if (Pather.useTeleport()) { - node.distance > 30 - ? Pather.moveTo(x, y) - : Pather.teleportTo(x, y, 3); - } else { - if (node.distance <= 4) { - Misc.click(0, 0, x, y); - } else if (!CollMap.checkColl(me, node, sdk.collision.BlockWalk, 3)) { - Pather.walkTo(x, y); + /** + * @param {number} x + * @param {number} y + */ + reposition: function (x, y) { + if (typeof x !== "number" || typeof y !== "number") return false; + const node = { x: x, y: y }; + if (node.distance > 0) { + if (Pather.useTeleport()) { + node.distance > 30 + ? Pather.moveTo(x, y) + : Pather.teleportTo(x, y, 3); } else { - // don't clear while trying to reposition - Pather.move(node, { clearSettings: { allowClearing: false } }); - } + if (node.distance <= 4) { + Misc.click(0, 0, x, y); + } else if (!CollMap.checkColl(me, node, sdk.collision.BlockWalk, 3)) { + Pather.walkTo(x, y); + } else { + // don't clear while trying to reposition + Pather.move(node, { clearSettings: { allowClearing: false } }); + } - delay(200); + delay(200); + } } - } - return true; - } -}; + return true; + } + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index d8b07c403..18a0babda 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -5,267 +5,269 @@ * */ -/** @implements {ClassAttack} */ -const ClassAttack = { - /** @param {Monster} unit */ - decideSkill: function (unit) { - let skills = { timed: -1, untimed: -1 }; - if (!unit || !unit.attackable) return skills; - - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - let classid = unit.classid; - - // 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]; - } +(function (module) { + module.exports = { + /** @param {Monster} unit */ + decideSkill: function (unit) { + let skills = { timed: -1, untimed: -1 }; + if (!unit || !unit.attackable) return skills; + + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + let classid = unit.classid; + + // 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]; + } - // 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, classid)) { - 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]; - } + // 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, classid)) { + 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; - }, - - /** - * @param {Monster} unit - * @param {boolean} preattack - * @returns {AttackResult} - */ - doAttack: function (unit, preattack = false) { - if (!unit) return Attack.Result.SUCCESS; - Config.TeleSwitch && me.switchToPrimary(); - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - if (Town.visitTown()) { - console.log("mercwatch"); - - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - console.debug("Lost reference to unit"); - return Attack.Result.SUCCESS; + return skills; + }, + + /** + * @param {Monster} unit + * @param {boolean} preattack + * @returns {AttackResult} + */ + doAttack: function (unit, preattack = false) { + if (!unit) return Attack.Result.SUCCESS; + Config.TeleSwitch && me.switchToPrimary(); + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + if (Town.visitTown()) { + console.log("mercwatch"); + + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + console.debug("Lost reference to unit"); + return Attack.Result.SUCCESS; + } } } - } - // Keep Energy Shield active - if (Skill.canUse(sdk.skills.EnergyShield) && !me.getState(sdk.states.EnergyShield)) { - Skill.cast(sdk.skills.EnergyShield, sdk.skills.hand.Right); - } - - // Keep Thunder-Storm active - if (Skill.canUse(sdk.skills.ThunderStorm) && !me.getState(sdk.states.ThunderStorm)) { - Skill.cast(sdk.skills.ThunderStorm, sdk.skills.hand.Right); - } + // Keep Energy Shield active + if (Skill.canUse(sdk.skills.EnergyShield) && !me.getState(sdk.states.EnergyShield)) { + Skill.cast(sdk.skills.EnergyShield, sdk.skills.hand.Right); + } - if (Config.ChargeCast.skill > -1) { - Attack.doChargeCast(unit); - } + // Keep Thunder-Storm active + if (Skill.canUse(sdk.skills.ThunderStorm) && !me.getState(sdk.states.ThunderStorm)) { + Skill.cast(sdk.skills.ThunderStorm, sdk.skills.hand.Right); + } - if (preattack) { - let preAttackResult = Attack.doPreAttack(unit); - if (preAttackResult !== Attack.Result.NOOP) { - return preAttackResult; + if (Config.ChargeCast.skill > -1) { + Attack.doChargeCast(unit); } - } - let useStatic = (Config.StaticList.length > 0 - && Config.CastStatic < 100 - && Skill.canUse(sdk.skills.StaticField) - && Attack.checkResist(unit, "lightning")); - let idCheck = function (id) { - if (unit) { - switch (true) { - case typeof id === "number" && unit.classid && unit.classid === id: - case typeof id === "string" && unit.name && unit.name.toLowerCase() === id.toLowerCase(): - case typeof id === "function" && id(unit): - return true; - default: - return false; + if (preattack) { + let preAttackResult = Attack.doPreAttack(unit); + if (preAttackResult !== Attack.Result.NOOP) { + return preAttackResult; } } - return false; - }; - - // Static - needs to be re-done - if (useStatic && Config.StaticList.some(id => idCheck(id)) && unit.hpPercent > Config.CastStatic) { - let staticRange = Skill.getRange(sdk.skills.StaticField); - let casts = 0; - - while (!me.dead && unit.hpPercent > Config.CastStatic && unit.attackable) { - if (unit.distance > staticRange || checkCollision(me, unit, sdk.collision.Ranged)) { - if (!Attack.getIntoPosition(unit, staticRange, sdk.collision.Ranged)) { - return Attack.Result.FAILED; + let useStatic = (Config.StaticList.length > 0 + && Config.CastStatic < 100 + && Skill.canUse(sdk.skills.StaticField) + && Attack.checkResist(unit, "lightning")); + let idCheck = function (id) { + if (unit) { + switch (true) { + case typeof id === "number" && unit.classid && unit.classid === id: + case typeof id === "string" && unit.name && unit.name.toLowerCase() === id.toLowerCase(): + case typeof id === "function" && id(unit): + return true; + default: + return false; } } - // if we fail to cast or we've casted 3 or more times - do something else - if (!Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right) || casts >= 3) { - break; - } else { - casts++; + return false; + }; + + // Static - needs to be re-done + if (useStatic && Config.StaticList.some(id => idCheck(id)) && unit.hpPercent > Config.CastStatic) { + let staticRange = Skill.getRange(sdk.skills.StaticField); + let casts = 0; + + while (!me.dead && unit.hpPercent > Config.CastStatic && unit.attackable) { + if (unit.distance > staticRange || checkCollision(me, unit, sdk.collision.Ranged)) { + if (!Attack.getIntoPosition(unit, staticRange, sdk.collision.Ranged)) { + return Attack.Result.FAILED; + } + } + + // if we fail to cast or we've casted 3 or more times - do something else + if (!Skill.cast(sdk.skills.StaticField, sdk.skills.hand.Right) || casts >= 3) { + break; + } else { + casts++; + } } - } - // re-check mob after static - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - console.debug("Lost reference to unit"); - return Attack.Result.SUCCESS; + // re-check mob after static + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + console.debug("Lost reference to unit"); + return Attack.Result.SUCCESS; + } } - } - let skills = this.decideSkill(unit); - let result = this.doCast(unit, skills.timed, skills.untimed); + 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(); - let mercRevive = 0; + if (result === Attack.Result.CANTATTACK && Attack.canTeleStomp(unit)) { + let merc = me.getMerc(); + let mercRevive = 0; - while (unit.attackable) { - if (!unit || !copyUnit(unit).x) { - unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); - } - if (!unit) return Attack.Result.SUCCESS; + while (unit.attackable) { + if (!unit || !copyUnit(unit).x) { + unit = Misc.poll(() => Game.getMonster(-1, -1, gid), 1000, 80); + } + 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) > 7) { + Pather.moveToUnit(unit); + + let spot = Attack.findSafeSpot(unit, 10, 5, 9); + !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + } + + let closeMob = Attack.getNearestMonster({ skipGid: gid }); + + if (!!closeMob) { + let findSkill = this.decideSkill(closeMob); + if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { + (Skill.haveTK && Packet.telekinesis(unit)); + } + } } - if (!!merc && getDistance(merc, unit) > 7) { - Pather.moveToUnit(unit); + return Attack.Result.SUCCESS; + } - let spot = Attack.findSafeSpot(unit, 10, 5, 9); - !!spot && !!spot.x && Pather.walkTo(spot.x, spot.y); + return result; + }, + + afterAttack: function () { + console.debug("Sorceress afterAttack: doPrecast"); + Precast.doPrecast(false); + }, + + /** + * @param {Monster | Player} unit + * @param {number} timedSkill + * @param {number} untimedSkill + * @returns {AttackResult} 0 - fail, 1 - success, 2 - no valid attack skills + */ + doCast: function (unit, timedSkill = -1, untimedSkill = -1) { + // 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; + Config.TeleSwitch && me.switchToPrimary(); + + let walk, noMana = false; + let classid = unit.classid; + + if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill)) && Skill.getManaCost(timedSkill) < me.mp) { + if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { + return Attack.Result.FAILED; } - let closeMob = Attack.getNearestMonster({ skipGid: gid }); - - if (!!closeMob) { - let findSkill = this.decideSkill(closeMob); - if (this.doCast(closeMob, findSkill.timed, findSkill.untimed) !== Attack.Result.SUCCESS) { - (Skill.haveTK && Packet.telekinesis(unit)); + 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; } } - } - - return Attack.Result.SUCCESS; - } - return result; - }, - - afterAttack: function () { - Precast.doPrecast(false); - }, - - /** - * @param {Monster | Player} unit - * @param {number} timedSkill - * @param {number} untimedSkill - * @returns {AttackResult} 0 - fail, 1 - success, 2 - no valid attack skills - */ - doCast: function (unit, timedSkill = -1, untimedSkill = -1) { - // 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; - Config.TeleSwitch && me.switchToPrimary(); - - let walk, noMana = false; - let classid = unit.classid; - - if (timedSkill > -1 && (!me.skillDelay || !Skill.isTimed(timedSkill)) && Skill.getManaCost(timedSkill) < me.mp) { - if (Skill.getRange(timedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, timedSkill, classid)) { - return Attack.Result.FAILED; + if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { + Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); + } + return Attack.Result.SUCCESS; + } else { + noMana = !me.skillDelay; } - if (unit.distance > Skill.getRange(timedSkill) || checkCollision(me, unit, sdk.collision.Ranged)) { - // Allow short-distance walking for melee skills - walk = (Skill.getRange(timedSkill) < 4 + if (untimedSkill > -1 && Skill.getManaCost(untimedSkill) < me.mp) { + if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { + 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(timedSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; + if (!Attack.getIntoPosition(unit, Skill.getRange(untimedSkill), sdk.collision.Ranged, walk)) { + return Attack.Result.FAILED; + } } - } - if (!unit.dead && !checkCollision(me, unit, sdk.collision.Ranged)) { - Skill.cast(timedSkill, Skill.getHand(timedSkill), unit); - } - return Attack.Result.SUCCESS; - } else { - noMana = !me.skillDelay; - } + !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); - if (untimedSkill > -1 && Skill.getManaCost(untimedSkill) < me.mp) { - if (Skill.getRange(untimedSkill) < 4 && !Attack.validSpot(unit.x, unit.y, untimedSkill, classid)) { - return Attack.Result.FAILED; + return Attack.Result.SUCCESS; + } else { + noMana = true; } - 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; - } - } + // don't count as failed + if (noMana) return Attack.Result.NEEDMANA; - !unit.dead && Skill.cast(untimedSkill, Skill.getHand(untimedSkill), unit); + Misc.poll(() => !me.skillDelay, 1000, 40); return Attack.Result.SUCCESS; - } else { - noMana = true; } - - // don't count as failed - if (noMana) return Attack.Result.NEEDMANA; - - Misc.poll(() => !me.skillDelay, 1000, 40); - - return Attack.Result.SUCCESS; - } -}; + }; +})(module); diff --git a/d2bs/kolbot/libs/core/Attacks/Wereform.js b/d2bs/kolbot/libs/core/Attacks/Wereform.js index 3f013d74f..f37ab1148 100644 --- a/d2bs/kolbot/libs/core/Attacks/Wereform.js +++ b/d2bs/kolbot/libs/core/Attacks/Wereform.js @@ -6,176 +6,185 @@ */ // todo - handle a Bear necro summonmancer - -const ClassAttack = (function () { - const baseLL = me.getStat(sdk.stats.LifeLeech); - const baseED = me.getStat(sdk.stats.DamagePercent); - const feralBoost = () => ( - ((Math.floor(me.getSkill(sdk.skills.FeralRage, sdk.skills.subindex.SoftPoints) / 2) + 3) * 4) + baseLL - ); - const maulBoost = () => ( - ((Math.floor(me.getSkill(sdk.skills.Maul, sdk.skills.subindex.SoftPoints) / 2) + 3) * 20) + baseED - ); - - const wereform = { - rage: 0, - maul: 0, - duration: 0, // todo - handle duration, if we are about to lose our form, recast it before attacking - }; - - return { - feralBoost: 0, - maulBoost: 0, - - doAttack: function (unit, preattack) { - if (!unit) return Attack.Result.SUCCESS; - let gid = unit.gid; - - if (Config.MercWatch && me.needMerc()) { - console.debug("mercwatch"); - - if (Town.visitTown()) { - if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { - return Attack.Result.SUCCESS; // lost reference to the mob we were attacking +(function (module) { + module.exports = (function () { + const baseLL = me.getStat(sdk.stats.LifeLeech); + const baseED = me.getStat(sdk.stats.DamagePercent); + const feralBoost = () => ( + ((Math.floor(me.getSkill(sdk.skills.FeralRage, sdk.skills.subindex.SoftPoints) / 2) + 3) * 4) + baseLL + ); + const maulBoost = () => ( + ((Math.floor(me.getSkill(sdk.skills.Maul, sdk.skills.subindex.SoftPoints) / 2) + 3) * 20) + baseED + ); + + const wereform = { + rage: 0, + maul: 0, + duration: 0, // todo - handle duration, if we are about to lose our form, recast it before attacking + }; + + return { + feralBoost: 0, + maulBoost: 0, + + /** + * @param {Monster | Player} unit + * @param {boolean} preattack + */ + doAttack: function (unit, preattack) { + if (!unit) return Attack.Result.SUCCESS; + let gid = unit.gid; + + if (Config.MercWatch && me.needMerc()) { + console.debug("mercwatch"); + + if (Town.visitTown()) { + if (!unit || !copyUnit(unit).x || !Game.getMonster(-1, -1, gid) || unit.dead) { + return Attack.Result.SUCCESS; // lost reference to the mob we were attacking + } } } - } - if (!wereform.rage && Config.AttackSkill.includes(sdk.skills.FeralRage)) { - // amount of life leech with max rage - wereform.rage = feralBoost(); - } - - if (!wereform.maul && Config.AttackSkill.includes(sdk.skills.Maul)) { - // amount of enhanced damage with max maul - wereform.maul = maulBoost(); - } + if (!wereform.rage && Config.AttackSkill.includes(sdk.skills.FeralRage)) { + // amount of life leech with max rage + wereform.rage = feralBoost(); + } - Skill.shapeShift(Config.Wereform); - - if (((Config.AttackSkill[0] === sdk.skills.FeralRage - && (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < wereform.rage)) - || (Config.AttackSkill[0] === sdk.skills.Maul - && (!me.getState(sdk.states.Maul) || me.getStat(sdk.stats.DamagePercent) < wereform.maul)) - || (Config.AttackSkill[0] === sdk.skills.ShockWave && !unit.isSpecial && !unit.getState(sdk.states.Stunned)) - || (preattack && Config.AttackSkill[0] > 0)) - && Attack.checkResist(unit, Config.AttackSkill[0]) - && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0])) - && (Skill.wereFormCheck(Config.AttackSkill[0]) || !me.shapeshifted)) { - if (unit.distance > Skill.getRange(Config.AttackSkill[0]) - || checkCollision(me, unit, sdk.collision.WallOrRanged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.WallOrRanged, true)) { - return Attack.Result.FAILED; - } + if (!wereform.maul && Config.AttackSkill.includes(sdk.skills.Maul)) { + // amount of enhanced damage with max maul + wereform.maul = maulBoost(); } - Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); + Skill.shapeShift(Config.Wereform); + + if (((Config.AttackSkill[0] === sdk.skills.FeralRage + && (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < wereform.rage)) + || (Config.AttackSkill[0] === sdk.skills.Maul + && (!me.getState(sdk.states.Maul) || me.getStat(sdk.stats.DamagePercent) < wereform.maul)) + || (Config.AttackSkill[0] === sdk.skills.ShockWave && !unit.isSpecial && !unit.getState(sdk.states.Stunned)) + || (preattack && Config.AttackSkill[0] > 0)) + && Attack.checkResist(unit, Config.AttackSkill[0]) + && (!me.skillDelay || !Skill.isTimed(Config.AttackSkill[0])) + && (Skill.wereFormCheck(Config.AttackSkill[0]) || !me.shapeshifted)) { + if (unit.distance > Skill.getRange(Config.AttackSkill[0]) + || checkCollision(me, unit, sdk.collision.WallOrRanged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(Config.AttackSkill[0]), sdk.collision.WallOrRanged, true)) { + return Attack.Result.FAILED; + } + } - return Attack.Result.SUCCESS; - } + Skill.cast(Config.AttackSkill[0], Skill.getHand(Config.AttackSkill[0]), unit); - // Rebuff Armageddon - if (Skill.canUse(sdk.skills.Armageddon) && !me.getState(sdk.states.Armageddon)) { - Skill.cast(sdk.skills.Armageddon, sdk.skills.hand.Right); - } + return Attack.Result.SUCCESS; + } - let timedSkill = -1; - let untimedSkill = -1; - let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; + // Rebuff Armageddon + if (Skill.canUse(sdk.skills.Armageddon) && !me.getState(sdk.states.Armageddon)) { + Skill.cast(sdk.skills.Armageddon, sdk.skills.hand.Right); + } - // Get timed skill - let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; + let timedSkill = -1; + let untimedSkill = -1; + let index = (unit.isSpecial || unit.isPlayer) ? 1 : 3; - if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { - timedSkill = checkSkill; - } else if (Config.AttackSkill[5] > -1 - && Attack.checkResist(unit, Config.AttackSkill[5]) - && Attack.validSpot(unit.x, unit.y)) { - timedSkill = Config.AttackSkill[5]; - } + // Get timed skill + let checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[0] : Config.AttackSkill[index]; - // Get untimed skill - checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; + if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { + timedSkill = checkSkill; + } else if (Config.AttackSkill[5] > -1 + && Attack.checkResist(unit, Config.AttackSkill[5]) + && Attack.validSpot(unit.x, unit.y)) { + timedSkill = Config.AttackSkill[5]; + } - if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { - untimedSkill = checkSkill; - } else if (Config.AttackSkill[6] > -1 - && Attack.checkResist(unit, Config.AttackSkill[6]) - && Attack.validSpot(unit.x, unit.y)) { - untimedSkill = Config.AttackSkill[6]; - } + // Get untimed skill + checkSkill = Attack.getCustomAttack(unit) ? Attack.getCustomAttack(unit)[1] : Config.AttackSkill[index + 1]; - // eval skills - switch (true) { - case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.FeralRage: - if (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < wereform.rage) { - timedSkill = sdk.skills.FeralRage; + if (Attack.checkResist(unit, checkSkill) && Skill.wereFormCheck(checkSkill) && Attack.validSpot(unit.x, unit.y)) { + untimedSkill = checkSkill; + } else if (Config.AttackSkill[6] > -1 + && Attack.checkResist(unit, Config.AttackSkill[6]) + && Attack.validSpot(unit.x, unit.y)) { + untimedSkill = Config.AttackSkill[6]; } - break; - case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.Rabies: - case timedSkill === sdk.skills.FireClaws && untimedSkill === sdk.skills.Rabies: - if (!unit.getState(sdk.states.Rabies)) { - timedSkill = sdk.skills.Rabies; - } + // eval skills + switch (true) { + case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.FeralRage: + if (!me.getState(sdk.states.FeralRage) || me.getStat(sdk.stats.LifeLeech) < wereform.rage) { + timedSkill = sdk.skills.FeralRage; + } - break; - case timedSkill === sdk.skills.ShockWave && untimedSkill === sdk.skills.Maul: - case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.ShockWave: - case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.FireClaws: - if (!me.getState(sdk.states.Maul)) { - timedSkill = sdk.skills.Maul; - } + break; + case timedSkill === sdk.skills.Fury && untimedSkill === sdk.skills.Rabies: + case timedSkill === sdk.skills.FireClaws && untimedSkill === sdk.skills.Rabies: + if (!unit.getState(sdk.states.Rabies)) { + timedSkill = sdk.skills.Rabies; + } - break; - } + break; + case timedSkill === sdk.skills.ShockWave && untimedSkill === sdk.skills.Maul: + case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.ShockWave: + case timedSkill === sdk.skills.Maul && untimedSkill === sdk.skills.FireClaws: + if (!me.getState(sdk.states.Maul)) { + timedSkill = sdk.skills.Maul; + } - // Low mana timed skill - if (Config.LowManaSkill[0] > -1 - && Skill.getManaCost(timedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[0])) { - timedSkill = Config.LowManaSkill[0]; - } + break; + } - // Low mana untimed skill - if (Config.LowManaSkill[1] > -1 - && Skill.getManaCost(untimedSkill) > me.mp - && Attack.checkResist(unit, Config.LowManaSkill[1])) { - untimedSkill = Config.LowManaSkill[1]; - } + // Low mana timed skill + if (Config.LowManaSkill[0] > -1 + && Skill.getManaCost(timedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[0])) { + timedSkill = Config.LowManaSkill[0]; + } - // use our secondary skill if we can't use our primary - let choosenSkill = (Skill.isTimed(timedSkill) && me.skillDelay && untimedSkill > -1 ? untimedSkill : timedSkill); + // Low mana untimed skill + if (Config.LowManaSkill[1] > -1 + && Skill.getManaCost(untimedSkill) > me.mp + && Attack.checkResist(unit, Config.LowManaSkill[1])) { + untimedSkill = Config.LowManaSkill[1]; + } - return this.doCast(unit, choosenSkill); - }, + // use our secondary skill if we can't use our primary + let choosenSkill = (Skill.isTimed(timedSkill) && me.skillDelay && untimedSkill > -1 ? untimedSkill : timedSkill); - afterAttack: function () { - Precast.doPrecast(false); - }, + return this.doCast(unit, choosenSkill); + }, - // Returns: 0 - fail, 1 - success, 2 - no valid attack skills - doCast: function (unit, skill) { - // unit reference no longer valid or it died - if (!unit || unit.dead) return Attack.Result.SUCCESS; - // No valid skills can be found - if (skill < 0) return Attack.Result.CANTATTACK; + afterAttack: function () { + Precast.doPrecast(false); + }, - if (Skill.getRange(skill) < 4 && !Attack.validSpot(unit.x, unit.y)) { - return Attack.Result.FAILED; - } + /** + * @param {Monster | Player} unit + * @param {number} skill + * @returns {AttackResult} + */ + doCast: function (unit, skill) { + // unit reference no longer valid or it died + if (!unit || unit.dead) return Attack.Result.SUCCESS; + // No valid skills can be found + if (skill < 0) return Attack.Result.CANTATTACK; - if (unit.distance > Skill.getRange(skill) || checkCollision(me, unit, sdk.collision.WallOrRanged)) { - if (!Attack.getIntoPosition(unit, Skill.getRange(skill), sdk.collision.WallOrRanged, true)) { + if (Skill.getRange(skill) < 4 && !Attack.validSpot(unit.x, unit.y)) { return Attack.Result.FAILED; } - } - unit.attackable && Skill.cast(skill, Skill.getHand(skill), unit); + if (unit.distance > Skill.getRange(skill) || checkCollision(me, unit, sdk.collision.WallOrRanged)) { + if (!Attack.getIntoPosition(unit, Skill.getRange(skill), sdk.collision.WallOrRanged, true)) { + return Attack.Result.FAILED; + } + } + + unit.attackable && Skill.cast(skill, Skill.getHand(skill), unit); - Misc.poll(() => !me.skillDelay, 1000, 40); + Misc.poll(() => !me.skillDelay, 1000, 40); - return Attack.Result.SUCCESS; - } - }; -})(); + return Attack.Result.SUCCESS; + } + }; + })(); +})(module); diff --git a/d2bs/kolbot/libs/core/ClassAttack.js b/d2bs/kolbot/libs/core/ClassAttack.js new file mode 100644 index 000000000..af33ff423 --- /dev/null +++ b/d2bs/kolbot/libs/core/ClassAttack.js @@ -0,0 +1,27 @@ +/** +* @filename ClassAttack.js +* @author theBGuy +* @desc Class specific attack sequences +* +*/ + +// each ClassAttack functionality is loaded into this object when it's needed +// for the actual function files @see core/Attacks/ +const ClassAttack = (function () { + const LazyLoader = require("../modules/LazyLoader"); + + /** + * @type {Map} + */ + const modulePathMap = new Map([ + [sdk.player.class.Amazon.toString(), "./Attacks/Amazon"], + [sdk.player.class.Assassin.toString(), "./Attacks/Assassin"], + [sdk.player.class.Barbarian.toString(), "./Attacks/Barbarian"], + [sdk.player.class.Druid.toString(), "./Attacks/Druid"], + [sdk.player.class.Necromancer.toString(), "./Attacks/Necromancer"], + [sdk.player.class.Paladin.toString(), "./Attacks/Paladin"], + [sdk.player.class.Sorceress.toString(), "./Attacks/Sorceress"], + ]); + + return LazyLoader(modulePathMap); +})(); diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index e18d5de0f..43ecdef62 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -165,10 +165,10 @@ break; case sdk.player.class.Assassin: if (Config.UseTraps) { - let check = ClassAttack.checkTraps({ x: 15094, y: 5028 }); + let check = ClassAttack[me.classid].checkTraps({ x: 15094, y: 5028 }); if (check) { - return ClassAttack.placeTraps({ x: 15094, y: 5028 }, 5); + return ClassAttack[me.classid].placeTraps({ x: 15094, y: 5028 }, 5); } } diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index b0e5498e4..e76edf76e 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -475,7 +475,7 @@ const Precast = (function () { Precast.summon(Config.Golem, sdk.summons.type.Golem); } - Config.ActiveSummon && ClassAttack.raiseArmy(); + Config.ActiveSummon && ClassAttack[me.classid].raiseArmy(); break; case sdk.player.class.Paladin: diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 27d83384f..687023499 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -1531,6 +1531,7 @@ Druid: 5, Assassin: 6, + /** @param {number} classid */ nameOf: function (classid) { if (classid === undefined || typeof classid !== "number") return false; if (classid < 0 || classid > 6) return false; diff --git a/d2bs/kolbot/libs/scripts/Andariel.js b/d2bs/kolbot/libs/scripts/Andariel.js index bcc729d47..39df5ebac 100644 --- a/d2bs/kolbot/libs/scripts/Andariel.js +++ b/d2bs/kolbot/libs/scripts/Andariel.js @@ -14,7 +14,7 @@ const Andariel = new Runnable( Config.MFLeader && Pather.makePortal() && say("kill " + sdk.monsters.Andariel); for (let i = 0; i < 300 && target.attackable; i += 1) { - ClassAttack.doAttack(target); + ClassAttack[me.classid].doAttack(target); target.distance <= 10 && Pather.moveTo(me.x > 22548 ? 22535 : 22560, 9520); } diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index 13f6707de..ef5b51ba0 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -78,7 +78,7 @@ const AutoBaal = new Runnable( const longRangeSupport = function () { switch (me.classid) { case sdk.player.class.Necromancer: - ClassAttack.raiseArmy(50); + ClassAttack[me.classid].raiseArmy(50); if (Config.Curse[1] > 0) { let monster = Game.getMonster(); @@ -86,7 +86,7 @@ const AutoBaal = new Runnable( if (monster) { do { if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged) - && monster.curseable && !monster.isSpecial && ClassAttack.canCurse(monster, Config.Curse[1])) { + && monster.curseable && !monster.isSpecial && ClassAttack[me.classid].canCurse(monster, Config.Curse[1])) { Skill.cast(Config.Curse[1], sdk.skills.hand.Right, monster); } } while (monster.getNext()); @@ -95,8 +95,8 @@ const AutoBaal = new Runnable( break; case sdk.player.class.Assassin: - if (Config.UseTraps && ClassAttack.checkTraps({ x: 15095, y: 5037 })) { - ClassAttack.placeTraps({ x: 15095, y: 5037 }, 5); + if (Config.UseTraps && ClassAttack[me.classid].checkTraps({ x: 15095, y: 5037 })) { + ClassAttack[me.classid].placeTraps({ x: 15095, y: 5037 }, 5); } break; @@ -141,7 +141,7 @@ const AutoBaal = new Runnable( if (Config.AttackSkill[index] > -1 && Attack.checkResist(monster, Attack.getSkillElement(Config.AttackSkill[index]))) { - ClassAttack.doCast(monster, Config.AttackSkill[index], Config.AttackSkill[index + 1]); + ClassAttack[me.classid].doCast(monster, Config.AttackSkill[index], Config.AttackSkill[index + 1]); } else { monList.shift(); } diff --git a/d2bs/kolbot/libs/scripts/Duriel.js b/d2bs/kolbot/libs/scripts/Duriel.js index c8690e4cc..900da32c8 100644 --- a/d2bs/kolbot/libs/scripts/Duriel.js +++ b/d2bs/kolbot/libs/scripts/Duriel.js @@ -18,7 +18,7 @@ const Duriel = new Runnable( } for (let i = 0; i < 300 && target.attackable; i += 1) { - ClassAttack.doAttack(target); + ClassAttack[me.classid].doAttack(target); target.distance <= 10 && Pather.moveTo(22638, me.y < target.y ? 15722 : 15693); } diff --git a/d2bs/kolbot/libs/scripts/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js index 8f4d22ca1..28c720862 100644 --- a/d2bs/kolbot/libs/scripts/Mephisto.js +++ b/d2bs/kolbot/libs/scripts/Mephisto.js @@ -35,7 +35,7 @@ const Mephisto = new Runnable( } } - if (ClassAttack.doAttack(meph) < 2) { + if (ClassAttack[me.classid].doAttack(meph) < 2) { break; } diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index fd6b9a666..cc1de4daf 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -1,4 +1,3 @@ -export {}; declare global { type DamageType = "physical" | "fire" | "lightning" | "magic" | "cold" | "poison" | "holybolt"; @@ -8,14 +7,7 @@ declare global { CANTATTACK: 2; // need to fix the ambiguity between this result and Failed NEEDMANA: 3; } - interface ClassAttack { - doAttack(unit: Monster, preattack?: boolean): AttackResult; - afterAttack(any?: any): void; - doCast(unit: Monster, timedSkill: number, untimedSkill: number): AttackResult; - // Self defined - decideSkill(unit: Monster, skipSkill?: number[]): { timed: number; untimed: number }; - } namespace Attack { const infinity: boolean; const auradin: boolean; @@ -125,3 +117,4 @@ declare global { function doChargeCast(unit: Monster): boolean; } } +export {}; diff --git a/d2bs/kolbot/sdk/types/ClassAttack.d.ts b/d2bs/kolbot/sdk/types/ClassAttack.d.ts new file mode 100644 index 000000000..12808cb97 --- /dev/null +++ b/d2bs/kolbot/sdk/types/ClassAttack.d.ts @@ -0,0 +1,27 @@ +type AttackModules = + | SDK["player"]["class"]["Amazon"] + | SDK["player"]["class"]["Assassin"] + | SDK["player"]["class"]["Barbarian"] + | SDK["player"]["class"]["Druid"] + | SDK["player"]["class"]["Sorceress"] + | SDK["player"]["class"]["Paladin"] + | SDK["player"]["class"]["Necromancer"] + | "Wereform"; + +declare global { + interface IClassAttack { + load: (moduleName: AttackModules) => unknown; + + [sdk.player.class.Amazon]: typeof import("../../libs/core/Attacks/Amazon"); + [sdk.player.class.Assassin]: typeof import("../../libs/core/Attacks/Assassin"); + [sdk.player.class.Barbarian]: typeof import("../../libs/core/Attacks/Barbarian"); + [sdk.player.class.Druid]: typeof import("../../libs/core/Attacks/Druid"); + [sdk.player.class.Sorceress]: typeof import("../../libs/core/Attacks/Sorceress"); + [sdk.player.class.Paladin]: typeof import("../../libs/core/Attacks/Paladin"); + [sdk.player.class.Necromancer]: typeof import("../../libs/core/Attacks/Necromancer"); + Wereform: typeof import("../../libs/core/Attacks/Wereform"); + } + + const ClassAttack: IClassAttack; +} +export {}; diff --git a/d2bs/kolbot/threads/AntiHostile.js b/d2bs/kolbot/threads/AntiHostile.js index 136c3e863..3ef16b3d1 100644 --- a/d2bs/kolbot/threads/AntiHostile.js +++ b/d2bs/kolbot/threads/AntiHostile.js @@ -299,10 +299,10 @@ function main() { while (!findPlayer() && hostiles.length > 0) { if (Config.UseTraps) { - check = ClassAttack.checkTraps({ x: 15099, y: 5242, classid: 544 }); + check = ClassAttack[me.classid].checkTraps({ x: 15099, y: 5242, classid: 544 }); if (check) { - ClassAttack.placeTraps({ x: 15099, y: 5242, classid: 544 }, 5); + ClassAttack[me.classid].placeTraps({ x: 15099, y: 5242, classid: 544 }, 5); } } @@ -381,7 +381,7 @@ function main() { break; } - ClassAttack.doAttack(player, false); + ClassAttack[me.classid].doAttack(player, false); // Specific attack additions switch (me.classid) { From 5aedc344a27d3c4ce72fa00f1a4d9e0325bc4d37 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:36:59 -0500 Subject: [PATCH 654/758] Disable nodeActions in doRandomPrecast when moving to spot away from wp --- d2bs/kolbot/libs/core/Precast.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index e76edf76e..27ecad0a3 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -594,7 +594,7 @@ const Precast = (function () { let spot = Pather.findSpotAtDistance(wp, 8); if (spot) { - Pather.move(spot); + Pather.move(spot, { allowNodeActions: false }); } Precast.doPrecast(force); } From 4cd68016e37e82ad30b308f5bfedcbd2ad2a9156 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 24 Nov 2025 18:41:33 -0500 Subject: [PATCH 655/758] Update ToolsThread.js - Remove old townChicken variable, we don't use the separate thread anymore --- d2bs/kolbot/threads/ToolsThread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index ed1fccf62..1e346e7e7 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -72,7 +72,7 @@ function main () { const keyEvent = function (key) { switch (key) { case sdk.keys.PauseBreak: // pause running threads - Common.Toolsthread.togglePause(townChicken); + Common.Toolsthread.togglePause(); break; case sdk.keys.Delete: // quit current game From b9a959d079bfd9d4222f7aa624a5f2902fa07300 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 24 Nov 2025 18:43:04 -0500 Subject: [PATCH 656/758] Add hardDelay and nativeDelay utilities to globals - Introduces global.hardDelay and global.nativeDelay functions for thread blocking and native delay operations. Sometimes we don't want the background work to run, we actually need a delay while the game is loading - Updates type definitions in globals.d.ts and exposes nativeSleep in Worker.js for improved delay handling. --- d2bs/kolbot/libs/globals.js | 31 ++++++++++++++++++++++++++++++ d2bs/kolbot/libs/modules/Worker.js | 1 + d2bs/kolbot/sdk/globals.d.ts | 11 +++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/globals.js b/d2bs/kolbot/libs/globals.js index 00da4f2b4..4e631bc9a 100644 --- a/d2bs/kolbot/libs/globals.js +++ b/d2bs/kolbot/libs/globals.js @@ -166,6 +166,37 @@ if (!global.hasOwnProperty("copyObj")) { }); } +if (!global.hasOwnProperty("hardDelay")) { + /** + * @description blocks the thread for specified milliseconds - use sparingly this does not yield to the other threads so it + * can potentially cause the heartbeat thread to crash + * @param {number} ms + */ + Object.defineProperty(global, "hardDelay", { + value: function (ms) { + let start = getTickCount(); + while (getTickCount() - start < ms) { + // + } + }, + }); +} + +if (!global.hasOwnProperty("nativeDelay")) { + /** + * @description Use sparingly, this calls the original delay function bypassing the background worker checks + * @param {number} ms + */ + Object.defineProperty(global, "nativeDelay", { + value: function (ms) { + if (global.hasOwnProperty("_delay")) { + return global._delay(ms); + } + return delay(ms); + }, + }); +} + /** * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Misc Utils ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index 505485c90..92160fd1b 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -114,6 +114,7 @@ }; global._delay = delay; // The original delay function + global.nativeSleep = delay; /** * Just makes it easier to peform a delay diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 611b697d3..a209568be 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -370,7 +370,7 @@ declare global { readonly classid: number; readonly mode: number; readonly name: string; - readonly act: any; + readonly act: 1 | 2 | 3 | 4 | 5; readonly gid: number; readonly x: number; readonly y: number; @@ -1609,7 +1609,14 @@ declare global { * This method sleeps the caller thread for the duration in ms passed to it * @note Use sparingly, this method stops the background workers on the callers thread */ - function threadSleep(ms: number); + function nativeDelay(ms: number): void; + + /** + * @description blocks the thread for specified milliseconds - use sparingly this does not yield to the other threads so it + * can potentially cause the heartbeat thread to crash + * @param ms + */ + function hardDelay(ms: number): void; /** * Deep merge objects, handling nested properties properly From 53bca985900e26a929ba6a88b3444814535ca6d8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 24 Nov 2025 18:52:13 -0500 Subject: [PATCH 657/758] Add `pickItems` to NodeActions & swap to use nativeDelay for after wp usage - Introduces a pickItems method to NodeAction and adds allowPicking to clearSettings, enabling item picking while pathing. - Use nativeDelay so no background work is run while the game level is loading after using a wp. It was occasionally causing CI's - Also fix pathSettings retry always being set to 10 --- d2bs/kolbot/libs/core/Pather.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 331537c42..5577520a4 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -113,6 +113,18 @@ const NodeAction = { } }, + /** + * Pick items while pathing + * @param {Pick} arg + */ + pickItems: function (arg = {}) { + const pickSettings = Object.assign({}, { + allowPicking: true, + }, arg); + if (!pickSettings.allowPicking) return; + Pickit.pickItems(Config.PickRange / 2); + }, + /** * Open chests while pathing */ @@ -374,6 +386,7 @@ const Pather = { * @property {boolean} [clearSettings.clearPath] * @property {number} [clearSettings.range] * @property {number} [clearSettings.specType] + * @property {boolean} [clearSettings.allowPicking] * @property {Function} [clearSettings.sort] * * @param {PathNode | Unit | PresetUnit} target @@ -406,6 +419,7 @@ const Pather = { clearPath: false, range: 10, specType: 0, + allowPicking: settings.allowPicking, sort: Attack.sortMonsters, }, settings.clearSettings); // set settings.clearSettings equal to the now properly asssigned clearSettings @@ -495,7 +509,7 @@ const Pather = { // node.y = tmpY; // } - if (settings.retry <= 3 && target.distance > useTeleport ? 120 : 60) { + if (settings.retry <= 3 && target.distance > (useTeleport ? 120 : 60)) { settings.retry = 10; } @@ -1570,16 +1584,16 @@ const Pather = { while (getTickCount() - tick < Math.max(Math.round((i + 1) * 1000 / (i / 5 + 1)), pingDelay * 2)) { if (me.area === targetArea) { - delay(1500); + nativeDelay(1500); break MainLoop; } - delay(20); + nativeDelay(20); } while (!me.gameReady) { - delay(1000); + nativeDelay(1000); } // In case lag causes the wp menu to stay open From 11fd8fbf8ee895beb115de1db8e17a0ca80add24 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:00:39 -0500 Subject: [PATCH 658/758] Improve ScriptError handling across core modules - Added explicit checks to rethrow ScriptError exceptions in various core modules to prevent them from being swallowed by generic error handling. Super obnoxious but only way to propagate the error to where we want it - Also enhanced debug logging for shrine interactions and updated Misc.openChests to support custom coordinates. --- d2bs/kolbot/libs/core/Attacks/Assassin.js | 3 ++ d2bs/kolbot/libs/core/Misc.js | 49 +++++++++++++++++++++-- d2bs/kolbot/libs/core/NTItemParser.js | 18 +++++++++ d2bs/kolbot/libs/core/Packet.js | 6 +++ d2bs/kolbot/libs/core/Precast.js | 6 +++ d2bs/kolbot/libs/core/Prototypes.js | 3 ++ d2bs/kolbot/libs/core/Skill.js | 6 +++ d2bs/kolbot/libs/core/Town.js | 42 +++++++++++++++++++ d2bs/kolbot/libs/core/Util.js | 6 +++ 9 files changed, 135 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Assassin.js b/d2bs/kolbot/libs/core/Attacks/Assassin.js index d7d1cacc0..be03ede79 100644 --- a/d2bs/kolbot/libs/core/Attacks/Assassin.js +++ b/d2bs/kolbot/libs/core/Attacks/Assassin.js @@ -237,6 +237,9 @@ return Attack.Result.SUCCESS; }, + /** + * @param {Monster} unit + */ checkTraps: function (unit) { if (!Config.UseTraps || !unit) return false; diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 35ad5d2e9..b8721e00e 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -83,6 +83,9 @@ const Misc = (function () { return true; } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } player = getParty(); if (player) { @@ -440,9 +443,11 @@ const Misc = (function () { /** * @param {number} range + * @param {number} [x=me.x] + * @param {number} [y=me.y] * @returns {boolean} */ - openChests: function (range = 15) { + openChests: function (range = 15, x = me.x, y = me.y) { if (!Config.OpenChests.Enabled) return true; let containers = []; @@ -479,8 +484,9 @@ const Misc = (function () { do { if (unit.name && unit.mode === sdk.objects.mode.Inactive && !seenGids.has(unit.gid) - && getDistance(me.x, me.y, unit.x, unit.y) <= range - && containers.includes(unit.name.toLowerCase())) { + && getDistance(x, y, unit.x, unit.y) <= range + && containers.includes(unit.name.toLowerCase()) + ) { seenGids.add(unit.gid); unitList.push(copyUnit(unit)); } @@ -489,7 +495,7 @@ const Misc = (function () { return unitList; }; - const startPos = new PathNode(me.x, me.y); + const startPos = new PathNode(x, y); let unitList = buildChestList(range); while (unitList.length > 0) { @@ -596,8 +602,23 @@ const Misc = (function () { case sdk.shrines.Experience: return me.charlvl < 99; case sdk.shrines.Skill: + if (Config.DebugMode.Shrines) { + console.debug( + "Skill shrine. Dist: " + walkDistance + + " Last shrine state: " + Misc.lastShrine.state + + " isMyCurrentState: " + Misc.lastShrine.isMyCurrentState() + ); + } return !me.getState(sdk.states.ShrineExperience); case sdk.shrines.ManaRecharge: + if (Config.DebugMode.Shrines) { + console.debug( + "Mana recharge shrine. Dist: " + walkDistance + + " Mana: " + me.mpPercent + "%" + + " Last shrine state: " + Misc.lastShrine.state + + " isMyCurrentState: " + Misc.lastShrine.isMyCurrentState() + ); + } // we only want if its close to us if we can't teleport if (!Pather.useTeleport() && walkDistance > 15) { return false; @@ -605,6 +626,15 @@ const Misc = (function () { // for now, only grab if we have nothing else active return !Misc.lastShrine.state || !me.getState(Misc.lastShrine.state); case sdk.shrines.Stamina: + if (Config.DebugMode.Shrines) { + console.debug( + "Staima shrine. Dist: " + walkDistance + + " Stamina: " + me.staminaPercent + + "% Max: " + me.staminamax + + " Last shrine state: " + Misc.lastShrine.state + + " isMyCurrentState: " + Misc.lastShrine.isMyCurrentState() + ); + } // we only want if its close to us if we can't teleport if ( !Pather.useTeleport() @@ -665,6 +695,14 @@ const Misc = (function () { // TODO: handle armor and combat shrines case sdk.shrines.Armor: case sdk.shrines.Combat: + if (Config.DebugMode.Shrines) { + console.debug( + "Armor/Combat. Last shrine state: " + Misc.lastShrine.state + + " isMyCurrentState: " + Misc.lastShrine.isMyCurrentState() + + " Distance: " + walkDistance + ); + } + // we only want if its close to us if we can't teleport if (!Pather.useTeleport() && walkDistance > 15) { return false; @@ -983,6 +1021,9 @@ const Misc = (function () { check = true; } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } return false; } diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index ab46c3b90..7d11f8bec 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -72,6 +72,9 @@ NTIP.OpenFile = function (filepath, notify) { try { nipfile = File.open(filepath, 0); } catch (fileError) { + if ((e instanceof ScriptError)) { + throw e; + } if (notify) { Misc.errorReport("ÿc1Failed to load NIP: ÿc0" + filename); } @@ -199,6 +202,9 @@ NTIP.generateTierFunc = function (tierType) { } } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } const info = stringArray[i]; Misc.errorReport("ÿc1Pickit Tier (" + tierType + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); } @@ -324,6 +330,9 @@ NTIP.CheckItem = function (item, entryList, verbose) { } } } catch (pickError) { + if ((e instanceof ScriptError)) { + throw e; + } showConsole(); if (!entryList) { @@ -622,6 +631,9 @@ NTIP.ParseLineInt = function (input, info) { // p_result[2].Tier = function(item) { return value }; 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) { + if ((e instanceof ScriptError)) { + throw e; + } throw new Error("ÿc1Pickit Tier (" + keyword + ") error! Line # ÿc2" + info.line + " ÿc1Entry: ÿc0" + info.string + " (" + info.file + ") Error message: " + e.message); } break; @@ -631,6 +643,9 @@ NTIP.ParseLineInt = function (input, info) { } } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } Misc.errorReport(e); return false; @@ -642,6 +657,9 @@ NTIP.ParseLineInt = function (input, info) { try { p_result[i] = (new Function("return function(item) { return " + p_result[i] + ";}")).call(null); // generate function out of } catch (e) { + if ((e instanceof ScriptError)) { + throw 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 diff --git a/d2bs/kolbot/libs/core/Packet.js b/d2bs/kolbot/libs/core/Packet.js index 39d3425a1..cd8060984 100644 --- a/d2bs/kolbot/libs/core/Packet.js +++ b/d2bs/kolbot/libs/core/Packet.js @@ -127,6 +127,9 @@ const Packet = { } } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); } @@ -175,6 +178,9 @@ const Packet = { } } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); } diff --git a/d2bs/kolbot/libs/core/Precast.js b/d2bs/kolbot/libs/core/Precast.js index 27ecad0a3..08074b0bd 100644 --- a/d2bs/kolbot/libs/core/Precast.js +++ b/d2bs/kolbot/libs/core/Precast.js @@ -262,6 +262,9 @@ const Precast = (function () { } return state ? me.getState(state) : true; } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); return false; @@ -603,6 +606,9 @@ const Precast = (function () { } Pather.useWaypoint(returnTo); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); } finally { if (me.area !== returnTo && (!Pather.useWaypoint(returnTo) || !Pather.useWaypoint(sdk.areas.townOf(me.area)))) { diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 501e91711..4a30ec83d 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -986,6 +986,9 @@ Unit.prototype.toCursor = function (usePacket = false) { clickItem(sdk.clicktypes.click.item.Left, this); } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } return false; } diff --git a/d2bs/kolbot/libs/core/Skill.js b/d2bs/kolbot/libs/core/Skill.js index 01e2d2d88..54a8f9bee 100644 --- a/d2bs/kolbot/libs/core/Skill.js +++ b/d2bs/kolbot/libs/core/Skill.js @@ -595,6 +595,9 @@ return me.inTown || (me.mpPercent > 25); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } return false; } }, @@ -748,6 +751,9 @@ try { return item.castChargedSkill(skillId, unit.x, unit.y); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); // maybe rebuild list? // Skill.getCharges(); diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 0e5f01539..90ff6d27c 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -452,6 +452,9 @@ const Town = { console.info(false, "Did " + reason + " at " + npc.name, "initNPC"); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); if (!!e.message && e.message === "Couldn't interact with npc") { @@ -712,6 +715,9 @@ const Town = { try { tome.buy(); } catch (e1) { + if ((e instanceof ScriptError)) { + throw e; + } console.log(e1); // Couldn't buy the tome, don't spam the scrolls return false; @@ -982,6 +988,9 @@ const Town = { item.buy(); } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); } } @@ -1182,6 +1191,9 @@ const Town = { } } } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); return false; @@ -1259,6 +1271,9 @@ const Town = { try { key.buy(true); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); return false; @@ -1688,6 +1703,9 @@ const Town = { console.debug("Open npc menu"); !!getInteractedNPC() && Misc.useMenu(sdk.menu.Trade); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); me.cancelUIFlags(); } @@ -1824,6 +1842,9 @@ const Town = { Item.logger("Sold", item); item.sell() && (sold = true); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); } sold && delay(250); // would a rand delay be better? @@ -1844,6 +1865,9 @@ const Town = { Item.logger("Dropped", item, "clearInventory"); item.drop() && (drop = true); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } console.error(e); } drop && delay(50); @@ -2081,6 +2105,9 @@ const Town = { } } } catch (e) { + if ((e instanceof ScriptError)) { + throw 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.")); @@ -2111,6 +2138,9 @@ const Town = { try { Pather.useWaypoint(sdk.areas.townOfAct(act), wpmenu); } catch (WPError) { + if ((e instanceof ScriptError)) { + throw e; + } throw new Error("Town.goToTown: Failed use WP"); } } @@ -2144,6 +2174,9 @@ const Town = { try { Town.goToTown(); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } return false; } @@ -2156,6 +2189,9 @@ const Town = { try { Pather.usePortal(preArea, me.name); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } throw new Error("Town.visitTown: Failed to go back from town"); } } @@ -2251,6 +2287,9 @@ const Town = { try { Town.goToTown(); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } return false; } } @@ -2289,6 +2328,9 @@ const Town = { try { Pather.usePortal(preArea, me.name); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } throw new Error("Town.visitTown: Failed to go back from town"); } } diff --git a/d2bs/kolbot/libs/core/Util.js b/d2bs/kolbot/libs/core/Util.js index fb2ec0ab5..2253f54d5 100644 --- a/d2bs/kolbot/libs/core/Util.js +++ b/d2bs/kolbot/libs/core/Util.js @@ -368,6 +368,9 @@ * @param {number} id */ const rewriteStack = function (err, area, id) { + if ((err instanceof ScriptError)) { + throw err; + } let stack = err.stack.match(/[^\r\n]+/g); let fileNameAndLine = stack[1].substring(stack[1].lastIndexOf("\\") + 1); let [fileName, line] = fileNameAndLine.split(":"); @@ -616,6 +619,9 @@ try { obj = JSON.parse(msg); } catch (e) { + if ((e instanceof ScriptError)) { + throw e; + } return false; } From f17b53691ea262d4e74c12f6e267a92b8825185b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 25 Nov 2025 01:10:27 -0500 Subject: [PATCH 659/758] Added `FAILED_POSITION` attack result & updated Attack.clear to return AttackResult - Added FAILED_POSITION and NOOP to Attack.Result for more granular result reporting. - Updated clear method to return specific AttackResult values and requeue monsters when position fails. - Refactored Barbarian attack logic to use FAILED_POSITION. - Deprecated Attack.openChests in favor of Misc.openChests. Minor formatting and debug output changes. --- d2bs/kolbot/libs/core/Attack.js | 30 +++++++++++++++++----- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 6 ++--- d2bs/kolbot/libs/scripts/AutoBaal.js | 10 ++++++-- d2bs/kolbot/sdk/types/Attack.d.ts | 2 ++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index c00ced041..fd5ef4332 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -28,6 +28,7 @@ const Attack = { CANTATTACK: 2, // need to fix the ambiguity between this result and Failed NEEDMANA: 3, NOOP: 4, // used for clearing, if we didn't find any monsters to clear it's not exactly a success or fail + FAILED_POSITION: 5, }, /** * Track bosses killed @@ -802,7 +803,7 @@ const Attack = { * @param {(a: T, b: T) => number} [sortfunc] * @param {boolean} [pickit] * @param {(unit: Monster) => boolean} [shouldAttackCb] - * @returns {boolean} + * @returns {AttackResult} * @todo change to passing an object */ clear: function (range, spectype, bossId, sortfunc, pickit = true, shouldAttackCb = () => true) { @@ -810,7 +811,9 @@ const Attack = { delay(40); } - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) return false; + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { + return Attack.Result.FAILED; + } range === undefined && (range = 25); spectype === undefined && (spectype = 0); @@ -818,7 +821,9 @@ const Attack = { sortfunc === undefined && (sortfunc = false); !sortfunc && (sortfunc = this.sortMonsters); - if (typeof (range) !== "number") throw new Error("Attack.clear: range must be a number."); + if (typeof (range) !== "number") { + throw new Error("Attack.clear: range must be a number."); + } /** @type {Map 0 && attackCount < Config.MaxAttackCount) { - if (me.dead) return false; + if (me.dead) return Attack.Result.FAILED; boss && (({ orgx, orgy } = { orgx: boss.x, orgy: boss.y })); monsterList.sort(sortfunc); @@ -950,6 +955,14 @@ const Attack = { _currMon.attacks += 1; attackCount += 1; + + if (result === this.Result.FAILED_POSITION && _currMon.attacks < 15) { + // push to the end of the list to try later + monsterList.push(monsterList.shift()); + console.debug("ÿc1Requeuing " + target.name + " " + target.gid + " " + _currMon.attacks); + continue; + } + const isSpecial = target.isSpecial; const secAttack = me.barbarian ? (isSpecial ? 2 : 4) : 5; const checkSkill = Config.AttackSkill[isSpecial ? 1 : 3]; @@ -1034,7 +1047,7 @@ const Attack = { if (attackCount > 0) { ClassAttack[me.classid].afterAttack(pickit); - this.openChests(range, orgx, orgy); + Misc.openChests(range, orgx, orgy); pickit && 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 @@ -1049,10 +1062,12 @@ const Attack = { ); } else { console.log("ÿc7Clear ÿc0:: ÿc1Failed to clear ÿc0:: " + (!!boss.name ? boss.name : bossId)); + + return Attack.Result.FAILED; } } - return true; + return attackCount > 0 ? Attack.Result.SUCCESS : Attack.Result.NOOP; }, /** @@ -1268,7 +1283,7 @@ const Attack = { if (attackCount > 0) { ClassAttack[me.classid].afterAttack(true); - this.openChests(Config.OpenChests.Range); + Misc.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 @@ -1793,6 +1808,7 @@ const Attack = { }, /** + * @deprecated - Use Misc.openChests instead * @description Open chests when clearing * @param {number} range * @param {number} [x=me.x] diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index 34e22d5a8..a5f6ab0c9 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -105,7 +105,7 @@ */ 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; + return Attack.Result.FAILED_POSITION; } } @@ -124,7 +124,7 @@ ); if (!Attack.getIntoPosition(unit, Skill.getRange(attackSkill), sdk.collision.Ranged, walk)) { - return Attack.Result.FAILED; + return Attack.Result.FAILED_POSITION; } } @@ -244,7 +244,7 @@ // if (!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)); + // console.debug("Failed to hork " + JSON.stringify(corpse) + " at " + getAreaName(me.area)); } } } diff --git a/d2bs/kolbot/libs/scripts/AutoBaal.js b/d2bs/kolbot/libs/scripts/AutoBaal.js index ef5b51ba0..63d2531f0 100644 --- a/d2bs/kolbot/libs/scripts/AutoBaal.js +++ b/d2bs/kolbot/libs/scripts/AutoBaal.js @@ -85,8 +85,14 @@ const AutoBaal = new Runnable( if (monster) { do { - if (monster.attackable && monster.distance < 50 && !checkCollision(me, monster, sdk.collision.Ranged) - && monster.curseable && !monster.isSpecial && ClassAttack[me.classid].canCurse(monster, Config.Curse[1])) { + if ( + monster.attackable + && monster.distance < 50 + && !checkCollision(me, monster, sdk.collision.Ranged) + && monster.curseable + && !monster.isSpecial + && ClassAttack[me.classid].canCurse(monster, Config.Curse[1]) + ) { Skill.cast(Config.Curse[1], sdk.skills.hand.Right, monster); } } while (monster.getNext()); diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index cc1de4daf..4031e1012 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -6,6 +6,8 @@ declare global { SUCCESS: 1; CANTATTACK: 2; // need to fix the ambiguity between this result and Failed NEEDMANA: 3; + NOOP: 4; // used for clearing, if we didn't find any monsters to clear it's not exactly a success or fail + FAILED_POSITION: 5; } namespace Attack { From 4193264067fcab2acf9aaa6094558031fb1bfeab Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 00:20:25 -0500 Subject: [PATCH 660/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 4492d3fa4..d0323de03 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 4492d3fa4ac137217d502601c6969b5ed014c41a +Subproject commit d0323de03e41a80b5276ed4ab8c062ffdef4f0e7 From 639745163a652fa9da7326e8e749e2d8b892b506 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 00:24:32 -0500 Subject: [PATCH 661/758] Add ladder check for AutoMule.getInfo - Also log to console the muleProfile being started --- d2bs/kolbot/libs/systems/automule/AutoMule.js | 62 +++++++++++++++---- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index 6108aa460..c8da7cea3 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -36,10 +36,21 @@ const AutoMule = { getInfo: function () { let info; + /** @param {muleObj} muleObj */ + const handleLadderCheck = function (muleObj) { + if (muleObj.ladder) { + return me.ladder; + } + return !me.ladder; + }; + for (let i in this.Mules) { if (this.Mules.hasOwnProperty(i)) { for (let profile of this.Mules[i].enabledProfiles) { - if (String.isEqual(profile, "all") || String.isEqual(profile, me.profile)) { + if ( + String.isEqual(profile, me.profile) + || (String.isEqual(profile, "all") && handleLadderCheck(this.Mules[i])) + ) { !info && (info = {}); info.muleInfo = this.Mules[i]; @@ -52,7 +63,10 @@ const AutoMule = { for (let i in this.TorchAnniMules) { if (this.TorchAnniMules.hasOwnProperty(i)) { for (let profile of this.TorchAnniMules[i].enabledProfiles) { - if (String.isEqual(profile, "all") || String.isEqual(profile, me.profile)) { + if ( + String.isEqual(profile, me.profile) + || (String.isEqual(profile, "all") && handleLadderCheck(this.TorchAnniMules[i])) + ) { !info && (info = {}); info.torchMuleInfo = this.TorchAnniMules[i]; @@ -66,14 +80,29 @@ const AutoMule = { }, muleCheck: function () { - let info = this.getInfo(); + const info = this.getInfo(); - if (info && info.hasOwnProperty("muleInfo")) { - let items = this.getMuleItems(); + /** + * @param {unknown} info + * @returns {info is {muleInfo: muleObj}} + */ + function hasMuleInfo(info) { + return info && info.hasOwnProperty("muleInfo"); + } - if (info.muleInfo.hasOwnProperty("usedStashTrigger") && info.muleInfo.hasOwnProperty("usedInventoryTrigger") - && Storage.Inventory.UsedSpacePercent() >= info.muleInfo.usedInventoryTrigger - && Storage.Stash.UsedSpacePercent() >= info.muleInfo.usedStashTrigger && items.length > 0) { + if (hasMuleInfo(info)) { + const items = this.getMuleItems(); + const { muleInfo } = info; + + if (Object.hasOwn(muleInfo, "usedStashTrigger") && Object.hasOwn(muleInfo, "usedInventoryTrigger") + && Storage.Inventory.UsedSpacePercent() >= muleInfo.usedInventoryTrigger + && Storage.Stash.UsedSpacePercent() >= muleInfo.usedStashTrigger + && items.length > 0 + ) { + console.debug( + "usedStashAmount: " + Storage.Stash.UsedSpacePercent() + "%" + + " usedInventoryAmount: " + Storage.Inventory.UsedSpacePercent() + "%" + ); D2Bot.printToConsole("MuleCheck triggered!", sdk.colors.D2Bot.DarkGold); return true; @@ -106,7 +135,9 @@ const AutoMule = { }, outOfGameCheck: function () { - if (!this.check && !this.torchAnniCheck) return false; + if (!this.check && !this.torchAnniCheck) { + return false; + } let muleObj = this.getMule(); if (!muleObj) return false; @@ -126,7 +157,7 @@ const AutoMule = { } if (muleObj.continuousMule) { - D2Bot.printToConsole("Starting mule.", sdk.colors.D2Bot.DarkGold); + D2Bot.printToConsole("Starting mule profile " + muleObj.muleProfile, sdk.colors.D2Bot.DarkGold); D2Bot.start(muleObj.muleProfile); } else { D2Bot.printToConsole( @@ -264,16 +295,21 @@ const AutoMule = { let status = "muling"; // Single player - if (!me.gamename) return false; + if (!me.gamename) { + return false; + } let info = this.getInfo(); // Profile is not a part of AutoMule - if (!info) return false; + if (!info) { + return false; + } // Profile is not in mule or torch mule game if (!((info.hasOwnProperty("muleInfo") && String.isEqual(me.gamename, info.muleInfo.muleGameName[0])) - || (info.hasOwnProperty("torchMuleInfo") && String.isEqual(me.gamename, info.torchMuleInfo.muleGameName[0])))) { + || (info.hasOwnProperty("torchMuleInfo") && String.isEqual(me.gamename, info.torchMuleInfo.muleGameName[0]))) + ) { return false; } From dce7793424e2163861d69138626a5ff96b61f291 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 00:59:44 -0500 Subject: [PATCH 662/758] Deprecate generic attack methods in ClassAttack - Marked doAttack, afterAttack, and doCast as deprecated in IClassAttack. Developers are advised to use the specific class attack modules instead for improved clarity and maintainability. --- d2bs/kolbot/sdk/types/ClassAttack.d.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/d2bs/kolbot/sdk/types/ClassAttack.d.ts b/d2bs/kolbot/sdk/types/ClassAttack.d.ts index 12808cb97..c20426fbc 100644 --- a/d2bs/kolbot/sdk/types/ClassAttack.d.ts +++ b/d2bs/kolbot/sdk/types/ClassAttack.d.ts @@ -20,6 +20,19 @@ declare global { [sdk.player.class.Paladin]: typeof import("../../libs/core/Attacks/Paladin"); [sdk.player.class.Necromancer]: typeof import("../../libs/core/Attacks/Necromancer"); Wereform: typeof import("../../libs/core/Attacks/Wereform"); + + /** + * @deprecated Use the specific class attack modules instead. ClassAttack[me.classid].doAttack(...) + */ + doAttack: (unit: Monster | Player, preattack?: boolean) => AttackResult; + /** + * @deprecated Use the specific class attack modules instead. ClassAttack[me.classid].afterAttack() + */ + afterAttack: () => void; + /** + * @deprecated Use the specific class attack modules instead. ClassAttack[me.classid].doCast(...) + */ + doCast: (unit: Monster | Player, timedSkill: number, untimedSkill: number) => AttackResult; } const ClassAttack: IClassAttack; From 4990c8f2215f30c747cdca9f252be8b54a34e7f7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 01:02:33 -0500 Subject: [PATCH 663/758] Replace Attack.openChests with Misc.openChests - Updated Mephisto.js and Radament.js to use Misc.openChests instead of Attack.openChests for opening chests. --- d2bs/kolbot/libs/scripts/Mephisto.js | 6 +++--- d2bs/kolbot/libs/scripts/Radament.js | 2 +- d2bs/kolbot/sdk/types/Attack.d.ts | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Mephisto.js b/d2bs/kolbot/libs/scripts/Mephisto.js index 28c720862..b39f9d11d 100644 --- a/d2bs/kolbot/libs/scripts/Mephisto.js +++ b/d2bs/kolbot/libs/scripts/Mephisto.js @@ -136,9 +136,9 @@ const Mephisto = new Runnable( Pickit.pickItems(); if (Config.OpenChests.Enabled) { - Pather.moveTo(17572, 8011) && Attack.openChests(5); - Pather.moveTo(17572, 8125) && Attack.openChests(5); - Pather.moveTo(17515, 8061) && Attack.openChests(5); + Pather.moveTo(17572, 8011) && Misc.openChests(5); + Pather.moveTo(17572, 8125) && Misc.openChests(5); + Pather.moveTo(17515, 8061) && Misc.openChests(5); } if (Config.Mephisto.TakeRedPortal) { diff --git a/d2bs/kolbot/libs/scripts/Radament.js b/d2bs/kolbot/libs/scripts/Radament.js index 987330b86..4e8e7b2d8 100644 --- a/d2bs/kolbot/libs/scripts/Radament.js +++ b/d2bs/kolbot/libs/scripts/Radament.js @@ -17,7 +17,7 @@ const Radament = new Runnable( Attack.kill(sdk.monsters.Radament); Pickit.pickItems(); - Attack.openChests(20); + Misc.openChests(20); return true; }, diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 4031e1012..271e8714a 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -68,6 +68,7 @@ declare global { function clearLevel(spectype?: number): boolean; function sortMonsters(unitA: Unit, unitB: Unit): boolean; function validSpot(x: number, y: number, skill?: number, unitid?: number): boolean; + /** @deprecated Use Misc.openChests instead */ function openChests(range: number, x?: number, y?: number): boolean; function buildMonsterList(): [] | Monster[]; function findSafeSpot( From a976573c2b9bdd553d994dfd546d415593060944 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:56:04 -0500 Subject: [PATCH 664/758] Remove the idle delay for pickItems to stop stutter stepping in pather --- d2bs/kolbot/libs/core/Pickit.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index e9bb2a11c..cbe8ad251 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -600,9 +600,9 @@ const Pickit = { }; // why wait for idle? - while (!me.idle) { - delay(40); - } + // while (!me.idle) { + // delay(40); + // } let item = Game.getItem(); From 221124a75610044da208d261af2dba1d3f6101df Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:33:39 -0500 Subject: [PATCH 665/758] Refactor location handling to use shared run function + track last 5 oog locations - Replaced custom locationAction.run implementations in multiple bot scripts with a shared run function from libs/oog/Locations.js. The run function now tracks recent locations in Starter.lastLocation and improves logging. - Updated OOG.js and globals.d.ts to support lastLocation tracking and adjusted login logic to use this history. - Added handler to prevent resetting inGame status if disconnected from lobby, this was preventing muling from happening --- d2bs/kolbot/D2BotChannel.dbj | 17 +- d2bs/kolbot/D2BotCharRefresher.dbj | 17 +- d2bs/kolbot/D2BotCleaner.dbj | 241 ++++++++++++++--------------- d2bs/kolbot/D2BotFollow.dbj | 17 +- d2bs/kolbot/D2BotGameAction.dbj | 17 +- d2bs/kolbot/D2BotLead.dbj | 16 +- d2bs/kolbot/D2BotMule.dbj | 17 +- d2bs/kolbot/D2BotMuleLog.dbj | 17 +- d2bs/kolbot/libs/OOG.js | 6 +- d2bs/kolbot/libs/oog/Locations.js | 20 +++ d2bs/kolbot/sdk/globals.d.ts | 1 + 11 files changed, 163 insertions(+), 223 deletions(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 8433b08a8..344a0c12f 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -61,7 +61,8 @@ const locationAction = (function () { const { locations, addLocations, - parseControlText + parseControlText, + run } = require("./libs/oog/Locations"); const pollQueue = function (timeout = Time.seconds(30)) { @@ -471,19 +472,7 @@ const locationAction = (function () { ); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); diff --git a/d2bs/kolbot/D2BotCharRefresher.dbj b/d2bs/kolbot/D2BotCharRefresher.dbj index c5baee073..89aaab749 100644 --- a/d2bs/kolbot/D2BotCharRefresher.dbj +++ b/d2bs/kolbot/D2BotCharRefresher.dbj @@ -50,7 +50,8 @@ const locationAction = (function () { const Controls = require("./libs/modules/Control"); const { locations, - addLocations + addLocations, + run } = require("./libs/oog/Locations"); const getLobbyTime = function() { @@ -234,19 +235,7 @@ const locationAction = (function () { ); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); diff --git a/d2bs/kolbot/D2BotCleaner.dbj b/d2bs/kolbot/D2BotCleaner.dbj index bf4d00073..6bbbb8fd8 100644 --- a/d2bs/kolbot/D2BotCleaner.dbj +++ b/d2bs/kolbot/D2BotCleaner.dbj @@ -198,155 +198,148 @@ function deleteAllCharacters () { return true; } -const { - locations, - addLocations -} = require("./libs/oog/Locations"); - -locations.set(sdk.game.locations.WaitingInLine, - function () { - Controls.CancelCreateGame.click(); - } -); -addLocations( - [ - sdk.game.locations.Lobby, - sdk.game.locations.LobbyChat, - sdk.game.locations.CreateGame, - sdk.game.locations.JoinGame, - sdk.game.locations.Ladder, - sdk.game.locations.ChannelList, - sdk.game.locations.GameNameExists, - sdk.game.locations.GameDoesNotExist, - sdk.game.locations.GameIsFull, - ], - function () { - Controls.LobbyQuit.click(); - } -); -addLocations( - [ - sdk.game.locations.MainMenu, - sdk.game.locations.Login, - sdk.game.locations.SplashScreen, - ], - function () { - if (!accounts.length) { - FileTools.remove("logs/D2BotCleaner.json"); - D2Bot.printToConsole("D2BotCleaner: Done cleaning accounts!", sdk.colors.D2Bot.Gold); - D2Bot.stop(me.profile, true); +const locationAction = (function () { + const { + run, + locations, + addLocations + } = require("./libs/oog/Locations"); + + locations.set(sdk.game.locations.WaitingInLine, + function () { + Controls.CancelCreateGame.click(); } - - if (!firstAccount) { - ControlAction.timeoutDelay("Waiting for next account in: ", Time.seconds(CleanerConfig.DelayBetweenAccounts)); + ); + addLocations( + [ + sdk.game.locations.Lobby, + sdk.game.locations.LobbyChat, + sdk.game.locations.CreateGame, + sdk.game.locations.JoinGame, + sdk.game.locations.Ladder, + sdk.game.locations.ChannelList, + sdk.game.locations.GameNameExists, + sdk.game.locations.GameDoesNotExist, + sdk.game.locations.GameIsFull, + ], + function () { + Controls.LobbyQuit.click(); } - - firstAccount = false; + ); + addLocations( + [ + sdk.game.locations.MainMenu, + sdk.game.locations.Login, + sdk.game.locations.SplashScreen, + ], + function () { + if (!accounts.length) { + FileTools.remove("logs/D2BotCleaner.json"); + D2Bot.printToConsole("D2BotCleaner: Done cleaning accounts!", sdk.colors.D2Bot.Gold); + D2Bot.stop(me.profile, true); + } + + if (!firstAccount) { + ControlAction.timeoutDelay("Waiting for next account in: ", Time.seconds(CleanerConfig.DelayBetweenAccounts)); + } + + firstAccount = false; - if (FileTools.exists("logs/D2BotCleaner.json")) { - obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); + if (FileTools.exists("logs/D2BotCleaner.json")) { + obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); - if (obj.currAcc) { - for (let i = 0; i < accounts.length; i += 1) { - if (accounts[i].split("/")[0] === obj.currAcc) { - accounts.splice(0, i); - chars.splice(0, i); + if (obj.currAcc) { + for (let i = 0; i < accounts.length; i += 1) { + if (accounts[i].split("/")[0] === obj.currAcc) { + accounts.splice(0, i); + chars.splice(0, i); - i -= 1; + i -= 1; - break; + break; + } } } } - } - let currAccInfo = accounts[0].split("/"); - currAcc = currAccInfo[0]; - obj.currAcc = currAccInfo[0]; - charList = chars[0]; + let currAccInfo = accounts[0].split("/"); + currAcc = currAccInfo[0]; + obj.currAcc = currAccInfo[0]; + charList = chars[0]; - D2Bot.printToConsole("D2BotCleaner: Cleaning account:" + currAcc + " , Character list: " + charList, sdk.colors.D2Bot.Gold); - FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); - - if (currAcc.toLowerCase() === "singleplayer") { - Controls.SinglePlayer.click(); - } else if (currAccInfo.length === 3) { - realm = currAccInfo[2].toLowerCase(); - ControlAction.loginAccount({ account: currAcc, password: currAccInfo[1], realm: realm }); + D2Bot.printToConsole("D2BotCleaner: Cleaning account:" + currAcc + " , Character list: " + charList, sdk.colors.D2Bot.Gold); + FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); + + if (currAcc.toLowerCase() === "singleplayer") { + Controls.SinglePlayer.click(); + } else if (currAccInfo.length === 3) { + realm = currAccInfo[2].toLowerCase(); + ControlAction.loginAccount({ account: currAcc, password: currAccInfo[1], realm: realm }); + } } - } -); -addLocations( - [ - sdk.game.locations.CharSelect, - sdk.game.locations.CharSelectNoChars, - ], - function () { - // Single Player screen fix - if (currAcc.toLowerCase() !== "singleplayer") { - if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { + ); + addLocations( + [ + sdk.game.locations.CharSelect, + sdk.game.locations.CharSelectNoChars, + ], + function () { + // Single Player screen fix + if (currAcc.toLowerCase() !== "singleplayer") { + if (getLocation() === sdk.game.locations.CharSelect && !Controls.CharSelectCurrentRealm.control) { + Controls.BottomLeftExit.click(); + + return; + } + } + + if (!charList.length) { Controls.BottomLeftExit.click(); return; } - } - - if (!charList.length) { - Controls.BottomLeftExit.click(); - return; - } + if (charList[0] === "all") { + deleteAllCharacters(); + } else { + if (FileTools.exists("logs/D2BotCleaner.json")) { + obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); - if (charList[0] === "all") { - deleteAllCharacters(); - } else { - if (FileTools.exists("logs/D2BotCleaner.json")) { - obj = JSON.parse(FileTools.readText("logs/D2BotCleaner.json")); - - if (obj.currChar) { - for (let i = 0; i < charList.length; i += 1) { - if (charList[i] === obj.currChar) { - // Remove the previous currChar as well - charList.splice(0, i + 1); + if (obj.currChar) { + for (let i = 0; i < charList.length; i += 1) { + if (charList[i] === obj.currChar) { + // Remove the previous currChar as well + charList.splice(0, i + 1); - break; + break; + } } } } + + let charInfo = { charName: charList[0] }; + CharactersToExclude.indexOf(charInfo) === -1 && ControlAction.deleteCharacter(charInfo); + delay(500); } + + let currChar = charList.shift(); + obj.currChar = currChar; - let charInfo = { charName: charList[0] }; - CharactersToExclude.indexOf(charInfo) === -1 && ControlAction.deleteCharacter(charInfo); - delay(500); + // last char in acc = trigger next acc + if (!charList.length) { + accounts.shift(); + chars.shift(); + Controls.BottomLeftExit.click(); + } + + FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); } + ); - let currChar = charList.shift(); - obj.currChar = currChar; - - // last char in acc = trigger next acc - if (!charList.length) { - accounts.shift(); - chars.shift(); - Controls.BottomLeftExit.click(); - } - - FileTools.writeText("logs/D2BotCleaner.json", JSON.stringify(obj)); - } -); - -/** @param {number} loc */ -function locationAction (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } -} + return { + run: run, + }; +})(); function main () { addEventListener("copydata", Starter.receiveCopyData); @@ -383,7 +376,7 @@ function main () { !accounts.length && parseInfo(); while (true) { - locationAction(getLocation()); + locationAction.run(getLocation()); delay(1000); } } diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 72da8adb0..55d351fdb 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -56,7 +56,8 @@ const locationAction = (function () { const Controls = require("./libs/modules/Control"); const { locations, - addLocations + addLocations, + run } = require("./libs/oog/Locations"); /** @param {string} leader */ @@ -316,19 +317,7 @@ const locationAction = (function () { ); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); diff --git a/d2bs/kolbot/D2BotGameAction.dbj b/d2bs/kolbot/D2BotGameAction.dbj index de8fc8ab2..a4fe163d9 100644 --- a/d2bs/kolbot/D2BotGameAction.dbj +++ b/d2bs/kolbot/D2BotGameAction.dbj @@ -45,7 +45,8 @@ const locationAction = (function () { const { locations, addLocations, - parseControlText + parseControlText, + run } = require("./libs/oog/Locations"); addLocations([sdk.game.locations.Lobby, sdk.game.locations.LobbyChat], @@ -277,19 +278,7 @@ const locationAction = (function () { ); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 102e1cca9..007cc85c6 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -27,23 +27,11 @@ if (DataFile.init()) { const locationAction = (function () { const { - locations, + run, } = require("./libs/oog/Locations"); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 4d0ad8b2d..01050a32e 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -29,7 +29,8 @@ function main () { const { locations, addLocations, - parseControlText + parseControlText, + run } = require("./libs/oog/Locations"); locations.set(sdk.game.locations.OtherMultiplayer, @@ -372,19 +373,7 @@ function main () { ); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); diff --git a/d2bs/kolbot/D2BotMuleLog.dbj b/d2bs/kolbot/D2BotMuleLog.dbj index 97c93d17d..39b11999f 100644 --- a/d2bs/kolbot/D2BotMuleLog.dbj +++ b/d2bs/kolbot/D2BotMuleLog.dbj @@ -45,7 +45,8 @@ const locationAction = (function () { const Controls = require("./libs/modules/Control"); const { locations, - addLocations + addLocations, + run } = require("./libs/oog/Locations"); addLocations([sdk.game.locations.Lobby, sdk.game.locations.LobbyChat], @@ -255,19 +256,7 @@ const locationAction = (function () { ); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 202cf7bf2..0381c2c45 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1068,6 +1068,8 @@ includeIfNotIncluded("core/Me.js"); gameInfo: {}, joinInfo: {}, profileInfo: {}, + /** @type {number[]} */ + lastLocation: [], sayMsg: function (string) { if (!this.useChat) return; @@ -1752,7 +1754,9 @@ includeIfNotIncluded("core/Me.js"); }, login: function (otherMultiCheck = false) { - Starter.inGame && (Starter.inGame = false); + if (!Starter.lastLocation.includes(sdk.game.locations.LobbyLostConnection)) { + Starter.inGame && (Starter.inGame = false); + } let currLocation = getLocation(); if (otherMultiCheck && currLocation === sdk.game.locations.OtherMultiplayer) { diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index 9ec7c8019..82fef5e8f 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -435,10 +435,30 @@ Starter.lastGameStatus = "ready"; } ); + + /** @param {number} loc */ + const run = function (loc) { + try { + let func = _loc.get(loc); + if (typeof func === "function") { + console.debug("Handling location: " + loc); + Starter.lastLocation.push(loc); + if (Starter.lastLocation.length > 5) { + Starter.lastLocation.shift(); + } + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }; module.exports = { locations: _loc, addLocations: addLocations, parseControlText: parseControlText, + run: run, }; })(module); diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index a209568be..6ea982f4b 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1442,6 +1442,7 @@ declare global { joinInfo: {}; profileInfo: ProfileInfo; ftjCount: number; + lastLocation: number[]; sayMsg(string: string): void; /** From 8f755da5e40dcd4a4b227f98745ebd99d45ea353 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:47:57 -0500 Subject: [PATCH 666/758] Update common/diablo for seal boss detection, light event worker sharing, and inf delay handling - Improves Diablo event detection by adding event listener management and spawn watcher. - Refactors seal opening and boss kill logic for Vizier, de Seis, and Infector, including error handling and path following. Prevent infinite wait, add 3 minute timeout to boss detection. - Handle diabloPrep being triggered due to async event and us not being in chaos - Updates Assassin trap usage to be class-specific. - Removes EventsInstance type from Diablo in Common.d.ts for type accuracy. --- d2bs/kolbot/libs/core/Common/Diablo.js | 286 ++++++++++++++++++------- d2bs/kolbot/sdk/types/Common.d.ts | 2 +- 2 files changed, 212 insertions(+), 76 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 644b28f5e..0bbbe3d93 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -54,16 +54,49 @@ /** @param {number[]} bytes */ diabloLightsEvent: function (bytes = []) { - if (me.inArea(sdk.areas.ChaosSanctuary) - && bytes - && bytes.length === 2 - && bytes[0] === 0x89 - && bytes[1] === 0x0C) { + if (!bytes || bytes.length !== 2) { + return; + } + if (me.act !== 4) { + return; + } + if (bytes[0] === 0x89 && bytes[1] === 0x0C) { + console.debug("Diablo lights event detected"); _Diablo.diabloSpawned = true; Misc._diabloSpawned = true; } }, + addLightsEventListener: function () { + addEventListener("gamepacket", _Diablo.diabloLightsEvent); + }, + + removeLightsEventListener: function () { + removeEventListener("gamepacket", _Diablo.diabloLightsEvent); + }, + + diaSpawnWatcher: ( + /** @param {null | (() => void)} onSpawn */ + function (onSpawn = null) { + let diaTick = 0; + + return function () { + if (Common.Diablo.done) return false; + // check every 1/4 second + if (getTickCount() - diaTick < 250) return true; + diaTick = getTickCount(); + + if (Common.Diablo.diabloSpawned) { + if (typeof onSpawn === "function") { + onSpawn(); + } + throw new ScriptError("Diablo spawned"); + } + return true; + }; + } + ), + /** * @param {Monster} a * @param {Monster} b @@ -93,7 +126,7 @@ */ getLayout: function (seal, value) { let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, seal); - if (!seal) throw new Error("Seal preset not found. Can't continue."); + if (!sealPreset) throw new Error("Seal preset not found. Can't continue."); let _seal = sealPreset.realCoords(); if (_seal.y === value || _seal.x === value) { @@ -117,6 +150,17 @@ _Diablo.infLayout = this.getLayout(sdk.objects.DiabloSealInfector, 7893); }, + /** + * @param {number} classid + * @returns {number} + */ + getSealDistance: function (classid) { + let sealPreset = Game.getPresetObject(sdk.areas.ChaosSanctuary, classid); + if (!sealPreset) throw new Error("Seal preset not found. Can't continue."); + let seal = sealPreset.realCoords(); + return seal.distance; + }, + /** * Follow static path * @param {number[][]} path @@ -188,7 +232,7 @@ runSeals: function (sealOrder, openSeals = true, recheck = false) { console.log("seal order: " + sealOrder); _Diablo.sealOrder = sealOrder; - let seals = { + const seals = { 1: () => this.vizierSeal(openSeals), 2: () => this.seisSeal(openSeals), 3: () => this.infectorSeal(openSeals), @@ -196,19 +240,9 @@ "seis": () => this.seisSeal(openSeals), "infector": () => this.infectorSeal(openSeals), }; - try { - addEventListener("gamepacket", _Diablo.diabloLightsEvent); - sealOrder.forEach(seal => { - if (recheck && _Diablo.diabloSpawned) throw new ScriptError("Diablo spawned"); - seals[seal](); - }); - } catch (e) { - if (!(e instanceof ScriptError)) { - throw e; // it wasn't the custom error so throw it to the next handler - } - } finally { - removeEventListener("gamepacket", _Diablo.diabloLightsEvent); - } + sealOrder.forEach(function (seal) { + seals[seal](); + }); }, /** @@ -283,9 +317,9 @@ usetk && (usetk = false); if (classid === sdk.objects.DiabloSealInfector && me.assassin && this.infLayout === 1) { - if (Config.UseTraps) { - let check = ClassAttack.checkTraps({ x: 7899, y: 5293 }); - check && ClassAttack.placeTraps({ x: 7899, y: 5293 }, check); + if (Config.UseTraps && me.classid === sdk.player.class.Assassin) { + let check = ClassAttack[me.classid].checkTraps({ x: 7899, y: 5293 }); + check && ClassAttack[me.classid].placeTraps({ x: 7899, y: 5293 }, check); } } @@ -326,8 +360,8 @@ return true; } console.log("Viz layout " + _Diablo.vizLayout); - let path = (_Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); - let distCheck = path.last(); + const path = (_Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + const distCheck = path.last(); if (Config.Diablo.SealLeader || Config.Diablo.Fast) { _Diablo.vizLayout === 1 ? Pather.moveTo(7708, 5269) : Pather.moveTo(7647, 5267); @@ -335,15 +369,28 @@ Config.Diablo.SealLeader && Pather.makePortal() && say("in"); } - distCheck.distance > 30 && this.followPath(_Diablo.vizLayout === 1 ? this.starToVizA : this.starToVizB); + if (distCheck.distance > 30) { + this.followPath(path); + } - if (openSeal - && ![sdk.objects.DiabloSealVizier2, sdk.objects.DiabloSealVizier].every(s => _Diablo.openSeal(s))) { + const seals = [sdk.objects.DiabloSealVizier2, sdk.objects.DiabloSealVizier]; + + const tryOpenSeals = function () { + if (!openSeal) return true; + for (let s of seals) { + if (!_Diablo.openSeal(s)) { + return false; + } + } + return true; + }; + + if (!tryOpenSeals()) { throw new Error("Failed to open Vizier seals."); } delay(1 + me.ping); - let cb = () => { + const cb = function () { let viz = Game.getMonster(vizier); return viz && (viz.distance < Skill.getRange(Config.AttackSkill[1]) || viz.dead); }; @@ -355,8 +402,20 @@ ? Pather.moveToEx(7691, 5292, { callback: cb }) : Pather.moveToEx(7695, 5316, { callback: cb }); - if (!_Diablo.getBoss(vizier)) { - throw new Error("Failed to kill Vizier"); + + try { + if (!_Diablo.getBoss(vizier)) { + throw new Error("Failed to kill Vizier"); + } + } catch (e) { + if (e instanceof ScriptError) { + throw e; + } + // sometimes we fail just because we aren't in range, move back towards star while checking + Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: cb }); + if (!_Diablo.getBoss(vizier)) { + throw new Error("Failed to kill Vizier"); + } } Config.Diablo.SealLeader && say("out"); @@ -374,8 +433,8 @@ return true; } console.log("Seis layout " + _Diablo.seisLayout); - let path = (_Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); - let distCheck = path.last(); + const path = (_Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + const distCheck = path.last(); if (Config.Diablo.SealLeader || Config.Diablo.Fast) { _Diablo.seisLayout === 1 ? Pather.moveTo(7767, 5147) : Pather.moveTo(7820, 5147); @@ -384,29 +443,38 @@ } if (distCheck.distance > 30) { - this.followPath(_Diablo.seisLayout === 1 ? this.starToSeisA : this.starToSeisB); + this.followPath(path); } if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealSeis)) { throw new Error("Failed to open de Seis seal."); } - let cb = () => { + + const cb = function () { let seis = Game.getMonster(deSeis); return seis && (seis.distance < Skill.getRange(Config.AttackSkill[1]) || seis.dead); }; + _Diablo.seisLayout === 1 ? Pather.moveToEx(7798, 5194, { callback: cb }) : Pather.moveToEx(7796, 5155, { callback: cb }); + try { if (!_Diablo.getBoss(deSeis)) { throw new Error("Failed to kill de Seis"); } } catch (e) { + if (e instanceof ScriptError) { + throw e; + } // sometimes we fail just because we aren't in range, Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { let seis = Game.getMonster(deSeis); return seis && (seis.distance < 30 || seis.dead); } }); + if (!_Diablo.getBoss(deSeis)) { + throw new Error("Failed to kill de Seis"); + } } Config.Diablo.SealLeader && say("out"); @@ -425,8 +493,9 @@ } Precast.doPrecast(true); console.log("Inf layout " + _Diablo.infLayout); - let path = (_Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); - let distCheck = path.last(); + const path = (_Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + const distCheck = path.last(); + const seals = [sdk.objects.DiabloSealInfector2, sdk.objects.DiabloSealInfector]; if (Config.Diablo.SealLeader || Config.Diablo.Fast) { _Diablo.infLayout === 1 ? Pather.moveTo(7860, 5314) : Pather.moveTo(7909, 5317); @@ -434,16 +503,20 @@ Config.Diablo.SealLeader && Pather.makePortal() && say("in"); } - distCheck.distance > 70 && this.followPath(_Diablo.infLayout === 1 ? this.starToInfA : this.starToInfB); + if (distCheck.distance > 70) { + this.followPath(path); + } - let cb = () => { + const cb = function () { let inf = Game.getMonster(infector); return inf && (inf.distance < Skill.getRange(Config.AttackSkill[1]) || inf.dead); }; - let moveToLoc = () => { + const moveToLoc = function () { if (_Diablo.infLayout === 1) { - (me.sorceress || me.assassin) && Pather.moveToEx(7876, 5296, { callback: cb }); + if (me.sorceress || me.assassin) { + Pather.moveToEx(7876, 5296, { callback: cb }); + } delay(1 + me.ping); } else { delay(1 + me.ping); @@ -451,40 +524,66 @@ } }; - if (Config.Diablo.Fast) { - if (openSeal - && ![ - sdk.objects.DiabloSealInfector2, sdk.objects.DiabloSealInfector - ].every(s => _Diablo.openSeal(s))) { - throw new Error("Failed to open Infector seals."); + const tryOpenSeals = function () { + if (!openSeal) return true; + for (let s of seals) { + if (!_Diablo.openSeal(s)) { + return false; + } } - moveToLoc(); + return true; + }; - if (!_Diablo.getBoss(infector)) { - throw new Error("Failed to kill Infector"); - } - } else { - if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealInfector)) { - throw new Error("Failed to open Infector seals."); - } - moveToLoc(); + try { + if (Config.Diablo.Fast) { + if (tryOpenSeals()) { + throw new Error("Failed to open Infector seals."); + } + moveToLoc(); + + if (!_Diablo.getBoss(infector)) { + throw new Error("Failed to kill Infector"); + } + } else { + if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealInfector)) { + throw new Error("Failed to open Infector seals."); + } + moveToLoc(); - if (!_Diablo.getBoss(infector)) { - throw new Error("Failed to kill Infector"); + if (!_Diablo.getBoss(infector)) { + throw new Error("Failed to kill Infector"); + } + if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealInfector2)) { + throw new Error("Failed to open Infector seals."); + } + // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss + !openSeal && [3, "infector"].includes(_Diablo.sealOrder.last()) && Misc.poll(() => { + if (_Diablo.diabloSpawned) return true; + + let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); + if (lastSeal && lastSeal.mode) { + return true; + } + return false; + }, Time.minutes(3), 1000); } - if (openSeal && !_Diablo.openSeal(sdk.objects.DiabloSealInfector2)) { - throw new Error("Failed to open Infector seals."); + } catch (e) { + if (e instanceof ScriptError) { + throw e; } - // wait until seal has been popped to avoid missing diablo due to wait time ending before he spawns, happens if leader does town chores after seal boss - !openSeal && [3, "infector"].includes(_Diablo.sealOrder.last()) && Misc.poll(() => { - if (_Diablo.diabloSpawned) return true; - - let lastSeal = Game.getObject(sdk.objects.DiabloSealInfector2); - if (lastSeal && lastSeal.mode) { - return true; + if (e instanceof Error && e.message === "Diablo not found") { + throw e; + } + if (e instanceof Error && e.message === "Failed to kill Infector") { + // sometimes we fail just because we aren't in range, + Pather.moveToEx(this.starCoords.x, this.starCoords.y, { minDist: 15, callback: () => { + let inf = Game.getMonster(infector); + return inf && (inf.distance < 30 || inf.dead); + } }); + if (!_Diablo.getBoss(infector)) { + throw new Error("Failed to kill Infector"); } - return false; - }, Time.minutes(3), 1000); + } } Config.Diablo.SealLeader && say("out"); @@ -563,10 +662,10 @@ return this.hammerdinPreAttack(id, 8); case sdk.player.class.Assassin: if (Config.UseTraps) { - let trapCheck = ClassAttack.checkTraps({ x: coords[0], y: coords[1] }); + let trapCheck = ClassAttack[me.classid].checkTraps({ x: coords[0], y: coords[1] }); if (trapCheck) { - ClassAttack.placeTraps({ x: coords[0], y: coords[1] }, 5); + ClassAttack[me.classid].placeTraps({ x: coords[0], y: coords[1] }, 5); return true; } @@ -583,6 +682,9 @@ * @returns {boolean} */ getBoss: function (name) { + // reasonable timeout to find boss before assuming something went wrong + const timeout = getTickCount() + Time.minutes(3); + let glow = Game.getObject(sdk.objects.SealGlow); if (this.waitForGlow) { @@ -596,6 +698,10 @@ if (glow) { break; } + + if (getTickCount() > timeout) { + throw new Error(name + " not found"); + } } } @@ -633,6 +739,30 @@ }, diabloPrep: function () { + if (!me.inArea(sdk.areas.ChaosSanctuary)) { + if (!me.inArea(sdk.areas.PandemoniumFortress)) { + Town.goToTown(4); + } + + Town.move("portalspot"); + + let tookPortalToChaos = getUnits(sdk.unittype.Object, "portal").filter(function (portal) { + return portal.objtype === sdk.areas.ChaosSanctuary; + }).sort(function (a, b) { + let aParent = a.getParent ? a.getParent() : null; + let bParent = b.getParent ? b.getParent() : null; + if (aParent === me.name) return -1; + if (bParent === me.name) return 1; + return getDistance(me, a) - getDistance(me, b); + }).some(function (portal) { + return Pather.usePortal(null, null, portal); + }); + + if (!tookPortalToChaos) { + throw new Error("Failed to go to Chaos Sanctuary"); + } + } + if (Config.Diablo.SealLeader) { Pather.moveTo(7763, 5267); Pather.makePortal() && say("in"); @@ -648,8 +778,12 @@ switch (me.classid) { case sdk.player.class.Sorceress: if ([ - sdk.skills.Meteor, sdk.skills.Blizzard, sdk.skills.FrozenOrb, sdk.skills.FireWall - ].includes(Config.AttackSkill[1])) { + 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); } @@ -675,8 +809,10 @@ 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[me.classid].checkTraps({ x: 7793, y: 5293 }); + if (trapCheck) { + ClassAttack[me.classid].placeTraps({ x: 7793, y: 5293, classid: sdk.monsters.Diablo }, trapCheck); + } } if (Config.AttackSkill[1] === sdk.skills.ShockWeb) { diff --git a/d2bs/kolbot/sdk/types/Common.d.ts b/d2bs/kolbot/sdk/types/Common.d.ts index 7ddffdda8..339aa7e8b 100644 --- a/d2bs/kolbot/sdk/types/Common.d.ts +++ b/d2bs/kolbot/sdk/types/Common.d.ts @@ -6,7 +6,7 @@ declare global { Baal: typeof import("../../libs/core/Common/Baal"); Cain: typeof import("../../libs/core/Common/Cain"); Cows: typeof import("../../libs/core/Common/Cows"); - Diablo: typeof import("../../libs/core/Common/Diablo") & EventsInstance; + Diablo: typeof import("../../libs/core/Common/Diablo"); Leecher: typeof import("../../libs/core/Common/Leecher"); Smith: typeof import("../../libs/core/Common/Smith"); Toolsthread: typeof import("../../libs/core/Common/Tools"); From f441630df2d85ee6fc7a35fc39912200afd2a494 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:49:15 -0500 Subject: [PATCH 667/758] Cleanup Wakka to properly use ScriptError for events & end background workers - Encapsulated level and leader tracking logic into reusable functions and updated background worker assignments for improved clarity and maintainability. - Replaced direct event listener manipulation with dedicated add/remove methods. - Enhanced error handling and cleanup in the script's main execution flow. --- d2bs/kolbot/libs/scripts/Wakka.js | 142 ++++++++++++++---------------- 1 file changed, 65 insertions(+), 77 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Wakka.js b/d2bs/kolbot/libs/scripts/Wakka.js index 83958e47a..e18ce25f2 100644 --- a/d2bs/kolbot/libs/scripts/Wakka.js +++ b/d2bs/kolbot/libs/scripts/Wakka.js @@ -210,6 +210,52 @@ const Wakka = new Runnable( console.log(msg); }; + const levelTracker = (function () { + let levelTick = getTickCount(); + + return function () { + if (Common.Diablo.done) return false; + // check every 3 seconds + if (getTickCount() - levelTick < 3000) return true; + levelTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + + if (me.charlvl >= Config.Wakka.StopAtLevel) { + Config.Wakka.StopProfile && D2Bot.stop(); + throw new ScriptError("Reached wanted level"); + } + + return true; + }; + })(); + + const leaderTracker = (function () { + let leadTick = getTickCount(); + + return function () { + if (Common.Diablo.done) return false; + // check every 3 seconds + if (getTickCount() - leadTick < 3000) return true; + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) { + throw new ScriptError("Empty game"); + } + + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { + Common.Diablo.done = true; + throw new ScriptError("Party leader is running baal"); + } + + return true; + }; + })(); + // START Town.goToTown(4); Town.move("portalspot"); @@ -228,88 +274,25 @@ const Wakka = new Runnable( timeout: timeout * 60e3 })); Town.doChores(); - if (!leader) throw new ScriptError("Wakka: Leader not found"); + if (!leader) { + throw new ScriptError("Wakka: Leader not found"); + } - addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + Common.Diablo.addLightsEventListener(); const Worker = require("../modules/Worker"); try { if (Config.Wakka.SkipIfBaal) { - let leadTick = getTickCount(); - let killLeaderTracker = false; - - Worker.runInBackground.leaderTracker = function () { - if (Common.Diablo.done || killLeaderTracker) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(getLeaderUnitArea())) { - if (Loader.scriptName() === "Wakka") { - killLeaderTracker = true; - Common.Diablo.done = true; - throw new Error("Party leader is running baal"); - } else { - // kill process - return false; - } - } - - return true; - }; + Worker.runInBackground.leaderTracker = leaderTracker; } - let levelTick = getTickCount(); - let killLevelTracker = false; - - Worker.runInBackground.levelTracker = function () { - if (Common.Diablo.done || killLevelTracker) return false; - // check every 3 seconds - if (getTickCount() - levelTick < 3000) return true; - levelTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - - if (me.charlvl >= Config.Wakka.StopAtLevel) { - Config.Wakka.StopProfile && D2Bot.stop(); - - if (Loader.scriptName() === "Wakka") { - killLevelTracker = true; - throw new Error("Reached wanted level"); - } else { - // kill process - return false; - } - } - - return true; - }; - - let diaTick = getTickCount(); + Worker.runInBackground.levelTracker = levelTracker; - Worker.runInBackground.diaSpawned = function () { - if (Common.Diablo.done || (internals.vizClear && internals.seisClear && internals.infClear)) { - return false; - } - // check every 1/4 second - if (getTickCount() - diaTick < 250) return true; - diaTick = getTickCount(); - - if (Common.Diablo.diabloSpawned) { - internals.vizClear = true; - internals.seisClear = true; - internals.infClear = true; - throw new Error("Diablo spawned"); - } - - return true; - }; + Worker.runInBackground.diaSpawned = Common.Diablo.diaSpawnWatcher(function () { + internals.vizClear = true; + internals.seisClear = true; + internals.infClear = true; + }); while (Misc.inMyParty(leader)) { try { @@ -445,10 +428,15 @@ const Wakka = new Runnable( } } } catch (e) { - // console.error(e); + if (!(e instanceof ScriptError)) { + console.error(e); + } } finally { - Common.Diablo.done; - removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + delete Worker.runInBackground.leaderTracker; + delete Worker.runInBackground.levelTracker; + delete Worker.runInBackground.diaSpawned; + + Common.Diablo.removeLightsEventListener(); } log("Wakka complete"); From a087774680539e3e366e1c3293e2bf84e5f22712 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:53:06 -0500 Subject: [PATCH 668/758] Update Sorceress.js - remove leftover debug log --- d2bs/kolbot/libs/core/Attacks/Sorceress.js | 1 - 1 file changed, 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Sorceress.js b/d2bs/kolbot/libs/core/Attacks/Sorceress.js index 18a0babda..50888050a 100644 --- a/d2bs/kolbot/libs/core/Attacks/Sorceress.js +++ b/d2bs/kolbot/libs/core/Attacks/Sorceress.js @@ -193,7 +193,6 @@ }, afterAttack: function () { - console.debug("Sorceress afterAttack: doPrecast"); Precast.doPrecast(false); }, From f16d3fc05b58a19a0599eb3b387f66fe3fb1e3cc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:29:33 -0500 Subject: [PATCH 669/758] Updated/refactored Diablo to use ScriptError for worker events + cleanup - Refactored all the worker methods to standalone methods and added cleanup at the end of the script - Swapped all internal errors for ScriptError --- d2bs/kolbot/libs/scripts/DiabloHelper.js | 227 +++++++++++++---------- 1 file changed, 134 insertions(+), 93 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index 00915b1c6..58a091cac 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -7,113 +7,151 @@ const DiabloHelper = new Runnable( function DiabloHelper () { - this.Leader = Config.Leader; Common.Diablo.waitForGlow = true; Common.Diablo.clearRadius = Config.DiabloHelper.ClearRadius; const Worker = require("../modules/Worker"); - try { - addEventListener("gamepacket", Common.Diablo.diabloLightsEvent); - - if (Config.DiabloHelper.SkipIfBaal) { - let leadTick = getTickCount(); - - Worker.runInBackground.leaderTracker = function () { - if (Common.Diablo.done) return false; - // check every 3 seconds - if (getTickCount() - leadTick < 3000) return true; - leadTick = getTickCount(); - - // check again in another 3 seconds if game wasn't ready - if (!me.gameReady) return true; - if (Misc.getPlayerCount() <= 1) throw new Error("Empty game"); - let party = getParty(); - - if (party) { - do { - // Player is in Throne of Destruction or Worldstone Chamber - if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { - if (Loader.scriptName() === "DiabloHelper") { - throw new Error("Party leader is running baal"); - } else { - // kill process - return false; - } - } - } while (party.getNext()); - } + let leader = Config.Leader; + const leaderTracker = (function (ignoreDelay = false) { + let leadTick = getTickCount(); + + return function () { + if (Common.Diablo.done) return false; + // check every 3 seconds + if (!ignoreDelay && getTickCount() - leadTick < 3000) { return true; - }; + } + leadTick = getTickCount(); + + // check again in another 3 seconds if game wasn't ready + if (!me.gameReady) return true; + if (Misc.getPlayerCount() <= 1) { + throw new ScriptError("Empty game"); + } + let party = getParty(); + + if (party) { + do { + // Player is in Throne of Destruction or Worldstone Chamber + if ([sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(party.area)) { + throw new ScriptError("Leader is running baal"); + } + } while (party.getNext()); + } + + return true; + }; + })(); + + const waitForPortal = function () { + if (!leader) { + leader = Misc.autoLeaderDetect({ + destination: sdk.areas.ChaosSanctuary, + quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), + timeout: Time.minutes(2) + }); } - Config.DiabloHelper.SafePrecast && Precast.needOutOfTownCast() - ? Precast.doRandomPrecast(true, sdk.areas.PandemoniumFortress) - : Precast.doPrecast(true); + if (!Misc.poll(() => { + if (Pather.getPortal(sdk.areas.ChaosSanctuary, Config.Leader || null) + && Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader || null)) { + return true; + } - if (Config.DiabloHelper.SkipTP) { - !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); + return false; + }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { + console.error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + throw new ScriptError("Player wait timed out"); + } + }; - if (!Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); - !Config.DiabloHelper.Entrance && Pather.moveTo(7774, 5305); + try { + Common.Diablo.addLightsEventListener(); + + if (Config.DiabloHelper.SkipIfBaal) { + // start tracking leader - run once manually to initialize + leaderTracker(); + Worker.runInBackground.leaderTracker = leaderTracker; + } - if (!Misc.poll(() => { - let party = getParty(); + let portalCheck = Pather.getPortal(sdk.areas.ChaosSanctuary, Config.Leader || null); + if (portalCheck) { + console.log("Found portal to Chaos Sanctuary"); + } - if (party) { - do { - if ((!DiabloHelper.Leader || party.name === DiabloHelper.Leader) - && party.area === sdk.areas.ChaosSanctuary) { - return true; - } - } while (party.getNext()); - } + try { + Common.Diablo.diaSpawnWatcher(); + Worker.runInBackground.diaSpawned = Common.Diablo.diaSpawnWatcher(); + + Config.DiabloHelper.SafePrecast && Precast.needOutOfTownCast() + ? Precast.doRandomPrecast( + true, + (Config.DiabloHelper.SkipTP && !portalCheck) ? sdk.areas.RiverofFlame : sdk.areas.PandemoniumFortress + ) + : Precast.doPrecast(true); + + if (Config.DiabloHelper.SkipTP && !portalCheck) { + !me.inArea(sdk.areas.RiverofFlame) && Pather.useWaypoint(sdk.areas.RiverofFlame); + + if (!Pather.moveTo(7790, 5544)) throw new ScriptError("Failed to move to Chaos Sanctuary"); + !Config.DiabloHelper.Entrance && Pather.moveTo(7774, 5305); + + if (!Misc.poll(() => { + let party = getParty(); + + if (party) { + do { + if ((!leader || party.name === leader) + && party.area === sdk.areas.ChaosSanctuary) { + return true; + } + } while (party.getNext()); + } - Attack.clear(30, 0, false, Common.Diablo.sort); + Attack.clear(30, 0, false, Common.Diablo.sort); - return false; - }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Chaos)"); - } - } else { - Town.goToTown(4); - Town.move("portalspot"); - if (!DiabloHelper.Leader) { - DiabloHelper.Leader = Misc.autoLeaderDetect({ - destination: sdk.areas.ChaosSanctuary, - quitIf: (area) => [sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber].includes(area), - timeout: Time.minutes(2) - }); - } + if (Misc.getPlayerCount() <= 1) { + throw new ScriptError("Empty game"); + } - if (!Misc.poll(() => { - if (Pather.getPortal(sdk.areas.ChaosSanctuary, Config.Leader || null) - && Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader || null)) { - return true; + return false; + }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { + console.error("Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Chaos)"); + throw new ScriptError("Player wait timed out"); } - - return false; - }, Time.minutes(Config.DiabloHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + } else { + Town.goToTown(4); + Town.move("portalspot"); + waitForPortal(); + } + } catch (e) { + if (e instanceof ScriptError) { + if (e.message === "Empty game" || e.message === "Leader is running baal" || e.message === "Player wait timed out") { + throw e; + } + if (e.message === "Failed to move to Chaos Sanctuary") { + Town.goToTown(4); + Town.move("portalspot"); + waitForPortal(); + } + if ((e.message === "Diablo spawned")) { + Town.goToTown(4); + Town.move("portalspot"); + if (!Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader || null)) { + throw new Error("Failed to use portal to Chaos Sanctuary after Diablo spawn"); + } + } + } else { + console.error(e); } } Common.Diablo.initLayout(); - let diaTick = getTickCount(); - - Worker.runInBackground.diaSpawned = function () { - if (Common.Diablo.done) return false; - // check every 1/4 second - if (getTickCount() - diaTick < 250) return true; - diaTick = getTickCount(); - - if (Common.Diablo.diabloSpawned) throw new Error("Diablo spawned"); - - return true; - }; - try { + Common.Diablo.diaSpawnWatcher(); + if (Config.DiabloHelper.Entrance && Common.Diablo.starCoords.distance > Common.Diablo.entranceCoords.distance) { Attack.clear(35, 0, false, Common.Diablo.sort); Common.Diablo.followPath(Common.Diablo.entranceToStar); @@ -133,18 +171,18 @@ const DiabloHelper = new Runnable( sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction, sdk.areas.WorldstoneChamber - ].includes(Misc.getPlayerArea(DiabloHelper.Leader))) { - throw new Error("END"); + ].includes(Misc.getPlayerArea(leader))) { + throw new ScriptError("Leader is running baal"); } return false; }, Time.minutes(2), 500); } catch (e) { - let eMsg = e.message ? e.message : e; - console.log(eMsg); - - if (eMsg === "END") { - return true; + if (e instanceof ScriptError) { + if (e.message === "Empty game" || e.message === "Leader is running baal") { + throw e; + } } + console.error(e); } try { @@ -170,8 +208,11 @@ const DiabloHelper = new Runnable( } catch (e) { console.error(e); } finally { + delete Worker.runInBackground.leaderTracker; + delete Worker.runInBackground.diaSpawned; + Common.Diablo.done = true; - removeEventListener("gamepacket", Common.Diablo.diabloLightsEvent); + Common.Diablo.removeLightsEventListener(); } return true; From c35adf0ea03c91e45aa8e8aedf8967deae9a0d64 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:48:12 -0500 Subject: [PATCH 670/758] Add `after game delay` handling when hit with LobbyleaseWait location --- d2bs/kolbot/libs/oog/Locations.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index 82fef5e8f..439ceec1e 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -395,7 +395,22 @@ Starter.LocationEvents.unableToConnect(); } ); - addLocations([sdk.game.locations.CharSelectPleaseWait, sdk.game.locations.LobbyPleaseWait], + addLocations([sdk.game.locations.LobbyPleaseWait], + function (location) { + let startTick = getTickCount(); + if (!Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location)) { + Controls.OkCentered.click(); + } else { + if (getTickCount() - startTick < Time.seconds(5)) { + ControlAction.timeoutDelay( + "After Game Delay", + Math.max((Time.seconds(5) - (getTickCount() - startTick), 1000)) + ); + } + } + } + ); + addLocations([sdk.game.locations.CharSelectPleaseWait], function (location) { if (!Starter.locationTimeout(Starter.Config.PleaseWaitTimeout * 1e3, location)) { Controls.OkCentered.click(); From 5a6e5b7f5d3474f2b1f828df51fd70d13be26a28 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:50:19 -0500 Subject: [PATCH 671/758] Update D2BotPubJoin to follow new LocationAction format --- d2bs/kolbot/D2BotPubJoin.dbj | 44 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index 07cf9543d..e96242c06 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -62,7 +62,8 @@ if (DataFile.init()) { Starter.firstRun = true; } -let lastGameTick, retry = 0; +let lastGameTick = 0; +let retry = 0; let retryTick = 0; const GameTracker = { @@ -102,7 +103,8 @@ const locationAction = (function () { const Controls = require("./libs/modules/Control"); const { locations, - addLocations + addLocations, + run } = require("./libs/oog/Locations"); /** @param {string} game */ @@ -171,12 +173,6 @@ const locationAction = (function () { Starter.pingQuit = false; } - if (Starter.Config.JoinChannel !== "" || Starter.Config.AnnounceGames) { - Controls.LobbyEnterChat.click(); - - return; - } - if (Starter.inGame || Starter.gameInfo.error) { !Starter.gameStart && (Starter.gameStart = DataFile.getStats().ingameTick); @@ -208,6 +204,17 @@ const locationAction = (function () { Starter.gameCount = 1; DataFile.updateStats("runs", Starter.gameCount); } + + ControlAction.timeoutDelay( + "Join Game Delay", + Starter.Config.JoinGameDelay * 1e3 + ); + } + + if (Starter.Config.JoinChannel !== "" || Starter.Config.AnnounceGames) { + Controls.LobbyEnterChat.click(); + + return; } Starter.LocationEvents.openJoinGameWindow(); @@ -232,6 +239,11 @@ const locationAction = (function () { Starter.gameCount += 1; Starter.lastGameStatus = "ready"; Starter.inGame = false; + + ControlAction.timeoutDelay( + "Join Game Delay", + Starter.Config.JoinGameDelay * 1e3 + ); } if (!Starter.chatActionsDone) { @@ -272,6 +284,8 @@ const locationAction = (function () { ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); } + D2Bot.updateStatus("Searching for Public Game"); + if (Starter.Config.AttemptNextGame && retry < Starter.Config.AttemptNextGameRetrys) { let ng = DataFile.getStats().nextGame; @@ -372,19 +386,7 @@ const locationAction = (function () { ); return { - /** @param {number} loc */ - run: function (loc) { - try { - let func = locations.get(loc); - if (typeof func === "function") { - func(loc); - } else if (loc !== undefined && loc !== null) { - console.log("Unhandled location: " + loc); - } - } catch (e) { - console.error(e); - } - }, + run: run, }; })(); From 9a7ec5365fda07718a9709ca4e79f2f3156d257e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:00:04 -0500 Subject: [PATCH 672/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index d0323de03..648ab42df 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit d0323de03e41a80b5276ed4ab8c062ffdef4f0e7 +Subproject commit 648ab42df542ee822ee185800040383695804ff4 From fd713852568c36d19ffdd6fb6ccefdaf207e0686 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:41:36 -0500 Subject: [PATCH 673/758] Add event emitter support to Diablo module - Integrated event methods (on, off, once, emit) from Events into the Diablo module and emit a 'diablospawned' event when Diablo spawns. - Updated type definitions to reflect the new event capabilities. --- d2bs/kolbot/libs/core/Common/Diablo.js | 8 ++++++++ d2bs/kolbot/sdk/types/Common.d.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 0bbbe3d93..5cba8ac89 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -6,7 +6,14 @@ */ (function (module) { + const Events = new (require("../../modules/Events")); + const _Diablo = { + on: Events.on, + off: Events.off, + once: Events.once, + emit: Events.emit, + diabloSpawned: false, diaWaitTime: Time.seconds(30), clearRadius: 30, @@ -64,6 +71,7 @@ console.debug("Diablo lights event detected"); _Diablo.diabloSpawned = true; Misc._diabloSpawned = true; + _Diablo.emit("diablospawned"); } }, diff --git a/d2bs/kolbot/sdk/types/Common.d.ts b/d2bs/kolbot/sdk/types/Common.d.ts index 339aa7e8b..7ddffdda8 100644 --- a/d2bs/kolbot/sdk/types/Common.d.ts +++ b/d2bs/kolbot/sdk/types/Common.d.ts @@ -6,7 +6,7 @@ declare global { Baal: typeof import("../../libs/core/Common/Baal"); Cain: typeof import("../../libs/core/Common/Cain"); Cows: typeof import("../../libs/core/Common/Cows"); - Diablo: typeof import("../../libs/core/Common/Diablo"); + Diablo: typeof import("../../libs/core/Common/Diablo") & EventsInstance; Leecher: typeof import("../../libs/core/Common/Leecher"); Smith: typeof import("../../libs/core/Common/Smith"); Toolsthread: typeof import("../../libs/core/Common/Tools"); From dcfd07445ab4036b1a0110e69dbcae2008f72541 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:51:14 -0500 Subject: [PATCH 674/758] Add AutoChaos script and config options - Introduces the AutoChaos script for automated Chaos Sanctuary runs, including new configuration options in Config.js and IConfig for script behavior. - Updates type definitions to support AutoChaos and its settings, and registers the script in KolbotScript types. --- d2bs/kolbot/libs/core/Config.js | 25 + d2bs/kolbot/libs/scripts/AutoChaos.js | 701 ++++++++++++++++++++++ d2bs/kolbot/sdk/types/Config.d.ts | 57 +- d2bs/kolbot/sdk/types/kolbot-scripts.d.ts | 88 ++- 4 files changed, 868 insertions(+), 3 deletions(-) create mode 100644 d2bs/kolbot/libs/scripts/AutoChaos.js diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 7dc62d39f..9a82eb97c 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -579,6 +579,31 @@ let Config = { SealOrder: ["vizier", "seis", "infector"], RecheckSeals: false }, + /** @type {IConfig["AutoChaos"]} */ + AutoChaos: { + Leader: "", + Diablo: 0, + Taxi: false, + FindShrine: false, + UseShrine: false, + Glitcher: false, + BO: false, + Leech: false, + Ranged: false, + RequireClass: { + Amazon: false, + Sorceress: false, + Necromancer: false, + Paladin: false, + Barbarian: false, + Druid: false, + Assassin: false, + }, + SealPrecast: false, + PreAttack: [0, 0, 0], + SealOrder: [1, 2, 3], + SealDelay: 0 + }, MFHelper: { BreakClearLevel: false, BreakOnDiaBaal: true, diff --git a/d2bs/kolbot/libs/scripts/AutoChaos.js b/d2bs/kolbot/libs/scripts/AutoChaos.js new file mode 100644 index 000000000..02b1b2f46 --- /dev/null +++ b/d2bs/kolbot/libs/scripts/AutoChaos.js @@ -0,0 +1,701 @@ +/** +* @filename AutoChaos.js +* @author noah-@github.com, theBGuy +* @desc AutoChaos is an anonymous Team CS script without any explicit in-game communication. +* This script is designed for running Classic Taxi CS. +* @note this has been experimentally updated to work with current kolbot and has not been fully tested. +*/ + + +const AutoChaos = new Runnable( + function AutoChaos() { + let taxi = ""; + let tpID = 0; + let lastCall = 0; + let lastUpdate = 0; + + /** + * @param {string} message + * @param {boolean} overhead + */ + function notify(message, overhead) { + if (overhead) { + me.overhead(overhead); + } + + takeScreenshot(); + return message; + } + + function setTaxi() { + if (Config.AutoChaos.Leader !== "" && getParty(Config.AutoChaos.Leader)) { + taxi = Config.AutoChaos.Leader; + } else if (taxi === "" || !getParty(taxi)) { + taxi = detectTaxi(); + } + } + + function detectTaxi() { + let player = getParty(); + let current = taxi; + let lvl = 0; + + if (!player) { + return current; + } + + do { + if (player.name !== me.name && player.classid === sdk.player.class.Sorceress && lvl < player.level) { + let tick = getTickCount(); + + while (player.area === sdk.areas.None && getTickCount() - tick < 5000) { + delay(100); + } + + if (player.area !== sdk.areas.None && player.area < sdk.areas.PandemoniumFortress) { + continue; + } + + current = player.name; + lvl = player.level; + + if (Pather.getPortal(108, current) && !Pather.getPortal(sdk.areas.ChaosSanctuary, player.name)) { + break; + } + } + } while (player.getNext()); + + return current; + } + + function doNext() { + let portal, gid, tick; + + if (taxi === "" || (lastCall >= 3 && Config.AutoChaos.Diablo === -1)) { + Town.doChores(); + return true; + } + + if (lastCall >= 3 && Config.AutoChaos.UseShrine) { + Town.goToTown(1); + Town.move("portalspot"); + + if ((portal = Pather.getPortal(sdk.areas.StonyField, null)) || + (portal = Pather.getPortal(sdk.areas.ColdPlains, null)) || + (portal = Pather.getPortal(sdk.areas.BloodMoor, null))) { + Pather.usePortal(null, null, portal); + getShrine(sdk.shrines.Experience, 8, 10); + + if (!Pather.usePortal(sdk.areas.RogueEncampment, null)) { + Town.goToTown(); + } + } + + Pather.useWaypoint(sdk.areas.PandemoniumFortress, true); + Town.move("portalspot"); + Config.AutoChaos.UseShrine = false; + } + + portal = Pather.getPortal(null, taxi); + + if (!portal || portal.gid === tpID) { + tick = getTickCount(); + + if (lastCall < 3 && (tick - lastUpdate) > 10000) { + taxi = ""; + lastUpdate = tick; + } + + return true; + } + + gid = portal.gid; + + if (Config.AutoChaos.BO && portal.objtype !== 107) { + return true; + } + + if (Config.AutoChaos.SealDelay) { + delay(Config.AutoChaos.SealDelay * 1000); + } + + if (!Pather.usePortal(null, null, portal)) { + return true; + } + + tpID = gid; + lastUpdate = getTickCount(); + + if (me.inArea(sdk.areas.RiverofFlame)) { + precast(); + } else if (me.inArea(sdk.areas.ChaosSanctuary)) { + if (Config.AutoChaos.SealPrecast) { + Precast.doPrecast(true); + } + + Common.Diablo.initLayout(); + + if (Common.Diablo.getSealDistance(sdk.objects.DiabloSealVizier) < 60) { + vizier(); + } else if (Common.Diablo.getSealDistance(sdk.objects.DiabloSealInfector) < 60) { + infector(); + } else if (Common.Diablo.getSealDistance(sdk.objects.DiabloSealSeis) < 80) { + seis(); + } else if (getDistance(me, 7795, 5293) < 30) { + diablo(); + return false; + } else { + notify("", "I should not be here..."); + } + } + + Town.goToTown(); + return true; + } + + /** + * @param {number} area + */ + function waitForParty(area = 0) { + let time = getTickCount(); + let classes = copyObj(Config.AutoChaos.RequireClass); + + while (Object.values(classes).indexOf(true) >= 0 && (getTickCount() - time < 25000)) { + let party = getParty(); + + if (party) { + do { + if (classes[sdk.player.class.nameOf(party.classid)] && party.level >= 30) { + if (area > 0 && party.area !== me.area) { + time = getTickCount(); + continue; + } + + classes[sdk.player.class.nameOf(party.classid)] = false; + } + } while (party.getNext()); + } + + delay(250); + } + + if (classes.indexOf(true) >= 0) { + throw new Error(notify("Not enough players.")); + } + } + + function precast() { + let sorc = getParty(taxi); + + while ( + sorc + && sorc.area !== sdk.areas.ChaosSanctuary + && (!me.getState(sdk.states.BattleOrders) || Game.getPlayer(taxi)) + ) { + Precast.doPrecast(true); + delay(500); + } + + if (!Pather.usePortal(null, taxi)) { + Pather.useWaypoint(sdk.areas.PandemoniumFortress, true); + } + + if (Town.needHealing() || (Town.checkScrolls("tbk") < 10)) { + Town.doChores(); + } + + Town.move("portalspot"); + } + + /** + * @param {number} type + * @param {number} distance + * @param {number} retry + */ + function getShrine(type, distance, retry) { + let shrine = getUnit(2); + + if (!shrine) { + return; + } + + do { + if (getDistance(me.x, me.y, shrine.x, shrine.y) < distance && shrine.objtype === type) { + while (retry-- > 0) { + if (Misc.getShrine(shrine)) { + break; + } + } + + return; + } + } while (shrine.getNext()); + } + + function sealDelay() { + if (!Config.AutoChaos.SealDelay) { + return; + } + + const time = Config.AutoChaos.SealDelay * 1000 + getTickCount(); + const loc = new PathNode(me.x, me.y); + + while (getTickCount() < time) { + if (me.inTown) { + delay(Config.AutoChaos.SealDelay * 1000); + } else { + doPreattack(loc); + } + } + } + + function clearOut() { + Pickit.pickItems(); + Town.goToTown(); + lastCall += 1; + Town.move("portalspot"); + } + + function vizier() { + slayBoss(sdk.locale.monsters.GrandVizierofChaos, Config.AutoChaos.PreAttack[0], 20); + clearOut(); + } + + function seis() { + slayBoss(sdk.locale.monsters.LordDeSeis, Config.AutoChaos.PreAttack[1], 30); + clearOut(); + } + + function infector() { + slayBoss(sdk.locale.monsters.InfectorofSouls, Config.AutoChaos.PreAttack[2], 20); + clearOut(); + } + + /** + * @param {number} amount + */ + function taxiInit(amount = 0) { + if (amount > 0) { + delay(amount); + } + + const pos = new PathNode(me.x, me.y); + const time = getTickCount(); + let count = time; + + Precast.doPrecast(); + + while (!me.getState(sdk.states.BattleOrders)) { + if (getTickCount() - count > 5000) { + count = getTickCount(); + Pather.moveTo(pos.x + rand(-4, 4), pos.y + rand(-4, 4)); + } + + if (getTickCount() - time > 50000) { + console.log("Game timed out."); + return false; + } + + delay(100); + } + + return true; + } + + /** + * @param {boolean} last + */ + function taxiVizier(last) { + const viz = Common.Diablo.vizLayout === 1 ? new PathNode(7683, 5302) : new PathNode(7687, 5315); + + Pather.moveTo(viz.x, viz.y); + Pather.makePortal(false); + Common.Diablo.openSeal(sdk.objects.DiabloSealVizier); + + if (!last) { + Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2); + } + + Pather.moveTo(viz.x, viz.y); + sealDelay(); + slayBoss(sdk.locale.monsters.GrandVizierofChaos, Config.AutoChaos.PreAttack[0], 25, 0); + + if (last) { + Common.Diablo.openSeal(sdk.objects.DiabloSealVizier2); + Pather.moveTo(viz.x, viz.y); + } + + Pickit.pickItems(); + } + + function taxiSeis() { + const seis = Common.Diablo.seisLayout === 1 ? new PathNode(7782, 5224) : new PathNode(7775, 5193); + + Pather.moveTo(seis.x, seis.y, 10); + Pather.makePortal(false); + Common.Diablo.openSeal(sdk.objects.DiabloSealSeis); + sealDelay(); + Pather.moveTo(seis.x, seis.y, 10); + Pather.teleDistance = 45; + slayBoss(sdk.locale.monsters.LordDeSeis, Config.AutoChaos.PreAttack[1], 30, 0); + Pickit.pickItems(); + } + + /** + * @param {boolean} last + */ + function taxiInfector(last) { + const inf = Common.Diablo.infLayout === 1 ? new PathNode(7926, 5300) : new PathNode(7924, 5282); + + Pather.moveTo(inf.x, inf.y); + Pather.makePortal(false); + Common.Diablo.openSeal(sdk.objects.DiabloSealInfector); + + if (!last) { + Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2); + } + + Pather.moveTo(inf.x, inf.y); + slayBoss(sdk.locale.monsters.InfectorofSouls, Config.AutoChaos.PreAttack[2], 25, 0); + + if (last) { + Common.Diablo.openSeal(sdk.objects.DiabloSealInfector2); + Pather.moveTo(inf.x, inf.y); + } + + Pickit.pickItems(); + } + + function diablo() { + let maxTime = 0; + let preCount = 1; + let postCount = 1; + let party = getParty(); + + if (party) { + do { + if (party.level >= 30) { + preCount += 1; + } + } while (party.getNext()); + } + + if (Config.AutoChaos.Leech) { + Pather.moveTo(7767, 5263); + } else { + if (Config.AutoChaos.Diablo >= 0) { + Pather.moveTo(7798, 5293); + slayBoss(sdk.locale.monsters.Diablo, 0, 30, Config.AutoChaos.Diablo); + + if (Config.AutoChaos.Diablo === 0) { + let pick = getTickCount(); + + while (getTickCount() - pick < 1500) { + Pickit.pickItems(); + delay(me.ping + 100); + } + + return; + } + } + + if (!Pather.usePortal(sdk.areas.PandemoniumFortress, null)) { + Town.goToTown(); + } + + Town.doChores(); + } + + while (maxTime < 180000) { + party = getParty(); + + if (party) { + postCount = 1; + do { + if (party.level >= 30) { + postCount += 1; + } + } while (party.getNext()); + + if (postCount < preCount || (!Config.AutoChaos.Taxi && !getParty(taxi))) { + break; + } + } + + maxTime += 3000; + delay(3000); + } + } + + /** + * @param {PathNode} loc + */ + function doPreattack(loc) { + switch (me.classid) { + case sdk.player.class.Amazon: + case sdk.player.class.Sorceress: + case sdk.player.class.Necromancer: { + if (me.getState(sdk.states.SkillDelay)) { + let player = Game.getPlayer(); + Skill.cast(Config.AttackSkill[2], 0, player.x, player.y); + } else { + Skill.cast(Config.AttackSkill[1], 0, me.x, me.y); + } + + if (Config.Dodge && me.hp * 100 / me.hpmax <= Config.DodgeHP) { + Attack.deploy(loc, Config.DodgeRange, 5, 15); + } + + return true; + } + case sdk.player.class.Paladin: + if (Config.AttackSkill[3] !== sdk.skills.BlessedHammer) { + return false; + } + + if (Config.AttackSkill[4] > 0) { + Skill.setSkill(Config.AttackSkill[4], 0); + } + + for (let i = 0; i < 3; i++) { + Skill.cast(Config.AttackSkill[3], 1); + } + return true; + case sdk.player.class.Barbarian: + Skill.cast(Config.AttackSkill[3], Skill.getHand(Config.AttackSkill[3])); + return true; + case sdk.player.class.Druid: + if (Config.AttackSkill[3] === sdk.skills.Tornado) { + return Skill.cast(Config.AttackSkill[3], 0, me.x, me.y); + } + + break; + case sdk.player.class.Assassin: { + if (Config.UseTraps) { + let check = ClassAttack[me.classid].checkTraps({ x: me.x, y: me.y }); + + if (check) { + return ClassAttack[me.classid].placeTraps({ x: me.x, y: me.y }, 5); + } + } + + break; + } + default: + delay(2000); + } + + return false; + } + + /** + * @param {number} classid + * @param {number} [preattack] + * @param {number} [retry] + * @param {number} [stop] + */ + function slayBoss(classid, preattack, retry, stop = 0) { + /** @type {Monster | null} */ + let boss = null; + let bosshp = 0; + let reposition = 0; + let checkPosition = 0; + let tick = 0; + let time = getTickCount() + (retry * 1000); + let loc = new PathNode(me.x, me.y); + + const name = getLocaleString(classid); + + while (getTickCount() < time) { + if (!boss) { + boss = Game.getMonster(name); + } else if (boss && boss.attackable && preattack > 0) { + preattack -= 1; + } else { + break; + } + + if (me.paladin || classid !== sdk.locale.monsters.Diablo) { + doPreattack(loc); + } + + delay(me.ping + 10); + } + + if (!boss) { + console.log("Unable to find: " + name); + return; + } + + if (!Config.AutoChaos.Ranged) { + for (let i = 0; i < 10; i++) { + if (Pather.moveTo(boss.x + rand(-3, 3), boss.y + rand(0, 3), 0)) { + break; + } else { + doPreattack(loc); + } + } + } + + time = getTickCount() + (retry * 1000); + + do { + if (!boss || !boss.attackable) { + return; + } + + bosshp = parseInt((boss.hp * 100) / 128, 10); + + if (bosshp < stop) { + return; + } + + if (tick - checkPosition >= 3000) { + if (Pather.useTeleport || bosshp >= reposition) { + if (Config.AutoChaos.Ranged) { + Attack.deploy(boss, 15, 5, 9); + } else { + if (Pather.useTeleport) { + Attack.deploy(boss, 3, 5, 9); + } else { + if (time - tick < 10000) { + notify("", name + " loc:" + boss.x + "," + boss.y + " me loc:" + me.x + "," + me.y); + loc.update(boss.x + rand(-5, 5), boss.y + rand(0, 5)); + } else { + loc.update(boss.x + rand(-3, 3), boss.y + rand(0, 3)); + } + + Pather.moveTo(loc.x, loc.y, 0); + Misc.click(0, 0, loc.x, loc.y); + } + } + } + + checkPosition = tick; + reposition = bosshp; + } + + try { + if (!ClassAttack[me.classid].doAttack(boss, false)) { + Skill.cast(Config.AttackSkill[3], Skill.getHand(Config.AttackSkill[3]), boss); + } + } catch (e) { + console.error(e); + } + + tick = getTickCount(); + } while (tick < time); + + throw new Error(notify("Failed to kill: " + name + " in " + retry + " sec", "Failed to kill:" + name + " loc:" + boss.x + "," + boss.y + " me loc:" + me.x + "," + me.y)); + } + + // START ------------------------------------------------------------------------------------------------------------- + Common.Diablo.addLightsEventListener(); + Common.Diablo.on("diablospawned", function () { + lastCall = 3; + }); + + if (me.area !== sdk.areas.PandemoniumFortress) { + Pather.useWaypoint(sdk.areas.PandemoniumFortress, true); + } + + Pather.walkTo(me.x + rand(-5, 5), me.y + rand(-5, 5)); + Town.doChores(); + + if (Config.AutoChaos.Taxi) { + waitForParty(); + + Pather.useWaypoint(sdk.areas.RiverofFlame, true); + Pather.makePortal(false); + Pather.moveTo(me.x, me.y - 5); + + waitForParty(sdk.areas.RiverofFlame); + + if (!taxiInit()) { + return false; + } + + Pather.moveTo(7797, 5606); + Pather.moveTo(7797, 5590); + + for (let i = 0; i < 3; i += 1) { + if (Config.AutoChaos.SealOrder[i] === 1) { + taxiVizier(i === 2); + } else if (Config.AutoChaos.SealOrder[i] === 2) { + taxiSeis(i === 2); + } else if (Config.AutoChaos.SealOrder[i] === 3) { + taxiInfector(i === 2); + } else { + throw new Error(notify("Invalid Config.AutoChaos.SealOrder setting.")); + } + } + + Pather.moveTo(7786, 5285); + Pather.makePortal(false); + diablo(); + } else if (Config.AutoChaos.FindShrine) { + Pather.useWaypoint(sdk.areas.StonyField, true); + + const foundShrine = [sdk.areas.StonyField, sdk.areas.ColdPlains, sdk.areas.BloodMoor].some(function (area) { + return Misc.getShrinesInArea(area, 15, false); + }); + + if (foundShrine) { + console.log("Exp shrine found."); + Town.goToTown(); + } else { + Pather.journeyTo(sdk.areas.RogueEncampment); + } + + Pather.useWaypoint(sdk.areas.PandemoniumFortress, true); + Town.doChores(); + let time = getTickCount(); + + while (me.ingame) { + if (getTickCount() - time > 5000) { + time = getTickCount(); + Pickit.pickItems(); // find stray gold on ground + setTaxi(); + + if (taxi === "" || !getParty(taxi)) { + break; + } + } + + delay(100); + } + } else if (Config.AutoChaos.Glitcher) { + // Glitcher mode + } else { + Town.move("portalspot"); + let time = getTickCount(); + + while (me.ingame) { + delay(100); + setTaxi(); + + if (getTickCount() - time > 25000) { + if (taxi === "" || !getParty(taxi)) { + break; + } + + time = getTickCount(); + } + + if (!doNext()) { + break; + } + } + } + + return true; + }, + { + startArea: sdk.areas.PandemoniumFortress, + preAction: null, + cleanup: function () { + Common.Diablo.off("diablospawned"); + Common.Diablo.removeLightsEventListener(); + } + } +); diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index a9d3320f9..7b5023474 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -5,7 +5,7 @@ declare global { type ExtendedCubingOpts = { Ethereal: number; MaxQuantity: number; condition: () => boolean }; type CubingRecipe = [number, string] | [number, string, number] | [number, string, ExtendedCubingOpts]; - interface Config { + interface IConfig { init(notify: boolean): void; Loaded: boolean; DebugMode: { @@ -387,6 +387,59 @@ declare global { SealOrder: DiabloSeal[]; RecheckSeals: boolean; }; + AutoChaos: { + Leader: string; + /** + * -1 = go to town during diablo, 0 = kill to death, x > 0 = kill to x% + */ + Diablo: number; + Taxi: boolean; + /** + * set true to search for shrine only + */ + FindShrine: boolean; + /** + * true = get shrine from act 1 (requires another character running FindShrine) + */ + UseShrine: boolean; + /** + * set true for low level EXP glitcher (unimplemented) + */ + Glitcher: boolean; + /** + * true = don't enter seals after boing at river, false = normal character that fights + */ + BO: boolean; + /** + * true = hide during diablo, false = stay at star + */ + Leech: boolean; + /** + * true = ranged character, false = melee character + */ + Ranged: boolean; + /** + * Classes required to start the chaos run set to true to require that class + */ + RequireClass: Record; + /** + * true = does precast sequence at every seal, false = does not precast at seal + */ + SealPrecast: boolean; + /** + * preattack count at each seal, useful for clearing tp's for safer entry, + * enter values in the following order: [/vizier/, /seis/, /infector/] + */ + PreAttack: number[]; + /** + * order in which the taxi will go through cs, 1: vizier, 2: seis, 3: infector + */ + SealOrder: DiabloSeal[]; + /** + * number of seconds to wait before entering hot tp + */ + SealDelay: number; + }; MFHelper: { BreakClearLevel: boolean; BreakOnDiaBaal: boolean; @@ -590,5 +643,5 @@ declare global { DebugMode: boolean; }; } - const Config: Config; + const Config: IConfig; } diff --git a/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts b/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts index b22fae9fb..d559dc067 100644 --- a/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts +++ b/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts @@ -1,2 +1,88 @@ // Auto-generated script types -export type KolbotScript = "Abaddon" | "AncientTunnels" | "Andariel" | "AutoBaal" | "Baal" | "BaalAssistant" | "BaalHelper" | "BattleOrders" | "BattlemaidSarina" | "Bishibosh" | "BoBarbHelper" | "BoneAsh" | "Bonesaw" | "ChestMania" | "ClassicChaosAssistant" | "ClearAnyArea" | "Coldcrow" | "Coldworm" | "ControlBot" | "Corpsefire" | "Countess" | "Cows" | "Crafting" | "CreepingFeature" | "CrushTele" | "DeveloperMode" | "Diablo" | "DiabloHelper" | "Duriel" | "Eldritch" | "Endugu" | "Eyeback" | "Fangskin" | "Follower" | "Frozenstein" | "Gamble" | "GemHunter" | "GetCube" | "GetEssences" | "GetFade" | "GetKeys" | "GhostBusters" | "Hephasto" | "IPHunter" | "Icehawk" | "Idle" | "Izual" | "KillDclone" | "KurastTemples" | "MFHelper" | "Mausoleum" | "Mephisto" | "Nihlathak" | "OrgTorch" | "OrgTorchHelper" | "OuterSteppes" | "Pindleskin" | "Pit" | "Questing" | "Radament" | "Rakanishu" | "Rushee" | "Rusher" | "SealLeecher" | "SharpTooth" | "ShopBot" | "Smith" | "Snapchip" | "Stormtree" | "Summoner" | "Synch" | "Synch2" | "Test" | "ThreshSocket" | "Tombs" | "Travincal" | "TravincalLeech" | "Treehead" | "Tristram" | "TristramLeech" | "UndergroundPassage" | "UserAddon" | "WPGetter" | "Wakka" | "Worldstone"; +export type KolbotScript = + | "Abaddon" + | "AncientTunnels" + | "Andariel" + | "AutoBaal" + | "AutoChaos" + | "Baal" + | "BaalAssistant" + | "BaalHelper" + | "BattleOrders" + | "BattlemaidSarina" + | "Bishibosh" + | "BoBarbHelper" + | "BoneAsh" + | "Bonesaw" + | "ChestMania" + | "ClassicChaosAssistant" + | "ClearAnyArea" + | "Coldcrow" + | "Coldworm" + | "ControlBot" + | "Corpsefire" + | "Countess" + | "Cows" + | "Crafting" + | "CreepingFeature" + | "CrushTele" + | "DeveloperMode" + | "Diablo" + | "DiabloHelper" + | "Duriel" + | "Eldritch" + | "Endugu" + | "Eyeback" + | "Fangskin" + | "Follower" + | "Frozenstein" + | "Gamble" + | "GemHunter" + | "GetCube" + | "GetEssences" + | "GetFade" + | "GetKeys" + | "GhostBusters" + | "Hephasto" + | "IPHunter" + | "Icehawk" + | "Idle" + | "Izual" + | "KillDclone" + | "KurastTemples" + | "MFHelper" + | "Mausoleum" + | "Mephisto" + | "Nihlathak" + | "OrgTorch" + | "OrgTorchHelper" + | "OuterSteppes" + | "Pindleskin" + | "Pit" + | "Questing" + | "Radament" + | "Rakanishu" + | "Rushee" + | "Rusher" + | "SealLeecher" + | "SharpTooth" + | "ShopBot" + | "Smith" + | "Snapchip" + | "Stormtree" + | "Summoner" + | "Synch" + | "Synch2" + | "Test" + | "ThreshSocket" + | "Tombs" + | "Travincal" + | "TravincalLeech" + | "Treehead" + | "Tristram" + | "TristramLeech" + | "UndergroundPassage" + | "UserAddon" + | "WPGetter" + | "Wakka" + | "Worldstone"; From e69615eb2f1015364a69d39e7a40f42fd4d8137a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:43:38 -0500 Subject: [PATCH 675/758] Fix D2BotPubJoin open join game delay handling - Move delay to the LocationEvents handler `openJoinGameWindow` - Fix overriding the LobbyChat location which was causing the orignal delay to be ignored --- d2bs/kolbot/D2BotPubJoin.dbj | 15 +++------------ d2bs/kolbot/libs/OOG.js | 4 ++++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/D2BotPubJoin.dbj b/d2bs/kolbot/D2BotPubJoin.dbj index e96242c06..dc7958af3 100644 --- a/d2bs/kolbot/D2BotPubJoin.dbj +++ b/d2bs/kolbot/D2BotPubJoin.dbj @@ -204,11 +204,6 @@ const locationAction = (function () { Starter.gameCount = 1; DataFile.updateStats("runs", Starter.gameCount); } - - ControlAction.timeoutDelay( - "Join Game Delay", - Starter.Config.JoinGameDelay * 1e3 - ); } if (Starter.Config.JoinChannel !== "" || Starter.Config.AnnounceGames) { @@ -239,11 +234,6 @@ const locationAction = (function () { Starter.gameCount += 1; Starter.lastGameStatus = "ready"; Starter.inGame = false; - - ControlAction.timeoutDelay( - "Join Game Delay", - Starter.Config.JoinGameDelay * 1e3 - ); } if (!Starter.chatActionsDone) { @@ -270,8 +260,9 @@ const locationAction = (function () { ); addLocations( [ - sdk.game.locations.LobbyChat, sdk.game.locations.CreateGame, - sdk.game.locations.Ladder, sdk.game.locations.ChannelList + sdk.game.locations.CreateGame, + sdk.game.locations.Ladder, + sdk.game.locations.ChannelList ], function () { Starter.LocationEvents.openJoinGameWindow(); diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index 0381c2c45..cf4565205 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -1737,6 +1737,10 @@ includeIfNotIncluded("core/Me.js"); openJoinGameWindow: function () { let currentLoc = getLocation(); + if (Starter.inGame) { + ControlAction.timeoutDelay("Open join game delay", 5000); + } + if (!Controls.JoinGameWindow.click()) { return; } From 305fbc5b4f1e549100f9b6a45be17fbca4833473 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 7 Dec 2025 15:40:11 -0500 Subject: [PATCH 676/758] fix: Add missing not operator for tryOpenSeals in Common.Diablo - Typo was causing infector seal to fail on success --- d2bs/kolbot/libs/core/Common/Diablo.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 5cba8ac89..9cba39c5e 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -237,7 +237,7 @@ * @param {number[] | string[]} sealOrder * @param {boolean} openSeals */ - runSeals: function (sealOrder, openSeals = true, recheck = false) { + runSeals: function (sealOrder, openSeals = true) { console.log("seal order: " + sealOrder); _Diablo.sealOrder = sealOrder; const seals = { @@ -249,6 +249,7 @@ "infector": () => this.infectorSeal(openSeals), }; sealOrder.forEach(function (seal) { + if (_Diablo.diabloSpawned) return; seals[seal](); }); }, @@ -308,6 +309,7 @@ } if (seal.mode) { + console.debug("Seal " + classid + " is already open."); warn && say(Config.Diablo.SealWarning); return true; } @@ -544,7 +546,7 @@ try { if (Config.Diablo.Fast) { - if (tryOpenSeals()) { + if (!tryOpenSeals()) { throw new Error("Failed to open Infector seals."); } moveToLoc(); From 11f543fac1b6a1954cc10c4dcc8e4b5f690b6fa3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:34:11 -0500 Subject: [PATCH 677/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 648ab42df..dc6bf7f0d 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 648ab42df542ee822ee185800040383695804ff4 +Subproject commit dc6bf7f0dbc5a2d252815499fd0d81c00cb7a4d1 From c06ab913dd11b7afe8bce086728aad161604db4d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:42:59 -0500 Subject: [PATCH 678/758] fix: Condition check of using Burst of Speed skill --- d2bs/kolbot/libs/core/GameData/SkillData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index f591427f8..8255c05ff 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -1189,7 +1189,7 @@ range: 1, state: sdk.states.BurstofSpeed, duration: () => (108 + (12 * me.getSkill(sdk.skills.BurstofSpeed, sdk.skills.subindex.SoftPoints))), - condition: () => !Config.UseBoS && !me.inTown, + condition: () => Config.UseBoS || me.inTown, }); skillMap.set(sdk.skills.FistsofFire, { hand: sdk.skills.hand.Left, From 187cea49c151b07d197995d490f2903098a88df8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:48:41 -0500 Subject: [PATCH 679/758] fix: process deletion logic in Worker module - Corrects the deleteProperty handler to only attempt to stop and delete a process if it exists, preventing errors when deleting non-existent processes. --- d2bs/kolbot/libs/modules/Worker.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/libs/modules/Worker.js b/d2bs/kolbot/libs/modules/Worker.js index 92160fd1b..4feb058b1 100644 --- a/d2bs/kolbot/libs/modules/Worker.js +++ b/d2bs/kolbot/libs/modules/Worker.js @@ -85,11 +85,10 @@ return true; }, deleteProperty: function (target, name) { - if (!target.processes.hasOwnProperty(name)) { - throw new Error("Process " + name + " does not exists."); + if (target.processes.hasOwnProperty(name)) { + target.processes[name].running = false; + delete target.processes[name]; } - target.processes[name].running = false; - delete target.processes[name]; return true; } }); From 3d613065f7572ba09de5ff7700c91507d9e90451 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:59:09 -0500 Subject: [PATCH 680/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index dc6bf7f0d..8dd0e8d5c 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit dc6bf7f0dbc5a2d252815499fd0d81c00cb7a4d1 +Subproject commit 8dd0e8d5ca340a68d88f7d87319cad91bdc99039 From 80c157a67aa6e805593f392fd35ea87226fcf522 Mon Sep 17 00:00:00 2001 From: tricky-eli Date: Fri, 19 Dec 2025 19:07:37 -0400 Subject: [PATCH 681/758] Add Header for Merc Items (#465) merc items header is blank (blank/blank/SEN) this will change to (acc / char / SEN) to make finding merc items easier in CharViewer --- d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 9e4b87a4a..95bea2067 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -344,6 +344,7 @@ const MuleLogger = { for (let item of mercItems) { let parsedItem = this.logItem(item); + !parsedItem.header && (parsedItem.header = (me.account || "Single Player") + " / " + me.name); parsedItem.title += " (merc)"; let string = JSON.stringify(parsedItem); finalString += (string + "\n"); From 39abfd639c05824612c2cfe274f5185e65b1247b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 20 Dec 2025 18:28:16 -0500 Subject: [PATCH 682/758] patch: Watch for friends joining in D2BotChannel when FriendListQuery is set - Watch for the whispermessages and process them for any valid games, before we would only process the messages from typing `/f l` --- d2bs/kolbot/D2BotChannel.dbj | 59 +++++++++++++++++++++++++++---- d2bs/kolbot/libs/oog/Locations.js | 2 +- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/D2BotChannel.dbj b/d2bs/kolbot/D2BotChannel.dbj index 344a0c12f..fb920a95c 100644 --- a/d2bs/kolbot/D2BotChannel.dbj +++ b/d2bs/kolbot/D2BotChannel.dbj @@ -4,6 +4,9 @@ * @desc Entry script for following bots using channels * * @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/torch/TorchSystem")} +* @typedef {import("./libs/systems/crafting/CraftingSystem")} +* @typedef {import("./libs/systems/gambling/Gambling")} */ include("critical.js"); // required @@ -28,6 +31,8 @@ const watch = { }; /** @type {Array<{ name: string, msg: string }>} */ const messageQueue = []; +/** @type {Array<{ name: string, game: string, time: number }>} */ +const friendsMessageQueue = []; /** @type {Map} */ const channelWatcher = new Map(); const joinInfo = { @@ -56,6 +61,17 @@ function ChannelChatHandler (name, msg) { messageQueue.push({ name: name, msg: msg }); } +/** + * @param {string} name + * @param {string} msg + */ +function WhisperChatHandler (name, msg) { + if (me.ingame) return; + if (!msg.includes("Your friend")) return; + let game = msg.split("game called")[1].replace(".", "").trim(); + friendsMessageQueue.push({ name: name, game: game, time: new Date().getTime() }); +} + const locationAction = (function () { const Controls = require("./libs/modules/Control"); const { @@ -100,6 +116,15 @@ const locationAction = (function () { return false; }; + const systemsOOGCheck = function () { + return AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck(); + }; + + const friendListQueryInterval = Time.seconds(ChannelConfig.FriendListQuery || 0); + let lastText; let channelTick = getTickCount(); let fListTick = 0; @@ -120,10 +145,7 @@ const locationAction = (function () { D2Bot.updateStatus("Lobby Chat"); if (Starter.inGame) { - if (AutoMule.outOfGameCheck() - || TorchSystem.outOfGameCheck() - || Gambling.outOfGameCheck() - || CraftingSystem.outOfGameCheck()) { + if (systemsOOGCheck()) { return; } @@ -230,8 +252,7 @@ const locationAction = (function () { retry = 0; } - if (ChannelConfig.FriendListQuery > 0 - && getTickCount() - fListTick >= Time.seconds(ChannelConfig.FriendListQuery)) { + if (friendListQueryInterval && getTickCount() - fListTick >= friendListQueryInterval) { say("/f l"); fListTick = getTickCount(); pollQueue(); @@ -269,6 +290,28 @@ const locationAction = (function () { } } + while (friendsMessageQueue.length > 0) { + let chat = friendsMessageQueue.shift(); + // lets determine if this is a player message or internal message + if (!chat.name || chat.name.split("*")[0] === me.charname) continue; + // how long ago was this message? + if (new Date().getTime() - chat.time > Time.minutes(3)) continue; + // we have a game from a friend whisper + let gameName = chat.game; + // double check that this is a valid game name + if (gameName.length > 15 || badGames.has(gameName) || exclude(gameName)) continue; + let validGame = ChannelConfig.Games.find(function (gInfo) { + return gameName.toLowerCase().includes(gInfo.game.toLowerCase()); + }); + if (!validGame) continue; + console.debug("Joining friend's game: " + gameName); + joinInfo.gameName = gameName; + joinInfo.gamePass = validGame.password || ""; + Controls.JoinGameWindow.click(); + + return; + } + fullText = ""; lines = Controls.LobbyChat.getText(); if (!lines) return; @@ -494,6 +537,10 @@ function main () { addEventListener("copydata", Starter.receiveCopyData); addEventListener("scriptmsg", Starter.scriptMsgEvent); addEventListener("chatmsg", ChannelChatHandler); + + if (ChannelConfig.FriendListQuery > 0) { + addEventListener("whispermsg", WhisperChatHandler); + } while (!Starter.handle) { delay(100); diff --git a/d2bs/kolbot/libs/oog/Locations.js b/d2bs/kolbot/libs/oog/Locations.js index 439ceec1e..10fb70f41 100644 --- a/d2bs/kolbot/libs/oog/Locations.js +++ b/d2bs/kolbot/libs/oog/Locations.js @@ -456,7 +456,7 @@ try { let func = _loc.get(loc); if (typeof func === "function") { - console.debug("Handling location: " + loc); + // console.debug("Handling location: " + loc); Starter.lastLocation.push(loc); if (Starter.lastLocation.length > 5) { Starter.lastLocation.shift(); From 5e94eef19ea6194d6941c38298776104c67a4dee Mon Sep 17 00:00:00 2001 From: tricky-eli Date: Sun, 21 Dec 2025 13:15:10 -0400 Subject: [PATCH 683/758] Add ItemiLevel to itemString in ToolsThread.js (#466) added ilvl to "numpad ." for easy way to check or verify iLvl of items within regular bot play --- d2bs/kolbot/threads/ToolsThread.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/threads/ToolsThread.js b/d2bs/kolbot/threads/ToolsThread.js index 1e346e7e7..a41745412 100644 --- a/d2bs/kolbot/threads/ToolsThread.js +++ b/d2bs/kolbot/threads/ToolsThread.js @@ -153,12 +153,13 @@ function main () { itemString = ( "ÿc4ItemName: ÿc0" + itemToCheck.prettyPrint + "\nÿc4ItemType: ÿc0" + itemToCheck.itemType - + "| ÿc4Classid: ÿc0" + itemToCheck.classid - + "| ÿc4Quality: ÿc0" + itemToCheck.quality - + "| ÿc4Gid: ÿc0" + itemToCheck.gid + + " | ÿc4Classid: ÿc0" + itemToCheck.classid + + " | ÿc4Quality: ÿc0" + itemToCheck.quality + + " | ÿc4Gid: ÿc0" + itemToCheck.gid + "\nÿc4ItemMode: ÿc0" + itemToCheck.mode - + "| ÿc4Location: ÿc0" + itemToCheck.location - + "| ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation + + " | ÿc4Location: ÿc0" + itemToCheck.location + + " | ÿc4Bodylocation: ÿc0" + itemToCheck.bodylocation + + " | ÿc4Item Level: ÿc0" + itemToCheck.ilvl ); generalString = pString + nString + "\nÿc4Cubing Item: ÿc0" + Cubing.keepItem(itemToCheck) From a5baa667d116342fd944fdf2714a14086f876e07 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 21 Dec 2025 18:26:26 -0500 Subject: [PATCH 684/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 8dd0e8d5c..b2a2e3590 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 8dd0e8d5ca340a68d88f7d87319cad91bdc99039 +Subproject commit b2a2e35907b7bb6caac7b244fb406aea672751d4 From 013b978707418a581396f7c5b04b8e7687b4338e Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:05:09 -0500 Subject: [PATCH 685/758] patch: Update Attacks.txt to remove `Not implemented yet` for preattack --- d2bs/kolbot/libs/config/Templates/Attacks.txt | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/d2bs/kolbot/libs/config/Templates/Attacks.txt b/d2bs/kolbot/libs/config/Templates/Attacks.txt index 2f82ccefd..7fbdce12a 100644 --- a/d2bs/kolbot/libs/config/Templates/Attacks.txt +++ b/d2bs/kolbot/libs/config/Templates/Attacks.txt @@ -8,7 +8,7 @@ *** Single element - lightning + chain lightning - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 49; // Primary skill to bosses. Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 53; // Primary skill to others. @@ -18,7 +18,7 @@ *** Single element - fireball + meteor - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 56; // Primary skill to bosses. Config.AttackSkill[2] = 47; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 56; // Primary skill to others. @@ -28,7 +28,7 @@ *** Single element - blizzard + glacial spike - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 59; // Primary skill to bosses. Config.AttackSkill[2] = 55; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 59; // Primary skill to others. @@ -38,7 +38,7 @@ *** Dual element - frozen orb + nova (with glacial spike to lightning immune) - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 64; // Primary skill to bosses. Config.AttackSkill[2] = 48; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 64; // Primary skill to others. @@ -48,7 +48,7 @@ *** Dual element - blizzard + fireball (with meteor to cold immune and glacial spike to fire immune) - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 59; // Primary skill to bosses. Config.AttackSkill[2] = 47; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 59; // Primary skill to others. @@ -58,7 +58,7 @@ *** Tri-element - blizzard with fireball, meteor on cold immune, nova on fire immune - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 59; // Primary skill to bosses. Config.AttackSkill[2] = 47; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 59; // Primary skill to others. @@ -68,7 +68,7 @@ *** Tri-element - frozen orb with nova, fire wall on cold immune, glacial spike on lightning immune - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 64; // Primary skill to bosses. Config.AttackSkill[2] = 48; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 64; // Primary skill to others. @@ -82,7 +82,7 @@ *** Poison nova with corpse explosion and lower resist - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 92; // Primary skill to bosses. Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 92; // Primary skill to others. @@ -104,7 +104,7 @@ *** Summoner with corpse explosion, amplify damage on non-bosses and decrepify on bosses - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 500; // Primary skill to bosses. Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 500; // Primary skill to others. @@ -122,7 +122,7 @@ *** Bone spirit/spear with corpse explosion and decrepify - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 93; // Primary skill to bosses. Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 84; // Primary skill to others. @@ -146,7 +146,7 @@ *** Hammerdin with holybolt/redemption on immune - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 112; // Primary skill to bosses. Config.AttackSkill[2] = 113; // Primary aura to bosses Config.AttackSkill[3] = 112; // Primary skill to others. @@ -156,7 +156,7 @@ *** Smiter with zeal on non-bosses - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 97; // Primary skill to bosses. Config.AttackSkill[2] = 122; // Primary aura to bosses Config.AttackSkill[3] = 106; // Primary skill to others. @@ -170,7 +170,7 @@ *** Tornado druid - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 245; // Primary skill to bosses. Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 245; // Primary skill to others. @@ -186,7 +186,7 @@ *** Trap assassin using lightning sentry and death sentry as traps, shock web as timed attack and fire blast as untimed attack - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 256; // Primary skill to bosses. Config.AttackSkill[2] = 251; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 256; // Primary skill to others. @@ -201,7 +201,7 @@ *** Whirlwind assassin using wake of fire traps - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 151; // Primary skill to bosses. Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 151; // Primary skill to others. @@ -216,7 +216,7 @@ *** Kicksin using Death Sentry for traps - Config.AttackSkill[0] = -1; // Preattack skill. Not implemented yet. + Config.AttackSkill[0] = -1; // Preattack skill. Config.AttackSkill[1] = 255; // Primary skill to bosses. Config.AttackSkill[2] = -1; // Primary untimed skill to bosses. Keep at -1 if Config.AttackSkill[1] is untimed skill. Config.AttackSkill[3] = 255; // Primary skill to others. From 05aa0283b8623ab707014bf826e2e5edd9ccb4b3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 26 Dec 2025 18:21:33 -0500 Subject: [PATCH 686/758] feat: Add optional priority to runeword creating - Allow prioritizing certain runewords in case there is overlap of runes or bases --- d2bs/kolbot/libs/core/Config.js | 4 +++- d2bs/kolbot/libs/core/Runewords.js | 30 ++++++++++++++++++++---------- d2bs/kolbot/sdk/types/Config.d.ts | 6 +++++- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 9a82eb97c..cf154d930 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -310,7 +310,8 @@ let Config = { Recipes: [], MakeRunewords: false, /** - * @type {[runeword, string | number, ?boolean][]} + * @type {[runeword, string | number, ?boolean, number | undefined][]} + * @example [Runeword.Enigma, 'Archon Plate', Roll.NonEth, 100] */ Runewords: [], KeepRunewords: [], @@ -632,6 +633,7 @@ let Config = { MaxGameLength: 0, }, ControlBot: { + WelcomePlayers: true, Bo: false, DropGold: false, Cows: { diff --git a/d2bs/kolbot/libs/core/Runewords.js b/d2bs/kolbot/libs/core/Runewords.js index 8bfb89a0b..a28711e10 100644 --- a/d2bs/kolbot/libs/core/Runewords.js +++ b/d2bs/kolbot/libs/core/Runewords.js @@ -34,16 +34,20 @@ const Runewords = { 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; - } + if (runeword.ladderRestricted()) { + continue; + } + + if (isNaN(base)) { + let cleanName = base.replace(/\s+/g, "").toLowerCase(); + + if (NTIPAliasClassID.hasOwnProperty(cleanName)) { + Config.Runewords[i][1] = NTIPAliasClassID[cleanName]; + } else { + Misc.errorReport("ÿc1Invalid runewords entry:ÿc0 " + base); + Config.Runewords.splice(i, 1); + + i -= 1; } } } @@ -141,6 +145,12 @@ const Runewords = { // 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); + Config.Runewords.sort(function (a, b) { + const aPriority = a[3] || 0; + const bPriority = b[3] || 0; + return bPriority - aPriority; + }); + for (let i = 0; i < Config.Runewords.length; i += 1) { let itemList = []; // reset item list let items = itemsRef.slice(); // copy itemsRef diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 7b5023474..f31484719 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -157,7 +157,11 @@ declare global { RepairPercent: number; Recipes: CubingRecipe[]; MakeRunewords: boolean; - Runewords: any[][]; + /** + * runeword, item name or id, ethereal (Roll.Eth, Roll.NonEth, Roll.Any or undefined), priority (number or undefined) + * @example [Runeword.Enigma, 'Archon Plate', Roll.NonEth, 100] + */ + Runewords: [runeword, string | number, boolean | undefined, number | undefined][]; KeepRunewords: any[]; LadderOverride: boolean; Gamble: boolean; From e5242ded5896edd4f51f1e77b9fb8221e9b9ae75 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 26 Dec 2025 18:33:18 -0500 Subject: [PATCH 687/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index b2a2e3590..d446e17d8 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit b2a2e35907b7bb6caac7b244fb406aea672751d4 +Subproject commit d446e17d801290886daf0ad21ac90f9507d46952 From 620e5e592f90e1b231b04bc50cc1bf8a09caf36c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 27 Dec 2025 11:15:31 -0500 Subject: [PATCH 688/758] fix: regex for file path extraction in LazyLoader - Updated the regular expressions in LazyLoader.js to more accurately match file paths by using a non-greedy pattern. This change ensures better handling of file paths that may contain spaces or special characters. - This was failing for users with kolbot installed in `Program Files (x86)` --- d2bs/kolbot/libs/modules/LazyLoader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/modules/LazyLoader.js b/d2bs/kolbot/libs/modules/LazyLoader.js index feea5f62a..bb6dc1f42 100644 --- a/d2bs/kolbot/libs/modules/LazyLoader.js +++ b/d2bs/kolbot/libs/modules/LazyLoader.js @@ -14,13 +14,13 @@ for (let i = 1; i < stack.length; i++) { // Find LazyLoader.js location if (/lazyloader\.js/i.test(stack[i])) { - let match = stack[i].match(/@([a-zA-Z]:[^\s:]+)\.js:/); + let match = stack[i].match(/@([a-zA-Z]:.+?)\.js:/); if (match) { loaderDir = match[1].replace(/\\/g, "/"); } } else { // Find first non-LazyLoader caller - const match = stack[i].match(/@([a-zA-Z]:[^\s:]+)\.js:/); + const match = stack[i].match(/@([a-zA-Z]:.+?)\.js:/); if (match) { callerDir = match[1].replace(/\\/g, "/"); break; From 63ddb475fe31f29b8a3889f4344cc540b23972fa Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 Dec 2025 15:44:24 -0500 Subject: [PATCH 689/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index d446e17d8..5b9d4896f 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit d446e17d801290886daf0ad21ac90f9507d46952 +Subproject commit 5b9d4896f26c1b4c73f5c4a07e0592de0c1680b3 From 96fad796ba0ae5498c8fe4b0604ae6fb308f73dd Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 28 Dec 2025 20:30:38 -0500 Subject: [PATCH 690/758] patch: Update BaalHelper preAction and cleanup methods - Refactored BaalHelper to use a context object for chat event handling and cleanup, improving modularity and event listener management. - Moved towning/precasting into preAction sequence and handled if user has Config.BaalHelper.SkipTp enabled as they have no reason to go back to harrogath before continuing on to wsk2 - Updated Loader.d.ts to add generic context support for Runnable and improved typings for better type safety and clarity. --- d2bs/kolbot/libs/scripts/BaalHelper.js | 68 ++++++++++++++------------ d2bs/kolbot/sdk/types/Loader.d.ts | 33 +++++++------ 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 7c8027a08..50a88fb31 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -5,13 +5,22 @@ * */ +/** + * @typedef {ScriptContext & { chatEvent: (nick: string, msg: string) => void }} BaalHelperContext + */ const BaalHelper = new Runnable( - function BaalHelper () { - Town.goToTown(5); - Config.RandomPrecast && Precast.needOutOfTownCast() - ? Precast.doRandomPrecast(true, sdk.areas.Harrogath) - : Precast.doPrecast(true); + /** @param {BaalHelperContext} ctx */ + function BaalHelper (ctx) { + const chatEvent = function (nick, msg) { + if (nick === Config.Leader) { + if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") + || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { + quitFlag = true; + } + } + }; + ctx.chatEvent = chatEvent; if (Config.BaalHelper.SkipTP) { !me.inArea(sdk.areas.WorldstoneLvl2) && Pather.useWaypoint(sdk.areas.WorldstoneLvl2); @@ -52,7 +61,7 @@ const BaalHelper = new Runnable( if (!Pather.moveToExit(sdk.areas.ThroneofDestruction, true)) { throw new Error("Failed to move to Throne of Destruction."); } - Pather.moveToEx(15113, 5040, { callback: () => { + Pather.moveToEx(15113, 5040, { callback: function () { if (Config.BaalHelper.DollQuit && Game.getMonster(sdk.monsters.SoulKiller)) { console.log("Undead Soul Killers found, ending script."); throw new ScriptError("Undead Soul Killers found, ending script."); @@ -69,38 +78,21 @@ const BaalHelper = new Runnable( let quitFlag = false; - const chatEvent = function (nick, msg) { - if (nick === Config.Leader) { - if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") - || (Config.BaalHelper.SoulQuit && msg === "Souls found! NG.")) { - quitFlag = true; - } - } - }; - if (Config.BaalHelper.DollQuit || Config.BaalHelper.SoulQuit) { addEventListener("chatmsg", chatEvent); } - try { - if (!Misc.poll(() => { - if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { - if (quitFlag) throw new ScriptError("Burning Souls or Dolls found, ending script."); - if (Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { - return true; - } + if (!Misc.poll(() => { + if (Pather.getPortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + if (quitFlag) throw new ScriptError("Burning Souls or Dolls found, ending script."); + if (Pather.usePortal(sdk.areas.ThroneofDestruction, Config.Leader || null)) { + return true; } - - return false; - }, Time.minutes(Config.BaalHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); } - } catch (e) { - console.log(e.message); - return true; - } finally { - removeEventListener("chatmsg", chatEvent); + return false; + }, Time.minutes(Config.BaalHelper.Wait), 1000)) { + throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); } } @@ -122,6 +114,7 @@ const BaalHelper = new Runnable( Common.Baal.killBaal(); } else { Town.goToTown(); + // infinite loops are bad, TODO: add break condition, maybe a 5-10 minute timeout? while (true) { delay(500); } @@ -137,6 +130,19 @@ const BaalHelper = new Runnable( if (getTickCount() - Town.lastChores > Time.minutes(1)) { Town.doChores(); } + + Config.RandomPrecast && Precast.needOutOfTownCast() + ? Precast.doRandomPrecast(true, (Config.BaalHelper.SkipTP ? sdk.areas.WorldstoneLvl2 : sdk.areas.Harrogath)) + : Precast.doPrecast(true); + + if (!Config.BaalHelper.SkipTP) { + Town.goToTown(5); + Town.move("portalspot"); + } + }, + /** @param {BaalHelperContext} ctx */ + cleanup: function (ctx) { + removeEventListener("chatmsg", ctx.chatEvent); } } ); diff --git a/d2bs/kolbot/sdk/types/Loader.d.ts b/d2bs/kolbot/sdk/types/Loader.d.ts index fc2554819..3daa01aae 100644 --- a/d2bs/kolbot/sdk/types/Loader.d.ts +++ b/d2bs/kolbot/sdk/types/Loader.d.ts @@ -1,31 +1,31 @@ -export {}; declare global { type GlobalScript = () => boolean; - type ScriptContext = { [key: string]: any }; - interface RunnableOptions { - setup?: (ctx: ScriptContext) => any; - preAction?: (ctx: ScriptContext) => any; - postAction?: (ctx: ScriptContext) => any; - cleanup?: (ctx: ScriptContext) => any; + type ScriptContext = { [key: string]: unknown }; + + interface RunnableOptions { + setup?: (ctx: TContext) => void; + preAction?: (ctx: TContext) => void; + postAction?: (ctx: TContext) => void; + cleanup?: (ctx: TContext) => void; forceTown?: boolean; bossid?: number; startArea?: number; } // @ts-ignore - class Runnable { - constructor(action: () => boolean, options: Partial); + class Runnable { + constructor(action: (ctx: TContext) => boolean, options: Partial>); - action: (ctx: ScriptContext) => boolean; + action: (ctx: TContext) => boolean; startArea: number | null; - setup: ((ctx: ScriptContext) => any) | null; - preAction: (ctx: ScriptContext) => any; - postAction: ((ctx: ScriptContext) => any) | null; - cleanup: ((ctx: ScriptContext) => any) | null; + setup: ((ctx: TContext) => void) | null; + preAction: (ctx: TContext) => void; + postAction: ((ctx: TContext) => void) | null; + cleanup: ((ctx: TContext) => void) | null; forceTown: boolean; bossid: number | null; } - + namespace Loader { const fileList: string[]; const scriptList: string[]; @@ -43,7 +43,7 @@ declare global { function clone(obj: any): void; function copy(from: any, to: any): void; function loadScripts(): void; - function runScript(name: string, configOverride: Partial | (() => any)): boolean; + function runScript(name: string, configOverride: Partial | (() => void)): boolean; function scriptName(offset?: number): string; } @@ -53,3 +53,4 @@ declare global { const Scripts: Scripts; } +export {}; From 9a7271f26dcf534b310c9b72700a65b231472de2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Dec 2025 00:48:43 -0500 Subject: [PATCH 691/758] Improve ScriptError handling and update BaalHelper errors - Added explicit rethrowing of ScriptError exceptions in multiple core and worker modules to ensure proper error propagation. - Updated BaalHelper.js to throw ScriptError instead of generic Error for player wait timeouts, improving error specificity and consistency. --- d2bs/kolbot/D2BotMule.dbj | 1 + d2bs/kolbot/libs/core/Attack.js | 3 +++ d2bs/kolbot/libs/core/Pather.js | 3 +++ d2bs/kolbot/libs/core/Storage.js | 9 +++++++++ d2bs/kolbot/libs/core/Town.js | 5 ++++- d2bs/kolbot/libs/modules/workers/SimpleParty.js | 3 +++ d2bs/kolbot/libs/modules/workers/TownChicken.js | 17 ++++++++++++++++- d2bs/kolbot/libs/scripts/BaalHelper.js | 4 ++-- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 3 +++ 9 files changed, 44 insertions(+), 4 deletions(-) diff --git a/d2bs/kolbot/D2BotMule.dbj b/d2bs/kolbot/D2BotMule.dbj index 01050a32e..d57fe4b11 100644 --- a/d2bs/kolbot/D2BotMule.dbj +++ b/d2bs/kolbot/D2BotMule.dbj @@ -4,6 +4,7 @@ * @desc Entry script for AutoMule.js * * @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/mulelogger/MuleLogger")} */ js_strict(true); include("critical.js"); // required diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index fd5ef4332..e452556e6 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1779,6 +1779,9 @@ const Attack = { try { result = getCollision(me.area, x, y); } catch (e) { + if (e instanceof ScriptError) { + throw e; + } return false; } diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 5577520a4..843174f6e 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -991,6 +991,9 @@ const Pather = { } } } catch (e) { + if (e instanceof ScriptError) { + throw e; + } continue; } } diff --git a/d2bs/kolbot/libs/core/Storage.js b/d2bs/kolbot/libs/core/Storage.js index a644c5cb8..af9d527c5 100644 --- a/d2bs/kolbot/libs/core/Storage.js +++ b/d2bs/kolbot/libs/core/Storage.js @@ -94,6 +94,9 @@ } } } catch (e2) { + if (e2 instanceof ScriptError) { + throw e2; + } throw new Error("Storage.IsLocked error! Item info: " + item.name + " " + item.y + " " + item.sizey + " " + item.x + " " + item.sizex + " " + item.mode + " " + item.location); } @@ -547,6 +550,9 @@ return this.MoveToSpot(item, nPos.y, nPos.x); } catch (e) { + if (e instanceof ScriptError) { + throw e; + } console.log("Storage.Container.MoveTo caught error : " + e + " - " + e.toSource()); return false; @@ -629,6 +635,9 @@ return itemList; } catch (e) { + if (e instanceof ScriptError) { + throw e; + } return false; } }; diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 90ff6d27c..a4d22d7ce 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -736,6 +736,9 @@ const Town = { try { scroll.buy(true); } catch (e2) { + if ((e2 instanceof ScriptError)) { + throw e2; + } console.log(e2.message); return false; @@ -2137,7 +2140,7 @@ const Town = { if (act !== me.act) { try { Pather.useWaypoint(sdk.areas.townOfAct(act), wpmenu); - } catch (WPError) { + } catch (e) { if ((e instanceof ScriptError)) { throw e; } diff --git a/d2bs/kolbot/libs/modules/workers/SimpleParty.js b/d2bs/kolbot/libs/modules/workers/SimpleParty.js index c751f18d5..c9af49e03 100644 --- a/d2bs/kolbot/libs/modules/workers/SimpleParty.js +++ b/d2bs/kolbot/libs/modules/workers/SimpleParty.js @@ -42,6 +42,9 @@ return b.used - a.used; }).first() || { partyid: -1 }).partyid; } catch (e) { + if (e instanceof ScriptError) { + throw e; + } console.error(e); return -1; diff --git a/d2bs/kolbot/libs/modules/workers/TownChicken.js b/d2bs/kolbot/libs/modules/workers/TownChicken.js index 4f96ff056..bcdaa7e8c 100644 --- a/d2bs/kolbot/libs/modules/workers/TownChicken.js +++ b/d2bs/kolbot/libs/modules/workers/TownChicken.js @@ -208,6 +208,9 @@ } } } catch (e) { + if (e instanceof ScriptError) { + throw 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.")); @@ -238,7 +241,10 @@ if (act !== me.act) { try { Pather.useWaypoint(sdk.areas.townOfAct(act), wpmenu); - } catch (WPError) { + } catch (e) { + if (e instanceof ScriptError) { + throw e; + } throw new Error("Town.goToTown: Failed use WP"); } } @@ -282,6 +288,9 @@ while (!me.area) delay (3); if (!me.inTown) return false; } catch (e) { + if (e instanceof ScriptError) { + throw e; + } return false; } @@ -302,6 +311,9 @@ try { usePortal(null, me.name); } catch (e) { + if (e instanceof ScriptError) { + throw e; + } throw new Error("Town.visitTown: Failed to go back from town"); } } @@ -360,6 +372,9 @@ visitTown(); } catch (e) { + if (e instanceof ScriptError) { + throw e; + } Misc.errorReport(e, "TownChicken.js"); scriptBroadcast("quit"); diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 50a88fb31..7005b3603 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -41,7 +41,7 @@ const BaalHelper = new Runnable( return false; }, Time.minutes(Config.BaalHelper.Wait), 1000)) { - throw new Error( + throw new ScriptError( "Player wait timed out (" + (Config.Leader ? "Leader not" : "No players") + " found in Throne)" ); } @@ -92,7 +92,7 @@ const BaalHelper = new Runnable( return false; }, Time.minutes(Config.BaalHelper.Wait), 1000)) { - throw new Error("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); + throw new ScriptError("Player wait timed out (" + (Config.Leader ? "No leader" : "No player") + " portals found)"); } } diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index e811857c0..b64c56d3f 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -1121,6 +1121,9 @@ console.log("Attempting to find Diablo"); inviteIn() && Common.Diablo.diabloPrep(); } catch (error) { + if (error instanceof ScriptError) { + throw error; + } console.log("Diablo wasn't found. Checking seals."); Common.Diablo.runSeals(Config.Diablo.SealOrder); inviteIn() && Common.Diablo.diabloPrep(); From 22f811d2dfdebd1b87dd21d6889229568bcc0131 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:43:56 -0500 Subject: [PATCH 692/758] Add RejectedByServer code to sdk - Introduced the RejectedByServer error code (5215) --- d2bs/kolbot/libs/modules/sdk.js | 1 + d2bs/kolbot/sdk/types/sdk.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 687023499..816146471 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -4520,6 +4520,7 @@ InvalidPassword: 5207, AccountDoesNotExist: 5208, AccountIsCorrupted: 5209, + RejectedByServer: 5215, AccountMustBeAtLeast: 5217, AccountCantBeMoreThan: 5218, PasswordMustBeAtLeast: 5219, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 5c3ff7ff5..80200bbdc 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4619,6 +4619,7 @@ declare global { InvalidPassword: 5207; AccountDoesNotExist: 5208; AccountIsCorrupted: 5209; + RejectedByServer: 5215, AccountMustBeAtLeast: 5217; AccountCantBeMoreThan: 5218; PasswordMustBeAtLeast: 5219; From 43a79128ba5754d999f947d20a915e456637e172 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:46:24 -0500 Subject: [PATCH 693/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 5b9d4896f..42f5edfeb 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 5b9d4896f26c1b4c73f5c4a07e0592de0c1680b3 +Subproject commit 42f5edfeb590762b4a162ff9374be1b64f3794e1 From 228cb135f42d1c3bb02b8e97dc41ea6b93301628 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 31 Dec 2025 00:34:01 -0500 Subject: [PATCH 694/758] Add HurtBaal and HurtDiablo config options for helper scripts - Introduces HurtBaal and HurtDiablo options to Baal and Diablo helper scripts, allowing bots to reduce Baal or Diablo's health to a specified percentage instead of killing them outright. - Updates configuration files, script logic, and type definitions to support these new options. --- d2bs/kolbot/libs/config/Amazon.js | 3 +++ d2bs/kolbot/libs/config/Assassin.js | 3 +++ d2bs/kolbot/libs/config/Barbarian.js | 3 +++ d2bs/kolbot/libs/config/Druid.js | 3 +++ d2bs/kolbot/libs/config/Necromancer.js | 3 +++ d2bs/kolbot/libs/config/Paladin.js | 3 +++ d2bs/kolbot/libs/config/Sorceress.js | 3 +++ d2bs/kolbot/libs/config/_BaseConfigFile.js | 3 +++ d2bs/kolbot/libs/core/Common/Baal.js | 6 ++++-- d2bs/kolbot/libs/core/Config.js | 10 +++++++--- d2bs/kolbot/libs/scripts/BaalAssistant.js | 6 ++++-- d2bs/kolbot/libs/scripts/BaalHelper.js | 4 ++-- d2bs/kolbot/libs/scripts/DiabloHelper.js | 4 +++- d2bs/kolbot/sdk/types/Config.d.ts | 3 +++ 14 files changed, 47 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index f91d87967..98c081a90 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -204,12 +204,14 @@ function LoadConfig () { Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.BaalHelper = false; Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -225,6 +227,7 @@ function LoadConfig () { Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 0a536958a..a67076145 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -204,12 +204,14 @@ function LoadConfig () { Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.BaalHelper = false; Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -225,6 +227,7 @@ function LoadConfig () { Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 245db2182..ee8572a60 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -204,12 +204,14 @@ function LoadConfig () { Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.BaalHelper = false; Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -225,6 +227,7 @@ function LoadConfig () { Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index c7ba24b35..336a9cd48 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -204,12 +204,14 @@ function LoadConfig () { Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.BaalHelper = false; Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -225,6 +227,7 @@ function LoadConfig () { Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 2a2f9e952..89eed8d17 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -204,12 +204,14 @@ function LoadConfig () { Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.BaalHelper = false; Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -225,6 +227,7 @@ function LoadConfig () { Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index 37074a7b8..e3d002446 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -204,12 +204,14 @@ function LoadConfig () { Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.BaalHelper = false; Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -225,6 +227,7 @@ function LoadConfig () { Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 137c04645..75051782b 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -204,12 +204,14 @@ function LoadConfig () { Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.BaalHelper = false; Config.BaalHelper.Wait = 5; // minutes to wait for a runner to be in Throne Config.BaalHelper.KillNihlathak = false; // Kill Nihlathak before going to Throne Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -225,6 +227,7 @@ function LoadConfig () { Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index fde106afb..4c450d622 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -166,6 +166,7 @@ Config.DiabloHelper.SafePrecast = true; // take random WP to safely precast Config.DiabloHelper.SealOrder = ["vizier", "seis", "infector"]; // the order in which to clear the seals. If seals are excluded, they won't be checked unless diablo fails to appear Config.DiabloHelper.RecheckSeals = false; // Teleport to each seal and double-check that it was opened and boss was killed if Diablo doesn't appear + Config.DiabloHelper.HurtDiablo = 0; // Hurt Diablo to X percent health. Set to 0 to disable Scripts.AutoBaal = false; // Baal leecher with auto leader assignment Config.AutoBaal.FindShrine = false; // false = disabled, 1 = search after hot tp message, 2 = search as soon as leader is found Config.AutoBaal.LeechSpot = [15115, 5050]; // X, Y coords of Throne Room leech spot @@ -176,6 +177,7 @@ Config.BaalHelper.FastChaos = false; // Kill Diablo before going to Throne Config.BaalHelper.SoulQuit = false; // End script if Souls are found Config.BaalHelper.DollQuit = false; // End script if Dolls (Undead Soul Killers) are found. + Config.BaalHelper.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalHelper.KillBaal = true; // Kill Baal. If set to false, you must configure Config.QuitList or the bot will wait indefinitely. Config.BaalHelper.SkipTP = false; // Don't wait for a TP, go to WSK3 and wait for someone to go to throne. Anti PK measure. @@ -191,6 +193,7 @@ Config.BaalAssistant.WaitForSafeTP = false; // Set to true to wait for a safe TP message (defined in SafeTPMessage) Config.BaalAssistant.DollQuit = false; // Quit on dolls. (Hardcore players?) Config.BaalAssistant.SoulQuit = false; // Quit on Souls. (Hardcore players?) + Config.BaalAssistant.HurtBaal = 0; // Hurt Baal to X percent health. Set to 0 to disable Config.BaalAssistant.KillBaal = true; // Set to true to kill baal, if you set to false you MUST configure Config.QuitList or Config.BaalAssistant.NextGameMessage or the bot will wait indefinitely. Config.BaalAssistant.HotTPMessage = ["Hot"]; // Configure safe TP messages. Config.BaalAssistant.SafeTPMessage = ["Safe", "Clear"]; // Configure safe TP messages. diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 43ecdef62..3e6fba9f0 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -298,7 +298,7 @@ return true; }; - this.killBaal = function () { + this.killBaal = function (hurtPercent = 0) { if (me.inArea(sdk.areas.ThroneofDestruction)) { Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done @@ -326,7 +326,9 @@ if (me.inArea(sdk.areas.WorldstoneChamber)) { Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); + hurtPercent > 0 + ? Attack.hurt(sdk.monsters.Baal, hurtPercent) + : Attack.kill(sdk.monsters.Baal); Pickit.pickItems(); return true; diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index cf154d930..4c55e5788 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -8,6 +8,7 @@ /** @type {Record} */ const Scripts = {}; +/** @implements {IConfig} */ let Config = { init: function (notify = true) { const className = sdk.player.class.nameOf(me.classid); @@ -536,7 +537,8 @@ let Config = { HotTPMessage: [], SafeTPMessage: [], BaalMessage: [], - NextGameMessage: [] + NextGameMessage: [], + HurtBaal: 0, }, BaalHelper: { Wait: 120, @@ -545,7 +547,8 @@ let Config = { DollQuit: false, SoulQuit: false, KillBaal: false, - SkipTP: false + SkipTP: false, + HurtBaal: 0, }, Corpsefire: { ClearDen: false @@ -578,7 +581,8 @@ let Config = { ClearRadius: 30, /** @type {import("sdk/types/Config").DiabloSeal[]} */ SealOrder: ["vizier", "seis", "infector"], - RecheckSeals: false + RecheckSeals: false, + HurtDiablo: 0, }, /** @type {IConfig["AutoChaos"]} */ AutoChaos: { diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index 34d121d66..80cee7373 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -461,10 +461,12 @@ const BaalAssistant = new Runnable( throw new Error("Couldn't find portal."); } - if (Helper) { + if (Helper || Config.BaalAssistant.HurtBaal > 0) { delay(1000); Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); + Config.BaalAssistant.HurtBaal > 0 + ? Attack.hurt(sdk.monsters.Baal, Config.BaalAssistant.HurtBaal) + : Attack.kill(sdk.monsters.Baal); Pickit.pickItems(); } else { Pather.moveTo(15177, 5952); diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index 7005b3603..bc81b8db5 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -110,8 +110,8 @@ const BaalHelper = new Runnable( throw new Error("Couldn't clear baal waves"); } - if (Config.BaalHelper.KillBaal) { - Common.Baal.killBaal(); + if (Config.BaalHelper.KillBaal || Config.BaalHelper.HurtBaal) { + Common.Baal.killBaal(Config.BaalHelper.HurtBaal); } else { Town.goToTown(); // infinite loops are bad, TODO: add break condition, maybe a 5-10 minute timeout? diff --git a/d2bs/kolbot/libs/scripts/DiabloHelper.js b/d2bs/kolbot/libs/scripts/DiabloHelper.js index 58a091cac..f688d9d35 100644 --- a/d2bs/kolbot/libs/scripts/DiabloHelper.js +++ b/d2bs/kolbot/libs/scripts/DiabloHelper.js @@ -203,7 +203,9 @@ const DiabloHelper = new Runnable( } } - Attack.kill(sdk.monsters.Diablo); + Config.DiabloHelper.HurtDiablo > 0 + ? Attack.hurt(sdk.monsters.Diablo, Config.DiabloHelper.HurtDiablo) + : Attack.kill(sdk.monsters.Diablo); Pickit.pickItems(); } catch (e) { console.error(e); diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index f31484719..b4153ce79 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -350,6 +350,7 @@ declare global { SafeTPMessage: any[]; BaalMessage: any[]; NextGameMessage: any[]; + HurtBaal: number; }; BaalHelper: { Wait: number; @@ -359,6 +360,7 @@ declare global { SoulQuit: boolean; KillBaal: boolean; SkipTP: boolean; + HurtBaal: number; }; Corpsefire: { ClearDen: boolean; @@ -390,6 +392,7 @@ declare global { ClearRadius: number; SealOrder: DiabloSeal[]; RecheckSeals: boolean; + HurtDiablo: number; }; AutoChaos: { Leader: string; From 932b8db6579ba7fbce6667f19e68d5e291537502 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 31 Dec 2025 00:53:10 -0500 Subject: [PATCH 695/758] Add Lead system config and update config structure - Introduced LeadConfig.js for the D2BotLead system and updated setup scripts to support the new lead system. - Standardized StarterConfig documentation across multiple system config files. - Updated D2BotLead.dbj to load LeadConfig if present and adjusted D2BotFollow.dbj to include system libraries earlier. - Updated .gitignore to exclude the new lead config path. --- +setup/automule/StarterConfig.js | 5 ++++- +setup/channel/ChannelConfig.js | 9 +++++++++ +setup/charrefresher/RefresherConfig.js | 5 ++++- +setup/follow/FollowConfig.js | 5 ++++- +setup/gameaction/GameActionConfig.js | 5 ++++- +setup/lead/LeadConfig.js | 20 ++++++++++++++++++++ +setup/mulelogger/LoggerConfig.js | 5 ++++- +setup/pubjoin/PubJoinConfig.js | 5 ++++- +setup/setup.ps1 | 2 ++ .gitignore | 1 + d2bs/kolbot/D2BotFollow.dbj | 6 ++++-- d2bs/kolbot/D2BotLead.dbj | 11 +++++++---- 12 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 +setup/lead/LeadConfig.js diff --git a/+setup/automule/StarterConfig.js b/+setup/automule/StarterConfig.js index b549b4234..9f345b261 100644 --- a/+setup/automule/StarterConfig.js +++ b/+setup/automule/StarterConfig.js @@ -6,7 +6,10 @@ */ (function (module) { - // D2BotMule specific settings - for global settings see libs/starter/StarterConfig.js + /** + * @description D2BotAutoMule specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ const StarterConfig = { MinGameTime: 30, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby MaxGameTime: 60, // Maximum game length in minutes, only for continuous muling diff --git a/+setup/channel/ChannelConfig.js b/+setup/channel/ChannelConfig.js index eeaf04e62..774cb2922 100644 --- a/+setup/channel/ChannelConfig.js +++ b/+setup/channel/ChannelConfig.js @@ -6,6 +6,14 @@ */ (function (module) { + /** + * @description D2BotChannel specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ + const StarterConfig = { + // JoinRetryDelay: 5, // Time in seconds to wait before next join attempt + }; + const ChannelConfig = { SkipMutedKey: true, MutedKeyTrigger: "Your account has had all chat privileges suspended.", @@ -52,5 +60,6 @@ module.exports = { ChannelConfig: ChannelConfig, + StarterConfig: StarterConfig, }; })(module); diff --git a/+setup/charrefresher/RefresherConfig.js b/+setup/charrefresher/RefresherConfig.js index 1c0afdbdb..5a1ece152 100644 --- a/+setup/charrefresher/RefresherConfig.js +++ b/+setup/charrefresher/RefresherConfig.js @@ -26,7 +26,10 @@ }, // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - // D2BotCharRefresher specific settings - for global settings see libs/starter/StarterConfig.js + /** + * @description D2BotCharRefresher specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ StarterConfig: { // none needed right now } diff --git a/+setup/follow/FollowConfig.js b/+setup/follow/FollowConfig.js index 5374f1266..91e089fae 100644 --- a/+setup/follow/FollowConfig.js +++ b/+setup/follow/FollowConfig.js @@ -6,7 +6,10 @@ */ (function (module) { - // D2BotFollow specific settings - for global settings see libs/starter/StarterConfig.js + /** + * @description D2BotFollow specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ const StarterConfig = { JoinRetryDelay: 5, // Time in seconds to wait before next join attempt }; diff --git a/+setup/gameaction/GameActionConfig.js b/+setup/gameaction/GameActionConfig.js index 0aaa18792..164909c53 100644 --- a/+setup/gameaction/GameActionConfig.js +++ b/+setup/gameaction/GameActionConfig.js @@ -15,7 +15,10 @@ IngameTime: 60, // Time to wait before leaving game // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - // D2BotGameAction specific settings - for global settings see libs/starter/StarterConfig.js + /** + * @description D2BotGameAction specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ StarterConfig: { MinGameTime: 0, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby CreateGameDelay: 5, // Seconds to wait before creating a new game diff --git a/+setup/lead/LeadConfig.js b/+setup/lead/LeadConfig.js new file mode 100644 index 000000000..cb6357453 --- /dev/null +++ b/+setup/lead/LeadConfig.js @@ -0,0 +1,20 @@ +/** +* @filename LeadConfig.js +* @author theBGuy +* @desc Configuration file for D2BotLead system +* +*/ + +(function (module) { + /** + * @description D2BotLead specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ + const StarterConfig = { + MinGameTime: 360, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby + }; + + module.exports = { + StarterConfig: StarterConfig, + }; +})(module); diff --git a/+setup/mulelogger/LoggerConfig.js b/+setup/mulelogger/LoggerConfig.js index 77aa3eca1..883e13c62 100644 --- a/+setup/mulelogger/LoggerConfig.js +++ b/+setup/mulelogger/LoggerConfig.js @@ -33,7 +33,10 @@ }, // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // - // D2BotMuleLog specific settings - for global settings see libs/starter/StarterConfig.js + /** + * @description D2BotMuleLog specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ StarterConfig: { MinGameTime: rand(150, 180), // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby CreateGameDelay: 5, // Seconds to wait before creating a new game diff --git a/+setup/pubjoin/PubJoinConfig.js b/+setup/pubjoin/PubJoinConfig.js index 89919102f..8b95b8fe9 100644 --- a/+setup/pubjoin/PubJoinConfig.js +++ b/+setup/pubjoin/PubJoinConfig.js @@ -6,7 +6,10 @@ */ (function (module) { - // D2BotPubJoin specific settings - for global settings see libs/starter/StarterConfig.js + /** + * @description D2BotPubJoin specific settings - for global settings see libs/starter/StarterConfig.js + * @type {Partial} + */ const StarterConfig = { MinGameTime: 0, // Minimum game length in seconds. If a game is ended too soon, the rest of the time is waited in the lobby ResetCount: 0, // Reset game count back to 1 every X games. diff --git a/+setup/setup.ps1 b/+setup/setup.ps1 index 4fba44ec7..72109e971 100644 --- a/+setup/setup.ps1 +++ b/+setup/setup.ps1 @@ -65,6 +65,7 @@ $dirs = @( "$SYSTEMS_DIR\follow", "$SYSTEMS_DIR\gambling", "$SYSTEMS_DIR\gameaction", + "$SYSTEMS_DIR\lead", "$SYSTEMS_DIR\mulelogger", "$SYSTEMS_DIR\pubjoin", "$SYSTEMS_DIR\torch", @@ -110,6 +111,7 @@ Copy-IfNotExists "$SETUP_DIR\crafting\TeamsConfig.js" "$SYSTEMS_DIR\crafting\Tea Copy-IfNotExists "$SETUP_DIR\follow\FollowConfig.js" "$SYSTEMS_DIR\follow\FollowConfig.js" "FollowConfig.js to follow directory" Copy-IfNotExists "$SETUP_DIR\gambling\TeamsConfig.js" "$SYSTEMS_DIR\gambling\TeamsConfig.js" "TeamsConfig.js to gambling directory" Copy-IfNotExists "$SETUP_DIR\gameaction\GameActionConfig.js" "$SYSTEMS_DIR\gameaction\GameActionConfig.js" "GameActionConfig.js to gameaction directory" +Copy-IfNotExists "$SETUP_DIR\lead\LeadConfig.js" "$SYSTEMS_DIR\lead\LeadConfig.js" "LeadConfig.js to lead directory" Copy-IfNotExists "$SETUP_DIR\mulelogger\LoggerConfig.js" "$SYSTEMS_DIR\mulelogger\LoggerConfig.js" "LoggerConfig.js to mulelogger directory" Copy-IfNotExists "$SETUP_DIR\pubjoin\PubJoinConfig.js" "$SYSTEMS_DIR\pubjoin\PubJoinConfig.js" "PubJoinConfig.js to pubjoin directory" Copy-IfNotExists "$SETUP_DIR\torch\FarmerConfig.js" "$SYSTEMS_DIR\torch\FarmerConfig.js" "FarmerConfig.js to torch directory" diff --git a/.gitignore b/.gitignore index d7528dd18..23e1e86c5 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ d2bs/kolbot/libs/starter/StarterConfig.js d2bs/kolbot/libs/systems/gameaction/GameActionConfig.js d2bs/kolbot/libs/systems/automule/config/StarterConfig.js d2bs/kolbot/libs/systems/charrefresher/RefresherConfig.js +d2bs/kolbot/libs/systems/lead/LeadConfig.js diff --git a/d2bs/kolbot/D2BotFollow.dbj b/d2bs/kolbot/D2BotFollow.dbj index 55d351fdb..69b727b6a 100644 --- a/d2bs/kolbot/D2BotFollow.dbj +++ b/d2bs/kolbot/D2BotFollow.dbj @@ -10,6 +10,10 @@ */ include("critical.js"); // required + +// the only things we really need from these are their oog checks +includeSystemLibs(); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // const { JoinSettings, @@ -17,8 +21,6 @@ const { } = require("./libs/systems/follow/FollowConfig"); Object.assign(Starter.Config, StarterConfig); -// the only things we really need from these are their oog checks -includeSystemLibs(); const Overrides = require("./libs/modules/Override"); diff --git a/d2bs/kolbot/D2BotLead.dbj b/d2bs/kolbot/D2BotLead.dbj index 007cc85c6..6f74e20e9 100644 --- a/d2bs/kolbot/D2BotLead.dbj +++ b/d2bs/kolbot/D2BotLead.dbj @@ -1,4 +1,3 @@ -/* eslint-disable no-fallthrough */ /** * @filename D2BotLead.dbj * @author kolton, theBGuy @@ -8,13 +7,16 @@ */ include("critical.js"); // required -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// - for global settings @see libs/starter/StarterConfig.js // the only things we really need from these are their oog checks includeSystemLibs(); -const Controls = require("./libs/modules/Control"); +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// - for global settings @see libs/starter/StarterConfig.js +if (FileTools.exists("libs/systems/lead/LeadConfig.js")) { + const { StarterConfig } = require("./libs/systems/lead/LeadConfig"); + Object.assign(Starter.Config, StarterConfig); +} if (typeof Starter.AdvancedConfig[me.profile] === "object") { Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); @@ -98,6 +100,7 @@ function main () { } DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); + while (true) { // returns true before actually in game so we can't only use this check while (me.ingame) { From d3d5896f0abaa3d2303bd8fd121445ae3397c282 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 31 Dec 2025 13:22:20 -0500 Subject: [PATCH 696/758] Fix using palace lvl 3 portal during summoner script - Using Pather.usePortal(null) failed if for any reason there was another blue portal around --- d2bs/kolbot/libs/scripts/Summoner.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/scripts/Summoner.js b/d2bs/kolbot/libs/scripts/Summoner.js index bb7c4856f..0b7c9dedc 100644 --- a/d2bs/kolbot/libs/scripts/Summoner.js +++ b/d2bs/kolbot/libs/scripts/Summoner.js @@ -19,8 +19,11 @@ const Summoner = new Runnable( } } - if (me.inArea(sdk.areas.PalaceCellarLvl3) && !Pather.usePortal(null)) { - throw new Error("Failed to move back to arcane"); + if (me.inArea(sdk.areas.PalaceCellarLvl3)) { + let portal = Game.getObject(sdk.objects.ArcaneSanctuaryPortal); + if (!portal || !Pather.usePortal(null, null, portal)) { + throw new Error("Failed to move back to arcane"); + } } if (Attack.haveKilled(sdk.monsters.TheSummoner)) { From 3f6ef62758364712a0d2f1e99dea3657fcdd403c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 31 Dec 2025 19:10:45 -0500 Subject: [PATCH 697/758] Update manualplay/AttackOverrides Attack.init for new structure --- d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js index ecf212f1b..fcaf4e37d 100644 --- a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js +++ b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js @@ -9,12 +9,12 @@ includeIfNotIncluded("core/Attack.js"); Attack.init = function (notify = false) { if (Config.Wereform) { - include("core/Attacks/wereform.js"); + ClassAttack.load(me.classid, require("../../core/Attacks/Wereform")); } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { console.log("Loading custom attack file"); - include("core/Attacks/" + Config.CustomClassAttack + ".js"); + ClassAttack.load(me.classid, require("../../core/Attacks/" + Config.CustomClassAttack)); } else { - include("core/Attacks/" + sdk.player.class.nameOf(me.classid) + ".js"); + ClassAttack.load(me.classid); } if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { From 8d63eac71bcaa541f6a2b2a5cb271b856dae82d1 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:22:46 -0500 Subject: [PATCH 698/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 42f5edfeb..0aa185cf6 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 42f5edfeb590762b4a162ff9374be1b64f3794e1 +Subproject commit 0aa185cf6251f84f5d8c35344b4a9b5805601c6a From c0301e76f10462e0326f29b053ade337cac28ecc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jan 2026 18:34:58 -0500 Subject: [PATCH 699/758] Add `ClearType` to Diablo scripts to control monsters cleared while following path to seals - Note here that if it's anything other than `0` followers using wakka will probably die --- d2bs/kolbot/libs/config/Amazon.js | 2 ++ d2bs/kolbot/libs/config/Assassin.js | 2 ++ d2bs/kolbot/libs/config/Barbarian.js | 2 ++ d2bs/kolbot/libs/config/Druid.js | 2 ++ d2bs/kolbot/libs/config/Necromancer.js | 2 ++ d2bs/kolbot/libs/config/Paladin.js | 2 ++ d2bs/kolbot/libs/config/Sorceress.js | 2 ++ d2bs/kolbot/libs/config/_BaseConfigFile.js | 2 ++ d2bs/kolbot/libs/core/Common/Diablo.js | 17 +++++++++++------ d2bs/kolbot/libs/core/Config.js | 2 ++ 10 files changed, 29 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 98c081a90..0b043dfca 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -105,6 +105,7 @@ function LoadConfig () { Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -196,6 +197,7 @@ function LoadConfig () { Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index a67076145..04fe808ec 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -105,6 +105,7 @@ function LoadConfig () { Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -196,6 +197,7 @@ function LoadConfig () { Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index ee8572a60..4281257cc 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -105,6 +105,7 @@ function LoadConfig () { Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -196,6 +197,7 @@ function LoadConfig () { Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index 336a9cd48..d2dd273a4 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -105,6 +105,7 @@ function LoadConfig () { Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -196,6 +197,7 @@ function LoadConfig () { Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 89eed8d17..582dd70c2 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -105,6 +105,7 @@ function LoadConfig () { Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -196,6 +197,7 @@ function LoadConfig () { Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index e3d002446..e97f3a5a8 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -105,6 +105,7 @@ function LoadConfig () { Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -196,6 +197,7 @@ function LoadConfig () { Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 75051782b..61af5367c 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -105,6 +105,7 @@ function LoadConfig () { Config.Hephasto.ClearRiver = false; // Clear river after killing Hephasto Config.Hephasto.ClearType = 0xF; // 0xF = skip normal, 0x7 = champions/bosses, 0 = all Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -196,6 +197,7 @@ function LoadConfig () { Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 4c450d622..95f6f4393 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -99,6 +99,7 @@ Scripts.Izual = false; Scripts.Hephasto = false; Scripts.Diablo = false; + Config.Diablo.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.Diablo.ClearRadius = 30; // Range cleared while following path to seals Config.Diablo.WalkClear = false; // Disable teleport while clearing to protect leechers Config.Diablo.Entrance = true; // Start from entrance @@ -158,6 +159,7 @@ Scripts.SealLeecher = false; // Enter safe portals to Chaos. Leader should run SealLeader. Scripts.DiabloHelper = false; // Chaos helper, kills monsters and doesn't open seals on its own. Config.DiabloHelper.Wait = 5; // minutes to wait for a runner to be in Chaos. If Config.Leader is set, it will wait only for the leader. + Config.DiabloHelper.ClearType = 0; // Monster spectype to kill while following path to seals. 0xF = skip normal, 0x7 = champions/bosses, 0 = all Config.DiabloHelper.ClearRadius = 30; // Range cleared while following path to seals Config.DiabloHelper.Entrance = true; // Start from entrance. Set to false to start from star. Config.DiabloHelper.SkipTP = false; // Don't wait for town portal and directly head to chaos. It will clear monsters around chaos entrance and wait for the runner. diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 9cba39c5e..62a7241e1 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -17,6 +17,7 @@ diabloSpawned: false, diaWaitTime: Time.seconds(30), clearRadius: 30, + clearType: sdk.monsters.spectype.All, done: false, waitForGlow: false, sealOrder: [], @@ -195,7 +196,7 @@ let node = new PathNode(path[i][0], path[i][1]); Pather.moveTo(node.x, node.y, 3, node.distance > 50); - Attack.clear(this.clearRadius, 0, false, _Diablo.sort); + Attack.clear(this.clearRadius, this.clearType, false, _Diablo.sort); // Push cleared positions so they can be checked for strays this.cleared.push(path[i]); @@ -218,9 +219,10 @@ for (let i = 0; i < this.cleared.length; i += 1) { let node = new PathNode(this.cleared[i][0], this.cleared[i][1]); if (node.distanceTo(monster) < 30 - && Attack.validSpot(monster.x, monster.y)) { + && Attack.validSpot(monster.x, monster.y) + ) { Pather.moveToUnit(monster); - Attack.clear(15, 0, false, _Diablo.sort); + Attack.clear(15, this.clearType, false, _Diablo.sort); break; } @@ -262,11 +264,14 @@ tkSeal: function (seal) { if (!Skill.useTK(seal)) return false; + const checkSeal = function () { + return seal.mode; + }; + for (let i = 0; i < 5; i++) { seal.distance > 20 && Attack.getIntoPosition(seal, 18, sdk.collision.WallOrRanged); - if (Packet.telekinesis(seal) - && Misc.poll(() => seal.mode, 1000, 100)) { + if (Packet.telekinesis(seal) && Misc.poll(checkSeal, 1000, 100)) { break; } } @@ -639,7 +644,7 @@ * @returns {boolean} */ preattack: function (id) { - const coords = (() => { + const coords = (function () { switch (id) { case getLocaleString(sdk.locale.monsters.GrandVizierofChaos): return _Diablo.vizLayout === 1 ? [7676, 5295] : [7684, 5318]; diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 4c55e5788..2882dac03 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -568,6 +568,7 @@ let Config = { StarTP: "Star TP up", DiabloMsg: "Diablo", ClearRadius: 30, + ClearType: sdk.monsters.spectype.All, /** @type {import("sdk/types/Config").DiabloSeal[]} */ SealOrder: ["vizier", "seis", "infector"] }, @@ -579,6 +580,7 @@ let Config = { OpenSeals: false, SafePrecast: true, ClearRadius: 30, + ClearType: sdk.monsters.spectype.All, /** @type {import("sdk/types/Config").DiabloSeal[]} */ SealOrder: ["vizier", "seis", "infector"], RecheckSeals: false, From 3a1f92f401910b30581e58df5c094ba60cc1e698 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 12 Jan 2026 18:46:52 -0500 Subject: [PATCH 700/758] Add `docubing` and `makerunewords` cmds to manualplay - Use with caution --- .../libs/manualplay/libs/AttackOverrides.js | 33 ------------- d2bs/kolbot/libs/manualplay/main.js | 20 ++++---- .../libs/manualplay/threads/MapHelper.js | 46 ++++++++++++------- 3 files changed, 41 insertions(+), 58 deletions(-) delete mode 100644 d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js diff --git a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js b/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js deleted file mode 100644 index fcaf4e37d..000000000 --- a/d2bs/kolbot/libs/manualplay/libs/AttackOverrides.js +++ /dev/null @@ -1,33 +0,0 @@ -/** -* @filename AttackOverrides.js -* @author kolton, theBGuy -* @desc Attack.js fixes to improve functionality for map mode -* -*/ - -includeIfNotIncluded("core/Attack.js"); - -Attack.init = function (notify = false) { - if (Config.Wereform) { - ClassAttack.load(me.classid, require("../../core/Attacks/Wereform")); - } else if (Config.CustomClassAttack && FileTools.exists("libs/core/Attacks/" + Config.CustomClassAttack + ".js")) { - console.log("Loading custom attack file"); - ClassAttack.load(me.classid, require("../../core/Attacks/" + Config.CustomClassAttack)); - } else { - ClassAttack.load(me.classid); - } - - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { - notify && console.log("ÿc1Bad attack config. Don't expect your bot to attack."); - } - - this.getPrimarySlot(); - Skill.init(); - - if (me.expansion) { - Precast.checkCTA(); - this.checkInfinity(); - this.checkAuradin(); - } -}; - diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index 08c96330a..d53d84fee 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -233,15 +233,19 @@ function main () { } break; - case "make": - { - let className = sdk.player.class.nameOf(me.classid); - if (!FileTools.exists("libs/manualplay/config/" + className + "." + me.name + ".js")) { - FileTools.copy("libs/manualplay/config/" + className + ".js", "libs/manualplay/config/" + className + "." + me.name + ".js"); - D2Bot.printToConsole("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); - log("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); - } + case "make": { + let className = sdk.player.class.nameOf(me.classid); + if (!FileTools.exists("libs/manualplay/config/" + className + "." + me.name + ".js")) { + FileTools.copy("libs/manualplay/config/" + className + ".js", "libs/manualplay/config/" + className + "." + me.name + ".js"); + D2Bot.printToConsole("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); + log("libs/manualplay/config/" + className + "." + me.name + ".js has been created. Configure the bot and reload to apply changes"); } + + break; + } + case "docubing": + case "makerunewords": + qolObj.action = cmd; break; default: diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 51fd5ebe2..762dad881 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -4,7 +4,9 @@ * @credits kolton * @desc MapHelper used in conjuction with main.js * +* @typedef {import("../../../sdk/globals")} */ + js_strict(true); include("critical.js"); // required @@ -73,6 +75,10 @@ function main () { Attack.init(true); Pickit.init(); Storage.Init(); + Runewords.init(); + Cubing.init(); + LocalChat.init(); + addEventListener("scriptmsg", function (msg) { action = msg; }); @@ -427,27 +433,33 @@ function main () { } break; - case "stack": - { - let quantity = obj.params.length > 0 && !isNaN(Number(obj.params[0])) - ? Number(obj.params[0]) - : 10; - switch (obj.action) { - case "thawing": - Town.buyPots(quantity, "Thawing", true, true); + case "stack": { + let quantity = obj.params.length > 0 && !isNaN(Number(obj.params[0])) + ? Number(obj.params[0]) + : 10; + switch (obj.action) { + case "thawing": + Town.buyPots(quantity, "Thawing", true, true); - break; - case "antidote": - Town.buyPots(quantity, "Antidote", true, true); + break; + case "antidote": + Town.buyPots(quantity, "Antidote", true, true); - break; - case "stamina": - Town.buyPots(quantity, "Stamina", true, true); + break; + case "stamina": + Town.buyPots(quantity, "Stamina", true, true); - break; - } + break; } - + break; + } + case "makerunewords": + Runewords.makeRunewords(); + + break; + case "docubing": + Cubing.doCubing(); + break; } } From 118097c8e19da5b6f7eafa2299256ddaaaffb198 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 18 Jan 2026 20:10:15 -0500 Subject: [PATCH 701/758] Add Config.SkipId to default configs & Create new Config.AdvancedSkipCheck - SkipId already existed was just missing from the configs, it allows skipping specific monster id's like fire towers - Create new AdvancedSkipCheck for granular control, maybe you only want to skip a lighting immune version of a specific monster but can attack it otherwise --- d2bs/kolbot/libs/config/Amazon.js | 15 ++++ d2bs/kolbot/libs/config/Assassin.js | 15 ++++ d2bs/kolbot/libs/config/Barbarian.js | 15 ++++ d2bs/kolbot/libs/config/Druid.js | 15 ++++ d2bs/kolbot/libs/config/Necromancer.js | 15 ++++ d2bs/kolbot/libs/config/Paladin.js | 15 ++++ d2bs/kolbot/libs/config/Sorceress.js | 15 ++++ d2bs/kolbot/libs/core/Attack.js | 87 ++++++++++++++++++- d2bs/kolbot/libs/core/Config.js | 4 + d2bs/kolbot/libs/manualplay/config/Amazon.js | 15 ++++ .../kolbot/libs/manualplay/config/Assassin.js | 15 ++++ .../libs/manualplay/config/Barbarian.js | 15 ++++ d2bs/kolbot/libs/manualplay/config/Druid.js | 15 ++++ .../libs/manualplay/config/Necromancer.js | 15 ++++ d2bs/kolbot/libs/manualplay/config/Paladin.js | 15 ++++ .../libs/manualplay/config/Sorceress.js | 15 ++++ d2bs/kolbot/sdk/types/Config.d.ts | 11 +++ 17 files changed, 310 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 0b043dfca..5116b1cd8 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -538,9 +538,24 @@ function LoadConfig () { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + // ########################### // /* ##### ATTACK SETTINGS ##### */ // ########################### // diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 04fe808ec..2934a61c7 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -538,9 +538,24 @@ function LoadConfig () { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + // ########################### // /* ##### ATTACK SETTINGS ##### */ // ########################### // diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index 4281257cc..c34503b37 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -538,9 +538,24 @@ function LoadConfig () { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + // ########################### // /* ##### ATTACK SETTINGS ##### */ // ########################### // diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index d2dd273a4..c962bc41a 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -538,9 +538,24 @@ function LoadConfig () { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + // ########################### // /* ##### ATTACK SETTINGS ##### */ // ########################### // diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 582dd70c2..c667ca937 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -538,9 +538,24 @@ function LoadConfig () { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + // ########################### // /* ##### ATTACK SETTINGS ##### */ // ########################### // diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index e97f3a5a8..f1ec0a142 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -538,9 +538,24 @@ function LoadConfig () { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + // ########################### // /* ##### ATTACK SETTINGS ##### */ // ########################### // diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index 61af5367c..d6e4b081d 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -537,9 +537,24 @@ function LoadConfig () { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + // ########################### // /* ##### ATTACK SETTINGS ##### */ // ########################### // diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index e452556e6..5680c4966 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -45,7 +45,7 @@ const Attack = { }, // Initialize attacks - init: function () { + init: function (notify = true) { // TODO: properly handle loading wereform and custom files so they work with LazyLoader and get the correct types if (Config.Wereform) { ClassAttack.load(me.classid, require("./Attacks/Wereform")); @@ -56,7 +56,7 @@ const Attack = { ClassAttack.load(me.classid); } - if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { + if (notify && (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0)) { showConsole(); console.warn( "ÿc1Bad attack config. Don't expect your bot to attack." + "\n" @@ -1994,6 +1994,89 @@ const Attack = { return true; } + if (Config.AdvancedSkipCheck.length) { + for (let check of Config.AdvancedSkipCheck) { + if (typeof check === "function") { + if (check(unit)) { + return true; + } + } else if (typeof check === "object") { + let shouldSkip = Object.keys(check).length > 0; + + for (let key in check) { + switch (key) { + case "classid": + if (check[key] === (unit.classid)) { + shouldSkip = shouldSkip && true; + } else { + shouldSkip = false; + } + break; + case "name": + if (check[key].includes(unit.name.toLowerCase())) { + shouldSkip = shouldSkip && true; + } else { + shouldSkip = false; + } + break; + case "spectype": + if (check[key] === (unit.spectype)) { + shouldSkip = shouldSkip && true; + } else { + shouldSkip = false; + } + break; + case "enchant": + if (Array.isArray(check[key])) { + let skipEnchant = check[key].every(function (enchant) { + return unit.getEnchant(enchant); + }); + if (skipEnchant) { + shouldSkip = shouldSkip && true; + } else { + shouldSkip = false; + } + } + break; + case "aura": + if (Array.isArray(check[key])) { + let skipAura = check[key].every(function (aura) { + return unit.getState(aura); + }); + if (skipAura) { + shouldSkip = shouldSkip && true; + } else { + shouldSkip = false; + } + } + break; + case "immunity": + if (Array.isArray(check[key])) { + let skipImmune = check[key].every(function (immune) { + return !Attack.checkResist(unit, immune); + }); + if (skipImmune) { + shouldSkip = shouldSkip && true; + } else { + shouldSkip = false; + } + } + break; + } + + if (!shouldSkip) { + break; + } + } + + if (shouldSkip) { + console.log("ÿc1Skip Advanced Check: " + unit.name); + return true; + } + } + } + } + let tempArray = []; // EnchantLoop: // Skip enchanted monsters diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 2882dac03..7306c0257 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -273,6 +273,10 @@ let Config = { SkipImmune: [], SkipAura: [], SkipException: [], + /** + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + */ + AdvancedSkipCheck: [], /** @type {DamageType[]} */ ImmunityException: [], /** @type {number[]} */ diff --git a/d2bs/kolbot/libs/manualplay/config/Amazon.js b/d2bs/kolbot/libs/manualplay/config/Amazon.js index 9e35ceb1c..fd5df145c 100644 --- a/d2bs/kolbot/libs/manualplay/config/Amazon.js +++ b/d2bs/kolbot/libs/manualplay/config/Amazon.js @@ -107,9 +107,24 @@ function LoadConfig() { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + /* Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Assassin.js b/d2bs/kolbot/libs/manualplay/config/Assassin.js index 2ab138f75..6f9e5c52a 100644 --- a/d2bs/kolbot/libs/manualplay/config/Assassin.js +++ b/d2bs/kolbot/libs/manualplay/config/Assassin.js @@ -107,9 +107,24 @@ function LoadConfig() { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + /* Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Barbarian.js b/d2bs/kolbot/libs/manualplay/config/Barbarian.js index 58aac3409..b88f4b161 100644 --- a/d2bs/kolbot/libs/manualplay/config/Barbarian.js +++ b/d2bs/kolbot/libs/manualplay/config/Barbarian.js @@ -107,9 +107,24 @@ function LoadConfig() { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + /* Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Druid.js b/d2bs/kolbot/libs/manualplay/config/Druid.js index c51606e04..ea20146cb 100644 --- a/d2bs/kolbot/libs/manualplay/config/Druid.js +++ b/d2bs/kolbot/libs/manualplay/config/Druid.js @@ -107,9 +107,24 @@ function LoadConfig() { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + /* Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Necromancer.js b/d2bs/kolbot/libs/manualplay/config/Necromancer.js index 727e69d5b..5655035ca 100644 --- a/d2bs/kolbot/libs/manualplay/config/Necromancer.js +++ b/d2bs/kolbot/libs/manualplay/config/Necromancer.js @@ -107,9 +107,24 @@ function LoadConfig() { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + /* Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Paladin.js b/d2bs/kolbot/libs/manualplay/config/Paladin.js index cffec69f3..58aa71365 100644 --- a/d2bs/kolbot/libs/manualplay/config/Paladin.js +++ b/d2bs/kolbot/libs/manualplay/config/Paladin.js @@ -107,9 +107,24 @@ function LoadConfig() { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + /* Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt diff --git a/d2bs/kolbot/libs/manualplay/config/Sorceress.js b/d2bs/kolbot/libs/manualplay/config/Sorceress.js index 890cec220..bdb2a143d 100644 --- a/d2bs/kolbot/libs/manualplay/config/Sorceress.js +++ b/d2bs/kolbot/libs/manualplay/config/Sorceress.js @@ -110,9 +110,24 @@ function LoadConfig() { Config.SkipEnchant = []; // Skip monsters with auras. Possible options: "fanaticism", "might", "holy fire", "blessed aim", "holy freeze", "holy shock". Conviction is bugged, don't use it. Config.SkipAura = []; + // Skip specific monsters by classid. For a list of monster names and ids, see -> \kolbot\libs\modules\sdk.js or usee sdk.monsters.MonsterID enums. + // Example: Config.SkipId = [sdk.monsters.FireTower, 310]; + Config.SkipId = []; // Uncomment the following line to always attempt to kill these bosses despite immunities and mods //Config.SkipException = [getLocaleString(sdk.locale.monsters.GrandVizierofChaos), getLocaleString(sdk.locale.monsters.LordDeSeis), getLocaleString(sdk.locale.monsters.InfectorofSouls)]; // vizier, de seis, infector + /** + * Advanced Skip config. Allows for more granular control over which monsters to skip. + * @type {({ classid?: number, name?: string, spectype?: number, enchant?: number[], aura?: number[], immunity?: DamageType[] }|((unit: Monster) => boolean))[]} + * Multiple entries are separated by commas + */ + Config.AdvancedSkipCheck = [ + // { + // name: getLocaleString(sdk.locale.monsters.Pindleskin), + // immunity: ["lightning"] + // } + ]; + /* Attack config * To disable an attack, set it to -1 * Skills MUST be POSITIVE numbers. For reference see ...\kolbot\sdk\skills.txt diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index b4153ce79..22fdfbf16 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -132,6 +132,17 @@ declare global { SkipImmune: string[]; SkipAura: string[]; SkipException: (number | string)[]; + AdvancedSkipCheck: ( + | { + classid?: number; + name?: string; + spectype?: number; + enchant?: number[]; + aura?: number[]; + immunity?: DamageType[]; + } + | ((unit: Monster) => boolean) + )[]; ImmunityException: DamageType[]; ScanShrines: number[]; AutoShriner: boolean; From 09d8c79b67e6edd15d70cf7b3ff615c42e671e71 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:35:44 -0500 Subject: [PATCH 702/758] Fix missing `KeyItem` for token recipe --- d2bs/kolbot/libs/core/Config.js | 3 ++- d2bs/kolbot/libs/core/Cubing.js | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index 7306c0257..b5ccf4aa8 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -8,7 +8,7 @@ /** @type {Record} */ const Scripts = {}; -/** @implements {IConfig} */ +/** @type {IConfig} */ let Config = { init: function (notify = true) { const className = sdk.player.class.nameOf(me.classid); @@ -312,6 +312,7 @@ let Config = { Cubing: false, CubeRepair: false, RepairPercent: 40, + /** @type {CubingRecipe[]} */ Recipes: [], MakeRunewords: false, /** diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 3c3b58f8f..9583ec728 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -588,8 +588,11 @@ const Cubing = { const recipeObj = Object.assign(baseRecipeObj, extendedOpts); switch (index) { - case Recipe.Gem: case Recipe.Token: + case Recipe.Gem: + if (index === Recipe.Token) { + recipeObj.KeyItem = sdk.items.TokenofAbsolution; + } recipeObj.AlwaysEnabled = true; break; From 040a965623283b06db2feac5b3796ce923e6cb30 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 24 Jan 2026 19:00:44 -0500 Subject: [PATCH 703/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 0aa185cf6..586cceb1e 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 0aa185cf6251f84f5d8c35344b4a9b5805601c6a +Subproject commit 586cceb1eaf67372502143fb2240c34ef6e436cb From 1e22cad12b5058f127498cb28351d8336f4246fb Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 3 Feb 2026 23:06:12 -0700 Subject: [PATCH 704/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 586cceb1e..f730e61f1 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 586cceb1eaf67372502143fb2240c34ef6e436cb +Subproject commit f730e61f1f0b533a8124493b499f00eb16c4a9fa From f081d16c7cd4cceb44405e58a0862a0e6fb4615f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 5 Feb 2026 17:23:26 -0700 Subject: [PATCH 705/758] Wrap Item.logItem in try/catch - handle rare instance when we lost reference to item, causing undefined error --- d2bs/kolbot/libs/core/Item.js | 102 ++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index 3ce80fbb1..41f7990ee 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -516,57 +516,65 @@ const Item = { * @param {string} keptLine */ logItem: function (action, unit, keptLine) { - if (!this.useItemLog) 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 skip of Config.SkipLogging) { - if (skip === unit.classid || skip === unit.code) return false; - } - - let lastArea; - const name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); - const color = (unit.getColor() || -1); - const code = this.getItemCode(unit); - const sock = unit.getItem(); - let desc = this.getItemDesc(unit); - - if (action.match("kept", "i")) { - lastArea = DataFile.getStats().lastArea; - lastArea && (desc += ("\n\\xffc0Area: " + lastArea)); - } + try { + if (!this.useItemLog) 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 skip of Config.SkipLogging) { + if (skip === unit.classid || skip === unit.code) return false; + } - if (sock) { - do { - if (sock.itemType === sdk.items.type.Jewel) { - desc += "\n\n"; - desc += this.getItemDesc(sock); - } - } while (sock.getNext()); - } + let lastArea; + const name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); + const color = (unit.getColor() || -1); + const code = this.getItemCode(unit); + const sock = unit.getItem(); + let desc = this.getItemDesc(unit); - keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); - desc += "$" + (unit.ethereal ? ":eth" : ""); - const formattedDate = new Date().dateStamp().replace(/\//g, "-"); - - const itemObj = { - title: formattedDate + " " + action + " " + name, - description: desc, - image: code, - textColor: unit.quality, - itemColor: color, - header: "", - sockets: this.getItemSockets(unit) - }; + if (action.match("kept", "i")) { + lastArea = DataFile.getStats().lastArea; + lastArea && (desc += ("\n\\xffc0Area: " + lastArea)); + } - D2Bot.printToItemLog(itemObj); + if (sock) { + do { + if (sock.itemType === sdk.items.type.Jewel) { + desc += "\n\n"; + desc += this.getItemDesc(sock); + } + } while (sock.getNext()); + } - return true; + keptLine && (desc += ("\n\\xffc0Line: " + keptLine)); + desc += "$" + (unit.ethereal ? ":eth" : ""); + const formattedDate = new Date().dateStamp().replace(/\//g, "-"); + + const itemObj = { + title: formattedDate + " " + action + " " + name, + description: desc, + image: code, + textColor: unit.quality, + itemColor: color, + header: "", + sockets: this.getItemSockets(unit) + }; + + D2Bot.printToItemLog(itemObj); + + return true; + } catch (e) { + if (e instanceof ScriptError) { + throw e; + } + console.error("Error logging item: ", e); + return false; + } }, /** From 4826e12e6e1101617844258e0d943b4ea67ae127 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:15:49 -0500 Subject: [PATCH 706/758] Fix typo in Cubing - `sdk.items.TokenofAbsolution` -> `sdk.items.quest.TokenofAbsolution` --- d2bs/kolbot/libs/core/Cubing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 9583ec728..879a53409 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -591,7 +591,7 @@ const Cubing = { case Recipe.Token: case Recipe.Gem: if (index === Recipe.Token) { - recipeObj.KeyItem = sdk.items.TokenofAbsolution; + recipeObj.KeyItem = sdk.items.quest.TokenofAbsolution; } recipeObj.AlwaysEnabled = true; From 493ff82a0cff1b5ab57bc928a0a5f8b67b5323c3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:31:55 -0500 Subject: [PATCH 707/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index f730e61f1..6ac52c739 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit f730e61f1f0b533a8124493b499f00eb16c4a9fa +Subproject commit 6ac52c73990d65aef1b3fa3f9ee1452a6b02a5d8 From fa7c0703755b27f901db3ab9d91706651ea01908 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:19:46 -0500 Subject: [PATCH 708/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 6ac52c739..f69a3bff4 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 6ac52c73990d65aef1b3fa3f9ee1452a6b02a5d8 +Subproject commit f69a3bff4816e5e20b21aaf28e993bb9ea0db2e0 From 1cd7bb3c3aba5feb67562e37fb242c665d2fa5e0 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:05:48 -0500 Subject: [PATCH 709/758] Add Baal silent option & minor refactors - Add Config.Baal.Silent to allow suppressing public Baal announcements and messages; guard announce/say calls in Baal, Common/Baal and BaalAssistant. - Update the TypeScript definition for the new config flag. - Also include several non-behavioral cleanups and small fixes: improve JSDoc/return typing in Cubing.checkRecipe, convert an inline callback to a function in Misc and remove the deprecated townCheck, fix waypoint filtering and moveTo retry behavior in Pather, tighten Pickit checks (null-safety, helper for matching items) and formatting, remove LocalChat.init from MapHelper startup, and drop an unused eslint header in AutoMule. --- .gitignore | 1 + d2bs/kolbot/libs/core/Common/Baal.js | 4 +- d2bs/kolbot/libs/core/Config.js | 3 +- d2bs/kolbot/libs/core/Cubing.js | 4 +- d2bs/kolbot/libs/core/Misc.js | 52 +++---------------- d2bs/kolbot/libs/core/Pather.js | 11 ++-- d2bs/kolbot/libs/core/Pickit.js | 20 +++++-- .../libs/manualplay/threads/MapHelper.js | 1 - d2bs/kolbot/libs/scripts/Baal.js | 8 ++- d2bs/kolbot/libs/scripts/BaalAssistant.js | 3 +- d2bs/kolbot/libs/systems/automule/AutoMule.js | 1 - d2bs/kolbot/sdk/types/Config.d.ts | 1 + 12 files changed, 47 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index 23e1e86c5..903db5762 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ d2bs/kolbot/libs/systems/gameaction/GameActionConfig.js d2bs/kolbot/libs/systems/automule/config/StarterConfig.js d2bs/kolbot/libs/systems/charrefresher/RefresherConfig.js d2bs/kolbot/libs/systems/lead/LeadConfig.js +/d2bs/kolbot/libs/core/Overrides diff --git a/d2bs/kolbot/libs/core/Common/Baal.js b/d2bs/kolbot/libs/core/Common/Baal.js index 3e6fba9f0..23f0f2247 100644 --- a/d2bs/kolbot/libs/core/Common/Baal.js +++ b/d2bs/kolbot/libs/core/Common/Baal.js @@ -300,7 +300,9 @@ this.killBaal = function (hurtPercent = 0) { if (me.inArea(sdk.areas.ThroneofDestruction)) { - Config.PublicMode && Loader.scriptName() === "Baal" && say(Config.Baal.BaalMessage); + if (Config.PublicMode && Loader.scriptName() === "Baal" && !Config.Baal.Silent) { + say(Config.Baal.BaalMessage); + } me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done Pather.moveTo(15090, 5008); Misc.poll(function () { diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index b5ccf4aa8..b7a0fe468 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -525,7 +525,8 @@ let Config = { KillBaal: false, HotTPMessage: "Hot TP!", SafeTPMessage: "Safe TP!", - BaalMessage: "Baal!" + BaalMessage: "Baal!", + Silent: false }, BaalAssistant: { KillNihlathak: false, diff --git a/d2bs/kolbot/libs/core/Cubing.js b/d2bs/kolbot/libs/core/Cubing.js index 879a53409..b0f2f5c18 100644 --- a/d2bs/kolbot/libs/core/Cubing.js +++ b/d2bs/kolbot/libs/core/Cubing.js @@ -832,10 +832,12 @@ const Cubing = { /** * @param {recipeObj} recipe - * @returns {boolean} + * @returns {ItemUnit[] | boolean} */ checkRecipe: function (recipe) { + /** @type {number[]} */ let usedGids = []; + /** @type {ItemUnit[]} */ let matchList = []; for (let i = 0; i < recipe.Ingredients.length; i += 1) { diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index b8721e00e..d7392c79c 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -972,11 +972,13 @@ const Misc = (function () { 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"); - return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; - } }); + Pather.moveToEx(coords[0], coords[1], { + minDist: Skill.haveTK ? 20 : 5, + callback: function () { + let shrine = Game.getObject("shrine"); + return !!shrine && shrine.x === coords[0] && shrine.y === coords[1]; + } + }); let shrine = Game.getObject("shrine"); @@ -1004,46 +1006,6 @@ const Misc = (function () { return result; }, - /** - * Go to town when low on hp/mp or when out of potions. can be upgraded to check for curses etc. - * @deprecated - will be removed in future versions - * @returns {boolean} - */ - townCheck: function () { - if (!me.canTpToTown()) return false; - - let tTick = getTickCount(); - let check = false; - - if (Config.TownCheck && !me.inTown) { - try { - if (me.needPotions() || (Config.OpenChests.Enabled && me.needKeys())) { - check = true; - } - } catch (e) { - if ((e instanceof ScriptError)) { - throw e; - } - return false; - } - - if (check) { - // check that townchicken is running - so we don't spam needing potions if it isn't - let townChick = getScript("threads/TownChicken.js"); - if (!townChick || townChick && !townChick.running) { - return false; - } - - townChick.send("townCheck"); - console.log("townCheck check Duration: " + (getTickCount() - tTick)); - - return true; - } - } - - return false; - }, - /** * Log someone's gear * @param {string} name diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 843174f6e..c7e747405 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1537,7 +1537,9 @@ const Pather = { switch (targetArea) { case "random": let validWps = this.nonTownWpAreas - .filter(area => getWaypoint(this.wpAreas.indexOf(area))); + .filter(function (area) { + return getWaypoint(this.wpAreas.indexOf(area)); + }); if (!validWps.length) { if (me.inTown && Pather.moveToExit(me.area + 1, true)) { break; @@ -1557,8 +1559,9 @@ const Pather = { me.cancel(); console.log("Trying to get the waypoint: " + getAreaName(targetArea)); me.overhead("Trying to get the waypoint"); - if (this.getWP(targetArea)) return true; - + if (this.getWP(targetArea)) { + return true; + } throw new Error("Pather.useWaypoint: Failed to go to waypoint " + getAreaName(targetArea)); } @@ -1572,7 +1575,7 @@ const Pather = { 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); + !!coord && this.moveTo(coord.x, coord.y, 3); delay(200); Packet.flash(me.gid, pingDelay); diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index cbe8ad251..ddbdcb49d 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -119,7 +119,9 @@ const Pickit = { */ canPick: function (unit) { if (!unit) return false; - if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) return false; + if (sdk.quest.items.includes(unit.classid) && me.getItem(unit.classid)) { + return false; + } switch (unit.itemType) { case sdk.items.type.Gold: @@ -283,13 +285,17 @@ const Pickit = { }; // make sure we have essentials - no pickit files loaded - if (rval.result === Pickit.Result.UNWANTED && Config.PickitFiles.length === 0 - && Pickit.essentials.includes(unit.itemType) && this.canPick(unit)) { + if (rval.result === Pickit.Result.UNWANTED + && Config.PickitFiles.length === 0 + && Pickit.essentials.includes(unit.itemType) + && this.canPick(unit) + ) { return resultObj(Pickit.Result.WANTED, "Essentials"); } if ((unit.classid === sdk.items.runes.Ort || unit.classid === sdk.items.runes.Ral) - && Cubing.repairIngredientCheck(unit)) { + && Cubing.repairIngredientCheck(unit) + ) { return resultObj(Pickit.Result.UTILITY, "Cubing Repair Ingredients"); } @@ -607,12 +613,16 @@ const Pickit = { let item = Game.getItem(); if (item) { + /** @param {ItemUnit} check */ + let sameItem = function (check) { + return (check.gid === item.gid); + }; do { if (Pickit.ignoreList.has(item.gid)) continue; if (item.classid === sdk.items.Gold && item.distance <= 4 && Pickit.canPick(item)) { if (Pickit.pickItem(item, Pickit.Result.WANTED, "gold", 1)) continue; } - if (Pickit.pickList.some(el => el.gid === item.gid)) continue; + if (Pickit.pickList.some(sameItem)) continue; if (item.onGroundOrDropping && item.distance <= range) { Pickit.pickList.push(copyItem(item)); } diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index 762dad881..d1843d59e 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -77,7 +77,6 @@ function main () { Storage.Init(); Runewords.init(); Cubing.init(); - LocalChat.init(); addEventListener("scriptmsg", function (msg) { action = msg; diff --git a/d2bs/kolbot/libs/scripts/Baal.js b/d2bs/kolbot/libs/scripts/Baal.js index d6b4aa16a..b52cf2f44 100644 --- a/d2bs/kolbot/libs/scripts/Baal.js +++ b/d2bs/kolbot/libs/scripts/Baal.js @@ -70,7 +70,9 @@ const Baal = new Runnable( } }); if (Config.PublicMode) { - announce(); + if (!Config.Baal.Silent) { + announce(); + } Pather.moveTo(15118, 5002); Pather.makePortal(); say(Config.Baal.HotTPMessage); @@ -82,7 +84,9 @@ const Baal = new Runnable( if (Config.PublicMode) { Pather.moveTo(15118, 5045); Pather.makePortal(); - say(Config.Baal.SafeTPMessage); + if (!Config.Baal.Silent) { + say(Config.Baal.SafeTPMessage); + } Precast.doPrecast(true); } diff --git a/d2bs/kolbot/libs/scripts/BaalAssistant.js b/d2bs/kolbot/libs/scripts/BaalAssistant.js index 80cee7373..d05de72be 100644 --- a/d2bs/kolbot/libs/scripts/BaalAssistant.js +++ b/d2bs/kolbot/libs/scripts/BaalAssistant.js @@ -221,7 +221,8 @@ const BaalAssistant = new Runnable( while (Misc.inMyParty(Leader)) { if (!secondAttempt && !safeCheck && !baalCheck && !ShrineStatus && !!Config.BaalAssistant.GetShrine - && me.inArea(sdk.areas.Harrogath)) { + && me.inArea(sdk.areas.Harrogath) + ) { if (!!Config.BaalAssistant.GetShrineWaitForHotTP) { Misc.poll(() => hotCheck, Time.seconds(Config.BaalAssistant.Wait), 1000); diff --git a/d2bs/kolbot/libs/systems/automule/AutoMule.js b/d2bs/kolbot/libs/systems/automule/AutoMule.js index c8da7cea3..1b2f77abc 100644 --- a/d2bs/kolbot/libs/systems/automule/AutoMule.js +++ b/d2bs/kolbot/libs/systems/automule/AutoMule.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /** * @filename AutoMule.js * @author kolton, theBGuy diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index 22fdfbf16..e1d74cc43 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -344,6 +344,7 @@ declare global { HotTPMessage: string; SafeTPMessage: string; BaalMessage: string; + Silent: boolean; }; BaalAssistant: { KillNihlathak: boolean; From 0da9cd794c3329440b908dc3f4e636d231d3d778 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:12:40 -0500 Subject: [PATCH 710/758] Add AntiIdle worker & switch IpHunter to use it - Also change town movement to running back and forth from akara to stash --- d2bs/kolbot/libs/modules/workers/AntiIdle.js | 35 ++++++++++++++++++++ d2bs/kolbot/libs/scripts/IPHunter.js | 7 ++-- 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 d2bs/kolbot/libs/modules/workers/AntiIdle.js diff --git a/d2bs/kolbot/libs/modules/workers/AntiIdle.js b/d2bs/kolbot/libs/modules/workers/AntiIdle.js new file mode 100644 index 000000000..bf8677201 --- /dev/null +++ b/d2bs/kolbot/libs/modules/workers/AntiIdle.js @@ -0,0 +1,35 @@ +/** +* @filename AntiIdle.js +* @author theBGuy +* @desc Worker script for idleing in town to prevent disconnects +* +*/ + +(function (module, require, Worker) { + // Only load this in global scope + if (new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { + Worker.runInBackground.antiIdle = (function () { + let idleTick = 0; + return function () { + if (!me.ingame || getTickCount() - me.gamestarttime < Time.minutes(1) || !me.gameReady) return true; + if (idleTick === 0) { + idleTick = getTickCount() + Time.seconds(rand(1200, 1500)); + console.log("Anti-idle refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + if (me.gameReady) { + if (getTickCount() - idleTick > 0) { + Packet.questRefresh(); + idleTick += Time.seconds(rand(1200, 1500)); + console.log("Sent anti-idle packet, next refresh in: (" + Time.format(idleTick - getTickCount()) + ")"); + } + } else if (getLocation() !== null) { + idleTick = 0; + } + + return true; + }; + })(); + + console.log("ÿc2Kolbotÿc0 :: AntiIdle running"); + } +})(module, require, typeof Worker === "object" && Worker || require("../Worker")); diff --git a/d2bs/kolbot/libs/scripts/IPHunter.js b/d2bs/kolbot/libs/scripts/IPHunter.js index 28dee9ee5..e808c30cc 100644 --- a/d2bs/kolbot/libs/scripts/IPHunter.js +++ b/d2bs/kolbot/libs/scripts/IPHunter.js @@ -12,7 +12,8 @@ const IPHunter = new Runnable( const ip = Number(me.gameserverip.split(".")[3]); if (Config.IPHunter.IPList.includes(ip)) { - load("threads/antiidle.js"); + require("../modules/workers/AntiIdle"); + const foundMsg = "IPHunter: IP found! - [" + ip + "] Game is : " + me.gamename + "//" + me.gamepassword; D2Bot.printToConsole(foundMsg, sdk.colors.D2Bot.DarkGold); console.log(foundMsg); @@ -36,8 +37,8 @@ const IPHunter = new Runnable( me.overhead(":D IP found! - [" + ip + "]"); try { - Town.move("waypoint"); - Town.move("stash"); + Town.move(NPC.Akara, false); + Town.move("stash", false); } catch (e) { // ensure it doesnt leave game by failing to walk due to desyncing. } From f435d6fc05c19c5f6cf7a14e92ee787a9f1186c9 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:29:52 -0500 Subject: [PATCH 711/758] Add adaptive and cluster-aware graph search - Replace nearestNeighbourSearch usage in Attack.clearLevelWalk with Graph.adaptiveSearch and introduce adaptive graph search utilities. - Added Graph.analyzeGraph to infer map characteristics (avg degree, dead-end ratio, components, mapType) and tune weights, Graph.adaptiveSearch to pick the best strategy, and Graph.clusterAwareSearch to prioritize connected components and clear smaller branches first. The cluster-aware implementation includes component detection, entry-point selection, branch-size heuristics, and scoring by size/distance to reduce backtracking and avoid leaving isolated sections unexplored. Includes logging of analysis results and preserves fallback to nearestNeighbourSearch for linear maps. --- d2bs/kolbot/libs/core/Attack.js | 2 +- d2bs/kolbot/libs/modules/Graph.js | 333 ++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 5680c4966..56e969a9a 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -1423,7 +1423,7 @@ const Attack = { try { console.info(true, getAreaName(me.area), "clearLevelWalk-nearestNeighbourSearch"); let graph = new Graph(); - Graph.nearestNeighbourSearch(graph, function (room) { + Graph.adaptiveSearch(graph, function (room) { if (typeof cb === "function" && cb()) { throw new ScriptError("Clearing stopped by callback"); } diff --git a/d2bs/kolbot/libs/modules/Graph.js b/d2bs/kolbot/libs/modules/Graph.js index fb1622663..b34da8278 100644 --- a/d2bs/kolbot/libs/modules/Graph.js +++ b/d2bs/kolbot/libs/modules/Graph.js @@ -357,5 +357,338 @@ } }; + /** + * @typedef {Object} GraphAnalysis + * @property {number} vertexCount - Total number of vertices + * @property {number} avgDegree - Average neighbors per vertex + * @property {number} deadEndRatio - Ratio of dead-ends (degree <= 1) to total vertices + * @property {number} maxDegree - Maximum neighbors any vertex has + * @property {number} componentCount - Number of disconnected components + * @property {"linear" | "maze" | "open" | "hub"} mapType - Inferred map type + * @property {number} sizeWeight - Recommended size weight for scoring + * @property {number} distWeight - Recommended distance weight for scoring + */ + + /** + * Analyze graph structure to determine map characteristics and optimal tuning + * @param {Graph} graph + * @returns {GraphAnalysis} + */ + Graph.analyzeGraph = function(graph) { + let vertices = graph.vertices; + let vertexCount = vertices.length; + + if (vertexCount === 0) { + return { + vertexCount: 0, + avgDegree: 0, + deadEndRatio: 0, + maxDegree: 0, + componentCount: 0, + mapType: "open", + sizeWeight: 0.33, + distWeight: 0.7 + }; + } + + // Calculate degree statistics + let degrees = vertices.map(function(v) { + return graph.nearbyVertices(v).length; + }); + let totalDegree = degrees.reduce(function(sum, d) { return sum + d; }, 0); + let avgDegree = totalDegree / vertexCount; + let maxDegree = Math.max.apply(null, degrees); + let deadEnds = degrees.filter(function(d) { return d <= 1; }).length; + let deadEndRatio = deadEnds / vertexCount; + + // Count connected components + let visited = new Set(); + let componentCount = 0; + for (let vertex of vertices) { + if (visited.has(vertex.id)) continue; + componentCount++; + let queue = [vertex]; + visited.add(vertex.id); + while (queue.length) { + let current = queue.shift(); + let neighbors = graph.nearbyVertices(current); + for (let neighbor of neighbors) { + if (!visited.has(neighbor.id)) { + visited.add(neighbor.id); + queue.push(neighbor); + } + } + } + } + + // Determine map type based on metrics + let mapType = "open"; + let sizeWeight = 0.33; + let distWeight = 0.7; + + if (avgDegree <= 1.5 && deadEndRatio < 0.15) { + // Low connectivity, few dead-ends = linear corridor + mapType = "linear"; + sizeWeight = 0.2; // Less important to clear small branches + distWeight = 0.8; // Just follow the path + } else if (deadEndRatio > 0.3) { + // Many dead-ends = maze-like + mapType = "maze"; + sizeWeight = 0.5; // Very important to clear branches + distWeight = 0.5; // Balance with distance + } else if (avgDegree > 3 && maxDegree > 5) { + // High connectivity = open area + mapType = "open"; + sizeWeight = 0.25; + distWeight = 0.75; // Prefer nearest neighbor + } else if (componentCount > 1 || (maxDegree > 4 && avgDegree < 2.5)) { + // Multiple components or hub pattern + mapType = "hub"; + sizeWeight = 0.4; // Important to clear smaller sections + distWeight = 0.6; + } + + return { + vertexCount: vertexCount, + avgDegree: avgDegree, + deadEndRatio: deadEndRatio, + maxDegree: maxDegree, + componentCount: componentCount, + mapType: mapType, + sizeWeight: sizeWeight, + distWeight: distWeight + }; + }; + + /** + * Adaptive search that analyzes the graph and uses optimal algorithm/tuning + * @param {Graph} graph + * @param {(vertex: Vertex) => any} explore + * @param {"walk" | "teleport"} mode + */ + Graph.adaptiveSearch = function(graph, explore, mode = "walk") { + let analysis = Graph.analyzeGraph(graph); + + console.log("Graph Analysis: " + JSON.stringify({ + vertices: analysis.vertexCount, + avgDegree: analysis.avgDegree.toFixed(2), + deadEndRatio: (analysis.deadEndRatio * 100).toFixed(1) + "%", + components: analysis.componentCount, + mapType: analysis.mapType + })); + + // For linear maps, simple nearest neighbor is often sufficient and faster + if (analysis.mapType === "linear" && analysis.componentCount === 1) { + console.log("Using nearestNeighbourSearch for linear map"); + return Graph.nearestNeighbourSearch(graph, explore, mode); + } + + // For all other types, use cluster-aware with tuned weights + console.log("Using clusterAwareSearch with weights: size=" + + analysis.sizeWeight + ", dist=" + analysis.distWeight); + return Graph.clusterAwareSearch(graph, explore, mode, analysis.sizeWeight, analysis.distWeight); + }; + + /** + * Cluster-aware nearest neighbor search that identifies connected components + * and prioritizes completing smaller/closer clusters before moving on. + * This prevents leaving isolated sections unexplored until the end. + * @param {Graph} graph + * @param {(vertex: Vertex) => any} explore + * @param {"walk" | "teleport"} mode + * @param {number} [sizeWeight=0.33] - Weight for component size in scoring + * @param {number} [distWeight=0.7] - Weight for distance in scoring + */ + Graph.clusterAwareSearch = function(graph, explore, mode = "walk", sizeWeight = 0.33, distWeight = 0.7) { + /** + * Find all connected components in the remaining unvisited graph + * @returns {Vertex[][]} + */ + function findComponents() { + let unvisited = graph.vertices.filter(filterSeen); + let components = []; + let visited = new Set(); + + for (let vertex of unvisited) { + if (visited.has(vertex.id)) continue; + + // BFS to find all vertices in this component + let component = []; + let queue = [vertex]; + visited.add(vertex.id); + + while (queue.length) { + let current = queue.shift(); + component.push(current); + + let neighbors = graph.nearbyVertices(current).filter(function(v) { + return !v.seen && !visited.has(v.id); + }); + + for (let neighbor of neighbors) { + visited.add(neighbor.id); + queue.push(neighbor); + } + } + + if (component.length > 0) { + components.push(component); + } + } + + return components; + } + + /** + * Find the entry point (closest vertex) into a component from current position + * @param {Vertex[]} component + * @returns {Vertex | null} + */ + function findEntryPoint(component) { + return component.reduce(function(best, v) { + v.clearCache(); + let dist = v.pathDistance(mode); + if (dist < best.dist) { + return { vertex: v, dist: dist }; + } + return best; + }, { vertex: null, dist: Infinity }).vertex; + } + + /** + * Score a component for priority (lower = should visit sooner) + * Factors: size (smaller first), distance to entry point + * @param {Vertex[]} component + * @returns {number} + */ + function scoreComponent(component) { + let entry = findEntryPoint(component); + if (!entry) return Infinity; + + let distanceToEntry = entry.pathDistance(mode); + let size = component.length; + + // Normalize size (1 vertex = 0, larger = higher score) + let sizeScore = (size - 1) * 50; + + return (sizeScore * sizeWeight) + (distanceToEntry * distWeight); + } + + /** + * Calculate the "branch size" reachable from a vertex without backtracking through origin + * Smaller branches should be cleared first to avoid backtracking + * @param {Vertex} vertex - The vertex to measure from + * @param {Vertex} origin - The vertex we came from (don't count paths back through here) + * @param {Vertex[]} component - The component we're exploring + * @returns {number} - Number of unvisited vertices reachable + */ + function getBranchSize(vertex, origin, component) { + let visited = new Set([origin.id, vertex.id]); + let queue = [vertex]; + let count = 1; + + while (queue.length) { + let current = queue.shift(); + let neighbors = graph.nearbyVertices(current).filter(function(v) { + return !v.seen && component.includes(v) && !visited.has(v.id); + }); + + for (let neighbor of neighbors) { + visited.add(neighbor.id); + queue.push(neighbor); + count++; + } + } + + return count; + } + + /** + * Explore within a component using nearest-neighbor with dead-end awareness + * @param {Vertex[]} component + */ + function exploreComponent(component) { + let currentVertex = findEntryPoint(component); + + while (currentVertex && !currentVertex.seen) { + CollMap.drawRoom(currentVertex, "green", true); + explore(currentVertex); + currentVertex.markAsSeen(); + CollMap.drawRoom(currentVertex, "purple", true); + + // Handle movement during explore + if (!currentVertex.coordsInRoom(me.x, me.y)) { + let _newVertex = graph.vertexForRoom(getRoom(me.x, me.y)); + if (_newVertex && component.includes(_newVertex)) { + currentVertex = _newVertex; + } + } + + // Get unvisited neighbors within this component + let neighbors = graph.nearbyVertices(currentVertex) + .filter(function(v) { + return !v.seen && component.includes(v); + }); + + if (neighbors.length === 0) { + // No neighbors in component, find nearest unvisited in component + currentVertex = component + .filter(filterSeen) + .reduce(function(best, v) { + v.clearCache(); + let dist = v.pathDistance(mode); + if (dist < best.dist) { + return { vertex: v, dist: dist }; + } + return best; + }, { vertex: null, dist: Infinity }).vertex; + } else { + // Sort neighbors by branch size (smaller branches first to avoid backtracking) + neighbors.sort(function(a, b) { + let aBranchSize = getBranchSize(a, currentVertex, component); + let bBranchSize = getBranchSize(b, currentVertex, component); + + // Prioritize smaller branches (clear them first) + if (aBranchSize !== bBranchSize) { + return aBranchSize - bBranchSize; + } + + // Tie-breaker: use path distance + let distA = currentVertex.pathDistanceTo(a, mode); + let distB = currentVertex.pathDistanceTo(b, mode); + return distA - distB; + }); + + currentVertex = neighbors[0]; + } + + // Clear caches for next iteration + for (let v of graph.vertices) { + v.clearCache(); + } + } + } + + // Main loop + while (graph.vertices.some(function(v) { return !v.seen; })) { + let components = findComponents(); + + if (components.length === 0) break; + + // Sort components by score (lower = higher priority) + components.sort(function(a, b) { + return scoreComponent(a) - scoreComponent(b); + }); + + let targetComponent = components[0]; + + for (let v of targetComponent) { + CollMap.drawRoom(v, "white", true); + } + + exploreComponent(targetComponent); + } + }; + module.exports = Graph; })(module, require); From 8f82ffbc979181897d69dc3b51e440107161a3e5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:31:24 -0500 Subject: [PATCH 712/758] U[pdate Town.doChores to run pickit, sort inventory, and prep stash - Town.js: call Pickit.pickItems() after clearing inventory in town flow; when Config.SortSettings.SortInventory is enabled call Storage.Inventory.SortItems() to organize items; and before stashing ensure inventory is identified and cleared (identify + clearInventory) to avoid putting unidentified/bad items into the stash. --- d2bs/kolbot/libs/core/Town.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index a4d22d7ce..5e39a8fad 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -174,6 +174,7 @@ const Town = { Town.heal(); Town.identify(); Town.clearInventory(); + Pickit.pickItems(); Town.fillTome(sdk.items.TomeofTownPortal); Town.buyPotions(); Config.FieldID.Enabled && Town.fillTome(sdk.items.TomeofIdentify); @@ -188,6 +189,10 @@ const Town = { Town.checkQuestItems(); !!me.getItem(sdk.items.TomeofTownPortal) && Town.clearScrolls(); + if (Config.SortSettings.SortInventory) { + Storage.Inventory.SortItems(); + } + me.act !== preAct && Town.goToTown(preAct); me.cancelUIFlags(); !me.barbarian && Precast.haveCTA === -1 && Precast.doPrecast(false); @@ -2298,7 +2303,11 @@ const Town = { } // stash the bad gems first - !isInventoryClean && Town.stash(); + if (!isInventoryClean) { + Town.identify(); + Town.clearInventory(); + Town.stash(); + } // get any good gem. flawless first (by lvlreq) const goodGem = me.getItemsEx() From 4f9148020edf722bcfc6c0a784c07384ba00c6a5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 28 Feb 2026 19:33:32 -0500 Subject: [PATCH 713/758] Refactor NTIP.CheckItem and add DebugCheckItem - Simplify and clean up NTIP.CheckItem control flow (use early continues and reduce nested branches) and fix a bug where NTIP.CheckQuantityOwned was called with a null stat argument in one branch. - Add NTIP.DebugCheckItem (with JSDoc) to return detailed match results for debugging, mirror MaxQuantity and identification handling, and include try/catch logic that reports errors and removes bad pickit entries when appropriate. --- d2bs/kolbot/libs/core/NTItemParser.js | 188 ++++++++++++++++++++++---- 1 file changed, 159 insertions(+), 29 deletions(-) diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index 7d11f8bec..d05301834 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -252,36 +252,12 @@ NTIP.CheckItem = function (item, entryList, verbose) { 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.isInStorage && num === wanted.MaxQuantity) { - result = 1; - - break; - } - } - } else { - result = 1; - - break; - } - } else if (!identified && result === 0) { - result = -1; - verbose && (rval.line = stringArray[i].file + " #" + stringArray[i].line); - } - } else { + if (!type(item)) continue; + + if (typeof stat === "function") { + if (stat(item)) { if (wanted && wanted.MaxQuantity && !isNaN(wanted.MaxQuantity)) { - num = NTIP.CheckQuantityOwned(type, null); + num = NTIP.CheckQuantityOwned(type, stat); if (num < wanted.MaxQuantity) { result = 1; @@ -300,6 +276,30 @@ NTIP.CheckItem = function (item, entryList, verbose) { break; } + } else if (!identified && result === 0) { + result = -1; + verbose && (rval.line = stringArray[i].file + " #" + stringArray[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.isInStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; } } } else if (typeof stat === "function") { @@ -369,6 +369,136 @@ NTIP.CheckItem = function (item, entryList, verbose) { return result; }; +/** + * @param {ItemUnit} item + * @param {[(item) => Boolean, (item) => Boolean, (item) => Boolean]} entryList + * @returns {{line: string, result: number}[]} + */ +NTIP.DebugCheckItem = function (item, entryList, verbose) { + let i, num; + /** @type {{ line: string, result: number }[]} */ + let results = []; + let result = 0; + + const list = entryList + ? entryList + : NTIP_CheckList; + const identified = item.getFlag(sdk.items.flags.Identified); + + for (i = 0; i < list.length; i++) { + try { + // Get the values in separated variables (its faster) + const [type, stat, wanted] = list[i]; + const rval = {}; + + if (typeof type === "function") { + if (!type(item)) continue; + + 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.isInStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; + } + } else if (!identified && result === 0) { + result = -1; + verbose && (rval.line = stringArray[i].file + " #" + stringArray[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.isInStorage && 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.isInStorage && num === wanted.MaxQuantity) { + result = 1; + + break; + } + } + } else { + result = 1; + + break; + } + } else if (!identified && result === 0) { + result = -1; + verbose && (rval.line = stringArray[i].file + " #" + stringArray[i].line); + } + } + + rval.result = result; + if (result === 1) { + rval.line = stringArray[i].file + " #" + stringArray[i].line; + } + + if (result !== 0) { + results.push(rval); + } + } catch (pickError) { + if ((e instanceof ScriptError)) { + throw e; + } + showConsole(); + + if (!entryList) { + Misc.errorReport("ÿc1Pickit error! Line # ÿc2" + stringArray[i].line + " ÿc1Entry: ÿc0" + stringArray[i].string + " (" + stringArray[i].file + ") Error message: " + pickError.message + " Trigger item: " + item.fname.split("\n").reverse().join(" ")); + + NTIP_CheckList.splice(i, 1); // Remove the element from the list + } else { + Misc.errorReport("ÿc1Pickit error in runeword config!"); + } + + result = 0; + } + } + + return results; +}; + /** @param {string} ch */ NTIP.IsSyntaxInt = function (ch) { return ( From c92843b67f7e3bd779be4fdcfa24dee3224232f8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 2 Mar 2026 01:43:16 -0500 Subject: [PATCH 714/758] Update D2BotCharRefresher to log accs/chars refreshed --- d2bs/kolbot/D2BotCharRefresher.dbj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/D2BotCharRefresher.dbj b/d2bs/kolbot/D2BotCharRefresher.dbj index 89aaab749..f5d758731 100644 --- a/d2bs/kolbot/D2BotCharRefresher.dbj +++ b/d2bs/kolbot/D2BotCharRefresher.dbj @@ -155,8 +155,11 @@ const locationAction = (function () { CharRefresher.save(md5(currAcc[2].toLowerCase() + currAcc[0].toLowerCase()), currAcc[1]); if (ControlAction.loginAccount({ account: currAcc[0], password: currAcc[1], realm: currAcc[2] })) { + D2Bot.printToConsole("Refreshing account: " + currAcc[0], sdk.colors.D2Bot.Green); FileTools.writeText("logs/CharRefresher.json", JSON.stringify(obj)); accounts.shift(); // remove current account from the list + } else { + D2Bot.printToConsole("Failed to login account: " + currAcc[0], sdk.colors.D2Bot.Red); } } ); @@ -205,6 +208,7 @@ const locationAction = (function () { obj.currChar = currChar; console.log("ÿc4CharRefresherÿc2: Login character: " + currChar); + D2Bot.printToConsole("Refreshing character: " + currChar, sdk.colors.D2Bot.Gray); FileTools.writeText("logs/CharRefresher.json", JSON.stringify(obj)); ControlAction.timeoutDelay( From a865f8e00f14001d2a880a2dfd5811b41598edd5 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 2 Mar 2026 01:44:52 -0500 Subject: [PATCH 715/758] Add RaiseArmy script for necro summoners to use Nith's temple to raise army --- d2bs/kolbot/libs/config/_BaseConfigFile.js | 1 + d2bs/kolbot/libs/scripts/RaiseArmy.js | 32 ++++++++++++++++++++++ d2bs/kolbot/sdk/types/kolbot-scripts.d.ts | 1 + 3 files changed, 34 insertions(+) create mode 100644 d2bs/kolbot/libs/scripts/RaiseArmy.js diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index 95f6f4393..f2d1f68a4 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -21,6 +21,7 @@ Config.BoBarbHelper.Wp = 35; // 35 = Catacombs level 2 Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + Scripts.RaiseArmy = false; // Go through pindle portal and raise an army of skeletons, then return to town. Only works if necromancer is configured to raise skeletons and/or revives. // ## Team MF system Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. diff --git a/d2bs/kolbot/libs/scripts/RaiseArmy.js b/d2bs/kolbot/libs/scripts/RaiseArmy.js new file mode 100644 index 000000000..774e64a33 --- /dev/null +++ b/d2bs/kolbot/libs/scripts/RaiseArmy.js @@ -0,0 +1,32 @@ +/** +* @filename RaiseArmy.js +* @author theBGuy +* @desc go through pindle portal and raise an army of skeletons, then return to town +* +*/ + +const RaiseArmy = new Runnable( + function RaiseArmy () { + if (!me.necromancer || (Config.Skeletons + Config.SkeletonMages + Config.Revives === 0)) { + console.warn("This script is only meant to be used with a necromancer configured to raise an army of skeletons and/or revives."); + return true; + } + + Town.goToTown(5); + + if (!Pather.journeyTo(sdk.areas.NihlathaksTemple)) { + throw new Error("Failed to use portal."); + } + Precast.doPrecast(true); + Pather.moveTo(10053, 13278); + + ClassAttack[sdk.player.class.Necromancer].raiseArmy(); + Town.goToTown(); + + return true; + }, + { + startArea: sdk.areas.Harrogath, + bossid: getLocaleString(sdk.locale.monsters.Pindleskin), + } +); diff --git a/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts b/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts index d559dc067..665c3bf76 100644 --- a/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts +++ b/d2bs/kolbot/sdk/types/kolbot-scripts.d.ts @@ -62,6 +62,7 @@ export type KolbotScript = | "Questing" | "Radament" | "Rakanishu" + | "RaiseArmy" | "Rushee" | "Rusher" | "SealLeecher" From f5e372fedbd452812d734a15e2c3c7a20c6cbd87 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 7 Mar 2026 01:07:11 -0500 Subject: [PATCH 716/758] Add gem locale string ids + Add extend GemUnit mock - Add missing gem item IDs to sdk.js and the TypeScript SDK definitions - In Town.js: introduce choresActive flag, set it around chores execution, and improve UI handling for stash (only cancel UI flags when stash UI isn't already open). - Reformat ignoredItemTypes for readability and add a comprehensive gemIdToName mapping plus a GemUnit wrapper that exposes name/fname, quality/ilvl, and delegates getStat/getStatEx to Unit. - Also change LocaleStringID/LocaleStringName to const for immutability. --- .../libs/core/GameData/LocaleStringID.js | 4 +- d2bs/kolbot/libs/core/Town.js | 80 +++++++++++++++++-- d2bs/kolbot/libs/modules/sdk.js | 30 +++++++ d2bs/kolbot/sdk/types/sdk.d.ts | 32 +++++++- 4 files changed, 136 insertions(+), 10 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/LocaleStringID.js b/d2bs/kolbot/libs/core/GameData/LocaleStringID.js index 96332bc3e..1ce775982 100644 --- a/d2bs/kolbot/libs/core/GameData/LocaleStringID.js +++ b/d2bs/kolbot/libs/core/GameData/LocaleStringID.js @@ -4,7 +4,7 @@ * @desc locale string indexes from NameStr ids */ (function (module) { - let LocaleStringID = { + const LocaleStringID = { "WarrivAct1IntroGossip1": 0, "WarrivAct1IntroPalGossip1": 1, "WarrivGossip1": 2, @@ -7793,7 +7793,7 @@ "MercX167": 22817 }; - let LocaleStringName = {}; + const LocaleStringName = {}; for (let k in LocaleStringID) { LocaleStringName[LocaleStringID[k]] = k; diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 5e39a8fad..3c84e3d88 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -11,6 +11,7 @@ const Town = { lastChores: 0, /** @type {Set} */ dontStashGids: new Set(), + choresActive: false, act: { 1: { @@ -123,10 +124,17 @@ const Town = { ignoredItemTypes: [ // Items that won't be stashed - 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 ], /** @@ -157,6 +165,7 @@ const Town = { } try { + Town.choresActive = true; Pather.allowBroadcast = false; if (Config.FastPick && new RegExp(/[default.dbj|main.js]/gi).test(getScript(true).name)) { // shopping causes this to bug out sometimes so remove it for duration of chores @@ -206,6 +215,7 @@ const Town = { addEventListener("itemaction", Pickit.itemEvent); } + Town.choresActive = false; Pather.allowBroadcast = true; Town.lastChores = getTickCount(); } @@ -1473,7 +1483,9 @@ const Town = { stash: function (stashGold = true) { if (!me.needStash()) return true; - me.cancelUIFlags(); + if (!getUIFlag(sdk.uiflags.Stash)) { + me.cancelUIFlags(); + } /** @type {ItemUnit[]} */ let items = (Storage.Inventory.Compare(Config.Inventory) || []) @@ -1511,7 +1523,8 @@ const Town = { // Stash gold if (stashGold) { if (me.getStat(sdk.stats.Gold) >= Config.StashGold - && me.getStat(sdk.stats.GoldBank) < 25e5 && Town.openStash()) { + && me.getStat(sdk.stats.GoldBank) < 25e5 && Town.openStash() + ) { gold(me.getStat(sdk.stats.Gold), 3); delay(1000); // allow UI to initialize me.cancel(); @@ -2227,9 +2240,42 @@ const Town = { ); } + const gemIdToName = {}; + gemIdToName[sdk.items.gems.Chipped.Amethyst] = getLocaleString(sdk.locale.items.ChippedAmethyst); + gemIdToName[sdk.items.gems.Chipped.Topaz] = getLocaleString(sdk.locale.items.ChippedTopaz); + gemIdToName[sdk.items.gems.Chipped.Sapphire] = getLocaleString(sdk.locale.items.ChippedSapphire); + gemIdToName[sdk.items.gems.Chipped.Emerald] = getLocaleString(sdk.locale.items.ChippedEmerald); + gemIdToName[sdk.items.gems.Chipped.Ruby] = getLocaleString(sdk.locale.items.ChippedRuby); + gemIdToName[sdk.items.gems.Chipped.Diamond] = getLocaleString(sdk.locale.items.ChippedDiamond); + gemIdToName[sdk.items.gems.Flawed.Amethyst] = getLocaleString(sdk.locale.items.FlawedAmethyst); + gemIdToName[sdk.items.gems.Flawed.Topaz] = getLocaleString(sdk.locale.items.FlawedTopaz); + gemIdToName[sdk.items.gems.Flawed.Sapphire] = getLocaleString(sdk.locale.items.FlawedSapphire); + gemIdToName[sdk.items.gems.Flawed.Emerald] = getLocaleString(sdk.locale.items.FlawedEmerald); + gemIdToName[sdk.items.gems.Flawed.Ruby] = getLocaleString(sdk.locale.items.FlawedRuby); + gemIdToName[sdk.items.gems.Flawed.Diamond] = getLocaleString(sdk.locale.items.FlawedDiamond); + gemIdToName[sdk.items.gems.Normal.Amethyst] = getLocaleString(sdk.locale.items.Amethyst); + gemIdToName[sdk.items.gems.Normal.Topaz] = getLocaleString(sdk.locale.items.Topaz); + gemIdToName[sdk.items.gems.Normal.Sapphire] = getLocaleString(sdk.locale.items.Sapphire); + gemIdToName[sdk.items.gems.Normal.Emerald] = getLocaleString(sdk.locale.items.Emerald); + gemIdToName[sdk.items.gems.Normal.Ruby] = getLocaleString(sdk.locale.items.Ruby); + gemIdToName[sdk.items.gems.Normal.Diamond] = getLocaleString(sdk.locale.items.Diamond); + gemIdToName[sdk.items.gems.Flawless.Amethyst] = getLocaleString(sdk.locale.items.FlawlessAmethyst); + gemIdToName[sdk.items.gems.Flawless.Topaz] = getLocaleString(sdk.locale.items.FlawlessTopaz); + gemIdToName[sdk.items.gems.Flawless.Sapphire] = getLocaleString(sdk.locale.items.FlawlessSapphire); + gemIdToName[sdk.items.gems.Flawless.Emerald] = getLocaleString(sdk.locale.items.FlawlessEmerald); + gemIdToName[sdk.items.gems.Flawless.Ruby] = getLocaleString(sdk.locale.items.FlawlessRuby); + gemIdToName[sdk.items.gems.Flawless.Diamond] = getLocaleString(sdk.locale.items.FlawlessDiamond); + gemIdToName[sdk.items.gems.Perfect.Amethyst] = getLocaleString(sdk.locale.items.PerfectAmethyst); + gemIdToName[sdk.items.gems.Perfect.Topaz] = getLocaleString(sdk.locale.items.PerfectTopaz); + gemIdToName[sdk.items.gems.Perfect.Sapphire] = getLocaleString(sdk.locale.items.PerfectSapphire); + gemIdToName[sdk.items.gems.Perfect.Emerald] = getLocaleString(sdk.locale.items.PerfectEmerald); + gemIdToName[sdk.items.gems.Perfect.Ruby] = getLocaleString(sdk.locale.items.PerfectRuby); + gemIdToName[sdk.items.gems.Perfect.Diamond] = getLocaleString(sdk.locale.items.PerfectDiamond); + /** * @class - * @param {ItemUnit} gem + * @param {ItemUnit} gem + * @implements {Partial} */ function GemUnit(gem) { this.itemType = gem.itemType; @@ -2237,11 +2283,31 @@ const Town = { this.type = gem.type; this.mode = gem.mode; this.gid = gem.gid; + this.quality = gem.quality; + this.ilvl = gem.ilvl; } // eslint-disable-next-line no-unused-vars GemUnit.prototype.getFlag = function (flag) { return true; }; + + GemUnit.prototype.getStat = Unit.prototype.getStat; + GemUnit.prototype.getStatEx = Unit.prototype.getStatEx; + + Object.defineProperties(GemUnit.prototype, { + name: { + /** @this {GemUnit} */ + get: function () { + return gemIdToName[this.classid] || "Unknown Gem"; + }, + }, + fname: { + /** @this {GemUnit} */ + get: function () { + return gemIdToName[this.classid] || "Unknown Gem"; + }, + }, + }); /** * @param {ItemUnit} unit diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 816146471..497dbf427 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -3923,6 +3923,36 @@ HoradricScroll: 2232, MephistosSoulstone: 2233, Ear: 2235, + ChippedAmethyst: 2236, + FlawedAmethyst: 2237, + Amethyst: 2238, + FlawlessAmethyst: 2239, + PerfectAmethyst: 2240, + ChippedTopaz: 2241, + FlawedTopaz: 2242, + Topaz: 2243, + FlawlessTopaz: 2244, + PerfectTopaz: 2245, + ChippedSapphire: 2246, + FlawedSapphire: 2247, + Sapphire: 2248, + FlawlessSapphire: 2249, + PerfectSapphire: 2250, + ChippedEmerald: 2251, + FlawedEmerald: 2252, + FlawlessEmerald: 2253, + Emerald: 2254, + PerfectEmerald: 2255, + ChippedRuby: 2256, + FlawedRuby: 2257, + Ruby: 2258, + FlawlessRuby: 2259, + PerfectRuby: 2260, + ChippedDiamond: 2261, + FlawedDiamond: 2262, + Diamond: 2263, + FlawlessDiamond: 2264, + PerfectDiamond: 2265, AmuletoftheViper: 2697, StaffofKings: 2698, HoradricStaff: 2699, diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index 80200bbdc..e2ff9c01d 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4022,6 +4022,36 @@ declare global { HoradricScroll: 2232; MephistosSoulstone: 2233; Ear: 2235; + ChippedAmethyst: 2236; + FlawedAmethyst: 2237; + Amethyst: 2238; + FlawlessAmethyst: 2239; + PerfectAmethyst: 2240; + ChippedTopaz: 2241; + FlawedTopaz: 2242; + Topaz: 2243; + FlawlessTopaz: 2244; + PerfectTopaz: 2245; + ChippedSapphire: 2246; + FlawedSapphire: 2247; + Sapphire: 2248; + FlawlessSapphire: 2249; + PerfectSapphire: 2250; + ChippedEmerald: 2251; + FlawedEmerald: 2252; + FlawlessEmerald: 2253; + Emerald: 2254; + PerfectEmerald: 2255; + ChippedRuby: 2256; + FlawedRuby: 2257; + Ruby: 2258; + FlawlessRuby: 2259; + PerfectRuby: 2260; + ChippedDiamond: 2261; + FlawedDiamond: 2262; + Diamond: 2263; + FlawlessDiamond: 2264; + PerfectDiamond: 2265; AmuletoftheViper: 2697; StaffofKings: 2698; HoradricStaff: 2699; @@ -4619,7 +4649,7 @@ declare global { InvalidPassword: 5207; AccountDoesNotExist: 5208; AccountIsCorrupted: 5209; - RejectedByServer: 5215, + RejectedByServer: 5215; AccountMustBeAtLeast: 5217; AccountCantBeMoreThan: 5218; PasswordMustBeAtLeast: 5219; From d41e748ec0825f34ebe24b2d563b232b26b340f2 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 7 Mar 2026 01:08:11 -0500 Subject: [PATCH 717/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index f69a3bff4..6eb677b24 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit f69a3bff4816e5e20b21aaf28e993bb9ea0db2e0 +Subproject commit 6eb677b243615c1706c0591f6a4d2ed31dd37229 From d8369bd9a93e8570a61056a8e08698613ee8b98f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 7 Mar 2026 01:46:35 -0500 Subject: [PATCH 718/758] Add NTIP codes, item IDs, and class->locale map - Introduce NTIPAliasCodes mapping and many missing item ID constants across the SDK and TypeScript declarations. - Export a ClassIdToLocaleString map from LocaleStringID.js (and fix the "hrb" locale id), and update Town.js to use that map for gem names instead of a large inline lookup. - Also update NTIP.d.ts with NTIPAliasCodes and minor signature/formatting tweaks. These changes centralize item/class->locale mappings, remove duplicated gem name data, and expand item/potion/skull identifiers for better typings and lookups. --- .../libs/core/GameData/LocaleStringID.js | 17 +- d2bs/kolbot/libs/core/GameData/NTItemAlias.js | 662 ++++++++++++++++++ d2bs/kolbot/libs/core/Town.js | 36 +- d2bs/kolbot/libs/modules/sdk.js | 312 +++++++++ d2bs/kolbot/sdk/types/NTIP.d.ts | 14 +- d2bs/kolbot/sdk/types/sdk.d.ts | 312 +++++++++ 6 files changed, 1315 insertions(+), 38 deletions(-) diff --git a/d2bs/kolbot/libs/core/GameData/LocaleStringID.js b/d2bs/kolbot/libs/core/GameData/LocaleStringID.js index 1ce775982..c296b0818 100644 --- a/d2bs/kolbot/libs/core/GameData/LocaleStringID.js +++ b/d2bs/kolbot/libs/core/GameData/LocaleStringID.js @@ -2273,7 +2273,7 @@ "mp3": 2273, "mp4": 2274, "mp5": 2275, - "hrb": 20434, + "hrb": 2276, "skc": 2277, "skf": 2278, "sku": 2279, @@ -7799,8 +7799,21 @@ LocaleStringName[LocaleStringID[k]] = k; } + const ClassIdToLocaleString = {}; + + for (let k in LocaleStringID) { + if (k.length <= 3 && Object.prototype.hasOwnProperty.call(NTIPAliasClassID, k)) { + ClassIdToLocaleString[NTIPAliasClassID[k]] = getLocaleString(LocaleStringID[k]); + } + } + module.exports = { LocaleStringName: LocaleStringName, - LocaleStringID: LocaleStringID + LocaleStringID: LocaleStringID, + /** + * Maps class ID to locale string. Only includes items right now + * @type {Record} + */ + ClassIdToLocaleString: ClassIdToLocaleString }; })(module); diff --git a/d2bs/kolbot/libs/core/GameData/NTItemAlias.js b/d2bs/kolbot/libs/core/GameData/NTItemAlias.js index 86353cb38..66bd0b979 100644 --- a/d2bs/kolbot/libs/core/GameData/NTItemAlias.js +++ b/d2bs/kolbot/libs/core/GameData/NTItemAlias.js @@ -773,6 +773,668 @@ NTIPAliasClassID["bet"] = 656; NTIPAliasClassID["burningessenceofterror"] = 656; NTIPAliasClassID["fed"] = 657; NTIPAliasClassID["festeringessenceofdestruction"] = 657; NTIPAliasClassID["std"] = 658; NTIPAliasClassID["standardofheroes"] = 658; +const NTIPAliasCodes = { + hax: 0, + axe: 1, + "2ax": 2, + mpi: 3, + wax: 4, + lax: 5, + bax: 6, + btx: 7, + gax: 8, + gix: 9, + wnd: 10, + ywn: 11, + bwn: 12, + gwn: 13, + clb: 14, + scp: 15, + gsc: 16, + wsp: 17, + spc: 18, + mac: 19, + mst: 20, + fla: 21, + whm: 22, + mau: 23, + gma: 24, + ssd: 25, + scm: 26, + sbr: 27, + flc: 28, + crs: 29, + bsd: 30, + lsd: 31, + wsd: 32, + "2hs": 33, + clm: 34, + gis: 35, + bsw: 36, + flb: 37, + gsd: 38, + dgr: 39, + dir: 40, + kri: 41, + bld: 42, + tkf: 43, + tax: 44, + bkf: 45, + bal: 46, + jav: 47, + pil: 48, + ssp: 49, + glv: 50, + tsp: 51, + spr: 52, + tri: 53, + brn: 54, + spt: 55, + pik: 56, + bar: 57, + vou: 58, + scy: 59, + pax: 60, + hal: 61, + wsc: 62, + sst: 63, + lst: 64, + cst: 65, + bst: 66, + wst: 67, + sbw: 68, + hbw: 69, + lbw: 70, + cbw: 71, + sbb: 72, + lbb: 73, + swb: 74, + lwb: 75, + lxb: 76, + mxb: 77, + hxb: 78, + rxb: 79, + gps: 80, + ops: 81, + gpm: 82, + opm: 83, + gpl: 84, + opl: 85, + d33: 86, + g33: 87, + leg: 88, + hdm: 89, + hfh: 90, + hst: 91, + msf: 92, + "9ha": 93, + "9ax": 94, + "92a": 95, + "9mp": 96, + "9wa": 97, + "9la": 98, + "9ba": 99, + "9bt": 100, + "9ga": 101, + "9gi": 102, + "9wn": 103, + "9yw": 104, + "9bw": 105, + "9gw": 106, + "9cl": 107, + "9sc": 108, + "9qs": 109, + "9ws": 110, + "9sp": 111, + "9ma": 112, + "9mt": 113, + "9fl": 114, + "9wh": 115, + "9m9": 116, + "9gm": 117, + "9ss": 118, + "9sm": 119, + "9sb": 120, + "9fc": 121, + "9cr": 122, + "9bs": 123, + "9ls": 124, + "9wd": 125, + "92h": 126, + "9cm": 127, + "9gs": 128, + "9b9": 129, + "9fb": 130, + "9gd": 131, + "9dg": 132, + "9di": 133, + "9kr": 134, + "9bl": 135, + "9tk": 136, + "9ta": 137, + "9bk": 138, + "9b8": 139, + "9ja": 140, + "9pi": 141, + "9s9": 142, + "9gl": 143, + "9ts": 144, + "9sr": 145, + "9tr": 146, + "9br": 147, + "9st": 148, + "9p9": 149, + "9b7": 150, + "9vo": 151, + "9s8": 152, + "9pa": 153, + "9h9": 154, + "9wc": 155, + "8ss": 156, + "8ls": 157, + "8cs": 158, + "8bs": 159, + "8ws": 160, + "8sb": 161, + "8hb": 162, + "8lb": 163, + "8cb": 164, + "8s8": 165, + "8l8": 166, + "8sw": 167, + "8lw": 168, + "8lx": 169, + "8mx": 170, + "8hx": 171, + "8rx": 172, + qf1: 173, + qf2: 174, + ktr: 175, + wrb: 176, + axf: 177, + ces: 178, + clw: 179, + btl: 180, + skr: 181, + "9ar": 182, + "9wb": 183, + "9xf": 184, + "9cs": 185, + "9lw": 186, + "9tw": 187, + "9qr": 188, + "7ar": 189, + "7wb": 190, + "7xf": 191, + "7cs": 192, + "7lw": 193, + "7tw": 194, + "7qr": 195, + "7ha": 196, + "7ax": 197, + "72a": 198, + "7mp": 199, + "7wa": 200, + "7la": 201, + "7ba": 202, + "7bt": 203, + "7ga": 204, + "7gi": 205, + "7wn": 206, + "7yw": 207, + "7bw": 208, + "7gw": 209, + "7cl": 210, + "7sc": 211, + "7qs": 212, + "7ws": 213, + "7sp": 214, + "7ma": 215, + "7mt": 216, + "7fl": 217, + "7wh": 218, + "7m7": 219, + "7gm": 220, + "7ss": 221, + "7sm": 222, + "7sb": 223, + "7fc": 224, + "7cr": 225, + "7bs": 226, + "7ls": 227, + "7wd": 228, + "72h": 229, + "7cm": 230, + "7gs": 231, + "7b7": 232, + "7fb": 233, + "7gd": 234, + "7dg": 235, + "7di": 236, + "7kr": 237, + "7bl": 238, + "7tk": 239, + "7ta": 240, + "7bk": 241, + "7b8": 242, + "7ja": 243, + "7pi": 244, + "7s7": 245, + "7gl": 246, + "7ts": 247, + "7sr": 248, + "7tr": 249, + "7br": 250, + "7st": 251, + "7p7": 252, + "7o7": 253, + "7vo": 254, + "7s8": 255, + "7pa": 256, + "7h7": 257, + "7wc": 258, + "6ss": 259, + "6ls": 260, + "6cs": 261, + "6bs": 262, + "6ws": 263, + "6sb": 264, + "6hb": 265, + "6lb": 266, + "6cb": 267, + "6s7": 268, + "6l7": 269, + "6sw": 270, + "6lw": 271, + "6lx": 272, + "6mx": 273, + "6hx": 274, + "6rx": 275, + ob1: 276, + ob2: 277, + ob3: 278, + ob4: 279, + ob5: 280, + am1: 281, + am2: 282, + am3: 283, + am4: 284, + am5: 285, + ob6: 286, + ob7: 287, + ob8: 288, + ob9: 289, + oba: 290, + am6: 291, + am7: 292, + am8: 293, + am9: 294, + ama: 295, + obb: 296, + obc: 297, + obd: 298, + obe: 299, + obf: 300, + amb: 301, + amc: 302, + amd: 303, + ame: 304, + amf: 305, + cap: 306, + skp: 307, + hlm: 308, + fhl: 309, + ghm: 310, + crn: 311, + msk: 312, + qui: 313, + lea: 314, + hla: 315, + stu: 316, + rng: 317, + scl: 318, + chn: 319, + brs: 320, + spl: 321, + plt: 322, + fld: 323, + gth: 324, + ful: 325, + aar: 326, + ltp: 327, + buc: 328, + sml: 329, + lrg: 330, + kit: 331, + tow: 332, + gts: 333, + lgl: 334, + vgl: 335, + mgl: 336, + tgl: 337, + hgl: 338, + lbt: 339, + vbt: 340, + mbt: 341, + tbt: 342, + hbt: 343, + lbl: 344, + vbl: 345, + mbl: 346, + tbl: 347, + hbl: 348, + bhm: 349, + bsh: 350, + spk: 351, + xap: 352, + xkp: 353, + xlm: 354, + xhl: 355, + xhm: 356, + xrn: 357, + xsk: 358, + xui: 359, + xea: 360, + xla: 361, + xtu: 362, + xng: 363, + xcl: 364, + xhn: 365, + xrs: 366, + xpl: 367, + xlt: 368, + xld: 369, + xth: 370, + xul: 371, + xar: 372, + xtp: 373, + xuc: 374, + xml: 375, + xrg: 376, + xit: 377, + xow: 378, + xts: 379, + xlg: 380, + xvg: 381, + xmg: 382, + xtg: 383, + xhg: 384, + xlb: 385, + xvb: 386, + xmb: 387, + xtb: 388, + xhb: 389, + zlb: 390, + zvb: 391, + zmb: 392, + ztb: 393, + zhb: 394, + xh9: 395, + xsh: 396, + xpk: 397, + dr1: 398, + dr2: 399, + dr3: 400, + dr4: 401, + dr5: 402, + ba1: 403, + ba2: 404, + ba3: 405, + ba4: 406, + ba5: 407, + pa1: 408, + pa2: 409, + pa3: 410, + pa4: 411, + pa5: 412, + ne1: 413, + ne2: 414, + ne3: 415, + ne4: 416, + ne5: 417, + ci0: 418, + ci1: 419, + ci2: 420, + ci3: 421, + uap: 422, + ukp: 423, + ulm: 424, + uhl: 425, + uhm: 426, + urn: 427, + usk: 428, + uui: 429, + uea: 430, + ula: 431, + utu: 432, + ung: 433, + ucl: 434, + uhn: 435, + urs: 436, + upl: 437, + ult: 438, + uld: 439, + uth: 440, + uul: 441, + uar: 442, + utp: 443, + uuc: 444, + uml: 445, + urg: 446, + uit: 447, + uow: 448, + uts: 449, + ulg: 450, + uvg: 451, + umg: 452, + utg: 453, + uhg: 454, + ulb: 455, + uvb: 456, + umb: 457, + utb: 458, + uhb: 459, + ulc: 460, + uvc: 461, + umc: 462, + utc: 463, + uhc: 464, + uh9: 465, + ush: 466, + upk: 467, + dr6: 468, + dr7: 469, + dr8: 470, + dr9: 471, + dra: 472, + ba6: 473, + ba7: 474, + ba8: 475, + ba9: 476, + baa: 477, + pa6: 478, + pa7: 479, + pa8: 480, + pa9: 481, + paa: 482, + ne6: 483, + ne7: 484, + ne8: 485, + ne9: 486, + nea: 487, + drb: 488, + drc: 489, + drd: 490, + dre: 491, + drf: 492, + bab: 493, + bac: 494, + bad: 495, + bae: 496, + baf: 497, + pab: 498, + pac: 499, + pad: 500, + pae: 501, + paf: 502, + neb: 503, + neg: 504, + ned: 505, + nee: 506, + nef: 507, + elx: 508, + hpo: 509, + mpo: 510, + hpf: 511, + mpf: 512, + vps: 513, + yps: 514, + rvs: 515, + rvl: 516, + wms: 517, + tbk: 518, + ibk: 519, + amu: 520, + vip: 521, + rin: 522, + gld: 523, + bks: 524, + bkd: 525, + aqv: 526, + tch: 527, + cqv: 528, + tsc: 529, + isc: 530, + hrt: 531, + brz: 532, + jaw: 533, + eyz: 534, + hrn: 535, + tal: 536, + flg: 537, + fng: 538, + qll: 539, + sol: 540, + scz: 541, + spe: 542, + key: 543, + luv: 544, + xyz: 545, + j34: 546, + g34: 547, + bbb: 548, + box: 549, + tr1: 550, + mss: 551, + ass: 552, + qey: 553, + qhr: 554, + qbr: 555, + ear: 556, + gcv: 557, + gfv: 558, + gsv: 559, + gzv: 560, + gpv: 561, + gcy: 562, + gfy: 563, + gsy: 564, + gly: 565, + gpy: 566, + gcb: 567, + gfb: 568, + gsb: 569, + glb: 570, + gpb: 571, + gcg: 572, + gfg: 573, + gsg: 574, + glg: 575, + gpg: 576, + gcr: 577, + gfr: 578, + gsr: 579, + glr: 580, + gpr: 581, + gcw: 582, + gfw: 583, + gsw: 584, + glw: 585, + gpw: 586, + hp1: 587, + hp2: 588, + hp3: 589, + hp4: 590, + hp5: 591, + mp1: 592, + mp2: 593, + mp3: 594, + mp4: 595, + mp5: 596, + skc: 597, + skf: 598, + sku: 599, + skl: 600, + skz: 601, + hrb: 602, + cm1: 603, + cm2: 604, + cm3: 605, + rps: 606, + rpl: 607, + bps: 608, + bpl: 609, + r01: 610, + r02: 611, + r03: 612, + r04: 613, + r05: 614, + r06: 615, + r07: 616, + r08: 617, + r09: 618, + r10: 619, + r11: 620, + r12: 621, + r13: 622, + r14: 623, + r15: 624, + r16: 625, + r17: 626, + r18: 627, + r19: 628, + r20: 629, + r21: 630, + r22: 631, + r23: 632, + r24: 633, + r25: 634, + r26: 635, + r27: 636, + r28: 637, + r29: 638, + r30: 639, + r31: 640, + r32: 641, + r33: 642, + jew: 643, + ice: 644, + "0sc": 645, + tr2: 646, + pk1: 647, + pk2: 648, + pk3: 649, + dhn: 650, + bey: 651, + mbr: 652, + toa: 653, + tes: 654, + ceh: 655, + bet: 656, + fed: 657, + std: 658, +}; + /** @global */ const NTIPAliasClass = {}; NTIPAliasClass["normal"] = 0; diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 3c84e3d88..3f924fcf5 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -2240,37 +2240,7 @@ const Town = { ); } - const gemIdToName = {}; - gemIdToName[sdk.items.gems.Chipped.Amethyst] = getLocaleString(sdk.locale.items.ChippedAmethyst); - gemIdToName[sdk.items.gems.Chipped.Topaz] = getLocaleString(sdk.locale.items.ChippedTopaz); - gemIdToName[sdk.items.gems.Chipped.Sapphire] = getLocaleString(sdk.locale.items.ChippedSapphire); - gemIdToName[sdk.items.gems.Chipped.Emerald] = getLocaleString(sdk.locale.items.ChippedEmerald); - gemIdToName[sdk.items.gems.Chipped.Ruby] = getLocaleString(sdk.locale.items.ChippedRuby); - gemIdToName[sdk.items.gems.Chipped.Diamond] = getLocaleString(sdk.locale.items.ChippedDiamond); - gemIdToName[sdk.items.gems.Flawed.Amethyst] = getLocaleString(sdk.locale.items.FlawedAmethyst); - gemIdToName[sdk.items.gems.Flawed.Topaz] = getLocaleString(sdk.locale.items.FlawedTopaz); - gemIdToName[sdk.items.gems.Flawed.Sapphire] = getLocaleString(sdk.locale.items.FlawedSapphire); - gemIdToName[sdk.items.gems.Flawed.Emerald] = getLocaleString(sdk.locale.items.FlawedEmerald); - gemIdToName[sdk.items.gems.Flawed.Ruby] = getLocaleString(sdk.locale.items.FlawedRuby); - gemIdToName[sdk.items.gems.Flawed.Diamond] = getLocaleString(sdk.locale.items.FlawedDiamond); - gemIdToName[sdk.items.gems.Normal.Amethyst] = getLocaleString(sdk.locale.items.Amethyst); - gemIdToName[sdk.items.gems.Normal.Topaz] = getLocaleString(sdk.locale.items.Topaz); - gemIdToName[sdk.items.gems.Normal.Sapphire] = getLocaleString(sdk.locale.items.Sapphire); - gemIdToName[sdk.items.gems.Normal.Emerald] = getLocaleString(sdk.locale.items.Emerald); - gemIdToName[sdk.items.gems.Normal.Ruby] = getLocaleString(sdk.locale.items.Ruby); - gemIdToName[sdk.items.gems.Normal.Diamond] = getLocaleString(sdk.locale.items.Diamond); - gemIdToName[sdk.items.gems.Flawless.Amethyst] = getLocaleString(sdk.locale.items.FlawlessAmethyst); - gemIdToName[sdk.items.gems.Flawless.Topaz] = getLocaleString(sdk.locale.items.FlawlessTopaz); - gemIdToName[sdk.items.gems.Flawless.Sapphire] = getLocaleString(sdk.locale.items.FlawlessSapphire); - gemIdToName[sdk.items.gems.Flawless.Emerald] = getLocaleString(sdk.locale.items.FlawlessEmerald); - gemIdToName[sdk.items.gems.Flawless.Ruby] = getLocaleString(sdk.locale.items.FlawlessRuby); - gemIdToName[sdk.items.gems.Flawless.Diamond] = getLocaleString(sdk.locale.items.FlawlessDiamond); - gemIdToName[sdk.items.gems.Perfect.Amethyst] = getLocaleString(sdk.locale.items.PerfectAmethyst); - gemIdToName[sdk.items.gems.Perfect.Topaz] = getLocaleString(sdk.locale.items.PerfectTopaz); - gemIdToName[sdk.items.gems.Perfect.Sapphire] = getLocaleString(sdk.locale.items.PerfectSapphire); - gemIdToName[sdk.items.gems.Perfect.Emerald] = getLocaleString(sdk.locale.items.PerfectEmerald); - gemIdToName[sdk.items.gems.Perfect.Ruby] = getLocaleString(sdk.locale.items.PerfectRuby); - gemIdToName[sdk.items.gems.Perfect.Diamond] = getLocaleString(sdk.locale.items.PerfectDiamond); + const { ClassIdToLocaleString } = require("./GameData/LocaleStringID"); /** * @class @@ -2298,13 +2268,13 @@ const Town = { name: { /** @this {GemUnit} */ get: function () { - return gemIdToName[this.classid] || "Unknown Gem"; + return ClassIdToLocaleString[this.classid] || "Unknown Gem"; }, }, fname: { /** @this {GemUnit} */ get: function () { - return gemIdToName[this.classid] || "Unknown Gem"; + return ClassIdToLocaleString[this.classid] || "Unknown Gem"; }, }, }); diff --git a/d2bs/kolbot/libs/modules/sdk.js b/d2bs/kolbot/libs/modules/sdk.js index 497dbf427..5e45d4b6e 100644 --- a/d2bs/kolbot/libs/modules/sdk.js +++ b/d2bs/kolbot/libs/modules/sdk.js @@ -3913,8 +3913,303 @@ KhalimsEye: 1064, KhalimsBrain: 1065, KhalimsHeart: 1066, + Cap: 1930, + SkullCap: 1931, + Helm: 1932, + FullHelm: 1933, + GreatHelm: 1934, + Crown: 1935, + Mask: 1936, + QuiltedArmor: 1937, + LeatherArmor: 1938, + HardLeatherArmor: 1939, + StuddedLeather: 1940, + RingMail: 1941, + ScaleMail: 1942, + ChainMail: 1943, + BreastPlate: 1944, + SplintMail: 1945, + PlateMail: 1946, + FieldPlate: 1947, + GothicPlate: 1948, + FullPlateMail: 1949, + AncientArmor: 1950, + LightPlate: 1951, + Buckler: 1952, + SmallShield: 1953, + LargeShield: 1954, + KiteShield: 1955, + TowerShield: 1956, + GothicShield: 1957, + LeatherGloves: 1958, + HeavyGloves: 1959, + ChainGloves: 1960, + LightGauntlets: 1961, + Gauntlets: 1962, + Boots: 1963, + HeavyBoots: 1964, + ChainBoots: 1965, + LightPlatedBoots: 1966, + Greaves: 1967, + Sash: 1968, + LightBelt: 1969, + Belt: 1970, + HeavyBelt: 1971, + PlatedBelt: 1972, + BoneHelm: 1973, + BoneShield: 1974, + SpikedShield: 1975, + HandAxe: 1976, + Axe: 1977, + DoubleAxe: 1978, + MilitaryPick: 1979, + WarAxe: 1980, + LargeAxe: 1981, + BroadAxe: 1982, + BattleAxe: 1983, + GreatAxe: 1984, + GiantAxe: 1985, + Wand: 1986, + YewWand: 1987, + BoneWand: 1988, + GrimWand: 1989, + Club: 1990, + Scepter: 1991, + GrandScepter: 1992, + WarScepter: 1993, + SpikedClub: 1994, + Mace: 1995, + MorningStar: 1996, + Flail: 1997, + WarHammer: 1998, + Maul: 1999, + GreatMaul: 2000, + ShortSword: 2001, + Scimitar: 2002, + Sabre: 2003, + Falchion: 2004, + CrystalSword: 2005, + BroadSword: 2006, + LongSword: 2007, + WarSword: 2008, + TwoHandedSword: 2009, + Claymore: 2010, + GiantSword: 2011, + BastardSword: 2012, + Flamberge: 2013, + GreatSword: 2014, + Dagger: 2015, + Dirk: 2016, + Kris: 2017, + Blade: 2018, + ThrowingKnife: 2019, + ThrowingAxe: 2020, + BalancedKnife: 2021, + BalancedAxe: 2022, + Javelin: 2023, + Pilum: 2024, + ShortSpear: 2025, + Glaive: 2026, + ThrowingSpear: 2027, + Spear: 2028, + Trident: 2029, + Brandistock: 2030, + Spetum: 2031, + Pike: 2032, + Bardiche: 2033, + Voulge: 2034, + Scythe: 2035, + Poleaxe: 2036, + Halberd: 2037, + WarScythe: 2038, + ShortStaff: 2039, + LongStaff: 2040, + GnarledStaff: 2041, + BattleStaff: 2042, + WarStaff: 2043, + ShortBow: 2044, + HuntersBow: 2045, + LongBow: 2046, + CompositeBow: 2047, + ShortBattleBow: 2048, + LongBattleBow: 2049, + ShortWarBow: 2050, + LongWarBow: 2051, + LightCrossbow: 2052, + Crossbow: 2053, + HeavyCrossbow: 2054, + RepeatingCrossbow: 2055, + BarbedShield: 2056, + GrimShield: 2057, + GrimHelm: 2058, + WarBelt: 2059, + BattleBelt: 2060, + MeshBelt: 2061, + SharkskinBelt: 2062, + DemonhideSash: 2063, + WarBoots: 2064, + BattleBoots: 2065, + MeshBoots: 2066, + SharkskinBoots: 2067, + DemonhideBoots: 2068, + WarGauntlets: 2069, + BattleGauntlets: 2070, + HeavyBracers: 2071, + SharkskinGloves: 2072, + DemonhideGloves: 2073, + AncientShield: 2074, + Pavise: 2075, + DragonShield: 2076, + Scutum: 2077, + RoundShield: 2078, + Defender: 2079, + MagePlate: 2080, + OrnatePlate: 2081, + ChaosArmor: 2082, + EmbossedPlate: 2083, + SharktoothArmor: 2084, + TemplarCoat: 2085, + RussetArmor: 2086, + Cuirass: 2087, + MeshArmor: 2088, + TigulatedMail: 2089, + LinkedMail: 2090, + TrellisedArmor: 2091, + DemonhideArmor: 2092, + SerpentskinArmor: 2093, + GhostArmor: 2094, + DeathMask: 2095, + GrandCrown: 2096, + WingedHelm: 2097, + Basinet: 2098, + Casque: 2099, + Sallet: 2100, + WarHat: 2101, + ChuKoNu: 2102, + Ballista: 2103, + SiegeCrossbow: 2104, + Arbalest: 2105, + GothicBow: 2106, + RuneBow: 2107, + LargeSiegeBow: 2108, + ShortSiegeBow: 2109, + DoubleBow: 2110, + CedarBow: 2111, + RazorBow: 2112, + EdgeBow: 2113, + RuneStaff: 2114, + GothicStaff: 2115, + CedarStaff: 2116, + Quarterstaff: 2117, + JoStaff: 2118, + GrimScythe: 2119, + BecdeCorbin: 2120, + Partizan: 2121, + BattleScythe: 2122, + Bill: 2123, + LochaberAxe: 2124, + Lance: 2125, + Yari: 2126, + WarFork: 2127, + Fuscina: 2128, + WarSpear: 2129, + Harpoon: 2130, + Spiculum: 2131, + Simbilan: 2132, + GreatPilum: 2133, + WarJavelin: 2134, + Hurlbat: 2135, + WarDart: 2136, + Francisca: 2137, + BattleDart: 2138, + Stilleto: 2139, + Cinquedeas: 2140, + Rondel: 2141, + Poignard: 2142, + ExecutionerSword: 2143, + Zweihander: 2144, + TuskSword: 2145, + DacianFalx: 2146, + Espandon: 2147, + AncientSword: 2148, + RuneSword: 2149, + BattleSword: 2150, + DimensionalBlade: 2151, + Tulwar: 2152, + Shamshir: 2153, + Cutlass: 2154, + Gladius: 2155, + MarteldeFer: 2156, + WarClub: 2157, + BattleHammer: 2158, + Knout: 2159, + JaggedStar: 2160, + FlangedMace: 2161, + BarbedClub: 2162, + DivineScepter: 2163, + HolyWaterSprinkler: 2164, + RuneScepter: 2165, + Cudgel: 2166, + GraveWand: 2167, + TombWand: 2168, + PetrifiedWand: 2169, + BurntWand: 2170, + AncientAxe: 2171, + GothicAxe: 2172, + Tabar: 2173, + BeardedAxe: 2174, + MilitaryAxe: 2175, + Naga: 2176, + Crowbill: 2177, + TwinAxe: 2178, + Cleaver: 2179, + Hatchet: 2180, + GothicSword: 2181, + StranglingGasPotion: 2182, + FulminatingPotion: 2183, + ChokingGasPotion: 2184, + ExplodingPotion: 2185, + RancidGasPotion: 2186, + OilPotion: 2187, + Gidbinn: 2188, + TheGidbinn1: 2189, + DecoyGidbinn: 2190, + WirtsLeg: 2191, + HoradricMalus1: 2192, + HoradricMalus2: 2193, + HellForgeHammer: 2194, + HoradricStaff1: 2195, + ShaftoftheHoradricStaff: 2196, + Orifice: 2197, + Elixir: 2198, + TomeofTownPortal: 2199, + ScrollofTownPortal: 2200, + TomeofIdentify: 2201, + ScrollofIdentify: 2202, + RightClicktoUse: 2203, + RightClicktoOpen: 2204, + RightClicktoRead: 2205, + InsertScrolls: 2206, + StaminaPotion: 2207, + AntidotePotion: 2208, + RejuvenationPotion: 2209, + FullRejuvenationPotion: 2210, + ThawingPotion: 2211, + Amulet: 2212, + TopoftheHoradricStaff: 2213, + Ring: 2214, + Gold: 2215, ScrollofInifuss: 2216, KeytotheCairnStones: 2217, + Arrows: 2218, + Torch: 2219, + Bolts: 2220, + Key1: 2221, + Key2: 2222, + TheBlackTowerKey: 2223, + RightClicktoPermanentlyAdd20toLifePotionofLife: 2224, + Shrine: 2225, + TeleportPad: 2226, AJadeFigurine: 2227, TheGoldenBird: 2228, LamEsensTome1: 2229, @@ -3922,6 +4217,7 @@ HoradricCube: 2231, HoradricScroll: 2232, MephistosSoulstone: 2233, + RightClicktoLearnSkillofYourChoiceBookofSkill: 2234, Ear: 2235, ChippedAmethyst: 2236, FlawedAmethyst: 2237, @@ -3953,6 +4249,22 @@ Diamond: 2263, FlawlessDiamond: 2264, PerfectDiamond: 2265, + MinorHealingPotion: 2266, + LightHealingPotion: 2267, + HealingPotion: 2268, + GreaterHealingPotion: 2269, + SuperHealingPotion: 2270, + MinorManaPotion: 2271, + LightManaPotion: 2272, + ManaPotion: 2273, + GreaterManaPotion: 2274, + SuperManaPotion: 2275, + Herb: 2276, + ChippedSkull: 2277, + FlawedSkull: 2278, + Skull: 2279, + FlawlessSkull: 2280, + PerfectSkull: 2281, AmuletoftheViper: 2697, StaffofKings: 2698, HoradricStaff: 2699, diff --git a/d2bs/kolbot/sdk/types/NTIP.d.ts b/d2bs/kolbot/sdk/types/NTIP.d.ts index 59e60ac7d..63a5a642c 100644 --- a/d2bs/kolbot/sdk/types/NTIP.d.ts +++ b/d2bs/kolbot/sdk/types/NTIP.d.ts @@ -2,16 +2,20 @@ export {}; declare global { const NTIPAliasType: Record; const NTIPAliasClassID: Record; + const NTIPAliasCodes: Record; const NTIPAliasClass: Record; const NTIPAliasQuality: Record; const NTIPAliasFlag: Record; const NTIPAliasColor: Record; const NTIPAliasStat: Record; - + namespace NTIP { function addLine(itemString: string, fileName: string): boolean; function OpenFile(filepath: string, notify: boolean): boolean; - function CheckQuantityOwned(item_type: (item: ItemUnit) => boolean, item_stats: (item: ItemUnit) => boolean): number; + function CheckQuantityOwned( + item_type: (item: ItemUnit) => boolean, + item_stats: (item: ItemUnit) => boolean, + ): number; function Clear(): void; function generateTierFunc(tierType: string): (item: ItemUnit) => number; function GetTier(item: ItemUnit): number; @@ -28,6 +32,10 @@ declare global { const _aliases: Map; const _lists: Map>; function ParseLineInt(input: string, info: any): boolean; - function CheckItem(item: ItemUnit, entryList?: [] | false, verbose?: boolean): number | { line: string, result: number }; + function CheckItem( + item: ItemUnit, + entryList?: [] | false, + verbose?: boolean, + ): number | { line: string; result: number }; } } diff --git a/d2bs/kolbot/sdk/types/sdk.d.ts b/d2bs/kolbot/sdk/types/sdk.d.ts index e2ff9c01d..056d40422 100644 --- a/d2bs/kolbot/sdk/types/sdk.d.ts +++ b/d2bs/kolbot/sdk/types/sdk.d.ts @@ -4012,8 +4012,303 @@ declare global { KhalimsEye: 1064; KhalimsBrain: 1065; KhalimsHeart: 1066; + Cap: 1930; + SkullCap: 1931; + Helm: 1932; + FullHelm: 1933; + GreatHelm: 1934; + Crown: 1935; + Mask: 1936; + QuiltedArmor: 1937; + LeatherArmor: 1938; + HardLeatherArmor: 1939; + StuddedLeather: 1940; + RingMail: 1941; + ScaleMail: 1942; + ChainMail: 1943; + BreastPlate: 1944; + SplintMail: 1945; + PlateMail: 1946; + FieldPlate: 1947; + GothicPlate: 1948; + FullPlateMail: 1949; + AncientArmor: 1950; + LightPlate: 1951; + Buckler: 1952; + SmallShield: 1953; + LargeShield: 1954; + KiteShield: 1955; + TowerShield: 1956; + GothicShield: 1957; + LeatherGloves: 1958; + HeavyGloves: 1959; + ChainGloves: 1960; + LightGauntlets: 1961; + Gauntlets: 1962; + Boots: 1963; + HeavyBoots: 1964; + ChainBoots: 1965; + LightPlatedBoots: 1966; + Greaves: 1967; + Sash: 1968; + LightBelt: 1969; + Belt: 1970; + HeavyBelt: 1971; + PlatedBelt: 1972; + BoneHelm: 1973; + BoneShield: 1974; + SpikedShield: 1975; + HandAxe: 1976; + Axe: 1977; + DoubleAxe: 1978; + MilitaryPick: 1979; + WarAxe: 1980; + LargeAxe: 1981; + BroadAxe: 1982; + BattleAxe: 1983; + GreatAxe: 1984; + GiantAxe: 1985; + Wand: 1986; + YewWand: 1987; + BoneWand: 1988; + GrimWand: 1989; + Club: 1990; + Scepter: 1991; + GrandScepter: 1992; + WarScepter: 1993; + SpikedClub: 1994; + Mace: 1995; + MorningStar: 1996; + Flail: 1997; + WarHammer: 1998; + Maul: 1999; + GreatMaul: 2000; + ShortSword: 2001; + Scimitar: 2002; + Sabre: 2003; + Falchion: 2004; + CrystalSword: 2005; + BroadSword: 2006; + LongSword: 2007; + WarSword: 2008; + TwoHandedSword: 2009; + Claymore: 2010; + GiantSword: 2011; + BastardSword: 2012; + Flamberge: 2013; + GreatSword: 2014; + Dagger: 2015; + Dirk: 2016; + Kris: 2017; + Blade: 2018; + ThrowingKnife: 2019; + ThrowingAxe: 2020; + BalancedKnife: 2021; + BalancedAxe: 2022; + Javelin: 2023; + Pilum: 2024; + ShortSpear: 2025; + Glaive: 2026; + ThrowingSpear: 2027; + Spear: 2028; + Trident: 2029; + Brandistock: 2030; + Spetum: 2031; + Pike: 2032; + Bardiche: 2033; + Voulge: 2034; + Scythe: 2035; + Poleaxe: 2036; + Halberd: 2037; + WarScythe: 2038; + ShortStaff: 2039; + LongStaff: 2040; + GnarledStaff: 2041; + BattleStaff: 2042; + WarStaff: 2043; + ShortBow: 2044; + HuntersBow: 2045; + LongBow: 2046; + CompositeBow: 2047; + ShortBattleBow: 2048; + LongBattleBow: 2049; + ShortWarBow: 2050; + LongWarBow: 2051; + LightCrossbow: 2052; + Crossbow: 2053; + HeavyCrossbow: 2054; + RepeatingCrossbow: 2055; + BarbedShield: 2056; + GrimShield: 2057; + GrimHelm: 2058; + WarBelt: 2059; + BattleBelt: 2060; + MeshBelt: 2061; + SharkskinBelt: 2062; + DemonhideSash: 2063; + WarBoots: 2064; + BattleBoots: 2065; + MeshBoots: 2066; + SharkskinBoots: 2067; + DemonhideBoots: 2068; + WarGauntlets: 2069; + BattleGauntlets: 2070; + HeavyBracers: 2071; + SharkskinGloves: 2072; + DemonhideGloves: 2073; + AncientShield: 2074; + Pavise: 2075; + DragonShield: 2076; + Scutum: 2077; + RoundShield: 2078; + Defender: 2079; + MagePlate: 2080; + OrnatePlate: 2081; + ChaosArmor: 2082; + EmbossedPlate: 2083; + SharktoothArmor: 2084; + TemplarCoat: 2085; + RussetArmor: 2086; + Cuirass: 2087; + MeshArmor: 2088; + TigulatedMail: 2089; + LinkedMail: 2090; + TrellisedArmor: 2091; + DemonhideArmor: 2092; + SerpentskinArmor: 2093; + GhostArmor: 2094; + DeathMask: 2095; + GrandCrown: 2096; + WingedHelm: 2097; + Basinet: 2098; + Casque: 2099; + Sallet: 2100; + WarHat: 2101; + ChuKoNu: 2102; + Ballista: 2103; + SiegeCrossbow: 2104; + Arbalest: 2105; + GothicBow: 2106; + RuneBow: 2107; + LargeSiegeBow: 2108; + ShortSiegeBow: 2109; + DoubleBow: 2110; + CedarBow: 2111; + RazorBow: 2112; + EdgeBow: 2113; + RuneStaff: 2114; + GothicStaff: 2115; + CedarStaff: 2116; + Quarterstaff: 2117; + JoStaff: 2118; + GrimScythe: 2119; + BecdeCorbin: 2120; + Partizan: 2121; + BattleScythe: 2122; + Bill: 2123; + LochaberAxe: 2124; + Lance: 2125; + Yari: 2126; + WarFork: 2127; + Fuscina: 2128; + WarSpear: 2129; + Harpoon: 2130; + Spiculum: 2131; + Simbilan: 2132; + GreatPilum: 2133; + WarJavelin: 2134; + Hurlbat: 2135; + WarDart: 2136; + Francisca: 2137; + BattleDart: 2138; + Stilleto: 2139; + Cinquedeas: 2140; + Rondel: 2141; + Poignard: 2142; + ExecutionerSword: 2143; + Zweihander: 2144; + TuskSword: 2145; + DacianFalx: 2146; + Espandon: 2147; + AncientSword: 2148; + RuneSword: 2149; + BattleSword: 2150; + DimensionalBlade: 2151; + Tulwar: 2152; + Shamshir: 2153; + Cutlass: 2154; + Gladius: 2155; + MarteldeFer: 2156; + WarClub: 2157; + BattleHammer: 2158; + Knout: 2159; + JaggedStar: 2160; + FlangedMace: 2161; + BarbedClub: 2162; + DivineScepter: 2163; + HolyWaterSprinkler: 2164; + RuneScepter: 2165; + Cudgel: 2166; + GraveWand: 2167; + TombWand: 2168; + PetrifiedWand: 2169; + BurntWand: 2170; + AncientAxe: 2171; + GothicAxe: 2172; + Tabar: 2173; + BeardedAxe: 2174; + MilitaryAxe: 2175; + Naga: 2176; + Crowbill: 2177; + TwinAxe: 2178; + Cleaver: 2179; + Hatchet: 2180; + GothicSword: 2181; + StranglingGasPotion: 2182; + FulminatingPotion: 2183; + ChokingGasPotion: 2184; + ExplodingPotion: 2185; + RancidGasPotion: 2186; + OilPotion: 2187; + Gidbinn: 2188; + TheGidbinn1: 2189; + DecoyGidbinn: 2190; + WirtsLeg: 2191; + HoradricMalus1: 2192; + HoradricMalus2: 2193; + HellForgeHammer: 2194; + HoradricStaff1: 2195; + ShaftoftheHoradricStaff: 2196; + Orifice: 2197; + Elixir: 2198; + TomeofTownPortal: 2199; + ScrollofTownPortal: 2200; + TomeofIdentify: 2201; + ScrollofIdentify: 2202; + RightClicktoUse: 2203; + RightClicktoOpen: 2204; + RightClicktoRead: 2205; + InsertScrolls: 2206; + StaminaPotion: 2207; + AntidotePotion: 2208; + RejuvenationPotion: 2209; + FullRejuvenationPotion: 2210; + ThawingPotion: 2211; + Amulet: 2212; + TopoftheHoradricStaff: 2213; + Ring: 2214; + Gold: 2215; ScrollofInifuss: 2216; KeytotheCairnStones: 2217; + Arrows: 2218; + Torch: 2219; + Bolts: 2220; + Key1: 2221; + Key2: 2222; + TheBlackTowerKey: 2223; + RightClicktoPermanentlyAdd20toLifePotionofLife: 2224; + Shrine: 2225; + TeleportPad: 2226; AJadeFigurine: 2227; TheGoldenBird: 2228; LamEsensTome1: 2229; @@ -4021,6 +4316,7 @@ declare global { HoradricCube: 2231; HoradricScroll: 2232; MephistosSoulstone: 2233; + RightClicktoLearnSkillofYourChoiceBookofSkill: 2234; Ear: 2235; ChippedAmethyst: 2236; FlawedAmethyst: 2237; @@ -4052,6 +4348,22 @@ declare global { Diamond: 2263; FlawlessDiamond: 2264; PerfectDiamond: 2265; + MinorHealingPotion: 2266; + LightHealingPotion: 2267; + HealingPotion: 2268; + GreaterHealingPotion: 2269; + SuperHealingPotion: 2270; + MinorManaPotion: 2271; + LightManaPotion: 2272; + ManaPotion: 2273; + GreaterManaPotion: 2274; + SuperManaPotion: 2275; + Herb: 2276; + ChippedSkull: 2277; + FlawedSkull: 2278; + Skull: 2279; + FlawlessSkull: 2280; + PerfectSkull: 2281; AmuletoftheViper: 2697; StaffofKings: 2698; HoradricStaff: 2699; From c1ef79b1444b1f3bd189272d850fb637cce060c4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 7 Mar 2026 01:47:37 -0500 Subject: [PATCH 719/758] Update Necromancer.js - Forgot to add the new RaiseArmy script --- d2bs/kolbot/libs/config/Necromancer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index c667ca937..6ed00e81d 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -34,6 +34,7 @@ function LoadConfig () { Config.BattleOrders.Wait = 10; // Duration to wait for players to join game in seconds (default: 10) Scripts.GetFade = false; // Get fade in River of Flames - only works if we are wearing an item with ctc Fade + Scripts.RaiseArmy = false; // Go through pindle portal and raise an army of skeletons, then return to town. Only works if necromancer is configured to raise skeletons and/or revives. // ## Team MF Config.MFLeader = false; // Set to true if you have one or more MFHelpers. Opens TP and gives commands when doing normal MF runs. From e7702860e733e39daa30f38f1e6cc816378a2e8d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:18:53 -0400 Subject: [PATCH 720/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 6eb677b24..050d252d6 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 6eb677b243615c1706c0591f6a4d2ed31dd37229 +Subproject commit 050d252d66e3c9e9b05112efa569518f676e6df4 From b4c42922726d4f0d7d6c600e92526f18171d1a56 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:27:11 -0400 Subject: [PATCH 721/758] Fix DiabloHelper ending runSeals early due to `fail to kill` seal boss - Wrap each seal invocation in a try/catch to avoid unhandled exceptions stopping the seal order. ScriptError is rethrown to preserve control-flow exceptions; generic Errors with "Failed to kill" are warned (console.warn) when running under the DiabloHelper script but rethrown for other scripts. This prevents non-fatal kill failures from crashing DiabloHelper while preserving error behavior for other contexts. --- d2bs/kolbot/libs/core/Common/Diablo.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 62a7241e1..50c624979 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -252,7 +252,21 @@ }; sealOrder.forEach(function (seal) { if (_Diablo.diabloSpawned) return; - seals[seal](); + try { + seals[seal](); + } catch (e) { + if (e instanceof ScriptError) { + throw e; + } + + if (e instanceof Error && e.message.includes("Failed to kill")) { + if (Loader.scriptName() === "DiabloHelper") { + console.warn(e); + } else { + throw e; + } + } + } }); }, From 14e2a21773d03d8d67a89e622ac10d653c30b456 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 12 Mar 2026 19:08:07 -0400 Subject: [PATCH 722/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 050d252d6..2081379ea 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 050d252d66e3c9e9b05112efa569518f676e6df4 +Subproject commit 2081379eaa7cf7cc4cdbf35a6f10e7bbec0b3c8c From 61a4cb39cedd31e4e1d33e7b33772286205142df Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 21 Mar 2026 20:14:53 -0400 Subject: [PATCH 723/758] Fix diablo light watcher ending chores early - Don't throw the script event if we are actively doing chores --- d2bs/kolbot/libs/core/Common/Diablo.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/d2bs/kolbot/libs/core/Common/Diablo.js b/d2bs/kolbot/libs/core/Common/Diablo.js index 50c624979..5eca918b4 100644 --- a/d2bs/kolbot/libs/core/Common/Diablo.js +++ b/d2bs/kolbot/libs/core/Common/Diablo.js @@ -91,6 +91,8 @@ return function () { if (Common.Diablo.done) return false; + // don't throw if we are doing chores + if (Town.choresActive) return true; // check every 1/4 second if (getTickCount() - diaTick < 250) return true; diaTick = getTickCount(); From 40e8cf3fd4f457d8fac15600aff3b5107ce32d8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:46:49 -0400 Subject: [PATCH 724/758] Bump flatted from 3.3.1 to 3.4.2 (#477) Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.1 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.3.1...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c12957767..4834d7311 100644 --- a/package-lock.json +++ b/package-lock.json @@ -809,9 +809,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "node_modules/fs.realpath": { @@ -2003,9 +2003,9 @@ } }, "flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "fs.realpath": { From ce64cd30863a5863ce7f25d2c697718a16bebb3a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 24 Mar 2026 01:01:38 -0400 Subject: [PATCH 725/758] Cleanup GameAction task logging - Don't log entire object, just the action we are starting and if the action is `doDrop` the items we are preparing to drop --- .../libs/systems/gameaction/GameAction.d.ts | 18 +++++++++++++----- .../libs/systems/gameaction/GameAction.js | 12 ++++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts index 3e3a1459e..7ba59c0d7 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.d.ts @@ -6,13 +6,13 @@ export interface GameActionType { LogMerc: boolean; SaveScreenShot: boolean; IngameTime: number; - task: { action: string, data: any } | null; + task: { hash: string; profile: string; action: string; data: unknown } | null; // Methods init(task: string): void; - update(action: string, data: string | Object): void; - gameInfo(): { gameName: string, gamePass: string }; - getLogin(): { realm: string, account: string, password: string }; + update(action: string, data: string | object): void; + gameInfo(): { gameName: string; gamePass: string }; + getLogin(): { realm: string; account: string; password: string }; getCharacters(): string[]; inGameCheck(): boolean; load(hash: string): string; @@ -22,6 +22,14 @@ export interface GameActionType { } declare global { + type DoDropGameActionData = { + items: Array<{ title: string; character: string; realm: string; account: string; itemid: string }>; + gameName: string; + gamePass: string; + realm: string; + account: string; + chars: string[]; + }; + const GameAction: GameActionType; } -export {}; \ No newline at end of file diff --git a/d2bs/kolbot/libs/systems/gameaction/GameAction.js b/d2bs/kolbot/libs/systems/gameaction/GameAction.js index c0ced3e89..2badd4276 100644 --- a/d2bs/kolbot/libs/systems/gameaction/GameAction.js +++ b/d2bs/kolbot/libs/systems/gameaction/GameAction.js @@ -9,7 +9,6 @@ include("systems/mulelogger/MuleLogger.js"); /** @type {import("./GameAction").GameActionType} */ const GameAction = { - /** @type {{ action: string, data: any } | null} */ task: null, /** @@ -19,12 +18,21 @@ const GameAction = { init: function (task) { try { GameAction.task = JSON.parse(task); - console.log("ÿc4GameActionÿc0: Task: ", GameAction.task); + const { action } = GameAction.task; + console.log("ÿc4GameActionÿc0: Task: " + action); if (this.task["data"] && typeof this.task.data === "string") { this.task.data = JSON.parse(this.task.data); } + if (this.task.action === "doDrop" && this.task.data && Array.isArray(this.task.data.items)) { + /** @type {GameActionData} */ + let data = (this.task.data); + for (let i = 0; i < data.items.length; i += 1) { + console.log("ÿc4GameActionÿc0: Item: " + data.items[i].title); + } + } + MuleLogger.LogNames = this.LogNames; MuleLogger.LogItemLevel = this.LogItemLevel; MuleLogger.LogEquipped = this.LogEquipped; From 33e309ba990c1581572485cebcd39420d9f08711 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 24 Mar 2026 02:08:58 -0400 Subject: [PATCH 726/758] Add mode + equipped boolean to MuleLogger.logItem --- d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 95bea2067..1b322bfce 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -208,6 +208,7 @@ const MuleLogger = { const itemInfo = { id: unit.classid, code: unit.code, + mode: unit.mode, name: name, prefix: unit.prefix, suffix: unit.suffix, @@ -230,7 +231,8 @@ const MuleLogger = { flags: unit.getFlags(), ethereal: unit.getFlag(sdk.items.flags.Ethereal), runeword: unit.getFlag(sdk.items.flags.Runeword), - stats: MuleLogger.dumpItemStats(unit) + stats: MuleLogger.dumpItemStats(unit), + equipped: unit.isEquipped, }; let desc = ( From 2a35cf44e15ab2c3a33d7df7b2c09f59bd677254 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 24 Mar 2026 02:09:02 -0400 Subject: [PATCH 727/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 2081379ea..6661ce0bc 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 2081379eaa7cf7cc4cdbf35a6f10e7bbec0b3c8c +Subproject commit 6661ce0bcf57e144b9a963d1e5d8cb5179550bf6 From 3fe5f2ad7d5300593de834e221b12d2b261dba7d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:34:20 -0400 Subject: [PATCH 728/758] Update Pather.move to check for itemoncursor - Sometimes items were getting stuck on cursor due to scriptevents bubbling up, handle it here --- d2bs/kolbot/libs/core/Pather.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index c7e747405..7ed83f37b 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -465,6 +465,14 @@ const Pather = { getUIFlag(this.cancelFlags[i]) && me.cancel(); } + if (me.itemoncursor) { + console.warn("Pather.move: Item on cursor, dropping it to prevent pathing issues."); + let item = Game.getCursorUnit(); + if (item) { + item.drop(); + } + } + if (typeof target.x !== "number" || typeof target.y !== "number") { throw new Error("move: Coords must be numbers"); } From 0e64db2f774ba28d6a37c61b3328335bdbaf642a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:23:18 -0400 Subject: [PATCH 729/758] Update barbarian.checkCorpse to ignore waterwatcher --- d2bs/kolbot/libs/core/Attacks/Barbarian.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attacks/Barbarian.js b/d2bs/kolbot/libs/core/Attacks/Barbarian.js index a5f6ab0c9..7372529cf 100644 --- a/d2bs/kolbot/libs/core/Attacks/Barbarian.js +++ b/d2bs/kolbot/libs/core/Attacks/Barbarian.js @@ -264,21 +264,32 @@ if (!unit || !copyUnit(unit).x || !unit.dead) { return false; } - if ([sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3].indexOf(unit.classid) === -1 - && unit.spectype === sdk.monsters.spectype.All) { + const councilClassIds = [sdk.monsters.Council1, sdk.monsters.Council2, sdk.monsters.Council3]; + if (councilClassIds.indexOf(unit.classid) === -1 && unit.spectype === sdk.monsters.spectype.All) { // why ignore all normal monsters? return false; } + const waterWatcherIds = [sdk.monsters.WaterWatcherHead, sdk.monsters.WaterWatcherLimb]; + if (waterWatcherIds.includes(unit.classid)) { + return false; + } + // monstats2 doesn't contain guest monsters info. sigh.. if (unit.classid <= sdk.monsters.BurningDeadArcher2 - && !getBaseStat("monstats2", unit.classid, "corpseSel")) { + && !getBaseStat("monstats2", unit.classid, "corpseSel") + ) { return false; } let states = [ - sdk.states.FrozenSolid, sdk.states.Revive, sdk.states.Redeemed, - sdk.states.CorpseNoDraw, sdk.states.Shatter, sdk.states.RestInPeace, sdk.states.CorpseNoSelect + sdk.states.FrozenSolid, + sdk.states.Revive, + sdk.states.Redeemed, + sdk.states.CorpseNoDraw, + sdk.states.Shatter, + sdk.states.RestInPeace, + sdk.states.CorpseNoSelect ]; return (unit.distance <= 25 From 9abe123a8580701bbe226e07bab4203f22e7d0fc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:25:59 -0400 Subject: [PATCH 730/758] Update Misc.poll to accept 4th param determining nativeDelay vs worker delay - We sometimes don't want to allow background work while polling for something since game might not be in a ready state. --- d2bs/kolbot/libs/core/Misc.js | 6 ++- d2bs/kolbot/libs/core/Pather.js | 80 ++++++++++++++++++++++++--------- d2bs/kolbot/sdk/types/Misc.d.ts | 2 +- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index d7392c79c..800a88f94 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -1152,17 +1152,19 @@ const Misc = (function () { * @param {function(): T} check * @param {number} [timeout=6000] * @param {number} [sleep=40] + * @param {boolean} [useNativeDelay=false] - use native delay function instead of alloying background processing during the wait time * @returns {T | false} */ - poll: function (check, timeout = 6000, sleep = 40) { + poll: function (check, timeout = 6000, sleep = 40, useNativeDelay = false) { let ret, start = getTickCount(); + let delayFunc = useNativeDelay ? delay : nativeDelay; while (getTickCount() - start <= timeout) { if ((ret = check())) { return ret; } - delay(sleep); + delayFunc(sleep); } return false; diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 7ed83f37b..4e0d1a82c 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -326,9 +326,12 @@ const Pather = { if (!nodes.length) return (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y }); } - return (nodes.find((node) => getDistance(spot.x, spot.y, node.x, node.y) < distance - && Pather.checkSpot(node.x, node.y, spotSettings.coll)) - || (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y })); + return (nodes.find(function (node) { + return ( + getDistance(spot.x, spot.y, node.x, node.y) < distance + && Pather.checkSpot(node.x, node.y, spotSettings.coll) + ); + }) || (spotSettings.returnSpotOnError ? spot : { x: me.x, y: me.y })); }, /** @@ -722,7 +725,9 @@ const Pather = { * @todo does this need a validLocation check? - maybe if we fail once check the spot */ teleportTo: function (x, y, maxRange = 5) { - for (let i = 0; i < 3; i += 1) { + const node = new PathNode(x, y); + + for (let i = 0; i < 3; i++) { Config.PacketCasting > 0 ? Packet.teleport(x, y) : Skill.cast(sdk.skills.Teleport, sdk.skills.hand.Right, x, y); @@ -730,7 +735,7 @@ const Pather = { let pingDelay = i === 0 ? 150 : me.getPingDelay(); while (getTickCount() - tick < Math.max(500, pingDelay * 2 + 200)) { - if ([x, y].distance < maxRange) { + if (node.distance < maxRange) { return true; } @@ -900,7 +905,7 @@ const Pather = { for (let i = 0; i < 3; i++) { Misc.click(0, 0, door); - if (Misc.poll(() => door.mode === sdk.objects.mode.Active, 1000, 30)) { + if (Misc.poll(function () { return door.mode === sdk.objects.mode.Active; }, 1000, 30)) { return true; } @@ -1091,7 +1096,9 @@ const Pather = { } delay(40); - Misc.poll(() => me.gameReady, 500, 100); + Misc.poll(function () { + return me.gameReady; + }, 500, 100); let unit = presetUnit.realCoords(); @@ -1130,7 +1137,9 @@ const Pather = { } delay(40); - Misc.poll(() => me.gameReady, 500, 100); + Misc.poll(function () { + return me.gameReady; + }, 500, 100); let { x, y } = presetUnit.realCoords(); return this.moveTo(x + offX, y + offY, 3, clearPath, pop); @@ -1166,7 +1175,9 @@ const Pather = { } delay(40); - Misc.poll(() => me.gameReady, 500, 100); + Misc.poll(function () { + return me.gameReady; + }, 500, 100); let { x, y } = presetUnit.realCoords(); return this.moveToEx(x + offX, y + offY, givenSettings); @@ -1197,7 +1208,9 @@ const Pather = { } delay(40); - Misc.poll(() => me.gameReady, 500, 100); + Misc.poll(function () { + return me.gameReady; + }, 500, 100); let { x, y } = presetUnit.realCoords(); return this.moveToEx(x + offX, y + offY, givenSettings); @@ -1223,7 +1236,9 @@ const Pather = { for (let currTarget of areas) { console.info(null, getAreaName(me.area) + "ÿc8 --> ÿc0" + getAreaName(currTarget)); - const area = Misc.poll(() => getArea(me.area)); + const area = Misc.poll(function () { + return getArea(me.area); + }); if (!area) throw new Error("moveToExit: error in getArea()"); /** @type {Array} */ @@ -1239,7 +1254,7 @@ const Pather = { 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) { @@ -1263,7 +1278,9 @@ const Pather = { 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) { @@ -1300,11 +1317,15 @@ const Pather = { getDistanceToExit: function (area, exit) { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); - let areaToCheck = Misc.poll(() => getArea(area)); + let areaToCheck = Misc.poll(function () { + return getArea(area); + }); if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); let exits = areaToCheck.exits; if (!exits.length) throw new Error("Failed to find exits"); - let loc = exits.find(a => a.target === exit); + let loc = exits.find(function (a) { + return a.target === exit; + }); console.debug(area, exit, loc); return loc ? [loc.x, loc.y].distance : Infinity; }, @@ -1317,11 +1338,15 @@ const Pather = { getExitCoords: function (area, exit) { area === undefined && (area = me.area); exit === undefined && (exit = me.area + 1); - let areaToCheck = Misc.poll(() => getArea(area)); + let areaToCheck = Misc.poll(function () { + return getArea(area); + }); if (!areaToCheck) throw new Error("Couldn't get area info for " + getAreaName(area)); let exits = areaToCheck.exits; if (!exits.length) throw new Error("Failed to find exits"); - let loc = exits.find(a => a.target === exit); + let loc = exits.find(function (a) { + return a.target === exit; + }); console.debug(area, exit, loc); return loc ? { x: loc.x, y: loc.y } : false; }, @@ -1334,7 +1359,9 @@ const Pather = { getNearestRoom: function (area) { let x, y, minDist = 10000; - let room = Misc.poll(() => getRoom(area), 1000, 200); + let room = Misc.poll(function () { + return getRoom(area); + }, 1000, 200); if (!room) return false; do { @@ -1393,7 +1420,9 @@ const Pather = { */ openUnit: function (type, id) { /** @type {ObjectUnit | Tile} */ - let unit = Misc.poll(() => getUnit(type, id), 1000, 200); + let unit = Misc.poll(function () { + return getUnit(type, id); + }, 1000, 200); if (!unit) throw new Error("openUnit: Unit not found. ID: " + unit); if (unit.mode !== sdk.objects.mode.Inactive) return true; @@ -1409,7 +1438,9 @@ const Pather = { */ useUnit: function (type, id, targetArea) { /** @type {ObjectUnit | Tile} */ - let unit = Misc.poll(() => getUnit(type, id), 2000, 200); + let unit = Misc.poll(function () { + return getUnit(type, id); + }, 2000, 200); if (!unit) { throw new Error( "useUnit: Unit not found. TYPE: " + type + " ID: " + id @@ -1481,12 +1512,15 @@ const Pather = { if (me.inTown) { if (me.inArea(sdk.areas.LutGholein)) { let npc = Game.getNPC(NPC.Warriv); + let checkA1Success = function () { + return me.gameReady && me.inArea(sdk.areas.RogueEncampment); + }; 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)) { + if (!Misc.poll(checkA1Success, 2000, 100, true)) { throw new Error("Failed to go to act 1 using Warriv"); } if (me.inArea(targetArea)) { @@ -1611,7 +1645,9 @@ const Pather = { } // In case lag causes the wp menu to stay open - Misc.poll(() => me.gameReady, 2000, 100) && getUIFlag(sdk.uiflags.Waypoint) && me.cancelUIFlags(); + Misc.poll(function () { + return me.gameReady; + }, 2000, 100) && getUIFlag(sdk.uiflags.Waypoint) && me.cancelUIFlags(); } Packet.flash(me.gid, pingDelay); diff --git a/d2bs/kolbot/sdk/types/Misc.d.ts b/d2bs/kolbot/sdk/types/Misc.d.ts index 237b935ff..bac30a3f2 100644 --- a/d2bs/kolbot/sdk/types/Misc.d.ts +++ b/d2bs/kolbot/sdk/types/Misc.d.ts @@ -47,7 +47,7 @@ declare global { errorReport(error: Error | string, script?: string): void; debugLog(msg: string): void; useMenu(id: number): boolean; - poll(check: () => T, timeout?: number, sleep?: number): T | false; + poll(check: () => T, timeout?: number, sleep?: number, useNativeDelay?: boolean): T | false; getUIFlags(excluded?: number[]): number[] | null; getQuestStates(questId: number): number[]; } From 627d9a27f58105859faf7c6c202cd4cc69e93eb3 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 29 Mar 2026 13:23:10 -0400 Subject: [PATCH 731/758] Fix typo in Misc.poll logic --- d2bs/kolbot/libs/core/Misc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/core/Misc.js b/d2bs/kolbot/libs/core/Misc.js index 800a88f94..4ad0c89d6 100644 --- a/d2bs/kolbot/libs/core/Misc.js +++ b/d2bs/kolbot/libs/core/Misc.js @@ -1157,7 +1157,7 @@ const Misc = (function () { */ poll: function (check, timeout = 6000, sleep = 40, useNativeDelay = false) { let ret, start = getTickCount(); - let delayFunc = useNativeDelay ? delay : nativeDelay; + let delayFunc = useNativeDelay ? nativeDelay : delay; while (getTickCount() - start <= timeout) { if ((ret = check())) { From 0f86a0a754826d89635253b332632f9cc44f376d Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:25:06 -0400 Subject: [PATCH 732/758] Fix typo in Follower.js - HallsoftheDeadLvl3 -> MaggotLairLvl3 for taxi staff --- d2bs/kolbot/libs/scripts/Follower.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/Follower.js b/d2bs/kolbot/libs/scripts/Follower.js index ff23e0bc9..3e83d7358 100644 --- a/d2bs/kolbot/libs/scripts/Follower.js +++ b/d2bs/kolbot/libs/scripts/Follower.js @@ -321,7 +321,7 @@ const Follower = new Runnable( ], [ "staff", () => { - Pather.journeyTo(sdk.areas.HallsoftheDeadLvl3); + Pather.journeyTo(sdk.areas.MaggotLairLvl3); Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest); } ], From 89b90d3945a108d466f50c7e509d71e37d24e0a4 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:28:34 -0400 Subject: [PATCH 733/758] Fix wpAreas references to use Pather - Replace incorrect uses of this.wpAreas (and related references) with Pather.wpAreas to ensure the module consistently accesses the shared waypoint arrays. These changes fix failures when methods or callbacks lose the correct this context (e.g., useWaypoint validation, waypoint checks in random selection and target checks, plotCourse logic, and town waypoint handling), preventing incorrect "this.wpAreas is undefined" --- d2bs/kolbot/libs/core/Pather.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 4e0d1a82c..95165b974 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -1490,7 +1490,7 @@ const Pather = { break; default: if (typeof targetArea !== "number") throw new Error("useWaypoint: Invalid targetArea parameter"); - if (this.wpAreas.indexOf(targetArea) < 0) throw new Error("useWaypoint: Invalid area"); + if (Pather.wpAreas.indexOf(targetArea) < 0) throw new Error("useWaypoint: Invalid area"); break; } @@ -1580,7 +1580,7 @@ const Pather = { case "random": let validWps = this.nonTownWpAreas .filter(function (area) { - return getWaypoint(this.wpAreas.indexOf(area)); + return getWaypoint(Pather.wpAreas.indexOf(area)); }); if (!validWps.length) { if (me.inTown && Pather.moveToExit(me.area + 1, true)) { @@ -1597,7 +1597,7 @@ const Pather = { return true; } - if (!getWaypoint(this.wpAreas.indexOf(targetArea))) { + if (!getWaypoint(Pather.wpAreas.indexOf(targetArea))) { me.cancel(); console.log("Trying to get the waypoint: " + getAreaName(targetArea)); me.overhead("Trying to get the waypoint"); @@ -2014,7 +2014,7 @@ const Pather = { target = this.plotCourse(area, me.area); } else { target = { course: [sdk.areas.CanyonofMagic, sdk.areas.DurielsLair], useWP: false }; - this.wpAreas.indexOf(me.area) === -1 && (target.useWP = true); + Pather.wpAreas.indexOf(me.area) === -1 && (target.useWP = true); } console.info(true, "Course :: " + target.course, "journeyTo"); @@ -2056,14 +2056,14 @@ const Pather = { if (!me.inTown) { Precast.doPrecast(false); - if (this.wpAreas.includes(currArea) - && !getWaypoint(this.wpAreas.indexOf(currArea))) { + if (Pather.wpAreas.includes(currArea) + && !getWaypoint(Pather.wpAreas.indexOf(currArea))) { this.getWP(currArea); } } if (me.inTown && this.nextAreas[currArea] !== targetArea - && this.wpAreas.includes(targetArea) && getWaypoint(this.wpAreas.indexOf(targetArea))) { + && Pather.wpAreas.includes(targetArea) && getWaypoint(Pather.wpAreas.indexOf(targetArea))) { this.useWaypoint(targetArea, !Pather.initialized); Precast.doPrecast(false); } else if (currArea === sdk.areas.StonyField && targetArea === sdk.areas.Tristram) { From ed546b90a0bcccef5f7eecbf8a0a097940406dff Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 20 Apr 2026 01:06:12 -0400 Subject: [PATCH 734/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 6661ce0bc..3c5827f3e 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 6661ce0bcf57e144b9a963d1e5d8cb5179550bf6 +Subproject commit 3c5827f3eefb9eec7c57488b9e900d5944a9cb1b From e0b3450812a9f1bc94109190fe63ff5ee143ae9f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 28 Apr 2026 20:45:43 -0400 Subject: [PATCH 735/758] Minor cleanups - Add game info logging in Experience.js when DataFile stats are not present. Add documentation comment for NTItemParser's alias in/notin parsing. Normalize braces and reformat complex conditions in Pickit.js for readability (no logic changes). Use nativeDelay in manualplay to avoid the previous delay call. Warn on attempts to access unknown properties in LazyLoader. Extend Town.d.ts with choresActive and tighten ignoredItemTypes to number[], and reformat drinkPots signature. Add ignoreDeprecations setting to tsconfig.json. --- d2bs/kolbot/libs/core/Experience.js | 2 + d2bs/kolbot/libs/core/NTItemParser.js | 5 + d2bs/kolbot/libs/core/Pickit.js | 37 +++- .../libs/manualplay/libs/MiscOverrides.js | 165 --------------- d2bs/kolbot/libs/manualplay/main.js | 2 +- .../libs/manualplay/threads/MapHelper.js | 190 +++++++++++++++++- d2bs/kolbot/libs/modules/LazyLoader.js | 3 + d2bs/kolbot/sdk/types/Town.d.ts | 29 ++- tsconfig.json | 1 + 9 files changed, 239 insertions(+), 195 deletions(-) delete mode 100644 d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js diff --git a/d2bs/kolbot/libs/core/Experience.js b/d2bs/kolbot/libs/core/Experience.js index 4d6a73185..e4658274a 100644 --- a/d2bs/kolbot/libs/core/Experience.js +++ b/d2bs/kolbot/libs/core/Experience.js @@ -134,6 +134,8 @@ const Experience = { if (me.getStat(sdk.stats.Level) > DataFile.getStats().level) { D2Bot.printToConsole("Congrats! You gained a level. Current level:" + me.getStat(sdk.stats.Level), sdk.colors.D2Bot.Green); } + } else { + D2Bot.printToConsole("[Game: " + me.gamename + (me.gamepassword ? "//" + me.gamepassword : "") + getGameTime + "]"); } } }; diff --git a/d2bs/kolbot/libs/core/NTItemParser.js b/d2bs/kolbot/libs/core/NTItemParser.js index d05301834..72ebcfa72 100644 --- a/d2bs/kolbot/libs/core/NTItemParser.js +++ b/d2bs/kolbot/libs/core/NTItemParser.js @@ -513,6 +513,11 @@ NTIP.IsSyntaxInt = function (ch) { ); }; +/** + * @desc Parses [alias]in() and [alias]notin() syntax in NIP and converts them to standard syntax. + * For example, [type]in(armor, weapon) will be converted to ([type] == armor || [type] == weapon) + * and [name]notin(ring, amulet) will be converted to ([name] != ring && [name] != amulet) + */ NTIP.parseAliasIn = { in: "\[([^\]]+)\]in\(", notin: "\[([^\]]+)\]notin\(", diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index ddbdcb49d..475c1a950 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -299,9 +299,15 @@ const Pickit = { return resultObj(Pickit.Result.UTILITY, "Cubing Repair Ingredients"); } - if (CraftingSystem.checkItem(unit)) return resultObj(Pickit.Result.CRAFTING, "Crafting System"); - if (Cubing.checkItem(unit)) return resultObj(Pickit.Result.CUBING, "Cubing"); - if (Runewords.checkItem(unit)) return resultObj(Pickit.Result.RUNEWORD, "Runewords"); + if (CraftingSystem.checkItem(unit)) { + return resultObj(Pickit.Result.CRAFTING, "Crafting System"); + } + if (Cubing.checkItem(unit)) { + return resultObj(Pickit.Result.CUBING, "Cubing"); + } + if (Runewords.checkItem(unit)) { + return resultObj(Pickit.Result.RUNEWORD, "Runewords"); + } // if Gemhunting, pick Item for Cubing, if no other system needs it if (Scripts.GemHunter && rval.result === Pickit.Result.UNWANTED) { @@ -339,7 +345,9 @@ const Pickit = { || (me.gold < me.getRepairCost()) )) { // Gold doesn't ta=ke up room, just pick it up - if (unit.classid === sdk.items.Gold) return resultObj(Pickit.Result.WANTED, "LowGold"); + if (unit.classid === sdk.items.Gold) { + return resultObj(Pickit.Result.WANTED, "LowGold"); + } if (!this.invoLocked) { const itemValue = unit.getItemCost(sdk.items.cost.ToSell); @@ -658,7 +666,8 @@ const Pickit = { // 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 (_item.onGroundOrDropping - && (Pather.useTeleport() || me.inTown || !checkCollision(me, _item, sdk.collision.BlockWall))) { + && (Pather.useTeleport() || me.inTown || !checkCollision(me, _item, sdk.collision.BlockWall)) + ) { // Check if the item should be picked let status = this.checkItem(_item); @@ -749,11 +758,18 @@ const Pickit = { for (let gid of this.gidList) { _removeList.push(gid); item = Game.getItem(-1, -1, gid); - if (item && item.onGroundOrDropping - && (!Town.ignoreType(item.itemType) || (item.itemType >= sdk.items.type.HealingPotion - && item.itemType <= sdk.items.type.RejuvPotion)) + if (item + && item.onGroundOrDropping + && ( + !Town.ignoreType(item.itemType) + || ( + item.itemType >= sdk.items.type.HealingPotion + && item.itemType <= sdk.items.type.RejuvPotion + ) + ) && item.itemType !== sdk.items.type.Gold - && getDistance(me, item) <= range) { + && getDistance(me, item) <= range + ) { itemList.push(copyUnit(item)); } } @@ -773,7 +789,8 @@ const Pickit = { let status = this.checkItem(item); if (status.result && this.canPick(item) - && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType))) { + && (Storage.Inventory.CanFit(item) || Pickit.essentials.includes(item.itemType)) + ) { this.pickItem(item, status.result, status.line + " / (fastpick)", retry); } } diff --git a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js b/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js deleted file mode 100644 index 5b8a53bb8..000000000 --- a/d2bs/kolbot/libs/manualplay/libs/MiscOverrides.js +++ /dev/null @@ -1,165 +0,0 @@ -/** -* @filename MiscOverrides.js -* @author theBGuy -* @desc Misc.js additions to improve functionality for map mode -* -*/ - -includeIfNotIncluded("core/Misc.js"); - -Misc.openRedPortal = function (portalID) { - if (!me.getItem(sdk.quest.item.Cube)) return; - - function getTome () { - 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); - tpTome.forEach(function (book) { - while (book.getStat(sdk.stats.Quantity) < 20) { - scroll = npc.getItem(sdk.items.ScrollofTownPortal); - - if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { - scroll.buy(); - } else { - break; - } - - delay(20); - } - }); - } - } - } finally { - me.cancel(); - } - } - - try { - let materials, validMats = []; - - switch (portalID) { - case sdk.areas.MooMooFarm: - if (me.getQuest(sdk.quest.id.TheSearchForCain, 10)) { - throw new Error("Unable to open cow portal because cow king has been killed"); - } - - materials = [sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal]; - - break; - case sdk.areas.UberTristram: - materials = [sdk.quest.item.DiablosHorn, sdk.quest.item.BaalsEye, sdk.quest.item.MephistosBrain]; - - break; - default: - materials = [sdk.quest.item.KeyofTerror, sdk.quest.item.KeyofHate, sdk.quest.item.KeyofDestruction]; - - break; - } - - materials.forEach(function (mat) { - mat === sdk.items.TomeofTownPortal && getTome(); - let item = me.getItem(mat); - !!item && validMats.push(item); - }); - - if (validMats.length !== materials.length) throw new Error("Missing materials to open portal"); - - portalID === sdk.areas.MooMooFarm - ? !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1) - : !me.inArea(sdk.areas.Harrogath) && Town.goToTown(5); - - Town.move("stash"); - - if (portalID && Pather.getPortal(portalID)) throw new Error("Portal is already open"); - - Cubing.openCube(); - - if (!Cubing.emptyCube()) throw new Error("Failed to empty cube"); - - validMats.forEach(function (mat) { - return Storage.Cube.MoveTo(mat); - }); - - Cubing.openCube() && transmute(); - } catch (e) { - console.error(e); - } finally { - me.cancel(); - } -}; - -Misc.talkToTyrael = function () { - if (!me.inArea(sdk.areas.DurielsLair)) return false; - - Pather.walkTo(22621, 15711); - Pather.moveTo(22602, 15705); - Pather.moveTo(22579, 15704); - Pather.moveTo(22575, 15675); - Pather.moveTo(22579, 15655); - Pather.walkTo(22578, 15642); // walk trough door - Pather.moveTo(22578, 15618); - Pather.moveTo(22576, 15591); // tyreal - - let tyrael = Game.getNPC(NPC.Tyrael); - - if (tyrael) { - for (let i = 0; i < 3; i++) { - if (getDistance(me, tyrael) > 3) { - Pather.moveToUnit(tyrael); - } - - tyrael.interact(); - delay(1000 + me.ping); - me.cancel(); - - if (Pather.getPortal(null)) { - me.cancel(); - - break; - } - } - } - - return Pather.usePortal(null); -}; - -Misc.dropItems = function (fromLoc) { - try { - if (!fromLoc) throw new Error("No location given"); - if (fromLoc === sdk.storage.Stash && !Town.openStash()) throw new Error("Failed to open stash"); - - let items = me.findItems(-1, sdk.items.mode.inStorage, fromLoc); - - if (items) { - while (items.length > 0) { - let item = items.shift(); - - if (item.classid === sdk.quest.item.Cube - || (item.isEquippedCharm && Storage.Inventory.IsLocked(item, Config.Inventory))) { - continue; - } else { - item.drop(); - } - } - } else { - throw new Error("Couldn't find any items"); - } - } catch (e) { - console.error(e); - } finally { - me.cancel(); - } -}; diff --git a/d2bs/kolbot/libs/manualplay/main.js b/d2bs/kolbot/libs/manualplay/main.js index d53d84fee..59839e8cf 100644 --- a/d2bs/kolbot/libs/manualplay/main.js +++ b/d2bs/kolbot/libs/manualplay/main.js @@ -281,7 +281,7 @@ function main () { try { while (true) { while (!me.area || !me.gameReady) { - delay(100); + nativeDelay(100); } let hideFlagFound = false; diff --git a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js index d1843d59e..6dcc9225c 100644 --- a/d2bs/kolbot/libs/manualplay/threads/MapHelper.js +++ b/d2bs/kolbot/libs/manualplay/threads/MapHelper.js @@ -72,7 +72,7 @@ function main () { }; Config.init(); - Attack.init(true); + Attack.init(false); Pickit.init(); Storage.Init(); Runewords.init(); @@ -124,6 +124,175 @@ function main () { return true; }; + /** + * @param {number} portalID + */ + const openRedPortal = function (portalID) { + if (!me.getItem(sdk.quest.item.Cube)) return; + + function getTome () { + 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); + tpTome.forEach(function (book) { + while (book.getStat(sdk.stats.Quantity) < 20) { + scroll = npc.getItem(sdk.items.ScrollofTownPortal); + + if (!!scroll && scroll.getItemCost(sdk.items.cost.ToBuy) < me.gold) { + scroll.buy(); + } else { + break; + } + + delay(20); + } + }); + } + } + } finally { + me.cancel(); + } + } + + try { + let materials, validMats = []; + + switch (portalID) { + case sdk.areas.MooMooFarm: + if (me.getQuest(sdk.quest.id.TheSearchForCain, 10)) { + throw new Error("Unable to open cow portal because cow king has been killed"); + } + + materials = [sdk.items.quest.WirtsLeg, sdk.items.TomeofTownPortal]; + + break; + case sdk.areas.UberTristram: + materials = [sdk.quest.item.DiablosHorn, sdk.quest.item.BaalsEye, sdk.quest.item.MephistosBrain]; + + break; + default: + materials = [sdk.quest.item.KeyofTerror, sdk.quest.item.KeyofHate, sdk.quest.item.KeyofDestruction]; + + break; + } + + materials.forEach(function (mat) { + mat === sdk.items.TomeofTownPortal && getTome(); + let item = me.getItem(mat); + !!item && validMats.push(item); + }); + + if (validMats.length !== materials.length) { + throw new Error("Missing materials to open portal"); + } + + portalID === sdk.areas.MooMooFarm + ? !me.inArea(sdk.areas.RogueEncampment) && Town.goToTown(1) + : !me.inArea(sdk.areas.Harrogath) && Town.goToTown(5); + + Town.move("stash"); + + if (portalID && Pather.getPortal(portalID)) { + throw new Error("Portal is already open"); + } + + Cubing.openCube(); + + if (!Cubing.emptyCube()) { + throw new Error("Failed to empty cube"); + } + + validMats.forEach(function (mat) { + return Storage.Cube.MoveTo(mat); + }); + + Cubing.openCube() && transmute(); + } catch (e) { + console.error(e); + } finally { + me.cancel(); + } + }; + + const talkToTyrael = function () { + if (!me.inArea(sdk.areas.DurielsLair)) return false; + + Pather.walkTo(22621, 15711); + Pather.moveTo(22602, 15705); + Pather.moveTo(22579, 15704); + Pather.moveTo(22575, 15675); + Pather.moveTo(22579, 15655); + Pather.walkTo(22578, 15642); // walk trough door + Pather.moveTo(22578, 15618); + Pather.moveTo(22576, 15591); // tyreal + + let tyrael = Game.getNPC(NPC.Tyrael); + + if (tyrael) { + for (let i = 0; i < 3; i++) { + if (getDistance(me, tyrael) > 3) { + Pather.moveToUnit(tyrael); + } + + tyrael.interact(); + delay(1000 + me.ping); + me.cancel(); + + if (Pather.getPortal(null)) { + me.cancel(); + + break; + } + } + } + + return Pather.usePortal(null); + }; + + /** + * @param {number} fromLoc + */ + const dropItems = function (fromLoc) { + try { + if (!fromLoc) throw new Error("No location given"); + if (fromLoc === sdk.storage.Stash && !Town.openStash()) throw new Error("Failed to open stash"); + + let items = me.findItems(-1, sdk.items.mode.inStorage, fromLoc); + + if (items) { + while (items.length > 0) { + let item = items.shift(); + + if (item.classid === sdk.quest.item.Cube + || (item.isEquippedCharm && Storage.Inventory.IsLocked(item, Config.Inventory))) { + continue; + } else { + item.drop(); + } + } + } else { + throw new Error("Couldn't find any items"); + } + } catch (e) { + console.error(e); + } finally { + me.cancel(); + } + }; + while (true) { if (getUIFlag(sdk.uiflags.EscMenu)) { delay(100); @@ -171,7 +340,7 @@ function main () { break; case "unit": if (me.inArea(sdk.areas.MooMooFarm) - || (me.inArea(sdk.areas.DurielsLair) && Misc.talkToTyrael())) { + || (me.inArea(sdk.areas.DurielsLair) && talkToTyrael())) { break; } @@ -324,19 +493,24 @@ function main () { break; case "clear": - Attack.clear(10); + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { + showConsole(); + console.warn("No valid attack skill(s) set for clear command. Check your Config."); + } else { + Attack.clear(10); + } break; case "cowportal": - Misc.openRedPortal(sdk.areas.MooMooFarm); + openRedPortal(sdk.areas.MooMooFarm); break; case "ubertrist": - Misc.openRedPortal(sdk.areas.UberTristram); + openRedPortal(sdk.areas.UberTristram); break; case "uberportal": - Misc.openRedPortal(); + openRedPortal(); break; case "filltps": @@ -422,11 +596,11 @@ function main () { case "drop": switch (obj.action) { case "invo": - Misc.dropItems(sdk.storage.Inventory); + dropItems(sdk.storage.Inventory); break; case "stash": - Misc.dropItems(sdk.storage.Stash); + dropItems(sdk.storage.Stash); break; } diff --git a/d2bs/kolbot/libs/modules/LazyLoader.js b/d2bs/kolbot/libs/modules/LazyLoader.js index bb6dc1f42..f3f6dd2ac 100644 --- a/d2bs/kolbot/libs/modules/LazyLoader.js +++ b/d2bs/kolbot/libs/modules/LazyLoader.js @@ -110,6 +110,9 @@ return typeof value === "function" ? value.bind(instance) : value; } } + if (!target.hasOwnProperty(property)) { + console.warn("Attempted to access unknown property: " + String(property)); + } return target[property]; }, diff --git a/d2bs/kolbot/sdk/types/Town.d.ts b/d2bs/kolbot/sdk/types/Town.d.ts index 62271f455..772ef2f4e 100644 --- a/d2bs/kolbot/sdk/types/Town.d.ts +++ b/d2bs/kolbot/sdk/types/Town.d.ts @@ -5,17 +5,21 @@ declare global { let sellTimer: number; let lastChores: number; const dontStashGids: Set; + let choresActive: boolean; - const tasks: Map; - const ignoredItemTypes: any[]; + const tasks: Map< + Act, + { + Heal: NPC; + Shop: NPC; + Gamble: NPC; + Repair: NPC; + Merc: NPC; + Key: NPC; + CainID: NPC; + } + >; + const ignoredItemTypes: number[]; function needPotions(): boolean; function doChores(repair?: boolean): boolean; function npcInteract(name?: string, cancel?: boolean): boolean | NPCUnit; @@ -43,7 +47,10 @@ declare global { function needGamble(): boolean; function getGambledItem(list?: any[]): false | ItemUnit; function buyPots(quantity?: number, type?: string | number, drink?: boolean, force?: boolean, npc?: Unit): boolean; - function drinkPots(type?: string | number, log?: boolean): { + function drinkPots( + type?: string | number, + log?: boolean, + ): { potName: string; quantity: number; }; diff --git a/tsconfig.json b/tsconfig.json index 23d371087..7faee461d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "ignoreDeprecations": "6.0", "module": "None", "lib": [ "ES2015", From 7548104bf77a768ebc2e28f924e9d11c30407056 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 10 May 2026 19:47:57 -0400 Subject: [PATCH 736/758] Fix charsi menu needing `TradeRepair` on shop task --- d2bs/kolbot/libs/core/Prototypes.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 4a30ec83d..2107ccfff 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -839,12 +839,22 @@ Unit.prototype.startTrade = function (mode) { if (this.type !== sdk.unittype.NPC) throw new Error("Unit.startTrade: Must be used on NPCs."); console.log("Starting " + mode + " at " + this.name); if (getUIFlag(sdk.uiflags.Shop)) return true; - - const menuId = mode === "Gamble" - ? sdk.menu.Gamble - : mode === "Repair" - ? sdk.menu.TradeRepair - : sdk.menu.Trade; + + const unitName = this.name; + const menuId = (function (mode) { + switch (true) { + case mode === "gamble": + return sdk.menu.Gamble; + case mode === "repair": + return sdk.menu.TradeRepair; + case mode === "shop" && String.isEqual(NPC.Charsi, unitName): + return sdk.menu.TradeRepair; + case mode === "shop": + return sdk.menu.Trade; + default: + throw new Error("Unit.startTrade: Invalid mode " + mode); + } + })(mode.toLowerCase()); for (let i = 0; i < 3; i += 1) { // Incremental delay on retries From 745ad100fe635271f20a76e71bf4592bb2b96455 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 May 2026 15:39:28 -0400 Subject: [PATCH 737/758] Refactor Pickit ItemStats and pickup checks - Move item position (x,y,area) into ItemStats and replace the static dist/useTk calculation with a dynamic useTk getter/setter that computes distance and collision via CollMap.checkColl, ensuring up-to-date decisions for telekinesis. Replace delay(500) with nativeDelay(500) before cancelling UI to avoid timing/interaction issues. Tidy up a collision condition in the pickup loop and enhance the "not enough room" warning to include current inventory used percent. These changes improve accuracy of pickup logic and provide clearer logging for automule triggers. --- d2bs/kolbot/libs/core/Pickit.js | 36 +++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 475c1a950..619be9832 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -390,12 +390,29 @@ const Pickit = { this.name = unit.name; this.color = Item.color(unit); this.gold = unit.getStat(sdk.stats.Gold); - this.dist = (unit.distance || Infinity); - this.useTk = (Skill.haveTK && Pickit.tkable.includes(this.type) - && this.dist > 5 && this.dist < 20 && !checkCollision(me, unit, sdk.collision.WallOrRanged)); + this.x = unit.x; + this.y = unit.y; + this.area = unit.area; + this._useTk = (Skill.haveTK && Pickit.tkable.includes(this.type)); this.picked = false; } + Object.defineProperty(ItemStats.prototype, "useTk", { + get: function () { + if (!this._useTk) return false; + let dist = this.distance; + let coll = CollMap.checkColl(me, this, sdk.collision.WallOrRanged); + return dist > 5 && dist < 20 && !coll; + }, + /** + * @this {ItemStats} + * @param {boolean} value + */ + set: function (value) { + this._useTk = value; + } + }); + const itemCount = me.itemcount; const cancelFlags = [ sdk.uiflags.Inventory, sdk.uiflags.NPCMenu, @@ -409,7 +426,7 @@ const Pickit = { if (!item.onGroundOrDropping) return false; if (cancelFlags.some(getUIFlag)) { - delay(500); + nativeDelay(500); me.cancel(0); } @@ -666,7 +683,11 @@ const Pickit = { // 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 (_item.onGroundOrDropping - && (Pather.useTeleport() || me.inTown || !checkCollision(me, _item, sdk.collision.BlockWall)) + && ( + Pather.useTeleport() + || me.inTown + || !checkCollision(me, _item, sdk.collision.BlockWall) + ) ) { // Check if the item should be picked let status = this.checkItem(_item); @@ -710,7 +731,10 @@ const Pickit = { // Can't make room - trigger automule if (copyUnit(_item).x !== undefined) { Item.logger("No room for", _item); - console.warn("ÿc7Not enough room for " + Item.color(_item) + _item.name); + console.warn( + "ÿc7Not enough room for " + Item.color(_item) + _item.name + + " ÿc0(" + Storage.Inventory.UsedSpacePercent() + "% full)" + ); Pickit.ignoreList.add(_item.gid); if (canUseMule) { console.debug("Attempt to trigger automule"); From c66ce573c1756d2f88c429e4a4db9b876189a210 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 May 2026 15:39:46 -0400 Subject: [PATCH 738/758] Update globals.d.ts --- d2bs/kolbot/sdk/globals.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 6ea982f4b..1cf5dbfbb 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -1372,6 +1372,7 @@ declare global { AnnounceGames: boolean; AnnounceMessage: string; AfterGameMessage: string; + GameDescription: string; /** * Seconds to wait before opening the join game window after a game From 9736d50388cb0f28649e8bd42db9cf4393334543 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 12 May 2026 15:40:00 -0400 Subject: [PATCH 739/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 3c5827f3e..3de40136c 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 3c5827f3eefb9eec7c57488b9e900d5944a9cb1b +Subproject commit 3de40136c304529de1012d84e8ef06a939d389ed From 198a7b41fad666a118f1c4f466f5d9e9064c9981 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 13 May 2026 16:23:32 -0400 Subject: [PATCH 740/758] Run town chores on last script; add TorchSystem Invoke Town.doChores() when the loader finishes the last script to clear leftover inventory (prevents false postive muling). - Also add a /// to TorchSystem.d.ts in globals.d.ts so TorchSystem types are available globally. --- d2bs/kolbot/libs/core/Loader.js | 5 +++++ d2bs/kolbot/sdk/globals.d.ts | 1 + 2 files changed, 6 insertions(+) diff --git a/d2bs/kolbot/libs/core/Loader.js b/d2bs/kolbot/libs/core/Loader.js index b5b19f1ad..2c06c8a6b 100644 --- a/d2bs/kolbot/libs/core/Loader.js +++ b/d2bs/kolbot/libs/core/Loader.js @@ -263,6 +263,11 @@ const Loader = { postAction(ctx); } } + + // run town chores on last script - prevents muling due to leftover items in inventory + if (Loader.scriptIndex === Loader.scriptList.length - 1) { + Town.doChores(); + } } } } catch (error) { diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 1cf5dbfbb..6e2314fee 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -20,6 +20,7 @@ /// /// /// +/// declare global { type IncludePath = import("./types/include-paths").IncludePath; From ed38d198641c677e7dcb81aa8ee4a4ca38d61c70 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 14 May 2026 20:43:58 -0400 Subject: [PATCH 741/758] Handle disappeared/invalid items in Pickit - Add a guard in d2bs/kolbot/libs/core/Pickit.js to detect when _item is falsy or missing a gid while attempting to pick it. When detected, log a warning, remove the entry from Pickit.pickList, and continue, preventing errors or crashes caused by items that disappear or become invalid during the pick process. --- d2bs/kolbot/libs/core/Pickit.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 619be9832..11c2e052d 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -701,6 +701,12 @@ const Pickit = { me.fieldID() && (canFit = (_item.gid !== undefined && Storage.Inventory.CanFit(_item))); } + if (!_item || _item.gid === undefined) { + console.warn("Item disappeared or became invalid while trying to pick " + itemName); + Pickit.pickList.shift(); + continue; + } + // Try to make room by selling items in town if (!canFit) { let usedSpace = Storage.Inventory.UsedSpacePercent(); From 4af04a8a354ad8da5e62fa498d635ffe60ed2311 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 15 May 2026 19:49:19 -0400 Subject: [PATCH 742/758] Mark .dbj files as JavaScript in linguist - Add .gitattributes with a linguist override so files matching *.dbj are treated as JavaScript by GitHub Linguist, ensuring correct syntax highlighting and language stats. - Based on https://github.com/orgs/community/discussions/45157#discussioncomment-16856678 --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..8fb7c07aa --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.dbj linguist-language=js \ No newline at end of file From 1cb2a152b30d3f240509730701f0b23eed0b3323 Mon Sep 17 00:00:00 2001 From: ResurrectedTrader Date: Sat, 16 May 2026 19:15:59 +0100 Subject: [PATCH 743/758] Implement handle updates in D2Bot initialization (#481) Added event listener to handle updates for D2Bot. --- d2bs/kolbot/libs/oog/D2Bot.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/d2bs/kolbot/libs/oog/D2Bot.js b/d2bs/kolbot/libs/oog/D2Bot.js index 7413be83c..4104d35f7 100644 --- a/d2bs/kolbot/libs/oog/D2Bot.js +++ b/d2bs/kolbot/libs/oog/D2Bot.js @@ -25,6 +25,13 @@ includeIfNotIncluded("oog/DataFile.js"); _entry: "", init: function () { + // Pick up handle updates, in case the instance is taken over by a new manager. + addEventListener("copydata", function (mode, msg) { + if (msg === "Handle" && typeof mode === "number") { + D2Bot.handle = mode; + } + }); + let handle = DataFile.getStats().handle; if (handle) { From d64fc9ac660ba05bdb33080f59f24d317d4a3ebc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 17 May 2026 14:24:11 -0400 Subject: [PATCH 744/758] Fix shopping at npcs that use TradeRepair menu - Add Fara, Hratli, Halbu, and Larzuk to the shop-case in Unit.prototype.startTrade so they return sdk.menu.TradeRepair when mode === 'shop'. Ensures these NPCs open the repair/shop menu correctly (d2bs/kolbot/libs/core/Prototypes.js). --- d2bs/kolbot/libs/core/Prototypes.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 2107ccfff..0bf3c983e 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -848,6 +848,10 @@ Unit.prototype.startTrade = function (mode) { case mode === "repair": return sdk.menu.TradeRepair; case mode === "shop" && String.isEqual(NPC.Charsi, unitName): + case mode === "shop" && String.isEqual(NPC.Fara, unitName): + case mode === "shop" && String.isEqual(NPC.Hratli, unitName): + case mode === "shop" && String.isEqual(NPC.Halbu, unitName): + case mode === "shop" && String.isEqual(NPC.Larzuk, unitName): return sdk.menu.TradeRepair; case mode === "shop": return sdk.menu.Trade; From 22b831a6760f9fc9fb62f12f0d89cb7f0ba7f246 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Mon, 18 May 2026 20:26:39 -0400 Subject: [PATCH 745/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 3de40136c..70a5e64c8 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 3de40136c304529de1012d84e8ef06a939d389ed +Subproject commit 70a5e64c8c15f0d3a3eb906c448a8563ea5ab645 From eef2d950b69a74975e97280b58a751428bd08d4b Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Tue, 26 May 2026 18:42:44 -0400 Subject: [PATCH 746/758] Add scrolls check to clearInventory nearest npclogic - Add logic to detect low TP/ID scroll counts and prefer visiting the Shop NPC. When scrolls are needed (TP < 13 or ID < 13 with FieldID enabled) Town choices now favor the shop and remove Repair/Gamble/Key options. After selling, if the shop UI is open the bot will buy TP/ID scrolls, purchase needed potions, and repair equipment when appropriate. Also includes a small refactor/comment repositioning around the NPC initialization retry loop. --- d2bs/kolbot/libs/core/Town.js | 47 +++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/d2bs/kolbot/libs/core/Town.js b/d2bs/kolbot/libs/core/Town.js index 3f924fcf5..8a31a6f71 100644 --- a/d2bs/kolbot/libs/core/Town.js +++ b/d2bs/kolbot/libs/core/Town.js @@ -395,6 +395,10 @@ const Town = { let npcs = Town.tasks.get(me.act); let _needPots = me.needPotions(); let _needRepair = me.needRepair().length > 0; + let _needScrolls = ( + me.checkScrolls(sdk.items.TomeofTownPortal) < 13 + || Config.FieldID.Enabled && me.checkScrolls(sdk.items.TomeofIdentify) < 13 + ); if (_needPots && _needRepair) { if (me.act === 2) { choices = new Set([npcs.Key, npcs.Repair]); @@ -405,9 +409,17 @@ const Town = { } } else if (!_needPots && _needRepair) { choices.add(npcs.Repair); - } else if (!_needPots && !_needRepair) { + } else if (!_needPots && !_needRepair && !_needScrolls) { choices = new Set([npcs.Key, npcs.Repair, npcs.Gamble, npcs.Shop]); } + + if (_needScrolls) { + choices.add(npcs.Shop); + choices.delete(npcs.Repair); + choices.delete(npcs.Gamble); + choices.delete(npcs.Key); + } + if (choices.size) { console.log("closest npc choices", choices); wantedNpc = Array.from(choices.values()).sort(function (a, b) { @@ -1847,8 +1859,8 @@ const Town = { }); // we have items to sell, might as well sell the dropable items as well if (sell.length) { - // should there be multiple attempts to interact with npc or if we fail should we move everything from the sell list to the drop list? let npc; + // should there be multiple attempts to interact with npc or if we fail should we move everything from the sell list to the drop list? for (let i = 0; i < 3 && !npc; i++) { npc = Town.initNPC("Shop", "clearInventory"); } @@ -1870,6 +1882,37 @@ const Town = { } sold && delay(250); // would a rand delay be better? }); + // quick check before closing shopui + if (Town.choresActive && getUIFlag(sdk.uiflags.Shop) && npc && typeof npc === "object") { + let _needTpScrolls = me.checkScrolls(sdk.items.TomeofTownPortal) < 13; + let _needIdScrolls = Config.FieldID.Enabled && me.checkScrolls(sdk.items.TomeofIdentify) < 13; + if (_needTpScrolls && npc.getItem(sdk.items.ScrollofTownPortal)) { + console.info(null, "Buying some tp scrolls before we leave"); + Town.fillTome(sdk.items.TomeofTownPortal); + } + if (_needIdScrolls && npc.getItem(sdk.items.ScrollofIdentify)) { + console.info(null, "Buying some id scrolls before we leave"); + Town.fillTome(sdk.items.TomeofIdentify); + } + let _needPots = me.needPotions(); + /** @param {ItemUnit} item */ + let isPot = function (item) { + return [ + sdk.items.type.HealingPotion, + sdk.items.type.ManaPotion, + sdk.items.type.RejuvPotion + ].includes(item.itemType); + }; + if (_needPots && npc.getItems().some(isPot)) { + console.info(null, "Buying some pots before we leave"); + Town.buyPotions(); + } + let _needRepair = me.needRepair(); + if (_needRepair && String.isEqual(Town.tasks.get(me.act).Repair, npc.name)) { + console.info(null, "Repairing before we leave"); + Town.repair(true); + } + } } // now lets see if we need to drop anything, so lets exit the shop me.cancelUIFlags(); From 4275a32f87d3c2b02c1d54641cb98486b66e6d5a Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 27 May 2026 02:12:34 -0400 Subject: [PATCH 747/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 70a5e64c8..06f8163b1 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 70a5e64c8c15f0d3a3eb906c448a8563ea5ab645 +Subproject commit 06f8163b116c09c4e32ce98214dcc5b9837f409b From 0a3d4620de708d35ae5e947599976db1e3d1e280 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 27 May 2026 02:34:14 -0400 Subject: [PATCH 748/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 06f8163b1..d0286d5e6 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 06f8163b116c09c4e32ce98214dcc5b9837f409b +Subproject commit d0286d5e65c4ab2c2bded2b6be79b59f1b34cbc0 From 183fd3b3a9eac26ef2d6f2a1f940d28ad2385fb8 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 28 May 2026 16:06:52 -0400 Subject: [PATCH 749/758] Add AutoRush system and RushConfig support - Introduce the AutoRush system: add D2BotAutoRush.dbj entry script and new autorush config (systems/autorush/RushConfig.js) with setup installer entry. - Update .gitignore and setup script to handle the new config. Refactor rushee logic into a Runnable with updated overrides (Rushee.js), load RushConfig automatically from core Config when the auto-rush script is present, and add RushConstants. Misc fixes and improvements: handle CharSelect connecting retry and randomized delays in OOG.js, add Pather.init(force) to allow forced initialization, add Pickit.enabled gating and guard fastPick/pickItems, and replace per-class auto-rush comments to point to the new central RushConfig. These changes integrate the new autorush workflow and stabilize login/creation/join behavior. --- +setup/autorush/RushConfig.js | 135 ++ +setup/setup.ps1 | 1 + .gitignore | 1 + d2bs/kolbot/D2BotAutoRush.dbj | 578 +++++ d2bs/kolbot/libs/OOG.js | 19 +- d2bs/kolbot/libs/config/Amazon.js | 17 +- d2bs/kolbot/libs/config/Assassin.js | 17 +- d2bs/kolbot/libs/config/Barbarian.js | 17 +- d2bs/kolbot/libs/config/Druid.js | 17 +- d2bs/kolbot/libs/config/Necromancer.js | 17 +- d2bs/kolbot/libs/config/Paladin.js | 17 +- d2bs/kolbot/libs/config/Sorceress.js | 17 +- d2bs/kolbot/libs/config/_BaseConfigFile.js | 17 +- d2bs/kolbot/libs/core/Config.js | 6 + d2bs/kolbot/libs/core/Pather.js | 4 +- d2bs/kolbot/libs/core/Pickit.js | 9 +- d2bs/kolbot/libs/scripts/Rushee.js | 1906 +++++++++-------- d2bs/kolbot/libs/scripts/Rusher.js | 428 ++-- d2bs/kolbot/libs/systems/autorush/AutoRush.js | 89 +- .../libs/systems/autorush/RushConfig.js | 275 ++- .../libs/systems/autorush/RushConstants.js | 328 +++ d2bs/kolbot/libs/systems/autorush/index.d.ts | 15 +- d2bs/kolbot/sdk/types/Pickit.d.ts | 1 + d2bs/kolbot/threads/RushThread.js | 1271 ++--------- 24 files changed, 2759 insertions(+), 2443 deletions(-) create mode 100644 +setup/autorush/RushConfig.js create mode 100644 d2bs/kolbot/D2BotAutoRush.dbj create mode 100644 d2bs/kolbot/libs/systems/autorush/RushConstants.js diff --git a/+setup/autorush/RushConfig.js b/+setup/autorush/RushConfig.js new file mode 100644 index 000000000..e44544ea5 --- /dev/null +++ b/+setup/autorush/RushConfig.js @@ -0,0 +1,135 @@ +/** +* @filename RushConfig.js +* @author theBGuy +* @desc Configuration file for AutoRush system +* +*/ + +(function (module) { + // no touchy - need these imports + const { RushModes } = require("./RushConstants"); + + /** + * This is what you edit. + * + * Each key is a profile name. + * The value shape depends on the selected mode (`type`). + * + * Rushee modes: + * - `RushModes.quester` + * - `RushModes.follower` + * - `RushModes.bumper` + * + * Rusher modes: + * - `RushModes.rusher` + * - `RushModes.manual` + * + * `create` usage variants: + * - Account + character creation: provide `account`, `password`, `charName`, and `charInfo`. + * - Character-only creation: provide only `charName` and `charInfo`. You can skip charName for automatic name generation (uses soloplay's NameGen if available, otherwise will error). + * + * Rusher config defaults: + * - Quest toggles default to `true`. + * - `Wps` defaults to `false`. + * - You do not need to define every `config` property when you want default behavior. + * + * @example + * // Quester with account + character creation + * "quester-profile": { + * type: RushModes.quester, + * startProfiles: ["bumper-profile", "follower-profile", "rusher-profile"], + * create: { + * account: "myacc", + * password: "mypassword", + * charName: "quester", + * charInfo: "scl-sorc", + * }, + * config: { + * Leader: "my-rusher-char", + * }, + * } + * + * @example + * // Follower with character-only creation + * "follower-profile": { + * type: RushModes.follower, + * leader: "quester-profile", + * create: { + * charName: "follower", + * charInfo: "scl-zon", + * }, + * config: { + * Leader: "my-rusher-char", + * }, + * } + * + * @example + * // Bumper with minimal config + * "bumper-profile": { + * type: RushModes.bumper, + * leader: "quester-profile", + * config: { + * Leader: "my-rusher-char", + * }, + * } + * + * @example + * // Rusher with minimal config (uses defaults) + * "rusher-profile": { + * type: RushModes.rusher, + * leader: "quester-profile", + * config: { + * Wps: true, + * LastRun: "baal", + * }, + * } + * + * @example + * // Rusher with explicit full config + * "rusher-profile-full": { + * type: RushModes.rusher, + * leader: "quester-profile", + * playerWaitTimeout: Time.minutes(1), + * config: { + * WaitPlayerCount: 1, + * Cain: true, + * Radament: true, + * LamEsen: true, + * Izual: true, + * Shenk: true, + * Anya: true, + * Ancients: { + * Normal: true, + * Nightmare: true, + * Hell: false, + * }, + * Wps: false, + * LastRun: "", + * }, + * } + * + * @example + * // Manual mode (same config shape as rusher) + * "manual-profile": { + * type: RushModes.manual, + * leader: "quester-profile", + * config: { + * Wps: true, + * }, + * } + * + * @type {Object.} + */ + const RushConfig = { + "quester-profile": { + type: RushModes.quester, + config: { + Leader: "my-rusher-char", + }, + } + }; + + module.exports = { + RushConfig: RushConfig, + }; +})(module); diff --git a/+setup/setup.ps1 b/+setup/setup.ps1 index 72109e971..f91c2a8eb 100644 --- a/+setup/setup.ps1 +++ b/+setup/setup.ps1 @@ -116,6 +116,7 @@ Copy-IfNotExists "$SETUP_DIR\mulelogger\LoggerConfig.js" "$SYSTEMS_DIR\mulelogge Copy-IfNotExists "$SETUP_DIR\pubjoin\PubJoinConfig.js" "$SYSTEMS_DIR\pubjoin\PubJoinConfig.js" "PubJoinConfig.js to pubjoin directory" Copy-IfNotExists "$SETUP_DIR\torch\FarmerConfig.js" "$SYSTEMS_DIR\torch\FarmerConfig.js" "FarmerConfig.js to torch directory" Copy-IfNotExists "$SETUP_DIR\charrefresher\RefresherConfig.js" "$SYSTEMS_DIR\charrefresher\RefresherConfig.js" "RefresherConfig.js to charrefresher directory" +Copy-IfNotExists "$SETUP_DIR\autorush\RushConfig.js" "$SYSTEMS_DIR\autorush\RushConfig.js" "RushConfig.js to autorush directory" Write-Host "System configuration files processed successfully!" Write-Host "" diff --git a/.gitignore b/.gitignore index 903db5762..3b1d354f8 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ d2bs/kolbot/libs/systems/automule/config/StarterConfig.js d2bs/kolbot/libs/systems/charrefresher/RefresherConfig.js d2bs/kolbot/libs/systems/lead/LeadConfig.js /d2bs/kolbot/libs/core/Overrides +d2bs/kolbot/libs/systems/autorush/RushConfig.js diff --git a/d2bs/kolbot/D2BotAutoRush.dbj b/d2bs/kolbot/D2BotAutoRush.dbj new file mode 100644 index 000000000..80d4128bd --- /dev/null +++ b/d2bs/kolbot/D2BotAutoRush.dbj @@ -0,0 +1,578 @@ +/* eslint-disable no-fallthrough */ +/** +* @filename D2BotAutoRush.dbj +* @author theBGuy +* @desc Entry script for AutoRush system +* +* @typedef {import("./sdk/globals")} +* @typedef {import("./libs/systems/autorush/index")} +*/ + +include("critical.js"); // required +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + +// the only things we really need from these are their oog checks +includeSystemLibs(); + + +if (typeof Starter.AdvancedConfig[me.profile] === "object") { + Object.assign(Starter.Config, Starter.AdvancedConfig[me.profile]); +} +delete Starter.AdvancedConfig; + +if (DataFile.init()) { + Starter.firstRun = true; +} + +// TODO: need to handle acc creation, char creation somewhat works but will fail if name is in use + +const { + RushConfig, +} = require("./libs/systems/autorush/RushConfig"); +const { RushModes, normalizeRushProfileConfig } = require("./libs/systems/autorush/RushConstants"); +const RushProfile = RushConfig[me.profile]; + +const locationAction = (function () { + const Controls = require("./libs/modules/Control"); + const { + locations, + addLocations, + } = require("./libs/oog/Locations"); + // const Overrides = require("./libs/modules/Override"); + // const madeChars = []; + // new Overrides.Override(ControlAction, ControlAction.makeCharacter, function (orignal, ...args) { + // /** @type {ControlAction.CharacterInfo} */ + // let _charInfo = args[0]; + // if (String.isEqual(_charInfo.charName, me.name)) { + // console.debug("Need a new name"); + // } + + // return orignal.apply(this, args); + // }).apply(); + + Starter.LocationEvents.login = function () { + Starter.inGame && (Starter.inGame = false); + const pType = Profile().type; + /** @type {Map} */ + const classMap = new Map([ + ["ZON", "amazon"], + ["SOR", "sorceress"], + ["NEC", "necromancer"], + ["PAL", "paladin"], + ["BAR", "barbarian"], + ["DRU", "druid"], + ["SIN", "assassin"], + ]); + /** + * @param {RusheeConfig} rushProfile + * @returns {Partial} + */ + const getCharInfo = function (rushProfile) { + let charInfo = rushProfile.create.charInfo; + let [modePrefix, charClass] = charInfo.toUpperCase().split("-"); + if (!rushProfile.create.charName) { + // we can use soloplay's namegen if available + if (FileTools.exists("libs/SoloPlay/Modules/NameGen.js")) { + const NameGen = require("./libs/SoloPlay/Modules/NameGen"); + rushProfile.create.charName = NameGen(); + Starter.profileInfo.charName = rushProfile.create.charName; + D2Bot.setProfile(null, null, rushProfile.create.charName); + D2Bot.printToConsole("Generated character name: " + rushProfile.create.charName, sdk.colors.D2Bot.Green); + } else { + throw new Error("No character name provided and NameGen module not found"); + } + } + /** @type {Partial} */ + let info = { + charName: rushProfile.create.charName, + charClass: classMap.get(charClass.slice(0, 3)) || "sorceress", + hardcore: modePrefix.includes("HC"), + expansion: modePrefix.indexOf("CC") === -1, + ladder: modePrefix.indexOf("NL") === -1, + }; + console.debug(info); + return info; + }; + + 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 + if (Starter.firstLogin && getLocation() === sdk.game.locations.Login) { + Controls.BottomLeftExit.click(); + } + + D2Bot.updateStatus("Logging In"); + + try { + // make battlenet accounts/characters + D2Bot.updateStatus("Logging in"); + console.debug("ProfileInfo: ", Starter.profileInfo); + // need to make an account if we can + if (Starter.profileInfo.account === "" && pType === sdk.game.profiletype.Battlenet) { + if (RushProfile.type === RushModes.rusher) { + D2Bot.printToConsole("Cannot make accounts for rusher", sdk.colors.D2Bot.Red); + D2Bot.stop(me.profile, true); + return; + } + if (RushProfile.create.account && RushProfile.create.password) { + let { account, password, charInfo } = RushProfile.create; + // let madeAcc = false; + if (ControlAction.makeAccount(RushProfile.create)) { + console.log("Created account " + RushProfile.create.account); + D2Bot.setProfile(account, password); + // madeAcc = true; + } else if (ControlAction.loginAccount(RushProfile.create)) { + console.log("Logged into account " + RushProfile.create.account); + } + if (Starter.profileInfo.charName === "") { + D2Bot.updateStatus("Char Select - Looking for character"); + // if (!madeAcc) { + // let charInfo = DataFile.getStats().charInfo; + // if (charInfo && charInfo.charName) { + // console.log("Found Existing character " + charName); + // Starter.profileInfo.charName = charName; + // if (ControlAction.loginCharacter(Starter.profileInfo)) { + // console.debug("Logged into character " + Starter.profileInfo.charName); + // return; + // } + // } + // } + // we are going to need to make a character too + if (charInfo) { + let info = getCharInfo(); + + if (!ControlAction.findCharacter(info)) { + if (ControlAction.makeCharacter(info, true)) { + console.log("Created character " + me.charname); + info.charName = me.charname; + DataFile.updateStats("charInfo", info); + D2Bot.setProfile(account, password, me.charname); + + return; + } + } + } + } + } else { + D2Bot.printToConsole("No account information found", sdk.colors.D2Bot.Red); + D2Bot.stop(me.profile, true); + } + } else { + // existing account + try { + login(me.profile); + // need a better solution for this + if (getLocation() === sdk.game.locations.Lobby && RushProfile.type !== RushModes.rusher) { + if (RushProfile.create.charName && me.charname !== RushProfile.create.charName) { + Starter.profileInfo.charName = RushProfile.create.charName; + console.debug("Logged into wrong character ", Starter.profileInfo); + ControlAction.timeoutDelay("Character Change", 5000); + Controls.LobbyQuit.click(); + throw new Error("Character name mismatch"); + } + } + } catch (e) { + while (!getLocation()) { + delay(1000); + } + switch (getLocation()) { + case sdk.game.locations.CharSelect: + case sdk.game.locations.CharSelectNoChars: + if (Starter.loginRetry < 2) { + Starter.loginRetry++; + // let charInfo = DataFile.getStats().charInfo; + // if (charInfo) { + // try { + // charInfo = JSON.parse(charInfo); + // if (charInfo.charName) { + // console.log("Found Existing character " + charInfo.charName); + // console.debug(charInfo.charName); + // if (ControlAction.loginCharacter(charInfo)) { + // console.debug("Logged into character " + me.charname); + // return; + // } + // } + // } catch (e) { + // console.error(e); + // } + // } + if (!ControlAction.findCharacter(Starter.profileInfo)) { + // dead hardcore character on sp + if (getLocation() === sdk.game.locations.OkCenteredErrorPopUp) { + // Exit from that pop-up + Controls.OkCentered.click(); + D2Bot.printToConsole("Character died", sdk.colors.D2Bot.Red); + D2Bot.stop(); + } else if (RushProfile.type !== RushModes.rusher && RushProfile.create.charInfo) { + let info = getCharInfo(RushProfile); + console.debug(info); + if (!ControlAction.findCharacter(info)) { + if (ControlAction.makeCharacter(info, true)) { + console.log("Created character " + me.charname); + info.charName = me.charname; + DataFile.updateStats("charInfo", info); + D2Bot.setProfile(null, null, info.charName); + + return; + } + } else { + ControlAction.loginCharacter(info); + } + } else { + Starter.loginRetry++; + } + } else { + ControlAction.loginCharacter(Starter.profileInfo); + // login(me.profile); + } + break; + } + } + } + } + } catch (e) { + console.log(e + " " + getLocation()); + } + }; + + if (RushProfile.type === RushModes.quester) { + locations.set(sdk.game.locations.CreateGame, function (location) { + 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(); + } + + if (typeof Starter.Config.MaxPlayerCount === "number") { + Controls.MaxPlayerCount.setText(Starter.Config.MaxPlayerCount.toString()); + } + + // Get game name if there is none + while (!Starter.gameInfo.gameName) { + D2Bot.requestGameInfo(); + delay(500); + } + + // 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(); + } + + const gameName = (Starter.gameInfo.gameName === "?" + ? Starter.randomString(null, true) + : Starter.gameInfo.gameName + Starter.gameCount); + const gamePass = (Starter.gameInfo.gamePass === "?" + ? Starter.randomString(null, true) + : Starter.gameInfo.gamePass); + + ControlAction.createGame( + gameName, + gamePass, + "Highest", + Starter.Config.CreateGameDelay * 1000 + ); + + Starter.lastGameStatus = "pending"; + Starter.setNextGame(Starter.gameInfo); + Starter.locationTimeout(10000, location); + }); + } + + if (([ + RushModes.follower, + RushModes.bumper, + RushModes.rusher, + ].includes(RushProfile.type))) { + // Setup follower locations + let lastGameTick; + const lastGame = []; + /** @param {string} leader */ + const joinCheck = function (leader) { + D2Bot.requestGame(leader); + delay(500); + + if (!Starter.joinInfo.inGame || (lastGame.length && lastGame.indexOf(Starter.joinInfo.gameName) === -1)) { + D2Bot.printToConsole("Game is finished. Stopping join delay."); + Starter.gameInfo.gameName = ""; + Starter.gameInfo.gamePass = ""; + + return true; + } + + return false; + }; + addLocations([sdk.game.locations.WaitingInLine, sdk.game.locations.CreateGame], + function () { + Controls.CancelCreateGame.click(); + Controls.JoinGameWindow.click(); + } + ); + locations.set(sdk.game.locations.LobbyChat, + function () { + D2Bot.updateStatus("Lobby Chat"); + + if (Starter.inGame) { + if (AutoMule.outOfGameCheck() + || TorchSystem.outOfGameCheck() + || Gambling.outOfGameCheck() + || CraftingSystem.outOfGameCheck()) { + return; + } + + console.log("updating runs"); + D2Bot.updateRuns(); + + lastGameTick = getTickCount(); + Starter.gameCount += 1; + Starter.lastGameStatus = "ready"; + Starter.inGame = false; + } + + if (!Starter.chatActionsDone) { + Starter.chatActionsDone = true; + + ControlAction.timeoutDelay("Chat delay", Starter.Config.ChatActionsDelay * 1e3); + say("/j " + Starter.Config.JoinChannel); + delay(1000); + + if (Starter.Config.FirstJoinMessage !== "") { + say(Starter.Config.FirstJoinMessage); + delay(500); + } + // need to handle channel join messages + } + + Starter.LocationEvents.openJoinGameWindow(); + } + ); + locations.set(sdk.game.locations.JoinGame, + function (location) { + D2Bot.updateStatus("Join Game"); + Starter.joinInfo = {}; + D2Bot.requestGame(RushProfile.leader); + delay(100); + + if (!Starter.joinInfo.hasOwnProperty("gameName") || Starter.joinInfo.gameName === "") { + delay(500); + return; + } + + if (lastGame.indexOf(Starter.joinInfo.gameName) === -1 || Starter.lastGameStatus === "pending") { + Controls.JoinGameName.setText(Starter.joinInfo.gameName); + Controls.JoinGamePass.setText(Starter.joinInfo.gamePass); + + if (Starter.lastGameStatus === "pending" + || (Starter.gameInfo.error && DataFile.getStats().gameName === Starter.joinInfo.gameName)) { + D2Bot.printToConsole("Failed to join game"); + ControlAction.timeoutDelay( + "Join Delay", + Starter.Config.JoinRetryDelay * 1000, joinCheck(RushProfile.leader) + ); + D2Bot.updateRuns(); + D2Bot.requestGame(RushProfile.leader); + delay(200); + + if (!Starter.joinInfo.inGame) { + Starter.lastGameStatus = "ready"; + + return; + } + } + + if (!Starter.joinInfo.inGame) { + if (Starter.joinInfo.delay) { + ControlAction.timeoutDelay("Leader Delay", Starter.joinInfo.delay); + } + return; + } + + // Don't join immediately after previous game to avoid FTJ + if (getTickCount() - lastGameTick < 5000) { + ControlAction.timeoutDelay("Game Delay", (lastGameTick - getTickCount() + 5000)); + } + + console.log("joining game " + Starter.joinInfo.gameName); + + if (typeof Starter.Config.JoinDelay === "number") { + ControlAction.timeoutDelay("Custom Join Delay", Starter.Config.JoinDelay * 1e3); + } + + me.blockMouse = true; + + DataFile.updateStats("gameName", Starter.joinInfo.gameName); + Controls.JoinGame.click(); + + me.blockMouse = false; + + lastGame.push(Starter.joinInfo.gameName); + + Starter.lastGameStatus = "pending"; + Starter.locationTimeout(15000, location); + + return; + } else { + // for now, if leader is in game and it's the last game we were in. delay to prevent copyData spam + if (lastGame.includes(Starter.joinInfo.gameName)) { + delay((Starter.joinInfo.inGame ? 5000 : 2000)); + } + } + } + ); + locations.set(sdk.game.locations.TcpIpEnterIp, + function () { + try { + for (let i = 0; i < 3; i++) { + D2Bot.requestGame(RushProfile.leader); + delay(1000); + + if (Object.keys(Starter.joinInfo).length && Starter.joinInfo.gameName !== "") { + break; + } + } + + if (Controls.IPAdress.setText(Object.keys(Starter.joinInfo).length ? Starter.joinInfo.gameName : "localhost") + && Controls.IPAdressOk.click() + && Starter.locationTimeout(2e3, sdk.game.locations.TcpIpEnterIp)) { + getLocation() === sdk.game.locations.CharSelect && login(me.profile); + } + } catch (e) { + console.error(e); + } + } + ); + } + + return { + /** @param {number} loc */ + run: function (loc) { + try { + let func = locations.get(loc); + if (typeof func === "function") { + func(loc); + } else if (loc !== undefined && loc !== null) { + console.log("Unhandled location: " + loc); + } + } catch (e) { + console.error(e); + } + }, + }; +})(); + +function main () { + debugLog(me.profile); + addEventListener("copydata", Starter.receiveCopyData); + addEventListener("scriptmsg", Starter.scriptMsgEvent); + + while (!Starter.handle) { + delay(100); + } + + DataFile.updateStats("handle", Starter.handle); + delay(500); + D2Bot.init(); + load("threads/heartbeat.js"); + + while (!Object.keys(Starter.gameInfo).length) { + D2Bot.requestGameInfo(); + delay(500); + } + + while (!Object.keys(Starter.profileInfo).length) { + D2Bot.getProfile(); + console.log("Getting Profile"); + delay(500); + } + console.debug(Starter.profileInfo); + + Starter.gameCount = (DataFile.getStats().runs + 1 || 1); + + if (Starter.gameInfo.error) { + delay(200); + + if (!!DataFile.getStats().debugInfo) { + Starter.gameInfo.crashInfo = DataFile.getStats().debugInfo; + D2Bot.printToConsole( + "Crash Info: Script: " + JSON.parse(Starter.gameInfo.crashInfo).currScript + + " Area: " + JSON.parse(Starter.gameInfo.crashInfo).area, + sdk.colors.D2Bot.Gray + ); + } + + ControlAction.timeoutDelay("Crash Delay", Starter.Config.CrashDelay * 1e3); + D2Bot.updateRuns(); + } + + DataFile.updateStats("debugInfo", JSON.stringify({ currScript: "none", area: "out of game" })); + + if (!RushProfile) { + D2Bot.printToConsole("No Rush Config found for " + me.profile, sdk.colors.D2Bot.Red); + D2Bot.stop(me.profile, true); + } + + normalizeRushProfileConfig(RushProfile); + + // now that we are all setup, check if we need to start other profiles + if (RushProfile.startProfiles.length) { + for (let profile of RushProfile.startProfiles) { + if (profile !== me.profile) { + D2Bot.start(profile); + } + } + } + + 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"); + } + + D2Bot.updateStatus( + me.charname + " (" + me.charlvl + ") | Game: " + (me.gamename || "singleplayer") + + Starter.timer(Starter.gameStart) + ); + } + + delay(1000); + } + + Starter.isUp = "no"; + + locationAction.run(getLocation()); + delay(1000); + } +} diff --git a/d2bs/kolbot/libs/OOG.js b/d2bs/kolbot/libs/OOG.js index cf4565205..5dbfff031 100644 --- a/d2bs/kolbot/libs/OOG.js +++ b/d2bs/kolbot/libs/OOG.js @@ -272,9 +272,11 @@ includeIfNotIncluded("core/Me.js"); } if (getLocation() === sdk.game.locations.CharSelectConnecting) { - if (!Starter.LocationEvents.charSelectConnecting()) { + if (Starter.charSelectConnectingRetry > 3 || !Starter.LocationEvents.charSelectConnecting()) { D2Bot.printToConsole("Stuck at connecting screen"); D2Bot.restart(); + } else { + Starter.charSelectConnectingRetry++; } } @@ -665,7 +667,8 @@ includeIfNotIncluded("core/Me.js"); makeAccount: function (info) { me.blockMouse = true; - let openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; + const openBnet = Profile().type === sdk.game.profiletype.OpenBattlenet; + const delays = [100, 150, 200, 250, 300, 350, 400, 450, 500]; // cycle until in empty char screen MainLoop: @@ -719,6 +722,15 @@ includeIfNotIncluded("core/Me.js"); } break; + case sdk.game.locations.CharSelectConnecting: + // if (Starter.charSelectConnectingRetry > 3 || !Starter.LocationEvents.charSelectConnecting()) { + // D2Bot.printToConsole("Stuck at connecting screen"); + // D2Bot.restart(); + // } else { + // Starter.charSelectConnectingRetry++; + // } + // bugged but account was technically created, so just break to char select and let caller handle it + break MainLoop; case sdk.game.locations.LoginError: Controls.LoginErrorOk.click(); @@ -727,7 +739,7 @@ includeIfNotIncluded("core/Me.js"); break; } - delay(100); + delay(delays.random()); } me.blockMouse = false; @@ -1070,6 +1082,7 @@ includeIfNotIncluded("core/Me.js"); profileInfo: {}, /** @type {number[]} */ lastLocation: [], + charSelectConnectingRetry: 0, sayMsg: function (string) { if (!this.useChat) return; diff --git a/d2bs/kolbot/libs/config/Amazon.js b/d2bs/kolbot/libs/config/Amazon.js index 5116b1cd8..e2d49c3c2 100644 --- a/d2bs/kolbot/libs/config/Amazon.js +++ b/d2bs/kolbot/libs/config/Amazon.js @@ -307,22 +307,7 @@ function LoadConfig () { Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/config/Assassin.js b/d2bs/kolbot/libs/config/Assassin.js index 2934a61c7..3e576b2d9 100644 --- a/d2bs/kolbot/libs/config/Assassin.js +++ b/d2bs/kolbot/libs/config/Assassin.js @@ -307,22 +307,7 @@ function LoadConfig () { Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/config/Barbarian.js b/d2bs/kolbot/libs/config/Barbarian.js index c34503b37..230b3c02a 100644 --- a/d2bs/kolbot/libs/config/Barbarian.js +++ b/d2bs/kolbot/libs/config/Barbarian.js @@ -307,22 +307,7 @@ function LoadConfig () { Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/config/Druid.js b/d2bs/kolbot/libs/config/Druid.js index c962bc41a..fc66978f4 100644 --- a/d2bs/kolbot/libs/config/Druid.js +++ b/d2bs/kolbot/libs/config/Druid.js @@ -307,22 +307,7 @@ function LoadConfig () { Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/config/Necromancer.js b/d2bs/kolbot/libs/config/Necromancer.js index 6ed00e81d..855458bea 100644 --- a/d2bs/kolbot/libs/config/Necromancer.js +++ b/d2bs/kolbot/libs/config/Necromancer.js @@ -308,22 +308,7 @@ function LoadConfig () { Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/config/Paladin.js b/d2bs/kolbot/libs/config/Paladin.js index f1ec0a142..2619406e7 100644 --- a/d2bs/kolbot/libs/config/Paladin.js +++ b/d2bs/kolbot/libs/config/Paladin.js @@ -307,22 +307,7 @@ function LoadConfig () { Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/config/Sorceress.js b/d2bs/kolbot/libs/config/Sorceress.js index d6e4b081d..8ebc82285 100644 --- a/d2bs/kolbot/libs/config/Sorceress.js +++ b/d2bs/kolbot/libs/config/Sorceress.js @@ -307,22 +307,7 @@ function LoadConfig () { Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/config/_BaseConfigFile.js b/d2bs/kolbot/libs/config/_BaseConfigFile.js index f2d1f68a4..3844e1264 100644 --- a/d2bs/kolbot/libs/config/_BaseConfigFile.js +++ b/d2bs/kolbot/libs/config/_BaseConfigFile.js @@ -289,22 +289,7 @@ Config.OrgTorchHelper.GetFade = false; // Get fade by standing in a fire. You MUST have Last Wish, Treachery, or SpiritWard on your character being worn. // ##### AUTO-RUSH ##### // - // RUSHER USES FOLLOWER ENTRY SCRIPT - Scripts.Rusher = false; // Rush bot. For a list of commands, see Rusher.js - Config.Rusher.WaitPlayerCount = 0; // Wait until game has a certain number of players (0 - don't wait, 8 - wait for full game). - Config.Rusher.Cain = false; // Do cain quest. - Config.Rusher.Radament = false; // Do Radament quest. - Config.Rusher.LamEsen = false; // Do Lam Esen quest. - Config.Rusher.Izual = false; // Do Izual quest. - Config.Rusher.Shenk = false; // Do Shenk quest. - Config.Rusher.Anya = false; // Do Anya quest. - Config.Rusher.HellAncients = false; // Does Ancient's quest in hell (only if quester is level 60+) - Config.Rusher.GiveWps = false; // Give all Wps - Config.Rusher.LastRun = ""; // End rush after this run. - // RUSHEE USES LEADER ENTRY SCRIPT - Scripts.Rushee = false; // Automatic rushee, works with Rusher. Set Rusher's character name as Config.Leader - Config.Rushee.Quester = false; // Enter portals and get quest items. - Config.Rushee.Bumper = false; // Do Ancients and Baal. Minimum levels: 20 - norm, 40 - nightmare + // Setup now uses D2BotAutoRush.dbj, and config is in systems/autorush/RushConfig.js // ##### MANUAL RUSH ##### // Scripts.CrushTele = false; // classic rush teleporter. go to area of interest and press "-" numpad key diff --git a/d2bs/kolbot/libs/core/Config.js b/d2bs/kolbot/libs/core/Config.js index b7a0fe468..31141d592 100644 --- a/d2bs/kolbot/libs/core/Config.js +++ b/d2bs/kolbot/libs/core/Config.js @@ -93,6 +93,12 @@ let Config = { } } + if (getScript("D2BotAutoRush.dbj")) { + const { RushConfig } = require("../systems/autorush/RushConfig"); + const { rushConfigInit } = require("../systems/autorush/RushConstants"); + rushConfigInit(RushConfig[me.profile]); + } + // Always set the orginal say function global._say = global.say; if (Config.Silence && !Config.LocalChat.Enabled) { diff --git a/d2bs/kolbot/libs/core/Pather.js b/d2bs/kolbot/libs/core/Pather.js index 95165b974..a310fb423 100644 --- a/d2bs/kolbot/libs/core/Pather.js +++ b/d2bs/kolbot/libs/core/Pather.js @@ -265,7 +265,7 @@ const Pather = { Pather.initialized = true; }, - init: function () { + init: function (force) { if (!this.initialized) { addEventListener("scriptmsg", Pather.cacheListener); me.classic && (Pather.nonTownWpAreas = this.nonTownWpAreas.filter((wp) => wp < sdk.areas.Harrogath)); @@ -273,7 +273,7 @@ const Pather = { scriptBroadcast("get-cached-waypoints"); delay(500); - if (!Config.WaypointMenu && !Pather.initialized) { + if ((!Config.WaypointMenu && !Pather.initialized) || force) { !getWaypoint(1) && this.getWP(me.area); me.cancelUIFlags(); Pather.initialized = true; diff --git a/d2bs/kolbot/libs/core/Pickit.js b/d2bs/kolbot/libs/core/Pickit.js index 11c2e052d..656516a21 100644 --- a/d2bs/kolbot/libs/core/Pickit.js +++ b/d2bs/kolbot/libs/core/Pickit.js @@ -9,6 +9,7 @@ * @namespace Pickit */ const Pickit = { + enabled: true, gidList: new Set(), invoLocked: true, beltSize: 1, @@ -614,7 +615,7 @@ const Pickit = { * @returns {boolean} If we picked items */ pickItems: function (range = Config.PickRange) { - if (me.dead) return false; + if (me.dead || !Pickit.enabled) return false; let needMule = false; const canUseMule = AutoMule.getInfo() && AutoMule.getInfo().hasOwnProperty("muleInfo"); @@ -780,14 +781,14 @@ const Pickit = { * @param {number} retry */ fastPick: function (retry = 3) { - let item; + if (me.dead || !Pickit.enabled) return false; const _removeList = []; const itemList = []; const range = Config.FastPickRange || Config.PickRange; for (let gid of this.gidList) { _removeList.push(gid); - item = Game.getItem(-1, -1, gid); + let item = Game.getItem(-1, -1, gid); if (item && item.onGroundOrDropping && ( @@ -812,7 +813,7 @@ const Pickit = { itemList.sort(this.sortFastPickItems); let check = itemList.shift(); // we were passed the copied unit, lets find the real thing - item = Game.getItem(check.classid, -1, check.gid); + let item = Game.getItem(check.classid, -1, check.gid); // Check if the item unit is still valid if (item && item.x !== undefined) { diff --git a/d2bs/kolbot/libs/scripts/Rushee.js b/d2bs/kolbot/libs/scripts/Rushee.js index 64803bd14..1e3dd4b4b 100644 --- a/d2bs/kolbot/libs/scripts/Rushee.js +++ b/d2bs/kolbot/libs/scripts/Rushee.js @@ -6,976 +6,932 @@ * */ -let Overrides = require("../modules/Override"); -new Overrides.Override(Town, Town.goToTown, function(orignal, act, wpmenu) { - try { - orignal(act, wpmenu); - - return true; - } catch (e) { - console.log(e); - - return Pather.useWaypoint(sdk.areas.townOf(me.area)); - } -}).apply(); +const Rushee = new Runnable( + function Rushee () { + const Overrides = require("../modules/Override"); + const { log, getBumperLvlReq } = require("../systems/autorush/AutoRush"); + const { + RushConfig + } = require("../systems/autorush/RushConfig"); + const { sequenceCheck, RushModes, AutoRush } = require("../systems/autorush/RushConstants"); + const rushConfig = RushConfig[me.profile]; + + if (!rushConfig) { + throw new Error("No rush config found for profile: " + me.profile); + } -new Overrides.Override(Pather, Pather.getWP, function(orignal, area, clearPath) { - if (area !== me.area) return false; + function applyOverrides() { + new Overrides.Override(Town, Town.goToTown, function (orignal, act, wpmenu) { + try { + return orignal(act, wpmenu); + } catch (e) { + console.error(e); + + return Pather.useWaypoint(sdk.areas.townOf(me.area)); + } + }).apply(); - for (let i = 0; i < sdk.waypoints.Ids.length; i++) { - let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + new Overrides.Override(Pather, Pather.getWP, function (orignal, area, clearPath) { + if (area !== me.area) return false; - if (preset) { - let x = (preset.roomx * 5 + preset.x); - let y = (preset.roomy * 5 + preset.y); - if (!me.inTown && [x, y].distance > 15) return false; + for (let i = 0; i < sdk.waypoints.Ids.length; i++) { + let preset = Game.getPresetObject(me.area, sdk.waypoints.Ids[i]); + if (!preset) continue; + + let coords = preset.realCoords(); + if (!me.inTown && coords.distance > 15) { + return false; + } - Skill.haveTK - ? this.moveNearUnit(preset, 20, { clearSettings: { clearPath: clearPath } }) - : this.moveToUnit(preset, 0, 0, clearPath); + Skill.haveTK + ? this.moveNearUnit(coords, 20, { clearSettings: { clearPath: clearPath } }) + : this.moveToUnit(coords, 0, 0, clearPath); - let wp = Game.getObject("waypoint"); + let wp = Game.getObject("waypoint"); + if (!wp) continue; - if (wp) { - for (let j = 0; j < 10; j++) { - if (!getUIFlag(sdk.uiflags.Waypoint)) { - if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { - wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); - Packet.telekinesis(wp); - } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { - this.moveToUnit(wp) && Misc.click(0, 0, wp); + for (let j = 0; j < 10; j++) { + if (!getUIFlag(sdk.uiflags.Waypoint)) { + if (wp.distance > 5 && Skill.useTK(wp) && j < 3) { + wp.distance > 21 && Attack.getIntoPosition(wp, 20, sdk.collision.Ranged); + Packet.telekinesis(wp); + } else if (wp.distance > 5 || !getUIFlag(sdk.uiflags.Waypoint)) { + this.moveToUnit(wp) && Misc.click(0, 0, wp); + } } - } - if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { - delay(500); - me.cancelUIFlags(); + if (Misc.poll(() => me.gameReady && getUIFlag(sdk.uiflags.Waypoint), 1000, 150)) { + delay(500); + me.cancelUIFlags(); - return true; - } + return true; + } - // handle getUnit bug - if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { - Town.getDistance("waypoint") > 5 && Town.move("waypoint"); - Misc.click(0, 0, wp); - } + // handle getUnit bug + if (!getUIFlag(sdk.uiflags.Waypoint) && me.inTown && wp.name.toLowerCase() === "dummy") { + Town.getDistance("waypoint") > 5 && Town.move("waypoint"); + Misc.click(0, 0, wp); + } - delay(500); + delay(500); + } } - } - } - } - - return false; -}).apply(); - -function Rushee() { - let act, leader, target, done = false; - let actions = []; - - this.log = function (msg = "", sayMsg = false) { - console.log(msg); - sayMsg && say(msg); - }; - - this.useScrollOfRes = function () { - let scroll = me.scrollofresistance; - if (scroll) { - clickItem(sdk.clicktypes.click.item.Right, scroll); - console.log("Using scroll of resistance"); - } - }; - this.revive = function () { - while (me.mode === sdk.player.mode.Death) { - delay(40); + return false; + }).apply(); } - if (me.mode === sdk.player.mode.Dead) { - me.revive(); - - while (!me.inTown) { - delay(40); + /** + * @param {string} who + * @param {string} msg + */ + function chatEvent (who, msg) { + if (msg === "i am rusher") { + Config.Leader = who; + console.debug("Assigned Leader: " + Config.Leader); } - } - }; - - // todo - map the chest to classid so we only need to pass in one value - this.getQuestItem = function (classid, chestid) { - let tick = getTickCount(); - - if (me.getItem(classid)) { - this.log("Already have: " + classid); - return true; - } - - if (me.inTown) return false; - - let chest = Game.getObject(chestid); - - if (!chest) { - this.log("Couldn't find: " + chestid); - return false; - } - - for (let i = 0; i < 5; i++) { - if (Misc.openChest(chest)) { - break; + if (!Config.Leader && msg.includes("questinfo")) { + Config.Leader = who; + console.debug("Assigned Leader: " + Config.Leader); } - this.log("Failed to open chest: Attempt[" + (i + 1) + "]"); - let coord = CollMap.getRandCoordinate(chest.x, -4, 4, chest.y, -4, 4); - coord && Pather.moveTo(coord.x, coord.y); - } - - let item = Game.getItem(classid); - - if (!item) { - if (getTickCount() - tick < 500) { - delay(500); + if (who === Config.Leader) { + // we can ignore starting messages as they are just for the rusher to log what they are doing + if (msg.toLowerCase().startsWith("starting")) return; + if (msg.toLowerCase().includes("rush complete")) { + commands.push("exit"); + } else { + console.debug("Leader: " + msg); + commands.push(msg); + } } - - return false; } - return Pickit.pickItem(item) && delay(1000); - }; - - this.checkQuestMonster = function (classid) { - let monster = Game.getMonster(classid); - - if (monster) { - while (!monster.dead) { - delay(500); + /** @param {number | null} area */ + const usePortal = function (area) { + return Pather.usePortal(area, Config.Leader); + }; + + const useScrollOfRes = function () { + let scroll = me.scrollofresistance; + if (scroll) { + clickItem(sdk.clicktypes.click.item.Right, scroll); + console.log("Using scroll of resistance"); } + }; - return true; - } - - return false; - }; - - this.tyraelTalk = function () { - let npc = Game.getNPC(NPC.Tyrael); - - if (!npc) return false; - - for (let i = 0; i < 3; i += 1) { - npc.distance > 3 && Pather.moveToUnit(npc); - npc.interact(); - delay(1000 + me.ping); - me.cancel(); - - if (Pather.getPortal(null)) { - me.cancel(); - - break; + const revive = function () { + while (me.mode === sdk.player.mode.Death) { + nativeDelay(40); } - } - - return Pather.usePortal(null) || Pather.usePortal(null, Config.Leader); - }; - - this.cubeStaff = function () { - let shaft = me.shaft; - let amulet = me.amulet; - - if (!shaft || !amulet) return false; - - Storage.Cube.MoveTo(amulet); - Storage.Cube.MoveTo(shaft); - Cubing.openCube(); - console.log("making staff"); - transmute(); - delay(750 + me.ping); - let staff = me.completestaff; - - if (!staff) return false; - - Storage.Inventory.MoveTo(staff); - me.cancel(); - - return true; - }; - - this.placeStaff = function () { - let tick = getTickCount(); - let orifice = Game.getObject(sdk.quest.chest.HoradricStaffHolder); - if (!orifice) return false; - - Misc.openChest(orifice); - - let staff = me.completestaff; + if (me.mode === sdk.player.mode.Dead) { + me.revive(); - if (!staff) { - if (getTickCount() - tick < 500) { - delay(500); + while (!me.inTown) { + nativeDelay(3); + } } - - return false; - } - - staff.toCursor(); - submitItem(); - delay(750 + me.ping); - - // unbug cursor - let item = me.findItem(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - - if (item && item.toCursor()) { - Storage.Inventory.MoveTo(item); - } - - return true; - }; - - this.changeAct = function (act) { - let preArea = me.area; - - if (me.mode === sdk.player.mode.Dead) { - me.revive(); - - while (!me.inTown) { - delay(500); + }; + + // todo - map the chest to classid so we only need to pass in one value + /** + * @param {number} classid + * @param {number} chestid + */ + const getQuestItem = function (classid, chestid) { + let tick = getTickCount(); + + if (me.getItem(classid)) { + log("Already have: " + classid); + return true; } - } - if (me.act === act || me.act > act) return true; + if (me.inTown) return false; - try { - switch (act) { - case 2: - if (!Town.npcInteract("Warriv", false)) return false; - Misc.useMenu(sdk.menu.GoEast); + let chest = Game.getObject(chestid); - break; - case 3: - // Non Quester needs to talk to Townsfolk to enable Harem TP - if (!Config.Rushee.Quester) { - // Talk to Atma - if (!Town.npcInteract("Atma")) { - break; - } - } - - Pather.usePortal(sdk.areas.HaremLvl1, Config.Leader); - Pather.moveToExit(sdk.areas.LutGholein, true); - - if (!Town.npcInteract("Jerhyn")) { - Pather.moveTo(5166, 5206); + if (!chest) { + log("Couldn't find: " + chestid); + return false; + } - return false; + for (let i = 0; i < 5; i++) { + if (Game.getItem(classid)) { + break; } - - me.cancel(); - Pather.moveToExit(sdk.areas.HaremLvl1, true); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - - if (!Town.npcInteract("Meshif", false)) return false; - Misc.useMenu(sdk.menu.SailEast); - - break; - case 4: - if (me.inTown) { - Town.npcInteract("Cain"); - Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader); - } else { - delay(1500); + if (Misc.openChest(chest)) { + break; } + log("Failed to open chest: Attempt[" + (i + 1) + "]"); + let coord = CollMap.getRandCoordinate(chest.x, -4, 4, chest.y, -4, 4); + coord && Pather.moveTo(coord.x, coord.y); + } - Pather.moveTo(17591, 8070); - Pather.usePortal(null); - - break; - case 5: - Town.npcInteract("Tyrael", false); - delay(me.ping + 1); + let item = Game.getItem(classid); - if (Game.getObject(sdk.objects.RedPortalToAct5)) { - me.cancel(); - Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath); - } else { - Misc.useMenu(sdk.menu.TravelToHarrogath); + if (!item) { + if (getTickCount() - tick < 500) { + delay(500); } - break; + return false; } - delay(1000 + me.ping * 2); + return Pickit.pickItem(item) && delay(1000); + }; - while (!me.area) { - delay(500); + const tyraelTalk = function () { + const bridgeNode = new PathNode(22577, 15609); + if (me.inArea(sdk.areas.DurielsLair) && bridgeNode.distance > 10) { + Pather.move(bridgeNode, { callback: function () { + return Game.getNPC(NPC.Tyrael); + } }); } + let npc = Game.getNPC(NPC.Tyrael); + if (!npc) return false; - if (me.area === preArea) { + for (let i = 0; i < 3; i += 1) { + npc.distance > 3 && Pather.moveToUnit(npc); + npc.interact(); + delay(1000 + me.ping); me.cancel(); - Town.move("portalspot"); - this.log("Act change failed.", Config.LocalChat.Enabled); - return false; - } - - this.log("Act change done.", Config.LocalChat.Enabled); - } catch (e) { - return false; - } + if (Pather.getPortal(null)) { + me.cancel(); - return true; - }; - - this.getQuestInfo = function (id) { - // note: end bosses double printed to match able to go to act flag - let quests = [ - ["cain", sdk.quest.id.TheSearchForCain], - ["andariel", sdk.quest.id.SistersToTheSlaughter], - ["andariel", sdk.quest.id.AbleToGotoActII], - ["radament", sdk.quest.id.RadamentsLair], - ["cube", sdk.quest.id.TheHoradricStaff], - ["amulet", sdk.quest.id.TheTaintedSun], - ["summoner", sdk.quest.id.TheArcaneSanctuary], - ["duriel", sdk.quest.id.TheSevenTombs], - ["duriel", sdk.quest.id.AbleToGotoActIII], - ["lamesen", sdk.quest.id.LamEsensTome], - ["travincal", sdk.quest.id.TheBlackenedTemple], - ["mephisto", sdk.quest.id.TheGuardian], - ["mephisto", sdk.quest.id.AbleToGotoActIV], - ["izual", sdk.quest.id.TheFallenAngel], - ["diablo", sdk.quest.id.TerrorsEnd], - ["diablo", sdk.quest.id.AbleToGotoActV], - ["shenk", sdk.quest.id.SiegeOnHarrogath], - ["anya", sdk.quest.id.PrisonofIce], - ["ancients", sdk.quest.id.RiteofPassage], - ["baal", sdk.quest.id.EyeofDestruction] - ]; - - let quest = quests.find(element => element[1] === id); - - return (!!quest ? quest[0] : ""); - }; - - this.nonQuesterNPCTalk = false; - - addEventListener("chatmsg", - function (who, msg) { - if (who === Config.Leader) { - actions.push(msg); + break; + } } - }); - - // START - Town.goToTown(me.highestAct); - me.inTown && Town.move("portalspot"); - - // if we can't find our leader after 5 minutes, I'm thinking they aren't showing up. Lets not wait around forever - leader = Misc.poll(() => Misc.findPlayer(Config.Leader), Time.minutes(5), 1000); - if (!leader) throw new Error("Failed to find my rusher"); - - Config.Rushee.Quester - ? this.log("Leader found", Config.LocalChat.Enabled) - : console.log("Leader Found: " + Config.Leader); - - // lets figure out if we either are the bumper or have a bumper so we know if we need to stop at the end of the rush - let bumperLevelReq = [20, 40, 60][me.diff]; - // ensure we are the right level to go to next difficulty if not on classic - let nextGame = (Config.Rushee.Bumper && (me.classic || me.charlvl >= bumperLevelReq)); - if (!nextGame) { - // we aren't the bumper, lets figure out if anyone else is a bumper - // hell is the end of a rush so always end profile after - if (Misc.getPlayerCount() > 2 && !me.hell) { - // there is more than just us and the rusher in game - so check party level - nextGame = Misc.checkPartyLevel(bumperLevelReq, leader.name); - } - } - console.debug("Is this our last run? " + (nextGame ? "No" : "Yes")); - - while (true) { - // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces - try { - if (actions.length > 0) { - switch (actions[0]) { - case "all in": - switch (leader.area) { - case sdk.areas.A2SewersLvl3: - // Pick Book of Skill, use Book of Skill - Town.move("portalspot"); - Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader); - delay(500); - - while (true) { - target = Game.getItem(sdk.quest.item.BookofSkill); - if (!target) { - break; - } + return Pather.usePortal(null) || usePortal(null); + }; + + const cube = (function () { + const staff = { + ingreds: [sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.item.ViperAmulet], + outcome: sdk.quest.item.HoradricStaff, + }; + const flail = { + ingreds: [ + sdk.quest.item.KhalimsFlail, sdk.quest.item.KhalimsEye, + sdk.quest.item.KhalimsBrain, sdk.quest.item.KhalimsHeart + ], + outcome: sdk.quest.item.KhalimsWill, + }; + /** @param {{ ingreds: number[], outcome: number }} item */ + const make = function (item) { + if (me.getItem(item.outcome)) return true; + const ingreds = item.ingreds.map(function (itemId) { + return me.getItem(itemId); + }); + if (!ingreds.every(i => i)) return false; + ingreds.forEach(i => Storage.Cube.MoveTo(i)); + Cubing.openCube(); + transmute(); + delay(750 + me.ping); + + let outcome = me.getItem(item.outcome); + if (!outcome) return false; + + Storage.Inventory.MoveTo(outcome); + me.cancel(); - Pickit.pickItem(target); - delay(250); - target = me.getItem(sdk.quest.item.BookofSkill); + return true; + }; + return { + Staff: function () { + log("Making staff", Config.LocalChat.Enabled); + return make(staff); + }, + Flail: function () { + log("Making flail", Config.LocalChat.Enabled); + return make(flail); + }, + }; + })(); + + const placeStaff = function () { + let tick = getTickCount(); + let orifice = Game.getObject(sdk.quest.chest.HoradricStaffHolder); + if (!orifice) return false; + + Misc.openChest(orifice); + + let staff = me.completestaff; + + if (!staff) { + if (getTickCount() - tick < 500) { + delay(500); + } - if (target) { - console.log("Using book of skill"); - clickItem(sdk.clicktypes.click.item.Right, target); + return false; + } - break; - } - } + staff.toCursor(); + submitItem(); + delay(750 + me.ping); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - actions.shift(); + // unbug cursor + let item = me.findItem(-1, sdk.items.mode.inStorage, sdk.storage.Inventory); - break; - } + if (item && item.toCursor()) { + Storage.Inventory.MoveTo(item); + } - actions.shift(); + return true; + }; - break; - case "questinfo": - if (!Config.Rushee.Quester) { - actions.shift(); + /** @param {Act} act */ + const changeAct = function (act) { + revive(); - break; - } + const actGoal = Number(act); + if (me.act === actGoal || me.act > actGoal) return true; + const preArea = me.area; + console.debug("Changing act to: " + actGoal); - say("highestquest " + this.getQuestInfo(me.highestQuestDone)); - actions.shift(); + try { + switch (actGoal) { + case 2: + if (!Town.npcInteract("Warriv", false)) return false; + Misc.useMenu(sdk.menu.GoEast); break; - case "wpinfo": - if (!Config.Rushee.Quester) { - actions.shift(); - - break; + case 3: + // Non Quester needs to talk to Townsfolk to enable Harem TP + if (rushConfig.type !== RushModes.quester) { + // Talk to Atma + if (!Town.npcInteract("Atma")) { + break; + } } + + usePortal(sdk.areas.HaremLvl1); + Pather.moveToExit(sdk.areas.LutGholein, true); + + if (!Town.npcInteract("Jerhyn")) { + Pather.moveTo(5166, 5206); - // go activate wp if we don't know our wps yet - !getWaypoint(1) && Pather.getWP(me.area); - - let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { - if (area === sdk.areas.HallsofPain) return false; - if (me.classic && area >= sdk.areas.Harrogath) return false; - if (getWaypoint(Pather.wpAreas.indexOf(area))) return false; - return true; - }); - - say("mywps " + JSON.stringify(myWps)); - actions.shift(); - - break; - case "wp": - if (!me.inTown && !Town.goToTown()) { - this.log("I can't get to town :(", Config.LocalChat.Enabled); - - break; + return false; } - act = Misc.getPlayerAct(leader); - - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } + me.cancel(); + Pather.moveToExit(sdk.areas.HaremLvl1, true); + usePortal(sdk.areas.LutGholein); - // make sure we talk to cain to access durance - leader.area === sdk.areas.DuranceofHateLvl2 && (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) && Town.npcInteract("Cain"); - - // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas - (!Config.Rushee.Quester && !this.nonQuesterNPCTalk) && (this.nonQuesterNPCTalk = true); + if (!Town.npcInteract("Meshif", false)) return false; + Misc.useMenu(sdk.menu.SailEast); - Town.getDistance("portalspot") > 10 && Town.move("portalspot"); - if (Pather.usePortal(null, Config.Leader) && Pather.getWP(me.area) && Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) && Town.move("portalspot")) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); + break; + case 4: + if (me.inTown) { + Town.npcInteract("Cain"); + usePortal(sdk.areas.DuranceofHateLvl3); } else { - // check for bugged portal - let p = Game.getObject("portal"); - let preArea = me.area; - if (!!p && Misc.click(0, 0, p) && Misc.poll(() => me.area !== preArea, 1000, 100) - && Pather.getWP(me.area) && (Pather.usePortal(sdk.areas.townOf(me.area), Config.Leader) || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { - me.inTown && Config.LocalChat.Enabled && say("gotwp"); - } else { - this.log("Failed to get wp", Config.LocalChat.Enabled); - !me.inTown && Town.goToTown(); - } + delay(1500); } - actions.shift(); + Pather.moveTo(17591, 8070); + // Pather.usePortal(null); + Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct4, sdk.areas.PandemoniumFortress); break; - case "1": - while (!leader.area) { - delay(500); - } + case 5: + Town.npcInteract("Tyrael", false); + delay(me.ping + 1); - act = Misc.getPlayerAct(leader); - - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); + if (Game.getObject(sdk.objects.RedPortalToAct5)) { + me.cancel(); + Pather.useUnit(sdk.unittype.Object, sdk.objects.RedPortalToAct5, sdk.areas.Harrogath); + } else { + Misc.useMenu(sdk.menu.TravelToHarrogath); } - // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester - if (this.nonQuesterNPCTalk) { - console.debug("Leader Area: " + getAreaName(leader.area)); - - switch (leader.area) { - case sdk.areas.ClawViperTempleLvl2: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Drognan")) { - actions.shift(); - console.debug("drognan done"); - } - - break; - case sdk.areas.ArcaneSanctuary: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Atma")) { - actions.shift(); - console.debug("atma done"); - } - - break; - case sdk.areas.Travincal: - Misc.poll(() => !!(Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) || Misc.checkQuest(sdk.quest.id.TheGuardian, 8), Time.seconds(20), 1000)); - if (Town.npcInteract("Cain")) { - actions.shift(); - console.debug("cain done"); - } - - break; - case sdk.areas.ArreatSummit: - Misc.poll(() => (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete), Time.seconds(20), 1000)); - if (Town.npcInteract("Malah")) { - actions.shift(); - console.debug("malah done"); - } + break; + default: + console.warn("Invalid act: " + act); + return false; + } - break; - } + delay(1000 + me.ping * 2); - me.inTown && Town.move("portalspot"); - } + while (!me.area) { + delay(500); + } - if (!Config.Rushee.Quester) { - actions.shift(); + if (me.area === preArea) { + me.cancel(); + Town.move("portalspot"); + log("Act change failed.", Config.LocalChat.Enabled); - break; - } + return false; + } - switch (leader.area) { - case sdk.areas.StonyField: - if (!Pather.usePortal(sdk.areas.StonyField, Config.Leader)) { - this.log("Failed to us portal to stony field", Config.LocalChat.Enabled); - break; - } + if (me.act === 2 && Game.getNPC(NPC.Jerhyn)) { + Town.npcInteract("Jerhyn"); + } else if (me.act === 3) { + Town.npcInteract("Hratli"); + } - let stones = [ - Game.getObject(sdk.quest.chest.StoneAlpha), - Game.getObject(sdk.quest.chest.StoneBeta), - Game.getObject(sdk.quest.chest.StoneGamma), - Game.getObject(sdk.quest.chest.StoneDelta), - Game.getObject(sdk.quest.chest.StoneLambda) - ]; - - while (stones.some((stone) => !stone.mode)) { - for (let i = 0; i < stones.length; i++) { - let stone = stones[i]; - - if (Misc.openChest(stone)) { - stones.splice(i, 1); - i--; - } - delay(10); - } - } + log("Act change done.", Config.LocalChat.Enabled); + } catch (e) { + return false; + } - let tick = getTickCount(); - // wait up to two minutes - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.getPortal(sdk.areas.Tristram)) { - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - - break; - } - } - Town.move("portalspot"); - actions.shift(); + return true; + }; - break; - case sdk.areas.DarkWood: - if (!Pather.usePortal(sdk.areas.DarkWood, Config.Leader)) { - this.log("Failed to use portal to dark wood", Config.LocalChat.Enabled); - break; - } + const getQuestInfo = function () { + return Object.keys(sequenceCheck).reverse().find(function (key) { + return sequenceCheck[key].check(); + }) || "none"; + }; - this.getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); - delay(500); - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - - if (Town.npcInteract("Akara")) { - this.log("Akara done", Config.LocalChat.Enabled); - } + const syncToLeaderAct = function () { + while (!leader.area) { + delay(500); + } - Town.move("portalspot"); - actions.shift(); + act = Misc.getPlayerAct(leader); - break; - case sdk.areas.Tristram: - if (!Pather.usePortal(sdk.areas.Tristram, Config.Leader)) { - this.log("Failed to use portal to Tristram", Config.LocalChat.Enabled); - break; - } + if (me.act !== act) { + Town.goToTown(act); + Town.move("portalspot"); + } + }; + + /** + * @param {() => boolean} checkQuestReady + * @param {string} npcName + * @param {string} doneLog + */ + const waitForQuestAndTalkToNpc = function (checkQuestReady, npcName, doneLog) { + Misc.poll(function () { + return !!checkQuestReady(); + }, Time.seconds(20), 1000); + + if (Town.npcInteract(npcName)) { + console.debug(doneLog + " done"); + return true; + } - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + return false; + }; - if (gibbet && !gibbet.mode) { - Pather.moveTo(gibbet.x, gibbet.y); - if (Misc.poll(() => Misc.openChest(gibbet), 2000, 100)) { - Pather.usePortal(sdk.areas.RogueEncampment, Config.Leader); - Town.npcInteract("Akara") && this.log("Akara done", Config.LocalChat.Enabled); - } - } - Town.move("portalspot"); - actions.shift(); + const handleNonQuesterNpcTalk = function () { + if (!nonQuesterNPCTalk) { + return null; + } - break; - case sdk.areas.CatacombsLvl4: - if (!Pather.usePortal(sdk.areas.CatacombsLvl4, Config.Leader)) { - this.log("Failed to use portal to catacombs", Config.LocalChat.Enabled); - break; - } + console.debug("Leader Area: " + getAreaName(leader.area)); + + switch (leader.area) { + case sdk.areas.ClawViperTempleLvl2: + return waitForQuestAndTalkToNpc(function () { + return (Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.ReqComplete) + || Misc.checkQuest(sdk.quest.id.TheTaintedSun, sdk.quest.states.PartyMemberComplete)); + }, "Drognan", "drognan"); + case sdk.areas.ArcaneSanctuary: + return waitForQuestAndTalkToNpc(function () { + return (Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.ReqComplete) + || Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.PartyMemberComplete)); + }, "Atma", "atma"); + case sdk.areas.Travincal: + return waitForQuestAndTalkToNpc(function () { + return (Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, 4) + || Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.PartyMemberComplete) + || Misc.checkQuest(sdk.quest.id.TheGuardian, 8)); + }, "Cain", "cain"); + case sdk.areas.ArreatSummit: + return waitForQuestAndTalkToNpc(function () { + return (Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.ReqComplete) + || Misc.checkQuest(sdk.quest.id.RiteofPassage, sdk.quest.states.PartyMemberComplete)); + }, "Malah", "malah"); + default: + me.inTown && Town.move("portalspot"); + return null; + } + }; - target = Pather.getPortal(null, Config.Leader); - target && Pather.walkTo(target.x, target.y); + const goToPortalSpot = function () { + if (me.inTown) { + Town.move("portalspot"); + } - actions.shift(); + return true; + }; + + /** + * @param {number} townArea + * @param {string} [leaderName] + */ + const returnToTown = function (townArea, leaderName) { + if (me.inTown) { + return true; + } - break; - case sdk.areas.A2SewersLvl3: - Town.move("portalspot"); + if (leaderName) { + if (leaderName === Config.Leader) { + return usePortal(townArea); + } - if (Pather.usePortal(sdk.areas.A2SewersLvl3, Config.Leader)) { - actions.shift(); - } + return Pather.usePortal(townArea, leaderName); + } - break; - case sdk.areas.HallsoftheDeadLvl3: - Pather.usePortal(sdk.areas.HallsoftheDeadLvl3, Config.Leader); - this.getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); + return Pather.usePortal(townArea); + }; - actions.shift(); + const handlePlayersOutNonQuester = function () { + // Non-questers can piggyback off quester out messages + switch (leader.area) { + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + if (me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { + Town.npcInteract("Tyrael"); + } + break; + case sdk.areas.BloodyFoothills: + me.act === 5 && Town.npcInteract("Larzuk"); - break; - case sdk.areas.ClawViperTempleLvl2: - Pather.usePortal(sdk.areas.ClawViperTempleLvl2, Config.Leader); - this.getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - - if (Town.npcInteract("Drognan")) { - actions.shift(); - say("drognan done", Config.LocalChat.Enabled); - } + break; + case sdk.areas.FrozenRiver: + if (me.act === 5) { + Town.npcInteract("Malah"); + useScrollOfRes(); + } - Town.move("portalspot"); + break; + } - break; - case sdk.areas.MaggotLairLvl3: - Pather.usePortal(sdk.areas.MaggotLairLvl3, Config.Leader); - this.getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); - delay(500); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - this.cubeStaff(); + commands.shift(); - actions.shift(); + return true; + }; - break; - case sdk.areas.ArcaneSanctuary: - if (!Pather.usePortal(sdk.areas.ArcaneSanctuary, Config.Leader)) { - break; - } + const handlePlayersOutQuester = function () { + revive(); - actions.shift(); - - break; - case sdk.areas.TalRashasTomb1: - case sdk.areas.TalRashasTomb2: - case sdk.areas.TalRashasTomb3: - case sdk.areas.TalRashasTomb4: - case sdk.areas.TalRashasTomb5: - case sdk.areas.TalRashasTomb6: - case sdk.areas.TalRashasTomb7: - Pather.usePortal(null, Config.Leader); - this.placeStaff(); - Pather.usePortal(sdk.areas.LutGholein, Config.Leader); - actions.shift(); - - break; - case sdk.areas.DurielsLair: - Pather.usePortal(sdk.areas.DurielsLair, Config.Leader); - this.tyraelTalk(); - - actions.shift(); - - break; - case sdk.areas.Travincal: - if (!Pather.usePortal(sdk.areas.Travincal, Config.Leader)) { - me.cancel(); + switch (me.area) { + case sdk.areas.CatacombsLvl4: + // Go to town if not there, break if procedure fails + if (!returnToTown(sdk.areas.RogueEncampment)) { + return false; + } - break; - } + if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { + D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); + scriptBroadcast("quit"); + } - actions.shift(); + return true; + case sdk.areas.A2SewersLvl3: + if (!returnToTown(sdk.areas.LutGholein, Config.Leader)) { + return false; + } - break; - case sdk.areas.RuinedTemple: - if (!Pather.usePortal(sdk.areas.RuinedTemple, Config.Leader)) { - me.cancel(); + return true; + case sdk.areas.ArcaneSanctuary: + if (!returnToTown(sdk.areas.LutGholein, Config.Leader)) { + return false; + } - break; - } + Town.npcInteract("Atma"); - this.getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); - Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader); - Town.npcInteract("Alkor"); - Town.move("portalspot"); - actions.shift(); + if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); + scriptBroadcast("quit"); + } + goToPortalSpot(); + return true; + case sdk.areas.Travincal: + if (!returnToTown(sdk.areas.KurastDocktown, Config.Leader)) { + return false; + } - break; - case sdk.areas.DuranceofHateLvl3: - if (!Pather.usePortal(sdk.areas.DuranceofHateLvl3, Config.Leader)) { - me.cancel(); + Town.npcInteract("Cain"); - break; - } + if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { + D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); + scriptBroadcast("quit"); + } - actions.shift(); + goToPortalSpot(); + + return true; + case sdk.areas.DuranceofHateLvl3: + return usePortal(sdk.areas.KurastDocktown); + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + if (!returnToTown(sdk.areas.PandemoniumFortress, Config.Leader)) { + return false; + } - break; - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - if (Pather.usePortal(null, Config.Leader)) { - actions.shift(); - } + if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { + Town.npcInteract("Tyrael"); + goToPortalSpot(); + } - break; - case sdk.areas.ChaosSanctuary: - Pather.usePortal(sdk.areas.ChaosSanctuary, Config.Leader); - Pather.moveTo(7762, 5268); - Packet.flash(me.gid); - delay(500); - Pather.walkTo(7763, 5267, 2); + return true; + case sdk.areas.ChaosSanctuary: + me.classic && D2Bot.restart(); - while (!Game.getMonster(sdk.monsters.Diablo)) { - delay(500); - } + if (!returnToTown(sdk.areas.PandemoniumFortress, Config.Leader)) { + return false; + } - Pather.moveTo(7763, 5267); - actions.shift(); + return true; + case sdk.areas.BloodyFoothills: + if (!returnToTown(sdk.areas.Harrogath, Config.Leader)) { + return false; + } - break; - case sdk.areas.BloodyFoothills: - Pather.usePortal(sdk.areas.BloodyFoothills, Config.Leader); - actions.shift(); + Town.npcInteract("Larzuk"); + goToPortalSpot(); + return true; + case sdk.areas.FrozenRiver: + if (!returnToTown(sdk.areas.FrozenRiver, Config.Leader)) { + return false; + } - break; - case sdk.areas.FrozenRiver: - Town.npcInteract("Malah"); + Town.npcInteract("Malah"); + useScrollOfRes(); + goToPortalSpot(); - Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader); - delay(500); + return true; + default: + if (!returnToTown(sdk.areas.townOf(me.area))) { + return false; + } + goToPortalSpot(); + return true; + } + }; + + const tryGetLeaderWaypoint = function () { + if (usePortal(null) + && Pather.getWP(me.area) + && usePortal(sdk.areas.townOf(me.area)) + && goToPortalSpot()) { + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + return true; + } - target = Game.getObject(sdk.objects.FrozenAnya); + // check for bugged portal + let p = Game.getObject("portal"); + let preArea = me.area; + + if (!!p + && Misc.click(0, 0, p) + && Misc.poll(function () { + return me.area !== preArea; + }, 1000, 100) + && Pather.getWP(me.area) + && (usePortal(sdk.areas.townOf(me.area)) + || Pather.useWaypoint(sdk.areas.townOf(me.area)))) { + me.inTown && Config.LocalChat.Enabled && say("gotwp"); + return true; + } - if (target) { - Pather.moveToUnit(target); - Misc.poll(() => { - Packet.entityInteract(target); - delay(100); - return !Game.getObject(sdk.objects.FrozenAnya); - }, 1000, 200); - delay(1000); - me.cancel(); - } + log("Failed to get wp", Config.LocalChat.Enabled); + !me.inTown && Town.goToTown(); - actions.shift(); + return false; + }; + + /** @param {number} area */ + const isTalRashasTombArea = function (area) { + return [ + sdk.areas.TalRashasTomb1, + sdk.areas.TalRashasTomb2, + sdk.areas.TalRashasTomb3, + sdk.areas.TalRashasTomb4, + sdk.areas.TalRashasTomb5, + sdk.areas.TalRashasTomb6, + sdk.areas.TalRashasTomb7, + ].includes(area); + }; + + const handlePlayersInStonyField = function () { + if (!me.getItem(sdk.quest.item.KeytotheCairnStones)) { + log("Failed to pick up scroll", Config.LocalChat.Enabled); + return false; + } + + if (!usePortal(sdk.areas.StonyField)) { + log("Failed to use portal to stony field", Config.LocalChat.Enabled); + return false; + } - break; - default: // unsupported area - actions.shift(); + let stones = [ + Game.getObject(sdk.quest.chest.StoneAlpha), + Game.getObject(sdk.quest.chest.StoneBeta), + Game.getObject(sdk.quest.chest.StoneGamma), + Game.getObject(sdk.quest.chest.StoneDelta), + Game.getObject(sdk.quest.chest.StoneLambda) + ]; + + let stoneTick = getTickCount(); + for (let i = 0; i < 5; i++) { + for (let stone of stones) { + if (!stone || stone.mode) continue; + clickUnitAndWait(sdk.clicktypes.click.map.LeftDown, sdk.clicktypes.shift.NoShift, stone); + } + } + + while (stones.some(function (stone) { + return !stone.mode; + })) { + if (getTickCount() - stoneTick > Time.minutes(2)) { + log("Failed to activate stones", Config.LocalChat.Enabled); + return false; + } + for (let i = 0; i < stones.length; i++) { + let stone = stones[i]; - break; + if (Misc.openChest(stone)) { + stones.splice(i, 1); + i--; } + delay(10); + } + } + let tick = getTickCount(); + // wait up to two minutes + while (getTickCount() - tick < Time.minutes(2)) { + if (Pather.getPortal(sdk.areas.Tristram)) { + usePortal(sdk.areas.RogueEncampment); + break; - case "2": // Go back to town and check quest - if (!Config.Rushee.Quester) { - // Non-questers can piggyback off quester out messages - switch (leader.area) { - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - me.act === 4 && Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete) && Town.npcInteract("Tyrael"); + } + } + Town.move("portalspot"); - break; - case sdk.areas.BloodyFoothills: - me.act === 5 && Town.npcInteract("Larzuk"); + return true; + }; - break; - case sdk.areas.FrozenRiver: - if (me.act === 5) { - Town.npcInteract("Malah"); - this.useScrollOfRes(); - } + const handlePlayersInDarkWood = function () { + if (!usePortal(sdk.areas.DarkWood)) { + log("Failed to use portal to dark wood", Config.LocalChat.Enabled); + return false; + } - break; - } + getQuestItem(sdk.items.quest.ScrollofInifuss, sdk.quest.chest.InifussTree); + delay(500); + usePortal(sdk.areas.RogueEncampment); + + if (Town.npcInteract("Akara")) { + log("Akara done", Config.LocalChat.Enabled); + } - actions.shift(); + Town.move("portalspot"); - break; - } + return true; + }; - this.revive(); + const handlePlayersInTristram = function () { + if (!usePortal(sdk.areas.Tristram)) { + log("Failed to use portal to Tristram", Config.LocalChat.Enabled); + return true; + } - switch (me.area) { - case sdk.areas.CatacombsLvl4: - // Go to town if not there, break if procedure fails - if (!me.inTown && !Pather.usePortal(sdk.areas.RogueEncampment)) { - break; - } + let gibbet = Game.getObject(sdk.quest.chest.CainsJail); - if (!Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, 4)) { - D2Bot.printToConsole("Andariel quest failed", sdk.colors.D2Bot.Red); - quit(); - } + if (gibbet && !gibbet.mode) { + Pather.moveTo(gibbet.x, gibbet.y); + if (Misc.poll(function () { + return Misc.openChest(gibbet); + }, 2000, 100)) { + usePortal(sdk.areas.RogueEncampment); + Town.npcInteract("Akara") && log("Akara done", Config.LocalChat.Enabled); + } + } + Town.move("portalspot"); + commands.shift(); - actions.shift(); + return true; + }; - break; - case sdk.areas.A2SewersLvl3: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - break; - } + const handlePlayersInFrozenRiver = function () { + Town.npcInteract("Malah"); - actions.shift(); + usePortal(sdk.areas.FrozenRiver); + delay(500); - break; - case sdk.areas.ArcaneSanctuary: - if (!me.inTown && !Pather.usePortal(sdk.areas.LutGholein, Config.Leader)) { - break; - } + target = Game.getObject(sdk.objects.FrozenAnya); - Town.npcInteract("Atma"); + if (target) { + Pather.moveToUnit(target); + Misc.poll(function () { + Packet.entityInteract(target); + delay(100); + return !Game.getObject(sdk.objects.FrozenAnya); + }, 1000, 200); + delay(1000); + me.cancel(); + } - if (!Misc.checkQuest(sdk.quest.id.TheSummoner, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Summoner quest failed", sdk.colors.D2Bot.Red); - quit(); - } + return true; + }; + + const handlePlayersInArea = function () { + switch (leader.area) { + case sdk.areas.StonyField: + return handlePlayersInStonyField(); + case sdk.areas.DarkWood: + return handlePlayersInDarkWood(); + case sdk.areas.Tristram: + return handlePlayersInTristram(); + case sdk.areas.CatacombsLvl4: + if (!usePortal(sdk.areas.CatacombsLvl4)) { + log("Failed to use portal to catacombs", Config.LocalChat.Enabled); + return false; + } - Town.move("portalspot"); - actions.shift(); + target = Pather.getPortal(null, Config.Leader); + target && Pather.walkTo(target.x, target.y); - break; - case sdk.areas.Travincal: - if (!me.inTown && !Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { - break; - } + return true; + case sdk.areas.A2SewersLvl3: + Town.move("portalspot"); - Town.npcInteract("Cain"); + return usePortal(sdk.areas.A2SewersLvl3); + case sdk.areas.HallsoftheDeadLvl3: + usePortal(sdk.areas.HallsoftheDeadLvl3); + getQuestItem(sdk.quest.item.Cube, sdk.quest.chest.HoradricCubeChest); + return usePortal(sdk.areas.LutGholein); + case sdk.areas.ClawViperTempleLvl2: + usePortal(sdk.areas.ClawViperTempleLvl2); + getQuestItem(sdk.quest.item.ViperAmulet, sdk.quest.chest.ViperAmuletChest); + usePortal(sdk.areas.LutGholein); + + if (Town.npcInteract("Drognan")) { + log("drognan done", Config.LocalChat.Enabled); + Town.move("portalspot"); + return true; + } - if (!Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed)) { - D2Bot.printToConsole("Travincal quest failed", sdk.colors.D2Bot.Red); - quit(); - } + return false; + case sdk.areas.MaggotLairLvl3: + usePortal(sdk.areas.MaggotLairLvl3); + getQuestItem(sdk.quest.item.ShaftoftheHoradricStaff, sdk.quest.chest.ShaftoftheHoradricStaffChest); + delay(500); + usePortal(sdk.areas.LutGholein); + + return cube.Staff(); + case sdk.areas.ArcaneSanctuary: + return usePortal(sdk.areas.ArcaneSanctuary); + case sdk.areas.DurielsLair: + usePortal(sdk.areas.DurielsLair); + tyraelTalk(); + + return true; + case sdk.areas.Travincal: + if (!usePortal(sdk.areas.Travincal)) { + me.cancel(); - Town.move("portalspot"); - actions.shift(); + return false; + } - break; - case sdk.areas.DuranceofHateLvl3: - if (!Pather.usePortal(sdk.areas.KurastDocktown, Config.Leader)) { - break; - } + return true; + case sdk.areas.RuinedTemple: + if (!usePortal(sdk.areas.RuinedTemple)) { + me.cancel(); - actions.shift(); + return false; + } - break; - case sdk.areas.OuterSteppes: - case sdk.areas.PlainsofDespair: - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - break; - } + getQuestItem(sdk.quest.item.LamEsensTome, sdk.quest.chest.LamEsensTomeHolder); + usePortal(sdk.areas.KurastDocktown); + Town.npcInteract("Alkor"); + Town.move("portalspot"); + return true; + case sdk.areas.DuranceofHateLvl3: + if (!usePortal(sdk.areas.DuranceofHateLvl3)) { + me.cancel(); - if (Misc.checkQuest(sdk.quest.id.TheFallenAngel, sdk.quest.states.ReqComplete)) { - Town.npcInteract("Tyrael"); - Town.move("portalspot"); - } + return false; + } - actions.shift(); + return true; + case sdk.areas.OuterSteppes: + case sdk.areas.PlainsofDespair: + return usePortal(null); + case sdk.areas.ChaosSanctuary: + usePortal(sdk.areas.ChaosSanctuary); + Pather.moveTo(7762, 5268); + Packet.flash(me.gid); + delay(500); + Pather.walkTo(7763, 5267, 2); - break; - case sdk.areas.ChaosSanctuary: - me.classic && D2Bot.restart(); + while (!Game.getMonster(sdk.monsters.Diablo)) { + delay(500); + } - if (!me.inTown && !Pather.usePortal(sdk.areas.PandemoniumFortress, Config.Leader)) { - break; - } + Pather.moveTo(7763, 5267); + return true; + case sdk.areas.BloodyFoothills: + return usePortal(sdk.areas.BloodyFoothills); + case sdk.areas.FrozenRiver: + return handlePlayersInFrozenRiver(); + default: + if (isTalRashasTombArea(leader.area)) { + usePortal(null); + placeStaff(); + usePortal(sdk.areas.LutGholein); + } - actions.shift(); + return true; + } + }; - break; - case sdk.areas.BloodyFoothills: - if (!me.inTown && !Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - break; - } + const actions = new Map([ + [AutoRush.allIn, function () { + switch (leader.area) { + case sdk.areas.A2SewersLvl3: + // Pick Book of Skill, use Book of Skill + Town.move("portalspot"); + usePortal(sdk.areas.A2SewersLvl3); + delay(500); - Town.npcInteract("Larzuk"); - Town.move("portalspot"); - actions.shift(); + while (true) { + target = Game.getItem(sdk.quest.item.BookofSkill); - break; - case sdk.areas.FrozenRiver: - if (!me.inTown && !Pather.usePortal(sdk.areas.FrozenRiver, Config.Leader)) { + if (!target) { break; } - Town.npcInteract("Malah"); - this.useScrollOfRes(); - Town.move("portalspot"); - - actions.shift(); + Pickit.pickItem(target); + delay(250); + target = me.getItem(sdk.quest.item.BookofSkill); - break; - default: - Town.move("portalspot"); - actions.shift(); + if (target) { + console.log("Using book of skill"); + clickItem(sdk.clicktypes.click.item.Right, target); - break; + break; + } } - break; - case "3": // Bumper - if (!Config.Rushee.Bumper) { - actions.shift(); + usePortal(sdk.areas.LutGholein); - break; - } - - while (!leader.area) { - delay(500); + return true; + default: + if (!weAreBumper) { + if (!me.hell || me.charlvl < bumperLevelReq) { + console.debug("Not bumper, waiting for leader to enter area"); + return true; + } } - act = Misc.getPlayerAct(leader); - - if (me.act !== act) { - Town.goToTown(act); - Town.move("portalspot"); - } + syncToLeaderAct(); switch (leader.area) { case sdk.areas.ArreatSummit: - if (!Pather.usePortal(sdk.areas.ArreatSummit, Config.Leader)) { - break; + if (!usePortal(sdk.areas.ArreatSummit)) { + return false; } // Wait until portal is gone @@ -988,94 +944,306 @@ function Rushee() { delay(500); } - if (!Pather.usePortal(sdk.areas.Harrogath, Config.Leader)) { - break; + if (!usePortal(sdk.areas.Harrogath)) { + return false; } - actions.shift(); - - break; + return true; case sdk.areas.WorldstoneChamber: - if (!Pather.usePortal(sdk.areas.WorldstoneChamber, Config.Leader)) { - break; + if (!usePortal(sdk.areas.WorldstoneChamber)) { + return false; } - actions.shift(); - - break; + return true; } + } + return true; + }], + [AutoRush.playersIn, function () { + syncToLeaderAct(); + + // we need to talk to certain npcs in order to be able to grab waypoints as a non-quester + let npcTalkResult = handleNonQuesterNpcTalk(); + if (npcTalkResult !== null) { + return npcTalkResult; + } - break; - case "quit": - done = true; + if (rushConfig.type !== RushModes.quester) { + return true; + } - break; - case "exit": - case "bye ~": - if (!nextGame) { - D2Bot.printToConsole("Rush Complete"); - D2Bot.stop(); - } else { - D2Bot.restart(); - } + return handlePlayersInArea(); + }], + [AutoRush.playersOut, function () { + if (rushConfig.type !== RushModes.quester) { + return handlePlayersOutNonQuester(); + } - break; - case "a2": - case "a3": - case "a4": - case "a5": - act = actions[0].toString()[1]; - !!act && (act = (parseInt(act, 10) || me.act + 1)); - - if (!this.changeAct(act)) { - break; + return handlePlayersOutQuester(); + }], + ["flail", function () { + if (rushConfig.type !== RushModes.quester) { + return true; + } + return true; + }], + ["questinfo", function () { + if (rushConfig.type !== RushModes.quester) { + return true; + } + say("qinfo " + getQuestInfo()); + return true; + }], + ["wpinfo", function () { + if (rushConfig.type !== RushModes.quester) { + return true; + } + // go activate wp if we don't know our wps yet + !Pather.initialized && Pather.init(true); + const includeWorldstone = me.ancients; + const highestAct = me.highestAct; + + let myWps = Pather.nonTownWpAreas.slice(0).filter(function (area) { + if (area === sdk.areas.HallsofPain) return false; + if (me.classic && area >= sdk.areas.Harrogath) return false; + if (me.haveWaypoint(area)) return false; + switch (highestAct) { + case 1: + return (area < sdk.areas.LutGholein); + case 2: + return (area < sdk.areas.KurastDocktown); + case 3: + return (area < sdk.areas.PandemoniumFortress); + case 4: + return (area < sdk.areas.Harrogath); } + if (!includeWorldstone && area === sdk.areas.WorldstoneLvl2) return false; + return true; // this returns a list of what we don't have + }); + + // say("wpinfo " + JSON.stringify({ areas: myWps })); + say("wpinfo " + JSON.stringify(myWps)); + return true; + }], + ["wp", function () { + if (!me.inTown && !Town.goToTown()) { + log("I can't get to town :(", Config.LocalChat.Enabled); + return false; + } - Town.move("portalspot"); - actions.shift(); - - break; - case me.name + " quest": - say("I am quester."); - Config.Rushee.Quester = true; + let leaderArea = Misc.getPlayerArea(leader); + if (leaderArea && me.haveWaypoint(leaderArea)) { + say("alreadyhave"); + return true; + } - actions.shift(); + syncToLeaderAct(); - break; - case "leader": - console.log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - Config.LocalChat.Enabled && say(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now"); - actions.shift(); + // make sure we talk to cain to access durance + if ( + leader.area === sdk.areas.DuranceofHateLvl2 + && !Misc.checkQuest(sdk.quest.id.TheBlackenedTemple, sdk.quest.states.Completed) + ) { + Town.npcInteract("Cain"); + } + + // we aren't the quester but need to talk to npcs in order to be able to get wps from certain areas + (rushConfig.type !== RushModes.quester && !nonQuesterNPCTalk) && (nonQuesterNPCTalk = true); - break; - default: // Invalid command - actions.shift(); + Town.getDistance("portalspot") > 10 && Town.move("portalspot"); + tryGetLeaderWaypoint(); - break; + return true; + }], + ["changeact", /** @param {number} act */ function (act) { + if (!changeAct(act)) { + return false; } - } - } catch (e) { - if (me.mode === sdk.player.mode.Dead) { - me.revive(); - while (!me.inTown) { - delay(500); + Town.move("portalspot"); + return true; + }], + ["quit", function () { + return (done = true); + }], + ["exit", function () { + if (!nextGame) { + D2Bot.printToConsole("Rush Complete"); + D2Bot.stop(); + } else { + // if (rushConfig && rushConfig.create && rushConfig.create.charsPerAcc > 1) { + // const charNumbers = "abcdefghijklmnopqrstuvwxyz"; + // let currName = me.charname; + // let suffix = currName.slice(-2); + + // if (suffix[1] === "z") { + // if (suffix[0] === "z") { + // // both characters were z, so wrap around + // D2Bot.setProfile(null, null, currName.slice(0, -2) + "aa"); + // } else { + // let nextCharIdx = charNumbers.indexOf(suffix[0]); + // let newSuffix = charNumbers[nextCharIdx + 1] + "a"; + // D2Bot.setProfile(null, null, currName.slice(0, -2) + newSuffix); + // } + // } else { + // let nextCharIdx = charNumbers.indexOf(suffix[1]); + // let newSuffix = suffix[0] + charNumbers[nextCharIdx + 1]; + // D2Bot.setProfile(null, null, currName.slice(0, -2) + newSuffix); + // } + // } + D2Bot.restart(); } - } + return true; + }], + ["leader", function () { + log(Config.Leader + " is my leader in my config. " + leader.name + " is my leader right now", Config.LocalChat.Enabled); + return true; + }], + [me.name + "-quest", function () { + say("I am quester."); + rushConfig.type = RushModes.quester; + + return true; + }], + ]); + + // -------------------------------------------------- + // START MAIN SCRIPT EXECUTION + // -------------------------------------------------- + + // Apply overrides + applyOverrides(); + addEventListener("chatmsg", chatEvent); + + const curr = { + cmd: "", + retry: 0 + }; + let nonQuesterNPCTalk = false; + let act, target, done = false; + /** @type {string[]} */ + const commands = []; + + if (Misc.checkQuest(sdk.quest.id.SistersToTheSlaughter, sdk.quest.states.ReqComplete)) { + changeAct(2); } - if (getUIFlag(sdk.uiflags.TradePrompt)) { - me.cancel(); + if (Misc.checkQuest(sdk.quest.id.TheSevenTombs, sdk.quest.states.ReqComplete)) { + changeAct(3); } - if (done) { - break; + if (Misc.checkQuest(sdk.quest.id.TerrorsEnd, sdk.quest.states.ReqComplete)) { + changeAct(5); + } + + Town.goToTown(me.highestAct); + me.inTown && Town.move("portalspot"); + + if (me.inArea(sdk.areas.RogueEncampment) + && !me.getQuest(sdk.quest.id.SpokeToWarriv, sdk.quest.states.Completed) + ) { + Town.npcInteract("Warriv"); + Town.move("portalspot"); } - delay(500); - } + // if we can't find our leader after 5 minutes, I'm thinking they aren't showing up. Lets not wait around forever + let checkIn = getTickCount() + Time.minutes(1); + const leader = Misc.poll(function () { + if (getTickCount() > checkIn) { + say("who is rusher"); + checkIn = getTickCount() + Time.minutes(1); + } + return Misc.findPlayer(Config.Leader); + }, Time.minutes(5), 1000); + if (!leader) throw new Error("Failed to find my rusher"); + + const maybeBumper = [RushModes.bumper, RushModes.quester].includes(rushConfig.type); + (rushConfig.type === RushModes.quester) + ? log("(Quester) Leader Found: " + Config.Leader, Config.LocalChat.Enabled) + : console.log("(NonQuester) Leader Found: " + Config.Leader); + + // lets figure out if we either are the bumper or have a bumper so we know if we need to stop at the end of the rush + const bumperLevelReq = getBumperLvlReq(); + const weAreBumper = maybeBumper && me.charlvl >= bumperLevelReq; + // ensure we are the right level to go to next difficulty if not on classic + let nextGame = ( + (maybeBumper && (me.classic || me.charlvl >= bumperLevelReq)) + // || (rushConfig && rushConfig.create && rushConfig.create.charsPerAcc > 1) // unsupported currentlly + ); + if (!nextGame) { + // we aren't the bumper, lets figure out if anyone else is a bumper + // hell is the end of a rush so always end profile after + if (Misc.getPlayerCount() > 2 && !me.hell) { + // there is more than just us and the rusher in game - so check party level + nextGame = Misc.checkPartyLevel(bumperLevelReq, leader.name); + } + } + console.debug("Is this our last run? " + (nextGame ? "No" : "Yes")); + + const processNextCommand = function () { + if (commands.length < 1) { + return; + } + + /** @type {string[]} */ + let args = commands[0].toLowerCase().split(" "); + let command = args.shift(); + + if (!actions.has(command)) { + console.debug("Command not found: " + command); + commands.shift(); + + return; + } + + curr.cmd = command; + + if (actions.get(command).apply(null, args)) { + commands.shift(); + curr.retry = 0; + } else { + console.debug("Command retry: " + command); + curr.retry++; + if (curr.retry > 3) { + log("Failed to do " + command, Config.LocalChat.Enabled); + if (command === "changeact") { + // well that's no good + scriptBroadcast("quit"); + } + commands.shift(); + } + } + }; + + while (true) { + // todo - clean all this up so there is clear distinction between quester/non-quester and no repeat sequnces + try { + if (me.inTown && me.needHealing()) { + Town.heal(); + Town.move("portalspot"); + } + + processNextCommand(); + } catch (e) { + console.error(e); + commands.shift(); + if (me.mode === sdk.player.mode.Dead) { + revive(); + } + } + + if (getUIFlag(sdk.uiflags.TradePrompt)) { + me.cancel(); + } - done && quit(); + if (done) { + break; + } - return true; -} + delay(500); + } + done && scriptBroadcast("quit"); + + return true; + } +); diff --git a/d2bs/kolbot/libs/scripts/Rusher.js b/d2bs/kolbot/libs/scripts/Rusher.js index 20ba4817b..391ada27c 100644 --- a/d2bs/kolbot/libs/scripts/Rusher.js +++ b/d2bs/kolbot/libs/scripts/Rusher.js @@ -1,3 +1,4 @@ +/// /** * @filename Rusher.js * @author kolton, theBGuy @@ -14,245 +15,272 @@ * */ -function Rusher () { - load("threads/rushthread.js"); - delay(500); - - let i, command, master, commandSplit0; - let commands = []; - let sequence = [ - "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", - "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" - ]; - const RushThread = { - /** @type {Script} */ - _thread: null, - path: "threads/rushthread.js", - - get: function () { - if (!this._thread) { - this._thread = getScript(this.path); - } - return this._thread; - }, - /** @param {String} msg */ - send: function (msg) { - // sign the msg so we can ignore other threads' messages - this.get().send("rush-" + msg); - }, - pause: function () { - say("Pausing"); - console.log("Pausing rush thread"); - this.get().pause(); - }, - resume: function () { - say("Resuming"); - console.log("Resuming rush thread"); - this.get().resume(); - }, - start: function () { - load(this.path); - delay(500); - - while (!this.get()) { - delay(500); - } - }, - stop: function () { - this.get().stop(); - }, - reload: function () { - this.stop(); - - while (this.get().running) { - delay(3); - } - this._thread = null; - this.start(); - }, - }; - - const getPartyAct = function () { - let party = getParty(); - let minArea = 999; +const Rusher = new Runnable( + function Rusher () { + load("threads/rushthread.js"); + delay(500); - do { - if (party.name !== me.name) { - while (!party.area) { - me.overhead("Waiting for party area info"); - delay(500); + const { + RushConfig, + } = require("../systems/autorush/RushConfig"); + const { RushModes, AutoRush } = require("../systems/autorush/RushConstants"); + + const commands = []; + /** @type {RusherConfig} */ + const rushProfile = RushConfig[me.profile]; + + let master = ""; + let done = false; + let gotQInfo = false; + + const RushThread = { + /** @type {Script} */ + _thread: null, + path: "threads/rushthread.js", + + get thread() { + if (!this._thread) { + this._thread = getScript(this.path); } - - if (party.area < minArea) { - minArea = party.area; + return this._thread; + }, + /** @param {String} msg */ + send: function (msg) { + // sign the msg so we can ignore other threads' messages + this.thread.send("rush-" + msg); + }, + /** + * @param {string} action + * @param {object} blob + */ + sendBlob: function (action, blob) { + // sign the msg so we can ignore other threads' messages + this.thread.send({ type: "rush", action: action, data: blob }); + }, + pause: function () { + say("Pausing"); + console.log("Pausing rush thread"); + this.thread.pause(); + }, + resume: function () { + say("Resuming"); + console.log("Resuming rush thread"); + this.thread.resume(); + }, + start: function () { + load(this.path); + nativeDelay(500); + + while (!this.thread) { + nativeDelay(500); } - } - } while (party.getNext()); - - return sdk.areas.actOf(minArea); - }; - - const chatEvent = function (nick, msg) { - if (nick !== me.name) { + }, + stop: function () { + this.thread.stop(); + }, + reload: function () { + this.stop(); + + while (this.thread.running) { + nativeDelay(50); + } + this._thread = null; + this.start(); + }, + }; + + const getPartyAct = function () { + let party = getParty(); + let minArea = 999; + + do { + if (party.name !== me.name) { + Misc.poll(function () { + me.overhead("Waiting for party area info from " + party.name); + return party.area; + }, Time.seconds(5), 500); + + if (party.area < minArea) { + minArea = party.area; + } + } + } while (party.getNext()); + + return sdk.areas.actOf(minArea); + }; + + /** + * @param {string} nick + * @param {string} msg + */ + const chatEvent = function (nick, msg) { + if (!nick || !msg) return; + if (nick === me.name) return; if (typeof msg !== "string") return; - switch (msg) { - case "master": - if (!master) { - say(nick + " is my master."); - - master = nick; - } else { - say("I already have a master."); + if (msg === "who is rusher") { + say("i am rusher"); + return; + } + try { + if (msg.includes("qinfo")) { + if ((!!master && nick === master) || !master) { + RushThread.sendBlob("highestquest", { + highestquest: msg.split("qinfo ")[1], + quester: nick, + }); + console.log("Received quest info from master"); + gotQInfo = true; + } + return; + } + if (msg.includes("wpinfo")) { + if ((!!master && nick === master) || !master) { + /** @type {string[]} */ + let wps = JSON.parse(msg.split("wpinfo ")[1]); + RushThread.sendBlob("wps", { + wps: wps.map(wp => parseInt(wp, 10)), + }); + console.log("Received wp info from master"); + } + return; } + switch (msg) { + case "master": + if (master) { + if (master !== nick) { + throw new Error("I already have a master."); + } + } else { + say(nick + " is my master."); - break; - case "release": - if (nick === master) { - say("I have no master now."); + master = nick; + } + break; + case "release": + if (nick !== master) { + throw new Error("I'm only accepting commands from my master."); + } + say("I have no master now."); master = false; - } else { - say("I'm only accepting commands from my master."); - } - break; - case "quit": - if (nick === master) { + break; + case "quit": + if (nick !== master) { + throw new Error("I'm only accepting commands from my master."); + } + done = true; say("bye ~"); scriptBroadcast("quit"); - } else { - say("I'm only accepting commands from my master."); - } - break; - default: - if (msg && msg.match(/^do \w|^clear \d|^pause$|^resume$/gi)) { - if (nick === master) { + break; + default: + if (msg && msg.match(/^do \w|^clear \d|^pause$|^resume$/gi)) { + if (nick !== master) { + throw new Error("I'm only accepting commands from my master."); + } commands.push(msg); - } else { - say("I'm only accepting commands from my master."); - } - } else if (msg && msg.includes("highestquest")) { - if (!!master && nick === master || !master) { - command = msg; - } else { - say("I'm only accepting commands from my master."); } + + break; } + } catch (e) { + say("Error: " + e.message); + } + }; + + addEventListener("chatmsg", chatEvent); + const playerWaitTimeout = getTickCount() + Time.minutes(2); + const { WaitPlayerCount } = rushProfile.config; + + while (Misc.getPartyCount() < Math.min(8, WaitPlayerCount)) { + if (getTickCount() > playerWaitTimeout) { + say("Player wait timed out. Expected: " + WaitPlayerCount + ", Found: " + Misc.getPartyCount()); break; } + me.overhead("Waiting " + Math.round((playerWaitTimeout - getTickCount()) / 1000) + "s for players to join"); + delay(500); } - }; - - addEventListener("chatmsg", chatEvent); - - while (Misc.getPartyCount() < Math.min(8, Config.Rusher.WaitPlayerCount)) { - me.overhead("Waiting for players to join"); - delay(500); - } - - // Skip to a higher act if all party members are there - switch (getPartyAct()) { - case 2: - say("Party is in act 2, starting from act 2"); - RushThread.send("skiptoact 2"); - - break; - case 3: - say("Party is in act 3, starting from act 3"); - RushThread.send("skiptoact 3"); - break; - case 4: - say("Party is in act 4, starting from act 4"); - RushThread.send("skiptoact 4"); - - break; - case 5: - say("Party is in act 5, starting from act 5"); - RushThread.send("skiptoact 5"); - - break; - } - - // get info from master - let tick = getTickCount(); - let askAgain = 1; - say("questinfo"); - while (!command) { - // wait up to 3 minutes - if (getTickCount() - tick > Time.minutes(3)) { - break; + // Skip to a higher act if all party members are there + let partyAct = getPartyAct(); + if (partyAct > 1) { + say("Party is in act " + partyAct + ", skipping to act " + partyAct); + RushThread.send("skiptoact " + partyAct); } - if (getTickCount() - tick > Time.minutes(askAgain)) { + if (rushProfile.type === RushModes.rusher) { + // get quest info from master + let tick = getTickCount(); + let askAgain = 1; say("questinfo"); - askAgain++; - } - } + + while (!gotQInfo) { + // wait up to 3 minutes + if (getTickCount() - tick > Time.minutes(3)) { + break; + } - if (command) { - commandSplit0 = command.split(" ")[1]; - if (!!commandSplit0 && sequence.some(el => el.toLowerCase() === commandSplit0)) { - RushThread.send(command.toLowerCase()); + if (getTickCount() - tick > Time.minutes(askAgain)) { + say("questinfo"); + askAgain++; + } + } } - } + + delay(200); + RushThread.send("go"); - delay(200); - RushThread.send("go"); + while (!done) { + if (commands.length > 0) { + let command = commands.shift(); - while (true) { - if (commands.length > 0) { - command = commands.shift(); + switch (command) { + case "pause": + RushThread.pause(); - switch (command) { - case "pause": - RushThread.pause(); + break; + case "resume": + RushThread.resume(); - break; - case "resume": - RushThread.resume(); - - break; - default: - if (typeof command === "string") { - commandSplit0 = command.split(" ")[0]; + break; + default: + if (typeof command === "string") { + let commandSplit0 = command.split(" ")[0]; - if (commandSplit0 === undefined) { - break; - } - - if (commandSplit0.toLowerCase() === "do") { - for (i = 0; i < sequence.length; i += 1) { - if (command.split(" ")[1] && sequence[i].match(command.split(" ")[1], "gi")) { - RushThread.reload(); - RushThread.send(command.split(" ")[1]); + if (commandSplit0 === undefined) { + break; + } + if (commandSplit0.toLowerCase() === "do") { + let script = (command.split(" ")[1] || "").toLowerCase(); + if (!script || !AutoRush.sequences.some(el => el.match(script, "gi"))) { + say("Invalid sequence"); + break; + } + RushThread.reload(); + RushThread.send(script); + } else if (commandSplit0.toLowerCase() === "clear") { + let area = command.split(" ")[1]; + if (!area) break; + let areaId = parseInt(area, 10); + if (isNaN(areaId) || areaId < sdk.areas.RogueEncampment || areaId > sdk.areas.WorldstoneChamber) { + say("Invalid area"); break; } - } - - i === sequence.length && say("Invalid sequence"); - } else if (commandSplit0.toLowerCase() === "clear") { - if (!isNaN(parseInt(command.split(" ")[1], 10)) - && parseInt(command.split(" ")[1], 10) > 0 - && parseInt(command.split(" ")[1], 10) <= 132) { RushThread.reload(); RushThread.send(command); - } else { - say("Invalid area"); } } - } - break; + break; + } } + + delay(100); } - delay(100); + return true; } - - // eslint-disable-next-line no-unreachable - return true; -} +); diff --git a/d2bs/kolbot/libs/systems/autorush/AutoRush.js b/d2bs/kolbot/libs/systems/autorush/AutoRush.js index b64c56d3f..4b249cade 100644 --- a/d2bs/kolbot/libs/systems/autorush/AutoRush.js +++ b/d2bs/kolbot/libs/systems/autorush/AutoRush.js @@ -9,8 +9,8 @@ const { AutoRush, RushModes, - RushConfig, - } = require("./RushConfig"); + } = require("./RushConstants"); + const { RushConfig } = require("./RushConfig"); /** * @param {string} msg @@ -48,7 +48,7 @@ return false; }; - const bumperLvlReq = function () { + const getBumperLvlReq = function () { return [20, 40, 60][me.diff]; }; @@ -58,8 +58,8 @@ */ const bumperCheck = function (nick) { return nick - ? Misc.findPlayer(nick).level >= bumperLvlReq() - : Misc.checkPartyLevel(bumperLvlReq()); + ? Misc.findPlayer(nick).level >= getBumperLvlReq() + : Misc.checkPartyLevel(getBumperLvlReq()); }; /** @param {Act} act */ @@ -83,7 +83,7 @@ }; /** @param {string} [nick] */ - const cain = function (nick) { + const cain = function cain (nick) { log("starting cain"); Town.doChores(); Pather.useWaypoint(sdk.areas.DarkWood, true) && Precast.doPrecast(true); @@ -125,7 +125,7 @@ while (getTickCount() - tick < Time.minutes(2)) { if (Pather.getPortal(sdk.areas.Tristram)) { let playersleftStony = Misc.poll(function () { - if (playerIn(me.area, nick)) { + if (playerIn(sdk.areas.RogueEncampment, nick)) { return true; } return false; @@ -164,7 +164,7 @@ if (playerIn(me.area, nick)) { return true; } - Pather.move(gibbet); + Pather.move(gibbet, { minDist: 8 }); return false; }, AutoRush.playerWaitTimeout, 1000); @@ -179,7 +179,7 @@ }; /** @param {string} [nick] */ - const andariel = function (nick) { + const andariel = function andariel (nick) { log("starting andariel"); Town.doChores(); Pather.useWaypoint(sdk.areas.CatacombsLvl2, true) && Precast.doPrecast(true); @@ -216,10 +216,10 @@ Pather.usePortal(null, me.name); for (let i = 0; i < 3; i++) { - log("a2"); + log("changeact 2"); let playersMoved = Misc.poll(function () { - return !playersInAct(2); + return playersInAct(2); }, Time.seconds(30), Time.seconds(1)); if (playersMoved) { @@ -233,7 +233,7 @@ }; /** @param {string} [nick] */ - const bloodraven = function (nick) { + const bloodraven = function bloodraven (nick) { log("starting bloodraven"); Town.doChores(); Pather.useWaypoint(sdk.areas.ColdPlains, true) && Precast.doPrecast(true); @@ -274,7 +274,7 @@ }; /** @param {string} [nick] */ - const smith = function (nick) { + const smith = function smith (nick) { log("starting smith"); if (Misc.findPlayer(nick).level < 8) { log(nick + " you are not eligible for smith. You need to be at least level 8"); @@ -306,7 +306,7 @@ }; /** @param {string} [nick] */ - const radament = function (nick) { + const radament = function radament (nick) { log("starting radament"); /** @@ -420,7 +420,7 @@ return true; }; /** @param {string} [nick] */ - const cube = function (nick) { + const cube = function cube (nick) { if (me.normal) { log("starting cube"); Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); @@ -453,7 +453,7 @@ return true; }; /** @param {string} [nick] */ - const amulet = function (nick) { + const amulet = function amulet (nick) { const exits = [sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2]; log("starting amulet"); Town.doChores(); @@ -487,7 +487,7 @@ return true; }; /** @param {string} [nick] */ - const staff = function (nick) { + const staff = function staff (nick) { log("starting staff"); Town.doChores(); Pather.useWaypoint(sdk.areas.FarOasis, true) && Precast.doPrecast(true); @@ -519,7 +519,7 @@ return true; }; /** @param {string} [nick] */ - const summoner = function (nick) { + const summoner = function summoner (nick) { // right up 25449 5081 (25431, 5011) // left up 25081 5446 (25011, 5446) // right down 25830 5447 (25866, 5431) @@ -601,7 +601,7 @@ return true; }; /** @param {string} [nick] */ - const duriel = function (nick) { + const duriel = function duriel (nick) { log("starting duriel"); if (me.inTown) { @@ -667,15 +667,23 @@ } Pather.useWaypoint(sdk.areas.PalaceCellarLvl1); - Pather.moveToExit([sdk.areas.HaremLvl2, sdk.areas.HaremLvl1], true); + if (!Pather.moveToExit([sdk.areas.HaremLvl2, sdk.areas.HaremLvl1], true)) { + // try again - rate but have seen it fail so safety check here + if (!Pather.moveToExit([sdk.areas.HaremLvl2, sdk.areas.HaremLvl1], true)) { + throw new Error("duriel failed to move to harem"); + } + } Pather.moveTo(10022, 5047); if (AutoRush.rushMode !== RushModes.chanter) { for (let i = 0; i < 3; i++) { + if (!Pather.getPortal(sdk.areas.LutGholein, me.name)) { + Pather.makePortal(); + } log("changeact 3"); let playersMoved = Misc.poll(function () { - return !playersInAct(3); + return playersInAct(3); }, Time.seconds(30), Time.seconds(1)); if (playersMoved) { @@ -693,7 +701,7 @@ return true; }; /** @param {string} [nick] */ - const gidbinn = function (nick) { + const gidbinn = function gidbinn (nick) { log("starting gidbinn"); Town.doChores(); @@ -743,7 +751,7 @@ return true; }; /** @param {string} [nick] */ - const lamesen = function (nick) { + const lamesen = function lamesen (nick) { log("starting lamesen"); if (!Town.goToTown() || !Pather.useWaypoint(sdk.areas.KurastBazaar, true)) { @@ -782,7 +790,7 @@ return true; }; /** @param {string} [nick] */ - const brain = function (nick) { + const brain = function brain (nick) { const exits = [ sdk.areas.FlayerDungeonLvl1, sdk.areas.FlayerDungeonLvl2, @@ -820,7 +828,7 @@ return true; }; /** @param {string} [nick] */ - const eye = function (nick) { + const eye = function eye (nick) { log("starting eye"); Town.doChores(); Pather.useWaypoint(sdk.areas.SpiderForest, true) && Precast.doPrecast(true); @@ -853,7 +861,7 @@ return true; }; /** @param {string} [nick] */ - const heart = function (nick) { + const heart = function heart (nick) { log("starting heart"); Town.doChores(); Pather.useWaypoint(sdk.areas.KurastBazaar, true) && Precast.doPrecast(true); @@ -887,7 +895,7 @@ }; // re-write to prevent fail to complete quest due to killing council from to far away /** @param {string} [nick] */ - const travincal = function (nick) { + const travincal = function travincal (nick) { log("starting travincal"); Town.doChores(); Pather.useWaypoint(sdk.areas.Travincal, true) && Precast.doPrecast(true); @@ -931,7 +939,7 @@ return true; }; /** @param {string} [nick] */ - const mephisto = function (nick) { + const mephisto = function mephisto (nick) { log("starting mephisto"); Town.doChores(); @@ -982,10 +990,10 @@ if (AutoRush.rushMode !== RushModes.chanter) { // allow 3 attempts for (let i = 0; i < 3; i++) { - log("a4"); + log("changeact 4"); let playersMoved = Misc.poll(function () { - return !playersInAct(4); + return playersInAct(4); }, Time.seconds(30), Time.seconds(1)); if (playersMoved) { @@ -1000,7 +1008,7 @@ return true; }; /** @param {string} [nick] */ - const izual = function (nick) { + const izual = function izual (nick) { log("starting izual"); /** @@ -1086,7 +1094,7 @@ return true; }; /** @param {string} [nick] */ - const diablo = function (nick) { + const diablo = function diablo (nick) { log("starting diablo"); function inviteIn () { @@ -1135,10 +1143,10 @@ if (me.expansion && AutoRush.rushMode !== RushModes.chanter) { // allow 3 attempts for (let i = 0; i < 3; i++) { - log("a5"); + log("changeact 5"); let playersMoved = Misc.poll(function () { - return !playersInAct(5); + return playersInAct(5); }, Time.seconds(30), Time.seconds(1)); if (playersMoved) { @@ -1153,7 +1161,7 @@ return true; }; /** @param {string} [nick] */ - const shenk = function (nick) { + const shenk = function shenk (nick) { log("starting shenk"); Pather.useWaypoint(sdk.areas.FrigidHighlands, true) && Precast.doPrecast(false); @@ -1185,7 +1193,7 @@ return true; }; /** @param {string} [nick] */ - const anya = function (nick) { + const anya = function anya (nick) { !me.inTown && Town.goToTown(); log("starting anya"); @@ -1248,7 +1256,7 @@ return true; }; /** @param {string} [nick] */ - const ancients = function (nick) { + const ancients = function ancients (nick) { if (AutoRush.rushMode !== RushModes.chanter) { if (!RushConfig[me.profile].config.Ancients[sdk.difficulty.nameOf(me.diff)]) { if (!RushConfig[me.profile].config.Wps) { @@ -1262,7 +1270,7 @@ if (!bumperCheck(nick)) { if (AutoRush.rushMode === RushModes.chanter) { - log(nick + " you are not eligible for ancients. You need to be at least level " + bumperLvlReq()); + log(nick + " you are not eligible for ancients. You need to be at least level " + getBumperLvlReq()); return false; } @@ -1314,7 +1322,7 @@ return true; }; /** @param {string} [nick] */ - const baal = function (nick) { + const baal = function baal (nick) { if (me.hell && AutoRush.rushMode !== RushModes.chanter) { if (!RushConfig[me.profile].config.Wps) { log("Baal not done in Hell ~Hell rush complete~"); @@ -1338,7 +1346,7 @@ } if (AutoRush.rushMode === RushModes.chanter && !bumperCheck(nick)) { - log(nick + " you are not eligible for baal. You need to be at least level " + bumperLvlReq()); + log(nick + " you are not eligible for baal. You need to be at least level " + getBumperLvlReq()); return false; } @@ -1413,6 +1421,7 @@ playerIn: playerIn, playersInAct: playersInAct, bumperCheck: bumperCheck, + getBumperLvlReq: getBumperLvlReq, andariel: andariel, bloodraven: bloodraven, smith: smith, diff --git a/d2bs/kolbot/libs/systems/autorush/RushConfig.js b/d2bs/kolbot/libs/systems/autorush/RushConfig.js index a3b3935f0..41b9e1573 100644 --- a/d2bs/kolbot/libs/systems/autorush/RushConfig.js +++ b/d2bs/kolbot/libs/systems/autorush/RushConfig.js @@ -6,161 +6,144 @@ */ (function (module) { - /** @enum */ - const RushModes = { - /** The rushee that does the quests */ - quester: 0, - /** The rushee that follows */ - follower: 1, - /** The rushee that bumps the quester */ - bumper: 2, - /** Autorush mode */ - rusher: 3, - /** ControlBot/Chant scripts mode */ - chanter: 4, - /** Manual follow mode - disables some of the bot <-> bot communication we need with auto */ - manual: 5, - }; - - const AutoRush = { - /** Command by rusher to tell players to enter a portal */ - playersIn: "1", - /** Command by rusher to tell players to go back to town */ - playersOut: "2", - allIn: "3", - rushMode: RushModes.rusher, - /** How long to wait for a player to leave/enter an area before ending quest script with failed */ - playerWaitTimeout: Time.minutes(1), - /** controls the order */ - sequences: [ - "cain", - "andariel", - "radament", - "cube", - "amulet", - "staff", - "summoner", - "duriel", - "lamesen", - "travincal", - "mephisto", - "izual", - "diablo", - "shenk", - "anya", - "ancients", - "baal", - "givewps", - ], - }; - - /** @type {Object.} */ + // no touchy - need these imports + const { RushModes } = require("./RushConstants"); + + /** + * This is what you edit. + * + * Each key is a profile name. + * The value shape depends on the selected mode (`type`). + * + * Rushee modes: + * - `RushModes.quester` + * - `RushModes.follower` + * - `RushModes.bumper` + * + * Rusher modes: + * - `RushModes.rusher` + * - `RushModes.manual` + * + * `create` usage variants: + * - Account + character creation: provide `account`, `password`, `charName`, and `charInfo`. + * - Character-only creation: provide only `charName` and `charInfo`. You can skip charName for automatic name generation (uses soloplay's NameGen if available, otherwise will error). + * + * Rusher config defaults: + * - Quest toggles default to `true`. + * - `Wps` defaults to `false`. + * - You do not need to define every `config` property when you want default behavior. + * + * @example + * // Quester with account + character creation + * "quester-profile": { + * type: RushModes.quester, + * startProfiles: ["bumper-profile", "follower-profile", "rusher-profile"], + * create: { + * account: "myacc", + * password: "mypassword", + * charName: "quester", + * charInfo: "scl-sorc", + * }, + * config: { + * Leader: "my-rusher-char", + * }, + * } + * + * @example + * // Follower with character-only creation + * "follower-profile": { + * type: RushModes.follower, + * leader: "quester-profile", + * create: { + * charName: "follower", + * charInfo: "scl-zon", + * }, + * config: { + * Leader: "my-rusher-char", + * }, + * } + * + * @example + * // Bumper with minimal config + * "bumper-profile": { + * type: RushModes.bumper, + * leader: "quester-profile", + * config: { + * Leader: "my-rusher-char", + * }, + * } + * + * @example + * // Rusher with minimal config (uses defaults) + * "rusher-profile": { + * type: RushModes.rusher, + * leader: "quester-profile", + * config: { + * Wps: true, + * LastRun: "baal", + * }, + * } + * + * @example + * // Rusher with explicit full config + * "rusher-profile-full": { + * type: RushModes.rusher, + * leader: "quester-profile", + * playerWaitTimeout: Time.minutes(1), + * config: { + * WaitPlayerCount: 1, + * Cain: true, + * Radament: true, + * LamEsen: true, + * Izual: true, + * Shenk: true, + * Anya: true, + * Ancients: { + * Normal: true, + * Nightmare: true, + * Hell: false, + * }, + * Wps: false, + * LastRun: "", + * }, + * } + * + * @example + * // Manual mode (same config shape as rusher) + * "manual-profile": { + * type: RushModes.manual, + * leader: "quester-profile", + * config: { + * Wps: true, + * }, + * } + * + * @type {Object.} + */ const RushConfig = { - "example-quester": { - type: RushModes.quester, - startProfiles: ["example-bumper"], - /** Optional - Fill this out to create a account/character if it doesn't exist already */ - create: { - account: "testacc", - password: "password", - charName: "quester", - charInfo: "scl-sorc", - } - }, - "example-follower": { - type: RushModes.follower, - leader: "example-quester", - create: { - charName: "follower", - charInfo: "scl-zon", - } - }, - "example-bumper": { - type: RushModes.bumper, - leader: "example-quester", - }, - "example-rusher": { + "liberaloutrun": { type: RushModes.rusher, - leader: "example-quester", + leader: "quester", config: { - WaitPlayerCount: 1, - Cain: true, - Radament: true, - LamEsen: true, - Izual: true, - Shenk: true, - Anya: true, - Ancients: { - Normal: true, - Nightmare: true, - Hell: false, - }, - Wps: false, - LastRun: "", - }, - }, - }; - - const _defaultConfig = { - /** @type {RushModes} */ - type: RushModes.quester, - /** @type {string[]} */ - startProfiles: [], - /** @type {string} */ - leader: "", - /** @type {Object} */ - create: { - /** @type {string} */ - account: "", - /** @type {string} */ - password: "", - /** @type {string} */ - charName: "", - /** - * @type {string} - * @desc Format: "scl-sorc" - "scl" = softcore ladder, "sorc" = sorceress - */ - charInfo: "", + Wps: true, + LastRun: "andariel", + } }, - /** @type {Object} */ - config: { - /** @type {number} */ - WaitPlayerCount: 1, - /** @type {boolean} */ - Cain: true, - /** @type {boolean} */ - Radament: true, - /** @type {boolean} */ - LamEsen: true, - /** @type {boolean} */ - Izual: true, - /** @type {boolean} */ - Shenk: true, - /** @type {boolean} */ - Anya: true, - /** @type {Object} */ - Ancients: { - /** @type {boolean} */ - Normal: true, - /** @type {boolean} */ - Nightmare: true, - /** @type {boolean} */ - Hell: false, + "quester": { + type: RushModes.quester, + startProfiles: ["liberaloutrun"], + create: { + account: "bgrushtesb", + password: "pass", + charInfo: "scl-sorc", }, - /** @type {boolean} */ - Wps: false, - /** @type {string} */ - LastRun: "", + config: { + Leader: "liberaloutrun", + } }, }; - for (let key in RushConfig) { - RushConfig[key] = Object.assign({}, _defaultConfig, RushConfig[key]); - } - module.exports = { - AutoRush: AutoRush, - RushModes: RushModes, RushConfig: RushConfig, }; })(module); diff --git a/d2bs/kolbot/libs/systems/autorush/RushConstants.js b/d2bs/kolbot/libs/systems/autorush/RushConstants.js new file mode 100644 index 000000000..0c6837e34 --- /dev/null +++ b/d2bs/kolbot/libs/systems/autorush/RushConstants.js @@ -0,0 +1,328 @@ +/** +* @filename RushConstants.js +* @author theBGuy +* @desc Constants for AutoRush system - no touchy +* +*/ + +(function (module) { + /** + * @const + */ + const RushModes = { + /** The rushee that does the quests */ + quester: 0, + /** The rushee that follows */ + follower: 1, + /** The rushee that bumps the quester */ + bumper: 2, + /** Autorush mode */ + rusher: 3, + /** ControlBot/Chant scripts mode */ + chanter: 4, + /** Manual follow mode - disables some of the bot <-> bot communication we need with auto */ + manual: 5, + }; + + const AutoRush = { + /** Command by rusher to tell players to enter a portal */ + playersIn: "1", + /** Command by rusher to tell players to go back to town */ + playersOut: "2", + allIn: "3", + rushMode: RushModes.rusher, + /** How long to wait for a player to leave/enter an area before ending quest script with failed */ + playerWaitTimeout: Time.minutes(1), + /** controls the order */ + sequences: [ + "cain", + "andariel", + "radament", + "cube", + "amulet", + "staff", + "summoner", + "duriel", + "lamesen", + "travincal", + "mephisto", + "izual", + "diablo", + "shenk", + "anya", + "ancients", + "baal", + "givewps", + ], + }; + + /** + * @type {Object.} + */ + const sequenceCheck = { + cain: { + check: function () { + return me.cain; + } + }, + andariel: { + check: function () { + return me.andariel; + } + }, + radament: { + check: function () { + return me.radament; + } + }, + cube: { + check: function () { + return !!me.cube; + } + }, + amulet: { + check: function () { + return (me.horadricstaff || me.amulet || me.completestaff); + } + }, + staff: { + check: function () { + return (me.horadricstaff || me.shaft || me.completestaff); + } + }, + summoner: { + check: function () { + return me.summoner; + } + }, + duriel: { + check: function () { + return me.duriel; + } + }, + lamesen: { + check: function () { + return me.lamessen; + } + }, + travincal: { + check: function () { + return me.travincal; + } + }, + mephisto: { + check: function () { + return me.mephisto; + } + }, + izual: { + check: function () { + return me.izual; + } + }, + diablo: { + check: function () { + return me.diablo; + } + }, + shenk: { + check: function () { + return me.shenk; + } + }, + anya: { + check: function () { + return me.anya || Misc.checkQuest(sdk.quest.id.PrisonofIce, 8/** Recieved the scroll */); + } + }, + ancients: { + check: function () { + return me.ancients; + } + }, + baal: { + check: function () { + return me.baal; + } + }, + }; + + const _defaultRusheeConfig = { + /** @type {RushModes} */ + type: RushModes.quester, + /** @type {string[]} */ + startProfiles: [], + leader: "", + create: { + /** @type {string} */ + account: "", + /** @type {string} */ + password: "", + /** @type {string} */ + charName: "", + /** + * @type {string} + * @desc Format: "scl-sorc" - "scl" = softcore ladder, "sorc" = sorceress + */ + charInfo: "", + }, + config: { + /** Rusher in game character name */ + Leader: "", + } + }; + + const _defaultRusherConfig = { + /** @type {RushModes.rusher} */ + type: RushModes.rusher, + /** How long to wait for a player to leave/enter an area before ending quest script with failed */ + playerWaitTimeout: Time.minutes(1), + config: { + WaitPlayerCount: 1, + Cain: true, + Radament: true, + LamEsen: true, + Izual: true, + Shenk: true, + Anya: true, + Ancients: { + Normal: true, + Nightmare: true, + Hell: false, + }, + Wps: false, + LastRun: "", + }, + /** @type {string[]} */ + startProfiles: [], + allowPickit: true, + }; + + /** + * Merge defaults into a single rush profile while preserving nested defaults. + * Mutates the passed profile so other consumers of RushConfig[me.profile] see normalized values. + * @param {DefaultConfig} rushProfile + * @returns {DefaultConfig} + */ + function normalizeRushProfileConfig (rushProfile) { + let mergedProfile; + + if (rushProfile.type === RushModes.rusher) { + mergedProfile = Object.assign({}, _defaultRusherConfig, rushProfile); + mergedProfile.config = Object.assign( + {}, + _defaultRusherConfig.config, + rushProfile.config ? rushProfile.config : {} + ); + mergedProfile.config.Ancients = Object.assign( + {}, + _defaultRusherConfig.config.Ancients, + rushProfile.config && rushProfile.config.Ancients ? rushProfile.config.Ancients : {} + ); + } else { + mergedProfile = Object.assign({}, _defaultRusheeConfig, rushProfile); + mergedProfile.create = Object.assign( + {}, + _defaultRusheeConfig.create, + rushProfile.create ? rushProfile.create : {} + ); + // Keep existing access pattern in rushConfigInit where Config.Leader reads rushProfile.config.Leader. + mergedProfile.config = Object.assign({}, rushProfile.config ? rushProfile.config : {}); + } + + mergedProfile.startProfiles = Array.isArray(mergedProfile.startProfiles) + ? mergedProfile.startProfiles.slice(0) + : []; + + Object.keys(rushProfile).forEach(function (key) { + delete rushProfile[key]; + }); + + Object.assign(rushProfile, mergedProfile); + + return rushProfile; + } + + /** @param {DefaultConfig} rushProfile */ + function rushConfigInit (rushProfile) { + if (!rushProfile) { + throw new Error("No rush config found for this profile. Please create one in RushConfig.js"); + } + + normalizeRushProfileConfig(rushProfile); + + // disabling all other scripts so nothing interfers + Object.keys(Scripts).forEach(function (script) { + Scripts[script] = false; + }); + + if (rushProfile.type === RushModes.rusher) { + Scripts.Rusher = true; + if (!rushProfile.allowPickit) { + Pickit.enabled = false; + } + } else { + Scripts.Rushee = true; + Config.Leader = rushProfile.config.Leader + ? rushProfile.config.Leader + : /** TODO: iterate config & try to find rusher profile with our game leader */ rushProfile.rusherProfile + ? (function () { + // we've been passed the rushers profile name so lets try to determine thier in game name + if (FileTools.exists("data/" + rushProfile.rusherProfile + ".json")) { + let string = FileAction.read("data/" + rushProfile.rusherProfile + ".json"); + + if (string) { + let obj = JSON.parse(string); + + if (obj && obj.hasOwnProperty("name")) { + return obj.name; + } + } + } + return ""; + })() + : ""; + Pickit.enabled = false; + } + + const { RushConfig } = require("./RushConfig"); + + // everyone except main rushee + if (me.profile !== rushProfile.leader) { + Config.QuitList.push(rushProfile.leader); + } else { + Object.keys(RushConfig).forEach(function (profile) { + if ( + // is a rusher type profile + RushConfig[profile].type === RushModes.rusher + // we are thier game maker + && RushConfig[profile].leader === me.profile + ) { + Config.QuitList.push(profile); + } + }); + } + Config.QuitList = Config.QuitList.filter(function (el) { + return !!el; + }); + + Config.QuitListMode = 1; + !Config.PublicMode && (Config.PublicMode = true); + !Config.LocalChat.Enabled && (Config.LocalChat.Enabled = true); + if (Config.AttackSkill[1] < 0 || Config.AttackSkill[3] < 0) { + // so we don't get the warning + Config.AttackSkill[1] = 0; + Config.AttackSkill[3] = 0; + } + Config.MFLeader = false; + Config.Gamble = false; + } + + module.exports = { + RushModes: RushModes, + AutoRush: AutoRush, + sequenceCheck: sequenceCheck, + normalizeRushProfileConfig: normalizeRushProfileConfig, + rushConfigInit: rushConfigInit, + }; +})(typeof module === "undefined" ? this.AutoRushConstants = {} : module); diff --git a/d2bs/kolbot/libs/systems/autorush/index.d.ts b/d2bs/kolbot/libs/systems/autorush/index.d.ts index 1bbfc7b12..5813edeab 100644 --- a/d2bs/kolbot/libs/systems/autorush/index.d.ts +++ b/d2bs/kolbot/libs/systems/autorush/index.d.ts @@ -7,8 +7,8 @@ declare global { chanter = 4, manual = 5, } - type DefaultConfig = { - type: RushModes; + type RusheeConfig = { + type: RushModes.bumper | RushModes.quester | RushModes.follower; startProfiles: string[]; leader: string; create: { @@ -20,6 +20,15 @@ declare global { */ charInfo: string; }; + config: { + /** Rusher in game character name */ + Leader: string; + }; + }; + type RusherConfig = { + type: RushModes.rusher | RushModes.manual; + /** How long to wait for a player to leave/enter an area before ending quest script with failed */ + playerWaitTimeout: number; config: { WaitPlayerCount: number; Cain: boolean; @@ -36,7 +45,9 @@ declare global { Wps: boolean; LastRun: string; }; + startProfiles: string[]; }; + type DefaultConfig = RusheeConfig | RusherConfig; type RushConfig = { [key: string]: DefaultConfig; }; diff --git a/d2bs/kolbot/sdk/types/Pickit.d.ts b/d2bs/kolbot/sdk/types/Pickit.d.ts index fe16a900f..908d5d148 100644 --- a/d2bs/kolbot/sdk/types/Pickit.d.ts +++ b/d2bs/kolbot/sdk/types/Pickit.d.ts @@ -11,6 +11,7 @@ declare global { UTILITY: 6 }; namespace Pickit { + const enabled: boolean; const gidList: number[]; let invoLocked: boolean; let beltSize: 1 | 2 | 3 | 4; diff --git a/d2bs/kolbot/threads/RushThread.js b/d2bs/kolbot/threads/RushThread.js index 7acd95457..f7062ed33 100644 --- a/d2bs/kolbot/threads/RushThread.js +++ b/d2bs/kolbot/threads/RushThread.js @@ -16,1049 +16,255 @@ includeSystemLibs(); include("systems/mulelogger/MuleLogger.js"); include("systems/gameaction/GameAction.js"); -const Overrides = require("../libs/modules/Override"); - -let count = 0; -let silentNameTracker = []; -let wpsToGive = Pather.nonTownWpAreas.slice(0).filter(function (area) { - if (area === sdk.areas.HallsofPain) return false; - if (me.classic && area >= sdk.areas.Harrogath) return false; - return true; -}); - -function wpEvent (who, msg) { - if (typeof msg === "string" && msg === "gotwp" || msg === "Failed to get wp") { - count++; - !silentNameTracker.includes(who) && silentNameTracker.push(who); - } -} - -function giveWP () { - let wp = Game.getObject("waypoint"); - let success = false; - if (wp && !me.inTown && wpsToGive.includes(me.area)) { - try { - addEventListener("chatmsg", wpEvent); - let playerCount = Misc.getPartyCount(); - let mobCount = getUnits(sdk.unittype.Monster).filter(mon => mon.distance <= 15 && mon.attackable).length; - mobCount > 0 && Attack.securePosition(me.x, me.y, { range: 15, duration: Time.seconds(30), skipBlocked: true }); - wp.distance > 5 && Pather.moveToUnit(wp); - Pather.makePortal(); - say("wp"); - let tick = getTickCount(); - while (getTickCount() - tick < Time.minutes(2)) { - let player = Game.getPlayer(); - if (player) { - do { - if (player.name !== me.name && !silentNameTracker.includes(player.name)) { - silentNameTracker.push(player.name); - } - } while (player.getNext()); - } - if (count === playerCount || (silentNameTracker.length === playerCount && Misc.getNearbyPlayerCount() === 0)) { - wpsToGive.remove(me.area); - success = true; - break; - } - delay(50); - } - } catch (e) { - console.error(e); - Config.LocalChat.Enabled && say("Failed to give wp"); - } finally { - removeEventListener("chatmsg", wpEvent); - silentNameTracker = []; - count = 0; - } - return success; - } - - return false; -} - -new Overrides.Override(Pather, Pather.useWaypoint, function(orignal, targetArea, check) { - if (orignal(targetArea, check)) { - return (Config.Rusher.GiveWps && giveWP()) || true; - } else { - console.log("failed"); - - return false; - } -}).apply(); function main () { - let tick; - - this.log = function (msg = "", sayMsg = true) { - console.log(msg); - sayMsg && say(msg); - }; - - this.playerIn = function (area) { - !area && (area = me.area); - - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name && party.area === area) { - return true; - } - } while (party.getNext()); - } - - return false; - }; - - this.bumperCheck = function () { - let bumperLevelReq = [20, 40, 60][me.diff]; - return Misc.checkPartyLevel(bumperLevelReq); - }; - - this.playersInAct = function (act) { - !act && (act = me.act); - - let area = sdk.areas.townOfAct(act); - let party = getParty(); - - if (party) { - do { - if (party.name !== me.name && party.area !== area) { - return false; - } - } while (party.getNext()); - } - - return true; - }; - - this.andariel = function () { - this.log("starting andariel"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.CatacombsLvl2, true) && Precast.doPrecast(true); - - if (!Pather.moveToExit([sdk.areas.CatacombsLvl3, sdk.areas.CatacombsLvl4], true) - || !Pather.moveTo(22582, 9612)) { - throw new Error("andy failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); - this.log("1"); - - while (!this.playerIn()) { - Pather.moveTo(22582, 9612); - delay(250); - } - - Attack.kill(sdk.monsters.Andariel); - this.log("2"); - Pather.moveTo(22582, 9612); - - while (this.playerIn()) { - delay(250); - } - - Pather.usePortal(null, me.name); - this.log("a2"); - Town.goToTown(2); - - while (!this.playersInAct(2)) { - delay(250); - } - - return true; - }; - - this.cube = function () { - if (me.normal) { - this.log("starting cube"); - Pather.useWaypoint(sdk.areas.HallsoftheDeadLvl2, true); - Precast.doPrecast(true); - - if (!Pather.moveToExit(sdk.areas.HallsoftheDeadLvl3, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricCubeChest)) { - throw new Error("cube failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); - this.log("1"); - - while (!this.playerIn()) { - delay(100); - } - - while (this.playerIn()) { - delay(100); - } - - Pather.usePortal(null, me.name); - } - - return true; - }; - - this.amulet = function () { - this.log("starting amulet"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.LostCity, true) && Precast.doPrecast(true); - - if (!Pather.moveToExit([sdk.areas.ValleyofSnakes, sdk.areas.ClawViperTempleLvl1, sdk.areas.ClawViperTempleLvl2], true) - || !Pather.moveTo(15044, 14045)) { - throw new Error("amulet failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, { range: 25, duration: 3000, skipBlocked: me.hell, useRedemption: me.hell }); - - this.log("1"); - - while (!this.playerIn()) { - delay(100); - } - - while (this.playerIn()) { - delay(100); - } - - Pather.usePortal(null, me.name); - - return true; - }; - - this.staff = function () { - this.log("starting staff"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.FarOasis, true) && Precast.doPrecast(true); - - if (!Pather.moveToExit([sdk.areas.MaggotLairLvl1, sdk.areas.MaggotLairLvl2, sdk.areas.MaggotLairLvl3], true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.ShaftoftheHoradricStaffChest)) { - throw new Error("staff failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true }); - this.log("1"); - - while (!this.playerIn()) { - delay(100); - } - - while (this.playerIn()) { - delay(100); - } + const { + log, + andariel, + cube, + amulet, + staff, + summoner, + duriel, + travincal, + mephisto, + diablo, + ancients, + baal, + cain, + radament, + lamesen, + izual, + shenk, + anya, + } = require("../libs/systems/autorush/AutoRush"); + const { AutoRush } = require("../libs/systems/autorush/RushConstants"); + const { RushConfig } = require("../libs/systems/autorush/RushConfig"); + /** @type {RusherConfig} */ + const rushProfile = RushConfig[me.profile]; + + if (!rushProfile) { + throw new Error("No rush config found for this profile. Please create one in RushConfig.js"); + } - Pather.usePortal(null, me.name); + const Overrides = require("../libs/modules/Override"); + let count = 0; + let silentNameTracker = []; + let wpsToGive = Pather.nonTownWpAreas.slice(0).filter(function (area) { + if (area === sdk.areas.HallsofPain) return false; + if (me.classic && area >= sdk.areas.Harrogath) return false; return true; - }; - - this.summoner = function () { - // right up 25449 5081 (25431, 5011) - // left up 25081 5446 (25011, 5446) - // right down 25830 5447 (25866, 5431) - // left down 25447 5822 (25431, 5861) + }); - this.log("starting summoner"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.ArcaneSanctuary, true) && Precast.doPrecast(true); - - let preset = Game.getPresetObject(sdk.areas.ArcaneSanctuary, sdk.quest.chest.Journal); - let spot = {}; - - switch (preset.roomx * 5 + preset.x) { - case 25011: - spot = { x: 25081, y: 5446 }; - break; - case 25866: - spot = { x: 25830, y: 5447 }; - break; - case 25431: - switch (preset.roomy * 5 + preset.y) { - case 5011: - spot = { x: 25449, y: 5081 }; - break; - case 5861: - spot = { x: 25447, y: 5822 }; - break; - } - - break; + function wpEvent (who, msg) { + if (typeof msg === "string" && msg === "gotwp" || msg === "Failed to get wp" || msg === "alreadyhave") { + count++; + !silentNameTracker.includes(who) && silentNameTracker.push(who); } + } - if (!Pather.moveToUnit(spot)) { - throw new Error("summoner failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, { range: 25, duration: 3000 }); - this.log("1"); - - while (!this.playerIn()) { - Pather.moveToUnit(spot); - Attack.securePosition(me.x, me.y, { range: 25, duration: 500 }); - delay(250); - } - - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); - Attack.kill(sdk.monsters.Summoner); - this.log("2"); - - while (this.playerIn()) { - delay(100); - } - - Pickit.pickItems(); - Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.Journal); - - let redPortal = Game.getObject(sdk.objects.RedPortal); - - if (!redPortal || !this.usePortal(null, null, redPortal)) { - if (!Misc.poll(() => { - let journal = Game.getObject(sdk.quest.chest.Journal); - - if (journal && journal.interact()) { - delay(1000); - me.cancel(); + function giveWP () { + let wp = Game.getObject("waypoint"); + let success = false; + + if (wp && !me.inTown && wpsToGive.includes(me.area)) { + try { + addEventListener("chatmsg", wpEvent); + let playerCount = Misc.getPartyCount(); + + if (me.getMobCount(15) > 0) { + Attack.securePosition(me.x, me.y, { range: 15, duration: Time.seconds(30), skipBlocked: true }); } - redPortal = Pather.getPortal(sdk.areas.CanyonofMagic); - - return (redPortal && Pather.usePortal(null, null, redPortal)); - })) throw new Error("summoner failed"); - } - - return true; - }; - - this.duriel = function () { - this.log("starting duriel"); - - if (me.inTown) { - Town.doChores(); - Pather.useWaypoint(sdk.areas.CanyonofMagic, true); - } else { - giveWP(); - } - - Precast.doPrecast(true); - - if (!Pather.moveToExit(getRoom().correcttomb, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.HoradricStaffHolder)) { - throw new Error("duriel failed"); - } - - Pather.makePortal(); - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000, skipBlocked: true, useRedemption: me.hell }); - this.log("1"); - - while (!this.playerIn()) { - delay(100); - } - - while (this.playerIn()) { - delay(100); - } - - while (!Game.getObject(sdk.objects.PortaltoDurielsLair)) { - delay(500); - } - - Pather.useUnit(sdk.unittype.Object, sdk.objects.PortaltoDurielsLair, sdk.areas.DurielsLair); - Attack.kill(sdk.monsters.Duriel); - Pickit.pickItems(); - - Pather.teleport = false; - - Pather.moveTo(22579, 15706); - - Pather.teleport = true; - - Pather.moveTo(22577, 15649, 10); - Pather.moveTo(22577, 15609, 10); - Pather.makePortal(); - this.log("1"); - - while (!this.playerIn()) { - delay(100); - } - - if (!Pather.usePortal(null, me.name)) { - Town.goToTown(); - } - - Pather.useWaypoint(sdk.areas.PalaceCellarLvl1); - Pather.moveToExit([sdk.areas.HaremLvl2, sdk.areas.HaremLvl1], true); - Pather.moveTo(10022, 5047); - this.log("a3"); - Town.goToTown(3); - Town.doChores(); - - while (!this.playersInAct(3)) { - delay(250); - } - - return true; - }; - - // re-write to prevent fail to complete quest due to killing council from to far away - this.travincal = function () { - this.log("starting travincal"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.Travincal, true) && Precast.doPrecast(true); - - let coords = [me.x, me.y]; - - Pather.moveTo(coords[0] + 23, coords[1] - 102); - Pather.makePortal(); - Attack.securePosition(me.x, me.y, { range: 40, duration: 3000 }); - this.log("1"); - - while (!this.playerIn()) { - delay(250); - } - - Pather.moveTo(coords[0] + 30, coords[1] - 134); - Pather.moveTo(coords[0] + 86, coords[1] - 130); - Pather.moveTo(coords[0] + 71, coords[1] - 94); - Attack.securePosition(me.x, me.y, { range: 40, duration: 3000 }); - - Pather.moveTo(coords[0] + 23, coords[1] - 102); - Pather.makePortal(); - this.log("2"); - Pather.usePortal(null, me.name); - - return true; - }; - - this.mephisto = function () { - this.log("starting mephisto"); - - Town.doChores(); - Pather.useWaypoint(sdk.areas.DuranceofHateLvl2, true) && Precast.doPrecast(true); - Pather.moveToExit(sdk.areas.DuranceofHateLvl3, true) && Pather.moveTo(17692, 8023) && Pather.makePortal(); - delay(2000); - this.log("1"); - - while (!this.playerIn()) { - delay(250); - } - - Pather.moveTo(17591, 8070); - Attack.kill(sdk.monsters.Mephisto); - Pickit.pickItems(); - Pather.moveTo(17692, 8023) && Pather.makePortal(); - this.log("2"); - - while (this.playerIn()) { - delay(250); - } - - Pather.moveTo(17591, 8070) && Attack.securePosition(me.x, me.y, { range: 40, duration: 3000 }); - - let hydra = Game.getMonster(getLocaleString(sdk.locale.monsters.Hydra)); - - if (hydra) { - do { - while (!hydra.dead && hydra.hp > 0) { - delay(500); + wp.distance > 5 && Pather.moveToUnit(wp); + Pather.makePortal(); + say("wp"); + let tick = getTickCount(); + while (getTickCount() - tick < Time.minutes(2)) { + let player = Game.getPlayer(); + if (player) { + do { + if (player.name !== me.name && !silentNameTracker.includes(player.name)) { + silentNameTracker.push(player.name); + } + } while (player.getNext()); + } + if (count === playerCount || (silentNameTracker.length === playerCount && Misc.getNearbyPlayerCount() === 0)) { + wpsToGive.remove(me.area); + success = true; + break; + } + delay(50); } - } while (hydra.getNext()); - } - - Pather.makePortal(); - Pather.moveTo(17581, 8070); - this.log("1"); - - while (!this.playerIn()) { - delay(250); - } - - this.log("a4"); - - while (!this.playersInAct(4)) { - delay(250); - } - - delay(2000); - Pather.usePortal(null); - - return true; - }; - - this.diablo = function () { - this.log("starting diablo"); - - function inviteIn () { - Pather.moveTo(7763, 5267) && Pather.makePortal(); - Pather.moveTo(7727, 5267); - this.log("1"); - - while (!this.playerIn()) { - delay(250); - } - - return true; - } - - Town.doChores(); - Pather.useWaypoint(sdk.areas.RiverofFlame); - Precast.doPrecast(true); - if (!Pather.moveToExit(sdk.areas.ChaosSanctuary, true) && !Pather.moveTo(7790, 5544)) throw new Error("Failed to move to Chaos Sanctuary"); - - Common.Diablo.initLayout(); - Config.Diablo.Fast = true; - Config.Diablo.SealLeader = false; - - try { - Common.Diablo.runSeals(Config.Diablo.SealOrder); - console.log("Attempting to find Diablo"); - inviteIn() && Common.Diablo.diabloPrep(); - } catch (error) { - console.log("Diablo wasn't found. Checking seals."); - Common.Diablo.runSeals(Config.Diablo.SealOrder); - inviteIn() && Common.Diablo.diabloPrep(); - } - - Attack.kill(sdk.monsters.Diablo); - this.log("2"); - - if (me.expansion) { - this.log("a5"); - - while (!this.playersInAct(5)) { - delay(250); - } - } - - Pickit.pickItems(); - !Pather.usePortal(null, me.name) && Town.goToTown(); - - return true; - }; - - this.ancients = function () { - if (me.hell && !Config.Rusher.HellAncients) { - if (!Config.Rusher.GiveWps) { - this.log("Hell rush complete~"); - delay(500); - quit(); + } catch (e) { + console.error(e); + Config.LocalChat.Enabled && say("Failed to give wp"); + } finally { + removeEventListener("chatmsg", wpEvent); + silentNameTracker = []; + count = 0; } - - return false; + return success; } - if (!this.bumperCheck()) { - if (!Config.Rusher.GiveWps) { - this.log("No eligible bumpers detected. Rush complete~"); - delay(500); - quit(); - } + return false; + } + new Overrides.Override(Pather, Pather.useWaypoint, function(orignal, targetArea, check) { + if (orignal(targetArea, check)) { + return (rushProfile.config.Wps && giveWP()) || true; + } else { + console.log("failed"); + return false; } + }).apply(); - this.log("starting ancients"); - - Town.doChores(); - Pather.useWaypoint(sdk.areas.AncientsWay, true) && Precast.doPrecast(true); - - if (!Pather.moveToExit(sdk.areas.ArreatSummit, true)) { - throw new Error("Failed to go to Ancients way."); - } - - Pather.moveTo(10089, 12622); - Pather.makePortal(); - this.log("3"); - - while (!this.playerIn()) { - delay(250); - } - - Pather.moveTo(10048, 12628); - Common.Ancients.touchAltar(); - Common.Ancients.startAncients(); - - Pather.moveTo(10089, 12622); - me.cancel(); - Pather.makePortal(); - - while (this.playerIn()) { - delay(100); - } - - !Pather.usePortal(null, me.name) && Town.goToTown(); - - return true; + const clearArea = function (area) { + Pather.journeyTo(area); + Attack.clearLevel(0); + log("Done clearing area: " + area); }; - this.baal = function () { - if (me.hell) { - if (!Config.Rusher.GiveWps) { - this.log("Baal not done in Hell ~Hell rush complete~"); - delay(500); - quit(); - } - wpsToGive.remove(sdk.areas.WorldstoneLvl2); - - return false; - } - - if (!this.bumperCheck()) { - if (!Config.Rusher.GiveWps) { - this.log("No eligible bumpers detected. ~Rush complete~"); - delay(500); - quit(); - } - wpsToGive.remove(sdk.areas.WorldstoneLvl2); - - return false; - } - - this.log("starting baal"); - - if (me.inTown) { - Town.doChores(); - Pather.useWaypoint(sdk.areas.WorldstoneLvl2) && Precast.doPrecast(true); + const givewps = function () { + if (!rushProfile.config.Wps) return false; - if (!Pather.moveToExit([sdk.areas.WorldstoneLvl3, sdk.areas.ThroneofDestruction], true)) { - throw new Error("Failed to move to Throne of Destruction."); - } - } - - Pather.moveTo(15113, 5040); - Attack.clear(15); - Common.Baal.clearThrone(); - - if (!Common.Baal.clearWaves()) { - throw new Error("Couldn't clear baal waves"); - } - - Common.Baal.clearThrone(); - me.checkForMobs({ range: 30 }) && this.clearWaves(); // ensure waves are actually done - Pather.moveTo(15090, 5008); - delay(5000); - Precast.doPrecast(true); - Misc.poll(() => !Game.getMonster(sdk.monsters.ThroneBaal), Time.minutes(3), 1000); - - let portal = Game.getObject(sdk.objects.WorldstonePortal); + say("wpinfo"); - if (portal) { - Pather.usePortal(null, null, portal); - } else { - throw new Error("Couldn't find portal."); - } + Misc.poll(function () { + return gotWpInfo; + }, Time.minutes(3), 1000); - Pather.moveTo(15213, 5908); - Pather.makePortal(); - Pather.moveTo(15170, 5950); - delay(1000); - this.log("3"); - - while (!this.playerIn()) { - delay(250); - } + log("Starting wps to give: " + wpsToGive.length); + let wpsLeft = wpsToGive.slice(0); + console.log(JSON.stringify(wpsLeft)); - Pather.moveTo(15134, 5923); - Attack.kill(sdk.monsters.Baal); - Pickit.pickItems(); + wpsLeft.forEach(function (wp) { + me.checkScrolls(sdk.items.TomeofTownPortal) <= 5 && (Pather.useWaypoint(sdk.areas.townOf(me.area)) || Town.goToTown()) && Town.doChores(); + Pather.useWaypoint(wp); + }); return true; }; - this.clearArea = function (area) { - Pather.journeyTo(area); - Attack.clearLevel(0); - this.log("Done clearing area: " + area); - }; - - // Quests - this.cain = function () { - if (!Config.Rusher.Cain) return true; - - this.log("starting cain"); - Town.doChores(); - Pather.useWaypoint(sdk.areas.DarkWood, true) && Precast.doPrecast(true); - - if (!Pather.moveToPreset(sdk.areas.DarkWood, sdk.unittype.Object, sdk.quest.chest.InifussTree, 5, 5)) { - throw new Error("Failed to move to Tree of Inifuss"); - } + console.log(sdk.colors.LightGold + "Loading RushThread"); - let tree = Game.getObject(sdk.quest.chest.InifussTree); - !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); - !!tree && tree.distance > 5 && Pather.moveToUnit(tree); - Pather.makePortal(); - this.log("1"); - tick = getTickCount(); + let command = ""; + let current = 0; + let questerName = ""; + let gotWpInfo = false; + + const sequences = [ + cain, + andariel, + radament, + cube, + amulet, + staff, + summoner, + duriel, + lamesen, + travincal, + mephisto, + izual, + diablo, + shenk, + anya, + ancients, + baal, + // givewps + ]; - while (getTickCount() - tick < Time.minutes(2)) { - if (tree.mode) { + const scriptEvent = function (msg) { + if (typeof msg === "string") { + if (!msg.startsWith("rush-")) return; + command = msg.substring(5); + } else if ( + isType(msg, "object") + && Object.hasOwn(msg, "type") + && msg.type === "rush" + ) { + switch (msg.action) { + case "highestquest": + questerName = msg.data.quester; + // hacky but don't feel like changing orginal logic in handleRushCommand right now + command = "highestquest " + msg.data.highestquest; break; - } - Attack.securePosition(me.x, me.y, { range: 20, duration: 1000 }); - } - - Pather.usePortal(1) || Town.goToTown(); - Pather.useWaypoint(sdk.areas.StonyField, true); - Precast.doPrecast(true); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Monster, sdk.monsters.preset.Rakanishu, 10, 10, false, true); - Attack.securePosition(me.x, me.y, { range: 40, duration: 3000, skipBlocked: true }); - Pather.moveToPreset(sdk.areas.StonyField, sdk.unittype.Object, sdk.quest.chest.StoneAlpha, null, null, true); - Pather.makePortal(); - this.log("1"); - - tick = getTickCount(); - - while (getTickCount() - tick < Time.minutes(2)) { - if (Pather.usePortal(sdk.areas.Tristram)) { + case "wps": + wpsToGive = msg.data.wps; + gotWpInfo = true; break; } - Attack.securePosition(me.x, me.y, { range: 35, duration: 1000 }); } + }; - if (me.inArea(sdk.areas.Tristram)) { - Pather.moveTo(me.x, me.y + 6); - let gibbet = Game.getObject(sdk.quest.chest.CainsJail); + addEventListener("scriptmsg", scriptEvent); - if (gibbet && !gibbet.mode) { - if (!Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.CainsJail, 0, 0, true, true)) { - throw new Error("Failed to move to Cain's Jail"); - } + /** @param {string} msg */ + const handleRushCommand = function (msg) { + if (!isType(msg, "string")) return; - Attack.securePosition(gibbet.x, gibbet.y, { range: 20, duration: 3000 }); - Pather.makePortal(); - this.log("1"); + let [action, info] = msg.toLowerCase().split(" "); + console.log("Received rush command: " + action + " info: " + info); - tick = getTickCount(); + if (action) { + if (action === "skiptoact") { + if (!isNaN(parseInt(info, 10))) { + switch (parseInt(info, 10)) { + case 2: + current = sequences.findIndex((s) => s.name === "andariel") + 1; + Town.goToTown(2); - while (getTickCount() - tick < Time.minutes(2)) { - if (gibbet.mode) { break; - } - Attack.securePosition(me.x, me.y, { range: 10, duration: 1000 }); - } - } - } - - return true; - }; - - this.radament = function () { - if (!Config.Rusher.Radament) return false; + case 3: + current = sequences.findIndex((s) => s.name === "duriel") + 1; + Town.goToTown(3); - this.log("starting radament"); - - let moveIntoPos = function (unit, range) { - let coords = []; - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 105, -105, 120, -120, 135, -135, 150, -150, 180]; + break; + case 4: + current = sequences.findIndex((s) => s.name === "mephisto") + 1; + Town.goToTown(4); - for (let i = 0; i < angles.length; i += 1) { - let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); - let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); + break; + case 5: + current = sequences.findIndex((s) => s.name === "diablo") + 1; + Town.goToTown(5); - try { - if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { - coords.push({ - x: coordx, - y: coordy - }); + break; } - } catch (e) { - continue; } - } - - if (coords.length > 0) { - coords.sort(Sort.units); - - return Pather.moveToUnit(coords[0]); - } - - return false; - }; - - Pather.useWaypoint(sdk.areas.A2SewersLvl2, true) && Precast.doPrecast(false); - Pather.moveToExit(sdk.areas.A2SewersLvl3, true); - - let radaPreset = Game.getPresetObject(sdk.areas.A2SewersLvl3, sdk.quest.chest.HoradricScrollChest); - let radaCoords = { - area: sdk.areas.A2SewersLvl3, - x: radaPreset.roomx * 5 + radaPreset.x, - y: radaPreset.roomy * 5 + radaPreset.y - }; - - moveIntoPos(radaCoords, 50); - let rada = Misc.poll(() => Game.getMonster(sdk.monsters.Radament), 1500, 500); - - rada ? moveIntoPos(rada, 60) : console.log("radament unit not found"); - Attack.securePosition(me.x, me.y, { range: 35, duration: 3000 }); - Pather.makePortal(); - this.log("1"); - - while (!this.playerIn()) { - delay(200); - } - - Attack.kill(sdk.monsters.Radament); - - let returnSpot = { - x: me.x, - y: me.y - }; - - this.log("2"); - Pickit.pickItems(); - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); - while (this.playerIn()) { - delay(200); - } - - Pather.moveToUnit(returnSpot); - Pather.makePortal(); - this.log("all in"); - - while (!this.playerIn()) { - delay(200); - } - - Misc.poll(() => !Game.getItem(sdk.quest.item.BookofSkill), 30000, 1000); - - while (this.playerIn()) { - delay(200); - } - - Pather.usePortal(null, null); - - return true; - }; - - this.lamesen = function () { - if (!Config.Rusher.LamEsen) return false; - - this.log("starting lamesen"); - - if (!Town.goToTown() || !Pather.useWaypoint(sdk.areas.KurastBazaar, true)) { - throw new Error("Lam Essen quest failed"); - } - - Precast.doPrecast(false); - - if (!Pather.moveToExit(sdk.areas.RuinedTemple, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.quest.chest.LamEsensTomeHolder)) { - throw new Error("Lam Essen quest failed"); - } - - Attack.securePosition(me.x, me.y, { range: 30, duration: 2000 }); - Pather.makePortal(); - this.log("1"); - - while (!this.playerIn()) { - delay(200); - } - - while (this.playerIn()) { - delay(200); - } - - Pather.usePortal(null, null); - - return true; - }; + command = ""; + } else if (action === "clear") { + clearArea(Number(info)); + Town.goToTown(); - this.izual = function () { - if (!Config.Rusher.Izual) return false; - - this.log("starting izual"); - - let moveIntoPos = function (unit, range) { - let coords = []; - let angle = Math.round(Math.atan2(me.y - unit.y, me.x - unit.x) * 180 / Math.PI); - let angles = [0, 15, -15, 30, -30, 45, -45, 60, -60, 75, -75, 90, -90, 105, -105, 120, -120, 135, -135, 150, -150, 180]; - - for (let i = 0; i < angles.length; i += 1) { - let coordx = Math.round((Math.cos((angle + angles[i]) * Math.PI / 180)) * range + unit.x); - let coordy = Math.round((Math.sin((angle + angles[i]) * Math.PI / 180)) * range + unit.y); - - try { - if (!(getCollision(unit.area, coordx, coordy) & 0x1)) { - coords.push({ - x: coordx, - y: coordy - }); - } - } catch (e) { - continue; - } + command = "go"; + } else if (action === "highestquest" && info) { + let foundIdx = sequences.findIndex(function (s) { + return s.name === info; + }); + console.log("highestquest idx: " + foundIdx); + foundIdx > -1 && (current = foundIdx + 1); + + command = ""; } + } else { + let foundIdx = sequences.findIndex(function (s) { + return s.name.toLowerCase() === command.toLowerCase(); + }); - if (coords.length > 0) { - coords.sort(Sort.units); - - return Pather.moveToUnit(coords[0]); + if (foundIdx > -1) { + current = foundIdx; } - return false; - }; - - Pather.useWaypoint(sdk.areas.CityoftheDamned, true) && Precast.doPrecast(false); - Pather.moveToExit(sdk.areas.PlainsofDespair, true); - - let izualPreset = Game.getPresetMonster(sdk.areas.PlainsofDespair, sdk.monsters.Izual); - let izualCoords = { - area: sdk.areas.PlainsofDespair, - x: izualPreset.roomx * 5 + izualPreset.x, - y: izualPreset.roomy * 5 + izualPreset.y - }; - - moveIntoPos(izualCoords, 50); - let izual = Misc.poll(() => Game.getMonster(sdk.monsters.Izual), 1500, 500); - - izual ? moveIntoPos(izual, 60) : console.log("izual unit not found"); - - let returnSpot = { - x: me.x, - y: me.y - }; - - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); - Pather.makePortal(); - this.log("1"); - - while (!this.playerIn()) { - delay(200); - } - - Attack.kill(sdk.monsters.Izual); - Pickit.pickItems(); - this.log("2"); - Pather.moveToUnit(returnSpot); - - while (this.playerIn()) { - delay(200); - } - - Pather.usePortal(null, null); - - return true; - }; - - this.shenk = function () { - if (!Config.Rusher.Shenk) return false; - - this.log("starting shenk"); - - Pather.useWaypoint(sdk.areas.FrigidHighlands, true) && Precast.doPrecast(false); - Pather.moveTo(3846, 5120); - Attack.securePosition(me.x, me.y, { range: 30, duration: 3000 }); - Pather.makePortal(); - this.log("1"); - - while (!this.playerIn()) { - delay(200); - } - - Attack.kill(getLocaleString(sdk.locale.monsters.ShenktheOverseer)); - Pickit.pickItems(); - Pather.moveTo(3846, 5120); - this.log("2"); - - while (this.playerIn()) { - delay(200); - } - - Pather.usePortal(null, null); - - return true; - }; - - this.anya = function () { - if (!Config.Rusher.Anya) return false; - - !me.inTown && Town.goToTown(); - - this.log("starting anya"); - - if (!Pather.useWaypoint(sdk.areas.CrystalizedPassage, true)) { - throw new Error("Anya quest failed"); - } - - Precast.doPrecast(false); - - if (!Pather.moveToExit(sdk.areas.FrozenRiver, true) - || !Pather.moveToPreset(me.area, sdk.unittype.Object, sdk.objects.FrozenAnyasPlatform)) { - throw new Error("Anya quest failed"); - } - - Attack.securePosition(me.x, me.y, { range: 30, duration: 2000 }); - - let anya = Game.getObject(sdk.objects.FrozenAnya); - - if (anya) { - Pather.moveToUnit(anya); - // Rusher should be able to interact so quester can get the potion without entering - Packet.entityInteract(anya); - delay(1000 + me.ping); - me.cancel(); - } - - Pather.makePortal(); - this.log("1"); - - while (!this.playerIn()) { - delay(200); - } - - Misc.poll(() => !Game.getObject(sdk.objects.FrozenAnya), 30000, 1000); - - this.log("2"); // Mainly for non-questers to know when to get the scroll of resistance - - while (this.playerIn()) { - delay(200); - } - - Pather.usePortal(null, null); - - return true; - }; - - this.givewps = function () { - if (!Config.Rusher.GiveWps) return false; - - let wpsLeft = wpsToGive.slice(0); - console.log(JSON.stringify(wpsLeft)); - - wpsLeft.forEach(function (wp) { - me.checkScrolls(sdk.items.TomeofTownPortal) <= 5 && (Pather.useWaypoint(sdk.areas.townOf(me.area)) || Town.goToTown()) && Town.doChores(); - Pather.useWaypoint(wp); - }); - - return true; - }; - - console.log(sdk.colors.LightGold + "Loading RushThread"); - - let command = ""; - let current = 0; - let commandsplit = ""; - let check = -1; - let sequence = [ - "cain", "andariel", "radament", "cube", "amulet", "staff", "summoner", "duriel", "lamesen", - "travincal", "mephisto", "izual", "diablo", "shenk", "anya", "ancients", "baal", "givewps" - ]; + Town.goToTown(); - this.scriptEvent = function (msg) { - if (typeof msg === "string") { - if (!msg.startsWith("rush-")) return; - command = msg.substring(5); + command = "go"; } }; - addEventListener("scriptmsg", this.scriptEvent); - // START Config.init(false); Pickit.init(false); @@ -1070,15 +276,34 @@ function main () { Config.MFLeader = false; + (function () { + let lastRunIdx = sequences.findIndex(function (s) { + return String.isEqual(rushProfile.config.LastRun, s.name); + }); + if (lastRunIdx > -1) { + let temp = sequences.slice(0, lastRunIdx + 1); + sequences.length = 0; + for (let i = 0; i < temp.length; i++) { + sequences.push(temp[i]); + } + } + })(); + + if (rushProfile.config.Wps) { + sequences.push(givewps); + } + + console.debug("Rush sequence: " + sequences.map((s) => s.name).join(", ")); + while (true) { if (command) { switch (command) { case "go": // End run if entire sequence is done or if Config.Rusher.LastRun is done - if (current >= sequence.length || (Config.Rusher.LastRun && current > sequence.indexOf(Config.Rusher.LastRun))) { + if (current >= sequences.length) { delay(3000); - this.log("bye ~"); - console.log("Current sequence length: " + current + " sequence length: " + sequence.length); + log("exit"); + console.log("Current sequence length: " + current + " sequence length: " + sequences.length); while (Misc.getPlayerCount() > 1) { delay(1000); @@ -1091,11 +316,13 @@ function main () { Town.doChores(); + // TODO: add extra checks before starting to ensure quester is moving along with us + try { - this[sequence[current]](); + sequences[current](questerName); } catch (sequenceError) { - this.log(sequenceError.message); - this.log("2"); + log(sequenceError.message); + log(AutoRush.playersOut); Town.goToTown(); } @@ -1104,61 +331,7 @@ function main () { break; default: - if (typeof command === "string") { - if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "skiptoact") { - if (!isNaN(parseInt(command.split(" ")[1], 10))) { - switch (parseInt(command.split(" ")[1], 10)) { - case 2: - current = sequence.indexOf("andariel") + 1; - Town.goToTown(2); - - break; - case 3: - current = sequence.indexOf("duriel") + 1; - Town.goToTown(3); - - break; - case 4: - current = sequence.indexOf("mephisto") + 1; - Town.goToTown(4); - - break; - case 5: - current = sequence.indexOf("diablo") + 1; - Town.goToTown(5); - - break; - } - } - - command = ""; - } else if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "clear") { - this.clearArea(Number(command.split(" ")[1])); - Town.goToTown(); - - command = "go"; - } else if (command.split(" ")[0] !== undefined && command.split(" ")[0] === "highestquest") { - command.split(" ")[1] !== undefined && (commandsplit = command.split(" ")[1]); - check = sequence.findIndex(i => i === commandsplit); - check > -1 && (current = check + 1); - - command = ""; - } else { - for (let i = 0; i < sequence.length; i += 1) { - if (command && sequence[i].match(command, "gi")) { - current = i; - - break; - } - } - - Town.goToTown(); - - command = "go"; - - break; - } - } + handleRushCommand(command); break; } From 3fae102ebc0778401b1fac0e05874699c1e6af20 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Thu, 28 May 2026 23:22:17 -0400 Subject: [PATCH 750/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index d0286d5e6..c9d813137 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit d0286d5e65c4ab2c2bded2b6be79b59f1b34cbc0 +Subproject commit c9d813137e5f656c480ddad0a86a67af793029e6 From 99038db443df8dbf1304707b49308e581a03e65c Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Fri, 29 May 2026 18:58:17 -0400 Subject: [PATCH 751/758] Update soloplay --- d2bs/kolbot/libs/SoloPlay | 2 +- d2bs/kolbot/sdk/globals.d.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index c9d813137..9dae740a4 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit c9d813137e5f656c480ddad0a86a67af793029e6 +Subproject commit 9dae740a4c7b993f92cb308205035d668d8a5300 diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 6e2314fee..05efd0b76 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -27,6 +27,11 @@ declare global { type KolbotScript = import("./types/kolbot-scripts").KolbotScript; type EventsInstance = InstanceType; + /** + * @description A string that can be used in NTIP lists, which supports some additional syntax for item properties. + */ + type NipString = string; + interface Error { fileName: string; lineNumber: number; From d23bc3d7b3a64ef66dc08ff66e5ff672a49bda93 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 30 May 2026 08:48:38 -0400 Subject: [PATCH 752/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 9dae740a4..a1289680b 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 9dae740a4c7b993f92cb308205035d668d8a5300 +Subproject commit a1289680bcc13f83798817d080267714ea4531cd From 1f0d8bdd26b568d17b547b36575df7a92b43689f Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sat, 30 May 2026 13:47:41 -0400 Subject: [PATCH 753/758] Use Set for monsterObjects; add isSoul prop - Replace Attack.monsterObjects array with a Set for faster membership checks and update code to use Set.has instead of includes. - Add Unit.prototype.isSoul (and its global type) to identify soul-type monsters. Tighten Attack.d.ts types: change monsterObjects to Set and refine buildGrid parameter/return types from any to number for better type safety. --- d2bs/kolbot/libs/core/Attack.js | 6 +++--- d2bs/kolbot/libs/core/Prototypes.js | 13 +++++++++++++ d2bs/kolbot/sdk/globals.d.ts | 1 + d2bs/kolbot/sdk/types/Attack.d.ts | 16 ++++++++-------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/d2bs/kolbot/libs/core/Attack.js b/d2bs/kolbot/libs/core/Attack.js index 56e969a9a..58c16eb91 100644 --- a/d2bs/kolbot/libs/core/Attack.js +++ b/d2bs/kolbot/libs/core/Attack.js @@ -10,7 +10,7 @@ const Attack = { infinity: false, auradin: false, - monsterObjects: [ + monsterObjects: new Set([ sdk.monsters.Turret1, sdk.monsters.Turret2, sdk.monsters.Turret3, sdk.monsters.MummyGenerator, sdk.monsters.GargoyleTrap, sdk.monsters.LightningSpire, @@ -21,7 +21,7 @@ const Attack = { sdk.monsters.CatapultW, sdk.monsters.BarricadeTower, sdk.monsters.PrisonDoor, sdk.monsters.DiablosBoneCage, sdk.monsters.DiablosBoneCage2, sdk.monsters.Hut, - ], + ]), Result: { FAILED: 0, SUCCESS: 1, @@ -1769,7 +1769,7 @@ const Attack = { } let result; - let mObject = Attack.monsterObjects.includes(unitid); + let mObject = Attack.monsterObjects.has(unitid); let nonFloorAreas = [ sdk.areas.ArcaneSanctuary, sdk.areas.RiverofFlame, sdk.areas.ChaosSanctuary, sdk.areas.Abaddon, sdk.areas.PitofAcheron, sdk.areas.InfernalPit diff --git a/d2bs/kolbot/libs/core/Prototypes.js b/d2bs/kolbot/libs/core/Prototypes.js index 0bf3c983e..b763c7f91 100644 --- a/d2bs/kolbot/libs/core/Prototypes.js +++ b/d2bs/kolbot/libs/core/Prototypes.js @@ -130,6 +130,19 @@ Object.defineProperties(Unit.prototype, { ].includes(this.classid); }, }, + isSoul: { + get: function () { + return [ + sdk.monsters.BurningSoul1, + sdk.monsters.BurningSoul2, + sdk.monsters.SoulKiller1, + sdk.monsters.SoulKiller2, + sdk.monsters.SoulKiller3, + sdk.monsters.SoulKiller4, + sdk.monsters.SoulKiller5, + ].includes(this.classid); + }, + }, isMonsterObject: { get: function () { return [ diff --git a/d2bs/kolbot/sdk/globals.d.ts b/d2bs/kolbot/sdk/globals.d.ts index 05efd0b76..31892a070 100644 --- a/d2bs/kolbot/sdk/globals.d.ts +++ b/d2bs/kolbot/sdk/globals.d.ts @@ -475,6 +475,7 @@ declare global { readonly isBoss: boolean; readonly isGhost: boolean; readonly isDoll: boolean; + readonly isSoul: boolean; readonly isMonsterObject: boolean; readonly isMonsterEgg: boolean; readonly isMonsterNest: boolean; diff --git a/d2bs/kolbot/sdk/types/Attack.d.ts b/d2bs/kolbot/sdk/types/Attack.d.ts index 271e8714a..1d566806b 100644 --- a/d2bs/kolbot/sdk/types/Attack.d.ts +++ b/d2bs/kolbot/sdk/types/Attack.d.ts @@ -13,7 +13,7 @@ declare global { namespace Attack { const infinity: boolean; const auradin: boolean; - const monsterObjects: number[]; + const monsterObjects: Set; const Result: AttackResult; const _killed: Set; function haveKilled(id: number | string): boolean; @@ -84,14 +84,14 @@ declare global { function deploy(unit: Monster, distance: any, spread: any, range: any, ...args: any[]): boolean; function getMonsterCount(x: any, y: any, range: any, list: any): number; function buildGrid( - xmin: any, - xmax: any, - ymin: any, - ymax: any, - spread: any, + xmin: number, + xmax: number, + ymin: number, + ymax: number, + spread: number, ): { - x: any; - y: any; + x: number; + y: number; coll: number; }[]; function skipCheck(unit: Monster): boolean; From 4e708fb20eae591c54ca3d5a11b188d71288bbf7 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 3 Jun 2026 17:56:25 -0400 Subject: [PATCH 754/758] Add missing condition for using Teleport & UseWells definition --- d2bs/kolbot/libs/core/GameData/SkillData.js | 3 +++ d2bs/kolbot/sdk/types/Config.d.ts | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/d2bs/kolbot/libs/core/GameData/SkillData.js b/d2bs/kolbot/libs/core/GameData/SkillData.js index 8255c05ff..9b5f81f4c 100644 --- a/d2bs/kolbot/libs/core/GameData/SkillData.js +++ b/d2bs/kolbot/libs/core/GameData/SkillData.js @@ -303,6 +303,9 @@ skillMap.set(sdk.skills.Teleport, { hand: sdk.skills.hand.Right, range: 40, + condition: function () { + return !Config.NoTele; + } }); skillMap.set(sdk.skills.GlacialSpike, { hand: sdk.skills.hand.Left, diff --git a/d2bs/kolbot/sdk/types/Config.d.ts b/d2bs/kolbot/sdk/types/Config.d.ts index e1d74cc43..8adbfe341 100644 --- a/d2bs/kolbot/sdk/types/Config.d.ts +++ b/d2bs/kolbot/sdk/types/Config.d.ts @@ -146,6 +146,12 @@ declare global { ImmunityException: DamageType[]; ScanShrines: number[]; AutoShriner: boolean; + UseWells: { + HpPercent: number, + MpPercent: number, + StaminaPercent: number, + StatusEffects: boolean, + }; Debug: boolean; AutoMule: { Trigger: (number | string | ((item: ItemUnit) => boolean))[]; From c3619345e6d320c72d7701d518959c6836469039 Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Wed, 3 Jun 2026 17:56:30 -0400 Subject: [PATCH 755/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index a1289680b..2d15b26b6 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit a1289680bcc13f83798817d080267714ea4531cd +Subproject commit 2d15b26b633d1698b0e08af7a3ed65eab52465a9 From f2506bace1f1249641c721d9f121e2b74e8fa3dc Mon Sep 17 00:00:00 2001 From: theBGuy <60308670+theBGuy@users.noreply.github.com> Date: Sun, 7 Jun 2026 01:08:40 -0400 Subject: [PATCH 756/758] Update SoloPlay --- d2bs/kolbot/libs/SoloPlay | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/SoloPlay b/d2bs/kolbot/libs/SoloPlay index 2d15b26b6..ada829064 160000 --- a/d2bs/kolbot/libs/SoloPlay +++ b/d2bs/kolbot/libs/SoloPlay @@ -1 +1 @@ -Subproject commit 2d15b26b633d1698b0e08af7a3ed65eab52465a9 +Subproject commit ada82906406663c72f2664f052fd6965c16794f0 From 3bf60366d7a920b9b3f40d964357b5c2a2e30594 Mon Sep 17 00:00:00 2001 From: ResurrectedTrader Date: Wed, 10 Jun 2026 01:24:49 +0100 Subject: [PATCH 757/758] Add InvTrans property to logged items (#484) * Add invTrans property to item description * Add invTrans property to MuleLogger return object --- d2bs/kolbot/libs/core/Item.js | 2 ++ d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js | 1 + 2 files changed, 3 insertions(+) diff --git a/d2bs/kolbot/libs/core/Item.js b/d2bs/kolbot/libs/core/Item.js index 41f7990ee..abbeee4f5 100644 --- a/d2bs/kolbot/libs/core/Item.js +++ b/d2bs/kolbot/libs/core/Item.js @@ -533,6 +533,7 @@ const Item = { let lastArea; const name = unit.fname.split("\n").reverse().join(" ").replace(/ÿc[0-9!"+<:;.*]|\/|\\/g, "").trim(); const color = (unit.getColor() || -1); + const invTrans = (getBaseStat("items", unit.classid, "InvTrans") || 0); const code = this.getItemCode(unit); const sock = unit.getItem(); let desc = this.getItemDesc(unit); @@ -561,6 +562,7 @@ const Item = { image: code, textColor: unit.quality, itemColor: color, + invTrans: invTrans, header: "", sockets: this.getItemSockets(unit) }; diff --git a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js index 1b322bfce..2c912a89a 100644 --- a/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js +++ b/d2bs/kolbot/libs/systems/mulelogger/MuleLogger.js @@ -256,6 +256,7 @@ const MuleLogger = { return { itemColor: color, + invTrans: (getBaseStat("items", unit.classid, "InvTrans") || 0), image: code, title: name, description: desc, From bf6c5bfede6146c796075bf333b8ea7a9eeecba2 Mon Sep 17 00:00:00 2001 From: Antiwarden <125026215+Antiwarden@users.noreply.github.com> Date: Tue, 16 Jun 2026 04:53:29 +0200 Subject: [PATCH 758/758] Fix quitFlag declaration in BaalHelper.js (#486) quitFlag was assigned a value before being declared. --- d2bs/kolbot/libs/scripts/BaalHelper.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/d2bs/kolbot/libs/scripts/BaalHelper.js b/d2bs/kolbot/libs/scripts/BaalHelper.js index bc81b8db5..5259fd96b 100644 --- a/d2bs/kolbot/libs/scripts/BaalHelper.js +++ b/d2bs/kolbot/libs/scripts/BaalHelper.js @@ -12,6 +12,8 @@ const BaalHelper = new Runnable( /** @param {BaalHelperContext} ctx */ function BaalHelper (ctx) { + let quitFlag; + const chatEvent = function (nick, msg) { if (nick === Config.Leader) { if ((Config.BaalHelper.DollQuit && msg === "Dolls found! NG.") @@ -76,7 +78,7 @@ const BaalHelper = new Runnable( Town.goToTown(5); Town.move("portalspot"); - let quitFlag = false; + quitFlag = false; if (Config.BaalHelper.DollQuit || Config.BaalHelper.SoulQuit) { addEventListener("chatmsg", chatEvent);